Остановить попадание фокуса в ловушку в QTableView

#python #pyqt5 #focus #qtableview

#python #pyqt5 #фокус #qtableview

Вопрос:

MRE:

 from PyQt5.QtCore import QRect, Qt, QAbstractTableModel
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView, QWidget, QVBoxLayout, QLineEdit, QListWidget
import sys, types

class MyTableModel( QAbstractTableModel ):
    def __init__( self ):
        super(MyTableModel, self).__init__()
        data = [
              [4, 9, 2],
              [1, 0, 0],
              [3, 5, 0],
        ]
        self._data = data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data[0])

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.resize(600, 700 )
        self.centralwidget = QWidget(MainWindow)
        self.verticalLayoutWidget = QWidget(self.centralwidget)
        self.verticalLayoutWidget.setGeometry( QRect(20, 20, 500, 500))
        self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
        self.comps = []
        
        self.title_line_edit = QLineEdit(self.verticalLayoutWidget)
        self.comps.append( self.title_line_edit )
        self.verticalLayout.addWidget(self.title_line_edit)

        self.tags_list = QListWidget(self.verticalLayoutWidget)
        self.comps.append( self.tags_list )
        self.verticalLayout.addWidget(self.tags_list)
        
        # UNCOMMENT THESE LINES:
#         self.table_view = QTableView(self.verticalLayoutWidget)
#         self.comps.append( self.table_view )
#         self.table_view.setGeometry(QRect(20, 20, 200, 200))
#         self.verticalLayout.addWidget(self.table_view)
#         self.table_view.setModel( MyTableModel() )

        self.title_line_edit2 = QLineEdit(self.verticalLayoutWidget)
        self.comps.append( self.title_line_edit2 )
        self.verticalLayout.addWidget(self.title_line_edit2)
        
        MainWindow.setCentralWidget(self.centralwidget)

class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        def focus_in_event( self, event ):
            self.original_stylesheet = self.styleSheet()
            self.setStyleSheet( 'border:1px solid rgb(0, 255, 0);' )
        def focus_out_event( self, event ):
            self.setStyleSheet( self.original_stylesheet )
        for comp in self.ui.comps:
            comp.focusInEvent = types.MethodType( focus_in_event, comp )
            comp.focusOutEvent = types.MethodType( focus_out_event, comp )

app = QApplication(sys.argv)
application = MyWindow()
application.show()
sys.exit(app.exec())
  

Как есть, фокус хорошо просматривается через компоненты.

Но когда вы раскомментируете добавление QTableView , добавление вкладок в этот компонент означает, что вы не сможете снова перейти на вкладку. Я попробовал Shift-Tab (вкладки в обратном циклическом направлении, но все еще внутри ячеек таблицы), и я попробовал Ctrl-Tab / Ctrl-Shift-Tab (который, кажется, перемещает текущую ячейку).

Я вообще не заинтересован в использовании Tab для навигации по этим ячейкам (клавиши навигации со стрелками делают то же самое) и просто хочу, чтобы этого «захвата» не произошло, поэтому я могу Tab / Shift-Tab по всем компонентам по кругу, как и раньше.

Я попробовал пару вещей, добавив эти строки в конец MyWindow.__init__ :

     # seemingly no effect:
    def focus_next_prev_child( self, next ):
        return False
    self.ui.table_view.focusNextPrevChild = types.MethodType( focus_next_prev_child, comp )
    # just omits the table view from the focus cycle:
    self.ui.table_view.setFocusPolicy( Qt.NoFocus )
  

Я также попытался перехватить нажатия клавиш, достигающие табличного представления (опять же, добавьте эти строки в MyWindow.__init__ :

     def keypress_event( self, key_event ):
        modifs = key_event.modifiers()
        key = key_event.key()
        def unmodified_not_keypad():
            print( f'unmodified, key: {hex( key )} text: {key_event.text()}' )
            # strangely, if you press Shift-Tab, this gets to here (i.e. unmodified)
            if key == Qt.Key_Tab or key == Qt.Key_Backtab:
                # what might I do here? 
                # how might I find the next component in the cycle (forwards or backwards)?
                return True
        switcher = {
            0x00000000: unmodified_not_keypad,
        }
        def print_invalid():
            print( f'invalid, hex( modifs): {hex(modifs)}' )
        # if any called function returns True this means skip the default action...
        if switcher.get( int( modifs ), print_invalid )():
            return
        # execute default action:
        QTableView.keyPressEvent( self, key_event )
    self.ui.table_view.keyPressEvent = types.MethodType( keypress_event, self.ui.table_view )
  

Это действительно перехватывает Tab и Shift-Tab и предотвращает действие по умолчанию, но я не знаю, как найти нужные компоненты в цикле фокусировки. Также кажется, что это похоже на кувалду, чтобы расколоть орех: предположительно, есть более элегантный способ добиться желаемого поведения…

Ответ №1:

Если вы вообще не хотите переходить по ячейкам, добавьте self.table_view.setTabKeyNavigation(False)

https://doc.qt.io/qt-5/qabstractitemview.html#tabKeyNavigation-prop