tkinter redraw rates

Dave Angel davea at davea.name
Thu Jul 18 00:52:16 EDT 2013


On 07/18/2013 12:38 AM, fronagzen at gmail.com wrote:
> On Thursday, July 18, 2013 9:07:24 AM UTC+8, Dave Angel wrote:
>> On 07/17/2013 08:44 PM, fronagzen at gmail.com wrote:
>>> On Thursday, July 18, 2013 1:38:34 AM UTC+8, Dave Angel wrote:
>>>> On 07/17/2013 09:18 AM, fronagzen at gmail.com wrote:
>>>>> On Wednesday, July 17, 2013 7:42:45 PM UTC+8, Dave Angel wrote:
>>>>>> On 07/17/2013 07:10 AM, fronagzen at gmail.com wrote:
>>>>>>> On Wednesday, July 17, 2013 6:07:22 PM UTC+8, Dave Angel wrote:
>>>>>>>> On 07/16/2013 11:04 PM, fronagzen at gmail.com wrote:
>>>>>>>>> Noted on the quoting thing.
>>>>>>>>> Regarding the threading, well, first, I'm not so much a programmer as someone who knows a bit of how to program.
>>>>>>>>> And it seems that the only way to update a tkinter window is to use the .update() method, which is what I was experimenting with. Start up a new thread that just loops the .update() with a 1ms sleep until the download is done. It seems to work, actually.
>>>>>>>> update() is to be used when it's too awkward to return to mainloop.  In
>>>>>>>> my second approach, you would periodically call it inside the processing
>>>>>>>> loop.  But unless tkinter is unique among GUI's, it's unsafe to do that
>>>>>>>> in any thread besides the GUI thread.
>>>>>>>> DaveA
>>>>>>> Yes, based on advice from this thread, I'm doing that. From my main thread, I create a thread that handles the download while updating a variable that the mainloop displays as a text output, and in that mainloop, I have a while loop that updates the GUI until the downloading is done.
>>>>>> I can't figure out what you're really doing, since each message from you
>>>>>> says something different.  You don't need a separate while loop, since
>>>>>> that's exactly what app.mainloop() is.
>>>>>> --
>>>>>> DaveA
>>
>>>>> Hm. My apologies for not being very clear. What I'm doing is this:
>>>>>            self.loader_thread = Thread(target=self.loadpages,
>>>>>                                        name="loader_thread")
>>>>>            self.loader_thread.start()
>>>>>            while self.loader_thread.isAlive():
>>>>>                self.root_window.update()
>>>>>                sleep(0.05)
>>>>> Where loadpages is a function defined elsewhere.
>>
>>>> Presumably this fragment is from a method of some class you've written.
>>>>     Is it an event handler, or is this happening before you finish setting
>>>> up the GUI?  Somewhere at top-level, you're supposed to fall into a call
>>>> to mainloop(), which doesn't return till the user cancels the app.
>>>> --
>>>> DaveA
>>
>>> This is, indeed, an event handler from a class for my GUI. My entire GUI is a bit large, so I'll not copy the entire thing here, but it roughly goes:
>>> class GUI(object):
>>>       def __init__(self):
>>>           [stuff]
>>>       def init_button(self):
>>>           self.execute = ttk.Button(self.input_frame, text='Tally',
>>>                                     command=self.execute_now)
>>>           self.execute.grid(column=1, row=2, sticky=(N, S, E, W), columnspan=4)
>>>       def execute_now(self):
>>>           [stuff]
>>>           self.loader_thread = Thread(target=self.loadpages,
>>>                                       name="loader_thread")
>>>           self.loader_thread.start()
>>              self.root_window.after(100, self.test_thread)
>>              return
>>>           while self.loader_thread.isAlive():
>>>               self.root_window.update()
>> Nope - don't use that.  Instead, post an event on the queue, and return
>> to the mainloop() from whence we came.
>>           def test_thread(self):
>>              if self.loader_thread.isAlive():
>>                  self.root_window.after(100, self.test_thread)
>>                  return
>>              [morestuff]
>>>               sleep(0.05)
>>>           [morestuff]
>>> if __name__ == "__main__":
>>>       APP = GUI()
>>>       APP.root_window.mainloop()
>> I probably don't have it quite right, but hopefully you'll get the idea.
>>    self.test_thread() is now a new event that will get repeatedly
>> invoked, to do the check on the thread status.  It returns rapidly
>> unless the condition has occurred.
>> There are other things that should be done, like blocking the specific
>> events that would create duplicate threads.
>> --
>>
>> DaveA
>
> I see, though it should be noted that your method doesn't actually block the rest of the even handler code from running, had to fiddle with it a bit to get that to work. May I ask what exactly is the rationale behind implementing it like this, though?
>

Simply because your goal is normally to have other events working.  For 
example, a user might resize the window while you're doing your network 
stuff.  This way all events are fired from the one place, and you can 
uniformly decide which ones to block for the special cases.  And you 
don't wind up with the GUI's internal code in a knot.  As I said at the 
beginning, I'm not that familiar with tkinter, but the same principles 
apply to all of them.

Sometimes these variations are equivalent, and sometimes there are 
problems.  For example, you might be testing on Linux, and then later 
when you try it on Windows, suddenly things don't work.  The closer you 
can conform to the standard way of using the GUI manager, the less 
likely you are to come up with strange problems, now or later.

-- 
DaveA




More information about the Python-list mailing list