我正在学习如何使用基于 GTK 库的图形用户界面构建 C++ 应用程序。
我在Linux Mint中工作,虽然我已经将系统升级到最新版本(Linux Mint 22 Wilma),但系统包管理器只允许安装libgtkmm版本3.24.9-1,因此我无法使用该功能
get_widget_derived()
(仅在后续版本中出现。
但是,这个问题中描述的问题的解决方案是基于该函数
get_widget_derived()
。
为了使用该函数,我尝试将其定义合并到我的代码中,在下面的最小示例中:
窗口.h
#pragma once
#include <gtkmm.h>
class MainWindow : public Gtk::Window
{
public:
MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder);
~MainWindow() override;
protected:
//Signal handlers:
Glib::RefPtr<Gtk::Builder> m_refBuilder;
};
template <typename T_Widget, typename... Args> void get_widget_derived(const Glib::ustring& name, T_Widget*& widget, Args&&... args);
窗口.cpp
#include "window.h"
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder)
: Gtk::Window(cobject),
m_refBuilder(refBuilder)
{}
MainWindow::~MainWindow()
{}
main.cpp
#include "window.h"
#include <iostream>
#include <cstring>
namespace
{
MainWindow* pDialog = nullptr;
Glib::RefPtr<Gtk::Application> app;
template <typename T_Widget, typename... Args> inline
void get_widget_derived(const Glib::ustring& name, T_Widget*& widget, Args&&... args)
{
// Initialize output parameter:
widget = nullptr;
// Get the widget from the GtkBuilder file.
using cwidget_type = typename T_Widget::BaseObjectType;
auto pCWidget = (cwidget_type*) Gtk::Builder::get_cwidget(name);
//The error was already reported by get_cwidget().
if(!pCWidget)
return;
//Check whether there is already a C++ wrapper instance associated with this C instance:
Glib::ObjectBase* pObjectBase = Glib::ObjectBase::_get_current_wrapper((GObject*)pCWidget);
//If there is already a C++ instance, then return it again:
if(pObjectBase)
{
widget = dynamic_cast<T_Widget*>( Glib::wrap((GtkWidget*)pCWidget) );
//Newer, more spec-complaint, versions of g++ cannot resolve a specific wrap() function in a template.
//The dynamic cast checks that it is of the correct type.
//Somebody might be trying to call get_widget_derived() after already calling get_widget(),
//or after already calling get_widget_derived() with a different derived C++ type.
if(!widget)
g_critical("Gtk::Builder::get_widget_derived(): dynamic_cast<> failed. An existing C++ instance, of a different type, seems to exist.");
}
else
{
//Create a new C++ instance to wrap the existing C instance:
Glib::RefPtr<Gtk::Builder> refThis(this);
refThis->reference(); //take a copy.
widget = new T_Widget(pCWidget, refThis, std::forward<Args>(args)...);
}
}
void on_app_activate()
{
// Load the GtkBuilder file and instantiate its widgets:
auto refBuilder = Gtk::Builder::create();
try
{
refBuilder->add_from_file("derived.ui");
}
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:
pDialog = get_widget_derived<MainWindow>(refBuilder, "mainWindow");
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->set_visible(true);
}
} // anonymous namespace
int main(int argc, char** argv)
{
app = Gtk::Application::create("org.gtkmm.example");
app->signal_activate().connect([] () { on_app_activate(); });
return app->run(argc, argv);
}
builder.ui
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkWindow" id="mainWindow">
<property name="default_width">400</property>
<property name="default_height">300</property>
</object>
</interface>
如果我尝试构建此代码,我会收到错误:
error: cannot call member function ‘GtkWidget* Gtk::Builder::get_cwidget(const Glib::ustring&)’ without object
23 | auto pCWidget = (cwidget_type*) Gtk::Builder::get_cwidget(name);
应该实例化的对象是什么? 这是解决问题的好方向吗?
我对 C++ 的经验很少,所以非常感谢任何帮助。
如果您是 C++ 新手,我知道您可能会对未记录的 Gtkmm API 感到有点困惑。我也在使用 3.24,这是一个工作示例。
窗口.h
首先,当你开始自己做时,你需要在某个地方有一个
MainWindow
定义/声明。在这里,我在同一个头文件中进行了声明和定义,但我也可以将其分成两部分(即 window.h 和 window.cpp):
#include <gtkmm/window.h>
class MainWindow : public Gtk::Window
{
public:
MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder)
: Gtk::Window(cobject)
, m_builder{p_builder}
{
}
protected:
Glib::RefPtr<Gtk::Builder> m_builder;
};
所以我继承了
Gtk::Window
并且在构造函数中,我传递了C指针和构建器。这正是构建器必须完成的工作方式(这是在线记录的内容)。
main.cpp
这是
main
函数。这是您创建构建器实例的位置。请注意,在 3.24 中,get_widget_derived
不是自由函数,而是类 Gtk::Builder
的方法:
#include <iostream>
#include <cstring>
#include <gtkmm/application.h>
#include <gtkmm/builder.h>
#include "window.h"
int main(int argc, char** argv)
{
// Create an application object. This is mandatory since it initializes
// the toolkit. If you don't do this, widgets won't show.
auto app = Gtk::Application::create(argc, argv);
// Since you want to use the builder, you need to instantiate one. You
// can do this directly using your UI file.
auto builder = Gtk::Builder::create_from_file("builder.ui");
// At this point, you are ready to create your window. The builder is
// going to create an instance for you, and you get a handle to this
// istance to later use.
// First, you create a MainWindow pointer, which points to nothing. It
// is going to be used by the builder to get you the handle.
MainWindow* window = nullptr;
// Then, using you builder instance. You call get_widget_derived and
// pass the pointer to get the handle.
builder->get_widget_derived("mainWindow", window);
// You then show the window by using you handle (i.e window).
return app->run(*window);
}
如果您想使用较新版本的 Gtkmm,则需要使用 JHBuild 自行构建。我不推荐它,除非你有充分的理由。根据我的经验,这并不是那么简单。