Qt learning notes

Posted by ahsan on Wed, 09 Feb 2022 01:19:27 +0100

1, Start with Hello World

Cited examples

#include <QApplication>
#include <QLabel>

int main(int argc, char* argv[])
{
    //Creating a QApplication instance is essential
    QApplication app(argc, argv);

    //Create a QLabel instance and assign Hello, world text to the constructor so that this text can be displayed on QLabel
    QLabel label("Hello, world");
    //Call the show() method to display it
    label.show();

    //Call app Exec() to start the event loop. Imagine that if the event loop is not started, the main function will exit immediately and the QLabel object cannot be displayed all the time
    return app.exec();
}

Supplement:

#include <QApplication>
#include <QLabel>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    //This is not recommended
    QLabel* label = new QLabel("Hello, world");
    label->show();

    return app.exec();
}

2, Signal slot

The so-called signal slot is actually the observer mode. When an event occurs, for example, a button detects that it has been clicked, it sends a signal. This kind of transmission has no purpose, similar to broadcasting. If an object is interested in the signal, it will use the connect function, which means that it processes the signal with one of its own functions (called slots). That is, when the signal is sent, the connected slot function will be called back automatically. This is similar to observer mode: when an event of interest occurs, an operation will be automatically triggered. (it is mentioned here that the signal slot of Qt uses additional processing, which is not the implementation of GoF's classic observer mode.)

example:

#include <QApplication>
#include <QPushButton>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
	
	//The button is called QPushButton in QT
    QPushButton firstButton("Quit");
    
    QObject::connect(&firstButton, &QPushButton::clicked, &QApplication::quit);
    firstButton.show();
    
    return app.exec();
}

QObject::connect():
This function allows us to connect the signals and slots provided by the system

The most commonly used general form of this function:

connect(sender, signal, receiver, slot);

sender: the object that signals
Signal: the signal sent by the sending object
receiver: the object that receives the signal
slot: the function that the receiving object will call automatically after receiving the signal

After the sender object sends a signal, it will automatically call the slot function of the receiver object

In Qt, there are five overloads:

//Handle signal and slot as strings
QMetaObject::Connection connect(const QObject *, const char *,
                                const QObject *, const char *,
                                Qt::ConnectionType);

//Each function can be regarded as a subclass of QMetaMethod, which can be used for type comparison
QMetaObject::Connection connect(const QObject *, const QMetaMethod &,
                                const QObject *, const QMetaMethod &,
                                Qt::ConnectionType);

//It can be seen that the receiver is missing. this pointer is used as the receiver
QMetaObject::Connection connect(const QObject *, const char *,
                                const char *,
                                Qt::ConnectionType) const;

//PointerToMemberFunction, pointer to member function
QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
                                const QObject *, PointerToMemberFunction,
                                Qt::ConnectionType)

//Functor type, which can accept static function, global function and Lambda expression
QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
                                Functor);		

The signal slot requires the parameters of the signal and slot to be consistent. The so-called consistency means that the parameter types are consistent. If it is inconsistent, it is allowed that the parameters of the slot function can be less than those of the signal. Even so, the order of those parameters of the slot function must be consistent with the first few of the signal. This is because you can choose to ignore the data transmitted by the signal in the slot function (that is, the parameters of the slot function are less than those of the signal), but you can't say that the signal doesn't have this data at all. You have to use it in the slot function (that is, the parameters of the slot function are more than those of the signal, which is not allowed).

Connect a signal to a Lambda expression:

  • Look back after learning Lambda expressions

2, Custom signal slot

The classic observer mode usually reports the examples of paper and subscribers when explaining examples. There is a Newspaper class Newspaper and a Subscriber class Subscriber. A Subscriber can subscribe to a Newspaper. In this way, the Subscriber can be notified immediately when there is new content in the Newspaper. In this example, the observer is Subscriber and the observed is Newspaper. In the classic implementation code, the observer will register itself in a container of the observed (such as subscriber.registerTo(newspaper)). When the observed person has any change, it will actively traverse the container and notify each observer in turn (newspaper.notifyAllSubscribers()).

QT signal slot mechanism is used to realize the above mechanism:

//main.cpp
#include <QCoreApplication>

#include "newspaper.h"
#include "reader.h"

int main(int argc, char* argv[])
{
    QCoreApplication app(argc, argv);

    Newspaper newspaper("Newspaper A");
    Reader reader;
    QObject::connect(&newspaper, &Newspaper::newPaper,
        &reader, &Reader::receiveNewspaper);
    newspaper.send();

    return app.exec();
}
//Reader.h
#include <QObject>
#include <QDebug>

class Reader : public QObject
{
    Q_OBJECT
public:
    Reader() {}

    //Slot function is an ordinary member function. As a member function, it will be affected by public, private and protected;
    void receiveNewspaper(const QString& name)
    {
        qDebug() << "Receives Newspaper: " << name;
    }
};
//Newspaper.h
#include <QObject>

//Only classes that inherit from QObject have the ability of signal slot
class Newspaper : public QObject
{
    //For all object classes, add Q in the first line_ Object macro
    //In particular, this macro needs to be placed in the header file   
    Q_OBJECT
public:
    Newspaper(const QString& name) :
        m_name(name)
    {
    }

    void send()
    {
        //emit is an extension of QT to c + +. It is a keyword and a macro
        //Meaning of emit: Send a newPaper() signal. In order to let the receiver know who sent the signal, the name of the newspaper is passed to the signal as a parameter
        //When the receiver connects the signal, the actual value is obtained through the slot function to complete a transfer of data from the sender to the receiver
        emit newPaper(m_name);
    }

//The module lists signals. Signals are function names, and the return value is void. Parameters are the data that the class wants to let the outside world know. There is no need to add any implementation
signals:
    void newPaper(const QString& name);

private:
    QString m_name;
};

3, Qt module

Qt5 module is divided into two parts:
Essentials Modules: available on all platforms
Add on modules: it is based on the basic modules and can be introduced selectively

See: Qt module

4, MainWIndow

QMainWindow is the Qt predefined main window.
Main window: the top-level window of an ordinary application. For example, when using a browser, the main window is the browser window

Under vs2019:

#include <QApplication>
#include <QMainWindow>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    QMainWindow win;
    win.show();

    return app.exec();
}   

effect:

Although I can't see it, it is said that this window is divided into these parts:

5, MainWindow add specific functions

QAction class: represents the action of the window
This class abstracts public actions and is used in the menu bar and toolbar
Including: icons, menu text, shortcut keys, status bar text, floating help and other information

code:

//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget* parent = 0);
    ~MainWindow();

private:
    void open();

    QAction* openAction;
};

#endif
//mainwindow.cpp
#include <QAction>
#include <QMenuBar>
#include <QMessageBox>
#include <QStatusBar>
#include <QToolBar>

#include "mainwindow.h"

//This line indicates that the constructor with parameters of the parent class QMainWindow needs to be called before the constructor of MainWindow
MainWindow::MainWindow(QWidget* parent) :
    QMainWindow(parent)
{
    //Set window main title
    //tr() function: a function used for Qt internationalization
    setWindowTitle(tr("Main Window"));

    //Create an openAction object on the heap and pass in an image, a text and this pointer to its constructor

    //QIcon: the incoming value is a string, corresponding to a path in the Qt resource file, which means finding resources from the resource file
    //The picture format supported by Qt is png. Pictures in other formats are imported into JPG, and GIF requires plug-in support

    //The second parameter: the text value is preceded by an &, which means it will become a shortcut key. Note that the File in the screenshot has an underscore
    openAction = new QAction(QIcon(":/images/image1"), tr("&Open..."), this);
    //Use the setShortcuts() function to specify the shortcut key of QAction
    //QKeySequence defines many built-in shortcut keys, such as Open below. Adding shortcut keys in this way will automatically define corresponding shortcut keys according to different platforms
    openAction->setShortcuts(QKeySequence::Open);
    //setStatusTi() realizes the prompt generated in the status bar below when the user clicks the Action with the mouse
    openAction->setStatusTip(tr("Open an existing file"));
    //Connect the triggered() signal of QAction with the open() signal of MainWindow class
    connect(openAction, &QAction::triggered, this, &MainWindow::open);

    //menuBar(), toolBar() and statusBar() are three built-in functions of QMainWindow, which are used to create corresponding parts of the window

    //Add a FIle menu to the menu bar and add openAction to the menu
    QMenu* file = menuBar()->addMenu(tr("&File"));
    file->addAction(openAction);

    //Add a new toolbar and add openAction to this menu
    QToolBar* toolBar = addToolBar(tr("&File"));
    toolBar->addAction(openAction);

    statusBar();
}

MainWindow::~MainWindow()
{
}

void MainWindow::open()
{
    QMessageBox::information(this, tr("Information"), tr("Open"));
}
//main.cpp
#include <QApplication>
#include "mainwindow.h"

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    MainWindow win;
    win.show();

    return app.exec();
}

6, Resource Management

reference resources:
resource management

7, Object model

Qt "extends c + +:
The original object model of C + + is stretched under certain circumstances and can not meet the requirements of GUI interface. Therefore, before the C + + compiler compiles the Qt source program, Qt first uses moc (Meta Object Compiler) to preprocess the Qt source code. This processing is released before the C + + preprocessor works.
The signal function can only be compiled by standard C + + after being processed by moc.

moc adds some features to standard C + +:

The signal slot mechanism is used to solve the communication between objects, which we have known and can be considered as one of the most obvious features of Qt;
The properties of the object can be queried and designed;
Powerful event mechanism and event filter;
We briefly introduced the context based string translation mechanism (internationalization), that is, the tr() function;
Complex timer implementation, which is used to embed task integration that can be accurately controlled in event driven GUI;
The hierarchical queryable object tree provides a natural way to manage object relationships.
The QPointer is automatically set to 0 after the object is destructed to prevent wild pointers;
A dynamic transformation mechanism that can cross library boundaries.

These features can be obtained by inheriting the QObject class.
For more information: object model

8, Layout manager

Component positioning: the so-called GUI interface is the superposition of a pile of components. We should put the components in the right place. There are two positioning methods: absolute positioning and layout positioning.

Absolute positioning: give component coordinates and length and width
Problem: when the user changes the window size, the component will not respond appropriately

Layout positioning: managed by a special manager to adapt to the adjustment of size and position

Introduce two new components:
QSpinBox: only numbers can be entered. It is a step button with up and down arrows
QSlider: slide bar with slider

Layout manager:

QHBoxLayout: layout from left to right in horizontal direction;
QVBoxLayout: layout from top to bottom according to the vertical direction;
QGridLayout: Layout in a grid, similar to HTML table;
QFormLayout: according to the table layout, each line is preceded by a paragraph of text followed by a component (usually an input box), similar to the HTML form;
QStackedLayout: stacked layout, which allows us to stack several components in the Z-axis direction, which can form the page by page effect of the wizard.

#include <QApplication>
#include <Qwidget>
#include <QSpinBox>
#include <QSlider>
#include <QObject>
#include <QHBoxLayout>

int main(int argc, char* argv[])
{
	QApplication app(argc, argv);

	QWidget window;
	window.setWindowTitle("Enter your age");

	QSpinBox* spinBox = new QSpinBox(&window);
	QSlider* slider = new QSlider(Qt::Horizontal, &window);
	//After creating an instance, use setRange to manage its range
	spinBox->setRange(0, 130);
	slider->setRange(0, 130);

	QObject::connect(slider, &QSlider::valueChanged, spinBox, &QSpinBox::setValue);
	//If it is written directly in the following way, the compiler will report an error:
	//QObject::connect(spinBox, &QSpinBox::valueChanged, slider, &QSlider::setValue);
	void (QSpinBox:: * spinBoxSignal)(int) = &QSpinBox::valueChanged;
	QObject::connect(spinBox, spinBoxSignal, slider, &QSlider::setValue);
	spinBox->setValue(35);

	QHBoxLayout* layout = new QHBoxLayout;
	layout->addWidget(spinBox);
	layout->addWidget(slider);
	window.setLayout(layout);

	window.show();
	
	return app.exec();
}

9, Menu bar, toolbar and status bar

In actual development, QMainWindow is usually only used as the main window, and more dialog boxes only use QDialog class, which lacks some convenient functions, such as menuBar() and toolBar().

QMenuBar: a class that represents a menu
addMenu(): you can add a menu for it. You can only provide a string as a parameter
&: you can create a shortcut key for the menu
addAction(): add an activity

QToolBar: class representing toolbar
addToolBar():

10, Dialog box

1. Introduction to dialog box

Dialog box: a top-level window that appears at the top of the program to realize short-term tasks or concise interaction
Implementation of dialog box with QDialog class

QDialog (and its subclasses, as well as all classes of Qt::Dialog type) has additional explanations for its parent pointer: if the parent is NULL, the dialog box will be used as a top-level window, otherwise it will be used as a child dialog box of its parent component (at this time, its default position is the center of the parent). The difference between top-level windows and non top-level windows is that top-level windows will have their own location in the taskbar, while non top-level windows will share the location of their parent components.

//Note whether the parent pointer (this in this case) has any impact on the QDialog instance
void MainWindow::open()
{
    QDialog dialog(this);
    dialog.setWindowTitle(tr("Hello,dialog"));
    dialog.exec();
}

Qt supports modal and non modal dialog boxes:
Modal dialog box: input from other windows in the same application
Modeless dialog box:

Qt supports two levels of modes:
Application level and window level
Application level: when the modal dialog box appears, the user must interact with the dialog box first, not with other windows in the application.
This window can only interact with other windows in the application level

QDialog::exec(): implement application level mode
QDialog::open(): implement window level mode
QDialog::show(): implement modeless

Special attention:
show() will not block the thread. If the dialog box is displayed, the function will return immediately and the code will continue to execute
To solve this problem, create a dialog on the heap:

void MainWindow::open()
{
    QDialog *dialog = new QDialog;
    dialog->setWindowTitle(tr("Hello, dialog!"));
    dialog->show();
}

Supplementary reference: dialog box

2. Data transfer between dialog boxes

Data transfer:
The data interaction between the dialog box and the main window is very important

Dialog boxes are divided into modal and non modal, which are different from the data interaction between the main window

For the modal dialog box displayed by exec(), we can get the data value directly from the object of the dialog box after the exec() function.

void MainWindow::open()
{
    QDialog dialog(this);
    dialog.setWindowTitle(tr("Hello, dialog!"));
    dialog.exec();
    qDebug() << dialog.result();
}

qDebug() is similar to std::cout
exec() starts an event loop, and the code is blocked here. Since the exec() function does not return, the following result() function will not be executed. Until the dialog box closes and the exec() function returns, we can get the data of the dialog box.

If we set the dialog property to WA_DeleteOnClose, then when the dialog box is closed and the object is destroyed, we can't use this method to obtain data. In this case, we can consider using the parent pointer to build the dialog box to avoid setting WA_DeleteOnClose attribute; Or use another way.

QDialog::exec() has a return value, which is QDialog::Accepted or QDialog::Rejected
Conventional writing:
It depends on whether the user clicks "OK" or "Cancel"

QDialog dialog(this);
if (dialog.exec() == QDialog::Accepted) {
    // do something
} else {
    // do something else
}

In the modeless dialog box, the QDialog::show() function will return immediately. If we write the same, it is impossible to obtain the data entered by the user.
Because the show() function will not block the main thread, show() returns immediately. Before the user has time to input, he has to execute the following code. Of course, there will be no correct result.

Using the signal slot mechanism:
Since the modeless dialog can call QDialog::accept() or QDialog::reject() or the more general QDialog::done() function when closing, we can send signals here. In addition, if we can't find a suitable signal sending point, we can rewrite the QDialog::closeEvent() function to send a signal here. Connect to this signal in the window that needs to receive data (here is the main window). Similar code snippets are as follows:

void UserAgeDialog::accept()
{
    emit userAgeChanged(newAge); // newAge is an int
    QDialog::accept();
}

// in main window:
void MainWindow::showUserAgeDialog()
{
    UserAgeDialog *dialog = new UserAgeDialog(this);
    connect(dialog, &UserAgeDialog::userAgeChanged, this, &MainWindow::setUserAge);
    dialog->show();
}

// ...

void MainWindow::setUserAge(int age)
{
    userAge = age;
}

3. Standard dialog box QMessageBox

QFileDialog
File dialog box:
QString getOpenFileName(): select to open a file
QStringList getOpenFileNames(): select to open multiple files
QString getSaveFileName(): select to save a file
QString getExistingDirectory(): select an existing directory
QUrl getOpenFileUrl(): select to open a file and select a remote network file

QcolorDialog
Color dialog box:
QColor getColor(): select a color

QFontDialog
Font dialog box:
QFont getFont(): select font

QinputDialog
Input dialog box:
QString getText(): enter single line text
int getlnt(): enter an integer
double getDouble(): enter a floating point number
QString getltem(): select input from a drop-down list box
QString getMultiLineText(): enter a multiline string

QMessageBox
Message box:

  • StandardButton information(): information prompt dialog box
  • StandardButton question(): ask and get the confirmation dialog box
  • Standardbutton waiting(): warning information prompt dialog box
  • StandardButton critical(): error message prompt dialog box
  • void about(): About dialog box for setting custom information
  • void aboutQt(): dialog box about Qt

QMessageBox details:

if (QMessageBox::Yes == QMessageBox::question(this,
                                              tr("Question"),
                                              tr("Are you OK?"),
                                              QMessageBox::Yes | QMessageBox::No,
                                              QMessageBox::Yes)) {
    QMessageBox::information(this, tr("Hmmm..."), tr("I'm glad to hear that!"));
} else {
    QMessageBox::information(this, tr("Hmmm..."), tr("I'm sorry!"));
}

The static function of QMessageBox class has the advantages of convenient use and obvious disadvantages: it is very inflexible. We can only use a few simple forms. In order to customize the details of QMessageBox, we must use the attribute setting API of QMessageBox. If we want to make a dialog box asking whether to save, we can use the following code:

QMessageBox msgBox;
msgBox.setText(tr("The document has been modified."));
msgBox.setInformativeText(tr("Do you want to save your changes?"));
msgBox.setDetailedText(tr("Differences here..."));
msgBox.setStandardButtons(QMessageBox::Save
                          | QMessageBox::Discard
                          | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::Save:
    qDebug() << "Save document!";
    break;
case QMessageBox::Discard:
    qDebug() << "Discard changes!";
    break;
case QMessageBox::Cancel:
    qDebug() << "Close document!";
    break;
}

A detailedText, that is, details, is used. When you click the details button, the dialog box can automatically display more information.

New syntax for deep Qt5 signal slot

Topics: C++ Qt