【问题标题】:How to implement a tkinter app with an MVC architecture?如何使用 MVC 架构实现 tkinter 应用程序?
【发布时间】:2022-02-26 06:09:13
【问题描述】:

我目前从 python 和 MVC 架构开始。我使用 MVC 是因为我读到使用这种架构显示简单的 GUI 应用程序非常容易。

我已经阅读了有关 MVC 架构的信息,但是在尝试自己实现它时,我遇到了几个问题。我想我不是很了解模型、视图和控制器之间的联系。

在我的程序中,我想显示一个按钮。当按下按钮时,文件选择打开,用户可以选择一个文件。之后文件名应该写在输入框中。

我面临的问题是,我不知道如何将视图与控制器和模型连接起来。因此,当按下按钮时,我想通知我的控制器,以便他管理文件选择。文件选择后我想将路径保存在模型中。

这是我的代码:

查看

from tkinter import filedialog
import tkinter as tk

class View(object):
    def __init__(self, controller):
       self.controller = controller

    def showGUI(self, title):
         self.projectWindow = tk.Tk()
         self.projectWindow.title(title)

         self.importEntry= tk.Entry(projectWindow, width=100)
         self.importEntry.pack(pady=10,padx=10)

         importButton = tk.Button(projectWindow, text ="Import File", command= lambda : self.controller.importButtonPressed(controller)) #Here I need the connection to the controller but I cant access the controllers methods
          #I know this is a very bad practise but I dont really know how to inform the controller in a other way 

         importButton.pack(pady=10, padx=10,side=tk.LEFT)

    def display_file_selection_view(self):
       file_path = filedialog.askopenfilename(title = "Select a Excel File", filetypes=[("Excel files", ".xlsx .xls")])
       return file_path

控制器

import Model 
import View
class Controller(object):
   def __init__(self, model, view):
       self.model = model
       self.view = view

       #I tried something like this to inform the model about the state change
       self.model.register_observer(self.view.importEntry)


  def update(self):
    self.view.importEntry.config(state="normal")
    self.view.importEntry.delete(0,'end')
    self.view.importEntry.insert(0,self.model.selectedElement)
    self.view.importEntry.config(state="readonly")

  def importButtonPressed(self):
    selectedElement = self.view.display_file_selection_view()

    #check if data has been selected
    if selectedElement:
        self.model.selectedExcel = selectedElement
    else:
        self.view.showGUI("File Selection")

型号

 class Model(object):
     def __init__(self):
        self._args = ""
        self._selectedExcel = ""
        self.observers = []
    
    def register_observer(self, observer):
        self.observers.append(observer)
    
    def notify(self):
        [observer.update() for observer in self.observers]

    @property
    def selectedExcel(self):
        return self._selectedExcel

    @selectedExcel.setter
    def selectedExcel(self, value):
       self._selectedExcel = value
       self.notify()

主要

  import Model
  import View 
  import Controller 
  if __name__ == "__main__":
     model = Model.Model()
     view = View.View(Controller)
     controller = Controller.Controller(model, view)

【问题讨论】:

    标签: python tkinter model-view-controller


    【解决方案1】:

    您的控制器类对象需要在 View 类中初始化。

    def __init__(self,controller):
        self. controller = controller
    

    这段代码的缺陷是控制器总是一个空对象。而且您基本上是将空对象的值分配给您的实例变量。

    您需要像这样在 View 构造函数中实例化控制器类的实例

    def __init__(self,controller):
        self.controller = Controller(model, view)
    

    您需要将变量模型和视图更正为模型和视图构造函数的对象。

    【讨论】:

      【解决方案2】:

      MVC 的重点在于封装元素。这意味着依赖关系总是朝着一个方向发展。视图不能“知道”控制器。控制器必须“知道”视图。保持 M V 和 C 分开的方法是使它们成为插座和插头。

      查看

      from tkinter import filedialog
      import tkinter as tk
      
      class View(object):
      
          # The view will only be responsible for displaying widgets
          # and getting some information from user
      
          def __init__(self):
              # it means it arranges all widgets just as it initialize
              self.callbacks = {}
              self._showGUI("File Selection")
      
          def _showGUI(self, title):
              # This method will only build widgets. I prefixed it underline,
              # as it is private method of the view class, and shouldn't be
              # used outside of it (i.e. by the controller)
              self.projectWindow = tk.Tk()
              self.projectWindow.title(title)
      
              self.importEntry= tk.Entry(self.projectWindow, width=100)
              self.importEntry.pack(pady=10,padx=10)
      
              self.importButton = tk.Button(self.projectWindow, text ="Import File")
              # In this moment we don't define command of this button
              self.importButton.pack(pady=10, padx=10,side=tk.LEFT)
      
          def add_callback(self, key, method):
              # This method makes connections, and lets the
              # controller share its own methods to view, and prevents
              # view being dependent on controller. This method will be
              # used specifically by the controller.
      
              self.callbacks[key] = method
      
          def bind_commands(self):
              # This method also will be used by the controller. And this
              # going to happened at the moment, when callbacks will be defined
      
              self.importButton.config(command=self.callbacks['import'])
      
          def run(self):
              # This method lets program works. It also is used by the controller
              self.projectWindow.mainloop()
      
          def get_file_selection_view(self):
              file_path = filedialog.askopenfilename(title = "Select a Excel File", filetypes=[("Excel files", ".xlsx .xls")])
              return file_path
      
          def display_selection(self, content):
              self.importEntry.config(state="normal")
              self.importEntry.delete(0,'end')
              self.importEntry.insert(0,self.model.selectedElement)
              self.importEntry.config(state="readonly")
      

      控制器

      class Controller(object):
          def __init__(self, model, view):
              self.model = model
              self.view = view
      
              # Here we pass to the view controller method without making view
              # dependent on it
              self.view.add_callback('import', self.importButtonPressed)
      
              # Now, callback exist, so the controller can ask the view to bind
              # its widgets to passed values of callbacks dictionary
              self.view.bind_commands()
      
              self.view.run()
      
          def importButtonPressed(self):
              # This method, as its controller method, should lead all actions
      
              # First it gets file path
              selectedElement = self.view.get_file_selection_view()
      
              # Check if data has been selected
              if selectedElement:
                  # Here it should use some model method to do whatever you expect
                  # to model do, and return result
                  # Honestly - I don't know what do you want from your model
                  # But whatever it is let model do it itself, and return result here.
                  result = self.model.some_method(selectedElement)
              else:
                  pass
      
              # Now you can easily display result. But still, to stick with MVC
              # architecture you should use view method to got it done, instead of
              # directly using some view objects method. It should work (both view and 
              # model), like you're
              # going to make in the future another View class with same interface
              # (all public methods and parameters) and all you need to do to start using 
              # your new class, would be change imports in main.py
      
              self.view.display_filename(result)
      

      主要

      import model
      import view
      import controller
      
      
      if __name__ == "__main__":
          model = model.Model()
          # The view doesn't need to have a controller parameter
          view = view.View()
          controller = controller.Controller(model, view)
      

      【讨论】:

        猜你喜欢
        • 2013-12-27
        • 1970-01-01
        • 1970-01-01
        • 2011-02-02
        • 2010-11-11
        • 2013-07-30
        • 1970-01-01
        • 2012-08-25
        • 1970-01-01
        相关资源
        最近更新 更多