在不使用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 :(得分: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 ::菜单类参考页面的更多信息答案 1 :(得分: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";
}
}