chila
chila

Reputation: 2442

Error adding a Menu in QML

I have the following code:

import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls 1.4

ApplicationWindow {
    title: qsTr("Hello World!")
    width: 640
    height: 480
    visible: true

    menuBar: MenuBar {
        id: menuBar
    }

    MouseArea
    {
        anchors.fill: parent
        onClicked: { menuBar.menus.addItem("test") }
    }
}

When I run it and click, the following message appears:

qrc:/main.qml:19: TypeError: Property 'addItem' of object [object Object] is not a function

Why is this?

EDIT: Getting the advice from https://stackoverflow.com/users/24283/timday I did this:

import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls 1.4

ApplicationWindow {
    title: qsTr("Hello World!")
    width: 640
    height: 480
    visible: true

    menuBar: MenuBar
    {
        id: menuBar

        function addMenu(text)
        {
            var newObject = Qt.createQmlObject('import QtQuick.Controls 1.4; Menu { id: test; title: "Test" }',
                menuBar, "dynamicSnippet1");

            newObject.visible = true
        }
    }

    MouseArea
    {
        anchors.fill: parent
        onClicked: { menuBar.addMenu("Test") }
    }
}

However, I cannot get the menu to show.

EDIT: Since it seems impossible to do what I want, I ended up with the recommendation of timday:

import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls 1.4

ApplicationWindow {
    title: qsTr("Hello World!")
    width: 640
    height: 480
    visible: true

    menuBar: MenuBar
    {
        id: menuBar

        Menu { id: menu00; visible: false; }
        Menu { id: menu01; visible: false; }
        Menu { id: menu02; visible: false; }
        Menu { id: menu03; visible: false; }
        Menu { id: menu04; visible: false; }
        Menu { id: menu05; visible: false; }
        Menu { id: menu06; visible: false; }
        Menu { id: menu07; visible: false; }
        Menu { id: menu08; visible: false; }
        Menu { id: menu09; visible: false; }
        Menu { id: menu10; visible: false; }
        Menu { id: menu11; visible: false; }
        Menu { id: menu12; visible: false; }
        Menu { id: menu13; visible: false; }
        Menu { id: menu14; visible: false; }
        Menu { id: menu15; visible: false; }
        Menu { id: menu16; visible: false; }
        Menu { id: menu17; visible: false; }
        Menu { id: menu18; visible: false; }
        Menu { id: menu19; visible: false; }

        property variant topMenus: [ menu00, menu01, menu02, menu03, menu04,
                                     menu05, menu06, menu07, menu08, menu09,
                                     menu10, menu11, menu12, menu13, menu14,
                                     menu15, menu16, menu17, menu18, menu19 ]
        property int currMenu: 0

        function addMenu(text)
        {
            if (currMenu == topMenus.length)
                console.log("Index out of range")
            else
            {
                var menu = topMenus[currMenu]
                menu.visible = true
                menu.title = text
                currMenu++
                return menu
            }
        }
    }

    MouseArea
    {
        anchors.fill: parent
        onClicked: { menuBar.addMenu("Test") }
    }
}

Upvotes: 2

Views: 2143

Answers (2)

Max Bespalov
Max Bespalov

Reputation: 3

I found a solution for "showing" problem. Digging in MenuBarPrivate gave me an idea to use menusChanged() signal and it actually works.

Menu {
   id: foo
   ...
}

MenuBar {
   Component.onCompleted: {
        menus.push(foo)
        menusChanged()
    }
}

Upvotes: 0

timday
timday

Reputation: 24892

You need to actually have a Menu to your MenuBar to add MenuItems to. Like this:

import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls 1.4

ApplicationWindow {
  title: qsTr("Hello World!")
  width: 640
  height: 480
  visible: true

  menuBar: MenuBar {
    id: menuBar
    Menu {
      id: tests
      title: "Tests"
    }
  }

  MouseArea
  {
    anchors.fill: parent
    onClicked: { 
      tests.addItem("Test");
    }
  }
}

This (run with Qt5.5.0's qmlscene) starts with a "Tests" Menu in the bar, and adds a "Test" item to it every time you click (away from the menubar). You have to click to open the menu to see the items of course.

Dynamic creation of Menus is a little harder; see Qt.createQmlObject or Qt.createComponent docs. (It may be simpler to just declare all the ones you need in your code, but with their visible property wired to whatever logic is appropriate).

Update: as you note in your updated question, and I've just confirmed myself, simply adding a dynamically created Menu as a child of MenuBar seems to be insufficient to get the Menu to appear. I note it also doesn't result in the MenuBar's menus list getting any bigger. Unfortunately it's not easy to append to QML lists, they're different from JavaScript arrays. And there may be something funny about MenuBar... even attempting to assign a new list of menus, or an empty list, to it results in an error message. Might be worth raising an issue/request for better (or easier, if it is possible somehow) dynamic MenuBar Menu item in the QtJira... but I suspect any restrictions may arise from Qt's use of native menus on some platforms, forcing least-common-denominator levels of functionality maybe.

For a "Plan B" using initially hidden placeholders, this works sensibly on my Linux system:

import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls 1.4

ApplicationWindow {
  title: qsTr("Hello World!")
  width: 640
  height: 480
  visible: true

  menuBar: MenuBar {
    id: menubar
    Menu {title: "File"}
    Menu {id: testsmenu;title: "Tests";visible: false}
    Menu {title: "Help"}
  }

  MouseArea {
    anchors.fill: parent
    onClicked: { 
      testsmenu.visible=true
      testsmenu.addItem("Test")
    }
  }
}

More general point: it occurs to me I'm slightly suspicious of any application design which is predicated on having a very dynamic set of menu bar menus to show the user. Sort of the whole point of the menu UX pattern is that it's pretty static and users' "muscle memory" lets them navigate it fast... but if menus are fairly randomly coming and going that'll break. (OK some applications might present a different set of menus in a couple of different modes e.g IDEs with edit/debug modes, but that'd be quite doable with the "Plan B" style above and QML's notion of states and wiring menu visibility to application state).

Upvotes: 2

Related Questions