wx version: 2.8.12.1
I'm trying to build a decored Dialog (Background, Buttons, Bitmap Border, etc) but on ShowModal()
the Paint event is not issued.
It works with Show()
super seeding wx.PopupTransientWindow
, but not with Show()
or ShowModal()
on wx.Dialog
If you run the example, open the Dialog and click either of the two buttons, you'll get on the terminal:
send refresh
Clicked OK/CANCEL
"Paint Event"
or "Draw Function"
won't be printed.
"send refresh"
indicates that a manual Refresh()
has been issued; and that should issue an wx.EVT_PAINT
.
If OnPaint
is binded to wx.EVT_SHOW
the functions will be called, the wx.BufferedPaintDC
will be correctly set, but it won't change anything visible.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import wx
def GetThemeTools(borderWidth, backgrounColour):
return {
'Pens': {
'DarkRectSolidBorder': wx.Pen( wx.Colour(67, 67, 67), borderWidth),
},
'Brushes': {
'Background': wx.Brush(backgrounColour),
'RectBoxFilling': wx.Brush( wx.Colour(119,120,119) ),
},
'ForegroundColor': wx.Colour(241,241,241),
'BackgroundColor': 'WHITE',
'Fonts': {
'Brief': wx.Font( pointSize=12,
family=wx.FONTFAMILY_SWISS,
style=wx.FONTSTYLE_NORMAL,
weight=wx.FONTWEIGHT_NORMAL,
encoding=wx.FONTENCODING_UTF8
),
},
}
class ConfirmDialog(wx.Dialog):
def __init__(self, parent, text="",
margin=10, borderRadio=10):
wx.Dialog.__init__(self, parent, style=wx.STAY_ON_TOP)
# Get data to show
self.parent = parent
self._margin = margin
self._borderRadio = borderRadio
self._text = text
self._font = None
self.initializeTools()
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_SHOW, self._sendRefresh)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self._setWidgets()
self._layout()
wx.CallAfter(self.Refresh)
def _sendRefresh(self, e):
if self.IsShown():
print("send refresh")
self.Refresh()
def _setWidgets(self):
self.panel = wx.Panel(self)
self.message = wx.StaticText(self.panel, label=self.GetText())
self.message.SetForegroundColour(self.tools['ForegroundColor'])
self.message.SetBackgroundColour(self.tools['BackgroundColor'])
self.cancelBtn = wx.Button(self.panel, id=wx.ID_CANCEL, label="Cancel")
self.okBtn = wx.Button(self.panel, id=wx.ID_OK, label="Ok")
def _layout(self):
self.vSizer = wx.BoxSizer(wx.VERTICAL)
self.buttonsSizer = wx.BoxSizer(wx.HORIZONTAL)
self.buttonsSizer.Add(self.okBtn)
self.buttonsSizer.AddSpacer(20)
self.buttonsSizer.Add(self.cancelBtn)
self.vSizer.Add(self.message, 0, wx.CENTER|wx.BOTTOM, 5)
self.vSizer.Add(self.buttonsSizer, 0, wx.CENTER|wx.TOP, 5)
self.panel.SetSizer(self.vSizer)
self.vSizer.Fit(self.panel)
def SetMargin(self, margin):
self._margin = margin
self.Refresh()
def GetMargin(self):
return self._margin
def SetBorderRadio(self, borderRadio):
self._borderRadio = borderRadio
self.Refresh()
def GetBorderRadio(self):
return self._borderRadio
def SetText(self, text):
self._text = text
self.Refresh()
def GetText(self):
return self._text
def GetFont(self):
if not self._font:
self._font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
return self._font
def initializeTools(self):
self.borderWidth = 6
backColour = self.GetBackgroundColour()
self.tools = GetThemeTools(self.borderWidth, backColour)
self._font = self.tools['Fonts']['Brief']
def OnPaint(self, event):
print("Paint Event")
dc = wx.BufferedPaintDC(self)
self.Draw(dc)
event.Skip()
def Draw(self, dc):
print("Draw Function")
margin = self.GetMargin()
borderRadio = self.GetBorderRadio()
text = self.GetText()
self.Layout()
dc.SetBackground(self.tools['Brushes']['Background'])
dc.SetFont(self.GetFont())
dc.SetTextBackground(self.tools['BackgroundColor'])
dc.Clear()
(H, W) = self.GetSize()
# Draw Border
dc.SetPen(self.tools['Pens']['DarkRectSolidBorder'])
dc.SetBrush(self.tools['Brushes']['RectBoxFilling'])
dc.DrawRoundedRectangle( 0, 0, H, W, borderRadio)
def OnEraseBackground(self, event):
pass
class AppFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Custom Dialog Test")
panel = wx.Panel(self)
frameSizer = wx.BoxSizer(wx.VERTICAL)
panelSizer = wx.BoxSizer(wx.VERTICAL)
btn = wx.Button(panel, label="Show Dialog")
btn.Bind(wx.EVT_BUTTON, self.ShowDlg)
panelSizer.Add(btn, 0, wx.ALL, 20)
panel.SetSizer(panelSizer)
self.SetSizer(frameSizer)
panelSizer.Fit(panel)
self.Layout()
self.Show()
def ShowDlg(self, event):
dia = ConfirmDialog(self, "Custom Dialog\nTest\nThird Line")
if dia.ShowModal() == wx.ID_OK:
print("Clicked OK")
else:
print("Clicked CANCEL")
app = wx.App(False)
frame = AppFrame()
app.MainLoop()
Following the hint on Drawing To Panel Inside of Frame:
I got wrong the bind of the Paint Event
self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
self.panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
For some reason I still don't fully get (examples at self.Bind vs self.button.Bind) why the panel won't Skip()
the Paint Event; leading to self
never getting aware of the Paint Event.
And the parenthood of the canvas:
dc = wx.BufferedPaintDC(self.panel)
After playing a little bit subclassing from wx.PopupWindow
(which resulted in a badly sized Window that I couldn't properly center in the frame) I realized that the DC was being rendered below the panel. It makes sence since self.panel
is a child of self
, so only self
was being painted.
Not an Error but on the Paint Event, it seems the panel or dialog gets back to their default size. If the panel is fit, the dialog is resized, and the window is centered again in the Paint Event, then new Paint Events will be raised and a flicker will appear. Since this is an parameterized static dialog, the dimensions can be fixed with this at the end of self._layout()
:
self.panel.Fit()
minSize = self.panel.GetSize()
self.SetMinSize(minSize)
self.SetMaxSize(minSize)