国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

在Python中封裝GObject模塊進行圖形化程序編程的教程

2019-11-25 17:44:29
字體:
來源:轉載
供稿:網友

Python 是用于編碼圖形界面的極佳語言。由于可以迅速地編寫工作代碼并且不需要費時的編譯周期, 所以可以立即使界面啟動和運行起來,并且不久便可使用這些界面。 將這一點與 Python 易于鏈接本機庫的能力結合起來,就可以形成一個出色的環境。

gnome-python 是為 Python 封裝 GNOME 及其相關庫的軟件包。 這使您能夠用 Python 編寫外觀與核心 GNOME 應用程序完全相同的應用程序,而所花的時間只是用 C 編寫該應用程序所花的一部分。

然而,不用 C 進行編程會有一個缺點。大多數 GNOME 都是用 C 編寫的,對于要在 Python 中使用的窗口小部件,必須將它們封裝。 對于知道封裝過程如何工作的人來說,這是一個快速任務,但它不是自動的, 除非窗口小部件屬于核心 GNOME 庫或者至少非常有用,否則將不會對它們進行封裝。C 程序員可能必須編寫更復雜的代碼,但它們確實先做了這一步!

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

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

為了演示該過程,我將封裝 EggTrayIcon ,它是用于在通知區中抽象表示圖標的 GTK+ 窗口小部件。 該庫在 GNOME CVS 中,位于 libegg 模塊。在本文的結尾,我們將有一個名為 trayicon 的本機 Python 模塊,它包含一個 TrayIcon 對象。

開始時,獲得 eggtrayicon.c 和 eggtrayicon.h(其鏈接在本文結尾的 參考資料一節中),然后將它們放入新目錄中。 應該在 automake 環境中構建該源文件(但我們將不在這種環境中), 所以或者除去這些文件中的 #include <config.h> ,或者創建一個名為 config.h 的空文件,然后創建一個空的 makefile;接下來,我們將填充它。
創建界面定義

該對象封裝過程的第一步是創建 trayicon.defs,該文件為該對象指定 API。 定義文件是用一種類 Scheme 的語言編寫的,雖然對于小型界面來說它們很容易生成, 但對于大型界面或初學者來說編寫它們會很吃力。

gnome-python 與名為 h2def 的工具一起提供。該工具將解析頭文件并生成粗略的定義文件。 注:因為它實際上并沒有解析 C 代碼,而只是使用正則表達式, 所以它的確要求傳統格式化的 GObject,并且不能正確解析奇特格式化的 C 代碼。

要生成初始定義文件,我們如下調用 h2def : python /usr/share/pygtk/2.0/codegen/h2def.py eggtrayicon.h > trayicon.defs

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

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

生成包裝器

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

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

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

讓我們再次更詳細地檢查該代碼:

  headers  #include <Python.h>  #include "pygobject.h"  #include "eggtrayicon.h"

    這些是在構建包裝器時要包括的頭文件。 始終必需包括 Python.h 和 pygobject.h,當我們封裝 eggtrayicon.h 時,我們也必需包括它們。
    

modulename trayicon

    modulename 規范聲明包裝器將在什么模塊中。
    

import gtk.Plug as PyGtkPlug_Type

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

  import gobject.GObject as PyGObject_Type  ignore-glob  *_get_type

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

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

再次詳細說明:

 

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

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

  trayicon.c: trayicon.defs trayicon.override

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

 pygtk-codegen-2.0 --prefix trayicon /

    這里調用 gnome-python 代碼生成器。 prefix 參數被用作在已生成的代碼內部的變量名的前綴。 您可以隨意命名該參數,但使用模塊名的話可使符號名保持一致。

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

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

  --override trayicon.override /

    該參數將我們創建的覆蓋文件傳遞給代碼生成器。

  trayicon.defs > $@

    這里,代碼生成器的最后一個選項是定義文件本身。 代碼生成器在標準輸出上進行輸出,所以我們將它重定向到目標 trayicon.c。

如果我們現在運行 make trayicon.c ,然后查看已生成的文件, 那么我們會看到 C 代碼包裝 EggTrayIcon 中的每個函數。不必擔心警告 No ArgType for GdkScreen*― 這是正常的。

正如您所看到的那樣,封裝代碼看上去復雜,所以我們感謝代碼生成器為我們編寫的每一行。 稍后,我們將學習當想要調優封裝時如何手工封裝各個方法,而我們自己不必編寫所有包裝器。

創建模塊

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

#include <pygobject.h> 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");  }}

這里需要說明一下一些細微的區別, 因為有多個使用單詞 trayicon 的源代碼。函數 inittrayicon 的名稱和初始化模塊的名稱是 Python 模塊的真實名稱,因此是最終共享對象的名稱。 數組 trayicon_functions 和函數 trayicon_register_classes 是根據代碼生成器的 --prefix 參數命名的。正如前面所提到的那樣,最好使這些名稱保持一致,以便編碼該文件不會變得很混亂。

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

現在我們有了所有代碼塊,就可以生成共享對象了。將以下內容添加到 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 $@

讓我們再次逐行仔細檢查:

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

    該行定義 C 編譯標志。我們使用 pkg-config 來獲取 GTK+ 和 PyGTK 的 include 路徑。
  

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

    該行定義鏈接程序標志。再次使用 pkg-config 來獲取正確的庫路徑。
    

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

    共享對象是根據生成的代碼、我們剛才編寫的模塊代碼以及 EggTrayIcon 的實現構造的。隱式規則根據我們創建的 .c 文件構建 .o 文件。

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

    這里我們構建最終的共享庫。

現在運行 make trayicon.so 應該會根據定義生成 C 代碼,編譯三個 C 文件, 最后將它們全都鏈接在一起。做得不錯 ― 我們已經構建了第一個本機 Python 模塊。 如果它沒有編譯和鏈接,請仔細檢查這些階段,并確保早先沒有出現會引起稍后出錯的警告。

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

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

希望從 dir 產生的結果與這里相同。現在我們準備開始一個更大的示例!
清單 6. Hello 示例

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

逐行細化它:

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

    這里,我們首先請求和導入 GTK+ 綁定,然后導入新模塊。

  t = trayicon.TrayIcon("MyFirstTrayIcon")

    現在創建 trayicon.TrayIcon 的實例。注:構造函數帶有字符串參數 ― 圖標名稱。

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

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

  t.show_all()  gtk.main()

    這里,我將窗口小部件設置為可視的,然后啟動 GTK+ 主事件循環。

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

2015414154123688.gif (258×24)

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

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

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

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

然而,這有點兒難看。這就是 Python;編程應該簡單。 如果我們堅持沿著這條路線,那么將以 C 告終,但是沒有分號。 幸運的是,在使用 gnome-python 代碼生成器時,可以手工封裝各個方法。

調優界面

到目前為止,我們已經有了 send_message(int timeout, char *message, int length) 函數。 如果 EggTrayIcon 的 Python API 允許我們調用 send_message(timeout, message) ,那會更好。幸運的是,這并不太難。

完成這一步將再次涉及編輯 trayicon.override。這正是文件名的意義所在:該文件主要包含手工覆蓋的包裝器函數。 比起演示一個示例并逐步說明其內容來,說明這些函數的工作原理要難得多,所以下面是手工封裝的 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); }

為了清晰起見,我們再次將該清單逐行細化:

  override egg_tray_icon_send_message kwargs

    該行告訴代碼生成器我們將提供 egg_tray_icon_send_message 的手工定義,它本身應該不會生成一個定義。

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

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

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

    這里,我們實際上調用 egg_tray_icon_send_message ,然后將返回的 int 轉換成 PyObject 。

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

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

要使用新的包裝器,只需重新構建共享對象并將測試程序中的 send_message 調用更改成:

t.send_message(1000, message)

如果每件事情都照常進行,那么這個修改后的示例應該有相同的行為,但具有更清晰的代碼。

結束游戲

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宜阳县| 康乐县| 通榆县| 周宁县| 慈溪市| 永寿县| 清丰县| 柏乡县| 砚山县| 涪陵区| 乐平市| 北流市| 阿图什市| 海南省| 日喀则市| 常州市| 东平县| 乳山市| 广西| 邛崃市| 余干县| 抚顺市| 同仁县| 东兴市| 和硕县| 吉水县| 长白| 白山市| 海阳市| 如皋市| 句容市| 五河县| 天门市| 彭阳县| 新源县| 高州市| 永嘉县| 罗田县| 石泉县| 阿拉善右旗| 观塘区|