Dialog 的 ShowModal() 不会发送 EVT_PAINT

Dialog's ShowModal() won't send EVT_PAINT

wx 版本:2.8.12.1

我正在尝试构建装饰对话框(背景、按钮、位图边框等),但在 ShowModal() 上未发出 Paint 事件。

它适用于 Show() 超级播种 wx.PopupTransientWindow,但不适用于 Show()ShowModal() wx.Dialog

如果您 运行 示例,打开对话框并单击两个按钮中的任何一个,您将进入终端:

send refresh
Clicked OK/CANCEL

"Paint Event""Draw Function" 不会被打印出来。 "send refresh" 表示已发出手册Refresh();那应该发出 wx.EVT_PAINT.

如果 OnPaint 绑定到 wx.EVT_SHOW 函数将被调用,wx.BufferedPaintDC 将被正确设置,但它不会改变任何可见的东西。

#!/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()

遵循 Drawing To Panel Inside of Frame 上的提示:

  • Paint Event绑定错误

    self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
    self.panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
    

    出于某种原因,我仍然没有完全理解(self.Bind vs self.button.Bind 处的示例)为什么面板不会 Skip() 绘制事件;导致 self 永远不会意识到 Paint 事件。

  • 以及 canvas 的父母身份:

    dc = wx.BufferedPaintDC(self.panel)
    

    玩了一点 wx.PopupWindow 的子类后(这导致 Window 尺寸不佳,我无法在框架中正确居中)我意识到 DC 被渲染在控制板。这是有道理的,因为 self.panelself 的 child,所以只绘制了 self


不是错误,但在 Paint 事件中,面板或对话框似乎恢复到默认大小。如果面板适合,调整对话框大小,并且 window 在 Paint Event 中再次居中,然后将引发新的 Paint Events 并出现闪烁。由于这是一个参数化的静态对话框,尺寸可以固定在 self._layout():

的末尾
self.panel.Fit()
minSize = self.panel.GetSize()
self.SetMinSize(minSize)
self.SetMaxSize(minSize)

您已将绘制事件处理程序绑定到对话框,但对话框有一个面板完全覆盖了对话框。由于对话框的表面永远不会暴露(因为它在面板下面),所以它永远不需要绘制,所以它永远不会从系统接收到绘制事件。

在大多数情况下,您不需要将面板作为对话框的顶部元素,因此您可能希望将其删除并仅将那部分代码中的所有 self.panel 替换为 self.