Display QQmlComponent in QQuickWidget - qt

According to QQuickWidget's documentation:
you can instantiate your own objects using QQmlComponent and place
them in a manually set up QQuickWidget.
But I can't find any example of that. I would like to have multiple QQmlComponents loaded into RAM and display them in QQuickWidget depending on which one is active. Any idea on how to display any content in QQuickWidget except for setSource()?

I've end up with following solution: create new QQuickWidget widget and use its QQuickWidget::setContent() to display already created QML content in it. It works in my Qt 5.9.
Note: setContent() is marked as internal and have some drawbacks although this API is public and available in public header.
First of all, QQuickWidget doesn't clear its content when QQuickWidget::setContent() is consequently called for different data. So old and new content overlaps. That's why I have to create a new QQuickWidget on every content change and replace old QQuickWidget with new fresh one.
Secondly, QQuickWidget thinks it owns pointers passed via QQuickWidget::setContent() and tries to delete content at destruction. To bypass this you may execute QQuickWidget::setContent(QUrl(), nullptr, nullptr) before QQuickWidget instance is destroyed. But this triggers a warning message from QML engine in stdout about incorrect qml content. So better approach is to set dummy data:
QQmlComponent* dummy = new QQmlComponent(engine);
dummy->setData(QByteArray("import QtQuick 2.0; Item { }"), QUrl());
wgt->setContent(dummy->url(), dummy, dummy->create());
wgt->deleteLater();
With these hacks I was able to load multiple QML objects (plugins with own UI) with QQmlComponent at runtime. Instantiate them and display one of them in QWidgets-based application depending on plugin selected.

Related

What's the purpose of auto added QLayout with objectName '_layout'

I was exploring Qt layouts and widget management and have come across the behaviour I can't explain.
I have a base UI with the following hierarchy:
MainWindow(QMainWindow) ->
centralwidget (QWidget),
menubar(QMenuBar),
statusbar(QStatusBar)
Later I add several layouts and widgets to the centralwidget, but, I beleive, it's not important in this context.
Now, if we check MainWindow's children objects and their objectNames we will see the following:
<PyQt5.QtWidgets.QLayout object at 0x00000000089425E8> _layout
<PyQt5.QtWidgets.QWidget object at 0x00000000087C6F78> centralwidget
<PyQt5.QtWidgets.QMenuBar object at 0x0000000008942048> menubar
<PyQt5.QtWidgets.QStatusBar object at 0x00000000089420D8> statusbar
For some reason, a QLayout object _layout appears. But this is not all. It seems to me that this QLayout is not persistent and, occasionally, is being added and removed, for example on resize. I have added an eventFilter for a resize event, and here are the object addresses after several window resizes:
<PyQt5.QtWidgets.QLayout object at 0x0000000008942708> _layout
<PyQt5.QtWidgets.QLayout object at 0x00000000089425E8> _layout
<PyQt5.QtWidgets.QLayout object at 0x0000000008942798> _layout
<PyQt5.QtWidgets.QLayout object at 0x00000000089428B8> _layout
The layout is there, but every time the object is new.
What's going on here? What's the purpose of this QLayout, and why it behaves this way?
The QMainWindow class has a built-in custom layout that manages all the toolbars, menubars, dock-widgets, statusbar and central-widget area:
This layout cannot be removed, or replaced by a different layout. If you try to do that, Qt will complain (or possibly even crash).
The object id shown in the repr doesn't mean much. This is the id of the PyQt wrapper for the layout and not the memory address of the underlying C++ object. The layout is actually a QMainWindowLayout - but that is not a public type, so PyQt can only return a QLayout (which is its base-class). It is likely that PyQt is returning a new wrapper around the same underlying C++ object every time you access it.

Right way to dynamically create qml windows

I'm working on a Qt application and I need to create windows dynamically. Each window consists of QObject-based c++ backend and qml-based interface. Each window needs to be connected to the bunch of signals emitted by core classes.
Current solution is to derive window from QQuickView, connect signals to it and load qml using setSource(). Is it a right way or there is a better way?
Is it better to use one QQmlEngine for all windows (and use this engine as parent for every window) or create new engine for each new window?
For this I would expose a c++ model to the QML code.
Since this model would be dynamic (elements can be added or removed), I'd use a QAbstractItemModel derived model that can inform the views that some elements are added/removed. Using something else like a QList<QObject*> would mean that you'd have to tell the view that the entire model should be reloaded after each change.
Instead of implementing the model from scratch, you could use a class like QQmlObjectListModel from Qt QML Tricks, it exposes a QList-like API from c++ but is a QAbstractItemModel exposing the QObject properties as roles under the scene.
Another solution that you could use if you don't want to use QObjects is benlau's QSyncable (I've actually used this in a similar situation to yours, where I expose my screens in a model and instantiate a Window displaying a taskbar for each).
Then, I'd use a QQmlApplicationEngine and expose the model to it with setContextProperty. A QQuickView is already a window, so I don't think you want to use that, better to manage your windows manually in the QML code.
Then in your QML code, use an Instantiator as your root object, set your model, and use Window as its delegate :
Instantiator {
model: yourModel
Window {
/* ... */
}
}

How to make app looks nice in Qt?

I want to know is it possible to make application fully skinned/styled in Qt I mean by that not only controls inside the application window but the mainwindow itself! like close button/maximize button/minimize button and the bar, and the mainwindow border!, is it possible to do that in Qt? and how to?
Yes it is possible. The best method in Qt is to use Qt style sheets. The Qt documentation has plenty of examples and you can style all the widgets, either by specifying a whole class such as QPushButton, or a single, named widget.
As for the items on the title bar, I'm not sure if that's possible directly, but you could certainly turn off the main tool bar that contains the minimise / maximise buttons and then implement and style your own widgets while replicating their functionality.
The second argument to the QWidget constructor is Qt::WindowFlags. You can use the flags to control the properties of a window. For example, pass the flag Qt::FramelessWindowHint to create a borderless window.
There are a few ways to do this in code. You can use the setWindowsFlag method from within your widgets constructor:
setWindowFlags(Qt::FramelessWindowHint);
If you are creating a custom widget, you can pass the flag to QWidget's constructor:
YourWidget::YourWidget(QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint)
{
// ....
}
Or you can pass the flag when you create a widget:
QWidget *your_widget = new QWidget(parent, Qt::FramelessWindowHint);
There are also flags for the minimize button (Qt::WindowMinimizeButtonHint), maximize button (Qt::WindowMaximizeButtonHint), close button (Qt::WindowCloseButtonHint), and title bar (Qt::WindowTitleHint). With these flags, you must also set Qt::CustomizeWindowHint to disable the defaults as described in the documentation.
See the flags documentation for a full list and additional details.
As #Merlin069 stated, style sheets allow you to control the look and feel of the widgets within your application.

Qt Designer dock widgets children acess

I made a form using the Qt Designer which has some dockwidgets, these dockwidgets have some children widgets. How I can access the dockwidget and these child widgets in my mainwindow.cpp?
I highly recommend reading the docs for these kinds of things, but to give you a little head start, QDockWidget inherits from QWidget, which inherits from QObject:
https://doc.qt.io/qt-4.8/qobject.html#children
widget->children() would simply tell you the children of this widget. This would be needed if you didn't already know the names of the objects to be accessed directly, or had no reference to them.
Update
When you create objects in Qt Designer, and you run the setupUi(this) that is generated for you, inside of your MainWindow, you will then have access to all of the widgets you had set up as members. You can access them directly as they were named in Qt Designer. Please check out one of the numerous tutorials on getting started with Qt. Here is one that shows you how to make use of your ui file, and access the members from it: http://sector.ynet.sk/qt4-tutorial/my-first-qt-gui-application.html
You can also get a list of all the dockWidgets from the mainwindow with
QList<QDockWidget *> dockWidgets = findChildren<QDockWidget *>();
A similar technique works for getting toolbars etc. so you don't have to manually store a list as you create them

Qt App Ui Multi-Language Support:Change Images Accordingly

I use UI Designer to set up the form layout. And I want to change the button background image to another one when language setting is changed in the system setting of the phone. How can I do this? I know how to support multi-language of the text , but I dont know how to support mult-language of the image. Thanks
When the language is changed, QCoreApplication::installTranslator() will be called. From the documentation:
Installing or removing a QTranslator,
or changing an installed QTranslator
generates a LanguageChange event for
the QCoreApplication instance. A
QApplication instance will propagate
the event to all toplevel windows,
where a reimplementation of
changeEvent can re-translate the user
interface by passing user-visible
strings via the tr() function to the
respective property setters.
User-interface classes generated by Qt
Designer provide a retranslateUi()
function that can be called.
So you should reimplement QWidget::changeEvent() in your toplevel window and change the image there if the type() of the event is LanguageChange.

Resources