QML: Button that opens a window from ApplicationWindow - qt

I have a button called Adavanced Settings in my ApplicationWindow. OnClicked I want it to open a Window, where I will have ComboBox, TextEntry, etc. Can anyone tell me how to proceed with the onClicked. Currently I have:
ApplicationWindow{
...
Button {
text: "Advanced Settings"
onClicked: {
var component = Qt.createComponent("PopUpSetingsWindows.qml");
}
}
}
PopUpSetingsWindows.qml
Window {
id:secondwindow
width: 800
height: 480
title: "Second"
Text {
text: "Hello World"
}
}
But the window doesn't show. Did I understand everything wrong ?

I found my answer in the link provided in my comment above. Here are the details for my example problem and also in case the link gets inactive in future.
ApplicationWindow{
id: root
property variant win; // you can hold this as a reference..
...
Button {
text: "Advanced Settings"
onClicked: {
var component = Qt.createComponent("PopUpSetingsWindows.qml");
win = component.createObject(root)
win.show();
}
}
}
QML file remains unchanged

Related

MouseArea calling a FileDialog : know which element opened the FileDialog (QML)

Since my last issue with my code, I've come across a new one. Unfortunately, it's not really an implementation issue but much more an "conceptual" issue.
Well so let met introduce the case. I have a grid full of button and then to deal with their onClicked events I have a ButtonGroup
GridLayout {
id: gl
anchors.fill: parent
...
CustomButton{
id: btnMILA1
text: "PlayBook 1"
... //Layout stuff
}
CustomButton{
id: btnMILA2
text: "PlayBook 1"
... //Layout stuff
}
CustomButton{
id: btnMILAN
text: "PlayBook 1"
... //Layout stuff
}
}
Those are generated in a loop so no worries, I didn't wrote all 40 buttons ^^ So here is my ButtonGroup
ButtonGroup {
id: btnGroup
buttons: gl.children
onClicked: {
... //Do some stuff
}
}
As you may have seen, I have a CustomButton element which is used for two reasons :
Esthetics (custom design, round corners, etc...)
Add a MouseArea to each button and onRightclick, show a Menu element
So here is a simplified version of my code for CustomButton element:
import QtQuick 2.15
Button {
id: button
property string optionalConf //SEE LATER BELOW, THIS ITEM WILL BE USEFUL
text: qsTr("Button")
contentItem: Item{
Text {
id: name
text: button.text
font: button.font
color: "#ffffff"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
}
background: Rectangle{
color: internal.dynamicColor //Used to deal with Hovered/Pressed/Default states
radius: 10
}
MouseArea {
id:mouseHovered
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked:{
rightClickMenu.open()
}
hoverEnabled: true
}
Menu {
id: rightClickMenu
MenuItem {
text: qsTr("Choix du fichier de configuration...")
shortcut: StandardKey.Open
onTriggered: confOpen.open()
}
MenuItem {
text: qsTr("Choix du firmware...")
shortcut: "Ctrl+Shift+O"
onTriggered: firmwareOpen.open()
}
MenuSeparator{}
MenuItem {
text: qsTr("Console")
shortcut: StandardKey.AddTab
//onTriggered: zoomOut()
enabled: false
}
}
}
I don't really know the efficiency in generating a mouseArea for each element so let me know if you have a better way to have an independent onRightclick option for something like 20 or 30 elements.
My issue is the following. On the page, let's say main.qml where the CustomButton is implemented, I have two fileDialog items : one called confOpen and the other called firmwareOpen as you could expect given the code above. When the user uses the rightclick, the MenuItem shows at the exact place of the mouse, he can choose wherever option he wants. Then a called is made to either confOpen or firmwareOpen and the user is able to select one file.
FileDialog{
id: confOpen
title: "Please choose a conf file"
folder: shortcuts.desktop
selectMultiple: false
nameFilters: ["Conf file (*.conf)"]
onAccepted: {
console.log(fileUrl)
//I'd like to do something like this :
//ButtonUsedToOpenFileDialog.optionalConf : fileUrl
}
}
So here is the real issue, I'd like to store the file path into a property of my CustomButton. I have a property string optionalConf in order to do so. But I can't manage to which button made the call to the FileDialog, so I don't know which button should have his optionalConf property updated.
I hope I've been clear and it doesn't take to long to read but I wanted to be clear and precise. Let me know if you have better ways to do what I'm doing, I'm always listening to advice :)
Add a function to your FileDialog called openDialog and pass to it the button like this:
[...]
MenuItem {
text: qsTr("Choix du fichier de configuration...")
shortcut: StandardKey.Open
onTriggered: confOpen.openDialog(button)
}
[...]
FileDialog {
id: confOpen
property var button
function openDialog(button_) {
button = button_;
open();
}
onAccepted: {
button.optionalConf = "UPDATED";
}
}

QML Popup: know how it was closed

I've a QML Popup that I can close by clicking outside of it or by pressing escape (hence default closing policy is fine).
However, I need to do different things if the popup is closed with escape (cancel a few things) or by clicking outside (commit the changes). I can't use Dialog, we don't want explicit buttons.
How can I reliably detect it ? So far, I've used a MouseArea to detect if the mouse is hover the Popup when it closes. The only issue is that is doesn't work if the user presses escape and it's mouse is outside the popup.
Popup
{
onAboutToHide: {
if(!ma.containsMouse)
{
}
}
contentItem: Text{
text: "hello"
}
MouseArea{
z:-1
id: ma
anchors.fill: parent
hoverEnabled:true
}
}
I tried to add
Keys.onEscapePressed:{
console.log('esc !')
}
to the popup, but then QML complains it's not an item.
you could change the default close policy and handle the Esc key press separately. The code will then look something like this:
Popup {
id: popup
onAboutToHide: {
if(!ma.containsMouse) {
console.log("click outside: commit the changes")
}
}
contentItem: Text {
text: "hello"
}
MouseArea {
z:-1
id: ma
anchors.fill: parent
hoverEnabled:true
}
closePolicy: Popup.CloseOnPressOutside
Shortcut {
sequence: "Esc"
onActivated: {
console.log("Esc: cancel a few things")
popup.close()
}
}
}

Open and close additional window (QML)

Currently I have a window openning in the following way:
property variant win
Button {
id: testButton
MouseArea {
onClicked: {
var component = Qt.createComponent("test.qml");
win = component.createObject(testButton);
win.show();
}
}
}
Is it ok to create a window like this or there is a better way to do it (from QML, not from C++)?
When I close this additional window (just by clicking "x" button), I want to connect it to another event (for example, changing color of the button). How to do it?
Thanks.
It is usually nicer to have it more declarative. If you want your button to only open one window, the usage of a Loader might be right for you.
I think this is what you want, as you store it in one variable, and if you click the button multiple times, you would lose access to your instance. If you need a larger amount of Windows created by the same Button, you might use a ListModel and a Instantiator to create the instances.
With the Loader this might look like this:
Button {
id: ldbutton
onClicked: winld.active = true
Rectangle {
id: ldindic
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
width: height
color: winld.active ? 'green' : 'red'
}
Loader {
id: winld
active: false
sourceComponent: Window {
width: 100
height: 100
color: 'green'
visible: true
onClosing: winld.active = false
}
}
}
In this code is also already the answer to your second question: The signal you are looking for is called closing - connect to it to do what ever is necessary.
In the case of the Loader it is necessary to unload the window, so it can be loaded again later, maybe. If you have the window created by a Instantiator, you need to remove the corresponding index from the Instantiator's ListModel.
This might look like this:
Button {
id: rpbutton
onClicked: rpmodel.append({})
text: 'Open Windows ' + rpmodel.count
ListModel {
id: rpmodel
}
Instantiator { // from QtQml 2.0
model: rpmodel
delegate: Window {
width: 100
height: 100
color: 'blue'
visible: true
onClosing: rpmodel.remove(index)
}
}
}
In your code you could connect to it, either by using a Connection-object, that connects to your property win, or by changing the JS onClicked like so:
onClicked: {
var component = Qt.createComponent("test.qml");
win = component.createObject(testButton);
win.closing.connect(function() { console.log('do something') })
win.show();
}

QML Cannot create Submenu in Menu

I'm trying to create a menu
import QtQuick 2.0
import QtQuick.Controls 2.2
...
Menu {
id: menu
title: "mainMenu"
MenuItem {
text: "menuItem1"
}
MenuItem {
text: "menuItem2"
}
Menu {
title: "contextMenu"
MenuItem {
text: "item1"
}
MenuItem {
text: "item2"
}
}
}
But when I'm trying menu.open() there is no contextMenu
Please find a screenshot below.
How do I fix this?
Qt Quick Controls 2.3 (Qt 5.10) adds support for nested menus and cascading sub-menus.
Maybe you meant to use QtQuick.Controls 1.x where those sub-menus are supported.
In QtQuick.Controls 2.2 - the version you are using - Menu inherits from Popup and therefore behaves like such - meaning, they are closed by default, and you need to set them visible or open() them.
The MenuItem on the other hand are AbstractButtons, that are preconfigured, to close Popups when clicked. If you want to use the QtQuick.Controls 2.x-style Menu you can define your own child type SubMenu which is a button that does not close the parent Popup but opens a second Menu as needed or inserts the right MenuItems when clicked (Accordion-style).
The right implementation depends on your requirements but should not be too challenging. Feel free to ask, if you need more help on this.
Maybe you can use Button instead of MenuItem,and adjust the background of Button yourself,Wrap them up
Item {
id: root
width: 500
height: 500
MouseArea {
id: mouse
anchors.fill: parent
onClicked: {
rootMenu.open()
}
}
Menu {
id: rootMenu
title: "rootMenu"
Button {
text: "menuItem1"
onClicked: {
console.log("choose A")
rootMenu.close()
}
}
Button {
text: "menuItem2"
onClicked: {
console.log("choose B")
rootMenu.close()
}
}
Button {
id: menu_c
text: "menuItem3"
onClicked: secondMenu.open()
}
}
Menu {
id: secondMenu
x: rootMenu.width
y: menu_c.y
MenuItem {
text: "item1"
onTriggered: {
console.log("item1")
rootMenu.close();
}
}
MenuItem {
text: "item2"
onTriggered: {
console.log("item2")
rootMenu.close();
}
}
}
}

How can I switch the focus for the pop-up window?

I encounter a problem which is that the pop-up window cannot get the focus when it is shown. I tried to use the activefocus function in main window, but it doesn't work. It is supposed that if I press the enter key, the pop-window will be closed. How can I get the focus for the pop-up window? Thanks.
...
GridView {
id:grid_main
anchors.fill: parent
focus: true
currentIndex: 0
model: FileModel{
id: myModel
folder: "c:\\folder"
nameFilters: ["*.mp4","*.jpg"]
}
highlight: Rectangle { width: 80; height: 80; color: "lightsteelblue" }
delegate: Item {
width: 100; height: 100
Text {
anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }
text: fileName
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
}
}
}
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show();
subWindow.forceActiveFocus();
event.accepted = true;
grid_main.focus = false;
}
}
}
Window {
id: subWindow
Keys.onPressed: {
if (event.key == 16777220) {//press enter
subWindow.close();
}
}
}
...
Let's start with some basics:
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show()
...
event.accepted = true
}
}
Not to mention how error-prone it is, just for the sake of readability, please don't hard-code enum values like 16777220. Qt provides Qt.Key_Return and Qt.Key_Enter (typically located on the keypad) and more conveniently, Keys.returnPressed and Keys.enterPressed signal handlers. These convenience handlers even automatically set event.accepted = true, so you can replace the signal handler with a lot simpler version:
Keys.onReturnPressed: {
subWindow.show()
...
}
Now, the next thing is to find the correct methods to call. First of all, the QML Window type does not have such method as forceActiveFocus(). If you pay some attention to the application output, you should see:
TypeError: Property 'forceActiveFocus' of object QQuickWindowQmlImpl(0x1a6253d9c50) is not a function
The documentation contains a list of available methods: Window QML type. You might want to try a combination of show() and requestActivate().
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
Then, you want to handle keys in the sub-window. Currently, you're trying to attach QML Keys to the Window. Again, if you pay attention to the application output, you should see:
Could not attach Keys property to: QQuickWindowQmlImpl(0x1ddb75d7fe0) is not an Item
Maybe it's just the simplified test-case, but you need to get these things right when you give a testcase, to avoid people focusing on wrong errors. Anyway, what you want to do is to create an item, request focus, and handle keys on it:
Window {
id: subWindow
Item {
focus: true
Keys.onReturnPressed: subWindow.close()
}
}
Finally, to put the pieces together, a working minimal testcase would look something like:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: window
width: 300
height: 300
visible: true
GridView {
focus: true
anchors.fill: parent
// ...
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
}
Window {
id: subWindow
Item {
focus: true
anchors.fill: parent
Keys.onReturnPressed: subWindow.close()
}
}
}
PS. Key events rely on focus being in where you expect it to be. This may not always be true, if the user tab-navigates focus elsewhere, for example. Consider using the Shortcut QML type for a more reliable way to close the popup.

Resources