1

I need to send updates to tkinter from another thread, which should be handled immediately. Sadly Tkinter freezes whenever I try multithreading.

My Attempt:

I've read through several Tkinter-threaded pages but didn't find anything that works well, since most of them try creating a new thread on button.click(), which doesn't help here.

I tried not calling .mainloop() and instead calling the update functions myself, whenever an update comes in:

#GUI    
def update(self, string):
        self._interactor.config(text=string)
        #interactor is a button
        self._tk.update_idletasks()
        self._tk.update()

This works fine until I use a loop with sleep() in the master to constantly update the text. GUI and master are frozen during sleep(). So I tried to use a threaded timer as discussed here.

#MASTER
gui=gui_remote(self)

def changetext():
    text=self.gettextsomewhere() 
    self._gui.update(text)

loop=RepeatedTimer(5, changetext)

But this just leads to the following error, thrown by Tkinter:

RuntimeError: main thread is not in main loop

Having a hard time on how to solve this. Is it possible to call a GUI class on main thread and still have proper access to its functions?

How I got to this point:

For my project, I need a button, which represents several Buttons.
Every y (eg. 1.5) seconds, the displayed text should be updated to a new one from the outside.
Also, I want to keep GUI, Controller and Data separated, (using blueprint methods) so that later adjustments on each of them will be easier.

I already got it to work, using TK's .after() function, but I had to use GUI and controlling functions closely together.

My Plan

Have a GUI class, which is updateable from another object via simple public functions. The other object (the master) should be able to create a GUI object and call the GUI's update functions with new data every y seconds. When the GUI button is clicked, simply call a certain method at master every time:

#GUI example
from tkinter import Tk, Button, Frame, Label

class gui_sample:

    def __init__(self, master):
        """This is the very simple GUI"""
        self._master=master
        self._tk=Tk()
        self._interactor= Button(self._tk, text="Apfelsaft", command=self._click)
        self._interactor.pack()
        self._tk.mainloop()

    def update(self, string):
        """Handle interactor update"""
        self._interactor.config(text=string)

    def _click(self):
        self._master.click()


#MASTER
from gui_module import *

class Controller:

    def __init__(self):
        self._gui=gui_sample(self)
        self._run()

    def _run(self):
        #call this every 5 seconds
        new_text=self.gettextfromsomewhere()
        self._gui.update(new_text)

    def click():
        #do something
        pass
#this code is just a blueprint it probably does nothing

My Problem:

I don't want the master to use TK functions since I might switch to another UI module later and keep the master's functionality. The master will constantly loop through what's being displayed next and needs to be accessible at the same time. Using loops with sleep() isn't a good idea since they will block both, the master and the GUI. Calling .mainloop() is also problematic since it will block all other programs. The gui should always respond to updates and not ask for them.

kilian579
  • 68
  • 2
  • 11
  • Since the GUI is initialized with a reference to the master (`self._master = master`), what's stopping the GUI from asking the master for the text? The GUI is already running the main loop, so could ask the master for the text every x seconds. – Will Keeling Dec 08 '18 at 16:34
  • Was my idea, but I was advised to do it the other way around. The master has to be working all the time, shooting out notifications at the GUI, regardless of whether the GUI is actively handling them or not. – kilian579 Dec 08 '18 at 16:40
  • I'll try to solve it using a Threading Queue [like this.](https://scorython.wordpress.com/2016/06/27/multithreading-with-tkinter/) – kilian579 Dec 20 '18 at 09:47

0 Answers0