QML application and screen flickering - qml

I use windows 10 (updated), Python 3.5 and IDE PyCharm (full updated).
My problem: The screen has flickering when I click on the button.
The button was used to open File dialog window. Application window is showed from the python side via showFullscreen option (code is below).
How can I solve this problem.
MktLabelButton.qml
import QtQuick 2.7
Item {
id: mktLabelButton
x: 10
y: 10
width: 48
height: 48
signal buttonClicked()
property alias imageSource : mktLabelButtonIcon.source
property alias imageLabel : mktLabelButtonText.text
Image{
id: mktLabelButtonIcon
source: imageSource
sourceSize.height: mktLabelButton.height
sourceSize.width: mktLabelButton.width
scale: mktLabelButtonMouseArea.containsMouse ? 1.2 : 0.85
Behavior on scale {
ScaleAnimator {duration: 100}
}
}
Text{
id: mktLabelButtonText
font.bold: true
text: imageLabel
x: (mktLabelButton.width - contentWidth) / 2
y: mktLabelButton.height + 5
color: mktLabelButtonMouseArea.containsMouse ? "yellow":"white"
Behavior on color {
ColorAnimation {duration: 100}
}
}
MouseArea{
id: mktLabelButtonMouseArea
anchors.fill: mktLabelButtonIcon
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: buttonClicked()
}
}
MainForm.py
import sys
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtCore import QUrl, QObject
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView
class MainForm:
def __init__(self, parent):
self.__parent = parent
self.__openButton = parent.findChild(QObject, "openButton")
self.__openButton.buttonClicked.connect(self.openButtonClicked)
def openButtonClicked(self):
fd = self.__parent.findChild(QObject, "mktFileDialog")
fd.setProperty("show", "true")
def show(self):
pass
if __name__ == '__main__':
appQueue = QApplication([])
appEngine = QQmlApplicationEngine()
appEngine.load(QUrl("file:///E:/QML/01012017/Verison1/qml/mainform/mainform.qml"))
appWindow = appEngine.rootObjects()[0]
appWindow.showFullScreen()
mf = MainForm(appWindow)
sys.exit(appQueue.exec_())
MktFileDialog.qml
import QtQuick 2.0
import QtQuick.Dialogs 1.2
Item {
id: mktFileDialog
objectName: "mktFileDialogPY"
property alias show: mktFileDialogMain.visible
FileDialog{
id: mktFileDialogMain
title: "Comet görüntüleri"
visible: show
}
}
mainform.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: root
width: Screen.width
height: Screen.height
color: "black"
MktLabelButton{
objectName: "openButton"
x:50
y:50
imageSource: "img/open.png"
imageLabel: "Open"
}
MktFileDialog{
objectName: "mktFileDialog"
}
}

Related

QML progress bar is not updating smoothly

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.

Is it possible to know default size of the QML Control?

Is it possible to know default size (height) of the QML Control? Something like QWidget::sizeHint()...
I want to set implicitHeight of the TextField to be 8mm, this is fine on desktops, but on Android 8mm is not enough, so I want something like:
implicitHeight: Math.max( minimumCtrlHeight (8mm), defaultHeight )
Maybe this can be done with another approach? Thanks.
Maybe in QML it's possible to use something like #ifdef to set implicitHeight on desktops but not on mobile?
You can do something like this:
import QtQuick 2.15
import QtQuick.Controls 2.15
Rectangle {
id: root
anchors.centerIn: parent;
function preffredButtonHeight(parent_: Item) {
if (Qt.platform.os == "andriod" || "wasm" || "ios") {
return Math.max(parent_.height / 25, 88, implicitHeight);
} else {
return Math.max(parent_.height / 25, 50, implicitHeight);
}
}
Button {
anchors.centerIn: parent;
text: "platform is: " + Qt.platform.os
height: preffredButtonHeight(parent)
}
}
This could be done more declaratively though I think it would be more messy.
You can also implement that JS function in C++, That is what I would do.
Note that you can use Screen.desktopAvailableHeight if you don't want to use parent or use both of them them...
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.2
Rectangle {
id: root
anchors.centerIn: parent;
function preffredButtonHeight(parent_: Item) {
if (Qt.platform.os == "andriod" || "wasm" || "ios") {
return Math.max(Screen.desktopAvailableHeight / 25, 88, implicitHeight);
} else {
return Math.max(Screen.desktopAvailableHeight / 25, 50, implicitHeight);
}
}
Button {
anchors.centerIn: parent;
text: "platform is: " + Qt.platform.os
height: preffredButtonHeight(parent)
}
}

Qml VideoOutput doesn't work after setting source to null once

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.

QML crashes when changing Text.text

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. -.-'

Change calendar style on button click

I need to change the Calendar style when clicking a Button. Currently, in the code below, the style change only works when the object is created for the first time but I need to do style change manually whenever the Button is clicked.
Below is the QML code:
import QtQuick 2.0
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0
import QtQuick.Controls.Styles 1.1
ApplicationWindow {
visible: true
width: 640
height: 400
minimumWidth: 400
minimumHeight: 300
color: "#f4f4f4"
id: root
Calendar {
id: cal_panel
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter;
frameVisible:false
style: CalendarStyle {
gridVisible: false
dayDelegate: Rectangle {
color: styleData.selected ? "#FF2E7BD2" : (styleData.visibleMonth && styleData.valid ? "#191919" : "#191919");
Text {
id:day_txt
text: styleData.date.getDate()
font.bold: true
anchors.centerIn: parent
color: {
var color = "#dddddd";
if (styleData.valid) {
color = styleData.visibleMonth ? "#bbb" : "#444";
var sel = root.getHiglightDates();
for(var i=0;i<sel.length;i++){
if(sel[i]===Qt.formatDateTime(styleData.date,"dd:MM:yyyy"))
color="red"
}
if (styleData.selected) {
color = "black";
}
}
color;
}
}
}
}
}
Button{
anchors.top:cal_panel.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
text:"Higlight"
onClicked: {
console.log("Higlight here....")
}
}
function getHighlightDates(){
var sel = ["10:11:2015","12:11:2015","11:11:2015","08:11:2015","09:11:2015"];
return sel;
}
}
Edit:
The return value of the function getHighlightDates() changes each time. In the snippet above I've just returned a predefined array for testing. In that case I am conduced how to edit style element which is already created.
Here is the screen shot:
As a simple solution, you can reassign the style on click event, forcing an under the hood refresh of the Calendar item.
To do that you can use
cal_panel.style=cal_panel.style
Be aware that this solution is not exactly performance friendly. :-)
Based on the comments in the question and in #folibis's answer, it looks the question might just revolve around how to get the calendar style to reflect the updated list of selected dates (from getHiglightDates()) after a user has updated the list by clicking a button.
What about just adding a new property selectedDates to store the selected dates (previously held in getHighlightDates()) like in the code below. By making use of property binding, the appearance of selected dates will automatically be updated whenever selectedDates changes. In the code below, the color of the "day_txt" Text is updated when selectedData is updated (which in turn is updated when selectedDates is updated).
import QtQuick 2.0
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.1
ApplicationWindow {
visible: true
width: 640
height: 400
minimumWidth: 400
minimumHeight: 300
color: "#f4f4f4"
id: root
property variant selectedDates : ["10:11:2015","12:11:2015","11:11:2015","08:11:2015","09:11:2015"]
Calendar {
id: cal_panel
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter;
frameVisible:false
style: CalendarStyle {
gridVisible: false
dayDelegate: Rectangle {
property bool selectedDate: selectedDates.indexOf(Qt.formatDateTime(styleData.date,"dd:MM:yyyy")) > -1
color: styleData.selected ? "#FF2E7BD2" : (styleData.visibleMonth && styleData.valid ? "#191919" : "#191919");
Text {
id:day_txt
text: styleData.date.getDate()
font.bold: true
anchors.centerIn: parent
color: selectedDate ? "red" : (styleData.selected ? "black" : (styleData.visibleMonth ? "#bbb" : "#444"));
}
}
}
}
Button{
anchors.top:cal_panel.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
text:"Higlight"
onClicked: {
var updatedDates = selectedDates
updatedDates.push(Qt.formatDateTime(cal_panel.selectedDate,"dd:MM:yyyy"))
selectedDates = updatedDates
# See http://stackoverflow.com/questions/19583234/qml-binding-to-an-array-element for why its done this way...
}
}
}
as #skypjack already suggested, you just can assign a new style on click. The style property is a Component so there is no problem to do something like this:
Component {
id: style1
CalendarStyle {
background: Rectangle { color: "lightyellow" }
}
}
Component {
id: style2
CalendarStyle {
background: Rectangle { color: "orange" }
}
}
Calendar {
id: calendar
anchors.fill: parent
style: style1
onClicked: {
calendar.style = style2;
}
}