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

a 夏天 提交于 2019-12-12 03:46:15

问题


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()

回答1:


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()



回答2:


Ok, after a lot of discussion, it appears that you don't actually need to wait for anything. A GUI is in a perpetual state of waiting for events, so you don't need to add additional waiting. All you need is a "save" button, and a way for the user to input a variable number of values.

To allow the user to enter multiple values, you can add a button that adds more entry widgets. The only real trick is to keep a list of the entry widgets so that you can get the values out later in your program.

The following is a working example that illustrates the technique.

Note: This is not how I would actually do this in a real GUI because it relies on global variables. The point is to illustrate the general principle of dynamically adding entry widgets.

import tkinter as tk

def add_entry():
    """
    Add two entries, one for a description, one for an amount,
    along with some labels
    """
    # create the label and entry widgets
    label1 = tk.Label(entry_frame, text="Description:")
    label2 = tk.Label(entry_frame, text="Amount:")
    entry1 = tk.Entry(entry_frame)
    entry2 = tk.Entry(entry_frame)

    # lay them out on the screen
    column, row = entry_frame.grid_size()
    label1.grid(row=row, column=0, sticky="e", pady=2)
    entry1.grid(row=row, column=1, sticky="ew", pady=2, padx=4)
    label2.grid(row=row, column=2, sticky="e", pady=2)
    entry2.grid(row=row, column=3, sticky="ew", pady=2, padx=4)
    entry_frame.grid_rowconfigure(row, weight=0)
    entry_frame.grid_rowconfigure(row+1, weight=1)

    # save the entries, so we can retrieve the values
    entries.append((entry1, entry2))

    # give focus to the new entry widget
    entry1.focus_set()

def save():
    # iterate over the entries, printing the values
    for description_entry, value_entry in entries:
        print("description: %s value: %s" % 
              (description_entry.get(), value_entry.get()))

# this is our global list of entries
entries = []

# create the main window and buttons
root = tk.Tk()

entry_frame = tk.Frame(root)
button_frame = tk.Frame(root)

entry_frame.pack(side="top", fill="both", expand=True)
button_frame.pack(side="bottom", fill="x")

add_button = tk.Button(button_frame, text="add another entry", command=add_entry)
save_button = tk.Button(button_frame, text="Save", command=save)

add_button.pack(side="left")
save_button.pack(side="right")

# create the first entry
add_entry()

# start the main loop -- this is where the GUI starts waiting,
# and why you don't need to add your own loop.
root.mainloop()


来源:https://stackoverflow.com/questions/38983322/how-to-stop-a-while-loop-to-get-input-from-user-using-tkinter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!