PDA

View Full Version : QGraphicsView : scroll on drag



kghose
12th August 2008, 18:35
Hi,

I want to have my cake and eat it too :) :

I want that when I drag on my GraphicsView I get a rubberband select box AND when I reach the edge of the scrollview while dragging the view scrolls to reveal the rest of the scene, so I can continue to include it under the rubberband box.

How can I do that?

Thanks
-K

wysota
12th August 2008, 18:46
Isn't it the default behaviour when using QGraphicsView::RubberBandDrag?

kghose
12th August 2008, 19:06
Hmm, not for me. I'm on Mac OS X 10.5 using qt 4.4.0

-K

kghose
14th August 2008, 21:41
Bump. Any hint would be most appreciated :) -K

pherthyl
14th August 2008, 21:50
Bump. Any hint would be most appreciated :) -K

Well I had the same problem. And it turns out the graphicsview rubber band implementation is not particularly useful, so I made my own. The problem with the default is that it's relative to the view, so when you move the scene view, the selection rectangle moves with it, instead of anchoring to the scene where you clicked the mouse.

So here's what I did:
First of all, setDragMode(QGraphicsView::NoDrag); to turn off the default.

Then in the view:


void GraphWidget::onScrollTimeout() {
if(scrollDirection & Left) {
horizontalScrollBar()->setValue(horizontalScrollBar()->value()-scrollMagnitude);
}
if(scrollDirection & Right) {
horizontalScrollBar()->setValue(horizontalScrollBar()->value()+scrollMagnitude);
}
if(scrollDirection & Up) {
verticalScrollBar()->setValue(verticalScrollBar()->value()-scrollMagnitude);
}
if(scrollDirection & Down) {
verticalScrollBar()->setValue(verticalScrollBar()->value()+scrollMagnitude);
}
if(rubberBand->isVisible()) { // update the rubber band
QPoint mouseDownView = mapFromScene(mouseDownPos);
QPoint diff = (lastMouseViewPos-mouseDownView);
rubberBand->setGeometry(qMin(lastMouseViewPos.x(), mouseDownView.x()), qMin(lastMouseViewPos.y(), mouseDownView.y()), qAbs(diff.x()), qAbs(diff.y()));
}
}

void GraphWidget::mousePressEvent(QMouseEvent* evt) {
// when shift is pressed, we drag the scene
if(evt->modifiers().testFlag(Qt::ShiftModifier)) {
setDragMode(QGraphicsView::ScrollHandDrag);
qDebug() << "setting dragging mode";
QGraphicsView::mousePressEvent(evt);
}
else {
qDebug() << "press";
scrollTimer.start(30);
QGraphicsView::mousePressEvent(evt);
if(!scene()->itemAt(mapToScene(evt->pos())) && scene()->selectedItems().isEmpty()) {
mouseDownPos = mapToScene(evt->pos()).toPoint();
startRubberBand = true;
rubberBand->setGeometry(QRect(evt->pos(), evt->pos()));
}
}
}

void GraphWidget::mouseMoveEvent(QMouseEvent* evt) {
if(!evt->modifiers().testFlag(Qt::ShiftModifier) && evt->buttons().testFlag(Qt::LeftButton)) {
QPoint pos = evt->pos();
int distance = 0;
scrollDirection = scrollMagnitude = 0;
// determine the direction of automatic scrolling
if(pos.x() < SCROLL_DISTANCE) {
scrollDirection = Left;
distance = pos.x();
}
else if(width()-pos.x() < SCROLL_DISTANCE) {
scrollDirection = Right;
distance = width()-pos.x();
}
if(pos.y() < SCROLL_DISTANCE) {
scrollDirection |= Up;
distance = pos.y();
}
else if(height()-pos.y() < SCROLL_DISTANCE) {
scrollDirection |= Down;
distance = height()-pos.y();
}
if(scrollDirection) {
scrollMagnitude = qRound((SCROLL_DISTANCE-distance)/8);
}

// handle the rubberband
if(startRubberBand && (mapFromScene(mouseDownPos)-evt->pos()).manhattanLength() > 4) {
rubberBand->show();
startRubberBand = false;
}
if(rubberBand->isVisible()) {
QPoint mouseDownView = mapFromScene(mouseDownPos);
lastMouseViewPos = evt->pos();
QRect rubberRect(mouseDownView, lastMouseViewPos);
rubberRect = rubberRect.normalized();
rubberBand->setGeometry(rubberRect);
projectScene->setSelectionAreaFixed(QRectF(mapToScene(rubberRect .topLeft()), mapToScene(rubberRect.bottomRight())));
}

}
QGraphicsView::mouseMoveEvent(evt);
}

void GraphWidget::mouseReleaseEvent(QMouseEvent* evt) {
rubberBand->hide();
startRubberBand = false;
scrollDirection = scrollMagnitude = 0;
scrollTimer.stop();
QGraphicsView::mouseReleaseEvent(evt);
setDragMode(QGraphicsView::NoDrag);
}


So when we press the mouse, we store the mouse down pos and unhide the QRubberBand. Then on mouse move we update the QRubberBand's geometry and select the underlying objects. (on Line 77, I call the function "setSelectionAreaFixed". You can use QGraphicsScene::setSelectionArea if you are using Qt >= 4.4. Versions before 4.4 had a bug in that function that made it not work properly)

To get the automatic scrolling of the view, I just start a timer when the drag starts. On mouse movement, if the cursor is close to the edge of the view, I set a variable to indicate the direction of scrolling and the speed, then on the timeout I move the scene.

It's not exactly elegant, but it works. Took me a while to sort everything out (the Qt bug didn't help)