使用PyGtk和Glade构建应用程序
  原文 
 在使用PyGTK和Glade完成了我的第一个例子后, 我决定写一个更加复杂的例子, 但是我写不出一个好的题目来. (注: 我很乐意听到你对任何例子或帖子有任何建议), 于是我决定写一个简单的程序或许会从这里找出一些有用的主题来.
  我的想法是创建一个可以让我记录我喝过酒的种类以及我对它们的喜好程度. 我写想这个程序很久了, 因此我想把学习PyGTK和写它结合起来会是一个好主意.
  我把这个项目命名为PyWine. 本例程的全部代码和Glade文件可以从这里下载.
  
文件: PyWine.tar.tar
大小: 30KB
下载: 下载

   我们要做的第一件事情是在项目目录下创建一个名为PyWine的新Glade项目. 然后创建一个名为”mainWindow”的窗口, 并把它的标题设置成”PyWine”. 然后像在例程1里那样为它添加一个销毁的信息处理器.
   然后我添加了一个有4行的垂直盒子(Vertical Box), (从上到下): 一行放置菜单栏,一行放工具栏,一行放一个树状或列表视图, 最后一行旋转一个状态栏. 把树状视图命名为”wineView”.

使用PyGTK和Glade创建用户界面>中拿来的).

#!/usr/bin/env python

import sys

try:

 import pygtk

  pygtk.require("2.0")

except:

  pass

try:

import gtk

  import gtk.glade

except:

sys.exit(1)

class pyWine:

"""This is the PyWine application"""

def __init__(self):

#Set the Glade file

self.gladefile = "pywine.glade"

self.wTree = gtk.glade.XML(self.gladefile, "mainWindow")

#Create our dictionay and connect it

dic = {"on_mainWindow_destroy" : gtk.main_quit

, "on_AddWine" : self.OnAddWine}

self.wTree.signal_autoconnect(dic)

def OnAddWine(self, widget):

"""Called when the use wants to add a wine"""

print "OnAddWine"

if __name__ == "__main__":

wine = pyWine()

gtk.main()
  在这里多了一点额外的东西. 一个是on_AddWine信号的处理器. 如果你运行这个程序你会注意到当你点击Add Wine按钮或是从菜单栏中选择Add | Wine时会打印出”OnAddWine”. 代码中的新加的另外一个是我们把主窗口的名字传给gtk.glade.XML, 这会让我们只加载这个窗口和它的子窗口.
   下一步是创建一个用来存储酒信息的类:
class Wine:

"""This class represents all the wine information"""

def __init__(self, wine="", winery="", grape="", year=""):

self.wine = wine

self.winery = winery

self.grape = grape

self.year = year
   很简单, 下一步我们需要创建一个给wineDlg使用的类, 我们称它为wineDialog: 
class wineDialog:

"""This class is used to show wineDlg"""

def __init__(self, wine="", winery="", grape="", year=""):

#setup the glade file

self.gladefile = "pywine.glade"

#setup the wine that we will return

self.wine = Wine(wine,winery,grape,year)

 

   下一步我们需要为wineDialog添加一个函数, 它来从glade文件中装载wineDialg物件并显示. 我们还想要返回这个对话框的执行结果, 这会是一个gtk_RESPONSE, 你可以从PyGTK的网站上得到更多的信息.
   这个就是run 函数:
def run(self):

"""This function will show the wineDlg"""

#load the dialog from the glade file

self.wTree = gtk.glade.XML(self.gladefile, "wineDlg")

#Get the actual dialog widget

self.dlg = self.wTree.get_widget("wineDlg")

#Get all of the Entry Widgets and set their text

self.enWine = self.wTree.get_widget("enWine")

self.enWine.set_text(self.wine.wine)

self.enWinery = self.wTree.get_widget("enWinery")

self.enWinery.set_text(self.wine.winery)

self.enGrape = self.wTree.get_widget("enGrape")

self.enGrape.set_text(self.wine.grape)

self.enYear = self.wTree.get_widget("enYear")

self.enYear.set_text(self.wine.year)

#run the dialog and store the response

self.result = self.dlg.run()

#get the value of the entry fields

self.wine.wine = self.enWine.get_text()

self.wine.winery = self.enWinery.get_text()

self.wine.grape = self.enGrape.get_text()

self.wine.year = self.enYear.get_text()

#we are done with the dialog, destroy it

self.dlg.destroy()

#return the result and the wine

return self.result,self.wine
   你会注意到我们装载对话框的方法用的是和装载主窗口一样的方法. 我们调用gtk_glade_XML()并把我们想要装载物件的名称传给它. 这将会自动的显示这个对话框(和装载主窗口的行为相同), 但这样不够好, 我们想让run函数等待用户退出了这个对话框才返回. 要这么做我们需要从物件树里得到这个这个对话框物件(self.dlg = self.wTree.get_widget(“wineDlg”)),然后调用GTKDialogs 的run函数. 下面是PyGTK文档对这个函数的描述:

 Run()方法会在一个无穷的主循环里阻塞到它收到一个”response”信号或是销毁. 如果这个对话框是被销毁了, run ()方法会返回一个gtk_RESPONSE_NONE; 否则它返回一个在response信号中的响应id. 在进入这个无穷的主循环之前run ()方法会为你调用对话框的gtk_Widget_show()函数. 注意你仍然需要自己负责显示对话框的子窗口.
   在run()方法运行期间, delete事件的默认行为是被禁止的, 如果对话框收到一个delete的事件, 它并不会像窗口通常那样进行销毁, 这时run()方法会返回一个gtk_RESPONSE_DELETE_EVENT. 而且在run ()方法运行的时候对话框是模态的. 你可以通过调用responese()函数产生一个response信号来强制run ()方法在任何时候退出. 在run()方法运行的时候销毁对话框是很糟糕的主意, 因为你后面的代码无法知道对话框是不是已经销毁了.
   在run()方法返回后, 你需要负责对话框的隐藏和销毁.

    Ok按钮会返回gtk_RESPONSE_OK, Cancel按钮会返回gtk_RESPONSE_CANCEL, 大部分情况下我们仅关心对话框返回的wine信息是不是由用户点Ok按钮产生的.
   你还可以看到我们从对话框里得到GTKEntry物件来取/设置它们的文字. 总得来说这个函数还是相当简单的.

树状视图和列表存储(List Stores)

   现在我们有了用户想要往列表中添加的酒, 我们需要把它添加进gtk_TreeView中.
   GTKTreeViews的主要特性就是它们按照模型指定的任何方式来显示它们的数据. 数据模型可以用gtk_ListStore, gtk_TreeStore, gtk_TreeModelSort,或是gtk_GenericTreeModel. 在本例子中我们将使用 gkt_ListStore.
树状视图与模型的关系有些复杂,但是一旦你可以使用它你就会理解为什么它们是这样的. 很简单的讲模型表现数据,而树状视图则是一个简单的显示数据的方法. 所以对于同一份数据(模型)你可以有多个完全不同的视图. 下面是GTK+参考手册中的内容:

  在GTK+中要创建一个树或列表可以使用结合使用GtkTreeModel接口和GtkTreeView物件. 这个物件使用模型/视图/控制器模式设计, 它由以下四个主要部分组成:

树状视图物件(GtkTreeView)
列视图 (GtkTreeViewcolumn)
单元渲染器(GtkCellRenderer等等)
模型接口(GtkTreeModel)

  视图是由前面的三组对象组成, 最后一个是模型. MVC设计模式的一个主要收益是可以使用单个模型创建多个视图. 例如:由文件系统映射的模型(可能由一个文件管理器创建), 可以创建多种视图来显示文件系统的各个部分, 但仅需要在内存中保存一份拷贝

   我们要做的第一件是在自动把词典和物件树连接上后往pyWine类的__init__函数中添加一些代码:

#Here are some variables that can be reused later

self.cWine = 0

self.cWinery = 1

self.cGrape = 2

self.cYear = 3

self.sWine = "Wine"

self.sWinery = "Winery"

self.sGrape = "Grape"

self.sYear = "Year"

#Get the treeView from the widget Tree

self.wineView = self.wTree.get_widget("wineView")

#Add all of the List Columns to the wineView

self.AddWineListColumn(self.sWine, self.cWine)

self.AddWineListColumn(self.sWinery, self.cWinery)

self.AddWineListColumn(self.sGrape, self.cGrape)

self.AddWineListColumn(self.sYear, self.cYear)
    这个代码是相当直观的, 首先我们创建一些类似定义的变量(这样我们在后面就可以很轻松的改变它们)然后我们从物件树中得到gtk_TreeView. 在这之后我们通过调用一个新的函数来往列表中添加我们需要的列. AddWineListColumn是一个很快的小函数,但它能防止我们每次重复写创建列的代码.
def AddWineListColumn(self, title, columnId):
“””This function adds a column to the list view.
First it create the gtk.TreeViewColumn and then set
some needed properties”””

column = gtk.TreeViewColumn(title, gtk.CellRendererText()
, text=columnId)
column.set_resizable(True)
column.set_sort_column_id(columnId)
self.wineView.append_column(column)

  这个代码有一点点复杂, 首先我们创建一个使用gtk_CellRendererText作为它的gtk_CellRenderer的新gtk_TreeViewColumn. 这里是一些从GTK+参考手册里的较全的信息:
   一旦GtkTreeView物件有了一个模型, 它需要知道如何显示这个模型. 它通过列和单元渲染器来完成这项工作.
单元渲染器是用来使用某种方法在树状模型中绘图这些数据. 在GTK+2.x中有很多单元渲染器, 包括: GtkCellRenderText, GtkCellRendererPixbuf 和GtkCellRendererToggle. 写一个自定义的渲染器也相对简单.
   GtkTreeView使用GtkTreeViewColumn对象来组织在树状视图中纵的列. 它需要知道列的名字用来以标签的形式显示给用户, 使用单元渲染器的类型和对于一个给定的行它需要得到的数据块.
   因此简单的讲我们在创建一个包含特定标题的列, 并指明它将会使用gtk_CellRendererText(来显示简单的文字),然后再告诉它它与模型中的哪一项关联在一起.然后我们把它设置成可以改变大小而且用户可以通过点击列的头来对它进行排序. 在这之后我们把这一列加到视图中.
   好了, 我们完成了模型的创建. 我们要再回到pyWine类的__init__函数里继续:
#Create the listStore Model to use with the wineView

self.wineList = gtk.ListStore(str, str, str, str)

#Attatch the model to the treeView

self.wineView.set_model(self.wineList)
   大体上我们仅仅创建了一个gtk_ListStore并告诉它它有四个子项, 并且它们都是字符串. 然后我们把这个模型与视图绑定起来, 这就是所有我们需要为初始化gtk_TreeView做的.

把所有合并到一起  

 我们要做的最后一件事是完成pyWine类中的OnAddWine函数(从菜单或工具栏按钮调用). 这个函数很简单:

OnAddWine(self, widget):
“”"Called when the use wants to add a wine“”"
#Create the dialog, show it, and store the results
wineDlg = wineDialog();
result,newWine = wineDlg.run()

if (result == gtk.RESPONSE_OK):
“”"The user clicked Ok, so let’s add this
wine to the wine list
“”"
self.wineList.append(newWine.getList())

   这里我们创建了一个wineDialog的实例, 然后运行它并把用户输入的酒的信息保存.然后我们检查result是不是gtk_RESPONSE_OK(用户点击了Ok按钮), 如果是我们就把酒的信息添加到gtk_ListStore中, 它会自动的在gtk_TreeView中显示出来因此它们是连接在一起的.
   在wine类中我使用简单的getList函数以使阅读代码变得简单一些:

def getList(self):

"""This function returns a list made up of the

wine information.  It is used to add a wine to the

wineList easily"""

return [self.wine, self.winery, self.grape, self.year]

这里下载,你也可心浏览下面的完整程序:

#!/usr/bin/env python

import sys

try:

 import pygtk

  pygtk.require("2.0")

except:

  pass

try:

import gtk

  import gtk.glade

except:

sys.exit(1)

class pyWine:

"""This is an Hello World GTK application"""

def __init__(self):

#Set the Glade file

self.gladefile = "pywine.glade"

self.wTree = gtk.glade.XML(self.gladefile, "mainWindow")

#Create our dictionay and connect it

dic = {"on_mainWindow_destroy" : gtk.main_quit

, "on_AddWine" : self.OnAddWine}

self.wTree.signal_autoconnect(dic)

#Here are some variables that can be reused later

self.cWine = 0

self.cWinery = 1

self.cGrape = 2

self.cYear = 3

self.sWine = "Wine"

self.sWinery = "Winery"

self.sGrape = "Grape"

self.sYear = "Year"

#Get the treeView from the widget Tree

self.wineView = self.wTree.get_widget("wineView")

#Add all of the List Columns to the wineView

self.AddWineListColumn(self.sWine, self.cWine)

self.AddWineListColumn(self.sWinery, self.cWinery)

self.AddWineListColumn(self.sGrape, self.cGrape)

self.AddWineListColumn(self.sYear, self.cYear)

#Create the listStore Model to use with the wineView

self.wineList = gtk.ListStore(str, str, str, str)

#Attache the model to the treeView

self.wineView.set_model(self.wineList)

def AddWineListColumn(self, title, columnId):

"""This function adds a column to the list view.

First it create the gtk.TreeViewColumn and then set

some needed properties"""

column = gtk.TreeViewColumn(title, gtk.CellRendererText()

, text=columnId)

column.set_resizable(True)

column.set_sort_column_id(columnId)

self.wineView.append_column(column)

def OnAddWine(self, widget):

"""Called when the use wants to add a wine"""

#Cteate the dialog, show it, and store the results

wineDlg = wineDialog();

result,newWine = wineDlg.run()

if (result == gtk.RESPONSE_OK):

"""The user clicked Ok, so let's add this

wine to the wine list"""

self.wineList.append(newWine.getList())

class wineDialog:

"""This class is used to show wineDlg"""

def __init__(self, wine="", winery="", grape="", year=""):

#setup the glade file

self.gladefile = "pywine.glade"

#setup the wine that we will return

self.wine = Wine(wine,winery,grape,year)

def run(self):

"""This function will show the wineDlg"""

#load the dialog from the glade file

self.wTree = gtk.glade.XML(self.gladefile, "wineDlg")

#Get the actual dialog widget

self.dlg = self.wTree.get_widget("wineDlg")

#Get all of the Entry Widgets and set their text

self.enWine = self.wTree.get_widget("enWine")

self.enWine.set_text(self.wine.wine)

self.enWinery = self.wTree.get_widget("enWinery")

self.enWinery.set_text(self.wine.winery)

self.enGrape = self.wTree.get_widget("enGrape")

self.enGrape.set_text(self.wine.grape)

self.enYear = self.wTree.get_widget("enYear")

self.enYear.set_text(self.wine.year)

#run the dialog and store the response

self.result = self.dlg.run()

#get the value of the entry fields

self.wine.wine = self.enWine.get_text()

self.wine.winery = self.enWinery.get_text()

self.wine.grape = self.enGrape.get_text()

self.wine.year = self.enYear.get_text()

#we are done with the dialog, destory it

self.dlg.destroy()

#return the result and the wine

return self.result,self.wine

class Wine:

"""This class represents all the wine information"""

def __init__(self, wine="", winery="", grape="", year=""):

self.wine = wine

self.winery = winery

self.grape = grape

self.year = year

def getList(self):

"""This function returns a list made up of the

wine information.  It is used to add a wine to the

wineList easily"""

return [self.wine, self.winery, self.grape, self.year]

if __name__ == "__main__":

wine = pyWine()

gtk.main()