36

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?

TrebledJ
  • 8,713
  • 7
  • 26
  • 48
aluuu
  • 443
  • 1
  • 6
  • 8

3 Answers3

47

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!

dman
  • 10,406
  • 18
  • 102
  • 201
cmaynard
  • 2,852
  • 2
  • 24
  • 34
  • 3
    +1, `don't set by direct access, your controller shouldn't get that deep`: why not mention the possibility of `self._title=title; self._year=year; ...`? – mlvljr Feb 19 '10 at 19:42
  • Why would manually editing your view cause it to be not separate from your model & control? – Kemeia Dec 13 '17 at 05:20
  • It doesn't cause it, but not doing it **does** prevent it – cmaynard Dec 14 '17 at 06:16
9

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.

abbot
  • 27,408
  • 6
  • 54
  • 57
  • 2
    Note that the Model/View framework in Qt is restricted to recursive tables (although the views support mainly lists, rows, tables and recursive rows (tree)). It is possible to build generic MVC apps in PyQt/Qt which doesn't use Qt's ModelView-framework, if your data has a different structure. (IMHO, forcing stuff into Qt's ModelView isn't the best idea always...) – Macke Jan 20 '11 at 12:10
  • indeed~ I still don't quite get the real idea of Qt's Model/View framework w/o Controller. – Drake Guan Jan 27 '11 at 07:56
  • It makes code reuse better. It is quite common when you use exactly the same model class or even instance with different views (e.g. with a QTreeView in one dialog window, and with a QComboBox in another). – abbot Jan 28 '11 at 12:44
  • @abbot: In my opinion, Qt's models would "be part" of some Controller (through composition or aggregation) in the MVC structure. The Model of the MVC structure shouldn't have any reference to the Qt framework. In other words, Qt's models are just data providers for the UI items. Am I oversimplifying them? – freitass Sep 01 '12 at 20:10
  • @freitass: yes, you are oversimplifying in my opinion. To better understand why try connecting the same Qt-based model class to several view classes, some through intermediate model-classes like `QSortFilterProxyModel`. – abbot Sep 02 '12 at 09:16
  • @abbot: I'm not sure if I get your point. As far as I know, Qt-based models are just a way to manage data at a layer immediately behind the UI. Regarding your suggestion, connecting the same model to multiple views seems like just a way to keep data consistent (and also to avoid data replication). Don't you think that the "intelligence" to sort and filter data `(QSortFilterProxyModel)` is strongly related to the UI? – freitass Sep 03 '12 at 19:01
  • @abbot: Just to make sure we are speaking the same language, [this](http://www.oracle.com/technetwork/articles/javase/index-142890.html) (Figure 4) is the MVC structure I have in mind, where there is no communication between Model and View. – freitass Sep 03 '12 at 19:02
  • 1
    @freitass: Qt models are quite normal models in this sense, whether they are proxies to your underlying "true" model classes or not (think `QAbstractItemModel` subclassed in your code vs. `QStandardItemModel` or `QStringListModel`). I would say that Qt views however are not "pure" views, they are a mix of a view and controller. Also I'd like to notice that this purity/terminology discussion is quite irrelevant to actual software development and is a matter of abstraction level of your preference ;) – abbot Sep 03 '12 at 20:31
7

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

Jayesh
  • 51,787
  • 22
  • 76
  • 99
  • +1 for giving a source to an authoritative, very helpful article instead of only saying your own opinions. I did appreciate abbot and Maynard's replies, but this is what really helped me understand how Qt deals with this so I'm not fighting against the framework. – labyrinth Nov 17 '22 at 23:42