停止焦点陷入 QTableView
Stop focus becoming ensnared in 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(反向循环方向的选项卡,但仍在 table 单元格内)并且我尝试了 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 )
我还尝试拦截到达 table 视图的击键(同样,将这些行添加到 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,并阻止了默认操作,但我不知道如何在焦点循环中找到正确的组件。此外,它似乎是一个敲开坚果的大锤:大概有一种更优雅的方式来获得我想要的行为......
如果您根本不想在单元格中切换,请添加 self.table_view.setTabKeyNavigation(False)
https://doc.qt.io/qt-5/qabstractitemview.html#tabKeyNavigation-prop
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(反向循环方向的选项卡,但仍在 table 单元格内)并且我尝试了 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 )
我还尝试拦截到达 table 视图的击键(同样,将这些行添加到 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,并阻止了默认操作,但我不知道如何在焦点循环中找到正确的组件。此外,它似乎是一个敲开坚果的大锤:大概有一种更优雅的方式来获得我想要的行为......
如果您根本不想在单元格中切换,请添加 self.table_view.setTabKeyNavigation(False)
https://doc.qt.io/qt-5/qabstractitemview.html#tabKeyNavigation-prop