How to run and show 4 executables in one qt qml window?

2.5k Views Asked by At

I have a 4 different executable program, you can consider that those are empty rectangle same sized windows, and i want to run those exes in one qt qml window.

enter image description here

a,b,c,d are different executables that fixed same size, and x is a windows that written in qt5.11/qml quick2, how can i do that in qt/qml project, any ideas?

I am trying with window container but no progress. The exe is writing its window id to a text and i am reading from that text.

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

There are 4 best solutions below

0
On BEST ANSWER

After long research i could have managed to run an executable in a qt app this is how i did: if your program has a window every windows has an id and you could use that, first of all i am running my outsource executable (4 spearate process but same exe for example)and they are writing their win id to a file, after that exes write finished, my main window program reading that wids and creating qt container with that wid`s (wid has some features like x and y axis ) when qt container created everty process goes inside that qt container and now you have some sparate processes running inside one splited window. enter image description here

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]));
    
    //createConteiner is a func has two line below    
    //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();
}
3
On

You are basically asking how to to create a contained windowing system. This is neither trivial, nor even possible in some operating systems.

If your 4 "executables" are QML code you have access to, you can easily compose them in a single executable.

If they are 3rd party applications, it ain't so easy. It is possible to do that under linux, by utilizing wayland, or even possibly using some X API. But on windows you don't really get that kind of access, at the very least, I haven't found a way to do that, the OS controls the process windows and there isn't much you can do about it.

It might be possible to use the low level GUI APIs windows possibly offers, and if possible, hide the decoration for the 4 windows, and compose the windows so they are on top of your QML application window, and scale and move the 4 windows by code as your QML application window scales and moves.

At any rate, it seems you have wildly underestimated the complexity of implementing this, mostly because it is not an unreasonable expectation that one should be able to do that, but the reality of the situation is different. Windowing systems are very much still black boxes, things people aren't supposed to be meddling with.

0
On

Assuming you really are trying to embed the GUI elements of child processes into your own process then the code you've has a few potential issues.

Firstly, it is possible that on some platforms QProcess::start simply queues the required data. The child process won't actually fork until the event loop is entered. Thus when you have...

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

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

it's possible that the sleep(10) call simply blocks everything and the process hasn't yet started when you try to read. Even if the child process does start there's no guarantee that it will have written its window id to winid.txt by the time you read it -- far better to act on the QProcess::readyReadStandardOutput signal instead.

Secondly, you pass a complete command line to QProcess::start. On certain platforms that will pass the command through a shell meaning you have to be very careful about quoting/escaping special characters. Better to use the...

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

...overload instead.

Finally, the command you're actually running here is sudo. Assuming this is on a Linux system (or similar) then sudo may well require a password and you haven't set up any means of providing one.

By way of an example, the following code does one of two things depending on how it's invoked.

If invoked with a single command line argument it creates/shows a QPushButton derived widget and writes that widget's window id to standard output.

If invoked with no arguments it will act as a parent process. It starts several child processes and, when each prints its window id to stdout, captures and embeds the associated widget within its own.

#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);
}

So, while it doesn't use QML, if you run it without any arguments it should create its own widget, create four child processes and embed the widgets associated with those child processes. Something like...

enter image description here

0
On

If you're on linux, you could write a wayland compositor to compose your applications.

This should do what you want:

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
    }
}

Clients can then be started with ./myclient -platform wayland.

If you're running a nested wayland session, you have to specify that they should connect to the inner compositor by setting WAYLAND_DISPLAY accordingly, i.e. env WAYLAND_DISPLAY=wayland-1 ./myclient -platform wayland