PyQT5: почему класс прямого ярлыка работает, а синтаксис ярлыка не работает в QAction?

Я столкнулся со странной проблемой, когда ярлык смены страниц работает в одном синтаксисе, а в другом нет.

Мое приложение имеет два окна, каждое на другом экране. В первом окне отображается основная информация, а во втором — информация мониторинга. Оба окна независимы, но должны иметь общие страницы (Настройки/меню...). Каждое окно имеет разные страницы: некоторые общие (меню, настройки), а некоторые свои (домашняя страница...).

Тогда структура немного сложнее: оба окна содержатся в объекте (конфигурации), который имеет дело со всеми объектами приложения.

Оба окна наследуют общий класс Windw, чтобы использовать общие методы и общие страницы (домашняя страница/предпочтения...). Каждое окно имеет свой собственный класс для всех выделенных ему переменных и объектов.

На данный момент все работает хорошо, и я пытаюсь создать метод изменения страницы. На каждую страницу есть ссылка в словаре оглавления, который вызывает правильный контент. Метод смены страниц также работает нормально.

Проблема связана с ярлыками/действиями. Если я использую этот синтаксис:

      menu_action = QAction ("Menu", self)
menu_action.setShortcut(QKeySequence("Ctrl+m"))
menu_action.activated.connect(self.page_change)

в классе WindowPcp или в self.pcp, когда я пытаюсь поместить этот код в класс конфигурации, «page_change» не вызывается.

Если я использую этот синтаксис:

      menu_short = QShortcut(QKeySequence("Ctrl+m", self)
menu_short.activated.connect(self.page_change)

ярлык ведет себя так, как ожидалось: страница Windwpcp изменяется с домашней_страницы на страницу меню.

Вот минимальный рабочий код скелета моего приложения (реальное приложение слишком велико, чтобы его можно было публиковать здесь (около 2500 строк для всего поведения окон):

      import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QGuiApplication, QKeySequence
from PyQt5.QtWidgets import (QAction,
                             QApplication,
                             QFrame,
                             QHBoxLayout,
                             QLabel,
                             QMainWindow,
                             QShortcut,
                             QStackedLayout,
                             QVBoxLayout,
                             QWidget)


class Windw(QMainWindow):
    def __init__(self):
        super().__init__()
        self.active_page = "home"

        self.menu = QLabel("Menu")

        m_body = QFrame()
        # Main common Layout
        self.central_layout = QStackedLayout()

        self.menu_page = QFrame()
        menu_page_layout = QVBoxLayout()
        self.menu_page.setLayout(menu_page_layout)

        self.menu_frame = QFrame()
        self.menu_frame_layout = QHBoxLayout()
        self.menu_frame.setLayout(self.menu_frame_layout)
        # Adding inner frame to main menu page frame
        menu_page_layout.addWidget(self.menu_frame)

        self.home_page = QFrame()
        self.home_page_layout = QVBoxLayout()
        self.home_page.setLayout(self.home_page_layout)

        self.preferences = QFrame()
        preferences_layout = QVBoxLayout()
        self.preferences.setLayout(preferences_layout)

        self.tbl_contents = {"menu": self.menu_page, "home": self.home_page, "preferences": self.preferences}

        self.central_layout.addWidget(self.menu_page)
        self.central_layout.addWidget(self.home_page)
        self.central_layout.addWidget(self.preferences)

        m_body.setLayout(self.central_layout)
        self.setCentralWidget(m_body)

    def page_change(self):
        origine = self.sender()
        print(f"page_changed activated !!!!")
        if origine.objectName() == "menu_short":
            self.active_page = "menu"
            self.central_layout.setCurrentWidget(self.tbl_contents[self.active_page])
        elif origine.objectName() == "home_short":
            self.active_page = "home"
            self.central_layout.setCurrentWidget(self.sommaire[self.page_active])
        elif origine.objectName() == "preferences_short":
            self.active_page = "preferences"
            self.central_layout.setCurrentWidget(self.sommaire[self.page_active])


class WindowPcp(Windw):
    def __init__(self):
        super().__init__()
        self.active_page = "home"
        self.setFocusPolicy(Qt.TabFocus)

        text_pcp = QLabel("Pcp Homepage")

        # Adding content to each pages
        self.menu_frame_layout.addWidget(self.menu)
        self.home_page_layout.addWidget(text_pcp)

        self.central_layout.setCurrentWidget(self.tbl_contents[self.active_page])

        """menu_act = QAction("Menu", self,)
        menu_act.setObjectName("menu_short")
        menu_act.setShortcut(QKeySequence("Ctrl+m"))                 # Never works
        menu_act.triggered.connect(self.page_change)"""

        menu_short = QShortcut(QKeySequence("Ctrl+m"), self)
        menu_short.setObjectName("menu_short")                       # works as expected
        menu_short.activated.connect(self.page_change)


class WindowScd(Windw):
    def __init__(self):
        super().__init__()
        self.active_page = "home"
        self.setFocusPolicy(Qt.NoFocus)

        text_scd = QLabel("Scd Homepage")

        # Adding content to each pages
        self.menu_frame_layout.addWidget(self.menu)
        self.home_page_layout.addWidget(text_scd)

        self.central_layout.setCurrentWidget(self.tbl_contents[self.active_page])


class Configuration(QWidget):
    def __init__(self):
        super().__init__()
        self.screen_conf = QGuiApplication.screens()
        self.prim_screen = QGuiApplication.primaryScreen()

        self.pcp = WindowPcp()
        self.scd = WindowScd()

        self.pcp.setGeometry(self.screen_conf[1].geometry())
        self.scd.setGeometry(self.prim_screen.geometry())

       # commented lines to compare working and not working syntax :
        """menu_act = QAction("Menu", self.pcp)
        menu_act.setObjectName("menu_short")                     # Never works
        menu_act.setShortcut(QKeySequence("Ctrl+m"))
        menu_act.triggered.connect(self.pcp.page_change)"""

        """menu_short = QShortcut(QKeySequence("Ctrl+m"), self.pcp)
        menu_short.setObjectName("menu_short")                   # works as expected
        menu_short.activated.connect(self.pcp.page_change)"""


# ------------- MAIN --------------------
app = QApplication.instance()
if not app:
    app = QApplication(sys.argv)

config = Configuration()  # Configuration object : contains windows and all main objects

config.scd.show()
config.pcp.show()

try:
    sys.exit(app.exec())
except SystemExit:
    print('windows closed...')

Сначала я попытался использовать QActions, но ярлык не работает. Я попробовал использовать QShortcut, и это работает... но окно должно быть в фокусе... Затем я попытался поместить QShortcut на уровень конфигурации, чтобы иметь глобальный контроль... Но QShortcut отказывается от "self" в качестве родительского объекта, когда относится к конфигурации и принимает только WindwPCP в качестве родительского.

Вот мои вопросы:

  1. Почему прямой QShortcut работает, а ярлык QAction (или, точнее, сам QAction) — нет? Для меня это странно, поскольку другие QActions работают внутри WindwPCP при использовании с QPushButtons.

  2. Мне действительно нужны эти ярлыки для работы, поскольку они будут использоваться пультом дистанционного управления. Тогда, даже если решение QShortcut работает, для меня оно недостаточно хорошо, поскольку окно должно быть в фокусе. Если фокус будет изменен, я потеряю удаленное управление своим приложением. Вот почему я попытался добавить QShortcut в класс конфигурации, чтобы он находился на верхнем уровне приложения. Но я столкнулся с другой проблемой: QShortcut не работает, если я заменюmenu_short = QShortcut(QKeySequence("Ctrl+m"), self.pcp)сmenu_short = QShortcut(QKeySequence("Ctrl+m"), self)

Ярлык не работает с самим объектом конфигурации (класс конфигурации), он принимает только мои окна в качестве родительского. Есть ли способ иметь глобальный уровень ярлыков, которые я затем буду фильтровать, чтобы отправлять их на нужные объекты, чтобы избежать необходимости иметь дело с фокусом?

  1. Если ничего для 2) невозможно, поскольку мне не нужен удаленный контроль над вторым окном, которое будет использоваться сенсорным экраном, есть ли способ принудительно сфокусироваться на WindwPCP, не теряя при этом контроля над его внутренними виджетами? setFocusPolicy в обоих окнах, похоже, на данный момент недостаточно...

Спасибо за ваше время и помощь: я действительно застрял в данный момент...

0 ответов

Другие вопросы по тегам