qt qml errors with QtQuick Dialogs 1 - qt

I"m using Qt 5.15.2. Below is the qml snippet that is failing for me.
Upon launching everything is ok. However when i try to open new windows, 1 window will be ok, but second (and subsequent) one will start showing something like this in the console.
bin/archdatadir/qml/QtQuick/Dialogs/DefaultFileDialog.qml:99: TypeError: Value is null and could not be converted to an object
bin/archdatadir/qml/QtQuick/Controls/SplitView.qml:630: TypeError: Property 'terminateItemConnections' of object QObject_QML_93(0x600003397f20) is not a function
bin/archdatadir/qml/QtQuick/Controls/SplitView.qml:630: TypeError: Property 'terminateItemConnections' of object QObject_QML_93(0x600003394500) is not a function
bin/archdatadir/qml/QtQuick/Dialogs/DefaultFileDialog.qml:436: TypeError: Cannot read property 'bottom' of undefined
....
If I remove the FileDialog definition completely, which is actually unused here, then these errors disappear completely. Would there be any suggestions on what I am doing incorrectly?
thanks!
import QtQuick 2.15
import QtQuick.Controls 2.4
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: root
onClosing: {
console.log("closing window")
}
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&New")
icon.name: "document-new"
onTriggered: newDocument()
}
}
}
FileDialog {
id: openDialog
title: "Open"
folder: shortcuts.home
onAccepted: {
console.log("open file dialog")
}
}
function newWindow() {
console.log("new window")
var component = Qt.createComponent("reproducer.qml")
var window = component.createObject()
return window
}
function newDocument() {
console.log("new document")
var window = newWindow()
window.show()
}
}
VK

Related

Where do I destroy a Dialog if I create it dynamically?

Assume I have the following Dialog in QML:
import QtQuick
import QtQuick.Controls
Dialog
{
title: "Bot Settings"
modal: true
standardButtons: Dialog.Ok | Dialog.Cancel
property var bot1
onAccepted: {}
onRejected: {}
Component.onCompleted: {}
Component.onDestruction: {}
}
and I create it with a code like this:
function openBotDialog()
{
var dynamicComp = Qt.createComponent("BotDialog.qml")
var dynamicDlg = dynamicComp.createObject(mainWindow, {"bot1" : bot});
dynamicDlg.open()
}
and I need to destroy the dialog when it closes.
What code do I use and where do I add it?
Should I destroy dynamicComp?
See the full source code of the app with my dialog on GitHub.
EDIT1
When I press the following buttons in my app the dialog is not destroyed by GC:
But when I press "Clear Dynamic Dlg" button that explicitly calls destroy() method the Dialog is destroyed and its Component.onDestruction handler is called.
Button
{
text: "Clear Dynamic Dlg"
onClicked:
{
dynamicComp.destroy()
dynamicComp = null
dynamicDlg.destroy()
dynamicDlg = null
}
}
How about:
onAccepted: Qt.callLater(destroy)
onRejected: Qt.callLater(destroy)
This means you are scheduling a destroy after the accepted/rejected signals are received. Qt.callLater adds enough of a delay so that the destruction isn't being done during the signal handler but immediately after.
import QtQuick
import QtQuick.Controls
Page {
id: thisPage
Button {
text: qsTr("Bot Dialog")
onClicked: openBotDialog()
}
function openBotDialog()
{
var dynamicComp = Qt.createComponent("BotDialog.qml")
var bot = new Date();
var dynamicDlg = dynamicComp.createObject(thisPage, {"bot1" : bot});
dynamicDlg.open()
}
}
//BotDialog.qml
import QtQuick
import QtQuick.Controls
Dialog
{
title: "Bot Settings"
modal: true
standardButtons: Dialog.Ok | Dialog.Cancel
property var bot1
onAccepted: Qt.callLater(destroy)
onRejected: Qt.callLater(destroy)
}
You can Try it Online!

QML progress bar is NOT showing up on UI

I have this QML progress bar:
import QtQuick.Controls 2.0 as QQC20
Item {
QQC20.ProgressBar {
id: progressbar_id
visible: false // even if "true", the progress bar does NOT show up on UI
from: editorScene.progressbarMin
to: editorScene.progressbarMax
value: editorScene.progressbarVal
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
I can confirm that the progress bar value and visibility are changed by the methods onValueChanged and onVisibleChanged.
However, the problem is that the progress bar does NOT show up on the UI! How can I actually show the progress bar on the UI? Can anybody give me a hint?
Right now, all you're doing is creating a QML type which you can use as part of your API. To actually see it, you need to create an instance of it under a ApplicationWindow or Window (or anything else equivalent, e.g. Canvas or Felgo's GameWindow).
There are two ways you can accomplish this. You can
Directly add your item as a child of a window.
Put your item in a separate file, and create an instance of that file under a window.
Lé Code
Method 1: Directly Adding as Child
Directly insert your codeblock as a child of an ApplicationWindow.
// Main.qml
import QtQuick 2.0 // for `Item`
import QtQuick.Window 2.0 // for `ApplicationWindow`
import QtQuick.Controls 2.0 // as QQC20 // no need to label a namespace unless disambiguation is necessary
ApplicationWindow {
width: 480 // set the dimensions of the application window
height: 320
// here's your item
Item {
anchors.centerIn: parent // place in centre of window
ProgressBar {
id: progressbar_id
anchors.horizontalCenter: parent.horizontalCenter // horizontally align the progress bar
from: 0 // don't know what editorScene is
to: 100 // so I'm using raw values
value: 5
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
// side note: I'm not getting any output from this handler
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
// provide user-interaction for changing progress bar's value
MouseArea {
anchors.fill: parent // clicking anywhere on the background
onClicked: progressbar_id.value += 5; // increments the progress bar
// and triggers onValueChanged
}
}
Method 2: Using a Separate File
Save your item into a new qml file.
// MyProgressBar.qml
import QtQuick 2.0 // for `Item`
import QtQuick.Controls 2.0 // for `ProgressBar`
// here is your item, it has grown up to be in a file of its own 🚼
Item {
property alias value: progressbar_id.value // for user-interaction
ProgressBar {
id: progressbar_id
anchors.horizontalCenter: parent.horizontalCenter // centre horizontally
from: 0
to: 100
value: 5
onValueChanged: {
console.log("Progressbar value changed: ", progressbar_id.value)
}
onVisibleChanged: {
console.log("Progressbar visibility chanaged: ", progressbar_id.visible)
}
}
}
Note that you still need the import statements.
Then call it from a window in Main.qml. We'll use an ApplicationWindow here.
// Main.qml
import QtQuick 2.0
import QtQuick.Window 2.0 // for `ApplicationWindow`
// import "relative/path/to/progressbar" // use this if MyProgressBar.qml is not in the same folder as Main.qml
ApplicationWindow {
width: 480
height: 320
MyProgressBar {
id: progressbar_id
}
MouseArea {
anchors.fill: parent
onClicked: progressbar_id.value += 5;
}
}
If your qml files aren't in the same directory, make sure you add an import "relative/path" at the top of the Main.qml file among the other import statements.
For example, if
Your Qml project is in /Users/Lorem/Project,
The full path to your Main.qml is /Users/Lorem/Project/qml/Main.qml, and
The full path to your MyProgressBar.qml is /Users/Lorem/Project/qml/myControls/MyProgressBar.qml...
Then use import "myControls" in Main.qml to import the items from the myControls subdirectory. Remember, you only need to import the directory, not the file itself.
Result
This is what the result resembles when I run it from a macOS.
At startup.
After 3 clicks on the background.
There is also console/debug output after each click:
Progressbar value changed: 10
Progressbar value changed: 15
Progressbar value changed: 20

How to add File name at recently opened file File menu

I want to write a QML app that adds the latest opened files from FileDialog to the main menu. I'm currently following this documentation example but the problem is that I can't understand how to pass the file name of an opened file.
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar : MenuBar
{
Menu
{
id: recentFilesMenu
Instantiator
{
model: recentFilesMenu
MenuItem
{
text: model.fileName // I neeed to pass name of opned file here
}
onObjectAdded: recentFilesMenu.insertItem(index, object)
}
title: "File"
MenuItem
{
text: "Open"
onTriggered: fileDialog.visible = true
}
MenuItem
{
text: "Exit"
}
}
}
FileDialog
{
id: fileDialog
title: "Oooopen"
onAccepted:
{
// Here is problem
recentFilesMenu.objectName = fileDialog.fileUrls
}
}
}
According to the documentation, Instantiator accepts the most common types of models - both C++ and QML ones. In the documentation example such an information is missing, probably to not force the usage of a specific one. An actual implementation can relay on ListModel. In this case the model would expose a fileName role used as the actual menu item.
Following this approach the result would be something like the following code. Mind that the urls are prepended with information which can be easily removed (see for instance this answer).
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar : MenuBar {
Menu {
id: recentFilesMenu
title: "File"
MenuItem {
text: "Open"
onTriggered: fileDialog.visible = true
}
MenuSeparator { }
Instantiator {
model: ListModel { id: files }
MenuItem { text: fileName }
onObjectAdded: recentFilesMenu.insertItem(index, object)
onObjectRemoved: recentFilesMenu.removeItem(object)
}
MenuSeparator { visible: files.count > 0 }
MenuItem { text: "Exit" }
}
}
FileDialog {
id: fileDialog
title: "Open"
onAccepted: {
for(var i = 0; i < fileDialog.fileUrls.length; ++i)
files.append({fileName: fileDialog.fileUrls[i]})
}
}
}
There is a widgets version of this kind of feature:
http://doc.qt.io/qt-5/qtwidgets-mainwindows-recentfiles-example.html
But the descriptive help is non-existent. Looking through the code here:
http://doc.qt.io/qt-5/qtwidgets-mainwindows-recentfiles-mainwindow-cpp.html
You will see that it stores a QStringList of a list of recent files in QSettings, and loads everything into an array of QActions.
Follow through the mainWindow.cpp for all the references to
enum { MaxRecentFiles = 5 };
QAction *recentFileActs[MaxRecentFiles];
And you should have some good ideas about how to do something similar in QML.
Hope that helps.
You probably have a finite number of recent files that you want to display. That being said, you can implement x number of MenuItems and set the text to QStringList[i] implemented as a Q_PROPERTY in a C++ class. Then, you can manipulate the QStringList elements(size, order) on your C++ class.

Undefined element when tab is not active

When I try to access a son of a Tab element, if it is not active QML throws an error saying is undefined.
main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.0
ApplicationWindow {
TabView {
Tab {
id: mytab1
}
Tab {
id: myTab2
Rectangle {
//(...)
}
}
}
Connections {
target: eventManager
onData: {
var scene = myTab2.children[0];
console.log(scene);
}
}
}
So, if myTab2 is active, I can see in the console QQuickItem_QML_15(0x27f1e2e0). If myTab2 is not active, then qml throws TypeError: Cannot read property 'children' of undefined.
Why is undefined if the tab is not active and how can I fix it?
Thanks!
From the Qt documentation website, I've found a solution.
Tabs are lazily loaded; only tabs that have been made current (for
example, by clicking on them) will have valid content. You can force
loading of tabs by setting the active property to true:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.0
ApplicationWindow {
TabView {
Tab {
id: mytab1
active: true
}
Tab {
id: myTab2
active: true
Rectangle {
//(...)
}
}
}
Connections {
target: eventManager
onData: {
var scene = myTab2.children[0];
console.log(scene);
}
}
}
So, I've added the property active: true in both tabs, and It works fine!
A TabView doesn't create its content items until a tab is activated.
Your example begins at tab 1, at this point the rectangle in tab 2 doesn't exist, so you get undefined. If you activate tab 2 the rectangle will be created, and then if you go back to tab 1 you will not get undefined.
A Tab inherits a Loader, and comes with an active property. I suppose there is an optimization that exists back in the Loader component to delay loading until the element becomes visible. If you set active: true in your Tab it will be loaded before the tab is activated. Note that this will not make the tab view open with the second tab active.
Tab {
id: t2
active: true
Rectangle {
}
}

How to use the setMetadata(key,value) method in QtMultimidia CameraCapture QML Type?

I'm interested in setting GPS location metadata to captured images in QML. I noticed the setMetadata(key,value) method in several qml capture elements but I can't understand how it works, or find any examples.
The definition in the documentation states:
"Sets a particular metadata key to value for the subsequent image
captures."
http://qt-project.org/doc/qt-5/qml-qtmultimedia-cameracapture.html
Does the method work? if so, please place a simple example.
If not, is there any other way to set (or edit) image metadata (even if use of C++ is necessary) in Qt?
Update (but not solved):
I've tried the following code, the app runs on desktop takes the picture and saves it. After I open it up with Preview (MAC) and check the metadata... and nothing special there (no comment key).
Camera {
id: camera
captureMode: Camera.CaptureStillImage
Component.onCompleted: {
imageCapture.setMetadata("Comment","My Picture")
}
imageCapture {
resolution: "640x480"
onImageCaptured: {
console.log("Image Captured Callback : Preview : "+preview)
}
onImageSaved: {
console.log("Image Saved Callback : Save Path : "+path)
}
onImageMetadataAvailable: {
console.log("Image Metadata Callback : "+key+" = "+value)
}
}
}
I think you should use a subset of the keys documented here before you start the capture.
edit
FWIW, here is a minimal test on Ubuntu, Qt 5.3 - I created an empty application, added a menu command, the camera and viewer
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.0
import QtMultimedia 5.0
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
MenuItem {
text: qsTr("Capture")
onTriggered: {
camA.imageCapture.setMetadata("Description", "my comment")
camA.imageCapture.captureToLocation("/home/carlo/Pictures/x.jpg")
}
}
}
}
Camera { id : camA }
VideoOutput { source: camA }
}
and the result seems ok...

Resources