Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

How to change a Tk object's property from a thread

by santi_h87 (Novice)
on Aug 04, 2011 at 19:39 UTC ( [id://918632]=perlquestion: print w/replies, xml ) Need Help??

santi_h87 has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks, I seek your everlasting wisdom. I am starting to think that Tk and Threads don't mix well... I'll go straight to the point: How can I change a Tk object's property from a different thread? I am using Windows XP Here's my code, I want to set the button's text to "hey" (lower case) from the subrutine thread_sub

use Tk; use threads; use threads::shared; my $mw = new MainWindow(); my $button = $mw->Button(-text => "HEY")->pack(); my $thread = threads->create(\&thread_sub); MainLoop(); sub thread_sub { for (my $i=0; $i<10_000_000; $i++) { # DO NOTHING. LET THE TIME BE TIME } # CHANGE BUTTON'S TEXT PROPERTY TO "hey" HERE }

Thank you, and may the Perl gods be with you.

Replies are listed 'Best First'.
Re: How to change a Tk object's property from a thread
by ikegami (Patriarch) on Aug 04, 2011 at 23:57 UTC

    Wrong question. Correct question: How do you notify Tk's thread that a property should be changed?

    You could poll a Thread::Queue.

    use strict; use warnings; use threads; use Thread::Queue qw( ); use Time::HiRes qw( sleep ); use Tk qw( MainLoop ); my $q = Thread::Queue->new(); my $mw = MainWindow->new(); my $text = $mw->Entry(-width => 8)->pack(); $mw->repeat(100, sub { while (defined(my $command = $q->dequeue_nb())) { $text->delete('0.0', "end"); $text->insert('0.0', $command); $mw->update(); } }); # Some worker. async { for (;;) { sleep(0.2 + 1.5 * rand()); $q->enqueue(chr(ord('A') + rand(26))); } }->detach(); MainLoop();
Re: How to change a Tk object's property from a thread
by jimicarlo (Initiate) on Aug 04, 2011 at 20:18 UTC
    Tk already has "threads" :-S try this:
    use Tk; my $mw = new MainWindow(); my $button = $mw->Button(-text => "HEY")->pack(); $mw->after(1,\&thread_sub); # after 1 ms, calls thread_sub # it'll run like a thread, but isn't really! # See http://www.foo.be/docs/tpj/issues/vol1_3/tpj0103-0006.html MainLoop(); sub thread_sub { for (my $i=0; $i<10_000_000; $i++) { # DO NOTHING. LET THE TIME BE TIME } # CHANGE BUTTON'S TEXT PROPERTY TO "hey" HERE $button->configure(-foreground=>'red'); }
    It's a weird example though... what were you planning on doing? Do you really need threads or would Tk timeslicing do?
      This might also be useful: http://www.perlmonks.org/bare/?node_id=732294
        This had exactly what I needed, thank you!
      Thanks jimmi! What I want to do is have a udp socket receive packets from another udp socket in a separate thread (otherwise the GUI freezes), and when the socket receives the message "STOP", then it would disable the Disconnect button. Your code works well but it freezes the GUI when the thread is running. Thanks for your answer though!
Re: How to change a Tk object's property from a thread
by zentara (Archbishop) on Aug 05, 2011 at 15:28 UTC
    Tk is not thread safe, that is why you get the GUI freeze you described in the node above. You cannot directly access a Tk widget from a thread. 

    Workarounds are available. The most foolproof way is to have a shared variable in the thread which gets set to a value signalling the main Tk thread that it's time to change the button text, which ikegami showed. Tk must run a timer to watch that shared variable, here is a simpler version to demonstrate.

    #!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; # declare, share then assign my $ret; share $ret; $ret = 0; my $val = 0; #create thread before any tk code is called my $thr = threads->create( \&worker ); use Tk; my $mw = MainWindow->new(); my $label = $mw->Label( -width => 50, -textvariable => \$val )->pack(); my $timer = $mw->repeat(10,sub{ $val = $ret; }); MainLoop; # no Tk code in thread sub worker { for(1..10){ print "$_\n"; $ret = $_; sleep 1; } $ret = 'thread done, ready to join'; print "$ret\n"; }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: How to change a Tk object's property from a thread
by zentara (Archbishop) on Aug 06, 2011 at 14:00 UTC
    I am starting to think that Tk and Threads don't mix well

    If you are willing to switch to Gtk2, it DOES allow you to access Gtk2 objects from a thread.

    A simple example:

    #!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; use Glib qw/TRUE FALSE/; use Gtk2 qw/-init -threads-init/; Glib::Object->set_threadsafe (TRUE); #setup shared hash my %shash; share(%shash); #will work for first level keys $shash{'go'} = 0; $shash{'work'} = ''; $shash{'die'} = 0; my $window = Gtk2::Window->new('toplevel'); $window ->signal_connect( 'destroy' => \&delete_event ); $window->set_border_width(10); $window->set_size_request(300,300); my $vbox = Gtk2::VBox->new( FALSE, 6 ); $window->add($vbox); $vbox->set_border_width(2); my $hbox= Gtk2::HBox->new( FALSE, 6 ); my $hbox1 = Gtk2::HBox->new( FALSE, 6 ); $vbox->pack_end($hbox,FALSE,FALSE,0); $vbox->pack_end (Gtk2::HSeparator->new, FALSE, FALSE, 0); $vbox->pack_end($hbox1,FALSE,FALSE,0); $hbox->set_border_width(2); $vbox->pack_end (Gtk2::HSeparator->new, FALSE, FALSE, 0); my $ebutton = Gtk2::Button->new_from_stock('gtk-quit'); $hbox->pack_end( $ebutton, FALSE, FALSE, 0 ); $ebutton->signal_connect( clicked => \&delete_event ); my $pbar = Gtk2::ProgressBar->new(); $pbar->set_pulse_step(.1); $hbox->pack_start($pbar,1,1,0); my $count = 0; my $label_w_markup = Gtk2::Label->new(); $label_w_markup->set_markup("<span foreground=\"yellow1\" size=\"40000\">$count</span>"); $vbox->pack_end($label_w_markup,FALSE,FALSE,4); ###################################################### my $tbutton = Gtk2::Button->new_with_label('Run Thread'); $hbox1->pack_start($tbutton , 1, 1, 0 ); my $lconnect = $tbutton->signal_connect( clicked => sub{ launch() }); my $sconnect; $window->show_all(); $pbar->hide; #needs to be called after show_all #create 1 sleeping thread passing it the label and pbar to control # notice you can create the thread anywhere, not like Tk, where # threads need to be created before a Tk code is invoked my $thread = threads->new(\&work, $label_w_markup, $pbar); Gtk2->main; ###################################### sub delete_event { $shash{'go'} = 0; $shash{'die'} = 1; $thread->join; Gtk2->main_quit; return FALSE; } ####################################### sub launch{ $pbar->show; $tbutton->set_label('Stop Thread'); $tbutton->signal_handler_block($lconnect); $sconnect = $tbutton->signal_connect( clicked => sub{ stop() }); $shash{'go'} = 1; } ################################################## sub stop{ print "stopped\n"; $shash{'go'} = 0; $pbar->hide; $tbutton->set_label('Run Thread'); $tbutton->signal_handler_block ($sconnect); $tbutton->signal_handler_unblock ($lconnect); } ######################################################### sub work{ my ($label,$pbar) = @_; $|++; while(1){ if($shash{'die'} == 1){ goto END }; if ( $shash{'go'} == 1 ){ foreach my $num (1..1000){ Glib::Idle->add( sub{ if($shash{'die'} == 1){ return }; $label->set_markup("<span foreground=\"yellow1\" size=\"40000\">$num</span>"); $pbar->pulse; return FALSE; }); select(undef,undef,undef, .1); if($shash{'go'} == 0){last} if($shash{'die'} == 1){ goto END }; } $shash{'go'} = 0; #turn off self before returning }else { select(undef,undef,undef,.1) } #sleep time } END: } #######################################

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://918632]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (4)
As of 2024-04-25 23:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found