def UpdateThresholdModes(self, threshold_range): thresh_min, thresh_max = threshold_range presets_list = (self.thresh_ct, self.thresh_mri) for presets in presets_list: for key in presets: (t_min, t_max) = presets[key] print(key, t_min, t_max) if (t_min is None) or (t_max is None): # setting custom preset t_min = thresh_min t_max = thresh_max if (t_min < thresh_min): t_min = thresh_min if (t_max > thresh_max): t_max = thresh_max # This has happened in Analyze files # TODO: find a good solution for presets in Analyze files if (t_min > thresh_max): t_min = thresh_min if (t_max < thresh_min): t_max = thresh_max presets[key] = (t_min, t_max) Publisher.sendMessage('Update threshold limits', threshold_range=(thresh_min, thresh_max))
def ReadBitmap(filepath): t = VerifyDataType(filepath) if _has_win32api: filepath = win32api.GetShortPathName(filepath) if t == False: try: measures_info = GetPixelSpacingFromInfoFile(filepath) except UnicodeDecodeError: measures_info = False if measures_info: Publisher.sendMessage("Set bitmap spacing", spacing=measures_info) return False img_array = VtkRead(filepath, t) if not (isinstance(img_array, numpy.ndarray)): no_error = True img_array = ScipyRead(filepath) if not (isinstance(img_array, numpy.ndarray)): return False return img_array
def OnComboBrushOp(self, evt): brush_op_id = evt.GetSelection() Publisher.sendMessage('Set edition operation', operation=brush_op_id) if brush_op_id == const.BRUSH_THRESH: self.gradient_thresh.Enable() else: self.gradient_thresh.Disable()
def OnRemove(self, surface_indexes): proj = prj.Project() old_dict = self.actors_dict new_dict = {} if surface_indexes: for index in surface_indexes: proj.RemoveSurface(index) if index in old_dict: actor = old_dict[index] for i in old_dict: if i < index: new_dict[i] = old_dict[i] if i > index: new_dict[i - 1] = old_dict[i] old_dict = new_dict Publisher.sendMessage('Remove surface actor from viewer', actor=actor) self.actors_dict = new_dict if self.last_surface_index in surface_indexes: if self.actors_dict: self.last_surface_index = 0 else: self.last_surface_index = None
def OnSlideChanging(self, evt): thresh_min = self.gradient.GetMinValue() thresh_max = self.gradient.GetMaxValue() Publisher.sendMessage('Changing threshold values', threshold_range=(thresh_min, thresh_max)) session = ses.Session() session.ChangeProject()
def OnBrushSize(self, evt): """ """ # FIXME: Using wx.EVT_SPINCTRL in MacOS it doesnt capture changes only # in the text ctrl - so we are capturing only changes on text # Strangelly this is being called twice Publisher.sendMessage('Set edition brush size', size=self.spin.GetValue())
def OnLinkNewMask(self, evt=None): try: evt.data evt = None except: pass dialog = dlg.NewMask() try: if dialog.ShowModal() == wx.ID_OK: ok = 1 else: ok = 0 except (wx._core.PyAssertionError): #TODO FIX: win64 ok = 1 if ok: mask_name, thresh, colour = dialog.GetValue() if mask_name: Publisher.sendMessage('Create new mask', mask_name=mask_name, thresh=thresh, colour=colour) dialog.Destroy()
def OnFoldPressCaption(self, evt): id = evt.GetTag().GetId() closed = evt.GetFoldStatus() if self.__id_editor == id: if closed: Publisher.sendMessage('Disable style', style=const.SLICE_STATE_EDITOR) self.last_style = None else: Publisher.sendMessage('Enable style', style=const.SLICE_STATE_EDITOR) self.last_style = const.SLICE_STATE_EDITOR elif self.__id_watershed == id: if closed: Publisher.sendMessage('Disable style', style=const.SLICE_STATE_WATERSHED) self.last_style = None else: Publisher.sendMessage('Enable style', style=const.SLICE_STATE_WATERSHED) # Publisher.sendMessage('Show help message', 'Mark the object and the background') self.last_style = const.SLICE_STATE_WATERSHED else: Publisher.sendMessage('Disable style', style=const.SLICE_STATE_EDITOR) self.last_style = None evt.Skip() wx.CallAfter(self.ResizeFPB)
def OnLinkExportMask(self, evt=None): project = proj.Project() if sys.platform == 'win32': project_name = project.name else: project_name = project.name + ".vti" dlg = wx.FileDialog( None, "Save mask as...", # title "", # last used directory project_name, # filename WILDCARD_SAVE_MASK, wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) dlg.SetFilterIndex(0) # default is VTI if dlg.ShowModal() == wx.ID_OK: filename = dlg.GetPath() extension = "vti" if sys.platform != 'win32': if filename.split(".")[-1] != extension: filename = filename + "." + extension filetype = const.FILETYPE_IMAGEDATA Publisher.sendMessage('Export mask to file', filename=filename, filetype=filetype)
def __init__(self, size=50): self.history = [] self.index = -1 self.size = size * 2 Publisher.sendMessage("Enable undo", value=False) Publisher.sendMessage("Enable redo", value=False)
def on_show(self): self.history._config_undo_redo(self.is_shown) if ses.Session().mask_3d_preview: Publisher.sendMessage('Show mask preview', index=self.index, flag=bool(self.is_shown)) Publisher.sendMessage("Render volume viewer")
def UpdateSurfaceInterpolation(self): interpolation = int(ses.Session().surface_interpolation) key_actors = self.actors_dict.keys() for key in self.actors_dict: self.actors_dict[key].GetProperty().SetInterpolation(interpolation) Publisher.sendMessage('Render volume viewer')
def OnMenuView(self, evt): """Events from button menus.""" bmp = wx.Bitmap(ID_TO_BMP[evt.GetId()][1], wx.BITMAP_TYPE_PNG) self.button_view.SetBitmapSelected(bmp) Publisher.sendMessage('Set volume view angle', view=evt.GetId()) self.Refresh()
def OnSetRelativeWindowLevel(self, diff_wl, diff_ww): ww = self.ww + diff_ww wl = self.wl + diff_wl Publisher.sendMessage('Set volume window and level text', ww=ww, wl=wl) self.SetWWWL(ww, wl) self.ww = ww self.wl = wl
def OnEditLabel(self, evt): if not evt.IsEditCancelled(): index = evt.GetIndex() self.SetItem(index, 1, evt.GetLabel()) Publisher.sendMessage('Change mask name', index=evt.GetIndex(), name=evt.GetLabel()) evt.Skip()
def _midi_to_pedal(self, msg): # TODO: At this stage, interpret all note_on messages as the pedal being pressed, # and note_off messages as the pedal being released. Later, use the correct # message types and be more stringent about the messages. # if msg.type == 'note_on': state = True elif msg.type == 'note_off': state = False else: print("Unknown message type received from MIDI device") return Publisher.sendMessage('Pedal state changed', state=state) for callback_info in self._callback_infos: callback = callback_info['callback'] callback(state) if not state: self._callback_infos = [ callback_info for callback_info in self._callback_infos if not callback_info['remove_when_released'] ]
def CleanUp(self): Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=False) self.viewer.on_wl = True self.viewer.text.Hide() self.viewer.interactor.Render()
def ImportMedicalImages(self, directory, gui=True): patients_groups = dcm.GetDicomGroups(directory) name = directory.rpartition('\\')[-1].split('.') if len(patients_groups): # OPTION 1: DICOM group = dcm.SelectLargerDicomGroup(patients_groups) matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0, 0], gui=gui) if matrix is None: return self.CreateDicomProject(dicom, matrix, matrix_filename) else: # OPTION 2: NIfTI, Analyze or PAR/REC if name[-1] == 'gz': name[1] = 'nii.gz' suptype = ('hdr', 'nii', 'nii.gz', 'par') filetype = name[1].lower() if filetype in suptype: group = oth.ReadOthers(directory) else: utils.debug("No medical images found on given directory") return if group: matrix, matrix_filename = self.OpenOtherFiles(group) self.CreateOtherProject(str(name[0]), matrix, matrix_filename) # OPTION 4: Nothing... self.LoadProject() Publisher.sendMessage("Enable state project", state=True)
def OpenBitmapFiles(self, bmp_data, rec_data): name = rec_data[0] orientation = rec_data[1] sp_x = float(rec_data[2]) sp_y = float(rec_data[3]) sp_z = float(rec_data[4]) interval = int(rec_data[5]) interval += 1 filelist = bmp_data.GetOnlyBitmapPath()[::interval] bits = bmp_data.GetFirstPixelSize() sx, sy = size = bmp_data.GetFirstBitmapSize() n_slices = len(filelist) resolution_percentage = utils.calculate_resizing_tofitmemory( int(sx), int(sy), n_slices, bits / 8) zspacing = sp_z * interval xyspacing = (sp_y, sp_x) if resolution_percentage < 1.0: re_dialog = dialog.ResizeImageDialog() re_dialog.SetValue(int(resolution_percentage * 100)) re_dialog_value = re_dialog.ShowModal() re_dialog.Close() if re_dialog_value == wx.ID_OK: percentage = re_dialog.GetValue() resolution_percentage = percentage / 100.0 else: return xyspacing = xyspacing[0] / resolution_percentage, xyspacing[ 1] / resolution_percentage self.matrix, scalar_range, self.filename = image_utils.bitmap2memmap( filelist, size, orientation, (sp_z, sp_y, sp_x), resolution_percentage) self.Slice = sl.Slice() self.Slice.matrix = self.matrix self.Slice.matrix_filename = self.filename if orientation == 'AXIAL': self.Slice.spacing = xyspacing[0], xyspacing[1], zspacing elif orientation == 'CORONAL': self.Slice.spacing = xyspacing[0], zspacing, xyspacing[1] elif orientation == 'SAGITTAL': self.Slice.spacing = zspacing, xyspacing[1], xyspacing[0] self.Slice.window_level = float(self.matrix.max() / 4) self.Slice.window_width = float(self.matrix.max()) scalar_range = int(self.matrix.min()), int(self.matrix.max()) Publisher.sendMessage('Update threshold limits list', threshold_range=scalar_range) return self.matrix, self.filename #, dicom
def disable_mask_preview(self): ses.Session().mask_3d_preview = False mask = self.Slice.current_mask if mask is not None: Publisher.sendMessage("Remove mask preview", mask_3d_actor=mask.volume._actor) Publisher.sendMessage("Render volume viewer")
def SaveProject(self, path=None, compress=False): Publisher.sendMessage('Begin busy cursor') session = ses.Session() if path: dirpath, filename = os.path.split(path) else: dirpath, filename = session.project_path if isinstance(filename, str): filename = utils.decode(filename, const.FS_ENCODE) proj = prj.Project() try: prj.Project().SavePlistProject(dirpath, filename, compress) except PermissionError as err: if wx.GetApp() is None: print( "Error: Permission denied, you don't have permission to write at {}" .format(dirpath)) else: dlg = dialogs.ErrorMessageBox( None, "Save project error", "It was not possible to save because you don't have permission to write at {}\n{}" .format(dirpath, err)) dlg.ShowModal() dlg.Destroy() else: session.SaveProject((dirpath, filename)) Publisher.sendMessage('End busy cursor')
def OnDuplicate(self): selected_items = self.parent.listctrl.GetSelected() if selected_items: Publisher.sendMessage('Duplicate surfaces', surface_indexes=selected_items) else: dlg.SurfaceSelectionRequiredForDuplication()
def OnDuplicate(self): selected_items = self.parent.listctrl.GetSelected() if selected_items: Publisher.sendMessage('Duplicate masks', mask_indexes=selected_items) else: dlg.MaskSelectionRequiredForDuplication()
def RemoveMeasurements(self): """ Remove items selected. """ # it is necessary to update internal dictionary # that maps bitmap given item index selected_items = self.GetSelected() selected_items.sort(reverse=True) old_dict = self._list_index if selected_items: for index in selected_items: new_dict = {} self.DeleteItem(index) for i in old_dict: if i < index: new_dict[i] = old_dict[i] if i > index: new_dict[i - 1] = old_dict[i] old_dict = new_dict self._list_index = new_dict Publisher.sendMessage('Remove measurements', indexes=selected_items) else: dlg.MeasureSelectionRequiredForRemoval()
def StopNavigation(self): self.event.set() if self.pedal_connection is not None: self.pedal_connection.remove_callback(name='navigation') self.coord_queue.clear() self.coord_queue.join() if self.serial_port_connection is not None: self.serial_port_connection.join() if self.serial_port_in_use: self.serial_port_queue.clear() self.serial_port_queue.join() if self.view_tracts: self.coord_tracts_queue.clear() self.coord_tracts_queue.join() self.tracts_queue.clear() self.tracts_queue.join() vis_components = [ self.serial_port_in_use, self.view_tracts, self.peel_loaded ] Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components)
def find_plugins(self): self.plugins = {} for p in chain( glob.glob(str( inv_paths.PLUGIN_DIRECTORY.joinpath("**/plugin.json")), recursive=True), glob.glob(str( inv_paths.USER_PLUGINS_DIRECTORY.joinpath( "**/plugin.json")), recursive=True), ): try: p = pathlib.Path(p) with p.open() as f: jdict = json.load(f) plugin_name = jdict["name"] plugin_description = jdict["description"] enable_startup = jdict.get("enable-startup", False) self.plugins[plugin_name] = { "name": plugin_name, "description": plugin_description, "folder": p.parent, "enable_startup": enable_startup, } except Exception as err: print("It was not possible to load plugin. Error: {}".format( err)) Publisher.sendMessage("Add plugins menu items", items=self.plugins)
def SetUp(self): Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=True) self.viewer.on_wl = True if self.viewer.raycasting_volume: self.viewer.text.Show() self.viewer.interactor.Render()
def _check_disconnected(self): if self._midi_in is not None: if self._active_input not in self._midi_inputs: self._midi_in = None Publisher.sendMessage('Pedal connection', state=False) print("Disconnected from MIDI device")
def OnOpenMesh(self): filename = dlg.ShowImportMeshFilesDialog() if filename: if not np.allclose(slice_.Slice().affine, np.eye(4)): convert_to_inv = dlg.ImportMeshCoordSystem() Publisher.sendMessage('Update convert_to_inv flag', convert_to_inv=convert_to_inv) Publisher.sendMessage('Import surface file', filename=filename)
def SetActorColour(self, surface_index, colour): """ """ self.actors_dict[surface_index].GetProperty().SetColor(colour[:3]) # Update value in project's surface_dict proj = prj.Project() proj.surface_dict[surface_index].colour = colour Publisher.sendMessage('Render volume viewer')