Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. "Dynamically" add QML menu items based on C++?

"Dynamically" add QML menu items based on C++?

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
12 Posts 4 Posters 1.9k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    Tom asso
    wrote on 7 Nov 2023, 02:28 last edited by Tom asso 11 Jul 2023, 02:28
    #1

    I’d like my QML to display a group of menu items, where the number of items and the name of each item is specified in accompanying C++ code. Currently my QML displays a statically-defined set of items, like this:

    Menu {
      title: "Color table"
      Action {checkable: true; checked: true; text: qsTr("Haxby");                      
      Action {checkable: true; text: qsTr("BrightRainbow");                
      Action {checkable: true; text: qsTr("MutedRainbow");                
      Action {checkable: true; text: qsTr("Grayscale");
    

    Is it possible for the QML to retrieve the item names and number from the C++?
    And can the QML “dynamically” add each item to the menu when it doesn’t a priori know how many items there are? From my reading it seems it would look something like this, assuming my C++ object has been registered as a singleton:

    Menu {
    title: "TEST"
    Component.onCompleted: {
      for (var i = 0; i < myObject.nItems; i++)  {
        howToInsertItem named myObject.name[i] into menu???                   
      }
    }
    

    Thanks!

    1 Reply Last reply
    0
    • J Offline
      J Offline
      jeremy_k
      wrote on 7 Nov 2023, 02:39 last edited by
      #2

      The documentation has a section on dynamic content, using Menu.addAction() or one of a set of similar functions.

      https://doc.qt.io/qt-6/qml-qtquick-controls-menu.html#dynamically-generating-menu-items

      Asking a question about code? http://eel.is/iso-c++/testcase/

      T 1 Reply Last reply 7 Nov 2023, 06:22
      4
      • J jeremy_k
        7 Nov 2023, 02:39

        The documentation has a section on dynamic content, using Menu.addAction() or one of a set of similar functions.

        https://doc.qt.io/qt-6/qml-qtquick-controls-menu.html#dynamically-generating-menu-items

        T Offline
        T Offline
        Tom asso
        wrote on 7 Nov 2023, 06:22 last edited by
        #3

        @jeremy_k - Awesome, thank you!

        T 1 Reply Last reply 17 Nov 2023, 23:56
        0
        • T Tom asso
          7 Nov 2023, 06:22

          @jeremy_k - Awesome, thank you!

          T Offline
          T Offline
          Tom asso
          wrote on 17 Nov 2023, 23:56 last edited by Tom asso
          #4

          @jeremy_k Thanks- looking at the link you provided, but I cannot find any examples of how Menu.addAction() (and other methods) are actually used in QML code. Do you know of an example? In my case, menu item names are stored in string array cmaps, and my QML looks like:

          Menu {
             title: "TEST"
             id: testMenu
             Component.onCompleted: {
             // Insert menu items with names 
             // from cmaps array	
             for (var i = 0; i < cmaps.length; i++)  { 
                console.log("map: ", cmaps[i])
                 testMenu.addItem({"text":cmaps[i]});
             } 
          }
          

          I can see the loop being executed, but items do not appear in the menu - do you know what I am missing? Thanks!

          J 1 Reply Last reply 21 Nov 2023, 01:15
          0
          • T Offline
            T Offline
            Tom asso
            wrote on 21 Nov 2023, 00:30 last edited by Tom asso
            #5

            I solved this with the following implementation, using Qt.createQmlObject() to create an Action and add it to the menu with Menu.addAction():

            Menu {
              title: "TEST"
              id: testMenu
              // Create and add Actions to menu
              Component.onCompleted: {
                // Insert menu items here, with number of items 
                // and item names as specified in the cmaps[] array that was
                // retrieved from C++
                for (var i = 0; i < cmaps.length; i++)  {
                  // Build QML string that specifies menu Action to add
                  var qmlStr = 
                     "import QtQuick.Controls 2.3; Action {id: myAction; text: \"" + cmaps[i] + "\"}";
                // Create the menu Action
                 var obj = 
                        Qt.createQmlObject(qmlStr,
                              testMenu,
                             "dynamicAction");
                                  
                 // Add created Action to the menu    
                 testMenu.addAction(obj);
               }
            }
            

            Thanks to @jeremy_k for the suggestion!

            B 1 Reply Last reply 21 Nov 2023, 13:35
            0
            • T Tom asso
              17 Nov 2023, 23:56

              @jeremy_k Thanks- looking at the link you provided, but I cannot find any examples of how Menu.addAction() (and other methods) are actually used in QML code. Do you know of an example? In my case, menu item names are stored in string array cmaps, and my QML looks like:

              Menu {
                 title: "TEST"
                 id: testMenu
                 Component.onCompleted: {
                 // Insert menu items with names 
                 // from cmaps array	
                 for (var i = 0; i < cmaps.length; i++)  { 
                    console.log("map: ", cmaps[i])
                     testMenu.addItem({"text":cmaps[i]});
                 } 
              }
              

              I can see the loop being executed, but items do not appear in the menu - do you know what I am missing? Thanks!

              J Offline
              J Offline
              jeremy_k
              wrote on 21 Nov 2023, 01:15 last edited by
              #6

              @Tom-asso said in "Dynamically" add QML menu items based on C++?:

              @jeremy_k Thanks- looking at the link you provided, but I cannot find any examples of how Menu.addAction() (and other methods) are actually used in QML code. Do you know of an example? In my case, menu item names are stored in string array cmaps, and my QML looks like:

              Menu {
                 title: "TEST"
                 id: testMenu
                 Component.onCompleted: {
                 // Insert menu items with names 
                 // from cmaps array	
                 for (var i = 0; i < cmaps.length; i++)  { 
                    console.log("map: ", cmaps[i])
                     testMenu.addItem({"text":cmaps[i]});
                 } 
              }
              

              I can see the loop being executed, but items do not appear in the menu - do you know what I am missing? Thanks!

              I see that you solved the problem in the following post. Perhaps you figured out the issue in the version above. If not:

              The reason it fails is that addItem() takes a Quick Item, but the code is passing a javascript dictionary object.

              Asking a question about code? http://eel.is/iso-c++/testcase/

              1 Reply Last reply
              1
              • T Tom asso
                21 Nov 2023, 00:30

                I solved this with the following implementation, using Qt.createQmlObject() to create an Action and add it to the menu with Menu.addAction():

                Menu {
                  title: "TEST"
                  id: testMenu
                  // Create and add Actions to menu
                  Component.onCompleted: {
                    // Insert menu items here, with number of items 
                    // and item names as specified in the cmaps[] array that was
                    // retrieved from C++
                    for (var i = 0; i < cmaps.length; i++)  {
                      // Build QML string that specifies menu Action to add
                      var qmlStr = 
                         "import QtQuick.Controls 2.3; Action {id: myAction; text: \"" + cmaps[i] + "\"}";
                    // Create the menu Action
                     var obj = 
                            Qt.createQmlObject(qmlStr,
                                  testMenu,
                                 "dynamicAction");
                                      
                     // Add created Action to the menu    
                     testMenu.addAction(obj);
                   }
                }
                

                Thanks to @jeremy_k for the suggestion!

                B Offline
                B Offline
                Bob64
                wrote on 21 Nov 2023, 13:35 last edited by Bob64
                #7

                @Tom-asso I know you have already got a solution, but I wonder if the approach suggested in this link might work for you. The idea is to define your dynamic menu entries by means of a model, and instantiate the items using a repeater. In your case you could expose the model from C++.

                From the link:

                Menu {
                    id:contextMenu
                    visible: true
                    Repeater {
                        model: menuItems
                        MenuItem {
                            text: modelData
                        }
                    }
                }
                

                The example instantiates MenuItem though, rather than Action, and Repeater is documented as only working with Item-based elements, which Action isn't. I don't know if there is a way around this, but if there were some way to get it to work, it would be a more "QML" way of doing it.

                T 1 Reply Last reply 22 Nov 2023, 17:15
                1
                • B Bob64
                  21 Nov 2023, 13:35

                  @Tom-asso I know you have already got a solution, but I wonder if the approach suggested in this link might work for you. The idea is to define your dynamic menu entries by means of a model, and instantiate the items using a repeater. In your case you could expose the model from C++.

                  From the link:

                  Menu {
                      id:contextMenu
                      visible: true
                      Repeater {
                          model: menuItems
                          MenuItem {
                              text: modelData
                          }
                      }
                  }
                  

                  The example instantiates MenuItem though, rather than Action, and Repeater is documented as only working with Item-based elements, which Action isn't. I don't know if there is a way around this, but if there were some way to get it to work, it would be a more "QML" way of doing it.

                  T Offline
                  T Offline
                  Tom asso
                  wrote on 22 Nov 2023, 17:15 last edited by
                  #8

                  @Bob64 - Thanks. In my application, the qml doesn't "know" the list of menu item names, which is provided by C++. But the C++ does not change that name list while the application is running, so it seems a bit more complex to use model rather than just a string array of names.

                  1 Reply Last reply
                  0
                  • J Offline
                    J Offline
                    jeremy_k
                    wrote on 22 Nov 2023, 19:27 last edited by
                    #9

                    @Tom-asso said in "Dynamically" add QML menu items based on C++?:

                    @Bob64 - Thanks. In my application, the qml doesn't "know" the list of menu item names, which is provided by C++. But the C++ does not change that name list while the application is running, so it seems a bit more complex to use model rather than just a string array of names.

                    A list is a valid model for Quick items that take a model.

                    import QtQuick
                    import QtQuick.Controls
                    import QtQml.Models
                    Window {
                        width: 100
                        height: 50
                        visible: true
                        Menu {
                            id: menu
                            Instantiator {
                                model: ["first", "second"]
                                delegate: MenuItem { text: modelData }
                                onObjectAdded: (index, object) => menu.insertItem(index, object)
                            }
                            Component.onCompleted: popup()
                        }
                    }
                    

                    Asking a question about code? http://eel.is/iso-c++/testcase/

                    GrecKoG 1 Reply Last reply 23 Nov 2023, 09:55
                    2
                    • J jeremy_k
                      22 Nov 2023, 19:27

                      @Tom-asso said in "Dynamically" add QML menu items based on C++?:

                      @Bob64 - Thanks. In my application, the qml doesn't "know" the list of menu item names, which is provided by C++. But the C++ does not change that name list while the application is running, so it seems a bit more complex to use model rather than just a string array of names.

                      A list is a valid model for Quick items that take a model.

                      import QtQuick
                      import QtQuick.Controls
                      import QtQml.Models
                      Window {
                          width: 100
                          height: 50
                          visible: true
                          Menu {
                              id: menu
                              Instantiator {
                                  model: ["first", "second"]
                                  delegate: MenuItem { text: modelData }
                                  onObjectAdded: (index, object) => menu.insertItem(index, object)
                              }
                              Component.onCompleted: popup()
                          }
                      }
                      
                      GrecKoG Offline
                      GrecKoG Offline
                      GrecKo
                      Qt Champions 2018
                      wrote on 23 Nov 2023, 09:55 last edited by GrecKo
                      #10

                      An Instantiator is not needed, Menu can handle dynamically added MenuItem by a Repeater.
                      Use the code provided by bob64.
                      And like jeremy_k said you don't have to pass it a full fledged QAbstractItemModel, a string list is fine.

                      J 1 Reply Last reply 23 Nov 2023, 16:41
                      0
                      • GrecKoG GrecKo
                        23 Nov 2023, 09:55

                        An Instantiator is not needed, Menu can handle dynamically added MenuItem by a Repeater.
                        Use the code provided by bob64.
                        And like jeremy_k said you don't have to pass it a full fledged QAbstractItemModel, a string list is fine.

                        J Offline
                        J Offline
                        jeremy_k
                        wrote on 23 Nov 2023, 16:41 last edited by
                        #11

                        @GrecKo said in "Dynamically" add QML menu items based on C++?:

                        An Instantiator is not needed, Menu can handle dynamically added MenuItem by a Repeater.

                        Except that as @Bob64 points out, Repeater cannot handle non-Item delegates. It will work for MenuItem, but not Menu or Action.

                        Asking a question about code? http://eel.is/iso-c++/testcase/

                        GrecKoG 1 Reply Last reply 23 Nov 2023, 22:41
                        0
                        • J jeremy_k
                          23 Nov 2023, 16:41

                          @GrecKo said in "Dynamically" add QML menu items based on C++?:

                          An Instantiator is not needed, Menu can handle dynamically added MenuItem by a Repeater.

                          Except that as @Bob64 points out, Repeater cannot handle non-Item delegates. It will work for MenuItem, but not Menu or Action.

                          GrecKoG Offline
                          GrecKoG Offline
                          GrecKo
                          Qt Champions 2018
                          wrote on 23 Nov 2023, 22:41 last edited by
                          #12

                          @jeremy_k not working with Action is not really an issue since you are in charge of chosing the delegate type, so you can create a MenuItem instead (that's what the Menu does anyway when adding an Action).

                          Instantiator is indeed needed for nested Menus.

                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved