目录
前言
效果
编写dll
编写主程序
总结
最近项目中遇到插件架构,将几个功能模块分别写成了dll供主程序调用。本文主要在QT下实现这样一个功能:
编写一个dll,里面包含一个QDialog对话框作为主窗口。该dll提供四个接口,它们的作用分别是
1 打开主窗口
2 关闭主窗口
3 更新插件数据(暂不讨论)
4 调用插件中的函数并获取返回值(暂不讨论)
然后由主程序调用dll,并将dll中的窗口嵌入自己的对话框中,并实现自适应。
1 创建一个Qt Library工程名为test,然后再插入一个QDialog名为testwidget。
此时工程中目录有以下文件:
2 编辑ui,利用布局让他能够随着窗口大小改变自适应。注意对象查看器里布局变化,具体方法如下:
3 编写接口
在test.h编写接口函数定义,在test.cpp中实现。
其中全局变量g_pTestWidget是为了防止重复打开或者关闭。
代码如下:
#ifndef TEST_H
#define TEST_H
#include "test_global.h"
#include "testwidget.h"
class TEST_EXPORT test
{
public:
test();
~test();
private:
};
#ifdef __cplusplus
extern "C" { // we need to export the C interface
#endif
void TEST_EXPORT PluginStart(void *lParentWidget);
void TEST_EXPORT PluginClose(bool bCompleteClose);
void TEST_EXPORT PluginUpdate(void *upDate);
TEST_EXPORT const char* PluginFunction(const char* input);
#ifdef __cplusplus
}
#endif
#endif // TEST_H
#include "test.h"
testWidget *g_pTestWidget = NULL;
test::test()
{
}
test::~test()
{
}
void TEST_EXPORT PluginStart(void *lParentWidget)
{
if (NULL == g_pTestWidget)
{
g_pTestWidget = new testWidget();
}
if (NULL != lParentWidget)
{
g_pTestWidget->setParent((QDialog *)lParentWidget);
g_pTestWidget->raise();
g_pTestWidget->setGeometry(0, 0, ((QDialog *)lParentWidget)->width(), ((QDialog *)lParentWidget)->height());
}
g_pTestWidget->show();
}
void TEST_EXPORT PluginClose(bool bCompleteClose)
{
if (g_pTestWidget != NULL)
{
if (bCompleteClose)
{
g_pTestWidget->hide();
}
else
{
g_pTestWidget->close();
delete g_pTestWidget;
g_pTestWidget = NULL;
}
}
}
void TEST_EXPORT PluginUpdate(void *upDate)
{
}
TEST_EXPORT const char* PluginFunction(const char* input)
{
return input;
}
至此插件部分的任务完成了。
主程序为一个QWidget窗口,我们要将加载的QDialog窗口放进Widget控件里,并使他相对于Widget实现自适应。因此我们需要
子类化Widget控件(QWidget),重写他的resizeEvent。
QResizingWidget继承自QWidget:
#ifndef QRESIZINGWIDGET_H
#define QRESIZINGWIDGET_H
#pragma once
#include
class QResizingWidget : public QWidget
{
Q_OBJECT
public:
explicit QResizingWidget(QWidget *parent = 0);
virtual ~QResizingWidget();
protected:
virtual void paintEvent(QPaintEvent *sEvent);
virtual void resizeEvent(QResizeEvent* sEvent);
};
#endif
#include "qresizingwidget.h"
#include
#include
#include
QResizingWidget::QResizingWidget(QWidget *parent /*= 0*/)
: QWidget(parent)
{
}
QResizingWidget::~QResizingWidget()
{
}
void QResizingWidget::resizeEvent(QResizeEvent* sEvent)
{
QWidget::resizeEvent(sEvent);
foreach(auto itr, children())
{
if (itr->isWidgetType())
{
itr->setProperty("geometry", QRectF(0, 0, geometry().width(), geometry().height()));
}
}
}
void QResizingWidget::paintEvent(QPaintEvent *sEvent)
{
/*
发现 继承自QWidget的自定义类 ,使用setStyleSheet无效,
如果删除头文件中的 Q_OBJECT,setStyleSheet生效,
但不用OBJECT 就无法所使用signal and slot,
最后找到解决办法,继承自QWidget的自定义类中,重载 paintEvent方法,
并加入如下代码:
*/
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
QWidget::paintEvent(sEvent);
}
主程序QWidget如下:
也以同样的布局方式设置自适应。
下面编写加载函数,用QLibrary显示加载dll,首先定义四个与dll中接口一致的函数指针。
代码如下:
#include "testdll.h"
#include
#include
typedef void (*fun_start)(void *lparentWidget);
typedef void (*fun_close)(bool bCompleteClose);
typedef void (*fun_update)(const char *upDate);
typedef const char* (*fun_callback)(void *input);
fun_start g_Start = NULL;
fun_close g_End = NULL;
QLibrary myDll;
testDll::testDll(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.load, SIGNAL(clicked()), this, SLOT(load()));
connect(ui.unload, SIGNAL(clicked()), this, SLOT(unload()));
}
testDll::~testDll()
{
}
void testDll::load()
{
myDll.setFileName("test.dll");
if (myDll.load())
{
if (!myDll.isLoaded())
{
qDebug() << "load error!";
}
else
{
g_Start = (fun_start)myDll.resolve("PluginStart");
qDebug() << g_Start;
qDebug() << "load success!";
g_Start(ui.widget);
}
}
}
void testDll::unload()
{
if (myDll.isLoaded())
{
g_End = (fun_close)myDll.resolve("PluginClose");
g_End(false);
myDll.unload();
}
}
至此主程序编写完毕。
如果主程序需要集成多个插件呢?那就要设计一个插件管理工具,下次再讲。