Search code examples
python-3.xtkinterwhile-loopuser-input

How to stop a while loop to get input from user using Tkinter?


it is incredible how the knowledge of something so basic can be buried in a so hard to find place. I have spent close to a month googling for an answer--watching videos, reading documents, books, etc--of how to stop a while loop to get input from an user using Tkinter.

My code below is looping twice and leaving the loop before I am able to provide an input!!!

Anyway, theory is appreciated, but a sample code will be of greater help. Thank you very, very much indeed.

# Python 3.5.1
from tkinter import *
from tkinter import ttk

loop = 0

while loop < 2:

    print ('loop')

    def user_data():
        user_input = data.get()
        print (user_input)

    lb=ttk.Label(root, text="Enter data")
    data=ttk.Entry(root)
    bt=ttk.Button(root, text='Ok', command=user_data)

    lb.grid(row=0, column=1)
    data.grid(row=0, column=2)
    bt.grid(row=0, column=3)

    loop += 1

print ('left loop')

root.mainloop()

Solution

  • Ok, I think the joke is on me after all. :-) I spent last three days telling you this can't be done easily, and it turns out I was wrong.

    I'll give you the code soon, just let me explain where I went wrong. While I was writing my last comment, the main thing I wanted to explain to you is that when you run a console Python program inside a GUI console, those are two different processes, and the event loop of a GUI has nothing to do with how the inner Python program works.

    In a GUI application, it breaks down since there is just one process, and its event loop cannot at the same time run (being responsive to normal app events like repainting a window or handling clicks/keypresses) and stay blocked (wait for you to click a button). It occured to me the things would be very simple if tkinter would enable the user to make additional event loops. It's not unreasonable: since tkinter already constructs one for its own purposes, it just needs to expose that constructor (or some other means of construction) to the user. And there won't be any GIL problems, since the other event loop just stays blocked (only one is actually executing).

    Once I knew what I was searching, it wasn't so hard to find: you construct a Tk variable (of any type, Boolean works nice for this semantics), toggle its value, and toggle it back in the callback. In the meantime you wait for it to change. Easy as pie. :-)

    I tried to change your code as little as possible, but of course, once you know the mechanism, you can do it much more nicely. I hope I've returned your faith in tkinter. :-)

    from tkinter import *
    from tkinter import ttk
    
    root = Tk()
    loop = 0
    block = BooleanVar(root, False)
    
    while loop < 2:
    
        print ('loop')
    
        def user_data():
            user_input = data.get()
            print (user_input)
            block.set(False)
    
        lb=ttk.Label(root, text="Enter data")
        data=ttk.Entry(root)
        bt=ttk.Button(root, text='Ok', command=user_data)
    
        lb.grid(row=0, column=1)
        data.grid(row=0, column=2)
        bt.grid(row=0, column=3)
    
        block.set(True)
        root.wait_variable(block)
        loop += 1
    
    print ('left loop')
    root.mainloop()