views:

144

answers:

2

I've written this small app that draws lines between two points selected by the user and it works but how do I keep the lines I draw from disappearing whenever the window is minimized or gets covered by another open window?

class SimpleDraw(wx.Frame):
  def __init__(self, parent, id, title, size=(640, 480)):
    self.points = []
    wx.Frame.__init__(self, parent, id, title, size)

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

    self.SetBackgroundColour("WHITE")
    self.Centre()
    self.Show(True)

  def DrawDot(self, event):
    self.points.append(event.GetPosition())
    if len(self.points) == 2:
        dc = wx.ClientDC(self)
        dc.SetPen(wx.Pen("#000000", 10, wx.SOLID))
        x1, y1 = self.points[0]
        x2, y2 = self.points[1]
        dc.DrawLine(x1, y1, x2, y2)
        # reset the list to empty
        self.points = []

if __name__ == "__main__":
  app = wx.App()
  SimpleDraw(None, -1, "Title Here!")
  app.MainLoop()
A: 

You have to structure your program differently in a GUI environment. Typically, you maintain a data structure called your model. In your case, you already have a start of one, self.points. Then you only draw on the window in response to a paint event. The windowing system will send you paint events when the window needs painting, including when it is first displayed, when it is maximized, and when it is revealed from beneath another window.

In your program, you'd bind the LeftDown event to a function that modifies self.points and invalidates the window, which would usually cause the windowing system to send you paint events. You'd bind the Paint event to a function that draws on the window.

Ned Batchelder
What do you mean by "invalidates the window"?
cornjuliox
In a windowed GUI, the windowing system keeps track of which parts of windows are properly painted, and which are not. When your underlying model changes, you don't paint directly in the window. Instead, you tell the windowing system that you are no longer correctly painted. This is called "invalidating the window". It then sends you paint events to repaint it.
Ned Batchelder
A: 

Your issue is that you are only drawing when the user clicks. The resize/erase (when another window covers yours) problems are because your window doesn't maintain a "buffer" which it can redraw.

Here, I've modified your sample, it seems to be working okay.

import wx

class SimpleDraw(wx.Frame):
    def __init__(self, parent, id, title, size=(640, 480)):
        self.points = []
        wx.Frame.__init__(self, parent, id, title, size)

        self.Bind(wx.EVT_LEFT_DOWN, self.DrawDot)
        self.Bind(wx.EVT_PAINT, self.Paint)

        self.SetBackgroundColour("WHITE")
        self.Centre()
        self.Show(True)
        self.buffer = wx.EmptyBitmap(640, 480)  # draw to this
        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
        dc.Clear()  # black window otherwise


    def DrawDot(self, event):
        self.points.append(event.GetPosition())
        if len(self.points) == 2:
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
            dc.Clear()
            dc.SetPen(wx.Pen("#000000", 10, wx.SOLID))
            x1, y1 = self.points[0]
            x2, y2 = self.points[1]
            dc.DrawLine(x1, y1, x2, y2)
            # reset the list to empty
            self.points = []


    def Paint(self, event):
        wx.BufferedPaintDC(self, self.buffer)


if __name__ == "__main__":
    app = wx.App(0)
    SimpleDraw(None, -1, "Title Here!")
    app.MainLoop()
Steven Sproat