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

Ten*_*ter 2 c++ qt qml qtquick2

我正在创建一个单实例应用程序,该应用程序最小化到系统托盘,我想显示当前正在运行的实例,然后退出新实例。我怎样才能创建这个功能?

主程序

#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();
}
Run Code Online (Sandbox Code Playgroud)

主.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()

            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试创建一个对象,并将其运行状态设置为 false 或 true,main.cpp并在 my 中main.qml 检查该值并退出应用程序。

QQmlApplicationEngine engine;

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

context->setContextProperty("SingleInstance", &singleInstance);
if (is_running) {
    singleInstance.running(true);
Run Code Online (Sandbox Code Playgroud)

在我的中,main.qml我检查应用程序是否正在运行。

Connections {
    target: SingleInstance
}

Component.onCompleted: {
    if (SingleInstance.running) {
        if(application.visibility === Window.Hidden) {
            Qt.quit()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 5

我们最终对单实例所做的是使用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;
}
Run Code Online (Sandbox Code Playgroud)

如果您得到了,false您将创建自己的服务器(在应用程序的生命周期内保留此实例):

QLocalServer* startSingleInstanceServer(QString appName) {
    QLocalServer* server = new QLocalServer;
    server->setSocketOptions(QLocalServer::WorldAccessOption);
    server->listen(appName);
}
Run Code Online (Sandbox Code Playgroud)

您还可以将命令行参数从启动应用程序传递到已经运行的实例 - 在应用程序的启动实例中打开套接字并发送命令行参数。在现有实例方面,只需挂钩QLocalServer::newConnection信号,打开套接字并挂钩QLocalSocket::readyRead信号。