如何使我的 PyQt5 应用程序只有一个实例?

问题描述 投票:0回答:2

我想要实现的目标:

  • 当我从开始菜单运行应用程序时,应用程序启动(如果应用程序未运行)。
  • 如果应用程序已在运行,则不要创建另一个实例,仅显示上一个正在运行的应用程序窗口。

我尝试过的:

  • 在目录中创建了一个
    .txt file
    ,在打开和退出窗口时将“
    running
    ”和“
    not running
    ”写入文件中。并从一开始就检查文件内容。例如: 启动应用程序时: 1.) 检查文件内容, 2.) 如果文件内容为“
    running
    ”,则显示警告并退出应用程序 3.) 如果文件内容为“
    not running
    ”,则写入“
    running
    ”写入文件并启动应用程序 4.) 退出时将“
    not running
    ”写入文件。

我面临的问题:

  • 这种方法感觉不是实现这一目标的正确方法。

  • 如果文件项显示“

    not running
    ”,应用程序将显示警告并退出。虽然我希望它显示已经在运行的实例。

  • 当应用程序退出时,由于代码中的某些错误,文件项未更新为“

    not running
    ”(因为由于错误而永远无法到达该部分)

有人可以帮我解决这个问题吗?

python pyqt5 instance
2个回答
3
投票

您可以使用 win32gui 模块实现类似的功能。要安装它,请输入 CMD

pip install pywin32
。现在这是代码:

from win32 import win32gui
import sys

def windowEnumerationHandler(hwnd, top_windows):
    top_windows.append((hwnd, win32gui.GetWindowText(hwnd)))

top_windows = []
win32gui.EnumWindows(windowEnumerationHandler, top_windows)
for i in top_windows:
    if "Program" in i[1]: #CHANGE PROGRAM TO THE NAME OF YOUR WINDOW
        win32gui.ShowWindow(i[0],5)
        win32gui.SetForegroundWindow(i[0])
        sys.exit()


from PyQt5.QtWidgets import QApplication, QWidget

def main():

    #YOUR PROGRAM GOES HERE

    app = QApplication(sys.argv)

    w = QWidget()
    w.setGeometry(500, 500, 500, 500)
    w.setWindowTitle('Simple')
    w.show()

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

基本上,一开始,程序会获取每个打开窗口的名称。如果窗口名称等于程序名称,则将程序置于最前面并关闭程序。如果没有,则会打开一个新程序。

PyWin32 链接

Stack Overflow 获取每个打开窗口的列表


0
投票

此解决方案不需要您安装 win32 或任何其他模块,可与 PyQt5 包本身一起使用。

import sys

from PyQt5 import QtWidgets
from PyQt5.QtCore import QSystemSemaphore, QSharedMemory

from ui import window_ui  # .py file compiled from .ui file


class LoginWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.ui = window_ui.Ui_MainWindow()  # call to init ui
        self.ui.setupUi(self)


def launch():
    app = QtWidgets.QApplication(sys.argv)  # create app instance at top, to able to show QMessageBox is required
    window_id = 'pingidapplication'
    shared_mem_id = 'pingidsharedmem'
    semaphore = QSystemSemaphore(window_id, 1)
    semaphore.acquire()  # Raise the semaphore, barring other instances to work with shared memory

    if sys.platform != 'win32':
        # in linux / unix shared memory is not freed when the application terminates abnormally,
        # so you need to get rid of the garbage
        nix_fix_shared_mem = QSharedMemory(shared_mem_id)
        if nix_fix_shared_mem.attach():
            nix_fix_shared_mem.detach()

    shared_memory = QSharedMemory(shared_mem_id)

    if shared_memory.attach():  # attach a copy of the shared memory, if successful, the application is already running
        is_running = True
    else:
        shared_memory.create(1)  # allocate a shared memory block of 1 byte
        is_running = False

    semaphore.release()

    if is_running:  # if the application is already running, show the warning message
        QtWidgets.QMessageBox.warning(None, 'Application already running',
                                      'One instance of the application is already running.')
        return

    # normal process of creating & launching MainWindow
    window = LoginWindow()
    window.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    launch()

这个解决方案是我从最初发布在此网站上的 C++ 代码转换而来的。

© www.soinside.com 2019 - 2024. All rights reserved.