如何让控制器保持活着状态

时间:2014-03-05 08:57:07

标签: python model-view-controller wxpython

我正在尝试使用this link实现MVC模式。一切都有效,直到我用它的控制器创建了第二帧。第一帧的控制器仍处于活动状态,因为代码被阻止:

app.MainLoop()

但是当第二个控制器(在按钮事件中创建)时,它将被GCed,因为它已经脱离了上下文。我不想将控制器引用到视图中,因为我想尝试被动视图方法,即视图是哑的,控制器更新视图,也会导致循环引用。

以下是我调用控制器的方式:

def OnClick(self, evt):
    controller = MyController2()

如果我将控制器引用到父控制器,如下所示:

def OnClick(self, evt):
    self.controller = MyController2()

控制器仍处于活动状态,但即使关闭第2帧后控制器仍然不会存在?如何让第二个控制器保持活动状态,但在视图关闭后仍可以进行GC控制?另外我想保持视图干净,因为它已经包含了小部件定义(我在1帧中有很多小部件)。

对于我糟糕的英语,任何帮助都会受到赞赏和抱歉。

编辑:

这是我收集控制器垃圾的示例。此示例使用blinker而不是pubsub。

import wx

from blinker import signal

class ChildView(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)

        sizer = wx.BoxSizer()

        self.btn_child = wx.Button(self, label="click me")
        sizer.Add(self.btn_child)

        self.SetSizer(sizer)
        self.Center()

        # events
        self.btn_child.Bind(wx.EVT_BUTTON, self.on_btn_child_click)

    def on_btn_child_click(self, event):
        signal("child.btn_child_click").send(self)

class ChildController(object):
    def __init__(self, parent):
        self.view = ChildView(parent)

        self.subscribe_signal()

        self.view.Show()

    def subscribe_signal(self):
        signal("child.btn_child_click").connect(self.on_btn_child_click)

    def on_btn_child_click(self, sender):
        print "button on child clicked"

class ParentView(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)

        sizer = wx.BoxSizer()

        self.btn = wx.Button(self, label="show child window")
        sizer.Add(self.btn)

        self.SetSizer(sizer)
        self.Center()

        # events
        self.btn.Bind(wx.EVT_BUTTON, self.on_btn_click)

    def on_btn_click(self, event):
        signal("parent.btn_click").send(self)

class ParentController(object):
    def __init__(self):
        self.view = ParentView(None)

        self.subscribe_signal()

        self.view.Show()

    def subscribe_signal(self):
        signal("parent.btn_click").connect(self.on_btn_click)

    def on_btn_click(self, sender):
        child_controller = ChildController(self.view)

def main():
    app = wx.App()

    controller = ParentController()

    app.MainLoop()

if __name__ == '__main__':
    main()

正如您所看到的,孩子的按钮不能正常工作,因为它的控制器已经被垃圾收集(没有人参考控制器)。我尝试了一些解决方案:

  1. 引用父控制器中的子控制器。 当孩子关闭时,子控制器仍然活着,除非我手动删除它或用新控制器替换控制器(重新打开子窗口)。如果控制器保存大量数据,这尤其糟糕。

  2. 控制器和视图之间的循环引用(在controller->视图中使用weakref)。 这是我最好的选择,但我想避免循环引用。

  3. 那么,我应该在哪里引用孩子的控制器以使其保持活力?

2 个答案:

答案 0 :(得分:0)

嗯,这是一个很大的话题:如何在wxPython中做MVC。没有一个正确的答案。如果您选择任何答案,当您尝试虔诚地遵循设计模式时,您将遇到困难的选择。

其中一些例子的复杂性不足以解决其中的一些问题。这是我创建的一个示例,用于尝试说明适用于我的方法。

您可以看到,在创建View元素和控制器之间没有耦合,并且控制器拥有“设置应用程序”的逻辑。视图是真正被动的 - 它的元素仅在需要满足控制器动作时才会存在。

我添加了两个视图实现,您可以在使用命令行参数之间进行选择。我要表明的是,这是对您是否实现了良好的控制器/视图分离的真实测试 - 您应该能够插入不同的视图实现而不必更改控制器。控制器依赖于业务逻辑(包括应用程序中可能发生的事件类型)和模型API。它不依赖于视图中的任何内容。

档案:mvc_demo_banking_simulator.py

#!/usr/bin/env python

# This is a demo/example of how you might do MVC (Passive View) in wxpython.
import sys

import wx

from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub

from enum import Enum

import logging

import bank_view
import bank_view_separate_frames


#####
#   The control events that the Controller can process
#   Usually this would be in a module imported by each of M, V and C
#   These are the possible values for the "event" parameter of an APP_EVENT message

class AppEvents(Enum):
    APP_EXIT = 0
    APP_ADD_WORKPLACE = 1
    APP_ADD_CUSTOMER = 2
    CUSTOMER_DEPOSIT = 3
    CUSTOMER_WITHDRAWAL = 4
    CUSTOMER_EARN = 5
    PERSON_WORK = 6
    EARN_REVENUE = 7

#############
#

class Controller:
    def __init__(self, view_separate_frames):
        self._log = logging.getLogger("MVC Logger")
        self._log.info("MVC Main Controller: starting...")

        pub.subscribe(self.OnAppEvent, "APP_EVENT")

        if view_separate_frames:
            self._view = bank_view_separate_frames.MainWindow("Demo MVC - Bank Simulator")
        else:
            self._view = bank_view.MainWindow("Demo MVC - Bank Simulator")

        # Note that this controller can conceptually handle many customers and workplaces,
        # even though the current view implementations can't...

        self._customers = []
        self._workplaces = []

        # Here is the place in the controller where we restore the app state from storage,
        # or (as in this case) create from scratch

        self._customers.append(CustomerModel("Fred Nerks"))

        self._bank = BankModel()
        self._bank.CreateAccountFor(self._customers[0])

        self._view.AddBank(self._bank)
        self._view.AddCustomer(self._customers[0])

    def OnAppEvent(self, event, value=None):
        if event == AppEvents.APP_EXIT:
            self._log.info("MVC Controller: exit requested.")
            # do any necessary state saving...
            # ... then:
            sys.exit()
        elif event == AppEvents.APP_ADD_WORKPLACE:
            self._log.info("Main Controller: Add workplace requested...")
            self._workplaces.append(WorkplaceModel("Workplace %d" % (len(self._workplaces)+1) ))
            # Note: here the controller is implementing application business logic driving interactions between models
            new_workplace = self._workplaces[-1]
            for customer in self._customers:
                if customer.AcceptEmployment(new_workplace):
                    new_workplace.AddEmployee(customer)
            self._view.AddWorkplace(new_workplace)

        elif event == AppEvents.CUSTOMER_DEPOSIT:
            (the_customer, the_amount) = value
            self._log.info("customer deposit funds requested(%s)..." % the_customer.name)
            the_customer.DepositFunds(the_amount)
        elif event == AppEvents.CUSTOMER_WITHDRAWAL:
            (the_customer, the_amount) = value
            self._log.info("customer withdraw funds requested(%s)..." % the_customer.name)
            the_customer.WithdrawFunds(the_amount)
        elif event == AppEvents.CUSTOMER_EARN:
            the_customer = value
            self._log.info("customer earn requested(%s)..." % the_customer.name)
            the_customer.Work()

        elif event == AppEvents.PERSON_WORK:
            the_person = value
            self._log.info("request for customer %s to work ..." % customer.name)
            the_person.Work()
        elif event == AppEvents.EARN_REVENUE:
            self._log.info("request for sales revenue payment ...")
            the_workplace = value
            the_workplace.EarnFromSales()
        else:
            raise Exception("Unknown APP_EVENT: %s" % event)

#################
#
#  Models
#

class AccountModel:
    def __init__(self, owner, bank):
        self._balance = 0
        self._owner = owner
        self._bank = bank

    def AddMoney(self, amount):
        self._balance += amount
        self._bank.ReceiveMoney(amount)

    def RemoveMoney(self, amount):
        withdrawal_amount = min(amount, self._balance)  # they can't take out more than their account balance
        pay_amount = self._bank.PayMoney(withdrawal_amount)
        self._balance -= pay_amount
        return pay_amount


class CustomerModel:
    def __init__(self, name):
        self._log = logging.getLogger("MVC Logger")
        self._log.info("Customer %s logging started" % name)
        self.name = name

        self._cash = 0
        self._account = None
        self._employer = None

    def GetAccount(self, account):
        self._account = account

    def CashInHand(self):
        return self._cash

    def AcceptEmployment(self, workplace):
        self._employer = workplace
        self._log.info("%s accepted employment at %s" % (self.name, workplace.name))
        return True

    def Work(self):
        if self._employer:
            self._cash += self._employer.RequestPay(self)
            pub.sendMessage("CUSTOMER_BALANCE_EVENT", value = self)
        else:
            self._log.info("%s cant work, not employed" % self.name)

    def DepositFunds(self, amount):
        deposit_amount = min(amount, self._cash) # can't deposit more than we have
        self._cash -= deposit_amount
        self._account.AddMoney(deposit_amount)
        pub.sendMessage("CUSTOMER_BALANCE_EVENT", value = self)

    def WithdrawFunds(self, amount):
        amount_received = self._account.RemoveMoney(amount)
        self._cash += amount_received
        pub.sendMessage("CUSTOMER_BALANCE_EVENT", value = self)


class BankModel:
    def __init__(self):
        self._funds = 0
        self._accounts = {}

    def Funds(self):
        return self._funds

    def CreateAccountFor(self, owner):
        new_account = AccountModel(owner, self)
        self._accounts[owner.name] = new_account
        owner.GetAccount(new_account)

    def PayMoney(self, amount):
        paid = min(self._funds, amount)
        self._funds -= paid
        pub.sendMessage("BANK_BALANCE_EVENT")
        return paid

    def ReceiveMoney(self, amount):
        self._funds += amount
        pub.sendMessage("BANK_BALANCE_EVENT")


class WorkplaceModel:
    def __init__(self, name):
        self.name = name

        self._employees = []
        self._standardWage = 10

        self._funds = 0
        self._salesRevenue = 20

    def AddEmployee(self, employee):
        self._employees.append(employee)

    def EarnFromSales(self):
        self._funds += self._salesRevenue
        pub.sendMessage("WORKPLACE_BALANCE_EVENT")

    def Funds(self):
        return self._funds

    def RequestPay(self, employee):
        # (should check if employee is legit)
        paid = min(self._funds, self._standardWage)
        self._funds -= paid
        pub.sendMessage("WORKPLACE_BALANCE_EVENT")
        return paid

##############
#
#

logging.basicConfig(level=logging.INFO)

view_separate_frames = False

if len(sys.argv) > 1:
    if sys.argv[1] == "view-separate-frames":
        view_separate_frames = True

app = wx.App()
controller = Controller(view_separate_frames)
app.MainLoop()

档案 bank_view.py

import wx
from enum import Enum

from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub

import logging

#####
#   The control events that the Controller can process
#   Usually this would be in a module imported by each of M, V and C
#   These are the possible values for the "event" parameter of an APP_EVENT message

class AppEvents(Enum):
    APP_EXIT = 0
    APP_ADD_WORKPLACE = 1
    APP_ADD_CUSTOMER = 2
    CUSTOMER_DEPOSIT = 3
    CUSTOMER_WITHDRAWAL = 4
    CUSTOMER_EARN = 5
    PERSON_WORK = 6
    EARN_REVENUE = 7


#################
#
#   View
#

class MainWindow(wx.Frame):

    def __init__(self,  title):
        wx.Frame.__init__(self, None, -1, title)

        self._log = logging.getLogger("MVC Logger")
        self._log.info("MVC View - separate workspace: starting...")

        self._bankStatusDisplay = None
        self._customerUIs = {}
        self._workplaceUIs = {}

        # this is where we will put display elements - it's up to the controller to add them
        self._sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.SetSizer(self._sizer)

        # but we do need one button immediately...
        add_workplace_button = wx.Button(self, label="Add Workplace")
        self._sizer.Add(add_workplace_button)
        self._sizer.Layout()

        self.Bind(wx.EVT_BUTTON, self._OnAddWorkplaceClick, add_workplace_button)

        # These are the events that cause us to update our display
        pub.subscribe(self._OnCustomerBalanceChange, "CUSTOMER_BALANCE_EVENT")
        pub.subscribe(self._OnBankBalanceChange, "BANK_BALANCE_EVENT")

        self.Show()

    def _OnAddWorkplaceClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.APP_ADD_WORKPLACE)

    def AddWorkplace(self, workplace):
        self._workplaceUIs[workplace.name] = the_ui = WorkplaceInterface(self, workplace)
        self._sizer.Add(the_ui)
        self._sizer.Layout()

    def AddBank(self, bank):
        if not(self._bankStatusDisplay):
            self._bankStatusDisplay = BankStatusDisplay(self, bank)        
            self._sizer.Add(self._bankStatusDisplay)
            self._sizer.Layout()
        else:
            raise Exception("We can only handle one bank at the moment")

    def AddCustomer(self, customer):
        self._customerUIs[customer.name] = the_ui = CustomerInterface(self, customer)
        self._sizer.Add(the_ui)
        self._sizer.Layout()

    def _OnCustomerBalanceChange(self, value):
        customer = value
        self._customerUIs[customer.name].UpdateBalance()

    def _OnBankBalanceChange(self):
        self._bankStatusDisplay.Update()


class BankStatusDisplay(wx.Panel):
    def __init__(self, parent, bank):
        wx.Panel.__init__(self, parent, style = wx.RAISED_BORDER)

        self._bank = bank

        sizer = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(self, label="Bank Funds")

        balance_display = wx.TextCtrl(self)
        balance_display.SetEditable(False)
        balance_display.SetValue('$' + str(bank.Funds()))

        sizer.Add(label, 0, wx.EXPAND | wx.ALL)
        sizer.Add(balance_display, 0, wx.EXPAND | wx.ALL)

        self.SetSizer(sizer)        

        self._balanceDisplay = balance_display

    def Update(self):
        self._balanceDisplay.SetValue('$' + str(self._bank.Funds()))


class CustomerInterface(wx.Panel):
    def __init__(self, parent, customer):
        wx.Panel.__init__(self, parent, style = wx.RAISED_BORDER)

        self._customer = customer
        self._standardTransaction = 5 # how much customers try to deposit and withdraw

        sizer = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(self, label=customer.name)

        self._balanceDisplay = wx.TextCtrl(self)
        self._balanceDisplay.SetEditable(False)
        self._balanceDisplay.SetValue('$' + str(customer.CashInHand()))

        deposit_button = wx.Button(self, label="Deposit $" + str(self._standardTransaction))
        withdraw_button = wx.Button(self, label="Withdraw $" + str(self._standardTransaction))
        earn_button = wx.Button(self, label="Earn Money")

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

        sizer.Add(deposit_button, 0, wx.EXPAND | wx.ALL)
        sizer.Add(withdraw_button, 0, wx.EXPAND | wx.ALL)        
        sizer.Add(earn_button, 0, wx.EXPAND | wx.ALL)

        self.Bind(wx.EVT_BUTTON, self._OnDepositClick, deposit_button)
        self.Bind(wx.EVT_BUTTON, self._OnWithdrawClick, withdraw_button)
        self.Bind(wx.EVT_BUTTON, self._OnEarnClick, earn_button)

        self.SetSizer(sizer)
        self.Show()

    def _OnDepositClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.CUSTOMER_DEPOSIT, value = (self._customer, self._standardTransaction))

    def _OnWithdrawClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.CUSTOMER_WITHDRAWAL, value = (self._customer, self._standardTransaction))

    def _OnEarnClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.CUSTOMER_EARN, value = self._customer)

    def UpdateBalance(self):
        self._balanceDisplay.SetValue('$' + str(self._customer.CashInHand()))


class WorkplaceInterface(wx.Panel):
    def __init__(self, parent, workplace):
        wx.Panel.__init__(self, parent, style = wx.RAISED_BORDER)

        self._workplace = workplace

        sizer = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(self, label="Workplace Funds")

        self._balanceDisplay = wx.TextCtrl(self)
        self._balanceDisplay.SetEditable(False)
        self._balanceDisplay.SetValue('$' + str(workplace.Funds()))

        revenue_button = wx.Button(self, label="Earn Revenue")

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

        sizer.Add(revenue_button, 0, wx.EXPAND | wx.ALL)

        self.SetSizer(sizer)
        self.Show()

        self.Bind(wx.EVT_BUTTON, self._OnRevenueClick, revenue_button)

        pub.subscribe(self._OnBalanceChange, "WORKPLACE_BALANCE_EVENT")

    def _OnRevenueClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.EARN_REVENUE, value = (self._workplace))

    def _OnBalanceChange(self):
        self._balanceDisplay.SetValue('$' + str(self._workplace.Funds()))

档案:bank_view_separate_frames.py

import wx
from enum import Enum

from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub

import logging

#####
#   The control events that the Controller can process
#   Usually this would be in a module imported by each of M, V and C
#   These are the possible values for the "event" parameter of an APP_EVENT message

class AppEvents(Enum):
    APP_EXIT = 0
    APP_ADD_WORKPLACE = 1
    APP_ADD_CUSTOMER = 2
    CUSTOMER_DEPOSIT = 3
    CUSTOMER_WITHDRAWAL = 4
    CUSTOMER_EARN = 5
    PERSON_WORK = 6
    EARN_REVENUE = 7


#################
#
#   View
#

class MainWindow(wx.Frame):

    def __init__(self,  title):
        wx.Frame.__init__(self, None, -1, title)

        self._log = logging.getLogger("MVC Logger")
        self._log.info("MVC View - separate workspace: starting...")

        self._bankStatusDisplay = None
        self._customerUIs = {}
        self._workplaceUIs = {}

        # this is where we will put display elements - it's up to the controller to add them
        self._sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.SetSizer(self._sizer)

        # but we do need one button immediately...
        add_workplace_button = wx.Button(self, label="Add Workplace")
        self._sizer.Add(add_workplace_button)
        self._sizer.Layout()

        self.Bind(wx.EVT_BUTTON, self._OnAddWorkplaceClick, add_workplace_button)

        # These are the events that cause us to update our display
        pub.subscribe(self._OnCustomerBalanceChange, "CUSTOMER_BALANCE_EVENT")
        pub.subscribe(self._OnBankBalanceChange, "BANK_BALANCE_EVENT")

        self.Show()

    def _OnAddWorkplaceClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.APP_ADD_WORKPLACE)

    def AddWorkplace(self, workplace):
        self._workplaceUIs[workplace.name] = WorkplaceInterface(self, workplace)

    def AddBank(self, bank):
        if not(self._bankStatusDisplay):
            self._bankStatusDisplay = BankStatusDisplay(self, bank)        
            self._sizer.Add(self._bankStatusDisplay)
            self._sizer.Layout()
        else:
            raise Exception("We can only handle one bank at the moment")

    def AddCustomer(self, customer):
        self._customerUIs[customer.name] = CustomerInterface(self, customer)

    def AddWorkplace(self, workplace):
        self._theWorkplaceUI = WorkplaceInterface(workplace)

    def _OnCustomerBalanceChange(self, value):
        customer = value
        self._customerUIs[customer.name].UpdateBalance()

    def _OnBankBalanceChange(self):
        self._bankStatusDisplay.Update()


class BankStatusDisplay(wx.Panel):
    def __init__(self, parent, bank):
        wx.Panel.__init__(self, parent, style = wx.RAISED_BORDER)

        self._bank = bank

        sizer = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(self, label="Bank Funds")

        balance_display = wx.TextCtrl(self)
        balance_display.SetEditable(False)
        balance_display.SetValue('$' + str(bank.Funds()))

        sizer.Add(label, 0, wx.EXPAND | wx.ALL)
        sizer.Add(balance_display, 0, wx.EXPAND | wx.ALL)

        self.SetSizer(sizer)        

        self._balanceDisplay = balance_display

    def Update(self):
        self._balanceDisplay.SetValue('$' + str(self._bank.Funds()))


class CustomerInterface(wx.Frame):
    def __init__(self, parent, customer):
        wx.Frame.__init__(self, None, -1, customer.name, size = (200,300))

        self._customer = customer
        self._standardTransaction = 5 # how much customers try to deposit and withdraw

        sizer = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(self, label=customer.name)

        self._balanceDisplay = wx.TextCtrl(self)
        self._balanceDisplay.SetEditable(False)
        self._balanceDisplay.SetValue('$' + str(customer.CashInHand()))

        deposit_button = wx.Button(self, label="Deposit $" + str(self._standardTransaction))
        withdraw_button = wx.Button(self, label="Withdraw $" + str(self._standardTransaction))
        earn_button = wx.Button(self, label="Earn Money")

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

        sizer.Add(deposit_button, 0, wx.EXPAND | wx.ALL)
        sizer.Add(withdraw_button, 0, wx.EXPAND | wx.ALL)        
        sizer.Add(earn_button, 0, wx.EXPAND | wx.ALL)

        self.Bind(wx.EVT_BUTTON, self._OnDepositClick, deposit_button)
        self.Bind(wx.EVT_BUTTON, self._OnWithdrawClick, withdraw_button)
        self.Bind(wx.EVT_BUTTON, self._OnEarnClick, earn_button)

        self.SetSizer(sizer)
        self.Show()

    def _OnDepositClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.CUSTOMER_DEPOSIT, value = (self._customer, self._standardTransaction))

    def _OnWithdrawClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.CUSTOMER_WITHDRAWAL, value = (self._customer, self._standardTransaction))

    def _OnEarnClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.CUSTOMER_EARN, value = self._customer)

    def UpdateBalance(self):
        self._balanceDisplay.SetValue('$' + str(self._customer.CashInHand()))


class WorkplaceInterface(wx.Frame):
    def __init__(self, workplace):
        wx.Frame.__init__(self, None, -1, workplace.name, size = (200,200))

        self._workplace = workplace

        self._panel = wx.Panel(self, style = wx.RAISED_BORDER)

        sizer = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(self._panel, label="Funds")

        self._balanceDisplay = wx.TextCtrl(self._panel)
        self._balanceDisplay.SetEditable(False)
        self._balanceDisplay.SetValue('$' + str(workplace.Funds()))

        revenue_button = wx.Button(self._panel, label="Earn Revenue")

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

        sizer.Add(revenue_button, 0, wx.EXPAND | wx.ALL)

        self._panel.SetSizer(sizer)

        self.Bind(wx.EVT_BUTTON, self._OnRevenueClick, revenue_button)

        pub.subscribe(self._OnBalanceChange, "WORKPLACE_BALANCE_EVENT")

        self.Show()

    def _OnRevenueClick(self, event):
        pub.sendMessage("APP_EVENT", event = AppEvents.EARN_REVENUE, value = (self._workplace))

    def _OnBalanceChange(self):
        self._balanceDisplay.SetValue('$' + str(self._workplace.Funds()))

随机笔记:

  • 我已将模型类命名为“ThingoModel”。通常情况下,你只需要将它们命名为Thingo - 暗示了“模型”,但我这样做是为了明白这一点。

  • 同样,选择视图组件的名称以强调其视角。

  • 此示例显示View的组件,通知发布者用户通过pubsub消息请求的内容(“App Events”)

  • 它显示模型通知“谁关心”(视图)有关可能需要使用pubsub消息(模型特定事件)进行视图更改的事件。

  • 它让Controller告诉View使用方法调用做什么

  • 非常小心不要直接使用View访问模型。 View具有处理(引用)模型以便显示来自它们的信息。在C ++中,这些将是View无法改变的常见问题。在python中,您所能做的就是确保View 从模型中读取内容,但将所有内容通知给Controller

  • 此代码扩展到非常容易支持多个客户。你应该能够在控制器中创造更多,并且(手指交叉)他们将被处理

  • 该代码假定只有一家银行。

我使用这种基本模式编写了几个大型wxpython应用程序。当我开始时,我构建了第一个这些应用程序作为MVC-P中的实验。当意想不到的情况迫使我完全重做GUI的布局时,我完全转变为这种方法。因为我的视图中没有业务逻辑,所以这完全易于处理,并且重新完成的应用程序可以相对快速地获得并且几乎没有功能回归。

答案 1 :(得分:-1)

感谢Yoriz的例子,我想创建一个继承视图并引用控制器的类,这样控制器就不会立即被垃圾回收。

这个新类将处理所有视图的事件并直接调用控制器的方法,并且还具有更新视图的所有方法。控制器将使用信号与视图进行通信,此类将监听该信号并更新视图。

当需要创建另一个视图时,事件将调用控制器的方法,视图本身作为参数,然后控制器将创建另一个视图和控制器,其视图作为父视图。

感谢大家的贡献,如果有更好的答案,我会接受它。