binding two elements from two different qml files - qt

I am trying to bind two dial elements such that if the dial1 value is changed, the same value will be reflected in dial2 which is in another qml.
QML is new to me and I don't know much of the aspect. But I want to use pure QML on this and no C, C++ for these thing.
//file1.qml
dial{
id: dial1
}
//file2.qml
dial{
id: dial2
}
Secondly, I want to have two way binding for same scenario to happen.

At some place you will instantiate both files. Let's assume it is a TabView. This is the point where you will make the connection between the two files by adding a property to the TabView that will hold the shared value.
The pro of having the sharedValue is, that the time where the two files will be instantiated can be different, so if you destroy the instance of file1 and create the instance of file2 a couple of minutes later, you can still have the value.
TabView {
property int sharedValue
File1 {
id: file1
}
File2 {
id: file2
}
}
Then you read this on bidirectional bindings and bind the two exposed values of the dials to the shared value. You can alternatively omit the property sharedValue and install the bidirectional binding between both exposed values directly.
What is necessary, of course, is that you expose the value of the dial, so your files would need to look
Tab {
property alias dialValue: dial.value // now you can access the dial's value via this new property.
CustomDial {
id: dial
}
}
As long as you don't alter the values in the binding, I reccommend to use two Binding-Object to install the bidirectional binding.

Related

What's the rightway to use attached properties,signals?

I found some code use Attached Properties, in a strange way.
case 1
Flickable {
// ...
ScrollIndicator.vertical: ScrollIndicator { }
}
case 2
ListView {
model: 3
delegate: Rectangle {
color: ListView.isCurrentItem ? "red" : "yellow"
}
}
case 3 or case 4...n
Item {
Component.onCompleted: {
console.log("hello")
}
}
I am confused on Attached Signals,Properties, don't know what's time Attached Signals,Properties can use(call) somewhere in my code and don't knonw what it real means and real usage?
The docs is very clear:
Attached properties and attached signal handlers are mechanisms that
enable objects to be annotated with extra properties or signal
handlers that are otherwise unavailable to the object. In particular,
they allow objects to access properties or signals that are
specifically relevant to the individual object.
A QML type implementation may choose to create an attaching type in
C++ with particular properties and signals. Instances of this type can
then be created and attached to specific objects at run time, allowing
those objects to access the properties and signals of the attaching
type. These are accessed by prefixing the properties and respective
signal handlers with the name of the attaching type.
References to attached properties and handlers take the following
syntax form:
<AttachingType>.<propertyName>
<AttachingType>.on<SignalName>
In simple words, it allows adding new functionalities without modifying the base class.
In the first example it is observed that a ScrollIndicator is added (which is not necessarily part of the Flickable) without modifying the behavior of the Flickable.
In the second case, the "ListView" property that is not the ListView object is added to each delegate so that it can access information about the view. It should be remembered that the delegate has its own scope that is different from the view, for example you cannot access the delegate's properties outside of it.
And in the third case, the "Completed" object is added, which has the completed signal that is emitted when the base object(Item) finishes building.

Is there a way to detect if a QML property is bound (not set to a static value)?

Is it possible to tell the difference between a property that is set to a static value vs. a property that is bound to something else? I checked documentation, but don't see anything about this.
Rectangle {
id: firstRect
color: "black" // set to static value
}
Rectangle {
color: firstRect.color // bound to external value
}
In this example, can I detect that the firstRect color is a static value, whereas the second rectangle color is bound?
I think what you're after is in a private static method: QQmlPropertyPrivate::binding(). For example here is some QtQuick Designer code using it.
Being technically in the Qt private parts, I'm not sure this helps. Interestingly, the Qt::QML Type has a Qt.binding() function which is used to assign bindings (and apparently calls the above private method behind the scenes). And there is the QML Binding type. But in neither case do I see a way to get an instance of an existing binding (QAbstractBinding) already assigned to a property.
Responding to some of the comments: Through the Qt meta object system you can find what is connected to a property notifier signal. But to do that you'd need to know the QObject and QMetaProperty to which the QML property in question is bound to (or not)... which brings us back to square 1 I believe (finding out what the property is bound to, if anything).

How to debug error "Cannot read property of null"

In a big QML project, sometimes I get the following error :
MyItem.qml:8: TypeError: Cannot read property of null
When I look at the concerned line, it is always in a sub-Item, with a property binding to a parent property like
Item
{
id: myItem
ASubItem
{
id: subItem
width: parent.width
}
}
So it seems like the parent became null and the inner item is trying to update its property after that.
We sometimes delete the items from C++ because we want to create new ones, it seems to be the cause of the error messages. We also create some items from C++ (using QQmlComponent) and set the parent and parent-item to the QQuickItem that contains the items, but it seems that I get this error on other items as well.
But I don't understand why an inner item would try to update itself while the parent is null, shouldn't it be deleted at the same time ?
Is there any way to debug this to see when the parent is deleted and when its child-item is trying to update ?
a workaround that worked for me, however my case is a little bit different since I'm creating the objects dynamically from Javascript not c++ but I'm sure you can figure it out on how to do it from c++.
I passed the QGuiApplication instance to the root QML Context
engine.rootContext()->setContextProperty("KApp",&app);
and then made this connection
Connections{
target: KApp;
onAboutToQuit:{
dynamicallyCreatedObject.destroy();
}
}
so the point is, you have to destroy the dynamically created QML objects when the aboutToQuit() signal is emitted

Broken recurssive instantiation checks

I notice that QML has some unspecified problem with the following - when a delegate tries to instantiate an object that contains it (the delegate that is).
Consider this trivial tree builder example:
// Object.qml
Row {
property int c : 0
Rectangle {
width: 50
height: 50
color: "red"
border.color: "black"
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) c++
else if (c) c--
}
}
}
List { model: c }
}
// List.qml
Column {
property alias model : r.model
Repeater {
id: r
delegate: Object {} // wont work
}
}
In this example Object contains List, which has a delegate that instantiates another Object. However it doesn't work, there are no error messages, not even in debug mode, the application window simply doesn't appear, and from the looks of memory usage, it is hopelessly stuck somewhere in oblivion. It doesn't crash either. It just doesn't work.
My first suspicion is that this is yet another one of QML's shortsighted implementation details, with the noble goal to protect against accidental infinite recursions. However, in this case there is no danger of such, because even though the two objects mutually instantiate each other, the process is controlled by the "model".
It also happens to work "flawlessly" if an "indirection" is added, for example:
Column {
property alias model : r.model
Repeater {
id: r
delegate: Loader { source: "Object.qml" }
}
}
If the delegate is replaced from an Object to a Loader that instnatiates an Object, it works OK, I assume because it doesn't trigger the dumb cross-reference check which interrupts execution.
You might be asking why I simply don't use the second scenario, and the answer is simple - it is trivial example it is OK, but in my actual production code this breaks something very complex I am making with cascading the amount of children items in negative xy space, and inserting a Loader in between simply kills it, and I haven't found a way to get it to work.
I also notice delegates have that absolutely identical problem when instantiating the tree example from Components within that same source, cross-reference or self-reference - when this type of instantiation recursion is used, the application gets stuck the same way - no errors, no warnings, no crash, no window, no nothing.
Any idea what's going on and how to fix it? My money is on infinite recursion protection, which assumes it is infinite even if it is in fact controlled and very much finite recursion.
EDIT: Also note that wrapping it this way doesn't work either delegate: Component { Object {} }. It works only if Object is not referenced as an instance, so it looks like some very superficial check if objects mutually contain each other regardless of the actual circumstances. So as expected, it also works if the wrapping is moved to Object:
Loader {
Component.onCompleted: setSource("List.qml", {"model": Qt.binding(function() {return c})})
}
UPDATE:
I also noticed this:
// Obj.qml
Item {
id: main
property Obj thisObj : main
}
Which doesn't work either. This time there is some actual output in the console, saying that Obj is instantiated recursively which it really isn't since it is used in the context of a type property, not a value/instance.
It seems that the recursive instantiation checks in QML and fundamentally broken, they just produce absolutely nothing if a literal QML element type self/cross reference is found in the body of an object tree, and produce false output when it the reference is used to specify a property type, even if QML object properties are actually implemented as references rather than instances.
I also issued a bug report and proposing how to distinguish between actual infinite recursions and cases of self or cross reference which are actually not infinite recursions, which is quite simple - simply don't trigger if the reference is a property type or inside a delegate.

QML TableView access model properties from delegate

I have a TableView for which I've defined my own itemDelegate. Now, from within this delegate I can access the value for the column using styleData.value, but I'd also need to access the other properties in this same item but I can't find how to.
I need this, because the text styling needs to change depending on some other property of the item model.
Any ideas? thanks!
There is some documentation missing. Within the item delegate you can access the following (taken from the source code of TreeView.qml):
styleData (see documentation)
model (currently not documented)
modelData (currently not documented, not sure about this but I guess it's similar to ListView)
(By the way, what's also missing in the documentation but which is useful is styleData.role. Also, the documentation of the other delegates lacks some available properties too; the best is to peek into the source code of the QML file and have a look for the Loader element which instantiates your delegate. As a plus you learn how that creepy stuff works. ;))
With model and the row/column information you can then navigate to the item data. This code depends on the type of model.
If you're using QML's ListModel, then you can use model.get: model.get(styleData.row)[styleData.role] should then work (untested since I use it rarely, please give feedback).
If you're using a C++ QAbstractItemModel or friends, the best is to add a slot to the model class which takes just the row and role name, since that's the information the TableView works with (nor with role numbers nor with columns...).
However in both cases you shouldn't use the expression in a property binding! The notification system will not work since you don't use the property system for accessing the data. According to your question, I guess you wanted to use it in a expression with binding. I don't know how to properly listen to changes in the model manually.
An alternative approach is to access the other items of the row and provide a property there. Some hints:
From within one item, you can access other items of the same row by walking the object tree up twice (first to the Loader which instantiates your component, then to the actual row) and then down twice (first to the particular child object which is a Loader, then its instantiated item). You need to know the column number you want to access (not the role name), I assume you want to access the first column (index 0):
parent.parent.children[0].item
You can provide the model data using a property in each item. Assuming a simple Text element this might be:
Text {
property variant value: styleData.value // <-- Here you make it available
// your other stuff
}
Putting them together could look like the following. In this example I assume the first row contains an integer, and if it is zero, the second column should be red.
// (within TableView)
itemDelegate: Text {
property variant value: styleData.value
text: styleData.value
color: (styleData.column == 1 && parent.parent.children[0].item.value === 0)
"red" : "black"
}
I think it's pretty easy if you read the source code of TableViewItemDelegateLoader.qml (it is a private code in qtquickcontrol)
To access any role you use use : model[your_role_name] .
For exp: model["comment"]
Faced with same problem today, this is result of my investigations (Qt 5.2.x)
If you have hard limit to TableView, there is only one correct solution - use model.get(styleData.row)["roleForStyling"] as #leemes wrote. But it will very slow if you have big amount of data in model and using, for example, proxy model for sorting/filtering.
Direct solution from #leemes answer is great, but in general case not be working, because in TableView any Item wrapped in Loader and therefore independent from parent and other items:
When some item is created (where you want to change text style)
another element (from which to receive identity) cannot yet be
created
You may not have "parent" on item creation (i.e. binding will
be broken)
In my case, the best solution for deep customise was creation of the simple wrapper for ListView. In this case you have access for complete row data in delegate without the overhead. Highlights for making component ("My own ListView as table"):
Create standalone header (Rectangle or Item) - do not use header form ListView.This make it fixed for any amount of data.
Wrap ListView to ScrollView (if you need scrollbars)
Use Clip: true property in list for make correct
Set style for highlight and set highlightFollowsCurrentItem:true in ListView
As bonus in future this may be used for make "TreeTable" :)

Resources