Python Forum
[Tkinter] GUI Freeze/tkinter - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: [Tkinter] GUI Freeze/tkinter (/thread-19350.html)



GUI Freeze/tkinter - SamGer - Jun-24-2019

Hello Users,

I have a python script called Master which runs continously (using while 1:). Now I want to create a simple GUI using tkinter to Start and Stop the Master script but once started, the GUI does not react and as far as I have read online, this issuse can be solved using Threading. But it seems I am not using the Threading in a correct manner as I am getting Exceptions because of this approach:

Error:
Exception in thread Thread-1: Traceback (most recent call last): File "C:\Program Files\Python27\lib\threading.py", line 801, in __bootstrap_inner self.run()
Will be greatful to your suggestions

My Code Looks as follows:

Master:

def Excecute():
 while 1:
  func1()
  func2()
  # and many other functions 
GUI:


import Tkinter
import tkSimpleDialog
import tkFileDialog
import tkMessageBox
import os
from subprocess import Popen
import sys
import Master as test
import threading


class PST(Tkinter.Tk):
    
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()
        
    
    def initialize(self):

        # Label
        
         self.grid()
         d="Sample Text"
         label1 = Tkinter.Label(self,text="Status :" + self.Status_Text(d),anchor="w").grid(column=0,row=0,columnspan=1,sticky='EW',padx=5,pady=5)        
        
        # Button
         
         button1 = Tkinter.Button(self,text=u"Start",command=self.anfang)
         button1.grid(column=0,row=11,pady=10,padx=10,columnspan=1,sticky='W')
         button2 = Tkinter.Button(self,text=u"Beenden",command=self.beenden)
         button2.grid(column=0,row=11,pady=10,padx=90,sticky='W')
   
    def anfang(self):

        t=threading.Thread(target=test.Execute)
        t.start()

    def Status_Text(self,Meldung):
        
        Text=Meldung
        return (Text)
    
    def beenden(self):
        
        print("Exiting")
        self.destroy()
        sys.exit()
        
        
    
master = CoA_PST(None)
master.title('CoA PST')
w = 300
h = 100
ws = master.winfo_screenwidth() 
hs = master.winfo_screenheight() 
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
master.geometry('%dx%d+%d+%d' % (w, h, x, y))
master.mainloop()



RE: GUI Freeze/tkinter - Yoriz - Jun-24-2019

Using the methods shown in thread https://python-forum.io/Thread-Tkinter-How-to-deal-with-code-that-blocks-the-mainloop-freezing-the-gui

import functools
import time
import tkinter as tk
from concurrent import futures

thread_pool_executor = futures.ThreadPoolExecutor(max_workers=1)


def tk_after(target):

    @functools.wraps(target)
    def wrapper(self, *args, **kwargs):
        args = (self,) + args
        self.after(0, target, *args, **kwargs)

    return wrapper


def submit_to_pool_executor(executor):
    '''Decorates a method to be sumbited to the passed in executor'''
    def decorator(target):

        @functools.wraps(target)
        def wrapper(*args, **kwargs):
            result = executor.submit(target, *args, **kwargs)
            result.add_done_callback(executor_done_call_back)
            return result

        return wrapper

    return decorator


def executor_done_call_back(future):
    exception = future.exception()
    if exception:
        raise exception


def func1():
    time.sleep(2)


def func2():
    time.sleep(3)


class MainFrame(tk.Frame):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.run_thread = False
        self.label = tk.Label(self, text='not running')
        self.label.pack()
        self.button = tk.Button(
            self, text='start task', command=self.on_button)
        self.button.pack(pady=15)
        self.pack()

    def on_button(self):
        if not self.run_thread:
            self.run_thread = True
            self.blocking_code()
            self.set_button_text('Stop task')
        else:
            self.run_thread = False
            self.set_button_state(False)
            self.set_button_text('Stopping')
            self.blocking_code_stopped()

    @tk_after
    def set_label_text(self, text=''):
        self.label['text'] = text

    @tk_after
    def set_button_text(self, text=''):
        self.button['text'] = text

    @tk_after
    def set_button_state(self, enable=True):
        state = 'normal' if enable else 'disable'
        self.button['state'] = state

    @submit_to_pool_executor(thread_pool_executor)
    def blocking_code(self):
        self.set_label_text('running')
        while self.run_thread:
            func1()
            self.set_label_text('func1 complete')
            func2()
            self.set_label_text('func2 complete')

    @submit_to_pool_executor(thread_pool_executor)
    def blocking_code_stopped(self):
        self.set_button_state(True)
        self.set_label_text('not running')
        self.set_button_text('Start task')


if __name__ == '__main__':
    app = tk.Tk()
    main_frame = MainFrame()
    app.mainloop()



RE: GUI Freeze/tkinter - noisefloor - Jun-24-2019

Hi,

a more simple approach would be start the thread outside Tkinter and share an event between the GUI class and the thread. The functions are run by setting the event, like while event.is_set():.

Except this:
while 1 is not pythonic, use while True instead. Function names are written in lower case letter, so the function should be names execute. You are using Python 2, which is EOL at the end of this year. Better switch to Python 3 NOW.

And finally @SamGer: you are aware that there is a German-language based forum for Python at python-forum.de? Nothing against posting here, but probably it's a bit more convenient to get high quality answers in your mother language.

Regards, noisefloor