对于使用Qt开发桌面软件的小伙伴,这里博主整理了一些常用技术,若大家有兴趣可以一一进行深入研究与探索。
严格来说,本篇博文并不能作为一篇学习技术的博客,更多的是对日常开发中高频使用的技术进行总结。由于博主技术及认知有限,若有笔误的地方请多多指正!
本篇博客整体上分为八个部分,分别是:常用工具类、常用控件类、事件处理与事件机制、QML开发、Qt应用开发框架与MVC思想、消息机制、动态库调用、插件机制 。
其中的每一部分若深入研究与讲解,那么将是非常浩瀚的,在此遵循以技术展望为主,常用技术介绍为辅的方式,为大家介绍使用Qt开发GUI界面常用到的一些技术。
常用工具类主要有:
1, 字符串处理类 QString ,主要用于字符串的处理,Qt对该类封装了非常多的API供大家调用,有兴趣的小伙伴可参阅Qt官方文档。根据实际需求查询即可,没必要逐一记住。
下面是官方对于Qstrign类的答题介绍:
QString stores a string of 16-bit QChars, where each QChar corresponds to one UTF-16 code unit. (Unicode characters with code values above 65535 are stored using surrogate pairs, i.e., two consecutive QChars.)
Unicode is an international standard that supports most of the writing systems in use today. It is a superset of US-ASCII (ANSI X3.4-1986) and Latin-1 (ISO 8859-1), and all the US-ASCII/Latin-1 characters are available at the same code positions.
Behind the scenes, QString uses implicit sharing (copy-on-write) to reduce memory usage and to avoid the needless copying of data. This also helps reduce the inherent overhead of storing 16-bit characters instead of 8-bit characters.
In addition to QString, Qt also provides the QByteArray class to store raw bytes and traditional 8-bit '\0'-terminated strings. For most purposes, QString is the class you want to use. It is used throughout the Qt API, and the Unicode support ensures that your applications will be easy to translate if you want to expand your application's market at some point. The two main cases where QByteArray is appropriate are when you need to store raw binary data, and when memory conservation is critical (like in embedded systems).
中文含义: QString存储一个16位QChars字符串,其中每个QChars对应一个UTF-16代码单元。(代码值大于65535的Unicode字符使用代理项对存储,即两个连续的QChars。)
Unicode是一种国际标准,支持当今使用的大多数书写系统。它是US-ASCII(ANSI X3.4-1986)和Latin-1(ISO 8859-1)的超集,所有US-ASCII/Latin-1字符在相同的代码位置可用。
在幕后,QString使用隐式共享(写时复制)来减少内存使用并避免不必要的数据复制。这也有助于减少存储16位字符而不是8位字符的固有开销。
除了QString,Qt还提供了QByteArray类来存储原始字节和传统的8位“\0”结尾字符串。对于大多数目的,QString是您要使用的类。它在QtAPI中使用,Unicode支持确保了如果您想在某个时候扩展应用程序的市场,您的应用程序将易于翻译。QByteArray适用的两种主要情况是需要存储原始二进制数据时,以及内存保护至关重要时(如嵌入式系统)。
部分API截图:
2, 容器类
这里所说的容器类是指Qt对基本数据结构进行封装好的一些类,主要有如下几个:
3, 基类QObject
QObject在Qt库中是所有类的基类,很多的技术都是基于该类而实现的,如自动回收内存、信号与槽机制、事件处理机制等,这个类是必须要熟悉和掌握的。
官方描述:
QObject is the heart of the Qt Object Model. The central feature in this model is a very powerful mechanism for seamless object communication called signals and slots. You can connect a signal to a slot with connect() and destroy the connection with disconnect(). To avoid never ending notification loops you can temporarily block signals with blockSignals(). The protected functions connectNotify() and disconnectNotify() make it possible to track connections.
QObjects organize themselves in object trees. When you create a QObject with another object as parent, the object will automatically add itself to the parent's children() list. The parent takes ownership of the object; i.e., it will automatically delete its children in its destructor. You can look for an object by name and optionally type using findChild() or findChildren().
Every object has an objectName() and its class name can be found via the corresponding metaObject() (see QMetaObject::className()). You can determine whether the object's class inherits another class in the QObject inheritance hierarchy by using the inherits() function.
When an object is deleted, it emits a destroyed() signal. You can catch this signal to avoid dangling references to QObjects.
QObjects can receive events through event() and filter the events of other objects. See installEventFilter() and eventFilter() for details. A convenience handler, childEvent(), can be reimplemented to catch child events.
Last but not least, QObject provides the basic timer support in Qt; see QTimer for high-level support for timers.
Notice that the Q_OBJECT macro is mandatory for any object that implements signals, slots or properties. You also need to run the Meta Object Compiler on the source file. We strongly recommend the use of this macro in all subclasses of QObject regardless of whether or not they actually use signals, slots and properties, since failure to do so may lead certain functions to exhibit strange behavior.
All Qt widgets inherit QObject. The convenience function isWidgetType() returns whether an object is actually a widget. It is much faster than qobject_cast
Some QObject functions, e.g. children(), return a QObjectList. QObjectList is a typedef for QList
中文含义:
QObject是Qt对象模型的核心。该模型的核心特征是一种非常强大的无缝对象通信机制,称为信号和槽。您可以使用connect()将信号连接到槽,并使用disconnect()断开连接。为了避免永不结束的通知循环,您可以使用blockSignals()暂时阻止信号。受保护的函数connectNotify()和disconnectNotify(()使跟踪连接成为可能。
QObjects在对象树中组织自己。当您创建另一个对象作为父对象的QObject时,该对象将自动将自己添加到父对象的children()列表中。父对象拥有对象的所有权;即,它将自动删除其析构函数中的子级。您可以通过名称查找对象,也可以选择使用findChild()或findChildren()键入。
每个对象都有一个objectName(),其类名可以通过相应的metaObject()找到(请参见QMetaObject::className())。通过使用inherits()函数,可以确定对象的类是否继承QObject继承层次结构中的另一个类。
删除对象时,它会发出destroyed()信号。您可以捕获此信号以避免对QObjects的悬空引用。
QObjects可以通过event()接收事件并过滤其他对象的事件。有关详细信息,请参阅installEventFilter()和eventFilter()。可以重新实现方便处理程序childEvent()来捕获子事件。
最后,QObject在Qt中提供了基本的计时器支持;有关计时器的高级支持,请参阅QTimer。
请注意,Q_OBJECT宏对于实现信号、插槽或财产的任何对象都是必需的。您还需要在源文件上运行元对象编译器。我们强烈建议在QObject的所有子类中使用此宏,无论它们是否实际使用信号、插槽和财产,因为如果不这样做,可能会导致某些函数表现出奇怪的行为。
所有Qt小部件都继承QObject。方便函数isWidgetType()返回对象是否实际上是小部件。它比qobject_cast<QWidget*>(obj)或obj->inherits(“QWidget”)快得多。
某些QObject函数,例如children(),返回QObjectList。QObjectList是QList<QObject*>的typedef。
4, 进程、线程相关类
进程相关类是QProcess,线程相关类是QThread,具体使用还是直接翻阅官方API即可。
关于进程,来看一段官方的描述:
QProcess then enters the Starting state, and when the program has started, QProcess enters the Running state and emits started().
QProcess allows you to treat a process as a sequential I/O device. You can write to and read from the process just as you would access a network connection using QTcpSocket. You can then write to the process's standard input by calling write(), and read the standard output by calling read(), readLine(), and getChar(). Because it inherits QIODevice, QProcess can also be used as an input source for QXmlReader, or for generating data to be uploaded using QNetworkAccessManager.
When the process exits, QProcess reenters the NotRunning state (the initial state), and emits finished().
The finished() signal provides the exit code and exit status of the process as arguments, and you can also call exitCode() to obtain the exit code of the last process that finished, and exitStatus() to obtain its exit status. If an error occurs at any point in time, QProcess will emit the errorOccurred() signal. You can also call error() to find the type of error that occurred last, and state() to find the current process state.
中文含义:
QProcess然后进入Starting状态,当程序启动时,QProcess进入Running状态并发出started()。
QProcess允许您将进程视为顺序I/O设备。您可以像使用QTcpSocket访问网络连接一样对进程进行写入和读取。然后,可以通过调用write()写入进程的标准输入,并通过调用read()、readLine()和getChar()读取标准输出。由于QProcess继承了QIODevice,因此它也可以用作QXmlReader的输入源,或用于生成要使用QNetworkAccessManager上载的数据。
当进程退出时,QProcess重新进入NotRunning状态(初始状态),并发出finished()。
finished()信号提供进程的退出代码和退出状态作为参数,您还可以调用exitCode()获取最后一个完成的进程的退出码,并调用exitStatus()获取其退出状态。如果在任何时间点发生错误,QProcess将发出errorOccurred()信号。您还可以调用error()查找上次发生的错误类型,并调用state()查找当前进程状态。
使用示范:
QObject *parent;
...
QString program = "./path/to/Qt/examples/widgets/analogclock";
QStringList arguments;
arguments << "-style" << "fusion";
QProcess *myProcess = new QProcess(parent);
myProcess->start(program, arguments);
常用API截图:
关于线程,我们也来看一段官方的解释及描述:
A QThread object manages one thread of control within the program. QThreads begin executing in run(). By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.
You can use worker objects by moving them to the thread using QObject::moveToThread().
中文含义:
QThread对象管理程序中的一个控制线程。QThread在run()中开始执行。默认情况下,run()通过调用exec()启动事件循环,并在线程内运行Qt事件循环。
通过使用QObject::moveToThread()将工作对象移动到线程中进行多线程操作。
常用API截图:
使用示例:
任务类:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
线程类:
class WorkerThread : public QThread
{
Q_OBJECT
void run() override {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
5, 网络通信类
网络通信类主要是QNetworkAccessManager ,来看一段官方的描述:
The Network Access API is constructed around one QNetworkAccessManager object, which holds the common configuration and settings for the requests it sends. It contains the proxy and cache configuration, as well as the signals related to such issues, and reply signals that can be used to monitor the progress of a network operation. One QNetworkAccessManager instance should be enough for the whole Qt application. Since QNetworkAccessManager is based on QObject, it can only be used from the thread it belongs to.
Once a QNetworkAccessManager object has been created, the application can use it to send requests over the network. A group of standard functions are supplied that take a request and optional data, and each return a QNetworkReply object. The returned object is used to obtain any data returned in response to the corresponding request.
中文含义:
网络访问API是围绕一个QNetworkAccessManager对象构建的,该对象保存其发送的请求的通用配置和设置。它包含代理和缓存配置,以及与此类问题相关的信号,以及可用于监视网络操作进度的回复信号。一个QNetworkAccessManager实例应该足够用于整个Qt应用程序。由于QNetworkAccessManager基于QObject,因此只能从它所属的线程中使用。
创建QNetworkAccessManager对象后,应用程序可以使用它通过网络发送请求。提供了一组标准函数,它们接受请求和可选数据,每个函数都返回一个QNetworkReply对象。返回的对象用于获取响应于相应请求而返回的任何数据。
使用示例:
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished,
this, &MyClass::replyFinished);
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
另外还有关于各种通信协议的类均在Qt Network模块里面,具体使用时,直接查阅官方API即可!
6, 数据库相关类
数据库操作相关类主要在QSql模块中定义,该模块下有很多实现类,用于支持不同的数据库,
其中常用的类是QSqlDatabase类,来看一段官方的描述:
The QSqlDatabase class provides an interface for accessing a database through a connection. An instance of QSqlDatabase represents the connection. The connection provides access to the database via one of the supported database drivers, which are derived from QSqlDriver. Alternatively, you can subclass your own database driver from QSqlDriver. See How to Write Your Own Database Driver for more information.
Create a connection (i.e., an instance of QSqlDatabase) by calling one of the static addDatabase() functions, where you specify the driver or type of driver to use (depending on the type of database) and a connection name. A connection is known by its own name, not by the name of the database it connects to. You can have multiple connections to one database. QSqlDatabase also supports the concept of a default connection, which is the unnamed connection. To create the default connection, don't pass the connection name argument when you call addDatabase(). Subsequently, the default connection will be assumed if you call any static member function without specifying the connection name.
中文含义:
QSqlDatabase类提供了通过连接访问数据库的接口。QSqlDatabase的实例表示连接。该连接通过一个受支持的数据库驱动程序提供对数据库的访问,这些驱动程序源自QSqlDriver。或者,您可以从QSqlDriver子类化自己的数据库驱动程序。有关详细信息,请参阅如何编写自己的数据库驱动程序。
通过调用一个静态addDatabase()函数创建连接(即QSqlDatabase的实例),在其中指定要使用的驱动程序或驱动程序类型(取决于数据库类型)和连接名称。一个连接是通过它自己的名称而不是通过它所连接的数据库的名称来识别的。可以有多个连接到一个数据库。QSqlDatabase还支持默认连接的概念,即未命名连接。要创建默认连接,请在调用addDatabase()时不要传递连接名称参数。随后,如果在不指定连接名称的情况下调用任何静态成员函数,则将假定默认连接。
使用示例:
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("acidalia");
db.setDatabaseName("customdb");
db.setUserName("mojito");
db.setPassword("J0a1m8");
bool ok = db.open();
常用API:
在Qt5及之后的版本中GUI相关的模块被拆分为GUI、Widget。GUI模块提供桌面系统集成,事件处理,OpenGL/Vulkan,基础图像,字体等功能。Widget则为所有控件的基类,这里所说的常用控件类实际上就是围绕Widget展开的。
Qt中的控件分为以下几个大类:
其中,每个类里面均有不同的实现控件,如下:
在具体使用时,上图中的每一个控件均有对应的实现类,具体使用时,查询API文档即可,也是不需要逐一全部记住。
在Qt在处理事件时,经过四个阶段: 事件发出--->事件过滤--->事件分发--->事件处理 ;
事件发出阶段主要是指事件的产生源,如按钮点击事件,窗口缩放事件,鼠标、键盘事件,系统消息事件等;
事件过滤阶段负责把当前事件进行分类,便于下一步的分发;
事件分发阶段负责把事件分发到各处,如点击事件分发给窗口管理类;
事件处理阶段由事件接收这负责完成,到此事件结束,系统将继续进行下一轮的循环扫描;
事件处理机制底层采用函数回调来完成,相对于信号与槽机制效率要高一点,但是就使用及维护层面来说,信号与槽更有优势,要不然也不会成为Qt的特色功能了。
关于事件处理,这里推荐一篇写得比较好的博客,链接如下:
Qt之事件处理机制_qt事件机制_风间琉璃•的博客-CSDN博客Qt 是一个基于 C++ 的框架,主要用来开发带窗口的应用程序。使用的基于窗口的应用程序都是基于事件,其目的主要是用来实现回调(因为只有这样程序的效率才是最高的)。所以在 Qt 框架内部提供了一些列的事件处理机制,当窗口事件产生之后,事件会经过:事件派发 -> 事件过滤->事件分发->事件处理四个阶段。Qt 窗口中对于产生的一系列事件都有默认的处理动作,如果有特殊需求就需要在合适的阶段重写事件的处理动作。事件(event)是由系统或者 Qt 本身在不同的场景下发出的。当用户按下 / 移动鼠标、敲下键盘,或者https://blog.csdn.net/qq_53144843/article/details/127144955?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167808468816800192215547%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167808468816800192215547&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-2-127144955-null-null.blog_rank_default&utm_term=Qt%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6&spm=1018.2226.3001.4450
Qt 引入了一种声明式脚本语言,称为 QML(Qt Meta Language),作为 C++ 语言的一种替代。而 Qt Quick 就是使用 QML 构建的一套类库。 QML 是一种基于 JavaScript 的声明式语言。
QML开发具有实时预览效果,绘制的界面非常美观漂亮,开发效率较高,不过在与C++交互方面稍微麻烦了一些,相较于QWidget交互而言,需要多写很多代码。
未来的桌面开发,QML有很大的发展空间,目前主要应用领域是嵌入式触摸屏方面,如车载触摸屏、HMI、手机、平板上面。
这里关于QML的详细使用就不一一展开,如下贴上Qt官方教程链接,有兴趣的小伙伴可专心研究与应用,后面会专门出一章博客,讲解QML的使用。
Qt QML 6.2.7https://doc.qt.io/qt-6.2/qtqml-index.htmlQt Quick 6.2.7https://doc.qt.io/qt-6.2/qtquick-index.html
在项目的实际开发过程中,通常由多人协作完成,每一个人可能仅负责单个模块的开发,这是开发框架就显得非常重要了。
开发框架的作用就是把一个大型程序的开发进行分层、分模块,当各模块完成后,集成到层级,当所有模块和层级完成后,框架负责最后的整合,最后项目才编译发布。
常用的分层思想是MVC (Model模型-View视图-Controller控制),模型负责整合业务逻辑,视图负责显示业务情况及与用户交互,控制负责模型层的具体实现。这是大概的简要介绍,但是请相信一点: 越是优秀的架构思想越简单,不要想复杂啦,真正的复杂是如何使用MVC思想合理地运用到项目上面。
下面推荐一篇优秀的博客:
MVC与三层架构理解_mvc三层架构_iqqcode的博客-CSDN博客1. JSP的发展2. MVC思想 优缺点3. 三层架构 为什么使用三层 三层优缺点4. MVC与三层架构的区别https://blog.csdn.net/weixin_43232955/article/details/104963392?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167809922216800227452913%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167809922216800227452913&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~top_positive~default-1-104963392-null-null.blog_rank_default&utm_term=MVC&spm=1018.2226.3001.4450
在Qt中实现MVC的方式叫MVD,也就是使用代理代替了控制层,如下图所示:
在日常程序开发中,消息的传递非常重要,这里大概整理了几个消息传递的方式:
说了这么多通信方式,在此重点说一下Qt信号与槽的通信方式,这是在Qt中传递消息非常重要的一个机制。
关于信号:
一个信号的申明不能有返回值类型且不需要定义。信号可以携带参数,参数数量无限制。 只有申明信号的类才有权发射信号;
一个信号可以与多个槽进行绑定; 与之绑定的槽函数里面可以继续发射其他信号;
多个信号可以与一个槽进行绑定; 与之绑定的槽函数里面可以继续发射其他信号;
关于槽:
槽就是普通函数,需要申明和定义。
一个槽可以与多个信号进行绑定; 每个槽函数里面可以继续发射其他信号;
多个信号可以与一个槽进行绑定; 与之绑定的槽函数里面可以继续发射其他信号;
图示:
更多示例参见他人博客:
Qt --- 信号与槽_小雪菜本菜的博客-CSDN博客信号与槽概述信号与槽是 Qt 框架引以为豪的机制之一。所谓信号与槽,实际就是观察者模式(发布-订阅模式)。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。 信https://blog.csdn.net/weixin_60569662/article/details/123428410?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167810179716800186585835%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167810179716800186585835&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~top_click~default-3-123428410-null-null.blog_rank_default&utm_term=Qt%E4%BF%A1%E5%8F%B7%E4%B8%8E%E6%A7%BD&spm=1018.2226.3001.4450
所谓动态库,就是他人或自己写好的程序,进过编译之后生产的程序包,主要作用是便于其他程序共享使用,同时动态库只有在使用时才会被加载。在实际开发中,有许多通用的模块或功能快,可以封装成动态库的形式发给他人使用,或者多个程序业务相同时,也可以采用共享库的方式。
Qt编写的程序在发布时,其依赖包也是采用动态库的方式进行发布。
这里不做深入演示,找到了一篇演示博客供大家参考。如下:
QT使用——外部动态库的调用(基于Windows)_qt 调用动态库_Yzy3089的博客-CSDN博客最近使用QT编辑界面,需要用到QT调用动态库这部分功能。网上参考了许多帖子,完成了该部分功能,现将整体流程重新记录,以便大家参考。如有疑问,欢迎交流!首先,使用VS生成一个动态库(具体如何生成,这里不做过多介绍,请参考其他帖子),动态库生成后,后续将使用以下三个文件:其次,打开QT生成一个Qt控制台程序(Qt Console Application,任何一个程序流程都差不多),界面如下:在对应的project文件下,把头文件(.H)和静态库(.lib)文件放入文件夹中,如下所示:在QT界面中https://blog.csdn.net/qq_36883399/article/details/109429909?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167810228616782427482167%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167810228616782427482167&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-2-109429909-null-null.blog_rank_default&utm_term=Qt%E5%8A%A8%E6%80%81%E5%BA%93%E8%B0%83%E7%94%A8&spm=1018.2226.3001.4450
插件实际上就是动态库的实际应用,Qt
定义一个了公共接口类作为插件开发标准,在开发插件需要继承该类并且实现对应的虚方法,每个插件作为独立子工程编译后生成对应的动态库。
有兴趣的小伙伴可以自己开发一个插件哦!
这里也找了一篇博客供大家参考:
Qt插件机制及加载流程_qt 插件_XX風的博客-CSDN博客简介插件实际上就是一个个动态库,动态库在不同平台下后缀名不一样,比如在 Windows下以.dll结尾,Linux 下以.so结尾。那么开发插件其实就是开发一个动态库,该动态库能够很好的加载进主程序、访问主程序资源、和主程序之间进行通信Qt Creator插件理解起来其实很简单,定义一个接口类作为基类,其他插件需要继承该类实现对应的虚方法,每个插件作为独立子工程编译后生成对应的动态库主函数加载每个插件对象,然后转化为对应插件实例QPluginLoader loader(pluginNhttps://blog.csdn.net/chengfenglee/article/details/124948808?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167810273516782425197547%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167810273516782425197547&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-1-124948808-null-null.blog_rank_default&utm_term=Qt%E6%8F%92%E4%BB%B6%E6%9C%BA%E5%88%B6&spm=1018.2226.3001.4450
至此,关于Qt开发桌面软件常用技术介绍完了,大家若有看法或想法关注我,私信交流哦。
上一篇博客:
Qt6教程之三(6) 界面自绘与绘图类_爱折腾的业余程序员的博客-CSDN博客界面绘制与绘图类介绍!https://blog.csdn.net/XiaoWang_csdn/article/details/129342923
下一篇博客:
Qt6教程之三(8 )多进程、进程间通讯和调度_爱折腾的业余程序员的博客-CSDN博客本篇博客主要介绍在Qt中的多进程、进程间通讯及进程间调度的基本使用!https://blog.csdn.net/XiaoWang_csdn/article/details/129369363?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22129369363%22%2C%22source%22%3A%22XiaoWang_csdn%22%7D