环境:VS2017,Qt5.12.10(安装了msvc2017组件,并添加环境变量)
需求和概览:
Qt的信号与槽和便捷的界面编写非常有利于UI开发,很多时候希望把已经编写好的界面和逻辑加载到现有工程中,这时选择DLL加载是很敏捷的做法。
众所周知的,只有在构建了 QApplication 或 QCoreApplication(以下以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.h,QtAddOn_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线程安全的结束。
没有实时性要求的话,尽量解耦,各干各的,通过微型数据库传递表单。
路过大神望不吝赐教!