I am new to wxRuby and just built a simple app with a frame containing a static text control and two gauges ... I used DialogBlocks to design the ui, and wx_sugar to generate a base class via xrcise, etc... All of this works peachy on my development box (Mac OSX ~ Leopard) with the latest gems... However, I tried running this little app on my Windows XP box and got the following exception: ./stream_downloader_gui.rb:25:in `find_window_by_id'': Error wrapping object; cla ss `wxGauge95'' is not supported in wxRuby (NotImplementedError) from ./stream_downloader_gui.rb:25:in `initialize'' from ./stream_downloader_gui.rb:34:in `call'' from ./stream_downloader_gui.rb:34:in `initialize'' from E:/stream_downloader/stream_downloader.rb:25:in `initialize'' from E:/stream_downloader/stream_downloader.rb:58:in `new'' from E:/stream_downloader/stream_downloader.rb:58:in `initialize'' from E:/stream_downloader/stream_downloader.rb:191:in `new'' from E:/stream_downloader/stream_downloader.rb:191:in `on_init'' from E:/stream_downloader/stream_downloader.rb:201:in `main_loop'' from E:/stream_downloader/stream_downloader.rb:201 I figure this has something to do with the gem trying to instantiate a Gauge95 instead of a GaugeMSW or a Gauge, but I see nothing in the docs that suggests a solution... Any ideas? Thanks much, Tim -- Posted via http://www.ruby-forum.com/.
Hello Tim, Yes, you are correct in your assumption, that wxRuby is trying to initialize wxGuage95, and not finding it. Dialog Blocks has a bad habbit of trying to create a unique Class for C++ Code Generation, in which it creates a Sub-Class of the main class. What you would want to do, is edit your xml file, and change Guage95 into just Guage. This will allow wxRuby to correctly interpret the class you are actually trying to create. I have found, that Dialog Blocks is a bit of an encumberance when dealing with creating GUI''s for wxRuby myself. Others have had excellent success, so it''s all in your point of view. I would suggest checking out wxFormBuilder as an alternative to Dialog Blocks. The website is: http://www.wxformbuilder.org/ I find I have a lot more success with this RAD Tool, then any other I have ran into. Let me know if you try it out, and what you think of it, same with your problem to. L8ers, Mario Steele On 2/17/08, Tim Ferrell <lists at ruby-forum.com> wrote:> > > I am new to wxRuby and just built a simple app with a frame containing a > static text control and two gauges ... I used DialogBlocks to design the > ui, and wx_sugar to generate a base class via xrcise, etc... All of this > works peachy on my development box (Mac OSX ~ Leopard) with the latest > gems... > > However, I tried running this little app on my Windows XP box and got > the following exception: > > ./stream_downloader_gui.rb:25:in `find_window_by_id'': Error wrapping > object; cla > ss `wxGauge95'' is not supported in wxRuby (NotImplementedError) > from ./stream_downloader_gui.rb:25:in `initialize'' > from ./stream_downloader_gui.rb:34:in `call'' > from ./stream_downloader_gui.rb:34:in `initialize'' > from E:/stream_downloader/stream_downloader.rb:25:in > `initialize'' > from E:/stream_downloader/stream_downloader.rb:58:in `new'' > from E:/stream_downloader/stream_downloader.rb:58:in > `initialize'' > from E:/stream_downloader/stream_downloader.rb:191:in `new'' > from E:/stream_downloader/stream_downloader.rb:191:in `on_init'' > from E:/stream_downloader/stream_downloader.rb:201:in > `main_loop'' > from E:/stream_downloader/stream_downloader.rb:201 > > I figure this has something to do with the gem trying to instantiate a > Gauge95 instead of a GaugeMSW or a Gauge, but I see nothing in the docs > that suggests a solution... Any ideas? > > Thanks much, > Tim > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > wxruby-users mailing list > wxruby-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users >-- Mario Steele http://www.trilake.net http://www.ruby-im.net http://rubyforge.org/projects/wxruby/ http://rubyforge.org/projects/wxride/ http://rubyforge.org/projects/vwmc/ -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/wxruby-users/attachments/20080217/83777a81/attachment.html
Mario Steele wrote:> Hello Tim,Hey Mario...> > Yes, you are correct in your assumption, that wxRuby is trying to > initialize > wxGuage95, and not finding it. Dialog Blocks has a bad habbit of trying > to > create a unique Class for C++ Code Generation, in which it creates a > Sub-Class of the main class. What you would want to do, is edit your > xml > file, and change Guage95 into just Guage. This will allow wxRuby to > correctly interpret the class you are actually trying to create. >That thought did occur to me and I looked into it... Here is the relevant snippet from the xrc: <object class="wxGauge" name="progress_item"> <tooltip>current file progress</tooltip> <style>wxGA_HORIZONTAL</style> <value>0</value> <range>100</range> </object> and I saw no direct reference to Gauge95... At this point I wondering if it is just a problem with the way the wxWidgets part of the binary gem was compiled... I may try stepping back to the 1.9.3 gem and see how things behave, or maybe just try building it myself... Thanks for the tip about wxFormBuilder ... I''ll look into that as well. :-) Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Well, figuring that this had something to do with loading from an xrc, I redid my code to just create the dialog by hand... I get a dialog now, without error, but something seems messed up with event handling... I cannot move or close the dialog, the Cancel button does not paint... the gauges work though :-p Again, this all works fine for me on my Mac - I only see this weirdness on Windows (XP) ... I''m sure it is some detail I am overlooking or, being so new to this, something I am misinterpreting... Attached is my code in hopes that someone will spot something... (this is just a small app to parse an m3u playlist and download the mp3s found there) ---- here be code ------ #!/usr/bin/env ruby # stdlib require ''fileutils'' require ''open-uri'' require ''tmpdir'' # gems require ''rubygems'' require ''wx'' # # StreamDownloaderProgress class # # progress dialog for the downloads... # class StreamDownloaderProgress < Wx::Frame attr_accessor :status_label, :progress_item, :progress_all, :cancel_button attr_accessor :cancelled def initialize super(nil, -1, StreamDownloader::TITLE, Wx::DEFAULT_POSITION, Wx::Size.new(350, 120), Wx::MINIMIZE_BOX | Wx::SYSTEM_MENU | Wx::CAPTION | Wx::CLOSE_BOX) sizer = Wx::BoxSizer.new(Wx::VERTICAL) # label @status_label = Wx::StaticText.new(self, -1, "Preparing downloads... please wait.", Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE) sizer.add(@status_label, 1, Wx::GROW | Wx::ALL | Wx::ALIGN_LEFT | Wx::FIXED_MINSIZE, 2) # item progress @progress_item = Wx::Gauge.new(self, -1, 100, Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE, Wx::GA_HORIZONTAL) @progress_item.set_tool_tip("current item progress") @progress_item.value = 0 sizer.add(@progress_item, 1, Wx::GROW | Wx::HORIZONTAL, 2) # all progress @progress_all = Wx::Gauge.new(self, -1, 100, Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE, Wx::GA_HORIZONTAL) @progress_all.set_tool_tip("overall progress") @progress_all.value = 0 sizer.add(@progress_all, 1, Wx::GROW | Wx::HORIZONTAL, 2) # cancel button @cancel_button = Wx::Button.new(self, -1, ''Cancel'', Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE) @cancel_button.set_tool_tip("cancel downloads") sizer.add(@cancel_button, 1, Wx::ALIGN_CENTER_HORIZONTAL, 2) self.set_sizer(sizer) # on close simply set var to show intention evt_close { |e| @cancelled = true } evt_button(@cancel_button) { |e| @cancelled = true } # set up location/controls self.center(Wx::BOTH) self.layout end end # # StreamDownloader class # # main class where all the work is donw # class StreamDownloader attr_accessor :destdir attr_reader :urls attr :m3ufile attr :dialog NAME = ''Stream Downloader'' VERSION = ''1.0.0'' TITLE = "#{NAME} #{VERSION}" def initialize # init our progress dialog @dialog = StreamDownloaderProgress.new Wx::get_app.yield # set up our destdir based on platform if RUBY_PLATFORM =~ /mswin32/ @destdir = File.join("C:/Documents and Settings", ENV[''USERNAME''], ''Desktop'') elsif RUBY_PLATFORM =~ /darwin/ @destdir = File.join(ENV[''HOME''], ''Desktop'') else @destdir = File.expand_path(File.dirname(__FILE__)) end end def ask fd = Wx::FileDialog.new(@dialog, ''Select the playlist to process...'', @destdir, "", "Playlist files (*.m3u)|*.m3u", Wx::FD_OPEN | Wx::FD_FILE_MUST_EXIST, Wx::DEFAULT_POSITION ) if fd.show_modal == Wx::ID_OK return(fd.get_path) else exit end end def file=(m3u) # ok ... assign and parse @m3ufile = m3u @urls = parse if @m3ufile end def file return @m3ufile end def parse(file = @m3ufile) found = [] if File.exist?(file) File.open(file) { |f| while line = f.gets found.push(line.chomp) if line =~ /\.mp3$/i end } end return found end def grab progress = 0 if @urls.size > 0 @dialog.show Wx::get_app.yield @urls.each_index { |idx| url = @urls[idx] fname = File.basename(url) @dialog.status_label.label = "Downloading #{idx + 1} of #{@urls.size}: #{fname}" @dialog.progress_all.value = (100 * (idx/@urls.size.to_f)).to_i begin size = 0 @dialog.progress_item.value = 0 # reset File.open(File.join(Dir::tmpdir, fname), ''wb'') { |ofile| open(url, :content_length_proc => lambda {|total| size = total }, :progress_proc => lambda { |current| @dialog.progress_item.value = (100 * (current/size.to_f)).to_i if size > 0 Wx::get_app.yield # update gui... raise "operation cancelled by user" if @dialog.cancelled } ) { |urlreader| urlreader.binmode ofile.write(urlreader.read) } } rename(File.join(Dir::tmpdir, fname)) rescue => err @dialog.hide unless @dialog.cancelled md = Wx::MessageDialog.new(@dialog, "An error occurred while downloading!\n#{url}", TITLE, Wx::OK | Wx::ICON_ERROR, Wx::DEFAULT_POSITION) md.show_modal end File.unlink(File.join(Dir::tmpdir, fname)) @dialog.destroy exit(1) end } @dialog.progress_all.value = 100 sleep(1) @dialog.hide md = Wx::MessageDialog.new(@dialog, ''Download(s) complete!'', TITLE, Wx::OK, Wx::DEFAULT_POSITION) md.show_modal else unless @m3ufile.empty? md = Wx::MessageDialog.new(@dialog, ''No supported media files found to download!'', TITLE, Wx::OK | Wx::ICON_EXCLAMATION, Wx::DEFAULT_POSITION) md.show_modal end end @dialog.destroy # and exit... end def rename(file) newname = "#{@destdir}/#{File.basename(@m3ufile, File.extname(@m3ufile))}/#{File.basename(file)}" FileUtils.mkdir_p(File.dirname(newname)) FileUtils.mv(file, newname) end end # StreamDownloader class # # StreamDownloaderApp class # # entry point for the app # class StreamDownloaderApp < Wx::App def on_init @sd = StreamDownloader.new begin playlist = (ARGV.first || @sd.ask) rescue => err $stdout.puts(err) end if playlist @sd.file = playlist @sd.grab end end end StreamDownloaderApp.new.main_loop -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> I am new to wxRuby and just built a simple app with a frame containing a > static text control and two gauges ... I used DialogBlocks to design the > ui, and wx_sugar to generate a base class via xrcise, etc... All of this > works peachy on my development box (Mac OSX ~ Leopard) with the latest > gems... > > However, I tried running this little app on my Windows XP box and got > the following exception: > > ./stream_downloader_gui.rb:25:in `find_window_by_id'': Error wrapping > object; cla > ss `wxGauge95'' is not supported in wxRuby (NotImplementedError)Could you post the XRC for this dialog please? It could be a problem with the XRC that DialogBlocks is generating, or with the way that''s interpreted by wxRuby or wxWidgets. Thanks alex
Alex Fenton wrote:> Tim Ferrell wrote: >> object; cla >> ss `wxGauge95'' is not supported in wxRuby (NotImplementedError) > > Could you post the XRC for this dialog please? It could be a problem > with the XRC that DialogBlocks is generating, or with the way that''s > interpreted by wxRuby or wxWidgets. > > Thanks > alexSure ... here you go ... and thanks! --- here be the xrc --- <?xml version="1.0" encoding="UTF-8"?> <resource version="2.3.0.1" xmlns="http://www.wxwidgets.org/wxxrc"> <object class="wxDialog" name="ID_WXFRAME" subclass="StreamDownloaderProgressBase"> <style>wxCLOSE_BOX|wxMINIMIZE_BOX|wxSUNKEN_BORDER|wxFULL_REPAINT_ON_RESIZE|wxCLIP_CHILDREN|wxTAB_TRAVERSAL</style> <title>Stream Downloader</title> <centered>1</centered> <object class="wxBoxSizer"> <orient>wxVERTICAL</orient> <object class="sizeritem"> <flag>wxALIGN_LEFT|wxALL|wxFIXED_MINSIZE</flag> <border>5</border> <object class="wxStaticText" name="status_label"> <label>Preparing downloads ... please wait.</label> </object> </object> <object class="sizeritem"> <flag>wxGROW|wxALL</flag> <border>5</border> <object class="wxGauge" name="progress_item"> <tooltip>current file progress</tooltip> <style>wxGA_HORIZONTAL</style> <value>0</value> <range>100</range> </object> </object> <object class="sizeritem"> <flag>wxGROW|wxALL</flag> <border>5</border> <object class="wxGauge" name="progress_all"> <tooltip>overall progress</tooltip> <style>wxGA_HORIZONTAL</style> <value>0</value> <range>100</range> </object> </object> <object class="sizeritem"> <flag>wxALIGN_CENTER_HORIZONTAL|wxALL</flag> <border>5</border> <object class="wxButton" name="cancel_button"> <tooltip>cancel downloading</tooltip> <label>Cancel</label> </object> </object> </object> </object> </resource> -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> Well, figuring that this had something to do with loading from an xrc, I > redid my code to just create the dialog by hand... I get a dialog now, > without error, but something seems messed up with event handling... I > cannot move or close the dialog, the Cancel button does not paint... the > gauges work though :-p > > Again, this all works fine for me on my Mac - I only see this weirdness > on Windows (XP) ... I''m sure it is some detail I am overlooking or, > being so new to this, something I am misinterpreting... >I will look to try this out on XP in the next few days, but in the meantime:> Attached is my code in hopes that someone will spot something... (this > is just a small app to parse an m3u playlist and download the mp3s found > there) >> def grab > progress = 0 > if @urls.size > 0 > @dialog.show > Wx::get_app.yield >I have never tried using Wx::App#yield so I''m not sure whether it will work correctly within a ruby context on all platforms. You might try an alternate strategy of using one or more Ruby threads within which to run the downloads. Include something like Wx::Timer.every(50) { Thread.pass } in your App#on_init to ensure the download threads get some time. Then, inside the worker downloader threads, update some shared variable with the progress at each step; inside the main GUI thread, have the progress bar read this variable. I am not sure this will solve it, but it has certainly worked for me doing long-running file-based tasks (eg tokenising and parsing) in the background while updating to a GUI representation of that. Take a look at the etc/threaded.rb sample for a simple example of this; Mario''s excellent chat client sample shows the use of Threads with network operations, but it is more complex. hth alex
Tim Ferrell wrote:> Sure ... here you go ... and thanks! >Seems there''s an inconsistency on the wxWidgets side where the C++ class of a Gauge on windows is reported as wxGauge95, a class which wxRuby has never heard about. I found that adding something like this before the main app code fixed the problem: Wx::Gauge95 = Wx::Gauge I''ll add something similar to the wxRuby core library for the next release so this won''t be a problem in the future. Thanks for the report. alex
Alex Fenton wrote:> I have never tried using Wx::App#yield so I''m not sure whether it will > work correctly within a ruby context on all platforms. You might try an > alternate strategy of using one or more Ruby threads within which to run > the downloads.That does seem to be the issue... #yield doesn''t behave the same under Windows as it does on OS X :-/ I''ll work on using a separate thread for the downloads and see how that fares... Thanks for the help with the xrc issue - I really didn''t want to have to code my interfaces by hand :-p Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> Alex Fenton wrote: >> I have never tried using Wx::App#yield so I''m not sure whether it will >> work correctly within a ruby context on all platforms. You might try an >> alternate strategy of using one or more Ruby threads within which to run >> the downloads. > > That does seem to be the issue... #yield doesn''t behave the same under > Windows as it does on OS X :-/ I''ll work on using a separate thread for > the downloads and see how that fares... > > Thanks for the help with the xrc issue - I really didn''t want to have to > code my interfaces by hand :-p > > Cheers, > TimWell, I am still seeing some strangeness under Windows ... not sure if I''ve done something wrong, though... As before this all works fine on my Mac... I wrapped the open-uri call with a thread and added the Wx::Timer.every call to the main app on_init as you suggested like so: -- snippet --- t = Thread.new { File.open(File.join(Dir::tmpdir, fname), ''wb'') { |ofile| open(url, :content_length_proc => lambda {|total| size = total }, :progress_proc => lambda { |current| Thread.exit if @dialog.cancelled @dialog.progress_item.value (100 * (current/size.to_f)).to_i if size > 0 @dialog.update }) { |urlreader| urlreader.binmode ofile.write(urlreader.read) } } } t.join t.kill if t.alive? # kill thread raise "user cancelled" if @dialog.cancelled -- end snippet --- @dialog is an instance of the subclass of the base class generated from my xrc by xrcise... that #update method looks like so: def update(center) self.layout self.fit self.center(Wx::BOTH) if center Wx::get_app.yield Thread.pass end My problem can be shown in these grabs: http://www.mcgeecorp.com/downloads/wxwin00.jpg http://www.mcgeecorp.com/downloads/wxwin01.jpg http://www.mcgeecorp.com/downloads/wxwin02.jpg You can see from this that the text label and the cancel button are not being drawn correctly ... the gauges paint and update fine... After the first file gets downloaded, successive runs through the loop seem clean up the label text but the cancel button never paints right *and* the dialog is unresponsive to user input - if I click the close box I get the windows message that the app has stopped responding... clicking where the close button should be does nothing... Am I doing something wrong or could this be something else? Thanks, Tim -- Posted via http://www.ruby-forum.com/.
Hey Tim, Actually, your code is perfectly fine, there''s nothing wrong with it. What is the problem though, is that when your running through your code, and utilizing open-uri, it''s utilizing blocking sockets, in which whenever a read method is called on the socket, if there is no data available, it will wait till there is data available. Since Ruby uses Green threads instead of Native OS Threads, this thends to block external C/C++ code from executing, as Ruby''s main System OS Thread has priority. This caues, as you have seen, repaint issues, and missed answering of Events generated by wxRuby/wxWidgets. The solution to this problem, is to write the actual Socket calls yourself, to do the calls Asynchronously. You can actually look here: http://wxruby.rubyforge.org/wiki/wiki.pl?Sockets on how to create Asynchronous Sockets with wxRuby, and utilize them to write to and read from sockets, without blocking. However, it is key to note, that since you will be writting the socket routines yourself, you will either have to modify the library your using, in this case open-uri, to use the new socket method routines, instead of the standard one, or you will have to do all the reading, writting, connecting, and disconnecting yourself. If you have any further questions about how to go about implementing this, let me know, and I''ll be happy to help you out with it. L8ers, Mario Steele On 2/22/08, Tim Ferrell <lists at ruby-forum.com> wrote:> > Tim Ferrell wrote: > > Alex Fenton wrote: > >> I have never tried using Wx::App#yield so I''m not sure whether it will > >> work correctly within a ruby context on all platforms. You might try an > >> alternate strategy of using one or more Ruby threads within which to > run > >> the downloads. > > > > That does seem to be the issue... #yield doesn''t behave the same under > > Windows as it does on OS X :-/ I''ll work on using a separate thread for > > the downloads and see how that fares... > > > > Thanks for the help with the xrc issue - I really didn''t want to have to > > code my interfaces by hand :-p > > > > Cheers, > > Tim > > Well, I am still seeing some strangeness under Windows ... not sure if > I''ve done something wrong, though... As before this all works fine on my > Mac... > > I wrapped the open-uri call with a thread and added the Wx::Timer.every > call to the main app on_init as you suggested like so: > > -- snippet --- > > t = Thread.new { > File.open(File.join(Dir::tmpdir, fname), ''wb'') { |ofile| > open(url, > :content_length_proc => lambda {|total| size = total }, > :progress_proc => lambda { |current| > Thread.exit if @dialog.cancelled > @dialog.progress_item.value > (100 * (current/size.to_f)).to_i if size > 0 > @dialog.update > }) { |urlreader| > urlreader.binmode > ofile.write(urlreader.read) > } > } > } > t.join > t.kill if t.alive? # kill thread > raise "user cancelled" if @dialog.cancelled > > -- end snippet --- > > @dialog is an instance of the subclass of the base class generated from > my xrc by xrcise... that #update method looks like so: > > def update(center) > self.layout > self.fit > self.center(Wx::BOTH) if center > Wx::get_app.yield > Thread.pass > end > > My problem can be shown in these grabs: > > http://www.mcgeecorp.com/downloads/wxwin00.jpg > http://www.mcgeecorp.com/downloads/wxwin01.jpg > http://www.mcgeecorp.com/downloads/wxwin02.jpg > > You can see from this that the text label and the cancel button are not > being drawn correctly ... the gauges paint and update fine... After the > first file gets downloaded, successive runs through the loop seem clean > up the label text but the cancel button never paints right *and* the > dialog is unresponsive to user input - if I click the close box I get > the windows message that the app has stopped responding... clicking > where the close button should be does nothing... > > Am I doing something wrong or could this be something else? > > Thanks, > Tim > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > wxruby-users mailing list > wxruby-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users >-- Mario Steele http://www.trilake.net http://www.ruby-im.net http://rubyforge.org/projects/wxruby/ http://rubyforge.org/projects/wxride/ http://rubyforge.org/projects/vwmc/ -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/wxruby-users/attachments/20080222/2f6bf211/attachment.html
Mario Steele wrote:> Hey Tim,Hey Mario...> Actually, your code is perfectly fine, there''s nothing wrong with it. What > is the problem though, is that when your running through your code, and > utilizing open-uri, it''s utilizing blocking sockets, in which whenever a > read method is called on the socket, if there is no data available, it will > wait till there is data available.I can see the concern there ... I would think (perhaps incorrectly) that adding Thread.pass as part of the progress_proc callback would alleviate some of that, though... In any case, I actually see the same problems with painting without using open-uri. I created a stripped down sample here: http://www.mcgeecorp.com/downloads/xrc_test.zip It uses the same xrc and base class as my program with open-uri, etc... but in this case I am just using sleep calls and an upto loop to simulate work. Any other ideas? What version of wxWidgets is bundled with the gem? Could that be an issue here? Thanks! Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> Well, I am still seeing some strangeness under Windows ... not sure if > I''ve done something wrong, though... As before this all works fine on my > Mac... > > t.join > t.kill if t.alive? # kill threadHave you tried without Thread#join? It''s only really necessary in a script to prevent the whole thing exiting, whereas in a GUI app things will keep running anyway until the app exits (which will presumably check pending downloads). alex
Alex Fenton wrote:> Tim Ferrell wrote: >> Well, I am still seeing some strangeness under Windows ... not sure if >> I''ve done something wrong, though... As before this all works fine on my >> Mac... >> >> t.join >> t.kill if t.alive? # kill thread > Have you tried without Thread#join? It''s only really necessary in a > script to prevent the whole thing exiting, whereas in a GUI app things > will keep running anyway until the app exits (which will presumably > check pending downloads). > > alexThe code snippet I provided did not give enough context ... that snippet is called in a loop and I use Thread#join to have the loop wait until that thread finishes before proceeding to the next item to process... I would need to do some kind of polling of thread status otherwise and that just seemed cleaner... I did notice another thing of interest with the xrc_test I did... Bear in mind there is not any blocking IO going on - just a simple counter loop with a short sleep to simulate work... I had the following code in an update method in the subclass of the xrc-derived progress dialog: def update self.layout self.fit self.center(Wx::BOTH) Wx::get_app.yield end and I noticed painting artifacts and the like even though there was little going on. I took out this method call (which I had after I updated the label text as part of the counter loop) and the painting problems went away! Of course the dialog does not resize itself then so that isn''t really a fix, but it did hint at the problem being one of the calls made in that method. I commented them out in turn and discovered that it is the Frame#fit method that is causing the painting weirdness. Any ideas what could be causing that? Is my usage of it proper? Thanks! Tim -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> The code snippet I provided did not give enough context ... that snippet > is called in a loop and I use Thread#join to have the loop wait until > that thread finishes before proceeding to the next item to process... I > would need to do some kind of polling of thread status otherwise and > that just seemed cleaner... >This is from the pickaxe documentation of Thread.join: "The calling thread will suspend execution and run /thr/. Does not return until /thr/ exits. Any threads not joined will be killed when the main program exits." I don''t think this is what you want - i.e. to suspend execution of the GUI thread while the download runs. If you want the GUI to keep updating, polling is the way to go. It needn''t be complicated. In your case it could be a simple as something like this, assuming the download is in a thread saved as @download_thread Wx::Timer.every(100) do if not @download_thread.status # false = finished normally, nil = finished with exception @download_thread = start_next_job end end Using Wx::Timer is probably preferable to sleep in your circumstance as it will give more reliable timings.> I commented them out in turn and discovered > that it is the Frame#fit method that is causing the painting weirdness. > Any ideas what could be causing that? Is my usage of it proper? >Looks OK to me. I guess it''s just because calling fit causes a repaint, which isn''t otherwise visible. Hopefully using the threads right will resolve this hth alex
Alex Fenton wrote:> Looks OK to me. I guess it''s just because calling fit causes a repaint, > which isn''t otherwise visible. Hopefully using the threads right will > resolve this >Hey Alex - I tried your suggestion, removing Thread#join in favor of a polling loop... and I still see the same odd painting errors on XP and the GUI remains unresponsive to user interaction (no matter how hard I click *grin*)... Maybe I''m just not understanding this clearly... as I see it there are 3 threads involved here: the main program thread, the gui thread spawned by Wx::App.main_loop, and the thread I am spawning to wrap the open-uri call... I think I have a handle on having them all pass off to each other and the app works fine on my mac and on Ubuntu - in both my test app and the full program I only see the painting problems and unresponsiveness under Windows... Could this be the result of some bug in wxWidgets? What version is bundled with the wxRuby gem? If it would help I''d be happy to try and set up a debuggable environment here ... I assume I''d need VC6 to compile since that is what the One-Click installer uses but are there any special steps I''d need to take with wxMSW and wxRuby? Is there, perhaps, a guide to setting up all of this from source? In any case, thanks much to you and Mario both for your help and suggestions thus far :-) Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> Alex Fenton wrote: > > I tried your suggestion, removing Thread#join in favor of a polling > loop... and I still see the same odd painting errors on XP and the GUI > remains unresponsive to user interaction (no matter how hard I click > *grin*)... >I''m sorry that didn''t help. Is this a problem only when downloading in the background, or even with a fake work task like ''sleep''?> Maybe I''m just not understanding this clearly... as I see it there are 3 > threads involved here: the main program thread, the gui thread spawned > by Wx::App.main_loop, and the thread I am spawning to wrap the open-uri > call... >main_loop doesn''t spawn a thread itself - the main program thread is the GUI thread. That''s why I was thinking that things that temporarily halt execution of that thread - like join - could stall updates.> Could this be the result of some bug in wxWidgets? What version is > bundled with the wxRuby gem?The version is that latest stable 2.8.7. It could be a wx bug, but much more likely is a problem in the way Ruby threads interact with the event and scheduling internals. All of the GUI toolkits face this problem - that they are not designed with ruby''s threading model in mind.> If it would help I''d be happy to try and > set up a debuggable environment here ... I assume I''d need VC6 to > compile since that is what the One-Click installer uses but are there > any special steps I''d need to take with wxMSW and wxRuby? Is there, > perhaps, a guide to setting up all of this from source?Thanks for the offer. We use VC 7.1 as 6.0 isn''t available. The wiki has several quite detailed pages on compiling wxWidgets and wxRuby. I''m not sure how helpful this will be, though - I think it''s more a question of designing the use of threads to avoid the problem. Are you able to reduce it to a simple standalone sample - perhaps by altering the samples/etc/threaded.rb example to demonstrate it? thanks alex
Alex Fenton wrote:> I''m sorry that didn''t help. Is this a problem only when downloading in > the background, or even with a fake work task like ''sleep''?This happens even with the test app I posted above that simulates the download thread work with a short sleep call... and, again, only under Windows :-) Here is the link: http://www.mcgeecorp.com/downloads/xrc_test.zip> main_loop doesn''t spawn a thread itself - the main program thread is the > GUI thread. That''s why I was thinking that things that temporarily halt > execution of that thread - like join - could stall updates.Ah... thanks for the clarification :-) Although I am new to wxRuby (and, as such, still getting my bearings), it looks to have a great blend of features and I''d like to get to a point where I can contribute in some way or another. Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> This happens even with the test app I posted above that simulates the > download thread work with a short sleep call... and, again, only under > Windows :-) > > Here is the link: http://www.mcgeecorp.com/downloads/xrc_test.zip >Attached is a version of your main app code restructured along the lines I was suggesting. The jobs run in threads, and a Wx::Timer repeating task (monitor) checks the state of the queue and whether cancelled. This works well for me on OS X and Windows. I think the problem may be using ruby''s sleep in the main thread - I guess on Windows this stops wx updating to screen. I don''t think you need to call update at all.> Although I am new to wxRuby (and, as such, still getting my bearings), > it looks to have a great blend of features and I''d like to get to a > point where I can contribute in some way or another.Thanks. Just being patient with problems and working through them on the list is much appreciated. Even if no-one remembers the solution, it''s archived and searchable. cheers alex -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: xrc_test.rb Url: http://rubyforge.org/pipermail/wxruby-users/attachments/20080228/b7fbed98/attachment.pl
Alex Fenton wrote:> > Attached is a version of your main app code restructured along the lines > I was suggesting. The jobs run in threads, and a Wx::Timer repeating > task (monitor) checks the state of the queue and whether cancelled. > > This works well for me on OS X and Windows. I think the problem may be > using ruby''s sleep in the main thread - I guess on Windows this stops wx > updating to screen. > > I don''t think you need to call update at all. >It ran fine for me on XP (thanks!) but for some reason when running it on my Mac it just freezes up as soon as it starts... I''m on Leopard (10.5.2) using MacPorts for ruby with the latest wxruby gem... Any ideas what might be happening?? Sorry to be such a bother... Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> It ran fine for me on XP (thanks!) but for some reason when running it > on my Mac it just freezes up as soon as it starts... I''m on Leopard > (10.5.2) using MacPorts for ruby with the latest wxruby gem... Any ideas > what might be happening??I tried it on 10.5.2 with system ruby + 1.9.4 gem - after a few runs, I see what you mean. I got this error message once. 2008-02-28 23:06:02.105 ruby[64018:10b] *** Exception handlers were not properly removed. Some code has jumped or returned out of an NS_DURING...NS_HANDLER region without using the NS_VOIDRETURN or NS_VALUERETURN macros. Which makes me think that the thread scheduler is jumping out of some critical portion of GUI code. I reworked the example so that all GUI updating is done through the main thread, using monitor to check progress with thread-local variables. This seems to work reliably now on OS X. hth alex __ def start_job(idx) Thread.abort_on_exception = true url = "blah #{idx}" fname = File.basename(url) @dialog.status_label.label = "Faking download #{idx} of #{10}: #{fname}" @dialog.update @dialog.progress_all.value = (100 * (idx/10.0)).to_i @worker = Thread.new do Thread.current[:progress] = 0 1.upto(10) do | count | break if @dialog.cancelled Thread.current[:progress] = (100 * (count/10.0)).to_i sleep(0.1) end if @dialog.cancelled Thread.stop end end end # Test the state of the queue and worker progress def monitor_jobs if not @worker # Get started @dialog.show start_job(@job_n) elsif @worker and @job_n < 10 # Still running if @dialog.cancelled # test for cancellation finish end if @worker.status @dialog.progress_item.value = @worker[:progress] else # start next start_job(@job_n += 1) end else # completed finish end end
Hi! I followed the ''problem with Gauge on windows xp...'' discussion and I''m having difficulties to understand the resulting code from Alex. As my problems are not connected to Tims problem, I start a new one. Please bear in mind that these are questions because I would like to understand the code and are not meant as a critical comment about the code. Am 28 Feb 2008 um 23:31 hat Alex Fenton geschrieben:> >def start_job(idx) > Thread.abort_on_exception = true > url = "blah #{idx}" > fname = File.basename(url) > @dialog.status_label.label = "Faking download #{idx} of #{10}: #{fname}"1. Is there any advantage of writing "#{10}" instead of "10" here, I don''t see?> @dialog.update > @dialog.progress_all.value = (100 * (idx/10.0)).to_i > @worker = Thread.new do > Thread.current[:progress] = 02. Is Thread.current not identical with self inside the block? 3. Why do we have to define the thread-local progress here? Without this line it would also be defined as a thread-local variable in the upto-block, wouldn''t be a block variable and would so survive the block cycles, wouldn''t it?> 1.upto(10) do | count | > break if @dialog.cancelled > Thread.current[:progress] = (100 * (count/10.0)).to_i4. Why is the former @dialog.update now unnecessary?> sleep(0.1) > end > if @dialog.cancelled > Thread.stop > end5. Why not directely ''Thread.stop if @dialog.cancelled'' in the upto- block? finish exits in this case anyway. What am I overlooking?> end >endAm 28 Feb 2008 um 21:15 hat Alex Fenton geschrieben:> def finish > @monitor.stop > if @dialog.cancelled > @dialog.hide > md = Wx::MessageDialog.new(@dialog, "You cancelled!", > TITLE, > Wx::OK|Wx::ICON_EXCLAMATION) > md.show_modal > code = 1 > else > @dialog.progress_all.value = 100 > @dialog.hide > md = Wx::MessageDialog.new(@dialog, ''Fake download(s) complete!'', > TITLE, Wx::OK, Wx::DEFAULT_POSITION) > md.show_modal > @dialog.hide6. Why do we need ''@dialog.hide'' two times?> code = 0 > end > @dialog.destroy > exit(code) > endThanks in advance if someone is willing to help me understand. Dirk
Hi Dirk Dirk Traulsen wrote:> I followed the ''problem with Gauge on windows xp...'' discussion and I''m > having difficulties to understand the resulting code from Alex. As my > problems are not connected to Tims problem, I start a new one. >Cool - it''s been a useful thread for me too.> Please bear in mind that these are questions because I would like to > understand the code and are not meant as a critical comment about the > code. >I made quick minimal changes to the original code to address the threading only, and Tim''s code was extracted from a real app, so some elements may well be redundant.>> @dialog.status_label.label = "Faking download #{idx} of #{10}: #{fname}" >> > > 1. Is there any advantage of writing "#{10}" instead of "10" here, I > don''t see? >No, there isn''t. I guess in Tim''s code it was a place holder where in the real code a variable would go.>> @dialog.update >> @dialog.progress_all.value = (100 * (idx/10.0)).to_i >> @worker = Thread.new do >> Thread.current[:progress] = 0 >> > > 2. Is Thread.current not identical with self inside the block? >No, self remains the same as it was (in this case, the instance of XRCTest). It''s a new execution thread, but nothing about the scope changes. Compare - a File.open do ... end block doesn''t change "self" to the file within it.> 3. Why do we have to define the thread-local progress here? Without > this line it would also be defined as a thread-local variable in the > upto-block, wouldn''t be a block variable and would so survive the block > cycles, wouldn''t it? >Because potentially "monitor" could be called again in the main thread, and in that, access the local variable before the execution in the worker thread reaches the point where the local variable is set. Then it''d be passing nil to progress.value= which is an exception. Try putting a "sleep 1" in the line immediately before this, and see what happens.>> 1.upto(10) do | count | >> break if @dialog.cancelled >> Thread.current[:progress] = (100 * (count/10.0)).to_i >> > > 4. Why is the former @dialog.update now unnecessary? >I think Tim was calling update because he wasn''t seeing the dialog refreshing. But it happened not to be the root of the problem. All of the in-built GUI widgets in wxRuby redraw themselves automatically as their state changes. Calling fit or layout upon a sizer is only needed when the contents of a container are changed - a widget is removed, or added.>> sleep(0.1) >> end >> if @dialog.cancelled >> Thread.stop >> end >> > > 5. Why not directely ''Thread.stop if @dialog.cancelled'' in the upto- > block? finish exits in this case anyway. What am I overlooking? >That should work too and save a few lines, though I haven''t tested.>> @dialog.progress_all.value = 100 >> @dialog.hide >> md = Wx::MessageDialog.new(@dialog, ''Fake download(s) complete!'', >> TITLE, Wx::OK, Wx::DEFAULT_POSITION) >> md.show_modal >> @dialog.hide >> > > 6. Why do we need ''@dialog.hide'' two times? >We don''t :) hth alex
Alex, thank you for your clear and helpful explanations. Dirk
Alex Fenton wrote:>> 1. Is there any advantage of writing "#{10}" instead of "10" here, I >> don''t see? >> > No, there isn''t. I guess in Tim''s code it was a place holder where in > the real code a variable would go.That is indeed the case...>> 4. Why is the former @dialog.update now unnecessary? >> > I think Tim was calling update because he wasn''t seeing the dialog > refreshing. But it happened not to be the root of the problem. All of > the in-built GUI widgets in wxRuby redraw themselves automatically as > their state changes. > > Calling fit or layout upon a sizer is only needed when the contents of a > container are changed - a widget is removed, or added.I was trying anything that seemed relevant to get the dialog to resize as I changed the label text :-) Actually I still find the call to #fit necessary in order to have the dialog resize when the label text grows beyond what the current dialog size can show...>> 5. Why not directely ''Thread.stop if @dialog.cancelled'' in the upto- >> block? finish exits in this case anyway. What am I overlooking? >> > That should work too and save a few lines, though I haven''t tested. >I condensed to that as I factored this code back into my app with no ill effects... Thanks again to Alex for all the help with this! One good thing that has come out of this - it has definitely reinforced my commitment to cross-platform *testing* as I develop... :-) Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Well, all is working fine now with a few tweaks... so consider this a followup with a small question :-) I ended up with a situation where the worker thread was not getting enough time (i.e. downloads were very slow) with the initial approach of using a timer to call Thread.pass every 10ms and, I noticed a slight improvement as I lowered that down (eventually) to every 1ms... but this still downloaded considerably slower than the "thread-less" console version I started from. In an attempt to address this, I did away with the timer calling Thread.pass and elected instead to add a Thread#join(limit) call just after the progress bar status is updated, like so: # Test the state of the queue and worker progress protected def monitor_queue if not @worker # start queue @dialog.show process_queue_item(@current_item) elsif @worker and @current_item < @queue.size - 1 finish(@worker.status, @worker[:tmpfile]) if @dialog.cancelled # still running? if @worker.status # update progress bar @dialog.progress_item.value = @worker[:progress] @worker.join(0.095) # << this is what I added else # start next process_queue_item(@current_item += 1) end else finish(@worker.status, @worker[:tmpfile]) end end As I understand it, the limit parameter gives the Thread a certain amount of time to work (in this case 95ms) before resuming the normal scheduling behavior ... the end result in this case seems to be a better balance between ui responsiveness and thread activity for this particular app. My question: since this is my first use of Thread with wx, does anyone see any potential pitfalls I may have overlooked? I did notice in testing that the Wx::Timer resolution seemed to vary significantly from platform to platform (as the docs suggested would be the case) so I suppose that there could be a bit of contention between the Thread.join call and successive calls to the monitor_queue method... Would there be any harm in wrapping the Thread.join call with some kind of flag to tell monitor_queue to skip over its normal processing if the worker thread is currently joined? # Test the state of the queue and worker progress protected def monitor_queue if not @working if not @worker # start queue @dialog.show process_queue_item(@current_item) elsif @worker and @current_item < @queue.size - 1 finish(@worker.status, @worker[:tmpfile]) if @dialog.cancelled # still running? if @worker.status # update progress bar @dialog.progress_item.value = @worker[:progress] @working = true @worker.join(0.095) @working = false else # start next process_queue_item(@current_item += 1) end else finish(@worker.status, @worker[:tmpfile]) end end end Thanks for any feedback... Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> @worker.join(0.095) # << this is what I added > >...> As I understand it, the limit parameter gives the Thread a certain > amount of time to work (in this case 95ms) before resuming the normal > scheduling behavior ... the end result in this case seems to be a better > balance between ui responsiveness and thread activity for this > particular app. >Interesting, I''ve never used this parameter ebefore.> My question: since this is my first use of Thread with wx, does anyone > see any potential pitfalls I may have overlooked? >None that I can see. Overall I think experience with Threads + wxRuby so it''s mainly a case of trying to see what works across platforms. Thanks for keeping us posted on your findings.> Would there be any harm in wrapping the Thread.join call with some kind > of flag to tell monitor_queue to skip over its normal processing if the > worker thread is currently joined? >Not that I can see. I''m not sure it would ever reach that point - would the main thread start running again until the limit expires? cheers alex
Alex Fenton wrote:> Interesting, I''ve never used this parameter ebefore.Me either :-) It does seem to work, though, joining for the amount of time specified... This gave me a little more flexibility to time things in such a way that the ui generally stays responsive while still letting the download thread have some undivided time to work... Downloads are much faster for me this way... they do still feel a little bit throttled but overall it feels like a better balance so I am happy for now... although I may try adjusting the timer/limit to smaller increments and see how that affects ui responsiveness and download speeds... :-)>> Would there be any harm in wrapping the Thread.join call with some kind >> of flag to tell monitor_queue to skip over its normal processing if the >> worker thread is currently joined? >> > Not that I can see. I''m not sure it would ever reach that point - would > the main thread start running again until the limit expires?Well, maybe I''m off the mark here but I was thinking that the Timer would fire the monitor-queue method call every 100ms regardless of the status of the joined thread and that, as such, it might end up calling the method while a previous invocation was still active... I may be overthinking it though! I have not yet had much exposure to Ruby''s threads... :-) Cheers, Tim -- Posted via http://www.ruby-forum.com/.
Tim Ferrell wrote:> > Downloads are much faster for me this way... they do still feel a > little bit throttled but overall it feels like a better balance so I am > happy for now... although I may try adjusting the timer/limit to smaller > increments and see how that affects ui responsiveness and download > speeds... :-)Well, if it helps anyone else, I discovered that setting the join limit too low caused crashes, at least on the Mac... I ended up using 10ms monitor interval and a 50ms join limit... I also had to remove the check of @dialog.canceled from within the thread as this occasionally led to malloc double free errors. Now the monitor_queue method just kills the thread if the user requests to cancel...> > Well, maybe I''m off the mark here but I was thinking that the Timer > would fire the monitor-queue method call every 100ms regardless of the > status of the joined thread and that, as such, it might end up calling > the method while a previous invocation was still active... >This concern proved to be unfounded as joining the thread inhibits the timer from firing until the thread releases the join... Thanks to everyone for all the help with this!! Cheers, Tim -- Posted via http://www.ruby-forum.com/.