Запускаем внешнюю программу в Qt6. Запуск внешних программ в Qt6. Часть 1.
При разработке программы вам может потребоваться запустить стороннюю программу. Причин для этого множество, от получения данных в текстовом виде, до копирования необходимой информации или файлов средствами операционной системы, до получения результата работы сторонней программы.
Здесь и далее я буду использовать термин нить (Thread), аналогом которого принято считать термины поток или тред.
Для запуска внешней программы в Qt6 используется класс QProcess. Помимо самого запуска вы можете столкнуться с блокировкой основной нити вашей программы, во время выполнения внешней программы.
Сегодня мы рассмотрим запуск внешней программы из Qt6 и используем QThread для её запуска в отдельной нити.
Создадим новый проект Qt6 Quick Application
Назовем его QtRunnerDemo
Выберем версию Qt 6.x
В этой статье форма нам нужна, чтобы иметь возможность быстро прервать выполнение программы и проверить доступность интерфейса программы.
Подготовка формы
Для большей наглядности подготовим простую форму с кнопкой и индикатором прогресса.
Откроем файл Main.qml и заменим содержимое на:
import QtQuick
import QtQuick.Controls
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Column {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Button {
width: 60
height: 30
anchors.horizontalCenter: parent.horizontalCenter
text: "Start"
onClicked: {
console.log("Clicked")
}
}
BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
}
}
}Запустим проект. Откроется окно:
Запускаем внешнюю программу
Откроем фай main.cpp и добавим перед
return app.exec();Код:
QProcess process;
QStringList params;
params.append("/C dir c:\windows");
qDebug() << "Запуск";
process.start("cmd", params);
qDebug() << "Запущено";
process.waitForFinished();
QString res = process.readAllStandardOutput();
qDebug() << "Результат:" << res;Добавим:
#include <QProcess>Запустим. В консоли получим:
Запуск
Запущено
Результат: " ??? ? ?????? C ?? ????? ???.\r\n ??\u0A29??? ????? ??: 5A32-0ABC\r\n\r\n ???????? ????? C:\\windows\r\n\r\n21.07.2025 12:42 <DIR> .\r\n21.07.2025 12:42 <DIR> ..\r\n08.12.2019 01:37 <DIR> addins\r\n15.11.2023 13:57 <DIR> ADFS\r\n21.10.2024
…
276 WindowsUpdate.log\r\n07.12.2019 20:10 11?776 winhlp32.exe\r\n13.07.2025 12:11 <DIR> WinSxS\r\n08.12.2019 01:39 316?640 WMSysPr9.prx\r\n07.12.2019 08:29 11?264 write.exe\r\n 31 ???? 12?525?295 ????\r\n 79 ????? 13?860?933?632 ???? \u18AE?????\r\n"Здесь я обрезал вывод.
Вроде всё работает, но тут может возникнуть проблема – блокировка основного потока программы.
Чтобы это продемонстрировать создадим файл command.cmd в папке сборки программы.
Для этого в QtCreator откроем вкладку terminal:
Введем команды
cd build
dirПерейдем в папку для сборки, у меня это
Work_Desktop_Qt_6_9_1_shared_MinGW_w64_MINGW64_MSYS2-Debug
Название папки зависит от названия комплекта Qt.
Введем команды:
cd Work_Desktop_Qt_6_9_1_shared_MinGW_w64_MINGW64_MSYS2-Debugnotepad command.cmdВведем новое содержимое в окно блокнота:
sleep 5
dir c:\windowsСохраним файл.
Обратите внимание! Файл command.cmd должен быть создан в папке с файлом appextprogrun1.exe. Если у вас при запуске программы ничего не происходит, проверьте был ли создан .cmd файл в нужной папке!
Теперь если мы запустим скрипт:
command.cmdТо список файлов будет выведен только по прошествии пяти секунд.
Изменим код для запуска нового скрипта в main.cpp:
QProcess process;
QStringList params;
params.append("");
qDebug() << "Запуск";
process.start("command.cmd", params);
qDebug() << "Запущено";
process.waitForFinished();
QString res = process.readAllStandardOutput();
qDebug() << "Результат:" << res;Запустим.
Сначала появится надписи:
Запуск
ЗапущеноИ только по прошествии пяти секунд вывод команды dir
Обратите внимание, что на время выполнения скрипта окно программы недоступно! Окно будет пустым, а кнопка и индикатор не загрузятся до тех пор, пока не будет выполнен скрипт!
Чтобы такой ситуации не возникало, все задачи, на выполнение которых требуется длительное время или ресурсы пк, необходимо запускать в отдельной нити.
Упаковываем код в класс
Для того, чтобы облегчить себе жизнь упакуем код запуска команды в класс.
Создадим класс Runner
Заголовок
#ifndef RUNNER_H
#define RUNNER_H
#include <QObject>
#include <QProcess>
class Runner : public QObject
{
Q_OBJECT
public:
Runner();
void run();
private:
QProcess *process;
};
#endif // RUNNER_HРеализация
#include "runner.h"
#include <QDebug>
Runner::Runner() {
this->process = new QProcess(this);
qDebug() << "Инит";
}
void Runner::run()
{
QStringList params;
qDebug() << "Запуск";
this->process->start("command.cmd", params);
qDebug() << "Запущено";
this->process->waitForFinished();
QString res = this->process->readAllStandardOutput();
qDebug() << "Результат:" << res;
}В файле main.cpp старый код заменим на
Runner r;
r.run();Добавим также
#include "runner.h"Запустим. Результат тот же, но кода в главной функции всего две строки!
Запуск внешней программы в отдельном потоке.
Чтобы запустить наш класс в отдельной нити, нам нужно произвести некоторые модификации кода.
Дело в том, что после запуска нити, она будет продолжать исполняться, даже после того, как наш код завершит работу. Если не дать указание нити на завершение работы, то после закрытия приложения мы получим ошибку:
QThread: Destroyed while thread '' is still runningА это уже может привести к утечкам памяти и непредсказуемому поведению при выполнении программы!
В класс Runner добавим сигнал:
signals:
void finished();В конец метода Runner::run() добавим отправку сигнала:
emit finished();В результате получим:
Заголовок:
#ifndef RUNNER_H
#define RUNNER_H
#include <QObject>
#include <QProcess>
class Runner : public QObject
{
Q_OBJECT
public:
explicit Runner(QObject *parent = 0);
void run();
private:
QProcess *process;
signals:
void finished();
};
#endif // RUNNER_HРеализация:
#include "runner.h"
#include <QDebug>
Runner::Runner(QObject *parent) :
QObject(parent)
{
this->process = new QProcess(this);
qDebug() << "Инит";
}
void Runner::run()
{
QStringList params;
qDebug() << "Запуск";
this->process->start("command.cmd", params);
qDebug() << "Запущено";
this->process->waitForFinished();
QString res = this->process->readAllStandardOutput();
qDebug() << "Результат:" << res;
emit finished();
}В файле main.cpp заменим код на:
QThread thread;
thread.setObjectName("Runner thread");
Runner r;
QObject::connect(&thread, &QThread::started, &r, &Runner::run);
QObject::connect(&r, &Runner::finished, &thread, &QThread::terminate);
r.moveToThread(&thread);
thread.start();Добавим:
#include <QThread>Запустим. Теперь окно программы доступно для взаимодействия даже при выполнении задачи класса Runner!
Заключение
Сегодня мы рассмотрели запуск сторонней программы из Qt6:
Добавили тестовую форму в наш проект QtQuick;
Написали простейший код для запуска команды операционной системы dir;
Написали скрипт для симуляции длительной работы внешней программы;
Проверили блокировку интерфейса при работе длительно исполняющегося скрипта;
Упаковали код для запуска сторонней программы в класс C++;
Проверили работоспособность класса;
Внесли изменения в класс Runner для работы с QThread – добавили сигнал окончания обработки нашего скрипта.
Добавили код для запуска нити.
В следующей статье мы рассмотрим обработку результатов работы сторонней программы.
Добавить комментарий