2

我有 4 个不同的可执行程序,您可以认为它们是相同大小的空矩形窗口,我想在一个 qt qml 窗口中运行这些 exe。

在此处输入图像描述

a、b、c、d 是固定相同大小的不同可执行文件,x 是用 qt5.11/qml quick2 编写的窗口,我如何在 qt/qml 项目中做到这一点,有什么想法吗?

我正在尝试使用窗口容器,但没有进展。该 exe 正在将其窗口 ID 写入文本,而我正在读取该文本。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QtQuick2ApplicationViewer viewer;
    viewer.addImportPath(QLatin1String("modules"));
    viewer.setOrientation(QtQuick2ApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile(QLatin1String("qrc:///main.qml"));
    viewer.showExpanded();

    QProcess ps;
    ps.start("sudo ./exe 1");

    sleep(10);
    ifstream myfile;
    myfile.open("winid.txt");
    WId id ; myfile >> id;
    cout<<"WId ? "<<id<<endl;
    myfile.close();

    //WId id = (WId)FindWindow(NULL, L"PMON");
    QWindow *container = QWindow::fromWinId(id);
    container->setFlags(Qt::FramelessWindowHint);
    QWidget *program_start = QWidget::createWindowContainer(container);
    program_start->setWindowTitle("Fero");

    QVBoxLayout *manageWindows = new QVBoxLayout(program_start);
    //manageWindows->addWidget(program_start);
    //manageWindows->setGeometry(QRect(0,0,1400,800));
    program_start->setLayout(manageWindows);
    program_start->show();


    return app.exec();
}
4

4 回答 4

2

您基本上是在询问如何创建包含的窗口系统。这在某些操作系统中既不是微不足道的,也不是不可能的。

如果您的 4 个“可执行文件”是您可以访问的 QML 代码,您可以轻松地将它们组合成一个可执行文件。

如果它们是 3rd 方应用程序,那就没那么容易了。可以在 linux 下通过使用 wayland 甚至可能使用一些 X API 来做到这一点。但是在 Windows 上,您并没有真正获得这种访问权限,至少,我还没有找到一种方法来做到这一点,操作系统控制着进程窗口,您对此无能为力。

可以使用窗口可能提供的低级 GUI API,如果可能的话,隐藏 4 个窗口的装饰,并组合窗口,使它们位于 QML 应用程序窗口的顶部,然后缩放和移动 4 个窗口在 QML 应用程序窗口缩放和移动时编写代码。

无论如何,您似乎大大低估了实现这一点的复杂性,主要是因为人们应该能够做到这一点并不是一个不合理的期望,但实际情况却不同。窗口系统仍然是黑匣子,人们不应该干预的东西。

于 2018-09-18T09:54:53.057 回答
1

经过长时间的研究,我可以设法在 qt 应用程序中运行可执行文件,这就是我所做的:如果你的程序有一个窗口,每个窗口都有一个 id,你可以使用它,首先我正在运行我的外包可执行文件(4 spearate进程但相同的exe例如)并且他们正在将他们的win id写入文件,然后s write finished, my main window program reading that wid当qt容器创建everty进程进入时,使用该wid`s创建qt容器(wid具有x和y轴等一些功能)那个 qt 容器,现在你在一个拆分的窗口中运行了一些独立的进程。 在此处输入图像描述

int main(int argc, char *argv[]){
QApplication app(argc, argv);

QtQuick2ApplicationViewer viewer;
viewer.addImportPath(QLatin1String("modules"));
viewer.setOrientation(QtQuick2ApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qrc:///main.qml"));
viewer.showExpanded();

QProcess ps;
ps.start("sudo ./exe 1");

sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id ; myfile >> id;
cout<<"WId ? "<<id<<endl;
myfile.close();

QTableWidget* grids ;

CreateContainer createContainerOBJ; grids->setCellWidget(i+(i+1),j,createContainerOBJ.createContainer(id[i*tableCol+j]));//createContainer是一个func,下面有两行
//createContainer func content //QWindow *container = QWindow::fromWinId(id); //program_start = QWidget::createWindowContainer(container);

//manageWindows->addWidget(program_start);
//manageWindows->setGeometry(QRect(0,0,1400,800));
program_start->setLayout(manageWindows);
program_start->show();


return app.exec();}
于 2019-02-21T09:15:26.340 回答
0

假设您确实正在尝试将子进程的 GUI 元素嵌入到您自己的进程中,那么您的代码就有一些潜在的问题。

首先,在某些平台上,可能QProcess::start只是简单地将所需数据排队。在进入事件循环之前,子进程实际上不会分叉。因此当你有...

QProcess ps;
ps.start("sudo ./exe 1");

sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id;
myfile >> id;

调用可能sleep(10)只是阻止了所有内容,并且当您尝试阅读时该过程尚未开始。即使子进程确实启动了,也不能保证它会在您读取它时将其窗口 id 写入其中——而不是对信号winid.txt采取行动要好得多。QProcess::readyReadStandardOutput

其次,您将完整的命令行传递给QProcess::start. 在某些将通过 shell 传递命令的平台上,这意味着您必须非常小心引用/转义特殊字符。最好使用...

void QProcess::start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite)

...改为重载。

最后,您在此处实际运行的命令是sudo. 假设这是在Linux系统(或类似系统)上,那么sudo很可能需要密码,而您还没有设置任何提供密码的方式。

作为示例,以下代码根据调用方式执行两件事之一。

如果使用单个命令行参数调用,它会创建/显示QPushButton派生的小部件并将该小部件的窗口 ID 写入标准输出。

如果不带参数调用,它将充当父进程。它启动了几个子进程,并且当每个子进程将其窗口 ID 打印到标准输出时,捕获相关联的小部件并将其嵌入到自己的小部件中。

#include <cstdlib>
#include <iostream>
#include <set>
#include <QApplication>
#include <QDebug>
#include <QLabel>
#include <QProcess>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QWindow>

namespace {

  /*
   * This is the basic QPushButton derived widget that will be created by the
   * child process(es).
   */
  class remote_process_widget: public QPushButton {
    using super = QPushButton;
  public:
    explicit remote_process_widget (const QString &name, QWidget *parent = nullptr)
      : super(name, parent)
      {
      }
  };
}

int
main (int argc, char **argv)
{
  try {
    QApplication app(argc, argv);
    std::set<QProcess *> processes;
    if (argc > 1) {

      /*
       * This process was started with at least one command line arg so we
       * assume it's a managed child process.  Need to write the window id to
       * stdout for the parent process to read.
       */
      auto *w = new remote_process_widget(QString::fromStdString(argv[1]));
      w->show();
      std::cout << w->winId() << std::endl;
    } else {

      /*
       * No command line args so start up as the parent process.  Create some
       * child processes and set things up to manage their widgets.
       */
      auto *w = new QWidget;
      auto *l = new QVBoxLayout(w);
      auto *label = new QLabel("Parent process");
      label->setAlignment(Qt::AlignCenter);
      l->addWidget(label);
      w->show();

      /*
       * Now create/start the child processes.
       */
      for (int i = 0; i < 4; ++i) {
        auto *process = new QProcess;
        processes.insert(process);

        /*
         * Connect to the `QProcess::readyReadStandardOutput` signal of the
         * child.  This is where the real work is done regarding the
         * capture/embedding of the child processes widgets.
         */
        QObject::connect(process, &QProcess::readyReadStandardOutput,
                         [l, process]()
                           {
                             auto wid = QString(process->readAllStandardOutput()).toULongLong();
                             std::cout << "wid = " << wid << "\n";
                             if (auto *window = QWindow::fromWinId(wid)) {
                               if (auto *container = QWidget::createWindowContainer(window)) {
                                 l->addWidget(container);
                               }
                             }
                           });

        /*
         * Start the child process.
         */
        process->start(argv[0], QStringList() << QString("Remote process %1").arg(i));
      }
    }

    app.exec();

    /*
     * Shut down all child processes.
     */
    for (auto process: processes) {
      process->terminate();
      std::cout << "waiting for process " << process->processId() << " to terminate\n";
      while (!process->waitForFinished())
        ;
    }
    std::cout << "done\n";
  }
  catch (std::exception &ex) {
    qCritical() << "\n" << ex.what();
  }
  catch (...) {
    qCritical() << "\nunrecognized exception";
  }
  exit(0);
}

因此,虽然它不使用QML,但如果您在没有任何参数的情况下运行它,它应该创建自己的小部件,创建四个子进程并嵌入与这些子进程关联的小部件。就像是...

在此处输入图像描述

于 2018-09-18T20:12:33.697 回答
0

如果您使用的是 linux,则可以编写一个 wayland 合成器来编写您的应用程序。

这应该做你想要的:

import QtQuick 2.0
import QtWayland.Compositor 1.3 // or 1.2 on Qt 5.11
import QtQuick.Window 2.2

WaylandCompositor {
    id: wlcompositor
    WaylandOutput {
        sizeFollowsWindow: true
        compositor: wlcompositor
        window: Window {
            width: 1024
            height: 768
            visible: true
            title: wlcompositor.socketName
            Grid {
                columns: 2
                Repeater {
                    model: shellSurfaces
                    ShellSurfaceItem {
                        autoCreatePopupItems: true
                        shellSurface: modelData
                        onSurfaceDestroyed: shellSurfaces.remove(index)
                    }
                }
            }
        }
    }
    ListModel { id: shellSurfaces }
    // Qt 5.11+
    XdgShellV6 {
        onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface})
    }
    // Qt 5.12+
    XdgShell {
        onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface})
    }
    // Qt 5.12+ Disable window decorations (for qt applications you can also do this by setting
    // QT_WAYLAND_DISABLE_WINDOWDECORATION=1 in the client's environment (any version).
    XdgDecorationManagerV1 {
        preferredMode: XdgToplevel.ServerSideDecoration
    }
}

然后可以使用./myclient -platform wayland.

如果您正在运行嵌套的 Wayland 会话,则必须通过相应设置指定它们应连接到内部合成器WAYLAND_DISPLAY,即env WAYLAND_DISPLAY=wayland-1 ./myclient -platform wayland

于 2018-10-03T07:34:47.110 回答