Hi,
There is some documentation in the Wx distribution
http://search.cpan.org/~mdootson/Wx-0.9901/lib/Wx/Thread.pod
Basically, you only get to access the gui in your main thread. None of
the wxWidgets synchronisation methods are wrapped - because they simply
don't fit with Perl's threading model.
In short, in secondary threads you have Wx::PlThreadEvent to post
events back to some event handler window, in your main thread.
If you need to pass complex data structures a common approach would be
to serialize / deserialize the information using something like
Storable::freeze / Storable::thaw.
Assuming that your total requirement is just to 'tail' a couple of files
and update a TextCtrl, this will work functionally. It is an answer to
the question, 'how do I pass information back to the main thread in a
threaded app.'
Using a Wx::Timer to poll for something periodically is a method I have
also used often - you just need to make sure you are polling the most
efficient thing. (e.g. accessing and checking a file every 0.5 seconds
probably isn't particularly efficient);
If you are looking at a general way to incorporate asynchronous
processing or non-blocking processing in your application, then that's a
question of a different order that would need a proper understanding of
what your application does to give a helpful answer.
Generally, if you are going to use threads, your application needs to be
designed with that in mind from the start with a fully developed model
for thread creation, destruction, and communication.
From my observation of other projects and comments by the author of the
threads module, there seems to be a single model favoured for using
threads in Perl in a practical and scalable way.
My interpretation of that would be to create a second thread - a thread
manager thread - as soon as possible in your application. For a Wx
application, this would certainly be before Wx is loaded.
This second thread creates new worker threads on request to carry out
your asynchronous work.
The worker threads pass information back to the main thread using a
Thread::Queue. You then need a way in your main thread to collect the
information from the queue and create events in Wx.
In prototyping this type of model in the past, I have used a timer to
check $queue->dequeue_nb periodically. An interesting alternate
possibility would be to use inter-thread signalling to tell the main
thread to check the queue.
threads->object(0)->kill('USR1');
Once you have info off the queue, creating an event out of this to
handle in your gui is fairly straightforward. When you ask the worker
thread to carry out a task, pass in the window id ( $window->GetId ).
The worker returns the windowid as part of the message it puts on the
queue. So in the main thread queue handler once you have the 'client'
window id
my $win = Wx::Window::FindWindowById($id, undef);
.....
$win->AddPendingEvent($somenewevent);
It must be pointed out that some very talented people with actual
threaded code in the wild have taken a slightly different approach in
the Padre application. The 'start a worker manager early' approach is
pretty much the same I understand but there is a different method of
passing information to and from threads that actually uses
Wx::PlThreadEvent (or did last time I browsed) and consequently loads Wx
before creating the worker-manager thread. The other major difference
between the Padre implementation and my outline is that the Padre one is
working code and mine exists, partially at least, only in my head.
It is conceptually reasonably simple so "all" you need to do is handle
thread creation and shutdown in clean way ..........
It is quite a lot of work really when all you want to do is tail a
couple of files and update a gui element.
Alternatives to threads for 'asynchronous' processing: -
The first, and not really asynchronous at all, is to use a Wx::Timer as
you suggested and carry out checks in the timer event. This can be a
good solution and is certainly the simplest. It all depends on how long
the code in your timer event handler blocks for and how much resources
it consumes.
All other methods I can think of involve using a separate process and
only differ in how they implement inter-process communication. A few
methods I have used:
Wx::Perl::ProcessStream offers IPC via stdin, stdout and stderr.
Whenever I use Wx::Perl::ProcessStream, I always think 'this is ugly'.
But it does work and it is relatively simple. For your case you would
create a separate process that watches the files and just prints a
message to stdout when there is a change. This would create an event in
your main application and you respond by updating the TextCtrl. You
could even pass the file content on stdout. It really depends on the
frequency of updates and size of the content.
Wx::SocketServer can provide socket based events to your main
application, so in this case - start a separate process, passing in your
server socket as a param. Connect with your separate process and pass
info back and forth across the connection.
An extension to this is to use an intermediate 'message broker' process.
Mine have been based around IO::Multiplex and just use that as an
extension to the basic Wx::SocketServer method to implement a one main
process to many helper processes model.
Hope the info is helpful. I think that if you simply create a separate
thread at some arbitrary point in your code, then send information back
to a window using Wx::PlThreadEvent, it will work perfectly well. It
just doesn't scale very well.
Regards
Mark
On 23/06/2011 14:26, m...@roqc.no wrote:
Hello,
We'd like to use threads in one of our Linux GTK Wx GUI's. The purpose
of the thread is to check some text files for changes every 2 seconds.
The changes need to be written back to a WxTextCtrl. I tried using
Wx::MutexGUIEnter and Wx::MutexGuiLeave to block the main thread from
execution while the GUI update is being made, but get the following error.
Error while autoloading 'Wx::MutexGuiEnter' at sqlplus_gui.pl line 594
thread 1
Do the Mutex functions not work in GTK? I see the message on the Wx
website.
"Note that under GTK, no creation of top-level windows is allowed in any
thread but the main one."
Wx::MutexGuiEnter();
$shared_data->{textctrl}->AppendText('abc');
Wx::MutexGuiLeave();
If this doesn't work in GTK, what is the best approach for having a
secondary thread relay the information back to the main thread for
updating?
Or is using Wx::Timer a better approach?
Thanks,
Mike