使用PyGtk和Glade构建应用程序
时间:2006-08-19 来源:pangwa
我的想法是创建一个可以让我记录我喝过酒的种类以及我对它们的喜好程度. 我写想这个程序很久了, 因此我想把学习PyGTK和写它结合起来会是一个好主意.
我把这个项目命名为PyWine. 本例程的全部代码和Glade文件可以从这里下载.
|
然后我添加了一个有4行的垂直盒子(Vertical Box), (从上到下): 一行放置菜单栏,一行放工具栏,一行放一个树状或列表视图, 最后一行旋转一个状态栏. 把树状视图命名为”wineView”.
500)this.width=500;" border=0> 我们刚添加的树形/列表视图会用来显示酒的信息. 因此我们要做的第一件事就是我们应当允许用户往对话框里添加一种酒. 我们需要使用一个菜单命令和工具栏按钮来完成这个任务.
首先添加一个工具栏按钮. 这只需要简单的在控件面板里选择”工具栏按钮”控件然后再点击我们往窗口里添加的工具栏. 这应该会往工具栏里添加一个样子很奇怪的按钮. 现在我们需要在”物件标签”(widget tab)来编辑这个工具栏按钮的属性. 我们把它的名字设置成”tbAddWine”, 图标设置成一个带加号的图标. (译注: 下面的图片可能与glade版本相关, 我这的图标名称为gtk-add, 请根据需要自己作少许变更:D). 500)this.width=500;" border=0> 现在我们要为tbAddWine按钮添加一个点击事件的处理器, 这次我们不使用默认的名字,把它命名为”on_AddWine”.
添加菜单栏
现在我们要对菜单作一些修改了, 因为我们希望人们既能够通过工具栏按钮或是通过菜单栏添加酒的条目. 那么就点击窗口上的菜单栏然后转到属性窗口中的物件标签页, 然后点”编辑菜单…”按钮来编辑菜单栏. (译注: 这里没有讲怎么往窗口添加菜单栏, 和添加工具栏一样, 从glade的面板里找:D).现在点击”添加”按钮来添加一个新的根菜单项, 把它的标签设置成”_Add”. 然后在菜单列表中选择这个”Add”项然后点击”添加子项”(Add Child)按钮, 这就会为Add菜单创建一个子菜单. 把这个新的菜单项的标签设置成”_Wine”并把它的事件处理器设成”on_AddWine”.
你可能会注意到工具栏按钮Add的点击事件处理器和菜单Add|Wine的点击事件处理器是一样的. 这是因为这两个物件都是要进行同样的任务: 往酒列表中添加酒的条目.
500)this.width=500;" border=0>
因此当用户点Add Wine按钮或是从菜单中选择Add|Wine时要发生就是弹出一个可以让用户添加一些有关酒详细信息的对话框. 如果他们最后选择Ok按钮那么就把他们输入的酒的信息添加进列表视图中.创建对话框
所以接下来我们要做的是创建一个可以让用户添加酒的对话框. 在这个例子中我们尽量让事情保持简单: 仅仅让用户输入酒的名字, 生产厂家, 装瓶的年代以及葡萄的类别.因此我们仅要通过点击Glade面板上的对话框按钮来创建一个新的对话框. 这就会弹出一个新建对话框窗口, 在里面选择包含Cancel和Ok按钮的”标准按钮布局”选项. 把对话框的名字设置成”wineDlg”, 窗口标题设置成”Add Wine”.
接下来我们要用一个表格来布置winedlg中的东西. 向对话框中添加表格(和通常添加物件的方法相同), 并把行数设成4, 列数设置成2. 然后我们就要用标签和文字输入物件来填充表格的空隙直到它看起来像这样:
500)this.width=500;" border=0> 我在表格的行间增加了3个像素的间隔, 这个设置可以在表格属性的物件标签页里找到. 如果你正在为怎么选择这个表格而犯愁, 你可以通过选择glade主窗口中的菜单栏”视图 | 显示物件树”(View|Show widget Tree). 或者按下shift键, 然后左键点击表格上的物件, 这会让你在这个对话框中的所有物件间循环.
现在我们要编辑这个对话框中的所有东西, 把它们命名为: enWine, enWinery, enGrape和enYear.
Python代码
现在我们要写一些让它工作的代码了. 我们把这个python文件命名为pywine.py, 把它放在/projects/Pywine目录中(和我们创建glade文件的目录相同). 下面是我们要用到的基本代码(大部分都是从上一个例程<使用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() |
下一步是创建一个用来存储酒信息的类:
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) |
这个就是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 |
Run()方法会在一个无穷的主循环里阻塞到它收到一个”response”信号或是销毁. 如果这个对话框是被销毁了, run ()方法会返回一个gtk_RESPONSE_NONE; 否则它返回一个在response信号中的响应id. 在进入这个无穷的主循环之前run ()方法会为你调用对话框的gtk_Widget_show()函数. 注意你仍然需要自己负责显示对话框的子窗口. |
你还可以看到我们从对话框里得到GTKEntry物件来取/设置它们的文字. 总得来说这个函数还是相当简单的.
树状视图和列表存储(List Stores)
现在我们有了用户想要往列表中添加的酒, 我们需要把它添加进gtk_TreeView中.GTKTreeViews的主要特性就是它们按照模型指定的任何方式来显示它们的数据. 数据模型可以用gtk_ListStore, gtk_TreeStore, gtk_TreeModelSort,或是gtk_GenericTreeModel. 在本例子中我们将使用 gkt_ListStore.
树状视图与模型的关系有些复杂,但是一旦你可以使用它你就会理解为什么它们是这样的. 很简单的讲模型表现数据,而树状视图则是一个简单的显示数据的方法. 所以对于同一份数据(模型)你可以有多个完全不同的视图. 下面是GTK+参考手册中的内容:
在GTK+中要创建一个树或列表可以使用结合使用GtkTreeModel接口和GtkTreeView物件. 这个物件使用模型/视图/控制器模式设计, 它由以下四个主要部分组成:
视图是由前面的三组对象组成, 最后一个是模型. 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) |
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() |
一旦GtkTreeView物件有了一个模型, 它需要知道如何显示这个模型. 它通过列和单元渲染器来完成这项工作. 单元渲染器是用来使用某种方法在树状模型中绘图这些数据. 在GTK+2.x中有很多单元渲染器, 包括: GtkCellRenderText, GtkCellRendererPixbuf 和GtkCellRendererToggle. 写一个自定义的渲染器也相对简单. GtkTreeView使用GtkTreeViewColumn对象来组织在树状视图中纵的列. 它需要知道列的名字用来以标签的形式显示给用户, 使用单元渲染器的类型和对于一个给定的行它需要得到的数据块. |
好了, 我们完成了模型的创建. 我们要再回到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] |
500)this.width=500;" border=0>
到这里这个简单程序就算完成了, 尽管它不保存任何信息或是任何和它类似的信息, 但是它确实描述了一些创建一个完整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 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() |