2
votes

In wxpython, I want to have a window with a picture that changes based on use of toolbar buttons with text controls on top of the picture. When I click the toolbar buttons, I am posting an erase background event, then capturing the erase event, and redrawing the new background from there (base on this).

Mostly works well, except that the text controls cease to be drawn once I redraw the background. They're still there, just not drawn.

Here is a simplified code that demonstrates the problem. If you run this code and click the button to toggle drawing the background image or not, the text controls disappear.:

import wx
import wx.lib.inspection

class PanelWithDrawing(wx.Panel):
    def __init__(self, parent):
        super(PanelWithDrawing, self).__init__(parent, size=(100, 40))

        self.showbmp = False

        self.txt = wx.TextCtrl(self, pos=(10, 10))

    def onErase(self, dc):
        if self.showbmp:
            # dc.DrawBitmap(wx.Bitmap('background.png', 0, 0)
            dc.DrawRectangle(0, 0, 40, 40)   # use a drawing instead so you don't have to find a png

class Toolbar(wx.ToolBar):
    def __init__(self, parent):
        super(Toolbar, self).__init__(parent, -1)

        self.AddLabelTool(wx.ID_SAVE, "Record", wx.Bitmap("picture.png", wx.BITMAP_TYPE_ANY), wx.NullBitmap, wx.ITEM_NORMAL, "", "")

class Example(wx.Frame):

    def __init__(self, parent, title):
        super(Example, self).__init__(parent, title=title)

        self.toolbar = Toolbar(self)
        self.SetToolBar(self.toolbar)
        self.toolbar.Realize()

        self.panel = wx.Panel(self)
        vbox = wx.BoxSizer(wx.VERTICAL)

        self.panel1 = PanelWithDrawing(self.panel)
        vbox.Add(self.panel1)

        # self.panel2 = PanelWithText(self.panel)
        # vbox.Add(self.panel2)

        self.panel.SetSizer(vbox)

        self.Centre()
        self.Show()

        self.toolbar.Bind(wx.EVT_TOOL, self.onButton)
        self.panel1.Bind(wx.EVT_ERASE_BACKGROUND, self.onErase)

    def onErase(self, evt):
        try:
            dc = evt.GetDC()
        except:
            dc = wx.ClientDC(self)
            rect = self.GetUpdateRegion().GetBox()
            dc.SetClippingRect(rect)
        dc.Clear()
        self.panel1.onErase(dc)

    def onButton(self, evt):
        self.panel1.showbmp = not self.panel1.showbmp
        wx.PostEvent(self.panel1, wx.PyCommandEvent(wx.wxEVT_ERASE_BACKGROUND))


if __name__ == '__main__':

    app = wx.App()
    Example(None, title='Example')
    wx.lib.inspection.InspectionTool().Show()   # use this for debugging GUI design
    app.MainLoop()

How do I tell wxpython to draw all the non-background stuff again? Alternatively, how do I not un-draw it in the first place?

1

1 Answers

1
votes

After working on it for a few days, I got it! And the answer is trivially simple (as usual).

wx.PostEvent(self.panel1, wx.PyCommandEvent(wx.wxEVT_ERASE_BACKGROUND)) should be replaced with self.Refresh() to refresh the whole frame and not just force a specific (and apparently unsafe) redraw.