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)

app.py
... ... @@ -490,9 +490,20 @@ def print_events(topic=Publisher.AUTO_TOPIC, **msg_data):
490 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 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 508 options, args = parse_command_line()
498 509  
... ... @@ -513,6 +524,10 @@ def main():
513 524  
514 525 PedalConnection().start()
515 526  
  527 + from invesalius.net.neuronavigation_api import NeuronavigationApi
  528 +
  529 + NeuronavigationApi(connection)
  530 +
516 531 if options.no_gui:
517 532 non_gui_startup(options, args)
518 533 else:
... ...
invesalius/gui/task_navigator.py
... ... @@ -73,6 +73,8 @@ from invesalius.navigation.navigation import Navigation
73 73 from invesalius.navigation.tracker import Tracker
74 74 from invesalius.navigation.robot import Robot
75 75  
  76 +from invesalius.net.neuronavigation_api import NeuronavigationApi
  77 +
76 78 HAS_PEDAL_CONNECTION = True
77 79 try:
78 80 from invesalius.net.pedal_connection import PedalConnection
... ... @@ -172,6 +174,8 @@ class InnerFoldPanel(wx.Panel):
172 174 tracker = Tracker()
173 175 pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None
174 176  
  177 + neuronavigation_api = NeuronavigationApi()
  178 +
175 179 # Fold panel style
176 180 style = fpb.CaptionBarStyle()
177 181 style.SetCaptionStyle(fpb.CAPTIONBAR_GRADIENT_V)
... ... @@ -180,7 +184,12 @@ class InnerFoldPanel(wx.Panel):
180 184  
181 185 # Fold 1 - Navigation panel
182 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 194 fold_panel.ApplyCaptionStyle(item, style)
186 195 fold_panel.AddFoldPanelWindow(item, ntw, spacing=0,
... ... @@ -336,7 +345,7 @@ class InnerFoldPanel(wx.Panel):
336 345  
337 346  
338 347 class NeuronavigationPanel(wx.Panel):
339   - def __init__(self, parent, tracker, pedal_connection):
  348 + def __init__(self, parent, tracker, pedal_connection, neuronavigation_api):
340 349 wx.Panel.__init__(self, parent)
341 350 try:
342 351 default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR)
... ... @@ -350,8 +359,10 @@ class NeuronavigationPanel(wx.Panel):
350 359  
351 360 # Initialize global variables
352 361 self.pedal_connection = pedal_connection
  362 +
353 363 self.navigation = Navigation(
354 364 pedal_connection=pedal_connection,
  365 + neuronavigation_api=neuronavigation_api,
355 366 )
356 367 self.icp = ICP()
357 368 self.tracker = tracker
... ...
invesalius/navigation/navigation.py
... ... @@ -64,7 +64,7 @@ class QueueCustom(queue.Queue):
64 64  
65 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 68 """Class (threading) to update the navigation scene with all graphical elements.
69 69  
70 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 77 :type event: threading.Event
78 78 :param sle: Sleep pause in seconds
79 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 84 threading.Thread.__init__(self, name='UpdateScene')
... ... @@ -84,6 +86,7 @@ class UpdateNavigationScene(threading.Thread):
84 86 self.coord_queue, self.serial_port_queue, self.tracts_queue, self.icp_queue, self.robot_target_queue = vis_queues
85 87 self.sle = sle
86 88 self.event = event
  89 + self.neuronavigation_api = neuronavigation_api
87 90  
88 91 def run(self):
89 92 # count = 0
... ... @@ -121,6 +124,12 @@ class UpdateNavigationScene(threading.Thread):
121 124 if view_obj:
122 125 wx.CallAfter(Publisher.sendMessage, 'Update object matrix', m_img=m_img, coord=coord)
123 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 133 self.coord_queue.task_done()
125 134 # print('UpdateScene: done {}'.format(count))
126 135 # count += 1
... ... @@ -132,8 +141,9 @@ class UpdateNavigationScene(threading.Thread):
132 141  
133 142  
134 143 class Navigation():
135   - def __init__(self, pedal_connection):
  144 + def __init__(self, pedal_connection, neuronavigation_api):
136 145 self.pedal_connection = pedal_connection
  146 + self.neuronavigation_api = neuronavigation_api
137 147  
138 148 self.image_fiducials = np.full([3, 3], np.nan)
139 149 self.correg = None
... ... @@ -329,8 +339,15 @@ class Navigation():
329 339 else:
330 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 352 for jobs in jobs_list:
336 353 # jobs.daemon = True
... ...
invesalius/net/neuronavigation_api.py 0 → 100644
... ... @@ -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 + )
... ...