初学Qt的一点小笔记

总字数:2379字,预计阅读时间 03分 57秒。

最近的大作业需要用 C/C++的技术栈实现一个图形化界面,Qt作为C++图形化框架久负盛名,正好借着这个写大作业的机会学习一下这个应用广泛的框架。

Qt的安装

现在比较新版本的Qt貌似都不支持下载离线安装包了,受制于网络环境,在线安装的体验并不是很好。为了比较顺利的完成安装,使用老套路——国内镜像。清华大学就提供了Qt在线安装的镜像,具体的使用说明参照镜像的这篇帮助,在下载之前记得注册一个Qt的账号。

版本的选择

现在Qt最新的版本已经来到了Qt6,虽然现在Qt5仍然十分的流行,网上大部分的资料与教程都是采用Qt5写成的,我最后还是选选择了Qt6,理由主要有以下三点:

  • 工程并不是很复杂,而且开发的周期很长,即使在中间遇到了一些新版本的玄学问题,我也有比较充足的时间来解决或者避开问题
  • Qt6开始完全转向cmake管理项目,而我的这个大作业“恰好”就是采用cmake管理的
  • Qt6是新版本,以后开发的时间还很长,稳定的开发环境还不是我现在需要的,学习的人还是应该积极的跟踪新技术(当然只是我自己的想法)

编译器的选择

我在Windows平台上进行开发,虽然在之前学习过程中MinGW64 GCC是我更常用的编译器,但是他毕竟是一个移植的编译器,而不是“官方”的编译器,我选择了MSVC作为这次项目的编译器。

这里还有一个理由是我正在大力推进WSL在我日常开发中的使用,我正在把我所有的C/C++项目都迁移到WSL中进行开发,在Windows中我已经不保留MinGW64编译器了。

预处理的使用

在这次Qt开发中我用到了两个预处理器——mocuic,前者服务于Qt的元对象系统(Meta Object System),后者负责与将Qt Designer生成了*.ui文件转换为编译器可以编译的ui_*.h文件。这里重点说说前者,Qt的元对象系统是Qt信号和槽机制的基础,所有需要用到信号槽的类都需要继承QObject这个基类,同时在类里申明Q_OBJECT这个宏,而且,这个头文件还得被moc预处理器预处理为moc_*.cpp之后才能被编译器所编译,否则就会报LNK2001连接错误,提示有三个函数无法找到定义,

@errorMsg.obj : error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall errorMsg::metaObject(void)const " (?metaObject@errorMsg@@UBEPBUQMetaObject@@XZ)

errorMsg.obj : error LNK2001: unresolved external symbol "public: virtual void * __thiscall errorMsg::qt_metacast(char const *)" (?qt_metacast@errorMsg@@UAEPAXPBD@Z)

errorMsg.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall errorMsg::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@errorMsg@@UAEHW4Call@QMetaObject@@HPAPAX@Z)@

在我这次的工程中,需要以下设置才能让moc预处理器正确工作:

  • CMakeLists.txt文件中需要定义set(CMAKE_AUTOMOC ON)
  • 在包含这个头文件的地方需要将头文件的名称改为预处理之后的名称moc_*.cpp,如下图所示

Qt Designer

在一开始我对于这个软件的使用是比较迷茫的,没有搞明白哪些内容是在Designer中完成的,哪些内容是在代码中完成的,在代码中是如何访问控件的。

这里需要说明的是,我是用CLion作为IDE开发C/C++项目,在这里Qt Designer是作为外部工具存在的。

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    ui = new Ui::MainWindow;
    ui->setupUi(this);
}

那个名叫Ui的命名空间让我迷茫了一阵子,在那个空间中也有一个和当前创建的UI类名字相同的类,我以为这是单例模式之类的高级设计模式,在研读了几篇博客,查看了UIC处理器生成的头文件之后,我才意识到这是两个处在不同命名空间但名字相同的类,在Ui命名空间中的那个类就是*.ui文件中定义了那个界面,可以通过这个指针来访问我们在Qt Designer中定义的那些控件。

Qt Property Animation

QPropertyAnimationQt自己实现的一个简单实用的动画框架,在这次开发中,我使用这个框架实现了对QGraphicsItem这个对象的动画。这里主要的问题是,QGraphicsItem这个类并没有继承QObject,然而QPropertyAnimation这个动画框架所作用的对象必须是一个继承自QObject的对象,而且需要实现动画的属性必须注册Q_PROPERTY,根据文档的说明,我定义了这样的一个类:

class BusItem: public QObject, public QGraphicsPixmapItem
{
    Q_OBJECT
    Q_PROPERTY(QPointF pos READ pos WRITE setPos)
public:
    explicit BusItem(const QPixmap& pixmap);
};

这个类多重继承了QObjectQGraphicsPixmapItem,这样既可以被QPropertyAnimation所作用,也可以像正常的QGraphicsItem一样被添加进QGrphicsScene并设置各种属性。

Q_PROPERTY这个宏给Qt的类型系统注册了一个类型为QPointF名叫pos的变量,这个变量的读通过调用pos()函数来实现,这个变量的写通过调用setPos()函数来实现,这个变量也就是我需要设计动画的变量。

这句代码给我的感觉像是在C#中的setget两个访问器。

2021 - 2024 © Ricardo Ren, 由 .NET 8.0.10 驱动。