Move-able, Re-size-able Graphic Box
Qt Graphics View Lesson 1 |
Example usage of a custom QGraphicsItem in a QGraphicsScene with examples of mouse hover events.
Download the complete project zip file
![]() |
This is my first foray into the Qt Graphics View Framework. To get my feet wet, my goal was to create a simpe box, in the shape of a UML State/Class box.
When the user hovers the mouse over the box, it should turn red, and the user should be able to drag the box around the scene.
I created the project with QtCreator. I started a new C++ UI project. But I did not use creator to add widgets to the main window,
but rather created instances of the widgets myself. QtCreator started a mainwindow.h and mainwindow.cpp files (when it created the UI project) – from there I added my widgets.
The class StateBox is the widget that represents the box in the scene. Its derived from the QGraphicsItem. MainWindow.cpp creates the scene and inserts a StateBox object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
/** * \class StateBox * This is short example/demonstration of creating a custom QGraphicsItem. * Example usage of a custom QGraphicsItem in a QGraphicsScene with examples of mouse hover events. * * My goal was to create a simpe box, in the shape of a UML State/Class box, with a title * area seprated from a main arear below by line. * * This sample class inherits from QGraphicsItem, and must reimplement boundingRect() and paint() * from the base class. * * To learn about handling mouse events in a QGraphicsScene, I wanted my box to turn red when the mouse moves inside, and black * when the mouse moves out. I also allow the user to drag the box around the scene. */ class StateBox : public QGraphicsItem { public: StateBox(); QGraphicsTextItem _text; ///< sample text to go in the title area. void setGridSpace(int space); protected: virtual QRectF boundingRect() const; ///< must be re-implemented in this class to provide the diminsions of the box to the QGraphicsView virtual void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); ///< must be re-implemented here to pain the box on the paint-event virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event ); ///< must be re-implemented to handle mouse hover enter events virtual void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ); ///< must be re-implemented to handle mouse hover leave events virtual void mouseMoveEvent ( QGraphicsSceneMouseEvent * event );///< re-implemented to capture events virtual void mouseMoveEvent(QGraphicsSceneDragDropEvent *event);///< re-implemented to capture events virtual void mousePressEvent (QGraphicsSceneMouseEvent * event );///< re-implemented to capture events virtual void mousePressEvent(QGraphicsSceneDragDropEvent *event);///< re-implemented to capture events virtual void mouseReleaseEvent (QGraphicsSceneMouseEvent * event );///< re-implemented to capture events QColor _outterborderColor; ///< the hover event handlers will toggle this between red and black QPen _outterborderPen; ///< the pen is used to paint the red/black border QPointF _location; QPointF _dragStart; int _gridSpace; qreal _width; qreal _height; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
StateBox::StateBox(): _text(), _outterborderColor(Qt::black), _outterborderPen(), _location(0,0), _dragStart(0,0), _gridSpace(10), _width(150), _height(100) { _outterborderPen.setWidth(2); _outterborderPen.setColor(_outterborderColor); _text.setPlainText("text goes here"); _text.setParentItem(this); this->setAcceptHoverEvents(true); } /* re-implement the mouse event handlers from the base QGraphicsItem class there are two versions provided in the base class, two that take QGraphicsSceneDragDropEvent - which I will ignore - and two that take QGraphicsSceneMouseEvent - which I will use */ void StateBox::mouseMoveEvent(QGraphicsSceneDragDropEvent *event) { event->setAccepted(false); } void StateBox::mousePressEvent(QGraphicsSceneDragDropEvent *event) { event->setAccepted(false); } void StateBox::mouseReleaseEvent ( QGraphicsSceneMouseEvent * event ) { event->setAccepted(true);// tell the base class we are handling this event // force my box to snap to grid, just truncate the pixel number and // snap to the next lowest grid value _location.setX( ( static_cast<int>(_location.x()) / _gridSpace) * _gridSpace ); _location.setY( ( static_cast<int>(_location.y()) / _gridSpace) * _gridSpace ); this->setPos(_location); } void StateBox::mousePressEvent ( QGraphicsSceneMouseEvent * event ) { // allow the user to drag the box, capture the starting position on mouse-down event->setAccepted(true); _dragStart = event->pos(); } void StateBox::mouseMoveEvent ( QGraphicsSceneMouseEvent * event ) { // user have moved the mouse, move the location of the box QPointF newPos = event->pos() ; _location += (newPos - _dragStart); this->setPos(_location); } void StateBox::hoverLeaveEvent ( QGraphicsSceneHoverEvent * ) { // return the box color to black when the mouse is no longer hovering _outterborderColor = Qt::black; this->update(0,0,_width,_height); } void StateBox::hoverEnterEvent ( QGraphicsSceneHoverEvent * ) { // draw the box in red if the mouse is hovering over it _outterborderColor = Qt::red; this->update(0,0,_width,_height); } // boundingRect must be re-implemented from the base class to provide the scene with // size info about this custom GraphicsItem QRectF StateBox::boundingRect() const { return QRectF(0,0,_width,_height); } void StateBox::paint (QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { // draw the outter box _outterborderPen.setColor( _outterborderColor ); _outterborderPen.setStyle(Qt::SolidLine); painter->setPen(_outterborderPen); QBrush background (QColor::fromRgb(255,255,255,255), Qt::SolidPattern); painter->setBackgroundMode(Qt::OpaqueMode); painter->setBrush( background); //draw upper left corner QPointF topLeft (0, 0); QPointF bottomRight ( _width, _height ); QRectF rect (topLeft, bottomRight); painter->drawRoundRect(rect,25,25); painter->drawLine(0,25,_width,25); } |
I used QtCreator to start a UI project – it creates the skeleton of the MainWindow program which is then the framework where I hang the GraphicsView, GraphicsScene, and scene items
sush as the StateBox and Grid.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow()), scene( new QGraphicsScene()), gview( new QGraphicsView ()), stateBox( new StateBox()), gridLines( NULL ) { QColor c (242,251,235); // create the background color QBrush brush (c, Qt::SolidPattern); scene->setBackgroundBrush(brush); /* QtCreator generates ui_mainwindow.h, which defines a new class Ui_MainWindow, then makes MainWindow inherit Ui_MainWindow. Ui_MainWindow defines the setupUi() method which creates instances of all QtCreator objects. */ ui->setupUi(this); /* I used QtCreator's design surface (right-click on main window area) to add a layout to hold the graphics view. This also allows the graphicsview to change size and expand/contract to fill the window area as the user changes the size of the main window frame. */ ui->horizontalLayout->addWidget( gview); /* I created a gridLines class to draw a grid to make it interresting and have the chance to see layering of objects in action. */ gridLines = new GridLines (this->width(), this->height()); scene->addItem(gridLines); /* scene holds the custom QGraphicsItem stateBox */ stateBox->setPos(100,100); // give my sample box a starting position (otherwise it would be at 0,0) scene->addItem( stateBox); /* setSizePolicy, when used with the horizontalLayout widget, allows the grapicsview to expand/contract as the user re-sizes the main window. */ gview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); /* add the scene to the QGraphicsView */ gview->setScene( scene); gview->show(); //let the show begin } /* re-implement the resizeEvent handler from the QMainWindow base class, so I can capture the main window resize events, and pass on the new window size to my grid drawing object. */ void MainWindow::resizeEvent(QResizeEvent * ) { QSize wsize = ui->centralWidget->frameSize(); if ( gridLines) gridLines->handleWindowSizeChanged(wsize.width(), wsize.height()); qDebug() << "cw frame height" << wsize.height() << " width: " << wsize.width(); } |
Trackback this post | Subscribe to the comments via RSS Feed