sroedal
Qt
Graphics View
Graphics Items
Graphics Dojo
Posted by sroedal
 in Qt, Graphics View, Graphics Items, Graphics Dojo
 on Friday, June 27, 2008 @ 13:47

To breathe some new life into the graphics dojo, here’s an example of how to put widgets on top of an OpenGL scene using QGraphicsView. By leveraging the synergy (tounge in cheek) of the OpenGL module and graphics view’s in 4.4 new widget capabilities, the long lacking feature of putting widgets in OpenGL becomes possible. All that’s needed is to set a QGLWidget as viewport on the graphics view, override QGraphicsScene::drawBackground() to do the OpenGL rendering, and add widgets and other graphics items to the graphics scene as usual. The result can be seen in the screenshot below, which shows a simple obj-model viewer application written in just a couple of hundred lines of code:

Widgets on top of an OpenGL scene

The source code is available by “svn checkout svn://labs.trolltech.com/svn/graphics/dojo/modelviewer”. Have fun!

Andreas
Qt
KDE
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, KDE, Labs, Graphics View, Graphics Items
 on Thursday, November 22, 2007 @ 11:05

The day has finally arrived. We are proud to announce that we have now integrated our development branch for the Widgets On The Canvas project into Qt/main, or what will become Qt 4.4. You have already seen a random selection of screenshots and feature rundowns in previous blogs, but now it’s all in your hands to try and enjoy. It should appear in tonight’s snapshots.

I’d like to thank Jan-Arve, Bjørn Erik, Jo, Benjamin, Jasmin, Girish, Lars, and everyone else who has helped us with rowing this boat ashore. It was a beast, but we seem to have managed to tame it. Now, we’d all like for you to play around with this API, and let us know what you think.

Graphics View now provides a brand new layout and widget API. It’s similar to the way QWidget works, and should be familiar for most of you right away. The purpose of this API is mainly so you can write layout-aware items, but it also means you now have a richer base class for your items. Because QGraphicsWidget inherits from QObject, it allows declaring signals and slots and manages event delivery just like any widget in Qt. Like QWidget it also provides a palette, a font, layout directions and a style.

Graphics View doesn’t provide its own widgets like QLineEdit and QComboBox - instead, you can embed existing widgets (indeed, any custom widget that you have written yourself) into the scene by calling QGraphicsScene::addWidget(). If you also pass the Qt::Window flag, your widgets will even get window decorations similar to QMdiSubWindow. You can move, resize, rotate and scale your widgets, or combine widgets and items to generate fancy overlay or transition effects.

There are still a few remaining issues to solve. For one, Graphics View automatically embeds popups of embedded widgets. So if you embed a combobox, its popup will also appear as an embedded popup inside the scene. And QMenuBar’s menus also also embedded. We aim to make this feature work 100% for 4.4, but currently it doesn’t work properly (in particular, modality and mouse grabs are missing). Also, there’s the well-known Mac OS X styling problem. We still haven’t found a perfect solution for this, so for now, if you use Mac style for Graphics View, embedded widgets will look a bit chunky when transformed. And finally, because this is all brand new API, we expect some bugs to appear in this pre-alpha phase, and hope to iron them out based on your feedback. But hey - Widgets on Graphics View! How about it??? ;-)

PS: The members of the WOC project are all present and listening on qt4-preview-feedback@trolltech.com (also available through nntp.trolltech.com). Now is the time that you can help hone Widgets on Canvas to be just as great as we all want it to be. So enjoy! We’re eagerly awaiting your feedback.

Andreas
Qt
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, Labs, Graphics View, Graphics Items
 on Friday, March 09, 2007 @ 16:10

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.

Andreas
Qt
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, Labs, Graphics View, Graphics Items
 on Thursday, March 01, 2007 @ 20:22

I don’t have much to say this time, just wanted to share a screenshot after I changed the 40000 Chips demo a bit. I replaced the single horizontal rotation-slider with three ones, each controlling one axis or rotation for the view. QTransform is really easy to use, and with some twiddling (thumb up, pointing finger straight and your index finger pointing to the left), you can get some pretty amazing effects.

QGraphicsView view(&scene);

QTransform transform;
transform.rotate(45, Qt::XAxis);
transform.rotate(22.5, Qt::YAxis);

view.setTransform(transform);
view.show();

Setting one on a QGraphicsView works well, but there are still some rough edges that’ll get fixed pretty soon. Anyway, this is what it looks like (interaction works just as well).


Transform - small

Andreas
Qt
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, Labs, Graphics View, Graphics Items
 on Tuesday, February 27, 2007 @ 19:12

We’re in the process of closing up the features for 4.3, and I’ve had a little run-down of what’s changed in Graphics View.

In general, you will notice that the graphics run faster, (especially for complex polygons,) collisions are absolutely precise, the rubberband looks 100x better… I think most of our Graphics View followers will be very pleased with the upcoming Qt 4.3.

When the first beta is released, it’s nice if you as a Graphics View developer try out your Qt 4.2 app with 4.3, so that we can nail out any possible regressions (both behavior and speed) before the final version is out. So please, let us know :-).

Here are some of the new features:

  • QGraphicsView has some new properties.
    • viewportUpdateMode: Now you can fine-tune how the viewport reacts the changes in the scene.
    • rubberBandSelectionMode: For those who want to select items that are inside but don’t intersect with the rubberband…
    • optimizationFlags: If you want faster graphics, away goes the safety net… ;-)
  • All flicker in rubberband mode is gone.
  • You can also transform the view with a QTransform; this works best for small scenes, though. But the effect of seeing your scene with perspective transformations is pretty cool.
  • QGraphicsItem has some new flags.
    • ItemClipsToShape: All item rendering is clipped to its own shape
    • ItemClipsChildrenToShape: Like widgets, the parent item clips all its descendents.
    • ItemIgnoresTransformations: The lovable item that just won’t transform… well.

There’s been some under-the-hood changes too. Where before, if you moved an item, it would always get continuously reindexed (for each mouse move event), now it goes into an intermediate state that keeps the item outside the index until you leave it for a while. When moving a lot of items together, this makes a huge difference. It also means you don’t have to use NoIndex for scenes that combine animated and static items! It’ll still be pretty fast.

Check out the chip demo, or run the Colliding Mice example in one window, and top/Task Manager in the other; it’s faster in Qt 4.3 snapshots!

[no carrier] - /me waits for the first comment going “Hey, it’s not fast at all!” ;-)

Andreas
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Labs, Graphics View, Graphics Items
 on Sunday, January 21, 2007 @ 17:43

Last summer, while we were finishing up 4.2, there was a great deal of activity on the mailing lists we set up for the 4.2 technical preview (http://lists.trolltech.com/). Some people kept on asking for support for “untransformable” items, that is, special QGraphicsItem items that don’t follow the parent’s or view’s transformations. Like the text labels in Google Earth, they stay horizontal when you rotate, and don’t change their size when you zoom.

Because of the risk of destabilizing the base behavior, we decided to postpone such a feature, and it was moved into research.

Good news, it looks like we will be adding this feature to 4.3. It’s still in research, but working quite well. There are some minor details to iron out, but mostly under the hood and they will not affect the API or behavior in any way.



Click to see a short animation.

To trigger this special behavior, you set a flag: ItemIgnoresViewTransformations (, or something like that). Then you can add the item as a regular child of another item. Its position will then show where the item is “anchored” to its parent. And the item is rendered without any rotation or scaling imposed by the view. For example:

QLinearGradient gradient(-100, -100, 0, 100);
gradient.setColorAt(0, QColor(128, 255, 192));
gradient.setColorAt(1, QColor(63, 255, 63));

// Create a scene, add the main ellipse.
QGraphicsScene scene;
QGraphicsEllipseItem *ellipse
= scene.addEllipse(QRectF(-100, -100, 200, 200),
QPen(Qt::black, 2), gradient);

// Add two child lines that cross over the ellipse.
QGraphicsLineItem *line1 = new QGraphicsLineItem(-100, 0, 100, 0, ellipse);
line1->setPen(QPen(Qt::black, 3));
QGraphicsLineItem *line2 = new QGraphicsLineItem(0, -100, 0, 100, ellipse);
line2->setPen(QPen(Qt::black, 3));

// Add five _untransformable_ text items around the ellipse.
for (int i = 0; i setParentItem(ellipse);
text->setFlag(QGraphicsItem::ItemIgnoresViewTransformations);
text->setPos(100 * cos((6.28 * i) / 5.0), 100 * sin((6.28 * i) / 5.0));

QFont font;
font.setPixelSize(20);
text->setFont(font);
}

The items still follow regular items’ rendering constraints (e.g., the stacking order is the same as with normal items). You can also rotate or scale the item itself; that still works. It just doesn’t inherit any transformations.

PS: The Hell Wyrm has been defeated! It took me a good 1.5 hrs of continuous struggle to nail this mark. Can you believe that it’s got 6 million HP?? ;-)

Andreas
Qt
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, Labs, Graphics View, Graphics Items
 on Wednesday, December 20, 2006 @ 07:33

After Simon and Paul made some advancements to Qt’s font rendering system, it’s possible to render fonts in Qt running in Tty / console mode; that is, you don’t need an X11 connection. That means that the last piece of the puzzle is in place to use Qt for CGI scripting. :-) It sort of always worked, but now also for text. Guess what this is:

It’s a screenshot of output generated from a Qt CGI script based on Graphics View. Here’s how to do it:

#include <QtGui>

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

Create your QApp with QApplication::Tty (former ‘false’) to run in console mode.

    QGraphicsScene scene;
    scene.addEllipse(QRectF(0, 0, 100, 100), QPen(Qt::black, 2), QBrush(QColor(64, 78, 128, 192)));

Create a scene, just anything. Text, shapes, or your existing scene. Try adding text and see it work.

    QImage image(scene.itemsBoundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied);
    image.setColorTable(QVector() << Qt::transparent);
    image.fill(0);

Prepare a suitably-sized transparent image.

    QPainter painter(&image);
    painter.setRenderHint(QPainter::Antialiasing);
    scene.render(&painter);
    painter.end();

Render the scene onto the image.

    QBuffer buffer;
    buffer.open(QIODevice::WriteOnly);
    QImageWriter writer(&buffer, "png");
    writer.write(image);

Use QImageWriter to stuff the image into a QBuffer so that we can get its size for…

    printf("Content-Type: image/png; name="image.png"rn");
    printf("Content-Length: %drn", int(buffer.size()));
    printf("rn");

…the CGI header. Finally,

    QFile file;
    file.open(stdout, QIODevice::WriteOnly);
    file.write(buffer.data(), buffer.data().size());
    return 0;
}

Dump the image to stdout, compile, and copy your new script to your public_html/cgi-bin directory. ;-)

Disclaimer: This happens to work, tested in 4.3 snapshots only, may work with 4.2, and the snapshots may change at any time. It’s not officially supported.

Andreas
Qt
Qtopia
KDE
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, Qtopia, KDE, Labs, Graphics View, Graphics Items
 on Monday, December 18, 2006 @ 14:24

Some people have asked if they can have more explicit control over how QGraphicsScene’s index works.

If you’ve got concrete suggestions, please post comments to this post, or send us an email, or post a suggestion through our public feedback form.

Some suggestions we’ve got so far are: setting the depth of the tree, forcing all items to be indexed, blocking item indexing, and so on.

Please, let us know so that we can improve this popular framework. :-)

Andreas
Qt
KDE
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, KDE, Labs, Graphics View, Graphics Items
 on Friday, December 15, 2006 @ 17:35

I hacked together some code today for interacting with a QGraphicsItem while its transformation changes.

The problem? QGraphicsItem’s events are always delivered in local coordinates, and operations in response to such events that change item’s transformation (scale it, or move it, or shear it) will leave its local coordinates intact, but might change its relative coordinates (position on scene compared to position in item, etc). For example, if you click on an item to “grab” and rotate or scale it, the next mouse move event you get will typically be… at the exact same position as the first one! Well that’s because you rotated the item in response to the first mouse move, visually and effectively keeping the button-down position for the item under the cursor (relative to the item) intact after the rotation.

So how can you find the correct delta movement when the local mouse position keeps… staying the same?

Here’s how to do it. This screenshot below shows an example of rotation and zooming for an item in response to the item’s own local mouse events. You click on a corner of the item and drag the mouse to both zoom and rotate it.

The neat trick is to use the button-down scene position that comes with the event. That is, Graphics View stores the original mouse position in scene coordinates for when each button was pressed. You can at any time map that point back to the item’s local coordinate system. That gives you the original button-down position in the item’s new local coordinates, after the transformation. Apply some trigonometry, and there you go! :-)

void InteractiveItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (!grabbed)
        return QGraphicsItem::mouseMoveEvent(event);

    // Map the original button-down position back to local coordinates.
    QPointF buttonDownPos = mapFromScene(event->buttonDownScenePos(Qt::LeftButton));

    // Determine the "old" and "new" angles to the item's (1, 0) vector.
    qreal oldAngle = (180 * angleForPos(buttonDownPos)) / pi;
    qreal newAngle = (180 * angleForPos(event->pos())) / pi;

    // Determine the item's new rotation and scaling
    newRotation = rotation + oldAngle - newAngle;
    newScale = scale * distanceToPoint(event->pos()) / distanceToPoint(buttonDownPos);

    // Apply the new transformation
    setMatrix(QMatrix().rotate(newRotation).scale(newScale, newScale));
}

The example stores the item’s last known local scale and rotation, but it might as well have calculated that when the mouse was pressed. Oh, and you can get all the helper functions in the source code below.

The example also shows how to do animated fade effects, using QTimeLine. Good for the organic interfaces that Aaron’s talking about?

You can download the sources here.

Andreas
Qt
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, Labs, Graphics View, Graphics Items
 on Thursday, November 30, 2006 @ 14:01

Have you seen flash-interfaces with controls that grow and twist when you move your mouse over them? Here’s how to do that in Graphics View. I was so surprised at how easy it was that I thought I’d share it. Here’s a screenshot, just so you know what I’m talking about:


I’ll do this in the good old all-in-one-file style that we all love to hate. So, main.cpp:

#include 

class ImageZoomItem : public QObject, public QGraphicsPixmapItem
{
    Q_OBJECT
public:
    ImageZoomItem(const QPixmap &pixmap, QGraphicsScene *scene = 0);
protected:
    void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
    void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
private slots:
    void setFrame(int frame);
private:
    QTimeLine timeLine;
};

An image item that cross-inherits from QObject and QGraphicsPixmapItem? Isn’t that considered inherently evil by the API purists we are? In fact, no. We designed QGraphicsItem specificly so you could do this. So you can easily have signals and slots and all that fun stuff, in addition to the events. Graphics items aren’t QObjects by default, because QObject requires memory. Event support, however, is free. So now you know. If you want slots, bring in QObject. So let’s move on.

ImageZoomItem::ImageZoomItem(const QPixmap &pixmap, QGraphicsScene *scene)
    : QGraphicsPixmapItem(pixmap, 0, scene)
{
    setAcceptsHoverEvents(true);

    timeLine.setDuration(100);
    timeLine.setFrameRange(0, 100);
    connect(&timeLine, SIGNAL(frameChanged(int)), this, SLOT(setFrame(int)));
}

The image zoom item starts by enabling hover events. You need those to track the mouse entering and leaving your item. Then, we set up a QTimeLine (beautiful class, that is ;-)), with a 100 millisecond duration, starting at frame 0 and ending at 100. We’ll use the timeline to control our animation as the item zooms in and out. Finally, we connect the timeline to a slot in this class.

void ImageZoomItem::hoverEnterEvent(QGraphicsSceneHoverEvent *)
{
    timeLine.setDirection(QTimeLine::Forward);
    if (timeLine.state() == QTimeLine::NotRunning)
        timeLine.start();
}

The hoverEnterEvent() implementation is called when the mouse enters the item’s area. For semi-transparent images like the one in the screenshot above, this means when the mouse enters the opaque part of the bulb itself. So we start by setting the timeline direction, then start the timeline if necessary. With QTimeLine::Forward, this will make QTimeLine call the slot below 25 times per second over a period of 100 milliseconds, increasing the frame step from 0 to 100 with even steps.

The leave event is almost the same:

void ImageZoomItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
{
    timeLine.setDirection(QTimeLine::Backward);
    if (timeLine.state() == QTimeLine::NotRunning)
        timeLine.start();
}

Just set the timeline direction to QTimeLine::Backward when the mouse leaves the item. The cool thing is that this seamlessly supports moving the mouse over a bunch of items like crazy. Once the mouse moves in, the item will start growing, and as soon as you leave the item it’ll switch back to shrinking again. OK now for the slot:

void ImageZoomItem::setFrame(int frame)
{
    QPointF center = boundingRect().center();
    resetMatrix();

    translate(center.x(), center.y());
    scale(1 + frame / 150.0, 1 + frame / 150.0);
    rotate(frame / 8.0);
    translate(-center.x(), -center.y());
}

Reset the matrix, translate the item by half its height and width to ensure the items doesn’t visually move while zooming, scale it a bit, rotate a bit, and translate back again. And make note that the scale and rotate operations are not accumulative, but rather a function of the frame number. That means we can play the animation forwards and backwards or skip around randomly.

That leaves us with the main function itself:

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

    QPixmap pix("light.png");
    QGraphicsScene scene;

    for (int i = 0; i setPos(qrand() % 1000, qrand() % 1000);
        scene.addItem(item);
    }

    QGraphicsView view(&scene);
    view.show();

    return app.exec();
}

#include "main.moc"

Scatter a hundred bulb items around the scene. Create and show a view for the scene. Done.

Download the sources here.