简体   繁体   中英

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

#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()

            }
        }
    }
}

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.

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.

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. 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 ):

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):

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.

For this use case QtSingleApplication has been created, which was adapted to Qt5, too:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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