【问题标题】:What's the fastest way to create menu programmatically in C++11/Gtkmm3?在 C++11/Gtkmm3 中以编程方式创建菜单的最快方法是什么?
【发布时间】:2017-01-09 18:47:15
【问题描述】:

在不使用 glade 的情况下,在 C++11/Gtkmm3 中以编程方式创建菜单的最快方法是什么?我正在寻找这样简单的东西:

      Gtk::MenuBar* menubar = ezmenubar.create(
        {"MenuBar1",
        "File.New",
        "File.Open",
        "File.Save",
        "File.Separate1:-",
        "File.Coffee:check!",
        "File.Cream:check!",
        "File.Sugar:check",
        "File.Separate2:-",
        "File.Donuts:check",
        "Edit.nested.item1:radio!",
        "Edit.nested.item2:radio",
        "Edit.nested.item3:radio",
        "Edit.Copy",
        "Edit.Cut",
        "Radio.On:radio!",
        "Radio.Off:radio",
        "Radio.Random:radio",
        "Help.About"}
      );
      add(m_box);
      m_box.pack_start(*menubar, 0, 0);

【问题讨论】:

    标签: c++ c++11 gtkmm3


    【解决方案1】:

    你的问题有点误导。如果您想手动构建它,我想向您推荐 gtkmm 主页上的 app_and_win_menus 示例:https://git.gnome.org/browse/gtkmm-documentation/tree/examples/book/application/app_and_win_menus

    请在下面找到有关如何在启动期间构建应用程序菜单的摘录。

    void ExampleApplication::on_startup(){
        //Call the base class's implementation:
        Gtk::Application::on_startup();
    
        auto app_menu = Gio::Menu::create();
        app_menu->append("_Something", "app.something");
        app_menu->append("_Quit", "app.quit");
        set_app_menu(app_menu);
    
        // [...]
    }
    

    完整的示例将为您提供有关如何使用 Gio::Menu 类函数创建菜单的良好概述。请在 https://developer.gnome.org/glibmm/stable/classGio_1_1Menu.html 的 Gui::Menu 类参考页面上找到更多信息

    【讨论】:

      【解决方案2】:

      可能有更好的方法可以在不使用 xml 的情况下执行此操作,但是,因为创建 gtkmm3 菜单的官方方法是根据 gktmm3 手册在源代码中嵌入 xml 字符串。这是一个为您完成杂乱的非人类友好的 xml 工作的类。希望 gnome 中的某个人会明白人们讨厌在他们的 C++ 代码中编写 xml 并切换回这样的内容:

      #include <gtkmm.h>
      #include <string>
      #include <iostream>
      #include <vector>
      #include <map>
      
      using namespace std;
      
      class EzMenuBar
      {
      public:
          EzMenuBar(Gtk::Window* window) : window{window}
          {}
      
          void create(initializer_list<const char*> ilist)
          {
              string         menu_name = "noname";
              vector<string> slist;
              menubar = nullptr;
              int i = 0;
              for(string e : ilist)
              {
                  if (i==0) menu_name = e;
                  else slist.push_back(e);
                  i++;
              }
              PrivateGetMenuBar(menu_name, slist);
              return;
          }
      
          void add_click(const string& name, const sigc::slot<void>& slot)
          {
              Gtk::MenuItem* menuitem;
              builder->get_widget(name, menuitem);
              if (!menuitem)
              {
                  throw std::runtime_error{string{"Error: widget does not exist:("} + name + string{")\n"}};
              }
              menuitem->signal_activate().connect(slot);
          }
      
          bool is_checked(const string& name) {
              Gtk::CheckMenuItem* menuitem;
              builder->get_widget(name, menuitem);
              if (!menuitem)
              {
                  throw std::runtime_error{string{"Error: widget does not exist:("} + name + string{")\n"}};
              }
              bool active = menuitem->get_active();
              //delete menuitem; //memory leak? or managed by builder object?
              return active;
          }
      
      
          Gtk::MenuBar& operator()()
          {
              return *menubar;
          }
      
      
      private:
          void PrivateGetMenuBar(string menu_name, vector<string>& ilist)
          {
              string MenuXml = BuildMenuXml(menu_name, ilist);
      
              try
              {
                  builder = Gtk::Builder::create_from_string(MenuXml);
              }
              catch (...)
              {
                  throw std::runtime_error{string{"Error: Menu XML Format:("} + menu_name + string{")\n"}};
              }
      
              builder->get_widget(menu_name, menubar);
              if (!menubar)
              {
                  throw std::runtime_error{string{"Error: widget does not exist:("} + menu_name + string{")\n"}};
              }
              return;
          }
      
          string BuildMenuXml(string menu_name, vector<string>& list)
          {
              tree.clear();
      
              auto top = pair<string, vector<string>>
              {
                  menu_name,
                  vector<string>{}
              };
      
              tree.insert(top);
      
              for (string x : list)
              {
                  string leaf_last {menu_name};
                  int    leaf_i = 0;
                  vector<string> branchlist = StrSplit('.', x);
                  for (string& leaf_this : branchlist)
                  {
                      if (tree.count(leaf_this) == 0)
                      {
                          auto newpair = pair<string, vector<string>>
                          {
                              leaf_this,
                              vector<string>{}
                          };
                          tree.insert(newpair);
                          tree[leaf_last].push_back(leaf_this);
                      }
                      leaf_last = leaf_this;
                      leaf_i++;
                  } // foreach leaf of treeachy
              } // foreach menuItem in list
      
              string xml = BuildIt1(menu_name);
              #if 1
              cout << xml << "\n";
              cout << "NOTES: to speed up, remove debug printing near:\n ";
              cout << "     " << __FILE__ << ":" << __LINE__ << "\n";
              cout << "\n";
              #endif
              return xml;
          }
      
          string BuildIt1(string menu_name)
          {
              string xml;
              if (!tree.count(menu_name))
                  return string{""};
              xml += "<interface>\n";
              xml += "    <!-- MENU BAR: " + menu_name + " -->\n";
              xml += "    <object class=\"GtkMenuBar\" id=\"" + menu_name + "\">\n";
              xml += "    <property name=\"visible\">True</property>\n";
              xml += "    <property name=\"can_focus\">False</property>\n";
              for (string leaf : tree[menu_name])
              {
                  xml += BuildIt2(string{menu_name + "." + leaf}, leaf, 1);
              }
              xml += "\n";
              xml += "    </object>\n";
              xml += "</interface>\n";
              return xml;
          }
      
          string BuildIt2(string fullpath, string leaf, int level)
          {
      
              string xml;
              if (!tree.count(leaf))
              {
                  return string{""};
              }
      
              int count    = tree[leaf].size();
              vector<string> tmp = StrSplit(':', fullpath);
              string fullpath_only = tmp[0];
              string label = StrSplitLast('.', fullpath_only);
              string attrib;
              string action_id = fullpath_only;
              size_t idpos = action_id.find_first_of('.');
              for(int i=idpos+1; i < (int)action_id.size(); i++) {
                  if (action_id[i]=='.') action_id[i]='_';
              }
      
              if (tmp.size() == 2)
              {
                  //cout << "TMP:(" << tmp[1] << ")\n";
                  attrib = tmp[1];
              }
      
              // GtkMenuItem
              if (count == 0)
              {
      
                  xml += indent(level) + "\n";
                  xml += indent(level) + "<!-- MENU ITEM: " + fullpath + " -->\n";
                  if (attrib == "-")
                  {
                      radio_group = string{};
                      xml += indent(level) + "<child><object class=\"GtkSeparatorMenuItem\" id=\"" + action_id + "\">\n";
                      xml += indent(level) + "<property name=\"visible\">True</property>\n";
                      xml += indent(level) + "<property name=\"can_focus\">False</property>\n";
                  }
                  else if (attrib == "check" || attrib == "check!")
                  {
                      radio_group = string{};
                      xml += indent(level) + "<child><object class=\"GtkCheckMenuItem\" id=\"" + action_id + "\">\n";
                      xml += indent(level) + "<property name=\"visible\">True</property>\n";
                      xml += indent(level) + "<property name=\"can_focus\">False</property>\n";
                      xml += indent(level) + "<property name=\"label\" translatable=\"yes\">" + label + "</property>\n";
                      xml += indent(level) + "<property name=\"use_underline\">True</property>\n";
                      if (attrib == "check!")
                      {
                          xml += indent(level) + "<property name=\"active\">True</property>\n";
                          // Not using xml signals. How to connect glade signals from c++??
                          xml += indent(level) + "<signal name=\"toggled\" handler=\"" + action_id + "\" swapped=\"no\"/>\n";
                      }
                  }
                  else if (attrib == "radio" || attrib == "radio!")
                  {
                      bool group_start = false;
                      if (radio_group.empty())
                      {
                          group_start = true;
                          radio_group = action_id; //fullpath_only;
                      }
                      xml += indent(level) + "<child><object class=\"GtkRadioMenuItem\" id=\"" + action_id + "\">\n";
                      xml += indent(level) + "<property name=\"visible\">True</property>\n";
                      xml += indent(level) + "<property name=\"can_focus\">False</property>\n";
                      xml += indent(level) + "<property name=\"label\" translatable=\"yes\">" + label + "</property>\n";
                      xml += indent(level) + "<property name=\"use_underline\">True</property>\n";
                      if (attrib == "radio!")
                      {
                          xml += indent(level) + "<property name=\"active\">True</property>\n";
                      }
                      xml += indent(level) + "<property name=\"draw_as_radio\">True</property>\n";
                      xml += indent(level) + "<property name=\"group\">" + radio_group + "</property>\n";
                      if (group_start) {
                      xml += indent(level) + "<signal name=\"group-changed\" handler=\"" + action_id + "\" swapped=\"no\"/>\n";
                      }
                  }
                  else
                  {
                      radio_group = string{};
                      xml += indent(level) + "<child><object class=\"GtkMenuItem\" id=\"" + action_id + "\">\n";
                      xml += indent(level) + "<property name=\"visible\">True</property>\n";
                      xml += indent(level) + "<property name=\"can_focus\">False</property>\n";
                      xml += indent(level) + "<property name=\"label\" translatable=\"yes\">" + label + "</property>\n";
                      xml += indent(level) + "<property name=\"use_underline\">True</property>\n";
                      xml += indent(level) + "<signal name=\"activate\" handler=\"" + action_id + "\" swapped=\"no\"/>\n";
                  }
              }
              // GtkMenu
              else
              {
                  xml += indent(level) + "\n";
                  xml += indent(level) + "<!-- SUB-MENU: " + fullpath + " -->\n";
                  xml += indent(level) + "<child><object class=\"GtkMenuItem\" id=\"" + action_id + "\">\n";
                  xml += indent(level) + "<property name=\"visible\">True</property>\n";
                  xml += indent(level) + "<property name=\"can_focus\">False</property>\n";
                  xml += indent(level) + "<property name=\"label\" translatable=\"yes\">" + label + "</property>\n";
                  xml += indent(level) + "<child type=\"submenu\"><object class=\"GtkMenu\" id=\"" + fullpath_only + ".submenu" + "\">\n";
                  xml += indent(level) + "<property name=\"visible\">True</property>\n";
                  xml += indent(level) + "<property name=\"can_focus\">False</property>\n";
              }
      
              level++;
              for (string child : tree[leaf])
              {
                  xml += BuildIt2(string{fullpath + string{"."} + child}, child, level);
              }
              level--;
              if (count == 0)
              {
                  xml += indent(level) + "</object></child>\n";
              }
              else
              {
                  xml += indent(level) + "</object></child>\n";
                  xml += indent(level) + "</object></child>\n";
              }
              return xml;
          }
      
      
          string indent(int level)
          {
              string INDENT;
              for(int i=0; i < level; i++) INDENT += "    ";
              return INDENT;
              //return string{level, ' '};
          }
      
          static vector<string> StrSplit(char delimit, string& line)
          {
              vector<string> split;
              size_t pos_last = -1;
              while(1)
              {
                  size_t pos_this = line.find_first_of(delimit, pos_last+1);
                  if (pos_this == string::npos)
                  {
                      split.push_back(line.substr(pos_last+1));
                      break;
                  }
                  split.push_back(line.substr(pos_last+1, pos_this-pos_last-1));
                  pos_last = pos_this;
              }
              return split;
          }
      
          static string StrSplitLast(char delimit, string& line)
          {
              size_t pos = line.find_last_of(delimit);
              if (pos == string::npos)
              {
                  return line;
              }
              return line.substr(pos+1);
          }
      
      private:
          Gtk::MenuBar*                  menubar;
          Gtk::Window*                   window;
          string                         radio_group;
          Glib::RefPtr<Gtk::Builder>     builder;
          map<string, vector<string>>    tree;
          string                         MenuXml;
      };
      
      
      class WnMain : public Gtk::Window
      {
      public:
          void callback() {
            cout << "HELLO\n";
          }
      
          WnMain()
          {
              ezmenubar.create({
                  "MenuBar1",
                  "File.New",
                  "File.Open",
                  "File.Save",
                  "File.Separate1:-",
                  "File.Coffee:check!",
                  "File.Cream:check!",
                  "File.Sugar:check",
                  "File.Separate2:-",
                  "File.Donuts:check",
                  "Edit.nested.item1:radio!",
                  "Edit.nested.item2:radio",
                  "Edit.nested.item3:radio",
                  "Edit.Copy",
                  "Edit.Cut",
                  "Radio.On:radio!",
                  "Radio.Off:radio",
                  "Radio.Random:radio",
                  "Help.About"
              });
      
              ezmenubar.add_click("MenuBar1.File_New", sigc::mem_fun(*this, &WnMain::callback));
      
              ezmenubar.add_click("MenuBar1.File_Open",
                [&]() {cout << "FILE.Open\n";}
              );
      
              ezmenubar.add_click("MenuBar1.Radio_On",
                [&]() {
                   // NOTE: signal_active is buggy for RadioMenuItem under windows,
                   //   ie. not always triggering...70% of time? i'll leave that one to gnome.org
                   //   to cleanup...(1/9/2017)
                   if (ezmenubar.is_checked("MenuBar1.Radio_On")) {
                      cout << "MenuBar1.Radio_On: checked\n";
                   }
                   else if (ezmenubar.is_checked("MenuBar1.Radio_Off")) {
                      cout << "MenuBar1.Radio_Off: checked\n";
                   }
                   else if (ezmenubar.is_checked("MenuBar1.Radio_Random")) {
                      cout << "MenuBar1.Radio_Random: checked\n";
                   }
                   else {
                      cout << "MenuBar1.Radio: nothing selected\n";
                   }
              });
      
              ezmenubar.add_click("MenuBar1.File_Coffee",
                [&]() {
                   if (ezmenubar.is_checked("MenuBar1.File_Coffee")) {
                      cout << "File.Coffee: checked\n";
                   }
                   else {
                      cout << "File.Coffee: not-checked\n";
                   }
              });
      
              add(m_box);
              m_box.pack_start(ezmenubar());
              show_all_children();
          }
      
      private:
          Gtk::Box                    m_box {Gtk::ORIENTATION_VERTICAL};
          EzMenuBar                   ezmenubar {this};
          string                      radio_group;
      };
      
      int main(int argc, char** argv)
      {
          try
          {
              auto app = Gtk::Application::create(argc, argv, "wmoore");
              WnMain wnmain;
              return app->run(wnmain);
          }
          catch (std::runtime_error e)
          {
              cout << "EXCEPTION:" << e.what() << "\n";
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        • 1970-01-01
        • 2012-09-20
        • 1970-01-01
        • 2017-10-07
        相关资源
        最近更新 更多