Qt插件的制作和调用

需求说明

最近有一个项目需要2个人一起开发,开发工具都是Qt,每个人负责一块内容,2块内容都有各自的UI文件和业务逻辑,我需要将其他人的界面子窗口嵌入到我的主窗口中,将2个软件整合成一个软件,而我们彼此的代码不共享,因此这2块内容必然有数据交互。这里最麻烦的事情就是我们2个人软件如何整合,如何交换数据,如何将他的界面嵌入到我的主窗口中,点击主窗口中某个按钮如何弹出他的子窗口。最开始我们想到的是利用Qt IPC进程间通信,将他的窗口嵌入到我的主窗口中(主要是这个函数QWindow *QWindow::fromWinId(WIdid)实现嵌套),再用共享内存实现数据的交互,这种方法可以实现我们的要求,但是这种方法每次需要先启动他的进程,记录他的窗口句柄,然后我再获取到他的窗口句柄,才能将他的窗口显示出来,首先这里存在启动先后的问题,我这边是主界面,软件启动理应先加载我的界面,其次,由于要记录他的窗口句柄,我的界面启动需要延时,造成软件启动不流畅,最后也是最大的问题是,2个进程间窗口的嵌套会造成软件的卡顿,流程性很差,而且用共享内存交换数据,两者需要不断收发数据,造成资源浪费,增加程序的复杂度,最终我们放弃了这种方法。

后续我探索了Qt插件的开发,如果将他的程序做成一个带UI的dll,我直接调用接口函数,得到他的窗口指针就可以将他的窗口嵌入到主窗口中,交换数据也通过接口函数实现,最终软件将是一个进程。最终证实这种方法确实可行,软件复杂度和运行效率提高不少。

方法实现

Qt插件,制作方和调用方需要一个公共的头文件,这个头文件也就是接口,里面包含一些接口类和接口函数,2者通过这些接口函数实现数据的交换。接口函数都是纯虚函数,由制作方继承接口类实现接口纯虚函数。我演示用的头文件interface.h如下:

#ifndef INTERFACE_H
#define INTERFACE_H

#include <QtPlugin>
#include <QDialog>
#include <QGraphicsView>

class PopWidgetInterface
{
public:
    virtual QDialog *dialogHandle() = 0;

    virtual QStringList paraList() = 0;

    virtual QGraphicsView  *graphicViewHandle() = 0;
};


#define PopWidgetInterface_iid "org.qt-project.Interface/1.0"

QT_BEGIN_NAMESPACE

Q_DECLARE_INTERFACE(PopWidgetInterface, PopWidgetInterface_iid)

QT_END_NAMESPACE

#endif // INTERFACE_H

头文件中非常重要的一行代码是Q_DECLARE_INTERFACE(PopWidgetInterface, PopWidgetInterface_iid),这个宏将给定的标识符PopWidgetInterface_iid关联到接口类PopWidgetInterface,后面通过这个唯一的标识符找到对应的接口类,这个宏通常在头文件中接口类定义之后使用。

接着插件制作方需要继承接口类PopWidgetInterface,并实现接口纯虚函数。我定义了一个WidgetPlugin类实现接口函数,WidgetPlugin.h代码如下:

#include <Interface.h>
#include "platesetting.h"
#include "mygraphic.h"
class WidgetPlugin : public QObject, PopWidgetInterface
{
    Q_OBJECT

    Q_PLUGIN_METADATA(IID PopWidgetInterface_iid)

    Q_INTERFACES(PopWidgetInterface)

public:
    explicit WidgetPlugin(QObject *parent = nullptr);

    QDialog* dialogHandle() override;

    QStringList paraList() override;

    QGraphicsView* graphicViewHandle() override;

private:
    PlateSetting *m_dlg;
};

头文件WidgetPlugin.h中也有2个很重要的宏,Q_PLUGIN_METADATA用于声明元数据,这些元数据是实列化类WidgetPlugin对象的一部分,可以将插件导出,宏中需要声明一个IID,也就是我们上述接口头文件中定义的 PopWidgetInterface_iid一串字符。Q_PLUGIN_METADATA还有一个FILE可选项,指向一个json文件,该文件包含插件相关的一些信息,json文件必须在构建系统指定的包含目录中,当选择了FILE参数,如果没有找到FILE指定的文件,moc出错退出,无法编译通过,这里为了方便,我没有指定FILE选项。

PlateSetting *m_dlg是一个待弹出对话框的类的实例,当在我的主窗口中点击某个按钮可以弹出别人的窗口,如下图所示,我通过接口函数paraList获取他的界面中某些参数。QGraphicsView* graphicViewHandle() 函数是返回一个MyGraphic对象,用于显示一个图像,这个widget对象需要嵌入到主窗口中。

WidgetPlugin.cpp文件如下:

WidgetPlugin::WidgetPlugin(QObject *parent) : QObject(parent)
{
    m_dlg = new PlateSetting;
}

QDialog* WidgetPlugin::dialogHandle()
{
    if(m_dlg)
        return m_dlg;

    return nullptr;
}

QStringList WidgetPlugin::paraList()
{
    return m_dlg->paraList();
}

QGraphicsView* WidgetPlugin::graphicViewHandle()
{
    return new MyGraphic;
}

MyGraphic类代码如下:

class MyGraphic : public QGraphicsView
{
    Q_OBJECT
public:
    MyGraphic(QWidget *parent = nullptr);

private:
    QGraphicsScene *m_pScene;
    QGraphicsPixmapItem *m_pItem;
};
MyGraphic::MyGraphic(QWidget *parent):QGraphicsView(parent)
{
    m_pScene = new QGraphicsScene;
    this->setScene(m_pScene);
    m_pItem = m_pScene->addPixmap(QPixmap(":/pic.bmp"));
}

最后我们需要在插件工程文件.pro中添加CONFIG += plugin和TEMPLATE= lib告诉编译器需要生成插件dll.

插件部分到此结束,编译之后会生成一个dll库,供调用方使用。

接下来需要建立一个工程调用此插件。首先需要将接口头文件包含到调用工程项目中,将上述生成的dll文件拷贝到我的调用工程生成的exe目录下。调用函数代码如下:

void MainWindow::loadPlugin()
{
    QDir pluginsDir(qApp->applicationDirPath());
    QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(QString("plugins.dll")));
    QObject *plugin = pluginLoader.instance();
    if(plugin) {
       m_pInterface = qobject_cast<PopWidgetInterface*>(plugin);
       if(!m_pInterface) {
            qDebug() <<"qobject_cast error";
       }
       else {
           ui->verticalLayout_2->addWidget( m_pInterface->graphicViewHandle());
       }
    }
    else {
        qDebug() << __FUNCTION__ << pluginLoader.errorString();
    }
}

通过类QPluginLoader加载插件,加载成功后将返回的QObject对象转换成接口类PopWidgetInterface实例对象即m_pInterface,转换成功后就可以调用接口函数,获取我们想要的子窗口以及参数列表。如下图所示,MainWindow是我的主窗口,窗口中包含一个按钮和一张表格,单机按钮可以弹出外部对话框,表格下方的图像窗口也是其他人的窗口,通过调用loadPlugin函数可以调用外部程序,成功实现窗口嵌套和数据交换。按钮单击事件代码如下:

void MainWindow::on_pushButton_8_clicked()
{
    if(m_pInterface)
    {
         m_pInterface->dialogHandle()->show();
         connect(m_pInterface->dialogHandle(), &QDialog::accepted, [this] {
             qDebug() << m_pInterface->paraList();
         });
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值