(W10 platform)
What I’m trying to get (I was asked to produce a target outcome, with Gimp):
What I currently get:
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())
What I’m trying to do here is 1) change the grid lines in the horizontal header; 2) put a solid line of some kind between the header and the data rows of the table. Uncomment my attempts if interested.
I’m not clear whether overriding functionality to do with ItemDataRole
is the way to go here: I can’t see any way that this functionality actually takes care of grid lines… Or whether style sheets should do this.
It seems that it may be significant that I’m working on a W10 machine (i.e. more understandable results may possibly be obtained in Linux/OS).
Advertisement
Answer
What you’re trying to style is the table header (a QHeaderView), not the table.
So, you should apply the stylesheet to it, and use the correct class::selector:state
syntax.
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; } ''')
Note that since in the example I’ve set the stylesheet to the horizontal header, the :horizontal
selector can be omitted (but the ::section
is required).
Also consider that you could still set the stylesheet above to the table: its syntax will be correctly applied since I’ve correctly used the class selector. The problem is that, since we need to show a border for the whole header, it has to be applied to the class without selectors, which will obviously cause both headers to show a bottom border, and that’s because in this case :horizontal
won’t work properly (AFAIK).