wxPython 油漆损坏,裁剪区域

wxPython Paint Damaged, Clipped area

我有以下简单的代码(点击粉红色框,你可以在按住鼠标左键的同时用鼠标移动它)。

import wx


class AppPanel(wx.Panel):

    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        p = MovablePanel(self, -1)
        self.i = 0
        self.Bind(wx.EVT_PAINT, self.OnPaint, self)

    def OnPaint(self, event):
        dc = wx.PaintDC(self)
        self.i = self.i+10
        c = self.i % 255
        c = (0, 0, c)
        dc.SetPen(wx.Pen(c))
        dc.SetBrush(wx.Brush(c))
        dc.DrawRectangle(0, 0, 10000,10000)





class MovablePanel(wx.Panel):

    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        self.SetMinSize((500,500))
        self.SetSize((500,500))
        self.SetBackgroundColour("PINK")
        self.LEFT_DOWN = False
        self.Bind(wx.EVT_MOTION, self.OnMove, self)

        self.Bind(wx.EVT_LEFT_DOWN,
                         self.OnClickDown,
                         self)


        self.Bind(wx.EVT_LEFT_UP,
                         self.OnClickUp,
                         self)

    def OnClickUp(self, event):
        self.LEFT_DOWN = False
        self.Refresh()

    def OnClickDown(self, event):    
        self.LEFT_DOWN = True
        self.Refresh()

    def OnMove(self, event):
        if self.LEFT_DOWN:
            p = self.GetTopLevelParent().ScreenToClient(wx.GetMousePosition())
            self.SetPosition(p)


if __name__ == "__main__":
    app = wx.App(False)
    f   = wx.Frame(None, -1, size = (700, 700))
    p   = AppPanel(f, -1)
    f.Show()
    f.Maximize()
    app.MainLoop()

假设如下所示(只需调整框架大小)

但是把粉红色的方块移动一下你就会发现它真的是这个样子

我试过以下方法

dc.Clear()

dc.DestroyClippingRegion() 

wx.FULL_REPAINT_ON_RESIZE

wx.EVT_ERASE_BACKGROUND

我很确定这与它是一个面板有关,因此 PaintEvent 仅将其标记为部分损坏。这部分的颜色不同,使 'ghosting' 或 'smearing' 显而易见。也许我使用了错误的词,因为我无法找到解决方案(而且我认为这似乎是一个不复杂的问题,仅与 'damaged' 区域有关)。

好的,我找到了问题,但稍后我会尝试 post 更多详细信息。

这段代码的目标基本上是移动面板,然后更新父面板。 SetPosition 调用 Move,它通过 wxWidget 代码调用 DoMoveWindow,所有这些都会导致位置变化和重绘调用(尚不确定调用重绘的是什么)。伟大的。然而,重绘只标记了某个 'area',因为它试图提高效率。这就是为什么有些问题可以通过让面板越过 'ghosted' 区域来解决的原因。您需要做的是在 SetPosition 之后,调用 GetParent().Refresh(),这将发送 'full' 没有任何排除区域的绘画。

另一件需要注意的事情是 'damaged' 或 'clipped' 区域有两个术语。一个是 'damage',但还有另一个 'dirty'。 wx PaintDC信息中使用了损坏

Using wx.PaintDC within EVT_PAINT handlers is important because it automatically sets the clipping area to the damaged area of the window. Attempts to draw outside this area do not appear.

相信文档,您将大失所望。然而,在其中一个 wxPython DoubleBuffer 中,行话如何改变(但它与 'damage' 相同)

Now the OnPaint() method. It's called whenever ther is a pain event sent by the system: i.e. whenever part of the window gets dirty.

知道这一点,如果你 Google wx Window 脏了你会得到以下内容

Mark the specified rectangle (or the whole window) as "dirty" so it will be repainted. Causes an EVT_PAINT event to be generated and sent to the window.

采取以下三个打印周期,其中 EVT_PAINT 在 SetPosition 调用后触发(这是 WITHOUT GetParent().Refresh() 调用)

# first EVT_PAINT
Drawing
Panel Size (1440, 851)
Clipping Rect (0, 0, 1440, 851)
Client Update Rect (x=0, y=6, w=500, h=501) # the only place getting update is 
                                            # directly below the panel 
                                            # (that is (500, 500) )

# second
Drawing
Panel Size (1440, 851)
Clipping Rect (0, 0, 1440, 851)
Client Update Rect (x=0, y=6, w=910, h=845) # however this time the update area is                                                                                                                            
                                            # bigger, this is also right before
                                            # the move
                                            # i believe what it is doing is
                                            # drawing from (0,6) to (910, 851)
                                            # why? because the panel is moving to
                                            # (410, 390) and the bottom right
                                            # corner of the panel (after moved)
                                            # is (410+500, 390+461) = (910, 851)
                                            # or about where the edge of the panel
                                            # will be

# third
Drawing
Panel Size (1440, 851)
Clipping Rect (0, 0, 1440, 851)
Client Update Rect (x=410, y=390, w=500, h=461)

这是可以试用的更新代码,希望这对其他人有所帮助。

import wx

instructions = """
How to use.
1) Hover your mouse over the pink panel.
2) Click down (left click)
3) While holding down drag mouse around
4) Release mouse button to stop.

"""

class AppPanel(wx.Panel):

    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.settings_sizer = wx.BoxSizer(wx.HORIZONTAL)
        p = MovablePanel(self, -1)

        self.c = wx.CheckBox(self, -1, label = "Ghosting On?")
        self.p = p
        self.i = 0

        self.settings_sizer.Add(self.c)

        self.sizer.Add(self.settings_sizer)
        self.sizer.Add(self.p)
        self.SetSizer(self.sizer)
        self.Layout()

        self.Bind(wx.EVT_CHECKBOX, self.OnCheck, self.c)
        self.Bind(wx.EVT_PAINT, self.OnPaint, self)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase, self)


    def OnCheck(self, event):
        print "CHECK\n\n\n\n\n"
        v = self.c.GetValue()
        self.p.r = v
        print v


    def OnErase(self, event):
        pass

    def OnPaint(self, event):
        print "Drawing"
        dc = wx.PaintDC(self)
        print "Panel Rect, ", self.p.GetPosition(),
        print self.p.GetSize()
        print "Clipping Rect", dc.GetClippingBox()
        print "Client Update Rect", self.GetUpdateClientRect()
        print "----------------------------"
        self.i = self.i+10
        c = self.i % 255
        c = (0, 0, c)
        dc.SetPen(wx.Pen(c))
        dc.SetBrush(wx.Brush(c))

        dc.DrawRectangle(0, 0, 10000,10000)
        self.SetBackgroundColour(c)

        dc.SetPen(wx.Pen("WHITE"))
        dc.SetBrush(wx.Brush("WHITE"))


        dc.DrawRectangle(0, 0, self.GetSize()[0], self.c.GetSize()[1])





class MovablePanel(wx.Panel):

    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        self.SetMinSize((300,300))
        self.SetSize((300,300))

        txt = wx.StaticText(self, -1, label = "CLICK AND DRAG ME!")
        inst = wx.StaticText(self, -1, label = instructions)

        font = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)
        txt.SetFont(font)
        inst.SetFont(font)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(txt, flag = wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_CENTRE_HORIZONTAL)
        sizer.Add(inst, flag = wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_CENTRE_HORIZONTAL)
        self.SetSizer(sizer)

        self.SetBackgroundColour("PINK")
        self.LEFT_DOWN = False
        self.r = False
        self.Bind(wx.EVT_MOTION, self.OnMove, self)

        self.Bind(wx.EVT_LEFT_DOWN,
                         self.OnClickDown,
                         self)


        self.Bind(wx.EVT_LEFT_UP,
                         self.OnClickUp,
                         self)

    def OnClickUp(self, event):
        self.LEFT_DOWN = False
        self.Refresh()

    def OnClickDown(self, event):    
        self.LEFT_DOWN = True
        self.Refresh()

    def OnMove(self, event):
        if self.LEFT_DOWN:
            p = self.GetTopLevelParent().ScreenToClient(wx.GetMousePosition())
            self.SetPosition(p)
            if not self.r:
                self.GetParent().Refresh()


if __name__ == "__main__":
    app = wx.App(False)
    f   = wx.Frame(None, -1, size = (700, 700))
    p   = AppPanel(f, -1)
    f.Show()
    app.MainLoop()