How can I access and change a item in Component in QML - qt

In a Qml file ,the code like:
StackView {
id: stackView
anchors.right: parent.right
width: parent.width/2-20
initialItem:patientdetail
Component{
id:patientdetail
Column {
id:detailcolumn
spacing: 2
anchors.right: parent.right
width: parent.width/2-20
Label {
id: label
color: "#ffffff"
text: qsTr("User ID")
}
TextField {
id: textField_id
readOnly: true
placeholderText: qsTr("")
}
}
}
Component{
id:component2
//...other component will add to stackview
}
}
And I want to change the text of TextField by a JS function(in same file) like:
function updatedetail(clear,rowindex){
if(clear){
textField_id.text="";
}
else{
textField_id.text=jsonModel1.model.get(rowindex).id;
}
}
But there is a error:
ReferenceError: textField_id is not defined
Where the error occurred?

As you try to change an Object that is not yet instantiated, it will fail. But even when it is instantiated, its id will be in a different scope, that can't be reached like that.
This is necessary, as the same Component might be instantiated multiple times (e.g. as a delegate in a ListView) so it would not be unique in the context anymore.
Uppon instantiation of your StackView, your Component will be instantiated an pushed on the StackView. Now you have an instance and might alter exposed properties by using:
currentItem.textFieldText = newValue
in your function. For this to work you need to expose the property:
Component{
id:patientdetail
Column {
id:detailcolumn
spacing: 2
anchors.right: parent.right
width: parent.width/2-20
property alias textFieldText: textField_id.text // in this context, you have access to the id. Expose it, to change it from the outside.
Label {
id: label
color: "#ffffff"
text: qsTr("User ID")
}
TextField {
id: textField_id
readOnly: true
placeholderText: qsTr("")
}
}
}
However, as the instance might be destroyed and recreated later, this change would not be permanent, so it would be better to bind the TextField.text to a property of an object, that will survive as long as necessary. This could be a contextProperty exposed from C++ or a QtObject passed as a model, or just a property, e.g. in the StackView.
StackView {
id: stackView
anchors.right: parent.right
width: parent.width/2-20
initialItem:patientdetail
// Change this property. The textField_id.text will be bound to it.
property string textFieldText
Component{
id:patientdetail
Column {
id:detailcolumn
spacing: 2
anchors.right: parent.right
width: parent.width/2-20
Label {
id: label
color: "#ffffff"
text: qsTr("User ID")
}
TextField {
id: textField_id
readOnly: true
placeholderText: qsTr("")
text: stackView.textFieldText
}
}
}
}

Related

Closing qml dialog properly

I've been playing around with dialogs and there is something that bothers me.
I have the following code:
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Button {
id: click
x: 285
y: 189
text: qsTr("Click")
onClicked: dlgTest.open()
}
Dialog{
id:dlgTest
visible:false
contentItem: Rectangle{
width: 300
height: 300
TextField{
id: tfText
anchors.top: parent.top
}
Button{
anchors.top: tfText.bottom
onClicked: dlgTest.close()
text: "Close"
}
}
}
}
When I open it the first time I add some text to the TextField and then I close it. However, If I open it again, the text will still be there. What I want is to "reset" the dialog to it's original state when I opened it the first time (with an empty TextField). It seems that calling the method "close" is exactly the same as changing visible to false.
Is there a way of doing this "reset"?
I have an other dialog with a lot of controls and it's annoying having to restore everything manually.
In your code you create the Dialog once, as a child of the ApplicationWindow.
To 'reset' it, you have two options:
Have a reset-function, that you call, and restores everything. You can use this to set it up in the first place as well
Create a new Object with everything set in place.
For the latter you can either use JavaScript Dynamic Object Creation or a Loader.
JavaScript Dynamic Object Creation:
Button {
id: click
x: 285
y: 189
text: qsTr("Click")
onClicked: {
var d = diaComp.createObject(null)
d.open()
}
}
Component {
id: diaComp
Dialog{
id:dlgTest
visible:false
contentItem: Rectangle{
width: 300
height: 300
TextField{
id: tfText
anchors.top: parent.top
}
Button{
anchors.top: tfText.bottom
onClicked: {
dlgTest.close()
dlgTest.destroy()
}
text: "Close"
}
}
}
}
However, as you destroyed the Object, the contents of your properties are lost, and you can't access them anymore. So you need to make sure, to copy them (not bind them) to some property that is not destroyed, first.
With the Loader you have the posibility to unload the Dialog right before you load it again, which basically resets it. But until you unloaded it, you can still access it's values, as you can see in the Buttons onClicked-handler.
Button {
id: click
x: 285
y: 189
text: qsTr("Click")
onClicked: {
console.log((dlgLoad.status === Loader.Ready ? dlgLoad.item.value : 'was not loaded yet'))
dlgLoad.active = false
dlgLoad.active = true
dlgLoad.item.open()
}
}
Loader {
id: dlgLoad
sourceComponent: diaComp
active: false
}
Component {
id: diaComp
Dialog{
id:dlgTest
visible:false
property alias value: tfText.text
contentItem: Rectangle{
width: 300
height: 300
TextField{
id: tfText
anchors.top: parent.top
}
Button{
anchors.top: tfText.bottom
onClicked: {
dlgTest.close()
}
text: "Close"
}
}
}
}
Of course, you could also copy the values from the Loader's item as well, and then unload it earlier, to possible free the memory.
But if the Dialog is frequently (most of the time) shown, it might be the wisest to avoid the creation and destruction of the objects, by reusing it and resetting it manually.

how to load a new screen in QML showing a new list based on previous user click input?

In my application i show a nested list, that shows groups and folders as its children. I have built the functions necesary to generate a new list in the backend in c++ based on which item is clicked by the user.
I allready have the necesary functionality to pass the list to qml through QProperty.
so my question is, how do i previous listviews and show new ones dynamically. Considering it should also be possible to click the button "back", which should load the previous page again showing the groups and the folders.
this is the code i have now, showing the groups and its children(folders)
import QtQuick 2.4
import QtQuick.Window 2.2
//import ListMode 1.0
Rectangle {
height: 250
width: 140
color: "pink"
//property var aNum: 0
Component {
id: folderDelegate
Item {
width: 140
height: col2.childrenRect.height
Column {
id: col2
anchors.left: parent.left
anchors.right: parent.right
Rectangle {
height: 20
width: parent.width
border.color: "black"
MouseArea {
anchors.fill: parent
onClicked: treemodel.getObject(model.ID + ":" + model.Name)
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
id: name1
text: model.Name
}
}
}
}
}
ListView {
id: outer
model: myModel
delegate: groupsDelegate
anchors.fill: parent
}
Component {
id: groupsDelegate
Item {
width: 140
height: col.childrenRect.height
Column {
id: col
anchors.left: parent.left
anchors.right: parent.right
Text {
anchors.horizontalCenter: parent.horizontalCenter
id: t1
font.bold: true
font.underline: true
font.pointSize: 9
text: model.Name
}
ListView {
id: folderlist
model: treemodel.lists[treemodel.modIndex]
delegate: folderDelegate
contentHeight: contentItem.childrenRect.height
height: childrenRect.height
anchors.left: parent.left
anchors.right: parent.right
clip: true
}
}
}
}
}
i have been reading documentations and searching forums, but the information is pretty overwhelming. So a pointer in the right direction would be appreciated.
the main model is setup for each item to have its own unique ID. So when an item is clicked, i run a function that grabs and stores the item based on the ID + name that was clicked
MouseArea {
anchors.fill: parent
onClicked :{
treemodel.getObject(model.ID + ":" + model.Name)
stackView.push(Qt.resolvedUrl("content/ButtonPage.qml"))
}
}
next, based on the item that was clicked i have functions that fill different QList items which are loaded into the ButtonPage.qml.
the function in c++ that is invoked is:
Q_INVOKABLE void getObject(QString index) {
clickedItemID = index;
getClickedItem();
getFilesByFolder();
}
now, i am not sure if this is a good solution. But for me it works. Maybe it will work for someone else too.

How propagate TableViewColumn list to child TableView?

I'm trying to make custom component for editable tables in QML, like this:
// BaseTableView.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
Item {
signal addActionPerformed()
signal editActionPerformed(int id)
signal deleteActionPerformed(int id)
property var model
ToolBar {
id: toolBar
anchors.left: parent.left
anchors.right: parent.right
Row {
ToolButton {
id: addButton
iconSource: "qrc:/icons/actions/add.png"
onClicked: addActionPerformed()
}
ToolButton {
id: editButton
enabled: false
iconSource: "qrc:/icons/actions/edit.png"
}
ToolButton {
id: deleteButton
enabled: false
iconSource: "qrc:/icons/actions/delete.png"
}
}
}
TableView {
id: tableView
model: parent.model
anchors.left: parent.left
anchors.right: parent.right
anchors.top: toolBar.bottom
anchors.bottom: parent.bottom
onCurrentRowChanged: {
editButton.enabled = currentRow !== null
deleteButton.enabled = currentRow !== null
}
}
}
and use this component in another file like this:
// Another.qml file
import QtQuick 2.3
import QtQuick.Controls 1.2
import "../common" // Here is BaseTableView.qml
BaseTableView {
TableViewColumn {
role: "id"
title: qsTr("Id")
}
TableViewColumn {
role: "object_expression"
title: qsTr("Expression")
}
}
So, problem is how i can pass table view columns from usage to underlying TableView?
I've tried to make property list in BaseTableView and assign a list of objects to this property in Aother.qml? but unsuccessfully.
Use default properties:
An object definition can have a single default property. A default property is the property to which a value is assigned if an object is declared within another object's definition without declaring it as a value for a particular property.
More relevant for your scenario:
You will notice that child objects can be added to any Item-based type without explicitly adding them to the children property. This is because the default property of Item is its data property, and any items added to this list for an Item are automatically added to its list of children.
Default properties can be useful for reassigning the children of an item. See the TabWidget Example, which uses a default property to automatically reassign children of the TabWidget as children of an inner ListView.
If you take a look at the TabWidget example that the last paragraph refers to, you should have all you need:
import QtQuick 2.0
Item {
id: tabWidget
// Setting the default property to stack.children means any child items
// of the TabWidget are actually added to the 'stack' item's children.
// See the "Property Binding"
// documentation for details on default properties.
default property alias content: stack.children
property int current: 0
onCurrentChanged: setOpacities()
Component.onCompleted: setOpacities()
function setOpacities() {
for (var i = 0; i < stack.children.length; ++i) {
stack.children[i].opacity = (i == current ? 1 : 0)
}
}
Row {
id: header
Repeater {
model: stack.children.length
delegate: Rectangle {
width: tabWidget.width / stack.children.length; height: 36
Rectangle {
width: parent.width; height: 1
anchors { bottom: parent.bottom; bottomMargin: 1 }
color: "#acb2c2"
}
BorderImage {
anchors { fill: parent; leftMargin: 2; topMargin: 5; rightMargin: 1 }
border { left: 7; right: 7 }
source: "tab.png"
visible: tabWidget.current == index
}
Text {
horizontalAlignment: Qt.AlignHCenter; verticalAlignment: Qt.AlignVCenter
anchors.fill: parent
text: stack.children[index].title
elide: Text.ElideRight
font.bold: tabWidget.current == index
}
MouseArea {
anchors.fill: parent
onClicked: tabWidget.current = index
}
}
}
}
Item {
id: stack
width: tabWidget.width
anchors.top: header.bottom; anchors.bottom: tabWidget.bottom
}
}
In cases like this, where you want to replicate something that is done by an item offered by Qt, it can also be helpful to take a look at the source code of what you're trying to replicate. However, the documentation is a bit easier to read. :)

QML ListModel: are multiple ListModels allowed to live on screen?

GridView {
id: gridv
model: ListModel {
id: modelone;
}
delegate: componentId
}
Rectangle {
id: whattheproblem
color: red
ListView {
id: listv
model: ListModel {
id: modeltwo;
}
delegate: anotherComponentId
}
}
I can do gridv.model.append(element), it adds elements to displayed GridView.
But, I can't do listv.model.append(element), it doesn't draw anything (the component code is valid, though), but at the same time, modeltwo.count shows that element is added to model. Rectangle was added to check the layout (it's managed by RowLayout currently), and it seems to be working; other layout things (think anchor, x/y/z) do not help.
QT 5.3, QtQuick 2..
From my point of view, I can only think now, that modelone associates all the ListModel logics to GridView it's created from, so ListModel can't work with ListView anymore. Sounds illogical, but already spent two hours on this.
is there a necessity to create custom Model's, when dealing with multiple views?
I think the problem is with the delegate, since i tried your example with some modifications and it worked.
Following is the code:
Item {
width: 200
height: 200
GridView {
id: gridv
width: 200
height: 100
model: ListModel {
id: modelone;
}
delegate: Text { text: name }
}
Rectangle {
id: whattheproblem
anchors.top: gridv.bottom
ListView {
id: listv
model: ListModel {
id: modeltwo;
}
delegate: Text { text: name }
}
}
Button {
anchors.left: parent.left
anchors.bottom: parent.bottom
text: "Add to Grid"
onClicked: gridv.model.append({name: "grid"})
}
Button {
anchors.right: parent.right
anchors.bottom: parent.bottom
text: "Add to List"
onClicked: listv.model.append({name: "list"})
}
}
I tried it with Qt 5.3.1

Change style property of QML element over event

I have a component which displays a custom combo box:
Column {
anchors.top: parent.top
anchors.topMargin: 200
anchors.left: parent.left
anchors.leftMargin: 30
Rectangle {
id: rectangle1
signal comboClicked
Column {
id: column_button
MenuButtonStyle {
id: buttonCombo
style: MyComboBoxButtonStyle {
label: ButtonTextStyle {
text: "Choose"
}
}
}
}
Column {
id: column1
anchors.left: column_button.right
anchors.leftMargin: -25
anchors.top: parent.top
anchors.topMargin: 3
Image {
id: image1
width: 16
source: "arrow_down.png"
}
}
Keys.onReturnPressed: {
rectangle1.state = rectangle1.state==="dropDown"?"":"dropDown"
console.log("Open Drop Down..")
}
states: State {
name: "dropDown";
PropertyChanges { target: buttonCombo; style: MyButtonStyle }
}
}
}
The goal is to change the style property in "MenuButtonStyle" module.
I tried it with the line
PropertyChanges { target: buttonCombo; style: MyButtonStyle }
but it gives me the error:
file:///D:/projekte/qt_quick/FirstTest/MainPane.qml:82: ReferenceError: MyButtonStyle is not defined
If I replace the "MyComboBoxButtonStyle" directly with "MyButtonStyle" QT did not complain over a not defined reference.
What is the problem? Is it not possible to change the style of a component like it is possible with CSS in HTML?
I am not sure how your MyButtonStyle is written, but you can instantiate MyButtonStyle in your root component and use it throughout the application:
MyButtonStyle { id: myButtonStyle }
or you can define it as a singleton object.
Reference: http://qt-project.org/wiki/QmlStyling

Resources