2017-01-09 70 views
-2

在C++ 11/Gtkmm3中不使用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); 

回答

0

有可能是一個更好的方式來做到這一點,而不但是使用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"; 
    } 
} 
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類函數創建菜單的很好概述。請查詢更多關於Gui :: Menu Class Reference頁面的信息:https://developer.gnome.org/glibmm/stable/classGio_1_1Menu.html

相關問題