Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tkinter GUI freezing, used Thread then encountered RuntimeError: threads can only be started once

263 views
Skip to first unread message

Abhay Singh

unread,
Jan 10, 2023, 9:58:10 AM1/10/23
to
Here is the entire code snippet of the same.

Please help

def change_flag(top_frame, bottom_frame, button1, button2, button3, button4, controller): global counter, canvas, my_image, chosen, flag, directory canvas.delete('all') button5['state'] = DISABLED counter += 1

chosen, options_text = function_options()
right_answer_flag = get_right_answer_flag(chosen, options_text)
#pdb.set_trace()

try:
location = directory + chosen + format_image
except:
controller.show_frame(PlayAgainExit)

my_image = PhotoImage(file=location)
canvas.create_image(160, 100, anchor=CENTER, image=my_image)

button1["text"] = options_text[0]
button2["text"] = options_text[1]
button3["text"] = options_text[2]
button4["text"] = options_text[3]

button1['state'] = NORMAL
button2['state'] = NORMAL
button3['state'] = NORMAL
button4['state'] = NORMAL
##############

button5 = Button(
next_frame,
width=20,
text="next",
fg="black",
#command=lambda: change_flag(top_frame,bottom_frame,button1,button2,button3,button4,controller))
command=Thread(target=change_flag, args =(top_frame,bottom_frame,button1,button2,button3,button4,controller)).start)

button5.pack(side=RIGHT, padx=5, pady=5)

Thanks,
Abhay

MRAB

unread,
Jan 10, 2023, 1:36:04 PM1/10/23
to
The formatting is messed up, which doesn't help.

Some points:

You have a 'bare' except, i.e. "except:". Don't do that. It swallows
_all_ exceptions and can hide bugs.

I don't like how you're passing Thread...start as an argument. IMHO, it
would be better/cleaner to pass a plain function, even if the only thing
that function does is to start the thread.

I can't tell what 'change_flag' is doing because of the formatting
issue. Is it doing GUI stuff? In a thread? If yes, don't do that. The
GUI doesn't like that. Only the main thread should do GUI stuff.

Cameron Simpson

unread,
Jan 10, 2023, 7:14:08 PM1/10/23
to
On 10Jan2023 18:32, MRAB <pyt...@mrabarnett.plus.com> wrote:
>I don't like how you're passing Thread...start as an argument. IMHO, it
>would be better/cleaner to pass a plain function, even if the only
>thing that function does is to start the thread.

Yes, and this is likely the thing causing the cited exception "threads
can only be started once". Your setup of the button with the action
defined as:

Thread(....).start

creates a _single_ new Thread _when you define the button_, and makes
hte button callback try to start it. On the second and following
callback, you're trying to start the _same_ single Thread again.

Do as MRAB suggests and have the callback create-and-start a Thread
instead of just starting an _existing_ Thread.

Also, for simple quick things there's no need to use a Thread at all. If
the final version of the programme is going to do something long running
at that point, then sure.

>I can't tell what 'change_flag' is doing because of the formatting
>issue. Is it doing GUI stuff? In a thread? If yes, don't do that. The
>GUI doesn't like that. Only the main thread should do GUI stuff.

Aye. This is very important in almost all GUI toolkits.

Bit me very badly with Qt once, badly in that the segfaults (yes!
segfaults! in a Python app!) were erratic and very timing dependent,
making them hard to reproduce and understand. It wasn't until I
_realised_ it was thread/concurrency related that I could fix it.

Note that in Tk you can have a callback do GUI work, just not in a
separate thread.

Cheers,
Cameron Simpson <c...@cskk.id.au>

MRAB

unread,
Jan 10, 2023, 8:21:17 PM1/10/23
to
On 2023-01-11 00:13, Cameron Simpson wrote:
> On 10Jan2023 18:32, MRAB <pyt...@mrabarnett.plus.com> wrote:
>>I don't like how you're passing Thread...start as an argument. IMHO, it
>>would be better/cleaner to pass a plain function, even if the only
>>thing that function does is to start the thread.
>
> Yes, and this is likely the thing causing the cited exception "threads
> can only be started once". Your setup of the button with the action
> defined as:
>
> Thread(....).start
>
> creates a _single_ new Thread _when you define the button_, and makes
> hte button callback try to start it. On the second and following
> callback, you're trying to start the _same_ single Thread again.
>
You're right! I missed that detail. :-(
0 new messages