0

I have a GUI program built using Tkinter in python 2.7.10.

It works flawlessly, for it's root cause anyways.

Unfortunately, it briefly goes into windows dreaded "Not Responding" state when being interacted with.

Here's the layout in short:

Launch script launches Main script. Main script reads settings file and boots GUI script. GUI script starts GUI. User enters a term to search for in a series of files. GUI script goes into a side script to process files and retrieve results. Side script inherits certain aspects of GUI script. Side script attempts to update user while working using the inherited elements; the GUI has none of it. GUI goes non-responsive briefly before returning to the GUI script and displaying the results.

Here's how I need it to go in short:

Launch script launches Main script. Main script reads settings file and boots GUI script. GUI script starts GUI. User enters a term to search for in a series of files. GUI script goes into a side script to process files and retrieve results. Side script inherits certain aspects of GUI script. Side script updates the user with a progress bar and imagery while working, using the GUI elements. GUI returns to the GUI script and displays the results.

I have the progress bar built, but the imagery is not yet, but if the progress bar will not work, I will not waste my time on the imagery.

Sample impossible, not-being-used-but-shows-the-point code; GUI;

import Tkinter, PIL, SideScript1

Tkinter()

ShowText()
ShowStuff()
input = GetInput()
ShowProgressBar()
SideScript1.processfilesbasedoninput(input, progressbarcontrolvar)
DisplayResults()

SideScript1

def proccessfilesbasedoninput(input, pbcv):
    DoStuff()
    pbcv.gofurther(5)
    DoMoreStuff()
    pbcv.goevenfurther(10)
    a1sauce = RandomMathsStuffs()
    for all the data in every file in that one directory:
        ReadData()
        pbcv.goabitfurther(a1sauce)
        if data is what I want:
            break
     pbcv.step(-100)
     return data

I guess my question is, How would I get the GUI to update those elements instead of going unconscious?

We are talking 100 000 files and 1.5 seconds its done in.

UPDATE: This question has been marked as a duplicate of another. Is it? Yep. but that's both because I was ((and still am)) unsure of how to search for this kind of question, and that the three solutions there; multithreading, multiprocessing, and smaller tasks. Unfortunately, the program was built to run on a single thread and process, and without a complete rewrite, getting the intended GUI response would cause a massive slowdown, if it worked at all.

I do see the issue, being TKinter is a blocking module. Unfortunately, I am fresh out of ideas on how I would un-block it without causing mass errors, and or a total rewrite.

Terry Jan Reedy
  • 18,414
  • 3
  • 40
  • 52
x otikoruk x
  • 422
  • 1
  • 6
  • 16
  • 1
    Possible duplicate of [Program freezing during the execution of a function in Tkinter](http://stackoverflow.com/questions/10847626/program-freezing-during-the-execution-of-a-function-in-tkinter) – Aran-Fey Jul 24 '16 at 04:51
  • Sorry, I was unsure of how to search for such an issue. Unfortunately, the program is single thread based, can't be run on multiple processes, and can't be broken up into chunks without a complete rewrite. Is there any other possible solution, or similar threads? – x otikoruk x Jul 24 '16 at 04:58
  • There's no way it can't be made to work with multiple threads, all you need to do is run `.mainloop()` in a new thread. (Well, that and you have to rewrite those parts of the code that modify widgets, because only the mainloop thread should be interacting with Tkinter.) If you __really__ can't do that, then your only option is to call `update_idletasks` periodically while doing the other task. – Aran-Fey Jul 24 '16 at 05:06
  • I did a bit of self-research ((Surprisingly)) and found my answer. Would it be proper to share what I've done in an answer or just forget that this question arose? – x otikoruk x Jul 24 '16 at 05:14
  • It's up to you. We can just mark this question as a dupe, but if you think the other question doesn't address your problem very well or the solution you've come up with isn't covered in the other thread, then it'd be good if you posted an answer. – Aran-Fey Jul 24 '16 at 05:25
  • Your code is impossible since it calls a module as if it were a function. – Terry Jan Reedy Jul 24 '16 at 19:41
  • I am calling a function from an external module, not calling the module itself. Also, I already got it working. properly Refer to the answer below :) – x otikoruk x Jul 24 '16 at 19:50

1 Answers1

0

The linked duplicate question held an answer. A bad one - but an answer none the less.

update_idletasks.

I tried that, and, it Worked! Well. Sort of.

It worked at first, then the same result came about. The GUI temporarily froze.

Then an idea popped in my head. Why not try update instead?

I did so, and it worked as I needed it to, however, it had a massive performance hit - nearly identical to update_idletasks.

To tackle this new problem, I added a bit more math to cause updates to happen, in my case, every 300 files, instead of every single file-balancing the performance hit and users not instantly deleting my program, because yes, it takes a toll on your resources. No, I did not initially heed that advice. Shoot first, ask questions later, right?

How did I use it? Glad I asked! Here's an example;

#GUI Code
DoStuff()
SideScript1.proccessdata(arg, kwarg, debate)
DoMoreStuff()

#File Management Code
DoStuff()
filenumber = 0
maxfilenumber = 0
for every file I need to search:
    SearchFile()
    filenumber +=1
    if filenumber == maxfilenumber:
        tkinter.update() #in my case, it was tkinst, or "TkInter Instance", since it was inherited from the GUI attributes.
        filenumber = 0
    if data is what I want:
        break
return data

I'm not sure about all the backend and hard facts, but update() seemed a lot more user friendly and quicker than update_idletasks(), and a lot less prone to errors and slowdowns as well.

My shenanigans are now back in order, running in 60 ((30? 120? 250 million??)) frames a seconds, smoothly and efficiently - and Tk doesn't have a sit-down strike every time I ask it for info anymore!

Thanks @Rawing for the attempt to help!

x otikoruk x
  • 422
  • 1
  • 6
  • 16
  • 1
    There's no magic: `update` is essentially one full trip through `mainloop` (or more accurately, a new, temporary `mainloop`) whereas `update_idletasks` merely processes pending "idle" events. In other words, `update_idletasks` does a subset of the work that `update` does. – Bryan Oakley Jul 24 '16 at 07:55
  • I believe that entirely-but to my poor eyesight, it seems `.update()` is faster. I do see where that would cause the issue to remain unfixed using `.update_idletasks()`. I know I'm running a pretty powerful PC, but, is there anything else that would cause `update` to seem faster than `update_idletasks`, even though it is actually doing more work, or is that just my mind seeing things? – x otikoruk x Jul 24 '16 at 07:59
  • Pause `mainloop` with another `loop` ? How to divide an gui app without `thread` ? – dsgdfg Jul 24 '16 at 08:57
  • If you are not running mainloop, I suggest use update and forget about update_idile_tasks. The latter has some specialized uses that don't apply here. If you call update about 50 times a second, your display will look fine. If every n files works well enough, go with that. Otherwise, check the elapsed time since last update with time.monotonic every, say, 100 files. – Terry Jan Reedy Jul 24 '16 at 20:08