Оптимизируем код Runner и упаковываем в класс код QThread. Запуск внешних программ в Qt6. Часть 3
В прошлой статье мы рассмотрели использование нитей для запуска внешних программ в Qt6.
Наш код работает, но его можно оптимизировать, обернув в отдельные классы.
Сегодня мы доработаем класс Runner, чтобы он мог запускать любые программы и обернем код создания нити в класс Thread.
Доработка класса Runner для запуска любых программ
Добавим приватные поля класса:
private:
bool isCodec = false;
QString codecName;
QString program;
QStringList params;isCodec – определяет нужно ли использовать перекодировку в Юникод
codecName – определяет имя кодировки, в которой программа выводит данные, для старых программ в русском Windows это "IBM 866".
program – указывает имя файла запускаемой программы
params – указывает параметры для запуска программы
Объявим методы для работы с этими полями:
public:
bool getCodecUsed() const;
void codecUsed(bool newIsCodec);
QString getCodecName() const;
void setCodecName(const QString &newCodecName);
QString getProgram() const;
void setProgram(const QString &newProgram);
QStringList getParams() const;
void setParams(const QStringList &newParams);
Их реализация:
bool Runner::getCodecUsed() const
{
return isCodec;
}
void Runner::codecUsed(bool newIsCodec)
{
isCodec = newIsCodec;
}
QString Runner::getCodecName() const
{
return codecName;
}
void Runner::setCodecName(const QString &newCodecName)
{
codecName = newCodecName;
this->codec = QTextCodec::codecForName(this->codecName.toLatin1().data());
}
QString Runner::getProgram() const
{
return program;
}
void Runner::setProgram(const QString &newProgram)
{
program = newProgram;
}
QStringList Runner::getParams() const
{
return params;
}
void Runner::setParams(const QStringList &newParams)
{
params = newParams;
}Изменим метод run()
void Runner::run()
{
qDebug() << "Запуск";
this->process->start(this->program, this->params);
qDebug() << "Запущено";
}Изменим метод onReady()
void Runner::onReady()
{
QString res;
if (this->isCodec == true) {
res = this->codec->toUnicode(this->process->readAllStandardOutput());
} else {
res = this->process->readAllStandardOutput();
}
qDebug() << "Результат:" << res;
emit ready(res);
}Изменим метод onError()
void Runner::onError()
{
QString res;
if (this->isCodec == true) {
res = this->codec->toUnicode(this->process->readAllStandardError());
} else {
res = this->process->readAllStandardError();
}
qDebug() << "Ошибка:" << res;
emit error(res);
}Внесём изменения в конструктор класса:
Runner::Runner(QObject *parent) :
QObject(parent)
{
this->process = new QProcess(this);
this->codecName = "IBM 866";
this->codec = QTextCodec::codecForName(this->codecName.toLatin1().data());
connect(this->process, SIGNAL(readyReadStandardOutput()),
this, SLOT(onReady()));
connect(this->process, SIGNAL(readyReadStandardError()),
this, SLOT(onError()));
connect(this->process, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(onFinished(int,QProcess::ExitStatus)));
qDebug() << "Инит";
}У нас получится:
Заголовок:
#ifndef RUNNER_H
#define RUNNER_H
#include <QObject>
#include <QProcess>
#include <QTextCodec>
class Runner : public QObject
{
Q_OBJECT
public:
explicit Runner(QObject *parent = 0);
void run();
bool getCodecUsed() const;
void codecUsed(bool newIsCodec);
QString getCodecName() const;
void setCodecName(const QString &newCodecName);
QString getProgram() const;
void setProgram(const QString &newProgram);
QStringList getParams() const;
void setParams(const QStringList &newParams);
private:
QProcess *process;
QTextCodec *codec;
bool isCodec = false;
QString codecName;
QString program;
QStringList params;
signals:
void finished();
void error(QString data);
void ready(QString data);
private slots:
void onReady();
void onError();
void onFinished(int exitCode, QProcess::ExitStatus exitStatus);
};
#endif // RUNNER_HРеализация:
#include "runner.h"
#include <QDebug>
Runner::Runner(QObject *parent) :
QObject(parent)
{
this->process = new QProcess(this);
this->codecName = "IBM 866";
this->codec = QTextCodec::codecForName(this->codecName.toLatin1().data());
connect(this->process, SIGNAL(readyReadStandardOutput()),
this, SLOT(onReady()));
connect(this->process, SIGNAL(readyReadStandardError()),
this, SLOT(onError()));
connect(this->process, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(onFinished(int,QProcess::ExitStatus)));
qDebug() << "Инит";
}
bool Runner::getCodecUsed() const
{
return isCodec;
}
void Runner::codecUsed(bool newIsCodec)
{
isCodec = newIsCodec;
}
QString Runner::getCodecName() const
{
return codecName;
}
void Runner::setCodecName(const QString &newCodecName)
{
codecName = newCodecName;
this->codec = QTextCodec::codecForName(this->codecName.toLatin1().data());
}
QString Runner::getProgram() const
{
return program;
}
void Runner::setProgram(const QString &newProgram)
{
program = newProgram;
}
QStringList Runner::getParams() const
{
return params;
}
void Runner::setParams(const QStringList &newParams)
{
params = newParams;
}
void Runner::run()
{
qDebug() << "Запуск";
this->process->start(this->program, this->params);
qDebug() << "Запущено";
}
void Runner::onReady()
{
QString res;
if (this->isCodec == true) {
res = this->codec->toUnicode(this->process->readAllStandardOutput());
} else {
res = this->process->readAllStandardOutput();
}
qDebug() << "Результат:" << res;
emit ready(res);
}
void Runner::onError()
{
QString res;
if (this->isCodec == true) {
res = this->codec->toUnicode(this->process->readAllStandardError());
} else {
res = this->process->readAllStandardError();
}
qDebug() << "Ошибка:" << res;
emit error(res);
}
void Runner::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
qDebug() << "Код завершения" << exitCode;
qDebug() << "Статус выхода" << exitStatus;
emit finished();
}В файле main.cpp изменим код запуска:
QThread thread;
thread.setObjectName("Runner thread");
Runner r;
r.codecUsed(true);
QStringList params;
params.append("/C dir c:\windows");
r.setProgram("cmd");
r.setParams(params);
QObject::connect(&thread, &QThread::started, &r, &Runner::run);
QObject::connect(&r, &Runner::finished, &thread, &QThread::terminate);
r.moveToThread(&thread);
thread.start();Запустим. Всё работает, как обычно, но теперь мы можем запустить любую другую программу.
Проверим, для этого изменим строку с параметром для запуска на:
params.append("/C type c:\\windows\\windowsupdate.log"); Обратите внимание, мы экранируем \ еще одним \\ это важно!
Запустим:
Инит
Запуск
Запущено
Результат: "Windows Update logs are now generated using ETW (Event Tracing for Windows).\r\nPlease run the Get-WindowsUpdateLog PowerShell command to convert ETW traces into a readable WindowsUpdate.log.\r\n\r\n\r\nFor more information, please visit https://go.microsoft.com/fwlink/?LinkId=518345"
Код завершения 0
Статус выхода QProcess::NormalExitВсё работает.
Упаковываем код для запуска внешней программы в отдельный класс Thread
У нас опять скопилось много кода в файле main.cpp, пришла пора его упаковать в отдельный класс.
Создадим новый класс - Thread
Заголовок:
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include "runner.h"
class Thread : public QObject
{
Q_OBJECT
public:
Thread(QString program, QStringList params, Runner *runner);
void start();
private:
QThread *thread;
Runner *runner;
QString program;
QStringList params;
};
#endif // THREAD_HРеализация:
#include "thread.h"
Thread::Thread(const QString program, QStringList params, Runner *runner) {
this->thread = new QThread();
this->thread->setObjectName("Runner thread");
this->runner = runner;
this->program = program;
this->params = params;
this->runner->codecUsed(true);
this->runner->setProgram(this->program);
this->runner->setParams(this->params);
QObject::connect(this->thread, QThread::started, this->runner, Runner::run);
QObject::connect(this->runner, Runner::finished, this->thread, QThread::terminate);
this->runner->moveToThread(thread);
}
void Thread::start()
{
this->thread->start();
}В main.cpp заменим код на:
QStringList params;
params.append("/C command.cmd");
Thread *t = new Thread("cmd", params, new Runner());
t->start();Запустим, результат будет таким же, но кода в главном файле меньше!
Заключение
Сегодня мы доработали класс Runner:
Добавили приватные поля для хранения имени файла запускаемой программы, параметров для запуска, имени кодека для перекодирования вывода программы, а также поле, определяющее нужно ли применять перекодировку вывода;
Реализовали соответствующие методы в классе Runner;
Внесли изменения в существующие методы, чтобы обеспечить работу с новыми полями;
Добавили новый класс Thread;
Заменили код для запуска в main.cpp с использованием нового класса.
Добавить комментарий