1

I'm putting together a tkinter gui app and occasionally when loading a new frame and adding it to a notebook the gui will freeze up on the call to update. I'm not sure what to provide for code, I have a lot of subclassing going on so posting all of that would get ridiculous, and I'm not sure if it would be usefull at all.

def _handle_events_button(self):
    ntbk = self.find_notebook()
    print("1: " + str(int(time.time())))
    w = EventListDetailsCombo(ntbk)
    print("2: " + str(int(time.time())))
    w.load()
    print("3: " + str(int(time.time())))
    w.add_to_notebook(ntbk)
    print("4: " + str(int(time.time())))
    # clipboard.root.update_idletasks()
    print("5: " + str(int(time.time())))
    ntbk.select(w)
    print("6: " + str(int(time.time())))

The output of the prints is this:

# Working correctly 
1: 1555952235
2: 1555952235
3: 1555952235
4: 1555952235
5: 1555952237
6: 1555952237

# Hang / Delay
1: 1555952240
2: 1555952240
3: 1555952240
4: 1555952240
5: 1555952266
6: 1555952266

That is with update_idletasks() without update_idletasks() all of these outputs are within 1-2 seconds of each other. The hang instead happens in the mainloop update_idletasks

Some Notes:

  • It doesn't freeze consistently it seems pretty random, but it does seem to happen every 4 to 5 tries.
  • The length of the freeze also seems random sometimes its 30 seconds sometimes its several minutes.
  • If I click other buttons while its frozen it will execute those actions after the freeze stops
  • usually if I terminate the app from pycharm the frame will load for a second before the app terminates.

I'm not sure where to go from here, any pointers on things I could look into?

Edit: So I went through and removed any calls to update and now it just hangs after this code, I assume in the main loop update step but I'm not really sure how to check that assumption, this _handle_events_button function is called directly from the events button so there shouldn't be anymore of my code after this point.

Edit 2: If I leave update_idletasks in and run a profiler on it, when its working properly it looks like this:

enter image description here

Everything looks the same on the runs where it hangs, except that instead of method 'call' taking about 1000ms it takes 25000ms or more, with roughly the same number of calls to 'call'.

Edit 3: I have added in the output of the print statements. Also I can not throw any of this function into a separate thread since it is all just preparing widgets.

Sean O
  • 177
  • 9
  • 2
    *"`.update_idletasks()`"* is not needed in general, therfore you are doing something unusual to prevent the `root.mainloop()` from running. [Edit] your Question and explain in detail **why** do you need `.update_idletasks()`? – stovfl Apr 22 '19 at 15:38
  • It turns out I probably don't need .update_idletasks(), that was just me not understanding things and thinking it helped with this issue when I added it. I edited my question with the fact that I have now removed all those calls. – Sean O Apr 22 '19 at 16:21
  • Can we agree, your *"freeze"* is only a `delay`? – stovfl Apr 22 '19 at 17:26
  • Yes I think we can agree that it is a delay. Something is still executing but to the user it looks like the program has froze. – Sean O Apr 22 '19 at 17:43
  • *"we can agree that it is a delay."*: So, i recommend to move `load()` or **all** from `_handle_events_button(...` into a own `Thread`. Read [use threads to preventing main event loop from “freezing”](https://stackoverflow.com/a/16747734/7414759) – stovfl Apr 22 '19 at 17:49
  • I can try that, but I really don't think it will help, each of the print statements output within less than 2 seconds of each other, so my code doesn't seem to be taking up the extra time. I created my own main loop with a call to update and threw a profiler on that. The profiler showed all the time taken up by the calls to _tkinter.tkapp.call – Sean O Apr 22 '19 at 18:12
  • *"print statements output within less than 2 seconds"*: Then you didn't find the time consuming part preventing the `.mainloop()`. – stovfl Apr 22 '19 at 18:18
  • So your saying that something else must be getting triggered by the button event that I'm not aware of? I'm not sure what could get triggered, this function is all that I have getting called from the button. Could the update in the main loop be triggering something in one of my widgets? Maybe if I had overridden an update function or something in one of my widgets? – Sean O Apr 22 '19 at 18:25
  • [Edit] your Question and show **all** your `print(...` output in `_handle_events_button(...`. Give the `Thread` a try an see if it changes anything. We have reached our comments limit, i'm off. – stovfl Apr 22 '19 at 18:32
  • Please consider adding a code sample, or revising the one you posted in this question. As it currently stands, its formatting and scope make it hard for us to help you; here is a [great resource](http://stackoverflow.com/help/mcve) to get you started on that. Good luck with your code! – Reblochon Masque Apr 22 '19 at 23:15

2 Answers2

0

You don't want to call update/update_idletasks if you have a persistent event loop running. These functions are the alternative to a persistent event loop.

When you call it from an event handler, you are essentially running a temporary nested event loop. Which blocks the code until all applicable pending events are processed -- and can also have other undesirable side effects since the actions your current event handler is supposed to perform are only half complete and other code may see the application in an inconsistent state.

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • I did go through and remove all calls to update and update idle tasks and it now just hangs/delays after all of this code. I'll sift through my code and make sure I don't have any other calls that I missed – Sean O Apr 22 '19 at 17:41
  • @SeanO If your event handlers have long-running code, your UI wll freeze while it's running. Long-running tasks are supposed to be run in worker threads. – ivan_pozdeev Apr 22 '19 at 17:49
  • It's not hanging on any of my code though. This function is called directly by the button and it makes it through it just fine without the update call. Even with the update call it will make it through all my code fine, then sometimes hang up on the update call, and then execute the rest just fine. – Sean O Apr 22 '19 at 18:05
0

So after a lot of banging my head on the desk I think I figured this out. To start with it seems like tkinter is just slow to load widgets, not sure if this is because of how many widgets I have (not all that many, probably less than 100, maybe less than 50 in each frame I load) or due to all the subclassing I'm doing (probably more than I should). Its annoying but workable for now.

What seemed to fix the occasional freeze was to stop using the tkcalender datepicker widget. As soon as I switched that out for an entry and frame based custom widget, things stopped freezing, now every time I load a frame it takes pretty much the same amount of time, still kinda slow, but consistent, which is all I really need for now.

Sean O
  • 177
  • 9