Loop in QML when using FileDialog - qt

I have some problem with the loop. I'm trying to load img from browserfile and show others in 3s as image - slide. I used while loop
FileDialog {
id: fileDialog
visible: false
title: "Choose a file"
property url defaultz: "E:\IMG"
folder: defaultz
selectMultiple: true
nameFilters: [ "Image files (*.jpg *.png *.bmp)", "All files (*)" ]
onAccepted: {
console.log("You chose: " + fileDialog.fileUrls)
console.log(fileDialog.fileUrls.length)
click.visible = false
//title.visible = false
while(i<fileDialog.fileUrls.length){
loop()
}
}
onRejected: {
console.log("Canceled")
fileDialog.visible = false
click.visible = false
}
Component.onCompleted: visible = false
}
Image {
id: show
visible: false
x:0
y:0
width: 300
height: 300
Timer{
id: tmr
interval: 5000
running: false
repeat: false
onTriggered: {
show.visible = false
}
}
}
function loop(){
show.source = fileDialog.fileUrls[i]
show.visible = true
tmr.running = true
i++
}
When loop() is called, it will run show.source = fileDialog.fileUrls[i] to stopped. After that, show.visible = true and tmr.running will be called.
Someone help me please?

Problem is with while, i value reached to the end before the time triggers.
One solution will be calling the loop on expiry of timer i.e onTriggered and stop the timer after showing all the selected pictures in loop function.
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property int i:0
FileDialog {
id: fileDialog
visible: false
title: "Choose a file"
property url defaultz: "E:\IMG"
folder: defaultz
selectMultiple: true
nameFilters: [ "Image files (*.jpg *.png *.bmp)", "All files (*)" ]
onAccepted: {
console.log("You chose: " + fileDialog.fileUrls)
console.log(fileDialog.fileUrls.length)
//click.visible = false
//title.visible = false
// while(i<fileDialog.fileUrls.length){
// loop()
// }
loop(); // show first picture immediately
tmr.start(); // start timer after selection
}
onRejected: {
console.log("Canceled")
fileDialog.visible = false
//click.visible = false
}
Component.onCompleted: {fileDialog.visible = true
}
}
Image {
id: show
visible: false
x:0
y:0
width: 300
height: 300
Timer{
id: tmr
interval: 5000
running: false
repeat: true
onTriggered: {
show.visible = false
show.visible = false
i++
loop()
console.log("triggered: " + i)
}
}
}
function loop(){
if(i<fileDialog.fileUrls.length)
{
show.source = fileDialog.fileUrls[i]
console.log("showing: " + i + " " + fileDialog.fileUrls[i])
show.visible = true
}else
{
tmr.stop(); // stop the timer
console.log("stopped")
}
}
}

Related

Qml how restoring of bindings works?

I trying to understand how bindings works when dynamic objects are used. And don't understand anything.
In code bellow I have "static" bindings:
property bool flag1: cfg_flag1
and create dynamic binding that set flag1 to true,
then I destroy binding and make sure that it really destroyed (via logs),
after that I trigger initial binding, but looks like binding restoring doesn't work, it prints:
qmlscene /tmp/Test.qml
qml: set flag1 to true
qml: buggon1 cliecked
qml: end of clicked
qml: destroyed
qml: timer trigger
So restoreMode: Binding.RestoreBinding doesn't restore previous binding or I missed something?
import QtQuick 2.0
import QtQuick.Controls 2.15
Rectangle {
id: rect
width: 100
height: 100
color: "red"
property bool flag1: {
console.log("set flag1 to", cfg_flag1);
return cfg_flag1;
}
property bool cfg_flag1: true
Text {
anchors.centerIn: parent
text: "flag1: " + flag1.toString() + ", cfg_flag1 " + cfg_flag1.toString()
}
Timer {
id: timer
interval: 1000
repeat: false
onTriggered: {
console.log("timer trigger");
cfg_flag1 = false;
}
}
Button {
anchors.top: parent.top
text: "button 1"
onClicked: {
console.log("buggon1 cliecked");
let temp = cmpBinding.createObject(rect, {
"target": rect,
"property": "flag1",
"value": true,
"restoreMode": Binding.RestoreBinding,
});
temp.Component.onDestruction.connect(function() { console.log("destroyed"); });
temp.destroy();
console.log("end of clicked");
timer.start();
}
}
Component {
id: cmpBinding
Binding {
}
}
}
Your code is OK. This is just another bug of Binding =/.
To make it work, you can add
"when": true to list of properties for temp
temp.when = false; on temp's destruction
Here is your edited code
import QtQuick 2.0
import QtQuick.Controls 2.15
Rectangle {
id: rect
width: 100
height: 100
color: "red"
property bool flag1: {
console.log("set flag1 to", cfg_flag1);
return cfg_flag1;
}
property bool cfg_flag1: true
Text {
anchors.centerIn: parent
text: "flag1: " + flag1.toString() + ", cfg_flag1 " + cfg_flag1.toString()
}
Timer {
id: timer
interval: 1000
repeat: false
onTriggered: {
console.log("timer trigger");
cfg_flag1 = false;
}
}
Button {
anchors.top: parent.top
text: "button 1"
onClicked: {
console.log("buggon1 cliecked");
let temp = cmpBinding.createObject(rect, {
"target": rect,
"property": "flag1",
"value": true,
"restoreMode": Binding.RestoreBinding,
"when": true
});
temp.Component.onDestruction.connect(function() { temp.when = false; console.log("destroyed"); });
temp.destroy();
console.log("end of clicked");
timer.start();
}
}
Component {
id: cmpBinding
Binding {
}
}
}

QML disable button after other button_click event and enable again after timer timeout

I would disable button after other button_click event and enable again after timer timeout. I tried with Timer and States with PropertyChanged, but it doesn't work.
How I can send a qml signal to reload/refresh the page?
When i click on id_button_add I would disable Id_button_edit and start a timer for 3 seconds. When the timer timeouts I enable already the id_button_edit.
Here is my code.
import QtQuick 2.0
import QtQuick.Layouts 1.3
import "../items"
import ch.example 1.0
Item {
id: id_root
z: 2
property bool prepare_delete: false
property string button_edit_enable_state: "enabled"
anchors.left: parent ? parent.left : undefined
anchors.right: parent ? parent.right : undefined
Connections {
target: id_root.ListView.view
onCurrentIndexChanged: {
prepare_delete = false
}
}
function update_remove_enable() {
id_button_remove.button_enabled = ui_resident_model.sourceModel.rowCount() > 1
}
Component.onCompleted: {
ui_resident_model.sourceModel.onRowsInserted.connect(update_remove_enable)
ui_resident_model.sourceModel.onRowsRemoved.connect(update_remove_enable)
update_remove_enable()
}
Rectangle {
anchors.fill: parent
color: "transparent"
border {
color: touchpanel_style.foreground_color
width: touchpanel_style.line_width
}
}
RowLayout {
anchors.left: parent.left
anchors.margins: touchpanel_style.margin
anchors.verticalCenter: parent.verticalCenter
.
.
.
.
}
RowLayout {
id: id_content
anchors.right: parent.right
anchors.margins: touchpanel_style.margin
anchors.verticalCenter: parent.verticalCenter
state: button_edit_enable_state
states: [
State {
name: "enabled"
PropertyChanges { target: id_button_edit; enabled: true }
},
State {
name: "disabled"
PropertyChanges { target: id_button_edit; enabled: false }
}
]
Timer {
id: serviceListItemTimer
interval: 3000
running: false
repeat: false
onTriggered: {
console.log("onTriggered")
button_edit_enable_state = "enabled"
}
}
IconButton {
id: id_button_edit
objectName: "button_edit"
Layout.fillHeight: true
width: height
icon_factor: 1.35
icon_source: "../icons/edit.png"
onButtonClick: {
prepare_delete = false
loader.source = "../service_menu/ResidentEdit.qml";
loader.item.onCancel.connect(cancel_edit)
loader.item.onTimeout.connect(timeout)
loader.item.model = id_root.ListView.view.currentItem.resident_model
}
function cancel_edit() {
loader.source = ""
}
}
IconButton {
id: id_button_add
objectName: "button_add"
Layout.fillHeight: true
width: height
icon_factor: 1.35
icon_source: "../icons/resident_add.svg"
onButtonClick: {
console.log("btn_add")
button_edit_enable_state = "disabled"
serviceListItemTimer.running = true
var index = id_root.ListView.view.currentIndex;
id_root.ListView.view.model.createItem(index);
set_index(index);
}
}
}
}
If you use enabled property of the item, you can disable the button.
For example:
IconButton {
id: id_button_edit
...
enabled: true
...
onButtonClick: {
id_button_edit.enabled = false;
...
}
Then the timer expire, you return to enable the button with id_button_edit.enabled = true.
Maybe an another solution can be the signal.
Here is more info:
https://doc.qt.io/qt-5/qtqml-syntax-signals.html
You can do it in a simple an declarative way by binding the enabled property of your edit button to the running property of the timer, no need to deal with states and such:
IconButton {
id: id_button_add
// ...
onButtonClick: {
serviceListItemTimer.start();
var index = id_root.ListView.view.currentIndex;
id_root.ListView.view.model.createItem(index);
set_index(index);
}
}
IconButton {
id: id_button_edit
enabled: !serviceListItemTimer.running
// ...
}
I found a solution --> bind qml property with cpp model and emit also in cpp module valueChanged() signal.
see http://imaginativethinking.ca/bi-directional-data-binding-qt-quick/

Why can't QML key events be intercepted?

I have the following code:
MyTest.qml
import QtQuick 2.0
FocusScope {
anchors.fill: parent
Keys.onReturnPressed: {
console.log("++++++++++")
}
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MyTest {
focus: true
Keys.onReturnPressed: {
console.log("==========")
event.accepted = true
}
}
}
The output is:
++++++++++
==========
What is event.accepted = true invalid?
I want to intercept keystroke events in Window and process the event on ly in Window (only output "=========="). How to do that?
You can't disconnect a method when you use onReturnPressed: {} definition.
You have to use Connections for that.
A quick example:
import QtQuick 2.9
import QtQuick.Controls 1.4
Item {
id: myItem
anchors.fill: parent
property bool acceptEvents: true
signal returnPressed()
Keys.onReturnPressed: returnPressed()
Connections {
id: connection
target: myItem
enabled: acceptEvents
onReturnPressed: {
console.log("++++++++++")
}
}
}
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
VolumeSlider {
id: obj
anchors.fill: parent
focus: true
Keys.onReturnPressed: {
console.log("==========")
event.accepted = true
}
Component.onCompleted: {
obj.acceptEvents = false; // Remove connections
}
}
}

Qml function with Timer works wrong

I have a QML project, there I have a Timer class. When I run code, there is just white window without anything. I expected what when I press "Up" button, there is a yellow rectangle showed for 5 seconds, and on this rectangle will be written number "5" for 1 second, then "4" for 1 second and so on and after 5 seconds this rectangle will disappear.
But my code works differently, and I really confused.
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Timer {
id: timer
function setTimeout(cb, delayTime) {
timer.interval = delayTime;
timer.repeat = true;
timer.triggered.connect(cb);
timer.start();
}
}
Rectangle{ // This is invisible rectangle
width: 100
height: 100
focus: true
Keys.onPressed: { //
if (event.key == Qt.Key_Up){ // this is function where I change values of number
console.log(tf.counter); //this is for debugging
tf.counter = 5; // tf is id of Text
test.visible = true; // test is id of yellow rectangle
timer.setTimeout(function(){ //setTimeout is function defined in Timer
tf.counter--;
if (tf.counter > 0){
tf.text = tf.counter;
}
else {
tf.counter = 5;
tf.text = tf.counter;
test.visible = false;
timer.stop();
}
}, 1000);
}
}
}
Rectangle{
id: test
visible: false // now this is invisible
color: "yellow"
width: 100
height: 100
x: 200
y: 200
Text {
x: 5
y: 5
property int counter: 5 // this is custom property which I assign to tf.text
id: tf
text: "5"
}
}
}
When I press "Up" button for the first time, it works totally fine, but then I press "Up" for the second time and after that it works so weird, I don't understand why. In second time, it gives me "5", then "3", then "1". In third time, it gives me "4", "1". And then my rectangle shows only for one second and there is always random number on it. Please help me. I tried really hard on solving why this code doesn't work properly.
You are complicating with logic, if the following rules are established, the logic is simple:
When you press the Up key, you must start the timer and make the Rect visible.
Each time a second elapses with the Timer, the counter is decreased by 1.
When the counter reaches 0, the timer must be stopped, the Rect invisible and the counter set to 0 again.
*.qml
Window {
visible: true
width: 640
height: 480
Timer {
id: timer
onTriggered: tf.counter--; // 2
interval: 1000
repeat: true
}
Rectangle{ // This is invisible rectangle
width: 100
height: 100
focus: true
Keys.onPressed: if (event.key === Qt.Key_Up){rect.visible = true; timer.start()} // 1
}
Rectangle{
id: rect
visible: false
color: "yellow"
width: 100
height: 100
x: 200
y: 200
Text {
id: tf
x: 5
y: 5
property int counter: 5
onCounterChanged: if(counter == 0){rect.visible = false; timer.stop(); counter=5} // 3
text: counter
}
}
}
Another solution:
Window {
visible: true
width: 640
height: 480
Timer {
id: timer
onTriggered: tf.counter--;
interval: 1000
repeat: true
running: tf.counter > 0
}
Rectangle{ // This is invisible rectangle
width: 100
height: 100
focus: true
Keys.onPressed: if (event.key === Qt.Key_Up && !timer.running){tf.counter = 5}
}
Rectangle{
id: rect
visible: timer.running
color: "yellow"
width: 100
height: 100
x: 200
y: 200
Text {
x: 5
y: 5
property int counter: 0
id: tf
text: counter
}
}
}

Make effective property alias, using Connections, Binding or Qt.binding

Is it possible to make mutual connection (without loop issue), using Connections, Binding or Qt.binding()?
It is possible to connect, say, SwipeView.currentIndex to TabBar.currentIndex and vise versa. When I page through SwipeView, then current tab of TabBar is also changed and vice versa. There is no binding loop of properties.
How to achieve this in Repeater? When some item created by Repeater became selected (in some sense), then I want to rebind its properties to another standalone item, which operates like editor of the selected item. The state of elements in selected item should depend on the state of items into the editor. But on selection changing I need to initialize items in editor using values from newly selected item.
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
ApplicationWindow {
id: root
property var currentDayOfWeek: { "enabled": false, "time": "08:00" }
visible: true
SystemPalette {
id: palette
}
RowLayout {
anchors.centerIn: parent
Column {
ButtonGroup { id: weekButtonGroup }
Repeater {
model: 7
RowLayout {
Settings {
id: dayOfWeekSettings
category: Qt.locale("C").standaloneDayName(index, Locale.LongFormat)
property bool enabled: false
property string time: "08:00"
}
Label {
text: dayOfWeekSettings.time
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
color: dayOfWeekSettings.enabled ? palette.highlight : palette.base
}
Layout.fillHeight: true
}
RadioButton {
text: Qt.locale().standaloneDayName(index, Locale.LongFormat)
onCheckedChanged: {
if (checked) {
root.currentDayOfWeek = dayOfWeekSettings
timeField.text = Qt.binding(function() { return dayOfWeekSettings.time }) // ???
enabledCheckBox.checked = Qt.binding(function() { return dayOfWeekSettings.enabled }) // ???
}
}
ButtonGroup.group: weekButtonGroup
Layout.fillHeight: true
}
}
}
Layout.fillHeight: true
}
Column {
// Editor
TextField {
id: timeField
text: currentDayOfWeek.time // ???
inputMask: "00:00;_"
inputMethodHints: Qt.ImhDigitsOnly
}
CheckBox {
id: enabledCheckBox
checked: currentDayOfWeek.enabled // ???
}
Layout.fillHeight: true
}
}
}
How to achieve this? Is there canonical way to do this? Above example is not the solution.
The following code works as I want:
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
ApplicationWindow {
id: root
//property var currentDayOfWeek: { "enabled": false, "time": "08:00" }
visible: true
SystemPalette {
id: palette
}
RowLayout {
anchors.centerIn: parent
Column {
ButtonGroup { id: weekButtonGroup }
Repeater {
model: 7
RowLayout {
Settings {
id: dayOfWeekSettings
category: Qt.locale("C").standaloneDayName(index, Locale.LongFormat)
property bool enabled: false
property string time: "08:00"
Binding on enabled {
when: dayOfWeekSettingsCheckBox.checked
value: enabledCheckBox.checked
}
Binding on time {
when: dayOfWeekSettingsCheckBox.checked
value: timeField.text
}
}
Label {
text: dayOfWeekSettings.time
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
color: dayOfWeekSettings.enabled ? palette.highlight : palette.base
}
Layout.fillHeight: true
}
RadioButton {
id: dayOfWeekSettingsCheckBox
text: Qt.locale().standaloneDayName(index, Locale.LongFormat)
onCheckedChanged: {
if (checked) {
//root.currentDayOfWeek = dayOfWeekSettings
timeField.text = dayOfWeekSettings.time
enabledCheckBox.checked = dayOfWeekSettings.enabled
}
}
ButtonGroup.group: weekButtonGroup
Layout.fillHeight: true
}
}
}
Layout.fillHeight: true
}
Column {
id: editor
TextField {
id: timeField
//text: currentDayOfWeek.time
inputMask: "00:00;_"
inputMethodHints: Qt.ImhDigitsOnly
}
CheckBox {
id: enabledCheckBox
//checked: currentDayOfWeek.enabled
}
Layout.fillHeight: true
}
}
}
but there is an issue: editor filled with values in onCheckedChanged when checked became true. But what if when: in Bindings shoot first? Is it possible? Should I use delayed?
If I comment out:
timeField.text = dayOfWeekSettings.time
enabledCheckBox.checked = dayOfWeekSettings.enabled
and uncomment all the commented in above solution, then also all works fine. But the suspicion still persist.

Resources