任何人都可以帮助我避免“必须删除任何推送的事件处理程序”。 当我运行wxpython4.0.2示例的Grid_MegaExample.py时,正常就可以了。 但是,当退出该应用程序时,或者当我单击booleditor更改bool列值时,得到这样的错误代码。
wx._core.wxAssertionError: C++ assertion "GetEventHandler() == this"
failed at ..\..\src\common\wincmn.cpp(478) in wxWindowBase::~wxWindowBase():
any pushed event handlers must have been removed
我知道该错误可能发生在wx.grid的SetEditor()SetColAttr()中,并检查了Google消息,但我无法纠正它... 任何答复将不胜感激。
import wx
import wx.grid as Grid
import images
import glob
import os
import sys
#---------------------------------------------------------------------------
class MegaTable(Grid.GridTableBase):
"""
A custom wx.Grid Table using user supplied data
"""
def __init__(self, data, colnames, plugins):
"""data is a list of the form
[(rowname, dictionary),
dictionary.get(colname, None) returns the data for column
colname
"""
# The base class must be initialized *first*
Grid.GridTableBase.__init__(self)
self.data = data
self.colnames = colnames
self.plugins = plugins or {}
# XXX
# we need to store the row length and column length to
# see if the table has changed size
self._rows = self.GetNumberRows()
self._cols = self.GetNumberCols()
def GetNumberCols(self):
return len(self.colnames)
def GetNumberRows(self):
return len(self.data)
def GetColLabelValue(self, col):
return self.colnames[col]
def GetRowLabelValue(self, row):
return "row %03d" % int(self.data[row][0])
def GetValue(self, row, col):
return str(self.data[row][1].get(self.GetColLabelValue(col), ""))
def GetRawValue(self, row, col):
return self.data[row][1].get(self.GetColLabelValue(col), "")
def SetValue(self, row, col, value):
self.data[row][1][self.GetColLabelValue(col)] = value
def ResetView(self, grid):
"""
(Grid) -> Reset the grid view. Call this to
update the grid if rows and columns have been added or deleted
"""
grid.BeginBatch()
for current, new, delmsg, addmsg in [
(self._rows, self.GetNumberRows(), Grid.GRIDTABLE_NOTIFY_ROWS_DELETED, Grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
(self._cols, self.GetNumberCols(), Grid.GRIDTABLE_NOTIFY_COLS_DELETED, Grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
]:
if new < current:
msg = Grid.GridTableMessage(self,delmsg,new,current-new)
grid.ProcessTableMessage(msg)
elif new > current:
msg = Grid.GridTableMessage(self,addmsg,new-current)
grid.ProcessTableMessage(msg)
self.UpdateValues(grid)
grid.EndBatch()
self._rows = self.GetNumberRows()
self._cols = self.GetNumberCols()
# update the column rendering plugins
self._updateColAttrs(grid)
# update the scrollbars and the displayed part of the grid
grid.AdjustScrollbars()
grid.ForceRefresh()
def UpdateValues(self, grid):
"""Update all displayed values"""
# This sends an event to the grid table to update all of the values
msg = Grid.GridTableMessage(self, Grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
grid.ProcessTableMessage(msg)
def _updateColAttrs(self, grid):
"""
wx.Grid -> update the column attributes to add the
appropriate renderer given the column name. (renderers
are stored in the self.plugins dictionary)
Otherwise default to the default renderer.
"""
col = 0
for colname in self.colnames:
attr = Grid.GridCellAttr()
if colname in self.plugins:
renderer = self.plugins[colname](self)
if renderer.colSize:
grid.SetColSize(col, renderer.colSize)
if renderer.rowSize:
grid.SetDefaultRowSize(renderer.rowSize)
attr.SetReadOnly(True)
attr.SetRenderer(renderer)
grid.SetColAttr(col, attr)
col += 1
attr = Grid.GridCellAttr()
attr.SetEditor(Grid.GridCellBoolEditor())
attr.SetRenderer(Grid.GridCellBoolRenderer())
grid.SetColAttr(5,attr)
grid.SetColSize(5,20)
# --------------------------------------------------------------------
# Sample wx.Grid renderers
class MegaImageRenderer(Grid.GridCellRenderer):
def __init__(self, table):
"""
Image Renderer Test. This just places an image in a cell
based on the row index. There are N choices and the
choice is made by choice[row%N]
"""
Grid.GridCellRenderer.__init__(self)
self.table = table
'''
self._choices = [images.Smiles.GetBitmap,
images.Mondrian.GetBitmap,
images.WXPdemo.GetBitmap,
]
'''
self.colSize = None
self.rowSize = None
def Draw(self, grid, attr, dc, rect, row, col, isSelected):
choice = self.table.GetRawValue(row, col)
bmp = wx.Bitmap( 1, 1 ) # Create a bitmap container object. The size values are dummies.
if isinstance(choice,str) and os.path.isfile(choice):
bmp.LoadFile( choice, wx.BITMAP_TYPE_ANY ) # Load it with a file image.
image = wx.MemoryDC()
image.SelectObject(bmp)
# clear the background
dc.SetBackgroundMode(wx.SOLID)
if isSelected:
dc.SetBrush(wx.Brush(wx.BLUE, wx.BRUSHSTYLE_SOLID))
dc.SetPen(wx.Pen(wx.BLUE, 1, wx.PENSTYLE_SOLID))
else:
dc.SetBrush(wx.Brush(wx.WHITE, wx.BRUSHSTYLE_SOLID))
dc.SetPen(wx.Pen(wx.WHITE, 1, wx.PENSTYLE_SOLID))
dc.DrawRectangle(rect)
# copy the image but only to the size of the grid cell
width, height = bmp.GetWidth(), bmp.GetHeight()
if width > rect.width-2:
width = rect.width-2
if height > rect.height-2:
height = rect.height-2
dc.Blit(rect.x+1, rect.y+1, width, height,
image,
0, 0, wx.COPY, True)
class MegaFontRenderer(Grid.GridCellRenderer):
def __init__(self, table, color="blue", font="ARIAL", fontsize=8):
"""Render data in the specified color and font and fontsize"""
Grid.GridCellRenderer.__init__(self)
self.table = table
self.color = color
self.font = wx.Font(fontsize, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, font)
self.selectedBrush = wx.Brush("blue", wx.BRUSHSTYLE_SOLID)
self.normalBrush = wx.Brush(wx.WHITE, wx.BRUSHSTYLE_SOLID)
self.colSize = None
self.rowSize = 50
def Draw(self, grid, attr, dc, rect, row, col, isSelected):
# Here we draw text in a grid cell using various fonts
# and colors. We have to set the clipping region on
# the grid's DC, otherwise the text will spill over
# to the next cell
dc.SetClippingRegion(rect)
# clear the background
dc.SetBackgroundMode(wx.SOLID)
if isSelected:
dc.SetBrush(wx.Brush(wx.BLUE, wx.BRUSHSTYLE_SOLID))
dc.SetPen(wx.Pen(wx.BLUE, 1, wx.PENSTYLE_SOLID))
else:
dc.SetBrush(wx.Brush(wx.WHITE, wx.BRUSHSTYLE_SOLID))
dc.SetPen(wx.Pen(wx.WHITE, 1, wx.PENSTYLE_SOLID))
dc.DrawRectangle(rect)
text = self.table.GetValue(row, col)
dc.SetBackgroundMode(wx.SOLID)
# change the text background based on whether the grid is selected
# or not
if isSelected:
dc.SetBrush(self.selectedBrush)
dc.SetTextBackground("blue")
else:
dc.SetBrush(self.normalBrush)
dc.SetTextBackground("white")
dc.SetTextForeground(self.color)
dc.SetFont(self.font)
dc.DrawText(text, rect.x+1, rect.y+1)
# Okay, now for the advanced class :)
# Let's add three dots "..."
# to indicate that that there is more text to be read
# when the text is larger than the grid cell
width, height = dc.GetTextExtent(text)
if width > rect.width-2:
width, height = dc.GetTextExtent("...")
x = rect.x+1 + rect.width-2 - width
dc.DrawRectangle(x, rect.y+1, width+1, height)
dc.DrawText("...", x, rect.y+1)
dc.DestroyClippingRegion()
# --------------------------------------------------------------------
# Sample Grid using a specialized table and renderers that can
# be plugged in based on column names
class MegaGrid(Grid.Grid):
def __init__(self, parent, data, colnames, plugins=None):
"""parent, data, colnames, plugins=None
Initialize a grid using the data defined in data and colnames
(see MegaTable for a description of the data format)
plugins is a dictionary of columnName -> column renderers.
"""
# The base class must be initialized *first*
Grid.Grid.__init__(self, parent, -1)
self._table = MegaTable(data, colnames, plugins)
self.SetTable(self._table)
self._plugins = plugins
def Reset(self):
"""reset the view based on the data in the table. Call
this when rows are added or destroyed"""
self._table.ResetView(self)
class MegaFontRendererFactory:
def __init__(self, color, font, fontsize):
"""
(color, font, fontsize) -> set of a factory to generate
renderers when called.
func = MegaFontRenderFactory(color, font, fontsize)
renderer = func(table)
"""
self.color = color
self.font = font
self.fontsize = fontsize
def __call__(self, table):
return MegaFontRenderer(table, self.color, self.font, self.fontsize)
#---------------------------------------------------------------------------
class TestFrame(wx.Frame):
def __init__(self, parent, plugins={"This":MegaFontRendererFactory("red", "ARIAL", 20),
"A":MegaImageRenderer,
"Test":MegaFontRendererFactory("orange", "TIMES", 24),
}):
wx.Frame.__init__(self, parent, -1,"Test Frame", size=(640,480))
self.panel = wx.Panel(self)
self.data = []
self.colnames = ["Row", "This", "Is", "A", "Test","Add"]
self.plugins = plugins
self.grid = MegaGrid(self.panel, self.data, self.colnames, self.plugins)
self.btnInit = wx.Button(self.panel, wx.ID_ANY, u"&Init Row")
self.btnAdd = wx.Button(self.panel, wx.ID_ANY, u"&Add Row")
self.btnQuit = wx.Button(self.panel, wx.ID_CANCEL, u"&Exit")
sizer_all = wx.BoxSizer(wx.HORIZONTAL)
sizer_bottom = wx.BoxSizer(wx.VERTICAL)
sizer_all.Add(self.grid, proportion=3,flag= wx.ALL|wx.EXPAND,border = 5)
sizer_all.Add(sizer_bottom, proportion=0,flag= wx.ALL|wx.EXPAND,border = 5)
sizer_bottom.Add(self.btnInit, proportion =0, flag= wx.ALIGN_RIGHT)
sizer_bottom.Add(self.btnAdd, proportion =0, flag= wx.ALIGN_RIGHT)
sizer_bottom.Add(self.btnQuit, proportion =0, flag = wx.ALIGN_RIGHT)
self.panel.SetSizer(sizer_all)
self.Layout()
self.grid.Reset()
self.Bind(wx.EVT_BUTTON, self.OnBtnInit, self.btnInit)
self.Bind(wx.EVT_BUTTON, self.OnBtnAdd, self.btnAdd)
self.Bind(wx.EVT_BUTTON, self.OnClose, self.btnQuit)
def OnBtnInit(self, event):
self.data = []
for row in range(100):
d = {}
for name in ["This", "Test", "Is"]:
d[name] = random.random()
d["Row"] = '%03d' %len(self.data)
# XXX
# the "A" column can only be between one and 4
d["A"] = random.choice(range(4))
d["Add"]='1'
self.data.append((str(row), d))
#self._table = MegaTable(data, colnames, plugins)
self.grid._table.data = self.data
self.grid.Reset()
def OnBtnAdd(self, event):
rows = self.grid.GetSelectedRows()
if len(rows)<=0 :
row = 0
else:
row = rows[0]
entry = {}
for name in ["This", "Test", "Is"]:
entry[name] = random.random()
entry["Row"] = row
# XXX
# the "A" column can only be between one and 4
entry["A"] = random.choice(range(4))
entry["Add"]='1'
self.data.insert(row, [row, entry])
#self.grid.SetTable(MegaTable(self.data, self.colnames, plugins))
self.grid._table.data = self.data
self.grid.Reset()
def OnClose(self, event):
dlg = wx.MessageDialog(self, "Are you sure exit?", "Question", wx.YES_NO | wx.ICON_WARNING |wx.CENTRE |wx.STAY_ON_TOP)
dlg.SetIcon(self.GetIcon())
if dlg.ShowModal() == wx.ID_YES:
dlg.Destroy()
self.Destroy()
else:
dlg.Destroy()
event.StopPropagation()
class GridDemoApp(wx.App):
def OnInit(self):
self.win = TestFrame(None)
self.win.Show(True)
self.win.Maximize(True)
return True
import random
if __name__ == '__main__':
app = GridDemoApp(0)
app.MainLoop()
答案 0 :(得分:0)
这是一个较晚(且不完整)的答案,但是罗宾·邓恩本人的authoritative analysis可能会有所帮助。
我无法假装完全理解该分析,并且您的代码太多让我无法找到并行对象,但是我在关闭时也收到了类似的消息,问题归结为具有单元格属性(例如{ {1}}和单元格编辑器,其中编辑器是属性的一部分,而其中一些属性是覆盖列属性的单元格属性。
如果您具有列属性,则在特定单元格中对该列属性的任何替代都必须为<input type="button" onclick="clientedetail()"></input>
<div id="guardadoC"></div>
,因为在这种情况下,当单元格出现时,属性会得到wx.grid.GridCellAttr
d。但是,与此类单元格属性关联的编辑器(“推送事件处理程序”)必须在所有克隆之间共享,而本身不能是克隆。如果它是一个克隆,则出于明显的原因,它在单元格运行时不会被.Clone
破坏,因此在关闭时会徘徊,这会导致显示消息。