gtkmm:如何在 GtkBuilder XML 文件中使用自定义小部件?
gtkmm: How do I use a custom widget in a GtkBuilder XML file?
我正在编写一个 gtkmm-4 应用程序,并希望将自定义小部件与 GtkBuilder
XML 一起使用。如果小部件是从 C++ 构造的,则它可以正常工作。但是,当从 XML.
构建时它不会呈现
注意:这个问题可能被认为是 this one 的重复问题。我不是在询问有关将 Glade 与我的小部件一起使用的问题;我的小部件根本不适用于简单的手写 XML.
很抱歉发布了这么多代码,但我相信这已经是我所能得到的最少的代码了。
自定义小部件代码
- joystick.hpp
#ifndef _TASDI2_JOYSTICK_HPP_
#define _TASDI2_JOYSTICK_HPP_
#include <gtkmm.h>
namespace tasdi2 {
class Joystick : public Gtk::Widget {
public:
Joystick();
virtual ~Joystick() {}
virtual Gtk::SizeRequestMode get_request_mode_vfunc() const override {
return Gtk::Widget::get_request_mode_vfunc();
}
virtual void measure_vfunc(
Gtk::Orientation orientation, int for_size, int& minimum, int& natural,
int& minimum_baseline, int& natural_baseline) const override;
void on_map() override;
void on_unmap() override;
void snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot> &snapshot) override;
Glib::PropertyProxy<int> property_xpos() {
return prop_xpos.get_proxy();
}
Glib::PropertyProxy_ReadOnly<int> property_xpos() const {
return prop_xpos.get_proxy();
}
Glib::PropertyProxy<int> property_ypos() {
return prop_ypos.get_proxy();
}
Glib::PropertyProxy_ReadOnly<int> property_ypos() const {
return prop_ypos.get_proxy();
}
private:
Glib::Property<int> prop_xpos;
Glib::Property<int> prop_ypos;
};
} // namespace tasdi2
#endif
- joystick.cpp
#include "joystick.hpp"
#include <gdkmm.h>
#include <gtkmm.h>
#include <iostream>
#include <numbers>
namespace {
inline void circle(
const Glib::RefPtr<Cairo::Context>& cairo, double cx, double cy, double r) {
cairo->arc(cx, cy, r, 0, 2 * std::numbers::pi);
}
inline void line(
const Glib::RefPtr<Cairo::Context>& cairo, double x1, double y1, double x2,
double y2) {
cairo->move_to(x1, y1);
cairo->line_to(x2, y2);
}
} // namespace
namespace tasdi2 {
Joystick::Joystick() :
Glib::ObjectBase("Tasdi2Joystick"),
Gtk::Widget(),
prop_xpos(*this, "xpos", 0),
prop_ypos(*this, "ypos", 0) {
set_hexpand();
set_hexpand_set();
set_vexpand();
set_vexpand_set();
}
void Joystick::measure_vfunc(
Gtk::Orientation orientation, int for_size, int& minimum, int& natural,
int& minimum_baseline, int& natural_baseline) const {
minimum = 128;
natural = 160;
minimum_baseline = -1;
natural_baseline = -1;
return;
}
void Joystick::on_map() { Gtk::Widget::on_map(); }
void Joystick::on_unmap() { Gtk::Widget::on_unmap(); }
void Joystick::snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot>& snapshot) {
const auto space = get_allocation();
const Gdk::Rectangle rect(0, 0, space.get_width(), space.get_height());
std::cout << "Allocation area: " << space.get_x() << ", " << space.get_y() << ", " << space.get_width() << ", " << space.get_height() << "\n";
auto cairo = snapshot->append_cairo(rect);
const double w = space.get_width();
const double h = space.get_height();
const double cx = w / 2;
const double cy = h / 2;
// colors
const Gdk::RGBA color_bg0("#7F7F7F");
const Gdk::RGBA color_bg1("#FFFFFF");
const Gdk::RGBA color_oln("#000000");
const Gdk::RGBA color_cln("#0000FF");
const Gdk::RGBA color_dot("#FF0000");
cairo->rectangle(0, 0, w, h);
Gdk::Cairo::set_source_rgba(cairo, color_bg0);
cairo->fill();
circle(cairo, cx, cy, cx);
Gdk::Cairo::set_source_rgba(cairo, color_bg1);
cairo->fill_preserve();
line(cairo, cx, 0, cx, h);
line(cairo, 0, cy, w, cy);
Gdk::Cairo::set_source_rgba(cairo, color_oln);
cairo->stroke();
}
} // namespace tasdi2
MVE测试代码
- 在XML
#include <gtkmm.h>
#include "joystick.hpp"
static const std::string ui_data = R"(
<interface>
<object class="GtkAspectFrame" id="root">
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<child>
<object class="gtkmm__CustomObject_Tasdi2Joystick" />
</child>
</object>
</interface>
)";
class MainWindow : public Gtk::Window {
public:
MainWindow() :
builder(Gtk::Builder::create_from_string(ui_data)) {
set_child(*builder->get_widget<Gtk::AspectFrame>("root"));
set_size_request(256, 256);
}
protected:
Glib::RefPtr<Gtk::Builder> builder;
};
int main(int argc, char* argv[]) {
auto app = Gtk::Application::create("io.github.jgcodes2020.testapp");
app->signal_startup().connect([&]() {
tasdi2::Joystick joystick;
});
return app->make_window_and_run<MainWindow>(argc, argv);
}
- 在 C++ 中
#include <gtkmm.h>
#include "joystick.hpp"
static const std::string ui_data = R"(
<interface>
<object class="GtkAspectFrame" id="root">
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
</object>
</interface>
)";
class MainWindow : public Gtk::Window {
public:
MainWindow() :
builder(Gtk::Builder::create_from_string(ui_data)) {
auto& root = *builder->get_widget<Gtk::AspectFrame>("root");
root.set_child(stick);
set_child(root);
set_size_request(256, 256);
}
protected:
Glib::RefPtr<Gtk::Builder> builder;
tasdi2::Joystick stick;
};
int main(int argc, char* argv[]) {
auto app = Gtk::Application::create("io.github.jgcodes2020.testapp");
return app->make_window_and_run<MainWindow>(argc, argv);
}
使用上面关于包含派生小部件的评论,我查看了 Gnome 开发人员网站上的示例代码,并查看了“操纵杆”小部件的 class 构造函数。我单独留下了您的标准构造函数,因为它对于直接在您的代码中构建的任何小部件都很有用。然后,使用开发者网站上的派生小部件示例,我修改了“joystick.hpp”头文件以包含构建器构造函数原型,如下所示。
Joystick();
Joystick(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade); /* New builder function for a derived widget */
virtual ~Joystick() {}
然后在文件“joystick.cpp”的“tasdi2”命名空间中,我添加了派生的构建器构造函数,基本上是在“操纵杆”class 的标准构造函数中克隆了细节。
Joystick::Joystick(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_Tasdi2Joystick.
Glib::ObjectBase("Tasdi2Joystick"),
Gtk::Widget(cobject),
prop_xpos(*this, "xpos", 0),
prop_ypos(*this, "ypos", 0)
{
set_hexpand();
set_hexpand_set();
set_vexpand();
set_vexpand_set();
}
最后,在“main.cpp”的“XML”版本中,我修改了代码以适应派生的小部件。在您的 XML 文本正文中,我添加了一个“ID”属性,以便稍后在程序中调用派生的构建器函数可以挂钩该小部件。
<object class="gtkmm__CustomObject_Tasdi2Joystick" id="stick_range"/>
除了添加“ID”属性外,我还在“MainWindow”构造函数中构建了自定义操纵杆小部件。
MainWindow() :
builder(Gtk::Builder::create_from_string(ui_data)) {
set_child(*builder->get_widget<Gtk::AspectFrame>("root"));
set_size_request(256, 256);
tasdi2::Joystick * stick_range = nullptr;
stick_range = Gtk::Builder::get_widget_derived<tasdi2::Joystick>(builder, "stick_range");
if (stick_range == nullptr)
printf("Joystick widget definition was not found\n");
}
将您的“XML”版本的“main.cpp”与其他更改相结合的最终结果显示了小部件。
您可能已经从之前的评论中理解了这些东西,但如果没有,这些额外的代码片段可能对您有用。
此致。
我正在编写一个 gtkmm-4 应用程序,并希望将自定义小部件与 GtkBuilder
XML 一起使用。如果小部件是从 C++ 构造的,则它可以正常工作。但是,当从 XML.
注意:这个问题可能被认为是 this one 的重复问题。我不是在询问有关将 Glade 与我的小部件一起使用的问题;我的小部件根本不适用于简单的手写 XML.
很抱歉发布了这么多代码,但我相信这已经是我所能得到的最少的代码了。
自定义小部件代码
- joystick.hpp
#ifndef _TASDI2_JOYSTICK_HPP_
#define _TASDI2_JOYSTICK_HPP_
#include <gtkmm.h>
namespace tasdi2 {
class Joystick : public Gtk::Widget {
public:
Joystick();
virtual ~Joystick() {}
virtual Gtk::SizeRequestMode get_request_mode_vfunc() const override {
return Gtk::Widget::get_request_mode_vfunc();
}
virtual void measure_vfunc(
Gtk::Orientation orientation, int for_size, int& minimum, int& natural,
int& minimum_baseline, int& natural_baseline) const override;
void on_map() override;
void on_unmap() override;
void snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot> &snapshot) override;
Glib::PropertyProxy<int> property_xpos() {
return prop_xpos.get_proxy();
}
Glib::PropertyProxy_ReadOnly<int> property_xpos() const {
return prop_xpos.get_proxy();
}
Glib::PropertyProxy<int> property_ypos() {
return prop_ypos.get_proxy();
}
Glib::PropertyProxy_ReadOnly<int> property_ypos() const {
return prop_ypos.get_proxy();
}
private:
Glib::Property<int> prop_xpos;
Glib::Property<int> prop_ypos;
};
} // namespace tasdi2
#endif
- joystick.cpp
#include "joystick.hpp"
#include <gdkmm.h>
#include <gtkmm.h>
#include <iostream>
#include <numbers>
namespace {
inline void circle(
const Glib::RefPtr<Cairo::Context>& cairo, double cx, double cy, double r) {
cairo->arc(cx, cy, r, 0, 2 * std::numbers::pi);
}
inline void line(
const Glib::RefPtr<Cairo::Context>& cairo, double x1, double y1, double x2,
double y2) {
cairo->move_to(x1, y1);
cairo->line_to(x2, y2);
}
} // namespace
namespace tasdi2 {
Joystick::Joystick() :
Glib::ObjectBase("Tasdi2Joystick"),
Gtk::Widget(),
prop_xpos(*this, "xpos", 0),
prop_ypos(*this, "ypos", 0) {
set_hexpand();
set_hexpand_set();
set_vexpand();
set_vexpand_set();
}
void Joystick::measure_vfunc(
Gtk::Orientation orientation, int for_size, int& minimum, int& natural,
int& minimum_baseline, int& natural_baseline) const {
minimum = 128;
natural = 160;
minimum_baseline = -1;
natural_baseline = -1;
return;
}
void Joystick::on_map() { Gtk::Widget::on_map(); }
void Joystick::on_unmap() { Gtk::Widget::on_unmap(); }
void Joystick::snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot>& snapshot) {
const auto space = get_allocation();
const Gdk::Rectangle rect(0, 0, space.get_width(), space.get_height());
std::cout << "Allocation area: " << space.get_x() << ", " << space.get_y() << ", " << space.get_width() << ", " << space.get_height() << "\n";
auto cairo = snapshot->append_cairo(rect);
const double w = space.get_width();
const double h = space.get_height();
const double cx = w / 2;
const double cy = h / 2;
// colors
const Gdk::RGBA color_bg0("#7F7F7F");
const Gdk::RGBA color_bg1("#FFFFFF");
const Gdk::RGBA color_oln("#000000");
const Gdk::RGBA color_cln("#0000FF");
const Gdk::RGBA color_dot("#FF0000");
cairo->rectangle(0, 0, w, h);
Gdk::Cairo::set_source_rgba(cairo, color_bg0);
cairo->fill();
circle(cairo, cx, cy, cx);
Gdk::Cairo::set_source_rgba(cairo, color_bg1);
cairo->fill_preserve();
line(cairo, cx, 0, cx, h);
line(cairo, 0, cy, w, cy);
Gdk::Cairo::set_source_rgba(cairo, color_oln);
cairo->stroke();
}
} // namespace tasdi2
MVE测试代码
- 在XML
#include <gtkmm.h>
#include "joystick.hpp"
static const std::string ui_data = R"(
<interface>
<object class="GtkAspectFrame" id="root">
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<child>
<object class="gtkmm__CustomObject_Tasdi2Joystick" />
</child>
</object>
</interface>
)";
class MainWindow : public Gtk::Window {
public:
MainWindow() :
builder(Gtk::Builder::create_from_string(ui_data)) {
set_child(*builder->get_widget<Gtk::AspectFrame>("root"));
set_size_request(256, 256);
}
protected:
Glib::RefPtr<Gtk::Builder> builder;
};
int main(int argc, char* argv[]) {
auto app = Gtk::Application::create("io.github.jgcodes2020.testapp");
app->signal_startup().connect([&]() {
tasdi2::Joystick joystick;
});
return app->make_window_and_run<MainWindow>(argc, argv);
}
- 在 C++ 中
#include <gtkmm.h>
#include "joystick.hpp"
static const std::string ui_data = R"(
<interface>
<object class="GtkAspectFrame" id="root">
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
</object>
</interface>
)";
class MainWindow : public Gtk::Window {
public:
MainWindow() :
builder(Gtk::Builder::create_from_string(ui_data)) {
auto& root = *builder->get_widget<Gtk::AspectFrame>("root");
root.set_child(stick);
set_child(root);
set_size_request(256, 256);
}
protected:
Glib::RefPtr<Gtk::Builder> builder;
tasdi2::Joystick stick;
};
int main(int argc, char* argv[]) {
auto app = Gtk::Application::create("io.github.jgcodes2020.testapp");
return app->make_window_and_run<MainWindow>(argc, argv);
}
使用上面关于包含派生小部件的评论,我查看了 Gnome 开发人员网站上的示例代码,并查看了“操纵杆”小部件的 class 构造函数。我单独留下了您的标准构造函数,因为它对于直接在您的代码中构建的任何小部件都很有用。然后,使用开发者网站上的派生小部件示例,我修改了“joystick.hpp”头文件以包含构建器构造函数原型,如下所示。
Joystick();
Joystick(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade); /* New builder function for a derived widget */
virtual ~Joystick() {}
然后在文件“joystick.cpp”的“tasdi2”命名空间中,我添加了派生的构建器构造函数,基本上是在“操纵杆”class 的标准构造函数中克隆了细节。
Joystick::Joystick(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_Tasdi2Joystick.
Glib::ObjectBase("Tasdi2Joystick"),
Gtk::Widget(cobject),
prop_xpos(*this, "xpos", 0),
prop_ypos(*this, "ypos", 0)
{
set_hexpand();
set_hexpand_set();
set_vexpand();
set_vexpand_set();
}
最后,在“main.cpp”的“XML”版本中,我修改了代码以适应派生的小部件。在您的 XML 文本正文中,我添加了一个“ID”属性,以便稍后在程序中调用派生的构建器函数可以挂钩该小部件。
<object class="gtkmm__CustomObject_Tasdi2Joystick" id="stick_range"/>
除了添加“ID”属性外,我还在“MainWindow”构造函数中构建了自定义操纵杆小部件。
MainWindow() :
builder(Gtk::Builder::create_from_string(ui_data)) {
set_child(*builder->get_widget<Gtk::AspectFrame>("root"));
set_size_request(256, 256);
tasdi2::Joystick * stick_range = nullptr;
stick_range = Gtk::Builder::get_widget_derived<tasdi2::Joystick>(builder, "stick_range");
if (stick_range == nullptr)
printf("Joystick widget definition was not found\n");
}
将您的“XML”版本的“main.cpp”与其他更改相结合的最终结果显示了小部件。
您可能已经从之前的评论中理解了这些东西,但如果没有,这些额外的代码片段可能对您有用。
此致。