Контекстное меню не работало на иконке в трее при использовании pyqt5 - PullRequest
1 голос
/ 27 февраля 2020

Я разрабатываю приложение с pyqt5, которое автоматически закроет окно, когда оно начнет работать. И я надеюсь построить иконку в трее с контекстным меню, чтобы снова показать окно. Ниже приведен мой краткий код:

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.set_sytem_tray_icon()

        self.current_status = Stop
        self.track_str = ''
        self.start_button.clicked.connect(self.start_button_clicked)
        self.stop_button.clicked.connect(self.stop_button_clicked)

        self.show()

    def set_sytem_tray_icon(self):
        contextMenu = QMenu()
        action = contextMenu.addAction("Show Window")
        action.triggered.connect(self.show)
        contextMenu.addAction(action)
        self.tray_icon = QSystemTrayIcon()
        self.tray_icon.setIcon(QtGui.QIcon("img.ico"))
        self.tray_icon.setContextMenu(contextMenu)
        self.tray_icon.show()

    def get_specific_window(self, class_name, title):


    def check_ULink_status(self):
        try:
            while True:
                # Doing something
                    break

    def start_button_clicked(self):

        self.hide()

        thread = threading.Thread(target=self.check_ULink_status)
        thread.setDaemon(True)
        thread.start()
        thread.join()

        self.show()

    def stop_button_clicked(self):
        reply = QMessageBox.information(self, "Warning", "Stop", QMessageBox.Yes, QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.current_status = Stop
            self.change_button_availability()

Вот моя проблема: когда я нажал кнопку «Пуск», приложение начало работать, но значок в трее не реагировал на какие-либо действия. Я считаю, что был какой-то конфликт с моей основной веткой, но я все еще не могу понять, что происходит. У кого-нибудь есть ответ на это?

Обновление, я хотел бы предоставить другое решение, использующее qthread.

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.set_sytem_tray_icon()

        self.current_status = Stop
        self.track_str = ''
        self.start_button.clicked.connect(self.start_button_clicked)
        self.stop_button.clicked.connect(self.stop_button_clicked)

        self.show()

    def set_sytem_tray_icon(self):
        contextMenu = QMenu()
        action = contextMenu.addAction("Show Window")
        action.triggered.connect(self.show)
        contextMenu.addAction(action)
        self.tray_icon = QSystemTrayIcon()
        self.tray_icon.setContextMenu(contextMenu)
        self.tray_icon.show()

    def set_sytem_tray_icon_with_qthread(self):
        set_sytem_tray_icon_qthread = qthread_definition.MyQThread2()
        set_sytem_tray_icon_qthread.signal.connect(self.set_sytem_tray_icon)
        set_sytem_tray_icon_qthread.start()
        set_sytem_tray_icon_qthread.wait()

    def show_mainwindow_with_qthread(self):
        show_mainwindow_qthread = qthread_definition.MyQThread2()
        show_mainwindow_qthread.signal.connect(self.show)
        show_mainwindow_qthread.start()
        show_mainwindow_qthread.wait()

    def get_specific_window(self, class_name, title):
        # doing something

    def check_ULink_status(self):
        self.set_sytem_tray_icon_with_qthread()  # add new qthread here
        try:
            while True:
                # Doing something
                    break
            self.show_mainwindow_with_qthread()

    def start_button_clicked(self):
        self.hide()
        thread = threading.Thread(target=self.check_ULink_status)
        thread.setDaemon(True)
        thread.start()

    def stop_button_clicked(self):
        reply = QMessageBox.information(self, "Warning", "Stop", QMessageBox.Yes, QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.current_status = Stop
            self.change_button_availability()

, где класс MyQThread2 показан ниже,

class MyQThread2(QtCore.QThread):
    # this thread is to create the tray icon
    signal = QtCore.pyqtSignal()

    def __init__(self):
        super().__init__()

    def run(self):
        self.signal.emit()

Чтобы показать главное окно в Поток, нам нужно создать qthread, чтобы завершить sh эту задачу, потому что показ окна является своего рода модификацией объекта qt, который не разрешен вне основного потока.

1 Ответ

2 голосов
/ 27 февраля 2020

Как указывают документы метода join():

join (timeout = None)

Дождаться завершения потока , Это блокирует вызывающий поток до тех пор, пока поток, чей метод join () вызывается, не завершит - либо нормально, либо через необработанное исключение - либо пока не произойдет необязательное время ожидания.

Когда присутствует аргумент времени ожидания, а не None , это должно быть число с плавающей запятой, указывающее время ожидания операции в секундах (или их доли). Поскольку join () всегда возвращает None, вы должны вызвать is_alive () после join (), чтобы решить, произошел ли тайм-аут - если поток все еще жив, тайм-аут вызова join ().

Когда аргумент timeout отсутствует или отсутствует, операция будет блокироваться до тех пор, пока поток не прекратит работу.

Поток может быть присоединен к () несколько раз.

join () вызывает ошибку RuntimeError, если предпринята попытка присоединиться к текущему потоку, так как это приведет к тупику. Также является ошибкой присоединение () к потоку до его запуска, и попытка сделать это вызывает то же исключение.

(выделение мое)

Этот метод блокируется до тех пор, пока поток не завершит выполнение, предотвращая выполнение события GUI l oop, в результате чего он не отвечает на события, поскольку он заморожен.

Решение состоит в том, чтобы удалить этот метод:

def start_button_clicked(self):
    self.hide()
    thread = threading.Thread(target=self.check_ULink_status)
    thread.setDaemon(True)
    thread.start()
    <b># thread.join() # remove this line</b>
    self.show()

Похоже, что OP использует join(), так что когда задача завершена, окно снова отображается, если это так, правильное решение - использовать сигнал:

class MainWindow(QMainWindow, Ui_MainWindow):
    <b>finished = QtCore.pyqtSignal()</b>

    def __init__(self):
        super().__init__()
        <b>self.finished.connect(self.show)</b>
        # ...

    # ...

    def check_ULink_status(self):
        # After finishing the task the signal must be emitted
        <b>self.finished.emit()</b>

    # ...

    def start_button_clicked(self):
        self.hide()
        thread = threading.Thread(target=self.check_ULink_status)
        thread.setDaemon(True)
        thread.start()

     # ...
...