PDA

View Full Version : Painting an image on QItemDelegates



sprek
9th June 2008, 20:48
Hello. I've been having some trouble trying to set up a table where each cell contains an image. To demonstrate the problem, I've modified wysota's example code from this thread: http://www.qtcentre.org/forum/f-qt-programming-2/t-fast-painting-scrollable-widget-9296.html

The way it is right now, scrolling the table messes up the image. If I comment out the line "painter->drawImage(0,0, m_image);" and uncomment the two lines below it that draw an ellipse, it behaves fine.

Can someone please tell me what I'm doing wrong? Thanks!

(To run the code you need to replace "/path/to/image.jpg" with a valid path.)


#include <QApplication>
#include <QTableWidget>
#include <QImage>
#include <QItemDelegate>
#include <QPainter>

const int num_images = 10;
const int img_width = 400;
const int img_height = 400;

class Delegate : public QItemDelegate {
public:
Delegate(QObject *parent=0) : QItemDelegate(parent){
QImage newImage("/path/to/image.jpg");
m_image = newImage.copy();
}

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

painter->drawImage(0,0, m_image);
//painter->setPen (QPen (Qt::red, 3));
//painter->drawEllipse(option.rect);
}

private:
QImage m_image;
};

int main(int argc, char **argv){
QApplication app(argc, argv);

QTableWidget tableWidget(1, num_images);
for (int i = 0; i < num_images; i++) {
tableWidget.setColumnWidth(i, img_width);
}
tableWidget.setRowHeight(0, img_height);
tableWidget.setSelectionMode(QAbstractItemView::No Selection);
tableWidget.setHorizontalScrollMode(QAbstractItemV iew::ScrollPerPixel);

tableWidget.setItemDelegate(new Delegate(&tableWidget));
tableWidget.show();

return app.exec();
}

jpn
10th June 2008, 15:14
You should draw within "option.rect" instead of point (0,0).

sprek
10th June 2008, 16:42
Thanks! using painter->drawImage(option.rect.x(),option.rect.y(), m_image); works.

sprek
11th June 2008, 23:35
Sorry, I'm stuck again. This time I can't get the QTableWidget to repaint.

The program below demonstrates the trouble I've been having. Originally I had it so that whenever the button is clicked, a new image will get added to my tableWidget. In order to get the image to display each time, I had to call tableWidget->update() and tableWidget->setFocus().


However, if I add images to my tableWidget from a timer (which it's doing now), any combination of update(), repaint(), and setFocus() has no effect. It only repaints when I scroll the table or click on a cell. Any help would be much appreciated. Thanks!

main.cpp

#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtGui>

class Delegate : public QItemDelegate {
public:
Delegate(QObject *parent=0) : QItemDelegate(parent){ }

void pushImage (const QImage &image)
{
imageVector.push_back(image);
}

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

if (index.column() < imageVector.size()) {
painter->drawImage(option.rect.x(),option.rect.y(),imageVec tor.at(index.column()));
}
}

private:
QVector <QImage> imageVector;
};

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = 0);
~MainWindow();

private slots:
void add_image();

private:
QTableWidget *tableWidget;
QPushButton *button;
Delegate *imageDelegate;
};

#endif // MAINWINDOW_H


mainwindow.cpp (need to replace /path/to/image with a valid path)

#include "mainwindow.h"
#include <QtGui>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *widget = new QWidget;
setCentralWidget(widget);

tableWidget = new QTableWidget;

tableWidget->setColumnCount(7);
tableWidget->setRowCount(1);
imageDelegate = new Delegate(tableWidget);
tableWidget->setItemDelegate(imageDelegate);
tableWidget->setSelectionMode(QAbstractItemView::NoSelection);
tableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollP erPixel);

for (int i = 0; i < tableWidget->columnCount(); i++) {
tableWidget->setColumnWidth(i,400);
}
tableWidget->setRowHeight(0,400);

button = new QPushButton;


QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(tableWidget);
layout->addWidget(button);
widget->setLayout(layout);

setMinimumSize(160,160);
resize(1000, 800);

QObject::connect(button, SIGNAL(clicked()), this, SLOT(add_image()));

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(add_image()));
timer->start(1000);
}

MainWindow::~MainWindow()
{

}

void MainWindow::add_image()
{
QImage newImage("/path/to/image");
imageDelegate->pushImage(newImage);
tableWidget->repaint();
tableWidget->update();
tableWidget->setFocus();
}

wysota
12th June 2008, 00:00
The delegate is only used to display data that is contained in the model (be it a real one or a hidden one when using convenience widgets). Storing any data within the delegate itself doesn't make much sense. it's like you wanted to store different coulours of paint on a brush for future use :) Treat the delegate as a brush that is used many many times for each and every item that gets drawn. Store the data in the model and use the delegate to fetch the data from the model and render it on the view. I don't know what is your exact usecase but doing it the way you are using now will simply not work.