在 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)。