wxpython入門第六步(高級組件)

wxpython高級組件

在這一章中,我們將討論以下高級部件:wx.ListBox、wx.html.HtmlWindow、wx.ListCtrl。

wxPython有幾個著名的高級部件。例如樹形組件、HTML 窗口、網格部件、listbox 部件、列表部件或具有高級樣式功能的編輯器。

wx.ListBox 組件

wx.ListBox 用於顯示和處理一個項目列表。 wx.ListBox 可以在兩種不同的狀態下創建:單選狀態或多選狀態。單選狀態是默認狀態。

wx.ListBox 中有兩個重要事件。第一個是 wx.EVT_COMMAND_LISTBOX_SELECTED 事件。當我們在 wx.ListBox 中選擇一個項目時,就會產生這個事件。第二個是 wx.EVT_COMMAND_LISTBOX_DOUBLE_CLICKED 事件。當我們雙擊 wx.ListBox 中的一個項目時,就會產生這個事件。元素從零開始編號。如果需要,滾動條會自動顯示。

很多人學習python,不知道從何學起。

很多人學習python,掌握了基本語法過後,不知道在哪裏尋找案例上手。

很多已經做案例的人,卻不知道如何去學習更加高深的知識。

那麼針對這三類人,我給大家提供一個好的學習平臺,免費領取****,電子書籍,以及課程的源代碼!??¤

QQ羣:1057034340

我們創建一個空的wx.ListBox。我們在列表框周圍加上一個20px的邊框。

self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnRename)

我們使用wx.EVT_COMMAND_LISTBOX_DOUBLE_CLICKED事件綁定器與OnRename()方法綁定一個wx.EVT_LISTBOX_DCLICK事件類型。這樣,如果我們雙擊列表框中的一個特定元素,我們就會顯示一個重命名對話框。

我們通過點擊New按鈕來調用NewItem()方法。該方法使用包裝器wx.GetTextFromUser()方法顯示了一個wx.TextEntryDialog。我們輸入的文本會返回到文本變量中。如果文本不是空的,我們就用Append()方法將其追加到列表框中。

我們通過刪除一個項目,並在同一位置插入一個新的項目來重新命名它。我們還將選區設置回修改後的項目。

要刪除一個項目,我們通過調用GetSelection()方法找到所選項目的索引。然後我們用Delete()方法刪除這個項目。Delete()方法的參數是選中的索引。

最簡單的就是清除整個列表框。我們只需調用Clear()方法。

wx.html.HtmlWindow 組件

wx.html.HtmlWindow 組件顯示HTML頁面。它不是一個成熟的瀏覽器。我們可以用 wx.html.HtmlWindow 組件做一些有趣的事情。

例如,在下面的程序中,我們創建了一個顯示基本統計數據的窗口。

這是要顯示的HTML頁面。

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201030141835060.png" alt="image-20201030141835060" style="zoom:50%;" />

本例在wx.html.HtmlWindow widget中派發一個HTML文件。

htmlwin = wx.html.HtmlWindow(panel, wx.ID_ANY, style=wx.NO_BORDER)
htmlwin.SetStandardFonts()
htmlwin.LoadPage("page.html")

wx.html.HtmlWindow被創建。用LoadPage()方法加載HTML文件。

Help window

我們可以使用wx.html.HtmlWindow來爲我們的應用程序提供幫助。我們可以創建一個獨立的窗口,也可以創建一個將成爲應用程序一部分的窗口。下面的腳本將使用後者來創建一個幫助窗口。

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201030142310825.png" alt="image-20201030142310825" style="zoom:50%;" />

幫助窗口在一開始是隱藏的,我們可以通過點擊工具欄上的幫助按鈕或按F1鍵來顯示它。我們可以通過點擊工具欄上的 "幫助 "按鈕或按F1鍵來顯示它。幫助窗口就會出現在應用程序的右側。要隱藏幫助窗口,我們點擊關閉按鈕。

self.splitter.SplitVertically(self.panelLeft, self.panelRight)
self.splitter.Unsplit()

我們創建左面板和右面板,並將它們垂直分割。之後,我們調用 Unsplit() 方法。默認情況下,該方法會隱藏右側或底部面板。

我們將右面板分爲兩個部分。頭部和麪板的主體。頭部是一個調整後的 wx.Panel 。頭部由一個靜態文本和一個位圖按鈕組成。我們把 wx.html.Window 放到面板的主體中。

closeBtn = wx.BitmapButton(header, wx.ID_ANY, wx.Bitmap('closebutton.png',
      wx.BITMAP_TYPE_PNG), style=wx.NO_BORDER)
closeBtn.SetBackgroundColour('#6f6a59')

位圖按鈕樣式設置爲wx.NO_BORDER。背景顏色被設置爲標題面板的顏色,這樣做是爲了使按鈕看起來像標題的一部分。這樣做的目的是爲了使按鈕作爲頭的一部分出現。

helpWin = html.HtmlWindow(self.panelRight, style=wx.NO_BORDER)
helpWin.LoadPage('page.html')

我們在右側面板上創建一個 wx.html.HtmlWindow 小部件。我們的HTML代碼在一個單獨的文件中。這次我們調用 LoadPage() 方法來獲取HTML代碼。

self.panelLeft.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
self.panelLeft.SetFocus()

我們將焦點設置在左側面板。我們可以用F1鍵啓動幫助窗口。爲了用鍵盤控制一個窗口,它必須有焦點。如果我們不設置焦點,我們就必須先點擊面板,只有這樣我們才能用按F1鍵啓動幫助窗口。

def OnHelp(self, e):

    self.splitter.SplitVertically(self.panelLeft, self.panelRight)
    self.panelLeft.SetFocus()

爲了顯示幫助窗口,我們調用OnHelp()方法。它將兩個面板垂直分割。我們不要忘記再次設置焦點,因爲初始焦點會因爲分割而丟失。

wx.ListCtrl 組件

wx.ListCtrl是一個項目列表的圖形表示。一個wx.ListBox只能有一列,而wx.ListCtrl可以有多於一列。例如,一個文件管理器使用wx.ListCtrl來顯示文件系統中的目錄和文件。一個CD刻錄程序在wx.ListCtrl中顯示要刻錄的文件。

一個wx.ListCtrl可以以三種不同的樣式使用。列表視圖、報告視圖或圖標視圖。這些樣式由wx.ListCtrl窗口樣式控制,wx.LC_REPORT、wx.LC_LIST和wx.LC_ICON。

wx.ListCtrl 樣式

  • wx.LC_LIST
  • wx.LC_REPORT
  • wx.LC_VIRTUAL
  • wx.LC_ICON
  • wx.LC_SMALL_ICON
  • wx.LC_ALIGN_LEFT
  • wx.LC_EDIT_LABELS
  • wx.LC_NO_HEADER
  • wx.LC_SORT_ASCENDING
  • wx.LC_SORT_DESCENDING
  • wx.LC_HRULES
  • wx.LC_VRULES

wx.ListCtrl 例子

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201030142803996.png" alt="image-20201030142803996" style="zoom:50%;" />

該代碼示例在 wx.ListCtrl 中顯示有關女演員的數據。

self.list = wx.ListCtrl(panel, wx.ID_ANY, style=wx.LC_REPORT)

我們創建一個具有 wx.LC_REPORT 風格的 wx.ListCtrl 。

self.list.InsertColumn(0, 'name', width=140)
self.list.InsertColumn(1, 'place', width=130)
self.list.InsertColumn(2, 'year', wx.LIST_FORMAT_RIGHT, 90)

我們插入三列。我們可以指定列的寬度和列的格式。默認的格式是wx.LIST_FORMAT_LEFT。

idx = 0

for i in data:

    index = self.list.InsertItem(idx, i[0])
    self.list.SetItem(index, 1, i[1])
    self.list.SetItem(index, 2, i[2])
    idx += 1

我們使用兩個方法將數據插入到 wx.ListCtrl 中。每一行都以 InsertItem() 方法開始。該方法的第一個參數指定了行號。該方法返回行的索引。 SetItem() 方法將數據添加到當前行的連續列中。

Mixins

Mixins是進一步增強 wx.ListCtrl 功能的類。它們位於 wx.lib.mixins.listctrl 模塊中。爲了使用它們,我們必須繼承這些類的功能。

有六個 mixins。

  • wx.ColumnSorterMixin
  • wx.ListCtrlAutoWidthMixin
  • wx.ListCtrlSelectionManagerMix
  • wx.TextEditMixin
  • wx.CheckListCtrlMixin
  • wx.ListRowHighlighter

wx.ColumnSorterMixin 是一個能夠在報表視圖中對列進行排序的mixin。 wx.ListCtrlAutoWidthMixin 類可以自動調整最後一列的大小到 wx.ListCtrl 的末端。默認情況下,最後一列不佔用剩餘空間。請看前面的例子。 wx.ListCtrlSelectionManagerMix 定義了獨立於平臺的選擇策略。 wx.TextEditMixin 可以編輯文本。 wx.CheckListCtrlMixin 爲每一行添加一個複選框。這樣我們就可以控制行。我們可以將每一行設置爲選中或不選中。 wx.ListRowHighlighter 處理 wx.ListCtrl 中交替行的自動背景高亮。

wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin

下面的代碼顯示了我們如何使用 wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin

#autowidth.py

import wx
import wx.lib.mixins.listctrl

data = [('Jessica Alba', 'Pomona', '1981'), ('Sigourney Weaver', 'New York', '1949'),
  ('Angelina Jolie', 'Los Angeles', '1975'), ('Natalie Portman', 'Jerusalem', '1981'),
  ('Rachel Weiss', 'London', '1971'), ('Scarlett Johansson', 'New York', '1984')]


class AutoWidthListCtrl(wx.ListCtrl, wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin):

    def __init__(self, parent, *args, **kw):
        wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT)
        wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.__init__(self)
class Example(wx.Frame):

    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)
        
        self.InitUI()

    def InitUI(self):        

        hbox = wx.BoxSizer(wx.HORIZONTAL)

        panel = wx.Panel(self)

        self.list = AutoWidthListCtrl(panel)
        self.list.InsertColumn(0, 'name', width=140)
        self.list.InsertColumn(1, 'place', width=130)
        self.list.InsertColumn(2, 'year', wx.LIST_FORMAT_RIGHT, 90)

        idx = 0

        for i in data:

            index = self.list.InsertItem(idx, i[0])
            self.list.SetItem(index, 1, i[1])
            self.list.SetItem(index, 2, i[2])
            idx += 1

        hbox.Add(self.list, 1, wx.EXPAND)
        panel.SetSizer(hbox)

        self.SetTitle('Actresses')
        self.Centre()


def main():

    app = wx.App()
    ex = Example(None)
    ex.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201030143105149.png" alt="image-20201030143105149" style="zoom:50%;" />

我們把前面的例子改一下。

import wx.lib.mixins.listctrl

這裏我們導入 mixin 模塊。

class AutoWidthListCtrl(wx.ListCtrl, wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin):

    def __init__(self, parent, *args, **kw):
        wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT)
        wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.__init__(self)

我們創建一個新的 AutoWidthListCtrl 類。這個類繼承自 wx.ListCtrl 和 wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin 。這就是所謂的 多繼承 。最後一列會自動調整大小以佔用 wx.ListCtrl 的剩餘寬度。

wx.lib.mixins.listctrl.ColumnSorterMixin

下面的例子創建了可排序的列。如果我們點擊列頭,列中相應的行就會被排序。

#sorted.py

import wx
import wx.lib.mixins.listctrl

actresses = {
1 : ('Jessica Alba', 'Pomona', '1981'),
2 : ('Sigourney Weaver', 'New York', '1949'),
3 : ('Angelina Jolie', 'Los Angeles', '1975'),
4 : ('Natalie Portman', 'Jerusalem', '1981'),
5 : ('Rachel Weiss', 'London', '1971'),
6 : ('Scarlett Johansson', 'New York', '1984')
}


class SortedListCtrl(wx.ListCtrl, wx.lib.mixins.listctrl.ColumnSorterMixin):

    def __init__(self, parent):

        wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT)
        wx.lib.mixins.listctrl.ColumnSorterMixin.__init__(self, len(actresses))
        self.itemDataMap = actresses

    def GetListCtrl(self):
        return self
        

class Example(wx.Frame):

    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)
        
        self.InitUI()

    def InitUI(self):        

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        panel = wx.Panel(self)

        self.list = SortedListCtrl(panel)
        self.list.InsertColumn(0, 'name', width=140)
        self.list.InsertColumn(1, 'place', width=130)
        self.list.InsertColumn(2, 'year', wx.LIST_FORMAT_RIGHT, 90)

        items = actresses.items()

        idx = 0

        for key, data in items:

            index = self.list.InsertItem(idx, data[0])
            self.list.SetItem(index, 1, data[1])
            self.list.SetItem(index, 2, data[2])
            self.list.SetItemData(index, key)
            idx += 1

        hbox.Add(self.list, 1, wx.EXPAND)
        panel.SetSizer(hbox)

        self.SetTitle('Actresses')
        self.Centre()


def main():

    app = wx.App()
    ex = Example(None)
    ex.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201030143320812.png" alt="image-20201030143320812" style="zoom:50%;" />

我們再以女演員爲例。

wx.lib.mixins.listctrl.ColumnSorterMixin.__init__(self, len(actresses))

wx.lib.mixins.listctrl.ColumnSorterMixin 接受一個參數:要排序的列數。

self.itemDataMap = actresses

我們必須將我們要顯示在列表控件中的數據映射到 itemDataMap 屬性中。數據必須是一個字典數據類型。

def GetListCtrl(self):
    return self

我們必須創建一個GetListCtrl()方法。這個方法返回要排序的wx.ListCtrl部件。

self.list.SetItemData(index, key)

我們必須爲每一行分配一個特殊的索引。這是通過 SetItemData 方法完成的。

wx.lib.mixins.listctrl.CheckListCtrl

列表控件中可以放置一個複選框。在wxPython中,我們可以使用 wx.lib. mixins.listctrl.CheckListCtrl 。

#repository.py

import wx
from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin

packages = [('abiword', '5.8M', 'base'), ('adie', '145k', 'base'),
    ('airsnort', '71k', 'base'), ('ara', '717k', 'base'), ('arc', '139k', 'base'),
    ('asc', '5.8M', 'base'), ('ascii', '74k', 'base'), ('ash', '74k', 'base')]

class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):

    def __init__(self, parent):
        wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT |
                wx.SUNKEN_BORDER)
        CheckListCtrlMixin.__init__(self)
        ListCtrlAutoWidthMixin.__init__(self)


class Example(wx.Frame):

    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)

        panel = wx.Panel(self)

        vbox = wx.BoxSizer(wx.VERTICAL)
        hbox = wx.BoxSizer(wx.HORIZONTAL)

        leftPanel = wx.Panel(panel)
        rightPanel = wx.Panel(panel)

        self.log = wx.TextCtrl(rightPanel, style=wx.TE_MULTILINE|wx.TE_READONLY)
        self.list = CheckListCtrl(rightPanel)
        self.list.InsertColumn(0, 'Package', width=140)
        self.list.InsertColumn(1, 'Size')
        self.list.InsertColumn(2, 'Repository')

        idx = 0

        for i in packages:

            index = self.list.InsertItem(idx, i[0])
            self.list.SetItem(index, 1, i[1])
            self.list.SetItem(index, 2, i[2])
            idx += 1

        vbox2 = wx.BoxSizer(wx.VERTICAL)

        selBtn = wx.Button(leftPanel, label='Select All')
        desBtn = wx.Button(leftPanel, label='Deselect All')
        appBtn = wx.Button(leftPanel, label='Apply')

        self.Bind(wx.EVT_BUTTON, self.OnSelectAll, id=selBtn.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnDeselectAll, id=desBtn.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnApply, id=appBtn.GetId())

        vbox2.Add(selBtn, 0, wx.TOP|wx.BOTTOM, 5)
        vbox2.Add(desBtn, 0, wx.BOTTOM, 5)
        vbox2.Add(appBtn)

        leftPanel.SetSizer(vbox2)

        vbox.Add(self.list, 4, wx.EXPAND | wx.TOP, 3)
        vbox.Add((-1, 10))
        vbox.Add(self.log, 1, wx.EXPAND)
        vbox.Add((-1, 10))

        rightPanel.SetSizer(vbox)

        hbox.Add(leftPanel, 0, wx.EXPAND | wx.RIGHT, 5)
        hbox.Add(rightPanel, 1, wx.EXPAND)
        hbox.Add((3, -1))

        panel.SetSizer(hbox)

        self.SetTitle('Repository')
        self.Centre()

    def OnSelectAll(self, event):

        num = self.list.GetItemCount()
        for i in range(num):
            self.list.CheckItem(i)

    def OnDeselectAll(self, event):

        num = self.list.GetItemCount()
        for i in range(num):
            self.list.CheckItem(i, False)

    def OnApply(self, event):

        num = self.list.GetItemCount()

        for i in range(num):

            if i == 0: self.log.Clear()

            if self.list.IsChecked(i):
                self.log.AppendText(self.list.GetItemText(i) + '\n')


def main():

    app = wx.App()
    ex = Example(None)
    ex.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201030143806315.png" alt="image-20201030143806315" style="zoom:50%;" />

這個例子用 wx.lib.mixins.listctrl.CheckListCtrl 創建了一個版本庫UI。

class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):

    def __init__(self, parent):
        wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT |
                wx.SUNKEN_BORDER)
        CheckListCtrlMixin.__init__(self)
        ListCtrlAutoWidthMixin.__init__(self)

我們從三個不同的類中繼承。

def OnSelectAll(self, event):
   
    num = self.list.GetItemCount()
    
    for i in range(num):
        self.list.CheckItem(i)

OnSelectAll() 方法選擇所有的複選框。 GetItemCount() 確定項數, CheckItem() 方法標記當前複選框。