wads 0.1.0 → 0.2.0
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 +4 -4
- data/CHANGELOG.md +16 -2
- data/README.md +62 -2
- data/data/sample_graph.csv +11 -0
- data/data/starwars-episode-4-interactions.json +336 -0
- data/lib/wads/app.rb +41 -183
- data/lib/wads/data_structures.rb +705 -17
- data/lib/wads/textinput.rb +66 -15
- data/lib/wads/version.rb +1 -1
- data/lib/wads/widgets.rb +2827 -271
- data/lib/wads.rb +1 -1
- data/media/CircleAlpha.png +0 -0
- data/media/CircleAqua.png +0 -0
- data/media/CircleBlue.png +0 -0
- data/media/CircleGray.png +0 -0
- data/media/CircleGreen.png +0 -0
- data/media/CirclePurple.png +0 -0
- data/media/CircleRed.png +0 -0
- data/media/CircleWhite.png +0 -0
- data/media/CircleYellow.png +0 -0
- data/media/SampleGraph.png +0 -0
- data/media/StocksSample.png +0 -0
- data/media/WadsScreenshot.png +0 -0
- data/run-graph +3 -0
- data/run-star-wars +3 -0
- data/run-stocks +3 -0
- data/run-theme-test +3 -0
- data/samples/basic_gosu_with_graph_widget.rb +66 -0
- data/samples/graph.rb +72 -0
- data/samples/star_wars.rb +112 -0
- data/samples/stocks.rb +126 -0
- data/samples/theme_test.rb +256 -0
- metadata +26 -5
- data/run-sample-app +0 -3
- data/sample_app.rb +0 -52
data/lib/wads.rb
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/media/CircleRed.png
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/run-graph
ADDED
data/run-star-wars
ADDED
data/run-stocks
ADDED
data/run-theme-test
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'gosu'
|
2
|
+
require_relative '../lib/wads'
|
3
|
+
|
4
|
+
include 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 BasicGosuAppWithGraph < Gosu::Window
|
12
|
+
def initialize
|
13
|
+
super(800, 600)
|
14
|
+
self.caption = 'Basic Gosu App with Graph Widget'
|
15
|
+
@font = Gosu::Font.new(22)
|
16
|
+
@graph_widget = GraphWidget.new(10, 110, # x, y coordinate of top left corder
|
17
|
+
780, 480, # width x height
|
18
|
+
create_graph,
|
19
|
+
GRAPH_DISPLAY_EXPLORER)
|
20
|
+
@update_count = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_graph
|
24
|
+
g = Graph.new
|
25
|
+
g.add("A")
|
26
|
+
g.add("B")
|
27
|
+
g.add("C")
|
28
|
+
g.add("D")
|
29
|
+
g.add("E")
|
30
|
+
g.add("F")
|
31
|
+
g.connect("A", "B")
|
32
|
+
g.connect("A", "C")
|
33
|
+
g.connect("B", "D")
|
34
|
+
g.connect("B", "E")
|
35
|
+
g.connect("E", "F")
|
36
|
+
g
|
37
|
+
end
|
38
|
+
|
39
|
+
def update
|
40
|
+
# Calling handle_update on the grpah widget is required if you want interactivity
|
41
|
+
# specifically, for drag and drop of the nodes
|
42
|
+
@update_count = @update_count + 1
|
43
|
+
@graph_widget.handle_update @update_count, mouse_x, mouse_y
|
44
|
+
end
|
45
|
+
|
46
|
+
def draw
|
47
|
+
@font.draw_text("Sample App with Wads Graph Widget", 240, 49, 1, 1, 1, COLOR_WHITE)
|
48
|
+
@graph_widget.draw
|
49
|
+
end
|
50
|
+
|
51
|
+
def button_down id
|
52
|
+
close if id == Gosu::KbEscape
|
53
|
+
# Delegate button events to the graph widget if you want the
|
54
|
+
# user to be able to interact with it
|
55
|
+
@graph_widget.button_down id, mouse_x, mouse_y
|
56
|
+
end
|
57
|
+
|
58
|
+
def button_up id
|
59
|
+
# Delegate button events to the graph widget if you want the
|
60
|
+
# user to be able to interact with it
|
61
|
+
@graph_widget.button_up id, mouse_x, mouse_y
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
BasicGosuAppWithGraph.new.show
|
data/samples/graph.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'gosu'
|
2
|
+
require_relative '../lib/wads'
|
3
|
+
|
4
|
+
include Wads
|
5
|
+
|
6
|
+
class SampleGraphApp < WadsApp
|
7
|
+
|
8
|
+
SAMPLE_GRAPH_DEFINITION_FILE = "./data/sample_graph.csv"
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super(800, 600, "Wads Sample Graph App", GraphDisplay.new(create_sample_graph))
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Below are three different ways you can construct a graph
|
16
|
+
#
|
17
|
+
def create_sample_graph
|
18
|
+
g = Graph.new
|
19
|
+
g.add("A")
|
20
|
+
g.add("B")
|
21
|
+
g.add("C")
|
22
|
+
g.add("D")
|
23
|
+
g.add("E")
|
24
|
+
g.add("F")
|
25
|
+
g.connect("A", "B")
|
26
|
+
g.connect("A", "C")
|
27
|
+
g.connect("B", "D")
|
28
|
+
g.connect("B", "E")
|
29
|
+
g.connect("E", "F")
|
30
|
+
g
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_sample_graph_using_nodes
|
34
|
+
root = Node.new("A")
|
35
|
+
b = root.add("B")
|
36
|
+
b.add("D")
|
37
|
+
b.add("E").add("F")
|
38
|
+
root.add("C")
|
39
|
+
Graph.new(root)
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_sample_graph_from_file
|
43
|
+
Graph.new(SAMPLE_GRAPH_DEFINITION_FILE)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class GraphDisplay < Widget
|
48
|
+
|
49
|
+
def initialize(graph)
|
50
|
+
super(0, 0, 800, 600)
|
51
|
+
set_layout(LAYOUT_TOP_MIDDLE_BOTTOM)
|
52
|
+
@graph = graph
|
53
|
+
|
54
|
+
image = get_layout.add_image("./media/Banner.png", { ARG_SECTION => SECTION_TOP})
|
55
|
+
image.add_text("Wads Sample App", 10, 20, nil, true)
|
56
|
+
image.add_text("Version #{Wads::VERSION}", 13, 54)
|
57
|
+
|
58
|
+
panel = get_layout.add_horizontal_panel({ ARG_SECTION => SECTION_BOTTOM})
|
59
|
+
panel.add_button("Exit", 0, panel.height - 30) do
|
60
|
+
WidgetResult.new(true)
|
61
|
+
end
|
62
|
+
panel.center_children
|
63
|
+
panel.disable_border
|
64
|
+
|
65
|
+
@graph_display = get_layout.add_graph_display(@graph, GRAPH_DISPLAY_EXPLORER,
|
66
|
+
{ ARG_SECTION => SECTION_CENTER})
|
67
|
+
disable_border
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#WadsConfig.instance.set_current_theme(WadsNoIconTheme.new)
|
72
|
+
SampleGraphApp.new.show
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'gosu'
|
2
|
+
require 'json'
|
3
|
+
require_relative '../lib/wads'
|
4
|
+
|
5
|
+
include Wads
|
6
|
+
|
7
|
+
class SampleStarWarsApp < WadsApp
|
8
|
+
|
9
|
+
STAR_WARS_DATA_FILE = "./data/starwars-episode-4-interactions.json"
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super(800, 800, "Wads Sample Star Wars App", StarWarsDisplay.new(process_star_wars_data))
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_star_wars_data
|
16
|
+
star_wars_json = File.read(STAR_WARS_DATA_FILE)
|
17
|
+
data_hash = JSON.parse(star_wars_json)
|
18
|
+
characters = data_hash['nodes']
|
19
|
+
interactions = data_hash['links']
|
20
|
+
|
21
|
+
# The interactions in the data set reference the characters by their
|
22
|
+
# zero based index, so we keep a reference in our graph by index.
|
23
|
+
# The character's value is the number of scenes in which they appear.
|
24
|
+
graph = Graph.new
|
25
|
+
characters.each do |character|
|
26
|
+
node_tags = {}
|
27
|
+
node_color_str = character['colour']
|
28
|
+
# This is a bit of a hack, but our background is black so black text
|
29
|
+
# will not show up. Change this to white
|
30
|
+
if node_color_str == "#000000"
|
31
|
+
node_color_str = "#FFFFFF"
|
32
|
+
end
|
33
|
+
# Convert hex string (ex. "#EE00AA") into int hex representation
|
34
|
+
# understood by Gosu color (ex. 0xFFEE00AA)
|
35
|
+
node_color = "0xFF#{node_color_str[1..-1]}".to_i(16)
|
36
|
+
node_tags['color'] = node_color
|
37
|
+
graph.add_node(Node.new(character['name'], character['value'], node_tags))
|
38
|
+
end
|
39
|
+
interactions.each do |interaction|
|
40
|
+
character_one = graph.node_by_index(interaction['source'])
|
41
|
+
character_two = graph.node_by_index(interaction['target'])
|
42
|
+
number_of_scenes_together = interaction['value']
|
43
|
+
edge_tags = {}
|
44
|
+
edge_tags["scenes"] = number_of_scenes_together
|
45
|
+
graph.add_edge(character_one, character_two, edge_tags)
|
46
|
+
end
|
47
|
+
graph
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class StarWarsDisplay < Widget
|
52
|
+
attr_accessor :graph
|
53
|
+
|
54
|
+
def initialize(graph)
|
55
|
+
super(0, 0, 800, 800)
|
56
|
+
set_layout(LAYOUT_TOP_MIDDLE_BOTTOM)
|
57
|
+
@graph = graph
|
58
|
+
|
59
|
+
image = get_layout.add_image("./media/Banner.png", { ARG_SECTION => SECTION_TOP})
|
60
|
+
image.add_text("Wads Sample App", 10, 20, nil, true)
|
61
|
+
image.add_text("Version #{Wads::VERSION}", 13, 54)
|
62
|
+
|
63
|
+
get_layout.add_document(sample_content, { ARG_SECTION => SECTION_CENTER})
|
64
|
+
|
65
|
+
panel = get_layout.add_horizontal_panel({ ARG_SECTION => SECTION_BOTTOM})
|
66
|
+
panel.add_button("Exit", 0, panel.height - 30) do
|
67
|
+
WidgetResult.new(true)
|
68
|
+
end
|
69
|
+
panel.center_children
|
70
|
+
panel.disable_border
|
71
|
+
|
72
|
+
@data_table = get_layout.add_single_select_table(
|
73
|
+
["Character", "Number of Scenes"], 4, { ARG_SECTION => SECTION_CENTER})
|
74
|
+
|
75
|
+
@graph.node_list.each do |character|
|
76
|
+
@data_table.add_row([character.name, character.value], character.get_tag(ARG_COLOR))
|
77
|
+
end
|
78
|
+
@graph_display = get_layout.add_graph_display(@graph, GRAPH_DISPLAY_EXPLORER,
|
79
|
+
{ ARG_SECTION => SECTION_CENTER})
|
80
|
+
|
81
|
+
disable_border
|
82
|
+
end
|
83
|
+
|
84
|
+
def sample_content
|
85
|
+
<<~HEREDOC
|
86
|
+
This sample analysis shows the interactions between characters in the Star Wars
|
87
|
+
Episode 4: A New Hope. Click on a character to see more detail.
|
88
|
+
HEREDOC
|
89
|
+
end
|
90
|
+
|
91
|
+
def handle_key_press id, mouse_x, mouse_y
|
92
|
+
if id == Gosu::KbUp
|
93
|
+
@data_table.scroll_up
|
94
|
+
elsif id == Gosu::KbDown
|
95
|
+
@data_table.scroll_down
|
96
|
+
end
|
97
|
+
WidgetResult.new(false)
|
98
|
+
end
|
99
|
+
|
100
|
+
def handle_mouse_down mouse_x, mouse_y
|
101
|
+
if @data_table.contains_click(mouse_x, mouse_y)
|
102
|
+
val = @data_table.set_selected_row(mouse_y, 0)
|
103
|
+
if not val.nil?
|
104
|
+
node = @graph.find_node(val)
|
105
|
+
#@graph_display.set_center_node(node, 2)
|
106
|
+
@graph_display.set_explorer_display(node)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
SampleStarWarsApp.new.show
|
data/samples/stocks.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'gosu'
|
2
|
+
require_relative '../lib/wads'
|
3
|
+
|
4
|
+
include Wads
|
5
|
+
|
6
|
+
class SampleStocksApp < WadsApp
|
7
|
+
|
8
|
+
STOCKS_DATA_FILE = "./data/NASDAQ.csv"
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super(800, 600, "Wads Sample Stocks App", StocksDisplay.new(process_stock_data))
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_stock_data
|
15
|
+
# The data file comes from https://finance.yahoo.com
|
16
|
+
# The format of this file is as follows:
|
17
|
+
#
|
18
|
+
# Date,Open,High,Low,Close,Adj Close,Volume
|
19
|
+
# 2000-01-03,4186.189941,4192.189941,3989.709961,4131.149902,4131.149902,1510070000
|
20
|
+
# 2000-01-04,4020.000000,4073.250000,3898.229980,3901.689941,3901.689941,1511840000
|
21
|
+
# ...
|
22
|
+
# 2020-12-30,12906.509766,12924.929688,12857.759766,12870.000000,12870.000000,5292210000
|
23
|
+
# 2020-12-31,12877.089844,12902.070313,12821.230469,12888.280273,12888.280273,4771390000
|
24
|
+
|
25
|
+
stats = Stats.new("NASDAQ")
|
26
|
+
previous_close = nil
|
27
|
+
|
28
|
+
puts "Read the data file #{STOCKS_DATA_FILE}"
|
29
|
+
File.readlines(STOCKS_DATA_FILE).each do |line|
|
30
|
+
line = line.chomp # remove the carriage return
|
31
|
+
|
32
|
+
# Ignore header and any empty lines, process numeric data lines
|
33
|
+
if line.length > 0 and line[0].match(/[0-9]/)
|
34
|
+
values = line.split(",")
|
35
|
+
date = Date.strptime(values[0], "%Y-%m-%d")
|
36
|
+
weekday = Date::DAYNAMES[date.wday]
|
37
|
+
|
38
|
+
open_value = values[1].to_f
|
39
|
+
close_value = values[4].to_f
|
40
|
+
|
41
|
+
if previous_close.nil?
|
42
|
+
# Just use the first day to set the baseline
|
43
|
+
previous_close = close_value
|
44
|
+
else
|
45
|
+
change_from_previous_close = close_value - previous_close
|
46
|
+
change_intraday = close_value - open_value
|
47
|
+
change_overnight = open_value - previous_close
|
48
|
+
|
49
|
+
change_percent = change_from_previous_close / previous_close
|
50
|
+
|
51
|
+
stats.add(weekday, change_percent)
|
52
|
+
stats.add("#{weekday} prev close", change_from_previous_close)
|
53
|
+
stats.add("#{weekday} intraday", change_intraday)
|
54
|
+
stats.add("#{weekday} overnight", change_overnight)
|
55
|
+
|
56
|
+
previous_close = close_value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
stats
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class StocksDisplay < Widget
|
66
|
+
|
67
|
+
def initialize(stats)
|
68
|
+
super(0, 0, 800, 600)
|
69
|
+
set_layout(LAYOUT_TOP_MIDDLE_BOTTOM)
|
70
|
+
|
71
|
+
image = get_layout.add_image("./media/Banner.png", { ARG_SECTION => SECTION_TOP})
|
72
|
+
image.add_text("Wads Sample App", 10, 20, nil, true)
|
73
|
+
image.add_text("Version #{Wads::VERSION}", 13, 54)
|
74
|
+
|
75
|
+
get_layout.add_document(sample_content, { ARG_SECTION => SECTION_CENTER})
|
76
|
+
|
77
|
+
panel = get_layout.add_horizontal_panel({ ARG_SECTION => SECTION_BOTTOM})
|
78
|
+
panel.add_button("Exit", 0, panel.height - 30) do
|
79
|
+
WidgetResult.new(true)
|
80
|
+
end
|
81
|
+
panel.center_children
|
82
|
+
|
83
|
+
@data_table = get_layout.add_single_select_table(
|
84
|
+
["Day", "Min", "Avg", "StdDev", "Max", "p10", "p90"], 5,
|
85
|
+
{ ARG_SECTION => SECTION_CENTER})
|
86
|
+
Date::DAYNAMES[1..5].each do |day|
|
87
|
+
min = format_percent(stats.min(day))
|
88
|
+
avg = format_percent(stats.average(day))
|
89
|
+
std = format_percent(stats.std_dev(day))
|
90
|
+
max = format_percent(stats.max(day))
|
91
|
+
p10 = format_percent(stats.percentile(day, 0.1))
|
92
|
+
p90 = format_percent(stats.percentile(day, 0.90))
|
93
|
+
@data_table.add_row([day, min, avg, std, max, p10, p90])
|
94
|
+
end
|
95
|
+
|
96
|
+
@selection_text = nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def format_percent(val)
|
100
|
+
"#{(val * 100).round(3)}%"
|
101
|
+
end
|
102
|
+
|
103
|
+
def sample_content
|
104
|
+
<<~HEREDOC
|
105
|
+
This sample stock analysis uses NASDAQ data from https://finance.yahoo.com looking
|
106
|
+
at closing data through the years 2000 to 2020. The percent gain or loss is broken
|
107
|
+
down per day, as shown in the table below.
|
108
|
+
HEREDOC
|
109
|
+
end
|
110
|
+
|
111
|
+
def render
|
112
|
+
@selection_text.draw unless @selection_text.nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
def handle_mouse_down mouse_x, mouse_y
|
116
|
+
if @data_table.contains_click(mouse_x, mouse_y)
|
117
|
+
val = @data_table.set_selected_row(mouse_y, 0)
|
118
|
+
if not val.nil?
|
119
|
+
@selection_text = Text.new(relative_x(5), relative_y(500), "You selected #{val}, a great day!")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
WadsConfig.instance.set_current_theme(WadsBrightTheme.new)
|
126
|
+
SampleStocksApp.new.show
|