Mask of two overlapped windows

Python 3.11.5
wxPython 4.2.1
Pillow 10.1.0

I would like to make fancy windows (or panels) with Pillow. As you see below, I am having a trouble with mask (or transparency, alpha, …). I also tried “ConvertAlphaToMask” but it didn’t work. I found that the white corners of the inner rectangle is colored by its “BackgroundColour”. What should I do?

import wx
from PIL import Image, ImageDraw

def get_rounded_rectangle_image(width:int, height:int, fill:tuple, outline:tuple):
    factor = 4 # for antialias
    image  = Image.new('RGBA', (factor*width, factor*height), (0, 0, 0, 0))
    draw   = ImageDraw.Draw(image)
    draw.rounded_rectangle(
        (0, 0, factor*width, factor*height),
        fill    = fill,
        outline = outline,
        width   = 20,
        radius  = 200
    )
    image = image.resize((width, height), Image.Resampling.LANCZOS)
    return image

class RoundedWindow(wx.Window):

    def __init__(self, parent:wx.Window, fill:tuple, outline:tuple):
        wx.Window.__init__(self, parent)
        self.fill    = fill
        self.outline = outline
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnSize(self, event):
        event.Skip()
        self.Refresh()

    def OnPaint(self, event):
        dc = wx.PaintDC(self)
        size = dc.GetSize()
        pil_image = get_rounded_rectangle_image(size[0], size[1], self.fill, self.outline)
        wx_image  = wx.Image(size[0], size[1])
        wx_image.SetData(pil_image.convert('RGB').tobytes())
        wx_image.SetAlpha(pil_image.tobytes()[3::4])
        pil_image.close()
        bmp = wx_image.ConvertToBitmap()
        dc.DrawBitmap(bmp, 0, 0, True)

class MainFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None)
        panel   = wx.Panel(self)
        window1 = RoundedWindow(panel  , (255, 0, 0, 255), (0, 0, 0, 255))
        window2 = RoundedWindow(window1, (0, 255, 0, 255), (0, 0, 0, 255))
        sz1 = wx.BoxSizer(wx.HORIZONTAL)
        sz1.Add(window2, 1, wx.EXPAND|wx.ALL, 30)
        window1.SetSizer(sz1)
        sz = wx.BoxSizer(wx.HORIZONTAL)
        sz.Add(window1, 1, wx.EXPAND|wx.ALL, 30)
        panel.SetSizer(sz)
        self.CenterOnScreen()

if __name__ == '__main__':
    app = wx.App()
    MainFrame().Show()
    app.MainLoop()

There are a few custom controls in the wx.lib folder (and sub-folders) that use non-rectangular areas for their representation. An example is wx.lib.agw.shapedbutton, but there’s many more.

What happens if you add this line to your event table:

self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)

?

SBitmapButton gives the same result.

If I add EVT_ERASE_BACKGROUND, it also shows the same one and goes wrong when I resize the frame.

Do you see the same behaviour in the wxPython demo for ShapedButton? If not, I’d suggest looking at the implementation of it.

Yes I Do. I made a PNG image file of a rounded rectangle and pass it to shapedbutton.SBitmapButton and saw the exactly same behavior. :frowning:

Sorry, that’s not what I meant: if you run the unmodified ShapedButton demo in the wxPython demo, do you see the buttons having the rounded shapes correctly? With the parent background showing through?

In some of my code base, I have the following:

                if wx.Platform == '__WXMSW__': # Windows refuses to draw non-transparent rectangle outside of image
                                               # so recreate dc with normal background colour
                    fbmp = wx.Bitmap.FromRGBA(self.bmpw, self.bmph, r, g, b, 0)
                    dc.SetBackground(wx.Brush(self._backgroundcolour))
                    dc.Clear()

I don’t recall how I came to that conclusion/solution but it may help.
My primary reason for suggesting it, is that your code, with PIL minor version changes, works happily on Linux, leading me to assume, it’s a Windows issue.

Regards,
Rolf

p.s. Your code on Linux:
Screenshot at 2024-02-15 20-17-20

1 Like

Yes I see the button correctly ONLY IF the parent does not have background image.

Oh, I might need to deal with it like you did. Thanks for the advice.