[Qt-interest] How to write QT applications?! please don't refers to QT websites.

Oliver.Knoll at comit.ch Oliver.Knoll at comit.ch
Fri Mar 20 16:59:06 CET 2009


Alexandre Beraud wrote on Friday, March 20, 2009 11:42 AM:
> ...
> You want to read this:
> http://doc.trolltech.com/4.3/signalsandslots.html Regards,

I thing the OP's question was not so much about how Qt signals work, but rather of to "organise" them. I understand this involves the question "Who connects signals and slots" and "Where are the slots implemented.

My answer can't possibly be complete, but here's some "best practise" approaches which I have evolved over the past few years. I think they hold very well in practise, since I rarely changed them. Let's assume a fairly simple dialog (about 10 interaction widgets such as QComboBox, QTextEdit, QCheckbox or so):

- "Don't rely on someone else to connect the signals from you - connect yourself!"

That's a real world advice which goes in analogy with "Do your own homework" (if possible) :) What that means: a class which expects to receive some signals should connect itself to those signals. In my life that is traditionally a method called frenchConnection():

class MyDialog : public QDialog {
  Q_OBJECT

public:
  MyDialog(MyModel &myModel, QWidget *parent)
  : QDialog(parent), m_myModel(myModel) { 
    this->frenchConnection();
  }

private:
  // some "Model" class which this dialog ("View") represents
  MyModel &m_myModel;

  void frenchConnection() {
    // this is pretty obvious that the "dialog implementation" connects
    // to its own gui elements
    this->connect(ui->okButton, SIGNAL(activated()),
                         this, SLOT(handleOkButtonClicked()));
    // but here it is also THIS dialog implementation which connects
    // itself to the signal sent by some "external" class (MyModel)
    this->connect(&m_myModel, SIGNAL(changed()),
                         this, SLOT(handleModelChanged()));
    
private slots:
  void handleOkButtonClicked() { ... } // note: you could also use "auto-connect feature": on_OkButton_clicked()
  void handleModelChanged() {  ... }
};

I know that one can also connect any signal to ANY slot (also PRIVATE slots!). But by making my handleXY() slots private I also indicate that this class itself is responsible for connection. Off course it would also be possible to connect MyModel with MyDialog "externally", as in:

void SomeClass::initialise() {
  MyModel *myModel = new MyModel();
  MyDialog *myDialog = new MyDialog(*model);
  connect->(model, SIGNAL(changed()),
                 myDialog, SLOT(handleModelChanged()));

The later solution is often the only possibility if you did not write the MyDialog implementation yourself (for example because you are using some 3rd party components).

- "Minimise the usage of signals as much as possible"

In any case: try to minimise the number of places in your code where you connect signals. And "localise" those places (as in the frenchConnection() approach above). Nothing is more annoying to try to debug performance problems because of signals "flying around" and calling who knows what slots (which on its turn could emit other signals which on its turn call yet other slots, and so on... you get the idea. These problems only come apparent at runtime and are very hard to analyse statically, especially if the 

And especially nasty: "circular signal updates": a slot A emits a signal which triggers a slot B which emits another signal which (at some point) triggers slot A again...

- "Externalise the slot implementation"

Especially for QMainWindows with lots of menu entries (typically QActions) it probably makes sense to introduce programmatically extra classes which handle the signal handling, because the number of slots grows almost exponentially within such a class, making it hard to read and maintain.

A natural approach could be to have "Controllers" for each menu (generally: for each "group of actions"):


  MyMainWindow
  FileMenuControl
  EditMenuControl
  ...
  HelpMenuControl

In the above setting the MyMainWindow "uses" the FileMenuControl etc. The slot implementation for each File->Open, File->New etc action is then in the respective "Controller" (FileMenuControl). When you offer context-menus which are "dynamically populated" (depending on which item the users clicks, for example) then it could also make sense to have a "ContextMenuControl" which is called whenever a right-click is done on some item, creating and handling the corresponding context menu.

Like this you again "localise" the signal/slot handling.

// c'tor
MyMaindWindow::MyMainWindow() {
  MyModel *myModel = new MyModel();
  // the control might need to know the "model" as well
  m_fileMenuControl = new FileMenuControl(myModel );
  // add a "File" menu to this main window - the 'fileMenuControl' then also reacts to "File actions"
  m_fileMenuControl->createMenu(this);
  ... etc.
}


Summary:

- "Connect locally" (so you have control who connects to what - also makes it easier to detect "double/tripple/etc. connects)
  or "Move the implementation of the slot as close as possible to the point where the signal is emitted")
- Use signals as sparse as possible
- "Externalise" (move into separate class or implementation file) your slot logic, if it grows and grows


Hope this give you some design ideas :)


Cheers, Oliver
-- 
Oliver Knoll
Dipl. Informatik-Ing. ETH
COMIT AG - ++41 79 520 95 22



More information about the Qt-interest-old mailing list