在 QTreeView header 中控制网格线和边框的显示
Control display of gridlines and borders in header of QTreeView
(W10平台)
我想得到什么(我被要求用 Gimp 产生一个目标结果):
我目前得到的:
MRE:
from PyQt5.QtCore import QRect, Qt, QAbstractTableModel
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView, QWidget, QVBoxLayout, QStyledItemDelegate, \
QPlainTextEdit, QShortcut
import sys, types
from PyQt5.QtGui import QFont, QBrush, QColor
class HistoryTableModel( QAbstractTableModel ):
def __init__( self ):
super(HistoryTableModel, 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 2
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def setData(self, index, value, role ):
if role == Qt.EditRole:
self._data[ index.row() ][ index.column() ] = value
return True
def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal:
print( f'headerData, horiz: section {section}, role {role}')
if role == Qt.DisplayRole:
return ( 'Date', 'Entry' )[section]
elif role == Qt.ForegroundRole:
# this has the desired effect: colours the font
result = super().headerData( section, orientation, role )
return QBrush( QColor( 'brown' ) )
elif role == Qt.BackgroundRole:
# this seems to have no effect
result = super().headerData( section, orientation, role )
return QBrush( QColor( 'yellow' ) )
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, 300, 300))
self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
self.comps = []
self.table_view = QTableView(self.verticalLayoutWidget)
# this has an effect on the table data rows, but it's the header I want to format...
self.table_view.setStyleSheet( 'background: palegoldenrod; gridline-color: black;' )
# # ATTEMPT 1
# # this has an inexplicable effect: most of the window outside the table is given a white background;
# # gridlines are unchanged
# self.table_view.setStyleSheet( """QTableHeader.section.horizontal {
# color: red;
# background: blue;
# gridline-color: green;
# }""" )
# # ATTEMPT 2
# # this puts a small corner of blue in the top right corner of the window;
# # gridlines are unchanged
# self.table_view.horizontalHeader().setStyleSheet( """
# color: red;
# background: blue;
# gridline-color: green;
# """ )
# # ATTEMPT 3
# # this puts a small line of red in the top right corner of the window;
# self.table_view.horizontalHeader().setStyleSheet( """
# border-bottom:3px solid red;
# """ )
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( HistoryTableModel() )
MainWindow.setCentralWidget(self.centralwidget)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
app = QApplication(sys.argv)
application = MainWindow()
application.show()
sys.exit(app.exec())
我在这里要做的是 1) 改变水平方向的网格线 header; 2) 在 header 和 table 的数据行之间放置一条实线。如果有兴趣,请取消评论我的尝试。
我不清楚与 ItemDataRole
相关的覆盖功能是否是这里的方法:我看不出该功能实际上处理网格线的任何方式......或者样式是否床单应该这样做。
我在 W10 机器上工作似乎很重要(即,可能会在 Linux/OS 中获得更易于理解的结果)。
尝试1的图片:
尝试2的图片:
尝试3的图片:
您要设置样式的是 table header(一个 QHeaderView),而不是 table .
因此,您应该对其应用样式表,并使用正确的 class::selector:state
语法。
table.horizontalHeader().setStyleSheet('''
QHeaderView {
/* set the bottom border of the header, in order to set a single
border you must declare a generic border first or set all other
borders */
border: none;
border-bottom: 2px solid green;
}
QHeaderView::section:horizontal {
/* set the right border (as before, the overall border must be
declared), and a minimum height, otherwise it will default to
the minimum required height based on the contents (font and
decoration sizes) */
border: none;
border-right: 1px solid red;
min-height: 28px;
}
''')
请注意,由于在示例中我已将样式表设置为水平 header,因此可以省略 :horizontal
选择器(但 ::section
是必需的)。
还要考虑到您仍然可以将上面的样式表设置为 table:它的语法将被正确应用,因为我已经正确使用了 class 选择器。问题在于,由于我们需要为整个 header 显示边框,因此必须在没有选择器的情况下将其应用于 class,这显然会导致 both headers 显示底部边框,这是因为在这种情况下 :horizontal
将无法正常工作(AFAIK)。
(W10平台)
我想得到什么(我被要求用 Gimp 产生一个目标结果):
我目前得到的:
MRE:
from PyQt5.QtCore import QRect, Qt, QAbstractTableModel
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView, QWidget, QVBoxLayout, QStyledItemDelegate, \
QPlainTextEdit, QShortcut
import sys, types
from PyQt5.QtGui import QFont, QBrush, QColor
class HistoryTableModel( QAbstractTableModel ):
def __init__( self ):
super(HistoryTableModel, 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 2
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def setData(self, index, value, role ):
if role == Qt.EditRole:
self._data[ index.row() ][ index.column() ] = value
return True
def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal:
print( f'headerData, horiz: section {section}, role {role}')
if role == Qt.DisplayRole:
return ( 'Date', 'Entry' )[section]
elif role == Qt.ForegroundRole:
# this has the desired effect: colours the font
result = super().headerData( section, orientation, role )
return QBrush( QColor( 'brown' ) )
elif role == Qt.BackgroundRole:
# this seems to have no effect
result = super().headerData( section, orientation, role )
return QBrush( QColor( 'yellow' ) )
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, 300, 300))
self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
self.comps = []
self.table_view = QTableView(self.verticalLayoutWidget)
# this has an effect on the table data rows, but it's the header I want to format...
self.table_view.setStyleSheet( 'background: palegoldenrod; gridline-color: black;' )
# # ATTEMPT 1
# # this has an inexplicable effect: most of the window outside the table is given a white background;
# # gridlines are unchanged
# self.table_view.setStyleSheet( """QTableHeader.section.horizontal {
# color: red;
# background: blue;
# gridline-color: green;
# }""" )
# # ATTEMPT 2
# # this puts a small corner of blue in the top right corner of the window;
# # gridlines are unchanged
# self.table_view.horizontalHeader().setStyleSheet( """
# color: red;
# background: blue;
# gridline-color: green;
# """ )
# # ATTEMPT 3
# # this puts a small line of red in the top right corner of the window;
# self.table_view.horizontalHeader().setStyleSheet( """
# border-bottom:3px solid red;
# """ )
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( HistoryTableModel() )
MainWindow.setCentralWidget(self.centralwidget)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
app = QApplication(sys.argv)
application = MainWindow()
application.show()
sys.exit(app.exec())
我在这里要做的是 1) 改变水平方向的网格线 header; 2) 在 header 和 table 的数据行之间放置一条实线。如果有兴趣,请取消评论我的尝试。
我不清楚与 ItemDataRole
相关的覆盖功能是否是这里的方法:我看不出该功能实际上处理网格线的任何方式......或者样式是否床单应该这样做。
我在 W10 机器上工作似乎很重要(即,可能会在 Linux/OS 中获得更易于理解的结果)。
尝试1的图片:
尝试2的图片:
尝试3的图片:
您要设置样式的是 table header(一个 QHeaderView),而不是 table .
因此,您应该对其应用样式表,并使用正确的 class::selector:state
语法。
table.horizontalHeader().setStyleSheet('''
QHeaderView {
/* set the bottom border of the header, in order to set a single
border you must declare a generic border first or set all other
borders */
border: none;
border-bottom: 2px solid green;
}
QHeaderView::section:horizontal {
/* set the right border (as before, the overall border must be
declared), and a minimum height, otherwise it will default to
the minimum required height based on the contents (font and
decoration sizes) */
border: none;
border-right: 1px solid red;
min-height: 28px;
}
''')
请注意,由于在示例中我已将样式表设置为水平 header,因此可以省略 :horizontal
选择器(但 ::section
是必需的)。
还要考虑到您仍然可以将上面的样式表设置为 table:它的语法将被正确应用,因为我已经正确使用了 class 选择器。问题在于,由于我们需要为整个 header 显示边框,因此必须在没有选择器的情况下将其应用于 class,这显然会导致 both headers 显示底部边框,这是因为在这种情况下 :horizontal
将无法正常工作(AFAIK)。