Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python MVC体系结构中的文本输入_Python_Design Patterns_Model View Controller_Textbox_Wxpython - Fatal编程技术网

Python MVC体系结构中的文本输入

Python MVC体系结构中的文本输入,python,design-patterns,model-view-controller,textbox,wxpython,Python,Design Patterns,Model View Controller,Textbox,Wxpython,我正在考虑如何为图形用户界面设计代码。我正在使用Python和WX:这并不是问题的焦点,但让我们以它为例 我理解MVC的意义,这是我以前尝试过实现的东西,但我似乎经常陷入混乱,接口泄漏,最终一切看起来都有点不愉快,如果有点工作的话。这一次,我想做对(TM) 我(认为我)通常存在的问题是UI控件的固有状态,例如文本条目。在中,MVC由一个“货币”模型和一个单独的控制器小部件中的“添加”和“删除”操作组成,并使用一个不可编辑的文本条目在视图中显示它 import wx from wx.lib.pu

我正在考虑如何为图形用户界面设计代码。我正在使用Python和WX:这并不是问题的焦点,但让我们以它为例

我理解MVC的意义,这是我以前尝试过实现的东西,但我似乎经常陷入混乱,接口泄漏,最终一切看起来都有点不愉快,如果有点工作的话。这一次,我想做对(TM)

我(认为我)通常存在的问题是UI控件的固有状态,例如文本条目。在中,MVC由一个“货币”模型和一个单独的控制器小部件中的“添加”和“删除”操作组成,并使用一个不可编辑的文本条目在视图中显示它

import wx

from wx.lib.pubsub import pub

class NameModel(object):
    def __init__(self):
        self.name = ''

    def set_name(self, new_name):
        self.name = new_name

        print("Model name is now: %s" % new_name)

        # name has changed - tell anyone who cares
        # (maybe bailiffs?)
        pub.sendMessage("NAME CHANGED", name=self.name)

class NameView(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, title="Name View")

        sizer = wx.BoxSizer(wx.VERTICAL)
        self.name_ctrl = wx.TextCtrl(self)

        sizer.Add(self.name_ctrl, 0, wx.EXPAND | wx.ALL)

        self.name_ctrl.SetEditable(True)
        self.SetSizer(sizer)

    def get_name(self):
        '''
        Why does the View have a getter?
        '''
        return self.name_ctrl.GetValue()

    def set_name(self, new_name):

        # check for infinite recursion, but this may need to be aware of
        # more state so we don't throw away useful updates
        # Or perhaps disconnect the event?
        if (self.get_name() != new_name):
            print("Setting name in view: %s" % new_name)
            self.name_ctrl.SetValue(new_name)


class NameController(object):

    def __init__(self, app):

        self.model = NameModel()

        self.view = NameView(None)

        # bind events to wire everything up

        # hmm, this feels a bit wierd - the text entry is in the View,
        # but it's driving the Controller here?
        self.view.name_ctrl.Bind(wx.EVT_TEXT, self.update_name)

        # subscribe the the model messages
        pub.subscribe(self.name_changed, "NAME CHANGED")

        self.view.Show()

    def update_name(self, evt):
        '''
        Called when the user has set the name somehow
        '''
        # ??? getting the data _from_ the view ???
        new_name = self.view.get_name()
        self.model.set_name(new_name)

    def name_changed(self, name):
        '''
        The model has changed, update the view(s) as needed
        '''

        self.view.set_name(name)


if __name__ == "__main__":

    app = wx.App(False)
    controller = NameController(app)
    app.MainLoop()
然而,我想要实现的是一个UI,它有一个字符串的可编辑文本条目(假设它是一个“名称”)。用户可以在框中键入,这将导致一些后端生成建议列表。这些将显示在UI中的列表中。若用户选择其中一个,条目将用该值更新,或者用户可以继续键入以获得更精确的建议。确认(例如“输入”或某些按钮)将对输入框的当前值执行某些操作。这与许多web浏览器的自动完成地址栏基本相同

但是,这似乎意味着输入文本框是视图和控制器的一部分,因为它都显示当前的“名称”(键入或从后端建议中选择),并提供来自用户的事件(在本例中是文本更改事件)。如果没有别的,一个简单的实现将以无限递归结束,因为对条目的修改将导致更新,而更新将修改条目

这是一个简化的示例,没有后端的建议。输入框存在于视图中,控制器可以伸入以从视图中获取值。
if(self.get\u name()!=new\u name):
避免递归,在这种情况下,由于文本首先来自此框,因此控件在视图中永远不会更新

import wx

from wx.lib.pubsub import pub

class NameModel(object):
    def __init__(self):
        self.name = ''

    def set_name(self, new_name):
        self.name = new_name

        print("Model name is now: %s" % new_name)

        # name has changed - tell anyone who cares
        # (maybe bailiffs?)
        pub.sendMessage("NAME CHANGED", name=self.name)

class NameView(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, title="Name View")

        sizer = wx.BoxSizer(wx.VERTICAL)
        self.name_ctrl = wx.TextCtrl(self)

        sizer.Add(self.name_ctrl, 0, wx.EXPAND | wx.ALL)

        self.name_ctrl.SetEditable(True)
        self.SetSizer(sizer)

    def get_name(self):
        '''
        Why does the View have a getter?
        '''
        return self.name_ctrl.GetValue()

    def set_name(self, new_name):

        # check for infinite recursion, but this may need to be aware of
        # more state so we don't throw away useful updates
        # Or perhaps disconnect the event?
        if (self.get_name() != new_name):
            print("Setting name in view: %s" % new_name)
            self.name_ctrl.SetValue(new_name)


class NameController(object):

    def __init__(self, app):

        self.model = NameModel()

        self.view = NameView(None)

        # bind events to wire everything up

        # hmm, this feels a bit wierd - the text entry is in the View,
        # but it's driving the Controller here?
        self.view.name_ctrl.Bind(wx.EVT_TEXT, self.update_name)

        # subscribe the the model messages
        pub.subscribe(self.name_changed, "NAME CHANGED")

        self.view.Show()

    def update_name(self, evt):
        '''
        Called when the user has set the name somehow
        '''
        # ??? getting the data _from_ the view ???
        new_name = self.view.get_name()
        self.model.set_name(new_name)

    def name_changed(self, name):
        '''
        The model has changed, update the view(s) as needed
        '''

        self.view.set_name(name)


if __name__ == "__main__":

    app = wx.App(False)
    controller = NameController(app)
    app.MainLoop()

这是将有状态UI小部件集成到MVC风格程序中的正确方法吗?它感觉有点不整洁,因为小部件同时是“视图”和“控制器”的一部分。正因为如此,我担心“应该更新”的检查将变得繁重、代码维护明智,而且关注点不会完全分离,从而导致将来的复杂性。

为什么视图会有一个getter?您需要知道用户在文本框中写了什么。您可以使用传递给绑定到事件的回调的getter或参数(在本例中为
wx.EVT\u TEXT
)。如果工具箱没有提供您想要的方法(免责声明:我忽略了关于wxwidgets的所有内容),您可能需要在视图中实现它或编写适配器

检查无限递归:在模型中进行检查。这个州属于它,所以它会感觉不那么凌乱。此外,在模型中设置名称并不一定意味着名称更改,只有在名称实际更改时才发送
“名称更改”
,这对我来说是有意义的,并且不会感觉不整洁或繁重

def set_name(self, new_name):
    old_name = self.name
    self.name = new_name

    print("Model name is now: %s" % new_name)
    if old_name != new_name:
        # name has changed - tell anyone who cares
        # (maybe bailiffs?)
        pub.sendMessage("NAME CHANGED", name=self.name)
在控制器中绑定视图事件:这在我的书中是可以接受的。在您的代码中,控制器是唯一知道视图的人,因此它是唯一可以进行绑定的人。当然还有其他的方法,但如果不知道你的书是怎么说的,我就不能提供一个具体的答案


从视图(在控制器中)获取数据:如上所述,
update\u name()
可以作为参数接收数据,也可以作为
evt
的属性接收数据。为什么视图有一个getter?您需要知道用户在文本框中写了什么。您可以使用传递给绑定到事件的回调的getter或参数(在本例中为
wx.EVT\u TEXT
)。如果工具箱没有提供您想要的方法(免责声明:我忽略了关于wxwidgets的所有内容),您可能需要在视图中实现它或编写适配器

检查无限递归:在模型中进行检查。这个州属于它,所以它会感觉不那么凌乱。此外,在模型中设置名称并不一定意味着名称更改,只有在名称实际更改时才发送
“名称更改”
,这对我来说是有意义的,并且不会感觉不整洁或繁重

def set_name(self, new_name):
    old_name = self.name
    self.name = new_name

    print("Model name is now: %s" % new_name)
    if old_name != new_name:
        # name has changed - tell anyone who cares
        # (maybe bailiffs?)
        pub.sendMessage("NAME CHANGED", name=self.name)
在控制器中绑定视图事件:这在我的书中是可以接受的。在您的代码中,控制器是唯一知道视图的人,因此它是唯一可以进行绑定的人。当然还有其他的方法,但如果不知道你的书是怎么说的,我就不能提供一个具体的答案

从视图(在控制器中)获取数据:如上所述,
update\u name()
可以作为参数接收数据,也可以作为
evt
的属性,而不是使用

self.name_ctrl.SetValue(new_name)
这将触发wxEVT_文本事件

使用

不会触发wxEVT_文本事件

如果需要维护插入点,请使用

insertion_point = self.name_ctrl.GetInsertionPoint()
self.name_ctrl.ChangeValue(new_name)
self.name_ctrl.SetInsertionPoint(insertion_point)
而不是使用

self.name_ctrl.SetValue(new_name)
这将触发wxEVT_文本事件

使用

不会触发wxEVT_文本事件

如果需要维护插入点,请使用

insertion_point = self.name_ctrl.GetInsertionPoint()
self.name_ctrl.ChangeValue(new_name)
self.name_ctrl.SetInsertionPoint(insertion_point)

这确实有帮助,但视图的行为仍然有点像控制器的一部分-这就是它与这种UI小部件的配合方式吗?对不起,现在我认为您的实际问题在代码的注释中。我将编辑我的答案来解决这些问题。这肯定会有所帮助,但视图的行为仍然有点像控制器的一部分-这就是这种UI小部件的工作方式吗?对不起,现在我认为您的实际问题在代码的注释中。我将编辑我的答案来解决这些问题。很高兴知道。但是,在这种特定情况下,当用户选择一些后端提供的建议时,我仍然需要处理更新,因为我希望改进建议。例如,用户类型