Custom QGraphicsItems, placed into a QVector<QGraphicsItems*>, aren't having their paint() methods called.
-
I'm trying to build a custom QGraphicsScene, named BoardScene, that works in tandem with a custom QGraphicsItem, named BoardSquare. In the constructor of my BoardScene, I call a method that populates a QVector<BoardSquare*> with 64 BoardSquare objects. I want them to form a checkerboard pattern on the screen.
I have two seperate implementations. One works, one doesn't. I cannot figure out what's going wrong to prevent the desired implementation from working.
Please note. A lot of what i've got written is just trying to test things out and get things working, as this is my first time interacting with Qt. So if any of the code is not specifically attributing to the problem at hand (which is the QGraphicsView never painting the BoardSquares) please ignore it haha.
## THIS DOESN'T WORK ##
boardscene.h
#ifndef BOARDSCENE_H #define BOARDSCENE_H #include <QGraphicsScene> #include "boardsquare.h" class BoardScene : public QGraphicsScene { public: BoardScene(int selector, QGraphicsScene *parent = NULL); ~BoardScene(); QVector <BoardSquare*> generateBoard(); void generateBoard(QVector<BoardSquare*> &board); void addSquares(QVector<BoardSquare*> &board); private: QVector <BoardSquare*> m_board; const int m_height = 850; const int m_width = 850; int m_selector; //used to select coloring options }; #endif // BOARDSCENE_H
boardscene.cpp
#include "boardscene.h" BoardScene::BoardScene(int selector, QGraphicsScene *parent) : QGraphicsScene(parent) { this->m_selector = selector; this->setBackgroundBrush(Qt::gray); this->setSceneRect(m_height, m_height, m_width, m_width); generateBoard(m_board); } BoardScene::~BoardScene() {} void BoardScene::generateBoard(QVector<BoardSquare*> &board) { int x = 25; int y = 25; int first, next, temp = 0; if(m_selector == 1) { first = 1; next = 2; } else { first = 3; next = 4; } for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { if (j % 2 == 0) { BoardSquare *square = new BoardSquare(x, y, first); board.push_back(square); x += 100; } else { BoardSquare *square = new BoardSquare(x, y, next); board.push_back(square); x += 100; } } x = 25; y += 100; temp = next; next = first; first = temp; } addSquares(board); } void BoardScene::addSquares(QVector<BoardSquare*> &board) { for(auto square : board) this->addItem(square); } QVector <BoardSquare*> BoardScene::getBoard() { return m_board; }
boardsquare.h
#ifndef BOARDSQUARE_H #define BOARDSQUARE_H #include <QGraphicsItem> class BoardSquare : public QGraphicsItem { public: BoardSquare(int x_off, int y_off, int selection, QGraphicsItem* parent = NULL); ~BoardSquare(); protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QRectF boundingRect() const; private: int m_selection; // 1 = dark green, 2 = tan, 3 = black, 4 = white int x_offset; int y_offset; }; #endif // BOARDSQUARE_H
boardsquare.cpp
#include "boardsquare.h" BoardSquare::BoardSquare(int x_off, int y_off, int selection, QGraphicsItem *parent) : QGraphicsItem(parent) { this->m_selection = selection; this->x_offset = x_off; this->y_offset = y_off; } BoardSquare::~BoardSquare() { } void BoardSquare::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { switch(m_selection) { case 1: //dark green painter->setBrush(QColor(118, 150, 86, 255)); painter->setPen(QColor(118, 150, 86, 255)); painter->drawRect(x_offset, y_offset, 100, 100); break; case 2: //tan painter->setBrush(QColor(238, 238, 210, 255)); painter->setPen(QColor(238, 238, 210, 255)); painter->drawRect(x_offset, y_offset, 100, 100); break; case 3: //black painter->setBrush(QColor(0, 0, 0, 255)); painter->setPen(QColor(0, 0, 0, 255)); painter->drawRect(x_offset, y_offset, 100, 100); break; case 4: //white painter->setBrush(QColor(255, 255, 255, 255)); painter->setPen(QColor(255, 255, 255, 255)); painter->drawRect(x_offset, y_offset, 100, 100); break; default: break; } } QRectF BoardSquare::boundingRect() const { return QRectF(0, 0, 100, 100); }
main.cpp
#include <QApplication> #include <QGraphicsView> #include "boardscene.h" #include "boardsquare.h" int main(int argc, char **argv) { QApplication app (argc, argv); QGraphicsView view; BoardScene *scene = new BoardScene(1); view.setScene(scene); view.show(); return app.exec(); }
## THIS WORKS ##
main.cpp
#include <QApplication> #include <QGraphicsScene> #include <QGraphicsView> #include <QGraphicsItem> class Rectangle : public QGraphicsItem { public: Rectangle(int a, int b) {x = a; y = b;} ~Rectangle() {} void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->setBrush(QColor(118, 150, 86, 255)); painter->setPen(QColor(118, 150, 86, 255)); painter->drawRect(x, x, y, y); } QRectF boundingRect() const { return QRectF(0, 0, 100, 100);} private: int x; int y; }; class GraphicsScene : public QGraphicsScene { public: GraphicsScene() { setBackgroundBrush(Qt::black); setSceneRect(0, 0, 150, 150); genVec(m_vector); addToScene(m_vector); } ~GraphicsScene() {} void genVec(QVector<Rectangle*> &vector) { int x = 10; int y = 10; for (int i = 0; i < 5; i++) { Rectangle* rect = new Rectangle(x, y); vector.push_back(rect); x += 10; } addToScene(vector); void addToScene(QVector<Rectangle*> vector) { for (auto thing : vector) { this->addItem(thing); } } private: QVector <Rectangle*> m_vector; }; int main (int argc, char **argv) { QApplication app (argc, argv); QGraphicsView view; GraphicsScene *scene = new GraphicsScene(); view.setScene(scene); view.show(); return app.exec(); }
I tried to make both of these do the same things, albeit the first one, with the separate header/source files, has a lot more going on, but it's mainly just in terms of generating the QGraphicsItems with different characteristics.
A QVector of pointers to custom QGraphicsItems is created and added to the scene in the same way, through methods. The QGraphicsView is set up the same way, the custom QGraphicsScene is generated in the same way (minus the int value used in the first). I just can't seem to find what i'm doing wrong.
I've spent probably around 5 hours now trying different things to get the BoardScene/BoardSquare implementation to work. I know I could just change the class structure around a little but I would prefer to learn what's actually causing the problem, as I can't figure it out.
I know, for a fact, that QGraphicsItem::paint() is never invoked for BoardSquare. It is invoked for the Rectangle, though. It happens after view.show() is called in main.cpp. And that's the story about how I learned to use QDebug() console output.
I'm doing something wrong but my eyes are so tired of looking at this that I thought I would come on here for some help.
Thank you in advance for your help!
Edit I know that it looks like I have my methods with the QGraphicsScene::addItem() call in different locations in the code that I posted, but I have tried multiple variations of adding the items in the BoardScene/Square version that didn't work, including the format in the second, working version I posted.
I should also add that BoardScene::m_board is populating and maintaining it's contents. I have code I wrote to help try and debug this that returns the private data members of each BoardSquare in m_board. The problem lies entirely within the paint() method. I have tested this in other ways and I know that the current set up of the overloaded paint() method that uses a switch case to generate different colors actually works.
But I'm missing something about how i'm setting this all up.
-
The code as posted does not compile. After fixing the trivial issues then the problem comes down to incorrect specification of the scene rectangle:
BoardScene::BoardScene(int selector, QGraphicsScene *parent) : QGraphicsScene(parent) { this->m_selector = selector; this->setBackgroundBrush(Qt::gray); this->setSceneRect(0, 0, m_width, m_width); // ^^^ was this->setSceneRect(m_width, m_width, m_width, m_width); generateBoard(m_board); }
Screenshot_20220331_173104.png
I find the passing of private member variables by reference to member functions a bit odd because the member functions, e.g. void generateBoard(), already have direct access to the private member variables.
It's very likely that these member functions can only be meaningfully be called by code with access to the private member variable, e.g. generateBoard(m_board), yet they are declared public not private. This only makes sense if you intend to call this from outside the class to populate a vector outside the class; I do not think this is the case.
-
Wow. Thank you so much!
That's really it? Everything else is working?
Hopefully I learn my lesson because I spent so much time on trying to fix this haha.
As far as passing those variables by reference, at that point I was just trying everything I could think of. I'll fix them though, originally I was not doing that.