Model/View/Presenter and Qt?

Here’s a question for Qt hackers:

Does Qt implement the Model/View/Presenter design pattern?

The short answer is (drumroll): it depends on some stuff. Let’s dive into it and give you a real answer.

First of all, I am no Model/View/Presenter expert. I’ve only read what little information is available online, and I’ve never implemented an application with it (though it turns out that I’ve been using it for years on accident). Bottom line: Please correct me.

If you search the Qt documentation, you won’t find any mention of this design pattern (although the more prevalent Model/View/Controller does make many appearances).

Before we start, we should define Model/View/Presenter. The MSDN and Wikipedia summarize it thus:

  1. It is a multi-tiered design pattern that helps separate display logic from business logic in a testable way.
  2. The View is kept as thin and ignorant as possible
  3. It enables better automated unit testing (and helps prevent relying on awkward UI test runners)
  4. It moves lots of logic out of the View and into the Presenter (things like input validation for example)
  5. The Presenter should be common to multiple views (though I’m not totally sure on this one — please correct me)
  6. Methods in the Presenter should not return a value (i.e., void) and should take no arguments.

Let’s look at a typical Qt application. I like to use Qt Designer to design as much of my user interface as possible. Doing so, my source code tends to look like this:

Qt veterans should be able to identify the boxes in this graphic quickly, but for those of you who don’t work in Qt every day, let me explain.

Your Data Model

The data model is a class (or set of classes) that provides access to your app’s data. It emits signals to tell you when its data has changed (like if another app changed the data), and provides slots you can call to change the data yourself. You may have, for example, a list of User objects in your app, and maybe they are all contained within a UserModel class. When a user’s phone number changes, your model may emit a signal called phoneNumberChanged(). If a new user is created, the UserModel would emit something like userAdded(User*) and likewise userRemoved(User*) when a user goes away.

Your Widget Subclass

When using Designer, you have to choose a QWidget-derived class from which your widget will inherit. Your widget may be a dialog, a main window, or even a nested widget inside some scroll box. Your widget subclass will usually connect to the data model’s signals (userAdded(), userRemoved(), and friends). Its job is to notice when the data model changes, and represent that on the screen by manipulating the view. It is also responsible for taking user input and pushing it into the model (like when the user wants to change a User‘s phone number, for example).

Generated UI Class

When you create and save a form in Qt Designer, it gets saved with a .ui extension. This file actually contains a bunch of XML that describes how you laid stuff out on the screen. Qt provides a tool called uic that takes that XML file and produces C++. uic stands for User Interface Compiler. The code that it generates is the view. It only does one thing: Draw crap on the screen, and tell you when the user does stuff. It is an extremely thin UI layer. So thin in fact, that it cannot implement any business logic at all. It’s simply not possible. The code is generated and unalterable (unless you’re an idiot and think it’s okay to go hack that code after it’s generated — hey, I’ve done stupid stuff too).

Now let’s apply some labels to our diagram:

I believe the widget subclass actually does conform to all the rules of a Presenter, even though it doesn’t conventionally bear that name. Let’s look at the rules again and see if it fits the bill:

  1. Separates display logic from business logic: Yes
  2. The View is thin and ignorant: Yes
  3. Enables automated unit testing: Yes (you can write unit tests against your widget subclass quite easily)
  4. It moves UI-related logic out of the View into the Presenter: Yes In fact, the View can do almost no input validation at all (unless you specify inputMasks in Designer, and note that QValidators are instantiated in the widget subclass).
  5. The Presenter should be common to multiple views: No Qt could allow this, but most people just don’t do it (perfectly possible, but I’ll discount it because it is not used in practice or recommended in the docs).
  6. Methods in the Presenter should not return a value: Yes Your widget subclass’s slots have to be void (and even if you do specify a return value, Qt ignores it anyway, so Qt actually enforces this requirement).

Conclusion

Qt can most definitely implement Model/View/Presenter, and in fact, the Qt documentation recommends exactly that, though they never use the term “Presenter”. You should probably also read up on the Model/View/Presenter’s latest development, since some argue that the term ought to be retired and replaced with new terminology, which coincidentally fits even better with Qt conventions.

What do you think? Does Qt actually implement this Design Pattern? It’s pretty important to recognize this stuff, or we can end up screwing up pretty bad.

5 comments to “Model/View/Presenter and Qt?”

You can leave a reply or Trackback this post.
  1. http://Steven%20Kerr says: -#1

    If your Model does not know about your View, than you definitely have a Presenter, not a Controller. In the Model/View/Controller pattern, the Controller is a layer of indirection going from your View to your Model. The data flow looks like this: View->Controller->Model. But, a change in the Model would notify the view directly: Model->View. When the Controller also becomes a layer of indirection going from the Model to the View (Model->Controller->View), the Controller is then promoted to being a Presenter. Not only does it handle user input, but it now also “presents” data. Some will go so far as to keep their Controller and Presenter as separate classes. I personally have not seen a good enough gain in this to warrant the bloat.
    It looks to me that QT will promote unit testing, which is a big part of the pattern; but, the inability to replace a view without altering the presenter in any way does bring QT’s adherence to the pattern into question for me.

  2. Steve,

    By your definition of MVC, Qt is definitely closer to MVP. Any updates from the model to the view must go through the widget subclass (what I’m tentatively calling the presenter). You *can* connect signals from the model directly to the view, which is convenient, but it seems that most applications apply a little, ahem, presentation logic to the data first.

    For example, if you have a QLabel in your view, and a model signal that emits textChanged(QString), you *can* connect your model’s textChanged() signal directly to your QLabel’s setText() slot. This is super convenient, but in practice, you almost always want to do stuff to the data before displaying it (like trim() it or capitalize it or apply some html formatting to it), necessitating a presentation layer between the model and view.

    On another note, I am very interesting in seeing a Qt MVP implementation that has a totally decoupled view and presenter. Qt seems to encourage the two to be tightly coupled and 1:1 instead of 1:N in relationship.

    –Dave

  3. If you consider your widget subclass to be part of the view instead of the presenter and add another layer of indirection to be the presenter, then you implement the MVP pattern correctly.

  4. Another failing of this approach is that the presenter is not injectable and therefore not unit testable. In other words, the “ui” instance on which the presenter operates can not be faked in a unit test. This is one of the advantages to using model/view/presenter and Qt fails to make this possible without some serious work on the part of the developer.

  5. If you’re interested in full realization MVP in Qt – you may see that link:
    http://habrahabr.ru/blogs/qt_software/107698/
    It’s in russian, but in this article contains diagrams and archive with examples.