Liebe Leserinnen, liebe Leser,

in diesem Beitrag möchte ich das zeigen, wie der alte Codestil eines Qt Widgets-Programms zu einem neuen Stil konvertiert werden kann. Das ist nicht zu kompliziert zu machen, aber ich habe beide diese Stile im Einsatz gefunden. Trotz der alten Stil ist verstehbarer, braucht das mehr Tippen. Der neuere Stil braucht andererseits weniger Tippen, aber das ist vernebelter und das ist schwere zu bestimmen, woraus die Klassen stammen. Um einfacher zwischen diese Stile konvertieren zu können, hier gibt’s einen kleinen Vergleich:

Der unten zu sehende Codeausschnitt zeigt die Header-Datei des alten Stils, in der die Vordeklaration der MainWindow Klasse innerhalb des Ui-Namensraums zu finden ist. Es gibt auch einen privaten Zeiger in der Klasse, der auf die Vordeklaration zeigt. Der Rest des Codes ist ganz standard, der sich mit dem Stil nicht ändert, deswegen werde ich eine Diskussion darüber vermeiden.

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// mainwindow.h with old style ui access

#include <QMainWindow>
// ...

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    
    // ...
    
private:
    Ui::MainWindow *ui;
    // ...
};

Wenn man die Quelldatei untersucht, wird man die Include von “ui_mainwindow.h” sofort bemerken, die nirgendwo in den Projektverzeichnissen besteht, und trotzdem ist das eine gültige Include. Das ist so, weil diese Header-Datei nach dem Anfang der Compilierverfahrens mithihilfe der .ui-Datei von Qt UIC erzeugt wird.

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// mainwindow.cpp file using old style ui access

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    
    ui->graphicsView->setScene(&scene);
    // ...
}

MainWindow::~MainWindow()
{
    delete ui;
}

Die andere bemerkenswerte Sache ist die Erstellung der MainWindow Instanz in dem Konstruktor mithihilfe des new-Operators und nicht leeren Destruktors, die das berühmte RAII Idiom ist. Alle diese Sachen sind erforderlich, um ein von .ui-Datei definiertes Fenster zu erstellen. Um auf beliebige Widgets im Fenster zugreifen zu können, soll man den ui-Zeiger und das zugehörige Zugriffsoperator verwenden, wie es in dem Beispiel oben zu sehen ist.

Das ist klar, aber die Nutzung von ui->xyz für alle Widgetzugriffe könnte nach einee Weile ganz mühsam werden. Glücklicherweise ist es möglich, diese Mühsamkeit mithilfe des neuen Stils (der heutzutage in Qt Creator der Standardstil ist) verringert werden. Um das zu machen, sollte man die obengenannte “ui_mainwindow.h” in die Header-Datei umziehen, und die MainWindow Vordeklaration und ui-Zieger komplett entfernen. Dann sollte die Klasse von der Ui::MainWindow Klasse auf eine private Weise vererben, aber man sollte bei der Reihenfolge der Vererbung sehr vorsichtig sein: Die Reihenfolge mag egal sein, aber als früher in einem meiner Beiträge darüber diskutiert wurde, ist das gar nicht egal. Man soll das gewährleisten, dass unsere Klasse erstens von QMainWindow vererbt. Der Rest der Header-Datei bleibt unverändert, also hier ist die Datei nach allen Änderungen:

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// mainwindow.h for accessing ui elements without ui pointer

#include "ui_mainwindow.h"
#include <QMainWindow>
// ...


class MainWindow : public QMainWindow, private Ui::MainWindow
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    
    // ...
    
private:
    // ...
};

Die Quelldatei behebt sich von da an selbst: Weil es keinen ui-Zeiger gibt, kann new Ui::MainWindow im Konstruktor entfernt werden, und der Destruktor kann auch geleert werden. Letztendlich sollten alle ui und Zugriffsoperatoren auch entfernt werden, und jetzt können auf alle Widgets durch den Namen zugegriffen werden, genauso als ob sie Mitglieder der Klasse wären (das ist wahr, weil sie von Ui::MainWindow vererbt wurden). Man sollte darauf zu achten, dass die Mitglieder nach dem Entfernen aller ui Instanzen Zeiger bleiben, deswegen sollte man den Zugriffsoperator für Zeiger weiter verwenden, genauso wie es in der setScene() Funktion unten zu sehen ist.

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// mainwindow.cpp file without ui pointer.

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    setupUi(this);
    
    graphicsView->setScene(&scene);
    // ...
}

MainWindow::~MainWindow()
{
}

Das ist alles für Heute, vielen Dank fürs Lesen.