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

Posted

技术标签:

【中文标题】Qt 显示应用程序,如果当前正在运行【英文标题】:Qt show application, if currently running 【发布时间】:2018-01-02 12:26:11 【问题描述】:

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

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 中将其运行状态设置为 false 或 true,然后检查值并退出应用程序。

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

【问题讨论】:

【参考方案1】:

我们最终为单个实例做的是使用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 信号。

【讨论】:

【参考方案2】:

对于这个用例 QtSingleApplication 已经创建,它也适用于 Qt5:

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

【讨论】:

以上是关于Qt 显示应用程序,如果当前正在运行的主要内容,如果未能解决你的问题,请参考以下文章

qt怎么查看当前正在执行的代码

手机qt后台在线 不退出就一直都在线吗

Ubuntu下用命令行运行QT程序的显示效果为啥跟双击程序的效果不一样?

用QT写程序,为啥运行成功了,却没有程序界面出来啊?

在VNC上运行Qt GUI应用程序导致分段错误,并显示错误消息

有没有办法在 Windows 上使用 Qt 列出当前进程?