How does QML handle app quitting from menu bar on Mac? - qt

I have an application which runs both on Windows and Mac.
The application is written in QML.
The most outer component in my QML code is an ApplicationWindow.
The ApplicationWindow component's API has a property called menuBar.
Here is a dummy example, which reproduces this with a MenuBar and a quitting Action:
ApplicationWindow {
id: _applicationWindow
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Component.onCompleted: {
console.info("ApplicationWindow completed.")
}
menuBar: MenuBar {
Menu {
title: "File"
MenuItem {
action: Action {
id: _myQuttingAction
text: "Quit MyApp"
shortcut: "Ctrl+Q"
onTriggered: {
// deinit...
console.info("I got called!")
Qt.quit()
}
}
}
}
}
}
When I run my application on Mac the following top menu bar titles appear:
Apple Icon menu | MyApp | File
If I drop down the "File" menu this is what I get:
As you can see the drop down is empty. Actually it isn't empty because it is pressable and if I press it, it does quit my application with all deinit calls in the action's onTriggered event.
If I drop down the "MyApp" menu, at the bottom I see "Quit MyApp" with the shortcut Cmd+Q. If I press this, this quits my application as well, but without calling my action's onTriggered event, thus my real application crashes on quit.
Unfortunately pressing Cmd+Q also calls the quit from the "MyApp" not my specified action.
On Windows I don't have this problem at all and the "Quit MyApp" appears under the "File" menu and does what I expect it to do.
I tried to google this problem and read the documentation without luck yet. Anybody know what is happening here?

It is not realistic to expect that Qt quitting will somehow magically use your particular menu action, which is completely optional and arbitrary.
It is possible that it is not your shortcut that's invoking the action, but something built in that is platform specific, like how Alt+F4 works on windoze for example.
Since you are using ApplicationWindow you can implement a custom exit handler, which will be used regardless how you quit the application:
onClosing: {
// do your stuff
close.accepted = true // exits, if set to false the quit request will be ignored
}
Update: Since you say the above code doesn't work as expected, the quitting must be happening at an even lower level that doesn't even notify qtquick, which could also explain the crashing. I don't currently have a macos machine to look into it, however there is decent chance you could get it to quit property by hooking a handler to the QCoreApplication::aboutToQuit() signal.

Related

Qt Creator autocomplete values for a global qml object in a file which is loaded by Stackloader?

So in QML, I can declare a stackview like this:
ApplicationWindow {
Item{
id: globalObject
specialProperty: "xyz"
}
StackView{
id: sv
}
}
Then I have a button which loads a "Page1.qml" file into the StackView. When the program runs, I can access the globalObject from code in Page1.qml but qtCreator's autocomplete is oblivious to this connection. It is not very quick to go back and forth between Page1.qml and main.qml to find the name of special property especially when there are many objects that have to be shared between multiple pages.
In general, what I want to know, is there a way to statically link a response to a dynamically loaded object or leave a hint for the IDE that the following objects in this file are available to this other file?

Using QML FileDialog leads to "Binding loop" in ToolBar

I have an issue that I can't solve by myself. I develop a UI using this third party library/framework - https://github.com/papyros/qml-material. It is with controls and layouts developed according to the Google Guidelines. The problem is that when I use FileDialog in the project, the page that it is defined in loads slowly and I get the following messages:
file:///C:/.../QtQuick/Controls/ToolBar.qml:146:9: QML QQuickItem*: Binding loop detected for property "layoutHeight"
file:///C:/.../QtQuick/Dialogs/DefaultFileDialog.qml:407:9: QML ToolBar: Binding loop detected for property "implicitHeight"
file:///C:/.../QtQuick/Dialogs/DefaultFileDialog.qml:407:9: QML ToolBar: Binding loop detected for property "implicitHeight"
It will be great if someone knows the answer.
The issue can be reproduced very easily with the Demo that is in the GitHub project of the library - https://github.com/papyros/qml-material/tree/develop/demo. Just add FileDialog somewhere there and see how the page will be loaded and what messages you will get.
Qt version used: 5.12.6
This is a somewhat known problem with FileDialogs when put in Loaders that are set to asynchronous loading. Here is a complete example demonstrating the problem:
import QtQuick.Window 2.12
import QtQuick.Dialogs 1.3
Window {
id: root
visible: true
width: 640
height: 480
Loader {
sourceComponent: FileDialog {}
asynchronous: true // change to false and ToolBar binding loop goes away
}
}
I noticed that the project you have referenced uses an asynchronous Loader in main.qml, so it appears that this is causing your issue. See line 243:
Loader {
id: example
anchors.fill: parent
asynchronous: true
I have found one bug report but it has been closed as unresolved. My understanding of this problem is that the error only affects the "backup" QFileDialog that only appears if an OS-specific one is not available. From the documentation:
The implementation of FileDialog will be a platform file dialog if possible. If that isn't possible, then it will try to instantiate a QFileDialog.
In my experience, on Windows and Mac, the binding loop error causes no problems and can be ignored because the OS file dialog always appears. I also don't think it is a correctable error outside of not using the asynchronous flag, as it is a Qt bug.

Qt Quick emulator layer crash

I created an empty QT Quick Application and a ui.qml file in the project folder but I'm unable to open the ui file in design mode.
A dialog box describing error message says:
ine:1: Qt Quick emulator layer crash
Taking a look at line 1 of the code, i can't find any error
import QtQuick 2.12
import QtQuick.Controls 2.12
Item {
width: 400
height: 400
Page {
id: page
width: 200
height: 200
Button {
id: button
}
CheckBox {
id: checkBox
text: qsTr("Check Box")
}
Label {
id: label
text: qsTr("Label")
}
}
}
I tried changing the path for the QML EMULATOR but it didn't solve the issue.
path configurations
What can be the problem and how can it be fixed?
I ran into this error today. But I've successfully fixed it.
You need to do this:Tool->QT Quick->QT Quick Designer->QML Emulation Layer , "Use QML Emulation Layer that is built with selected QT"
No need to choose a path,
The solution was first in this thread.
Cannot Connect to QML Emluation Layer (QML Puppet)
Try changing Item to Rectangle. Not sure why but it works for me sometimes.

How can I open second qml page on button click

I use Qt 5.5.0 and Qt Creator 3.4.2 on Mac-book, I want to open second qml on button click, I want to develop application in which login page come and after login control move to next page.
I try suggestion of stackoverflow, but still I face problem to open next qml on button click.
I tried
button2.onClicked:{
var component = Qt.createComponent("test.qml")
var window = component.createObject(root)
window.show()
}
How can I create a new window from within QML?
In upper case error come related to root, ReferenceError: root is not defined. I gave root id in test.qml file.
I also tried many more example to solve to develop functionality. Please give brief idea or code to develop this functionality.
The code below works, Take note of the property status and the function errorString() – they will give you information in debugging (.e.g. "file not found" errors, components not ready, etc).
See these pages for background:
Dynamic QML Object Creation from JavaScript
Component QML Type
Window QML Type
function createWindows() {
var component = Qt.createComponent("Child.qml");
console.log("Component Status:", component.status, component.errorString());
var window = component.createObject(root, {"x": 100, "y": 300});
window.show();
}
For this type of ui I'd use [StackView][1] component that allows also animations between transitions. Other solution is to use a Loader component.

How to change the transient parent of a QML Dialog/Window?

I'm working on a Qt (5.3) desktop application (C++ core with a QML ui) whose main window is an ApplicationWindow and that at some point launches Dialogs.
Since there are differences in the use of dialog modality between Windows and Mac OS X (e. g. An about dialog is rarely modal on Mac OS X but is almost always modal on Windows) and also in the way of presenting some dialogs’ content, I’ve changed the design to allow the implementation of platform specific versions of the dialogs.
For so I created the following DialogLoader:
Loader {
id: dialogFactory
property string dialogName
function platformFolder() {
if (Qt.platform.os === "osx")
return "osx"
return "win"
}
onDialogNameChanged: { source = platformFolder() + "/" + dialogName + ".qml" }
onStatusChanged: {
if (dialogFactory.status === Loader.Error)
console.log("DialogFactory: failed to load file: " + source);
else if (dialogFactory.status === Loader.Ready)
console.log("DialogFactory: file \"" + source + "\" loaded")
}
}
Which I use as follows:
ApplicationWindow {
// …
property alias aboutDialog: aboutDialogLoader.item
// …
DialogLoader { id: aboutDialogLoader; dialogName: "AboutDialog" }
// …
Action { text: qsTr("About..."); onTriggered: aboutDialog.show() }
// …
}
That approach is working fine and it suits my needs except for one thing:
Modal dialogs on Windows don’t behave as they do when I declare them in the ApplicationWindow directly. If the app looses focus when a modal windows is opened and the focus is granted again, the modal window appears behind the main window which causes the app to be inoperative.
After some research I’ve realized that the cause of the problem is that with the loader approach the ApplicationWindow is not acting as the transient parent of the Dialog.
I've found a work-around for this by manually bringing the Dialog to front when that happens:
MainWindow {
// ...
onActiveChanged: {
if (Qt.platform.os === "windows") {
if (active) {
// ...
if (aboutDialog.visible) {
aboutDialog.requestActivate()
}
// ...
}
}
}
// ...
}
but I think it would be better to avoid such work-arounds, if possible. After digging into the Qt documentation without any luck, I decided to post this question, which can be resumed as follows:
Is it possible to change the transient parent of a QML Window? If so, please would you point out how?
Suggestions about a different approach that could avoid the reparent stuff are welcome.
I guess it is impossible to do that. At least in Qt 5.4.
From documentation (default property "data" of QML type "Window")
data : list The data property allows you to freely mix visual
children, resources and other Windows in a Window.
If you assign another Window to the data list, the nested window will
become "transient for" the outer Window.
If you assign an Item to the data list, it becomes a child of the
Window's contentItem, so that it appears inside the window. The item's
parent will be the window's contentItem, which is the root of the Item
ownership tree within that Window.
If you assign any other object type, it is added as a resource.
It should not generally be necessary to refer to the data property, as
it is the default
property for Window and thus all child items are automatically
assigned to this property.
It seems that all dynamic creation of "Window" has wrong transient parent, because of QML Binding limitation. All QML list elements is not modifiable. Thus, it is not recommended to use lists with property bindings. You'll never get notify signal if it changes. So, I guess, QML Engine never knows that new QML element is "Window" type and it necessary to add it to Windows hierarchy.
From documentation (https://www.ics.com/files/qtdocs/qml-list.html):
A list property cannot be modified in any other way. Items cannot be
dynamically added to or removed from the list through JavaScript
operations; any push() operations on the list only modify a copy of
the list and not the actual list. (These current limitations are due
to restrictions on Property Binding where lists are involved.)
Maybe this is a bug, maybe feature. Anyway, for now it is impossible to load "window" type dynamically with proper transient parent.
The best workaround I've found - use modality: Qt.ApplicationModal for dialog.

Resources