Re: (pyqt)set different column font for qtableview
Maziar Parsijani <[email protected]>
| Newsgroups | gmane.comp.python.pyqt-pykde |
|---|---|
| Message-ID | <CAPD04aOTaKVrCJ_xno5bn61SKA+uFwoxi3oBrUX_SkLjaX2uOg@mail.gmail.com> |
This is great, thank you so much! On Fri, Apr 26, 2019 at 3:43 AM Maurizio Berti <[email protected]> wrote: > Il giorno gio 25 apr 2019 alle ore 21:04 Maziar Parsijani < > [email protected]> ha scritto: > >> Hi >> I want to know if it is possible to change font for each column in >> Qtableview and if it is possible even more different appearances in columns >> like backgrounds and font colors. >> > > > There are different possible approaches, and the choice usually depends on > how the table data is filled and if it's editable. > The most common way is to set the QtCore.Qt.ItemDataRole for each field > index, by providing the FontRole, ForegroundRole and BackgroundRole to the > index.setData(value, role). > If you're using a QStandardItemModel, the data is simple and you don't > need interaction, just use setData method on each item. > > If you are using any model based on QAbstractItemModel you could subclass > its data method like this: > > class SimpleModel(QtGui.QStandardItemModel): > backgrounds = QtGui.QColor(QtCore.Qt.lightGray), > QtGui.QColor(QtCore.Qt.darkCyan), QtGui.QColor(QtCore.Qt.darkGray) > foregrounds = QtGui.QColor(QtCore.Qt.darkGreen), > QtGui.QColor(QtCore.Qt.darkBlue), QtGui.QColor(QtCore.Qt.yellow) > fonts = QtGui.QFont('monospace'), QtGui.QFont(), QtGui.QFont('times') > > def data(self, index, role=QtCore.Qt.DisplayRole): > if role == QtCore.Qt.BackgroundRole: > return self.backgrounds[index.column()] > elif role == QtCore.Qt.ForegroundRole: > return self.foregrounds[index.column()] > elif role == QtCore.Qt.FontRole: > return self.fonts[index.column()] > return QtGui.QStandardItemModel.data(self, index, role) > > Note that you could also set a QIdentityProxyModel with your original > model (and use the proxy on the table instead) and then implement the > data() method in the same way. Just use the code above with > QIdentityProxyModel instead of QStandardItemModel, do a > setSource(originalModel) and you're done; this is usually better and much > more transparent. > > If you cannot have that kind of access, the alternative is to create your > own item delegate subclass and implement the paint() method. Be aware that > item painting is not an easy task, expecially if you want to mimic the > default implementation. > > class SimpleDelegate(QtWidgets.QStyledItemDelegate): > backgrounds = QtGui.QColor(QtCore.Qt.lightGray), > QtGui.QColor(QtCore.Qt.darkCyan), QtGui.QColor(QtCore.Qt.darkGray) > foregrounds = QtGui.QColor(QtCore.Qt.darkGreen), > QtGui.QColor(QtCore.Qt.darkBlue), QtGui.QColor(QtCore.Qt.yellow) > fonts = QtGui.QFont('monospace'), QtGui.QFont(), QtGui.QFont('times') > fontData = zip(backgrounds, foregrounds, fonts) > > def paint(self, qp, option, index): > #painting needs performance, let's get all data at once > background, foreground, font = self.fontData[index.column()] > > # never reuse the given option argument, always create a new one > based on it! > # reusing QStyleOptions might create issues and inconsistencies > with item "siblings" > option = QtWidgets.QStyleOptionViewItem(option) > self.initStyleOption(option, index) > > # reset the text so that QStyle won't paint it > option.text = '' > option.backgroundBrush = background > > widget = option.widget > style = widget.style() > > # we could use the drawPrimitive instead, but it won't paint the > decoration (icon), > # if it exists; drawControl paints everything, that's why I > cleared the text before, > # otherwise you'll see the text drawn twice > style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, option, qp) > #style.drawPrimitive(QtWidgets.QStyle.PE_PanelItemViewItem, > option, qp) > > # get the rectangle available for item text and adjust it to > standard margins; > # one pixel is added for consistency with the original Qt painting > behavior > textRect = style.subElementRect(style.SE_ItemViewItemText, option, > widget) > margin = style.pixelMetric(style.PM_FocusFrameHMargin, option, > widget) + 1 > textRect.adjust(margin, 0, -margin, 0) > > # set the foreground color to the palette (not to the painter!) > option.palette.setColor(option.palette.Active, > option.palette.Base, foreground) > # if you want to have differrent colors for disabled items, use > again the setColor > # method with option.palette.Disabled > #option.palette.setColor(option.palette.Disabled, > option.palette.Base, disabledColor) > > qp.setFont(font) > style.drawItemText(qp, textRect, > style.visualAlignment(option.direction, option.displayAlignment), > option.palette, option.state & style.State_Enabled, > option.fontMetrics.elidedText(index.data(), > option.textElideMode, textRect.width()), > option.palette.Base) > > > class MyTable(QtWidget.QTableView): > def __init__(self, *args, **kwargs): > QtWidgets.QTableView.__init__(self, *args, **kwargs) > self.setItemDelegate(SimpleDelegate()) > > > Unfortunately, this is not quite perfect: while it should be fine for most > user cases, it doesn't take into account the word wrapping, meaning that > the text will always be on one line, no matter how much the row height is > big. To fix this, it would take about 60-70 more lines of code, which would > be run at each paintEvent for each visible item. Not always a good idea, > but if you think it's good enough, you can find how it's done from the > calculateElidedText method called by viewItemDrawText in here: > https://code.woboq.org/qt5/qtbase/src/widgets/styles/qcommonstyle.cpp.html > > Cheers, > Maurizio > > -- > È difficile avere una convinzione precisa quando si parla delle ragioni > del cuore. - "Sostiene Pereira", Antonio Tabucchi > http://www.jidesk.net > _______________________________________________ PyQt mailing list [email protected] https://www.riverbankcomputing.com/mailman/listinfo/pyqt