Mohamed Anwer
Mohamed Anwer

Reputation: 182

Painting QPixmap in the center of QTableView cell

I have a QTableView that works very well, the first column holds some thumbnails, in each cell of this column the thumbnails are vertically centered, but not horizontally centered.

Do I really need to use a delegate? If yes, How to center them horizontally using QStyledItemDelegate?

Upvotes: 4

Views: 4511

Answers (3)

dismine
dismine

Reputation: 575

I will just leave my version that literary is a combination of the two answers.

class DecorationAligningDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    explicit DecorationAligningDelegate(Qt::Alignment alignment, QObject *parent = nullptr)
    : QStyledItemDelegate(parent), m_alignment(alignment) {}

    Qt::Alignment alignment() const { return m_alignment; }

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        QIcon icon = QIcon(qvariant_cast<QIcon>(index.data(Qt::DecorationRole)));

        if (option.state & QStyle::State_Selected)
        {
             painter->fillRect(option.rect, option.palette.highlight());
        }

        icon.paint(painter, option.rect, m_alignment);
    }

private:
    Q_DISABLE_COPY(VDecorationAligningDelegate)
    Qt::Alignment const m_alignment;
};

I assume you define your item like this:

auto *item = new QTableWidgetItem();
item->setIcon(QIcon("Your pixmap file path"));

Don't forget about setting a delegate.

Upvotes: 1

kh25
kh25

Reputation: 1298

Construct your own delegate and inherit QStyledItemDelegate. Override the paint method.

Then do something like this:

void
MyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
                            const QModelIndex& index) const
{

     QPixmap pixmap;
     pixmap.load("Your pixmap file path");
     pixmap = pixmap.scaled(option.rect.width(), option.rect.height(), Qt::KeepAspectRatio);

    // Position our pixmap
    const int x = option.rect.center().x() - pixmap.rect().width() / 2;
    const int y = option.rect.center().y() - pixmap.rect().height() / 2;

    if (option.state & QStyle::State_Selected) {
        painter->fillRect(option.rect, option.palette.highlight());         
    }

    painter->drawPixmap(QRect(x, y, pixmap.rect().width(), pixmap.rect().height()), pixmap);

}

Upvotes: 5

Drawing by yourself is not necessary, but a custom delegate - is. The styled item delegate uses the style's control element drawing code to draw a CE_ItemViewItem - see the source code for Qt 5.5.0. The drawing code takes the style option's decorationAlignment member into account. Unfortunately, there's no data role that would pass that alignment to the styles's implementation. Instead, you have to override the alignment in your delegate:

class DecorationAligningDelegate : public QStyledItemDelegate {
  Q_OBJECT
  Qt::Alignment const m_alignment;
public:
  explicit DecorationAligningDelegate(Qt::Alignment alignment, QObject * parent = 0) :
    QStyledItemDelegate(parent), m_alignment(alignment) {}
  Qt::Alignment alignment() const { return m_alignment; }
  void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
    auto opt = option;
    opt.decorationAlignment = m_alignment;
    QStyledItemDelegate::paint(painter, opt, index);
  }
};

Then, to center the thumbnails:

view.setItemDelegateForColumn(0, 
  new DecorationAligningDelegate(Qt::AlignHCenter, &view));
//or
view->setItemDelegateForColumn(0, 
  new DecorationAligningDelegate(Qt::AlignHCenter, view));

If you really wished to paint it all yourself, even though it's unnecessary, the rectangle of the item to be painted is given in the style option (option.rect). To draw the pixmap centered in the item's rectangle, you could do as follows:

QStyleOption option;
QPixmap pix;
QPainter painter;
...
painter.save();
auto loc = option.rect.center() - pix.rect().center()
painter.drawPixmap(loc, pix);
painter.restore();

Upvotes: 3

Related Questions