PDA

View Full Version : Problem with QGraphicsScene, QGraphicsProxyWidget, and setting widget focus



shobogenzo
25th June 2010, 01:50
First, a user-level description of my problem:

I wrote a popup window that hovers when a user right-clicks on a triangle mesh. The popup window appears correctly. However, the popup does not handle click and move events correctly, and instead they are passed through to the open gl window. (For example, attempting to move the popup rotates the scene.)

Now the code:

I wrote a QGraphicsView and attached a custom QGraphicsScene to it. The custom QGraphicsScene overrides keyboard and mouse callbacks.

To implement my right-click popup window, I created a minimal custom proxy class. It looks like this:


class ProxyWidget : public QGraphicsProxyWidget {
public:
ProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags flags = 0) : QGraphicsProxyWidget(parent, flags) {
setFocusPolicy(Qt::StrongFocus);
}
};

My popup widget class is named "SelectionPopup." To initialize the proxy class, I do this:


void SelectionPopup::initProxy() {
m_proxy = new ProxyWidget(NULL, Qt::Tool);
m_proxy->setWidget(this);
MainWindow::ptrGraphcsView()->scene()->addItem(m_proxy);

MainWindow::ptrGraphcsView()->scene()->setActiveWindow(m_proxy);
m_proxy->setFocus();
}

Then in my overridden setVisible method, I set the proxy widget's focus like so:


void SelectionPopup::setVisible(bool visible) {
if (visible) {
MainWindow::ptrGraphcsView()->scene()->setActiveWindow(m_proxy);
m_proxy->setFocus();
}
else {
MainWindow::ptrGraphcsView()->scene()->setActiveWindow(0);
}
}


So, as I said above, after the setVisible call, the popup appears beautifully, but I am unable to click on or move it. The mouse events are passed straight to the QGraphicsScene event handlers.

Does someone know what is going on? With a debugger I can see that within the QGraphicsScene, "focusItem()" returns the proxy widget. However the program behaves as if the opengl widget still has focus.

How can I change the code so that the proxy window behaves correctly when in focus, and (also when the proxy widget is in focus) QGraphicsScene ignores mouse input? Do I need to implement event handling with a custom QGlWidget? Do I need to set some additional flags in my QGraphicsScene class?

Any help is appreciated and I will gladly provide more information if needed. Thanks.

JohannesMunk
25th June 2010, 21:38
I'm not sure. But I think the default handlers of the scene dispatch scene events to the items. And then when a proxy item recieves a sceneEvent it dispatches a normal event to the embedded widget?

So try and remove your scene handlers.

Joh

shobogenzo
25th June 2010, 22:32
Thank you for your reply. Do you mean try to remove the mouse event handlers in QGraphicsScene? If so, I tried that. The mouse focus still did not get passed to my proxy widget.


I'm not sure. But I think the default handlers of the scene dispatch scene events to the items. And then when a proxy item recieves a sceneEvent it dispatches a normal event to the embedded widget?

So try and remove your scene handlers.

JohannesMunk
25th June 2010, 22:38
Ok. And you removed it fully? So that the base classes implementation gets called.

Another thing. You are setting the focus to the proxy. Have you tried to set it to the embedded widget?

But mouse events have not much to do with the focus anyhow. I do know that I got that working.
But I can't see your error right now.

Joh

JohannesMunk
25th June 2010, 22:41
Oh and have you set the Qgraphicsitem flag ItemIsFocusable?

proxy.setFlag(QGraphicsItem::ItemIsFocusable,true) ;

Joh

shobogenzo
25th June 2010, 23:10
Hi Joh,

Yep I tried setting the ItemIsFocusable flag.

(And yep I tried removing the mouse move event handlers completely.)


Oh and have you set the Qgraphicsitem flag ItemIsFocusable?

proxy.setFlag(QGraphicsItem::ItemIsFocusable,true) ;

JohannesMunk
25th June 2010, 23:20
Could you create a small test project, just a view, scene, and add a widget to it and try handling its mouse events.

No time to do it myself, right now.

Joh

shobogenzo
28th June 2010, 17:34
Yep, I'll try creating a small test project this week. But first I'll try moving all mouse/keyboard events out of QGraphicsView and into QGraphicsScene.

Ian

shobogenzo
9th July 2010, 00:37
OK, finally got the code working correctly.

In terms of focus, the key piece of missing functionality that I added is checking for a focus item in QGraphicsScene event handlers. Specifically, if the graphics scene has a focus item, the code ignores the event and instead calls the default QGraphicsScene event handler. The mouse move event excerpt is below.


void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMous eEvent *event) {
if(!focusItem()) { //only handle events if no proxy items are in focus
//rotate openGL scene, etc
}
else {
QGraphicsScene::mouseMoveEvent(event); //call this so that proxy widget will receive event
}
}

Before (when the code did not check for a focus item) the openGL scene would rotate correctly when no popup was displayed. However, when the popup was displayed and the user tried to move it, the openGL scene would rotate rather than correctly transferring focus to the popup and moving that instead.

Then, even after I fixed the above problem I ran into an additional issue: calling QDialog::show() and QGraphicsProxyWidget::setFocus() messed up the openGL state. The result was that the QGraphicsScene::drawBackround() code produced artifacts/garbage once the proxy popup was opened.

To fix that problem I added beginNativePainting() and endNativePainting() calls to the drawBackground() definition (which I should have had in place before anyways).


void MyGraphicsScene::drawBackground(QPainter *painter, const QRectF &)
{
painter->beginNativePainting();
...
painter->endNativePainting();
}