Jorge C
Jorge C

Reputation: 53

Qt Table widget. How to set the meaning/title for vertical header and horizontal header together?

I know how to set text labels into row or column headers. But I want to do something like this:

https://i.sstatic.net/eMM6U.jpg

I don't find any info on how to do the thing inside the red circumference. I'm starting to believe this can't be done with QTableWidget.

Thanks ;)

Upvotes: 5

Views: 2407

Answers (2)

a_manthey_67
a_manthey_67

Reputation: 4286

if text elements as shown here

textelements in cornerButton

are sufficient, it can be done with standard header and the solution from the link i posted in my comment:

from PyQt5 import QtCore, QtWidgets

class MyTableWidget(QtWidgets.QTableWidget):
    def __init__(self, parent = None):
        QtWidgets.QTableWidget.__init__(self, parent)
        self.setRowCount(4)
        self.setColumnCount(5)
        self.items = []
        self.items.append(['white ppt','no reaction', 'no reaction', 'no reaction', 'no reaction'])
        self.items.append(['no reaction','white ppt', 'dissolves', 'dissolves', 'no reaction'])
        self.items.append(['no reaction','pale yellow\nprecipitate', 'dissolves\npartly', 'dissolves', 'no reaction'])
        self.items.append(['no reaction','yellow ppt', 'does not\ndissolve', 'does not\ndissolve', 'turns\nblue'])
        self.horizontalHeader().setFixedHeight(90)
        self.verticalHeader().setFixedWidth(120)
        self.hh = ['Ca(NO\u2083)\u2082', 'AgNO\u2083','AgNO\u2083\n+\nNH\u2083','AgNO\u2083\n+\nNa\u2083S\u2082O\u2083','Starch\n+\nNaOCl']
        self.vh = ['NaF', 'NaCl', 'NaBr or KBR', 'NaJ']

        self.addItems()
        self.addHeaderItems()
        # text in cornerButton
        btnTxt = '{: >15}\n{: >19}\n{:<}\n{:<}'.format('reagent', '\u21D2','halide', 'ion \u21D3')

        # add cornerbutton from http://stackoverflow.com/questions/22635867/is-it-possible-to-set-the-text-of-the-qtableview-corner-button
        btn = self.findChild(QtWidgets.QAbstractButton)
        btn.setText(btnTxt)
        btn.installEventFilter(self)

        opt = QtWidgets.QStyleOptionHeader()
        opt.text = btn.text()    
        # end cornerbutton 

    def addItems(self):  
        for r in range(0,len(self.items)):
            for c in range(0,len(self.items[r])):
                item = QtWidgets.QTableWidgetItem()
                item.setText(self.items[r][c])
                self.setItem(r,c,item)

    def addHeaderItems(self):
        for i in range(0,len(self.hh)):
            item = QtWidgets.QTableWidgetItem()
            item.setText(self.hh[i])
            self.setHorizontalHeaderItem(i,item)
            self.setColumnWidth(i,150)
        for i in range(0,len(self.vh)):
            item = QtWidgets.QTableWidgetItem()
            item.setText(self.vh[i])
            self.setVerticalHeaderItem(i,item)
            self.setRowHeight(i,60)

        # eventfilter from http://stackoverflow.com/questions/22635867/is-it-possible-to-set-the-text-of-the-qtableview-corner-button
    def eventFilter(self, obj, event):
        if event.type() != QtCore.QEvent.Paint or not isinstance(
            obj, QtWidgets.QAbstractButton):
            return False

        # Paint by hand (borrowed from QTableCornerButton)
        opt = QtWidgets.QStyleOptionHeader()
        opt.initFrom(obj)
        styleState = QtWidgets.QStyle.State_None
        if obj.isEnabled():
            styleState |= QtWidgets.QStyle.State_Enabled
        if obj.isActiveWindow():
            styleState |= QtWidgets.QStyle.State_Active
        if obj.isDown():
            styleState |= QtWidgets.QStyle.State_Sunken
        opt.state = styleState
        opt.rect = obj.rect()
        # This line is the only difference to QTableCornerButton
        opt.text = obj.text()
        opt.position = QtWidgets.QStyleOptionHeader.OnlyOneSection
        painter = QtWidgets.QStylePainter(obj)
        painter.drawControl(QtWidgets.QStyle.CE_Header, opt)

        return True

if painter.drawControl()-method is replaced by any other painter.draw...()-method arbitrary elements incl. graphics can be drawn on cornerButton.

Upvotes: 1

Jablonski
Jablonski

Reputation: 18504

I think that it is impossible with standard headers (QHeaderView) because of:

Note: Each header renders the data for each section itself, and does not rely on a delegate. As a result, calling a header's setItemDelegate() function will have no effect.

So you need forgot about it and disable it, you should implement your own headers(set your color, your text and so on), but I of course will help with meaning/title. I achieved this with next item delegate:

.h:

#ifndef ITEMDELEGATEPAINT_H
#define ITEMDELEGATEPAINT_H

#include <QStyledItemDelegate>

class ItemDelegatePaint : public QStyledItemDelegate
{
    Q_OBJECT
public:
    explicit ItemDelegatePaint(QObject *parent = 0);
    ItemDelegatePaint(const QString &txt, QObject *parent = 0);


protected:
    void paint( QPainter *painter,
                const QStyleOptionViewItem &option,
                const QModelIndex &index ) const;
    QSize sizeHint( const QStyleOptionViewItem &option,
                    const QModelIndex &index ) const;
    QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    void setEditorData(QWidget * editor, const QModelIndex & index) const;
    void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const;
    void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const;

signals:

public slots:

};

#endif // ITEMDELEGATEPAINT_H

But all these method is not very useful here, you can implement it yourself with some specific actions, I will show you main method - paint()

void ItemDelegatePaint::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.row() == 0 && index.column() == 0)
    {
        QRect source1 = QRect(option.rect.topLeft(),option.rect.size()/2);
        QRect source2 = QRect(option.rect.topLeft(),option.rect.size()/2);

        painter->drawLine(option.rect.topLeft(),option.rect.bottomRight());

        source1.moveTopLeft(source1.topLeft() += QPoint(source1.size().width(),0));
        painter->drawText(source1,"agent reagent");

        source2.moveBottomLeft(source2.bottomLeft() += QPoint(0,source2.size().height()));
        painter->drawText(source2,"hallide ion");
    }

    else
    {
        QStyledItemDelegate::paint(painter,option,index);
    } 
}

This code shows main idea and it is not final version, but you should do all these specific things by yourself. Of course this approach is not very easy, you can just create picture and set it to cell, but in this case picture will not be good scalable. My code works normal if user will resize some headers. To prove, see screenshots with different sizes.

enter image description here

enter image description here

Upvotes: 2

Related Questions