Разрабатываем консольную утилиту с использованием 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->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.
Добавить комментарий