Вторник, 29.07.2025 13:03

Запускаем внешнюю программу в Qt6. Запуск внешних программ в Qt6. Часть 1.

Запускаем внешнюю программу в 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-Debug
notepad 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 – добавили сигнал окончания обработки нашего скрипта.

Добавили код для запуска нити.

В следующей статье мы рассмотрим обработку результатов работы сторонней программы.

>> Часть 2

Категория Qt6
Теги Qt Qt6 Cpp

Добавить комментарий

Простой текст

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Строки и абзацы переносятся автоматически.
  • Адреса веб-страниц и email-адреса преобразовываются в ссылки автоматически.
Просмотров: 465