Понедельник, 04.08.2025 19:00

Оптимизируем код Runner и упаковываем в класс код QThread. Запуск внешних программ в Qt6. Часть 3

Оптимизируем код 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 с использованием нового класса.

>> Часть 4

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

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

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

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