使用派生部件
你可以使用Gtk::Builder和Glade布局你从gtkmm部件类派生的自定义部件。这使得你的代码保持有序和封装性,将声明式表述与业务逻辑分离开来,避免你的大多数源代码都是在设置属性和装填容器。
像这样用Gtk::Builder::get_widget_derived():
auto pDialog = Gtk::Builder::get_widget_derived<DerivedDialog>(builder, "DialogDerived");
你的派生类必须有一个将指向底层C类型的指针和Gtk::Builder实例作为参数的构造函数。所有与gtkmm相关的类的都将其的底层C类型别名(typedef)为BaseObjectType(例如:Gtk::Dialog将GtkDialog别名为BaseObjectType)。
你必须在初始化列表中调用基类的构造函数,并提供C指针。例如:
DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder) : Gtk::Dialog(cobject) { }
然后你可以将对子部件的操作封装于派生类的构造函数中,可以在构造函数中再次使用get_widget()或get_widget_derived()。例如:
DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder) : Gtk::Dialog(cobject), m_builder(builder), //Get the Glade-instantiated Button, and connect a signal handler: m_pButton(m_builder->get_widget<Gtk::Button>("quit_button")) { if(m_pButton) { m_pButton->signal_clicked().connect( sigc::mem_fun(*this, &DerivedDialog::on_button_quit) ); } }
可以传递额外的参数给get_widget_derived(),他们将被转发给派生部件的构造函数。例如这样调用get_widget_derived():
auto pDialog = Gtk::Builder::get_widget_derived<DerivedDialog>(builder, "DialogDerived", true);
DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder, bool warning) : Gtk::Dialog(cobject), m_builder(builder), m_pButton(m_builder->get_widget<Gtk::Button>("quit_button")) { // .... }
- 24.3.1. Gtk::Builder和Glib::Property
- 24.3.2. 示例
24.3.1. Gtk::Builder和Glib::Property
如果你派生的部件使用了Glib::Property,它将变得更复杂了一些。包涵Glib::Property成员的派生部件必须在GType系统中用自己的名称进行注册。必须在调用任何create_from_*()或add_from_*()方法之前进行注册,这意味着你可能需要完成对该类的注册才能创建派生部件的实例。你派生的部件必须有一个具有get_widget_derived()所需的所有参数以及调用Glib::ObjectBase构造函数以注册GType的构造函数。
DerivedButton::DerivedButton(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder) : Glib::ObjectBase("MyButton"), // The GType name will be gtkmm__CustomObject_MyButton. Gtk::Button(cobject), prop_ustring(*this, "button-ustring"), prop_int(*this, "button-int", 10) { // .... }
如果将gtkmm与glibmm2.62以上的版本一起使用,则还可以在.glade文件中指定使用gtkmm在C++代码中声明的派生部件的属性,并使用Gtk::Builder加载/设置这些属性。更多实现此功能的详细信息请参阅Gtk::Builder的文档。Glade不会按原样识别这些属性,但这应该能通过使用属性类定义和声明一个新属性目录来实现。
24.3.2. 示例
本示例展示了如何在运行时加载Glade文件以及如何通过派生类访问部件。
File: derivedbutton.h (For use with gtkmm 4)
#ifndef GTKMM_EXAMPLE_DERIVED_BUTTON_H #define GTKMM_EXAMPLE_DERIVED_BUTTON_H #include <gtkmm.h> class DerivedButton : public Gtk::Button { public: DerivedButton(); DerivedButton(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade); virtual ~DerivedButton(); // Provide proxies for the properties. The proxy allows you to connect to // the 'changed' signal, etc. Glib::PropertyProxy<Glib::ustring> property_ustring() { return prop_ustring.get_proxy(); } Glib::PropertyProxy<int> property_int() { return prop_int.get_proxy(); } private: Glib::Property<Glib::ustring> prop_ustring; Glib::Property<int> prop_int; }; #endif //GTKMM_EXAMPLE_DERIVED_BUTTON_H
File: deriveddialog.h (For use with gtkmm 4)
#ifndef GTKMM_EXAMPLE_DERIVED_DIALOG_H #define GTKMM_EXAMPLE_DERIVED_DIALOG_H #include <gtkmm.h> #include "derivedbutton.h" class DerivedDialog : public Gtk::Dialog { public: DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade); DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade, bool is_glad); virtual ~DerivedDialog(); protected: //Signal handlers: void on_button_quit(); Glib::RefPtr<Gtk::Builder> m_refGlade; DerivedButton* m_pButton; }; #endif //GTKMM_EXAMPLE_DERIVED_DIALOG_H
File: main.cc (For use with gtkmm 4)
#include "deriveddialog.h" #include <iostream> #include <cstring> namespace { bool show_icon = false; bool is_glad = true; DerivedDialog* pDialog = nullptr; Glib::RefPtr<Gtk::Application> app; void on_app_activate() { // Create a dummy instance before the call to refBuilder->add_from_file(). // This creation registers DerivedButton's class in the GType system. // This is necessary because DerivedButton contains user-defined properties // (Glib::Property) and is created by Gtk::Builder. static_cast<void>(DerivedButton()); // Load the GtkBuilder file and instantiate its widgets: auto refBuilder = Gtk::Builder::create(); try { refBuilder->add_from_file("derived.glade"); } catch(const Glib::FileError& ex) { std::cerr << "FileError: " << ex.what() << std::endl; return; } catch(const Glib::MarkupError& ex) { std::cerr << "MarkupError: " << ex.what() << std::endl; return; } catch(const Gtk::BuilderError& ex) { std::cerr << "BuilderError: " << ex.what() << std::endl; return; } // Get the GtkBuilder-instantiated dialog: if (show_icon) pDialog = Gtk::Builder::get_widget_derived<DerivedDialog>(refBuilder, "DialogDerived", is_glad); else pDialog = Gtk::Builder::get_widget_derived<DerivedDialog>(refBuilder, "DialogDerived"); if (!pDialog) { std::cerr << "Could not get the dialog" << std::endl; return; } // It's not possible to delete widgets after app->run() has returned. // Delete the dialog with its child widgets before app->run() returns. pDialog->signal_hide().connect([] () { delete pDialog; }); app->add_window(*pDialog); pDialog->show(); } } // anonymous namespace int main(int argc, char** argv) { int argc1 = argc; if (argc > 1) { if (std::strcmp(argv[1], "--glad") == 0) { show_icon = true; is_glad = true; argc1 = 1; // Don't give the command line arguments to Gtk::Application. } else if (std::strcmp(argv[1], "--sad") == 0) { show_icon = true; is_glad = false; argc1 = 1; // Don't give the command line arguments to Gtk::Application. } } app = Gtk::Application::create("org.gtkmm.example"); // Instantiate a dialog when the application has been activated. // This can only be done after the application has been registered. // It's possible to call app->register_application() explicitly, but // usually it's easier to let app->run() do it for you. app->signal_activate().connect([] () { on_app_activate(); }); return app->run(argc1, argv); }
File: derivedbutton.cc (For use with gtkmm 4)
#include "derivedbutton.h" #include <iostream> namespace { void on_ustring_changed() { std::cout << "- ustring property changed!" << std::endl; } void on_int_changed() { std::cout << "- int property changed!" << std::endl; } } // anonymous namespace // For creating a dummy object in main.cc. DerivedButton::DerivedButton() : Glib::ObjectBase("MyButton"), prop_ustring(*this, "button-ustring"), prop_int(*this, "button-int", 10) { } DerivedButton::DerivedButton(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& /* refGlade */) : // To register custom properties, you must register a custom GType. If // you don't know what that means, don't worry, just remember to add // this Glib::ObjectBase constructor call to your class' constructor. // The GType name will be gtkmm__CustomObject_MyButton. Glib::ObjectBase("MyButton"), Gtk::Button(cobject), // register the properties with the object and give them names prop_ustring(*this, "button-ustring"), // this one has a default value prop_int(*this, "button-int", 10) { // Register some handlers that will be called when the values of the // specified parameters are changed. property_ustring().signal_changed().connect(sigc::ptr_fun(&on_ustring_changed)); property_int().signal_changed().connect(sigc::ptr_fun(&on_int_changed)); } DerivedButton::~DerivedButton() { }
File: deriveddialog.cc (For use with gtkmm 4)
#include "deriveddialog.h" #include <iostream> DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade) : Gtk::Dialog(cobject), m_refGlade(refGlade), m_pButton(nullptr) { // Get the Glade-instantiated Button, and connect a signal handler: m_pButton = Gtk::Builder::get_widget_derived<DerivedButton>(m_refGlade, "quit_button"); if (m_pButton) { m_pButton->signal_clicked().connect( sigc::mem_fun(*this, &DerivedDialog::on_button_quit) ); std::cout << "ustring, int: " << m_pButton->property_ustring() << ", " << m_pButton->property_int() << std::endl; m_pButton->property_int() = 99; std::cout << "ustring, int: " << m_pButton->property_ustring() << ", " << m_pButton->property_int() << std::endl; } } // The first two parameters are mandatory in a constructor that will be called // from Gtk::Builder::get_widget_derived(). // Additional parameters, if any, correspond to additional arguments in the call // to Gtk::Builder::get_widget_derived(). DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade, bool is_glad) : DerivedDialog(cobject, refGlade) // Delegate to the other constructor { // Show an icon. auto pImage = Gtk::make_managed<Gtk::Image>(); pImage->set_from_icon_name(is_glad ? "face-smile" : "face-sad"); pImage->set_icon_size(Gtk::IconSize::LARGE); pImage->set_expand(); get_content_area()->append(*pImage); } DerivedDialog::~DerivedDialog() { } void DerivedDialog::on_button_quit() { hide(); //hide() will cause Gtk::Application::run() to end. }