qt

meta-object system

  1. The QObject class provides a base class for objects that can take advantage of the meta-object system.
  2. The Q_OBJECT macro inside the private section of the class declaration is used to enable meta-object features, such as dynamic properties, signals, and slots.
  3. The Meta-Object Compiler (moc) supplies each QObject subclass with the necessary code to implement meta-object features. (when meet a Q_OBJECT macro in .cpp, poduce another .cpp)

qobject_cast(just like dynamic_cast, without RTTI)

QObject *obj = new MyWidget;
QWidget *widget = qobject_cast<QWidget *>(obj);

property system

Q_PROPERTY(bool focus READ hasFocus)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
		Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
Q_PROPERTY(qreal spacing MEMBER m_spacing NOTIFY spacingChanged)
Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
...
signals:
void colorChanged();
void spacingChanged();
void textChanged(const QString &newText);

private:
QColor m_color;
qreal m_spacing;
QString m_text;
  • A READ accessor function is required if no MEMBER variable was specified. It is for reading the property value. Ideally, a const function is used for this purpose, and it must return either the property's type or a const reference to that type. e.g., QWidget::focus is a read-only property with READ function, QWidget::hasFocus().
  • A WRITE accessor function is optional. It is for setting the property value. It must return void and must take exactly one argument, either of the property's type or a pointer or reference to that type. e.g., QWidget::enabled has the WRITE function QWidget::setEnabled(). Read-only properties do not need WRITE functions. e.g., QWidget::focus has no WRITE function.
  • A MEMBER variable association is required if no READ accessor function is specified. This makes the given member variable readable and writable without the need of creating READ and WRITE accessor functions. It's still possible to use READ or WRITE accessor functions in addition to MEMBER variable association (but not both), if you need to control the variable access.
  • A RESET function is optional. It is for setting the property back to its context specific default value. e.g., QWidget::cursor has the typical READ and WRITE functions, QWidget::cursor() and QWidget::setCursor(), and it also has a RESET function, QWidget::unsetCursor(), since no call to QWidget::setCursor() can mean reset to the context specific cursor. The RESET function must return void and take no parameters.
  • A NOTIFY signal is optional. If defined, it should specify one existing signal in that class that is emitted whenever the value of the property changes. NOTIFY signals for MEMBER variables must take zero or one parameter, which must be of the same type as the property. The parameter will take the new value of the property. The NOTIFY signal should only be emitted when the property has really been changed, to avoid bindings being unnecessarily re-evaluated in QML, for example. Qt emits automatically that signal when needed for MEMBER properties that do not have an explicit setter.
  • A REVISION number or REVISION() macro is optional. If included, it defines the property and its notifier signal to be used in a particular revision of the API (usually for exposure to QML). If not included, it defaults to 0.
  • The DESIGNABLE attribute indicates whether the property should be visible in the property editor of GUI design tool (e.g., Qt Designer). Most properties are DESIGNABLE (default true). Valid values are true and false.
  • The SCRIPTABLE attribute indicates whether this property should be accessible by a scripting engine (default true). Valid values are true and false.
  • The STORED attribute indicates whether the property should be thought of as existing on its own or as depending on other values. It also indicates whether the property value must be saved when storing the object's state. Most properties are STORED (default true), but e.g., QWidget::minimumWidth() has STORED false, because its value is just taken from the width component of property QWidget::minimumSize(), which is a QSize.
  • The USER attribute indicates whether the property is designated as the user-facing or user-editable property for the class. Normally, there is only one USER property per class (default false). e.g., QAbstractButton::checked is the user editable property for (checkable) buttons. Note that QItemDelegate gets and sets a widget's USER property.
  • The BINDABLE bindableProperty attribute indicates that the property supports bindings, and that it is possible to set and inspect bindings to this property via the meta object system (QMetaProperty). bindableProperty names a class member of type QBindable, where T is the property type. This attribute was introduced in Qt 6.0.
  • The presence of the CONSTANT attribute indicates that the property value is constant. For a given object instance, the READ method of a constant property must return the same value every time it is called. This constant value may be different for different instances of the object. A constant property cannot have a WRITE method or a NOTIFY signal.
  • The presence of the FINAL attribute indicates that the property will not be overridden by a derived class. This can be used for performance optimizations in some cases, but is not enforced by moc. Care must be taken never to override a FINAL property.
  • The presence of the REQUIRED attribute indicates that the property should be set by a user of the class. This is not enforced by moc, and is mostly useful for classes exposed to QML. In QML, classes with REQUIRED properties cannot be instantiated unless all REQUIRED properties have been set.

A property can be read and written using the generic functions QObject::property() and QObject::setProperty(), without knowing anything about the owning class except the property's name. In the code snippet below, the call to QAbstractButton::setDown() and the call to QObject::setProperty() both set property "down".

QPushButton *button = new QPushButton;
QObject *object = button;

button->setDown(true);
object->setProperty("down", true);

qt object

Qt Objects should be treated as identities, not as values. Identities are cloned, not copied or assigned, and cloning an identity is a more complex operation than copying or assigning a value. Therefore, QObject and all subclasses of QObject (direct or indirect) have their copy constructor and assignment operator disabled.

object tree

When you create a QObject with another object as parent, it's added to the parent's children() list, and is deleted when the parent is.

int main()
{
QPushButton quit("Quit");
QWidget window;

quit.setParent(&window);
...
}

注意在 stack 上的情况, 这种情况 child 被析构两次是会异常的.

信号与槽

Tips

  • parent object tree, setparent
  • Qt::QueuedConnection
  • MOC meta object compiler
  • Q_DISABLE_COPY, QObject 必须 unique, 以memory 位置确定
  • QDeleteAll + clear
  • QScopePointer, QSharedPointer

Qt 入门

what is Qt

Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。基本上,Qt 同X Window上的 Motif,Openwin,GTK 等图形界 面库和 Windows 平台上的MFC,OWL,VCL,ATL 是同类型的东西。

how to use Qt

安装

Qt 官网

在 Qt 官网下载 Qt 及其 IDE QtCreator

截屏2021-04-24 下午7.38.05

用 MaintenanceTool 下载对应版本和额外的组件.

用 QtCreator 进行开发

简单介绍

进入 QtCreator 后新建项目可以看到以下选项

截屏2021-04-24 下午7.43.14

Qt Widgets Application 是新建窗口项目

Qt Console Application 是新建命令行项目

下面以窗口项目进行演示

窗口项目

目录结构

截屏2021-04-24 下午7.46.59

下面依次介绍一个 Qt 项目的目录

  • .pro

    QT += core gui 等是代表模块的加载, 如果需要新的模块比如编写 socket 网络内容需要添加如QT += network

    之后的目录代码如SOURCES += main.cpp mainwindow.cpp是项目文件配置, 一般是自动补充的.

  • Headers/

    保存项目头文件(.h)

  • Sources/

    保存项目文件(.cpp)

  • Forms/

    设计界面

  • Other/

    其他文件

代码主要保存在 Sources 和 Headers 文件夹中, ui 设计在 Forms 文件夹中

main 文件与类

main.cpp

#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);//新应用
MainWindow w; //新窗口
w.show(); //窗口显示
return a.exec(); //应用执行
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
Q_OBJECT
//继承 QObject 类的宏, 在 Qt 中, 所有组件都继承 QObject 类

public:
MainWindow(QWidget *parent = nullptr);
//构造函数
~MainWindow();
//析构函数

private:
Ui::MainWindow *ui;
//ui 对象指针
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//建立 ui
}

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

按钮实例

下面演示实现一个按钮功能

  1. 在 mainwindow.ui 中拖入 qpushbutton, 在右侧属性栏可以对组件属性进行编辑(比如文字, 大小等) 截屏2021-04-24 下午8.05.27

  2. 如果要对 button 实现点击功能, 可以右键按钮->转到槽->clicked(), 就会添加对应的槽函数(可以理解为点击后执行的函数) 具体实现机理请查阅 "Qt 信号与槽"

    void MainWindow::on_pushButton_clicked()
    {

    }
  3. 如果要用代码实现对这个按钮进行操作, 比如设置显示文字, 可以参考如下代码

    void MainWindow::on_pushButton_clicked()
    {
    ui->pushButton->setText("text");
    }
    //这个例子是为了说明, 如果要访问这个按钮需要使用 ui 指针
    //注意有的时候 ui->后可能没有对应组件, 一般是新组件未加载, 可以编译一边来解决

以上就是基本的 Qt 使用方式.