菜单项在 gtkmm3 中总是被禁用
Menu items always disabled in gtkmm3
我正在尝试使用 gtkmm3 和 Application::set_menubar 方法创建程序。我可以创建菜单,但是当我 运行 程序时,菜单中的所有项目都是灰色的。我搜索了 glibmm 和 glib 库,寻找启用(以及后来的 disable/hide)菜单项的方法,但我找不到任何函数或方法来执行此操作。我该如何解决这个问题?
我正在编译:
g++ -std=c++0x -o program program.cpp `pkg-config --cflags --libs gtkmm-3.0`
代码代码如下:
// vim:set ai et shiftwidth=4 softtabstop=4 :
#include <gtkmm.h>
#include <glibmm.h>
#include <giomm/menulinkiter.h>
class ApplicationMain{
private:
Glib::RefPtr<Gtk::Application> app;
Glib::RefPtr<Gio::Menu> mnuMenu;
/// Builds menu and puts menu actions into the application's action map
void createMenu();
/// Recursive method that iterates over the supplied MenuModel and
/// extracts actions into the application's action map
void extractMenuActions(const Glib::RefPtr<Gio::MenuModel> &model);
public:
ApplicationMain(int argc, char **argv);
~ApplicationMain();
int run();
};
using namespace std;
/// Container for menu xml data
struct MenuStringContainer{
static const char *string;
};
/// Builds menu and puts menu actions into the application's action map
void ApplicationMain::createMenu(){
// Build menu from xml data
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_string(MenuStringContainer::string);
mnuMenu = Glib::RefPtr<Gio::Menu>::cast_dynamic(builder->get_object("menubar"));
// Extract actions from menu model and add them to our action map
extractMenuActions(Glib::RefPtr<Gio::MenuModel>::cast_static(mnuMenu));
}
void ApplicationMain::extractMenuActions(const Glib::RefPtr<Gio::MenuModel> &model){
// Get the number of items in this menu model
gint count = g_menu_model_get_n_items(model->gobj());
// Iterate over the items in this model
for(gint i = 0; i < count; i++){
// Iterate over and recurse into the links in this menu model item
auto iter = model->iterate_item_links(i);
while(iter->next()){
extractMenuActions(iter->get_value());
}
try{
// Get the action for this item. Throws std::bad_cast if the cast can't be made
Glib::Variant<Glib::ustring> act = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(
model->get_item_attribute(i, Gio::MENU_ATTRIBUTE_ACTION, Glib::Variant<Glib::ustring>::variant_type())
);
// If this action is valid, get the action name and add it to the action map
if(act.gobj() != nullptr){
Glib::ustring actName = act.get();
app->add_action(Gio::SimpleAction::create(actName));
}
} catch(std::bad_cast &){
}
}
}
ApplicationMain::ApplicationMain(int argc, char **argv){
app = Gtk::Application::create(argc, argv, "com.angellistaliuu.jaguar.Chummer5");
chdir(Gio::File::create_for_commandline_arg(argv[0])->get_basename().c_str());
}
ApplicationMain::~ApplicationMain(){
}
int ApplicationMain::run(){
if(app->register_application()){
createMenu();
app->set_menubar(mnuMenu);
{
Gtk::ApplicationWindow *frm = new Gtk::ApplicationWindow();
// Allow pointer to be unreferenced when this scope ends
Glib::RefPtr<Gtk::ApplicationWindow> frmPtr(frm);
app->run(*frm);
}
} else {
app->activate();
}
}
int main(int argc, char **argv){
ApplicationMain appMain(argc, argv);
return appMain.run();
}
// XML data for application menu
const char *MenuStringContainer::string = R"rawstring(
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="menubar">
<submenu>
<attribute name="label">_File</attribute>
<section>
<item>
<attribute name="label">_New Character</attribute>
<attribute name="action">file.newchar</attribute>
</item>
<item>
<attribute name="label">New _Critter</attribute>
<attribute name="action">file.newcrit</attribute>
</item>
<item>
<attribute name="label">_Open</attribute>
<attribute name="action">file.open</attribute>
</item>
</section>
<section>
<item>
<attribute name="label">_Print</attribute>
<attribute name="action">file.print</attribute>
</item>
<item>
<attribute name="label">Print _Multiple</attribute>
<attribute name="action">file.printmultiple</attribute>
</item>
<item>
<attribute name="label">Print Setup</attribute>
<attribute name="action">file.printsetup</attribute>
</item>
</section>
<section id="mru">
<item>
<attribute name="label">[StickyMRU0]</attribute>
<attribute name="action">file.smru0</attribute>
</item>
<item>
<attribute name="label">[StickyMRU1]</attribute>
<attribute name="action">file.smru1</attribute>
</item>
<item>
<attribute name="label">[StickyMRU2]</attribute>
<attribute name="action">file.smru2</attribute>
</item>
<item>
<attribute name="label">[StickyMRU3]</attribute>
<attribute name="action">file.smru3</attribute>
</item>
<item>
<attribute name="label">[StickyMRU4]</attribute>
<attribute name="action">file.smru4</attribute>
</item>
<item>
<attribute name="label">[StickyMRU5]</attribute>
<attribute name="action">file.smru5</attribute>
</item>
<item>
<attribute name="label">[StickyMRU6]</attribute>
<attribute name="action">file.smru6</attribute>
</item>
<item>
<attribute name="label">[StickyMRU7]</attribute>
<attribute name="action">file.smru7</attribute>
</item>
<item>
<attribute name="label">[StickyMRU8]</attribute>
<attribute name="action">file.smru8</attribute>
</item>
<item>
<attribute name="label">[StickyMRU9]</attribute>
<attribute name="action">file.smru9</attribute>
</item>
<item>
<attribute name="label">[MRU0]</attribute>
<attribute name="action">file.mru0</attribute>
</item>
<item>
<attribute name="label">[MRU1]</attribute>
<attribute name="action">file.mru1</attribute>
</item>
<item>
<attribute name="label">[MRU2]</attribute>
<attribute name="action">file.mru2</attribute>
</item>
<item>
<attribute name="label">[MRU3]</attribute>
<attribute name="action">file.mru3</attribute>
</item>
<item>
<attribute name="label">[MRU4]</attribute>
<attribute name="action">file.mru4</attribute>
</item>
<item>
<attribute name="label">[MRU5]</attribute>
<attribute name="action">file.mru5</attribute>
</item>
<item>
<attribute name="label">[MRU6]</attribute>
<attribute name="action">file.mru6</attribute>
</item>
<item>
<attribute name="label">[MRU7]</attribute>
<attribute name="action">file.mru7</attribute>
</item>
<item>
<attribute name="label">[MRU8]</attribute>
<attribute name="action">file.mru8</attribute>
</item>
<item>
<attribute name="label">[MRU9]</attribute>
<attribute name="action">file.mru9</attribute>
</item>
</section>
<section>
<item>
<attribute name="label">E_xit</attribute>
<attribute name="action">file.exit</attribute>
</item>
</section>
</submenu>
<submenu>
<attribute name="label">_Tools</attribute>
<section>
<item>
<attribute name="label">_Dice Roller</attribute>
<attribute name="action">tools.roller</attribute>
</item>
</section>
<section>
<item>
<attribute name="label">_Options</attribute>
<attribute name="action">tools.options</attribute>
</item>
<item>
<attribute name="label">Check for Updates</attribute>
<attribute name="action">tools.updates</attribute>
</item>
<item>
<attribute name="label">Omae</attribute>
<attribute name="action">tools.omae</attribute>
</item>
</section>
</submenu>
<submenu>
<attribute name="label">_Windows</attribute>
<section>
<item>
<attribute name="label">_New Window</attribute>
<attribute name="action">windows.new</attribute>
</item>
<item>
<attribute name="label">C_lose All</attribute>
<attribute name="action">windows.closeall</attribute>
</item>
</section>
<section id="windowlist">
</section>
</submenu>
<submenu>
<attribute name="label">_Help</attribute>
<section>
<section>
<item>
<attribute name="label">Chummer Wiki</attribute>
<attribute name="action">help.chummerwiki</attribute>
</item>
</section>
<item>
<attribute name="label">_Revision History</attribute>
<attribute name="action">help.revisionhistory</attribute>
</item>
<item>
<attribute name="label">_Dumpshock Thread</attribute>
<attribute name="action">help.dumpshockthread</attribute>
</item>
<item>
<attribute name="label">_About...</attribute>
<attribute name="action">help.about</attribute>
</item>
</section>
</submenu>
</menu>
</interface>
)rawstring"; //"(
您正在手动调用 register_application(),而不是让 GtkApplication/GApplication 为您调用。您可能这样做是为了避免出现此警告,我认为这是主要线索,因为 gtkmm 示例永远不需要这样做:
(a.out:24959): Gtk-CRITICAL **: gtk_application_set_menubar: assertion 'g_application_get_is_registered (G_APPLICATION (application))' failed
我已将您的测试用例缩减为更简单的测试用例,没有 class 定义,也没有使用 GtkBuilder,现在我猜问题可能是您要添加这些操作或菜单,太早了,虽然我不确定到底是什么还没有正确设置。如果你愿意,我可以 post 这里的测试用例。
例如,此示例有效,但请注意它在派生的 Gtk::Application 的 on_startup() 中添加了菜单及其操作:
https://git.gnome.org/browse/gtkmm-documentation/tree/examples/book/application/app_and_win_menus/exampleapplication.cc#n32
派生一个 Gtk::Application 无论如何都是更正确的结构,允许应用程序在正确的时间做正确的事情。在我看来,GtkApplication 是一个相当尴尬和无情的 API,所以我不会偏离这条路。
顺便说一句,定义一个不是从 Gtk::Application 派生的 ApplicationMain class 也很令人困惑。并且您应该避免将 RefPtr<> 与小部件一起使用 (Gtk::ApplicationWindow) class。您永远不需要在应用程序代码中引用或取消引用小部件。
我正在尝试使用 gtkmm3 和 Application::set_menubar 方法创建程序。我可以创建菜单,但是当我 运行 程序时,菜单中的所有项目都是灰色的。我搜索了 glibmm 和 glib 库,寻找启用(以及后来的 disable/hide)菜单项的方法,但我找不到任何函数或方法来执行此操作。我该如何解决这个问题?
我正在编译:
g++ -std=c++0x -o program program.cpp `pkg-config --cflags --libs gtkmm-3.0`
代码代码如下:
// vim:set ai et shiftwidth=4 softtabstop=4 :
#include <gtkmm.h>
#include <glibmm.h>
#include <giomm/menulinkiter.h>
class ApplicationMain{
private:
Glib::RefPtr<Gtk::Application> app;
Glib::RefPtr<Gio::Menu> mnuMenu;
/// Builds menu and puts menu actions into the application's action map
void createMenu();
/// Recursive method that iterates over the supplied MenuModel and
/// extracts actions into the application's action map
void extractMenuActions(const Glib::RefPtr<Gio::MenuModel> &model);
public:
ApplicationMain(int argc, char **argv);
~ApplicationMain();
int run();
};
using namespace std;
/// Container for menu xml data
struct MenuStringContainer{
static const char *string;
};
/// Builds menu and puts menu actions into the application's action map
void ApplicationMain::createMenu(){
// Build menu from xml data
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_string(MenuStringContainer::string);
mnuMenu = Glib::RefPtr<Gio::Menu>::cast_dynamic(builder->get_object("menubar"));
// Extract actions from menu model and add them to our action map
extractMenuActions(Glib::RefPtr<Gio::MenuModel>::cast_static(mnuMenu));
}
void ApplicationMain::extractMenuActions(const Glib::RefPtr<Gio::MenuModel> &model){
// Get the number of items in this menu model
gint count = g_menu_model_get_n_items(model->gobj());
// Iterate over the items in this model
for(gint i = 0; i < count; i++){
// Iterate over and recurse into the links in this menu model item
auto iter = model->iterate_item_links(i);
while(iter->next()){
extractMenuActions(iter->get_value());
}
try{
// Get the action for this item. Throws std::bad_cast if the cast can't be made
Glib::Variant<Glib::ustring> act = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(
model->get_item_attribute(i, Gio::MENU_ATTRIBUTE_ACTION, Glib::Variant<Glib::ustring>::variant_type())
);
// If this action is valid, get the action name and add it to the action map
if(act.gobj() != nullptr){
Glib::ustring actName = act.get();
app->add_action(Gio::SimpleAction::create(actName));
}
} catch(std::bad_cast &){
}
}
}
ApplicationMain::ApplicationMain(int argc, char **argv){
app = Gtk::Application::create(argc, argv, "com.angellistaliuu.jaguar.Chummer5");
chdir(Gio::File::create_for_commandline_arg(argv[0])->get_basename().c_str());
}
ApplicationMain::~ApplicationMain(){
}
int ApplicationMain::run(){
if(app->register_application()){
createMenu();
app->set_menubar(mnuMenu);
{
Gtk::ApplicationWindow *frm = new Gtk::ApplicationWindow();
// Allow pointer to be unreferenced when this scope ends
Glib::RefPtr<Gtk::ApplicationWindow> frmPtr(frm);
app->run(*frm);
}
} else {
app->activate();
}
}
int main(int argc, char **argv){
ApplicationMain appMain(argc, argv);
return appMain.run();
}
// XML data for application menu
const char *MenuStringContainer::string = R"rawstring(
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="menubar">
<submenu>
<attribute name="label">_File</attribute>
<section>
<item>
<attribute name="label">_New Character</attribute>
<attribute name="action">file.newchar</attribute>
</item>
<item>
<attribute name="label">New _Critter</attribute>
<attribute name="action">file.newcrit</attribute>
</item>
<item>
<attribute name="label">_Open</attribute>
<attribute name="action">file.open</attribute>
</item>
</section>
<section>
<item>
<attribute name="label">_Print</attribute>
<attribute name="action">file.print</attribute>
</item>
<item>
<attribute name="label">Print _Multiple</attribute>
<attribute name="action">file.printmultiple</attribute>
</item>
<item>
<attribute name="label">Print Setup</attribute>
<attribute name="action">file.printsetup</attribute>
</item>
</section>
<section id="mru">
<item>
<attribute name="label">[StickyMRU0]</attribute>
<attribute name="action">file.smru0</attribute>
</item>
<item>
<attribute name="label">[StickyMRU1]</attribute>
<attribute name="action">file.smru1</attribute>
</item>
<item>
<attribute name="label">[StickyMRU2]</attribute>
<attribute name="action">file.smru2</attribute>
</item>
<item>
<attribute name="label">[StickyMRU3]</attribute>
<attribute name="action">file.smru3</attribute>
</item>
<item>
<attribute name="label">[StickyMRU4]</attribute>
<attribute name="action">file.smru4</attribute>
</item>
<item>
<attribute name="label">[StickyMRU5]</attribute>
<attribute name="action">file.smru5</attribute>
</item>
<item>
<attribute name="label">[StickyMRU6]</attribute>
<attribute name="action">file.smru6</attribute>
</item>
<item>
<attribute name="label">[StickyMRU7]</attribute>
<attribute name="action">file.smru7</attribute>
</item>
<item>
<attribute name="label">[StickyMRU8]</attribute>
<attribute name="action">file.smru8</attribute>
</item>
<item>
<attribute name="label">[StickyMRU9]</attribute>
<attribute name="action">file.smru9</attribute>
</item>
<item>
<attribute name="label">[MRU0]</attribute>
<attribute name="action">file.mru0</attribute>
</item>
<item>
<attribute name="label">[MRU1]</attribute>
<attribute name="action">file.mru1</attribute>
</item>
<item>
<attribute name="label">[MRU2]</attribute>
<attribute name="action">file.mru2</attribute>
</item>
<item>
<attribute name="label">[MRU3]</attribute>
<attribute name="action">file.mru3</attribute>
</item>
<item>
<attribute name="label">[MRU4]</attribute>
<attribute name="action">file.mru4</attribute>
</item>
<item>
<attribute name="label">[MRU5]</attribute>
<attribute name="action">file.mru5</attribute>
</item>
<item>
<attribute name="label">[MRU6]</attribute>
<attribute name="action">file.mru6</attribute>
</item>
<item>
<attribute name="label">[MRU7]</attribute>
<attribute name="action">file.mru7</attribute>
</item>
<item>
<attribute name="label">[MRU8]</attribute>
<attribute name="action">file.mru8</attribute>
</item>
<item>
<attribute name="label">[MRU9]</attribute>
<attribute name="action">file.mru9</attribute>
</item>
</section>
<section>
<item>
<attribute name="label">E_xit</attribute>
<attribute name="action">file.exit</attribute>
</item>
</section>
</submenu>
<submenu>
<attribute name="label">_Tools</attribute>
<section>
<item>
<attribute name="label">_Dice Roller</attribute>
<attribute name="action">tools.roller</attribute>
</item>
</section>
<section>
<item>
<attribute name="label">_Options</attribute>
<attribute name="action">tools.options</attribute>
</item>
<item>
<attribute name="label">Check for Updates</attribute>
<attribute name="action">tools.updates</attribute>
</item>
<item>
<attribute name="label">Omae</attribute>
<attribute name="action">tools.omae</attribute>
</item>
</section>
</submenu>
<submenu>
<attribute name="label">_Windows</attribute>
<section>
<item>
<attribute name="label">_New Window</attribute>
<attribute name="action">windows.new</attribute>
</item>
<item>
<attribute name="label">C_lose All</attribute>
<attribute name="action">windows.closeall</attribute>
</item>
</section>
<section id="windowlist">
</section>
</submenu>
<submenu>
<attribute name="label">_Help</attribute>
<section>
<section>
<item>
<attribute name="label">Chummer Wiki</attribute>
<attribute name="action">help.chummerwiki</attribute>
</item>
</section>
<item>
<attribute name="label">_Revision History</attribute>
<attribute name="action">help.revisionhistory</attribute>
</item>
<item>
<attribute name="label">_Dumpshock Thread</attribute>
<attribute name="action">help.dumpshockthread</attribute>
</item>
<item>
<attribute name="label">_About...</attribute>
<attribute name="action">help.about</attribute>
</item>
</section>
</submenu>
</menu>
</interface>
)rawstring"; //"(
您正在手动调用 register_application(),而不是让 GtkApplication/GApplication 为您调用。您可能这样做是为了避免出现此警告,我认为这是主要线索,因为 gtkmm 示例永远不需要这样做:
(a.out:24959): Gtk-CRITICAL **: gtk_application_set_menubar: assertion 'g_application_get_is_registered (G_APPLICATION (application))' failed
我已将您的测试用例缩减为更简单的测试用例,没有 class 定义,也没有使用 GtkBuilder,现在我猜问题可能是您要添加这些操作或菜单,太早了,虽然我不确定到底是什么还没有正确设置。如果你愿意,我可以 post 这里的测试用例。
例如,此示例有效,但请注意它在派生的 Gtk::Application 的 on_startup() 中添加了菜单及其操作: https://git.gnome.org/browse/gtkmm-documentation/tree/examples/book/application/app_and_win_menus/exampleapplication.cc#n32
派生一个 Gtk::Application 无论如何都是更正确的结构,允许应用程序在正确的时间做正确的事情。在我看来,GtkApplication 是一个相当尴尬和无情的 API,所以我不会偏离这条路。
顺便说一句,定义一个不是从 Gtk::Application 派生的 ApplicationMain class 也很令人困惑。并且您应该避免将 RefPtr<> 与小部件一起使用 (Gtk::ApplicationWindow) class。您永远不需要在应用程序代码中引用或取消引用小部件。