【问题标题】:tkinter GUI design: managing variables from multiple widgets/toolbarstkinter GUI 设计:管理来自多个小部件/工具栏的变量
【发布时间】:2016-08-26 16:42:12
【问题描述】:

{编辑:Bryan Oakley 在建议的重复问题enter link description here a) 中的回答会触发对数组变量 (arrayvar.trace mode="w") 更改的响应,我需要触发它FocusOut,如我原来的问题中所述; b) 适用于 Python 2,但我无法将其转换为在 Python 3.5 中工作。我目前正在使用他和 pyfunc 的答案作为线索,并尝试使用 FocusOut 事件找出类似的解决方案。}

我正在开发一个 tkinter GUI,它允许用户使用一对单选按钮列表来选择特定类型的计算。根据选择,工具栏会填充多个模块化条目小部件,每个小部件用于计算所需的每个变量。目标是将数值输入值传递给模型,模型将返回要在画布或 matplotlib 小部件上绘制的数据。

我的问题是:使用什么典型策略来收集和不断刷新来自多个小部件的值,以便更新显示并将它们传递给模型?这里的诀窍是会有大量可能的计算类型,每个都有自己的工具栏。我希望活动工具栏能够“了解”其内容,并在对小部件条目的每次更改时对模型执行 ping 操作。

我认为小部件和工具栏必须是类,其中工具栏可以在检测到更改时查询每个小部件以获取其条目值的新副本,并将它们存储为传递给模型的某个集合。我不完全确定它如何跟踪小部件的更改。在入口小部件上使用“validate='focusout'”验证(例如,如 this validation reference) 建议自己,但我已经使用“validate='key'”将所有条目限制为数字。我不想使用“validate=all”并捎带它,因为我不想不断要求模型对每次按键都进行冗长的计算。

不过,我是 GUI 编程的新手,所以我可能会找错树。我确信必须有一个标准的设计模式来解决这个问题,但我还没有找到。

下面是一个样机的屏幕截图,用于说明我希望 GUI 做什么。任务单选按钮控制下面出现的二级按钮菜单。第二个菜单中的选择会使用必要的条目小部件填充顶部工具栏。

【问题讨论】:

  • 您是否尝试过创建与<FocusOut> 的绑定?听起来这就是你所需要的。
  • @BryanOakley 这就是我今晚要尝试的。我想我不需要 ArrayVar,但可以在父工具栏框架中制作一个普通的字典。然后,让小部件“在 FocusOut 上,将内容存储在字典中并调用‘ping 模型’例程”。

标签: python-3.x model-view-controller tkinter


【解决方案1】:

下面的代码(大部分)做了我想要的。 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()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-13
    • 1970-01-01
    • 2019-06-17
    • 2021-12-29
    • 2012-05-01
    • 2010-10-14
    • 1970-01-01
    • 2021-04-12
    相关资源
    最近更新 更多