Qt - change property from a component in a different qml file - qt

Update 1
The idea is to be able to change the front and back of CardForm from main.qml because i want to be able to use multiple CardForm instances. I tried to do what they did here but it doesnt work.
Here is the code:
CardForm.qml
import QtQuick 2.0
Flipable {
id: sCard
width: 75
height: 200
property bool flipped: false
property string front: "Front"
property string back: "Back"
property alias callFront : front
property alias callBack : back
front: Rectangle{
id: front
anchors.fill: sCard
border.width: 2
border.color: "black"
radius: 5
Text{
anchors.centerIn: parent
text: sCard.front
}
}
back: Column{
Rectangle{
id: back
anchors.fill: sCard
radius: 5
border.width: 2
border.color: "black"
Text{
anchors.centerIn: parent
text: sCard.front
}
Text{
anchors.centerIn: parent
text: sCard.front
}
}
}
transform: Rotation{
id: flip
origin.x: sCard.width
origin.y: sCard.height/2
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
angle: 0 // the default angle
}
states: State {
name: "back"
PropertyChanges {
target: flip
angle: 180
}
when: sCard.flipped
}
transitions: Transition{
NumberAnimation {
target: flip
property: "angle"
duration: 200
}
}
MouseArea{
anchors.fill: parent
onClicked: sCard.flipped = !sCard.flipped
}
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Neuro Seed")
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Column {
CardForm{
id: test
anchors.centerIn: parent
test.callFront: "Hello World!"
test.callBack: "Bonjour le Monde!
}
}
}
}
Here are the error messages:
SHGetSpecialFolderPath() failed for standard location "Shared Configuration", clsid=0x1c. ()
qrc:/main.qml:17:13: QML CardForm: back is a write-once property
qrc:/main.qml:17:13: QML CardForm: front is a write-once property
qrc:/main.qml:16:9: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column. Column will not function.
the c1.getFront() and getBack() were from a C++ class that I made. I changed these to "Hello World!" and "Bonjour le Monde!"

So after many hours of struggling I figured out that to create a property which is accessible by other .qml files you must create a property alias name: id.property. The id must point towards an existing instance of a object in your code and the property of this instance that you wish to be able to change from the outside. So in my case it would be like so:
CardForm.qml
Flipable {
id: sCard
width: 75
height: 200
property bool flipped: false
property alias frontText : front.text
front: Rectangle{
id: front
anchors.fill: sCard
border.width: 2
border.color: "black"
radius: 5
Text{
anchors.centerIn: parent
text: frontText
}
}
}
and in the main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Neuro Seed")
Rectangle {
anchors.fill: parent
CardForm{
id: test
anchors.centerIn: parent
frontText: "Hello World!"
}
}
}
}

Related

Qt Select At Most 1 Marker on Map

In my code every marker that I clicked are selected(turn into green from red). I want just 1 can change. When I click another marker the marker I clicked before turns red again. Or When I click an empty area the marker I clicked before turns red again.
In qml my Item's code:
Component {
id: hazardous_img
MapQuickItem {
id: hazardousitem
anchorPoint.x: image.width/4
anchorPoint.y: image.height
coordinate: position
property bool isClicked: false
MouseArea {
anchors.fill: parent
onDoubleClicked: {
mainwindow.hazardousIconClicked(mapview.toCoordinate(Qt.point(mouse.x,mouse.y)))
}
onClicked: {
if (isClicked === false) {
image.source = "qrc:/grn-pushpin.png"
isClicked = true
} else {
image.source = "qrc:/red-pushpin.png"
isClicked = false
}
}
}
sourceItem: Image {
id: image
source: "qrc:/red-pushpin.png"
}
}
}
In QML this is usually done with using a ButtonGroup, but as you're not using AbstractButtons you need to write it yourself. Here is my solution for it.
I've used the ListModel to not only store the coordinates of each marker, but also a selected flag which is set to false by default. In the delegate I'm using the selected data role to show if a marker is selected or not.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15
ApplicationWindow {
id: window
width: 640
height: 480
visible: true
title: qsTr("Map")
ListModel { id: markerModel }
Plugin {
id: mapPlugin
name: "osm"
}
Map {
id: map
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 14
MouseArea {
anchors.fill: parent
onDoubleClicked: {
var coordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y))
var jsonObject = JSON.parse(JSON.stringify(coordinate))
jsonObject["selected"] = false
markerModel.append(jsonObject)
}
onClicked: map.deselectAll()
}
MapItemView {
model: markerModel
delegate: markerDelegate
}
function deselectAll() {
for (var i = 0; i < markerModel.count; ++i)
markerModel.setProperty(i, "selected", false)
}
Component {
id: markerDelegate
MapQuickItem {
id: markerItem
required property int index
required property real latitude
required property real longitude
required property bool selected
anchorPoint.x: waypointMarker.width / 2
anchorPoint.y: waypointMarker.height / 2
coordinate: QtPositioning.coordinate(latitude, longitude)
sourceItem: Rectangle {
id: waypointMarker
width: 20
height: 20
radius: 20
border.width: 1
border.color: mouseArea.containsMouse ? "red" : "black"
color: markerItem.selected ? "red" : "gray"
}
MouseArea {
id: mouseArea
hoverEnabled: true
anchors.fill: parent
onClicked: {
map.deselectAll()
markerModel.setProperty(markerItem.index, "selected", true)
}
}
}
}
}
}
I came up with yet another solution without looping over all items in the model. It just stores the index of the selected marker in a dedicated property. This has the drawback that if the model order changes the index can become invalid, also potential multi selection is hard to handle, but on the other hand it is faster because it doesn't need to iterate over all items.
I experimented a lot with DelegateModel, it seems to be a perfect match if one could use it in combination with MapItemView, because of the groups and the attached properties like inGroupName.
After that I've tried ItemSelectionModel, but it seems it is only intended to be used in combination with a view, e.g. TreeView. I couldn't find out how to generate a QModelIndex in QML without a TreeView.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
title: qsTr("Map")
property int selectedMarker: -1
Map {
id: map
anchors.fill: parent
plugin: Plugin {
id: mapPlugin
name: "osm"
}
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 14
MouseArea {
anchors.fill: parent
onDoubleClicked: {
var coordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y))
markerModel.append(JSON.parse(JSON.stringify(coordinate)))
}
onClicked: root.selectedMarker = -1
}
MapItemView {
model: ListModel { id: markerModel }
delegate: markerDelegate
}
Component {
id: markerDelegate
MapQuickItem {
id: markerItem
required property int index
required property real latitude
required property real longitude
anchorPoint.x: waypointMarker.width / 2
anchorPoint.y: waypointMarker.height / 2
coordinate: QtPositioning.coordinate(latitude, longitude)
sourceItem: Rectangle {
id: waypointMarker
width: 20
height: 20
radius: 20
border.width: 1
border.color: mouseArea.containsMouse ? "red" : "black"
color: markerItem.index === root.selectedMarker ? "red" : "gray"
}
MouseArea {
id: mouseArea
hoverEnabled: true
anchors.fill: parent
onClicked: root.selectedMarker = markerItem.index
}
}
}
}
}
I promise this is the last answer on that question.
This one is using an ItemSelectionModel and a few undocumented functions, e.g. ListModel.index(row, col).
itemSelectionModel.hasSelection is used in the color binding to trigger a reevaluation in order to call isRowSelected and set the color accordingly whenever the selection has changed.
If the user clicks on the background the clear() is called to clear the selection.
I think out of the three this is the best solution. It can be easily upgraded to allow multi selection as shown below. Also the ItemSelectionModel can be used by other views to show the data and selection.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15
import QtQml.Models 2.15
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
title: qsTr("Map")
Map {
id: map
anchors.fill: parent
plugin: Plugin {
id: mapPlugin
name: "osm"
}
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 14
MouseArea {
anchors.fill: parent
onDoubleClicked: function(mouse) {
markerModel.append(map.toCoordinate(Qt.point(mouse.x, mouse.y)))
}
onClicked: itemSelectionModel.clear()
}
MapItemView {
model: ListModel { id: markerModel }
delegate: markerDelegate
}
ItemSelectionModel {
id: itemSelectionModel
model: markerModel
}
Component {
id: markerDelegate
MapQuickItem {
id: markerItem
required property int index
required property real latitude
required property real longitude
anchorPoint.x: waypointMarker.width / 2
anchorPoint.y: waypointMarker.height / 2
coordinate: QtPositioning.coordinate(latitude, longitude)
sourceItem: Rectangle {
id: waypointMarker
width: 20
height: 20
radius: 20
border.width: 1
border.color: mouseArea.containsMouse ? "red" : "black"
color: {
itemSelectionModel.hasSelection
return itemSelectionModel.isRowSelected(markerItem.index) ? "red" : "gray"
}
}
MouseArea {
id: mouseArea
hoverEnabled: true
anchors.fill: parent
onClicked: itemSelectionModel.select(markerModel.index(markerItem.index, 0),
ItemSelectionModel./*ClearAnd*/Select)
}
}
}
}
}

How to call data from elsewhere when I click a button in QML

When I click a button in QML, I want the data from another place that I created before to come to the textfields in my table, how can I do it? Can I do this in QML or do I need to create a separate backend cpp file? Can you help me please?
For example this below code is one row of my table
Rectangle{
border.width: 2
border.color: "black"
id:rectangle_mov_mean_nokta_sayisi
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: mainWindow.width/8
Layout.preferredHeight: mainWindow.height/22
Layout.margins: -3
Layout.fillWidth: true
color: row_even
Text{
color:normal_text
id:text_rectangle_mov_mean_nokta_sayisi
text:"Mov Mean Nokta Sayısı"
anchors.centerIn: parent
}
}
Rectangle{
border.width: 2
border.color: "black"
id:rectangle_mov_mean_nokta_sayisi_deger
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: mainWindow.width/8
Layout.preferredHeight: mainWindow.height/22
Layout.margins: -3
Layout.fillWidth: true
color:row_even
TextField {
id:textfield_rectangle_mov_mean_nokta_sayisi_deger
anchors.centerIn: parent
placeholderText: qsTr("")
color:normal_text
}
}
There are 20 of these rectangles. When I click a button, a separate value will be displayed for each text field. I need to create these values elsewhere.
you should read Signal and Slots in QML.
For example :
If you have 3 separate QML files:
In main.qml:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("External Components with signals and slots")
Notifier{
id : notifierId
rectColor: "yellowgreen"
target: receiverId
}
Receiver {
id : receiverId
rectColor: "dodgerblue"
anchors.right: parent.right
}
Component.onCompleted: {
notifierId.notify.connect(receiverId.receiveInfo)//Connect signal to slot
}
}
and in Receiver.qml :
import QtQuick 2.12
Item {
property alias rectColor: receiverRectId.color
width: receiverRectId.width
height: receiverRectId.height
function receiveInfo( count){
receiverDisplayTextId.text = count
console.log("Receiver received number : "+ count)
}
Rectangle {
id : receiverRectId
width: 200
height: 200
color: "red"
Text {
id : receiverDisplayTextId
anchors.centerIn: parent
font.pointSize: 20
text : "0"
}
}
}
and in Notifier.qml:
import QtQuick 2.12
Item {
property alias rectColor: notifierRectId.color
width: notifierRectId.width
height: notifierRectId.height
property int count: 0
signal notify( string count)//Declare signal
property Receiver target : null
onTargetChanged: {
notify.connect(target.receiveInfo)
}
Rectangle {
id : notifierRectId
width: 200
height: 200
color: "red"
Text {
id : displayTextId
anchors.centerIn: parent
font.pointSize: 20
text : count
}
MouseArea{
anchors.fill: parent
onClicked: {
count++
notify(count)
}
}
}
}
you can see that by using signals and slots you can send data from Notifier.qml to Receiver.qml.

qml: How to format column of a listview

I have a list view but all text elements start printing at x position 0.
Is it possible to format the column width? I can set the text element x position by simply
x: 100
But that seems to be the wrong way. How can I set the row width to see the content like a table?
What I currently have prints all elements in the first row.
import QtQuick 2.9
import QtQuick.Controls 1.4
import MyTypes 1.0
ListView {
id: listView
implicitWidth: contentItem.childrenRect.width
anchors.fill: parent
//model: mymodel
model: ExportedListModel {}
delegate: Item {
implicitHeight: text1.height
TextEdit {
id: text1
text: model.heading
Keys.onReturnPressed: model.heading = text
}
TextEdit {
id: text2
text: model.description
Keys.onReturnPressed: model.description = text
}
TextEdit {
id: text3
text: model.quantity
Keys.onReturnPressed: model.quantity = text
}
TextEdit {
id: text4
text: model.someEnum
Keys.onReturnPressed: model.someEnum = text
}
}
}
I think you should specify the width property in your List View and not the implicitWidth. The latter is just a hint.
Besides, what is referenced by your "contentItem" ?
You can specify your delegate's item width as it is done in the second example here :
https://doc.qt.io/qt-5/qml-qtquick-listview.html
EDIT :
Whether it is "width" or "implicitWidth" properties, the example will work.
To use the column, it is important to provide a sufficient height value to your delegate's root item in order to be able to display all the rows in your column.
Here is a working example inspired by yours, you can copy paste it in an empty QtQuick project :
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListModel {
id: mymodel
ListElement {
TEXT: "text_1"
ID: 1
COMMENT: "Comment1"
}
ListElement {
TEXT: "text_2"
ID: 2
COMMENT: "Comment2"
}
ListElement {
TEXT: "text_3"
ID: 3
COMMENT: "Comment3"
}
ListElement {
TEXT: "text_4"
ID: 4
COMMENT: "Comment4"
}
}
ListView {
id: listView
anchors.fill: parent
model: mymodel
delegate:
Rectangle
{
implicitWidth: contentItem.childrenRect.width
implicitHeight: textEdit.height*2
border.color: "black"
border.width: 1
color: "red"
Column{
TextEdit {
id: textEdit
color: "white"
text: TEXT + " number is " + ID
}
TextEdit {
id: textEdit2
color: "white"
text: COMMENT
}
}
}
}
}
Here is a picture of the result :
https://imgur.com/4yoj5gU

QML reference errors

I have a small QML-project and I'm facing a problem with qml component references. So I'm trying to start the NumComponent.qml's numberTimer from startButton in the main.qml.
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
NumComponent{} //my component written in NumComponent.qml
Rectangle{
id: startButton
anchors.centerIn: parent
height: parent.height * 0.2
width: height
color: "lightblue"
MouseArea{
anchors.fill: parent
onClicked: {
numberTimer.start();
}
}
}
}
NumComponent.qml
import QtQuick 2.0
Rectangle {
id: numberRect
color: "red"
height: parent.height * 0.4
width: height
Text{
id: numberText
anchors.centerIn: parent
text: ""
}
Timer{
id: numberTimer
interval: 100
repeat: true
onTriggered: {
numberText.text = Math.floor(Math.random() * 8);
}
}
}
I get this error: "qrc:/main.qml:22: ReferenceError: numberRect is not defined"
Give your NumComponent in main.qml an id:
NumComponent{
id: numComponent
} //my component written in NumComponent.qml
change your onClicked handler to:
numComponent.startTimer();
Another variant:
Add to your numberRect a property alias:
property alias timed: numberTimer.running
Change you onClicked handler in main to:
numComponent.timed = !numComponent.timed;
Add to your NumComponent.qml in your root item:
function startTimer() {
numberTimer.start();
}
Now you can start and stop your timer.

Qt QML How to change size of a component and get the whole page change

I am working on an android application and I am facing a problem. In a page of the application I have some input fields, one of them is for date and I wanted to add a Calendar that open on demand for selecting the date or just enter the date manually, for this, I created a custom component which is composed of a TextInput and a button which when clicked will create the calendar item with a loader and set the size of the loader to 80 (it was 0 initially) all this components are included in a columnlayout. When the button get clicked the calendar is drawn below the other input fields.
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
FocusScope {
id: root
Layout.preferredHeight: 20
property alias text: input.text
property alias border: background.border
property alias backgroundColor: background.color
property alias textColor: input.color
ColumnLayout{
anchors.fill: parent
spacing: 1
RowLayout{
Layout.fillHeight: true
Layout.fillWidth: true
Rectangle {
id: background
Layout.fillHeight: true
Layout.fillWidth: true
color: "darkgrey"
TextInput {
id: input
anchors.fill: parent
anchors.margins: 3
verticalAlignment: TextInput.AlignVCenter
focus: true
text: dateInput.selectedDate
}
}
CustomButton {
id: calandar
Layout.fillHeight: true
Layout.preferredWidth: 40
image: "icons/CalandarButton.svg"
onClicked: {
console.log("clicked calandar")
if(calendarLoader.status === Loader.Null){
calendarLoader.height = 80
calendarLoader.sourceComponent = Qt.createQmlObject("import QtQuick 2.5; import QtQuick.Controls 1.4; Calendar {}",
calendarLoader,
"calandarpp")
}
else{
calendarLoader.height = 0
calendarLoader.sourceComponent = undefined
}
}
}
}
Loader {
id: calendarLoader
Layout.fillWidth: true
height: 0
}
}
}
If something is below, then try changing its z coordinate.
There is no need to do Qt.createQmlObject() ever. It's enough to toggle Loader.active or Item.visible.
Example is not reproducible, make sure that it runs by itself with qmlscene.
This works for me:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
FocusScope {
id: root
Layout.preferredHeight: 20
property alias text: input.text
property alias border: background.border
property alias backgroundColor: background.color
property alias textColor: input.color
z: 1
Loader {
id: calendarLoader
active: false
sourceComponent: Calendar {}
z: 1
}
ColumnLayout {
anchors.fill: parent
spacing: 1
RowLayout{
Layout.fillHeight: true
Layout.fillWidth: true
Rectangle {
id: background
Layout.fillHeight: true
Layout.fillWidth: true
color: "darkgrey"
TextInput {
id: input
anchors.fill: parent
anchors.margins: 3
verticalAlignment: TextInput.AlignVCenter
focus: true
}
}
Button {
id: calandar
Layout.fillHeight: true
Layout.preferredWidth: 40
onClicked: {
console.log("clicked calandar")
calendarLoader.active = !calendarLoader.active
}
}
}
}
}

Resources