0
votes

I have subclassed a QGraphicsView, QGraphicsScene, and QGraphicsPolygonItem. The Item is just a small square that can be different colors. The scene lays them out in a 90 rows by 360 columns grid.

header file

class MyView : public QGraphicsView {
Q_OBJECT
public:
    MyView(QGraphicsScene * scene,QWidget *parent = NULL);

public slots:
    void zoomIn(){ scale(1.2,1.2);};
    void zoomOut(){ scale(1/1.2,1/1.2);};
    void wheelEvent(QWheelEvent * event);
};

cpp file

MyView::MyView(QGraphicsScene * scene,QWidget *parent) : QGraphicsView(scene,parent){
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
}
void MyView::wheelEvent(QWheelEvent * event){
if(event->modifiers() & Qt::ControlModifier){
     if(event->delta() > 0){
        zoomIn();
     } else {
        zoomOut();
     }
}else{
    QGraphicsView::wheelEvent(event);
}
}

header file

#define MAX_ROW 90
#define MAX_COL 360
class MyScene : public QGraphicsScene
{
public:
    MyScene(QObject * parent =0);

};

cpp file

MyScene::MyScene(QObject *parent) : QGraphicsScene(parent){
   setSceneRect(0,0,MAX_COL*10,MAX_ROW*10);
   QGraphicsScene::setBackgroundBrush (QColor("lightblue"));
   MyCell * cell;
   for(int x=1;x<=MAX_COL;++x){
        for(int y=1;y<=MAX_ROW;++y){
            cell= new MyCell;
            cell->setPos(x*7,y*7);
            addItem(cell);
            cell->update();
        }
    }
}

header file

class MyCell :public QGraphicsItem {
public:
    MyCell(QGraphicsItem * parent =0,int state =0);

    QRectF boundingRect() const;
    void drawImage();

    void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget =0);
    QColor color();
    int state(){return _state;}
    void setState(int state);
    enum{NO,HARD,SOFT};
private:
    int _state; 
    QImage _image;
};

cpp file

MyCell::MyCell (QGraphicsItem* parent, int state) : QGraphicsItem (parent) {
    _state=state;
}


void MyCell::drawImage() {
     QPainter paint;
     QImage image = QImage(boundingRect().size().toSize(),QImage::Format_ARGB32_Premultiplied);
     paint.begin(&image);
     paint.setRenderHint(QPainter::Antialiasing);
     paint.fillRect(boundingRect(),color());
     paint.end();
     _image = image.copy();
}
void MyCell::setState(int state){
    if(state != HARD && state != SOFT){
        _state=NO;
    }else{
        _state=state;
    }
        drawImage();
}
QColor MyCell::color(){
    if(_state==HARD) return QColor(0xFFFFFFFF);
    else if(_state==SOFT) return QColor(0xFF7F7F7F);
    else return QColor(0xFF000000);

}
void MyCell::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget){
    painter->save();
        painter->drawImage(boundingRect(),_image,boundingRect());
    painter->restore();
}

QRectF MyCell::boundingRect() const {
    return QRectF(0,0,6,6);

}

code from main window widget that creates starts it all

    scene = new MyScene(this);
    view = new MyView(scene,this);
    setCentralWidget(view);

When I scroll with the view some rows and columns aren't fully repainted. The performance also seems slow. If I remove this line from MyView constructor:

setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

The performance increases, but now even more rows are forgotten and when I zoom the rounded rectangle shapes don't look quite right. Eventually I want the user to be able to click on the graphics items and change their color. I also want them to be able to rubber band click and select multiple squares and change the color that way as well.

But since there is a performance issue already, I was thinking of just using a single graphic item and store a 2D array of ints, and inside the paint function paint a single image based on the values of the array. Will this increase performance? How would clicking,selecting multiple squares/rubber banding work in that implementation?

update: I've updated the code in the MyCell class. The graphics rendering is better now, but it is still slow when zooming and scrolling. Is there no way to speed it up with the amount of items I have? 360 x 90 = 32,400 items. If not I may just have to make 360 items and instead of drawing a single square, I draw it like a bar graph. But I would have to keep track of the colors of all 90 subsections of each of the 360 vertical bars and paint them accordingly. It would also make cell selection much more difficult.

1
How many items do you have? Some performance hints: 1) Do not create a new QPainterPath in shape(), also called in every paint(). Either cache the path, or in this case, just call painter->drawRoundedRect() directly 2) Do not reimplement shape() unless you really care about the exact shape (collisions). Otherwise just let boundingRect() return the rect without caring about the corners 3) QColor("red") requires parsing and thus is more expensive than QColor(255, 0, 0) (or use static constants).Frank Osterfeld
32,400 items - also, I've updated the codeRidesTheShortBus

1 Answers

0
votes

The problem is probably caused by incorrect subclassing of QGraphicsPolygonItem. Default implementation of QGraphicsPolygonItem::boundingRect returns bounding rect of the polygon. But you don't set any polygon and don't reimplement boundingRect method, so MyCell::boundingRect is empty. Drawing outside item's bounding rect leads to incorrect behavior of the graphics view.

I don't see a point in subclassing QGraphicsPolygonItem in your code. You might as well subclass QGraphicsItem because you don't use any of QGraphicsPolygonItem's features. Maybe QGraphicsPathItem will be useful for you with no subclassing. If you still want to reimplement paint function, make sure that you reimplement boundingRect virtual function correctly.