Commit 76643a9e3602a8d1ac3bacd933812b19711dbe25

Authored by Thiago Franco de Moraes
Committed by GitHub
2 parents 30c6a6ab 43729e8f

Merge pull request #42 from tfmoraes/measure_canvas

Add a canvas to draw over the Slice Viewer. This canvas is able to draw geometric forms, unicode text and has support to RGBA. Also, this adapts the measurement system to show the measurements using this canvas system.
invesalius/data/converters.py
... ... @@ -56,3 +56,25 @@ def to_vtk(n_array, spacing, slice_number, orientation):
56 56 image_copy.DeepCopy(image)
57 57  
58 58 return image_copy
  59 +
  60 +
  61 +def np_rgba_to_vtk(n_array, spacing=(1.0, 1.0, 1.0)):
  62 + dy, dx, dc = n_array.shape
  63 + v_image = numpy_support.numpy_to_vtk(n_array.reshape(dy*dx, dc))
  64 +
  65 + extent = (0, dx -1, 0, dy -1, 0, 0)
  66 +
  67 + # Generating the vtkImageData
  68 + image = vtk.vtkImageData()
  69 + image.SetOrigin(0, 0, 0)
  70 + image.SetSpacing(spacing)
  71 + image.SetDimensions(dx, dy, 1)
  72 + # SetNumberOfScalarComponents and SetScalrType were replaced by
  73 + # AllocateScalars
  74 + # image.SetNumberOfScalarComponents(1)
  75 + # image.SetScalarType(numpy_support.get_vtk_array_type(n_array.dtype))
  76 + image.AllocateScalars(numpy_support.get_vtk_array_type(n_array.dtype), dc)
  77 + image.SetExtent(extent)
  78 + image.GetPointData().SetScalars(v_image)
  79 +
  80 + return image
... ...
invesalius/data/measures.py
... ... @@ -5,6 +5,8 @@ import math
5 5 import random
6 6  
7 7 from wx.lib.pubsub import pub as Publisher
  8 +
  9 +import numpy as np
8 10 import vtk
9 11  
10 12 import constants as const
... ... @@ -137,7 +139,7 @@ class MeasurementManager(object):
137 139 (actors, m.slice_number))
138 140 self.current = None
139 141  
140   - if not m.is_shown:
  142 + if not m.visible:
141 143 mr.SetVisibility(False)
142 144 if m.location == const.SURFACE:
143 145 Publisher.sendMessage('Render volume viewer')
... ... @@ -307,7 +309,7 @@ class MeasurementManager(object):
307 309 def _set_visibility(self, pubsub_evt):
308 310 index, visibility = pubsub_evt.data
309 311 m, mr = self.measures[index]
310   - m.is_shown = visibility
  312 + m.visible = visibility
311 313 mr.SetVisibility(visibility)
312 314 if m.location == const.SURFACE:
313 315 Publisher.sendMessage('Render volume viewer')
... ... @@ -318,22 +320,24 @@ class MeasurementManager(object):
318 320 if self.current is None:
319 321 return
320 322  
321   - mr = self.current[1]
322   - print "RM INC M", self.current, mr.IsComplete()
  323 + m, mr = self.current
323 324 if not mr.IsComplete():
324 325 print "---To REMOVE"
325   - self.measures.pop()
  326 + idx = self.measures._list_measures.index((m, mr))
  327 + self.measures.remove((m, mr))
  328 + Publisher.sendMessage("Remove GUI measurement", idx)
326 329 actors = mr.GetActors()
327 330 slice_number = self.current[0].slice_number
328   - Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)),
329   - (actors, slice_number))
  331 + if m.location == const.SURFACE:
  332 + Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)),
  333 + (actors, slice_number))
330 334 if self.current[0].location == const.SURFACE:
331 335 Publisher.sendMessage('Render volume viewer')
332 336 else:
333 337 Publisher.sendMessage('Update slice viewer')
334 338  
335   - if self.measures:
336   - self.measures.pop()
  339 + # if self.measures:
  340 + # self.measures.pop()
337 341 self.current = None
338 342  
339 343  
... ... @@ -349,7 +353,7 @@ class Measurement():
349 353 self.type = const.LINEAR # ANGULAR
350 354 self.slice_number = 0
351 355 self.points = []
352   - self.is_shown = True
  356 + self.visible = True
353 357  
354 358 def Load(self, info):
355 359 self.index = info["index"]
... ... @@ -360,7 +364,7 @@ class Measurement():
360 364 self.type = info["type"]
361 365 self.slice_number = info["slice_number"]
362 366 self.points = info["points"]
363   - self.is_shown = info["visible"]
  367 + self.visible = info["visible"]
364 368  
365 369 class CirclePointRepresentation(object):
366 370 """
... ... @@ -559,12 +563,38 @@ class LinearMeasure(object):
559 563 a.GetProperty().SetOpacity(0.75)
560 564 self.text_actor = a
561 565  
  566 + def draw_to_canvas(self, gc, canvas):
  567 + """
  568 + Draws to an wx.GraphicsContext.
  569 +
  570 + Parameters:
  571 + gc: is a wx.GraphicsContext
  572 + canvas: the canvas it's being drawn.
  573 + """
  574 + coord = vtk.vtkCoordinate()
  575 + points = []
  576 + for p in self.points:
  577 + coord.SetValue(p)
  578 + cx, cy = coord.GetComputedDisplayValue(canvas.viewer.slice_data.renderer)
  579 + # canvas.draw_circle((cx, cy), 2.5)
  580 + points.append((cx, cy))
  581 +
  582 + if len(points) > 1:
  583 + for (p0, p1) in zip(points[:-1:], points[1::]):
  584 + canvas.draw_line(p0, p1)
  585 +
  586 + txt = u"%.3f mm" % self.GetValue()
  587 + canvas.draw_text_box(txt, ((points[0][0]+points[1][0])/2.0, (points[0][1]+points[1][1])/2.0))
  588 +
562 589 def GetNumberOfPoints(self):
563 590 return len(self.points)
564 591  
565 592 def GetValue(self):
566   - p1, p2 = self.points
567   - return math.sqrt(vtk.vtkMath.Distance2BetweenPoints(p1, p2))
  593 + if self.IsComplete():
  594 + p1, p2 = self.points
  595 + return math.sqrt(vtk.vtkMath.Distance2BetweenPoints(p1, p2))
  596 + else:
  597 + return 0.0
568 598  
569 599 def SetRenderer(self, renderer):
570 600 if self.point_actor1:
... ... @@ -607,21 +637,22 @@ class LinearMeasure(object):
607 637 return actors
608 638  
609 639 def Remove(self):
610   - if self.point_actor1:
611   - self.renderer.RemoveActor(self.point_actor1)
612   - del self.point_actor1
  640 + pass
  641 + # if self.point_actor1:
  642 + # self.renderer.RemoveActor(self.point_actor1)
  643 + # del self.point_actor1
613 644  
614   - if self.point_actor2:
615   - self.renderer.RemoveActor(self.point_actor2)
616   - del self.point_actor2
  645 + # if self.point_actor2:
  646 + # self.renderer.RemoveActor(self.point_actor2)
  647 + # del self.point_actor2
617 648  
618   - if self.line_actor:
619   - self.renderer.RemoveActor(self.line_actor)
620   - del self.line_actor
  649 + # if self.line_actor:
  650 + # self.renderer.RemoveActor(self.line_actor)
  651 + # del self.line_actor
621 652  
622   - if self.text_actor:
623   - self.renderer.RemoveActor(self.text_actor)
624   - del self.text_actor
  653 + # if self.text_actor:
  654 + # self.renderer.RemoveActor(self.text_actor)
  655 + # del self.text_actor
625 656  
626 657 # def __del__(self):
627 658 # self.Remove()
... ... @@ -630,7 +661,7 @@ class LinearMeasure(object):
630 661 class AngularMeasure(object):
631 662 def __init__(self, colour=(1, 0, 0), representation=None):
632 663 self.colour = colour
633   - self.points = [0, 0, 0]
  664 + self.points = []
634 665 self.number_of_points = 0
635 666 self.point_actor1 = None
636 667 self.point_actor2 = None
... ... @@ -658,7 +689,7 @@ class AngularMeasure(object):
658 689  
659 690 def SetPoint1(self, x, y, z):
660 691 if self.number_of_points == 0:
661   - self.points[0] = (x, y, z)
  692 + self.points.append((x, y, z))
662 693 self.number_of_points = 1
663 694 self.point_actor1 = self.representation.GetRepresentation(x, y, z)
664 695 else:
... ... @@ -677,7 +708,7 @@ class AngularMeasure(object):
677 708 def SetPoint2(self, x, y, z):
678 709 if self.number_of_points == 1:
679 710 self.number_of_points = 2
680   - self.points[1] = (x, y, z)
  711 + self.points.append((x, y, z))
681 712 self.point_actor2 = self.representation.GetRepresentation(x, y, z)
682 713 else:
683 714 self.points[1] = (x, y, z)
... ... @@ -695,7 +726,7 @@ class AngularMeasure(object):
695 726 def SetPoint3(self, x, y, z):
696 727 if self.number_of_points == 2:
697 728 self.number_of_points = 3
698   - self.points[2] = (x, y, z)
  729 + self.points.append((x, y, z))
699 730 self.point_actor3 = self.representation.GetRepresentation(x, y, z)
700 731 self.CreateMeasure()
701 732 else:
... ... @@ -793,11 +824,41 @@ class AngularMeasure(object):
793 824 a.GetPositionCoordinate().SetValue(x,y,z)
794 825 self.text_actor = a
795 826  
  827 + def draw_to_canvas(self, gc, canvas):
  828 + """
  829 + Draws to an wx.GraphicsContext.
  830 +
  831 + Parameters:
  832 + gc: is a wx.GraphicsContext
  833 + canvas: the canvas it's being drawn.
  834 + """
  835 +
  836 + coord = vtk.vtkCoordinate()
  837 + points = []
  838 + for p in self.points:
  839 + coord.SetValue(p)
  840 + cx, cy = coord.GetComputedDisplayValue(canvas.viewer.slice_data.renderer)
  841 + # canvas.draw_circle((cx, cy), 2.5)
  842 + points.append((cx, cy))
  843 +
  844 + if len(points) > 1:
  845 + for (p0, p1) in zip(points[:-1:], points[1::]):
  846 + canvas.draw_line(p0, p1)
  847 +
  848 + if len(points) == 3:
  849 + txt = u"%.3f° / %.3f°" % (self.GetValue(), 360.0 - self.GetValue())
  850 +
  851 + canvas.draw_arc(points[1], points[0], points[2])
  852 + canvas.draw_text_box(txt, (points[1][0], points[1][1]))
  853 +
796 854 def GetNumberOfPoints(self):
797 855 return self.number_of_points
798 856  
799 857 def GetValue(self):
800   - return self.CalculateAngle()
  858 + if self.IsComplete():
  859 + return self.CalculateAngle()
  860 + else:
  861 + return 0.0
801 862  
802 863 def SetVisibility(self, v):
803 864 self.point_actor1.SetVisibility(v)
... ... @@ -835,30 +896,35 @@ class AngularMeasure(object):
835 896 v2 = [j-i for i,j in zip(self.points[2], self.points[1])]
836 897 #print vtk.vtkMath.Normalize(v1)
837 898 #print vtk.vtkMath.Normalize(v2)
838   - cos = vtk.vtkMath.Dot(v1, v2)/(vtk.vtkMath.Norm(v1)*vtk.vtkMath.Norm(v2))
  899 + try:
  900 + cos = vtk.vtkMath.Dot(v1, v2)/(vtk.vtkMath.Norm(v1)*vtk.vtkMath.Norm(v2))
  901 + except ZeroDivisionError:
  902 + return 0.0
  903 +
839 904 angle = math.degrees(math.acos(cos))
840 905 return angle
841 906  
842 907 def Remove(self):
843   - if self.point_actor1:
844   - self.renderer.RemoveActor(self.point_actor1)
845   - del self.point_actor1
846   -
847   - if self.point_actor2:
848   - self.renderer.RemoveActor(self.point_actor2)
849   - del self.point_actor2
850   -
851   - if self.point_actor3:
852   - self.renderer.RemoveActor(self.point_actor3)
853   - del self.point_actor3
854   -
855   - if self.line_actor:
856   - self.renderer.RemoveActor(self.line_actor)
857   - del self.line_actor
858   -
859   - if self.text_actor:
860   - self.renderer.RemoveActor(self.text_actor)
861   - del self.text_actor
  908 + pass
  909 + # if self.point_actor1:
  910 + # self.renderer.RemoveActor(self.point_actor1)
  911 + # del self.point_actor1
  912 +
  913 + # if self.point_actor2:
  914 + # self.renderer.RemoveActor(self.point_actor2)
  915 + # del self.point_actor2
  916 +
  917 + # if self.point_actor3:
  918 + # self.renderer.RemoveActor(self.point_actor3)
  919 + # del self.point_actor3
  920 +
  921 + # if self.line_actor:
  922 + # self.renderer.RemoveActor(self.line_actor)
  923 + # del self.line_actor
  924 +
  925 + # if self.text_actor:
  926 + # self.renderer.RemoveActor(self.text_actor)
  927 + # del self.text_actor
862 928  
863 929 def SetRenderer(self, renderer):
864 930 if self.point_actor1:
... ...
invesalius/data/slice_data.py
... ... @@ -38,6 +38,7 @@ class SliceData(object):
38 38 self.number = 0
39 39 self.orientation = 'AXIAL'
40 40 self.renderer = None
  41 + self.canvas_renderer = None
41 42 self.overlay_renderer = None
42 43 self.__create_text()
43 44 self.__create_box()
... ...
invesalius/data/styles.py
... ... @@ -354,6 +354,7 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle):
354 354  
355 355 self.measures = MeasureData()
356 356 self.selected = None
  357 + self.creating = None
357 358  
358 359 self._type = const.LINEAR
359 360  
... ... @@ -377,25 +378,57 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle):
377 378 self._bind_events()
378 379  
379 380 def _bind_events(self):
380   - self.AddObserver("LeftButtonPressEvent", self.OnInsertLinearMeasurePoint)
  381 + self.AddObserver("LeftButtonPressEvent", self.OnInsertMeasurePoint)
381 382 self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseMeasurePoint)
382 383 self.AddObserver("MouseMoveEvent", self.OnMoveMeasurePoint)
  384 + self.AddObserver("LeaveEvent", self.OnLeaveMeasureInteractor)
383 385  
384   - def OnInsertLinearMeasurePoint(self, obj, evt):
  386 + def OnInsertMeasurePoint(self, obj, evt):
385 387 slice_number = self.slice_data.number
386 388 x, y, z = self._get_pos_clicked()
  389 + mx, my = self.viewer.interactor.GetEventPosition()
387 390  
388   - selected = self._verify_clicked(x, y, z)
  391 + if self.selected:
  392 + self.selected = None
  393 + self.viewer.scroll_enabled = True
  394 + return
  395 +
  396 + if self.creating:
  397 + n, m, mr = self.creating
  398 + if mr.IsComplete():
  399 + self.creating = None
  400 + self.viewer.scroll_enabled = True
  401 + else:
  402 + Publisher.sendMessage("Add measurement point",
  403 + ((x, y, z), self._type,
  404 + ORIENTATIONS[self.orientation],
  405 + slice_number, self.radius))
  406 + n = len(m.points)-1
  407 + self.creating = n, m, mr
  408 + Publisher.sendMessage('Reload actual slice %s' % self.orientation)
  409 + self.viewer.scroll_enabled = False
  410 + return
  411 +
  412 + selected = self._verify_clicked_display(mx, my)
389 413 if selected:
390 414 self.selected = selected
  415 + self.viewer.scroll_enabled = False
391 416 else:
392 417 if self.picker.GetViewProp():
393 418 renderer = self.viewer.slice_data.renderer
394 419 Publisher.sendMessage("Add measurement point",
395   - ((x, y,z), self._type,
  420 + ((x, y, z), self._type,
  421 + ORIENTATIONS[self.orientation],
  422 + slice_number, self.radius))
  423 + Publisher.sendMessage("Add measurement point",
  424 + ((x, y, z), self._type,
396 425 ORIENTATIONS[self.orientation],
397 426 slice_number, self.radius))
  427 +
  428 + n, (m, mr) = 1, self.measures.measures[self._ori][slice_number][-1]
  429 + self.creating = n, m, mr
398 430 Publisher.sendMessage('Reload actual slice %s' % self.orientation)
  431 + self.viewer.scroll_enabled = False
399 432  
400 433 def OnReleaseMeasurePoint(self, obj, evt):
401 434 if self.selected:
... ... @@ -405,16 +438,41 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle):
405 438 Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z)))
406 439 Publisher.sendMessage('Reload actual slice %s' % self.orientation)
407 440 self.selected = None
  441 + self.viewer.scroll_enabled = True
408 442  
409 443 def OnMoveMeasurePoint(self, obj, evt):
  444 + x, y, z = self._get_pos_clicked()
410 445 if self.selected:
411 446 n, m, mr = self.selected
412   - x, y, z = self._get_pos_clicked()
413 447 idx = self.measures._list_measures.index((m, mr))
414 448 Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z)))
415 449  
416 450 Publisher.sendMessage('Reload actual slice %s' % self.orientation)
417 451  
  452 + elif self.creating:
  453 + n, m, mr = self.creating
  454 + idx = self.measures._list_measures.index((m, mr))
  455 + Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z)))
  456 +
  457 + Publisher.sendMessage('Reload actual slice %s' % self.orientation)
  458 +
  459 + else:
  460 + mx, my = self.viewer.interactor.GetEventPosition()
  461 + if self._verify_clicked_display(mx, my):
  462 + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
  463 + else:
  464 + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
  465 +
  466 + def OnLeaveMeasureInteractor(self, obj, evt):
  467 + if self.creating or self.selected:
  468 + n, m, mr = self.creating
  469 + if not mr.IsComplete():
  470 + Publisher.sendMessage("Remove incomplete measurements")
  471 + self.creating = None
  472 + self.selected = None
  473 + Publisher.sendMessage('Update slice viewer')
  474 + self.viewer.scroll_enabled = True
  475 +
418 476 def CleanUp(self):
419 477 self.picker.PickFromListOff()
420 478 Publisher.sendMessage("Remove incomplete measurements")
... ... @@ -449,6 +507,21 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle):
449 507 return (n, m, mr)
450 508 return None
451 509  
  510 + def _verify_clicked_display(self, x, y, max_dist=5.0):
  511 + slice_number = self.slice_data.number
  512 + max_dist = max_dist**2
  513 + coord = vtk.vtkCoordinate()
  514 + if slice_number in self.measures.measures[self._ori]:
  515 + for m, mr in self.measures.measures[self._ori][slice_number]:
  516 + if mr.IsComplete():
  517 + for n, p in enumerate(m.points):
  518 + coord.SetValue(p)
  519 + cx, cy = coord.GetComputedDisplayValue(self.viewer.slice_data.renderer)
  520 + dist = ((cx-x)**2 + (cy-y)**2)
  521 + if dist <= max_dist:
  522 + return (n, m, mr)
  523 + return None
  524 +
452 525 class AngularMeasureInteractorStyle(LinearMeasureInteractorStyle):
453 526 def __init__(self, viewer):
454 527 LinearMeasureInteractorStyle.__init__(self, viewer)
... ...
invesalius/data/viewer_slice.py
... ... @@ -23,7 +23,7 @@ import collections
23 23 import itertools
24 24 import tempfile
25 25  
26   -import numpy
  26 +import numpy as np
27 27  
28 28 import vtk
29 29 from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor
... ... @@ -46,6 +46,8 @@ import project
46 46 import slice_data as sd
47 47 import utils
48 48  
  49 +from data import converters
  50 +
49 51 from data import measures
50 52  
51 53 ID_TO_TOOL_ITEM = {}
... ... @@ -87,7 +89,7 @@ class ContourMIPConfig(wx.Panel):
87 89 " order to compound the"
88 90 " visualization instead of"
89 91 " ascending order.")))
90   -
  92 +
91 93 txt_mip_size = wx.StaticText(self, -1, _("Number of slices"), style=wx.ALIGN_CENTER_HORIZONTAL)
92 94 self.txt_mip_border = wx.StaticText(self, -1, _("Sharpness"))
93 95  
... ... @@ -111,7 +113,7 @@ class ContourMIPConfig(wx.Panel):
111 113 self.mip_size_spin.Bind(wx.EVT_SPINCTRL, self.OnSetMIPSize)
112 114 self.border_spin.Bind(wx.EVT_SPINCTRL, self.OnSetMIPBorder)
113 115 self.inverted.Bind(wx.EVT_CHECKBOX, self.OnCheckInverted)
114   -
  116 +
115 117 Publisher.subscribe(self._set_projection_type, 'Set projection type')
116 118  
117 119 def OnSetMIPSize(self, evt):
... ... @@ -134,7 +136,7 @@ class ContourMIPConfig(wx.Panel):
134 136 self.inverted.Enable()
135 137 else:
136 138 self.inverted.Disable()
137   -
  139 +
138 140 if tprojection in (const.PROJECTION_CONTOUR_MIP,
139 141 const.PROJECTION_CONTOUR_MIDA):
140 142 self.border_spin.Enable()
... ... @@ -144,6 +146,262 @@ class ContourMIPConfig(wx.Panel):
144 146 self.txt_mip_border.Disable()
145 147  
146 148  
  149 +class CanvasRendererCTX:
  150 + def __init__(self, viewer):
  151 + self.viewer = viewer
  152 + self.canvas_renderer = viewer.slice_data.canvas_renderer
  153 + self._size = self.canvas_renderer.GetSize()
  154 + self.gc = None
  155 + self._init_canvas()
  156 + viewer.slice_data.renderer.AddObserver("StartEvent", self.OnPaint)
  157 +
  158 + def _init_canvas(self):
  159 + w, h = self._size
  160 + self._array = np.zeros((h, w, 4), dtype=np.uint8)
  161 +
  162 + self._cv_image = converters.np_rgba_to_vtk(self._array)
  163 +
  164 + self.mapper = vtk.vtkImageMapper()
  165 + self.mapper.SetInputData(self._cv_image)
  166 + self.mapper.SetColorWindow(255)
  167 + self.mapper.SetColorLevel(128)
  168 +
  169 + self.actor = vtk.vtkActor2D()
  170 + self.actor.SetPosition(0, 0)
  171 + self.actor.SetMapper(self.mapper)
  172 + self.actor.GetProperty().SetOpacity(0.99)
  173 +
  174 + self.canvas_renderer.AddActor2D(self.actor)
  175 +
  176 + self.rgb = np.zeros((h, w, 3), dtype=np.uint8)
  177 + self.alpha = np.zeros((h, w, 1), dtype=np.uint8)
  178 +
  179 + self.bitmap = wx.EmptyBitmapRGBA(w, h)
  180 + self.image = wx.ImageFromBuffer(w, h, self.rgb, self.alpha)
  181 +
  182 + def _resize_canvas(self, w, h):
  183 + self._array = np.zeros((h, w, 4), dtype=np.uint8)
  184 + self._cv_image = converters.np_rgba_to_vtk(self._array)
  185 + self.mapper.SetInputData(self._cv_image)
  186 + self.mapper.Update()
  187 +
  188 + self.rgb = np.zeros((h, w, 3), dtype=np.uint8)
  189 + self.alpha = np.zeros((h, w, 1), dtype=np.uint8)
  190 +
  191 + self.bitmap = wx.EmptyBitmapRGBA(w, h)
  192 + self.image = wx.ImageFromBuffer(w, h, self.rgb, self.alpha)
  193 +
  194 + def OnPaint(self, evt, obj):
  195 + self._array[:] = 0
  196 + size = self.canvas_renderer.GetSize()
  197 + w, h = size
  198 + if self._size != size:
  199 + self._size = size
  200 + self._resize_canvas(w, h)
  201 +
  202 + coord = vtk.vtkCoordinate()
  203 +
  204 + self.image.SetDataBuffer(self.rgb)
  205 + self.image.SetAlphaBuffer(self.alpha)
  206 + self.image.Clear()
  207 + gc = wx.GraphicsContext.Create(self.image)
  208 + gc.SetAntialiasMode(0)
  209 +
  210 + self.gc = gc
  211 +
  212 + font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
  213 + # font.SetWeight(wx.BOLD)
  214 + font = gc.CreateFont(font, (0, 0, 255))
  215 + gc.SetFont(font)
  216 +
  217 + pen = wx.Pen(wx.Colour(255, 0, 0, 128), 2, wx.SOLID)
  218 + brush = wx.Brush(wx.Colour(0, 255, 0, 128))
  219 + gc.SetPen(pen)
  220 + gc.SetBrush(brush)
  221 + gc.Scale(1, -1)
  222 +
  223 + modified = False
  224 + for (m, mr) in self.viewer.measures.get(self.viewer.orientation, self.viewer.slice_data.number):
  225 + if not m.visible:
  226 + continue
  227 + mr.draw_to_canvas(gc, self)
  228 + modified = True
  229 +
  230 + gc.Destroy()
  231 +
  232 + self.gc = None
  233 +
  234 + if modified:
  235 + self.bitmap = self.image.ConvertToBitmap()
  236 + self.bitmap.CopyToBuffer(self._array, wx.BitmapBufferFormat_RGBA)
  237 +
  238 + self._cv_image.Modified()
  239 +
  240 + def draw_line(self, pos0, pos1, arrow_start=False, arrow_end=False, colour=(255, 0, 0, 128), width=2, style=wx.SOLID):
  241 + """
  242 + Draw a line from pos0 to pos1
  243 + """
  244 + if self.gc is None:
  245 + return None
  246 + gc = self.gc
  247 +
  248 + p0x, p0y = pos0
  249 + p1x, p1y = pos1
  250 +
  251 + p0y = -p0y
  252 + p1y = -p1y
  253 +
  254 + pen = wx.Pen(wx.Colour(*colour), width, wx.SOLID)
  255 + pen.SetCap(wx.CAP_BUTT)
  256 + gc.SetPen(pen)
  257 +
  258 + path = gc.CreatePath()
  259 + path.MoveToPoint(p0x, p0y)
  260 + path.AddLineToPoint(p1x, p1y)
  261 + gc.StrokePath(path)
  262 +
  263 + font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
  264 + font = gc.CreateFont(font)
  265 + gc.SetFont(font)
  266 + w, h = gc.GetTextExtent("M")
  267 +
  268 + p0 = np.array((p0x, p0y))
  269 + p3 = np.array((p1x, p1y))
  270 + if arrow_start:
  271 + v = p3 - p0
  272 + v = v / np.linalg.norm(v)
  273 + iv = np.array((v[1], -v[0]))
  274 + p1 = p0 + w*v + iv*w/2.0
  275 + p2 = p0 + w*v + (-iv)*w/2.0
  276 +
  277 + path = gc.CreatePath()
  278 + path.MoveToPoint(p0)
  279 + path.AddLineToPoint(p1)
  280 + path.MoveToPoint(p0)
  281 + path.AddLineToPoint(p2)
  282 + gc.StrokePath(path)
  283 +
  284 + if arrow_end:
  285 + v = p3 - p0
  286 + v = v / np.linalg.norm(v)
  287 + iv = np.array((v[1], -v[0]))
  288 + p1 = p3 - w*v + iv*w/2.0
  289 + p2 = p3 - w*v + (-iv)*w/2.0
  290 +
  291 + path = gc.CreatePath()
  292 + path.MoveToPoint(p3)
  293 + path.AddLineToPoint(p1)
  294 + path.MoveToPoint(p3)
  295 + path.AddLineToPoint(p2)
  296 + gc.StrokePath(path)
  297 +
  298 + def draw_circle(self, center, radius, width=2, line_colour=(255, 0, 0, 128), fill_colour=(0, 0, 0, 0)):
  299 + """
  300 + Draw a circle centered at center with the given radius.
  301 +
  302 + Params:
  303 + center: (x, y) position.
  304 + radius: float number.
  305 + width: line width.
  306 + line_colour: RGBA line colour
  307 + fill_colour: RGBA fill colour.
  308 + """
  309 + if self.gc is None:
  310 + return None
  311 + gc = self.gc
  312 +
  313 + pen = wx.Pen(wx.Colour(*line_colour), width, wx.SOLID)
  314 + gc.SetPen(pen)
  315 +
  316 + brush = wx.Brush(wx.Colour(*fill_colour))
  317 + gc.SetBrush(brush)
  318 +
  319 + cx, cy = center
  320 + cy = -cy
  321 +
  322 + path = gc.CreatePath()
  323 + path.AddCircle(cx, cy, 2.5)
  324 + gc.StrokePath(path)
  325 + gc.FillPath(path)
  326 +
  327 +
  328 + def draw_text_box(self, text, pos, font=None, txt_colour=(255, 255, 255), bg_colour=(128, 128, 128, 128), border=5):
  329 + """
  330 + Draw text inside a text box.
  331 +
  332 + Params:
  333 + text: an unicode text.
  334 + pos: (x, y) position.
  335 + font: if None it'll use the default gui font.
  336 + txt_colour: RGB text colour
  337 + bg_colour: RGBA box colour
  338 + border: the border size.
  339 + """
  340 + if self.gc is None:
  341 + return None
  342 + gc = self.gc
  343 +
  344 + if font is None:
  345 + font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
  346 +
  347 + font = gc.CreateFont(font, txt_colour)
  348 + gc.SetFont(font)
  349 + w, h = gc.GetTextExtent(text)
  350 +
  351 + px, py = pos
  352 + py = -py
  353 +
  354 + # Drawing the box
  355 + cw, ch = w + border * 2, h + border * 2
  356 + gc.SetBrush(wx.Brush(bg_colour))
  357 + gc.SetPen(wx.Pen(bg_colour))
  358 + gc.DrawRectangle(px, py, cw, ch)
  359 +
  360 + tpx, tpy = px + border, py + border
  361 + gc.DrawText(text, tpx, tpy)
  362 +
  363 + def draw_arc(self, center, p0, p1, line_colour=(255, 0, 0, 128), width=2):
  364 + """
  365 + Draw an arc passing in p0 and p1 centered at center.
  366 +
  367 + Params:
  368 + center: (x, y) center of the arc.
  369 + p0: (x, y).
  370 + p1: (x, y).
  371 + line_colour: RGBA line colour.
  372 + width: width of the line.
  373 + """
  374 + if self.gc is None:
  375 + return None
  376 + gc = self.gc
  377 + pen = wx.Pen(wx.Colour(*line_colour), width, wx.SOLID)
  378 + gc.SetPen(pen)
  379 +
  380 + c = np.array(center)
  381 + v0 = np.array(p0) - c
  382 + v1 = np.array(p1) - c
  383 +
  384 + c[1] = -c[1]
  385 + v0[1] = -v0[1]
  386 + v1[1] = -v1[1]
  387 +
  388 + s0 = np.linalg.norm(v0)
  389 + s1 = np.linalg.norm(v1)
  390 +
  391 + a0 = np.arctan2(v0[1] , v0[0])
  392 + a1 = np.arctan2(v1[1] , v1[0])
  393 +
  394 + if (a1 - a0) % (np.pi*2) < (a0 - a1) % (np.pi*2):
  395 + sa = a0
  396 + ea = a1
  397 + else:
  398 + sa = a1
  399 + ea = a0
  400 +
  401 + path = gc.CreatePath()
  402 + path.AddArc((c[0], c[1]), min(s0, s1), sa, ea)
  403 + gc.StrokePath(path)
  404 +
147 405  
148 406 class Viewer(wx.Panel):
149 407  
... ... @@ -157,7 +415,7 @@ class Viewer(wx.Panel):
157 415  
158 416 self._number_slices = const.PROJECTION_MIP_SIZE
159 417 self._mip_inverted = False
160   -
  418 +
161 419 self.style = None
162 420 self.last_position_mouse_move = ()
163 421 self.state = const.STATE_DEFAULT
... ... @@ -178,6 +436,7 @@ class Viewer(wx.Panel):
178 436  
179 437 self.orientation = orientation
180 438 self.slice_number = 0
  439 + self.scroll_enabled = True
181 440  
182 441 self.__init_gui()
183 442  
... ... @@ -230,7 +489,7 @@ class Viewer(wx.Panel):
230 489 self.menu.caller = self
231 490 self.PopupMenu(self.menu)
232 491 evt.Skip()
233   -
  492 +
234 493 def SetPopupMenu(self, menu):
235 494 self.menu = menu
236 495  
... ... @@ -316,14 +575,14 @@ class Viewer(wx.Panel):
316 575 (slc.window_width, slc.window_level))
317 576  
318 577 def SetWLText(self, window_width, window_level):
319   - value = STR_WL%(window_level, window_width)
  578 + value = STR_WL%(window_level, window_width)
320 579 if (self.wl_text):
321 580 self.wl_text.SetValue(value)
322 581 #self.interactor.Render()
323 582  
324 583 def EnableText(self):
325 584 if not (self.wl_text):
326   - proj = project.Project()
  585 + proj = project.Project()
327 586 colour = const.ORIENTATION_COLOUR[self.orientation]
328 587  
329 588 # Window & Level text
... ... @@ -336,7 +595,7 @@ class Viewer(wx.Panel):
336 595 values = [_('P'), _('A'), _('T'), _('B')]
337 596 else:
338 597 values = [_('R'), _('L'), _('T'), _('B')]
339   -
  598 +
340 599 left_text = self.left_text = vtku.TextZero()
341 600 left_text.ShadowOff()
342 601 left_text.SetColour(colour)
... ... @@ -415,30 +674,30 @@ class Viewer(wx.Panel):
415 674  
416 675 elif(croll > 91 and croll <= 135):
417 676 self.RenderTextDirection([_("LP"), _("AL"), _("RA"), _("PR")])
418   -
  677 +
419 678 elif(croll > 135 and croll <= 177):
420 679 self.RenderTextDirection([_("PL"), _("LA"), _("AR"), _("RP")])
421   -
  680 +
422 681 elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177):
423 682 self.RenderTextDirection([_("P"), _("L"), _("A"), _("R")])
424   -
  683 +
425 684 elif(croll >= -177 and croll <= -133):
426 685 self.RenderTextDirection([_("PR"), _("LP"), _("AL"), _("RA")])
427   -
  686 +
428 687 elif(croll >= -132 and croll <= -101):
429 688 self.RenderTextDirection([_("RP"), _("PL"), _("LA"), _("AR")])
430 689  
431 690 elif(croll >= -101 and croll <= -87):
432 691 self.RenderTextDirection([_("R"), _("P"), _("L"), _("A")])
433   -
  692 +
434 693 elif(croll >= -86 and croll <= -42):
435 694 self.RenderTextDirection([_("RA"), _("PR"), _("LP"), _("AL")])
436   -
  695 +
437 696 elif(croll >= -41 and croll <= -2):
438 697 self.RenderTextDirection([_("AR"), _("RP"), _("PL"), _("LA")])
439 698  
440 699 elif(self.orientation == "CORONAL"):
441   -
  700 +
442 701 if (croll >= -2 and croll <= 1):
443 702 self.RenderTextDirection([_("T"), _("R"), _("B"), _("L")])
444 703  
... ... @@ -453,25 +712,25 @@ class Viewer(wx.Panel):
453 712  
454 713 elif(croll > 91 and croll <= 135):
455 714 self.RenderTextDirection([_("LB"), _("TL"), _("RT"), _("BR")])
456   -
  715 +
457 716 elif(croll > 135 and croll <= 177):
458 717 self.RenderTextDirection([_("BL"), _("LT"), _("TR"), _("RB")])
459   -
  718 +
460 719 elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177):
461 720 self.RenderTextDirection([_("B"), _("L"), _("T"), _("R")])
462   -
  721 +
463 722 elif(croll >= -177 and croll <= -133):
464 723 self.RenderTextDirection([_("BR"), _("LB"), _("TL"), _("RT")])
465   -
  724 +
466 725 elif(croll >= -132 and croll <= -101):
467 726 self.RenderTextDirection([_("RB"), _("BL"), _("LT"), _("TR")])
468 727  
469 728 elif(croll >= -101 and croll <= -87):
470 729 self.RenderTextDirection([_("R"), _("B"), _("L"), _("T")])
471   -
  730 +
472 731 elif(croll >= -86 and croll <= -42):
473 732 self.RenderTextDirection([_("RT"), _("BR"), _("LB"), _("TL")])
474   -
  733 +
475 734 elif(croll >= -41 and croll <= -2):
476 735 self.RenderTextDirection([_("TR"), _("RB"), _("BL"), _("LT")])
477 736  
... ... @@ -479,13 +738,13 @@ class Viewer(wx.Panel):
479 738  
480 739 if(croll >= -101 and croll <= -87):
481 740 self.RenderTextDirection([_("T"), _("P"), _("B"), _("A")])
482   -
  741 +
483 742 elif(croll >= -86 and croll <= -42):
484 743 self.RenderTextDirection([_("TA"), _("PT"), _("BP"), _("AB")])
485   -
  744 +
486 745 elif(croll >= -41 and croll <= -2):
487 746 self.RenderTextDirection([_("AT"), _("TP"), _("PB"), _("BA")])
488   -
  747 +
489 748 elif (croll >= -2 and croll <= 1):
490 749 self.RenderTextDirection([_("A"), _("T"), _("P"), _("B")])
491 750  
... ... @@ -500,16 +759,16 @@ class Viewer(wx.Panel):
500 759  
501 760 elif(croll > 91 and croll <= 135):
502 761 self.RenderTextDirection([_("BP"), _("AB"), _("TA"), _("PT")])
503   -
  762 +
504 763 elif(croll > 135 and croll <= 177):
505 764 self.RenderTextDirection([_("PB"), _("BA"), _("AT"), _("TP")])
506   -
  765 +
507 766 elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177):
508 767 self.RenderTextDirection([_("P"), _("B"), _("A"), _("T")])
509   -
  768 +
510 769 elif(croll >= -177 and croll <= -133):
511 770 self.RenderTextDirection([_("PT"), _("BP"), _("AB"), _("TA")])
512   -
  771 +
513 772 elif(croll >= -132 and croll <= -101):
514 773 self.RenderTextDirection([_("TP"), _("PB"), _("BA"), _("AT")])
515 774  
... ... @@ -544,12 +803,12 @@ class Viewer(wx.Panel):
544 803 def Navigation(self, pubsub_evt):
545 804 # Get point from base change
546 805 x, y, z = pubsub_evt.data
547   - coord_cross = x, y, z
  806 + coord_cross = x, y, z
548 807 position = self.slice_data.actor.GetInput().FindPoint(x, y, z)
549 808 coord_cross = self.slice_data.actor.GetInput().GetPoint(position)
550   - coord = self.calcultate_scroll_position(position)
  809 + coord = self.calcultate_scroll_position(position)
551 810 Publisher.sendMessage('Update cross position', coord_cross)
552   -
  811 +
553 812 self.ScrollSlice(coord)
554 813 self.interactor.Render()
555 814  
... ... @@ -574,7 +833,7 @@ class Viewer(wx.Panel):
574 833 #for slice_data in self.slice_data_list:
575 834 #if slice_data.renderer is render:
576 835 #return slice_data
577   - # WARN: Return the only slice_data used in this slice_viewer.
  836 + # WARN: Return the only slice_data used in this slice_viewer.
578 837 return self.slice_data
579 838  
580 839 def calcultate_scroll_position(self, position):
... ... @@ -701,7 +960,7 @@ class Viewer(wx.Panel):
701 960 'Hide text actors on viewers')
702 961 Publisher.subscribe(self.OnExportPicture,'Export picture to file')
703 962 Publisher.subscribe(self.SetDefaultCursor, 'Set interactor default cursor')
704   -
  963 +
705 964 Publisher.subscribe(self.AddActors, 'Add actors ' + str(ORIENTATIONS[self.orientation]))
706 965 Publisher.subscribe(self.RemoveActors, 'Remove actors ' + str(ORIENTATIONS[self.orientation]))
707 966 Publisher.subscribe(self.OnSwapVolumeAxes, 'Swap volume axes')
... ... @@ -727,11 +986,11 @@ class Viewer(wx.Panel):
727 986  
728 987 def SetDefaultCursor(self, pusub_evt):
729 988 self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
730   -
  989 +
731 990 def OnExportPicture(self, pubsub_evt):
732 991 Publisher.sendMessage('Begin busy cursor')
733 992 view_prop_list = []
734   - view_prop_list.append(self.slice_data.box_actor)
  993 + view_prop_list.append(self.slice_data.box_actor)
735 994 self.slice_data.renderer.RemoveViewProp(self.slice_data.box_actor)
736 995  
737 996 id, filename, filetype = pubsub_evt.data
... ... @@ -793,7 +1052,7 @@ class Viewer(wx.Panel):
793 1052 def CloseProject(self):
794 1053 for slice_data in self.slice_data_list:
795 1054 del slice_data
796   -
  1055 +
797 1056 self.slice_data_list = []
798 1057 self.layout = (1, 1)
799 1058 self.orientation_texts = []
... ... @@ -805,10 +1064,10 @@ class Viewer(wx.Panel):
805 1064 def OnSetInteractorStyle(self, pubsub_evt):
806 1065 state = pubsub_evt.data
807 1066 self.SetInteractorStyle(state)
808   -
  1067 +
809 1068 if (state != const.SLICE_STATE_EDITOR):
810 1069 Publisher.sendMessage('Set interactor default cursor')
811   -
  1070 +
812 1071 def __bind_events_wx(self):
813 1072 self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar)
814 1073 self.scroll.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnScrollBarRelease)
... ... @@ -896,6 +1155,8 @@ class Viewer(wx.Panel):
896 1155 self.cam = self.slice_data.renderer.GetActiveCamera()
897 1156 self.__build_cross_lines()
898 1157  
  1158 + canvas = CanvasRendererCTX(self)
  1159 +
899 1160 # Set the slice number to the last slice to ensure the camera if far
900 1161 # enough to show all slices.
901 1162 self.set_slice_number(max_slice_number - 1)
... ... @@ -958,13 +1219,20 @@ class Viewer(wx.Panel):
958 1219 renderer.SetLayer(0)
959 1220 cam = renderer.GetActiveCamera()
960 1221  
  1222 + canvas_renderer = vtk.vtkRenderer()
  1223 + canvas_renderer.SetLayer(1)
  1224 + canvas_renderer.SetActiveCamera(cam)
  1225 + canvas_renderer.SetInteractive(0)
  1226 + canvas_renderer.PreserveDepthBufferOn()
  1227 +
961 1228 overlay_renderer = vtk.vtkRenderer()
962   - overlay_renderer.SetLayer(1)
  1229 + overlay_renderer.SetLayer(2)
963 1230 overlay_renderer.SetActiveCamera(cam)
964 1231 overlay_renderer.SetInteractive(0)
965   -
966   - self.interactor.GetRenderWindow().SetNumberOfLayers(2)
  1232 +
  1233 + self.interactor.GetRenderWindow().SetNumberOfLayers(3)
967 1234 self.interactor.GetRenderWindow().AddRenderer(overlay_renderer)
  1235 + self.interactor.GetRenderWindow().AddRenderer(canvas_renderer)
968 1236 self.interactor.GetRenderWindow().AddRenderer(renderer)
969 1237  
970 1238 actor = vtk.vtkImageActor()
... ... @@ -974,12 +1242,14 @@ class Viewer(wx.Panel):
974 1242 slice_data = sd.SliceData()
975 1243 slice_data.SetOrientation(self.orientation)
976 1244 slice_data.renderer = renderer
  1245 + slice_data.canvas_renderer = canvas_renderer
977 1246 slice_data.overlay_renderer = overlay_renderer
978 1247 slice_data.actor = actor
979 1248 slice_data.SetBorderStyle(sd.BORDER_ALL)
980 1249 renderer.AddActor(actor)
981 1250 renderer.AddActor(slice_data.text.actor)
982 1251 renderer.AddViewProp(slice_data.box_actor)
  1252 +
983 1253 return slice_data
984 1254  
985 1255 def __update_camera(self):
... ... @@ -1027,15 +1297,15 @@ class Viewer(wx.Panel):
1027 1297 def set_scroll_position(self, position):
1028 1298 self.scroll.SetThumbPosition(position)
1029 1299 self.OnScrollBar()
1030   -
  1300 +
1031 1301 def UpdateSlice3D(self, pos):
1032 1302 original_orientation = project.Project().original_orientation
1033 1303 pos = self.scroll.GetThumbPosition()
1034 1304 Publisher.sendMessage('Change slice from slice plane',\
1035 1305 (self.orientation, pos))
1036   -
  1306 +
1037 1307 def OnScrollBar(self, evt=None, update3D=True):
1038   - pos = self.scroll.GetThumbPosition()
  1308 + pos = self.scroll.GetThumbPosition()
1039 1309 self.set_slice_number(pos)
1040 1310 if update3D:
1041 1311 self.UpdateSlice3D(pos)
... ... @@ -1044,14 +1314,14 @@ class Viewer(wx.Panel):
1044 1314 # the actual orientation.
1045 1315 focal_point = self.cross.GetFocalPoint()
1046 1316 Publisher.sendMessage('Update cross position', focal_point)
1047   - Publisher.sendMessage('Update slice viewer')
  1317 + Publisher.sendMessage('Update slice viewer')
1048 1318 else:
1049   - self.interactor.Render()
  1319 + self.interactor.Render()
1050 1320 if evt:
1051 1321 if self._flush_buffer:
1052 1322 self.slice_.apply_slice_buffer_to_mask(self.orientation)
1053 1323 evt.Skip()
1054   -
  1324 +
1055 1325 def OnScrollBarRelease(self, evt):
1056 1326 pos = self.scroll.GetThumbPosition()
1057 1327 evt.Skip()
... ... @@ -1077,7 +1347,7 @@ class Viewer(wx.Panel):
1077 1347 if (evt.GetKeyCode() == wx.WXK_UP and pos > min):
1078 1348 self.OnScrollForward()
1079 1349 self.OnScrollBar()
1080   -
  1350 +
1081 1351 elif (evt.GetKeyCode() == wx.WXK_DOWN and pos < max):
1082 1352 self.OnScrollBackward()
1083 1353 self.OnScrollBar()
... ... @@ -1101,7 +1371,7 @@ class Viewer(wx.Panel):
1101 1371 Publisher.sendMessage('Set projection type', projections[evt.GetKeyCode()])
1102 1372 Publisher.sendMessage('Reload actual slice')
1103 1373 skip = False
1104   -
  1374 +
1105 1375 self.UpdateSlice3D(pos)
1106 1376 self.interactor.Render()
1107 1377  
... ... @@ -1109,20 +1379,24 @@ class Viewer(wx.Panel):
1109 1379 evt.Skip()
1110 1380  
1111 1381 def OnScrollForward(self, evt=None, obj=None):
  1382 + if not self.scroll_enabled:
  1383 + return
1112 1384 pos = self.scroll.GetThumbPosition()
1113 1385 min = 0
1114   -
  1386 +
1115 1387 if(pos > min):
1116 1388 if self._flush_buffer:
1117 1389 self.slice_.apply_slice_buffer_to_mask(self.orientation)
1118 1390 pos = pos - 1
1119 1391 self.scroll.SetThumbPosition(pos)
1120 1392 self.OnScrollBar()
1121   -
  1393 +
1122 1394 def OnScrollBackward(self, evt=None, obj=None):
  1395 + if not self.scroll_enabled:
  1396 + return
1123 1397 pos = self.scroll.GetThumbPosition()
1124 1398 max = self.slice_.GetMaxSliceNumber(self.orientation)
1125   -
  1399 +
1126 1400 if(pos < max):
1127 1401 if self._flush_buffer:
1128 1402 self.slice_.apply_slice_buffer_to_mask(self.orientation)
... ... @@ -1131,7 +1405,7 @@ class Viewer(wx.Panel):
1131 1405 self.OnScrollBar()
1132 1406  
1133 1407 def OnSize(self, evt):
1134   - w, h = evt.GetSize()
  1408 + w, h = evt.GetSize()
1135 1409 w = float(w)
1136 1410 h = float(h)
1137 1411 if self.slice_data:
... ... @@ -1168,7 +1442,7 @@ class Viewer(wx.Panel):
1168 1442 self.mip_ctrls.Hide()
1169 1443 self.GetSizer().Remove(self.mip_ctrls)
1170 1444 self.Layout()
1171   -
  1445 +
1172 1446 def OnSetOverwriteMask(self, pubsub_evt):
1173 1447 value = pubsub_evt.data
1174 1448 self.overwrite_mask = value
... ... @@ -1184,14 +1458,14 @@ class Viewer(wx.Panel):
1184 1458 for actor in self.actors_by_slice_number[index]:
1185 1459 self.slice_data.renderer.AddActor(actor)
1186 1460  
1187   - for (m, mr) in self.measures.get(self.orientation, self.slice_data.number):
1188   - for actor in mr.GetActors():
1189   - self.slice_data.renderer.RemoveActor(actor)
  1461 + # for (m, mr) in self.measures.get(self.orientation, self.slice_data.number):
  1462 + # for actor in mr.GetActors():
  1463 + # self.slice_data.renderer.RemoveActor(actor)
1190 1464  
1191   - for (m, mr) in self.measures.get(self.orientation, index):
1192   - mr.renderer = self.slice_data.renderer
1193   - for actor in mr.GetActors():
1194   - self.slice_data.renderer.AddActor(actor)
  1465 + # for (m, mr) in self.measures.get(self.orientation, index):
  1466 + # mr.renderer = self.slice_data.renderer
  1467 + # for actor in mr.GetActors():
  1468 + # self.slice_data.renderer.AddActor(actor)
1195 1469  
1196 1470 if self.slice_._type_projection == const.PROJECTION_NORMAL:
1197 1471 self.slice_data.SetNumber(index)
... ... @@ -1225,7 +1499,7 @@ class Viewer(wx.Panel):
1225 1499 # orientation
1226 1500 axis0, axis1 = pubsub_evt.data
1227 1501 cursor = self.slice_data.cursor
1228   - spacing = cursor.spacing
  1502 + spacing = cursor.spacing
1229 1503 if (axis0, axis1) == (2, 1):
1230 1504 cursor.SetSpacing((spacing[1], spacing[0], spacing[2]))
1231 1505 elif (axis0, axis1) == (2, 0):
... ...
invesalius/gui/data_notebook.py
... ... @@ -943,6 +943,7 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
943 943 Publisher.subscribe(self.OnShowSingle, 'Show single measurement')
944 944 Publisher.subscribe(self.OnShowMultiple, 'Show multiple measurements')
945 945 Publisher.subscribe(self.OnLoadData, 'Load measurement dict')
  946 + Publisher.subscribe(self.OnRemoveGUIMeasure, 'Remove GUI measurement')
946 947  
947 948 def __bind_events_wx(self):
948 949 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
... ... @@ -959,6 +960,19 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
959 960 elif (keycode == wx.WXK_DELETE):
960 961 self.RemoveMeasurements()
961 962  
  963 + def OnRemoveGUIMeasure(self, pubsub_evt):
  964 + idx = pubsub_evt.data
  965 + self.DeleteItem(idx)
  966 +
  967 + old_dict = self._list_index
  968 + new_dict = {}
  969 + j = 0
  970 + for i in old_dict:
  971 + if i != idx:
  972 + new_dict[j] = old_dict[i]
  973 + j+=1
  974 + self._list_index = new_dict
  975 +
962 976 def RemoveMeasurements(self):
963 977 """
964 978 Remove items selected.
... ... @@ -1105,7 +1119,7 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
1105 1119 value = (u"%.2f°") % m.value
1106 1120 self.InsertNewItem(m.index, m.name, colour, location, type, value)
1107 1121  
1108   - if not m.is_shown:
  1122 + if not m.visible:
1109 1123 self.SetItemImage(i, False)
1110 1124  
1111 1125 def AddItem_(self, pubsub_evt):
... ...
invesalius/project.py
... ... @@ -190,7 +190,7 @@ class Project(object):
190 190 item["type"] = m.type
191 191 item["slice_number"] = m.slice_number
192 192 item["points"] = m.points
193   - item["visible"] = m.is_shown
  193 + item["visible"] = m.visible
194 194 measures[str(m.index)] = item
195 195 return measures
196 196  
... ...