You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

wx.grid单元格图片无法加载问题求助(宝可梦卡牌查看器开发)

问题排查与修复

核心原因

你遇到的问题是自定义GridCellRenderer的Draw函数未被调用,根源在于:当通过SetTable设置自定义GridTableBase(即你的CardTableSource)后,Grid会优先使用GridTableBase.GetAttr()返回的单元格属性。而你的CardTableSource.GetAttr()每次都返回一个全新的默认GridCellAttr,这会覆盖你之前通过SetCellRenderer为单个单元格设置的自定义渲染器,导致Grid从未使用你的CardImageRenderer。

修复方案

修改CardTableSource,让它针对图片列(索引1)返回带有自定义渲染器的GridCellAttr,同时将图片数据存储在TableSource中以保证生命周期(避免Bitmap被垃圾回收)。

修改后的完整代码

import wx
import wx.grid as wxGrid
import pandas as pd


class MainApp(wx.App):
    def OnInit(self):
        MainFrame().Show(True)
        return True


class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title='Pokemon Cards', size=(1080, 720))
        self.Centre()
        self.SetBackgroundColour('Green')

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        tableBasePanel = wx.Panel(self)
        tableBasePanel.SetBackgroundColour('Yellow')
        tableSizer = wx.BoxSizer(wx.HORIZONTAL)

        self.SetSizer(mainSizer)
        mainSizer.Add(tableBasePanel, 1, wx.ALL | wx.EXPAND, 5)
        tableBasePanel.SetSizer(tableSizer)
        tableSizer.Add(CardTable(tableBasePanel))


class CardTable(wxGrid.Grid):
    def __init__(self, parent):
        wxGrid.Grid.__init__(self, parent)
        self.parent = parent
        self.data = pd.read_csv('Card Database.csv', na_filter=False)
        self.LoadRows(0, 20)
    
    def LoadRows(self, start, end):
        # 提取图片列数据并加载Bitmap
        df_slice = self.data.iloc[start:end]
        # 存储图片Bitmap到列表,传递给TableSource
        self.card_images = []
        for img_name in df_slice.iloc[:, 1]:
            img_path = f'Card Images/{img_name}'
            self.card_images.append(wx.Bitmap(img_path, wx.BITMAP_TYPE_ANY))
        
        # 初始化TableSource并传入图片列表
        table_source = CardTableSource(df_slice, self.card_images)
        self.SetTable(table_source, takeOwnership=True)

        # 原有设置保留
        self.HideRowLabels()
        self.DisableDragColSize()
        self.DisableCellEditControl()
        self.AutoSizeColumns()
        self.SetColSize(1, 200)
        
        eff1Col = self.data.columns.get_loc('Effect 1')
        for iCol in range(eff1Col, eff1Col + 7):
            self.SetColSize(iCol, 300)
        
        # 移除原有的SetCellRenderer循环,改为由TableSource管理
        for iRow in range(end-start):
            self.SetRowSize(iRow, 280)
            # 清空图片列的文本值(可选,因为渲染器会覆盖显示)
            self.SetCellValue(iRow, 1, '')


class CardImageRenderer(wxGrid.GridCellRenderer):
    def __init__(self, img):
        wxGrid.GridCellRenderer.__init__(self)
        self.img = img

    def Draw(self, grid, attr, dc, rect, row, col, isSelected):
        # 绘制单元格背景
        dc.SetBackgroundMode(wx.SOLID)
        if isSelected:
            dc.SetBrush(wx.Brush(wx.BLUE, wx.SOLID))
            dc.SetPen(wx.Pen(wx.BLUE, 1, wx.SOLID))
        else:
            dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
            dc.SetPen(wx.Pen(wx.WHITE, 1, wx.SOLID))
        dc.DrawRectangle(rect)
        
        # 绘制图片(适配单元格大小,避免超出)
        if self.img.IsOk():
            img_width, img_height = self.img.GetSize()
            # 计算居中位置
            x = rect.x + (rect.width - img_width) // 2
            y = rect.y + (rect.height - img_height) // 2
            dc.DrawBitmap(self.img, x, y, True)
    
    # 必须重写Clone方法,wxGrid需要复制渲染器实例
    def Clone(self):
        return CardImageRenderer(self.img)


class CardTableSource(wxGrid.GridTableBase):
    def __init__(self, data=None, card_images=None):
        wxGrid.GridTableBase.__init__(self)
        self.headerRows = 1
        self.data = data if data is not None else pd.DataFrame()
        self.card_images = card_images if card_images is not None else []

    def GetNumberRows(self):
        return self.data.shape[0]

    def GetNumberCols(self):
        return self.data.shape[1]

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

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

    def GetColLabelValue(self, col):
        return str(self.data.columns[col])

    def GetTypeName(self, row, col):
        return wxGrid.GRID_VALUE_STRING

    def GetAttr(self, row, col, prop):
        attr = wxGrid.GridCellAttr()
        # 针对图片列(索引1)设置自定义渲染器
        if col == 1 and row < len(self.card_images):
            attr.SetRenderer(CardImageRenderer(self.card_images[row]))
        return attr


MainApp().MainLoop()

关键修改点说明

  1. CardTableSource新增图片存储:在初始化时接收card_images列表,保存每个行对应的卡牌图片Bitmap,确保图片不会被垃圾回收。
  2. 重写GetAttr方法:判断当前列是否为图片列,若是则返回带有CardImageRenderer的GridCellAttr,让Grid正确使用自定义渲染器。
  3. CardImageRenderer添加Clone方法:wxGrid在渲染单元格时会克隆渲染器实例,必须实现此方法才能正常工作(原代码缺少这一步,也是潜在问题)。
  4. 优化图片绘制逻辑:改为居中绘制图片,适配单元格大小,避免图片超出边界。
  5. 移除原SetCellRenderer循环:不再单独给每个单元格设置渲染器,统一由TableSource管理,避免被默认Attr覆盖。

内容的提问来源于stack exchange,提问作者MarcoTalin12

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:41:00