BPoy
BPoy

Reputation: 159

Drawing to Panel inside of Frame in wxPython

Below, I have a panel inside of a frame. Why am I not able to draw to the panel? I just get a plain white screen. If I get rid of the panel and draw directly to the frame...it works. any help would be appreciated.

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)

Upvotes: 6

Views: 5464

Answers (2)

sdaau
sdaau

Reputation: 38691

I was looking for quite a bit for a complete working example of a custom wx Python control which subclasses wx.Panel and does custom drawing on itself, and I couldn't find any. Thanks to this (and other) questions, I finally managed to come to a minimal working example - which I'm going to post here, because it does show "drawing to Panel inside of a Frame"; except, unlike the OP, where the Frame does the drawing on the Panel - here the Panel draws on itself (while sitting in the Frame).

The code produces something like this:

test.png

... and basically the red rectangle will be redrawn when you resize the window.

Note the code comments, especially about the need to Refresh() in OnSize to avoid corrupt rendering / flicker.

The MWE code:

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()

Upvotes: 1

Petr Blahos
Petr Blahos

Reputation: 2433

Try binding the event to the panel, not to the whole frame:

self.panel.Bind(wx.EVT_PAINT, self.onPaint)

Your version kind-of work for me (windows), but it keeps redrawing the panel so it eats up the whole processor.

From documentation: Note that In a paint event handler, the application must always create a wxPaintDC object, even if you do not use it. Otherwise, under MS Windows, refreshing for this and other windows will go wrong.

Here, you received the paint event for the whole frame but used the dc for the panel.

EDIT: This http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind explains quite nicely why this won't work:

self.Bind(wx.EVT_PAINT, self.onPaint, self.panel)

In this case the onPaint handler is never called.

Upvotes: 8

Related Questions