Commit 431bc08bd795287a22be1c1a97278d5c1d25a0a9
1 parent
bb8f5543
Exists in
master
and in
67 other branches
ENH: Removing the cairo requirement in clut_raycasting, now using wx.GraphicsContext
Showing
1 changed file
with
95 additions
and
117 deletions
Show diff stats
invesalius/gui/widgets/clut_raycasting.py
... | ... | @@ -22,27 +22,27 @@ import math |
22 | 22 | import os |
23 | 23 | import sys |
24 | 24 | |
25 | -import cairo | |
26 | 25 | import numpy |
27 | 26 | import wx |
28 | -import wx.lib.wxcairo | |
29 | 27 | import wx.lib.pubsub as ps |
30 | 28 | |
31 | 29 | import gui.dialogs as dialog |
32 | 30 | import constants as const |
33 | 31 | |
34 | 32 | FONT_COLOUR = (1, 1, 1) |
35 | -LINE_COLOUR = (0.5, 0.5, 0.5) | |
33 | +LINE_COLOUR = (128, 128, 128) | |
36 | 34 | LINE_WIDTH = 2 |
37 | 35 | HISTOGRAM_LINE_WIDTH = 1 |
38 | -HISTOGRAM_LINE_COLOUR = (0.5, 0.5, 0.5) | |
39 | -HISTOGRAM_FILL_COLOUR = (0.25, 0.25, 0.25) | |
40 | -BACKGROUND_TEXT_COLOUR_RGBA = (1, 0, 0, 0.5) | |
41 | -TEXT_COLOUR = (1, 1, 1) | |
42 | -GRADIENT_RGBA = 0.75 | |
36 | +HISTOGRAM_LINE_COLOUR = (128, 128, 128) | |
37 | +HISTOGRAM_FILL_COLOUR = (64, 64, 64) | |
38 | +BACKGROUND_TEXT_COLOUR_RGBA = (255, 0, 0, 128) | |
39 | +TEXT_COLOUR = (255, 255, 255) | |
40 | +GRADIENT_RGBA = 0.75 * 255 | |
43 | 41 | RADIUS = 5 |
44 | 42 | SELECTION_SIZE = 10 |
45 | 43 | TOOLBAR_SIZE = 30 |
44 | +TOOLBAR_COLOUR = (25 , 25, 25) | |
45 | +PADDING = 2 | |
46 | 46 | |
47 | 47 | class Node(object): |
48 | 48 | """ |
... | ... | @@ -483,154 +483,133 @@ class CLUTRaycastingWidget(wx.Panel): |
483 | 483 | #The gradient |
484 | 484 | height += self.padding |
485 | 485 | for curve in self.curves: |
486 | - first_node = curve.nodes[0] | |
487 | - last_node = curve.nodes[-1] | |
488 | - xini, yini = first_node.x, first_node.y | |
489 | - xend, yend = last_node.x, last_node.y | |
490 | - gradient = cairo.LinearGradient(xini, height, xend, height) | |
491 | - ctx.move_to(xini, yini) | |
492 | - for node in curve.nodes: | |
493 | - x, y = node.x, node.y | |
494 | - r, g, b = node.colour | |
495 | - ctx.line_to(x, y) | |
496 | - gradient.add_color_stop_rgba((x - xini) * 1.0 / (xend - xini), | |
497 | - r, g, b, GRADIENT_RGBA) | |
498 | - ctx.line_to(x, height) | |
499 | - ctx.line_to(xini, height) | |
500 | - ctx.close_path() | |
501 | - ctx.set_source(gradient) | |
502 | - ctx.fill() | |
486 | + for nodei, nodej in zip(curve.nodes[:-1], curve.nodes[1:]): | |
487 | + path = ctx.CreatePath() | |
488 | + path.MoveToPoint(int(nodei.x), height) | |
489 | + path.AddLineToPoint(int(nodei.x), height) | |
490 | + path.AddLineToPoint(int(nodei.x), nodei.y) | |
491 | + path.AddLineToPoint(int(nodej.x), nodej.y) | |
492 | + path.AddLineToPoint(int(nodej.x), height) | |
493 | + | |
494 | + colouri = nodei.colour[0],nodei.colour[1],nodei.colour[2], GRADIENT_RGBA | |
495 | + colourj = nodej.colour[0],nodej.colour[1],nodej.colour[2], GRADIENT_RGBA | |
496 | + b = ctx.CreateLinearGradientBrush(nodei.x, height, | |
497 | + nodej.x, height, | |
498 | + colouri, colourj) | |
499 | + ctx.SetBrush(b) | |
500 | + ctx.FillPath(path) | |
503 | 501 | |
504 | 502 | def _draw_curves(self, ctx): |
503 | + path = ctx.CreatePath() | |
504 | + ctx.SetPen(wx.Pen(LINE_COLOUR, LINE_WIDTH)) | |
505 | 505 | for curve in self.curves: |
506 | - ctx.move_to(curve.nodes[0].x, curve.nodes[0].y) | |
506 | + path.MoveToPoint(curve.nodes[0].x, curve.nodes[0].y) | |
507 | 507 | for node in curve.nodes: |
508 | - ctx.line_to(node.x, node.y) | |
509 | - ctx.set_source_rgb(*LINE_COLOUR) | |
510 | - ctx.stroke() | |
508 | + path.AddLineToPoint(node.x, node.y) | |
509 | + ctx.StrokePath(path) | |
511 | 510 | |
512 | 511 | def _draw_points(self, ctx): |
513 | 512 | for curve in self.curves: |
514 | 513 | for node in curve.nodes: |
515 | - ctx.arc(node.x, node.y, RADIUS, 0, math.pi * 2) | |
516 | - ctx.set_source_rgb(*node.colour) | |
517 | - ctx.fill_preserve() | |
518 | - ctx.set_source_rgb(*LINE_COLOUR) | |
519 | - ctx.stroke() | |
514 | + path = ctx.CreatePath() | |
515 | + ctx.SetPen(wx.Pen(LINE_COLOUR, LINE_WIDTH)) | |
516 | + ctx.SetBrush(wx.Brush(node.colour)) | |
517 | + path.AddCircle(node.x, node.y, RADIUS) | |
518 | + ctx.DrawPath(path) | |
520 | 519 | |
521 | 520 | def _draw_selected_point_text(self, ctx): |
522 | - ctx.select_font_face('Sans') | |
523 | - ctx.set_font_size(15) | |
524 | 521 | i,j = self.point_dragged |
525 | 522 | node = self.curves[i].nodes[j] |
526 | 523 | x,y = node.x, node.y |
527 | 524 | value = node.graylevel |
528 | 525 | alpha = node.opacity |
529 | - widget_width = self.GetVirtualSizeTuple()[0] | |
526 | + widget_width, widget_height = self.GetVirtualSizeTuple() | |
527 | + | |
528 | + font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) | |
529 | + font.SetWeight(wx.BOLD) | |
530 | + font = ctx.CreateFont(font, TEXT_COLOUR) | |
531 | + ctx.SetFont(font) | |
530 | 532 | |
531 | - # To better understand text in cairo, see: | |
532 | - # http://www.tortall.net/mu/wiki/CairoTutorial#understanding-text | |
533 | - if ctx.text_extents("Value %d" % value)[2] > \ | |
534 | - ctx.text_extents("Alpha: %.3f" % alpha)[2]: | |
535 | - text = "Value: %6d" % value | |
533 | + text1 = "Value: %6d" % value | |
534 | + text2 = "Alpha: %.3f" % alpha | |
535 | + | |
536 | + if ctx.GetTextExtent(text1)[0] > ctx.GetTextExtent(text2)[0]: | |
537 | + wt, ht = ctx.GetTextExtent(text1) | |
536 | 538 | else: |
537 | - text = "Alpha: %.3f" % alpha | |
539 | + wt, ht = ctx.GetTextExtent(text2) | |
538 | 540 | |
539 | - x_bearing, y_bearing, width, height, x_advance, y_advance\ | |
540 | - = ctx.text_extents(text) | |
541 | - fascent, fdescent, fheight, fxadvance, fyadvance = ctx.font_extents() | |
541 | + wr, hr = wt + 2 * PADDING, ht * 2 + 2 * PADDING | |
542 | + xr, yr = x + RADIUS, y - RADIUS - hr | |
542 | 543 | |
543 | - # The text box height is the double of text height plus 2, that is text | |
544 | - # box border | |
545 | - box_height = fheight * 2 + 2 | |
546 | - box_y = y - RADIUS - 1 - box_height | |
547 | - | |
548 | - # The bottom position of the text box mustn't be upper than the top of | |
549 | - # the width to always appears in the widget | |
550 | - if box_y <= self.padding: | |
551 | - box_y = y + RADIUS + 1 | |
552 | - | |
553 | - y_text1 = box_y + fascent | |
554 | - y_text2 = y_text1 + fheight | |
555 | - | |
556 | - x_left = x + RADIUS + 1 | |
557 | - box_width = width + 2 | |
558 | - # The right position of the text box mustn't be in the widget area to | |
559 | - # always appears in the widget | |
560 | - if x_left + box_width > widget_width: | |
561 | - x_left = x - box_width - 1 - RADIUS | |
562 | - | |
563 | - x_text = x_left + 1 | |
564 | - | |
565 | - ctx.set_source_rgba(*BACKGROUND_TEXT_COLOUR_RGBA) | |
566 | - ctx.rectangle(x_left, box_y, | |
567 | - box_width, box_height) | |
568 | - ctx.fill() | |
569 | - | |
570 | - ctx.set_source_rgb(*TEXT_COLOUR) | |
571 | - ctx.move_to(x_text, y_text1) | |
572 | - ctx.show_text("Value: %d" % value) | |
573 | - ctx.move_to(x_text, y_text2) | |
574 | - ctx.show_text("Alpha: %.3f" % alpha) | |
544 | + if xr + wr > widget_width: | |
545 | + xr = x - RADIUS - wr | |
546 | + if yr < 0: | |
547 | + yr = y + RADIUS | |
548 | + | |
549 | + xf, yf = xr + PADDING, yr + PADDING | |
550 | + | |
551 | + ctx.SetBrush(wx.Brush(BACKGROUND_TEXT_COLOUR_RGBA)) | |
552 | + ctx.SetPen(wx.Pen(BACKGROUND_TEXT_COLOUR_RGBA)) | |
553 | + ctx.DrawRectangle(xr, yr, wr, hr) | |
554 | + ctx.DrawText(text1, xf, yf) | |
555 | + ctx.DrawText(text2, xf, yf + ht) | |
575 | 556 | |
576 | 557 | def _draw_histogram(self, ctx, height): |
577 | 558 | # The histogram |
578 | 559 | x,y = self.Histogram.points[0] |
579 | 560 | print "=>", x,y |
580 | - ctx.move_to(x,y) | |
581 | - ctx.set_line_width(HISTOGRAM_LINE_WIDTH) | |
561 | + | |
562 | + ctx.SetPen(wx.Pen(HISTOGRAM_LINE_COLOUR, HISTOGRAM_LINE_WIDTH)) | |
563 | + ctx.SetBrush(wx.Brush(HISTOGRAM_FILL_COLOUR)) | |
564 | + | |
565 | + path = ctx.CreatePath() | |
566 | + path.MoveToPoint(x,y) | |
582 | 567 | for x,y in self.Histogram.points: |
583 | - ctx.line_to(x,y) | |
584 | - ctx.set_source_rgb(*HISTOGRAM_LINE_COLOUR) | |
585 | - ctx.stroke_preserve() | |
586 | - ctx.line_to(x, height + self.padding) | |
587 | - ctx.line_to(self.HounsfieldToPixel(self.Histogram.init), height + self.padding) | |
568 | + print x,y | |
569 | + path.AddLineToPoint(x, y) | |
570 | + | |
571 | + ctx.PushState() | |
572 | + ctx.StrokePath(path) | |
573 | + ctx.PopState() | |
574 | + path.AddLineToPoint(x, height + self.padding) | |
575 | + path.AddLineToPoint(self.HounsfieldToPixel(self.Histogram.init), height + self.padding) | |
588 | 576 | x,y = self.Histogram.points[0] |
589 | - ctx.line_to(x, y) | |
590 | - ctx.set_source_rgb(*HISTOGRAM_FILL_COLOUR) | |
591 | - ctx.fill() | |
577 | + path.AddLineToPoint(x, y) | |
578 | + ctx.FillPath(path) | |
592 | 579 | |
593 | 580 | def _draw_selection_curve(self, ctx, height): |
581 | + ctx.SetPen(wx.Pen(LINE_COLOUR, LINE_WIDTH)) | |
582 | + ctx.SetBrush(wx.Brush((0, 0, 0))) | |
594 | 583 | for curve in self.curves: |
595 | 584 | x_center, y_center = curve.wl_px |
596 | - ctx.rectangle(x_center-SELECTION_SIZE/2.0, y_center, SELECTION_SIZE, | |
597 | - SELECTION_SIZE) | |
598 | - ctx.set_source_rgb(0,0,0) | |
599 | - ctx.fill_preserve() | |
600 | - ctx.set_source_rgb(*LINE_COLOUR) | |
601 | - ctx.stroke() | |
585 | + ctx.DrawRectangle(x_center-SELECTION_SIZE/2.0, y_center, | |
586 | + SELECTION_SIZE, SELECTION_SIZE) | |
602 | 587 | |
603 | 588 | def _draw_tool_bar(self, ctx, height): |
604 | - ctx.rectangle(0, 0, TOOLBAR_SIZE, height + self.padding * 2) | |
605 | - ctx.set_source_rgb(0.1,0.1,0.1) | |
606 | - ctx.fill() | |
607 | - #ctx.set_source_rgb(1, 1, 1) | |
608 | - #ctx.stroke() | |
589 | + ctx.SetPen(wx.TRANSPARENT_PEN) | |
590 | + ctx.SetBrush(wx.Brush(TOOLBAR_COLOUR)) | |
591 | + ctx.DrawRectangle(0, 0, TOOLBAR_SIZE, height + self.padding * 2) | |
609 | 592 | image = self.save_button.image |
610 | 593 | w, h = self.save_button.size |
611 | 594 | x = (TOOLBAR_SIZE - w) / 2.0 |
612 | 595 | y = self.padding |
613 | 596 | self.save_button.position = (x, y) |
614 | - ctx.set_source_surface(image, x, y) | |
615 | - ctx.paint() | |
597 | + ctx.DrawBitmap(image, x, y, w, h) | |
616 | 598 | |
617 | 599 | def Render(self, dc): |
618 | - ctx = wx.lib.wxcairo.ContextFromDC(dc) | |
600 | + ctx = wx.GraphicsContext.Create(dc) | |
619 | 601 | width, height= self.GetVirtualSizeTuple() |
620 | 602 | height -= (self.padding * 2) |
621 | 603 | width -= self.padding |
622 | 604 | |
623 | 605 | self._draw_histogram(ctx, height) |
624 | - ctx.set_line_width(LINE_WIDTH) | |
625 | 606 | self._draw_gradient(ctx, height) |
626 | 607 | self._draw_curves(ctx) |
627 | 608 | self._draw_points(ctx) |
628 | 609 | self._draw_selection_curve(ctx, height) |
629 | 610 | self._draw_tool_bar(ctx, height) |
630 | - if sys.platform != "darwin": | |
631 | - self._draw_tool_bar(ctx, height) | |
632 | - if self.point_dragged: | |
633 | - self._draw_selected_point_text(ctx) | |
611 | + if self.point_dragged: | |
612 | + self._draw_selected_point_text(ctx) | |
634 | 613 | |
635 | 614 | def _build_histogram(self): |
636 | 615 | width, height = self.GetVirtualSizeTuple() |
... | ... | @@ -653,15 +632,12 @@ class CLUTRaycastingWidget(wx.Panel): |
653 | 632 | self.Histogram.points.append((x, y)) |
654 | 633 | |
655 | 634 | def _build_buttons(self): |
656 | - if sys.platform == 'darwin': | |
657 | - self.save_button = Button() | |
658 | - else: | |
659 | - img = cairo.ImageSurface.create_from_png(os.path.join(const.ICON_DIR, 'Floppy.png')) | |
660 | - width = img.get_width() | |
661 | - height = img.get_height() | |
662 | - self.save_button = Button() | |
663 | - self.save_button.image = img | |
664 | - self.save_button.size = (width, height) | |
635 | + img = wx.Image(os.path.join(const.ICON_DIR, 'Floppy.png')) | |
636 | + width = img.GetWidth() | |
637 | + height = img.GetHeight() | |
638 | + self.save_button = Button() | |
639 | + self.save_button.image = wx.BitmapFromImage(img) | |
640 | + self.save_button.size = (width, height) | |
665 | 641 | |
666 | 642 | def __sort_pixel_points(self): |
667 | 643 | """ |
... | ... | @@ -692,7 +668,9 @@ class CLUTRaycastingWidget(wx.Panel): |
692 | 668 | node.y = y |
693 | 669 | node.graylevel = point['x'] |
694 | 670 | node.opacity = point['y'] |
695 | - node.colour = colour['red'], colour['green'], colour['blue'] | |
671 | + node.colour = (int(colour['red'] * 255), | |
672 | + int(colour['green'] * 255), | |
673 | + int(colour['blue'] * 255)) | |
696 | 674 | curve.nodes.append(node) |
697 | 675 | curve.CalculateWWWl() |
698 | 676 | curve.wl_px = (self.HounsfieldToPixel(curve.wl), | ... | ... |