#!/usr/bin/env python """ PolyEditor: a simple app for editing polygons Used as a demo for FloatCanvas """ import numpy as np import random import numpy.random as RandomArray from wx.lib.floatcanvas import NavCanvas, FloatCanvas # import a local copy: #import sys #sys.path.append("..") #from floatcanvas import NavCanvas, FloatCanvas import wx class DrawFrame(wx.Frame): """ A frame used for the FloatCanvas Demo """ def __init__(self,parent, id,title,position,size): wx.Frame.__init__(self,parent, id,title,position, size) ## Set up the MenuBar MenuBar = wx.MenuBar() FileMenu = wx.Menu() exit = FileMenu.Append(wx.ID_EXIT, "", "Close Application") self.Bind(wx.EVT_MENU, self.OnQuit, exit) MenuBar.Append(FileMenu, "&File") view_menu = wx.Menu() zfit = view_menu.Append(wx.ID_ANY, "Zoom to &Fit", "Zoom to fit the window") self.Bind(wx.EVT_MENU, self.ZoomToFit, zfit) MenuBar.Append(view_menu, "&View") help_menu = wx.Menu() about = help_menu.Append(wx.ID_ABOUT, "", "More information About this program") self.Bind(wx.EVT_MENU, self.OnAbout, about) MenuBar.Append(help_menu, "&Help") self.SetMenuBar(MenuBar) self.CreateStatusBar() # Add the Canvas self.Canvas = NavCanvas.NavCanvas(self,-1,(500,500), Debug = 0, BackgroundColor = "DARK SLATE BLUE" ).Canvas self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove) self.Canvas.Bind(FloatCanvas.EVT_LEFT_UP, self.OnLeftUp) self.Canvas.Bind(FloatCanvas.EVT_LEFT_DOWN, self.OnLeftClick) self.ResetSelections() return None def ResetSelections(self): self.SelectedPoly = None self.SelectedPolyOrig = None self.SelectedPoints = None self.PointSelected = False self.SelectedPointNeighbors = None def OnAbout(self, event): dlg = wx.MessageDialog(self, "This is a small program to demonstrate\n" "the use of the FloatCanvas\n", "About Me", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def ZoomToFit(self,event): self.Canvas.ZoomToBB() def Clear(self,event = None): self.Canvas.ClearAll() self.Canvas.SetProjectionFun(None) self.Canvas.Draw() def OnQuit(self,event): self.Close(True) def OnCloseWindow(self, event): self.Destroy() def OnMove(self, event): """ Updates the status bar with the world coordinates And moves a point if there is one selected """ self.SetStatusText("%.2f, %.2f"%tuple(event.Coords)) if self.PointSelected: PolyPoints = self.SelectedPoly.Points Index = self.SelectedPoints.Index dc = wx.ClientDC(self.Canvas) PixelCoords = event.GetPosition() dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH)) dc.SetLogicalFunction(wx.XOR) if self.SelectedPointNeighbors is None: self.SelectedPointNeighbors = np.zeros((3,2), np.float64) #fixme: This feels very inelegant! if Index == 0: self.SelectedPointNeighbors[0] = self.SelectedPoly.Points[-1] self.SelectedPointNeighbors[1:3] = self.SelectedPoly.Points[:2] elif Index == len(self.SelectedPoly.Points)-1: self.SelectedPointNeighbors[0:2] = self.SelectedPoly.Points[-2:] self.SelectedPointNeighbors[2] = self.SelectedPoly.Points[0] else: self.SelectedPointNeighbors = self.SelectedPoly.Points[Index-1:Index+2] self.SelectedPointNeighbors = self.Canvas.WorldToPixel(self.SelectedPointNeighbors) else: dc.DrawLines(self.SelectedPointNeighbors) self.SelectedPointNeighbors[1] = PixelCoords dc.DrawLines(self.SelectedPointNeighbors) def OnLeftUp(self, event): ## if a point was selected, it's not anymore if self.PointSelected: self.SelectedPoly.Points[self.SelectedPoints.Index] = event.GetCoords() self.SelectedPoly.SetPoints(self.SelectedPoly.Points, copy = False) self.SelectedPoints.SetPoints(self.SelectedPoly.Points, copy = False) self.PointSelected = False self.SelectedPointNeighbors = None self.Canvas.Draw() def OnLeftClick(self,event): ## If a click happens outside the polygon, it's no longer selected self.DeSelectPoly() self.Canvas.Draw() def Setup(self, event = None): "Setting up with some random polygons" wx.GetApp().Yield() self.ResetSelections() self.Canvas.ClearAll() Range = (-10,10) # Create a couple of random Polygons for i, color in enumerate(("Light Blue", "Green", "Purple","Yellow")): points = RandomArray.uniform(Range[0],Range[1],(6,2)) Poly = self.Canvas.AddPolygon(points, LineWidth = 2, LineColor = "Black", FillColor = color, FillStyle = 'Solid') Poly.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.SelectPoly) self.Canvas.ZoomToBB() def SelectPoly(self, Object): Canvas = self.Canvas if Object is self.SelectedPolyOrig: pass else: if self.SelectedPoly: self.DeSelectPoly() self.SelectedPolyOrig = Object self.SelectedPoly = Canvas.AddPolygon(Object.Points, LineWidth = 2, LineColor = "Red", FillColor = "Red", FillStyle = "CrossHatch", InForeground = True) # Draw points on the Vertices of the Selected Poly: self.SelectedPoints = Canvas.AddPointSet(Object.Points, Diameter = 6, Color = "Red", InForeground = True) self.SelectedPoints.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.SelectPointHit) Canvas.Draw() def DeSelectPoly(self): Canvas = self.Canvas if self.SelectedPolyOrig is not None: self.SelectedPolyOrig.SetPoints(self.SelectedPoly.Points, copy = False) self.Canvas.Draw(Force = True) Canvas.RemoveObject(self.SelectedPoly) Canvas.RemoveObject(self.SelectedPoints) self.ResetSelections() def SelectPointHit(self, PointSet): PointSet.Index = PointSet.FindClosestPoint(PointSet.HitCoords) print("point #%i hit"%PointSet.Index) #Index = PointSet.Index self.PointSelected = True class PolyEditor(wx.App): """ A simple example of making editable shapes with FloatCanvas """ def OnInit(self): frame = DrawFrame(None, -1, "FloatCanvas Demo App", wx.DefaultPosition, (700,700), ) self.SetTopWindow(frame) frame.Show() frame.Setup() return True PolyEditor().MainLoop()