Я использую Gtkmm3 (Ubuntu) для создания небольшого GUI приложения. В этом приложении у меня есть пара windows для создания, которые в основном все следуют одному и тому же шаблону:
- регистрация основного макета (
Gtk::Grid
); - настройка свойств окна ( значок, заголовок и т. д. c);
- настройка свойств макета (расширение, вложенные макеты и т. д. c);
- настройка виджетов окна (добавление их к макетам, меткам и т. д.) c).
Чтобы избежать необходимости переписывать все эти логи c каждый раз, когда я создаю новое окно, я написал следующий базовый класс:
template<typename GtkmmWindow>
class Window
{
public:
Window();
virtual int Show() = 0;
protected:
virtual void ConfigureWindow() = 0;
virtual void ConfigureLayouts() = 0;
virtual void ConfigureWidgets() = 0;
void Init();
Gtk::Grid m_mainLayout;
GtkmmWindow m_window;
};
template<typename GtkmmWindow>
Window<GtkmmWindow>::Window()
{
m_window.add(m_mainLayout);
// When signal 'realize' is sent, 'Init' will be triggered.
// This happens after construction, so virtual methods can
// be used safely:
m_window.signal_realize().connect([this](){Init();});
}
// Initialize child window according to its own needs:
template<typename GtkmmWindow>
void Window<GtkmmWindow>::Init()
{
ConfigureWindow();
ConfigureLayouts();
ConfigureWidgets();
// If this line is removed, no widgets are shown.
m_window.show_all_children();
}
Цель этого класса - убедиться, что пункты с 1 по 4 выполнены всеми windows одинаково. Это происходит путем вызова соответствующих виртуальных методов (которые должны быть переопределены в конкретных дочерних классах) при отправке сигнала realize
. Это связано с тем, что при отправке сигнала realize
я знаю, что были вызваны конструкторы окон и что я могу безопасно использовать виртуальные методы.
Например, вот как я использую его для создания главного окна приложения. :
class MyWindow : public Window<Gtk::ApplicationWindow>
{
public:
MyWindow(Gtk::Application& p_app) : m_app{p_app} {}
int Show() override
{
m_window.show_all();
return m_app.run(m_window);
}
private:
Gtk::Application& m_app;
Gtk::Button m_button;
void ConfigureWindow() override
{
m_window.set_title("SO Question");
// If I set this to false, the window shrinks to fit the button size:
m_window.set_resizable(false);
}
void ConfigureLayouts() override
{
m_mainLayout.override_background_color(Gdk::RGBA("yellow"));
}
void ConfigureWidgets() override
{
m_mainLayout.attach(m_button, 0, 0, 1, 1);
m_button.set_label("Hello");
}
};
В этом главном окне основной макет задается желтым фоном; в главном макете есть Gtk::Button
с меткой «Hello». Проблема, связанная с этой стратегией, заключается в том, что при запуске кода я получаю странные размеры окна / макета:
Заметьте, что желтый макет - это больше, чем единственный виджет (кнопка), содержащийся в нем. Это результат, который я ожидал бы:
То есть окно и основной макет должны сжиматься до размера их единственного содержащегося виджета. Странно, если я сделаю окно set_resizable(false)
, я получу желаемый размер, но тогда я не смогу изменить его размер, что часто неприемлемо.
Вопросы:
- Почему это не так (почему макет занимает так много дополнительного пространства)?
- Как этого добиться, не дублируя базовый код для каждого окна?
Вы можете создать этот код, используя g++
, добавив его в:
#include <memory>
#include <gtkmm.h>
// Add here...
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "so.realize");
std::unique_ptr<MyWindow> mainWindow = std::make_unique<MyWindow>(*(app.get()));
return mainWindow->Show();
}
и запустив:
g++ -std=c++17 main.cpp -o example.out `pkg-config gtkmm-3.0 --cflags --libs`