Я работаю над встроенным проектом Linux, используя gtk/gtkmm 3. Я использую файлы компоновщика Glade для определения пользовательского интерфейса и загружаю их во время выполнения программы. Это отлично работает. Теперь мне нужен собственный виджет циклического прогресса, поэтому я наследую класс от Gtk::DrawingArea, как предложено в документации gtkmm.
// MyCircularProgress.hpp
class MyCircularProgress : public Gtk::DrawingArea
{
public:
MyCircularProgress();
MyCircularProgress(GtkDrawingArea* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder);
virtual ~MyCircularProgress();
protected:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
};
// MyCircularProgress.cpp
#include "CircularProgress.hpp"
MyCircularProgress::MyCircularProgress() :
// 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_MyCircularProgress.
Glib::ObjectBase("MyCircularProgress")
{
}
MyCircularProgress::MyCircularProgress(GtkDrawingArea* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder) :
// 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_MyCircularProgress.
Glib::ObjectBase("MyCircularProgress"),
Gtk::DrawingArea(cobject)
{
}
MyCircularProgress::~MyCircularProgress()
{
}
bool MyCircularProgress::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
Gtk::Allocation allocation = get_allocation();
const int width = allocation.get_width();
const int height = allocation.get_height();
cr->set_source_rgb(0.8, 0.0, 0.0);
cr->rectangle(0, 0, width, height);
cr->fill();
return true;
}
Соответствующий файл Gladederder.glade:
<?xml version = "1.0" encoding = "UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib = "gtk+" version = "3.24"/>
<object class = "GtkApplicationWindow" id = "wnd_app">
<property name = "name">wnd_app</property>
<property name = "width_request">480</property>
<property name = "height_request">320</property>
<property name = "can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class = "GtkBox">
<property name = "visible">True</property>
<property name = "can_focus">False</property>
<property name = "baseline_position">top</property>
<child>
<placeholder/>
</child>
<child type = "center">
<object class = "gtkmm__CustomObject_MyCircularProgress" id = "draw_progress">
<property name = "width_request">200</property>
<property name = "height_request">200</property>
<property name = "visible">True</property>
<property name = "can_focus">False</property>
<property name = "valign">start</property>
</object>
<packing>
<property name = "expand">False</property>
<property name = "fill">True</property>
<property name = "position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
В файле main.cpp я зарегистрировал новую оболочку C++ перед использованием компоновщика.
#include <vector>
#include "CircularProgress.hpp"
#include <iostream>
#include <cstring>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
#include <memory>
#include <string>
#include <cstdlib>
template <class T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
// Not really used anywhere, but force an instance to be created.
MyCircularProgress* my_global_accessible_progress = nullptr;
int main (int argc, char **argv)
{
auto app = Gtk::Application::create(argc, argv, "my.circularprogress");
// Create a dummy instance before the call to refBuilder->add_from_file().
// This creation registers DerivedButton's class in the GType system.
my_global_accessible_progress = new MyCircularProgress();
//Load the Glade 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 1;
}
catch(const Glib::MarkupError& ex)
{
std::cerr << "MarkupError: " << ex.what() << std::endl;
return 1;
}
catch(const Gtk::BuilderError& ex)
{
std::cerr << "BuilderError: " << ex.what() << std::endl;
return 1;
}
Gtk::ApplicationWindow* pWnd = nullptr;
refBuilder->get_widget("wnd_app", pWnd);
if (pWnd)
{
auto objects = refBuilder->get_objects();
for(auto item : objects)
{
std::cout << typeid(item).name() << std::endl;
std::cout << type_name<decltype(item)>() << std::endl;
std::cout << "----------------------------------------" << std::endl;
}
MyCircularProgress* pProgress;
refBuilder->get_widget_derived("draw_progress", pProgress);
if (pProgress)
{
std::cout << "MyCircularProgress name: " << pProgress->get_name() << std::endl;
std::cout << type_name<decltype(pProgress)>() << std::endl;
}
Gtk::DrawingArea* pDraw;
refBuilder->get_widget("draw_progress", pDraw);
if (pDraw)
{
std::cout << "Gtk::DrawingArea name: " << pDraw->get_name() << std::endl;
std::cout << type_name<decltype(pDraw)>() << std::endl;
}
app->run(*pWnd);
}
delete pWnd;
delete my_global_accessible_progress;
return 0;
}
Когда я запускаю программу и загружаю пользовательский интерфейс с помощью компоновщика, экземпляр производного класса MyCircularProgress
не создается. На консоль не выводится ошибка, но ожидаемый конструктор MyCircularProgress(GtkDrawingArea* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder)
не вызывается.
Когда я пытаюсь получить экземпляр MyCircularProgress
от строителя, используя:
MyCircularProgress* pProgress;
refBuilder->get_widget_derived("draw_progress", pProgress);
Я получаю следующее сообщение об ошибке на консоли:
Gtk::Builder::get_widget_derived(): динамический_cast<> не выполнен. Кажется, существует существующий экземпляр C++ другого типа.
Когда я пытаюсь получить экземпляр базового класса от строителя, используя:
Gtk::DrawingArea* pDraw;
refBuilder->get_widget("draw_progress", pDraw);
Тогда я получаю действительный указатель.
Есть у кого-нибудь идеи, что не так? Кажется, что компоновщик Gtk создает правильный базовый класс Gtk::DrawingArea
производного класса C++ MyCircularProgress
, но не назначает его правильно.
Я обновил вопрос соответственно. В целевой системе установлен Gtk+ 3.24.
Эта строка является проблемой:
auto objects = refBuilder->get_objects();
Я получаю все объекты, построенные застройщиком. В вашем случае он получает производный виджет как Gtk::DrawingArea
(именно поэтому вызывается конструктор для этого типа). Когда вы пытаетесь снова получить тот же виджет другого типа, это не удается.
Примечание. Если бы вы создали минимальный пример, вы бы наверняка легко это нашли.
Большое спасибо за предложение, теперь оно работает. Этот фрагмент кода был вставлен при поиске проблемы, чтобы увидеть типы созданных объектов. Я бы не ожидал такого поведения. Я ожидал, что зарегистрированный производный тип C++ будет возвращен, особенно после явной регистрации GType, без влияния на последующие вызовы.
Не за что. Если это работает для вас, не могли бы вы принять ответ?
Конечно, принято, еще раз спасибо.
Не могли бы вы привести минимальный и воспроизводимый пример?