I am working on a desktop application the uses a QML GUI with a lot of QML Components.
These are parts of the hierarchy:
main -> toolbar -> searchbar -> editfield
and
main -> resultlist -> header -> button1
I could not find a way to access the text contents of editfield in a signal handler for button1. Is it possible to do that in QML or Javascript?
I know I can access the element in the C++ part by using the objectName property.
Due to QML uses dynamic scoping (→ Doc), child elements can access the properties of all ancestors
and it doesn't matter if they are in different files.
So you could add an editFieldText property to main and bind the
text property of editfield to it. Then you can access editFieldText
from everywhere:
//=== main.qml ===
import QtQuick 1.0
Rectangle {
id: main
property string editFieldText
Toolbar {
// [...]
}
Resultlist {
// [...]
}
}
//=== EditField.qml ===
import QtQuick 1.0
TextInput {
// bind text property to main.editFieldText
Binding {
target: main;
property: "editFieldText";
value: text
}
}
//=== Header.qml ===
import QtQuick 1.0
Rectangle {
Button {
onClick: {
console.log(main.editFieldText);
// or simply
console.log(editFieldText);
}
}
}
You can use alias properties to have the editfield.text as a property of main. This property should be accessible from button1.
Related
I have an application in which i am loading some resources inside a main file with a Loader object:
main.qml:
ApplicationWindow {
property string pageSource: pageLoader.source
...
Loader {
id: pageLoader
source: "page1.qml"
}
}
In page1.qml i have a layout that wraps some buttons and text fields. Loading this page in pageLoader works fine. As you can see i have a foo Button that should switch the source of the Loader to page2.qml, but for some reason it's not working.
page1.qml:
Item {
ColumnLayout {
TexField {...}
TexField {...}
Button {
id: foo
onClicked: {
pageSource = "page2.qml"
}
}
}
}
By checking some console logs i was able to know that i am actually changing the source of the loader, but the view is not updating as expected and all controls from page1.qml is not replaced by page2.qml. This second page has a ListView. Toggling active property of Loader doesn't work either. Can anyone please shed a light?
I've been trying to wrap my head around this one for a while and came up with a few hacks, but none of them seem to be the Right Way. Hopefully this makes sense. Say I have three qml files.
First QML:
...
ListView {
id: patientList
model: patientModel
delegate: E3DPatientListItem {
onClicked: {
if (patientList.currentIndex !== index)
{
patientList.currentIndex = index
detailStackView.push("DetailPanel.qml",
{ "view": view, "ptInfo": model })
...
DetailPanel:
...
Page {
property QtObject ptInfo
Timeline {
ptInfo: ptInfo // <- how do I pass this to Timeline?
...
Timeline.qml
...
Item {
property QtObject ptInfo // <- always null :(
...
...
Page {
property QtObject ptInfo
Timeline {
ptInfo: ptInfo // <- how do I pass this to Timeline?
...
What do you suppose ptInfo: ptInfo is achieving? You are binding the property to its own identifier.
Maybe try not using the same identifier to avoid shadowing, or give the Page and id and then ptInfo: pageid.ptInfo. parent.ptInfo will also work as long as the object has the parent property exposed, keep in mind that QtObject does not.
However, you don't really need to have property QtObject ptInfo in Timeline.qml, as long as a timeline is always instantiated inside a page, you can access ptInfo directly from inside the timeline because of dynamic scoping. Note that dynamic scope properties will only work for properties that are defined in the root element of the particular qml file:
// Obj1.qml
Item {
property int test: 7
Obj2{}
}
// Obj2.qml
Item {
Component.onCompleted: console.log(test) // it will work
}
I am using a qml Loader component to push a page dynamically into view. Now the page I push has various properties and I would like to access them from the Loader component itself. However, I am unable to create aliases to these properties. So, I have something like:
Loader {
id: loginLoader
source: "qrc:/pages/IdPinLoginPage.qml"
property alias hasNavBar: loginLoader.item.hasNavBar
property alias hasStatusBar: loginLoader.item.hasStatusBar
}
This results in Invalid alias target location. How can I redirect these properties to the parent level in the Loader component?
I recommended you to use sourceComponent instead of source property of the Loader for assign/bind value to loaded item easily.
Also for access loaded item, you can use item property of the loader:
Loader {
id: loader
sourceComponent: Component {
SampleItem {
foo: 13 //assign values to loaded item properties
bar: "aleyk!"
}
}
}
Text {
text: loader.item.bar //Access to loaded item properties
}
SampleItem.qml
import QtQuick 2.6
Item {
property int foo: 0
property string bar: "salam";
}
I have a QML application where I'm creating lists of elements received from JavaScript. Using the details from this answer I'm populating the model as a JS array, and it works great. However, I'd like it so that when properties of the JavaScript objects change that ListView items driven from them update live.
Here's a simple test app showing the problem. The ListView is properly populated with MyRow instances showing the correct id/title, but when the rand property is changed by the timer, the List rows are unchanged (they show 0 for the last item).
MyRow.qml
import QtQuick 2.0
import QtQuick.Layouts 1.3
Rectangle {
property var obj
color:'#eeeeff'; height:20
RowLayout {
anchors.fill:parent
Text { text:obj.id }
Text { text:obj.title; Layout.fillWidth:true }
Text { text:obj.rand }
}
}
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
id:app; visible:true; width:200; height:100
property var database: ({"17":"World","42":"Hello"})
property var objById: ({})
function getObj(id){
if (!objById[id]) objById[id] = { id:id, title:database[id], rand:0 };
return objById[id];
}
ListView {
id:mylist
anchors.fill:parent
model: [42,17] // object ids
delegate: MyRow {
width:parent.width
obj:getObj(mylist.model[index])
}
}
Timer { // Update every object's rand value every second
interval:1000; running:true; repeat:true
onTriggered: {
Object.keys(objById).forEach(function(id){
objById[id].rand = Math.random()*100<<0;
})
}
}
}
How can I get the delegate's Text items to update their text when properties of the objects change?
The easiest (only?) way to get property bindings to work properly is to create real Qt objects to hook the values to. If you don't want to use a ListModel (because you want to quickly populate a model with items from a master library), then you can use createObject() to generate objects and pass them to your delegate.
Here's an updated main.qml that works as desired:
Window {
// ...same as above...
Component { // Creates real Qt objects with bindable properties
id:objFactory
QtObject {
property int id
property string title
property int rand:0
}
}
function getObj(id){
if (!objById[id])
objById[id] = objFactory.createObject( app, {id:id,title:database[id]} );
return objById[id];
}
// ...same as above...
}
Additionally, you may wish to change the property var obj in MyRow.qml to a more specific property QtObject obj (or a more specific object type, depending on what you pass in).
Finally, note that it's slightly cleaner/simpler to use modelData instead of mylist.model[index]:
ListView {
anchors.fill:parent
model: [42,17] // object ids
delegate: MyRow {
width:parent.width
obj:getObj(modelData)
}
}
Ok like if I'm trying to make something like a menu for a simple game. How can I make it so that when the start button is clicked it loads a different QML file?
You can use Qt.createComponent() or Loader. For example:
import QtQuick 1.0
Item {
MyButton {
onClicked: loader.source = "MyGameFile.qml"
}
Loader {
id: loader
anchors.fill: parent
}
}