Commit 3f9f34c4dff93b27fa01eafaee41688987d6042d

Authored by Olli-Pekka Kahilakoski
Committed by GitHub
1 parent a6843134
Exists in master

ADD: NeuronavigationApi to send coil pose via connection object (#413)

@@ -490,9 +490,20 @@ def print_events(topic=Publisher.AUTO_TOPIC, **msg_data): @@ -490,9 +490,20 @@ def print_events(topic=Publisher.AUTO_TOPIC, **msg_data):
490 utils.debug("%s\n\tParameters: %s" % (topic, msg_data)) 490 utils.debug("%s\n\tParameters: %s" % (topic, msg_data))
491 491
492 492
493 -def main(): 493 +def main(connection=None):
494 """ 494 """
495 Initialize InVesalius GUI 495 Initialize InVesalius GUI
  496 +
  497 + Parameters:
  498 + connection: An object to communicate with the outside world.
  499 + In theory, can be any object supports certain function calls.
  500 + See invesalius.net.neuronavigation_api for a comprehensive
  501 + description of how the object is used.
  502 +
  503 + Note that if InVesalius is started in the usual way by running
  504 + app.py, the connection object defaults to None. To enable this
  505 + functionality, InVesalius needs to be started by calling the main
  506 + function directly with a proper connection object.
496 """ 507 """
497 options, args = parse_command_line() 508 options, args = parse_command_line()
498 509
@@ -513,6 +524,10 @@ def main(): @@ -513,6 +524,10 @@ def main():
513 524
514 PedalConnection().start() 525 PedalConnection().start()
515 526
  527 + from invesalius.net.neuronavigation_api import NeuronavigationApi
  528 +
  529 + NeuronavigationApi(connection)
  530 +
516 if options.no_gui: 531 if options.no_gui:
517 non_gui_startup(options, args) 532 non_gui_startup(options, args)
518 else: 533 else:
invesalius/gui/task_navigator.py
@@ -73,6 +73,8 @@ from invesalius.navigation.navigation import Navigation @@ -73,6 +73,8 @@ from invesalius.navigation.navigation import Navigation
73 from invesalius.navigation.tracker import Tracker 73 from invesalius.navigation.tracker import Tracker
74 from invesalius.navigation.robot import Robot 74 from invesalius.navigation.robot import Robot
75 75
  76 +from invesalius.net.neuronavigation_api import NeuronavigationApi
  77 +
76 HAS_PEDAL_CONNECTION = True 78 HAS_PEDAL_CONNECTION = True
77 try: 79 try:
78 from invesalius.net.pedal_connection import PedalConnection 80 from invesalius.net.pedal_connection import PedalConnection
@@ -172,6 +174,8 @@ class InnerFoldPanel(wx.Panel): @@ -172,6 +174,8 @@ class InnerFoldPanel(wx.Panel):
172 tracker = Tracker() 174 tracker = Tracker()
173 pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None 175 pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None
174 176
  177 + neuronavigation_api = NeuronavigationApi()
  178 +
175 # Fold panel style 179 # Fold panel style
176 style = fpb.CaptionBarStyle() 180 style = fpb.CaptionBarStyle()
177 style.SetCaptionStyle(fpb.CAPTIONBAR_GRADIENT_V) 181 style.SetCaptionStyle(fpb.CAPTIONBAR_GRADIENT_V)
@@ -180,7 +184,12 @@ class InnerFoldPanel(wx.Panel): @@ -180,7 +184,12 @@ class InnerFoldPanel(wx.Panel):
180 184
181 # Fold 1 - Navigation panel 185 # Fold 1 - Navigation panel
182 item = fold_panel.AddFoldPanel(_("Neuronavigation"), collapsed=True) 186 item = fold_panel.AddFoldPanel(_("Neuronavigation"), collapsed=True)
183 - ntw = NeuronavigationPanel(item, tracker, pedal_connection) 187 + ntw = NeuronavigationPanel(
  188 + parent=item,
  189 + tracker=tracker,
  190 + pedal_connection=pedal_connection,
  191 + neuronavigation_api=neuronavigation_api,
  192 + )
184 193
185 fold_panel.ApplyCaptionStyle(item, style) 194 fold_panel.ApplyCaptionStyle(item, style)
186 fold_panel.AddFoldPanelWindow(item, ntw, spacing=0, 195 fold_panel.AddFoldPanelWindow(item, ntw, spacing=0,
@@ -336,7 +345,7 @@ class InnerFoldPanel(wx.Panel): @@ -336,7 +345,7 @@ class InnerFoldPanel(wx.Panel):
336 345
337 346
338 class NeuronavigationPanel(wx.Panel): 347 class NeuronavigationPanel(wx.Panel):
339 - def __init__(self, parent, tracker, pedal_connection): 348 + def __init__(self, parent, tracker, pedal_connection, neuronavigation_api):
340 wx.Panel.__init__(self, parent) 349 wx.Panel.__init__(self, parent)
341 try: 350 try:
342 default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) 351 default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR)
@@ -350,8 +359,10 @@ class NeuronavigationPanel(wx.Panel): @@ -350,8 +359,10 @@ class NeuronavigationPanel(wx.Panel):
350 359
351 # Initialize global variables 360 # Initialize global variables
352 self.pedal_connection = pedal_connection 361 self.pedal_connection = pedal_connection
  362 +
353 self.navigation = Navigation( 363 self.navigation = Navigation(
354 pedal_connection=pedal_connection, 364 pedal_connection=pedal_connection,
  365 + neuronavigation_api=neuronavigation_api,
355 ) 366 )
356 self.icp = ICP() 367 self.icp = ICP()
357 self.tracker = tracker 368 self.tracker = tracker
invesalius/navigation/navigation.py
@@ -64,7 +64,7 @@ class QueueCustom(queue.Queue): @@ -64,7 +64,7 @@ class QueueCustom(queue.Queue):
64 64
65 class UpdateNavigationScene(threading.Thread): 65 class UpdateNavigationScene(threading.Thread):
66 66
67 - def __init__(self, vis_queues, vis_components, event, sle): 67 + def __init__(self, vis_queues, vis_components, event, sle, neuronavigation_api):
68 """Class (threading) to update the navigation scene with all graphical elements. 68 """Class (threading) to update the navigation scene with all graphical elements.
69 69
70 Sleep function in run method is used to avoid blocking GUI and more fluent, real-time navigation 70 Sleep function in run method is used to avoid blocking GUI and more fluent, real-time navigation
@@ -77,6 +77,8 @@ class UpdateNavigationScene(threading.Thread): @@ -77,6 +77,8 @@ class UpdateNavigationScene(threading.Thread):
77 :type event: threading.Event 77 :type event: threading.Event
78 :param sle: Sleep pause in seconds 78 :param sle: Sleep pause in seconds
79 :type sle: float 79 :type sle: float
  80 + :param neuronavigation_api: An API object for communicating the coil position.
  81 + :type neuronavigation_api: invesalius.net.neuronavigation_api.NeuronavigationAPI
80 """ 82 """
81 83
82 threading.Thread.__init__(self, name='UpdateScene') 84 threading.Thread.__init__(self, name='UpdateScene')
@@ -84,6 +86,7 @@ class UpdateNavigationScene(threading.Thread): @@ -84,6 +86,7 @@ class UpdateNavigationScene(threading.Thread):
84 self.coord_queue, self.serial_port_queue, self.tracts_queue, self.icp_queue, self.robot_target_queue = vis_queues 86 self.coord_queue, self.serial_port_queue, self.tracts_queue, self.icp_queue, self.robot_target_queue = vis_queues
85 self.sle = sle 87 self.sle = sle
86 self.event = event 88 self.event = event
  89 + self.neuronavigation_api = neuronavigation_api
87 90
88 def run(self): 91 def run(self):
89 # count = 0 92 # count = 0
@@ -121,6 +124,12 @@ class UpdateNavigationScene(threading.Thread): @@ -121,6 +124,12 @@ class UpdateNavigationScene(threading.Thread):
121 if view_obj: 124 if view_obj:
122 wx.CallAfter(Publisher.sendMessage, 'Update object matrix', m_img=m_img, coord=coord) 125 wx.CallAfter(Publisher.sendMessage, 'Update object matrix', m_img=m_img, coord=coord)
123 wx.CallAfter(Publisher.sendMessage, 'Update object arrow matrix', m_img=m_img, coord=coord, flag= self.peel_loaded) 126 wx.CallAfter(Publisher.sendMessage, 'Update object arrow matrix', m_img=m_img, coord=coord, flag= self.peel_loaded)
  127 +
  128 + self.neuronavigation_api.update_coil_pose(
  129 + position=coord[:3],
  130 + orientation=coord[3:],
  131 + )
  132 +
124 self.coord_queue.task_done() 133 self.coord_queue.task_done()
125 # print('UpdateScene: done {}'.format(count)) 134 # print('UpdateScene: done {}'.format(count))
126 # count += 1 135 # count += 1
@@ -132,8 +141,9 @@ class UpdateNavigationScene(threading.Thread): @@ -132,8 +141,9 @@ class UpdateNavigationScene(threading.Thread):
132 141
133 142
134 class Navigation(): 143 class Navigation():
135 - def __init__(self, pedal_connection): 144 + def __init__(self, pedal_connection, neuronavigation_api):
136 self.pedal_connection = pedal_connection 145 self.pedal_connection = pedal_connection
  146 + self.neuronavigation_api = neuronavigation_api
137 147
138 self.image_fiducials = np.full([3, 3], np.nan) 148 self.image_fiducials = np.full([3, 3], np.nan)
139 self.correg = None 149 self.correg = None
@@ -329,8 +339,15 @@ class Navigation(): @@ -329,8 +339,15 @@ class Navigation():
329 else: 339 else:
330 jobs_list.append(dti.ComputeTractsThread(self.trk_inp, queues, self.event, self.sleep_nav)) 340 jobs_list.append(dti.ComputeTractsThread(self.trk_inp, queues, self.event, self.sleep_nav))
331 341
332 - jobs_list.append(UpdateNavigationScene(vis_queues, vis_components,  
333 - self.event, self.sleep_nav)) 342 + jobs_list.append(
  343 + UpdateNavigationScene(
  344 + vis_queues=vis_queues,
  345 + vis_components=vis_components,
  346 + event=self.event,
  347 + sle=self.sleep_nav,
  348 + neuronavigation_api=self.neuronavigation_api,
  349 + )
  350 + )
334 351
335 for jobs in jobs_list: 352 for jobs in jobs_list:
336 # jobs.daemon = True 353 # jobs.daemon = True
invesalius/net/neuronavigation_api.py 0 → 100644
@@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
  1 +#--------------------------------------------------------------------------
  2 +# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas
  3 +# Copyright: (C) 2001 Centro de Pesquisas Renato Archer
  4 +# Homepage: http://www.softwarepublico.gov.br
  5 +# Contact: invesalius@cti.gov.br
  6 +# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt)
  7 +#--------------------------------------------------------------------------
  8 +# Este programa e software livre; voce pode redistribui-lo e/ou
  9 +# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme
  10 +# publicada pela Free Software Foundation; de acordo com a versao 2
  11 +# da Licenca.
  12 +#
  13 +# Este programa eh distribuido na expectativa de ser util, mas SEM
  14 +# QUALQUER GARANTIA; sem mesmo a garantia implicita de
  15 +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM
  16 +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais
  17 +# detalhes.
  18 +#--------------------------------------------------------------------------
  19 +
  20 +from invesalius.utils import Singleton
  21 +
  22 +class NeuronavigationApi(metaclass=Singleton):
  23 + """
  24 + An API used internally in InVesalius to communicate with the
  25 + outside world.
  26 +
  27 + When something noteworthy happens when running InVesalius, e.g.,
  28 + the coil is moved during neuronavigation, an object created from
  29 + this class can be used to update that information.
  30 +
  31 + When created for the first time, takes a connection object obtained
  32 + from outside InVesalius (see the main entrypoint in app.py).
  33 +
  34 + If connection object is not given or it is None, skip doing the updates.
  35 + """
  36 + def __init__(self, connection=None):
  37 + if connection is not None:
  38 + assert self._hasmethod(connection, 'update_coil_pose')
  39 +
  40 + self.connection = connection
  41 +
  42 + def _hasmethod(self, obj, name):
  43 + return hasattr(obj, name) and callable(getattr(obj, name))
  44 +
  45 + def update_coil_pose(self, position, orientation):
  46 + if self.connection is not None:
  47 + self.connection.update_coil_pose(
  48 + position=position,
  49 + orientation=orientation,
  50 + )