Обработка вывода и ошибок, класс Worker. Доработка класса Thread. Запуск внешних программ в Qt6. Часть 4
До сих пор мы только просматривали результат работы и никак его не обрабатывали, не считая вывода текста в окно отладки.
Сегодня мы рассмотрим создание класса для обработки вывода и ошибок Worker, а также доработаем класс Thread для использования класса Worker.
Добавление класса Worker
Для обработки результата мы будем использовать сигналы onRead и onError, которые соединим со слотами нового класса – Worker. Данный класс будет нами в дальнейшем использоваться для разбора (парсинга) результатов работы запускаемых внешних программ.
Создадим новый класс Worker.
Заголовок:
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class Worker: public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
public slots:
void onRead(const QString &data);
void onError(const QString &data);
};
#endif // WORKER_HРеализация:
#include "worker.h"
#include <QDebug>
Worker::Worker(QObject *parent) :
QObject(parent)
{
}
void Worker::onRead(const QString &data)
{
qDebug() << "Worker:" << data;
}
void Worker::onError(const QString &data)
{
qDebug() << "Worker error:" << data;
}В классе Runner закомментируем строки:
qDebug() << "Результат:" << res;
qDebug() << "Ошибка:" << res;Все ошибки будут обрабатываться классом Worker!
В файл main.cpp добавим:
Worker w;
Runner r;Изменим и добавим код:
QStringList params;
params.append("/C command.cmd");
QObject::connect(&r, &Runner::ready, &w, &Worker::onRead);
QObject::connect(&r, &Runner::error, &w, &Worker::onError);
Thread *t = new Thread("cmd", params, &r);
t->start();Запустим:
Инит
Запуск
Запущено
Worker: "\r\nD:\\projects\\QtRunnerDemo3\\build\\Work_Desktop_Qt_6_9_1_shared_MinGW_w64_MINGW64_MSYS2-Debug>sleep 1 \r\n"
Worker: "\r\nD:\\projects\\QtRunnerDemo3\\build\\Work_Desktop_Qt_6_9_1_shared_MinGW_w64_MINGW64_MSYS2-Debug>dir c:\\windows \r\n"
Worker: " Том в устройстве C не имеет метки.\r\n Серийный номер тома: 5A32-0ABC\r\n"
Worker: "\r\n Содержимое папки c:\\windows\r\n\r\n"
Worker: "21.07.2025 12:42 <DIR> ."
…
Worker: "07.12.2019 08:29 11 264 write.exe"
Worker: "\r\n"
Worker: " 31 файлов 12 657 809 байт\r\n"
Worker: " 79 папок 8 305 139 712 байт свободно\r\n"
Код завершения 0
Статус выхода QProcess::NormalExitДобавим ошибку, изменим строку на:
params.append("/C command.cmd1");Запустим:
Инит
Запуск
Запущено
Код завершения 1
Статус выхода QProcess::NormalExit
Worker error: "\"command.cmd1\"\" не является внутренней или внешней\r\nкомандой, исполняемой программой или пакетным файлом.\r\n"Теперь Worker обрабатывает и вывод и ошибки.
Вернем всё как было:
params.append("/C command.cmd");Обработка ошибок запуска программы
В предыдущей части мы рассмотрели обработку ошибок при выполнении программы, но также ошибки могут возникать до во время запуска программы. Например, при указании неверного имени исполняемого файла.
Если мы изменим имя запускаемой программы в строке:
Thread *t = new Thread("cmd1", params, &r);и запустим программу мы получим:
Инит
Запуск
ЗапущеноИ больше ничего не происходит. Вся причина в том, что при возникновении ошибок запуска с помощью QProcess отправляется отдельный сигнал:
void QProcess::errorOccurred(QProcess::ProcessError error)Так как мы его до сих пор не обрабатывали, то и результата нет.
В класс Runner добавим слот:
void onProcessError(QProcess::ProcessError errorCode);Реализация:
void Runner::onProcessError(QProcess::ProcessError errorCode)
{
QString res;
res.append("Ошибка запуска программы: ");
res.append(QString::number(errorCode));
emit error(res);
onFinished(1, QProcess::ExitStatus::CrashExit);
}Обратите внимание, мы вызываем:
onFinished(1, QProcess::ExitStatus::CrashExit);Это необходимо сделать, так как при таком типе ошибок дальнейшая работа нити не имеет смысла.
В конструктор класса Runner добавим:
connect(this->process, SIGNAL(errorOccurred(QProcess::ProcessError )),
this, SLOT(onProcessError(QProcess::ProcessError )));Запустим:
Инит
Запуск
Код завершения 1
Статус выхода QProcess::CrashExit
Запущено
Worker error: "Ошибка запуска программы: 0"Теперь мы получили ошибку и её код. Так как нить завершила свою работу с сообщением об ошибке, было бы неплохо обработать её код.
Перечисление QProcess::ProcessError предоставляет несколько значений:
QProcess::FailedToStart 0
QProcess::Crashed 1
QProcess::Timedout 2
QProcess::WriteError 4
QProcess::ReadError 3
QProcess::UnknownError 5Наша программа должна иметь возможность обработать эти коды, и при необходимости, например, перезапустить внешнюю программу или сама завершится с ошибкой.
В класс Runner добавим сигнал:
void errorFinished(QProcess::ProcessError errorCode);В конец метода void Runner::onProcessError(QProcess::ProcessError errorCode) после emit добавим:
emit errorFinished(errorCode);В main.cpp добавим
QObject::connect(&r, &Runner::errorFinished, &w, &Worker::onErrorFinished);В класс Worker добавим слот:
void onErrorFinished(const QProcess::ProcessError &errorCode);Реализация:
void Worker::onErrorFinished(const QProcess::ProcessError &errorCode)
{
qDebug() << "Worker: Внешняя программа завершилась с кодом ошибки:" << errorCode;
}Запустим:
Инит
Запуск
Код завершения 1
Статус выхода QProcess::CrashExit
Запущено
Worker error: "Ошибка запуска программы: 0"
Worker: Внешняя программа завершилась с кодом ошибки: QProcess::FailedToStartДоступ к коду завершения программы
Во время выполнения программы возможно возникновение разных ситуаций. Результат внутренней ошибки, неверно заданных параметров и так далее. Чтобы иметь возможность обработать результат выполнения программы мы так же должны отправить этот код в виде сигнала.
Мы не сможем использовать для этого сигнал finished так как он уже используется со слотом QThread::terminate у которого нет параметров.
В класс Runner добавим сигнал:
signals:
void programFinished(int exitCode, QProcess::ExitStatus exitStatus);В слот onFinished(int exitCode, QProcess::ExitStatus exitStatus) добавим:
emit programFinished(exitCode, exitStatus);В класс Worker добавим слот:
public slots:
void onProgramFinished(const int &exitCode, const QProcess::ExitStatus &exitStatus);Реализация:
void Worker::onProgramFinished(const int &exitCode, const QProcess::ExitStatus &exitStatus)
{
qDebug() << "Worker: Внешняя программа успешно завершилась с кодом:" << exitCode << "и статусом " << exitStatus;
}Дорабатываем класс Thread
Добавим в класс Thread возможность использование нашего класса Worker.
Добавим новое поле:
Worker *worker;В конструктор добавим:
this->worker = worker;и соединение сигналов со слотами:
QObject::connect(this->runner, Runner::ready, this->worker, Worker::onRead);
QObject::connect(this->runner, Runner::error, this->worker, Worker::onError);
QObject::connect(this->runner, Runner::errorFinished, this->worker, Worker::onErrorFinished);
QObject::connect(this->runner, Runner::programFinished, this->worker, Worker::onProgramFinished);Изменим объявление конструктора класса на:
Thread(QString program, QStringList params, Runner *runner, Worker *worker);В main.cpp заменим код на:
QStringList params;
params.append("/C command.cmd");
Thread *t = new Thread("cmd", params, new Runner(), new Worker());
t->start();Запустим, результат будет таким же.
Заключение
Сегодня мы рассмотрели создание класса Worker, который может использоваться для обработки ошибок и вывода запущенной вешней программы:
Создали класс Worker;
Добавили связь между сигналами класса Runner и слотами класса Worker;
Добавили обработку ошибок, возникающих при запуске самой внешней программы;
Доработали класс Thread для использования класса Worker.
Добавить комментарий