按 T​​ab 键时移动到下一个选项卡并专注于相应的小部件

如何解决按 T​​ab 键时移动到下一个选项卡并专注于相应的小部件

在我的应用程序中,我有一个 QTabWidget,它包含可变数量的看似“相同”的选项卡和可变数量的小部件。 我希望,一旦按下 TAB(或 shift-TAB)按钮,应用程序的焦点就会移动到下一个(或上一个)选项卡,并专注于该选项卡的相应小部件(与小部件对应的小部件)焦点直到按下按键)。

以简单的方式解决这个问题的最佳方法是什么?我尝试使用 QShortcut 来捕捉按键,但我似乎无法找到一种方法来在下一个或上一个选项卡中获取相应的小部件并专注于此。

这是代码一个最小示例,它只是移动到下一个选项卡而不是相应的小部件:

import sys

from PyQt5 import QtCore,QtWidgets,QtGui
from PyQt5.QtWidgets import *


class tabdemo(QTabWidget):
    def __init__(self,num_tabs=2):
        super().__init__()

        shortcut = QtWidgets.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Tab),self)
        shortcut.activated.connect(self.on_tab)
        shortcut2 = QtWidgets.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Backtab),self)
        shortcut2.activated.connect(self.on_shift_tab)


        self.tabs = []
        for i in range(num_tabs):
            newtab = QWidget()
            self.tabs.append(newtab)
            self.addTab(newtab,f'Tab {i}')
            self.add_widgets_to(newtab)


    def add_widgets_to(self,tab):
        layout = QVBoxLayout()
        tab.setLayout(layout)

        layout.addWidget(QSpinBox())
        layout.addWidget(QCheckBox())

        gender = QHBoxLayout()
        gender.addWidget(qradiobutton("Male"))
        gender.addWidget(qradiobutton("Female"))
        layout.addLayout(gender)

    @QtCore.pyqtSlot()
    def on_tab(self):
        current_tab = self.currentIndex()
        self.setCurrentIndex((current_tab + 1) % self.count())
        # Todo find current widget in focus,and find the corresponding one in the next tab,and focus on that one... note that widgets Could be complex (i.e.,not direct children...)




    @QtCore.pyqtSlot()
    def on_shift_tab(self):
        print("do_something")
        current_tab = self.currentIndex()
        self.setCurrentIndex((current_tab - 1) % self.count())


def main():
    app = QApplication(sys.argv)
    ex = tabdemo()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

解决方法

由于OP表明每个页面将具有相同的组件,因此可以关联索引,以便在更改之前可以获取选项卡的索引,然后设置小部件的焦点,然后将焦点设置到其他相应的小部件。

import sys

from PyQt5.QtCore import pyqtSlot,Qt
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import (
    QApplication,QCheckBox,QHBoxLayout,QRadioButton,QShortcut,QSpinBox,QTabWidget,QVBoxLayout,QWidget,)


class Page(QWidget):
    def __init__(self,parent=None):
        super().__init__(parent)

        spinbox = QSpinBox()
        checkbox = QCheckBox()
        male_radio = QRadioButton("Male")
        female_radio = QRadioButton("Female")

        layout = QVBoxLayout(self)
        layout.addWidget(spinbox)
        layout.addWidget(checkbox)

        gender = QHBoxLayout()
        gender.addWidget(male_radio)
        gender.addWidget(female_radio)
        layout.addLayout(gender)

        for i,widget in enumerate((spinbox,checkbox,male_radio,female_radio)):
            widget.setProperty("tab_index",i)


class Tabdemo(QTabWidget):
    def __init__(self,num_tabs=2):
        super().__init__()

        shortcut = QShortcut(QKeySequence(Qt.Key_Tab),self)
        shortcut.activated.connect(self.next_tab)
        shortcut2 = QShortcut(QKeySequence(Qt.Key_Backtab),self)
        shortcut2.activated.connect(self.previous_tab)

        for i in range(num_tabs):
            page = Page()
            self.addTab(page,f"Tab {i}")

    @pyqtSlot()
    def next_tab(self):
        self.change_tab((self.currentIndex() + 1) % self.count())

    @pyqtSlot()
    def previous_tab(self):
        self.change_tab((self.currentIndex() - 1) % self.count())

    def change_tab(self,index):
        focus_widget = QApplication.focusWidget()
        tab_index = focus_widget.property("tab_index") if focus_widget else None
        self.setCurrentIndex(index)
        if tab_index is not None and self.currentWidget() is not None:
            for widget in self.currentWidget().findChildren(QWidget):
                i = widget.property("tab_index")
                if i == tab_index:
                    widget.setFocus(True)


def main():
    app = QApplication(sys.argv)
    ex = Tabdemo()
    ex.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
,

基于 eyllanesc 的回答,我改进了功能:

  • 滚动条位置的帐户(如果存在)
  • 使用双向字典 (implemented here) 而不是线性查找
  • 使用 update_map() 方法动态添加所有相关小部件,而不必手动添加每个小部件。

发布以防有人觉得这有用。

from PyQt5.QtCore import Qt,pyqtSlot
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import QWidget,QApplication,QScrollArea


class BidirectionalDict(dict):
    def __init__(self,*args,**kwargs):
        super(BidirectionalDict,self).__init__(*args,**kwargs)
        self.inverse = {}
        for key,value in self.items():
            self.inverse.setdefault(value,[]).append(key)

    def __setitem__(self,key,value):
        if key in self:
            self.inverse[self[key]].remove(key)
        super(BidirectionalDict,self).__setitem__(key,value)
        self.inverse.setdefault(value,[]).append(key)

    def __delitem__(self,key):
        self.inverse.setdefault(self[key],[]).remove(key)
        if self[key] in self.inverse and not self.inverse[self[key]]:
            del self.inverse[self[key]]
        super(BidirectionalDict,self).__delitem__(key)

    def get_first_inv(self,key):
        return self.inverse.get(key,[None])[0]


class Page(QWidget):
    def __init__(self,parent=None):
        super().__init__(parent)
        self.widgets_map = BidirectionalDict()
        # ... add your widgets ...
        self.update_map()


    def update_map(self):
        widgets = self.findChildren(QWidget)
        for i,widget in enumerate(widgets):
            self.widgets_map[i] = widget


class MyQTabWidget(QTabWidget):
    def __init__(self):
        super().__init__()

        shortcut = QShortcut(QKeySequence(Qt.Key_Tab),self)
        shortcut2.activated.connect(self.previous_tab)


    @pyqtSlot()
    def next_tab(self):
        self.change_tab((self.currentIndex() + 1) % self.count())

    @pyqtSlot()
    def previous_tab(self):
        self.change_tab((self.currentIndex() - 1) % self.count())

    def change_tab(self,new_tab_index):
        old_tab: Page = self.currentWidget()
        focus_widget = QApplication.focusWidget()
        widget_index = old_tab.widgets_map.get_first_inv(focus_widget) if focus_widget else None

        self.setCurrentIndex(new_tab_index)

        new_tab: Page = self.currentWidget()

        if new_tab is not None and widget_index is not None:
            corresponding_widget: QWidget = new_tab.widgets_map[widget_index]
            corresponding_widget.setFocus(True)

            # Move scrollbar to the corresponding position
            if hasattr(old_tab,'scrollbar'):
                # Tabs are identical so new_tab must have scrollbar as well
                old_y = old_tab.scrollbar.verticalScrollBar().value()
                scrollbar: QScrollArea = new_tab.scrollbar
                scrollbar.verticalScrollBar().setValue(old_y)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?