Commit 275f57c077a2a1e48e2dd3fb77761cd9d1b1bc1e
1 parent
2cfc3302
Exists in
master
and in
6 other branches
ENH: Using MeasureManager
Showing
5 changed files
with
181 additions
and
95 deletions
Show diff stats
invesalius/constants.py
... | ... | @@ -28,7 +28,7 @@ from project import Project |
28 | 28 | #--------------- |
29 | 29 | |
30 | 30 | # Measurements |
31 | -MEASURE_NAME_PATTERN = _("M %d") | |
31 | +MEASURE_NAME_PATTERN = _("M %d") | |
32 | 32 | MEASURE_LINEAR = 101 |
33 | 33 | MEASURE_ANGULAR = 102 |
34 | 34 | |
... | ... | @@ -79,6 +79,10 @@ SAGITAL = 3 |
79 | 79 | VOLUME = 4 |
80 | 80 | SURFACE = 5 |
81 | 81 | |
82 | +# Measure type | |
83 | +LINEAR = 6 | |
84 | +ANGULAR = 7 | |
85 | + | |
82 | 86 | # Colour representing each orientation |
83 | 87 | ORIENTATION_COLOUR = {'AXIAL': (1,0,0), # Red |
84 | 88 | 'CORONAL': (0,1,0), # Green | ... | ... |
invesalius/control.py
... | ... | @@ -24,16 +24,16 @@ import tempfile |
24 | 24 | import wx.lib.pubsub as ps |
25 | 25 | |
26 | 26 | import constants as const |
27 | -import project as prj | |
28 | - | |
29 | 27 | import data.imagedata_utils as utils |
30 | 28 | import data.mask as msk |
29 | +import data.measures | |
31 | 30 | import data.surface as srf |
32 | 31 | import data.volume as volume |
33 | -import reader.dicom_grouper as dg | |
34 | 32 | import gui.dialogs as dialog |
35 | -import reader.dicom_reader as dcm | |
33 | +import project as prj | |
36 | 34 | import reader.analyze_reader as analyze |
35 | +import reader.dicom_grouper as dg | |
36 | +import reader.dicom_reader as dcm | |
37 | 37 | import session as ses |
38 | 38 | |
39 | 39 | from utils import debug |
... | ... | @@ -51,6 +51,8 @@ class Controller(): |
51 | 51 | self.cancel_import = False |
52 | 52 | #Init session |
53 | 53 | session = ses.Session() |
54 | + self.measure_manager = data.measures.MeasurementManager() | |
55 | + | |
54 | 56 | |
55 | 57 | def __bind_events(self): |
56 | 58 | ps.Publisher().subscribe(self.OnImportMedicalImages, 'Import directory') | ... | ... |
invesalius/data/measures.py
1 | 1 | #!/usr/bin/env python |
2 | 2 | # -*- coding: UTF-8 -*- |
3 | 3 | |
4 | -# This example demonstrates the use of vtkSTLReader to load data into | |
5 | -# VTK from a file. This example also uses vtkLODActor which changes | |
6 | -# its graphical representation of the data to maintain interactive | |
7 | -# performance. | |
8 | - | |
9 | - | |
10 | -from itertools import cycle | |
11 | 4 | import math |
12 | -import os | |
13 | -import sys | |
14 | -import time | |
5 | +import random | |
15 | 6 | |
16 | -import wx | |
7 | +import wx.lib.pubsub as ps | |
17 | 8 | import vtk |
18 | 9 | |
19 | 10 | import constants as const |
20 | 11 | |
12 | +TYPE = {const.LINEAR: _(u"Linear"), | |
13 | + const.ANGULAR: _(u"Angular"), | |
14 | + } | |
15 | + | |
16 | +LOCATION = {const.SURFACE: _(u"3D"), | |
17 | + const.AXIAL: _(u"Axial"), | |
18 | + const.CORONAL: _(u"Coronal"), | |
19 | + const.SAGITAL: _(u"Sagittal") | |
20 | + } | |
21 | + | |
22 | +class MeasurementManager(object): | |
23 | + """ | |
24 | + A class to manage the use (Addition, remotion and visibility) from | |
25 | + measures. | |
26 | + """ | |
27 | + def __init__(self): | |
28 | + self.current = None | |
29 | + self.measures = [] | |
30 | + self._bind_events() | |
31 | + | |
32 | + def _bind_events(self): | |
33 | + ps.Publisher().subscribe(self._add_point, "Add measurement point") | |
34 | + | |
35 | + def _add_point(self, pubsub_evt): | |
36 | + position = pubsub_evt.data[0] | |
37 | + type = pubsub_evt.data[1] # Linear or Angular | |
38 | + location = pubsub_evt.data[2] # 3D, AXIAL, SAGITAL, CORONAL | |
39 | + try: | |
40 | + slice_number = pubsub_evt.data[3] | |
41 | + except IndexError: | |
42 | + slice_number = 0 | |
43 | + | |
44 | + if self.current is None: | |
45 | + to_create = True | |
46 | + elif self.current[0].slice_number != slice_number: | |
47 | + to_create = True | |
48 | + elif self.current[0].location != location: | |
49 | + to_create = True | |
50 | + else: | |
51 | + to_create = False | |
52 | + | |
53 | + if to_create: | |
54 | + m = Measurement() | |
55 | + m.location = location | |
56 | + m.points.append(position) | |
57 | + m.slice_number = slice_number | |
58 | + if type == const.LINEAR: | |
59 | + mr = LinearMeasure(m.colour) | |
60 | + else: | |
61 | + mr = AngularMeasure(m.colour) | |
62 | + self.current = (m, mr) | |
63 | + | |
64 | + x, y, z = position | |
65 | + actors = self.current[1].AddPoint(x, y, z) | |
66 | + ps.Publisher().sendMessage(("Add Actors", location), actors) | |
67 | + | |
68 | + if self.current[1].IsComplete(): | |
69 | + self.measures.append(self.current) | |
70 | + index = self.current[0].index | |
71 | + name = self.current[0].name | |
72 | + colour = self.current[0].colour | |
73 | + self.current[0].value = self.current[1].GetValue() | |
74 | + type_ = TYPE[type] | |
75 | + location = LOCATION[location] | |
76 | + value = u"%.2f mm"% self.current[0].value | |
77 | + | |
78 | + msg = 'Update measurement info in GUI', | |
79 | + ps.Publisher().sendMessage(msg, | |
80 | + (index, name, colour, | |
81 | + type_, location, | |
82 | + value)) | |
83 | + self.current = None | |
84 | + | |
85 | + | |
86 | + | |
21 | 87 | class Measurement(): |
22 | 88 | general_index = -1 |
23 | 89 | def __init__(self): |
... | ... | @@ -28,6 +94,7 @@ class Measurement(): |
28 | 94 | self.value = None |
29 | 95 | self.location = const.SURFACE # AXIAL, CORONAL, SAGITTAL |
30 | 96 | self.type = const.LINEAR # ANGULAR |
97 | + self.slice_number = 0 | |
31 | 98 | self.points = [] |
32 | 99 | self.is_shown = False |
33 | 100 | |
... | ... | @@ -131,16 +198,15 @@ class CrossPointRepresentation(object): |
131 | 198 | return a |
132 | 199 | |
133 | 200 | class LinearMeasure(object): |
134 | - def __init__(self, render, colour=(1, 0, 0), representation=None): | |
201 | + def __init__(self, colour=(1, 0, 0), representation=None): | |
135 | 202 | self.colour = colour |
136 | 203 | self.points = [] |
137 | 204 | self.point_actor1 = None |
138 | 205 | self.point_actor2 = None |
139 | 206 | self.line_actor = None |
140 | 207 | self.text_actor = None |
141 | - self.render = render | |
142 | 208 | if not representation: |
143 | - representation = CirclePointRepresentation() | |
209 | + representation = CirclePointRepresentation(colour) | |
144 | 210 | self.representation = representation |
145 | 211 | print colour |
146 | 212 | |
... | ... | @@ -153,18 +219,18 @@ class LinearMeasure(object): |
153 | 219 | def AddPoint(self, x, y, z): |
154 | 220 | if not self.point_actor1: |
155 | 221 | self.SetPoint1(x, y, z) |
222 | + return (self.point_actor1, ) | |
156 | 223 | elif not self.point_actor2: |
157 | 224 | self.SetPoint2(x, y, z) |
225 | + return (self.point_actor2, self.line_actor, self.text_actor) | |
158 | 226 | |
159 | 227 | def SetPoint1(self, x, y, z): |
160 | 228 | self.points.append((x, y, z)) |
161 | 229 | self.point_actor1 = self.representation.GetRepresentation(x, y, z) |
162 | - self.render.AddActor(self.point_actor1) | |
163 | 230 | |
164 | 231 | def SetPoint2(self, x, y, z): |
165 | 232 | self.points.append((x, y, z)) |
166 | 233 | self.point_actor2 = self.representation.GetRepresentation(x, y, z) |
167 | - self.render.AddActor(self.point_actor2) | |
168 | 234 | self.CreateMeasure() |
169 | 235 | |
170 | 236 | def CreateMeasure(self): |
... | ... | @@ -187,7 +253,6 @@ class LinearMeasure(object): |
187 | 253 | a.SetMapper(m) |
188 | 254 | a.GetProperty().SetColor(self.colour) |
189 | 255 | self.line_actor = a |
190 | - self.render.AddActor(self.line_actor) | |
191 | 256 | |
192 | 257 | def _draw_text(self): |
193 | 258 | p1, p2 = self.points |
... | ... | @@ -207,8 +272,8 @@ class LinearMeasure(object): |
207 | 272 | a.DragableOn() |
208 | 273 | a.GetPositionCoordinate().SetCoordinateSystemToWorld() |
209 | 274 | a.GetPositionCoordinate().SetValue(x,y,z) |
275 | + a.GetProperty().SetColor((0, 1, 0)) | |
210 | 276 | self.text_actor = a |
211 | - self.render.AddActor(self.text_actor) | |
212 | 277 | |
213 | 278 | def GetNumberOfPoints(self): |
214 | 279 | return len(self.points) |
... | ... | @@ -264,7 +329,7 @@ class LinearMeasure(object): |
264 | 329 | |
265 | 330 | |
266 | 331 | class AngularMeasure(object): |
267 | - def __init__(self, render, colour=(1, 0, 0), representation=None): | |
332 | + def __init__(self, colour=(1, 0, 0), representation=None): | |
268 | 333 | self.colour = colour |
269 | 334 | self.points = [0, 0, 0] |
270 | 335 | self.number_of_points = 0 |
... | ... | @@ -273,9 +338,8 @@ class AngularMeasure(object): |
273 | 338 | self.point_actor3 = None |
274 | 339 | self.line_actor = None |
275 | 340 | self.text_actor = None |
276 | - self.render = render | |
277 | 341 | if not representation: |
278 | - representation = CirclePointRepresentation() | |
342 | + representation = CirclePointRepresentation(colour) | |
279 | 343 | self.representation = representation |
280 | 344 | print colour |
281 | 345 | |
... | ... | @@ -285,28 +349,28 @@ class AngularMeasure(object): |
285 | 349 | def AddPoint(self, x, y, z): |
286 | 350 | if not self.point_actor1: |
287 | 351 | self.SetPoint1(x, y, z) |
352 | + return (self.point_actor1,) | |
288 | 353 | elif not self.point_actor2: |
289 | 354 | self.SetPoint2(x, y, z) |
355 | + return (self.point_actor2,) | |
290 | 356 | elif not self.point_actor3: |
291 | 357 | self.SetPoint3(x, y, z) |
358 | + return (self.point_actor3, self.line_actor, self.text_actor) | |
292 | 359 | |
293 | 360 | def SetPoint1(self, x, y, z): |
294 | 361 | self.points[0] = (x, y, z) |
295 | 362 | self.number_of_points = 1 |
296 | 363 | self.point_actor1 = self.representation.GetRepresentation(x, y, z) |
297 | - self.render.AddActor(self.point_actor1) | |
298 | 364 | |
299 | 365 | def SetPoint2(self, x, y, z): |
300 | 366 | self.number_of_points = 2 |
301 | 367 | self.points[1] = (x, y, z) |
302 | 368 | self.point_actor2 = self.representation.GetRepresentation(x, y, z) |
303 | - self.render.AddActor(self.point_actor2) | |
304 | 369 | |
305 | 370 | def SetPoint3(self, x, y, z): |
306 | 371 | self.number_of_points = 3 |
307 | 372 | self.points[2] = (x, y, z) |
308 | 373 | self.point_actor3 = self.representation.GetRepresentation(x, y, z) |
309 | - self.render.AddActor(self.point_actor3) | |
310 | 374 | self.CreateMeasure() |
311 | 375 | |
312 | 376 | def CreateMeasure(self): |
... | ... | @@ -340,7 +404,6 @@ class AngularMeasure(object): |
340 | 404 | a.SetMapper(m) |
341 | 405 | a.GetProperty().SetColor(self.colour) |
342 | 406 | self.line_actor = a |
343 | - self.render.AddActor(self.line_actor) | |
344 | 407 | |
345 | 408 | def DrawArc(self): |
346 | 409 | |
... | ... | @@ -389,7 +452,6 @@ class AngularMeasure(object): |
389 | 452 | a.GetPositionCoordinate().SetCoordinateSystemToWorld() |
390 | 453 | a.GetPositionCoordinate().SetValue(x,y,z) |
391 | 454 | self.text_actor = a |
392 | - self.render.AddActor(self.text_actor) | |
393 | 455 | |
394 | 456 | def GetNumberOfPoints(self): |
395 | 457 | return self.number_of_points | ... | ... |
invesalius/data/viewer_slice.py
... | ... | @@ -44,9 +44,9 @@ ID_TO_TOOL_ITEM = {} |
44 | 44 | STR_WL = "WL: %d WW: %d" |
45 | 45 | |
46 | 46 | ORIENTATIONS = { |
47 | - "AXIAL": _("Axial"), | |
48 | - "CORONAL": _("Coronal"), | |
49 | - "SAGITAL": _("Sagital"), | |
47 | + "AXIAL": const.AXIAL, | |
48 | + "CORONAL": const.CORONAL, | |
49 | + "SAGITAL": const.SAGITAL, | |
50 | 50 | } |
51 | 51 | |
52 | 52 | class Viewer(wx.Panel): |
... | ... | @@ -863,6 +863,8 @@ class Viewer(wx.Panel): |
863 | 863 | ps.Publisher().subscribe(self.OnExportPicture,'Export picture to file') |
864 | 864 | ps.Publisher().subscribe(self.SetDefaultCursor, 'Set interactor default cursor') |
865 | 865 | |
866 | + ps.Publisher().subscribe(self.AddActors, ('Add Actors', ORIENTATIONS[self.orientation])) | |
867 | + | |
866 | 868 | def SetDefaultCursor(self, pusub_evt): |
867 | 869 | self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) |
868 | 870 | |
... | ... | @@ -1444,17 +1446,21 @@ class Viewer(wx.Panel): |
1444 | 1446 | x, y, z = self.pick.GetPickPosition() |
1445 | 1447 | if self.pick.GetViewProp(): |
1446 | 1448 | print "Hey, you inserted measure point" |
1447 | - if not self.measures or self.measures[-1][1].IsComplete(): | |
1448 | - m = measures.LinearMeasure(render) | |
1449 | - m.AddPoint(x, y, z) | |
1450 | - self.measures.append((slice_number, m)) | |
1451 | - else: | |
1452 | - m = self.measures[-1][1] | |
1453 | - m.AddPoint(x, y, z) | |
1454 | - if m.IsComplete(): | |
1455 | - ps.Publisher().sendMessage("Add measure to list", | |
1456 | - (ORIENTATIONS[self.orientation], | |
1457 | - _(u"%.3f mm" % m.GetValue()))) | |
1449 | + # if not self.measures or self.measures[-1][1].IsComplete(): | |
1450 | + # m = measures.LinearMeasure(render) | |
1451 | + # m.AddPoint(x, y, z) | |
1452 | + # self.measures.append((slice_number, m)) | |
1453 | + # else: | |
1454 | + # m = self.measures[-1][1] | |
1455 | + # m.AddPoint(x, y, z) | |
1456 | + # if m.IsComplete(): | |
1457 | + # ps.Publisher().sendMessage("Add measure to list", | |
1458 | + # (ORIENTATIONS[self.orientation], | |
1459 | + # _(u"%.3f mm" % m.GetValue()))) | |
1460 | + self.render_to_add = slice_data.renderer | |
1461 | + ps.Publisher().sendMessage("Add measurement point", | |
1462 | + ((x, y,z), const.LINEAR, ORIENTATIONS[self.orientation], | |
1463 | + slice_number)) | |
1458 | 1464 | self.interactor.Render() |
1459 | 1465 | |
1460 | 1466 | def OnInsertAngularMeasurePoint(self, obj, evt): |
... | ... | @@ -1466,15 +1472,25 @@ class Viewer(wx.Panel): |
1466 | 1472 | self.pick.Pick(x, y, 0, render) |
1467 | 1473 | x, y, z = self.pick.GetPickPosition() |
1468 | 1474 | if self.pick.GetViewProp(): |
1469 | - if not self.measures or self.measures[-1][1].IsComplete(): | |
1470 | - m = measures.AngularMeasure(render) | |
1471 | - m.AddPoint(x, y, z) | |
1472 | - self.measures.append((slice_number, m)) | |
1473 | - else: | |
1474 | - m = self.measures[-1][1] | |
1475 | - m.AddPoint(x, y, z) | |
1476 | - if m.IsComplete(): | |
1477 | - ps.Publisher().sendMessage("Add measure to list", | |
1478 | - (ORIENTATIONS[self.orientation], | |
1479 | - _(u"%.3fº" % m.GetValue()))) | |
1475 | + # if not self.measures or self.measures[-1][1].IsComplete(): | |
1476 | + # m = measures.AngularMeasure(render) | |
1477 | + # m.AddPoint(x, y, z) | |
1478 | + # self.measures.append((slice_number, m)) | |
1479 | + # else: | |
1480 | + # m = self.measures[-1][1] | |
1481 | + # m.AddPoint(x, y, z) | |
1482 | + # if m.IsComplete(): | |
1483 | + # ps.Publisher().sendMessage("Add measure to list", | |
1484 | + # (ORIENTATIONS[self.orientation], | |
1485 | + # _(u"%.3fº" % m.GetValue()))) | |
1486 | + self.render_to_add = slice_data.renderer | |
1487 | + ps.Publisher().sendMessage("Add measurement point", | |
1488 | + ((x, y,z), const.ANGULAR, ORIENTATIONS[self.orientation], | |
1489 | + slice_number)) | |
1480 | 1490 | self.interactor.Render() |
1491 | + | |
1492 | + def AddActors(self, pubsub_evt): | |
1493 | + "Inserting actors" | |
1494 | + actors = pubsub_evt.data | |
1495 | + for actor in actors: | |
1496 | + self.render_to_add.AddActor(actor) | ... | ... |
invesalius/data/viewer_volume.py
... | ... | @@ -142,6 +142,8 @@ class Viewer(wx.Panel): |
142 | 142 | ps.Publisher().subscribe(self.OnHideText, |
143 | 143 | 'Hide text actors on viewers') |
144 | 144 | |
145 | + ps.Publisher().subscribe(self.AddActors, ('Add Actors', const.SURFACE)) | |
146 | + | |
145 | 147 | ps.Publisher().subscribe(self.OnShowText, |
146 | 148 | 'Show text actors on viewers') |
147 | 149 | ps.Publisher().subscribe(self.OnCloseProject, 'Close project data') |
... | ... | @@ -225,6 +227,12 @@ class Viewer(wx.Panel): |
225 | 227 | self.text.Show() |
226 | 228 | self.interactor.Render() |
227 | 229 | |
230 | + def AddActors(self, pubsub_evt): | |
231 | + "Inserting actors" | |
232 | + actors = pubsub_evt.data | |
233 | + for actor in actors: | |
234 | + self.ren.AddActor(actor) | |
235 | + | |
228 | 236 | def AddPointReference(self, position, radius=1, colour=(1, 0, 0)): |
229 | 237 | """ |
230 | 238 | Add a point representation in the given x,y,z position with a optional |
... | ... | @@ -628,26 +636,18 @@ class Viewer(wx.Panel): |
628 | 636 | self.measure_picker.Pick(x, y, 0, self.ren) |
629 | 637 | x, y, z = self.measure_picker.GetPickPosition() |
630 | 638 | if self.measure_picker.GetActor(): |
631 | - if not self.measures or self.measures[-1].IsComplete(): | |
632 | - m = measures.LinearMeasure(self.ren) | |
633 | - m.AddPoint(x, y, z) | |
634 | - self.measures.append(m) | |
635 | - else: | |
636 | - m = self.measures[-1] | |
637 | - m.AddPoint(x, y, z) | |
638 | - if m.IsComplete(): | |
639 | - index = len(self.measures) - 1 | |
640 | - name = "M" | |
641 | - colour = m.colour | |
642 | - type_ = _("Linear") | |
643 | - location = u"3D" | |
644 | - value = u"%.2f mm"% m.GetValue() | |
645 | - | |
646 | - msg = 'Update measurement info in GUI', | |
647 | - ps.Publisher().sendMessage(msg, | |
648 | - (index, name, colour, | |
649 | - type_, location, | |
650 | - value)) | |
639 | + # if not self.measures or self.measures[-1].IsComplete(): | |
640 | + # m = measures.LinearMeasure(self.ren) | |
641 | + # m.AddPoint(x, y, z) | |
642 | + # self.measures.append(m) | |
643 | + # else: | |
644 | + # m = self.measures[-1] | |
645 | + # m.AddPoint(x, y, z) | |
646 | + # if m.IsComplete(): | |
647 | + # ps.Publisher().sendMessage("Add measure to list", | |
648 | + # (u"3D", _(u"%.3f mm" % m.GetValue()))) | |
649 | + ps.Publisher().sendMessage("Add measurement point", | |
650 | + ((x, y,z), const.LINEAR, const.SURFACE)) | |
651 | 651 | self.interactor.Render() |
652 | 652 | |
653 | 653 | def OnInsertAngularMeasurePoint(self, obj, evt): |
... | ... | @@ -656,25 +656,27 @@ class Viewer(wx.Panel): |
656 | 656 | self.measure_picker.Pick(x, y, 0, self.ren) |
657 | 657 | x, y, z = self.measure_picker.GetPickPosition() |
658 | 658 | if self.measure_picker.GetActor(): |
659 | - if not self.measures or self.measures[-1].IsComplete(): | |
660 | - m = measures.AngularMeasure(self.ren) | |
661 | - m.AddPoint(x, y, z) | |
662 | - self.measures.append(m) | |
663 | - else: | |
664 | - m = self.measures[-1] | |
665 | - m.AddPoint(x, y, z) | |
666 | - if m.IsComplete(): | |
667 | - index = len(self.measures) - 1 | |
668 | - name = "M" | |
669 | - colour = m.colour | |
670 | - type_ = _("Angular") | |
671 | - location = u"3D" | |
672 | - value = u"%.2f˚"% m.GetValue() | |
673 | - msg = 'Update measurement info in GUI', | |
674 | - ps.Publisher().sendMessage(msg, | |
675 | - (index, name, colour, | |
676 | - type_, location, | |
677 | - value)) | |
659 | + # if not self.measures or self.measures[-1].IsComplete(): | |
660 | + # m = measures.AngularMeasure(self.ren) | |
661 | + # m.AddPoint(x, y, z) | |
662 | + # self.measures.append(m) | |
663 | + # else: | |
664 | + # m = self.measures[-1] | |
665 | + # m.AddPoint(x, y, z) | |
666 | + # if m.IsComplete(): | |
667 | + # index = len(self.measures) - 1 | |
668 | + # name = "M" | |
669 | + # colour = m.colour | |
670 | + # type_ = _("Angular") | |
671 | + # location = u"3D" | |
672 | + # value = u"%.2f˚"% m.GetValue() | |
673 | + # msg = 'Update measurement info in GUI', | |
674 | + # ps.Publisher().sendMessage(msg, | |
675 | + # (index, name, colour, | |
676 | + # type_, location, | |
677 | + # value)) | |
678 | + ps.Publisher().sendMessage("Add measurement point", | |
679 | + ((x, y,z), const.ANGULAR, const.SURFACE)) | |
678 | 680 | self.interactor.Render() |
679 | 681 | |
680 | 682 | ... | ... |