Tuesday, August 23, 2011

Draw Grid on QGraphicsScene

Derived QGraphisScene class enables child class to reimplement drawBackground() function of the parent class. By reimplementing drawBackground() function, child class draws the background of the scene using painter.
Sample project started as a QT Gui Application and contains main.cpp, CustomQGraphicsScene.h, CustomQGraphicsScene.cpp, Dialog.h and Dialog.cpp files.
Project Directory Structure in Qt Creator IDE :
Qt project cpp and header files for draw grid on qgraphicsscene

DrawGridOnQGraphicsScene.pro is the project configuration file and contains:
QT       += core gui
TARGET = DrawGridOnQGraphicsScene
TEMPLATE = app
SOURCES += main.cpp \
           Dialog.cpp \
           CustomQGraphicsScene.cpp
HEADERS  += Dialog.h \
            CustomQGraphicsScene.h

main.cpp file contains the custom QDialog instance and shows it.
#include <QApplication>
#include "Dialog.h"

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   Dialog w;
   w.show();
   return a.exec();
}


Dialog.h is derived from QDialog class which contains QGraphicsView in it.
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>

class CustomQGraphicsScene;
class QGraphicsView;
class QVBoxLayout;

class Dialog : public QDialog
{
   Q_OBJECT
public:
   Dialog(QWidget *parent = 0);
   ~Dialog();
private:
   CustomQGraphicsScene* scene;
   QGraphicsView* view;
   QVBoxLayout* layout;
};

#endif // DIALOG_H

Dialog.cpp contains implementation details for derived QDialog class.
#include "Dialog.h"
#include "CustomQGraphicsScene.h"
#include <QVBoxLayout>
#include <QGraphicsView>

Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
   scene = new CustomQGraphicsScene(this);
   view = new QGraphicsView(scene,this);
   layout = new QVBoxLayout(this);
   layout->addWidget(view);
   setLayout(layout);
   resize(270,200);
}

Dialog::~Dialog()
{
   delete scene;
   delete view;
}


Custom QDialog creates an instance of derived QGraphicsScene and displays in a QGraphicsView.
CustomQGraphicsScene.h is derived from QGraphicsScene in which drawBackground() function is reimplemented to draw grid on it.
#ifndef CUSTOMQGRAPHICSSCENE_H
#define CUSTOMQGRAPHICSSCENE_H
#include <QGraphicsScene>

class CustomQGraphicsScene : public QGraphicsScene
{
public:
   CustomQGraphicsScene(QObject *parent);

protected:
   void drawBackground(QPainter * painter, const QRectF & rect );
};

#endif // CUSTOMQGRAPHICSSCENE_H

CustomQGraphicsScene.cpp contains implementation details for derived QGraphicsScene class.
#include "CustomQGraphicsScene.h"
#include <QPainter>

static const int GRID_STEP = 30;

inline qreal round(qreal val, int step) {
   int tmp = int(val) + step /2;
   tmp -= tmp % step;
   return qreal(tmp);
}

CustomQGraphicsScene::CustomQGraphicsScene(QObject *parent ) : QGraphicsScene(parent)
{}

void CustomQGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
{
   int step = GRID_STEP;
   painter->setPen(QPen(QColor(200, 200, 255, 125)));
   // draw horizontal grid
   qreal start = round(rect.top(), step);
   if (start > rect.top()) {
      start -= step;
   }
   for (qreal y = start - step; y < rect.bottom(); ) {
      y += step;
      painter->drawLine(rect.left(), y, rect.right(), y);
   }
   // now draw vertical grid
   start = round(rect.left(), step);
   if (start > rect.left()) {
      start -= step;
   }
   for (qreal x = start - step; x < rect.right(); ) {
      x += step;
      painter->drawLine(x, rect.top(), x, rect.bottom());
   }
}


Drawing grid process is divided into two substeps such as drawing vertical and drawing horizontal lines. Derived QGraphicsScene instance in which drawBackground() function is reimplemented looks like following screenShot :

qt result qgraphicsscene with a grid

2 comments:

  1. Hello, I know this thread is so old, but I think I can contribute. Recently I had to do something similar and I got the same problem, eficiency, I develop a simple solution with a simple technique that uses a bitmap to draw all, is so fast and efficient, the explanation is in my website Creación de grilla de forma eficiente en QGraphicsView/QGraphicsScene but is spanish, if you think that you can exploit this, then visit it.

    ReplyDelete
  2. Just a side-note: I would advise against calling `delete` on your scene and view. Both have an assigned parent (which you did when you called their constructors and passed `this` (that is the `Dialog`'s instance) as a parent). It's not that `delete` is something you shouldn't use at all but in your case it's a waste and basically eliminates the automatic memory management Qt offers with the whole parent-child stuff.

    ReplyDelete