PDA

View Full Version : Getting focus of a QWidget inside a QGraphicsScene



jovin
12th March 2012, 21:42
Hello,

I'm working on a project where I have a QGraphicsScene with QWidgets on it.
I add the QWidgets with QGraphicsScene::addWidget().

However there are a few flaws and I can't seem to find a solution inside the Documentation alone.
First, It seems that I can't move the widgets. They are always centered. Resizing works, but I can't move them. Weird?

Then my main problem is that I can't get "their" focus. I'm developing on KDE and Widgets with focus get a highlighted border, this
is not the case for my QWidgets inside my QGraphicsScene. I'm trying to click on them or tab through the widgets. But neither works. Is there a reason for it?

I would like to provide code, but first I would have to make an example, since my project's code is a bit hard to understand.

high_flyer
13th March 2012, 14:11
Have a look at:
http://qt-project.org/doc/qt-4.8/qgraphicsitem.html#GraphicsItemFlag-enum

jovin
13th March 2012, 20:22
That's not possible. I'm not using QGraphicsItems inside my QGraphicsScene, but QWidgets.

Edit:

Ok, I'm setting the flags now via QGraphicsProxyWidgets. However, it makes no difference at all...

Here example code:


// Here I store my QGraphicsScene widgets...
std::vector <QGraphicsProxyWidget*> proxy_for_mywidgetclass;
std::vector <MyWidgetClass*> mywidgets;

// Now I call a function to fill the vectors
void MySuperWindow::draw_widget(SomeOtherClass object )
{
MyWidgetClass *widget = new MyWidgetClass(object, 200, 150);
QGraphicsProxyWidget *widgetproxy = MyScene->addWidget(widget);

widgetproxy->setFlag(QGraphicsItem::ItemIsSelectable); // Also tried
widgetproxy->setFlag(QGraphicsItem::ItemIsMovable); // this and
widgetproxy->setFlag(QGraphicsItem::ItemIsFocusable); // this

mywidgets.push_back(widget);
proxy_for_mywidgetclass.push_back(widgetproxy);
}


One widget shows up on my scene, I can resize it, but neither move nor adding labels on it etc.
And I can't get the focus.

high_flyer
14th March 2012, 09:53
What happens if you just use a Qt widget (say a QPushButton) and not your own custom widget - can you move it then?

EDIT:
I am not sure about this - but try not parenting your widget - see if it helps.

jovin
14th March 2012, 14:26
I tried it with QPushButton, resizing works and the button itself works, but again, I can't move it.


QPushButton *pushy = new QPushButton;
pushy->setGeometry(23, 100, 200, 120);

The position won't change at all, but the size.

I'm not sure if I understand you. You mean I should leave QWidget *parent = 0 ?
If so, I'm already doing it. :(

high_flyer
14th March 2012, 14:47
I'm not sure if I understand you. You mean I should leave QWidget *parent = 0 ?
If so, I'm already doing it.
Not in the code you posted:

MyWidgetClass *widget = new MyWidgetClass(object, 200, 150);

I'll have to look on an example of my own, but can't at the moment.
I'll try later and see if I can help you further.

high_flyer
14th March 2012, 18:20
After trying - indeed, the widget items do not adhere to the same move policy as regular items. (it did surprise me)
I imagine that this is done on purpose, since regular widgets on regular forms don't move around.
You could try to subclass or install an eventFilter() and catch the move events - and call the QGraphicsItem implementation there.
Or read really carefully the docs, maybe there is some flag somewhere that can control this behavior with out sub classing or installing an eventFilter().

jovin
14th March 2012, 19:06
That's nasty.
If I set the size for QGraphicsScene (instead of leaving it uninitialized at 0,0 and thus it's not able to move, I guess?) to the size of the QGraphicsView
it's possible to move the widgets again.


myscene = new QGraphicsScene(QRect(0, 0, 800, 600), this);

Now, there is one problem left. My widgets are still unable to get focused.
And I'm already using a Proxy, tried all flags and got them moving again. :(

high_flyer
15th March 2012, 13:49
If I set the size for QGraphicsScene (instead of leaving it uninitialized at 0,0 and thus it's not able to move, I guess?) to the size of the QGraphicsView
it's possible to move the widgets again.
In my test application I did initialize the scene rect, but not to the views size - and the widget item stil didn't move.
I am interested how you came to the idea to try it?

My widgets are still unable to get focused.
Just to make sure we both mean the same with "focused" - what do you mean by "focused"?
Again, I'll have to test it, and I can do it only much later.

jovin
15th March 2012, 19:47
In my test application I did initialize the scene rect, but not to the views size - and the widget item stil didn't move.
I am interested how you came to the idea to try it?


After a long google orgy I found a hint, because someone had a similiar problem with the always "centered widgets", though
I don't know where I found it anymore.



Just to make sure we both mean the same with "focused" - what do you mean by "focused"?
Again, I'll have to test it, and I can do it only much later.

Hmm. I mean that the widget is selected, gets a blue border in KDE terms.
But I think since QPushButtons work (it's getting a blue border around the button when I push), a simple subclass of QWidget (like mine) will not show that they are
"focused".

I want my widget to have a visible effect when it's focused.

I thought setting the flag, would do so.

high_flyer
15th March 2012, 22:20
I tried what you said with setting the scene to the views geometry, but it didn't help my widget item to move...
Could you post your initialization code?
That is interesting.
Here is mine:


QGraphicsView* pView = new QGraphicsView();
pView->show();
pView->resize(300,300);

QGraphicsScene* pScene = new QGraphicsScene(QRect(0,0,300,300),pView);
QGraphicsProxyWidget *pProx = pScene->addWidget(new QPushButton("test"));
pProx->setFlag(QGraphicsItem::ItemIsMovable);
pView->setScene(pScene);

pView->update();



I want my widget to have a visible effect when it's focused.
This might be a window manager issue.
The same code as above but with a QTextEdit under windows, produces a QTextEdit that is showing a light blue border when focused, which gets gray when the widget loses focus.

jovin
15th March 2012, 23:07
My initialization code looks the same, except that my scene parent isn't the QGraphicsView, but my QMainWindow.
I dunno why it works for me, if it isn't because of the different parents.

Hmm. I will try out other widgets and post my results.

You really helped me a lot. I have to thank you.

wysota
16th March 2012, 00:45
I don't have any problems with focusing a widget using the following code:


#include <QtGui>

int main(int argc, char **argv) {
QApplication app(argc, argv);
QGraphicsView view;
QGraphicsScene scene(QRect(-100, -200, 500, 400));
view.setScene(&scene);
QGraphicsProxyWidget *w = scene.addWidget(new QLineEdit);
//w->setFlag(QGraphicsItem::ItemIsMovable); // doesn't work anyway since the widget gets input events
view.show();
return app.exec();
}

If you are using a custom widget, did you set a focus policy on it?

high_flyer
16th March 2012, 09:11
@wysota - can you get widgets items to be movable in the scene?

@jovin - you are welcome :)
I lern that way too!
I will try the thing with parenting the scene and let you know.

wysota
16th March 2012, 10:51
@wysota - can you get widgets items to be movable in the scene?
No. The widget steals mouse events. One would need to reimplement mouse events in the view to prevent that. I was trying the OP's idea but it didn't work for me.

high_flyer
16th March 2012, 11:24
The widget steals mouse events. One would need to reimplement mouse events in the view to prevent that.
In the view?
I would expect in the widget - since the mouse events go through the proxy to the widget, which I guess then consumes it.
I'd expect that if the widget returns 'false' on the event handler the proxy could continue to act on the event with its regular graphics item behavior...
Am I wrong with this theory?

wysota
16th March 2012, 11:43
In the view?
Yes.

I would expect in the widget - since the mouse events go through the proxy to the widget, which I guess then consumes it.
But for the events to reach the proxy, they have to go through the view :) And since it's the item that handles the ItemIsMovable flag, you'd have to reimplement the proxy widget or catch the event earlier.


I'd expect that if the widget returns 'false' on the event handler the proxy could continue to act on the event with its regular graphics item behavior...
Am I wrong with this theory?
I think you are wrong. Yesterday I thought the same so I checked with a widget (QGroupBox, I think) that doesn't handle mouse events (so it should ignore the event) and it was not movable.

Let's look at the code...


void QGraphicsItem::mousePressEvent(QGraphicsSceneMouse Event *event)
{
if (event->button() == Qt::LeftButton && (flags() & ItemIsSelectable)) {
bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0;
if (!multiSelect) {
if (!d_ptr->selected) {
if (QGraphicsScene *scene = d_ptr->scene) {
++scene->d_func()->selectionChanging;
scene->clearSelection();
--scene->d_func()->selectionChanging;
}
setSelected(true);
}
}
} else if (!(flags() & ItemIsMovable)) {
event->ignore();
}
if (d_ptr->isWidget) {
// Qt::Popup closes when you click outside.
QGraphicsWidget *w = static_cast<QGraphicsWidget *>(this);
if ((w->windowFlags() & Qt::Popup) == Qt::Popup) {
event->accept();
if (!w->rect().contains(event->pos()))
w->close();
}
}
}


void QGraphicsProxyWidget::mousePressEvent(QGraphicsSce neMouseEvent *event)
{
Q_D(QGraphicsProxyWidget);
#ifdef GRAPHICSPROXYWIDGET_DEBUG
qDebug() << "QGraphicsProxyWidget::mousePressEvent";
#endif
d->sendWidgetMouseEvent(event);
}

See? QGraphicsProxyWidget unconditionally transfers the event to the widget.

However if you ignore it (as you suggested) the proxy should ignore it as well:

void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent( QGraphicsSceneHoverEvent *event)
{
QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
mouseEvent.setPos(event->pos());
mouseEvent.setScreenPos(event->screenPos());
mouseEvent.setButton(Qt::NoButton);
mouseEvent.setButtons(0);
mouseEvent.setModifiers(event->modifiers());
sendWidgetMouseEvent(&mouseEvent);
event->setAccepted(mouseEvent.isAccepted());
}

... but the event won't reach the code responsible for moving the item which was overridden by reimplementing mouse events in the proxy widget. So again, it's either subclassing QGraphicsProxyWidget or QGraphicsView.

This also explains that setting the scene size to whatever the OP set it couldn't have made the items move ;)

high_flyer
16th March 2012, 12:17
However if you ignore it (as you suggested) the proxy should ignore it as well:
I see.
Indeed - in such a case I would probably go for changing the proxy like so:


void QGraphicsProxyWidget::mousePressEvent(QGraphicsSce neMouseEvent *event)
{
Q_D(QGraphicsProxyWidget);
#ifdef GRAPHICSPROXYWIDGET_DEBUG
qDebug() << "QGraphicsProxyWidget::mousePressEvent";
#endif
d->sendWidgetMouseEvent(event);

QGraphicsItem::mousePressEvent(event); //This should then allow the regular QGraphicsItem behaviour - I'd think... (specially as int the code you posted QGrphicsItem is not checking if the event is already accepted or not.

}


But I'm writing from work (currently, sadly not working on a Qt project), and have little time to think it through, I might be well missing stuff..
But if you can try it, I'd appreciate it if you shared the results! :)

wysota
16th March 2012, 12:30
Subclassing the proxy is definitely a good approach but it then prevents you from using QGraphicsScene::addWidget(). It's all just a matter of personal preferences.

Charvi
16th March 2012, 13:04
Hi jovin,

I am trying something very similar. But neither my resizing works nor the moving. When I click on the widget the widget takes the mouse press event. Could you post your code or upload a zip. It would be very kind of you.

Charvi.