简体   繁体   English

Qt显示应用程序(如果当前正在运行)

[英]Qt show application, if currently running

I'm creating a single instance app, which is minimised to system tray, I want to show the currently running instance, then quit the new one. 我正在创建一个单实例应用程序,该应用程序已最小化到系统托盘,我想显示当前正在运行的实例,然后退出新实例。 How can I create this functionality? 如何创建此功能?

main.cpp 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 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()

            }
        }
    }
}

I have tried creating an object, and set its running status to false or true in my main.cpp and in my main.qml I check the value and quit the application. 我尝试创建一个对象,并在main.cppmain.qml中将其运行状态设置为false或true,然后检查该值并退出应用程序。

QQmlApplicationEngine engine;

QQmlContext *context = engine.rootContext();
SingleInstance singleInstance;

context->setContextProperty("SingleInstance", &singleInstance);
if (is_running) {
    singleInstance.running(true);

In my main.qml I check if the application is running. 在我的main.qml ,检查应用程序是否正在运行。

Connections {
    target: SingleInstance
}

Component.onCompleted: {
    if (SingleInstance.running) {
        if(application.visibility === Window.Hidden) {
            Qt.quit()
        }
    }
}

What we ended up doing for single instance is to use QLocalSocket which is named pipe on Windows and local domain socket on Unix instead of QSharedMemory which requires more tweaking to get it right. 我们最终对单个实例所做的是使用QLocalSocket (在Windows上命名为管道,在Unix上使用本地域套接字),而不是QSharedMemory ,后者需要更多调整才能正确使用。 Basically you do something like this to check if the server is running ( appName must be unique application identifier, you can use eg QCoreApplication::applicationName ): 基本上,您可以执行以下操作来检查服务器是否正在运行( appName必须是唯一的应用程序标识符,您可以使用例如QCoreApplication :: applicationName ):

bool isSingleInstanceRunning(QString appName) {
    QLocalSocket socket;
    socket.connectToServer(m_appName);
    bool isOpen = socket.isOpen();
    socket.close();
    return isOpen;
}

If you get false you will create your own server (keep this instance during lifetime of your application): 如果false ,则将创建自己的服务器(在应用程序的生命周期内保留此实例):

QLocalServer* startSingleInstanceServer(QString appName) {
    QLocalServer* server = new QLocalServer;
    server->setSocketOptions(QLocalServer::WorldAccessOption);
    server->listen(appName);
}

You can also pass command line arguments from starting application to the already running instance - in starting instance of your application open the socket and send the command line parameters. 您还可以将命令行参数从启动应用程序传递到已经运行的实例-在应用程序的启动实例中,打开套接字并发送命令行参数。 On existing instance side just hook to QLocalServer::newConnection signal, open the socket and hook to QLocalSocket::readyRead signal. 在现有实例侧,只需挂接到QLocalServer :: newConnection信号,打开套接字并挂接到QLocalSocket :: readyRead信号即可。

For this use case QtSingleApplication has been created, which was adapted to Qt5, too: 对于这种使用情况QtSingleApplication已经建立,这是适应QT5,太:

https://github.com/qtproject/qt-solutions/tree/master/qtsingleapplication https://github.com/qtproject/qt-solutions/tree/master/qtsingle应用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM