Is it possible to wrapper a QWidget as QT quick control? [duplicate] - qt

I am using Qt5 beta and trying to embed a QWidget-based object into QML. The goal is to use QML as much as possible, and only use QWidget objects where QML does not do what I need. I found a link explaining how to do this for Qt4.7, but I have not found any information explaining how to do this in Qt5.
http://doc.qt.digia.com/4.7/declarative-cppextensions-qwidgets.html
The same example is also available in the Qt5 examples folder under:
examples\qtquick1\declarative\cppextensions\qwidgets
Unfortunately, this example uses QtQuick 1, rather than QtQuick 2, and I would like to use the new features of Qt5. I actually want to embed a qwt widget, but as a first step I would be happy to embed any simple QWidget-based object.
Can anybody help me get the example working under Qt5 / QtQuick 2 ?

Qt Quick 2 uses a scene graph for efficient rendering on the GPU. Unfortunately this makes it impossible to embed classic widgets into the scene. The old approach to embed such widgets with the help of QGraphicsProxyWidget works only with Qt Quick 1, because internally it uses a QGraphicsView for all the heavy lifting and QGraphicsProxyWidget is meant to be used with it.
As of now there are no plans to enable embedding classic QWidgets into the scene graph I know of. I think this is rather unlikely to change, because the concepts of QPainter, the painting framework used for the classic widgets, and the new scene graph doesn't play well with each other.
There some efforts to develop new widgets sets specifically tailored for the needs of QML, but none of them are as powerful and mature as the classic widgets. The most prominent ones are the QML Quick Controls, bundled with Qt since version 5.1.
If you really depend on QWT my advice would be to stick with Qt Quick 1.1 for now. It's still bundled with Qt 5, probably for cases like yours. That way you won't take advantage of the new scene graph, though.

You can embed QWidget to QML by using QQuickPaintedItem class:
http://doc.qt.io/qt-5/qquickpainteditem.html
Qt5 has an example:
http://doc.qt.io/qt-5/qtquick-customitems-painteditem-example.html
You should implement an inherent of QQuickPaintedItem with private widget attribute, that you want to embed. Provide paint method, that just render the QtWidget and provide mouse and other event transmitting from inherit of QQuickPaintedItem to embed QtWidget.
There's also QSG (Qt scene graph API), but my experience with that thing wasn't smooth. I believe the clue in multithreading (performing rendering in the different thread (not the Qt GUI thread one, however on Windows that's not true and all is done in main GUI thread).
I've implemented embedding of QCustomPlot, here's link: github.com/mosolovsa/qmlplot

What could be done is to render the widget to an image and upload as texture.For interaction someone needs to forward events like mouseClick or keyPressed from the sceneGraph, translate to widget coordinates, pass on, render and upload texture again. Just an idea :)

The recommended approach is to stay with a QWidget based application and embed the QML parts using QWidget::createWindowContainer.

Further to Julien's answer - a simple way to achieve this is to use QQuickWidget to display the QML scene, and then add a regular QWidget as a child of the QQuickWidget. You can also add a simple intermediate QObject to anchor the QWidget to an item in the scene.
E.g.:
In main.qml:
Item {
... // layouts, extra items, what have you
Item
{
objectName: "layoutItem"
anchors.fill: parent
}
... // more layouts, extra items, etc.
}
widgetanchor.h:
class WidgetAnchor: public QObject
{
ptr<QWidget> _pWidget;
QPointer<QQuickItem> _pQuickItem;
public:
WidgetAnchor(QWidget* pWidget, QQuickItem* pItem)
: QObject(pWidget), _pWidget(pWidget), _pQuickItem(pItem)
{
connect(_pQuickItem, &QQuickItem::xChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::yChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::widthChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::heightChanged, this, &WidgetAnchor::updateGeometry);
updateGeometry();
}
private:
void updateGeometry()
{
if (_pQuickItem)
{
QRectF r = _pQuickItem->mapRectToItem(0, QRectF(_pQuickItem->x(), _pQuickItem->y(), _pQuickItem->width(), _pQuickItem->height()));
_pWidget->setGeometry(r.toRect());
}
}
};
In main.cpp:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
auto pqw = new QQuickWidget;
pqw->setSource(QUrl::fromLocalFile("main.qml"));
pqw->setResizeMode(QQuickWidget::SizeRootObjectToView);
pqw->setAttribute(Qt::WA_DeleteOnClose);
auto pOwt = new MyWidget(pqw);
if (auto pOverlayItem = pqw->rootObject()->findChild<QQuickItem*>("overlayItem"))
new WidgetAnchor(pOwt, pOverlayItem);
pqw->show();
return app.exec();
}
The documentation states that using QQuickWidget has advantages over QQuickView and QWidget::createWindowContainer, such as no restrictions on stacking order, but has a 'minor performance hit'.
Hope that helps.

Related

QML and QGraphicsScene [duplicate]

I am using Qt5 beta and trying to embed a QWidget-based object into QML. The goal is to use QML as much as possible, and only use QWidget objects where QML does not do what I need. I found a link explaining how to do this for Qt4.7, but I have not found any information explaining how to do this in Qt5.
http://doc.qt.digia.com/4.7/declarative-cppextensions-qwidgets.html
The same example is also available in the Qt5 examples folder under:
examples\qtquick1\declarative\cppextensions\qwidgets
Unfortunately, this example uses QtQuick 1, rather than QtQuick 2, and I would like to use the new features of Qt5. I actually want to embed a qwt widget, but as a first step I would be happy to embed any simple QWidget-based object.
Can anybody help me get the example working under Qt5 / QtQuick 2 ?
Qt Quick 2 uses a scene graph for efficient rendering on the GPU. Unfortunately this makes it impossible to embed classic widgets into the scene. The old approach to embed such widgets with the help of QGraphicsProxyWidget works only with Qt Quick 1, because internally it uses a QGraphicsView for all the heavy lifting and QGraphicsProxyWidget is meant to be used with it.
As of now there are no plans to enable embedding classic QWidgets into the scene graph I know of. I think this is rather unlikely to change, because the concepts of QPainter, the painting framework used for the classic widgets, and the new scene graph doesn't play well with each other.
There some efforts to develop new widgets sets specifically tailored for the needs of QML, but none of them are as powerful and mature as the classic widgets. The most prominent ones are the QML Quick Controls, bundled with Qt since version 5.1.
If you really depend on QWT my advice would be to stick with Qt Quick 1.1 for now. It's still bundled with Qt 5, probably for cases like yours. That way you won't take advantage of the new scene graph, though.
You can embed QWidget to QML by using QQuickPaintedItem class:
http://doc.qt.io/qt-5/qquickpainteditem.html
Qt5 has an example:
http://doc.qt.io/qt-5/qtquick-customitems-painteditem-example.html
You should implement an inherent of QQuickPaintedItem with private widget attribute, that you want to embed. Provide paint method, that just render the QtWidget and provide mouse and other event transmitting from inherit of QQuickPaintedItem to embed QtWidget.
There's also QSG (Qt scene graph API), but my experience with that thing wasn't smooth. I believe the clue in multithreading (performing rendering in the different thread (not the Qt GUI thread one, however on Windows that's not true and all is done in main GUI thread).
I've implemented embedding of QCustomPlot, here's link: github.com/mosolovsa/qmlplot
What could be done is to render the widget to an image and upload as texture.For interaction someone needs to forward events like mouseClick or keyPressed from the sceneGraph, translate to widget coordinates, pass on, render and upload texture again. Just an idea :)
The recommended approach is to stay with a QWidget based application and embed the QML parts using QWidget::createWindowContainer.
Further to Julien's answer - a simple way to achieve this is to use QQuickWidget to display the QML scene, and then add a regular QWidget as a child of the QQuickWidget. You can also add a simple intermediate QObject to anchor the QWidget to an item in the scene.
E.g.:
In main.qml:
Item {
... // layouts, extra items, what have you
Item
{
objectName: "layoutItem"
anchors.fill: parent
}
... // more layouts, extra items, etc.
}
widgetanchor.h:
class WidgetAnchor: public QObject
{
ptr<QWidget> _pWidget;
QPointer<QQuickItem> _pQuickItem;
public:
WidgetAnchor(QWidget* pWidget, QQuickItem* pItem)
: QObject(pWidget), _pWidget(pWidget), _pQuickItem(pItem)
{
connect(_pQuickItem, &QQuickItem::xChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::yChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::widthChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::heightChanged, this, &WidgetAnchor::updateGeometry);
updateGeometry();
}
private:
void updateGeometry()
{
if (_pQuickItem)
{
QRectF r = _pQuickItem->mapRectToItem(0, QRectF(_pQuickItem->x(), _pQuickItem->y(), _pQuickItem->width(), _pQuickItem->height()));
_pWidget->setGeometry(r.toRect());
}
}
};
In main.cpp:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
auto pqw = new QQuickWidget;
pqw->setSource(QUrl::fromLocalFile("main.qml"));
pqw->setResizeMode(QQuickWidget::SizeRootObjectToView);
pqw->setAttribute(Qt::WA_DeleteOnClose);
auto pOwt = new MyWidget(pqw);
if (auto pOverlayItem = pqw->rootObject()->findChild<QQuickItem*>("overlayItem"))
new WidgetAnchor(pOwt, pOverlayItem);
pqw->show();
return app.exec();
}
The documentation states that using QQuickWidget has advantages over QQuickView and QWidget::createWindowContainer, such as no restrictions on stacking order, but has a 'minor performance hit'.
Hope that helps.

QGLWidget for graphics and QT for GUI

I am trying to implement a custom OpenGL scene (wrapped in a QGLWidget), but still use QT controls (such as slide bars and buttons). I tried to make my main widget a QGLWidget, which has QWidgets as children. The child widgets seem to work (when I click a pulldown menu, its options appear), but the widget fails to draw (I see a white square). I changed the base class of my children widgets from QWidget to QGLWidget, and now QPainter calls work, but QT GUI stuff is still not displaying...
Some claim that a QGLWidget can not have QWidgets as children... I am not sure about that and I'm not willing to give up. By the way, I am using Visualization Library to draw the OpenGL scene, but it is just an OpenGL wrapper...
Any clues where the problem might be? Also, key events stop being processed for some reason when I add subwidgets to the GQLWidget.
Update:
I tried various combinations of widgets and layouts. It seems that the QGLWidget just gets drawn on top of anything. I even tried with raise() to arrange Z-depth of the widgets, to no avail. Is overlayGL() the only way to draw on top of an OpenGL widget?
Update 2
After months of trying, I figured out it is something related to QT. Whenever a QGLWidget is drawn on top of another QGLWidget, the first's background() function is not called. So a button is there and can be clicked, but it's not drawn. My workaround was to draw everything myself using QPainter - that works. What I found curious is, one has to use QPainter::startNativePainting() in order to draw with it. One would expect that they would overload start() in QPainter to call startNativePainting() whenever a QGLWidget is the painting device...
Another (maybe useful) fact is that QPainter calls CHANGE the Opengl context. So if you have another tool in cooperation with QPainter (in my case Visualization Library), chances are that one of them will crash (in my case VL which checks if the context state has been cleared before each frame).
There is a labs example of rendering Qt widgets using opengl it's a little old and I haven't tried it with the improved qglwidget in 4.8
There is also an issue with drawing over the top of a fullscreen QGLwidget - it was supposedly a bug in NVidia's openGL driver. It doesn't seem to have been resolved
https://bugreports.qt-project.org/browse/QTBUG-7556
If I were you I would use the QGLWidget as a viewport for a QGraphicsScene, then you can draw your widgets using a QGraphicsProxyWidget. That way you get a proper OpenGL viewport with the ability to put widgets in and manipulate them easily.
Did u try to call update() explicitly? I find that sometimes QGLWidget update the display stuff kind of slow.
This is an old question, but I recently had quite a bit of trouble with this, and this is what helped me -
Use QML/QT Quick. QML is a javascript-like language that allows you to layout your widget elements on a window and do basic processing on events like mouse clicks. There are a few other cool features, but the one that is most relevant to this question is that you can import qt widgets from C++ code.
So you can make a custom QGLWidget, import it into the QML window, and set the size/position/stick widgets on top of it in whatever you want, very easily. QT's Scenegraph example does a great job explaining this. Scenegraph's code is also included as an example in at least Qt Creator 3.2.1 Open Source (that's the version I have anyway)

OpenGL rendering to a QML item

I have a QML file which contains a layout of QML items and now I want one of those items to be a QGLWidget. i.e. I want to render to a specific QML item.
Is anyone aware of how to do this?
The simplest way I suppose it to provide QML a new custom component implemented in C++. I couldn't find anything ready.
You could subclass the QDeclarativeItem and implement your OpenGL code in the paint function after using the QPainter::beginNative() function. After that you can "export" your new custom item to QML this way. This is quite simple and should work, but you'll have to setup the viewport of you QDeclarativeView to be a QGLWidget, something like this:
QDeclarativeView view;
// This is needed because OpenGL viewport doesn't support partial updates.
view.setViewportUpdateMode(QGraphicsView::FullViewportUpdateMode);
view.setViewport(new QGLWidget);
or you'll have to use the opengl graphics system for the entire application.
Another way is using QML/3D.
This thread will give you some other information.

Does it matter if i start a qt base as QMainWindow or as a QDialog?

if i need multiple dialogs for my application. QmainWindow is just for layout of multiple dialogs?
QMainWindow is still a single window but it provides facilities for advanced GUI programming.
If you need to pop up multiple dialogs first read Modeless Dialogs section of qt docs.
If basically says that create your dialogs on the heap and use show() method.
Something like below (untested code). This should show two dialogs at the same time.
int main( int argc, char ** argv )
{
QApplication app;
Mydialog1 dlg1 = new Mydialog1();
dlg1->show ();
Mydialog1 dlg2 = new Mydialog2();
dlg2->show ();
a.exec();
}
Yes, a QMainWindow provides a the base window for a regular GUI application. A regular GUI application is thought of as having "Menus", "Toolbars", "Status bar"
AFAIK, a QDialog does not provide any of the above. if your application doesn't require any menus, toolbars etc... then you can simply use QDialogs as you said. But I'd strongly recommend using a QMainWindow if your application has multiple widgets. If you can explain what you are trying to achieve then maybe we can help you with better alternatives.

Is it possible to declare a QWidget as a child of a window created outside of Qt?

I'd like to use Qt in my browser plugin, but I don't get to create my own window, the browser does.
What I'd like to do is create a QWidget as a child of a native window handle... Is this possible?
You may be able to take over a native window handle by calling QWidget::create() in your custom widget's constructor. Note that it's a protected method so you can't call it on a normal QWidget.
If you're using a dialog and not an embedded window it's relatively easy. Call QWidget::winId() on your top-level widget. In Qt4 this will return a WId, which is a preprocessor definition for HWND. In Qt5, WId is a typedef for HWND, so you have to perform an explicit cast:
HWND hwnd = (HWND)widget->winId()
Unfortunately, the functionality in Qt5 is currently unreliable/partially broken. As of Qt 5.4.1, it's not resolved and there's a note in the source that QWidget::winId() is "going away". note that this issue primarily affects embedded native windows in a Qt app, not vice versa. Your mileage may be better.
Note: QWidget::create() is intended for embedding native windows in Qt. It probably does not apply in your situation.
Only if you can cast it as a QWidget.

Resources