PDA

View Full Version : Design movable menu (buttons) in QGraphicsScene



whiteShadow
9th November 2015, 16:37
Hello,

I'm trying to design a custom menu (made of buttons, not a contextual menu) in a graphics scene.
I would like to move my menu at the mouse location when somewhere on the graphics scene (but now when I click on one of the buttons).

Here is an example of what I'm trying to do:
11506

My guess is that I need to create a container for my buttons and add it to the scene. Then when I click on the scene somewhere, it moves the container at this location, and when I click on a button it trigger some actions.

I tried several things (container as a QWidget, as a QGraphicsItemGroup, as a QGraphicsItem) but none of them seems to work as expected, especially when I try to click on one of the buttons, it moves the menu instead of triggering an action.

Did anyone already tried something like that? Or can someone point me in the right direction (I'm running out of idea) ?

Here is some questions I'm wondering:
- Should I use container ? if yes, which one ?
- Should the buttons in the scene be QPushButton or QGraphicsItems with a button behavior ?
- Who should handle the buttons clicks ? The button itself ? The containers ? The scene ? The view ?

Thank you very much.

anda_skoa
10th November 2015, 09:08
Did anyone already tried something like that? Or can someone point me in the right direction (I'm running out of idea) ?

A runnable example might help



Here is some questions I'm wondering:
- Should I use container ? if yes, which one ?

Probably QGraphicsItemGroup



- Should the buttons in the scene be QPushButton or QGraphicsItems with a button behavior ?

No widgets if you can avoid it, so QGraphicsItem based



- Who should handle the buttons clicks ? The button itself ? The containers ? The scene ? The view ?

I'd say the button.

Cheers,
_

whiteShadow
10th November 2015, 15:22
Unfortunately I'm not allowed to disclose the code but I will try maybe to create something similar on a dummy project to show you.

May I ask you why you said to avoid widgets ?
My first thought was that even if it is a custom menu, buttons are still buttons. Why not using QPushButton then ?

Thank you very much for your help.

whiteShadow
10th November 2015, 21:58
Thanks to anda_skoa, I finally find a way to do it.

I don't think it's the best way but it works.

For those having the same issue here is what I did:

I changed my menu buttons from QPushButton to QGraphicsObject. In my buttons constructor, I set setAcceptHoverEvents(true) to be able to change button color when mouse hover them (I check in the paint() if option.state has the QStyle::State_MouseOver flag on and change Brush if it has).
I also set the flag ItemIgnoresTransformation because I don't want my buttons to be zoomed in or move when the scene is transformed (but still I can move then programmatically if I want to position them somewhere else on my scene).
Then I defined boundingRect(), paint() and mousePressEvent(). mousePressEvent accept the event (e->accept()) and emit a signal in my case, because I want to connect my button to slots (which is why I subclassed a QGraphicsObject and not a QGraphicsItem. It's not as good performance wise, but I have a small scene, so it should be fine).

Then I created a container for these buttons. This class subclass QObject (I wanted some signals/slots here too) AND QGraphicsItemGroup.
In this constructor I set setHandlesChildEvents(false) because this is just a container, and I want my buttons inside to receive the actual event.

Finally in my scene, I create a new container, add it to the scene, and created+added buttons in the container. Then I overloaded mousePressEvent in my scene to detect if a click happened on a QGraphicsItem or not. If it's not the case, I accept the event to avoid propagation to other widget, and I do what I have to do when a click on my scene happen.
If a QGraphicsItem has been clicked, then I just call the QGraphicsScene::mousePressEvent to propagate the events to children (then my container will ignore it because I asked him not to handle child events, and finally the buttons will accept it and react by emit a signal).

Here is my customScene::mousePressEvent (it's probably not the best way to implement it, so if anyone has some suggestions to improve it, I'll take'em !!! :D):


void customScene::mousePressEvent(QGraphicsSceneMouseEv ent *event)
{
if (event->button() == Qt::LeftButton)
{
QGraphicsItem* item = this->itemAt(event->scenePos(), QTransform());

if (!item)
{
// No graphics item has been clicked. The scene handle the event then.
event->accept();

// Move probes center
this->centerCrosshair->setPos(event->scenePos());
this->centerCrosshair->show();

// Move menu
this->menu->setMenuCenter(event->scenePos().x(), event->scenePos().y());
this->menu->show();

// Move pattern
for (int i = 0; i < this->pat.size(); ++i)
{
this->pat[i]->setCenter(event->scenePos().x(), event->scenePos().y());
}
}
}

QGraphicsScene::mousePressEvent(event);
}

I hope this could help some people trying to do the same thing. I spent a couple of days trying to figure this out. Now I read it, it seems trivial, but when you don't know, it's not.

If anyone has comments or suggestions to improve it, I'd be happy to ear them.

Thank you.