Qml function with Timer works wrong - qt

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
}
}
}

Related

QML ApplicationWindow zooms in unintentionally and doesn't show all components

So I'm trying to create an application in QML, and doing so by using an ApplicationWindow. The following code is in main.qml, where some of the components are defined in other files (they are not essential for this problem):
import QtQuick 2.12
import QtQuick.Extras 1.4
import QtQuick.Controls 2.5
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.3
import QtDataVisualization 1.3
import Qt3D.Core 2.9
import Qt3D.Render 2.9
import QtCharts 2.3
ApplicationWindow {
id: window
width: 1280
height: 720
//visibility: ApplicationWindow.FullScreen
visible: true
color: "#444444"
Speedometer {
id: speedometer
x: 49
y: 144
width: 306
height: 320
minValue: 0
maxValue: 600
visible: true
}
Thermometer {
id: thermometer
x: 1170
y: 233
scale: 2
minValue: 0
maxValue: 50
visible: true
}
Slider {
id: slider
from: 0
to: 100
x: 28
y: 594
width: 1224
height: 98
font.pointSize: 14
hoverEnabled: false
enabled: false
live: true
snapMode: Slider.NoSnap
value: 0
visible: true
Behavior on value {
NumberAnimation {
duration: 200
}
}
background: Rectangle {
x: slider.leftPadding
y: slider.topPadding + slider.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: 4
width: slider.availableWidth
height: implicitHeight
radius: 2
color: "#999999"
}
handle: Rectangle {
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
y: slider.topPadding + slider.availableHeight / 2 - height / 2
implicitWidth: 26
implicitHeight: 26
radius: 13
color: "#0099ff"
}
}
Timer {
interval: 200
running: true
repeat: true
property var distance: Math.random
onTriggered: update()
}
function update(){
var distance = (Math.random() * 0.03) + 0.1;
var speed = (distance * 50) / 0.02;
slider.value = slider.value + distance;
speedometer.value = speed;
thermometer.value = Math.random() * 25 + 25;
}
DataManager {
id: manager
onNewVelocity: {
lineSeries.append(velocity.x, velocity.y)
chartView.title = name
}
}
Timer {
id: timer
repeat: true
interval: 1
onTriggered: {
manager.dummyData();
}
}
Chart {
id: chart
height: 250
width: 250
x: 1000
}
Text {
id: labelText
color: "white"
width: 300
height: 100
}
Button {
id: startThread
text: "Start"
onClicked: {
timer.start()
}
}
Button {
id: stopThread
text: "Stop"
x: 200
onClicked: {
timer.stop()
}
}
Button {
id: clearGraph
text: "Clear"
x: 100
onClicked: {
lineSeries.clear()
}
}
}
The content itself isn't that important, but the thing that is, is the fact that when I personally run the project, the application window doesn't show what it is supposed to. And I'd like to emphasize: I'm collaborating with others on this exact project, and when they run the exact same code as me, they get different results.
The first image is what I get when running the code:
The second image is what they get, and what is actually supposed to show when running the code:
So I've been trying to search Google for every possible solution, but have found nothing. I've reinstalled Qt and QtCreator, but to no prevail. We are running the exact same thing, but I'm the only one that don't see all components show up. The first image is way more zoomed in than the latter, and I'd really like if anyone knew how to fix this. I've struggled to find anything that could help so far
(Extra note: the code I ran didn't include the graph as it wasn't up to date with current version on git, but the other things remain the same. So the bar below and the thermometer component on the right SHOULD be visible, but they're not).
I eventually found a solution to what seems to be a bug from Qt's end. I had to launch QtCreator via a qtcreator.bat file in the same directory as qtcreator.exe with the following content:
#echo off
set QT_SCALE_FACTOR_ROUNDING_POLICY=Round
set QT_AUTO_SCREEN_SCALE_FACTOR=0
qtcreator.exe
When I now run the project, it shows the correct scale :)

QML repeater item highlight handling

I have implemented the following section
{
id: idLeftArrow
.
.
.
.
}
Row
{
id: idIpEditModeItem
anchors.left: idLeftArrow.right
visible: true
Repeater
{
id: idIpHighlightRepeater
model: 12
Text
{
id: idDigits
text: "0"
font.pointSize: 10
color: "yellow"
}
}
}
Image
{
id: idIpHiglight_Image
width: editModeIPWidth
height: editModeIPHeight
x: idIpHighlightRepeater.itemAt(ipCurrSelectedDigitIndex).x
y: idIpHighlightRepeater.itemAt(ipCurrSelectedDigitIndex).y
visible: false
source: "focus.png"
}
Here I am getting output like this
But I want output like this(there will be a gap between each character)
Also I have a idIpHiglight_Image which is using to highlight each digit. On launch I need output like this
But in my case the highlight is not getting set to the proper location. I am getting output something like this
Could anyone please help me to set the output exactly like this:
Also, on each left and right key press, I need to move the cursor properly to next/previous digit.
I wrote code like
onIpCurrSelectedDigitIndexChanged:
{
if( idIpHighlightRepeater.count == ipCurrSelectedDigitIndex)
{
ipCurrSelectedDigitIndex = 0
}
else if( 0 > ipCurrSelectedDigitIndex)
{
ipCurrSelectedDigitIndex = idIpHighlightRepeater.count - 1
}
}
After executing the code, I am getting error like
[W] (qrc:/common/qml/controls/CustomItem.qml:120) qrc:/common/qml/controls/EditListItem.qml:120: TypeError: Type error
[W] (qrc:/common/qml/controls/CustomItem.qml:119) qrc:/common/qml/controls/EditListItem.qml:119: TypeError: Type error
This the lines were i am getting the above error
I would do 2 different Components for the number and for the delimeter, something like this:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
id: main
visible: true
width: 600
height: 400
Component {
id: number
Text
{
text: "0"
font.pointSize: 16
color: "yellow"
padding: 5
Rectangle {
anchors.fill: parent
color: "transparent"
border { width: 3; color: "orange" }
visible: itemIndex == itemSelected
}
}
}
Component {
id: delimeter
Text
{
text: "."
font.pointSize: 16
color: "yellow"
}
}
Rectangle
{
id: rect
property int selected: -1;
color: "black"
anchors.centerIn: parent
width: layout.width
height: layout.height
Row {
id: layout
Repeater
{
id: repeater
model: 15
delegate: Loader {
id: loader
property int itemSelected: rect.selected;
property int itemIndex: index;
sourceComponent: ((index + 1) % 4 === 0) ? delimeter : number
}
}
}
}
Timer {
interval: 1000
repeat: true
running: true
onTriggered: {
if(rect.selected >= 15)
rect.selected = 0;
else
rect.selected ++;
}
}
}
the result:

Adaptable UI in QML

I am developing a QML application where I have three main views. My code looks like below.
SplitView{
ListView{
id: firstView
}
ListView{
id: secondView
}
WebView{
id: thirdView
}
}
Now this is working fine, but I would like to do this: when my main window is resized below a certain width (500) I would like to show only one view, so that clicking on the delegate will show the next view (with the possibility of going back to the previous view). So for example clicking on the first view will show the second view and clicking on the second view will show the third view. The approach I want is very similar to the Mail application in Windows 10.
Does anyone know how can this be achieved in QML?
OK, I've cooked up a crude example, I didn't modularize it deliberately, so you can see how it works in a single source. Also, I don't know how the windows 10 mail application does it, since I don't have it, but it still close enough to your description.
You begin with 3 list views in a row, which are sized to fill the entire UI, but if you reduce the UI size to the minimum of 500, the views will increase in size to fill almost the entire ui, and when you click on a view item, it will move you to the next view, and if you click the showing previous view, you will be returned back to it.
ApplicationWindow {
title: qsTr("Hello World")
width: 800
height: 300
minimumWidth: 500
visible: true
Item {
id: adapt
width: parent.width
height: parent.height
property int sizeUnit: width > 500 ? width / 5 : 400
property bool isPaged: width == 500 ? true : false
onIsPagedChanged: { if (!isPaged) { x = 0; page = 0; } }
property int page: 0
Behavior on x { NumberAnimation { duration: 250; easing.type: Easing.OutBack } }
ListModel {
id: mod
ListElement { name: "one" }
ListElement { name: "two" }
ListElement { name: "three" }
ListElement { name: "four" }
ListElement { name: "five" }
}
ListView {
id: v1
width: adapt.sizeUnit
height: parent.height
model: mod
delegate: Rectangle {
height: 70
width: v1.width
color: "red"
border.color: "black"
Text { anchors.centerIn: parent; text: name }
MouseArea {
anchors.fill: parent
onClicked: {
if (adapt.isPaged) {
if (adapt.page == 0) {
adapt.x = -(v2.x - 100)
adapt.page = 1
} else {
adapt.x = 0
adapt.page = 0
}
}
}
}
}
}
ListView {
id: v2
width: adapt.sizeUnit
height: parent.height
x: adapt.sizeUnit + 10
model: mod
delegate: Rectangle {
height: 70
width: v2.width
color: "cyan"
border.color: "black"
Text { anchors.centerIn: parent; text: name }
MouseArea {
anchors.fill: parent
onClicked: {
if (adapt.isPaged) {
if (adapt.page == 1) {
adapt.x = -(v3.x - 100)
adapt.page = 2
} else {
adapt.x = -(v2.x - 100)
adapt.page = 1
}
}
}
}
}
}
ListView {
id: v3
width: adapt.isPaged ? adapt.sizeUnit : 3 * adapt.sizeUnit - 20
height: parent.height
x: v2.x + v2.width + 10
model: mod
delegate: Rectangle {
height: 70
width: v3.width
color: "yellow"
border.color: "black"
Text { anchors.centerIn: parent; text: name }
}
}
}
}
This should be enough to get you going. Obviously, for production you can go for more elegant layouting and nagivation, such as use a function for the actual "page sliding" and anchors, the above is just for the purpose of example.
Here is my idea of how to solve the problem.
// When width of MainWindow is smaller than 500 stop using SplitView
property bool useSplitView: (width < 500 ? false : true)
onUseSplitViewChanged: {
if (useSplitView) {
firstView.anchors.fill = undefined
secondView.anchors.fill = undefined
thirdView.anchors.fill = undefined
firstView.parent = splitView // splitView is id of SplitView
secondView.parent = splitView
thirdView.parent = splitView
}
else {
firstView.parent = mainWindow.contentItem // mainWindow is id of MainWindow
secondView.parent = mainWindow.contentItem
thirdView.parent = mainWindow.contentItem
secondView.visible = false
thirdView.visible = false
firstView.anchors.fill = firstView.parent
secondView.anchors.fill = secondView.parent
thirdView.anchors.fill = thirdView.parent
}
}
When useSplitView flag changes you need to enable some kind of touch area on views and switch between them on click.

QML Subwindow does not close as expected

I'm trying to implement a subwindow as a pop up that should disappear when the user clicks outside of it. Following the example set by this question, I came up with this:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
id: win
width: 360
height: 360
color: "black"
Rectangle {
id: block
width: 20
height: 20
color: "green"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
console.log( "Entered" );
menu.visible = true;
menu.requestActivate();
}
}
Window {
id: menu
width: 100
height: 100
x: win.x + block.width
y: win.y + block.height
flags: Qt.Popup
color: "red"
visible: false
onActiveChanged: {
console.log( "Pop up:", active );
if ( !active ) {
visible = false;
}
}
}
}
onActiveChanged: {
console.log( "Main win:", active );
}
}
However the popup does not disappear when clicked outside of, the debug output is:
// Main win opens
qml: Main win: true
// Green square entered
qml: Entered
qml: Main win: true
qml: Pop up: true
// Clicked outside of the pop up
qml: Pop up: true
qml: Main win: true
As you can see the main window does not lose focus when the pop up becomes active, so when the user clicks outside of it the overall focus does not change. So how is this approach supposed to work!?
The solution I found feels quite hackish, but it works, it's self-contained, and it's scalable.
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
id: win
width: 360
height: 360
color: "black"
Rectangle {
id: block
width: 20
height: 20
color: "green"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
menu.visible = true;
}
}
Rectangle {
id: menu
width: 100
height: 100
anchors.top: parent.bottom
anchors.left: parent.right
color: "red"
visible: false
Loader {
id: exitAreaLoader
Component {
id: exitArea
MouseArea {
x: 0
y: 0
parent: menu.getRoot()
width: if ( parent !== null ) { return parent.width }
height: if ( parent !== null ) { return parent.height }
onPressed: {
var pnt = Qt.point( mouse.x - menu.parent.width,
mouse.y - menu.parent.height );
if ( !menu.contains( pnt ) )
{
console.log( "Closing", pnt );
menu.visible = false;
exitAreaLoader.sourceComponent = undefined;
} else {
mouse.accepted = false;
}
}
}
}
}
onVisibleChanged: {
if ( visible ) {
console.log( "Opening" );
exitAreaLoader.sourceComponent = exitArea;
}
}
function getRoot() {
var par = parent;
while ( par.parent !== null ) {
par = par.parent;
}
return par;
}
}
}
}
The general principle is that when the menu/popup/drop down is made visible a Loader creates a MouseArea as the most descendent child in the entire QObject tree (this guarantees that all mouse events within the window will be received by it). However this means that mouse events that are destined for whatever the popup contains are also captured by the MouseArea, so we have to explicitly test for that and reject the event.
It's a real shame that there's not an official object for this, one was requested years ago and it seems to have adopted by Ubuntu, but that's not very useful for cross-platform development.

Timer not working correctly

We have to make progress bar consisting with slider, which has colour transition as the slider proceeds as shown in the figure below.
I tried my hand with below logic but could not get the desired effect. Any help or suggestion how to implement the same.
Below is my code snippet
import QtQuick 1.1
Rectangle {
id: container
width: 500; height: 400
Row {
id:repeaterid
x: 75
y: 280
anchors.bottom: parent.bottom
anchors.bottomMargin: 114
spacing: 4
Repeater {
model: 50
Rectangle {
id: smallrect
color: "red"
width:4
height:4
}
}
}
Timer {
id: progressTimer
interval: 50
running: true
repeat: true
onTriggered: {
if (slider.x < 460)
{
slider.x += repeaterid.spacing + 4
smallrect.color = "green"
}
}
}
Rectangle {
id: slider
x: repeaterid.x
y: repeaterid.y
width: 6; height: 6
color: "blue"
}
}
I have tried to use ColorAnimation, but got any luck.
The timer works correctly. The problem is entirely different: Your try to access smallrect in the onTriggerd handler, an undefined reference outside of the Repeater. Try to solve the problem more declarative:
use an integer property in the container to store the current position of progress bar
in smallrect use the value of that property to set the color (index < position? "green": ... )
use a Timer or better a NumberAnimation to update that property
get rid of slider rectangle, just give the right rectangle in the Repeater a blue color
To access the items within the repeater you can use the itemAt(index) function. This will allow you to change the color of the repeaters children. I also added a indexCurrent property to keep track of the current index.
Try this code:
import QtQuick 1.1
Rectangle {
id: container
width: 500; height: 400
property int indexCurrent: 0
Row {
id:repeaterid
x: 75
y: 280
anchors.bottom: parent.bottom
anchors.bottomMargin: 114
spacing: 4
Repeater {
id: repeater
model: 50
Rectangle {
id: smallrect
color: "red"
width:4
height:4
}
}
}
Timer {
id: progressTimer
interval: 50
running: true
repeat: true
onTriggered: {
if (slider.x < 460)
{
slider.x += repeaterid.spacing + 4
repeater.itemAt(indexCurrent).color = "green"
indexCurrent = indexCurrent + 1
}
}
}
Rectangle {
id: slider
x: repeaterid.x
y: repeaterid.y
width: 6; height: 6
color: "blue"
}
}

Resources