April
30th 2003
中文版声明:
Translator:marxu
E-mail:[email protected]
version:1.1
为保证本文档自由传播,保留版权,中文版版权归译者所有。
本许可授权你制作和发布本手册逐字的拷贝,但在所有拷贝上要保留本版权声明和许可声明。
本许可授权你复制和发布本文档的修订版,在逐字的拷贝原文,包含和原版完全一致的版权声明,并且所有修订部分也是以本许可声明发布的条件下。
如果你准备出版本文档,请联系维护者,以确保你获得本文档的最新版本。
对本文档的适用范围不作担保,
它仅仅是作为一个免费的资源提供。因此,这里提供的这些信息的作者和维护者无法做出这些信息一定正确的保证。
- 目录表
- 1.总说明
-
- 1.1. 注意:本文档主要是关于GTK+1.2
- 1.2.
致谢
- 1.3. 作者
- 1.4.
什么是GTK+
- 1.5.
GTK+中的+是什么意思?
- 1.6.
GTK+, GDK和GLib中的G代表什么?
- 1.7.
那里可以找到GTK+的文档
- 1.8.
有没有关于GTK+的邮件列表(或者是邮件列表的压缩包)?
- 1.9.
GTK+如何获得帮助?
- 1.10.
GTK+中如何报告bug?
- 1.11.
GTK+有没有windows版本?
- 1.12.
哪些程序是用GTK+编成的?
- 1.13.
我想用GTK+编写一个程序,IRC(在线聊天系统)的客户端如何?
- 2. GTK+如何得到 配置
安装和查错
-
- 2.1. 运行GTK+需要安装什么
- 2.2.
那里可以得到GTK+
- 2.3.
如何配置/编译GTK+
- 2.4.
编译的时候出现错误 如:make: file `Makefile' line 456:
Syntax erro
- 2.5. 我编译和安装了GTK+,但是不能将程序和它连接[GTK
2.x]
- 2.6. 当我安装GTK+的程序时,configure报告不能找到GTK.[GTK
2.x]
- 3. GTK+的开发和维护
-
- 3.1. 每个人都在谈论的CVS是什么东西,怎样才能访问它?
- 3.2.
如何向GTK+投稿?
- 3.3.
如何知道我写的补丁是否被采用,如果没有,原因是什么?
- 3.4.
向库中添加新的控件的方针是什么?
- 3.5.
除了C之外,有人在绑定其他的语言吗?
- 4. 用GTK+开发:开始
-
- 4.1. 如何开始?
- 4.2.
如何用GTK+编写安全的/SUID/SGID敏感的程序?GTK+安全吗?听说的GTK_MODULES安
全漏洞是什么?
- 4.3. 我尝试编译一个小型的Hello
World程序,但是失败了,有什么线索? [GTK
2.x]
- 4.4. 怎样使用make程序?
[GTK 2.x]?
- 4.5.
我在make文件中使用了后置引用(backquote),但是我的make失败了.
- 4.6.
我想添加一些配置资料(configure
stuff),应如何做?
- 4.7. 我尝试使用gdb来调试程序,但是当我执行到一些断点时,它挂起了我的X服务器(X
server),怎么办?
- 5. 用GTK+开发:常规问题
-
- 5.1.
GTK中有那些控件(widget)?
- 5.2.
GTK+的线程安全吗?如何编写多线程GTK+程序?
- 5.3.
当在GTK+的app文件中使用fork()时,为什么会出现奇怪的 'x io
error'错误?
- 5.4. 为什么当按钮按下时文本(contents)不移动?这里有一个补丁可以使它这样.
- 5.5. 如何识别一个控件的最高层窗口(top level window)或者其他的祖先?
- 5.6. 如何得到一个GtkWindow窗口的ID(Window ID)?
- 5.7.
如何捕获一个双击(double
click)事件(event)(例如在一个列表控件中)?
- 5.8.
顺便问一下,信号(signals)和事件(events)的区别是什么?
- 5.9.
传递给delete_event(或者其他事件)处理函数的数据被损坏.
- 5.10. 我将信号和所有事件都连接起来,但是看起来我并没有捕获它,什么原因?
- 5.11. 我要向GTK+控件中添加一个新的信号,怎么办?
- 5.12. 文本能否换行显示?
- 5.13. 如何设置窗口模式(modal)?/如何激活一个单一的窗口?
- 5.14.
为什么我的控件(例如 进度条)没有更新?
- 5.15.
如何向GTK+的对象/控件附加数据
- 5.16. 如何去掉附加在控件上的数据?
- 5.17.
如何重定控件的父窗口?
- 5.18. 如何得到控件的位置?
- 5.19.
如何设置控件/窗口的大小?如何才能禁止用户重新设置窗口的大小?
- 5.20.
如何向我的GTK+程序中添加一个弹出式菜单?
- 5.21.
如何禁止或使能一个控件,例如一个按钮?
- 5.22. 为什么gtk_clist*函数中的文本参数( text
argument)不应被声明为const?
- 5.23.
如何在屏幕上显示象素(图像数据)?
- 5.24.
如何在不创建窗口或不显示窗口的情况下创建一个象素映射(
pixmap)?
- 5.25. 如何拖放?
- 5.26. 为什么GTK+/GLib会泄漏内存?
- 6. 用GTK+开发:
控件专题
-
- 6.1.
在GtkList控件中如何找出选择项( selection)?- 6.2.
在GtkCList控件中,当表内容滚动时,怎样避免标题消失?
- 6.3.
我不想让我的程序的用户在组合框(GtkCombo)中输入文本,怎么办?
- 6.4.
如何捕捉组合框( combo
box)的改变?
- 6.5. 如何在菜单中定义分隔线(
separation line)?
- 6.6. 如何向右对齐菜单,比如Help?
- 6.7.
如何在菜单项中添加带下滑线的加速键?
- 6.8.
如何从GtkMenuItem中重新找回文本?
- 6.9.
如何向右(或其他方向)对齐GtkLabel?
- 6.10.
如何给 GtkLabel控件设置背景颜色?
- 6.11.
如何使用 Resource 文件来给 GtkLabel设置颜色和字体?
- 6.12.
如何在 Resource 文件中配置工具提示(
Tooltips)?
- 6.13. 无法在GtkEntry中输入大于2000(大约)个字符,什么原因?
- 6.14.
如何能在按下回车时使
GtkEntry控件激活?
- 6.15. 如何确认/限制/筛选GtkEntry中的输入?
- 6.16.
如何在GtkText控件中使用水平滚动条(
horizontal scrollbars)?
- 6.17. 如何改变GtkText控件的字体?
- 6.18.
如何在GtkText控件中设定光标位置(
cursor position)?
- 7. 关于GDK
-
- 7.1.
什么是GDK?- 7.2.
如何使用颜色分配?
- 8. 关于GLib
-
- 8.1. 什么是GLib?
- 8.2.
如何使用双向链表(doubly linked lists)?
- 8.3.
当我释放分配的链表节点时,内存似乎没有被释放.
- 8.4.
为什么使用g_print,
g_malloc, g_strdup和其他的glib函数?
- 8.5.
什么是扫描器(GScanner),如何使用?
- 9. GTK+ FAQ
投稿,维护者和版权(英文版)
第一章 总说明
目录表
注意:本文档主要是关于GTK+1.2
本FAQ主要包含的是 GTK+
1.2,在文中的包含 GTK+ 2.x的部分将用[GTK+
2.x]提示.
致谢
FAQ的作者要感谢下面各位:
- Havoc Pennington
- Erik Mouw
- Owen Taylor
- Tim Janik
- Thomas Mailund Jensen
- Joe Pfeiffer
- Andy Kahn
- Federico Mena Quntero
- Damon Chaplin
- and all the members of the GTK+ lists
If we forgot you, please email us! Thanks again (I know, it's really
short :)
作者
GTK+的原始的作者是
- Peter Mattis
- Spencer Kimball
- Josh MacDonald
以后,其他的人加了很多的东西进去,请参GTK发布版的AUTHORS文件.
什么是GTK+?
GTK+是一个小型而高效的控件库,具有Motif的外观和风格.实际上,它比Motif看起来好多了,它包含有基本的控件和一些很复杂的的控件:例如文件选择控件和颜色选择控件.
GTK+提供了一些独特的特性,(至少,我知道其他的控件库不提供他们),例如,按钮不提供标签,它包含了一个子控件,在很多的时候是一个标签,但是,这个子控件也可以是一个映射,图像或者任何其他的程序员想要的集合.在整个的库中,你随处可见这种伸缩性.
GTK+中的+是什么意思?
Peter Mattis的邮件这样表述:
我原来编写的gtk包含以下的三个库:libglib, libgdk and libgtk.它是一种平行的控件层次,你不能够从一个已存在的控件中得到一个新的控件,新版的gtk+包含一个比信号机制更加标准的回调机制,+用来区别原来的版本和新的版本,你可以把它看作一个加入了面向对象特性的旧版的加强版.
GTK+, GDK和GLib中的G代表什么?
GTK+ == Gimp Toolkit
GDK == GTK+ Drawing Kit
GLib == G Library
GTK+如何获得帮助
首先,确定你在这份文档或指南中找不到答案,你确定你做了这件事,是吗?这样的话,你最好将它放在GTK+的邮件列表上.
1.10. GTK+中如何报告bug?
GTK+中如何报告bug?
bug应该报告给GNOME的bug跟踪系统,(http://bugzilla.gnome.org/)在你报告一个新的bug之前,你应该输入你的电子邮件地址,并且得到一个密码才能够进入该系统.
当你提交一个bug的时候有一些选项和文本需要你选择和填写.你给出越多的信息,就越容易把这个bug跟踪找到.你应该提交的有用的信息包括:
如何重现这个bug
如果你能够通过包含在gtk/子文件夹的测试程序来重现这个bug,那将是最方便的.否则请包含一个简短的测试程序能展现出它的行为.实在不行的话,你可以指出一个可以下载的软件中的测试点.
(如果在GIMP能够重现的bug在gtk的测试程序中是最容易发现的.如果你使用GIMP的时候发现了一个bug,请加上你使用的GIMP的版本号.)
-
如果这个bug是一个会导致崩溃(crash),应该准确的记录该崩溃发生时的输出.
-
更进一步的信息,比如堆栈跟踪(stack traces)可能会有用,但是并不是必须的.如果你送出一个堆栈跟踪(stack traces),并且这个错误是X错误,如果能在运行测试命令加上--sync选项产生,它将会很有用.
1.13. 我想用GTK+编写一个程序,IRC(在线聊天系统)的客户端如何?
我想用GTK+编写一个程序,IRC(在线聊天系统)的客户端如何?
到gtk-list找一些建议.现在至少有三个IRC的客户端软件正在开发.(更多信息参阅 http://www.forcix.cx/irc-clients.html ).
- X-Chat.
- girc. (Included with GNOME)
- gsirc. (In the gnome CVS tree)
第二章 GTK+如何得到
配置 安装和查错
2.1. 运行GTK+需要安装什么
运行GTK+需要安装什么
编译GTK+,你只需要一个c编译器(gcc),一个X
window系统和相关的库文件.
编译的时候出现错误:make: file `Makefile' line 456: Syntax
error[GTK
2.x]
确定你是否使用的是GNU make(用make -v 检查),现在有很多形形色色的make的版本,但是不是所有的都能正确的处理自动产生的make文件(
Makefiles).
我编译和安装了GTK+,但是我不能将程序和它连接 [GTK 2.x]
在不能找到GTK+库或者版本不正确时,这个问题经常碰到,一般说来,编译器会出
'unresolved symbol'.
确使GTK库能够被找到,你可以编辑/etc/Id.so.conf文件将包含GTK+库的路径包含进去,它看起来想下面:
/usr/X11R6/lib /usr/local/lib
然后以root身份运行/sbin/ldconfig ,用pkg-config gtk+-2.0
--libs你可以找到GTK需要那些库:
pkg-config gtk+-2.0
--libs
如果你的系统不是通过ld.so 来寻找库文件(例如solaris),那你必须使用LD_LIBRARY_PATH环境变量,(或将路径编译到你的程序中,我不打算在这里细讲),在一个Bourne
shell里:你可以使用(假设你的GTK库在
/usr/local/lib中)
export
LD_LIBRARY_PATH=/usr/local/lib
在c shell中,为:
setenv LD_LIBRARY_PATH
/usr/local/lib
当我在安装GTK+的程序时,configure报告不能找到GTK. [GTK 2.x]
这里有几种较常见的原因:
-
某个地方安装了一个旧版的GTK,你应该将此删除,但是注意:这样可能会破坏那些通过此旧版GTK编译的程序.
-
pkg-config (或GTK的其他组件)不在你的路径中,
或者你的系统上有一个旧版的GTK 输入:
pkg-config gtk+-2.0
--version
来检查上述两种情况,如果输出的不是你期望的结果,那你的系统上就有一个旧版的GTK.
-
./configure J脚本不能找到你的GTK库,当./confgiure编译各种的测试程序时,它需要找到GTK库,请参考前面有关的内容.
如果上面的都不凑效,那看看config.log,它时./configure在运行的时候产生的,在文件底部将有它在失败前的最后的行为,如果是源代码的一段,将它copy到一个文件中然后用config.log中的对应的一行指令来编译它,如果编译成功,尝试运行一下.
每个人都在谈论的CVS是什么东西,怎样采能访问它?
CVS叫协作版本系统(Concurrent Version System),是一种非常流行的软件版本控制手段,它设计用来允许不同的开发者能够同时在相同的源树(source
tree)上操作,源树(source tree)是集中维护的,但是每个开发者都有一个可以修改的本地镜像.
GTK+开发者使用CVS库来储存目前正在开发的版本号的拷贝.同样的,如果要捐献GTK+的补丁,你应该根据CVS版本号来生成.一般的用户应该使用打包的发布版本.
在RedHat的站点上你能找到RPM格式的CVS工具包,下面的站点可以找到最新的版本.
http://download.cyclic.com/pub/
任何人都可以通过匿名登陆下载最新版本的GTK+的CVS版本号,请使用下面的步骤:
-
在类bourne的shell(例如 bash)中,输入:
CVSROOT=':pserver:[email protected]:/cvs/gnome' export CVSROOT
-
然后,如果是第一次下载,需要登陆cvs:
cvs login
-
系统将要求你输入一个密码( password), cvs.gimp.org是没有密码的,所以只需输入回车即可.
-
下载树(tree)并将它放在你目前的工作目录中,输入下面的命令:
cvs -z3 get gtk+
-
注意:在GTK+1.1的树(tree)中,glib被放在另外一个单独的CVS组件中,所以如果没有,你需要下面的方法来得到它:
cvs -z3 get glib
如何向GTK+投稿?
很简单,如果某个地方没有向你预想的那样工作,首先查看文档,确定你是否忘记了什么东西,如果它确实是个bug,或者是不支持的特性,在GTK+的源程序中将它跟踪找到,改写它,然后以(context
diff)的形式制作一个补丁(patch),可以用下面的命令:
diff -ru <oldfile> <newfile>.
然后将补丁文件上传到
ftp://ftp.gtk.org/incoming
请附带一个README文件.并确定你使用正确的命名规则,否则你的补丁将被删掉.文件名应该具有下面的形式:
gtk<username>-<date
yymmdd-n>.patch.gz gtk-<username>-<date
yymmdd-n>.patch.README
日期中的'n'应该是一个唯一的数字(从0开始),表示你当天上传的第几个补丁,除非你在一天中上传了不止一个的补丁,否则数字应该为0.
例如:
gtk-gale-982701-0.patch.gz gtk-gale-982701-0.patch.README
一旦你上传了任何东西,请发一个README 文件到: [email protected]
如何知道我写的补丁是否被采用,如果没有,原因是什么?
上传的补丁都将被移到ftp://ftp.gtk.org/pub/gtk/patches,那儿一个GTK+的开发团队将对补丁进行筛选.如果应用了.它们将会被移动到/pub/gtk/patches/old.
如果补丁因为某种原因没有被应用,将被移动到/pub/gtk/patches/unapplied 或者 /pub/gtk/patches/outdated.你可以在gtk-list邮件列表上询问你的补丁为什么没有被应用.原因很多,从补丁不能干净的使用到根本不正确,如果第一次没有被应用,请不要灰心.
向库中添加新的控件的方针是什么?
这取决于作者,所以如果你改变了控件(widget),必须去问问他们.作为一个普遍的方针,控件如果有用,能正常的工作,不会破坏整个控件集(widget
set),都会被乐意接受的.
除了C外,有人在绑定其他的语言吗?
GTK+的主页上列出了GTK+的语言绑定(http://www.gtk.org/) :
-
这里有几种c++的封装.
-
三个著名的Objective-c绑定正在开发.
-
-
Perl 绑定 ftp://ftp.gtk.org/pub/gtk/perl
-
Guile 绑定. 主页是 http://www.ping.de/sites/zagadka/guile-gtk.
顺便说一句,Guile是GNU计划中R4RS Scheme的具体实现,如果你喜欢Scheme,你可以看看.
-
David Monniaux 报告: "我已经着手开发gtk-O'Caml绑定系统,基本的系统,包括回调(callback),都工作正常,最新的消息可以在这里找到. http://www.ens-lyon.fr/~dmonniau/arcs"
-
几种 python 的绑定:
-
这里还有一些用于GTK+的OpenGL/Mesa的控件.建议看看下面的网页: http://www.student.oulu.fi/~jlof/gtkglarea/index.html
-
最后,还有很多其他的绑定的语言,例如:Eiffel, TOM, Pascal,
Pike 等等.
我尝试编译一个小型的Hello World程序,但是失败了,有什么线索? [GTK 2.x]
既然你很擅长编程,我们在这里将不会讨论编译时间错误(compile time error).
传统的编译GTK+程序的命令是:
gcc -o myprog [c files]
`pkg-config gtk+-2.0 --cflags --libs`
你应该注意到命令中使用的后置引用(backquote),一个普通的错误是:当你编译GTK+的程序时,使用了引用(quote),而不是后置引用(backquotes),如果这样,编译器会告诉:一个未知的文件名为:pkg-config gtk+-2.0
--cflags --libs ,后置引用(backquote)是一条shell的指令,代替命令行中执行这条命令的输出.
上面的命令行将确保:
怎样使用make程序? [GTK 2.x]
下面是一个编译基于GTK+的程序的简单的make文件:
# basic GTK+ app makefile
SOURCES = myprg.c foo.c bar.c
OBJS = ${SOURCES:.c=.o}
CFLAGS = `pkg-config gtk+-2.0 --cflags`
LDADD = `pkg-config gtk+-2.0 --libs`
CC = gcc
PACKAGE = myprg
all : ${OBJS}
${CC} -o ${PACKAGE} ${OBJS} ${LDADD}
.c.o:
${CC} ${CFLAGS} -c $<
# end of file |
了解更多的关于make程序的信息,应该阅读相关的手册页(man
page)或者相关的信息文件(info file).
我在make文件中使用了后置引用(backquote),但是我的make失败了.
后置引用(back qoute)语句可能不会被一些旧版的make程序识别.如果你使用旧版的make程序,make可能会失败,为了让后置引用的语法正确的工作,你应该使用GUN
make程序.(在GNU ftp上得到GNU make ftp://ftp.gnu.org/).
我想添加一些配置资料(configure stuff),应如何做?
使用autoconf/automake,你首先应安装相关的包,分别是:
- m4 preprocessor v1.4 或更高
- autoconf v2.13 或更高
- automake v1.4 或更高
你可以在GUN的ftp主站 (ftp://ftp.gnu.org/)或任何一个镜像上找到这些包,
为了使用这个强大的autoconf/automake工具,你必须先配置,如下:
dnl Process this file with autoconf to produce a configure script.
dnl configure.in for a GTK+ based program
AC_INIT(myprg.c)dnl
AM_INIT_AUTOMAKE(mypkgname,0.0.1)dnl
AM_CONFIG_HEADER(config.h)dnl
dnl Checks for programs.
AC_PROG_CC dnl check for the c compiler
dnl you should add CFLAGS="" here, 'cos it is set to -g by PROG_CC
dnl Checks for libraries.
AM_PATH_GTK(1.2.0,,AC_MSG_ERROR(mypkgname 0.1 needs GTK))dnl
AC_OUTPUT(
Makefile
)dnl |
必须添加一个Makefile.am:
bin_PROGRAMS = myprg
myprg_SOURCES = myprg.c foo.c bar.c
INCLUDES = @GTK_CFLAGS@
LDADD = @GTK_LIBS@
CLEANFILES = *~
DISTCLEANFILES = .deps/*.P |
如果你的工程中包含不止一个的子文件夹,你应该在每个子文件夹中创建一个Makefile.am加上一个主Makefile.am,主Makefile.am如下:
SUBDIRS = mydir1 mydir2 mydir3 |
然后,来使用他们,输入:
aclocal
autoheader
autoconf
automake --add-missing --include-deps --foreign |
了解更多的信息,你应该看autoconf和
automake的文档(附带的文档很容易理解,还有很多有关autoconf和automake的网上资源)
我尝试使用gdb来调试程序,但是当我执行到一些断点时,它挂起了我的X服务器(X
server),怎么办?
来自Federico Mena Quintero:
"X并没有锁定,但是当你执行到从GTK的什么地方回调的断点时,并且这个回调得到一个鼠标的信息,就会出现这种状态."
"加上--sync 选项来运行你的程序,会使调试变得简单一些,如果你想在控制台中执行调试程序,就在一个和X
server不同的控制台中运行.
Eric Mouw还有另外一种解决方案:
"一个旧的终端连接到另外一个不使用的串行口(serial port)也很适合来调试X程序,旧的vt100/vt220终端非常的便宜,但是很难获得."(在 The Netherlands,
YMMV).
GTK中有那些控件(widget)?
GTK+指南中列出了下面的控件:
GtkObject
+GtkData
| +GtkAdjustment
| `GtkTooltips
`GtkWidget
+GtkContainer
| +GtkBin
| | +GtkAlignment
| | +GtkEventBox
| | +GtkFrame
| | | `GtkAspectFrame
| | +GtkHandleBox
| | +GtkItem
| | | +GtkListItem
| | | +GtkMenuItem
| | | | `GtkCheckMenuItem
| | | | `GtkRadioMenuItem
| | | `GtkTreeItem
| | +GtkViewport
| | `GtkWindow
| | +GtkColorSelectionDialog
| | +GtkDialog
| | | `GtkInputDialog
| | `GtkFileSelection
| +GtkBox
| | +GtkButtonBox
| | | +GtkHButtonBox
| | | `GtkVButtonBox
| | +GtkHBox
| | | +GtkCombo
| | | `GtkStatusbar
| | `GtkVBox
| | +GtkColorSelection
| | `GtkGammaCurve
| +GtkButton
| | +GtkOptionMenu
| | `GtkToggleButton
| | `GtkCheckButton
| | `GtkRadioButton
| +GtkCList
| `GtkCTree
| +GtkFixed
| +GtkList
| +GtkMenuShell
| | +GtkMenuBar
| | `GtkMenu
| +GtkNotebook
| +GtkPaned
| | +GtkHPaned
| | `GtkVPaned
| +GtkScrolledWindow
| +GtkTable
| +GtkToolbar
| `GtkTree
+GtkDrawingArea
| `GtkCurve
+GtkEditable
| +GtkEntry
| | `GtkSpinButton
| `GtkText
+GtkMisc
| +GtkArrow
| +GtkImage
| +GtkLabel
| | `GtkTipsQuery
| `GtkPixmap
+GtkPreview
+GtkProgressBar
+GtkRange
| +GtkScale
| | +GtkHScale
| | `GtkVScale
| `GtkScrollbar
| +GtkHScrollbar
| `GtkVScrollbar
+GtkRuler
| +GtkHRuler
| `GtkVRuler
`GtkSeparator
+GtkHSeparator
`GtkVSeparator |
GTK+的线程安全吗?如何编写多线程GTK+程序?
在执行其他的GLib 调用之前调用g_thread_init()可以使GLib库工作在线程安全模式(thread-safe mode)之下.在这种模式中,GLib将会根据需要自动的锁定所有内部数据结构(internal data structures),这并不是说两个线程可以同时访问.例如:一个单一的hash表(hash
table),他们可以同时访问两个不同的hash表,如果两个不同的线程需要访问相同的hash表,程序将会锁定自身.
当GLib被初始化为安全线程模式(thread-safe),GTK+能够"线程识别"(thread aware),在执行任何的GDK调用之前,你必须通过调用gdk_threads_enter()获得一个单一全局锁定(single global lock),而后又调用gdk_threads_leave()释放.
一个最小的GTK+线程程序主函数看起来象下面这样:
int
main (int argc, char *argv[])
{
GtkWidget *window;
g_thread_init(NULL);
gtk_init(&argc, &argv);
window = create_window();
gtk_widget_show(window);
gdk_threads_enter();
gtk_main();
gdk_threads_leave();
return(0);
} |
回调信号需要引起主意,GTK+中的回调信号(信号(signals))在GTK+锁定(lock)的内部,但是GLib中的回调信号(超时,IO和系统空闲(timeouts, IO
callbacks, and idle functions))在GTK+锁定(lock)之外,所以,在一个信号处理函数(signal handler)中你不需要调用gdk_threads_enter(),但是在其他类型的调用中,你需要调用gdk_threads_enter().
Erik Mouw
提供了下面的代码实例说明了如何在GTK+中使用线程:
/*-------------------------------------------------------------------------
* Filename: gtk-thread.c
* Version: 0.99.1
* Copyright: Copyright (C) 1999, Erik Mouw
* Author: Erik Mouw <[email protected]>
* Description: GTK threads example.
* Created at: Sun Oct 17 21:27:09 1999
* Modified by: Erik Mouw <[email protected]>
* Modified at: Sun Oct 24 17:21:41 1999
*-----------------------------------------------------------------------*/
/*
* Compile with:
*
* cc -o gtk-thread gtk-thread.c `pkg-config gtk+-2.0 --cflags --libs gthread`
*
* Thanks to Sebastian Wilhelmi and Owen Taylor for pointing out some
* bugs.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <pthread.h>
#define YES_IT_IS (1)
#define NO_IT_IS_NOT (0)
typedef struct
{
GtkWidget *label;
int what;
} yes_or_no_args;
G_LOCK_DEFINE_STATIC (yes_or_no);
static volatile int yes_or_no = YES_IT_IS;
void destroy(GtkWidget *widget, gpointer data)
{
gtk_main_quit();
}
void *argument_thread(void *args)
{
yes_or_no_args *data = (yes_or_no_args *)args;
gboolean say_something;
for(;;)
{
/* sleep a while */
sleep(rand() / (RAND_MAX / 3) + 1);
/* lock the yes_or_no_variable */
G_LOCK(yes_or_no);
/* do we have to say something? */
say_something = (yes_or_no != data->what);
if(say_something)
{
/* set the variable */
yes_or_no = data->what;
}
/* Unlock the yes_or_no variable */
G_UNLOCK(yes_or_no);
if(say_something)
{
/* get GTK thread lock */
gdk_threads_enter();
/* set label text */
if(data->what == YES_IT_IS)
gtk_label_set_text(GTK_LABEL(data->label), "O yes, it is!");
else
gtk_label_set_text(GTK_LABEL(data->label), "O no, it isn't!");
/* release GTK thread lock */
gdk_threads_leave();
}
}
return(NULL);
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *label;
yes_or_no_args yes_args, no_args;
pthread_t no_tid, yes_tid;
/* init threads */
g_thread_init(NULL);
/* init gtk */
gtk_init(&argc, &argv);
/* init random number generator */
srand((unsigned int)time(NULL));
/* create a window */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT (window), "destroy",
GTK_SIGNAL_FUNC(destroy), NULL);
gtk_container_set_border_width(GTK_CONTAINER (window), 10);
/* create a label */
label = gtk_label_new("And now for something completely different ...");
gtk_container_add(GTK_CONTAINER(window), label);
/* show everything */
gtk_widget_show(label);
gtk_widget_show (window);
/* create the threads */
yes_args.label = label;
yes_args.what = YES_IT_IS;
pthread_create(&yes_tid, NULL, argument_thread, &yes_args);
no_args.label = label;
no_args.what = NO_IT_IS_NOT;
pthread_create(&no_tid, NULL, argument_thread, &no_args);
/* enter the GTK main loop */
gdk_threads_enter();
gtk_main();
gdk_threads_leave();
return(0);
} |
当在GTK+的app文件中使用fork()时,为什么会出现奇怪的'x io error'错误?
这实际上并不是GTK+的问题,而且也和fork()没有关系,如果发生了'x io error',你可以使用exit()函数来从子程序中退出.
当GDK打开了一个X显示,它创建了一个插座文件描述符(socket file
descriptor),当你使用exit() 函数时,你默认的关闭了所有的打开文件描述符(open file
descriptors).但是底层(underlying)的X库(X library)并不如此.
这里应该使用的函数是exit().
Erik Mouw贡献了这个代码实例来说明如何处理
fork()和exit().
/*-------------------------------------------------------------------------
* Filename: gtk-fork.c
* Version: 0.99.1
* Copyright: Copyright (C) 1999, Erik Mouw
* Author: Erik Mouw <[email protected]>
* Description: GTK+ fork example
* Created at: Thu Sep 23 21:37:55 1999
* Modified by: Erik Mouw <[email protected]>
* Modified at: Thu Sep 23 22:39:39 1999
*-----------------------------------------------------------------------*/
/*
* Compile with:
*
* cc -o gtk-fork gtk-fork.c `pkg-config gtk+-2.0 --cflags --libs`
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <gtk/gtk.h>
void sigchld_handler(int num)
{
sigset_t set, oldset;
pid_t pid;
int status, exitstatus;
/* block other incoming SIGCHLD signals */
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, &oldset);
/* wait for child */
while((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0)
{
if(WIFEXITED(status))
{
exitstatus = WEXITSTATUS(status);
fprintf(stderr,
"Parent: child exited, pid = %d, exit status = %d\n",
(int)pid, exitstatus);
}
else if(WIFSIGNALED(status))
{
exitstatus = WTERMSIG(status);
fprintf(stderr,
"Parent: child terminated by signal %d, pid = %d\n",
exitstatus, (int)pid);
}
else if(WIFSTOPPED(status))
{
exitstatus = WSTOPSIG(status);
fprintf(stderr,
"Parent: child stopped by signal %d, pid = %d\n",
exitstatus, (int)pid);
}
else
{
fprintf(stderr,
"Parent: child exited magically, pid = %d\n",
(int)pid);
}
}
/* re-install the signal handler (some systems need this) */
signal(SIGCHLD, sigchld_handler);
/* and unblock it */
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &set, &oldset);
}
gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
return(FALSE);
}
void destroy(GtkWidget *widget, gpointer data)
{
gtk_main_quit();
}
void fork_me(GtkWidget *widget, gpointer data)
{
pid_t pid;
pid = fork();
if(pid == -1)
{
/* ouch, fork() failed */
perror("fork");
exit(-1);
}
else if(pid == 0)
{
/* child */
fprintf(stderr, "Child: pid = %d\n", (int)getpid());
execlp("ls", "ls", "-CF", "/", NULL);
/* if exec() returns, there is something wrong */
perror("execlp");
/* exit child. note the use of _exit() instead of exit() */
_exit(-1);
}
else
{
/* parent */
fprintf(stderr, "Parent: forked a child with pid = %d\n", (int)pid);
}
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *button;
gtk_init(&argc, &argv);
/* the basic stuff: make a window and set callbacks for destroy and
* delete events
*/
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT (window), "delete_event",
GTK_SIGNAL_FUNC(delete_event), NULL);
gtk_signal_connect(GTK_OBJECT (window), "destroy",
GTK_SIGNAL_FUNC(destroy), NULL);
#if (GTK_MAJOR_VERSION == 1) && (GTK_MINOR_VERSION == 0)
gtk_container_border_width(GTK_CONTAINER (window), 10);
#else
gtk_container_set_border_width(GTK_CONTAINER (window), 10);
#endif
/* add a button to do something usefull */
button = gtk_button_new_with_label("Fork me!");
gtk_signal_connect(GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC(fork_me), NULL);
gtk_container_add(GTK_CONTAINER(window), button);
/* show everything */
gtk_widget_show (button);
gtk_widget_show (window);
/* install a signal handler for SIGCHLD signals */
signal(SIGCHLD, sigchld_handler);
/* main loop */
gtk_main ();
exit(0);
} |
为什么当按钮按下时文本(contents)不移动?这里有一个补丁可以使它这样.
来自Peter Mattis:
按钮被按下时不将其内的子控件向右下方稍微移动的原因是我认为实际上的视觉效果并不是这样。我的观点是你是从按钮的正上方看它们的。也就是说,用户界面在一个平面上而你在其上面直接看下去。当按钮被按下时它直接朝远离你的方向移动。为了完全正确地表现这种情况我想其内的子控件应该实现上稍微缩小一点点。但我无法理解为什么子控件要向左下方偏移。记住,子控件是被设想为附在按钮的表面上。让子控件看起来像是在按钮的表
面滑动并不是什么好主意。
另外还有个实践提示,我有一次也实现这个,但发现这样看起来并不好然后就删掉了它。
(本段感谢川大胡正提供帮助)
如何识别一个控件的最高层窗口(top level window)或者其他的祖先?
这里有几种方法来识别最高层父窗口(top level window).最简单的方法式调用gtk_widget_top_level() 函数,他将返回一个指向一个GTK控件(GtkWidget)的指针,就是最高层父窗口.
一个更复杂的方法是这样:(这样做限制较少,它可以使用户得到一个类型(type)的最近的祖先),调用这个函数:gtk_widget_get_ancestor()
GtkWidget *widget;
widget = gtk_widget_get_ancestor(w, GTK_TYPE_WINDOW); |
事实上,既然所有的;GTK类型(GTK_TYPE)都可以作为这个函数的第二个参数,你就可以通过它得到任何一个控件的父控件,假设你有一个横向盒(hbox),包含了一个纵向盒(vbox),并依次的包含了其他的一些原子控件(atomic widget),(例如输入条(entry),
标签(label)等等),寻找主横向盒(hbox)使用下面的这个函数:
GtkWidget *hbox;
hbox = gtk_widget_get_ancestor(w, GTK_TYPE_HBOX); |
如何得到一个GtkWindow窗口的ID(Window ID)?
在控件被实现时Gdk/X窗口将被创建.
你可以这样得到窗口的ID:
#include <gdk/gdkx.h>
Window xwin = GDK_WINDOW_XWINDOW (GTK_WIDGET (my_window)->window); |
如何捕获一个双击(double click)事件(event)(例如在一个列表控件中)?
Tim Janik在gtk邮件列表(gtk-list)上写道(稍做修改):
定义一个信号处理函数:
gint
signal_handler_event(GtkWidget *widget, GdkEventButton *event, gpointer func_data)
{
if (GTK_IS_LIST_ITEM(widget) &&
(event->type==GDK_2BUTTON_PRESS ||
event->type==GDK_3BUTTON_PRESS) ) {
printf("I feel %s clicked on button %d\n",
event->type==GDK_2BUTTON_PRESS ? "double" : "triple",
event->button);
}
return FALSE;
} |
将函数和你的控件连接:
{
/* list, list item init stuff */
gtk_signal_connect(GTK_OBJECT(list_item),
"button_press_event",
GTK_SIGNAL_FUNC(signal_handler_event),
NULL);
/* and/or */
gtk_signal_connect(GTK_OBJECT(list_item),
"button_release_event",
GTK_SIGNAL_FUNC(signal_handler_event),
NULL);
/* something else */
} |
Owen Taylor 写道:
"注意:一个单击的信号将在之前被受到,如果你双击一个按钮,将会在之前收到一个单击("clicked")的信号(任何的工具都是这样,因为电脑无法读懂你的思想.)
顺便问一下,信号(signals)和事件(events)的区别是什么?
首先,Havoc Pennington在他的免费书(这里http://www106.pair.com/rhp/sample_chapters.html)中对事件和信号的区别给出了一个完整的描述.
此外,Havoc在邮件列表上写道:事件Events是从X
server收到的消息流.他们驱动着Gtk的主循环,"等待事件,然后处理它".(这还不太准确,它实际更加的复杂,它可以同时等待多个不同的输入流).事件(Event)是一个Gdk/Xlib概念.
"信号是GtkObject和它的子集(subclasses)的特性,他们和任何的输入流都没有关系.实际上,信号只是保持了一个回调函数列表,然后调用回调函数(就是发出信号),当然,还有很多其他的细节和特性.信号是对象(object)发出的,和Gtk的主循环一点关系都没有.按照常规,信号一般在对象"发生了一些改变"的时候发出.
"信号(Signals)和事件(events)有关是因为GTK控件(GtkWidget)通常收到一个事件的时候发出一个信号.这纯粹是为了方便,所以你可以在控件收到一个事件的时候调用一个回调函数.当你按下一个按钮的时候会发出一个信号,所以才让人认为信号和事件有天生的联系.
传递给delete_event(或者其他事件)处理函数的数据被损坏.
所有的事件处理函数都有一个附加的参数,包含了触发这个函数的事件的信息,所以,一个delete_event 处理函数应该这样声明:
gint delete_event_handler (GtkWidget *widget,
GdkEventAny *event,
gpointer data); |
我将信号和所有事件都连接起来,但是看起来我并没有捕获它,什么原因?
要捕获一些特殊的事件要采取一些特殊的措施.实际上,在捕获一些特殊的事件之前,你应该正确的设定控件的event mask.
例如:
gtk_widget_add_events(window, GDK_KEY_RELEASE_MASK); |
可以让你捕获键盘释放的事件.如果你想捕获所有的事件,只要使用GDK_ALL_EVENTS_MASK就行了.
所有的event mask的定义都在gdktypes.h文件中.
我要向GTK+控件中添加一个新的信号,怎么办?
如果你要加入的信号对其他的GTK+使用者也有好处,你可以提交一个补丁,请查阅指南得到更多的关于向控件中添加信号的信息.
如果你不想提交补丁或者你的补丁没有被应用,你必须使用gtk_object_class_user_signal_new 函数来实现,这个函数允许你在不修改GTK+的源代码的基础上向一个预先定义的控件中添加新的信号.可以使用gtk_signal_emit函数来发出这个新的信号,处理方式和其他的信号一样.
Tim Janik 张贴了这个程序片断:
static guint signal_user_action = 0;
signal_user_action =
gtk_object_class_user_signal_new (gtk_type_class (GTK_TYPE_WIDGET),
"user_action",
GTK_RUN_LAST | GTK_RUN_ACTION,
gtk_marshal_NONE__POINTER,
GTK_TYPE_NONE, 1,
GTK_TYPE_POINTER);
void
gtk_widget_user_action (GtkWidget *widget,
gpointer act_data)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_signal_emit (GTK_OBJECT (widget), signal_user_action, act_data);
} |
文本能否换行显示?
GTK的行为(no clipping)是它努力的节约X资源的结果.标签(Label)控件没有它自己的X窗口--他们仅仅是将内容画在父窗口上,你也可以在显示之前设置clip mask来剪裁一番,但是这会明显的导致速度的下降.
你当然可以这样做.从长远来看,最好的解决方法是让gtk给标签分配一个窗口,权宜之计是将它放入一个其他的控件之中,viewport控件是一个较好的选择.
viewport = gtk_viewport (NULL, NULL);
gtk_widget_set_usize (viewport, 50, 25);
gtk_viewport_set_shadow_type (GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
gtk_widget_show(viewport);
label = gtk_label ("a really long label that won't fit");
gtk_container_add (GTK_CONTAINER(viewport), label);
gtk_widget_show (label); |
如果你要对一大堆的控件这样操作,你可以复制gtkviewport.c文件,然后去掉调整(adjustment)和屏蔽(shadow)功能(你可以叫它GtkClipper控件).
如何设置窗口模式(modal)?/如何激活一个单一的窗口?
创建了窗口之后调用gtk_grab_add(my_window).关闭了窗口之后调用gtk_grab_remove(my_window)。
为什么我的控件(例如 进度条)没有更新?
你可能没有将控制权返回gtk_main(),你在做一些耗时较长的计算的时候,就可能出现上述的情况.所有的绘图更新都放在一个队列中,在gtk_main()中处理,你可以使用下面的方法来强迫执行:
while (g_main_iteration(FALSE)); |
这样可以更新你的控件.
上面的程序片断是运行所有的等待判断的事件和高优先级的idle函数,然后立即返回(绘图是在一个高优先级的idle函数中完成的).
如何向GTK+的对象/控件附加数据?
首先,要附加的数据存放在一个gtk对象(GtkObject)的数据域中,这个域的类型是GData,GData在glib.h文件中有定义.所以你应该仔细的阅读你的glib文件夹下面的gdataset.c文件.
这里还有两种比较简单的方法:使用gtk_object_set_data()
和 gtk_object_get_data() 是最常见的.因为它提供了一个将对象和数据连续起来的强有力的手段.
void gtk_object_set_data(GtkObject *object, const gchar *key, gpointer data);
gpointer gtk_object_get_data(GtkObject *object, const gchar *key); |
一个简短的实例往往比长篇大论来得更有效:
struct my_struct p1,p2,*result;
GtkWidget *w;
gtk_object_set_data(GTK_OBJECT(w),"p1 data",(gpointer)&p1);
gtk_object_set_data(GTK_OBJECT(w),"p2 data",(gpointer)&p2);
result = gtk_object_get_data(GTK_OBJECT(w),"p1 data"); |
gtk_object_set_user_data()
和 gtk_object_get_user_data() 的行为和上面的两个函数一摸一样,但是不让你指定“键”参数,相反,它使用了标准的“用户数据”键(key)。注意:这样使用函数并不推荐,只是提供了对一些较旧的GTK包的兼容。
如何去掉附加在控件上的数据?
当你向对象中附加数据的时候,你可以使用gtk_object_set_data_full() 函数.前面的三个参数和gtk_object_set_data()一样,第四个参数是一个指向一个回调函数的指针,当数据销毁的时候将调用这个回调函数.下列情况你的数据将被销毁:
如何重定控件的父窗口?
常规的做法是调用下面的这个函数:
void gtk_widget_reparent (GtkWidget *widget,
GtkWidget *new_parent) |
但是这只是一个常规的做法,因为对有些特殊的控件这个函数不能正确执行.如果控件和它的新的父窗口都存在(在这种情况下,widget->window将会被正确的替代),gtk_widget_reparent()函数的主要的目的是为了避免出现不合理的控件.这里的问题是:在GTK+的控件层次结构中,一些控件和X的子窗口有多重的联系,显著的例子就是GtkSpinButton控件.在这种情况下,gtk_widget_reparent()函数不能正确的执行.
避免这样的情况,使用下面的代码段:
gtk_widget_ref(widget);
gtk_container_remove(GTK_CONTAINER(old_parent), widget);
gtk_container_add(GTK_CONTAINER(new_parent), widget);
gtk_widget_unref(widget); |
如何得到控件的位置?
正如Tim Janik指出的,不同的情况对应有不同的解决的办法.
-
如果你想得到一个控件相对于它的父窗口的位置,你需要使用widget->allocation.x
和 widget->allocation.y.
-
如果你想的到一个窗口相对于X主窗口(X root
window)的位置,你应该使用gdk_window_get_geometry() gdk_window_get_position() 或者 gdk_window_get_origin().
-
如果你想得到一个窗口(包括WM修饰的窗口)的位置,应该使用gdk_window_get_root_origin().
-
最后但不是不重要,如果你想得到窗口管理器的位置,你应该使用gdk_window_get_deskrelative_origin().
你选择的窗口管理器(Window Manager)将可能影响上面函数的结果,在写你的程序的时候,你应该记住这一点.这主要是决定于窗口管理器如何管理对窗口添加的修饰.
如何设置控件/窗口的大小?如何才能禁止用户重新设置窗口的大小?
gtk_widget_set_uposition()函数用来设定任何控件的位置.
gtk_widget_set_usize()
函数用来设定一个控件的大小.当它作用在一个窗口上时,为了使用这个函数所提供的所有的特性,你可能会使用下面的这个函数:gtk_window_set_policy,他们的定义如下:
void gtk_widget_set_usize (GtkWidget *widget,
gint width,
gint height);
void gtk_window_set_policy (GtkWindow *window,
gint allow_shrink,
gint allow_grow,
gint auto_shrink); |
当子控件要求的大小和当前的窗口的大小不适应的时候,自动收缩(auto_shrink)将自动的收缩窗口.allow_shrink
授权用户把窗口变得比正常小,allow_grow授权用户把窗口变得比正常大,这三个参数的缺省值是:
allow_shrink = FALSE
allow_grow = TRUE
auto_shrink = FALSE |
gtk_widget_set_usize()并不是设置窗口大小的最方便的手段,你不能通过另一个调用来使一个窗口变小,除非你调用它两次,就象这样:
gtk_widget_set_usize(your_widget, -1, -1);
gtk_widget_set_usize(your_widget, new_x_size, new_y_size); |
另外的一条途径来设置窗口的大小或者移动窗口是调用gdk_window_move_resize(),可以放大或者缩小窗口.
gdk_window_move_resize(window->window,
x_pos, y_pos,
x_size, y_size); |
如何向我的GTK+程序中添加一个弹出式菜单?
在GTK+ distribution发布中的examples/menu
目录下面,通过下面的技巧实现了一个弹出式菜单:
static gint button_press (GtkWidget *widget, GdkEvent *event)
{
if (event->type == GDK_BUTTON_PRESS) {
GdkEventButton *bevent = (GdkEventButton *) event;
gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
bevent->button, bevent->time);
/* Tell calling code that we have handled this event; the buck
* stops here. */
return TRUE;
}
/* Tell calling code that we have not handled this event; pass it on. */
return FALSE;
} |
如何禁止或使能一个控件,例如一个按钮?
禁用(或者使能)一个控件,使用gtk_widget_set_sensitive()函数,第一个参数是指向你的控件的指针,第二个参数一个布尔值:当值是TRUE时,这个控件使能.
为什么gtk_clist*函数中的文本参数(text argument)不应被声明为const?
例如:
gint gtk_clist_prepend (GtkCList *clist,
gchar *text[]); |
回答:一个"gchar*"类型(指向char的指针)将被自动的转换成为"const gchar*"(指向const
char的指针),但是这并不适合将"gchar *[]"(gchar指针数组)转换成为"const gchar *[]"(const
gchar指针数组)类型.
类型修饰符"const"可以自动转换,但是对于数组的情况,并不是数组本身需要转换,它的数组成员也需要转换.
如何在屏幕上显示象素(图像数据)?
有多种的方法来实现.最简单的方法时使用GdkRGB,请参看gdk/gdkrgb.h.你可以开出一块RGB缓冲区(buffer),然后将图像载入,然后用GdkRGB将你的RGB缓冲区(buffer)copy到一块绘图区或者目标控件中."GTK+/Gnome Application Development"
这本书中给出了更多的细节, GTK+ reference documentation中也有相关的文档.
如果你在编写一个游戏或者其他的对图像要求很高的程序,你应该考虑使用一个更加精细的方案,OpenGL是图像处理标准,可以使你在新版的XFree86中使用硬件加速,达到最快的速度,这可能会吸引你的注意.GtkGLArea控件可以使你在GTK+中使用OpenGL(但是GtkGLArea并不和GTK+一起发行),还有其他的几个开源(open source)游戏库,比如ClanLib
和 Loki编写的 Simple DirectMedia Layer library (SDL)
不要使用gdk_draw_point(),因为它实在太慢.
象gdk_pixmap_create_from_xpm()这样的函数需要一个空的窗口作为参数,在一个程序的初始化阶段,窗口没有显示,空的窗口就不可用,这会导致一些问题.为了避免这样的问题,gdk_pixmap_colormap_create_from_xpm可以派上用场,象这样:
char *pixfile = "foo.xpm";
GtkWidget *top, *box, *pixw;
GdkPixmap *pixmap, *pixmap_mask;
top = gtk_window_new (GKT_WINDOW_TOPLEVEL);
box = gtk_hbox_new (FALSE, 4);
gtk_conainer_add (GTK_CONTAINER(top), box);
pixmap = gdk_pixmap_colormap_create_from_xpm (
NULL, gtk_widget_get_colormap(top),
&pixmap_mask, NULL, pixfile);
pixw = gtk_pixmap_new (pixmap, pixmap_mask);
gdk_pixmap_unref (pixmap);
gdk_pixmap_unref (pixmap_mask); |
如何拖放?
GTK+通过拖放系统(drag-and-drop
system)提供了一组高端的函数集来实现进程间通信.GTK+可以在底层的Xdnd和Motif
拖放协议(drag-and-drop protocols)上实现拖放功能.
关于GTK+拖放的文档并不完整,但是在 Tutorial有一些信息.你也可以在gtk/testdnd.c文件中看到拖放的实例代码.
为什么GTK+/GLib会泄漏内存?
并不是这样,GLib和C library(执行malloc)有时会对分配的内存缓冲,即使你使用free()来释放.
所以你不应该象上面那样使用一些工具来看你是否正确使用free()(除了最粗略的估计之外,比如你十分确定会出现上面的内存泄漏,但是你无法区别是错误还是GLib/malloc缓冲)
找出内存泄漏(memory leaks),请使用合适的内存管理工具.
在GtkList控件中如何找出选择项(selection)?
这样可以得到选择项:
GList *sel;
sel = GTK_LIST(list)->selection; |
这是GList如何定义的(引用自glist.h)
typedef struct _GList GList;
struct _GList
{
gpointer data;
GList *next;
GList *prev;
}; |
一个GList结构是一个用于双向链表(doubly linked
list)的简单结构体.在glib.h有几个g_list_*()这样的函数来修改链表.但是GTK_LIST(MyGtkList)->selection是由gtk_list_*()函数维护的,而且不应被修改.
GtkList的选择模式(selection_mode)决定了一个GtkList控件的选择功能,所以也决定了GTK_LIST(AnyGtkList)->selection:
{
gchar *list_items[]={
"Item0",
"Item1",
"foo",
"last Item",
};
guint nlist_items=sizeof(list_items)/sizeof(list_items[0]);
GtkWidget *list_item;
guint i;
list=gtk_list_new();
gtk_list_set_selection_mode(GTK_LIST(list), GTK_SELECTION_MULTIPLE);
gtk_container_add(GTK_CONTAINER(AnyGtkContainer), list);
gtk_widget_show (list);
for (i = 0; i < nlist_items; i++)
{
list_item=gtk_list_item_new_with_label(list_items[i]);
gtk_object_set_user_data(GTK_OBJECT(list_item), (gpointer)i);
gtk_container_add(GTK_CONTAINER(list), list_item);
gtk_widget_show(list_item);
}
} |
得到选择项:
{
GList *items;
items=GTK_LIST(list)->selection;
printf("Selected Items: ");
while (items) {
if (GTK_IS_LIST_ITEM(items->data))
printf("%d ", (guint)
gtk_object_get_user_data(items->data));
items=items->next;
}
printf("\n");
} |
在GtkCList控件中,当表内容滚动时,怎样避免栏目消失?
在GtkScrolledWindow中添加一个 GtkCList,使用gtk_scroll_window_add_with_viewport()函数即可,向一个scrolled window中添加CList控件较好的办法是使用gtk_container_add函数,象这样:
GtkWidget *scrolled, *clist;
char *titles[] = { "Title1" , "Title2" };
scrolled = gtk_scrolled_window_new(NULL, NULL);
clist = gtk_clist_new_with_titles(2, titles);
gtk_container_add(GTK_CONTAINER(scrolled), clist); |
我不想让我的程序的用户在组合框(GtkCombo)中输入文本,怎么办?
GtkCombo控件有一个关联的文本输入变量(entry),可以这样访问:
GTK_COMBO(combo_widget)->entry |
如果你不想用户修改这个文本输入变量(entry)的内容,你可以使用gtk_entry_set_editable()函数:
void gtk_entry_set_editable(GtkEntry *entry,
gboolean editable); |
将editable变量设置为FALSE可以使用户不能输入.
如何在菜单中定义分隔线(separation line)?
参看Tutorial关于如何创建菜单.创建分隔线只需要创建一个空的菜单项:
menuitem = gtk_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_show(menuitem); |
如何向右对齐菜单,比如Help?
取决于你是否使用了MenuFactory,有不同的方法来实现:
使用MenuFactory,这样做:
menu_path = gtk_menu_factory_find (factory, "<MyApp>/Help");
gtk_menu_item_right_justify(menu_path->widget); |
如果没有使用MenuFactory,这样做:
gtk_menu_item_right_justify(my_menu_item); |
如何在菜单项中添加带下滑线的加速键?
Damon Chaplin-- Glade项目的技术中坚,提供了下面的代码例子(这个代码是Glade输出的):它的中只定义了一个选项(), 中的F和中的N都加了下划线,并创建了相关的加速键.
menubar1 = gtk_menu_bar_new ();
gtk_object_set_data (GTK_OBJECT (window1), "menubar1", menubar1);
gtk_widget_show (menubar1);
gtk_box_pack_start (GTK_BOX (vbox1), menubar1, FALSE, FALSE, 0);
file1 = gtk_menu_item_new_with_label ("");
tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (file1)->child),
_("_File"));
gtk_widget_add_accelerator (file1, "activate_item", accel_group,
tmp_key, GDK_MOD1_MASK, 0);
gtk_object_set_data (GTK_OBJECT (window1), "file1", file1);
gtk_widget_show (file1);
gtk_container_add (GTK_CONTAINER (menubar1), file1);
file1_menu = gtk_menu_new ();
file1_menu_accels = gtk_menu_ensure_uline_accel_group (GTK_MENU (file1_menu));
gtk_object_set_data (GTK_OBJECT (window1), "file1_menu", file1_menu);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (file1), file1_menu);
new1 = gtk_menu_item_new_with_label ("");
tmp_key = gtk_label_parse_uline (GTK_LABEL (GTK_BIN (new1)->child),
_("_New"));
gtk_widget_add_accelerator (new1, "activate_item", file1_menu_accels,
tmp_key, 0, 0);
gtk_object_set_data (GTK_OBJECT (window1), "new1", new1);
gtk_widget_show (new1);
gtk_container_add (GTK_CONTAINER (file1_menu), new1); |
如何从GtkMenuItem中重新找回文本?
你可以这样从一个特定的GtkMenuItem中重新得到标签.
if (GTK_BIN (menu_item)->child)
{
GtkWidget *child = GTK_BIN (menu_item)->child;
/* do stuff with child */
if (GTK_IS_LABEL (child))
{
gchar *text;
gtk_label_get (GTK_LABEL (child), &text);
g_print ("menu item text: %s\n", text);
}
} |
从一个GtkOptionMenu中得到活动的菜单选项:
if (GTK_OPTION_MENU (option_menu)->menu_item)
{
GtkWidget *menu_item = GTK_OPTION_MENU (option_menu)->menu_item;
} |
但是,这里有一个例外,在这种特殊的情况下,你不能用上面的代码从menu_item
中得到标签控件.因为选项菜单(option menu)临时调整了菜单选项和子菜单的层次关系来显示当前活动的条目.所以你想得到一个选项菜单(option
menu)当前活动的菜单选项的子菜单,应这样做:
if (GTK_BIN (option_menu)->child)
{
GtkWidget *child = GTK_BIN (option_menu)->child;
/* do stuff with child */
} |
如何向右(或其他方向)对齐GtkLabel?
你确定你要调整标签?菜单类提供了gtk_label_set_justify()
函数来对一个多行的标签进行调整
最重要的是设置对齐方式(alignment),比如:向右对齐,居中或者向左对齐,如果你想调整它,你应该这样做:
void gtk_misc_set_alignment (GtkMisc *misc,
gfloat xalign,
gfloat yalign); |
xalign 和 yalign的值为[0.00;1.00]之间的一个浮点数.
GtkWidget *label;
/* horizontal : left align, vertical : top */
gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.0f);
/* horizontal : centered, vertical : centered */
gtk_misc_set_alignment(GTK_MISC(label), 0.5f, 0.5f);
/* horizontal : right align, vertical : bottom */
gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 1.0f); |
如何给GtkLabel控件设置背景颜色?
GtkLabel是少数的几个不创建自己的窗口的GTK+控件之一.相反,它直接的将自己画在父控件上.这就是说:如果你想为GtkLabel控件设置背景,你需要改变它的父控件的的背景,比如,你将标签填充的控件.
如何使用Resource 文件来给GtkLabel设置颜色和字体?
一个标签的控件名路径最好由控件的对象层次名称构成.比如:
window (name:
humphrey) hbox label
(name: mylabel)
你应匹配的控件路径为:
humphrey.GtkHBox.mylabel
resource文件看起来象这样:
style "title"
{
fg[NORMAL] = {1.0, 0.0, 0.0}
font = "-adobe-helvetica-bold-r-normal--*-140-*-*-*-*-*-*"
}
widget "*mylabel" style "title" |
在你的程序中,你还是需要对Label控件取一个名字,可以这样:
label = gtk_label_new("Some Label Text");
gtk_widget_set_name(label, "mylabel");
gtk_widget_show(label); |
如何在Resource 文件中配置工具提示(Tooltips)?
工具提示窗口名为:"gtk-tooltips",GtkTooltips本身并不是一个GTK控件(GtkWidget
),同样的,它不需要对应任何的控件类型(widget styles).
所以,你的resource文件应该这样:
style "postie"
{
bg[NORMAL] = {1.0, 1.0, 0.0}
}
widget "gtk-tooltips*" style "postie" |
无法在GtkEntry中输入大于2000(大约)个字符,什么原因?
在GtkEntry
控件中有一个大家都知道的问题,在gtk_entry_insert_text() 函数中,下面的程序段将字符的数量限制为2047.
/* The algorithms here will work as long as, the text size (a
* multiple of 2), fits into a guint16 but we specify a shorter
* maximum length so that if the user pastes a very long text, there
* is not a long hang from the slow X_LOCALE functions. */
if (entry->text_max_length == 0)
max_length = 2047;
else
max_length = MIN (2047, entry->text_max_length); |
如何能在按下回车时使GtkEntry控件激活?
当按回车时,Entry控件将发出一个'activate'信号,你只需和这个信号相连,然后做你想做的事.典型的代码是这样:
entry = gtk_entry_new();
gtk_signal_connect (GTK_OBJECT(entry), "activate",
GTK_SIGNAL_FUNC(entry_callback),
NULL); |
如何确认/限制/筛选GtkEntry中的输入?
如果你想确认用户在GtkEntry控件中输入的文本,你可以连接"insert_text"信号,在回调函数中修改文本.下面的代码要求所有的字符为大写,并且字符的范围为A-Z,注意:当GtkEntry定义时,entry被转换成了GtkEditable类型的一个对象,
#include <ctype.h>
#include <gtk/gtk.h>
void insert_text_handler (GtkEntry *entry,
const gchar *text,
gint length,
gint *position,
gpointer data)
{
GtkEditable *editable = GTK_EDITABLE(entry);
int i, count=0;
gchar *result = g_new (gchar, length);
for (i=0; i < length; i++) {
if (!isalpha(text[i]))
continue;
result[count++] = islower(text[i]) ? toupper(text[i]) : text[i];
}
if (count > 0) {
gtk_signal_handler_block_by_func (GTK_OBJECT (editable),
GTK_SIGNAL_FUNC (insert_text_handler),
data);
gtk_editable_insert_text (editable, result, count, position);
gtk_signal_handler_unblock_by_func (GTK_OBJECT (editable),
GTK_SIGNAL_FUNC (insert_text_handler),
data);
}
gtk_signal_emit_stop_by_name (GTK_OBJECT (editable), "insert_text");
g_free (result);
}
int main (int argc,
char *argv[])
{
GtkWidget *window;
GtkWidget *entry;
gtk_init (&argc, &argv);
/* create a new window */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW (window), "GTK Entry");
gtk_signal_connect(GTK_OBJECT (window), "delete_event",
(GtkSignalFunc) gtk_exit, NULL);
entry = gtk_entry_new();
gtk_signal_connect(GTK_OBJECT(entry), "insert_text",
GTK_SIGNAL_FUNC(insert_text_handler),
NULL);
gtk_container_add(GTK_CONTAINER (window), entry);
gtk_widget_show(entry);
gtk_widget_show(window);
gtk_main();
return(0);
} |
如何在GtkText控件中使用水平滚动条(horizontal scrollbars)?
回答是你不能这样做,目前的版本的GtkText控件不支持使用水平滚动,现在有完全重写GtkText控件的计划,到那时这个限制就被取消了.
如何改变GtkText控件的字体?
有好几种方法可以这样做.因为GTK+允许在程序运行的时候通过改变资源文件来改变程序的外观,你可以这样做:
style "text"
{
font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
} |
另外的一个方法是在你的程序中加载一个字体,然后在向text
控件中添加文本的函数中应用,例如:
GdkFont *font;
font = gdk_font_load("-adobe-helvetica-medium-r-normal--*-140-*-*-*-*-*-*"); |
如何在GtkText控件中设定光标位置(cursor position)?
注意:下面的回答对所有的从GtkEditable类中继承的对象都是有效的.
确信你要移动光标的位置?很多时候,当光标的位置合适时,插入点并不对应光标的位置.如果这正是你想要的效果,你应该使用gtk_text_set_point()
函数,如果你想在光标的位置设置插入点,这样做:
gtk_text_set_point(GTK_TEXT(text),
gtk_editable_get_position(GTK_EDITABLE(text))); |
如果你想插入点一直都跟随着光标,你应该捕获按钮按下事件(button press
event),然后移动插入点.小心:当控件改变光标的位置之后,你必须捕获这个事件.
建议使用下面的代码:
static void
insert_bar (GtkWidget *text)
{
/* jump to cursor mark */
gtk_text_set_point (GTK_TEXT (text),
gtk_editable_get_position (GTK_EDITABLE (text)));
gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
"bar", strlen ("bar"));
}
int
main (int argc, char *argv[])
{
GtkWidget *window, *text;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
text = gtk_text_new (NULL, NULL);
gtk_text_set_editable (GTK_TEXT (text), TRUE);
gtk_container_add (GTK_CONTAINER (window), text);
/* connect after everything else */
gtk_signal_connect_after (GTK_OBJECT(text), "button_press_event",
GTK_SIGNAL_FUNC (insert_bar), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
} |
now,如果你想改变光标的位置,你应该可以使用gtk_editable_set_position()函数了.
什么是GDK?
GDK是标准Xlib函数调用的一个基本封装(wrapper),如果你对Xlib很熟悉,就不需要来重新熟悉绝大多数的GDK函数.所有的函数都是为了提供一个方便直观的风格来访问Xlib函数库.另外,自从GDK使用GLib
,在多平台上使用时,GDK变得更加的方便和安全.
如何使用颜色分配?
有关GDK的一个很好的特性是它建立在Xlib之上,但这也是一个问题,特别是在颜色管理方面,如果你想在程序中使用颜色(例如画一个矩形等),你的代码可能象这样:
{
GdkColor *color;
int width, height;
GtkWidget *widget;
GdkGC *gc;
...
/* first, create a GC to draw on */
gc = gdk_gc_new(widget->window);
/* find proper dimensions for rectangle */
gdk_window_get_size(widget->window, &width, &height);
/* the color we want to use */
color = (GdkColor *)malloc(sizeof(GdkColor));
/* red, green, and blue are passed values, indicating the RGB triple
* of the color we want to draw. Note that the values of the RGB components
* within the GdkColor are taken from 0 to 65535, not 0 to 255.
*/
color->red = red * (65535/255);
color->green = green * (65535/255);
color->blue = blue * (65535/255);
/* the pixel value indicates the index in the colormap of the color.
* it is simply a combination of the RGB values we set earlier
*/
color->pixel = (gulong)(red*65536 + green*256 + blue);
/* However, the pixel valule is only truly valid on 24-bit (TrueColor)
* displays. Therefore, this call is required so that GDK and X can
* give us the closest color available in the colormap
*/
gdk_color_alloc(gtk_widget_get_colormap(widget), color);
/* set the foreground to our color */
gdk_gc_set_foreground(gc, color);
/* draw the rectangle */
gdk_draw_rectangle(widget->window, gc, 1, 0, 0, width, height);
...
} |
什么是GLib?
GLib库提供了一系列函数(functions)和定义(definitions),在设计GDK和GTK程序时很有用.它提供了一些标准c函数库(standard libc)的替代函数,比如malloc,但在其他的系统上使用时有很多问题.
它还提供一些常用工具:
- Doubly Linked Lists双向链表
- Singly Linked Lists单向链表
- Timers计时器
- String Handling串处理
- A Lexical Scanner语法扫描器
- Error Functions错误处理函数
如何使用双向链表( doubly linked lists)?
GList对象是这样定义的:
typedef struct _GList GList;
struct _GList
{
gpointer data;
GList *next;
GList *prev;
}; |
使用GList对象,只需:
GList *list = NULL;
GList *listrunner;
gint array[] = { 1, 2, 3, 4, 5, 6 };
gint pos;
gint *value;
/* add data to the list */
for (pos=0;pos < sizeof array; pos++) {
list = g_list_append(list, (gpointer)&array[pos]);
}
/* run through the list */
listrunner = g_list_first(list);
while (listrunner) {
value = (gint *)listrunner->data;
printf("%d\n", *value);
listrunner = g_list_next(listrunner);
}
/* removing datas from the list */
listrunner = g_list_first(list);
list = g_list_remove_link(list, listrunner);
list = g_list_remove(list, &array[4]); |
对单向链表(GSList 对象)上面的代码也适用,只需用相应的g_slist_*函数(g_slist_append, g_slist_remove,
...)来代替g_list_*函数.请记住:单向链表中不能反向(go backward),这里没有g_slist_first函数,你应该保留一个链表第一个节点的引用(reference).
当我释放分配的链表节点时,内存似乎没有被释放.
在这个特殊的问题上,GLib努力的显得"智能化":它假设你将再次使用这个对象,所以分缓冲分配了的内存.如果你不想使用这个功能,你应该使用一个特殊的allocator.
引用自Tim Janik:
"如果你某些部分的代码使用了很多GLists和GNodes,然后全部的释放他们,使用GAllocator.将一个allocator压入一个双向链表(g_list)中,将会导致所有后来的对双向链表(glist)的操作对allocator来说都是私有的(private)(所以你必须在做任何的外部调用之前小心的将allocator弹出,)
GAllocator *allocator;
GList *list = NULL;
guint i;
/* set a new allocation pool for GList nodes */
allocator = g_allocator_new ("list heap", 1024);
g_list_push_allocator (allocator);
/* do some list operations */
for (i = 0; i < 4096; i++)
list = g_list_prepend (list, NULL);
list = g_list_reverse (list);
/* beware to pop allocator befor calling external functions */
g_list_pop_allocator ();
gtk_label_set_text (GTK_LABEL (some_label), "some text");
/* and set our private glist pool again */
g_list_push_allocator (allocator);
/* do some list operations */
g_list_free (list);
list = NULL;
for (i = 0; i < 4096; i++)
list = g_list_prepend (list, NULL);
/* and back out (while freeing all of the list nodes in our pool) */
g_list_pop_allocator ();
g_allocator_free (allocator); |
为什么使用 g_print, g_malloc, g_strdup和其他的glib函数?
感谢Tim Janik在gtk邮件列表gtk-list上写道:(稍有改动)
"关于g_malloc(), g_free()
和 siblings,这些函数都比直接调用libc中对应的函数安全,例如,
g_free()的值为NULL时,直接返回,同样的,如果定义了USE_DMALLOC,这个函数的定义(glib.h文件中)将变成使用MALLOC(), FREE()等等.如果定义了MEM_PROFILE
或者 MEM_CHECK,它甚至会计算使用块(block)大小(通过g_mem_profile()/g_mem_check()函数).
"glib提供了一个界面(interface)来减少存储器使用量,如果你有很多的大小相同的块(block),假设被定义为ALLOC_ONLY,它会直接创建较小的一块(可调整的(debug
able)),并将一般的malloc/free封装起来--就像gdk封装XLib一样.
在象GIMP这样的完全依赖gtk的软件中.使用g_error()及g_warning()可以在你自己的gtk窗口中弹出一个警告信息窗口,并可以将它和你的信号处理函数相连(通过使用g_set_error_handler()及gtk_print()(在gtkmain.c文件中)
什么是扫描器(GScanner),如何使用?
一个扫描器(GScanner)能够符号化(tokenize)文本.就是说:它将会对输入流中的每一个字符和数字返回一个整数(integer),当然是根据一定的规则(可由客户定制)来实现这种转换.但是你还是需要根据自己的需要来编写分析函数(parsing functions).
这是由Tim Janik提供的一个小测试程序,将会这样符号化:
<SYMBOL> =
<OPTIONAL-MINUS> <NUMBER> ;
跳过 "#\n" 和"/**/" 形式的注释.
#include <glib.h>
/* some test text to be fed into the scanner */
static const gchar *test_text =
( "ping = 5;\n"
"/* slide in some \n"
" * comments, just for the\n"
" * fun of it \n"
" */\n"
"pong = -6; \n"
"\n"
"# the next value is a float\n"
"zonk = 0.7;\n"
"# redefine ping\n"
"ping = - 0.5;\n" );
/* define enumeration values to be returned for specific symbols */
enum {
SYMBOL_PING = G_TOKEN_LAST + 1,
SYMBOL_PONG = G_TOKEN_LAST + 2,
SYMBOL_ZONK = G_TOKEN_LAST + 3
};
/* symbol array */
static const struct {
gchar *symbol_name;
guint symbol_token;
} symbols[] = {
{ "ping", SYMBOL_PING, },
{ "pong", SYMBOL_PONG, },
{ "zonk", SYMBOL_ZONK, },
{ NULL, 0, },
}, *symbol_p = symbols;
static gfloat ping = 0;
static gfloat pong = 0;
static gfloat zonk = 0;
static guint
parse_symbol (GScanner *scanner)
{
guint symbol;
gboolean negate = FALSE;
/* expect a valid symbol */
g_scanner_get_next_token (scanner);
symbol = scanner->token;
if (symbol < SYMBOL_PING ||
symbol > SYMBOL_ZONK)
return G_TOKEN_SYMBOL;
/* expect '=' */
g_scanner_get_next_token (scanner);
if (scanner->token != '=')
return '=';
/* feature optional '-' */
g_scanner_peek_next_token (scanner);
if (scanner->next_token == '-')
{
g_scanner_get_next_token (scanner);
negate = !negate;
}
/* expect a float (ints are converted to floats on the fly) */
g_scanner_get_next_token (scanner);
if (scanner->token != G_TOKEN_FLOAT)
return G_TOKEN_FLOAT;
/* make sure the next token is a ';' */
if (g_scanner_peek_next_token (scanner) != ';')
{
/* not so, eat up the non-semicolon and error out */
g_scanner_get_next_token (scanner);
return ';';
}
/* assign value, eat the semicolon and exit successfully */
switch (symbol)
{
case SYMBOL_PING:
ping = negate ? - scanner->value.v_float : scanner->value.v_float;
break;
case SYMBOL_PONG:
pong = negate ? - scanner->value.v_float : scanner->value.v_float;
break;
case SYMBOL_ZONK:
zonk = negate ? - scanner->value.v_float : scanner->value.v_float;
break;
}
g_scanner_get_next_token (scanner);
return G_TOKEN_NONE;
}
int
main (int argc, char *argv[])
{
GScanner *scanner;
guint expected_token;
scanner = g_scanner_new (NULL);
/* adjust lexing behaviour to suit our needs
*/
/* convert non-floats (octal values, hex values...) to G_TOKEN_INT */
scanner->config->numbers_2_int = TRUE;
/* convert G_TOKEN_INT to G_TOKEN_FLOAT */
scanner->config->int_2_float = TRUE;
/* don't return G_TOKEN_SYMBOL, but the symbol's value */
scanner->config->symbol_2_token = TRUE;
/* load symbols into the scanner */
while (symbol_p->symbol_name)
{
g_scanner_add_symbol (scanner,
symbol_p->symbol_name,
GINT_TO_POINTER (symbol_p->symbol_token));
symbol_p++;
}
/* feed in the text */
g_scanner_input_text (scanner, test_text, strlen (test_text));
/* give the error handler an idea on how the input is named */
scanner->input_name = "test text";
/* scanning loop, we parse the input until its end is reached,
* the scanner encountered a lexing error, or our sub routine came
* across invalid syntax
*/
do
{
expected_token = parse_symbol (scanner);
g_scanner_peek_next_token (scanner);
}
while (expected_token == G_TOKEN_NONE &&
scanner->next_token != G_TOKEN_EOF &&
scanner->next_token != G_TOKEN_ERROR);
/* give an error message upon syntax errors */
if (expected_token != G_TOKEN_NONE)
g_scanner_unexp_token (scanner, expected_token, NULL, "symbol", NULL, NULL, TRUE);
/* finsish parsing */
g_scanner_destroy (scanner);
/* print results */
g_print ("ping: %f\n", ping);
g_print ("pong: %f\n", pong);
g_print ("zonk: %f\n", zonk);
return 0;
} |
你需要理解扫描器将会分析输入然后符号化,取决于你如何翻译这些符号,在被分析之前请不要定义它们的类型.例如分析一个字符串:
"hi i am 17" | | | | | | | v | | v TOKEN_INT, value: 17 | v
TOKEN_IDENTIFIER, value: "am" v TOKEN_CHAR,
value: 'i' TOKEN_IDENTIFIER, value:
"hi"
如果你这样配置扫描器:
scanner->config->int_2_float = TRUE;
scanner->config->char_2_token = TRUE;
scanner->config->scan_symbols = TRUE; |
然后将"am"作为一个符号加入:
g_scanner_add_symbol (scanner, "am", "symbol value"); |
扫描器将会这样分析:
"hi i am 17" | | | | | | | v | | v TOKEN_FLOAT, value: 17.0 (automatic int->float
conversion) | | TOKEN_SYMBOL, value: "symbol
value" (a successfull hash table lookup | |
turned a TOKEN_IDENTIFIER into a | |
TOKEN_SYMBOL and took over the | v symbol's
value) v 'i' ('i' can be a valid token as well,
as all chars >0 and <256) TOKEN_IDENTIFIER, value: "hi"
你需要自己编程来匹配这些符号序列(token sequence),如果你遇到一些不想要的东西,可以作为错误输出:
/* expect an identifier ("hi") */
g_scanner_get_next_token (scanner);
if (scanner->token != G_TOKEN_IDENTIFIER)
return G_TOKEN_IDENTIFIER;
/* expect a token 'i' */
g_scanner_get_next_token (scanner);
if (scanner->token != 'i')
return 'i';
/* expect a symbol ("am") */
g_scanner_get_next_token (scanner);
if (scanner->token != G_TOKEN_SYMBOL)
return G_TOKEN_SYMBOL;
/* expect a float (17.0) */
g_scanner_get_next_token (scanner);
if (scanner->token != G_TOKEN_FLOAT)
return G_TOKEN_FLOAT; |
如果你这样做,可以分析"hi i am 17","dooh i am 42"和
"bah i am 0.75"也能接受,但是"hi 7 am 17"和 "hi i hi 17"就不能接受.
第九章 GTK+ FAQ
投稿,维护者和版权
如果你想向FAQ投稿,请向我们其中之一发一封电子邮件.准确的写上内容(问题和回答),有了你的努力,这份文档会变得更加的有用.
这份文档由这些人维护:
Tony Gale mailto:[email protected] Nathan Froyd mailto:[email protected], 和 Emmanuel Deloget mailto:[email protected]. 这份FAQ
由Shawn T. Amundson mailto:[email protected] 编写并继续提供支持.
投稿应该寄到 Tony Gale mailto:[email protected].
The GTK+ FAQ is Copyright (C) 1997-2000 by Shawn T. Amundson, Tony
Gale, Emmanuel Deloget and Nathan Froyd.
Permission is granted to make and distribute verbatim copies of this
manual provided the copyright notice and this permission notice are
preserved on all copies.
Permission is granted to copy and distribute modified versions of this
document under the conditions for verbatim copying, provided that this
copyright notice is included exactly as in the original, and that the
entire resulting derived work is distributed under the terms of a
permission notice identical to this one.
Permission is granted to copy and distribute translations of this
document into another language, under the above conditions for modified
versions.
If you are intending to incorporate this document into a published
work, please contact one of the maintainers, and we will make an effort to
ensure that you have the most up to date information available.
There is no guarentee that this document lives up to its intended
purpose. This is simply provided as a free resource. As such, the authors
and maintainers of the information provided within can not make any
guarentee that the information is even
accurate. |