wads 0.1.3 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14aff80a445145535994cd93357eae0a5ed6b3b5ae31c722ab043e92857dc645
4
- data.tar.gz: 5cbcc5dea862306c0d9e572f8781b78f1da32e77947f130ef49670ff3f9ef8fd
3
+ metadata.gz: d09e80867e43c756f3f39c4e56e0559578b58b289914fffbd4c085db10e58cff
4
+ data.tar.gz: 6acffd90dcf53460d36cb8f3c20687740a2f8892a5fc14313530fda6deb88191
5
5
  SHA512:
6
- metadata.gz: 1e43b86028c31695ecee423d4cda06a59e09386f55cd08507058443373541f2dd2914dae44d0f6c5d2937688546b37986f0c7cc66f7ae4f24d749d4457e390f3
7
- data.tar.gz: 954a9e628886be15e7e91cdf3a7522c845480e48b49cd0c01666d91fb695f6885c06775933d1f36ff64ce3db75e508a2f2d37e20b8067fa8dce442dead4aa32d
6
+ metadata.gz: 395234eb8cffb136c2d2331b2fa844feff4c0056e2a5c720e18367f340a26472f26b08abd34c57d858d48da73ee107f89b8554106534b357910300fbe426b299
7
+ data.tar.gz: 3e50d54e3b85b2f5cdf1336d27a68126c7ec3a1ebb45d2e39e4bb34a0b1296ba2acd902f04ce2e9b2ac39a46594e1baf86351c5232cf97177b9b223a3eed0ffa
data/CHANGELOG.md CHANGED
@@ -10,3 +10,14 @@
10
10
 
11
11
  - Fixed bugs with data structures. Added convenience methods to manage child widgets
12
12
 
13
+ ## [0.1.3] - 2021-09-30
14
+
15
+ - Improvements to graph display including user ability to change focus node
16
+
17
+ ## [0.2.0] - 2021-10-05
18
+
19
+ - Breaking change in order to refactor, including standardizing order of widget constructor args
20
+
21
+ ## [0.2.3] - 2021-10-21
22
+
23
+ - Upgrade dependency versions
data/README.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  A number of Ruby Gosu-based (W)idgets (a)nd (D)ata (S)tructures for your applications.
4
4
 
5
+ The user guide and examples can be found at https://www.rubydoesitagain.com
6
+
7
+ Wads (Widgets and Data Structures) is intended to help you quickly build native Ruby applications using the Gosu gem.
8
+ GUI widgets, such as tables and buttons, are provided to simplify building graphical applications.
9
+ Widgets in wads can be used independently within your Gosu app, or you can use wads as a holistic GUI framework.
10
+ Wads includes basic features found in many GUI toolkits including The use of themes and a layout manager.
11
+
12
+ One of the motivations for creating wads is to making data analysis, games, and productivity applications using Ruby easier to do.
13
+ Data structures are included that can be used for statistical analysis or graph-based applications.
14
+ The samples provided illustrate both use cases (Stocks - stats) and Star Wars character interactions (graphs).
15
+
5
16
  ![alt Screenshot](https://github.com/dbroemme/ruby-wads/blob/main/media/WadsScreenshot.png?raw=true)
6
17
  ## Installation
7
18
 
@@ -0,0 +1,11 @@
1
+ N,A,color=COLOR_YELLOW
2
+ N,B,color=COLOR_GREEN
3
+ N,C,color=COLOR_BLUE
4
+ N,D,color=COLOR_RED
5
+ N,E,color=COLOR_PURPLE
6
+ N,F,color=COLOR_GRAY
7
+ C,A,B
8
+ C,A,C
9
+ C,B,D
10
+ C,B,E
11
+ C,E,F
data/lib/wads/app.rb CHANGED
@@ -1,349 +1,73 @@
1
1
  require 'gosu'
2
- require 'json'
3
- require_relative 'data_structures'
4
2
  require_relative 'widgets'
5
- require_relative 'version'
6
3
 
7
- include Wads
8
-
9
- class WadsSampleApp < Gosu::Window
10
-
11
- STOCKS_DATA_FILE = "./data/NASDAQ.csv"
12
- LOTTERY_DATA_FILE = "./data/Pick4_12_21_2020.txt"
13
- STAR_WARS_DATA_FILE = "./data/starwars-episode-4-interactions.json"
14
-
15
- def initialize
16
- super(800, 600)
17
- self.caption = "Wads Sample App"
18
- @font = Gosu::Font.new(32)
19
- @title_font = Gosu::Font.new(38)
20
- @version_font = Gosu::Font.new(22)
21
- @small_font = Gosu::Font.new(22)
22
- @banner_image = Gosu::Image.new("./media/Banner.png")
23
- @display_widget = nil
24
- @update_count = 0
25
- end
4
+ module Wads
5
+ #
6
+ # The WadsApp class provides a simple starting point to quickly build a native
7
+ # Ruby application using Gosu as an underlying library. It provides all the necessary
8
+ # hooks to get started. All you need to do is supply the parent Wads widget using
9
+ # the set_display(widget) method. See one of the Wads samples for example usage.
10
+ #
11
+ class WadsApp < Gosu::Window
12
+ def initialize(width, height, caption, widget)
13
+ super(width, height)
14
+ self.caption = caption
15
+ @update_count = 0
16
+ WadsConfig.instance.set_window(self)
17
+ set_display(widget)
18
+ WadsConfig.instance.set_log_level("info")
19
+ @registered_hold_down_buttons = []
20
+ end
26
21
 
27
- def parse_opts_and_run
28
- # Make help the default output if no args are specified
29
- if ARGV.length == 0
30
- ARGV[0] = "-h"
22
+ #
23
+ # This method must be invoked with any Wads::Widget instance. It then handles
24
+ # delegating all events and drawing all child widgets.
25
+ #
26
+ def set_display(widget)
27
+ @main_widget = widget
31
28
  end
32
29
 
33
- opts = SampleAppCommand.new.parse.run
34
- if opts[:stocks]
35
- stats = process_stock_data
36
- if opts[:text]
37
- stats.report(Date::DAYNAMES[1..5])
38
- else
39
- @display_widget = SampleStocksDisplay.new(@small_font, stats)
40
- show
41
- end
42
-
43
- elsif opts[:jedi]
44
- graph = process_star_wars_data
45
- @display_widget = SampleStarWarsDisplay.new(@small_font, graph)
46
- show
47
-
48
- elsif opts[:lottery]
49
- process_lottery_data
50
-
51
- elsif opts[:graph]
52
- graph = create_test_graph #_using_nodes
53
- if not opts[:text]
54
- @display_widget = SampleGraphDisplay.new(@small_font, graph)
55
- show
56
- end
57
- else
58
- puts " "
59
- puts "Select one of the following sample analysis options"
60
- puts "-s Run sample stocks analysis"
61
- puts "-j Run sample Star Wars character analysis"
62
- puts "-g Run sample graph display"
63
- puts " "
64
- exit
30
+ def get_display
31
+ @main_widget
65
32
  end
66
33
 
67
- end
68
-
69
- def update
70
- @display_widget.update(@update_count, mouse_x, mouse_y)
71
- @update_count = @update_count + 1
72
- end
73
-
74
- def draw
75
- draw_banner
76
- @display_widget.draw
77
- end
78
-
79
- def draw_banner
80
- @banner_image.draw(1,1,1,0.9,0.9)
81
- @title_font.draw_text("Wads Sample App", 10, 20, 2, 1, 1, Gosu::Color::WHITE)
82
- @version_font.draw_text("Version #{Wads::VERSION}", 13, 54, 2, 1, 1, Gosu::Color::WHITE)
83
- end
84
-
85
- def button_down id
86
- close if id == Gosu::KbEscape
87
- # Delegate button events to the primary display widget
88
- result = @display_widget.button_down id, mouse_x, mouse_y
89
- if not result.nil? and result.is_a? WidgetResult
90
- if result.close_widget
91
- close
92
- end
34
+ # Register a key (identified by the Gosu id) to check if it is being held down.
35
+ # If so, the handle_key_held_down callback will be invoked on widgets
36
+ # For example, register_hold_down_key(Gosu::KbLeft)
37
+ def register_hold_down_key(id)
38
+ @registered_hold_down_buttons << id
93
39
  end
94
- end
95
-
96
- def button_up id
97
- @display_widget.button_up id, mouse_x, mouse_y
98
- end
99
-
100
- def process_stock_data
101
- # The data file comes from https://finance.yahoo.com
102
- # The format of this file is as follows:
103
- #
104
- # Date,Open,High,Low,Close,Adj Close,Volume
105
- # 2000-01-03,4186.189941,4192.189941,3989.709961,4131.149902,4131.149902,1510070000
106
- # 2000-01-04,4020.000000,4073.250000,3898.229980,3901.689941,3901.689941,1511840000
107
- # ...
108
- # 2020-12-30,12906.509766,12924.929688,12857.759766,12870.000000,12870.000000,5292210000
109
- # 2020-12-31,12877.089844,12902.070313,12821.230469,12888.280273,12888.280273,4771390000
110
-
111
- stats = Stats.new("NASDAQ")
112
- previous_close = nil
113
-
114
- puts "Read the data file #{STOCKS_DATA_FILE}"
115
- File.readlines(STOCKS_DATA_FILE).each do |line|
116
- line = line.chomp # remove the carriage return
117
40
 
118
- # Ignore header and any empty lines, process numeric data lines
119
- if line.length > 0 and line[0].match(/[0-9]/)
120
- values = line.split(",")
121
- date = Date.strptime(values[0], "%Y-%m-%d")
122
- weekday = Date::DAYNAMES[date.wday]
41
+ def update
42
+ @main_widget.update(@update_count, mouse_x, mouse_y)
123
43
 
124
- open_value = values[1].to_f
125
- close_value = values[4].to_f
126
-
127
- if previous_close.nil?
128
- # Just use the first day to set the baseline
129
- previous_close = close_value
130
- else
131
- change_from_previous_close = close_value - previous_close
132
- change_intraday = close_value - open_value
133
- change_overnight = open_value - previous_close
134
-
135
- change_percent = change_from_previous_close / previous_close
136
-
137
- stats.add(weekday, change_percent)
138
- stats.add("#{weekday} prev close", change_from_previous_close)
139
- stats.add("#{weekday} intraday", change_intraday)
140
- stats.add("#{weekday} overnight", change_overnight)
141
-
142
- previous_close = close_value
44
+ # Look for keys that are held down and delegate those events
45
+ @registered_hold_down_buttons.each do |id|
46
+ if button_down?(id)
47
+ @main_widget.handle_key_held_down id, mouse_x, mouse_y
143
48
  end
144
49
  end
145
- end
146
-
147
- stats
148
- end
149
-
150
- def process_lottery_data
151
- puts "The lottery example has not been implemented yet"
152
- puts "however the data is available in the data directory,"
153
- puts "and the same pattern used in the stocks example can"
154
- puts "be applied here."
155
- end
156
-
157
- def process_star_wars_data
158
- star_wars_json = File.read(STAR_WARS_DATA_FILE)
159
- data_hash = JSON.parse(star_wars_json)
160
- characters = data_hash['nodes']
161
- interactions = data_hash['links']
162
-
163
- # The interactions in the data set reference the characters by their
164
- # zero based index, so we keep a reference in our graph by index.
165
- # The character's value is the number of scenes in which they appear.
166
- graph = Graph.new
167
- characters.each do |character|
168
- node_tags = {}
169
- node_color_str = character['colour']
170
- # This is a bit of a hack, but our background is black so black text
171
- # will not show up. Change this to white
172
- if node_color_str == "#000000"
173
- node_color_str = "#FFFFFF"
174
- end
175
- # Convert hex string (ex. "#EE00AA") into int hex representation
176
- # understood by Gosu color (ex. 0xFFEE00AA)
177
- node_color = "0xFF#{node_color_str[1..-1]}".to_i(16)
178
- node_tags['color'] = node_color
179
- graph.add_node(Node.new(character['name'], character['value'], node_tags))
180
- end
181
- interactions.each do |interaction|
182
- character_one = graph.node_by_index(interaction['source'])
183
- character_two = graph.node_by_index(interaction['target'])
184
- number_of_scenes_together = interaction['value']
185
- edge_tags = {}
186
- edge_tags["scenes"] = number_of_scenes_together
187
- graph.add_edge(character_one, character_two, edge_tags)
188
- end
189
- graph
190
- end
191
-
192
- def create_test_graph
193
- g = Graph.new
194
- g.add("a")
195
- g.add("b")
196
- g.add("c")
197
- g.add("d")
198
- g.add("e")
199
- g.add("f")
200
- g.connect("a", "b")
201
- g.connect("a", "c")
202
- g.connect("b", "d")
203
- g.connect("b", "e")
204
- g.connect("e", "f")
205
- g
206
- end
207
50
 
208
- def create_test_graph_using_nodes
209
- root = Node.new("a")
210
- b = root.add("b")
211
- b.add("d")
212
- b.add("e").add("f")
213
- root.add("c")
214
- Graph.new(root)
215
- end
216
- end
217
-
218
- class SampleStocksDisplay < Widget
219
- attr_accessor :stats
220
-
221
- def initialize(font, stats)
222
- super(10, 100, COLOR_HEADER_BRIGHT_BLUE)
223
- set_dimensions(780, 500)
224
- set_font(font)
225
- add_document(sample_content, 5, 5, @width, @height)
226
- add_button("Exit", 380, @height - 30) do
227
- WidgetResult.new(true)
228
- end
229
-
230
- @stats = stats
231
- @data_table = add_single_select_table(5, 100, # top left corner
232
- 770, 200, # width, height
233
- ["Day", "Min", "Avg", "StdDev", "Max", "p10", "p90"], # column headers
234
- COLOR_WHITE) # font and text color
235
- @data_table.selected_color = COLOR_LIGHT_GRAY
236
- Date::DAYNAMES[1..5].each do |day|
237
- min = format_percent(@stats.min(day))
238
- avg = format_percent(@stats.average(day))
239
- std = format_percent(@stats.std_dev(day))
240
- max = format_percent(@stats.max(day))
241
- p10 = format_percent(@stats.percentile(day, 0.1))
242
- p90 = format_percent(@stats.percentile(day, 0.90))
243
- @data_table.add_row([day, min, avg, std, max, p10, p90], COLOR_HEADER_BRIGHT_BLUE)
244
- end
245
-
246
- @selection_text = nil
247
- end
248
-
249
- def format_percent(val)
250
- "#{(val * 100).round(3)}%"
251
- end
252
-
253
- def sample_content
254
- <<~HEREDOC
255
- This sample stock analysis uses NASDAQ data from https://finance.yahoo.com looking
256
- at closing data through the years 2000 to 2020. The percent gain or loss is broken
257
- down per day, as shown in the table below.
258
- HEREDOC
259
- end
260
-
261
- def render
262
- if @selection_text
263
- @selection_text.draw
264
- end
265
- end
266
-
267
- def handle_mouse_down mouse_x, mouse_y
268
- if @data_table.contains_click(mouse_x, mouse_y)
269
- val = @data_table.set_selected_row(mouse_y, 0)
270
- if not val.nil?
271
- @selection_text = Text.new("You selected #{val}, a great day!",
272
- x + 5, y + 400, @font)
273
- end
51
+ @update_count = @update_count + 1
52
+ end
53
+
54
+ def draw
55
+ @main_widget.draw
274
56
  end
275
- nil
276
- end
277
- end
278
-
279
- class SampleStarWarsDisplay < Widget
280
- attr_accessor :graph
281
-
282
- def initialize(font, graph)
283
- super(10, 100, COLOR_HEADER_BRIGHT_BLUE)
284
- set_dimensions(780, 500)
285
- set_font(font)
286
- @graph = graph
287
57
 
288
- add_document(sample_content, 5, 5, @width, @height)
289
- add_button("Exit", 370, @height - 30) do
290
- WidgetResult.new(true)
58
+ def button_down id
59
+ close if id == Gosu::KbEscape
60
+ # Delegate button events to the primary display widget
61
+ result = @main_widget.button_down id, mouse_x, mouse_y
62
+ if not result.nil? and result.is_a? WidgetResult
63
+ if result.close_widget
64
+ close
65
+ end
66
+ end
291
67
  end
292
-
293
- @data_table = add_single_select_table(5, 70, # top left corner
294
- 770, 180, # width, height
295
- ["Character", "Number of Scenes"], # column headers
296
- COLOR_WHITE, # font and text color
297
- 5) # max visible rows
298
- @data_table.selected_color = COLOR_LIGHT_GRAY
299
-
300
- @graph.node_list.each do |character|
301
- @data_table.add_row([character.name, character.value], character.get_tag("color"))
68
+
69
+ def button_up id
70
+ @main_widget.button_up id, mouse_x, mouse_y
302
71
  end
303
- @graph_display = add_graph_display(5, 260, 770, 200, @graph)
304
72
  end
305
-
306
- def sample_content
307
- <<~HEREDOC
308
- This sample analysis shows the interactions between characters in the Star Wars
309
- Episode 4: A New Hope. Click on a character to see more detail.
310
- HEREDOC
311
- end
312
-
313
- def handle_key_press id, mouse_x, mouse_y
314
- if id == Gosu::KbUp
315
- @data_table.scroll_up
316
- elsif id == Gosu::KbDown
317
- @data_table.scroll_down
318
- end
319
- WidgetResult.new(false)
320
- end
321
-
322
- def handle_mouse_down mouse_x, mouse_y
323
- if @data_table.contains_click(mouse_x, mouse_y)
324
- val = @data_table.set_selected_row(mouse_y, 0)
325
- if not val.nil?
326
- node = @graph.find_node(val)
327
- @graph_display.set_center_node(node, 2)
328
- end
329
- end
330
- nil
331
- end
332
73
  end
333
-
334
- class SampleGraphDisplay < Widget
335
- attr_accessor :graph
336
-
337
- def initialize(font, graph)
338
- super(10, 100, COLOR_CYAN)
339
- set_dimensions(780, 500)
340
- set_font(font)
341
- @graph = graph
342
- exit_button = add_button("Exit", 370, @height - 30) do
343
- WidgetResult.new(true)
344
- end
345
- exit_button.text_color = COLOR_CYAN
346
- @graph_display = add_graph_display(5, 60, 770, 400, @graph)
347
- @graph_display.set_tree_display
348
- end
349
- end