Display time automatically updating every minute - qt

I have an QML application in which I'm trying crate a simple clock that would show current time - similar as those in every operating system.
The time is supposed to be presented to the user as a text in format hh:mm, so i.e. 16:12.
Currently I'm trying a solution with a Timer component running during the application lifetime and updating the text by invoking:
timeText.text = Qt.formatTime(new Date(),"hh:mm")
every 60 seconds. Is there a better way to do this or using a Timer component is necessary.
Snippet with the whole code:
Text {
id: timeText
x: 10
y: 10
text: Qt.formatTime(new Date(),"hh:mm")
}
Timer {
id: timer
interval: 60000
repeat: true
running: true
onTriggered:
{
timeText.text = Qt.formatTime(new Date(),"hh:mm")
}
}

I realise this question is a few years old now, but no solution was given, so here's how I did it (this applies to SailfishOS);
Add a property;
property alias updatesEnabled: timeText.updatesEnabled
Then, the important part;
Item {
id: timeText
color: timeText.color
font.pixelSize: Theme.fontSizeHuge * 2.0
text: { updatesEnabled: timeText.time <--------add line here
Qt.formatTime(new Date(), "hh:mm:ss")
}
anchors {
horizontalCenter: parent.horizontalCenter
}
}
The above now keeps the time/date up-to-date. I played around inserting the code at different lines, but this was the only way I could get it to work.

.qml
Item{
Timer{
interval:60000; running:true; repeat: true
onTriggered: time.text=Tcp.time()
}
Text{
id:time
color: "white"
font.pointSize: 20
}}
.cpp
QString Tcp::time()
{
return QDateTime:currentDateTime().toString("hh:mm");
}

Related

QML how force update / redraw

In my application I have a process which is started by clicking on a button. The button already has a property to indicate when it is busy, this changes the colours to reflect the busy state. When the process is finished the busy property is set back to false.
The problem is that although the steps are:
button.busy = true
... Do something ...
button.busy = false
In reality the button does not change to reflect the busy state until the process is almost complete, it then changes back to non-busy.
Is there anyway I can insert something after setting the busy state to true and before doing something to get the GUI to update and reflect the state?
My button QML:
Button {
id: root
property bool busy: false
property bool showDropShadow: true
property color bottomColour: MerlinStyle.greenButtonBottom
property color iconColour: "white"
property color topColour: MerlinStyle.greenButtonTop
property string icon: ""
opacity: (pressed || !enabled) ? 0.5 : 1.0
onBusyChanged: {
//Set the colours according to busy state
if ( root.busy == true ) {
root.bottomColour = MerlinStyle.indicatorOrange;
root.topColour = MerlinStyle.indicatorOrange;
} else {
root.bottomColour = MerlinStyle.greenButtonBottom;
root.topColour = MerlinStyle.greenButtonTop;
}
}
background: Item {
RadiusRectangle {
id: rect
anchors.fill: parent
radius: MerlinStyle.rectRadius
topLeftPointed: true
gradient: Gradient {
GradientStop { position: 0.0; color: root.topColour }
GradientStop { position: 1.0; color: root.bottomColour }
}
}
DropShadow {
visible: showDropShadow && !pressed && enabled
anchors.fill: rect
horizontalOffset: 1
verticalOffset: 2
color: "#80000000"
source: rect
}
}
contentItem: Item {
ColoredImage {
anchors.centerIn: parent
height: parent.height * 0.85
width: parent.width * 0.85
source: icon
color: root.iconColour
}
}
}
I've tried to trigger an update using:
idOfButton.update
This always results in :
Button_QMLTYPE_28 : Update called for a item without content
The update function takes no parameters.
When you call that function, it simply blocks the GUI thread, and those events which have been put to the event queue will wait until the program returns to the event loop again. That's why you cannot see that the button is updated based on the property changes.
This happens because of bad design. As per Qt documentation:
use asynchronous, event-driven programming wherever possible
use worker threads to do significant processing
never manually spin the event loop
never spend more than a couple of milliseconds per frame within blocking functions
You should not call a blocking function from within the GUI thread. You need to run that function from another thread or if you have a purpose to do that, you can call your function with a Timer which is a dirty hack.
Timer{
id: dummyTimer
interval:1
repeat: false
running: false
onTriggered: {
control.someLazyBlockingFunction();
idOfButton.busy = false;
}
}
Button{
id: anotherButton
...
onClicked:{
idOfButton.busy = true;
dummyTimer.running= true;
}
}
Late answer, but might help someone.
In my case, my application is running on python (PyQt5 + QML), the event comes from QML code, and is handled in a slot (nothing new here).
The thing is, as stated in the qt documentation, you cannot block the main thread, so, the best way I found to handle it was spinning a new daemon thread to run the event code without hanging the frontend.
#pyqtslot(name="doMyButtonCallback")
def do_mybuttoncallback():
def mybuttonwork():
time.sleep(3)
print("i did not hang your code!"
t=threading.Thread(target=mybuttonwork)
t.daemon=True
t.start()

From a numerical spinbox how to prevent the user from typing no numbers in QML

Having a SpinBox that is supposed to have always numbers that can go from a minimum value to a maximum value how can we prevent the user from an editable spinbox to delete the numbers on it, and the result turns into an empty value, like we can see in the top SpinBox:
Im looking to prevent this from happening on a spinbox of the following type:
SpinBox {
editable: true
from:1
to:100
}
i already tried a lot of properties and none of them seem to work.
Looks like you have found a QML bug. SpinBox supposed to validate its value within the from-to range, but it is not happening every time. Here is a crude workaround which involves timer and will write the value into the SpinBox thus forcing to validate its value over and over again when no active focus present. If you have a lot of SpinBoxes, you may consider increasing the interval value of the timer in order to avoid performance degradation.
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("SpinBox bug workaround")
function fixSpinBox(sb)
{
if(!sb.activeFocus) {
var tmp = sb.value
sb.value = sb.from
sb.value = sb.to
sb.value = tmp
}
}
Timer {
interval: 250; running: true; repeat: true
onTriggered: {
fixSpinBox(sb1)
fixSpinBox(sb2)
}
}
Column {
SpinBox {
id: sb1
from: 1
to: 100
editable: true
}
SpinBox {
id: sb2
from: 1
to: 100
editable: true
}
}
}
I think you are trying to disable input.
See "editable": https://doc.qt.io/qt-5/qml-qtquick-controls2-spinbox.html

Rotate multiple 'Text' messages in Qt Quick application

My application has a message area that can fit only one Text item from the below code: But I wanted a logic to rotate the messages Text 1, Text 2 and Text 3 every one second. So that all the three messages can be displayed in my message area.
import QtQuick 2.3
Item {
id: root
width: 480
height: 320
Rectangle {
color: "#272822"
width: 480
height: 320
}
Column {
spacing: 20
Text {
text: 'Text 1'
visible: (timerCount == 1)
}
Text {
text: 'Text 2'
visible: (timerCount == 2)
}
Text {
text: 'Text 3'
visible: (timerCount == 3)
}
}
}
I am able to achieve this using a timer. To set visible property of Text items based on the timerCount.
property int timerCount = 0;
Timer {
id: onesecondtimer
interval: 1000
running: true
repeat: true
triggeredOnStart: true
onTriggered: {
if(++timerCount > 3)
timerCount=1;
}
It works fine, but am not happy with this approach since it gets more difficult if I want to maintain multiple messages (more than 3).
Am just wandering if QtQuick already supports something like this, which am not yet able to find. I tried searching Qt documentation for some time but was not able to find it.
Any help on this is appreciated. Thanks in advance.

QML Moving text with timer

I need to create moving text on the screen continuously from right to left, I have implemented it using QML Timer and Text element.
The below code works fine but I am concerned about the below code causing more cpu or memory usage mainly because the timer is triggered every 33 ms. I have to use this in my places in my application and in multiple instances, like inside many grid windows.
Is this right approach? or does anything exist better than this?
Rectangle{
width:parent.width
height:parent.height
color: "#333333"
Timer {
id: resetTimer
interval: 33
repeat: true
running: true
onTriggered: {
console.log("triggred");
moving_text.x = moving_text.x-1
if(moving_text.x<-1*moving_text.paintedWidth)
moving_text.x=parent.width
}
}
Text{
id:moving_text
x:parent.width
text:"Moving text"
color: "white"
}
}
Why to make things so complected. You could use NumberAnimation on x as follows:
import QtQuick 2.0
Rectangle{
id: root
width:250
height:250
color: "#333333"
Text{
id:moving_text
x:parent.width
text:"Moving text"
color: "white"
NumberAnimation on x{
from: root.width
to: -1*moving_text.width
loops: Animation.Infinite
duration: 3000
}
}
}
As for your concern about memory and cpu usage, you should compare both the methods and check which one suits you. But my personal recommendation is to use NumberAnimation.

Memory leak even if I don't create any qml object dynamically?

I want to create a toolbar, which will rotate and expand when hovered. But the memory leak is so serious that may consume arbitrary RAM, the initial memory usage is about 20MB and reaches 300mb after 1000 animations. I just uses basic components, like ListView, Rectangle and MouseArea. Here is the whole source code. I'm new to qml. Any suggestions would be appreciated, Thanks.
//MyListView.qml
//The ListView will expand when activated.
ListView{
id:root
orientation: ListView.Horizontal
property alias list: listModel
width: calcWidth()
signal optionSelected(string option)
function enableItems(big){
for(var i in root.contentItem.children){
var child = root.contentItem.children[i]
if(child.objectName!=="delegate") continue
if(big){ child.state="big" }
else child.state=""
}
}
function calcWidth(){
var w = 0
for(var i in root.contentItem.children) {
var child = root.contentItem.children[i]
if(child.objectName==="delegate"){
w+=child.width
}
}
return w
}
model: ListModel{
id:listModel
}
delegate: Rectangle{
id: rect
objectName: "delegate"
height: parent.height
width: 0
color:"transparent"
border.color:"grey"; border.width:1; radius: 5
rotation: width/root.height * 360
states:State{
name: "big"
PropertyChanges{
target: rect; width: root.height
}
}
transitions: Transition {
from: "*"
to: "big"
reversible: true
NumberAnimation{
properties: "width";easing.type: Easing.OutQuad; duration: 400
}
}
Image{
anchors.fill: parent
source: imgSrc
}
MouseArea{
anchors.fill: parent
hoverEnabled: true
onEntered: enableItems(true)
onExited: enableItems(false)
onClicked: root.optionSelected(option)
}
}
}
//MyRow.qml
Row{
id:root
width: mainImg.width + listView.width
property alias list: listView.list
property alias mainImg: mainImg.source
signal optionSelected(string option)
Image{
id: mainImg; height: parent.height; width: parent.height
MouseArea{
anchors.fill: parent
hoverEnabled: true
onEntered: listView.enableItems(true)
onExited: listView.enableItems(false)
}
}
MyListView{
id: listView
height: root.height
onOptionSelected: root.optionSelected(option)
}
//test memory usage
Timer{
id: timer
running: true; interval: 400; repeat: true
property real times:0
property bool big :false
onTriggered: {
times ++
big = !big
listView.enableItems(big)
console.log(big+""+times)
if(times>500){
timer.running = false
gc()
}
}
}
}
//main.qml
Rectangle {
width: 360
height: 360
MyRow{
height: 128
mainImg: "qrc:/4.png"
onOptionSelected: console.log(option)
Component.onCompleted: {
list.append({imgSrc:"qrc:/1.png",option:"111"})
list.append({imgSrc:"qrc:/2.png",option:"222"})
list.append({imgSrc:"qrc:/3.png",option:"333"})
}
}
}
Edit1: I was using Qt 5.5.0 + Linux Mint 17.1 + KDE. Everything works fine after I switched to Qt 5.4.2 + windows 10.
I am not convinced that you have actually established a memory leak. The QML engine will cache and it is a common thing to see memory usage rising by 20-30 mb for no apparent reason.
But the memory leak is serious
Without any concrete numbers, one can only guess what you mean by that. Does memory usage rise with more than 30 MB and keep rising? Does it reach a 100? Or more?
Keep in mind JS is garbage collected, and collection in my own experience is far from logical, at least in QML. Sometimes I get non-referenced objects living all the way through the application life cycle, sometimes I get objects which are still in use when QML decides for some reason to delete them and the result is segfaults and crashes.
BTW you can force garbage collection with gc() and see if that does any good.
As already established in this similar post, the "normal" behavior is that memory will go up to a certain point and then stabilize, which I suspect is also happening in your case. You should investigate further and post concrete numbers, and if necessary - a bug report.
EDIT: I tested your example code on Qt 5.42, memory usage quickly reaches 47.068 MB, and after 500 animations it is still there, hasn't moved up even a single kb according to the task manager, it actually goes down a little if the animation is stopped, and returns to 47.068 MB if resumed.
With Qt 5.5 it stabilizes at 51.672 MB
In case you are running literally the same code, your Qt version may have a bug.

Resources