2016-05-02 8 views
0

{編輯:由Bryan Oakley在建議的重複問題enter link description here a中的答案在數組變量(arrayvar.trace mode =「w」)上觸發響應,我需要它在FocusOut上觸發,如我原來的問題所述; b)適用於Python 2,但我很難將其轉換爲Python 3.5中的工作。我目前正在使用他和pyfunc的答案作爲線索,並試圖找出使用FocusOut事件的類似解決方案。}tkinter GUI設計:管理來自多個小部件/工具欄的變量

我正在開發一個tkinter GUI,讓用戶選擇特定類型的計算,使用一對單選按鈕列表。根據選擇,一個工具欄被填充多個模塊化輸入小部件,每個變量需要一個。目標是將數值輸入值傳遞給模型,該模型將返回要繪製在畫布或matplotlib小部件上的數據。

我的問題是:什麼典型的策略用於收集和持續刷新多個小部件的值,以便更新顯示並將它們傳遞給模型?這裏的訣竅是會有大量可能的計算類型,每個計算類型都有自己的工具欄。我希望活動工具欄能夠「意識到」其內容,並在每次更改窗口小部件條目時ping模型。

我認爲窗口小部件和工具欄必須是類,在檢測到更改時,工具欄可以查詢每個窗口小部件的入口值的新副本,並將它們存儲爲傳遞給模型的某個集合。我不完全確定它如何跟蹤對小部件的更改。對入口小部件使用「validate ='focusout'」驗證(例如 this validation reference) 暗示自己,但我已經使用「validate ='key'」將所有條目限制爲數字。我不想使用「validate = all」並搭載到它上面,因爲我不想讓模型持續地對每個按鍵進行冗長的計算。

我是GUI編程的新手,但是,所以我可能會吠叫錯誤的樹。我確定必須有一個標準的設計模式來解決這個問題,但我還沒有找到它。

下面是一個模型的截圖,用於說明我想要的GUI。任務單選按鈕控制下面顯示哪個輔助按鈕菜單。第二個菜單中的選擇使用必要的輸入小部件填充頂部工具欄。

enter image description here

+1

可能重複[如何在Tkinter小部件值發生變化時運行代碼?](http://stackoverflow.com/questions/3876229/how-to-run-a-code-whenever-a- tkinter-widget-value-changes) –

+1

你試過創建一個綁定到''嗎?這聽起來就是你所需要的。 –

+0

@BryanOakley這就是我今晚要嘗試的。我想我不需要ArrayVar,但可以在父工具欄框架中創建一個簡單的字典。然後,在FocusOut上擁有小部件,將內容存儲在字典中並調用'ping模型'例程「。 –

回答

0

下面的代碼做(主要是)我想要的東西。 ToolBar框架對象將存儲來自其包含的小部件的值,並根據需要調用適當的模型。 VarBox對象是具有額外功能的Entry小部件。按Tab或Return鍵刷新存儲在ToolBar字典中的數據,通知ToolBar將數據發送到模型,並將焦點移至下一個VarBox小部件。

from tkinter import * 


# Actual model would be imported. "Dummy" model for testing below. 
def dummy_model(dic): 
    """ 
    A "dummy" model for testing the ability for a toolbar to ping the model. 
    Argument: 
    -dic: a dictionary whose values are numbers. 
    Result: 
    -prints the sum of dic's values. 
    """ 
    total = 0 
    for value in dic.values(): 
     total += value 
    print('The total of the entries is: ', total) 


class ToolBar(Frame): 
    """ 
    A frame object that contains entry widgets, a dictionary of 
    their current contents, and a function to call the appropriate model. 
    """ 
    def __init__(self, parent=None, **options): 
     Frame.__init__(self, parent, **options) 
     self.vars = {} 

    def call_model(self): 
     print('Sending to dummy_model: ', self.vars) 
     dummy_model(self.vars) 


class VarBox(Frame): 
    """ 
    A customized Frame containing a numerical entry box 
    Arguments: 
    -name: Name of the variable; appears above the entry box 
    -default: default value in entry 
    """ 
    def __init__(self, parent=None, name='', default=0.00, **options): 
     Frame.__init__(self, parent, relief=RIDGE, borderwidth=1, **options) 
     Label(self, text=name).pack(side=TOP) 
     self.widgetName = name # will be key in dictionary 

     # Entries will be limited to numerical 
     ent = Entry(self, validate='key') # check for number on keypress 
     ent.pack(side=TOP, fill=X) 
     self.value = StringVar() 
     ent.config(textvariable=self.value) 
     self.value.set(str(default)) 
     ent.bind('<Return>', lambda event: self.to_dict(event)) 
     ent.bind('<FocusOut>', lambda event: self.to_dict(event)) 

     # check on each keypress if new result will be a number 
     ent['validatecommand'] = (self.register(self.is_number), '%P') 
     # sound 'bell' if bad keypress 
     ent['invalidcommand'] = 'bell' 

    @staticmethod 
    def is_number(entry): 
     """ 
     tests to see if entry is acceptable (either empty, or able to be 
     converted to a float.) 
     """ 
     if not entry: 
      return True # Empty string: OK if entire entry deleted 
     try: 
      float(entry) 
      return True 
     except ValueError: 
      return False 

    def to_dict(self, event): 
     """ 
     On event: Records widget's status to the container's dictionary of 
     values, fills the entry with 0.00 if it was empty, tells the container 
     to send data to the model, and shifts focus to the next entry box (after 
     Return or Tab). 
     """ 
     if not self.value.get(): # if entry left blank, 
      self.value.set(0.00) # fill it with zero 
     # Add the widget's status to the container's dictionary 
     self.master.vars[self.widgetName] = float(self.value.get()) 
     self.master.call_model() 
     event.widget.tk_focusNext().focus() 


root = Tk() # create app window 
BarParentFrame = ToolBar(root) # holds individual toolbar frames 
BarParentFrame.pack(side=TOP) 
BarParentFrame.widgetName = 'BarParentFrame' 

# Pad out rest of window for visual effect 
SpaceFiller = Canvas(root, width=800, height=600, bg='beige') 
SpaceFiller.pack(expand=YES, fill=BOTH) 

Label(BarParentFrame, text='placeholder').pack(expand=NO, fill=X) 
A = VarBox(BarParentFrame, name='A', default=5.00) 
A.pack(side=LEFT) 
B = VarBox(BarParentFrame, name='B', default=3.00) 
B.pack(side=LEFT) 

root.mainloop()