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.
Related
I have a QML application with 2 rectangles: a large rectangle and a small rectangle inside the large one.
The small rectangle is animated and moves inside the large rectangle.
The animation is done by combining 2 NumberAnimation in a SequentialAnimation.
It works well, except that the to property of one of the NumberAnimation can change.
I would except the change of value to be applied immediately.
However, it is not taken into account until the animations are fully stopped and restarted.
Calling stop()/start() or restart() does not do anything.
I need to wait for the animation to actually finish and then start it again.
This can be demonstrated with the following QML code:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Window {
width: 640
height: 480
visible: true
RowLayout {
Rectangle {
id: topRect
width: 400
height: 400
border {
color: "red"
width: 2
}
Rectangle {
id: animatedRectangle
width: 100
height:100
color: "blue"
}
SequentialAnimation{
id: animation
loops: Animation.Infinite
running: cbAnimate.checked
alwaysRunToEnd: true
NumberAnimation {
id: forwardAnimation
target: animatedRectangle
property: "x"
to: sbWidth.value
duration: 2000
}
NumberAnimation {
id: backwardAnimation
target: animatedRectangle
property: "x"
to: 0
duration: 2000
}
}
}
ColumnLayout {
CheckBox {
id: cbAnimate
text: "Animate"
}
SpinBox {
id: sbWidth
value: 300
to: 400
}
SpinBox {
value: forwardAnimation.to
to: 999
}
}
}
}
Start the animation with the checkbox
Change the value of to with sbWidth
See in the other SpinBox that the value of to was changed
Observe that the animation is still using the old value
Stop the animation, wait for the Rectangle to stop moving, Start the animation
Observe that the animation is using the value set in step 2
Isn't there a way to make the animation use the new value of to immediately?
This behavior is particularly painful when a QML element is animated by default and the to value depends on the geometry of Items, as during the creation of the QML scene Qt will create and then resize Items. Meaning that animation started at creation time won't get their values updated during the resize.
TLDR
In this particular case the best solution is to use the workaround suggedted by #stephen-quan: animate a proxy property property real animatedX between 0.0 and 1.0. And then bind the property I want to animate to this animated property and do the extra computation in this binding: x: animatedX * sbWidth.value. Eliminating the need of changing the to property of the animation.
Details
The issue of animations not taking property change into account until restarted is a very old issue. It has been reported numerous times:
PropertyAnimation ignores from/to changes
Changing an Animation
duration has no effect
Since Qt 6.4, the state has slightly improved. Quoting Qt documentation:
Since Qt 6.4, it is possible to set the from, to, duration, and easing properties on a top-level animation while it is running. The animation will take the changes into account on the next loop.
However, it still does not affect the current loop and requires the animation to be top-level. So even with this improvement, I still need to animate a proxy property, ensuring changes are taken into account in real-time.
I made various changes to your sample.
I introduced from: 0 to your first NumberAnimation. This ensures that whenever you stop/start the animation, it will reset. Also, removing alwaysRunToEnd helps with that.
I introduced a new property double val: 0 property which will range from 0.0 to 1.0. This is what I used NumberAnimation on instead of x. The advantage is, we know that the NumberAnimation will happily move from 0.0 to 1.0 and back to 0.0 consistently.
Then, I introduced a formula linking val to x and takes into account of sbWidth.value.
To make it easier to change sbWidth.value I changed it from a SpinBox to a Slider.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
RowLayout {
Rectangle {
id: topRect
width: 400
height: 400
border {
color: "red"
width: 2
}
Rectangle {
id: animatedRectangle
property double val: 0.0
x: val * sbWidth.value
width: 100
height:100
color: "blue"
Label {
anchors.centerIn: parent
text: parent.x.toFixed(2)
color: "white"
}
}
SequentialAnimation{
id: animation
loops: Animation.Infinite
running: cbAnimate.checked
//alwaysRunToEnd: true
NumberAnimation {
id: forwardAnimation
target: animatedRectangle
property: "val"
from: 0
to: 1.0
duration: 2000
}
NumberAnimation {
id: backwardAnimation
target: animatedRectangle
property: "val"
to: 0
duration: 2000
}
}
}
ColumnLayout {
CheckBox {
id: cbAnimate
text: "Animate"
}
Slider {
id: sbWidth
value: 300
from: 100
to: 400
}
Text {
text: sbWidth.value.toFixed(2)
}
}
}
}
You can Try it Online!
I'm currently learning QML and I want to create a top round transparent window.
I've build something that looks like that, but it seems wrong for multiple reason.
Here's my code:
Window {
id: app
visible: true
width: 70
height: 70
flags: Qt.Window | Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
Rectangle {
anchors.fill: parent
radius: parent.width / 2.0
color: "black"
MouseArea {
property point clickPos: "1,1"
anchors.fill: parent
drag.target: parent
onPressed: {
clickPos = Qt.point(mouse.x,mouse.y)
}
onPositionChanged: {
var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
app.x += delta.x;
app.y += delta.y;
}
onDoubleClicked: app.close()
}
}
}
using these flags in the main :
QQuickWindow::setDefaultAlphaBuffer(true);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
The main problem is that the background is not transparent.
I think it is because the 'round' rectangle is fully painted !?
I've tried multiple flags (Qt.Tool, Qt.Transparent, ...) but none works.
I was wondering if I started well to do what I want (I think I don't) and what is the best way to do it.
I've seen the clipping property for the qml item but I also see there's performance issues. I don't know if it's a good idea to use that property.
I'm running on Qt 5.10 and Win7 using MSVC as compiler.
Thank you
EDIT: Adding transparent background color to the window
Adding an answer, just so I can post an image to prove to you that all you need is to set the color:
Window {
id: app
visible: true
width: 70
height: 70
flags: Qt.Window | Qt.FramelessWindowHint
color: "#00000000"
Rectangle {
anchors.fill: parent
radius: parent.width / 2.0
color: ma.pressed ? "red" : "black"
MouseArea {
id: ma
property point clickPos: "1,1"
anchors.fill: parent
drag.target: parent
onPressed: {
clickPos = Qt.point(mouse.x,mouse.y)
}
onPositionChanged: {
var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
app.x += delta.x;
app.y += delta.y;
}
onDoubleClicked: app.close()
}
}
}
And the result:
I didn't use any of the flags you are setting from C++, maybe setDefaultAlphaBuffer() is breaking it for you.
I've figured it out.
Searching more deeply on the net and thx to #dtech, I found this article
It was the exact same problem as me. But without #dtech, I would never have thought about the graphic card problem, which led me to this solution.
It seems that you need to have the aero mode enable on windows in order to be able to use transparency on Qt.
I activated the aero mode and then retried the given solution (the one of #dtech), it works very nice.
EDIT: It's a well known "bug" on Qt
Now that I have the solution, it seems obvious but I didn't think about it before.
Thx everyone
According to Animation documentation in section "Default Animation as Behaviors", they say that
There are several methods of assigning behavior animations to properties.
One of them is that we should be able to use Behaviour without on property but I don't succeed in having it working.
Here is my example code. I have a colored circle, and changing the color should trigger the ColorAnimation but it doesn't
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
width: 75; height: 75; radius: width
id: ball
color: "salmon"
Behavior {
ColorAnimation { target: ball; duration: 100 }
}
}
Component.onCompleted: timer.start()
Timer{
id: timer
onTriggered: {ball.color = "blue" }
interval: 1000
}
}
If I add on color, it works. I also tried to add property: "color" into ColorAnimation definition but nothing happens.
After browsing the docs a bit more I do not think there is another way to specify the property for Behaviors in QML, even though the animation docs suggest so.
Behavior is a Property Modifier Type more specific a property value write interceptor. Currently Behavior is the only one, see:
http://doc.qt.io/qt-5/qtqml-cppintegration-definetypes.html
Writing Behavior without the on <property> only defines a new Behavior component. To use it, it must be applied on a property. Code from Qt 5 documentation:
// FadeBehavior.qml
import QtQuick 2.15
Behavior {
...
}
Then use that Behavior:
Text {
FadeBehavior on text {}
}
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.
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");
}