Производный виджет Gtk::DrawingArea загружается из файла компоновщика Glade неправильно

Я работаю над встроенным проектом 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, но не назначает его правильно.

Не могли бы вы привести минимальный и воспроизводимый пример?

BobMorane 24.08.2024 14:16

Я обновил вопрос соответственно. В целевой системе установлен Gtk+ 3.24.

KBO 26.08.2024 09:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Эта строка является проблемой:

auto objects = refBuilder->get_objects();

Я получаю все объекты, построенные застройщиком. В вашем случае он получает производный виджет как Gtk::DrawingArea (именно поэтому вызывается конструктор для этого типа). Когда вы пытаетесь снова получить тот же виджет другого типа, это не удается.

Примечание. Если бы вы создали минимальный пример, вы бы наверняка легко это нашли.

Большое спасибо за предложение, теперь оно работает. Этот фрагмент кода был вставлен при поиске проблемы, чтобы увидеть типы созданных объектов. Я бы не ожидал такого поведения. Я ожидал, что зарегистрированный производный тип C++ будет возвращен, особенно после явной регистрации GType, без влияния на последующие вызовы.

KBO 28.08.2024 09:08

Не за что. Если это работает для вас, не могли бы вы принять ответ?

BobMorane 28.08.2024 12:46

Конечно, принято, еще раз спасибо.

KBO 28.08.2024 14:53

Другие вопросы по теме

Похожие вопросы

Ошибка нарушения прав доступа при многопоточном сканировании диска с помощью C++
Является ли замена всех const std::string & на std::string_view хорошим выбором?
C++ Неопределенное поведение при использовании переинтерпретации приведения
Как получить понятное имя, принадлежащее дескриптору HMONITOR в Windows?
Цикл while в C++ завершается сбоем, если целое число содержит более 10 цифр
Какова точная цитата из стандарта C++, в которой говорится, что ошибка вывода типа при выведении аргумента шаблона не является ошибкой (SFINAE)?
Метод удаления двоичного дерева поиска удаляет все узлы, а не только выбранный C++
Ошибка Boost::program_options «опция не может быть указана более одного раза» при реализации программы с несколькими режимами
Как обнаружить клавишу Enter при вставке многострочного текста (содержащего `\r`)?
Переключатели С++ Возврат внутри/вне скобок? перерыв?