QGraphicsView: Widgets on the Canvas

Published Friday March 9th, 2007
10 Comments on QGraphicsView: Widgets on the Canvas
Posted in Graphics Items, Graphics View, Labs, Qt

Items or widgets? QGraphicsItem or QWidget? Which should I choose? Can you do everything with both? Mostly. You can write a games board widget, or a games board scene. An audio spectrum widget, or an audio spectrum item. Is QWidget faster? It can be, especially for simple widgets, and if you do everything right and have the time to spend. Will Graphics View save me time?

Maybe the task you’re trying to solve is a perfect match for Graphics View, and as you go along implementing it, you implement zooming and scrolling and right there I’d like to double-click to open a line edit – pushbutton box but… how? Maybe you search the QGraphicsItem docs and can’t find anything, so you create a QPushButton, and uh.. Where do I put it? Who is its parent? What do I do when the user scrolls the view? (Install event filters?)

void MyItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    QPushButton *button = new QPushButton("Hello!", event->widget());
    button->move("#¤!¤ &am!#¤!"#¤%&/(/p;!¤#QRE // censored
}

Embedding a widget inside a QGraphicsScene… does stir your interest? Have you ever invested time and energy on creating the world’s finest widget, and then wanted to use it with Graphics View? Or maybe your scene could use a combobox /right there/, but then you go… oh, I don’t have any QGraphicsComboBoxItem. So what do you do?

  • Put the widget on one of the QGraphicsViews’ viewports… somewhere.
    • It’s /the/ widget, no emulation, no nothing.
    • Supports any widget, even custom widgets.
    • But: It’s hard to keep track of the item’s movement.
    • But: The item cannot be transformed. OTOH, it always looks right.
    • But: The item is always on top of the non-widget items (i.e., stacking order does not apply anymore).
    • But: With several views, the widget will have to pick one somehow.
  • Keep the actual widget outside the desktop, create an “emulator” item that draws by redirecting the painter, and sending paint events.
    • Supports any widget, even custom widgets.
    • You can have the same widget in many views.
    • You can transform the widget (but it won’t necessarily look good).
    • But: You have to emulate all events, and the widget can only support the subset of what QGraphicsItem supports.
    • But: It’s a horrible horrible hack, and I don’t even know if it works at all ;-).
  • Write your own widget item, using QStyle to make it look like a real widget.
    • You have full control over the item, and can make it do what you want.
    • But: You have to write one item for each widget you want.
    • But: QStyle isn’t exactly resolution-independent. Rotating something that uses cosmetic pens, for example?….
    • But: As fun as it may sound, you still have to implement all the logics inside the item yourself.

With either approach, what you want is to end up with something like this, graphics items and widgets dancing together as if nothing had happened:

QGraphicsWidgetItem in action

I myself and several other Trolls’ve spent some time researching this topic. It’s not trivial; most solutions to embedding widgets into a scene end up with several serious drawbacks. That’s also why Qt doesn’t have any off-the-shelf solution to this.

  • Widgets cannot be scaled or rotated, but graphics items can.
  • Widgets can only appear once on the desktop, but several views can observe one graphicsitem.
  • Widgets express their geometries in pixels, graphics items use logical units. (…int vs. double)
  • Widgets support tons of features that graphics items don’t understand. Just look at QWidget’s property docs.
  • Widgets understand layouts, graphics items don’t.
  • 4000000 widgets don’t work that well, but 4000000 items works perfectly.

So, fundamentally, the two are very different. But place some push button items on a scene, and pull up a view, and compare that to QPushButtons inside a QScrollArea, the two look the same, and feel the same. But they aren’t the same. And that especially comes into play when you try to combine them.

I think the most reasonable thing to do is to either use the widget directly, or write your own custom control. To demonstrate the first approach, I’ve added a Graphics View experimental items section in Labs. You can take a look, download the example and see how it works. This approach doesn’t support transformations, but… when was the last time you typed text comfortably into a line edit at a 22 degree angle?…

http://labs.trolltech.com/page/Projects/Graphics_View/Graphics_Items

Btw, later on we’ll be adding more experimental items to this page. If you have suggestions, please let us know.

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in Graphics Items, Graphics View, Labs, Qt

10 comments

manyoso says:

“Keep the actual widget outside the desktop, create an “emulator” item that draws by redirecting the painter, and sending paint events.”

I’ve tried it. Doesn’t work worth a (#&$(#^% (censored)

Imagine emulating a QCombBox for instance. You transform the QGraphicsSceneMouseEvent into a proper QMouseEvent and send it along to the real item all the while redirecting the painting so graphicsview controls. Now, where do you think the listview of the QComboBox will show up?

Doesn’t work.

wysota says:

You can try a hybrid approach – “emulate” a widget when no interaction is needed and place a real widget when you need to interact with it. Of course it still sucks if you want to scale/rotate/animate the scene.

My personal best candidate for the job is the last approach – using QStylePainter or simmilar. If we had a “QGraphicsWidgetItem” that would provide some basic pseudo-widget functionality, one could subclass the item to provide more complex “widget” items. It’d be tedious and certainly would have drawbacks, but should be the easiest to handle, at least for simple widgets.

Some time ago we were thinking about a bit simmilar dilema with widgets – providing an OpenGL backend for widgets. Benefits would be obvious, but currently there is no way to do it – one would have to reimplement all the widgets from scratch using the GL paint engine.

I think it would be best to find a solution that addresses both issues at once.

Chouser says:

Fantastic post. I’ve never actually wanted to do this, but it’s so helpful to understand the limits of such a flexible tool set, as described by those who know it best. More blog posts like this one, please!

–Chouser

nobukichi says:

Hi Andreas,

This topic covers exactly what my team need to resolve in the next 6 month.

Do we need to use Qt4.3 snapshot to try the sample?
I could not compile it under WinXP+VS.net2003+Qt4.2.2commercial combination.
The first two error I saw were:

main.cpp(48) : error C2660: ‘QGraphicsScene::addEllipse’ : function does not take 6 arguments
qgraphicswidgetitem.cpp(32) : error C2065: ‘ItemIgnoresTransformations’ : undeclared identifier

thanks,
nobu

Chris says:

Thanks for the code… I implemented something very similar but ditched out when I couldn’t figure out how to support multiple views.

I think a more generic example should be posted too… one with multiple inheritance from QObject and QGraphicsItem to show how to do signals/slots from QGraphicsItems to other stuff.

Here is a silly example from a game I made my niece for Christmas… it’s Santa picture that dances around and drops gifts for the Princess to pick up.

class santa : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
public:

santa(QGraphicsItem *parent = 0, QGraphicsScene *scene = 0)
: QGraphicsPixmapItem(parent, scene)
, to_move(20)
, m_num_mm(0)
, m_sign(1)
{
QPixmap santa_pix;
santa_pix.load(“:/Resources/santa.PNG”);
setPixmap(santa_pix);
startTimer(10); // Ever 10 milliseconds update the movement
}

public slots:
// Other GUI elements can signal Santa to drop more gifts
void addWrappedGift(unsigned int number, unsigned int speed)
{
for (unsigned int cur_gift = 0; cur_gift 600)
{
m_sign = -1;
}
else if (m_num_mm > 100)
{
m_sign *= -1;
m_num_mm = 0;
}
else
{
qrand() % 2 == 0 ? m_sign = -1 : m_sign = 1;
}

if (to_move != 0)
moveBy( m_sign * to_move, 0);
to_move = qrand() % 100;
if (to_move m_gifts;
int to_move;
int m_num_mm;
int m_sign;
};

Chris says:

Whoa… my code was destroyed for some reason… the whole middle portion didn’t show up in the post… well, it doesn’t really matter… just ignore everything but the function signatures.

Andreas says:

Thanks for all these comments! And please come with more. Everyone benefits from us finding a nice solution to this problem.

For those who want to compile and try the QGraphicsWidgetItem in the lab, you need today’s snapshot (20070310 or later).

andyed says:

>>This approach doesn’t support transformations, but… when was the last time you typed text comfortably into a line edit at a 22 >?>>gree angle?…

Andreas the ability you have given us in QGraphicsView to edit text at any angle/zoom factor is invaluable for our application. I would love to tell you more but not in a public forum like this. How can I get this info to you ?

wysota says:

I’m sure you can also reach him via a private message @ QtCentre (http://www.qtcentre.org) 😉

Commenting closed.

Get started today with Qt Download now