diff --git a/pymlac/pymlac b/pymlac/pymlac index 00cef3e..2f8632c 100755 --- a/pymlac/pymlac +++ b/pymlac/pymlac @@ -369,12 +369,10 @@ class MyFrame(wx.Frame): size=(self.WIDTH_SCREEN, self.HEIGHT_SCREEN), pos=(0,0)) self.screen.SetBackgroundColour(self.SCREEN_COLOUR) - self.screen.Bind(wx.EVT_PAINT, self.on_paint) self.console = wx.Panel(self, style=wx.SIMPLE_BORDER, size=(self.WIDTH_CONSOLE, self.HEIGHT_SCREEN), pos=(self.WIDTH_SCREEN,0)) self.console.SetBackgroundColour(self.CONSOLE_COLOUR) - self.console.Bind(wx.EVT_PAINT, self.on_paint) python_png = wx.Image('images/PythonPowered.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap() @@ -698,12 +696,10 @@ class MyFrame(wx.Frame): size=(self.WIDTH_SCREEN, self.HEIGHT_SCREEN), pos=(0,0)) self.screen.SetBackgroundColour(self.SCREEN_COLOUR) - self.screen.Bind(wx.EVT_PAINT, self.on_paint) self.console = wx.Panel(self, style=wx.SIMPLE_BORDER, size=(self.WIDTH_CONSOLE, self.HEIGHT_SCREEN), pos=(self.WIDTH_SCREEN,0)) self.console.SetBackgroundColour(self.CONSOLE_COLOUR) - self.console.Bind(wx.EVT_PAINT, self.on_paint) python_png = wx.Image('images/PythonPowered.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap() @@ -786,6 +782,9 @@ class MyFrame(wx.Frame): self.Fit() + self.screen.Bind(wx.EVT_PAINT, self.on_paint) + self.console.Bind(wx.EVT_PAINT, self.on_paint) + def on_paint(self, event): global count diff --git a/pymlac/pymlac_display.py b/pymlac/pymlac_display.py new file mode 100755 index 0000000..348cc9a --- /dev/null +++ b/pymlac/pymlac_display.py @@ -0,0 +1,268 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +A display widget for the pymlac interpreter. +""" + + +import os +import sys +import traceback +import wx + +# if we don't have log.py, don't crash +try: + import log + log = log.Log('test.log', log.Log.DEBUG) +except ImportError: + def log(*args, **kwargs): + pass + + +# the widget version +__version__ = '0.0' + +# type of events +DisplayStop = 1 + + +###### +# Base class for the widget canvas - buffered and flicker-free. +###### + +class _BufferedCanvas(wx.Panel): + """Implements a buffered, flicker-free canvas widget. + + This class is based on: + http://wiki.wxpython.org/index.cgi/BufferedCanvas + """ + + # The backing buffer + buffer = None + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.NO_FULL_REPAINT_ON_RESIZE): + """Initialise the canvas. + + parent reference to 'parent' widget + id the unique widget ID (NB: shadows builtin 'id') + pos canvas position + size canvas size + style wxPython style + """ + + wx.Panel.__init__(self, parent, id, pos, size, style) + + # Bind events + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_SIZE, self.OnSize) + + # Disable background erasing (flicker-licious) + def disable_event(*args, **kwargs): + pass # the sauce, please + self.Bind(wx.EVT_ERASE_BACKGROUND, disable_event) + + # set callback upon onSize event + self.onSizeCallback = None + + def Draw(self, dc): + """Stub: called when the canvas needs to be re-drawn.""" + + pass + + def Update(self): + """Causes the canvas to be updated.""" + + dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) + dc.BeginDrawing() + dc.Clear() # because maybe view size > map size + self.Draw(dc) + dc.EndDrawing() + + def OnPaint(self, event): + """Paint the canvas to the screen.""" + + # Blit the front buffer to the screen + wx.BufferedPaintDC(self, self.buffer) + + def OnSize(self, event=None): + """Create a new off-screen buffer to hold drawn data.""" + + (width, height) = self.GetClientSizeTuple() + if width == 0: + width = 1 + if height == 0: + height = 1 + + minsize = min(width, height) + self.SetSize((minsize, minsize)) + + self.view_width = minsize + self.view_height = minsize + + # new off-screen buffer + self.buffer = wx.EmptyBitmap(minsize, minsize) + + # call onSize callback, if registered + if self.onSizeCallback: + self.onSizeCallback() + + # Now update the screen + self.Update() + +############################################################################### +# Define the events that are raised by the display widget. +############################################################################### + +# display stop +_myEVT_DISPLAY_STOP = wx.NewEventType() +EVT_DISPLAY_STOP = wx.PyEventBinder(_myEVT_DISPLAY_STOP, 1) + +class _DisplayEvent(wx.PyCommandEvent): + """Event sent from the display widget.""" + + def __init__(self, eventType, id): + """Construct a display event. + + eventType type of event + id unique event number + + Event will be adorned with attributes by raising code. + """ + + wx.PyCommandEvent.__init__(self, eventType, id) + +############################################################################### +# The display widget proper +############################################################################### + +class PymlacDisplay(_BufferedCanvas): + """A widget to display pymlac draw lists.""" + + # line colour + DrawColour = '#ffff00' + + # panel background colour + BackgroundColour = '#000000' + + # max coorcinates of pymlac display + PymlacMaxX = 1024 + PymlaxMaxY = 1024 + + def __init__(self, parent, **kwargs): + """Initialise a display instance. + + parent reference to parent object + **kwargs keyword args for Panel + """ + + # create and initialise the base panel + _BufferedCanvas.__init__(self, parent=parent, **kwargs) + self.SetBackgroundColour(PymlacDisplay.BackgroundColour) + + # set some internal state + self.default_cursor = wx.CURSOR_DEFAULT + self.drawlist = None # list of (x1,y1,x2,y2) + + # set callback when parent resizes +# self.onSizeCallback = self.ResizeCallback + + # force a resize, which sets up the rest of the state + # eventually calls ResizeCallback() + self.OnSize() + + ###### + # GUI stuff + ###### + + def OnKeyDown(self, event): + pass + + def OnKeyUp(self, event): + pass + + def OnLeftUp(self, event): + """Left mouse button up. + """ + + pass + + def OnMiddleUp(self, event): + """Middle mouse button up. Do nothing in this version.""" + + pass + + def OnRightUp(self, event): + """Right mouse button up. + + Note that when we iterate through the layer_z_order list we must + iterate on a *copy* as the user select process can modify + self.layer_z_order. + + THIS CODE HASN'T BEEN LOOKED AT IN A LONG, LONG TIME. + """ + + pass + + def Draw(self, dc): + """Do actual widget drawing. + Overrides the _BufferedCanvas.draw() method. + + dc device context to draw on + + Draws the current drawlist to the screen. + """ + + scaleX = self.view_width*1.0 / self.PymlacMaxX + scaleY = self.view_height*1.0 / self.PymlaxMaxY + dc.SetUserScale(min(scaleX,scaleY), min(scaleX,scaleY)) + + if self.drawlist: + dc.SetPen(wx.Pen(self.DrawColour)) + dc.DrawLineList(self.drawlist) + + ###### + # Change the drawlist + ###### + + def Drawlist(self, dl): + self.drawlist = dl + self.Update() + + ###### + # Routines for display events + ###### + + def RaiseEventStop(self): + """Raise a display stop event.""" + + event = _DisplayEvent(_myEVT_DISPLAY_STOP, self.GetId()) + self.GetEventHandler().ProcessEvent(event) + + def info(self, msg): + """Display an information message, log and graphically.""" + + log_msg = '# ' + msg + length = len(log_msg) + prefix = '#### Information ' + banner = prefix + '#'*(80 - len(log_msg) - len(prefix)) + log(banner) + log(log_msg) + log(banner) + + wx.MessageBox(msg, 'Warning', wx.OK | wx.ICON_INFORMATION) + + def warn(self, msg): + """Display a warning message, log and graphically.""" + + log_msg = '# ' + msg + length = len(log_msg) + prefix = '#### Warning ' + banner = prefix + '#'*(80 - len(log_msg) - len(prefix)) + log(banner) + log(log_msg) + log(banner) + + wx.MessageBox(msg, 'Warning', wx.OK | wx.ICON_ERROR) + diff --git a/pymlac/test_pymlac_display.py b/pymlac/test_pymlac_display.py new file mode 100755 index 0000000..b45de1a --- /dev/null +++ b/pymlac/test_pymlac_display.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Test the pymlac display widget. + +Usage: test_pymlac_display.py [-h] +""" + + +import wx +import pymlac_display as Display + +# if we don't have log.py, don't crash +try: + import log + log = log.Log('test.log', log.Log.DEBUG) +except ImportError: + def log(*args, **kwargs): + pass + + +###### +# Various demo constants +###### + +DefaultAppSize = (200, 200) + +################################################################################ +# The main application frame +################################################################################ + +class TestFrame(wx.Frame): + def __init__(self): + wx.Frame.__init__(self, None, size=DefaultAppSize, + title=('Test pymlac display - %s' + % Display.__version__)) + self.SetMinSize(DefaultAppSize) + self.panel = wx.Panel(self, wx.ID_ANY) + self.panel.ClearBackground() + + # build the GUI + box = wx.BoxSizer(wx.VERTICAL) + self.display = Display.PymlacDisplay(self.panel) + box.Add(self.display, proportion=1, border=1, flag=wx.EXPAND) + self.panel.SetSizer(box) + self.panel.Layout() + self.Centre() + self.Show(True) + + self.Bind(wx.EVT_SIZE, self.OnSize) + + self.display.Drawlist([(0,0,1024,1024), (1024,0,0,1024)]) + + def OnSize(self, event): + """Maintain square window.""" + + if deltaw is None: + +# (fwidth, fheight) = self.GetSize() +# log('####: fwidth=%d, fheight=%d' % (fwidth, fheight)) +# +# (pwidth, pheight) = self.display.GetClientSizeTuple() +# deltaw = fwidth - pwidth +# deltah = fheight - pheight +# log('####: deltaw=%d, deltah=%d' % (deltaw, deltah)) +# +# psize = min(pwidth, pheight) +# fsize = (pwidth+deltaw, pheight+deltah) +# self.SetSize(fsize) + + event.Skip() + +################################################################################ + +if __name__ == '__main__': + import sys + import getopt + import traceback + + # print some usage information + def usage(msg=None): + if msg: + print(msg+'\n') + print(__doc__) # module docstring used + + # our own handler for uncaught exceptions + def excepthook(type, value, tb): + msg = '\n' + '=' * 80 + msg += '\nUncaught exception:\n' + msg += ''.join(traceback.format_exception(type, value, tb)) + msg += '=' * 80 + '\n' + print msg + sys.exit(1) + + # plug our handler into the python system + sys.excepthook = excepthook + + # decide which tiles to use, default is GMT + argv = sys.argv[1:] + + try: + (opts, args) = getopt.getopt(argv, 'h', ['help']) + except getopt.error: + usage() + sys.exit(1) + + for (opt, param) in opts: + if opt in ['-h', '--help']: + usage() + sys.exit(0) + + # start wxPython app + app = wx.App() + TestFrame().Show() + app.MainLoop() +