实战wxPython:043 - 高级控件之表格Grid

wx.grid.Grid及其相关类用于显示和编辑表格数据。它们提供了一组丰富的功能,用于显示、编辑和与各种数据源交互。

wx.grid.Grid是一个功能强大的但是又稍微有一些复杂的窗口类,它用来显示表格类型的数据。可以使用wx.Grid来作为一个包含名称和值两栏的属性编辑器。或者是通过代码使其作为一个一般意义上的表格,用来显示一个数据库或者是应用程序产生的特定统计数据。

一、wx.grid相关类

对于简单的应用程序,我们只需要简单地使用wx.grid.Grid类,它将设置其他相关类的默认实例并自动管理它们。对于更复杂的应用,我们可以为自定义网格视图、网格数据表、单元格编辑器和渲染器(Renderer)派生自己的类。wx.grid提供一组类来实现表格应用。

wx.grid.Grid:主网格控件类本身。

wx.grid.GridTableBase:类控制要显示的实际数据。

wx.grid.GridStringTable:简单的GridTableBase实现,只支持字符串数据项,并将它们全部存储在内存中(因此只适用于不太大的网格)。

wx.grid.GridCellAttr:保存用于渲染表格的属性数据。可以显式的通过类似SetCellTextColour这样的函数更改表格的属性。也可以通过SetAttr函数设置某个单独的表格的属性或者是通过SetRowAttr和SetColAttr函数设置某一列或者某一行的属性。还可以在定义的表格类中通过GetAttr函数返回指定表格的属性。


wx.grid.GridCellAttrProvider:负责存储和检索单元格属性的对象。


wx.grid.GridColLabelWindow:显示网格列标签的窗口。


wx.grid.GridRowLabelWindow:显示网格行标签的窗口。


wx.grid.GridCornerLabelWindow:在左上角网格角中使用的窗口。

wx.grid.GridWindow:表示网格的主要部分窗口。

wx.grid.GridCellRenderer是用于在单元格中呈现内容的抽象基类,负责对单元格进行绘画。wxPython默认提供了几种派生类来实现具体的绘制。

  • wx.grid.GridCellBoolRenderer:显示单元格为选中或未选中的框。
  • wx.grid.GridCellFloatRenderer:格式化输出单元格中的浮点数数据。
  • wx.grid.GridCellNumberRenderer:格式化输出单元格中的整数数据。
  • wx.grid.GridCellStringRenderer:格式化输出单元格中的字符串数据。
  • wx.grid.GridCellDateRenderer:格式化输出日期。
  • wx.grid.GridCellDateTimeRenderer:格式化输出日期时间。

wx.grid.GridCellEditor是用于编辑单元格值的抽象基类。负责实现对表格数据的即时编辑功能的控件。wxPytho 默认提供了几种派生类来实现具体的即时编辑功能。

  • wx.grid.GridCellBoolEditor:布尔值单元格的编辑器。
  • wx.grid.GridCellChoiceEditor:允许选择一个预定义的字符串(也可能输入一个新的字符串)的编辑器。
  • wx.grid.GridCellFloatEditor:用于浮点数的单元格的编辑器。
  • wx.grid.GridCellNumberEditor:用于整数的单元格的编辑器。
  • wx.grid.GridCellTextEditor:用于文本的单元格的编辑器。
  • wx.grid.GridCellDateEditor:用于日期的单元格的编辑器。

wx.GridEvent这个类包含了各种网格相关事件的信息,比如鼠标在表格上单击事件,表格数据改变事件,表格被选中事件,表格编辑器被显示或者隐藏事件等。

二、wx.grid.Grid成员函数

wx.grid.Grid的成员函数较多,下面按相关功能列举了一些常用的成员函数。

用于创建,删除和数据交互的函数

  • AppendCols(selfnumCols=1updateLabels=True):将一个或多个新列追加到网格的右侧。
  • AppendRows(selfnumRows=1updateLabels=True):将一个或多个新行追加到网格的底部。
  • InsertRows(self, pos=0, numRows=1, updateLabels=True):在网格指定位置插入一列或者多列。
  • InsertCols(self, pos=0, numCols=1, updateLabels=True):在网格指定位置插入一行或者多行。
  • GetNumberCols(self):返回网格的总列数。
  • GetNumberRows(self):返回网格的总行数。
  • CreateGrid(selfnumRowsnumColsselmode=GridSelectCells):创建具有指定初始行数和列数的网格。直接在网格构造函数之后调用它。当您使用这个函数时,wx.grid.Grid将为您创建和管理一个简单的字符串值表。所有网格数据都将存储在内存中。
  • ClearGrid(self):清除所有的网格绑定表格中的数据并且刷新网格的显示。表格本身并不会被释放。
  • DeleteCols(selfpos=0numCols=1updateLabels=True):从指定位置开始从网格中删除一列或多列。
  • DeleteRows(selfpos=0numRows=1updateLabels=True):从指定位置开始从网格中删除一行或多行。
  • GetColLabelValue(selfcol):返回指定的列标签。默认的网格表类提供了A,B…Z,AA,AB…ZZ,AAA…这种形式的列标签。如果使用的是自定义网格表,则可以重写wx.grid.GridTableBase.GetColLabelValue来提供自己的标签。
  • GetCellValue (self, row, col):返回单元格中包含的指定位置的字符串。

界面相关函数:

  • BeginBatch(self):增加网格的批处理计数。当计数大于零时,网格的重绘将被抑制。每个对BeginBatch的调用都必须与后面对EndBatch的调用相匹配。做大量网格修改的代码可以被封装在BeginBatch和EndBatch调用之间,以避免屏幕闪烁。最后的EndBatch调用将导致网格被重新绘制。
  • EndBatch(self):减少网格的批处理计数。当计数大于零时,网格的重绘将被抑制。每个之前对BeginBatch的调用都必须与后面对EndBatch的调用相匹配。做大量网格修改的代码可以被封装在BeginBatch和EndBatch调用之间,以避免屏幕闪烁。最后的EndBatch将导致网格被重新绘制。
  • GetBatchCount(self):返回BeginBatch被调用的次数。网格的批处理计数等零时,网格将刷新显示。
  • EnableGridLines(selfenable=True):打开或关闭网格线的绘制。
  • GridLinesEnabled(self):如果打开绘制网格线,则返回True,否则返回False。
  • ForceRefresh(self):强制立即重新绘制网格。
  • Fit(self):使网格控件将自己的大小更改为当前行数和列数所要求的最小大小。
  • GetCellAlignment(selfrowcol):返回指定单元格在垂直和水平方向上的对齐方式。
  • GetColLabelAlignment(self):返回列标签的对齐方式。
  • GetRowLabelAlignment(self):返回行标签的对齐方式。
  • GetDefaultCellAlignment(self):返回默认单元格的对齐方式。
  • GetCellBackgroundColour(selfrowcol):返回指定单元格背景颜色。
  • GetDefaultCellBackgroundColour(self):返回单元格的当前默认背景色。
  • GetLabelBackgroundColour(self):返回行标签和列标签的背景颜色。
  • GetCellFont(selfrowcol):返回指定单元格的字体。
  • GetDefaultCellFont(self):返回单元格文本的当前默认字体。
  • GetLabelFont(self):返回用于行和列标签的字体。
  • GetCellTextColour(selfrowcol):返回指定单元格的文本颜色。
  • GetDefaultCellFont(self):返回单元格文本的当前默认字体。
  • GetLabelTextColour(self):返回用于行和列标签文本的颜色。
  • GetGridLineColour(self):返回网格线使用的颜色。

注:以上Get方法都有相应的Set方法。

  • SetColAttr(self, col, attr):为指定列中的所有单元格设置单元格属性。
  • SetRowAttr(self, row, attr):为指定行中的所有单元格设置单元格属性。

尺寸相关函数

  • AutoSize(self):自动设置所有行和列的高度和宽度以适应其内容。
  • AutoSizeColumn(selfcolsetAsMin=True):自动调整列的大小以适应其内容。
  • AutoSizeColumns(selfsetAsMin=True):自动调整所有列的大小以适应其内容。
  • AutoSizeRow(selfrowsetAsMin=True):自动调整行的大小以适应其内容。
  • AutoSizeRows(selfsetAsMin=True):自动调整所有行的大小以适应其内容。
  • CellToRect (self, row, col):返回与网格单元格的大小和逻辑坐标位置对应的矩形。
  • GetColMinimalWidth(selfcol):获取给定列的最小宽度。
  • GetRowMinimalHeight(selfrow) :获取给定行的最小高度。
  • GetColLabelSize(self):返回列标签的当前高度。
  • GetDefaultColLabelSize(self):返回列标签的默认高度。
  • GetDefaultColSize(self):返回网格列的当前默认宽度。
  • GetColSize(selfcol):返回指定列的宽度。
  • GetRowLabelSize(self):返回行标签的当前宽度。
  • GetDefaultRowLabelSize(self):返回行标签的默认宽度。
  • GetDefaultRowSize(self):返回网格行的当前默认高度。
  • GetRowSize(selfrow):返回指定行的高度。

注:以上Get方法都有相应的Set方法。

选择和光标函数:

  • GetGridCursorCol(self):返回当前单元格列的位置。
  • GetGridCursorRow(self):返回当前单元格行的位置。
  • MoveCursorDownMoveCursorLeftMoveCursorRightMoveCursorUp:以每次一格的方式移动光标。
  • MoveCursorDownBlockMoveCursorLeftBlockMoveCursorRightBlockMoveCursorUpBlock:移动的时候跳到第一个非空单元格。
  • MovePageDownMovePageUp:一次移动一页,页大小由网格窗口的大小决定。
  • GetSelectionMode(self):返回当前选择模式。
  • GetSelectedCells(self):返回由单独选择的单元格组成的数组。
  • GetSelectedCols(self):返回所选列的数组。请注意,这个方法本身并不足以找到所有被选中的列,因为它只包含被单独选中的列。
  • GetSelectedRows(self):返回选定行的数组。请注意,这个方法本身并不足以找到所有被选中的行,因为它只包含被单独选中的行。
  • GetSelectionBlockTopLeft(self):返回所选单元格块的左上角数组。
  • GetSelectionBlockBottomRight(self):返回所选单元格块的右下角的数组。
  • SelectAll(self):选择网格中的所有单元格。

其他函数:

  • GetTable(self):返回网格用于保存内部数据的绑定表格对象。
  • GetCellEditor(selfrowcol):返回一个指向指定位置单元格的编辑器指针。
  • GetDefaultEditor(self):返回指向当前默认网格单元格编辑器的指针。
  • GetCellRenderer(selfrowcol):返回指向指定位置网格单元格的渲染器的指针。
  • GetDefaultRenderer(self):返回指向当前默认网格单元格渲染器的指针。

注:以上Get方法都有相应的Set方法。

  • ShowCellEditControl(self):显示当前单元格被隐藏的编辑器。
  • HideCellEditControl(self):隐藏当前单元格被隐藏的编辑器。
  • SetReadOnly(self, row, col, isReadOnly=True):将指定单元格设置为可读或者可编辑。

 图1:wx.grid.Grid类继承关系

三、wx.grid.Grid演示

#网格控件(wx.grid)

import wx
import wx.grid

#grid column type
class GridColumnControlKind:
    Text = "Text"
    CheckBox = "CheckBox"
    Colour = "Colour"

class GridCellColorEditor(wx.grid.GridCellEditor):
    def Create(self, parent, id, evtHandler):
        """
        创建一个控件, 该控件必须继承自wx.Control
        *必须重载*
        """

        self.__parent = parent
        self.__dlgColor = None
        self.__btnColor = wx.Button(parent, id, "")
        self.SetControl(self.__btnColor)

        #添加新的事件句柄,防止窗口弹出后,单元格自动调用编辑器
        newEventHandler = wx._core.EvtHandler()
        if evtHandler:
            self.__btnColor.PushEventHandler(newEventHandler)
        self.__btnColor.Bind(wx.EVT_BUTTON, self.OnClick)

    def OnClick(self, e):
        self.__btnColor.SetFocus()
        self.ShowColorDialog()

    def SetSize(self, rect):
        """
        用于在单元格矩形中定位/调整编辑控件的大小。
        如果没有填充单元格(矩形),那么一定要重写
        PaintBackground做一些必要的事情。
        """
        self.__btnColor.SetDimensions(rect.x, rect.y, rect.width + 2, rect.height + 2, wx.SIZE_ALLOW_MINUS_ONE)

    def Clone(self):
        """
        创建一个新对象,它是这个对象的副本
        *必须重载*
        """
        return GridCellColorEditor()

    def BeginEdit(self, row, col, grid):
        """
        从表中获取值并准备编辑控件开始编辑。将焦点设置为编辑控件。
        *必须重载*
        """
        self.startValue = grid.GetTable().GetValue(row, col)
        self.endValue = self.startValue
        self.__btnColor.SetBackgroundColour(self.startValue)

    def EndEdit(self, row, col, grid):
        """
        完成当前单元格的编辑。如果已发生改变,则返回True
        如有必要,可以销毁控件。
        *必须重载*
        """
        changed = False
        if self.endValue != self.startValue:
            changed = True
            grid.GetTable().SetValue(row, col, self.endValue) # 更新该表
            self.startValue = ""
        return changed

    def ShowColorDialog(self):
        colorDlg = wx.ColourDialog(self.__parent)
        self.__dlgColor = colorDlg
        colorDlg.GetColourData().SetColour(self.startValue)
        if wx.ID_OK == colorDlg.ShowModal():
            data = colorDlg.GetColourData()
            colour = data.GetColour()
            self.__btnColor.SetBackgroundColour(colour)
            self.endValue = colour

        del self.__dlgColor
        self.__dlgColor = None

class GridCellColorRender(wx.grid.GridCellRenderer):
    def __init__(self):
        wx.grid.GridCellRenderer.__init__(self)

    def Draw(self, grid, attr, dc, rect, row, col, isSelected):
        color = grid.GetTable().GetValue(row, col)
        dc.SetBrush(wx.Brush(color, wx.BRUSHSTYLE_SOLID))
        dc.SetPen(wx.TRANSPARENT_PEN)
        dc.DrawRectangle(rect)
        dc.SetBackgroundMode(wx.BRUSHSTYLE_TRANSPARENT)

    def GetBestSize(self, grid, attr, dc, row, col):
        return wx.Size(-1, -1)

    def Clone(self):
        return GridCellColorRender()

#根据具体业务逻辑定制grid的 table
class CustomGridTable(wx.grid.GridTableBase):
    def __init__(self):
        wx.grid.GridTableBase.__init__(self)

        #添加表的列的头
        self.colLabels = ["名字", "可见", "最小门限", "最大门限", "颜色"]
        #指定一个列的控制类型
        self.colControlKinds = [GridColumnControlKind.Text, 
                                GridColumnControlKind.CheckBox, 
                                GridColumnControlKind.Text,
                                GridColumnControlKind.Text,
                                GridColumnControlKind.Colour]
        self.colControlEditorEnableStatus = [True, True, False, False, True]
        self.rowLabels = ["", "", "", "", ""]

        #添加数据源
        self.data = [ 
            ['Mask 1', 1, "2.5","320.6",(200,20,100)]
            ,['Mask 2', 1, "2.5","320.6",(50,0,200)]
        ]

    def GetNumberRows(self):
        return len(self.data)

    def GetNumberCols(self):
        return len(self.colLabels)

    def IsEmptyCell(self, row, col):
        return False

    def GetValue(self, row, col):
        return self.data[row][col]

    def SetValue(self, row, col, value):
        self.data[row][col] = value

    def GetColLabelValue(self, col):
        return self.colLabels[col]

    def GetRowLabelValue(self, row):
        return self.rowLabels[row]

    def InsertRow(self, index, row):
        if len(self.data) < index:
            return

        self.data.insert(index, row)
        print(self.data)
        self.GetView().BeginBatch()

        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_INSERTED, index, 1)
        self.GetView().ProcessTableMessage(msg)

        # ... same thing for columns ....

        self.GetView().EndBatch()
        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
        self.GetView().ProcessTableMessage(msg)

    def DeleteRow(self, row):
        rowIndex = self.data.index(row)
        if rowIndex < 0:
            return
        
        self.data.remove(row)

        self.GetView().BeginBatch()

        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, rowIndex, 1)
        self.GetView().ProcessTableMessage(msg)

        # ... same thing for columns ....
        self.GetView().EndBatch()
        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
        self.GetView().ProcessTableMessage(msg)

    def Clear(self):
        self.GetView().BeginBatch()

        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, 0, self.GetNumberRows())
        self.GetView().ProcessTableMessage(msg)

        # ... same thing for columns ....
        self.GetView().EndBatch()
        self.data = []
        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
        self.GetView().ProcessTableMessage(msg)
        

    def AppendRow(self, row):
        self.data.append(row)

        self.GetView().BeginBatch()
        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED)
        self.GetView().ProcessTableMessage(msg)

        # ... same thing for columns ....
        self.GetView().EndBatch()
        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
        self.GetView().ProcessTableMessage(msg)

#对grid的功能进行封装 以方便处理
class CustomGrid(wx.grid.Grid):
    def __init__(self, parent, id, rowLabelSize = 0, customGridTable = None):
        wx.grid.Grid.__init__(self, parent, id)

        self.rowLabelSize = rowLabelSize
        self.__customTableSource = customGridTable
        self.SetTable(self.__customTableSource, True)

        self.__InitStyle()

        #设置column 对应的 editor
        self.__InitColumnsEditor()

        # self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK,self.__OnMouse)
        self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.__OnCellSelected)
        self.Bind(wx.grid.EVT_GRID_EDITOR_CREATED, self.__OnEditorCreated)

    def __InitStyle(self):
        #设置选中后的背景色
        self.SetSelectionBackground(wx.Colour(237, 145, 33))

    def __InitColumnsEditor(self):
        index = -1
        for columnKind in self.__customTableSource.colControlKinds:
            index += 1
            if columnKind == GridColumnControlKind.CheckBox:
                self.__InitCheckBoxColumnEditor(index)
            elif columnKind == GridColumnControlKind.Colour:
                self.__InitColorColumnEditor(index)

    def __InitCheckBoxColumnEditor(self, columnIndex):
        attr = wx.grid.GridCellAttr()
        attr.SetEditor(wx.grid.GridCellBoolEditor())
        attr.SetRenderer(wx.grid.GridCellBoolRenderer())
        self.SetColAttr(columnIndex, attr)

    def __InitColorColumnEditor(self, columnIndex):
        attr = wx.grid.GridCellAttr()
        attr.SetEditor(GridCellColorEditor())
        attr.SetRenderer(GridCellColorRender())
        self.SetColAttr(columnIndex, attr)

    def __OnCellSelected(self, e):
        if self.__customTableSource.colControlEditorEnableStatus[e.Col]:
            wx.CallAfter(self.EnableCellEditControl)
            e.Skip()

        #设置行为选中状态 
        self.SelectRow(e.Row)

    def __OnEditorCreated(self, event):
        pass

    def ForceRefresh(self):
        wx.grid.Grid.ForceRefresh(self)

class SampleGrid(wx.Frame):

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

        self.InitUi()

    def InitUi(self):
        self.SetTitle("实战wxPython: Gird演示")
        self.SetSize(500, 300)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        addButton = wx.Button(self, -1, "添加")
        deleteButton = wx.Button(self, -1, "删除")
        clearButton = wx.Button(self, -1, "清理")
        sizer.Add(addButton, 0, wx.SHAPED)
        sizer.Add(deleteButton, 0, wx.SHAPED)
        sizer.Add(clearButton, 0, wx.SHAPED)

        table = CustomGridTable()
        grid = CustomGrid(self, id = -1, customGridTable = table)
        self.__grid = grid

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(sizer)
        mainSizer.Add(grid, 1, wx.EXPAND)
        self.SetSizerAndFit(mainSizer)

        addButton.Bind(wx.EVT_BUTTON, self.OnAddClick)
        deleteButton.Bind(wx.EVT_BUTTON, self.OnDeleteClick)
        clearButton.Bind(wx.EVT_BUTTON, self.OnClearClick)

        self.Centre()

    def OnClearClick(self, e):
        table  = self.__grid.GetTable()
        table.Clear()
        print(self.__grid.GetTable().data)

    def OnDeleteClick(self, e):
        table  = self.__grid.GetTable()
        firstRow = table.data[0]
        table.DeleteRow(firstRow)
        print(self.__grid.GetTable().data)

    def OnAddClick(self, e):
        table  = self.__grid.GetTable()
        table.InsertRow(0, ['insert index ', 1, "2.5","110.6",(50,200,30)])
        print(self.__grid.GetTable().data)

def main():
    app = wx.App()
    sample = SampleGrid(None)
    sample.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()

上面的代码演示了如何从基类实现自定义的单元编辑器和渲染器,是一个使用wx.grid比较完整的例子,可以尝试修改其中的代码,观察如何操控wx.grid.Grid。

 图2:wx.grid.Grid演示

四、本文知识点

  • 了解wx.gird相关类。
  • 掌握如何使用wx.grid.Grid网格控件。

前一篇:实战wxPython:042 - 高级控件之选项卡Notebook

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值