diff --git a/invesalius/data/viewer_volume.py b/invesalius/data/viewer_volume.py index 94b23a791..4643683cf 100644 --- a/invesalius/data/viewer_volume.py +++ b/invesalius/data/viewer_volume.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- import math -#-------------------------------------------------------------------------- + +# -------------------------------------------------------------------------- # Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas # Copyright: (C) 2001 Centro de Pesquisas Renato Archer # Homepage: http://www.softwarepublico.gov.br # Contact: invesalius@cti.gov.br # License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- # Este programa e software livre; voce pode redistribui-lo e/ou # modifica-lo sob os termos da Licenca Publica Geral GNU, conforme # publicada pela Free Software Foundation; de acordo com a versao 2 @@ -17,44 +18,41 @@ # COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais # detalhes. -#-------------------------------------------------------------------------- - +# -------------------------------------------------------------------------- # from math import cos, sin import os +import queue +import random import sys import numpy as np import wx -import queue +from imageio import imsave +from scipy.spatial import distance +from vtk import vtkCommand +from vtkmodules.vtkCommonColor import vtkColorSeries, vtkNamedColors + # TODO: Check that these imports are not used -- vtkLookupTable, vtkMinimalStandardRandomSequence, vtkPoints, vtkUnsignedCharArray from vtkmodules.vtkCommonComputationalGeometry import vtkParametricTorus from vtkmodules.vtkCommonCore import ( + mutable, + vtkDoubleArray, vtkIdList, - vtkMath, vtkLookupTable, + vtkMath, vtkPoints, vtkUnsignedCharArray, - vtkDoubleArray, - mutable -) -from vtkmodules.vtkCommonColor import ( - vtkColorSeries, - vtkNamedColors ) from vtkmodules.vtkCommonDataModel import ( - vtkPolyData, vtkCellLocator, + vtkPolyData, ) from vtkmodules.vtkCommonMath import vtkMatrix4x4 from vtkmodules.vtkCommonTransforms import vtkTransform -from vtkmodules.vtkFiltersCore import ( - vtkPolyDataNormals, - vtkCenterOfMass, - vtkGlyph3D -) -from vtkmodules.vtkFiltersModeling import vtkBandedPolyDataContourFilter +from vtkmodules.vtkFiltersCore import vtkCenterOfMass, vtkGlyph3D, vtkPolyDataNormals from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter from vtkmodules.vtkFiltersHybrid import vtkRenderLargeImage +from vtkmodules.vtkFiltersModeling import vtkBandedPolyDataContourFilter from vtkmodules.vtkFiltersSources import ( vtkArrowSource, vtkDiskSource, @@ -93,27 +91,12 @@ vtkRenderer, vtkWindowToImageFilter, ) -from vtk import vtkCommand from vtkmodules.vtkRenderingOpenGL2 import vtkCompositePolyDataMapper2 from vtkmodules.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor -from invesalius.data.ruler_volume import GenericLeftRulerVolume -from invesalius.gui.widgets.canvas_renderer import CanvasRendererCTX -from invesalius.pubsub import pub as Publisher -import random -from scipy.spatial import distance - -from imageio import imsave - import invesalius.constants as const import invesalius.data.coordinates as dco import invesalius.data.coregistration as dcr -from invesalius.data.markers.marker import Marker, MarkerType -from invesalius.data.markers.surface_geometry import SurfaceGeometry -from invesalius.data.actor_factory import ActorFactory -from invesalius.data.visualization.marker_visualizer import MarkerVisualizer -from invesalius.data.visualization.coil_visualizer import CoilVisualizer -from invesalius.data.visualization.vector_field_visualizer import VectorFieldVisualizer import invesalius.data.slice_ as sl import invesalius.data.styles_3d as styles import invesalius.data.transformations as tr @@ -122,14 +105,23 @@ import invesalius.session as ses import invesalius.style as st import invesalius.utils as utils - from invesalius import inv_paths -from invesalius.math_utils import inner1d +from invesalius.data.actor_factory import ActorFactory +from invesalius.data.markers.marker import Marker, MarkerType +from invesalius.data.markers.surface_geometry import SurfaceGeometry +from invesalius.data.ruler_volume import GenericLeftRulerVolume +from invesalius.data.visualization.coil_visualizer import CoilVisualizer +from invesalius.data.visualization.marker_visualizer import MarkerVisualizer +from invesalius.data.visualization.vector_field_visualizer import VectorFieldVisualizer +from invesalius.gui.widgets.canvas_renderer import CanvasRendererCTX from invesalius.i18n import tr as _ +from invesalius.math_utils import inner1d +from invesalius.pubsub import pub as Publisher -if sys.platform == 'win32': +if sys.platform == "win32": try: import win32api + _has_win32api = True except ImportError: _has_win32api = False @@ -140,6 +132,7 @@ # from invesalius.gui.widgets.canvas_renderer import CanvasRendererCTX, Polygon + class Viewer(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent, size=wx.Size(320, 320)) @@ -153,7 +146,7 @@ def __init__(self, parent): self.plot_vector = None self.style = None - interactor = wxVTKRenderWindowInteractor(self, -1, size = self.GetSize()) + interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize()) self.interactor = interactor self.interactor.SetRenderWhenDisabled(True) @@ -201,7 +194,7 @@ def __init__(self, parent): self.text = vtku.TextZero() self.text.SetValue("") self.text.SetPosition(const.TEXT_POS_LEFT_UP) - if sys.platform == 'darwin': + if sys.platform == "darwin": font_size = const.TEXT_SIZE_LARGE * self.GetContentScaleFactor() self.text.SetSize(int(round(font_size, 0))) self.ren.AddActor(self.text.actor) @@ -239,7 +232,7 @@ def __init__(self, parent): self.points_reference = [] self.measure_picker = vtkPropPicker() - #self.measure_picker.SetTolerance(0.005) + # self.measure_picker.SetTolerance(0.005) self.measures = [] self.repositioned_axial_plan = 0 @@ -266,7 +259,7 @@ def __init__(self, parent): self.show_coil = False self.guide_coil_actors = None self.guide_arrow_actors = None - self.pTarget = [0., 0., 0.] + self.pTarget = [0.0, 0.0, 0.0] self.distance_text = None @@ -291,8 +284,10 @@ def __init__(self, parent): # Set the angle and distance thresholds. session = ses.Session() - angle_threshold = session.GetConfig('angle_threshold', const.DEFAULT_ANGLE_THRESHOLD) - distance_threshold = session.GetConfig('distance_threshold', const.DEFAULT_DISTANCE_THRESHOLD) + angle_threshold = session.GetConfig("angle_threshold", const.DEFAULT_ANGLE_THRESHOLD) + distance_threshold = session.GetConfig( + "distance_threshold", const.DEFAULT_DISTANCE_THRESHOLD + ) self.angle_threshold = angle_threshold self.distance_threshold = distance_threshold @@ -336,17 +331,17 @@ def __init__(self, parent): self.colors_init = vtkUnsignedCharArray() self.plot_no_connection = False - self.old_coord = np.zeros((6,),dtype=float) + self.old_coord = np.zeros((6,), dtype=float) self.efield_mesh = None self.max_efield_vector = None self.ball_max_vector = None self.ball_GoGEfieldVector = None self.GoGEfieldVector = None - self.vectorfield_actor =None + self.vectorfield_actor = None self.efield_scalar_bar = None - self.edge_actor= None - #self.dummy_efield_coil_actor = None + self.edge_actor = None + # self.dummy_efield_coil_actor = None self.target_at_cortex = None self.SpreadEfieldFactorTextActor = None self.mTMSCoordTextActor = None @@ -359,9 +354,9 @@ def __init__(self, parent): self.cell_id_indexes_above_threshold = None # Automatically enable and press 'Track object' button. - Publisher.sendMessage('Enable track object button', enabled=True) - Publisher.sendMessage('Press track object button', pressed=True) - Publisher.sendMessage('Press target mode button', pressed=False) + Publisher.sendMessage("Enable track object button", enabled=True) + Publisher.sendMessage("Press track object button", pressed=True) + Publisher.sendMessage("Press target mode button", pressed=False) def UpdateCanvas(self): if self.canvas is not None: @@ -372,7 +367,7 @@ def UpdateCanvas(self): def EnableRuler(self): self.ruler = GenericLeftRulerVolume(self) self.interactor.AddObserver(vtkCommand.AnyEvent, self.OnInteractorEvent) - Publisher.sendMessage('Send ruler visibility status') + Publisher.sendMessage("Send ruler visibility status") def ShowRuler(self): if self.ruler and (self.ruler not in self.canvas.draw_list): @@ -404,143 +399,138 @@ def OnInteractorEvent(self, sender, event): self.UpdateCanvas() def __bind_events(self): - Publisher.subscribe(self.AddSurface, - 'Load surface actor into viewer') - Publisher.subscribe(self.RemoveSurface, - 'Remove surface actor from viewer') + Publisher.subscribe(self.AddSurface, "Load surface actor into viewer") + Publisher.subscribe(self.RemoveSurface, "Remove surface actor from viewer") # Publisher.subscribe(self.OnShowSurface, 'Show surface') - Publisher.subscribe(self.UpdateRender, - 'Render volume viewer') - Publisher.subscribe(self.ChangeBackgroundColour, - 'Change volume viewer background colour') + Publisher.subscribe(self.UpdateRender, "Render volume viewer") + Publisher.subscribe(self.ChangeBackgroundColour, "Change volume viewer background colour") # Related to raycasting - Publisher.subscribe(self.LoadVolume, - 'Load volume into viewer') - Publisher.subscribe(self.UnloadVolume, - 'Unload volume') - Publisher.subscribe(self.OnSetWindowLevelText, - 'Set volume window and level text') - Publisher.subscribe(self.OnHideRaycasting, - 'Hide raycasting volume') - Publisher.subscribe(self.OnShowRaycasting, - 'Update raycasting preset') + Publisher.subscribe(self.LoadVolume, "Load volume into viewer") + Publisher.subscribe(self.UnloadVolume, "Unload volume") + Publisher.subscribe(self.OnSetWindowLevelText, "Set volume window and level text") + Publisher.subscribe(self.OnHideRaycasting, "Hide raycasting volume") + Publisher.subscribe(self.OnShowRaycasting, "Update raycasting preset") ### - Publisher.subscribe(self.AppendActor,'AppendActor') - Publisher.subscribe(self.SetWidgetInteractor, - 'Set Widget Interactor') - Publisher.subscribe(self.OnSetViewAngle, - 'Set volume view angle') + Publisher.subscribe(self.AppendActor, "AppendActor") + Publisher.subscribe(self.SetWidgetInteractor, "Set Widget Interactor") + Publisher.subscribe(self.OnSetViewAngle, "Set volume view angle") - Publisher.subscribe(self.OnDisableBrightContrast, - 'Set interaction mode '+ - str(const.MODE_SLICE_EDITOR)) + Publisher.subscribe( + self.OnDisableBrightContrast, "Set interaction mode " + str(const.MODE_SLICE_EDITOR) + ) - Publisher.subscribe(self.OnExportSurface, 'Export surface to file') + Publisher.subscribe(self.OnExportSurface, "Export surface to file") - Publisher.subscribe(self.LoadSlicePlane, 'Load slice plane') + Publisher.subscribe(self.LoadSlicePlane, "Load slice plane") - Publisher.subscribe(self.ResetCamClippingRange, 'Reset cam clipping range') + Publisher.subscribe(self.ResetCamClippingRange, "Reset cam clipping range") - Publisher.subscribe(self.enable_style, 'Enable style') - Publisher.subscribe(self.OnDisableStyle, 'Disable style') + Publisher.subscribe(self.enable_style, "Enable style") + Publisher.subscribe(self.OnDisableStyle, "Disable style") - Publisher.subscribe(self.OnHideText, - 'Hide text actors on viewers') + Publisher.subscribe(self.OnHideText, "Hide text actors on viewers") - Publisher.subscribe(self.AddActors, 'Add actors ' + str(const.SURFACE)) - Publisher.subscribe(self.RemoveActors, 'Remove actors ' + str(const.SURFACE)) + Publisher.subscribe(self.AddActors, "Add actors " + str(const.SURFACE)) + Publisher.subscribe(self.RemoveActors, "Remove actors " + str(const.SURFACE)) - Publisher.subscribe(self.OnShowText, - 'Show text actors on viewers') - Publisher.subscribe(self.OnShowRuler, - 'Show rulers on viewers') - Publisher.subscribe(self.OnHideRuler, - 'Hide rulers on viewers') - Publisher.subscribe(self.OnRulerVisibilityStatus, 'Receive ruler visibility status') - Publisher.subscribe(self.OnCloseProject, 'Close project data') + Publisher.subscribe(self.OnShowText, "Show text actors on viewers") + Publisher.subscribe(self.OnShowRuler, "Show rulers on viewers") + Publisher.subscribe(self.OnHideRuler, "Hide rulers on viewers") + Publisher.subscribe(self.OnRulerVisibilityStatus, "Receive ruler visibility status") + Publisher.subscribe(self.OnCloseProject, "Close project data") - Publisher.subscribe(self.RemoveAllActors, 'Remove all volume actors') + Publisher.subscribe(self.RemoveAllActors, "Remove all volume actors") - Publisher.subscribe(self.OnExportPicture,'Export picture to file') + Publisher.subscribe(self.OnExportPicture, "Export picture to file") - Publisher.subscribe(self.OnStartSeed,'Create surface by seeding - start') - Publisher.subscribe(self.OnEndSeed,'Create surface by seeding - end') + Publisher.subscribe(self.OnStartSeed, "Create surface by seeding - start") + Publisher.subscribe(self.OnEndSeed, "Create surface by seeding - end") - Publisher.subscribe(self.SetStereoMode, 'Set stereo mode') + Publisher.subscribe(self.SetStereoMode, "Set stereo mode") - Publisher.subscribe(self.Reposition3DPlane, 'Reposition 3D Plane') + Publisher.subscribe(self.Reposition3DPlane, "Reposition 3D Plane") - Publisher.subscribe(self.UpdatePointer, 'Update volume viewer pointer') + Publisher.subscribe(self.UpdatePointer, "Update volume viewer pointer") - Publisher.subscribe(self.RemoveVolume, 'Remove Volume') + Publisher.subscribe(self.RemoveVolume, "Remove Volume") - Publisher.subscribe(self.OnSensors, 'Sensors ID') - Publisher.subscribe(self.OnRemoveSensorsID, 'Remove sensors ID') + Publisher.subscribe(self.OnSensors, "Sensors ID") + Publisher.subscribe(self.OnRemoveSensorsID, "Remove sensors ID") # TODO: This shouldn't be here, rather in marker_visualizer.py. The problem is that # e-field-related markers are stored in this class, even though all other marker # types have been moved to MarkerViewer. - Publisher.subscribe(self.DeleteEFieldMarkers, 'Delete markers') + Publisher.subscribe(self.DeleteEFieldMarkers, "Delete markers") # Related to object tracking during neuronavigation - Publisher.subscribe(self.OnNavigationStatus, 'Navigation status') - Publisher.subscribe(self.UpdateArrowPose, 'Update object arrow matrix') - Publisher.subscribe(self.UpdateEfieldPointLocation, 'Update point location for e-field calculation') - Publisher.subscribe(self.GetEnorm, 'Get enorm') - Publisher.subscribe(self.TrackObject, 'Track object') - Publisher.subscribe(self.SetTargetMode, 'Set target mode') - Publisher.subscribe(self.OnUpdateCoilPose, 'Update coil pose') - Publisher.subscribe(self.OnSetTarget, 'Set target') - Publisher.subscribe(self.OnUnsetTarget, 'Unset target') - Publisher.subscribe(self.OnUpdateAngleThreshold, 'Update angle threshold') - Publisher.subscribe(self.OnUpdateDistanceThreshold, 'Update distance threshold') - Publisher.subscribe(self.OnUpdateTracts, 'Update tracts') - Publisher.subscribe(self.OnUpdateEfieldvis, 'Update efield vis') - Publisher.subscribe(self.InitializeColorArray, 'Initialize color array') - Publisher.subscribe(self.OnRemoveTracts, 'Remove tracts') - Publisher.subscribe(self.UpdateSeedOffset, 'Update seed offset') - Publisher.subscribe(self.UpdateMarkerOffsetState, 'Update marker offset state') - Publisher.subscribe(self.AddPeeledSurface, 'Update peel') - Publisher.subscribe(self.InitEfield, 'Initialize E-field brain') - Publisher.subscribe(self.GetPeelCenters, 'Get peel centers and normals') - Publisher.subscribe(self.InitLocatorViewer, 'Get init locator') - Publisher.subscribe(self.GetPeelCenters, 'Get peel centers and normals') - Publisher.subscribe(self.InitLocatorViewer, 'Get init locator') - Publisher.subscribe(self.load_mask_preview, 'Load mask preview') - Publisher.subscribe(self.remove_mask_preview, 'Remove mask preview') - Publisher.subscribe(self.GetEfieldActor, 'Send Actor') - Publisher.subscribe(self.ReturnToDefaultColorActor, 'Recolor again') - Publisher.subscribe(self.SaveEfieldData, 'Save Efield data') - Publisher.subscribe(self.SavedAllEfieldData, 'Save all Efield data') - Publisher.subscribe(self.SaveEfieldTargetData, 'Save target data') - Publisher.subscribe(self.GetTargetSavedEfieldData, 'Get target index efield') - Publisher.subscribe(self.CheckStatusSavedEfieldData, 'Check efield data') - Publisher.subscribe(self.GetNeuronavigationApi, 'Get Neuronavigation Api') - Publisher.subscribe(self.UpdateEfieldPointLocationOffline,'Update interseccion offline') - Publisher.subscribe(self.MaxEfieldActor, 'Show max Efield actor') - Publisher.subscribe(self.CoGEfieldActor, 'Show CoG Efield actor') - Publisher.subscribe(self.CalculateDistanceMaxEfieldCoGE, 'Show distance between Max and CoG Efield') - Publisher.subscribe(self.EfieldVectors, 'Show Efield vectors') - Publisher.subscribe(self.RecolorEfieldActor, 'Recolor efield actor') - Publisher.subscribe(self.GetScalpEfield, 'Send scalp index') + Publisher.subscribe(self.OnNavigationStatus, "Navigation status") + Publisher.subscribe(self.UpdateArrowPose, "Update object arrow matrix") + Publisher.subscribe( + self.UpdateEfieldPointLocation, "Update point location for e-field calculation" + ) + Publisher.subscribe(self.GetEnorm, "Get enorm") + Publisher.subscribe(self.TrackObject, "Track object") + Publisher.subscribe(self.SetTargetMode, "Set target mode") + Publisher.subscribe(self.OnUpdateCoilPose, "Update coil pose") + Publisher.subscribe(self.OnSetTarget, "Set target") + Publisher.subscribe(self.OnUnsetTarget, "Unset target") + Publisher.subscribe(self.OnUpdateAngleThreshold, "Update angle threshold") + Publisher.subscribe(self.OnUpdateDistanceThreshold, "Update distance threshold") + Publisher.subscribe(self.OnUpdateTracts, "Update tracts") + Publisher.subscribe(self.OnUpdateEfieldvis, "Update efield vis") + Publisher.subscribe(self.InitializeColorArray, "Initialize color array") + Publisher.subscribe(self.OnRemoveTracts, "Remove tracts") + Publisher.subscribe(self.UpdateSeedOffset, "Update seed offset") + Publisher.subscribe(self.UpdateMarkerOffsetState, "Update marker offset state") + Publisher.subscribe(self.AddPeeledSurface, "Update peel") + Publisher.subscribe(self.InitEfield, "Initialize E-field brain") + Publisher.subscribe(self.GetPeelCenters, "Get peel centers and normals") + Publisher.subscribe(self.InitLocatorViewer, "Get init locator") + Publisher.subscribe(self.GetPeelCenters, "Get peel centers and normals") + Publisher.subscribe(self.InitLocatorViewer, "Get init locator") + Publisher.subscribe(self.load_mask_preview, "Load mask preview") + Publisher.subscribe(self.remove_mask_preview, "Remove mask preview") + Publisher.subscribe(self.GetEfieldActor, "Send Actor") + Publisher.subscribe(self.ReturnToDefaultColorActor, "Recolor again") + Publisher.subscribe(self.SaveEfieldData, "Save Efield data") + Publisher.subscribe(self.SavedAllEfieldData, "Save all Efield data") + Publisher.subscribe(self.SaveEfieldTargetData, "Save target data") + Publisher.subscribe(self.GetTargetSavedEfieldData, "Get target index efield") + Publisher.subscribe(self.CheckStatusSavedEfieldData, "Check efield data") + Publisher.subscribe(self.GetNeuronavigationApi, "Get Neuronavigation Api") + Publisher.subscribe(self.UpdateEfieldPointLocationOffline, "Update interseccion offline") + Publisher.subscribe(self.MaxEfieldActor, "Show max Efield actor") + Publisher.subscribe(self.CoGEfieldActor, "Show CoG Efield actor") + Publisher.subscribe( + self.CalculateDistanceMaxEfieldCoGE, "Show distance between Max and CoG Efield" + ) + Publisher.subscribe(self.EfieldVectors, "Show Efield vectors") + Publisher.subscribe(self.RecolorEfieldActor, "Recolor efield actor") + Publisher.subscribe(self.GetScalpEfield, "Send scalp index") # Related to robot tracking during neuronavigation - Publisher.subscribe(self.OnUpdateRobotStatus, 'Robot to Neuronavigation: Update robot status') - Publisher.subscribe(self.GetCoilPosition, 'Calculate position and rotation') - Publisher.subscribe(self.CreateCortexProjectionOnScalp, 'Send efield target position on brain') - Publisher.subscribe(self.UpdateEfieldThreshold, 'Update Efield Threshold') - Publisher.subscribe(self.UpdateEfieldROISize, 'Update Efield ROI size') - Publisher.subscribe(self.SetEfieldTargetAtCortex, 'Set as Efield target at cortex') - Publisher.subscribe(self.EnableShowEfieldAboveThreshold, 'Show area above threshold') - Publisher.subscribe(self.EnableEfieldTools, 'Enable Efield tools') - Publisher.subscribe(self.ClearTargetAtCortex, 'Clear efield target at cortex') - Publisher.subscribe(self.CoGEforCortexMarker, 'Get Cortex position') - Publisher.subscribe(self.AddCortexMarkerActor, 'Add cortex marker actor') - Publisher.subscribe(self.CortexMarkersVisualization, 'Display efield markers at cortex') - Publisher.subscribe(self.GetTargetPositions, 'Get targets Ids for mtms') - Publisher.subscribe(self.GetTargetPathmTMS, 'Send targeting file path') - Publisher.subscribe(self.GetdIsfromCoord,'Send mtms coords') - Publisher.subscribe(self.EnableSaveAutomaticallyEfieldData, 'Save automatically efield data') + Publisher.subscribe( + self.OnUpdateRobotStatus, "Robot to Neuronavigation: Update robot status" + ) + Publisher.subscribe(self.GetCoilPosition, "Calculate position and rotation") + Publisher.subscribe( + self.CreateCortexProjectionOnScalp, "Send efield target position on brain" + ) + Publisher.subscribe(self.UpdateEfieldThreshold, "Update Efield Threshold") + Publisher.subscribe(self.UpdateEfieldROISize, "Update Efield ROI size") + Publisher.subscribe(self.SetEfieldTargetAtCortex, "Set as Efield target at cortex") + Publisher.subscribe(self.EnableShowEfieldAboveThreshold, "Show area above threshold") + Publisher.subscribe(self.EnableEfieldTools, "Enable Efield tools") + Publisher.subscribe(self.ClearTargetAtCortex, "Clear efield target at cortex") + Publisher.subscribe(self.CoGEforCortexMarker, "Get Cortex position") + Publisher.subscribe(self.AddCortexMarkerActor, "Add cortex marker actor") + Publisher.subscribe(self.CortexMarkersVisualization, "Display efield markers at cortex") + Publisher.subscribe(self.GetTargetPositions, "Get targets Ids for mtms") + Publisher.subscribe(self.GetTargetPathmTMS, "Send targeting file path") + Publisher.subscribe(self.GetdIsfromCoord, "Send mtms coords") + Publisher.subscribe( + self.EnableSaveAutomaticallyEfieldData, "Save automatically efield data" + ) def get_vtk_mouse_position(self): """ @@ -553,7 +543,7 @@ def get_vtk_mouse_position(self): mposx, mposy = wx.GetMousePosition() cposx, cposy = self.interactor.ScreenToClient((mposx, mposy)) mx, my = cposx, self.interactor.GetSize()[1] - cposy - if sys.platform == 'darwin': + if sys.platform == "darwin": # It's needed to mutiple by scale factor in HighDPI because of # https://docs.wxpython.org/wx.glcanvas.GLCanvas.html # For now we are doing this only on Mac but it may be needed on @@ -569,7 +559,6 @@ def SetStereoMode(self, mode): if mode == const.STEREO_OFF: ren_win.StereoRenderOff() else: - if mode == const.STEREO_RED_BLUE: ren_win.SetStereoTypeToRedBlue() elif mode == const.STEREO_CRISTAL: @@ -595,12 +584,12 @@ def DeleteEFieldMarkers(self, markers): if len(self.static_markers_efield) > 0: for i in range(len(self.static_markers_efield)): self.ren.RemoveActor(self.static_markers_efield[i][0]) - self.static_markers_efield= [] + self.static_markers_efield = [] def CreatePointer(self): """ Create pointer and add it to the renderer. - + The pointer refers to the ball that is shown to indicate the 3D point in the volume viewer that corresponds to the selected slice positions. The same pointer is also used to show the point selected from the 3D viewer by right-clicking on it. """ @@ -608,8 +597,8 @@ def CreatePointer(self): actor = self.actor_factory.CreatePointer() # Store the pointer actor. - self.pointer_actor = actor - + self.pointer_actor = actor + # Add the actor to the renderer. self.ren.AddActor(actor) @@ -665,7 +654,7 @@ def CreateSensorID(self): dummy_probe_actor = vtkActor() dummy_probe_actor.SetMapper(mapper) dummy_probe_actor.GetProperty().SetColor(1, 1, 1) - dummy_probe_actor.GetProperty().SetOpacity(1.) + dummy_probe_actor.GetProperty().SetOpacity(1.0) self.dummy_probe_actor = dummy_probe_actor self.ren_probe.AddActor(dummy_probe_actor) @@ -687,7 +676,7 @@ def CreateSensorID(self): dummy_ref_actor = vtkActor() dummy_ref_actor.SetMapper(mapper) dummy_ref_actor.GetProperty().SetColor(1, 1, 1) - dummy_ref_actor.GetProperty().SetOpacity(1.) + dummy_ref_actor.GetProperty().SetOpacity(1.0) self.dummy_ref_actor = dummy_ref_actor self.ren_ref.AddActor(dummy_ref_actor) @@ -709,7 +698,7 @@ def CreateSensorID(self): dummy_obj_actor = vtkActor() dummy_obj_actor.SetMapper(mapper) dummy_obj_actor.GetProperty().SetColor(1, 1, 1) - dummy_obj_actor.GetProperty().SetOpacity(1.) + dummy_obj_actor.GetProperty().SetOpacity(1.0) self.dummy_obj_actor = dummy_obj_actor self.ren_obj.AddActor(dummy_obj_actor) @@ -737,68 +726,68 @@ def OnStartSeed(self): self.seed_points = [] def OnEndSeed(self): - Publisher.sendMessage("Create surface from seeds", - seeds=self.seed_points) + Publisher.sendMessage("Create surface from seeds", seeds=self.seed_points) def OnExportPicture(self, orientation, filename, filetype): if orientation == const.VOLUME: - Publisher.sendMessage('Begin busy cursor') + Publisher.sendMessage("Begin busy cursor") if _has_win32api: utils.touch(filename) win_filename = win32api.GetShortPathName(filename) self._export_picture(orientation, win_filename, filetype) else: self._export_picture(orientation, filename, filetype) - Publisher.sendMessage('End busy cursor') + Publisher.sendMessage("End busy cursor") def _export_picture(self, id, filename, filetype): - if filetype == const.FILETYPE_POV: - renwin = self.interactor.GetRenderWindow() - image = vtkWindowToImageFilter() - image.SetInput(renwin) - writer = vtkPOVExporter() - writer.SetFileName(filename.encode(const.FS_ENCODE)) - writer.SetRenderWindow(renwin) - writer.Write() - else: - #Use tiling to generate a large rendering. - image = vtkRenderLargeImage() - image.SetInput(self.ren) - image.SetMagnification(1) - image.Update() - - image = image.GetOutput() - - # write image file - if (filetype == const.FILETYPE_BMP): - writer = vtkBMPWriter() - elif (filetype == const.FILETYPE_JPG): - writer = vtkJPEGWriter() - elif (filetype == const.FILETYPE_PNG): - writer = vtkPNGWriter() - elif (filetype == const.FILETYPE_PS): - writer = vtkPostScriptWriter() - elif (filetype == const.FILETYPE_TIF): - writer = vtkTIFFWriter() - filename = u"%s.tif"%filename.strip(".tif") - - writer.SetInputData(image) - writer.SetFileName(filename.encode(const.FS_ENCODE)) - writer.Write() - - if not os.path.exists(filename): - wx.MessageBox(_("InVesalius was not able to export this picture"), _("Export picture error")) + if filetype == const.FILETYPE_POV: + renwin = self.interactor.GetRenderWindow() + image = vtkWindowToImageFilter() + image.SetInput(renwin) + writer = vtkPOVExporter() + writer.SetFileName(filename.encode(const.FS_ENCODE)) + writer.SetRenderWindow(renwin) + writer.Write() + else: + # Use tiling to generate a large rendering. + image = vtkRenderLargeImage() + image.SetInput(self.ren) + image.SetMagnification(1) + image.Update() + + image = image.GetOutput() + + # write image file + if filetype == const.FILETYPE_BMP: + writer = vtkBMPWriter() + elif filetype == const.FILETYPE_JPG: + writer = vtkJPEGWriter() + elif filetype == const.FILETYPE_PNG: + writer = vtkPNGWriter() + elif filetype == const.FILETYPE_PS: + writer = vtkPostScriptWriter() + elif filetype == const.FILETYPE_TIF: + writer = vtkTIFFWriter() + filename = "%s.tif" % filename.strip(".tif") + + writer.SetInputData(image) + writer.SetFileName(filename.encode(const.FS_ENCODE)) + writer.Write() + if not os.path.exists(filename): + wx.MessageBox( + _("InVesalius was not able to export this picture"), _("Export picture error") + ) def OnCloseProject(self): if self.raycasting_volume: self.raycasting_volume = False - if self.slice_plane: + if self.slice_plane: self.slice_plane.Disable() self.slice_plane.DeletePlanes() del self.slice_plane - Publisher.sendMessage('Uncheck image plane menu') + Publisher.sendMessage("Uncheck image plane menu") self.mouse_pressed = 0 self.on_wl = False self.slice_plane = 0 @@ -822,7 +811,7 @@ def AddActors(self, actors): def RemoveVolume(self): volumes = self.ren.GetVolumes() - if (volumes.GetNumberOfItems()): + if volumes.GetNumberOfItems(): self.ren.RemoveVolume(volumes.GetLastProp()) if not self.nav_status: self.UpdateRender() @@ -898,7 +887,7 @@ def CreateTargetGuide(self): mapper = vtkPolyDataMapper() mapper.SetInputData(normals.GetOutput()) mapper.ScalarVisibilityOff() - #mapper.ImmediateModeRenderingOn() # improve performance + # mapper.ImmediateModeRenderingOn() # improve performance obj_roll = vtkActor() obj_roll.SetMapper(mapper) @@ -959,8 +948,14 @@ def CreateTargetGuide(self): arrow_pitch_x2.RotateZ(180) self.guide_coil_actors = obj_roll, obj_yaw, obj_pitch - self.guide_arrow_actors = arrow_roll_z1, arrow_roll_z2, arrow_yaw_y1, arrow_yaw_y2,\ - arrow_pitch_x1, arrow_pitch_x2 + self.guide_arrow_actors = ( + arrow_roll_z1, + arrow_roll_z2, + arrow_yaw_y1, + arrow_yaw_y2, + arrow_pitch_x1, + arrow_pitch_x2, + ) for ind in self.guide_coil_actors: self.target_guide_renderer.AddActor(ind) @@ -1000,7 +995,7 @@ def EnableTargetMode(self): self.ren.ResetCamera() self.SetCameraTarget() - #self.ren.GetActiveCamera().Zoom(4) + # self.ren.GetActiveCamera().Zoom(4) self.target_guide_renderer.ResetCamera() self.target_guide_renderer.GetActiveCamera().Zoom(2) @@ -1056,8 +1051,9 @@ def SetTargetMode(self, enabled=False): def OnUpdateCoilPose(self, m_img, coord): vtk_colors = vtkNamedColors() if self.target_coord and self.target_mode: - distance_to_target = distance.euclidean(coord[0:3], - (self.target_coord[0], -self.target_coord[1], self.target_coord[2])) + distance_to_target = distance.euclidean( + coord[0:3], (self.target_coord[0], -self.target_coord[1], self.target_coord[2]) + ) formatted_distance = "Distance: {: >5.1f} mm".format(distance_to_target) @@ -1080,8 +1076,14 @@ def OnUpdateCoilPose(self, m_img, coord): # Send displacement to the robot. # # TODO: Unify naming; displacement is more correct term than distance, it should be used consistently. - displacement_to_target_robot = dcr.ComputeRelativeDistanceToTarget(target_coord=self.target_coord, m_img=m_img_flip) - wx.CallAfter(Publisher.sendMessage, 'Neuronavigation to Robot: Update displacement to target', displacement=displacement_to_target_robot) + displacement_to_target_robot = dcr.ComputeRelativeDistanceToTarget( + target_coord=self.target_coord, m_img=m_img_flip + ) + wx.CallAfter( + Publisher.sendMessage, + "Neuronavigation to Robot: Update displacement to target", + displacement=displacement_to_target_robot, + ) distance_to_target = displacement_to_target_robot.copy() if distance_to_target[3] > const.ARROW_UPPER_LIMIT: @@ -1106,7 +1108,11 @@ def OnUpdateCoilPose(self, m_img, coord): for actor in self.guide_arrow_actors: self.target_guide_renderer.RemoveActor(actor) - if self.angle_threshold * const.ARROW_SCALE > coordrx_arrow > -self.angle_threshold * const.ARROW_SCALE: + if ( + self.angle_threshold * const.ARROW_SCALE + > coordrx_arrow + > -self.angle_threshold * const.ARROW_SCALE + ): is_under_x_angle_threshold = True self.guide_coil_actors[0].GetProperty().SetColor(0, 1, 0) else: @@ -1115,17 +1121,25 @@ def OnUpdateCoilPose(self, m_img, coord): offset = 5 - arrow_roll_x1 = self.actor_factory.CreateArrow([-55, -35, offset], [-55, -35, offset - coordrx_arrow]) + arrow_roll_x1 = self.actor_factory.CreateArrow( + [-55, -35, offset], [-55, -35, offset - coordrx_arrow] + ) arrow_roll_x1.RotateX(-60) arrow_roll_x1.RotateZ(180) arrow_roll_x1.GetProperty().SetColor(1, 1, 0) - arrow_roll_x2 = self.actor_factory.CreateArrow([55, -35, offset], [55, -35, offset + coordrx_arrow]) + arrow_roll_x2 = self.actor_factory.CreateArrow( + [55, -35, offset], [55, -35, offset + coordrx_arrow] + ) arrow_roll_x2.RotateX(-60) arrow_roll_x2.RotateZ(180) arrow_roll_x2.GetProperty().SetColor(1, 1, 0) - if self.angle_threshold * const.ARROW_SCALE > coordrz_arrow > -self.angle_threshold * const.ARROW_SCALE: + if ( + self.angle_threshold * const.ARROW_SCALE + > coordrz_arrow + > -self.angle_threshold * const.ARROW_SCALE + ): is_under_z_angle_threshold = True self.guide_coil_actors[1].GetProperty().SetColor(0, 1, 0) else: @@ -1134,17 +1148,25 @@ def OnUpdateCoilPose(self, m_img, coord): offset = -35 - arrow_yaw_z1 = self.actor_factory.CreateArrow([-55, offset, 0], [-55, offset - coordrz_arrow, 0]) + arrow_yaw_z1 = self.actor_factory.CreateArrow( + [-55, offset, 0], [-55, offset - coordrz_arrow, 0] + ) arrow_yaw_z1.SetPosition(0, -150, 0) arrow_yaw_z1.RotateZ(180) arrow_yaw_z1.GetProperty().SetColor(0, 1, 0) - arrow_yaw_z2 = self.actor_factory.CreateArrow([55, offset, 0], [55, offset + coordrz_arrow, 0]) + arrow_yaw_z2 = self.actor_factory.CreateArrow( + [55, offset, 0], [55, offset + coordrz_arrow, 0] + ) arrow_yaw_z2.SetPosition(0, -150, 0) arrow_yaw_z2.RotateZ(180) arrow_yaw_z2.GetProperty().SetColor(0, 1, 0) - if self.angle_threshold * const.ARROW_SCALE > coordry_arrow > -self.angle_threshold * const.ARROW_SCALE: + if ( + self.angle_threshold * const.ARROW_SCALE + > coordry_arrow + > -self.angle_threshold * const.ARROW_SCALE + ): is_under_y_angle_threshold = True self.guide_coil_actors[2].GetProperty().SetColor(0, 1, 0) else: @@ -1152,30 +1174,44 @@ def OnUpdateCoilPose(self, m_img, coord): self.guide_coil_actors[2].GetProperty().SetColor(1, 1, 1) offset = 38 - arrow_pitch_y1 = self.actor_factory.CreateArrow([0, 65, offset], [0, 65, offset + coordry_arrow]) + arrow_pitch_y1 = self.actor_factory.CreateArrow( + [0, 65, offset], [0, 65, offset + coordry_arrow] + ) arrow_pitch_y1.SetPosition(0, -300, 0) arrow_pitch_y1.RotateY(90) arrow_pitch_y1.RotateZ(180) arrow_pitch_y1.GetProperty().SetColor(1, 0, 0) offset = 5 - arrow_pitch_y2 = self.actor_factory.CreateArrow([0, -55, offset], [0, -55, offset - coordry_arrow]) + arrow_pitch_y2 = self.actor_factory.CreateArrow( + [0, -55, offset], [0, -55, offset - coordry_arrow] + ) arrow_pitch_y2.SetPosition(0, -300, 0) arrow_pitch_y2.RotateY(90) arrow_pitch_y2.RotateZ(180) arrow_pitch_y2.GetProperty().SetColor(1, 0, 0) # Combine all the conditions to check if the coil is at the target. - coil_at_target = is_under_distance_threshold and \ - is_under_x_angle_threshold and \ - is_under_y_angle_threshold and \ - is_under_z_angle_threshold + coil_at_target = ( + is_under_distance_threshold + and is_under_x_angle_threshold + and is_under_y_angle_threshold + and is_under_z_angle_threshold + ) - wx.CallAfter(Publisher.sendMessage, 'Coil at target', state=coil_at_target) - wx.CallAfter(Publisher.sendMessage, 'From Neuronavigation: Coil at target', state=coil_at_target) + wx.CallAfter(Publisher.sendMessage, "Coil at target", state=coil_at_target) + wx.CallAfter( + Publisher.sendMessage, "From Neuronavigation: Coil at target", state=coil_at_target + ) - self.guide_arrow_actors = arrow_roll_x1, arrow_roll_x2, arrow_yaw_z1, arrow_yaw_z2, \ - arrow_pitch_y1, arrow_pitch_y2 + self.guide_arrow_actors = ( + arrow_roll_x1, + arrow_roll_x2, + arrow_yaw_z1, + arrow_yaw_z2, + arrow_pitch_y1, + arrow_pitch_y2, + ) for ind in self.guide_arrow_actors: self.target_guide_renderer.AddActor(ind) @@ -1205,7 +1241,7 @@ def CreateVTKObjectMatrix(self, direction, orientation): m_img = dco.coordinates_to_transformation_matrix( position=direction, orientation=orientation, - axes='sxyz', + axes="sxyz", ) m_img = np.asmatrix(m_img) @@ -1221,7 +1257,7 @@ def CreateDistanceText(self): distance_text = vtku.Text() distance_text.SetSize(const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION) - distance_text.SetPosition((const.X, 1.-const.Y)) + distance_text.SetPosition((const.X, 1.0 - const.Y)) distance_text.SetVerticalJustificationToBottom() distance_text.BoldOn() @@ -1264,10 +1300,14 @@ def Plane(self, x0, pTarget): x2 = x0 + v1 # calculates the matrix for the change of coordinate systems (from canonical to the plane's). # remember that, in np.dot(M,p), even though p is a line vector (e.g.,np.array([1,2,3])), it is treated as a column for the dot multiplication. - M_plane_inv = np.array([[v1[0], v2[0], v3[0], x0[0]], - [v1[1], v2[1], v3[1], x0[1]], - [v1[2], v2[2], v3[2], x0[2]], - [0, 0, 0, 1]]) + M_plane_inv = np.array( + [ + [v1[0], v2[0], v3[0], x0[0]], + [v1[1], v2[1], v3[1], x0[1]], + [v1[2], v2[2], v3[2], x0[2]], + [0, 0, 0, 1], + ] + ) return v3, M_plane_inv @@ -1293,7 +1333,13 @@ def SetCameraTarget(self): v0 = cam_pos0 - cam_focus0 v0n = np.sqrt(inner1d(v0, v0)) - v1 = np.array([cam_focus[0] - cam_focus0[0], cam_focus[1] - cam_focus0[1], cam_focus[2] - cam_focus0[2]]) + v1 = np.array( + [ + cam_focus[0] - cam_focus0[0], + cam_focus[1] - cam_focus0[1], + cam_focus[2] - cam_focus0[2], + ] + ) v1n = np.sqrt(inner1d(v1, v1)) if not v1n: v1n = 1.0 @@ -1303,16 +1349,16 @@ def SetCameraTarget(self): def UpdatePointer(self, position): """ - When not navigating, update the position of the pointer sphere. It is done - when the slice planes are moved or a new point is selected from the volume viewer. + Update the position of the pointer sphere. It is done when the navigation without object is on, + slice planes are moved or a new point is selected from the volume viewer. """ - # Update the pointer sphere when not navigating. - if self.pointer_actor is not None and not self.nav_status: - self.pointer_actor.SetPosition(position) - - # Update the render window manually, as it is not updated automatically when not navigating. - if not self.nav_status: - self.UpdateRender() + # Update the pointer sphere. + if self.pointer_actor is None: + self.CreatePointer() + self.pointer_actor.SetPosition(position) + # Update the render window manually, as it is not updated automatically when not navigating. + if not self.nav_status: + self.UpdateRender() def ObjectArrowLocation(self, m_img, coord): # m_img[:3, 0] is from posterior to anterior direction of the coil @@ -1326,7 +1372,9 @@ def ObjectArrowLocation(self, m_img, coord): coil_face = m_img_flip[:-1, 1] coil_norm = np.cross(coil_dir, coil_face) - p2_norm = p1 - vec_length * coil_norm # point normal to the coil away from the center by vec_length + p2_norm = ( + p1 - vec_length * coil_norm + ) # point normal to the coil away from the center by vec_length coil_dir = np.array([coord[3], coord[4], coord[5]]) return coil_dir, p2_norm, coil_norm, p1 @@ -1388,8 +1436,12 @@ def MaxEfieldActor(self): self.ren.RemoveActor(self.ball_max_vector) self.position_max = self.efield_mesh.GetPoint(self.Idmax) orientation = [self.max_efield_array[0], self.max_efield_array[1], self.max_efield_array[2]] - self.max_efield_vector= self.DrawVectors(self.position_max, orientation, vtk_colors.GetColor3d('Red')) - self.ball_max_vector = self.actor_factory.CreateBall(self.position_max, vtk_colors.GetColor3d('Red'), 0.5) + self.max_efield_vector = self.DrawVectors( + self.position_max, orientation, vtk_colors.GetColor3d("Red") + ) + self.ball_max_vector = self.actor_factory.CreateBall( + self.position_max, vtk_colors.GetColor3d("Red"), 0.5 + ) self.ren.AddActor(self.max_efield_vector) self.ren.AddActor(self.ball_max_vector) @@ -1398,60 +1450,94 @@ def CoGEfieldActor(self): if self.GoGEfieldVector and self.ball_GoGEfieldVector is not None: self.ren.RemoveActor(self.GoGEfieldVector) self.ren.RemoveActor(self.ball_GoGEfieldVector) - orientation = [self.max_efield_array[0] , self.max_efield_array[1], self.max_efield_array[2]] - [self.cell_id_indexes_above_threshold, self.positions_above_threshold]= self.GetIndexesAboveThreshold(self.efield_threshold) - self.center_gravity_position = self.FindCenterofGravity(self.cell_id_indexes_above_threshold, self.positions_above_threshold) - self.GoGEfieldVector = self.DrawVectors(self.center_gravity_position, orientation, vtk_colors.GetColor3d('Blue')) - self.ball_GoGEfieldVector = self.actor_factory.CreateBall(self.center_gravity_position, vtk_colors.GetColor3d('Blue'), 0.5) + orientation = [self.max_efield_array[0], self.max_efield_array[1], self.max_efield_array[2]] + [self.cell_id_indexes_above_threshold, self.positions_above_threshold] = ( + self.GetIndexesAboveThreshold(self.efield_threshold) + ) + self.center_gravity_position = self.FindCenterofGravity( + self.cell_id_indexes_above_threshold, self.positions_above_threshold + ) + self.GoGEfieldVector = self.DrawVectors( + self.center_gravity_position, orientation, vtk_colors.GetColor3d("Blue") + ) + self.ball_GoGEfieldVector = self.actor_factory.CreateBall( + self.center_gravity_position, vtk_colors.GetColor3d("Blue"), 0.5 + ) self.ren.AddActor(self.GoGEfieldVector) self.ren.AddActor(self.ball_GoGEfieldVector) def CoGEforCortexMarker(self): if self.e_field_norms is not None: [cell_id_indexes, positions_above_threshold] = self.GetIndexesAboveThreshold(0.98) - center_gravity_position_for_marker = self.FindCenterofGravity(cell_id_indexes, positions_above_threshold) - center_gravity_orientation_for_marker = [self.max_efield_array[0], self.max_efield_array[1], - self.max_efield_array[2]] - Publisher.sendMessage('Update Cortex Marker', CoGposition = center_gravity_position_for_marker, CoGorientation = center_gravity_orientation_for_marker) + center_gravity_position_for_marker = self.FindCenterofGravity( + cell_id_indexes, positions_above_threshold + ) + center_gravity_orientation_for_marker = [ + self.max_efield_array[0], + self.max_efield_array[1], + self.max_efield_array[2], + ] + Publisher.sendMessage( + "Update Cortex Marker", + CoGposition=center_gravity_position_for_marker, + CoGorientation=center_gravity_orientation_for_marker, + ) # Publisher.sendMessage('Save target data', target_list_index=marker_id, position= center_gravity_position_for_marker, # orientation= center_gravity_orientation_for_marker, plot_efield_vectors=self.plot_vector) - #return [marker_actor_brain, center_gravity_position_for_marker, center_gravity_orientation_for_marker] + # return [marker_actor_brain, center_gravity_position_for_marker, center_gravity_orientation_for_marker] def AddCortexMarkerActor(self, position_orientation, marker_id): vtk_colors = vtkNamedColors() - marker_actor_brain = self.DrawVectors(position_orientation[:3], - position_orientation[3:], vtk_colors.GetColor3d('Orange'), - scale_factor=3) + marker_actor_brain = self.DrawVectors( + position_orientation[:3], + position_orientation[3:], + vtk_colors.GetColor3d("Orange"), + scale_factor=3, + ) self.static_markers_efield.append([marker_actor_brain, marker_id]) self.ren.AddActor(marker_actor_brain) if self.save_automatically: - import invesalius.project as prj import time + import invesalius.gui.dialogs as dlg + import invesalius.project as prj + proj = prj.Project() timestamp = time.localtime(time.time()) - stamp_date = '{:0>4d}{:0>2d}{:0>2d}'.format(timestamp.tm_year, timestamp.tm_mon, timestamp.tm_mday) - stamp_time = '{:0>2d}{:0>2d}{:0>2d}'.format(timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec) - sep = '-' + stamp_date = "{:0>4d}{:0>2d}{:0>2d}".format( + timestamp.tm_year, timestamp.tm_mon, timestamp.tm_mday + ) + stamp_time = "{:0>2d}{:0>2d}{:0>2d}".format( + timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec + ) + sep = "-" if self.path_meshes is None: import os + current_folder_path = os.getcwd() else: current_folder_path = self.path_meshes - parts = [current_folder_path, '/', stamp_date, stamp_time, proj.name, 'Efield'] - default_filename = sep.join(parts) + '.csv' + parts = [current_folder_path, "/", stamp_date, stamp_time, proj.name, "Efield"] + default_filename = sep.join(parts) + ".csv" - filename = dlg.ShowLoadSaveDialog(message=_(u"Save markers as..."), - wildcard='(*.csv)|*.csv', - style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, - default_filename=default_filename) + filename = dlg.ShowLoadSaveDialog( + message=_("Save markers as..."), + wildcard="(*.csv)|*.csv", + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, + default_filename=default_filename, + ) if not filename: return - Publisher.sendMessage('Save Efield data', filename=filename, plot_efield_vectors=self.plot_efield_vectors, marker_id = marker_id) + Publisher.sendMessage( + "Save Efield data", + filename=filename, + plot_efield_vectors=self.plot_efield_vectors, + marker_id=marker_id, + ) def EnableSaveAutomaticallyEfieldData(self, enable, path_meshes, plot_efield_vectors): self.save_automatically = enable @@ -1473,12 +1559,16 @@ def CreateTextLegend(self, FontSize, Position): return TextLegend def CreateEfieldSpreadLegend(self): - self.SpreadEfieldFactorTextActor = self.CreateTextLegend(const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION,(0.4, 0.9)) + self.SpreadEfieldFactorTextActor = self.CreateTextLegend( + const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION, (0.4, 0.9) + ) self.ren.AddActor(self.SpreadEfieldFactorTextActor.actor) def CalculateDistanceMaxEfieldCoGE(self): self.distance_efield = distance.euclidean(self.center_gravity_position, self.position_max) - self.SpreadEfieldFactorTextActor.SetValue('Spread distance: ' + str("{:04.2f}".format(self.distance_efield))) + self.SpreadEfieldFactorTextActor.SetValue( + "Spread distance: " + str("{:04.2f}".format(self.distance_efield)) + ) def EfieldVectors(self): vtk_colors = vtkNamedColors() @@ -1491,13 +1581,15 @@ def EfieldVectors(self): for i in range(self.radius_list.GetNumberOfIds()): point = self.efield_mesh.GetPoint(self.radius_list.GetId(i)) points.InsertNextPoint(point) - vectors.InsertNextTuple3(self.e_field_col1[i], self.e_field_col2[i], self.e_field_col3[i]) + vectors.InsertNextTuple3( + self.e_field_col1[i], self.e_field_col2[i], self.e_field_col3[i] + ) dataset = vtkPolyData() dataset.SetPoints(points) dataset.GetPointData().SetVectors(vectors) - arrowSource= vtkArrowSource() + arrowSource = vtkArrowSource() glyphFilter = vtkGlyph3D() glyphFilter.SetSourceConnection(arrowSource.GetOutputPort()) @@ -1505,72 +1597,105 @@ def EfieldVectors(self): glyphFilter.SetScaleFactor(1) glyphFilter.Update() - mapper =vtkPolyDataMapper() + mapper = vtkPolyDataMapper() mapper.SetInputData(glyphFilter.GetOutput()) self.vectorfield_actor = vtkActor() self.vectorfield_actor.SetMapper(mapper) - self.vectorfield_actor.GetProperty().SetColor(vtk_colors.GetColor3d('Blue')) + self.vectorfield_actor.GetProperty().SetColor(vtk_colors.GetColor3d("Blue")) self.ren.AddActor(self.vectorfield_actor) self.interactor.Update() def SaveEfieldTargetData(self, target_list_index, position, orientation, plot_efield_vectors): - if len(self.Id_list)>0: + if len(self.Id_list) > 0: if self.efield_coords is not None: import invesalius.data.imagedata_utils as imagedata_utils position_world, orientation_world = imagedata_utils.convert_invesalius_to_world( position=[self.efield_coords[0], self.efield_coords[1], self.efield_coords[2]], - orientation=[self.efield_coords[3], self.efield_coords[4], self.efield_coords[5]], + orientation=[ + self.efield_coords[3], + self.efield_coords[4], + self.efield_coords[5], + ], ) efield_coords_position = [list(position_world), list(orientation_world)] enorms_list = list(self.e_field_norms) if plot_efield_vectors: e_field_vectors = list(self.max_efield_array) - self.target_radius_list.append([target_list_index, self.Id_list, enorms_list, self.Idmax, self.coil_position, efield_coords_position, self.efield_coords, self.coil_position_Trot, e_field_vectors, self.focal_factor_members, self.efield_threshold, self.efield_ROISize, self.mtms_coord]) + self.target_radius_list.append( + [ + target_list_index, + self.Id_list, + enorms_list, + self.Idmax, + self.coil_position, + efield_coords_position, + self.efield_coords, + self.coil_position_Trot, + e_field_vectors, + self.focal_factor_members, + self.efield_threshold, + self.efield_ROISize, + self.mtms_coord, + ] + ) self.mtms_coord = None else: - self.target_radius_list.append([target_list_index, self.Id_list, enorms_list, self.Idmax, self.coil_position, efield_coords_position, self.efield_coords, self.coil_position_Trot]) + self.target_radius_list.append( + [ + target_list_index, + self.Id_list, + enorms_list, + self.Idmax, + self.coil_position, + efield_coords_position, + self.efield_coords, + self.coil_position_Trot, + ] + ) def GetTargetSavedEfieldData(self, target_index_list): - if len(self.target_radius_list)>0: + if len(self.target_radius_list) > 0: target_index = 0 for i in range(len(self.target_radius_list)): if target_index_list == self.target_radius_list[i][0]: - target_index= i + target_index = i self.saved_target_data = self.target_radius_list[target_index] break def CreateEfieldmTMSCoorlegend(self): - self.mTMSCoordTextActor = self.CreateTextLegend(const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION,(0.4, 0.2)) + self.mTMSCoordTextActor = self.CreateTextLegend( + const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION, (0.4, 0.2) + ) self.ren.AddActor(self.mTMSCoordTextActor.actor) - def GetTargetPathmTMS(self,targeting_file): + def GetTargetPathmTMS(self, targeting_file): self.targeting_file = targeting_file def GetTargetPositions(self, target1_origin, target2): if self.mTMSCoordTextActor is None: self.CreateEfieldmTMSCoorlegend() self.mtms_coord = None - x_diff = round(target1_origin[0]- target2[0]) - y_diff = round(target1_origin[1]- target2[1]) + x_diff = round(target1_origin[0] - target2[0]) + y_diff = round(target1_origin[1] - target2[1]) csv_filename = self.targeting_file target_numbers = [-x_diff, y_diff, 0] self.matching_row = self.find_and_extract_data(csv_filename, target_numbers) dIs = self.mTMS_multiplyFactor(1000) - Publisher.sendMessage('Get dI for mtms', dIs = dIs) - self.mTMSCoordTextActor.SetValue('mTMS coords: '+ str(target_numbers)) + Publisher.sendMessage("Get dI for mtms", dIs=dIs) + self.mTMSCoordTextActor.SetValue("mTMS coords: " + str(target_numbers)) self.mtms_coord = target_numbers - def GetdIsfromCoord(self,mtms_coord): + def GetdIsfromCoord(self, mtms_coord): if self.mTMSCoordTextActor is None: self.CreateEfieldmTMSCoorlegend() self.mtms_coord = None self.matching_row = self.find_and_extract_data(self.targeting_file, mtms_coord) dIs = self.mTMS_multiplyFactor(1000) - Publisher.sendMessage('Get dI for mtms', dIs=dIs) - self.mTMSCoordTextActor.SetValue('mTMS coords: ' + str(mtms_coord)) + Publisher.sendMessage("Get dI for mtms", dIs=dIs) + self.mTMSCoordTextActor.SetValue("mTMS coords: " + str(mtms_coord)) self.mtms_coord = mtms_coord def mTMS_multiplyFactor(self, factor): @@ -1580,11 +1705,12 @@ def mTMS_multiplyFactor(self, factor): result.append(convert) return result - def find_and_extract_data(self,csv_filename, target_numbers): + def find_and_extract_data(self, csv_filename, target_numbers): import csv + matching_rows = [] - with open(csv_filename, 'r') as csvfile: + with open(csv_filename, "r") as csvfile: csv_reader = csv.reader(csvfile) for row in csv_reader: # Extract the first three numbers from the current row @@ -1600,26 +1726,30 @@ def find_and_extract_data(self,csv_filename, target_numbers): def CheckStatusSavedEfieldData(self): indexes_saved_list = [] - if len(self.target_radius_list)>0: - efield_data_loaded= True + if len(self.target_radius_list) > 0: + efield_data_loaded = True for i in range(len(self.target_radius_list)): indexes_saved_list.append(self.target_radius_list[i][0]) - indexes_saved_list= np.array(indexes_saved_list) + indexes_saved_list = np.array(indexes_saved_list) else: efield_data_loaded = False - Publisher.sendMessage('Get status of Efield saved data', efield_data_loaded=efield_data_loaded, indexes_saved_list= indexes_saved_list ) + Publisher.sendMessage( + "Get status of Efield saved data", + efield_data_loaded=efield_data_loaded, + indexes_saved_list=indexes_saved_list, + ) def InitializeColorArray(self): self.colors_init.SetNumberOfComponents(3) - self.colors_init.SetName('Colors') + self.colors_init.SetName("Colors") color = 3 * [const.CORTEX_COLOR] for i in range(self.efield_mesh.GetNumberOfCells()): self.colors_init.InsertTuple(i, color) def ReturnToDefaultColorActor(self): self.efield_mesh.GetPointData().SetScalars(self.colors_init) - wx.CallAfter(Publisher.sendMessage, 'Initialize color array') - wx.CallAfter(Publisher.sendMessage, 'Recolor efield actor') + wx.CallAfter(Publisher.sendMessage, "Initialize color array") + wx.CallAfter(Publisher.sendMessage, "Recolor efield actor") def CreateLUTTableForEfield(self, min, max): lut = vtkLookupTable() @@ -1636,12 +1766,12 @@ def GetEfieldMaxMin(self, e_field_norms): min = np.amin(self.e_field_norms) self.efield_min = min self.efield_max = max - #self.Idmax = np.array(self.e_field_norms).argmax() - wx.CallAfter(Publisher.sendMessage, 'Update efield vis') + # self.Idmax = np.array(self.e_field_norms).argmax() + wx.CallAfter(Publisher.sendMessage, "Update efield vis") def FindClosestValueEfieldEdges(self, arr, threshold): - closest_value = min(arr, key = lambda x: abs(x-threshold)) - closest_index = np.argmin(np.abs(arr-closest_value)) + closest_value = min(arr, key=lambda x: abs(x - threshold)) + closest_index = np.argmin(np.abs(arr - closest_value)) return closest_index def CalculateEdgesEfield(self): @@ -1656,15 +1786,21 @@ def CalculateEdgesEfield(self): bcf.ClippingOn() lower_edge = self.FindClosestValueEfieldEdges(np.array(self.e_field_norms), self.efield_min) - middle_edge = self.FindClosestValueEfieldEdges(np.array(self.e_field_norms), self.efield_max * 0.2) - middle_edge1 = self.FindClosestValueEfieldEdges(np.array(self.e_field_norms), self.efield_max * 0.7) - upper_edge = self.FindClosestValueEfieldEdges(np.array(self.e_field_norms), self.efield_max * 0.9) + middle_edge = self.FindClosestValueEfieldEdges( + np.array(self.e_field_norms), self.efield_max * 0.2 + ) + middle_edge1 = self.FindClosestValueEfieldEdges( + np.array(self.e_field_norms), self.efield_max * 0.7 + ) + upper_edge = self.FindClosestValueEfieldEdges( + np.array(self.e_field_norms), self.efield_max * 0.9 + ) lower_edge = self.efield_mesh.GetPoint(lower_edge) middle_edge = self.efield_mesh.GetPoint(middle_edge) middle_edge1 = self.efield_mesh.GetPoint(middle_edge1) upper_edge = self.efield_mesh.GetPoint(upper_edge) - edges = [ lower_edge, middle_edge, middle_edge1, upper_edge] + edges = [lower_edge, middle_edge, middle_edge1, upper_edge] for i in range(len(edges)): bcf.SetValue(i, edges[i][2]) bcf.SetScalarModeToIndex() @@ -1683,7 +1819,7 @@ def CalculateEdgesEfield(self): self.edge_actor = vtkActor() self.edge_actor.SetMapper(edge_mapper) - self.edge_actor.GetProperty().SetColor(named_colors.GetColor3d('Black')) + self.edge_actor.GetProperty().SetColor(named_colors.GetColor3d("Black")) self.edge_actor.GetProperty().SetLineWidth(3.0) actor.GetProperty().SetOpacity(0) self.ren.AddViewProp(actor) @@ -1695,8 +1831,11 @@ def CalculateEdgesEfield(self): def GetIndexesAboveThreshold(self, threshold): cell_id_indexes = [] positions = [] - indexes = [index for index, value in enumerate(self.e_field_norms) if - value > self.efield_max * threshold] + indexes = [ + index + for index, value in enumerate(self.e_field_norms) + if value > self.efield_max * threshold + ] for index, value in enumerate(indexes): cell_id_indexes.append(self.Id_list[value]) positions.append(self.efield_mesh.GetPoint(self.Id_list[value])) @@ -1743,7 +1882,9 @@ def FindCenterofGravity(self, cell_id_indexes, positions): cell_id = mutable(0) sub_id = mutable(0) distance = mutable(0.0) - self.locator_efield_cell.FindClosestPoint(query_point, center_gravity, cell_id, sub_id, distance) + self.locator_efield_cell.FindClosestPoint( + query_point, center_gravity, cell_id, sub_id, distance + ) return center_gravity def DetectClustersEfieldSpread(self, points): @@ -1756,18 +1897,49 @@ def DetectClustersEfieldSpread(self, points): n_clusters = len(set(labels)) - (1 if -1 in labels else 0) core_sample_indices = dbscan.core_sample_indices_ cluster_centers = points[core_sample_indices, :] - representative_centers = np.array([cluster.mean(axis=0) for cluster in np.split(cluster_centers, np.cumsum( - np.unique(dbscan.labels_, return_counts=True)[1])[:-1])]) + representative_centers = np.array( + [ + cluster.mean(axis=0) + for cluster in np.split( + cluster_centers, + np.cumsum(np.unique(dbscan.labels_, return_counts=True)[1])[:-1], + ) + ] + ) distances_between_representatives = np.max(pairwise_distances(representative_centers)) - focal_factor = n_clusters/len(self.Id_list) + distances_between_representatives/30 + self.distance_efield/15 - focal_factor = 1/focal_factor - self.ClusterEfieldTextActor.SetValue('Clusters above '+ str(int(self.efield_threshold*100)) + '% percent: ' + - str(n_clusters)+ '\n' +' distance:' +str(distances_between_representatives) + - '\n'+ 'Focal Factor: ' + ' '+str(focal_factor)) - self.focal_factor_members = [n_clusters, n_clusters/len(self.Id_list), distances_between_representatives,distances_between_representatives/30, self.distance_efield, self.distance_efield/15,focal_factor] + focal_factor = ( + n_clusters / len(self.Id_list) + + distances_between_representatives / 30 + + self.distance_efield / 15 + ) + focal_factor = 1 / focal_factor + self.ClusterEfieldTextActor.SetValue( + "Clusters above " + + str(int(self.efield_threshold * 100)) + + "% percent: " + + str(n_clusters) + + "\n" + + " distance:" + + str(distances_between_representatives) + + "\n" + + "Focal Factor: " + + " " + + str(focal_factor) + ) + self.focal_factor_members = [ + n_clusters, + n_clusters / len(self.Id_list), + distances_between_representatives, + distances_between_representatives / 30, + self.distance_efield, + self.distance_efield / 15, + focal_factor, + ] def CreateClustersEfieldLegend(self): - self.ClusterEfieldTextActor = self.CreateTextLegend(const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION,(0.03, 0.99)) + self.ClusterEfieldTextActor = self.CreateTextLegend( + const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION, (0.03, 0.99) + ) self.ren.AddActor(self.ClusterEfieldTextActor.actor) def EnableShowEfieldAboveThreshold(self, enable): @@ -1775,15 +1947,17 @@ def EnableShowEfieldAboveThreshold(self, enable): def SegmentEfieldMax(self, cell_id_indexes): color = [255, 165, 0] - for j , value in enumerate(cell_id_indexes): + for j, value in enumerate(cell_id_indexes): self.colors_init.InsertTuple(value, color) def GetEfieldActor(self, e_field_actor): - self.efield_actor = e_field_actor + self.efield_actor = e_field_actor def FindPointsAroundRadiusEfield(self, cellId): radius = int(self.efield_ROISize) - self.locator_efield.FindPointsWithinRadius(radius, self.e_field_mesh_centers.GetPoint(cellId), self.radius_list) + self.locator_efield.FindPointsWithinRadius( + radius, self.e_field_mesh_centers.GetPoint(cellId), self.radius_list + ) def CreateCortexProjectionOnScalp(self, marker_id, position, orientation): self.target_at_cortex = None @@ -1793,7 +1967,9 @@ def CreateCortexProjectionOnScalp(self, marker_id, position, orientation): self.target_at_cortex = position_flip point_scalp = self.FindClosestPointToMesh(position_flip, self.scalp_mesh) self.CreateEfieldAtTargetLegend() - Publisher.sendMessage('Create Marker from tangential', point = point_scalp, orientation =orientation) + Publisher.sendMessage( + "Create Marker from tangential", point=point_scalp, orientation=orientation + ) def ClearTargetAtCortex(self): self.target_at_cortex = None @@ -1806,41 +1982,43 @@ def SetEfieldTargetAtCortex(self, position, orientation): self.target_at_cortex = position_flip self.CreateEfieldAtTargetLegend() - def ShowEfieldAtCortexTarget(self): if self.target_at_cortex is not None: import vtk + index = self.efield_mesh.FindPoint(self.target_at_cortex) if index in self.Id_list: cell_number = self.Id_list.index(index) self.EfieldAtTargetLegend.SetValue( - 'Efield at Target: ' + str("{:04.2f}".format(self.e_field_norms[cell_number]))) + "Efield at Target: " + str("{:04.2f}".format(self.e_field_norms[cell_number])) + ) else: - self.EfieldAtTargetLegend.SetValue( - 'Efield at Target: ' + str("{:04.2f}".format(0))) + self.EfieldAtTargetLegend.SetValue("Efield at Target: " + str("{:04.2f}".format(0))) def CreateEfieldAtTargetLegend(self): if self.EfieldAtTargetLegend is not None: self.ren.RemoveActor(self.EfieldAtTargetLegend.actor) - self.EfieldAtTargetLegend = self.CreateTextLegend(const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION,(0.4, 0.96)) + self.EfieldAtTargetLegend = self.CreateTextLegend( + const.TEXT_SIZE_DISTANCE_DURING_NAVIGATION, (0.4, 0.96) + ) self.ren.AddActor(self.EfieldAtTargetLegend.actor) - def FindClosestPointToMesh(self, point,mesh): - closest_distance = float('inf') + def FindClosestPointToMesh(self, point, mesh): + closest_distance = float("inf") closest_point = None point = np.array(point) for i in range(mesh.GetNumberOfCells()): - distance_sq = np.linalg.norm(np.array(self.scalp_mesh.GetPoint(i)) -point) + distance_sq = np.linalg.norm(np.array(self.scalp_mesh.GetPoint(i)) - point) if distance_sq < closest_distance: closest_distance = distance_sq closest_point = self.scalp_mesh.GetPoint(i) return closest_point def GetScalpEfield(self, scalp_actor): - self.scalp_actor = scalp_actor + self.scalp_actor = scalp_actor def InitEfield(self, e_field_brain): - self.e_field_mesh_normals =e_field_brain.e_field_mesh_normals + self.e_field_mesh_normals = e_field_brain.e_field_mesh_normals self.e_field_mesh_centers = e_field_brain.e_field_mesh_centers self.locator_efield = e_field_brain.locator_efield self.locator_efield_cell = e_field_brain.locator_efield_Cell @@ -1861,8 +2039,8 @@ def InitEfield(self, e_field_brain): self.e_field_norms = None self.efield_threshold = const.EFIELD_MAX_RANGE_SCALE self.efield_ROISize = const.EFIELD_ROI_SIZE - self.target_radius_list=[] - self.focal_factor_members=[] + self.target_radius_list = [] + self.focal_factor_members = [] self.distance_efield = None self.mtms_coord = None @@ -1913,49 +2091,52 @@ def ShowEfieldintheintersection(self, intersectingCellIds, p1, coil_norm, coil_d self.radius_list.Reset() def OnUpdateEfieldvis(self): - if self.radius_list.GetNumberOfIds() !=0: + if self.radius_list.GetNumberOfIds() != 0: self.efield_lut = self.CreateLUTTableForEfield(self.efield_min, self.efield_max) self.CalculateEdgesEfield() self.colors_init.SetNumberOfComponents(3) self.colors_init.Fill(const.CORTEX_COLOR) for h in range(len(self.Id_list)): - dcolor = 3 * [0.0] - index_id = self.Id_list[h] - if self.plot_vector: + dcolor = 3 * [0.0] + index_id = self.Id_list[h] + if self.plot_vector: self.efield_lut.GetColor(self.e_field_norms[h], dcolor) - else: + else: self.efield_lut.GetColor(self.e_field_norms[index_id], dcolor) - color = 3 * [0.0] - for j in range(0, 3): - color[j] = int(255.0 * dcolor[j]) - self.colors_init.InsertTuple(index_id, color) + color = 3 * [0.0] + for j in range(0, 3): + color[j] = int(255.0 * dcolor[j]) + self.colors_init.InsertTuple(index_id, color) self.efield_mesh.GetPointData().SetScalars(self.colors_init) - wx.CallAfter(Publisher.sendMessage, 'Recolor efield actor') + wx.CallAfter(Publisher.sendMessage, "Recolor efield actor") if self.vectorfield_actor is not None: self.ren.RemoveActor(self.vectorfield_actor) if self.plot_vector: - wx.CallAfter(Publisher.sendMessage,'Show max Efield actor') - wx.CallAfter(Publisher.sendMessage,'Show CoG Efield actor') + wx.CallAfter(Publisher.sendMessage, "Show max Efield actor") + wx.CallAfter(Publisher.sendMessage, "Show CoG Efield actor") if self.efield_tools: - wx.CallAfter(Publisher.sendMessage, 'Show distance between Max and CoG Efield') + wx.CallAfter(Publisher.sendMessage, "Show distance between Max and CoG Efield") if self.positions_above_threshold is not None: self.DetectClustersEfieldSpread(self.positions_above_threshold) - if self.enableefieldabovethreshold and self.cell_id_indexes_above_threshold is not None: + if ( + self.enableefieldabovethreshold + and self.cell_id_indexes_above_threshold is not None + ): self.SegmentEfieldMax(self.cell_id_indexes_above_threshold) self.ShowEfieldAtCortexTarget() if self.plot_no_connection: - wx.CallAfter(Publisher.sendMessage,'Show Efield vectors') - self.plot_vector= False + wx.CallAfter(Publisher.sendMessage, "Show Efield vectors") + self.plot_vector = False self.plot_no_connection = False else: - wx.CallAfter(Publisher.sendMessage,'Recolor again') + wx.CallAfter(Publisher.sendMessage, "Recolor again") def UpdateEfieldPointLocation(self, m_img, coord, queue_IDs): - #TODO: In the future, remove the "put_nowait" and mesh processing to another module (maybe e_field.py) + # TODO: In the future, remove the "put_nowait" and mesh processing to another module (maybe e_field.py) # this might work because a python instance from the 3D mesh can be edited in the thread. Check how to extract # the instance from the desired mesh for visualization and if it works. Optimally, there should be no # processing or threading related commands inside viewer_volume. - [coil_dir, norm, coil_norm, p1]= self.ObjectArrowLocation(m_img, coord) + [coil_dir, norm, coil_norm, p1] = self.ObjectArrowLocation(m_img, coord) intersectingCellIds = self.GetCellIntersection(p1, norm, self.locator_efield_cell) self.ShowEfieldintheintersection(intersectingCellIds, p1, coil_norm, coil_dir) try: @@ -1974,7 +2155,7 @@ def UpdateEfieldPointLocationOffline(self, m_img, coord, list_index): id_list = [] for h in range(self.radius_list.GetNumberOfIds()): id_list.append(self.radius_list.GetId(h)) - Publisher.sendMessage('Get ID list', ID_list = id_list) + Publisher.sendMessage("Get ID list", ID_list=id_list) self.plot_no_connection = True self.list_index_efield_vectors = list_index @@ -1994,13 +2175,12 @@ def GetCoilPosition(self, position, orientation): T_rot = np.append(ct1, ct2, axis=0) T_rot = np.append(T_rot, cn, axis=0) # append T_rot = T_rot.tolist() # to list - Publisher.sendMessage('Send coil position and rotation', T_rot=T_rot, cp=cp, m_img=m_img) - + Publisher.sendMessage("Send coil position and rotation", T_rot=T_rot, cp=cp, m_img=m_img) def GetEnorm(self, enorm_data, plot_vector): - self.e_field_col1=[] - self.e_field_col2=[] - self.e_field_col3=[] + self.e_field_col1 = [] + self.e_field_col2 = [] + self.e_field_col3 = [] session = ses.Session() self.plot_vector = plot_vector self.coil_position_Trot = enorm_data[0] @@ -2008,14 +2188,18 @@ def GetEnorm(self, enorm_data, plot_vector): self.efield_coords = enorm_data[2] self.Id_list = enorm_data[4] if self.plot_vector: - if session.GetConfig('debug_efield'): - self.e_field_norms = enorm_data[3][self.Id_list,0] - self.e_field_col1 = enorm_data[3][self.Id_list,1] - self.e_field_col2 = enorm_data[3][self.Id_list,1] - self.e_field_col3 = enorm_data[3][self.Id_list,3] + if session.GetConfig("debug_efield"): + self.e_field_norms = enorm_data[3][self.Id_list, 0] + self.e_field_col1 = enorm_data[3][self.Id_list, 1] + self.e_field_col2 = enorm_data[3][self.Id_list, 1] + self.e_field_col3 = enorm_data[3][self.Id_list, 3] self.Idmax = np.array(self.Id_list[np.array(self.e_field_norms).argmax()]) - max =np.array(self.e_field_norms).argmax() - self.max_efield_array = [self.e_field_col1[max],self.e_field_col2[max],self.e_field_col3[max] ] + max = np.array(self.e_field_norms).argmax() + self.max_efield_array = [ + self.e_field_col1[max], + self.e_field_col2[max], + self.e_field_col3[max], + ] else: self.e_field_norms = enorm_data[3].enorm self.e_field_col1 = enorm_data[3].column1 @@ -2024,34 +2208,47 @@ def GetEnorm(self, enorm_data, plot_vector): self.max_efield_array = enorm_data[3].mvector self.Idmax = self.Id_list[enorm_data[3].maxindex] if self.save_automatically and self.plot_no_connection: - import invesalius.project as prj import time + import invesalius.gui.dialogs as dlg + import invesalius.project as prj + proj = prj.Project() timestamp = time.localtime(time.time()) - stamp_date = '{:0>4d}{:0>2d}{:0>2d}'.format(timestamp.tm_year, timestamp.tm_mon, timestamp.tm_mday) - stamp_time = '{:0>2d}{:0>2d}{:0>2d}'.format(timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec) - sep = '-' + stamp_date = "{:0>4d}{:0>2d}{:0>2d}".format( + timestamp.tm_year, timestamp.tm_mon, timestamp.tm_mday + ) + stamp_time = "{:0>2d}{:0>2d}{:0>2d}".format( + timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec + ) + sep = "-" if self.path_meshes is None: import os + current_folder_path = os.getcwd() else: current_folder_path = self.path_meshes - parts = [current_folder_path, '/', stamp_date, stamp_time, proj.name, 'Efield'] - default_filename = sep.join(parts) + '.csv' + parts = [current_folder_path, "/", stamp_date, stamp_time, proj.name, "Efield"] + default_filename = sep.join(parts) + ".csv" - filename = dlg.ShowLoadSaveDialog(message=_(u"Save markers as..."), - wildcard='(*.csv)|*.csv', - style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, - default_filename=default_filename) + filename = dlg.ShowLoadSaveDialog( + message=_("Save markers as..."), + wildcard="(*.csv)|*.csv", + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, + default_filename=default_filename, + ) if not filename: return - Publisher.sendMessage('Save Efield data', filename=filename, - plot_efield_vectors=self.plot_efield_vectors, marker_id = self.list_index_efield_vectors ) + Publisher.sendMessage( + "Save Efield data", + filename=filename, + plot_efield_vectors=self.plot_efield_vectors, + marker_id=self.list_index_efield_vectors, + ) else: self.e_field_norms = enorm_data[3] self.Idmax = np.array(self.e_field_norms).argmax() @@ -2059,39 +2256,100 @@ def GetEnorm(self, enorm_data, plot_vector): self.GetEfieldMaxMin(self.e_field_norms) def SaveEfieldData(self, filename, plot_efield_vectors, marker_id): - import invesalius.data.imagedata_utils as imagedata_utils import csv - all_data=[] - header = ['Marker ID', 'T_rot','Coil center','Coil position in world coordinates', 'InVesalius coordinates', 'Enorm','ID cell max', 'Efield vectors', 'Enorm cell indexes', 'Focal factors', 'Efield threshold', 'Efield ROI size', 'Mtms_coord'] + import invesalius.data.imagedata_utils as imagedata_utils + + all_data = [] + + header = [ + "Marker ID", + "T_rot", + "Coil center", + "Coil position in world coordinates", + "InVesalius coordinates", + "Enorm", + "ID cell max", + "Efield vectors", + "Enorm cell indexes", + "Focal factors", + "Efield threshold", + "Efield ROI size", + "Mtms_coord", + ] if self.efield_coords is not None: position_world, orientation_world = imagedata_utils.convert_invesalius_to_world( - position=[self.efield_coords[0], self.efield_coords[1], self.efield_coords[2]], - orientation=[self.efield_coords[3], self.efield_coords[4], self.efield_coords[5]], - ) + position=[self.efield_coords[0], self.efield_coords[1], self.efield_coords[2]], + orientation=[self.efield_coords[3], self.efield_coords[4], self.efield_coords[5]], + ) efield_coords_position = [list(position_world), list(orientation_world)] if plot_efield_vectors: if self.plot_no_connection: - e_field_vectors = [list(self.e_field_col1), list(self.e_field_col2), list(self.e_field_col3)] + e_field_vectors = [ + list(self.e_field_col1), + list(self.e_field_col2), + list(self.e_field_col3), + ] else: e_field_vectors = list(self.max_efield_array) - all_data.append([marker_id, self.coil_position_Trot, self.coil_position, efield_coords_position, self.efield_coords, list(self.e_field_norms), self.Idmax, e_field_vectors, self.Id_list, self.focal_factor_members, self.efield_threshold, self.efield_ROISize, self.mtms_coord]) - #REMOVE THIS + all_data.append( + [ + marker_id, + self.coil_position_Trot, + self.coil_position, + efield_coords_position, + self.efield_coords, + list(self.e_field_norms), + self.Idmax, + e_field_vectors, + self.Id_list, + self.focal_factor_members, + self.efield_threshold, + self.efield_ROISize, + self.mtms_coord, + ] + ) + # REMOVE THIS self.mtms_coord = None else: - all_data.append([marker_id, self.coil_position_Trot, self.coil_position, efield_coords_position, self.efield_coords, list(self.e_field_norms)]) + all_data.append( + [ + marker_id, + self.coil_position_Trot, + self.coil_position, + efield_coords_position, + self.efield_coords, + list(self.e_field_norms), + ] + ) - with open(filename, 'w', newline='') as file: + with open(filename, "w", newline="") as file: writer = csv.writer(file) writer.writerow(header) writer.writerows(all_data) def SavedAllEfieldData(self, filename): - import invesalius.data.imagedata_utils as imagedata_utils import csv - header = ['Marker ID', 'Enorm cell indexes', 'Enorm', 'ID cell Max', 'Coil center','Coil position world coordinates','InVesalius coordinates','T_rot', 'Efield vectors', 'Focal factors', 'Efield threshold', 'Efield ROI size', 'Mtms_coord'] + + import invesalius.data.imagedata_utils as imagedata_utils + + header = [ + "Marker ID", + "Enorm cell indexes", + "Enorm", + "ID cell Max", + "Coil center", + "Coil position world coordinates", + "InVesalius coordinates", + "T_rot", + "Efield vectors", + "Focal factors", + "Efield threshold", + "Efield ROI size", + "Mtms_coord", + ] all_data = list(self.target_radius_list) - with open(filename, 'w', newline='') as f: + with open(filename, "w", newline="") as f: writer = csv.writer(f) writer.writerow(header) writer.writerows(all_data) @@ -2100,7 +2358,7 @@ def GetCellIntersection(self, p1, p2, locator): vtk_colors = vtkNamedColors() # This find store the triangles that intersect the coil's normal intersectingCellIds = vtkIdList() - locator.FindCellsAlongLine(p1, p2, .001, intersectingCellIds) + locator.FindCellsAlongLine(p1, p2, 0.001, intersectingCellIds) return intersectingCellIds def ShowCoilProjection(self, intersectingCellIds, p1, coil_norm, coil_dir): @@ -2119,7 +2377,7 @@ def ShowCoilProjection(self, intersectingCellIds, p1, coil_norm, coil_dir): closestPoint = point pointnormal = np.array(self.peel_normals.GetTuple(cellId)) angle = np.rad2deg(np.arccos(np.dot(pointnormal, coil_norm))) - #print('the angle:', angle) + # print('the angle:', angle) self.ren.AddActor(self.obj_projection_arrow_actor) self.ren.AddActor(self.object_orientation_torus_actor) @@ -2131,11 +2389,19 @@ def ShowCoilProjection(self, intersectingCellIds, p1, coil_norm, coil_dir): # change color of arrow and disk according to angle if angle < self.angle_arrow_projection_threshold: - self.object_orientation_torus_actor.GetProperty().SetDiffuseColor([51/255,176/255,102/255]) - self.obj_projection_arrow_actor.GetProperty().SetColor([55/255,120/255,163/255]) + self.object_orientation_torus_actor.GetProperty().SetDiffuseColor( + [51 / 255, 176 / 255, 102 / 255] + ) + self.obj_projection_arrow_actor.GetProperty().SetColor( + [55 / 255, 120 / 255, 163 / 255] + ) else: - self.object_orientation_torus_actor.GetProperty().SetDiffuseColor([240/255,146/255,105/255]) - self.obj_projection_arrow_actor.GetProperty().SetColor([240/255,146/255,105/255]) + self.object_orientation_torus_actor.GetProperty().SetDiffuseColor( + [240 / 255, 146 / 255, 105 / 255] + ) + self.obj_projection_arrow_actor.GetProperty().SetColor( + [240 / 255, 146 / 255, 105 / 255] + ) else: self.ren.RemoveActor(self.obj_projection_arrow_actor) self.ren.RemoveActor(self.object_orientation_torus_actor) @@ -2158,8 +2424,8 @@ def UpdateMarkerOffsetState(self, create=False): if create: if not self.mark_actor: self.mark_actor = self.actor_factory.CreateBall( - position=[0., 0., 0.], - colour=[0., 1., 1.], + position=[0.0, 0.0, 0.0], + colour=[0.0, 1.0, 1.0], size=1.5, ) self.ren.AddActor(self.mark_actor) @@ -2171,7 +2437,7 @@ def UpdateMarkerOffsetState(self, create=False): self.UpdateRender() def UpdateArrowPose(self, m_img, coord, flag): - [coil_dir, norm, coil_norm, p1 ]= self.ObjectArrowLocation(m_img,coord) + [coil_dir, norm, coil_norm, p1] = self.ObjectArrowLocation(m_img, coord) if flag: self.ren.RemoveActor(self.obj_projection_arrow_actor) @@ -2183,15 +2449,15 @@ def TrackObject(self, enabled): if enabled: vtk_colors = vtkNamedColors() self.obj_projection_arrow_actor = self.actor_factory.CreateArrowUsingDirection( - position=[0., 0., 0.], - orientation=[0., 0., 0.], - colour=vtk_colors.GetColor3d('Red'), + position=[0.0, 0.0, 0.0], + orientation=[0.0, 0.0, 0.0], + colour=vtk_colors.GetColor3d("Red"), length_multiplier=0.8, ) self.object_orientation_torus_actor = self.actor_factory.CreateTorus( - position=[0., 0., 0.], - orientation=[0., 0., 0.], - colour=vtk_colors.GetColor3d('Red') + position=[0.0, 0.0, 0.0], + orientation=[0.0, 0.0, 0.0], + colour=vtk_colors.GetColor3d("Red"), ) else: self.ren.RemoveActor(self.mark_actor) @@ -2229,7 +2495,7 @@ def OnUpdateRobotStatus(self, robot_status): self.dummy_robot_actor.GetProperty().SetColor(1, 0, 0) def __bind_events_wx(self): - #self.Bind(wx.EVT_SIZE, self.OnSize) + # self.Bind(wx.EVT_SIZE, self.OnSize) # self.canvas.subscribe_event('LeftButtonPressEvent', self.on_insert_point) pass @@ -2238,11 +2504,15 @@ def on_insert_point(self, evt): self.polygon.append_point(pos) self.canvas.Refresh() - arr = self.canvas.draw_element_to_array([self.polygon,]) - imsave('/tmp/polygon.png', arr) + arr = self.canvas.draw_element_to_array( + [ + self.polygon, + ] + ) + imsave("/tmp/polygon.png", arr) def SetInteractorStyle(self, state): - cleanup = getattr(self.style, 'CleanUp', None) + cleanup = getattr(self.style, "CleanUp", None) if cleanup: self.style.CleanUp() @@ -2250,7 +2520,7 @@ def SetInteractorStyle(self, state): style = styles.Styles.get_style(state)(self) - setup = getattr(style, 'SetUp', None) + setup = getattr(style, "SetUp", None) if setup: style.SetUp() @@ -2301,14 +2571,20 @@ def VolumetricCamera(self, cam_focus): self.camera_show_object = self.coil_visualizer.show_coil if self.camera_show_object: - v1 = np.array([cam_focus[0] - self.pTarget[0], cam_focus[1] - self.pTarget[1], cam_focus[2] - self.pTarget[2]]) + v1 = np.array( + [ + cam_focus[0] - self.pTarget[0], + cam_focus[1] - self.pTarget[1], + cam_focus[2] - self.pTarget[2], + ] + ) else: v1 = cam_focus - self.initial_focus v1n = np.sqrt(inner1d(v1, v1)) if not v1n: v1n = 1.0 - cam_pos = (v1/v1n)*v0n + cam_focus + cam_pos = (v1 / v1n) * v0n + cam_focus cam.SetFocalPoint(cam_focus) cam.SetPosition(cam_pos) @@ -2319,10 +2595,12 @@ def VolumetricCamera(self, cam_focus): # self.ren.ResetCamera() def OnExportSurface(self, filename, filetype, convert_to_world=False): - if filetype not in (const.FILETYPE_STL, - const.FILETYPE_VTP, - const.FILETYPE_PLY, - const.FILETYPE_STL_ASCII): + if filetype not in ( + const.FILETYPE_STL, + const.FILETYPE_VTP, + const.FILETYPE_PLY, + const.FILETYPE_STL_ASCII, + ): if _has_win32api: utils.touch(filename) win_filename = win32api.GetShortPathName(filename) @@ -2375,7 +2653,7 @@ def OnDisableBrightContrast(self): def OnSetWindowLevelText(self, ww, wl): if self.raycasting_volume: - self.text.SetValue("WL: %d WW: %d"%(wl, ww)) + self.text.SetValue("WL: %d WW: %d" % (wl, ww)) # self.canvas.modified = True def OnShowRaycasting(self): @@ -2458,7 +2736,7 @@ def LoadVolume(self, volume, colour, ww, wl): self.light = self.ren.GetLights().GetNextItem() self.ren.AddVolume(volume) - self.text.SetValue("WL: %d WW: %d"%(wl, ww)) + self.text.SetValue("WL: %d WW: %d" % (wl, ww)) if self.on_wl: self.text.Show() @@ -2500,7 +2778,10 @@ def load_mask_preview(self, mask_3d_actor, flag=True): else: self.ren.RemoveVolume(mask_3d_actor) - if self.ren.GetActors().GetNumberOfItems() == 0 and self.ren.GetVolumes().GetNumberOfItems() == 1: + if ( + self.ren.GetActors().GetNumberOfItems() == 0 + and self.ren.GetVolumes().GetNumberOfItems() == 1 + ): self.ren.ResetCamera() self.ren.ResetCameraClippingRange() @@ -2514,36 +2795,36 @@ def OnSetViewAngle(self, view): def GetCameraSettings(self): camera = self.ren.GetActiveCamera() settings = { - 'position': camera.GetPosition(), - 'focal_point': camera.GetFocalPoint(), - 'view_up': camera.GetViewUp(), - 'clipping_range': camera.GetClippingRange(), - 'view_angle': camera.GetViewAngle(), - 'parallel_scale': camera.GetParallelScale(), + "position": camera.GetPosition(), + "focal_point": camera.GetFocalPoint(), + "view_up": camera.GetViewUp(), + "clipping_range": camera.GetClippingRange(), + "view_angle": camera.GetViewAngle(), + "parallel_scale": camera.GetParallelScale(), } return settings def ApplyCameraSettings(self, settings): camera = self.ren.GetActiveCamera() - camera.SetPosition(settings['position']) - camera.SetFocalPoint(settings['focal_point']) - camera.SetViewUp(settings['view_up']) - camera.SetClippingRange(settings['clipping_range']) - camera.SetViewAngle(settings['view_angle']) - camera.SetParallelScale(settings['parallel_scale']) + camera.SetPosition(settings["position"]) + camera.SetFocalPoint(settings["focal_point"]) + camera.SetViewUp(settings["view_up"]) + camera.SetClippingRange(settings["clipping_range"]) + camera.SetViewAngle(settings["view_angle"]) + camera.SetParallelScale(settings["parallel_scale"]) def SetViewAngle(self, view): cam = self.ren.GetActiveCamera() - cam.SetFocalPoint(0,0,0) + cam.SetFocalPoint(0, 0, 0) proj = prj.Project() orig_orien = proj.original_orientation - xv,yv,zv = const.VOLUME_POSITION[const.AXIAL][0][view] - xp,yp,zp = const.VOLUME_POSITION[const.AXIAL][1][view] + xv, yv, zv = const.VOLUME_POSITION[const.AXIAL][0][view] + xp, yp, zp = const.VOLUME_POSITION[const.AXIAL][1][view] - cam.SetViewUp(xv,yv,zv) - cam.SetPosition(xp,yp,zp) + cam.SetViewUp(xv, yv, zv) + cam.SetPosition(xp, yp, zp) self.ren.ResetCameraClippingRange() self.ren.ResetCamera() @@ -2552,20 +2833,20 @@ def SetViewAngle(self, view): def ShowOrientationCube(self): cube = vtkAnnotatedCubeActor() - cube.GetXMinusFaceProperty().SetColor(1,0,0) - cube.GetXPlusFaceProperty().SetColor(1,0,0) - cube.GetYMinusFaceProperty().SetColor(0,1,0) - cube.GetYPlusFaceProperty().SetColor(0,1,0) - cube.GetZMinusFaceProperty().SetColor(0,0,1) - cube.GetZPlusFaceProperty().SetColor(0,0,1) - cube.GetTextEdgesProperty().SetColor(0,0,0) + cube.GetXMinusFaceProperty().SetColor(1, 0, 0) + cube.GetXPlusFaceProperty().SetColor(1, 0, 0) + cube.GetYMinusFaceProperty().SetColor(0, 1, 0) + cube.GetYPlusFaceProperty().SetColor(0, 1, 0) + cube.GetZMinusFaceProperty().SetColor(0, 0, 1) + cube.GetZPlusFaceProperty().SetColor(0, 0, 1) + cube.GetTextEdgesProperty().SetColor(0, 0, 0) # anatomic labelling - cube.SetXPlusFaceText ("A") + cube.SetXPlusFaceText("A") cube.SetXMinusFaceText("P") - cube.SetYPlusFaceText ("L") + cube.SetYPlusFaceText("L") cube.SetYMinusFaceText("R") - cube.SetZPlusFaceText ("S") + cube.SetZPlusFaceText("S") cube.SetZMinusFaceText("I") axes = vtkAxesActor() @@ -2574,12 +2855,12 @@ def ShowOrientationCube(self): axes.SetXAxisLabelText("X") axes.SetYAxisLabelText("Y") axes.SetZAxisLabelText("Z") - #axes.SetNormalizedLabelPosition(.5, .5, .5) + # axes.SetNormalizedLabelPosition(.5, .5, .5) orientation_widget = vtkOrientationMarkerWidget() orientation_widget.SetOrientationMarker(cube) - orientation_widget.SetViewport(0.85,0.85,1.0,1.0) - #orientation_widget.SetOrientationMarker(axes) + orientation_widget.SetViewport(0.85, 0.85, 1.0, 1.0) + # orientation_widget.SetOrientationMarker(axes) orientation_widget.SetInteractor(self.interactor) orientation_widget.SetEnabled(1) orientation_widget.On() @@ -2596,18 +2877,19 @@ def AppendActor(self, actor): def Reposition3DPlane(self, plane_label): if not self.surface_added and not self.raycasting_volume: - if not self.repositioned_axial_plan and plane_label == 'Axial': + if not self.repositioned_axial_plan and plane_label == "Axial": self.SetViewAngle(const.VOL_ISO) self.repositioned_axial_plan = 1 - elif not self.repositioned_sagital_plan and plane_label == 'Sagital': + elif not self.repositioned_sagital_plan and plane_label == "Sagital": self.SetViewAngle(const.VOL_ISO) self.repositioned_sagital_plan = 1 - elif not self.repositioned_coronal_plan and plane_label == 'Coronal': + elif not self.repositioned_coronal_plan and plane_label == "Coronal": self.SetViewAngle(const.VOL_ISO) self.repositioned_coronal_plan = 1 + class SlicePlane: def __init__(self): project = prj.Project() @@ -2617,16 +2899,16 @@ def __init__(self): self.__bind_evt() def __bind_evt(self): - Publisher.subscribe(self.Enable, 'Enable plane') - Publisher.subscribe(self.Disable, 'Disable plane') - Publisher.subscribe(self.ChangeSlice, 'Change slice from slice plane') - Publisher.subscribe(self.UpdateAllSlice, 'Update all slice') + Publisher.subscribe(self.Enable, "Enable plane") + Publisher.subscribe(self.Disable, "Disable plane") + Publisher.subscribe(self.ChangeSlice, "Change slice from slice plane") + Publisher.subscribe(self.UpdateAllSlice, "Update all slice") def Create(self): plane_x = self.plane_x = vtkImagePlaneWidget() plane_x.InteractionOff() - #Publisher.sendMessage('Input Image in the widget', - #(plane_x, 'SAGITAL')) + # Publisher.sendMessage('Input Image in the widget', + # (plane_x, 'SAGITAL')) plane_x.SetPlaneOrientationToXAxes() plane_x.TextureVisibilityOn() plane_x.SetLeftButtonAction(0) @@ -2637,8 +2919,8 @@ def Create(self): plane_y = self.plane_y = vtkImagePlaneWidget() plane_y.DisplayTextOff() - #Publisher.sendMessage('Input Image in the widget', - #(plane_y, 'CORONAL')) + # Publisher.sendMessage('Input Image in the widget', + # (plane_y, 'CORONAL')) plane_y.SetPlaneOrientationToYAxes() plane_y.TextureVisibilityOn() plane_y.SetLeftButtonAction(0) @@ -2646,108 +2928,94 @@ def Create(self): plane_y.SetMiddleButtonAction(0) prop1 = plane_y.GetPlaneProperty() cursor_property = plane_y.GetCursorProperty() - cursor_property.SetOpacity(0) + cursor_property.SetOpacity(0) plane_z = self.plane_z = vtkImagePlaneWidget() plane_z.InteractionOff() - #Publisher.sendMessage('Input Image in the widget', - #(plane_z, 'AXIAL')) + # Publisher.sendMessage('Input Image in the widget', + # (plane_z, 'AXIAL')) plane_z.SetPlaneOrientationToZAxes() plane_z.TextureVisibilityOn() plane_z.SetLeftButtonAction(0) plane_z.SetRightButtonAction(0) plane_z.SetMiddleButtonAction(0) - - cursor_property = plane_z.GetCursorProperty() - cursor_property.SetOpacity(0) + cursor_property = plane_z.GetCursorProperty() + cursor_property.SetOpacity(0) prop3 = plane_z.GetPlaneProperty() prop3.SetColor(1, 0, 0) - - selected_prop3 = plane_z.GetSelectedPlaneProperty() - selected_prop3.SetColor(1,0,0) - + + selected_prop3 = plane_z.GetSelectedPlaneProperty() + selected_prop3.SetColor(1, 0, 0) + prop1 = plane_x.GetPlaneProperty() prop1.SetColor(0, 0, 1) - selected_prop1 = plane_x.GetSelectedPlaneProperty() + selected_prop1 = plane_x.GetSelectedPlaneProperty() selected_prop1.SetColor(0, 0, 1) - + prop2 = plane_y.GetPlaneProperty() prop2.SetColor(0, 1, 0) - selected_prop2 = plane_y.GetSelectedPlaneProperty() + selected_prop2 = plane_y.GetSelectedPlaneProperty() selected_prop2.SetColor(0, 1, 0) - Publisher.sendMessage('Set Widget Interactor', widget=plane_x) - Publisher.sendMessage('Set Widget Interactor', widget=plane_y) - Publisher.sendMessage('Set Widget Interactor', widget=plane_z) + Publisher.sendMessage("Set Widget Interactor", widget=plane_x) + Publisher.sendMessage("Set Widget Interactor", widget=plane_y) + Publisher.sendMessage("Set Widget Interactor", widget=plane_z) - Publisher.sendMessage('Render volume viewer') + Publisher.sendMessage("Render volume viewer") def Enable(self, plane_label=None): if plane_label: - if(plane_label == "Axial"): + if plane_label == "Axial": self.plane_z.On() - elif(plane_label == "Coronal"): + elif plane_label == "Coronal": self.plane_y.On() - elif(plane_label == "Sagital"): + elif plane_label == "Sagital": self.plane_x.On() - Publisher.sendMessage('Reposition 3D Plane', plane_label=plane_label) + Publisher.sendMessage("Reposition 3D Plane", plane_label=plane_label) else: self.plane_z.On() self.plane_x.On() self.plane_y.On() - Publisher.sendMessage('Set volume view angle', - view=const.VOL_ISO) + Publisher.sendMessage("Set volume view angle", view=const.VOL_ISO) - Publisher.sendMessage('Render volume viewer') + Publisher.sendMessage("Render volume viewer") def Disable(self, plane_label=None): if plane_label: - if(plane_label == "Axial"): + if plane_label == "Axial": self.plane_z.Off() - elif(plane_label == "Coronal"): + elif plane_label == "Coronal": self.plane_y.Off() - elif(plane_label == "Sagital"): + elif plane_label == "Sagital": self.plane_x.Off() else: self.plane_z.Off() self.plane_x.Off() self.plane_y.Off() - Publisher.sendMessage('Render volume viewer') + Publisher.sendMessage("Render volume viewer") def ChangeSlice(self, orientation, index): - if orientation == "CORONAL" and self.plane_y.GetEnabled(): - Publisher.sendMessage('Update slice 3D', - widget=self.plane_y, - orientation=orientation) - Publisher.sendMessage('Render volume viewer') + if orientation == "CORONAL" and self.plane_y.GetEnabled(): + Publisher.sendMessage("Update slice 3D", widget=self.plane_y, orientation=orientation) + Publisher.sendMessage("Render volume viewer") elif orientation == "SAGITAL" and self.plane_x.GetEnabled(): - Publisher.sendMessage('Update slice 3D', - widget=self.plane_x, - orientation=orientation) - Publisher.sendMessage('Render volume viewer') + Publisher.sendMessage("Update slice 3D", widget=self.plane_x, orientation=orientation) + Publisher.sendMessage("Render volume viewer") - elif orientation == 'AXIAL' and self.plane_z.GetEnabled() : - Publisher.sendMessage('Update slice 3D', - widget=self.plane_z, - orientation=orientation) - Publisher.sendMessage('Render volume viewer') + elif orientation == "AXIAL" and self.plane_z.GetEnabled(): + Publisher.sendMessage("Update slice 3D", widget=self.plane_z, orientation=orientation) + Publisher.sendMessage("Render volume viewer") def UpdateAllSlice(self): - Publisher.sendMessage('Update slice 3D', - widget=self.plane_y, - orientation="CORONAL") - Publisher.sendMessage('Update slice 3D', - widget=self.plane_x, - orientation="SAGITAL") - Publisher.sendMessage('Update slice 3D', - widget=self.plane_z, - orientation="AXIAL") + Publisher.sendMessage("Update slice 3D", widget=self.plane_y, orientation="CORONAL") + Publisher.sendMessage("Update slice 3D", widget=self.plane_x, orientation="SAGITAL") + Publisher.sendMessage("Update slice 3D", widget=self.plane_z, orientation="AXIAL") def DeletePlanes(self): del self.plane_x diff --git a/invesalius/data/visualization/coil_visualizer.py b/invesalius/data/visualization/coil_visualizer.py index c0b1a6368..3a9504ea0 100644 --- a/invesalius/data/visualization/coil_visualizer.py +++ b/invesalius/data/visualization/coil_visualizer.py @@ -2,25 +2,25 @@ import vtk -import invesalius.data.coordinates as dco import invesalius.constants as const -import invesalius.data.vtk_utils as vtku +import invesalius.data.coordinates as dco import invesalius.data.polydata_utils as pu - -from invesalius.pubsub import pub as Publisher -from invesalius.navigation.tracker import Tracker +import invesalius.data.vtk_utils as vtku import invesalius.session as ses +from invesalius.navigation.tracker import Tracker +from invesalius.pubsub import pub as Publisher class CoilVisualizer: """ A class for visualizing coil in the volume viewer. """ + # Color for highlighting a marker. - HIGHLIGHT_COLOR = vtk.vtkNamedColors().GetColor3d('Red') + HIGHLIGHT_COLOR = vtk.vtkNamedColors().GetColor3d("Red") # Color for the marker for target when the coil at the target. - COIL_AT_TARGET_COLOR = vtk.vtkNamedColors().GetColor3d('Green') + COIL_AT_TARGET_COLOR = vtk.vtkNamedColors().GetColor3d("Green") def __init__(self, renderer, interactor, actor_factory, vector_field_visualizer): self.renderer = renderer @@ -29,7 +29,7 @@ def __init__(self, renderer, interactor, actor_factory, vector_field_visualizer) # Keeps track of whether tracker fiducials have been set. self.tracker_fiducials_set = self.tracker.AreTrackerFiducialsSet() - + # The actor factory is used to create actors for the coil and coil center. self.actor_factory = actor_factory @@ -76,26 +76,26 @@ def __init__(self, renderer, interactor, actor_factory, vector_field_visualizer) self.__bind_events() def __bind_events(self): - Publisher.subscribe(self.SetCoilAtTarget, 'Coil at target') - Publisher.subscribe(self.OnNavigationStatus, 'Navigation status') - Publisher.subscribe(self.TrackObject, 'Track object') - Publisher.subscribe(self.ShowCoil, 'Show coil in viewer volume') - Publisher.subscribe(self.ConfigureCoil, 'Configure coil') - Publisher.subscribe(self.UpdateCoilPose, 'Update coil pose') - Publisher.subscribe(self.UpdateVectorField, 'Update vector field') - Publisher.subscribe(self.OnSetTrackerFiducials, 'Tracker fiducials set') - Publisher.subscribe(self.OnResetTrackerFiducials, 'Reset tracker fiducials') - Publisher.subscribe(self.OnLoadProject, 'Project loaded successfully') + Publisher.subscribe(self.SetCoilAtTarget, "Coil at target") + Publisher.subscribe(self.OnNavigationStatus, "Navigation status") + Publisher.subscribe(self.TrackObject, "Track object") + Publisher.subscribe(self.ShowCoil, "Show coil in viewer volume") + Publisher.subscribe(self.ConfigureCoil, "Configure coil") + Publisher.subscribe(self.UpdateCoilPose, "Update coil pose") + Publisher.subscribe(self.UpdateVectorField, "Update vector field") + Publisher.subscribe(self.OnSetTrackerFiducials, "Tracker fiducials set") + Publisher.subscribe(self.OnResetTrackerFiducials, "Reset tracker fiducials") + Publisher.subscribe(self.OnLoadProject, "Project loaded successfully") def SaveConfig(self): coil_path = self.coil_path.decode(const.FS_ENCODE) if self.coil_path is not None else None session = ses.Session() - session.SetConfig('coil_path', coil_path) + session.SetConfig("coil_path", coil_path) def LoadConfig(self): session = ses.Session() - coil_path_unencoded = session.GetConfig('coil_path') + coil_path_unencoded = session.GetConfig("coil_path") if coil_path_unencoded is None: return @@ -108,10 +108,10 @@ def OnSetTrackerFiducials(self): # If the track coil button is not pressed, press it after tracker fiducials have been set if not self.track_object_pressed: - Publisher.sendMessage('Press track object button', pressed=True) + Publisher.sendMessage("Press track object button", pressed=True) elif not self.show_coil_pressed: - Publisher.sendMessage('Press show-coil button', pressed=True) + Publisher.sendMessage("Press show-coil button", pressed=True) else: self.ShowCoil(self.show_coil_pressed) @@ -120,16 +120,15 @@ def OnResetTrackerFiducials(self): # If the show coil button is pressed, press it again to hide the coil if self.show_coil_pressed: - Publisher.sendMessage('Press show-coil button', pressed=False) + Publisher.sendMessage("Press show-coil button", pressed=False) def OnLoadProject(self): - # When loading some other than the initially loaded project, reset some instance variables if self.initial_project: self.initial_project = False else: self.tracker_fiducials_set = False - #self.initial_button_press = True + # self.initial_button_press = True def UpdateVectorField(self): """ @@ -139,7 +138,9 @@ def UpdateVectorField(self): new_vector_field_assembly = self.vector_field_visualizer.CreateVectorFieldAssembly() # Replace the old vector field assembly with the new one. - self.actor_factory.ReplaceActor(self.renderer, self.vector_field_assembly, new_vector_field_assembly) + self.actor_factory.ReplaceActor( + self.renderer, self.vector_field_assembly, new_vector_field_assembly + ) # Store the new vector field assembly. self.vector_field_assembly = new_vector_field_assembly @@ -154,7 +155,9 @@ def SetCoilAtTarget(self, state): vtk_colors = vtk.vtkNamedColors() # Set the color of the target coil based on whether the coil is at the target or not. - target_coil_color = vtk_colors.GetColor3d('Green') if state else vtk_colors.GetColor3d('DarkOrange') + target_coil_color = ( + vtk_colors.GetColor3d("Green") if state else vtk_colors.GetColor3d("DarkOrange") + ) # Set the color of both target coil (representing the target) and the coil center (representing the actual coil). self.target_coil_actor.GetProperty().SetDiffuseColor(target_coil_color) @@ -202,19 +205,15 @@ def ShowCoil(self, state): # Initially, if the tracker fiducials are not set but the show coil button is pressed, # press it again to hide the coil if not self.tracker_fiducials_set and self.show_coil_pressed and self.initial_button_press: - self.initial_button_press = False # Press the show coil button to turn it off - Publisher.sendMessage('Press show-coil button', pressed=False) + Publisher.sendMessage("Press show-coil button", pressed=False) return if self.target_coil_actor is not None: self.target_coil_actor.SetVisibility(self.show_coil_pressed) - if self.coil_center_actor is not None: - self.coil_center_actor.SetVisibility(self.show_coil_pressed) - if self.coil_actor: self.coil_actor.SetVisibility(self.show_coil_pressed) @@ -226,13 +225,17 @@ def AddTargetCoil(self, m_target): vtk_colors = vtk.vtkNamedColors() - decoded_path = self.coil_path.decode('utf-8') + decoded_path = self.coil_path.decode("utf-8") coil_filename = os.path.basename(decoded_path) coil_dir = os.path.dirname(decoded_path) # A hack to load the coil without the handle for the Magstim figure-8 coil. - coil_path = os.path.join(coil_dir, coil_filename) if coil_filename != 'magstim_fig8_coil.stl' else os.path.join(coil_dir, 'magstim_fig8_coil_no_handle.stl') + coil_path = ( + os.path.join(coil_dir, coil_filename) + if coil_filename != "magstim_fig8_coil.stl" + else os.path.join(coil_dir, "magstim_fig8_coil_no_handle.stl") + ) obj_polydata = vtku.CreateObjectPolyData(coil_path) @@ -253,14 +256,14 @@ def AddTargetCoil(self, m_target): obj_mapper = vtk.vtkPolyDataMapper() obj_mapper.SetInputData(normals.GetOutput()) obj_mapper.ScalarVisibilityOff() - #obj_mapper.ImmediateModeRenderingOn() # improve performance + # obj_mapper.ImmediateModeRenderingOn() # improve performance self.target_coil_actor = vtk.vtkActor() self.target_coil_actor.SetMapper(obj_mapper) - self.target_coil_actor.GetProperty().SetDiffuseColor(vtk_colors.GetColor3d('DarkOrange')) + self.target_coil_actor.GetProperty().SetDiffuseColor(vtk_colors.GetColor3d("DarkOrange")) self.target_coil_actor.GetProperty().SetSpecular(0.5) self.target_coil_actor.GetProperty().SetSpecularPower(10) - self.target_coil_actor.GetProperty().SetOpacity(.3) + self.target_coil_actor.GetProperty().SetOpacity(0.3) self.target_coil_actor.SetVisibility(self.show_coil_pressed) self.target_coil_actor.SetUserMatrix(m_target) @@ -300,23 +303,23 @@ def AddCoilActor(self, coil_path): obj_mapper = vtk.vtkPolyDataMapper() obj_mapper.SetInputData(normals.GetOutput()) obj_mapper.ScalarVisibilityOff() - #obj_mapper.ImmediateModeRenderingOn() # improve performance + # obj_mapper.ImmediateModeRenderingOn() # improve performance coil_actor = vtk.vtkActor() coil_actor.SetMapper(obj_mapper) - coil_actor.GetProperty().SetAmbientColor(vtk_colors.GetColor3d('GhostWhite')) + coil_actor.GetProperty().SetAmbientColor(vtk_colors.GetColor3d("GhostWhite")) coil_actor.GetProperty().SetSpecular(30) coil_actor.GetProperty().SetSpecularPower(80) - coil_actor.GetProperty().SetOpacity(.4) + coil_actor.GetProperty().SetOpacity(0.4) coil_actor.SetVisibility(0) self.coil_actor = coil_actor # Create an actor for the coil center. coil_center_actor = self.actor_factory.CreateTorus( - position=[0., 0., 0.], - orientation=[0., 0., 0.], - colour=vtk_colors.GetColor3d('Red'), + position=[0.0, 0.0, 0.0], + orientation=[0.0, 0.0, 0.0], + colour=vtk_colors.GetColor3d("Red"), scale=0.5, ) self.coil_center_actor = coil_center_actor diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index c5d72af77..8279db5c4 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -1,10 +1,10 @@ -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- # Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas # Copyright: (C) 2001 Centro de Pesquisas Renato Archer # Homepage: http://www.softwarepublico.gov.br # Contact: invesalius@cti.gov.br # License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- # Este programa e software livre; voce pode redistribui-lo e/ou # modifica-lo sob os termos da Licenca Publica Geral GNU, conforme # publicada pela Free Software Foundation; de acordo com a versao 2 @@ -15,59 +15,55 @@ # COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais # detalhes. -#-------------------------------------------------------------------------- -import os - -from functools import partial +# -------------------------------------------------------------------------- import itertools +import os import time +from functools import partial import numpy as np + try: - #TODO: the try-except could be done inside the mTMS() method call + # TODO: the try-except could be done inside the mTMS() method call from invesalius.navigation.mtms import mTMS + mTMS() has_mTMS = True except: has_mTMS = False -import wx import sys import uuid +import wx + try: import wx.lib.agw.foldpanelbar as fpb except ImportError: import wx.lib.foldpanelbar as fpb -import wx.lib.platebtn as pbtn - import wx.lib.colourselect as csel import wx.lib.masked.numctrl -from invesalius.pubsub import pub as Publisher +import wx.lib.platebtn as pbtn +from wx.lib.mixins.listctrl import ColumnSorterMixin import invesalius.constants as const - -from invesalius.data.markers.marker import MarkerType, Marker - import invesalius.gui.dialogs as dlg import invesalius.project as prj import invesalius.session as ses - -from invesalius import utils +from invesalius import inv_paths, utils +from invesalius.data.markers.marker import Marker, MarkerType from invesalius.gui.widgets.fiducial_buttons import OrderedFiducialButtons from invesalius.navigation.navigation import NavigationHub from invesalius.navigation.robot import RobotObjective -from wx.lib.mixins.listctrl import ColumnSorterMixin - -from invesalius import inv_paths +from invesalius.pubsub import pub as Publisher BTN_NEW = wx.NewIdRef() BTN_IMPORT_LOCAL = wx.NewIdRef() def GetBitMapForBackground(): - image_file = os.path.join('head.png') + image_file = os.path.join("head.png") bmp = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath(image_file)), wx.BITMAP_TYPE_PNG) return bmp @@ -79,19 +75,19 @@ def __init__(self, parent): inner_panel = InnerTaskPanel(self) sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(inner_panel, 1, wx.EXPAND|wx.GROW|wx.BOTTOM|wx.RIGHT | - wx.LEFT, 7) + sizer.Add(inner_panel, 1, wx.EXPAND | wx.GROW | wx.BOTTOM | wx.RIGHT | wx.LEFT, 7) sizer.Fit(self) self.SetSizer(sizer) self.Update() self.SetAutoLayout(1) + class InnerTaskPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) default_colour = self.GetBackgroundColour() - background_colour = wx.Colour(255,255,255) + background_colour = wx.Colour(255, 255, 255) self.SetBackgroundColour(background_colour) # Fold panel which contains navigation configurations @@ -100,7 +96,7 @@ def __init__(self, parent): # Add line sizer into main sizer main_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.Add(fold_panel, 1, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT, 5) + main_sizer.Add(fold_panel, 1, wx.GROW | wx.EXPAND | wx.LEFT | wx.RIGHT, 5) main_sizer.AddSpacer(5) main_sizer.Fit(self) @@ -110,6 +106,7 @@ def __init__(self, parent): self.sizer = main_sizer + class FoldPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) @@ -117,13 +114,14 @@ def __init__(self, parent): inner_panel = InnerFoldPanel(self) sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(inner_panel, 0, wx.EXPAND|wx.GROW) + sizer.Add(inner_panel, 0, wx.EXPAND | wx.GROW) sizer.Fit(self) self.SetSizerAndFit(sizer) self.Update() self.SetAutoLayout(1) + class InnerFoldPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) @@ -141,9 +139,10 @@ def __init__(self, parent): # parent panel. Perhaps we need to insert the item into the sizer also... # Study this. - fold_panel = fpb.FoldPanelBar(self, -1, wx.DefaultPosition, - (10, 800), 0, fpb.FPB_SINGLE_FOLD) - gbs = wx.GridBagSizer(5,5) + fold_panel = fpb.FoldPanelBar( + self, -1, wx.DefaultPosition, (10, 800), 0, fpb.FPB_SINGLE_FOLD + ) + gbs = wx.GridBagSizer(5, 5) gbs.AddGrowableCol(0, 1) self.gbs = gbs @@ -167,8 +166,7 @@ def __init__(self, parent): self.fold_panel = fold_panel self.__calc_best_size(ntw) fold_panel.ApplyCaptionStyle(item, style) - fold_panel.AddFoldPanelWindow(item, ntw, spacing=0, - leftSpacing=0, rightSpacing=0) + fold_panel.AddFoldPanelWindow(item, ntw, spacing=0, leftSpacing=0, rightSpacing=0) fold_panel.Expand(fold_panel.GetFoldPanel(0)) item = fold_panel.AddFoldPanel(_("Navigation"), collapsed=True) @@ -176,12 +174,11 @@ def __init__(self, parent): ntw = NavigationPanel(parent=item, nav_hub=nav_hub) fold_panel.ApplyCaptionStyle(item, style) - fold_panel.AddFoldPanelWindow(item, ntw, spacing=0, - leftSpacing=0, rightSpacing=0) + fold_panel.AddFoldPanelWindow(item, ntw, spacing=0, leftSpacing=0, rightSpacing=0) self.fold_panel.Bind(fpb.EVT_CAPTIONBAR, self.OnFoldPressCaption) sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(gbs, 1, wx.GROW|wx.EXPAND) + sizer.Add(gbs, 1, wx.GROW | wx.EXPAND) self.SetSizer(sizer) sizer.Fit(self) @@ -190,17 +187,17 @@ def __init__(self, parent): gbs.Layout() sizer.Fit(self) self.Fit() - + self.__bind_events() self.Update() self.SetAutoLayout(1) - + def __bind_events(self): - #Publisher.subscribe(self.OnShowDbs, "Show dbs folder") - #Publisher.subscribe(self.OnHideDbs, "Hide dbs folder") - Publisher.subscribe(self.OpenNavigation, 'Open navigation menu') + # Publisher.subscribe(self.OnShowDbs, "Show dbs folder") + # Publisher.subscribe(self.OnHideDbs, "Hide dbs folder") + Publisher.subscribe(self.OpenNavigation, "Open navigation menu") Publisher.subscribe(self.OnEnableState, "Enable state project") - + def __calc_best_size(self, panel): parent = panel.GetParent() panel.Reparent(self) @@ -210,7 +207,7 @@ def __calc_best_size(self, panel): # Calculating the size gbs.AddGrowableRow(1, 1) - #gbs.AddGrowableRow(0, 1) + # gbs.AddGrowableRow(0, 1) gbs.Add(fold_panel, (0, 0), flag=wx.EXPAND) gbs.Add(panel, (1, 0), flag=wx.EXPAND) gbs.Layout() @@ -228,7 +225,7 @@ def __calc_best_size(self, panel): def OnEnableState(self, state): if not state: self.fold_panel.Expand(self.fold_panel.GetFoldPanel(0)) - Publisher.sendMessage('Move to image page') + Publisher.sendMessage("Move to image page") def OnShowDbs(self): self.dbs_item.Show() @@ -245,6 +242,7 @@ def OnCheckStatus(self, nav_status, vis_status): def OnEnableSerialPort(self, evt, ctrl): if ctrl.GetValue(): from wx import ID_OK + dlg_port = dlg.SetCOMPort(select_baud_rate=False) if dlg_port.ShowModal() != ID_OK: @@ -254,9 +252,14 @@ def OnEnableSerialPort(self, evt, ctrl): com_port = dlg_port.GetCOMPort() baud_rate = 115200 - Publisher.sendMessage('Update serial port', serial_port_in_use=True, com_port=com_port, baud_rate=baud_rate) + Publisher.sendMessage( + "Update serial port", + serial_port_in_use=True, + com_port=com_port, + baud_rate=baud_rate, + ) else: - Publisher.sendMessage('Update serial port', serial_port_in_use=False) + Publisher.sendMessage("Update serial port", serial_port_in_use=False) # 'Show coil' button @@ -270,8 +273,8 @@ def EnableShowCoilButton(self, enabled=False): def OnShowCoil(self, evt=None): pressed = self.show_coil_button.GetValue() - Publisher.sendMessage('Show coil in viewer volume', state=pressed) - + Publisher.sendMessage("Show coil in viewer volume", state=pressed) + def OnFoldPressCaption(self, evt): id = evt.GetTag().GetId() expanded = evt.GetFoldStatus() @@ -286,15 +289,19 @@ def OnFoldPressCaption(self, evt): def ResizeFPB(self): sizeNeeded = self.fold_panel.GetPanelsLength(0, 0)[2] - self.fold_panel.SetMinSize((self.fold_panel.GetSize()[0], sizeNeeded )) + self.fold_panel.SetMinSize((self.fold_panel.GetSize()[0], sizeNeeded)) self.fold_panel.SetSize((self.fold_panel.GetSize()[0], sizeNeeded)) def CheckRegistration(self): - return self.tracker.AreTrackerFiducialsSet() and self.image.AreImageFiducialsSet() and self.navigation.GetObjectRegistration() is not None + return ( + self.tracker.AreTrackerFiducialsSet() + and self.image.AreImageFiducialsSet() + and self.navigation.GetObjectRegistration() is not None + ) def OpenNavigation(self): self.fold_panel.Expand(self.fold_panel.GetFoldPanel(1)) - + class CoregistrationPanel(wx.Panel): def __init__(self, parent, nav_hub): @@ -303,14 +310,14 @@ def __init__(self, parent, nav_hub): default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) except AttributeError: default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) - #Changed from default color for OSX + # Changed from default color for OSX background_colour = (255, 255, 255) self.SetBackgroundColour(background_colour) - book = wx.Notebook(self, -1,style= wx.BK_DEFAULT) + book = wx.Notebook(self, -1, style=wx.BK_DEFAULT) book.Bind(wx.EVT_BOOKCTRL_PAGE_CHANGING, self.OnPageChanging) book.Bind(wx.EVT_BOOKCTRL_PAGE_CHANGED, self.OnPageChanged) - if sys.platform != 'win32': + if sys.platform != "win32": book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) self.nav_hub = nav_hub @@ -331,20 +338,16 @@ def __init__(self, parent, nav_hub): book.Refresh() self.book = book self.__bind_events() - + def __bind_events(self): - Publisher.subscribe(self._FoldTracker, - 'Move to tracker page') - Publisher.subscribe(self._FoldRefine, - 'Move to refine page') - Publisher.subscribe(self._FoldStimulator, - 'Move to stimulator page') - Publisher.subscribe(self._FoldImage, - 'Move to image page') + Publisher.subscribe(self._FoldTracker, "Move to tracker page") + Publisher.subscribe(self._FoldRefine, "Move to refine page") + Publisher.subscribe(self._FoldStimulator, "Move to stimulator page") + Publisher.subscribe(self._FoldImage, "Move to image page") def OnPageChanging(self, evt): page = evt.GetOldSelection() - + def OnPageChanged(self, evt): old_page = evt.GetOldSelection() new_page = evt.GetSelection() @@ -358,7 +361,7 @@ def OnPageChanged(self, evt): if old_page != 2: # Load data into refine tab Publisher.sendMessage("Update UI for refine tab") - + # new page validations if (old_page == 1) and (new_page == 2 or new_page == 3): # Do not allow user to move to other (forward) tabs if tracker fiducials not done. @@ -380,6 +383,7 @@ def _FoldRefine(self): def _FoldStimulator(self): self.book.SetSelection(3) + class ImagePage(wx.Panel): def __init__(self, parent, nav_hub): wx.Panel.__init__(self, parent) @@ -393,9 +397,9 @@ def __init__(self, parent, nav_hub): # Toggle buttons for image fiducials background = wx.StaticBitmap(self, -1, self.bg_bmp, (0, 0)) for n, fiducial in enumerate(const.IMAGE_FIDUCIALS): - button_id = fiducial['button_id'] - label = fiducial['label'] - tip = fiducial['tip'] + button_id = fiducial["button_id"] + label = fiducial["label"] + tip = fiducial["tip"] ctrl = wx.ToggleButton(self, button_id, label=label, style=wx.BU_EXACTFIT) ctrl.SetToolTip(tip) @@ -408,9 +412,9 @@ def __init__(self, parent, nav_hub): for n in range(3): self.numctrls_fiducial[m].append( wx.lib.masked.numctrl.NumCtrl(parent=self, integerWidth=4, fractionWidth=1) - ) + ) self.numctrls_fiducial[m][n].Hide() - + start_button = wx.ToggleButton(self, label="Start Registration") start_button.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnStartRegistration, ctrl=start_button)) self.start_button = start_button @@ -425,36 +429,51 @@ def __init__(self, parent, nav_hub): self.next_button = next_button top_sizer = wx.BoxSizer(wx.HORIZONTAL) - top_sizer.AddMany([ - (start_button), - (reset_button) - ]) + top_sizer.AddMany([(start_button), (reset_button)]) bottom_sizer = wx.BoxSizer(wx.HORIZONTAL) bottom_sizer.Add(next_button) sizer = wx.GridBagSizer(5, 5) - sizer.Add(self.btns_set_fiducial[1], wx.GBPosition(1, 0), span=wx.GBSpan(1, 2), flag=wx.ALIGN_CENTER_VERTICAL) - sizer.Add(self.btns_set_fiducial[2], wx.GBPosition(0, 2), span=wx.GBSpan(1, 2), flag=wx.ALIGN_CENTER_HORIZONTAL) - sizer.Add(self.btns_set_fiducial[0], wx.GBPosition(1, 3), span=wx.GBSpan(1, 2), flag=wx.ALIGN_CENTER_VERTICAL) + sizer.Add( + self.btns_set_fiducial[1], + wx.GBPosition(1, 0), + span=wx.GBSpan(1, 2), + flag=wx.ALIGN_CENTER_VERTICAL, + ) + sizer.Add( + self.btns_set_fiducial[2], + wx.GBPosition(0, 2), + span=wx.GBSpan(1, 2), + flag=wx.ALIGN_CENTER_HORIZONTAL, + ) + sizer.Add( + self.btns_set_fiducial[0], + wx.GBPosition(1, 3), + span=wx.GBSpan(1, 2), + flag=wx.ALIGN_CENTER_VERTICAL, + ) sizer.Add(background, wx.GBPosition(1, 2)) main_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddMany([ - (top_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 10), - (sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT, 5), - (bottom_sizer, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, 30)]) + main_sizer.AddMany( + [ + (top_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 10), + (sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT, 5), + (bottom_sizer, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, 30), + ] + ) self.sizer = main_sizer self.SetSizerAndFit(main_sizer) self.__bind_events() def __bind_events(self): - Publisher.subscribe(self.LoadImageFiducials, 'Load image fiducials') - Publisher.subscribe(self.SetImageFiducial, 'Set image fiducial') - Publisher.subscribe(self.UpdateImageCoordinates, 'Set cross focal point') + Publisher.subscribe(self.LoadImageFiducials, "Load image fiducials") + Publisher.subscribe(self.SetImageFiducial, "Set image fiducial") + Publisher.subscribe(self.UpdateImageCoordinates, "Set cross focal point") Publisher.subscribe(self.OnResetImageFiducials, "Reset image fiducials") Publisher.subscribe(self._OnStateProject, "Enable state project") - Publisher.subscribe(self.StopRegistration, 'Stop image registration') + Publisher.subscribe(self.StopRegistration, "Stop image registration") def _OnStateProject(self, state): self.UpdateData() @@ -473,28 +492,32 @@ def UpdateData(self): self.UpdateNextButton() def LoadImageFiducials(self, label, position): - fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, 'fiducial_name', label[:2]) + fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, "fiducial_name", label[:2]) - fiducial_index = fiducial['fiducial_index'] - fiducial_name = fiducial['fiducial_name'] + fiducial_index = fiducial["fiducial_index"] + fiducial_name = fiducial["fiducial_name"] - Publisher.sendMessage('Set image fiducial', fiducial_name=fiducial_name, position=position) + Publisher.sendMessage("Set image fiducial", fiducial_name=fiducial_name, position=position) self.btns_set_fiducial[fiducial_index].SetValue(True) for m in [0, 1, 2]: self.numctrls_fiducial[fiducial_index][m].SetValue(position[m]) - + self.UpdateNextButton() def GetFiducialByAttribute(self, fiducials, attribute_name, attribute_value): found = [fiducial for fiducial in fiducials if fiducial[attribute_name] == attribute_value] - assert len(found) != 0, "No fiducial found for which {} = {}".format(attribute_name, attribute_value) + assert len(found) != 0, "No fiducial found for which {} = {}".format( + attribute_name, attribute_value + ) return found[0] def SetImageFiducial(self, fiducial_name, position): - fiducial = self.GetFiducialByAttribute(const.IMAGE_FIDUCIALS, 'fiducial_name', fiducial_name) - fiducial_index = fiducial['fiducial_index'] + fiducial = self.GetFiducialByAttribute( + const.IMAGE_FIDUCIALS, "fiducial_name", fiducial_name + ) + fiducial_index = fiducial["fiducial_index"] self.image.SetImageFiducial(fiducial_index, position) @@ -511,18 +534,20 @@ def UpdateImageCoordinates(self, position): self.numctrls_fiducial[m][n].SetValue(float(position[n])) def OnImageFiducials(self, n, evt): - fiducial_name = const.IMAGE_FIDUCIALS[n]['fiducial_name'] + fiducial_name = const.IMAGE_FIDUCIALS[n]["fiducial_name"] if self.btns_set_fiducial[n].GetValue(): - position = self.numctrls_fiducial[n][0].GetValue(),\ - self.numctrls_fiducial[n][1].GetValue(),\ - self.numctrls_fiducial[n][2].GetValue() + position = ( + self.numctrls_fiducial[n][0].GetValue(), + self.numctrls_fiducial[n][1].GetValue(), + self.numctrls_fiducial[n][2].GetValue(), + ) else: for m in [0, 1, 2]: self.numctrls_fiducial[n][m].SetValue(float(self.current_coord[m])) position = np.nan - Publisher.sendMessage('Set image fiducial', fiducial_name=fiducial_name, position=position) + Publisher.sendMessage("Set image fiducial", fiducial_name=fiducial_name, position=position) def OnNext(self, evt): Publisher.sendMessage("Move to tracker page") @@ -562,6 +587,7 @@ def OnStartRegistration(self, evt, ctrl): else: self.StopRegistration() + class TrackerPage(wx.Panel): def __init__(self, parent, nav_hub): wx.Panel.__init__(self, parent) @@ -578,7 +604,12 @@ def __init__(self, parent, nav_hub): self.bg_bmp = GetBitMapForBackground() # Toggle buttons for image fiducials - self.fiducial_buttons = OrderedFiducialButtons(self, const.TRACKER_FIDUCIALS, self.tracker.IsTrackerFiducialSet, order=const.FIDUCIAL_REGISTRATION_ORDER) + self.fiducial_buttons = OrderedFiducialButtons( + self, + const.TRACKER_FIDUCIALS, + self.tracker.IsTrackerFiducialSet, + order=const.FIDUCIAL_REGISTRATION_ORDER, + ) background = wx.StaticBitmap(self, -1, self.bg_bmp, (0, 0)) for index, btn in enumerate(self.fiducial_buttons): @@ -586,7 +617,7 @@ def __init__(self, parent, nav_hub): btn.Disable() self.fiducial_buttons.Update() - + register_button = wx.Button(self, label="Record Fiducial") register_button.Bind(wx.EVT_BUTTON, partial(self.OnRegister)) register_button.Disable() @@ -621,50 +652,71 @@ def __init__(self, parent, nav_hub): if tracker_status: main_label.SetLabel(self.tracker.get_trackers()[self.tracker.GetTrackerId() - 1]) - + self.main_label = main_label top_sizer = wx.BoxSizer(wx.HORIZONTAL) - top_sizer.AddMany([ - (start_button), - (reset_button) - ]) + top_sizer.AddMany([(start_button), (reset_button)]) middle_sizer = wx.BoxSizer(wx.HORIZONTAL) - middle_sizer.AddMany([ - (current_label), - (main_label) - ]) - + middle_sizer.AddMany([(current_label), (main_label)]) + bottom_sizer = wx.BoxSizer(wx.HORIZONTAL) - bottom_sizer.AddMany([ - (back_button, 0, wx.EXPAND), - (preferences_button, 0, wx.EXPAND), - (next_button, 0, wx.EXPAND) - ]) + bottom_sizer.AddMany( + [ + (back_button, 0, wx.EXPAND), + (preferences_button, 0, wx.EXPAND), + (next_button, 0, wx.EXPAND), + ] + ) sizer = wx.GridBagSizer(5, 5) - sizer.Add(self.fiducial_buttons[1], wx.GBPosition(1, 0), span=wx.GBSpan(1, 2), flag=wx.ALIGN_CENTER_VERTICAL) - sizer.Add(self.fiducial_buttons[2], wx.GBPosition(0, 2), span=wx.GBSpan(1, 2), flag=wx.ALIGN_CENTER_HORIZONTAL) - sizer.Add(self.fiducial_buttons[0], wx.GBPosition(1, 3), span=wx.GBSpan(1, 2), flag=wx.ALIGN_CENTER_VERTICAL) + sizer.Add( + self.fiducial_buttons[1], + wx.GBPosition(1, 0), + span=wx.GBSpan(1, 2), + flag=wx.ALIGN_CENTER_VERTICAL, + ) + sizer.Add( + self.fiducial_buttons[2], + wx.GBPosition(0, 2), + span=wx.GBSpan(1, 2), + flag=wx.ALIGN_CENTER_HORIZONTAL, + ) + sizer.Add( + self.fiducial_buttons[0], + wx.GBPosition(1, 3), + span=wx.GBSpan(1, 2), + flag=wx.ALIGN_CENTER_VERTICAL, + ) + sizer.Add(background, wx.GBPosition(1, 2)) - sizer.Add(register_button, wx.GBPosition(2, 2), span=wx.GBSpan(1, 2), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) + + sizer.Add( + register_button, + wx.GBPosition(2, 2), + span=wx.GBSpan(1, 2), + flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, + ) main_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddMany([ - (top_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 10), - (sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT, 5), - (middle_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, 20), - (5, 5), - (bottom_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.BOTTOM, 20)]) - + main_sizer.AddMany( + [ + (top_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 10), + (sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT, 5), + (middle_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, 20), + (5, 5), + (bottom_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.LEFT | wx.RIGHT | wx.BOTTOM, 20), + ] + ) + self.sizer = main_sizer self.SetSizerAndFit(main_sizer) self.Layout() self.__bind_events() def __bind_events(self): - Publisher.subscribe(self.SetTrackerFiducial, 'Set tracker fiducial') + Publisher.subscribe(self.SetTrackerFiducial, "Set tracker fiducial") Publisher.subscribe(self.OnTrackerChanged, "Tracker changed") Publisher.subscribe(self.OnResetTrackerFiducials, "Reset tracker fiducials") @@ -678,7 +730,7 @@ def UpdateElements(self): def StartRegistration(self): if not self.tracker.IsTrackerInitialized(): self.start_button.SetValue(False) - dlg.ShowNavigationTrackerWarning(0, 'choose') + dlg.ShowNavigationTrackerWarning(0, "choose") return self.registration_on = True @@ -693,7 +745,9 @@ def set_fiducial_callback(state): if state and index is not None: self.SetTrackerFiducial(index) - self.pedal_connector.add_callback('fiducial', set_fiducial_callback, remove_when_released=False) + self.pedal_connector.add_callback( + "fiducial", set_fiducial_callback, remove_when_released=False + ) def StopRegistration(self): self.registration_on = False @@ -705,21 +759,26 @@ def StopRegistration(self): self.start_button.SetValue(False) self.start_button.SetLabel(self.START_REGISTRATION_LABEL) - self.pedal_connector.remove_callback('fiducial') + self.pedal_connector.remove_callback("fiducial") def GetFiducialByAttribute(self, fiducials, attribute_name, attribute_value): found = [fiducial for fiducial in fiducials if fiducial[attribute_name] == attribute_value] - assert len(found) != 0, "No fiducial found for which {} = {}".format(attribute_name, attribute_value) + assert len(found) != 0, "No fiducial found for which {} = {}".format( + attribute_name, attribute_value + ) return found[0] def OnSetTrackerFiducial(self, fiducial_name): - fiducial = self.GetFiducialByAttribute(const.TRACKER_FIDUCIALS, 'fiducial_name', fiducial_name) - fiducial_index = fiducial['fiducial_index'] + fiducial = self.GetFiducialByAttribute( + const.TRACKER_FIDUCIALS, + "fiducial_name", + fiducial_name, + ) + fiducial_index = fiducial["fiducial_index"] self.SetTrackerFiducial(fiducial_index) def SetTrackerFiducial(self, fiducial_index): - # XXX: The reference mode is fetched from navigation object, however it seems like not quite # navigation-related attribute here, as the reference mode used during the fiducial registration # is more concerned with the calibration than the navigation. @@ -759,8 +818,8 @@ def OnRegister(self, evt): def ResetICP(self): self.icp.ResetICP() - #self.checkbox_icp.Enable(False) - #self.checkbox_icp.SetValue(False) + # self.checkbox_icp.Enable(False) + # self.checkbox_icp.SetValue(False) def OnReset(self, evt): self.tracker.ResetTrackerFiducials() @@ -774,10 +833,10 @@ def OnResetTrackerFiducials(self): def OnNext(self, evt): Publisher.sendMessage("Move to refine page") - + def OnBack(self, evt): - Publisher.sendMessage('Move to image page') - + Publisher.sendMessage("Move to image page") + def OnPreferences(self, evt): Publisher.sendMessage("Open preferences menu", page=2) @@ -798,7 +857,6 @@ def OnTrackerChanged(self): class RefinePage(wx.Panel): def __init__(self, parent, nav_hub): - wx.Panel.__init__(self, parent) self.icp = nav_hub.icp self.tracker = nav_hub.tracker @@ -807,7 +865,7 @@ def __init__(self, parent, nav_hub): self.numctrls_fiducial = [[], [], [], [], [], []] const_labels = [label for label in const.FIDUCIAL_LABELS] - labels = const_labels + const_labels # duplicate labels for image and tracker + labels = const_labels + const_labels # duplicate labels for image and tracker self.labels = [wx.StaticText(self, -1, _(label)) for label in labels] for m in range(6): @@ -818,8 +876,11 @@ def __init__(self, parent, nav_hub): value = self.tracker.GetTrackerFiducialForUI(m - 3, n) self.numctrls_fiducial[m].append( - wx.lib.masked.numctrl.NumCtrl(parent=self, integerWidth=4, fractionWidth=1, value=value)) - + wx.lib.masked.numctrl.NumCtrl( + parent=self, integerWidth=4, fractionWidth=1, value=value + ) + ) + txt_label_image = wx.StaticText(self, -1, _("Image Fiducials:")) txt_label_image.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) coord_sizer = wx.GridBagSizer(hgap=5, vgap=0) @@ -827,29 +888,29 @@ def __init__(self, parent, nav_hub): for m in range(3): coord_sizer.Add(self.labels[m], pos=wx.GBPosition(m, 0)) for n in range(3): - coord_sizer.Add(self.numctrls_fiducial[m][n], pos=wx.GBPosition(m, n+1)) + coord_sizer.Add(self.numctrls_fiducial[m][n], pos=wx.GBPosition(m, n + 1)) if m in range(6): self.numctrls_fiducial[m][n].SetEditable(False) - + txt_label_track = wx.StaticText(self, -1, _("Tracker Fiducials:")) txt_label_track.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) coord_sizer_track = wx.GridBagSizer(hgap=5, vgap=0) for m in range(3, 6): - coord_sizer_track.Add(self.labels[m], pos=wx.GBPosition(m-3, 0)) + coord_sizer_track.Add(self.labels[m], pos=wx.GBPosition(m - 3, 0)) for n in range(3): - coord_sizer_track.Add(self.numctrls_fiducial[m][n], pos=wx.GBPosition(m-3, n+1)) + coord_sizer_track.Add(self.numctrls_fiducial[m][n], pos=wx.GBPosition(m - 3, n + 1)) if m in range(1, 6): self.numctrls_fiducial[m][n].SetEditable(False) - txt_fre = wx.StaticText(self, -1, _('FRE:')) + txt_fre = wx.StaticText(self, -1, _("FRE:")) tooltip = _("Fiducial registration error") txt_fre.SetToolTip(tooltip) value = self.icp.GetFreForUI() txtctrl_fre = wx.TextCtrl(self, value=value, size=wx.Size(60, -1), style=wx.TE_CENTRE) txtctrl_fre.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) - txtctrl_fre.SetBackgroundColour('WHITE') + txtctrl_fre.SetBackgroundColour("WHITE") txtctrl_fre.SetEditable(0) txtctrl_fre.SetToolTip(tooltip) self.txtctrl_fre = txtctrl_fre @@ -857,12 +918,13 @@ def __init__(self, parent, nav_hub): self.OnUpdateUI() fre_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) - fre_sizer.AddMany([ - (txt_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), - (txtctrl_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL) - - ]) - + fre_sizer.AddMany( + [ + (txt_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), + (txtctrl_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), + ] + ) + back_button = wx.Button(self, label="Back") back_button.Bind(wx.EVT_BUTTON, partial(self.OnBack)) self.back_button = back_button @@ -876,23 +938,27 @@ def __init__(self, parent, nav_hub): self.next_button = next_button button_sizer = wx.BoxSizer(wx.HORIZONTAL) - button_sizer.AddMany([ - (back_button, 0, wx.EXPAND), - (refine_button, 0, wx.EXPAND), - (next_button, 0, wx.EXPAND) - ]) + button_sizer.AddMany( + [ + (back_button, 0, wx.EXPAND), + (refine_button, 0, wx.EXPAND), + (next_button, 0, wx.EXPAND), + ] + ) main_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddMany([ - (txt_label_image, 0, wx.EXPAND | wx.ALL, 10), - (coord_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), - (txt_label_track, 0, wx.EXPAND | wx.ALL, 10), - (coord_sizer_track, 0, wx.ALIGN_CENTER_HORIZONTAL), - (10, 10, 0), - (fre_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), - (button_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 20), - (10, 10, 0) - ]) + main_sizer.AddMany( + [ + (txt_label_image, 0, wx.EXPAND | wx.ALL, 10), + (coord_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), + (txt_label_track, 0, wx.EXPAND | wx.ALL, 10), + (coord_sizer_track, 0, wx.ALIGN_CENTER_HORIZONTAL), + (10, 10, 0), + (fre_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), + (button_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 20), + (10, 10, 0), + ] + ) self.sizer = main_sizer self.SetSizerAndFit(main_sizer) self.__bind_events() @@ -900,7 +966,7 @@ def __init__(self, parent, nav_hub): def __bind_events(self): Publisher.subscribe(self.OnUpdateUI, "Update UI for refine tab") Publisher.subscribe(self.OnResetTrackerFiducials, "Reset tracker fiducials") - + def OnUpdateUI(self): for m in range(6): for n in range(3): @@ -920,7 +986,7 @@ def OnUpdateUI(self): self.txtctrl_fre.SetBackgroundColour(const.GREEN_COLOR_RGB) else: self.txtctrl_fre.SetBackgroundColour(const.RED_COLOR_RGB) - + def OnResetTrackerFiducials(self): for m in range(3): for n in range(3): @@ -928,10 +994,10 @@ def OnResetTrackerFiducials(self): self.numctrls_fiducial[m + 3][n].SetValue(value) def OnBack(self, evt): - Publisher.sendMessage('Move to tracker page') - + Publisher.sendMessage("Move to tracker page") + def OnNext(self, evt): - Publisher.sendMessage('Move to stimulator page') + Publisher.sendMessage("Move to stimulator page") def OnRefine(self, evt): self.icp.RegisterICP(self.navigation, self.tracker) @@ -941,14 +1007,13 @@ def OnRefine(self, evt): class StimulatorPage(wx.Panel): def __init__(self, parent, nav_hub): - wx.Panel.__init__(self, parent) self.navigation = nav_hub.navigation border = wx.FlexGridSizer(2, 3, 5) object_reg = self.navigation.GetObjectRegistration() self.object_reg = object_reg - + lbl = wx.StaticText(self, -1, _("No TMS coil configured!")) lbl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) self.lbl = lbl @@ -962,14 +1027,16 @@ def __init__(self, parent, nav_hub): btn_edit.SetToolTip("Open preferences menu") btn_edit.Bind(wx.EVT_BUTTON, self.OnEditPreferences) - border.AddMany([ - (lbl, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10), - (0, 0), - (config_txt, 1, wx.EXPAND | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 10), - (0, 0), - (lbl_edit, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10), - (btn_edit, 0, wx.EXPAND | wx.ALL | wx.ALIGN_LEFT, 10) - ]) + border.AddMany( + [ + (lbl, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10), + (0, 0), + (config_txt, 1, wx.EXPAND | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 10), + (0, 0), + (lbl_edit, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10), + (btn_edit, 0, wx.EXPAND | wx.ALL | wx.ALIGN_LEFT, 10), + ] + ) next_button = wx.Button(self, label="Proceed to navigation") next_button.Bind(wx.EVT_BUTTON, partial(self.OnNext)) @@ -982,25 +1049,27 @@ def __init__(self, parent, nav_hub): if self.object_reg is not None: self.OnObjectUpdate() - + main_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddMany([ - (border, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 10), - (bottom_sizer, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, 20) - ]) - + main_sizer.AddMany( + [ + (border, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 10), + (bottom_sizer, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, 20), + ] + ) + self.SetSizerAndFit(main_sizer) self.Layout() self.__bind_events() def __bind_events(self): - Publisher.subscribe(self.OnObjectUpdate, 'Update object registration') - Publisher.subscribe(self.OnCloseProject, 'Close project data') - Publisher.subscribe(self.OnCloseProject, 'Remove object data') - + Publisher.subscribe(self.OnObjectUpdate, "Update object registration") + Publisher.subscribe(self.OnCloseProject, "Close project data") + Publisher.subscribe(self.OnCloseProject, "Remove object data") + def OnCloseProject(self): - Publisher.sendMessage('Press track object button', pressed=False) - Publisher.sendMessage('Enable track object button', enabled=False) + Publisher.sendMessage("Press track object button", pressed=False) + Publisher.sendMessage("Enable track object button", enabled=False) def UpdateObjectRegistration(self): self.object_reg = self.navigation.GetObjectRegistration() @@ -1015,12 +1084,13 @@ def OnObjectUpdate(self, data=None): self.lbl.Show() self.config_txt.Show() self.next_button.Enable() - + def OnEditPreferences(self, evt): - Publisher.sendMessage('Open preferences menu', page=3) - + Publisher.sendMessage("Open preferences menu", page=3) + def OnNext(self, evt): - Publisher.sendMessage('Open navigation menu') + Publisher.sendMessage("Open navigation menu") + class NavigationPanel(wx.Panel): def __init__(self, parent, nav_hub): @@ -1040,45 +1110,43 @@ def __init__(self, parent, nav_hub): self.marker_panel = MarkersPanel(self, nav_hub) top_sizer = wx.BoxSizer(wx.HORIZONTAL) - top_sizer.Add(self.marker_panel, 1, wx.GROW | wx.EXPAND ) + top_sizer.Add(self.marker_panel, 1, wx.GROW | wx.EXPAND) bottom_sizer = wx.BoxSizer(wx.HORIZONTAL) bottom_sizer.Add(self.control_panel, 0, wx.EXPAND | wx.TOP, 5) main_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddMany([(top_sizer, 1, wx.EXPAND | wx.GROW), - (bottom_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL) - ]) + main_sizer.AddMany( + [(top_sizer, 1, wx.EXPAND | wx.GROW), (bottom_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)] + ) self.sizer = main_sizer self.SetSizerAndFit(main_sizer) self.Update() def __bind_events(self): - Publisher.subscribe(self.OnCloseProject, 'Close project data') - + Publisher.subscribe(self.OnCloseProject, "Close project data") + def OnCloseProject(self): self.tracker.ResetTrackerFiducials() self.image.ResetImageFiducials() - Publisher.sendMessage('Disconnect tracker') - Publisher.sendMessage('Delete all markers') + Publisher.sendMessage("Disconnect tracker") + Publisher.sendMessage("Delete all markers") Publisher.sendMessage("Update marker offset state", create=False) Publisher.sendMessage("Remove tracts") Publisher.sendMessage("Disable style", style=const.SLICE_STATE_CROSS) # TODO: Reset camera initial focus - Publisher.sendMessage('Reset cam clipping range') + Publisher.sendMessage("Reset cam clipping range") self.navigation.StopNavigation() self.navigation.__init__( - pedal_connector=self.pedal_connector, - neuronavigation_api=self.neuronavigation_api + pedal_connector=self.pedal_connector, neuronavigation_api=self.neuronavigation_api ) self.tracker.__init__() self.icp.__init__() - + class ControlPanel(wx.Panel): def __init__(self, parent, nav_hub): - wx.Panel.__init__(self, parent) self.navigation = nav_hub.navigation @@ -1101,8 +1169,10 @@ def __init__(self, parent, nav_hub): btn_nav.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) btn_nav.SetToolTip(tooltip) self.btn_nav = btn_nav - self.btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnStartNavigationButton, btn_nav=self.btn_nav)) - + self.btn_nav.Bind( + wx.EVT_TOGGLEBUTTON, partial(self.OnStartNavigationButton, btn_nav=self.btn_nav) + ) + # Constants for bitmap parent toggle button ICON_SIZE = (48, 48) RED_COLOR = const.RED_COLOR_RGB @@ -1113,45 +1183,61 @@ def __init__(self, parent, nav_hub): self.GREY_COLOR = GREY_COLOR # Toggle Button for Tractography - tooltip = _(u"Control Tractography") + tooltip = _("Control Tractography") BMP_TRACT = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("tract.png")), wx.BITMAP_TYPE_PNG) - tractography_checkbox = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) + tractography_checkbox = wx.ToggleButton( + self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE + ) tractography_checkbox.SetBackgroundColour(GREY_COLOR) tractography_checkbox.SetBitmap(BMP_TRACT) tractography_checkbox.SetValue(False) tractography_checkbox.Enable(False) tractography_checkbox.SetToolTip(tooltip) - tractography_checkbox.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnTractographyCheckbox, ctrl=tractography_checkbox)) + tractography_checkbox.Bind( + wx.EVT_TOGGLEBUTTON, partial(self.OnTractographyCheckbox, ctrl=tractography_checkbox) + ) self.tractography_checkbox = tractography_checkbox # Toggle button to track the coil - tooltip = _(u"Track coil") + tooltip = _("Track coil") BMP_TRACK = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("coil.png")), wx.BITMAP_TYPE_PNG) - track_object_button = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) + track_object_button = wx.ToggleButton( + self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE + ) track_object_button.SetBackgroundColour(GREY_COLOR) track_object_button.SetBitmap(BMP_TRACK) track_object_button.SetValue(False) if not self.track_obj: track_object_button.Enable(False) track_object_button.SetToolTip(tooltip) - track_object_button.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnTrackObjectButton, ctrl=track_object_button)) + track_object_button.Bind( + wx.EVT_TOGGLEBUTTON, partial(self.OnTrackObjectButton, ctrl=track_object_button) + ) self.track_object_button = track_object_button # Toggle button for allowing triggering only if coil is at target - tooltip = _(u"Allow triggering only if the coil is at the target") - BMP_LOCK = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("lock_to_target.png")), wx.BITMAP_TYPE_PNG) - lock_to_target_button = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) + tooltip = _("Allow triggering only if the coil is at the target") + BMP_LOCK = wx.Bitmap( + str(inv_paths.ICON_DIR.joinpath("lock_to_target.png")), wx.BITMAP_TYPE_PNG + ) + lock_to_target_button = wx.ToggleButton( + self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE + ) lock_to_target_button.SetBackgroundColour(GREY_COLOR) lock_to_target_button.SetBitmap(BMP_LOCK) lock_to_target_button.SetValue(False) lock_to_target_button.Enable(False) - lock_to_target_button.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnLockToTargetButton, ctrl=lock_to_target_button)) + lock_to_target_button.Bind( + wx.EVT_TOGGLEBUTTON, partial(self.OnLockToTargetButton, ctrl=lock_to_target_button) + ) lock_to_target_button.SetToolTip(tooltip) self.lock_to_target_button = lock_to_target_button # Toggle button for showing coil during navigation tooltip = _("Show coil") - BMP_SHOW_COIL = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("coil_eye.png")), wx.BITMAP_TYPE_PNG) + BMP_SHOW_COIL = wx.Bitmap( + str(inv_paths.ICON_DIR.joinpath("coil_eye.png")), wx.BITMAP_TYPE_PNG + ) show_coil_button = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) show_coil_button.SetBackgroundColour(GREY_COLOR) show_coil_button.SetBitmap(BMP_SHOW_COIL) @@ -1164,30 +1250,38 @@ def __init__(self, parent, nav_hub): # Toggle Button to use serial port to trigger pulse signal and create markers tooltip = _("Enable serial port communication to trigger pulse and create markers") BMP_PORT = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("wave.png")), wx.BITMAP_TYPE_PNG) - checkbox_serial_port = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) + checkbox_serial_port = wx.ToggleButton( + self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE + ) checkbox_serial_port.SetBackgroundColour(RED_COLOR) checkbox_serial_port.SetBitmap(BMP_PORT) checkbox_serial_port.SetToolTip(tooltip) checkbox_serial_port.SetValue(False) - checkbox_serial_port.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnEnableSerialPort, ctrl=checkbox_serial_port)) + checkbox_serial_port.Bind( + wx.EVT_TOGGLEBUTTON, partial(self.OnEnableSerialPort, ctrl=checkbox_serial_port) + ) self.checkbox_serial_port = checkbox_serial_port - #Toggle Button for Efield - tooltip = _(u"Control E-Field") + # Toggle Button for Efield + tooltip = _("Control E-Field") BMP_FIELD = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("field.png")), wx.BITMAP_TYPE_PNG) efield_checkbox = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) efield_checkbox.SetBackgroundColour(GREY_COLOR) efield_checkbox.SetBitmap(BMP_FIELD) efield_checkbox.SetValue(False) efield_checkbox.Enable(False) - efield_checkbox.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnEfieldCheckbox, ctrl=efield_checkbox)) + efield_checkbox.Bind( + wx.EVT_TOGGLEBUTTON, partial(self.OnEfieldCheckbox, ctrl=efield_checkbox) + ) efield_checkbox.SetToolTip(tooltip) self.efield_checkbox = efield_checkbox - #Toggle Button for Target Mode - tooltip = _(u"Target mode") + # Toggle Button for Target Mode + tooltip = _("Target mode") BMP_TARGET = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("target.png")), wx.BITMAP_TYPE_PNG) - target_mode_button = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) + target_mode_button = wx.ToggleButton( + self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE + ) target_mode_button.SetBackgroundColour(GREY_COLOR) target_mode_button.SetBitmap(BMP_TARGET) target_mode_button.SetValue(False) @@ -1199,58 +1293,79 @@ def __init__(self, parent, nav_hub): # Toggle button for tracking target with robot during navigation tooltip = _("Track target with robot") - BMP_TRACK_TARGET = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("robot_track_target.png")), wx.BITMAP_TYPE_PNG) - robot_track_target_button = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) + BMP_TRACK_TARGET = wx.Bitmap( + str(inv_paths.ICON_DIR.joinpath("robot_track_target.png")), wx.BITMAP_TYPE_PNG + ) + robot_track_target_button = wx.ToggleButton( + self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE + ) robot_track_target_button.SetBackgroundColour(GREY_COLOR) robot_track_target_button.SetBitmap(BMP_TRACK_TARGET) robot_track_target_button.SetToolTip(tooltip) robot_track_target_button.SetValue(False) robot_track_target_button.Enable(False) - robot_track_target_button.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnRobotTrackTargetButton, ctrl=robot_track_target_button)) + robot_track_target_button.Bind( + wx.EVT_TOGGLEBUTTON, + partial(self.OnRobotTrackTargetButton, ctrl=robot_track_target_button), + ) self.robot_track_target_button = robot_track_target_button # Toggle button for moving robot away from head tooltip = _("Move robot away from head") - BMP_ENABLE_MOVE_AWAY = wx.Bitmap(str(inv_paths.ICON_DIR.joinpath("robot_move_away.png")), wx.BITMAP_TYPE_PNG) - robot_move_away_button = wx.ToggleButton(self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE) + BMP_ENABLE_MOVE_AWAY = wx.Bitmap( + str(inv_paths.ICON_DIR.joinpath("robot_move_away.png")), wx.BITMAP_TYPE_PNG + ) + robot_move_away_button = wx.ToggleButton( + self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE + ) robot_move_away_button.SetBackgroundColour(GREY_COLOR) robot_move_away_button.SetBitmap(BMP_ENABLE_MOVE_AWAY) robot_move_away_button.SetToolTip(tooltip) robot_move_away_button.SetValue(False) robot_move_away_button.Enable(False) - robot_move_away_button.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnRobotMoveAwayButton, ctrl=robot_move_away_button)) + robot_move_away_button.Bind( + wx.EVT_TOGGLEBUTTON, partial(self.OnRobotMoveAwayButton, ctrl=robot_move_away_button) + ) self.robot_move_away_button = robot_move_away_button # Sizers start_navigation_button_sizer = wx.BoxSizer(wx.VERTICAL) - start_navigation_button_sizer.AddMany([ - (btn_nav, 0, wx.EXPAND | wx.GROW), - ]) + start_navigation_button_sizer.AddMany( + [ + (btn_nav, 0, wx.EXPAND | wx.GROW), + ] + ) navigation_buttons_sizer = wx.FlexGridSizer(4, 5, 5) - navigation_buttons_sizer.AddMany([ - (tractography_checkbox), - (target_mode_button), - (track_object_button), - (checkbox_serial_port), - (efield_checkbox), - (lock_to_target_button), - (show_coil_button), - ]) + navigation_buttons_sizer.AddMany( + [ + (tractography_checkbox), + (target_mode_button), + (track_object_button), + (checkbox_serial_port), + (efield_checkbox), + (lock_to_target_button), + (show_coil_button), + ] + ) robot_buttons_sizer = wx.FlexGridSizer(2, 5, 5) - robot_buttons_sizer.AddMany([ - (robot_track_target_button), - (robot_move_away_button), - ]) + robot_buttons_sizer.AddMany( + [ + (robot_track_target_button), + (robot_move_away_button), + ] + ) main_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddMany([ - (start_navigation_button_sizer, 0, wx.EXPAND | wx.ALL, 10), - (navigation_buttons_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.BOTTOM, 10), - (robot_buttons_sizer, 0, wx.ALIGN_LEFT | wx.TOP | wx.BOTTOM, 5) - ]) + main_sizer.AddMany( + [ + (start_navigation_button_sizer, 0, wx.EXPAND | wx.ALL, 10), + (navigation_buttons_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.BOTTOM, 10), + (robot_buttons_sizer, 0, wx.ALIGN_LEFT | wx.TOP | wx.BOTTOM, 5), + ] + ) self.sizer = main_sizer self.SetSizerAndFit(main_sizer) @@ -1260,74 +1375,74 @@ def __init__(self, parent, nav_hub): self.LoadConfig() def __bind_events(self): - Publisher.subscribe(self.OnStartNavigation, 'Start navigation') - Publisher.subscribe(self.OnStopNavigation, 'Stop navigation') - Publisher.subscribe(self.OnCheckStatus, 'Navigation status') - Publisher.subscribe(self.SetTarget, 'Set target') - Publisher.subscribe(self.UnsetTarget, 'Unset target') - Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status') + Publisher.subscribe(self.OnStartNavigation, "Start navigation") + Publisher.subscribe(self.OnStopNavigation, "Stop navigation") + Publisher.subscribe(self.OnCheckStatus, "Navigation status") + Publisher.subscribe(self.SetTarget, "Set target") + Publisher.subscribe(self.UnsetTarget, "Unset target") + Publisher.subscribe(self.UpdateNavigationStatus, "Navigation status") Publisher.subscribe(self.OnRobotStatus, "Robot to Neuronavigation: Robot connection status") - Publisher.subscribe(self.SetTargetMode, 'Set target mode') + Publisher.subscribe(self.SetTargetMode, "Set target mode") - Publisher.subscribe(self.UpdateTractsVisualization, 'Update tracts visualization') + Publisher.subscribe(self.UpdateTractsVisualization, "Update tracts visualization") # Externally press/unpress and enable/disable buttons. - Publisher.subscribe(self.PressShowCoilButton, 'Press show-coil button') - Publisher.subscribe(self.EnableShowCoilButton, 'Enable show-coil button') + Publisher.subscribe(self.PressShowCoilButton, "Press show-coil button") + Publisher.subscribe(self.EnableShowCoilButton, "Enable show-coil button") - Publisher.subscribe(self.PressTrackObjectButton, 'Press track object button') - Publisher.subscribe(self.EnableTrackObjectButton, 'Enable track object button') + Publisher.subscribe(self.PressTrackObjectButton, "Press track object button") + Publisher.subscribe(self.EnableTrackObjectButton, "Enable track object button") - Publisher.subscribe(self.PressRobotTrackTargetButton, 'Press robot button') - Publisher.subscribe(self.EnableRobotTrackTargetButton, 'Enable robot button') + Publisher.subscribe(self.PressRobotTrackTargetButton, "Press robot button") + Publisher.subscribe(self.EnableRobotTrackTargetButton, "Enable robot button") - Publisher.subscribe(self.PressRobotMoveAwayButton, 'Press move away button') - Publisher.subscribe(self.EnableRobotMoveAwayButton, 'Enable move away button') + Publisher.subscribe(self.PressRobotMoveAwayButton, "Press move away button") + Publisher.subscribe(self.EnableRobotMoveAwayButton, "Enable move away button") - Publisher.subscribe(self.ShowTargetButton, 'Show target button') - Publisher.subscribe(self.HideTargetButton, 'Hide target button') - Publisher.subscribe(self.PressTargetModeButton, 'Press target mode button') + Publisher.subscribe(self.ShowTargetButton, "Show target button") + Publisher.subscribe(self.HideTargetButton, "Hide target button") + Publisher.subscribe(self.PressTargetModeButton, "Press target mode button") # Conditions for enabling 'target mode' button: - Publisher.subscribe(self.TrackObject, 'Track object') - - #Tractography - Publisher.subscribe(self.UpdateTrekkerObject, 'Update Trekker object') - Publisher.subscribe(self.UpdateNumTracts, 'Update number of tracts') - Publisher.subscribe(self.UpdateSeedOffset, 'Update seed offset') - Publisher.subscribe(self.UpdateSeedRadius, 'Update seed radius') - Publisher.subscribe(self.UpdateNumberThreads, 'Update number of threads') - Publisher.subscribe(self.UpdateTractsVisualization, 'Update tracts visualization') - Publisher.subscribe(self.UpdatePeelVisualization, 'Update peel visualization') - Publisher.subscribe(self.UpdateEfieldVisualization, 'Update e-field visualization') - Publisher.subscribe(self.EnableACT, 'Enable ACT') - Publisher.subscribe(self.UpdateACTData, 'Update ACT data') - - # Config + Publisher.subscribe(self.TrackObject, "Track object") + + # Tractography + Publisher.subscribe(self.UpdateTrekkerObject, "Update Trekker object") + Publisher.subscribe(self.UpdateNumTracts, "Update number of tracts") + Publisher.subscribe(self.UpdateSeedOffset, "Update seed offset") + Publisher.subscribe(self.UpdateSeedRadius, "Update seed radius") + Publisher.subscribe(self.UpdateNumberThreads, "Update number of threads") + Publisher.subscribe(self.UpdateTractsVisualization, "Update tracts visualization") + Publisher.subscribe(self.UpdatePeelVisualization, "Update peel visualization") + Publisher.subscribe(self.UpdateEfieldVisualization, "Update e-field visualization") + Publisher.subscribe(self.EnableACT, "Enable ACT") + Publisher.subscribe(self.UpdateACTData, "Update ACT data") + + # Config def SaveConfig(self): track_object = self.track_object_button state = { - 'track_object': { - 'checked': track_object.GetValue(), - 'enabled': track_object.IsEnabled(), + "track_object": { + "checked": track_object.GetValue(), + "enabled": track_object.IsEnabled(), } } session = ses.Session() - session.SetConfig('object_registration_panel', state) + session.SetConfig("object_registration_panel", state) def LoadConfig(self): session = ses.Session() - state = session.GetConfig('object_registration_panel') + state = session.GetConfig("object_registration_panel") if state is None: return - track_object = state['track_object'] + track_object = state["track_object"] - self.EnableTrackObjectButton(track_object['enabled']) - self.PressTrackObjectButton(track_object['checked']) + self.EnableTrackObjectButton(track_object["enabled"]) + self.PressTrackObjectButton(track_object["checked"]) # Toggle Button Helpers def UpdateToggleButton(self, ctrl, state=None): @@ -1352,13 +1467,13 @@ def EnableToggleButton(self, ctrl, state): ctrl.Enable(state) ctrl.SetBackgroundColour(self.GREY_COLOR) - # Navigation + # Navigation def OnStartNavigation(self): if not self.tracker.AreTrackerFiducialsSet() or not self.image.AreImageFiducialsSet(): wx.MessageBox(_("Invalid fiducials, select all coordinates."), _("InVesalius 3")) elif not self.tracker.IsTrackerInitialized(): - dlg.ShowNavigationTrackerWarning(0, 'choose') + dlg.ShowNavigationTrackerWarning(0, "choose") errors = True else: @@ -1375,7 +1490,7 @@ def OnStartNavigation(self): def OnStartNavigationButton(self, evt, btn_nav): nav_id = btn_nav.GetValue() if not nav_id: - wx.CallAfter(Publisher.sendMessage, 'Stop navigation') + wx.CallAfter(Publisher.sendMessage, "Stop navigation") tooltip = _("Start neuronavigation") btn_nav.SetToolTip(tooltip) btn_nav.SetLabelText(_("Start neuronavigation")) @@ -1438,7 +1553,9 @@ def OnCheckStatus(self, nav_status, vis_status): # Enable/Disable track-object checkbox if navigation is off/on and object registration is valid. obj_registration = self.navigation.GetObjectRegistration() - enable_track_object = obj_registration is not None and obj_registration[0] is not None and not nav_status + enable_track_object = ( + obj_registration is not None and obj_registration[0] is not None and not nav_status + ) self.EnableTrackObjectButton(enable_track_object) # Robot @@ -1453,9 +1570,14 @@ def UpdateRobotButtons(self): # - Target is set # - Target mode is on # - Robot is connected - track_target_button_enabled = self.nav_status and self.target_selected and self.target_mode and self.robot.IsConnected() + track_target_button_enabled = ( + self.nav_status + and self.target_selected + and self.target_mode + and self.robot.IsConnected() + ) self.EnableRobotTrackTargetButton(enabled=track_target_button_enabled) - + # Enable 'move away' robot button if robot is connected. move_away_button_enabled = self.robot.IsConnected() self.EnableRobotMoveAwayButton(enabled=move_away_button_enabled) @@ -1474,9 +1596,9 @@ def SetTargetMode(self, enabled=False): def OnTractographyCheckbox(self, evt, ctrl): self.view_tracts = ctrl.GetValue() self.UpdateToggleButton(ctrl) - Publisher.sendMessage('Update tracts visualization', data=self.view_tracts) + Publisher.sendMessage("Update tracts visualization", data=self.view_tracts) if not self.view_tracts: - Publisher.sendMessage('Remove tracts') + Publisher.sendMessage("Remove tracts") Publisher.sendMessage("Update marker offset state", create=False) def UpdateTractsVisualization(self, data): @@ -1527,13 +1649,15 @@ def OnTrackObjectButton(self, evt=None, ctrl=None): if ctrl is not None: self.UpdateToggleButton(ctrl) pressed = self.track_object_button.GetValue() - Publisher.sendMessage('Track object', enabled=pressed) + Publisher.sendMessage("Track object", enabled=pressed) + if not pressed: + Publisher.sendMessage("Press target mode button", pressed=pressed) # Disable or enable 'Show coil' button, based on if 'Track object' button is pressed. - Publisher.sendMessage('Enable show-coil button', enabled=pressed) + Publisher.sendMessage("Enable show-coil button", enabled=pressed) # Also, automatically press or unpress 'Show coil' button. - Publisher.sendMessage('Press show-coil button', pressed=pressed) + Publisher.sendMessage("Press show-coil button", pressed=pressed) self.SaveConfig() @@ -1555,13 +1679,14 @@ def EnableShowCoilButton(self, enabled=False): def OnShowCoil(self, evt=None): self.UpdateToggleButton(self.show_coil_button) pressed = self.show_coil_button.GetValue() - Publisher.sendMessage('Show coil in viewer volume', state=pressed) - + Publisher.sendMessage("Show coil in viewer volume", state=pressed) + # 'Serial Port Com' def OnEnableSerialPort(self, evt, ctrl): self.UpdateToggleButton(ctrl) if ctrl.GetValue(): from wx import ID_OK + dlg_port = dlg.SetCOMPort(select_baud_rate=False) if dlg_port.ShowModal() != ID_OK: @@ -1571,9 +1696,14 @@ def OnEnableSerialPort(self, evt, ctrl): com_port = dlg_port.GetCOMPort() baud_rate = 115200 - Publisher.sendMessage('Update serial port', serial_port_in_use=True, com_port=com_port, baud_rate=baud_rate) + Publisher.sendMessage( + "Update serial port", + serial_port_in_use=True, + com_port=com_port, + baud_rate=baud_rate, + ) else: - Publisher.sendMessage('Update serial port', serial_port_in_use=False) + Publisher.sendMessage("Update serial port", serial_port_in_use=False) # 'E Field' def OnEfieldCheckbox(self, evt, ctrl): @@ -1607,7 +1737,7 @@ def OnTargetButton(self, evt=None): pressed = self.target_mode_button.GetValue() self.UpdateToggleButton(self.target_mode_button, pressed) - Publisher.sendMessage('Set target mode', enabled=pressed) + Publisher.sendMessage("Set target mode", enabled=pressed) if pressed: # Set robot objective to NONE when target mode is enabled. self.robot.SetObjective(RobotObjective.NONE) @@ -1685,7 +1815,7 @@ def __init__(self, parent, nav_hub): self.cortex_position_orientation = [None, None, None, None, None, None] self.nav_status = False self.efield_data_saved = False - self.efield_target_idx = None + self.efield_target_idx = None self.marker_colour = const.MARKER_COLOUR self.marker_size = const.MARKER_SIZE @@ -1708,90 +1838,99 @@ def __init__(self, parent, nav_hub): spin_session.Bind(wx.EVT_SPINCTRL, partial(self.OnSessionChanged, ctrl=spin_session)) # Marker colour select - select_colour = csel.ColourSelect(self, -1, colour=[255*s for s in self.marker_colour], size=wx.Size(20, 23)) + select_colour = csel.ColourSelect( + self, -1, colour=[255 * s for s in self.marker_colour], size=wx.Size(20, 23) + ) select_colour.SetToolTip("Set colour") select_colour.Bind(csel.EVT_COLOURSELECT, partial(self.OnSelectColour, ctrl=select_colour)) - btn_create = wx.Button(self, -1, label=_('Create marker'), size=wx.Size(135, 23)) + btn_create = wx.Button(self, -1, label=_("Create marker"), size=wx.Size(135, 23)) btn_create.Bind(wx.EVT_BUTTON, self.OnCreateMarker) sizer_create = wx.FlexGridSizer(rows=1, cols=3, hgap=5, vgap=5) - sizer_create.AddMany([(spin_session, 1), - (select_colour, 0), - (btn_create, 0)]) + sizer_create.AddMany([(spin_session, 1), (select_colour, 0), (btn_create, 0)]) # Buttons to save and load markers and to change its visibility as well - btn_save = wx.Button(self, -1, label=_('Save'), size=wx.Size(65, 23)) + btn_save = wx.Button(self, -1, label=_("Save"), size=wx.Size(65, 23)) btn_save.Bind(wx.EVT_BUTTON, self.OnSaveMarkers) - btn_load = wx.Button(self, -1, label=_('Load'), size=wx.Size(65, 23)) + btn_load = wx.Button(self, -1, label=_("Load"), size=wx.Size(65, 23)) btn_load.Bind(wx.EVT_BUTTON, self.OnLoadMarkers) btn_show_hide_all = wx.ToggleButton(self, -1, _("Hide all"), size=wx.Size(65, 23)) - btn_show_hide_all.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnShowHideAllMarkers, ctrl=btn_show_hide_all)) + btn_show_hide_all.Bind( + wx.EVT_TOGGLEBUTTON, partial(self.OnShowHideAllMarkers, ctrl=btn_show_hide_all) + ) sizer_btns = wx.FlexGridSizer(rows=1, cols=3, hgap=5, vgap=5) - sizer_btns.AddMany([(btn_save, 1, wx.RIGHT), - (btn_load, 0, wx.LEFT | wx.RIGHT), - (btn_show_hide_all, 0, wx.LEFT)]) + sizer_btns.AddMany( + [ + (btn_save, 1, wx.RIGHT), + (btn_load, 0, wx.LEFT | wx.RIGHT), + (btn_show_hide_all, 0, wx.LEFT), + ] + ) # Buttons to delete markers - btn_delete_single = wx.Button(self, -1, label=_('Delete'), size=wx.Size(65, 23)) + btn_delete_single = wx.Button(self, -1, label=_("Delete"), size=wx.Size(65, 23)) btn_delete_single.Bind(wx.EVT_BUTTON, self.OnDeleteSelectedMarkers) - btn_delete_all = wx.Button(self, -1, label=_('Delete all'), size=wx.Size(135, 23)) + btn_delete_all = wx.Button(self, -1, label=_("Delete all"), size=wx.Size(135, 23)) btn_delete_all.Bind(wx.EVT_BUTTON, self.OnDeleteAllMarkers) sizer_delete = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) - sizer_delete.AddMany([(btn_delete_single, 1, wx.RIGHT), - (btn_delete_all, 0, wx.LEFT)]) + sizer_delete.AddMany([(btn_delete_single, 1, wx.RIGHT), (btn_delete_all, 0, wx.LEFT)]) screen_width, screen_height = wx.DisplaySize() # The marker list height is set to 120 pixels (accommodating 4 markers) if the screen height is # at most 1080 pixels (a commonly used height in laptops). Otherwise, the height grows linearly with # the screen height. - marker_list_height = max(120, int(screen_height/4)) + marker_list_height = max(120, int(screen_height / 4)) - marker_list_ctrl = wx.ListCtrl(self, -1, style=wx.LC_REPORT, size=wx.Size(0, marker_list_height)) - marker_list_ctrl.InsertColumn(const.ID_COLUMN, '#') + marker_list_ctrl = wx.ListCtrl( + self, -1, style=wx.LC_REPORT, size=wx.Size(0, marker_list_height) + ) + marker_list_ctrl.InsertColumn(const.ID_COLUMN, "#") marker_list_ctrl.SetColumnWidth(const.ID_COLUMN, 24) - marker_list_ctrl.InsertColumn(const.SESSION_COLUMN, 'Session') + marker_list_ctrl.InsertColumn(const.SESSION_COLUMN, "Session") marker_list_ctrl.SetColumnWidth(const.SESSION_COLUMN, 51) - marker_list_ctrl.InsertColumn(const.MARKER_TYPE_COLUMN, 'Type') + marker_list_ctrl.InsertColumn(const.MARKER_TYPE_COLUMN, "Type") marker_list_ctrl.SetColumnWidth(const.MARKER_TYPE_COLUMN, 77) - marker_list_ctrl.InsertColumn(const.LABEL_COLUMN, 'Label') + marker_list_ctrl.InsertColumn(const.LABEL_COLUMN, "Label") marker_list_ctrl.SetColumnWidth(const.LABEL_COLUMN, 95) - marker_list_ctrl.InsertColumn(const.TARGET_COLUMN, 'Target') + marker_list_ctrl.InsertColumn(const.TARGET_COLUMN, "Target") marker_list_ctrl.SetColumnWidth(const.TARGET_COLUMN, 45) - marker_list_ctrl.InsertColumn(const.Z_OFFSET_COLUMN, 'Z-offset') + marker_list_ctrl.InsertColumn(const.Z_OFFSET_COLUMN, "Z-offset") marker_list_ctrl.SetColumnWidth(const.Z_OFFSET_COLUMN, 45) - marker_list_ctrl.InsertColumn(const.POINT_OF_INTEREST_TARGET_COLUMN, 'Efield Target') - marker_list_ctrl.SetColumnWidth(const.POINT_OF_INTEREST_TARGET_COLUMN,45) + marker_list_ctrl.InsertColumn(const.POINT_OF_INTEREST_TARGET_COLUMN, "Efield Target") + marker_list_ctrl.SetColumnWidth(const.POINT_OF_INTEREST_TARGET_COLUMN, 45) - if self.session.GetConfig('debug'): - marker_list_ctrl.InsertColumn(const.X_COLUMN, 'X') + if self.session.GetConfig("debug"): + marker_list_ctrl.InsertColumn(const.X_COLUMN, "X") marker_list_ctrl.SetColumnWidth(const.X_COLUMN, 45) - marker_list_ctrl.InsertColumn(const.Y_COLUMN, 'Y') + marker_list_ctrl.InsertColumn(const.Y_COLUMN, "Y") marker_list_ctrl.SetColumnWidth(const.Y_COLUMN, 45) - marker_list_ctrl.InsertColumn(const.Z_COLUMN, 'Z') + marker_list_ctrl.InsertColumn(const.Z_COLUMN, "Z") marker_list_ctrl.SetColumnWidth(const.Z_COLUMN, 45) marker_list_ctrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnMouseRightDown) marker_list_ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnMarkerFocused) marker_list_ctrl.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnMarkerUnfocused) marker_list_ctrl.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.SetCameraToFocusOnMarker) - + self.marker_list_ctrl = marker_list_ctrl - self.column_sorter = ColumnSorterMixin.__init__(self, self.marker_list_ctrl.GetColumnCount()) + self.column_sorter = ColumnSorterMixin.__init__( + self, self.marker_list_ctrl.GetColumnCount() + ) # In the future, it would be better if the panel could initialize itself based on markers in MarkersControl self.markers.LoadState() @@ -1817,39 +1956,39 @@ def OnSortOrderChanged(self): self.marker_list_ctrl.ShowSortIndicator(column, ascending) def __bind_events(self): - Publisher.subscribe(self.UpdateCurrentCoord, 'Set cross focal point') + Publisher.subscribe(self.UpdateCurrentCoord, "Set cross focal point") # Called when selecting a marker in the volume viewer. - Publisher.subscribe(self.OnSelectMarkerByActor, 'Select marker by actor') - - Publisher.subscribe(self.OnDeleteFiducialMarker, 'Delete fiducial marker') - Publisher.subscribe(self.OnDeleteSelectedMarkers, 'Delete selected markers') - Publisher.subscribe(self.OnDeleteAllMarkers, 'Delete all markers') - Publisher.subscribe(self.OnCreateMarker, 'Create marker') - Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status') - Publisher.subscribe(self.UpdateSeedCoordinates, 'Update tracts') - Publisher.subscribe(self.OnChangeCurrentSession, 'Current session changed') - Publisher.subscribe(self.UpdateMarker, 'Update marker') - Publisher.subscribe(self.UpdateMarkerOrientation, 'Open marker orientation dialog') - Publisher.subscribe(self.AddPeeledSurface, 'Update peel') - Publisher.subscribe(self.GetEfieldDataStatus, 'Get status of Efield saved data') - Publisher.subscribe(self.GetIdList, 'Get ID list') - Publisher.subscribe(self.GetRotationPosition, 'Send coil position and rotation') - Publisher.subscribe(self.CreateMarkerEfield, 'Create Marker from tangential') - Publisher.subscribe(self.UpdateCortexMarker, 'Update Cortex Marker') + Publisher.subscribe(self.OnSelectMarkerByActor, "Select marker by actor") + + Publisher.subscribe(self.OnDeleteFiducialMarker, "Delete fiducial marker") + Publisher.subscribe(self.OnDeleteSelectedMarkers, "Delete selected markers") + Publisher.subscribe(self.OnDeleteAllMarkers, "Delete all markers") + Publisher.subscribe(self.OnCreateMarker, "Create marker") + Publisher.subscribe(self.UpdateNavigationStatus, "Navigation status") + Publisher.subscribe(self.UpdateSeedCoordinates, "Update tracts") + Publisher.subscribe(self.OnChangeCurrentSession, "Current session changed") + Publisher.subscribe(self.UpdateMarker, "Update marker") + Publisher.subscribe(self.UpdateMarkerOrientation, "Open marker orientation dialog") + Publisher.subscribe(self.AddPeeledSurface, "Update peel") + Publisher.subscribe(self.GetEfieldDataStatus, "Get status of Efield saved data") + Publisher.subscribe(self.GetIdList, "Get ID list") + Publisher.subscribe(self.GetRotationPosition, "Send coil position and rotation") + Publisher.subscribe(self.CreateMarkerEfield, "Create Marker from tangential") + Publisher.subscribe(self.UpdateCortexMarker, "Update Cortex Marker") # Update marker_list_ctrl - Publisher.subscribe(self._AddMarker, 'Add marker') - Publisher.subscribe(self._DeleteMarker, 'Delete marker') - Publisher.subscribe(self._DeleteMultiple, 'Delete markers') - Publisher.subscribe(self._SetPointOfInterest, 'Set point of interest') - Publisher.subscribe(self._SetTarget, 'Set target') - Publisher.subscribe(self._UnsetTarget, 'Unset target') - Publisher.subscribe(self._UnsetPointOfInterest, 'Unset point of interest') - Publisher.subscribe(self._UpdateMarkerLabel, 'Update marker label') + Publisher.subscribe(self._AddMarker, "Add marker") + Publisher.subscribe(self._DeleteMarker, "Delete marker") + Publisher.subscribe(self._DeleteMultiple, "Delete markers") + Publisher.subscribe(self._SetPointOfInterest, "Set point of interest") + Publisher.subscribe(self._SetTarget, "Set target") + Publisher.subscribe(self._UnsetTarget, "Unset target") + Publisher.subscribe(self._UnsetPointOfInterest, "Unset point of interest") + Publisher.subscribe(self._UpdateMarkerLabel, "Update marker label") def __get_selected_items(self): - """ + """ Returns a (possibly empty) list of the selected items in the list control. """ selection = [] @@ -1879,7 +2018,6 @@ def _DeleteMarker(self, marker): if current_uuid == deleted_marker_uuid: self.itemDataMap.pop(key) - num_items = self.marker_list_ctrl.GetItemCount() for n in range(num_items): m_id = self.__get_marker_id(n) @@ -1930,7 +2068,7 @@ def _DeleteMultiple(self, markers): def _SetPointOfInterest(self, marker): idx = self.__find_marker_index(marker.marker_id) - self.marker_list_ctrl.SetItemBackgroundColour(idx, 'PURPLE') + self.marker_list_ctrl.SetItemBackgroundColour(idx, "PURPLE") self.marker_list_ctrl.SetItem(idx, const.POINT_OF_INTEREST_TARGET_COLUMN, _("Yes")) uuid = marker.marker_uuid @@ -1943,7 +2081,7 @@ def _SetPointOfInterest(self, marker): def _UnsetPointOfInterest(self, marker): idx = self.__find_marker_index(marker.marker_id) - self.marker_list_ctrl.SetItemBackgroundColour(idx, 'white') + self.marker_list_ctrl.SetItemBackgroundColour(idx, "white") self.marker_list_ctrl.SetItem(idx, const.POINT_OF_INTEREST_TARGET_COLUMN, "") uuid = marker.marker_uuid @@ -1967,7 +2105,9 @@ def _UpdateMarkerLabel(self, marker): @staticmethod def __list_fiducial_labels(): """Return the list of marker labels denoting fiducials.""" - return list(itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS))) + return list( + itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS)) + ) def UpdateCurrentCoord(self, position): self.current_position = list(position[:3]) @@ -1982,11 +2122,13 @@ def UpdateNavigationStatus(self, nav_status, vis_status): else: self.nav_status = True - def UpdateSeedCoordinates(self, root=None, affine_vtk=None, coord_offset=(0, 0, 0), coord_offset_w=(0, 0, 0)): + def UpdateSeedCoordinates( + self, root=None, affine_vtk=None, coord_offset=(0, 0, 0), coord_offset_w=(0, 0, 0) + ): self.current_seed = coord_offset_w def UpdateCortexMarker(self, CoGposition, CoGorientation): - self.cortex_position_orientation = CoGposition + CoGorientation + self.cortex_position_orientation = CoGposition + CoGorientation def OnMouseRightDown(self, evt): focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() @@ -2012,18 +2154,20 @@ def OnMouseRightDown(self, evt): # Create the context menu. menu_id = wx.Menu() - edit_id = menu_id.Append(unique_menu_id, _('Change label')) # Use non-zero ID + edit_id = menu_id.Append(unique_menu_id, _("Change label")) # Use non-zero ID menu_id.Bind(wx.EVT_MENU, self.ChangeLabel, edit_id) - color_id = menu_id.Append(unique_menu_id + 1, _('Change color')) # Increment the unique_menu_id + color_id = menu_id.Append( + unique_menu_id + 1, _("Change color") + ) # Increment the unique_menu_id menu_id.Bind(wx.EVT_MENU, self.ChangeColor, color_id) - delete_id = menu_id.Append(unique_menu_id + 2, _('Delete')) + delete_id = menu_id.Append(unique_menu_id + 2, _("Delete")) menu_id.Bind(wx.EVT_MENU, self.OnDeleteSelectedMarkers, delete_id) # Allow duplicate only for markers that are not fiducials. if not is_fiducial: - duplicate_menu_item = menu_id.Append(unique_menu_id + 3, _('Duplicate')) + duplicate_menu_item = menu_id.Append(unique_menu_id + 3, _("Duplicate")) menu_id.Bind(wx.EVT_MENU, self.OnMenuDuplicateMarker, duplicate_menu_item) menu_id.AppendSeparator() @@ -2031,48 +2175,68 @@ def OnMouseRightDown(self, evt): # Show 'Set as target'/'Unset target' menu item only if the marker is a coil target. if is_coil_target: if is_active_target: - target_menu_item = menu_id.Append(unique_menu_id + 4, _('Unset target')) + target_menu_item = menu_id.Append(unique_menu_id + 4, _("Unset target")) menu_id.Bind(wx.EVT_MENU, self.OnMenuUnsetTarget, target_menu_item) if has_mTMS: - brain_target_menu_item= menu_id.Append(unique_menu_id + 4, _('Set brain target')) + brain_target_menu_item = menu_id.Append( + unique_menu_id + 4, _("Set brain target") + ) menu_id.Bind(wx.EVT_MENU, self.OnSetBrainTarget, brain_target_menu_item) else: - target_menu_item = menu_id.Append(unique_menu_id + 4, _('Set as target')) + target_menu_item = menu_id.Append(unique_menu_id + 4, _("Set as target")) menu_id.Bind(wx.EVT_MENU, self.OnMenuSetTarget, target_menu_item) # Show 'Create coil target' menu item if the marker is a coil pose. if is_coil_pose: # 'Create coil target' menu item. - create_coil_target_menu_item = menu_id.Append(unique_menu_id + 6, _('Create coil target')) - menu_id.Bind(wx.EVT_MENU, self.OnCreateCoilTargetFromCoilPose, create_coil_target_menu_item) + create_coil_target_menu_item = menu_id.Append( + unique_menu_id + 6, _("Create coil target") + ) + menu_id.Bind( + wx.EVT_MENU, self.OnCreateCoilTargetFromCoilPose, create_coil_target_menu_item + ) # Show 'Create brain target' and 'Create coil target' menu items only if the marker is a landmark. if is_landmark: # 'Create brain target' menu item. - create_brain_target_menu_item = menu_id.Append(unique_menu_id + 5, _('Create brain target')) - menu_id.Bind(wx.EVT_MENU, self.OnCreateBrainTargetFromLandmark, create_brain_target_menu_item) + create_brain_target_menu_item = menu_id.Append( + unique_menu_id + 5, _("Create brain target") + ) + menu_id.Bind( + wx.EVT_MENU, self.OnCreateBrainTargetFromLandmark, create_brain_target_menu_item + ) # 'Create coil target' menu item. - create_coil_target_menu_item = menu_id.Append(unique_menu_id + 6, _('Create coil target')) - menu_id.Bind(wx.EVT_MENU, self.OnCreateCoilTargetFromLandmark, create_coil_target_menu_item) + create_coil_target_menu_item = menu_id.Append( + unique_menu_id + 6, _("Create coil target") + ) + menu_id.Bind( + wx.EVT_MENU, self.OnCreateCoilTargetFromLandmark, create_coil_target_menu_item + ) is_brain_target = focused_marker.marker_type == MarkerType.BRAIN_TARGET if is_brain_target and has_mTMS: - send_brain_target_menu_item = menu_id.Append(unique_menu_id + 7, _('Send brain target to mTMS')) + send_brain_target_menu_item = menu_id.Append( + unique_menu_id + 7, _("Send brain target to mTMS") + ) menu_id.Bind(wx.EVT_MENU, self.OnSendBrainTarget, send_brain_target_menu_item) if self.nav_status and self.navigation.e_field_loaded: - #Publisher.sendMessage('Check efield data') - #if not tuple(np.argwhere(self.indexes_saved_lists == self.marker_list_ctrl.GetFocusedItem())): + # Publisher.sendMessage('Check efield data') + # if not tuple(np.argwhere(self.indexes_saved_lists == self.marker_list_ctrl.GetFocusedItem())): if is_active_target: - efield_menu_item = menu_id.Append(unique_menu_id + 8, _('Save Efield target Data')) + efield_menu_item = menu_id.Append(unique_menu_id + 8, _("Save Efield target Data")) menu_id.Bind(wx.EVT_MENU, self.OnMenuSaveEfieldTargetData, efield_menu_item) if self.navigation.e_field_loaded: - efield_target_menu_item = menu_id.Append(unique_menu_id + 9, _('Set as Efield target 1 (origin)')) + efield_target_menu_item = menu_id.Append( + unique_menu_id + 9, _("Set as Efield target 1 (origin)") + ) menu_id.Bind(wx.EVT_MENU, self.OnMenuSetEfieldTarget, efield_target_menu_item) - efield_target_menu_item = menu_id.Append(unique_menu_id + 10, _('Set as Efield target 2')) + efield_target_menu_item = menu_id.Append( + unique_menu_id + 10, _("Set as Efield target 2") + ) menu_id.Bind(wx.EVT_MENU, self.OnMenuSetEfieldTarget2, efield_target_menu_item) # Publisher.sendMessage('Check efield data') # if self.efield_data_saved: @@ -2086,15 +2250,23 @@ def OnMouseRightDown(self, evt): if self.navigation.e_field_loaded and not self.nav_status: if is_active_target: - efield_vector_plot_menu_item = menu_id.Append(unique_menu_id + 11,_('Show vector field')) + efield_vector_plot_menu_item = menu_id.Append( + unique_menu_id + 11, _("Show vector field") + ) menu_id.Bind(wx.EVT_MENU, self.OnMenuShowVectorField, efield_vector_plot_menu_item) if self.navigation.e_field_loaded: if focused_marker.is_point_of_interest: - create_efield_target = menu_id.Append(unique_menu_id + 12, _('Remove Efield Cortex target')) - menu_id.Bind(wx.EVT_MENU, self.OnMenuRemoveEfieldTargetatCortex, create_efield_target) + create_efield_target = menu_id.Append( + unique_menu_id + 12, _("Remove Efield Cortex target") + ) + menu_id.Bind( + wx.EVT_MENU, self.OnMenuRemoveEfieldTargetatCortex, create_efield_target + ) else: - create_efield_target = menu_id.Append(unique_menu_id + 12, _('Set as Efield Cortex target')) + create_efield_target = menu_id.Append( + unique_menu_id + 12, _("Set as Efield Cortex target") + ) menu_id.Bind(wx.EVT_MENU, self.OnSetEfieldBrainTarget, create_efield_target) self.marker_list_ctrl.GetFocusedItem() @@ -2112,10 +2284,14 @@ def FocusOnMarker(self, idx): # If the marker has been deleted, it might not be found in the list of markers. In that case, # do not try to deselect it. if current_marker_idx is not None: - self.marker_list_ctrl.SetItemState(current_marker_idx, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED) + self.marker_list_ctrl.SetItemState( + current_marker_idx, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED + ) # Trigger EVT_LIST_ITEM_DESELECTED event manually for the old item. - event_deselect = wx.ListEvent(wx.EVT_LIST_ITEM_DESELECTED.typeId, self.marker_list_ctrl.GetId()) + event_deselect = wx.ListEvent( + wx.EVT_LIST_ITEM_DESELECTED.typeId, self.marker_list_ctrl.GetId() + ) event_deselect.SetIndex(current_marker_idx) event_deselect.SetEventObject(self.marker_list_ctrl) self.marker_list_ctrl.GetEventHandler().ProcessEvent(event_deselect) @@ -2159,7 +2335,7 @@ def OnMarkerFocused(self, evt): # TODO: Support multiple highlighted markers at the same time. if self.currently_focused_marker is not None: # Unhighlight the previously focused marker in the viewer volume. - Publisher.sendMessage('Unhighlight marker') + Publisher.sendMessage("Unhighlight marker") self.currently_focused_marker = marker self.markers.SelectMarker(marker_id) @@ -2173,7 +2349,7 @@ def OnMarkerUnfocused(self, evt): def SetCameraToFocusOnMarker(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() marker = self.markers.list[idx] - Publisher.sendMessage('Set camera to focus on marker', marker=marker) + Publisher.sendMessage("Set camera to focus on marker", marker=marker) def OnCreateCoilTargetFromLandmark(self, evt): list_index = self.marker_list_ctrl.GetFocusedItem() @@ -2199,7 +2375,9 @@ def ChangeLabel(self, evt): wx.MessageBox(_("No data selected."), _("InVesalius 3")) return marker = self.__get_marker(list_index) - new_label = dlg.ShowEnterMarkerID(self.marker_list_ctrl.GetItemText(list_index, const.LABEL_COLUMN)) + new_label = dlg.ShowEnterMarkerID( + self.marker_list_ctrl.GetItemText(list_index, const.LABEL_COLUMN) + ) self.markers.ChangeLabel(marker, new_label) def OnMenuSetTarget(self, evt): @@ -2213,7 +2391,7 @@ def OnMenuSetTarget(self, evt): def _SetTarget(self, marker): idx = self.__find_marker_index(marker.marker_id) - self.marker_list_ctrl.SetItemBackgroundColour(idx, 'RED') + self.marker_list_ctrl.SetItemBackgroundColour(idx, "RED") self.marker_list_ctrl.SetItem(idx, const.TARGET_COLUMN, _("Yes")) target_uuid = marker.marker_uuid @@ -2234,19 +2412,18 @@ def OnMenuDuplicateMarker(self, evt): new_marker = self.__get_marker(idx).duplicate() # Add suffix to marker name. - new_marker.label = new_marker.label + ' (copy)' + new_marker.label = new_marker.label + " (copy)" self.markers.AddMarker(new_marker, render=True, focus=True) def GetEfieldDataStatus(self, efield_data_loaded, indexes_saved_list): - self.indexes_saved_lists= [] + self.indexes_saved_lists = [] self.efield_data_saved = efield_data_loaded self.indexes_saved_lists = indexes_saved_list def CreateMarkerEfield(self, point, orientation): - from vtkmodules.vtkCommonColor import ( - vtkNamedColors - ) + from vtkmodules.vtkCommonColor import vtkNamedColors + vtk_colors = vtkNamedColors() position_flip = list(point) position_flip[1] = -position_flip[1] @@ -2254,7 +2431,7 @@ def CreateMarkerEfield(self, point, orientation): marker = self.CreateMarker( position=position_flip, orientation=list(orientation), - colour=vtk_colors.GetColor3d('Orange'), + colour=vtk_colors.GetColor3d("Orange"), size=2, marker_type=MarkerType.COIL_TARGET, ) @@ -2266,22 +2443,28 @@ def OnMenuShowVectorField(self, evt): marker = self.__get_marker(idx) position = marker.position orientation = np.radians(marker.orientation) - Publisher.sendMessage('Calculate position and rotation', position=position, orientation=orientation) + Publisher.sendMessage( + "Calculate position and rotation", position=position, orientation=orientation + ) coord = [position, orientation] coord = np.array(coord).flatten() - #Check here, it resets the radious list - Publisher.sendMessage('Update interseccion offline', m_img=self.m_img_offline, coord=coord, list_index=marker.marker_id) + # Check here, it resets the radious list + Publisher.sendMessage( + "Update interseccion offline", + m_img=self.m_img_offline, + coord=coord, + list_index=marker.marker_id, + ) - if session.GetConfig('debug_efield'): + if session.GetConfig("debug_efield"): enorm = self.navigation.debug_efield_enorm else: - enorm = self.navigation.neuronavigation_api.update_efield_vectorROI(position=self.cp, - orientation=orientation, - T_rot=self.T_rot, - id_list=self.ID_list) + enorm = self.navigation.neuronavigation_api.update_efield_vectorROI( + position=self.cp, orientation=orientation, T_rot=self.T_rot, id_list=self.ID_list + ) enorm_data = [self.T_rot, self.cp, coord, enorm, self.ID_list] - Publisher.sendMessage('Get enorm', enorm_data = enorm_data , plot_vector = True) + Publisher.sendMessage("Get enorm", enorm_data=enorm_data, plot_vector=True) def GetRotationPosition(self, T_rot, cp, m_img): self.T_rot = T_rot @@ -2291,7 +2474,7 @@ def GetRotationPosition(self, T_rot, cp, m_img): def GetIdList(self, ID_list): self.ID_list = ID_list - def OnMenuSetEfieldTarget(self,evt): + def OnMenuSetEfieldTarget(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() if idx == -1: wx.MessageBox(_("No data selected."), _("InVesalius 3")) @@ -2300,26 +2483,36 @@ def OnMenuSetEfieldTarget(self,evt): self.markers.SetTarget(marker_id) self.efield_target_idx_origin = marker_id - #Publisher.sendMessage('Get target index efield', target_index_list = marker_id ) + # Publisher.sendMessage('Get target index efield', target_index_list = marker_id ) - def OnMenuSetEfieldTarget2(self,evt): + def OnMenuSetEfieldTarget2(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() if idx == -1: wx.MessageBox(_("No data selected."), _("InVesalius 3")) return efield_target_idx_2 = self.__get_marker_id(idx) - target1_origin = self.markers.list[self.efield_target_idx_origin].cortex_position_orientation + target1_origin = self.markers.list[ + self.efield_target_idx_origin + ].cortex_position_orientation target2 = self.markers.list[efield_target_idx_2].cortex_position_orientation - Publisher.sendMessage('Get targets Ids for mtms', target1_origin=target1_origin, target2=target2) + Publisher.sendMessage( + "Get targets Ids for mtms", target1_origin=target1_origin, target2=target2 + ) - def OnMenuSaveEfieldTargetData(self,evt): + def OnMenuSaveEfieldTargetData(self, evt): list_index = self.marker_list_ctrl.GetFocusedItem() marker = self.__get_marker(list_index) position = marker.position orientation = marker.orientation plot_efield_vectors = self.navigation.plot_efield_vectors - Publisher.sendMessage('Save target data', target_list_index=marker.marker_id, position=position, orientation=orientation, plot_efield_vectors=plot_efield_vectors) + Publisher.sendMessage( + "Save target data", + target_list_index=marker.marker_id, + position=position, + orientation=orientation, + plot_efield_vectors=plot_efield_vectors, + ) def OnSetEfieldBrainTarget(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() @@ -2331,7 +2524,12 @@ def OnSetEfieldBrainTarget(self, evt): orientation = [0, 0, 0] self.markers.SetPointOfInterest(marker_id) - Publisher.sendMessage('Send efield target position on brain', marker_id=marker_id, position=position, orientation=orientation) + Publisher.sendMessage( + "Send efield target position on brain", + marker_id=marker_id, + position=position, + orientation=orientation, + ) def OnCreateBrainTargetFromLandmark(self, evt): list_index = self.marker_list_ctrl.GetFocusedItem() @@ -2339,9 +2537,16 @@ def OnCreateBrainTargetFromLandmark(self, evt): position = marker.position orientation = marker.orientation - dialog = dlg.CreateBrainTargetDialog(marker=position+orientation, brain_actor=self.brain_actor) + dialog = dlg.CreateBrainTargetDialog( + marker=position + orientation, brain_actor=self.brain_actor + ) if dialog.ShowModal() == wx.ID_OK: - coil_position_list, coil_orientation_list, brain_position_list, brain_orientation_list = dialog.GetValue() + ( + coil_position_list, + coil_orientation_list, + brain_position_list, + brain_orientation_list, + ) = dialog.GetValue() position = list(coil_position_list[0]) orientation = list(coil_orientation_list[0]) @@ -2356,7 +2561,7 @@ def OnCreateBrainTargetFromLandmark(self, evt): ) self.markers.AddMarker(marker, render=True, focus=True) - for (position, orientation) in zip(brain_position_list, brain_orientation_list): + for position, orientation in zip(brain_position_list, brain_orientation_list): marker = self.CreateMarker( position=list(position), orientation=list(orientation), @@ -2382,7 +2587,7 @@ def OnMenuRemoveEfieldTargetatCortex(self, evt): marker.marker_type = MarkerType.LANDMARK self.markers.UnsetPointOfInterest(marker.marker_id) - Publisher.sendMessage('Clear efield target at cortex') + Publisher.sendMessage("Clear efield target at cortex") def OnMenuUnsetTarget(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() @@ -2393,10 +2598,10 @@ def _UnsetTarget(self, marker): idx = self.__find_marker_index(marker.marker_id) # When unsetting a target, automatically unpress the target mode button. - Publisher.sendMessage('Press target mode button', pressed=False) + Publisher.sendMessage("Press target mode button", pressed=False) # Update the marker list control. - self.marker_list_ctrl.SetItemBackgroundColour(idx, 'white') + self.marker_list_ctrl.SetItemBackgroundColour(idx, "white") self.marker_list_ctrl.SetItem(idx, const.TARGET_COLUMN, "") # Unset the target in itemDataMap @@ -2457,11 +2662,16 @@ def OnSetBrainTarget(self, evt): position = marker.position orientation = marker.orientation - dialog = dlg.CreateBrainTargetDialog(mTMS=self.mTMS, marker=position+orientation, brain_target=True, brain_actor=self.brain_actor) + dialog = dlg.CreateBrainTargetDialog( + mTMS=self.mTMS, + marker=position + orientation, + brain_target=True, + brain_actor=self.brain_actor, + ) if dialog.ShowModal() == wx.ID_OK: position_list, orientation_list = dialog.GetValueBrainTarget() - for (position, orientation) in zip(position_list, orientation_list): + for position, orientation in zip(position_list, orientation_list): new_marker = self.CreateMarker( position=list(position), orientation=list(orientation), @@ -2486,7 +2696,7 @@ def OnSendBrainTarget(self, evt): coil_pose = target.position + target.orientation if self.navigation.coil_at_target: self.mTMS.UpdateTarget(coil_pose, brain_target) - #wx.CallAfter(Publisher.sendMessage, 'Send brain target to mTMS API', coil_pose=coil_pose, brain_target=brain_target) + # wx.CallAfter(Publisher.sendMessage, 'Send brain target to mTMS API', coil_pose=coil_pose, brain_target=brain_target) print("Send brain target to mTMS API") else: print("The coil is not at the target") @@ -2495,7 +2705,7 @@ def OnSendBrainTarget(self, evt): def OnSessionChanged(self, evt, ctrl): value = ctrl.GetValue() - Publisher.sendMessage('Current session changed', new_session_id=value) + Publisher.sendMessage("Current session changed", new_session_id=value) def OnSelectMarkerByActor(self, actor): """ @@ -2506,12 +2716,12 @@ def OnSelectMarkerByActor(self, actor): place where the list of markers, including information about their visualization, is stored. """ - for (m, idx) in zip(self.markers.list, range(len(self.markers.list))): + for m, idx in zip(self.markers.list, range(len(self.markers.list))): visualization = m.visualization if visualization is None: continue - if visualization['actor'] == actor: + if visualization["actor"] == actor: # Unselect the previously selected item. idx_old = self.marker_list_ctrl.GetFocusedItem() if idx_old != -1 and idx_old != idx: @@ -2559,21 +2769,36 @@ def OnDeleteSelectedMarkers(self, evt=None): # Re-focus on the marker with the same index as the first marker that was selected before deletion. if self.currently_focused_marker is not None: first_deleted_index = indexes[0] - first_existing_index = first_deleted_index if first_deleted_index < len(self.markers.list) else len(self.markers.list) - 1 + first_existing_index = ( + first_deleted_index + if first_deleted_index < len(self.markers.list) + else len(self.markers.list) - 1 + ) self.FocusOnMarker(first_existing_index) def GetNextMarkerLabel(self): return self.markers.GetNextMarkerLabel() - def OnCreateMarker(self, evt=None, position=None, orientation=None, colour=None, size=None, label=None, - is_target=False, seed=None, session_id=None, marker_type=None, cortex_position_orientation=None): - + def OnCreateMarker( + self, + evt=None, + position=None, + orientation=None, + colour=None, + size=None, + label=None, + is_target=False, + seed=None, + session_id=None, + marker_type=None, + cortex_position_orientation=None, + ): if label is None: label = self.GetNextMarkerLabel() if self.nav_status and self.navigation.e_field_loaded: - Publisher.sendMessage('Get Cortex position') + Publisher.sendMessage("Get Cortex position") # XXX: Set marker type to 'coil target' if created during navigation, otherwise 'landmark'. This enables creating # coil targets during navigation. However, this logic shouldn't be inferred from the navigation status. Ideally, @@ -2584,7 +2809,11 @@ def OnCreateMarker(self, evt=None, position=None, orientation=None, colour=None, # MarkerType.FIDUCIAL by the caller), do not automatically infer the marker type; only do it, if # marker_type is None. if marker_type is None: - marker_type = MarkerType.COIL_TARGET if self.nav_status else MarkerType.LANDMARK + marker_type = ( + MarkerType.COIL_TARGET + if self.nav_status and self.navigation.track_obj + else MarkerType.LANDMARK + ) marker = self.CreateMarker( position=position, @@ -2611,7 +2840,7 @@ def ParseValue(self, value): if value == "None": return None try: - if '.' in value: + if "." in value: return float(value) return int(value) @@ -2623,21 +2852,21 @@ def ParseValue(self, value): def GetMarkersFromFile(self, filename, overwrite_image_fiducials): try: - with open(filename, 'r') as file: + with open(filename, "r") as file: magick_line = file.readline() assert magick_line.startswith(const.MARKER_FILE_MAGICK_STRING) - version = int(magick_line.split('_')[-1]) + version = int(magick_line.split("_")[-1]) if version not in const.SUPPORTED_MARKER_FILE_VERSIONS: wx.MessageBox(_("Unknown version of the markers file."), _("InVesalius 3")) return # Use the first line after the magick_line as the names for dictionary keys. - column_names = file.readline().strip().split('\t') + column_names = file.readline().strip().split("\t") column_names_parsed = [self.ParseValue(name) for name in column_names] markers_data = [] for line in file: - values = line.strip().split('\t') + values = line.strip().split("\t") values_parsed = [self.ParseValue(value) for value in values] marker_data = dict(zip(column_names_parsed, values_parsed)) @@ -2657,14 +2886,16 @@ def GetMarkersFromFile(self, filename, overwrite_image_fiducials): self.markers.AddMarker(marker, render=False) if overwrite_image_fiducials and marker.label in self.__list_fiducial_labels(): - Publisher.sendMessage('Load image fiducials', label=marker.label, position=marker.position) + Publisher.sendMessage( + "Load image fiducials", label=marker.label, position=marker.position + ) except Exception as e: wx.MessageBox(_("Invalid markers file."), _("InVesalius 3")) utils.debug(e) self.marker_list_ctrl.Show() - Publisher.sendMessage('Render volume viewer') + Publisher.sendMessage("Render volume viewer") Publisher.sendMessage("Update UI for refine tab") self.markers.SaveState() @@ -2673,8 +2904,10 @@ def OnLoadMarkers(self, evt): The file should contain no more than a single target marker. Also the file should not contain any fiducials already in the list.""" - last_directory = ses.Session().GetConfig('last_directory_3d_surface', '') - dialog = dlg.FileSelectionDialog(_(u"Load markers"), last_directory, const.WILDCARD_MARKER_FILES) + last_directory = ses.Session().GetConfig("last_directory_3d_surface", "") + dialog = dlg.FileSelectionDialog( + _("Load markers"), last_directory, const.WILDCARD_MARKER_FILES + ) overwrite_checkbox = wx.CheckBox(dialog, -1, _("Overwrite current image fiducials")) dialog.sizer.Add(overwrite_checkbox, 0, wx.CENTER) dialog.FitSizers() @@ -2684,34 +2917,43 @@ def OnLoadMarkers(self, evt): def OnShowHideAllMarkers(self, evt, ctrl): if ctrl.GetValue(): - Publisher.sendMessage('Hide markers', markers=self.markers.list) - ctrl.SetLabel('Show all') + Publisher.sendMessage("Hide markers", markers=self.markers.list) + ctrl.SetLabel("Show all") else: - Publisher.sendMessage('Show markers', markers=self.markers.list) - ctrl.SetLabel('Hide all') + Publisher.sendMessage("Show markers", markers=self.markers.list) + ctrl.SetLabel("Hide all") def OnSaveMarkers(self, evt): prj_data = prj.Project() timestamp = time.localtime(time.time()) - stamp_date = '{:0>4d}{:0>2d}{:0>2d}'.format(timestamp.tm_year, timestamp.tm_mon, timestamp.tm_mday) - stamp_time = '{:0>2d}{:0>2d}{:0>2d}'.format(timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec) - sep = '-' - parts = [stamp_date, stamp_time, prj_data.name, 'markers'] - default_filename = sep.join(parts) + '.mkss' - - filename = dlg.ShowLoadSaveDialog(message=_(u"Save markers as..."), - wildcard=const.WILDCARD_MARKER_FILES, - style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, - default_filename=default_filename) + stamp_date = "{:0>4d}{:0>2d}{:0>2d}".format( + timestamp.tm_year, timestamp.tm_mon, timestamp.tm_mday + ) + stamp_time = "{:0>2d}{:0>2d}{:0>2d}".format( + timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec + ) + sep = "-" + parts = [stamp_date, stamp_time, prj_data.name, "markers"] + default_filename = sep.join(parts) + ".mkss" + + filename = dlg.ShowLoadSaveDialog( + message=_("Save markers as..."), + wildcard=const.WILDCARD_MARKER_FILES, + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, + default_filename=default_filename, + ) if not filename: return - version_line = '%s%i\n' % (const.MARKER_FILE_MAGICK_STRING, const.CURRENT_MARKER_FILE_VERSION) - header_line = '%s\n' % Marker.to_csv_header() - data_lines = [marker.to_csv_row() + '\n' for marker in self.markers.list] + version_line = "%s%i\n" % ( + const.MARKER_FILE_MAGICK_STRING, + const.CURRENT_MARKER_FILE_VERSION, + ) + header_line = "%s\n" % Marker.to_csv_header() + data_lines = [marker.to_csv_row() + "\n" for marker in self.markers.list] try: - with open(filename, 'w', newline='') as file: + with open(filename, "w", newline="") as file: file.writelines([version_line, header_line]) file.writelines(data_lines) file.close() @@ -2748,20 +2990,33 @@ def UpdateMarkerOrientation(self, marker_id=None): list_index = marker_id if marker_id else 0 position = self.markers.list[list_index].position orientation = self.markers.list[list_index].orientation - dialog = dlg.CreateBrainTargetDialog(mTMS=self.mTMS, marker=position+orientation) + dialog = dlg.CreateBrainTargetDialog(mTMS=self.mTMS, marker=position + orientation) if dialog.ShowModal() == wx.ID_OK: orientation = dialog.GetValue() - Publisher.sendMessage('Update target orientation', - target_id=marker_id, orientation=list(orientation)) + Publisher.sendMessage( + "Update target orientation", target_id=marker_id, orientation=list(orientation) + ) dialog.Destroy() def AddPeeledSurface(self, flag, actor): self.brain_actor = actor - def CreateMarker(self, position=None, orientation=None, colour=None, size=None, label=None, is_target=False, seed=None, - session_id=None, marker_type=MarkerType.LANDMARK, cortex_position_orientation=None, - z_offset=0.0, z_rotation=0.0): + def CreateMarker( + self, + position=None, + orientation=None, + colour=None, + size=None, + label=None, + is_target=False, + seed=None, + session_id=None, + marker_type=MarkerType.LANDMARK, + cortex_position_orientation=None, + z_offset=0.0, + z_rotation=0.0, + ): """ Create a new marker object. """ @@ -2780,7 +3035,9 @@ def CreateMarker(self, position=None, orientation=None, colour=None, size=None, marker.seed = seed or self.current_seed marker.session_id = session_id or self.current_session marker.marker_type = marker_type - marker.cortex_position_orientation = cortex_position_orientation or self.cortex_position_orientation + marker.cortex_position_orientation = ( + cortex_position_orientation or self.cortex_position_orientation + ) marker.z_offset = z_offset marker.z_rotation = z_rotation @@ -2796,7 +3053,6 @@ def CreateMarker(self, position=None, orientation=None, colour=None, size=None, return marker def _AddMarker(self, marker, render, focus): - # Add marker to the marker list in GUI and to the itemDataMap. num_items = self.marker_list_ctrl.GetItemCount() @@ -2812,9 +3068,11 @@ def _AddMarker(self, marker, render, focus): list_entry[const.LABEL_COLUMN] = marker.label list_entry[const.Z_OFFSET_COLUMN] = str(marker.z_offset) if marker.z_offset != 0.0 else "" list_entry[const.TARGET_COLUMN] = "Yes" if marker.is_target else "" - list_entry[const.POINT_OF_INTEREST_TARGET_COLUMN] = "Yes" if marker.is_point_of_interest else "" + list_entry[const.POINT_OF_INTEREST_TARGET_COLUMN] = ( + "Yes" if marker.is_point_of_interest else "" + ) - if self.session.GetConfig('debug'): + if self.session.GetConfig("debug"): list_entry.append(round(marker.x, 1)) list_entry.append(round(marker.y, 1)) list_entry.append(round(marker.z, 1)) diff --git a/invesalius/navigation/navigation.py b/invesalius/navigation/navigation.py index 7c397b6c6..54d8e87ee 100644 --- a/invesalius/navigation/navigation.py +++ b/invesalius/navigation/navigation.py @@ -1,10 +1,10 @@ -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- # Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas # Copyright: (C) 2001 Centro de Pesquisas Renato Archer # Homepage: http://www.softwarepublico.gov.br # Contact: invesalius@cti.gov.br # License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- # Este programa e software livre; voce pode redistribui-lo e/ou # modifica-lo sob os termos da Licenca Publica Geral GNU, conforme # publicada pela Free Software Foundation; de acordo com a versao 2 @@ -15,44 +15,44 @@ # COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais # detalhes. -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- -import threading import queue +import threading from time import sleep -import wx import numpy as np +import wx import invesalius.constants as const -import invesalius.project as prj -import invesalius.utils as utils import invesalius.data.bases as db import invesalius.data.coregistration as dcr +import invesalius.data.e_field as e_field import invesalius.data.serial_port_connection as spc import invesalius.data.slice_ as sl import invesalius.data.tractography as dti -import invesalius.data.e_field as e_field import invesalius.data.transformations as tr import invesalius.data.vtk_utils as vtk_utils +import invesalius.project as prj import invesalius.session as ses +import invesalius.utils as utils from invesalius.data.markers.marker import MarkerType -from invesalius.pubsub import pub as Publisher -from invesalius.utils import Singleton - -from invesalius.navigation.iterativeclosestpoint import IterativeClosestPoint from invesalius.navigation.image import Image -from invesalius.navigation.tracker import Tracker +from invesalius.navigation.iterativeclosestpoint import IterativeClosestPoint from invesalius.navigation.markers import MarkersControl from invesalius.navigation.robot import Robot +from invesalius.navigation.tracker import Tracker from invesalius.net.neuronavigation_api import NeuronavigationApi from invesalius.net.pedal_connection import PedalConnector +from invesalius.pubsub import pub as Publisher +from invesalius.utils import Singleton class NavigationHub(metaclass=Singleton): """ Class to initialize and store references to navigation components. """ + def __init__(self, window=None): self.tracker = Tracker() self.image = Image() @@ -60,8 +60,7 @@ def __init__(self, window=None): self.neuronavigation_api = NeuronavigationApi() self.pedal_connector = PedalConnector(self.neuronavigation_api, window) self.navigation = Navigation( - pedal_connector=self.pedal_connector, - neuronavigation_api=self.neuronavigation_api + pedal_connector=self.pedal_connector, neuronavigation_api=self.neuronavigation_api ) self.robot = Robot( tracker=self.tracker, @@ -90,7 +89,7 @@ def clear(self): unfinished = self.unfinished_tasks - len(self.queue) if unfinished <= 0: if unfinished < 0: - raise ValueError('task_done() called too many times') + raise ValueError("task_done() called too many times") self.all_tasks_done.notify_all() self.unfinished_tasks = unfinished self.queue.clear() @@ -98,7 +97,6 @@ def clear(self): class UpdateNavigationScene(threading.Thread): - def __init__(self, vis_queues, vis_components, event, sle, neuronavigation_api): """Class (threading) to update the navigation scene with all graphical elements. @@ -116,9 +114,22 @@ def __init__(self, vis_queues, vis_components, event, sle, neuronavigation_api): :type neuronavigation_api: invesalius.net.neuronavigation_api.NeuronavigationAPI """ - threading.Thread.__init__(self, name='UpdateScene') - self.serial_port_enabled, self.view_tracts, self.peel_loaded, self.e_field_loaded, self.plot_efield_vectors = vis_components - self.coord_queue, self.serial_port_queue, self.tracts_queue, self.icp_queue, self.e_field_norms_queue, self.e_field_IDs_queue = vis_queues + threading.Thread.__init__(self, name="UpdateScene") + ( + self.serial_port_enabled, + self.view_tracts, + self.peel_loaded, + self.e_field_loaded, + self.plot_efield_vectors, + ) = vis_components + ( + self.coord_queue, + self.serial_port_queue, + self.tracts_queue, + self.icp_queue, + self.e_field_norms_queue, + self.e_field_IDs_queue, + ) = vis_queues self.sle = sle self.event = event self.neuronavigation_api = neuronavigation_api @@ -134,46 +145,79 @@ def run(self): # use of CallAfter is mandatory otherwise crashes the wx interface if self.view_tracts: - bundle, affine_vtk, coord_offset, coord_offset_w = self.tracts_queue.get_nowait() - #TODO: Check if possible to combine the Remove tracts with Update tracts in a single command - wx.CallAfter(Publisher.sendMessage, 'Remove tracts') - wx.CallAfter(Publisher.sendMessage, 'Update tracts', root=bundle, affine_vtk=affine_vtk, - coord_offset=coord_offset, coord_offset_w=coord_offset_w) + bundle, affine_vtk, coord_offset, coord_offset_w = ( + self.tracts_queue.get_nowait() + ) + # TODO: Check if possible to combine the Remove tracts with Update tracts in a single command + wx.CallAfter(Publisher.sendMessage, "Remove tracts") + wx.CallAfter( + Publisher.sendMessage, + "Update tracts", + root=bundle, + affine_vtk=affine_vtk, + coord_offset=coord_offset, + coord_offset_w=coord_offset_w, + ) self.tracts_queue.task_done() if self.serial_port_enabled: trigger_on = self.serial_port_queue.get_nowait() if trigger_on: - wx.CallAfter(Publisher.sendMessage, 'Create marker', marker_type=MarkerType.COIL_POSE) + wx.CallAfter( + Publisher.sendMessage, "Create marker", marker_type=MarkerType.COIL_POSE + ) self.serial_port_queue.task_done() # TODO: If using the view_tracts substitute the raw coord from the offset coordinate, so the user # see the red cross in the position of the offset marker # Update the slice viewers to show the current position of the tracked object. - wx.CallAfter(Publisher.sendMessage, 'Update slices position', position=coord[:3]) + wx.CallAfter(Publisher.sendMessage, "Update slices position", position=coord[:3]) # Update the cross position to the current position of the tracked object, so that, e.g., when a # new marker is created, it is created in the current position of the object. - wx.CallAfter(Publisher.sendMessage, 'Set cross focal point', position=coord) + wx.CallAfter(Publisher.sendMessage, "Set cross focal point", position=coord) if self.e_field_loaded and object_visible_flag: - wx.CallAfter(Publisher.sendMessage, 'Update point location for e-field calculation', m_img=m_img, - coord=coord, queue_IDs=self.e_field_IDs_queue) + wx.CallAfter( + Publisher.sendMessage, + "Update point location for e-field calculation", + m_img=m_img, + coord=coord, + queue_IDs=self.e_field_IDs_queue, + ) if not self.e_field_norms_queue.empty(): try: enorm_data = self.e_field_norms_queue.get_nowait() - wx.CallAfter(Publisher.sendMessage, 'Get enorm', enorm_data=enorm_data, plot_vector = self.plot_efield_vectors) + wx.CallAfter( + Publisher.sendMessage, + "Get enorm", + enorm_data=enorm_data, + plot_vector=self.plot_efield_vectors, + ) finally: self.e_field_norms_queue.task_done() if view_obj: - wx.CallAfter(Publisher.sendMessage, 'Update coil pose', m_img=m_img, coord=coord) - wx.CallAfter(Publisher.sendMessage, 'Update object arrow matrix', m_img=m_img, coord=coord, flag= self.peel_loaded) - + wx.CallAfter( + Publisher.sendMessage, "Update coil pose", m_img=m_img, coord=coord + ) + wx.CallAfter( + Publisher.sendMessage, + "Update object arrow matrix", + m_img=m_img, + coord=coord, + flag=self.peel_loaded, + ) + else: + wx.CallAfter( + Publisher.sendMessage, + "Update volume viewer pointer", + position=[coord[0], -coord[1], coord[2]], + ) # Render the volume viewer and the slice viewers. - wx.CallAfter(Publisher.sendMessage, 'Render volume viewer') - wx.CallAfter(Publisher.sendMessage, 'Update slice viewer') + wx.CallAfter(Publisher.sendMessage, "Render volume viewer") + wx.CallAfter(Publisher.sendMessage, "Update slice viewer") self.coord_queue.task_done() @@ -224,10 +268,10 @@ def __init__(self, pedal_connector, neuronavigation_api): self.enable_act = False self.act_data = None self.n_tracts = const.N_TRACTS - + # Sleep parameters session = ses.Session() - sleep_nav = session.GetConfig('sleep_nav', const.SLEEP_NAVIGATION) + sleep_nav = session.GetConfig("sleep_nav", const.SLEEP_NAVIGATION) self.sleep_nav = sleep_nav @@ -249,10 +293,10 @@ def __init__(self, pedal_connector, neuronavigation_api): self.__bind_events() def __bind_events(self): - Publisher.subscribe(self.CoilAtTarget, 'Coil at target') - Publisher.subscribe(self.UpdateSerialPort, 'Update serial port') - Publisher.subscribe(self.UpdateObjectRegistration, 'Update object registration') - Publisher.subscribe(self.TrackObject, 'Track object') + Publisher.subscribe(self.CoilAtTarget, "Coil at target") + Publisher.subscribe(self.UpdateSerialPort, "Update serial port") + Publisher.subscribe(self.UpdateObjectRegistration, "Update object registration") + Publisher.subscribe(self.TrackObject, "Track object") def SaveConfig(self): # XXX: This shouldn't be needed, but task_navigator.py currently calls UpdateObjectRegistration with @@ -260,30 +304,37 @@ def SaveConfig(self): if self.object_registration is None: return - object_fiducials, object_orientations, object_reference_mode, object_name = self.object_registration + object_fiducials, object_orientations, object_reference_mode, object_name = ( + self.object_registration + ) state = { - 'object_fiducials': object_fiducials.tolist(), - 'object_orientations': object_orientations.tolist(), - 'object_reference_mode': object_reference_mode, - 'object_name': object_name.decode(const.FS_ENCODE), + "object_fiducials": object_fiducials.tolist(), + "object_orientations": object_orientations.tolist(), + "object_reference_mode": object_reference_mode, + "object_name": object_name.decode(const.FS_ENCODE), } session = ses.Session() - session.SetConfig('navigation', state) + session.SetConfig("navigation", state) def LoadConfig(self): session = ses.Session() - state = session.GetConfig('navigation') + state = session.GetConfig("navigation") if state is None: return - object_fiducials = np.array(state['object_fiducials']) - object_orientations = np.array(state['object_orientations']) - object_reference_mode = state['object_reference_mode'] - object_name = state['object_name'].encode(const.FS_ENCODE) - self.object_registration = (object_fiducials, object_orientations, object_reference_mode, object_name) + object_fiducials = np.array(state["object_fiducials"]) + object_orientations = np.array(state["object_orientations"]) + object_reference_mode = state["object_reference_mode"] + object_name = state["object_name"].encode(const.FS_ENCODE) + self.object_registration = ( + object_fiducials, + object_orientations, + object_reference_mode, + object_name, + ) def CoilAtTarget(self, state): self.coil_at_target = state @@ -304,7 +355,6 @@ def UpdateObjectRegistration(self, data=None): def GetObjectRegistration(self): return self.object_registration - def TrackObject(self, enabled=False): self.track_obj = enabled @@ -324,7 +374,9 @@ def UpdateFiducialRegistrationError(self, tracker, image): self.all_fiducials = np.vstack([image_fiducials, tracker_fiducials]) - self.fre = db.calculate_fre(tracker_fiducials_raw, self.all_fiducials, self.ref_mode_id, self.m_change) + self.fre = db.calculate_fre( + tracker_fiducials_raw, self.all_fiducials, self.ref_mode_id, self.m_change + ) def GetFiducialRegistrationError(self, icp): fre = icp.icp_fre if icp.use_icp else self.fre @@ -334,8 +386,9 @@ def PedalStateChanged(self, state): if not self.serial_port_in_use: return - permission_to_stimulate = (self.lock_to_target and self.coil_at_target) or \ - not self.lock_to_target + permission_to_stimulate = ( + self.lock_to_target and self.coil_at_target + ) or not self.lock_to_target if state and permission_to_stimulate: self.serial_port_connection.SendPulse() @@ -346,8 +399,9 @@ def EstimateTrackerToInVTransformationMatrix(self, tracker, image): self.all_fiducials = np.vstack([image_fiducials, tracker_fiducials]) - self.m_change = tr.affine_matrix_from_points(self.all_fiducials[3:, :].T, self.all_fiducials[:3, :].T, - shear=False, scale=False) + self.m_change = tr.affine_matrix_from_points( + self.all_fiducials[3:, :].T, self.all_fiducials[:3, :].T, shear=False, scale=False + ) def StartNavigation(self, tracker, icp): # initialize jobs list @@ -356,8 +410,21 @@ def StartNavigation(self, tracker, icp): if self.event.is_set(): self.event.clear() - vis_components = [self.serial_port_in_use, self.view_tracts, self.peel_loaded, self.e_field_loaded, self.plot_efield_vectors] - vis_queues = [self.coord_queue, self.serial_port_queue, self.tracts_queue, self.icp_queue, self.e_field_norms_queue, self.e_field_IDs_queue] + vis_components = [ + self.serial_port_in_use, + self.view_tracts, + self.peel_loaded, + self.e_field_loaded, + self.plot_efield_vectors, + ] + vis_queues = [ + self.coord_queue, + self.serial_port_queue, + self.tracts_queue, + self.icp_queue, + self.e_field_norms_queue, + self.e_field_IDs_queue, + ] Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components) errors = False @@ -380,23 +447,52 @@ def StartNavigation(self, tracker, icp): else: coord_raw = np.array([None]) - self.obj_data = db.object_registration(obj_fiducials, obj_orients, coord_raw, self.m_change) + self.obj_data = db.object_registration( + obj_fiducials, obj_orients, coord_raw, self.m_change + ) coreg_data.extend(self.obj_data) - queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue, self.object_at_target_queue, self.efield_queue] - jobs_list.append(dcr.CoordinateCorregistrate(self.ref_mode_id, tracker, coreg_data, - self.view_tracts, queues, - self.event, self.sleep_nav, tracker.tracker_id, - self.target, icp, self.e_field_loaded)) + queues = [ + self.coord_queue, + self.coord_tracts_queue, + self.icp_queue, + self.object_at_target_queue, + self.efield_queue, + ] + jobs_list.append( + dcr.CoordinateCorregistrate( + self.ref_mode_id, + tracker, + coreg_data, + self.view_tracts, + queues, + self.event, + self.sleep_nav, + tracker.tracker_id, + self.target, + icp, + self.e_field_loaded, + ) + ) else: coreg_data = (self.m_change, 0) queues = [self.coord_queue, self.coord_tracts_queue, self.icp_queue, self.efield_queue] - jobs_list.append(dcr.CoordinateCorregistrateNoObject(self.ref_mode_id, tracker, coreg_data, - self.view_tracts, queues, - self.event, self.sleep_nav, icp, self.e_field_loaded)) + jobs_list.append( + dcr.CoordinateCorregistrateNoObject( + self.ref_mode_id, + tracker, + coreg_data, + self.view_tracts, + queues, + self.event, + self.sleep_nav, + icp, + self.e_field_loaded, + ) + ) if not errors: - #TODO: Test the serial port thread + # TODO: Test the serial port thread if self.serial_port_in_use: self.serial_port_connection = spc.SerialPortConnection( com_port=self.com_port, @@ -424,20 +520,40 @@ def StartNavigation(self, tracker, icp): Publisher.sendMessage("Update marker offset state", create=True) - self.trk_inp = self.trekker, affine, self.seed_offset, self.n_tracts, self.seed_radius,\ - self.n_threads, self.act_data, affine_vtk, img_shift + self.trk_inp = ( + self.trekker, + affine, + self.seed_offset, + self.n_tracts, + self.seed_radius, + self.n_threads, + self.act_data, + affine_vtk, + img_shift, + ) # print("Appending the tract computation thread!") queues = [self.coord_tracts_queue, self.tracts_queue] if self.enable_act: - jobs_list.append(dti.ComputeTractsACTThread(self.trk_inp, queues, self.event, self.sleep_nav)) + jobs_list.append( + dti.ComputeTractsACTThread(self.trk_inp, queues, self.event, self.sleep_nav) + ) else: - jobs_list.append(dti.ComputeTractsThread(self.trk_inp, queues, self.event, self.sleep_nav)) + jobs_list.append( + dti.ComputeTractsThread(self.trk_inp, queues, self.event, self.sleep_nav) + ) if self.e_field_loaded: queues = [self.efield_queue, self.e_field_norms_queue, self.e_field_IDs_queue] - jobs_list.append(e_field.Visualize_E_field_Thread(queues, self.event, 2*self.sleep_nav, - self.neuronavigation_api, self.debug_efield_enorm, self.plot_efield_vectors)) - + jobs_list.append( + e_field.Visualize_E_field_Thread( + queues, + self.event, + 2 * self.sleep_nav, + self.neuronavigation_api, + self.debug_efield_enorm, + self.plot_efield_vectors, + ) + ) jobs_list.append( UpdateNavigationScene( @@ -454,12 +570,12 @@ def StartNavigation(self, tracker, icp): jobs.start() # del jobs - self.pedal_connector.add_callback('navigation', self.PedalStateChanged) + self.pedal_connector.add_callback("navigation", self.PedalStateChanged) def StopNavigation(self): self.event.set() - self.pedal_connector.remove_callback('navigation') + self.pedal_connector.remove_callback("navigation") self.coord_queue.clear() self.coord_queue.join() @@ -488,6 +604,11 @@ def StopNavigation(self): self.e_field_IDs_queue.clear() self.e_field_IDs_queue.join() - - vis_components = [self.serial_port_in_use, self.view_tracts, self.peel_loaded, self.e_field_loaded, self.plot_efield_vectors ] + vis_components = [ + self.serial_port_in_use, + self.view_tracts, + self.peel_loaded, + self.e_field_loaded, + self.plot_efield_vectors, + ] Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components)