日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

在Python中封裝GObject模塊進(jìn)行圖形化程序編程的教程

系統(tǒng) 1686 0

Python 是用于編碼圖形界面的極佳語(yǔ)言。由于可以迅速地編寫(xiě)工作代碼并且不需要費(fèi)時(shí)的編譯周期, 所以可以立即使界面啟動(dòng)和運(yùn)行起來(lái),并且不久便可使用這些界面。 將這一點(diǎn)與 Python 易于鏈接本機(jī)庫(kù)的能力結(jié)合起來(lái),就可以形成一個(gè)出色的環(huán)境。

gnome-python 是為 Python 封裝 GNOME 及其相關(guān)庫(kù)的軟件包。 這使您能夠用 Python 編寫(xiě)外觀與核心 GNOME 應(yīng)用程序完全相同的應(yīng)用程序,而所花的時(shí)間只是用 C 編寫(xiě)該應(yīng)用程序所花的一部分。

然而,不用 C 進(jìn)行編程會(huì)有一個(gè)缺點(diǎn)。大多數(shù) GNOME 都是用 C 編寫(xiě)的,對(duì)于要在 Python 中使用的窗口小部件,必須將它們封裝。 對(duì)于知道封裝過(guò)程如何工作的人來(lái)說(shuō),這是一個(gè)快速任務(wù),但它不是自動(dòng)的, 除非窗口小部件屬于核心 GNOME 庫(kù)或者至少非常有用,否則將不會(huì)對(duì)它們進(jìn)行封裝。C 程序員可能必須編寫(xiě)更復(fù)雜的代碼,但它們確實(shí)先做了這一步!

但并不一定是那樣!雖然從傳統(tǒng)上講封裝窗口小部件過(guò)程這一技術(shù)只有極少數(shù)人才知道,但它并不真的那么難。 如果您在發(fā)現(xiàn)新的窗口小部件時(shí)可以將它們封裝,那么您就可以立刻在 Python 程序中使用它們。

本文將描述如何封裝用 C 編碼的 GObject(所有 GTK+ 窗口小部件和許多相關(guān)對(duì)象的最終基類), 以便可以從 Python 代碼使用它。假設(shè)您的機(jī)器上安裝了 gnome-python V1.99.x(如果沒(méi)有安裝, 請(qǐng)參閱 參考資料以獲取鏈接)。如果您正在使用軟件包,請(qǐng)確保安裝了該開(kāi)發(fā)軟件包。 另外,還必須安裝 Python 2.2 及其頭文件。 假設(shè)您了解 Make、Python、GTK+ 2 和一些 C 方面的知識(shí)。

為了演示該過(guò)程,我將封裝 EggTrayIcon ,它是用于在通知區(qū)中抽象表示圖標(biāo)的 GTK+ 窗口小部件。 該庫(kù)在 GNOME CVS 中,位于 libegg 模塊。在本文的結(jié)尾,我們將有一個(gè)名為 trayicon 的本機(jī) Python 模塊,它包含一個(gè) TrayIcon 對(duì)象。

開(kāi)始時(shí),獲得 eggtrayicon.c 和 eggtrayicon.h(其鏈接在本文結(jié)尾的 參考資料一節(jié)中),然后將它們放入新目錄中。 應(yīng)該在 automake 環(huán)境中構(gòu)建該源文件(但我們將不在這種環(huán)境中), 所以或者除去這些文件中的 #include ,或者創(chuàng)建一個(gè)名為 config.h 的空文件,然后創(chuàng)建一個(gè)空的 makefile;接下來(lái),我們將填充它。
創(chuàng)建界面定義

該對(duì)象封裝過(guò)程的第一步是創(chuàng)建 trayicon.defs,該文件為該對(duì)象指定 API。 定義文件是用一種類 Scheme 的語(yǔ)言編寫(xiě)的,雖然對(duì)于小型界面來(lái)說(shuō)它們很容易生成, 但對(duì)于大型界面或初學(xué)者來(lái)說(shuō)編寫(xiě)它們會(huì)很吃力。

gnome-python 與名為 h2def 的工具一起提供。該工具將解析頭文件并生成粗略的定義文件。 注:因?yàn)樗鼘?shí)際上并沒(méi)有解析 C 代碼,而只是使用正則表達(dá)式, 所以它的確要求傳統(tǒng)格式化的 GObject,并且不能正確解析奇特格式化的 C 代碼。

要生成初始定義文件,我們?nèi)缦抡{(diào)用 h2def : python /usr/share/pygtk/2.0/codegen/h2def.py eggtrayicon.h > trayicon.defs

注:如果沒(méi)有將 h2def.py 安裝在 /usr 下,則必須更改該路徑以指向它所在的地方。

如果我們現(xiàn)在查看已生成的定義文件,它應(yīng)該具有某些意義。 該文件中含有類 EggTrayIcon 的定義、構(gòu)造函數(shù)以及方法 send_message 和 cancel_message 。 該文件沒(méi)有任何明顯錯(cuò)誤,我們不想除去任何方法或字段,所以我們不需編輯它。 注:該文件不是特定于 Python 的,其它語(yǔ)言綁定也可以使用它。

生成包裝器

既然我們有了界面定義,那么就可以生成 Python 包裝器的代碼塊。這包括生成一個(gè)覆蓋文件。 覆蓋文件告訴代碼生成器要包括哪些頭文件、模塊名將是什么等等。

通過(guò)使用 %% 將覆蓋文件分成多個(gè)節(jié)(以 lex/yacc 樣式)。 這些節(jié)定義要包括哪些頭文件、模塊名、要包括哪些 Python 模塊、要忽略哪些函數(shù)以及最后所有手工封裝的函數(shù)。 下面是 trayicon 模塊的初始覆蓋文件。
清單 1. trayicon.override

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


            
          

讓我們?cè)俅胃敿?xì)地檢查該代碼:

            
  headers
  #include 
            
              
  #include "pygobject.h"
  #include "eggtrayicon.h"

            
          

??? 這些是在構(gòu)建包裝器時(shí)要包括的頭文件。 始終必需包括 Python.h 和 pygobject.h,當(dāng)我們封裝 eggtrayicon.h 時(shí),我們也必需包括它們。
????

            
modulename trayicon

          

??? modulename 規(guī)范聲明包裝器將在什么模塊中。
????

            
import gtk.Plug as PyGtkPlug_Type

          

??? 這些是用于包裝器的 Python 導(dǎo)入。請(qǐng)注意命名約定;對(duì)于要編譯的模塊,必須遵守它。 通常,導(dǎo)入對(duì)象的超類就足夠了。例如,如果對(duì)象直接從 GObject 繼承,則使用:

            
  import gobject.GObject as PyGObject_Type
  ignore-glob
  *_get_type

          

??? 這是一個(gè)要忽略的函數(shù)名的 glob 模式(shell 樣式的正則表達(dá)式)。Python 替我們處理了類型代碼,因此我們忽略 *_get_type 函數(shù);否則,將對(duì)它們進(jìn)行封裝。

既然我們構(gòu)造了覆蓋文件,那么就可以使用它來(lái)生成包裝器。 gnome-python 綁定為生成包裝器提供了一種神奇的工具, 我們可以隨意使用它。 將下列內(nèi)容添加到 makefile:
清單 2. 初始 makefile

再次詳細(xì)說(shuō)明:

?

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

          

??? DEFS 是包含 Python GTK+ 綁定定義文件的路徑。

            
  trayicon.c: trayicon.defs trayicon.override

          

??? 生成的 C 代碼取決于定義文件和覆蓋文件。
???

            
 pygtk-codegen-2.0 --prefix trayicon \

          

??? 這里調(diào)用 gnome-python 代碼生成器。 prefix 參數(shù)被用作在已生成的代碼內(nèi)部的變量名的前綴。 您可以隨意命名該參數(shù),但使用模塊名的話可使符號(hào)名保持一致。

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

          

??? 模塊使用 GLib 和 GTK+ 中的類型,所以我們還必須告訴代碼生成器裝入這些類型。

            
  --override trayicon.override \

          

??? 該參數(shù)將我們創(chuàng)建的覆蓋文件傳遞給代碼生成器。

            
  trayicon.defs > $@

          

??? 這里,代碼生成器的最后一個(gè)選項(xiàng)是定義文件本身。 代碼生成器在標(biāo)準(zhǔn)輸出上進(jìn)行輸出,所以我們將它重定向到目標(biāo) trayicon.c。

如果我們現(xiàn)在運(yùn)行 make trayicon.c ,然后查看已生成的文件, 那么我們會(huì)看到 C 代碼包裝 EggTrayIcon 中的每個(gè)函數(shù)。不必?fù)?dān)心警告 No ArgType for GdkScreen*― 這是正常的。

正如您所看到的那樣,封裝代碼看上去復(fù)雜,所以我們感謝代碼生成器為我們編寫(xiě)的每一行。 稍后,我們將學(xué)習(xí)當(dāng)想要調(diào)優(yōu)封裝時(shí)如何手工封裝各個(gè)方法,而我們自己不必編寫(xiě)所有包裝器。

創(chuàng)建模塊

既然已經(jīng)創(chuàng)建了包裝器的代碼塊,那么就需要一個(gè)啟動(dòng)它的方法。 這涉及創(chuàng)建 trayiconmodule.c,該文件可被視為 Python 模塊的 main() 函數(shù)。 該文件是樣板文件代碼(與覆蓋文件相似),我們對(duì)它稍作修改。下面是我們將使用的 trayiconmodule.c:
清單 3. TrayIcon 模塊代碼

            
#include 
            
              
 
void trayicon_register_classes (PyObject *d); 
extern PyMethodDef trayicon_functions[];
 
DL_EXPORT(void)
inittrayicon(void)
{
  PyObject *m, *d;
 
  init_pygobject ();
 
  m = Py_InitModule ("trayicon", trayicon_functions);
  d = PyModule_GetDict (m);
 
  trayicon_register_classes (d);
 
  if (PyErr_Occurred ()) {
    Py_FatalError ("can't initialise module trayicon");
  }
}


            
          

這里需要說(shuō)明一下一些細(xì)微的區(qū)別, 因?yàn)橛卸鄠€(gè)使用單詞 trayicon 的源代碼。函數(shù) inittrayicon 的名稱和初始化模塊的名稱是 Python 模塊的真實(shí)名稱,因此是最終共享對(duì)象的名稱。 數(shù)組 trayicon_functions 和函數(shù) trayicon_register_classes 是根據(jù)代碼生成器的 --prefix 參數(shù)命名的。正如前面所提到的那樣,最好使這些名稱保持一致,以便編碼該文件不會(huì)變得很混亂。

盡管名稱源可能存在混淆,但該 C 代碼非常簡(jiǎn)單。 它初始化 GObject 和 trayicon 模塊,然后向 Python 注冊(cè)這些類。

現(xiàn)在我們有了所有代碼塊,就可以生成共享對(duì)象了。將以下內(nèi)容添加到 makefile:
清單 4. makefile 附加代碼部分

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


          

讓我們?cè)俅沃鹦凶屑?xì)檢查:

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

          

??? 該行定義 C 編譯標(biāo)志。我們使用 pkg-config 來(lái)獲取 GTK+ 和 PyGTK 的 include 路徑。
??

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

          

??? 該行定義鏈接程序標(biāo)志。再次使用 pkg-config 來(lái)獲取正確的庫(kù)路徑。
????

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

          

??? 共享對(duì)象是根據(jù)生成的代碼、我們剛才編寫(xiě)的模塊代碼以及 EggTrayIcon 的實(shí)現(xiàn)構(gòu)造的。隱式規(guī)則根據(jù)我們創(chuàng)建的 .c 文件構(gòu)建 .o 文件。

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

          

??? 這里我們構(gòu)建最終的共享庫(kù)。

現(xiàn)在運(yùn)行 make trayicon.so 應(yīng)該會(huì)根據(jù)定義生成 C 代碼,編譯三個(gè) C 文件, 最后將它們?nèi)兼溄釉谝黄稹W龅貌诲e(cuò) ― 我們已經(jīng)構(gòu)建了第一個(gè)本機(jī) Python 模塊。 如果它沒(méi)有編譯和鏈接,請(qǐng)仔細(xì)檢查這些階段,并確保早先沒(méi)有出現(xiàn)會(huì)引起稍后出錯(cuò)的警告。

既然我們有了 trayicon.so,就可以在 Python 程序中嘗試并使用它。 開(kāi)始時(shí)最好裝入它,然后列出其成員。在 shell 中運(yùn)行 python 以打開(kāi)交互式解釋器,然后輸入以下命令。
清單 5. TrayIcon 的交互式測(cè)試

            
$ python
Python 2.2.2 (#1, Jan 18 2003, 10:18:59)
[GCC 3.2.2 20030109 (Debian prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pygtk
>>> pygtk.require("2.0")
>>> import trayicon
>>> dir (trayicon)
['TrayIcon', '__doc__', '__file__', '__name__']


          

希望從 dir 產(chǎn)生的結(jié)果與這里相同。現(xiàn)在我們準(zhǔn)備開(kāi)始一個(gè)更大的示例!
清單 6. Hello 示例

            
#! /usr/bin/python
import pygtk
pygtk.require("2.0")
import gtk
import trayicon                
t = trayicon.TrayIcon("MyFirstTrayIcon")   
t.add(gtk.Label("Hello"))           
t.show_all()
gtk.main()


          

逐行細(xì)化它:

            
  #! /usr/bin/python
  import pygtk
  pygtk.require("2.0")
  import gtk
  import trayicon

          

??? 這里,我們首先請(qǐng)求和導(dǎo)入 GTK+ 綁定,然后導(dǎo)入新模塊。

            
  t = trayicon.TrayIcon("MyFirstTrayIcon")

          

??? 現(xiàn)在創(chuàng)建 trayicon.TrayIcon 的實(shí)例。注:構(gòu)造函數(shù)帶有字符串參數(shù) ― 圖標(biāo)名稱。

            
  t.add(gtk.Label("Hello"))

          

??? TrayIcon 元素是 GTK+ 容器,所以您可以將任何東西添加到其中。 這里,我添加一個(gè)標(biāo)簽窗口小部件。

            
  t.show_all()
  gtk.main()

          

??? 這里,我將窗口小部件設(shè)置為可視的,然后啟動(dòng) GTK+ 主事件循環(huán)。

現(xiàn)在,如果您還未這樣做,則將 Notification Area applet 添加到 GNOME 面板(在該面板上單擊鼠標(biāo)右鍵,然后選擇“Add to Panel”-> Utility -> Notification Area)。 運(yùn)行該測(cè)試程序應(yīng)該會(huì)在欄中顯示“Hello”。很酷,不是嗎??

2015414154123688.gif (258×24)

?通知區(qū)還允許我們做什么?好,程序可以告訴通知區(qū)顯示消息。 該消息的實(shí)際顯示方式是特定于實(shí)現(xiàn)的;目前,GNOME 通知區(qū)顯示工具提示。 我們可以通過(guò)調(diào)用 send_message() 函數(shù)發(fā)送消息。快速查看 API 可以得知它希望有超時(shí)和消息, 所以它應(yīng)該如下工作:

            
...
t = trayicon.TrayIcon("test")
...
t.send_message(1000, "My First Message")


          

但并不是那樣。C 原型是 send_message(int timeout, char* message, int length) , 所以 Python API 還需要字符指針和長(zhǎng)度。這確實(shí)起作用:

            
...
t = trayicon.TrayIcon("test")
...
message = "My First Message"
t.send_message(1000, message, len(message))


          

然而,這有點(diǎn)兒難看。這就是 Python;編程應(yīng)該簡(jiǎn)單。 如果我們堅(jiān)持沿著這條路線,那么將以 C 告終,但是沒(méi)有分號(hào)。 幸運(yùn)的是,在使用 gnome-python 代碼生成器時(shí),可以手工封裝各個(gè)方法。

調(diào)優(yōu)界面

到目前為止,我們已經(jīng)有了 send_message(int timeout, char *message, int length) 函數(shù)。 如果 EggTrayIcon 的 Python API 允許我們調(diào)用 send_message(timeout, message) ,那會(huì)更好。幸運(yùn)的是,這并不太難。

完成這一步將再次涉及編輯 trayicon.override。這正是文件名的意義所在:該文件主要包含手工覆蓋的包裝器函數(shù)。 比起演示一個(gè)示例并逐步說(shuō)明其內(nèi)容來(lái),說(shuō)明這些函數(shù)的工作原理要難得多,所以下面是手工封裝的 send_message 代碼。
清單 7. 手工覆蓋

            
override egg_tray_icon_send_message kwargs 
static PyObject*
_wrap_egg_tray_icon_send_message(PyGObject *self,
                 PyObject *args, PyObject *kwargs) 
{
  static char *kwlist[] = {"timeout", "message", NULL}; 
  int timeout, len, ret;
  char *message;
  if (!PyArg_ParseTupleAndKeywords(args, kwargs,  
                   "is#:TrayIcon.send_message", kwlist,
                   &timeout, &message, &len))
    return NULL;
  ret = egg_tray_icon_send_message(EGG_TRAY_ICON(self->obj),
                   timeout, message, len);
  return PyInt_FromLong(ret); 
}


          

為了清晰起見(jiàn),我們?cè)俅螌⒃撉鍐沃鹦屑?xì)化:

            
  override egg_tray_icon_send_message kwargs

          

??? 該行告訴代碼生成器我們將提供 egg_tray_icon_send_message 的手工定義,它本身應(yīng)該不會(huì)生成一個(gè)定義。

            
  static PyObject*
  _wrap_egg_tray_icon_send_message(PyGObject *self,
  PyObject *args, PyObject *kwargs)

          

??? 這是 Python-to-C 橋的原型。它由正在對(duì)其調(diào)用方法的 GObject 的指針、參數(shù)數(shù)組和關(guān)鍵字參數(shù)數(shù)組組成。 返回值始終是 PyObject* ,因?yàn)?Python 中的所有值都是對(duì)象(甚至整數(shù))。

            
  {
  static char *kwlist[] = {"timeout", "message", NULL};
  int timeout, len, ret;
  char *message;

          

??? 該數(shù)組定義該函數(shù)接受的關(guān)鍵字參數(shù)的名稱。 提供使用關(guān)鍵字參數(shù)的能力不是必需的,但它可以使帶有許多參數(shù)的代碼變得清楚許多,而且不需要大量的額外工作。

            
  if (!PyArg_ParseTupleAndKeywords(args, kwargs,
  "is#:TrayIcon.send_message", kwlist,
  &timeout, &message, &len))
  return NULL;

          

??? 這個(gè)復(fù)雜的函數(shù)調(diào)用執(zhí)行參數(shù)解析。我們向它提供所知道的關(guān)鍵字參數(shù)以及所有給定參數(shù)的列表, 它將設(shè)置最終參數(shù)指向的值。那個(gè)看上去費(fèi)解的字符串聲明了所需要的變量類型,我們稍后將說(shuō)明它。

            
  ret = egg_tray_icon_send_message(EGG_TRAY_ICON(self->obj),
  timeout, message, len);
  return PyInt_FromLong(ret);
  }

          

??? 這里,我們實(shí)際上調(diào)用 egg_tray_icon_send_message ,然后將返回的 int 轉(zhuǎn)換成 PyObject 。

起先這看上去有點(diǎn)可怕,但它最初是從 trayicon.c 中的生成代碼復(fù)制來(lái)的。 在大多數(shù)情況下,如果您只想調(diào)優(yōu)所需要的參數(shù),那么這是完全有可能的。 只要從生成的 C 中復(fù)制并粘貼相關(guān)函數(shù),添加有魔力的覆蓋行并編輯該代碼, 直到它如您所愿。

最重要的更改是修改所需要的參數(shù)。 PyArg_ParseTupleAndKeywords 函數(shù)中看上去費(fèi)解的字符串定義了所需要的參數(shù)。 最初,它是 isi:TrayIcon.send_message ;這意味著參數(shù)依次是 int 、 char* (s 表示字符串)和 int ; 而且如果拋出一個(gè)異常,則該函數(shù)稱作 TrayIcon.send_message 。 我們不想必須在 Python 代碼中指定字符串長(zhǎng)度,所以將 isi 更改為 is# 。使用 s# 來(lái)代替 s 意味著 PyArg_ParseTupleAndKeywords 將自動(dòng)計(jì)算字符串長(zhǎng)度并為我們?cè)O(shè)置另一個(gè)變量 ― 這正是我們想要的。

要使用新的包裝器,只需重新構(gòu)建共享對(duì)象并將測(cè)試程序中的 send_message 調(diào)用更改成:

            
t.send_message(1000, message)


          

如果每件事情都照常進(jìn)行,那么這個(gè)修改后的示例應(yīng)該有相同的行為,但具有更清晰的代碼。

結(jié)束游戲

我們采用了小型的但有用的 C GObject,封裝它,這樣就可以在 Python 中使用它, 甚至可以對(duì)包裝器進(jìn)行度身定做以符合我們的需要。這里的技術(shù)可以多次應(yīng)用于不同對(duì)象, 允許您使用在 Python 中找到的任何 GObject。


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 北票市| 乡宁县| 乌拉特中旗| 遵义市| 六安市| 房产| 克拉玛依市| 利辛县| 苍南县| 邢台市| 元朗区| 新密市| 交城县| 临澧县| 托克托县| 新兴县| 吴忠市| 永丰县| 德钦县| 大连市| 胶南市| 华坪县| 龙泉市| 团风县| 申扎县| 泾源县| 五家渠市| 舟山市| 淮北市| 凤台县| 弥勒县| 洛川县| 拜城县| 新绛县| 五寨县| 威信县| 句容市| 柯坪县| 东明县| 黔东| 治县。|