I want to update my progress bar every 5 ms to get smooth looking decrasing progress bar. I created timer and progres bar. Problem is that my progres bar looks like it is "jumping" from 100-80-60-40-20, nothing smooth.
import QtQuick
import QtQuick.Controls
ApplicationWindow {
id: root
visible: true
minimumWidth: 840
minimumHeight: 600
property real prgVal1: 100
Timer {
interval: 5
running: true
repeat: true
onTriggered: root.updateProgress()
}
function updateProgress() {
if (root.prgVal1 > 0)
root.prgVal1 -= 0.1
else
root.prgVal1 = 100
}
ProgressBar {
visible: true
width: 120
height: 40
x: 20
y: 50
value: root.prgVal1
from: 0
to: 100
}
}
Can anyone help me please?
Gif can be seen here: https://ibb.co/Wk4w2bn
This isn't an issue caused by your hardware but rather by the operating system. Because you didn't specify a specific style in your application it will pick up the native style of your OS, this is why it works on Ubuntu and not on Windows. The native Windows style of the QQuickProgressBar for some reason only updates in multiple of 5%. I couldn't find the related location in the code to share here.
You can work around the issue by using a different style by default like QtQuick.Controls.Universal.
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Universal
ApplicationWindow {
id: root
visible: true
width: 320
height: 240
Timer {
interval: 5
running: true
repeat: true
onTriggered: {
if (progressBar.value > 0)
progressBar.value -= 0.1
else
progressBar.value = 100
}
}
ProgressBar {
id: progressBar
anchors.centerIn: parent
width: 120
height: 80
from: 0
to: 100
}
}
Just use already existing components, instead of reinventing the things. Think declarative not imperative.
All you need is assign the value, nothing more. The Timer here is for example only.
ProgressBar {
id: progressBar
width: parent.width * 0.8
anchors.centerIn: parent
from: 0
to: 100
value: 0
Behavior on value {
PropertyAnimation { duration: 300; easing.type: Easing.OutBack }
}
}
Timer {
interval: 1000;
running: true;
repeat: true
onTriggered: {
var val = Math.floor(Math.random() * (progressBar.to - progressBar.from + 1) + progressBar.from);
progressBar.value = val;
}
}
[Edit: Original answers deleted]
#WITC okay, I have installed pyside6 (it appears to be based on Qt6.3.0) on Ubuntu 20 and used the following Python to start my application.
# main.py
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')
sys.exit(app.exec_())
Then I did small changes to your QML program so that I can capture all the image frames I'm seeing to keyframe PNGs.
// main.qml
import QtQuick
import QtQuick.Controls 6.3
ApplicationWindow {
id: root
visible: true
minimumWidth: 840
minimumHeight: 600
property real prgVal1:100
property int count: 0
Frame {
id: frame
background: Rectangle {
color: "white"
border.color: "black"
}
ProgressBar {
id: progressBar1
visible: true
implicitWidth: 120
implicitHeight: 40
value: prgVal1
from:0
to: 100
}
}
function step() {
frame.grabToImage(function (res) {
res.saveToFile(`/tmp/img/grab${count}.png`);
count++;
if (prgVal1 >= 1.0) {
prgVal1 -= 1.0;
Qt.callLater(step);
return;
}
} );
}
Component.onCompleted: step()
}
For combining the above image frames into an animated GIF, I used ffmpeg as follows:
# mkanim.sh
ffmpeg -y -framerate 20 -i '/tmp/img/grab%d.png' -vf fps=20,palettegen /tmp/img/pal.png
ffmpeg -y -framerate 20 -i '/tmp/img/grab%d.png' -i /tmp/img/pal.png -lavfi "fps=20 [x]; [x][1:v] paletteuse" anim.gif
You note that the animation I get is smooth.
Related
I'm coding a widget that can be expanded to its FullRepresentation by being clicked on:
onClicked: {
plasmoid.expanded = !plasmoid.expanded
}
I have a function that updates its contents and puts them into a Gridview. The update function is first called from Component.onCompleted, but then I call it every time the user updates the data. The problem is the data I feed into the expandable FullRepresentation can contain a varying number of elements that I put into the gridview and calculate its AND the FullRepresentation's sizes based on this number.
However, I find I'm only able to specify the Representation's size from the Component.onCompleted block. When I call the update function outside it, it doesn't change the size, no matter whether the size is defined in the item properties like so:
Item
{
id: fullRepresentation
width: no_items > 2 ? 465 : no_items * 155
height: Math.ceil(no_items / 3)* 155
property int no_items
...and I only try to change the no_items property from the UpdateFunction, or just try to run the two equations from it.
Is there any way around this? I've also tried the implicitHeight and implicitWidth properties. I really would like to be able to dynamically adjust the size of the main expandable representation, it feels bad to have to hardcode it and then have a lot of unfilled space.
EDIT:
Here is the requested example:
Root.qml
import org.kde.plasma.plasmoid 2.0
import QtQuick 2.2
Item
{
id: root
width: 185; height: 185
Plasmoid.compactRepresentation: Compact {}
Plasmoid.fullRepresentation: Full {}
Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation
signal addItems()
}
Compact.qml
import QtQuick 2.2
Item
{
Rectangle
{
width: root.width; height: root.height
MouseArea
{
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: if (mouse.button == Qt.RightButton) root.addItems()
else plasmoid.expanded = !plasmoid.expanded
}
}
}
Full.qml
import QtQuick 2.2
Item
{
width: no_items > 2 ? 465 : no_items * 155
height: Math.ceil(no_items / 3)* 155
property int no_items : 0
function redrawGridView()
{
readingListModel.clear()
var i
for (i = 0; i < no_items; i++) readingListModel.append({})
}
function addItems()
{
no_items = 6
width = no_items > 2 ? 465 : no_items * 155
height = Math.ceil(no_items / 3)* 155
redrawGridView()
}
ListModel
{
id: readingListModel
}
GridView
{
id: readingGridView
width: count > 2 ? 465 : count * 155
height: Math.ceil(count/3) * 155
cellWidth: 155; cellHeight: 155
model: readingListModel
delegate: Rectangle
{
width: 150; height: 150;
}
}
Component.onCompleted:
{
root.addItems.connect(addItems)
no_items = 3
redrawGridView()
}
}
Well, I got no answer but I've figured this out myself.
Indeed, this doesn't seem to be possible using the plasmoid.expanded method with the Full Representation, and produces other bugs, as I mentioned in my comment.
But you can dynamically resize a Window item size from anywhere in your code, so this works just fine:
in Compact.qml
Full
{
id: fullRep
}
MouseArea
{
anchors.fill: parent
onClicked:
{
if (!fullRep.visible) fullRep.show()
else fullRep.close()
}
}
start of Full.qml
Window
{
x: 100;y: 100
width: 465;height: 195
color: theme.backgroundColor
flags: Qt.FramelessWindowHint //You can make it frameless like the expandable
//representation is when using the previous method
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtMultimedia 5.8
ApplicationWindow {
visible: true
width: 640
height: 480
property bool flip: true
Timer {
interval: 5000
running: true
repeat: true
onTriggered: {
flip = !flip
if(flip) {
videoOutput.source = null
} else {
videoOutput.source = player
}
}
}
VideoOutput {
id: videoOutput
anchors.fill: parent
source: player
}
MediaPlayer {
id: player
source: "file://video.mp4"
autoPlay: true
loops: MediaPlayer.Infinite
}
}
After a few a trigger of Timer that sets videoOutput.source to null, it will never work again. The VideoOutput will just have a stale painting of a previously decoded frame, and will never change.
I want an object, need it to fellow a complex path and move as an animation.
The path is included line and curve. just like a train.
Two solution: 1. PathAnimation or 2. states with multi-animation
Problem of solution 1:
The train maybe stop at a random time-point(pause the animation), and go reverse back to the start position(play animation reversely).
So i want know any way to play PathAnimation reversely?
I think QML doesn't have that functionality.
You could set a new path whenever you need a new one. I.e, when you animation ends.
Here you have an example:
import QtQuick 2.3
import QtQuick.Controls 1.1
ApplicationWindow {
visible: true
width: 350
height: 300
Rectangle {
id: window
width: 350
height: 300
Canvas {
id: canvas
anchors.fill: parent
antialiasing: true
onPaint: {
var context = canvas.getContext("2d")
context.clearRect(0, 0, width, height)
context.strokeStyle = "black"
context.path = pathAnim.path
context.stroke()
}
}
SequentialAnimation {
id: mySeqAnim
running: true
PauseAnimation { duration: 1000 }
PathAnimation {
id: pathAnim
duration: 2000
easing.type: Easing.InQuad
target: box
orientation: PathAnimation.RightFirst
anchorPoint: Qt.point(box.width/2, box.height/2)
path: myPath1
}
onStopped: {
console.log("test")
pathAnim.path = myPath2;
mySeqAnim.start();
}
}
Path {
id: myPath1
startX: 50; startY: 100
PathLine { x: 300; y: 100 }
onChanged: canvas.requestPaint()
}
Path {
id: myPath2
startX: 300; startY: 100
PathLine { x: 50; y: 100 }
onChanged: canvas.requestPaint()
}
Rectangle {
id: box
x: 25; y: 75
width: 50; height: 50
border.width: 1
antialiasing: true
Text {
anchors.centerIn: parent
}
}
}
}
I am new to QML and so I tried a couple of things. Most works, but the Programm always crashes, when I try to change the text-element.
E.g.:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
id: root
visible: true
width: 640
height: 480
Text {
id: mytex
// text: area.mouseX + " / " + area.mouseY
}
MouseArea {
id: area
anchors.fill: parent
// hoverEnabled: true
// onClicked: { mytex.text = mouseX + " / " + mouseY }
onMouseXChanged: { mytex.text = mouseX + " / " + mouseY }
}
}
All I want is a text, that shows the current mouse coordinates (whenever I click or better, whenever I hover the MouseArea )
I use QtQuick 2.3 with mingw on a Windows-Machine.
Edit:
I tried the following code:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property int count: 0
property string countstr: count.toString()
MouseArea {
anchors.fill: parent
onClicked: {
count += 1
console.log(countstr)
}
}
Text {
text: qsTr(countstr + ' times clicked')
anchors.centerIn: parent
}
}
with the QTcreator on both my Windows and my Linux machine.
On Linux it runs flawless.
Therefore I conclude, it should work (and can finally stop the frustrating search for a fault in my code) but I still don't know why it does not work on Windows.
In debugging mode it sais (for the latter code):
qrc:/main.qml:20:5: QML Text: Binding loop detected for property "text"
qrc:/main.qml:20:5: QML Text: Binding loop detected for property "text"
qrc:/main.qml:20:5: QML Text: Binding loop detected for property "text"
qrc:/main.qml:20:5: QML Text: Binding loop detected for property "text"
QQmlExpression: Expression qrc:/main.qml:21:15 depends on non-NOTIFYable properties:
...
Seems mouseX and mouseY don't get updated even if hover is enabled. A short test in qmlscene worked for the code you find on the bottom. Hope it helps.
import QtQuick 2.3
import QtQuick.Window 2.2
Item {
id: root
visible: true
width: 640
height: 480
Text {
id: mytex
}
MouseArea {
id: area
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
mytex.text = mouseX + " / " + mouseY
}
}
}
The solution was simple and easy: My graphic card was running in compatibility mode. Installing the appropriate propriatery driver solved this problem.
Therfore the OpenGL-Engine crashed. -.-'
If QML rendering is hardware accelerated, shouldn't this simple example outperform the equivalent implementation in Qt classic?
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
id: app
visible: true
width: 640
height: 480
Column {
id: cc
property real val: 0
Row {
spacing: 10
TextField {
id: numRows
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
TextField {
id: numCols
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
}
Repeater {
model: numRows.value
Row {
Repeater {
model: numCols.value
Slider {
width: app.width / numCols.value
height: 18.5
value: cc.val
onValueChanged: cc.val = value
}
}
}
}
}
}
I idea is to fill the screen with rows and columns of sliders, and have each slider connect to every other slider. For my screen I use 55 rows and 20 columns. As I move a slider, I expect to see a fluid motion of all the sliders on the screen, yet the frame rate is very low (I would guess 5 to 10 fps). I have a very beefy GPU, and I was expecting much better performance. What could be wrong?
Looking at this under a (CPU, not QML) profiler, the majority of the time spent is actually in rendering the Slider items using native styling. If performance is an important issue, and native desktop styling is not, I would suggest not using QtQuickControls 1, as the styling has a significant amount of overhead.
If I update your example to use QtQuickControls 2, it is significantly smoother:
import QtQuick 2.3
import QtQuick.Controls 2.0
ApplicationWindow {
id: app
visible: true
width: 640
height: 480
Column {
id: cc
property real val: 0
Row {
spacing: 10
TextField {
id: numRows
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
TextField {
id: numCols
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
}
Repeater {
model: numRows.value
Row {
Repeater {
model: numCols.value
Slider {
width: app.width / numCols.value
height: 18.5
value: cc.val
onPositionChanged: cc.val = position
}
}
}
}
}
}
If you get yourself 1100 sliders, you are asking to continuously update 1100 sliders. That is a lot of signals being sent. I will not ask you what you need 1100 synchronized sliders for ...
Although you can inch out a few cycles by only updating cc.val when the slider is pressed, this will not change very much.
All in all, you can only seriously reduce the work being done by choosing a stepSize or setting updateValueWhileDragging: false. You will still see a delay when you release a slider, but the frame rate will not disturb the experience as much.