У меня есть 4 разные исполняемые программы, вы можете считать, что это пустые окна прямоугольника одинакового размера, и я хочу запустить эти exes в одном окне qt qml.
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 «исполняемых файла» представляют собой код QML, к которому у вас есть доступ, вы можете легко составить их в один исполняемый файл.
Если это сторонние приложения, это не так просто. Это можно сделать в Linux, используя Wayland или даже возможно используя некоторый X API. Но на окнах вы на самом деле не получаете такой доступ, по крайней мере, я не нашел способа сделать это, ОС контролирует окна процесса, и вы ничего не можете с этим поделать.
Возможно, будет возможно использовать низкоуровневые интерфейсы API-интерфейсов, предлагаемые окнами, и, если возможно, скрыть оформление для 4 окон и составить окна так, чтобы они находились поверх окна вашего приложения QML, а также масштабировать и перемещать 4 окна с помощью код как окно вашего приложения QML масштабируется и перемещается.
Во всяком случае, кажется, вы дико недооценили сложность реализации этого, в основном потому, что это не является необоснованным ожиданием того, что можно это сделать, но реальность ситуации иная. Оконные системы — это все еще черные ящики, в которые люди не должны вмешиваться.
Если вы действительно пытаетесь встроить элементы GUI дочерних процессов в свой собственный процесс, то в коде у вас есть несколько потенциальных проблем.
Во-первых, возможно, что на некоторых платформах QProcess::start
просто ставит в очередь необходимые данные. Дочерний процесс не будет фактически разветвляться, пока не будет введен цикл обработки событий. Таким образом, когда у вас есть …
QProcess ps;
ps.start("sudo ./exe 1");
sleep(10);
ifstream myfile;
myfile.open("winid.txt");
WId id;
myfile >> id;
возможно, что sleep(10)
call просто блокирует все, и процесс еще не начался, когда вы пытаетесь читать. Даже если дочерний процесс запускается, нет гарантии, что он записал свой идентификатор окна в winid.txt
к тому времени, как вы это прочитаете — гораздо лучше действовать на QProcess::readyReadStandardOutput
сигнал вместо.
Во-вторых, вы передаете полную командную строку QProcess::start
, На некоторых платформах, которые передают команду через оболочку, это означает, что вы должны быть очень осторожны с цитированием / экранированием специальных символов. Лучше использовать …
void QProcess::start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite)
…перегрузка вместо.
Наконец, команда, которую вы на самом деле запускаете здесь sudo
, Предполагая, что это на Linux
система (или аналог), то sudo
может потребоваться пароль, и вы не настроили способ его предоставления.
В качестве примера, следующий код выполняет одну из двух вещей в зависимости от того, как он вызывается.
Если вызывается с одним аргументом командной строки, он создает / показывает QPushButton
производный виджет и записывает идентификатор окна этого виджета в стандартный вывод.
Если вызывается без аргументов, он будет действовать как родительский процесс. Он запускает несколько дочерних процессов и, когда каждый печатает свой идентификатор окна в stdout, захватывает и встраивает связанный виджет в свой собственный.
#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
Если вы запускаете его без аргументов, он должен создать свой собственный виджет, создать четыре дочерних процесса и встроить виджеты, связанные с этими дочерними процессами. Что-то вроде…
Если вы используете 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