What is the proper way to use ids in QML - qt

I need to use most of my child qml childrens in parent qml so using aliasing i am doing it now but some how i am not ok that calling login.help.visible too many dots. could some one give me best solution for this.
here is my code
main.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle{
anchors.fill: parent
Widget{
id: login
}
MouseArea{
anchors.fill: parent
onClicked: login.help.visible = false
onEntered: {
login.help.visible = true
login.progress.visible = true
ogin.pwd.visible = true
}
onExited: {
login.help.visible = false
login.progress.visible = false
ogin.pwd.visible = false
}
}
}
}
Widget.qml
import QtQuick 2.0
import QtQuick.Controls 1.2
Item {
id: newid
property alias help: helpid
property alias progress: ploder
property alias username: name
property alias password: pwd
Help{
id: helpid
}
TextCustom{
id: name
}
TextCustom{
id: pwd
}
progressLoader{
id: ploder
}
}

Usually, once I define a new Component, I use to encapsulate its children, logic and properties by hiding everything to the users, while I expose the properties that are freely modifiable (even the ones that belong to the children of my component) by means of its properties (aka the property keyword, correctly bound to the target properties of the component or of its children).
This way, the children of my component are nicely isolated from the users of the component itself.
It follows an example:
// MyComponent.qml
Item {
property alias B: firstChild.B
Item {
id: firstChild
property int A: 0 // this is hidden to the users of MyComponent...
property int B: 0 // ... this is not, indeed
}
}
// AnotherComponent.qml
// the outer Component has the following child
MyComponent {
B: 3 // directly modifies the property B of firstChild in MyComponent
}

Related

Why can't ListView delegate refer to properties of ListView?

If a ListView contains user-defined properties, these properties can be referenced in a binding for model but they can not for anything inside the delegate. Why is this?
The docs seem to say that a Component should be able see properties in enclosing scopes where it was declared.
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
visible:true
ListView {
orientation: ListView.Vertical; height: 300; width: 100
property var myCount: 3
property var myMessage: "Hello"
Component {
id: myComp
Text {text: myMessage} // ReferenceError: myMessage is not defined
}
model: myCount // this works
delegate: myComp
}
}
(In my real application, the ListView is a component (.qml file) and the invoker needs to pass in information needed to configure the delegate; not
literal text like in this example, but information for a nested ListView.)
Thanks for any help...
The variables in QML have a scope, in your case when using myMessage without reference these indicating that the variable belongs to the Text item.
# ...
Component {
id: myComp
Text {text: myMessage}
}
# ...
So the solution is to use the ListView id as a reference:
# ...
ListView {
id: lv
orientation: ListView.Vertical; height: 300; width: 100
property var myCount: 3
property var myMessage: "Hello"
Component {
id: myComp
Text {text: lv.myMessage}
}
model: myCount // this works
delegate: myComp
}
# ...

Can a RowLayout item have signals in QML?

I'm having a little problem with some custom Items I'm using im my QML code. I created a set of Items consisting in a RowLayout containing a label and an input control (e.g. SpinBox or ComboBox). In order to "bring the value changed signal outside" i added to the external RowLayout a signal that should be emitted onValueChanged. Here's the code:
import QtQuick 2.10
import QtQuick.Controls 2.2
import QtQuick.Window 2.2
import QtQuick.Controls.Material 2.3
import QtQuick.Layouts 1.0
RowLayout {
property alias label: innerLabel.text
property alias value: innerNum.value
property alias editable: innerNum.editable
property alias min: innerNum.from
property alias max: innerNum.to
property string myPage: ""
property string myMeasure: ""
property int fontSize: 16
property bool signaling: true
property var colorAccent: Material.accent
property var colorPrimary: Material.primary
signal fabValueChanged(var message)
Label{
id: innerLabel
Layout.fillHeight: filling
Layout.fillWidth: filling
Material.foreground: colorAccent
font.pixelSize: fontSize
verticalAlignment: Text.AlignVCenter
property bool filling: true
onTextChanged: function(){
if (text == ""){
filling = false;
width = 0;
}
else{
filling = true;
}
}
}
SpinBox{
id: innerNum
Layout.fillHeight: true
Layout.fillWidth: true
Material.foreground: colorAccent
editable: true
to: 100000
from: 1
value: 1
onValueChanged: function(){
if(parent.signaling){
var toSend = {"name":parent.objectName,"value":value,"measure":parent.myMeasure};
fabValueChanged(toSend);
}
}
}
}
When I connect the fabValueChanged to a simple slot that just prints the message I got nothing, even if the onValueChanged signal is correctly emitted.
Can it be that RowLayout cannot emit signals? If that's the case, How can I modify my object in order to avoid this problem?
If my problem is not the layout, wht is going on?
I'm really rossy for waistin other people's time on this issue. In the end the problem was with the function I used to dynamically create these objects. So the object as it is can emit signal without any problem.
Sorry again

How i can save into JSON file a QML list model? [duplicate]

I am able to save settings for list items which is statically created using Component.onComponent method. But Settings for statically created list items take affect after reopening app. I would like to save settings for dynamically created list model. I am unable to save Settings for a dynamically created list item. The code below does that a list item is on and off while clicking Show/Hide action. When I reopen the app, created list item disappears. How to save list item using Setting?
import QtQuick 2.9
import Fluid.Controls 1.0
import Qt.labs.settings 1.0
import QtQuick.Controls 1.4
ApplicationWindow {
id:root
visible: true
width: 640
height: 480
property variant addlist
property int countt2: 0
Settings{
id:mysetting4
property alias ekranCosinus: root.countt2
}
function listonoff(){
if(countt2%2==1){
return true
}
else if(countt2%2==0){
return false
}
}
Connections {
target: addlist
onTriggered: listonoff()
}
addlist: favourite2
/* main.qml */
menuBar: MenuBar {
Menu {
title: "&Edit"
MenuItem { action: favourite2 }
}
}
Action {
id:favourite2
text: qsTr("Show/Hide")
onTriggered: {
countt2++
console.log(countt2)
if(listonoff()===true){
return list_model.insert(list_model.index,{ title: "First item."} )
}
else if(listonoff()===false){
return list_model.remove(list_model.index)
}
}
}
ListView {
id:contactlist
width: parent.width
height: parent.height
focus: true
interactive: true
clip: true
model: ListModel {
id:list_model
}
delegate: ListItem {
text: model.title
height:60
}
}
MouseArea {
id: mouse
anchors.fill: parent
}
}
Quite curious that you expect that saving a single integer value will somehow be able to store the content of an arbitrary data model... It doesn't work even for the static model data, it is only "restored" because it is static - it is part of the code, you are not really saving and restoring anything.
If you want to store all that data, you will have to serialize it when your app quits, and deserialize it when the app starts.
You could still use Settings, but to store a string value, that will represent the serialized data.
The easiest way to do it is to transfer the model items back and forth with a JS array, this way the JS JSON object functionality can be used to easily serialize and deserialize the data:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Window 2.3
import Qt.labs.settings 1.0
ApplicationWindow {
id: main
width: 640
height: 480
visible: true
property string datastore: ""
Component.onCompleted: {
if (datastore) {
dataModel.clear()
var datamodel = JSON.parse(datastore)
for (var i = 0; i < datamodel.length; ++i) dataModel.append(datamodel[i])
}
}
onClosing: {
var datamodel = []
for (var i = 0; i < dataModel.count; ++i) datamodel.push(dataModel.get(i))
datastore = JSON.stringify(datamodel)
}
Settings {
property alias datastore: main.datastore
}
ListView {
id: view
anchors.fill: parent
model: ListModel {
id: dataModel
ListElement { name: "test1"; value: 1 }
}
delegate: Text {
text: name + " " + value
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) {
var num = Math.round(Math.random() * 10)
dataModel.append({ "name": "test" + num, "value": num })
} else if (dataModel.count) {
dataModel.remove(0, 1)
}
}
}
}
The application begins with a single data model value, more data items can be added or removed by pressing the left and right mouse button respectively.
As long as the application is closed properly, the data model will be copied into an array, which will be serialized to a string, which will be stored by the Settings element. So upon relaunching the app, if the data string is present, the model is cleared to remove the initial value so it is not duplicated, the data string is deserialized back into an array, which is iterated to restore the content of the data model. Easy peasy.
Of course, you could also use the LocalStorage API as well, or even write a simple file reader and writer by exposing a C++ object to QML. All this approach needs is to be able to store and retrieve a single string.

How to setup my button component to open a window

Here is the code of the window I wanna be opened in file PopUpFreeCoins.qml:
import QtQuick 2.0
import QtQuick.Controls 2.1
Item {
property int t
property int c
ListModel{
id:ff
ListElement {
name: "ByFollow"
s: "Images/follow.png"
}
ListElement {
name: "ByLike"
s: "Images/care.png"
}
ListElement {
name: "ByComment"
s: "Images/chat.png"
}
}
ListView{
width:t-t/10
height: c/5
layoutDirection:Qt.LeftToRight
orientation: ListView.Horizontal
model: ff
spacing:50
delegate: Button{
contentItem: Image{
source: s
}}
}
}
property t is set equal to window width in main file and property c is set to window height. This is code of my Button.qml:
Button{//Below Right
width:profilePicture.width/2
height:profilePicture.width/2
x:profilePicture.x+profilePicture.width
y:profilePicture.y+profilePicture.height
contentItem: Image {
source: "Images/freecoins.png"
anchors.fill: parent
}
onClicked: PopUp{height:100;width:300;PopUpFreeCoins{t:a;c:b;}}
}
property a is window width and b is window height.
this line onClicked: PopUp{height:100;width:300;PopUpFreeCoins{t:a;c:b;}} has an error I don't know how to handle!
Here is the error:
Cannot assign object type PopUpFreeCoins_QMLTYPE_0 with no default
method
You need to create the Object somehow. You have multiple ways for dynamically create Objects. One way is to use Component.createObject(parent) which requires you to have a Component instantiated in your file.
Here you can also pass a Object ({property0 : value, property1:value ... }) as second argument, to set the properties of the Component to be instantiated. You should not set the parent to null as it might happen, that the JS-garbage collector is too aggressive once again.
Alternatively you can use the Loader to load it from either a source (QML-file) or sourceComponent. Here you won't have problems with the garbage collector.
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
width: 1024
height: 800
visible: true
Button {
text: 'create'
onClicked: test.createObject(this)
}
Button {
x: 200
text: 'load'
onClicked: loader.active = !loader.active
}
Loader {
id: loader
source: 'TestObj.qml'
active: false
}
Component {
id: test
TestObj {}
}
}
TestObj.qml includes the Window to be opened.
Alternatively you can have the Window created from the beginning, and just change the visible to true or false.

How to make some reusable QML object, which can inject another QML object?

How to make some reusable QML object, which can inject another object?
I've ever tried to use Component & Loader , but seems not what I want. (It still encapsulate the whole QML type and lacks of elasticity, hard to reuse)
Usage example:
Card.qml
import QtQuick 2.0
import QtQuick.Layouts 1.3
Rectangle {
default property var innerObject
property string titleText: "[Hello Untitled Title]"
id: root
color: "#fff"
ColumnLayout {
anchors.fill: parent
Rectangle {
id: header
height: 10
width: parent.width
color: "#666"
RowLayout {
Text { text: titleText; color: "#fff" }
}
}
// How to inject innerObject in here ?
}
}
main.qml
import QtQuick 2.0
import QtQuick.Layouts 1.3
Card {
titleText: "Image Information"
ColumnLayout { /* .......*/ } // innerObject
}
Card {
titleText: "Image Viewer"
Rectangle { /* .......*/ } // innerObject
}
The answer I linked works like this:
Main.qml
Card {
titleText: "Image Viewer"
innerObject: Rectangle {
Component.onCompleted: {
console.log(parent.objectName)
}
}
}
Card.qml
Rectangle {
property string titleText: "[Hello Untitled Title]"
default property alias innerObject : innercolumn.children
id: root
color: "#fff"
ColumnLayout {
id: innercolumn
objectName: "column"
anchors.fill: parent
Rectangle {
id: header
height: 10
width: parent.width
color: "#666"
RowLayout {
Text { text: titleText; color: "#fff" }
}
}
}
}
I also want to suggest a solution based on default property and reparenting:
The Item which can embed another Item:
MyItem.qml
import QtQuick 2.7
import QtQuick.Layouts 1.2
Rectangle {
id: root
default property Item contentItem: null
border {
width: 1
color: "#999"
}
ColumnLayout {
anchors.fill: parent
Rectangle {
Layout.fillWidth: true
height: 30
color: "lightgreen"
}
Item {
id: container
Layout.fillWidth: true
Layout.fillHeight: true
}
}
onContentItemChanged: {
if(root.contentItem !== null)
root.contentItem.parent = container;
}
}
Can be used as below:
main.qml
import QtQuick 2.7
import QtQuick.Window 2.0
Window {
visible: true
width: 600
height: 600
MyItem{
width: 400
height: 400
anchors.centerIn: parent
Text {
text: "Hello!"
anchors.centerIn: parent
}
}
}
But I still agree with #ddriver that Loader is the best solution for this case
It is not mandatory that you use a Loader with a component. You can just go:
Loader {
source: "Something.qml"
}
When the source is something that can be loaded synchronously, you can directly use the loader's item for stuff like bindings, without worrying about whether or not it is created. If you load over network, you have to delay the bindings until the item is completed and use either a Binding element or Qt.binding() to do it respectively in a declarative or imperative manner.
In your case, a loader would be appropriate, and the property for the inner dynamic object outta be a Component. This way you can populate it either with an inline component, or with Qt.createComponent() from existing source.
property Component innerObject
...
innerObject: Component { stuff }
...
innerObject: Qt.CreateComponent(source)
Of course, there are even more advanced ways to do it, for example, the "generic QML model object" I have outlined here. It allows to quickly and easily create arbitrary data structure trees both declaratively and imperatively, and since the object is also a model, you can directly use listviews or positioner elements with repeaters to layout the gui without actually writing the UI code each and every time.
Also, from your main.qml code example - you cannot have more than one root element in a qml file.
Edit: The default property approach actually works if the element is moved to its own qml file, so also basically you could just:
default property alias innerObject: innerColumn.children
where innerColumn is the id of your ColumnLayout. Also, innerObject could be whatever legal name, since as a default property, it will not actually be used.
There is also the option to not use a default property, which is useful when the root item still needs to have its own children, but still have the ability to redirect declarative objects to be children of a sub-object:
property alias content: innerColumn.children
// and then
content: [ Obj1{}, Obj2{}, Obj3{} ] // will become children of innerColumn

Resources