[Qt-interest] Advice on QGraphicsScene, View, ...
Christian Gagneraud
cgagneraud at techworks.ie
Mon Jul 12 12:45:18 CEST 2010
Hi all,
I'm currently working on a kind of diagram editor. For now I'm
focusing only on the basics: background, grid and zoom.
What I would like to do is to add a "mouse position marker" which will
snap on the grid, typically it should be made of one vertical line,
spreading all the height of the display area and one horizontal line
that spread all the width of the display area. But I don't want to
replace the mouse cursor, which will still be used to reflect the
current operating mode (selection, move, ...).
My first attempt was to create a specialized QGraphicsItem which seems
to work OK, but I'm not really sure I'm doing it the right way (event
problems, and line size problems).
The grid and the zoom is manage in the view, and the position marker
is managed in the scene (for now).
/////////////////////////////////////////////////////////
//// The marker
/////////////////////////////////////////////////////////
class MousePositionMarker : public QGraphicsItem
{
public:
explicit MousePositionMarker(QGraphicsScene *parent = 0);
void move(QPointF);
virtual QRectF boundingRect() const;
virtual void paint(QPainter *painter, const
QStyleOptionGraphicsItem *option, QWidget *widget = 0);
private:
QGraphicsLineItem *m_line_x;
QGraphicsLineItem *m_line_y;
};
MousePositionMarker::MousePositionMarker(QGraphicsScene *scene)
{
setPos(0, 0);
m_line_x = new QGraphicsLineItem(this);
m_line_x->setPos(0, 0);
m_line_y = new QGraphicsLineItem(this);
m_line_y->setPos(0, 0);
scene->addItem(this);
}
QRectF MousePositionMarker::boundingRect() const
{
return m_line_x->boundingRect().united(m_line_y->boundingRect());
}
void MousePositionMarker::paint(QPainter *painter, const
QStyleOptionGraphicsItem *option, QWidget *widget)
{
m_line_x->paint(painter, option, widget);
m_line_y->paint(painter, option, widget);
}
void MousePositionMarker::move(QPointF point)
{
m_line_x->setPen(QPen(QBrush(Qt::black), 1));
m_line_x->setPos(m_line_x->x(), point.y());
m_line_x->setLine(-100, 0, scene()->width()+100, 0);
m_line_y->setPen(QPen(QBrush(Qt::black), 1));
m_line_y->setPos(point.x(), m_line_y->y());
m_line_y->setLine(0, -100, 0, scene()->height()+100);
}
/////////////////////////////////////////////////////////
// The view:
/////////////////////////////////////////////////////////
View::View( QGraphicsScene * scene, QWidget * parent) :
QGraphicsView(scene, parent), m_drawGrid(true)
{
}
void View::setGrid(Grid *grid)
{
m_grid = grid;
}
QRectF View::displayedSceneRect() const {
QSize viewport_size = viewport()->size();
QTransform view_to_scene = viewportTransform().inverted();
QPointF scene_left_top = view_to_scene.map(QPointF(0.0, 0.0));
QPointF scene_right_bottom =
view_to_scene.map(QPointF(viewport_size.width(), viewport_size.height()));
return(QRectF(scene_left_top, scene_right_bottom));
}
void View::drawBackground(QPainter *p, const QRectF &r)
{
// White background
p->save();
p->setRenderHint(QPainter::Antialiasing, false);
p->setRenderHint(QPainter::SmoothPixmapTransform, false);
p->setPen(Qt::NoPen);
p->setBrush(Qt::white);
p->drawRect(r);
p->restore();
// Draw grid
if (m_drawGrid)
m_grid->draw(p, r, this);
}
void View::zoomFitAll()
{
fitInView(scene()->sceneRect(),
Qt::KeepAspectRatio);
emit zoomChanged(transform().m11());
}
void View::zoomFitSelection()
{
}
void View::zoomIn()
{
if (zoomFactor() < zoomFactorMax) {
scale(1.2, 1.2);
emit zoomChanged(transform().m11());
}
}
void View::zoomOut()
{
if (zoomFactor() > zoomFactorMin) {
scale(1/1.2, 1/1.2);
emit zoomChanged(transform().m11());
}
}
qreal View::zoomFactor() const
{
return transform().m11();
}
void View::wheelEvent(QWheelEvent* event)
{
if (event->modifiers() & Qt::ControlModifier) {
if (event->delta()>0)
zoomIn();
else
zoomOut();
event->accept();
}
else
QGraphicsView::wheelEvent(event);
}
void View::mouseMoveEvent(QMouseEvent* event)
{
QGraphicsView::mouseMoveEvent(event);
}
/////////////////////////////////////////////////////////
//// The scene
/////////////////////////////////////////////////////////
Scene::Scene(QObject *parent) :
QGraphicsScene(parent)
{
// A4-ish landscape, 0.1mm pitch
setSceneRect(0, 0, 3000, 2100);
// Borders
addRect(sceneRect(),QPen(Qt::gray,3));
//
m_mousePositionMarker = new MousePositionMarker(this);
}
bool Scene::event(QEvent * event )
{
//
// This doesn't work, the cast always fail
//
QGraphicsSceneHoverEvent *hev =
dynamic_cast<QGraphicsSceneHoverEvent *>(event);
if ( hev && m_mousePositionMarker)
m_mousePositionMarker->move(hev->scenePos());
}
return QGraphicsScene::event(event);
}
void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
//
// This works, but only when a button is pressed
//
if(m_mousePositionMarker)
m_mousePositionMarker->move(event->scenePos());
QGraphicsView::mouseMoveEvent(event);
}
I have several problems:
1 - The thickness of the marker lines is one pixel wide (QPen width is
set to 1 within MousePositionMarker), but this is not one screen
pixel, so when I zoom in and in (so that 1 pixel in the scene is more
than one pixel on the screen), the lines became more than one pixel
wide. I would like to have a 1 pixel line whatever the zoom level.
2 - The position marker moves only when the mouse move *and* a mouse
button is pressed. What I would like to do is make the marker follow
the mouse pointer, all the time, but I didn't find any MouseHover event.
3 - I didn't implement the snap to grid thing yet, but I have the
feeling that I should move the MousePositionMarker into the view, not
the scene.
Can anyone around enlighten me on these, point me to some examples or
documentation? Any idea to share?
Thanks,
Chris
--
Christian Gagneraud,
Electronics and software engineer
TechWorks Marine Ltd
4a, Park Lane
Dun Laoghaire, Co Dublin
Ireland
Tel: + 353 1 2365990
Fax: + 353 1 2365992
Web: www.techworks.ie
More information about the Qt-interest-old
mailing list