wxPython:我应该如何组织控制器中的每个小部件数据?
我有一个小部件,它显示文件系统层次结构以方便浏览(基本上是一个树控件和一些相关的工具栏按钮,如“刷新”)。每个小部件都有一组基本目录供其显示(递归)。假设用户可以在他们觉得方便的时候实例化尽可能多的这些小部件。请注意,这些小部件不对应于任何业务数据——它们独立于模型 在好的MVC设计中,(每个小部件)基本目录集应该位于哪里? 当按下刷新按钮时,控制器捕获一个事件,该事件包含相应的文件系统浏览器小部件。控制器(以某种方式)确定该特定小部件的基本目录,遍历该目录路径,并向小部件传递一些数据以进行渲染 我认为有两个地方可以存储基本目录:wxPython:我应该如何组织控制器中的每个小部件数据?,python,model-view-controller,user-interface,Python,Model View Controller,User Interface,我有一个小部件,它显示文件系统层次结构以方便浏览(基本上是一个树控件和一些相关的工具栏按钮,如“刷新”)。每个小部件都有一组基本目录供其显示(递归)。假设用户可以在他们觉得方便的时候实例化尽可能多的这些小部件。请注意,这些小部件不对应于任何业务数据——它们独立于模型 在好的MVC设计中,(每个小部件)基本目录集应该位于哪里? 当按下刷新按钮时,控制器捕获一个事件,该事件包含相应的文件系统浏览器小部件。控制器(以某种方式)确定该特定小部件的基本目录,遍历该目录路径,并向小部件传递一些数据以进行渲染
{widget:base\u directory\u set}
映射我可能缺少一些MVC知识来很好地解决这类问题。基于MVC方法的运作方式,我建议您对列出的第一个解决方案进行修改: 使基本目录成为小部件上的实例变量,并让控制器对其进行操作以保留该小部件的状态 为什么??您说过小部件独立于模型,但是它们实际上是从模型中引用的吗?如果您没有将小部件绑定到模型,那么您就偏离了MVC的基本概念 我对wxPython一无所知,所以我不能谈论它是如何符合MVC的,如果有的话。即使在我看来,你应该考虑将小部件集成到模型中,或者把它们当作模型本身来对待。 因此,如果我们假设在此上下文中,小部件实际上是模型层次结构的一部分,那么这可能不仅是您所说的简单解决方案,而且是正确的解决方案 因为MVC的核心基础之一是保持每个部分之间的松散耦合,所以您总是希望将业务逻辑与数据输入和表示隔离开来。维护与模型分开显示的小部件会破坏这一点,因此将任何类型的信息方法放入控制器都不合适。您希望模型包含控制器在视图中显示数据时需要操纵或显示的所有内容 您是否考虑过为所有小部件创建一个超类,以便它们始终继承一组公共的方法 例如:
import os
WIDGET_PREFIX = '/tmp'
class Widget:
def __init__(self, name):
self.name = name
self.widget_prefix = WIDGET_PREFIX
self.dirs = os.walk(os.path.join(self.widget_prefix, name))
def _get_base_directory_set(self):
return self.dirs
base_directory_set = property(_get_base_directory_set)
我希望这至少能让您思考一下。我目前采用的解决方案是“每个小部件控制器”。(可能有一个现有的名称)。它将任何UI范围的功能委托给“父”控制器,但存在的目的是控制小部件并在每个小部件的基础上关联任何相关数据 “每个小部件控制器”概念避免将任何不相关的属性投射到小部件本身。您可以扩展这些控制器,以便在需要执行多个小部件操作时,在创建/销毁时注册/注销受控小部件,从而避免使用weakref magic 例如:
class FSBrowserController(wx.EvtHandler):
"""Widget-specific controller for filesystem browsers.
:ivar parent: Parent controller -- useful when UI-wide control is
necessary to respond to an event.
"""
def __init__(self, parent, frame, tree_ctrl, base_dirs):
self.parent = parent
self.frame = frame
self.tree_ctrl = tree_ctrl
self.base_dirs = base_dirs
frame.Bind(EVT_FS_REFRESH, self.refresh)
frame.Bind(wx.EVT_WINDOW_DESTROY, self._unregister)
self.refresh()
self._register()
def _register(self):
"""Register self with parent controller."""
self.parent._fsb_controllers.append(self)
def _unregister(self, event):
"""Unregister self with parent controller."""
if event.GetEventObject() == self.frame:
self.parent._fsb_controllers.remove(self)
def refresh(self, event=None):
"""Refresh the :ivar:`tree_ctrl` using :ivar:`base_dirs`."""
raise NotImplementedError
class Controller(wx.EvtHandler):
"""Main controller for the application.
Handles UI-wide behaviors.
"""
def __init__(self):
self._fsb_controllers = []
fsb_frame = FSBrowserFrame(parent=None)
FSBrowserController(self, fsb_frame, fsb_frame.tree_ctrl,
initial_base_dirs)
fsb_frame.Show()
这样,当FSBrowserFrame被破坏时,控制器和相关数据将随之自然消失。使这种设计难以符合MVC的异常(从MVC的角度来看)是,您希望显示根据您的概念,“不存在于模型中”的信息。MVC中没有“不存在于模型中的信息”这样的东西:它的概念根是“模型保存所有信息,视图只执行表示任务,控制器调解用户交互”
很可能您显示的信息不“对应于任何业务数据”,但(在MVC worldview中)这并不意味着信息“独立于模型”,因为不存在这样的事情——它只是意味着您需要另一个模型类(超出您用来保存“业务数据”的任何类),保存此“非业务”数据!-)
因此,当用户“实例化一个小部件”(创建一个目录显示视图,可能是通过用户在某个主/协调视图上的某些操作,如果“克隆”是实例化一个小部件的方法之一,则可能在另一个现有小部件上创建),控制器负责创建一个小部件对象和该小部件的实例“目录显示模型类”,并在它们之间建立连接(通常通过在小部件上设置对相关模型实例的引用),以及告诉模型进行其初始信息加载。当小部件上的用户操作意味着对模型的操作时,控制器从事件中涉及的小部件中检索对模型实例的引用,并向该实例发送适当的请求(让视图对它感兴趣的人知道信息的变化——通常是通过一些观察者模式;控制器的任务肯定不是向视图提供信息——这是真实的