天天看點

GTK+ FAQ  GTK+ FAQ 第二章 GTK+如何得到 配置 安裝和查錯 第三章 GTK+的開發和維護 第四章 . 用GTK+開發:開始 第五章 . 用GTK+開發: 正常問題 第六章:用GTK+開發:控件專題 第七章 關于GDK 第八章 關于GLib 第九章 GTK+ FAQ 投稿,維護者和版權

  GTK+ FAQ

Tony Gale

Shawn Amundson

Emmanuel Deloget

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 投稿,維護者和版權(英文版)

第一章 總說明

目錄表

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(線上聊天系統)的用戶端如何?

1.1. 注意:本文檔主要是關于GTK+1.2

注意:本文檔主要是關于GTK+1.2

本FAQ主要包含的是 GTK+ 1.2,在文中的包含 GTK+ 2.x的部分将用[GTK+ 2.x] 提示.

1.2. 緻謝

緻謝

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 :)

1.3. 作者

作者

GTK+的原始的作者是
  • Peter Mattis
  • Spencer Kimball
  • Josh MacDonald
以後,其他的人加了很多的東西進去,請參GTK釋出版的AUTHORS檔案.

1.4. 什麼是GTK+?

什麼是 GTK+?

GTK+是一個小型而高效的控件庫,具有Motif的外觀和風格.實際上,它比Motif看起來好多了,它包含有基本的控件和一些很複雜的的控件:例如檔案選擇控件和顔色選擇控件.

GTK+提供了一些獨特的特性,(至少,我知道其他的控件庫不提供他們),例如,按鈕不提供标簽,它包含了一個子控件,在很多的時候是一個标簽,但是,這個子控件也可以是一個映射,圖像或者任何其他的程式員想要的集合.在整個的庫中,你随處可見這種伸縮性.

1.5. GTK+中的+是什麼意思?

GTK+中的+是什麼意思?

Peter Mattis的郵件這樣表述:

我原來編寫的gtk包含以下的三個庫: libglib, libgdk and libgtk.它是一種平行的控件層次,你不能夠從一個已存在的控件中得到一個新的控件,新版的gtk+包含一個比信号機制更加标準的回調機制,+用來區 别原來的版本和新的版本,你可以把它看作一個加入了面向對象特性的舊版的加強版.

1.6. GTK+, GDK和GLib中的G代表什麼?

GTK+, GDK和GLib中的G代表什麼?

GTK+ == Gimp Toolkit

GDK == GTK+ Drawing Kit

GLib == G Library

1.7. 那裡可以找到GTK+的文檔?

那裡可以找到GTK+的文檔?

在 GTK+釋出版本的doc/檔案夾下面,你可以找到 GTK 和 GDK的參考資料,本FAQ和GTK指南.

另外,你可以通過下面的網址找到這個檔案HTML版本 http://www.gtk.org/ ,打包的gtk指南,SGML, HTML, Postscript, DVI或text 的版本可以在下面找到ftp://ftp.gtk.org/pub/gtk/tutorial

這裡還有一些有關GTK+, GDK and GNOME程式設計的書刊:

  • Eric Harlows   "Developing Linux Applications with GTK+ and GDK". The ISBN is 0-7357-0021-4
  • Eric書上的例子的源程式 http://www.bcpl.net/~eharlow/book
  • Havoc Pennington "GTK+/GNOME Application Development". The ISBN is 0-7357-0078-8

    免費版本 : http://developer.gnome.org/doc/GGAD/

    Havoc對書的維護資訊和勘誤表在: http://pobox.com/~hp/gnome-app-devel.html

1.8. 有沒有關于GTK+的郵件清單(或者郵件清單的壓縮包)?

有沒有關于 GTK+的郵件清單(或者郵件清單的壓縮包)?

有關GTK+的郵件資訊可以在下面找到: http://www.gtk.org/mailinglists.html

1.9. GTK+如何獲得幫助

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.11. GTK+有沒有windows版本?

GTK+有沒有windows版本?

現在有一項正在進行的将 GTK+移植到windows平台的項目,取得了很大的進展.參閱

  http://www.iki.fi/tml/gimp/win32 .

1.12. 哪些程式是用GTK+編成的?

哪些程式是用GTK+編成的?

你可以在GTK+ 的網站上找到一個基于 GTK+ 的程式的清單,大約包含了350個程式. http://www.gtk.org/apps/ .

如果上面不行,那就到GNOME項目上領取一個工程,編寫一個小遊戲.寫些有用的東西.http://www.gnome.org/ .

其中包括

  • GIMP (http://www.gimp.org/ ),一個圖像處理程式
  • AbiWord (http://www.abisource.com/ ), 一個專業文字處理程式.
  • Gzilla (http://www.levien.com/gzilla/ ), 一個浏覽器
  • XQF (http://www.botik.ru/~roma/quake/ ), 一個QuakeWorld/Quake2 server 浏覽器和加載器 
  • GDK Imlib (http://www.rasterman.com/imlib.html ), 為開發GDK的快速圖像加載和處理庫
  • Glade (http://glade.pn.org/ ), 基于GTK+的快速開發工具(RAD)

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+需要安裝什麼 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]

2.1. 運作GTK+需要安裝什麼

運作GTK+需要安裝什麼

編譯GTK+,你隻需要一個c編譯器(gcc),一個X window系統和相關的庫檔案.

2.2. 那裡可以得到GTK+

那裡可以得到GTK+

官方網站 ftp://ftp.gtk.org/pub/gtk .

一般新的GTK+版本釋出的時候,這個網站都很擁擠,是以你可以試試下面清單中的鏡像網站. ftp://ftp.gtk.org/etc/mirrors

下面有一些鏡像網站:

  • 非洲 - ftp://ftp.is.co.za/applications/gimp
  • 澳洲 - ftp://ftp.au.gimp.org/pub/gimp
  • 芬蘭 - ftp://ftp.funet.fi/pub/sci/graphics/packages/gimp
  • 德國 - ftp://infosoc.uni-koeln.de/pub/ftp.gimp.org
  • 日本 - ftp://SunSITE.sut.ac.jp/pub/archives/packages/gimp
  • 英國 - ftp://ftp.flirble.org/pub/X/gimp
  • 美國 - ftp://ftp.insync.net/pub/mirrors/ftp.gimp.org

2.3. 如何配置/編譯GTK+

如何配置/編譯GTK+

一般來說,你隻需在gtk+-version/ directory下執行下面的指令:

./configure

make

2.4. 編譯的時候出現錯誤:make: file `Makefile' line 456: Syntax error [GTK 2.x]

編譯的時候出現錯誤:make: file `Makefile' line 456: Syntax error [GTK 2.x]

确定你是否使用的是GNU make(用make -v 檢查),現在有很多形形色色的make的版本,但是不是所有的都能正确的處理自動産生的make檔案( Makefiles).

2.5. 我編譯和安裝了GTK+,但是我不能将程式和它連接配接 [GTK 2.x]

我編譯和安裝了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

2.6. 當我在安裝GTK+的程式時,configure報告不能找到GTK. [GTK 2.x]

當我在安裝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中的對應的一行指令來編譯它,如果編譯成功,嘗試運作一下.

第三章 GTK+的開發和維護

目錄表 3.1. 每個人都在 談論的CVS是什麼東西,怎樣才能通路它? 3.2. 如何向GTK+投稿? 3.3. 如何知道我寫的更新檔是否被采用,如果沒有,原因是什麼? 3.4. 向庫中添加新的控件的方針是什麼? 3.5. 除了C之外,有人在綁定其他的語言嗎?

3.1. 每個人都在談論的CVS是什麼東西,怎樣采能通路它?

每個人都在談論的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

3.2. 如何向GTK+投稿?

如何向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]

3.3. 如何知道我寫的更新檔是否被采用,如果沒有,原因是什麼?

如何知道我寫的更新檔是否被采用,如果沒有,原因是什麼?

上傳的更新檔都将被移到ftp://ftp.gtk.org/pub/gtk/patches, 那兒一個GTK+的開發團隊将對更新檔進行篩選.如果應用了.它們将會被移動到/pub/gtk/patches/old .

如果更新檔因為某種原因沒有被應用,将被移動到/pub/gtk/patches/unapplied 或者 /pub/gtk/patches/outdated. 你可以在gtk-list郵件清單上詢問你的更新檔為什麼沒有被應用.原因很多,從更新檔不能幹淨的使用到根本不正确,如果第一次沒有被應用,請不要灰心.

3.4. 向庫中添加新的控件的方針是什麼?

向庫中添加新的控件的方針是什麼?

這取決于作者,是以如果你改變了控件(widget),必須去問問他們.作為一個普遍的方針,控件如果有用,能正常的工作,不會破壞整個控件集(widget set),都會被樂意接受的.

 

3.5. 除了C外,有人在綁定其他的語言嗎?

除了C外,有人在綁定其他的語言嗎?

GTK+的首頁上列出了GTK+的語言綁定( http://www.gtk.org/ ) :
  • 這裡有幾種c++的封裝.
    • gtk--包,是一個非常小的GTK+的封裝,這是它的首頁:http//www.cs.tut.fi/~p150650/gtk/gtk--.html . FTP ftp://ftp.gtk.org/pub/gtk/gtk-- .
    • VDK包,它被設計為GTK+程式的類Borland編譯器的基礎包.你可以找到它的首頁: http://www.guest.net/homepages/mmotta/VDKHome .
    • wxWindows/Gtk 包, 是一個免費跨平台GUI開發的C++庫,首頁是http://www.freiburg.linux.de/~wxxt/ .
  • 三個著名的Objective-c綁定正在開發.
    • Objgtk http://www.gnome.org/ ,Objgtk是基于Object類的,由Elliot Lee 維護.顯然的,objgtk已經作為GTK+上的一種标準的Objective-C被接受了.
    • 如果你傾向于GNUstep project ,你可能想看看GTKKit. Helge He?/A>. The intention is to setup a GTK+ binding using the FoundationKit. GTKKit includes nicities like writing a XML-type template file to construct a GTK+ interface.
    • GToolKit包, 可以在下面的站點找到://ftp.gtk.org/pub/gtk/objc-gtoolkit/ .
  • 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 的綁定 :
    • pygtk  http://www.daa.com.au/~james/pygtk 和 ftp://ftp.gtk.org/pub/gtk/python
    • python-gtk  http://www.ucalgary.ca/~nascheme/python-gtk
  • 這裡還有一些用于GTK+的OpenGL/Mesa的控件.建議看看下面的網頁: http://www.student.oulu.fi/~jlof/gtkglarea/index.html
  • 最後,還有很多其他的綁定的語言,例如:Eiffel, TOM, Pascal, Pike 等等.

第四章 . 用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),怎麼辦?

4.1. 如何開始?

如何開始 ?

你安裝了GTK+之後,有一些東西可以讓你輕松的使用GTK+來開發.在這裡有一份GTK+指南: http://www.gtk.org/tutorial/ , 貫穿整個開發過程,它将會向你介紹如何用C語言來編寫程式.

目前,這份指南沒有包括GTK+中的所有控件(widget)的資訊,是以,使用所有的GTK+控件的基本的功能的執行個體代碼,你應該看GTK+釋出中的gtk/testgtk.c檔案(以及相關的源檔案).看看這些例子可以給你一個良好的基礎,了解這些控件都能幹什麼.

4.2. 如何用GTK+編寫安全的/SUID/SGID敏感的程式? GTK+安全嗎? 聽說的GTK_MODULES安全漏洞是什麼?

如何用GTK+編寫安全的/SUID/SGID敏感的程式? GTK+安全嗎? 聽說的GTK_MODULES安全漏洞是什麼?

簡短的回答是:不要用GTK+來編寫SUID/SGID程式,在這個問題上的詳細的解釋: http://www.gtk.org/setuid.html .

4.3. 我嘗試編譯一個小型的Hello World程式,但是失敗了,有什麼線索? [GTK 2.x]

我嘗試編譯一個小型的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的指令,代替指令行中執行這條指令的輸出.

上面的指令行将確定:

  • 使用正确的C編譯器标志來編譯這個程式(包括完整的C頭檔案路徑清單).
  • 你的程式将和必要的庫相連接配接.

4.4. 怎樣使用make程式? [GTK 2.x]

怎樣使用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).

4.5. 我在make檔案中使用了後置引用(backquote),但是我的make失敗了.

我在make檔案中使用了後置引用(backquote),但是我的make失敗了.

後置引用(back qoute)語句可能不會被一些舊版的make程式識别.如果你使用舊版的make程式,make可能會失敗,為了讓後置引用的文法正确的工作,你應該使用GUN make程式.(在GNU ftp上得到GNU make  ftp://ftp.gnu.org/ ).

4.6. 我想添加一些配置資料(configure stuff),應如何做?

我想添加一些配置資料(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        = @[email protected]

LDADD           = @[email protected]

CLEANFILES      = *~

DISTCLEANFILES  = .deps/*.P
      
如果你的工程中包含不止一個的子檔案夾,你應該在每個子檔案夾中建立一個Makefile.am 加上一個主Makefile.am ,主Makefile.am 如下:
然後,來使用他們,輸入:
aclocal

autoheader

autoconf

automake --add-missing --include-deps --foreign 
      
了解更多的資訊,你應該看autoconf和 automake的文檔(附帶的文檔很容易了解,還有很多有關autoconf和automake的網上資源)

4.7. 我嘗試使用gdb來調試程式,但是當我執行到一些斷點時,它挂起了我的X伺服器(X server),怎麼辦?

我嘗試使用gdb來調試程式,但是當我執行到一些斷點時,它挂起了我的X伺服器(X server),怎麼辦?

來自 Federico Mena Quintero:

" X并沒有鎖定,但是當你執行到從GTK的什麼地方回調的斷點時,并且這個回調得到一個滑鼠的資訊,就會出現這種狀态."

"加上--sync 選項來運作你的程式,會使調試變得簡單一些,如果你想在控制台中執行調試程式,就在一個和X server不同的控制台中運作.

Eric Mouw還有另外一種解決方案:

"一個舊的終端連接配接到另外一個不使用的串行口( serial port )也很适合來調試X程式,舊的 vt100/vt220終端非常的便宜,但是很難獲得."(在 The Netherlands, YMMV).

 

第五章 . 用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會洩漏記憶體?

5.1.GTK中有那些控件(widget)?

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
      

5.2. GTK+的線程安全嗎?如何編寫多線程GTK+程式?

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);

}
      

5.3. 當在GTK+的app檔案中使用fork()時,為什麼會出現奇怪的'x io error'錯誤?

當在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);         

}
      

5.4. 為什麼當按鈕按下時文本(contents)不移動?這裡有一個更新檔可以使它這樣.

為什麼當按鈕按下時文本(contents)不移動?這裡有一個更新檔可以使它這樣.

來自Peter Mattis:

按鈕被按下時不将其内的子控件向右下方稍微移動的原因是我認為實際上的視覺效果并不是這 樣。我的觀點是你是從按鈕的正上方看它們的。也就是說,使用者界面在一個平面上而你在其上面直接看下去。當按鈕被按下時它直接朝遠離你的方向移動。為了完全 正确地表現這種情況我想其内的子控件應該實作上稍微縮小一點點。但我無法了解為什麼子控件要向左下方偏移。記住,子控件是被設想為附在按鈕的表面上。讓子 控件看起來像是在按鈕的表

面滑動并不是什麼好主意。

另外還有個實踐提示,我有一次也實作這個,但發現這樣看起來并不好然後就删掉了它。

(本段感謝川大胡正 提供幫助)

5.5. 如何識别一個控件的最高層視窗(top level window)或者其他的祖先?

如何識别一個控件的最高層視窗(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);
      

5.6.如何得到一個GtkWindow視窗的ID(Window ID)?

如何得到一個GtkWindow視窗的ID(Window ID)?

在控件被實作時Gdk/X視窗将被建立.

你可以這樣得到視窗的ID:

#include <gdk/gdkx.h>



Window xwin = GDK_WINDOW_XWINDOW (GTK_WIDGET (my_window)->window);
      

5.7.如何捕獲一個輕按兩下(double click)事件(event)(例如在一個清單控件中)?

如何捕獲一個輕按兩下(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")的信号(任何的工具都是這樣,因為電腦無法讀懂你的思想.)

 

5.8. 順便問一下,信号(signals)和事件(events)的差別是什麼?

順便問一下,信号(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)通常收到一個事件的時候發出一個信号.這純粹是為了友善,是以你可以在控件收到一個事件的時候調用一個回調函數.當你 按下一個按鈕的時候會發出一個信号,是以才讓人認為信号和事件有天生的聯系.

5.9. 傳遞給delete_event(或者其他事件)處理函數的資料被損壞.

傳遞給delete_event(或者其他事件)處理函數的資料被損壞.

所有的事件處理函數都有一個附加的參數,包含了觸發這個函數的事件的資訊,是以,一個delete_event  處理函數應該這樣聲明:
gint delete_event_handler (GtkWidget   *widget,

                           GdkEventAny *event,

                           gpointer     data);
      

 

5.10. 我将信号和所有事件都連接配接起來,但是看起來我并沒有捕獲它,什麼原因?

我将信号和所有事件都連接配接起來,但是看起來我并沒有捕獲它,什麼原因?

要捕獲一些特殊的事件要采取一些特殊的措施.實際上,在捕獲一些特殊的事件之前,你應該正确的設定控件的event mask.

例如:

可以讓你捕獲鍵盤釋放的事件.如果你想捕獲所有的事件,隻要使用GDK_ALL_EVENTS_MASK就行了.

所有的event mask的定義都在gdktypes.h檔案中.

 

5.11. 我要向GTK+控件中添加一個新的信号,怎麼辦?

我要向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);

}
      

 

 

5.12.文本能否換行顯示?

文本能否換行顯示?

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控件).

 

5.13. 如何設定視窗模式(modal)?/如何激活一個單一的視窗?

如何設定視窗模式(modal)?/如何激活一個單一的視窗?

建立了視窗之後調用gtk_grab_add(my_window) .關閉了視窗之後調用gtk_grab_remove(my_window)。

5.14. 為什麼我的控件(例如 進度條)沒有更新?

為什麼我的控件(例如 進度條)沒有更新?

你可能沒有将控制權傳回gtk_main(), 你在做一些耗時較長的計算的時候,就可能出現上述的情況.所有的繪圖更新都放在一個隊列中,在 gtk_main() 中處理,你可以使用下面的方法來強迫執行:

這樣可以更新你的控件.

上面的程式片斷是運作所有的等待判斷的事件和高優先級的 idle函數,然後立即傳回(繪圖是在一個高優先級的idle函數中完成的).

 

5.15. 如何向GTK+的對象/控件附加資料?

如何向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包的相容。

5.16. 如何去掉附加在控件上的資料?

如何去掉附加在控件上的資料?

當你向對象中附加資料的時候,你可以使用gtk_object_set_data_full() 函數.前面的三個參數和 gtk_object_set_data()一樣,第四個參數是一個指向一個回調函數的指針,當資料銷毀的時候将調用這個回調函數.下列情況你的資料将被銷毀:
  •  銷毀對象
  • 用一個新的資料代替舊的資料(相同的鍵(key)).
  • 用NULL代替舊的資料 (相同的鍵(key)).

5.17. 如何重定控件的父視窗?

如何重定控件的父視窗?

正常的做法是調用下面的這個函數:
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);
      
       

5.18. 如何得到控件的位置?

如何得到控件的位置?

正如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 )将可能影響上面函數的結果,在寫你的程式的時候,你應該記住這一點.這主要是決定于視窗管理器如何管理對視窗添加的修飾.

 

5.19. 如何設定控件/視窗的大小?如何才能禁止使用者重新設定視窗的大小?

如何設定控件/視窗的大小?如何才能禁止使用者重新設定視窗的大小?

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);
      

5.20. 如何向我的GTK+程式中添加一個彈出式菜單?

如何向我的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;

}
      

5.21. 如何禁止或使能一個控件,例如一個按鈕?

如何禁止或使能一個控件,例如一個按鈕?

禁用(或者使能)一個控件,使用gtk_widget_set_sensitive() 函數,第一個參數是指向你的控件的指針,第二個參數一個布爾值:當值是TRUE時,這個控件使能.

 

5.22. 為什麼gtk_clist*函數中的文本參數(text argument)不應被聲明為const?

為什麼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"可以自動轉換,但是對于數組的情況,并不是數組本身需要轉換,它的數組成員也需要轉換.

 

5.23. 如何在螢幕上顯示象素(圖像資料)?

如何在螢幕上顯示象素(圖像資料)?

有多種的方法來實作.最簡單的方法時使用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(),因為它實在太慢.

 

5.24. 如何在不建立視窗或不顯示視窗的情況下建立一個象素映射(pixmap)?

如何在不建立視窗或不顯示視窗的情況下建立一個象素映射(pixmap)?

象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);
      

5.25. 如何拖放?

如何拖放?

GTK+通過拖放系統(drag-and-drop system )提供了一組高端的函數集來實作程序間通信.GTK+可以在底層的Xdnd和Motif 拖放協定(drag-and-drop protocols)上實作拖放功能.

關于GTK+拖放的文檔并不完整,但是在 Tutorial 有一些資訊.你也可以在gtk/testdnd.c檔案中看到拖放的執行個體代碼.

5.26. 為什麼GTK+/GLib會洩漏記憶體?

為什麼GTK+/GLib會洩漏記憶體?

并不是這樣,GLib和C library(執行malloc)有時會對配置設定的記憶體緩沖,即使你使用free()來釋放.

是以你不應該象上面那樣使用一些工具來看你是否正确使用free()(除了最粗略的估計之外,比如你十分确定會出現上面的記憶體洩漏,但是你無法差別是錯誤還是GLib/malloc緩沖 )

找出記憶體洩漏(memory leaks ),請使用合适的記憶體管理工具.

 

第六章:用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 )?

6.1. 在GtkList控件中如何找出選擇項(selection)?

在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:

selection_mode GTK_LIST()->selection contents
GTK_SELECTION_SINGLE selection is either NULL or contains a GList* pointer for a single selected item.
GTK_SELECTION_BROWSE selection is NULL if the list contains no widgets, otherwise it contains a GList* pointer for one GList structure.
GTK_SELECTION_MULTIPLE selection is NULL if no listitems are selected or a a GList* pointer for the first selected item. that in turn points to a GList structure for the second selected item and so on.
GTK_SELECTION_EXTENDED selection is NULL.

GList 結構體中資料域(data field) GTK_LIST(MyGtkList)->selection指向第一個被選擇的清單條目(GtkListItem),是以,如果你想知道那個清單條目被選中,你應該這樣做:

 

{

        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");

}
      

6.2. 在GtkCList控件中,當表内容滾動時,怎樣避免标題消失?

在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);
      

6.3. 我不想讓我的程式的使用者在組合框(GtkCombo)中輸入文本,怎麼辦?

我不想讓我的程式的使用者在組合框(GtkCombo)中輸入文本,怎麼辦?

GtkCombo控件有一個關聯的文本輸入變量(entry),可以這樣通路:

如果你不想使用者修改這個文本輸入變量(entry)的内容,你可以使用gtk_entry_set_editable()函數:

 

void gtk_entry_set_editable(GtkEntry *entry, 

                                  gboolean editable);
      
将editable變量設定為FALSE可以使使用者不能輸入.

6.4. 如何捕捉組合框(combo box)的改變?

如何捕捉組合框(combo box)的改變?

下列情況下,GtkCombo關聯的 文本輸入變量(entry) 将送出一個 "changed" 信号:
  • 一些文本被輸入
  • combo box的選擇項(selection)改變.
捕捉任何的combo box改變,隻要将你的信号處理函數和它相連:
gtk_signal_connect(GTK_COMBO(cb)->entry,

                         "changed",

                         GTK_SIGNAL_FUNC(my_cb_change_handler),

                         NULL);
      

6.5. 如何在菜單中定義分隔線(separation line)?

如何在菜單中定義分隔線(separation line)?

參看 Tutorial 關于如何建立菜單.建立分隔線隻需要建立一個空的菜單項:
menuitem = gtk_menu_item_new();

gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);

gtk_widget_show(menuitem);
      

6.6. 如何向右對齊菜單,比如Help?

如何向右對齊菜單,比如Help?

取決于你是否使用了MenuFactory,有不同的方法來實作:

使用MenuFactory,這樣做:

menu_path = gtk_menu_factory_find (factory,  "<MyApp>/Help");

gtk_menu_item_right_justify(menu_path->widget);
      
如果沒有使用MenuFactory,這樣做:

6.7. 如何在菜單項中添加帶下滑線的加速鍵?

如何在菜單項中添加帶下滑線的加速鍵?

Damon Chaplin-- Glade項目的技術中堅,提供了下面的代碼例子(這個代碼是Glade輸出的):它的File 中隻定義了一個選項(New ),File 中的F和New 中的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);
      

6.8. 如何從GtkMenuItem中重新找回文本?

如何從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 */

    }
      

6.9. 如何向右(或其他方向)對齊GtkLabel?

如何向右(或其他方向)對齊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);
      

6.10. 如何給GtkLabel控件設定背景顔色?

如何給GtkLabel控件設定背景顔色?

GtkLabel是少數的幾個不建立自己的視窗的GTK+控件之一.相反,它直接的将自己畫在父控件上.這就是說:如果你想為GtkLabel控件設定背景,你需要改變它的父控件的的背景,比如,你将标簽填充的控件.

6.11. 如何使用Resource 檔案來給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);
      

6.12. 如何在Resource 檔案中配置工具提示(Tooltips)?

如何在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"
      

6.13. 無法在GtkEntry中輸入大于2000(大約)個字元,什麼原因?

無法在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);
      

6.14. 如何能在按下回車時使GtkEntry控件激活?

如何能在按下回車時使GtkEntry控件激活?

當按回車時,Entry控件将發出一個'activate'信号,你隻需和這個信号相連,然後做你想做的事.典型的代碼是這樣:
entry = gtk_entry_new();

  gtk_signal_connect (GTK_OBJECT(entry), "activate",

                      GTK_SIGNAL_FUNC(entry_callback),

                      NULL);
      

6.15. 如何确認/限制/篩選GtkEntry中的輸入?

如何确認/限制/篩選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);

}
      

6.16. 如何在GtkText控件中使用水準滾動條(horizontal scrollbars)?

如何在GtkText控件中使用水準滾動條(horizontal scrollbars)?

回答是你不能這樣做,目前的版本的GtkText控件不支援使用水準滾動,現在有完全重寫GtkText控件的計劃,到那時這個限制就被取消了.

 

6.17. 如何改變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-*-*-*-*-*-*");
      

6.18. 如何在GtkText控件中設定光标位置(cursor position)?

如何在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

目錄表 7.1. 什麼是GDK? 7.2. 如何使用顔色配置設定?

7.1.什麼是GDK?

什麼是GDK?

GDK是标準Xlib函數調用的一個基本封裝(wrapper ),如果你對Xlib很熟悉,就不需要來重新熟悉絕大多數的GDK函數.所有的函數都是為了提供一個友善直覺的風格來通路Xlib函數庫.另外,自從GDK使用GLib ,在多平台上使用時,GDK變得更加的友善和安全.

 

7.2. 如何使用顔色配置設定?

如何使用顔色配置設定?

有關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

目錄表 8.1. 什麼是GLib? 8.2. 如何使用雙向連結清單( doubly linked lists)? 8.3. 當我釋放配置設定的連結清單節點時,記憶體似乎沒有被釋放. 8.4. 為什麼使用 g_print, g_malloc, g_strdup和其他的glib函數? 8.5. 什麼是掃描器(GScanner),如何使用?

8.1. 什麼是GLib?

什麼是GLib?

GLib庫提供了一系列函數(functions)和定義(definitions),在設計GDK和GTK程式時很有用.它提供了一些标準c函數庫(standard libc)的替代函數,比如malloc,但在其他的系統上使用時有很多問題.

  它還提供一些常用工具:

  • Doubly Linked Lists雙向連結清單
  • Singly Linked Lists單向連結清單
  • Timers計時器
  • String Handling串處理
  • A Lexical Scanner文法掃描器
  • Error Functions錯誤處理函數

8.2. 如何使用雙向連結清單( doubly linked lists)?

如何使用雙向連結清單( 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 ).

 

8.3. 當我釋放配置設定的連結清單節點時,記憶體似乎沒有被釋放.

當我釋放配置設定的連結清單節點時,記憶體似乎沒有被釋放.

在這個特殊的問題上,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);
      

8.4. 為什麼使用 g_print, g_malloc, g_strdup和其他的glib函數?

為什麼使用 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檔案中)

8.5. 什麼是掃描器(GScanner),如何使用?

什麼是掃描器(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"作為一個符号加入:

掃描器将會這樣分析:

"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.

 
 

繼續閱讀