wads 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ced90f1073417f101cc5688139f63c6437814a7bf96249b54344ad3a72f5a0fc
4
- data.tar.gz: '08fafccb767210d6f3653ee9ab6c33ca01962b4bd71503c54a3afcca83efcd9f'
3
+ metadata.gz: a1cf7ab7c4229f57f2bb4691819c5a4282a3fd0f7703e5caa6c93e49aeb35053
4
+ data.tar.gz: a1f0e971db1ea5064e44fef39c31767125f844b8ab3ef87eeaf09e3e92f27ad1
5
5
  SHA512:
6
- metadata.gz: 2daa8f10e3cabab4c8824f5f070b5b9f5b124b5c90910b6bd9d0a75c8775e8ed1285ed4cb2a019a08320f27c735065789a2401e60b2098731d6e7d915073868d
7
- data.tar.gz: 235bd6b70e43ef6f8714ea6406598a49747f5846495193d5193d089acf09b30ef80ff5dbf91733c6eb6e0e08468f34b192735262900b31b89d4caf71b41a1648
6
+ metadata.gz: 315790b3c8fe99525c3ad78eaa90eecd17fadd09a2a3e16a13ba82cb61ea5279472c57ada6e88021e9c6649236bb598aad0a46a1954928a1c37dc8dbf8b45b5e
7
+ data.tar.gz: 89de4f997dcef2e1b9e25062af443d6994d7ee4070d89dab21f06cb492ed3d106f837a9c3bdf85d7ba7e6522d30a039694982d939d49dd41b7da30c2637299ea
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
- ## [Unreleased]
2
-
3
1
  ## [0.1.0] - 2021-08-23
4
2
 
5
3
  - Initial release
4
+
5
+ ## [0.1.1] - 2021-08-29
6
+
7
+ - Added graph data structure and corresponding display widgets
8
+
9
+ ## [0.1.2] - 2021-08-30
10
+
11
+ - Fixed bugs with data structures. Added convenience methods to manage child widgets
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
data/README.md CHANGED
@@ -2,6 +2,18 @@
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
+
16
+ ![alt Screenshot](https://github.com/dbroemme/ruby-wads/blob/main/media/WadsScreenshot.png?raw=true)
5
17
  ## Installation
6
18
 
7
19
  Add this line to your application’s Gemfile:
@@ -9,7 +21,7 @@ Add this line to your application’s Gemfile:
9
21
  ```
10
22
  gem 'wads'
11
23
  ```
12
- And the run bundle install.
24
+ And then run bundle install.
13
25
 
14
26
  ## Sample Application
15
27
 
@@ -20,8 +32,56 @@ git clone https://github.com/dbroemme/ruby-wads.git
20
32
  cd ruby-wads
21
33
  ./bin/setup
22
34
  ./run
23
- ./run-sample-app -s -g
35
+ ./run-sample-app -s
24
36
  ```
25
37
 
26
38
  This will run the sample NASDAQ stocks analysis that features the use of the
27
39
  Stats class, and a table Gosu widget used to display the results of the analysis.
40
+
41
+ ![alt Screenshot](https://github.com/dbroemme/ruby-wads/blob/main/media/StocksSample.png?raw=true)
42
+
43
+
44
+ You can also see the graph capabilities by running the Star Wars analysis example.
45
+ This uses a data set that captures all character interactions within Episode 4.
46
+
47
+ ```
48
+ ./run-sample-app -j
49
+ ```
50
+
51
+ There is also a sample Graph display using the following command.
52
+ ```
53
+ ./run-sample-app -g
54
+ ```
55
+ Note that you can construct graphs in one of two ways. The first approach uses Nodes directly.
56
+ ```
57
+ root = Node.new("a")
58
+ b = root.add("b")
59
+ b.add("d")
60
+ b.add("e").add("f")
61
+ root.add("c")
62
+ Graph.new(root)
63
+ ```
64
+ The second approach uses an overarching Graph object.
65
+ ```
66
+ g = Graph.new
67
+ g.add("a")
68
+ g.add("b")
69
+ g.add("c")
70
+ g.add("d")
71
+ g.add("e")
72
+ g.add("f")
73
+ g.connect("a", "b")
74
+ g.connect("a", "c")
75
+ g.connect("b", "d")
76
+ g.connect("b", "e")
77
+ g.connect("e", "f")
78
+ ```
79
+
80
+ Currently, the Graph object is used by the Graph Widget for display. For the code above, this appears by default as follows:
81
+
82
+ ![alt Screenshot](https://github.com/dbroemme/ruby-wads/blob/main/media/SampleGraph.png?raw=true)
83
+
84
+ ## References
85
+
86
+ The Star Wars data set is courtesy of:
87
+ Gabasova, E. (2016). Star Wars social network. DOI: https://doi.org/10.5281/zenodo.1411479.
@@ -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
@@ -0,0 +1,336 @@
1
+ {
2
+ "nodes": [
3
+ {
4
+ "name": "C-3PO",
5
+ "value": 42,
6
+ "colour": "#FFD700"
7
+ },
8
+ {
9
+ "name": "LUKE",
10
+ "value": 88,
11
+ "colour": "#3881e5"
12
+ },
13
+ {
14
+ "name": "DARTH VADER",
15
+ "value": 25,
16
+ "colour": "#000000"
17
+ },
18
+ {
19
+ "name": "CAMIE",
20
+ "value": 3,
21
+ "colour": "#808080"
22
+ },
23
+ {
24
+ "name": "BIGGS",
25
+ "value": 18,
26
+ "colour": "#808080"
27
+ },
28
+ {
29
+ "name": "LEIA",
30
+ "value": 25,
31
+ "colour": "#DCDCDC"
32
+ },
33
+ {
34
+ "name": "BERU",
35
+ "value": 5,
36
+ "colour": "#808080"
37
+ },
38
+ {
39
+ "name": "OWEN",
40
+ "value": 6,
41
+ "colour": "#808080"
42
+ },
43
+ {
44
+ "name": "OBI-WAN",
45
+ "value": 22,
46
+ "colour": "#48D1CC"
47
+ },
48
+ {
49
+ "name": "MOTTI",
50
+ "value": 3,
51
+ "colour": "#808080"
52
+ },
53
+ {
54
+ "name": "TARKIN",
55
+ "value": 12,
56
+ "colour": "#808080"
57
+ },
58
+ {
59
+ "name": "HAN",
60
+ "value": 44,
61
+ "colour": "#ff9400"
62
+ },
63
+ {
64
+ "name": "GREEDO",
65
+ "value": 2,
66
+ "colour": "#808080"
67
+ },
68
+ {
69
+ "name": "JABBA",
70
+ "value": 2,
71
+ "colour": "#808080"
72
+ },
73
+ {
74
+ "name": "DODONNA",
75
+ "value": 3,
76
+ "colour": "#808080"
77
+ },
78
+ {
79
+ "name": "GOLD LEADER",
80
+ "value": 14,
81
+ "colour": "#808080"
82
+ },
83
+ {
84
+ "name": "WEDGE",
85
+ "value": 15,
86
+ "colour": "#808080"
87
+ },
88
+ {
89
+ "name": "RED LEADER",
90
+ "value": 33,
91
+ "colour": "#808080"
92
+ },
93
+ {
94
+ "name": "RED TEN",
95
+ "value": 8,
96
+ "colour": "#808080"
97
+ },
98
+ {
99
+ "name": "GOLD FIVE",
100
+ "value": 8,
101
+ "colour": "#808080"
102
+ }
103
+ ],
104
+ "links": [
105
+ {
106
+ "source": 3,
107
+ "target": 1,
108
+ "value": 2
109
+ },
110
+ {
111
+ "source": 4,
112
+ "target": 3,
113
+ "value": 2
114
+ },
115
+ {
116
+ "source": 4,
117
+ "target": 1,
118
+ "value": 4
119
+ },
120
+ {
121
+ "source": 2,
122
+ "target": 5,
123
+ "value": 1
124
+ },
125
+ {
126
+ "source": 6,
127
+ "target": 1,
128
+ "value": 3
129
+ },
130
+ {
131
+ "source": 6,
132
+ "target": 7,
133
+ "value": 3
134
+ },
135
+ {
136
+ "source": 6,
137
+ "target": 0,
138
+ "value": 2
139
+ },
140
+ {
141
+ "source": 1,
142
+ "target": 7,
143
+ "value": 3
144
+ },
145
+ {
146
+ "source": 0,
147
+ "target": 1,
148
+ "value": 18
149
+ },
150
+ {
151
+ "source": 0,
152
+ "target": 7,
153
+ "value": 2
154
+ },
155
+ {
156
+ "source": 0,
157
+ "target": 5,
158
+ "value": 6
159
+ },
160
+ {
161
+ "source": 5,
162
+ "target": 1,
163
+ "value": 17
164
+ },
165
+ {
166
+ "source": 6,
167
+ "target": 5,
168
+ "value": 1
169
+ },
170
+ {
171
+ "source": 1,
172
+ "target": 8,
173
+ "value": 19
174
+ },
175
+ {
176
+ "source": 0,
177
+ "target": 8,
178
+ "value": 6
179
+ },
180
+ {
181
+ "source": 5,
182
+ "target": 8,
183
+ "value": 1
184
+ },
185
+ {
186
+ "source": 9,
187
+ "target": 10,
188
+ "value": 2
189
+ },
190
+ {
191
+ "source": 2,
192
+ "target": 9,
193
+ "value": 1
194
+ },
195
+ {
196
+ "source": 2,
197
+ "target": 10,
198
+ "value": 7
199
+ },
200
+ {
201
+ "source": 11,
202
+ "target": 8,
203
+ "value": 9
204
+ },
205
+ {
206
+ "source": 11,
207
+ "target": 1,
208
+ "value": 26
209
+ },
210
+ {
211
+ "source": 12,
212
+ "target": 11,
213
+ "value": 1
214
+ },
215
+ {
216
+ "source": 11,
217
+ "target": 13,
218
+ "value": 1
219
+ },
220
+ {
221
+ "source": 0,
222
+ "target": 11,
223
+ "value": 6
224
+ },
225
+ {
226
+ "source": 5,
227
+ "target": 9,
228
+ "value": 1
229
+ },
230
+ {
231
+ "source": 5,
232
+ "target": 10,
233
+ "value": 1
234
+ },
235
+ {
236
+ "source": 11,
237
+ "target": 5,
238
+ "value": 13
239
+ },
240
+ {
241
+ "source": 2,
242
+ "target": 8,
243
+ "value": 1
244
+ },
245
+ {
246
+ "source": 14,
247
+ "target": 15,
248
+ "value": 1
249
+ },
250
+ {
251
+ "source": 14,
252
+ "target": 16,
253
+ "value": 1
254
+ },
255
+ {
256
+ "source": 14,
257
+ "target": 1,
258
+ "value": 1
259
+ },
260
+ {
261
+ "source": 15,
262
+ "target": 16,
263
+ "value": 1
264
+ },
265
+ {
266
+ "source": 15,
267
+ "target": 1,
268
+ "value": 1
269
+ },
270
+ {
271
+ "source": 1,
272
+ "target": 16,
273
+ "value": 2
274
+ },
275
+ {
276
+ "source": 4,
277
+ "target": 5,
278
+ "value": 1
279
+ },
280
+ {
281
+ "source": 5,
282
+ "target": 17,
283
+ "value": 1
284
+ },
285
+ {
286
+ "source": 1,
287
+ "target": 17,
288
+ "value": 3
289
+ },
290
+ {
291
+ "source": 4,
292
+ "target": 17,
293
+ "value": 3
294
+ },
295
+ {
296
+ "source": 4,
297
+ "target": 0,
298
+ "value": 1
299
+ },
300
+ {
301
+ "source": 0,
302
+ "target": 17,
303
+ "value": 1
304
+ },
305
+ {
306
+ "source": 17,
307
+ "target": 16,
308
+ "value": 3
309
+ },
310
+ {
311
+ "source": 15,
312
+ "target": 17,
313
+ "value": 1
314
+ },
315
+ {
316
+ "source": 4,
317
+ "target": 16,
318
+ "value": 2
319
+ },
320
+ {
321
+ "source": 17,
322
+ "target": 18,
323
+ "value": 1
324
+ },
325
+ {
326
+ "source": 4,
327
+ "target": 15,
328
+ "value": 1
329
+ },
330
+ {
331
+ "source": 1,
332
+ "target": 18,
333
+ "value": 1
334
+ }
335
+ ]
336
+ }
data/lib/wads/app.rb CHANGED
@@ -1,199 +1,57 @@
1
1
  require 'gosu'
2
- require_relative 'data_structures'
3
2
  require_relative 'widgets'
4
- require_relative 'version'
5
3
 
6
- include Wads
7
-
8
- class WadsSampleApp < Gosu::Window
9
-
10
- STOCKS_DATA_FILE = "./data/NASDAQ.csv"
11
- LOTTERY_DATA_FILE = "./data/Pick4_12_21_2020.txt"
12
-
13
- def initialize
14
- super(800, 600)
15
- self.caption = "Wads Sample App"
16
- @font = Gosu::Font.new(32)
17
- @title_font = Gosu::Font.new(38)
18
- @version_font = Gosu::Font.new(22)
19
- @small_font = Gosu::Font.new(22)
20
- @banner_image = Gosu::Image.new("./media/Banner.png")
21
- @display_widget = nil
22
- 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
+ end
23
20
 
24
- def parse_opts_and_run
25
- # Make help the default output if no args are specified
26
- if ARGV.length == 0
27
- ARGV[0] = "-h"
21
+ #
22
+ # This method must be invoked with any Wads::Widget instance. It then handles
23
+ # delegating all events and drawing all child widgets.
24
+ #
25
+ def set_display(widget)
26
+ @main_widget = widget
28
27
  end
29
28
 
30
- opts = SampleAppCommand.new.parse.run
31
- if opts[:stocks]
32
- stats = process_stock_data
33
- if opts[:gui]
34
- @display_widget = SampleStocksDisplay.new(@small_font, stats)
35
- show
36
- else
37
- stats.report(Date::DAYNAMES[1..5])
38
- end
39
-
40
- elsif opts[:lottery]
41
- process_lottery_data
42
- else
43
- puts " "
44
- puts "Select one of the following sample analysis options"
45
- puts "-s Run sample stocks analysis"
46
- puts "-l Run sample analysis of lottery numbers"
47
- puts " "
48
- exit
29
+ def get_display
30
+ @main_widget
49
31
  end
50
32
 
51
- end
52
-
53
- def update
54
- # TODO
55
- end
33
+ def update
34
+ @main_widget.update(@update_count, mouse_x, mouse_y)
35
+ @update_count = @update_count + 1
36
+ end
56
37
 
57
- def draw
58
- draw_banner
59
- @display_widget.draw
60
- end
61
-
62
- def draw_banner
63
- @banner_image.draw(1,1,1,0.9,0.9)
64
- @title_font.draw_text("Wads Sample App", 10, 20, 2, 1, 1, Gosu::Color::WHITE)
65
- @version_font.draw_text("Version #{Wads::VERSION}", 13, 54, 2, 1, 1, Gosu::Color::WHITE)
66
- end
67
-
68
- def button_down id
69
- close if id == Gosu::KbEscape
70
- # Delegate button events to the primary display widget
71
- result = @display_widget.button_down id, mouse_x, mouse_y
72
- if result.close_widget
73
- close
38
+ def draw
39
+ @main_widget.draw
74
40
  end
75
- end
76
-
77
- def process_stock_data
78
- # The data file comes from https://finance.yahoo.com
79
- # The format of this file is as follows:
80
- #
81
- # Date,Open,High,Low,Close,Adj Close,Volume
82
- # 2000-01-03,4186.189941,4192.189941,3989.709961,4131.149902,4131.149902,1510070000
83
- # 2000-01-04,4020.000000,4073.250000,3898.229980,3901.689941,3901.689941,1511840000
84
- # ...
85
- # 2020-12-30,12906.509766,12924.929688,12857.759766,12870.000000,12870.000000,5292210000
86
- # 2020-12-31,12877.089844,12902.070313,12821.230469,12888.280273,12888.280273,4771390000
87
-
88
- stats = Stats.new("NASDAQ")
89
- previous_close = nil
90
-
91
- puts "Read the data file #{STOCKS_DATA_FILE}"
92
- File.readlines(STOCKS_DATA_FILE).each do |line|
93
- line = line.chomp # remove the carriage return
94
-
95
- # Ignore header and any empty lines, process numeric data lines
96
- if line.length > 0 and line[0].match(/[0-9]/)
97
- values = line.split(",")
98
- date = Date.strptime(values[0], "%Y-%m-%d")
99
- weekday = Date::DAYNAMES[date.wday]
100
-
101
- open_value = values[1].to_f
102
- close_value = values[4].to_f
103
41
 
104
- if previous_close.nil?
105
- # Just use the first day to set the baseline
106
- previous_close = close_value
107
- else
108
- change_from_previous_close = close_value - previous_close
109
- change_intraday = close_value - open_value
110
- change_overnight = open_value - previous_close
111
-
112
- change_percent = change_from_previous_close / previous_close
113
-
114
- stats.add(weekday, change_percent)
115
- stats.add("#{weekday} prev close", change_from_previous_close)
116
- stats.add("#{weekday} intraday", change_intraday)
117
- stats.add("#{weekday} overnight", change_overnight)
118
-
119
- previous_close = close_value
42
+ def button_down id
43
+ close if id == Gosu::KbEscape
44
+ # Delegate button events to the primary display widget
45
+ result = @main_widget.button_down id, mouse_x, mouse_y
46
+ if not result.nil? and result.is_a? WidgetResult
47
+ if result.close_widget
48
+ close
120
49
  end
121
50
  end
122
51
  end
123
-
124
- stats
125
- end
126
-
127
- def process_lottery_data
128
- puts "The lottery example has not been implemented yet"
129
- puts "however the data is available in the data directory,"
130
- puts "and the same pattern used in the stocks example can"
131
- puts "be applied here."
132
- end
133
- end
134
-
135
- class SampleStocksDisplay < Widget
136
- attr_accessor :stats
137
-
138
- def initialize(font, stats)
139
- super(10, 100, COLOR_HEADER_BRIGHT_BLUE)
140
- set_dimensions(780, 500)
141
- set_font(font)
142
- add_child(Document.new(sample_content, x + 5, y + 5, @width, @height, @font))
143
- @exit_button = Button.new("Exit", 380, bottom_edge - 30, @font)
144
- add_child(@exit_button)
145
-
146
- @stats = stats
147
- @data_table = SingleSelectTable.new(@x + 5, @y + 100, # top left corner
148
- 770, 200, # width, height
149
- ["Day", "Min", "Avg", "StdDev", "Max", "p10", "p90"], # column headers
150
- @font, COLOR_WHITE) # font and text color
151
- @data_table.selected_color = COLOR_LIGHT_GRAY
152
- Date::DAYNAMES[1..5].each do |day|
153
- min = format_percent(@stats.min(day))
154
- avg = format_percent(@stats.average(day))
155
- std = format_percent(@stats.std_dev(day))
156
- max = format_percent(@stats.max(day))
157
- p10 = format_percent(@stats.percentile(day, 0.1))
158
- p90 = format_percent(@stats.percentile(day, 0.90))
159
- @data_table.add_row([day, min, avg, std, max, p10, p90], COLOR_HEADER_BRIGHT_BLUE)
160
- end
161
- add_child(@data_table)
162
- @selection_text = nil
163
- end
164
-
165
- def format_percent(val)
166
- "#{(val * 100).round(3)}%"
167
- end
168
-
169
- def sample_content
170
- <<~HEREDOC
171
- This sample stock analysis uses NASDAQ data from https://finance.yahoo.com looking
172
- at closing data through the years 2000 to 2020. The percent gain or loss is broken
173
- down per day, as shown in the table below.
174
- HEREDOC
175
- end
176
-
177
- def render
178
- if @selection_text
179
- @selection_text.draw
52
+
53
+ def button_up id
54
+ @main_widget.button_up id, mouse_x, mouse_y
180
55
  end
181
56
  end
182
-
183
- def button_down id, mouse_x, mouse_y
184
- if id == Gosu::MsLeft
185
- if @exit_button.contains_click(mouse_x, mouse_y)
186
- return WidgetResult.new(true)
187
- elsif @data_table.contains_click(mouse_x, mouse_y)
188
- val = @data_table.set_selected_row(mouse_y, 0)
189
- if val.nil?
190
- # nothing to do
191
- else
192
- @selection_text = Text.new("You selected #{val}, a great day!",
193
- x + 5, y + 400, @font)
194
- end
195
- end
196
- end
197
- WidgetResult.new(false)
198
- end
199
- end
57
+ end