gpt4 book ai didi

Python 2.7 - 如何在 Tkinter GUI 中使用 Observer,在框架之间切换?

转载 作者:太空宇宙 更新时间:2023-11-03 11:23:03 25 4
gpt4 key购买 nike

我目前正在开发一个基于线程 How to get variable data from a class 的 GUI .由于要处理大量数据,我想使用模型类,它通过观察者获取更新。

现在,ttk.Combobox 中的变化在第一页上通过 <<ComboboxSelect>> 注册, 拉入变量 self.shared_dataController并传递给 Model .这样,就不会使用 Oberserver/Observable 逻辑。相反,Model 中的数据每当用户在 GUI 中采取相应的操作时,就会发生变化。

但是,我希望不必使用像 <<ComboboxSelect>> 这样的绑定(bind)更改Model中的相应数据,而是一个 Observer/Observable 逻辑,它检测到条目 "Inputformat"在字典里self.shared_dataController 内已更改,这又会刷新 Model 中的数据,即 self.model_data ,其中 ttk.Combobox 的实际状态已保存。

简而言之,我想通过使用观察者实现以下目标:

用户在 ttk.Combobox 中选择即“条目 01” --> Controller 中的 self.shared_data["Inputformat"]现在填充了“Entry 01” --> Observer/Observable 逻辑检测到这个 --> Model 中的相应变量正在改变。

这里是代码。

# -*- coding: utf-8 -*-

import csv
import Tkinter as tk # python2
import ttk
import tkFileDialog


# Register a new csv dialect for global use.
# Its delimiter shall be the semicolon:
csv.register_dialect('excel-semicolon', delimiter = ';')

font = ('Calibri', 12)

'''
###############################################################################
# Model #
###############################################################################
'''

class Model:
def __init__(self, *args, **kwargs):
# There shall be a variable, which is updated every time the entry
# of the combobox is changed
self.model_keys = {}
self.model_directories = {}

def set_keys(self, keys_model):
self.model_keys = keys_model
keys = []
keyentries = []
for key in self.model_keys:
keys.append(key)
for entry in self.model_keys:
keyentries.append(self.model_keys[entry].get())

print "model_keys: {0}".format(keys)
print "model_keyentries: {0}".format(keyentries)

def get_keys(self):
keys_model = self.model_keys
return(keys_model)

def set_directories(self, model_directories):
self.model_directories = model_directories
print "Directories: {0}".format(self.model_directories)

def get_directories(self):
model_directories = self.model_directories
return(model_directories)


'''
###############################################################################
# Controller #
###############################################################################
'''

# controller handles the following: shown pages (View), calculations
# (to be implemented), datasets (Model), communication
class PageControl(tk.Tk):

''' Initialisations '''
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs) # init
tk.Tk.wm_title(self, "MCR-ALS-Converter") # title

# initiate Model
self.model = Model()

# file dialog options
self.file_opt = self.file_dialog_options()

# stores checkboxstatus, comboboxselections etc.
self.shared_keys = self.keys()

# creates the frames, which are stacked all over each other
container = self.create_frame()
self.stack_frames(container)

#creates the menubar for all frames
self.create_menubar(container)

# raises the chosen frame over the others
self.frame = self.show_frame("StartPage")


''' Methods to show View'''
# frame, which is the container for all pages
def create_frame(self):
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
return(container)

def stack_frames(self, container):
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent = container, controller = self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")

# overarching menubar, seen by all pages
def create_menubar(self, container):
# the menubar is going to be seen by all pages
menubar = tk.Menu(container)
menubar.add_command(label = "Quit", command = lambda: app.destroy())
tk.Tk.config(self, menu = menubar)

# function of the controller, to show the desired frame
def show_frame(self, page_name):
#Show the frame for the given page name
frame = self.frames[page_name]
frame.tkraise()
return(frame)


''' Push and Pull of Data from and to Model '''
# calls the method, which pushes the keys in Model (setter)
def push_keys(self):
self.model.set_keys(self.shared_keys)

# calls the method, which pulls the key data from Model (getter)
def pull_keys(self):
pulled_keys = self.model.get_keys()
return(pulled_keys)

# calls the method, which pushes the directory data in Model (setter)
def push_directories(self, directories):
self.model.set_directories(directories)

# calls the method, which pulls the directory data from Model (getter)
def pull_directories(self):
directories = self.model.get_directories()
return(directories)


''' Keys '''
# dictionary with all the variables regarding widgetstatus like checkbox checked
def keys(self):
keys = {}
keys["Inputformat"] = tk.StringVar()
keys["Outputformat"] = tk.StringVar()
return(keys)


''' Options '''
# function, which defines the options for file input and output
def file_dialog_options(self):
#Options for saving and loading of files:
options = {}
options['defaultextension'] = '.csv'
options['filetypes'] = [('Comma-Seperated Values', '.csv'),
('ASCII-File','.asc'),
('Normal Text File','.txt')]
options['initialdir'] = 'C//'
options['initialfile'] = ''
options['parent'] = self
options['title'] = 'MCR-ALS Data Preprocessing'
return(options)


''' Methods (bindings) for PageOne '''
def open_button(self):
self.get_directories()


''' Methods (functions) for PageOne '''
# UI, where the user can selected data, that shall be opened
def get_directories(self):
# open files
file_input = tkFileDialog.askopenfilenames(** self.file_opt)
file_input = sorted(list(file_input))
# create dictionary
file_input_dict = {}
file_input_dict["Input_Directories"] = file_input
self.push_directories(file_input_dict)


'''
###############################################################################
# View #
###############################################################################
'''


class StartPage(ttk.Frame):

''' Initialisations '''
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
self.controller = controller

self.labels()
self.buttons()


''' Widgets '''
def labels(self):
label = tk.Label(self, text = "This is the start page", font = font)
label.pack(side = "top", fill = "x", pady = 10)

def buttons(self):
button1 = ttk.Button(self, text = "Go to Page One",
command = lambda: self.controller.show_frame("PageOne"))
button2 = ttk.Button(self, text = "Go to Page Two",
command = lambda: self.controller.show_frame("PageTwo"))
button_close = ttk.Button(self, text = "Close",
command = lambda: app.destroy())
button1.pack(side = "top", fill = "x", pady = 10)
button2.pack(side = "top", fill = "x", pady = 10)
button_close.pack(side = "top", fill = "x", pady = 10)


class PageOne(ttk.Frame):

''' Initialisations '''
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
self.controller = controller

self.labels()
self.buttons()
self.combobox()

''' Widgets '''
def labels(self):
label = tk.Label(self, text = "On this page, you can read data", font = font)
label.pack(side = "top", fill = "x", pady = 10)

def buttons(self):
button_open = ttk.Button(self, text = "Open",
command = lambda: self.controller.open_button())
button_forward = ttk.Button(self, text = "Next Page >>",
command = lambda: self.controller.show_frame("PageTwo"))
button_back = ttk.Button(self, text = "<< Go back",
command = lambda: self.controller.show_frame("StartPage"))
button_home = ttk.Button(self, text = "Home",
command = lambda: self.controller.show_frame("StartPage"))
button_close = ttk.Button(self, text = "Close",
command = lambda: app.destroy())
button_open.pack(side = "top", fill = "x", pady = 10)
button_forward.pack(side = "top", fill = "x", pady = 10)
button_back.pack(side = "top", fill = "x", pady = 10)
button_home.pack(side = "top", fill = "x", pady = 10)
button_close.pack(side = "top", fill = "x", pady = 10)

def combobox(self):
entries = ("", "Inputformat_01", "Inputformat_02", "Inputformat_03")
combobox = ttk.Combobox(self, state = 'readonly', values = entries,
textvariable = self.controller.shared_keys["Inputformat"])
combobox.current(0)
combobox.bind('<<ComboboxSelected>>', self.updater)
combobox.pack(side = "top", fill = "x", pady = 10)


''' Bindings '''
# wrapper, which notifies the controller, that it can update keys in Model
def updater(self, event):
self.controller.push_keys()



class PageTwo(ttk.Frame):

''' Initialisations '''
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
self.controller = controller

self.labels()
self.buttons()
self.combobox()


''' Widgets '''
def labels(self):
label = tk.Label(self, text = "This is page 2", font = font)
label.pack(side = "top", fill = "x", pady = 10)

def buttons(self):
button_back = ttk.Button(self, text = "<< Go back",
command = lambda: self.controller.show_frame("PageOne"))
button_home = ttk.Button(self, text = "Home",
command = lambda: self.controller.show_frame("StartPage"))
button_close = ttk.Button(self, text = "Close",
command = lambda: app.destroy())
button_back.pack(side = "top", fill = "x", pady = 10)
button_home.pack(side = "top", fill = "x", pady = 10)
button_close.pack(side = "top", fill = "x", pady = 10)

def combobox(self):
entries = ("Outputformat_01", "Outputformat_02")
combobox = ttk.Combobox(self, state = 'readonly', values = entries,
textvariable = self.controller.shared_keys["Outputformat"])
combobox.bind('<<ComboboxSelected>>', self.updater)
combobox.pack(side = "top", fill = "x", pady = 10)


''' Bindings '''
# wrapper, which notifies the controller, that it can update keys in Model
def updater(self, event):
self.controller.push_keys()



if __name__ == "__main__":
app = PageControl()
app.mainloop()

最佳答案

由于我无法实现一个 Observer 来监视像 ttk.Combobox 这样的小部件,我决定创建一个解决方法。以下是我为从 Bryan Oakleys 示例(链接在问题中)实现 MVC 架构而采取的步骤,只要用户在 View (GUI) 中执行操作,它就会通过 Controller 类刷新其模型类。

第 1 步:添加模型类

首先,为了使用 MVC 架构,我们必须将代码分离为模型、 View 和控件。在此示例中,型号为 class Model: , 控件是 class PageControl(tk.Tk):和 View 是页面 class StartPage(tk.Frame) , PageOne(tk.Frame)PageTwo(tk.Frame) .

第 2 步:设置模型类

现在我们必须决定要在模型类中包含哪些变量。在这个例子中,我们有目录和键(组合框的状态),我们想将它们保存在字典中。将它们设置为空后,我们所要做的就是为每个变量添加 setter 和 getter,这样我们就可以刷新模型中的数据,并在需要时检索一些数据。此外,如果需要,我们可以为每个变量实现删除方法。

第三步:在控件类中添加push和pull方法

现在有了一个模型类,我们可以通过 e 引用它。 G。 self.model = Model()PageControl(tk.Tk) (控制)。现在我们有了在 Model 中设置数据的基本工具通过 e. G。 self.model.set_keys(self.shared_keys)并从 Model 获取数据.因为我们希望我们的控制类做到这一点,所以我们需要一些方法来实现这一点。所以我们将 push 和 pull 方法添加到 PageControl (例如 def push_key(self) ),它又可以通过 Controller 从 View (StartPage、PageOne、PageTwo)中引用。

第 4 步:将小部件添加到 View 类

现在我们必须决定哪些小部件应该在哪个页面上以及您希望它们做什么。在这个例子中,有导航按钮,为了任务的缘故可以忽略,两个组合框和一个按钮,它打开一个文件对话框。

在这里,我们希望组合框在状态发生变化时刷新它们的状态,并通过 Controller 将新状态发送到模型。而 Open PageOne 的按钮应打开一个文件对话框,用户然后在其中选择他/她想要打开的文件。我们从此交互中获得的目录将通过 Controller 发送到模型。

第 5 步:将所有功能放入 Controller 类

因为有一个 Controller 变量,我们可以用它来引用 Controller 类中的方法。这样,我们可以将页面中的所有方法外包到 Controller 中,并通过 self.controller.function_of_controller_class 引用它们。但是我们必须知道,这些方法通过 lambda: 绑定(bind)到命令。不能返回任何值,但它们也不会在程序启动时被调用。所以请记住这一点。

第 6 步:设置绑定(bind)和包装器

这里我们必须设置 .bind()对于我们的组合框。由于 Controller allready 已设置为存储数据并且组合框具有文本变量,因此我们可以使用它通过 combobox.bind(<<ComboboxSelect>>) 收集有关组合框状态的信息。 .我们所要做的就是设置一个包装器,每当 combobox.bind(<<ComboboxSelect>>) 时调用它。正在引发一个事件。

结束语

现在我们有了它,一个基于 Bryan Oakley 的示例“如何从类中获取变量数据”的程序,它利用了一个模型,每当用户在 View 中执行相应的操作时,该模型就会通过 Controller 更新。不幸的是,它没有像最初预期的那样使用 Observer 类,但是当我找到令人满意的解决方案时,我会继续努力并更新它。

关于Python 2.7 - 如何在 Tkinter GUI 中使用 Observer,在框架之间切换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38978182/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com