Unsolved drags, drops, signals and slots of QGraphicsPixmapItem(s)...
-
I am creating a project which someday will resemble the game called freecell. I'm still new-ish to QT, so I'm learning as I go along. For this game, I have created a QGraphicsScene which, once I've added all the elements, (cards in this case) will be contained by a QGraphicsView. So far it's going fairly well. By that I mean I can place the cards (which are descendants of QGraphicsPixmapItem) into the scene using the addItem() function of the scene. Before being placed, the cards are made mobile using the setFlag() method with the parameters of (QGraphicsItem::ItemIsMoveable,true). So now I have cards and the user is free to move them anywhere within the scene. What I need to do is restrict the places where the card may be dropped or, if that's not possible, changing the location of the card (setPos()) after it's dropped to a place that I've decided is appropriate. I also would like to be able to examine the card when it's first clicked with intent to move, so the program can decide if there are other cards below it which must move also. My guess is that each card probably sends a signal when it's clicked, when it's moved, when it's finished moving and when it's un-clicked. I think the scene will probably contain the slot that will react to these signals. I don't know if this is accurate and even if it is, I don't know what the names of the signals or the slot(s) are. Obviously, I have to override the existing slot(s) and connect them to the appropriate signals so I will need to know what they're called. I've spent a considerable amount of time searching the help regarding QGraphicsView, QGraphicsItem, QGraphicsScene, etc. but have not been able to find out this information. I looked through the help and tutorials for solutions to my needs, but the only thing I can find is a tutorial that starts with creating a drag and doing other things. I don't want to create a drag, I want to REACT to a drag.
I am also aware that the possibility exists that I'm thinking about this completely the wrong way and there's actually another easy way to do it. I'd like to hear from the learned minds of QT users to set me straight.
-
@ffej2ffej said in drags, drops, signals and slots of QGraphicsPixmapItem(s)...:
I am creating a project which someday will resemble the game called freecell. I'm still new-ish to QT, so I'm learning as I go along.
For this game, I have created a QGraphicsScene which, once I've added all the elements, (cards in this case) will be contained by a QGraphicsView. So far it's going fairly well. By that I mean I can place the cards (which are descendants of QGraphicsPixmapItem) into the scene using the addItem() function of the scene. Before being placed, the cards are made mobile using the setFlag() method with the parameters of (QGraphicsItem::ItemIsMoveable,true).
So now I have cards and the user is free to move them anywhere within the scene. What I need to do is restrict the places where the card may be dropped or, if that's not possible, changing the location of the card (setPos()) after it's dropped to a place that I've decided is appropriate. I also would like to be able to examine the card when it's first clicked with intent to move, so the program can decide if there are other cards below it which must move also.
My guess is that each card probably sends a signal when it's clicked, when it's moved, when it's finished moving and when it's un-clicked. I think the scene will probably contain the slot that will react to these signals. I don't know if this is accurate and even if it is, I don't know what the names of the signals or the slot(s) are. Obviously, I have to override the existing slot(s) and connect them to the appropriate signals so I will need to know what they're called. I've spent a considerable amount of time searching the help regarding QGraphicsView, QGraphicsItem, QGraphicsScene, etc. but have not been able to find out this information. I looked through the help and tutorials for solutions to my needs, but the only thing I can find is a tutorial that starts with creating a drag and doing other things. I don't want to create a drag, I want to REACT to a drag.
I am also aware that the possibility exists that I'm thinking about this completely the wrong way and there's actually another easy way to do it. I'd like to hear from the learned minds of QT users to set me straight.Wow. Wall ;)
anyway, i think you can use
https://doc.qt.io/qt-5/qgraphicsitem.html#itemChange
with this
https://doc.qt.io/qt-5/qgraphicsitem.html#GraphicsItemChange-enumQGraphicsItem::ItemPositionChange
as you can see from sample in first link. its possible to return another pos and that gives the ability
to snap to cards or return the orginal value to cancel move.note.
you most likely need to do
setFlag( QGraphicsItem::ItemSendsScenePositionChanges, true );
for the items to have it send the needed info.here is sample that snaps to a grid.
https://www.walletfox.com/course/qgraphicsitemsnaptogrid.php -
Hi,
Just in case, the Graphics View examples already provides several nice starting points.
-
@mrjj I read the pages referred in your reply and added the ItemPositionHasChanged() function into the Card object. I also added the setFlag(QGraphicsItem::ItemSendsGeometryChanges,true) statement per the documentation.
The function was not called when I moved the items with the mouse so I added 2 setPos() statements into the object that creates and places the cards. It still didn't call the ItemPositionHasChanged() function. AAAaaaargh!
P.S. I tested whether the function was called by adding a qDebug("some text") statement to the function. No qDebugs occurred. -
@ffej2ffej
Hi
did you use the override keyword to be truly sure you did get function
signature 100% right ?
Without code, its hard to guess at :) -
@mrjj
Here is all the code with horizontal dividers to distinguish the beginnings and endings of all the files.
---------------------------------------card.h----------------------------------------
#ifndef CARD_H
#define CARD_H
#include <QGraphicsPixmapItem>
#include <QGraphicsSceneDragDropEvent>class Card : public QGraphicsPixmapItem
{
public:
Card();
int suit;
int number;
void setSuit(int sentSuit);
void setNumber(int sentNumber);
protected:
//void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override;
void QGraphicsPixmapItem::ItemPositionHasChanged() override;
};#endif // CARD_H
-----------------------------------end of card.h-----------------------------------------------------------------------card.cpp-------------------------------------------
#include "card.h"
#include <QDebug>Card::Card()
{
//ItemSendsGeometryChanges
}void ItemPositionHasChanged()
{
qDebug("Card has moved.");
}
------------------------------------end of card.cpp----------------------------------
-------------------------------------game.h------------------------------------------
#ifndef GAME_H
#define GAME_H
#include <QGraphicsView>class Game : public QGraphicsView
{
public:
Game();//constructor (duh!)
};#endif // GAME_H
-----------------------------------end of game.h-------------------------------------
------------------------------------game.cpp-----------------------------------------
#include "game.h"
#include <QGraphicsScene>
#include <card.h>
#include <QImage>Game::Game()
{
QGraphicsScene *scene = new QGraphicsScene();
Card *card1 = new Card();
Card *card2 = new Card();
Card *bigCard = new Card();
card1->setPixmap(QPixmap(":/images/1s.png"));
card2->setPixmap(QPixmap(":/images/2s.png"));
bigCard->setPixmap(QPixmap(":/images/1sbigger.png"));
card1->setFlag(QGraphicsItem::ItemIsMovable,true);
card2->setFlag(QGraphicsItem::ItemIsMovable,true);
bigCard->setFlag(QGraphicsItem::ItemIsMovable,true);
bigCard->setFlag(QGraphicsItem::ItemSendsGeometryChanges,true);
card1->setPos(0,0);
card2->setPos(20,0);
bigCard->setPos(30,30);//arbitrary numbers. Sometimes a cigar is just a cigar.
scene->addItem(card1);
scene->addItem(card2);
scene->addItem(bigCard);
QGraphicsView *theview = new QGraphicsView(scene);
theview->show();
theview->setFixedSize(800,600);//make sure to adjust this to a proper size for the game!!!!!!!!!!!!!!!!!!!!!
scene->setSceneRect(0,0,800,600);
setBackgroundBrush(QBrush(QImage(":/images/cardsMoveBG.png")));//Doesn't work here or on the video game from tutorial.
bigCard->setPos(40,40);
bigCard->setPos(50,50);
}
-----------------------------------------end of game.cpp-------------------------------
--------------------------------------------main.cpp-----------------------------------
#include <QApplication>
#include "game.h"int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Game *theGame = new Game();
//theGame->show();//this is weird. It shows some mysterious window that is NOT the window I created and specificied all the
//contents, etc. However, if I don't create a Game object, nothing shows at all.return a.exec();
}
----------------------------------------end of main.cpp------------------------------- -
Why are you creating a subclass of QGrapchicsView in which you create a QGraphicsView object and show that one separately ?
-
BTW, I sent the code with the override specified. I cannot, however, compile with it like that because it causes an error.
-
The error being that it's not overriding anything known ?
-
@SGaist I opened and studied both the 40,000 chips and the robot drag&drop examples. I'm afraid I could not figure out how they worked or how I could apply them to my work. Maybe I'm too stoopid to be a programmer. I miss doing assembly language where you simply put a character (optionally, a color too) in a memory spot and it appears on the screen. In graphics modes, you instructed BIOS of the color you want at the x and y coordinates you want then called a function and it appeared. I'm glad they simplified it for c++, he said sarcastically.
-
@ffej2ffej
Hi
Well the step from good old direct framebuffer writing - to full blown object orientated
GUI building is not trivial so some stumbling blocks are to be expected.
The code looks ok but Game class IS a QGraphicsView already so no need to
create another inside Game constructor.
QGraphicsView *theview = new QGraphicsView(scene); // seems not needed.
so should fix that, OR remove the class Game : public QGraphicsView
part so its a clean class and then let it setup the View. ( as you do in that code)However, im not sure what exact error you get so its hard to help.
-
If I state
void QGraphicsPixmapItem::ItemPositionHasChanged() override;
I get
non-friend class member 'ItemPositionHasChanged' cannot have a qualified nameIf I state
void ItemPositionHasChanged() override;
I get
only virtual member functions can be marked 'override'
I wish I knew what either of those errors MEANT, let alone how to solve them. -
@mrjj The problem about the notifications of dragging and dropping are still unsolved but I want to thank you for pointing out the fact that I had made the Game object a descendant of QGraphicsView. The details of how I made that mistake are long a boring and suffice to say I've fixed it. I still, however, am not notified when the user drags and drops a card.
-
@ffej2ffej said in drags, drops, signals and slots of QGraphicsPixmapItem(s)...:
ItemPositionHasChanged
Aren't you trying to override an enum value ?
-
Hi
Its
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
it has to match exactly.