- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我目前正在开发一个基于线程 How to get variable data from a class 的 GUI .由于要处理大量数据,我想使用模型类,它通过观察者获取更新。
现在,ttk.Combobox
中的变化在第一页上通过 <<ComboboxSelect>>
注册, 拉入变量 self.shared_data
的 Controller
并传递给 Model
.这样,就不会使用 Oberserver/Observable 逻辑。相反,Model
中的数据每当用户在 GUI 中采取相应的操作时,就会发生变化。
但是,我希望不必使用像 <<ComboboxSelect>>
这样的绑定(bind)更改Model
中的相应数据,而是一个 Observer/Observable 逻辑,它检测到条目 "Inputformat"
在字典里self.shared_data
在 Controller
内已更改,这又会刷新 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/
我需要修复 getLineNumberFor 方法,以便如果 lastName 的第一个字符位于 A 和 M 之间,则返回 1;如果它位于 N 和 Z 之间,则返回 2。 在我看来听起来很简单,但我不
您好,感谢您的帮助!我有这个: 0 我必须在每次点击后增加“pinli
Javascript 中是否有一种方法可以在不使用 if 语句的情况下通过 switch case 结构将一个整数与另一个整数进行比较? 例如。 switch(integer) { case
我有一列是“日期”类型的。如何在自定义选项中使用“之间”选项? 最佳答案 请注意,您有2个盒子。 between(在SQL中)包含所有内容,因此将框1设置为:DATE >= startdate,将框2
我有一个表,其中包含年、月和一些数字列 Year Month Total 2011 10 100 2011 11 150 2011 12 100 20
这个问题已经有答案了: Extract a substring between double quotes with regular expression in Java (2 个回答) how to
我有一个带有类别的边栏。正如你在这里看到的:http://kees.een-site-bouwen.nl/ url 中类别的 ID。带有 uri 段(3)当您单击其中一个类别时,例如网页设计。显示了一
这个问题在这里已经有了答案: My regex is matching too much. How do I make it stop? [duplicate] (5 个答案) 关闭 4 年前。 我
我很不会写正则表达式。 我正在尝试获取括号“()”之间的值。像下面这样的东西...... $a = "POLYGON((1 1,2 2,3 3,1 1))"; preg_match_all("/\((
我必须添加一个叠加层 (ImageView),以便它稍微移动到包含布局的左边界的左侧。 执行此操作的最佳方法是什么? 尝试了一些简单的方法,比如将 ImageView 放在布局中并使用负边距 andr
Rx 中是否有一些扩展方法来完成下面的场景? 我有一个开始泵送的值(绿色圆圈)和其他停止泵送的值(簧片圆圈),蓝色圆圈应该是预期值,我不希望这个命令被取消并重新创建(即“TakeUntil”和“Ski
我有一个看起来像这样的数据框(Dataframe X): id number found 1 5225 NA 2 2222 NA 3 3121 NA 我有另一个看起来
所以,我正在尝试制作正则表达式,它将解析存储在对象中的所有全局函数声明,例如,像这样 const a = () => {} 我做了这样的事情: /(?:const|let|var)\s*([A-z0-
我正在尝试从 Intellivision 重新创建 Astro-Smash,我想让桶保持在两个 Angular 之间。我只是想不出在哪里以及如何让这个东西停留在两者之间。 我已经以各种方式交换了函数,
到处检查但找不到答案。 我有这个页面,我使用 INNER JOIN 将两个表连接在一起,获取它们的值并显示它们。我有这个表格,用来获取变量(例如开始日期、结束日期和卡号),这些变量将作为从表中调用值的
我陷入了两个不同的问题/错误之间,无法想出一个合适的解决方案。任何帮助将不胜感激 上下文、FFI 和调用大量 C 函数,并将 C 类型包装在 rust 结构中。 第一个问题是ICE: this pat
我在 MySQL 中有一个用户列表,在订阅时,时间戳是使用 CURRENT_TIMESTAMP 在数据库中设置的。 现在我想从此表中选择订阅日期介于第 X 天和第 Y 天之间的表我尝试了几个查询,但不
我的输入是开始日期和结束日期。我想检查它是在 12 月 1 日到 3 月 31 日之间。(年份可以更改,并且只有在此期间内或之外的日期)。 到目前为止,我还没有找到任何关于 Joda-time 的解决
我正在努力了解线程与 CPU 使用率的关系。有很多关于线程与多处理的讨论(一个很好的概述是 this answer )所以我决定通过在运行 Windows 10、Python 3.4 的 8 CPU
我正在尝试编写 PHP 代码来循环遍历数组以创建 HTML 表格。我一直在尝试做类似的事情: fetchAll(PDO::FETCH_ASSOC); ?>
我是一名优秀的程序员,十分优秀!