PDA

View Full Version : Subclassing QGraphicsPixmapItem ???



mdavidjohnson
1st November 2016, 23:20
I'm trying to establish an array of QGraphicsPixmapItems.



// Pre-Try as a general check:

QPixmap pixmapA = QPixmap( ":/images/imageA.png" );
QGraphicsPixmapItem pixmapItemA;
pixmapItemA.setPixmap( pixmapA );

// This works


But then:



// First Try

QVector< QGraphicsPixmapItem > itemVector( 100 );
itemVector[23].setPixmap( pixmapA );

// Fails: In QGraphicsPixmapItem, 'DISABLE COPY' is private


And:



// Second Try:

QVector< QGraphicsPixmapItem > itemVector( 100 );
QGraphicsPixmapItem pixmapItemA;
pixmapItemA.setPixmap( pixmapA );
itemVector[23] = pixmapItemA;

// Fails: In QGraphicsPixmapItem, 'operator=' is private


And, finally:



// Third Try:

QVector< QGraphicsItem > itemVector( 100 );
QGraphicsPixmapItem pixmapItemA;
pixmapItemA.setPixmap( pixmapA );
itemVector[23] = pixmapItemA;

// Fails - invalid abstract return type 'QGraphicsItem'


Is there something I'm missing?

Or, am I going to have to subclass QGraphicsPixmapItem in order to get this to work?

If the latter, could somebody please point me to a reasonably straightforward link on subclassing? I've never tried anything like that and have numerous trepidations.

BTW, I do note in qglobal.h :



/*
Some classes do not permit copies to be made of an object. These
classes contains a private copy constructor and assignment
operator to disable copying (the compiler gives an error message).
*/
#define Q_DISABLE_COPY(Class) \
Class(const Class &) Q_DECL_EQ_DELETE;\
Class &operator=(const Class &) Q_DECL_EQ_DELETE;


Why would copies not be permitted? If there's a valid reason, subclassing to get past it might break something, in which case Qt would seem not to be what I need to use ???

anda_skoa
2nd November 2016, 00:22
Vector of pointers to QGraphicsPixmapItem



QVector< QGraphicsPixmapItem*> itemVector( 100 );


Cheers,
_

mdavidjohnson
2nd November 2016, 05:08
Thanks -

That does indeed work with my short sample.

I'll try to extend it to the entire chess board.

d_stranz
2nd November 2016, 14:58
Why are you storing your QGraphicsPixmapItem instances in a QVector in the first place? You should be adding them to your QGraphicsScene, where they will be properly parented (and their lifetimes managed by the scene). If you need to keep a copy of the *pointer* for ease of use, keep that in the QVector as well as adding *the same pointer* to the scene, but remember that after the item has been destroyed by the scene it will no longer point to a valid instance.

mdavidjohnson
2nd November 2016, 18:16
To anda_skoa and d_stranz --

To anda_skoa:

Unfortunately, it doesn't seem to have worked when extended to the entire chess board.

Here's an abbreviated sample to show what's happening.

First, a successful test without the vector:



// ChessTest2
// MDJ 2016/11/02

// Qt 5.7 - Qt Creator 4.0.2 - Qt Widgets Application
// No changes to files other than this main.cpp - except:
// Added line "RESOURCES += application.qrc" to ChessTest2.pro.
// Added images folder with files:
// blackSquare.png
// whiteSquare.png
// Added application.qrc Resources file:
// <!DOCTYPE RCC><RCC version="1.0">
// <qresource>
// <file>images/blackSquare.png</file>
// <file>images/whiteSquare.png</file>
// </qresource>
// </RCC>

// This test compiles:
// and runs as expected, displaying a "black" square
// and a "white" square in the lower left corner
// of the view.

#include "mainwindow.h"
#include <QApplication>
#include <QPixmap>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QGraphicsView>

int main( int argc, char **argv )
{
QApplication app(argc, argv);

// Load the Resource Graphics
QPixmap pixmapBlackSquare = QPixmap( ":/images/blackSquare.png" );
QPixmap pixmapWhiteSquare = QPixmap( ":/images/whiteSquare.png" );

// Establish the Scene
QGraphicsScene scene;
scene.setSceneRect( -370.0, -370.0, 740.0, 740.0 );

// Set up the Board
QGraphicsPixmapItem pixmapItem0;
pixmapItem0.setPixmap( pixmapBlackSquare );
pixmapItem0.setPos( -370, 296 );
scene.addItem( &pixmapItem0 );

QGraphicsPixmapItem pixmapItem1;
pixmapItem1.setPixmap( pixmapWhiteSquare );
pixmapItem1.setPos( -296, 296 );
scene.addItem( &pixmapItem1 );

// Establish the View
QGraphicsView view( &scene );
view.setGeometry( 400, 200, 760, 760 );
view.setRenderHints( QPainter::Antialiasing );
view.show();

return app.exec();
}


And then the unsuccessful test with the vector:



// ChessVectorTest2
// MDJ 2016/11/02

// Qt 5.7 - Qt Creator 4.0.2 - Qt Widgets Application
// No changes to files other than this main.cpp - except:
// Added line "RESOURCES += application.qrc" to ChessTest2.pro.
// Added images folder with files:
// blackSquare.png
// whiteSquare.png
// Added application.qrc Resources file:
// <!DOCTYPE RCC><RCC version="1.0">
// <qresource>
// <file>images/blackSquare.png</file>
// <file>images/whiteSquare.png</file>
// </qresource>
// </RCC>

// This test compiles:
// but displays nothing
// and reports no errors

#include "mainwindow.h"
#include <QApplication>
#include <QPixmap>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QVector>

int main( int argc, char **argv )
{
QApplication app(argc, argv);

// Load the Resource Graphics
QPixmap pixmapBlackSquare = QPixmap( ":/images/blackSquare.png" );
QPixmap pixmapWhiteSquare = QPixmap( ":/images/whiteSquare.png" );

// Establish the Scene
QGraphicsScene scene;
scene.setSceneRect( -370.0, -370.0, 740.0, 740.0 );

// Set up the Board
QVector< QGraphicsPixmapItem* > boardVector( 2 );

boardVector[ 0 ]->setPixmap( pixmapBlackSquare );
boardVector[ 0 ]->setPos( -370, 296 );
scene.addItem( boardVector[ 0 ] );

boardVector[ 1 ]->setPixmap( pixmapWhiteSquare );
boardVector[ 1 ]->setPos( -296, 296 );
scene.addItem( boardVector[ 1 ] );

// Establish the View
QGraphicsView view( &scene );
view.setGeometry( 400, 200, 760, 760 );
view.setRenderHints( QPainter::Antialiasing );
view.show();

return app.exec();
}


Without any error messages, I'm pretty much lost.

Added after 10 minutes:

To d_stranz:

I'm trying to organize the QGraphicsPixmapItems into a QVector and then add the individual vector entries to the scene. This will facilitate subsequent logic.

This is a chess board. The squares are added to the scene, and the pieces will be added with the squares as parents. This will facilitate checking how many attacks are currently leveled at a given square and the legality of proposed moves. It will also facilitate the actual moves themselves by setting a new parent for the piece in question.

anda_skoa
2nd November 2016, 19:15
// Set up the Board
QVector< QGraphicsPixmapItem* > boardVector( 2 );

boardVector[ 0 ]->setPixmap( pixmapBlackSquare );


Here you are accessing an uninitialized pointer.
The vector contains "space" for pointers, you need to put pointers to objects into it.


boardVector[ 0 ] = new QGraphicsPixmapItem();
boardVector[ 0 ]->setPixmap( pixmapBlackSquare );




Without any error messages, I'm pretty much lost.

This should have crashed in the first line where you access an invalid pointer.

Cheers,
_

d_stranz
3rd November 2016, 02:11
I'm trying to organize the QGraphicsPixmapItems into a QVector and then add the individual vector entries to the scene. This will facilitate subsequent logic.

It actually doesn't matter in which order you do this, so long as at some point you *do* add the items to the scene. Just remember that when your item is destroyed by the scene, the pointer stored in the QVector will not be valid. Do not delete the pointers that you add to the QVector; they will automatically be deleted by the scene.


the pieces will be added with the squares as parents

You might want to rethink that, since the pieces will be moving from square to square and thus will be reparented often. You might want to make them direct children of the scene, but set their z-numbers so they are drawn on top of their respective squares. It will be very easy to keep track of which piece is on which square if you make two reciprocal QMaps: one that maps piece to square, the other that maps square to piece.

Regardless, it would probably be a better design to model the game independently of the display of the game. That is, instead of using QGraphicsPixmap items to represent the actual pieces in the game, model the board, pieces, and rules in a display-independent model. Use signals and slots to communicate changes in the board position, etc. to update the scene. By doing this, you can develop the game rules independently of how you display the board, and can get the logic for that working correctly without also getting all tied up in the game display at the same time. You'll also find it easier to change how the game is displayed, because you won't have to also change the game logic at the same time.

mdavidjohnson
3rd November 2016, 21:59
anda_skoa and d_franz --

BTW, When I reply to two different posts, how can I force the system to publish them as two separate posts instead of combining them like this one?

Thank you, anda_skoa -

That does seem to have fixed it.

I'm not sure why it didn't crash before though.

Added after 23 minutes:

Thank you, d_franz -

Using two reciprocal QMaps is an intriguing idea. And I like the concept of separating the logic from the display.

But I wonder if the overhead of repeatedly referring to the maps might exceed the benefits.

For a simplified example, suppose I have a White Bishop at a1. It's potential moves would then be limited to b2, c3, d4, e5, f6, g7, and h8.

We check b2 and there's nothing on that square, so it's possible to move to b2.

We check c3 and there's a piece on it. If it is a White piece, it's not possible to move to c3. If it is a Black piece, it's possible to move to c3 with a capture.

In either case, our checking is short-circuited: There's no need to check d4, e5, f6, g7, or h8 because our Bishop cannot move past c3.

But, as an extended example, a Queen on a central square might have to check as many as 7 + 7 + 7 + 7 = 28 different squares for move legality.

I will think more on this.

Would reciprocal QHashs be superior to QMaps for this application?

d_stranz
4th November 2016, 15:46
But, as an extended example, a Queen on a central square might have to check as many as 7 + 7 + 7 + 7 = 28 different squares for move legality.

This is why it took decades for IBM to teach Big Blue to play master-level chess. And that isn't even considering look-ahead or strategy.

Map lookup -is- hash-based, the main difference being that QMap iterators return entries in key-sorted order, whereas QHash entries are arbitrarily ordered. If you don't have a need for ordering pieces or squares, then a QHash would work fine. (I would presume your keys would be the names of the squares or something like "wb1" for pieces, which are unique).

Lookups are extremely fast, so even if you did have 28 potential moves, checking them all would be quick. That particular scenario is the worst case anyway - for other pieces and for more typical piece placements, the number of things you'd need to check would be much less before you were blocked by another piece or ran off the edge of the board. I think I would also compute the allowed moves on the fly, using rules for each piece, rather than try to build a lookup table for each piece at each location on the board. That is, knowing the position of a white bishop ("x,y"), you know that it has potential moves to (x+1, y+2), (x-1, y+2), (x+1, y-2), (x-1, y-2), (x+2, y-1), etc., if I remember my chess rules correctly, so long as the target squares are on the board and not occupied by a piece of the same color. For a queen at (x,y), the rules are (x+n, y+n; n <=7), (x+n, y; n<=7), etc.

It is always good to separate logic from visualization. As you get the logic right, you can report the game moves as text (wb a1 ->c3) and worry about displaying it graphically later. You're probably going to want to create game log anyway, so that's how you'd report it.