真●完美●封装 Qt 界面、事件循环为DLL供调用(完整实现)

环境:VS2017,Qt5.12.10(安装了msvc2017组件,并添加环境变量)
需求和概览:
Qt的信号与槽和便捷的界面编写非常有利于UI开发,很多时候希望把已经编写好的界面和逻辑加载到现有工程中,这时选择DLL加载是很敏捷的做法。
众所周知的,只有在构建了 QApplicationQCoreApplication(以下以QApplication为例) 后才能使用 QWidget 和信号与槽以及众多其他需要事件循环的功能。
在一个独立的Qt程序中,QApplication*一般是在main中构建的:

#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    A a;
    return app.exec();
}

然而要封装成DLL就需要在类里构建QApplication,形如QtAddOn_1.h

#pragma once
#include <QtWidgets/QDialog>
#include <QtWidgets/QApplication>
class QtAddOn_1:public QDialog
{
	Q_OBJECT
private:
	QApplication* app;
public:
	explicit QtAddOn_1();
};

形如QtAddOn_1.cpp

#include "pch.h"
#include "QtAddOn_1.h"
QtAddOn_1::QtAddOn_1() {
	int argc;
	char ** argv = nullptr;
	app = new QApplication(argc, argv);

	//做一些Qt能做的所有事

	app->exec();
}
#include"moc_QtAddOn_1.app"

形如DLL导出文件Modle.h

#pragma once
#include "QtAddOn_1.h"
namespace QtModles {
	__declspec(dllexport) void initQtModle_1() {
		QtAddOn_1 modle;
	}
}

形如DLL导出文件Modle.cpp

#include "pch.h"
#include "Modle.h"

moc_QtAddOn_1.app是什么?是编译Qt程序时必要的文件。怎么生成的?下面来看完整实现。

完整实现
一、预备知识
1.C++调用Qt库
2.C++导出Dll并使用
二、实现
创建一个DLL程序并添加Qt依赖,添加需求和概览所述 的QtAddOn_1.hQtAddOn_1.cpp, Modle.h, Modle.cpp。编译。


(懒惰是进步的源动力,不是吗)


给这个DLL添加一个界面和信号与槽。
各文件做如下修改:
Modle.h

#pragma once
#include "QtAddOn_1.h"
namespace QtModles {
	__declspec(dllexport) void initQtModle_1() {
		QtAddOn_1 modle;
	}
}

QtAddOn_1.h

#pragma once
#include <QtWidgets/QDialog>
#include <QtWidgets/QApplication>
class QtAddOn_1:public QObject
{
	Q_OBJECT
private:
	QApplication* app;
public:
	explicit QtAddOn_1();
private slots:
	void endEventLoop();
};


Model.cpp

#include "pch.h"
#include "Modle.h"

QtAddOn_1.cpp

#include "pch.h"
#include "QtAddOn_1.h"
#include "UI.h"
QtAddOn_1::QtAddOn_1() {
	int argc;
	char ** argv = nullptr;
	app = new QApplication(argc, argv);
	Client client;
	connect(&client, &Client::endQt, this, &QtAddOn_1::endEventLoop);
	client.show();
	app->exec();
}
void QtAddOn_1::endEventLoop(){
	app->exit(0);
}
#include"moc_QtAddOn_1.app"

UI.h

#pragma once
#include <QtCore/QOBJECT>
#include <QtWidgets/QDialog>
#include <QtCore/qmutex.h>
#include <QtWidgets/qlineedit.h>
#include <QtWidgets/qpushbutton.h>
#include <QtWidgets/qwidget.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qdialogbuttonbox.h>
#include <QtWidgets/qlayout.h>
class Client : public QDialog
{
	Q_OBJECT
public:
	explicit Client(QWidget *parent = nullptr);
private:
	QLineEdit *hostLineEdit;
	QPushButton *getFortuneButton;
	QLabel *statusLabel;
private slots:
	void EndSupperEventLoop();
signals:
	void endQt();
};

UI.cpp

#include "pch.h"
#include "UI.h"
#include "QtGui/qevent.h"
Client::Client(QWidget *parent)
	: QDialog(parent),
	hostLineEdit(new QLineEdit("fortune")),
	getFortuneButton(new QPushButton(tr("Get Fortune"))),
	statusLabel(new QLabel(tr("This examples requires that you run the "
		"Local Fortune Server example as well.")))
{
	setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
	QLabel *hostLabel = new QLabel(tr("&Server name:"));
	hostLabel->setBuddy(hostLineEdit);

	statusLabel->setWordWrap(true);

	getFortuneButton->setDefault(true);
	QPushButton *quitButton = new QPushButton(tr("Quit"));
	connect(quitButton, &QPushButton::clicked, this, &Client::EndSupperEventLoop);
	QDialogButtonBox *buttonBox = new QDialogButtonBox;
	buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);
	buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);

	QGridLayout *mainLayout = new QGridLayout(this);
	mainLayout->addWidget(hostLabel, 0, 0);
	mainLayout->addWidget(hostLineEdit, 0, 1);
	mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
	mainLayout->addWidget(buttonBox, 3, 0, 1, 2);

	hostLineEdit->setFocus();
}
void Client::EndSupperEventLoop()
{
	emit endQt();
}
#include"moc_UI.app"

编译,完成。
新建一个工程来调用他:

int main()
{
	QtModles::initQtModle_1(); 
	system("pause");
    std::cout << "Hello World!\n";
}

就出现了这个非常熟悉的界面
在这里插入图片描述
点击quit后Qt的事件循环结束,执行到system(“pause”);

由此可见这种方式是阻塞的(噢嚯,模态的),不阻塞的话在线程中 QtModles::initQtModle_1(); 即可。

另外在不阻塞的情况下,是否有必要让Qt程序所在的线程与主线程通讯呢?我的想法是紧耦合肯定是要通讯的,比如实时性要求高的场景;这种场景下要特别特别注意安全的结束掉Qt所在线程,就我的实践来看,最好是在主线程确定不需要与Qt线程有任何交互时,在主线程调用可以触发 app->exit(0); 的语句,让Qt线程安全的结束。

没有实时性要求的话,尽量解耦,各干各的,通过微型数据库传递表单。

路过大神望不吝赐教!

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值