2

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();
}
Marek R
  • 32,568
  • 6
  • 55
  • 140
ikaloron
  • 59
  • 8
  • 1
    what have you tried so far? What are the problems you are facing? – IIIIIIIIIIIIIIIIIIIIII Sep 18 '18 at 08:27
  • First you should reduce problem. IMO showing application inside of host application is the real issue here. If you solve it then showing 4 at the same time will be simple. Why you can't use plugins and single process? It smells a bit like [XY prolem](http://xyproblem.info/). – Marek R Sep 18 '18 at 08:30
  • I have tried using some qt resources like windowcontainer etc. and i researched but problem is that I couldn`t find a good idea about that i don`t know where to start or to go which way, i have qt project and some exes. @IIIIIIIIIIIIIIIIIIIIII – ikaloron Sep 18 '18 at 08:50
  • This scheme is not xy prolem i know that i smells like that but it is not. If i can do this i will use a lot and this is just came in my mind and i`m trying to figure out how to. @MarekR – ikaloron Sep 18 '18 at 08:53
  • just use mousearea for all 4 rectangles and onClicked method to start the application you want. check QProcess to start external programm from QML – nayab Sep 18 '18 at 08:54
  • @nayab starting a process is not the issue here, how to embed external process UI into current process UI this is the issue. – Marek R Sep 18 '18 at 09:00
  • @undderRatedCrossTraveller I do not see more details, definition of requirements, so I'm still suspecting XY problem, denying it will not reduce my suspicions. For example provide details about sub-processes. What kind of application is it. Is it yours (your code)? Does it have a UI? If it does is it QML/Qt? Or is it a console application? If you will be more precise, the is a bigger change that you will get a desired answer. Now you are risking that question will be closed (not my vote yet). – Marek R Sep 18 '18 at 09:02
  • You can certainly embed a window from one process into a parent window from some other process using [`QWidget::createWindowContainer`](http://doc.qt.io/qt-5/qwidget.html#createWindowContainer), but it's up to you to provide the relevant window ids etc. – G.M. Sep 18 '18 at 10:32
  • I tried with container as your advices. – ikaloron Sep 18 '18 at 11:44
  • As G.M`s advices i tried this, and now i am trying using createWindowContainer, sorry for missunderstanding thats my first question and i`m willing to do what you say so i added my try. @MarekR – ikaloron Sep 18 '18 at 12:02
  • 4 embedded dialogs each with their own thread? – Mike Sep 18 '18 at 19:23
  • Should have used Qt Wayland – ArmadilloChubbz65 Apr 04 '22 at 12:56

4 Answers4

2

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.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • I am under linux, and qt versions are qt5.11.1 and qml quick 2. – ikaloron Sep 18 '18 at 10:28
  • Thank you for your explanatory answer, the main purpose that is running an executable in a qt/qml created project`s window. After that making 4 is becomes quite possible in my opinion. thanks again. – ikaloron Sep 18 '18 at 10:33
  • We're actually trying to make it trivial to do this with the Qt Wayland Compositor API. See my answer. – bobbaluba Oct 03 '18 at 07:38
1

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

bobbaluba
  • 3,584
  • 2
  • 31
  • 45
1

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();
}
Mars
  • 2,505
  • 17
  • 26
ikaloron
  • 59
  • 8
0

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

G.M.
  • 12,232
  • 2
  • 15
  • 18