In my program, there are QtWaylandCompositor which contains two windows for each screen.
WaylandCompositor {
WaylandOutput {
window: Window {
id: screen1
}
}
WaylandOutput {
window: Window {
id: screen2
}
}
}
I want to control the maximum FPS of each window surface.
one screen performs max 30 FPS and another max 60 FPS.
So I am looking for a solution like QT_QPA_UPDATE_IDLE_TIME not for the process but the window.
QPlatformWindow subclasses can re-implement this function to provide
display refresh synchronized updates.
I am trying to reimplement QPlatformWindow::requestUpdate https://code.woboq.org/data/symbol.html?root=../qt5/&ref=_ZN15QPlatformWindow13requestUpdateEv,
but I don't know how to put the customized QPlatformWindow class into QQuickWindow.
https://code.woboq.org/qt5/qtbase/src/gui/kernel/qwindow.h.html#_ZNK7QWindow6handleEv
Related
I understand that I can get to know if the width or height of a QML item changed using the slots onWidthChanged and onHeightChanged. This is by doing something like below.
import QtQuick 2.12
Item {
id: my_item
onWidthChanged: {
if (my_item.visible) {
console.log("Dimension chnaged")
}
}
onHeightChanged: {
if (my_item.visible) {
console.log("Dimension chnaged")
}
}
}
Above works well. But I am only interested to know if the dimension of my QML item changed. I just need a callback when width or the height changed. I don't need a callback for both.
Is there a QML signal to listen only for a dimension change?
I am using Qt 5.15.7 commercial version.
As a workaround, you can create a property which is bound to both width and height and connect a handler to the changed signal of that property:
property double dimensions: width * height
onDimensionsChanged: {
if(my_item.visible)
console.log("Dimension changed")
}
There is a small risk, namely the number won't change when swapping width & height, but that might be worth it in your situation
As mentioned in the comments no such signal currently exists by default.
You can create a size or rect type property and react on that instead though:
property size dimensions: Qt.size(width, height)
onDimensionsChanged: console.log("Dimension changed")
Here's a WASM example of it working to power an emitter:
https://www.canonic.com/#https://playground.canonic.com/525f6691-fe05-4824-a7f1-574bb8cabd8b/dimension-changed-signal-example
Suppose there is a code like this (sorry for the contrived code)
Window {
id: window
visible: true
width: 700
height: 700
MediaPlayer {
id: mediaplayer
source: "test.avi"
autoPlay: true
}
VideoOutput {
anchors.fill: parent
source: mediaplayer
}
Repeater {
id: repeater
property int n: 1
model: 1
Label {
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max))
}
id: label
y: getRandomInt(window.width)
x: getRandomInt(window.height)
text: "label"
}
}
Timer {
interval: 10
running: true
repeat: true
onTriggered: {
repeater.n += 1
if (!(repeater.n % 100)) {
repeater.model = repeater.n
}
}
}
}
When the number of labels increases, the video starts to break. How to start a video in a separate thread so that manipulations with any widgets do not interrupt it (video).
You should not overload main thread (with it's event loop). Otherwise, whole the software will lag, not only VideoOutput.
Don't move to another thread well-optimized and efficient controls. Move to another thread huge things, hard & long computations.
So
avoid using timers with low interval or if you use them, don't attach to them hard things
if you should create many or hard controls in QML, then use Loader or QQmlIncubator, they allow to create QML controls in a separate threads. Also Loader allows dynamically load and unload needed/unneeded controls. So, QML engine will not render too many controls, most of which even are not visible.
Note about Loader/QQmlIncubator: they create control in a separate thread, not run it there.
avoid writing custom properties and functions inside QML controls, place them in some single QtObject: QtObject { id: internals; ...all your custom data... }
Use ListView instead of Repeater. It's more efficient because it instantiates only visible items (and several out of visible area) - probably the most important for your sample
More advice and samples you can find here:
Performance Considerations And Suggestions: https://doc.qt.io/qt-5/qtquick-performance.html
QML performance tips and tricks: https://katastrophos.net/harmattan-dev/html/guide/html/Developer_Library_Best_practices_for_application_development_QML_performance_tips_and_tricks.html
QtObject: https://doc.qt.io/qt-5/qml-qtqml-qtobject.html
My objective is to allow a user to edit a MapPolyLine using the mouse to drag and drop points on the line. I have implemented this by creating a C++ model that exposes a path() method for dumping all points to draw the line.
I then used this model to display a set of MapCircles for each vertex on the line. These Mapcircles have drag events which update the MapPolyLine model after drag has finished.
This works a treat for lines with < 500 vertices. Then performance suffers really badly. Some of my lines (from GPX files) have 10,000 vertices.
So I tried to only expose MapCircles on the line close to the mouse hover point. But (unless I am mistaken) the MouseArea for a MapPolyLine appears to be the bounding box of the line and hover can be triggered in strange situations.
I found that the onEntered event worked well enough for me to work out the position on the line and then display a set of MapCircles close to that position. It is a sub-optimal solution though as the user has to actively click the line to get these MapCircles to show.
My question is: "am I using the right strategy to allow a MapPolyLine to be edited?". I cannot see any other way of doing it in the current implementation of QtLocation.
The code is now quite complex. It's structure is:-
GPXModel {
id: gpxModel
}
MapPolyLine {
path: gpxModel.path
MouseArea {
onEntered: {
var mapCoord = gpxLine.mapToItem(mapView,mouseX,mouseY);
var coord = mapView.toCoordinate(Qt.point(mapCoord.x,mapCoord.y));
//tell the model where we are in the array of points so we can display MapCircles around this point
gpxModel.setEditLocationFromCoordinate(coord);
}
}
}
MapItemView {
model: gpxModel
MapCircle {
//this is a custom role I use to return a sub-set of points set by setEditLocationFromCoordinate
center: positionRole
MouseArea{
anchors.fill: parent
drag.target: parent
onDragActiveChanged: {
if(!drag.active){
gpxModel.updatePositionOfMarker();
}
}
}
}
}
The project is open sourced at https://github.com/citizenfish/servalan_2020 with the caveat that I am using this as a learning exercise so it's all a bit hacked together.
EDIT: I do not want to use webview based solutions. The point of the project was to do this in native Qt with minimal reliance on third party javascript libraries/browsers.
We have a fairly big QtQuick application, with a lot of modal dialogs. All of these modals share a consistent look and behaviour, and have leftButtons, rightButtons, a content and additional warning widgets. We use the following base class (PFDialog.qml):
Window {
property alias content: contentLayout.children
ColumnLayout {
id: contentLayout
}
}
and declare dialogs in the following way (main.qml):
Window {
visible: true
property var window: PFDialog {
content: Text { text: "Foobar" }
}
}
The problem is that when the application is closed, a segfault happens in the QQuickItem destructor. This segfault is hard to reproduce, but here is a surefire way of making it happen: with visual studio in debug mode, freed memory is filled with 0xDDDDDDD with triggers the segfault every time.
Full example application can be found here: https://github.com/wesen/testWindowCrash
The crash happens in QQuickItem::~QQuickItem:
for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate();
if (anchor)
anchor->clearItem(this);
}
The reason for this is that the content of our dialog (the Text item in the example above) is a QObject child of the main Window, but a visual child of the dialog window. When closing the application, the dialog window is destroyed first, and at the time the Text item is deleted, the dialog window (still registered as a changeListener) is stale.
Now my question is:
is this a QtQuick bug? Should the dialog deregister itself as a changeListener for its children when it is destroyed (I think it should)
is our property alias content: layout.children pattern correct, or is there a better way to do this? This also happens when declaring a default property alias.
For the sake of completeness, here is how we hotfix it in our application. When content changes, we reparent all the items to the layout item. A of elegance, as you will all agree.
function reparentTo(objects, newParent) {
for (var i = 0; i < objects.length; i++) {
qmlHelpers.qml_SetQObjectParent(objects[i], newParent)
}
}
onContentChanged: reparentTo(content, contentLayout)
I have had this problem lots of times, I don't think it is a bug, more like a design limitation. The more implicit behavior you get, the less control you have, leading to inappropriate orders of object destruction and access to dangling references.
There are numerous situations where this can happen "on its own" as you exceed the bounds of a trivial "by the book" qml application, but in your case it is you who's doing it.
If you want proper ownership, don't use this:
property var window: PFDialog {
content: Text { text: "Foobar" }
}
Instead use this:
property Window window: dlg // if you need to access it externally
PFDialog {
id: dlg
content: Text { text: "Foobar" }
}
Here is a good reason why:
property var item : Item {
Item {
Component.onCompleted: console.log(parent) // qml: QQuickItem(0x4ed720) - OK
}
}
// vs
property var item : Item {
property var i: Item {
Component.onCompleted: console.log(parent) // qml: null - BAD
}
}
A child is not the same as a property. Properties are still collected but they are not parented.
As for achieving the "dynamic content" thingie, I've had good results with ObjectModel:
Window {
property ObjectModel layout
ListView {
width: contentItem.childrenRect.width // expand to content size
height: contentItem.childrenRect.height
model: layout
interactive: false // don't flick
orientation: ListView.Vertical
}
}
Then:
PFDialog {
layout: ObjectModel {
Text { text: "Foobar" }
// other stuff
}
}
Lastly, for the sake of doing explicit cleanups before closing the application, on your main QML file you can implement a handler:
onClosing: {
if (!canExit) doCleanup()
close.accepted = true
}
This ensures the window will not be destroyed without doing the cleanup first.
Finally:
is our property alias content: layout.children pattern correct, or is
there a better way to do this? This also happens when declaring a
default property alias.
It wasn't last time I looked into it, but it was at least couple of years back. It would certainly be nice to have objects declared as children actually becoming children of some other object, but at the time this was not possible, and still may not be. Thus the need for the slightly more verbose solution involving the object model and the list view. If you investigate the matter and find something different, leave a comment to let me know.
I believe that you cannot declare a Window Object in a var. In my tests the SubWindow never open and sometimes broken on startup.
A Window can be declared inside an Item or inside another Window; in that case the inner Window will automatically become "transient for" the outer Window
See: http://doc.qt.io/qt-5/qml-qtquick-window-window.html
Modify your code to this:
Window {
visible: true
PFDialog {
content: Text { text: "Foobar" }
}
}
In QtQuick 2 using the QtQuick Controls you can create complex desktop apps. However it seems to me that the entire UI must be declared and create all at once at the start of the app. Any parts that you don't want to use yet (for example the File->Open dialog) must still be created but they are hidden, like this:
ApplicationWindow {
FileDialog {
id: fileOpenDialog
visible: false
// ...
}
FileDialog {
id: fileSaveDialog
visible: false
// ...
}
// And so on for every window in your app and every piece of UI.
Now, this may be fine for simple apps, but for complex ones or apps with many dialogs surely this is a crazy thing to do? In the traditional QtWidgets model you would dynamically create your dialog when needed.
I know there are some workarounds for this, e.g. you can use a Loader or even create QML objects dynamically directly in javascript, but they are very ugly and you lose all the benefits of the nice QML syntax. Also you can't really "unload" the components. Well Loader claims you can but I tried it and my app crashed.
Is there an elegant solution to this problem? Or do I simply have to bite the bullet and create all the potential UI for my app at once and then hide most of it?
Note: this page has information about using Loaders to get around this, but as you can see it is not a very nice solution.
Edit 1 - Why is Loader suboptimal?
Ok, to show you why Loader is not really that pleasant, consider this example which starts some complex task and waits for a result. Suppose that - unlike all the trivial examples people usually give - the task has many inputs and several outputs.
This is the Loader solution:
Window {
Loader {
id: task
source: "ComplexTask.qml"
active: false
}
TextField {
id: input1
}
TextField {
id: output1
}
Button {
text: "Begin complex task"
onClicked: {
// Show the task.
if (task.active === false)
{
task.active = true;
// Connect completed signal if it hasn't been already.
task.item.taskCompleted.connect(onTaskCompleted)
}
view.item.input1 = input1.text;
// And several more lines of that...
}
}
}
function onTaskCompleted()
{
output1.text = view.item.output1
// And several more lines...
// This actually causes a crash in my code:
// view.active = false;
}
}
If I was doing it without Loader, I could have something like this:
Window {
ComplexTask {
id: task
taskInput1: input1.text
componentLoaded: false
onCompleted: componentLoaded = false
}
TextField {
id: input1
}
TextField {
id: output1
text: task.taskOutput1
}
Button {
text: "Begin complex task"
onClicked: task.componentLoaded = true
}
}
That is obviously way simpler. What I clearly want is some way for the ComplexTask to be loaded and have all its declarative relationships activated when componentLoaded is set to true, and then have the relationships disconnected and unload the component when componentLoaded is set to false. I'm pretty sure there is no way to make something like this in Qt currently.
Creating QML components from JS dynamically is just as ugly as creating widgets from C++ dynamically (if not less so, as it is actually more flexible). There is nothing ugly about it, you can implement your QML components in separate files, use every assistance Creator provides in their creation, and instantiate those components wherever you need them as much as you need them. It is far uglier to have everything hidden from the get go, it is also a lot heavier and it could not possibly anticipate everything that might happen as well dynamic component instantiation can.
Here is a minimalistic self-contained example, it doesn't even use a loader, since the dialog is locally available QML file.
Dialog.qml
Rectangle {
id: dialog
anchors.fill: parent
color: "lightblue"
property var target : null
Column {
TextField {
id: name
text: "new name"
}
Button {
text: "OK"
onClicked: {
if (target) target.text = name.text
dialog.destroy()
}
}
Button {
text: "Cancel"
onClicked: dialog.destroy()
}
}
}
main.qml
ApplicationWindow {
visible: true
width: 200
height: 200
Button {
id: button
text: "rename me"
width: 200
onClicked: {
var component = Qt.createComponent("Dialog.qml")
var obj = component.createObject(overlay)
obj.target = button
}
}
Item {
id: overlay
anchors.fill: parent
}
}
Also, the above example is very barebone and just for the sake of illustration, consider using a stack view, either your own implementation or the available since 5.1 stock StackView.
Here's a slight alternative to ddriver's answer that doesn't call Qt.createComponent() every time you create an instance of that component (which will be quite slow):
// Message dialog box component.
Component {
id: messageBoxFactory
MessageDialog {
}
}
// Create and show a new message box.
function showMessage(text, title, modal)
{
if (typeof modal === 'undefined')
modal = true;
// mainWindow is the parent. We can also specify initial property values.
var messageDialog = messageBoxFactory.createObject(mainWindow, {
text: text,
title: title,
visible: true,
modality: modal ? Qt.ApplicationModal : Qt.NonModal
} );
messageDialog.accepted.connect(messageDialog.destroy);
messageDialog.rejected.connect(messageDialog.destroy);
}
I think loading and unloading elements is not actual any more because every user have more than 2GB RAM.
And do you think your app can take more than even 512 MB ram? I doubt it.
You should load qml elements and don't unload them, no crashes will happens, just store all pointers and manipulate qml frames.
If you just keep all your QML elements in RAM and store their states, it will works faster and looks better.
Example is my project that developed in that way: https://youtube.com/watch?v=UTMOd2s9Vkk
I have made base frame that inherited by all windows. This frame does have methods hide/show and resetState. Base window does contains all child frames, so via signal/slots other frames show/hide next required frame.