Commit 24de58350db20db24f2267d4ac91feaaf9f24341
Committed by
GitHub
1 parent
9d79c88d
Exists in
master
ADD: Remote control via Socket.IO (#282)
* ADD: Remote control via Socket.IO - Add a command line option (--remote-host) to connect to a remote server via Socket.IO. - Add a hook to send all internal events to the remote server using `from_neuronavigation` event. - Add a Socket.IO event listener for `to_neuronavigation` events, send publish those events internally. * FIX: WxPython-UI modifying Socket.IO messages not working * FIX: Allow sending message to neuronavigation with data None * Review comments: Create RemoteControl class under invesalius/net * Review comments: Move invesalius_pubsub dir to invesalius/pubsub * ADD: Python modules needed by Socket.IO to requirements.txt * MOD: Move python-socketio to optional-requirements.txt - Also, change the requirement name from python-socketio to python-socketio[client], causing all dependencies needed by the Socket.IO client (e.g., requests) to be installed - Due to that, remove explicit 'requests' library from the requirements Co-authored-by: Olli-Pekka Kahilakoski <olli-pekka.kahilakoski@aalto.fi>
Showing
54 changed files
with
234 additions
and
52 deletions
Show diff stats
app.py
| ... | ... | @@ -51,7 +51,7 @@ try: |
| 51 | 51 | except ImportError: |
| 52 | 52 | from wx import SplashScreen |
| 53 | 53 | |
| 54 | -from pubsub import pub as Publisher | |
| 54 | +from invesalius.pubsub import pub as Publisher | |
| 55 | 55 | |
| 56 | 56 | #import wx.lib.agw.advancedsplash as agw |
| 57 | 57 | #if sys.platform.startswith('linux'): |
| ... | ... | @@ -323,6 +323,10 @@ def parse_comand_line(): |
| 323 | 323 | |
| 324 | 324 | parser.add_option("--import-folder", action="store", dest="import_folder") |
| 325 | 325 | |
| 326 | + parser.add_option("--remote-host", | |
| 327 | + action="store", | |
| 328 | + dest="remote_host") | |
| 329 | + | |
| 326 | 330 | parser.add_option("-s", "--save", |
| 327 | 331 | help="Save the project after an import.") |
| 328 | 332 | |
| ... | ... | @@ -503,6 +507,12 @@ def main(): |
| 503 | 507 | """ |
| 504 | 508 | options, args = parse_comand_line() |
| 505 | 509 | |
| 510 | + if options.remote_host is not None: | |
| 511 | + from invesalius.net.remote_control import RemoteControl | |
| 512 | + | |
| 513 | + remote_control = RemoteControl(options.remote_host) | |
| 514 | + remote_control.connect() | |
| 515 | + | |
| 506 | 516 | if options.no_gui: |
| 507 | 517 | non_gui_startup(options, args) |
| 508 | 518 | else: | ... | ... |
docs/devel/example_pubsub.py
| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | # More information about this design pattern can be found at: |
| 4 | 4 | # http://wiki.wxpython.org/ModelViewController |
| 5 | 5 | # http://wiki.wxpython.org/PubSub |
| 6 | -from pubsub import pub as Publisher | |
| 6 | +from invesalius.pubsub import pub as Publisher | |
| 7 | 7 | |
| 8 | 8 | # The maintainer of Pubsub module is Oliver Schoenborn. |
| 9 | 9 | # Since the end of 2006 Pubsub is now maintained separately on SourceForge at: | ... | ... |
docs/devel/example_singleton_pubsub.py
invesalius/control.py
invesalius/data/coordinates.py
invesalius/data/editor.py
invesalius/data/geometry.py
invesalius/data/imagedata_utils.py
invesalius/data/mask.py
| ... | ... | @@ -33,7 +33,7 @@ from invesalius.data.volume import VolumeMask |
| 33 | 33 | import numpy as np |
| 34 | 34 | import vtk |
| 35 | 35 | from invesalius_cy import floodfill |
| 36 | -from pubsub import pub as Publisher | |
| 36 | +from invesalius.pubsub import pub as Publisher | |
| 37 | 37 | from scipy import ndimage |
| 38 | 38 | from vtk.util import numpy_support |
| 39 | 39 | ... | ... |
invesalius/data/measures.py
invesalius/data/polydata_utils.py
invesalius/data/record_coords.py
| ... | ... | @@ -23,7 +23,7 @@ import time |
| 23 | 23 | import wx |
| 24 | 24 | from numpy import array, savetxt, hstack,vstack, asarray |
| 25 | 25 | import invesalius.gui.dialogs as dlg |
| 26 | -from pubsub import pub as Publisher | |
| 26 | +from invesalius.pubsub import pub as Publisher | |
| 27 | 27 | |
| 28 | 28 | |
| 29 | 29 | class Record(threading.Thread): | ... | ... |
invesalius/data/slice_.py
| ... | ... | @@ -22,7 +22,7 @@ import tempfile |
| 22 | 22 | import numpy as np |
| 23 | 23 | import vtk |
| 24 | 24 | from scipy import ndimage |
| 25 | -from pubsub import pub as Publisher | |
| 25 | +from invesalius.pubsub import pub as Publisher | |
| 26 | 26 | |
| 27 | 27 | import invesalius.constants as const |
| 28 | 28 | import invesalius.data.converters as converters | ... | ... |
invesalius/data/styles.py
| ... | ... | @@ -31,7 +31,7 @@ from scipy import ndimage |
| 31 | 31 | from imageio import imsave |
| 32 | 32 | from scipy.ndimage import generate_binary_structure, watershed_ift |
| 33 | 33 | from skimage.morphology import watershed |
| 34 | -from pubsub import pub as Publisher | |
| 34 | +from invesalius.pubsub import pub as Publisher | |
| 35 | 35 | |
| 36 | 36 | import invesalius.constants as const |
| 37 | 37 | import invesalius.data.converters as converters | ... | ... |
invesalius/data/styles_3d.py
invesalius/data/surface.py
invesalius/data/tractography.py
invesalius/data/trigger.py
invesalius/data/viewer_slice.py
| ... | ... | @@ -31,7 +31,7 @@ from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor |
| 31 | 31 | import invesalius.data.styles as styles |
| 32 | 32 | import wx |
| 33 | 33 | import sys |
| 34 | -from pubsub import pub as Publisher | |
| 34 | +from invesalius.pubsub import pub as Publisher | |
| 35 | 35 | |
| 36 | 36 | try: |
| 37 | 37 | from agw import floatspin as FS | ... | ... |
invesalius/data/viewer_volume.py
| ... | ... | @@ -29,7 +29,7 @@ from numpy.core.umath_tests import inner1d |
| 29 | 29 | import wx |
| 30 | 30 | import vtk |
| 31 | 31 | from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor |
| 32 | -from pubsub import pub as Publisher | |
| 32 | +from invesalius.pubsub import pub as Publisher | |
| 33 | 33 | import random |
| 34 | 34 | from scipy.spatial import distance |
| 35 | 35 | |
| ... | ... | @@ -622,6 +622,7 @@ class Viewer(wx.Panel): |
| 622 | 622 | |
| 623 | 623 | self.ren.AddActor(self.staticballs[self.ball_id]) |
| 624 | 624 | self.ball_id = self.ball_id + 1 |
| 625 | + | |
| 625 | 626 | #self.UpdateRender() |
| 626 | 627 | self.Refresh() |
| 627 | 628 | ... | ... |
invesalius/data/volume.py
| ... | ... | @@ -24,7 +24,7 @@ from distutils.version import LooseVersion |
| 24 | 24 | import numpy |
| 25 | 25 | import vtk |
| 26 | 26 | import wx |
| 27 | -from pubsub import pub as Publisher | |
| 27 | +from invesalius.pubsub import pub as Publisher | |
| 28 | 28 | |
| 29 | 29 | import invesalius.constants as const |
| 30 | 30 | import invesalius.project as prj | ... | ... |
invesalius/data/vtk_utils.py
invesalius/gui/bitmap_preview_panel.py
| ... | ... | @@ -5,7 +5,7 @@ import numpy |
| 5 | 5 | |
| 6 | 6 | from vtk.util import numpy_support |
| 7 | 7 | from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor |
| 8 | -from pubsub import pub as Publisher | |
| 8 | +from invesalius.pubsub import pub as Publisher | |
| 9 | 9 | |
| 10 | 10 | import invesalius.constants as const |
| 11 | 11 | import invesalius.data.vtk_utils as vtku | ... | ... |
invesalius/gui/brain_seg_dialog.py
invesalius/gui/data_notebook.py
| ... | ... | @@ -34,7 +34,7 @@ except ImportError: |
| 34 | 34 | import wx.lib.flatnotebook as fnb |
| 35 | 35 | |
| 36 | 36 | import wx.lib.platebtn as pbtn |
| 37 | -from pubsub import pub as Publisher | |
| 37 | +from invesalius.pubsub import pub as Publisher | |
| 38 | 38 | |
| 39 | 39 | import invesalius.constants as const |
| 40 | 40 | import invesalius.data.slice_ as slice_ | ... | ... |
invesalius/gui/default_tasks.py
| ... | ... | @@ -22,7 +22,7 @@ try: |
| 22 | 22 | import wx.lib.agw.foldpanelbar as fpb |
| 23 | 23 | except ModuleNotFoundError: |
| 24 | 24 | import wx.lib.foldpanelbar as fpb |
| 25 | -from pubsub import pub as Publisher | |
| 25 | +from invesalius.pubsub import pub as Publisher | |
| 26 | 26 | |
| 27 | 27 | import invesalius.constants as const |
| 28 | 28 | import invesalius.gui.data_notebook as nb | ... | ... |
invesalius/gui/default_viewers.py
| ... | ... | @@ -21,7 +21,7 @@ import os |
| 21 | 21 | |
| 22 | 22 | import wx |
| 23 | 23 | import wx.lib.agw.fourwaysplitter as fws |
| 24 | -from pubsub import pub as Publisher | |
| 24 | +from invesalius.pubsub import pub as Publisher | |
| 25 | 25 | |
| 26 | 26 | import invesalius.data.viewer_slice as slice_viewer |
| 27 | 27 | import invesalius.data.viewer_volume as volume_viewer |
| ... | ... | @@ -316,7 +316,7 @@ class VolumeInteraction(wx.Panel): |
| 316 | 316 | |
| 317 | 317 | import wx.lib.platebtn as pbtn |
| 318 | 318 | import wx.lib.buttons as btn |
| 319 | -from pubsub import pub as Publisher | |
| 319 | +from invesalius.pubsub import pub as Publisher | |
| 320 | 320 | import wx.lib.colourselect as csel |
| 321 | 321 | |
| 322 | 322 | RAYCASTING_TOOLS = wx.NewId() | ... | ... |
invesalius/gui/dialogs.py
| ... | ... | @@ -47,7 +47,7 @@ from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor |
| 47 | 47 | from wx.lib import masked |
| 48 | 48 | from wx.lib.agw import floatspin |
| 49 | 49 | from wx.lib.wordwrap import wordwrap |
| 50 | -from pubsub import pub as Publisher | |
| 50 | +from invesalius.pubsub import pub as Publisher | |
| 51 | 51 | |
| 52 | 52 | try: |
| 53 | 53 | from wx.adv import AboutDialogInfo, AboutBox | ... | ... |
invesalius/gui/dicom_preview_panel.py
| ... | ... | @@ -29,7 +29,7 @@ import vtk |
| 29 | 29 | |
| 30 | 30 | from vtk.util import numpy_support |
| 31 | 31 | from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor |
| 32 | -from pubsub import pub as Publisher | |
| 32 | +from invesalius.pubsub import pub as Publisher | |
| 33 | 33 | |
| 34 | 34 | import invesalius.constants as const |
| 35 | 35 | import invesalius.reader.dicom_reader as dicom_reader | ... | ... |
invesalius/gui/frame.py
| ... | ... | @@ -43,7 +43,7 @@ import wx.lib.popupctl as pc |
| 43 | 43 | from invesalius import inv_paths |
| 44 | 44 | from invesalius.gui import project_properties |
| 45 | 45 | from wx.lib.agw.aui.auibar import AUI_TB_PLAIN_BACKGROUND, AuiToolBar |
| 46 | -from pubsub import pub as Publisher | |
| 46 | +from invesalius.pubsub import pub as Publisher | |
| 47 | 47 | |
| 48 | 48 | try: |
| 49 | 49 | from wx.adv import TaskBarIcon as wx_TaskBarIcon | ... | ... |
invesalius/gui/import_bitmap_panel.py
| ... | ... | @@ -18,7 +18,7 @@ |
| 18 | 18 | #-------------------------------------------------------------------------- |
| 19 | 19 | import wx |
| 20 | 20 | import wx.gizmos as gizmos |
| 21 | -from pubsub import pub as Publisher | |
| 21 | +from invesalius.pubsub import pub as Publisher | |
| 22 | 22 | import wx.lib.splitter as spl |
| 23 | 23 | |
| 24 | 24 | import invesalius.constants as const | ... | ... |
invesalius/gui/import_network_panel.py
invesalius/gui/import_panel.py
| ... | ... | @@ -18,7 +18,7 @@ |
| 18 | 18 | #-------------------------------------------------------------------------- |
| 19 | 19 | import wx |
| 20 | 20 | import wx.gizmos as gizmos |
| 21 | -from pubsub import pub as Publisher | |
| 21 | +from invesalius.pubsub import pub as Publisher | |
| 22 | 22 | import wx.lib.splitter as spl |
| 23 | 23 | |
| 24 | 24 | import invesalius.constants as const | ... | ... |
invesalius/gui/preferences.py
| ... | ... | @@ -4,7 +4,7 @@ import invesalius.constants as const |
| 4 | 4 | import invesalius.session as ses |
| 5 | 5 | import wx |
| 6 | 6 | from invesalius.gui.language_dialog import ComboBoxLanguage |
| 7 | -from pubsub import pub as Publisher | |
| 7 | +from invesalius.pubsub import pub as Publisher | |
| 8 | 8 | |
| 9 | 9 | |
| 10 | 10 | class Preferences(wx.Dialog): | ... | ... |
invesalius/gui/project_properties.py
invesalius/gui/task_exporter.py
| ... | ... | @@ -29,7 +29,7 @@ except ImportError: |
| 29 | 29 | import wx.lib.hyperlink as hl |
| 30 | 30 | |
| 31 | 31 | import wx.lib.platebtn as pbtn |
| 32 | -from pubsub import pub as Publisher | |
| 32 | +from invesalius.pubsub import pub as Publisher | |
| 33 | 33 | |
| 34 | 34 | import invesalius.constants as const |
| 35 | 35 | import invesalius.gui.dialogs as dlg | ... | ... |
invesalius/gui/task_importer.py
| ... | ... | @@ -26,7 +26,7 @@ except ImportError: |
| 26 | 26 | import wx.lib.hyperlink as hl |
| 27 | 27 | import wx.lib.platebtn as pbtn |
| 28 | 28 | |
| 29 | -from pubsub import pub as Publisher | |
| 29 | +from invesalius.pubsub import pub as Publisher | |
| 30 | 30 | |
| 31 | 31 | import invesalius.constants as const |
| 32 | 32 | import invesalius.gui.dialogs as dlg | ... | ... |
invesalius/gui/task_navigator.py
| ... | ... | @@ -42,7 +42,7 @@ except ImportError: |
| 42 | 42 | |
| 43 | 43 | import wx.lib.colourselect as csel |
| 44 | 44 | import wx.lib.masked.numctrl |
| 45 | -from pubsub import pub as Publisher | |
| 45 | +from invesalius.pubsub import pub as Publisher | |
| 46 | 46 | from time import sleep |
| 47 | 47 | |
| 48 | 48 | import invesalius.constants as const | ... | ... |
invesalius/gui/task_slice.py
| ... | ... | @@ -31,7 +31,7 @@ except ImportError: |
| 31 | 31 | |
| 32 | 32 | import wx.lib.platebtn as pbtn |
| 33 | 33 | import wx.lib.colourselect as csel |
| 34 | -from pubsub import pub as Publisher | |
| 34 | +from invesalius.pubsub import pub as Publisher | |
| 35 | 35 | |
| 36 | 36 | import invesalius.data.mask as mask |
| 37 | 37 | import invesalius.data.slice_ as slice_ | ... | ... |
invesalius/gui/task_surface.py
| ... | ... | @@ -28,7 +28,7 @@ except ImportError: |
| 28 | 28 | import wx.lib.hyperlink as hl |
| 29 | 29 | import wx.lib.foldpanelbar as fpb |
| 30 | 30 | |
| 31 | -from pubsub import pub as Publisher | |
| 31 | +from invesalius.pubsub import pub as Publisher | |
| 32 | 32 | import wx.lib.colourselect as csel |
| 33 | 33 | import wx.lib.scrolledpanel as scrolled |
| 34 | 34 | ... | ... |
invesalius/gui/task_tools.py
| ... | ... | @@ -27,7 +27,7 @@ except ImportError: |
| 27 | 27 | import wx.lib.hyperlink as hl |
| 28 | 28 | |
| 29 | 29 | import wx.lib.platebtn as pbtn |
| 30 | -from pubsub import pub as Publisher | |
| 30 | +from invesalius.pubsub import pub as Publisher | |
| 31 | 31 | |
| 32 | 32 | import invesalius.constants as constants |
| 33 | 33 | import invesalius.constants as const | ... | ... |
invesalius/gui/widgets/canvas_renderer.py
invesalius/gui/widgets/clut_raycasting.py
invesalius/gui/widgets/slice_menu.py
| ... | ... | @@ -26,7 +26,7 @@ except(ImportError): |
| 26 | 26 | from ordereddict import OrderedDict |
| 27 | 27 | |
| 28 | 28 | import wx |
| 29 | -from pubsub import pub as Publisher | |
| 29 | +from invesalius.pubsub import pub as Publisher | |
| 30 | 30 | |
| 31 | 31 | import invesalius.constants as const |
| 32 | 32 | import invesalius.data.slice_ as sl | ... | ... |
| ... | ... | @@ -0,0 +1,83 @@ |
| 1 | +#!/usr/bin/env python3 | |
| 2 | +#-------------------------------------------------------------------------- | |
| 3 | +# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas | |
| 4 | +# Copyright: (C) 2001 Centro de Pesquisas Renato Archer | |
| 5 | +# Homepage: http://www.softwarepublico.gov.br | |
| 6 | +# Contact: invesalius@cti.gov.br | |
| 7 | +# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) | |
| 8 | +#-------------------------------------------------------------------------- | |
| 9 | +# Este programa e software livre; voce pode redistribui-lo e/ou | |
| 10 | +# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme | |
| 11 | +# publicada pela Free Software Foundation; de acordo com a versao 2 | |
| 12 | +# da Licenca. | |
| 13 | +# | |
| 14 | +# Este programa eh distribuido na expectativa de ser util, mas SEM | |
| 15 | +# QUALQUER GARANTIA; sem mesmo a garantia implicita de | |
| 16 | +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM | |
| 17 | +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais | |
| 18 | +# detalhes. | |
| 19 | +#------------------------------------------------------------------------- | |
| 20 | + | |
| 21 | +import time | |
| 22 | + | |
| 23 | +import socketio | |
| 24 | +import wx | |
| 25 | + | |
| 26 | +from invesalius.pubsub import pub as Publisher | |
| 27 | + | |
| 28 | +class RemoteControl: | |
| 29 | + def __init__(self, remote_host): | |
| 30 | + self._remote_host = remote_host | |
| 31 | + self._connected = False | |
| 32 | + self._sio = None | |
| 33 | + | |
| 34 | + def _on_connect(self): | |
| 35 | + print("Connected to {}".format(self._remote_host)) | |
| 36 | + self._connected = True | |
| 37 | + | |
| 38 | + def _on_disconnect(self): | |
| 39 | + print("Disconnected") | |
| 40 | + self._connected = False | |
| 41 | + | |
| 42 | + def _to_neuronavigation(self, msg): | |
| 43 | + topic = msg["topic"] | |
| 44 | + data = msg["data"] | |
| 45 | + if data is None: | |
| 46 | + data = {} | |
| 47 | + | |
| 48 | + print("Received an event into topic '{}' with data {}".format(topic, str(data))) | |
| 49 | + Publisher.sendMessage_no_hook( | |
| 50 | + topicName=topic, | |
| 51 | + **data | |
| 52 | + ) | |
| 53 | + | |
| 54 | + def _to_neuronavigation_wrapper(self, msg): | |
| 55 | + # wx.CallAfter wrapping is needed to make messages that update WxPython UI work properly, as the | |
| 56 | + # Socket.IO listener runs inside a thread. (See WxPython and thread-safety for more information.) | |
| 57 | + wx.CallAfter(self._to_neuronavigation, msg) | |
| 58 | + | |
| 59 | + def connect(self): | |
| 60 | + self._sio = socketio.Client() | |
| 61 | + | |
| 62 | + self._sio.on('connect', self._on_connect) | |
| 63 | + self._sio.on('disconnect', self._on_disconnect) | |
| 64 | + self._sio.on('to_neuronavigation', self._to_neuronavigation_wrapper) | |
| 65 | + | |
| 66 | + self._sio.connect(self._remote_host) | |
| 67 | + | |
| 68 | + while not self._connected: | |
| 69 | + print("Connecting...") | |
| 70 | + time.sleep(1.0) | |
| 71 | + | |
| 72 | + def _emit(topic, data): | |
| 73 | + print("Emitting data {} to topic {}".format(data, topic)) | |
| 74 | + try: | |
| 75 | + if isinstance(topic, str): | |
| 76 | + self._sio.emit("from_neuronavigation", { | |
| 77 | + "topic": topic, | |
| 78 | + "data": data, | |
| 79 | + }) | |
| 80 | + except TypeError: | |
| 81 | + pass | |
| 82 | + | |
| 83 | + Publisher.add_sendMessage_hook(_emit) | ... | ... |
invesalius/plugins.py
invesalius/presets.py
invesalius/project.py
| ... | ... | @@ -0,0 +1,86 @@ |
| 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 typing import Callable | |
| 21 | + | |
| 22 | +from pubsub import pub as Publisher | |
| 23 | +from pubsub.core.listener import UserListener | |
| 24 | + | |
| 25 | +__all__ = [ | |
| 26 | + # subscribing | |
| 27 | + 'subscribe', | |
| 28 | + 'unsubscribe', | |
| 29 | + | |
| 30 | + # publishing | |
| 31 | + 'sendMessage', | |
| 32 | + 'sendMessage_no_hook', | |
| 33 | + | |
| 34 | + # adding hooks | |
| 35 | + 'add_sendMessage_hook' | |
| 36 | +] | |
| 37 | + | |
| 38 | +Hook = Callable[[str, dict], None] | |
| 39 | + | |
| 40 | +sendMessage_hook: Hook = None | |
| 41 | + | |
| 42 | +def add_sendMessage_hook(hook: Hook): | |
| 43 | + """Add a hook for sending messages. The hook is a function that takes the topic | |
| 44 | + name as the first parameter and the message dict as the second parameter, and | |
| 45 | + returns None. | |
| 46 | + | |
| 47 | + :param hook: | |
| 48 | + """ | |
| 49 | + global sendMessage_hook | |
| 50 | + sendMessage_hook = hook | |
| 51 | + | |
| 52 | +def subscribe(listener: UserListener, topicName: str, **curriedArgs): | |
| 53 | + """Subscribe to a topic. | |
| 54 | + | |
| 55 | + :param listener: | |
| 56 | + :param topicName: | |
| 57 | + :param curriedArgs: | |
| 58 | + """ | |
| 59 | + subscribedListener, success = Publisher.subscribe(listener, topicName, **curriedArgs) | |
| 60 | + return subscribedListener, success | |
| 61 | + | |
| 62 | +def unsubscribe(*args, **kwargs): | |
| 63 | + """Unsubscribe from a topic. | |
| 64 | + | |
| 65 | + """ | |
| 66 | + Publisher.unsubscribe(*args, **kwargs) | |
| 67 | + | |
| 68 | +def sendMessage(topicName: str, **msgdata): | |
| 69 | + """Send a message in a given topic. | |
| 70 | + | |
| 71 | + :param topicName: | |
| 72 | + :param msgdata: | |
| 73 | + """ | |
| 74 | + Publisher.sendMessage(topicName, **msgdata) | |
| 75 | + if sendMessage_hook is not None: | |
| 76 | + sendMessage_hook(topicName, msgdata) | |
| 77 | + | |
| 78 | +def sendMessage_no_hook(topicName: str, **msgdata): | |
| 79 | + """Send a message in a given topic, but do not call the hook. | |
| 80 | + | |
| 81 | + :param topicName: | |
| 82 | + :param msgdata: | |
| 83 | + """ | |
| 84 | + Publisher.sendMessage(topicName, **msgdata) | |
| 85 | + | |
| 86 | +AUTO_TOPIC = Publisher.AUTO_TOPIC | ... | ... |
invesalius/reader/bitmap_reader.py
invesalius/reader/dicom_reader.py
| ... | ... | @@ -28,7 +28,7 @@ import gdcm |
| 28 | 28 | # Not showing GDCM warning and debug messages |
| 29 | 29 | gdcm.Trace_DebugOff() |
| 30 | 30 | gdcm.Trace_WarningOff() |
| 31 | -from pubsub import pub as Publisher | |
| 31 | +from invesalius.pubsub import pub as Publisher | |
| 32 | 32 | |
| 33 | 33 | import invesalius.constants as const |
| 34 | 34 | import invesalius.reader.dicom as dicom | ... | ... |
invesalius/session.py
invesalius/style.py
optional-requirements.txt