2016-06-22 53 views
1

我很抱歉如果这个问题的答案很明显,但我不熟悉Python的内部工作原理。如何让脚本等待按钮按下?

基本情景:我有一个Python脚本来完成任务。定期地,当满足某些条件时(比如设置标志变量),我希望脚本暂停并等待用户按下GUI按钮。

我该如何执行此操作?为了澄清,我希望它像input()一样工作,因为它暂停了一切,并等待函数调用被解析,除了我不想将它与文本绑定,而是一个GUI按钮。我打算使用Tkinter制作按钮。

我最初的想法是做一个while循环是这样的:

x = 2 
while (x > 1): 
    #do nothing 

然后该按钮将调用设置x = 0

这是做到这一点的正确方法的功能?有没有更好的办法?我错过了明显的东西?

示例代码:

class Displayable(object): 
    max_display_level = 1 # can be overridden in subclasses 
    manual_step = False  # can be overridden in subclasses 

    def display(self,level,*args,**kwargs): 
     -Do stuff unrelated to the question- 

     if (self.manual_step): 
      if level <= self.max_display_level: 
       input("Waiting for input: ") 

背后的想法,这是对象将扩展类可显示和设定自己的max_display_level和manual_step值。更高的max_display_level意味着将显示更多的消息(即,如果我用level = 1,2,3和4调用display(),如果我的max_display_level == 2,只有前2个调用将执行所有逻辑。是一种允许用户设置执行的详细程度的方法,这是我不会涉及的原因,但它应该保持不变。)如果特定对象具有manual_step == true并且级别满足当调用display()时,它应该等待用户输入。诀窍是我希望它等待按钮按下而不是文本+输入。

+0

忙碌的等待不是一个好习惯。通常你会想用'time.sleep()'来暂停脚本(这可以让系统上的其他进程运行)。也就是说,一般来说,图形用户界面不会暂停,它们会进入一种状态,在这种状态下,大多数事件会被忽略,直到发生状态改变。最后,'tkinter'不支持多线程本身。如果你仔细的话,可以在线程中做非gui的事情。没有更多细节(或示例代码)在你的问题中,我不能更具体。 – martineau

+0

脚本是单线程的,我不是故意暗示它是多线程的。我也并不关心“按钮输入”是做什么的,只要它让所有其他事情等待它被解决/按下。如果您有任何想法,我正在倾听。 – GreySage

+1

您要求的东西的名称是_modal dialog_。一旦显示,它会暂停GUI,直到用户作出响应。网上有很多关于在tkinter中创建模态对话框的例子。 –

回答

1

如果您打算使用GUI按钮(尤其是tkinter),则不需要执行任何操作 - GUI往往会运行自己的无限循环来处理绘图和处理事件。当你所有的.mainloop()为tkinter的时候,你的代码中的那一点之后什么都不会执行,除了回调和tkinter事件;即:

root= Tk() 
root.mainloop() 
print("This won't be printed until root window is closed") 

最佳做法是将按钮被按下时应该发生的事情绑定到该按钮作为命令/回调。 (Button(master, ... command=callback OR command=lambda *e: callback())

要做到你想什么,我建议在最后把一个Button.disable()在稍长的运行代码(使按键不看点击)开始,然后Button.enable()这样你就可以点击它。或创建/显示按钮在功能结束时执行下一步骤(即时创建该按钮可以让您将此功能的结果直接通过lambdas传递给下一个功能的回调)

def cb_1(root_window, btn_to_disable): 
    btn_to_disable.disable() 
    foo = complex_algorithm() 
    # Create a button for the next part 
    Button(root_window, text='do cb_2 with foo', 
      command=lambda e, arg=foo, r=root_window: cb_2(r, arg)).pack() 

def cb_2(root, argument): 
    print("This is foo, from cb_1:",argument) # prints foo 

root = Tk() 
btn = Button(root, text="do cb_1", 
      command=lambda *e: cb_1(root, btn)) 
btn.pack() 
root.mainloop() 
print("This isn't printed until the GUI is closed!") 

编辑:根据你最新的编辑,它似乎after()将成为你的朋友 - 它可以让一个函数通过tkinter的主循环再次调度自己,而不会阻止GUI的操作。即在Displayable

def display(self, level, *args, **kwargs): 
    #unrelated# 
    self.wait_for_next(level, *args, **kwargs) 

def wait_for_next(self, level, *args, **kwargs) 
    if self.manual_step: 
     if (level <= self.max_display_level): 
      if self.button_set_value: 
       # Do something with button-set value 
       print('FOO') 
      else: 
       # set ourselves to re-check after 1000 ms 
       self.root.after(1000, 
           lambda *e:self.wait_for_next(level, *args, **kwargs)) 

据推测,无论你的按钮是,他们设置一些值(或只是设置一个标志)。当这个值是真的,这将打印"FOO"。这时您可能再次调用display()。

另一种做法是将display()作为按钮的回调,所以当按下按钮时,调用display

+0

看到您的编辑我即将添加更多相关信息 – Delioth

+0

我不得不稍微修改您的答案。我使用Jupyter笔记本电脑,因为某些原因,如果主进程被阻塞,它会阻止GUI线程,所以我只是将所有真正的计算关闭到一个线程并且它工作。 – GreySage

+1

@GreySage是一个不错的选择,许多GUI不喜欢在子线程上正常运行 - 在主线程中外包计算并离开GUI通常是一个好主意。 – Delioth