PDA

View Full Version : Color rectangles using QGraphicsRectItem



poor_fool_mloo
3rd January 2018, 22:51
Hi guys, iam stuck in one case. I have scene (using QGraphicsScene) and i fill that scene with squares (using QGraphicsRectItem). I want squares to change their color to black if my mouse button is pressed and if my mouse coursor is over them so i can move my mouse over the whole scene and make every square to change their color to black whenever cursor enters an area of a square and mouse button is pressed. Can you please give me any idea how to make that happen ? I was trying to solve that using mousePressEvent, mouseMoveEvent, dragEnterEvent etc. and I think that this is a proper way to do that but i have no idea how to push that through. To put more light on my case i have added sample of my code. Thanks for help.

main.cpp:

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include "square.h"
#include "background.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

// create a scene
QGraphicsScene * scene = new QGraphicsScene(0,0,200,250);


Background * background = new Background();
background->fillBackgroundWithSquares(scene);



// add a view
QGraphicsView * view = new QGraphicsView(scene);

view->show();


return a.exec();
}


background.h:

#ifndef BACKGROUND_H
#define BACKGROUND_H
#include <QGraphicsScene>
#include <square.h>
class Background
{
public:
Background();
void fillBackgroundWithSquares(QGraphicsScene *scene);

};

#endif // BACKGROUND_H


background.cpp:

#include "background.h"

Background::Background()
{

}

void Background::fillBackgroundWithSquares(QGraphicsSce ne *scene)
{
// create an item to put into the scene
Square *squares[20][25];


// add squares to the scene
for (int i = 0; i < 20; i++)
for (int j = 0; j < 25; j++) {
squares[i][j] = new Square(i*10,j*10);
scene->addItem(squares[i][j]);
}

}

square.h:


#ifndef SQUARE_H
#define SQUARE_H
#include <QGraphicsRectItem>
#include <QGraphicsView>
#include <QDragEnterEvent>


class Square : public QGraphicsRectItem
{
public:
Square(int x, int y);

private:
QPen pen;

protected:
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void dragEnterEvent(QGraphicsSceneDragDropEvent * event);
void mouseMoveEvent(QGraphicsSceneMouseEvent * event);

};

#endif // SQUARE_H


square.cpp:

#include "square.h"

Square::Square(int x, int y)
{
// draw a square
setRect(x,y,10,10);
pen.setBrush(Qt::NoBrush);

setPen(pen);

setBrush(Qt::cyan);
setAcceptDrops(true);
setAcceptHoverEvents(true);
}

// following functions are just attempt to make every square to be painted on black if I press my mouse button and move cursor over squares but none of the following work as I want.

void Square::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
setBrush(Qt::black);
}

void Square::dragEnterEvent(QGraphicsSceneDragDropEvent *event)

{
setBrush(Qt::black);
}

void Square::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
setBrush(Qt::black);
}

d_stranz
3rd January 2018, 23:56
Try calling QGraphicsItem::update() after you set the new brush.

I would also insert code that checks the current brush color -before- you call setBrush(), especially in the mouseMoveEvent(). You don't want to set the same color over and over again (and redraw over and over again with every tiny movement) if the color is already the one you want:



if ( brush().color() != Qt::black )
{
setBrush( Qt::black );
update();
}

poor_fool_mloo
4th January 2018, 13:12
Thank you for your answer d_stranz I've made some improvements as you suggest but still my problem has'nt been solved yet. Can you please give me a hint how to make every square color to black as i move mouse over squares with mouse button pressed ?

d_stranz
4th January 2018, 17:05
i move mouse over squares with mouse button pressed

If you read the documentation for QGraphicsItem::mousePressEvent() you will learn that once you press the mouse on an item, it grabs the mouse. This means that whether you move the mouse off of that item and onto another item, the original item receives the mouse events until you release the button. The way around this is to call QEvent::ignore() in your handler, but then that results in the mouse press being ignored until you release and press it again.

If you call ignore() and then move to a different square with the mouse button still pressed, I do not know if this will result in a new mouse press event for the new item. If it does not, then basically you cannot accomplish what you want.

poor_fool_mloo
5th January 2018, 14:47
I've got stupid question, how to call ignore() ? Because if I do something like this:


void Square::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if ( brush().color() != Qt::black ){
setBrush( Qt::black );
update();

}
event->ignore();
}

then application throws an error: invalid use of incomplete type 'class QGraphicsSceneMouseEvent'

so if i change event type to QEvent like that:


void Square::mouseMoveEvent(QEvent *event)
{
if ( brush().color() != Qt::black ){
setBrush( Qt::black );
update();

}
event->ignore();
}

Application run properly but mouseMoveEvent handler will never be handled.

d_stranz
5th January 2018, 19:34
invalid use of incomplete type 'class QGraphicsSceneMouseEvent'

Learn to understand what your compiler is telling you. This message means you have forgotten to #include the header file that defines QGraphicsSceneMouseEvent at the top of your cpp file.


Application run properly but mouseMoveEvent handler will never be handled.

Because by changing the signature for mouseMoveEvent() you have defined a new method for your Square class that Qt's event system knows nothing about and will never call. You should also understand that in C++, the combination of method name, return value type, and argument types define a function, not just the method name. You can't just arbitrarily replace one argument type for another. Those are different functions as far as the compiler, linker, and runtime are concerned, and the new one does not substitute for the old one. The old one still exists in Square's base class, and that's the one being called.

poor_fool_mloo
7th January 2018, 12:29
Thank You for the expenation d_stranz. I will leave this thread open maybe someone will have an idea how to solve that.

d_stranz
7th January 2018, 21:51
maybe someone will have an idea how to solve that.

How to solve what? The "incomplete type" error? I told you how to solve it - in your cpp file, you need to #include the header file where QGraphicsSceneMouseEvent is defined.

poor_fool_mloo
8th January 2018, 16:38
Sorry for my impreciseness. By writting that maybe someone will have an idea how to solve that I meant that maybe someone will have an idea how to make every square color to black as i move mouse over squares with mouse button pressed. Adding #include <QGraphicsSceneMouseEvent> as You suggested was good answer to my ignore() error problem, thank You. But adding ignore() as above did'nt solved that problem with coloring squares.

d_stranz
8th January 2018, 17:20
did'nt solved that problem with coloring squares.

No, it will not solve that problem. As I said, when you press the mouse button when the mouse is over a graphics item, that item will grab all of the mouse events until you release the button, no matter where the mouse moves to. This is to support click and drag so graphics items can be moved in a scene.

Unless you rewrite the way the Qt graphics / view architecture processes mouse events, you can't change this behavior. It is not possible to implement something where you can press the mouse button on an item, keep it pressed, and have other items respond to the mouse. The item you pressed the mouse on owns the mouse until you release the button.

I suggest you look for a different way to implement the behavior you want. One way could be to combine the mouse movement with a keyboard modifier (like Shift or Ctrl). If you get a hover enter event and the Shift key is pressed, then you can change the color of the square, for example.

poor_fool_mloo
12th January 2018, 13:52
Thank You for your explanation and the advice d_stranz! The combination of hoverEnterEvent() and keyboard modifier really worked for me. Problem solved :)