Commit 1a15b12cdce9ad70e461fd3177fe10d77d35f52d
1 parent
4d583c07
Exists in
master
and in
6 other branches
ADD: Project close feature (under devel)
Showing
12 changed files
with
183 additions
and
74 deletions
Show diff stats
invesalius/constants.py
... | ... | @@ -85,10 +85,18 @@ SLICE_POSITION = {AXIAL:[AXIAL_SLICE_CAM_VIEW_UP, AXIAL_SLICE_CAM_POSITION], |
85 | 85 | PROJ_NEW = 0 |
86 | 86 | PROJ_OPEN = 1 |
87 | 87 | PROJ_CHANGE = 2 |
88 | +PROJ_CLOSE = 3 | |
88 | 89 | |
89 | 90 | PROJ_MAX = 4 |
90 | 91 | |
91 | 92 | |
93 | +#### | |
94 | +MODE_RP = 0 | |
95 | +MODE_NAVIGATOR = 1 | |
96 | +MODE_RADIOLOGY = 2 | |
97 | +MODE_ODONTOLOGY = 3 | |
98 | + | |
99 | + | |
92 | 100 | |
93 | 101 | #Color Table from Slice |
94 | 102 | #NumberOfColors, SaturationRange, HueRange, ValueRange | ... | ... |
invesalius/control.py
... | ... | @@ -27,7 +27,8 @@ import constants as const |
27 | 27 | import project as prj |
28 | 28 | |
29 | 29 | import data.imagedata_utils as utils |
30 | -import data.surface as surface | |
30 | +import data.mask as msk | |
31 | +import data.surface as srf | |
31 | 32 | import data.volume as volume |
32 | 33 | import reader.dicom_grouper as dg |
33 | 34 | import gui.dialogs as dialog |
... | ... | @@ -40,7 +41,7 @@ DEFAULT_THRESH_MODE = 0 |
40 | 41 | class Controller(): |
41 | 42 | |
42 | 43 | def __init__(self, frame): |
43 | - self.surface_manager = surface.SurfaceManager() | |
44 | + self.surface_manager = srf.SurfaceManager() | |
44 | 45 | self.volume = volume.Volume() |
45 | 46 | self.__bind_events() |
46 | 47 | self.frame = frame |
... | ... | @@ -51,7 +52,6 @@ class Controller(): |
51 | 52 | |
52 | 53 | def __bind_events(self): |
53 | 54 | ps.Publisher().subscribe(self.OnImportMedicalImages, 'Import directory') |
54 | - #ps.Publisher().subscribe(self.StartImportPanel, "Load data to import panel") | |
55 | 55 | ps.Publisher().subscribe(self.OnShowDialogImportDirectory, |
56 | 56 | 'Show import directory dialog') |
57 | 57 | ps.Publisher().subscribe(self.OnShowDialogOpenProject, |
... | ... | @@ -68,8 +68,6 @@ class Controller(): |
68 | 68 | ps.Publisher().subscribe(self.Progress, "Update dicom load") |
69 | 69 | ps.Publisher().subscribe(self.OnLoadImportPanel, "End dicom load") |
70 | 70 | ps.Publisher().subscribe(self.OnCancelImport, 'Cancel DICOM load') |
71 | - #ps.Publisher().subscribe(self.OnSaveProject, 'Save Project') | |
72 | - #ps.Publisher().subscribe(self.OnOpenProject, 'Open Project') | |
73 | 71 | ps.Publisher().subscribe(self.OnCloseProject, 'Close Project') |
74 | 72 | |
75 | 73 | |
... | ... | @@ -110,6 +108,12 @@ class Controller(): |
110 | 108 | session = ses.Session() |
111 | 109 | session.OpenProject(path) |
112 | 110 | |
111 | + mask = msk.Mask() | |
112 | + mask._set_class_index(proj.last_mask_index) | |
113 | + | |
114 | + surface = srf.Surface() | |
115 | + surface._set_class_index(proj.last_surface_index) | |
116 | + | |
113 | 117 | self.LoadProject() |
114 | 118 | |
115 | 119 | def ShowDialogSaveProject(self, saveas=False): |
... | ... | @@ -139,6 +143,26 @@ class Controller(): |
139 | 143 | proj = prj.Project() |
140 | 144 | prj.Project().SavePlistProject(dirpath, filename) |
141 | 145 | |
146 | + def CloseProject(self): | |
147 | + print "Close Project" | |
148 | + session = ses.Session() | |
149 | + session.CloseProject() | |
150 | + | |
151 | + proj = prj.Project() | |
152 | + proj.Close() | |
153 | + | |
154 | + # TODO: | |
155 | + # Remove items from combo of masks | |
156 | + # Remove items from combo of surfaces | |
157 | + # Remove items from dictionaries | |
158 | + # Slice | |
159 | + # Surface | |
160 | + # -------------- | |
161 | + # | |
162 | + | |
163 | + ps.Publisher().sendMessage('Hide content panel') | |
164 | + | |
165 | + | |
142 | 166 | ################################## |
143 | 167 | |
144 | 168 | |
... | ... | @@ -348,11 +372,15 @@ class Controller(): |
348 | 372 | answer = dialog.SaveChangesDialog(filename) |
349 | 373 | if not answer: |
350 | 374 | print "Close without changes" |
375 | + self.CloseProject() | |
351 | 376 | elif answer == 1: |
377 | + self.ShowDialogSaveProject() | |
352 | 378 | print "Save changes and close" |
379 | + self.CloseProject() | |
353 | 380 | #else: |
354 | 381 | # print "Cancel" |
355 | 382 | else: |
356 | 383 | print ":) Close without changes" |
384 | + self.CloseProject() | |
357 | 385 | |
358 | 386 | ... | ... |
invesalius/data/mask.py
invesalius/data/slice_.py
... | ... | @@ -85,6 +85,19 @@ class Slice(object): |
85 | 85 | ps.Publisher().subscribe(self.InputImageWidget, 'Input Image in the widget') |
86 | 86 | ps.Publisher().subscribe(self.OnExportMask,'Export mask to file') |
87 | 87 | |
88 | + ps.Publisher().subscribe(self.OnCloseProject, 'Close Project') | |
89 | + | |
90 | + def OnCloseProject(self, pubsub_evt): | |
91 | + self.CloseProject() | |
92 | + | |
93 | + def CloseProject(self): | |
94 | + self.imagedata = None | |
95 | + self.current_mask = None | |
96 | + self.blend_filter = None | |
97 | + #self.blend_filter = None | |
98 | + #self.num_gradient = 0 | |
99 | + | |
100 | + | |
88 | 101 | def __set_current_mask_threshold_limits(self, pubsub_evt): |
89 | 102 | thresh_min = pubsub_evt.data[0] |
90 | 103 | thresh_max = pubsub_evt.data[1] |
... | ... | @@ -523,8 +536,8 @@ class Slice(object): |
523 | 536 | |
524 | 537 | # insert new mask into project and retrieve its index |
525 | 538 | proj = Project() |
526 | - proj.AddMask(future_mask.index, future_mask) | |
527 | - | |
539 | + index = proj.AddMask(future_mask) | |
540 | + future_mask.index = index | |
528 | 541 | |
529 | 542 | # update gui related to mask |
530 | 543 | ps.Publisher().sendMessage('Add mask', | ... | ... |
invesalius/data/surface.py
... | ... | @@ -71,7 +71,8 @@ class Surface(): |
71 | 71 | else: |
72 | 72 | setattr(self, key, surface[key]) |
73 | 73 | |
74 | - | |
74 | + def _set_class_index(self, index): | |
75 | + Surface.general_index = index | |
75 | 76 | |
76 | 77 | |
77 | 78 | # TODO: will be initialized inside control as it is being done? |
... | ... | @@ -102,6 +103,15 @@ class SurfaceManager(): |
102 | 103 | ps.Publisher().subscribe(self.OnShowSurface, 'Show surface') |
103 | 104 | ps.Publisher().subscribe(self.OnExportSurface,'Export surface to file') |
104 | 105 | ps.Publisher().subscribe(self.OnLoadSurfaceDict, 'Load surface dict') |
106 | + ps.Publisher().subscribe(self.OnCloseProject, 'Close Project') | |
107 | + | |
108 | + def OnCloseProject(self, pubsub_evt): | |
109 | + self.CloseProject() | |
110 | + | |
111 | + def CloseProject(self): | |
112 | + del self.actors_dict | |
113 | + self.actors_dict = {} | |
114 | + | |
105 | 115 | |
106 | 116 | def OnLoadSurfaceDict(self, pubsub_evt): |
107 | 117 | surface_dict = pubsub_evt.data |
... | ... | @@ -291,7 +301,8 @@ class SurfaceManager(): |
291 | 301 | |
292 | 302 | # Append surface into Project.surface_dict |
293 | 303 | proj = prj.Project() |
294 | - proj.surface_dict[surface.index] = surface | |
304 | + index = proj.AddSurface(surface) | |
305 | + surface.index = index | |
295 | 306 | |
296 | 307 | |
297 | 308 | session = ses.Session() | ... | ... |
invesalius/gui/data_notebook.py
... | ... | @@ -84,7 +84,13 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
84 | 84 | 'Change mask colour in notebook') |
85 | 85 | |
86 | 86 | ps.Publisher().subscribe(self.OnChangeCurrentMask, 'Change mask selected') |
87 | - | |
87 | + ps.Publisher().subscribe(self.OnCloseProject, 'Close Project') | |
88 | + | |
89 | + def OnCloseProject(self, pubsub_evt): | |
90 | + self.DeleteAllItems() | |
91 | + self.mask_list_index = {} | |
92 | + self.mask_bmp_idx_to_name = {} | |
93 | + | |
88 | 94 | def OnChangeCurrentMask(self, pubsub_evt): |
89 | 95 | |
90 | 96 | mask_index = pubsub_evt.data |
... | ... | @@ -211,6 +217,12 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
211 | 217 | 'Set surface transparency') |
212 | 218 | ps.Publisher().subscribe(self.EditSurfaceColour, |
213 | 219 | 'Set surface colour') |
220 | + ps.Publisher().subscribe(self.OnCloseProject, 'Close Project') | |
221 | + | |
222 | + def OnCloseProject(self, pubsub_evt): | |
223 | + self.DeleteAllItems() | |
224 | + self.surface_list_index = {} | |
225 | + self.surface_bmp_idx_to_name = {} | |
214 | 226 | |
215 | 227 | def __bind_events_wx(self): |
216 | 228 | self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) | ... | ... |
invesalius/gui/dialogs.py
... | ... | @@ -132,13 +132,9 @@ WILDCARD_OPEN = "InVesalius 3 project (*.inv3)|*.inv3|"\ |
132 | 132 | |
133 | 133 | def ShowOpenProjectDialog(): |
134 | 134 | # Default system path |
135 | - if sys.platform == 'win32': | |
136 | - default_path = "" | |
137 | - else: | |
138 | - default_path = os.getcwd() | |
139 | 135 | |
140 | 136 | dlg = wx.FileDialog(None, message="Open InVesalius 3 project...", |
141 | - defaultDir=default_path, | |
137 | + defaultDir="", | |
142 | 138 | defaultFile="", wildcard=WILDCARD_OPEN, |
143 | 139 | style=wx.OPEN|wx.CHANGE_DIR) |
144 | 140 | ... | ... |
invesalius/gui/frame.py
... | ... | @@ -89,7 +89,7 @@ class Frame(wx.Frame): |
89 | 89 | ps.Publisher().subscribe(self.HideImportPanel, 'Hide import panel') |
90 | 90 | ps.Publisher().subscribe(self.BeginBusyCursor, 'Begin busy cursor') |
91 | 91 | ps.Publisher().subscribe(self.EndBusyCursor, 'End busy cursor') |
92 | - | |
92 | + ps.Publisher().subscribe(self.HideContentPanel, 'Hide content panel') | |
93 | 93 | |
94 | 94 | def EndBusyCursor(self, pubsub_evt=None): |
95 | 95 | wx.EndBusyCursor() |
... | ... | @@ -209,6 +209,12 @@ class Frame(wx.Frame): |
209 | 209 | aui_manager.GetPane("Tasks").Show(1) |
210 | 210 | aui_manager.Update() |
211 | 211 | |
212 | + def HideContentPanel(self, pubsub_evt): | |
213 | + aui_manager = self.aui_manager | |
214 | + aui_manager.GetPane("Data").Show(0) | |
215 | + aui_manager.GetPane("Tasks").Show(1) | |
216 | + aui_manager.Update() | |
217 | + | |
212 | 218 | def OnSize(self, evt): |
213 | 219 | ps.Publisher().sendMessage(('ProgressBar Reposition')) |
214 | 220 | evt.Skip() | ... | ... |
invesalius/gui/task_slice.py
... | ... | @@ -291,6 +291,19 @@ class MaskProperties(wx.Panel): |
291 | 291 | 'Set threshold values in gradient') |
292 | 292 | ps.Publisher().subscribe(self.SelectMaskName, 'Select mask name in combo') |
293 | 293 | ps.Publisher().subscribe(self.ChangeMaskName, 'Change mask name') |
294 | + ps.Publisher().subscribe(self.OnCloseProject, 'Close Project') | |
295 | + | |
296 | + def OnCloseProject(self, pubsub_evt): | |
297 | + self.CloseProject() | |
298 | + | |
299 | + def CloseProject(self): | |
300 | + n = self.combo_mask_name.GetCount() | |
301 | + for i in xrange(n-1, -1, -1): | |
302 | + self.combo_mask_name.Delete(i) | |
303 | + n = self.combo_thresh.GetCount() | |
304 | + for i in xrange(n-1, -1, -1): | |
305 | + self.combo_thresh.Delete(i) | |
306 | + | |
294 | 307 | |
295 | 308 | def __bind_events_wx(self): |
296 | 309 | self.Bind(grad.EVT_THRESHOLD_CHANGE, self.OnSlideChanged, self.gradient) | ... | ... |
invesalius/gui/task_surface.py
... | ... | @@ -339,11 +339,19 @@ class SurfaceProperties(wx.Panel): |
339 | 339 | def __bind_events(self): |
340 | 340 | ps.Publisher().subscribe(self.InsertNewSurface, |
341 | 341 | 'Update surface info in GUI') |
342 | - ps.Publisher().subscribe(self.ChangeMaskName, | |
342 | + ps.Publisher().subscribe(self.ChangeSurfaceName, | |
343 | 343 | 'Change surface name') |
344 | + ps.Publisher().subscribe(self.OnCloseProject, 'Close Project') | |
344 | 345 | |
346 | + def OnCloseProject(self, pubsub_evt): | |
347 | + self.CloseProject() | |
345 | 348 | |
346 | - def ChangeMaskName(self, pubsub_evt): | |
349 | + def CloseProject(self): | |
350 | + n = self.combo_surface_name.GetCount() | |
351 | + for i in xrange(n-1, -1, -1): | |
352 | + self.combo_surface_name.Delete(i) | |
353 | + | |
354 | + def ChangeSurfaceName(self, pubsub_evt): | |
347 | 355 | index, name = pubsub_evt.data |
348 | 356 | self.combo_surface_name.SetString(index, name) |
349 | 357 | self.combo_surface_name.Refresh() | ... | ... |
invesalius/project.py
... | ... | @@ -43,80 +43,61 @@ class Project(object): |
43 | 43 | __metaclass__= Singleton |
44 | 44 | |
45 | 45 | def __init__(self): |
46 | - # TODO: Discuss | |
47 | - # [Tati] Will this type of data be written on project file? What if user | |
48 | - # changes file name and directory? I guess no... But, who knows... | |
49 | - #self.name = "Default" | |
50 | - #self.dir_ = "C:\\" | |
51 | - | |
52 | - # Original vtkImageData, build based on medical images read. | |
53 | - # To be used for general 2D slices rendering both on 2D and 3D | |
54 | - # coordinate systems. It might be used, as well, for 3D raycasting. | |
55 | - # rendering. | |
56 | - # TODO: Discuss when this will be used. | |
57 | - self.imagedata = '' | |
58 | - | |
46 | + # Patient/ acquistion information | |
59 | 47 | self.name = '' |
60 | 48 | #self.dicom = '' |
61 | 49 | self.modality = '' |
62 | - self.original_orientation = -1 | |
63 | - | |
64 | - # Masks are related to vtkImageData | |
65 | - self.mask_dict = {} | |
66 | - # Predefined threshold values | |
50 | + self.original_orientation = '' | |
67 | 51 | self.min_threshold = '' |
68 | 52 | self.max_threshold = '' |
69 | - | |
70 | 53 | self.window = '' |
71 | 54 | self.level = '' |
72 | 55 | |
56 | + # Original imagedata (shouldn't be changed) | |
57 | + self.imagedata = '' | |
58 | + | |
59 | + # Masks (vtkImageData) | |
60 | + self.mask_dict = {} | |
61 | + self.last_mask_index = 0 | |
62 | + | |
63 | + # Surfaces are (vtkPolyData) | |
64 | + self.surface_dict = {} | |
65 | + self.last_surface_index = -1 | |
66 | + | |
67 | + # TODO: Future | |
68 | + self.measure_dict = {} | |
69 | + | |
70 | + # TODO: Future ++ | |
71 | + self.annotation_dict = {} | |
72 | + | |
73 | + # InVesalius related data | |
74 | + # So we can find bugs and reproduce user-related problems | |
75 | + self.invesalius_version = version.get_svn_revision() | |
76 | + | |
73 | 77 | self.presets = Presets() |
78 | + | |
74 | 79 | self.threshold_modes = self.presets.thresh_ct |
75 | 80 | self.threshold_range = '' |
76 | - | |
77 | - self.original_orientation = '' | |
78 | - # MRI ? CT? | |
79 | 81 | |
82 | + self.raycasting_preset = '' | |
80 | 83 | |
81 | - # TODO: define how we will relate these threshold values to | |
82 | - # default threshold labels | |
83 | - # TODO: Future + | |
84 | - # Allow insertion of new threshold modes | |
85 | 84 | |
86 | - # Surfaces are related to vtkPolyDataa | |
87 | - self.surface_dict = {} | |
88 | 85 | #self.surface_quality_list = ["Low", "Medium", "High", "Optimal *", |
89 | - # "Custom"] | |
86 | + # "Custom"i] | |
87 | + | |
90 | 88 | # TOOD: define how we will relate this quality possibilities to |
91 | 89 | # values set as decimate / smooth |
92 | 90 | # TODO: Future + |
93 | 91 | # Allow insertion of new surface quality modes |
94 | 92 | |
95 | - self.measure_dict = {} | |
93 | + def Close(self): | |
94 | + for name in self.__dict__: | |
95 | + attr = getattr(self, name) | |
96 | + del attr | |
96 | 97 | |
97 | - # TODO: Future ++ | |
98 | - #self.annotation_dict = {} | |
98 | + self.__init__() | |
99 | 99 | |
100 | - # TODO: Future + | |
101 | - # Volume rendering modes related to vtkImageData | |
102 | - # this will need to be inserted both in the project and in the user | |
103 | - # InVesalius configuration file | |
104 | - # self.render_mode = {} | |
105 | - | |
106 | - # The raycasting preset setted in this project | |
107 | - self.raycasting_preset = '' | |
108 | - | |
109 | - self.invesalius_version = version.get_svn_revision() | |
110 | - print self.invesalius_version | |
111 | - | |
112 | - #self.save_as = True | |
113 | - | |
114 | - #self.path = "" | |
115 | - self.debug = 0 | |
116 | - | |
117 | - ####### MASK OPERATIONS | |
118 | - | |
119 | - def AddMask(self, index, mask): | |
100 | + def AddMask(self, mask): | |
120 | 101 | """ |
121 | 102 | Insert new mask (Mask) into project data. |
122 | 103 | |
... | ... | @@ -126,11 +107,37 @@ class Project(object): |
126 | 107 | output |
127 | 108 | @ index: index of item that was inserted |
128 | 109 | """ |
110 | + self.last_mask_index = mask.index | |
111 | + index = len(self.mask_dict) | |
129 | 112 | self.mask_dict[index] = mask |
113 | + return index | |
114 | + | |
115 | + def RemoveMask(self, index): | |
116 | + new_dict = {} | |
117 | + for i in self.mask_dict: | |
118 | + if i < index: | |
119 | + new_dict[i] = self.mask_dict[i] | |
120 | + if i > index: | |
121 | + new_dict[i-1] = self.mask_dict[i] | |
122 | + self.mask_dict = new_dict | |
130 | 123 | |
131 | 124 | def GetMask(self, index): |
132 | 125 | return self.mask_dict[index] |
133 | 126 | |
127 | + def AddSurface(self, surface): | |
128 | + self.last_surface_index = surface.index | |
129 | + index = len(self.surface_dict) | |
130 | + self.surface_dict[index] = surface | |
131 | + return index | |
132 | + | |
133 | + def RemoveSurface(self, index): | |
134 | + new_dict = {} | |
135 | + for i in self.surface_dict: | |
136 | + if i < index: | |
137 | + new_dict[i] = self.surface_dict[i] | |
138 | + if i > index: | |
139 | + new_dict[i-1] = self.surface_dict[i] | |
140 | + self.surface_dict = new_dict | |
134 | 141 | |
135 | 142 | def SetAcquisitionModality(self, type_=None): |
136 | 143 | if type_ is None: | ... | ... |
invesalius/session.py
... | ... | @@ -11,10 +11,11 @@ class Session(object): |
11 | 11 | def __init__(self): |
12 | 12 | self.project_path = () |
13 | 13 | |
14 | - self.project_status = const.PROJ_NEW | |
15 | - # const.PROJ_NEW*, const.PROJ_OPEN, const.PROJ_CHANGE* | |
14 | + self.project_status = const.PROJ_CLOSE | |
15 | + # const.PROJ_NEW*, const.PROJ_OPEN, const.PROJ_CHANGE*, | |
16 | + # const.PROJ_CLOSE | |
16 | 17 | |
17 | - self.mode = "" | |
18 | + self.mode = const.MODE_RP | |
18 | 19 | # const.MODE_RP, const.MODE_NAVIGATOR, const.MODE_RADIOLOGY, |
19 | 20 | # const.MODE_ODONTOLOGY |
20 | 21 | |
... | ... | @@ -30,6 +31,11 @@ class Session(object): |
30 | 31 | # Recent projects list |
31 | 32 | self.recent_projects = [] |
32 | 33 | |
34 | + def CloseProject(self): | |
35 | + self.project_path = () | |
36 | + self.project_status = const.PROJ_CLOSE | |
37 | + self.mode = const.MODE_RP | |
38 | + self.temp_item = False | |
33 | 39 | |
34 | 40 | def SaveProject(self, path=()): |
35 | 41 | self.project_status = const.PROJ_OPEN | ... | ... |