#pragma section-numbers off = wxOGL = OGL is an interesting graphics package. It has documentation problems. This page is a result of my (LionKimbro) research on OGL, and the contributions of several, including RobinDunn, and Nikolai. <> == Status == === Development Status === * OGL development appears to be dead. * There does not appear to be OGL maintenance. * There is no OGL community, outside of what you might find on the wxPython channels. However, I have not heard any word about it disappearing from the wxPython distribution. "I'm not dead yet!" More information about OGL's development status can be found in the wxPython demo notes: The OGL library was originally written in C++ and provided to wxPython via an extension module wrapper as is most of the rest of wxPython. The code has now been ported to Python (with many thanks to Pierre Hjälm!) in order to make it be more easily maintainable and less likely to get rusty because nobody cares about the C++ lib any more. View the source code at http://svn.wxwidgets.org/viewvc/wx/wxPython/trunk/wx/lib/ogl/ === Documentation Status === The standard OGL documentation is old and erronius. http://biolpc22.york.ac.uk/wx/contrib/docs/html/ogl/ogl.htm Standard OGL docs consist of: * class library documentation * notes * a complicated sample This wiki entry is an effort to fix the situation. If you are using OGL, please post your notes and corrections to this wiki page..! With some time, I'll feel confident enough to post a cookbook entry ([[wxPython Cookbook]]). I don't think it's important enough, yet, to warrent a challenge (ObstacleCourse). == Sample Code == === Minimal === This is a minimal application demonstrating wxOGL, contributed by LionKimbro. Lines with "#" at the end are not necessary. {{{ #!python import wx import wx.lib.ogl as ogl class AppFrame(wx.Frame): def __init__(self): wx.Frame.__init__( self, None, -1, "Demo", size=(300,200), style=wx.DEFAULT_FRAME_STYLE ) sizer = wx.BoxSizer( wx.VERTICAL ) # put stuff into sizer canvas = ogl.ShapeCanvas( self ) sizer.Add( canvas, 1, wx.GROW ) canvas.SetBackgroundColour( "LIGHT BLUE" ) # diagram = ogl.Diagram() canvas.SetDiagram( diagram ) diagram.SetCanvas( canvas ) shape = ogl.CircleShape( 20.0 ) # shape.SetX( 25.0 ) # shape.SetY( 25.0 ) # canvas.AddShape( shape ) # diagram.ShowAll( 1 ) # # apply sizer self.SetSizer(sizer) self.SetAutoLayout(1) self.Show(1) app = wx.PySimpleApp() ogl.OGLInitialize() frame = AppFrame() app.MainLoop() app.Destroy() }}} === Mouse Chasing Circle === ''The following entry was contributed by Nikolai.'' Here is a (not so ;-)) simple example of wxOGL, the first one for versions of wxPython before it went into the "new" wx namespace. Scroll down for one that works in wxPy2.6 on Py2.4... --TomPlunket It should still be easier to understand than the wxOGL.py demo that comes with wxPython. What this beast does is to keep a circle under the mouse-cursor as you move it. Attached values (self.circle.a i.e.) are kept and can be accessed later. {{{ #!python from wxPython.wx import * from wxPython.ogl import * wxOGLInitialize() class MyEvtHandler(wxShapeEvtHandler): """ Overwrite the default event handler to implement some custom features. """ def __init__(self): wxShapeEvtHandler.__init__(self) def OnLeftClick(self, x, y, keys = 0, attachment = 0): """ The dragging is done here. You should probably comment out the EVT_MOTION below, to see it work. """ shape = self.GetShape() print shape.__class__, shape.GetClassName(), shape.a canvas = shape.GetCanvas() dc = wxClientDC(canvas) canvas.PrepareDC(dc) if shape.Selected(): shape.Select(False, dc) canvas.Redraw(dc) else: redraw = False shapeList = canvas.GetDiagram().GetShapeList() toUnselect = [] for s in shapeList: if s.Selected(): toUnselect.append(s) shape.Select(True, dc) if toUnselect: for s in toUnselect: s.Select(False, dc) canvas.Redraw(dc) class OGLCanvas(wxShapeCanvas): def __init__(self, parent, frame): wxShapeCanvas.__init__(self, parent) self.SetBackgroundColour("LIGHT BLUE") self.diagram = wxDiagram() self.SetDiagram(self.diagram) self.diagram.SetCanvas(self) self.circle = wxCircleShape(100) self.circle.SetCanvas(self) self.circle.a="Circle identified" self.diagram.AddShape(self.circle) self.circle.Show(True) self.evthandler = MyEvtHandler() self.evthandler.SetShape(self.circle) self.evthandler.SetPreviousHandler(self.circle.GetEventHandler()) self.circle.SetEventHandler(self.evthandler) EVT_MOTION(self, self.OnMotion) def OnMotion(self, event): shape = self.evthandler.GetShape() bx = shape.GetX() by = shape.GetY() bw, bh = shape.GetBoundingBoxMax() oldrect = wxRect(int(bx-bw/2)-1, int(by-bh/2)-1, int(bw)+2, int(bh)+2) canvas = shape.GetCanvas() dc = wxClientDC(canvas) canvas.PrepareDC(dc) shape.Move(dc, event.GetPosition()[0], event.GetPosition()[1]) canvas.Refresh(False, oldrect) event.Skip() class OGLFrame(wxFrame): def __init__(self, *args, **kwds): wxFrame.__init__(self, *args, **kwds) self.SetTitle("OGL TEST") self.SetBackgroundColour(wxColour(8, 197, 248)) self.canvas = OGLCanvas(self, self) class Main(wxApp): def OnInit(self): wxInitAllImageHandlers() frame = OGLFrame(None, -1, "") self.SetTopWindow(frame) frame.Show(True) return True if __name__ == "__main__": app = Main() app.MainLoop() }}} For the "new" wx namespace, this bit works in wx 2.6 on Py 2.4. Mind, I still don't understand it entirely, but it works as expected. Yeah, I use tabs. Sue me. ;) {{{ #!python import wx import wx.lib.ogl as ogl CLICK_TO_DRAG = True class MyEvtHandler(ogl.ShapeEvtHandler): """ Overwrite the default event handler to implement some custom features. """ def __init__(self): ogl.ShapeEvtHandler.__init__(self) def OnLeftClick(self, x, y, keys = 0, attachment = 0): """ The dragging is done here. """ shape = self.GetShape() print shape.__class__, shape.GetClassName(), shape.a canvas = shape.GetCanvas() dc = wx.ClientDC(canvas) canvas.PrepareDC(dc) if shape.Selected(): shape.Select(False, dc) canvas.Redraw(dc) else: redraw = False shapeList = canvas.GetDiagram().GetShapeList() toUnselect = [] for s in shapeList: if s.Selected(): toUnselect.append(s) shape.Select(True, dc) if toUnselect: for s in toUnselect: s.Select(False, dc) canvas.Redraw(dc) class OGLCanvas(ogl.ShapeCanvas): def __init__(self, parent, frame): ogl.ShapeCanvas.__init__(self, parent) self.SetBackgroundColour("LIGHT BLUE") self.diagram = ogl.Diagram() self.SetDiagram(self.diagram) self.diagram.SetCanvas(self) self.circle = ogl.CircleShape(100) self.circle.SetCanvas(self) self.circle.a="Circle identified" self.diagram.AddShape(self.circle) self.circle.Show(True) if CLICK_TO_DRAG: self.evthandler = MyEvtHandler() self.evthandler.SetShape(self.circle) self.evthandler.SetPreviousHandler(self.circle.GetEventHandler()) self.circle.SetEventHandler(self.evthandler) else: self.Bind(wx.EVT_MOTION, self.OnMotion, self) def OnMotion(self, event): shape = self.circle bx = shape.GetX() by = shape.GetY() bw, bh = shape.GetBoundingBoxMax() oldrect = wx.Rect(int(bx-bw/2)-1, int(by-bh/2)-1, int(bw)+2, int(bh)+2) canvas = shape.GetCanvas() dc = wx.ClientDC(canvas) canvas.PrepareDC(dc) shape.Move(dc, event.GetPosition()[0], event.GetPosition()[1]) canvas.Refresh(False, oldrect) event.Skip() class OGLFrame(wx.Frame): def __init__(self, *args, **kwds): wx.Frame.__init__(self, *args, **kwds) self.SetTitle("OGL TEST") self.SetBackgroundColour(wx.Colour(8, 197, 248)) self.canvas = OGLCanvas(self, self) if __name__ == "__main__": app = wx.PySimpleApp(False) wx.InitAllImageHandlers() ogl.OGLInitialize() frame = OGLFrame(None, -1, "") app.SetTopWindow(frame) frame.Show(True) app.MainLoop() }}} == Documentation Errors == === wxShapeCanvas' Parent === [[http://biolpc22.york.ac.uk/wx/contrib/docs/html/ogl/ogl27.htm#ogloverview|The documentation says that wxShapeCanvas is derived from wxCanvas.]] In fact, it is derived from [[wxScrolledWindow.]] RobinDunn refers us [[http://cvs.wxwindows.org/viewcvs.cgi/wxWindows/contrib/include/wx/ogl/canvas.h|to the CVS source.]] == Alternatives == [[http://www.wxart2d.org/moin|wxArt2D]] (Nov 2, 2011) [[http://sourceforge.net/projects/wxsf/|wxShapeFramework]] (Nov 2, 2011) [[FloatCanvas]] (Nov 2, 2011) See also [[http://www.lechak.info/pyxel/|Pyxel]], which works with WxPython or PyGame (link updated Jan 7, 2011). ---- KevinAltis has informed me of two sort-of alternatives to wxOGL: 1. JohnAnderson's [[http://viewcvs.osafoundation.org/chandler/trunk/chandler/application/SimpleCanvas.py?rev=5563&view=log|SimpleCanvas.py]]. (used to be part of Chandler) 1. Chris Barker's FloatCanvas (now included with wxPython) ---- You might to check out [[http://piddle.sourceforge.net/|Piddle]], too. It's like an [[http://www.anygui.org/|anygui]] for canvases. Download Piddle and then run [[http://darwin.cwru.edu/~pjacobs/piddleWxDcDemo.py|piddleWxDcDemo.py]]. Piddle doesn't have persistent drawing objects like simplecanvas or floatcanvas or connected objects like wxOGL, but if you made something that integrated something like simplecanvas or floatcanvas with Piddle it would make it easier to draw diagrams to PDFs or PIL images or other contexts besides the application window. SimpleCanvas seems better designed to build a wxOGL alternative. It separates model-view-controller and has built in support for drag and drop and scrolling, although it is complex. You subclass `wxSimpleDrawableObject` for objects in the canvas (like `draw_object` in FloatCanvas) and override the Draw method and !OnMouseEvent. The only example I can see of this though is [[http://cvs.osafoundation.org/index.cgi/*checkout*/osaf/chandler/Chandler/parcels/OSAF/calendar/ColumnarItem.py?rev=1.5&content-type=text/plain|ColumnarItem.py]], which draws a rounded rectangle to show an event in the Chandler [[http://cvs.osafoundation.org/index.cgi/*checkout*/osaf/chandler/Chandler/parcels/OSAF/calendar/|calendar parcel]]. Other example code that is informative is the [[http://cvs.osafoundation.org/index.cgi/*checkout*/osaf/chandler/wxpython/wxPython/demo/wxGrid_MegaExample.py?rev=1.1.1.1&content-type=text/plain|wxGrid_MegaExample.py]] included in the wxPython demo. It shows how you can create custom renderers for cells in a grid (!MegaImageRenderer and !MegaFontRenderer). And one other example helpful to me is the source for the throbber control seen in the wxPython demo (/wxPython/lib/[[http://cvs.osafoundation.org/index.cgi/*checkout*/osaf/chandler/wxpython/wxPython/wxPython/lib/throbber.py?rev=1.1.1.2&content-type=text/plain|throbber.py]]). Because I'd like to have some components in an OGL-type canvas be animated. But here's basic skeleton code that is similar to what FloatCanvas and SimpleCanvas do. Don't use this code though, use something like SimpleCanvas or FloatCanvas. This is just to help make them more understandable. {{{ #!python class DrawableObject: def __init__(self, parentcanvas, pos, size): self.canvas = parentcanvas self.x = pos[0] self.y = pos[1] self.width = size[0] self.height = size[1] self.bounds = wxRect (self.x,self.y,self.width,self.height) #self.forecolor, bgcolor, border, font def Draw(self,dc): pass def OnMouseDown(self,x,y): return false class Rectangle(DrawableObject): def __init__(self, parentcanvas,pos, size): DrawableObject.__init__(self,parentcanvas,pos,size) def Draw(self, dc): #maybe try Piddle here instead of using dc #draw a rectangle, something like: dc.DrawRectangle(self.x,self.y,self.width,self.height) def OnMouseDown(self, x,y): #handle mouse event return true #if handled class ObjectCanvas(wxWindow): def __init__(self): wxWindow.__init__( self, parent, id, wxDefaultPosition, size) self.DrawableObjectList = [] EVT_PAINT (self, self.OnPaint) EVT_MOUSE_EVENTS (self, self.OnMouseEvent) def OnPaint (self, event): paintDC = wxPaintDC (self) self.PrepareDC (paintDC) self.Draw(dc) #actually they use DoubleBufferedDrawing def Draw(self, dc): for Object in self.DrawableObjectList: Object.Draw(dc) #here you could use Piddle instead of using wxPython's paintDC,memoryDC def OnMouseEvent (self, event): #you'd handle drag and drop here too x, y = event.GetX(),event.GetY() if event.LeftDown(): #or just pass any mouse event directly to the drawableobject like simplecanvas for obj in self.DrawableObjectList: if obj.bounds.Inside ((x, y)): if obj.OnMouseDown(x - obj.bounds.GetX(), y - obj.bounds.GetY()): return true def AddObject(self, drawable): self.DrawableObjectList.insert(0,drawable) def RemoveObject(self, drawable): self.DrawableObjectList.remove(drawable) }}} We just need something like a DraggableToolBar class too, to make it easier for users to drag new objects into the OGL-like canvas. == Other Notes == There is no relationship between wxOGL and [[wxArt2d]]. (http://wxart2d.sourceforge.net/) [[WineRackDesigner]] Using wx.OGL to design a wine rack layout, or alternatively FloatCanvas == Comments == I had to change the code from LionKimbro slightly to get this to work with wxPython 2.5 for Python 2.3. Additionally, I had to modify the wx.lib.ogl.diagram.ShowAll() function to pass the second parameter to Show(). My new version looks like this: {{{ def ShowAll(self, show): """Call Show for each shape in the diagram.""" for shape in self._shapeList: shape.Show(show) }}} instead of this: {{{ def ShowAll(self, show): """Call Show for each shape in the diagram.""" for shape in self._shapeList: shape.Show() }}} Here's the example from LionKimbro, modified and working on my computer™ {{{ #!python from wxPython.wx import * from wx.lib.ogl import * class AppFrame(wxFrame): def __init__(self): wxFrame.__init__( self, None, -1, "Demo", size=(300,200), style=wxDEFAULT_FRAME_STYLE ) sizer = wxBoxSizer( wxVERTICAL ) # put stuff into sizer canvas = ShapeCanvas( self ) sizer.Add( canvas, 1, wxGROW ) canvas.SetBackgroundColour( "LIGHT BLUE" ) # diagram = Diagram() canvas.SetDiagram( diagram ) diagram.SetCanvas( canvas ) shape = CircleShape( 20.0 ) # shape.SetX( 25.0 ) # shape.SetY( 25.0 ) # canvas.AddShape( shape ) # diagram.ShowAll( 1 ) # # apply sizer self.SetSizer(sizer) self.SetAutoLayout(1) self.Show(1) app = wxPySimpleApp() OGLInitialize() frame = AppFrame() app.MainLoop() }}}