PDA

View Full Version : QGraphicsView / QGraphicsItem selective visibility question



d_stranz
1st February 2013, 19:50
I have a QGraphicsScene that contains many text labels (a subclass of QGraphicsTextItem). I would like to render the same scene in multiple QGraphicsView windows. Depending on the region of the scene displayed in each view and the pixel size of the view, some of the labels in a particular view might overlap.

I want to prevent this. Is there a mechanism in the Graphics / View architecture to selectively disable rendering of QGraphicsItem instances on a view-by-view basis?

I do not think that z-order and clipping will do what I want. The text is labeling points in a data plot. I do not want the labels to clip the data curve under them (or vice-versa), nor do I want "half labels" where two labels overlap and only part of the one on the bottom of the z-order gets drawn. If I am wrong about that, let me know.

A couple more points of information - all of the labels for a given data curve are siblings, and each curve's labels are children of the curve. A plot might have multiple curves, each with its own set of child labels. Label collision detection must be done on a global basis.

I already have code to do the collision detection and selective hiding of labels, but it only works when there is a single view for the scene. It does this by examining all labels, and setting the visible flag to false for the label with a lower z value when a collision is detected. his obviously won't work when there are multiple views of the same scene.

norobro
1st February 2013, 21:04
From QGraphicsItem::isVisible():
Note that the item's general visibility is unrelated to whether or not it is actually being visualized by a QGraphicsView.

So doesn't your collision detection work for the whole scene? If so, you can then set a different sceneRect on the second view.

Seems to work in this simple example that I created while following your previous thread. Please excuse the state of the code!
#include <QtGui>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
int noItems =10000;
int xSize = 1600;
int ySize = 900;
qsrand(QTime::currentTime().msec());
QElapsedTimer t;
t.start();
QGraphicsScene scene(0,0,xSize,ySize); // put some other items in the scene
QGraphicsItem *i = scene.addEllipse(600,600,150,150,QPen(),QBrush(Qt: :cyan));
i->setFlag(QGraphicsItem::ItemIsMovable);
i = scene.addRect(300,300,160,90,QPen(),QBrush(Qt::yel low));
i->setFlag(QGraphicsItem::ItemIsMovable);
for(int i=0;i<noItems;++i){
QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem;
if(!(i%1333)){
item->setZValue(2);
item->setText(QString("Really High Z-Item #%1").arg(i));
item->setBrush(QBrush(Qt::red));
}
else if(!(i % 133)) {
item->setZValue(1);
item->setText(QString("High Z-Item #%1").arg(i));
item->setBrush(QBrush(Qt::magenta));
}
else item->setText(QString("Item #%1").arg(i));
scene.addItem(item);
item->setFlag(QGraphicsItem::ItemIsMovable);
item->setPos(qrand()%(xSize-75),qrand()%(ySize-10));
}
qDebug() << "populate scene" << t.elapsed();
QList<QGraphicsItem *> list = scene.items(); // ordered by zValue - hides items underneath higher zValue items first
int visible =10000;
foreach(QGraphicsItem *itemByZ, list){
//++visible;
if(itemByZ->isVisible() && itemByZ->type()==QGraphicsSimpleTextItem::Type){ // Don't check non-QGraphicsSimpleTextItems
QList<QGraphicsItem *> itemsUnderList = scene.items(itemByZ->mapToScene(itemByZ->boundingRect()));
foreach(QGraphicsItem *itemUnder, itemsUnderList)
if(itemUnder->type()==QGraphicsSimpleTextItem::Type && itemUnder!=itemByZ){
itemUnder->hide(); //Don't hide non-QGraphicsSimpleTextItems
--visible;
}
}
}
qDebug() << "Finished" << t.elapsed() << "visible"<< visible;
QGraphicsView view;
view.setScene(&scene);
view.show();
QGraphicsView view1;
view1.setSceneRect(0,0,250,250);
view1.setScene(&scene);
view1.show();
return a.exec();
}

d_stranz
1st February 2013, 21:59
foreach(QGraphicsItem *itemByZ, list){
//++visible;
if(itemByZ->isVisible() && itemByZ->type()==QGraphicsSimpleTextItem::Type){ // Don't check non-QGraphicsSimpleTextItems
QList<QGraphicsItem *> itemsUnderList = scene.items(itemByZ->mapToScene(itemByZ->boundingRect()));
foreach(QGraphicsItem *itemUnder, itemsUnderList)
if(itemUnder->type()==QGraphicsSimpleTextItem::Type && itemUnder!=itemByZ){
itemUnder->hide(); //Don't hide non-QGraphicsSimpleTextItems
--visible;
}
}
}


I don't see how this operates dynamically. This is just a static evaluation of the *scene* position of each item before the scene is even assigned to a view, much less rendered. What I need is something that evaluates the *view* position of each item when rendering.

I guess I forgot to mention a very important point, which probably explains why you posted this solution. The text labels have the ItemIgnoresTransformations flag set, so the labels do not scale with the view scaling (as occurs when the user zooms in / out on a region). Thus the pixel size of the labels is invariant, so as you zoom in, the labels move farther apart.

In an earlier version of Qt, I think QGraphicsView had a protected "drawItems" method (or something like that). This is gone in 4.8. So I will probably have to implement a custom QGraphicsView with a paintEvent handler where this collision detection is performed and I selectively choose which items to draw. If I also turn on caching, there will be a one-time performance hit each time the view is resized or the user changes the zoom state.

Thanks to open source, I can see what's happening inside QGraphicsView:: paintEvent() and duplicate it in a derived class :)

Edit: And in examining that source, I see that drawItems() still exists but is not called unless the QGraphicsView IndirectPainting flag is set. This is a problem; who is to say that flag and method will still exist in a future version of Qt?