在wxPython中在框架内绘制面板
下面,我在一个框架里有一个面板。为什么我不能在这个面板上画东西呢?我只看到一片白屏。如果我去掉面板,直接在框架上画,就可以了。希望能得到一些帮助。
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,-1,'window',(200,200),(600,600))
self.Center()
self.panel=wx.Panel(self)
self.panel.SetBackgroundColour('white')
self.firstpoint=wx.Point(300,300)
self.secondpoint=wx.Point(400,400)
self.Bind(wx.EVT_PAINT,self.onPaint)
def onPaint(self,event):
dc=wx.PaintDC(self.panel)
dc.DrawLine(self.firstpoint.x,self.firstpoint.y,
self.secondpoint.x,self.secondpoint.y)
2 个回答
1
我一直在寻找一个完整的示例,想要了解如何创建一个自定义的 wx Python 控件,这个控件是从 wx.Panel
继承而来的,并且能够在自己上面进行自定义绘图,但一直没找到。感谢这个问题(和其他一些问题),我终于搞出了一个最小的可工作示例——我决定把它分享出来,因为它展示了“在一个框架内的面板上绘图”;不过,与原问题的作者不同的是,这里是面板自己在绘图(而不是框架在面板上绘图)。
这段代码的效果大概是这样的:
... 基本上,当你调整窗口大小时,红色的矩形会被重新绘制。
注意代码中的注释,特别是关于在 OnSize 方法中需要调用 Refresh(),以避免出现渲染错误或闪烁的问题。
下面是最小工作示例的代码:
import wx
# tested on wxPython 2.8.11.0, Python 2.7.1+, Ubuntu 11.04
# http://stackoverflow.com/questions/2053268/side-effects-of-handling-evt-paint-event-in-wxpython
# http://stackoverflow.com/questions/25756896/drawing-to-panel-inside-of-frame-in-wxpython
# http://www.infinity77.net/pycon/tutorial/pyar/wxpython.html
# also, see: wx-2.8-gtk2-unicode/wx/lib/agw/buttonpanel.py
class MyPanel(wx.Panel): #(wx.PyPanel): #PyPanel also works
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, name="MyPanel"):
super(MyPanel, self).__init__(parent, id, pos, size, style, name)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnSize(self, event):
print("OnSize" +str(event))
#self.SetClientRect(event.GetRect()) # no need
self.Refresh() # MUST have this, else the rectangle gets rendered corruptly when resizing the window!
event.Skip() # seems to reduce the ammount of OnSize and OnPaint events generated when resizing the window
def OnPaint(self, event):
#~ dc = wx.BufferedPaintDC(self) # works, somewhat
dc = wx.PaintDC(self) # works
print(dc)
rect = self.GetClientRect()
# "Set a red brush to draw a rectangle"
dc.SetBrush(wx.RED_BRUSH)
dc.DrawRectangle(10, 10, rect[2]-20, 50)
#self.Refresh() # recurses here!
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "Custom Panel Demo")
self.SetSize((300, 200))
self.panel = MyPanel(self) #wx.Panel(self)
self.panel.SetBackgroundColour(wx.Colour(10,10,10))
self.panel.SetForegroundColour(wx.Colour(50,50,50))
sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
sizer_1.Add(self.panel, 1, wx.EXPAND | wx.ALL, 0)
self.SetSizer(sizer_1)
self.Layout()
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
8
试着把事件绑定到面板上,而不是整个窗口:
self.panel.Bind(wx.EVT_PAINT, self.onPaint)
你的版本在我这(Windows)有点效果,但它一直在重新绘制面板,导致整个处理器都被占满了。
根据文档:注意,在绘制事件处理器中,应用程序必须始终创建一个 wxPaintDC 对象,即使你不使用它。否则,在 Windows 系统下,刷新这个窗口和其他窗口时会出问题。
在这里,你收到了整个窗口的绘制事件,但却使用了面板的 dc。
补充:这个 http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind 解释得很清楚,为什么这样做不行:
self.Bind(wx.EVT_PAINT, self.onPaint, self.panel)
在这种情况下,onPaint 处理器根本不会被调用。