PDA

View Full Version : Scene vs. multiple views



lni
6th February 2009, 15:50
Hi,

I need multiple views for the same scene, and need to render/paint differently in different views.

How can I detect those views insider QGraphicsItem so I can draw differently? The "widget" argument in the paint(...) method is not reliable as I understand...

Thanks.

lni
6th February 2009, 21:17
Anyone pls?

wysota
6th February 2009, 23:56
In short - you can't. And you shouldn't need it actually. If you need your items to look differently in the two views maybe you should think about having two scenes and synchronizing them.

lni
7th February 2009, 01:09
In short - you can't. And you shouldn't need it actually. If you need your items to look differently in the two views maybe you should think about having two scenes and synchronizing them.

Synchronizing two scenes is too much. I need to take care of all geometry changes, QTransform changes, and pen/brush synchronization etc. I just need different presentation of the same scene...

I think the QGraphics has design issues. It does allow multiple views to look at the same scene, but with assumption they will have the same paint method across the board.. But in reality, different presentation in different views is more common. If Qt wants to follow OpenInventor, then OpenInventor can look at a volume at different angle simultaneously, for instance, I can look at slice X, Y, Z at the same time...

seneca
7th February 2009, 01:27
Have a look at the QStyleOptionGraphicsItem argument of the paint method for QGraphicsItem. You can find there the level of detail, and a matrix as the sum of scene+view matrix. (By subtracting the scene matrix from it you would get the plain view matrix.)


OpenInventor can look at a volume at different angle simultaneously, for instance, I can look at slice X, Y, Z at the same time...

And why exactly do you think the graphics view/scene of Qt would not allow that?

wysota
7th February 2009, 09:19
I think the QGraphics has design issues.
Here we go again :)


It does allow multiple views to look at the same scene, but with assumption they will have the same paint method across the board..
A different paint method most probably means a different shape of the item. And a different shape of the item means that implementations of shape() and boundingRect() will be different. And this in turn implies the items are different which leads to a conclusion that you don't have two views on the same scene but on different scenes as they contain different items.


If Qt wants to follow OpenInventor,
Who said Qt wants to follow OpenInventor?


then OpenInventor can look at a volume at different angle simultaneously, for instance, I can look at slice X, Y, Z at the same time...

As my preposter said, Qt can do that as well. But it doesn't need a different paint method to do that. All it takes is a different transformation applied to the view. Run the chips demo and play with it.

lni
7th February 2009, 16:15
Here we go again

Yes, there is a design issue there.


A different paint method most probably means a different shape of the item


This is not true, example below.




As my preposter said, Qt can do that as well. But it doesn't need a different paint method to do that. All it takes is a different transformation applied to the view.

Using Qt's Model/ModelView approach, you have a model and same model, and you can look at it using QListView, QTreeView, or QTableView. How this can be done? Because the view implement the paint method.

However, this design is not used in QGraphics. It asks QGraphicsItem (the model) to implement the paint method, which obviously causes the problem. What is the need for users to bring up two views looking at the same thing? Because of the views can "zoom" them differently, or can transform them differently? I can zoom/transform the same scene using the same view and be happy with that already. With current design, multiple views doesn't give much to the users. Imagine if QAbstractItemModel has to implement the paint method itself.

In short, how to paint an item is the view's job. The model is to provide data, which should know nothing about painting itself.

If you stands in front of a mirror, you got different reflection depending on the type of mirrors you are using (flat mirror, haha mirror, papa minor, etc.), because it is mirror that gives you reflection. You don't force same kind of reflection to different type of mirror.

wysota
7th February 2009, 19:28
Using Qt's Model/ModelView approach, you have a model and same model, and you can look at it using QListView, QTreeView, or QTableView. How this can be done? Because the view implement the paint method.
Because "items" don't have "shapes", they are just a pieces of data. And actually you are wrong here, the items look the same because they are drawn by QAbstractItemDelegate subclass, the view only lays them out differently.


However, this design is not used in QGraphics. It asks QGraphicsItem (the model)
QGraphicsItem is not a model. QGraphicsScene is closer to a model but it is also related to the presentation layer so you can't treat it as a pure data model. The design is similar (but not identical) - you can use different graphics views to view the same scene. If it were identical, we wouldn't have a need for Graphics View architecture as it would be identical to Item Views architecture.


to implement the paint method, which obviously causes the problem.
Let's assume your point of view is correct. To solve it we would have to move the painting, collision detection and few other things from items and scene to the view. This would:
1. leave the scene as nothing more than a container for items (aka QList<QGraphicsItem*>)
2. make it required to provide another object (let's call it a delegate) to do painting of each item class - you would need to have two objects (the item itself and the delegate for it) to be able to use it. At the same time it would leave QGraphicsItem as nothing more but a container for data and making everything heavier (because of the need for the other object). I don't remember the exact figures but each QGraphicsItem takes at least 48 or 64 bytes (I don't remember which one, you can try it out yourself). Providing a delegate for each class would mean another sizeof(void*) per item + ~100B per item class. With 1M items it would be additional 4 (or 8) MB for items + ~1MB per view for delegates. And all QGraphicsItem subclasses are larger than 48 bytes so the differences would be bigger.
3. make it impossible or at least unnatural to share selections between views as hitting an item in one view would not necessarily mean the same item in another view would occupy the same position.

If we follow that convention it would make everything to be done in the view which would result in merging the scene into the view and effectively having a worse architecture than currently (as it wouldn't be possible to share a scene between views) or a one practically identical to what we have now (if we refactored the view into two objects to make it possible to share some data).


What is the need for users to bring up two views looking at the same thing?
Oh, that's an easy one :) Ever played an RTS game or Civilization/Freeciv/whatever? There is a big view that you can scroll to see the action and a mini-map where you see everything at once. These are two views on the same scene. Let's go further - if you have a functionality to "track" an object from the game in another window (as in Transport Tycoon and family), that's even more views on the same scene. Another example? Every 3D modeller allows to see the same object from different perspectives. The item is painted in exactly the same way (with optional different attributes like shading - which is an argument reasoning your point of view (but read on)).


I can zoom/transform the same scene using the same view and be happy with that already.
Ok but does that mean everybody wants that?


With current design, multiple views doesn't give much to the users. Imagine if QAbstractItemModel has to implement the paint method itself.
QAbstractItemModel is part of QtCore module so it can't paint anything :) It's strictly a data model - what would it paint for SQL models? :)


In short, how to paint an item is the view's job. The model is to provide data, which should know nothing about painting itself.
Yes, that's true. That's why we have the scene and items. Scene is the model, items are "delegates" placed inside it.


If you stands in front of a mirror, you got different reflection depending on the type of mirrors you are using (flat mirror, haha mirror, papa minor, etc.), because it is mirror that gives you reflection. You don't force same kind of reflection to different type of mirror.

Ouch, that's an own goal :)

But you still see yourself. The view is not the mirror - the view is your eye which always "paints" the same way and the mirror only transforms the way rays of light fall onto your eye. So the mirror is the transformation matrix, the eye is the view and the world is the scene.

And now for the fatality that once again proves you don't spend enough time reading the documentation before making statements about wrong design. There is a virtual method called QGraphicsView::drawItems(). Reimplement it. Do the drawing in the view. Change the options passed with QStyleOptionGraphicsItem to QGraphicsItem::paint() - thanks to that you can make your items "wireframe" in one view and "phong shaded" in another with one paint() implementation. The only limitation is that you can't modify the shape of items betwen views but I understand that's not what you want anyway.

Oh... and let others do things their way. The fact that you want something different doesn't mean everyone wants it your way. Graphics View was implemented after gathering experiences from thousands of users of QwtSpriteField (Qt2) and QCanvas (Qt3). It's probably not perfect but you can't say it doesn't do what users wanted.

Edit:
By the way, synchronizing two scenes is easy using QGraphicsItem::itemChange().

lni
7th February 2009, 21:15
I understand it is hard to satisfy everyone's need. Qt is by far the best development framework I have seen. Maybe I am one of the rare users who needs unusual features...

For now, QGraphicsView::drawItems seems to be the closest function I need. Thanks!

wysota
7th February 2009, 23:03
Maybe I am one of the rare users who needs unusual features...
That's not the point. Your problem is that you give opinions on some subject before you learn enough about it.


For now, QGraphicsView::drawItems seems to be the closest function I need. Thanks!

By "closest" I understand it's not exactly what you wanted. What functionality are you missing exactly?

lni
26th February 2009, 21:03
By "closest" I understand it's not exactly what you wanted. What functionality are you missing exactly?

OK, I am getting to it now... current code is this:



void QfwSummaryView::drawItems( QPainter * painter,
int numItems,
QGraphicsItem* items[],
const QStyleOptionGraphicsItem options[] )
{
for ( int idx = 0; idx < numItems; idx++ ) {
QfwSummarizableItem* item = dynamic_cast<QfwSummarizableItem*>( items[ idx ] );
if ( item ) {
painter->save();
item->drawSummary( painter );
painter->restore();
}
}
}



See the attached screen dump, I have 2 tracks with multiple curves, each curve shares the same Y axis, but they have their own X axis. The Y axis is long that require scroll bar, so the idea is to have 2 views: the bottom view is to draw those curves, the top view is to draw "legend" along with X axis min value and max value....

I am currently using the same QGraphicsScene, there is a boundingRect problem, for instance the area plot (in the right track) has narrow width, but the "legend" at the top is wider, yet exposure is controlled by the real object's boundingRect...

So, I think I need to have two scenes, can someone gives me a simple UML class diagram, as well as how to keep then in sync?

wysota
26th February 2009, 21:37
I think this is a candidate case for two scenes. As for synchronization, simply connect signals and slots from the scrollbars together.

lni
26th February 2009, 22:03
I think this is a candidate case for two scenes. As for synchronization, simply connect signals and slots from the scrollbars together.

Scrollbar synchronization is not a problem. But the item's synchronization may be complicated.

The pen may be changed (assuming I have a pen editor), the brush may be changed, or the width of the track may be changed.

At the end, I need to print them in the same paper without QSplitter...

wysota
26th February 2009, 22:10
The pen may be changed (assuming I have a pen editor), the brush may be changed, or the width of the track may be changed.
It's not changed by some "outside force", when you apply the change to items in one scene, you can apply them to items on the other scene as well. You can even store pointers to items from scene A in corresponding items in scene B (and vice versa) to be able to quickly map between them.


At the end, I need to print them in the same paper without QSplitter...
I don't think that's a problem. After all you are rendering scenes, not widgets.

lni
26th February 2009, 22:28
It's not changed by some "outside force", when you apply the change to items in one scene, you can apply them to items on the other scene as well. You can even store pointers to items from scene A in corresponding items in scene B (and vice versa) to be able to quickly map between them.



Ya, somewhere I need to keep link of two items in different scene. But I need a good design with UML class diagram :)