PDA

View Full Version : performance issue(QGV + QPixmap::grabWidget)



momesana
27th September 2007, 03:49
Hi,
I have a QTabWidget derived class and I have implemented some sort of a ThumbnailMode, where every Tab is displayed as a small Thumbnail in a QGraphicsView and upon clicking on the Thumbnail the corresponding Tab is activated.

The Problem is that creating and displaying the Thumbnails seems to be pretty cpuintensive. With about 20 Tabs it takes approximately one second to switch to Thumbnailmode. Is there a way to improve the performance somehow?
Below, I have pasted the corresponding functions and the QGraphicsItem derived class. If someone needs a minimal compilable version let me know.

Thanx in advance


p.s. Qt-4.4 will hopefully support QWidgets on QGV ...

The functions that populate the scene with items


void ThumbnailScene::createItems()
{
foreach(QGraphicsItem *item, items) {
delete(item);
}
invalidate(sceneRect());
items.clear();


for (int i=0; i<tabWidget->count(); i++) {
QWidget *w = tabWidget->widget(i);
QPixmap pm = QPixmap::grabWidget(w);

ThumbnailItem * item = new ThumbnailItem(pm, i); // Take size from config
items << item;
item->scale(0.2, 0.2);
connect(item, SIGNAL(tabSelected(int)), tabWidget, SLOT(onTabSelected(int)));
connect(item, SIGNAL(tabSelected(int)), this, SIGNAL(toggleThumbnailMode()));
addItem(item);
}
}


void ThumbnailScene::layoutThumbs(int w) // w is the width of the scene
{
int padding, x, y, maxw;
padding = 20;
x = 0;
y = padding;
maxw = w;

foreach(QGraphicsItem * item, items) {
QRectF bRect = item->boundingRect();
QPointF tl,br;
tl = item->mapToScene(bRect.topLeft());
br = item->mapToScene(bRect.bottomRight());
QRectF rect(tl, br);

if (x + rect.width() > maxw) {
x = 0;
y += rect.height() + padding;
}

item->setPos(x, y);
x += rect.width() + padding;
}

setSceneRect(0, 0, itemsBoundingRect().width(), itemsBoundingRect().height() + padding*2);
}
The implementation of the QGraphicsItem


ThumbnailItem::ThumbnailItem(const QPixmap& pixmap, int index, QGraphicsItem *parent)
: QGraphicsItem(parent), mIndex(index), pm(pixmap)
{
setAcceptsHoverEvents(true);
}

QRectF ThumbnailItem::boundingRect() const
{
return QRectF(0, 0, pm.width(), pm.height());
}

void ThumbnailItem::paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*)
{
p->setRenderHints(QPainter::Antialiasing);
p->setPen(QPen(Qt::gray, 6));
p->drawPixmap(0, 0, pm);
p->drawRect(0, 0, pm.width(), pm.height());
}

wysota
27th September 2007, 10:14
First of all don't use item->scale, because this will make the pixmap scale each time the item is redrawn. Scale the pixmap itself, so that each is scaled only once. And second of all you can add items in batches - like add 4 items, process events, add another 4 items, process events, etc. This way you won't block the application and you'll be able to perform other tasks while thumbnails are being built.

momesana
27th September 2007, 11:02
Thanks for the reply wysota. According to your suggestions, I have changed the function createItems in the following way:


for (int i=0; i<tabWidget->count(); i++) {
foreach(QGraphicsItem *item, items) {
delete(item);
}
invalidate(sceneRect());
items.clear();

int batchSz = 4;
for (int i=0; i<tabWidget->count(); i++) {
QWidget *w = tabWidget->widget(i);
QPixmap pm = QPixmap::grabWidget(w);
pm = pm.scaled(300,200, Qt::IgnoreAspectRatio);
ThumbnailItem * item = new ThumbnailItem(pm, i); // TODO Take size from config
items << item;
connect(item, SIGNAL(tabSelected(int)), tabWidget, SLOT(onTabSelected(int)));
connect(item, SIGNAL(tabSelected(int)), this, SIGNAL(toggleThumbnailMode()));
if (items.count() % batchSz == 0) {
foreach(QGraphicsItem *i, items.mid(items.count()-4))
addItem(i);
QCoreApplication::processEvents();
}
}
foreach(QGraphicsItem *i, items.mid(items.count() - items.count()%batchSz))
addItem(i);
}
Now the switch to thumbnail-mode is immediate which is a good thing. But, now the users see the items being created which is not optimal. Is it expensive to add some QGV Animation to hide this from the user? Maybe to display an opaque QGraphicsRectItem that covers the whole scene and then fading it out using QTimeLine in conjunction with QGraphicsItemAnimation?

Thanx

wysota
27th September 2007, 11:06
Yes, you can do that. You can even hide() all items after you create them and then when all items have been created, show() all of them in one go.

momesana
27th September 2007, 11:31
Thank you. I'll try that :).