def glue(self, view, item, handle, wx, wy): """ It allows the tool to glue to a Box or (other) Line item. The distance from the item to the handle is determined in canvas coordinates, using a 10 pixel glue distance. """ if not handle.connectable: return # Make glue distance depend on the zoom ratio (should be about 10 pixels) inverse = Matrix(*view.matrix) inverse.invert() #glue_distance, dummy = inverse.transform_distance(10, 0) glue_distance = 10 glue_point = None glue_item = None for i in view.canvas.get_all_items(): if not i is item: v2i = view.get_matrix_v2i(i).transform_point ix, iy = v2i(wx, wy) try: distance, point = i.glue(item, handle, ix, iy) # Transform distance to world coordinates #distance, dumy = matrix_i2w(i).transform_distance(distance, 0) if distance <= glue_distance: glue_distance = distance i2v = view.get_matrix_i2v(i).transform_point glue_point = i2v(*point) glue_item = i except AttributeError: pass if glue_point: v2i = view.get_matrix_v2i(item).transform_point handle.x, handle.y = v2i(*glue_point) return glue_item
def __init__(self, w=10, h=10, path=''): self._w, self._h = w, h surface = ImageSurface.create_from_png(path) self._imgpat = SurfacePattern(surface) self._imgpat.set_filter(FILTER_BEST) scaler = Matrix() scaler.scale(surface.get_width() / w, surface.get_height() / h) self._imgpat.set_matrix(scaler)
def update_matrix(self, item): """ Update item matrices related to view. """ try: i2v = item._matrix_i2c.multiply(self._matrix) except AttributeError: # Fall back to old behaviour i2v = item._matrix_i2c * self._matrix item._matrix_i2v[self] = i2v v2i = Matrix(*i2v) v2i.invert() item._matrix_v2i[self] = v2i
def on_adjustment_changed(self, adj): """ Change the transformation matrix of the view to reflect the value of the x/y adjustment (scrollbar). """ if adj.value == 0.0: return # Can not use self._matrix.translate( - adj.value , 0) here, since # the translate method effectively does a m * self._matrix, which # will result in the translation being multiplied by the orig. matrix m = Matrix() if adj is self._hadjustment: m.translate( - adj.value, 0) elif adj is self._vadjustment: m.translate(0, - adj.value) self._matrix *= m # Force recalculation of the bounding boxes: self.request_update((), self._canvas.get_all_items()) self.queue_draw_refresh()
def paint(self, context): view = self.view item = view.hovered_item if item and item is view.focused_item: cr = context.cairo h = item.handles() for h1, h2 in zip(h[:-1], h[1:]): p1, p2 = h1.pos, h2.pos cx = (p1.x + p2.x) / 2 cy = (p1.y + p2.y) / 2 cr.save() cr.identity_matrix() m = Matrix(*view.get_matrix_i2v(item)) cr.set_antialias(ANTIALIAS_NONE) cr.translate(*m.transform_point(cx, cy)) cr.rectangle(-3, -3, 6, 6) cr.set_source_rgba(0, 0.5, 0, .4) cr.fill_preserve() cr.set_source_rgba(.25, .25, .25, .6) cr.set_line_width(1) cr.stroke() cr.restore()
def __init__(self, canvas=None): self._matrix = Matrix() self._painter = DefaultPainter(self) self._bounding_box_painter = BoundingBoxPainter(self) # Handling selections. # TODO: Move this to a context? self._selected_items = set() self._focused_item = None self._hovered_item = None self._dropzone_item = None self._qtree = Quadtree() self._bounds = Rectangle(0, 0, 0, 0) self._canvas = None if canvas: self._set_canvas(canvas)
y0 = H//2 + (random.uniform()-.1)*50 for dx,dy in spiral(): c = .25+.75*random.random() x = int(x0+dx) y = int(y0+dy) checked = False I.flush() if not (x <= w//2 or y <= h//2 or x >= (W-w//2) or y >= (H-h//2)): ndI = ndarray(shape=(h,w), buffer=I.get_data(), dtype=ubyte, order='C', offset=(x-w//2) + I.get_stride() * (y-h//2), strides=[I.get_stride(), 1]) ndL = ndarray(shape=(h,w), buffer=L.get_data(), dtype=ubyte, order='C', strides=[L.get_stride(), 1]) if ((ndI * ndL).sum() == 0): checked = True new_region = RectangleInt(x-w//2, y-h//2, w, h) if (checked or ( drawn_regions.contains_rectangle(new_region) == REGION_OVERLAP_OUT )): ctxI.set_source_surface(L, 0, 0) pattern = ctxI.get_source() scalematrix = Matrix() scalematrix.scale(1.0,1.0) scalematrix.translate(w//2 - x, h//2 - y) pattern.set_matrix(scalematrix) ctxI.set_source_rgba(c,c,c,c) ctxI.mask(pattern) drawn_regions.union(new_region) break I.flush() I.write_to_png("wordle-cairo.png") Image.open("wordle-cairo.png").show()
class View(object): """ View class for gaphas.Canvas objects. """ def __init__(self, canvas=None): self._matrix = Matrix() self._painter = DefaultPainter(self) self._bounding_box_painter = BoundingBoxPainter(self) # Handling selections. ### TODO: Move this to a context? self._selected_items = set() self._focused_item = None self._hovered_item = None self._dropzone_item = None ###/ self._qtree = Quadtree() self._bounds = Rectangle(0, 0, 0, 0) self._canvas = None if canvas: self._set_canvas(canvas) matrix = property(lambda s: s._matrix, doc="Canvas to view transformation matrix") def _set_canvas(self, canvas): """ Use view.canvas = my_canvas to set the canvas to be rendered in the view. """ if self._canvas: self._qtree.clear() self._selected_items.clear() self._focused_item = None self._hovered_item = None self._dropzone_item = None self._canvas = canvas canvas = property(lambda s: s._canvas, _set_canvas) def emit(self, *args, **kwargs): """ Placeholder method for signal emission functionality. """ pass def queue_draw_item(self, *items): """ Placeholder for item redraw queueing. """ pass def select_item(self, item): """ Select an item. This adds @item to the set of selected items. """ self.queue_draw_item(item) if item not in self._selected_items: self._selected_items.add(item) self.emit('selection-changed', self._selected_items) def unselect_item(self, item): """ Unselect an item. """ self.queue_draw_item(item) if item in self._selected_items: self._selected_items.discard(item) self.emit('selection-changed', self._selected_items) def select_all(self): for item in self.canvas.get_all_items(): self.select_item(item) def unselect_all(self): """ Clearing the selected_item also clears the focused_item. """ self.queue_draw_item(*self._selected_items) self._selected_items.clear() self.focused_item = None self.emit('selection-changed', self._selected_items) selected_items = property(lambda s: s._selected_items, select_item, unselect_all, "Items selected by the view") def _set_focused_item(self, item): """ Set the focused item, this item is also added to the selected_items set. """ if not item is self._focused_item: self.queue_draw_item(self._focused_item, item) if item: self.select_item(item) if item is not self._focused_item: self._focused_item = item self.emit('focus-changed', item) def _del_focused_item(self): """ Items that loose focus remain selected. """ self._set_focused_item(None) focused_item = property(lambda s: s._focused_item, _set_focused_item, _del_focused_item, "The item with focus (receives key events a.o.)") def _set_hovered_item(self, item): """ Set the hovered item. """ if item is not self._hovered_item: self.queue_draw_item(self._hovered_item, item) self._hovered_item = item self.emit('hover-changed', item) def _del_hovered_item(self): """ Unset the hovered item. """ self._set_hovered_item(None) hovered_item = property(lambda s: s._hovered_item, _set_hovered_item, _del_hovered_item, "The item directly under the mouse pointer") def _set_dropzone_item(self, item): """ Set dropzone item. """ if item is not self._dropzone_item: self.queue_draw_item(self._dropzone_item, item) self._dropzone_item = item self.emit('dropzone-changed', item) def _del_dropzone_item(self): """ Unset dropzone item. """ self._set_dropzone_item(None) dropzone_item = property(lambda s: s._dropzone_item, _set_dropzone_item, _del_dropzone_item, 'The item which can group other items') def _set_painter(self, painter): """ Set the painter to use. Painters should implement painter.Painter. """ self._painter = painter painter.set_view(self) self.emit('painter-changed') painter = property(lambda s: s._painter, _set_painter) def _set_bounding_box_painter(self, painter): """ Set the painter to use for bounding box calculations. """ self._bounding_box_painter = painter painter.set_view(self) self.emit('painter-changed') bounding_box_painter = property(lambda s: s._bounding_box_painter, _set_bounding_box_painter) def get_item_at_point(self, pos, selected=True): """ Return the topmost item located at ``pos`` (x, y). Parameters: - selected: if False returns first non-selected item """ items = self._qtree.find_intersect((pos[0], pos[1], 1, 1)) for item in self._canvas.sort(items, reverse=True): if not selected and item in self.selected_items: continue # skip selected items v2i = self.get_matrix_v2i(item) ix, iy = v2i.transform_point(*pos) if item.point((ix, iy)) < 0.5: return item return None def get_handle_at_point(self, pos, distance=6): """ Look for a handle at ``pos`` and return the tuple (item, handle). """ def find(item): """ Find item's handle at pos """ v2i = self.get_matrix_v2i(item) d = v2i.transform_distance(distance, 0)[0] x, y = v2i.transform_point(*pos) for h in item.handles(): if not h.movable: continue hx, hy = h.pos if -d < (hx - x) < d and -d < (hy - y) < d: return h # The focused item is the prefered item for handle grabbing if self.focused_item: h = find(self.focused_item) if h: return self.focused_item, h # then try hovered item if self.hovered_item: h = find(self.hovered_item) if h: return self.hovered_item, h # Last try all items, checking the bounding box first x, y = pos items = self.get_items_in_rectangle((x - distance, y - distance, distance * 2, distance * 2), reverse=True) found_item, found_h = None, None for item in items: h = find(item) if h: return item, h return None, None def get_port_at_point(self, vpos, distance=10, exclude=None): """ Find item with port closest to specified position. List of items to be ignored can be specified with `exclude` parameter. Tuple is returned - found item - closest, connectable port - closest point on found port (in view coordinates) :Parameters: vpos Position specified in view coordinates. distance Max distance from point to a port (default 10) exclude Set of items to ignore. """ v2i = self.get_matrix_v2i vx, vy = vpos max_dist = distance port = None glue_pos = None item = None rect = (vx - distance, vy - distance, distance * 2, distance * 2) items = self.get_items_in_rectangle(rect, reverse=True) for i in items: if i in exclude: continue for p in i.ports(): if not p.connectable: continue ix, iy = v2i(i).transform_point(vx, vy) pg, d = p.glue((ix, iy)) if d >= max_dist: continue item = i port = p # transform coordinates from connectable item space to view # space i2v = self.get_matrix_i2v(i).transform_point glue_pos = i2v(*pg) return item, port, glue_pos def get_items_in_rectangle(self, rect, intersect=True, reverse=False): """ Return the items in the rectangle 'rect'. Items are automatically sorted in canvas' processing order. """ if intersect: items = self._qtree.find_intersect(rect) else: items = self._qtree.find_inside(rect) return self._canvas.sort(items, reverse=reverse) def select_in_rectangle(self, rect): """ Select all items who have their bounding box within the rectangle @rect. """ items = self._qtree.find_inside(rect) map(self.select_item, items) def zoom(self, factor): """ Zoom in/out by factor @factor. """ # TODO: should the scale factor be clipped? self._matrix.scale(factor, factor) # Make sure everything's updated #map(self.update_matrix, self._canvas.get_all_items()) self.request_update((), self._canvas.get_all_items()) def set_item_bounding_box(self, item, bounds): """ Update the bounding box of the item. ``bounds`` is in view coordinates. Coordinates are calculated back to item coordinates, so matrix-only updates can occur. """ v2i = self.get_matrix_v2i(item).transform_point ix0, iy0 = v2i(bounds.x, bounds.y) ix1, iy1 = v2i(bounds.x1, bounds.y1) self._qtree.add(item=item, bounds=bounds, data=Rectangle(ix0, iy0, x1=ix1, y1=iy1)) def get_item_bounding_box(self, item): """ Get the bounding box for the item, in view coordinates. """ return self._qtree.get_bounds(item) bounding_box = property(lambda s: s._bounds) def update_bounding_box(self, cr, items=None): """ Update the bounding boxes of the canvas items for this view, in canvas coordinates. """ painter = self._bounding_box_painter if items is None: items = self.canvas.get_all_items() # The painter calls set_item_bounding_box() for each rendered item. painter.paint(Context(cairo=cr, items=items, area=None)) # Update the view's bounding box with the rest of the items self._bounds = Rectangle(*self._qtree.soft_bounds) def paint(self, cr): self._painter.paint(Context(cairo=cr, items=self.canvas.get_all_items(), area=None)) def get_matrix_i2v(self, item): """ Get Item to View matrix for ``item``. """ if self not in item._matrix_i2v: self.update_matrix(item) return item._matrix_i2v[self] def get_matrix_v2i(self, item): """ Get View to Item matrix for ``item``. """ if self not in item._matrix_v2i: self.update_matrix(item) return item._matrix_v2i[self] def update_matrix(self, item): """ Update item matrices related to view. """ try: i2v = item._matrix_i2c.multiply(self._matrix) except AttributeError: # Fall back to old behaviour i2v = item._matrix_i2c * self._matrix item._matrix_i2v[self] = i2v v2i = Matrix(*i2v) v2i.invert() item._matrix_v2i[self] = v2i def _clear_matrices(self): """ Clear registered data in Item's _matrix{i2c|v2i} attributes. """ for item in self.canvas.get_all_items(): try: del item._matrix_i2v[self] del item._matrix_v2i[self] except KeyError: pass
dtype=uint32, order='C', strides=[I.get_stride(), 4]) except NotImplementedError: raise SystemExit("For python 3.x, you need pycairo >= 1.11+ (from https://github.com/pygobject/pycairo)") # 255 * 2**24 = opaque ndI[:,:] = 255 * 2**24 + ndR[:,:] * 2**16 + ndG[:,:] * 2**8 + ndB[:,:] I.mark_dirty() surface = ImageSurface(FORMAT_ARGB32, 800, 600) ctx = Context(surface) ctx.set_source_surface(I, 0, 0) pattern = ctx.get_source() SurfacePattern.set_filter(pattern, FILTER_BEST) scale = 480.0 / rows scalematrix = Matrix() scalematrix.scale(1.0/scale,1.0/scale) scalematrix.translate(-(400.0 - width *scale /2.0 )+200, -60) pattern.set_matrix(scalematrix) ctx.paint() # we need this later for shifting the taller LCD_V glyph up. rows_old = rows # LCD_V face.load_char('S', FT_LOAD_RENDER | FT_LOAD_TARGET_LCD_V ) bitmap = face.glyph.bitmap width = face.glyph.bitmap.width rows = face.glyph.bitmap.rows//3 pitch = face.glyph.bitmap.pitch
ctx = Context(surface) # fill background as gray ctx.rectangle(0,0,1200,500) ctx.set_source_rgb (0.5 , 0.5, 0.5) ctx.fill() # use the stroked font's size as scale, as it is likely slightly larger scale = 400.0 / rowsZ # draw bitmap first ctx.set_source_surface(F, 0, 0) patternF = ctx.get_source() SurfacePattern.set_filter(patternF, FILTER_BEST) scalematrix = Matrix() scalematrix.scale(1.0/scale,1.0/scale) scalematrix.translate(-(600.0 - widthF *scale /2.0 ), -50) patternF.set_matrix(scalematrix) ctx.set_source_rgb (1 , 1, 0) ctx.mask(patternF) ctx.fill() scalematrix.translate(+400,0) patternF.set_matrix(scalematrix) ctx.mask(patternF) ctx.fill() # stroke on top ctx.set_source_surface(Z, 0, 0) patternZ = ctx.get_source()
face.load_char('S', FT_LOAD_RENDER | FT_LOAD_TARGET_MONO) bitmap = face.glyph.bitmap width = face.glyph.bitmap.width rows = face.glyph.bitmap.rows pitch = face.glyph.bitmap.pitch glyph_surface = make_image_surface(face.glyph.bitmap) surface = ImageSurface(FORMAT_ARGB32, 800, 600) ctx = Context(surface) ctx.rectangle(0, 0, 800, 600) ctx.set_line_width(0) ctx.set_source_rgb(0.5, 0.5, 0.5) ctx.fill() # scale = 480.0 / rows ctx.set_source_surface(glyph_surface, 0, 0) pattern = ctx.get_source() SurfacePattern.set_filter(pattern, FILTER_BEST) scalematrix = Matrix() scalematrix.scale(1.0 / scale, 1.0 / scale) scalematrix.translate(-(400.0 - width * scale / 2.0), -60) pattern.set_matrix(scalematrix) ctx.set_source_rgb(0, 0, 1) ctx.mask(pattern) ctx.fill() surface.flush() surface.write_to_png("glyph-mono+alpha-cairo.png") Image.open("glyph-mono+alpha-cairo.png").show()
FT_LOAD_TARGET_MONO ) bitmap = face.glyph.bitmap width = face.glyph.bitmap.width rows = face.glyph.bitmap.rows pitch = face.glyph.bitmap.pitch glyph_surface = make_image_surface(face.glyph.bitmap) surface = ImageSurface(FORMAT_ARGB32, 800, 600) ctx = Context(surface) ctx.rectangle(0,0,800,600) ctx.set_line_width(0) ctx.set_source_rgb (0.5 , 0.5, 0.5) ctx.fill() # scale = 480.0 / rows ctx.set_source_surface(glyph_surface, 0, 0) pattern = ctx.get_source() SurfacePattern.set_filter(pattern, FILTER_BEST) scalematrix = Matrix() scalematrix.scale(1.0/scale,1.0/scale) scalematrix.translate(-(400.0 - width *scale /2.0 ), -60) pattern.set_matrix(scalematrix) ctx.set_source_rgb (0 , 0, 1) ctx.mask(pattern) ctx.fill() surface.flush() surface.write_to_png("glyph-mono+alpha-cairo.png") Image.open("glyph-mono+alpha-cairo.png").show()
ctx = Context(surface) # fill background as gray ctx.rectangle(0, 0, 1200, 500) ctx.set_source_rgb(0.5, 0.5, 0.5) ctx.fill() # use the stroked font's size as scale, as it is likely slightly larger scale = 400.0 / rowsZ # draw bitmap first ctx.set_source_surface(F, 0, 0) patternF = ctx.get_source() SurfacePattern.set_filter(patternF, FILTER_BEST) scalematrix = Matrix() scalematrix.scale(1.0 / scale, 1.0 / scale) scalematrix.translate(-(600.0 - widthF * scale / 2.0), -50) patternF.set_matrix(scalematrix) ctx.set_source_rgb(1, 1, 0) ctx.mask(patternF) ctx.fill() scalematrix.translate(+400, 0) patternF.set_matrix(scalematrix) ctx.mask(patternF) ctx.fill() # stroke on top ctx.set_source_surface(Z, 0, 0) patternZ = ctx.get_source()
def __init__(self, child, to=Matrix(), **kwargs): super(Transform, self).__init__(child, **kwargs) self.to = to
i += 2 elif (CODES[i] == CURVE4): ctx.curve_to(VERTS[i][0],VERTS[i][1], VERTS[i+1][0],VERTS[i+1][1], VERTS[i+2][0],VERTS[i+2][1]) i += 3 ctx.fill_preserve() ctx.set_source_rgb(0,0,0) ctx.set_line_width(6) ctx.stroke() ctx.restore() scale2 = (height_s - 2.0 * MARGIN)/rows ctx.set_source_surface(Z, 0, 0) pattern = ctx.get_source() SurfacePattern.set_filter(pattern, FILTER_BEST) scalematrix = Matrix() scalematrix.scale(1.0/scale2, 1.0/scale2) scalematrix.translate(-( width_s/2.0 - width *scale2 /2.0 ), -MARGIN) pattern.set_matrix(scalematrix) ctx.set_source_rgba (0, 0, 0, 0.7) ctx.mask(pattern) ctx.fill() surface.flush() surface.write_to_png("glyph-vector-2-cairo.png") surface.finish() Image.open("glyph-vector-2-cairo.png").show()