[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pykde
Subject:    Re: [PyQt] (pyqt)set different column font for qtableview
From:       Maziar Parsijani <maziar.parsijani () gmail ! com>
Date:       2019-04-26 5:48:06
Message-ID: CAPD04aOTaKVrCJ_xno5bn61SKA+uFwoxi3oBrUX_SkLjaX2uOg () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


This is great, thank you so much!

On Fri, Apr 26, 2019 at 3:43 AM Maurizio Berti <maurizio.berti@gmail.com>
wrote:

> Il giorno gio 25 apr 2019 alle ore 21:04 Maziar Parsijani <
> maziar.parsijani@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
>

[Attachment #5 (text/html)]

<div dir="ltr">This is great, thank you so much! <br></div><br><div \
class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Apr 26, 2019 at 3:43 AM \
Maurizio Berti &lt;<a \
href="mailto:maurizio.berti@gmail.com">maurizio.berti@gmail.com</a>&gt; \
wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px \
0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div \
dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div \
dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div \
dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div \
dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr" \
class="gmail_attr">Il giorno gio 25 apr 2019 alle ore 21:04 Maziar Parsijani &lt;<a \
href="mailto:maziar.parsijani@gmail.com" \
target="_blank">maziar.parsijani@gmail.com</a>&gt; ha scritto:<br></div><blockquote \
class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid \
rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>Hi</div><div>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.</div></div></blockquote><div><br></div><div><br></div></div><div \
dir="ltr">There are different possible approaches, and the choice usually depends on \
how the table data is filled and if it&#39;s editable.<div>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).<br>If you&#39;re \
using a QStandardItemModel, the data is simple and you don&#39;t need interaction, \
just use setData method on each item.</div><div><br></div><div>If you are using any \
model based on QAbstractItemModel you could subclass its data method like \
this:</div><div><br></div><div><div><font face="monospace, monospace">class \
SimpleModel(QtGui.QStandardItemModel):</font></div><div><span \
style="font-family:monospace,monospace">      backgrounds = \
QtGui.QColor(QtCore.Qt.lightGray), QtGui.QColor(QtCore.Qt.darkCyan), \
QtGui.QColor(QtCore.Qt.darkGray)</span><br></div><div><font face="monospace, \
monospace">      foregrounds = QtGui.QColor(QtCore.Qt.darkGreen), \
QtGui.QColor(QtCore.Qt.darkBlue), \
QtGui.QColor(QtCore.Qt.yellow)</font></div><div><div><font face="monospace, \
monospace">      fonts = QtGui.QFont(&#39;monospace&#39;), QtGui.QFont(), \
QtGui.QFont(&#39;times&#39;)</font></div></div><div><font face="monospace, \
monospace"><br></font></div><div><font face="monospace, monospace">      def \
data(self, index, role=QtCore.Qt.DisplayRole):</font></div><div><font \
face="monospace, monospace">            if role == \
QtCore.Qt.BackgroundRole:</font></div><div><font face="monospace, monospace">         \
return self.backgrounds[index.column()]</font></div><div><span \
style="font-family:monospace,monospace">            elif role == \
QtCore.Qt.ForegroundRole:</span><br></div><div><font face="monospace, monospace">     \
return self.foregrounds[index.column()]</font></div><div><font face="monospace, \
monospace">            elif role == QtCore.Qt.FontRole:</font></div><div><font \
face="monospace, monospace">                  return \
self.fonts[index.column()]</font></div><div><span \
style="font-family:monospace,monospace">            return \
QtGui.QStandardItemModel.data(self, index, \
role)</span><br></div></div><div><br></div><div>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&#39;re done; this is usually better and much more \
transparent.</div><div><br></div><div>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.</div><div><br></div><div><div><font \
face="monospace, monospace">class \
SimpleDelegate(QtWidgets.QStyledItemDelegate):</font></div><div><font \
face="monospace, monospace">      backgrounds = QtGui.QColor(QtCore.Qt.lightGray), \
QtGui.QColor(QtCore.Qt.darkCyan), \
QtGui.QColor(QtCore.Qt.darkGray)</font></div><div><font face="monospace, monospace">  \
foregrounds = QtGui.QColor(QtCore.Qt.darkGreen), QtGui.QColor(QtCore.Qt.darkBlue), \
QtGui.QColor(QtCore.Qt.yellow)</font></div><div><font face="monospace, monospace">    \
fonts = QtGui.QFont(&#39;monospace&#39;), QtGui.QFont(), \
QtGui.QFont(&#39;times&#39;)</font></div><div><font face="monospace, monospace">      \
fontData = zip(backgrounds, foregrounds, fonts)</font></div><div><font \
face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">  \
def paint(self, qp, option, index):</font></div><div><font face="monospace, \
monospace">            #painting needs performance, let&#39;s get all data at \
once</font></div><div><font face="monospace, monospace">            background, \
foreground, font = self.fontData[index.column()]</font></div><div><font \
face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">  \
# never reuse the given option argument, always create a new one based on \
it!</font></div><div><font face="monospace, monospace">            # reusing \
QStyleOptions might create issues and inconsistencies with item \
&quot;siblings&quot;</font></div><div><font face="monospace, monospace">            \
option = QtWidgets.QStyleOptionViewItem(option)</font></div><div><font \
face="monospace, monospace">            self.initStyleOption(option, \
index)</font></div><div><font face="monospace, monospace"><br></font></div><div><font \
face="monospace, monospace">            # reset the text so that QStyle won&#39;t \
paint it</font></div><div><font face="monospace, monospace">            option.text = \
&#39;&#39;</font></div><div><font face="monospace, monospace">            \
option.backgroundBrush = background</font></div><div><font face="monospace, \
monospace"><br></font></div><div><font face="monospace, monospace">            widget \
= option.widget</font></div><div><span style="font-family:monospace,monospace">       \
style = widget.style()</span><br></div><div><font face="monospace, \
monospace"><br></font></div><div><font face="monospace, monospace">            # we \
could use the drawPrimitive instead, but it won&#39;t paint the decoration (icon),  \
</font></div><div><font face="monospace, monospace">            # if it exists; \
drawControl paints everything, that&#39;s why I cleared the text \
before,</font></div><div><font face="monospace, monospace">            # otherwise \
you&#39;ll see the text drawn twice</font></div><div><font face="monospace, \
monospace">            style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, option, \
qp)</font></div><div><font face="monospace, monospace">            \
#style.drawPrimitive(QtWidgets.QStyle.PE_PanelItemViewItem, option, \
qp)</font></div><div><br></div><div><span style="font-family:monospace,monospace">    \
# get the rectangle available for item text and adjust it to standard \
margins;</span></div><div><span style="font-family:monospace,monospace">            # \
one pixel is added for consistency with the original Qt painting \
behavior</span></div><div><span style="font-family:monospace,monospace">            \
textRect = style.subElementRect(style.SE_ItemViewItemText, option, \
widget)</span><br></div><div><font face="monospace, monospace">            margin = \
style.pixelMetric(style.PM_FocusFrameHMargin, option, widget) + \
1</font></div><div><font face="monospace, monospace">            \
textRect.adjust(margin, 0, -margin, 0)</font></div><div><font face="monospace, \
monospace"><br></font></div><div><div><font face="monospace, monospace">            # \
set the foreground color to the palette (not to the painter!)</font></div><div><span \
style="font-family:monospace,monospace">            \
option.palette.setColor(option.palette.Active, option.palette.Base, \
foreground)</span><br></div><div><span style="font-family:monospace,monospace">       \
# if you want to have differrent colors for disabled items, use again the \
setColor</span><br></div><div><font face="monospace, monospace">            \
#</font><span style="font-family:monospace,monospace">  </span><span \
style="font-family:monospace,monospace">method</span><span \
style="font-family:monospace,monospace">  with \
option.palette.Disabled</span></div><div><span \
style="font-family:monospace,monospace">            \
#option.palette.setColor(option.palette.Disabled, option.palette.Base, \
disabledColor)</span><br></div><div><font face="monospace, \
monospace"><br></font></div><div><font face="monospace, monospace">            \
qp.setFont(font)</font></div><div><font face="monospace, monospace">            \
style.drawItemText(qp, textRect, style.visualAlignment(option.direction, \
option.displayAlignment),  </font></div><div><font face="monospace, monospace">       \
option.palette, option.state &amp; style.State_Enabled,  </font></div><div><span \
style="font-family:monospace,monospace">                  \
option.fontMetrics.elidedText(index.data(), option.textElideMode, textRect.width()),  \
</span><br></div><div><font face="monospace, monospace">                  \
option.palette.Base)</font></div></div></div><div><font face="monospace, \
monospace"><br></font></div><div><font face="monospace, \
monospace"><br></font></div><div><font face="monospace, monospace">class \
MyTable(QtWidget.QTableView):</font></div><div><font face="monospace, monospace">     \
def __init__(self, *args, **kwargs):</font></div><div><font face="monospace, \
monospace">            QtWidgets.QTableView.</font><span \
style="font-family:monospace,monospace">__init__(self, *args, \
**kwargs)</span></div><div><span style="font-family:monospace,monospace">            \
self.setItemDelegate(SimpleDelegate())</span></div><div><font face="monospace, \
monospace"><br></font></div><div><br></div><div>Unfortunately, this is not quite \
perfect: while it should be fine for most user cases, it doesn&#39;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&#39;s good enough, you can find how it&#39;s \
done from the calculateElidedText method called by viewItemDrawText in here:  <a \
href="https://code.woboq.org/qt5/qtbase/src/widgets/styles/qcommonstyle.cpp.html" \
target="_blank">https://code.woboq.org/qt5/qtbase/src/widgets/styles/qcommonstyle.cpp. \
html</a></div><div><br></div><div>Cheers,</div><div>Maurizio</div><div><br></div></div></div></div></div></div></div>-- \
<br><div dir="ltr" class="gmail-m_6407532008947120603gmail_signature">È difficile \
avere una convinzione precisa quando si parla delle ragioni del cuore. - \
&quot;Sostiene Pereira&quot;, Antonio Tabucchi<br><a href="http://www.jidesk.net" \
target="_blank">http://www.jidesk.net</a></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
 </blockquote></div>


[Attachment #6 (text/plain)]

_______________________________________________
PyQt mailing list    PyQt@riverbankcomputing.com
https://www.riverbankcomputing.com/mailman/listinfo/pyqt


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic