Commit 431bc08bd795287a22be1c1a97278d5c1d25a0a9

Authored by tfmoraes
1 parent bb8f5543

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),
... ...