Problem with a click-and-drag event, and a question on good practice
Hi everyone, this is my first post here, so I apologize if this is the wrong category to be posting in.
I'm trying to design a node graph editor, similar to those seen in Blender, the Unreal Engine, and Unity. As part of this, I am trying to make a widget extended off of QGraphicsView that, when displayed, a large grid is in the background, and this view can be scrolled by holding down a mouse button and dragging it. However, the code I have right now doesn't work quite right, as the grid doesn't update it's position with the drag offsets.
NodeGraphWidget.h:
Code:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QGraphicsView>
#include <QKeyEvent>
#include <QPoint>
{
Q_OBJECT
public:
NodeGraphWidget
(QWidget *parent
= 0);
public slots:
//void zoomIn();
//void zoomOut();
protected:
float mScale;
int mOffsetX;
int mOffsetY;
bool mMiddleMouseDown;
QPoint mOldMousePos, mNewMousePos;
private:
};
#endif // MAINWINDOW_H
NodeGraphWidget.cpp:
(Note that I purposefully set the offsets to a constant, just to test if the graph would move)
Code:
#include "NodeGraphWidget.h"
#include <QBrush>
#include <QDebug>
#include <QEvent>
#include <QGraphicsScene>
#include <QKeyEvent>
NodeGraphWidget
::NodeGraphWidget(QWidget *parent
){
scene->setSceneRect(0, 0, 400, 400);
setScene(scene);
setViewportUpdateMode(BoundingRectViewportUpdate);
setCacheMode(CacheBackground);
setTransformationAnchor(AnchorUnderMouse);
//this->ch
scale(qreal(1.0), qreal(1.0));
//setMinimumSize(400, 400);
setWindowTitle(tr("Test"));
mOffsetX = 0;
mOffsetY = 0;
mScale = 1.f;
}
void NodeGraphWidget
::keyPressEvent(QKeyEvent *event
) {
Q_UNUSED(event);
qDebug() << event->key();
switch(event->key())
{
default: setWindowTitle("FUCK");
}
}
void NodeGraphWidget
::drawBackground(QPainter *painter,
const QRectF &rect
) {
Q_UNUSED(rect);
qDebug() << "redraw bkg, " << mOffsetX << " " << mOffsetY;
QRectF sceneRect
= this
->sceneRect
();
painter->fillRect(sceneRect, Qt::SolidPattern);
painter
->setPen
(QColor(80,
80,
80));
const int gridSize = 150;
for(int y = -3; y < (int)(height()*mScale)%gridSize; y++)
{
for(int x = -3; x < (int)(width()*mScale)%gridSize; x++)
{
painter->drawRect((x*gridSize)-(mOffsetX), (y*gridSize)-(mOffsetY), gridSize, gridSize);
}
}
}
void NodeGraphWidget
::mousePressEvent(QMouseEvent *event
) {
switch(event->button())
{
case Qt::MiddleButton:
qDebug() << "DOWN";
mMiddleMouseDown = true;
mOldMousePos = event->localPos().toPoint();
break;
}
}
void NodeGraphWidget
::mouseMoveEvent(QMouseEvent *event
) {
if(mMiddleMouseDown == true)
{
qDebug() << "DRAG";
mNewMousePos = event->localPos().toPoint();
mOffsetX = 75;// mOldMousePos.x() - mNewMousePos.x();
mOffsetY = 75;// mOldMousePos.y() - mNewMousePos.y();
invalidateScene();
}
}
void NodeGraphWidget
::mouseReleaseEvent(QMouseEvent *event
) {
switch(event->button())
{
case Qt::MiddleButton:
qDebug() << "UP";
mMiddleMouseDown = false;
//mOldMousePos = QPoint(0, 0);
//mNewMousePos = QPoint(0, 0);
mOffsetX = 0;
mOffsetY = 0;
break;
}
}
At my qDebug() points, the offset variables are indeed being set, it's just when the view is "redrawn" it isn't being offset. Can anybody offer any help on this? I'm totally stumped.
Which brings me to my next question: is it good practice to have a widget that isn't specified as a scrollable one to be able to do so? I mostly did it so the scrollbars wouldn't show up (and yes, I know that they can be disabled on the scrollable widget classes). It just seemed like a good idea in hindsight. Now that I'm here, however, is this at all good practice? Or should I switch to a scrollable widget to extend off of? Any suggestions would be greatly appreciated, for either of my issues.
Thanks.
Re: Problem with a click-and-drag event, and a question on good practice
Basically in drawBackground() all drawing is done in scene coordinates and not in view coordinates and it seems to me you are trying to do it the other way.
Code:
#include <QtWidgets>
public:
setCacheMode(CacheBackground);
}
protected:
const int gridSize = 80;
painter->save();
painter->fillRect(rect, Qt::white);
pen.setColor(Qt::gray);
pen.setWidth(0);
painter->setPen(pen);
int i = rect.top() / gridSize;
while(i <= rect.bottom()) {
painter->drawLine((int)rect.left(), i, (int)rect.right(), i);
i+=gridSize;
}
i = rect.left() / gridSize;
while(i<= rect.right()) {
painter->drawLine(i, (int)rect.top(), i, (int)rect.bottom());
i+=gridSize;
}
painter->restore();
}
};
int main(int argc, char **argv) {
GraphicsView view;
view.show();
return app.exec();
}
BTW. The code posted here has some funny behavior if you scroll the view too fast but you should get the general picture of what I mean.
Re: Problem with a click-and-drag event, and a question on good practice
Thanks wysota, after looking at your example, I picked mine apart and tweaked it some, and now it's working.
BTW, I just threw in an invalidateScene() call at the start of the drawBackground() and that seemed to fix the weird display problem.
Re: Problem with a click-and-drag event, and a question on good practice
Quote:
Originally Posted by
Aunvre
BTW, I just threw in an invalidateScene() call at the start of the drawBackground() and that seemed to fix the weird display problem.
Bad idea. This way you are redrawing the whole scene (including items). "My" artifacts come most probably from flooring before assignment to "i" variable. They can easily be overcome by assigning 0 instead (with a slight performance penalty).