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