# 第十三章 建造列表控件并管理列表項
本章內容:
* 創建不同樣式的列表控件
* 處理列表中的項目
* 響應列表中用戶的選擇
* 編輯標簽和對列表排序
* 創建大的列表控件
在`wxPython`中,有兩個控件,你可以用來顯示基于列表的信息。較簡單的是列表框,它是可滾動的單列列表,類似于使用`HTML`的 `select?`標記所得到的。列表框已經在第8章中討論過了,本章不再作進一步的討論。
本章討論較為復雜的列表:列表控件,它是一個完整特性的列表窗口部件。這個列表控件可以每行顯示多列信息,并可基于任一行進行排序,還能以不同的樣式顯示。對于列表控件的每個部分的細節顯示,你有很大的靈活性。
## 建造一個列表控件
列表控件能夠以下面四種不同模式建造:
* 圖標(`icon)`
* 小圖標(`small?icon)`
* 列表(`list)`
* 報告(`report)`
如果你用過微軟`Windows`的資源管理器(`Explorer)`或`Mac`的`Finder`,那么這些方式你應該熟悉。我們將通過說明如何建立四種不同模式的列表作為開始來介紹列表控件。
### 什么是圖標模式?
列表控件看起來類似于微軟的`Windows`資源管理器的一個文件樹系統的顯示面板。它以四種模式之一的一種顯示一個信息的列表。默認模式是圖標模式,顯示在列表中的每個元素都是一個其下帶有文本的一個圖標。圖13.1顯示了一個圖標模式的列表。
例13.1是產生圖13.1的代碼。注意這個例子使用了同目錄下的一些.`png`文件。
**例13.1** **創建一個圖標模式的列表**
```
#-*- encoding:UTF-8 -*-
import wx
import sys, glob
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
"wx.ListCtrl in wx.LC_ICON mode",
size=(600,400))
# load some images into an image list
il = wx.ImageList(32,32, True)#創建圖像列表
for name in glob.glob("icon??.png"):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
# create the list control
#創建列表窗口部件
self.list = wx.ListCtrl(self, -1,
style=wx.LC_ICON | wx.LC_AUTOARRANGE)
# assign the image list to it
self.list.AssignImageList(il, wx.IMAGE_LIST_NORMAL)
# create some items for the list
#為列表創建一些項目
for x in range(25):
img = x % (il_max+1)
self.list.InsertImageStringItem(x,
"This is item %02d" % x, img)
app = wx.PySimpleApp()
frame = DemoFrame()
frame.Show()
app.MainLoop()
```
**圖13.1**

在例13.1中,`DemoFrame`創建了一個“`image?list`(圖像列表)”來包含對要顯示的圖像的引用,然后它建造并擴充了這個列表控件。我們將在本章稍后的部分討論“`image?list`(圖像列表)”。
### 什么是小圖標模式?
小圖標模式類似標準的圖標模式,但是圖標更小點。圖13.2以小圖標模式顯示了相同的列表。
當你想在窗口部件中放入更多的顯示項目時,小圖標模式是最有用的,尤其是當圖標不夠精細時。
**圖13.2**

產生圖13.2的示例代碼如下:
```
import wx
import sys, glob
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
"wx.ListCtrl in wx.LC_SMALL_ICON mode",
size=(600,400))
# load some images into an image list
il = wx.ImageList(16,16, True)
for name in glob.glob("smicon??.png"):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
# create the list control
self.list = wx.ListCtrl(self, -1,
style=wx.LC_SMALL_ICON
| wx.LC_AUTOARRANGE
)
# assign the image list to it
self.list.AssignImageList(il, wx.IMAGE_LIST_SMALL)
# create some items for the list
for x in range(25):
img = x % (il_max+1)
self.list.InsertImageStringItem(x,
"This is item %02d" % x,
img)
app = wx.PySimpleApp()
frame = DemoFrame()
frame.Show()
app.MainLoop()
```
### 什么是列表模式?
在列表模式中,列表以多列的形式顯示,一列到達底部后自動從下一列的上部繼續,如圖13.3所示。
列模式在相同元素的情況下,幾乎與小圖標模式所能容納的項目數相同。對這兩個模式的選擇,主要是根據你的數據是按列組織好呢還是按行組織好。
**圖13.3**

產生圖13.3的示例代碼如下:
```
import wx
import sys, glob
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
"wx.ListCtrl in wx.LC_LIST mode",
size=(600,400))
# load some images into an image list
il = wx.ImageList(16,16, True)
for name in glob.glob("smicon??.png"):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
# create the list control
self.list = wx.ListCtrl(self, -1, style=wx.LC_LIST)
# assign the image list to it
self.list.AssignImageList(il, wx.IMAGE_LIST_SMALL)
# create some items for the list
for x in range(25):
img = x % (il_max+1)
self.list.InsertImageStringItem(x,
"This is item %02d" % x,
img)
app = wx.PySimpleApp()
frame = DemoFrame()
frame.Show()
app.MainLoop()
```
在報告模式中,列表顯示為真正的多列格式,每行可以有任一數量的列,如圖13.4所示。
### 圖13.4

報告模式與圖標模式不盡相同。例13.2顯示了圖13.4的代碼。
**例13.2** **創建報告模式的一個列表**
```
#!/usr/bin/python
#-*- encoding:UTF-8 -*-
import wx
import sys, glob, random
import data
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
"wx.ListCtrl in wx.LC_REPORT mode",
size=(600,400))
il = wx.ImageList(16,16, True)
for name in glob.glob("smicon??.png"):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
self.list = wx.ListCtrl(self, -1, style=wx.LC_REPORT)#創建列表
self.list.AssignImageList(il, wx.IMAGE_LIST_SMALL)
# Add some columns
for col, text in enumerate(data.columns):#增加列
self.list.InsertColumn(col, text)
# add the rows
for item in data.rows:#增加行
index = self.list.InsertStringItem(sys.maxint, item[0])
for col, text in enumerate(item[1:]):
self.list.SetStringItem(index, col+1, text)
# give each item a random image
img = random.randint(0, il_max)
self.list.SetItemImage(index, img, img)
# set the width of the columns in various ways
self.list.SetColumnWidth(0, 120)#設置列的寬度
self.list.SetColumnWidth(1, wx.LIST_AUTOSIZE)
self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
self.list.SetColumnWidth(3, wx.LIST_AUTOSIZE_USEHEADER)
app = wx.PySimpleApp()
frame = DemoFrame()
frame.Show()
app.MainLoop()
```
注意:如果代碼中有中文或中文注釋,那么請在代碼開頭加上#-*- `encoding:UTF`-8 -*-
在接下來的部分,我們將討論如何將值插入適當的位置。報告控件是最適合用于那些包含一兩個附加的數據列的簡單列表,它的顯示邏輯沒有打算做得很復雜。如果你的列表控件復雜的話,或包含更多的數據的話,那么建議你使用`grid`控件,說明見第14章。
### 如何創建一個列表控件?
一個`wxPython`列表控件是類`wx.ListCtrl`的一個實例。它的構造函數與其它的窗口部件的構造函數相似:
`wx.ListCtrl(parent,?id,?pos=wx.DefaultPosition,?`
* `size=wx.DefaultSize,?style=wx.LC_ICON,?` `validator=wx.DefaultValidator,?name=`"`listCtrl`")
這些參數我們在其它的窗口部件的構造函數中見過。參數`parent`是容器部件,`id`是`wxPython`標識符,使用-1表明自動創建標識符。具體的布局由參數`pos`和`size`來管理。`style`控制模式和其它的顯示方案——貫穿本章,我們都將看到這些值。參數`validator`用于驗證特定的輸入,我們在第9章討論過。參數`name`我們很少使用。
樣式(`style`)標記是一個位掩碼,它管理列表控件的一些不同的特定。樣式標記的第一組值用于設置列表的顯示模式。默認模式是`wx.LC_ICON`。表13.1顯示了列表控件的模式值。
**表13.1** **列表控件模式值**
| | |
| --- | --- |
| `wx.LC_ICON` | 圖標模式,使用大圖標 |
| `wx.LC_LIST` | 列表模式 |
| `wx.LC_REPORT` | 報告模式 |
| `wx.LC_SMALL_ICON` | 圖標模式,使用小圖標 |
在圖標或小圖標列表中,有三個樣式標記用來控件圖標相對于列表對齊的。默認值是`wx.LC_ALIGN_TOP`,它按列表的頂部對齊。要左對齊的話,使用`wx.LC_ALIGN_LEFT`。樣式`LC_AUTOARRANGE`使得當圖標排列到達窗口右或底邊時自動換行或換列。
表13.2顯示了作用于報告列表顯示的樣式。
**表13.2** **報告列表的顯示樣式**
| | |
| --- | --- |
| `wx.LC_HRULES` | 在列表的行與行間顯示網格線(水平分隔線) |
| `wx.LC_NO_HEADER` | 不顯示列標題 |
| `wx.LC_VRULES` | 顯示列與列之間的網格線(豎直分隔線) |
樣式標記可以通過位運算符來組合。使用`wx.LC_REPORT`|`wx.LC_HRULES`|`wx.LC_VRULES`組合可以得到一個非常像網格的一個列表。默認情況下,所有的列表控件都允許多選。要使得一次只能選列表中的一個項目,可以使用標記`wx.LC_SINGLE_SEL`。
與我們見過的其它的窗口部件不同,列表控件增加了一對用于在運行時改變已存在的列表控件的樣式標記的方法。`SetSingleStyle(style,?add=True)`方法使你能夠增加或去掉一個樣式標記,這依賴于參數`add`的值。`listCtrl.SetSingleStyle(LC_HRULES,True)`將增加水平分隔線,而`listCtrl.SetSingleStyle(LC_HRULES,False)`將去掉水平分隔線。`listCtrl`代表具體的列表控件。`SetWindowStyleFlag(style)`能夠重置整個窗口的樣式,如`SetWindowStyleFlag(LC_REPORT?`| `LC_NO_HEADER)`。這些方法對于在運行時修改列表控件的樣式就有用處的。
## 處理列表中的項目
一旦列表控件被創建,你就能夠開始將信息添加到該列表中。在`wxPython`中,對于純文本信息和對與列表中的每個項目相關的圖像的處理是不同的。在接下來的幾節里,我們將如何添加圖像和文本到你的列表控件中。
### 什么是一個圖像列表以及如何將圖像添加給它?
在我們討論信息是如何被添加到列表控件之前,我們需要對列表如何控制圖像說兩句。任何使用在一個列表控件中的圖像,首先必須被添加到一個圖像列表,圖像列表是一個圖像索引數組,使用列表控件存儲。當一個圖像與列表中的一個特定項目相關聯時,圖像列表中的該圖像的索引被用來引用該圖像,而非使用圖像本身。該機制確保每個圖像只被裝載一次。這是為了在一個圖標被列表中的幾個項目重復使用時節約內存。它也允許相同圖像的多個版本之間的相對直接的連接,這些版本被用來表示不同的模式。關于創建`wxPython`圖像和位圖的更多的信息,請看第12章。
**創建一個圖像列表**
圖像列表是`wx.ImageList`的一個實例,構造函數如下:
`wx.ImageList(width,?height,?mask=True,?initialCount=1)?`
參數`width`和`height`指定了添加到列表中的圖像的像素尺寸。比指定大小大的圖像是不允許的。參數`mask`是一個布爾值。如果為`True`,假如圖像有遮罩,則使用遮罩繪制圖像。參數`initialCount`設置列表的初始的內在尺寸。如果你知道列表會很大,那么指定初始量可以獲得更多的內存分配以便稍后使用。
**添加及移去圖像**
你可以使用方法`Add(bitmap,?mask=wx.NullBitmap)`來將一個圖像添加到列表,參數`bitmap`和`mask`都是`wx.Bitmap`的實例。`mask`參數是一個單色位圖,它代表該圖像的透明部分,如果指定了`mask`參數的話。如果位圖已經有一個與之相關的遮罩,那么該遮罩被默認使用。如果位圖沒有一個遮罩,并且你不使用單色透明映射,但設置了該位圖的一個特定顏色作為這個透明色的話,那么你可以使用`AddWithColourMask(bitmap`,`colour)`方法,其中參數`colour`是用作遮罩的`wxPython`顏色(或它的顏色名)。如果你有一個`wx.Icon`對象要添加到圖像列表,可以使用方法`AddIcon(icon)`。所有這些添加方法都返回這個新加的圖像在列表中的索引值,你可以保留索引值以便日后使用該圖像。
下面的代碼片斷顯示了一個創建圖像列表的例子(類似于例13.1中的)。
```
il = wx.ImageList(32, 32, True)
for name in glob.glob("icon??.png"):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
```
然后這個圖像列表必須被賦給一個列表控件,使用下面的方法:
`self.list.AssignImageList(il,?wx.IMAGE_LIST_NORMAL)?`
要從圖像列表刪除一個圖像,可以使用`Remove(index)`方法,其中的`index`是圖像在圖像列表中的整數索引值。這個方法會修刪除點之后的圖像在圖像列表中的索引值,如果在你的程序中有對特定的索引存在依賴關系的話,這可能會導致一些問題。要刪除整個圖像列表,使用`RemoveAll()`。你可以使用方法`Replace(index,??bitmap,??mask=wx.NullBitmap)`修改特定索引相關的位圖,其中`index`是列表中要修改處的索引,`bitmap`和`mask`與`Add()`方法中的一樣。如果要修改的項目是一個圖標,可以使用方法`ReplaceIcon(index,?icon)`。這里沒有處理顏色遮罩的替換方法。
**使用圖像列表**
通過使用方法`GetImageCount()`,你能夠得到圖像列表的長度,使用`GetSize()`方法,你可以得到其中個個圖像的尺寸,它返回一個(`width,?height)`元組。
在列表控件上下文中沒有直接相關的圖像的時候,你也可以根據圖像列表繪制一個圖像到設備上下文中。關于設備上下文的更多信息,請看第6章和第12章。這個方法是`Draw`,如下所示:
```
Draw(index, dc, x, y, flags=wx.IMAGELIST_DRAW_NORMAL,
solidBackground=False)
```
在這個調用中,參數`index`是要繪制的項目在圖像列表中的索引,參數`dc`是要繪制到的一個`wx.DC`設備上下文。`flags`控制圖像被如何繪制,`flags`的可取值有`wx.IMAGELIST_DRAW_NORMAL,??wx.IMAGELIST_DRAW_TRANSPARENT,??wx.IMAGELISTDRAW_SelectED,?`和 `wx.IMAGELIST_DRAW_FOCUSED`。如果`solidBackground`為`True`,那么該繪制方法使用一個更快的算法工作。
一旦你有了一個圖像列表,你就需要將它附給列表控件。這個以通過后面的任一個方法來實現:`AssignImage(imageList,?which)`或`SetImage(imageList,?which)`。`imageList`參數是一個圖像列表,參數`which`是標記值:`wx.IMAGE_LIST_NORMAL?`或 `wx.IMAGE_LIST_SMALL`。這兩個方法的唯一的不同之處是C++對圖像列表的處理方面。對于`AssignImage()`,圖像列表變成了列表控件的一部分,并隨列表控件的銷毀而銷毀。對于`SetImage()`,圖像列表有自己的生命周期,當列表控件被銷毀時不被自動處理,只是當其`Python`對象退出作用域時,才被處理。
可以賦給列表控件兩個圖像列表。普通的圖像列表(使用了`wx.IMAGE_LIST_NORMAL`)被用于標準的圖標模式。小圖像列表(使用了`wx.IMAGE_LIST_SMALL`)被用于報告和小圖標模式。在大多數情況下,你只需要一個圖像列表,但是如果你希望列表以多模式顯示(這樣用戶可以從普通模式切換到小圖標模式),那么你應該兩個都提供。如果你這樣做了,那么記住,列表控件中的選項將只會經由圖像列表中的索引知道相關的圖像。如果文檔圖標在普通尺寸的圖像列表中有兩個索引,那么也必須在小圖像列表中有兩個索引。
關于列表控件還有一個相關的`get`*方法:`GetImageList(which)`,它返回與`which`標記參數相關的圖像列表。
### 如何對一個列表添加或刪除項目?
在你能顯示一個列表之前,你需要給它增加文本信息。在一個圖標列表中,你可以增加新的項目如圖標、字符串或兩個都添加。在一個報告視圖中,你也可以在設置了初始圖標和/或字符串后,為一行中的不同的列設置信息。用于處理列表控件項目的方法的`API`及其命名習慣與迄今為止我們所見過的其它一些控件的是有區別的,因此,盡管你已經理解了菜單或列表框是如何工作的,但是你仍將需要讀這一節。
對于一個圖標列表,增加文本信息到列表控件是一個單步的處理過程,但是對于一個報告列表就需要多步才行。通常對于每個列表,第一步是在行中增加第一個項目。對于報告列表,你必須分別地增加列和列中的信息,而非最左邊的一個。
**增加一個新行**
要增加一個新行,使用`InsertItem()`這類的一種方法。具體所用的方法依賴于你所插入的項目的類型。如果你僅僅插入一個字符串到列表中,使用`InsertStringItem(index,?label)`,其中的`index`是要插入并顯示新項目的行的索引。如果你只插入一個圖像,那么使用`InsertImageItem(index,?imageIndex)`。在這種情況下,這`index`是要插入圖像的行的索引,`imageIndex`是附加到該列表控件的圖像列表中的圖像的索引。要插入一個圖像項目,圖像列表必須已經被創建并賦值。如果你使用的圖像索引超出了圖像列表的邊界,那么你將得到一個空圖像。如果你想增加一個既有圖像又有字符串標簽的項目,使用`InsertImageStringItem(index,?label,?imageIndex)`。這個方法綜合了前面兩個方法的參數,參數的意義不變。
在內部,列表控件使用類`wx.ListItem`的實例來管理有關它的項目的信息。我還要說的是,最后一種插入項目到列表控件中方法是`InsertItem(index,?item)`,其中的`item`是`wx.ListItem`的一個實例。對于`wx.ListItem`,這里我們不將做很詳細的說明,這是因為你幾乎不會用到它并且該類也不很復雜——它幾乎都是由`get`*和`set`*方法組成的。一個列表項的幾乎所有屬性都可通過列表控件的方法來訪問。
**增加列**
要增加報告模式的列表控件的列,先要創建列,然后設置每行/列對的單獨的數據單元格。使用`InsertColumn()`方法創建列,它的語法如下:
```
InsertColumn(col, heading, format=wx.LIST_FORMAT_LEFT, width=-1)
```
在這個方法中,參數`col`是列表中的新列的索引,你必須提供這個值。參數`heading`是列標題。參數`format`控件列中文本的對齊方式,取值有:`wx.LIST_FORMAT_CENTRE`、`wx.LIST_FORMAT_LEFT`、和 `wx.LIST_FORMAT_RIGHT`。 參數`width`是列的初始顯示寬度(像素單位)——用戶可以通過拖動列的頭部的邊來改變它的寬度。要使用一個`wx.ListItem`對象來設置列的話,也有一個名為`InsertColumnInfo(info)`的方法,它要求一個列表項作為參數。
**設置多列列表中的值**
你可能已經注意到使用前面說明的行的方法來插入項目,對于一個多列的報告列表來說只能設置最初的那列。要在另外的列中設置字符串,可以使用方法`SetStringItem()`。
```
SetStringItem(index, col, label, imageId=-1)
```
參數`index`和`col`是你要設置的單元格的行和列的索引。你可以設定`col`為0來設置第一列,但是參數`index`必須對應列表控件中已有的行——換句話說,這個方法只能對已有的行使用。參數`label`是顯示在單元格中文本,參數`imageId`是圖像列表中的索引(如果你想在單元格中顯示一個圖像的話可以設置這個參數)。
`SetStringItem()`是`SetItem(info)`方法的一種特殊情況,`SetItem(info)`方法要求一個`wx.ListItem`實例。要使用這個方法,在將`wx.ListItem`實例增加到一個列表之前,要先設置它行,列和其它的參數。你也可以使用`GetItem(index,col=0)`方法來得到單元格處的`wx.ListItem`實例,默認情況下,該方法返回一行的第一列,你可以通過設置參數`col`來選擇其它列的一項。
**項目屬性**
有許多的`get`*和`set`*方法使你能夠指定部分項目。通常這些方法工作在一行的第一列上。要得工作在其它的列上,你需要使用`GetItem()`來得到項目,并使用項目類的`get`*和`set`*方法。你可以使用`SetItemImage(item,?image,?selImage)`來為一個項目設置圖像,其中的`item`參數是該項目在列表中的索引,`image`和`selImage`都是圖像列表中的索引,分別代表通常顯示的圖像和被選中時顯示的圖像。你可以通過使用`GetItemText(item)`和`SetItemText(item,text)`方法來得到或設置一個項目的文本。
你可以使用`GetItemState(item,stateMask)`和`SetItemState(item,?state,?stateMask)`來得到或設置單獨一個項目的狀態。`state`和`stateMask`的取值見表13.3。參數`state`(及`GetItemState`的返回值)是項目的實際狀態,`stateMask`是當前關注的所有可能值的一個掩碼。
你可以使用`GetColumn(col)`來得到一個指定的列,它返回索引`col`處的列的`wx.ListItem`實例。
**表13.3** **狀態掩碼參數**
狀態及說明如下:
| | |
| --- | --- |
| `wx.LIST_STATE_CUT` | 被剪切狀態。這個狀態只在微軟`Windows`下有效。 |
| `wx.LIST_STATE_DONTCARE` | 無關狀態。這個狀態只在微軟`Windows`下有效。 |
| `wx.LIST_STATE_DropHILITED` | 拖放狀態。項目顯示為高亮,這個狀態只在微軟`Windows`下有效。 |
| `wx.LIST_STATE_FOCUSED` | 獲得光標焦點狀態。 |
| `wx.LIST_STATE_SelectED` | 被選中狀態。 |
你也可以用`SetColumn(col,?item)`方法對一個已添加的列進行設置。你也可以在程序中用`GetColumnWidth(col)`方法方法得到一個列的寬度,該方法返回列表的寬度(像素單位)——顯然這只對報告模式的列表有用。你可以使用`SetColumnWidth(col,width)`來設置列的寬度。這個`width`可以是一個整數值或特殊值,這些特殊值有:`wx.LIST_AUTOSIZE`,它將列的寬度設置為最長項目的寬度,或`wx.LIST_AUTOSIZE_USEHEADER`,它將寬度設置為列的首部文本(列標題)的寬度。在非`Windows`操作系統下,`wx.LIST_AUTOSIZE_USEHEADER`可能只自動地將列寬度設置到80像素。
如果你對已有的索引不清楚了,你可以查詢列表中項目的數量。方法有`GetColumnCount()`,它返回列表中所定義的列的數量,`GetItemCount()`返回行的數量。如果你的列表是列表模式,那么方法`GetCountPerPage()`返回每列中項目的數量。
要從列表中刪除項目,使用`DeleteItem(item)`方法,參數`item`是項目在列表中的索引。如果你想一次刪除所有的項目,可以使用`DeleteAllItems()`或`ClearAll()`。你可以使用`DeleteColumn(col)`刪除一列,`col`是列的索引。
## 響應用戶
通常,一個列表控件在當用戶選擇了列表中的一個項目后都要做一些事情。在接下來的部分, 我們將展示一個列表控件都能響應哪些事件,并提供一個使用列表控件事件的例子。
### 如何響應用戶在列表中的選擇?
像別的控件一樣,列表控件也觸發事件以響應用戶的動作。你可以像我們在第三章那樣使用`Bind()`方法為這些 事件設置處理器。所有這些事件處理器都接受一個`wx.ListEvent`實例,`wx.ListEvent`是`wx.CommandEvent`的子類。`wx.ListEvent`有少量專用的`get`*方法。某些屬性只對特定的事件類型有效,這些特定的事件類型在本章的另外部分說明。適用于所有事件類型的屬性見表13.4。
**表13.4** **`wx.ListEvent`的屬性**
| | |
| --- | --- |
| `GetData()` | 與該事件的列表項相關的用戶數據項 |
| `GetKeyCode()` | 在一個按鍵事件中,所按下的鍵的鍵碼 |
| `GetIndex()` | 得到列表中與事件相關的項目的索引 |
| `GetItem()` | 得到與事件相關的實際的`wx.ListItem` |
| `GetImage()` | 得到與事件相關單元格中的圖像 |
| `GetMask()` | 得到與事件相關單元格中的位掩碼 |
| `GetPoint()` | 產生事件的實際的鼠標位置 |
| `GetText()` | 得到與事件相關的單元格中的文本 |
這兒有幾個關于`wx.ListEvent`的不同的事件類型,每個都可以有一個不同的處理器。某些關聯性更強的事件將在后面的部分討論。表13.5列出了選擇列表中的項目時的所有事件類型。
**表13.5** **與選擇一個列表控件中的項目相關的事件類型**
| | |
| --- | --- |
| `EVT_LIST_BEGIN_DRAG` | 當用戶使用鼠標左按鍵開始一個拖動操作時,觸發該事件 |
| `EVT_LIST_BEGIN_RDRAG` | 當用戶使用鼠標右按鍵開始一個拖動操作時,觸發該事件 |
| `EVT_LIST_Delete_ALL_ITEMS` | 調用列表的 `DeleteAll()`將觸發該事件 |
| `EVT_LIST_Delete_ITEM` | 調用列表的 `Delete()`將觸發該事件 |
| `EVT_LIST_Insert_ITEM` | 當一個項目被插入到列表中時,觸發該事件 |
| `EVT_LIST_ITEM_ACTIVATED` | 用戶通過在已選擇的項目上按下回車或雙擊來激活一個項目時 |
| `EVT_LIST_ITEM_DESelectED` | 當項目被取消選擇時觸發該事件 |
| `EVT_LIST_ITEM_FOCUSED` | 當項目的焦點變化時觸發該事件 |
| `EVT_LIST_ITEM_MIDDLE_CLICK` | 當在列表上敲擊了鼠標的中間按鈕時觸發該事件 |
| `EVT_LIST_ITEM_RIGHT_CLICK` | 當在列表上敲擊了鼠標的右按鈕時觸發該事件 |
| `EVT_LIST_ITEM_SelectED` | 當通過敲擊鼠標左按鈕來選擇一個項目時,觸發該事件 |
| `EVT_LIST_ITEM_KEY_DOWN` | 在列表控件已經獲得了焦點時,一個按鍵被按下將觸發該事件 |
下節中例13.3將提供一個關于上述事件中的一些事件的應用例子。
### 如何響應用戶在一個列的首部中的選擇?
除了用戶在列表體中觸發的事件以外,還有在報告列表控件的列首中所觸發的事件。列事件創建的`wx.ListEvent`對象有另一個方法:`GetColumn()`,該方法返回產生事件的列的索引。如果事件是一個列邊框的拖動事件,那么這個索引是所拖動的邊框的左邊位置。如果事件是一個敲擊所觸發的,且敲擊不在列內,那么該方法返回-1。表13.6包含了列事件類型的列表。
**表13.6** **列表控件列事件類型**
| | |
| --- | --- |
| `EVT_LIST_COL_BEGIN_DRAG` | 當用戶開始拖動一個列的邊框時,觸發該事件 |
| `EVT_LIST_COL_CLICK` | 列表首部內的一個敲擊將觸發該事件 |
| `EVT_LIST_COL_RIGHT_CLICK` | 列表首部內的一個右擊將觸發該事件 |
| `EVT_LiST_COL_END_DRAG` | 當用戶完成對一個列表邊框的拖動時,觸發該事件 |
例13.3顯示了一些列表事件的處理,并也提供了方法的一些演示。
**例13.3** **一些不同列表事件和屬性的一個例子**
```
import wx
import sys, glob, random
import data
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
"Other wx.ListCtrl Stuff",
size=(700,500))
self.list = None
self.editable = False
self.MakeMenu()
self.MakeListCtrl()
def MakeListCtrl(self, otherflags=0):
# if we already have a listctrl then get rid of it
if self.list:
self.list.Destroy()
if self.editable:
otherflags |= wx.LC_EDIT_LABELS
# load some images into an image list
il = wx.ImageList(16,16, True)
for name in glob.glob("smicon??.png"):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
# create the list control
self.list = wx.ListCtrl(self, -1, style=wx.LC_REPORT|otherflags)
# assign the image list to it
self.list.AssignImageList(il, wx.IMAGE_LIST_SMALL)
# Add some columns
for col, text in enumerate(data.columns):
self.list.InsertColumn(col, text)
# add the rows
for row, item in enumerate(data.rows):
index = self.list.InsertStringItem(sys.maxint, item[0])
for col, text in enumerate(item[1:]):
self.list.SetStringItem(index, col+1, text)
# give each item a random image
img = random.randint(0, il_max)
self.list.SetItemImage(index, img, img)
# set the data value for each item to be its position in
# the data list
self.list.SetItemData(index, row)
# set the width of the columns in various ways
self.list.SetColumnWidth(0, 120)
self.list.SetColumnWidth(1, wx.LIST_AUTOSIZE)
self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
self.list.SetColumnWidth(3, wx.LIST_AUTOSIZE_USEHEADER)
# bind some interesting events
self.Bind(wx.EVT_LIST_ITEM_SelectED, self.OnItemSelected, self.list)
self.Bind(wx.EVT_LIST_ITEM_DESelectED, self.OnItemDeselected, self.list)
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.list)
# in case we are recreating the list tickle the frame a bit so
# it will redo the layout
self.SendSizeEvent()
def MakeMenu(self):
mbar = wx.MenuBar()
menu = wx.Menu()
item = menu.Append(-1, "E \tAlt-X")
self.Bind(wx.EVT_MENU, self.OnExit, item)
mbar.Append(menu, " ")
menu = wx.Menu()
item = menu.Append(-1, "Sort ascending")
self.Bind(wx.EVT_MENU, self.OnSortAscending, item)
item = menu.Append(-1, "Sort descending")
self.Bind(wx.EVT_MENU, self.OnSortDescending, item)
item = menu.Append(-1, "Sort by submitter")
self.Bind(wx.EVT_MENU, self.OnSortBySubmitter, item)
menu.AppendSeparator()
item = menu.Append(-1, "Show selected")
self.Bind(wx.EVT_MENU, self.OnShowSelected, item)
item = menu.Append(-1, "Select all")
self.Bind(wx.EVT_MENU, self.OnSelectAll, item)
item = menu.Append(-1, "Select none")
self.Bind(wx.EVT_MENU, self.OnSelectNone, item)
menu.AppendSeparator()
item = menu.Append(-1, "Set item text colour")
self.Bind(wx.EVT_MENU, self.OnSetTextColour, item)
item = menu.Append(-1, "Set item background colour")
self.Bind(wx.EVT_MENU, self.OnSetBGColour, item)
menu.AppendSeparator()
item = menu.Append(-1, "Enable item editing", kind=wx.ITEM_CHECK)
self.Bind(wx.EVT_MENU, self.OnEnableEditing, item)
item = menu.Append(-1, "Edit current item")
self.Bind(wx.EVT_MENU, self.OnEditItem, item)
mbar.Append(menu, " ")
self.SetMenuBar(mbar)
def OnExit(self, evt):
self.Close()
def OnItemSelected(self, evt):
item = evt.GetItem()
print "Item selected:", item.GetText()
def OnItemDeselected(self, evt):
item = evt.GetItem()
print "Item deselected:", item.GetText()
def OnItemActivated(self, evt):
item = evt.GetItem()
print "Item activated:", item.GetText()
def OnSortAscending(self, evt):
# recreate the listctrl with a sort style
self.MakeListCtrl(wx.LC_SORT_ASCENDING)
def OnSortDescending(self, evt):
# recreate the listctrl with a sort style
self.MakeListCtrl(wx.LC_SORT_DESCENDING)
def OnSortBySubmitter(self, evt):
def compare_func(row1, row2):
# compare the values in the 4th col of the data
val1 = data.rows[row1][3]
val2 = data.rows[row2][3]
if val1 val2: return -1
if val1 val2: return 1
return 0
self.list.SortItems(compare_func)
def OnShowSelected(self, evt):
print "These items are selected:"
index = self.list.GetFirstSelected()
if index == -1:
print "\tNone"
return
while index != -1:
item = self.list.GetItem(index)
print "\t%s" % item.GetText()
index = self.list.GetNextSelected(index)
def OnSelectAll(self, evt):
for index in range(self.list.GetItemCount()):
self.list.Select(index, True)
def OnSelectNone(self, evt):
index = self.list.GetFirstSelected()
while index != -1:
self.list.Select(index, False)
index = self.list.GetNextSelected(index)
def OnSetTextColour(self, evt):
dlg = wx.ColourDialog(self)
if dlg.ShowModal() == wx.ID_OK:
colour = dlg.GetColourData().GetColour()
index = self.list.GetFirstSelected()
while index != -1:
self.list.SetItemTextColour(index, colour)
index = self.list.GetNextSelected(index)
dlg.Destroy()
def OnSetBGColour(self, evt):
dlg = wx.ColourDialog(self)
if dlg.ShowModal() == wx.ID_OK:
colour = dlg.GetColourData().GetColour()
index = self.list.GetFirstSelected()
while index != -1:
self.list.SetItemBackgroundColour(index, colour)
index = self.list.GetNextSelected(index)
dlg.Destroy()
def OnEnableEditing(self, evt):
self.editable = evt.IsChecked()
self.MakeListCtrl()
def OnEditItem(self, evt):
index = self.list.GetFirstSelected()
if index != -1:
self.list.EditLabel(index)
class DemoApp(wx.App):
def OnInit(self):
frame = DemoFrame()
self.SetTopWindow(frame)
print "Program output appears here..."
frame.Show()
return True
app = DemoApp(redirect=True)
app.MainLoop()
```
一旦你輸入上面的代碼并執行它,你將看到列表控件特性的演示,包括像項目的排序,這我們將在下一節討論。
在這一節,我們將討論對列表控件中的項目進行編輯、排序和想找。
### 如何編輯標簽?
除了報告列表外,編輯一個列表中的項目是簡單的,在報告列表中,用戶只能編輯一行的第一個一。而對于其它的列表,則沒有問題;每個項目的標準的標簽都是可編輯的。
要使一個列表是可編輯的,則當列表被創建時要在構造函數中包含樣?獎曇莧。
`list?=?wx.ListCtrl(self,?`-1, `style=wx.LC_REPORT?`| `wx.LC_EDIT_LABELS)`
如果這個編輯標記被設置了,那么用戶就能夠通過在一個已選擇的列表項上敲擊來開始一個編輯會話。編輯完后按下`Enter`鍵結束編輯會話,新的文本就變成了文本標簽。在列表控件中的鼠標敲擊也可結束編輯會話(一次只能有一個編輯會話)。按下`Esc`鍵則取消編輯會話,這樣的話,新輸入的文本就沒有用了。
下面的兩個事件類型是由編輯會話觸發的。
* `EVT_LIST_BEGIN_LABEL_EDIT?`
* `EVT_LIST_END_LABEL_EDIT`
記住,如果你想事件在被你的自定義的事件處理器處理后繼續被處理,那么你需要在你的事件處理器中包括`Skip()`調用。當用戶開始一個編輯會話時,一個`EVT_LIST_BEGIN_LABEL_EDIT`類型的列表事件被觸發,當會話結束時(通過使用`Enter`或`Esc`),`EVT_LIST_END_LABEL_EDIT`類型的列表事件被觸發。你可以否決(`veto)`編輯事件的開始,這樣編輯會話就不會開始了。否決編輯事件的結束將阻止列表文本的改變。
`wx.ListEvent`類有兩個屬性,這兩個屬性只在當處理一個`EVT_LIST_END_LABEL_EDIT`事件時才會用到。如果編輯結束并確認后,`GetLabel()`返回列表項目標簽的新文本,如果編輯被`Esc`鍵取消了,那么`GetLabel()`返回一個空字符串。這意味著你不能使用`GetLabel()`來區別“取消”和“用戶故意輸入的空字符串標簽”。如果必須的話,可以使用`IsEditCancelled()`,它在因取消而導致的編輯結束時返回`True`,否則返回`False`。
如果你想通過其它的用戶事件來啟動一個編輯會話的話,你可以在程序中使用列表控件的`EditLabel(item)`方法來觸發一個編輯。其中的`item`參數是是要被編輯的列表項的索引。該方法觸發`EVT_LIST_BEGIN_LABEL_EDIT`事件。
如果你愿意直接處理用于列表項編輯控件,你可以使用列表控件的方法`GetEditControl()`來得到該編輯控件。該方法返回用于當前編輯的文本控件。如果當前沒有編輯,該方法返回`None`。目前該方法只工作于`Windows`操作系統下。
**`boa`-`constructor`簡介**
`boa`-`constructor`是一個跨平臺的`Python`集成開發環境和`wxPython`圖形用戶界面構建器。它提供了可視化方式的框架(窗口)的創建和處理、對象檢視器(`object?inspector)`、編輯器、繼承的等級、`html`文檔字符串、高級的調試器和集成化的幫助系統。儼然一個用于`Python`的`Delphi`。
對的`Zope`支持:對象的創建和編輯。剪切,復制,粘貼,導入和導出。檢視器(`inspector)`和`Python`腳本調試中的創建和編輯。
`boa`-`constructor`是用`Python`和`wxPython`庫寫成的。使用它之前,你必須安裝了`wxPython?2.4.0.7`或`wxPython`更高的版本以及`Python?2.1`或`Python`的更高的版本。建議在使用`boa`-`constructor`之前,先入門`wxPython`。
`boa`-`constructor`項目位于`SourceForge`中。 下載`boa`-`constructor??`
### 如何對列表排序?
在`wxPython`中有三個有用的方法可以對列表進行排序,在這一節,我們將按照從易到難的順序來討論。
**在創建的時候告訴列表去排序**
對一個列表控件排序的最容易的方法,是在構造函數中告訴該列表控件對項目進行排序。你可以通過使用樣式標記`wx.LC_SORT_ASCENDING`或`wx.LC_SORT_DESCENDING`來實現。這兩個標記導致了列表在初始顯示的時候被排序,并且在`Windows`上,當新的項目被添加時,依然遵循所樣式標記來排序。對于每個列表項的數據的排序,是基于其字符串文本的,只是簡單的對字符串進行比較。如果列表是報告模式的,則排序是基于每行的最左邊的列的字符串的。
**基于數據而非所顯示的文本來排序**
有時,你想根據其它方面而非列表標簽的字符串來對列表排序。在`wxPython`中,你可以做到這一點,但這是較為復雜的。首先,你需要為列表中的每個項目設置項目數據,這通過使用`SetItemData(item,?data)`方法。參數`item`是項目在未排序的列表中的索引,參數`data`必須是一個整形或長整形的值(由于C++的數據類型的限制),這就有點限制了該機制的作用。如果要獲取某行的項目數據,可以使用方法`GetItemData(item)`。
一旦你設置了項目數據,你就可以使用方法`SortItems(func)`來排序項目。參數`func`是一個可調用的`Python`對象(函數),它需要兩個整數。`func`函數對兩個列表項目的數據進行比較——你不能得到行自身的引用。如果第一項比第二項大的話,函數將返回一個正整數,如果第一項比第二項小的話,返回一個負值,如果相等則返回0。盡管實現這個函數的最顯而易見的方法是只對這兩個項目做一個數字的比較就可以了,但是這并不唯一的排序方法。比如,數據項可能是外部字典或列表中的一個關鍵字,與該關鍵字相應的是一個更復雜的數據項,這種情況下,你可以通過比較與該關鍵字相應的數據項來排序。
**使用`mixin`類進行列排序**
關于對一個列表控件進行排序的常見的情況是,讓用戶能夠通過在報告模式的列表的任一列上進行敲擊來根據該列進行排序。你可以使用`SortItems()`機制來實現,但是它在保持到列的跟蹤方面有點復雜。幸運的是,一個名為`ColumnSorterMixin`的`wxPython`的`mixin`類可以為你處理這些信息,它位于`wx.lib.mixins.listctrl`模塊中。圖13.5顯示了使用該`mixin`類對列進行的排序。
**圖13.5**

聲明這個`mixin`就和`Python`中聲明任何其它的多重繼承一樣,如下所示:
```
import wx.lib.mixins.listctrl as listmix
class ListCtrlPanel(wx.Panel, listmix.ColumnSorterMixin):
def __init__(self, parent, log):
wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
self.list = TestListCtrl(self, tID)
self.itemDataMap = musicdata
listmix.ColumnSorterMixin.__init__(self, 3)
```
例13.4是圖13.5的例子代碼
**例13.4 使用`mixin`對一個報告列表進行排序**
```
#!/usr/bin/python
#-*- encoding:UTF-8 -*-
import wx
import wx.lib.mixins.listctrl
import sys, glob, random
import data
class DemoFrame(wx.Frame, wx.lib.mixins.listctrl.ColumnSorterMixin):#多重繼承
def __init__(self):
wx.Frame.__init__(self, None, -1,
"wx.ListCtrl with ColumnSorterMixin",
size=(600,400))
# load some images into an image list
il = wx.ImageList(16,16, True)
for name in glob.glob("smicon??.png"):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
# add some arrows for the column sorter
# 添加箭頭到圖像列表
self.up = il.AddWithColourMask(
wx.Bitmap("sm_up.bmp", wx.BITMAP_TYPE_BMP), "blue")
self.dn = il.AddWithColourMask(
wx.Bitmap("sm_down.bmp", wx.BITMAP_TYPE_BMP), "blue")
# create the list control
self.list = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
# assign the image list to it
self.list.AssignImageList(il, wx.IMAGE_LIST_SMALL)
# Add some columns
for col, text in enumerate(data.columns):
self.list.InsertColumn(col, text)
# add the rows
# 創建數據映射
self.itemDataMap = {}
for item in data.rows:
index = self.list.InsertStringItem(sys.maxint, item[0])
for col, text in enumerate(item[1:]):
self.list.SetStringItem(index, col+1, text)
# give each item a data value, and map it back to the
# item values, for the column sorter
self.list.SetItemData(index, index)# 關聯數據和映射
self.itemDataMap[index] = item
# give each item a random image
img = random.randint(0, il_max)
self.list.SetItemImage(index, img, img)
# set the width of the columns in various ways
self.list.SetColumnWidth(0, 120)
self.list.SetColumnWidth(1, wx.LIST_AUTOSIZE)
self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
self.list.SetColumnWidth(3, wx.LIST_AUTOSIZE_USEHEADER)
# initialize the column sorter
wx.lib.mixins.listctrl.ColumnSorterMixin.__init__(self,
len(data.columns))
def GetListCtrl(self):
return self.list
def GetSortImages(self):
return (self.dn, self.up)
app = wx.PySimpleApp()
frame = DemoFrame()
frame.Show()
app.MainLoop()
```
為了使用該`mixin`工作,你需要執行下面的東西:
1、擴展自`ColumnSorterMixin`的類(這里是`DemoFrame`)必須有一個名為`GetListCtrl()`的方法,它返回實際要被排序的列表控件。該方法被這個`mixin`用來得到控件的一個索引。
2、在擴展自`ColumnSorterMixin`的類(這里是`DemoFrame`)的`__init__()`方法中,在你調用`ColumnSorterMixin`的`__init__()`方法之前,你必須創建`GetListCtrl()`所要引用的列表控件。該`mixin`的`__init__()`方法要求一個代表列表控?械牧瀉諾惱怠? 3、你必須使用`SetItemData()`為列表中的每行設置一個唯一的數據值。
4、擴展自`ColumnSorterMixin`的類(這里是`DemoFrame`)必須有一個名為`itemDataMap`的屬性。該屬性必須是一個字典。字典中的關鍵性的東西是由`SetItemData()`設置的數據值。這些值是你想用來對每列進行排序的值的一個元組。(典型情況下,這些值將是每列中的文本)。按句話說,`itemDataMap`本質上是將控件中的數據復制成另一種易于排序的形式。
在`ColumnSorterMixin`的通常用法中,你要么創建`itemDataMap`用來添加項目到你的列表控件,要么你首先創建`itemDataMap`,并用它來建造列表控件本身。
盡管配置可能有點復雜,但`ColumnSorterMixin`對于列的排序是一個不錯的選擇。
### 進一步了解列表控件
有時候,在你的程序中的某處你需要確定列表中的哪個項目被選擇了,或者你需要通過編程來改變當前的選擇以響應用戶事件,或其它發生在你的程序中的一些事情。
有幾個與查找列表中的一個項目的索引相關的方法,它們提供了項目的一些信息,如表13.7所示。
表13.8顯示出了由`HitTest()`方法返回的可能的標記。實際應用中,可能返回不止一個標記。
**表13.7** **查找列表中的項目的方法**
| | |
| --- | --- |
| `FindItem(start,?str,?partial=False)` | 查找第一個與`str`匹配的項目。如果`start`為-1,那么搜索從頭開始,否則搜索從`start`的指定的索引處開始。如果`partial`為`True`,那么這個匹配是匹配以`str`頭的字符串,而非完全匹配。返回值是所匹配的字符串的索引。 |
| `FindItemAtPos(start`,`point,?direction)` | 查找與最接近位置點`point`的項目,`point`是一個`wx.Point`,它是相對于列表控件左上角的位置。參數`direction`是查找進行的方向。可能的取值有 `wx.LIST_FIND_DOWN,?wx.LIST_FIND_LEFT,?wx.LIST_FIND_RIGHT,?`和`wx.LIST_FIND_UP`。 |
| `FindItemData(start,data)` | 查找項目數據(使用`SetItemData()`設置的)與參數`data`匹配的項目。參數`start`同`FindItem()`。 |
| `HitTest(point)` | 返回一個(`index,?flags)`形式的`Python`元組。其中,`index`是項目在列表控件中的索引,如果沒有所匹配的項目,那么`index`為-1。`flags`包含了關于位置點和項目的進一步的信息。`flags`是一個位掩碼,其取值說明在表13.8中。 |
**表13.8** **關于`HitTest()`方法返回值中的標記**
| | |
| --- | --- |
| `wx.LIST_HITTEST_ABOVE` | 位置點在列表的客戶區域的上面。 |
| `wx.LIST_HITTEST_BELOW` | 位置點在列表的客戶區域的下面。 |
| `wx.LIST_HITTEST_NOWhere` | 位置點在列表的客戶區域中,但不屬于任何項目的部分。通常這是因為它是在列表的結尾處。 |
| `wx.LIST_HITTEST_ONITEM` | 位置點在項目的矩形區域中,(`index,?flags)`中的`index`是該項目的索引。 |
| `wx.LIST_HITTEST_ONITEMICON` | 位置點在項目的圖標區域中,(`index,?flags)`中的`index`是該項目的索引。 |
| `wx.LIST_HITTEST_ONITEMLABEL` | 位置點在項目的標簽區域中,(`index,?flags)`中的`index`是該項目的索引。 |
| `wx.LIST_HITTEST_ONITEMRIGHT` | 位置點在項目右邊的空白區域中。 |
| `wx.LIST_HITTEST_ONITEMSTATEICON` | 位置點在一個項目的狀態圖標中。我們這里假設列表是樹形的模式,并且存在一個用戶定義的狀態。 |
| `wx.LIST_HITTEST_TOLEFT` | 位置點在列表的客戶區域的左邊。 |
| `wx.LIST_HITTEST_TORIGHT` | 位置點在列表的客戶區域的右邊。 |
至于其它的方面,還有幾個方法,它們將為你提供關于所指定的項目的一些信息。方法`GetItem()`和`GetItemText()`方法我們早先已說過了,其它的見表13.9
**表13.9** **獲得列表控件的項目信息的方法**
| | |
| --- | --- |
| `GetItemPosition(item)` | 返回一個`wx.Point`,它是指定項目的位置。只用于圖標或小圖標模式。所返回的位置點是該項目位置的左上角。 |
| `GetItemRect(item,code=?wx.LIST_RECT_BOUNDS)` | 返回`item`所指定的項目的矩形區域 | `wx.Rect`。參數`code`是可選的。`code`的默認值是`wx.LIST_RECT_BOUNDS`,這使得`wxPython`返回項目的整個矩形區域。`code`的其它取值還有 | `wx.LIST_RECT_ICON`,它導致返回的只是項目的圖標部分的矩形區域,`wx.LIST_RECT_LABEL`,它導致返回的只是項目的標簽部分的矩形區域。 |
| `GetNextItem(item,?geometry=wx.LIST_ALL,?state=wx.LIST_STATE_DONTCARE?)` | 根據`geometry`和`state`參數,返回列表中位于`item`所指定的項目之后的下一個項目。其中的`geometry`和`state`參數,它們都有自己的取值,后面的列表將有說明。 |
| `SetItemPosition(item,?pos)` | 將`item`所指定的項目移動到`pos`所指定的位置處。只對圖標或小圖標模式的列表有意義。 |
表13.10列出了用于`GetNextItem()`的`geometry`參數的取值。`geometry`參數只用于微軟`Windows`下。
**表13.10** **`GetNextItem()`的`geometry`參數的取值**
| | |
| --- | --- |
| `wx.LIST_NEXT_ABOVE` | 查找顯示上位于開始項目之上的下一個為指定狀態的項目。 |
| `wx.LIST_NEXT_ALL` | 在列表中按索引的順序查找下一個為指定狀態的項目。 |
| `wx.LIST_NEXT_BELOW` | 查找顯示上位于開始項目之下的下一個為指定狀態的項目。 |
| `wx.LIST_NEXT_LEFT` | 查找顯示上位于開始項目左邊的下一個為指定狀態的項目。 |
| `wx.LIST_NEXT_RIGHT` | 查找顯示上位于開始項目右邊的下一個為指定狀態的項目。 |
表13.11列出了用于`GetNextItem()`的`state`參數的取值
**表13.11** **用于`GetNextItem()`的`state`參數的取值**
| | |
| --- | --- |
| `wx.LIST_STATE_CUT` | 只查找所選擇的用于剪貼板剪切和粘貼的項目。 |
| `wx.LIST_STATE_DONTCARE` | 查找項目,不管它當前的狀態。 |
| `wx.LIST_STATE_DropHILITED` | 只查找鼠標要釋放的項目。 |
| `wx.LIST_STATE_FOCUSED` | 只查找當前有焦點的項目。 |
| `wx.LIST_STATE_SelectED` | 只查找當前被選擇的項目。 |
表13.12顯示了用于改變一個項目的文本顯示的方法以及用于控件項目的字體和顏色的方法。
**表13.2** **列表控件的顯示屬性**
| | |
| --- | --- |
| `GetBackgroundColour()` | 處理整個列表控件的背景色。參數`col`是一個`wx.Colour`或顏色名。 |
| `SetBackgroundColour(col)` |
| `GetItemBackgroundColour(item)` | 處理索引`item`所指定的項目的背景色。這個屬性只用于報告模式。 |
| `SetItemBackgroundColour(item,col)` |
| `GetItemTextColour(item)` | 處理索引`item`所指定的項目的文本的顏色。這個屬性只用于報告模式。 |
| `SetItemTextColour(item,?col)` |
| `GetTextColour()` | 處理整個列表的文本的顏色。 |
| `SetTextColour(col)` |
表13.3顯示了列表控件的其它的一些方法。
**表13.3** **列表控件的其它的一些方法**
| | |
| --- | --- |
| `GetItemSpacing()` | 返回位于圖標間的空白的`wx.Size`。單位為像素。 |
| `GetSelectedItemCount()` | 返回列表中當前被選擇的項目的數量。 |
| `GetTopItem()` | 返回可見區域頂部的項目的索引。只在報告模式中有意義。 |
| `GetViewRect()` | 返回一個`wx.Rect`,它是能夠包含所有項目所需的最小矩形(沒有滾動條)。只對圖標或小圖標模式有意義。 |
| `ScrollList(dx,?dy)` | 使用控件滾動。參數`dy`是垂直量,`dx`是水平量,單位是像素。對于圖標、小圖標或報告模式,單位是像素。如果是列表模式,那么單位是列數。 |
上面的這些表涉及了一個列表控件的大多數功能。然而到目前為止,我們所見過的所有的列表控件,它們被限制為:在程序的運行期間,它們的所有數據必須存在于內存中。在下一節,我們將討論一個機制,這個機制僅在數據需要被顯示時,才提供列表數據。
## 創建一個虛列表控件
讓我們設想你的`wxPython`應用程序需要去顯示包含你所有客戶的一個列表。開始時你使用一個標準的列表控件,并且它工作的很好。后來人的客戶列表變得越來越大,太多的客戶使得你的應用程序開始出現了效率問題。這時你的程序起動所需的時間變得較長了,并占用越來越多的內存。你怎么辦呢?你可以創建一個虛的列表控件。
問題的實質就是列表控件的數據處理。通常,這些數據都是從數據產生的地方將數據拷貝到列表控件中。這是潛在地浪費資源,對于一個小的列表,這好象看不出任何問題,但對于創建一個較大的列表控件,這將占用很多的內存,并導致啟動變慢。
為了將一個列表控件所占的內存和啟動所需的時間降到最小化,`wxPython`允許你去聲明一個虛的列表控件,這意味關于每項的信息只在控件需要去顯示該項時才生成。這就防止了控件一開始就將每項存儲到它的內存空間中,并且這也意味著在啟動時,并沒有聲明完整的列表控件。同時這個方案的缺點就是虛列表中的列表項的恢復可能變得較慢。圖13.6顯示了一個虛列表。
例13.5顯示了產生該虛列表控件的完整代碼
**圖13.6**

**例13.5** **一個虛列表控件**
```
#!/usr/bin/python
#-*- encoding:UTF-8 -*-
import wx
import sys, glob, random
import data
class DataSource:#數據源
"""
A simple data source class that just uses our sample data items.
A real data source class would manage fetching items from a
database o similar.
"""
def GetColumnHeaders(self):
return data.columns
def GetCount(self):
return len(data.rows)
def GetItem(self, index):
return data.rows[index]
def UpdateCache(self, start, end):
pass
class VirtualListCtrl(wx.ListCtrl):#1 聲明虛列表
"""
A generic virtual listctrl that fetches data from a DataSource.
"""
def __init__(self, parent, dataSource):
wx.ListCtrl.__init__(self, parent,
style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VIRTUAL)#使用wx.LC_VIRTUAL標記創建虛列表
self.dataSource = dataSource
self.Bind(wx.EVT_LIST_CACHE_HINT, self.DoCacheItems)
self.SetItemCount(dataSource.GetCount())#設置列表的大小
columns = dataSource.GetColumnHeaders()
for col, text in enumerate(columns):
self.InsertColumn(col, text)
def DoCacheItems(self, evt):
self.dataSource.UpdateCache(
evt.GetCacheFrom(), evt.GetCacheTo())
def OnGetItemText(self, item, col):#得到需求時的文本
data = self.dataSource.GetItem(item)
return data[col]
def OnGetItemAttr(self, item): return None
def OnGetItemImage(self, item): return -1
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
"Virtual wx.ListCtrl",
size=(600,400))
self.list = VirtualListCtrl(self, DataSource())
app = wx.PySimpleApp()
frame = DemoFrame()
frame.Show()
app.MainLoop()
```
這個數據源的類是一個簡單的例子,它存儲了我們所需要的數據項。真實情況下的數據源類還會處理從一個數據庫中獲取數據之類的情況,這種情況下只需要重新實現本例中的同一接口。
要創建一個虛列表,第一步就是在初始化的時候對列表控件使用`wx.LC_VIRTUAL`標記如#1。通常使用子類`wx.ListCtrl`來創建你的虛列表控件,而非僅僅使用構造函數。這是因為你需要覆蓋`wx.ListCtrl`的一些方法,以便擴展這個虛列表。虛列表的聲明類似如下:
```
class MyVirtualList(wx.ListCtrl):
def __init__(self, parent):
wx.ListCtrl.__init__(self, parent, -1,
style=wx.LC_REPORT|wx.LC_VIRTUAL)
```
有時在虛列表的初始化期間,必須調用`SetItemCount()`方法。這將告訴控件在數據源中存在多少數據項,這樣它就可以設置適當的限制并處理滾動條。如果數據源中的數據項的數量改變了,你可以再調用`SetItemCount()`一次。你所覆蓋的任何以`On`開關的方法,必須能夠處理[0,`SetItemCount()`-1]間的數據。
你的虛列表控件可以覆蓋其父類的三個方法,以便決定在列表控件中顯示些什么。最重要的要覆蓋的方法是`OnGetItemText(item,?col)`。其中的參數`item`和`col`是要繪制的單元格的行和列,方法的返回值是顯示在該單元格中的文本字符串。例如,下面的方法將只顯示相關單元格的坐標。
```
def OnGetItemText(self, item, col):
return "Item %d, column %d" % (item, col)
```
如果你想在一行中顯示一個圖像,你需要覆蓋`OnGetItemImage(item)`。它的返回值是較早聲明的列表控件的圖像列中的一個整數索引。如果你沒有覆蓋這個方法,那么基類版本的`OnGetItemImage`將返回-1,這表明不顯示圖像。如果你想改變行的一些顯示屬性,那么你可以覆蓋`OnGetItemAttr(item)`方法,`item`是行的索引,該方法返回類`wx.ListItemAttr`的一個實例。該類有一些`get`*和`set`*方法可以用來設置行的顏色、對齊方式等等顯示屬性。
如果你的虛列表所基于的數據改變了,而你想更新顯示,那么你可以使用該列表控件的`RefreshItem(item)`來重繪特定的行。相關的方法`RefreshItems(itemFrom,itemTo)`重繪位于索引`itemFrom`和`itemTo`間的所有行。
為了對數據源中的數據的獲取提供優化幫助,對于要顯示一頁新的數據,虛列表控件會發送`EVT_LIST_CACHE_HINT`事件。這將給你的數據源一個時機用以從數據庫(或另處)一次獲取幾個記錄并保存它們。這樣就使得隨后的`OnGetItemText()`執行的更快。
## 本章小結
1、列表控件是`wxPython`用于顯示列表信息的窗口部件。它比簡單的列表框部件要更復雜且有完整的特性。列表控件是類`wx.ListCtrl`的實例。列表控件可以顯示為圖標模式,每個圖標下都有一個項目文本,也可以顯示為帶有小圖標的小圖標模式等等。在列表模式中,元素按列顯示,在報告模式中,以多列的格式顯示列表,每列都有列標簽。
2、用于列表控件的圖像是由一個圖像列表管理的,圖像列表是一個可經由索引來訪問的一個圖像的數組。列表控件可以為不同的列表模式維護各自的圖像列表,這使得能夠容易地在模式間切換。
3、你可以使用`InsertStringItem(index,label)`方法來插入文本到列表中,使用`InsertImageItem(index,??imageIndex)`方法插入圖像到列表中。要一次做上面兩件事,可以使用`InsertImageStringItem(index,label,`
* `imageIndex)`。要對報告模式的列表添加列,可以使用`InsertColumn(col,??heading,?format=`"`wx.LIST_FORMAT_LEFT,??width=`-1)方法。一旦已經添加了列后,你就可以使用`SetStringItem(index,?col,?label,?imageId=`-1)方法為新的列增加文本。
4、列表控件產生的幾個事件可以被綁到程序的動作。這些事件項屬于類`wx.ListEvent`。通常的事件類型包括`EVT_LIST_Insert_ITEM,?EVT_LIST_ITEM_ACTIVATED,`和`EVT_LIST_?ITEM_SelectED`。
5、如果列表控件聲明時使用了`wx.LC_EDIT_LABELS`標記,那么用戶就可以編輯列表項的文本。編輯的確認是通過按下回車鍵或在列表中敲擊完成的,也可以通過按下`Esc`鍵來取消編輯。
6、你可以通過在聲明列表時使用`wx.LC_SORT_ASCENDING`或`wx.LC_SORT_DESCENDING`來排序列表。這將按照項目的字符串的順序來排序列表。在報告模式中,這將根據0列的字符串來排序。你也可以使用`SortItems(func)`方法來創建你自定義的排序方法。對于報告模式的列表,`mixin`類`wx.lib.mixins.listctrl.ColumnSorterMixin`給了你根據用戶所選擇的列來排序的能力。
7、使用了標記`wx.LC_VIRTUAL`聲明的列表控件是一個虛列表控件。這意味著它的數據是當列表中的項目被顯示時動態地確定的。對于虛列表控件,你必須覆蓋`OnGetItemText(item,?col)`方法以返回適當的文本給所顯示的行和列。你也可以使用`OnGetItemImage(item)`和`OnGetItemAttr(item)`方法來返回關于每行的圖像或列表的顯示屬性。如果數據源的數據改變了,你可以使用`RefreshItem(item)`方法來更新列表的某個行或使用`RefreshItems(itemFrom,?itemTo)`方法來更新多個行。
最終,你的數據將變得復雜得不能放在一個簡單的列表中。你將會需要類似二維的電子表格樣式的東西,這就是網格控件,我們將在下一章進行討論。
- 活學活用wxPython
- 前言
- 致謝
- 關于本書
- 第一部分
- 第一章 歡迎使用wxPython
- 第二章 給wxPython程序一個堅實的基礎
- 第三章 在事件驅動環境中開發
- 第四章 用PyCrust使得wxPython更易處理
- 第五章 繪制藍圖
- 第六章 使用wxPython基本構件
- 第二部分 基礎wxPython
- 第七章 使用基礎控件
- 第八章 將構件放入窗體中
- 第九章 通過對話框讓用戶選擇
- 第十章 創建和使用wxPython菜單
- 第十一章 使用sizer放置構件
- 第十二章 操作基本圖像
- 第三部分 高級wxPython
- 第十三章 建造列表控件并管理列表項
- 第十四章 網格控件
- 第十五章 樹形控件
- 第十六章 在應用程序中加入HTML
- 第十七章 wxPython的打印構架
- 第十八章 使用wxPython的其他功能