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.
Related
I have an abstract class representing data objects. Each object represents one data item. The derived classes implement the function updateValue() that gets called periodically in a thread to compute new values. All these classes are from a non-Qt C++ library.
Something like:
class DataObject
{
double value;
virtual void updateValue() = 0;
};
At present, I have several types of QWdiget based display widgets, each widget tied to a DataObject. Something like:
class DisplayWidget
{
DataObject *dataObject;
virtual void updateDisplay() = 0;
};
The widgets could be simple QLabel boxes, Qwt-based dials, gauges etc. The application has multiple pages, each page containing several of such widgets. At every 100ms or so (QTimer tick), I iterate through all widgets in the current page and call updateDisplay() for each of them to update their display.
NOW: I want to have some shiny Qt Quick based widgets/pages, that I can generate using qml files. I can use a QQuickWindow container widget on a page, whose contents are loaded from a specified qml file at runtime.
My question is how do I efficiently export/map my C++ DataObject objects to corresponding QtQuick widgets so that I can periodically update the display?
Since DataObject class and its children are from a non-Qt library, I cannot create properties and notifier signals in those classes
I can of course create wrapper class object for each object that needs to be displayed in QtQuick window. This wrapper class will need to be derived from QObject and have properties and notifier signals. Then periodically on display timer tick, I'll need to emit the notifier signal (may be after checking if the corresponding data object's value has actually changed since the last update).
When the QML file is loaded, I'll need a way to dynamically create the wrapper objects for each DataObject that the file needs.
Is this the most efficient and elegant way to do this? I may need about a hundred display widgets on a page.
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..
QML has a type called 'WebView', this thing is also known as 'QQuickWebView'. But 'QQuickWebView' is not compatible with 'QWebView' (which is a widget component, not QML type).
I want to access and modify 'WebView' in C++ but the closest class that I can cast for WebView is 'QQuickItem'.
Any solution to make QWebView embeddable in QML?
In QML, use "import QtWebKit.experimental 1.0"
Reference:
http://rschroll.github.io/beru/2013/08/21/qtwebview.experimental.html
I have created a traditional Qt (widget based) GUI, something like this: MainWindow::MainWindow(parent) : QMainWindow(parent)
This is designed by Qt Creator as forms (mainwindow.ui), aka Design Mode. Everything works fine. But the GUI code with all widgets, initializing the corresponding models, and functionality gets quit long. I'd like to refactor to small units. Things I came up with:
I tried using specialized (derived) widgets. Example: A created MyTableView::QTableView contains the specialized model, as well the signal/slot handling between model and widget. This reduces the amount of code in MainWindow. However, I do loose the capability to design the GUI via Qt Creator's Design mode.
The best thing I came up with so far, was to spilt the source code (multiple cpp files). It still represents one class, but less code in one file.
So, how could I better partition my GUI class?
If you still want to uncouple the initialization of widgets by derived widgets, you can use "promote to ..." option in Qt designer. Steps:
class MyTableView : public QTableView {}, and so initialization of table view is moved to the constructor of MyTableView.
In Qt Designer, open the ui form (MainWidow.ui), and drag and drop a QTableView on it;
Right mouse click the QTableView, in prompt menu, there's a "promote to" option, open it
In the dialog of "promoting widget", specify your custom QTableView's class name and header file, say MyTableView, MyTableView.h. This step requires existing custom class and header file.
Borrowed a picture:
You could create your own Qt widgets and register them with QtDesigner. Then will you be able to use them on forms as mere QLabels and friends. See this link
In a recent project, we had pretty restrictive uncoupling requirements (especially not to be too strongly linked to Qt). What we used to do based on MVC-like pattern is:
Implement a controller that controls the application workflow
Add a GUI "adapter" class per screen that communicates with the controller. Let's say HomeScreen class, SecondScreen class
Each adapter class contains a given number of widgets: TimelineWidget, FormWidget
Each widget is composed of a ui member (Ui::TimelineWidget ui) that is generated from a .ui file designd with Qt designer
Note that this structure might not be suitable for small projects.
I am creating a GUI to manipulate a robot arm. The location of the arm can be described by 6 floats (describing the positions of the various arm joints.
The interface consists of a QGraphicsView with a diagram of the arm (which can be clicked to change the arm position - adjusting the 6 floats). The interface also has 6 lineEdit boxes, to also adjust those values separately.
When the graphics view is clicked, and when the line edit boxes are changed, I'd like the line edit boxes / graphics view to stay in synchronisation.
This brings me to confusion about how to store the 6 floats, and trigger events when they're updated. My current idea is this:
The robot arm's location should be represented by a class, RobotArmLocation. Objects of this class then have methods such as obj.ShoulderRotation() and obj.SetShoulderRotation().
The MainWindow has a single instance of RobotArmLocation.
Next is the bit I'm more confused about, how to join everything up. I am thinking:
The MainWindow has a ArmLocationChanged slot. This is signalled whenever the location object is changed.
The diagram class will have a SetRobotArmLocation(RobotArmLocation &loc). When the diagram is changed, it's free to change the location object, and fire a signal to the ArmLocationChanged slot.
Likewise, changing any of the text boxes will fire a signal to that ArmLocationChanged slot. The slot then has code to synchronise all the elements.
This kind of seems like a mess to me, does anyone have any other suggestions? I've also thought of the following, does it have any merrit?
The RobotArmLocation class has a ValueChanged slot, the diagram and textboxes can use that directly, and bypass the MainWindow directly (seems cleaner?)
thanks for any wisdom!
Except for really simple cases (e.g. a label that shows the value of a slider) my experience has been to stay away from inter-component signal/slot connections inside Qt Designer. Rather, have the component firing the signal of interest connect to a slog defined in the top level QWidget class you're subclassing (i.e. QMainWidow, etc... let's just call it the Form class). You can also go the other way: if you have a custom signal in the Form class, you can connect it with Qt Designer to one of the public widget slots.
To be specific using your example:
I'm going to assume that you have subclassed QMainWindow and QGraphicsView. Let's call the subclasses RobotMainWindow and RobotView.
RobotMainWindow contains the QLineEdit fields and RobotView. The way you specify RobotView in Qt Designer is to insert a QWidget and use the Promote to... feature to tell Qt that the QWidget should be replaced at compile time with your custom RobotView.
Name the QLineEdit fields edit1, edit2...edit6.
In the Qt Designer signal/slot editor define slots in RobotMainWindow to be called when the value in a QLineEdit changes. There are some more elegant ways of doing this, but for simplicity let's say you define 6 slots named setValue1(float), setValue2(float), etc.
In the source code for RobotMainWindow, go declare and define these slots, such that they update your arm, shoulder, whatever.
Also in the source code, define a signal valueChanged() (or multiple for each field, your choice). Have the slots you defined emit valueChanged().
Back in Qt Designer you can now link the appropriate signal from each QLineEdit to the corresponding slot in RobotMainWindow.
Back in the signal/slot editor add the valueChanged() signal to the RobotMainWindow (so Qt Designer knows about it). Connect this signal to a new slot in RobotView, using the procedure above, so it can update the rendering.
Repeat all of this for handling changes from RobotView to the editing fields (via RobotMainWindow.
In short, I think you'll find everything more straight-forward if your route the signals through your Form subclass (which I think of as the Controller in MVC parlance).