-1
import tkinter as tk
import tkinter.font
import threading
import time
def funct1():
   i = 0
   while True:
       i+=1
       time.sleep(5)


def funct2():
   i = 0
   while True:
       i+=2
       time.sleep(5)
       label2.config(text=i)

def funct3():
   i = 0
   while True:
       time.sleep(5)
       i+=2
       label3.config(text=i)

thread1 = threading.Thread(target=funct1)
thread2 = threading.Thread(target=funct2)
thread3 = threading.Thread(target=funct3)

thread1.start()

mainwindow = tk.Tk()

HEIGHT = 700
WIDTH = 800

canvas = tk.Canvas(mainwindow, height = HEIGHT, width = WIDTH)
canvas.pack()
frame = tk.Frame(mainwindow, bg='#08030D')  #inside box
frame.place(relx=0, rely=0.1, relwidth = 0.95, relheight = 0.6)

start2=tk.Button(frame, text = "to Start 2", bg='#292230',fg='white',command = thread2.start)
start2.place(relx=0, rely=0.06, relwidth = 0.2, relheight = 0.05)
label2 = tk.Label(frame,text = "state", bg='gray')  
label2.place(relx=0.45, rely=0.12, relwidth = 0.07, relheight = 0.05)

start3=tk.Button(frame, text = "to Start 3", bg='#292230',fg='white',command = thread3.start)
start3.place(relx=0, rely=0.12, relwidth = 0.2, relheight = 0.05)
label3 = tk.Label(frame,text = "state ", bg='gray')  
label3.place(relx=0.45, rely=0.18, relwidth = 0.07, relheight = 0.05)
mainwindow.mainloop()

Currently I have 3 functions running as threads, with two of them only running if I press the buttons. Now if I scale that code up by

  1. adding more threads and functions
  2. create more labels and buttons
  3. increase sizes of functions The Tkinter window would freeze up and "lag". I have already implemented Threading as shown from online sources, but not sure if I am doing it in the most effective way regarding the GUI.
Alan Cai
  • 21
  • 6
  • Why exactly are you expecting labele or buttons to be displayed periodically? In other words, what part of your code is supposed to be making that happen? – martineau Jun 22 '20 at 17:44
  • Sorry for the confusion, I edited the title. I just meant that the Tkinter freezes when there are more threads added. There arn't any labels added in this code, but when I do in my actual, it freezes too along with the buttons. – Alan Cai Jun 22 '20 at 17:55
  • 1
    I don't see elements which could freeze up or become unresponsive. The only error I can see you getting is `RuntimeError: threads can only be started once` by clicking on the buttons more than once. Are there other components to this GUI that aren't mentioned? – Tresdon Jun 22 '20 at 17:58
  • Is your full code making any Tkinter-related calls from the threads? That's not allowed, you have to do everything from the main thread. – jasonharper Jun 22 '20 at 18:02
  • My actual code is just a scaled up version of this one, with a lot more labels and buttons that starts more threads. Also in my actual code, the threads functions are doing more tasks such as renaming the labels from within the thread functions. – Alan Cai Jun 22 '20 at 18:04
  • As @jasonharper already mentioned, Tkinter doesn't support multithreading (in the sense that only one thread can use/interact with it). A common workaround it to set up some sort of communications between the non-Gui threads and the one running the GUI using a thread-safe `Queue`. Here's an [example](https://stackoverflow.com/questions/53696888/freezing-hanging-tkinter-gui-in-waiting-for-the-thread-to-complete). – martineau Jun 22 '20 at 18:45
  • @martineau I looked at the link you provide. I am still confused how to actually implement it to my current code. Sorry I am kind of a noob to this. – Alan Cai Jun 22 '20 at 20:17
  • Alan: Sorry if the example was too advanced — but you _are_ after all asking how to do something fairly sophisticated (e.g. how to write a multi-threaded tkinter application). See the [answer](https://stackoverflow.com/a/62525469/355230) I posted below which does essentially the same thing, but in a somewhat simplified fashion (i.e. without defining classes and using global variables instead). – martineau Jun 23 '20 at 00:42

1 Answers1

0

Here's how to do something similar to what's in my answer to the question which I referred you in a comment after you asked for an example.

import queue
import sys
import tkinter as tk
import tkinter.font
import threading
import time

WIDTH, HEIGHT = 800, 700
LABEL2_MSGID, LABEL3_MSGID = 2, 3

def funct1():
   i = 0
   while True:
       i += 1
       time.sleep(5)

def funct2():
   i = 0
   while True:
       i += 2
       time.sleep(5)
       msg_queue.put((LABEL2_MSGID, i))  # Put a message in queue.

def funct3():
   i = 0
   while True:
       time.sleep(5)
       i += 2
       msg_queue.put((LABEL3_MSGID, i))  # Put a message in queue.


def process_incoming():
    """ Handle any messages currently in the queue. """
    while msg_queue.qsize():
        try:
            msg = msg_queue.get_nowait()
            # Check contents of message and do whatever is needed to the GUI.
            msg_id, text = msg
            if msg_id == LABEL2_MSGID:
               label2.config(text=text)
            elif msg_id == LABEL3_MSGID:
               label3.config(text=text)
            else:
                pass  # Unknown identifier.
        except queue.Empty:
            # Shouldn't happen, but if it does, just ignore it.
            pass

def periodic_call():
    """ Check every 200 ms if there is something new in the queue. """
    mainwindow.after(200, periodic_call)
    process_incoming()
    if not running:
        # This is the brutal stop of the system.  You may want to do some
        # cleanup before actually shutting it down.
        sys.exit(1)

thread1 = threading.Thread(target=funct1, daemon=True)
thread2 = threading.Thread(target=funct2, daemon=True)
thread3 = threading.Thread(target=funct3, daemon=True)

mainwindow = tk.Tk()
msg_queue = queue.Queue()  # For inter-thread communications.

canvas = tk.Canvas(mainwindow, height=HEIGHT, width=WIDTH)
canvas.pack()

frame = tk.Frame(mainwindow, bg='#08030D')  # Inside box.
frame.place(relx=0, rely=0.1, relwidth=0.95, relheight=0.6)

start2 = tk.Button(frame, text="to Start 2", bg='#292230', fg='white', command=thread2.start)
start2.place(relx=0, rely=0.06, relwidth=0.2, relheight=0.05)
label2 = tk.Label(frame, text="state", bg='gray')
label2.place(relx=0.45, rely=0.12, relwidth=0.07, relheight=0.05)

start3 = tk.Button(frame, text="to Start 3", bg='#292230', fg='white', command=thread3.start)
start3.place(relx=0, rely=0.12, relwidth=0.2, relheight=0.05)
label3 = tk.Label(frame, text="state ", bg='gray')
label3.place(relx=0.45, rely=0.18, relwidth=0.07, relheight=0.05)

running = True
thread1.start()  # Start background thread that doesn't affect the GUI.
periodic_call()  # Start checking and processing messages from other threads.
mainwindow.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301