2

Everywhere I look when researching tkinter multithreading, the things I find mention tkinter must run on the main thread (like lots of GUI frameworks), and when a separate thread needs to communicate with the GUI, one must use queues instead of accessing the widgets directly from the worker thread.

I was trying to learn to use the queue method, but of course initially I wanted to see what happens if I do it wrong, so I wrote this piece of code, which approximates Pi (π) for a few seconds:

import tkinter as tk
from tkinter import ttk
from threading import Thread
import math

window = tk.Tk()
window.geometry("300x150")

lbl = tk.Label(window, text="Press Start")
lbl.place(width=280, height=60, x=10, y=10)

pb = ttk.Progressbar(window)
pb.place(width=280, height=25, x=10, y=80)

def calculate():
    """ 1 / i^2 =  PI^2 / 6 """
    s = 0.0
    for i in range(1, 10000001):
        s += (1 / i**2)
        if i % 1000000 == 0:            
            value = math.sqrt(s * 6)
            lbl.config(text=value) #???
            pb.step(10) #???

def start():
    lbl.config(text="Press Start")
    #calculate() #irresponsive GUI this way, obviously
    t = Thread(target=calculate)
    t.start()

btn = tk.Button(window, text="Start", command=start)
btn.place(width=280, height=25, x=10, y=115)

window.mainloop()

As far as I understand, lines marked by the ??? comment are the problem. But this code runs just fine. Stays responsive, both the label and the progress bar are updated.

Is this cross-thread error undeterministic in terms of sometimes it happens / sometimes not? Would this code potentially break eventually? Or am I naive to believe the only good solution is a queue? I believe in C# this would yield a cross-thread operation not valid, but python doesn't seem to have a problem with it.

martineau
  • 119,623
  • 25
  • 170
  • 301
Innkeeper
  • 663
  • 1
  • 10
  • 22
  • In this code, it's too simple to have an issue. Issues arise when you have many threads trying to update the same widget simultaneously. – dilbert May 27 '14 at 01:43
  • Possible duplicate of [Tkinter: How to use threads to preventing main event loop from "freezing"](https://stackoverflow.com/questions/16745507/tkinter-how-to-use-threads-to-preventing-main-event-loop-from-freezing) – ivan_pozdeev Mar 01 '18 at 20:29
  • I wrote a similar code that worked fine on Mac (python 3.6) but crashed on Linux (python 3.5). The error was " out of stack space (infinite loop?)" – quickbug Mar 12 '18 at 13:25

1 Answers1

2

Here's one thing that would be unacceptable in a regular consumer application: What happens when you hit the "Start" button, then close the window before the progress bar finishes? In a large application with interdependent parts, destroying gui elements that are used in another thread is likely to have unpleasant consequences. I found that while running in a terminal, this program sometimes hangs hard enough on an unexpected exit that ctrl-c has no effect.

Incidentally, the

s += (1 / i ** 2)

line is doing integer division, so the value of s only changes once.

tasteslikelemons
  • 397
  • 1
  • 2
  • 8
  • Thank you. Yes, it really should be `//`. I see what you mean with closing the window, but that wouldn't be solved with queues either, would it? So it's a separate concern. – Innkeeper May 27 '14 at 09:45