PDA

View Full Version : Can't trigger mousePressEvent on QGraphicsPixmapItem



blooglet
11th November 2010, 18:24
To learn Qt, I'm creating something that resembles a chess board. I'm not going to implement the game logic; I just want to toy with mouse click events and drag events.

I want to be able to drag chess pieces (without drop for now), but the chess piece's mousePressEvent and mouseMoveEvent don't get triggered when I expect them to. I double checked it by putting breakpoints at the start of both event handling functions.

The GUI

http://i.imgur.com/HdOeA.jpg

Each chess piece is represented by an instance of this class.


class ChessPiece : public QGraphicsPixmapItem

Each board tile is represented by an instance of this class.


class BoardTile : public QGraphicsItem

===============================

chesspiece.h (snippet)


void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);

If there are any important code snippets that you need to see to help me out, either ask me to post them or download the full source here (http://www.pieterdedecker.be/tmp/QtChess-20101111.zip). I work with QtCreator.

qlands
11th November 2010, 18:31
Hi,

Did you override the scene mousepress/move/release event? If you did I think you need to call for example QGraphicsScene::mousePressEvent(mouseEvent) in the mousePress to propagate the event to the items.

blooglet
11th November 2010, 18:39
Did you override the scene mousepress/move/release event? If you did I think you need to call for example QGraphicsScene::mousePressEvent(mouseEvent) in the mousePress to propagate the event to the items.

I did override the default event. Are you saying that I need to do this?


void ChessPiece::mousePressEvent(QGraphicsSceneMouseEve nt *event) {
QGraphicsScene::mousePressEvent(mouseEvent);
// ...


But if that function will never get called, the call to QGraphicsScene::mousePressEvent(mouseEvent) will never be made, right?

qlands
11th November 2010, 18:49
Not ChessPiece but the scene.

Inside your void myScene::mousePressEvent(QGraphicsSceneMouseEvent *event) you need to call QGraphicsScene::mousePressEvent(mouseEvent);

blooglet
11th November 2010, 19:27
Oh, you meant if I overrode myScene::mousePressEvent(QGraphicsSceneMouseEvent *event). No, I didn't override the default mousePressEvent handler for the graphics scene. I do have some classes (BoardTile and ChessPiece) that inherit from QGraphicsItem that do have mousePressEvent overrides.

Either way, if I define a custom myScene::mousePressEvent(QGraphicsSceneMouseEvent *event) with a call to QGraphicsScene::mousePressEvent(mouseEvent) in it, I can see that clicks are being registered on the level of the graphics scene, but the objects are still not getting the click.

qlands
11th November 2010, 20:40
Hi,

It work when you set the flag ItemIsSelectable to true:

boardTiles[i][j]->setFlag(QGraphicsItem::ItemIsSelectable,true);

blooglet
11th November 2010, 20:49
I set the flag on both the tiles and the pieces. But when I click on a white piece, the mouseReleaseEvent of the underlaying tile is triggered instead of the mousePressEvent of the white piece. Isn't Qt supposed to trigger the mouse event for the topmost object on the graphics scene when it is being clicked? In case you're wondering why I implemented a mouseReleaseEvent instead of a mousePressEvent on the tiles, it's because the press event behaved oddly and the release event worked fine.

qlands
11th November 2010, 21:27
The click on the piece does not work because the QGraphicsPixmapItem does not have any pixmap. This because you are wrongly overriding the paint event to set the image... No need for this!!!

Remove the paint event form the piece and add:
QPixmap logo(":/img/pawn.png","PNG");
newCP->setPixmap(logo);

When you click on a piece you will find a box around it, you can remove it by properly overriding the paint()



void ChessPiece::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
if (isSelected())
{
QStyleOptionGraphicsItem *newOption = const_cast<QStyleOptionGraphicsItem*>(option);
//Remove the style
newOption->state &= ~QStyle::State_Selected;
QGraphicsPixmapItem::paint(painter, newOption, widget);
}
else
QGraphicsPixmapItem::paint(painter,option,widget);
}


Basically you are braking the events of items and this cause things to behave weird. I suggest you to take an look at the documentation and examples on how to use QGraphicsPixmapItem

blooglet
11th November 2010, 22:30
Cool... I can now initiate a drag on the chess pieces. I've figured out that I can set the position of the QGraphicsPixmapItem by doing setPos(x, y) but I also want to set the width and height. As far as I can tell from reading the doc, I can only do setScale(a number between 0 and 1).

About the incorrect use of the paint event... What about my paint override for the board tiles? Here's what I did there:


void BoardTile::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
if (tileColor == 0) {
painter->setBrush(QBrush(QColor(251, 201, 159)));
painter->setPen(QPen(QColor(251, 201, 159)));
} else {
painter->setBrush(QBrush(QColor(207, 139, 70)));
painter->setPen(QPen(QColor(207, 139, 70)));
}
painter->drawRect(x, y, width, height);
}

I was trying to apply this example from the doc (http://doc.qt.nokia.com/4.7/qgraphicsitem.html) for QGraphicsItem to my objects:


class SimpleItem : public QGraphicsItem
{
public:
QRectF boundingRect() const
{
qreal penWidth = 1;
return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,
20 + penWidth, 20 + penWidth);
}

void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
painter->drawRoundedRect(-10, -10, 20, 20, 5, 5);
}
};

Why is that a good override for the paint event and why was mine bad?

qlands
12th November 2010, 06:54
With void BoardTile:: paint().. You are overriding the paint event and modifying the pointer to the painter (line 9) but not calling the ancestor. You are sub-classing BoardTile from QGraphicsItem thus you usually need to call the ancestor paint event otherwise you don't know what your are braking. In my experience overriding a paint event and not calling the ancestor usually ended up in unpredictable behavior.

Look at this post where they discuss a little of overriding events: http://www.qtcentre.org/threads/24014-General-question-about-events-overriding

Width and height is defined by the size of your QPixmap. You can use a QPixmap.scaled() to return a scaled version of the pixmap and use it in the QGraphicsPixmapItem.

blooglet
12th November 2010, 12:47
I updated the paint overrides. The dragging works on the top left piece, but other pieces still won't budge.

Screen capture: http://www.screentoaster.com/watch/stUE5QREZKRFtXSVhaWl1RVFVX/qtchess
Updated code: http://www.pieterdedecker.be/tmp/QtChess-20101112.zip

Any thoughts on why the code doesn't work in all cases?

qlands
16th November 2010, 13:03
Hi,

You are implementing the movement in the piece itself and not in the scene... Both of them are different as they have different coordinate systems for example.

I mode some changes in your code to allow the movement of the pieces and to control where the piece if moved to.

You can get the code here:

http://www.qlands.com/other_files/QtChess-20101112.tar.gz

I made changes in piece,tile and the boargui.

Basically I:
1. removed the events from piece and tile.
2. Added an type to piece and tile so I can cast them
3. Added mousepress and mouserelease to the boardgui
4. In those event I coded some bit so the piece can be moved and control when is taken and dropped
5. Each piece need a flag movable to true.

See that I dont use the event of drag and drop. If you want to use those events to set each piece to allow drops but is not really neccesary. You can also use the scene mousemove to check the current tile under the mouse while holding the piece and change its color to indicate that is allowed in that time.

Hope this helps!

blooglet
16th November 2010, 15:07
I appreciate your effort, but the dragging is glitchy on my computer. Have a look at this: http://www.screentoaster.com/watch/stUE5QREZKRFtXSVRdXVhfUVVU/dragndrop

Do I have to repaint the board at the end of mousePressEvent and mouseReleaseEvent?

qlands
17th November 2010, 07:14
Yes, I saw that. What is happening is that the scene is not refreshing properly as the piece pass under the tile items. It could be a bug. I recommend you to post that as a Thread is this forums.

Carlos.

blooglet
17th November 2010, 11:03
Alright, thanks!