Суббота, 27.01.2024 18:28

Разрабатываем консольную утилиту с использованием Qt 6.6.0 в РЕДОС Linux

Разрабатываем консольную утилиту с использованием Qt 6.6.0 в РЕДОС Linux

В прошлых статьях мы установили фреймворк Qt 6.6.0 и IDE QtCreator. Рассмотрели настройку доступа к удаленному серверу по VNC. Пришло время свести всё это вместе и заняться непосредственно программированием.

Сегодня мы начнем разработку новой полезной утилиты, которая позволит нам автоматизировать процесс создания ярлыков на рабочем столе. Это будет небольшая и простая программа, на примере которой рассмотрим особенности разработки, распространения и установки программ под Linux, в нашем случае РЕДОС Linux.

Создаем консольный проект

Запустим QtCreator и создадим новый проект. На этот раз выберем пункт:

Консольное приложение Qt
Изображение удалено.

Назовем его:

Addlink

Приведем файл main.cpp к виду:

#include <QCoreApplication>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "TEST!";

    return a.exec();
}

Запустим:

Изображение удалено.

Мы столкнулись с проблемой, программа запускается, но не завершает свою работу.

Мы не получаем сообщения о завершении работы программы:

00:00:00: /home/user/Projects/build-addlink-Desktop-Debug/addlink exited with code 0

Это происходит потому, что после команды a.exec() происходит запуск бесконечного цикла, в котором и работает любая программа написанная на Qt. Мы же не посылаем никаких сигналов в этом цикле, чтобы завершить программу!

Даже если мы добавим:

QCoreApplication::quit();

Перед:

    return a.exec();

Это ни к чему не приведет, так как отправка сигнала произойдет до инициализации цикла!

Нам нужен способ запустить наш код после того, как программа будет инициализирована и начнет принимать сигналы и реагировать на события.

Создание класса ConsoleRunner

Создадим класс ConsoleRunner.

Объявление:

#ifndef CONSOLERUNNER_H
#define CONSOLERUNNER_H

#include <QObject>

class ConsoleRunner : public QObject
{
    Q_OBJECT
public slots:
    void run(char *argv[]);
};

#endif // CONSOLERUNNER_H

Реализация:

#include <QCoreApplication>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QString>

#include <QStandardPaths>
#include <QDir>

#include "consolerunner.h"
#include <unistd.h>
#include <iostream>

void ConsoleRunner::run(char *argv[])
{
    qDebug() << "TEST!";
    QCoreApplication::quit();
}

Приведем main.cpp к виду:

#include <QCoreApplication>
#include <QString>
#include <iostream>
#include <QTimer>

#include "consolerunner.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);


    ConsoleRunner *r = new ConsoleRunner();

    QTimer::singleShot(0, r, [argv, r](){ r->run(argv); });

    return a.exec();
}

Здесь всё просто. Мы создаем указатель на экземпляр класса ConsoleRunner.

Затем используем QTimer::singleShot чтобы сразу, как только станет возможным, запустить метод ConsoleRunner::run()

Единственное что немного сложно для понимания это код:

[argv, r](){ r->run(argv); }

Лямбда-функция

На самом деле это простая лямбда-функция, поддержка которых  появилась в C++11.

Если упрощать, то этот код можно развернуть в подобие:

QObject * function(argv, r) {
r->run(argv);
}

На самом деле это не совсем верное представление, более точным будет:

QObject * function() {
r->run(argv);
}

Так как:

argv, r

Это не параметры, а так называемые захваченные (captured) переменные!

Для использования в лямбда-функции внешних переменный их список обязательно нужно указывать в квадратных скобках []. В противном случае, мы получим такую ошибку:

/home/user/Projects/addlink/main.cpp</a>:15:40: error: ?r? is not captured
   15 |     QTimer::singleShot(0, r, [argv](){ r-&gt;run(argv); });
      |                                        ^

Запустим сборку нашего проекта:

Ctrl+R

В консоли программа выведет:

00:00:09: Starting /home/user/project1/build-addlink-Desktop-Debug/addlink...
TEST!
00:00:09: /home/user/project1/build-addlink-Desktop-Debug/addlink exited with code 0

На этот раз программа отработала как и ожидалось.

Как работают ярлыки рабочего стола в Linux

Для того, чтобы добавить ярлык на рабочий стол в Linux нам нужно создать в папке:

~/Рабочий стол

Файл с расширением:

.desktop

Для примера приведу файл создающий ярлык для запуска QtCreator:

[Desktop Entry]
Encoding=UTF-8
Name=QtCreator
Comment=Среда разработки QtCreator
Icon=qtcreator
Exec=/usr/local/qtcreator1201/bin/qtcreator
Terminal=false
StartupNotify=true
Type=Application
Categories=

Это минимум настроек, с помощью которых мы можем получить рабочий ярлык!

Рассмотрим подробнее:

Encoding=UTF-8 - эта строка будет всегда неизменной;

Name=QtCreator – задает текст, который будет отображаться на рабочем столе;

Comment=Среда разработки QtCreator - комментарий – в ярлыках рабочего стола не используется, работает только для пунктов главного меню;

Icon=qtcreator - значок который будет отображаться на рабочем столе.

Разработчики РЕДОС уже включили иконку для qtcreator в стандартную поставку, вы можете найти её в папке:

/usr/share/icons/breeze/apps/48/qtcreator.svg

Для своей программы, вам придется создать свой значок и разместить его в папке:

/usr/share/icons/breeze/apps/48/

Exec=/usr/local/qtcreator1201/bin/qtcreator – полный путь к запускаемой программе;

Terminal=false – будет ли открываться окно консоли при запуске программы, может быть удобно при отладке, для графических программ должно быть установлено в false;

StartupNotify=true - Если true то при запуске изменяется курсор мыши сигнализируя о том, что происходит запуск приложения;

Type=Application – для программ всегда указываем Application;

Categories= – используется для меню, можно оставить пустым!

Добавляем шаблон для файла .desktop

В начало метода:

run()

Добавим код:

QString fileTemplate;
fileTemplate = QString("[Desktop Entry]\n" \
                           "Encoding=UTF-8\n" \
                           "Name=%1\n" \
                           "Comment=%1\n" \
                           "Icon=%1\n" \
                           "Exec=%2\n" \
                           "Terminal=false\n" \
                           "StartupNotify=false\n" \
                           "Type=Application\n" \
                           "Categories=");
QString desktopFile;
desktopFile =  fileTemplate.arg("qtcreator", "/usr/local/qtcreator1201/bin/qtcreator");
qDebug() << desktopFile;

Запустим:

"[Desktop Entry]\nEncoding=UTF-8\nName=qtcreator\nComment=qtcreator\nIcon=qtcreator\nExec=/usr/local/qtcreator1201/bin/qtcreator\nTerminal=false\nStartupNotify=false\nType=Application\nCategories="

Метод arg() заменяет все встреченные значения %1, %2 и так далее по порядку, на соответствующие параметры передаваемые самому методу!

Запишем информацию в файл:

    QFile p("qtcreator.desktop");
    if (p.open(QFile::WriteOnly | QIODevice::Text)) {
        QTextStream s(&p);
        s << desktopFile;
    }
    p.close();

Запустим, в папке:

../build-addlink-Desktop-Debug 

будет создан файл:

qtcreator.desktop

С содержимым:

[Desktop Entry]
Encoding=UTF-8
Name=qtcreator
Comment=qtcreator
Icon=qtcreator
Exec=/usr/local/qtcreator1201/bin/qtcreator
Terminal=false
StartupNotify=false
Type=Application
Categories=

Сохранение файла в домашнюю папку пользователя

Чтобы ярлык появился на рабочем столе, нужно добавить его в папку:

~/Рабочий стол

Получим абсолютный путь к этой папке. Добавим в начало метода run():

    QStringList homePath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
    qDebug() << homePath.first();

Запустим:

"/home/user"

Изменим код:

    QFile p(homePath.first().append("/Рабочий стол/").append("qtcreator.desktop"));

    if (p.open(QFile::WriteOnly | QIODevice::Text)) {
        QTextStream s(&p);
        s << desktopFile;
    }

    p.close();

Запустим. Файл будет создан в папке:

/home/user/Рабочий стол/

Разбираем параметры командной строки

В Qt для разбора командной строки есть специальный класс - QCommandLineParser, но его мы будем рассматривать в следующей статье, сегодня мы просто воспользуемся параметрами передаваемыми в функцию main().

Изменим вызов QTimer::singleShot:

    QTimer::singleShot(0, r, [argc, argv, r](){

        if (argc == 2 ) {
            r->run(argv);
        } else {
            std::cout << "Не найден обязательный параметр!\n";
            std::cout << "Первый параметр должен задавать полный путь до запускаемой программы!\n";

            QCoreApplication::quit();
        }
    });

Запустим:

Не найден обязательный параметр!
Первый параметр должен задавать полный путь до запускаемой программы!

Здесь мы просто проверяем значение:

argc

Параметр argc всегда равен 1, если в командной строке для программы не указаны параметры!

Для каждого указанного параметра командной строки значение увеличивается на 1.

Так как мы ожидаем, что параметр у нас будет только один, то и значение argc в этом случае должно быть равно 2.

Добавление параметров командной строки для программы в QtCreator

Чтобы протестировать нашу программу нам нужно указать для нее параметры командной строки, для этого нажмем на кнопку Проекты:

Изображение удалено.

Выберем:

Запустить

В поле:

Параметры командной строки

Введем:

/usr/local/qtcreator1201/bin/qtcreator
Изображение удалено.

Нажмем на кнопку Редактор, чтобы вернуться обратно и сохраним проект.

Запустим – если удалить файл из папки /home/user/Рабочий стол/ то он будет снова создан.

Использование параметра командной строки в программе

Обратите внимание, мы просто проверили наличие параметра! Сегодня мы не будем рассматривать проверку параметра и его очистку от потенциально вредоносных символов. Для этого есть класс - QCommandLineParser использование которого мы рассмотрим в одной из следующих статей!

Проверка параметров это важная часть информационной безопасности! Если вы позволите передавать в параметрах программе строки любого вида, да еще и без фильтрации - это может привести к печальным последствиям. Злоумышленник может воспользоваться переполнением буфера для запуска любого произвольного кода.

Сегодня мы просто используем первый параметр в нашей программе.

Добавим в начало метода run() код:

    QFileInfo fileInfo(argv[1]);
    QString fileName;
    fileName = fileInfo.fileName();

Здесь с помощью класса QFileInfo мы разбираем строку, выделяем из нее имя файла и записываем в переменную fileName.

Внесем изменения в код:

    desktopFile = fileTemplate.arg(fileName, argv[1]);

    QFile p(homePath.first().append("/Рабочий стол/").append(fileName).append(".desktop"));

В результате метод run() у нас примет вид:

void ConsoleRunner::run(char *argv[])
{

    QFileInfo fileInfo(argv[1]);
    QString fileName;
    fileName = fileInfo.fileName();

    QStringList homePath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);

    QString fileTemplate;
    fileTemplate = QString("[Desktop Entry]\n" \
                           "Encoding=UTF-8\n" \
                           "Name=%1\n" \
                           "Comment=%1\n" \
                           "Icon=%1\n" \
                           "Exec=%2\n" \
                           "Terminal=false\n" \
                           "StartupNotify=false\n" \
                           "Type=Application\n" \
                           "Categories=");
    QString desktopFile;


    desktopFile = fileTemplate.arg(fileName, argv[1]);

    QFile p(homePath.first().append("/Рабочий стол/").append(fileName).append(".desktop"));

    if (p.open(QFile::WriteOnly | QIODevice::Text)) {
        QTextStream s(&p);
        s << desktopFile;
    }

    p.close();

    qDebug() << "TEST!";
    QCoreApplication::quit();
}

Запустим:

На рабочем столе будет создан ярлык:

Изображение удалено.

Давайте создадим ярлык для mc.

Откроем консоль и перейдем в папку:

cd ~/Projects/build-addlink-Desktop-Debug

Запустим:

./addlink mc
TEST!

Будет создан ярлык, но при щелчке на нем, сам mc не будет запущен.

Это происходит из-за того, что у нас по умолчанию указан параметр:

Terminal=false

Откроем файл:

mcedit ~/"Рабочий стол"/mc.desktop

Исправим

Terminal=false

На

Terminal=true

Теперь ярлык запускает mc:

Изображение удалено.

Обратите внимание, так как у mc нет своего значка в папке

/usr/share/icons/breeze/apps/48

То для него используется значок по-умолчанию.

Вот и всё на сегодня, в одной следующих статей мы рассмотрим класс QCommandLineParser для разбора параметров командной строки и добавим дополнительный функционал.

Заключение

Сегодня мы рассмотрели добавление ярлыка для запуска программы на рабочий стол РЕДОС Linux и написали простую программу для автоматизации этого процесса:

Создали проект консольного приложения Qt;

Рассмотрели использование QTimer::singleShot для запуска консольного приложения;

Рассмотрели использование лямбда-функций;

Рассмотрели добавление параметров командной строки для программы в QtCreator;

Рассмотрели добавление ярлыков на рабочий стол РЕДОС Linux;

Добавили в программу шаблон для файла .desktop

Получили абсолютный путь к домашней папке для текущего пользователя;

Доработали программу для проверки наличия первого параметра командной строки;

Получили имя файла из первого параметра;

Использовали параметр для создания .desktop-файла;

Проверили работу программы создав ярлык для консольной программы mc.

Категория Qt6

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

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

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