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

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

Related

Error importing "Esri.ArcGISRuntime" to Qt Creator

After running the code an error came:
QQmlApplicationEngine failed to load component
qrc:/main.qml:19:1: module "Esri.ArcGISRuntime" is not installed
I am using Qt Creator 4.11.1 Qt 5.14.1
I am using Ubuntu 20.04
I have already added the path to the LD_LIBRARY_PATH as stated here: https://developers.arcgis.com/qt/install-and-set-up/
I have restarted the Qt Creator and my system but still not working.
Any suggestions?
The code is:
main.qml
import QtQuick 2.6
import QtQuick.Controls 2.2
import Esri.ArcGISRuntime 100.14 //error here
Rectangle {
id: rootRectangle
clip: true
width: 800
height: 600
readonly property int initialZoomScale: 8000
property bool trackingEnabled: false
property Point lastPosition: null
// add a mapView component
MapView {
anchors.fill: parent
Map {
Basemap {
initStyle: Enums.BasemapStyleArcGISDarkGray
}
}
// set initial viewpoint near UCLA, Los Angeles
ViewpointCenter {
Point {
x: -13185535.98
y: 4037766.28
}
targetScale: initialZoomScale
}
Button {
id: button
text: trackingEnabled ? "Stop tracking" : "Start tracking"
anchors.horizontalCenter: parent.horizontalCenter
width: 200
onClicked: trackingEnabled =! trackingEnabled
}
SimulationParameters {
id: simulationParameters
velocity: 30
}
locationDisplay {
dataSource: SimulatedLocationDataSource {
id: simulatedLocationDataSource
Component.onCompleted: {
setLocationsWithPolylineAndParameters(polyline, simulationParameters);
simulatedLocationDataSource.start();
}
}
initialZoomScale: initialZoomScale
}
// if tracking is enabled then show location history
locationDisplay.onLocationChanged: {
if (!trackingEnabled)
return;
// clear old route
locationHistoryLineOverlay.graphics.clear();
if (lastPosition !== null) {
polylineBuilder.addPoint(lastPosition);
locationHistoryOverlay.graphics.append(ArcGISRuntimeEnvironment.createObject("Graphic", {geometry: lastPosition}));
}
// update last position
lastPosition = locationDisplay.location.position;
// update the polyline
locationHistoryLineOverlay.graphics.append(ArcGISRuntimeEnvironment.createObject("Graphic", {geometry: polylineBuilder.geometry}));
}
Component.onCompleted: {
// Set the focus on MapView to initially enable keyboard navigation
forceActiveFocus();
locationDisplay.autoPanMode = Enums.LocationDisplayAutoPanModeRecenter;
}
Polyline {
id: polyline
json: {"paths":[[[-13185646.046666779,4037971.5966668758],[-13185586.780000051,4037827.6633333955],
[-13185514.813333312,4037709.1299999417],[-13185569.846666701,4037522.8633330846],
[-13185591.01333339,4037378.9299996048],[-13185629.113333428,4037283.6799995075],
[-13185770.93000024,4037425.4966663187],[-13185821.730000293,4037546.146666442],
[-13185880.996667018,4037704.8966666036],[-13185948.730000421,4037874.2300001099],
[-13185974.130000448,4037946.1966668498],[-13186120.180000596,4037958.896666863],
[-13186264.113334076,4037984.296666889],[-13186336.080000836,4038001.2300002342],
[-13186314.91333415,4037757.8133333195],[-13186272.580000773,4037560.9633331187],
[-13186187.913334005,4037463.59666635],[-13186431.33000092,4037404.3299996229],
[-13186676.863334503,4037290.0299995062],[-13187625.130002158,4038589.6633341513],
[-13187333.030001862,4038756.8800009824],[-13187091.730001617,4038617.1800008402],
[-13186791.163334643,4038805.5633343654],[-13186721.313334571,4038801.3300010278],
[-13186833.49666802,4038195.9633337436],[-13186977.677439401,4037699.8176972247],
[-13186784.921301765,4037820.4541915278],[-13186749.517113185,4038150.8932846226],
[-13186649.860878762,4038288.5762400767],[-13186556.760975549,4038323.9804286221],
[-13186472.839936033,4038481.3323777127],[-13186373.183701571,4038489.1999751539],
[-13186344.335844241,4038242.6819215398],[-13186126.665647998,4038308.245233661],
[-13185814.584282301,4038358.0733508728],[-13185651.987268206,4038116.8003622484],
[-13185203.534213299,4038048.6145176427],[-13184576.748949422,4038150.8932845518],
[-13184251.55492135,4037833.5668537691],[-13184146.653621957,4037524.1080205571],
[-13183949.963685593,4037621.1417224966],[-13183687.71043711,4037781.1162040718],
[-13183480.530370807,4037875.5273735262],[-13182307.629999243,4037859.7460188437],
[-13181376.039484169,4037820.9297473822],[-13180716.162869323,4038364.3575478429],
[-13180182.439136729,4038810.7446696493],[-13178474.523192419,4040237.2426458625],
[-13178321.134040033,4039740.6894803117],[-13177958.020228144,4039140.1550991111],
[-13177073.512224896,4037459.5898928214],[-13177757.842101147,4037589.9384406791],
[-13178386.308314031,4037799.427178307],[-13180095.012208173,4037811.6550856642],
[-13180126.165447287,4036845.9046731163],[-13179806.844746364,4036324.0879179495],
[-13180928.361354485,4035887.9425703473],[-13181598.155995468,4035428.432293402],
[-13182984.47513606,4034447.105261297],[-13182229.264383668,4033222.8051626245],
[-13182058.735615831,4033339.8690072047],[-13181939.035180708,4033691.2477038498],
[-13182116.65518121,4033861.1450956347],[-13181792.305615077,4034085.1007484416],
[-13182027.845180977,4034467.3698799671],[-13181877.254310986,4034644.9898804692],
[-13181630.130832028,4034517.5668366305],[-13181386.868657427,4034424.8955320208],
[-13181228.555178719,4034652.7124891868],[-13181379.14604871,4034942.3103160923],
[-13181267.168222306,4035189.4337950516],[-13181074.103004368,4035015.6750989081],
[-13180807.673003616,4034934.5877073747],[-13180618.469090037,4034814.8872722536],
[-13180599.162568243,4035374.7764042714],[-13181047.073873857,4035494.476839392],
[-13181317.365178969,4035413.3894478586],[-13180765.198655669,4035143.0981427468],
[-13180328.871263131,4034892.1133594285],[-13180270.951697765,4035258.9372735149],
[-13180325.009958787,4035718.4324922049],[-13180707.279090302,4035695.2646660525],
[-13181413.897788007,4035536.9511873648],[-13181618.54691902,4035807.2424924765],
[-13181884.976919774,4036065.949884512],[-13182159.129529245,4035861.3007534989],
[-13182174.57474668,4035668.2355355616],[-13182417.83692128,4035664.374231203],
[-13182784.660835361,4035409.5281435261],[-13182997.032575091,4035255.0759691764],
[-13182618.624747934,4034679.7416197238]]],
"spatialReference":{"latestWkid":3857,"wkid":102100}}
}
GraphicsOverlay {
id: locationHistoryLineOverlay
SimpleRenderer {
SimpleLineSymbol {
color: "lime"
style: Enums.SimpleLineSymbolStyleSolid
width: 2
}
}
}
GraphicsOverlay {
id: locationHistoryOverlay
SimpleRenderer {
SimpleMarkerSymbol {
color: "red"
style: Enums.SimpleMarkerSymbolStyleCircle
size: 3
}
}
}
}
PolylineBuilder {
id: polylineBuilder
spatialReference: SpatialReference { wkid: 3857 }
}
}
I will summarise the issues discussed in the comment thread to bring to an end.
Use Qt 5.15 these days and preferably always the latest bug fix release. Which is currently 5.15.5 for open-source, and not commercial, customers at the time of writing this. Even better to switch to 6.x when possible.
Setting the QML_IMPORT_TRACE environment variable can help debugging these issues.
When using qmake, they seem to suggest including their pri file to your qmake pro file.
Something like a export QML2_IMPORT_PATH=..:$QML2_IMPORT_PATH can also help localising these issues as a quick experiment.

qt qml errors with QtQuick Dialogs 1

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

How to implement single connection between objects in QML?

In my Qt app I have many windows, and sometimes they need a "Back" button. This button is placed on ToolBar component in the header of the ApplicationWindow .
What I want to achieve, is that this Back button, would have only single connection to other objects , i.e. the connection to the last object that called connect method. Right now with every connect I am getting a new connection and when the signal is emitted, it is called multiple times. Unfortunately Qt doesn'thave disconnectAll method, if it would , that would have solve my problem , I would just call disconnectAll before and then connect and that would implement single connection.
So , how are you doing this functionality in Qt , with a simple method?
Here is a minimal reproducible example, click on the tabs many times, then press 'Back' button and you will see lots of console.log messages. And what I need is this message to correspond to the last object that is connected to the Back button.
import QtQuick 2.11
import QtQuick.Controls 2.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Tabs")
signal back_btn_clicked()
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page1Form {
id: page1
function page1_callback() {
console.log("page 1 back button triggered")
}
function install_button() {
enable_back_button(page1_callback)
}
}
Page2Form {
id: page2
function page2_callback() {
console.log("page 2 back button triggered")
}
function install_button() {
enable_back_button(page2_callback)
}
}
function install_back_button(idx) {
if (idx===0) {
page1.install_button()
}
if (idx===1) {
page2.install_button()
}
}
}
Button {
id: btn_back
visible: false
text: "Back Button"
onClicked: back_btn_clicked()
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("Page 1")
onClicked: swipeView.install_back_button(0)
}
TabButton {
text: qsTr("Page 2")
onClicked: swipeView.install_back_button(1)
}
}
function enable_back_button(func_name) {
btn_back.visible=true
back_btn_clicked.connect(func_name)
}
}
PageForm.ui is defined like this
import QtQuick 2.11
import QtQuick.Controls 2.4
Page {
width: 600
height: 400
header: Label {
text: qsTr("Page 1")
font.pixelSize: Qt.application.font.pixelSize * 2
padding: 10
}
Label {
text: qsTr("You are on Page 1.")
anchors.centerIn: parent
}
}
The simplest hack, I think, would be to store the callback in a property, then in enable_back_button(), reference that property in your disconnect() function, and update the property accordingly with the new callback passed as a function argument. (The rationale for this argument being that the disconnect() function must take in an argument: the slot to disconnect. So we'll need to keep track of it some way or another.)
ApplicationWindow {
visible: true
// ... omitted for brevity
property var prevCallback: null
// ... ofb
function enable_back_button(func_name) {
btn_back.visible=true
if (prevCallback)
back_btn_clicked.disconnect(prevCallback) // disconnect previous callback
back_btn_clicked.connect(func_name) // connect new callback
prevCallback = func_name // update property with new callback
}
}
And this could work on multiple connections as well, by simply changing the storage into an array, then iterating through that.

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.

How to access fileUrl of element "FileDialog" when using it as component in qt5?

I have made a qml file named fileDialog.qml and which use element FileDialog{} available from qt5
http://qt-project.org/doc/qt-5.1/qtquickdialogs/qml-qtquick-dialogs1-filedialog.html.
Whenever i need the location of resource i want to use fileDialog.qml as component and set all the properties like title, filter etc. These are working fine but when i tried to use id.fileUrl then no response. details are given below.
The file fileDialog.qml is
import QtQuick 2.1
import QtQuick.Dialogs 1.0
FileDialog {
id: fileDialog
objectName: "fileBrowser"
title: "Add New Layer"
visible: false
property alias selectedFilename: fileDialog.fileUrls
onAccepted: {
console.log("You chose: " + fileDialog.fileUrls)//-------- (1)
}
onRejected: {
console.log("Canceled")
}
//Component.onCompleted: visible = true
}
Now using this as component when Browse(an item in QML to be used like button) button is clicked then i am performing following steps.
onClicked: {
//Default Values fileDialog.{selectExisting = true, selectFolder = false}
fileDialog.title = "Add New Image"
//fileDialog1.selectMultiple = true
fileDialog.nameFilters = ["Image File (*.png *.jpg *.bmp)"]
//fileDialog.fileUrls
//string path
fileDialog.visible = true
console.log(" Image chosen: " + fileDialog.fileUrl + " in image")//--- (2)
}
The line (1) is working fine but but (2) is not working. The output of (2) line in console is just Image chosen: in image.
I don't understand what am I doing wrong here, because when I am setting other(like title, filer) property of component fileDialog its working but not for the fileUrl or fileUrls.
Please somebody suggest how to get the fileUrl when using it as component.
Thanks,
I think what you're trying to do is creating a special FileDialog component that fits your needs.
First, let's call this component "MyFileFialog" saved in MyFileDialog.qml.
// MyFileDialog.qml
import QtQuick 2.0
import QtQuick.Dialogs 1.0
Item {
id: root
property alias title: qmlFileDialog.title
property alias fileUrl: qmlFileDialog.fileUrl
property alias fileUrls: qmlFileDialog.fileUrls
signal accepted()
signal rejected()
function open() { qmlFileDialog.open() }
function close() { qmlFileDialog.close() }
FileDialog {
id: qmlFileDialog
modality: Qt.WindowModal
nameFilters: ["Image File (*.png *.jpg *.bmp)"]
onAccepted: root.accepted()
onRejected: root.rejected()
}
}
There are now a bunch of connections between MyFileDialog and FileFialog:
modality and nameFilters are fixed
title, fileUrl, fileUrls are passed between both components
when the FileDialog is accepted or rejected, the MyFileDialog will be as well
when you open() or close() your MyFileDialog, the inner FleDialog will open or close
Now that you have your very own MyFileDialog, you can use it:
Button {
text: "open"
MyFileDialog {
id: saveFileDialog
title: qsTr("Save to ...")
onRejected: {
console.log("Canceled")
}
onAccepted: {
console.log("File selected: " + fileUrl)
}
}
onClicked: {
saveFileDialog.open()
}
}
Just a try. From documentation, you can see that fileUrl property is only set if you make a single file selection. So you are right to expect it to be setted by your FileDialog
The problem is that you try to display fileUrl before the FileDialog is closed I think.
Showing a modal dialog probably don't block your function execution.
As you do in your base component fileDialog.qml, you can put a handler on onAccepted. When your handler will be called, fileUrl property will be available.
edit:
Browse {
id: browser
signal fileChosen
FileDialog {
id: fileDialog
//configure your fileDialog here
//...
//emit parent signal when done
onAccepted: browser.fileChosen();
}
onClicked: {
fileDialog.open();
}
onFileChosen: {
//fileUrl should be available here
console.log(fileDialog.fileUrl);
}
}
I got a solution after reading the answer of #jbh whose answer helps in accessing fileUrl outside FileDialog{} element in same qml file but that's not an answer to my question.
Element FileDialog{} have an signal accepted so we use this signal to connect to a method and then access the fileUrl or Urls. This how fileBrowser.qml look like.
import QtQuick 2.1
import QtQuick.Dialogs 1.0
FileDialog {
id: fileDialog
objectName: "fileBrowser"
title: "Add New Layer"
visible: false
//property alias selectedFilename: fileDialog.fileUrls
// signal fileChosen
// onAccepted: {
// console.log("You chose: " + fileDialog.fileUrls)
// fileChosen();
// }
onRejected: {
console.log("Canceled")
}
//Component.onCompleted: visible = true
}
onAccepted slot above is commented and we use accepted signal to access fileUrl.
This is how a qml file will look when using fileBrowser.qml as component whenever file dialog is required.
Item{
id: popup
Rectangle{
id: browse button
// properties setting for construction a button such as width, color, mouse area, states, etc..
// the method where we can use the URLS
function dialogAccepted(){
fileDialog.accepted.disconnect(dialogAccepted)
filePath.text = Qt.resolvedUrl( fileDialog.fileUrl ).toString()// to set the text in text field
console.log("You Chose in elev: " + fileDialog.fileUrl)
//browseButtonClicked(checkBox.checked)
}
onClicked: {
//Default Values fileDialog.{selectExisting = true, selectFolder = false}
fileDialog.title = "Add New Image"
//fileDialog1.selectMultiple = true
fileDialog.nameFilters = ["Image File (*.png *.jpg *.bmp)"]
fileDialog.visible = true
fileDialog.accepted.connect(dialogAccepted)
}
}
}
well, this worked for me but i am still facing problem with how to resolve Url when multiple file is selected and how to send it to c++ file so that it is accepted.

Resources