#!/usr/bin/env ruby # wxRuby2 Sample Code. Copyright (c) 2004-2008 wxRuby development team # Freely reusable code: see SAMPLES-LICENSE.TXT for details begin require 'rubygems' rescue LoadError end require 'wx' # This simple sample demonstrates how to use Ruby 1.8's lightweight # (green) threads to execute non-GUI code in parallel with a wxRuby # GUI. This strategy is useful in a number of situations: # # * To keep the GUI responsive whilst computationally intensive # operations are carried out in the background # * To keep the GUI responsive while waiting for networking operations # to complete # # The basic problem is that, as with other Ruby GUI toolkits, non-GUI # threads will not, by default, get allocated time to run while Ruby is # busy in Wx code - the main wxRuby event loop. Strategies to deal with # this include using non-blocking IO, and, more generically, using # wxRuby's Timer class to explicitly allocate time for non-GUI threads # to run. The latter technique is shown here. # This frame shows a set of progress bars which monitor progress of # long-running tasks. In this example, this long-running task is # emulated by simply sleep-ing for random periods, but could equally be # downloading from a socket or parsing a file. class ProgressFrame < Wx::Frame STEPS = 20 def initialize super(nil, :title => 'Threading demo') @gauges = [] panel = Wx::Panel.new(self) sizer = Wx::BoxSizer.new(Wx::VERTICAL) # show ten gauges 10.times do gauge = Wx::Gauge.new(panel, :range => STEPS) # For each gauge, start a new thread in which the task runs Thread.new do # The long-running task STEPS.times do | i | sleep rand(100) / 50.0 # Update the main GUI gauge.value = i + 1 end end @gauges << gauge sizer.add(gauge, 0, Wx::GROW|Wx::ALL, 2) end panel.sizer = sizer sizer.fit(panel) end end # This app class creates a frame, and, importantly, a timer to allow class GaugeApp < Wx::App # Get a guaranteed-unique id THREAD_TIMER_ID = Wx::ID_HIGHEST + 1 def on_init # Create a global application timer that passes control to other # ruby threads. The timer will run every 1/40 second (25ms). Higher # values will make the other threads run more often, but will # eventually degrade the responsiveness of the GUI. Wx::Timer.every(25) { Thread.pass } prog = ProgressFrame.new prog.show end end GaugeApp.new.main_loop