I want to dynamically build a QML context menu.
When I call 'addMenu' the menu entry is added, but I get this warning:
QQmlComponent: Created graphical object was not placed in the graphics scene.
Here is the code to reproduce the issue:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
Menu {
id:contextMenu
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
contextMenu.addMenu("NewMenu");
contextMenu.popup();
}
}
}
What am I doing wrong here?
This looks like a bug in Qt to me. If you look at Menu.qml (where the Menu QML component is defined), addMenu is defined as follows:
function addMenu(title) {
return root.insertMenu(items.length, title)
}
function insertMenu(index, title) {
if (!__selfComponent)
__selfComponent = Qt.createComponent("Menu.qml", root)
var submenu = __selfComponent.createObject(__selfComponent, { "title": title })
root.insertItem(index, submenu)
return submenu
}
/*! \internal */
property Component __selfComponent: null
The important line here is __selfComponent.createObject(__selfComponent, { "title": title }). This sets __selfComponent (the Menu component, not the menu itself) as the parent for the newly-created sub menu. I believe that this is wrong, the parent should instead be set to root, the menu itself.
Add visible: true to ApplicationWindow like that:
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
Menu {
id:contextMenu
}
...
Maybe that helps.
Related
working on an application using qml for the interface. I have a stack view in the main qml file which will push the qml file which will be a page to the stack view. Because there are multiple pages and buttons in these pages that when clicked may push a different page onto the stack view, I created a function in the main qml file which i will call from the pages when buttons are clicke. The pages folder which contain many different pages is a subdirectory of main.qml folder. The functionis supposed to push the pages unto the stack view. However, on click of the button, the satck view does not push the new page.
This is the code below
main qml file
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import "pages"
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property url source: ""
function changeView(source){
stackView.push(Qt.resolvedUrl(source))
}
Rectangle {
id: rectangle
color: "#391199"
anchors.fill: parent
StackView {
id: stackView
anchors.fill: parent
initialItem: Qt.resolvedUrl("pages/homepage.qml")
}
}
}
/*##^##
Designer {
D{i:0;formeditorZoom:0.75}
}
##^##*/
homePage.qml
import QtQuick 2.0
import QtQuick.Controls 2.15
import "qrc:../main.qml" as Main
Item {
Rectangle {
id: rectangle
color: "#08630f"
anchors.fill: parent
Button {
id: button
x: 478
y: 255
text: qsTr("Change ")
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: 10
anchors.bottomMargin: 10
onClicked: {
source = "pages/nextPage.qml"
Main.changeView(source)
}
}
}
}
nextPage.qml
import QtQuick 2.0
import QtQuick.Controls 2.15
import "../main.qml" as Main
Item {
Rectangle {
id: rectangle
color: "#08404b"
anchors.fill: parent
Button {
id: button
x: 478
y: 255
text: qsTr("Change ")
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: 10
anchors.bottomMargin: 10
onClicked: {
Main.changeView("pages/homePage.qml")
}
}
}
}
I suspect the problem is coming from the calling of the function but i have a little expertise in qml. I get this error: Property 'changeView' of object [object Object] is not a function
You will want to call the changeView function on the actual main window, not on the import.
This involves a bit of "magic", you can give the main Window an id, which will be available in all the children of it (so, magic because if looking at a single page file, you can wonder what that id is doing there). So, make sure to pick a wise name which won't collide with other id (i.e. root would be quite bad)
//main.qml
Window {
id: main_window
function changeView(source) { ... }
}
//homePage.qml
Item {
...
Button {
...
onClicked: main_window.changeView("pages/nextPage.qml")
}
}
The following code:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.5
import QtQuick.Controls.Styles 1.4
Window {
visible: true
width: 640
height: 480
title: qsTr("This is my application title!")
ColumnLayout
{
id: col1
spacing: 2
MenuBar
{
Menu {
title: "File"
MenuItem {
text: "Open"
Shortcut: "Ctrl+O"
onTriggered: console.log("Ctrl+O trigged")
}
MenuItem { text: "Paste link from Ctrl+V" }
MenuItem { text: "Save log as" }
}
Menu { title: "Help" }
Menu { title: "About" }
Menu { title: "Exit" }
}
}
Give the following error:
qrc:/main.qml:25:21: Invalid attached object assignment
the line on error is Shortcut: "Ctrl+O". The Qt documentation give example like this. What am I missing?
edit: added documentation link.
edit 2: updated imports
In qml there are at least 2 groups of controls:
Qt Quick Controls 1
Qt Quick Controls 2
These groups have components with the same one that is the cause of your error since you try to apply the property of the MenuItem from one group to another (check the imports so that you realize the error).
QQC1 MenuItem
QQC2 MenuItem
Depending on which group you want to use, there are different options:
Qt QuickControls 1
import QtQuick 2.12
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("This is my application title!")
menuBar: MenuBar{
Menu {
title: "File"
MenuItem {
text: "Open"
shortcut: "Ctrl+O"
onTriggered: console.log("Ctrl+O trigged")
}
MenuItem{ text: "Paste link from Ctrl+V" }
MenuItem { text: "Save log as" }
}
Menu { title: "Help" }
Menu { title: "About" }
Menu { title: "Exit" }
}
}
Qt QuickControls 2
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("This is my application title!")
menuBar: MenuBar{
Menu {
title: "File"
Action {
text: "Open"
shortcut: "Ctrl+O"
onTriggered: console.log("Ctrl+O trigged")
}
Action { text: "Paste link from Ctrl+V" }
Action { text: "Save log as" }
}
Menu { title: "Help" }
Menu { title: "About" }
Menu { title: "Exit" }
}
}
Possibly you are going to have a similar problem with styles so it is recommended that you read this answer where I point out that using namespace can be a solution if you want to combine components of both modules.
Note: QML is case sensitive, in the docs you indicate it indicates shortcut but you use Shortcut.
I start "QML App with controls" project in Qt Creator. I see that I can add to canvas different kind of controls, but I do not see how I can in graphical mode edit menu like: File, View, Edit... In constructor on canvas it's simple do not exists, but it's exists of running app, like http://img.ctrlv.in/img/15/10/03/560f856edb26c.png
You can create the menu in the main.qml file, here is an example application:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&Open")
onTriggered: messageDialog.show(qsTr("Open action triggered"));
}
MenuItem {
text: qsTr("Save")
onTriggered: messageDialog.show(qsTr("Save action triggered"));
}
}
Menu {
title: qsTr("&Help")
MenuItem {
text: qsTr("About")
onTriggered: messageDialog.show(qsTr("About: test QML app with menu"));
}
}
}
MainForm {
anchors.fill: parent
button1.onClicked: messageDialog.show(qsTr("Button 1 pressed"))
button2.onClicked: messageDialog.show(qsTr("Button 2 pressed"))
}
MessageDialog {
id: messageDialog
title: qsTr("Message Test")
function show(caption) {
messageDialog.text = caption;
messageDialog.open();
}
}
}
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.
I tried to call functions of qml file from another qml file user component id but i am facing some issues. could some one help me out of this.
here is my code.
Browser.qml:
import QtQuick 2.0
Item {
function callme(message) {
console.log(message)
}
}
main.qml:
import QtQuick 2.3
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.0
ApplicationWindow {
visible: true
width: 640
height: 100
TabView {
id: tabView
width: 640
height: 50
Tab {
width: 100
title: "Sample1.html"
onVisibleChanged: {
browser1.callme("hi")
}
Browser {
id: browser1
}
}
Tab {
width: 100
title: "Sample2.html"
onVisibleChanged: {
browser2.callme("bye")
}
Browser {
id: browser2
}
}
}
}
Error reported:
ReferenceError: browser1 is not defined
If you want access to items inside Tab control, you have to use its item property. I have changed your signal handler and it works:
...
onVisibleChanged: {
item.callme("hi")
}
Browser{
id: browser1
}
...
Tab control inherits from Loader component. It takes its children as delegate and they are only created with the tab is activated. Most of the behavior is the same then the Loader component.
Experimentation for the record
What happend if we define two or more components inside a Tab? Loader component only accepts a delegate and the component created is accessed by item property. Tab component maps children property to delegate and you can define more than one, but I realized that only the last child is created.