Программирование приложений — только с Qt! (+ пример приложения отправки горячих клавиш)

Привет всем!

Так уж сложилось, что до сих пор мой опыт программирования предназначался для создания узкоспециализированных приложений. Так, профессиональное знание Simpl+ и NetLinx помогает мне писать модули и программы для контроллеров Crestron и AMX, базовые знания PHP и MySQL раньше помогали создавать простые веб-сайты, а сейчас помогают писать небольшие скрипты для сетевых накопителей, классический С я использую в своих хобби-поделках для программирования 8-битных микроконтроллеров Silabs. Но, параллельно с этим, я все время хотел найти какую-нибудь среду для написания десктопных приложений.

Познакомьтесь, Qt! Кроссплатформенная среда для написания приложений на языке С++. Один и тот же код можно без изменений откомпилировать для Windows / Mac / Linux. Qt доступен как под коммерческой, так и под открытой лицензией. А последние планы по развитию среды впечатляют еще больше! Разработчики Qt поставили перед собой задачу поддержки почти всех основных мобильных платформ: iOS, Android и даже Windows RT! Таким образом, разработчик, владеющий инструментарием Qt, будет способен писать приложения "вообще для всего"!

Такие возможности мне показались убедительными для того, чтобы еще несколько лет назад купить в магазине учебник по Qt и попробовать освоить базовые концепции языка / среды в действии. Я написал несколько простейших программ, одной их которых хочу поделиться со всеми.

Программа Send Hotkey должна имитировать нажатие горячих клавиш на ПК, где она запущена. Основные требования к данной программы такие:

  • автоматический запуск с Windows, блокирование запуска второго экземпляра;
  • автоматическое сворачивание в системный трей, простое контекстное меню в трее;
  • настройка порта для взаимодействия с внешней системой управления, сохранение номера порта в системном реестре;
  • встроенная справка с примерами взаимодействия с программой;
  • простая инсталляция / деинсталляция.

У меня нет цели выложить полные исходники данной программы, но основной файл "window.cpp" оказался менее 100 строк. Привожу его ниже:

#include "window.h"
#include "ui_window.h"

// конструктор
Window::Window(QWidget *parent):QDialog(parent), ui(new Ui::Window)
{

// создаем объект типа QSharedMemory (строковой ключ сгенерирован заранее случайным образом)
 sm = new QSharedMemory("0E94CC22-CC9D-4C61-B0F5-34C9E738C21D");
 if(sm->attach())
 {
// если смогли присоединиться к Shared Memory, значит это уже не первый экзмепляр программы,
// выводим сообщение и завершаем программу
 QMessageBox::information(this, "Notice", "Already running in system tray!");
 QTimer::singleShot(250, qApp, SLOT(quit()));
 return;
 }
 else
 {
// иначе это первый экземпляр программы, создаем Shared Memory длиной 1 байт и продолжаем работу
 sm->create(1);
 }

// инилиализируем пользовательский интерфейс
 ui->setupUi(this);

// соединяем сигнал о наличии датаграмм UDP со слотом отправки горячих клавиш
 connect(&udpSocket, SIGNAL(readyRead()), this, SLOT(sendHotkeys()));

// соединяем сигнал об изменении номера порта UDP со слотом обработки этого события
// считываем номер порта из реестра, если он был изменен, иначе используем порт 50001
 connect(ui->portSpinBox, SIGNAL(valueChanged(int)), this, SLOT(portChanged()));
 settings = new QSettings("HKEY_LOCAL_MACHINE\\SOFTWARE\\Caesium Tools\\Send Hotkey", QSettings::NativeFormat);
 ui->portSpinBox->setValue(settings->value("UDP Port", "50001").toInt());

// создаем 3 пункта контекстного меню
 settingsAction = new QAction(tr("&Settings"), this);
 connect(settingsAction, SIGNAL(triggered()), this, SLOT(settingsClick()));
 helpAction = new QAction(tr("&Help"), this);
 connect(helpAction, SIGNAL(triggered()), this, SLOT(needHelp()));
 quitAction = new QAction(tr("&Quit"), this);
 connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));

// создаем само контекстное меню, добавляем в него созданные только что пункты
 trayIconMenu = new QMenu(this);
 trayIconMenu->addAction(settingsAction);
 trayIconMenu->addAction(helpAction);
 trayIconMenu->addAction(quitAction);

// создаем значок в систсемном трее, добавляем контекстное меню,
// делаем его видимым и сообщаем о нем пользователю
 trayIcon = new QSystemTrayIcon(this);
 trayIcon->setIcon(QIcon("button.png"));
 trayIcon->setContextMenu(trayIconMenu);
 trayIcon->show();
 trayIcon->showMessage("Send Hotkey", "Send Hotkey is running!");

// соединяем сигнал активации значка в систсемном трее со слотом обработки этого события
 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayIconClick(QSystemTrayIcon::ActivationReason)));

// соединяем сигнал клика на строчку Help со слотом обработки этого события
 connect(ui->helpLabel, SIGNAL(linkActivated(QString)), this, SLOT(needHelp()));

// устанавливаем флаги окна и значок диалогового окна
 this->setWindowFlags(Qt:: Dialog | Qt:: WindowStaysOnTopHint);
 this->setWindowIcon(QIcon("button.png"));
}

// стандартный деструктор
Window::~Window()
{
 delete ui;
}

// обработка события закрытия диалогового окна: просто скрываем окно
// и сообщаем пользователю, что программа продолжает работу в трее
void Window::closeEvent(QCloseEvent *event)
{
 this->hide();
 trayIcon->showMessage("Send Hotkey", "Send Hotkey continue running!");
 event->ignore();
}

// обрабатываем событие активации значка в трее: просто показываем диалоговое окно
void Window::trayIconClick(QSystemTrayIcon::ActivationReason reason)
{
 if(reason!=QSystemTrayIcon::Context) this->showNormal();
}

// обрабатываем пункт контекстного меню Settings
void Window::settingsClick()
{
 this->showNormal();
}

// обрабатываем смену порта, сохраняем новый порт в реестре, связываем новый порт с сокетом
void Window::portChanged(void)
{
 settings->setValue("UDP Port", ui->portSpinBox->value());
 udpSocket.close();
 udpSocket.bind(ui->portSpinBox->value());
}

// обработка датаграмм UDP, отправка кодов нажатия и отпускания клавиш в ОС
void Window::sendHotkeys(void)
{
 QByteArray b;
 b.resize(udpSocket.pendingDatagramSize());
 udpSocket.readDatagram(b.data(), b.size());
 for(int i=0; i<b.length(); i++) keybd_event(b[i], 0, 0, 0);
 for(int i=b.length()-1; i>=0; i--) keybd_event(b[i], 0, KEYEVENTF_KEYUP, 0);
}

// обработка зароса помощи (строка Help в диалоговом окне программы или пункт Help контекстного меню)
void Window::needHelp(void)
{
 this->hide();
 QProcess::execute("hh.exe help.chm");
}

В качестве инструмента для создания инсталлятора отлично подошел еще один свободный продукт - Inno Setup. Инсталляторы с его помощью создаются быстро и качественно, а множество встроенных примеров и понятная справка ускоряют процесс. В общем, рекомендую.

Ну и если уж вы дошли до этого текста, то, вероятно, для тестирования программы Send Hotkey вам пригодится отличная Freeware-утилита UDP Test Tool. Скачивается после регистрации.

Саму программу Send Hotkey можно скачать прямо здесь: Send_Hotkey_1.1.001_Setup. Я протестировал ее на виртуальных Windows XP SP2 и Windows 7 x64. Буду рад отзывам о реальном применении моей программы!

Написать ответ


[ Ctrl + Enter ]