Commit 088d7b8b7090e0e55678f33f32c1d6085ee1b2e6
1 parent
a3665303
Exists in
master
and in
68 other branches
FIX: Save existing project under OSX
Showing
9 changed files
with
246 additions
and
64 deletions
Show diff stats
invesalius/constants.py
... | ... | @@ -78,10 +78,17 @@ SLICE_POSITION = {AXIAL:[AXIAL_SLICE_CAM_VIEW_UP, AXIAL_SLICE_CAM_POSITION], |
78 | 78 | SAGITAL:[SAGITAL_SLICE_CAM_VIEW_UP, SAGITAL_SLICE_CAM_POSITION], |
79 | 79 | CORONAL:[CORONAL_SLICE_CAM_VIEW_UP, CORONAL_SLICE_CAM_POSITION]} |
80 | 80 | #Project Status |
81 | -NEW_PROJECT = 0 | |
82 | -OPEN_PROJECT = 1 | |
83 | -CHANGE_PROJECT = 2 | |
84 | -SAVE_PROJECT = 3 | |
81 | +#NEW_PROJECT = 0 | |
82 | +#OPEN_PROJECT = 1 | |
83 | +#CHANGE_PROJECT = 2 | |
84 | +#SAVE_PROJECT = 3 | |
85 | +PROJ_NEW = 0 | |
86 | +PROJ_OPEN = 1 | |
87 | +PROJ_CHANGE = 2 | |
88 | + | |
89 | +PROJ_MAX = 4 | |
90 | + | |
91 | + | |
85 | 92 | |
86 | 93 | #Color Table from Slice |
87 | 94 | #NumberOfColors, SaturationRange, HueRange, ValueRange | ... | ... |
invesalius/control.py
... | ... | @@ -33,7 +33,7 @@ import reader.dicom_grouper as dg |
33 | 33 | import gui.dialogs as dialog |
34 | 34 | import reader.dicom_reader as dcm |
35 | 35 | import reader.analyze_reader as analyze |
36 | -import session | |
36 | +import session as ses | |
37 | 37 | |
38 | 38 | DEFAULT_THRESH_MODE = 0 |
39 | 39 | |
... | ... | @@ -47,7 +47,7 @@ class Controller(): |
47 | 47 | self.progress_dialog = None |
48 | 48 | self.cancel_import = False |
49 | 49 | #Init session |
50 | - session.Session() | |
50 | + session = ses.Session() | |
51 | 51 | |
52 | 52 | def __bind_events(self): |
53 | 53 | ps.Publisher().subscribe(self.OnImportMedicalImages, 'Import directory') |
... | ... | @@ -63,6 +63,7 @@ class Controller(): |
63 | 63 | ps.Publisher().subscribe(self.OnCancelImport, 'Cancel DICOM load') |
64 | 64 | ps.Publisher().subscribe(self.OnSaveProject, 'Save Project') |
65 | 65 | ps.Publisher().subscribe(self.OnOpenProject, 'Open Project') |
66 | + ps.Publisher().subscribe(self.OnCloseProject, 'Close Project') | |
66 | 67 | |
67 | 68 | |
68 | 69 | def OnCancelImport(self, pubsub_evt): |
... | ... | @@ -136,6 +137,13 @@ class Controller(): |
136 | 137 | |
137 | 138 | def LoadProject(self): |
138 | 139 | proj = prj.Project() |
140 | + | |
141 | + const.THRESHOLD_OUTVALUE = proj.threshold_range[0] | |
142 | + const.THRESHOLD_INVALUE = proj.threshold_range[1] | |
143 | + const.WINDOW_LEVEL['Default'] = (proj.window, proj.level) | |
144 | + const.WINDOW_LEVEL['Manual'] = (proj.window, proj.level) | |
145 | + | |
146 | + | |
139 | 147 | ps.Publisher().sendMessage('Set project name', proj.name) |
140 | 148 | ps.Publisher().sendMessage('Load slice to viewer', |
141 | 149 | (proj.imagedata, |
... | ... | @@ -162,11 +170,6 @@ class Controller(): |
162 | 170 | proj.window = proj.threshold_range[1] - proj.threshold_range[0] |
163 | 171 | proj.level = (0.5 * (proj.threshold_range[1] + proj.threshold_range[0])) |
164 | 172 | |
165 | - const.THRESHOLD_OUTVALUE = proj.threshold_range[0] | |
166 | - const.THRESHOLD_INVALUE = proj.threshold_range[1] | |
167 | - const.WINDOW_LEVEL['Default'] = (proj.window, proj.level) | |
168 | - const.WINDOW_LEVEL['Manual'] = (proj.window, proj.level) | |
169 | - | |
170 | 173 | |
171 | 174 | def CreateDicomProject(self, imagedata, dicom): |
172 | 175 | name_to_const = {"AXIAL":const.AXIAL, |
... | ... | @@ -185,10 +188,14 @@ class Controller(): |
185 | 188 | proj.level = float(dicom.image.level) |
186 | 189 | proj.threshold_range = imagedata.GetScalarRange() |
187 | 190 | |
188 | - const.THRESHOLD_OUTVALUE = proj.threshold_range[0] | |
189 | - const.THRESHOLD_INVALUE = proj.threshold_range[1] | |
190 | - const.WINDOW_LEVEL['Default'] = (proj.window, proj.level) | |
191 | - const.WINDOW_LEVEL['Manual'] = (proj.window, proj.level) | |
191 | + | |
192 | + ###### | |
193 | + session = ses.Session() | |
194 | + filename = proj.name+".inv3" | |
195 | + dirpath = session.CreateProject(filename) | |
196 | + proj.SavePlistProject(dirpath, filename) | |
197 | + | |
198 | + | |
192 | 199 | |
193 | 200 | def OnOpenDicomGroup(self, pubsub_evt): |
194 | 201 | group = pubsub_evt.data |
... | ... | @@ -259,33 +266,68 @@ class Controller(): |
259 | 266 | plistlib.writePlist(preset, preset_dir) |
260 | 267 | |
261 | 268 | def OnSaveProject(self, pubsub_evt): |
269 | + session = ses.Session() | |
262 | 270 | |
263 | - if not(pubsub_evt.data): | |
264 | - filename = prj.Project().path | |
265 | - else: | |
266 | - filename = pubsub_evt.data | |
267 | - dir_,filename = os.path.split(filename) | |
268 | - | |
269 | - if not (filename): | |
270 | - filename = prj.Project().name | |
271 | + path = pubsub_evt.data | |
272 | + if path: | |
273 | + print "----- FILENAME" | |
274 | + dirpath, filename = os.path.split(path) | |
275 | + session.SaveProject((dirpath, filename)) | |
271 | 276 | else: |
272 | - filename = filename.replace(' ','_') | |
273 | - prj.Project().name = filename | |
274 | - prj.Project().path = filename | |
275 | - print prj.Project().path | |
276 | - prj.Project().SavePlistProject(dir_, filename) | |
277 | - session.Session().project_status = const.SAVE_PROJECT | |
277 | + dirpath, filename = session.project_path | |
278 | + | |
279 | + print "$$$$$$$$$$$$$$$$$$$$$$$$$$" | |
280 | + print "filename: ", filename | |
281 | + print "dirpath: ", dirpath | |
282 | + | |
283 | + proj = prj.Project() | |
284 | + prj.Project().SavePlistProject(dirpath, filename) | |
285 | + | |
286 | + | |
287 | + | |
288 | + | |
289 | + #if not(pubsub_evt.data): | |
290 | + # filename = prj.Project().path | |
291 | + #else: | |
292 | + # filename = pubsub_evt.data | |
293 | + #dir_,filename = os.path.split(filename) | |
294 | + | |
295 | + #if not (filename): | |
296 | + # filename = prj.Project().name | |
297 | + #else: | |
298 | + # filename = filename.replace(' ','_') | |
299 | + # prj.Project().name = filename | |
300 | + #prj.Project().path = filename | |
301 | + #print prj.Project().path | |
302 | + | |
303 | + #prj.Project().SavePlistProject(dirpath, filename) | |
304 | + | |
305 | + #session.project_status = const.PROJ_OPEN | |
306 | + #session.project_path = (dirpath, filename) | |
278 | 307 | |
279 | 308 | def OnOpenProject(self, pubsub_evt): |
280 | - filename = os.path.abspath(pubsub_evt.data) | |
281 | - session.Session().project_status = const.OPEN_PROJECT | |
309 | + path = os.path.abspath(pubsub_evt.data) | |
310 | + | |
282 | 311 | proj = prj.Project() |
283 | - proj.OpenPlistProject(filename) | |
312 | + proj.OpenPlistProject(path) | |
284 | 313 | proj.SetAcquisitionModality(proj.modality) |
285 | - proj.save_as = False | |
286 | - proj.path = filename | |
287 | - const.THRESHOLD_OUTVALUE = proj.threshold_range[0] | |
288 | - const.THRESHOLD_INVALUE = proj.threshold_range[1] | |
289 | - const.WINDOW_LEVEL['Default'] = (proj.window, proj.level) | |
290 | - const.WINDOW_LEVEL['Manual'] = (proj.window, proj.level) | |
314 | + ###proj.path = filename | |
315 | + ###proj.save_as = False | |
316 | + | |
317 | + session = ses.Session() | |
318 | + session.OpenProject(path) | |
319 | + | |
291 | 320 | self.LoadProject() |
321 | + | |
322 | + def OnCloseProject(self, pubsub_evt): | |
323 | + print "OnCloseProject" | |
324 | + session = ses.Session() | |
325 | + st = session.project_status | |
326 | + filename = session.project_path[1] | |
327 | + if (st == const.PROJ_NEW) or (st == const.PROJ_CHANGE): | |
328 | + answer = dialog.SaveChangesDialog(filename) | |
329 | + if not answer: | |
330 | + print "Delete all" | |
331 | + elif answer > 1: | |
332 | + print "Save" | |
333 | + | ... | ... |
invesalius/data/slice_.py
... | ... | @@ -25,7 +25,7 @@ import constants as const |
25 | 25 | import imagedata_utils as iu |
26 | 26 | from mask import Mask |
27 | 27 | from project import Project |
28 | -import session | |
28 | +import session as ses | |
29 | 29 | from utils import Singleton |
30 | 30 | |
31 | 31 | |
... | ... | @@ -115,9 +115,10 @@ class Slice(object): |
115 | 115 | self.SetMaskEditionThreshold(index, threshold_range) |
116 | 116 | |
117 | 117 | def __set_current_mask_threshold(self, evt_pubsub): |
118 | + session = ses.Session() | |
118 | 119 | #FIXME: find a better way to implement this |
119 | 120 | if (self.num_gradient >= 2) or \ |
120 | - (session.Session().project_status != const.OPEN_PROJECT): | |
121 | + (session.project_status != const.PROJ_OPEN): | |
121 | 122 | threshold_range = evt_pubsub.data |
122 | 123 | index = self.current_mask.index |
123 | 124 | self.SetMaskThreshold(index, threshold_range) |
... | ... | @@ -180,11 +181,19 @@ class Slice(object): |
180 | 181 | if update: |
181 | 182 | ps.Publisher().sendMessage('Update slice viewer') |
182 | 183 | |
184 | + session = ses.Session() | |
185 | + session.ChangeProject() | |
186 | + | |
187 | + | |
183 | 188 | def SetMaskName(self, index, name): |
184 | 189 | "Rename a mask given its index and the new name" |
185 | 190 | proj = Project() |
186 | 191 | proj.mask_dict[index].name = name |
187 | 192 | |
193 | + session = ses.Session() | |
194 | + session.ChangeProject() | |
195 | + | |
196 | + | |
188 | 197 | def SetMaskEditionThreshold(self, index, threshold_range): |
189 | 198 | "Set threshold bounds to be used while editing slice" |
190 | 199 | proj = Project() |
... | ... | @@ -224,8 +233,13 @@ class Slice(object): |
224 | 233 | proj.mask_dict[index].threshold_range = threshold_range |
225 | 234 | |
226 | 235 | proj = Project() |
227 | - proj.mask_dict[self.current_mask.index ].threshold_range = threshold_range | |
228 | - | |
236 | + proj.mask_dict[self.current_mask.index].threshold_range = threshold_range | |
237 | + | |
238 | + session = ses.Session() | |
239 | + session.ChangeProject() | |
240 | + | |
241 | + | |
242 | + | |
229 | 243 | |
230 | 244 | def ShowMask(self, index, value): |
231 | 245 | "Show a mask given its index and 'show' value (0: hide, other: show)" |
... | ... | @@ -246,6 +260,10 @@ class Slice(object): |
246 | 260 | imagedata = self.current_mask.imagedata |
247 | 261 | imagedata.SetScalarComponentFromDouble(x, y, z, 0, colour) |
248 | 262 | self.current_mask.edited_points[(x, y, z)] = colour |
263 | + | |
264 | + session = ses.Session() | |
265 | + session.ChangeProject() | |
266 | + | |
249 | 267 | |
250 | 268 | def DrawPixel(self, position, colour=None): |
251 | 269 | "Draw pixel, based on x, y and z position coordinates." |
... | ... | @@ -255,6 +273,10 @@ class Slice(object): |
255 | 273 | imagedata.SetScalarComponentFromDouble(x, y, z, 0, colour) |
256 | 274 | self.current_mask.edited_points[(x, y, z)] = colour |
257 | 275 | |
276 | + session = ses.Session() | |
277 | + session.ChangeProject() | |
278 | + | |
279 | + | |
258 | 280 | def EditPixelBasedOnThreshold(self, position): |
259 | 281 | "Erase or draw pixel based on edition threshold range." |
260 | 282 | x, y, z = position |
... | ... | @@ -264,6 +286,11 @@ class Slice(object): |
264 | 286 | self.DrawPixel(position, colour) |
265 | 287 | else: |
266 | 288 | self.ErasePixel(position) |
289 | + | |
290 | + session = ses.Session() | |
291 | + session.ChangeProject() | |
292 | + | |
293 | + | |
267 | 294 | #--------------------------------------------------------------------------- |
268 | 295 | def SelectCurrentMask(self, index): |
269 | 296 | "Insert mask data, based on given index, into pipeline." | ... | ... |
invesalius/data/surface.py
... | ... | @@ -25,9 +25,10 @@ import wx.lib.pubsub as ps |
25 | 25 | |
26 | 26 | import constants as const |
27 | 27 | import imagedata_utils as iu |
28 | +import polydata_utils as pu | |
28 | 29 | import project as prj |
30 | +import session as ses | |
29 | 31 | import vtk_utils as vu |
30 | -import polydata_utils as pu | |
31 | 32 | from imagedata_utils import BuildEditedImage |
32 | 33 | |
33 | 34 | class Surface(): |
... | ... | @@ -292,6 +293,11 @@ class SurfaceManager(): |
292 | 293 | proj = prj.Project() |
293 | 294 | proj.surface_dict[surface.index] = surface |
294 | 295 | |
296 | + | |
297 | + session = ses.Session() | |
298 | + session.ChangeProject() | |
299 | + | |
300 | + | |
295 | 301 | # Save actor for future management tasks |
296 | 302 | self.actors_dict[surface.index] = actor |
297 | 303 | ... | ... |
invesalius/gui/dialogs.py
... | ... | @@ -190,4 +190,18 @@ def ShowSaveAsProjectDialog(default_filename=None): |
190 | 190 | if sys.platform != 'win32': |
191 | 191 | if filename.split(".")[-1] != extension: |
192 | 192 | filename = filename + "." + extension |
193 | - return filename | |
193 | + return filename | |
194 | + | |
195 | +def SaveChangesDialog(filename): | |
196 | + dlg = wx.MessageDialog(None, | |
197 | + "InVesalius 3", | |
198 | + "Save changes to "+filename+"?", | |
199 | + wx.YES | wx.NO | wx.CANCEL | wx.ICON_INFORMATION) | |
200 | + | |
201 | + if dlg.ShowModal() == wx.ID_YES: | |
202 | + return 1 | |
203 | + elif dlg.ShowModal() == wx.ID_NO: | |
204 | + return 0 | |
205 | + else: | |
206 | + return -1 | |
207 | + | ... | ... |
invesalius/gui/frame.py
... | ... | @@ -30,7 +30,8 @@ import default_tasks as tasks |
30 | 30 | import default_viewers as viewers |
31 | 31 | import gui.dialogs as dlg |
32 | 32 | import import_panel as imp |
33 | -from project import Project | |
33 | +import project as prj | |
34 | +import session as ses | |
34 | 35 | |
35 | 36 | # Object toolbar |
36 | 37 | OBJ_TOOLS = [ID_ZOOM, ID_ZOOM_SELECT, ID_ROTATE, ID_MOVE, |
... | ... | @@ -216,12 +217,15 @@ class Frame(wx.Frame): |
216 | 217 | #ps.Publisher().sendMessage(("Event from GUI", |
217 | 218 | # evt.GetId())) |
218 | 219 | id = evt.GetId() |
220 | + #proj = prj.Project() | |
221 | + session = ses.Session() | |
219 | 222 | if id == const.ID_DICOM_IMPORT: |
220 | 223 | self.ImportDicom() |
221 | 224 | elif id == const.ID_PROJECT_OPEN: |
222 | 225 | self.OpenProject() |
223 | 226 | elif id == const.ID_PROJECT_SAVE: |
224 | - if Project().save_as: | |
227 | + #if proj.save_as: | |
228 | + if session.temp_item: | |
225 | 229 | self.SaveAsProject() |
226 | 230 | else: |
227 | 231 | self.SaveProject() |
... | ... | @@ -246,14 +250,20 @@ class Frame(wx.Frame): |
246 | 250 | self.SaveProject(True) |
247 | 251 | |
248 | 252 | def SaveProject(self, saveas=False): |
249 | - filename = (Project().name).replace(' ','_') | |
250 | - if Project().save_as or saveas: | |
251 | - filename = dlg.ShowSaveAsProjectDialog(filename) | |
252 | - if filename: | |
253 | - Project().save_as = False | |
253 | + | |
254 | + session = ses.Session() | |
255 | + if saveas: | |
256 | + proj = prj.Project() | |
257 | + filepath = dlg.ShowSaveAsProjectDialog(proj.name) | |
258 | + if filepath: | |
259 | + session.RemoveTemp() | |
260 | + session.OpenProject(filepath) | |
254 | 261 | else: |
255 | 262 | return |
256 | - ps.Publisher().sendMessage('Save Project',filename) | |
263 | + else: | |
264 | + dirpath, filename = session.project_path | |
265 | + filepath = os.path.join(dirpath, filename) | |
266 | + ps.Publisher().sendMessage('Save Project',filepath) | |
257 | 267 | |
258 | 268 | |
259 | 269 | def SaveAsOld(self): |
... | ... | @@ -272,7 +282,7 @@ class Frame(wx.Frame): |
272 | 282 | ps.Publisher().sendMessage('Save Project',filename) |
273 | 283 | |
274 | 284 | def CloseProject(self): |
275 | - print "TODO: CloseProject" | |
285 | + ps.Publisher().sendMessage('Close Project') | |
276 | 286 | |
277 | 287 | def Exit(self): |
278 | 288 | print "TODO: Exit" |
... | ... | @@ -544,10 +554,10 @@ class ProjectToolBar(wx.ToolBar): |
544 | 554 | self.Realize() |
545 | 555 | |
546 | 556 | def __bind_events(self): |
547 | - | |
548 | - self.Bind(wx.EVT_TOOL, self.OnToolSave, id=const.ID_PROJECT_SAVE) | |
549 | - self.Bind(wx.EVT_TOOL, self.OnToolOpen, id=const.ID_PROJECT_OPEN) | |
550 | - self.Bind(wx.EVT_TOOL, self.OnToolImport, id=const.ID_DICOM_IMPORT) | |
557 | + pass | |
558 | + #self.Bind(wx.EVT_TOOL, self.OnToolSave, id=const.ID_PROJECT_SAVE) | |
559 | + #self.Bind(wx.EVT_TOOL, self.OnToolOpen, id=const.ID_PROJECT_OPEN) | |
560 | + #self.Bind(wx.EVT_TOOL, self.OnToolImport, id=const.ID_DICOM_IMPORT) | |
551 | 561 | |
552 | 562 | def OnToolImport(self, event): |
553 | 563 | dirpath = dlg.ShowImportDirDialog() |
... | ... | @@ -562,11 +572,12 @@ class ProjectToolBar(wx.ToolBar): |
562 | 572 | event.Skip() |
563 | 573 | |
564 | 574 | def OnToolSave(self, event): |
565 | - filename = (Project().name).replace(' ','_') | |
566 | - if Project().save_as: | |
575 | + proj = prj.Project() | |
576 | + filename = (prj.name).replace(' ','_') | |
577 | + if prj.save_as: | |
567 | 578 | filename = dlg.ShowSaveAsProjectDialog(filename) |
568 | 579 | if filename: |
569 | - Project().save_as = False | |
580 | + prj.save_as = False | |
570 | 581 | else: |
571 | 582 | return |
572 | 583 | ps.Publisher().sendMessage('Save Project',filename) | ... | ... |
invesalius/project.py
... | ... | @@ -109,9 +109,9 @@ class Project(object): |
109 | 109 | self.invesalius_version = version.get_svn_revision() |
110 | 110 | print self.invesalius_version |
111 | 111 | |
112 | - self.save_as = True | |
112 | + #self.save_as = True | |
113 | 113 | |
114 | - self.path = "" | |
114 | + #self.path = "" | |
115 | 115 | self.debug = 0 |
116 | 116 | |
117 | 117 | ####### MASK OPERATIONS | ... | ... |
invesalius/reader/dicom.py
invesalius/session.py
1 | +import os | |
2 | + | |
1 | 3 | import constants as const |
2 | 4 | from utils import Singleton |
3 | 5 | |
... | ... | @@ -7,4 +9,77 @@ class Session(object): |
7 | 9 | __metaclass__= Singleton |
8 | 10 | |
9 | 11 | def __init__(self): |
10 | - self.project_status = const.NEW_PROJECT | |
11 | 12 | \ No newline at end of file |
13 | + self.project_path = () | |
14 | + | |
15 | + self.project_status = const.PROJ_NEW | |
16 | + # const.PROJ_NEW*, const.PROJ_OPEN, const.PROJ_CHANGE* | |
17 | + | |
18 | + self.mode = "" | |
19 | + # const.MODE_RP, const.MODE_NAVIGATOR, const.MODE_RADIOLOGY, | |
20 | + # const.MODE_ODONTOLOGY | |
21 | + | |
22 | + # InVesalius default projects' directory | |
23 | + homedir = os.path.expanduser('~') | |
24 | + invdir = os.path.join(homedir, ".invesalius", "temp") | |
25 | + if not os.path.isdir(invdir): | |
26 | + os.makedirs(invdir) | |
27 | + self.invdir = invdir | |
28 | + | |
29 | + self.temp_item = False | |
30 | + | |
31 | + # Recent projects list | |
32 | + self.recent_projects = [] | |
33 | + | |
34 | + | |
35 | + def SaveProject(self, path=()): | |
36 | + self.project_status = const.PROJ_OPEN | |
37 | + if path: | |
38 | + self.project_path = path | |
39 | + self.__add_to_list(path) | |
40 | + if self.temp_item: | |
41 | + self.temp_item = False | |
42 | + | |
43 | + def ChangeProject(self): | |
44 | + self.project_status = const.PROJ_CHANGE | |
45 | + | |
46 | + def CreateProject(self, filename): | |
47 | + # Set session info | |
48 | + self.project_path = (self.invdir, filename) | |
49 | + self.project_status = const.PROJ_NEW | |
50 | + self.temp_item = True | |
51 | + return self.invdir | |
52 | + | |
53 | + | |
54 | + def OpenProject(self, filepath): | |
55 | + # Add item to recent projects list | |
56 | + item = (path, file) = os.path.split(filepath) | |
57 | + self.__add_to_list(item) | |
58 | + | |
59 | + # Set session info | |
60 | + self.project_path = item | |
61 | + self.project_status = const.PROJ_OPEN | |
62 | + | |
63 | + def RemoveTemp(self): | |
64 | + if self.temp_item: | |
65 | + (dirpath, file) = self.project_path | |
66 | + path = os.path.join(dirpath, file) | |
67 | + os.remove(path) | |
68 | + self.temp_item = False | |
69 | + | |
70 | + | |
71 | + def __add_to_list(self, item): | |
72 | + # Last projects list | |
73 | + l = self.recent_projects | |
74 | + | |
75 | + # If item exists, remove it from list | |
76 | + if l.count(item): | |
77 | + l.remove(item) | |
78 | + | |
79 | + # Add new item | |
80 | + l.insert(0, item) | |
81 | + | |
82 | + # Remove oldest projects from list | |
83 | + if len(l)>const.PROJ_MAX: | |
84 | + for i in xrange(len(l)-const.PROJ_MAX): | |
85 | + l.pop() | |
86 | + | ... | ... |