Commit 76643a9e3602a8d1ac3bacd933812b19711dbe25
Committed by
GitHub
Exists in
master
and in
25 other branches
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.
Showing
7 changed files
with
570 additions
and
120 deletions
Show diff stats
invesalius/data/converters.py
@@ -56,3 +56,25 @@ def to_vtk(n_array, spacing, slice_number, orientation): | @@ -56,3 +56,25 @@ def to_vtk(n_array, spacing, slice_number, orientation): | ||
56 | image_copy.DeepCopy(image) | 56 | image_copy.DeepCopy(image) |
57 | 57 | ||
58 | return image_copy | 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,6 +5,8 @@ import math | ||
5 | import random | 5 | import random |
6 | 6 | ||
7 | from wx.lib.pubsub import pub as Publisher | 7 | from wx.lib.pubsub import pub as Publisher |
8 | + | ||
9 | +import numpy as np | ||
8 | import vtk | 10 | import vtk |
9 | 11 | ||
10 | import constants as const | 12 | import constants as const |
@@ -137,7 +139,7 @@ class MeasurementManager(object): | @@ -137,7 +139,7 @@ class MeasurementManager(object): | ||
137 | (actors, m.slice_number)) | 139 | (actors, m.slice_number)) |
138 | self.current = None | 140 | self.current = None |
139 | 141 | ||
140 | - if not m.is_shown: | 142 | + if not m.visible: |
141 | mr.SetVisibility(False) | 143 | mr.SetVisibility(False) |
142 | if m.location == const.SURFACE: | 144 | if m.location == const.SURFACE: |
143 | Publisher.sendMessage('Render volume viewer') | 145 | Publisher.sendMessage('Render volume viewer') |
@@ -307,7 +309,7 @@ class MeasurementManager(object): | @@ -307,7 +309,7 @@ class MeasurementManager(object): | ||
307 | def _set_visibility(self, pubsub_evt): | 309 | def _set_visibility(self, pubsub_evt): |
308 | index, visibility = pubsub_evt.data | 310 | index, visibility = pubsub_evt.data |
309 | m, mr = self.measures[index] | 311 | m, mr = self.measures[index] |
310 | - m.is_shown = visibility | 312 | + m.visible = visibility |
311 | mr.SetVisibility(visibility) | 313 | mr.SetVisibility(visibility) |
312 | if m.location == const.SURFACE: | 314 | if m.location == const.SURFACE: |
313 | Publisher.sendMessage('Render volume viewer') | 315 | Publisher.sendMessage('Render volume viewer') |
@@ -318,22 +320,24 @@ class MeasurementManager(object): | @@ -318,22 +320,24 @@ class MeasurementManager(object): | ||
318 | if self.current is None: | 320 | if self.current is None: |
319 | return | 321 | return |
320 | 322 | ||
321 | - mr = self.current[1] | ||
322 | - print "RM INC M", self.current, mr.IsComplete() | 323 | + m, mr = self.current |
323 | if not mr.IsComplete(): | 324 | if not mr.IsComplete(): |
324 | print "---To REMOVE" | 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 | actors = mr.GetActors() | 329 | actors = mr.GetActors() |
327 | slice_number = self.current[0].slice_number | 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 | if self.current[0].location == const.SURFACE: | 334 | if self.current[0].location == const.SURFACE: |
331 | Publisher.sendMessage('Render volume viewer') | 335 | Publisher.sendMessage('Render volume viewer') |
332 | else: | 336 | else: |
333 | Publisher.sendMessage('Update slice viewer') | 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 | self.current = None | 341 | self.current = None |
338 | 342 | ||
339 | 343 | ||
@@ -349,7 +353,7 @@ class Measurement(): | @@ -349,7 +353,7 @@ class Measurement(): | ||
349 | self.type = const.LINEAR # ANGULAR | 353 | self.type = const.LINEAR # ANGULAR |
350 | self.slice_number = 0 | 354 | self.slice_number = 0 |
351 | self.points = [] | 355 | self.points = [] |
352 | - self.is_shown = True | 356 | + self.visible = True |
353 | 357 | ||
354 | def Load(self, info): | 358 | def Load(self, info): |
355 | self.index = info["index"] | 359 | self.index = info["index"] |
@@ -360,7 +364,7 @@ class Measurement(): | @@ -360,7 +364,7 @@ class Measurement(): | ||
360 | self.type = info["type"] | 364 | self.type = info["type"] |
361 | self.slice_number = info["slice_number"] | 365 | self.slice_number = info["slice_number"] |
362 | self.points = info["points"] | 366 | self.points = info["points"] |
363 | - self.is_shown = info["visible"] | 367 | + self.visible = info["visible"] |
364 | 368 | ||
365 | class CirclePointRepresentation(object): | 369 | class CirclePointRepresentation(object): |
366 | """ | 370 | """ |
@@ -559,12 +563,38 @@ class LinearMeasure(object): | @@ -559,12 +563,38 @@ class LinearMeasure(object): | ||
559 | a.GetProperty().SetOpacity(0.75) | 563 | a.GetProperty().SetOpacity(0.75) |
560 | self.text_actor = a | 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 | def GetNumberOfPoints(self): | 589 | def GetNumberOfPoints(self): |
563 | return len(self.points) | 590 | return len(self.points) |
564 | 591 | ||
565 | def GetValue(self): | 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 | def SetRenderer(self, renderer): | 599 | def SetRenderer(self, renderer): |
570 | if self.point_actor1: | 600 | if self.point_actor1: |
@@ -607,21 +637,22 @@ class LinearMeasure(object): | @@ -607,21 +637,22 @@ class LinearMeasure(object): | ||
607 | return actors | 637 | return actors |
608 | 638 | ||
609 | def Remove(self): | 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 | # def __del__(self): | 657 | # def __del__(self): |
627 | # self.Remove() | 658 | # self.Remove() |
@@ -630,7 +661,7 @@ class LinearMeasure(object): | @@ -630,7 +661,7 @@ class LinearMeasure(object): | ||
630 | class AngularMeasure(object): | 661 | class AngularMeasure(object): |
631 | def __init__(self, colour=(1, 0, 0), representation=None): | 662 | def __init__(self, colour=(1, 0, 0), representation=None): |
632 | self.colour = colour | 663 | self.colour = colour |
633 | - self.points = [0, 0, 0] | 664 | + self.points = [] |
634 | self.number_of_points = 0 | 665 | self.number_of_points = 0 |
635 | self.point_actor1 = None | 666 | self.point_actor1 = None |
636 | self.point_actor2 = None | 667 | self.point_actor2 = None |
@@ -658,7 +689,7 @@ class AngularMeasure(object): | @@ -658,7 +689,7 @@ class AngularMeasure(object): | ||
658 | 689 | ||
659 | def SetPoint1(self, x, y, z): | 690 | def SetPoint1(self, x, y, z): |
660 | if self.number_of_points == 0: | 691 | if self.number_of_points == 0: |
661 | - self.points[0] = (x, y, z) | 692 | + self.points.append((x, y, z)) |
662 | self.number_of_points = 1 | 693 | self.number_of_points = 1 |
663 | self.point_actor1 = self.representation.GetRepresentation(x, y, z) | 694 | self.point_actor1 = self.representation.GetRepresentation(x, y, z) |
664 | else: | 695 | else: |
@@ -677,7 +708,7 @@ class AngularMeasure(object): | @@ -677,7 +708,7 @@ class AngularMeasure(object): | ||
677 | def SetPoint2(self, x, y, z): | 708 | def SetPoint2(self, x, y, z): |
678 | if self.number_of_points == 1: | 709 | if self.number_of_points == 1: |
679 | self.number_of_points = 2 | 710 | self.number_of_points = 2 |
680 | - self.points[1] = (x, y, z) | 711 | + self.points.append((x, y, z)) |
681 | self.point_actor2 = self.representation.GetRepresentation(x, y, z) | 712 | self.point_actor2 = self.representation.GetRepresentation(x, y, z) |
682 | else: | 713 | else: |
683 | self.points[1] = (x, y, z) | 714 | self.points[1] = (x, y, z) |
@@ -695,7 +726,7 @@ class AngularMeasure(object): | @@ -695,7 +726,7 @@ class AngularMeasure(object): | ||
695 | def SetPoint3(self, x, y, z): | 726 | def SetPoint3(self, x, y, z): |
696 | if self.number_of_points == 2: | 727 | if self.number_of_points == 2: |
697 | self.number_of_points = 3 | 728 | self.number_of_points = 3 |
698 | - self.points[2] = (x, y, z) | 729 | + self.points.append((x, y, z)) |
699 | self.point_actor3 = self.representation.GetRepresentation(x, y, z) | 730 | self.point_actor3 = self.representation.GetRepresentation(x, y, z) |
700 | self.CreateMeasure() | 731 | self.CreateMeasure() |
701 | else: | 732 | else: |
@@ -793,11 +824,41 @@ class AngularMeasure(object): | @@ -793,11 +824,41 @@ class AngularMeasure(object): | ||
793 | a.GetPositionCoordinate().SetValue(x,y,z) | 824 | a.GetPositionCoordinate().SetValue(x,y,z) |
794 | self.text_actor = a | 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 | def GetNumberOfPoints(self): | 854 | def GetNumberOfPoints(self): |
797 | return self.number_of_points | 855 | return self.number_of_points |
798 | 856 | ||
799 | def GetValue(self): | 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 | def SetVisibility(self, v): | 863 | def SetVisibility(self, v): |
803 | self.point_actor1.SetVisibility(v) | 864 | self.point_actor1.SetVisibility(v) |
@@ -835,30 +896,35 @@ class AngularMeasure(object): | @@ -835,30 +896,35 @@ class AngularMeasure(object): | ||
835 | v2 = [j-i for i,j in zip(self.points[2], self.points[1])] | 896 | v2 = [j-i for i,j in zip(self.points[2], self.points[1])] |
836 | #print vtk.vtkMath.Normalize(v1) | 897 | #print vtk.vtkMath.Normalize(v1) |
837 | #print vtk.vtkMath.Normalize(v2) | 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 | angle = math.degrees(math.acos(cos)) | 904 | angle = math.degrees(math.acos(cos)) |
840 | return angle | 905 | return angle |
841 | 906 | ||
842 | def Remove(self): | 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 | def SetRenderer(self, renderer): | 929 | def SetRenderer(self, renderer): |
864 | if self.point_actor1: | 930 | if self.point_actor1: |
invesalius/data/slice_data.py
@@ -38,6 +38,7 @@ class SliceData(object): | @@ -38,6 +38,7 @@ class SliceData(object): | ||
38 | self.number = 0 | 38 | self.number = 0 |
39 | self.orientation = 'AXIAL' | 39 | self.orientation = 'AXIAL' |
40 | self.renderer = None | 40 | self.renderer = None |
41 | + self.canvas_renderer = None | ||
41 | self.overlay_renderer = None | 42 | self.overlay_renderer = None |
42 | self.__create_text() | 43 | self.__create_text() |
43 | self.__create_box() | 44 | self.__create_box() |
invesalius/data/styles.py
@@ -354,6 +354,7 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): | @@ -354,6 +354,7 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): | ||
354 | 354 | ||
355 | self.measures = MeasureData() | 355 | self.measures = MeasureData() |
356 | self.selected = None | 356 | self.selected = None |
357 | + self.creating = None | ||
357 | 358 | ||
358 | self._type = const.LINEAR | 359 | self._type = const.LINEAR |
359 | 360 | ||
@@ -377,25 +378,57 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): | @@ -377,25 +378,57 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): | ||
377 | self._bind_events() | 378 | self._bind_events() |
378 | 379 | ||
379 | def _bind_events(self): | 380 | def _bind_events(self): |
380 | - self.AddObserver("LeftButtonPressEvent", self.OnInsertLinearMeasurePoint) | 381 | + self.AddObserver("LeftButtonPressEvent", self.OnInsertMeasurePoint) |
381 | self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseMeasurePoint) | 382 | self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseMeasurePoint) |
382 | self.AddObserver("MouseMoveEvent", self.OnMoveMeasurePoint) | 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 | slice_number = self.slice_data.number | 387 | slice_number = self.slice_data.number |
386 | x, y, z = self._get_pos_clicked() | 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 | if selected: | 413 | if selected: |
390 | self.selected = selected | 414 | self.selected = selected |
415 | + self.viewer.scroll_enabled = False | ||
391 | else: | 416 | else: |
392 | if self.picker.GetViewProp(): | 417 | if self.picker.GetViewProp(): |
393 | renderer = self.viewer.slice_data.renderer | 418 | renderer = self.viewer.slice_data.renderer |
394 | Publisher.sendMessage("Add measurement point", | 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 | ORIENTATIONS[self.orientation], | 425 | ORIENTATIONS[self.orientation], |
397 | slice_number, self.radius)) | 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 | Publisher.sendMessage('Reload actual slice %s' % self.orientation) | 430 | Publisher.sendMessage('Reload actual slice %s' % self.orientation) |
431 | + self.viewer.scroll_enabled = False | ||
399 | 432 | ||
400 | def OnReleaseMeasurePoint(self, obj, evt): | 433 | def OnReleaseMeasurePoint(self, obj, evt): |
401 | if self.selected: | 434 | if self.selected: |
@@ -405,16 +438,41 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): | @@ -405,16 +438,41 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): | ||
405 | Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z))) | 438 | Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z))) |
406 | Publisher.sendMessage('Reload actual slice %s' % self.orientation) | 439 | Publisher.sendMessage('Reload actual slice %s' % self.orientation) |
407 | self.selected = None | 440 | self.selected = None |
441 | + self.viewer.scroll_enabled = True | ||
408 | 442 | ||
409 | def OnMoveMeasurePoint(self, obj, evt): | 443 | def OnMoveMeasurePoint(self, obj, evt): |
444 | + x, y, z = self._get_pos_clicked() | ||
410 | if self.selected: | 445 | if self.selected: |
411 | n, m, mr = self.selected | 446 | n, m, mr = self.selected |
412 | - x, y, z = self._get_pos_clicked() | ||
413 | idx = self.measures._list_measures.index((m, mr)) | 447 | idx = self.measures._list_measures.index((m, mr)) |
414 | Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z))) | 448 | Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z))) |
415 | 449 | ||
416 | Publisher.sendMessage('Reload actual slice %s' % self.orientation) | 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 | def CleanUp(self): | 476 | def CleanUp(self): |
419 | self.picker.PickFromListOff() | 477 | self.picker.PickFromListOff() |
420 | Publisher.sendMessage("Remove incomplete measurements") | 478 | Publisher.sendMessage("Remove incomplete measurements") |
@@ -449,6 +507,21 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): | @@ -449,6 +507,21 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): | ||
449 | return (n, m, mr) | 507 | return (n, m, mr) |
450 | return None | 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 | class AngularMeasureInteractorStyle(LinearMeasureInteractorStyle): | 525 | class AngularMeasureInteractorStyle(LinearMeasureInteractorStyle): |
453 | def __init__(self, viewer): | 526 | def __init__(self, viewer): |
454 | LinearMeasureInteractorStyle.__init__(self, viewer) | 527 | LinearMeasureInteractorStyle.__init__(self, viewer) |
invesalius/data/viewer_slice.py
@@ -23,7 +23,7 @@ import collections | @@ -23,7 +23,7 @@ import collections | ||
23 | import itertools | 23 | import itertools |
24 | import tempfile | 24 | import tempfile |
25 | 25 | ||
26 | -import numpy | 26 | +import numpy as np |
27 | 27 | ||
28 | import vtk | 28 | import vtk |
29 | from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor | 29 | from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor |
@@ -46,6 +46,8 @@ import project | @@ -46,6 +46,8 @@ import project | ||
46 | import slice_data as sd | 46 | import slice_data as sd |
47 | import utils | 47 | import utils |
48 | 48 | ||
49 | +from data import converters | ||
50 | + | ||
49 | from data import measures | 51 | from data import measures |
50 | 52 | ||
51 | ID_TO_TOOL_ITEM = {} | 53 | ID_TO_TOOL_ITEM = {} |
@@ -87,7 +89,7 @@ class ContourMIPConfig(wx.Panel): | @@ -87,7 +89,7 @@ class ContourMIPConfig(wx.Panel): | ||
87 | " order to compound the" | 89 | " order to compound the" |
88 | " visualization instead of" | 90 | " visualization instead of" |
89 | " ascending order."))) | 91 | " ascending order."))) |
90 | - | 92 | + |
91 | txt_mip_size = wx.StaticText(self, -1, _("Number of slices"), style=wx.ALIGN_CENTER_HORIZONTAL) | 93 | txt_mip_size = wx.StaticText(self, -1, _("Number of slices"), style=wx.ALIGN_CENTER_HORIZONTAL) |
92 | self.txt_mip_border = wx.StaticText(self, -1, _("Sharpness")) | 94 | self.txt_mip_border = wx.StaticText(self, -1, _("Sharpness")) |
93 | 95 | ||
@@ -111,7 +113,7 @@ class ContourMIPConfig(wx.Panel): | @@ -111,7 +113,7 @@ class ContourMIPConfig(wx.Panel): | ||
111 | self.mip_size_spin.Bind(wx.EVT_SPINCTRL, self.OnSetMIPSize) | 113 | self.mip_size_spin.Bind(wx.EVT_SPINCTRL, self.OnSetMIPSize) |
112 | self.border_spin.Bind(wx.EVT_SPINCTRL, self.OnSetMIPBorder) | 114 | self.border_spin.Bind(wx.EVT_SPINCTRL, self.OnSetMIPBorder) |
113 | self.inverted.Bind(wx.EVT_CHECKBOX, self.OnCheckInverted) | 115 | self.inverted.Bind(wx.EVT_CHECKBOX, self.OnCheckInverted) |
114 | - | 116 | + |
115 | Publisher.subscribe(self._set_projection_type, 'Set projection type') | 117 | Publisher.subscribe(self._set_projection_type, 'Set projection type') |
116 | 118 | ||
117 | def OnSetMIPSize(self, evt): | 119 | def OnSetMIPSize(self, evt): |
@@ -134,7 +136,7 @@ class ContourMIPConfig(wx.Panel): | @@ -134,7 +136,7 @@ class ContourMIPConfig(wx.Panel): | ||
134 | self.inverted.Enable() | 136 | self.inverted.Enable() |
135 | else: | 137 | else: |
136 | self.inverted.Disable() | 138 | self.inverted.Disable() |
137 | - | 139 | + |
138 | if tprojection in (const.PROJECTION_CONTOUR_MIP, | 140 | if tprojection in (const.PROJECTION_CONTOUR_MIP, |
139 | const.PROJECTION_CONTOUR_MIDA): | 141 | const.PROJECTION_CONTOUR_MIDA): |
140 | self.border_spin.Enable() | 142 | self.border_spin.Enable() |
@@ -144,6 +146,262 @@ class ContourMIPConfig(wx.Panel): | @@ -144,6 +146,262 @@ class ContourMIPConfig(wx.Panel): | ||
144 | self.txt_mip_border.Disable() | 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 | class Viewer(wx.Panel): | 406 | class Viewer(wx.Panel): |
149 | 407 | ||
@@ -157,7 +415,7 @@ class Viewer(wx.Panel): | @@ -157,7 +415,7 @@ class Viewer(wx.Panel): | ||
157 | 415 | ||
158 | self._number_slices = const.PROJECTION_MIP_SIZE | 416 | self._number_slices = const.PROJECTION_MIP_SIZE |
159 | self._mip_inverted = False | 417 | self._mip_inverted = False |
160 | - | 418 | + |
161 | self.style = None | 419 | self.style = None |
162 | self.last_position_mouse_move = () | 420 | self.last_position_mouse_move = () |
163 | self.state = const.STATE_DEFAULT | 421 | self.state = const.STATE_DEFAULT |
@@ -178,6 +436,7 @@ class Viewer(wx.Panel): | @@ -178,6 +436,7 @@ class Viewer(wx.Panel): | ||
178 | 436 | ||
179 | self.orientation = orientation | 437 | self.orientation = orientation |
180 | self.slice_number = 0 | 438 | self.slice_number = 0 |
439 | + self.scroll_enabled = True | ||
181 | 440 | ||
182 | self.__init_gui() | 441 | self.__init_gui() |
183 | 442 | ||
@@ -230,7 +489,7 @@ class Viewer(wx.Panel): | @@ -230,7 +489,7 @@ class Viewer(wx.Panel): | ||
230 | self.menu.caller = self | 489 | self.menu.caller = self |
231 | self.PopupMenu(self.menu) | 490 | self.PopupMenu(self.menu) |
232 | evt.Skip() | 491 | evt.Skip() |
233 | - | 492 | + |
234 | def SetPopupMenu(self, menu): | 493 | def SetPopupMenu(self, menu): |
235 | self.menu = menu | 494 | self.menu = menu |
236 | 495 | ||
@@ -316,14 +575,14 @@ class Viewer(wx.Panel): | @@ -316,14 +575,14 @@ class Viewer(wx.Panel): | ||
316 | (slc.window_width, slc.window_level)) | 575 | (slc.window_width, slc.window_level)) |
317 | 576 | ||
318 | def SetWLText(self, window_width, window_level): | 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 | if (self.wl_text): | 579 | if (self.wl_text): |
321 | self.wl_text.SetValue(value) | 580 | self.wl_text.SetValue(value) |
322 | #self.interactor.Render() | 581 | #self.interactor.Render() |
323 | 582 | ||
324 | def EnableText(self): | 583 | def EnableText(self): |
325 | if not (self.wl_text): | 584 | if not (self.wl_text): |
326 | - proj = project.Project() | 585 | + proj = project.Project() |
327 | colour = const.ORIENTATION_COLOUR[self.orientation] | 586 | colour = const.ORIENTATION_COLOUR[self.orientation] |
328 | 587 | ||
329 | # Window & Level text | 588 | # Window & Level text |
@@ -336,7 +595,7 @@ class Viewer(wx.Panel): | @@ -336,7 +595,7 @@ class Viewer(wx.Panel): | ||
336 | values = [_('P'), _('A'), _('T'), _('B')] | 595 | values = [_('P'), _('A'), _('T'), _('B')] |
337 | else: | 596 | else: |
338 | values = [_('R'), _('L'), _('T'), _('B')] | 597 | values = [_('R'), _('L'), _('T'), _('B')] |
339 | - | 598 | + |
340 | left_text = self.left_text = vtku.TextZero() | 599 | left_text = self.left_text = vtku.TextZero() |
341 | left_text.ShadowOff() | 600 | left_text.ShadowOff() |
342 | left_text.SetColour(colour) | 601 | left_text.SetColour(colour) |
@@ -415,30 +674,30 @@ class Viewer(wx.Panel): | @@ -415,30 +674,30 @@ class Viewer(wx.Panel): | ||
415 | 674 | ||
416 | elif(croll > 91 and croll <= 135): | 675 | elif(croll > 91 and croll <= 135): |
417 | self.RenderTextDirection([_("LP"), _("AL"), _("RA"), _("PR")]) | 676 | self.RenderTextDirection([_("LP"), _("AL"), _("RA"), _("PR")]) |
418 | - | 677 | + |
419 | elif(croll > 135 and croll <= 177): | 678 | elif(croll > 135 and croll <= 177): |
420 | self.RenderTextDirection([_("PL"), _("LA"), _("AR"), _("RP")]) | 679 | self.RenderTextDirection([_("PL"), _("LA"), _("AR"), _("RP")]) |
421 | - | 680 | + |
422 | elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177): | 681 | elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177): |
423 | self.RenderTextDirection([_("P"), _("L"), _("A"), _("R")]) | 682 | self.RenderTextDirection([_("P"), _("L"), _("A"), _("R")]) |
424 | - | 683 | + |
425 | elif(croll >= -177 and croll <= -133): | 684 | elif(croll >= -177 and croll <= -133): |
426 | self.RenderTextDirection([_("PR"), _("LP"), _("AL"), _("RA")]) | 685 | self.RenderTextDirection([_("PR"), _("LP"), _("AL"), _("RA")]) |
427 | - | 686 | + |
428 | elif(croll >= -132 and croll <= -101): | 687 | elif(croll >= -132 and croll <= -101): |
429 | self.RenderTextDirection([_("RP"), _("PL"), _("LA"), _("AR")]) | 688 | self.RenderTextDirection([_("RP"), _("PL"), _("LA"), _("AR")]) |
430 | 689 | ||
431 | elif(croll >= -101 and croll <= -87): | 690 | elif(croll >= -101 and croll <= -87): |
432 | self.RenderTextDirection([_("R"), _("P"), _("L"), _("A")]) | 691 | self.RenderTextDirection([_("R"), _("P"), _("L"), _("A")]) |
433 | - | 692 | + |
434 | elif(croll >= -86 and croll <= -42): | 693 | elif(croll >= -86 and croll <= -42): |
435 | self.RenderTextDirection([_("RA"), _("PR"), _("LP"), _("AL")]) | 694 | self.RenderTextDirection([_("RA"), _("PR"), _("LP"), _("AL")]) |
436 | - | 695 | + |
437 | elif(croll >= -41 and croll <= -2): | 696 | elif(croll >= -41 and croll <= -2): |
438 | self.RenderTextDirection([_("AR"), _("RP"), _("PL"), _("LA")]) | 697 | self.RenderTextDirection([_("AR"), _("RP"), _("PL"), _("LA")]) |
439 | 698 | ||
440 | elif(self.orientation == "CORONAL"): | 699 | elif(self.orientation == "CORONAL"): |
441 | - | 700 | + |
442 | if (croll >= -2 and croll <= 1): | 701 | if (croll >= -2 and croll <= 1): |
443 | self.RenderTextDirection([_("T"), _("R"), _("B"), _("L")]) | 702 | self.RenderTextDirection([_("T"), _("R"), _("B"), _("L")]) |
444 | 703 | ||
@@ -453,25 +712,25 @@ class Viewer(wx.Panel): | @@ -453,25 +712,25 @@ class Viewer(wx.Panel): | ||
453 | 712 | ||
454 | elif(croll > 91 and croll <= 135): | 713 | elif(croll > 91 and croll <= 135): |
455 | self.RenderTextDirection([_("LB"), _("TL"), _("RT"), _("BR")]) | 714 | self.RenderTextDirection([_("LB"), _("TL"), _("RT"), _("BR")]) |
456 | - | 715 | + |
457 | elif(croll > 135 and croll <= 177): | 716 | elif(croll > 135 and croll <= 177): |
458 | self.RenderTextDirection([_("BL"), _("LT"), _("TR"), _("RB")]) | 717 | self.RenderTextDirection([_("BL"), _("LT"), _("TR"), _("RB")]) |
459 | - | 718 | + |
460 | elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177): | 719 | elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177): |
461 | self.RenderTextDirection([_("B"), _("L"), _("T"), _("R")]) | 720 | self.RenderTextDirection([_("B"), _("L"), _("T"), _("R")]) |
462 | - | 721 | + |
463 | elif(croll >= -177 and croll <= -133): | 722 | elif(croll >= -177 and croll <= -133): |
464 | self.RenderTextDirection([_("BR"), _("LB"), _("TL"), _("RT")]) | 723 | self.RenderTextDirection([_("BR"), _("LB"), _("TL"), _("RT")]) |
465 | - | 724 | + |
466 | elif(croll >= -132 and croll <= -101): | 725 | elif(croll >= -132 and croll <= -101): |
467 | self.RenderTextDirection([_("RB"), _("BL"), _("LT"), _("TR")]) | 726 | self.RenderTextDirection([_("RB"), _("BL"), _("LT"), _("TR")]) |
468 | 727 | ||
469 | elif(croll >= -101 and croll <= -87): | 728 | elif(croll >= -101 and croll <= -87): |
470 | self.RenderTextDirection([_("R"), _("B"), _("L"), _("T")]) | 729 | self.RenderTextDirection([_("R"), _("B"), _("L"), _("T")]) |
471 | - | 730 | + |
472 | elif(croll >= -86 and croll <= -42): | 731 | elif(croll >= -86 and croll <= -42): |
473 | self.RenderTextDirection([_("RT"), _("BR"), _("LB"), _("TL")]) | 732 | self.RenderTextDirection([_("RT"), _("BR"), _("LB"), _("TL")]) |
474 | - | 733 | + |
475 | elif(croll >= -41 and croll <= -2): | 734 | elif(croll >= -41 and croll <= -2): |
476 | self.RenderTextDirection([_("TR"), _("RB"), _("BL"), _("LT")]) | 735 | self.RenderTextDirection([_("TR"), _("RB"), _("BL"), _("LT")]) |
477 | 736 | ||
@@ -479,13 +738,13 @@ class Viewer(wx.Panel): | @@ -479,13 +738,13 @@ class Viewer(wx.Panel): | ||
479 | 738 | ||
480 | if(croll >= -101 and croll <= -87): | 739 | if(croll >= -101 and croll <= -87): |
481 | self.RenderTextDirection([_("T"), _("P"), _("B"), _("A")]) | 740 | self.RenderTextDirection([_("T"), _("P"), _("B"), _("A")]) |
482 | - | 741 | + |
483 | elif(croll >= -86 and croll <= -42): | 742 | elif(croll >= -86 and croll <= -42): |
484 | self.RenderTextDirection([_("TA"), _("PT"), _("BP"), _("AB")]) | 743 | self.RenderTextDirection([_("TA"), _("PT"), _("BP"), _("AB")]) |
485 | - | 744 | + |
486 | elif(croll >= -41 and croll <= -2): | 745 | elif(croll >= -41 and croll <= -2): |
487 | self.RenderTextDirection([_("AT"), _("TP"), _("PB"), _("BA")]) | 746 | self.RenderTextDirection([_("AT"), _("TP"), _("PB"), _("BA")]) |
488 | - | 747 | + |
489 | elif (croll >= -2 and croll <= 1): | 748 | elif (croll >= -2 and croll <= 1): |
490 | self.RenderTextDirection([_("A"), _("T"), _("P"), _("B")]) | 749 | self.RenderTextDirection([_("A"), _("T"), _("P"), _("B")]) |
491 | 750 | ||
@@ -500,16 +759,16 @@ class Viewer(wx.Panel): | @@ -500,16 +759,16 @@ class Viewer(wx.Panel): | ||
500 | 759 | ||
501 | elif(croll > 91 and croll <= 135): | 760 | elif(croll > 91 and croll <= 135): |
502 | self.RenderTextDirection([_("BP"), _("AB"), _("TA"), _("PT")]) | 761 | self.RenderTextDirection([_("BP"), _("AB"), _("TA"), _("PT")]) |
503 | - | 762 | + |
504 | elif(croll > 135 and croll <= 177): | 763 | elif(croll > 135 and croll <= 177): |
505 | self.RenderTextDirection([_("PB"), _("BA"), _("AT"), _("TP")]) | 764 | self.RenderTextDirection([_("PB"), _("BA"), _("AT"), _("TP")]) |
506 | - | 765 | + |
507 | elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177): | 766 | elif(croll >= -180 and croll <= -178) or (croll < 180 and croll > 177): |
508 | self.RenderTextDirection([_("P"), _("B"), _("A"), _("T")]) | 767 | self.RenderTextDirection([_("P"), _("B"), _("A"), _("T")]) |
509 | - | 768 | + |
510 | elif(croll >= -177 and croll <= -133): | 769 | elif(croll >= -177 and croll <= -133): |
511 | self.RenderTextDirection([_("PT"), _("BP"), _("AB"), _("TA")]) | 770 | self.RenderTextDirection([_("PT"), _("BP"), _("AB"), _("TA")]) |
512 | - | 771 | + |
513 | elif(croll >= -132 and croll <= -101): | 772 | elif(croll >= -132 and croll <= -101): |
514 | self.RenderTextDirection([_("TP"), _("PB"), _("BA"), _("AT")]) | 773 | self.RenderTextDirection([_("TP"), _("PB"), _("BA"), _("AT")]) |
515 | 774 | ||
@@ -544,12 +803,12 @@ class Viewer(wx.Panel): | @@ -544,12 +803,12 @@ class Viewer(wx.Panel): | ||
544 | def Navigation(self, pubsub_evt): | 803 | def Navigation(self, pubsub_evt): |
545 | # Get point from base change | 804 | # Get point from base change |
546 | x, y, z = pubsub_evt.data | 805 | x, y, z = pubsub_evt.data |
547 | - coord_cross = x, y, z | 806 | + coord_cross = x, y, z |
548 | position = self.slice_data.actor.GetInput().FindPoint(x, y, z) | 807 | position = self.slice_data.actor.GetInput().FindPoint(x, y, z) |
549 | coord_cross = self.slice_data.actor.GetInput().GetPoint(position) | 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 | Publisher.sendMessage('Update cross position', coord_cross) | 810 | Publisher.sendMessage('Update cross position', coord_cross) |
552 | - | 811 | + |
553 | self.ScrollSlice(coord) | 812 | self.ScrollSlice(coord) |
554 | self.interactor.Render() | 813 | self.interactor.Render() |
555 | 814 | ||
@@ -574,7 +833,7 @@ class Viewer(wx.Panel): | @@ -574,7 +833,7 @@ class Viewer(wx.Panel): | ||
574 | #for slice_data in self.slice_data_list: | 833 | #for slice_data in self.slice_data_list: |
575 | #if slice_data.renderer is render: | 834 | #if slice_data.renderer is render: |
576 | #return slice_data | 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 | return self.slice_data | 837 | return self.slice_data |
579 | 838 | ||
580 | def calcultate_scroll_position(self, position): | 839 | def calcultate_scroll_position(self, position): |
@@ -701,7 +960,7 @@ class Viewer(wx.Panel): | @@ -701,7 +960,7 @@ class Viewer(wx.Panel): | ||
701 | 'Hide text actors on viewers') | 960 | 'Hide text actors on viewers') |
702 | Publisher.subscribe(self.OnExportPicture,'Export picture to file') | 961 | Publisher.subscribe(self.OnExportPicture,'Export picture to file') |
703 | Publisher.subscribe(self.SetDefaultCursor, 'Set interactor default cursor') | 962 | Publisher.subscribe(self.SetDefaultCursor, 'Set interactor default cursor') |
704 | - | 963 | + |
705 | Publisher.subscribe(self.AddActors, 'Add actors ' + str(ORIENTATIONS[self.orientation])) | 964 | Publisher.subscribe(self.AddActors, 'Add actors ' + str(ORIENTATIONS[self.orientation])) |
706 | Publisher.subscribe(self.RemoveActors, 'Remove actors ' + str(ORIENTATIONS[self.orientation])) | 965 | Publisher.subscribe(self.RemoveActors, 'Remove actors ' + str(ORIENTATIONS[self.orientation])) |
707 | Publisher.subscribe(self.OnSwapVolumeAxes, 'Swap volume axes') | 966 | Publisher.subscribe(self.OnSwapVolumeAxes, 'Swap volume axes') |
@@ -727,11 +986,11 @@ class Viewer(wx.Panel): | @@ -727,11 +986,11 @@ class Viewer(wx.Panel): | ||
727 | 986 | ||
728 | def SetDefaultCursor(self, pusub_evt): | 987 | def SetDefaultCursor(self, pusub_evt): |
729 | self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | 988 | self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) |
730 | - | 989 | + |
731 | def OnExportPicture(self, pubsub_evt): | 990 | def OnExportPicture(self, pubsub_evt): |
732 | Publisher.sendMessage('Begin busy cursor') | 991 | Publisher.sendMessage('Begin busy cursor') |
733 | view_prop_list = [] | 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 | self.slice_data.renderer.RemoveViewProp(self.slice_data.box_actor) | 994 | self.slice_data.renderer.RemoveViewProp(self.slice_data.box_actor) |
736 | 995 | ||
737 | id, filename, filetype = pubsub_evt.data | 996 | id, filename, filetype = pubsub_evt.data |
@@ -793,7 +1052,7 @@ class Viewer(wx.Panel): | @@ -793,7 +1052,7 @@ class Viewer(wx.Panel): | ||
793 | def CloseProject(self): | 1052 | def CloseProject(self): |
794 | for slice_data in self.slice_data_list: | 1053 | for slice_data in self.slice_data_list: |
795 | del slice_data | 1054 | del slice_data |
796 | - | 1055 | + |
797 | self.slice_data_list = [] | 1056 | self.slice_data_list = [] |
798 | self.layout = (1, 1) | 1057 | self.layout = (1, 1) |
799 | self.orientation_texts = [] | 1058 | self.orientation_texts = [] |
@@ -805,10 +1064,10 @@ class Viewer(wx.Panel): | @@ -805,10 +1064,10 @@ class Viewer(wx.Panel): | ||
805 | def OnSetInteractorStyle(self, pubsub_evt): | 1064 | def OnSetInteractorStyle(self, pubsub_evt): |
806 | state = pubsub_evt.data | 1065 | state = pubsub_evt.data |
807 | self.SetInteractorStyle(state) | 1066 | self.SetInteractorStyle(state) |
808 | - | 1067 | + |
809 | if (state != const.SLICE_STATE_EDITOR): | 1068 | if (state != const.SLICE_STATE_EDITOR): |
810 | Publisher.sendMessage('Set interactor default cursor') | 1069 | Publisher.sendMessage('Set interactor default cursor') |
811 | - | 1070 | + |
812 | def __bind_events_wx(self): | 1071 | def __bind_events_wx(self): |
813 | self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar) | 1072 | self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar) |
814 | self.scroll.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnScrollBarRelease) | 1073 | self.scroll.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnScrollBarRelease) |
@@ -896,6 +1155,8 @@ class Viewer(wx.Panel): | @@ -896,6 +1155,8 @@ class Viewer(wx.Panel): | ||
896 | self.cam = self.slice_data.renderer.GetActiveCamera() | 1155 | self.cam = self.slice_data.renderer.GetActiveCamera() |
897 | self.__build_cross_lines() | 1156 | self.__build_cross_lines() |
898 | 1157 | ||
1158 | + canvas = CanvasRendererCTX(self) | ||
1159 | + | ||
899 | # Set the slice number to the last slice to ensure the camera if far | 1160 | # Set the slice number to the last slice to ensure the camera if far |
900 | # enough to show all slices. | 1161 | # enough to show all slices. |
901 | self.set_slice_number(max_slice_number - 1) | 1162 | self.set_slice_number(max_slice_number - 1) |
@@ -958,13 +1219,20 @@ class Viewer(wx.Panel): | @@ -958,13 +1219,20 @@ class Viewer(wx.Panel): | ||
958 | renderer.SetLayer(0) | 1219 | renderer.SetLayer(0) |
959 | cam = renderer.GetActiveCamera() | 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 | overlay_renderer = vtk.vtkRenderer() | 1228 | overlay_renderer = vtk.vtkRenderer() |
962 | - overlay_renderer.SetLayer(1) | 1229 | + overlay_renderer.SetLayer(2) |
963 | overlay_renderer.SetActiveCamera(cam) | 1230 | overlay_renderer.SetActiveCamera(cam) |
964 | overlay_renderer.SetInteractive(0) | 1231 | overlay_renderer.SetInteractive(0) |
965 | - | ||
966 | - self.interactor.GetRenderWindow().SetNumberOfLayers(2) | 1232 | + |
1233 | + self.interactor.GetRenderWindow().SetNumberOfLayers(3) | ||
967 | self.interactor.GetRenderWindow().AddRenderer(overlay_renderer) | 1234 | self.interactor.GetRenderWindow().AddRenderer(overlay_renderer) |
1235 | + self.interactor.GetRenderWindow().AddRenderer(canvas_renderer) | ||
968 | self.interactor.GetRenderWindow().AddRenderer(renderer) | 1236 | self.interactor.GetRenderWindow().AddRenderer(renderer) |
969 | 1237 | ||
970 | actor = vtk.vtkImageActor() | 1238 | actor = vtk.vtkImageActor() |
@@ -974,12 +1242,14 @@ class Viewer(wx.Panel): | @@ -974,12 +1242,14 @@ class Viewer(wx.Panel): | ||
974 | slice_data = sd.SliceData() | 1242 | slice_data = sd.SliceData() |
975 | slice_data.SetOrientation(self.orientation) | 1243 | slice_data.SetOrientation(self.orientation) |
976 | slice_data.renderer = renderer | 1244 | slice_data.renderer = renderer |
1245 | + slice_data.canvas_renderer = canvas_renderer | ||
977 | slice_data.overlay_renderer = overlay_renderer | 1246 | slice_data.overlay_renderer = overlay_renderer |
978 | slice_data.actor = actor | 1247 | slice_data.actor = actor |
979 | slice_data.SetBorderStyle(sd.BORDER_ALL) | 1248 | slice_data.SetBorderStyle(sd.BORDER_ALL) |
980 | renderer.AddActor(actor) | 1249 | renderer.AddActor(actor) |
981 | renderer.AddActor(slice_data.text.actor) | 1250 | renderer.AddActor(slice_data.text.actor) |
982 | renderer.AddViewProp(slice_data.box_actor) | 1251 | renderer.AddViewProp(slice_data.box_actor) |
1252 | + | ||
983 | return slice_data | 1253 | return slice_data |
984 | 1254 | ||
985 | def __update_camera(self): | 1255 | def __update_camera(self): |
@@ -1027,15 +1297,15 @@ class Viewer(wx.Panel): | @@ -1027,15 +1297,15 @@ class Viewer(wx.Panel): | ||
1027 | def set_scroll_position(self, position): | 1297 | def set_scroll_position(self, position): |
1028 | self.scroll.SetThumbPosition(position) | 1298 | self.scroll.SetThumbPosition(position) |
1029 | self.OnScrollBar() | 1299 | self.OnScrollBar() |
1030 | - | 1300 | + |
1031 | def UpdateSlice3D(self, pos): | 1301 | def UpdateSlice3D(self, pos): |
1032 | original_orientation = project.Project().original_orientation | 1302 | original_orientation = project.Project().original_orientation |
1033 | pos = self.scroll.GetThumbPosition() | 1303 | pos = self.scroll.GetThumbPosition() |
1034 | Publisher.sendMessage('Change slice from slice plane',\ | 1304 | Publisher.sendMessage('Change slice from slice plane',\ |
1035 | (self.orientation, pos)) | 1305 | (self.orientation, pos)) |
1036 | - | 1306 | + |
1037 | def OnScrollBar(self, evt=None, update3D=True): | 1307 | def OnScrollBar(self, evt=None, update3D=True): |
1038 | - pos = self.scroll.GetThumbPosition() | 1308 | + pos = self.scroll.GetThumbPosition() |
1039 | self.set_slice_number(pos) | 1309 | self.set_slice_number(pos) |
1040 | if update3D: | 1310 | if update3D: |
1041 | self.UpdateSlice3D(pos) | 1311 | self.UpdateSlice3D(pos) |
@@ -1044,14 +1314,14 @@ class Viewer(wx.Panel): | @@ -1044,14 +1314,14 @@ class Viewer(wx.Panel): | ||
1044 | # the actual orientation. | 1314 | # the actual orientation. |
1045 | focal_point = self.cross.GetFocalPoint() | 1315 | focal_point = self.cross.GetFocalPoint() |
1046 | Publisher.sendMessage('Update cross position', focal_point) | 1316 | Publisher.sendMessage('Update cross position', focal_point) |
1047 | - Publisher.sendMessage('Update slice viewer') | 1317 | + Publisher.sendMessage('Update slice viewer') |
1048 | else: | 1318 | else: |
1049 | - self.interactor.Render() | 1319 | + self.interactor.Render() |
1050 | if evt: | 1320 | if evt: |
1051 | if self._flush_buffer: | 1321 | if self._flush_buffer: |
1052 | self.slice_.apply_slice_buffer_to_mask(self.orientation) | 1322 | self.slice_.apply_slice_buffer_to_mask(self.orientation) |
1053 | evt.Skip() | 1323 | evt.Skip() |
1054 | - | 1324 | + |
1055 | def OnScrollBarRelease(self, evt): | 1325 | def OnScrollBarRelease(self, evt): |
1056 | pos = self.scroll.GetThumbPosition() | 1326 | pos = self.scroll.GetThumbPosition() |
1057 | evt.Skip() | 1327 | evt.Skip() |
@@ -1077,7 +1347,7 @@ class Viewer(wx.Panel): | @@ -1077,7 +1347,7 @@ class Viewer(wx.Panel): | ||
1077 | if (evt.GetKeyCode() == wx.WXK_UP and pos > min): | 1347 | if (evt.GetKeyCode() == wx.WXK_UP and pos > min): |
1078 | self.OnScrollForward() | 1348 | self.OnScrollForward() |
1079 | self.OnScrollBar() | 1349 | self.OnScrollBar() |
1080 | - | 1350 | + |
1081 | elif (evt.GetKeyCode() == wx.WXK_DOWN and pos < max): | 1351 | elif (evt.GetKeyCode() == wx.WXK_DOWN and pos < max): |
1082 | self.OnScrollBackward() | 1352 | self.OnScrollBackward() |
1083 | self.OnScrollBar() | 1353 | self.OnScrollBar() |
@@ -1101,7 +1371,7 @@ class Viewer(wx.Panel): | @@ -1101,7 +1371,7 @@ class Viewer(wx.Panel): | ||
1101 | Publisher.sendMessage('Set projection type', projections[evt.GetKeyCode()]) | 1371 | Publisher.sendMessage('Set projection type', projections[evt.GetKeyCode()]) |
1102 | Publisher.sendMessage('Reload actual slice') | 1372 | Publisher.sendMessage('Reload actual slice') |
1103 | skip = False | 1373 | skip = False |
1104 | - | 1374 | + |
1105 | self.UpdateSlice3D(pos) | 1375 | self.UpdateSlice3D(pos) |
1106 | self.interactor.Render() | 1376 | self.interactor.Render() |
1107 | 1377 | ||
@@ -1109,20 +1379,24 @@ class Viewer(wx.Panel): | @@ -1109,20 +1379,24 @@ class Viewer(wx.Panel): | ||
1109 | evt.Skip() | 1379 | evt.Skip() |
1110 | 1380 | ||
1111 | def OnScrollForward(self, evt=None, obj=None): | 1381 | def OnScrollForward(self, evt=None, obj=None): |
1382 | + if not self.scroll_enabled: | ||
1383 | + return | ||
1112 | pos = self.scroll.GetThumbPosition() | 1384 | pos = self.scroll.GetThumbPosition() |
1113 | min = 0 | 1385 | min = 0 |
1114 | - | 1386 | + |
1115 | if(pos > min): | 1387 | if(pos > min): |
1116 | if self._flush_buffer: | 1388 | if self._flush_buffer: |
1117 | self.slice_.apply_slice_buffer_to_mask(self.orientation) | 1389 | self.slice_.apply_slice_buffer_to_mask(self.orientation) |
1118 | pos = pos - 1 | 1390 | pos = pos - 1 |
1119 | self.scroll.SetThumbPosition(pos) | 1391 | self.scroll.SetThumbPosition(pos) |
1120 | self.OnScrollBar() | 1392 | self.OnScrollBar() |
1121 | - | 1393 | + |
1122 | def OnScrollBackward(self, evt=None, obj=None): | 1394 | def OnScrollBackward(self, evt=None, obj=None): |
1395 | + if not self.scroll_enabled: | ||
1396 | + return | ||
1123 | pos = self.scroll.GetThumbPosition() | 1397 | pos = self.scroll.GetThumbPosition() |
1124 | max = self.slice_.GetMaxSliceNumber(self.orientation) | 1398 | max = self.slice_.GetMaxSliceNumber(self.orientation) |
1125 | - | 1399 | + |
1126 | if(pos < max): | 1400 | if(pos < max): |
1127 | if self._flush_buffer: | 1401 | if self._flush_buffer: |
1128 | self.slice_.apply_slice_buffer_to_mask(self.orientation) | 1402 | self.slice_.apply_slice_buffer_to_mask(self.orientation) |
@@ -1131,7 +1405,7 @@ class Viewer(wx.Panel): | @@ -1131,7 +1405,7 @@ class Viewer(wx.Panel): | ||
1131 | self.OnScrollBar() | 1405 | self.OnScrollBar() |
1132 | 1406 | ||
1133 | def OnSize(self, evt): | 1407 | def OnSize(self, evt): |
1134 | - w, h = evt.GetSize() | 1408 | + w, h = evt.GetSize() |
1135 | w = float(w) | 1409 | w = float(w) |
1136 | h = float(h) | 1410 | h = float(h) |
1137 | if self.slice_data: | 1411 | if self.slice_data: |
@@ -1168,7 +1442,7 @@ class Viewer(wx.Panel): | @@ -1168,7 +1442,7 @@ class Viewer(wx.Panel): | ||
1168 | self.mip_ctrls.Hide() | 1442 | self.mip_ctrls.Hide() |
1169 | self.GetSizer().Remove(self.mip_ctrls) | 1443 | self.GetSizer().Remove(self.mip_ctrls) |
1170 | self.Layout() | 1444 | self.Layout() |
1171 | - | 1445 | + |
1172 | def OnSetOverwriteMask(self, pubsub_evt): | 1446 | def OnSetOverwriteMask(self, pubsub_evt): |
1173 | value = pubsub_evt.data | 1447 | value = pubsub_evt.data |
1174 | self.overwrite_mask = value | 1448 | self.overwrite_mask = value |
@@ -1184,14 +1458,14 @@ class Viewer(wx.Panel): | @@ -1184,14 +1458,14 @@ class Viewer(wx.Panel): | ||
1184 | for actor in self.actors_by_slice_number[index]: | 1458 | for actor in self.actors_by_slice_number[index]: |
1185 | self.slice_data.renderer.AddActor(actor) | 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 | if self.slice_._type_projection == const.PROJECTION_NORMAL: | 1470 | if self.slice_._type_projection == const.PROJECTION_NORMAL: |
1197 | self.slice_data.SetNumber(index) | 1471 | self.slice_data.SetNumber(index) |
@@ -1225,7 +1499,7 @@ class Viewer(wx.Panel): | @@ -1225,7 +1499,7 @@ class Viewer(wx.Panel): | ||
1225 | # orientation | 1499 | # orientation |
1226 | axis0, axis1 = pubsub_evt.data | 1500 | axis0, axis1 = pubsub_evt.data |
1227 | cursor = self.slice_data.cursor | 1501 | cursor = self.slice_data.cursor |
1228 | - spacing = cursor.spacing | 1502 | + spacing = cursor.spacing |
1229 | if (axis0, axis1) == (2, 1): | 1503 | if (axis0, axis1) == (2, 1): |
1230 | cursor.SetSpacing((spacing[1], spacing[0], spacing[2])) | 1504 | cursor.SetSpacing((spacing[1], spacing[0], spacing[2])) |
1231 | elif (axis0, axis1) == (2, 0): | 1505 | elif (axis0, axis1) == (2, 0): |
invesalius/gui/data_notebook.py
@@ -943,6 +943,7 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -943,6 +943,7 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
943 | Publisher.subscribe(self.OnShowSingle, 'Show single measurement') | 943 | Publisher.subscribe(self.OnShowSingle, 'Show single measurement') |
944 | Publisher.subscribe(self.OnShowMultiple, 'Show multiple measurements') | 944 | Publisher.subscribe(self.OnShowMultiple, 'Show multiple measurements') |
945 | Publisher.subscribe(self.OnLoadData, 'Load measurement dict') | 945 | Publisher.subscribe(self.OnLoadData, 'Load measurement dict') |
946 | + Publisher.subscribe(self.OnRemoveGUIMeasure, 'Remove GUI measurement') | ||
946 | 947 | ||
947 | def __bind_events_wx(self): | 948 | def __bind_events_wx(self): |
948 | self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) | 949 | self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) |
@@ -959,6 +960,19 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -959,6 +960,19 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
959 | elif (keycode == wx.WXK_DELETE): | 960 | elif (keycode == wx.WXK_DELETE): |
960 | self.RemoveMeasurements() | 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 | def RemoveMeasurements(self): | 976 | def RemoveMeasurements(self): |
963 | """ | 977 | """ |
964 | Remove items selected. | 978 | Remove items selected. |
@@ -1105,7 +1119,7 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -1105,7 +1119,7 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
1105 | value = (u"%.2f°") % m.value | 1119 | value = (u"%.2f°") % m.value |
1106 | self.InsertNewItem(m.index, m.name, colour, location, type, value) | 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 | self.SetItemImage(i, False) | 1123 | self.SetItemImage(i, False) |
1110 | 1124 | ||
1111 | def AddItem_(self, pubsub_evt): | 1125 | def AddItem_(self, pubsub_evt): |
invesalius/project.py
@@ -190,7 +190,7 @@ class Project(object): | @@ -190,7 +190,7 @@ class Project(object): | ||
190 | item["type"] = m.type | 190 | item["type"] = m.type |
191 | item["slice_number"] = m.slice_number | 191 | item["slice_number"] = m.slice_number |
192 | item["points"] = m.points | 192 | item["points"] = m.points |
193 | - item["visible"] = m.is_shown | 193 | + item["visible"] = m.visible |
194 | measures[str(m.index)] = item | 194 | measures[str(m.index)] = item |
195 | return measures | 195 | return measures |
196 | 196 |