Commit 08e6f1bdae672082c685fe7a010b4a1244238ad5

Authored by Thiago Franco de Moraes
Committed by GitHub
1 parent 794ea503
Exists in master

Drag and drop support to measures in slices

* Created an class to keep measurements data

* Picking only the slice actor when adding measure

* Moving measure points

* melhorias

* Improvements

* Improvements

* Improvements

* Improvements
invesalius/data/measures.py
@@ -10,6 +10,7 @@ import vtk @@ -10,6 +10,7 @@ import vtk
10 import constants as const 10 import constants as const
11 import project as prj 11 import project as prj
12 import session as ses 12 import session as ses
  13 +import utils
13 14
14 TYPE = {const.LINEAR: _(u"Linear"), 15 TYPE = {const.LINEAR: _(u"Linear"),
15 const.ANGULAR: _(u"Angular"), 16 const.ANGULAR: _(u"Angular"),
@@ -21,6 +22,63 @@ LOCATION = {const.SURFACE: _(u"3D"), @@ -21,6 +22,63 @@ LOCATION = {const.SURFACE: _(u"3D"),
21 const.SAGITAL: _(u"Sagittal") 22 const.SAGITAL: _(u"Sagittal")
22 } 23 }
23 24
  25 +map_locations_id = {
  26 + "3D": const.SURFACE,
  27 + "AXIAL": const.AXIAL,
  28 + "CORONAL": const.CORONAL,
  29 + "SAGITAL": const.SAGITAL,
  30 +}
  31 +
  32 +map_id_locations = {const.SURFACE: "3D",
  33 + const.AXIAL: "AXIAL",
  34 + const.CORONAL: "CORONAL",
  35 + const.SAGITAL: "SAGITAL",
  36 + }
  37 +
  38 +class MeasureData:
  39 + """
  40 + Responsible to keep measures data.
  41 + """
  42 + __metaclass__= utils.Singleton
  43 + def __init__(self):
  44 + self.measures = {const.SURFACE: {},
  45 + const.AXIAL: {},
  46 + const.CORONAL: {},
  47 + const.SAGITAL: {}}
  48 + self._list_measures = []
  49 +
  50 + def append(self, m):
  51 + try:
  52 + self.measures[m[0].location][m[0].slice_number].append(m)
  53 + except KeyError:
  54 + self.measures[m[0].location][m[0].slice_number] = [m, ]
  55 +
  56 + self._list_measures.append(m)
  57 +
  58 + def get(self, location, slice_number):
  59 + return self.measures[map_locations_id[location]].get(slice_number, [])
  60 +
  61 + def pop(self, idx=None):
  62 + if idx is None:
  63 + m = self._list_measures.pop()
  64 + else:
  65 + m = self._list_measures.pop(idx)
  66 + self.measures[m[0].location][m[0].slice_number].remove(m)
  67 + return m
  68 +
  69 + def remove(self, m):
  70 + self._list_measures.remove(m)
  71 + self.measures[m[0].location][m[0].slice_number].remove(m)
  72 +
  73 + def __contains__(self, m):
  74 + return m in self._list_measures
  75 +
  76 + def __getitem__(self, idx):
  77 + return self._list_measures[idx]
  78 +
  79 + def __len__(self):
  80 + return len(self._list_measures)
  81 +
24 82
25 class MeasurementManager(object): 83 class MeasurementManager(object):
26 """ 84 """
@@ -29,7 +87,7 @@ class MeasurementManager(object): @@ -29,7 +87,7 @@ class MeasurementManager(object):
29 """ 87 """
30 def __init__(self): 88 def __init__(self):
31 self.current = None 89 self.current = None
32 - self.measures = [] 90 + self.measures = MeasureData()
33 self._bind_events() 91 self._bind_events()
34 92
35 def _bind_events(self): 93 def _bind_events(self):
@@ -40,6 +98,7 @@ class MeasurementManager(object): @@ -40,6 +98,7 @@ class MeasurementManager(object):
40 Publisher.subscribe(self._load_measurements, "Load measurement dict") 98 Publisher.subscribe(self._load_measurements, "Load measurement dict")
41 Publisher.subscribe(self._rm_incomplete_measurements, 99 Publisher.subscribe(self._rm_incomplete_measurements,
42 "Remove incomplete measurements") 100 "Remove incomplete measurements")
  101 + Publisher.subscribe(self._change_measure_point_pos, 'Change measurement point position')
43 102
44 def _load_measurements(self, pubsub_evt): 103 def _load_measurements(self, pubsub_evt):
45 try: 104 try:
@@ -49,7 +108,7 @@ class MeasurementManager(object): @@ -49,7 +108,7 @@ class MeasurementManager(object):
49 spacing = 1.0, 1.0, 1.0 108 spacing = 1.0, 1.0, 1.0
50 for i in dict: 109 for i in dict:
51 m = dict[i] 110 m = dict[i]
52 - 111 +
53 if m.location == const.AXIAL: 112 if m.location == const.AXIAL:
54 radius = min(spacing[1], spacing[2]) * const.PROP_MEASURE 113 radius = min(spacing[1], spacing[2]) * const.PROP_MEASURE
55 114
@@ -72,8 +131,10 @@ class MeasurementManager(object): @@ -72,8 +131,10 @@ class MeasurementManager(object):
72 for point in m.points: 131 for point in m.points:
73 x, y, z = point 132 x, y, z = point
74 actors = mr.AddPoint(x, y, z) 133 actors = mr.AddPoint(x, y, z)
75 - Publisher.sendMessage(("Add actors " + str(m.location)),  
76 - (actors, m.slice_number)) 134 +
  135 + if m.location == const.SURFACE:
  136 + Publisher.sendMessage(("Add actors " + str(m.location)),
  137 + (actors, m.slice_number))
77 self.current = None 138 self.current = None
78 139
79 if not m.is_shown: 140 if not m.is_shown:
@@ -136,14 +197,15 @@ class MeasurementManager(object): @@ -136,14 +197,15 @@ class MeasurementManager(object):
136 mr = AngularMeasure(m.colour, representation) 197 mr = AngularMeasure(m.colour, representation)
137 if to_remove: 198 if to_remove:
138 print "---To REMOVE" 199 print "---To REMOVE"
139 - actors = self.current[1].GetActors()  
140 - slice_number = self.current[0].slice_number  
141 - Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)),  
142 - (actors, slice_number)) 200 + # actors = self.current[1].GetActors()
  201 + # slice_number = self.current[0].slice_number
  202 + # Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)),
  203 + # (actors, slice_number))
  204 + self.measures.pop()[1].Remove()
143 if self.current[0].location == const.SURFACE: 205 if self.current[0].location == const.SURFACE:
144 Publisher.sendMessage('Render volume viewer') 206 Publisher.sendMessage('Render volume viewer')
145 else: 207 else:
146 - Publisher.sendMessage('Update slice viewer') 208 + Publisher.sendMessage('Reload actual slice')
147 209
148 session = ses.Session() 210 session = ses.Session()
149 session.ChangeProject() 211 session.ChangeProject()
@@ -156,13 +218,17 @@ class MeasurementManager(object): @@ -156,13 +218,17 @@ class MeasurementManager(object):
156 x, y, z = position 218 x, y, z = position
157 actors = mr.AddPoint(x, y, z) 219 actors = mr.AddPoint(x, y, z)
158 m.points.append(position) 220 m.points.append(position)
159 - Publisher.sendMessage("Add actors " + str(location),  
160 - (actors, m.slice_number)) 221 +
  222 + if m.location == const.SURFACE:
  223 + Publisher.sendMessage("Add actors " + str(location),
  224 + (actors, m.slice_number))
  225 +
  226 + if self.current not in self.measures:
  227 + self.measures.append(self.current)
161 228
162 if mr.IsComplete(): 229 if mr.IsComplete():
163 index = prj.Project().AddMeasurement(m) 230 index = prj.Project().AddMeasurement(m)
164 #m.index = index # already done in proj 231 #m.index = index # already done in proj
165 - self.measures.append(self.current)  
166 name = m.name 232 name = m.name
167 colour = m.colour 233 colour = m.colour
168 m.value = mr.GetValue() 234 m.value = mr.GetValue()
@@ -181,19 +247,57 @@ class MeasurementManager(object): @@ -181,19 +247,57 @@ class MeasurementManager(object):
181 value)) 247 value))
182 self.current = None 248 self.current = None
183 249
  250 + def _change_measure_point_pos(self, pubsub_evt):
  251 + index, npoint, pos = pubsub_evt.data
  252 + print index, npoint, pos
  253 + m, mr = self.measures[index]
  254 + x, y, z = pos
  255 + if npoint == 0:
  256 + mr.SetPoint1(x, y, z)
  257 + m.points[0] = x, y, z
  258 + elif npoint == 1:
  259 + mr.SetPoint2(x, y, z)
  260 + m.points[1] = x, y, z
  261 + elif npoint == 2:
  262 + mr.SetPoint3(x, y, z)
  263 + m.points[2] = x, y, z
  264 +
  265 + m.value = mr.GetValue()
  266 +
  267 + name = m.name
  268 + colour = m.colour
  269 + m.value = mr.GetValue()
  270 + type_ = TYPE[m.type]
  271 + location = LOCATION[m.location]
  272 +
  273 + if m.type == const.LINEAR:
  274 + value = u"%.2f mm"% m.value
  275 + else:
  276 + value = u"%.2f°"% m.value
  277 +
  278 + Publisher.sendMessage('Update measurement info in GUI',
  279 + (index, name, colour,
  280 + location,
  281 + type_,
  282 + value))
  283 +
184 def _change_name(self, pubsub_evt): 284 def _change_name(self, pubsub_evt):
185 index, new_name = pubsub_evt.data 285 index, new_name = pubsub_evt.data
186 - self.measures[index][0].name = new_name 286 + self.measures[index].name = new_name
187 287
188 def _remove_measurements(self, pubsub_evt): 288 def _remove_measurements(self, pubsub_evt):
189 indexes = pubsub_evt.data 289 indexes = pubsub_evt.data
190 - print indexes  
191 for index in indexes: 290 for index in indexes:
192 m, mr = self.measures.pop(index) 291 m, mr = self.measures.pop(index)
193 - actors = mr.GetActors() 292 + try:
  293 + mr.Remove()
  294 + except AttributeError:
  295 + # The is not being displayed
  296 + pass
194 prj.Project().RemoveMeasurement(index) 297 prj.Project().RemoveMeasurement(index)
195 - Publisher.sendMessage(('Remove actors ' + str(m.location)),  
196 - (actors, m.slice_number)) 298 + if m.location == const.SURFACE:
  299 + Publisher.sendMessage(('Remove actors ' + str(m.location)),
  300 + (mr.GetActors(), m.slice_number))
197 Publisher.sendMessage('Update slice viewer') 301 Publisher.sendMessage('Update slice viewer')
198 Publisher.sendMessage('Render volume viewer') 302 Publisher.sendMessage('Render volume viewer')
199 303
@@ -212,12 +316,13 @@ class MeasurementManager(object): @@ -212,12 +316,13 @@ class MeasurementManager(object):
212 316
213 def _rm_incomplete_measurements(self, pubsub_evt): 317 def _rm_incomplete_measurements(self, pubsub_evt):
214 if self.current is None: 318 if self.current is None:
215 - return 319 + return
216 320
217 mr = self.current[1] 321 mr = self.current[1]
218 print "RM INC M", self.current, mr.IsComplete() 322 print "RM INC M", self.current, mr.IsComplete()
219 if not mr.IsComplete(): 323 if not mr.IsComplete():
220 print "---To REMOVE" 324 print "---To REMOVE"
  325 + self.measures.pop()
221 actors = mr.GetActors() 326 actors = mr.GetActors()
222 slice_number = self.current[0].slice_number 327 slice_number = self.current[0].slice_number
223 Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)), 328 Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)),
@@ -364,6 +469,7 @@ class LinearMeasure(object): @@ -364,6 +469,7 @@ class LinearMeasure(object):
364 self.point_actor2 = None 469 self.point_actor2 = None
365 self.line_actor = None 470 self.line_actor = None
366 self.text_actor = None 471 self.text_actor = None
  472 + self.renderer = None
367 if not representation: 473 if not representation:
368 representation = CirclePointRepresentation(colour) 474 representation = CirclePointRepresentation(colour)
369 self.representation = representation 475 self.representation = representation
@@ -384,13 +490,31 @@ class LinearMeasure(object): @@ -384,13 +490,31 @@ class LinearMeasure(object):
384 return (self.point_actor2, self.line_actor, self.text_actor) 490 return (self.point_actor2, self.line_actor, self.text_actor)
385 491
386 def SetPoint1(self, x, y, z): 492 def SetPoint1(self, x, y, z):
387 - self.points.append((x, y, z))  
388 - self.point_actor1 = self.representation.GetRepresentation(x, y, z) 493 + if len(self.points) == 0:
  494 + self.points.append((x, y, z))
  495 + self.point_actor1 = self.representation.GetRepresentation(x, y, z)
  496 + else:
  497 + self.points[0] = (x, y, z)
  498 + if len(self.points) == 2:
  499 + self.Remove()
  500 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
  501 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
  502 + self.CreateMeasure()
  503 + else:
  504 + self.Remove()
  505 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
389 506
390 def SetPoint2(self, x, y, z): 507 def SetPoint2(self, x, y, z):
391 - self.points.append((x, y, z))  
392 - self.point_actor2 = self.representation.GetRepresentation(x, y, z)  
393 - self.CreateMeasure() 508 + if len(self.points) == 1:
  509 + self.points.append((x, y, z))
  510 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
  511 + self.CreateMeasure()
  512 + else:
  513 + self.points[1] = (x, y, z)
  514 + self.Remove()
  515 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
  516 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
  517 + self.CreateMeasure()
394 518
395 def CreateMeasure(self): 519 def CreateMeasure(self):
396 self._draw_line() 520 self._draw_line()
@@ -432,6 +556,7 @@ class LinearMeasure(object): @@ -432,6 +556,7 @@ class LinearMeasure(object):
432 a.GetPositionCoordinate().SetCoordinateSystemToWorld() 556 a.GetPositionCoordinate().SetCoordinateSystemToWorld()
433 a.GetPositionCoordinate().SetValue(x,y,z) 557 a.GetPositionCoordinate().SetValue(x,y,z)
434 a.GetProperty().SetColor((0, 1, 0)) 558 a.GetProperty().SetColor((0, 1, 0))
  559 + a.GetProperty().SetOpacity(0.75)
435 self.text_actor = a 560 self.text_actor = a
436 561
437 def GetNumberOfPoints(self): 562 def GetNumberOfPoints(self):
@@ -443,22 +568,22 @@ class LinearMeasure(object): @@ -443,22 +568,22 @@ class LinearMeasure(object):
443 568
444 def SetRenderer(self, renderer): 569 def SetRenderer(self, renderer):
445 if self.point_actor1: 570 if self.point_actor1:
446 - self.render.RemoveActor(self.point_actor1) 571 + self.renderer.RemoveActor(self.point_actor1)
447 renderer.AddActor(self.point_actor1) 572 renderer.AddActor(self.point_actor1)
448 573
449 if self.point_actor2: 574 if self.point_actor2:
450 - self.render.RemoveActor(self.point_actor2) 575 + self.renderer.RemoveActor(self.point_actor2)
451 renderer.AddActor(self.point_actor2) 576 renderer.AddActor(self.point_actor2)
452 577
453 if self.line_actor: 578 if self.line_actor:
454 - self.render.RemoveActor(self.line_actor) 579 + self.renderer.RemoveActor(self.line_actor)
455 renderer.AddActor(self.line_actor) 580 renderer.AddActor(self.line_actor)
456 581
457 if self.text_actor: 582 if self.text_actor:
458 - self.render.RemoveActor(self.text_actor) 583 + self.renderer.RemoveActor(self.text_actor)
459 renderer.AddActor(self.text_actor) 584 renderer.AddActor(self.text_actor)
460 585
461 - self.render = renderer 586 + self.renderer = renderer
462 587
463 def SetVisibility(self, v): 588 def SetVisibility(self, v):
464 self.point_actor1.SetVisibility(v) 589 self.point_actor1.SetVisibility(v)
@@ -483,19 +608,19 @@ class LinearMeasure(object): @@ -483,19 +608,19 @@ class LinearMeasure(object):
483 608
484 def Remove(self): 609 def Remove(self):
485 if self.point_actor1: 610 if self.point_actor1:
486 - self.render.RemoveActor(self.point_actor1) 611 + self.renderer.RemoveActor(self.point_actor1)
487 del self.point_actor1 612 del self.point_actor1
488 613
489 if self.point_actor2: 614 if self.point_actor2:
490 - self.render.RemoveActor(self.point_actor2) 615 + self.renderer.RemoveActor(self.point_actor2)
491 del self.point_actor2 616 del self.point_actor2
492 617
493 if self.line_actor: 618 if self.line_actor:
494 - self.render.RemoveActor(self.line_actor) 619 + self.renderer.RemoveActor(self.line_actor)
495 del self.line_actor 620 del self.line_actor
496 621
497 if self.text_actor: 622 if self.text_actor:
498 - self.render.RemoveActor(self.text_actor) 623 + self.renderer.RemoveActor(self.text_actor)
499 del self.text_actor 624 del self.text_actor
500 625
501 # def __del__(self): 626 # def __del__(self):
@@ -532,20 +657,59 @@ class AngularMeasure(object): @@ -532,20 +657,59 @@ class AngularMeasure(object):
532 return (self.point_actor3, self.line_actor, self.text_actor) 657 return (self.point_actor3, self.line_actor, self.text_actor)
533 658
534 def SetPoint1(self, x, y, z): 659 def SetPoint1(self, x, y, z):
535 - self.points[0] = (x, y, z)  
536 - self.number_of_points = 1  
537 - self.point_actor1 = self.representation.GetRepresentation(x, y, z) 660 + if self.number_of_points == 0:
  661 + self.points[0] = (x, y, z)
  662 + self.number_of_points = 1
  663 + self.point_actor1 = self.representation.GetRepresentation(x, y, z)
  664 + else:
  665 + self.points[0] = (x, y, z)
  666 + if len(self.points) == 3:
  667 + self.Remove()
  668 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
  669 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
  670 + self.point_actor3 = self.representation.GetRepresentation(*self.points[2])
  671 + self.CreateMeasure()
  672 + else:
  673 + self.Remove()
  674 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
  675 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
538 676
539 def SetPoint2(self, x, y, z): 677 def SetPoint2(self, x, y, z):
540 - self.number_of_points = 2  
541 - self.points[1] = (x, y, z)  
542 - self.point_actor2 = self.representation.GetRepresentation(x, y, z) 678 + if self.number_of_points == 1:
  679 + self.number_of_points = 2
  680 + self.points[1] = (x, y, z)
  681 + self.point_actor2 = self.representation.GetRepresentation(x, y, z)
  682 + else:
  683 + self.points[1] = (x, y, z)
  684 + if len(self.points) == 3:
  685 + self.Remove()
  686 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
  687 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
  688 + self.point_actor3 = self.representation.GetRepresentation(*self.points[2])
  689 + self.CreateMeasure()
  690 + else:
  691 + self.Remove()
  692 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
  693 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
543 694
544 def SetPoint3(self, x, y, z): 695 def SetPoint3(self, x, y, z):
545 - self.number_of_points = 3  
546 - self.points[2] = (x, y, z)  
547 - self.point_actor3 = self.representation.GetRepresentation(x, y, z)  
548 - self.CreateMeasure() 696 + if self.number_of_points == 2:
  697 + self.number_of_points = 3
  698 + self.points[2] = (x, y, z)
  699 + self.point_actor3 = self.representation.GetRepresentation(x, y, z)
  700 + self.CreateMeasure()
  701 + else:
  702 + self.points[2] = (x, y, z)
  703 + if len(self.points) == 3:
  704 + self.Remove()
  705 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
  706 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
  707 + self.point_actor3 = self.representation.GetRepresentation(*self.points[2])
  708 + self.CreateMeasure()
  709 + else:
  710 + self.Remove()
  711 + self.point_actor1 = self.representation.GetRepresentation(*self.points[0])
  712 + self.point_actor2 = self.representation.GetRepresentation(*self.points[1])
549 713
550 def CreateMeasure(self): 714 def CreateMeasure(self):
551 self._draw_line() 715 self._draw_line()
@@ -677,47 +841,47 @@ class AngularMeasure(object): @@ -677,47 +841,47 @@ class AngularMeasure(object):
677 841
678 def Remove(self): 842 def Remove(self):
679 if self.point_actor1: 843 if self.point_actor1:
680 - self.render.RemoveActor(self.point_actor1) 844 + self.renderer.RemoveActor(self.point_actor1)
681 del self.point_actor1 845 del self.point_actor1
682 846
683 if self.point_actor2: 847 if self.point_actor2:
684 - self.render.RemoveActor(self.point_actor2) 848 + self.renderer.RemoveActor(self.point_actor2)
685 del self.point_actor2 849 del self.point_actor2
686 850
687 if self.point_actor3: 851 if self.point_actor3:
688 - self.render.RemoveActor(self.point_actor3) 852 + self.renderer.RemoveActor(self.point_actor3)
689 del self.point_actor3 853 del self.point_actor3
690 854
691 if self.line_actor: 855 if self.line_actor:
692 - self.render.RemoveActor(self.line_actor) 856 + self.renderer.RemoveActor(self.line_actor)
693 del self.line_actor 857 del self.line_actor
694 858
695 if self.text_actor: 859 if self.text_actor:
696 - self.render.RemoveActor(self.text_actor) 860 + self.renderer.RemoveActor(self.text_actor)
697 del self.text_actor 861 del self.text_actor
698 862
699 def SetRenderer(self, renderer): 863 def SetRenderer(self, renderer):
700 if self.point_actor1: 864 if self.point_actor1:
701 - self.render.RemoveActor(self.point_actor1) 865 + self.renderer.RemoveActor(self.point_actor1)
702 renderer.AddActor(self.point_actor1) 866 renderer.AddActor(self.point_actor1)
703 867
704 if self.point_actor2: 868 if self.point_actor2:
705 - self.render.RemoveActor(self.point_actor2) 869 + self.renderer.RemoveActor(self.point_actor2)
706 renderer.AddActor(self.point_actor2) 870 renderer.AddActor(self.point_actor2)
707 871
708 if self.point_actor3: 872 if self.point_actor3:
709 - self.render.RemoveActor(self.point_actor3) 873 + self.renderer.RemoveActor(self.point_actor3)
710 renderer.AddActor(self.point_actor3) 874 renderer.AddActor(self.point_actor3)
711 875
712 if self.line_actor: 876 if self.line_actor:
713 - self.render.RemoveActor(self.line_actor) 877 + self.renderer.RemoveActor(self.line_actor)
714 renderer.AddActor(self.line_actor) 878 renderer.AddActor(self.line_actor)
715 879
716 if self.text_actor: 880 if self.text_actor:
717 - self.render.RemoveActor(self.text_actor) 881 + self.renderer.RemoveActor(self.text_actor)
718 renderer.AddActor(self.text_actor) 882 renderer.AddActor(self.text_actor)
719 883
720 - self.render = renderer 884 + self.renderer = renderer
721 885
722 # def __del__(self): 886 # def __del__(self):
723 # self.Remove() 887 # self.Remove()
invesalius/data/styles.py
@@ -40,6 +40,8 @@ from scipy.ndimage import watershed_ift, generate_binary_structure @@ -40,6 +40,8 @@ from scipy.ndimage import watershed_ift, generate_binary_structure
40 from skimage.morphology import watershed 40 from skimage.morphology import watershed
41 from skimage import filter 41 from skimage import filter
42 42
  43 +from .measures import MeasureData
  44 +
43 import watershed_process 45 import watershed_process
44 46
45 import utils 47 import utils
@@ -350,78 +352,107 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): @@ -350,78 +352,107 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle):
350 self.orientation = viewer.orientation 352 self.orientation = viewer.orientation
351 self.slice_data = viewer.slice_data 353 self.slice_data = viewer.slice_data
352 354
  355 + self.measures = MeasureData()
  356 + self.selected = None
  357 +
  358 + self._type = const.LINEAR
  359 +
353 spacing = self.slice_data.actor.GetInput().GetSpacing() 360 spacing = self.slice_data.actor.GetInput().GetSpacing()
354 361
355 if self.orientation == "AXIAL": 362 if self.orientation == "AXIAL":
356 self.radius = min(spacing[1], spacing[2]) * 0.8 363 self.radius = min(spacing[1], spacing[2]) * 0.8
  364 + self._ori = const.AXIAL
357 365
358 elif self.orientation == 'CORONAL': 366 elif self.orientation == 'CORONAL':
359 self.radius = min(spacing[0], spacing[1]) * 0.8 367 self.radius = min(spacing[0], spacing[1]) * 0.8
  368 + self._ori = const.CORONAL
360 369
361 elif self.orientation == 'SAGITAL': 370 elif self.orientation == 'SAGITAL':
362 self.radius = min(spacing[1], spacing[2]) * 0.8 371 self.radius = min(spacing[1], spacing[2]) * 0.8
  372 + self._ori = const.SAGITAL
363 373
364 self.picker = vtk.vtkCellPicker() 374 self.picker = vtk.vtkCellPicker()
  375 + self.picker.PickFromListOn()
365 376
  377 + self._bind_events()
  378 +
  379 + def _bind_events(self):
366 self.AddObserver("LeftButtonPressEvent", self.OnInsertLinearMeasurePoint) 380 self.AddObserver("LeftButtonPressEvent", self.OnInsertLinearMeasurePoint)
  381 + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseMeasurePoint)
  382 + self.AddObserver("MouseMoveEvent", self.OnMoveMeasurePoint)
367 383
368 def OnInsertLinearMeasurePoint(self, obj, evt): 384 def OnInsertLinearMeasurePoint(self, obj, evt):
369 - iren = obj.GetInteractor()  
370 - x,y = iren.GetEventPosition()  
371 - render = iren.FindPokedRenderer(x, y)  
372 slice_number = self.slice_data.number 385 slice_number = self.slice_data.number
373 - self.picker.Pick(x, y, 0, render)  
374 - x, y, z = self.picker.GetPickPosition()  
375 - if self.picker.GetViewProp():  
376 - Publisher.sendMessage("Add measurement point",  
377 - ((x, y,z), const.LINEAR,  
378 - ORIENTATIONS[self.orientation],  
379 - slice_number, self.radius))  
380 - self.viewer.interactor.Render() 386 + x, y, z = self._get_pos_clicked()
  387 +
  388 + selected = self._verify_clicked(x, y, z)
  389 + if selected:
  390 + self.selected = selected
  391 + else:
  392 + if self.picker.GetViewProp():
  393 + renderer = self.viewer.slice_data.renderer
  394 + Publisher.sendMessage("Add measurement point",
  395 + ((x, y,z), self._type,
  396 + ORIENTATIONS[self.orientation],
  397 + slice_number, self.radius))
  398 + Publisher.sendMessage('Reload actual slice %s' % self.orientation)
  399 +
  400 + def OnReleaseMeasurePoint(self, obj, evt):
  401 + if self.selected:
  402 + n, m, mr = self.selected
  403 + x, y, z = self._get_pos_clicked()
  404 + idx = self.measures._list_measures.index((m, mr))
  405 + Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z)))
  406 + Publisher.sendMessage('Reload actual slice %s' % self.orientation)
  407 + self.selected = None
  408 +
  409 + def OnMoveMeasurePoint(self, obj, evt):
  410 + if self.selected:
  411 + n, m, mr = self.selected
  412 + x, y, z = self._get_pos_clicked()
  413 + idx = self.measures._list_measures.index((m, mr))
  414 + Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z)))
  415 +
  416 + Publisher.sendMessage('Reload actual slice %s' % self.orientation)
381 417
382 def CleanUp(self): 418 def CleanUp(self):
  419 + self.picker.PickFromListOff()
383 Publisher.sendMessage("Remove incomplete measurements") 420 Publisher.sendMessage("Remove incomplete measurements")
384 421
  422 + def _get_pos_clicked(self):
  423 + iren = self.viewer.interactor
  424 + mx,my = iren.GetEventPosition()
  425 + render = iren.FindPokedRenderer(mx, my)
  426 + self.picker.AddPickList(self.slice_data.actor)
  427 + self.picker.Pick(mx, my, 0, render)
  428 + x, y, z = self.picker.GetPickPosition()
  429 + self.picker.DeletePickList(self.slice_data.actor)
  430 + return (x, y, z)
385 431
386 -class AngularMeasureInteractorStyle(DefaultInteractorStyle):  
387 - """  
388 - Interactor style responsible for insert angular measurements.  
389 - """  
390 - def __init__(self, viewer):  
391 - DefaultInteractorStyle.__init__(self, viewer)  
392 -  
393 - self.viewer = viewer  
394 - self.orientation = viewer.orientation  
395 - self.slice_data = viewer.slice_data  
396 -  
397 - spacing = self.slice_data.actor.GetInput().GetSpacing()  
398 -  
399 - if self.orientation == "AXIAL":  
400 - self.radius = min(spacing[1], spacing[2]) * 0.8  
401 -  
402 - elif self.orientation == 'CORONAL':  
403 - self.radius = min(spacing[0], spacing[1]) * 0.8  
404 -  
405 - elif self.orientation == 'SAGITAL':  
406 - self.radius = min(spacing[1], spacing[2]) * 0.8  
407 -  
408 - self.picker = vtk.vtkCellPicker()  
409 -  
410 - self.AddObserver("LeftButtonPressEvent", self.OnInsertAngularMeasurePoint)  
411 -  
412 - def OnInsertAngularMeasurePoint(self, obj, evt):  
413 - iren = obj.GetInteractor()  
414 - x,y = iren.GetEventPosition()  
415 - render = iren.FindPokedRenderer(x, y) 432 + def _verify_clicked(self, x, y, z):
416 slice_number = self.slice_data.number 433 slice_number = self.slice_data.number
417 - self.picker.Pick(x, y, 0, render)  
418 - x, y, z = self.picker.GetPickPosition()  
419 - if self.picker.GetViewProp():  
420 - Publisher.sendMessage("Add measurement point",  
421 - ((x, y,z), const.ANGULAR,  
422 - ORIENTATIONS[self.orientation],  
423 - slice_number, self.radius))  
424 - self.viewer.interactor.Render() 434 + sx, sy, sz = self.viewer.slice_.spacing
  435 + if self.orientation == "AXIAL":
  436 + max_dist = 2 * max(sx, sy)
  437 + elif self.orientation == "CORONAL":
  438 + max_dist = 2 * max(sx, sz)
  439 + elif self.orientation == "SAGITAL":
  440 + max_dist = 2 * max(sy, sz)
  441 +
  442 + if slice_number in self.measures.measures[self._ori]:
  443 + for m, mr in self.measures.measures[self._ori][slice_number]:
  444 + if mr.IsComplete():
  445 + for n, p in enumerate(m.points):
  446 + px, py, pz = p
  447 + dist = ((px-x)**2 + (py-y)**2 + (pz-z)**2)**0.5
  448 + if dist < max_dist:
  449 + return (n, m, mr)
  450 + return None
  451 +
  452 +class AngularMeasureInteractorStyle(LinearMeasureInteractorStyle):
  453 + def __init__(self, viewer):
  454 + LinearMeasureInteractorStyle.__init__(self, viewer)
  455 + self._type = const.ANGULAR
425 456
426 457
427 class PanMoveInteractorStyle(DefaultInteractorStyle): 458 class PanMoveInteractorStyle(DefaultInteractorStyle):
invesalius/data/viewer_slice.py
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 # detalhes. 19 # detalhes.
20 #-------------------------------------------------------------------------- 20 #--------------------------------------------------------------------------
21 21
  22 +import collections
22 import itertools 23 import itertools
23 import tempfile 24 import tempfile
24 25
@@ -171,8 +172,8 @@ class Viewer(wx.Panel): @@ -171,8 +172,8 @@ class Viewer(wx.Panel):
171 self.layout = (1, 1) 172 self.layout = (1, 1)
172 self.orientation_texts = [] 173 self.orientation_texts = []
173 174
174 - self.measures = []  
175 - self.actors_by_slice_number = {} 175 + self.measures = measures.MeasureData()
  176 + self.actors_by_slice_number = collections.defaultdict(list)
176 self.renderers_by_slice_number = {} 177 self.renderers_by_slice_number = {}
177 178
178 self.orientation = orientation 179 self.orientation = orientation
@@ -1178,11 +1179,20 @@ class Viewer(wx.Panel): @@ -1178,11 +1179,20 @@ class Viewer(wx.Panel):
1178 image = self.slice_.GetSlices(self.orientation, index, 1179 image = self.slice_.GetSlices(self.orientation, index,
1179 self.number_slices, inverted, border_size) 1180 self.number_slices, inverted, border_size)
1180 self.slice_data.actor.SetInputData(image) 1181 self.slice_data.actor.SetInputData(image)
1181 - for actor in self.actors_by_slice_number.get(self.slice_data.number, []): 1182 + for actor in self.actors_by_slice_number[self.slice_data.number]:
1182 self.slice_data.renderer.RemoveActor(actor) 1183 self.slice_data.renderer.RemoveActor(actor)
1183 - for actor in self.actors_by_slice_number.get(index, []): 1184 + for actor in self.actors_by_slice_number[index]:
1184 self.slice_data.renderer.AddActor(actor) 1185 self.slice_data.renderer.AddActor(actor)
1185 1186
  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)
  1190 +
  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)
  1195 +
1186 if self.slice_._type_projection == const.PROJECTION_NORMAL: 1196 if self.slice_._type_projection == const.PROJECTION_NORMAL:
1187 self.slice_data.SetNumber(index) 1197 self.slice_data.SetNumber(index)
1188 else: 1198 else:
@@ -1239,10 +1249,7 @@ class Viewer(wx.Panel): @@ -1239,10 +1249,7 @@ class Viewer(wx.Panel):
1239 for actor in actors: 1249 for actor in actors:
1240 self.slice_data.renderer.AddActor(actor) 1250 self.slice_data.renderer.AddActor(actor)
1241 1251
1242 - try:  
1243 - self.actors_by_slice_number[n].extend(actors)  
1244 - except KeyError:  
1245 - self.actors_by_slice_number[n] = list(actors) 1252 + self.actors_by_slice_number[n].extend(actors)
1246 1253
1247 def RemoveActors(self, pubsub_evt): 1254 def RemoveActors(self, pubsub_evt):
1248 "Remove a list of actors" 1255 "Remove a list of actors"
invesalius/gui/data_notebook.py
@@ -1127,6 +1127,8 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): @@ -1127,6 +1127,8 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
1127 self.UpdateItemInfo(index, name, colour, location, type_, value) 1127 self.UpdateItemInfo(index, name, colour, location, type_, value)
1128 else: 1128 else:
1129 self.InsertNewItem(index, name, colour, location, type_, value) 1129 self.InsertNewItem(index, name, colour, location, type_, value)
  1130 + else:
  1131 + self.UpdateItemInfo(index, name, colour, location, type_, value)
1130 1132
1131 1133
1132 1134
invesalius/utils.py
@@ -23,6 +23,8 @@ import re @@ -23,6 +23,8 @@ import re
23 import locale 23 import locale
24 import math 24 import math
25 25
  26 +import numpy as np
  27 +
26 def format_time(value): 28 def format_time(value):
27 sp1 = value.split(".") 29 sp1 = value.split(".")
28 sp2 = value.split(":") 30 sp2 = value.split(":")
@@ -418,3 +420,11 @@ def UpdateCheck(): @@ -418,3 +420,11 @@ def UpdateCheck():
418 if (last!=const.INVESALIUS_VERSION): 420 if (last!=const.INVESALIUS_VERSION):
419 print " ...New update found!!! -> version:", last #, ", url=",url 421 print " ...New update found!!! -> version:", last #, ", url=",url
420 wx.CallAfter(wx.CallLater, 1000, _show_update_info) 422 wx.CallAfter(wx.CallLater, 1000, _show_update_info)
  423 +
  424 +
  425 +def vtkarray_to_numpy(m):
  426 + nm = np.zeros((4, 4))
  427 + for i in xrange(4):
  428 + for j in xrange(4):
  429 + nm[i, j] = m.GetElement(i, j)
  430 + return nm