wxPython 4.2.0 ultimatelistctrl issue with floats in Rect()
Hi There,
WX Version: 4.2.0 msw (phoenix) wxWidgets 3.2.0, Python Version: 3.10.6 (tags/v3.10.6:9c7b4bd, Aug 1 2022, 21:53:49) [MSC v.1932 64 bit (AMD64)].
Using ultimatelistctrl in VirtualList setup. Finding it wont accept a ulc.ULC_FORMAT_CENTER for column definitions. Generates the following error;
Traceback (most recent call last): File "C:\Python310\lib\site-packages\wx\lib\agw\ultimatelistctrl.py", line 5284, in OnPaint self.DrawTextFormatted(dc, text, wx.Rect(xAligned+EXTRA_WIDTH, int(HEADER_OFFSET_Y), int(cw-EXTRA_WIDTH), int(h-4))) TypeError: Rect(): arguments did not match any overloaded call: overload 1: too many arguments overload 2: argument 1 has unexpected type 'float' overload 3: argument 1 has unexpected type 'float' overload 4: argument 1 has unexpected type 'float' overload 5: argument 1 has unexpected type 'float' overload 6: argument 1 has unexpected type 'float'
To fix it I need to make the vars within the Rect() in the following line in def OnPaint(self, event): an integer;
From: self.DrawTextFormatted(dc, text, wx.Rect(xAligned+EXTRA_WIDTH, HEADER_OFFSET_Y, cw-EXTRA_WIDTH, h-4))
To:
self.DrawTextFormatted(dc, text, wx.Rect(int(xAligned+EXTRA_WIDTH), int(HEADER_OFFSET_Y), int(cw-EXTRA_WIDTH), int(h-4)))
Then all is good.
Python is dynamic but strongly typed language and an int is an int and a float is a float, no magic here, and that rigidity should have been pursued long ago (I'm sure that will peeve a lot of people) but better they straighten that out than removing the dynamics: I love to use attributes on the fly, and the pressure to introduce declarations/public etc is huge
Just as some further clarification, the issue arises due to this section within ultimatelistctrl.py in the OnPaint function; elif align == ULC_FORMAT_CENTER: xAligned = x + wcheck + (cw - wLabel)/2
As I am using ULC_FORMAT_CENTER, its calculation is creating a float in xAligned and then that variable is being used further down in the Rect(). The /2 just needs to be changed to a //2 and all is well. For now as my workaround I am distributing a local copy of the custom control with the necessary patch until a future update / release is made.
in the docu of Rect it clearly says int and that goes into another language, but the note on // (python 310) warns clearly that the result type may not be int it's these imponderables that must be avoided, not so much within python but certainly at the API (I think)
I've hit a related issue that does not pertain to formatting, should a new issue be created for this?
File "...\venv\lib\site-packages\wx\lib\agw\ultimatelistctrl.py", line 5606, in HandleColumnCheck rect = wx.Rect(theX + HEADER_OFFSET_X, HEADER_OFFSET_Y + (h - 4 - iy)/2, ix, iy) TypeError: Rect(): arguments did not match any overloaded call: overload 1: too many arguments overload 2: argument 2 has unexpected type 'float' overload 3: argument 1 has unexpected type 'int' overload 4: argument 1 has unexpected type 'int' overload 5: argument 1 has unexpected type 'int' overload 6: argument 1 has unexpected type 'int'
Simple program to reproduce the issue:
import wx
import wx.lib.agw.ultimatelistctrl as ULC
class MainFrame(wx.Frame):
def __init__(self):
super().__init__(None, title="Test Rect Issue")
self.SetSize((1250, 573))
frame_sizer = wx.BoxSizer(wx.VERTICAL)
self.panel = wx.Panel(self, wx.ID_ANY)
panel_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, 'Test Rect Call'), wx.VERTICAL)
panel_sizer.Add((0, 0), 0, 0, 0)
panel_list_ctrl = UlcListCtrl(self.panel)
panel_sizer.Add(panel_list_ctrl, 0, wx.ALL | wx.EXPAND, 3)
self.panel.SetSizer(panel_sizer)
frame_sizer.Add(self.panel, 0, wx.ALL | wx.EXPAND, 0)
self.SetSizer(frame_sizer)
frame_sizer.Fit(self)
self.Layout()
self.Center()
# Display the frame
self.Show()
class UlcListCtrl(ULC.UltimateListCtrl):
def __init__(self, parent: wx.Panel, **kwargs):
self.parent = parent
kwargs['agwStyle'] = (
wx.LC_REPORT | ULC.ULC_VRULES | ULC.ULC_HRULES | ULC.ULC_USER_ROW_HEIGHT | ULC.ULC_AUTO_CHECK_CHILD)
super().__init__(parent, size=(400, 200), **kwargs)
self.SetUserLineHeight(18)
self._create_columns()
def _create_columns(self) -> None:
columns = [('', -1, 1), ('Col 1', 100, 0), ('Col 2', 100, 0), ('Col3', -3, 0)]
for index, (column_name, width, kind) in enumerate(columns):
column_item = self._create_list_item(column_name, kind)
self.InsertColumnInfo(index, column_item)
self.SetColumnWidth(index, width)
@staticmethod
def _create_list_item(column_name: str, kind: int) -> ULC.UltimateListItem:
item = ULC.UltimateListItem()
item.SetMask(wx.LIST_MASK_TEXT | wx.LIST_MASK_FORMAT | ULC.ULC_MASK_CHECK)
item.SetAlign(ULC.ULC_FORMAT_LEFT)
item.SetKind(kind)
item.SetText(column_name)
return item
if __name__ == '__main__':
app = wx.App()
frame = MainFrame()
frame.Show()
app.MainLoop()
As mentioned by @finchyfinch if you wrap all of the arguments to the wx.Rect method with an int the issue is resolved in python 3.10 or if you add floor division (//) it resolves the issue as well.
wx.Rect(int(theX + HEADER_OFFSET_X), int(HEADER_OFFSET_Y + (h - 4 - iy)/2), int(ix), int(iy))
or
wx.Rect(theX + HEADER_OFFSET_X, HEADER_OFFSET_Y + (h - 4 - iy)//2, ix, iy)
This issue has been mentioned on Discuss wxPython. There might be relevant details there:
https://discuss.wxpython.org/t/questions-about-plans-for-4-2-1-release/36294/2
This issue has been mentioned on Discuss wxPython. There might be relevant details there:
https://discuss.wxpython.org/t/facing-an-error-in-ultimatelistctrl-py-file-for-python-3-10-10/36696/2