• Tkinter GUI freezing, used Thread then encountered RuntimeError: thread

    From Abhay Singh@21:1/5 to All on Tue Jan 10 06:57:57 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From MRAB@21:1/5 to Abhay Singh on Tue Jan 10 18:32:32 2023
    On 2023-01-10 14:57, Abhay Singh wrote:
    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)

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cameron Simpson@21:1/5 to MRAB on Wed Jan 11 11:13:42 2023
    On 10Jan2023 18:32, MRAB <python@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 <cs@cskk.id.au>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From MRAB@21:1/5 to Cameron Simpson on Wed Jan 11 01:20:53 2023
    On 2023-01-11 00:13, Cameron Simpson wrote:
    On 10Jan2023 18:32, MRAB <python@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. :-(

    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.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)