Обработка нажатия на пункт меню pywin32 в PyQT5
У меня есть небольшой графический интерфейс, написанный на Python PyQT5. При нажатии на иконку окна в Windows всплывает встроенное меню. Стоит задача немного переопределить это меню, а именно - добавить пункт "о программе", при нажатии на который будет открываться небольшое окно с информацией. Я не нашел простого способа переопределить это встроенное меню с помощью PyQT5, но мне подсказали, как сделать это используя pywin32. Я делаю это, добавляя следующий код:
self.custom_menu_id = 6
hwnd = self.winId()
hmenu = win32gui.GetSystemMenu(hwnd, False)
win32gui.AppendMenu(hmenu, win32con.MF_SEPARATOR, 5, '')
win32gui.AppendMenu(hmenu, win32con.MF_STRING, self.custom_menu_id, 'О программе')
Остается вопрос, как мне связать нажатие на этот пункт меню с открытием диалогового окна с некоторой информацией в PyQT5? Ниже будет полный код программы (для того, чтобы его запустить нужны библиотеки PyQt5==5.15.8 и pywin32==305) и скриншоты окна с выпадающим меню.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import win32gui
import win32.lib.win32con as win32con
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# конфигурация главного окна
self.central_widget = QWidget(self)
self.setCentralWidget(self.central_widget)
self.layout_main_window = QVBoxLayout()
self.central_widget.setLayout(self.layout_main_window)
# тут добавляю новый пункт меню:
self.custom_menu_id = 6
hwnd = self.winId()
hmenu = win32gui.GetSystemMenu(hwnd, False)
win32gui.AppendMenu(hmenu, win32con.MF_SEPARATOR, 5, '')
win32gui.AppendMenu(hmenu, win32con.MF_STRING, self.custom_menu_id, 'О программе')
# конфигурация верхней панели инструментов
self.layout_toolbar_buttons = QHBoxLayout()
self.layout_toolbar_buttons.addStretch(1)
self.btn_start_reading = QPushButton("Считать")
self.layout_toolbar_buttons.addWidget(self.btn_start_reading)
self.layout_main_window.addLayout(self.layout_toolbar_buttons)
# конфигурация таблицы
self.layout_table = QHBoxLayout()
self.table = QTableWidget()
self.table.setColumnCount(9)
self.table.setHorizontalHeaderLabels(['№', 'Дата и время', 'БКУ', 'КЛ', 'АУ', 'Канал', 'Код события', 'Доп. параметр', 'Описание'])
self.layout_table.addWidget(self.table)
self.layout_main_window.addLayout(self.layout_table)
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
всплывающее меню с пунктом "О программе":
Ответы (2 шт):
Можно привязаться на QMessageBox.about()
def menu_action_triggered(self, action):
if action == self.custom_menu_id:
QMessageBox.about(self, 'О программе', 'Инфа')
Ну и соответственно привязка:
menu = self.menuBar()
menu.triggered.connect(self.menu_action_triggered)
Интересный кейс, хотя его полезность и близится к нулю)
Все же Вам придется вмешиваться в эвентлуп приложения, но на счастье в QWidget для этих целей есть виртуальный метод nativeEvent (в старых Qt назывался winEvent). На всякий случай обращаю внимание что для macos и linux nativeEvent также есть, но работать будет иначе.
Согласно WinAPI, когда пользователь выбирает команду из системного меню, окно приложения получает сообщение WM_SYSCOMMAND. в wParam при этом может принимать одно из предопределенных значений (см. документацию) или идентификатор пункта меню, который Вы добавляли.
В общем и целом, реализация будет примерно такая:
import sys
from PyQt5.QtWidgets import *
import win32gui
import win32.lib.win32con as win32con
import ctypes.wintypes
class MainWindow(QMainWindow):
# Нам надо определить свой обработчик событий эвентлупа
def nativeEvent(self, eventType, message):
retval, result = super(MainWindow,self).nativeEvent(eventType, message)
# из параметра message нам надо получить стандартную структуру типа MSG
msg = ctypes.wintypes.MSG.from_address(message.__int__())
# Что-то тыкнули в системном меню
if msg.message == win32con.WM_SYSCOMMAND:
# И судя по идентификатору - то что нам надо
if msg.wParam == self.custom_menu_id:
QMessageBox.information(self, "бляблябля", "Ебаут програм")
return retval, result
def __init__(self):
super().__init__()
# конфигурация главного окна
self.central_widget = QWidget(self)
self.setCentralWidget(self.central_widget)
self.layout_main_window = QVBoxLayout()
self.central_widget.setLayout(self.layout_main_window)
# тут добавляю новый пункт меню:
self.custom_menu_id = 0xFF
hwnd = self.winId()
hmenu = win32gui.GetSystemMenu(hwnd, False)
win32gui.AppendMenu(hmenu, win32con.MF_SEPARATOR, 5, '')
m = win32gui.AppendMenu(hmenu, win32con.MF_STRING, self.custom_menu_id, 'О программе')
# конфигурация верхней панели инструментов
self.layout_toolbar_buttons = QHBoxLayout()
self.layout_toolbar_buttons.addStretch(1)
self.btn_start_reading = QPushButton("Считать")
self.layout_toolbar_buttons.addWidget(self.btn_start_reading)
self.layout_main_window.addLayout(self.layout_toolbar_buttons)
# конфигурация таблицы
self.layout_table = QHBoxLayout()
self.table = QTableWidget()
self.table.setColumnCount(9)
self.table.setHorizontalHeaderLabels(['№', 'Дата и время', 'БКУ', 'КЛ', 'АУ', 'Канал', 'Код события', 'Доп. параметр', 'Описание'])
self.layout_table.addWidget(self.table)
self.layout_main_window.addLayout(self.layout_table)
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())

