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() 方法标记当前复选框。