【问题标题】:Passing values between classes for Tkinter在 Tkinter 的类之间传递值
【发布时间】:2018-11-05 17:30:19
【问题描述】:

我正在开发一个小型 GUI,以从我们的 CMDB 查询信息以显示给用户。我遇到的麻烦是在一个类中的事件发生(按钮)之后,我想更新另一个类中的组合框。我想我应该使用 tk.StringVar() 来传递列表,但是组合框只显示一个 'PC_VAR#' 值并且不会更新。请问有人可以帮忙吗?

#!/usr/bin/python

import Tkinter as tk
import ttk
import signal


class LoginUI:
    def __init__(self, frame):
        self.frame = frame

        # Set default list entry
        self.dc_list = tk.StringVar()
        self.dc_list.set(['Login first'])

        # Add a button to log in
        self.button = tk.Button(self.frame, text='Login', command=self.change_combobox)
        self.button.grid(column=0, row=0, pady=5)

    def change_combobox(self):
        # Change combobox values
        dc_list = ['Site_1', 'Site_2', 'Site_3']
        self.dc_list.set(dc_list)


class QueryUI:
    def __init__(self, frame, dc_list):
        self.frame = frame
        self.dc = tk.StringVar()
        self.dc_list = tk.StringVar()
        self.dc_list.set(dc_list)

        # Create site combobox
        tk.Label(self.frame, text='Site:').grid(column=0, row=0, sticky="w")
        self.dc_combobox = ttk.Combobox(
            self.frame,
            textvariable=self.dc,
            width=20,
            state='readonly'
        )
        self.dc_combobox['values'] = self.dc_list.get()
        self.dc_combobox.grid(column=1, row=0, sticky="w")


class App:
    def __init__(self, root):
        self.root = root
        self.root.title('Logging Handler')
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)

        # Create the left frame panels
        left_frame = tk.Frame(self.root, padx=5, pady=5)
        login_frame = tk.LabelFrame(left_frame, text="Login", borderwidth=2, relief="groove", padx=5, pady=5)
        query_frame = tk.LabelFrame(left_frame, text="Query", borderwidth=2, relief="groove", padx=5, pady=5)

        # Align frames
        left_frame.grid(row=0, column=0, sticky="nw")
        login_frame.grid(row=0, column=0, pady=5, sticky="nw")
        query_frame.grid(row=1, column=0, pady=5, sticky="nw")

        # Initialize all frames
        self.login = LoginUI(login_frame)
        self.query = QueryUI(query_frame, self.login.dc_list)
        self.root.protocol('WM_DELETE_WINDOW', self.quit)
        self.root.bind('<Control-q>', self.quit)
        signal.signal(signal.SIGINT, self.quit)

    def quit(self, *args):
        self.root.destroy()


def main():
    root = tk.Tk()
    app = App(root)
    app.root.mainloop()


if __name__ == '__main__':
    main()

【问题讨论】:

  • 欢迎来到 Stack Overflow。请提供Minimal, Complete, and Verifiable example。我们不需要查看您的所有代码。请创建一个简单的示例。
  • 你真的不想在这里使用 StringVar。不需要。
  • 只是一个评论:您也可以考虑在 Flask 中创建您的应用程序,并使用 HTML、CSS 和 Javascript 作为前端。
  • @Anton 我也想过这个,等我完成后可能会这样做。

标签: python python-2.7 tkinter


【解决方案1】:

我在这里要做的是将控制类(应用程序)传递给更新组合框所需的类。这样我们可以在以后需要时与之交互。通过将Appself 传递给LoginUI,我们可以在LoginUI 中与App 的类属性和方法进行交互。这使得更新组合框变得很简单。

也就是说你真的不需要所有的 StringVars。只需将列表作为列表过去,您就可以开始了。

import Tkinter as tk
import ttk
import signal


class LoginUI:
    def __init__(self, controller, frame):
        self.controller = controller
        self.frame = frame
        self.dc_list = ['Login first']
        self.button = tk.Button(self.frame, text='Login', command=self.change_combobox)
        self.button.grid(column=0, row=0, pady=5)

    def change_combobox(self):
        self.controller.query.dc_combobox['values'] = ['Site_1', 'Site_2', 'Site_3']
        self.controller.query.dc.set('Site_1')


class QueryUI:
    def __init__(self, frame, dc_list):
        self.frame = frame
        self.dc = tk.StringVar()
        tk.Label(self.frame, text='Site:').grid(column=0, row=0, sticky="w")
        self.dc_combobox = ttk.Combobox(self.frame, textvariable=self.dc, width=20, state='readonly')
        self.dc_combobox['values'] = dc_list
        self.dc_combobox.grid(column=1, row=0, sticky="w")


class App:
    def __init__(self, root):
        self.root = root
        self.root.title('Logging Handler')
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)

        left_frame = tk.Frame(self.root, padx=5, pady=5)
        login_frame = tk.LabelFrame(left_frame, text="Login", borderwidth=2, relief="groove", padx=5, pady=5)
        query_frame = tk.LabelFrame(left_frame, text="Query", borderwidth=2, relief="groove", padx=5, pady=5)
        left_frame.grid(row=0, column=0, sticky="nw")
        login_frame.grid(row=0, column=0, pady=5, sticky="nw")
        query_frame.grid(row=1, column=0, pady=5, sticky="nw")
        self.login = LoginUI(self, login_frame)
        self.query = QueryUI(query_frame, self.login.dc_list)
        self.root.protocol('WM_DELETE_WINDOW', self.quit)
        self.root.bind('<Control-q>', self.quit)
        signal.signal(signal.SIGINT, self.quit)

    def quit(self, *args):
        self.root.destroy()


if __name__ == '__main__':
    root = tk.Tk()
    app = App(root)
    app.root.mainloop()

【讨论】:

  • 谢谢!我已经尝试过不使用 StringVar() 但它正在将“self”传递给似乎是关键的 LoginUI 类。谢谢!
  • 作为一个附带问题,使用 StringVar() 仅设置 str() 或 list() 会带来什么价值?
  • @Rjin StringVar 适用于需要同时更新多个字段的情况。例如,如果您有多个标签正在更新。或者说您需要一个自动更新的变量,那么 StringVar 与其他变量类型(如 IntVar 等)一起很有用。
  • 感谢您的帮助!
猜你喜欢
  • 2016-05-03
  • 1970-01-01
  • 2014-01-05
  • 1970-01-01
  • 2014-01-15
  • 1970-01-01
  • 2012-04-14
  • 2023-03-27
  • 1970-01-01
相关资源
最近更新 更多