PyQt 和 MVC 模式

发布于 2024-08-10 04:19:06 字数 217 浏览 2 评论 0原文

我正在尝试使用 PyQt 设计 MVC 模式。 我想将所有程序分为 3 部分:

  1. 从所有 Qt 类(模型)中抽象的类,
  2. 将模型中的数据提供给 Qt 应用程序(控制器)
  3. Qt 应用程序本身,具有连接信号的已定义方法 SignalsToSlots带控制器。

这是最优的吗? PyQt开发推荐使用什么方案?

I am trying to design an MVC-pattern with PyQt.
I want to split all programs into 3 parts:

  1. classes abstracted from all Qt classes (model)
  2. classes providing data from the model to a Qt app (controller)
  3. the Qt app itself with defined method SignalsToSlots that connects signals with controller.

Is this optimally? What scheme is recommended for use in PyQt development?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夏末染殇 2024-08-17 04:19:06

你应该做的第一件事就是使用 Qt4 设计器来设计你的 gui 并使用 pyuic4 来生成你的 python GUI。这将是你的观点,你永远不会手动编辑这些 python 文件。始终使用设计器进行更改,这可确保您的视图与模型和控件分开。

对于控制元素,创建一个继承自基本 gui 小部件(例如 QMainWindow)的中心类。然后,该对象将包含一个成员 ui,它是您刚刚生成的视图对象。

这是教程 2013年更新的示例

:这里是有关 PyQt 和 MVC 模型的最新教程
PyQt MVC 教程系列

import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notepad

class StartQT4(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_notepad()
        self.ui.setupUi(self)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = StartQT4()
    myapp.show()
    sys.exit(app.exec_())

上面示例中的关键点是控制器包含ui 并且不直接继承它。控制器将负责管理 GUI 的信号槽连接并为您的数据模型提供接口。

为了描述模型部分,我们需要一个示例,假设您的项目是创建一个电影集合数据库。该模型将包括代表各个电影的内部对象以及代表电影列表的对象。您的控件将从视图中输入数据并捕获信号,然后在要求模型自行更新之前验证它们。这部分至关重要,如果可能的话,控制器不应该直接访问模型,它应该要求模型访问自身。

下面是这种交互的一个小例子(未经测试,可能有一些拼写错误):

class Movie():
    def __init__(self,title=None,year=None,genre=None):
        self.title=title
        self.year=year
        self.genre=genre
    def update(self,title=None,year=None,genre=None):
        self.title=title
        self.year=year
        self.genre=genre
    def to_xml(self,title=None,date=None,genre=None):
        pass #not implementing this for an example!

#when the controller tries to update it should use update function
movie1.update("Manos Hands Of Fate",1966,"Awesome")
#don't set by direct access, your controller shouldn't get that deep
movie1.title="Bad Idea" #do not want!

在 MVC 中集中访问也很重要,比如用户可以通过在屏幕上双击标题或单击标题旁边的编辑来更改标题字段,这些接口两个最终应该使用相同的方法进行更改。我并不是说每个人都调用 movie.update_title(title)。我的意思是两个信号应该在控制器中使用相同的方法。

尽可能尝试使视图和控制器之间的所有关系为多对一。意思是,您有 5 种方法来更改 gui 中的某些内容,控制器中有 1 种方法来处理此问题。如果插槽并非全部兼容,则为每个方法创建方法,然后调用一个方法。如果您针对 5 种视图样式解决该问题 5 次,那么确实没有理由将视图与控件分开。此外,由于您现在只有一种方法可以在控制器中执行某些操作,因此您在控制和模型之间拥有良好的一对一关系。

至于让您的模型与 Qt 完全分离,这并不是真正必要的,而且实际上可能会让您的生活变得更加困难。在模型中使用 QStrings 之类的东西可能会很方便,如果在另一个应用程序中您不希望 Gui 的开销,但希望模型仅导入 QtCore。希望这有帮助!

One of the first things you should do is use Qt4 designer to design your gui and use pyuic4 to generate your python GUI. This will be your view, you NEVER edit these python files by hand. Always make changes using designer, this ensures your View is separate from your model and control.

For the control element, create a central class that inherits from your base gui widget such as QMainWindow. This object will then contain a member ui that is your view object you just generated.

here is an example from a tutorial

UPDATE 2013: Here is a more recent tutorial(s) on PyQt and MVC Model
PyQt MVC Tutorial Series

import sys
from PyQt4 import QtCore, QtGui
from edytor import Ui_notepad

class StartQT4(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_notepad()
        self.ui.setupUi(self)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = StartQT4()
    myapp.show()
    sys.exit(app.exec_())

The key point in the above example is the controller contains the ui and doesn't inherit it directly. The controller will be responsible for managing signal slots connections for your gui and providing an interface to you data model.

To describe the model part we need an example, lets assume your project is to create a movie collection database. The model would include the internal objects that represent individual movies, along with objects that represent lists of movies. You control would take the data entered from the view and catch the signals, then validate them before asking the model to update itself. That part is crucial, the controller shouldn't directly access the model if at all possible, it should ask the model to access itself.

Here is a small example of this interaction(untested, may be some typos):

class Movie():
    def __init__(self,title=None,year=None,genre=None):
        self.title=title
        self.year=year
        self.genre=genre
    def update(self,title=None,year=None,genre=None):
        self.title=title
        self.year=year
        self.genre=genre
    def to_xml(self,title=None,date=None,genre=None):
        pass #not implementing this for an example!

#when the controller tries to update it should use update function
movie1.update("Manos Hands Of Fate",1966,"Awesome")
#don't set by direct access, your controller shouldn't get that deep
movie1.title="Bad Idea" #do not want!

It is also important in MVC to centralize access, say the user can change the title by double clicking it on the screen, or by click edit next to the title field, both of those interfaces should end up using the same method for the change. And by this I don't mean each one calls movie.update_title(title). I mean that both signals should use the same method in the controller.

Try as much as possible to make all relationships between the View and the controller many to 1. Meaning, that is you have 5 ways to change something in the gui, have 1 method in the controller to handle this. If the slots aren't all compatible than create methods for each of the methods that then call one single method. If you solve the problem 5 times for 5 view styles then there really isn't and reason to separate the view from the control. Also since you now have only one way to do something in the controller you ahve a nice 1 to 1 relationship between control and model.

As far as having your model completely separate from Qt, that isn't really necessary and may actually make life harder for you. Using things like QStrings in you model can be convenient, and if in another application you don't want the overhead of a Gui but want the models just import QtCore only. Hopefully this helps!

美煞众生 2024-08-17 04:19:06

是的,PyQt 使用模型/视图概念(正式没有“控制器”部分),但您可能对 PyQt 中的含义有些扭曲。

有两部分:

  1. 模型,从适当的 PyQt 基本抽象模型类(QAbstractItemModelQAbstractTableModelQAbstractListModel 等)子类化。这些模型可以直接与您的数据源(文件、数据库)对话,或者代理您自己之前编写的与 PyQt 无关的模型。
  2. 视图,在 Qt 库中实现,通常不需要任何修改(例如:QTreeViewQTableView 等)。即使是一些更简单的控件,例如 QComboBox,也可以充当 PyQt 模型的视图。

应用程序中对信号等做出反应的所有其他部分都可以被视为“控制器”。

PyQt 还提供了一组预定义的“通用”模型,如果您只需要模型中的简单功能,例如 QStringListModel、QStandardItemModel 等,则可以对其进行子类化或直接使用。还有一些可以直接与数据库对话的模型,例如QSqlTableModel。

Yes, PyQt uses Model/View concept (officially without the "Controller" part), but may be you have a somewhat distorted picture what does it mean in PyQt.

There are two parts:

  1. Models, subclassed from appropriate PyQt base abstract model classes (QAbstractItemModel, QAbstractTableModel, QAbstractListModel, etc.). These models can talk to your data sources directly (files, databases), or proxy your own PyQt-agnostic models which were written before.
  2. Views, which are implemented in Qt library, and often do not require any modifications (examples: QTreeView, QTableView and others). Even some simpler controls, like QComboBox can act as a view for a PyQt model.

All other parts of you application, which react to signals, etc. may be considered as "Controller".

PyQt also provides a set of predefined "universal" models which can be subclassed or used directly if you need only simple functionality from the model, like QStringListModel, QStandardItemModel, etc. And there are also models which can talk to databases directly, like QSqlTableModel.

焚却相思 2024-08-17 04:19:06

以下是关于 Qt 架构如何为应用程序提供模型视图设计的官方详细指南的链接

http://doc.qt.io/qt-5/model-view-programming.html

在Qt中,视图和控制器结合在一起,因此可以使用模型-视图框架来设计应用程序。

模型与数据源通信,提供接口
对于架构中的其他组件。的性质
通信取决于数据源的类型和方式
模型已实施。视图从模型中获取模型索引;
这些是对数据项的引用。通过提供模型索引
对于模型,视图可以从数据源检索数据项。
在标准视图中,委托呈现数据项。当一个项目
被编辑后,委托直接使用与模型进行通信
模型索引。

...

模型、视图和委托使用信号和槽相互通信

Here's a link to the official and detailed guide on how Qt architecture offers Model-View design to an application

http://doc.qt.io/qt-5/model-view-programming.html

In Qt, view and controller are combined, therefore an app can be designed using Model-View framework.

The model communicates with a source of data, providing an interface
for the other components in the architecture. The nature of the
communication depends on the type of data source, and the way the
model is implemented. The view obtains model indexes from the model;
these are references to items of data. By supplying model indexes to
the model, the view can retrieve items of data from the data source.
In standard views, a delegate renders the items of data. When an item
is edited, the delegate communicates with the model directly using
model indexes.

...

Models, views, and delegates communicate with each other using signals and slots

红墙和绿瓦 2024-08-17 04:19:06

我通常使用 peewee 来创建模型,这是一个示例:

from peewee import *


from pathlib import Path
import datetime
import os

root_path = Path(__file__).resolve().parent
db_path = os.path.join(root_path, 'db', 'db.sqlite3')
db = SqliteDatabase(db_path)

class BaseModel(Model):
    class Meta:
        database = db

class Categorie(BaseModel) :
    name = CharField(max_length=64, unique=True, null=False)

class Product(BaseModel) :
    code = CharField(max_length=20, unique=True, null=False)
    name = CharField(max_length=128, unique=True, null=False)
    mark = CharField(max_length=128, unique=True, null=False)
    model = CharField(max_length=128, unique=True, null=False)
    category = ForeignKeyField(Categorie, null=False)
    image_path = CharField(max_length=1024, null=True)

I usually use peewee to create the model, here is an example:

from peewee import *


from pathlib import Path
import datetime
import os

root_path = Path(__file__).resolve().parent
db_path = os.path.join(root_path, 'db', 'db.sqlite3')
db = SqliteDatabase(db_path)

class BaseModel(Model):
    class Meta:
        database = db

class Categorie(BaseModel) :
    name = CharField(max_length=64, unique=True, null=False)

class Product(BaseModel) :
    code = CharField(max_length=20, unique=True, null=False)
    name = CharField(max_length=128, unique=True, null=False)
    mark = CharField(max_length=128, unique=True, null=False)
    model = CharField(max_length=128, unique=True, null=False)
    category = ForeignKeyField(Categorie, null=False)
    image_path = CharField(max_length=1024, null=True)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文