Python GtkBuilder Template 的坑
Jan 29, 2015最近在学习 PyGTK,在学习 GTK+3 参考手册(翻译版),在学习填充窗口中遇到了一个 python 绑定中 GtkBuilder template resource 的坑。
代码文件如下:
#!/usr/bin/env python3
from gi.repository import Gtk, Gio
import sys
import os
class AppWindow(Gtk.ApplicationWindow):
def __init__(self, app):
Gtk.ApplicationWindow.__init__(self, application=app)
self.set_default_size(400, 200)
# 首先从资源中加载 ui template 文件
self.set_template_from_resource('/in/everyx/doubanfmgtk/application.ui')
# 要记得初始化
self.init_template()
class DoubanFMGtkApp(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self,
application_id='in.everyx.doubanfmgtk',
flags=Gio.ApplicationFlags.FLAGS_NONE)
def do_activate(self):
main_window = AppWindow(app=self)
main_window.show_all()
if __name__ == "__main__":
# 加在资源文件
resource = Gio.resource_load(os.path.abspath(os.path.join(
os.path.abspath("__file__"),
"../../data/doubanfm-gtk.gresource")))
# 注册资源文件
Gio.Resource._register(resource)
app = DoubanFMGtkApp()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
运行后,问题来了,一直不能出现正确的窗口,错误提示如下:
(application.py:19907): Gtk-CRITICAL **: Error building template class '__main__+AppWindow' for an instance of type '__main__+AppWindow': Parsed template definition for type `AppWindow', expected type `__main__+AppWindow'.
再看看 ui
文件
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.8 -->
<template class="AppWindow" parent="GtkApplicationWindow">
<property name="title" translatable="yes">Example Application</property>
<property name="default-width">600</property>
<property name="default-height">400</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="header">
<property name="visible">True</property>
<child type="title">
<object class="GtkStackSwitcher" id="tabs">
<property name="visible">True</property>
<property name="margin">6</property>
<property name="stack">stack</property>
</object>
</child>
<child>
<object class="GtkToggleButton" id="search">
<property name="visible">True</property>
<property name="sensitive">False</property>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="search-icon">
<property name="visible">True</property>
<property name="icon-name">edit-find-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
</object>
</child>
</template>
</interface>
其中第四行的 class 明明是 AppWindow
,但是在 使用 python 绑定的时候居然要用 __main__+AppWindow
这么奇葩的类名,改过来就运行正常了。终于明白为什么看的 PyGTK 项目的代码中都没有用这种方法来创建窗口了,太反人类了。所以下面就用常见的 GtkBuilder 方法来使用 resource。
上面的 ui 文件中,其实就是将 titlebar 设置为 GtkHeaderBar,改造一下 ui 文件,如下:
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.8 -->
<object class="GtkHeaderBar" id="headerbar">
<property name="visible">True</property>
<property name="show-close-button">True</property>
<child type="title">
<object class="GtkStackSwitcher" id="tabs">
<property name="visible">True</property>
<property name="margin">6</property>
<property name="stack">stack</property>
</object>
</child>
<child>
<object class="GtkToggleButton" id="search">
<property name="visible">True</property>
<property name="sensitive">False</property>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="search-icon">
<property name="visible">True</property>
<property name="icon-name">edit-find-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
</object>
</interface>
修改代码
class AppWindow(Gtk.ApplicationWindow):
def __init__(self, app):
Gtk.ApplicationWindow.__init__(self, application=app)
self.set_default_size(400, 200)
builder = Gtk.Builder()
builder.add_from_resource('/in/everyx/doubanfmgtk/headerbar.ui')
headerbar = builder.get_object('headerbar')
self.set_titlebar(headerbar)
就能正确加载了。
总结
不要使用 GtkBuilder template,使用 GtkBuilder。代码的界面和逻辑分离,使用 ui 文件控制界面,可以将各个部件,如 headerbar,一些 GtkBox 中的内容合理的利用 ui 文件组织起来,在 python 代码中按照需要再组装成界面。代码结构可以参考Gnome Music 项目。