Semi-Transparent Panel

I’m trying to mimic the MacOS style of a semi-transparent left panel and solid right panel as seen in applications like Xcode. So far, I am able to make the whole frame transparent but can’t manage to make a single panel transparent.

TransparentLeftPanel

Is this something that is possible?

hello and enjoy the page :joy:
does this help (a bit) :pray:

Thanks. Got that part where I can make the whole frame transparent. In my case I put a splitter control with two panels: left and right but only want the left panel transparent not the right. Can’t seem to do that part.

Well, this should work, but my system does not support transparency :thinking:

import wx


class MainFrame(wx.Frame):
    """Create MainFrame class."""
    def __init__(self, *args, **kwargs):
        super(MainFrame, self).__init__(None, *args, **kwargs)
        self.Title = 'Transparency'
        panel = MainPanel(self, size=(200, 100))
        sizer = wx.BoxSizer()
        sizer.Add(panel)
        self.SetSizerAndFit(sizer)
        self.Centre()
        self.Show()


class MainPanel(wx.Panel):
    """Panel class to contain frame widgets."""
    def __init__(self, parent, *args, **kwargs):
        super(MainPanel, self).__init__(parent, *args, **kwargs)

        """Create and populate main sizer."""
        left_panel = LeftPanel(self, size=(100, 100))
        right_panel = RightPanel(self, size=(100, 100))
        panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
        panel_sizer.Add(left_panel)
        panel_sizer.Add(right_panel)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(panel_sizer)
        self.SetSizer(sizer)


class LeftPanel(wx.Panel):
    """Transparent panel."""
    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        supports_transparency = self.SetTransparent(wx.IMAGE_ALPHA_TRANSPARENT)
        if not supports_transparency:
            print('This system does not support transparency')
            self.SetBackgroundColour(wx.BLUE)


class RightPanel(wx.Panel):
    """Opaque panel."""
    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)


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

well, if you change Panel to Window and the like you’ll get what is ask for :wink:

Not quite right because the TextCtrl’s background has turned grey?

import wx


class MyPanel(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent=parent)
        self.Bind(wx.EVT_PAINT, self.OnPaint)


    def OnPaint(self, event):
        dc = wx.ClientDC(self)
        panel = event.GetEventObject()
        w, h = panel.GetSize()
        # print(f"OnPaint() {w} x {h}")

        # Create an all black image
        img = wx.Image(w, h, clear=True)
        # Convert to bitmap
        bmp = wx.Bitmap(img)
        # Mask uses white for transparent pixels
        mask = wx.Mask(bmp, wx.WHITE)
        # Apply mask to bitmap which should then be opaque
        bmp.SetMask(mask)

        dc.DrawBitmap(bmp, 0, 0, True)


class MyFrame(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        self.SetSize((500, 400))
        self.SetTitle("Transparency Test")

        self.splitter = wx.SplitterWindow(self, wx.ID_ANY)
        self.splitter.SetMinimumPaneSize(20)

        self.left_pane = wx.Panel(self.splitter)
        left_sizer = wx.BoxSizer(wx.VERTICAL)
        self.tree_ctrl = wx.TreeCtrl(self.left_pane, wx.ID_ANY)
        left_sizer.Add(self.tree_ctrl, 1, wx.EXPAND, 0)
        self.left_pane.SetSizer(left_sizer)

        self.right_pane = MyPanel(self.splitter)
        right_sizer = wx.BoxSizer(wx.VERTICAL)
        self.text_ctrl = wx.TextCtrl(self.right_pane, wx.ID_ANY, "", style=wx.TE_MULTILINE)
        right_sizer.Add(self.text_ctrl, 1, wx.EXPAND, 0)
        self.right_pane.SetSizer(right_sizer)

        self.splitter.SplitVertically(self.left_pane, self.right_pane)

        self.SetTransparent(185)

        self.tree_ctrl.SetForegroundColour(wx.RED)
        self.root = self.tree_ctrl.AddRoot("Root")
        for i in range(8):
            self.tree_ctrl.AppendItem(self.root, f"Item {i}")
        self.tree_ctrl.Expand(self.root)

        for i in range(10):
            self.text_ctrl.AppendText(f"Line {i+1}\n")


if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame(None)
    frame.Center()
    frame.Show()
    app.MainLoop()

Screenshot at 2024-04-11 18-59-12

Tested on wxPython 4.2.1 + Python 3.10.12 + Linux Mint 21.3.

I made a change to the MyPanel.OnPaint() method so the TextCtrl has a white background:

    def OnPaint(self, event):
        dc = wx.ClientDC(self)
        panel = event.GetEventObject()
        w, h = panel.GetSize()
        # print(f"OnPaint() {w} x {h}")

        # Create an all WHITE image
        data = bytearray((255, 255, 255) * w * h)
        img = wx.Image(w, h, data)
        # Convert to bitmap
        bmp = wx.Bitmap(img)
        # Mask uses BLACK for transparent pixels
        mask = wx.Mask(bmp, wx.BLACK)
        # Apply mask to bitmap which should then be opaque
        bmp.SetMask(mask)

        dc.DrawBitmap(bmp, 0, 0, True)


Thanks RichardT,

This seems very close but for some reason on MacOS, the right panel continues to be transparent.

I was afraid that might be the case. From what I have been reading, transparency is an area that has some of the more noticeable differences on the various platforms. Unfortunately I can only test on Linux.

I got the idea to use a Mask for this implementation after reading a couple of threads on this forum where it had been mentioned:

well, a mask is not transparency: the latter may be stored in a permanent file whereas the first always needs some bitwise operation (that’s were some photographers turn into maniacs, or so) :crazy_face:

well, the splitter seems to be the culprit, maybe an aui approach is more promising :thinking:

Never used that library before but will look into it to see if it might take care of business. Thanks.