20 x 20 wx.Control bug/glitch.. or MSW quirk?

I came across this oddity when sub-classing a wx.Control

If I set the size to 20 x 20, the ClientSize and VirtualSize calculated out to be 16 x 16?!?
Setting the control to any other dimension calculates as expected, matching the set size.

I only noticed it when drawing the control in a FlexSizer and saw the strange border/placement anomaly.
The 20 x 20 draws correctly in other sizers that I have tested, even though the ClientSize and VirtualSize still come out as 16 x 16

Is this a MS Windows quirk or an issue with wx?

demo4_.py (7.8 KB)

Well you are doing a lot of things in your code…
Now, as far as I can see, this toy model works fine:

import wx

class BareBones(wx.Control): 
    def __init__(self, *a, **k):
        wx.Control.__init__(self, *a, **k)
        self.SetBackgroundColour(wx.RED)

class MainFrame(wx.Frame):
    def __init__(self, *a, **k):
        wx.Frame.__init__(self, *a, **k)
        p = wx.Panel(self)
        self.c1 = BareBones(p, size=(15, 15))
        self.c2 = BareBones(p, size=(20, 20))
        self.c3 = BareBones(p, size=(25, 25))
        b = wx.Button(p, -1, 'info')
        b.Bind(wx.EVT_BUTTON, self.onclic)
        s = wx.GridSizer(1, 4, 0, 0) # or BoxSizer, or FlexGridSizer...
        s.Add(self.c1)
        s.Add(self.c2)
        s.Add(self.c3)
        s.Add(b)
        p.SetSizer(s)

    def onclic(self, e):
        for ctl in self.c1, self.c2, self.c3:
            print(ctl.GetSize(), ctl.GetBestSize(), ctl.GetClientSize())

app = wx.App()
MainFrame(None).Show()
app.MainLoop()

If it works for you too, then I guess it’s a matter of slowly stripping down your code, until you hit the bit that triggers the glitch…
riccardo

Hi riccardo,
thanks for having a look.

What I see is 3 boxes with a border around around them - that border is the problem
My code removes the border for every rectangle, except for the one sized at 20 x20.
I can solve the problem, and have done so in the control I’m building, but I was just interested in knowing why that 20 x 20 was different to every other size.

Picking around the internetz didn’t reveal much but I did come across a mention of some legacy with early windows doing something similar with icon sizing, but I couldn’t say it was the same issue.

Hi,

The class Simple got an argument size, but you don’t pass it to the base class.
Then, the base class initializes the default client size, that is, (16,16).

1 Like

Hi Kazuya,

good point, however if I create a wx.control (base class) and set it’s size to 20x20, the client size and virtual size are still showing 16 x 16.
If I create a control of size 19x19 or 21x21 the client and virtual sizes show 19x19 and 21x21 respectively.

When running the demo code, mousing over the bottom row of squares shows the wx parameters applying to the default wx.control.

There seems to be a built in overriding exception for controls that are specifically sized at 20x20.
I’m still curious as to what and why.

The code that riccardo provided also demonstrates the same problem

Hi rucky,

This seems to be something complicated than I expected… :mask:

I think @ricpol’s code works right as expected. To guess what’s going on, modify it as follows:

class BareBones(wx.Control): 
    def __init__(self, *a, size=None, **k):
        wx.Control.__init__(self, *a, **k)
        self.SetBackgroundColour(wx.RED)
        self.SetWindowStyleFlag(wx.NO_BORDER)
        self._size = size

    def DoGetBestSize(self):
        return self._size

This test uses Python 3.8.6 windows 10 (AMD64) wx.version 4.1.1 msw (phoenix) wxWidgets 3.1.5. The result is like this:
Clipboard02

$ onclic
(15, 15) (15, 15) (15, 15)
(20, 20) (20, 20) (16, 16)
(25, 25) (25, 25) (25, 25)

Now, the middle one has an unexpected size (as expected!).

I guess the scenario like this. The class BareBones doesn’t pass the size to the base class. So, the base class initializes the client size with (16,16), and the total size (20,20), including the border. When the sizer wants to do a layout, it may tell the children to repaint, but the child who has (16,16) client size won’t because the size is already the best (20,20).

well, I’m not sure what you are talking about (as @ricpol & pylint say there is a lot of ???), but try this

demo4.py (7.8 KB)

Thanks guys for your time and input, it’s an interesting (but non-fatal) quirk.

Georg, in the sample demo4.py you did this…

super().init(parent, size=size, style=wx.BORDER_NONE, *args, **kwargs)

In particular: …style=wx.BORDER_NONE…

I don’t know why but my brain didn’t go there initially, but this is the proper way to do it.

In the code I’m working on (not the demo above) I had the sub-class handle the correct client sizing.
In fact I should have let the parent class (wx.control) do the work for me as you have demonstrated.

Cheers guys,
Pete

it was the subtle remark of @komoto48g when I realized your cut & paste sub-classing (a hint of the 20x20 exception is given in the signature of wx.Control)

If the base class wx.Control is changed to wx.Panel, this issue won’t occur (in Windows).

Since wx.Control is the base class of all controls for general purposes, thus I think it has a limited implementation. I use wx.Control only for type checking, and if I want to customize the control, it inherits more downstream classes such as wx.Panel, as the purpose is usually clear.

Well, I have to admit that my thoughts were more guided by the docu than what works, especially since it got glitchy. The structure of the app is standard: Frame - Panel - Control. The docu of SetWindowStyleFlag points to the fact that is does not always work after window creation and anything like that from the creator, especially if it can be done at creation, keeps me off using it (I like to know what’s a fatal quirk for @rucky :upside_down_face:)