wads 0.1.2 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/wads.rb CHANGED
@@ -4,4 +4,4 @@ require_relative "wads/version"
4
4
  require_relative "wads/data_structures"
5
5
  require_relative "wads/widgets"
6
6
  require_relative "wads/textinput"
7
-
7
+ require_relative "wads/app"
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/run-graph ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ruby samples/graph.rb
data/run-star-wars ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ruby samples/star_wars.rb
data/run-stocks ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ruby samples/stocks.rb
data/run-theme-test ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ruby samples/theme_test.rb $1 $2 $3
@@ -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
@@ -0,0 +1,34 @@
1
+ require 'gosu'
2
+
3
+ class AppWindow < Gosu::Window
4
+ def initialize
5
+ super(800, 600)
6
+ self.caption = 'Bouncing Ball'
7
+ @img = Gosu::Image.new("media/CircleYellow.png")
8
+ @x = 400
9
+ @y = 300
10
+ @delta_x = 2
11
+ @delta_y = 2
12
+ end
13
+
14
+ def update
15
+ @x = @x + @delta_x
16
+ @y = @y + @delta_y
17
+ if @x < 0
18
+ @delta_x = 2
19
+ elsif @x > 800 - @img.width
20
+ @delta_x = -2
21
+ end
22
+ if @y < 0
23
+ @delta_y = 2
24
+ elsif @y > 600 - @img.height
25
+ @delta_y = -2
26
+ end
27
+ end
28
+
29
+ def draw
30
+ @img.draw @x, @y, 1
31
+ end
32
+ end
33
+
34
+ AppWindow.new.show
@@ -0,0 +1,16 @@
1
+ require 'gosu'
2
+
3
+ class GameWindow < Gosu::Window
4
+ def initialize
5
+ super(400, 300)
6
+ self.caption = 'Hello World App'
7
+ @font = Gosu::Font.new(24)
8
+ end
9
+
10
+ def draw
11
+ @font.draw_text("Hello World", 144, 120, 1)
12
+ end
13
+ end
14
+
15
+ window = GameWindow.new
16
+ window.show
@@ -0,0 +1,121 @@
1
+ require 'gosu'
2
+
3
+ AVERAGE_RESPONSE_TIME = 284.to_f # Per humanbenchmark.com, in milliseconds
4
+ GAME_STATE_PROMPT_TO_START = 0
5
+ GAME_STATE_RED = 1
6
+ GAME_STATE_YELLOW = 2
7
+ GAME_STATE_GREEN = 3
8
+ GAME_STATE_OVER = 4
9
+
10
+ class AppWindow < Gosu::Window
11
+ def initialize
12
+ super(600, 300)
13
+ self.caption = 'Reaction Time Game'
14
+ @update_count = 0
15
+ @next_light_count = 0
16
+ @messages = []
17
+ @font = Gosu::Font.new(32)
18
+ @red_light = Gosu::Image.new("media/CircleRed.png")
19
+ @yellow_light = Gosu::Image.new("media/CircleYellow.png")
20
+ @green_light = Gosu::Image.new("media/CircleGreen.png")
21
+ # game state 0 1 2 3 4
22
+ # state Prompt Red Yellow Green Game over
23
+ @traffic_light_images = [@red_light, @red_light, @yellow_light, @green_light, @green_light]
24
+ @traffic_light_color = GAME_STATE_PROMPT_TO_START
25
+ end
26
+
27
+ def update
28
+ @update_count = @update_count + 1
29
+ @traffic_light_image = @traffic_light_images[@traffic_light_color]
30
+
31
+ # Once the game is started, the lights progress automatically until green
32
+ if @traffic_light_color > GAME_STATE_PROMPT_TO_START
33
+ next_traffic_light unless @update_count < @next_light_count
34
+ @button_text = "Click when the light turns green" unless @traffic_light_color > GAME_STATE_GREEN
35
+ end
36
+ end
37
+
38
+ def draw
39
+ @font.draw_text("Reaction Time Game", 180, 10, 1)
40
+ @traffic_light_image.draw(248, 60, 1)
41
+
42
+ if @traffic_light_color == GAME_STATE_PROMPT_TO_START
43
+ @font.draw_text("Press 'p' to play", 200, 200, 1)
44
+ elsif @traffic_light_color == GAME_STATE_OVER
45
+ @font.draw_text("Press 'p' to play again", 50, 200, 1)
46
+ else
47
+ draw_button(@button_text) unless @button_text.nil?
48
+ end
49
+
50
+ y = 232
51
+ @messages.each do |msg|
52
+ @font.draw_text(msg, 50, y, 1)
53
+ y = y + 32
54
+ end
55
+ end
56
+
57
+ def button_down id
58
+ close if id == Gosu::KbEscape
59
+
60
+ if @traffic_light_color == GAME_STATE_PROMPT_TO_START or @traffic_light_color == GAME_STATE_OVER
61
+ if id == Gosu::KbP
62
+ next_traffic_light
63
+ end
64
+ return
65
+ end
66
+
67
+ if id == Gosu::MsLeft and button_contains_click
68
+ if @traffic_light_color < GAME_STATE_YELLOW
69
+ next_traffic_light
70
+ elsif @traffic_light_color == GAME_STATE_YELLOW
71
+ @traffic_light_color = GAME_STATE_OVER
72
+ @messages = ["Sorry, you were too early"]
73
+ @button_text = "Click to play again"
74
+ elsif @traffic_light_color == GAME_STATE_GREEN
75
+ @button_text = nil
76
+ time_since_green = ((Time.now - @mark_time) * 1000.to_f).round(3)
77
+ @messages = ["Response time: #{time_since_green} ms"]
78
+ diff_from_average = (((time_since_green - AVERAGE_RESPONSE_TIME) / AVERAGE_RESPONSE_TIME) * 100).round
79
+ if diff_from_average > 0
80
+ @messages << "#{diff_from_average}% slower than the average human"
81
+ elsif diff_from_average < 0
82
+ @messages << "#{-diff_from_average}% faster than the average human"
83
+ else
84
+ @messages << "Wow, that is exactly the human average."
85
+ end
86
+ @button_text = "Click to play again"
87
+ @traffic_light_color = GAME_STATE_OVER
88
+ else
89
+ @traffic_light_color = GAME_STATE_PROMPT_TO_START
90
+ @messages = []
91
+ end
92
+ end
93
+ end
94
+
95
+ def next_traffic_light
96
+ return unless @traffic_light_color < GAME_STATE_GREEN
97
+ @traffic_light_color = @traffic_light_color + 1
98
+ @mark_time = Time.now
99
+ @next_light_count = @update_count + 60 + rand(100)
100
+ end
101
+
102
+ def button_contains_click
103
+ mouse_x > 100 and mouse_x < 500 and mouse_y > 198 and mouse_y < 232
104
+ end
105
+
106
+ def draw_button(text)
107
+ text_width = @font.text_width(text)
108
+ draw_box(100, 198, 500, 232)
109
+ text_x = (600 - text_width) / 2
110
+ @font.draw_text(@button_text, text_x, 200, 1)
111
+ end
112
+
113
+ def draw_box(x1, y1, x2, y2, color = Gosu::Color::WHITE)
114
+ Gosu::draw_line x1, y1, color, x2, y1, color, 1
115
+ Gosu::draw_line x1, y1, color, x1, y2, color, 1
116
+ Gosu::draw_line x1, y2, color, x2, y2, color, 1
117
+ Gosu::draw_line x2, y1, color, x2, y2, color, 1
118
+ end
119
+ end
120
+
121
+ AppWindow.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(WadsAquaTheme.new)
126
+ SampleStocksApp.new.show