[PyQt] (pyqt)set different column font for qtableview
Maziar Parsijani
maziar.parsijani at gmail.com
Fri Apr 26 06:36:06 BST 2019
This is great, thank you so much!
On Fri, Apr 26, 2019 at 3:43 AM Maurizio Berti <maurizio.berti at gmail.com>
wrote:
> Il giorno gio 25 apr 2019 alle ore 21:04 Maziar Parsijani <
> maziar.parsijani at gmail.com> 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190426/10a83c59/attachment-0001.html>
More information about the PyQt
mailing list