I'm trying to fill a Menu dynamically from a ListModel, but this approach won't work (when I right click the menu won't show anything):
this my menuItems:
import QtQuick.Controls 1.3
ListModel{
id:menuItems
ListElement{
text:"hello1"
}
ListElement{
text:"hello2"
}
ListElement{
text:"hello3"
}
}
and this my menu
Menu{
id:contextMenu
Repeater{
model: menuItems
MenuItem{}
}
I even tried to put a an Instantiator but the menu won't show anything
After looking in documentation I figured out how to achieve that:
Menu {
id: contextMenu
Instantiator {
model: menuItems
MenuItem {
text: model.text
}
// The trick is on those two lines
onObjectAdded: contextMenu.insertItem(index, object)
onObjectRemoved: contextMenu.removeItem(object)
}
}
You just need to add the text for every single ListElement to your MenuItem like this:
Menu{
id:contextMenu
visible: true
Repeater {
model: menuItems
MenuItem {
text: modelData
}
}
}
I also added "visible: true" to your Menu to show it(I dont know if you are opening it somewhere else).
Related
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";
}
}
I'm trying to create a tabbed pages in qml. I used TabBar associated with StackLayout:
TabBar {
id: bar
width: parent.width
TabButton {
text: qsTr("Home")
}
TabButton {
text: qsTr("Discover")
}
TabButton {
text: qsTr("Activity")
}
}
StackLayout {
width: parent.width
currentIndex: bar.currentIndex
Item {
id: homeTab
}
Item {
id: discoverTab
}
Item {
id: activityTab
}
}
A new tabButton can be easily added by this code dynamically:
var tab = tabButton.createObject(TTabButton, {text: tabName});
bar.addItem(tab);
which TTabButton is a separate file consisting TabButton item. But I can't find any ways to add a new page to StackLayout. It seems it is supposed to be static. So my question is how to have dynamic tab-paged in qml?
You can add to the children of StackLayout:
var item = stackItem.createObject(null, {id: "tabName"})
layout.children.push(item)
Where stackItem is the Component of those items you add to your StackLayout layout.
You can manipulate the children (or data) property of the StackLayout directly as suggested, but potentially a better idea is to use a Repeater in conjunction with an ObjectModel:
StackLayout
{
id: stackLayout
currentIndex: bar.currentIndex
Repeater
{
model: ObjectModel
{
id: container
}
}
}
This way you get the much richer and easier API of ObjectModel:
let tabPage = tabPageComponent.createObject(stackLayout);
container.append(tabPage);
let tabPage2 = tabPageComponent.createObject(stackLayout);
container.insert(0, tabPage2);
container.move(1, 0);
// etc.
One important detail here is that when removing a tab, it's necessary to manually destroy() it, since the Repeater/ObjectModel only manages the hierarchy, not lifetime.
let tabPage = container.get(0);
container.remove(0);
tabPage.destroy();
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();
}
}
}
}
Straitforward approach to create a multiple separate visible pages with easy navigation is to use SwipeView and TabBar together:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Page {
id: page
header: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("Page1")
}
TabButton {
text: qsTr("Page2")
}
}
SwipeView {
id: swipeView
width: page.width
height: page.height
currentIndex: tabBar.currentIndex
Page {
width: swipeView.width
height: swipeView.height
title: qsTr("Page1")
Pane {
anchors.fill: parent
}
}
Page {
width: swipeView.width
height: swipeView.height
title: qsTr("Page2")
Pane {
anchors.fill: parent
}
}
}
}
There is title property in the Page component. It is not visible in the above case, but it is proper place to store the tab title on my mind. Can I generate (say, using Repeater) a contents of TabBar using some properties of SwipeView pages (namely .title) to fill TabButton.text?
I looked through documentation, but can't find a property of SwipeView, which allows me to get access to its pages by means of indices (e.g. pages[index]).
Qt Quick is great! I found the answer really quickly:
Repeater {
model: swipeView.count
TabButton {
text: swipeView.contentChildren[index].title
}
}
or even simplier
Repeater {
model: swipeView.contentChildren
TabButton {
text: modelData.title
}
}
I have a Widget as QML component in Qt Quick application which is to be used in various screens to display contents.
How can I use this particular QML component to adjust according to the items in it.
If it's a generic Item you can't: you have to manually set the container's size to fit its content.
The only QML components that fits their content are the Row, Column and Grid elements.
coming in way late, but if you want to have an update-able component you can set the model for the component to any list model like:
Component{
id:comp1
model:model1
}
ListModel {
id: model1
ListElement{
name:"a"
}
ListElement{
name: "b"
}
}
Component {
id: fruitDelegate
Row {
spacing: 10
Text { text: name }
}
}
ListView {
id:listView1
anchors.fill: parent
model: fruitModel
delegate: fruitDelegate
contentWidth: Screen.width
}
then you can update the listview at will
TextInput{
id: text_input1
width:parent.width * 0.80
text:"waddup?"
}
Button {
id: button2
anchors.left: text_input1.right
text: qsTr("Send")
onClicked: {
listView1.model.append( {name: text_input1.text, colorCode:"Red" });
/*text_input1.text = ""*/
}
}