Как запустить и показать 4 исполняемых файла в одном окне qt qml? - PullRequest
0 голосов
/ 18 сентября 2018

У меня есть 4 разные исполняемые программы, вы можете считать, что это пустые окна прямоугольника одинакового размера, и я хочу запустить эти exes в одном окне qt qml.

enter image description here

a, b, c, d - это разные исполняемые файлы с фиксированным размером, а x - это окно, написанное на qt5.11 / qml quick2, как я могу это сделать в проекте qt / qml, есть идеи?

Я пытаюсь с контейнером окна, но без прогресса. Exe пишет свой идентификатор окна в текст, и я читаю из этого текста.

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 ]

0 голосов
/ 21 февраля 2019

После долгих исследований я мог бы запустить исполняемый файл в приложении qt, вот как я это сделал: если у вашей программы есть окно, у каждого окна есть идентификатор, и вы можете использовать это, прежде всего, я запускаю свой исполняемый файл с внешним доступом(4 отдельных процесса, но один и тот же exe, например), и они записывают свой win win в файл, после этого exe s write finished, my main window program reading that wid s и создают контейнер qt с этим wid (виджет имеет некоторые функции, такие как оси x и y), когда qtEverty Process, созданный контейнером, идет внутри этого 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])); // createConteiner - это функция, имеющая две строки ниже
// содержимое функции createContainer // 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();}
0 голосов
/ 18 сентября 2018

Если вы действительно пытаетесь встроить элементы GUI дочерних процессов в свой собственный процесс, то в коде у вас есть несколько потенциальных проблем.

Во-первых, возможно, что на некоторых платформах QProcess::startпросто ставит в очередь необходимые данные.Дочерний процесс не будет фактически разветвляться, пока не будет введен цикл обработки событий.Таким образом, когда у вас есть ...

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

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

, возможно, что вызов sleep(10) просто блокирует все, и процесс еще не начался, когда вы пытаетесь читать.Даже если дочерний процесс действительно запускается, нет гарантии, что он будет записывать свой идентификатор окна в winid.txt к тому времени, когда вы его прочитаете, - гораздо лучше вместо этого воздействовать на сигнал QProcess::readyReadStandardOutput.

Во-вторых, вы передаете полную командную строку QProcess::start.На некоторых платформах, которые передают команду через оболочку, это означает, что вы должны быть очень осторожны с цитированием / экранированием специальных символов.Лучше вместо этого использовать перегрузку ...

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

....

Наконец, команда, которую вы здесь фактически запускаете, - sudo.Если предположить, что это система Linux (или аналогичная), тогда для sudo может потребоваться пароль, и вы не настроили способ его предоставления.

В качестве примера приведен следующий кодвыполняет одну из двух вещей в зависимости от того, как он вызывается.

Если вызывается с одним аргументом командной строки, он создает / показывает производный виджет QPushButton и записывает идентификатор окна этого виджета в стандартный вывод.

Если вызывается без аргументов, он будет действовать как родительский процесс.Он запускает несколько дочерних процессов и, когда каждый печатает свой идентификатор окна в стандартный вывод, захватывает и встраивает связанный виджет в свой собственный.

#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, если вы запуститебез каких-либо аргументов он должен создать свой собственный виджет, создать четыре дочерних процесса и встроить виджеты, связанные с этими дочерними процессами.Что-то вроде ...

enter image description here

0 голосов
/ 03 октября 2018

Если вы работаете в Linux, вы можете написать Wayland CompoSitor для создания ваших приложений.

Это должно делать то, что вы хотите:

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

0 голосов
/ 18 сентября 2018

Вы в основном спрашиваете, как создать автономную оконную систему. Это не тривиально и даже невозможно в некоторых операционных системах.

Если ваши 4 «исполняемых файла» представляют собой код QML, к которому у вас есть доступ, вы можете легко составить их в один исполняемый файл.

Если это сторонние приложения, это не так просто. Это можно сделать в Linux, используя Wayland или даже возможно используя некоторый X API. Но в Windows вы не получаете такого доступа, по крайней мере, я не нашел способа сделать это, ОС контролирует окна процесса, и вы ничего не можете с этим поделать.

Возможно, будет возможно использовать низкоуровневые окна API графического интерфейса, которые, возможно, предлагаются, и, если возможно, скрыть оформление для 4 окон и составить окна так, чтобы они находились в верхней части окна приложения QML, а также масштабировать и перемещать 4 окна по коду при масштабировании и перемещении окна вашего приложения QML.

Во всяком случае, кажется, вы дико недооценили сложность реализации этого, в основном потому, что это не является необоснованным ожиданием того, что можно это сделать, но реальность ситуации иная. Оконные системы - это все еще чёрные ящики, в которые люди не должны вмешиваться.

...