What is the proper way of implementing threading in a GTK application? - python

I am aware that this is quite a vague question, but bear with me. When trying to add threading to GTK applications, I often find my implementation to be quite cumbersome. Take an example where upon pressing a button, example_function gets called. This function calls other functions, performs a long calculation, then calls GTK code. Point is that this function performs a lot of different tasks.
The question is, how should a function (example_function) be structured? Should the entire function be moved to a separate thread, with all code that updates the UI moved into separate functions and called using GLib.idle_add? Or should it run on the main thread with only the long-taking code moved into a separate function, and having that threaded - if this were the case, then how should sequentiality be handled?
Take the following non-threaded function:
def main_function(self, widget):
print('start of main_function')
output = self.long_function(example_argument)
self.button.set_label(label=output)
print('label set')
self.another_function()
print('end of main_function')
What would be the post optimal way of adding threading to such a function, while maintaining sequentiality. Ideally, all calculations would be moved off the main UI thread.

Related

GLib.idle_add(function) has different policies for different functions

I have a GTK window. It turns out that if I schedule several redraw calls all at once, without any delay, from a separate thread, using idle_add(window.queue_draw), only one call will execute.
While if I do idle_add(custom_function), every single scheduled call to custom_function will run.
While it's clear that this is done for optimization, I can't see if/where this is mentioned in the documentation [1] and I also wonder if there are other such rules for idle_add.
[1] https://developer.gnome.org/pygobject/stable/glib-functions.html
It actually isn't idle_add that is making that behavior. The docs for widget-queue-draw-region, which gets called by queue-redraw, state that redrawing only gets done after the main loop is no longer busy.

Internal working of Tkinter after method

I'm working Tkinter GUI for quite sometime. And I was wondering, how after method actually works on it. My questions are very simple.
How after method is implemented internally in Tkinter ?
Are after calls threaded ?
What are the cons (if any) of multiple after calls in Tkinter ?
How are after callbacks scheduled ?
I did go through a couple of links like this. But couldn't get these informations.
after() calls Tck/Tk's after command, which registers a callback to be called later by the Tcl/Tk event loop.
after() calls are not threaded.
One issue is that a blocking or long-running operation may cause something scheduled with after() to run significantly later than you meant. There are many more possible pros and cons which may be relevant, but you'll need to specify your concerns or use cases...
A callback is registered into Tcl/Tk event loop, which takes care of the scheduling. For details on how that works, see the documentation.
Explanation:
As you may already know, Tkinter uses Tcl/Tk internally.
Taking a look at the code for the after() method, it appears that Tkinter is just calling the Tcl/Tk after command.
The documentation for Tcl/Tk's after command doesn't mention anything about threads. This makes sense given Tcl/Tk's single-threaded event loop design.
So, my conclusion is that Tkinter's after() method does not use threads, rather it just registers a callback using the internal Tcl/Tk after command, which will be called at the most appropriate time possible by the Tcl/Tk event loop.
It's really quite simple: Tkinter maintains a queue of work to be done (the event queue), and mainloop (as well as update) processes that queue. after merely adds a timestamped event to the queue. No threads are involved.

Is declaring function within function for threading good programming style?

I have following code for click handler in my PyQT4 program:
def click_btn_get_info(self):
task = self.window.le_task.text()
self.statusBar().showMessage('Getting task info...')
def thread_routine(task_id):
order = self.ae.get_task_info(task_id)
if order:
info_str = "Customer: {email}\nTitle: {title}".format(**order)
self.window.lbl_order_info.setText(info_str)
self.statusBar().showMessage('Done')
else:
self.statusBar().showMessage('Authentication: failed!')
thread = threading.Thread(target=thread_routine, args=(task,))
thread.start()
Is it a good practice to declare function in function for using with threads?
In general, yes, this is perfectly reasonable. However, the alternative of creating a separate method (or, for top-level code, a separate function) is also perfectly reasonable. And so is creating a Thread subclass. So, there's no rule saying to always do one of the three; there are different cases where each one seems more reasonable than the others, but there's overlap between those cases, so it's usually a judgment call.
As Maxime pointed out, you probably want to use Qt's threading, not native Python threading. Especially since you want to call methods on your GUI objects. The Qt docs article Threads, Events and QObjects in the Qt documentation gives you an overview (although from a C++, not Python, viewpoint). And if you're using a QThread rather than a threading.Thread, it is much more common to use the OO method—define a subclass of QThread and override its run method than to define a function, which makes your question moot.
But if you do stick with Python threading, here's how I'd decide.
Pro separate method:
You're doing this in a class method, rather than a function, and that the only state you want to share with the new thread is self.
Non-trivial code, longer than the function it's embedded in.
Pro local function:
Pretty specific to the info button callback; no one else will ever want to call it.
I'd probably make it a method, but I wouldn't complain about someone else's code that made it a local function.
In a different case—e.g., if the thread needed access to a local variable that had no business being part of the object, or if it were a trivial function I could write as an inline lambda, or if this were a top-level function sharing globals rather than a method sharing self, I'd go the other direction.

Two threads using same function

Basic using threads question here.
I'm modifying a program with 2 thread classes and I'd like to use a function defined in one class in both classes now.
As a thread newbie (only been playing with them for a few months) is it OK to move the function out of the thread class into the main program and just call it from both classes or do I need to duplicate the function in the other class that doesn't have it?
regards
Simon
You can call the same function from both threads. The issue to be aware of is modifying shared data from two threads at once. If the function attempts to modify the same data from both threads, you will end up with an unpredictable program.
So the answer to your question is, "it depends what the function does."
It certainly won't help to copy the function into both thread classes. What matters is what the function does, not how many copies of the code there are.
might wanna checkout thread locking. threads operating on 1 function/method can 'lock' that function in many languages so other threads can't access it at the same time. http://en.wikipedia.org/wiki/Lock_(computer_science)

stop tkinter window from freezing while program is sleeping

This is quite an essential part of my program and I need to have sorted out as soon as possible so anything would be a massive help.
My program consists of three modules which are imported to each other. One module consists of my user interface for which I am using tkinter. The user inputs data on a canvas which is sent to a second program to be processed and is then sent to the third module which contains the algorithm which I intend to step through with the user.
The "first" and "third" modules can interact with each other and during certain points in explaining the algorithm I change the appearance of the canvas and some text on the interface. The third module should then pause (for which I'm currently using a basic sleep method), and wait (ideally it will wait for the user to press the "Next Step" button on the user interface). It is during this step that my interface decides that it wants to freeze.
Is there any way I can stop this?
Many thanks in advance.
Edit: I've found a way to fix this. Thank you for all the suggestions!
Calling time.sleep() will stop your program doing anything until it finishes sleeping. You need Tkinter to keep processing events until it should run the next part of your code.
To do that, put the next part of your code in a separate function, and get Tkinter to call it when it's ready. Typically, you want this to happen when the user triggers it (e.g. by clicking a button), so you need to bind it to an event (docs). If you actually want it to happen after a fixed time, you can use the .after() method on any tkinter widget (docs).
GUI programming takes a bit of getting used to. You don't code as a series of things happening one after the other, you write separate bits of code which are triggered by what the user does.
Terminology note: if your Python files import each other, you have three modules, but it's still all one program. Talking about the "first program" will confuse people.
H.E.P -
The traditional way to do this does indeed involve using a separate thread and co-ordinating the work between the "worker" thread and the GUI thread using some sort of polling or eventing mechanism.
But, as Thomas K. points out, that can get very complex and tricky, especially regarding Python's use of the Global Interpreter Lock (GIL) etc. and having to also contend with Tkinter's processing loop.
(The only good reason to use a multi-threaded GUI is if you absolutely MUST ensure that the GUI remains responsive during a potentially long-running background task, which I don't believe is the issue in this case.)
What I would suggest instead is a generator-based "co-routine"-type architecture.
As noted in "The Python (2.7) Language Reference", Section 6.8, [the "yield" statement is used when defining a generator function and is only used in the body of the generator function. Using a yield statement in a function definition is sufficient to cause that definition to create a generator function instead of a normal function.]
(This effectively forms the basis of a co-routine architecture. (ed.))
[When a generator function is called, it returns an iterator known as a generator iterator, or more commonly, a generator. The body of the generator function is executed by calling the generator’s next() method repeatedly until it raises an exception.
When a yield statement is executed, the state of the generator is frozen and the value of expression_list is returned to next()‘s caller. By “frozen” we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, and the internal evaluation stack: enough information is saved so that the next time next() is invoked, the function can proceed exactly as if the yield statement were just another external call.]
(Also see "PEP 0342 - Coroutines via Enhanced Generators " for additional background and general info.)
This should allow your GUI to call the next part of your algorithm specification generator, on demand, without it having to be put to sleep until the operator presses the "Next" button.
You would basically just be creating a little 'domain-specific language', (DSL), consisting of just the list of steps for your presentation of this particular algorithm, and the generator (iterator) would simply execute each next step when called (on demand).
Much simpler and easier to maintain.
A GUI program is always waiting for some action to occur. When actions do occur, the event code corresponding to that action is executed. Therefore, there is no need to call sleep(). All you need to do is set it up so that the third program is executed from the appropriate event.

Categories

Resources