Commit dc6fb66558962f136f708ffa20cd292240b9b932

Authored by tatiana
1 parent 9716c1f9

ADD: New mask dialog and slice creation, based on threshold and colour

invesalius/constants.py
... ... @@ -28,7 +28,7 @@ from project import Project
28 28 #---------------
29 29  
30 30 # Measurements
31   -MEASURE_NAME_PATTERN = _("Measure %d")
  31 +MEASURE_NAME_PATTERN = _("M %d")
32 32 MEASURE_LINEAR = 101
33 33 MEASURE_ANGULAR = 102
34 34  
... ... @@ -182,16 +182,16 @@ THRESHOLD_OUTVALUE = 0
182 182 MASK_NAME_PATTERN = _("Mask %d")
183 183 MASK_OPACITY = 0.40
184 184 #MASK_OPACITY = 0.35
185   -MASK_COLOUR = [(0.33, 1, 0.33),
186   - (1, 1, 0.33),
187   - (0.33, 0.91, 1),
188   - (1, 0.33, 1),
189   - (1, 0.68, 0.33),
190   - (1, 0.33, 0.33),
191   - (0.33333333333333331, 0.33333333333333331, 1.0),
  185 +MASK_COLOUR = [[0.33, 1, 0.33],
  186 + [1, 1, 0.33],
  187 + [0.33, 0.91, 1],
  188 + [1, 0.33, 1],
  189 + [1, 0.68, 0.33],
  190 + [1, 0.33, 0.33],
  191 + [0.33333333333333331, 0.33333333333333331, 1.0],
192 192 #(1.0, 0.33333333333333331, 0.66666666666666663),
193   - (0.74901960784313726, 1.0, 0.0),
194   - (0.83529411764705885, 0.33333333333333331, 1.0)]#,
  193 + [0.74901960784313726, 1.0, 0.0],
  194 + [0.83529411764705885, 0.33333333333333331, 1.0]]#,
195 195 #(0.792156862745098, 0.66666666666666663, 1.0),
196 196 #(1.0, 0.66666666666666663, 0.792156862745098), # too "light"
197 197 #(0.33333333333333331, 1.0, 0.83529411764705885),#],
... ...
invesalius/data/slice_.py
... ... @@ -49,7 +49,7 @@ class Slice(object):
49 49 ps.Publisher().subscribe(self.CreateSurfaceFromIndex,
50 50 'Create surface from index')
51 51 # Mask control
52   - ps.Publisher().subscribe(self.__add_mask, 'Create new mask')
  52 + ps.Publisher().subscribe(self.__add_mask_thresh, 'Create new mask')
53 53 ps.Publisher().subscribe(self.__select_current_mask,
54 54 'Change mask selected')
55 55 # Mask properties
... ... @@ -169,6 +169,14 @@ class Slice(object):
169 169 self.CreateMask(name=mask_name)
170 170 self.SetMaskColour(self.current_mask.index, self.current_mask.colour)
171 171  
  172 + def __add_mask_thresh(self, pubsub_evt):
  173 + mask_name = pubsub_evt.data[0]
  174 + thresh = pubsub_evt.data[1]
  175 + colour = pubsub_evt.data[2]
  176 + self.CreateMask(name=mask_name, threshold_range=thresh, colour =colour)
  177 + self.SetMaskColour(self.current_mask.index, self.current_mask.colour)
  178 + self.SelectCurrentMask(self.current_mask.index)
  179 +
172 180 def __select_current_mask(self, pubsub_evt):
173 181 mask_index = pubsub_evt.data
174 182 self.SelectCurrentMask(mask_index)
... ... @@ -519,8 +527,6 @@ class Slice(object):
519 527 future_mask.colour = colour
520 528 if opacity:
521 529 future_mask.opacity = opacity
522   - if threshold_range:
523   - future_mask.threshold_range = threshold_range
524 530 if edition_threshold_range:
525 531 future_mask.edition_threshold_range = edition_threshold_range
526 532 if edited_points:
... ... @@ -532,11 +538,15 @@ class Slice(object):
532 538 imagedata = old_mask.imagedata
533 539 future_mask.threshold_range = old_mask.threshold_range
534 540  
535   - # if not defined in the method call, this will have been computed on
536   - # previous if
537   - future_mask.imagedata = vtk.vtkImageData()
538   - future_mask.imagedata.DeepCopy(imagedata)
539   - future_mask.imagedata.Update()
  541 + if threshold_range:
  542 + future_mask.threshold_range = threshold_range
  543 + future_mask.imagedata = self.__create_mask_threshold(self.imagedata,
  544 + threshold_range)
  545 + else:
  546 + future_mask.imagedata = vtk.vtkImageData()
  547 + future_mask.imagedata.DeepCopy(imagedata)
  548 + future_mask.imagedata.Update()
  549 +
540 550  
541 551 # when this is not the first instance, user will have defined a name
542 552 if name is not None:
... ... @@ -551,6 +561,9 @@ class Slice(object):
551 561 proj = Project()
552 562 index = proj.AddMask(future_mask)
553 563 future_mask.index = index
  564 + if threshold_range:
  565 + self.SetMaskThreshold(index, threshold_range)
  566 + future_mask.edited_points = {}
554 567  
555 568 # update gui related to mask
556 569 ps.Publisher().sendMessage('Add mask',
... ... @@ -630,8 +643,11 @@ class Slice(object):
630 643 return img_colours_mask.GetOutput()
631 644  
632 645  
633   - def __create_mask_threshold(self, imagedata):
634   - thresh_min, thresh_max = self.current_mask.threshold_range
  646 + def __create_mask_threshold(self, imagedata, threshold_range=None):
  647 + if not threshold_range:
  648 + thresh_min, thresh_max = self.current_mask.threshold_range
  649 + else:
  650 + thresh_min, thresh_max = threshold_range
635 651  
636 652 # flexible threshold
637 653 img_thresh_mask = vtk.vtkImageThreshold()
... ...
invesalius/gui/data_notebook.py
... ... @@ -293,16 +293,20 @@ class ButtonControlPanel(wx.Panel):
293 293  
294 294 def OnNew(self):
295 295 dialog = dlg.NewMask()
296   -
  296 +
297 297 try:
298   - answer = dialog.ShowModal()
299   - except(wx._core.PyAssertionError): #TODO: FIX win64
300   - answer = wx.ID_YES
  298 + if dialog.ShowModal() == wx.ID_OK:
  299 + ok = 1
  300 + else:
  301 + ok = 0
  302 + except(wx._core.PyAssertionError): #TODO FIX: win64
  303 + ok = 1
301 304  
302   - if wx.ID_YES:
303   - mask_name = dialog.GetValue()
  305 + if ok:
  306 + mask_name, thresh, colour = dialog.GetValue()
304 307 if mask_name:
305   - ps.Publisher().sendMessage('Create new mask', mask_name)
  308 + ps.Publisher().sendMessage('Create new mask',
  309 + (mask_name, thresh, colour))
306 310  
307 311 def OnRemove(self):
308 312 self.parent.listctrl.RemoveMasks()
... ...
invesalius/gui/dialogs.py
... ... @@ -19,6 +19,7 @@
19 19 # detalhes.
20 20 #--------------------------------------------------------------------------
21 21 import os
  22 +import random
22 23 import sys
23 24  
24 25 import wx
... ... @@ -26,8 +27,8 @@ from wx.lib import masked
26 27 from wx.lib.wordwrap import wordwrap
27 28 import wx.lib.pubsub as ps
28 29  
29   -
30 30 import constants as const
  31 +import gui.widgets.gradient as grad
31 32 import project as proj
32 33 import session as ses
33 34 import utils
... ... @@ -340,6 +341,17 @@ def SurfaceSelectionRequiredForRemoval():
340 341 dlg.Destroy()
341 342  
342 343  
  344 +def MeasureSelectionRequiredForRemoval():
  345 + msg = _("No measures were selected for removal.")
  346 + if sys.platform == 'darwin':
  347 + dlg = wx.MessageDialog(None, "", msg,
  348 + wx.ICON_INFORMATION | wx.OK)
  349 + else:
  350 + dlg = wx.MessageDialog(None, msg, "InVesalius 3",
  351 + wx.ICON_INFORMATION | wx.OK)
  352 + dlg.ShowModal()
  353 + dlg.Destroy()
  354 +
343 355 def MaskSelectionRequiredForDuplication():
344 356 msg = _("No masks were selected for duplication.")
345 357 if sys.platform == 'darwin':
... ... @@ -364,28 +376,7 @@ def SurfaceSelectionRequiredForDuplication():
364 376 dlg.ShowModal()
365 377 dlg.Destroy()
366 378  
367   -
368   -
369   -def NewMask():
370   - import data.mask as mask
371   - dlg = wx.TextEntryDialog(None, _('Name of new mask:'),
372   - _('InVesalius 3 - New mask'))
373   -
374   - dlg.CenterOnScreen()
375   - default_mask_name = const.MASK_NAME_PATTERN %(mask.Mask.general_index+2)
376   - dlg.SetValue(default_mask_name)
377   -
378   - try:
379   - op = dlg.ShowModal() == wx.ID_OK
380   - except(wx._core.PyAssertionError):
381   - print "win64 - wx._core.PyAssertionError"
382   - op = True
383   -
384   - if op:
385   - return dlg.GetValue()
386   - return None
387   -
388   -class NewMaskDialog(wx.Dialog):
  379 +class NewMask(wx.Dialog):
389 380 def __init__(self,
390 381 parent=None,
391 382 ID=-1,
... ... @@ -395,7 +386,7 @@ class NewMaskDialog(wx.Dialog):
395 386 style=wx.DEFAULT_DIALOG_STYLE,
396 387 useMetal=False):
397 388 import constants as const
398   - import data.surface as surface
  389 + import data.mask as mask
399 390 import project as prj
400 391  
401 392 # Instead of calling wx.Dialog.__init__ we precreate the dialog
... ... @@ -421,69 +412,60 @@ class NewMaskDialog(wx.Dialog):
421 412  
422 413 # LINE 1: Surface name
423 414  
424   - label_surface = wx.StaticText(self, -1, _("New mask name:"))
  415 + label_mask = wx.StaticText(self, -1, _("New mask name:"))
425 416  
426   - default_name = const.SURFACE_NAME_PATTERN %(surface.Surface.general_index+2)
  417 + default_name = const.MASK_NAME_PATTERN %(mask.Mask.general_index+2)
427 418 text = wx.TextCtrl(self, -1, "", size=(80,-1))
428 419 text.SetHelpText(_("Name the mask to be created"))
429 420 text.SetValue(default_name)
430 421 self.text = text
431 422  
432   - # LINE 2: Mask of reference
  423 + # LINE 2: Threshold of reference
433 424  
434 425 # Informative label
435   - label_mask = wx.StaticText(self, -1, _("Threshold preset:"))
  426 + label_thresh = wx.StaticText(self, -1, _("Threshold preset:"))
436 427  
437 428 # Retrieve existing masks
438 429 project = prj.Project()
439 430 thresh_list = project.threshold_modes.keys()
440 431 thresh_list.sort()
441   - default_index = proj.threshold_modes.get_key(_("Default"))
  432 + default_index = thresh_list.index(_("Bone"))
442 433 self.thresh_list = thresh_list
443 434  
444 435 # Mask selection combo
445   - combo_mask = wx.ComboBox(self, -1, "", choices= self.thresh_list,
  436 + combo_thresh = wx.ComboBox(self, -1, "", choices= self.thresh_list,
446 437 style=wx.CB_DROPDOWN|wx.CB_READONLY)
447   - combo_mask.SetSelection(len(self.thresh_list)-1)
448   - if sys.platform != 'win32':
449   - combo_mask.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
450   - self.combo_mask = combo_mask
451   -
452   - # LINE 3: Surface quality
453   - label_quality = wx.StaticText(self, -1, _("Surface quality:"))
454   -
455   - choices = const.SURFACE_QUALITY_LIST,
456   - style = wx.CB_DROPDOWN|wx.CB_READONLY
457   - combo_quality = wx.ComboBox(self, -1, "",
458   - choices= choices,
459   - style=style)
460   - combo_quality.SetSelection(3)
  438 + combo_thresh.SetSelection(default_index)
461 439 if sys.platform != 'win32':
462   - combo_quality.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
463   - self.combo_quality = combo_quality
464   -
  440 + combo_thresh.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
  441 + self.combo_thresh = combo_thresh
  442 +
  443 + # LINE 3: Gradient
  444 + bound_min, bound_max = project.threshold_range
  445 + thresh_min, thresh_max = project.threshold_modes[_("Bone")]
  446 + original_colour = random.choice(const.MASK_COLOUR)
  447 + self.colour = original_colour
  448 + colour = [255*i for i in original_colour]
  449 + colour.append(100)
  450 + gradient = grad.GradientSlider(self, -1, int(bound_min),
  451 + int(bound_max),
  452 + int(thresh_min), int(thresh_max),
  453 + colour)
  454 + self.gradient = gradient
465 455  
466 456 # OVERVIEW
467 457 # Sizer that joins content above
468 458 flag_link = wx.EXPAND|wx.GROW|wx.ALL
469 459 flag_button = wx.ALL | wx.EXPAND| wx.GROW
470 460  
471   - fixed_sizer = wx.FlexGridSizer(rows=2, cols=2, hgap=10, vgap=0)
  461 + fixed_sizer = wx.FlexGridSizer(rows=2, cols=2, hgap=10, vgap=10)
472 462 fixed_sizer.AddGrowableCol(0, 1)
473   - fixed_sizer.AddMany([ (label_surface, 1, flag_link, 5),
  463 + fixed_sizer.AddMany([ (label_mask, 1, flag_link, 5),
474 464 (text, 1, flag_button, 2),
475   - (label_mask, 1, flag_link, 5),
476   - (combo_mask, 0, flag_button, 1),
477   - (label_quality, 1, flag_link, 5),
478   - (combo_quality, 0, flag_button, 1)])
479   -
480   -
481   - # LINES 4 and 5: Checkboxes
482   - check_box_holes = wx.CheckBox(self, -1, _("Fill holes"))
483   - check_box_holes.SetValue(True)
484   - self.check_box_holes = check_box_holes
485   - check_box_largest = wx.CheckBox(self, -1, _("Keep largest region"))
486   - self.check_box_largest = check_box_largest
  465 + (label_thresh, 1, flag_link, 5),
  466 + (combo_thresh, 0, flag_button, 1)])#,
  467 + #(label_quality, 1, flag_link, 5),
  468 + #(combo_quality, 0, flag_button, 1)])
487 469  
488 470 # LINE 6: Buttons
489 471  
... ... @@ -499,28 +481,47 @@ class NewMaskDialog(wx.Dialog):
499 481 # OVERVIEW
500 482 # Merge all sizers and checkboxes
501 483 sizer = wx.BoxSizer(wx.VERTICAL)
502   - sizer.Add(fixed_sizer, 0, wx.TOP|wx.RIGHT|wx.LEFT|wx.GROW|wx.EXPAND, 20)
503   - sizer.Add(check_box_holes, 0, wx.RIGHT|wx.LEFT, 30)
504   - sizer.Add(check_box_largest, 0, wx.RIGHT|wx.LEFT, 30)
505   - sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 10)
  484 + sizer.Add(fixed_sizer, 0, wx.ALL|wx.GROW|wx.EXPAND, 15)
  485 + sizer.Add(gradient, 1, wx.BOTTOM|wx.RIGHT|wx.LEFT|wx.EXPAND|wx.GROW, 20)
  486 + sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT|wx.BOTTOM, 10)
506 487  
507 488 self.SetSizer(sizer)
508 489 sizer.Fit(self)
509 490  
510   - def GetValue(self):
511   - mask_index = self.combo_mask.GetSelection()
512   - surface_name = self.text.GetValue()
513   - quality = const.SURFACE_QUALITY_LIST[self.combo_quality.GetSelection()]
514   - fill_holes = self.check_box_holes.GetValue()
515   - keep_largest = self.check_box_largest.GetValue()
516   - return (mask_index, surface_name, quality, fill_holes, keep_largest)
517   -
518   -
519   -
  491 + self.Bind(grad.EVT_THRESHOLD_CHANGE, self.OnSlideChanged, self.gradient)
  492 + self.combo_thresh.Bind(wx.EVT_COMBOBOX, self.OnComboThresh)
520 493  
521 494  
  495 + def OnComboThresh(self, evt):
  496 + import project as prj
  497 + proj = prj.Project()
  498 + (thresh_min, thresh_max) = proj.threshold_modes[evt.GetString()]
  499 + self.gradient.SetMinValue(thresh_min, True)
  500 + self.gradient.SetMaxValue(thresh_max, True)
522 501  
  502 + def OnSlideChanged(self, evt):
  503 + import project as prj
  504 + thresh_min = self.gradient.GetMinValue()
  505 + thresh_max = self.gradient.GetMaxValue()
  506 + thresh = (thresh_min, thresh_max)
  507 + proj = prj.Project()
  508 + if thresh in proj.threshold_modes.values():
  509 + preset_name = proj.threshold_modes.get_key(thresh)[0]
  510 + index = self.thresh_list.index(preset_name)
  511 + self.combo_thresh.SetSelection(index)
  512 + else:
  513 + index = self.thresh_list.index(_("Custom"))
  514 + self.combo_thresh.SetSelection(index)
523 515  
  516 + def GetValue(self):
  517 + #mask_index = self.combo_mask.GetSelection()
  518 + mask_name = self.text.GetValue()
  519 + thresh_value = [self.gradient.GetMinValue(), self.gradient.GetMaxValue()]
  520 + #quality = const.SURFACE_QUALITY_LIST[self.combo_quality.GetSelection()]
  521 + #fill_holes = self.check_box_holes.GetValue()
  522 + #keep_largest = self.check_box_largest.GetValue()
  523 + #return (mask_index, surface_name, quality, fill_holes, keep_largest)
  524 + return mask_name, thresh_value, self.colour
524 525  
525 526  
526 527 def InexistentPath(path):
... ... @@ -595,24 +596,26 @@ def ShowAboutDialog(parent):
595 596 info.WebSite = ("http://svn.softwarepublico.gov.br/trac/invesalius")
596 597 info.License = _("GNU GPL (General Public License) version 2")
597 598  
598   - #info.Translators = ""
599   - #info.Artists =
600 599 info.Developers = ["Tatiana Al-Chueyr",
601   - "Paulo Henrique Junqueira Amorim",
602   - "Thiago Franco de Moraes"]
603   - #info.DocWriters =
  600 + "Paulo Henrique Junqueira Amorim",
  601 + "Thiago Franco de Moraes"]
  602 +
604 603 info.Translators = ["Alex P. Natsios (EL)",
605 604 "Andreas Loupasakis (EL)",
606 605 "Cheng-Chia Tseng (ZH)",
607 606 "Dimitris Glezos (EL)",
  607 + "Eugene Liscio (EN)",
608 608 u"Frédéric Lopez (FR)",
609 609 "J. Javier de Lima Moreno (ES)"
610 610 "Nikos Korkakakis (EL)",
611 611 "Sebastian Hilbert (DE)"]
612 612  
  613 + info.DocWriters = ["Eugene Liscio (EN)",
  614 + "Fabio Francisco da Silva (PT)"]
  615 +
613 616 info.Artists = ["Otavio Henrique Junqueira Amorim"]
614 617  
615   - # Then we call wx.AboutBox giving its info object
  618 + # Then we call wx.AboutBox providing its info object
616 619 wx.AboutBox(info)
617 620  
618 621  
... ...
invesalius/gui/task_slice.py
... ... @@ -152,12 +152,21 @@ class InnerTaskPanel(wx.Panel):
152 152 dlg.InexistentMask()
153 153  
154 154 def OnLinkNewMask(self, evt=None):
155   - mask_name = dlg.NewMask()
156   - if mask_name:
157   - ps.Publisher().sendMessage('Create new mask', mask_name)
  155 + dialog = dlg.NewMask()
158 156  
159   - if evt:
160   - evt.Skip()
  157 + try:
  158 + if dialog.ShowModal() == wx.ID_OK:
  159 + ok = 1
  160 + else:
  161 + ok = 0
  162 + except(wx._core.PyAssertionError): #TODO FIX: win64
  163 + ok = 1
  164 +
  165 + if ok:
  166 + mask_name, thresh, colour = dialog.GetValue()
  167 + if mask_name:
  168 + ps.Publisher().sendMessage('Create new mask',
  169 + (mask_name, thresh, colour))
161 170  
162 171 def GetMaskSelected(self):
163 172 return self.fold_panel.GetMaskSelected()
... ...