Lightningbeam/GUI/Cocoa/Canvas.py

322 lines
10 KiB
Python

#
# Python GUI - Drawing - PyObjC
#
from array import array
from Foundation import NSPoint, NSMakeRect, NSString
from AppKit import NSGraphicsContext, NSBezierPath, NSEvenOddWindingRule, \
NSFontAttributeName, NSForegroundColorAttributeName, \
NSCompositeCopy, NSCompositeSourceOver, NSAffineTransform
from GUI import export
from GUI.StdColors import black, white
from GUI.GCanvases import Canvas as GCanvas
import math
class Canvas(GCanvas):
def __init__(self):
self._ns_path = NSBezierPath.bezierPath()
self._ns_path.setWindingRule_(NSEvenOddWindingRule)
self._stack = []
ctx = NSGraphicsContext.currentContext()
ctx.setCompositingOperation_(NSCompositeSourceOver)
GCanvas.__init__(self)
self._printing = not ctx.isDrawingToScreen()
self.initgraphics()
self.transformstack = [[]]
def get_pencolor(self):
return self._pencolor
def set_pencolor(self, c):
self._pencolor = c
def get_fillcolor(self):
return self._fillcolor
def set_fillcolor(self, c):
self._fillcolor = c
def get_textcolor(self):
return self._textcolor
def set_textcolor(self, c):
self._textcolor = c
def get_backcolor(self):
return self._backcolor
def set_backcolor(self, c):
self._backcolor = c
def get_pensize(self):
return self._pensize
def set_pensize(self, d):
self._pensize = d
self._ns_path.setLineWidth_(d)
def get_font(self):
return self._font
def set_font(self, f):
self._font = f
def get_current_point(self):
return self._ns_path.currentPoint()
def newpath(self):
self._ns_path.removeAllPoints()
#for i in range(len(self.transformstack)):
#j = self.transformstack.pop()
#transforms = {"translate":self.translate,"rotate":self.rotate,"scale":self.scale}
#transforms[j[0]](*j[1:])
def moveto(self, x, y):
x, y = self._transform(x, y)
self._ns_path.moveToPoint_((x, y))
def rmoveto(self, dx, dy):
self._ns_path.relativeMoveToPoint_((dx, dy))
def lineto(self, x, y):
x, y = self._transform(x, y)
self._ns_path.lineToPoint_((x, y))
def rlineto(self, dx, dy):
self._ns_path.relativeLineToPoint_((dx, dy))
def curveto(self, cp1, cp2, ep):
cp1 = self._transform(*cp1)
cp2 = self._transform(*cp2)
ep = self._transform(*ep)
self._ns_path.curveToPoint_controlPoint1_controlPoint2_(
ep, cp1, cp2)
def rcurveto(self, cp1, cp2, ep):
self._ns_path.relativeCurveToPoint_controlPoint1_controlPoint2_(
ep, cp1, cp2)
def arc(self, c, r, a0, a1):
c = self._transform(*c)
self._ns_path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(
c, r, a0, a1)
def rect(self, rect):
try:
rect = self._transform(rect[0],rect[1]) + self._transform(rect[2], rect[3])
except:
print "Error here - line 113"
self._ns_path.appendBezierPathWithRect_(_ns_rect(rect))
def oval(self, rect):
rect = (self._transform(*rect[0]), self._transform(*rect[1]))
self._ns_path.appendBezierPathWithOvalInRect(_ns_rect(rect))
def lines(self, points):
# Due to a memory leak in PyObjC 2.3, we need to be very careful
# about the type of object that we pass to appendBezierPathWithPoints_count_.
# If 'points' is a numpy array, we convert it to an array.array of type 'f',
# else we fall back on iterating over the points in Python.
# ns = self._ns_path
# ns.moveToPoint_(points[0])
# ns.appendBezierPathWithPoints_count_(points, len(points))
try:
p = points.flat
except AttributeError:
GCanvas.lines(self, points)
else:
a = array('f', p)
ns = self._ns_path
ns.moveToPoint_(points[0])
ns.appendBezierPathWithPoints_count_(a, len(points))
def poly(self, points):
# ns = self._ns_path
# ns.moveToPoint_(points[0])
# ns.appendBezierPathWithPoints_count_(points, len(points))
# ns.closePath()
self.lines(points)
self.closepath()
def closepath(self):
self._ns_path.closePath()
def clip(self):
ns = self._ns_path
ns.addClip()
def rectclip(self, (l, t, r, b)):
ns_rect = NSMakeRect(l, t, r - l, b - t)
NSBezierPath.clipRect_(ns_rect)
def gsave(self):
self._stack.append((
self._pencolor, self._fillcolor, self._textcolor, self._backcolor,
self._pensize, self._font))
self.transformstack.append([])
NSGraphicsContext.currentContext().saveGraphicsState()
def grestore(self):
(self._pencolor, self._fillcolor, self._textcolor, self._backcolor,
self._pensize, self._font) = self._stack.pop()
self.transformstack.pop()
NSGraphicsContext.currentContext().restoreGraphicsState()
def stroke(self):
ns = self._ns_path
self._pencolor._ns_color.set()
ns.stroke()
def fill(self):
ns = self._ns_path
self._fillcolor._ns_color.set()
ns.fill()
def erase(self):
ns = self._ns_path
self._backcolor._ns_color.set()
ctx = NSGraphicsContext.currentContext()
ctx.setCompositingOperation_(NSCompositeCopy)
ns.fill()
ctx.setCompositingOperation_(NSCompositeSourceOver)
def fill_stroke(self):
ns = self._ns_path
self._pencolor._ns_color.set()
ns.stroke()
self._fillcolor._ns_color.set()
ns.fill()
def show_text(self, text):
x, y = self._ns_path.currentPoint()
font = self._font
ns_font = font._ns_font
ns_color = self._textcolor._ns_color
ns_string = NSString.stringWithString_(text)
ns_attrs = {
NSFontAttributeName: ns_font,
NSForegroundColorAttributeName: ns_color,
}
# print "Canvas.show_text:", repr(text) ###
# print "family:", ns_font.familyName() ###
# print "size:", ns_font.pointSize() ###
# print "ascender:", ns_font.ascender() ###
# print "descender:", ns_font.descender() ###
# print "capHeight:", ns_font.capHeight() ###
# print "leading:", ns_font.leading() ###
# print "matrix:", ns_font.matrix() ###
# print "defaultLineHeightForFont:", ns_font.defaultLineHeightForFont() ###
h = ns_font.defaultLineHeightForFont()
d = -ns_font.descender()
dy = h - d
if ns_font.familyName() == "Courier New":
dy += ns_font.pointSize() * 0.229167
ns_point = NSPoint(x, y - dy)
#print "drawing at:", ns_point ###
ns_string.drawAtPoint_withAttributes_(ns_point, ns_attrs)
dx = ns_font.widthOfString_(ns_string)
#self._ns_path.relativeMoveToPoint_(NSPoint(x + dx, y))
self._ns_path.relativeMoveToPoint_((dx, 0))
def _ns_frame_rect(self, (l, t, r, b)):
p = self._pensize
q = 0.5 * p
return NSMakeRect(l + q, t + q, r - l - p, b - t - p)
def stroke_rect(self, r):
self._pencolor._ns_color.set()
NSBezierPath.setDefaultLineWidth_(self._pensize)
NSBezierPath.strokeRect_(_ns_rect(r))
def frame_rect(self, r):
self._pencolor._ns_color.set()
NSBezierPath.setDefaultLineWidth_(self._pensize)
NSBezierPath.strokeRect_(self._ns_frame_rect(r))
def fill_rect(self, r):
self._fillcolor._ns_color.set()
NSBezierPath.fillRect_(_ns_rect(r))
def erase_rect(self, r):
self._backcolor._ns_color.set()
NSBezierPath.fillRect_(_ns_rect(r))
def _ns_oval_path(self, ns_rect):
ns_path = NSBezierPath.bezierPathWithOvalInRect_(ns_rect)
ns_path.setLineWidth_(self._pensize)
return ns_path
def stroke_oval(self, r):
self._pencolor._ns_color.set()
self._ns_oval_path(_ns_rect(r)).stroke()
def frame_oval(self, r):
self._pencolor._ns_color.set()
self._ns_oval_path(self._ns_frame_rect(r)).stroke()
def fill_oval(self, r):
self._fillcolor._ns_color.set()
self._ns_oval_path(_ns_rect(r)).fill()
def erase_oval(self, r):
self._backcolor._ns_color.set()
self._ns_oval_path(_ns_rect(r)).fill()
def _ns_arc_path(self, c, r, sa, ea):
ns_path = NSBezierPath.bezierPath()
ns_path.setLineWidth_(self._pensize)
ns_path.\
appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(
c, r, sa, ea)
return ns_path
def stroke_arc(self, center, radius, start_angle, arc_angle):
ns_path = self._ns_arc_path(center, radius, start_angle, arc_angle)
self._pencolor._ns_color.set()
ns_path.stroke()
def frame_arc(self, center, radius, start_angle, arc_angle):
r = radius - 0.5 * self._pensize
ns_path = self._ns_arc_path(center, r, start_angle, arc_angle)
self._pencolor._ns_color.set()
ns_path.stroke()
def translate(self, dx, dy):
matrix = NSAffineTransform.transform()
matrix.translateXBy_yBy_(dx, dy)
self._ns_path.transformUsingAffineTransform_(matrix)
self.transformstack[-1].append(["translate",dx,dy])
def rotate(self, rotation):
matrix = NSAffineTransform.transform()
matrix.rotateByDegrees_(rotation)
self._ns_path.transformUsingAffineTransform_(matrix)
self.transformstack[-1].append(["rotate",rotation])
def scale(self, sx, sy):
matrix = NSAffineTransform.transform()
matrix.scaleXBy_yBy_(sx, sy)
self._ns_path.transformUsingAffineTransform_(matrix)
self.transformstack[-1].append(["scale",sx,sy])
def _transform(self, x, y):
for i in self.transformstack: #reversed(self.transformstack):
for j in i: # reversed(i):
if j[0]=="translate":
x = x+j[1]
y = y+j[2]
elif j[0]=="rotate":
x = x*math.cos(j[1])-y*math.sin(j[1])
y = x*math.sin(j[1])+y*math.cos(j[1])
elif j[0]=="scale":
x = x*j[1]
y = y*j[2]
return x, y
def _ns_rect((l, t, r, b)):
return NSMakeRect(l, t, r - l, b - t)
export(Canvas)