<div dir="ltr"><div dir="ltr">Using multiple columns with a "shared" header cell is possible, but not easy.<div><br></div><div>You'll need to create a subclass of QHeaderView for the horizontal header and possibly set a custom proxy model (QIdentityProxyModel) overriding its setModel, but that's not only. I think setSelectionModel requires some care, and you'll have to implement a lot of things if you need correct resizing of columns.</div><div>I would sincerely discourage this approach, at least if you're looking for a good implementation in a similar scenario: since you're obviously grouping all icons in the same item, the obvious solution is an item delegate.</div><div><br></div><div>Looking at your image reference it seems that the icon on the sixth item is right aligned, but it's unclear if that's because extra icons have some reserved space or it's just a matter of alignment.</div><div><br></div><div>I've decided for a more "standard" approach which simply puts all icons left aligned, but since you've cited tooltips I also added support for those.</div><div>The idea is that you use a role for each icon you want to show, given that a "status" icon is always visible, and any other icon is shown whenever exists and its role is set to True.</div><div>I use QStyle and QStyleItemView to draw the base item rectangle, then find the rectangle where an icon would be shown and use that data to paint all icons.</div><div><br></div><div><div><font face="monospace">from random import choice</font></div><div><font face="monospace">from PyQt5 import QtCore, QtGui, QtWidgets</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">StateRole = QtCore.Qt.UserRole + 1</font></div><div><font face="monospace">FolderRole = StateRole + 1</font></div><div><font face="monospace">DriveRole = FolderRole + 1</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">class IconDelegate(QtWidgets.QStyledItemDelegate):</font></div><div><font face="monospace">    lastIndex = None</font></div><div><font face="monospace">    def __init__(self):</font></div><div><font face="monospace">        super(IconDelegate, self).__init__()</font></div><div><font face="monospace">        self.stateIcons = (QtGui.QIcon.fromTheme('offline'), </font></div><div><font face="monospace">            QtGui.QIcon.fromTheme('online'))</font></div><div><font face="monospace">        self.extraIconData = [</font></div><div><font face="monospace">            (FolderRole, 'Folder', QtGui.QIcon.fromTheme('folder')), </font></div><div><font face="monospace">            (DriveRole, 'Drive', QtGui.QIcon.fromTheme('drive-harddisk'))</font></div><div><font face="monospace">        ]</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">    def sizeHint(self, option, index):</font></div><div><font face="monospace">        hint = super(IconDelegate, self).sizeHint(option, index)</font></div><div><font face="monospace">        option = QtWidgets.QStyleOptionViewItem(option)</font></div><div><font face="monospace">        option.features |= option.HasDecoration</font></div><div><font face="monospace">        widget = option.widget</font></div><div><font face="monospace">        style = widget.style()</font></div><div><font face="monospace">        iconRect = style.subElementRect(style.SE_ItemViewItemDecoration, </font></div><div><font face="monospace">            option, widget)</font></div><div><font face="monospace">        margin = iconRect.left() - option.rect.left()</font></div><div><font face="monospace">        width = iconRect.width() * (len(self.extraIconData) + 1)</font></div><div><font face="monospace">        width += margin * (len(self.extraIconData) + 2)</font></div><div><font face="monospace">        hint.setWidth(width)</font></div><div><font face="monospace">        return hint</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">    def paint(self, qp, option, index):</font></div><div><font face="monospace">        # create a new QStyleOption (*never* use the one given in arguments)</font></div><div><font face="monospace">        option = QtWidgets.QStyleOptionViewItem(option)</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        widget = option.widget</font></div><div><font face="monospace">        style = widget.style()</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        # paint the base item (borders, gradients, selection colors, etc)</font></div><div><font face="monospace">        style.drawControl(style.CE_ItemViewItem, option, qp, widget)</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        # "lie" about the decoration, to get a valid icon rectangle (even if we</font></div><div><font face="monospace">        # don't have any "real" icon set for the item)</font></div><div><font face="monospace">        option.features |= option.HasDecoration</font></div><div><font face="monospace">        iconRect = style.subElementRect(style.SE_ItemViewItemDecoration, </font></div><div><font face="monospace">            option, widget)</font></div><div><font face="monospace">        iconSize = iconRect.size()</font></div><div><font face="monospace">        margin = iconRect.left() - option.rect.left()</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        qp.save()</font></div><div><font face="monospace">        # ensure that we do not draw outside the item rectangle (and add some</font></div><div><font face="monospace">        # fancy margin on the right</font></div><div><font face="monospace">        qp.setClipRect(option.rect.adjusted(0, 0, -margin, 0))</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        # draw the main state icon, assuming all items have one</font></div><div><font face="monospace">        qp.drawPixmap(iconRect, </font></div><div><font face="monospace">            self.stateIcons[index.data(StateRole)].pixmap(iconSize))</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        # cycle through roles, draw an icon if the index has that role set to True</font></div><div><font face="monospace">        left = delta = margin + iconRect.width()</font></div><div><font face="monospace">        for role, name, icon in self.extraIconData:</font></div><div><font face="monospace">            if index.data(role):</font></div><div><font face="monospace">                qp.drawPixmap(iconRect.translated(left, 0), </font></div><div><font face="monospace">                    icon.pixmap(iconSize))</font></div><div><font face="monospace">                left += delta</font></div><div><font face="monospace">        qp.restore()</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">    def helpEvent(self, event, view, option, index):</font></div><div><font face="monospace">        if event.type() != QtCore.QEvent.ToolTip:</font></div><div><font face="monospace">            return super(IconDelegate, self).helpEvent(event, view, option, index)</font></div><div><font face="monospace">        option = QtWidgets.QStyleOptionViewItem(option)</font></div><div><font face="monospace">        widget = option.widget</font></div><div><font face="monospace">        style = widget.style()</font></div><div><font face="monospace">        option.features |= option.HasDecoration</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        iconRect = style.subElementRect(style.SE_ItemViewItemDecoration, </font></div><div><font face="monospace">            option, widget)</font></div><div><font face="monospace">        iconRect.setTop(option.rect.y())</font></div><div><font face="monospace">        iconRect.setHeight(option.rect.height())</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        # similar to what we do in the paint() method</font></div><div><font face="monospace">        if event.pos() in iconRect:</font></div><div><font face="monospace">            # (*) clear any existing tooltip; a single space is better , as</font></div><div><font face="monospace">            # sometimes it's not enough to use an empty string</font></div><div><font face="monospace">            if index != self.lastIndex:</font></div><div><font face="monospace">                QtWidgets.QToolTip.showText(QtCore.QPoint(), ' ')</font></div><div><font face="monospace">            QtWidgets.QToolTip.showText(event.globalPos(), </font></div><div><font face="monospace">                'State: {}'.format(('Off', 'On')[index.data(StateRole)]), view)</font></div><div><font face="monospace">        else:</font></div><div><font face="monospace">            margin = iconRect.left() - option.rect.left()</font></div><div><font face="monospace">            left = delta = margin + iconRect.width()</font></div><div><font face="monospace">            for role, name, icon in self.extraIconData:</font></div><div><font face="monospace">                if index.data(role):</font></div><div><font face="monospace">                    # if the role is set to True check if the mouse is within</font></div><div><font face="monospace">                    # the icon rectangle</font></div><div><font face="monospace">                    if event.pos() in iconRect.translated(left, 0):</font></div><div><font face="monospace">                        # see above (*)</font></div><div><font face="monospace">                        if index != self.lastIndex:</font></div><div><font face="monospace">                            QtWidgets.QToolTip.showText(QtCore.QPoint(), ' ')</font></div><div><font face="monospace">                        QtWidgets.QToolTip.showText(event.globalPos(), name, view)</font></div><div><font face="monospace">                        break</font></div><div><font face="monospace">                    # shift the left *only* if the role is True, otherwise we</font></div><div><font face="monospace">                    # can assume that that icon doesn't exist at all</font></div><div><font face="monospace">                    left += delta</font></div><div><font face="monospace">            else:</font></div><div><font face="monospace">                # nothing interesting, hide the existing tooltip, if any</font></div><div><font face="monospace">                QtWidgets.QToolTip.hideText()</font></div><div><font face="monospace">        self.lastIndex = index</font></div><div><font face="monospace">        return True</font></div><div><font face="monospace"><br></font></div><div><font face="monospace"><br></font></div><div><font face="monospace">class Window(QtWidgets.QWidget):</font></div><div><font face="monospace">    def __init__(self):</font></div><div><font face="monospace">        QtWidgets.QWidget.__init__(self)</font></div><div><font face="monospace">        layout = QtWidgets.QGridLayout()</font></div><div><font face="monospace">        self.setLayout(layout)</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        table = QtWidgets.QTableView()</font></div><div><font face="monospace">        layout.addWidget(table)</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        model = QtGui.QStandardItemModel()</font></div><div><font face="monospace">        model.setHorizontalHeaderLabels(['Name', 'State'])</font></div><div><font face="monospace">        for row in range(5):</font></div><div><font face="monospace">            mainItem = QtGui.QStandardItem('Item {}'.format(row + 1))</font></div><div><font face="monospace">            stateItem = QtGui.QStandardItem()</font></div><div><font face="monospace">            stateItem.setData(choice([0, 1]), StateRole)</font></div><div><font face="monospace">            stateItem.setData(choice([0, 1]), FolderRole)</font></div><div><font face="monospace">            stateItem.setData(choice([0, 1]), DriveRole)</font></div><div><font face="monospace">            model.appendRow([mainItem, stateItem])</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">        table.setModel(model)</font></div><div><font face="monospace">        table.setItemDelegateForColumn(1, IconDelegate())</font></div><div><font face="monospace">        table.resizeColumnsToContents()</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">if __name__ == '__main__':</font></div><div><font face="monospace">    import sys</font></div><div><font face="monospace">    app = QtWidgets.QApplication(sys.argv)</font></div><div><font face="monospace">    w = Window()</font></div><div><font face="monospace">    w.show()</font></div><div><font face="monospace">    sys.exit(app.exec_())</font></div></div><div><br></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Il giorno ven 6 set 2019 alle ore 16:45 donoban <<a href="mailto:donoban@riseup.net">donoban@riseup.net</a>> 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">Hi,<br>
<br>
I am trying to adapt a medium sized pyqt appication to model/view<br>
architecture (<a href="https://github.com/QubesOS/qubes-manager/pull/195" rel="noreferrer" target="_blank">https://github.com/QubesOS/qubes-manager/pull/195</a>)<br>
<br>
I have a problem when trying to imitate the 'State' column behavior. As<br>
you could see here:<br>
<a href="https://www.linuxjournal.com/sites/default/files/styles/850x500/public/nodeimage/story/12011f2.png" rel="noreferrer" target="_blank">https://www.linuxjournal.com/sites/default/files/styles/850x500/public/nodeimage/story/12011f2.png</a><br>
<br>
This column can have multiple icons which show the current state of the<br>
VM. In the internal code it uses a custom widget with a layout and adds<br>
new widgets with a pixmap and tooltip.<br>
<br>
Does anybody know how to imitate this with model/view design? Could I<br>
split the 'State' column in multiple columns which share the same header<br>
cell?<br>
<br>
Regards.<br>
_______________________________________________<br>
PyQt mailing list    <a href="mailto:PyQt@riverbankcomputing.com" target="_blank">PyQt@riverbankcomputing.com</a><br>
<a href="https://www.riverbankcomputing.com/mailman/listinfo/pyqt" rel="noreferrer" target="_blank">https://www.riverbankcomputing.com/mailman/listinfo/pyqt</a><br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature">È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi<br><a href="http://www.jidesk.net" target="_blank">http://www.jidesk.net</a></div>