How to add detachable areas (QDockWidget) to a QML application - c++

How do I add detachable areas to a QML ApplicationWindow?
Take the following application (Tiled) as an example. It has multiple detachable areas. In the first image all areas are attached, while in the second one area is in the process of being detached/reattached:
From C++ this can be realized with QDockWidget (see this question). But is there a solution with QML?

I made a simple working example. You can find it here. There must be enough comments for you to sort it out.
I used dynamic creation of objects like that:
dynamically create component(DetachableItem.qml) and assign it to property(not necessary, but it is easier to find it)
create connection for this component's attached property, where I can call some function when it changes;
move the item into another window pushing the object into it's data property
move it back the same way - pushing it to data property of main window and hiding separate window.
Feel free to ask question or proposing some improvements. I am interested in any suggestions how to improve it!
UPD: I updated the example with new commit where I got rid of dynamic objects creation. If you are still interested in dynamic object creation, you can checkout to this commit

Just an idea on how to achieve such behaviour. Have a look at the Window QML class and dynamic object creation to actually create a window by request.
Some (UNTESTED) pseudo-code, just to give an idea "DockWindow.qml":
import QtQuick 2.0
import QtQuick.Window 2.2
Rectangle {
id: dockWidget
property Window window: null
property Item embedIn: null
parent: window ? window : embedIn
readonly property bool detached: window
function detach() {
if (!window) {
window = Qt.createQmlObject('
import QtQuick.Window 2.2
Window { flags: …; }
', dockWidget, "dockWidget");
}
}
function close() {
if (window) {
window.close();
}
}
}
Note: This code will not work out of the box and probably leads to a dependency loop on the "parent" property!

As one possible solution you can create custom QDialog, use inside it QQuickView with desired qml stuff loaded from appropriate qml file. Communication with your main qml window and dialog will be done through Q_PROPERTY and Q_INVOKABLE defines in your custom dialog.
The pointer to your QDialog instance, for example, can be propagated to the QML as context property with help of QQmlContext::setContextProperty.

You can use KDDockWidget they have exported it to qml, its not LGPL though..

Related

Qt Quick / QML flag changes in C++ back to UI

I'm familiar with Qt from some time back, but I could use a little guidence with QML/QTQuick interactions with C++. I feel I'm missing something simple here, but am a little stuck.
I'm developing an embedded system displays the status of distributed switches communicating on a serial bus. The serial bus runs as a separate thread in C++ (has always been Qt) and automatically polls devices in a round robin fashion to get updates from the devices.
Looking into this, I found a pretty simple example using QAbstractList model to share the status via QML properties from the backed C++. https://www.youtube.com/watch?v=9BcAYDlpuT8&list=PLo12gBvZwC78nnrZHCBowKf36ZAi7iOLW&index=4&t=0s
Initially the model looked great, I simply loaded a list with the information I needed and can see it from the UI. The question is, how do I access the model from C++ to have updates bubble up to the UI when they change in the background.
What I've done so far:
Register model:
qmlReisterType<ModelDerrivedClass>("DeviceListModel",1,0,"DeviceList")
Define roles:
enum {
OpenRole = Qt::UserRole,
StatusRole
}
Define Hash table for model
QHash<int,QByteArray> ModelDerrivedClass::roleNames() const
{
QHash<int, QByteArray> names;
names[OpenRole] = "openstatus";
names[StatusRole] = "devicestatus";
return names;
}
Create simple list of structures with the proper information, implent then necessary methods, etc...works like a charm from top down.
How do I access the model from the bottom up? These devices will update status according to external input the UI doesn't need to be aware of, but these events need to be able to drive the front end. It almost looks like this scenario isn't accounted for.
I've also looked into registering a device type with QML, but can't figure out how to link a QML object to a C++ object so the the READ/WRITE/NOTIFY properties work with individual QML objects in a list. In this scenario, I would register OPEN and STATUS as properties of a QML type that could be used in the QML code directly, but I would need to associate the object instance int C++ with the QML object instance. This is something the QAbstractListModel appers to work around.
Any assistance would be appreciated.
You have two options, but I think the first is the better:
option 1: rootContext
You can set a property on the rootContext of a QQMlEngine, which will be available throughout every QML file loaded by that qml engine (see Qt docs):
QQuickView view;
view.rootContext()->setContextProperty("currentDateTime",
QDateTime::currentDateTime());
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
You should have an instance of the model:
ModelDerivedClass devices;
QQuickView view;
view.rootContext()->setContextProperty("devices", &devices);
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
You can now make changes to the ModelDerivedClass using setData or your own methods.
BTW, in this option you should not register as type, but as uncreatable type:
qmlRegisterUncreatableType<ModelDerrivedClass>("DeviceListModel",1,0,"DeviceList", "available as rootContext property 'devices'");
Option 2: singleton
You can make the ModelDerivedClass a Singleton, which is only available once. Add an instance function to the class:
class ModelDerivedClass
{
...
ModelDerivedClass* instance() {
static ModelDerivedClass theInstance;
return &theInstance;
}
...
}
From C++ you can use ModelDerivedClass::instance() to operate the changes.
You should also register it to QML as singleton:
qmlRegisterSingletonType<ModelDerivedClass>("DeviceListModel", 1, 0, "DeviceList",
[](QQmlEngine *eng, QJSEngine *js) -> QObject*
{
return ModelDerivedClass::instance();
});
But in this case you don't have much control over the lifetime of the object, which is arguably not good (lots of discussion can be found on this subject). So I would advice to take option 1.
The main thing to realize is that your main thread is a special thread that must be the only thread that accesses and changes anything about your GUI state. Most everything about Qt GUI and QML is single threaded.
Therefore, you'll want to communicate the change from your serial bus thread over to the main thread. This can be done connecting a slot/signal from the serial bus thread to an appropriate object on the main thread.
Qt knows when you've crossed thread boundaries with a connection like that and will post it in a way to the main thread that ensures it gets handled in a single-threaded fashion.

QT QML Items both in C++ and QML

My goal is to describe a qml item in a qml file and then use a corresponding C++ class to add to the visual representation of this item.
I know it is possible to describe a qml item and use it in qml file and the communicate with it from c++ through signal/slots.
I also know it is possible to describe a qml item in C++ using QQuickPaintedItem and use it in a qml file (making it available through the register mechanism). Are there other ways?
So, the actual question is it possible to combine both in one item?
So, I want to have a qml item and a c++ class painting the same area, e.g. I have specific OpenGL code in c++ and a usual Rectangle Frame with MouseArea defined in qml.
After the hint from Andrej I decided to realize my goal of having both the qml representation and a C++ rendering class by creating a wrapper class which derives from QQuickPaintedItem and by this overriding the paint method. With this I can render into the item in code. The item is used in my main qml file.
It is a wrapper class because it loads the qml file I want to show through a QQmlComponent which creates the QuickItem I want to show, too. I do this by setting the parent of the loaded/created item to my wrapper class.
So in my QuickPaintedItem class (best done in classbegin, when engine is already initialized):
QQmlComponent component(engine,QUrl("qrc:/myqml.qml"));
QObject* object = component.create();
QQuickItem* quickItem = qobject_cast<QQuickItem*>(object);
quickItem->setParentItem(this);
Then myqml.qml is rendered and my paint method, too. And I have a central place where I can use both.
After the comments from Velkan, another way is to put the loading of the component in a Loader qml item:
Wrapper { Loader{ onQmlChanged: source = newQml } }
where onQmlChanged would be a slot which consumes a signal:
signal onQmlChanged(string newQml);
I cannot say which way is better performance wise. Defining the structure in qml seems easier and cleaner. A difference to the code version, is that it loads the item at creation time of the Wrapper, so during the creation of the main.qml and before it is shown.

Repopulate window with new layout

I'm building a Qt (C++ with QML) app that I wish to deploy across
multiple platforms, including desktop and mobile.
I've run into a difficulty in that I haven't found any way to (possibly
simulate) replacing the contents of the current (and only) application
window on an action (say a button press).
Note that I don't want to create a new window, I simply wish to change
the widgets shown in the current window.
For reference, a QML stub for the main window is shown below
ApplicationWindow {
id: appWindow
---
---
toolBar: ToolBar { // Should be replaced on button click (eg: new)
---
---
}
ListView { // I need a way to replace this with a new sub-window
id: listView
model: myModel
---
---
}
}
You can use a StackView component as well as a Loader for that.
The former is a good way to let the user move within the pages that compose your app, the latter indeed does exactly what you asked for (replace the content of the window).
The documentation already contains good examples of use for both components.

Qt 5 - How to send data back from child dialog in real time

I have a settings dialog that has some settings that require another dialog to fully configure. My window shows a preview of the data as it's being tweaked by these settings. Upon clicking on the configuration button I launch another modal dialog with some knobs to twist to fine tune the particular setting.
I wish to send the result of the twisting of the knobs on the child dialog back to the parent dialog so that it can show the new preview data as the knobs on the child are being played with. The way I imagine it is I have a "refresh preview" function in the parent that is called after modification of it's data preview member variables. The question is, how do I do this? How can I access getters/setters from the parent dialog as a modal child dialog? If I do access them will the preview change or will it be blocked because of the modality of the child?
Thank you!
In Qt world, it is generally encouraged to exploit the Signal/Slot mechanism. In short, classes can send signals when something changes within that class. And slots can receive such signals provided we notified the receiving classes appropriately.
Let us look at how we can do it for our present case.
In our settings dialog constructor, we include this code (assumption is that you display the "another" dialog when a button is pressed):
Dialog *dialog = new Dialog();
connect(dialog->dial(), &QDial::valueChanged, this, &QSettingsDialog::changeTemp);
Code walkthrough:
Our QDialog has been constructed with a QDial object, dial. We access that member pointer with dialog->dial().
We tie the signal that emits the value changed on the dial to the slot called changeTemp that receives the value changed and sets the display widget on the settings dialog (parent) accordingly.
The changeTemp method might be like so:
void QSettingsDialog::changeTemp(int val)
{
lineEdit->setText(QString::number(val));
}
Notes:
You need to declare the Q_OBJECT macro on all classes that need to implement Signals and slot. In this case, both the settings dialog and the child dialog.
The above signal/slot signature is the new Qt5 signature. If you are on a version below 5.0, the signature is different. Refer to the docs.

Setting parent for a QMessageBox

i can't understand what's the benefit of setting parent for a QMessageBox, for instance in the following code:
void mainWindow::showMessage(QString msg) {
QMesageBox::information(this, "title", msg); //'this' is parent
}
can somebody help me ?
Probably a few things. First of all QMessageBox inherits from QDialog. Since QDialog has a concept of a parent, QMessageBox should too for consistency.
Specifically, the documentation says:
parent is passed to the QDialog constructor.
At the very least, a new dialog is often displayed centered in top of its parent.
However, there is more!
According to the documentation it can effect actually functionality. For example:
On Mac OS X, if you want your message box to appear as a Qt::Sheet of
its parent, set the message box's window modality to Qt::WindowModal
or use open(). Otherwise, the message box will be a standard dialog.
Also, there is a concept of both "Window Modality" and "Application Modality", where the former only prevents input in the parent window, the latter prevents input for the whole application. This obviously requires the concept of a parent to be known.
Finally, for certain static functions such as ::about(...), the first place it looks for an icon to use is parent->icon().
So, if you want to get nice platform specific behavior and have your code be cross platform, you are better off passing a sane parent to it.
The parent-child hierarchy of dialogs defines the window stacking behavior in the various platforms. If you pass dialog P as parent of dialog C, C will appear above P on all (desktop) platforms. If you pass 0, the window stacking will differ and usually not behave as wanted. The worst such issues I've seen on OS X, where some message boxes showed up behind the main window, which was disabled as the message boxes being modal, without any way to get to the message box (neither shortcuts nor moving windows via mouse helped).
In short, my suggestion: always pass a sensible parent.
The other answers are probably better, but my own little reason is that it puts the message box at the centre of the parent instead of the centre of the screen...
Don't forget to mention that QMessageBox will inherit of the palette and the style-sheets of its parent. Believe me when you use custom complex style-sheets you don't want you message to pop like they doesn't belong to your application ...
It is also useful for memory management if you don't use static functions, but actually create an instance of QMessageBox. When the parent is deleted your instance will be deleted too.