Skip to content
Advertisement

Control display of gridlines and borders in header of QTreeView

(W10 platform)

What I’m trying to get (I was asked to produce a target outcome, with Gimp): what I want

What I currently get:

current result

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).

Image of attempt1: failed attempt 1

Image of attempt2: failed attempt 2

Image of attempt3: failed attempt 3

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).

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement