Qt show application, если в данный момент запущено
Я создаю приложение с одним экземпляром, которое свернуто в системный трей, я хочу показать текущий запущенный экземпляр, а затем выйти из нового. Как я могу создать эту функциональность?
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QIcon>
#include <QQuickWidget>
#include <QSystemTrayIcon>
#include <QQmlContext>
#include <QQmlEngine>
#include <QSystemSemaphore>
#include <QSharedMemory>
// Declare a user-defined data type to work with an icon in QML
Q_DECLARE_METATYPE(QSystemTrayIcon::ActivationReason)
Q_DECL_EXPORT int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QApplication app(argc, argv);
QQmlApplicationEngine engine;
QSystemSemaphore semaphore("deploy", 1); // create semaphore
semaphore.acquire(); // Raise the semaphore, barring other instances to work with shared memory
#ifndef Q_OS_WIN32
// in linux / unix shared memory is not freed when the application terminates abnormally,
// so you need to get rid of the garbage
QSharedMemory nix_fix_shared_memory("deploy Shared Memory");
if(nix_fix_shared_memory.attach()){
nix_fix_shared_memory.detach();
}
#endif
QSharedMemory sharedMemory("deploy Shared Memory"); // Create a copy of the shared memory
bool is_running; // variable to test the already running application
if (sharedMemory.attach()){ // We are trying to attach a copy of the shared memory
// To an existing segment
is_running = true; // If successful, it determines that there is already a running instance
}else{
sharedMemory.create(1); // Otherwise allocate 1 byte of memory
is_running = false; // And determines that another instance is not running
}
semaphore.release();
// If you already run one instance of the application, then we inform the user about it
// and complete the current instance of the application
if(is_running){
return -1;
}
// Register QSystemTrayIcon in Qml
qmlRegisterType<QSystemTrayIcon>("QSystemTrayIcon", 1, 0, "QSystemTrayIcon");
// Register in QML the data type of click by tray icon
qRegisterMetaType<QSystemTrayIcon::ActivationReason>("ActivationReason");
// Set icon in the context of the engine
engine.rootContext()->setContextProperty("iconTray", QIcon(":/deploy.png"));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls 1.4 as Tray
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.2
import QtQuick.Window 2.0
import QSystemTrayIcon 1.0
Window {
visible: true
id: application
width: 640
height: 480
title: qsTr("Test")
// system tray
QSystemTrayIcon {
id: systemTray
// Initial initialization of the system tray
Component.onCompleted: {
icon = iconTray // Set icon
toolTip = "Deploy App"
show();
if(application.visibility === Window.Hidden) {
application.show()
} else {
application.hide()
}
}
/* By clicking on the tray icon define the left or right mouse button click was.
* If left, then hide or open the application window.
* If right, then open the System Tray menu
* */
onActivated: {
if(reason === 1){
trayMenu.popup()
} else {
if(application.visibility === Window.Hidden) {
application.show()
} else {
application.hide()
}
}
}
}
// Menu system tray
Tray.Menu {
id: trayMenu
Tray.MenuItem {
text: qsTr("Show App")
onTriggered: application.show()
}
Tray.MenuItem {
text: qsTr("Quit")
onTriggered: {
systemTray.hide()
Qt.quit()
}
}
}
}
Я попытался создать объект и установить его состояние выполнения на ложь или истина в моем main.cpp
и в моем main.qml
Я проверяю значение и выхожу из приложения.
QQmlApplicationEngine engine;
QQmlContext *context = engine.rootContext();
SingleInstance singleInstance;
context->setContextProperty("SingleInstance", &singleInstance);
if (is_running) {
singleInstance.running(true);
В моем main.qml
Я проверяю, запущено ли приложение.
Connections {
target: SingleInstance
}
Component.onCompleted: {
if (SingleInstance.running) {
if(application.visibility === Window.Hidden) {
Qt.quit()
}
}
}
2 ответа
То, что мы в итоге сделали для одного экземпляра, это использование QLocalSocket, который называется именованным каналом в Windows и сокетом локального домена в Unix вместо QSharedMemory
который требует больше настроек, чтобы сделать это правильно. В основном вы делаете что-то вроде этого, чтобы проверить, работает ли сервер (appName
должен быть уникальный идентификатор приложения, вы можете использовать, например, QCoreApplication:: applicationName):
bool isSingleInstanceRunning(QString appName) {
QLocalSocket socket;
socket.connectToServer(m_appName);
bool isOpen = socket.isOpen();
socket.close();
return isOpen;
}
Если вы получаете false
вы создадите свой собственный сервер (сохраняйте этот экземпляр при жизни вашего приложения):
QLocalServer* startSingleInstanceServer(QString appName) {
QLocalServer* server = new QLocalServer;
server->setSocketOptions(QLocalServer::WorldAccessOption);
server->listen(appName);
}
Вы также можете передать аргументы командной строки от запуска приложения к уже запущенному экземпляру - при запуске экземпляра приложения откройте сокет и отправьте параметры командной строки. На существующей стороне экземпляра просто подключите сигнал QLocalServer::newConnection, откройте сокет и подключите сигнал QLocalSocket::readyRead.
Для этого варианта использования QtSingleApplication
был создан, который также был адаптирован к Qt5:
https://github.com/qtproject/qt-solutions/tree/master/qtsingleapplication