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