wxruby3 1.7.0 → 1.8.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.
Files changed (220) hide show
  1. checksums.yaml +4 -4
  2. data/INSTALL.md +27 -2
  3. data/README.md +15 -15
  4. data/ext/wxruby3/include/wxRubyApp.h +145 -46
  5. data/ext/wxruby3/include/wxruby-ComboPopup.h +25 -8
  6. data/ext/wxruby3/include/wxruby-GCTracking.h +262 -0
  7. data/ext/wxruby3/include/wxruby-MBConv.h +190 -0
  8. data/ext/wxruby3/include/wxruby-SharedEventHandler.h +183 -0
  9. data/ext/wxruby3/include/wxruby-SharedPtr.h +104 -0
  10. data/ext/wxruby3/include/wxruby-runtime.h +29 -0
  11. data/ext/wxruby3/swig/custom/rubytracking.swg +8 -22
  12. data/ext/wxruby3/swig/custom/wx_ruby_shared_ptr.i +434 -0
  13. data/ext/wxruby3/swig/custom/wx_shared_ptr.i +431 -0
  14. data/ext/wxruby3/swig/mark_free_impl.i +7 -132
  15. data/ext/wxruby3/swig/memory_management.i +8 -44
  16. data/ext/wxruby3/swig/wx.i +46 -73
  17. data/lib/wx/core/const.rb +0 -1
  18. data/lib/wx/core/context_help.rb +17 -0
  19. data/lib/wx/core/cursor.rb +1 -0
  20. data/lib/wx/core/helpcontrollerhelpprovider.rb +10 -2
  21. data/lib/wx/core/mbconv.rb +11 -0
  22. data/lib/wx/core/media_ctrl.rb +31 -0
  23. data/lib/wx/core/top_level_window.rb +7 -0
  24. data/lib/wx/core.rb +6 -0
  25. data/lib/wx/doc/art_provider.rb +17 -0
  26. data/lib/wx/doc/context_help.rb +42 -0
  27. data/lib/wx/doc/core.rb +0 -5
  28. data/lib/wx/doc/functions.rb +10 -0
  29. data/lib/wx/doc/gen/affine_matrix2d.rb +2 -0
  30. data/lib/wx/doc/gen/art_provider.rb +12 -0
  31. data/lib/wx/doc/gen/aui/aui_notebook_event.rb +15 -15
  32. data/lib/wx/doc/gen/aui/aui_pane_info.rb +8 -8
  33. data/lib/wx/doc/gen/aui/aui_tool_bar_event.rb +5 -5
  34. data/lib/wx/doc/gen/book_ctrl_base.rb +2 -1
  35. data/lib/wx/doc/gen/calendar_event.rb +5 -5
  36. data/lib/wx/doc/gen/choicebook.rb +2 -2
  37. data/lib/wx/doc/gen/collapsible_pane.rb +1 -1
  38. data/lib/wx/doc/gen/colour_dialog.rb +1 -1
  39. data/lib/wx/doc/gen/colour_picker_event.rb +3 -3
  40. data/lib/wx/doc/gen/context_help_button.rb +0 -53
  41. data/lib/wx/doc/gen/core.rb +4 -4
  42. data/lib/wx/doc/gen/credential_entry_dialog.rb +2 -2
  43. data/lib/wx/doc/gen/date_event.rb +2 -2
  44. data/lib/wx/doc/gen/dial_up_event.rb +2 -2
  45. data/lib/wx/doc/gen/dir_filter_list_ctrl.rb +2 -2
  46. data/lib/wx/doc/gen/event.rb +58 -58
  47. data/lib/wx/doc/gen/ext_help_controller.rb +62 -16
  48. data/lib/wx/doc/gen/file_ctrl.rb +4 -4
  49. data/lib/wx/doc/gen/file_dir_picker_event.rb +2 -2
  50. data/lib/wx/doc/gen/file_system.rb +1 -1
  51. data/lib/wx/doc/gen/find_dialog_event.rb +5 -5
  52. data/lib/wx/doc/gen/font_picker_event.rb +1 -1
  53. data/lib/wx/doc/gen/grid/grid_ctrl.rb +67 -32
  54. data/lib/wx/doc/gen/header_ctrl_event.rb +13 -13
  55. data/lib/wx/doc/gen/help_controller.rb +45 -2
  56. data/lib/wx/doc/gen/html/html_help_controller.rb +74 -32
  57. data/lib/wx/doc/gen/html/html_help_window.rb +3 -3
  58. data/lib/wx/doc/gen/html/html_window.rb +4 -4
  59. data/lib/wx/doc/gen/hyperlink_ctrl.rb +1 -1
  60. data/lib/wx/doc/gen/list_ctrl.rb +22 -22
  61. data/lib/wx/doc/gen/listbook.rb +2 -2
  62. data/lib/wx/doc/gen/mb_conv.rb +10 -0
  63. data/lib/wx/doc/gen/media_event.rb +6 -6
  64. data/lib/wx/doc/gen/notebook.rb +2 -2
  65. data/lib/wx/doc/gen/pg/pg_validation_info.rb +14 -14
  66. data/lib/wx/doc/gen/pg/property_grid_interface.rb +2 -2
  67. data/lib/wx/doc/gen/rbn/ribbon_art_provider.rb +19 -9
  68. data/lib/wx/doc/gen/rbn/ribbon_button_bar.rb +2 -2
  69. data/lib/wx/doc/gen/rbn/ribbon_gallery.rb +3 -3
  70. data/lib/wx/doc/gen/rbn/ribbon_panel.rb +2 -2
  71. data/lib/wx/doc/gen/rbn/ribbon_tool_bar.rb +2 -2
  72. data/lib/wx/doc/gen/rt/event_list.rb +17 -0
  73. data/lib/wx/doc/gen/rt/shared_evt_handler.rb +14 -0
  74. data/lib/wx/doc/gen/rt/thread_event.rb +71 -0
  75. data/lib/wx/doc/gen/rtc/rich_text_ctrl.rb +19 -19
  76. data/lib/wx/doc/gen/sash_event.rb +2 -2
  77. data/lib/wx/doc/gen/sash_layout_window.rb +2 -2
  78. data/lib/wx/doc/gen/search_ctrl.rb +2 -2
  79. data/lib/wx/doc/gen/spin_ctrl.rb +2 -2
  80. data/lib/wx/doc/gen/splitter_window.rb +6 -6
  81. data/lib/wx/doc/gen/stc/styled_text_event.rb +34 -34
  82. data/lib/wx/doc/gen/task_bar_icon_event.rb +10 -10
  83. data/lib/wx/doc/gen/text_ctrl.rb +4 -4
  84. data/lib/wx/doc/gen/toggle_button.rb +1 -1
  85. data/lib/wx/doc/gen/toolbook.rb +2 -2
  86. data/lib/wx/doc/gen/top_level_window.rb +19 -0
  87. data/lib/wx/doc/gen/tree_ctrl.rb +21 -21
  88. data/lib/wx/doc/gen/treebook.rb +4 -4
  89. data/lib/wx/doc/gen/utils.rb +2 -2
  90. data/lib/wx/doc/gen/web/event_list.rb +95 -0
  91. data/lib/wx/doc/gen/web/web_view.rb +1136 -0
  92. data/lib/wx/doc/gen/web/web_view_event.rb +210 -0
  93. data/lib/wx/doc/gen/web/web_view_handler.rb +318 -0
  94. data/lib/wx/doc/gen/window.rb +1 -1
  95. data/lib/wx/doc/gen/wizard_event.rb +7 -7
  96. data/lib/wx/doc/html/html_help_controller.rb +23 -1
  97. data/lib/wx/doc/mbconv.rb +108 -0
  98. data/lib/wx/doc/rt/shared_event_handler.rb +49 -0
  99. data/lib/wx/doc/rt/thread_event.rb +28 -0
  100. data/lib/wx/doc/webview.rb +40 -0
  101. data/lib/wx/helpers.rb +1 -1
  102. data/lib/wx/html/htmlhelpcontroller.rb +10 -0
  103. data/lib/wx/keyword_defs.rb +11 -0
  104. data/lib/wx/rt/events/evt_list.rb +8 -0
  105. data/lib/wx/rt/require.rb +8 -0
  106. data/lib/wx/rt/thread_event.rb +14 -0
  107. data/lib/wx/rt.rb +16 -0
  108. data/lib/wx/version.rb +1 -1
  109. data/lib/wx/web/events/evt_list.rb +74 -0
  110. data/lib/wx/web/require.rb +8 -0
  111. data/lib/wx/web/webview.rb +106 -0
  112. data/lib/wx/web.rb +17 -0
  113. data/lib/wx/wxruby/cmd/setup.rb +15 -0
  114. data/lib/wx.rb +2 -0
  115. data/rakelib/configure.rb +24 -0
  116. data/rakelib/gem.rb +3 -2
  117. data/rakelib/install.rb +54 -27
  118. data/rakelib/lib/config/macosx.rb +7 -5
  119. data/rakelib/lib/config/mingw.rb +66 -5
  120. data/rakelib/lib/config/pkgman/mingw.rb +1 -1
  121. data/rakelib/lib/config/unixish.rb +4 -2
  122. data/rakelib/lib/config.rb +37 -3
  123. data/rakelib/lib/core/include/client_data.inc +38 -17
  124. data/rakelib/lib/core/include/funcall.inc +1 -1
  125. data/rakelib/lib/core/include/swigdirector.inc +3 -3
  126. data/rakelib/lib/core/include/swigrubyrun.inc +14 -26
  127. data/rakelib/lib/core/package.rb +0 -2
  128. data/rakelib/lib/director/app.rb +1 -8
  129. data/rakelib/lib/director/art_provider.rb +7 -3
  130. data/rakelib/lib/director/comboctrl.rb +0 -7
  131. data/rakelib/lib/director/context_help_button.rb +35 -2
  132. data/rakelib/lib/director/event_handler.rb +32 -28
  133. data/rakelib/lib/director/fs_file.rb +15 -10
  134. data/rakelib/lib/director/functions.rb +8 -0
  135. data/rakelib/lib/director/grid_cell_attr.rb +1 -3
  136. data/rakelib/lib/director/grid_cell_editor.rb +14 -12
  137. data/rakelib/lib/director/grid_cell_renderer.rb +11 -8
  138. data/rakelib/lib/director/grid_ctrl.rb +140 -121
  139. data/rakelib/lib/director/help_controller.rb +70 -2
  140. data/rakelib/lib/director/mb_conv.rb +30 -0
  141. data/rakelib/lib/director/menu.rb +92 -42
  142. data/rakelib/lib/director/menu_bar.rb +84 -45
  143. data/rakelib/lib/director/menu_item.rb +2 -2
  144. data/rakelib/lib/director/persistence_manager.rb +3 -2
  145. data/rakelib/lib/director/preview_frame.rb +2 -2
  146. data/rakelib/lib/director/richtext_buffer.rb +5 -2
  147. data/rakelib/lib/director/shared_evt_handler.rb +30 -0
  148. data/rakelib/lib/director/sizer.rb +8 -17
  149. data/rakelib/lib/director/sizer_item.rb +3 -4
  150. data/rakelib/lib/director/task_bar_icon.rb +7 -7
  151. data/rakelib/lib/director/thread_event.rb +33 -0
  152. data/rakelib/lib/director/validator.rb +3 -1
  153. data/rakelib/lib/director/variant.rb +16 -12
  154. data/rakelib/lib/director/webview.rb +166 -0
  155. data/rakelib/lib/director/webview_event.rb +37 -0
  156. data/rakelib/lib/director/webview_handler.rb +63 -0
  157. data/rakelib/lib/generate/doc/thread_event.yaml +20 -0
  158. data/rakelib/lib/generate/doc/web_view.yaml +135 -0
  159. data/rakelib/lib/generate/doc/web_view_event.yaml +38 -0
  160. data/rakelib/lib/generate/doc/web_view_handler.yaml +58 -0
  161. data/rakelib/lib/specs/interfaces.rb +12 -0
  162. data/rakelib/lib/typemap/common.rb +34 -1
  163. data/rakelib/lib/typemap/mb_conv.rb +58 -0
  164. data/rakelib/prepost.rake +23 -31
  165. data/rakelib/prepost.rb +20 -0
  166. data/samples/event/threaded.rb +295 -42
  167. data/samples/event/tn_threaded.png +0 -0
  168. data/samples/help/doc/back.gif +0 -0
  169. data/samples/help/doc/contents.gif +0 -0
  170. data/samples/help/doc/cshelp.txt +9 -0
  171. data/samples/help/doc/doc.chm +0 -0
  172. data/samples/help/doc/doc.cnt +8 -0
  173. data/samples/help/doc/doc.h +7 -0
  174. data/samples/help/doc/doc.hhc +40 -0
  175. data/samples/help/doc/doc.hhk +31 -0
  176. data/samples/help/doc/doc.hhp +33 -0
  177. data/samples/help/doc/doc.hpj +21 -0
  178. data/samples/help/doc/doc.htm +27 -0
  179. data/samples/help/doc/doc1.htm +24 -0
  180. data/samples/help/doc/doc2.htm +12 -0
  181. data/samples/help/doc/doc3.htm +12 -0
  182. data/samples/help/doc/doc4.htm +12 -0
  183. data/samples/help/doc/doc5.htm +14 -0
  184. data/samples/help/doc/forward.gif +0 -0
  185. data/samples/help/doc/up.gif +0 -0
  186. data/samples/help/doc.chm +0 -0
  187. data/samples/help/doc.chw +0 -0
  188. data/samples/help/doc.zip +0 -0
  189. data/samples/help/help.rb +352 -0
  190. data/samples/help/tn_help.png +0 -0
  191. data/samples/webview/handler_advanced.html +55 -0
  192. data/samples/webview/tn_webview.png +0 -0
  193. data/samples/webview/webview.rb +1264 -0
  194. data/tests/assets/handler_advanced.html +55 -0
  195. data/tests/assets/test.css +1 -0
  196. data/tests/assets/test.html +9 -0
  197. data/tests/assets/test.zip +0 -0
  198. data/tests/lib/text_entry_tests.rb +2 -2
  199. data/tests/lib/wxapp_runner.rb +40 -0
  200. data/tests/lib/wxframe_runner.rb +17 -2
  201. data/tests/test_art.rb +8 -8
  202. data/tests/test_clipboard.rb +4 -4
  203. data/tests/test_config.rb +6 -6
  204. data/tests/test_exceptions.rb +8 -6
  205. data/tests/test_ext_controls.rb +3 -3
  206. data/tests/test_file_dialog.rb +5 -5
  207. data/tests/test_font.rb +7 -7
  208. data/tests/test_grid_ctrl.rb +133 -0
  209. data/tests/test_help.rb +88 -0
  210. data/tests/test_intl.rb +1 -1
  211. data/tests/test_media_ctrl.rb +14 -6
  212. data/tests/test_menu.rb +94 -86
  213. data/tests/test_persistence.rb +1 -1
  214. data/tests/test_proof_check.rb +5 -5
  215. data/tests/test_propgrid.rb +1 -1
  216. data/tests/test_shared_event_handler.rb +141 -0
  217. data/tests/test_std_controls.rb +5 -5
  218. data/tests/test_webview.rb +492 -0
  219. data/tests/test_window.rb +3 -3
  220. metadata +78 -2
data/rakelib/prepost.rake CHANGED
@@ -56,27 +56,18 @@ namespace 'wxruby' do
56
56
  if WXRuby3.config.windows?
57
57
  if WXRuby3.config.get_config('with-wxwin')
58
58
  WXRuby3::Post.create_startup <<~__CODE
59
- begin
60
- require 'ruby_installer'
61
- if RubyInstaller::Runtime.respond_to?(:add_dll_directory)
62
- RubyInstaller::Runtime.add_dll_directory('#{File.expand_path('ext')}')
63
- else
64
- RubyInstaller::Build.add_dll_directory('#{File.expand_path('ext')}')
65
- end
66
- rescue LoadError
67
- end
59
+ #{WXRuby3::Post.setup_add_dll_directory(File.expand_path(File.join('ext', 'lib')))}
60
+ #{WXRuby3::Post.setup_adjust_wx_prefix}
68
61
  __CODE
69
62
  elsif !WXRuby3.config.get_cfg_string('wxwin').empty? && File.directory?(WXRuby3.config.get_cfg_string('wxwininstdir'))
70
63
  WXRuby3::Post.create_startup <<~__CODE
71
- begin
72
- require 'ruby_installer'
73
- if RubyInstaller::Runtime.respond_to?(:add_dll_directory)
74
- RubyInstaller::Runtime.add_dll_directory('#{WXRuby3.config.get_cfg_string('wxwininstdir')}')
75
- else
76
- RubyInstaller::Build.add_dll_directory('#{WXRuby3.config.get_cfg_string('wxwininstdir')}')
77
- end
78
- rescue LoadError
79
- end
64
+ #{WXRuby3::Post.setup_add_dll_directory(WXRuby3.config.get_cfg_string('wxwininstdir'))}
65
+ __CODE
66
+ end
67
+ else
68
+ if WXRuby3.config.get_config('with-wxwin')
69
+ WXRuby3::Post.create_startup <<~__CODE
70
+ #{WXRuby3::Post.setup_adjust_wx_prefix}
80
71
  __CODE
81
72
  end
82
73
  end
@@ -84,19 +75,20 @@ namespace 'wxruby' do
84
75
  end
85
76
 
86
77
  task :install do
87
- if WXRuby3.config.windows? && WXRuby3.config.get_config('with-wxwin') && !Rake::FileUtilsExt.nowrite_flag
88
- File.open(File.join(WXRuby3.config.get_cfg_string('siterubyver'), 'wx/startup.rb'), 'w') do |f|
89
- f.puts <<~__CODE
90
- begin
91
- require 'ruby_installer'
92
- if RubyInstaller::Runtime.respond_to?(:add_dll_directory)
93
- RubyInstaller::Runtime.add_dll_directory('#{WXRuby3.config.get_cfg_string('siterubyverarch')}')
94
- else
95
- RubyInstaller::Build.add_dll_directory('#{WXRuby3.config.get_cfg_string('siterubyverarch')}')
96
- end
97
- rescue LoadError
98
- end
99
- __CODE
78
+ if WXRuby3.config.get_config('with-wxwin') && !Rake::FileUtilsExt.nowrite_flag
79
+ if WXRuby3.config.windows?
80
+ File.open(File.join(WXRuby3.config.get_cfg_string('siterubyver'), 'wx/startup.rb'), 'w') do |f|
81
+ f.puts <<~__CODE
82
+ #{WXRuby3::Post.setup_add_dll_directory(WXRuby3.config.get_cfg_string('siterubyverarch'))}
83
+ #{WXRuby3::Post.setup_adjust_wx_prefix}
84
+ __CODE
85
+ end
86
+ else
87
+ File.open(File.join(WXRuby3.config.get_cfg_string('siterubyver'), 'wx/startup.rb'), 'w') do |f|
88
+ f.puts <<~__CODE
89
+ #{WXRuby3::Post.setup_adjust_wx_prefix}
90
+ __CODE
91
+ end
100
92
  end
101
93
  end
102
94
  end
data/rakelib/prepost.rb CHANGED
@@ -24,6 +24,26 @@ module WXRuby3
24
24
  end
25
25
  end
26
26
 
27
+ def self.setup_add_dll_directory(dll_directory)
28
+ <<~__CODE
29
+ begin
30
+ require 'ruby_installer'
31
+ if RubyInstaller::Runtime.respond_to?(:add_dll_directory)
32
+ RubyInstaller::Runtime.add_dll_directory('#{dll_directory}')
33
+ else
34
+ RubyInstaller::Build.add_dll_directory('#{dll_directory}')
35
+ end
36
+ rescue LoadError
37
+ end
38
+ __CODE
39
+ end
40
+
41
+ def self.setup_adjust_wx_prefix
42
+ <<~__CODE
43
+ ENV['WXPREFIX'] = File.realpath(File.join(__dir__, '..', '..', 'ext'))
44
+ __CODE
45
+ end
46
+
27
47
  end
28
48
 
29
49
  end
@@ -12,20 +12,18 @@
12
12
  require 'wx'
13
13
 
14
14
  # This simple sample demonstrates how to use Ruby (green) threads
15
- # to execute non-GUI code in parallel with a wxRuby
15
+ # or fibers to execute non-GUI code concurrently with a wxRuby
16
16
  # GUI. This strategy is useful in a number of situations:
17
17
  #
18
18
  # * To keep the GUI responsive whilst computationally intensive
19
19
  # operations are carried out in the background
20
20
  # * To keep the GUI responsive while waiting for networking operations
21
- # to complete
22
- #
23
- # The basic problem is that, as with other Ruby GUI toolkits, non-GUI
24
- # threads will not, by default, get allocated time to run while Ruby is
25
- # busy in Wx code - the main wxRuby event loop. Strategies to deal with
26
- # this include using non-blocking IO, and, more generically, using
27
- # wxRuby's Timer class to explicitly allocate time for non-GUI threads
28
- # to run. The latter technique is shown here.
21
+ # to complete
22
+ #
23
+ # This sample showcases how to use Ruby threading (Thread or Ractor) or
24
+ # cooperative concurrency (Fiber) in a wxRuby GUI application, how to
25
+ # communicate thread safely with the main thread and, if needed, how to
26
+ # provide processing time for non-GUI threads of execution.
29
27
 
30
28
  module Threaded
31
29
 
@@ -51,42 +49,244 @@ end
51
49
 
52
50
  # This frame shows a set of progress bars which monitor progress of
53
51
  # long-running tasks. In this example, this long-running task is
54
- # emulated by simply sleep-ing for random periods, but could equally be
55
- # downloading from a socket or parsing a file.
52
+ # a (partially) simulated word counter, but could equally be
53
+ # downloading from a socket or anything else requiring substantial
54
+ # computation or blocking IO operations.
56
55
  class ProgressFrame < Wx::Frame
57
- STEPS = 20
56
+
57
+ module ID
58
+ include Wx::IDHelper
59
+ RUN_GT_WITH_CUSTOM_EVENT = self.next_id
60
+ RUN_GT_WITH_ASYNC_CALL = self.next_id
61
+ RUN_GT_WITH_QUEUE = self.next_id
62
+ RUN_FIBER_YIELD = self.next_id
63
+ RUN_FIBER_UPDATE = self.next_id
64
+ RUN_RACTOR_THREADS = self.next_id
65
+ end
66
+
67
+ WORKERS = 8
68
+ STEPS = 100
69
+
70
+ # word count simulator
71
+ class Simulator
72
+ class << self
73
+ def run(timeslice)
74
+ start = Time.now
75
+ count = 0
76
+ # use max timeslice time to analyze max 100 files
77
+ 100.times do
78
+ text = File.read(__FILE__)
79
+ text.gsub(/\w+/) { count += 1; '' }
80
+ break unless (Time.now - start) < timeslice
81
+ end
82
+ count
83
+ end
84
+ end
85
+ end
86
+
58
87
  def initialize
59
- super(nil, :title => 'Threading demo')
88
+ super(nil, :title => 'Threading demo', size: [600, 400])
60
89
 
61
90
  self.icon = Wx.Icon(:sample, Wx::BITMAP_TYPE_XPM, art_path: File.join(__dir__, '..'))
62
91
 
92
+ menuFile = Wx::Menu.new
93
+ helpMenu = Wx::Menu.new
94
+ helpMenu.append(Wx::ID_ABOUT, "&About...\tF1", "Show about dialog")
95
+ gt_submenu = Wx::Menu.new
96
+ gt_submenu.append(ID::RUN_GT_WITH_CUSTOM_EVENT, "Run with custom event", 'Run Green threads simulation with custom events')
97
+ gt_submenu.append(ID::RUN_GT_WITH_ASYNC_CALL, "Run with async calls", 'Run Green threads simulation with asynchronous calls')
98
+ gt_submenu.append(ID::RUN_GT_WITH_QUEUE, "Run with thread queue", 'Run Green threads simulation with thread queue')
99
+ @mi_gt = menuFile.append_sub_menu(gt_submenu, "Run &Green Threads", 'Run simulation using standard Ruby Green threads')
100
+ fb_submenu = Wx::Menu.new
101
+ fb_submenu.append(ID::RUN_FIBER_YIELD, "Run Fibers with yield", 'Run Ruby fibers with update through yield')
102
+ fb_submenu.append(ID::RUN_FIBER_UPDATE, "Run Fibers with update", 'Run Ruby fibers with direct GUI update')
103
+ @mi_fb = menuFile.append_sub_menu(fb_submenu, "Run &Fibers", 'Run simulation using Ruby Fibers')
104
+ @mi_rt = menuFile.append(ID::RUN_RACTOR_THREADS, "Run &Ractor Threads", 'Run simulation using Ruby Ractor threads')
105
+ menuFile.append_separator
106
+ menuFile.append(Wx::ID_EXIT, "E&xit\tAlt-X", "Quit this program")
107
+ menuBar = Wx::MenuBar.new
108
+ menuBar.append(menuFile, "&File")
109
+ menuBar.append(helpMenu, "&Help")
110
+ set_menu_bar(menuBar)
111
+
112
+ create_status_bar
113
+
63
114
  @gauges = []
115
+ @workers = []
116
+ @queue = nil
64
117
  panel = Wx::Panel.new(self)
65
118
  sizer = Wx::BoxSizer.new(Wx::VERTICAL)
66
119
  # progress update handler
67
120
  evt_update_progress(:on_progress_update)
68
- frame = self
69
- # show ten gauges
70
- 10.times do |gauge_ix|
71
- gauge = Wx::Gauge.new(panel, :range => STEPS)
72
- # For each gauge, start a new thread in which the task runs
73
- Thread.new do
121
+ # show a gauge for each worker
122
+ WORKERS.times do
123
+ gauge = Wx::Gauge.new(panel, size: [-1, 30], :range => STEPS)
124
+ @gauges << gauge
125
+ sizer.add(gauge, 0, Wx::GROW|Wx::ALL, 2)
126
+ end
127
+ panel.sizer = sizer
128
+ sizer.fit(panel)
129
+
130
+ evt_menu Wx::ID_EXIT, :on_quit
131
+ evt_menu Wx::ID_ABOUT, :on_about
132
+ evt_menu_range ID::RUN_GT_WITH_CUSTOM_EVENT, ID::RUN_GT_WITH_QUEUE, :on_run_green_threads
133
+ evt_menu_range ID::RUN_FIBER_YIELD, ID::RUN_FIBER_UPDATE, :on_run_fibers
134
+ evt_menu ID::RUN_RACTOR_THREADS, :on_run_ractor_threads
135
+
136
+ evt_thread Wx::ID_ANY, :on_thread_event
137
+
138
+ evt_idle :on_idle
139
+ end
140
+
141
+ private def start_run
142
+ @mi_gt.enable(false)
143
+ @mi_fb.enable(false)
144
+ @mi_rt.enable(false)
145
+ reset_gauges
146
+ @total = 0
147
+ @run_start = Time.now
148
+ end
149
+
150
+ private def end_run(total)
151
+ set_status_text("#{Time.now - @run_start} seconds | #{total} words counted")
152
+ @mi_gt.enable
153
+ @mi_fb.enable
154
+ @mi_rt.enable
155
+ @queue = nil
156
+ @workers.clear
157
+ self.refresh
158
+ end
159
+
160
+ def on_run_green_threads(evt)
161
+ start_run
162
+ @queue = Thread::Queue.new if evt.id == ID::RUN_GT_WITH_QUEUE
163
+ # run a Thread for each worker
164
+ @workers = (0...WORKERS).collect do |worker|
165
+ # For each worker, start a new thread in which the task runs
166
+ Thread.new(evt.id, evt.id == ID::RUN_GT_WITH_QUEUE ? @queue : self) do |evt_id, queue_or_frame|
74
167
  # The long-running task
168
+ count = 0
75
169
  STEPS.times do | i |
76
- sleep rand(100) / 50.0
170
+ # simulate processing step
171
+ count += Simulator.run(0.1) # give each processing cycle a maximum timeslice of 100 msec
77
172
  # Update the main GUI asynchronously (more ways than 1)
78
- if (gauge_ix % 2) == 0
79
- frame.event_handler.queue_event(ProgressUpdateEvent.new(i+1, gauge_ix))
173
+ case evt_id
174
+ when ID::RUN_GT_WITH_QUEUE
175
+ queue_or_frame << [worker, i+1]
176
+ when ID::RUN_GT_WITH_CUSTOM_EVENT
177
+ queue_or_frame.event_handler.queue_event(ProgressUpdateEvent.new(i+1, worker))
178
+ else # ID::RUN_GT_WITH_ASYNC_CALL
179
+ queue_or_frame.call_after(:update_gauge, worker, i+1)
180
+ end
181
+ end
182
+ count
183
+ end
184
+ end
185
+ end
186
+
187
+ def on_run_fibers(evt)
188
+ start_run
189
+ # run a Fiber for each worker
190
+ evt_id = evt.id
191
+ @workers = (0...WORKERS).collect do |worker|
192
+ # For each worker, start a new fiber in which the task runs
193
+ # Use a smaller time slice for each worker cycle, so as not to block the
194
+ # event loop too long, but increase cycles so the workers still get a decent
195
+ # amount of 'CPU' time running the simulated process.
196
+ Fiber.new do
197
+ # The long-running task
198
+ count = 0
199
+ (4*STEPS).times do | i |
200
+ # simulate processing step
201
+ count += Simulator.run(0.01) # give each processing cycle a maximum timeslice of 10 msec
202
+ # Communicate update
203
+ if evt_id == ID::RUN_FIBER_YIELD
204
+ Fiber.yield [worker, (i+1)/4]
80
205
  else
81
- frame.call_after(:update_gauge, gauge_ix, i+1)
206
+ update_gauge(worker, (i+1)/4)
207
+ Fiber.yield
82
208
  end
83
209
  end
210
+ count
84
211
  end
85
- @gauges << gauge
86
- sizer.add(gauge, 0, Wx::GROW|Wx::ALL, 2)
87
212
  end
88
- panel.sizer = sizer
89
- sizer.fit(panel)
213
+ end
214
+
215
+ def on_run_ractor_threads(_)
216
+ start_run
217
+ # run a Ractor for each worker
218
+ @workers = (0...WORKERS).collect do |worker|
219
+ # for each worker start a Ractor to run the task
220
+ if RUBY_VERSION >= '4.0.0'
221
+ pin = Ractor::Port.new
222
+ pmon = Ractor::Port.new
223
+ r = Ractor.new(worker, self.make_shared, pin) do |worker_id, evt_handler, pout|
224
+ # The long-running task
225
+ count = 0
226
+ STEPS.times do | i |
227
+ count += Simulator.run(0.1) # give each processing cycle a maximum timeslice of 100 msec
228
+ # Update the main GUI asynchronously
229
+ pout.send(i+1)
230
+ evt = Wx::RT::ThreadEvent.new
231
+ evt.set_int(worker_id)
232
+ evt_handler.queue_event(evt)
233
+ end
234
+ pout.send(-1)
235
+ evt = Wx::RT::ThreadEvent.new
236
+ evt.set_int(worker_id)
237
+ evt_handler.queue_event(evt)
238
+ count
239
+ end
240
+ r.monitor(pmon)
241
+ [r, pin, pmon]
242
+ else
243
+ r = Ractor.new(worker, self.make_shared) do |worker_id, evt_handler|
244
+ # The long-running task
245
+ count = 0
246
+ STEPS.times do | i |
247
+ # simulate processing step
248
+ count += Simulator.run(0.1) # give each processing cycle a maximum timeslice of 100 msec
249
+ # Update the main GUI asynchronously
250
+ evt = Wx::RT::ThreadEvent.new
251
+ evt.set_int(worker_id)
252
+ evt_handler.queue_event(evt)
253
+ Ractor.yield(i+1)
254
+ end
255
+ evt = Wx::RT::ThreadEvent.new
256
+ evt.set_int(worker_id)
257
+ evt_handler.queue_event(evt)
258
+ Ractor.yield(-1)
259
+ count
260
+ end
261
+ [r]
262
+ end
263
+ end
264
+ end
265
+
266
+ def on_thread_event(evt)
267
+ w = evt.get_int
268
+ if RUBY_VERSION >= '4.0.0'
269
+ step = @workers[w][1].receive
270
+ if step >= 0
271
+ update_gauge(w, step)
272
+ else
273
+ rc = @workers[w][2].receive
274
+ Wx.message_box("Worker ##{w} aborted with error.", 'Worker Error',
275
+ Wx::OK|Wx::CENTRE|Wx::ICON_ERROR, self) if rc == :aborted
276
+ @workers[w] << :stopped
277
+ end
278
+ else
279
+ step = begin; @workers[w][0].take; rescue Ractor::ClosedError; nil; end
280
+ if step && step >= 0
281
+ update_gauge(w, step)
282
+ else
283
+ @workers[w] << :stopped
284
+ end
285
+ end
286
+ end
287
+
288
+ def reset_gauges
289
+ WORKERS.times { |w| update_gauge(w, 0) }
90
290
  end
91
291
 
92
292
  def update_gauge(gauge_ix, value)
@@ -96,17 +296,77 @@ class ProgressFrame < Wx::Frame
96
296
  def on_progress_update(evt)
97
297
  update_gauge(evt.gauge, evt.value)
98
298
  end
299
+
300
+ def on_idle(evt)
301
+ unless @workers.empty?
302
+ case @workers.first
303
+ when ::Thread
304
+ if @workers.any? { |worker| worker.alive? }
305
+ if @queue
306
+ WORKERS.times do
307
+ data = @queue.shift(true) rescue nil
308
+ update_gauge(*data) if data
309
+ break unless data
310
+ end
311
+ end
312
+ else
313
+ @total = @workers.sum { |w| w.value }
314
+ @workers.clear
315
+ if @queue
316
+ begin
317
+ data = @queue.shift(true) rescue nil
318
+ update_gauge(*data) if data
319
+ end while data
320
+ else
321
+ Wx.get_app.yield
322
+ end
323
+ end_run(@total)
324
+ end
325
+ when ::Fiber
326
+ if @workers.any? { |worker| worker.alive? }
327
+ @workers.each do |fbr|
328
+ if fbr.alive?
329
+ data = fbr.resume rescue nil
330
+ if data
331
+ if ::Array === data
332
+ update_gauge(*data)
333
+ else
334
+ @total += data
335
+ end
336
+ end
337
+ end
338
+ end
339
+ evt.request_more # make sure we get another idle event to provide time slices for the fibers
340
+ else
341
+ end_run(@total)
342
+ end
343
+ else
344
+ if @workers.all? { |worker| worker.last == :stopped }
345
+ @total = @workers.sum { |w| w[0].value }
346
+ @workers.clear
347
+ end_run(@total)
348
+ end
349
+ end
350
+ evt.skip
351
+ end
352
+ end
353
+
354
+ def on_quit(_)
355
+ close(true)
356
+ end
357
+
358
+ def on_about(_)
359
+ msg = sprintf("This is the About dialog of the threaded sample.\n" \
360
+ "Welcome to wxRuby, version %s", Wx::WXRUBY_VERSION)
361
+ Wx::message_box(msg, "About Threaded", Wx::OK|Wx::ICON_INFORMATION, self)
362
+ end
363
+
99
364
  end
100
365
 
101
366
  # This app class creates a frame, and, importantly, a timer to allow
102
367
  # the threads some computing time
103
368
  class GaugeApp < Wx::App
104
369
  def on_init
105
- # Create a global application timer that passes control to other
106
- # ruby threads. The timer will run every 1/40 second (25ms). Higher
107
- # values will make the other threads run more often, but will
108
- # eventually degrade the responsiveness of the GUI.
109
- Wx::Timer.every(25) { Thread.pass }
110
370
  prog = ProgressFrame.new
111
371
  prog.show
112
372
  end
@@ -122,22 +382,15 @@ module ThreadSample
122
382
  { file: __FILE__,
123
383
  summary: 'wxRuby threading example.',
124
384
  description: <<~__TXT
125
- wxRuby example demonstrating how to use ruby threads in wxRuby.
126
- This simple sample demonstrates how to use Ruby (green) threads
127
- to execute non-GUI code in parallel with a wxRuby
385
+ wxRuby example demonstrating how to use concurrency in wxRuby.
386
+ This sample demonstrates how to use Ruby (green) threads, fibers
387
+ and Ractor threads to execute non-GUI code in concurrently with a wxRuby
128
388
  GUI. This strategy is useful in a number of situations:
129
389
 
130
390
  * To keep the GUI responsive whilst computationally intensive
131
391
  operations are carried out in the background
132
392
  * To keep the GUI responsive while waiting for networking operations
133
393
  to complete
134
-
135
- The basic problem is that, as with other Ruby GUI toolkits, non-GUI
136
- threads will not, by default, get allocated time to run while Ruby is
137
- busy in Wx code - the main wxRuby event loop. Strategies to deal with
138
- this include using non-blocking IO, and, more generically, using
139
- wxRuby's Timer class to explicitly allocate time for non-GUI threads
140
- to run. The latter technique is shown here.
141
394
  __TXT
142
395
  }
143
396
  end
Binary file
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ .topic IDH_PANEL
2
+ This is the topic for the main panel.
3
+
4
+ .topic IDH_TEXT
5
+ This is the topic for the text control.
6
+
7
+ .topic IDH_OK
8
+ This is the topic for the OK button.
9
+
Binary file
@@ -0,0 +1,8 @@
1
+ :Base doc.hlp
2
+ 1 Introduction
3
+ 2 Introduction=intro
4
+ 2 Classes=classes
5
+ 2 Functions=functions
6
+ 2 About=about
7
+ 1 Chapter 2
8
+ 2 Chapter 2=chapter2
@@ -0,0 +1,7 @@
1
+ #define doc1 100
2
+ #define doc2 2
3
+ #define doc3 1
4
+ #define doc4 3
5
+ #define IDH_PANEL 300
6
+ #define IDH_TEXT 301
7
+ #define IDH_OK 302
@@ -0,0 +1,40 @@
1
+ <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
2
+ <HTML>
3
+ <HEAD>
4
+ <meta name="GENERATOR" content="tex2rtf">
5
+ <!-- Sitemap 1.0 -->
6
+ </HEAD><BODY>
7
+ <OBJECT type="text/site properties">
8
+ <param name="ImageType" value="Folder">
9
+ </OBJECT>
10
+ <UL>
11
+ <LI> <OBJECT type="text/sitemap">
12
+ <param name="Local" value="doc.htm">
13
+ <param name="Name" value="Contents">
14
+ <param name="ID" value=0>
15
+ </OBJECT>
16
+ <LI> <OBJECT type="text/sitemap">
17
+ <param name="Local" value="doc1.htm#intro">
18
+ <param name="Name" value="Introduction">
19
+ </OBJECT>
20
+ <UL> <LI> <OBJECT type="text/sitemap">
21
+ <param name="Local" value="doc2.htm#classes">
22
+ <param name="Name" value="Classes">
23
+ <param name="ID" value=2>
24
+ </OBJECT>
25
+ <LI> <OBJECT type="text/sitemap">
26
+ <param name="Local" value="doc3.htm#functions">
27
+ <param name="Name" value="Functions">
28
+ <param name="ID" value=1>
29
+ </OBJECT>
30
+ <LI> <OBJECT type="text/sitemap">
31
+ <param name="Local" value="doc4.htm#about">
32
+ <param name="Name" value="About">
33
+ <param name="ID" value=3>
34
+ </OBJECT>
35
+ </UL> <LI> <OBJECT type="text/sitemap">
36
+ <param name="Local" value="doc5.htm#chapter2">
37
+ <param name="Name" value="Chapter 2">
38
+ <param name="ID" value=4>
39
+ </OBJECT>
40
+ </UL>
@@ -0,0 +1,31 @@
1
+ <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
2
+ <HTML>
3
+ <HEAD>
4
+ <meta name="GENERATOR" content="tex2rtf">
5
+ <!-- Sitemap 1.0 -->
6
+ </HEAD><BODY>
7
+ <OBJECT type="text/site properties">
8
+ <param name="ImageType" value="Folder">
9
+ </OBJECT>
10
+ <UL>
11
+ <LI> <OBJECT type="text/sitemap">
12
+ <param name="Local" value="doc4.htm#about">
13
+ <param name="Name" value="About">
14
+ </OBJECT>
15
+ <LI> <OBJECT type="text/sitemap">
16
+ <param name="Local" value="doc1.htm#intro">
17
+ <param name="Name" value="Introduction">
18
+ </OBJECT>
19
+ <LI> <OBJECT type="text/sitemap">
20
+ <param name="Local" value="doc2.htm#classes">
21
+ <param name="Name" value="Classes">
22
+ </OBJECT>
23
+ <LI> <OBJECT type="text/sitemap">
24
+ <param name="Local" value="doc5.htm#chapter2">
25
+ <param name="Name" value="Chapter 2">
26
+ </OBJECT>
27
+ <LI> <OBJECT type="text/sitemap">
28
+ <param name="Local" value="doc3.htm#functions">
29
+ <param name="Name" value="Functions">
30
+ </OBJECT>
31
+ </UL>
@@ -0,0 +1,33 @@
1
+ [OPTIONS]
2
+ Compatibility=1.1 or later
3
+ Compiled file=doc.chm
4
+ Contents file=doc.hhc
5
+ Default Window=docHelp
6
+ Default topic=doc.htm
7
+ Display compile progress=No
8
+ Full-text search=Yes
9
+ Index file=doc.hhk
10
+ Language=0x809 English (United Kingdom)
11
+ Title=Help Demo
12
+
13
+ [WINDOWS]
14
+ docHelp=,"doc.hhc","doc.hhk","doc.htm",,,,,,0x2420,,0x380e,,,,,0,,,0
15
+
16
+
17
+ [FILES]
18
+ doc.htm
19
+ doc1.htm
20
+ doc2.htm
21
+ doc3.htm
22
+ doc4.htm
23
+ doc5.htm
24
+
25
+ [MAP]
26
+ #include doc.h
27
+
28
+ [TEXT POPUPS]
29
+ doc.h
30
+ cshelp.txt
31
+
32
+ [INFOTYPES]
33
+
@@ -0,0 +1,21 @@
1
+ [OPTIONS]
2
+ BMROOT=D:\wx2\wxWind~1\samples\help
3
+ TITLE=Help Demo Document
4
+ CONTENTS=Contents
5
+ COMPRESS=HIGH
6
+
7
+ [FILES]
8
+ doc.rtf
9
+
10
+ [CONFIG]
11
+ CreateButton("Up", "&Up", "JumpId(`doc.hlp', `Contents')")
12
+ BrowseButtons()
13
+
14
+ [MAP]
15
+ #define intro 100
16
+ #define functions 1
17
+ #define classes 2
18
+ #define about 3
19
+
20
+ [BITMAPS]
21
+