I have an application that uses a number of non-modal dialogs, one of which contains a custom control derived from wx.Control. The custom control displays several pieces of text, plus small bitmaps and some lines and had been working fine until I recently made a change to two other dialogs in the application.
Previously those two other dialogs had been using a wx.ListCtrl
to display some data. I wanted to be able to highlight some of the data in different colours, so I changed the dialogs to use an UltimateListCtrl
instead. That conversion worked OK and the dialogs now display the values in the required colours.
However, I have noticed that once I have displayed one of the dialogs containing an UltimateListCtrl
, the custom control remains blank when it is updated. This effect persists even after the dialog containing the UltimateListCtrl
has been destroyed! The only way to get the custom control working again is to restart the application. Running the application in debug shows that its Draw()
method is being called, but the commands it executes appear to have no visible effects.
Below is a much simplified application that demonstrates the problem. If you click on the “Inc Number” button, the number shown in the CustomCtrl
will increment.
If you click on the “ListCtrl Dialog” button a dialog containing a wx.ListCtrl
will appear. This will not prevent the CustomCtrl
from updating when you click the “Inc Number” button.
If you click on the “ULC Dialog” button a dialog containing an UltimateListCtrl
will appear. If you then click on the “Inc Number” button, the CustomCtrl
will go blank and not display the number value.
I am only able to test this on Python 3.10.6 + wxPython 4.2.0 gtk3 (phoenix) wxWidgets 3.2.0 + Linux Mint 21.1.
Does anyone have any ideas why this is happening? I wondered if the UltimateListCtrl
is changing some global property that is affecting the behaviour of the custom control.
import sys
import wx
from wx.lib.agw import ultimatelistctrl as ULC
DATA = (
("One", "Alpha"),
("Two", "Bravo"),
("Three", "Charlie"),
("Four", "Delta"),
("Five", "Echo"),
)
class CustomCtrl(wx.Control):
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.BORDER_NONE,
validator=wx.DefaultValidator, name="CustomCtrl"):
wx.Control.__init__(self, parent, id, pos, size, style, validator, name)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.number = 0
def Draw(self, dc):
print("In Draw() number = %d" % self.number)
bg_brush = wx.Brush(wx.WHITE, wx.BRUSHSTYLE_SOLID)
dc.SetBackground(bg_brush)
dc.Clear()
width, height = dc.GetSize()
dc.SetTextForeground(wx.BLACK)
xc = width // 2
yc = height // 2
text = str(self.number)
tw, th = dc.GetTextExtent(text)
x = xc - tw // 2
y = yc - th // 2
dc.DrawText(text, x, y)
def incNumber(self):
self.number += 1
self.Refresh()
self.Update()
def OnEraseBackground(self, event):
pass
def OnPaint(self, _event):
dc = wx.BufferedPaintDC(self)
self.Draw(dc)
class ListCtrlDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, title="LC Dialog")
self.SetSize((400, 200))
main_sizer = wx.BoxSizer(wx.VERTICAL)
style = wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES
self.list_ctrl = wx.ListCtrl(self, style=style)
main_sizer.Add(self.list_ctrl, 1, wx.EXPAND, 0)
self.SetSizer(main_sizer)
self.Layout()
self.list_ctrl.InsertColumn(0, "Number")
self.list_ctrl.InsertColumn(1, "Code Word")
for n, w in DATA:
idx = self.list_ctrl.InsertItem(sys.maxsize, n)
self.list_ctrl.SetItem(idx, 1, w)
self.Bind(wx.EVT_CLOSE, self.OnClose)
def OnClose(self, _evt):
self.Destroy()
class ULCDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, title="ULC Dialog")
self.SetSize((400, 200))
main_sizer = wx.BoxSizer(wx.VERTICAL)
agw_style = wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES
self.ulc = ULC.UltimateListCtrl(self, agwStyle=agw_style)
main_sizer.Add(self.ulc, 1, wx.EXPAND, 0)
self.SetSizer(main_sizer)
self.Layout()
self.ulc.InsertColumn(0, "Number")
self.ulc.InsertColumn(1, "Code Word")
for n, w in DATA:
idx = self.ulc.InsertStringItem(sys.maxsize, n)
self.ulc.SetStringItem(idx, 1, w)
self.Bind(wx.EVT_CLOSE, self.OnClose)
def OnClose(self, _evt):
self.Destroy()
class TestFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="Test Frame")
self.SetSize((250, 200))
main_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.custom_ctrl = CustomCtrl(self, size=(50, 50))
main_sizer.Add(self.custom_ctrl, 0, wx.ALL, 16)
button_sizer = wx.BoxSizer(wx.VERTICAL)
self.inc_num_button = wx.Button(self, label="Inc Number")
button_sizer.Add(self.inc_num_button, 0, 0, 0)
self.lc_dialog_button = wx.Button(self, label="ListCtrl Dialog")
button_sizer.Add(self.lc_dialog_button, 0, wx.TOP, 8)
self.ulc_dialog_button = wx.Button(self, label="ULC Dialog")
button_sizer.Add(self.ulc_dialog_button, 0, wx.TOP, 8)
main_sizer.Add(button_sizer, 0, wx.TOP, 8)
self.SetSizer(main_sizer)
self.Layout()
self.Bind(wx.EVT_BUTTON, self.OnIncNumber, self.inc_num_button)
self.Bind(wx.EVT_BUTTON, self.OnULCDialog, self.ulc_dialog_button)
self.Bind(wx.EVT_BUTTON, self.OnLCDialog, self.lc_dialog_button)
self.ulc_dialog = None
self.lc_dialog = None
def OnIncNumber(self, _evt):
self.custom_ctrl.incNumber()
def OnLCDialog(self, _evt):
if not self.lc_dialog:
self.lc_dialog = ListCtrlDialog(self)
self.lc_dialog.SetPosition((300, 0))
self.lc_dialog.Show()
else:
self.lc_dialog.Destroy()
def OnULCDialog(self, _evt):
if not self.ulc_dialog:
self.ulc_dialog = ULCDialog(self)
self.ulc_dialog.SetPosition((300, 220))
self.ulc_dialog.Show()
else:
self.ulc_dialog.Destroy()
if __name__ == "__main__":
app = wx.App(0)
frame = TestFrame(None)
frame.Show()
app.MainLoop()