最近接触到外协厂家的一套系统,这套系统的主界面由几个功能界面组成的,他的实现方式并不是将所有的功能界面设计在这一个主界面程序上,而是将各个功能界面做成单独的界面程序(单独的进程),每个程序都可以独立运行,然后通过将这几个程序嵌入到主界面中,形成一个完整的主界面。这种模块化的实现方式耦合度不高,各个模块比较独立。因为外协厂家的源码是没有开放给我们,所以从网上翻了几篇博客,对可能的实现方式有了基本的了解,虽然这种在当前进程界面中嵌入其他进程界面的应用场景可能不多,但是记录一下,作为知识储备,方便以后查用。
我使用的环境是Ubuntu12.04, QT5.5 开始之前对几个用到几个知识点交代一下。
1、QProcess:这个类用于启动外部进程,它有两种启动方式
一体式:QProcess::start 方法。外部程序启动后,将随主程序的退出而退出。 貌似要使用QProcess的close方法才可以关闭启动的外部程序。
分离式:QProcess::startDetached 方法。外部程序启动后,当主程序退出时并不退出,而是继续运行。
它不是重点这里简单介绍。
2、QWindow* QWindow::fromWinId(WId id):这是QWindow的一个方法,可以通过传入的一个窗口句柄创建一个QWindow对象,然后将它嵌入到当前进程的的界面容器中。
3、 QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent = Q_NULLPTR, Qt::WindowFlags flags = Qt::WindowFlags()):这是QWidget的一个方法,它会创建一个QWidget窗口容器,该窗口容器被创建来作为父窗口的子窗口,并且可以在参数中设置窗体属性标志。
代码如下,这里分成两部分,child是要嵌入的子窗体进程,parent是父窗体进程。界面很简单。
这是child的代码,就是最简单的UI界面,唯一添加的一点就是将窗口的winID存到了一个文件里面,方便给parent进程读取,然后用这个ID来创建要嵌入的界面。
头文件
#ifndef CHILDWIDGET_H
#define CHILDWIDGET_H
#include <QWidget>
namespace Ui {
class ChildWidget;
}
class ChildWidget : public QWidget
{
Q_OBJECT
public:
explicit ChildWidget(QWidget *parent = 0);
~ChildWidget();
private:
Ui::ChildWidget *ui;
};
#endif // CHILDWIDGET_H
#include "childwidget.h"
#include "ui_childwidget.h"
#include <QDebug>
#include <QSettings>
ChildWidget::ChildWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ChildWidget)
{
ui->setupUi(this);
QSettings* settings = new QSettings("/ID.ini",QSettings::IniFormat);
settings->setValue("T3",this->winId());
}
ChildWidget::~ChildWidget()
{
delete ui;
}
#include "childwidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ChildWidget w;
w.show();
return a.exec();
}
下面时parent进程的代码
首先是头文件
#ifndef PARENTWIDGET_H
#define PARENTWIDGET_H
#include <QWidget>
namespace Ui {
class ParentWidget;
}
class ParentWidget : public QWidget
{
Q_OBJECT
public:
explicit ParentWidget(QWidget *parent = 0);
~ParentWidget();
private:
Ui::ParentWidget *ui;
QWidget *childWidget;
};
#endif // PARENTWIDGET_H
下面是源文件,在构造方法中,我们先通过QProcess启动了child进程,然后再读取child进程写到文件中的winID,利用这个winID创建要嵌入的窗体,然后放到窗体容器中,最后在放到窗体布局中。
#include "parentwidget.h"
#include "ui_parentwidget.h"
#include <QWindow>
#include <QSettings>
#include <QProcess>
ParentWidget::ParentWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ParentWidget),
childWidget(NULL)
{
ui->setupUi(this);
QProcess* caller = new QProcess(this);//创建对象
caller->start("/root/qt-windows-composition-example-master/child/child");//启动notepaid
sleep(1);
QSettings* settings = new QSettings("/ID.ini",QSettings::IniFormat);
int childWidgetWinId = settings->value("T3").toInt();; //get from debug output
QWindow *childWindow = QWindow::fromWinId(childWidgetWinId);
childWidget = QWidget::createWindowContainer(childWindow, this, Qt::Widget );
childWidget->setMinimumSize(100,100);
ui->verticalLayout_2->addWidget(childWidget,1,0);
}
ParentWidget::~ParentWidget()
{
delete ui;
}
下面是主函数
#include "parentwidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ParentWidget w;
w.show();
return a.exec();
}
这是parent的界面
启动parent进程,运行效果如图
这种方式虽然有耦合度低的好处,但是也有一定的缺陷,大量使用会影响程序的整体性能,在Qt的帮助文档中有明确说明:
- Using many window container instances in a QWidget-based application can greatly hurt the overall performance of the application.
译:在基于QWidget的应用中使用大量的window窗口容器实例,会在很大程度上影响应用程序的整体性能。