📄 floatcanvas.py
字号:
from __future__ import division
try:
from Numeric import array,asarray,Float,cos, sin, pi,sum,minimum,maximum,Int32,zeros, ones, concatenate, sqrt, argmin, power, absolute, matrixmultiply, transpose, sometrue, arange, hypot
except ImportError:
try:
from numarray import array, asarray, Float, cos, sin, pi, sum, minimum, maximum, Int32, zeros, concatenate, matrixmultiply, transpose, sometrue, arange, hypot
except ImportError:
raise ImportError("I could not import either Numeric or numarray")
from time import clock, sleep
import Resources # A file with icons, etc for FloatCanvas
import wx
import types
import os
## A global variable to hold the Pixels per inch that wxWindows thinks is in use
## This is used for scaling fonts.
## This can't be computed on module __init__, because a wx.App might not have initialized yet.
global ScreenPPI
## a custom Exceptions:
class FloatCanvasError(Exception):
pass
## Create all the mouse events
# I don't see a need for these two, but maybe some day!
#EVT_FC_ENTER_WINDOW = wx.NewEventType()
#EVT_FC_LEAVE_WINDOW = wx.NewEventType()
EVT_FC_LEFT_DOWN = wx.NewEventType()
EVT_FC_LEFT_UP = wx.NewEventType()
EVT_FC_LEFT_DCLICK = wx.NewEventType()
EVT_FC_MIDDLE_DOWN = wx.NewEventType()
EVT_FC_MIDDLE_UP = wx.NewEventType()
EVT_FC_MIDDLE_DCLICK = wx.NewEventType()
EVT_FC_RIGHT_DOWN = wx.NewEventType()
EVT_FC_RIGHT_UP = wx.NewEventType()
EVT_FC_RIGHT_DCLICK = wx.NewEventType()
EVT_FC_MOTION = wx.NewEventType()
EVT_FC_MOUSEWHEEL = wx.NewEventType()
## these two are for the hit-test stuff, I never make them real Events
EVT_FC_ENTER_OBJECT = wx.NewEventType()
EVT_FC_LEAVE_OBJECT = wx.NewEventType()
##Create all mouse event binding functions
#def EVT_ENTER_WINDOW( window, function ):
# window.Connect( -1, -1, EVT_FC_ENTER_WINDOW, function )
#def EVT_LEAVE_WINDOW( window, function ):
# window.Connect( -1, -1,EVT_FC_LEAVE_WINDOW , function )
def EVT_LEFT_DOWN( window, function ):
window.Connect( -1, -1,EVT_FC_LEFT_DOWN , function )
def EVT_LEFT_UP( window, function ):
window.Connect( -1, -1,EVT_FC_LEFT_UP , function )
def EVT_LEFT_DCLICK ( window, function ):
window.Connect( -1, -1,EVT_FC_LEFT_DCLICK , function )
def EVT_MIDDLE_DOWN ( window, function ):
window.Connect( -1, -1,EVT_FC_MIDDLE_DOWN , function )
def EVT_MIDDLE_UP ( window, function ):
window.Connect( -1, -1,EVT_FC_MIDDLE_UP , function )
def EVT_MIDDLE_DCLICK ( window, function ):
window.Connect( -1, -1,EVT_FC_MIDDLE_DCLICK , function )
def EVT_RIGHT_DOWN ( window, function ):
window.Connect( -1, -1,EVT_FC_RIGHT_DOWN , function )
def EVT_RIGHT_UP( window, function ):
window.Connect( -1, -1,EVT_FC_RIGHT_UP , function )
def EVT_RIGHT_DCLICK( window, function ):
window.Connect( -1, -1,EVT_FC_RIGHT_DCLICK , function )
def EVT_MOTION( window, function ):
window.Connect( -1, -1,EVT_FC_MOTION , function )
def EVT_MOUSEWHEEL( window, function ):
window.Connect( -1, -1,EVT_FC_MOUSEWHEEL , function )
class _MouseEvent(wx.PyCommandEvent):
"""
This event class takes a regular wxWindows mouse event as a parameter,
and wraps it so that there is access to all the original methods. This
is similar to subclassing, but you can't subclass a wxWindows event
The goal is to be able to it just like a regular mouse event.
It adds the method:
GetCoords() , which returns and (x,y) tuple in world coordinates.
Another difference is that it is a CommandEvent, which propagates up
the window hierarchy until it is handled.
"""
def __init__(self, EventType, NativeEvent, WinID, Coords = None):
wx.PyCommandEvent.__init__(self)
self.SetEventType( EventType )
self._NativeEvent = NativeEvent
self.Coords = Coords
# I don't think this is used.
# def SetCoords(self,Coords):
# self.Coords = Coords
def GetCoords(self):
return self.Coords
def __getattr__(self, name):
#return eval(self.NativeEvent.__getattr__(name) )
return getattr(self._NativeEvent, name)
def _cycleidxs(indexcount, maxvalue, step):
"""
Utility function used by _colorGenerator
"""
if indexcount == 0:
yield ()
else:
for idx in xrange(0, maxvalue, step):
for tail in _cycleidxs(indexcount - 1, maxvalue, step):
yield (idx, ) + tail
def _colorGenerator():
"""
Generates a seris of unique colors used to do hit-tests with the HIt
Test bitmap
"""
import sys
if sys.platform == 'darwin':
depth = 24
else:
b = wx.EmptyBitmap(1,1)
depth = b.GetDepth()
if depth == 16:
step = 8
elif depth >= 24:
step = 1
else:
raise "ColorGenerator does not work with depth = %s" % depth
return _cycleidxs(indexcount=3, maxvalue=256, step=step)
#### I don't know if the Set objects are useful, beyond the pointset
#### object. The problem is that when zoomed in, the BB is checked to see
#### whether to draw the object. A Set object can defeat this. One day
#### I plan to write some custon C++ code to draw sets of objects
##class ObjectSetMixin:
## """
## A mix-in class for draw objects that are sets of objects
## It contains methods for setting lists of pens and brushes
## """
## def SetPens(self,LineColors,LineStyles,LineWidths):
## """
## This method used when an object could have a list of pens, rather than just one
## It is used for LineSet, and perhaps others in the future.
## fixme: this should be in a mixin
## fixme: this is really kludgy, there has got to be a better way!
## """
## length = 1
## if type(LineColors) == types.ListType:
## length = len(LineColors)
## else:
## LineColors = [LineColors]
## if type(LineStyles) == types.ListType:
## length = len(LineStyles)
## else:
## LineStyles = [LineStyles]
## if type(LineWidths) == types.ListType:
## length = len(LineWidths)
## else:
## LineWidths = [LineWidths]
## if length > 1:
## if len(LineColors) == 1:
## LineColors = LineColors*length
## if len(LineStyles) == 1:
## LineStyles = LineStyles*length
## if len(LineWidths) == 1:
## LineWidths = LineWidths*length
## self.Pens = []
## for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):
## if LineColor is None or LineStyle is None:
## self.Pens.append(wx.TRANSPARENT_PEN)
## # what's this for?> self.LineStyle = 'Transparent'
## if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
## Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
## self.Pens.append(Pen)
## else:
## self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])
## if length == 1:
## self.Pens = self.Pens[0]
class DrawObject:
"""
This is the base class for all the objects that can be drawn.
One must subclass from this (and an assortment of Mixins) to create
a new DrawObject.
"""
def __init__(self, InForeground = False, IsVisible = True):
self.InForeground = InForeground
self._Canvas = None
self.HitColor = None
self.CallBackFuncs = {}
## these are the defaults
self.HitAble = False
self.HitLine = True
self.HitFill = True
self.MinHitLineWidth = 3
self.HitLineWidth = 3 ## this gets re-set by the subclasses if necessary
self.Brush = None
self.Pen = None
self.FillStyle = "Solid"
self.Visible = IsVisible
# I pre-define all these as class variables to provide an easier
# interface, and perhaps speed things up by caching all the Pens
# and Brushes, although that may not help, as I think wx now
# does that on it's own. Send me a note if you know!
BrushList = {
( None,"Transparent") : wx.TRANSPARENT_BRUSH,
("Blue","Solid") : wx.BLUE_BRUSH,
("Green","Solid") : wx.GREEN_BRUSH,
("White","Solid") : wx.WHITE_BRUSH,
("Black","Solid") : wx.BLACK_BRUSH,
("Grey","Solid") : wx.GREY_BRUSH,
("MediumGrey","Solid") : wx.MEDIUM_GREY_BRUSH,
("LightGrey","Solid") : wx.LIGHT_GREY_BRUSH,
("Cyan","Solid") : wx.CYAN_BRUSH,
("Red","Solid") : wx.RED_BRUSH
}
PenList = {
(None,"Transparent",1) : wx.TRANSPARENT_PEN,
("Green","Solid",1) : wx.GREEN_PEN,
("White","Solid",1) : wx.WHITE_PEN,
("Black","Solid",1) : wx.BLACK_PEN,
("Grey","Solid",1) : wx.GREY_PEN,
("MediumGrey","Solid",1) : wx.MEDIUM_GREY_PEN,
("LightGrey","Solid",1) : wx.LIGHT_GREY_PEN,
("Cyan","Solid",1) : wx.CYAN_PEN,
("Red","Solid",1) : wx.RED_PEN
}
FillStyleList = {
"Transparent" : wx.TRANSPARENT,
"Solid" : wx.SOLID,
"BiDiagonalHatch": wx.BDIAGONAL_HATCH,
"CrossDiagHatch" : wx.CROSSDIAG_HATCH,
"FDiagonal_Hatch": wx.FDIAGONAL_HATCH,
"CrossHatch" : wx.CROSS_HATCH,
"HorizontalHatch": wx.HORIZONTAL_HATCH,
"VerticalHatch" : wx.VERTICAL_HATCH
}
LineStyleList = {
"Solid" : wx.SOLID,
"Transparent": wx.TRANSPARENT,
"Dot" : wx.DOT,
"LongDash" : wx.LONG_DASH,
"ShortDash" : wx.SHORT_DASH,
"DotDash" : wx.DOT_DASH,
}
def Bind(self, Event, CallBackFun):
self.CallBackFuncs[Event] = CallBackFun
self.HitAble = True
self._Canvas.UseHitTest = True
if not self._Canvas._HTdc:
self._Canvas.MakeNewHTdc()
if not self.HitColor:
if not self._Canvas.HitColorGenerator:
self._Canvas.HitColorGenerator = _colorGenerator()
self._Canvas.HitColorGenerator.next() # first call to prevent the background color from being used.
self.HitColor = self._Canvas.HitColorGenerator.next()
self.SetHitPen(self.HitColor,self.HitLineWidth)
self.SetHitBrush(self.HitColor)
# put the object in the hit dict, indexed by it's color
if not self._Canvas.HitDict:
self._Canvas.MakeHitDict()
self._Canvas.HitDict[Event][self.HitColor] = (self) # put the object in the hit dict, indexed by it's color
def UnBindAll(self):
## fixme: this only removes one from each list, there could be more.
if self._Canvas.HitDict:
for List in self._Canvas.HitDict.itervalues():
try:
List.remove(self)
except ValueError:
pass
self.HitAble = False
def SetBrush(self,FillColor,FillStyle):
if FillColor is None or FillStyle is None:
self.Brush = wx.TRANSPARENT_BRUSH
##fixme: should I really re-set the style?
self.FillStyle = "Transparent"
else:
self.Brush = self.BrushList.setdefault( (FillColor,FillStyle), wx.Brush(FillColor,self.FillStyleList[FillStyle] ) )
def SetPen(self,LineColor,LineStyle,LineWidth):
if (LineColor is None) or (LineStyle is None):
self.Pen = wx.TRANSPARENT_PEN
self.LineStyle = 'Transparent'
else:
self.Pen = self.PenList.setdefault( (LineColor,LineStyle,LineWidth), wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle]) )
def SetHitBrush(self,HitColor):
if not self.HitFill:
self.HitBrush = wx.TRANSPARENT_BRUSH
else:
self.HitBrush = self.BrushList.setdefault( (HitColor,"solid"), wx.Brush(HitColor,self.FillStyleList["Solid"] ) )
def SetHitPen(self,HitColor,LineWidth):
if not self.HitLine:
self.HitPen = wx.TRANSPARENT_PEN
else:
self.HitPen = self.PenList.setdefault( (HitColor, "solid", self.HitLineWidth), wx.Pen(HitColor, self.HitLineWidth, self.LineStyleList["Solid"]) )
def PutInBackground(self):
if self._Canvas and self.InForeground:
self._Canvas._ForeDrawList.remove(self)
self._Canvas._DrawList.append(self)
self._Canvas._BackgroundDirty = True
self.InForeground = False
def PutInForeground(self):
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -