1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
|
#!/usr/bin/env python
"""
This is a small demo, showing how to make an object that can be moved around.
It also contains a simple prototype for a "Connector" object
-- a line connecting two other objects
"""
import wx
#ver = 'local'
ver = 'installed'
if ver == 'installed': ## import the installed version
from wx.lib.floatcanvas import NavCanvas, Resources
from wx.lib.floatcanvas import FloatCanvas as FC
from wx.lib.floatcanvas.Utilities import BBox
print("using installed version: %s" % wx.lib.floatcanvas.__version__)
elif ver == 'local':
## import a local version
import sys
sys.path.append("..")
from floatcanvas import NavCanvas, Resources
from floatcanvas import FloatCanvas as FC
from floatcanvas.Utilities import BBox
import numpy as np
## here we create some new mixins:
class MovingObjectMixin:
"""
Methods required for a Moving object
"""
def GetOutlinePoints(self):
BB = self.BoundingBox
OutlinePoints = np.array( ( (BB[0,0], BB[0,1]),
(BB[0,0], BB[1,1]),
(BB[1,0], BB[1,1]),
(BB[1,0], BB[0,1]),
)
)
return OutlinePoints
class ConnectorObjectMixin:
"""
Mixin class for DrawObjects that can be connected with lines
NOte that this versionony works for Objects that have an "XY" attribute:
that is, one that is derived from XHObjectMixin.
"""
def GetConnectPoint(self):
return self.XY
class MovingBitmap(FC.ScaledBitmap, MovingObjectMixin, ConnectorObjectMixin):
"""
ScaledBitmap Object that can be moved
"""
## All we need to do is is inherit from:
## ScaledBitmap, MovingObjectMixin and ConnectorObjectMixin
pass
class MovingCircle(FC.Circle, MovingObjectMixin, ConnectorObjectMixin):
"""
ScaledBitmap Object that can be moved
"""
## All we need to do is is inherit from:
## ScaledBitmap, MovingObjectMixin and ConnectorObjectMixin
pass
class MovingArc(FC.Arc, MovingObjectMixin, ConnectorObjectMixin):
"""
ScaledBitmap Object that can be moved
"""
## All we need to do is is inherit from:
## ScaledBitmap, MovingObjectMixin and ConnectorObjectMixin
pass
class ConnectorLine(FC.LineOnlyMixin, FC.DrawObject,):
"""
A Line that connects two objects -- it uses the objects to get its coordinates
"""
##fixme: this should be added to the Main FloatCanvas Objects some day.
def __init__(self,
Object1,
Object2,
LineColor = "Black",
LineStyle = "Solid",
LineWidth = 1,
InForeground = False):
FC.DrawObject.__init__(self, InForeground)
self.Object1 = Object1
self.Object2 = Object2
self.LineColor = LineColor
self.LineStyle = LineStyle
self.LineWidth = LineWidth
self.CalcBoundingBox()
self.SetPen(LineColor,LineStyle,LineWidth)
self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
def CalcBoundingBox(self):
self.BoundingBox = BBox.fromPoints((self.Object1.GetConnectPoint(),
self.Object2.GetConnectPoint()) )
if self._Canvas:
self._Canvas.BoundingBoxDirty = True
def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
Points = np.array( (self.Object1.GetConnectPoint(),
self.Object2.GetConnectPoint()) )
Points = WorldToPixel(Points)
dc.SetPen(self.Pen)
dc.DrawLines(Points)
if HTdc and self.HitAble:
HTdc.SetPen(self.HitPen)
HTdc.DrawLines(Points)
class TriangleShape1(FC.Polygon, MovingObjectMixin):
def __init__(self, XY, L):
"""
An equilateral triangle object
XY is the middle of the triangle
L is the length of one side of the Triangle
"""
XY = np.asarray(XY)
XY.shape = (2,)
Points = self.CompPoints(XY, L)
FC.Polygon.__init__(self, Points,
LineColor = "Black",
LineStyle = "Solid",
LineWidth = 2,
FillColor = "Red",
FillStyle = "Solid")
## Override the default OutlinePoints
def GetOutlinePoints(self):
return self.Points
def CompPoints(self, XY, L):
c = L/ np.sqrt(3)
Points = np.array(((0, c),
( L/2.0, -c/2.0),
(-L/2.0, -c/2.0)),
np.float64)
Points += XY
return Points
class DrawFrame(wx.Frame):
"""
A simple frame used for the Demo
"""
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.CreateStatusBar()
# Add the Canvas
Canvas = NavCanvas.NavCanvas(self,-1,(500,500),
ProjectionFun = None,
Debug = 0,
BackgroundColor = "DARK SLATE BLUE",
).Canvas
self.Canvas = Canvas
Canvas.Bind(FC.EVT_MOTION, self.OnMove )
Canvas.Bind(FC.EVT_LEFT_UP, self.OnLeftUp )
Points = np.array(((0,0),
(1,0),
(0.5, 1)),
float)
data = (( (0,0), 1),
( (3,3), 2),
( (-2,3), 2.5 ),
)
for p, L in data:
Tri = TriangleShape1(p, 1)
Canvas.AddObject(Tri)
Tri.Bind(FC.EVT_FC_LEFT_DOWN, self.ObjectHit)
Circle = MovingCircle( (1, 3), 2, FillColor="Blue")
Canvas.AddObject(Circle)
Circle.Bind(FC.EVT_FC_LEFT_DOWN, self.ObjectHit)
Bitmaps = []
## create the bitmaps first
for Point in ((1,1), (-4,3)):
Bitmaps.append(MovingBitmap(Resources.getMondrianImage(),
Point,
Height=1,
Position='cc')
)
Line = ConnectorLine(Bitmaps[0], Bitmaps[1], LineWidth=3, LineColor="Red")
Canvas.AddObject(Line)
## then add them to the Canvas, so they are on top of the line
for bmp in Bitmaps:
Canvas.AddObject(bmp)
bmp.Bind(FC.EVT_FC_LEFT_DOWN, self.ObjectHit)
A = MovingArc((-5, 0),(-2, 2),(-5, 2), LineColor="Red", LineWidth=2)
self.Canvas.AddObject(A)
A.Bind(FC.EVT_FC_LEFT_DOWN, self.ObjectHit)
self.Show(True)
self.Canvas.ZoomToBB()
self.MoveObject = None
self.Moving = False
return None
def ObjectHit(self, object):
if not self.Moving:
self.Moving = True
self.StartPoint = object.HitCoordsPixel
self.StartObject = self.Canvas.WorldToPixel(object.GetOutlinePoints())
self.MoveObject = None
self.MovingObject = object
def OnMove(self, event):
"""
Updates the status bar with the world coordinates
and moves the object it is clicked on
"""
self.SetStatusText("%.4f, %.4f"%tuple(event.Coords))
if self.Moving:
dxy = event.GetPosition() - self.StartPoint
# Draw the Moving Object:
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.MoveObject is not None:
dc.DrawPolygon(self.MoveObject)
self.MoveObject = self.StartObject + dxy
dc.DrawPolygon(self.MoveObject)
def OnLeftUp(self, event):
if self.Moving:
self.Moving = False
if self.MoveObject is not None:
dxy = event.GetPosition() - self.StartPoint
dxy = self.Canvas.ScalePixelToWorld(dxy)
self.MovingObject.Move(dxy)
self.MoveTri = None
self.Canvas.Draw(True)
if __name__ == "__main__":
app = wx.App(0)
DrawFrame(None, -1, "FloatCanvas Moving Object App", wx.DefaultPosition, (700,700) )
app.MainLoop()
|