【问题标题】:How to stop a while loop to get input from user using Tkinter?如何使用 Tkinter 停止 while 循环以获取用户的输入?
【发布时间】:2016-12-23 07:49:10
【问题描述】:

令人难以置信的是,如此基础的知识竟能埋藏在如此难找的地方。我花了将近一个月的时间在谷歌上搜索答案——观看视频、阅读文档、书籍等——关于如何停止 while 循环以获取使用 Tkinter 的用户的输入。

我下面的代码循环了两次,在我能够提供输入之前离开循环!!!

无论如何,理论是值得赞赏的,但示例代码会更有帮助。非常非常感谢。

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

【问题讨论】:

  • 你在哪里定义root
  • 这很难做到,因为这根本不是图形用户界面的设计方式。这有点像说你已经搜索了一个月如何在搅拌机中煮牛排。如果您需要暂停并从用户那里获取信息,您应该使用模态对话框。见effbot.org/tkinterbook/tkinter-dialog-windows.htm
  • 好的,布莱恩。牛排在搅拌机里。我花了将近五个月的时间来研究那个!
  • @Hobbes,我在 'loop = 0' 正上方定义 'root',只是忘记过去了。
  • @Bryan,除非对话框窗口比我看到的更强大,否则我认为对于需要在一行中包含七个输入/输入框的表单来说,这不是一个可行的解决方案添加 n 行的选项。我是 Python 新手,但据我所知,例如 input() 会停止程序/循环,直到用户提供一些数据。 input() 的问题是我只看到它在 shell 中工作,而不是在 GUI 中。

标签: python-3.x tkinter while-loop user-input


【解决方案1】:

好吧,我想这个笑话毕竟是在我身上。 :-) 我花了三天时间告诉你这不容易做到,结果证明我错了。

我很快就会给你代码,让我解释一下我哪里出错了。在我写最后一条评论时,我想向您解释的主要内容是,当您在 GUI 控制台中运行控制台 Python 程序时,这是两个不同的进程,GUI 的事件循环与如何内部 Python 程序有效。

在 GUI 应用程序中,由于只有一个进程,它会崩溃,并且它的事件循环不能同时运行(响应正常的应用程序事件,如重新绘制窗口或处理点击/按键)并保持阻塞状态(等待供您单击按钮)。在我看来,如果 tkinter 能让用户创建额外的事件循环,事情就会变得非常简单。这并非不合理:由于 tkinter 已经为自己的目的构造了一个,它只需要将该构造函数(或其他一些构造方法)暴露给用户。并且不会有任何 GIL 问题,因为另一个事件循环只是保持阻塞状态(只有一个实际在执行)。

一旦我知道我在搜索什么,就不会那么难找到了:你构造一个 Tk 变量(任何类型,布尔值都适合这种语义),切换它的值,然后在回调中切换回它。与此同时,你等待它改变。易如反掌。 :-)

我尝试尽可能少地更改您的代码,但是当然,一旦您了解了机制,您就可以做得更好。我希望我已经恢复了你对 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()

【讨论】:

  • 只是一个评论:当然,我仍然认为这不是普通的 GUI 应用程序应该如何工作的。 @Bryan Oakley 已经解决了你的 X。我希望我已经解决了你的 Y。(有关术语,请参阅 mywiki.wooledge.org/XyProblem。)我认为这是一次有趣的教育经历。 :-)
  • @ Veky,太棒了!!!我知道 stackoverflow 要求不要在这里说谢谢,但我还能在哪里做。
  • 如果它回答了您的问题,请接受它。 :)
  • 答案一针见血。非常感谢!
  • 嗯...谢谢。 :-)
【解决方案2】:

好的,经过大量讨论,您似乎实际上不需要等待任何东西。 GUI 处于等待事件的永久状态,因此您无需添加额外的等待。您所需要的只是一个“保存”按钮,以及一种让用户输入可变数量值的方法。

要允许用户输入多个值,您可以添加一个添加更多条目小部件的按钮。唯一真正的技巧是保留条目小部件的列表,以便稍后在程序中获取值。

以下是说明该技术的工作示例。

注意:这不是我在真正的 GUI 中实际执行此操作的方式,因为它依赖于全局变量。重点是说明动态添加入口小部件的一般原理。

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

【讨论】:

  • 非常感谢!这可能是我需要的路径,我将深入研究您的示例以尝试使其适合我的需要。在某个地方,我需要检查类别的数字加起来是否等于总数。如果我可以使用“if”来做到这一点,我应该是安全的。否则,我可能会回到“while循环”的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-20
  • 1970-01-01
  • 2020-10-17
  • 2017-01-26
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
相关资源
最近更新 更多