diff --git a/demo/FloatCanvas.py b/demo/FloatCanvas.py index 48d971cf3a..75168d7b57 100644 --- a/demo/FloatCanvas.py +++ b/demo/FloatCanvas.py @@ -9,21 +9,28 @@ # numpy isn't there haveNumpy = False errorText = ( - "The FloatCanvas requires the numpy module, version 1.* \n\n" - "You can get info about it at:\n" - "http://numpy.scipy.org/\n\n" + "The FloatCanvas requires the numpy module, version > 1.24 \n\n" + "You can get info about it from conda or pip" ) #--------------------------------------------------------------------------- +# # uncomment and adjust to use a local copy +# import sys +# sys.path.append("./") +# from floatcanvas import NavCanvas, FloatCanvas, Resources +# print("FloatCanvas at:", FloatCanvas.__file__) + +from wx.lib.floatcanvas import NavCanvas, FloatCanvas, Resources +# print("FloatCanvas at:", FloatCanvas.__file__) + +import wx.lib.colourdb +import time +import random + + def BuildDrawFrame(): # this gets called when needed, rather than on import - try: - from floatcanvas import NavCanvas, FloatCanvas, Resources - except ImportError: # if it's not there locally, try the wxPython lib. - from wx.lib.floatcanvas import NavCanvas, FloatCanvas, Resources - import wx.lib.colourdb - import time, random class DrawFrame(wx.Frame): @@ -32,11 +39,10 @@ class DrawFrame(wx.Frame): """ + def __init__(self, parent, id, title, position, size): + wx.Frame.__init__(self, parent, id, title, position, size) - def __init__(self,parent, id,title,position,size): - wx.Frame.__init__(self,parent, id,title,position, size) - - ## Set up the MenuBar + # Set up the MenuBar MenuBar = wx.MenuBar() file_menu = wx.Menu() @@ -100,7 +106,7 @@ def __init__(self,parent, id,title,position,size): MenuBar.Append(draw_menu, "&Tests") view_menu = wx.Menu() - item = view_menu.Append(-1, "Zoom to &Fit","Zoom to fit the window") + item = view_menu.Append(-1, "Zoom to &Fit", "Zoom to fit the window") self.Bind(wx.EVT_MENU, self.ZoomToFit, item) MenuBar.Append(view_menu, "&View") @@ -118,7 +124,7 @@ def __init__(self,parent, id,title,position,size): # Add the Canvas NC = NavCanvas.NavCanvas(self, Debug = 0, - BackgroundColor = "DARK SLATE BLUE") + BackgroundColor = "MEDIUM SLATE BLUE") self.Canvas = NC.Canvas # reference the contained FloatCanvas @@ -1845,9 +1851,12 @@ def __init__(self, *args, **kwargs): wx.App.__init__(self, *args, **kwargs) def OnInit(self): - wx.InitAllImageHandlers() + # wx.InitAllImageHandlers() DrawFrame = BuildDrawFrame() - frame = DrawFrame(None, -1, "FloatCanvas Demo App",wx.DefaultPosition,(700,700)) + frame = DrawFrame(None, -1, + "FloatCanvas Demo App", + wx.DefaultPosition, + (700,700)) self.SetTopWindow(frame) frame.Show() diff --git a/samples/floatcanvas/BB_HitTest.py b/samples/floatcanvas/BB_HitTest.py index a78d292f00..06503953bf 100644 --- a/samples/floatcanvas/BB_HitTest.py +++ b/samples/floatcanvas/BB_HitTest.py @@ -1,8 +1,10 @@ #!/usr/bin/env python """ -Test of an alternative hit test methoid that used the bounding boxes of the objects instead. +Test of an alternative hit test method that used the bounding boxes of the objects instead. -Poorly tested! +Poorly tested -- and broken in recent versions + +NOTE: probably the issue is different event IDs or something. Edited from code contributed by Benjamin Jessup on the mailing list diff --git a/samples/floatcanvas/BNAEditor.py b/samples/floatcanvas/BNAEditor.py index fdc73a1813..ffa2c618a1 100644 --- a/samples/floatcanvas/BNAEditor.py +++ b/samples/floatcanvas/BNAEditor.py @@ -7,7 +7,6 @@ """ import os, sys -import sets import numpy as N @@ -64,20 +63,20 @@ def Load(self, filename): self.Names = [] self.Types = [] - with open(filename,'rU') as file_: + with open(filename,'r') as file_: for line in file_: if not line: break line = line.strip() Name, line = line.split('","') Name = Name[1:] - Type,line = line.split('",') + Type, line = line.split('",') num_points = int(line) self.Types.append(Type) self.Names.append(Name) - polygon = N.zeros((num_points,2),N.float) + polygon = N.zeros((num_points,2),N.float64) for i in range(num_points): - polygon[i,:] = map(float, file_.readline().split(',')) + polygon[i,:] = [float(p) for p in file_.readline().split(',')] self.PointsData.append(polygon) return None @@ -127,11 +126,11 @@ def __init__(self,parent, id,title,position,size): BackgroundColor = "DARK SLATE BLUE" ).Canvas - wx.EVT_CLOSE(self, self.OnCloseWindow) + self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) - FloatCanvas.EVT_MOTION(self.Canvas, self.OnMove ) - FloatCanvas.EVT_LEFT_UP(self.Canvas, self.OnLeftUp ) - FloatCanvas.EVT_LEFT_DOWN(self.Canvas, self.OnLeftDown) + 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.OnLeftDown) try: self.FileDialog = wx.FileDialog(self, "Pick a BNA file",".","","*", wx.FD_OPEN) @@ -208,7 +207,7 @@ def OnMove(self, event): dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH)) dc.SetLogicalFunction(wx.XOR) if self.SelectedPointNeighbors is None: - self.SelectedPointNeighbors = N.zeros((3,2), N.float) + self.SelectedPointNeighbors = N.zeros((3,2), N.float64) #fixme: This feels very inelegant! if Index == 0: self.SelectedPointNeighbors[0] = self.SelectedPoly.Points[-1] @@ -278,6 +277,16 @@ def LoadBNA(self, filename): try: AllPolys = [] self.BNAFile = BNAData(filename) + except Exception as err: + raise + dlg = wx.MessageDialog(None, + 'There was something wrong with the selected bna file', + 'File Loading Error:\n' + f'{err}', + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + else: print("loaded BNAFile:", self.BNAFile.Filename) for i, shoreline in enumerate(self.BNAFile.PointsData): Poly = self.Canvas.AddPolygon(shoreline, @@ -289,16 +298,8 @@ def LoadBNA(self, filename): Poly.BNAIndex = i AllPolys.append(Poly) self.Canvas.ZoomToBB() - self.ChangedPolys = sets.Set() + self.ChangedPolys = set() self.AllPolys = AllPolys - except: - #raise - dlg = wx.MessageDialog(None, - 'There was something wrong with the selected bna file', - 'File Loading Error', - wx.OK | wx.ICON_ERROR) - dlg.ShowModal() - dlg.Destroy() class BNAEditor(wx.App): diff --git a/samples/floatcanvas/BarPlot.py b/samples/floatcanvas/BarPlot.py index 2c96448aa1..7c5f9fd544 100644 --- a/samples/floatcanvas/BarPlot.py +++ b/samples/floatcanvas/BarPlot.py @@ -27,7 +27,7 @@ def YScaleFun(center): """ # center gets ignored in this case - return N.array((1, float(NumChannels)/MaxValue), N.float) + return N.array((1, float(NumChannels) / MaxValue), N.float64) def ScaleWorldToPixel(self, Lengths): """ @@ -41,7 +41,7 @@ def ScaleWorldToPixel(self, Lengths): Lengths should be a NX2 array of (x,y) coordinates, or a 2-tuple, or sequence of 2-tuples. """ - return N.ceil(( (N.asarray(Lengths, N.float)*self.TransformVector) )).astype('i') + return N.ceil(( (N.asarray(Lengths, N.float64)*self.TransformVector) )).astype('i') class DrawFrame(wx.Frame): diff --git a/samples/floatcanvas/DrawRect.py b/samples/floatcanvas/DrawRect.py index 8431f8009f..d37cc2e6b9 100644 --- a/samples/floatcanvas/DrawRect.py +++ b/samples/floatcanvas/DrawRect.py @@ -1,44 +1,30 @@ #!/usr/bin/env python """ -A simple demo that shows how to use FloatCanvas to draw rectangles on the screen - -Note: this is now broken -- the events are not getting to the Rubber Band Box object. - It should be re-factored to use GUIMode +A simple demo that shows how to use FloatCanvas to draw rectangles +on a Canvas. """ - - import wx -## import a local version -#import sys -#sys.path.append("..") -#from floatcanvas import NavCanvas, FloatCanvas, Resources, Utilities, GUIMode -#from floatcanvas.Utilities import GUI - -## import the installed version from wx.lib.floatcanvas import NavCanvas, FloatCanvas, GUIMode from wx.lib.floatcanvas.Utilities import GUI -import numpy as N class DrawFrame(wx.Frame): - """ - A frame used for the FloatCanvas Demo - + A frame used for the Demo """ - def __init__(self,parent, id,title,position,size): - wx.Frame.__init__(self,parent, id,title,position, size) + def __init__(self, parent, id, title, position, size): + wx.Frame.__init__(self, parent, id, title, position, size) self.CreateStatusBar() # Add the Canvas NC = NavCanvas.NavCanvas(self, - size= (500,500), - ProjectionFun = None, - Debug = 0, - BackgroundColor = "DARK SLATE BLUE", + size=(500, 500), + ProjectionFun=None, + Debug=0, + BackgroundColor="DARK SLATE BLUE", ) self.Canvas = NC.Canvas @@ -96,11 +82,13 @@ def OnMove(self, event): Updates the status bar with the world coordinates """ - self.SetStatusText("%.4f, %.4f"%tuple(event.Coords)) + self.SetStatusText(f"{event.Coords[0]:.2f}, {event.Coords[1]:.2f}") event.Skip() + app = wx.App() -DrawFrame(None, -1, "FloatCanvas Rectangle Drawer", wx.DefaultPosition, (700,700) ) +DrawFrame(None, wx.ID_ANY, "FloatCanvas Rectangle Drawer", + wx.DefaultPosition, (700,700) ) app.MainLoop() diff --git a/samples/floatcanvas/DrawShapes.py b/samples/floatcanvas/DrawShapes.py new file mode 100644 index 0000000000..7eecad3675 --- /dev/null +++ b/samples/floatcanvas/DrawShapes.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python + +""" +A simple demo that shows how to use FloatCanvas to draw shapes +on a Canvas with RubberBandShapes. This also demonstrates the +use of GUIModes to customize drawing behavior. +""" +import wx + +from wx.lib.floatcanvas import NavCanvas, FloatCanvas, GUIMode, Resources +from wx.lib.floatcanvas.Utilities import GUI + + +class DrawFrame(wx.Frame): + """ + A frame used for the Demo + """ + + def __init__(self, parent, id, title, position, size): + + try: + imgFilename = 'data/TestMap.png' + self.bgImage = wx.Image(imgFilename) +# imgSize = self.bgImage.GetSize() +# if self.bgImage.IsOk(): +# size=(imgSize[0] / 2, imgSize[1] / 2) + except: + + print("Problem loading {0}".format(imgFilename)) + + import sys + err = sys.exc_info() + print(err[0]) + print(err[1]) + + + wx.Frame.__init__(self, parent, id, title, position, size) + + self.CreateStatusBar() + # Add the Canvas + NC = NavCanvas.NavCanvas(self, + size=(500, 500), + ProjectionFun=None, + Debug=0, + BackgroundColor="DARK SLATE BLUE", + ) + + # We need to hijack the NavCanvas GUIMouse Mode Button + NC.Modes + + self.Canvas = NC.Canvas + + self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove) + + # Add some buttons to the Toolbar + tb = NC.ToolBar + tb.AddSeparator() + + ClearButton = wx.Button(tb, wx.ID_ANY, "Clear") + tb.AddControl(ClearButton) + ClearButton.Bind(wx.EVT_BUTTON, self.Clear) + + DrawButton = wx.Button(tb, wx.ID_ANY, "StopDrawing") + tb.AddControl(DrawButton) + DrawButton.Bind(wx.EVT_BUTTON, self.SetDraw) + self.DrawButton = DrawButton + + self.Shape = wx.Choice(tb, wx.ID_ANY, choices=("Rectangle", "Ellipse", "Line")) + self.Shape.SetStringSelection("Rectangle") + tb.AddControl(self.Shape) + self.Shape.Bind(wx.EVT_CHOICE, self.SetShape) + + self.Color = wx.Choice(tb, wx.ID_ANY, choices=('Black', 'Red', 'Green', 'Blue', 'White')) + self.Color.SetStringSelection('Black') + tb.AddControl(self.Color) + + self.Width = wx.Choice(tb, wx.ID_ANY, choices=('1', '2', '3', '4', '5', '6')) + self.Width.SetStringSelection('4') + tb.AddControl(self.Width) + + tb.Realize() + + # Initialize a few values + self.Clear() + + self.RBShapeMode = RubberBandShape(self.Shape.GetStringSelection(), self.NewShape) + self.Canvas.SetMode(self.RBShapeMode) + + # The "Pointer" button from the NavCanvas loads a GUI.GUIMouse GUIMode object. We need it to load our + # RBShapeMode GUIMode object instead. Here, we replace that button's GUIMode. (See NavCanvas code.) + keys = list(NC.ModesDict.keys()) + NC.ModesDict[keys[0]] = self.RBShapeMode + + self.Canvas.Draw() + + self.Show(True) + self.Canvas.ZoomToBB() + return None + + def Clear(self, event=None): + self.Shapes = [] + oldScale = self.Canvas.Scale + oldViewPortCenter = self.Canvas.ViewPortCenter + self.Canvas.ClearAll() + self.Canvas.AddScaledBitmap(self.bgImage, (0 - (float(self.bgImage.GetWidth()) / 2.0), (float(self.bgImage.GetHeight()) / 2.0)), Height = self.bgImage.GetSize()[1], Position = "tl") + self.Canvas.Scale = oldScale + self.Canvas.SetToNewScale() + self.Canvas.ViewPortCenter = oldViewPortCenter + self.Canvas.Draw() + # This is necessary to get the Scale and ViewPortCenter settings displayed correctly + self.Canvas.SendSizeEvent() + + def SetDraw(self, event=None): + label = self.DrawButton.GetLabel() + if label == "Draw": + self.DrawButton.SetLabel("StopDrawing") + self.Canvas.SetMode(self.RBShapeMode) + elif label == "StopDrawing": + self.DrawButton.SetLabel("Draw") + self.Canvas.SetMode(GUIMode.GUIMouse()) + else: # huh? + pass + + def SetShape(self, event=None): + # When the Shape changes, we need to let the RubberBandShape know. + self.RBShapeMode.SetShape(self.Shape.GetStringSelection()) + + def NewShape(self, rect): + +# print('NewShape():', self.Shape.GetStringSelection(), rect) +# print() + + color = self.Color.GetStringSelection() + width = int(self.Width.GetStringSelection()) + if self.Shape.GetStringSelection() == "Rectangle": + shape = self.Canvas.AddRectangle(*rect, LineColor=color, LineWidth=width) + elif self.Shape.GetStringSelection() == "Ellipse": + shape = self.Canvas.AddEllipse(*rect, LineColor=color, LineWidth=width) + elif self.Shape.GetStringSelection() == "Line": + # Data passed back for Lines by RubberBandShapes is in the form of + # ((x1, y1), (w, h)) so it's the same as Rectangles and Ellipses. + # See the RubberBandShapes object below for more. + # This code converts ((x1, y1), (w, h)) to (x1, y1), (x2, y2)) + # needed for AddLine(). + ((x1, y1), (w, h)) = rect + x2 = w + x1 + y2 = h + y1 + points = [(x1, y1), (x2, y2)] + shape = self.Canvas.AddLine(points, LineColor=color, LineWidth=width) + + self.Shapes.append(shape) + self.Canvas.Draw(True) + + def OnMove(self, event): + """ + Updates the status bar with the world coordinates + + """ + self.SetStatusText(f"{event.Coords[0]:.2f}, {event.Coords[1]:.2f}") + event.Skip() + +class RubberBandShape(GUI.RubberBandBox): + """ + Class to provide a GUI Mode that makes a rubber band shape + that can be drawn on a Window + """ + + def __init__(self, Shape, CallBack, Tol=5, style='dashed'): + """ + Create a Rubber Band Box + :param `Shape`: The Shape to be drawn in RubberBand style + :param `CallBack`: is the method you want called when the mouse is + released. That method will be called, passing in a rect + parameter, where rect is: (Point, WH) of the rect in + world coords. + :param `Tol`: The tolerance for the smallest rectangle allowed. defaults + to 5. In pixels + :param style: style of RB box: 'dashed': a dashed outline + 'grey' a black outline with grey fill + """ + if not Shape in ('Rectangle', 'Ellipse', 'Line'): + raise Exception('Illegal Shape for RubberBandShape') + GUI.RubberBandBox.__init__(self, CallBack, Tol, style) + self.Shape = Shape + self.EndPoint = None + + def SetShape(self, Shape): + if not Shape in ('Rectangle', 'Ellipse', 'Line'): + raise Exception('Illegal Shape for RubberBandShape') + self.Shape = Shape + + def OnMove(self, event): + """ Over-rides RubberBandBox's OnMove() to allow different shapes """ + mac = 'wxMac' in wx.PlatformInfo + if self.Drawing: + x, y = self.StartPoint + Cornerx, Cornery = event.GetPosition() + # Rectangles and Ellipses need to compensate for StartPoint + if self.Shape in ('Rectangle', 'Ellipse'): + w, h = (Cornerx - x, Cornery - y) + # Lines do not compensate for StartPoint. If you do, the + # RubberBandShape Line position is wrong. + else: + w, h = ( Cornerx, Cornery ) + if abs(w) > self.Tol and abs(h) > self.Tol: + # Draw the rubber-band rectangle using an overlay so it + # will manage keeping the rectangle and the former window + # contents separate. + dc = wx.ClientDC(self.Canvas) + if mac: + odc = wx.DCOverlay(self.overlay, dc) + odc.Clear() + if self.style == 'dashed': + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(wx.Pen(wx.Colour(200, 200, 200), 3)) + else: + dc.SetBrush(wx.Brush(wx.Colour(192, 192, 192, 128))) + dc.SetPen(wx.Pen("black", 1)) + if not mac: + dc.SetLogicalFunction(wx.XOR) + if self.RBRect: + if self.Shape == "Rectangle": + dc.DrawRectangle(*self.RBRect) + elif self.Shape == "Ellipse": + dc.DrawEllipse(*self.RBRect) + elif self.Shape == "Line": + dc.DrawLine(*self.RBRect) + self.RBRect = ((x, y), (w, h) ) + if self.Shape == "Rectangle": + dc.DrawRectangle(*self.RBRect) + elif self.Shape == "Ellipse": + dc.DrawEllipse(*self.RBRect) + elif self.Shape == "Line": + dc.DrawLine(*self.RBRect) + if mac: + if self.style == 'dashed': + dc.SetPen(wx.Pen("black", 3, style=wx.PENSTYLE_SHORT_DASH)) + if self.Shape == "Rectangle": + dc.DrawRectangle(*self.RBRect) + elif self.Shape == "Ellipse": + dc.DrawEllipse(*self.RBRect) + elif self.Shape == "Line": + dc.DrawLine(*self.RBRect) + del odc # work around a bug in the Python wrappers to make + # sure the odc is destroyed before the ClientDC is. + self.Canvas._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) + + def OnLeftUp(self, event): + """ Over-rides RubberBandBox's OnLeftUp() so that data for Lines is + correctly provided. """ + self.EndPoint = event.GetPosition() + # Stop Drawing + if self.Drawing: + self.Drawing = False + dc = wx.ClientDC(self.Canvas) + odc = wx.DCOverlay(self.overlay, dc) + odc.Clear() + if self.RBRect: + # Rectangles and Ellipses have already compensated for StartPoint + if self.Shape in ('Rectangle', 'Ellipse'): + world_rect = (self.Canvas.PixelToWorld(self.RBRect[0]), + self.Canvas.ScalePixelToWorld(self.RBRect[1]) + ) + # Lines compensate for StartPoint here for (w, h). If you do not, the + # finalized Line position is wrong. + elif self.Shape in ('Line',): + world_rect = (self.Canvas.PixelToWorld(self.RBRect[0]), + self.Canvas.ScalePixelToWorld(self.EndPoint - self.StartPoint) + ) + wx.CallAfter(self.CallBack, world_rect) + self.RBRect = None + wx.CallAfter(self._delete_overlay) + + def _delete_overlay(self): + """ + This is a total Kludge, but I was getting crashes (on a Mac) if it was kept around + + Putting it in __del__ didn't work :-( + + Clearing it directly in OnLeftUp crashed also - I guess rending wasn't completely done? + + I *think* this is due to a bug in wxPython -- it should delete an Overlay on it's own, + but what can you do? + """ + self.overlay = None + + + +app = wx.App() +DrawFrame(None, wx.ID_ANY, "FloatCanvas Shape Drawer", + wx.DefaultPosition, (700,700) ) +app.MainLoop() + + + + + + + + + + + + + diff --git a/samples/floatcanvas/MouseTest.py b/samples/floatcanvas/MouseTest.py index 602cd1627e..a4c47dbd1c 100644 --- a/samples/floatcanvas/MouseTest.py +++ b/samples/floatcanvas/MouseTest.py @@ -3,27 +3,20 @@ """ Small demo of catching Mouse events using just FloatCanvas, rather than NavCanvas - """ import wx +from wx.lib.floatcanvas import FloatCanvas, GUIMode app = wx.App(0) -try: - # See if there is a local copy - import sys - sys.path.append("../") - from floatcanvas import NavCanvas, FloatCanvas, GUIMode -except ImportError: - from wx.lib.floatcanvas import NavCanvas, FloatCanvas, GUIMode class TestFrame(wx.Frame): def __init__(self, *args, **kwargs): wx.Frame.__init__(self, *args, **kwargs) - self.canvas =FloatCanvas.FloatCanvas(self, BackgroundColor = "DARK SLATE BLUE") + self.canvas =FloatCanvas.FloatCanvas(self, BackgroundColor="DARK SLATE BLUE") # Layout MainSizer = wx.BoxSizer(wx.VERTICAL) @@ -32,7 +25,7 @@ def __init__(self, *args, **kwargs): self.canvas.Bind(FloatCanvas.EVT_LEFT_DOWN, self.OnLeftDown) - self.canvas.AddRectangle((10,10), (100, 20), FillColor="red") + self.canvas.AddRectangle((10, 10), (100, 20), FillColor="red") self.canvas.SetMode(GUIMode.GUIMouse(self.canvas)) diff --git a/samples/floatcanvas/MovingElements.py b/samples/floatcanvas/MovingElements.py index 40db285852..eb654b764c 100644 --- a/samples/floatcanvas/MovingElements.py +++ b/samples/floatcanvas/MovingElements.py @@ -160,7 +160,7 @@ def CompPoints(self, XY, L): Points = N.array(((0, c), ( L/2.0, -c/2.0), (-L/2.0, -c/2.0)), - N.float_) + N.float64) Points += XY return Points @@ -192,7 +192,7 @@ def __init__(self, *args, **kwargs): Points = N.array(((0,0), (1,0), (0.5, 1)), - N.float) + N.float64) data = (( (0,0), 1), ( (3,3), 2), diff --git a/samples/floatcanvas/MovingTriangle.py b/samples/floatcanvas/MovingTriangle.py index f3277f212a..8226624670 100644 --- a/samples/floatcanvas/MovingTriangle.py +++ b/samples/floatcanvas/MovingTriangle.py @@ -64,7 +64,7 @@ def CompPoints(self, XY, L): Points = N.array(((0, c), ( L/2.0, -c/2.0), (-L/2.0, -c/2.0)), - N.float_) + N.float64) Points += XY return Points @@ -104,7 +104,7 @@ def __init__(self,parent, id,title,position,size): Points = N.array(((0,0), (1,0), (0.5, 1)), - N.float_) + N.float64) data = (( (0,0), 1), ( (3,3), 2), diff --git a/samples/floatcanvas/PieChart.py b/samples/floatcanvas/PieChart.py index 82379ac2ce..147b9f81b1 100644 --- a/samples/floatcanvas/PieChart.py +++ b/samples/floatcanvas/PieChart.py @@ -1,6 +1,5 @@ #!/usr/bin/env python - import wx ## import the installed version @@ -13,11 +12,10 @@ #from floatcanvas import NavCanvas, FloatCanvas #from floatcanvas.SpecialObjects import PieChart - import numpy as N -class DrawFrame(wx.Frame): +class DrawFrame(wx.Frame): """ A frame used for the FloatCanvas Demo @@ -29,15 +27,17 @@ def __init__(self, *args, **kwargs): self.CreateStatusBar() # Add the Canvas - Canvas = NavCanvas.NavCanvas(self,-1, - size = (500,500), - Debug = 0, - BackgroundColor = "DARK SLATE BLUE", - ).Canvas + Canvas = NavCanvas.NavCanvas( + self, + -1, + size=(500, 500), + Debug=0, + BackgroundColor="DARK SLATE BLUE", + ).Canvas self.Canvas = Canvas - Values = (10,10,10) + Values = (10, 10, 10) Colors = ('Red', 'Blue', 'Green') Pie1 = PieChart(N.array((0, 0)), 10, Values, Colors, Scaled=False) Canvas.AddObject(Pie1) @@ -54,13 +54,16 @@ def __init__(self, *args, **kwargs): # missng slice! Values = (10, 15, 12, 24) Colors = ('Red', 'Blue', 'Green', None) - Pie4 = PieChart(N.array((0, -15)), 10, Values, Colors, LineColor="Black") + Pie4 = PieChart(N.array((0, -15)), + 10, + Values, + Colors, + LineColor="Black") Canvas.AddObject(Pie4) - # Test the styles Values = (10, 12, 14) - Styles = ("Solid", "CrossDiagHatch","CrossHatch") + Styles = ("Solid", "CrossDiagHatch", "CrossHatch") Colors = ('Red', 'Blue', 'Green') Pie4 = PieChart(N.array((20, -20)), 10, Values, Colors, Styles) Canvas.AddObject(Pie2) @@ -68,7 +71,7 @@ def __init__(self, *args, **kwargs): Pie1.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.Pie1Hit) Pie2.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.Pie2Hit) - self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove ) + self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove) self.Show() Canvas.ZoomToBB() @@ -83,9 +86,9 @@ def OnMove(self, event): """ Updates the status bar with the world coordinates """ - self.SetStatusText("%.2g, %.2g"%tuple(event.Coords)) + self.SetStatusText("%.2g, %.2g" % tuple(event.Coords)) app = wx.App(False) -F = DrawFrame(None, title="FloatCanvas Demo App", size=(700,700) ) +F = DrawFrame(None, title="FloatCanvas Demo App", size=(700, 700)) app.MainLoop() diff --git a/samples/floatcanvas/PolyEditor.py b/samples/floatcanvas/PolyEditor.py index 55b1af429c..270daa835c 100644 --- a/samples/floatcanvas/PolyEditor.py +++ b/samples/floatcanvas/PolyEditor.py @@ -112,7 +112,7 @@ def OnMove(self, event): dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH)) dc.SetLogicalFunction(wx.XOR) if self.SelectedPointNeighbors is None: - self.SelectedPointNeighbors = N.zeros((3,2), N.float_) + self.SelectedPointNeighbors = N.zeros((3,2), N.float64) #fixme: This feels very inelegant! if Index == 0: self.SelectedPointNeighbors[0] = self.SelectedPoly.Points[-1] diff --git a/samples/floatcanvas/ProcessDiagram.py b/samples/floatcanvas/ProcessDiagram.py index cf8609055b..d9896b9b85 100644 --- a/samples/floatcanvas/ProcessDiagram.py +++ b/samples/floatcanvas/ProcessDiagram.py @@ -100,8 +100,8 @@ def __init__(self, TextColor = "Black", InForeground = False, IsVisible = True): - XY = N.asarray(XY, N.float).reshape(2,) - WH = N.asarray(WH, N.float).reshape(2,) + XY = N.asarray(XY, N.float64).reshape(2,) + WH = N.asarray(WH, N.float64).reshape(2,) Label = FC.ScaledText(Label, XY, Size = WH[1] / 2.0, @@ -212,7 +212,7 @@ def CompPoints(self, XY, L): Points = N.array(((0, c), ( L/2.0, -c/2.0), (-L/2.0, -c/2.0)), - N.float_) + N.float64) Points += XY return Points diff --git a/samples/floatcanvas/RubberBandBox.py b/samples/floatcanvas/RubberBandBox.py new file mode 100644 index 0000000000..17b1e94e85 --- /dev/null +++ b/samples/floatcanvas/RubberBandBox.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import wx + +from wx.lib.floatcanvas import NavCanvas, FloatCanvas +from wx.lib.floatcanvas.Utilities.GUI import RubberBandBox + + +class DrawFrame(wx.Frame): + """ + A frame used for the FloatCanvas Demo + """ + + def __init__(self, *args, **kwargs): + wx.Frame.__init__(self, *args, **kwargs) + + self.CreateStatusBar() + + # Add the Canvas + Canvas = NavCanvas.NavCanvas(self,-1, + size = (500,500), + ProjectionFun = None, + Debug = 0, + BackgroundColor = "DARK SLATE BLUE", + ).Canvas + + self.Canvas = Canvas + + self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove ) + + # Just to have something to display + Rect = Canvas.AddRectangle((50, 20), (40,10), FillColor="Red", LineStyle = None) + + self.RBBoxMode = RubberBandBox(self.RBBoxDone, style='grey') + self.Canvas.SetMode(self.RBBoxMode) + + self.Show() + Canvas.ZoomToBB() + + def OnMove(self, event): + """ + Updates the status bar with the world coordinates + """ + self.SetStatusText("%.2f, %.2f" % tuple(event.Coords)) + + def RBBoxDone(self, rect): + print(f"Rubber Band Box Released at: {rect}") + +app = wx.App(False) +F = DrawFrame(None, title="Rubber Band Box Demo", size=(700, 700)) +app.MainLoop() + + + + + + + + + + + + + diff --git a/samples/floatcanvas/ScaleDemo.py b/samples/floatcanvas/ScaleDemo.py index 5e70c7cfdf..7771ee02e2 100644 --- a/samples/floatcanvas/ScaleDemo.py +++ b/samples/floatcanvas/ScaleDemo.py @@ -30,7 +30,7 @@ def YScaleFun(center): """ # center gets ignored in this case - return N.array((5e7, 1), N.float) + return N.array((5e7, 1), N.float64) class DrawFrame(wx.Frame): @@ -49,7 +49,7 @@ def __init__(self, *args, **kwargs): size = (500,500), ProjectionFun = YScaleFun, Debug = 0, - BackgroundColor = "DARK SLATE BLUE", + BackgroundColor = "LIGHT BLUE", ).Canvas self.Canvas = Canvas diff --git a/samples/floatcanvas/TextBox.py b/samples/floatcanvas/TextBox.py index 5badeb1d05..d4f1b0de2e 100644 --- a/samples/floatcanvas/TextBox.py +++ b/samples/floatcanvas/TextBox.py @@ -188,7 +188,7 @@ def __init__(self,parent, id,title,position,size): Family = wx.ROMAN, Alignment = "right" ) - Point = N.array((100, -20), N.float_) + Point = N.array((100, -20), N.float64) Box = Canvas.AddScaledTextBox("Here is even more auto wrapped text. This time the line spacing is set to 0.8. \n\nThe Padding is set to 0.", Point, Size = 3, @@ -202,8 +202,8 @@ def __init__(self,parent, id,title,position,size): ) Canvas.AddPoint(Point, "Red", 2) - Point = N.array((0, -40), N.float_) -# Point = N.array((0, 0), N.float_) + Point = N.array((0, -40), N.float64) +# Point = N.array((0, 0), N.float64) for Position in ["tl", "bl", "tr", "br"]: # for Position in ["br"]: Box = Canvas.AddScaledTextBox("Here is a\nfour liner\nanother line\nPosition=%s"%Position, @@ -221,7 +221,7 @@ def __init__(self,parent, id,title,position,size): ) Canvas.AddPoint(Point, "Red", 4) - Point = N.array((-20, 60), N.float_) + Point = N.array((-20, 60), N.float64) Box = Canvas.AddScaledTextBox("Here is some\ncentered\ntext", Point, Size = 4, @@ -237,7 +237,7 @@ def __init__(self,parent, id,title,position,size): LineSpacing = 0.8 ) - Point = N.array((-20, 20), N.float_) + Point = N.array((-20, 20), N.float64) Box = Canvas.AddScaledTextBox("Here is some\nright aligned\ntext", Point, Size = 4, @@ -252,7 +252,7 @@ def __init__(self,parent, id,title,position,size): LineSpacing = 0.8 ) - Point = N.array((100, -60), N.float_) + Point = N.array((100, -60), N.float64) Box = Canvas.AddScaledTextBox("Here is some auto wrapped text. This time it is centered, rather than right aligned.\n\nThe Padding is set to 2.", Point, Size = 3, diff --git a/samples/floatcanvas/TextBox2.py b/samples/floatcanvas/TextBox2.py index 14ec15eac7..161f61160b 100644 --- a/samples/floatcanvas/TextBox2.py +++ b/samples/floatcanvas/TextBox2.py @@ -9,10 +9,8 @@ this really needs to be re-done with GUI-Modes. """ - import wx - ## import the installed version from wx.lib.floatcanvas import NavCanvas, FloatCanvas, Resources @@ -62,7 +60,7 @@ def __init__(self,parent, id,title,position,size): self.Canvas.Bind(FloatCanvas.EVT_LEFT_UP, self.OnLeftUp ) self.Canvas.Bind(FloatCanvas.EVT_LEFT_DOWN, self.OnLeftDown) - Point = N.array((0,0), N.float) + Point = N.array((0,0), N.float64) diff --git a/samples/floatcanvas/Tree.py b/samples/floatcanvas/Tree.py index 7757651f5c..fb53386841 100644 --- a/samples/floatcanvas/Tree.py +++ b/samples/floatcanvas/Tree.py @@ -100,8 +100,8 @@ def __init__(self, TextColor = "Black", InForeground = False, IsVisible = True): - XY = N.asarray(XY, N.float).reshape(2,) - WH = N.asarray(WH, N.float).reshape(2,) + XY = N.asarray(XY, N.float64).reshape(2,) + WH = N.asarray(WH, N.float64).reshape(2,) Label = FC.ScaledText(Label, XY, Size = WH[1] / 2.0, @@ -204,7 +204,7 @@ def CompPoints(self, XY, L): Points = N.array(((0, c), ( L/2.0, -c/2.0), (-L/2.0, -c/2.0)), - N.float_) + N.float64) Points += XY return Points diff --git a/wx/lib/floatcanvas/FCObjects.py b/wx/lib/floatcanvas/FCObjects.py index bf307bdc96..773f09d372 100644 --- a/wx/lib/floatcanvas/FCObjects.py +++ b/wx/lib/floatcanvas/FCObjects.py @@ -7,11 +7,11 @@ # Created: # Version: # Date: -# Licence: +# License: # Tags: phoenix-port, unittest, documented, py3-port #---------------------------------------------------------------------------- """ -This is where FloatCanvas defines its drawings objects. +This is where FloatCanvas defines its drawing objects. """ import sys @@ -76,8 +76,8 @@ def colormatch(color): pacc.MoveTo(pdata, 0, 0) outcolor = pacc.Get()[:3] else: - outcolor = dc.GetPixel(0,0) - return outcolor == color + outcolor = dc.GetPixel(0, 0) + return outcolor[:indexcount] == color[:indexcount] if indexcount == 0: yield () @@ -243,6 +243,7 @@ def Bind(self, Event, CallBackFun): self._Canvas.MakeHitDict() self._Canvas.HitDict[Event][self.HitColor] = (self) # put the object in the hit dict, indexed by its color + def UnBindAll(self): """ Unbind all events @@ -692,7 +693,7 @@ def __init__(self, self.SetBrush(FillColor,FillStyle) def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel = None, HTdc=None): - Points = WorldToPixel(self.Points)#.tolist() + Points = WorldToPixel(self.Points) dc.SetPen(self.Pen) dc.SetBrush(self.Brush) dc.DrawPolygon(Points) @@ -780,12 +781,12 @@ def __init__(self, XY, Length, Direction, - LineColor = "Black", - LineStyle = "Solid", - LineWidth = 2, - ArrowHeadSize = 8, - ArrowHeadAngle = 30, - InForeground = False): + LineColor="Black", + LineStyle="Solid", + LineWidth=2, + ArrowHeadSize=8, + ArrowHeadAngle=30, + InForeground=False): """ Default class constructor. @@ -806,7 +807,7 @@ def __init__(self, DrawObject.__init__(self, InForeground) self.XY = N.array(XY, float) - self.XY.shape = (2,) # Make sure it is a length 2 vector + self.XY.shape = (2,) # Make sure it is a length 2 vector self.Length = Length self.Direction = float(Direction) self.ArrowHeadSize = ArrowHeadSize @@ -819,10 +820,10 @@ def __init__(self, self.LineStyle = LineStyle self.LineWidth = LineWidth - self.SetPen(LineColor,LineStyle,LineWidth) + self.SetPen(LineColor, LineStyle, LineWidth) - ##fixme: How should the HitTest be drawn? - self.HitLineWidth = max(LineWidth,self.MinHitLineWidth) + # fixme: How should the HitTest be drawn? + self.HitLineWidth = max(LineWidth, self.MinHitLineWidth) def SetDirection(self, Direction): """ @@ -893,7 +894,7 @@ def CalcArrowPoints(self): def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None): dc.SetPen(self.Pen) xy = WorldToPixel(self.XY) - ArrowPoints = xy + self.ArrowPoints + ArrowPoints = N.round((xy + self.ArrowPoints)).astype(N.int32) dc.DrawLines(ArrowPoints) if HTdc and self.HitAble: HTdc.SetPen(self.HitPen) @@ -969,13 +970,14 @@ def CalcArrowPoints(self): self.ArrowPoints[i,:,:] = AP self.ArrowPoints *= S - def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None): + def _Draw(self, dc, WorldToPixel, ScaleWorldToPixel, HTdc=None): Points = WorldToPixel(self.Points) - ArrowPoints = Points[1:,N.newaxis,:] + self.ArrowPoints + ArrowPoints = N.round(Points[1:, N.newaxis, :] + + self.ArrowPoints).astype(N.int32) dc.SetPen(self.Pen) dc.DrawLines(Points) for arrow in ArrowPoints: - dc.DrawLines(arrow) + dc.DrawLines(arrow) if HTdc and self.HitAble: HTdc.SetPen(self.HitPen) HTdc.DrawLines(Points) @@ -1440,15 +1442,36 @@ def LayoutText(self): ## than pixel coords as the y axis is reversed ## pad is the extra space around the text ## if world = 1, the vertical shift is done in y-up coordinates - ShiftFunDict = {'tl': lambda x, y, w, h, world=0, pad=0: (x + pad, y + pad - 2*world*pad), - 'tc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y + pad - 2*world*pad), - 'tr': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y + pad - 2*world*pad), - 'cl': lambda x, y, w, h, world=0, pad=0: (x + pad, y - h/2 + world*h), - 'cc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y - h/2 + world*h), - 'cr': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y - h/2 + world*h), - 'bl': lambda x, y, w, h, world=0, pad=0: (x + pad, y - h + 2*world*h - pad + world*2*pad) , - 'bc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y - h + 2*world*h - pad + world*2*pad) , - 'br': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y - h + 2*world*h - pad + world*2*pad)} + ShiftFunDict = { + 'tl': + lambda x, y, w, h, world=0, pad=0: + (x + pad, y + pad - 2 * world * pad), + 'tc': + lambda x, y, w, h, world=0, pad=0: + (x - w // 2, y + pad - 2 * world * pad), + 'tr': + lambda x, y, w, h, world=0, pad=0: + (x - w - pad, y + pad - 2 * world * pad), + 'cl': + lambda x, y, w, h, world=0, pad=0: + (x + pad, y - h // 2 + world * h), + 'cc': + lambda x, y, w, h, world=0, pad=0: + (x - w // 2, y - h // 2 + world * h), + 'cr': + lambda x, y, w, h, world=0, pad=0: + (x - w - pad, y - h // 2 + world * h), + 'bl': + lambda x, y, w, h, world=0, pad=0: + (x + pad, y - h + 2 * world * h - pad + world * 2 * pad), + 'bc': + lambda x, y, w, h, world=0, pad=0: + (x - w // 2, y - h + 2 * world * h - pad + world * 2 * pad), + 'br': + lambda x, y, w, h, world=0, pad=0: + (x - w - pad, y - h + 2 * world * h - pad + world * 2 * pad) + } + class Text(TextObjectMixin, DrawObject): """ @@ -2158,12 +2181,11 @@ def CalcBoundingBox(self): def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None): XY = WorldToPixel(self.XY) H = ScaleWorldToPixel(self.Height)[0] - W = H * (self.bmpWidth / self.bmpHeight) - if (self.ScaledBitmap is None) or (H != self.ScaledHeight) : + W = int(H * self.bmpWidth / self.bmpHeight) + if (self.ScaledBitmap is None) or (H != self.ScaledHeight): self.ScaledHeight = H Img = self.Image.Scale(int(W), int(H)) self.ScaledBitmap = wx.Bitmap(Img) - XY = self.ShiftFun(XY[0], XY[1], W, H) dc.DrawBitmap(self.ScaledBitmap, XY, True) if HTdc and self.HitAble: @@ -2171,6 +2193,7 @@ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None): HTdc.SetBrush(self.HitBrush) HTdc.DrawRectangle(XY, (W, H) ) + class ScaledBitmap2(TextObjectMixin, DrawObject, ): """ Draws a scaled bitmap @@ -2232,7 +2255,7 @@ def __init__(self, ## fixme: this should all accommodate different scales for X and Y if Width is None: self.BmpScale = float(self.bmpHeight) / Height - self.Width = self.bmpWidth / self.BmpScale + self.Width = int(self.bmpWidth / self.BmpScale) self.WH = N.array((self.Width, Height), float) ##fixme: should this have a y = -1 to shift to y-up? self.BmpScale = self.bmpWH / self.WH @@ -2291,63 +2314,60 @@ def _DrawSubBitmap(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc): """ Subsets just the part of the bitmap that is visible then scales and draws that. - """ BBworld = BBox.asBBox(self._Canvas.ViewPortBB) - BBbitmap = BBox.fromPoints(self.WorldToBitmap(BBworld)) + BBbitmap = BBox.fromPoints(self.WorldToBitmap(BBworld)).astype(N.int32) XYs = WorldToPixel(self.XY) # figure out subimage: # fixme: this should be able to be done more succinctly! - - if BBbitmap[0,0] < 0: + if BBbitmap[0, 0] < 0: Xb = 0 - elif BBbitmap[0,0] > self.bmpWH[0]: # off the bitmap + elif BBbitmap[0, 0] > self.bmpWH[0]: # off the bitmap Xb = 0 else: - Xb = BBbitmap[0,0] - XYs[0] = 0 # draw at origin + Xb = BBbitmap[0, 0] + XYs[0] = 0 # draw at origin - if BBbitmap[0,1] < 0: + if BBbitmap[0, 1] < 0: Yb = 0 - elif BBbitmap[0,1] > self.bmpWH[1]: # off the bitmap + elif BBbitmap[0, 1] > self.bmpWH[1]: # off the bitmap Yb = 0 ShouldDraw = False else: - Yb = BBbitmap[0,1] - XYs[1] = 0 # draw at origin + Yb = BBbitmap[0, 1] + XYs[1] = 0 # draw at origin - if BBbitmap[1,0] < 0: - #off the screen -- This should never happen! + if BBbitmap[1, 0] < 0: + # off the screen -- This should never happen! Wb = 0 - elif BBbitmap[1,0] > self.bmpWH[0]: + elif BBbitmap[1, 0] > self.bmpWH[0]: Wb = self.bmpWH[0] - Xb else: - Wb = BBbitmap[1,0] - Xb + Wb = BBbitmap[1, 0] - Xb - if BBbitmap[1,1] < 0: + if BBbitmap[1, 1] < 0: # off the screen -- This should never happen! Hb = 0 ShouldDraw = False - elif BBbitmap[1,1] > self.bmpWH[1]: + elif BBbitmap[1, 1] > self.bmpWH[1]: Hb = self.bmpWH[1] - Yb else: - Hb = BBbitmap[1,1] - Yb + Hb = BBbitmap[1, 1] - Yb FullHeight = ScaleWorldToPixel(self.Height)[0] - scale = float(FullHeight) / float(self.bmpWH[1]) - Ws = int(scale * Wb + 0.5) # add the 0.5 to round + # scale = float(FullHeight) / float(self.bmpWH[1]) + scale = FullHeight / self.bmpWH[1] + Ws = int(scale * Wb + 0.5) # add the 0.5 to round Hs = int(scale * Hb + 0.5) - if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (Xb, Yb, Wb, Hb, Ws, Ws) ): + if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (Xb, Yb, Wb, Hb, Ws, Ws)): Img = self.Image.GetSubImage(wx.Rect(Xb, Yb, Wb, Hb)) - #print("rescaling with High quality") Img.Rescale(Ws, Hs, quality=wx.IMAGE_QUALITY_HIGH) bmp = wx.Bitmap(Img) - self.ScaledBitmap = ((Xb, Yb, Wb, Hb, Ws, Ws), bmp)# this defines the cached bitmap - #XY = self.ShiftFun(XY[0], XY[1], W, H) - #fixme: get the shiftfun working! + self.ScaledBitmap = ((Xb, Yb, Wb, Hb, Ws, Ws), bmp) # this defines the cached bitmap + # XY = self.ShiftFun(XY[0], XY[1], W, H) + # fixme: get the shiftfun working! else: - #print("Using cached bitmap") ##fixme: The cached bitmap could be used if the one needed is the same scale, but ## a subset of the cached one. bmp = self.ScaledBitmap[1] @@ -2446,10 +2466,10 @@ def _Draw(self, dc, Canvas): if len(Points) > 100: xy = Points xywh = N.concatenate((xy-radius, N.ones(xy.shape) * self.Size ), 1 ) - dc.DrawEllipseList(xywh) + dc.DrawEllipseList(xywh.astype(N.int32)) else: for xy in Points: - dc.DrawCircle(xy[0],xy[1], radius) + dc.DrawCircle(xy[0], xy[1], radius) class Arc(XYObjectMixin, LineAndFillMixin, DrawObject): """ @@ -2684,8 +2704,8 @@ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None): Diameter = ScaleWorldToPixel( (self.Diameter,self.Diameter) )[0] else: Diameter = self.Diameter - WH = N.array((Diameter,Diameter), dtype = float) - Corner = CenterXY - (WH / 2) + WH = N.array((Diameter,Diameter), dtype = N.int32) + Corner = CenterXY - (WH / 2).astype(N.int32) dc.SetPen(self.Pen) for i, brush in enumerate(self.Brushes): dc.SetBrush( brush ) @@ -2694,7 +2714,7 @@ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None): if self.Scaled: radius = (ScaleWorldToPixel(self.Diameter)/2)[0]# just the x-coord else: - radius = self.Diameter/2 + radius = int(self.Diameter/2) HTdc.SetPen(self.HitPen) HTdc.SetBrush(self.HitBrush) HTdc.DrawCircle(CenterXY.tolist(), int(radius)) diff --git a/wx/lib/floatcanvas/FloatCanvas.py b/wx/lib/floatcanvas/FloatCanvas.py index a0c316cd31..89d80b3c4a 100644 --- a/wx/lib/floatcanvas/FloatCanvas.py +++ b/wx/lib/floatcanvas/FloatCanvas.py @@ -601,7 +601,7 @@ def Draw(self, Force=False): """ - if N.sometrue(self.PanelSize <= 2 ): + if N.any(self.PanelSize <= 2 ): # it's possible for this to get called before being properly initialized. return if self.Debug: start = clock() @@ -778,8 +778,9 @@ def ZoomToBB(self, NewBB=None, DrawFlag=True): self._ResetBoundingBox() BoundingBox = self.BoundingBox if (BoundingBox is not None) and (not BoundingBox.IsNull()): - self.ViewPortCenter = N.array(((BoundingBox[0,0]+BoundingBox[1,0])/2, - (BoundingBox[0,1]+BoundingBox[1,1])/2 ),N.float_) + self.ViewPortCenter = N.array(((BoundingBox[0, 0] + BoundingBox[1, 0]) / 2, + (BoundingBox[0, 1] + BoundingBox[1, 1]) / 2), + float) self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter) # Compute the new Scale BoundingBox = BoundingBox*self.MapProjectionVector # this does need to make a copy! @@ -907,19 +908,18 @@ def PixelToWorld(self, Points): (self.PanelSize/2))/self.TransformVector) + self.ViewPortCenter) - def WorldToPixel(self,Coordinates): + def WorldToPixel(self, Coordinates): """ This function will get passed to the drawing functions of the objects, to transform from world to pixel coordinates. Coordinates should be a NX2 array of (x,y) coordinates, or a 2-tuple, or sequence of 2-tuples. """ - #Note: this can be called by users code for various reasons, so N.asarray is needed. - return (((N.asarray(Coordinates,float) - - self.ViewPortCenter)*self.TransformVector)+ - (self.HalfPanelSize)).astype('i') + # Note: this can be called by users code for various reasons, so N.asarray is needed. + return (((N.asarray(Coordinates, float) - self.ViewPortCenter) * + self.TransformVector) + (self.HalfPanelSize)).astype(N.int32) - def ScaleWorldToPixel(self,Lengths): + def ScaleWorldToPixel(self, Lengths): """ This function will get passed to the drawing functions of the objects, to Change a length from world to pixel coordinates. @@ -927,17 +927,18 @@ def ScaleWorldToPixel(self,Lengths): Lengths should be a NX2 array of (x,y) coordinates, or a 2-tuple, or sequence of 2-tuples. """ - return ( (N.asarray(Lengths, float)*self.TransformVector) ).astype('i') + return ((N.asarray(Lengths, float) * self.TransformVector)).astype(N.int32) - def ScalePixelToWorld(self,Lengths): + + def ScalePixelToWorld(self, Lengths): """ - This function computes a pair of x.y lengths, + This function computes a pair of x,y lengths, to change then from pixel to world coordinates. Lengths should be a NX2 array of (x,y) coordinates, or a 2-tuple, or sequence of 2-tuples. """ - return (N.asarray(Lengths,float) / self.TransformVector) + return (N.asarray(Lengths, float) / self.TransformVector) def AddObject(self, obj): """ @@ -977,12 +978,13 @@ def _DrawObjects(self, dc, DrawList, ScreenDC, ViewPortBB, HTdc=None): device context. """ dc.SetBackground(self.BackgroundBrush) - #i = 0 - PanelSize0, PanelSize1 = self.PanelSize # for speed - WorldToPixel = self.WorldToPixel # for speed - ScaleWorldToPixel = self.ScaleWorldToPixel # for speed - Blit = ScreenDC.Blit # for speed - NumBetweenBlits = self.NumBetweenBlits # for speed + + PanelSize0, PanelSize1 = self.PanelSize # for speed + WorldToPixel = self.WorldToPixel # for speed + ScaleWorldToPixel = self.ScaleWorldToPixel # for speed + Blit = ScreenDC.Blit # for speed + NumBetweenBlits = self.NumBetweenBlits # for speed + for i, Object in enumerate(self._ShouldRedraw(DrawList, ViewPortBB)): if Object.Visible: Object._Draw(dc, WorldToPixel, ScaleWorldToPixel, HTdc) diff --git a/wx/lib/floatcanvas/Utilities/GUI.py b/wx/lib/floatcanvas/Utilities/GUI.py index 68998a63b4..2650c7b65b 100644 --- a/wx/lib/floatcanvas/Utilities/GUI.py +++ b/wx/lib/floatcanvas/Utilities/GUI.py @@ -23,21 +23,22 @@ """ -import numpy as np import wx from wx.lib.floatcanvas import FloatCanvas, GUIMode + class RubberBandBox(GUIMode.GUIBase): """ - Class to provide a GUI Mode that makes a rubber band box that can be drawn on a Window - + Class to provide a GUI Mode that makes a rubber band box + that can be drawn on a Window """ - def __init__(self, CallBack, Tol=5): + def __init__(self, CallBack, Tol=5, style='dashed'): """ - Default class constructor. + + Create a Rubber Band Box :param `CallBack`: is the method you want called when the mouse is released. That method will be called, passing in a rect @@ -45,47 +46,93 @@ def __init__(self, CallBack, Tol=5): world coords. :param `Tol`: The tolerance for the smallest rectangle allowed. defaults to 5. In pixels + :param style: style of RB box: 'dashed': a dashed outline + 'grey' a black outline with grey fill """ self.Canvas = None # this will be set when the mode is set on a Canvas self.CallBack = CallBack self.Tol = Tol + if style not in {'dashed', 'grey'}: + raise ValueError("style must be one of: 'dashed', 'grey'") + self.style = style self.Drawing = False self.RBRect = None self.StartPointWorld = None + # self.overlay = wx.Overlay() + self.overlay = None return None + # def __del__(self): + # """ + # you get a crash on exit if it's not deleted + + # though this di + # """ + + # self.overlay = None + def OnMove(self, event): if self.Drawing: x, y = self.StartPoint Cornerx, Cornery = event.GetPosition() - w, h = ( Cornerx - x, Cornery - y) + w, h = (Cornerx - x, Cornery - y) if abs(w) > self.Tol and abs(h) > self.Tol: - # draw the RB box + # Draw the rubber-band rectangle using an overlay so it + # will manage keeping the rectangle and the former window + # contents separate. dc = wx.ClientDC(self.Canvas) - dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH)) - dc.SetBrush(wx.TRANSPARENT_BRUSH) - dc.SetLogicalFunction(wx.XOR) - if self.RBRect: + odc = wx.DCOverlay(self.overlay, dc) + odc.Clear() + self.RBRect = ((x, y), (w, h)) + if self.style == 'dashed': + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(wx.Pen(wx.Colour(200, 200, 200), 3)) + dc.DrawRectangle(*self.RBRect) + dc.SetPen(wx.Pen("black", 3, style=wx.PENSTYLE_SHORT_DASH)) + dc.DrawRectangle(*self.RBRect) + else: + dc.SetBrush(wx.Brush(wx.Colour(192, 192, 192, 128))) + dc.SetPen(wx.Pen("black", 1)) dc.DrawRectangle(*self.RBRect) - self.RBRect = ((x, y), (w, h) ) - dc.DrawRectangle(*self.RBRect) + del odc # work around a bug in the Python wrappers to make + # sure the odc is destroyed before the ClientDC is. + self.Canvas._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) def OnLeftDown(self, event): # Start drawing self.Drawing = True self.StartPoint = event.GetPosition() + self.overlay = wx.Overlay() def OnLeftUp(self, event): - # Stop Drawing + # Stop Drawing and clear the overlay if self.Drawing: self.Drawing = False - if self.RBRect: - world_rect = (self.Canvas.PixelToWorld(self.RBRect[0]), - self.Canvas.ScalePixelToWorld(self.RBRect[1]) - ) - wx.CallAfter(self.CallBack, world_rect) + dc = wx.ClientDC(self.Canvas) + odc = wx.DCOverlay(self.overlay, dc) + odc.Clear() + if self.RBRect: + world_rect = (self.Canvas.PixelToWorld(self.RBRect[0]), + self.Canvas.ScalePixelToWorld(self.RBRect[1]) + ) + wx.CallAfter(self.CallBack, world_rect) self.RBRect = None + wx.CallAfter(self._delete_overlay) + + def _delete_overlay(self): + """ + This is a total Kludge, but I was getting crashes (on a Mac) if it was kept around + + Putting it in __del__ didn't work :-( + + Clearing it directly in OnLeftUp crashed also - I guess rending wasn't completely done? + + I *think* this is due to a bug in wxPython -- it should delete an Overlay on it's own, + but what can you do? + + """ + self.overlay = None diff --git a/wx/lib/floatcanvas/__init__.py b/wx/lib/floatcanvas/__init__.py index a8a1339511..b4e7a69bb3 100644 --- a/wx/lib/floatcanvas/__init__.py +++ b/wx/lib/floatcanvas/__init__.py @@ -96,19 +96,10 @@ http://trac.paulmcnett.com/floatcanvas -Mailing List: -http://mail.paulmcnett.com/cgi-bin/mailman/listinfo/floatcanvas +For Development issues / questions use the wxPython resources -The latest code is in the main wx SVN: - -For classic: - -http://svn.wxwidgets.org/viewvc/wx/wxPython/3rdParty/FloatCanvas/ - -For Phoenix: - -http://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk/wx/lib/floatcanvas +https://github.com/wxWidgets/Phoenix Check for updates or answers to questions, send me an email. Please let me know if you're using this!!! @@ -119,6 +110,6 @@ """ -__version__ = "0.9.18" +__version__ = "0.10.0" diff --git a/wx/lib/floatcanvas/test_FC_Objects.py b/wx/lib/floatcanvas/test_FC_Objects.py new file mode 100644 index 0000000000..0420608407 --- /dev/null +++ b/wx/lib/floatcanvas/test_FC_Objects.py @@ -0,0 +1,50 @@ +""" +test code for FC_Objects.py + +Note: hardly anything here yet. If we add more, we should probably move this +to the main unittest suite location + +On the Mac with conda, this needs to be run with pythonw: + +pythonw -m pytest + +""" + +import pytest + +import wx + +from .FCObjects import _cycleidxs + +app = wx.App() + + +def test__cycleidxs_start(): + """ + on first call, it should provide ... + """ + CG = _cycleidxs(indexcount=3, maxvalue=256, step=1) + assert next(CG) == (0, 0, 0) + + + +def test__cycleidxs_multiple(): + """ + make sure it gets the first few anyway ... + """ + CG = _cycleidxs(indexcount=3, maxvalue=256, step=1) + + print("first three") + for i in range(3): + color = next(CG) + print(color) + assert color == (0, 0, i) + for i in range(253): + next(CG) + print("after 255") + for i in range(3): + color = next(CG) + print(color) + assert color == (0, 1, i) + + assert False