Changeset 1611
- Timestamp:
- 07/10/07 16:07:16 (1 year ago)
- Files:
-
- gaphas/branches/hw/demo.py (modified) (2 diffs)
- gaphas/branches/hw/gaphas/canvas.py (modified) (15 diffs)
- gaphas/branches/hw/gaphas/examples.py (modified) (6 diffs)
- gaphas/branches/hw/gaphas/item.py (modified) (3 diffs)
- gaphas/branches/hw/gaphas/painter.py (modified) (4 diffs)
- gaphas/branches/hw/gaphas/tool.py (modified) (7 diffs)
- gaphas/branches/hw/gaphas/view.py (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
gaphas/branches/hw/demo.py
r1597 r1611 321 321 322 322 # # AJM: extra boxes: 323 #bb=Box()324 #print 'box', bb325 #bb.matrix.rotate(math.pi/4.)326 #c.add(bb, parent=b)323 bb=Box() 324 print 'box', bb 325 bb.matrix.rotate(math.pi/4.) 326 c.add(bb, parent=b) 327 327 # for i in xrange(1): 328 328 # bb=Box() … … 336 336 337 337 l=MyLine() 338 h = l.handles()339 x, y = h[0].pos340 h[0].x = 30341 h[0].y = 30342 h[1].x += h[0].x + 30343 h[1].y += h[0].y + 30344 338 l.split_segment(0, 3) 345 339 l.matrix.translate(30, 30) gaphas/branches/hw/gaphas/canvas.py
r1597 r1611 7 7 # $HeadURL$ 8 8 9 from weakref import WeakKeyDictionary 9 10 10 11 import cairo … … 12 13 from gaphas import tree 13 14 from gaphas import solver 14 from gaphas.decorators import async, PRIORITY_HIGH_IDLE15 from gaphas.decorators import nonrecursive, async, PRIORITY_HIGH_IDLE 15 16 from state import observed, reversible_method, reversible_pair 16 17 … … 36 37 37 38 39 class ViewBucket(object): 40 __slots__ = ('matrix_v2i', 'matrix_i2v', 'handles') 41 def __init__(self): 42 self.matrix_v2i = None 43 self.matrix_i2v = None 44 self.handles = [] 45 46 47 class CanvasBucket(object): 48 __slots__ = ('matrix_c2i', 'matrix_i2c', 'view', 'handles') 49 def __init__(self): 50 self.matrix_c2i = None 51 self.matrix_i2c = None 52 self.view = {} 53 self.handles = [] 54 55 56 38 57 class Canvas(object): 39 58 """ 40 59 Container class for Items. 60 61 Attributes: 62 - _cache: additional cache of item data 41 63 """ 42 64 … … 46 68 self._dirty_items = set() 47 69 self._dirty_matrix_items = set() 48 self._in_update = False49 70 50 71 self._registered_views = set() 72 self._cache = WeakKeyDictionary() 51 73 52 74 solver = property(lambda s: s._solver) … … 69 91 item.canvas = self 70 92 self._tree.add(item, parent) 93 94 self._cache[item] = CanvasBucket() 95 for v in self._registered_views: 96 self._cache[item].view[v] = ViewBucket() 97 v.update_matrix(item) 98 71 99 self.request_update(item) 72 100 self._update_views((item,)) 101 73 102 74 103 @observed … … 259 288 return connected_items 260 289 261 def get_matrix_i2 w(self, item, calculate=False):290 def get_matrix_i2c(self, item, calculate=False): 262 291 """ 263 292 Get the Item to World matrix for @item. … … 269 298 yet. Note that out-of-date matrices are not recalculated. 270 299 """ 271 try: 272 return item._canvas_matrix_i2w 273 except AttributeError, e: 274 if calculate: 275 self.update_matrix(item, recursive=False) 276 return item._canvas_matrix_i2w 277 else: 278 self.request_matrix_update(item) 279 raise e 280 281 def get_matrix_w2i(self, item, calculate=False): 300 data = self._cache[item] 301 if data.matrix_i2c is None or calculate: 302 self.update_matrix(item, recursive=False) 303 return data.matrix_i2c 304 305 306 def get_matrix_c2i(self, item, calculate=False): 282 307 """ 283 308 Get the World to Item matrix for @item. 284 309 See get_matrix_i2w(). 285 310 """ 286 try: 287 return item._canvas_matrix_w2i 288 except AttributeError, e: 289 if calculate: 290 self.update_matrix(item, recursive=False) 291 return item._canvas_matrix_w2i 292 else: 293 self.request_matrix_update(item) 294 raise e 311 data = self._cache[item] 312 if data.matrix_c2i is None or calculate: 313 self.update_matrix(item, recursive=False) 314 return data.matrix_c2i 315 295 316 296 317 @observed … … 357 378 update job is scheduled as idle job. 358 379 """ 359 if not self._in_update:360 self.update_now() 361 380 self.update_now() 381 382 @nonrecursive 362 383 def update_now(self): 363 384 """ 364 385 Peform an update of the items that requested an update. 365 386 """ 366 self._in_update = True367 368 387 # Order the dirty items, so they are updated bottom to top 369 388 dirty_items = [ item for item in reversed(self._tree.nodes) \ … … 395 414 dirty_matrix_items.update(self._dirty_matrix_items) 396 415 self.update_matrices() 416 417 # Also need to set up the dirty_items list here, since items 418 # may be marked as dirty during maxtrix update or solving. 419 dirty_items = [ item for item in reversed(self._tree.nodes) \ 420 if item in self._dirty_items ] 397 421 398 422 for item in dirty_items: … … 411 435 self._update_views(self._dirty_items, dirty_matrix_items) 412 436 self._dirty_items.clear() 413 self._in_update = False414 437 415 438 def update_matrices(self): … … 450 473 self._dirty_matrix_items.discard(item) 451 474 475 data = self._cache[item] 452 476 if parent: 453 477 if parent in self._dirty_matrix_items: … … 456 480 return 457 481 else: 458 item._canvas_matrix_i2w= Matrix(*item.matrix)459 item._canvas_matrix_i2w *= parent._canvas_matrix_i2w482 data.matrix_i2c = Matrix(*item.matrix) 483 data.matrix_i2c *= self.get_matrix_i2c(parent) 460 484 else: 461 item._canvas_matrix_i2w= Matrix(*item.matrix)485 data.matrix_i2c = Matrix(*item.matrix) 462 486 463 487 # It's nice to have the W2I matrix present too: 464 item._canvas_matrix_w2i = Matrix(*item._canvas_matrix_i2w) 465 item._canvas_matrix_w2i.invert() 488 data.matrix_c2i = Matrix(*data.matrix_i2c) 489 data.matrix_c2i.invert() 490 for v in self._registered_views: 491 v.update_matrix(item) 466 492 467 493 # Make sure handles are marked (for constraint solving) … … 471 497 request_resolve(h.y) 472 498 473 474 499 if recursive: 475 500 for child in self._tree.get_children(item): 476 501 self.update_matrix(child) 502 477 503 478 504 def _update_handles(self, item): … … 515 541 item.matrix.translate(0, y) 516 542 for h in handles: 517 h.y -= y 543 h.y._value -= y 544 518 545 519 546 def register_view(self, view): … … 523 550 """ 524 551 self._registered_views.add(view) 552 for item in self.get_all_items(): 553 data = ViewBucket() 554 self._cache[item].view[view] = data 555 view.update_matrix(item) 556 525 557 526 558 def unregister_view(self, view): gaphas/branches/hw/gaphas/examples.py
r1557 r1611 27 27 #print 'Box.draw', self 28 28 c = context.cairo 29 nw = self._handles[NW]30 29 c.rectangle(0, 0, self.width, self.height) 31 30 if context.hovered: … … 90 89 def _set_height(self, height): 91 90 h1, h2 = self._handles 92 h2.y = h 1.y + height91 h2.y = height 93 92 94 93 95 94 def _get_height(self): 96 95 h1, h2 = self._handles 97 return h2.y - h1.y96 return h2.y 98 97 99 98 … … 115 114 cr = context.cairo 116 115 cr.set_line_width(10) 116 h1, h2 = self.handles() 117 117 cr.move_to(0, 0) 118 118 cr.line_to(0, self.height) … … 129 129 def _set_radius(self, r): 130 130 h1, h2 = self._handles 131 h2.x = h1.x +r132 h2.y = h1.y +r131 h2.x = r 132 h2.y = r 133 133 134 134 … … 137 137 return ((h2.x - h1.x) ** 2 + (h2.y - h1.y) ** 2) ** 0.5 138 138 139 140 139 radius = property(_get_radius, _set_radius) 141 142 143 def _get_pos(self):144 h1, h2 = self._handles145 x, y = h2.x - h1.x, h2.y - h1.y146 if x > 0:147 x = 0148 if y > 0:149 y = 0150 return abs(x), abs(y)151 152 153 pos = property(_get_pos)154 140 155 141 … … 162 148 def draw(self, context): 163 149 cr = context.cairo 164 x, y = self.pos 165 path_ellipse(cr, x, y, 2 * self.radius, 2 * self.radius) 150 path_ellipse(cr, 0, 0, 2 * self.radius, 2 * self.radius) 166 151 cr.stroke() 167 152 gaphas/branches/hw/gaphas/item.py
r1597 r1611 358 358 self._constraints = [ 359 359 add(eq(a=h_nw.y, b=h_ne.y)), 360 add(eq(a=h_sw.y, b=h_se.y)),361 360 add(eq(a=h_nw.x, b=h_sw.x)), 362 add(eq(a=h_ne.x, b=h_se.x)), 361 add(eq(a=h_se.y, b=h_sw.y)), 362 add(eq(a=h_se.x, b=h_ne.x)), 363 363 # set h_nw < h_se and h_sw < h_ne constraints 364 364 # with minimal size functionality 365 add(lt(smaller=h_nw. x, bigger=h_se.x, delta=10)),366 add(lt(smaller=h_nw.y, bigger=h_se.y, delta=10)),367 add(lt(smaller=h_ sw.x, bigger=h_ne.x, delta=10)),368 add(lt(smaller=h_ne.y, bigger=h_sw.y, delta=10)),365 add(lt(smaller=h_nw.y, bigger=h_se.y, delta=30)), 366 # add(lt(smaller=h_ne.y, bigger=h_sw.y, delta=30)), 367 add(lt(smaller=h_nw.x, bigger=h_se.x, delta=30)), 368 # add(lt(smaller=h_sw.x, bigger=h_ne.x, delta=30)), 369 369 ] 370 370 … … 658 658 See Item.draw(context). 659 659 """ 660 m = self._canvas.get_matrix_w2i(self)661 662 660 def draw_line_end(handle, angle, draw): 663 661 cr = context.cairo 664 662 cr.save() 665 663 try: 666 cr.translate( *m.transform_point(handle.x, handle.y))664 cr.translate(handle.x, handle.y) 667 665 cr.rotate(angle) 668 666 draw(context) … … 674 672 draw_line_end(self._handles[0], self._head_angle, self.draw_head) 675 673 h = self._handles[0] 676 cr.move_to( *m.transform_point(h.x, h.y))674 cr.move_to(h.x, h.y) 677 675 for h in self._handles[1:]: 678 cr.line_to( *m.transform_point(h.x, h.y))676 cr.line_to(h.x, h.y) 679 677 h0, h1 = self._handles[-2:] 680 678 draw_line_end(self._handles[-1], self._tail_angle, self.draw_tail) gaphas/branches/hw/gaphas/painter.py
r1597 r1611 95 95 try: 96 96 cairo.set_matrix(view.matrix) 97 cairo.transform(view.canvas.get_matrix_i2 w(item))97 cairo.transform(view.canvas.get_matrix_i2c(item)) 98 98 99 99 item.draw(DrawContext(painter=self, … … 248 248 draw_all = True 249 249 250 def _draw_handles(self, item, view, cairo):251 """252 Update the bounding box with handle's position.253 """254 cairo.save()255 try:256 m = Matrix(*view.canvas.get_matrix_i2w(item))257 m *= view._matrix258 259 for h in item.handles():260 cairo.identity_matrix()261 cairo.translate(*m.transform_point(h.x, h.y))262 cairo.translate(h.x, h.y)263 cairo.rectangle(-5, -5, 9, 9)264 cairo.fill()265 finally:266 cairo.restore()267 268 269 250 def _draw_item(self, item, view, cairo, area=None): 270 251 cairo = CairoBoundingBoxContext(cairo) 271 252 super(BoundingBoxPainter, self)._draw_item(item, view, cairo) 272 253 bounds = cairo.get_bounds() 273 254 274 255 # Update bounding box with handles. 275 #transform_i2c = (view.canvas.get_matrix_i2w(item) * view._matrix).transform_point276 transform_i2c = view.canvas.get_matrix_i2w(item).transform_point256 i2v = view.get_matrix_i2v(item).transform_point 257 i2v = (view.canvas.get_matrix_i2c(item) * view._matrix).transform_point 277 258 for h in item.handles(): 278 cx, cy = transform_i2c(h.x, h.y)259 cx, cy = i2v(h.x, h.y) 279 260 bounds += (cx - 5, cy - 5, 9, 9) 280 261 … … 309 290 """ 310 291 cairo.save() 311 m = Matrix(*view.canvas.get_matrix_i2w(item)) 312 m *= view._matrix 292 i2v = view.get_matrix_i2v(item) 313 293 if not opacity: 314 294 opacity = (item is view.focused_item) and .7 or .4 … … 328 308 cairo.identity_matrix() 329 309 cairo.set_antialias(ANTIALIAS_NONE) 330 cairo.translate( h.x, h.y)310 cairo.translate(*i2v.transform_point(h.x, h.y)) 331 311 cairo.rectangle(-4, -4, 8, 8) 332 312 cairo.set_source_rgba(r, g, b, opacity) gaphas/branches/hw/gaphas/tool.py
r1597 r1611 330 330 331 331 # Calculate the distance the item has to be moved 332 dx, dy = view.transform_distance_c2w(event.x - self.last_x, 333 event.y - self.last_y) 334 335 get_matrix_w2i = canvas.get_matrix_w2i 332 dx, dy = event.x - self.last_x, event.y - self.last_y 336 333 337 334 # Now do the actual moving. 338 335 for i in self._movable_items: 339 336 # Move the item and schedule it for an update 340 i.matrix.translate(*get_matrix_w2i(i).transform_distance(dx, dy)) 341 for h in i.handles(): 342 h.x += dx 343 h.y += dy 337 v2i = view.get_matrix_v2i(i) 338 i.matrix.translate(*v2i.transform_distance(dx, dy)) 344 339 canvas.request_matrix_update(i) 345 340 … … 377 372 Find item's handle at (event.x, event.y) 378 373 """ 379 transform_point = view.canvas.get_matrix_i2w(item).transform_point 380 e_x, e_y = view.transform_point_c2w(event.x, event.y) 381 dx, dy = view.transform_distance_c2w(6, 6) 374 i2v = view.get_matrix_i2v(item).transform_point 375 x, y = event.x, event.y 382 376 for h in item.handles(): 383 377 if not h.movable: 384 378 continue 385 #wx, wy = transform_point(h.x, h.y) 386 wx, wy = h.x, h.y 387 if -dx < (wx - e_x) < dx and -dy < (wy - e_y) < dy: 379 wx, wy = i2v(h.x, h.y) 380 if -6 < (wx - x) < 6 and -6 < (wy - y) < 6: 388 381 return h 389 382 return None … … 432 425 def move(self, view, item, handle, x, y): 433 426 """ 434 Move the handle to position (x,y). If handle changed the position 435 of an item, then move the item, too. 436 """ 437 handle.x += x 438 handle.y += y 439 440 # calculate current position 441 matrix_w2i = view.canvas.get_matrix_w2i(item) 442 x1 = min(h.x for h in item.handles()) 443 y1 = min(h.y for h in item.handles()) 444 dx, dy = matrix_w2i.transform_point(x1, y1) 445 item.matrix.translate(dx, dy) 446 427 Move the handle to position (x,y). 428 """ 429 handle.x = x 430 handle.y = y 447 431 448 432 … … 472 456 item, handle = self.find_handle(view, event) 473 457 if handle: 474 self.last_x, self.last_y = event.x, event.y475 458 # Deselect all items unless CTRL or SHIFT is pressed 476 459 # or the item is already selected. … … 525 508 view.queue_draw_item(item) 526 509 527 dx, dy = view.transform_distance_c2w(event.x - self.last_x, 528 event.y - self.last_y) 529 wx, wy = view.transform_point_c2w(event.x, event.y) 510 v2i = view.get_matrix_v2i(item) 511 x, y = v2i.transform_point(event.x, event.y) 530 512 531 513 # Do the actual move: 532 self.move(view, item, handle, dx, dy)514 self.move(view, item, handle, x, y) 533 515 534 516 item.request_update() … … 536 518 try: 537 519 if self._grabbed_handle.connectable: 538 self.glue(view, item, handle, wx, wy)520 self.glue(view, item, handle, x, y) 539 521 finally: 540 522 pass 541 self.last_x, self.last_y = event.x, event.y542 523 return True 543 524 else: … … 620 601 x, y = view.transform_point_c2w(x, y) 621 602 item.matrix.translate(x, y) 622 h = item.handles()[0]623 h.x = x624 h.y = y625 603 return item 626 604 gaphas/branches/hw/gaphas/view.py
r1597 r1611 199 199 200 200 if point in self.get_item_bounding_box(item): 201 inverse = Matrix(*self._matrix) 202 inverse.invert() 203 wx, wy = inverse.transform_point(x, y) 204 ix, iy = self._canvas.get_matrix_w2i(item).transform_point(wx, wy) 201 v2i = self.get_matrix_v2i(item) 202 ix, iy = v2i.transform_point(x, y) 205 203 if item.point(ix, iy) < 0.5: 206 204 return item … … 224 222 # Make sure everything's updated 225 223 self.request_update(self._canvas.get_all_items()) 224 for i in self._canvas.get_all_items(): 225 self.update_matrix(i) 226 226 227 227 def set_item_bounding_box(self, item, bounds): … … 236 236 # bounding boxes are calculated. Now, the child objects should not 237 237 # be hindered by their own matrix settings. 238 ix0, iy0 = self.transform_point_c2i(item, bounds.x, bounds.y) 239 ix1, iy1 = self.transform_point_c2i(item, bounds.x1, bounds.y1) 238 c2i = self._canvas.get_matrix_c2i(item).transform_point 239 ix0, iy0 = c2i(bounds.x, bounds.y) 240 ix1, iy1 = c2i(bounds.x1, bounds.y1) 240 241 self._item_bounds[item] = bounds, Rectangle(ix0, iy0, x1=ix1, y1=iy1) 241 242 … … 260 261 inverse = Matrix(*self._matrix) 261 262 inverse.invert() 262 ww, wh = self.transform_point_c2w(self._bounds.x1, self._bounds.y1)263 ww, wh = inverse.transform_point(self._bounds.x1, self._bounds.y1) 263 264 return self._matrix.transform_distance(ww, wh) 264 265 … … 289 290 area=None)) 290 291 291 def transform_distance_c2w(self, x, y): 292 """ 293 Transform a point from canvas to world coordinates. 294 """ 295 inverse = Matrix(*self._matrix) 296 inverse.invert() 297 return inverse.transform_distance(x, y) 298 299 def transform_distance_w2c(self, x, y): 300 """ 301 Transform a distance from world to canvas coordinates. 302 """ 303 return self._matrix.transform_distance(x, y) 304 305 def transform_point_c2w(self, x, y): 306 """ 307 Transform a distance from canvas to world coordinates. 308 """ 309 inverse = Matrix(*self._matrix) 310 inverse.invert() 311 return inverse.transform_point(x, y) 312 313 def transform_point_w2c(self, x, y): 314 """ 315 Transform a point from world to canvas coordinates. 316 """ 317 return self._matrix.transform_point(x, y) 318 319 def transform_point_c2i(self, item, x, y): 320 """ 321 Transforma point from canvas to item coordinates. 322 """ 323 assert self.canvas 324 wx, wy = self.transform_point_c2w(x, y) 325 return self._canvas.get_matrix_w2i(item).transform_point(wx, wy) 326 327 def transform_point_i2c(self, item, x, y): 328 """ 329 Transform a point from item coordinates to canvas coordinates. 330 """ 331 assert self.canvas 332 wx, wy = self._canvas.get_matrix_i2w(item).transform_point(x, y) 333 return self.transform_point_w2c(wx, wy) 292 def get_matrix_i2v(self, item): 293 return self._canvas._cache[item].view[self].matrix_i2v 294 295 def get_matrix_v2i(self, item): 296 return self._canvas._cache[item].view[self].matrix_v2i 297 298 299 def update_matrix(self, item): 300 """ 301 Update item matrices related to view. 302 """ 303 v = self._canvas._cache[item].view[self] 304 v.matrix_i2v = self._canvas.get_matrix_i2c(item) * self._matrix 305 v.matrix_v2i = Matrix(*v.matrix_i2v) 306 v.matrix_v2i.invert() 307 334 308 335 309 … … 562 536 else: 563 537 self.queue_draw_item(i) 564 x0, y0 = self.transform_point_i2c(i, bounds.x, bounds.y) 565 x1, y1 = self.transform_point_i2c(i, bounds.x1, bounds.y1) 538 539 i2c = self._canvas.get_matrix_i2c(i).transform_point 540 x0, y0 = i2c(bounds.x, bounds.y) 541 x1, y1 = i2c(bounds.x1, bounds.y1) 566 542 cbounds = Rectangle(x0, y0, x1=x1, y1=y1) 567 543 self._item_bounds[i] = cbounds, bounds
