Commit 08e6f1bdae672082c685fe7a010b4a1244238ad5
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
Showing
5 changed files
with
324 additions
and
110 deletions
Show diff stats
invesalius/data/measures.py
... | ... | @@ -10,6 +10,7 @@ import vtk |
10 | 10 | import constants as const |
11 | 11 | import project as prj |
12 | 12 | import session as ses |
13 | +import utils | |
13 | 14 | |
14 | 15 | TYPE = {const.LINEAR: _(u"Linear"), |
15 | 16 | const.ANGULAR: _(u"Angular"), |
... | ... | @@ -21,6 +22,63 @@ LOCATION = {const.SURFACE: _(u"3D"), |
21 | 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 | 83 | class MeasurementManager(object): |
26 | 84 | """ |
... | ... | @@ -29,7 +87,7 @@ class MeasurementManager(object): |
29 | 87 | """ |
30 | 88 | def __init__(self): |
31 | 89 | self.current = None |
32 | - self.measures = [] | |
90 | + self.measures = MeasureData() | |
33 | 91 | self._bind_events() |
34 | 92 | |
35 | 93 | def _bind_events(self): |
... | ... | @@ -40,6 +98,7 @@ class MeasurementManager(object): |
40 | 98 | Publisher.subscribe(self._load_measurements, "Load measurement dict") |
41 | 99 | Publisher.subscribe(self._rm_incomplete_measurements, |
42 | 100 | "Remove incomplete measurements") |
101 | + Publisher.subscribe(self._change_measure_point_pos, 'Change measurement point position') | |
43 | 102 | |
44 | 103 | def _load_measurements(self, pubsub_evt): |
45 | 104 | try: |
... | ... | @@ -49,7 +108,7 @@ class MeasurementManager(object): |
49 | 108 | spacing = 1.0, 1.0, 1.0 |
50 | 109 | for i in dict: |
51 | 110 | m = dict[i] |
52 | - | |
111 | + | |
53 | 112 | if m.location == const.AXIAL: |
54 | 113 | radius = min(spacing[1], spacing[2]) * const.PROP_MEASURE |
55 | 114 | |
... | ... | @@ -72,8 +131,10 @@ class MeasurementManager(object): |
72 | 131 | for point in m.points: |
73 | 132 | x, y, z = point |
74 | 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 | 138 | self.current = None |
78 | 139 | |
79 | 140 | if not m.is_shown: |
... | ... | @@ -136,14 +197,15 @@ class MeasurementManager(object): |
136 | 197 | mr = AngularMeasure(m.colour, representation) |
137 | 198 | if to_remove: |
138 | 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 | 205 | if self.current[0].location == const.SURFACE: |
144 | 206 | Publisher.sendMessage('Render volume viewer') |
145 | 207 | else: |
146 | - Publisher.sendMessage('Update slice viewer') | |
208 | + Publisher.sendMessage('Reload actual slice') | |
147 | 209 | |
148 | 210 | session = ses.Session() |
149 | 211 | session.ChangeProject() |
... | ... | @@ -156,13 +218,17 @@ class MeasurementManager(object): |
156 | 218 | x, y, z = position |
157 | 219 | actors = mr.AddPoint(x, y, z) |
158 | 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 | 229 | if mr.IsComplete(): |
163 | 230 | index = prj.Project().AddMeasurement(m) |
164 | 231 | #m.index = index # already done in proj |
165 | - self.measures.append(self.current) | |
166 | 232 | name = m.name |
167 | 233 | colour = m.colour |
168 | 234 | m.value = mr.GetValue() |
... | ... | @@ -181,19 +247,57 @@ class MeasurementManager(object): |
181 | 247 | value)) |
182 | 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 | 284 | def _change_name(self, pubsub_evt): |
185 | 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 | 288 | def _remove_measurements(self, pubsub_evt): |
189 | 289 | indexes = pubsub_evt.data |
190 | - print indexes | |
191 | 290 | for index in indexes: |
192 | 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 | 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 | 301 | Publisher.sendMessage('Update slice viewer') |
198 | 302 | Publisher.sendMessage('Render volume viewer') |
199 | 303 | |
... | ... | @@ -212,12 +316,13 @@ class MeasurementManager(object): |
212 | 316 | |
213 | 317 | def _rm_incomplete_measurements(self, pubsub_evt): |
214 | 318 | if self.current is None: |
215 | - return | |
319 | + return | |
216 | 320 | |
217 | 321 | mr = self.current[1] |
218 | 322 | print "RM INC M", self.current, mr.IsComplete() |
219 | 323 | if not mr.IsComplete(): |
220 | 324 | print "---To REMOVE" |
325 | + self.measures.pop() | |
221 | 326 | actors = mr.GetActors() |
222 | 327 | slice_number = self.current[0].slice_number |
223 | 328 | Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)), |
... | ... | @@ -364,6 +469,7 @@ class LinearMeasure(object): |
364 | 469 | self.point_actor2 = None |
365 | 470 | self.line_actor = None |
366 | 471 | self.text_actor = None |
472 | + self.renderer = None | |
367 | 473 | if not representation: |
368 | 474 | representation = CirclePointRepresentation(colour) |
369 | 475 | self.representation = representation |
... | ... | @@ -384,13 +490,31 @@ class LinearMeasure(object): |
384 | 490 | return (self.point_actor2, self.line_actor, self.text_actor) |
385 | 491 | |
386 | 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 | 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 | 519 | def CreateMeasure(self): |
396 | 520 | self._draw_line() |
... | ... | @@ -432,6 +556,7 @@ class LinearMeasure(object): |
432 | 556 | a.GetPositionCoordinate().SetCoordinateSystemToWorld() |
433 | 557 | a.GetPositionCoordinate().SetValue(x,y,z) |
434 | 558 | a.GetProperty().SetColor((0, 1, 0)) |
559 | + a.GetProperty().SetOpacity(0.75) | |
435 | 560 | self.text_actor = a |
436 | 561 | |
437 | 562 | def GetNumberOfPoints(self): |
... | ... | @@ -443,22 +568,22 @@ class LinearMeasure(object): |
443 | 568 | |
444 | 569 | def SetRenderer(self, renderer): |
445 | 570 | if self.point_actor1: |
446 | - self.render.RemoveActor(self.point_actor1) | |
571 | + self.renderer.RemoveActor(self.point_actor1) | |
447 | 572 | renderer.AddActor(self.point_actor1) |
448 | 573 | |
449 | 574 | if self.point_actor2: |
450 | - self.render.RemoveActor(self.point_actor2) | |
575 | + self.renderer.RemoveActor(self.point_actor2) | |
451 | 576 | renderer.AddActor(self.point_actor2) |
452 | 577 | |
453 | 578 | if self.line_actor: |
454 | - self.render.RemoveActor(self.line_actor) | |
579 | + self.renderer.RemoveActor(self.line_actor) | |
455 | 580 | renderer.AddActor(self.line_actor) |
456 | 581 | |
457 | 582 | if self.text_actor: |
458 | - self.render.RemoveActor(self.text_actor) | |
583 | + self.renderer.RemoveActor(self.text_actor) | |
459 | 584 | renderer.AddActor(self.text_actor) |
460 | 585 | |
461 | - self.render = renderer | |
586 | + self.renderer = renderer | |
462 | 587 | |
463 | 588 | def SetVisibility(self, v): |
464 | 589 | self.point_actor1.SetVisibility(v) |
... | ... | @@ -483,19 +608,19 @@ class LinearMeasure(object): |
483 | 608 | |
484 | 609 | def Remove(self): |
485 | 610 | if self.point_actor1: |
486 | - self.render.RemoveActor(self.point_actor1) | |
611 | + self.renderer.RemoveActor(self.point_actor1) | |
487 | 612 | del self.point_actor1 |
488 | 613 | |
489 | 614 | if self.point_actor2: |
490 | - self.render.RemoveActor(self.point_actor2) | |
615 | + self.renderer.RemoveActor(self.point_actor2) | |
491 | 616 | del self.point_actor2 |
492 | 617 | |
493 | 618 | if self.line_actor: |
494 | - self.render.RemoveActor(self.line_actor) | |
619 | + self.renderer.RemoveActor(self.line_actor) | |
495 | 620 | del self.line_actor |
496 | 621 | |
497 | 622 | if self.text_actor: |
498 | - self.render.RemoveActor(self.text_actor) | |
623 | + self.renderer.RemoveActor(self.text_actor) | |
499 | 624 | del self.text_actor |
500 | 625 | |
501 | 626 | # def __del__(self): |
... | ... | @@ -532,20 +657,59 @@ class AngularMeasure(object): |
532 | 657 | return (self.point_actor3, self.line_actor, self.text_actor) |
533 | 658 | |
534 | 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 | 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 | 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 | 714 | def CreateMeasure(self): |
551 | 715 | self._draw_line() |
... | ... | @@ -677,47 +841,47 @@ class AngularMeasure(object): |
677 | 841 | |
678 | 842 | def Remove(self): |
679 | 843 | if self.point_actor1: |
680 | - self.render.RemoveActor(self.point_actor1) | |
844 | + self.renderer.RemoveActor(self.point_actor1) | |
681 | 845 | del self.point_actor1 |
682 | 846 | |
683 | 847 | if self.point_actor2: |
684 | - self.render.RemoveActor(self.point_actor2) | |
848 | + self.renderer.RemoveActor(self.point_actor2) | |
685 | 849 | del self.point_actor2 |
686 | 850 | |
687 | 851 | if self.point_actor3: |
688 | - self.render.RemoveActor(self.point_actor3) | |
852 | + self.renderer.RemoveActor(self.point_actor3) | |
689 | 853 | del self.point_actor3 |
690 | 854 | |
691 | 855 | if self.line_actor: |
692 | - self.render.RemoveActor(self.line_actor) | |
856 | + self.renderer.RemoveActor(self.line_actor) | |
693 | 857 | del self.line_actor |
694 | 858 | |
695 | 859 | if self.text_actor: |
696 | - self.render.RemoveActor(self.text_actor) | |
860 | + self.renderer.RemoveActor(self.text_actor) | |
697 | 861 | del self.text_actor |
698 | 862 | |
699 | 863 | def SetRenderer(self, renderer): |
700 | 864 | if self.point_actor1: |
701 | - self.render.RemoveActor(self.point_actor1) | |
865 | + self.renderer.RemoveActor(self.point_actor1) | |
702 | 866 | renderer.AddActor(self.point_actor1) |
703 | 867 | |
704 | 868 | if self.point_actor2: |
705 | - self.render.RemoveActor(self.point_actor2) | |
869 | + self.renderer.RemoveActor(self.point_actor2) | |
706 | 870 | renderer.AddActor(self.point_actor2) |
707 | 871 | |
708 | 872 | if self.point_actor3: |
709 | - self.render.RemoveActor(self.point_actor3) | |
873 | + self.renderer.RemoveActor(self.point_actor3) | |
710 | 874 | renderer.AddActor(self.point_actor3) |
711 | 875 | |
712 | 876 | if self.line_actor: |
713 | - self.render.RemoveActor(self.line_actor) | |
877 | + self.renderer.RemoveActor(self.line_actor) | |
714 | 878 | renderer.AddActor(self.line_actor) |
715 | 879 | |
716 | 880 | if self.text_actor: |
717 | - self.render.RemoveActor(self.text_actor) | |
881 | + self.renderer.RemoveActor(self.text_actor) | |
718 | 882 | renderer.AddActor(self.text_actor) |
719 | 883 | |
720 | - self.render = renderer | |
884 | + self.renderer = renderer | |
721 | 885 | |
722 | 886 | # def __del__(self): |
723 | 887 | # self.Remove() | ... | ... |
invesalius/data/styles.py
... | ... | @@ -40,6 +40,8 @@ from scipy.ndimage import watershed_ift, generate_binary_structure |
40 | 40 | from skimage.morphology import watershed |
41 | 41 | from skimage import filter |
42 | 42 | |
43 | +from .measures import MeasureData | |
44 | + | |
43 | 45 | import watershed_process |
44 | 46 | |
45 | 47 | import utils |
... | ... | @@ -350,78 +352,107 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle): |
350 | 352 | self.orientation = viewer.orientation |
351 | 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 | 360 | spacing = self.slice_data.actor.GetInput().GetSpacing() |
354 | 361 | |
355 | 362 | if self.orientation == "AXIAL": |
356 | 363 | self.radius = min(spacing[1], spacing[2]) * 0.8 |
364 | + self._ori = const.AXIAL | |
357 | 365 | |
358 | 366 | elif self.orientation == 'CORONAL': |
359 | 367 | self.radius = min(spacing[0], spacing[1]) * 0.8 |
368 | + self._ori = const.CORONAL | |
360 | 369 | |
361 | 370 | elif self.orientation == 'SAGITAL': |
362 | 371 | self.radius = min(spacing[1], spacing[2]) * 0.8 |
372 | + self._ori = const.SAGITAL | |
363 | 373 | |
364 | 374 | self.picker = vtk.vtkCellPicker() |
375 | + self.picker.PickFromListOn() | |
365 | 376 | |
377 | + self._bind_events() | |
378 | + | |
379 | + def _bind_events(self): | |
366 | 380 | self.AddObserver("LeftButtonPressEvent", self.OnInsertLinearMeasurePoint) |
381 | + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseMeasurePoint) | |
382 | + self.AddObserver("MouseMoveEvent", self.OnMoveMeasurePoint) | |
367 | 383 | |
368 | 384 | def OnInsertLinearMeasurePoint(self, obj, evt): |
369 | - iren = obj.GetInteractor() | |
370 | - x,y = iren.GetEventPosition() | |
371 | - render = iren.FindPokedRenderer(x, y) | |
372 | 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 | 418 | def CleanUp(self): |
419 | + self.picker.PickFromListOff() | |
383 | 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 | 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 | 458 | class PanMoveInteractorStyle(DefaultInteractorStyle): | ... | ... |
invesalius/data/viewer_slice.py
... | ... | @@ -19,6 +19,7 @@ |
19 | 19 | # detalhes. |
20 | 20 | #-------------------------------------------------------------------------- |
21 | 21 | |
22 | +import collections | |
22 | 23 | import itertools |
23 | 24 | import tempfile |
24 | 25 | |
... | ... | @@ -171,8 +172,8 @@ class Viewer(wx.Panel): |
171 | 172 | self.layout = (1, 1) |
172 | 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 | 177 | self.renderers_by_slice_number = {} |
177 | 178 | |
178 | 179 | self.orientation = orientation |
... | ... | @@ -1178,11 +1179,20 @@ class Viewer(wx.Panel): |
1178 | 1179 | image = self.slice_.GetSlices(self.orientation, index, |
1179 | 1180 | self.number_slices, inverted, border_size) |
1180 | 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 | 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 | 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 | 1196 | if self.slice_._type_projection == const.PROJECTION_NORMAL: |
1187 | 1197 | self.slice_data.SetNumber(index) |
1188 | 1198 | else: |
... | ... | @@ -1239,10 +1249,7 @@ class Viewer(wx.Panel): |
1239 | 1249 | for actor in actors: |
1240 | 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 | 1254 | def RemoveActors(self, pubsub_evt): |
1248 | 1255 | "Remove a list of actors" | ... | ... |
invesalius/gui/data_notebook.py
... | ... | @@ -1127,6 +1127,8 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
1127 | 1127 | self.UpdateItemInfo(index, name, colour, location, type_, value) |
1128 | 1128 | else: |
1129 | 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 | 23 | import locale |
24 | 24 | import math |
25 | 25 | |
26 | +import numpy as np | |
27 | + | |
26 | 28 | def format_time(value): |
27 | 29 | sp1 = value.split(".") |
28 | 30 | sp2 = value.split(":") |
... | ... | @@ -418,3 +420,11 @@ def UpdateCheck(): |
418 | 420 | if (last!=const.INVESALIUS_VERSION): |
419 | 421 | print " ...New update found!!! -> version:", last #, ", url=",url |
420 | 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 | ... | ... |