我正在开发一个使用 gtkmm(版本 3.24)和外部 XML 文件用 C++ 编写的 GUI 应用程序的简单示例。
感谢this其他问题的回答,我设法按照我想要的方法创建一个空窗口(用于“mainWindow”类声明的头
myWindow.h
文件,用于类定义的另一个myWindow.cpp
文件,一个main.cpp
文件,以及带有小部件的 builder.ui
文件)。
我的目标是用小部件填充窗口,也许从一个简单的按钮开始。 但由于我对 C++ 的了解很差,所以我不能。 经过一番研究,我尝试合并类似示例中的代码,特别是来自here的代码,这就是我到目前为止所拥有的:
myWindow.h
#pragma once
#include <gtkmm/window.h>
#include <gtkmm/builder.h>
#include <gtkmm/box.h>
#include <gtkmm/button.h>
class MainWindow : public Gtk::Window
{
public:
MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder);
protected:
Glib::RefPtr<Gtk::Builder> m_builder;
};
myWindow.cpp
#include "myWindow.h"
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder)
: Gtk::Window(cobject)
{
this->m_builder = p_builder;
auto pButton = p_builder->get_widget<Gtk::Button>("button1");
}
main.cpp
#include <iostream>
#include <cstring>
#include <gtkmm/application.h>
#include <gtkmm/builder.h>
#include "myWindow.h"
int main(int argc, char** argv)
{
auto app = Gtk::Application::create(argc, argv);
auto builder = Gtk::Builder::create_from_file("builder.ui");
MainWindow* window = nullptr;
builder->get_widget_derived("mainWindow", window);
try
{
// You then show the window by using you handle (i.e window).
app->run(*window);
}
catch(const Gtk::BuilderError& bdre)
{
// speciffic handling for BuilderError
std::cout << "Builder error: " << bdre.what() << std::endl;
}
catch(const std::runtime_error& re)
{
// speciffic handling for runtime_error
std::cout << "Other runtime error: " << re.what() << std::endl;
}
catch(const std::exception& ex)
{
// speciffic handling for all exceptions extending std::exception, except
// std::runtime_error which is handled explicitly
std::cout << "Error occurred: " << ex.what() << std::endl;
}
catch(...)
{
// catch any other errors (that we have no information about)
std::cout << "Unknown failure occurred. Possible memory corruption" << std::endl;
}
return 0;
}
builder.ui
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkWindow" id="mainWindow">
<property name="title">Grid</property>
<property name="default-width">600</property>
<property name="default-height">400</property>
<child>
<object id="button1" class="GtkButton">
<property name="label">Button</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
</child>
</object>
</interface>
当我构建此代码时,出现以下错误:
myWindow.cpp:14:54: error: no matching function for call to ‘Gtk::Builder::get_widget<Gtk::Button>(const char [8])’
14 | auto pButton = p_builder->get_widget<Gtk::Button>("button1");
如果我添加
Gtk::Button *addButton;
作为头文件中的受保护指针
myWindow.h
,我按以下方式使用get_widget()
函数
p_builder->get_widget("button1", addButton);
在文件
myWindow.cpp
中的构造函数定义中,然后我可以构建可执行文件,但随后出现以下运行时错误:
terminate called after throwing an instance of 'Gtk::BuilderError'
Aborted (core dumped)
没有更多信息(即“try / catch”无法捕获错误详细信息)。
系统日志报告以下内容:
systemd-coredump[826232]: Process 826227 (base) of user 1000 dumped core.#012#012Module libzstd.so.1 from deb libzstd-1.5.5+dfsg2-2build1.1.amd64#012Module libsystemd.so.0 from deb systemd-255.4-1ubuntu8.4.amd64#012Stack trace of thread 826227:#012#0 0x000073d024a9eb1c __pthread_kill_implementation (libc.so.6 + 0x9eb1c)#012#1 0x000073d024a4526e __GI_raise (libc.so.6 + 0x4526e)#012#2 0x000073d024a288ff __GI_abort (libc.so.6 + 0x288ff)#012#3 0x000073d024ea5ffe n/a (libstdc++.so.6 + 0xa5ffe)#012#4 0x000073d024ebae9c n/a (libstdc++.so.6 + 0xbae9c)#012#5 0x000073d024ea5a49 _ZSt9terminatev (libstdc++.so.6 + 0xa5a49)#012#6 0x000073d024ebb128 __cxa_throw (libstdc++.so.6 + 0xbb128)#012#7 0x000073d025614130 _ZN3Gtk12BuilderError10throw_funcEP7_GError (libgtkmm-3.0.so.1 + 0x214130)#012#8 0x000073d0253dc34c _ZN4Glib5Error15throw_exceptionEP7_GError (libglibmm-2.4.so.1 + 0x6834c)#012#9 0x000073d02564819e _ZN3Gtk7Builder13add_from_fileERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE (libgtkmm-3.0.so.1 + 0x24819e)#012#10 0x000073d0256481ee _ZN3Gtk7Builder16create_from_fileERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE (libgtkmm-3.0.so.1 + 0x2481ee)#012#11 0x000055e7fb40e6c8 n/a (/home/fabio/Documents/programming/C++/Esperimenti_GTK/GTK4/simple_button/build/base + 0x76c8)#012#12 0x000073d024a2a1ca __libc_start_call_main (libc.so.6 + 0x2a1ca)#012#13 0x000073d024a2a28b __libc_start_main_impl (libc.so.6 + 0x2a28b)#012#14 0x000055e7fb40e565 n/a (/home/fabio/Documents/programming/C++/Esperimenti_GTK/GTK4/simple_button/build/base + 0x7565)#012ELF object binary architecture: AMD x86-64
我做错了什么? 我应该在哪里“调用”XML 文件中定义的小部件,以及如何调用?
所呈现代码中的问题来自于对 Gtk::Builder 工作原理的理解不足。
事实上,一旦从 XML 文件实例化构建器,XML 文件中存在的所有小部件都将被创建并显示在窗口中,无需在代码中“调用”或实例化它们。
一个最小的工作示例,窗口中只有几个小部件(按钮),如下:
myWindow.h
#pragma once
#include <gtkmm/window.h>
#include <gtkmm/builder.h>
class MainWindow : public Gtk::Window
{
public:
MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder);
protected:
Glib::RefPtr<Gtk::Builder> m_builder;
};
myWindow.cpp
#include "myWindow.h"
#include <gtkmm/builder.h>
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder)
: Gtk::Window(cobject), m_builder{p_builder}
{
}
main.cpp
#include <iostream>
#include <cstring>
#include <gtkmm/application.h>
#include <gtkmm/builder.h>
#include "myWindow.h"
int main(int argc, char** argv)
{
auto app = Gtk::Application::create(argc, argv);
auto builder = Gtk::Builder::create_from_file("builder.ui");
MainWindow* window = nullptr;
builder->get_widget_derived("mainWindow", window);
app->run(*window);
return 0;
}
builder.ui
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object id="mainWindow" class="GtkWindow">
<property name="title">Pippo</property>
<property name="default_width">400</property>
<property name="default_height">300</property>
<child>
<object class="GtkGrid" id="grid">
<property name="visible">True</property>
<child>
<object id="button1" class="GtkButton">
<property name="label">Button 1</property>
<property name="visible">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object id="button2" class="GtkButton">
<property name="label">Button 2</property>
<property name="visible">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object id="button3" class="GtkButton">
<property name="label">Button 3</property>
<property name="visible">True</property>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object id="button4" class="GtkButton">
<property name="label">Button 4</property>
<property name="visible">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>