Focus/Activation/Update problem for widget in nested QGraphicsScenes
Hi,
(Using Qt 4.8.1)
I've have an editable QComboBox which I put into a QGraphicsScene. The graphics scene gets visualised by a QGraphicsView which itself gets added to another QGraphicsScene. This second graphics scene gets again visualised by a QGraphicsView. (see code below)
I've got a few issues with this configuration:
I have to set the cache mode to ItemCoordinateCache or DeviceCoordinateCache in order to see the drop down list of the combo box. Not a major issue so far, but why is this necessary?
When hoovering with the mouse over the edit field of the combo box I would expect the mouse cursor to change into the caret symbol, this does not happen.
When clicking once into the edit field the text caret does not appear, typing however changes the text, so the edit field 'somehow' gets the keyboard events.
When using a QOpenGL widget as viewport then it seems like the inner scene does not update properly.
Has anyone any idea what I have to do so that the combo box behaves as if it would be added directly into the outer scene? Has anyone ever tried something like this successfully? Any idea or suggestion is welcome and appreciated.
Following the sourcecode which shows the described behaviour. This is just a simplified example of what I want to do. Later I want to use all kind of other widgets and dialogs for where I use the combo box at the moment.
@
#include <QApplication>
#include <QComboBox>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsProxyWidget>
#include <QGLWidget>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QComboBox* combo = new QComboBox();
combo->addItem("First");
combo->addItem("Second");
combo->setEditable(true);
combo->setObjectName("the combo box");
// Create the outer scene and view, it may contain several 'inner' graphics
// views and scenes.
QGraphicsScene* outerScene = new QGraphicsScene();
outerScene->setObjectName("the outer scene");
QGraphicsView* outerView = new QGraphicsView(outerScene);
outerView->setObjectName("the outer view");
// The outer graphics view is our 'main window'
outerView->show();
// if set to 0 then the combo box gets added directly into the outer scene,
// if set to 1 then the combo box gets added into a graphics scene which
// gets visualised by a graphics view added to the outer scene.
if (0)
{
// In this scenario we add the combo box into the outer scene.
outerScene->addWidget(combo);
}
else
{
// In the second scenario we add another QGraphicsScene and QGraphicsView,
// the inner scene and inner view. The inner view gets added to the outer
// scene using a QProxyWidget. The combo box now gets added to this inner
// scene.
// Adding the combo box to its 'own' scene allows us to do all kind of
// funny things like scaling, rotation, etc.
QGraphicsScene* innerScene = new QGraphicsScene(); innerScene->setObjectName("the inner scene"); QGraphicsView* innerView = new QGraphicsView(innerScene); innerView->setObjectName("the inner view"); // If set to 1 then an OpenGL viewport gets set onto the inner scene if (0) { innerView->setViewport(new QGLWidget); } QGraphicsProxyWidget* innerViewProxy = outerScene->addWidget(innerView); innerViewProxy->setObjectName("the inner view proxy"); innerViewProxy->setFlag(QGraphicsItem::ItemIsPanel, true); innerViewProxy->setFlag(QGraphicsItem::ItemIsFocusable, true); innerViewProxy->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); QGraphicsProxyWidget* proxy = innerScene->addWidget(combo); proxy->setObjectName("the combo proxy"); proxy->setFlag(QGraphicsItem::ItemIsPanel, true); proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); proxy->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); // Seems like we have to set the cache mode to Item or DeviceCorrdinateCaching // in order to see the drop down list. proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache); //proxy->setCacheMode(QGraphicsItem::DeviceCoordinateCache); innerScene->setActiveWindow(proxy); // Turn the combo box upside down and scale it a bit. It gets rotated and // scaled independent of objects in the outer scene, Nice! innerView->scale(2.0, 2.0); innerView->rotate(180.0);
}
return a.exec();
}
@
Here the project file:
@
QT += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = EmbeddedGraphicsViewTest
TEMPLATE = app
SOURCES += main.cpp
HEADERS +=
FORMS +=
@
Thank You!
Peer
edited: added a comment about the used Qt version
- Asperamanca
To me, this sounds like a case of "not everything that can be done should be done". Nested graphic scenes?
What are you trying to accomplish? I think it's likely you can accomplish it with a simpler structure, and at least some of your problems would go away in the process.
Well Asperamanca, you are right, not everything that can be done should be done. However, in this case I do it for a special purpose, actually for two:
I want to display UI items in an outer scene. We talk here about mostly custom QGraphicsItems or Objects deriving from QWidget (up to comples UI with lots of widgets and panels). So far no problem, all that is properly supported and works fine. However, due to the potentially large number of objects I want to use a mechanism where I put some of the items into a second 'inner scene' which is part of the outer scene. The inner scene is display scaled down so that the items appearing in the inner scene are kind of minimized but still show their content as if they would be in the outer scene. No mouse interaction necessary so far. Works fairly good.
Some of the Ui which I want to display in the outer scene shall render using OpenGL. Because I need several of them in parallel I need to have several scenes with QGraphicsViews using a QGLWidget as viewport. I also need to add this UI elements into the outer scene. And here I also need some user interaction and therefore, events etc. should work.
If you, or someone else, has a better way of doing what I need/want, then I would love to hear about it ;)
Thanks for your reply!
Peer
Ok I learned something new in the meantime. I did not knew that I can apply transformations on proxies directly.
So there is only one scenarion left where I think I have to use nested scenes. This is when I want to display several OpenGL items in one scene at the same time. Anybody ever tried this?
- Asperamanca
If your "outer scene" is basically just widgets and regular UI, why do it in GraphicsView in the first place? Are the custom GraphicsItems just Widgets you happened to write based on GraphicsItem? Or are they truly something you wouldn't do as a QWidget?
If there is a way to cleanly separate your UI between a QWidget and a QGraphicsView part, I advise to do it.
The outer scene consists of widgets, regular UI, custom QGraphicsItems and the OpenGL displays I am talking about. I want to be able to move the items around on the scene, I want to scale (maybe later rotate) them, I want to group them and scale them as a group, I want to dock them together, etc. . For this functionality the QGraphicsScene framework is ideal; if I would not have to display some OpenGL displays I would not have any problems ;)
I know that I have to find another solution If I cannot get this done, however before I did not dig deep on it I do not want to give up so easily. Thanks!
@joesteeve: My solution is to use just one scene and no OpenGL.
Embedding other QGraphicViews into a QGraphicScene seems not to be supported, and because this would have to work for me in order to use OpenGL I do not use it. I would have used OpenGL just for displaying image data, however I was able to achieve a good enough performance by using Pixmaps, so I am fine.