Commit 0f30fe61b8c6b18f491c123b13f7fae165c26f7e
1 parent
259d97fe
Exists in
master
and in
6 other branches
ADD: Initial works in measures
Showing
7 changed files
with
394 additions
and
16 deletions
Show diff stats
.gitattributes
... | ... | @@ -141,6 +141,7 @@ invesalius/data/cursor_actors.py -text |
141 | 141 | invesalius/data/editor.py -text |
142 | 142 | invesalius/data/imagedata_utils.py -text |
143 | 143 | invesalius/data/mask.py -text |
144 | +invesalius/data/measures.py -text | |
144 | 145 | invesalius/data/orientation.py -text |
145 | 146 | invesalius/data/polydata_utils.py -text |
146 | 147 | invesalius/data/slice_.py -text | ... | ... |
invesalius/constants.py
... | ... | @@ -350,6 +350,7 @@ MODE_ZOOM_SELECTION = 1 #:"Set Zoom Select Mode", |
350 | 350 | MODE_ROTATE = 2#:"Set Spin Mode", |
351 | 351 | MODE_MOVE = 3#:"Set Pan Mode", |
352 | 352 | MODE_WW_WL = 4#:"Bright and contrast adjustment"} |
353 | +MODE_LINEAR_MEASURE = 5 | |
353 | 354 | |
354 | 355 | |
355 | 356 | # self.states = {0:"Set Zoom Mode", 1:"Set Zoom Select Mode", |
... | ... | @@ -431,6 +432,7 @@ SLICE_STATE_CROSS = 1006 |
431 | 432 | SLICE_STATE_SCROLL = 1007 |
432 | 433 | SLICE_STATE_EDITOR = 1008 |
433 | 434 | VOLUME_STATE_SEED = 2001 |
435 | +STATE_LINEAR_MEASURE = 3001 | |
434 | 436 | |
435 | 437 | |
436 | 438 | TOOL_STATES = [ STATE_WL, STATE_SPIN, STATE_ZOOM, |
... | ... | @@ -443,7 +445,7 @@ SLICE_STYLES = TOOL_STATES + TOOL_SLICE_STATES |
443 | 445 | SLICE_STYLES.append(STATE_DEFAULT) |
444 | 446 | SLICE_STYLES.append(SLICE_STATE_EDITOR) |
445 | 447 | |
446 | -VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED] | |
448 | +VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED, STATE_LINEAR_MEASURE] | |
447 | 449 | VOLUME_STYLES.append(STATE_DEFAULT) |
448 | 450 | |
449 | 451 | |
... | ... | @@ -456,4 +458,5 @@ STYLE_LEVEL = {SLICE_STATE_EDITOR: 1, |
456 | 458 | STATE_ZOOM: 2, |
457 | 459 | STATE_ZOOM_SL: 2, |
458 | 460 | STATE_PAN:2, |
459 | - VOLUME_STATE_SEED:1} | |
461 | + VOLUME_STATE_SEED:1, | |
462 | + STATE_LINEAR_MEASURE: 2} | ... | ... |
... | ... | @@ -0,0 +1,341 @@ |
1 | +#!/usr/bin/env python | |
2 | +# -*- coding: UTF-8 -*- | |
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 | +import wx | |
10 | +import sys | |
11 | +import os | |
12 | +import time | |
13 | +import math | |
14 | + | |
15 | +from itertools import cycle | |
16 | +from wx.grid import Grid, GridCellBoolRenderer, GridCellBoolEditor, EVT_GRID_CELL_CHANGE | |
17 | + | |
18 | +import vtk | |
19 | +from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor | |
20 | + | |
21 | +class CirclePointRepresentation(object): | |
22 | + """ | |
23 | + This class represents a circle that indicate a point in the surface | |
24 | + """ | |
25 | + def __init__(self, color=(1, 0, 0), radius=1.0): | |
26 | + """ | |
27 | + color: the color of the representation | |
28 | + radius: the radius of circle representation | |
29 | + """ | |
30 | + self.color = color | |
31 | + self.radius = radius | |
32 | + | |
33 | + def GetRepresentation(self, x, y, z): | |
34 | + """ | |
35 | + Return a actor that represents the point in the given x, y, z point | |
36 | + """ | |
37 | + sphere = vtk.vtkSphereSource() | |
38 | + sphere.SetCenter(x, y, z) | |
39 | + sphere.SetRadius(self.radius) | |
40 | + | |
41 | + c = vtk.vtkCoordinate() | |
42 | + c.SetCoordinateSystemToWorld() | |
43 | + | |
44 | + m = vtk.vtkPolyDataMapper2D() | |
45 | + m.SetInputConnection(sphere.GetOutputPort()) | |
46 | + m.SetTransformCoordinate(c) | |
47 | + | |
48 | + a = vtk.vtkActor2D() | |
49 | + a.SetMapper(m) | |
50 | + a.GetProperty().SetColor(self.color) | |
51 | + | |
52 | + return a | |
53 | + | |
54 | +class CrossPointRepresentation(object): | |
55 | + """ | |
56 | + This class represents a cross that indicate a point in the surface | |
57 | + """ | |
58 | + def __init__(self, camera, color=(1, 0, 0), size=1.0): | |
59 | + """ | |
60 | + color: the color of the representation | |
61 | + size: the size of the representation | |
62 | + camera: the active camera, to get the orientation to draw the cross | |
63 | + """ | |
64 | + self.camera = camera | |
65 | + self.color = color | |
66 | + self.size = size | |
67 | + | |
68 | + def GetRepresentation(self, x, y, z): | |
69 | + pc = self.camera.GetPosition() # camera position | |
70 | + pf = self.camera.GetFocalPoint() # focal position | |
71 | + pp = (x, y, z) # point where the user clicked | |
72 | + | |
73 | + # Vector from camera position to user clicked point | |
74 | + vcp = [j-i for i,j in zip(pc, pp)] | |
75 | + # Vector from camera position to camera focal point | |
76 | + vcf = [j-i for i,j in zip(pc, pf)] | |
77 | + # the vector where the perpendicular vector will be given | |
78 | + n = [0,0,0] | |
79 | + # The cross, or vectorial product, give a vector perpendicular to vcp | |
80 | + # and vcf, in this case this vector will be in horizontal, this vector | |
81 | + # will be stored in the variable "n" | |
82 | + vtk.vtkMath.Cross(vcp, vcf, n) | |
83 | + # then normalize n to only indicate the direction of this vector | |
84 | + vtk.vtkMath.Normalize(n) | |
85 | + # then | |
86 | + p1 = [i*self.size + j for i,j in zip(n, pp)] | |
87 | + p2 = [i*-self.size + j for i,j in zip(n, pp)] | |
88 | + | |
89 | + sh = vtk.vtkLineSource() | |
90 | + sh.SetPoint1(p1) | |
91 | + sh.SetPoint2(p2) | |
92 | + | |
93 | + n = [0,0,0] | |
94 | + vcn = [j-i for i,j in zip(p1, pc)] | |
95 | + vtk.vtkMath.Cross(vcp, vcn, n) | |
96 | + vtk.vtkMath.Normalize(n) | |
97 | + p3 = [i*self.size + j for i,j in zip(n, pp)] | |
98 | + p4 = [i*-self.size +j for i,j in zip(n, pp)] | |
99 | + | |
100 | + sv = vtk.vtkLineSource() | |
101 | + sv.SetPoint1(p3) | |
102 | + sv.SetPoint2(p4) | |
103 | + | |
104 | + cruz = vtk.vtkAppendPolyData() | |
105 | + cruz.AddInput(sv.GetOutput()) | |
106 | + cruz.AddInput(sh.GetOutput()) | |
107 | + | |
108 | + c = vtk.vtkCoordinate() | |
109 | + c.SetCoordinateSystemToWorld() | |
110 | + | |
111 | + m = vtk.vtkPolyDataMapper2D() | |
112 | + m.SetInputConnection(cruz.GetOutputPort()) | |
113 | + m.SetTransformCoordinate(c) | |
114 | + | |
115 | + a = vtk.vtkActor2D() | |
116 | + a.SetMapper(m) | |
117 | + a.GetProperty().SetColor(self.color) | |
118 | + return a | |
119 | + | |
120 | +class LinearMeasure(object): | |
121 | + def __init__(self, render, color=(1, 0, 0), representation=None): | |
122 | + self.color = color | |
123 | + self.points = [] | |
124 | + self.point_actor1 = None | |
125 | + self.point_actor2 = None | |
126 | + self.line_actor = None | |
127 | + self.render = render | |
128 | + if not representation: | |
129 | + representation = CirclePointRepresentation() | |
130 | + self.representation = representation | |
131 | + print color | |
132 | + | |
133 | + def SetPoint1(self, x, y, z): | |
134 | + self.points.append((x, y, z)) | |
135 | + self.point_actor1 = self.representation.GetRepresentation(x, y, z) | |
136 | + self.render.AddActor(self.point_actor1) | |
137 | + | |
138 | + def SetPoint2(self, x, y, z): | |
139 | + self.points.append((x, y, z)) | |
140 | + self.point_actor2 = self.representation.GetRepresentation(x, y, z) | |
141 | + self.render.AddActor(self.point_actor2) | |
142 | + self.CreateMeasure() | |
143 | + | |
144 | + def CreateMeasure(self): | |
145 | + self._draw_line() | |
146 | + self._draw_text() | |
147 | + | |
148 | + def _draw_line(self): | |
149 | + line = vtk.vtkLineSource() | |
150 | + line.SetPoint1(self.points[0]) | |
151 | + line.SetPoint2(self.points[1]) | |
152 | + | |
153 | + c = vtk.vtkCoordinate() | |
154 | + c.SetCoordinateSystemToWorld() | |
155 | + | |
156 | + m = vtk.vtkPolyDataMapper2D() | |
157 | + m.SetInputConnection(line.GetOutputPort()) | |
158 | + m.SetTransformCoordinate(c) | |
159 | + | |
160 | + a = vtk.vtkActor2D() | |
161 | + a.SetMapper(m) | |
162 | + a.GetProperty().SetColor(self.color) | |
163 | + self.line_actor = a | |
164 | + self.render.AddActor(self.line_actor) | |
165 | + | |
166 | + def _draw_text(self): | |
167 | + p1, p2 = self.points | |
168 | + text = ' %.2f mm ' % \ | |
169 | + math.sqrt(vtk.vtkMath.Distance2BetweenPoints(p1, p2)) | |
170 | + x,y,z=[(i+j)/2 for i,j in zip(p1, p2)] | |
171 | + textsource = vtk.vtkTextSource() | |
172 | + textsource.SetText(text) | |
173 | + textsource.SetBackgroundColor((250/255.0, 247/255.0, 218/255.0)) | |
174 | + textsource.SetForegroundColor(self.color) | |
175 | + | |
176 | + m = vtk.vtkPolyDataMapper2D() | |
177 | + m.SetInputConnection(textsource.GetOutputPort()) | |
178 | + | |
179 | + a = vtk.vtkActor2D() | |
180 | + a.SetMapper(m) | |
181 | + a.DragableOn() | |
182 | + a.GetPositionCoordinate().SetCoordinateSystemToWorld() | |
183 | + a.GetPositionCoordinate().SetValue(x,y,z) | |
184 | + self.text_actor = a | |
185 | + self.render.AddActor(self.text_actor) | |
186 | + | |
187 | + def GetNumberOfPoints(self): | |
188 | + return len(self.points) | |
189 | + | |
190 | + def GetValue(self): | |
191 | + p1, p2 = self.points | |
192 | + return math.sqrt(vtk.vtkMath.Distance2BetweenPoints(p1, p2)) | |
193 | + | |
194 | + def SetVisibility(self, v): | |
195 | + self.point_actor1.SetVisibility(v) | |
196 | + self.point_actor2.SetVisibility(v) | |
197 | + self.line_actor.SetVisibility(v) | |
198 | + self.text_actor.SetVisibility(v) | |
199 | + | |
200 | + | |
201 | +class AngularMeasure(object): | |
202 | + def __init__(self, render, color=(1, 0, 0), representation=None): | |
203 | + self.color = color | |
204 | + self.points = [0, 0, 0] | |
205 | + self.number_of_points = 0 | |
206 | + self.point_actor1 = None | |
207 | + self.point_actor2 = None | |
208 | + self.point_actor3 = None | |
209 | + self.line_actor = None | |
210 | + self.render = render | |
211 | + if not representation: | |
212 | + representation = CirclePointRepresentation() | |
213 | + self.representation = representation | |
214 | + print color | |
215 | + | |
216 | + def SetPoint1(self, x, y, z): | |
217 | + self.points[0] = (x, y, z) | |
218 | + self.number_of_points = 1 | |
219 | + self.point_actor1 = self.representation.GetRepresentation(x, y, z) | |
220 | + self.render.AddActor(self.point_actor1) | |
221 | + | |
222 | + def SetPoint2(self, x, y, z): | |
223 | + self.number_of_points = 2 | |
224 | + self.points[1] = (x, y, z) | |
225 | + self.point_actor2 = self.representation.GetRepresentation(x, y, z) | |
226 | + self.render.AddActor(self.point_actor2) | |
227 | + | |
228 | + def SetPoint3(self, x, y, z): | |
229 | + self.number_of_points = 3 | |
230 | + self.points[2] = (x, y, z) | |
231 | + self.point_actor3 = self.representation.GetRepresentation(x, y, z) | |
232 | + self.render.AddActor(self.point_actor3) | |
233 | + | |
234 | + def _draw_line(self): | |
235 | + line1 = vtk.vtkLineSource() | |
236 | + line1.SetPoint1(self.points[0]) | |
237 | + line1.SetPoint2(self.points[1]) | |
238 | + | |
239 | + line2 = vtk.vtkLineSource() | |
240 | + line2.SetPoint1(self.points[1]) | |
241 | + line2.SetPoint2(self.points[2]) | |
242 | + | |
243 | + arc = self.DrawArc() | |
244 | + | |
245 | + line = vtk.vtkAppendPolyData() | |
246 | + line.AddInput(line1.GetOutput()) | |
247 | + line.AddInput(line2.GetOutput()) | |
248 | + line.AddInput(arc.GetOutput()) | |
249 | + | |
250 | + c = vtk.vtkCoordinate() | |
251 | + c.SetCoordinateSystemToWorld() | |
252 | + | |
253 | + m = vtk.vtkPolyDataMapper2D() | |
254 | + m.SetInputConnection(line.GetOutputPort()) | |
255 | + m.SetTransformCoordinate(c) | |
256 | + | |
257 | + a = vtk.vtkActor2D() | |
258 | + a.SetMapper(m) | |
259 | + a.GetProperty().SetColor(self.color) | |
260 | + self.line_actor = a | |
261 | + return a | |
262 | + | |
263 | + def DrawArc(self): | |
264 | + | |
265 | + d1 = math.sqrt(vtk.vtkMath.Distance2BetweenPoints(self.points[0], | |
266 | + self.points[1])) | |
267 | + d2 = math.sqrt(vtk.vtkMath.Distance2BetweenPoints(self.points[2], | |
268 | + self.points[1])) | |
269 | + | |
270 | + if d1 < d2: | |
271 | + d = d1 | |
272 | + p1 = self.points[0] | |
273 | + a,b,c = [j-i for i,j in zip(self.points[1], self.points[2])] | |
274 | + else: | |
275 | + d = d2 | |
276 | + p1 = self.points[2] | |
277 | + a,b,c = [j-i for i,j in zip(self.points[1], self.points[0])] | |
278 | + | |
279 | + t = (d / math.sqrt(a**2 + b**2 + c**2)) | |
280 | + x = self.points[1][0] + a*t | |
281 | + y = self.points[1][1] + b*t | |
282 | + z = self.points[1][2] + c*t | |
283 | + p2 = (x, y, z) | |
284 | + | |
285 | + arc = vtk.vtkArcSource() | |
286 | + arc.SetPoint1(p1) | |
287 | + arc.SetPoint2(p2) | |
288 | + arc.SetCenter(self.points[1]) | |
289 | + arc.SetResolution(20) | |
290 | + return arc | |
291 | + | |
292 | + def _draw_text(self): | |
293 | + text = u' %.2f ' % \ | |
294 | + self.CalculateAngle() | |
295 | + x,y,z= self.points[1] | |
296 | + textsource = vtk.vtkTextSource() | |
297 | + textsource.SetText(text) | |
298 | + textsource.SetBackgroundColor((250/255.0, 247/255.0, 218/255.0)) | |
299 | + textsource.SetForegroundColor(self.color) | |
300 | + | |
301 | + m = vtk.vtkPolyDataMapper2D() | |
302 | + m.SetInputConnection(textsource.GetOutputPort()) | |
303 | + | |
304 | + a = vtk.vtkActor2D() | |
305 | + a.SetMapper(m) | |
306 | + a.DragableOn() | |
307 | + a.GetPositionCoordinate().SetCoordinateSystemToWorld() | |
308 | + a.GetPositionCoordinate().SetValue(x,y,z) | |
309 | + self.text_actor = a | |
310 | + return a | |
311 | + | |
312 | + def GetNumberOfPoints(self): | |
313 | + return self.number_of_points | |
314 | + | |
315 | + def GetValue(self): | |
316 | + return self.CalculateAngle() | |
317 | + | |
318 | + def SetVisibility(self, v): | |
319 | + self.point_actor1.SetVisibility(v) | |
320 | + self.point_actor2.SetVisibility(v) | |
321 | + self.point_actor3.SetVisibility(v) | |
322 | + self.line_actor.SetVisibility(v) | |
323 | + self.text_actor.SetVisibility(v) | |
324 | + | |
325 | + def CalculateAngle(self): | |
326 | + """ | |
327 | + Calculate the angle between 2 vectors in 3D space. It is based on law of | |
328 | + cosines for vector. | |
329 | + The Alpha Cosine is equal the dot product from two vector divided for | |
330 | + product between the magnitude from that vectors. Then the angle is inverse | |
331 | + cosine. | |
332 | + """ | |
333 | + v1 = [j-i for i,j in zip(self.points[0], self.points[1])] | |
334 | + v2 = [j-i for i,j in zip(self.points[2], self.points[1])] | |
335 | + #print vtk.vtkMath.Normalize(v1) | |
336 | + #print vtk.vtkMath.Normalize(v2) | |
337 | + cos = vtk.vtkMath.Dot(v1, v2)/(vtk.vtkMath.Norm(v1)*vtk.vtkMath.Norm(v2)) | |
338 | + angle = math.degrees(math.acos(cos)) | |
339 | + return angle | |
340 | + | |
341 | + | ... | ... |
invesalius/data/viewer_volume.py
... | ... | @@ -30,6 +30,8 @@ import project as prj |
30 | 30 | import style as st |
31 | 31 | import utils |
32 | 32 | |
33 | +from data import measures | |
34 | + | |
33 | 35 | class Viewer(wx.Panel): |
34 | 36 | def __init__(self, parent): |
35 | 37 | wx.Panel.__init__(self, parent, size=wx.Size(320, 320)) |
... | ... | @@ -88,6 +90,10 @@ class Viewer(wx.Panel): |
88 | 90 | self.seed_points = [] |
89 | 91 | |
90 | 92 | self.points_reference = [] |
93 | + | |
94 | + self.measure_picker = vtk.vtkPointPicker() | |
95 | + self.measure_picker.SetTolerance(0.005) | |
96 | + self.measures = [] | |
91 | 97 | |
92 | 98 | |
93 | 99 | def __bind_events(self): |
... | ... | @@ -287,7 +293,11 @@ class Viewer(wx.Panel): |
287 | 293 | const.VOLUME_STATE_SEED: |
288 | 294 | { |
289 | 295 | "LeftButtonPressEvent": self.OnInsertSeed |
290 | - } | |
296 | + }, | |
297 | + const.STATE_LINEAR_MEASURE: | |
298 | + { | |
299 | + "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint | |
300 | + } | |
291 | 301 | } |
292 | 302 | |
293 | 303 | if state == const.STATE_WL: |
... | ... | @@ -309,6 +319,9 @@ class Viewer(wx.Panel): |
309 | 319 | self.interactor.SetInteractorStyle(style) |
310 | 320 | self.style = style |
311 | 321 | |
322 | + if state == const.STATE_LINEAR_MEASURE: | |
323 | + self.interactor.SetPicker(self.measure_picker) | |
324 | + | |
312 | 325 | # Check each event available for each mode |
313 | 326 | for event in action[state]: |
314 | 327 | # Bind event |
... | ... | @@ -590,6 +603,21 @@ class Viewer(wx.Panel): |
590 | 603 | self.seed_points.append(point_id) |
591 | 604 | self.interactor.Render() |
592 | 605 | |
606 | + def OnInsertLinearMeasurePoint(self, obj, evt): | |
607 | + print "Hey, you inserted measure point" | |
608 | + x,y = self.interactor.GetEventPosition() | |
609 | + self.measure_picker.Pick(x, y, 0, self.ren) | |
610 | + x, y, z = self.measure_picker.GetPickPosition() | |
611 | + if self.measure_picker.GetPointId() != -1: | |
612 | + if not self.measures or self.measures[-1].point_actor2: | |
613 | + m = measures.LinearMeasure(self.ren) | |
614 | + m.SetPoint1(x, y, z) | |
615 | + self.measures.append(m) | |
616 | + else: | |
617 | + m = self.measures[-1] | |
618 | + m.SetPoint2(x, y, z) | |
619 | + self.interactor.Render() | |
620 | + | |
593 | 621 | |
594 | 622 | class SlicePlane: |
595 | 623 | def __init__(self): | ... | ... |
invesalius/gui/data_notebook.py
... | ... | @@ -50,8 +50,8 @@ class NotebookPanel(wx.Panel): |
50 | 50 | |
51 | 51 | book.AddPage(MaskPage(book), _("Masks")) |
52 | 52 | book.AddPage(SurfacePage(book), _("Surfaces")) |
53 | - #book.AddPage(MeasuresListCtrlPanel(book), _("Measures")) | |
54 | - #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations")) | |
53 | + book.AddPage(MeasuresListCtrlPanel(book), _("Measures")) | |
54 | + book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations")) | |
55 | 55 | |
56 | 56 | book.SetSelection(0) |
57 | 57 | ... | ... |
invesalius/gui/default_tasks.py
... | ... | @@ -144,15 +144,15 @@ class LowerTaskPanel(wx.Panel): |
144 | 144 | # Fold 2 - Tools |
145 | 145 | # Measures |
146 | 146 | # Text Annotations |
147 | - #item = fold_panel.AddFoldPanel(_("Tools"), collapsed=False, | |
148 | - # foldIcons=image_list) | |
149 | - #style = fold_panel.GetCaptionStyle(item) | |
150 | - #col = style.GetFirstColour() | |
151 | - #self.enable_items.append(item) | |
152 | - # | |
153 | - #fold_panel.AddFoldPanelWindow(item, tools.TaskPanel(item), Spacing= 0, | |
154 | - # leftSpacing=0, rightSpacing=0) | |
155 | - #fold_panel.Expand(fold_panel.GetFoldPanel(1)) | |
147 | + item = fold_panel.AddFoldPanel(_("Tools"), collapsed=False, | |
148 | + foldIcons=image_list) | |
149 | + style = fold_panel.GetCaptionStyle(item) | |
150 | + col = style.GetFirstColour() | |
151 | + self.enable_items.append(item) | |
152 | + | |
153 | + fold_panel.AddFoldPanelWindow(item, tools.TaskPanel(item), Spacing= 0, | |
154 | + leftSpacing=0, rightSpacing=0) | |
155 | + fold_panel.Expand(fold_panel.GetFoldPanel(1)) | |
156 | 156 | |
157 | 157 | self.SetStateProjectClose() |
158 | 158 | self.__bind_events() | ... | ... |
invesalius/gui/task_tools.py
... | ... | @@ -18,9 +18,12 @@ |
18 | 18 | #-------------------------------------------------------------------------- |
19 | 19 | |
20 | 20 | import wx |
21 | +import wx.lib.embeddedimage as emb | |
21 | 22 | import wx.lib.hyperlink as hl |
22 | 23 | import wx.lib.platebtn as pbtn |
23 | -import wx.lib.embeddedimage as emb | |
24 | +import wx.lib.pubsub as ps | |
25 | + | |
26 | +import constants | |
24 | 27 | |
25 | 28 | ID_BTN_MEASURE_LINEAR = wx.NewId() |
26 | 29 | ID_BTN_MEASURE_ANGULAR = wx.NewId() |
... | ... | @@ -118,7 +121,9 @@ class InnerTaskPanel(wx.Panel): |
118 | 121 | print "TODO: Send Signal - Add text annotation (both 2d and 3d)" |
119 | 122 | |
120 | 123 | def OnLinkLinearMeasure(self): |
121 | - print "TODO: Send Signal - Add linear measure (both 2d and 3d)" | |
124 | + #print "TODO: Send Signal - Add linear measure (both 2d and 3d)" | |
125 | + ps.Publisher().sendMessage('Enable style', | |
126 | + constants.STATE_LINEAR_MEASURE) | |
122 | 127 | |
123 | 128 | def OnLinkAngularMeasure(self): |
124 | 129 | print "TODO: Send Signal - Add angular measure (both 2d and 3d)" | ... | ... |