How to properly dispose Scene3D from QML? - qml

For my project I need to place QtQuick.Scene3D within QtQuick.Component
Scene is successfully created, but when I try to dispose component I get segfault at
0 Qt3D::QCamera::position() const 0xb7226e6b
1 Qt3D::QCamera::translate(QVector3D const&, Qt3D::QCamera::CameraTranslationOption) 0xb7226fa4
It seems that objects are deleted in wrong order. So, there is a question: should I implement whole object graph in C++, or there is a correct way to make Scene3D re-creatable?
There is my component qml file:
import Qt3D 2.0
import Qt3D.Renderer 2.0
import QtQuick.Scene3D 2.0
import QtQuick 2.0 as QQ2
import CeGui 1.0;
import VectorPlot 1.0;
QQ2.Component {
QQ2.Item {
Scene3D {
anchors.fill: parent
id: rootscene
aspects: "input"
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 25.0, -25.0, 25.0 )
upVector: Qt.vector3d( 0.0, 0.0, 1.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
Configuration {
id: cfg
controlledCamera: camera
}
Viewport {
id: viewport
rect: Qt.rect(0.0, 0.0, 1.0, 1.0) // From Top Left
clearColor: Qt.rgba(0, 0.5, 1, 1)
CameraSelector {
id : cameraSelector
camera: camera
ClearBuffer {
buffers : ClearBuffer.ColorDepthBuffer
}
}
}
components: [
FrameGraph {
id: framgraph
activeFrameGraph: viewport
}
]
BarChartScene {
id: bcs
model: GlobalViewModel.harmonicsEditModel
}
}
}
}
}
I use Qt 5.5 for 32-bit gcc in Linux

It seems that it is known bug, which will be fixed in upcoming releases

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

How to rotate only the right or left part of an image

I want to simulate the closure of one page of a book.
Any suggestions on how to do that in qml?
Thanks in advance
Perhaps Flipable, in combination with the fillMode property of Image:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
id: window
width: 640
height: 480
visible: true
Image {
id: backgroundImage
source: "http://www.thebookdesigner.com/wp-content/uploads/2013/04/pages-vs-spreads.png"
anchors.centerIn: parent
Flipable {
id: flipable
anchors.fill: parent
anchors.leftMargin: parent.width / 2
property bool flipped: false
front: Image {
id: foldImage
source: backgroundImage.source
fillMode: Image.Pad
width: foldImage.implicitWidth / 2
horizontalAlignment: Image.AlignRight
}
back: Image {
source: backgroundImage.source
width: foldImage.implicitWidth / 2
fillMode: Image.Pad
horizontalAlignment: Image.AlignLeft
}
transform: Rotation {
id: rotation
origin.x: 0
origin.y: flipable.height / 2
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
angle: 0 // the default angle
}
states: State {
name: "back"
PropertyChanges {
target: rotation;
angle: -180
}
when: flipable.flipped
}
transitions: Transition {
NumberAnimation {
target: rotation
property: "angle"
duration: 1000
easing.type: Easing.InCubic
}
}
MouseArea {
anchors.fill: parent
onClicked: flipable.flipped = !flipable.flipped
}
}
}
}
Flipable does what its name suggests, and the fillMode property in combination with a width that is too small for the entire image allows you to "reposition" the contents of the image within the bounds of the item.
So, the front item contains the right side of the image, and the back item contains the left side. With an actual book that has many pages, you'd have to use the relevant pages instead of the same one.

QML application and screen flickering

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

QML How reverse play animation

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