2016-06-22 79 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