专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Python » gobject:在 Python 中封装 GObject »正文

gobject:在 Python 中封装 GObject

来源: 发布时间:星期四, 2009年1月8日 浏览:26次 评论:0
  本文举例源代码或素材下载

  无论您是否对 C 特别精通通过学习如何在 Python 中封装可供使用 GTK+ C 模块都使您能够在 Python 中根据您需要随时使用用 C 编码 GObject

  Python 是用于编码图形界面极佳语言由于可以迅速地编写工作代码并且不需要费时编译周期所以可以立即使界面启动和运行起来并且不久便可使用这些界面将这点和 Python 易于链接本机库能力结合起来就可以形成个出色环境

  gnome-python 是为 Python 封装 GNOME 及其相关库软件Software包这使您能够用 Python 编写外观和核心 GNOME 应用完全相同应用而所花时间只是用 C 编写该应用所花部分

  然而不用 C 进行编程会有个缺点大多数 GNOME 都是用 C 编写对于要在 Python 中使用窗口小部件必须将它们封装对于知道封装过程如何工作人来说这是个快速任务但它不是自动除非窗口小部件属于核心 GNOME 库或者至少非常有用否则将不会对它们进行封装C 员可能必须编写更复杂代码但它们确实先做了这步!

  但并不定是那样!虽然从传统上讲封装窗口小部件过程这技术只有极少数人才知道但它并不真那么难如果您在发现新窗口小部件时可以将它们封装那么您就可以立刻在 Python 中使用它们

  本文将描述如何封装用 C 编码 GObject(所有 GTK+ 窗口小部件和许多相关对象最终基类)以便可以从 Python 代码使用它假设您机器上安装了 gnome-python V1.99.x(如果没有安装请参阅 参考资料以获取链接)如果您正在使用软件Software包请确保安装了该开发软件Software包另外还必须安装 Python 2.2 及其头文件假设您了解 Make、Python、GTK+ 2 和些 C 方面知识

  为了演示该过程我将封装 EggTrayIcon 它是用于在通知区中抽象表示图标 GTK+ 窗口小部件该库在 GNOME CVS 中位于 libegg 模块在本文结尾我们将有个名为 trayicon 本机 Python 模块它包含个 TrayIcon 对象

  开始时获得 eggtrayicon.c 和 eggtrayicon.h(其链接在本文结尾 参考资料节中)然后将它们放入新目录中应该在 automake 环境中构建该源文件(但我们将不在这种环境中)所以或者除去这些文件中 # <config.h> 或者创建个名为 config.h 空文件然后创建个空 makefile;接下来我们将填充它

  创建界面定义

  该对象封装过程步是创建 trayicon.defs该文件为该对象指定 API定义文件是用种类 Scheme 语言编写虽然对于小型界面来说它们很容易生成但对于大型界面或初学者来说编写它们会很吃力

  gnome-python 和名为 h2def 工具起提供该工具将解析头文件并生成粗略定义文件注:它实际上并没有解析 C 代码而只是使用正则表达式所以它确要求传统格式化 GObject并且不能正确解析奇特格式化 C 代码

  要生成定义文件我们如下 h2def : python /usr/share/pygtk/2.0/codegen/h2def.py eggtrayicon.h > trayicon.defs

  注:如果没有将 h2def.py 安装在 /usr 下则必须更改该路径以指向它所在地方

  如果我们现在查看已生成定义文件它应该具有某些意义该文件中含有类 EggTrayIcon 定义、构造以及思路方法 send_message 和 cancel_message 该文件没有任何明显我们不想除去任何思路方法或字段所以我们不需编辑它注:该文件不是特定于 Python 其它语言绑定也可以使用它

  生成包装器

  既然我们有了界面定义那么就可以生成 Python 包装器代码块这包括生成个覆盖文件覆盖文件告诉代码生成器要包括哪些头文件、模块名将是什么等等

  通过使用 %% 将覆盖文件分成多个节(以 lex/yacc 样式)这些节定义要包括哪些头文件、模块名、要包括哪些 Python 模块、要忽略哪些以及最后所有手工封装下面是 trayicon 模块覆盖文件

  清单 1. trayicon.override

%%
headers
# <Python.h>       
# "pygobject.h"
# "eggtrayicon.h"
%%
modulename trayicon          
%%
import gtk.Plug as PyGtkPlug_Type   
%%
ignore-glob
 *_get_type              
%%


  让我们再次更详细地检查该代码:

  headers

  # <Python.h>

  # "pygobject.h"

  # "eggtrayicon.h"

  这些是在构建包装器时要包括头文件始终必需包括 Python.h 和 pygobject.h当我们封装 eggtrayicon.h 时我们也必需包括它们

  modulename trayicon

  modulename 规范标准声明包装器将在什么模块中

  import gtk.Plug as PyGtkPlug_Type

  这些是用于包装器 Python 导入请注意命名约定;对于要编译模块必须遵守它通常导入对象超类就足够了例如如果对象直接从 GObject 继承则使用:

  import gobject.GObject as PyGObject_Type

  ignore-glob

  *_get_type

  这是个要忽略 glob 模式(shell 样式正则表达式)Python 替我们处理了类型代码因此我们忽略 *_get_type ;否则将对它们进行封装

  既然我们构造了覆盖文件那么就可以使用它来生成包装器 gnome-python 绑定为生成包装器提供了种神奇工具我们可以随意使用它将下列内容添加到 makefile:

  清单 2. makefile

DEFS='pkg-config --variable=defsdir pygtk-2.0'    
trayicon.c: trayicon.defs trayicon.override      
  pygtk-codegen-2.0 --prefix trayicon        
  --register $(DEFS)/gdk-types.defs         
  --register $(DEFS)/gtk-types.defs
  --override trayicon.override           
  trayicon.defs > $@               


  再次详细介绍说明:

  DEFS='pkg-config --variable=defsdir pygtk-2.0'

  DEFS 是包含 Python GTK+ 绑定定义文件路径

  trayicon.c: trayicon.defs trayicon.override

  生成 C 代码取决于定义文件和覆盖文件

  pygtk-codegen-2.0 --prefix trayicon

  这里 gnome-python 代码生成器 prefix 参数被用作在已生成代码内部变量名前缀您可以随意命名该参数但使用模块名话可使符号名保持

  --register $(DEFS)/gdk-types.defs

  --register $(DEFS)/gtk-types.defs

  模块使用 GLib 和 GTK+ 中类型所以我们还必须告诉代码生成器装入这些类型

  --override trayicon.override

  该参数将我们创建覆盖文件传递给代码生成器

  trayicon.defs > $@

  这里代码生成器最后个选项是定义文件本身代码生成器在标准输出上进行输出所以我们将它重定向到目标 trayicon.c

  如果我们现在运行 make trayicon.c 然后查看已生成文件那么我们会看到 C 代码包装 EggTrayIcon 中每个不必担心警告 No ArgType for GdkScreen*— 这是正常

  正如您所看到那样封装代码看上去复杂所以我们感谢代码生成器为我们编写稍后我们将学习当想要调优封装时如何手工封装各个思路方法而我们自己不必编写所有包装器

  创建模块

  既然已经创建了包装器代码块那么就需要个启动它思路方法这涉及创建 trayiconmodule.c该文件可被视为 Python 模块 该文件是样板文件代码(和覆盖文件相似)我们对它稍作修改下面是我们将使用 trayiconmodule.c:

  清单 3. TrayIcon 模块代码# <pygobject.h>
void trayicon_register_es (PyObject *d);
extern PyMethodDef trayicon_functions;
DL_EXPORT(void)
inittrayicon(void)
{
  PyObject *m, *d;
  init_pygobject ;
  m = Py_InitModule ("trayicon", trayicon_functions);
  d = PyModule_GetDict (m);
  trayicon_register_es (d);
   (PyErr_Occurred ) {
    Py_FatalError ("can't initialise module trayicon");
  }
}


  这里需要介绍说明些细微区别有多个使用单词 trayicon 源代码 inittrayicon 名称和化模块名称是 Python 模块真实名称因此是最终共享对象名称 trayicon_functions 和 trayicon_register_es 是根据代码生成器 --prefix 参数命名正如前面所提到那样最好使这些名称保持以便编码该文件不会变得很混乱

  尽管名称源可能存在混淆但该 C 代码非常简单化 GObject 和 trayicon 模块然后向 Python 注册这些类

  现在我们有了所有代码块就可以生成共享对象了将以下内容添加到 makefile:

  清单 4. makefile 附加代码部分

CFLAGS = 'pkg-config --cflags gtk+-2.0 pygtk-2.0' -I/usr//python2.2/ -I.  
LDFLAGS = 'pkg-config --libs gtk+-2.0 pygtk-2.0'                 
trayicon.so: trayicon.o eggtrayicon.o trayiconmodule.o              
  $(CC) $(LDFLAGS) -shared $^ -o $@                       


  让我们再次逐行仔细检查:

  CFLAGS = 'pkg-config --cflags gtk+-2.0 pygtk-2.0' -I/usr//python2.2/ -I.

  该行定义 C 编译标志我们使用 pkg-config 来获取 GTK+ 和 PyGTK 路径

  LDFLAGS = 'pkg-config --libs gtk+-2.0 pygtk-2.0'

  该行定义链接标志再次使用 pkg-config 来获取正确库路径

  trayicon.so: trayicon.o eggtrayicon.o trayiconmodule.o

  共享对象是根据生成代码、我们刚才编写模块代码以及 EggTrayIcon 实现构造隐式规则根据我们创建 .c 文件构建 .o 文件

  $(CC) $(LDFLAGS) -shared $^ -o $@

  这里我们构建最终共享库

  现在运行 make trayicon.so 应该会根据定义生成 C 代码编译 3个 C 文件最后将它们全都链接在做得不错 — 我们已经构建了第个本机 Python 模块如果它没有编译和链接请仔细检查这些阶段并确保早先没有出现会引起稍后出错警告

  既然我们有了 trayicon.so就可以在 Python 中尝试并使用它开始时最好装入它然后列出其成员在 shell 中运行 python 以打开交互式解释器然后输入以下命令

  清单 5. TrayIcon 交互式测试

$ python
Python 2.2.2 (#1, Jan 18 2003, 10:18:59)
[GCC 3.2.2 20030109 (Debian prerelease)] _disibledevent= egg_tray_icon_send_message(EGG_TRAY_ICON(self->obj),

  timeout, message, len);

   PyInt_FromLong(ret);

  }

  这里我们实际上 egg_tray_icon_send_message 然后将返回 转换成 PyObject

  起先这看上去有点可怕但它最初是从 trayicon.c 中生成代码复制来在大多数情况下如果您只想调优所需要参数那么这是完全有可能只要从生成 C 中复制并粘贴相关添加有魔力覆盖行并编辑该代码直到它如您所愿

  最重要更改是修改所需要参数 PyArg_ParseTupleAndKeywords 中看上去费解串定义了所需要参数最初它是 isi:TrayIcon.send_message ;这意味着参数依次是 、 char* (s 表示串)和 ;而且如果抛出个异常则该称作 TrayIcon.send_message 我们不想必须在 Python 代码中指定串长度所以将 isi 更改为 is# 使用 s# 来代替 s 意味着 PyArg_ParseTupleAndKeywords 将自动计算串长度并为我们设置另个变量 — 这正是我们想要



  要使用新包装器只需重新构建共享对象并将测试 send_message 更改成:

t.send_message(1000, message)

  如果每件事情都照常进行那么这个修改后举例应该有相同行为但具有更清晰代码

  结束游戏

  我们采用了小型但有用 C GObject封装它这样就可以在 Python 中使用它甚至可以对包装器进行度身定做以符合我们需要这里技术可以多次应用于区别对象允许您使用在 Python 中找到任何 GObject



0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: