Background logo at a corner of a panel

Python 3.9.5
wxPython 4.2.0

I’m trying to fix a logo at at corner of a panel. If the panel get resize, the logo should move (or redrawn). It is not good to make a StaticBitmap and set its position because of z-order issues. So I tried using PAINT or ERASE_BACKGROUND events like the attached code.

However it works well for macOS but bad for Windows.

Anyone has suggestions?

import wx
import base64
from io import BytesIO

class BackgroundPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        img_b64   = "iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAMAAABibqotAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAzUExURQAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAOmONxAAAAAQdFJOUwAQIDBAUGBwgI+fr7/P3+8jGoKKAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACbklEQVRYR+2X65KrIBCEMyIaFYX3f9rTc0F0427A1PmXr2orY1ZbaGYG8vjy5f+weAsyFDqLWvDpRSfOFrWwRLJoZ4oWNEBxsqjg0mBRPWPqLTqwLRbUs6wWHJnSy1zfcTEtTPbF+3e4yycojRbV0qfLXGk2aLzWCcGCWobiqF+KVR/ojCml/ekbOss0OAQuQifxiMg/Q2xOoGfg55d54w8IBejFdb5TqR0GoCpgnUbXnISFXejOQApkKqk5Ac94U4FT9k0D5AR2o+hwC7N/yE0VzPYomk2Z1xO+29rFSq+eersI5XilXSaFtnXDUkFo5KfjJDKxtWsoKiQr330go0I9y6T5ExkRUlPibZl+4r1BJiWwTP/k71qgCWW5ZndUpl/wuTbVR4/ZrPzU0IkK6l1V8IdEqoQmvH8kx/mYc4a7UPAPxy+oTGgejJ4IRCkDFYYT82pLOtF77zH+uBf3rmQqwGFy6+j9xXabsaqUwVCn2MRGvZKK4I4Nfj980Lptm76Z2NILZBQ0476tKgfyOv2ktZ1BpyOBBxb7HN/R0WiATj57hNs6vDqMmnFbZ+9dKnRbpySiHA3v6nSyKSuscFenbBe68bTroA1CxzJX4DULDdUu0CZnlX3nAJteN3VFWlLk7nDUkRwK+n0tk3Xjoz9SlhjnVr97wRb1oWynubLgf/WRDEew3Kn2NLRKlwV428YUJA12YaUYtB/v0XSrvCZ4yaksnM4JBhpmjdfzacfLhXH4EYXu8fqT6gV4fMxZ+Coc849ihdfrDxthBzgvNrx+OyA3/LhF+6FdGO6PzeI36GzYB7w39cuXT3g8/gGjSCvxOsZEaAAAAABJRU5ErkJggg=="
        img_bytes = base64.b64decode(img_b64.encode("ascii"))
        img_io    = BytesIO(img_bytes)
        img_wx    = wx.Image(img_io, wx.BITMAP_TYPE_PNG)
        self.bmp  = wx.Bitmap(img_wx)
        self.bmp_size = self.bmp.GetSize()
        img_io.close()

        self.SetBackgroundStyle(wx.BG_STYLE_PAINT)
        self.Bind(wx.EVT_PAINT, self.on_paint)

        """
        Below works incorretly also.
        """
        # self.SetBackgroundStyle(wx.BG_STYLE_ERASE)
        # self.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase)

    def on_paint(self, event):
        dc = wx.PaintDC(self)
        dc.Clear()
        size_dc = dc.GetSize()
        margin  = 15
        dc.DrawBitmap(
            self.bmp,
            size_dc[0]-self.bmp_size[0]-margin,
            size_dc[1]-self.bmp_size[1]-margin
        )

    # def on_erase(self, event):
    #     dc = event.GetDC()
    #     dc.Clear()
    #     size_dc = dc.GetSize()
    #     margin  = 15
    #     dc.DrawBitmap(
    #         self.bmp,
    #         size_dc[0]-self.bmp_size[0]-margin,
    #         size_dc[1]-self.bmp_size[1]-margin
    #     )

class TestFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title="TEST")
        panel = BackgroundPanel(self)
        n_row = 5
        n_col = 5
        sz_grid = wx.GridSizer(n_row, n_col, 30, 30)
        for i in range(n_col):
            for j in range(n_row):
                bt = wx.Button(panel, label=f"{i}{j}")
                sz_grid.Add(bt)

        sz_panel = wx.BoxSizer(wx.VERTICAL)
        sz_panel.AddMany((
            ((-1, -1), 1),
            (sz_grid, 0, wx.ALIGN_CENTER_HORIZONTAL),
            ((-1, -1), 1)
        ))
        sz_main = wx.BoxSizer(wx.VERTICAL)
        sz_main.Add(sz_panel, 1, wx.EXPAND|wx.ALL, 50)
        panel.SetSizerAndFit(sz_main)
        self.SetSize(self.GetBestSize())

if __name__ == "__main__":
    app = wx.App()
    TestFrame().Show()
    app.MainLoop()

Hi seaba,

Though you have a z-position problem, I think the simple solution would be to use StaticBitmap :wink: and reposition it when the parent panel is resized.

You can find a general solution in the “/wxdemo/Core Windows/Controls/StatusBar/” demo code. There, a checkbox control appears in a fixed position in the status bar and moves when the frame is resized.

EDIT the solution for now would be:

        def on_size(evt):
            self.Refresh()
            evt.Skip()
        self.Bind(wx.EVT_SIZE, on_size)

Hi komoto48g,

Thanks your tip.

But StaticBitmap overlaps the other widgets (the buttons in the example code) so I can’t use it. Raise nor Lower are helpful. Do you know how to make the image keeps its position at the bottom? It is not necessary to control strictly z-orders of all the widgets. I just want to make the background image keep the bottom.

Hi seaba,

Please can you post the code you are using to test StaticBitmap ?

Also, have you tried using a GenStaticBitmap instead of a StaticBitmap ?

You are correct.
I thought it should be possible to control Z-order, but it seems not…

https://docs.wxpython.org/wx.Window.html#wx.Window.Raise

Hi RichardT,

It seems GenStaticBitmap is the same with StaticBitmap. The bitmap overlaps the buttons though it goes bottom if the buttons get focused. This is not satisfying… :disappointed:

import wx
import base64
from wx.lib.statbmp import GenStaticBitmap
from io import BytesIO

class BackgroundPanel(wx.Panel):

    def __init__(self, parent):

        wx.Panel.__init__(self, parent)
        img_b64   = "iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAMAAABibqotAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAzUExURQAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAOmONxAAAAAQdFJOUwAQIDBAUGBwgI+fr7/P3+8jGoKKAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACbklEQVRYR+2X65KrIBCEMyIaFYX3f9rTc0F0427A1PmXr2orY1ZbaGYG8vjy5f+weAsyFDqLWvDpRSfOFrWwRLJoZ4oWNEBxsqjg0mBRPWPqLTqwLRbUs6wWHJnSy1zfcTEtTPbF+3e4yycojRbV0qfLXGk2aLzWCcGCWobiqF+KVR/ojCml/ekbOss0OAQuQifxiMg/Q2xOoGfg55d54w8IBejFdb5TqR0GoCpgnUbXnISFXejOQApkKqk5Ac94U4FT9k0D5AR2o+hwC7N/yE0VzPYomk2Z1xO+29rFSq+eersI5XilXSaFtnXDUkFo5KfjJDKxtWsoKiQr330go0I9y6T5ExkRUlPibZl+4r1BJiWwTP/k71qgCWW5ZndUpl/wuTbVR4/ZrPzU0IkK6l1V8IdEqoQmvH8kx/mYc4a7UPAPxy+oTGgejJ4IRCkDFYYT82pLOtF77zH+uBf3rmQqwGFy6+j9xXabsaqUwVCn2MRGvZKK4I4Nfj980Lptm76Z2NILZBQ0476tKgfyOv2ktZ1BpyOBBxb7HN/R0WiATj57hNs6vDqMmnFbZ+9dKnRbpySiHA3v6nSyKSuscFenbBe68bTroA1CxzJX4DULDdUu0CZnlX3nAJteN3VFWlLk7nDUkRwK+n0tk3Xjoz9SlhjnVr97wRb1oWynubLgf/WRDEew3Kn2NLRKlwV428YUJA12YaUYtB/v0XSrvCZ4yaksnM4JBhpmjdfzacfLhXH4EYXu8fqT6gV4fMxZ+Coc849ihdfrDxthBzgvNrx+OyA3/LhF+6FdGO6PzeI36GzYB7w39cuXT3g8/gGjSCvxOsZEaAAAAABJRU5ErkJggg=="
        img_bytes = base64.b64decode(img_b64.encode("ascii"))
        img_io    = BytesIO(img_bytes)
        img_wx    = wx.Image(img_io, wx.BITMAP_TYPE_PNG)
        bmp       = wx.Bitmap(img_wx)
        self.bmp_size      = bmp.GetSize()
        # self.static_bitmap = wx.StaticBitmap(self, bitmap=bmp, pos=self.get_bmp_pos())
        self.static_bitmap = GenStaticBitmap(self, -1, bmp, pos=self.get_bmp_pos(), style=wx.TRANSPARENT_WINDOW)

        self.Bind(wx.EVT_SIZE, self.on_size)

    def get_bmp_pos(self):
        panel_size = self.GetSize()
        margin = 15
        return (panel_size[0]-self.bmp_size[0]-margin, panel_size[1]-self.bmp_size[1]-margin)

    def on_size(self, event):
        event.Skip()
        self.static_bitmap.SetPosition(self.get_bmp_pos())
        self.static_bitmap.Lower()
        self.Refresh()

class TestFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title="TEST")
        panel = BackgroundPanel(self)
        n_row = 5
        n_col = 5
        sz_grid = wx.GridSizer(n_row, n_col, 30, 30)
        for i in range(n_col):
            for j in range(n_row):
                bt = wx.Button(panel, label=f"{i}{j}")
                sz_grid.Add(bt)

        sz_panel = wx.BoxSizer(wx.VERTICAL)
        sz_panel.AddMany((
            ((-1, -1), 1),
            (sz_grid, 0, wx.ALIGN_CENTER_HORIZONTAL),
            ((-1, -1), 1)
        ))
        sz_main = wx.BoxSizer(wx.VERTICAL)
        sz_main.Add(sz_panel, 1, wx.EXPAND|wx.ALL, 50)
        panel.SetSizerAndFit(sz_main)
        self.SetSize(self.GetBestSize())

if __name__ == "__main__":
    app = wx.App()
    TestFrame().Show()
    app.MainLoop()
2 Likes

Hi seaba,

The following code appears to work on Linux (but then so did your original code). I can’t test it on Windows myself,

Basically it uses two panels, one on top of the other. The logo is drawn in a StaticBitmap on the back panel and the buttons are drawn on the front panel. I am using Python 3.10.6 | wxPython 4.2.0 gtk3 (phoenix) wxWidgets 3.2.0 | Linux Mint 21.1 and the front panel is transparent by default. You may need to set its style parameter to wx.TRANSPARENT_WINDOW on Windows.

Of course, it is possible that Windows may render them the other way round again!

import wx
from wx.lib.embeddedimage import PyEmbeddedImage

logo = PyEmbeddedImage(
    b'iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAABHNCSVQICAgIfAhkiAAAA/NJ'
    b'REFUeJztW9GR2yAQfcqkAKUDpQN1cE4HLkEluINzB5pU4HSgEnypwNeBnQq4DjYfXiyEACEL'
    b'jHzHm2E8I6P18lh2lwUDGRkZGRkZGRkZi0BAR8B2oYySgCMBVSC10oOALQEUiBxBwCGUbsnB'
    b'ViMIKAPIagkQIfRKDmW220DyarbCJoS8pCBgx4PZBJR5JqALJS8ZeEmdAstsmfDFyzQpQi4p'
    b'RWYZwsFP4VtM4QTUuM7u30g/UUWSCyAyOejN/j2k0AL4AHAB8BJSro7Y5NQRZV8Q2efEJudD'
    b'+7SCE8UutH9agu+pFQCu4R4KKQTUBfAroUrxQUDDUaXj8NuQttQ4qRPcT22t1q9k69rz/ko8'
    b'fa6jDEYffEfAgRM6/bsbQQoR8pkg4MTvVjF1L2IK18GDqbm9wC9rfgfwBuAff14KDx/29LBY'
    b'lN6q1Ho+HEqWO9V2qXSMHcpd2Hj2i5rouRA0lPNGsLJ8fa+vGLzj+o0icCYeFBxBXEukUfr6'
    b'Lqu98k41Ed3Ean0Uh+2pwTYz+p/YUnyIIXbwz1fG0CJTozzfGQYtOMcxESMocqkiCRwEmUJ6'
    b'xd99fmIkdIII2FiWyOFLESOhEWTzI+LTE8OW0ZJ2WmBZSjaStpq8vS7vqUDXUN3ScNN48vA1'
    b'RmKYlM4QzapUY7wLPJCzNgh9UA37FBc5rYMUYy60WijWos78jp/XNE4Sp/IWvc5zVCypNkxA'
    b'zLLs/TBYy9Fk8haSptqNFIM8PZFMW2ZlIrZKU81ekMdO2pMkKykGWepSO7HFSv02QQbuoch2'
    b'YjCV1r9k32JrrqW1c7w32iZQf/RsaodHkFPyzJyVZpxhpe+cJTSnbSy/edD0O9PaQj9NR6Ol'
    b'LWoh7JHFrp8AfliaXof5wPVoxrd/FDz03MpW7CLgN4Y+4VJci+lG0IMK7CnLpLcog7GzXMUF'
    b'pWQnnhzVOrjT/wMBKIA/D1BphJSW8wq/fVGypC4JOWw1viWIMnZUsiGV5chLTb54iaWIC7HJ'
    b'qWY+nyvnOe/ncHovo9Bbcb1sJDF3UHp/eY3u1ZSZrxqcwsvNqKDxtROfIxy1jW6jUl8oG8lf'
    b'Naiv6Rhrv+TevHptGnkC5Kb1TM9wXkXDnfHe0sf3tNO5h9L2bseoA1sKGt7ScuYnNF0FdO6+'
    b'FTnq0c5q7hMOwLMoibkd4zr6+/qdydvvNCzNrstBU/8/KOlnKs93fMjZe+qgViPX46CpL3fO'
    b'Onyj6TKp99+QaFhYC/L3pcXQHPCsNJ+mi2H7mfLk35fW4aCV2brLGWr+Qm13hWfNQae1Ho5Q'
    b'zRJFaFxnFq4I5anT3e+vCtQXxWf7rS+D5MsgIyMjIyMjI+OL4D+jCumjZrwXVAAAAABJRU5E'
    b'rkJggg==')


class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)
        self.SetSize((400, 300))
        self.SetTitle("Test Frame")

        self.back_panel = wx.Panel(self, wx.ID_ANY)
        # self.back_panel.SetBackgroundColour(wx.Colour(120, 240, 120))

        v_back_sizer = wx.BoxSizer(wx.VERTICAL)
        h_back_sizer = wx.BoxSizer(wx.HORIZONTAL)

        bmp = logo.GetBitmap()
        self.st_bitmap = wx.StaticBitmap(self.back_panel, bitmap=bmp)
        h_back_sizer.Add(self.st_bitmap, 0, wx.ALIGN_BOTTOM, 0)
        v_back_sizer.Add(h_back_sizer, 1, wx.ALIGN_RIGHT, 0)
        self.back_panel.SetSizer(v_back_sizer)


        # self.front_panel = wx.Panel(self.back_panel, wx.ID_ANY, style=wx.TRANSPARENT_WINDOW)
        self.front_panel = wx.Panel(self.back_panel, wx.ID_ANY)
        # self.front_panel.SetBackgroundColour(wx.Colour(240, 120, 120))

        bp_size = self.back_panel.GetSize()
        self.front_panel.SetSize(bp_size)

        n_row = 5
        n_col = 5
        sz_grid = wx.GridSizer(n_row, n_col, 30, 30)
        for i in range(n_col):
            for j in range(n_row):
                bt = wx.Button(self.front_panel, label=f"{i}{j}")
                sz_grid.Add(bt)

        sz_panel = wx.BoxSizer(wx.VERTICAL)
        sz_panel.AddMany((
            ((-1, -1), 1),
            (sz_grid, 0, wx.ALIGN_CENTER_HORIZONTAL),
            ((-1, -1), 1)
        ))
        sz_main = wx.BoxSizer(wx.VERTICAL)
        sz_main.Add(sz_panel, 1, wx.EXPAND|wx.ALL, 50)
        self.front_panel.SetSizerAndFit(sz_main)
        self.SetSize(self.front_panel.GetBestSize())
        self.Layout()

        # print(self.back_panel.GetSize(), self.front_panel.GetSize())


class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame()
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True


if __name__ == "__main__":
    app = MyApp(0)
    app.MainLoop()

Hi RicharT,

I tested it on Windows. The logo work well, but front_panel's layout has little problem. So I edited slightly then everything is nice.

Thank you so much!


import wx
from wx.lib.embeddedimage import PyEmbeddedImage

logo = PyEmbeddedImage(
b'iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAAABHNCSVQICAgIfAhkiAAAA/NJ'
    b'REFUeJztW9GR2yAQfcqkAKUDpQN1cE4HLkEluINzB5pU4HSgEnypwNeBnQq4DjYfXiyEACEL'
  b'jHzHm2E8I6P18lh2lwUDGRkZGRkZGRkZi0BAR8B2oYySgCMBVSC10oOALQEUiBxBwCGUbsnB'
    b'ViMIKAPIagkQIfRKDmW220DyarbCJoS8pCBgx4PZBJR5JqALJS8ZeEmdAstsmfDFyzQpQi4p'
    b'RWYZwsFP4VtM4QTUuM7u30g/UUWSCyAyOejN/j2k0AL4AHAB8BJSro7Y5NQRZV8Q2efEJudD'
    b'+7SCE8UutH9agu+pFQCu4R4KKQTUBfAroUrxQUDDUaXj8NuQttQ4qRPcT22t1q9k69rz/ko8'
    b'fa6jDEYffEfAgRM6/bsbQQoR8pkg4MTvVjF1L2IK18GDqbm9wC9rfgfwBuAff14KDx/29LBY'
    b'lN6q1Ho+HEqWO9V2qXSMHcpd2Hj2i5rouRA0lPNGsLJ8fa+vGLzj+o0icCYeFBxBXEukUfr6'
    b'Lqu98k41Ed3Ean0Uh+2pwTYz+p/YUnyIIXbwz1fG0CJTozzfGQYtOMcxESMocqkiCRwEmUJ6'
    b'xd99fmIkdIII2FiWyOFLESOhEWTzI+LTE8OW0ZJ2WmBZSjaStpq8vS7vqUDXUN3ScNN48vA1'
    b'RmKYlM4QzapUY7wLPJCzNgh9UA37FBc5rYMUYy60WijWos78jp/XNE4Sp/IWvc5zVCypNkxA'
    b'zLLs/TBYy9Fk8haSptqNFIM8PZFMW2ZlIrZKU81ekMdO2pMkKykGWepSO7HFSv02QQbuoch2'
    b'YjCV1r9k32JrrqW1c7w32iZQf/RsaodHkFPyzJyVZpxhpe+cJTSnbSy/edD0O9PaQj9NR6Ol'
    b'LWoh7JHFrp8AfliaXof5wPVoxrd/FDz03MpW7CLgN4Y+4VJci+lG0IMK7CnLpLcog7GzXMUF'
    b'pWQnnhzVOrjT/wMBKIA/D1BphJSW8wq/fVGypC4JOWw1viWIMnZUsiGV5chLTb54iaWIC7HJ'
    b'qWY+nyvnOe/ncHovo9Bbcb1sJDF3UHp/eY3u1ZSZrxqcwsvNqKDxtROfIxy1jW6jUl8oG8lf'
    b'Naiv6Rhrv+TevHptGnkC5Kb1TM9wXkXDnfHe0sf3tNO5h9L2bseoA1sKGt7ScuYnNF0FdO6+'
    b'FTnq0c5q7hMOwLMoibkd4zr6+/qdydvvNCzNrstBU/8/KOlnKs93fMjZe+qgViPX46CpL3fO'
    b'Onyj6TKp99+QaFhYC/L3pcXQHPCsNJ+mi2H7mfLk35fW4aCV2brLGWr+Qm13hWfNQae1Ho5Q'
    b'zRJFaFxnFq4I5anT3e+vCtQXxWf7rS+D5MsgIyMjIyMjI+OL4D+jCumjZrwXVAAAAABJRU5E'
    b'rkJggg==')

class MyFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None)
        self.SetSize((400, 300))
        self.SetTitle("Test Frame")

        """
        On Windows, 'front_panel' layout goes wrong if 'back_panel' has its own Sizer. I don't know why.
        So, 'st_bitmap' is not in a sizer and its position is controlled explicitly by EVT_SIZE.
        """
        self.back_panel = wx.Panel(self, wx.ID_ANY)
        bmp = logo.GetBitmap()
        self.st_bitmap = wx.StaticBitmap(self.back_panel, bitmap=bmp)
        self.front_panel = wx.Panel(self.back_panel, wx.ID_ANY)

        n_row = 5
        n_col = 5
        sz_grid = wx.GridSizer(n_row, n_col, 30, 30)
        for i in range(n_col):
            for j in range(n_row):
                bt = wx.Button(self.front_panel, label=f"{i}{j}")
                sz_grid.Add(bt)

        sz_panel = wx.BoxSizer(wx.VERTICAL)
        sz_panel.AddMany((
            ((-1, -1), 1),
            (sz_grid, 0, wx.ALIGN_CENTER_HORIZONTAL),
            ((-1, -1), 1)
        ))
        sz_main = wx.BoxSizer(wx.VERTICAL)
        sz_main.Add(sz_panel, 1, wx.EXPAND|wx.ALL, 50)
        self.front_panel.SetSizerAndFit(sz_main)
        self.SetSize(self.front_panel.GetBestSize())
        self.Layout()
        self.Bind(wx.EVT_SIZE, self.OnSize)

    def OnSize(self, event):
        """
        'st_bitmap's position is controlled here.
        """
        event.Skip()
        sz = self.GetClientSize()
        self.front_panel.SetSize(sz)

        margin = 15
        self.st_bitmap.SetPosition((
            sz[0]-self.st_bitmap.GetSize()[0]-margin,
            sz[1]-self.st_bitmap.GetSize()[1]-margin
        ))
        self.Refresh()

class MyApp(wx.App):

    def OnInit(self):
        self.frame = MyFrame()
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

if __name__ == "__main__":
    app = MyApp(0)
    app.MainLoop()

It is like a mirage on Windows.
You can see the button, but you cannot touch it.

Referencing this thread: Aug 2012,

@RichardT I guess it works on Mac/Linux, but you cannot expect it to work on Windows unfortunately.
I was a bit surprised that the z-order is only guaranteed with parent-children relationships, not between bros or uncles.