Commit 08e6f1bdae672082c685fe7a010b4a1244238ad5
Committed by
GitHub
1 parent
794ea503
Exists in
master
and in
26 other branches
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 | ... | ... |