diff -Nru invesalius-3.1.1/app.py invesalius-3.1.99991/app.py --- invesalius-3.1.1/app.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/app.py 2018-08-17 16:41:39.000000000 +0000 @@ -18,31 +18,42 @@ # detalhes. #------------------------------------------------------------------------- +from __future__ import print_function import multiprocessing import optparse as op import os import sys import shutil +import traceback + +import re if sys.platform == 'win32': - import _winreg -else: - if sys.platform != 'darwin': - import wxversion - #wxversion.ensureMinimal('2.8-unicode', optionsRequired=True) - #wxversion.select('2.8-unicode', optionsRequired=True) - wxversion.ensureMinimal('3.0') + try: + import winreg + except ImportError: + import _winreg as winreg +# else: + # if sys.platform != 'darwin': + # import wxversion + # #wxversion.ensureMinimal('2.8-unicode', optionsRequired=True) + # #wxversion.select('2.8-unicode', optionsRequired=True) + # # wxversion.ensureMinimal('4.0') import wx +try: + from wx.adv import SplashScreen +except ImportError: + from wx import SplashScreen #from wx.lib.pubsub import setupv1 #new wx -from wx.lib.pubsub import setuparg1# as psv1 +# from wx.lib.pubsub import setuparg1# as psv1 #from wx.lib.pubsub import Publisher #import wx.lib.pubsub as ps from wx.lib.pubsub import pub as Publisher #import wx.lib.agw.advancedsplash as agw -#if sys.platform == 'linux2': +#if sys.platform.startswith('linux'): # _SplashScreen = agw.AdvancedSplash #else: # if sys.platform != 'darwin': @@ -61,9 +72,9 @@ try: USER_DIR = expand_user() except: - USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + USER_DIR = utils.decode(os.path.expanduser('~'), FS_ENCODE) else: - USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + USER_DIR = utils.decode(os.path.expanduser('~'),FS_ENCODE) USER_INV_DIR = os.path.join(USER_DIR, u'.invesalius') USER_PRESET_DIR = os.path.join(USER_INV_DIR, u'presets') @@ -72,6 +83,15 @@ # ------------------------------------------------------------------ +if sys.platform in ('linux2', 'linux', 'win32'): + try: + tmp_var = wx.GetXDisplay + except AttributeError: + # A workaround to make InVesalius run with wxPython4 from Ubuntu 18.04 + wx.GetXDisplay = lambda: None + else: + del tmp_var + class InVesalius(wx.App): """ @@ -86,7 +106,7 @@ freeze_support() self.SetAppName("InVesalius 3") - self.splash = SplashScreen() + self.splash = Inv3SplashScreen() self.splash.Show() wx.CallLater(1000,self.Startup2) @@ -97,7 +117,7 @@ Open drag & drop files under darwin """ path = os.path.abspath(filename) - Publisher.sendMessage('Open project', path) + Publisher.sendMessage('Open project', filepath=path) def Startup2(self): self.control = self.splash.control @@ -108,7 +128,7 @@ # ------------------------------------------------------------------ -class SplashScreen(wx.SplashScreen): +class Inv3SplashScreen(SplashScreen): """ Splash screen to be shown in InVesalius initialization. """ @@ -175,7 +195,10 @@ if lang: # print "LANG", lang, _, wx.Locale(), wx.GetLocale() import locale - locale.setlocale(locale.LC_ALL, '') + try: + locale.setlocale(locale.LC_ALL, '') + except locale.Error: + pass # For pt_BR, splash_pt.png should be used if (lang.startswith('pt')): icon_file = "splash_pt.png" @@ -196,16 +219,20 @@ bmp = wx.Image(path).ConvertToBitmap() - style = wx.SPLASH_TIMEOUT | wx.SPLASH_CENTRE_ON_SCREEN - wx.SplashScreen.__init__(self, - bitmap=bmp, - splashStyle=style, - milliseconds=1500, - id=-1, - parent=None) + try: + style = wx.adv.SPLASH_TIMEOUT | wx.adv.SPLASH_CENTRE_ON_SCREEN + except AttributeError: + style = wx.SPLASH_TIMEOUT | wx.SPLASH_CENTRE_ON_SCREEN + + SplashScreen.__init__(self, + bitmap=bmp, + splashStyle=style, + milliseconds=1500, + id=-1, + parent=None) self.Bind(wx.EVT_CLOSE, self.OnClose) wx.Yield() - wx.CallLater(200,self.Startup) + wx.CallLater(200, self.Startup) def Startup(self): # Importing takes sometime, therefore it will be done @@ -218,7 +245,8 @@ self.control = Controller(self.main) self.fc = wx.FutureCall(1, self.ShowMain) - wx.FutureCall(1, parse_comand_line) + options, args = parse_comand_line() + wx.FutureCall(1, use_cmd_optargs, options, args) # Check for updates from threading import Thread @@ -244,6 +272,24 @@ if self.fc.IsRunning(): self.Raise() + +def non_gui_startup(options, args): + lang = 'en' + _ = i18n.InstallLanguage(lang) + + from invesalius.control import Controller + from invesalius.project import Project + + session = ses.Session() + if not session.ReadSession(): + session.CreateItens() + session.SetLanguage(lang) + session.WriteSessionFile() + + control = Controller(None) + + use_cmd_optargs(options, args) + # ------------------------------------------------------------------ @@ -262,51 +308,170 @@ action="store_true", dest="debug") + parser.add_option('--no-gui', + action='store_true', + dest='no_gui') + # -i or --import: import DICOM directory # chooses largest series parser.add_option("-i", "--import", action="store", dest="dicom_dir") + + parser.add_option("--import-all", + action="store") + + parser.add_option("-s", "--save", + help="Save the project after an import.") + + parser.add_option("-t", "--threshold", + help="Define the threshold for the export (e.g. 100-780).") + + parser.add_option("-e", "--export", + help="Export to STL.") + + parser.add_option("-a", "--export-to-all", + help="Export to STL for all mask presets.") + options, args = parser.parse_args() + return options, args + +def use_cmd_optargs(options, args): # If debug argument... if options.debug: Publisher.subscribe(print_events, Publisher.ALL_TOPICS) + session = ses.Session() session.debug = 1 # If import DICOM argument... if options.dicom_dir: import_dir = options.dicom_dir - Publisher.sendMessage('Import directory', import_dir) + Publisher.sendMessage('Import directory', directory=import_dir, use_gui=not options.no_gui) + + if options.save: + Publisher.sendMessage('Save project', filepath=os.path.abspath(options.save)) + exit(0) + + check_for_export(options) + + return True + elif options.import_all: + import invesalius.reader.dicom_reader as dcm + for patient in dcm.GetDicomGroups(options.import_all): + for group in patient.GetGroups(): + Publisher.sendMessage('Import group', + group=group, + use_gui=not options.no_gui) + check_for_export(options, suffix=group.title, remove_surfaces=False) + Publisher.sendMessage('Remove masks', mask_indexes=(0,)) return True # Check if there is a file path somewhere in what the user wrote # In case there is, try opening as it was a inv3 else: - i = len(args) - while i: - i -= 1 - file = args[i].decode(sys.stdin.encoding) + for arg in reversed(args): + + file = utils.decode(arg, FS_ENCODE) + if os.path.isfile(file): + path = os.path.abspath(file) + Publisher.sendMessage('Open project', filepath=path) + check_for_export(options) + return True + + file = utils.decode(arg, sys.stdin.encoding) if os.path.isfile(file): path = os.path.abspath(file) - Publisher.sendMessage('Open project', path) - i = 0 + Publisher.sendMessage('Open project', filepath=path) + check_for_export(options) return True + return False -def print_events(data): +def sanitize(text): + text = str(text).strip().replace(' ', '_') + return re.sub(r'(?u)[^-\w.]', '', text) + + +def check_for_export(options, suffix='', remove_surfaces=False): + suffix = sanitize(suffix) + + if options.export: + if not options.threshold: + print("Need option --threshold when using --export.") + exit(1) + threshold_range = tuple([int(n) for n in options.threshold.split(',')]) + + if suffix: + if options.export.endswith('.stl'): + path_ = '{}-{}.stl'.format(options.export[:-4], suffix) + else: + path_ = '{}-{}.stl'.format(options.export, suffix) + else: + path_ = options.export + + export(path_, threshold_range, remove_surface=remove_surfaces) + elif options.export_to_all: + # noinspection PyBroadException + try: + from invesalius.project import Project + + for threshold_name, threshold_range in Project().presets.thresh_ct.iteritems(): + if isinstance(threshold_range[0], int): + path_ = u'{}-{}-{}.stl'.format(options.export_to_all, suffix, threshold_name) + export(path_, threshold_range, remove_surface=True) + except: + traceback.print_exc() + finally: + exit(0) + + +def export(path_, threshold_range, remove_surface=False): + import invesalius.constants as const + + Publisher.sendMessage('Set threshold values', + threshold_range=threshold_range) + + surface_options = { + 'method': { + 'algorithm': 'Default', + 'options': {}, + }, 'options': { + 'index': 0, + 'name': '', + 'quality': _('Optimal *'), + 'fill': False, + 'keep_largest': False, + 'overwrite': False, + } + } + Publisher.sendMessage('Create surface from index', + surface_parameters=surface_options) + Publisher.sendMessage('Export surface to file', + filename=path_, filetype=const.FILETYPE_STL) + if remove_surface: + Publisher.sendMessage('Remove surfaces', + surface_indexes=(0,)) + + +def print_events(topic=Publisher.AUTO_TOPIC, **msg_data): """ Print pubsub messages """ - utils.debug(data.topic) + utils.debug("%s\n\tParameters: %s" % (topic, msg_data)) def main(): """ Initialize InVesalius GUI """ - application = InVesalius(0) - application.MainLoop() + options, args = parse_comand_line() + + if options.no_gui: + non_gui_startup(options, args) + else: + application = InVesalius(0) + application.MainLoop() if __name__ == '__main__': #Is needed because of pyinstaller @@ -316,10 +481,10 @@ if hasattr(sys,"frozen") and sys.platform.startswith('win'): #Click in the .inv3 file support - root = _winreg.HKEY_CLASSES_ROOT + root = winreg.HKEY_CLASSES_ROOT key = "InVesalius 3.1\InstallationDir" - hKey = _winreg.OpenKey (root, key, 0, _winreg.KEY_READ) - value, type_ = _winreg.QueryValueEx (hKey, "") + hKey = winreg.OpenKey (root, key, 0, winreg.KEY_READ) + value, type_ = winreg.QueryValueEx (hKey, "") path = os.path.join(value,'dist') os.chdir(path) diff -Nru invesalius-3.1.1/debian/changelog invesalius-3.1.99991/debian/changelog --- invesalius-3.1.1/debian/changelog 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/changelog 2018-08-17 12:07:48.000000000 +0000 @@ -1,3 +1,15 @@ +invesalius (3.1.99991-1~cosmic) cosmic; urgency=medium + + [ Thiago Franco de Moraes ] + * New upstream version + Closes: #901562 + + [ Andreas Tille ] + * debhelper 11 + * Standards-Version: 4.2.0 + + -- Thiago Franco de Moraes Fri, 17 Aug 2018 14:07:48 +0200 + invesalius (3.1.1-3) unstable; urgency=medium * Further changes concerning #779655 diff -Nru invesalius-3.1.1/debian/compat invesalius-3.1.99991/debian/compat --- invesalius-3.1.1/debian/compat 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/compat 2018-08-17 12:07:48.000000000 +0000 @@ -1 +1 @@ -10 +11 diff -Nru invesalius-3.1.1/debian/control invesalius-3.1.99991/debian/control --- invesalius-3.1.1/debian/control 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/control 2018-08-17 12:07:48.000000000 +0000 @@ -4,15 +4,15 @@ Thiago Franco de Moraes Section: graphics Priority: optional -Build-Depends: debhelper (>= 10), +Build-Depends: debhelper (>= 11~), python-all-dev, cython, dh-python, python-numpy Build-Depends-Indep: python -Standards-Version: 4.1.0 -Vcs-Browser: https://anonscm.debian.org/cgit/debian-med/invesalius.git -Vcs-Git: https://anonscm.debian.org/git/debian-med/invesalius.git +Standards-Version: 4.2.0 +Vcs-Browser: https://salsa.debian.org/med-team/invesalius +Vcs-Git: https://salsa.debian.org/med-team/invesalius.git Homepage: http://www.cti.gov.br/invesalius/ X-Python-Version: 2.7 @@ -23,7 +23,7 @@ python-numpy, python-scipy, python-skimage, - python-wxgtk3.0, + python-wxgtk4.0, python-pil, python-gdcm, python-vtkgdcm, diff -Nru invesalius-3.1.1/debian/patches/10_change_casmoothing_import.patch invesalius-3.1.99991/debian/patches/10_change_casmoothing_import.patch --- invesalius-3.1.1/debian/patches/10_change_casmoothing_import.patch 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/patches/10_change_casmoothing_import.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -Author: Thiago Franco de Moraes -Date: Mon, 10 Dec 2012 14:22:46 -0200 -Description: Upstream has ca_smoothing integrated and Debian have it separated -in another package (python-casmoothing), so the import must be changed. ---- a/invesalius/data/surface.py -+++ b/invesalius/data/surface.py -@@ -37,7 +37,7 @@ import surface_process - import utils as utl - import vtk_utils as vu - try: -- import data.ca_smoothing as ca_smoothing -+ import ca_smoothing - except: - pass - diff -Nru invesalius-3.1.1/debian/patches/10_import_cython_modules.patch invesalius-3.1.99991/debian/patches/10_import_cython_modules.patch --- invesalius-3.1.1/debian/patches/10_import_cython_modules.patch 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/patches/10_import_cython_modules.patch 2018-08-17 12:07:48.000000000 +0000 @@ -52,7 +52,7 @@ --- a/setup.py +++ b/setup.py @@ -11,26 +11,26 @@ - if sys.platform == 'linux2': + if sys.platform.startswith('linux'): setup( cmdclass = {'build_ext': build_ext}, - ext_modules = cythonize([ Extension("invesalius.data.mips", ["invesalius/data/mips.pyx"], diff -Nru invesalius-3.1.1/debian/patches/10_import_mips.patch invesalius-3.1.99991/debian/patches/10_import_mips.patch --- invesalius-3.1.1/debian/patches/10_import_mips.patch 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/patches/10_import_mips.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -Author: Thiago Franco de Moraes -Date: Mon, 28 Apr 2014 11:08:46 -0200 -Description: Upstream has the mips module inside the invesalius source code and -Debian has it separated inside /usr/lib/invesaliu so it was necessary to change -the import -diff --git a/invesalius/data/slice_.py b/invesalius/data/slice_.py -index 4816b96..8ecc872 100644 ---- a/invesalius/data/slice_.py -+++ b/invesalius/data/slice_.py -@@ -33,7 +33,7 @@ import utils - - from mask import Mask - from project import Project --from data import mips -+import mips - - OTHER=0 - PLIST=1 diff -Nru invesalius-3.1.1/debian/patches/10_wxpython3.patch invesalius-3.1.99991/debian/patches/10_wxpython3.patch --- invesalius-3.1.1/debian/patches/10_wxpython3.patch 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/patches/10_wxpython3.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,541 +0,0 @@ -Author: Thiago Franco de Moraes and Olly Betts -Date: Tue, 23 Sep 2014 16:36:46 -0300 -Description: Patches necessary to make InVesalius3 runs using wxPython 3.0 -diff --git a/invesalius/gui/data_notebook.py b/invesalius/gui/data_notebook.py -index eff129a..bf51d19 100644 ---- a/invesalius/gui/data_notebook.py -+++ b/invesalius/gui/data_notebook.py -@@ -22,9 +22,9 @@ import sys - import platform - - try: -- from PIL import Image --except(ImportError): - import Image -+except ImportError: -+ from PIL import Image - - import wx - import wx.grid -@@ -162,16 +162,16 @@ class MeasureButtonControlPanel(wx.Panel): - button_new = pbtn.PlateButton(self, BTN_NEW, "", - BMP_NEW, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - self.button_new = button_new - button_remove = pbtn.PlateButton(self, BTN_REMOVE, "", - BMP_REMOVE, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "", - BMP_DUPLICATE, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - button_duplicate.Disable() - - # Add all controls to gui -@@ -276,15 +276,15 @@ class ButtonControlPanel(wx.Panel): - button_new = pbtn.PlateButton(self, BTN_NEW, "", - BMP_NEW, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - button_remove = pbtn.PlateButton(self, BTN_REMOVE, "", - BMP_REMOVE, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "", - BMP_DUPLICATE, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - - # Add all controls to gui - sizer = wx.BoxSizer(wx.HORIZONTAL) -@@ -444,7 +444,7 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): - self.InsertColumn(1, _("Name")) - self.InsertColumn(2, _("Threshold"), wx.LIST_FORMAT_RIGHT) - -- self.SetColumnWidth(0, 20) -+ self.SetColumnWidth(0, 25) - self.SetColumnWidth(1, 120) - self.SetColumnWidth(2, 90) - -@@ -595,15 +595,15 @@ class SurfaceButtonControlPanel(wx.Panel): - button_new = pbtn.PlateButton(self, BTN_NEW, "", - BMP_NEW, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - button_remove = pbtn.PlateButton(self, BTN_REMOVE, "", - BMP_REMOVE, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "", - BMP_DUPLICATE, - style=button_style, -- size = wx.Size(18, 18)) -+ size = wx.Size(24, 20)) - - # Add all controls to gui - sizer = wx.BoxSizer(wx.HORIZONTAL) -@@ -768,7 +768,7 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): - self.InsertColumn(2, _(u"Volume (mm³)")) - self.InsertColumn(3, _("Transparency"), wx.LIST_FORMAT_RIGHT) - -- self.SetColumnWidth(0, 20) -+ self.SetColumnWidth(0, 25) - self.SetColumnWidth(1, 85) - self.SetColumnWidth(2, 85) - self.SetColumnWidth(3, 80) -@@ -1008,7 +1008,7 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): - self.InsertColumn(3, _("Type")) - self.InsertColumn(4, _("Value"), wx.LIST_FORMAT_RIGHT) - -- self.SetColumnWidth(0, 20) -+ self.SetColumnWidth(0, 25) - self.SetColumnWidth(1, 65) - self.SetColumnWidth(2, 55) - self.SetColumnWidth(3, 50) -@@ -1199,7 +1199,7 @@ class AnnotationsListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): - self.InsertColumn(2, _("Type"), wx.LIST_FORMAT_CENTER) - self.InsertColumn(3, _("Value")) - -- self.SetColumnWidth(0, 20) -+ self.SetColumnWidth(0, 25) - self.SetColumnWidth(1, 90) - self.SetColumnWidth(2, 50) - self.SetColumnWidth(3, 120) -diff --git a/invesalius/gui/default_tasks.py b/invesalius/gui/default_tasks.py -index 9c38ba4..7725073 100644 ---- a/invesalius/gui/default_tasks.py -+++ b/invesalius/gui/default_tasks.py -@@ -113,10 +113,7 @@ class Panel(wx.Panel): - # Lower fold panel - class LowerTaskPanel(wx.Panel): - def __init__(self, parent): -- wx.Panel.__init__(self, parent, pos=wx.Point(5, 5), -- # size=wx.Size(280, 700)) -- size=wx.Size(280, 420)) -- -+ wx.Panel.__init__(self, parent) - fold_panel = fpb.FoldPanelBar(self, -1, wx.DefaultPosition, - self.GetSize(),FPB_DEFAULT_STYLE, - fpb.FPB_COLLAPSE_TO_BOTTOM) -@@ -182,9 +179,7 @@ class LowerTaskPanel(wx.Panel): - # Upper fold panel - class UpperTaskPanel(wx.Panel): - def __init__(self, parent): -- wx.Panel.__init__(self, parent, pos=wx.Point(5, 5), -- size=wx.Size(280, 656)) -- -+ wx.Panel.__init__(self, parent) - fold_panel = fpb.FoldPanelBar(self, -1, wx.DefaultPosition, - self.GetSize(),FPB_DEFAULT_STYLE, - fpb.FPB_SINGLE_FOLD) -@@ -210,7 +205,8 @@ class UpperTaskPanel(wx.Panel): - tasks = [(_("Load data"), importer.TaskPanel), - (_("Select region of interest"), slice_.TaskPanel), - (_("Configure 3D surface"), surface.TaskPanel), -- (_("Export data"), exporter.TaskPanel)] -+ (_("Export data"), exporter.TaskPanel) -+ ] - elif int(session.mode) == const.MODE_NAVIGATOR: - tasks = [(_("Load data"), importer.TaskPanel), - (_("Select region of interest"), slice_.TaskPanel), -diff --git a/invesalius/gui/default_viewers.py b/invesalius/gui/default_viewers.py -index 9d8a3ef..c31f801 100644 ---- a/invesalius/gui/default_viewers.py -+++ b/invesalius/gui/default_viewers.py -@@ -330,7 +330,7 @@ class VolumeViewerCover(wx.Panel): - - class VolumeToolPanel(wx.Panel): - def __init__(self, parent): -- wx.Panel.__init__(self, parent, size = (10,100)) -+ wx.Panel.__init__(self, parent) - - # VOLUME RAYCASTING BUTTON - BMP_RAYCASTING = wx.Bitmap("../icons/volume_raycasting.png", -@@ -346,15 +346,15 @@ class VolumeToolPanel(wx.Panel): - - button_raycasting = pbtn.PlateButton(self, BUTTON_RAYCASTING,"", - BMP_RAYCASTING, style=pbtn.PB_STYLE_SQUARE, -- size=(24,24)) -+ size=(32,32)) - - button_stereo = pbtn.PlateButton(self, BUTTON_3D_STEREO,"", - BMP_3D_STEREO, style=pbtn.PB_STYLE_SQUARE, -- size=(24,24)) -+ size=(32,32)) - - button_slice_plane = self.button_slice_plane = pbtn.PlateButton(self, BUTTON_SLICE_PLANE,"", - BMP_SLICE_PLANE, style=pbtn.PB_STYLE_SQUARE, -- size=(24,24)) -+ size=(32,32)) - - self.button_raycasting = button_raycasting - self.button_stereo = button_stereo -@@ -363,13 +363,13 @@ class VolumeToolPanel(wx.Panel): - BMP_FRONT = wx.Bitmap(ID_TO_BMP[const.VOL_FRONT][1], - wx.BITMAP_TYPE_PNG) - button_view = pbtn.PlateButton(self, BUTTON_VIEW, "", -- BMP_FRONT, size=(24,24), -+ BMP_FRONT, size=(32,32), - style=pbtn.PB_STYLE_SQUARE) - self.button_view = button_view - - # VOLUME COLOUR BUTTON - if sys.platform == 'linux2': -- size = (28,28) -+ size = (32,32) - sp = 2 - else: - size = (24,24) -diff --git a/invesalius/gui/dialogs.py b/invesalius/gui/dialogs.py -index c6d7271..af1dc66 100644 ---- a/invesalius/gui/dialogs.py -+++ b/invesalius/gui/dialogs.py -@@ -214,7 +214,7 @@ def ShowOpenProjectDialog(): - dlg = wx.FileDialog(None, message=_("Open InVesalius 3 project..."), - defaultDir="", - defaultFile="", wildcard=WILDCARD_OPEN, -- style=wx.OPEN|wx.CHANGE_DIR) -+ style=wx.FD_OPEN|wx.FD_CHANGE_DIR) - - # inv3 filter is default - dlg.SetFilterIndex(0) -@@ -242,7 +242,7 @@ def ShowOpenAnalyzeDialog(): - dlg = wx.FileDialog(None, message=_("Open Analyze file"), - defaultDir="", - defaultFile="", wildcard=WILDCARD_ANALYZE, -- style=wx.OPEN|wx.CHANGE_DIR) -+ style=wx.FD_OPEN|wx.FD_CHANGE_DIR) - - # inv3 filter is default - dlg.SetFilterIndex(0) -@@ -312,7 +312,7 @@ def ShowSaveAsProjectDialog(default_filename=None): - "", # last used directory - default_filename, - _("InVesalius project (*.inv3)|*.inv3"), -- wx.SAVE|wx.OVERWRITE_PROMPT) -+ wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) - #dlg.SetFilterIndex(0) # default is VTI - - filename = None -@@ -779,7 +779,8 @@ def ShowAboutDialog(parent): - "Tatiana Al-Chueyr (former)", - "Guilherme Cesar Soares Ruppert (former)", - "Fabio de Souza Azevedo (former)", -- "Bruno Lara Bottazzini (contributor)"] -+ "Bruno Lara Bottazzini (contributor)", -+ "Olly Betts (patches to support wxPython3)"] - - info.Translators = ["Alex P. Natsios", - "Andreas Loupasakis", -@@ -972,7 +973,7 @@ def ExportPicture(type_=""): - "", # last used directory - project_name, # filename - WILDCARD_SAVE_PICTURE, -- wx.SAVE|wx.OVERWRITE_PROMPT) -+ wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) - dlg.SetFilterIndex(1) # default is VTI - - if dlg.ShowModal() == wx.ID_OK: -diff --git a/invesalius/gui/frame.py b/invesalius/gui/frame.py -index 9f5b5c4..458e994 100644 ---- a/invesalius/gui/frame.py -+++ b/invesalius/gui/frame.py -@@ -68,6 +68,7 @@ class Frame(wx.Frame): - if sys.platform != 'darwin': - self.Maximize() - -+ self.sizeChanged = True - #Necessary update AUI (statusBar in special) - #when maximized in the Win 7 and XP - self.SetSize(self.GetSize()) -@@ -114,6 +115,7 @@ class Frame(wx.Frame): - Bind normal events from wx (except pubsub related). - """ - self.Bind(wx.EVT_SIZE, self.OnSize) -+ self.Bind(wx.EVT_IDLE, self.OnIdle) - self.Bind(wx.EVT_MENU, self.OnMenuClick) - self.Bind(wx.EVT_CLOSE, self.OnClose) - -@@ -375,8 +377,17 @@ class Frame(wx.Frame): - """ - Refresh GUI when frame is resized. - """ -- Publisher.sendMessage(('ProgressBar Reposition')) - evt.Skip() -+ self.Reposition() -+ self.sizeChanged = True -+ -+ def OnIdle(self, evt): -+ if self.sizeChanged: -+ self.Reposition() -+ -+ def Reposition(self): -+ Publisher.sendMessage(('ProgressBar Reposition')) -+ self.sizeChanged = False - - def ShowPreferences(self): - -@@ -560,13 +571,13 @@ class MenuBar(wx.MenuBar): - - file_edit_item_undo = wx.MenuItem(file_edit, wx.ID_UNDO, _("Undo\tCtrl+Z")) - file_edit_item_undo.SetBitmap(self.BMP_UNDO) -- file_edit_item_undo.Enable(False) - file_edit.AppendItem(file_edit_item_undo) -+ file_edit_item_undo.Enable(False) - - file_edit_item_redo = wx.MenuItem(file_edit, wx.ID_REDO, _("Redo\tCtrl+Y")) - file_edit_item_redo.SetBitmap(self.BMP_REDO) -- file_edit_item_redo.Enable(False) - file_edit.AppendItem(file_edit_item_redo) -+ file_edit_item_redo.Enable(False) - else: - file_edit.Append(wx.ID_UNDO, _("Undo\tCtrl+Z")).Enable(False) - file_edit.Append(wx.ID_REDO, _("Redo\tCtrl+Y")).Enable(False) -@@ -691,9 +702,10 @@ class ProgressBar(wx.Gauge): - """ - Compute new size and position, according to parent resize - """ -- rect = self.parent.GetFieldRect(2) -+ rect = self.Parent.GetFieldRect(2) - self.SetPosition((rect.x + 2, rect.y + 2)) - self.SetSize((rect.width - 4, rect.height - 4)) -+ self.Show() - - def SetPercentage(self, value): - """ -diff --git a/invesalius/gui/import_panel.py b/invesalius/gui/import_panel.py -index c52b21f..2ea7af2 100644 ---- a/invesalius/gui/import_panel.py -+++ b/invesalius/gui/import_panel.py -@@ -110,16 +110,16 @@ class InnerPanel(wx.Panel): - - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(splitter, 20, wx.EXPAND) -- sizer.Add(panel, 1, wx.EXPAND|wx.LEFT, 90) -- -- self.SetSizer(sizer) -- sizer.Fit(self) -+ sizer.Add(panel, 0, wx.EXPAND|wx.LEFT, 90) - - self.text_panel = TextPanel(splitter) - splitter.AppendWindow(self.text_panel, 250) - - self.image_panel = ImagePanel(splitter) - splitter.AppendWindow(self.image_panel, 250) -+ -+ self.SetSizer(sizer) -+ sizer.Fit(self) - - self.Layout() - self.Update() -diff --git a/invesalius/gui/task_exporter.py b/invesalius/gui/task_exporter.py -index e11620d..ecd293b 100644 ---- a/invesalius/gui/task_exporter.py -+++ b/invesalius/gui/task_exporter.py -@@ -160,26 +160,28 @@ class InnerTaskPanel(wx.Panel): - if sys.platform == 'darwin': - BMP_EXPORT_SURFACE = wx.Bitmap(\ - "../icons/surface_export_original.png", -- wx.BITMAP_TYPE_PNG) -+ wx.BITMAP_TYPE_PNG).ConvertToImage()\ -+ .Rescale(25, 25).ConvertToBitmap() - BMP_TAKE_PICTURE = wx.Bitmap(\ - "../icons/tool_photo_original.png", -- wx.BITMAP_TYPE_PNG) -+ wx.BITMAP_TYPE_PNG).ConvertToImage()\ -+ .Rescale(25, 25).ConvertToBitmap() -+ - #BMP_EXPORT_MASK = wx.Bitmap("../icons/mask.png", - # wx.BITMAP_TYPE_PNG) - else: - BMP_EXPORT_SURFACE = wx.Bitmap("../icons/surface_export.png", -- wx.BITMAP_TYPE_PNG) -+ wx.BITMAP_TYPE_PNG).ConvertToImage()\ -+ .Rescale(25, 25).ConvertToBitmap() -+ - BMP_TAKE_PICTURE = wx.Bitmap("../icons/tool_photo.png", -- wx.BITMAP_TYPE_PNG) -+ wx.BITMAP_TYPE_PNG).ConvertToImage()\ -+ .Rescale(25, 25).ConvertToBitmap() -+ - #BMP_EXPORT_MASK = wx.Bitmap("../icons/mask_small.png", - # wx.BITMAP_TYPE_PNG) - - -- bmp_list = [BMP_TAKE_PICTURE, BMP_EXPORT_SURFACE]#, -- # BMP_EXPORT_MASK] -- for bmp in bmp_list: -- bmp.SetWidth(25) -- bmp.SetHeight(25) - - # Buttons related to hyperlinks - button_style = pbtn.PB_STYLE_SQUARE | pbtn.PB_STYLE_DEFAULT -@@ -276,7 +278,7 @@ class InnerTaskPanel(wx.Panel): - "", # last used directory - project_name, # filename - WILDCARD_SAVE_MASK, -- wx.SAVE|wx.OVERWRITE_PROMPT) -+ wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) - dlg.SetFilterIndex(0) # default is VTI - - if dlg.ShowModal() == wx.ID_OK: -@@ -312,7 +314,7 @@ class InnerTaskPanel(wx.Panel): - "", # last used directory - project_name, # filename - WILDCARD_SAVE_3D, -- wx.SAVE|wx.OVERWRITE_PROMPT) -+ wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) - dlg.SetFilterIndex(3) # default is STL - - if dlg.ShowModal() == wx.ID_OK: -diff --git a/invesalius/gui/task_importer.py b/invesalius/gui/task_importer.py -index 8dad9fe..0cf763a 100644 ---- a/invesalius/gui/task_importer.py -+++ b/invesalius/gui/task_importer.py -@@ -94,9 +94,9 @@ class InnerTaskPanel(wx.Panel): - BMP_OPEN_PROJECT = wx.Bitmap("../icons/file_open.png", wx.BITMAP_TYPE_PNG) - - bmp_list = [BMP_IMPORT, BMP_NET, BMP_OPEN_PROJECT] -- for bmp in bmp_list: -- bmp.SetWidth(25) -- bmp.SetHeight(25) -+ #for bmp in bmp_list: -+ #bmp.SetWidth(25) -+ #bmp.SetHeight(25) - - # Buttons related to hyperlinks - button_style = pbtn.PB_STYLE_SQUARE | pbtn.PB_STYLE_DEFAULT -diff --git a/invesalius/gui/task_slice.py b/invesalius/gui/task_slice.py -index ea81462..78ff21d 100644 ---- a/invesalius/gui/task_slice.py -+++ b/invesalius/gui/task_slice.py -@@ -70,8 +70,8 @@ class InnerTaskPanel(wx.Panel): - - # Image(s) for buttons - BMP_ADD = wx.Bitmap("../icons/object_add.png", wx.BITMAP_TYPE_PNG) -- BMP_ADD.SetWidth(25) -- BMP_ADD.SetHeight(25) -+ #BMP_ADD.SetWidth(25) -+ #BMP_ADD.SetHeight(25) - - # Button for creating new surface - button_new_mask = pbtn.PlateButton(self, BTN_NEW, "", BMP_ADD, style=\ -@@ -126,7 +126,7 @@ class InnerTaskPanel(wx.Panel): - # Add line sizers into main sizer - main_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.Add(line_new, 0,wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) -- main_sizer.Add(fold_panel, 6, wx.GROW|wx.EXPAND|wx.ALL, 5) -+ main_sizer.Add(fold_panel, 5, wx.GROW|wx.EXPAND|wx.ALL, 5) - main_sizer.AddSizer(line_sizer, 1, wx.GROW|wx.EXPAND) - main_sizer.Fit(self) - -@@ -313,7 +313,7 @@ class MaskProperties(wx.Panel): - # Combo related to mask naem - combo_mask_name = wx.ComboBox(self, -1, "", choices= MASK_LIST, - style=wx.CB_DROPDOWN|wx.CB_READONLY) -- combo_mask_name.SetSelection(0) # wx.CB_SORT -+ #combo_mask_name.SetSelection(0) # wx.CB_SORT - if sys.platform != 'win32': - combo_mask_name.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) - self.combo_mask_name = combo_mask_name -@@ -327,19 +327,21 @@ class MaskProperties(wx.Panel): - line1.Add(combo_mask_name, 1, wx.EXPAND|wx.GROW|wx.TOP|wx.RIGHT, 2) - line1.Add(button_colour, 0, wx.TOP|wx.LEFT|wx.RIGHT, 2) - -- ## LINE 2 -+ ### LINE 2 - text_thresh = wx.StaticText(self, -1, - _("Set predefined or manual threshold:")) - -- ## LINE 3 -- combo_thresh = wx.ComboBox(self, -1, "", size=(15,-1), -- choices=[],#THRESHOLD_LIST -+ ### LINE 3 -+ THRESHOLD_LIST = ["",] -+ combo_thresh = wx.ComboBox(self, -1, "", #size=(15,-1), -+ choices=THRESHOLD_LIST, - style=wx.CB_DROPDOWN|wx.CB_READONLY) - combo_thresh.SetSelection(0) - if sys.platform != 'win32': - combo_thresh.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) - self.combo_thresh = combo_thresh - -+ - ## LINE 4 - gradient = grad.GradientCtrl(self, -1, -5000, 5000, 0, 5000, - (0, 255, 0, 100)) -@@ -347,9 +349,9 @@ class MaskProperties(wx.Panel): - - # Add all lines into main sizer - sizer = wx.BoxSizer(wx.VERTICAL) -- sizer.Add(line1, 1, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) -- sizer.Add(text_thresh, 1, wx.GROW|wx.EXPAND|wx.ALL, 5) -- sizer.Add(combo_thresh, 1, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT, 5) -+ sizer.Add(line1, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) -+ sizer.Add(text_thresh, 0, wx.GROW|wx.EXPAND|wx.ALL, 5) -+ sizer.Add(combo_thresh, 0, wx.EXPAND|wx.GROW|wx.TOP|wx.RIGHT, 2) - sizer.Add(gradient, 1, wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT|wx.BOTTOM, 6) - sizer.Fit(self) - -diff --git a/invesalius/gui/task_surface.py b/invesalius/gui/task_surface.py -index 354314f..6166caf 100644 ---- a/invesalius/gui/task_surface.py -+++ b/invesalius/gui/task_surface.py -@@ -73,8 +73,8 @@ class InnerTaskPanel(wx.Panel): - - - BMP_ADD = wx.Bitmap("../icons/object_add.png", wx.BITMAP_TYPE_PNG) -- BMP_ADD.SetWidth(25) -- BMP_ADD.SetHeight(25) -+ #BMP_ADD.SetWidth(25) -+ #BMP_ADD.SetHeight(25) - - # Button for creating new surface - button_new_surface = pbtn.PlateButton(self, BTN_NEW, "", BMP_ADD, style=\ -@@ -384,7 +384,8 @@ class SurfaceProperties(wx.Panel): - ## LINE 1 - - # Combo related to mask naem -- combo_surface_name = wx.ComboBox(self, -1, "", choices= self.surface_dict.keys(), -+ combo_surface_name = wx.ComboBox(self, -1, "", choices= -+ self.surface_dict.keys() or ["", ], - style=wx.CB_DROPDOWN|wx.CB_READONLY) - combo_surface_name.SetSelection(0) - if sys.platform != 'win32': -@@ -533,7 +534,9 @@ class QualityAdjustment(wx.Panel): - - # LINE 1 - -- combo_quality = wx.ComboBox(self, -1, "", choices=const.SURFACE_QUALITY.keys(), -+ combo_quality = wx.ComboBox(self, -1, "", -+ choices=const.SURFACE_QUALITY.keys() or -+ ["", ], - style=wx.CB_DROPDOWN|wx.CB_READONLY) - combo_quality.SetSelection(3) - combo_quality.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) -diff --git a/invesalius/invesalius.py b/invesalius/invesalius.py -index 883f090..5d2d4f1 100755 ---- a/invesalius/invesalius.py -+++ b/invesalius/invesalius.py -@@ -30,8 +30,9 @@ if sys.platform == 'win32': - else: - if sys.platform != 'darwin': - import wxversion -- wxversion.ensureMinimal('2.8-unicode', optionsRequired=True) -- wxversion.select('2.8-unicode', optionsRequired=True) -+ #wxversion.ensureMinimal('2.8-unicode', optionsRequired=True) -+ #wxversion.select('2.8-unicode', optionsRequired=True) -+ wxversion.ensureMinimal('3.0') - - import wx - #from wx.lib.pubsub import setupv1 #new wx diff -Nru invesalius-3.1.1/debian/patches/20_fix_division_by_zero.patch invesalius-3.1.99991/debian/patches/20_fix_division_by_zero.patch --- invesalius-3.1.1/debian/patches/20_fix_division_by_zero.patch 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/patches/20_fix_division_by_zero.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -Description: Avoid division by zero in progress display -Author: Gert Wollny -Debian-Bug: https://bugs.debian.org/779655 - ---- a/invesalius/data/vtk_utils.py -+++ b/invesalius/data/vtk_utils.py -@@ -49,7 +49,10 @@ - - # when the pipeline is larger than 1, we have to consider this object - # percentage -- ratio = (100.0 / number_of_filters) -+ if number_of_filters > 0: -+ ratio = (100.0 / number_of_filters) -+ else: -+ ratio = 100.0 - - def UpdateProgress(obj, label=""): - """ diff -Nru invesalius-3.1.1/debian/patches/20_fix_one_slice_import.patch invesalius-3.1.99991/debian/patches/20_fix_one_slice_import.patch --- invesalius-3.1.1/debian/patches/20_fix_one_slice_import.patch 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/patches/20_fix_one_slice_import.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -Description: Fix import of dicom serie with one unique slice. -Author: Thiago Franco de Moraes -Debian-Bug: https://bugs.debian.org/779655 - ---- a/invesalius/control.py -+++ b/invesalius/control.py -@@ -408,6 +408,10 @@ class Controller(): - - def OnLoadImportPanel(self, evt): - patient_series = evt.data -+ if self.progress_dialog: -+ self.progress_dialog.Close() -+ self.progress_dialog = None -+ - ok = self.LoadImportPanel(patient_series) - if ok: - Publisher.sendMessage('Show import panel') ---- a/invesalius/data/imagedata_utils.py -+++ b/invesalius/data/imagedata_utils.py -@@ -523,7 +523,8 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): - returns it and its related filename. - """ - message = _("Generating multiplanar visualization...") -- update_progress= vtk_utils.ShowProgress(len(files) - 1, dialog_type = "ProgressDialog") -+ if len(files) > 1: -+ update_progress= vtk_utils.ShowProgress(len(files) - 1, dialog_type = "ProgressDialog") - - temp_file = tempfile.mktemp() - -@@ -584,7 +585,8 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): - else: - array.shape = matrix.shape[1], matrix.shape[2] - matrix[n] = array -- update_progress(cont,message) -+ if len(files) > 1: -+ update_progress(cont,message) - cont += 1 - - matrix.flush() ---- a/invesalius/data/slice_.py -+++ b/invesalius/data/slice_.py -@@ -633,7 +633,7 @@ class Slice(object): - if np.any(self.q_orientation[1::]): - transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, self.interp_method, self.matrix.min(), tmp_array) - if self._type_projection == const.PROJECTION_NORMAL: -- n_image = tmp_array.squeeze() -+ n_image = tmp_array[0] - else: - if inverted: - tmp_array = tmp_array[::-1] -@@ -681,7 +681,7 @@ class Slice(object): - transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, self.interp_method, self.matrix.min(), tmp_array) - - if self._type_projection == const.PROJECTION_NORMAL: -- n_image = tmp_array.squeeze() -+ n_image = tmp_array[:, 0, :] - else: - #if slice_number == 0: - #slice_number = 1 -@@ -731,7 +731,7 @@ class Slice(object): - transforms.apply_view_matrix_transform(self.matrix, self.spacing, M, slice_number, orientation, self.interp_method, self.matrix.min(), tmp_array) - - if self._type_projection == const.PROJECTION_NORMAL: -- n_image = tmp_array.squeeze() -+ n_image = tmp_array[:, :, 0] - else: - if inverted: - tmp_array = tmp_array[:, :, ::-1] ---- a/invesalius/gui/import_panel.py -+++ b/invesalius/gui/import_panel.py -@@ -186,11 +186,8 @@ class InnerPanel(wx.Panel): - slice_amont = group.nslices - - nslices_result = slice_amont / (interval + 1) -- if (nslices_result > 1): -- Publisher.sendMessage('Open DICOM group', (group, interval, -- [self.first_image_selection, self.last_image_selection])) -- else: -- dlg.MissingFilesForReconstruction() -+ Publisher.sendMessage('Open DICOM group', (group, interval, -+ [self.first_image_selection, self.last_image_selection])) - - class TextPanel(wx.Panel): - def __init__(self, parent): diff -Nru invesalius-3.1.1/debian/patches/series invesalius-3.1.99991/debian/patches/series --- invesalius-3.1.1/debian/patches/series 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/patches/series 2018-08-17 12:07:48.000000000 +0000 @@ -1,3 +1,2 @@ 10_sample_path.patch 10_import_cython_modules.patch -20_fix_one_slice_import.patch diff -Nru invesalius-3.1.1/debian/rules invesalius-3.1.99991/debian/rules --- invesalius-3.1.1/debian/rules 2017-09-15 15:18:48.000000000 +0000 +++ invesalius-3.1.99991/debian/rules 2018-08-17 12:07:48.000000000 +0000 @@ -40,6 +40,7 @@ create-launcher: echo '#!/bin/sh' > invesalius3 echo 'export PYTHONPATH=$$PYTHONPATH:"/usr/lib/invesalius"' >> invesalius3 + echo 'export PYTHONPATH=$$PYTHONPATH:"/usr/lib/python2.7/dist-packages/wxPython-4.0.1-py2.7-linux-amd64.egg"' >> invesalius3 echo 'export INVESALIUS_LIBRARY_PATH="/usr/share/invesalius/"' >> invesalius3 echo 'cd $$INVESALIUS_LIBRARY_PATH' >> invesalius3 echo 'python app.py $$@' >> invesalius3 diff -Nru invesalius-3.1.1/Dockerfile invesalius-3.1.99991/Dockerfile --- invesalius-3.1.1/Dockerfile 1970-01-01 00:00:00.000000000 +0000 +++ invesalius-3.1.99991/Dockerfile 2018-08-17 16:41:39.000000000 +0000 @@ -0,0 +1,30 @@ +FROM ubuntu:16.04 + +RUN apt-get update +RUN apt-get install -y \ + cython \ + locales \ + python-concurrent.futures \ + python-gdcm \ + python-matplotlib \ + python-nibabel \ + python-numpy \ + python-pil \ + python-psutil \ + python-scipy \ + python-serial \ + python-skimage \ + python-vtk6 \ + python-vtkgdcm \ + python-wxgtk3.0 + +RUN locale-gen en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +WORKDIR /usr/local/app + +COPY . . + +RUN python setup.py build_ext --inplace diff -Nru invesalius-3.1.1/.dockerignore invesalius-3.1.99991/.dockerignore --- invesalius-3.1.1/.dockerignore 1970-01-01 00:00:00.000000000 +0000 +++ invesalius-3.1.99991/.dockerignore 2018-08-17 16:41:39.000000000 +0000 @@ -0,0 +1,3 @@ +.git* +.python-version +Dockerfile diff -Nru invesalius-3.1.1/docs/devel/example_singleton_pubsub.py invesalius-3.1.99991/docs/devel/example_singleton_pubsub.py --- invesalius-3.1.1/docs/devel/example_singleton_pubsub.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/docs/devel/example_singleton_pubsub.py 2018-08-17 16:41:39.000000000 +0000 @@ -52,7 +52,7 @@ people = [p1, p2, p3] print "Everyone eats 2 pieces:" -for i in xrange(2): +for i in range(2): for person in people: person.EatPieceOfPizza() Binary files /tmp/tmpzIbyXT/Keg7HLuK6t/invesalius-3.1.1/icons/target.png and /tmp/tmpzIbyXT/nBkSI9neJD/invesalius-3.1.99991/icons/target.png differ diff -Nru invesalius-3.1.1/invesalius/constants.py invesalius-3.1.99991/invesalius/constants.py --- invesalius-3.1.1/invesalius/constants.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/constants.py 2018-08-17 16:41:39.000000000 +0000 @@ -23,9 +23,13 @@ import wx import itertools +from invesalius import utils + #from invesalius.project import Project INVESALIUS_VERSION = "3.1.1" +INVESALIUS_ACTUAL_FORMAT_VERSION = 1.1 + #--------------- # Measurements @@ -56,6 +60,7 @@ TEXT_SIZE_SMALL = 11 TEXT_SIZE = 12 TEXT_SIZE_LARGE = 16 +TEXT_SIZE_EXTRA_LARGE = 20 TEXT_COLOUR = (1,1,1) (X,Y) = (0.03, 0.97) @@ -329,15 +334,15 @@ try: USER_DIR = expand_user() except: - USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + USER_DIR = utils.decode(os.path.expanduser('~'), FS_ENCODE) else: - USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + USER_DIR = utils.decode(os.path.expanduser('~'), FS_ENCODE) USER_INV_DIR = os.path.join(USER_DIR, u'.invesalius') USER_PRESET_DIR = os.path.join(USER_INV_DIR, u'presets') USER_LOG_DIR = os.path.join(USER_INV_DIR, u'logs') -FILE_PATH = os.path.split(__file__)[0].decode(FS_ENCODE) +FILE_PATH = utils.decode(os.path.split(__file__)[0], FS_ENCODE) if hasattr(sys,"frozen") and (sys.frozen == "windows_exe"\ or sys.frozen == "console_exe"): @@ -541,6 +546,8 @@ ID_THRESHOLD_SEGMENTATION = wx.NewId() ID_FLOODFILL_SEGMENTATION = wx.NewId() ID_CROP_MASK = wx.NewId() +ID_CREATE_SURFACE = wx.NewId() +ID_CREATE_MASK = wx.NewId() #--------------------------------------------------------- STATE_DEFAULT = 1000 @@ -678,6 +685,10 @@ DEFAULT_REF_MODE = DYNAMIC_REF REF_MODE = [_("Static ref."), _("Dynamic ref.")] +DEFAULT_COIL = SELECT +COIL = [_("Select coil:"), _("Neurosoft Figure-8"), + _("Magstim 70 mm"), _("Nexstim")] + IR1 = wx.NewId() IR2 = wx.NewId() IR3 = wx.NewId() @@ -694,19 +705,48 @@ IR2: {1: 'REI'}, IR3: {2: 'NAI'}} -TIPS_IMG = [wx.ToolTip(_("Select left ear in image")), - wx.ToolTip(_("Select right ear in image")), - wx.ToolTip(_("Select nasion in image"))] +TIPS_IMG = [_("Select left ear in image"), + _("Select right ear in image"), + _("Select nasion in image")] BTNS_TRK = {TR1: {3: _('LET')}, TR2: {4: _('RET')}, TR3: {5: _('NAT')}, SET: {6: _('SET')}} -TIPS_TRK = [wx.ToolTip(_("Select left ear with spatial tracker")), - wx.ToolTip(_("Select right ear with spatial tracker")), - wx.ToolTip(_("Select nasion with spatial tracker")), - wx.ToolTip(_("Show set coordinates in image"))] +TIPS_TRK = [_("Select left ear with spatial tracker"), + _("Select right ear with spatial tracker"), + _("Select nasion with spatial tracker"), + _("Show set coordinates in image")] + +OBJL = wx.NewId() +OBJR = wx.NewId() +OBJA = wx.NewId() +OBJC = wx.NewId() +OBJF = wx.NewId() + +BTNS_OBJ = {OBJL: {0: _('Left')}, + OBJR: {1: _('Right')}, + OBJA: {2: _('Anterior')}, + OBJC: {3: _('Center')}, + OBJF: {4: _('Fixed')}} + +TIPS_OBJ = [_("Select left object fiducial"), + _("Select right object fiducial"), + _("Select anterior object fiducial"), + _("Select object center"), + _("Attach sensor to object")] CAL_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'CalibrationFiles')) MAR_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'Markers')) + +#OBJECT TRACKING +OBJ_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'objects')) +ARROW_SCALE = 3 +ARROW_UPPER_LIMIT = 30 +#COIL_ANGLES_THRESHOLD = 3 * ARROW_SCALE +COIL_ANGLES_THRESHOLD = 3 +COIL_COORD_THRESHOLD = 3 +TIMESTAMP = 2.0 + +CAM_MODE = True diff -Nru invesalius-3.1.1/invesalius/control.py invesalius-3.1.99991/invesalius/control.py --- invesalius-3.1.1/invesalius/control.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/control.py 2018-08-17 16:41:39.000000000 +0000 @@ -68,6 +68,7 @@ def __bind_events(self): Publisher.subscribe(self.OnImportMedicalImages, 'Import directory') + Publisher.subscribe(self.OnImportGroup, 'Import group') Publisher.subscribe(self.OnShowDialogImportDirectory, 'Show import directory dialog') Publisher.subscribe(self.OnShowDialogImportOtherFiles, @@ -105,40 +106,40 @@ Publisher.subscribe(self.SetBitmapSpacing, 'Set bitmap spacing') - def SetBitmapSpacing(self, pubsub_evt): + Publisher.subscribe(self.OnSaveProject, 'Save project') + + def SetBitmapSpacing(self, spacing): proj = prj.Project() - proj.spacing = pubsub_evt.data + proj.spacing = spacing - def OnCancelImport(self, pubsub_evt): + def OnCancelImport(self): #self.cancel_import = True Publisher.sendMessage('Hide import panel') - def OnCancelImportBitmap(self, pubsub_evt): + def OnCancelImportBitmap(self): #self.cancel_import = True Publisher.sendMessage('Hide import bitmap panel') ########################### ########################### - def OnShowDialogImportDirectory(self, pubsub_evt): + def OnShowDialogImportDirectory(self): self.ShowDialogImportDirectory() - def OnShowDialogImportOtherFiles(self, pubsub_evt): - id_type = pubsub_evt.data + def OnShowDialogImportOtherFiles(self, id_type): self.ShowDialogImportOtherFiles(id_type) - def OnShowDialogOpenProject(self, pubsub_evt): + def OnShowDialogOpenProject(self): self.ShowDialogOpenProject() - def OnShowDialogSaveProject(self, pubsub_evt): - saveas = pubsub_evt.data - self.ShowDialogSaveProject(saveas) + def OnShowDialogSaveProject(self, save_as): + self.ShowDialogSaveProject(save_as) - def OnShowDialogCloseProject(self, pubsub_evt): + def OnShowDialogCloseProject(self): self.ShowDialogCloseProject() - def OnShowBitmapFile(self, pubsub_evt): + def OnShowBitmapFile(self): self.ShowDialogImportBitmapFile() ########################### @@ -152,10 +153,10 @@ if answer: self.ShowDialogSaveProject() self.CloseProject() - #Publisher.sendMessage("Enable state project", False) + #Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") - Publisher.sendMessage("Set slice interaction style", const.STATE_DEFAULT) + Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT) # Import TIFF, BMP, JPEG or PNG dirpath = dialog.ShowImportBitmapDirDialog(self.frame) @@ -176,17 +177,16 @@ if answer: self.ShowDialogSaveProject() self.CloseProject() - #Publisher.sendMessage("Enable state project", False) + #Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") - Publisher.sendMessage("Set slice interaction style", const.STATE_DEFAULT) + Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT) # Import project dirpath = dialog.ShowImportDirDialog(self.frame) if dirpath and not os.listdir(dirpath): dialog.ImportEmptyDirectory(dirpath) elif dirpath: self.StartImportPanel(dirpath) - Publisher.sendMessage("Load data to import panel", dirpath) def ShowDialogImportOtherFiles(self, id_type): # Offer to save current project if necessary @@ -198,17 +198,17 @@ if answer: self.ShowDialogSaveProject() self.CloseProject() - # Publisher.sendMessage("Enable state project", False) + # Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") - Publisher.sendMessage("Set slice interaction style", const.STATE_DEFAULT) + Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT) # Warning for limited support to Analyze format if id_type == const.ID_ANALYZE_IMPORT: dialog.ImportAnalyzeWarning() filepath = dialog.ShowImportOtherFilesDialog(id_type) - Publisher.sendMessage("Open other files", filepath) + Publisher.sendMessage("Open other files", filepath=filepath) def ShowDialogOpenProject(self): # Offer to save current project if necessary @@ -231,17 +231,19 @@ session = ses.Session() if saveas or session.temp_item: proj = prj.Project() - filepath = dialog.ShowSaveAsProjectDialog(proj.name) + filepath, compress = dialog.ShowSaveAsProjectDialog(proj.name) if filepath: #session.RemoveTemp() session.OpenProject(filepath) else: return else: + proj = prj.Project() + compress = proj.compress dirpath, filename = session.project_path filepath = os.path.join(dirpath, filename) - self.SaveProject(filepath) + self.SaveProject(filepath, compress) def ShowDialogCloseProject(self): @@ -261,21 +263,21 @@ if not answer: utils.debug("Close without changes") self.CloseProject() - Publisher.sendMessage("Enable state project", False) + Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") elif answer == 1: self.ShowDialogSaveProject() utils.debug("Save changes and close") self.CloseProject() - Publisher.sendMessage("Enable state project", False) + Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") elif answer == -1: utils.debug("Cancel") else: self.CloseProject() - Publisher.sendMessage("Enable state project", False) + Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") @@ -284,13 +286,10 @@ ########################### - def OnOpenProject(self, pubsub_evt): - path = pubsub_evt.data - self.OpenProject(path) - - def OnOpenRecentProject(self, pubsub_evt): - filepath = pubsub_evt.data + def OnOpenProject(self, filepath): + self.OpenProject(filepath) + def OnOpenRecentProject(self, filepath): if os.path.exists(filepath): session = ses.Session() st = session.project_status @@ -321,15 +320,18 @@ self.Slice.window_width = proj.window Publisher.sendMessage('Update threshold limits list', - proj.threshold_range) + threshold_range=proj.threshold_range) self.LoadProject() session = ses.Session() session.OpenProject(filepath) - Publisher.sendMessage("Enable state project", True) + Publisher.sendMessage("Enable state project", state=True) - def SaveProject(self, path=None): + def OnSaveProject(self, filepath): + self.SaveProject(filepath) + + def SaveProject(self, path=None, compress=False): Publisher.sendMessage('Begin busy cursor') session = ses.Session() if path: @@ -339,16 +341,16 @@ dirpath, filename = session.project_path if isinstance(filename, str): - filename = filename.decode(const.FS_ENCODE) + filename = utils.decode(filename, const.FS_ENCODE) proj = prj.Project() - prj.Project().SavePlistProject(dirpath, filename) + prj.Project().SavePlistProject(dirpath, filename, compress) session.SaveProject() Publisher.sendMessage('End busy cursor') def CloseProject(self): - Publisher.sendMessage('Set slice interaction style', const.STATE_DEFAULT) + Publisher.sendMessage('Set slice interaction style', style=const.STATE_DEFAULT) Publisher.sendMessage('Hide content panel') Publisher.sendMessage('Close project data') @@ -382,8 +384,7 @@ reader.SetDirectoryPath(path) Publisher.sendMessage('End busy cursor') - def Progress(self, evt): - data = evt.data + def Progress(self, data): if (data): message = _("Loading file %d of %d ...")%(data[0],data[1]) if not(self.progress_dialog): @@ -396,22 +397,21 @@ Publisher.sendMessage('Begin busy cursor') else: #Is None if user canceled the load - self.progress_dialog.Close() - self.progress_dialog = None + if self.progress_dialog is not None: + self.progress_dialog.Close() + self.progress_dialog = None - def OnLoadImportPanel(self, evt): - patient_series = evt.data + def OnLoadImportPanel(self, patient_series): ok = self.LoadImportPanel(patient_series) if ok: Publisher.sendMessage('Show import panel') Publisher.sendMessage("Show import panel in frame") self.img_type = 1 - def OnLoadImportBitmapPanel(self, evt): - data = evt.data + def OnLoadImportBitmapPanel(self, data): ok = self.LoadImportBitmapPanel(data) if ok: - Publisher.sendMessage('Show import bitmap panel in frame') + Publisher.sendMessage('Show import bitmap panel in frame') self.img_type = 2 #Publisher.sendMessage("Show import panel in invesalius.gui.frame") as frame @@ -421,7 +421,7 @@ #first_patient = patient_series[0] #Publisher.sendMessage("Load bitmap preview", first_patient) if data: - Publisher.sendMessage("Load import bitmap panel", data) + Publisher.sendMessage("Load import bitmap panel", data=data) return True else: dialog.ImportInvalidFiles("Bitmap") @@ -430,9 +430,9 @@ def LoadImportPanel(self, patient_series): if patient_series and isinstance(patient_series, list): - Publisher.sendMessage("Load import panel", patient_series) + Publisher.sendMessage("Load import panel", dicom_groups=patient_series) first_patient = patient_series[0] - Publisher.sendMessage("Load dicom preview", first_patient) + Publisher.sendMessage("Load dicom preview", patient=first_patient) return True else: dialog.ImportInvalidFiles("DICOM") @@ -441,19 +441,18 @@ #----------- to import by command line --------------------------------------------------- - def OnImportMedicalImages(self, pubsub_evt): - directory = pubsub_evt.data - self.ImportMedicalImages(directory) + def OnImportMedicalImages(self, directory, use_gui): + self.ImportMedicalImages(directory, use_gui) - def ImportMedicalImages(self, directory): + def ImportMedicalImages(self, directory, gui=True): patients_groups = dcm.GetDicomGroups(directory) name = directory.rpartition('\\')[-1].split('.') - print "patients: ", patients_groups + print("patients: ", patients_groups) if len(patients_groups): # OPTION 1: DICOM group = dcm.SelectLargerDicomGroup(patients_groups) - matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0, 0], gui=True) + matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0, 0], gui=gui) self.CreateDicomProject(dicom, matrix, matrix_filename) else: # OPTION 2: NIfTI, Analyze or PAR/REC @@ -474,7 +473,17 @@ # OPTION 4: Nothing... self.LoadProject() - Publisher.sendMessage("Enable state project", True) + Publisher.sendMessage("Enable state project", state=True) + + def OnImportGroup(self, group, use_gui): + self.ImportGroup(group, use_gui) + + def ImportGroup(self, group, gui=True): + matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0, 0], gui=gui) + self.CreateDicomProject(dicom, matrix, matrix_filename) + + self.LoadProject() + Publisher.sendMessage("Enable state project", state=True) #------------------------------------------------------------------------------------- @@ -492,21 +501,23 @@ self.Slice.spacing = proj.spacing Publisher.sendMessage('Load slice to viewer', - (proj.mask_dict)) + mask_dict=proj.mask_dict) Publisher.sendMessage('Load slice plane') - Publisher.sendMessage('Bright and contrast adjustment image',\ - (proj.window, proj.level)) - Publisher.sendMessage('Update window level value',\ - (proj.window, proj.level)) + Publisher.sendMessage('Bright and contrast adjustment image', + window=proj.window, + level=proj.level) + Publisher.sendMessage('Update window level value', + window=proj.window, + level=proj.level) - Publisher.sendMessage('Set project name', proj.name) + Publisher.sendMessage('Set project name', proj_name=proj.name) Publisher.sendMessage('Load surface dict', - proj.surface_dict) + surface_dict=proj.surface_dict) Publisher.sendMessage('Hide surface items', - proj.surface_dict) + surface_dict=proj.surface_dict) self.LoadImagedataInfo() # TODO: where do we insert this << file_range[0]: + if file_range is not None and file_range[0] is not None and file_range[1] > file_range[0]: filelist = filelist[file_range[0]:file_range[1] + 1] zspacing = dicom_group.zspacing * interval @@ -774,47 +784,53 @@ xyspacing = dicom.image.spacing orientation = dicom.image.orientation_label + wl = float(dicom.image.level) + ww = float(dicom.image.window) + if sop_class_uid == '1.2.840.10008.5.1.4.1.1.7': #Secondary Capture Image Storage use_dcmspacing = 1 else: use_dcmspacing = 0 imagedata = None - - sx, sy = size - n_slices = len(filelist) - resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8) - - if resolution_percentage < 1.0: - re_dialog = dialog.ResizeImageDialog() - re_dialog.SetValue(int(resolution_percentage*100)) - re_dialog_value = re_dialog.ShowModal() - re_dialog.Close() - - if re_dialog_value == wx.ID_OK: - percentage = re_dialog.GetValue() - resolution_percentage = percentage / 100.0 - else: - return - xyspacing = xyspacing[0] / resolution_percentage, xyspacing[1] / resolution_percentage - - - wl = float(dicom.image.level) - ww = float(dicom.image.window) - self.matrix, scalar_range, self.filename = image_utils.dcm2memmap(filelist, size, - orientation, resolution_percentage) + if dicom.image.number_of_frames == 1: + sx, sy = size + n_slices = len(filelist) + resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8) + + if resolution_percentage < 1.0 and gui: + re_dialog = dialog.ResizeImageDialog() + re_dialog.SetValue(int(resolution_percentage*100)) + re_dialog_value = re_dialog.ShowModal() + re_dialog.Close() + + if re_dialog_value == wx.ID_OK: + percentage = re_dialog.GetValue() + resolution_percentage = percentage / 100.0 + else: + return + + xyspacing = xyspacing[0] / resolution_percentage, xyspacing[1] / resolution_percentage + + self.matrix, scalar_range, self.filename = image_utils.dcm2memmap(filelist, size, + orientation, resolution_percentage) + + print(xyspacing, zspacing) + if orientation == 'AXIAL': + spacing = xyspacing[0], xyspacing[1], zspacing + elif orientation == 'CORONAL': + spacing = xyspacing[0], zspacing, xyspacing[1] + elif orientation == 'SAGITTAL': + spacing = zspacing, xyspacing[1], xyspacing[0] + else: + self.matrix, spacing, scalar_range, self.filename = image_utils.dcmmf2memmap(filelist[0], orientation) self.Slice = sl.Slice() self.Slice.matrix = self.matrix self.Slice.matrix_filename = self.filename - if orientation == 'AXIAL': - self.Slice.spacing = xyspacing[0], xyspacing[1], zspacing - elif orientation == 'CORONAL': - self.Slice.spacing = xyspacing[0], zspacing, xyspacing[1] - elif orientation == 'SAGITTAL': - self.Slice.spacing = zspacing, xyspacing[1], xyspacing[0] + self.Slice.spacing = spacing # 1(a): Fix gantry tilt, if any tilt_value = dicom.acquisition.tilt @@ -833,7 +849,8 @@ scalar_range = int(self.matrix.min()), int(self.matrix.max()) - Publisher.sendMessage('Update threshold limits list', scalar_range) + Publisher.sendMessage('Update threshold limits list', + threshold_range=scalar_range) return self.matrix, self.filename, dicom @@ -858,14 +875,15 @@ self.Slice.window_width = ww scalar_range = int(scalar_range[0]), int(scalar_range[1]) - Publisher.sendMessage('Update threshold limits list', scalar_range) + Publisher.sendMessage('Update threshold limits list', + threshold_range=scalar_range) return self.matrix, self.filename def LoadImagedataInfo(self): proj = prj.Project() thresh_modes = proj.threshold_modes.keys() - thresh_modes.sort() + thresh_modes = sorted(thresh_modes) default_threshold = const.THRESHOLD_PRESETS_INDEX if proj.mask_dict: keys = proj.mask_dict.keys() @@ -881,24 +899,20 @@ [a,b] = default_threshold default_threshold = (a,b) Publisher.sendMessage('Set threshold modes', - (thresh_modes,default_threshold)) + thresh_modes_names=thresh_modes, + default_thresh=default_threshold) - def LoadRaycastingPreset(self, pubsub_evt=None): - if pubsub_evt: - label = pubsub_evt.data - else: - return - - if label != const.RAYCASTING_OFF_LABEL: - if label in const.RAYCASTING_FILES.keys(): + def LoadRaycastingPreset(self, preset_name): + if preset_name != const.RAYCASTING_OFF_LABEL: + if preset_name in const.RAYCASTING_FILES.keys(): path = os.path.join(const.RAYCASTING_PRESETS_DIRECTORY, - const.RAYCASTING_FILES[label]) + const.RAYCASTING_FILES[preset_name]) else: path = os.path.join(const.RAYCASTING_PRESETS_DIRECTORY, - label+".plist") + preset_name+".plist") if not os.path.isfile(path): path = os.path.join(const.USER_RAYCASTING_PRESETS_DIRECTORY, - label+".plist") + preset_name+".plist") preset = plistlib.readPlist(path) prj.Project().raycasting_preset = preset # Notify volume @@ -908,17 +922,16 @@ prj.Project().raycasting_preset = 0 Publisher.sendMessage('Update raycasting preset') - def SaveRaycastingPreset(self, pubsub_evt): - preset_name = pubsub_evt.data + def SaveRaycastingPreset(self, preset_name): preset = prj.Project().raycasting_preset preset['name'] = preset_name preset_dir = os.path.join(const.USER_RAYCASTING_PRESETS_DIRECTORY, preset_name + '.plist') plistlib.writePlist(preset, preset_dir) - def ShowBooleanOpDialog(self, pubsub_evt): + def ShowBooleanOpDialog(self): dlg = dialogs.MaskBooleanDialog(prj.Project().mask_dict) dlg.Show() - def ApplyReorientation(self, pubsub_evt): + def ApplyReorientation(self): self.Slice.apply_reorientation() diff -Nru invesalius-3.1.1/invesalius/data/bases.py invesalius-3.1.99991/invesalius/data/bases.py --- invesalius-3.1.1/invesalius/data/bases.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/bases.py 2018-08-17 16:41:39.000000000 +0000 @@ -1,5 +1,7 @@ -from math import sqrt +from math import sqrt, pi import numpy as np +import invesalius.data.coordinates as dco +import invesalius.data.transformations as tr def angle_calculation(ap_axis, coil_axis): @@ -19,7 +21,7 @@ return float(angle) -def base_creation(fiducials): +def base_creation_old(fiducials): """ Calculate the origin and matrix for coordinate system transformation. @@ -55,31 +57,70 @@ [g2[0], g2[1], g2[2]], [g3[0], g3[1], g3[2]]]) - q.shape = (3, 1) - q = np.matrix(q.copy()) m_inv = m.I - # print"M: ", m - # print"q: ", q + return m, q, m_inv + + +def base_creation(fiducials): + """ + Calculate the origin and matrix for coordinate system + transformation. + q: origin of coordinate system + g1, g2, g3: orthogonal vectors of coordinate system + + :param fiducials: array of 3 rows (p1, p2, p3) and 3 columns (x, y, z) with fiducials coordinates + :return: matrix and origin for base transformation + """ + + p1 = fiducials[0, :] + p2 = fiducials[1, :] + p3 = fiducials[2, :] + + sub1 = p2 - p1 + sub2 = p3 - p1 + lamb = (sub1[0]*sub2[0]+sub1[1]*sub2[1]+sub1[2]*sub2[2])/np.dot(sub1, sub1) + + q = p1 + lamb*sub1 + g1 = p3 - q + g2 = p1 - q + + if not g1.any(): + g1 = p2 - q + + g3 = np.cross(g1, g2) + + g1 = g1/sqrt(np.dot(g1, g1)) + g2 = g2/sqrt(np.dot(g2, g2)) + g3 = g3/sqrt(np.dot(g3, g3)) + + m = np.matrix([[g1[0], g2[0], g3[0]], + [g1[1], g2[1], g3[1]], + [g1[2], g2[2], g3[2]]]) + + m_inv = m.I return m, q, m_inv -def calculate_fre(fiducials, minv, n, q1, q2): +def calculate_fre(fiducials, minv, n, q, o): """ Calculate the Fiducial Registration Error for neuronavigation. :param fiducials: array of 6 rows (image and tracker fiducials) and 3 columns (x, y, z) with coordinates :param minv: inverse matrix given by base creation :param n: base change matrix given by base creation - :param q1: origin of first base - :param q2: origin of second base + :param q: origin of first base + :param o: origin of second base :return: float number of fiducial registration error """ img = np.zeros([3, 3]) dist = np.zeros([3, 1]) + q1 = np.mat(q).reshape(3, 1) + q2 = np.mat(o).reshape(3, 1) + p1 = np.mat(fiducials[3, :]).reshape(3, 1) p2 = np.mat(fiducials[4, :]).reshape(3, 1) p3 = np.mat(fiducials[5, :]).reshape(3, 1) @@ -133,3 +174,98 @@ x, y, z = point_rot.tolist()[0][:3] return x, y, z + + +def flip_x_m(point): + """ + Rotate coordinates of a vector by pi around X axis in static reference frame. + + InVesalius also require to multiply the z coordinate by (-1). Possibly + because the origin of coordinate system of imagedata is + located in superior left corner and the origin of VTK scene coordinate + system (polygonal surface) is in the interior left corner. Second + possibility is the order of slice stacking + + :param point: list of coordinates x, y and z + :return: rotated coordinates + """ + + point_4 = np.hstack((point, 1.)).reshape([4, 1]) + point_4[2, 0] = -point_4[2, 0] + + m_rot = np.asmatrix(tr.euler_matrix(pi, 0, 0)) + + point_rot = m_rot*point_4 + + return point_rot[0, 0], point_rot[1, 0], point_rot[2, 0] + + +def object_registration(fiducials, orients, coord_raw, m_change): + """ + + :param fiducials: 3x3 array of fiducials translations + :param orients: 3x3 array of fiducials orientations in degrees + :param coord_raw: nx6 array of coordinates from tracking device where n = 1 is the reference attached to the head + :param m_change: 3x3 array representing change of basis from head in tracking system to vtk head system + :return: + """ + + coords_aux = np.hstack((fiducials, orients)) + mask = np.ones(len(coords_aux), dtype=bool) + mask[[3]] = False + coords = coords_aux[mask] + + fids_dyn = np.zeros([4, 6]) + fids_img = np.zeros([4, 6]) + fids_raw = np.zeros([3, 3]) + + # compute fiducials of object with reference to the fixed probe in source frame + for ic in range(0, 3): + fids_raw[ic, :] = dco.dynamic_reference_m2(coords[ic, :], coords[3, :])[:3] + + # compute initial alignment of probe fixed in the object in source frame + t_s0_raw = np.asmatrix(tr.translation_matrix(coords[3, :3])) + r_s0_raw = np.asmatrix(tr.euler_matrix(np.radians(coords[3, 3]), np.radians(coords[3, 4]), + np.radians(coords[3, 5]), 'rzyx')) + s0_raw = np.asmatrix(tr.concatenate_matrices(t_s0_raw, r_s0_raw)) + + # compute change of basis for object fiducials in source frame + base_obj_raw, q_obj_raw, base_inv_obj_raw = base_creation(fids_raw[:3, :3]) + r_obj_raw = np.asmatrix(np.identity(4)) + r_obj_raw[:3, :3] = base_obj_raw[:3, :3] + t_obj_raw = np.asmatrix(tr.translation_matrix(q_obj_raw)) + m_obj_raw = np.asmatrix(tr.concatenate_matrices(t_obj_raw, r_obj_raw)) + + for ic in range(0, 4): + if coord_raw.any(): + # compute object fiducials in reference frame + fids_dyn[ic, :] = dco.dynamic_reference_m2(coords[ic, :], coord_raw[1, :]) + fids_dyn[ic, 2] = -fids_dyn[ic, 2] + else: + # compute object fiducials in source frame + fids_dyn[ic, :] = coords[ic, :] + + # compute object fiducials in vtk head frame + a, b, g = np.radians(fids_dyn[ic, 3:]) + T_p = tr.translation_matrix(fids_dyn[ic, :3]) + R_p = tr.euler_matrix(a, b, g, 'rzyx') + M_p = np.asmatrix(tr.concatenate_matrices(T_p, R_p)) + M_img = np.asmatrix(m_change) * M_p + + angles_img = np.degrees(np.asarray(tr.euler_from_matrix(M_img, 'rzyx'))) + coord_img = np.asarray(flip_x_m(tr.translation_from_matrix(M_img))) + + fids_img[ic, :] = np.hstack((coord_img, angles_img)) + + # compute object base change in vtk head frame + base_obj_img, q_obj_img, base_inv_obj_img = base_creation(fids_img[:3, :3]) + r_obj_img = np.asmatrix(np.identity(4)) + r_obj_img[:3, :3] = base_obj_img[:3, :3] + + # compute initial alignment of probe fixed in the object in reference (or static) frame + s0_trans_dyn = np.asmatrix(tr.translation_matrix(fids_dyn[3, :3])) + s0_rot_dyn = np.asmatrix(tr.euler_matrix(np.radians(fids_dyn[3, 3]), np.radians(fids_dyn[3, 4]), + np.radians(fids_dyn[3, 5]), 'rzyx')) + s0_dyn = np.asmatrix(tr.concatenate_matrices(s0_trans_dyn, s0_rot_dyn)) + + return t_obj_raw, s0_raw, r_s0_raw, s0_dyn, m_obj_raw, r_obj_img diff -Nru invesalius-3.1.1/invesalius/data/coordinates.py invesalius-3.1.99991/invesalius/data/coordinates.py --- invesalius-3.1.1/invesalius/data/coordinates.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/coordinates.py 2018-08-17 16:41:39.000000000 +0000 @@ -20,10 +20,13 @@ from math import sin, cos import numpy as np +import invesalius.data.transformations as tr + from time import sleep from random import uniform from wx.lib.pubsub import pub as Publisher + def GetCoordinates(trck_init, trck_id, ref_mode): """ @@ -44,7 +47,7 @@ 5: DebugCoord} coord = getcoord[trck_id](trck_init, trck_id, ref_mode) else: - print "Select Tracker" + print("Select Tracker") return coord @@ -54,34 +57,36 @@ scale = np.array([1.0, 1.0, -1.0]) coord = None k = 0 - # TODO: try to replace while and use some Claron internal computation + # TODO: try to replace 'while' and use some Claron internal computation if ref_mode: while k < 20: try: trck.Run() - probe = np.array([trck.PositionTooltipX1 * scale[0], trck.PositionTooltipY1 * scale[1], - trck.PositionTooltipZ1 * scale[2], trck.AngleX1, trck.AngleY1, trck.AngleZ1]) - reference = np.array([trck.PositionTooltipX2 * scale[0], trck.PositionTooltipY2 * scale[1], - trck.PositionTooltipZ2 * scale[2], trck.AngleX2, trck.AngleY2, trck.AngleZ2]) + probe = np.array([trck.PositionTooltipX1, trck.PositionTooltipY1, + trck.PositionTooltipZ1, trck.AngleX1, trck.AngleY1, trck.AngleZ1]) + reference = np.array([trck.PositionTooltipX2, trck.PositionTooltipY2, + trck.PositionTooltipZ2, trck.AngleZ2, trck.AngleY2, trck.AngleX2]) k = 30 except AttributeError: k += 1 - print "wait, collecting coordinates ..." + print("wait, collecting coordinates ...") if k == 30: coord = dynamic_reference(probe, reference) + coord = (coord[0] * scale[0], coord[1] * scale[1], coord[2] * scale[2], coord[3], coord[4], coord[5]) else: while k < 20: try: trck.Run() coord = np.array([trck.PositionTooltipX1 * scale[0], trck.PositionTooltipY1 * scale[1], trck.PositionTooltipZ1 * scale[2], trck.AngleX1, trck.AngleY1, trck.AngleZ1]) + k = 30 except AttributeError: k += 1 - print "wait, collecting coordinates ..." + print("wait, collecting coordinates ...") - Publisher.sendMessage('Sensors ID', [trck.probeID, trck.refID]) + Publisher.sendMessage('Sensors ID', probe_id=trck.probeID, ref_id=trck.refID) return coord @@ -103,26 +108,23 @@ def PolhemusWrapperCoord(trck, trck_id, ref_mode): - scale = 25.4 * np.array([1., 1.0, -1.0]) - coord = None + trck.Run() + scale = 10.0 * np.array([1., 1., 1.]) - if ref_mode: - trck.Run() - probe = np.array([float(trck.PositionTooltipX1) * scale[0], float(trck.PositionTooltipY1) * scale[1], - float(trck.PositionTooltipZ1) * scale[2], float(trck.AngleX1), float(trck.AngleY1), - float(trck.AngleZ1)]) - reference = np.array([float(trck.PositionTooltipX2) * scale[0], float(trck.PositionTooltipY2) * scale[1], - float(trck.PositionTooltipZ2) * scale[2], float(trck.AngleX2), float(trck.AngleY2), - float(trck.AngleZ2)]) + coord1 = np.array([float(trck.PositionTooltipX1)*scale[0], float(trck.PositionTooltipY1)*scale[1], + float(trck.PositionTooltipZ1)*scale[2], + float(trck.AngleX1), float(trck.AngleY1), float(trck.AngleZ1)]) + + coord2 = np.array([float(trck.PositionTooltipX2)*scale[0], float(trck.PositionTooltipY2)*scale[1], + float(trck.PositionTooltipZ2)*scale[2], + float(trck.AngleX2), float(trck.AngleY2), float(trck.AngleZ2)]) + coord = np.vstack([coord1, coord2]) - if probe.all() and reference.all(): - coord = dynamic_reference(probe, reference) - - else: - trck.Run() - coord = np.array([float(trck.PositionTooltipX1) * scale[0], float(trck.PositionTooltipY1) * scale[1], - float(trck.PositionTooltipZ1) * scale[2], float(trck.AngleX1), float(trck.AngleY1), - float(trck.AngleZ1)]) + if trck_id == 2: + coord3 = np.array([float(trck.PositionTooltipX3) * scale[0], float(trck.PositionTooltipY3) * scale[1], + float(trck.PositionTooltipZ3) * scale[2], + float(trck.AngleX3), float(trck.AngleY3), float(trck.AngleZ3)]) + coord = np.vstack([coord, coord3]) if trck.StylusButton: Publisher.sendMessage('PLH Stylus Button On') @@ -148,13 +150,12 @@ # six coordinates of first and second sensor: x, y, z and alfa, beta and gama # jump one element for reference to avoid the sensor ID returned by Polhemus - probe = data[0] * scale[0], data[1] * scale[1], data[2] * scale[2], \ - data[3], data[4], data[5], data[6] - reference = data[7] * scale[0], data[8] * scale[1], data[9] * scale[2], data[10], \ - data[11], data[12], data[13] + probe = data[0], data[1], data[2], data[3], data[4], data[5], data[6] + reference = data[7], data[8], data[9], data[10], data[11], data[12], data[13] if probe.all() and reference.all(): coord = dynamic_reference(probe, reference) + coord = (coord[0] * scale[0], coord[1] * scale[1], coord[2] * scale[2], coord[3], coord[4], coord[5]) return coord @@ -179,7 +180,7 @@ coord = None if lines[0][0] != '0': - print "The Polhemus is not connected!" + print("The Polhemus is not connected!") else: for s in lines: if s[1] == '1': @@ -197,7 +198,7 @@ plh1 = [float(s) for s in data[1:len(data)]] j = 1 except: - print "error!!" + print("error!!") coord = data[0:6] return coord @@ -212,22 +213,21 @@ :param trck_id: id of tracking device :return: six coordinates x, y, z, alfa, beta and gama """ - sleep(0.2) - if ref_mode: - probe = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), - uniform(1, 200), uniform(1, 200), uniform(1, 200)]) - reference = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), - uniform(1, 200), uniform(1, 200), uniform(1, 200)]) - coord = dynamic_reference(probe, reference) + sleep(0.05) - else: - coord = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), - uniform(1, 200), uniform(1, 200), uniform(1, 200)]) + coord1 = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), + uniform(-180.0, 180.0), uniform(-180.0, 180.0), uniform(-180.0, 180.0)]) - Publisher.sendMessage('Sensors ID', [int(uniform(0, 5)), int(uniform(0, 5))]) + coord2 = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), + uniform(-180.0, 180.0), uniform(-180.0, 180.0), uniform(-180.0, 180.0)]) - return coord + coord3 = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), + uniform(-180.0, 180.0), uniform(-180.0, 180.0), uniform(-180.0, 180.0)]) + + Publisher.sendMessage('Sensors ID', probe_id=int(uniform(0, 5)), ref_id=int(uniform(0, 5))) + + return np.vstack([coord1, coord2, coord3]) def dynamic_reference(probe, reference): @@ -244,28 +244,143 @@ """ a, b, g = np.radians(reference[3:6]) - vet = probe[0:3] - reference[0:3] - vet = np.mat(vet.reshape(3, 1)) + vet = np.asmatrix(probe[0:3] - reference[0:3]) + # vet = np.mat(vet.reshape(3, 1)) - # Attitude Matrix given by Patriot Manual - Mrot = np.mat([[cos(a) * cos(b), sin(b) * sin(g) * cos(a) - cos(g) * sin(a), - cos(a) * sin(b) * cos(g) + sin(a) * sin(g)], - [cos(b) * sin(a), sin(b) * sin(g) * sin(a) + cos(g) * cos(a), - cos(g) * sin(b) * sin(a) - sin(g) * cos(a)], - [-sin(b), sin(g) * cos(b), cos(b) * cos(g)]]) + # Attitude matrix given by Patriot manual + # a: rotation of plane (X, Y) around Z axis (azimuth) + # b: rotation of plane (X', Z) around Y' axis (elevation) + # a: rotation of plane (Y', Z') around X'' axis (roll) + m_rot = np.mat([[cos(a) * cos(b), sin(b) * sin(g) * cos(a) - cos(g) * sin(a), + cos(a) * sin(b) * cos(g) + sin(a) * sin(g)], + [cos(b) * sin(a), sin(b) * sin(g) * sin(a) + cos(g) * cos(a), + cos(g) * sin(b) * sin(a) - sin(g) * cos(a)], + [-sin(b), sin(g) * cos(b), cos(b) * cos(g)]]) - coord_rot = Mrot.T * vet + # coord_rot = m_rot.T * vet + coord_rot = vet*m_rot coord_rot = np.squeeze(np.asarray(coord_rot)) - return coord_rot[0], coord_rot[1], coord_rot[2], probe[3], probe[4], probe[5] + return coord_rot[0], coord_rot[1], -coord_rot[2], probe[3], probe[4], probe[5] + + +def dynamic_reference_m(probe, reference): + """ + Apply dynamic reference correction to probe coordinates. Uses the alpha, beta and gama + rotation angles of reference to rotate the probe coordinate and returns the x, y, z + difference between probe and reference. Angles sequences and equation was extracted from + Polhemus manual and Attitude matrix in Wikipedia. + General equation is: + coord = Mrot * (probe - reference) + :param probe: sensor one defined as probe + :param reference: sensor two defined as reference + :return: rotated and translated coordinates + """ + + a, b, g = np.radians(reference[3:6]) + + T = tr.translation_matrix(reference[:3]) + R = tr.euler_matrix(a, b, g, 'rzyx') + M = np.asmatrix(tr.concatenate_matrices(T, R)) + # M = tr.compose_matrix(angles=np.radians(reference[3:6]), translate=reference[:3]) + # print M + probe_4 = np.vstack((np.asmatrix(probe[:3]).reshape([3, 1]), 1.)) + coord_rot = M.I * probe_4 + coord_rot = np.squeeze(np.asarray(coord_rot)) + + return coord_rot[0], coord_rot[1], -coord_rot[2], probe[3], probe[4], probe[5] + +def dynamic_reference_m2(probe, reference): + """ + Apply dynamic reference correction to probe coordinates. Uses the alpha, beta and gama + rotation angles of reference to rotate the probe coordinate and returns the x, y, z + difference between probe and reference. Angles sequences and equation was extracted from + Polhemus manual and Attitude matrix in Wikipedia. + General equation is: + coord = Mrot * (probe - reference) + :param probe: sensor one defined as probe + :param reference: sensor two defined as reference + :return: rotated and translated coordinates + """ + + a, b, g = np.radians(reference[3:6]) + a_p, b_p, g_p = np.radians(probe[3:6]) + + T = tr.translation_matrix(reference[:3]) + T_p = tr.translation_matrix(probe[:3]) + R = tr.euler_matrix(a, b, g, 'rzyx') + R_p = tr.euler_matrix(a_p, b_p, g_p, 'rzyx') + M = np.asmatrix(tr.concatenate_matrices(T, R)) + M_p = np.asmatrix(tr.concatenate_matrices(T_p, R_p)) + # M = tr.compose_matrix(angles=np.radians(reference[3:6]), translate=reference[:3]) + # print M + + M_dyn = M.I * M_p + + al, be, ga = tr.euler_from_matrix(M_dyn, 'rzyx') + coord_rot = tr.translation_from_matrix(M_dyn) + + coord_rot = np.squeeze(coord_rot) + + # probe_4 = np.vstack((np.asmatrix(probe[:3]).reshape([3, 1]), 1.)) + # coord_rot_test = M.I * probe_4 + # coord_rot_test = np.squeeze(np.asarray(coord_rot_test)) + # + # print "coord_rot: ", coord_rot + # print "coord_rot_test: ", coord_rot_test + # print "test: ", np.allclose(coord_rot, coord_rot_test[:3]) + + return coord_rot[0], coord_rot[1], coord_rot[2], np.degrees(al), np.degrees(be), np.degrees(ga) + +# def dynamic_reference_m3(probe, reference): +# """ +# Apply dynamic reference correction to probe coordinates. Uses the alpha, beta and gama +# rotation angles of reference to rotate the probe coordinate and returns the x, y, z +# difference between probe and reference. Angles sequences and equation was extracted from +# Polhemus manual and Attitude matrix in Wikipedia. +# General equation is: +# coord = Mrot * (probe - reference) +# :param probe: sensor one defined as probe +# :param reference: sensor two defined as reference +# :return: rotated and translated coordinates +# """ +# +# a, b, g = np.radians(reference[3:6]) +# a_p, b_p, g_p = np.radians(probe[3:6]) +# +# T = tr.translation_matrix(reference[:3]) +# T_p = tr.translation_matrix(probe[:3]) +# R = tr.euler_matrix(a, b, g, 'rzyx') +# R_p = tr.euler_matrix(a_p, b_p, g_p, 'rzyx') +# M = np.asmatrix(tr.concatenate_matrices(T, R)) +# M_p = np.asmatrix(tr.concatenate_matrices(T_p, R_p)) +# # M = tr.compose_matrix(angles=np.radians(reference[3:6]), translate=reference[:3]) +# # print M +# +# M_dyn = M.I * M_p +# +# # al, be, ga = tr.euler_from_matrix(M_dyn, 'rzyx') +# # coord_rot = tr.translation_from_matrix(M_dyn) +# # +# # coord_rot = np.squeeze(coord_rot) +# +# # probe_4 = np.vstack((np.asmatrix(probe[:3]).reshape([3, 1]), 1.)) +# # coord_rot_test = M.I * probe_4 +# # coord_rot_test = np.squeeze(np.asarray(coord_rot_test)) +# # +# # print "coord_rot: ", coord_rot +# # print "coord_rot_test: ", coord_rot_test +# # print "test: ", np.allclose(coord_rot, coord_rot_test[:3]) +# +# return M_dyn def str2float(data): """ - Converts string detected wth Polhemus device to float array of coordinates. THis method applies + Converts string detected wth Polhemus device to float array of coordinates. This method applies a correction for the minus sign in string that raises error while splitting the string into coordinates. :param data: string of coordinates read with Polhemus - :return: six float coordinates x, y, z, alfa, beta and gama + :return: six float coordinates x, y, z, alpha, beta and gamma """ count = 0 diff -Nru invesalius-3.1.1/invesalius/data/coregistration.py invesalius-3.1.99991/invesalius/data/coregistration.py --- invesalius-3.1.1/invesalius/data/coregistration.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/coregistration.py 2018-08-17 16:41:39.000000000 +0000 @@ -20,16 +20,123 @@ import threading from time import sleep -from numpy import mat +from numpy import asmatrix, mat, degrees, radians, identity import wx from wx.lib.pubsub import pub as Publisher import invesalius.data.coordinates as dco +import invesalius.data.transformations as tr # TODO: Optimize navigation thread. Remove the infinite loop and optimize sleep. -class Coregistration(threading.Thread): +class CoregistrationStatic(threading.Thread): + """ + Thread to update the coordinates with the fiducial points + co-registration method while the Navigation Button is pressed. + Sleep function in run method is used to avoid blocking GUI and + for better real-time navigation + """ + + def __init__(self, coreg_data, nav_id, trck_info): + threading.Thread.__init__(self) + self.coreg_data = coreg_data + self.nav_id = nav_id + self.trck_info = trck_info + self._pause_ = False + self.start() + + def stop(self): + self._pause_ = True + + def run(self): + # m_change = self.coreg_data[0] + # obj_ref_mode = self.coreg_data[2] + # + # trck_init = self.trck_info[0] + # trck_id = self.trck_info[1] + # trck_mode = self.trck_info[2] + + m_change, obj_ref_mode = self.coreg_data + trck_init, trck_id, trck_mode = self.trck_info + + while self.nav_id: + coord_raw = dco.GetCoordinates(trck_init, trck_id, trck_mode) + + psi, theta, phi = coord_raw[obj_ref_mode, 3:] + t_probe_raw = asmatrix(tr.translation_matrix(coord_raw[obj_ref_mode, :3])) + + t_probe_raw[2, -1] = -t_probe_raw[2, -1] + + m_img = m_change * t_probe_raw + + coord = m_img[0, -1], m_img[1, -1], m_img[2, -1], psi, theta, phi + + wx.CallAfter(Publisher.sendMessage, 'Co-registered points', arg=m_img, position=coord) + + # TODO: Optimize the value of sleep for each tracking device. + sleep(0.175) + + if self._pause_: + return + + +class CoregistrationDynamic(threading.Thread): + """ + Thread to update the coordinates with the fiducial points + co-registration method while the Navigation Button is pressed. + Sleep function in run method is used to avoid blocking GUI and + for better real-time navigation + """ + + def __init__(self, coreg_data, nav_id, trck_info): + threading.Thread.__init__(self) + self.coreg_data = coreg_data + self.nav_id = nav_id + self.trck_info = trck_info + self._pause_ = False + self.start() + + def stop(self): + self._pause_ = True + + def run(self): + m_change, obj_ref_mode = self.coreg_data + trck_init, trck_id, trck_mode = self.trck_info + + while self.nav_id: + coord_raw = dco.GetCoordinates(trck_init, trck_id, trck_mode) + + psi, theta, phi = radians(coord_raw[obj_ref_mode, 3:]) + r_probe = tr.euler_matrix(psi, theta, phi, 'rzyx') + t_probe = tr.translation_matrix(coord_raw[obj_ref_mode, :3]) + m_probe = asmatrix(tr.concatenate_matrices(t_probe, r_probe)) + + psi_ref, theta_ref, phi_ref = radians(coord_raw[1, 3:]) + r_ref = tr.euler_matrix(psi_ref, theta_ref, phi_ref, 'rzyx') + t_ref = tr.translation_matrix(coord_raw[1, :3]) + m_ref = asmatrix(tr.concatenate_matrices(t_ref, r_ref)) + + m_dyn = m_ref.I * m_probe + m_dyn[2, -1] = -m_dyn[2, -1] + + m_img = m_change * m_dyn + + scale, shear, angles, trans, persp = tr.decompose_matrix(m_img) + + coord = m_img[0, -1], m_img[1, -1], m_img[2, -1], \ + degrees(angles[0]), degrees(angles[1]), degrees(angles[2]) + + wx.CallAfter(Publisher.sendMessage, 'Co-registered points', arg=m_img, position=coord) + + # TODO: Optimize the value of sleep for each tracking device. + sleep(0.175) + + if self._pause_: + return + + +class CoregistrationDynamic_old(threading.Thread): """ Thread to update the coordinates with the fiducial points co-registration method while the Navigation Button is pressed. @@ -44,10 +151,10 @@ self.trck_info = trck_info self._pause_ = False self.start() - + def stop(self): self._pause_ = True - + def run(self): m_inv = self.bases[0] n = self.bases[1] @@ -58,24 +165,199 @@ trck_mode = self.trck_info[2] while self.nav_id: - trck_coord = dco.GetCoordinates(trck_init, trck_id, trck_mode) - trck_xyz = mat([[trck_coord[0]], [trck_coord[1]], [trck_coord[2]]]) + # trck_coord, probe, reference = dco.GetCoordinates(trck_init, trck_id, trck_mode) + coord_raw = dco.GetCoordinates(trck_init, trck_id, trck_mode) - img = q1 + (m_inv*n)*(trck_xyz - q2) + trck_coord = dco.dynamic_reference(coord_raw[0, :], coord_raw[1, :]) + + trck_xyz = mat([[trck_coord[0]], [trck_coord[1]], [trck_coord[2]]]) + img = q1 + (m_inv * n) * (trck_xyz - q2) coord = (float(img[0]), float(img[1]), float(img[2]), trck_coord[3], trck_coord[4], trck_coord[5]) + angles = coord_raw[0, 3:6] # Tried several combinations and different locations to send the messages, # however only this one does not block the GUI during navigation. - wx.CallAfter(Publisher.sendMessage, 'Co-registered points', coord[0:3]) - wx.CallAfter(Publisher.sendMessage, 'Set camera in volume', coord[0:3]) + wx.CallAfter(Publisher.sendMessage, 'Co-registered points', arg=None, position=coord) + wx.CallAfter(Publisher.sendMessage, 'Set camera in volume', coord) + wx.CallAfter(Publisher.sendMessage, 'Update tracker angles', angles) # TODO: Optimize the value of sleep for each tracking device. # Debug tracker is not working with 0.175 so changed to 0.2 # However, 0.2 is too low update frequency ~5 Hz. Need optimization URGENTLY. - #sleep(.3) + # sleep(.3) sleep(0.175) if self._pause_: return + + +class CoregistrationObjectStatic(threading.Thread): + """ + Thread to update the coordinates with the fiducial points + co-registration method while the Navigation Button is pressed. + Sleep function in run method is used to avoid blocking GUI and + for better real-time navigation + """ + + def __init__(self, coreg_data, nav_id, trck_info): + threading.Thread.__init__(self) + self.coreg_data = coreg_data + self.nav_id = nav_id + self.trck_info = trck_info + self._pause_ = False + self.start() + + def stop(self): + self._pause_ = True + + def run(self): + # m_change = self.coreg_data[0] + # t_obj_raw = self.coreg_data[1] + # s0_raw = self.coreg_data[2] + # r_s0_raw = self.coreg_data[3] + # s0_dyn = self.coreg_data[4] + # m_obj_raw = self.coreg_data[5] + # r_obj_img = self.coreg_data[6] + # obj_ref_mode = self.coreg_data[7] + # + # trck_init = self.trck_info[0] + # trck_id = self.trck_info[1] + # trck_mode = self.trck_info[2] + + m_change, obj_ref_mode, t_obj_raw, s0_raw, r_s0_raw, s0_dyn, m_obj_raw, r_obj_img = self.coreg_data + trck_init, trck_id, trck_mode = self.trck_info + + while self.nav_id: + coord_raw = dco.GetCoordinates(trck_init, trck_id, trck_mode) + + as1, bs1, gs1 = radians(coord_raw[obj_ref_mode, 3:]) + r_probe = asmatrix(tr.euler_matrix(as1, bs1, gs1, 'rzyx')) + t_probe_raw = asmatrix(tr.translation_matrix(coord_raw[obj_ref_mode, :3])) + t_offset_aux = r_s0_raw.I * r_probe * t_obj_raw + t_offset = asmatrix(identity(4)) + t_offset[:, -1] = t_offset_aux[:, -1] + t_probe = s0_raw * t_offset * s0_raw.I * t_probe_raw + m_probe = asmatrix(tr.concatenate_matrices(t_probe, r_probe)) + + m_probe[2, -1] = -m_probe[2, -1] + + m_img = m_change * m_probe + r_obj = r_obj_img * m_obj_raw.I * s0_dyn.I * m_probe * m_obj_raw + + m_img[:3, :3] = r_obj[:3, :3] + + scale, shear, angles, trans, persp = tr.decompose_matrix(m_img) + + coord = m_img[0, -1], m_img[1, -1], m_img[2, -1], \ + degrees(angles[0]), degrees(angles[1]), degrees(angles[2]) + + wx.CallAfter(Publisher.sendMessage, 'Co-registered points', arg=m_img, position=coord) + wx.CallAfter(Publisher.sendMessage, 'Update object matrix', m_img=m_img, coord=coord) + + # TODO: Optimize the value of sleep for each tracking device. + sleep(0.175) + + # Debug tracker is not working with 0.175 so changed to 0.2 + # However, 0.2 is too low update frequency ~5 Hz. Need optimization URGENTLY. + # sleep(.3) + + # partially working for translate and offset, + # but offset is kept always in same axis, have to fix for rotation + # M_dyn = M_reference.I * T_stylus + # M_dyn[2, -1] = -M_dyn[2, -1] + # M_dyn_ch = M_change * M_dyn + # ddd = M_dyn_ch[0, -1], M_dyn_ch[1, -1], M_dyn_ch[2, -1] + # M_dyn_ch[:3, -1] = asmatrix(db.flip_x_m(ddd)).reshape([3, 1]) + # M_final = S0 * M_obj_trans_0 * S0.I * M_dyn_ch + + # this works for static reference object rotation + # R_dyn = M_vtk * M_obj_rot_raw.I * S0_rot_raw.I * R_stylus * M_obj_rot_raw + # this works for dynamic reference in rotation but not in translation + # R_dyn = M_vtk * M_obj_rot_raw.I * S0_rot_dyn.I * R_reference.I * R_stylus * M_obj_rot_raw + + if self._pause_: + return + + +class CoregistrationObjectDynamic(threading.Thread): + """ + Thread to update the coordinates with the fiducial points + co-registration method while the Navigation Button is pressed. + Sleep function in run method is used to avoid blocking GUI and + for better real-time navigation + """ + + def __init__(self, coreg_data, nav_id, trck_info): + threading.Thread.__init__(self) + self.coreg_data = coreg_data + self.nav_id = nav_id + self.trck_info = trck_info + self._pause_ = False + self.start() + + def stop(self): + self._pause_ = True + + def run(self): + + m_change, obj_ref_mode, t_obj_raw, s0_raw, r_s0_raw, s0_dyn, m_obj_raw, r_obj_img = self.coreg_data + trck_init, trck_id, trck_mode = self.trck_info + + while self.nav_id: + coord_raw = dco.GetCoordinates(trck_init, trck_id, trck_mode) + + as1, bs1, gs1 = radians(coord_raw[obj_ref_mode, 3:]) + r_probe = asmatrix(tr.euler_matrix(as1, bs1, gs1, 'rzyx')) + t_probe_raw = asmatrix(tr.translation_matrix(coord_raw[obj_ref_mode, :3])) + t_offset_aux = r_s0_raw.I * r_probe * t_obj_raw + t_offset = asmatrix(identity(4)) + t_offset[:, -1] = t_offset_aux[:, -1] + t_probe = s0_raw * t_offset * s0_raw.I * t_probe_raw + m_probe = asmatrix(tr.concatenate_matrices(t_probe, r_probe)) + + a, b, g = radians(coord_raw[1, 3:]) + r_ref = tr.euler_matrix(a, b, g, 'rzyx') + t_ref = tr.translation_matrix(coord_raw[1, :3]) + m_ref = asmatrix(tr.concatenate_matrices(t_ref, r_ref)) + + m_dyn = m_ref.I * m_probe + m_dyn[2, -1] = -m_dyn[2, -1] + + m_img = m_change * m_dyn + r_obj = r_obj_img * m_obj_raw.I * s0_dyn.I * m_dyn * m_obj_raw + + m_img[:3, :3] = r_obj[:3, :3] + + scale, shear, angles, trans, persp = tr.decompose_matrix(m_img) + + coord = m_img[0, -1], m_img[1, -1], m_img[2, -1],\ + degrees(angles[0]), degrees(angles[1]), degrees(angles[2]) + + wx.CallAfter(Publisher.sendMessage, 'Co-registered points', arg=m_img, position=coord) + wx.CallAfter(Publisher.sendMessage, 'Update object matrix', m_img=m_img, coord=coord) + + # TODO: Optimize the value of sleep for each tracking device. + sleep(0.175) + + # Debug tracker is not working with 0.175 so changed to 0.2 + # However, 0.2 is too low update frequency ~5 Hz. Need optimization URGENTLY. + # sleep(.3) + + # partially working for translate and offset, + # but offset is kept always in same axis, have to fix for rotation + # M_dyn = M_reference.I * T_stylus + # M_dyn[2, -1] = -M_dyn[2, -1] + # M_dyn_ch = M_change * M_dyn + # ddd = M_dyn_ch[0, -1], M_dyn_ch[1, -1], M_dyn_ch[2, -1] + # M_dyn_ch[:3, -1] = asmatrix(db.flip_x_m(ddd)).reshape([3, 1]) + # M_final = S0 * M_obj_trans_0 * S0.I * M_dyn_ch + + # this works for static reference object rotation + # R_dyn = M_vtk * M_obj_rot_raw.I * S0_rot_raw.I * R_stylus * M_obj_rot_raw + # this works for dynamic reference in rotation but not in translation + # R_dyn = M_vtk * M_obj_rot_raw.I * S0_rot_dyn.I * R_reference.I * R_stylus * M_obj_rot_raw + + if self._pause_: + return diff -Nru invesalius-3.1.1/invesalius/data/cursor_actors.py invesalius-3.1.99991/invesalius/data/cursor_actors.py --- invesalius-3.1.1/invesalius/data/cursor_actors.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/cursor_actors.py 2018-08-17 16:41:39.000000000 +0000 @@ -205,7 +205,7 @@ def _set_colour(self, imagedata, colour): scalar_range = int(imagedata.GetScalarRange()[1]) - r, g, b = colour + r, g, b = colour[:3] # map scalar values into colors lut_mask = vtk.vtkLookupTable() @@ -319,21 +319,21 @@ """ Function to plot the Retangle """ - print "Building rectangle cursor", self.orientation + print("Building rectangle cursor", self.orientation) r = self.radius sx, sy, sz = self.spacing if self.orientation == 'AXIAL': - x = math.floor(2*r/sx) - y = math.floor(2*r/sy) + x = int(math.floor(2*r/sx)) + y = int(math.floor(2*r/sy)) z = 1 elif self.orientation == 'CORONAL': - x = math.floor(r/sx) + x = int(math.floor(r/sx)) y = 1 - z = math.floor(r/sz) + z = int(math.floor(r/sz)) elif self.orientation == 'SAGITAL': x = 1 - y = math.floor(r/sy) - z = math.floor(r/sz) + y = int(math.floor(r/sy)) + z = int(math.floor(r/sz)) rectangle_m = numpy.ones((z, y, x), dtype='uint8') rectangle_i = to_vtk(rectangle_m, self.spacing, 0, self.orientation) @@ -353,13 +353,13 @@ r = self.radius sx, sy, sz = self.spacing if self.orientation == 'AXIAL': - x = math.floor(2*r/sx) - y = math.floor(2*r/sy) + x = int(math.floor(2*r/sx)) + y = int(math.floor(2*r/sy)) elif self.orientation == 'CORONAL': - x = math.floor(r/sx) - y = math.floor(r/sz) + x = int(math.floor(r/sx)) + y = int(math.floor(r/sz)) elif self.orientation == 'SAGITAL': - x = math.floor(r/sy) - y = math.floor(r/sz) + x = int(math.floor(r/sy)) + y = int(math.floor(r/sz)) self.points = numpy.ones((y, x), dtype='bool') diff -Nru invesalius-3.1.1/invesalius/data/geometry.py invesalius-3.1.99991/invesalius/data/geometry.py --- invesalius-3.1.1/invesalius/data/geometry.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/geometry.py 2018-08-17 16:41:39.000000000 +0000 @@ -18,6 +18,8 @@ # detalhes. #-------------------------------------------------------------------------- +from six import with_metaclass + import numpy as np import math import vtk @@ -27,14 +29,12 @@ import invesalius.constants as const -class Box(object): +class Box(with_metaclass(utils.Singleton, object)): """ - This class is a data structure for storing the + This class is a data structure for storing the coordinates (min and max) of box used in crop-mask. """ - __metaclass__= utils.Singleton - def __init__(self): self.xi = None self.xf = None @@ -136,7 +136,7 @@ self.axial[const.AXIAL_RIGHT] = [[self.xf + (self.xs/2), self.yi, self.zi],\ [self.xf + (self.xs/2), self.yf, self.zf]] - Publisher.sendMessage('Update crop limits into gui', self.GetLimits()) + Publisher.sendMessage('Update crop limits into gui', limits=self.GetLimits()) def GetLimits(self): @@ -356,7 +356,7 @@ x_pos_sl = x_pos_sl_ * xs y_pos_sl = y_pos_sl_ * ys - for k, p in self.box.axial.iteritems(): + for k, p in self.box.axial.items(): p0 = p[0] p1 = p[1] @@ -386,7 +386,7 @@ x_pos_sl = x_pos_sl_ * xs y_pos_sl = y_pos_sl_ * zs - for k, p in self.box.coronal.iteritems(): + for k, p in self.box.coronal.items(): p0 = p[0] p1 = p[1] @@ -415,7 +415,7 @@ x_pos_sl = x_pos_sl_ * ys y_pos_sl = y_pos_sl_ * zs - for k, p in self.box.sagital.iteritems(): + for k, p in self.box.sagital.items(): p0 = p[0] p1 = p[1] diff -Nru invesalius-3.1.1/invesalius/data/imagedata_utils.py invesalius-3.1.99991/invesalius/data/imagedata_utils.py --- invesalius-3.1.1/invesalius/data/imagedata_utils.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/imagedata_utils.py 2018-08-17 16:41:39.000000000 +0000 @@ -19,6 +19,7 @@ import math import os +import sys import tempfile import gdcm @@ -27,7 +28,7 @@ import vtkgdcm from wx.lib.pubsub import pub as Publisher -from scipy.ndimage import shift +from scipy.ndimage import shift, zoom from vtk.util import numpy_support import invesalius.constants as const @@ -35,6 +36,16 @@ import invesalius.reader.bitmap_reader as bitmap_reader import invesalius.utils as utils import invesalius.data.converters as converters + +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + # TODO: Test cases which are originally in sagittal/coronal orientation # and have gantry @@ -69,20 +80,20 @@ dimensions = imagedata.GetDimensions() if resolution_percentage: - px = math.ceil(dimensions[0] * resolution_percentage) - py = math.ceil(dimensions[1] * resolution_percentage) - - if abs(extent[1]-extent[3]) < abs(extent[3]-extent[5]): - f = extent[1] - elif abs(extent[1]-extent[5]) < abs(extent[1] - extent[3]): - f = extent[1] - elif abs(extent[3]-extent[5]) < abs(extent[1] - extent[3]): - f = extent[3] + factor_x = resolution_percentage + factor_y = resolution_percentage else: - f = extent[1] + if abs(extent[1]-extent[3]) < abs(extent[3]-extent[5]): + f = extent[1] + elif abs(extent[1]-extent[5]) < abs(extent[1] - extent[3]): + f = extent[1] + elif abs(extent[3]-extent[5]) < abs(extent[1] - extent[3]): + f = extent[3] + else: + f = extent[1] - factor_x = px/float(f+1) - factor_y = py/float(f+1) + factor_x = px/float(f+1) + factor_y = py/float(f+1) resample = vtk.vtkImageResample() resample.SetInputData(imagedata) @@ -98,6 +109,35 @@ return resample.GetOutput() + +def resize_slice(im_array, resolution_percentage): + """ + Uses ndimage.zoom to resize a slice. + + input: + im_array: slice as a numpy array. + resolution_percentage: percentage of resize. + """ + out = zoom(im_array, resolution_percentage, im_array.dtype, order=2) + return out + + +def read_dcm_slice_as_np(filename, resolution_percentage=1.0): + """ + read a dicom slice file and return the slice as numpy ndarray + """ + dcm_reader = vtkgdcm.vtkGDCMImageReader() + dcm_reader.SetFileName(filename) + dcm_reader.Update() + image = dcm_reader.GetOutput() + if resolution_percentage < 1.0: + image = ResampleImage2D(image, resolution_percentage=resolution_percentage) + dx, dy, dz = image.GetDimensions() + im_array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) + im_array.shape = dy, dx + return im_array + + def FixGantryTilt(matrix, spacing, tilt): """ Fix gantry tilt given a vtkImageData and the tilt value. Return new @@ -217,11 +257,79 @@ """ voi = vtk.vtkExtractVOI() voi.SetVOI(xi,xf,yi,yf,zi,zf) - voi.SetInput(imagedata) + voi.SetInputData(imagedata) voi.SetSampleRate(1, 1, 1) voi.Update() return voi.GetOutput() + +def create_dicom_thumbnails(filename, window=None, level=None): + rvtk = vtkgdcm.vtkGDCMImageReader() + rvtk.SetFileName(utils.encode(filename, const.FS_ENCODE)) + rvtk.Update() + + img = rvtk.GetOutput() + if window is None or level is None: + _min, _max = img.GetScalarRange() + window = _max - _min + level = _min + window / 2 + + dx, dy, dz = img.GetDimensions() + + if dz > 1: + thumbnail_paths = [] + for i in range(dz): + img_slice = ExtractVOI(img, 0, dx-1, 0, dy-1, i, i+1) + + colorer = vtk.vtkImageMapToWindowLevelColors() + colorer.SetInputData(img_slice) + colorer.SetWindow(window) + colorer.SetLevel(level) + colorer.SetOutputFormatToRGB() + colorer.Update() + + resample = vtk.vtkImageResample() + resample.SetInputData(colorer.GetOutput()) + resample.SetAxisMagnificationFactor ( 0, 0.25 ) + resample.SetAxisMagnificationFactor ( 1, 0.25 ) + resample.SetAxisMagnificationFactor ( 2, 1 ) + resample.Update() + + thumbnail_path = tempfile.mktemp() + + write_png = vtk.vtkPNGWriter() + write_png.SetInputData(resample.GetOutput()) + write_png.SetFileName(thumbnail_path) + write_png.Write() + + thumbnail_paths.append(thumbnail_path) + + return thumbnail_paths + else: + colorer = vtk.vtkImageMapToWindowLevelColors() + colorer.SetInputData(img) + colorer.SetWindow(window) + colorer.SetLevel(level) + colorer.SetOutputFormatToRGB() + colorer.Update() + + resample = vtk.vtkImageResample() + resample.SetInputData(colorer.GetOutput()) + resample.SetAxisMagnificationFactor ( 0, 0.25 ) + resample.SetAxisMagnificationFactor ( 1, 0.25 ) + resample.SetAxisMagnificationFactor ( 2, 1 ) + resample.Update() + + thumbnail_path = tempfile.mktemp() + + write_png = vtk.vtkPNGWriter() + write_png.SetInputData(resample.GetOutput()) + write_png.SetFileName(thumbnail_path) + write_png.Write() + + return thumbnail_path + + def CreateImageData(filelist, zspacing, xyspacing,size, bits, use_dcmspacing): message = _("Generating multiplanar visualization...") @@ -247,7 +355,7 @@ update_progress= vtk_utils.ShowProgress(1, dialog_type = "ProgressDialog") array = vtk.vtkStringArray() - for x in xrange(len(filelist)): + for x in range(len(filelist)): array.InsertValue(x,filelist[x]) reader = vtkgdcm.vtkGDCMImageReader() @@ -277,7 +385,7 @@ # Reformat each slice - for x in xrange(len(filelist)): + for x in range(len(filelist)): # TODO: We need to check this automatically according # to each computer's architecture # If the resolution of the matrix is too large @@ -351,7 +459,7 @@ update_progress= vtk_utils.ShowProgress(1, dialog_type = "ProgressDialog") array = vtk.vtkStringArray() - for x in xrange(len(filelist)): + for x in range(len(filelist)): if not self.running: return False array.InsertValue(x,filelist[x]) @@ -383,7 +491,7 @@ # Reformat each slice - for x in xrange(len(filelist)): + for x in range(len(filelist)): # TODO: We need to check this automatically according # to each computer's architecture # If the resolution of the matrix is too large @@ -525,74 +633,73 @@ message = _("Generating multiplanar visualization...") update_progress= vtk_utils.ShowProgress(len(files) - 1, dialog_type = "ProgressDialog") + first_slice = read_dcm_slice_as_np(files[0], resolution_percentage) + slice_size = first_slice.shape[::-1] + temp_file = tempfile.mktemp() if orientation == 'SAGITTAL': - if resolution_percentage == 1.0: - shape = slice_size[0], slice_size[1], len(files) - else: - shape = int(math.ceil(slice_size[0]*resolution_percentage)),\ - int(math.ceil(slice_size[1]*resolution_percentage)), len(files) - + shape = slice_size[0], slice_size[1], len(files) elif orientation == 'CORONAL': - if resolution_percentage == 1.0: - shape = slice_size[1], len(files), slice_size[0] - else: - shape = int(math.ceil(slice_size[1]*resolution_percentage)), len(files),\ - int(math.ceil(slice_size[0]*resolution_percentage)) + shape = slice_size[1], len(files), slice_size[0] else: - if resolution_percentage == 1.0: - shape = len(files), slice_size[1], slice_size[0] - else: - shape = len(files), int(math.ceil(slice_size[1]*resolution_percentage)),\ - int(math.ceil(slice_size[0]*resolution_percentage)) + shape = len(files), slice_size[1], slice_size[0] matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) - dcm_reader = vtkgdcm.vtkGDCMImageReader() - cont = 0 - max_scalar = None - min_scalar = None - for n, f in enumerate(files): - dcm_reader.SetFileName(f) - dcm_reader.Update() - image = dcm_reader.GetOutput() - - if resolution_percentage != 1.0: - image_resized = ResampleImage2D(image, px=None, py=None,\ - resolution_percentage = resolution_percentage, update_progress = None) - - image = image_resized + im_array = read_dcm_slice_as_np(f, resolution_percentage) - min_aux, max_aux = image.GetScalarRange() - if min_scalar is None or min_aux < min_scalar: - min_scalar = min_aux - - if max_scalar is None or max_aux > max_scalar: - max_scalar = max_aux - - array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) if orientation == 'CORONAL': - array.shape = matrix.shape[0], matrix.shape[2] - matrix[:, shape[1] - n - 1, :] = array + matrix[:, shape[1] - n - 1, :] = im_array elif orientation == 'SAGITTAL': - array.shape = matrix.shape[0], matrix.shape[1] # TODO: Verify if it's necessary to add the slices swapped only in # sagittal rmi or only in # Rasiane's case or is necessary in all # sagittal cases. - matrix[:, :, n] = array + matrix[:, :, n] = im_array else: - array.shape = matrix.shape[1], matrix.shape[2] - matrix[n] = array - update_progress(cont,message) - cont += 1 + matrix[n] = im_array + update_progress(n, message) matrix.flush() - scalar_range = min_scalar, max_scalar + scalar_range = matrix.min(), matrix.max() return matrix, scalar_range, temp_file +def dcmmf2memmap(dcm_file, orientation): + r = vtkgdcm.vtkGDCMImageReader() + r.SetFileName(dcm_file) + r.Update() + + temp_file = tempfile.mktemp() + + o = r.GetOutput() + x, y, z = o.GetDimensions() + spacing = o.GetSpacing() + + matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=(z, y, x)) + + d = numpy_support.vtk_to_numpy(o.GetPointData().GetScalars()) + d.shape = z, y, x + if orientation == 'CORONAL': + matrix.shape = y, z, x + for n in range(z): + matrix[:, n, :] = d[n] + elif orientation == 'SAGITTAL': + matrix.shape = x, z, y + for n in range(z): + matrix[:, :, n] = d[n] + else: + matrix[:] = d + + matrix.flush() + scalar_range = matrix.min(), matrix.max() + + print("ORIENTATION", orientation) + + return matrix, spacing, scalar_range, temp_file + + def img2memmap(group): """ From a nibabel image data creates a memmap file in the temp folder and diff -Nru invesalius-3.1.1/invesalius/data/mask.py invesalius-3.1.99991/invesalius/data/mask.py --- invesalius-3.1.1/invesalius/data/mask.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/mask.py 2018-08-17 16:41:39.000000000 +0000 @@ -46,7 +46,7 @@ def _save_array(self, array): np.save(self.filename, array) - print "Saving history", self.index, self.orientation, self.filename, self.clean + print("Saving history", self.index, self.orientation, self.filename, self.clean) def commit_history(self, mvolume): array = np.load(self.filename) @@ -65,10 +65,10 @@ elif self.orientation == 'VOLUME': mvolume[:] = array - print "applying to", self.orientation, "at slice", self.index + print("applying to", self.orientation, "at slice", self.index) def __del__(self): - print "Removing", self.filename + print("Removing", self.filename) os.remove(self.filename) @@ -78,8 +78,8 @@ self.index = -1 self.size = size * 2 - Publisher.sendMessage("Enable undo", False) - Publisher.sendMessage("Enable redo", False) + Publisher.sendMessage("Enable undo", value=False) + Publisher.sendMessage("Enable redo", value=False) def new_node(self, index, orientation, array, p_array, clean): # Saving the previous state, used to undo/redo correctly. @@ -99,9 +99,9 @@ self.history.append(node) self.index += 1 - print "INDEX", self.index, len(self.history), self.history - Publisher.sendMessage("Enable undo", True) - Publisher.sendMessage("Enable redo", False) + print("INDEX", self.index, len(self.history), self.history) + Publisher.sendMessage("Enable undo", value=True) + Publisher.sendMessage("Enable redo", value=False) def undo(self, mvolume, actual_slices=None): h = self.history @@ -114,7 +114,7 @@ self.index -= 1 h[self.index].commit_history(mvolume) self._reload_slice(self.index) - Publisher.sendMessage("Enable redo", True) + Publisher.sendMessage("Enable redo", value=True) elif actual_slices and actual_slices[h[self.index - 1].orientation] != h[self.index - 1].index: self._reload_slice(self.index - 1) else: @@ -124,11 +124,11 @@ self.index -= 1 h[self.index].commit_history(mvolume) self._reload_slice(self.index) - Publisher.sendMessage("Enable redo", True) + Publisher.sendMessage("Enable redo", value=True) if self.index == 0: - Publisher.sendMessage("Enable undo", False) - print "AT", self.index, len(self.history), self.history[self.index].filename + Publisher.sendMessage("Enable undo", value=False) + print("AT", self.index, len(self.history), self.history[self.index].filename) def redo(self, mvolume, actual_slices=None): h = self.history @@ -142,7 +142,7 @@ self.index += 1 h[self.index].commit_history(mvolume) self._reload_slice(self.index) - Publisher.sendMessage("Enable undo", True) + Publisher.sendMessage("Enable undo", value=True) elif actual_slices and actual_slices[h[self.index + 1].orientation] != h[self.index + 1].index: self._reload_slice(self.index + 1) else: @@ -152,15 +152,15 @@ self.index += 1 h[self.index].commit_history(mvolume) self._reload_slice(self.index) - Publisher.sendMessage("Enable undo", True) + Publisher.sendMessage("Enable undo", value=True) if self.index == len(h) - 1: - Publisher.sendMessage("Enable redo", False) - print "AT", self.index, len(h), h[self.index].filename + Publisher.sendMessage("Enable redo", value=False) + print("AT", self.index, len(h), h[self.index].filename) def _reload_slice(self, index): Publisher.sendMessage(('Set scroll position', self.history[index].orientation), - self.history[index].index) + index=self.history[index].index) def _config_undo_redo(self, visible): v_undo = False @@ -174,14 +174,14 @@ elif self.index == len(self.history) - 1: v_redo = False - Publisher.sendMessage("Enable undo", v_undo) - Publisher.sendMessage("Enable redo", v_redo) + Publisher.sendMessage("Enable undo", value=v_undo) + Publisher.sendMessage("Enable redo", value=v_redo) def clear_history(self): self.history = [] self.index = -1 - Publisher.sendMessage("Enable undo", False) - Publisher.sendMessage("Enable redo", False) + Publisher.sendMessage("Enable undo", value=False) + Publisher.sendMessage("Enable redo", value=False) class Mask(): @@ -236,7 +236,7 @@ mask['index'] = self.index mask['name'] = self.name - mask['colour'] = self.colour + mask['colour'] = self.colour[:3] mask['opacity'] = self.opacity mask['threshold_range'] = self.threshold_range mask['edition_threshold_range'] = self.edition_threshold_range @@ -273,8 +273,7 @@ path = os.path.join(dirpath, mask_file) self._open_mask(path, tuple(shape)) - def OnFlipVolume(self, pubsub_evt): - axis = pubsub_evt.data + def OnFlipVolume(self, axis): submatrix = self.matrix[1:, 1:, 1:] if axis == 0: submatrix[:] = submatrix[::-1] @@ -286,16 +285,15 @@ submatrix[:] = submatrix[:, :, ::-1] self.matrix[0, 0, 1::] = self.matrix[0, 0, :0:-1] - def OnSwapVolumeAxes(self, pubsub_evt): - axis0, axis1 = pubsub_evt.data + def OnSwapVolumeAxes(self, axes): + axis0, axis1 = axes self.matrix = self.matrix.swapaxes(axis0, axis1) - print type(self.matrix) def _save_mask(self, filename): shutil.copyfile(self.temp_file, filename) def _open_mask(self, filename, shape, dtype='uint8'): - print ">>", filename, shape + print(">>", filename, shape) self.temp_file = filename self.matrix = np.memmap(filename, shape=shape, dtype=dtype, mode="r+") diff -Nru invesalius-3.1.1/invesalius/data/measures.py invesalius-3.1.99991/invesalius/data/measures.py --- invesalius-3.1.1/invesalius/data/measures.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/measures.py 2018-08-17 16:41:39.000000000 +0000 @@ -1,6 +1,8 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- +from six import with_metaclass + import math import random import sys @@ -46,11 +48,10 @@ MEASURE_TEXT_COLOUR = (0, 0, 0) MEASURE_TEXTBOX_COLOUR = (255, 255, 165, 255) -class MeasureData: +class MeasureData(with_metaclass(utils.Singleton)): """ Responsible to keep measures data. """ - __metaclass__= utils.Singleton def __init__(self): self.measures = {const.SURFACE: {}, const.AXIAL: {}, @@ -119,14 +120,9 @@ Publisher.subscribe(self._change_measure_point_pos, 'Change measurement point position') Publisher.subscribe(self.OnCloseProject, 'Close project data') - def _load_measurements(self, pubsub_evt): - try: - dict, spacing = pubsub_evt.data - except ValueError: - dict = pubsub_evt.data - spacing = 1.0, 1.0, 1.0 - for i in dict: - m = dict[i] + def _load_measurements(self, measurement_dict, spacing=(1.0, 1.0, 1.0)): + for i in measurement_dict: + m = measurement_dict[i] if m.location == const.AXIAL: radius = min(spacing[1], spacing[2]) * const.PROP_MEASURE @@ -153,7 +149,7 @@ if m.location == const.SURFACE: Publisher.sendMessage(("Add actors " + str(m.location)), - (actors, m.slice_number)) + actors=actors) self.current = None if not m.visible: @@ -163,28 +159,7 @@ else: Publisher.sendMessage('Redraw canvas') - def _add_point(self, pubsub_evt): - position = pubsub_evt.data[0] - type = pubsub_evt.data[1] # Linear or Angular - location = pubsub_evt.data[2] # 3D, AXIAL, SAGITAL, CORONAL - - if location == const.SURFACE: - slice_number = 0 - try: - radius = pubsub_evt.data[3] - except IndexError: - radius = const.PROP_MEASURE - else: - try: - slice_number = pubsub_evt.data[3] - except IndexError: - slice_number = 0 - - try: - radius = pubsub_evt.data[4] - except IndexError: - radius = const.PROP_MEASURE - + def _add_point(self, position, type, location, slice_number=0, radius=const.PROP_MEASURE): to_remove = False if self.current is None: to_create = True @@ -232,8 +207,7 @@ m.points.append(position) if m.location == const.SURFACE: - Publisher.sendMessage("Add actors " + str(location), - (actors, m.slice_number)) + Publisher.sendMessage("Add actors " + str(location), actors=actors) if self.current not in self.measures: self.measures.append(self.current) @@ -253,14 +227,12 @@ msg = 'Update measurement info in GUI', Publisher.sendMessage(msg, - (index, name, colour, - location, - type_, - value)) + index=index, name=name, + colour=colour, location=location, + type_=type_, value=value) self.current = None - def _change_measure_point_pos(self, pubsub_evt): - index, npoint, pos = pubsub_evt.data + def _change_measure_point_pos(self, index, npoint, pos): m, mr = self.measures[index] x, y, z = pos if npoint == 0: @@ -287,17 +259,14 @@ value = u"%.3f°"% m.value Publisher.sendMessage('Update measurement info in GUI', - (index, name, colour, - location, - type_, - value)) - - def _change_name(self, pubsub_evt): - index, new_name = pubsub_evt.data - self.measures[index].name = new_name + index=index, name=name, colour=colour, + location=location, type_=type_, + value=value) + + def _change_name(self, index, name): + self.measures[index][0].name = name - def _remove_measurements(self, pubsub_evt): - indexes = pubsub_evt.data + def _remove_measurements(self, indexes): for index in indexes: m, mr = self.measures.pop(index) try: @@ -307,16 +276,15 @@ pass prj.Project().RemoveMeasurement(index) if m.location == const.SURFACE: - Publisher.sendMessage(('Remove actors ' + str(m.location)), - (mr.GetActors(), m.slice_number)) + Publisher.sendMessage('Remove actors ' + str(m.location), + actors=mr.GetActors()) Publisher.sendMessage('Redraw canvas') Publisher.sendMessage('Render volume viewer') session = ses.Session() session.ChangeProject() - def _set_visibility(self, pubsub_evt): - index, visibility = pubsub_evt.data + def _set_visibility(self, index, visibility): m, mr = self.measures[index] m.visible = visibility mr.SetVisibility(visibility) @@ -325,7 +293,7 @@ else: Publisher.sendMessage('Redraw canvas') - def _rm_incomplete_measurements(self, pubsub_evt): + def _rm_incomplete_measurements(self): if self.current is None: return @@ -333,12 +301,12 @@ if not mr.IsComplete(): idx = self.measures._list_measures.index((m, mr)) self.measures.remove((m, mr)) - Publisher.sendMessage("Remove GUI measurement", idx) + Publisher.sendMessage("Remove GUI measurement", measure_index=idx) actors = mr.GetActors() slice_number = self.current[0].slice_number if m.location == const.SURFACE: Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)), - (actors, slice_number)) + actors=actors) if self.current[0].location == const.SURFACE: Publisher.sendMessage('Render volume viewer') else: @@ -348,7 +316,7 @@ # self.measures.pop() self.current = None - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.measures.clean() @@ -358,7 +326,7 @@ Measurement.general_index += 1 self.index = Measurement.general_index self.name = const.MEASURE_NAME_PATTERN %(self.index+1) - self.colour = const.MEASURE_COLOUR.next() + self.colour = next(const.MEASURE_COLOUR) self.value = 0 self.location = const.SURFACE # AXIAL, CORONAL, SAGITTAL self.type = const.LINEAR # ANGULAR @@ -649,7 +617,7 @@ def Remove(self): actors = self.GetActors() - Publisher.sendMessage("Remove actors " + str(const.SURFACE), (actors,)) + Publisher.sendMessage("Remove actors " + str(const.SURFACE), actors=actors) def __del__(self): self.Remove() @@ -832,7 +800,7 @@ for p in self.points: coord.SetValue(p) cx, cy = coord.GetComputedDoubleDisplayValue(canvas.evt_renderer) - print cx, cy + print(cx, cy) # canvas.draw_circle((cx, cy), 2.5) points.append((cx, cy)) @@ -900,7 +868,7 @@ def Remove(self): actors = self.GetActors() - Publisher.sendMessage("Remove actors " + str(const.SURFACE), (actors,)) + Publisher.sendMessage("Remove actors " + str(const.SURFACE), actors=actors) def SetRenderer(self, renderer): if self.point_actor1: diff -Nru invesalius-3.1.1/invesalius/data/polydata_utils.py invesalius-3.1.99991/invesalius/data/polydata_utils.py --- invesalius-3.1.1/invesalius/data/polydata_utils.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/polydata_utils.py 2018-08-17 16:41:39.000000000 +0000 @@ -78,7 +78,7 @@ Fill holes in the given polydata. """ # Filter used to detect and fill holes. Only fill - print "Filling polydata" + print("Filling polydata") filled_polydata = vtk.vtkFillHolesFilter() filled_polydata.SetInputData(polydata) filled_polydata.SetHoleSize(500) @@ -133,9 +133,9 @@ def Import(filename): reader = vtk.vtkXMLPolyDataReader() - if isinstance(filename, unicode): + try: reader.SetFileName(filename.encode(wx.GetDefaultPyEncoding())) - else: + except AttributeError: reader.SetFileName(filename) reader.Update() return reader.GetOutput() @@ -198,7 +198,7 @@ if progress: UpdateProgress = vu.ShowProgress(progress) - for region in xrange(nregions): + for region in range(nregions): conn.InitializeSpecifiedRegionList() conn.AddSpecifiedRegion(region) conn.Update() diff -Nru invesalius-3.1.1/invesalius/data/record_coords.py invesalius-3.1.99991/invesalius/data/record_coords.py --- invesalius-3.1.1/invesalius/data/record_coords.py 1970-01-01 00:00:00.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/record_coords.py 2018-08-17 16:41:39.000000000 +0000 @@ -0,0 +1,67 @@ +#-------------------------------------------------------------------------- +# 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 +# da Licenca. +# +# Este programa eh distribuido na expectativa de ser util, mas SEM +# QUALQUER GARANTIA; sem mesmo a garantia implicita de +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais +# detalhes. +#-------------------------------------------------------------------------- + +import threading +import time + +import wx +from numpy import array, savetxt, hstack,vstack, asarray +import invesalius.gui.dialogs as dlg +from wx.lib.pubsub import pub as Publisher + + +class Record(threading.Thread): + """ + Thread created to save obj coords with software during neuronavigation + """ + + def __init__(self, nav_id, timestamp): + threading.Thread.__init__(self) + self.nav_id = nav_id + self.coord = None + self.timestamp = timestamp + self.coord_list = array([]) + self.__bind_events() + self._pause_ = False + self.start() + + def __bind_events(self): + Publisher.subscribe(self.UpdateCurrentCoords, 'Co-registered points') + + def UpdateCurrentCoords(self, arg, position): + self.coord = asarray(position) + + def stop(self): + self._pause_ = True + #save coords dialog + filename = dlg.ShowSaveCoordsDialog("coords.csv") + if filename: + savetxt(filename, self.coord_list, delimiter=',', fmt='%.4f', header="time, x, y, z, a, b, g", comments="") + + def run(self): + initial_time = time.time() + while self.nav_id: + relative_time = asarray(time.time() - initial_time) + time.sleep(self.timestamp) + if self.coord_list.size == 0: + self.coord_list = hstack((relative_time, self.coord)) + else: + self.coord_list = vstack((self.coord_list, hstack((relative_time, self.coord)))) + if self._pause_: + return diff -Nru invesalius-3.1.1/invesalius/data/slice_.py invesalius-3.1.99991/invesalius/data/slice_.py --- invesalius-3.1.1/invesalius/data/slice_.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/slice_.py 2018-08-17 16:41:39.000000000 +0000 @@ -16,6 +16,8 @@ # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais # detalhes. #-------------------------------------------------------------------------- +from six import with_metaclass + import os import tempfile @@ -72,12 +74,10 @@ self.vtk_mask = None -class Slice(object): - __metaclass__= utils.Singleton - # Only one slice will be initialized per time (despite several viewers - # show it from distinct perspectives). - # Therefore, we use Singleton design pattern for implementing it. - +# Only one slice will be initialized per time (despite several viewers +# show it from distinct perspectives). +# Therefore, we use Singleton design pattern for implementing it. +class Slice(with_metaclass(utils.Singleton, object)): def __init__(self): self.current_mask = None self.blend_filter = None @@ -219,10 +219,9 @@ buffer_.discard_vtk_mask() buffer_.discard_mask() - def OnRemoveMasks(self, pubsub_evt): - selected_items = pubsub_evt.data + def OnRemoveMasks(self, mask_indexes): proj = Project() - for item in selected_items: + for item in mask_indexes: proj.RemoveMask(item) # if the deleted mask is the current mask, cleans the current mask @@ -234,14 +233,13 @@ buffer_.discard_vtk_mask() buffer_.discard_mask() - Publisher.sendMessage('Show mask', (item, 0)) + Publisher.sendMessage('Show mask', index=item, value=False) Publisher.sendMessage('Reload actual slice') - def OnDuplicateMasks(self, pubsub_evt): - selected_items = pubsub_evt.data + def OnDuplicateMasks(self, mask_indexes): proj = Project() mask_dict = proj.mask_dict - for index in selected_items: + for index in mask_indexes: original_mask = mask_dict[index] # compute copy name name = original_mask.name @@ -251,34 +249,32 @@ copy_mask = original_mask.copy(new_name) self._add_mask_into_proj(copy_mask) - def OnEnableStyle(self, pubsub_evt): - state = pubsub_evt.data - if (state in const.SLICE_STYLES): - new_state = self.interaction_style.AddState(state) - Publisher.sendMessage('Set slice interaction style', new_state) - self.state = state - - def OnDisableStyle(self, pubsub_evt): - state = pubsub_evt.data - if (state in const.SLICE_STYLES): - new_state = self.interaction_style.RemoveState(state) - Publisher.sendMessage('Set slice interaction style', new_state) + def OnEnableStyle(self, style): + if (style in const.SLICE_STYLES): + new_state = self.interaction_style.AddState(style) + Publisher.sendMessage('Set slice interaction style', style=new_state) + self.state = style + + def OnDisableStyle(self, style): + if (style in const.SLICE_STYLES): + new_state = self.interaction_style.RemoveState(style) + Publisher.sendMessage('Set slice interaction style', style=new_state) - if (state == const.SLICE_STATE_EDITOR): + if (style == const.SLICE_STATE_EDITOR): Publisher.sendMessage('Set interactor default cursor') self.state = new_state - def OnDisableActualStyle(self, pubsub_evt): + def OnDisableActualStyle(self): actual_state = self.interaction_style.GetActualState() if actual_state != const.STATE_DEFAULT: new_state = self.interaction_style.RemoveState(actual_state) - Publisher.sendMessage('Set slice interaction style', new_state) + Publisher.sendMessage('Set slice interaction style', style=new_state) # if (actual_state == const.SLICE_STATE_EDITOR): # Publisher.sendMessage('Set interactor default cursor') self.state = new_state - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.CloseProject() def CloseProject(self): @@ -310,39 +306,32 @@ Publisher.sendMessage('Select first item from slice menu') - def __set_current_mask_threshold_limits(self, pubsub_evt): - thresh_min = pubsub_evt.data[0] - thresh_max = pubsub_evt.data[1] + def __set_current_mask_threshold_limits(self, threshold_range): + thresh_min = threshold_range[0] + thresh_max = threshold_range[1] if self.current_mask: index = self.current_mask.index self.SetMaskEditionThreshold(index, (thresh_min, thresh_max)) - def __add_mask(self, pubsub_evt): - mask_name = pubsub_evt.data + def __add_mask(self, mask_name): self.create_new_mask(name=mask_name) self.SetMaskColour(self.current_mask.index, self.current_mask.colour) - def __add_mask_thresh(self, pubsub_evt): - mask_name = pubsub_evt.data[0] - thresh = pubsub_evt.data[1] - colour = pubsub_evt.data[2] + def __add_mask_thresh(self, mask_name, thresh, colour): self.create_new_mask(name=mask_name, threshold_range=thresh, colour=colour) self.SetMaskColour(self.current_mask.index, self.current_mask.colour) self.SelectCurrentMask(self.current_mask.index) Publisher.sendMessage('Reload actual slice') - def __select_current_mask(self, pubsub_evt): - mask_index = pubsub_evt.data - self.SelectCurrentMask(mask_index) + def __select_current_mask(self, index): + self.SelectCurrentMask(index) - def __set_current_mask_edition_threshold(self, evt_pubsub): + def __set_current_mask_edition_threshold(self, threshold_range): if self.current_mask: - threshold_range = evt_pubsub.data index = self.current_mask.index self.SetMaskEditionThreshold(index, threshold_range) - def __set_current_mask_threshold(self, evt_pubsub): - threshold_range = evt_pubsub.data + def __set_current_mask_threshold(self, threshold_range): index = self.current_mask.index self.num_gradient += 1 self.current_mask.matrix[:] = 0 @@ -359,25 +348,27 @@ # TODO: merge this code with apply_slice_buffer_to_mask b_mask = self.buffer_slices["AXIAL"].mask - n = self.buffer_slices["AXIAL"].index + 1 - self.current_mask.matrix[n, 1:, 1:] = b_mask - self.current_mask.matrix[n, 0, 0] = 1 + if b_mask is not None: + n = self.buffer_slices["AXIAL"].index + 1 + self.current_mask.matrix[n, 1:, 1:] = b_mask + self.current_mask.matrix[n, 0, 0] = 1 b_mask = self.buffer_slices["CORONAL"].mask - n = self.buffer_slices["CORONAL"].index + 1 - self.current_mask.matrix[1:, n, 1:] = b_mask - self.current_mask.matrix[0, n, 0] = 1 + if b_mask is not None: + n = self.buffer_slices["CORONAL"].index + 1 + self.current_mask.matrix[1:, n, 1:] = b_mask + self.current_mask.matrix[0, n, 0] = 1 b_mask = self.buffer_slices["SAGITAL"].mask - n = self.buffer_slices["SAGITAL"].index + 1 - self.current_mask.matrix[1:, 1:, n] = b_mask - self.current_mask.matrix[0, 0, n] = 1 + if b_mask is not None: + n = self.buffer_slices["SAGITAL"].index + 1 + self.current_mask.matrix[1:, 1:, n] = b_mask + self.current_mask.matrix[0, 0, n] = 1 if to_reload: Publisher.sendMessage('Reload actual slice') - def __set_current_mask_threshold_actual_slice(self, evt_pubsub): - threshold_range = evt_pubsub.data + def __set_current_mask_threshold_actual_slice(self, threshold_range): index = self.current_mask.index for orientation in self.buffer_slices: self.buffer_slices[orientation].discard_vtk_mask() @@ -388,44 +379,41 @@ Publisher.sendMessage('Reload actual slice') - def __set_current_mask_colour(self, pubsub_evt): + def __set_current_mask_colour(self, colour): # "if" is necessary because wx events are calling this before any mask # has been created if self.current_mask: - colour_wx = pubsub_evt.data - colour_vtk = [c/255.0 for c in colour_wx] + colour_vtk = [c/255.0 for c in colour] self.SetMaskColour(self.current_mask.index, colour_vtk) - def __set_mask_name(self, pubsub_evt): - index, name = pubsub_evt.data + def __set_mask_name(self, index, name): self.SetMaskName(index, name) - def __show_mask(self, pubsub_evt): + def __show_mask(self, index, value): # "if" is necessary because wx events are calling this before any mask # has been created if self.current_mask: - index, value = pubsub_evt.data self.ShowMask(index, value) if not value: - Publisher.sendMessage('Select mask name in combo', -1) + Publisher.sendMessage('Select mask name in combo', index=-1) if self._type_projection != const.PROJECTION_NORMAL: self.SetTypeProjection(const.PROJECTION_NORMAL) Publisher.sendMessage('Reload actual slice') - def __hide_current_mask(self, pubsub_evt): + def __hide_current_mask(self): if self.current_mask: index = self.current_mask.index value = False - Publisher.sendMessage('Show mask', (index, value)) + Publisher.sendMessage('Show mask', index=index, value=value) - def __show_current_mask(self, pubsub_evt): + def __show_current_mask(self): if self.current_mask: index = self.current_mask.index value = True - Publisher.sendMessage('Show mask', (index, value)) + Publisher.sendMessage('Show mask', index=index, value=value) - def __clean_current_mask(self, pubsub_evt): + def __clean_current_mask(self): if self.current_mask: self.current_mask.clean() for buffer_ in self.buffer_slices.values(): @@ -838,11 +826,11 @@ proj = Project() proj.mask_dict[index].colour = colour - (r,g,b) = colour + (r,g,b) = colour[:3] colour_wx = [r*255, g*255, b*255] Publisher.sendMessage('Change mask colour in notebook', - (index, (r,g,b))) - Publisher.sendMessage('Set GUI items colour', colour_wx) + index=index, colour=(r,g,b)) + Publisher.sendMessage('Set GUI items colour', colour=colour_wx) if update: # Updating mask colour on vtkimagedata. for buffer_ in self.buffer_slices.values(): @@ -888,15 +876,16 @@ self.current_mask.matrix[n+1, 1:, 1:] = m else: slice_ = self.buffer_slices[orientation].image - self.buffer_slices[orientation].mask = (255 * ((slice_ >= thresh_min) & (slice_ <= thresh_max))).astype('uint8') + if slice_ is not None: + self.buffer_slices[orientation].mask = (255 * ((slice_ >= thresh_min) & (slice_ <= thresh_max))).astype('uint8') # Update viewer #Publisher.sendMessage('Update slice viewer') # Update data notebook (GUI) Publisher.sendMessage('Set mask threshold in notebook', - (self.current_mask.index, - self.current_mask.threshold_range)) + index=self.current_mask.index, + threshold_range=self.current_mask.threshold_range) else: proj = Project() proj.mask_dict[index].threshold_range = threshold_range @@ -932,32 +921,30 @@ "SAGITAL": SliceBuffer()} Publisher.sendMessage('Set mask threshold in notebook', - (index, - self.current_mask.threshold_range)) + index=index, + threshold_range=self.current_mask.threshold_range) Publisher.sendMessage('Set threshold values in gradient', - self.current_mask.threshold_range) - Publisher.sendMessage('Select mask name in combo', index) + threshold_range=self.current_mask.threshold_range) + Publisher.sendMessage('Select mask name in combo', index=index) Publisher.sendMessage('Update slice viewer') #--------------------------------------------------------------------------- - def CreateSurfaceFromIndex(self, pubsub_evt): - surface_parameters = pubsub_evt.data - + def CreateSurfaceFromIndex(self, surface_parameters): proj = Project() mask = proj.mask_dict[surface_parameters['options']['index']] self.do_threshold_to_all_slices(mask) - Publisher.sendMessage('Create surface', (self, mask, surface_parameters)) + Publisher.sendMessage('Create surface', + slice_=self, mask=mask, + surface_parameters=surface_parameters) def GetOutput(self): return self.blend_filter.GetOutput() - def _set_projection_type(self, pubsub_evt): - tprojection = pubsub_evt.data - self.SetTypeProjection(tprojection) + def _set_projection_type(self, projection_id): + self.SetTypeProjection(projection_id) - def _set_interpolation_method(self, pubsub_evt): - interp_method = pubsub_evt.data + def _set_interpolation_method(self, interp_method): self.SetInterpolationMethod(interp_method) def SetTypeProjection(self, tprojection): @@ -966,15 +953,15 @@ Publisher.sendMessage('Hide current mask') if tprojection == const.PROJECTION_NORMAL: - Publisher.sendMessage('Show MIP interface', False) + Publisher.sendMessage('Show MIP interface', flag=False) else: - Publisher.sendMessage('Show MIP interface', True) + Publisher.sendMessage('Show MIP interface', flag=True) self._type_projection = tprojection for buffer_ in self.buffer_slices.values(): buffer_.discard_buffer() - Publisher.sendMessage('Check projection menu', tprojection) + Publisher.sendMessage('Check projection menu', projection_id=tprojection) def SetInterpolationMethod(self, interp_method): if self.interp_method != interp_method: @@ -983,8 +970,7 @@ buffer_.discard_buffer() Publisher.sendMessage('Reload actual slice') - def UpdateWindowLevelBackground(self, pubsub_evt): - window, level = pubsub_evt.data + def UpdateWindowLevelBackground(self, window, level): self.window_width = window self.window_level = level @@ -1000,8 +986,7 @@ Publisher.sendMessage('Reload actual slice') - def UpdateColourTableBackground(self, pubsub_evt): - values = pubsub_evt.data + def UpdateColourTableBackground(self, values): self.from_= OTHER self.number_of_colours= values[0] self.saturation_range = values[1] @@ -1011,16 +996,16 @@ buffer_.discard_vtk_image() Publisher.sendMessage('Reload actual slice') - def UpdateColourTableBackgroundPlist(self, pubsub_evt): - self.values = pubsub_evt.data + def UpdateColourTableBackgroundPlist(self, values): + self.values = values self.from_= PLIST for buffer_ in self.buffer_slices.values(): buffer_.discard_vtk_image() Publisher.sendMessage('Reload actual slice') - def UpdateColourTableBackgroundWidget(self, pubsub_evt): - self.nodes = pubsub_evt.data + def UpdateColourTableBackgroundWidget(self, nodes): + self.nodes = nodes self.from_= WIDGET for buffer_ in self.buffer_slices.values(): if self._type_projection in (const.PROJECTION_NORMAL, @@ -1041,8 +1026,7 @@ Publisher.sendMessage('Reload actual slice') - def UpdateSlice3D(self, pubsub_evt): - widget, orientation = pubsub_evt.data + def UpdateSlice3D(self, widget, orientation): img = self.buffer_slices[orientation].vtk_image original_orientation = Project().original_orientation cast = vtk.vtkImageCast() @@ -1121,15 +1105,12 @@ ## update gui related to mask Publisher.sendMessage('Add mask', - (mask.index, - mask.name, - mask.threshold_range, - mask.colour)) + mask=mask) if show: self.current_mask = mask - Publisher.sendMessage('Show mask', (mask.index, 1)) - Publisher.sendMessage('Change mask selected', mask.index) + Publisher.sendMessage('Show mask', index=mask.index, value=True) + Publisher.sendMessage('Change mask selected', index=mask.index) Publisher.sendMessage('Update slice viewer') def do_ww_wl(self, image): @@ -1226,7 +1207,7 @@ """ if mask is None: mask = self.current_mask - for n in xrange(1, mask.matrix.shape[0]): + for n in range(1, mask.matrix.shape[0]): if mask.matrix[n, 0, 0] == 0: m = mask.matrix[n, 1:, 1:] mask.matrix[n, 1:, 1:] = self.do_threshold_to_a_slice(self.matrix[n-1], m, mask.threshold_range) @@ -1256,7 +1237,7 @@ def do_colour_mask(self, imagedata, opacity): scalar_range = int(imagedata.GetScalarRange()[1]) - r, g, b = self.current_mask.colour + r, g, b = self.current_mask.colour[:3] # map scalar values into colors lut_mask = vtk.vtkLookupTable() @@ -1332,9 +1313,8 @@ return blend_imagedata.GetOutput() - def _do_boolean_op(self, pubsub_evt): - op, m1, m2 = pubsub_evt.data - self.do_boolean_op(op, m1, m2) + def _do_boolean_op(self, operation, mask1, mask2): + self.do_boolean_op(operation, mask1, mask2) def do_boolean_op(self, op, m1, m2): @@ -1445,15 +1425,17 @@ self.q_orientation = np.array((1, 0, 0, 0)) self.center = [(s * d/2.0) for (d, s) in zip(self.matrix.shape[::-1], self.spacing)] - self.__clean_current_mask(None) - self.current_mask.matrix[:] = 0 + self.__clean_current_mask() + if self.current_mask: + self.current_mask.matrix[:] = 0 + self.current_mask.was_edited = False for o in self.buffer_slices: self.buffer_slices[o].discard_buffer() Publisher.sendMessage('Reload actual slice') - def __undo_edition(self, pub_evt): + def __undo_edition(self): buffer_slices = self.buffer_slices actual_slices = {"AXIAL": buffer_slices["AXIAL"].index, "CORONAL": buffer_slices["CORONAL"].index, @@ -1465,7 +1447,7 @@ self.buffer_slices[o].discard_vtk_mask() Publisher.sendMessage('Reload actual slice') - def __redo_edition(self, pub_evt): + def __redo_edition(self): buffer_slices = self.buffer_slices actual_slices = {"AXIAL": buffer_slices["AXIAL"].index, "CORONAL": buffer_slices["CORONAL"].index, @@ -1481,8 +1463,7 @@ self.matrix_filename = filename self.matrix = np.memmap(filename, shape=shape, dtype=dtype, mode='r+') - def OnFlipVolume(self, pubsub_evt): - axis = pubsub_evt.data + def OnFlipVolume(self, axis): if axis == 0: self.matrix[:] = self.matrix[::-1] elif axis == 1: @@ -1493,8 +1474,8 @@ for buffer_ in self.buffer_slices.values(): buffer_.discard_buffer() - def OnSwapVolumeAxes(self, pubsub_evt): - axis0, axis1 = pubsub_evt.data + def OnSwapVolumeAxes(self, axes): + axis0, axis1 = axes self.matrix = self.matrix.swapaxes(axis0, axis1) if (axis0, axis1) == (2, 1): self.spacing = self.spacing[1], self.spacing[0], self.spacing[2] @@ -1506,20 +1487,17 @@ for buffer_ in self.buffer_slices.values(): buffer_.discard_buffer() - def OnExportMask(self, pubsub_evt): - pass - ##imagedata = self.current_mask.imagedata - #imagedata = self.imagedata - #filename, filetype = pubsub_evt.data - #if (filetype == const.FILETYPE_IMAGEDATA): - #iu.Export(imagedata, filename) - - def _fill_holes_auto(self, pubsub_evt): - data = pubsub_evt.data - target = data['target'] - conn = data['conn'] - orientation = data['orientation'] - size = data['size'] + def OnExportMask(self, filename, filetype): + imagedata = self.current_mask.imagedata + # imagedata = self.imagedata + if (filetype == const.FILETYPE_IMAGEDATA): + iu.Export(imagedata, filename) + + def _fill_holes_auto(self, parameters): + target = parameters['target'] + conn = parameters['conn'] + orientation = parameters['orientation'] + size = parameters['size'] if target == '2D': index = self.buffer_slices[orientation].index diff -Nru invesalius-3.1.1/invesalius/data/styles.py invesalius-3.1.99991/invesalius/data/styles.py --- invesalius-3.1.1/invesalius/data/styles.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/styles.py 2018-08-17 16:41:39.000000000 +0000 @@ -17,6 +17,8 @@ # detalhes. #-------------------------------------------------------------------------- +from six import with_metaclass + import os import multiprocessing import tempfile @@ -212,12 +214,12 @@ def SetUp(self): self.viewer._set_cross_visibility(1) Publisher.sendMessage('Toggle toolbar item', - (self.state_code, True)) + _id=self.state_code, value=True) def CleanUp(self): self.viewer._set_cross_visibility(0) Publisher.sendMessage('Toggle toolbar item', - (self.state_code, False)) + _id=self.state_code, value=False) def OnCrossMouseClick(self, obj, evt): iren = obj.GetInteractor() @@ -226,7 +228,7 @@ def OnCrossMove(self, obj, evt): # The user moved the mouse with left button pressed if self.left_pressed: - print "OnCrossMove interactor style" + print("OnCrossMove interactor style") iren = obj.GetInteractor() self.ChangeCrossPosition(iren) @@ -235,29 +237,29 @@ wx, wy, wz = self.viewer.get_coordinate_cursor(mouse_x, mouse_y, self.picker) px, py = self.viewer.get_slice_pixel_coord_by_world_pos(wx, wy, wz) coord = self.viewer.calcultate_scroll_position(px, py) - Publisher.sendMessage('Update cross position', (wx, wy, wz)) + Publisher.sendMessage('Update cross position', position=(wx, wy, wz)) self.ScrollSlice(coord) - Publisher.sendMessage('Set ball reference position', (wx, wy, wz)) - Publisher.sendMessage('Set camera in volume', (wx, wy, wz)) + Publisher.sendMessage('Set ball reference position', position=(wx, wy, wz)) + Publisher.sendMessage('Co-registered points', arg=None, position=(wx, wy, wz, 0., 0., 0.)) iren.Render() def ScrollSlice(self, coord): if self.orientation == "AXIAL": Publisher.sendMessage(('Set scroll position', 'SAGITAL'), - coord[0]) + index=coord[0]) Publisher.sendMessage(('Set scroll position', 'CORONAL'), - coord[1]) + index=coord[1]) elif self.orientation == "SAGITAL": Publisher.sendMessage(('Set scroll position', 'AXIAL'), - coord[2]) + index=coord[2]) Publisher.sendMessage(('Set scroll position', 'CORONAL'), - coord[1]) + index=coord[1]) elif self.orientation == "CORONAL": Publisher.sendMessage(('Set scroll position', 'AXIAL'), - coord[2]) + index=coord[2]) Publisher.sendMessage(('Set scroll position', 'SAGITAL'), - coord[0]) + index=coord[0]) class WWWLInteractorStyle(DefaultInteractorStyle): @@ -283,14 +285,14 @@ def SetUp(self): self.viewer.on_wl = True Publisher.sendMessage('Toggle toolbar item', - (self.state_code, True)) + _id=self.state_code, value=True) self.viewer.canvas.draw_list.append(self.viewer.wl_text) self.viewer.UpdateCanvas() def CleanUp(self): self.viewer.on_wl = False Publisher.sendMessage('Toggle toolbar item', - (self.state_code, False)) + _id=self.state_code, value=False) if self.viewer.wl_text is not None: self.viewer.canvas.draw_list.remove(self.viewer.wl_text) self.viewer.UpdateCanvas() @@ -304,7 +306,8 @@ self.last_x, self.last_y = mouse_x, mouse_y Publisher.sendMessage('Bright and contrast adjustment image', - (self.acum_achange_window, self.acum_achange_level)) + window=self.acum_achange_window, + level=self.acum_achange_level) #self.SetWLText(self.acum_achange_level, # self.acum_achange_window) @@ -312,8 +315,9 @@ const.WINDOW_LEVEL['Manual'] = (self.acum_achange_window,\ self.acum_achange_level) Publisher.sendMessage('Check window and level other') - Publisher.sendMessage('Update window level value',(self.acum_achange_window, - self.acum_achange_level)) + Publisher.sendMessage('Update window level value', + window=self.acum_achange_window, + level= self.acum_achange_level) #Necessary update the slice plane in the volume case exists Publisher.sendMessage('Update slice viewer') Publisher.sendMessage('Render volume viewer') @@ -372,11 +376,11 @@ def SetUp(self): Publisher.sendMessage('Toggle toolbar item', - (self.state_code, True)) + _id=self.state_code, value=True) def CleanUp(self): Publisher.sendMessage('Toggle toolbar item', - (self.state_code, False)) + _id=self.state_code, value=False) self.picker.PickFromListOff() Publisher.sendMessage("Remove incomplete measurements") @@ -397,9 +401,9 @@ self.viewer.scroll_enabled = True else: Publisher.sendMessage("Add measurement point", - ((x, y, z), self._type, - ORIENTATIONS[self.orientation], - slice_number, self.radius)) + position=(x, y, z), type=self._type, + location=ORIENTATIONS[self.orientation], + slice_number=slice_number, radius=self.radius) n = len(m.points)-1 self.creating = n, m, mr self.viewer.UpdateCanvas() @@ -414,13 +418,15 @@ if self.picker.GetViewProp(): renderer = self.viewer.slice_data.renderer Publisher.sendMessage("Add measurement point", - ((x, y, z), self._type, - ORIENTATIONS[self.orientation], - slice_number, self.radius)) + position=(x, y, z), type=self._type, + location=ORIENTATIONS[self.orientation], + slice_number=slice_number, + radius=self.radius) Publisher.sendMessage("Add measurement point", - ((x, y, z), self._type, - ORIENTATIONS[self.orientation], - slice_number, self.radius)) + position=(x, y, z), type=self._type, + location=ORIENTATIONS[self.orientation], + slice_number=slice_number, + radius=self.radius) n, (m, mr) = 1, self.measures.measures[self._ori][slice_number][-1] self.creating = n, m, mr @@ -432,7 +438,7 @@ n, m, mr = self.selected x, y, z = self._get_pos_clicked() idx = self.measures._list_measures.index((m, mr)) - Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z))) + Publisher.sendMessage('Change measurement point position', index=idx, npoint=n, pos=(x, y, z)) self.viewer.UpdateCanvas() self.selected = None self.viewer.scroll_enabled = True @@ -442,13 +448,13 @@ if self.selected: n, m, mr = self.selected idx = self.measures._list_measures.index((m, mr)) - Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z))) + Publisher.sendMessage('Change measurement point position', index=idx, npoint=n, pos=(x, y, z)) self.viewer.UpdateCanvas() elif self.creating: n, m, mr = self.creating idx = self.measures._list_measures.index((m, mr)) - Publisher.sendMessage('Change measurement point position', (idx, n, (x, y, z))) + Publisher.sendMessage('Change measurement point position', index=idx, npoint=n, pos=(x, y, z)) self.viewer.UpdateCanvas() else: @@ -537,11 +543,12 @@ def SetUp(self): Publisher.sendMessage('Toggle toolbar item', - (self.state_code, True)) + _id=self.state_code, value=True) def CleanUp(self): + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK) Publisher.sendMessage('Toggle toolbar item', - (self.state_code, False)) + _id=self.state_code, value=False) def OnPanMove(self, obj, evt): if self.left_pressed: @@ -572,11 +579,12 @@ def SetUp(self): Publisher.sendMessage('Toggle toolbar item', - (self.state_code, True)) + _id=self.state_code, value=True) def CleanUp(self): + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK) Publisher.sendMessage('Toggle toolbar item', - (self.state_code, False)) + _id=self.state_code, value=False) def OnSpinMove(self, obj, evt): iren = obj.GetInteractor() @@ -616,11 +624,12 @@ def SetUp(self): Publisher.sendMessage('Toggle toolbar item', - (self.state_code, True)) + _id=self.state_code, value=True) def CleanUp(self): + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK) Publisher.sendMessage('Toggle toolbar item', - (self.state_code, False)) + _id=self.state_code, value=False) def OnZoomMoveLeft(self, obj, evt): if self.left_pressed: @@ -649,11 +658,12 @@ def SetUp(self): Publisher.sendMessage('Toggle toolbar item', - (self.state_code, True)) + _id=self.state_code, value=True) def CleanUp(self): + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK) Publisher.sendMessage('Toggle toolbar item', - (self.state_code, False)) + _id=self.state_code, value=False) def OnUnZoom(self, evt): mouse_x, mouse_y = self.viewer.interactor.GetLastEventPosition() @@ -681,11 +691,11 @@ def SetUp(self): Publisher.sendMessage('Toggle toolbar item', - (self.state_code, True)) + _id=self.state_code, value=True) def CleanUp(self): Publisher.sendMessage('Toggle toolbar item', - (self.state_code, False)) + _id=self.state_code, value=False) def OnChangeSliceMove(self, evt, obj): if self.left_pressed: @@ -712,8 +722,7 @@ self.last_position = position[1] -class EditorConfig(object): - __metaclass__= utils.Singleton +class EditorConfig(with_metaclass(utils.Singleton, object)): def __init__(self): self.operation = const.BRUSH_THRESH self.cursor_type = const.BRUSH_CIRCLE @@ -752,22 +761,38 @@ self._set_cursor() self.viewer.slice_data.cursor.Show(0) + def SetUp(self): + + x, y = self.viewer.interactor.ScreenToClient(wx.GetMousePosition()) + if self.viewer.interactor.HitTest((x, y)) == wx.HT_WINDOW_INSIDE: + self.viewer.slice_data.cursor.Show() + + y = self.viewer.interactor.GetSize()[1] - y + w_x, w_y, w_z = self.viewer.get_coordinate_cursor(x, y, self.picker) + self.viewer.slice_data.cursor.SetPosition((w_x, w_y, w_z)) + + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK)) + self.viewer.interactor.Render() + def CleanUp(self): Publisher.unsubscribe(self.set_bsize, 'Set edition brush size') Publisher.unsubscribe(self.set_bformat, 'Set brush format') Publisher.unsubscribe(self.set_boperation, 'Set edition operation') - def set_bsize(self, pubsub_evt): - size = pubsub_evt.data + self.viewer.slice_data.cursor.Show(0) + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) + self.viewer.interactor.Render() + + def set_bsize(self, size): self.config.cursor_size = size self.viewer.slice_data.cursor.SetSize(size) - def set_bformat(self, pubsub_evt): - self.config.cursor_type = pubsub_evt.data + def set_bformat(self, cursor_format): + self.config.cursor_type = cursor_format self._set_cursor() - def set_boperation(self, pubsub_evt): - self.config.operation = pubsub_evt.data + def set_boperation(self, operation): + self.config.operation = operation def _set_cursor(self): if self.config.cursor_type == const.BRUSH_SQUARE: @@ -914,7 +939,7 @@ size += 1 if size <= 100: - Publisher.sendMessage('Set edition brush size', size) + Publisher.sendMessage('Set edition brush size', size=size) cursor.SetPosition(cursor.position) self.viewer.interactor.Render() else: @@ -932,7 +957,7 @@ size -= 1 if size > 0: - Publisher.sendMessage('Set edition brush size', size) + Publisher.sendMessage('Set edition brush size', size=size) cursor.SetPosition(cursor.position) self.viewer.interactor.Render() else: @@ -964,8 +989,7 @@ self.dlg.Destroy() -class WatershedConfig(object): - __metaclass__= utils.Singleton +class WatershedConfig(with_metaclass(utils.Singleton, object)): def __init__(self): self.algorithm = "Watershed" self.con_2d = 4 @@ -984,23 +1008,23 @@ Publisher.subscribe(self.set_3dcon, "Set watershed 3d con") Publisher.subscribe(self.set_gaussian_size, "Set watershed gaussian size") - def set_operation(self, pubsub_evt): - self.operation = WATERSHED_OPERATIONS[pubsub_evt.data] + def set_operation(self, operation): + self.operation = WATERSHED_OPERATIONS[operation] - def set_use_ww_wl(self, pubsub_evt): - self.use_ww_wl = pubsub_evt.data + def set_use_ww_wl(self, use_ww_wl): + self.use_ww_wl = use_ww_wl - def set_algorithm(self, pubsub_evt): - self.algorithm = pubsub_evt.data + def set_algorithm(self, algorithm): + self.algorithm = algorithm - def set_2dcon(self, pubsub_evt): - self.con_2d = pubsub_evt.data + def set_2dcon(self, con_2d): + self.con_2d = con_2d - def set_3dcon(self, pubsub_evt): - self.con_3d = pubsub_evt.data + def set_3dcon(self, con_3d): + self.con_3d = con_3d - def set_gaussian_size(self, pubsub_evt): - self.mg_size = pubsub_evt.data + def set_gaussian_size(self, size): + self.mg_size = size WALGORITHM = {"Watershed": watershed, "Watershed IFT": watershed_ift} @@ -1041,11 +1065,23 @@ self.viewer.slice_data.cursor.Show(0) def SetUp(self): + mask = self.viewer.slice_.current_mask.matrix self._create_mask() self.viewer.slice_.to_show_aux = 'watershed' self.viewer.OnScrollBar() + x, y = self.viewer.interactor.ScreenToClient(wx.GetMousePosition()) + if self.viewer.interactor.HitTest((x, y)) == wx.HT_WINDOW_INSIDE: + self.viewer.slice_data.cursor.Show() + + y = self.viewer.interactor.GetSize()[1] - y + w_x, w_y, w_z = self.viewer.get_coordinate_cursor(x, y, self.picker) + self.viewer.slice_data.cursor.SetPosition((w_x, w_y, w_z)) + + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK)) + self.viewer.interactor.Render() + def CleanUp(self): #self._remove_mask() Publisher.unsubscribe(self.expand_watershed, 'Expand watershed to 3D ' + self.orientation) @@ -1055,6 +1091,10 @@ self.viewer.slice_.to_show_aux = '' self.viewer.OnScrollBar() + self.viewer.slice_data.cursor.Show(0) + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) + self.viewer.interactor.Render() + def _create_mask(self): if self.matrix is None: try: @@ -1067,7 +1107,7 @@ if self.matrix is not None: self.matrix = None os.remove(self.temp_file) - print "deleting", self.temp_file + print("deleting", self.temp_file) def _set_cursor(self): if self.config.cursor_type == const.BRUSH_SQUARE: @@ -1087,13 +1127,12 @@ cursor.SetSize(self.config.cursor_size) self.viewer.slice_data.SetCursor(cursor) - def set_bsize(self, pubsub_evt): - size = pubsub_evt.data + def set_bsize(self, size): self.config.cursor_size = size self.viewer.slice_data.cursor.SetSize(size) - def set_bformat(self, pubsub_evt): - self.config.cursor_type = pubsub_evt.data + def set_bformat(self, brush_format): + self.config.cursor_type = brush_format self._set_cursor() def OnEnterInteractor(self, obj, evt): @@ -1120,7 +1159,7 @@ size -= 1 if size > 0: - Publisher.sendMessage('Set watershed brush size', size) + Publisher.sendMessage('Set watershed brush size', size=size) cursor.SetPosition(cursor.position) self.viewer.interactor.Render() else: @@ -1138,7 +1177,7 @@ size += 1 if size <= 100: - Publisher.sendMessage('Set watershed brush size', size) + Publisher.sendMessage('Set watershed brush size', size=size) cursor.SetPosition(cursor.position) self.viewer.interactor.Render() else: @@ -1212,7 +1251,7 @@ position = self.viewer.get_slice_pixel_coord_by_world_pos(*coord) radius = cursor.radius - if position < 0: + if isinstance(position, int) and position < 0: position = viewer.calculate_matrix_position(coord) operation = self.config.operation @@ -1386,7 +1425,7 @@ if roi_m.size: roi_m[index] = operation - def expand_watershed(self, pubsub_evt): + def expand_watershed(self): markers = self.matrix image = self.viewer.slice_.matrix self.viewer.slice_.do_threshold_to_all_slices() @@ -1418,7 +1457,7 @@ del wp w_x, w_y = wx.GetMousePosition() - x, y = self.viewer.ScreenToClientXY(w_x, w_y) + x, y = self.viewer.ScreenToClient((w_x, w_y)) flag = self.viewer.interactor.HitTest((x, y)) if flag == wx.HT_WINDOW_INSIDE: @@ -1511,6 +1550,8 @@ Publisher.sendMessage('Reload actual slice') def CleanUp(self): + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK) + for actor in self.actors: self.viewer.slice_data.renderer.RemoveActor(actor) @@ -1592,7 +1633,7 @@ self.viewer.slice_.rotations = [0, 0, 0] self.viewer.slice_.q_orientation = np.array((1, 0, 0, 0)) - Publisher.sendMessage('Update reorient angles', (0, 0, 0)) + Publisher.sendMessage('Update reorient angles', angles=(0, 0, 0)) self._discard_buffers() self.viewer.slice_.current_mask.clear_history() @@ -1648,10 +1689,11 @@ self.viewer.slice_.q_orientation = transformations.quaternion_multiply(self.viewer.slice_.q_orientation, transformations.quaternion_about_axis(angle, axis)) az, ay, ax = transformations.euler_from_quaternion(self.viewer.slice_.q_orientation) - Publisher.sendMessage('Update reorient angles', (ax, ay, az)) + Publisher.sendMessage('Update reorient angles', angles=(ax, ay, az)) self._discard_buffers() - self.viewer.slice_.current_mask.clear_history() + if self.viewer.slice_.current_mask: + self.viewer.slice_.current_mask.clear_history() Publisher.sendMessage('Reload actual slice %s' % self.viewer.orientation) self.p0 = self.get_image_point_coord(x, y, z) @@ -1673,8 +1715,8 @@ # print (z, y, x), tcoord return tcoord - def _set_reorientation_angles(self, pubsub_evt): - ax, ay, az = pubsub_evt.data + def _set_reorientation_angles(self, angles): + ax, ay, az = angles q = transformations.quaternion_from_euler(az, ay, ax) self.viewer.slice_.q_orientation = q @@ -1727,8 +1769,7 @@ buffer_.discard_image() -class FFillConfig(object): - __metaclass__= utils.Singleton +class FFillConfig(with_metaclass(utils.Singleton, object)): def __init__(self): self.dlg_visible = False self.target = "2D" @@ -1864,8 +1905,7 @@ self._progr_title = _(u"Remove part") self._progr_msg = _(u"Removing part ...") -class CropMaskConfig(object): - __metaclass__= utils.Singleton +class CropMaskConfig(with_metaclass(utils.Singleton, object)): def __init__(self): self.dlg_visible = False @@ -1930,9 +1970,8 @@ self.viewer.canvas.draw_list.remove(self.draw_retangle) Publisher.sendMessage('Redraw canvas') - def CropMask(self, pubsub_evt): + def CropMask(self): if self.viewer.orientation == "AXIAL": - xi, xf, yi, yf, zi, zf = self.draw_retangle.box.GetLimits() xi += 1 @@ -1940,7 +1979,7 @@ yi += 1 yf += 1 - + zi += 1 zf += 1 @@ -1948,13 +1987,13 @@ cp_mask = self.viewer.slice_.current_mask.matrix.copy() tmp_mask = self.viewer.slice_.current_mask.matrix[zi-1:zf+1, yi-1:yf+1, xi-1:xf+1].copy() - + self.viewer.slice_.current_mask.matrix[:] = 1 self.viewer.slice_.current_mask.matrix[zi-1:zf+1, yi-1:yf+1, xi-1:xf+1] = tmp_mask self.viewer.slice_.current_mask.save_history(0, 'VOLUME', self.viewer.slice_.current_mask.matrix.copy(), cp_mask) - + self.viewer.slice_.buffer_slices['AXIAL'].discard_mask() self.viewer.slice_.buffer_slices['CORONAL'].discard_mask() self.viewer.slice_.buffer_slices['SAGITAL'].discard_mask() @@ -1964,11 +2003,10 @@ self.viewer.slice_.buffer_slices['SAGITAL'].discard_vtk_mask() self.viewer.slice_.current_mask.was_edited = True - Publisher.sendMessage('Reload actual slice') + Publisher.sendMessage('Reload actual slice') + - -class SelectPartConfig(object): - __metaclass__= utils.Singleton +class SelectPartConfig(with_metaclass(utils.Singleton, object)): def __init__(self): self.mask = None self.con_3d = 6 @@ -2029,7 +2067,7 @@ self.config.mask.name = self.config.mask_name self.viewer.slice_._add_mask_into_proj(self.config.mask) self.viewer.slice_.SelectCurrentMask(self.config.mask.index) - Publisher.sendMessage('Change mask selected', self.config.mask.index) + Publisher.sendMessage('Change mask selected', index=self.config.mask.index) del self.viewer.slice_.aux_matrices['SELECT'] self.viewer.slice_.to_show_aux = '' @@ -2074,8 +2112,7 @@ self.config.mask = mask -class FFillSegmentationConfig(object): - __metaclass__= utils.Singleton +class FFillSegmentationConfig(with_metaclass(utils.Singleton, object)): def __init__(self): self.dlg_visible = False self.target = "2D" @@ -2185,7 +2222,7 @@ elif self.config.method == 'dynamic': if self.config.use_ww_wl: - print "Using WW&WL" + print("Using WW&WL") ww = self.viewer.slice_.window_width wl = self.viewer.slice_.window_level image = get_LUT_value_255(image, ww, wl) @@ -2242,7 +2279,7 @@ elif self.config.method == 'dynamic': if self.config.use_ww_wl: - print "Using WW&WL" + print("Using WW&WL") ww = self.viewer.slice_.window_width wl = self.viewer.slice_.window_level image = get_LUT_value_255(image, ww, wl) @@ -2295,18 +2332,18 @@ bool_mask = np.zeros_like(mask, dtype='bool') out_mask = np.zeros_like(mask) - for k in xrange(int(z-1), int(z+2)): + for k in range(int(z-1), int(z+2)): if k < 0 or k >= bool_mask.shape[0]: continue - for j in xrange(int(y-1), int(y+2)): + for j in range(int(y-1), int(y+2)): if j < 0 or j >= bool_mask.shape[1]: continue - for i in xrange(int(x-1), int(x+2)): + for i in range(int(x-1), int(x+2)): if i < 0 or i >= bool_mask.shape[2]: continue bool_mask[k, j, i] = True - for i in xrange(self.config.confid_iters): + for i in range(self.config.confid_iters): var = np.std(image[bool_mask]) mean = np.mean(image[bool_mask]) diff -Nru invesalius-3.1.1/invesalius/data/surface_process.py invesalius-3.1.99991/invesalius/data/surface_process.py --- invesalius-3.1.1/invesalius/data/surface_process.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/surface_process.py 2018-08-17 16:41:39.000000000 +0000 @@ -206,7 +206,7 @@ writer.SetFileName(filename) writer.Write() - print "Writing piece", roi, "to", filename + print("Writing piece", roi, "to", filename) del polydata del writer diff -Nru invesalius-3.1.1/invesalius/data/surface.py invesalius-3.1.99991/invesalius/data/surface.py --- invesalius-3.1.1/invesalius/data/surface.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/surface.py 2018-08-17 16:41:39.000000000 +0000 @@ -74,15 +74,23 @@ else: self.name = name + self.filename = None + def SavePlist(self, dir_temp, filelist): - filename = u'surface_%d' % self.index - vtp_filename = filename + u'.vtp' - vtp_filepath = os.path.join(dir_temp, vtp_filename) - pu.Export(self.polydata, vtp_filepath, bin=True) + if self.filename and os.path.exists(self.filename): + filename = u'surface_%d' % self.index + vtp_filename = filename + u'.vtp' + vtp_filepath = self.filename + else: + filename = u'surface_%d' % self.index + vtp_filename = filename + u'.vtp' + vtp_filepath = tempfile.mktemp() + pu.Export(self.polydata, vtp_filepath, bin=True) + self.filename = vtp_filepath filelist[vtp_filepath] = vtp_filename - surface = {'colour': self.colour, + surface = {'colour': self.colour[:3], 'index': self.index, 'name': self.name, 'polydata': vtp_filename, @@ -163,11 +171,10 @@ Publisher.subscribe(self.OnImportSurfaceFile, 'Import surface file') - def OnDuplicate(self, pubsub_evt): - selected_items = pubsub_evt.data + def OnDuplicate(self, surface_indexes): proj = prj.Project() surface_dict = proj.surface_dict - for index in selected_items: + for index in surface_indexes: original_surface = surface_dict[index] # compute copy name name = original_surface.name @@ -182,37 +189,37 @@ volume = original_surface.volume, area = original_surface.area) - def OnRemove(self, pubsub_evt): - selected_items = pubsub_evt.data + def OnRemove(self, surface_indexes): proj = prj.Project() old_dict = self.actors_dict new_dict = {} - if selected_items: - for index in selected_items: + if surface_indexes: + for index in surface_indexes: proj.RemoveSurface(index) - actor = old_dict[index] - for i in old_dict: - if i < index: - new_dict[i] = old_dict[i] - if i > index: - new_dict[i-1] = old_dict[i] - old_dict = new_dict - Publisher.sendMessage('Remove surface actor from viewer', actor) + if index in old_dict: + actor = old_dict[index] + for i in old_dict: + if i < index: + new_dict[i] = old_dict[i] + if i > index: + new_dict[i-1] = old_dict[i] + old_dict = new_dict + Publisher.sendMessage('Remove surface actor from viewer', actor=actor) self.actors_dict = new_dict - if self.last_surface_index in selected_items: + if self.last_surface_index in surface_indexes: if self.actors_dict: self.last_surface_index = 0 else: self.last_surface_index = None - def OnSeedSurface(self, pubsub_evt): + def OnSeedSurface(self, seeds): """ Create a new surface, based on the last selected surface, using as reference seeds user add to surface of reference. """ - points_id_list = pubsub_evt.data + points_id_list = seeds index = self.last_surface_index proj = prj.Project() surface = proj.surface_dict[index] @@ -220,10 +227,10 @@ new_polydata = pu.JoinSeedsParts(surface.polydata, points_id_list) index = self.CreateSurfaceFromPolydata(new_polydata) - Publisher.sendMessage('Show single surface', (index, True)) + Publisher.sendMessage('Show single surface', index=index, visibility=True) #self.ShowActor(index, True) - def OnSplitSurface(self, pubsub_evt): + def OnSplitSurface(self): """ Create n new surfaces, based on the last selected surface, according to their connectivity. @@ -239,9 +246,9 @@ index_list.append(index) #self.ShowActor(index, True) - Publisher.sendMessage('Show multiple surfaces', (index_list, True)) + Publisher.sendMessage('Show multiple surfaces', index_list=index_list, visibility=True) - def OnLargestSurface(self, pubsub_evt): + def OnLargestSurface(self): """ Create a new surface, based on largest part of the last selected surface. @@ -252,13 +259,12 @@ new_polydata = pu.SelectLargestPart(surface.polydata) new_index = self.CreateSurfaceFromPolydata(new_polydata) - Publisher.sendMessage('Show single surface', (new_index, True)) + Publisher.sendMessage('Show single surface', index=new_index, visibility=True) - def OnImportSurfaceFile(self, pubsub_evt): + def OnImportSurfaceFile(self, filename): """ Creates a new surface from a surface file (STL, PLY, OBJ or VTP) """ - filename = pubsub_evt.data self.CreateSurfaceFromFile(filename) def CreateSurfaceFromFile(self, filename): @@ -352,49 +358,41 @@ area = measured_polydata.GetSurfaceArea() surface.volume = volume surface.area = area - print ">>>>", surface.volume + print(">>>>", surface.volume) else: surface.volume = volume surface.area = area self.last_surface_index = surface.index - Publisher.sendMessage('Load surface actor into viewer', actor) + Publisher.sendMessage('Load surface actor into viewer', actor=actor) - Publisher.sendMessage('Update surface info in GUI', - (surface.index, surface.name, - surface.colour, surface.volume, - surface.area, surface.transparency)) + Publisher.sendMessage('Update surface info in GUI', surface=surface) return surface.index - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.CloseProject() def CloseProject(self): for index in self.actors_dict: - Publisher.sendMessage('Remove surface actor from viewer', self.actors_dict[index]) + Publisher.sendMessage('Remove surface actor from viewer', actor=self.actors_dict[index]) del self.actors_dict self.actors_dict = {} # restarting the surface index Surface.general_index = -1 - def OnSelectSurface(self, pubsub_evt): - index = pubsub_evt.data - #self.last_surface_index = index + def OnSelectSurface(self, surface_index): + #self.last_surface_index = surface_index # self.actors_dict. proj = prj.Project() - surface = proj.surface_dict[index] - Publisher.sendMessage('Update surface info in GUI', - (index, surface.name, - surface.colour, surface.volume, - surface.area, surface.transparency)) - self.last_surface_index = index + surface = proj.surface_dict[surface_index] + Publisher.sendMessage('Update surface info in GUI', surface=surface) + self.last_surface_index = surface_index # if surface.is_shown: - self.ShowActor(index, True) + self.ShowActor(surface_index, True) - def OnLoadSurfaceDict(self, pubsub_evt): - surface_dict = pubsub_evt.data + def OnLoadSurfaceDict(self, surface_dict): for key in surface_dict: surface = surface_dict[key] @@ -421,33 +419,29 @@ actor.SetMapper(mapper) # Set actor colour and transparency - actor.GetProperty().SetColor(surface.colour) + actor.GetProperty().SetColor(surface.colour[:3]) actor.GetProperty().SetOpacity(1-surface.transparency) self.actors_dict[surface.index] = actor # Send actor by pubsub to viewer's render - Publisher.sendMessage('Load surface actor into viewer', (actor)) + Publisher.sendMessage('Load surface actor into viewer', actor=actor) Publisher.sendMessage('Update status text in GUI', - _("Ready")) + label=_("Ready")) # The following lines have to be here, otherwise all volumes disappear - Publisher.sendMessage('Update surface info in GUI', - (surface.index, surface.name, - surface.colour, surface.volume, - surface.area, surface.transparency)) + Publisher.sendMessage('Update surface info in GUI', surface=surface) if not surface.is_shown: self.ShowActor(key, False) #### #(mask_index, surface_name, quality, fill_holes, keep_largest) - def AddNewActor(self, pubsub_evt): + def AddNewActor(self, slice_, mask, surface_parameters): """ Create surface actor, save into project and send it to viewer. """ - slice_, mask, surface_parameters = pubsub_evt.data matrix = slice_.matrix filename_img = slice_.matrix_filename spacing = slice_.spacing @@ -462,7 +456,7 @@ mode = 'CONTOUR' # 'GRAYSCALE' min_value, max_value = mask.threshold_range - colour = mask.colour + colour = mask.colour[:3] try: overwrite = surface_parameters['options']['overwrite'] @@ -512,7 +506,7 @@ q_out = multiprocessing.Queue() p = [] - for i in xrange(n_processors): + for i in range(n_processors): sp = surface_process.SurfaceProcess(pipe_in, filename_img, matrix.shape, matrix.dtype, mask.temp_file, @@ -530,12 +524,12 @@ p.append(sp) sp.start() - for i in xrange(n_pieces): + for i in range(n_pieces): init = i * piece_size end = init + piece_size + o_piece roi = slice(init, end) q_in.put(roi) - print "new_piece", roi + print("new_piece", roi) for i in p: q_in.put(None) @@ -655,7 +649,7 @@ if decimate_reduction: - print "Decimating", decimate_reduction + print("Decimating", decimate_reduction) decimation = vtk.vtkQuadricDecimation() # decimation.ReleaseDataFlagOn() decimation.SetInputData(polydata) @@ -674,7 +668,6 @@ # polydata.SetSource(None) del decimation - to_measure = polydata #to_measure.Register(None) # to_measure.SetSource(None) @@ -713,117 +706,131 @@ # polydata.DebugOn() del filled_polydata - normals = vtk.vtkPolyDataNormals() - # normals.ReleaseDataFlagOn() - normals_ref = weakref.ref(normals) - normals_ref().AddObserver("ProgressEvent", lambda obj,evt: - UpdateProgress(normals_ref(), _("Creating 3D surface..."))) - normals.SetInputData(polydata) - normals.SetFeatureAngle(80) - normals.AutoOrientNormalsOn() - # normals.GetOutput().ReleaseDataFlagOn() - normals.Update() - del polydata - polydata = normals.GetOutput() - #polydata.Register(None) - # polydata.SetSource(None) - del normals - - # Improve performance - stripper = vtk.vtkStripper() - # stripper.ReleaseDataFlagOn() - stripper_ref = weakref.ref(stripper) - stripper_ref().AddObserver("ProgressEvent", lambda obj,evt: - UpdateProgress(stripper_ref(), _("Creating 3D surface..."))) - stripper.SetInputData(polydata) - stripper.PassThroughCellIdsOn() - stripper.PassThroughPointIdsOn() - # stripper.GetOutput().ReleaseDataFlagOn() - stripper.Update() - del polydata - polydata = stripper.GetOutput() - #polydata.Register(None) - # polydata.SetSource(None) - del stripper + to_measure = polydata - # Map polygonal data (vtkPolyData) to graphics primitives. - mapper = vtk.vtkPolyDataMapper() - mapper.SetInputData(polydata) - mapper.ScalarVisibilityOff() - # mapper.ReleaseDataFlagOn() - mapper.ImmediateModeRenderingOn() # improve performance + # If InVesalius is running without GUI + if wx.GetApp() is None: + proj = prj.Project() + #Create Surface instance + if overwrite: + surface = Surface(index = self.last_surface_index) + proj.ChangeSurface(surface) + else: + surface = Surface(name=surface_name) + index = proj.AddSurface(surface) + surface.index = index + self.last_surface_index = index + surface.colour = colour + surface.polydata = polydata - # Represent an object (geometry & properties) in the rendered scene - actor = vtk.vtkActor() - actor.SetMapper(mapper) - del mapper - #Create Surface instance - if overwrite: - surface = Surface(index = self.last_surface_index) + # With GUI else: - surface = Surface(name=surface_name) - surface.colour = colour - surface.polydata = polydata - del polydata + normals = vtk.vtkPolyDataNormals() + # normals.ReleaseDataFlagOn() + normals_ref = weakref.ref(normals) + normals_ref().AddObserver("ProgressEvent", lambda obj,evt: + UpdateProgress(normals_ref(), _("Creating 3D surface..."))) + normals.SetInputData(polydata) + normals.SetFeatureAngle(80) + normals.AutoOrientNormalsOn() + # normals.GetOutput().ReleaseDataFlagOn() + normals.Update() + del polydata + polydata = normals.GetOutput() + #polydata.Register(None) + # polydata.SetSource(None) + del normals - # Set actor colour and transparency - actor.GetProperty().SetColor(colour) - actor.GetProperty().SetOpacity(1-surface.transparency) + # Improve performance + stripper = vtk.vtkStripper() + # stripper.ReleaseDataFlagOn() + stripper_ref = weakref.ref(stripper) + stripper_ref().AddObserver("ProgressEvent", lambda obj,evt: + UpdateProgress(stripper_ref(), _("Creating 3D surface..."))) + stripper.SetInputData(polydata) + stripper.PassThroughCellIdsOn() + stripper.PassThroughPointIdsOn() + # stripper.GetOutput().ReleaseDataFlagOn() + stripper.Update() + del polydata + polydata = stripper.GetOutput() + #polydata.Register(None) + # polydata.SetSource(None) + del stripper - prop = actor.GetProperty() + # Map polygonal data (vtkPolyData) to graphics primitives. + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputData(polydata) + mapper.ScalarVisibilityOff() + # mapper.ReleaseDataFlagOn() + mapper.ImmediateModeRenderingOn() # improve performance - interpolation = int(ses.Session().surface_interpolation) + # Represent an object (geometry & properties) in the rendered scene + actor = vtk.vtkActor() + actor.SetMapper(mapper) + del mapper + #Create Surface instance + if overwrite: + surface = Surface(index = self.last_surface_index) + else: + surface = Surface(name=surface_name) + surface.colour = colour + surface.polydata = polydata + del polydata - prop.SetInterpolation(interpolation) + # Set actor colour and transparency + actor.GetProperty().SetColor(colour) + actor.GetProperty().SetOpacity(1-surface.transparency) - proj = prj.Project() - if overwrite: - proj.ChangeSurface(surface) - else: - index = proj.AddSurface(surface) - surface.index = index - self.last_surface_index = index + prop = actor.GetProperty() - session = ses.Session() - session.ChangeProject() + interpolation = int(ses.Session().surface_interpolation) - # The following lines have to be here, otherwise all volumes disappear - measured_polydata = vtk.vtkMassProperties() - # measured_polydata.ReleaseDataFlagOn() - measured_polydata.SetInputData(to_measure) - volume = float(measured_polydata.GetVolume()) - area = float(measured_polydata.GetSurfaceArea()) - surface.volume = volume - surface.area = area - self.last_surface_index = surface.index - del measured_polydata - del to_measure + prop.SetInterpolation(interpolation) + + proj = prj.Project() + if overwrite: + proj.ChangeSurface(surface) + else: + index = proj.AddSurface(surface) + surface.index = index + self.last_surface_index = index - Publisher.sendMessage('Load surface actor into viewer', actor) + session = ses.Session() + session.ChangeProject() - # Send actor by pubsub to viewer's render - if overwrite and self.actors_dict.keys(): - old_actor = self.actors_dict[self.last_surface_index] - Publisher.sendMessage('Remove surface actor from viewer', old_actor) + measured_polydata = vtk.vtkMassProperties() + # measured_polydata.ReleaseDataFlagOn() + measured_polydata.SetInputData(to_measure) + volume = float(measured_polydata.GetVolume()) + area = float(measured_polydata.GetSurfaceArea()) + surface.volume = volume + surface.area = area + self.last_surface_index = surface.index + del measured_polydata + del to_measure - # Save actor for future management tasks - self.actors_dict[surface.index] = actor + Publisher.sendMessage('Load surface actor into viewer', actor=actor) + + # Send actor by pubsub to viewer's render + if overwrite and self.actors_dict.keys(): + old_actor = self.actors_dict[self.last_surface_index] + Publisher.sendMessage('Remove surface actor from viewer', actor=old_actor) + + # Save actor for future management tasks + self.actors_dict[surface.index] = actor + + Publisher.sendMessage('Update surface info in GUI', surface=surface) - Publisher.sendMessage('Update surface info in GUI', - (surface.index, surface.name, - surface.colour, surface.volume, - surface.area, - surface.transparency)) - - #When you finalize the progress. The bar is cleaned. - UpdateProgress = vu.ShowProgress(1) - UpdateProgress(0, _("Ready")) - Publisher.sendMessage('Update status text in GUI', _("Ready")) + #When you finalize the progress. The bar is cleaned. + UpdateProgress = vu.ShowProgress(1) + UpdateProgress(0, _("Ready")) + Publisher.sendMessage('Update status text in GUI', label=_("Ready")) - Publisher.sendMessage('End busy cursor') - del actor + Publisher.sendMessage('End busy cursor') + del actor - def UpdateSurfaceInterpolation(self, pub_evt): + def UpdateSurfaceInterpolation(self): interpolation = int(ses.Session().surface_interpolation) key_actors = self.actors_dict.keys() @@ -835,20 +842,18 @@ """ Remove actor, according to given actor index. """ - Publisher.sendMessage('Remove surface actor from viewer', (index)) + Publisher.sendMessage('Remove surface actor from viewer', actor=index) self.actors_dict.pop(index) # Remove surface from project's surface_dict proj = prj.Project() proj.surface_dict.pop(index) - def OnChangeSurfaceName(self, pubsub_evt): - index, name = pubsub_evt.data + def OnChangeSurfaceName(self, index, name): proj = prj.Project() proj.surface_dict[index].name = name - def OnShowSurface(self, pubsub_evt): - index, value = pubsub_evt.data - self.ShowActor(index, value) + def OnShowSurface(self, index, visibility): + self.ShowActor(index, visibility) def ShowActor(self, index, value): """ @@ -860,30 +865,27 @@ proj.surface_dict[index].is_shown = value Publisher.sendMessage('Render volume viewer') - def SetActorTransparency(self, pubsub_evt): + def SetActorTransparency(self, surface_index, transparency): """ Set actor transparency (oposite to opacity) according to given actor index and value. """ - index, value = pubsub_evt.data - self.actors_dict[index].GetProperty().SetOpacity(1-value) + self.actors_dict[surface_index].GetProperty().SetOpacity(1-transparency) # Update value in project's surface_dict proj = prj.Project() - proj.surface_dict[index].transparency = value + proj.surface_dict[surface_index].transparency = transparency Publisher.sendMessage('Render volume viewer') - def SetActorColour(self, pubsub_evt): + def SetActorColour(self, surface_index, colour): """ """ - index, colour = pubsub_evt.data - self.actors_dict[index].GetProperty().SetColor(colour) + self.actors_dict[surface_index].GetProperty().SetColor(colour[:3]) # Update value in project's surface_dict proj = prj.Project() - proj.surface_dict[index].colour = colour + proj.surface_dict[surface_index].colour = colour Publisher.sendMessage('Render volume viewer') - def OnExportSurface(self, pubsub_evt): - filename, filetype = pubsub_evt.data + def OnExportSurface(self, filename, filetype): ftype_prefix = { const.FILETYPE_STL: '.stl', const.FILETYPE_VTP: '.vtp', @@ -899,7 +901,7 @@ temp_file = win32api.GetShortPathName(temp_file) os.remove(_temp_file) - temp_file = temp_file.decode(const.FS_ENCODE) + temp_file = utl.decode(temp_file, const.FS_ENCODE) self._export_surface(temp_file, filetype) shutil.move(temp_file, filename) diff -Nru invesalius-3.1.1/invesalius/data/trackers.py invesalius-3.1.99991/invesalius/data/trackers.py --- invesalius-3.1.1/invesalius/data/trackers.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/trackers.py 2018-08-17 16:41:39.000000000 +0000 @@ -21,18 +21,19 @@ # TODO: Test if there are too many prints when connection fails -def TrackerConnection(tracker_id, action): +def TrackerConnection(tracker_id, trck_init, action): """ - Initialize spatial trackers for coordinate detection during navigation. + Initialize or disconnect spatial trackers for coordinate detection during navigation. :param tracker_id: ID of tracking device. + :param trck_init: tracker initialization instance. :param action: string with to decide whether connect or disconnect the selected device. :return spatial tracker initialization instance or None if could not open device. """ if action == 'connect': trck_fcn = {1: ClaronTracker, - 2: PolhemusTrackerFT, # FASTRAK + 2: PolhemusTracker, # FASTRAK 3: PolhemusTracker, # ISOTRAK 4: PolhemusTracker, # PATRIOT 5: DebugTracker} @@ -40,7 +41,7 @@ trck_init = trck_fcn[tracker_id](tracker_id) elif action == 'disconnect': - trck_init = DisconnectTracker(tracker_id) + trck_init = DisconnectTracker(tracker_id, trck_init) return trck_init @@ -49,10 +50,10 @@ trck_init = None try: # import spatial tracker library - print 'Connect to default tracking device.' + print('Connect to default tracking device.') except: - print 'Could not connect to default tracker.' + print('Could not connect to default tracker.') # return tracker initialization variable and type of connection return trck_init, 'wrapper' @@ -67,8 +68,8 @@ lib_mode = 'wrapper' trck_init = pyclaron.pyclaron() - trck_init.CalibrationDir = const.CAL_DIR - trck_init.MarkerDir = const.MAR_DIR + trck_init.CalibrationDir = const.CAL_DIR.encode(const.FS_ENCODE) + trck_init.MarkerDir = const.MAR_DIR.encode(const.FS_ENCODE) trck_init.NumberFramesProcessed = 10 trck_init.FramesExtrapolated = 0 trck_init.PROBE_NAME = "1Probe" @@ -77,76 +78,66 @@ if trck_init.GetIdentifyingCamera(): trck_init.Run() - print "MicronTracker camera identified." + print("MicronTracker camera identified.") else: trck_init = None except ImportError: lib_mode = 'error' - print 'The ClaronTracker library is not installed.' + print('The ClaronTracker library is not installed.') return trck_init, lib_mode -def PolhemusTrackerFT(tracker_id): - trck_init = None - lib_mode = 'wrapper' - try: - import polhemusFT - - trck_init = polhemusFT.polhemusFT() - trck_check = trck_init.Initialize() - - if trck_check: - # First run is necessary to discard the first coord collection - trck_init.Run() - else: - trck_init = trck_check - except: - print 'Could not connect to Polhemus via wrapper.' - - return trck_init, lib_mode def PolhemusTracker(tracker_id): - trck_init = None try: - trck_init = PlhWrapperConnection() + trck_init = PlhWrapperConnection(tracker_id) lib_mode = 'wrapper' if not trck_init: - print 'Could not connect with Polhemus wrapper, trying USB connection...' + print('Could not connect with Polhemus wrapper, trying USB connection...') trck_init = PlhUSBConnection(tracker_id) lib_mode = 'usb' if not trck_init: - print 'Could not connect with Polhemus USB, trying serial connection...' + print('Could not connect with Polhemus USB, trying serial connection...') trck_init = PlhSerialConnection(tracker_id) lib_mode = 'serial' except: + trck_init = None lib_mode = 'error' - print 'Could not connect to Polhemus.' + print('Could not connect to Polhemus.') return trck_init, lib_mode def DebugTracker(tracker_id): trck_init = True - print 'Debug device started.' + print('Debug device started.') return trck_init, 'debug' -def PlhWrapperConnection(): - trck_init = None +def PlhWrapperConnection(tracker_id): try: - import polhemus + from time import sleep + if tracker_id == 2: + import polhemusFT + trck_init = polhemusFT.polhemusFT() + else: + import polhemus + trck_init = polhemus.polhemus() - trck_init = polhemus.polhemus() trck_check = trck_init.Initialize() if trck_check: - # First run is necessary to discard the first coord collection - trck_init.Run() + # Sequence of runs necessary to throw away unnecessary data + for n in range(0, 5): + trck_init.Run() + sleep(0.175) else: - trck_init = trck_check + trck_init = None + print('Could not connect to Polhemus via wrapper without error.') except: - print 'Could not connect to Polhemus via wrapper.' + trck_init = None + print('Could not connect to Polhemus via wrapper with error.') return trck_init @@ -172,10 +163,11 @@ if not data: trck_init = None + print('Could not connect to Polhemus serial without error.') except: trck_init = None - print 'Could not connect to Polhemus serial.' + print('Could not connect to Polhemus serial with error.') return trck_init @@ -184,7 +176,10 @@ trck_init = None try: import usb.core as uc - trck_init = uc.find(idVendor=0x0F44, idProduct=0x0003) + # Check the idProduct using the usbdeview software, the idProduct is unique for each + # device and connection fails when is incorrect + # trck_init = uc.find(idVendor=0x0F44, idProduct=0x0003) [used in a different device] + trck_init = uc.find(idVendor=0x0F44, idProduct=0xEF12) cfg = trck_init.get_active_configuration() for i in cfg: for x in i: @@ -203,58 +198,35 @@ endpoint.wMaxPacketSize) if not data: trck_init = None + print('Could not connect to Polhemus USB without error.') except: - print 'Could not connect to Polhemus USB.' + print('Could not connect to Polhemus USB with error.') return trck_init -def DisconnectTracker(tracker_id): +def DisconnectTracker(tracker_id, trck_init): """ Disconnect current spatial tracker :param tracker_id: ID of tracking device. + :param trck_init: Initialization variable of tracking device. """ - from wx.lib.pubsub import pub as Publisher - Publisher.sendMessage('Update status text in GUI', _("Disconnecting tracker ...")) - Publisher.sendMessage('Remove sensors ID') - trck_init = None - # TODO: create individual functions to disconnect each other device, e.g. Polhemus. - if tracker_id == 1: - try: - import pyclaron - pyclaron.pyclaron().Close() - lib_mode = 'wrapper' - print 'Claron tracker disconnected.' - except ImportError: - lib_mode = 'error' - print 'The ClaronTracker library is not installed.' - - elif tracker_id == 2: - try: - import polhemusFT - polhemusFT.polhemusFT().Close() - lib_mode = 'wrapper' - print 'Polhemus tracker disconnected.' - except ImportError: - lib_mode = 'error' - print 'The polhemus library is not installed.' - elif tracker_id == 4: + if tracker_id == 5: + trck_init = False + lib_mode = 'debug' + print('Debug tracker disconnected.') + else: try: - import polhemus - polhemus.polhemus().Close() + trck_init.Close() + trck_init = False lib_mode = 'wrapper' - print 'Polhemus tracker disconnected.' - except ImportError: + print('Tracker disconnected.') + except: + trck_init = True lib_mode = 'error' - print 'The polhemus library is not installed.' + print('The tracker could not be disconnected.') - elif tracker_id == 5: - print 'Debug tracker disconnected.' - lib_mode = 'debug' - - Publisher.sendMessage('Update status text in GUI', _("Ready")) - - return trck_init, lib_mode \ No newline at end of file + return trck_init, lib_mode diff -Nru invesalius-3.1.1/invesalius/data/trigger.py invesalius-3.1.99991/invesalius/data/trigger.py --- invesalius-3.1.1/invesalius/data/trigger.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/trigger.py 2018-08-17 16:41:39.000000000 +0000 @@ -33,20 +33,21 @@ threading.Thread.__init__(self) self.trigger_init = None self.stylusplh = False + self.COM = False + self.nav_id = nav_id self.__bind_events() try: import serial self.trigger_init = serial.Serial('COM1', baudrate=9600, timeout=0) - self.nav_id = nav_id - self._pause_ = False - self.start() + self.COM = True - except ImportError: - print 'PySerial library not installed. Please install to use Trigger option.' + except: + wx.MessageBox(_('Connection with port COM1 failed'), _('Communication error'), wx.OK | wx.ICON_ERROR) + self.COM = False - except serial.serialutil.SerialException: - print 'Connection with port COM1 failed.' + self._pause_ = False + self.start() def __bind_events(self): Publisher.subscribe(self.OnStylusPLH, 'PLH Stylus Button On') @@ -59,15 +60,16 @@ def run(self): while self.nav_id: - self.trigger_init.write('0') - sleep(0.3) - lines = self.trigger_init.readlines() - # Following lines can simulate a trigger in 3 sec repetitions - # sleep(3) - # lines = True - if lines: - wx.CallAfter(Publisher.sendMessage, 'Create marker') - sleep(0.5) + if self.COM: + self.trigger_init.write('0') + sleep(0.3) + lines = self.trigger_init.readlines() + # Following lines can simulate a trigger in 3 sec repetitions + # sleep(3) + # lines = True + if lines: + wx.CallAfter(Publisher.sendMessage, 'Create marker') + sleep(0.5) if self.stylusplh: wx.CallAfter(Publisher.sendMessage, 'Create marker') diff -Nru invesalius-3.1.1/invesalius/data/viewer_slice.py invesalius-3.1.99991/invesalius/data/viewer_slice.py --- invesalius-3.1.1/invesalius/data/viewer_slice.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/viewer_slice.py 2018-08-17 16:41:39.000000000 +0000 @@ -105,10 +105,16 @@ sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(txt_mip_size, 0, wx.EXPAND | wx.ALL, 2) sizer.Add(self.mip_size_spin, 0, wx.EXPAND) - sizer.AddSpacer((10, 0)) + try: + sizer.Add(10, 0) + except TypeError: + sizer.Add((10, 0)) sizer.Add(self.txt_mip_border, 0, wx.EXPAND | wx.ALL, 2) sizer.Add(self.border_spin, 0, wx.EXPAND) - sizer.AddSpacer((10, 0)) + try: + sizer.Add(10, 0) + except TypeError: + sizer.Add((10, 0)) sizer.Add(self.inverted, 0, wx.EXPAND) self.SetSizer(sizer) sizer.Fit(self) @@ -126,29 +132,27 @@ Publisher.subscribe(self._set_projection_type, 'Set projection type') - def OnSetMIPSize(self, evt): + def OnSetMIPSize(self, number_slices): val = self.mip_size_spin.GetValue() - Publisher.sendMessage('Set MIP size %s' % self.orientation, val) + Publisher.sendMessage('Set MIP size %s' % self.orientation, number_slices=val) def OnSetMIPBorder(self, evt): val = self.border_spin.GetValue() - Publisher.sendMessage('Set MIP border %s' % self.orientation, val) + Publisher.sendMessage('Set MIP border %s' % self.orientation, border_size=val) def OnCheckInverted(self, evt): val = self.inverted.GetValue() - Publisher.sendMessage('Set MIP Invert %s' % self.orientation, val) - - def _set_projection_type(self, pubsub_evt): - tprojection = pubsub_evt.data + Publisher.sendMessage('Set MIP Invert %s' % self.orientation, invert=val) - if tprojection in (const.PROJECTION_MIDA, - const.PROJECTION_CONTOUR_MIDA): + def _set_projection_type(self, projection_id): + if projection_id in (const.PROJECTION_MIDA, + const.PROJECTION_CONTOUR_MIDA): self.inverted.Enable() else: self.inverted.Disable() - if tprojection in (const.PROJECTION_CONTOUR_MIP, - const.PROJECTION_CONTOUR_MIDA): + if projection_id in (const.PROJECTION_CONTOUR_MIP, + const.PROJECTION_CONTOUR_MIDA): self.border_spin.Enable() self.txt_mip_border.Enable() else: @@ -206,7 +210,10 @@ self.alpha = np.zeros((h, w, 1), dtype=np.uint8) self.bitmap = wx.EmptyBitmapRGBA(w, h) - self.image = wx.ImageFromBuffer(w, h, self.rgb, self.alpha) + try: + self.image = wx.Image(w, h, self.rgb, self.alpha) + except TypeError: + self.image = wx.ImageFromBuffer(w, h, self.rgb, self.alpha) def _resize_canvas(self, w, h): self._array = np.zeros((h, w, 4), dtype=np.uint8) @@ -218,7 +225,10 @@ self.alpha = np.zeros((h, w, 1), dtype=np.uint8) self.bitmap = wx.EmptyBitmapRGBA(w, h) - self.image = wx.ImageFromBuffer(w, h, self.rgb, self.alpha) + try: + self.image = wx.Image(w, h, self.rgb, self.alpha) + except TypeError: + self.image = wx.ImageFromBuffer(w, h, self.rgb, self.alpha) self.modified = True @@ -325,7 +335,7 @@ p0y = -p0y p1y = -p1y - pen = wx.Pen(wx.Colour(*colour), width, wx.SOLID) + pen = wx.Pen(wx.Colour(*[int(c) for c in colour]), width, wx.SOLID) pen.SetCap(wx.CAP_BUTT) gc.SetPen(pen) @@ -496,7 +506,7 @@ if self.gc is None: return None gc = self.gc - pen = wx.Pen(wx.Colour(*line_colour), width, wx.SOLID) + pen = wx.Pen(wx.Colour(*[int(c) for c in line_colour]), width, wx.SOLID) gc.SetPen(pen) c = np.array(center) @@ -521,7 +531,7 @@ ea = a0 path = gc.CreatePath() - path.AddArc((c[0], c[1]), min(s0, s1), sa, ea) + path.AddArc(float(c[0]), float(c[1]), float(min(s0, s1)), float(sa), float(ea), True) gc.StrokePath(path) self._drawn = True @@ -600,7 +610,7 @@ sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) background_sizer = wx.BoxSizer(wx.VERTICAL) - background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) + background_sizer.Add(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) #background_sizer.Add(self.mip_ctrls, 0, wx.EXPAND|wx.GROW|wx.ALL, 2) self.SetSizer(background_sizer) background_sizer.Fit(self) @@ -652,8 +662,7 @@ self.UpdateCanvas() self.on_text = True - def __set_layout(self, pubsub_evt): - layout = pubsub_evt.data + def __set_layout(self, layout): self.SetLayout(layout) def __config_interactor(self): @@ -681,8 +690,7 @@ self.state = state - def UpdateWindowLevelValue(self, pubsub_evt): - window, level = pubsub_evt.data + def UpdateWindowLevelValue(self, window, level): self.acum_achange_window, self.acum_achange_level = (window, level) self.SetWLText(window, level) @@ -692,18 +700,18 @@ Publisher.sendMessage('Update all slice') Publisher.sendMessage('Update clut imagedata widget') - def UpdateWindowLevelText(self, pubsub_evt): - window, level = pubsub_evt.data - self.acum_achange_window, self.acum_achange_level = (window, level) + def UpdateWindowLevelText(self, window, level): + self.acum_achange_window, self.acum_achange_level = window, level self.SetWLText(window, level) self.interactor.Render() def OnClutChange(self, evt): Publisher.sendMessage('Change colour table from background image from widget', - evt.GetNodes()) + nodes=evt.GetNodes()) slc = sl.Slice() Publisher.sendMessage('Update window level value', - (slc.window_width, slc.window_level)) + window=slc.window_width, + level=slc.window_level) def SetWLText(self, window_width, window_level): value = STR_WL%(window_level, window_width) @@ -918,46 +926,45 @@ ren.GetActiveCamera().Zoom(1.0) self.interactor.Render() - def ChangeBrushColour(self, pubsub_evt): - vtk_colour = pubsub_evt.data[3] + def ChangeBrushColour(self, colour): + vtk_colour = colour self._brush_cursor_colour = vtk_colour if (self.cursor): for slice_data in self.slice_data_list: slice_data.cursor.SetColour(vtk_colour) - def SetBrushColour(self, pubsub_evt): - colour_wx = pubsub_evt.data - colour_vtk = [colour/float(255) for colour in colour_wx] + def SetBrushColour(self, colour): + colour_vtk = [colour/float(255) for colour in colour] self._brush_cursor_colour = colour_vtk if self.slice_data.cursor: self.slice_data.cursor.SetColour(colour_vtk) - def UpdateSlicesNavigation(self, pubsub_evt): + def UpdateSlicesNavigation(self, arg, position): # Get point from base change - wx, wy, wz = pubsub_evt.data - px, py = self.get_slice_pixel_coord_by_world_pos(wx, wy, wz) + ux, uy, uz = position[:3] + px, py = self.get_slice_pixel_coord_by_world_pos(ux, uy, uz) coord = self.calcultate_scroll_position(px, py) - self.cross.SetFocalPoint((wx, wy, wz)) + self.cross.SetFocalPoint((ux, uy, uz)) self.ScrollSlice(coord) - Publisher.sendMessage('Set ball reference position', (wx, wy, wz)) + Publisher.sendMessage('Set ball reference position', position=(ux, uy, uz)) def ScrollSlice(self, coord): if self.orientation == "AXIAL": Publisher.sendMessage(('Set scroll position', 'SAGITAL'), - coord[0]) + index=coord[0]) Publisher.sendMessage(('Set scroll position', 'CORONAL'), - coord[1]) + index=coord[1]) elif self.orientation == "SAGITAL": Publisher.sendMessage(('Set scroll position', 'AXIAL'), - coord[2]) + index=coord[2]) Publisher.sendMessage(('Set scroll position', 'CORONAL'), - coord[1]) + index=coord[1]) elif self.orientation == "CORONAL": Publisher.sendMessage(('Set scroll position', 'AXIAL'), - coord[2]) + index=coord[2]) Publisher.sendMessage(('Set scroll position', 'SAGITAL'), - coord[0]) + index=coord[0]) def get_slice_data(self, render): #for slice_data in self.slice_data_list: @@ -1239,39 +1246,37 @@ Publisher.subscribe(self.UpdateInterpolatedSlice, "Update Slice Interpolation") - def RefreshViewer(self, pubsub_evt): + def RefreshViewer(self): self.Refresh() - def SetDefaultCursor(self, pusub_evt): + def SetDefaultCursor(self): self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) - def SetSizeNSCursor(self, pusub_evt): + def SetSizeNSCursor(self): self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_SIZENS)) - def SetSizeWECursor(self, pusub_evt): + def SetSizeWECursor(self): self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_SIZEWE)) - def SetSizeNWSECursor(self, pubsub_evt): - if sys.platform == 'linux2': + def SetSizeNWSECursor(self): + if sys.platform.startswith('linux'): self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_SIZENWSE)) else: self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) - def OnExportPicture(self, pubsub_evt): - id, filename, filetype = pubsub_evt.data - + def OnExportPicture(self, orientation, filename, filetype): dict = {"AXIAL": const.AXIAL, "CORONAL": const.CORONAL, "SAGITAL": const.SAGITAL} - if id == dict[self.orientation]: + if orientation == dict[self.orientation]: Publisher.sendMessage('Begin busy cursor') if _has_win32api: utils.touch(filename) win_filename = win32api.GetShortPathName(filename) - self._export_picture(id, win_filename, filetype) + self._export_picture(orientation, win_filename, filetype) else: - self._export_picture(id, filename, filetype) + self._export_picture(orientation, filename, filetype) Publisher.sendMessage('End busy cursor') def _export_picture(self, id, filename, filetype): @@ -1328,13 +1333,13 @@ Publisher.sendMessage('End busy cursor') - def OnShowText(self, pubsub_evt): + def OnShowText(self): self.ShowTextActors() - def OnHideText(self, pubsub_evt): + def OnHideText(self): self.HideTextActors() - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.CloseProject() def CloseProject(self): @@ -1359,11 +1364,10 @@ self.wl_text = None self.pick = vtk.vtkWorldPointPicker() - def OnSetInteractorStyle(self, pubsub_evt): - state = pubsub_evt.data - self.SetInteractorStyle(state) + def OnSetInteractorStyle(self, style): + self.SetInteractorStyle(style) - if (state != const.SLICE_STATE_EDITOR): + if (style not in [const.SLICE_STATE_EDITOR, const.SLICE_STATE_WATERSHED]): Publisher.sendMessage('Set interactor default cursor') def __bind_events_wx(self): @@ -1374,15 +1378,14 @@ self.interactor.Bind(wx.EVT_RIGHT_UP, self.OnContextMenu) self.interactor.Bind(wx.EVT_SIZE, self.OnSize) - def LoadImagedata(self, pubsub_evt): - mask_dict = pubsub_evt.data + def LoadImagedata(self, mask_dict): self.SetInput(mask_dict) def LoadRenderers(self, imagedata): number_renderers = self.layout[0] * self.layout[1] diff = number_renderers - len(self.slice_data_list) if diff > 0: - for i in xrange(diff): + for i in range(diff): slice_data = self.create_slice_window(imagedata) self.slice_data_list.append(slice_data) elif diff < 0: @@ -1400,8 +1403,8 @@ w *= proportion_x h *= proportion_y n = 0 - for j in xrange(self.layout[1]-1, -1, -1): - for i in xrange(self.layout[0]): + for j in range(self.layout[1]-1, -1, -1): + for i in range(self.layout[0]): slice_xi = i*proportion_x slice_xf = (i+1)*proportion_x slice_yi = j*proportion_y @@ -1497,9 +1500,8 @@ renderer.AddActor(cross_actor) - def __update_cross_position(self, pubsub_evt): - pos = pubsub_evt.data - self.cross.SetFocalPoint(pos) + def __update_cross_position(self, position): + self.cross.SetFocalPoint(position) def _set_cross_visibility(self, visibility): self.cross_actor.SetVisibility(visibility) @@ -1557,8 +1559,8 @@ return slice_data - def UpdateInterpolatedSlice(self, pub_sub): - if self.slice_actor != None: + def UpdateInterpolatedSlice(self): + if self.slice_actor != None: if ses.Session().slice_interpolation: self.slice_actor.InterpolateOff() else: @@ -1566,15 +1568,13 @@ self.interactor.Render() - def SetInterpolatedSlices(self, pub_sub): - self.interpolation_slice_status = status = pub_sub.data - + def SetInterpolatedSlices(self, flag): + self.interpolation_slice_status = flag if self.slice_actor != None: - if status == True: + if self.interpolation_slice_status == True: self.slice_actor.InterpolateOn() else: self.slice_actor.InterpolateOff() - self.interactor.Render() def __update_camera(self): @@ -1593,7 +1593,7 @@ self.slice_data.actor.SetDisplayExtent(image.GetExtent()) self.slice_data.renderer.ResetCameraClippingRange() - def UpdateRender(self, evt): + def UpdateRender(self): self.interactor.Render() def UpdateCanvas(self, evt=None): @@ -1643,8 +1643,8 @@ def UpdateSlice3D(self, pos): original_orientation = project.Project().original_orientation pos = self.scroll.GetThumbPosition() - Publisher.sendMessage('Change slice from slice plane',\ - (self.orientation, pos)) + Publisher.sendMessage('Change slice from slice plane', + orientation=self.orientation, index=pos) def OnScrollBar(self, evt=None, update3D=True): pos = self.scroll.GetThumbPosition() @@ -1655,7 +1655,7 @@ # Update other slice's cross according to the new focal point from # the actual orientation. focal_point = self.cross.GetFocalPoint() - Publisher.sendMessage('Update cross position', focal_point) + Publisher.sendMessage('Update cross position', position=focal_point) Publisher.sendMessage('Update slice viewer') else: self.interactor.Render() @@ -1710,7 +1710,7 @@ elif evt.GetKeyCode() in projections: self.slice_.SetTypeProjection(projections[evt.GetKeyCode()]) - Publisher.sendMessage('Set projection type', projections[evt.GetKeyCode()]) + Publisher.sendMessage('Set projection type', projection_id=projections[evt.GetKeyCode()]) Publisher.sendMessage('Reload actual slice') skip = False @@ -1754,28 +1754,24 @@ self.slice_data.SetSize((w, h)) evt.Skip() - def OnSetMIPSize(self, pubsub_evt): - val = pubsub_evt.data - self.number_slices = val + def OnSetMIPSize(self, number_slices): + self.number_slices = number_slices self.ReloadActualSlice() - def OnSetMIPBorder(self, pubsub_evt): - val = pubsub_evt.data - self.slice_.n_border = val + def OnSetMIPBorder(self, border_size): + self.slice_.n_border = border_size buffer_ = self.slice_.buffer_slices[self.orientation] buffer_.discard_buffer() self.ReloadActualSlice() - def OnSetMIPInvert(self, pubsub_evt): - val = pubsub_evt.data - self._mip_inverted = val + def OnSetMIPInvert(self, invert): + self._mip_inverted = invert buffer_ = self.slice_.buffer_slices[self.orientation] buffer_.discard_buffer() self.ReloadActualSlice() - def OnShowMIPInterface(self, pubsub_evt): - value = pubsub_evt.data - if value: + def OnShowMIPInterface(self, flag): + if flag: if not self.mip_ctrls.Shown: self.mip_ctrls.Show() self.GetSizer().Add(self.mip_ctrls, 0, wx.EXPAND|wx.GROW|wx.ALL, 2) @@ -1785,9 +1781,8 @@ self.GetSizer().Detach(self.mip_ctrls) self.Layout() - def OnSetOverwriteMask(self, pubsub_evt): - value = pubsub_evt.data - self.overwrite_mask = value + def OnSetOverwriteMask(self, flag): + self.overwrite_mask = flag def set_slice_number(self, index): inverted = self.mip_ctrls.inverted.GetValue() @@ -1819,28 +1814,27 @@ self.__update_display_extent(image) self.cross.SetModelBounds(self.slice_data.actor.GetBounds()) - def ChangeSliceNumber(self, pubsub_evt): - index = pubsub_evt.data + def ChangeSliceNumber(self, index): #self.set_slice_number(index) self.scroll.SetThumbPosition(index) pos = self.scroll.GetThumbPosition() self.set_slice_number(pos) self.interactor.Render() - def ReloadActualSlice(self, pubsub_evt=None): + def ReloadActualSlice(self): pos = self.scroll.GetThumbPosition() self.set_slice_number(pos) self.interactor.Render() - def OnUpdateScroll(self, pubsub_evt): + def OnUpdateScroll(self): max_slice_number = sl.Slice().GetNumberOfSlices(self.orientation) self.scroll.SetScrollbar(wx.SB_VERTICAL, 1, max_slice_number, max_slice_number) - def OnSwapVolumeAxes(self, pubsub_evt): + def OnSwapVolumeAxes(self, axes): # Adjusting cursor spacing to match the spacing from the actual slice # orientation - axis0, axis1 = pubsub_evt.data + axis0, axis1 = axes cursor = self.slice_data.cursor spacing = cursor.spacing if (axis0, axis1) == (2, 1): @@ -1852,34 +1846,32 @@ self.slice_data.renderer.ResetCamera() - def AddActors(self, pubsub_evt): + def AddActors(self, actors, slice_number): "Inserting actors" - actors, n = pubsub_evt.data pos = self.scroll.GetThumbPosition() #try: - #renderer = self.renderers_by_slice_number[n] + #renderer = self.renderers_by_slice_number[slice_number] #for actor in actors: #renderer.AddActor(actor) #except KeyError: #pass - if pos == n: + if pos == slice_number: for actor in actors: self.slice_data.renderer.AddActor(actor) - self.actors_by_slice_number[n].extend(actors) + self.actors_by_slice_number[slice_number].extend(actors) - def RemoveActors(self, pubsub_evt): + def RemoveActors(self, actors, slice_number): "Remove a list of actors" - actors, n = pubsub_evt.data try: - renderer = self.renderers_by_slice_number[n] + renderer = self.renderers_by_slice_number[slice_number] except KeyError: for actor in actors: - self.actors_by_slice_number[n].remove(actor) + self.actors_by_slice_number[slice_number].remove(actor) self.slice_data.renderer.RemoveActor(actor) else: for actor in actors: # Remove the actor from the renderer renderer.RemoveActor(actor) # and remove the actor from the actor's list - self.actors_by_slice_number[n].remove(actor) + self.actors_by_slice_number[slice_number].remove(actor) diff -Nru invesalius-3.1.1/invesalius/data/viewer_volume.py invesalius-3.1.99991/invesalius/data/viewer_volume.py --- invesalius-3.1.1/invesalius/data/viewer_volume.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/viewer_volume.py 2018-08-17 16:41:39.000000000 +0000 @@ -20,6 +20,7 @@ # detalhes. #-------------------------------------------------------------------------- +from math import cos, sin import os import sys @@ -29,14 +30,16 @@ import vtk from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor from wx.lib.pubsub import pub as Publisher +import random +from scipy.spatial import distance import invesalius.constants as const import invesalius.data.bases as bases +import invesalius.data.transformations as tr import invesalius.data.vtk_utils as vtku import invesalius.project as prj import invesalius.style as st import invesalius.utils as utils -import invesalius.data.measures as measures if sys.platform == 'win32': try: @@ -95,6 +98,14 @@ self.text.SetValue("") self.ren.AddActor(self.text.actor) + # axes = vtk.vtkAxesActor() + # axes.SetXAxisLabelText('x') + # axes.SetYAxisLabelText('y') + # axes.SetZAxisLabelText('z') + # axes.SetTotalLength(50, 50, 50) + # + # self.ren.AddActor(axes) + self.slice_plane = None @@ -123,9 +134,20 @@ self.repositioned_coronal_plan = 0 self.added_actor = 0 - self.camera_state = True + self.camera_state = const.CAM_MODE + + self.nav_status = False self.ball_actor = None + self.obj_actor = None + self.obj_axes = None + self.obj_name = False + self.obj_state = None + self.obj_actor_list = None + self.arrow_actor_list = None + + # self.obj_axes = None + self._mode_cross = False self._to_show_ball = 0 self._ball_ref_visibility = False @@ -136,6 +158,13 @@ self.timer = False self.index = False + self.target_coord = None + self.aim_actor = None + self.dummy_coil_actor = None + self.target_mode = False + self.anglethreshold = const.COIL_ANGLES_THRESHOLD + self.distthreshold = const.COIL_COORD_THRESHOLD + def __bind_events(self): Publisher.subscribe(self.LoadActor, 'Load surface actor into viewer') @@ -159,7 +188,7 @@ 'Update raycasting preset') ### Publisher.subscribe(self.AppendActor,'AppendActor') - Publisher.subscribe(self.SetWidgetInteractor, + Publisher.subscribe(self.SetWidgetInteractor, 'Set Widget Interactor') Publisher.subscribe(self.OnSetViewAngle, 'Set volume view angle') @@ -173,7 +202,8 @@ Publisher.subscribe(self.LoadSlicePlane, 'Load slice plane') Publisher.subscribe(self.ResetCamClippingRange, 'Reset cam clipping range') - Publisher.subscribe(self.SetVolumeCamera, 'Set camera in volume') + Publisher.subscribe(self.SetVolumeCamera, 'Co-registered points') + # Publisher.subscribe(self.SetVolumeCamera, 'Set camera in volume') Publisher.subscribe(self.SetVolumeCameraState, 'Update volume camera state') Publisher.subscribe(self.OnEnableStyle, 'Enable style') @@ -190,16 +220,16 @@ Publisher.subscribe(self.OnCloseProject, 'Close project data') Publisher.subscribe(self.RemoveAllActor, 'Remove all volume actors') - + 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.SetStereoMode, 'Set stereo mode') - + Publisher.subscribe(self.Reposition3DPlane, 'Reposition 3D Plane') - + Publisher.subscribe(self.RemoveVolume, 'Remove Volume') Publisher.subscribe(self.SetBallReferencePosition, @@ -219,10 +249,24 @@ Publisher.subscribe(self.BlinkMarker, 'Blink Marker') Publisher.subscribe(self.StopBlinkMarker, 'Stop Blink Marker') - def SetStereoMode(self, pubsub_evt): - mode = pubsub_evt.data + # Related to object tracking during neuronavigation + Publisher.subscribe(self.OnNavigationStatus, 'Navigation status') + Publisher.subscribe(self.UpdateObjectOrientation, 'Update object matrix') + Publisher.subscribe(self.UpdateTrackObjectState, 'Update track object state') + Publisher.subscribe(self.UpdateShowObjectState, 'Update show object state') + + Publisher.subscribe(self.ActivateTargetMode, 'Target navigation mode') + Publisher.subscribe(self.OnUpdateObjectTargetGuide, 'Update object matrix') + Publisher.subscribe(self.OnUpdateTargetCoordinates, 'Update target') + Publisher.subscribe(self.OnRemoveTarget, 'Disable or enable coil tracker') + # Publisher.subscribe(self.UpdateObjectTargetView, 'Co-registered points') + Publisher.subscribe(self.OnTargetMarkerTransparency, 'Set target transparency') + Publisher.subscribe(self.OnUpdateAngleThreshold, 'Update angle threshold') + Publisher.subscribe(self.OnUpdateDistThreshold, 'Update dist threshold') + + def SetStereoMode(self, mode): ren_win = self.interactor.GetRenderWindow() - + if mode == const.STEREO_OFF: ren_win.StereoRenderOff() else: @@ -245,26 +289,22 @@ ren_win.SetStereoTypeToAnaglyph() ren_win.StereoRenderOn() - + self.interactor.Render() - def _check_ball_reference(self, pubsub_evt): - st = pubsub_evt.data - if st == const.SLICE_STATE_CROSS: + def _check_ball_reference(self, style): + if style == const.SLICE_STATE_CROSS: self._mode_cross = True self._check_and_set_ball_visibility() self.interactor.Render() - def _uncheck_ball_reference(self, pubsub_evt): - st = pubsub_evt.data - if st == const.SLICE_STATE_CROSS: + def _uncheck_ball_reference(self, style): + if style == const.SLICE_STATE_CROSS: self._mode_cross = False self.RemoveBallReference() self.interactor.Render() - def OnSensors(self, pubsub_evt): - probe_id = pubsub_evt.data[0] - ref_id = pubsub_evt.data[1] + def OnSensors(self, probe_id, ref_id): if not self.sen1: self.CreateSensorID() @@ -300,39 +340,36 @@ self.interactor.Render() - def OnRemoveSensorsID(self, pubsub_evt): + def OnRemoveSensorsID(self): if self.sen1: self.ren.RemoveActor(self.sen1.actor) self.ren.RemoveActor(self.sen2.actor) self.sen1 = self.sen2 = False self.interactor.Render() - def OnShowSurface(self, pubsub_evt): - index, value = pubsub_evt.data - if value: + def OnShowSurface(self, index, visibility): + if visibility: self._to_show_ball += 1 else: self._to_show_ball -= 1 self._check_and_set_ball_visibility() - def OnStartSeed(self, pubsub_evt): - index = pubsub_evt.data + def OnStartSeed(self): self.seed_points = [] - - def OnEndSeed(self, pubsub_evt): + + def OnEndSeed(self): Publisher.sendMessage("Create surface from seeds", - self.seed_points) + seeds=self.seed_points) - def OnExportPicture(self, pubsub_evt): - id, filename, filetype = pubsub_evt.data - if id == const.VOLUME: + def OnExportPicture(self, orientation, filename, filetype): + if orientation == const.VOLUME: Publisher.sendMessage('Begin busy cursor') if _has_win32api: utils.touch(filename) win_filename = win32api.GetShortPathName(filename) - self._export_picture(id, win_filename, filetype) + self._export_picture(orientation, win_filename, filetype) else: - self._export_picture(id, filename, filetype) + self._export_picture(orientation, filename, filetype) Publisher.sendMessage('End busy cursor') def _export_picture(self, id, filename, filetype): @@ -374,7 +411,7 @@ wx.MessageBox(_("InVesalius was not able to export this picture"), _("Export picture error")) - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): if self.raycasting_volume: self.raycasting_volume = False @@ -391,22 +428,21 @@ self.SetInteractorStyle(const.STATE_DEFAULT) self._last_state = const.STATE_DEFAULT - def OnHideText(self, pubsub_evt): + def OnHideText(self): self.text.Hide() self.interactor.Render() - def OnShowText(self, pubsub_evt): + def OnShowText(self): if self.on_wl: self.text.Show() self.interactor.Render() - def AddActors(self, pubsub_evt): + def AddActors(self, actors): "Inserting actors" - actors = pubsub_evt.data[0] for actor in actors: self.ren.AddActor(actor) - def RemoveVolume(self, pub_evt): + def RemoveVolume(self): volumes = self.ren.GetVolumes() if (volumes.GetNumberOfItems()): self.ren.RemoveVolume(volumes.GetLastProp()) @@ -414,9 +450,8 @@ self._to_show_ball -= 1 self._check_and_set_ball_visibility() - def RemoveActors(self, pubsub_evt): + def RemoveActors(self, actors): "Remove a list of actors" - actors = pubsub_evt.data[0] for actor in actors: self.ren.RemoveActor(actor) @@ -456,26 +491,22 @@ actor = self.points_reference.pop(point) self.ren.RemoveActor(actor) - def AddMarker(self, pubsub_evt): + def AddMarker(self, ball_id, size, colour, coord): """ - Markers create by navigation tools and - rendered in volume viewer. + Markers created by navigation tools and rendered in volume viewer. """ - self.ball_id = pubsub_evt.data[0] - ballsize = pubsub_evt.data[1] - ballcolour = pubsub_evt.data[2] - coord = pubsub_evt.data[3] + self.ball_id = ball_id x, y, z = bases.flip_x(coord) ball_ref = vtk.vtkSphereSource() - ball_ref.SetRadius(ballsize) + ball_ref.SetRadius(size) ball_ref.SetCenter(x, y, z) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(ball_ref.GetOutputPort()) prop = vtk.vtkProperty() - prop.SetColor(ballcolour) + prop.SetColor(colour) #adding a new actor for the present ball self.staticballs.append(vtk.vtkActor()) @@ -487,57 +518,540 @@ self.ball_id = self.ball_id + 1 #self.UpdateRender() self.Refresh() - - def HideAllMarkers(self, pubsub_evt): - ballid = pubsub_evt.data + + def HideAllMarkers(self, indexes): + ballid = indexes for i in range(0, ballid): self.staticballs[i].SetVisibility(0) self.UpdateRender() - def ShowAllMarkers(self, pubsub_evt): - ballid = pubsub_evt.data + def ShowAllMarkers(self, indexes): + ballid = indexes for i in range(0, ballid): self.staticballs[i].SetVisibility(1) self.UpdateRender() - def RemoveAllMarkers(self, pubsub_evt): - ballid = pubsub_evt.data + def RemoveAllMarkers(self, indexes): + ballid = indexes for i in range(0, ballid): self.ren.RemoveActor(self.staticballs[i]) self.staticballs = [] self.UpdateRender() - def RemoveMarker(self, pubsub_evt): - index = pubsub_evt.data + def RemoveMarker(self, index): for i in reversed(index): self.ren.RemoveActor(self.staticballs[i]) del self.staticballs[i] self.ball_id = self.ball_id - 1 self.UpdateRender() - def BlinkMarker(self, pubsub_evt): + def BlinkMarker(self, index): if self.timer: self.timer.Stop() self.staticballs[self.index].SetVisibility(1) - self.index = pubsub_evt.data + self.index = index self.timer = wx.Timer(self) - self.Bind(wx.EVT_TIMER, self.blink, self.timer) + self.Bind(wx.EVT_TIMER, self.OnBlinkMarker, self.timer) self.timer.Start(500) self.timer_count = 0 - def blink(self, evt): + def OnBlinkMarker(self, evt): self.staticballs[self.index].SetVisibility(int(self.timer_count % 2)) self.Refresh() self.timer_count += 1 - def StopBlinkMarker(self, pubsub_evt): + def StopBlinkMarker(self, index=None): if self.timer: self.timer.Stop() - if pubsub_evt.data == None: + if index is None: self.staticballs[self.index].SetVisibility(1) self.Refresh() self.index = False + def OnTargetMarkerTransparency(self, status, index): + if status: + self.staticballs[index].GetProperty().SetOpacity(1) + # self.staticballs[index].GetProperty().SetOpacity(0.4) + else: + self.staticballs[index].GetProperty().SetOpacity(1) + + def OnUpdateAngleThreshold(self, angle): + self.anglethreshold = angle + + def OnUpdateDistThreshold(self, dist_threshold): + self.distthreshold = dist_threshold + + def ActivateTargetMode(self, evt=None, target_mode=None): + self.target_mode = target_mode + if self.target_coord and self.target_mode: + self.CreateTargetAim() + + # Create a line + self.ren.SetViewport(0, 0, 0.75, 1) + self.ren2 = vtk.vtkRenderer() + + self.interactor.GetRenderWindow().AddRenderer(self.ren2) + self.ren2.SetViewport(0.75, 0, 1, 1) + self.CreateTextDistance() + + obj_polydata = self.CreateObjectPolyData(self.obj_name) + + normals = vtk.vtkPolyDataNormals() + normals.SetInputData(obj_polydata) + normals.SetFeatureAngle(80) + normals.AutoOrientNormalsOn() + normals.Update() + + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputData(normals.GetOutput()) + mapper.ScalarVisibilityOff() + mapper.ImmediateModeRenderingOn() # improve performance + + obj_roll = vtk.vtkActor() + obj_roll.SetMapper(mapper) + obj_roll.SetPosition(0, 25, -30) + obj_roll.RotateX(-60) + obj_roll.RotateZ(180) + + obj_yaw = vtk.vtkActor() + obj_yaw.SetMapper(mapper) + obj_yaw.SetPosition(0, -115, 5) + obj_yaw.RotateZ(180) + + obj_pitch = vtk.vtkActor() + obj_pitch.SetMapper(mapper) + obj_pitch.SetPosition(5, -265, 5) + obj_pitch.RotateY(90) + obj_pitch.RotateZ(180) + + arrow_roll_z1 = self.CreateArrowActor([-50, -35, 12], [-50, -35, 50]) + arrow_roll_z1.GetProperty().SetColor(1, 1, 0) + arrow_roll_z1.RotateX(-60) + arrow_roll_z1.RotateZ(180) + arrow_roll_z2 = self.CreateArrowActor([50, -35, 0], [50, -35, -50]) + arrow_roll_z2.GetProperty().SetColor(1, 1, 0) + arrow_roll_z2.RotateX(-60) + arrow_roll_z2.RotateZ(180) + + arrow_yaw_y1 = self.CreateArrowActor([-50, -35, 0], [-50, 5, 0]) + arrow_yaw_y1.GetProperty().SetColor(0, 1, 0) + arrow_yaw_y1.SetPosition(0, -150, 0) + arrow_yaw_y1.RotateZ(180) + arrow_yaw_y2 = self.CreateArrowActor([50, -35, 0], [50, -75, 0]) + arrow_yaw_y2.GetProperty().SetColor(0, 1, 0) + arrow_yaw_y2.SetPosition(0, -150, 0) + arrow_yaw_y2.RotateZ(180) + + arrow_pitch_x1 = self.CreateArrowActor([0, 65, 38], [0, 65, 68]) + arrow_pitch_x1.GetProperty().SetColor(1, 0, 0) + arrow_pitch_x1.SetPosition(0, -300, 0) + arrow_pitch_x1.RotateY(90) + arrow_pitch_x1.RotateZ(180) + arrow_pitch_x2 = self.CreateArrowActor([0, -55, 5], [0, -55, -30]) + arrow_pitch_x2.GetProperty().SetColor(1, 0, 0) + arrow_pitch_x2.SetPosition(0, -300, 0) + arrow_pitch_x2.RotateY(90) + arrow_pitch_x2.RotateZ(180) + + self.obj_actor_list = obj_roll, obj_yaw, obj_pitch + self.arrow_actor_list = arrow_roll_z1, arrow_roll_z2, arrow_yaw_y1, arrow_yaw_y2,\ + arrow_pitch_x1, arrow_pitch_x2 + + for ind in self.obj_actor_list: + self.ren2.AddActor(ind) + + for ind in self.arrow_actor_list: + self.ren2.AddActor(ind) + + self.ren.ResetCamera() + self.SetCameraTarget() + #self.ren.GetActiveCamera().Zoom(4) + + self.ren2.ResetCamera() + self.ren2.GetActiveCamera().Zoom(2) + self.ren2.InteractiveOff() + self.interactor.Render() + + else: + self.DisableCoilTracker() + + def OnUpdateObjectTargetGuide(self, m_img, coord): + if self.target_coord and self.target_mode: + + target_dist = distance.euclidean(coord[0:3], + (self.target_coord[0], -self.target_coord[1], self.target_coord[2])) + + # self.txt.SetCoilDistanceValue(target_dist) + self.textSource.SetText('Dist: ' + str("{:06.2f}".format(target_dist)) + ' mm') + self.ren.ResetCamera() + self.SetCameraTarget() + if target_dist > 100: + target_dist = 100 + # ((-0.0404*dst) + 5.0404) is the linear equation to normalize the zoom between 1 and 5 times with + # the distance between 1 and 100 mm + self.ren.GetActiveCamera().Zoom((-0.0404 * target_dist) + 5.0404) + + if target_dist <= self.distthreshold: + thrdist = True + self.aim_actor.GetProperty().SetColor(0, 1, 0) + else: + thrdist = False + self.aim_actor.GetProperty().SetColor(1, 1, 1) + + coordx = self.target_coord[3] - coord[3] + if coordx > const.ARROW_UPPER_LIMIT: + coordx = const.ARROW_UPPER_LIMIT + elif coordx < -const.ARROW_UPPER_LIMIT: + coordx = -const.ARROW_UPPER_LIMIT + coordx = const.ARROW_SCALE * coordx + + coordy = self.target_coord[4] - coord[4] + if coordy > const.ARROW_UPPER_LIMIT: + coordy = const.ARROW_UPPER_LIMIT + elif coordy < -const.ARROW_UPPER_LIMIT: + coordy = -const.ARROW_UPPER_LIMIT + coordy = const.ARROW_SCALE * coordy + + coordz = self.target_coord[5] - coord[5] + if coordz > const.ARROW_UPPER_LIMIT: + coordz = const.ARROW_UPPER_LIMIT + elif coordz < -const.ARROW_UPPER_LIMIT: + coordz = -const.ARROW_UPPER_LIMIT + coordz = const.ARROW_SCALE * coordz + + for ind in self.arrow_actor_list: + self.ren2.RemoveActor(ind) + + if self.anglethreshold * const.ARROW_SCALE > coordx > -self.anglethreshold * const.ARROW_SCALE: + thrcoordx = True + self.obj_actor_list[0].GetProperty().SetColor(0, 1, 0) + else: + thrcoordx = False + self.obj_actor_list[0].GetProperty().SetColor(1, 1, 1) + + offset = 5 + + arrow_roll_x1 = self.CreateArrowActor([-55, -35, offset], [-55, -35, offset - coordx]) + arrow_roll_x1.RotateX(-60) + arrow_roll_x1.RotateZ(180) + arrow_roll_x1.GetProperty().SetColor(1, 1, 0) + + arrow_roll_x2 = self.CreateArrowActor([55, -35, offset], [55, -35, offset + coordx]) + arrow_roll_x2.RotateX(-60) + arrow_roll_x2.RotateZ(180) + arrow_roll_x2.GetProperty().SetColor(1, 1, 0) + + if self.anglethreshold * const.ARROW_SCALE > coordz > -self.anglethreshold * const.ARROW_SCALE: + thrcoordz = True + self.obj_actor_list[1].GetProperty().SetColor(0, 1, 0) + else: + thrcoordz = False + self.obj_actor_list[1].GetProperty().SetColor(1, 1, 1) + + offset = -35 + + arrow_yaw_z1 = self.CreateArrowActor([-55, offset, 0], [-55, offset - coordz, 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.CreateArrowActor([55, offset, 0], [55, offset + coordz, 0]) + arrow_yaw_z2.SetPosition(0, -150, 0) + arrow_yaw_z2.RotateZ(180) + arrow_yaw_z2.GetProperty().SetColor(0, 1, 0) + + if self.anglethreshold * const.ARROW_SCALE > coordy > -self.anglethreshold * const.ARROW_SCALE: + thrcoordy = True + self.obj_actor_list[2].GetProperty().SetColor(0, 1, 0) + else: + thrcoordy = False + self.obj_actor_list[2].GetProperty().SetColor(1, 1, 1) + + offset = 38 + arrow_pitch_y1 = self.CreateArrowActor([0, 65, offset], [0, 65, offset + coordy]) + 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.CreateArrowActor([0, -55, offset], [0, -55, offset - coordy]) + 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) + + if thrdist and thrcoordx and thrcoordy and thrcoordz: + self.dummy_coil_actor.GetProperty().SetColor(0, 1, 0) + else: + self.dummy_coil_actor.GetProperty().SetColor(1, 1, 1) + + self.arrow_actor_list = arrow_roll_x1, arrow_roll_x2, arrow_yaw_z1, arrow_yaw_z2, \ + arrow_pitch_y1, arrow_pitch_y2 + + for ind in self.arrow_actor_list: + self.ren2.AddActor(ind) + + self.Refresh() + + def OnUpdateTargetCoordinates(self, coord): + self.target_coord = coord + self.target_coord[1] = -self.target_coord[1] + self.CreateTargetAim() + + def OnRemoveTarget(self, status): + if not status: + self.target_mode = None + self.target_coord = None + self.RemoveTargetAim() + self.DisableCoilTracker() + + def CreateTargetAim(self): + if self.aim_actor: + self.RemoveTargetAim() + self.aim_actor = None + + self.textSource = vtk.vtkVectorText() + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputConnection(self.textSource.GetOutputPort()) + tactor = vtk.vtkFollower() + tactor.SetMapper(mapper) + tactor.GetProperty().SetColor(1.0, 0.25, 0.0) + tactor.SetScale(5) + tactor.SetPosition(self.target_coord[0]+10, self.target_coord[1]+30, self.target_coord[2]+20) + self.ren.AddActor(tactor) + self.tactor = tactor + tactor.SetCamera(self.ren.GetActiveCamera()) + + + # v3, M_plane_inv = self.Plane(self.target_coord[0:3], self.pTarget) + # mat4x4 = vtk.vtkMatrix4x4() + # for i in range(4): + # mat4x4.SetElement(i, 0, M_plane_inv[i][0]) + # mat4x4.SetElement(i, 1, M_plane_inv[i][1]) + # mat4x4.SetElement(i, 2, M_plane_inv[i][2]) + # mat4x4.SetElement(i, 3, M_plane_inv[i][3]) + + a, b, g = np.radians(self.target_coord[3:]) + r_ref = tr.euler_matrix(a, b, g, 'sxyz') + t_ref = tr.translation_matrix(self.target_coord[:3]) + m_img = np.asmatrix(tr.concatenate_matrices(t_ref, r_ref)) + + m_img_vtk = vtk.vtkMatrix4x4() + + for row in range(0, 4): + for col in range(0, 4): + m_img_vtk.SetElement(row, col, m_img[row, col]) + + self.m_img_vtk = m_img_vtk + + filename = os.path.join(const.OBJ_DIR, "aim.stl") + + reader = vtk.vtkSTLReader() + reader.SetFileName(filename) + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputConnection(reader.GetOutputPort()) + + # Transform the polydata + transform = vtk.vtkTransform() + transform.SetMatrix(m_img_vtk) + #transform.SetMatrix(mat4x4) + transformPD = vtk.vtkTransformPolyDataFilter() + transformPD.SetTransform(transform) + transformPD.SetInputConnection(reader.GetOutputPort()) + transformPD.Update() + # mapper transform + mapper.SetInputConnection(transformPD.GetOutputPort()) + + aim_actor = vtk.vtkActor() + aim_actor.SetMapper(mapper) + aim_actor.GetProperty().SetColor(1, 1, 1) + aim_actor.GetProperty().SetOpacity(0.6) + self.aim_actor = aim_actor + self.ren.AddActor(aim_actor) + + obj_polydata = self.CreateObjectPolyData(os.path.join(const.OBJ_DIR, "magstim_fig8_coil_no_handle.stl")) + + transform = vtk.vtkTransform() + transform.RotateZ(90) + + transform_filt = vtk.vtkTransformPolyDataFilter() + transform_filt.SetTransform(transform) + transform_filt.SetInputData(obj_polydata) + transform_filt.Update() + + normals = vtk.vtkPolyDataNormals() + normals.SetInputData(transform_filt.GetOutput()) + normals.SetFeatureAngle(80) + normals.AutoOrientNormalsOn() + normals.Update() + + obj_mapper = vtk.vtkPolyDataMapper() + obj_mapper.SetInputData(normals.GetOutput()) + obj_mapper.ScalarVisibilityOff() + obj_mapper.ImmediateModeRenderingOn() # improve performance + + self.dummy_coil_actor = vtk.vtkActor() + self.dummy_coil_actor.SetMapper(obj_mapper) + self.dummy_coil_actor.GetProperty().SetOpacity(0.4) + self.dummy_coil_actor.SetVisibility(1) + self.dummy_coil_actor.SetUserMatrix(m_img_vtk) + + self.ren.AddActor(self.dummy_coil_actor) + + self.Refresh() + + def RemoveTargetAim(self): + self.ren.RemoveActor(self.aim_actor) + self.ren.RemoveActor(self.dummy_coil_actor) + self.ren.RemoveActor(self.tactor) + self.Refresh() + + def CreateTextDistance(self): + txt = vtku.Text() + txt.SetSize(const.TEXT_SIZE_EXTRA_LARGE) + txt.SetPosition((0.76, 0.05)) + txt.ShadowOff() + txt.BoldOn() + self.txt = txt + self.ren2.AddActor(txt.actor) + + def DisableCoilTracker(self): + try: + self.ren.SetViewport(0, 0, 1, 1) + self.interactor.GetRenderWindow().RemoveRenderer(self.ren2) + self.SetViewAngle(const.VOL_FRONT) + self.ren.RemoveActor(self.txt.actor) + self.CreateTargetAim() + self.interactor.Render() + except: + None + + def CreateArrowActor(self, startPoint, endPoint): + # Compute a basis + normalizedX = [0 for i in range(3)] + normalizedY = [0 for i in range(3)] + normalizedZ = [0 for i in range(3)] + + # The X axis is a vector from start to end + math = vtk.vtkMath() + math.Subtract(endPoint, startPoint, normalizedX) + length = math.Norm(normalizedX) + math.Normalize(normalizedX) + + # The Z axis is an arbitrary vector cross X + arbitrary = [0 for i in range(3)] + arbitrary[0] = random.uniform(-10, 10) + arbitrary[1] = random.uniform(-10, 10) + arbitrary[2] = random.uniform(-10, 10) + math.Cross(normalizedX, arbitrary, normalizedZ) + math.Normalize(normalizedZ) + + # The Y axis is Z cross X + math.Cross(normalizedZ, normalizedX, normalizedY) + matrix = vtk.vtkMatrix4x4() + + # Create the direction cosine matrix + matrix.Identity() + for i in range(3): + matrix.SetElement(i, 0, normalizedX[i]) + matrix.SetElement(i, 1, normalizedY[i]) + matrix.SetElement(i, 2, normalizedZ[i]) + + # Apply the transforms arrow 1 + transform_1 = vtk.vtkTransform() + transform_1.Translate(startPoint) + transform_1.Concatenate(matrix) + transform_1.Scale(length, length, length) + # source + arrowSource1 = vtk.vtkArrowSource() + arrowSource1.SetTipResolution(50) + # Create a mapper and actor + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputConnection(arrowSource1.GetOutputPort()) + # Transform the polydata + transformPD = vtk.vtkTransformPolyDataFilter() + transformPD.SetTransform(transform_1) + transformPD.SetInputConnection(arrowSource1.GetOutputPort()) + # mapper transform + mapper.SetInputConnection(transformPD.GetOutputPort()) + # actor + actor_arrow = vtk.vtkActor() + actor_arrow.SetMapper(mapper) + + return actor_arrow + + def CenterOfMass(self): + proj = prj.Project() + surface = proj.surface_dict[0].polydata + barycenter = [0.0, 0.0, 0.0] + n = surface.GetNumberOfPoints() + for i in range(n): + point = surface.GetPoint(i) + barycenter[0] += point[0] + barycenter[1] += point[1] + barycenter[2] += point[2] + barycenter[0] /= n + barycenter[1] /= n + barycenter[2] /= n + + return barycenter + + def Plane(self, x0, pTarget): + v3 = np.array(pTarget) - x0 # normal to the plane + v3 = v3 / np.linalg.norm(v3) # unit vector + + d = np.dot(v3, x0) + # prevents division by zero. + if v3[0] == 0.0: + v3[0] = 1e-09 + + x1 = np.array([(d - v3[1] - v3[2]) / v3[0], 1, 1]) + v2 = x1 - x0 + v2 = v2 / np.linalg.norm(v2) # unit vector + v1 = np.cross(v3, v2) + v1 = v1 / np.linalg.norm(v1) # unit vector + 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]]) + + return v3, M_plane_inv + + def SetCameraTarget(self): + cam_focus = self.target_coord[0:3] + cam = self.ren.GetActiveCamera() + + oldcamVTK = vtk.vtkMatrix4x4() + oldcamVTK.DeepCopy(cam.GetViewTransformMatrix()) + + newvtk = vtk.vtkMatrix4x4() + newvtk.Multiply4x4(self.m_img_vtk, oldcamVTK, newvtk) + + transform = vtk.vtkTransform() + transform.SetMatrix(newvtk) + transform.Update() + cam.ApplyTransform(transform) + + cam.Roll(90) + + cam_pos0 = np.array(cam.GetPosition()) + cam_focus0 = np.array(cam.GetFocalPoint()) + v0 = cam_pos0 - cam_focus0 + v0n = np.sqrt(inner1d(v0, v0)) + + v1 = (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 + cam_pos = (v1 / v1n) * v0n + cam_focus + cam.SetFocalPoint(cam_focus) + cam.SetPosition(cam_pos) + + def CreateBallReference(self): """ Red sphere on volume visualization to reference center of @@ -545,7 +1059,7 @@ The sphere's radius will be scale times bigger than the average of image spacing values. """ - scale = 3.0 + scale = 2.0 proj = prj.Project() s = proj.spacing r = (s[0] + s[1] + s[2]) / 3.0 * scale @@ -576,18 +1090,135 @@ self.ren.RemoveActor(self.ball_actor) self.ball_actor = None - def SetBallReferencePosition(self, pubsub_evt): + def SetBallReferencePosition(self, position): if self._to_show_ball: if not self.ball_actor: self.ActivateBallReference() - coord = pubsub_evt.data + coord = position x, y, z = bases.flip_x(coord) self.ball_actor.SetPosition(x, y, z) else: self.RemoveBallReference() + def CreateObjectPolyData(self, filename): + """ + Coil for navigation rendered in volume viewer. + """ + filename = utils.decode(filename, const.FS_ENCODE) + if filename: + if filename.lower().endswith('.stl'): + reader = vtk.vtkSTLReader() + elif filename.lower().endswith('.ply'): + reader = vtk.vtkPLYReader() + elif filename.lower().endswith('.obj'): + reader = vtk.vtkOBJReader() + elif filename.lower().endswith('.vtp'): + reader = vtk.vtkXMLPolyDataReader() + else: + wx.MessageBox(_("File format not reconized by InVesalius"), _("Import surface error")) + return + else: + filename = os.path.join(const.OBJ_DIR, "magstim_fig8_coil.stl") + reader = vtk.vtkSTLReader() + + if _has_win32api: + obj_name = win32api.GetShortPathName(filename).encode(const.FS_ENCODE) + else: + obj_name = filename.encode(const.FS_ENCODE) + + reader.SetFileName(obj_name) + reader.Update() + obj_polydata = reader.GetOutput() + + if obj_polydata.GetNumberOfPoints() == 0: + wx.MessageBox(_("InVesalius was not able to import this surface"), _("Import surface error")) + obj_polydata = None + + return obj_polydata + + def AddObjectActor(self, obj_name): + """ + Coil for navigation rendered in volume viewer. + """ + + obj_polydata = self.CreateObjectPolyData(obj_name) + + transform = vtk.vtkTransform() + transform.RotateZ(90) + + transform_filt = vtk.vtkTransformPolyDataFilter() + transform_filt.SetTransform(transform) + transform_filt.SetInputData(obj_polydata) + transform_filt.Update() + + normals = vtk.vtkPolyDataNormals() + normals.SetInputData(transform_filt.GetOutput()) + normals.SetFeatureAngle(80) + normals.AutoOrientNormalsOn() + normals.Update() + + obj_mapper = vtk.vtkPolyDataMapper() + obj_mapper.SetInputData(normals.GetOutput()) + obj_mapper.ScalarVisibilityOff() + obj_mapper.ImmediateModeRenderingOn() # improve performance + + self.obj_actor = vtk.vtkActor() + self.obj_actor.SetMapper(obj_mapper) + self.obj_actor.GetProperty().SetOpacity(0.9) + self.obj_actor.SetVisibility(0) + + self.ren.AddActor(self.obj_actor) + + # self.obj_axes = vtk.vtkAxesActor() + # self.obj_axes.SetShaftTypeToCylinder() + # self.obj_axes.SetXAxisLabelText("x") + # self.obj_axes.SetYAxisLabelText("y") + # self.obj_axes.SetZAxisLabelText("z") + # self.obj_axes.SetTotalLength(50.0, 50.0, 50.0) + + # self.ren.AddActor(self.obj_axes) + + def OnNavigationStatus(self, status): + self.nav_status = status + self.pTarget = self.CenterOfMass() + if self.obj_actor and self.nav_status: + self.obj_actor.SetVisibility(self.obj_state) + if not self.obj_state: + self.Refresh() + + def UpdateObjectOrientation(self, m_img, coord): + m_img[:3, -1] = np.asmatrix(bases.flip_x_m((m_img[0, -1], m_img[1, -1], m_img[2, -1]))).reshape([3, 1]) + + m_img_vtk = vtk.vtkMatrix4x4() + + for row in range(0, 4): + for col in range(0, 4): + m_img_vtk.SetElement(row, col, m_img[row, col]) + + self.obj_actor.SetUserMatrix(m_img_vtk) + # self.obj_axes.SetUserMatrix(m_rot_vtk) + + self.Refresh() + + def UpdateTrackObjectState(self, evt=None, flag=None, obj_name=None): + if flag: + self.obj_name = obj_name + if not self.obj_actor: + self.AddObjectActor(self.obj_name) + else: + if self.obj_actor: + self.ren.RemoveActor(self.obj_actor) + self.obj_actor = None + self.Refresh() + + def UpdateShowObjectState(self, state): + self.obj_state = state + if self.obj_actor and not self.obj_state: + self.obj_actor.SetVisibility(self.obj_state) + self.Refresh() + def __bind_events_wx(self): #self.Bind(wx.EVT_SIZE, self.OnSize) pass @@ -613,7 +1244,7 @@ "LeftButtonReleaseEvent": self.OnReleaseSpinClick, }, const.STATE_WL: - { + { "MouseMoveEvent": self.OnWindowLevelMove, "LeftButtonPressEvent": self.OnWindowLevelClick, "LeftButtonReleaseEvent":self.OnWindowLevelRelease @@ -661,7 +1292,7 @@ else: style = vtk.vtkInteractorStyleTrackballCamera() self.interactor.SetInteractorStyle(style) - self.style = style + self.style = style # Check each event available for each mode for event in action[state]: @@ -716,8 +1347,8 @@ diff_y = mouse_y - self.last_y self.last_x, self.last_y = mouse_x, mouse_y Publisher.sendMessage('Set raycasting relative window and level', - (diff_x, diff_y)) - Publisher.sendMessage('Refresh raycasting widget points', None) + diff_wl=diff_x, diff_ww=diff_y) + Publisher.sendMessage('Refresh raycasting widget points') self.interactor.Render() def OnWindowLevelClick(self, obj, evt): @@ -732,31 +1363,29 @@ if const.RAYCASTING_WWWL_BLUR: self.style.EndZoom() - def OnEnableStyle(self, pubsub_evt): - state = pubsub_evt.data - if (state in const.VOLUME_STYLES): - new_state = self.interaction_style.AddState(state) + def OnEnableStyle(self, style): + if (style in const.VOLUME_STYLES): + new_state = self.interaction_style.AddState(style) self.SetInteractorStyle(new_state) else: - new_state = self.interaction_style.RemoveState(state) + new_state = self.interaction_style.RemoveState(style) self.SetInteractorStyle(new_state) - def OnDisableStyle(self, pubsub_evt): - state = pubsub_evt.data - new_state = self.interaction_style.RemoveState(state) + def OnDisableStyle(self, style): + new_state = self.interaction_style.RemoveState(style) self.SetInteractorStyle(new_state) - def ResetCamClippingRange(self, pubsub_evt): + def ResetCamClippingRange(self): self.ren.ResetCamera() self.ren.ResetCameraClippingRange() - def SetVolumeCameraState(self, pubsub_evt): - self.camera_state = pubsub_evt.data + def SetVolumeCameraState(self, camera_state): + self.camera_state = camera_state - def SetVolumeCamera(self, pubsub_evt): + def SetVolumeCamera(self, arg, position): if self.camera_state: - #TODO: exclude dependency on initial focus - cam_focus = np.array(bases.flip_x(pubsub_evt.data)) + # TODO: exclude dependency on initial focus + cam_focus = np.array(bases.flip_x(position[:3])) cam = self.ren.GetActiveCamera() if self.initial_focus is None: @@ -764,11 +1393,14 @@ cam_pos0 = np.array(cam.GetPosition()) cam_focus0 = np.array(cam.GetFocalPoint()) - v0 = cam_pos0 - cam_focus0 v0n = np.sqrt(inner1d(v0, v0)) - v1 = (cam_focus - self.initial_focus) + if self.obj_state: + v1 = (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 @@ -784,8 +1416,7 @@ #self.interactor.Render() self.Refresh() - def OnExportSurface(self, pubsub_evt): - filename, filetype = pubsub_evt.data + def OnExportSurface(self, filename, filetype): if filetype not in (const.FILETYPE_STL, const.FILETYPE_VTP, const.FILETYPE_PLY, @@ -793,9 +1424,9 @@ if _has_win32api: utils.touch(filename) win_filename = win32api.GetShortPathName(filename) - self._export_surface(win_filename.encode(const.FS_ENCODE), filetype) + self._export_surface(win_filename, filetype) else: - self._export_surface(filename.encode(const.FS_ENCODE), filetype) + self._export_surface(filename, filetype) def _export_surface(self, filename, filetype): fileprefix = filename.split(".")[-2] @@ -829,23 +1460,22 @@ writer.SetInput(renwin) writer.Write() - def OnEnableBrightContrast(self, pubsub_evt): + def OnEnableBrightContrast(self): style = self.style style.AddObserver("MouseMoveEvent", self.OnMove) style.AddObserver("LeftButtonPressEvent", self.OnClick) style.AddObserver("LeftButtonReleaseEvent", self.OnRelease) - def OnDisableBrightContrast(self, pubsub_evt): + def OnDisableBrightContrast(self): style = vtk.vtkInteractorStyleTrackballCamera() self.interactor.SetInteractorStyle(style) self.style = style - def OnSetWindowLevelText(self, pubsub_evt): + def OnSetWindowLevelText(self, ww, wl): if self.raycasting_volume: - ww, wl = pubsub_evt.data self.text.SetValue("WL: %d WW: %d"%(wl, ww)) - def OnShowRaycasting(self, pubsub_evt): + def OnShowRaycasting(self): if not self.raycasting_volume: self.raycasting_volume = True self._to_show_ball += 1 @@ -853,7 +1483,7 @@ if self.on_wl: self.text.Show() - def OnHideRaycasting(self, pubsub_evt): + def OnHideRaycasting(self): self.raycasting_volume = False self.text.Hide() self._to_show_ball -= 1 @@ -866,13 +1496,11 @@ self.interactor.Update() evt.Skip() - def ChangeBackgroundColour(self, pubsub_evt): - colour = pubsub_evt.data - self.ren.SetBackground(colour) + def ChangeBackgroundColour(self, colour): + self.ren.SetBackground(colour[:3]) self.UpdateRender() - def LoadActor(self, pubsub_evt): - actor = pubsub_evt.data + def LoadActor(self, actor): self.added_actor = 1 ren = self.ren ren.AddActor(actor) @@ -889,31 +1517,26 @@ self._to_show_ball += 1 self._check_and_set_ball_visibility() - def RemoveActor(self, pubsub_evt): + def RemoveActor(self, actor): utils.debug("RemoveActor") - actor = pubsub_evt.data ren = self.ren ren.RemoveActor(actor) self.interactor.Render() self._to_show_ball -= 1 self._check_and_set_ball_visibility() - def RemoveAllActor(self, pubsub_evt): + def RemoveAllActor(self): utils.debug("RemoveAllActor") self.ren.RemoveAllProps() Publisher.sendMessage('Render volume viewer') - def LoadSlicePlane(self, pubsub_evt): + def LoadSlicePlane(self): self.slice_plane = SlicePlane() - def LoadVolume(self, pubsub_evt): + def LoadVolume(self, volume, colour, ww, wl): self.raycasting_volume = True self._to_show_ball += 1 - volume = pubsub_evt.data[0] - colour = pubsub_evt.data[1] - ww, wl = pubsub_evt.data[2] - self.light = self.ren.GetLights().GetNextItem() self.ren.AddVolume(volume) @@ -935,16 +1558,14 @@ self._check_and_set_ball_visibility() self.UpdateRender() - def UnloadVolume(self, pubsub_evt): - volume = pubsub_evt.data + def UnloadVolume(self, volume): self.ren.RemoveVolume(volume) del volume self.raycasting_volume = False self._to_show_ball -= 1 self._check_and_set_ball_visibility() - def OnSetViewAngle(self, evt_pubsub): - view = evt_pubsub.data + def OnSetViewAngle(self, view): self.SetViewAngle(view) def SetViewAngle(self, view): @@ -960,7 +1581,7 @@ cam.SetViewUp(xv,yv,zv) cam.SetPosition(xp,yp,zp) - self.ren.ResetCameraClippingRange() + self.ren.ResetCameraClippingRange() self.ren.ResetCamera() self.interactor.Render() @@ -999,14 +1620,14 @@ orientation_widget.On() orientation_widget.InteractiveOff() - def UpdateRender(self, evt_pubsub=None): + def UpdateRender(self): self.interactor.Render() - def SetWidgetInteractor(self, evt_pubsub=None): - evt_pubsub.data.SetInteractor(self.interactor._Iren) + def SetWidgetInteractor(self, widget=None): + widget.SetInteractor(self.interactor._Iren) - def AppendActor(self, evt_pubsub=None): - self.ren.AddActor(evt_pubsub.data) + def AppendActor(self, actor): + self.ren.AddActor(actor) def OnInsertSeed(self, obj, evt): x,y = self.interactor.GetEventPosition() @@ -1020,10 +1641,10 @@ x,y = self.interactor.GetEventPosition() self.measure_picker.Pick(x, y, 0, self.ren) x, y, z = self.measure_picker.GetPickPosition() - + proj = prj.Project() radius = min(proj.spacing) * PROP_MEASURE - if self.measure_picker.GetActor(): + if self.measure_picker.GetActor(): # if not self.measures or self.measures[-1].IsComplete(): # m = measures.LinearMeasure(self.ren) # m.AddPoint(x, y, z) @@ -1032,10 +1653,13 @@ # m = self.measures[-1] # m.AddPoint(x, y, z) # if m.IsComplete(): - # Publisher.sendMessage("Add measure to list", + # Publisher.sendMessage("Add measure to list", # (u"3D", _(u"%.3f mm" % m.GetValue()))) Publisher.sendMessage("Add measurement point", - ((x, y,z), const.LINEAR, const.SURFACE, radius)) + position=(x, y,z), + type=const.LINEAR, + location=const.SURFACE, + radius=radius) self.interactor.Render() def OnInsertAngularMeasurePoint(self, obj, evt): @@ -1045,7 +1669,7 @@ proj = prj.Project() radius = min(proj.spacing) * PROP_MEASURE - if self.measure_picker.GetActor(): + if self.measure_picker.GetActor(): # if not self.measures or self.measures[-1].IsComplete(): # m = measures.AngularMeasure(self.ren) # m.AddPoint(x, y, z) @@ -1066,21 +1690,23 @@ # type_, location, # value)) Publisher.sendMessage("Add measurement point", - ((x, y,z), const.ANGULAR, const.SURFACE, radius)) + position=(x, y,z), + type=const.ANGULAR, + location=const.SURFACE, + radius=radius) self.interactor.Render() - def Reposition3DPlane(self, evt_pubsub): - position = evt_pubsub.data + def Reposition3DPlane(self, plane_label): if not(self.added_actor) and not(self.raycasting_volume): - if not(self.repositioned_axial_plan) and (position == '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 (position == '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 (position == 'Coronal'): + elif not(self.repositioned_coronal_plan) and (plane_label == 'Coronal'): self.SetViewAngle(const.VOL_ISO) self.repositioned_coronal_plan = 1 @@ -1167,39 +1793,36 @@ selected_prop2 = plane_y.GetSelectedPlaneProperty() selected_prop2.SetColor(0, 1, 0) - Publisher.sendMessage('Set Widget Interactor', plane_x) - Publisher.sendMessage('Set Widget Interactor', plane_y) - Publisher.sendMessage('Set Widget Interactor', 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) self.Render() - def Enable(self, evt_pubsub=None): - if (evt_pubsub): - label = evt_pubsub.data - if(label == "Axial"): + def Enable(self, plane_label=None): + if plane_label: + if(plane_label == "Axial"): self.plane_z.On() - elif(label == "Coronal"): + elif(plane_label == "Coronal"): self.plane_y.On() - elif(label == "Sagital"): + elif(plane_label == "Sagital"): self.plane_x.On() - - Publisher.sendMessage('Reposition 3D 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', const.VOL_ISO) + Publisher.sendMessage('Set volume view angle', + view=const.VOL_ISO) self.Render() - def Disable(self, evt_pubsub=None): - if (evt_pubsub): - label = evt_pubsub.data - if(label == "Axial"): + def Disable(self, plane_label=None): + if plane_label: + if(plane_label == "Axial"): self.plane_z.Off() - elif(label == "Coronal"): + elif(plane_label == "Coronal"): self.plane_y.Off() - elif(label == "Sagital"): + elif(plane_label == "Sagital"): self.plane_x.Off() else: self.plane_z.Off() @@ -1211,23 +1834,33 @@ def Render(self): Publisher.sendMessage('Render volume viewer') - def ChangeSlice(self, pubsub_evt = None): - orientation, number = pubsub_evt.data - + def ChangeSlice(self, orientation, index): if orientation == "CORONAL" and self.plane_y.GetEnabled(): - Publisher.sendMessage('Update slice 3D', (self.plane_y,orientation)) + Publisher.sendMessage('Update slice 3D', + widget=self.plane_y, + orientation=orientation) self.Render() elif orientation == "SAGITAL" and self.plane_x.GetEnabled(): - Publisher.sendMessage('Update slice 3D', (self.plane_x,orientation)) + Publisher.sendMessage('Update slice 3D', + widget=self.plane_x, + orientation=orientation) self.Render() elif orientation == 'AXIAL' and self.plane_z.GetEnabled() : - Publisher.sendMessage('Update slice 3D', (self.plane_z,orientation)) + Publisher.sendMessage('Update slice 3D', + widget=self.plane_z, + orientation=orientation) self.Render() - def UpdateAllSlice(self, pubsub_evt): - Publisher.sendMessage('Update slice 3D', (self.plane_y,"CORONAL")) - Publisher.sendMessage('Update slice 3D', (self.plane_x,"SAGITAL")) - Publisher.sendMessage('Update slice 3D', (self.plane_z,"AXIAL")) + 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") def DeletePlanes(self): diff -Nru invesalius-3.1.1/invesalius/data/volume.py invesalius-3.1.99991/invesalius/data/volume.py --- invesalius-3.1.1/invesalius/data/volume.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/volume.py 2018-08-17 16:41:39.000000000 +0000 @@ -110,17 +110,17 @@ Publisher.subscribe(self.ChangeBackgroundColour, 'Change volume viewer background colour') - Publisher.subscribe(self.ResetRayCasting, 'Reset Reaycasting') + Publisher.subscribe(self.ResetRayCasting, 'Reset Raycasting') Publisher.subscribe(self.OnFlipVolume, 'Flip volume') - def ResetRayCasting(self, pub_evt): + def ResetRayCasting(self): if self.exist: self.exist = None self.LoadVolume() - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.CloseProject() def CloseProject(self): @@ -134,9 +134,9 @@ if self.exist: self.exist = None - Publisher.sendMessage('Remove surface actor from viewer', self.volume) + Publisher.sendMessage('Remove surface actor from viewer', actor=self.volume) Publisher.sendMessage('Disable volume cut menu') - Publisher.sendMessage('Unload volume', self.volume) + Publisher.sendMessage('Unload volume', volume=self.volume) del self.image del self.imagedata @@ -155,40 +155,41 @@ self.color_transfer = None Publisher.sendMessage('Render volume viewer') - def OnLoadVolume(self, pubsub_evt): - label = pubsub_evt.data + def OnLoadVolume(self, label): + label = label #self.LoadConfig(label) self.LoadVolume() - def OnHideVolume(self, pubsub_evt): - print 'Hide Volume' + def OnHideVolume(self): + print('Hide Volume') self.volume.SetVisibility(0) if (self.plane and self.plane_on): self.plane.Disable() Publisher.sendMessage('Render volume viewer') - def OnShowVolume(self, pubsub_evt = None): - print 'Show volume' + def OnShowVolume(self): + print('Show volume') if self.exist: - print 'Volume exists' + print('Volume exists') self.volume.SetVisibility(1) if (self.plane and self.plane_on): self.plane.Enable() Publisher.sendMessage('Render volume viewer') else: - print 'Volume doesnt exit' - Publisher.sendMessage('Load raycasting preset', const.RAYCASTING_LABEL) + print('Volume doesnt exit') + Publisher.sendMessage('Load raycasting preset', + preset_name=const.RAYCASTING_LABEL) self.LoadConfig() self.LoadVolume() self.exist = 1 - def OnUpdatePreset(self, pubsub_evt): + def OnUpdatePreset(self): self.__load_preset_config() if self.config: if self.to_reload: self.exist = False - Publisher.sendMessage('Unload volume', self.volume) + Publisher.sendMessage('Unload volume', volume=self.volume) if self.exist: self.__load_preset() @@ -200,10 +201,10 @@ self.exist = 1 colour = self.GetBackgroundColour() - Publisher.sendMessage('Change volume viewer background colour', colour) - Publisher.sendMessage('Change volume viewer gui colour', colour) + Publisher.sendMessage('Change volume viewer background colour', colour=colour) + Publisher.sendMessage('Change volume viewer gui colour', colour=colour) else: - Publisher.sendMessage('Unload volume', self.volume) + Publisher.sendMessage('Unload volume', volume=self.volume) del self.image del self.imagedata del self.final_imagedata @@ -221,8 +222,8 @@ self.color_transfer = None Publisher.sendMessage('Render volume viewer') - def OnFlipVolume(self, pubsub_evt): - print "Flipping Volume" + def OnFlipVolume(self, axis): + print("Flipping Volume") self.loaded_image = False del self.image self.image = None @@ -252,28 +253,24 @@ self.SetShading() self.SetTypeRaycasting() - def OnSetCurve(self, pubsub_evt): - self.curve = pubsub_evt.data + def OnSetCurve(self, curve): + self.curve = curve self.CalculateWWWL() ww = self.ww wl = self.wl - Publisher.sendMessage('Set volume window and level text', - (ww, wl)) + Publisher.sendMessage('Set volume window and level text', ww=ww, wl=wl) - def OnSetRelativeWindowLevel(self, pubsub_evt): - diff_wl, diff_ww = pubsub_evt.data + def OnSetRelativeWindowLevel(self, diff_wl, diff_ww): ww = self.ww + diff_ww wl = self.wl + diff_wl - Publisher.sendMessage('Set volume window and level text', - (ww, wl)) + Publisher.sendMessage('Set volume window and level text', ww=ww, wl=wl) self.SetWWWL(ww, wl) self.ww = ww self.wl = wl - def OnSetWindowLevel(self, pubsub_evt): - ww, wl, n = pubsub_evt.data - self.curve = n - self.SetWWWL(ww,wl) + def OnSetWindowLevel(self, ww, wl, curve): + self.curve = curve + self.SetWWWL(ww, wl) def SetWWWL(self, ww, wl): if self.config['advancedCLUT']: @@ -321,7 +318,7 @@ self.ww = last_point - first_point self.wl = first_point + self.ww / 2.0 - def Refresh(self, pubsub_evt): + def Refresh(self): self.__update_colour_table() def Create16bColorTable(self, scale): @@ -360,10 +357,10 @@ r = p['Red'] g = p['Green'] b = p['Blue'] - colors = zip(r,g,b) + colors = list(zip(r,g,b)) else: # Grayscale from black to white - colors = [(i, i, i) for i in xrange(256)] + colors = [(i, i, i) for i in range(256)] ww = self.config['ww'] wl = self.TranslateScale(scale, self.config['wl']) @@ -445,11 +442,11 @@ self.config['backgroundColorBlueComponent']) return colour - def ChangeBackgroundColour(self, pubsub_evt): + def ChangeBackgroundColour(self, colour): if (self.config): - self.config['backgroundColorRedComponent'] = pubsub_evt.data[0] * 255 - self.config['backgroundColorGreenComponent'] = pubsub_evt.data[1] * 255 - self.config['backgroundColorBlueComponent'] = pubsub_evt.data[2] * 255 + self.config['backgroundColorRedComponent'] = colour[0] * 255 + self.config['backgroundColorGreenComponent'] = colour[1] * 255 + self.config['backgroundColorBlueComponent'] = colour[2] * 255 def BuildTable(): curve_table = p['16bitClutCurves'] @@ -663,16 +660,16 @@ self.plane.SetVolumeMapper(volume_mapper) Publisher.sendMessage('Load volume into viewer', - (volume, colour, (self.ww, self.wl))) + volume=volume, colour=colour, + ww=self.ww, wl=self.wl) del flip del cast - def OnEnableTool(self, pubsub_evt): - tool_name, enable = pubsub_evt.data + def OnEnableTool(self, tool_name, flag): if tool_name == _("Cut plane"): if self.plane: - if enable: + if flag: self.plane_on = True self.plane.Enable() else: @@ -695,8 +692,10 @@ accumulate.Update() n_image = numpy_support.vtk_to_numpy(accumulate.GetOutput().GetPointData().GetScalars()) del accumulate - Publisher.sendMessage('Load histogram', (n_image, - image.GetScalarRange())) + init, end = image.GetScalarRange() + Publisher.sendMessage('Load histogram', histogram=n_image, init=init, end=end) + + def TranslateScale(self, scale, value): #if value < 0: @@ -747,8 +746,8 @@ plane_actor.GetProperty().BackfaceCullingOn() plane_actor.GetProperty().SetOpacity(0) plane_widget.AddObserver("InteractionEvent", self.Update) - Publisher.sendMessage('AppendActor', self.plane_actor) - Publisher.sendMessage('Set Widget Interactor', self.plane_widget) + Publisher.sendMessage('AppendActor', actor=self.plane_actor) + Publisher.sendMessage('Set Widget Interactor', widget=self.plane_widget) plane_actor.SetVisibility(1) plane_widget.On() self.plane = plane = vtk.vtkPlane() @@ -775,21 +774,21 @@ self.plane_actor.VisibilityOn() self.plane.SetNormal(plane_source.GetNormal()) self.plane.SetOrigin(plane_source.GetOrigin()) - Publisher.sendMessage('Render volume viewer', None) + Publisher.sendMessage('Render volume viewer') - def Enable(self, evt_pubsub=None): + def Enable(self): self.plane_widget.On() self.plane_actor.VisibilityOn() self.volume_mapper.AddClippingPlane(self.plane) - Publisher.sendMessage('Render volume viewer', None) + Publisher.sendMessage('Render volume viewer') - def Disable(self,evt_pubsub=None): + def Disable(self): self.plane_widget.Off() self.plane_actor.VisibilityOff() self.volume_mapper.RemoveClippingPlane(self.plane) - Publisher.sendMessage('Render volume viewer', None) + Publisher.sendMessage('Render volume viewer') - def Reset(self, evt_pubsub=None): + def Reset(self): plane_source = self.plane_source plane_widget = self.plane_widget plane_source.SetOrigin(self.origin) @@ -799,10 +798,10 @@ self.plane_actor.VisibilityOn() self.plane.SetNormal(self.normal) self.plane.SetOrigin(self.origin) - Publisher.sendMessage('Render volume viewer', None) + Publisher.sendMessage('Render volume viewer') def DestroyObjs(self): - Publisher.sendMessage('Remove surface actor from viewer', self.plane_actor) + Publisher.sendMessage('Remove surface actor from viewer', actor=self.plane_actor) self.Disable() del self.plane_widget del self.plane_source diff -Nru invesalius-3.1.1/invesalius/data/vtk_utils.py invesalius-3.1.99991/invesalius/data/vtk_utils.py --- invesalius-3.1.1/invesalius/data/vtk_utils.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/data/vtk_utils.py 2018-08-17 16:41:39.000000000 +0000 @@ -44,11 +44,16 @@ progress = [0] last_obj_progress = [0] if (dialog_type == "ProgressDialog"): - dlg = ProgressDialog(100) + try: + dlg = ProgressDialog(100) + except wx._core.PyNoAppError: + return lambda obj, label: 0 # when the pipeline is larger than 1, we have to consider this object # percentage + if number_of_filters < 1: + number_of_filters = 1 ratio = (100.0 / number_of_filters) def UpdateProgress(obj, label=""): @@ -76,8 +81,7 @@ progress[0] = progress[0] + ratio*difference # Tell GUI to update progress status value if (dialog_type == "GaugeProgress"): - Publisher.sendMessage('Update status in GUI', - (progress[0], label)) + Publisher.sendMessage('Update status in GUI', value=progress[0], label=label) else: if (progress[0] >= 99.999): progress[0] = 100 @@ -121,6 +125,9 @@ def ShadowOff(self): self.property.ShadowOff() + def BoldOn(self): + self.property.BoldOn() + def SetSize(self, size): self.property.SetFontSize(size) @@ -132,11 +139,27 @@ # With some encoding in some dicom fields (like name) raises a # UnicodeEncodeError because they have non-ascii characters. To avoid # that we encode in utf-8. - - if sys.platform == 'win32': - self.mapper.SetInput(value.encode("utf-8")) + if sys.platform == 'win32': + self.mapper.SetInput(value.encode("utf-8")) + else: + try: + self.mapper.SetInput(value.encode("latin-1")) + except(UnicodeEncodeError): + self.mapper.SetInput(value.encode("utf-8")) + + def SetCoilDistanceValue(self, value): + if isinstance(value, int) or isinstance(value, float): + value = 'Dist: ' + str("{:06.2f}".format(value)) + ' mm' + if sys.platform == 'win32': + value += "" # Otherwise 0 is not shown under win32 + # With some encoding in some dicom fields (like name) raises a + # UnicodeEncodeError because they have non-ascii characters. To avoid + # that we encode in utf-8. + + if sys.platform == 'win32': + self.mapper.SetInput(value.encode("utf-8")) else: - try: + try: self.mapper.SetInput(value.encode("latin-1")) except(UnicodeEncodeError): self.mapper.SetInput(value.encode("utf-8")) diff -Nru invesalius-3.1.1/invesalius/gui/bitmap_preview_panel.py invesalius-3.1.99991/invesalius/gui/bitmap_preview_panel.py --- invesalius-3.1.1/invesalius/gui/bitmap_preview_panel.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/bitmap_preview_panel.py 2018-08-17 16:41:39.000000000 +0000 @@ -218,7 +218,10 @@ def OnEnter(self, evt): if not self.select_on: - c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) + try: + c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) + except AttributeError: + c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) self.SetBackgroundColour(c) def OnLeave(self, evt): @@ -229,7 +232,7 @@ def OnSelect(self, evt): shift_pressed = False - if evt.m_shiftDown: + if evt.shiftDown: shift_pressed = True self.select_on = True @@ -246,7 +249,7 @@ my_evt.SetEventObject(self) self.GetEventHandler().ProcessEvent(my_evt) - Publisher.sendMessage('Set bitmap in preview panel', self.bitmap_info.pos) + Publisher.sendMessage('Set bitmap in preview panel', pos=self.bitmap_info.pos) evt.Skip() @@ -257,7 +260,10 @@ def Select(self, on=True): if self.select_on: - c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) + try: + c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) + except AttributeError: + c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) else: c = (PREVIEW_BACKGROUND) self.SetBackgroundColour(c) @@ -291,10 +297,10 @@ self.grid = wx.GridSizer(rows=NROWS, cols=NCOLS, vgap=3, hgap=3) sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.AddSizer(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) + sizer.Add(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) background_sizer = wx.BoxSizer(wx.HORIZONTAL) - background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) + background_sizer.Add(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) self.SetSizer(background_sizer) background_sizer.Fit(self) @@ -311,8 +317,8 @@ def _Add_Panels_Preview(self): self.previews = [] - for i in xrange(NROWS): - for j in xrange(NCOLS): + for i in range(NROWS): + for j in range(NCOLS): p = Preview(self) p.Bind(EVT_PREVIEW_CLICK, self.OnSelect) @@ -363,15 +369,14 @@ self._display_previews() - def RemovePanel(self, pub_sub): - data = pub_sub.data + def RemovePanel(self, data): for p, f in zip(self.previews, self.files): if p.bitmap_info != None: if data.encode('utf-8') in p.bitmap_info.data: self.files.remove(f) p.Hide() self._display_previews() - Publisher.sendMessage('Update max of slidebar in single preview image', len(self.files)) + Publisher.sendMessage('Update max of slidebar in single preview image', max_value=len(self.files)) self.Update() self.Layout() @@ -387,7 +392,7 @@ initial = self.displayed_position * NCOLS final = initial + NUM_PREVIEWS if len(self.files) < final: - for i in xrange(final-len(self.files)): + for i in range(final-len(self.files)): try: self.previews[-i-1].Hide() except IndexError: @@ -396,7 +401,7 @@ self.nhidden_last_display = final-len(self.files) else: if self.nhidden_last_display: - for i in xrange(self.nhidden_last_display): + for i in range(self.nhidden_last_display): try: self.previews[-i-1].Show() except IndexError: @@ -504,7 +509,7 @@ maxValue=99, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS) slider.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) - slider.SetTickFreq(1, 1) + slider.SetTickFreq(1) self.slider = slider checkbox = wx.CheckBox(self, -1, _("Auto-play")) @@ -533,8 +538,7 @@ Publisher.subscribe(self.UpdateMaxValueSliderBar, 'Update max of slidebar in single preview image') Publisher.subscribe(self.ShowBlackSlice, 'Show black slice in single preview image') - def ShowBitmapByPosition(self, evt): - pos = evt.data + def ShowBitmapByPosition(self, pos): if pos != None: self.ShowSlice(int(pos)) @@ -578,8 +582,8 @@ self.slider.SetValue(0) self.ShowSlice() - def UpdateMaxValueSliderBar(self, pub_sub): - self.slider.SetMax(pub_sub.data - 1) + def UpdateMaxValueSliderBar(self, max_value): + self.slider.SetMax(max_value - 1) self.slider.Refresh() self.slider.Update() diff -Nru invesalius-3.1.1/invesalius/gui/data_notebook.py invesalius-3.1.99991/invesalius/gui/data_notebook.py --- invesalius-3.1.1/invesalius/gui/data_notebook.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/data_notebook.py 2018-08-17 16:41:39.000000000 +0000 @@ -29,7 +29,11 @@ import wx import wx.grid -import wx.lib.flatnotebook as fnb +try: + import wx.lib.agw.flatnotebook as fnb +except ImportError: + import wx.lib.flatnotebook as fnb + import wx.lib.platebtn as pbtn from wx.lib.pubsub import pub as Publisher @@ -37,10 +41,11 @@ import invesalius.data.slice_ as slice_ import invesalius.gui.dialogs as dlg import invesalius.gui.widgets.listctrl as listmix +# import wx.lib.mixins.listctrl as listmix import invesalius.utils as ul -BTN_NEW, BTN_REMOVE, BTN_DUPLICATE, BTN_OPEN = [wx.NewId() for i in xrange(4)] +BTN_NEW, BTN_REMOVE, BTN_DUPLICATE, BTN_OPEN = [wx.NewId() for i in range(4)] TYPE = {const.LINEAR: _(u"Linear"), const.ANGULAR: _(u"Angular"), @@ -93,20 +98,20 @@ 'Fold mask page') - def _FoldSurface(self, pubusb_evt): + def _FoldSurface(self): """ Fold surface notebook page. """ self.book.SetSelection(1) - def _FoldMeasure(self, pubsub_evt): + def _FoldMeasure(self): """ Fold measure notebook page. """ self.book.SetSelection(2) - def _FoldMask(self, pubsub_evt): + def _FoldMask(self): """ Fold mask notebook page. """ @@ -329,7 +334,8 @@ mask_name, thresh, colour = dialog.GetValue() if mask_name: Publisher.sendMessage('Create new mask', - (mask_name, thresh, colour)) + mask_name=mask_name, + thresh=thresh, colour=colour) dialog.Destroy() def OnRemove(self): @@ -338,7 +344,7 @@ def OnDuplicate(self): selected_items = self.parent.listctrl.GetSelected() if selected_items: - Publisher.sendMessage('Duplicate masks', selected_items) + Publisher.sendMessage('Duplicate masks', mask_indexes=selected_items) else: dlg.MaskSelectionRequiredForDuplication() @@ -393,7 +399,7 @@ selected_items = self.GetSelected() if selected_items: - Publisher.sendMessage('Remove masks', selected_items) + Publisher.sendMessage('Remove masks', mask_indexes=selected_items) else: dlg.MaskSelectionRequiredForRemoval() return @@ -416,8 +422,8 @@ if new_dict: if index == self.current_index: self.SetItemImage(0, 1) - Publisher.sendMessage('Change mask selected', 0) - Publisher.sendMessage('Show mask', (0, 1)) + Publisher.sendMessage('Change mask selected', index=0) + Publisher.sendMessage('Show mask', index=0, value=1) Publisher.sendMessage('Refresh viewer') for key in new_dict: if key: @@ -428,27 +434,28 @@ self.SetItemImage(self.current_index, 1) - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.DeleteAllItems() self.mask_list_index = {} - def OnChangeCurrentMask(self, pubsub_evt): - mask_index = pubsub_evt.data + def OnChangeCurrentMask(self, index): try: - self.SetItemImage(mask_index, 1) - self.current_index = mask_index + self.SetItemImage(index, 1) + self.current_index = index except wx._core.PyAssertionError: #in SetItem(): invalid item index in SetItem pass for key in self.mask_list_index.keys(): - if key != mask_index: + if key != index: self.SetItemImage(key, 0) - def __hide_current_mask(self, pubsub_evt): - self.SetItemImage(self.current_index, 0) - - def __show_current_mask(self, pubsub_evt): - self.SetItemImage(self.current_index, 1) + def __hide_current_mask(self): + if self.mask_list_index: + self.SetItemImage(self.current_index, 0) + + def __show_current_mask(self): + if self.mask_list_index: + self.SetItemImage(self.current_index, 1) def __init_columns(self): @@ -481,20 +488,20 @@ def OnEditLabel(self, evt): Publisher.sendMessage('Change mask name', - (evt.GetIndex(), evt.GetLabel())) + index=evt.GetIndex(), name=evt.GetLabel()) evt.Skip() def OnItemActivated(self, evt): - self.ToggleItem(evt.m_itemIndex) + self.ToggleItem(evt.Index) def OnCheckItem(self, index, flag): if flag: for key in self.mask_list_index.keys(): if key != index: self.SetItemImage(key, 0) - Publisher.sendMessage('Change mask selected',index) + Publisher.sendMessage('Change mask selected', index=index) self.current_index = index - Publisher.sendMessage('Show mask', (index, flag)) + Publisher.sendMessage('Show mask', index=index, value=flag) def CreateColourBitmap(self, colour): """ @@ -503,8 +510,8 @@ """ image = self.image_gray new_image = Image.new("RGB", image.size) - for x in xrange(image.size[0]): - for y in xrange(image.size[1]): + for x in range(image.size[0]): + for y in range(image.size[1]): pixel_colour = [int(i*image.getpixel((x,y))) for i in colour] new_image.putpixel((x,y), tuple(pixel_colour)) @@ -529,19 +536,16 @@ # self.SetItemImage(key, 0) # self.current_index = index - def AddMask(self, pubsub_evt): - index, mask_name, threshold_range, colour = pubsub_evt.data - image = self.CreateColourBitmap(colour) + def AddMask(self, mask): + image = self.CreateColourBitmap(mask.colour) image_index = self.imagelist.Add(image) - self.mask_list_index[index] = image_index - self.InsertNewItem(index, mask_name, str(threshold_range)) + self.mask_list_index[mask.index] = image_index + self.InsertNewItem(mask.index, mask.name, str(mask.threshold_range)) - def EditMaskThreshold(self, pubsub_evt): - index, threshold_range = pubsub_evt.data + def EditMaskThreshold(self, index, threshold_range): self.SetStringItem(index, 2, str(threshold_range)) - def EditMaskColour(self, pubsub_evt): - index, colour = pubsub_evt.data + def EditMaskColour(self, index, colour): image = self.CreateColourBitmap(colour) image_index = self.mask_list_index[index] self.imagelist.Replace(image_index, image) @@ -671,7 +675,8 @@ if ok: surface_options = dialog.GetValue() - Publisher.sendMessage('Create surface from index', surface_options) + Publisher.sendMessage('Create surface from index', + surface_parameters=surface_options) dialog.Destroy() def OnRemove(self): @@ -681,14 +686,14 @@ def OnDuplicate(self): selected_items = self.parent.listctrl.GetSelected() if selected_items: - Publisher.sendMessage('Duplicate surfaces', selected_items) + Publisher.sendMessage('Duplicate surfaces', surface_indexes=selected_items) else: dlg.SurfaceSelectionRequiredForDuplication() def OnOpenMesh(self): filename = dlg.ShowImportMeshFilesDialog() if filename: - Publisher.sendMessage('Import surface file', filename) + Publisher.sendMessage('Import surface file', filename=filename) class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): @@ -737,8 +742,7 @@ elif (keycode == wx.WXK_DELETE): self.RemoveSurfaces() - def OnHideSurface(self, pubsub_evt): - surface_dict = pubsub_evt.data + def OnHideSurface(self, surface_dict): for key in surface_dict: if not surface_dict[key].is_shown: self.SetItemImage(key, False) @@ -763,11 +767,11 @@ old_dict = new_dict self.surface_list_index = new_dict - Publisher.sendMessage('Remove surfaces', selected_items) + Publisher.sendMessage('Remove surfaces', surface_indexes=selected_items) else: dlg.SurfaceSelectionRequiredForRemoval() - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.DeleteAllItems() self.surface_list_index = {} self.surface_bmp_idx_to_name = {} @@ -777,9 +781,9 @@ # Otherwise the parent's method will be overwritten and other # things will stop working, e.g.: OnCheckItem - last_surface_index = evt.m_itemIndex + last_surface_index = evt.Index Publisher.sendMessage('Change surface selected', - last_surface_index) + surface_index=last_surface_index) evt.Skip() def GetSelected(self): @@ -830,46 +834,44 @@ self.image_gray = Image.open(os.path.join(const.ICON_DIR, "object_colour.jpg")) def OnEditLabel(self, evt): - Publisher.sendMessage('Change surface name', (evt.GetIndex(), evt.GetLabel())) + Publisher.sendMessage('Change surface name', index=evt.GetIndex(), name=evt.GetLabel()) evt.Skip() def OnItemActivated(self, evt): - self.ToggleItem(evt.m_itemIndex) + self.ToggleItem(evt.Index) evt.Skip() def OnCheckItem(self, index, flag): - Publisher.sendMessage('Show surface', (index, flag)) + Publisher.sendMessage('Show surface', index=index, visibility=flag) - def OnShowSingle(self, pubsub_evt): - index, visibility = pubsub_evt.data + def OnShowSingle(self, index, visibility): for key in self.surface_list_index.keys(): if key != index: self.SetItemImage(key, not visibility) Publisher.sendMessage('Show surface', - (key, not visibility)) + index=key, visibility=not visibility) self.SetItemImage(index, visibility) Publisher.sendMessage('Show surface', - (index, visibility)) + index=index, visibility=visibility) - def OnShowMultiple(self, pubsub_evt): - index_list, visibility = pubsub_evt.data + def OnShowMultiple(self, index_list, visibility): for key in self.surface_list_index.keys(): if key not in index_list: self.SetItemImage(key, not visibility) Publisher.sendMessage('Show surface', - (key, not visibility)) + index=key, visibility=not visibility) for index in index_list: self.SetItemImage(index, visibility) Publisher.sendMessage('Show surface', - (index, visibility)) + index=index, visibility=visibility) - def AddSurface(self, pubsub_evt): - index = pubsub_evt.data[0] - name = pubsub_evt.data[1] - colour = pubsub_evt.data[2] - volume = "%.3f"%pubsub_evt.data[3] - area = "%.3f"%pubsub_evt.data[4] - transparency = "%d%%"%(int(100*pubsub_evt.data[5])) + def AddSurface(self, surface): + index = surface.index + name = surface.name + colour = surface.colour + volume = "%.3f" % surface.volume + area = "%.3f" % surface.area + transparency = "%d%%" % (int(100*surface.transparency)) if index not in self.surface_list_index: image = self.CreateColourBitmap(colour) @@ -879,11 +881,17 @@ self.surface_list_index[index] = image_index if (index in index_list) and index_list: - self.UpdateItemInfo(index, name, volume, area, transparency, colour) + try: + self.UpdateItemInfo(index, name, volume, area, transparency, colour) + except wx._core.wxAssertionError: + self.InsertNewItem(index, name, volume, area, transparency, colour) else: self.InsertNewItem(index, name, volume, area, transparency, colour) else: - self.UpdateItemInfo(index, name, volume, area, transparency, colour) + try: + self.UpdateItemInfo(index, name, volume, area, transparency, colour) + except wx._core.wxAssertionError: + self.InsertNewItem(index, name, volume, area, transparency, colour) def InsertNewItem(self, index=0, label="Surface 1", volume="0 mm3", area="0 mm2", transparency="0%%", colour=None): @@ -897,6 +905,8 @@ def UpdateItemInfo(self, index=0, label="Surface 1", volume="0 mm3", area="0 mm2", transparency="0%%", colour=None): + print("UpdateItemInfo", index) + # TODO: Retornar esse codigo self.SetStringItem(index, 1, label, imageId = self.surface_list_index[index]) self.SetStringItem(index, 2, volume) @@ -911,8 +921,8 @@ """ image = self.image_gray new_image = Image.new("RGB", image.size) - for x in xrange(image.size[0]): - for y in xrange(image.size[1]): + for x in range(image.size[0]): + for y in range(image.size[1]): pixel_colour = [int(i*image.getpixel((x,y))) for i in colour] new_image.putpixel((x,y), tuple(pixel_colour)) @@ -926,20 +936,18 @@ return wx.BitmapFromImage(wx_image.Scale(16, 16)) - def EditSurfaceTransparency(self, pubsub_evt): + def EditSurfaceTransparency(self, surface_index, transparency): """ Set actor transparency (oposite to opacity) according to given actor index and value. """ - index, value = pubsub_evt.data - self.SetStringItem(index, 4, "%d%%"%(int(value*100))) + self.SetStringItem(surface_index, 4, "%d%%"%(int(transparency*100))) - def EditSurfaceColour(self, pubsub_evt): + def EditSurfaceColour(self, surface_index, colour): """ """ - index, colour = pubsub_evt.data image = self.CreateColourBitmap(colour) - image_index = self.surface_list_index[index] + image_index = self.surface_list_index[surface_index] self.imagelist.Replace(image_index, image) self.Refresh() @@ -992,15 +1000,14 @@ elif (keycode == wx.WXK_DELETE): self.RemoveMeasurements() - def OnRemoveGUIMeasure(self, pubsub_evt): - idx = pubsub_evt.data - self.DeleteItem(idx) + def OnRemoveGUIMeasure(self, measure_index): + self.DeleteItem(measure_index) old_dict = self._list_index new_dict = {} j = 0 for i in old_dict: - if i != idx: + if i != measure_index: new_dict[j] = old_dict[i] j+=1 self._list_index = new_dict @@ -1026,12 +1033,12 @@ new_dict[i-1] = old_dict[i] old_dict = new_dict self._list_index = new_dict - Publisher.sendMessage('Remove measurements', selected_items) + Publisher.sendMessage('Remove measurements', indexes=selected_items) else: dlg.MeasureSelectionRequiredForRemoval() - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.DeleteAllItems() self._list_index = {} self._bmp_idx_to_name = {} @@ -1041,9 +1048,9 @@ # Otherwise the parent's method will be overwritten and other # things will stop working, e.g.: OnCheckItem - last_index = evt.m_itemIndex - Publisher.sendMessage('Change measurement selected', - last_index) + last_index = evt.Index + # Publisher.sendMessage('Change measurement selected', + # last_index) evt.Skip() def GetSelected(self): @@ -1095,47 +1102,41 @@ def OnEditLabel(self, evt): - Publisher.sendMessage('Change measurement name', (evt.GetIndex(), evt.GetLabel())) + Publisher.sendMessage('Change measurement name', index=evt.GetIndex(), name=evt.GetLabel()) evt.Skip() def OnItemActivated(self, evt): - self.ToggleItem(evt.m_itemIndex) + self.ToggleItem(evt.Index) evt.Skip() def OnCheckItem(self, index, flag): - Publisher.sendMessage('Show measurement', (index, flag)) + Publisher.sendMessage('Show measurement', index=index, visibility=flag) - def OnShowSingle(self, pubsub_evt): - index, visibility = pubsub_evt.data + def OnShowSingle(self, index, visibility): for key in self._list_index.keys(): if key != index: self.SetItemImage(key, not visibility) Publisher.sendMessage('Show measurement', - (key, not visibility)) + index=key, visibility=not visibility) self.SetItemImage(index, visibility) Publisher.sendMessage('Show measurement', - (index, visibility)) + index=index, visibility=visibility) - def OnShowMultiple(self, pubsub_evt): - index_list, visibility = pubsub_evt.data + def OnShowMultiple(self, index_list, visibility): for key in self._list_index.keys(): if key not in index_list: self.SetItemImage(key, not visibility) Publisher.sendMessage('Show measurement', - (key, not visibility)) + index=key, visibility=not visibility) for index in index_list: self.SetItemImage(index, visibility) Publisher.sendMessage('Show measurement', - (index, visibility)) + index=index, visibility=visibility) - def OnLoadData(self, pubsub_evt): - try: - items_dict, spacing = pubsub_evt.data - except ValueError: - items_dict = pubsub_evt.data - for i in items_dict: - m = items_dict[i] + def OnLoadData(self, measurement_dict, spacing=(1.0, 1.0, 1.0)): + for i in measurement_dict: + m = measurement_dict[i] image = self.CreateColourBitmap(m.colour) image_index = self.imagelist.Add(image) @@ -1154,14 +1155,7 @@ if not m.visible: self.SetItemImage(i, False) - def AddItem_(self, pubsub_evt): - index = pubsub_evt.data[0] - name = pubsub_evt.data[1] - colour = pubsub_evt.data[2] - location = pubsub_evt.data[3] - type_ = pubsub_evt.data[4] - value = pubsub_evt.data[5] - + def AddItem_(self, index, name, colour, location, type_, value): if index not in self._list_index: image = self.CreateColourBitmap(colour) image_index = self.imagelist.Add(image) @@ -1170,11 +1164,17 @@ self._list_index[index] = image_index if (index in index_list) and index_list: - self.UpdateItemInfo(index, name, colour, location, type_, value) + try: + self.UpdateItemInfo(index, name, colour, location, type_, value) + except wx._core.wxAssertionError: + self.InsertNewItem(index, name, colour, location, type_, value) else: self.InsertNewItem(index, name, colour, location, type_, value) else: - self.UpdateItemInfo(index, name, colour, location, type_, value) + try: + self.UpdateItemInfo(index, name, colour, location, type_, value) + except wx._core.wxAssertionError: + self.InsertNewItem(index, name, colour, location, type_, value) @@ -1206,8 +1206,8 @@ """ image = self.image_gray new_image = Image.new("RGB", image.size) - for x in xrange(image.size[0]): - for y in xrange(image.size[1]): + for x in range(image.size[0]): + for y in range(image.size[1]): pixel_colour = [int(i*image.getpixel((x,y))) for i in colour] new_image.putpixel((x,y), tuple(pixel_colour)) @@ -1220,12 +1220,11 @@ wx_image.SetData(new_image.tobytes()) return wx.BitmapFromImage(wx_image.Scale(16, 16)) - def EditItemColour(self, pubsub_evt): + def EditItemColour(self, measure_index, colour): """ """ - index, colour = pubsub_evt.data image = self.CreateColourBitmap(colour) - image_index = self._list_index[index] + image_index = self._list_index[measure_index] self.imagelist.Replace(image_index, image) self.Refresh() @@ -1294,14 +1293,14 @@ def OnItemActivated(self, evt): - self.ToggleItem(evt.m_itemIndex) + self.ToggleItem(evt.Index) def OnCheckItem(self, index, flag): # TODO: use pubsub to communicate to models if flag: - print "checked, ", index + print("checked, ", index) else: - print "unchecked, ", index + print("unchecked, ", index) def InsertNewItem(self, index=0, name="Axial 1", type_="2d", value="bla", colour=None): diff -Nru invesalius-3.1.1/invesalius/gui/default_tasks.py invesalius-3.1.99991/invesalius/gui/default_tasks.py --- invesalius-3.1.1/invesalius/gui/default_tasks.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/default_tasks.py 2018-08-17 16:41:39.000000000 +0000 @@ -18,7 +18,10 @@ #-------------------------------------------------------------------------- import wx -import wx.lib.foldpanelbar as fpb +try: + import wx.lib.agw.foldpanelbar as fpb +except ModuleNotFoundError: + import wx.lib.foldpanelbar as fpb from wx.lib.pubsub import pub as Publisher import invesalius.constants as const @@ -35,7 +38,7 @@ def GetCollapsedIconData(): return \ -'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ +b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ \x00\x01\x8eIDAT8\x8d\xa5\x93-n\xe4@\x10\x85?g\x03\n6lh)\xc4\xd2\x12\xc3\x81\ \xd6\xa2I\x90\x154\xb9\x81\x8f1G\xc8\x11\x16\x86\xcd\xa0\x99F\xb3A\x91\xa1\ @@ -58,13 +61,13 @@ return wx.BitmapFromImage(GetCollapsedIconImage()) def GetCollapsedIconImage(): - import cStringIO - stream = cStringIO.StringIO(GetCollapsedIconData()) + from io import BytesIO + stream = BytesIO(GetCollapsedIconData()) return wx.ImageFromStream(stream) def GetExpandedIconData(): return \ -'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ +b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ \x00\x01\x9fIDAT8\x8d\x95\x93\xa1\x8e\xdc0\x14EO\xb2\xc4\xd0\xd2\x12\xb7(mI\ \xa4%V\xd1lQT4[4-\x9a\xfe\xc1\xc2|\xc6\xc2~BY\x83:A3E\xd3\xa0*\xa4\xd2\x90H!\ @@ -89,8 +92,8 @@ return wx.BitmapFromImage(GetExpandedIconImage()) def GetExpandedIconImage(): - import cStringIO - stream = cStringIO.StringIO(GetExpandedIconData()) + from io import BytesIO + stream = BytesIO(GetExpandedIconData()) return wx.ImageFromStream(stream) @@ -201,8 +204,7 @@ def __bind_events(self): Publisher.subscribe(self.OnEnableState, "Enable state project") - def OnEnableState(self, pubsub_evt): - state = pubsub_evt.data + def OnEnableState(self, state): if state: self.SetStateProjectOpen() else: @@ -239,7 +241,7 @@ self.overwrite = False session = ses.Session() - print "session mode: ", session.mode + print("session mode: ", session.mode) if int(session.mode) == const.MODE_RP: tasks = [(_("Load data"), importer.TaskPanel), (_("Select region of interest"), slice_.TaskPanel), @@ -253,7 +255,7 @@ (_("Export data"), exporter.TaskPanel), (_("Navigation system"), navigator.TaskPanel)] - for i in xrange(len(tasks)): + for i in range(len(tasks)): (name, panel) = tasks[i] # Create panel item = fold_panel.AddFoldPanel("%d. %s"%(i+1, name), @@ -302,25 +304,24 @@ Publisher.subscribe(self.OnFoldExport, 'Fold export task') Publisher.subscribe(self.SetNavigationMode, "Set navigation mode") - def OnOverwrite(self, pubsub_evt): - self.overwrite = pubsub_evt.data['options']['overwrite'] + def OnOverwrite(self, surface_parameters): + self.overwrite = surface_parameters['options']['overwrite'] - def OnFoldSurface(self, pubsub_evt): + def OnFoldSurface(self): if not self.overwrite: self.fold_panel.Expand(self.fold_panel.GetFoldPanel(2)) - def OnFoldExport(self, pubsub_evt): + def OnFoldExport(self): self.fold_panel.Expand(self.fold_panel.GetFoldPanel(3)) - def OnEnableState(self, pubsub_evt): - state = pubsub_evt.data + def OnEnableState(self, state): if state: self.SetStateProjectOpen() else: self.SetStateProjectClose() - def SetNavigationMode(self, pubsub_evt): - self.navigation_mode_status = status = pubsub_evt.data + def SetNavigationMode(self, status): + self.navigation_mode_status = status name = _("Navigation system") panel = navigator.TaskPanel if status and (self.fold_panel.GetCount()<=4): diff -Nru invesalius-3.1.1/invesalius/gui/default_viewers.py invesalius-3.1.99991/invesalius/gui/default_viewers.py --- invesalius-3.1.1/invesalius/gui/default_viewers.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/default_viewers.py 2018-08-17 16:41:39.000000000 +0000 @@ -43,6 +43,7 @@ self.__init_aui_manager() self.__bind_events_wx() + self.__bind_events() #self.__init_four_way_splitter() #self.__init_mix() @@ -124,13 +125,16 @@ self.aui_manager.Bind(wx.aui.EVT_AUI_PANE_MAXIMIZE, self.OnMaximize) self.aui_manager.Bind(wx.aui.EVT_AUI_PANE_RESTORE, self.OnRestore) + def __bind_events(self): + Publisher.subscribe(self._Exit, 'Exit') + def OnMaximize(self, evt): if evt.GetPane().name == self.s4.name: - Publisher.sendMessage('Show raycasting widget', None) + Publisher.sendMessage('Show raycasting widget') def OnRestore(self, evt): if evt.GetPane().name == self.s4.name: - Publisher.sendMessage('Hide raycasting widget', None) + Publisher.sendMessage('Hide raycasting widget') def __init_four_way_splitter(self): @@ -152,6 +156,8 @@ p4 = volume_viewer.Viewer(self) splitter.AppendWindow(p4) + def _Exit(self): + self.aui_manager.UnInit() def __init_mix(self): aui_manager = wx.aui.AuiManager() @@ -246,31 +252,32 @@ 'Refresh raycasting widget points') Publisher.subscribe(self.LoadHistogram, 'Load histogram') + Publisher.subscribe(self._Exit, 'Exit') def __update_curve_wwwl_text(self, curve): ww, wl = self.clut_raycasting.GetCurveWWWl(curve) - Publisher.sendMessage('Set raycasting wwwl', (ww, wl, curve)) + Publisher.sendMessage('Set raycasting wwwl', ww=ww, wl=wl, curve=curve) - def ShowRaycastingWidget(self, evt_pubsub=None): + def ShowRaycastingWidget(self): self.can_show_raycasting_widget = 1 if self.clut_raycasting.to_draw_points: p = self.aui_manager.GetPane(self.clut_raycasting) p.Show() self.aui_manager.Update() - def HideRaycastingWidget(self, evt_pubsub=None): + def HideRaycastingWidget(self): self.can_show_raycasting_widget = 0 p = self.aui_manager.GetPane(self.clut_raycasting) p.Hide() self.aui_manager.Update() def OnPointChanged(self, evt): - Publisher.sendMessage('Set raycasting refresh', None) - Publisher.sendMessage('Set raycasting curve', evt.GetCurve()) + Publisher.sendMessage('Set raycasting refresh') + Publisher.sendMessage('Set raycasting curve', curve=evt.GetCurve()) Publisher.sendMessage('Render volume viewer') def OnCurveSelected(self, evt): - Publisher.sendMessage('Set raycasting curve', evt.GetCurve()) + Publisher.sendMessage('Set raycasting curve', curve=evt.GetCurve()) Publisher.sendMessage('Render volume viewer') def OnChangeCurveWL(self, evt): @@ -278,7 +285,7 @@ self.__update_curve_wwwl_text(curve) Publisher.sendMessage('Render volume viewer') - def OnSetRaycastPreset(self, evt_pubsub): + def OnSetRaycastPreset(self): preset = project.Project().raycasting_preset p = self.aui_manager.GetPane(self.clut_raycasting) self.clut_raycasting.SetRaycastPreset(preset) @@ -289,16 +296,18 @@ p.Hide() self.aui_manager.Update() - def LoadHistogram(self, pubsub_evt): - histogram = pubsub_evt.data[0] - init, end = pubsub_evt.data[1] + def LoadHistogram(self, histogram, init, end): self.clut_raycasting.SetRange((init, end)) self.clut_raycasting.SetHistogramArray(histogram, (init, end)) - def RefreshPoints(self, pubsub_evt): + def RefreshPoints(self): self.clut_raycasting.CalculatePixelPoints() self.clut_raycasting.Refresh() + def _Exit(self): + self.aui_manager.UnInit() + + import wx.lib.platebtn as pbtn import wx.lib.buttons as btn import wx.lib.pubsub as ps @@ -306,7 +315,7 @@ import invesalius.constants as const -[BUTTON_RAYCASTING, BUTTON_VIEW, BUTTON_SLICE_PLANE, BUTTON_3D_STEREO] = [wx.NewId() for num in xrange(4)] +[BUTTON_RAYCASTING, BUTTON_VIEW, BUTTON_SLICE_PLANE, BUTTON_3D_STEREO, BUTTON_TARGET] = [wx.NewId() for num in range(5)] RAYCASTING_TOOLS = wx.NewId() ID_TO_NAME = {} @@ -346,6 +355,9 @@ BMP_3D_STEREO = wx.Bitmap(os.path.join(const.ICON_DIR, "3D_glasses.png"), wx.BITMAP_TYPE_PNG) + BMP_TARGET = wx.Bitmap(os.path.join(const.ICON_DIR, "target.png"), + wx.BITMAP_TYPE_PNG) + button_raycasting = pbtn.PlateButton(self, BUTTON_RAYCASTING,"", BMP_RAYCASTING, style=pbtn.PB_STYLE_SQUARE, @@ -359,6 +371,11 @@ BMP_SLICE_PLANE, style=pbtn.PB_STYLE_SQUARE, size=(32,32)) + button_target = self.button_target = pbtn.PlateButton(self, BUTTON_TARGET,"", + BMP_TARGET, style=pbtn.PB_STYLE_SQUARE|pbtn.PB_STYLE_TOGGLE, + size=(32,32)) + self.button_target.Enable(0) + self.button_raycasting = button_raycasting self.button_stereo = button_stereo @@ -371,7 +388,7 @@ self.button_view = button_view # VOLUME COLOUR BUTTON - if sys.platform == 'linux2': + if sys.platform.startswith('linux'): size = (32,32) sp = 2 else: @@ -389,7 +406,11 @@ sizer.Add(button_view, 0, wx.TOP|wx.BOTTOM, 1) sizer.Add(button_slice_plane, 0, wx.TOP|wx.BOTTOM, 1) sizer.Add(button_stereo, 0, wx.TOP|wx.BOTTOM, 1) + sizer.Add(button_target, 0, wx.TOP | wx.BOTTOM, 1) + self.navigation_status = False + self.status_target_select = False + self.status_obj_tracker = False sizer.Fit(self) @@ -408,8 +429,10 @@ Publisher.subscribe(self.DisablePreset, 'Close project data') Publisher.subscribe(self.Uncheck, 'Uncheck image plane menu') Publisher.subscribe(self.DisableVolumeCutMenu, 'Disable volume cut menu') + Publisher.subscribe(self.StatusTargetSelect, 'Disable or enable coil tracker') + Publisher.subscribe(self.StatusObjTracker, 'Status target button') - def DisablePreset(self, pubsub_evt): + def DisablePreset(self): self.off_item.Check(1) @@ -419,6 +442,7 @@ self.button_view.Bind(wx.EVT_LEFT_DOWN, self.OnButtonView) self.button_colour.Bind(csel.EVT_COLOURSELECT, self.OnSelectColour) self.button_stereo.Bind(wx.EVT_LEFT_DOWN, self.OnButtonStereo) + self.button_target.Bind(wx.EVT_LEFT_DOWN, self.OnButtonTarget) def OnButtonRaycasting(self, evt): # MENU RELATED TO RAYCASTING TYPES @@ -433,14 +457,36 @@ def OnButtonSlicePlane(self, evt): self.button_slice_plane.PopupMenu(self.slice_plane_menu) + def StatusObjTracker(self, status): + self.status_obj_tracker = status + self.StatusNavigation() + + def StatusTargetSelect(self, status): + self.status_target_select = status + self.StatusNavigation() + + def StatusNavigation(self): + if self.status_target_select and self.status_obj_tracker: + self.button_target.Enable(1) + else: + self.OnButtonTarget(False) + self.button_target.Enable(0) + + def OnButtonTarget(self, evt): + if not self.button_target.IsPressed() and evt is not False: + self.button_target._pressed = True + Publisher.sendMessage('Target navigation mode', target_mode=self.button_target._pressed) + elif self.button_target.IsPressed() or evt is False: + self.button_target._pressed = False + Publisher.sendMessage('Target navigation mode', target_mode=self.button_target._pressed) + def OnSavePreset(self, evt): d = wx.TextEntryDialog(self, _("Preset name")) if d.ShowModal() == wx.ID_OK: preset_name = d.GetValue() - Publisher.sendMessage(_("Save raycasting preset"), - preset_name) + Publisher.sendMessage("Save raycasting preset", preset_name=preset_name) - def __init_menus(self, pubsub_evt=None): + def __init_menus(self): # MENU RELATED TO RAYCASTING TYPES menu = self.menu = wx.Menu() #print "\n\n" @@ -527,7 +573,7 @@ self.Fit() self.Update() - def DisableVolumeCutMenu(self, pusub_evt): + def DisableVolumeCutMenu(self): self.menu.Enable(RAYCASTING_TOOLS, 0) item = ID_TO_TOOL_ITEM[self.id_cutplane] item.Check(0) @@ -547,23 +593,23 @@ label = item.GetLabel() if not (checked): - Publisher.sendMessage('Disable plane', label) + Publisher.sendMessage('Disable plane', plane_label=label) else: - Publisher.sendMessage('Enable plane', label) + Publisher.sendMessage('Enable plane', plane_label=label) def OnMenuStereo(self, evt): id = evt.GetId() mode = ID_TO_STEREO_NAME[id] - Publisher.sendMessage('Set stereo mode', mode) + Publisher.sendMessage('Set stereo mode', mode=mode) - def Uncheck(self, pubsub_evt): + def Uncheck(self): for item in self.slice_plane_menu.GetMenuItems(): if (item.IsChecked()): item.Check(0) - def ChangeButtonColour(self, pubsub_evt): - colour = [i*255 for i in pubsub_evt.data] + def ChangeButtonColour(self, colour): + colour = [i*255 for i in colour] self.button_colour.SetColour(colour) def OnMenuRaycasting(self, evt): @@ -573,7 +619,7 @@ # Raycassting type was selected name = ID_TO_NAME[id] Publisher.sendMessage('Load raycasting preset', - ID_TO_NAME[id]) + preset_name=ID_TO_NAME[id]) # Enable or disable tools if name != const.RAYCASTING_OFF_LABEL: self.menu_raycasting.Enable(RAYCASTING_TOOLS, 1) @@ -590,12 +636,12 @@ # i.Check(0) if not TOOL_STATE[id]: Publisher.sendMessage('Enable raycasting tool', - [ID_TO_TOOL[id],1]) + tool_name=ID_TO_TOOL[id], flag=1) TOOL_STATE[id] = True item.Check(1) else: Publisher.sendMessage('Enable raycasting tool', - [ID_TO_TOOL[id],0]) + tool_name=ID_TO_TOOL[id], flag=0) TOOL_STATE[id] = False item.Check(0) @@ -606,10 +652,10 @@ self.button_view.SetBitmapSelected(bmp) Publisher.sendMessage('Set volume view angle', - evt.GetId()) + view=evt.GetId()) self.Refresh() def OnSelectColour(self, evt): colour = c = [i/255.0 for i in evt.GetValue()] - Publisher.sendMessage('Change volume viewer background colour', colour) + Publisher.sendMessage('Change volume viewer background colour', colour=colour) diff -Nru invesalius-3.1.1/invesalius/gui/dialogs.py invesalius-3.1.99991/invesalius/gui/dialogs.py --- invesalius-3.1.1/invesalius/gui/dialogs.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/dialogs.py 2018-08-17 16:41:39.000000000 +0000 @@ -18,13 +18,26 @@ # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais # detalhes. #-------------------------------------------------------------------------- + import os import random import sys +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + import vtk import wx -import wx.combo +try: + from wx.adv import BitmapComboBox +except ImportError: + from wx.combo import BitmapComboBox from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor from wx.lib import masked @@ -32,13 +45,18 @@ from wx.lib.wordwrap import wordwrap from wx.lib.pubsub import pub as Publisher +try: + from wx.adv import AboutDialogInfo, AboutBox +except ImportError: + from wx import AboutDialogInfo, AboutBox + import invesalius.constants as const +import invesalius.data.coordinates as dco import invesalius.gui.widgets.gradient as grad import invesalius.session as ses import invesalius.utils as utils from invesalius.gui.widgets import clut_imagedata from invesalius.gui.widgets.clut_imagedata import CLUTImageDataWidget, EVT_CLUT_NODE_CHANGED - import numpy as np try: @@ -57,11 +75,16 @@ class NumberDialog(wx.Dialog): def __init__(self, message, value=0): - pre = wx.PreDialog() - pre.Create(None, -1, "InVesalius 3", size=wx.DefaultSize, - pos=wx.DefaultPosition, - style=wx.DEFAULT_DIALOG_STYLE) - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.Create(None, -1, "InVesalius 3", size=wx.DefaultSize, + pos=wx.DefaultPosition, + style=wx.DEFAULT_DIALOG_STYLE) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, None, -1, "InVesalius 3", size=wx.DefaultSize, + pos=wx.DefaultPosition, + style=wx.DEFAULT_DIALOG_STYLE) # Static text which contains message to user label = wx.StaticText(self, -1, message) @@ -107,11 +130,16 @@ class ResizeImageDialog(wx.Dialog): def __init__(self):#, message, value=0): - pre = self.pre = wx.PreDialog() - pre.Create(None, -1, "InVesalius 3", size=wx.DefaultSize, - pos=wx.DefaultPosition, - style=wx.DEFAULT_DIALOG_STYLE) - self.PostCreate(pre) + try: + pre = self.pre = wx.PreDialog() + pre.Create(None, -1, "InVesalius 3", size=wx.DefaultSize, + pos=wx.DefaultPosition, + style=wx.DEFAULT_DIALOG_STYLE) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, None, -1, "InVesalius 3", size=wx.DefaultSize, + pos=wx.DefaultPosition, + style=wx.DEFAULT_DIALOG_STYLE) lbl_message = wx.StaticText(self, -1, _("InVesalius is running on a 32-bit operating system or has insufficient memory. \nIf you want to work with 3D surfaces or volume rendering, \nit is recommended to reduce the medical images resolution.")) icon = wx.ArtProvider.GetBitmap(wx.ART_WARNING, wx.ART_MESSAGE_BOX, (32,32)) @@ -139,12 +167,12 @@ sizer_itens = wx.BoxSizer(wx.VERTICAL) sizer_itens.Add(lbl_message, 0, wx.EXPAND|wx.ALL, 5) - sizer_itens.AddSizer(sizer_percent, 0, wx.EXPAND|wx.ALL, 5) + sizer_itens.Add(sizer_percent, 0, wx.EXPAND|wx.ALL, 5) sizer_itens.Add(btn_sizer, 0, wx.EXPAND|wx.ALL, 5) sizer_general = wx.BoxSizer(wx.HORIZONTAL) sizer_general.Add(bmp, 0, wx.ALIGN_CENTRE|wx.ALL, 10) - sizer_general.AddSizer(sizer_itens, 0, wx.ALL , 5) + sizer_general.Add(sizer_itens, 0, wx.ALL , 5) #self.SetAutoLayout(True) self.SetSizer(sizer_general) @@ -159,7 +187,7 @@ return self.num_ctrl_porcent.GetValue() def Close(self): - self.pre.Destroy() + self.Destroy() def ShowNumberDialog(message, value=0): dlg = NumberDialog(message, value) @@ -210,6 +238,13 @@ # --------- + +INV_NON_COMPRESSED = 0 +INV_COMPRESSED = 1 + +WILDCARD_INV_SAVE = _("InVesalius project (*.inv3)|*.inv3") + "|" + \ + _("InVesalius project compressed (*.inv3)|*.inv3") + WILDCARD_OPEN = "InVesalius 3 project (*.inv3)|*.inv3|" \ "All files (*.*)|*.*" @@ -262,7 +297,7 @@ def ShowImportDirDialog(self): current_dir = os.path.abspath(".") - if (sys.platform == 'win32') or (sys.platform == 'linux2'): + if sys.platform == 'win32' or sys.platform.startswith('linux'): session = ses.Session() if (session.GetLastDicomFolder()): @@ -303,7 +338,7 @@ def ShowImportBitmapDirDialog(self): current_dir = os.path.abspath(".") - if (sys.platform == 'win32') or (sys.platform == 'linux2'): + if sys.platform == 'win32' or sys.platform.startswith('linux'): session = ses.Session() if (session.GetLastDicomFolder()): @@ -414,7 +449,7 @@ _("Save project as..."), # title "", # last used directory default_filename, - _("InVesalius project (*.inv3)|*.inv3"), + WILDCARD_INV_SAVE, wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) #dlg.SetFilterIndex(0) # default is VTI @@ -435,8 +470,9 @@ if filename.split(".")[-1] != extension: filename = filename + "." + extension + wildcard = dlg.GetFilterIndex() os.chdir(current_dir) - return filename + return filename, wildcard == INV_COMPRESSED # Dialog for neuronavigation markers @@ -447,7 +483,7 @@ "", # last used directory default_filename, _("Markers files (*.mks)|*.mks"), - wx.SAVE | wx.OVERWRITE_PROMPT) + wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) # dlg.SetFilterIndex(0) # default is VTI filename = None @@ -470,6 +506,36 @@ os.chdir(current_dir) return filename +def ShowSaveCoordsDialog(default_filename=None): + current_dir = os.path.abspath(".") + dlg = wx.FileDialog(None, + _("Save coords as..."), # title + "", # last used directory + default_filename, + _("Coordinates files (*.csv)|*.csv"), + wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + # dlg.SetFilterIndex(0) # default is VTI + + filename = None + try: + if dlg.ShowModal() == wx.ID_OK: + filename = dlg.GetPath() + ok = 1 + else: + ok = 0 + except(wx._core.PyAssertionError): # TODO: fix win64 + filename = dlg.GetPath() + ok = 1 + + if (ok): + extension = "csv" + if sys.platform != 'win32': + if filename.split(".")[-1] != extension: + filename = filename + "." + extension + + os.chdir(current_dir) + return filename + def ShowLoadMarkersDialog(): current_dir = os.path.abspath(".") @@ -478,7 +544,67 @@ defaultDir="", defaultFile="", wildcard=_("Markers files (*.mks)|*.mks"), - style=wx.OPEN|wx.CHANGE_DIR) + style=wx.FD_OPEN|wx.FD_CHANGE_DIR) + + # inv3 filter is default + dlg.SetFilterIndex(0) + + # Show the dialog and retrieve the user response. If it is the OK response, + # process the data. + filepath = None + try: + if dlg.ShowModal() == wx.ID_OK: + # This returns a Python list of files that were selected. + filepath = dlg.GetPath() + except(wx._core.PyAssertionError): # FIX: win64 + filepath = dlg.GetPath() + + # Destroy the dialog. Don't do this until you are done with it! + # BAD things can happen otherwise! + dlg.Destroy() + os.chdir(current_dir) + return filepath + + +def ShowSaveRegistrationDialog(default_filename=None): + current_dir = os.path.abspath(".") + dlg = wx.FileDialog(None, + _("Save object registration as..."), # title + "", # last used directory + default_filename, + _("Registration files (*.obr)|*.obr"), + wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + # dlg.SetFilterIndex(0) # default is VTI + + filename = None + try: + if dlg.ShowModal() == wx.ID_OK: + filename = dlg.GetPath() + ok = 1 + else: + ok = 0 + except(wx._core.PyAssertionError): # TODO: fix win64 + filename = dlg.GetPath() + ok = 1 + + if (ok): + extension = "obr" + if sys.platform != 'win32': + if filename.split(".")[-1] != extension: + filename = filename + "." + extension + + os.chdir(current_dir) + return filename + + +def ShowLoadRegistrationDialog(): + current_dir = os.path.abspath(".") + + dlg = wx.FileDialog(None, message=_("Load object registration"), + defaultDir="", + defaultFile="", + wildcard=_("Registration files (*.obr)|*.obr"), + style=wx.FD_OPEN|wx.FD_CHANGE_DIR) # inv3 filter is default dlg.SetFilterIndex(0) @@ -502,10 +628,14 @@ class MessageDialog(wx.Dialog): def __init__(self, message): - pre = wx.PreDialog() - pre.Create(None, -1, "InVesalius 3", size=(360, 370), pos=wx.DefaultPosition, - style=wx.DEFAULT_DIALOG_STYLE|wx.ICON_INFORMATION) - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.Create(None, -1, "InVesalius 3", size=(360, 370), pos=wx.DefaultPosition, + style=wx.DEFAULT_DIALOG_STYLE|wx.ICON_INFORMATION) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, None, -1, "InVesalius 3", size=(360, 370), pos=wx.DefaultPosition, + style=wx.DEFAULT_DIALOG_STYLE|wx.ICON_INFORMATION) # Static text which contains message to user label = wx.StaticText(self, -1, message) @@ -544,10 +674,14 @@ title=_("Invesalius Update") self.url = url - pre = wx.PreDialog() - pre.Create(None, -1, title, size=(360, 370), pos=wx.DefaultPosition, - style=wx.DEFAULT_DIALOG_STYLE|wx.ICON_INFORMATION) - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.Create(None, -1, title, size=(360, 370), pos=wx.DefaultPosition, + style=wx.DEFAULT_DIALOG_STYLE|wx.ICON_INFORMATION) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, None, -1, title, size=(360, 370), pos=wx.DefaultPosition, + style=wx.DEFAULT_DIALOG_STYLE|wx.ICON_INFORMATION) # Static text which contains message to user label = wx.StaticText(self, -1, msg) @@ -592,7 +726,7 @@ self.Close() self.Destroy() - def _OnCloseInV(self, pubsub_evt): + def _OnCloseInV(self): # Closes and destroy this dialog. self.Close() self.Destroy() @@ -626,6 +760,15 @@ dlg.Destroy() +def ImportOldFormatInvFile(): + msg = _("File was created in a newer InVesalius version. Some functionalities may not work correctly.") + dlg = wx.MessageDialog(None, msg, + "InVesalius 3", + wx.ICON_INFORMATION | wx.OK) + dlg.ShowModal() + dlg.Destroy() + + def ImportInvalidFiles(ftype="DICOM"): if ftype == "Bitmap": msg = _("There are no Bitmap, JPEG, PNG or TIFF files in the selected folder.") @@ -729,7 +872,19 @@ # Dialogs for neuronavigation mode def InvalidFiducials(): - msg = _("Fiducials are invalid. Select six coordinates.") + msg = _("Fiducials are invalid. Select all coordinates.") + if sys.platform == 'darwin': + dlg = wx.MessageDialog(None, "", msg, + wx.ICON_INFORMATION | wx.OK) + else: + dlg = wx.MessageDialog(None, msg, "InVesalius 3 - Neuronavigator", + wx.ICON_INFORMATION | wx.OK) + dlg.ShowModal() + dlg.Destroy() + + +def InvalidObjectRegistration(): + msg = _("Perform coil registration before navigation.") if sys.platform == 'darwin': dlg = wx.MessageDialog(None, "", msg, wx.ICON_INFORMATION | wx.OK) @@ -793,6 +948,7 @@ dlg.ShowModal() dlg.Destroy() + def DeleteAllMarkers(): msg = _("Do you really want to delete all markers?") if sys.platform == 'darwin': @@ -805,6 +961,38 @@ dlg.Destroy() return result +def DeleteTarget(): + msg = _("Target deleted") + if sys.platform == 'darwin': + dlg = wx.MessageDialog(None, "", msg, + wx.ICON_INFORMATION | wx.OK) + else: + dlg = wx.MessageDialog(None, msg, "InVesalius 3 - Neuronavigator", + wx.ICON_INFORMATION | wx.OK) + dlg.ShowModal() + dlg.Destroy() + +def NewTarget(): + msg = _("New target selected") + if sys.platform == 'darwin': + dlg = wx.MessageDialog(None, "", msg, + wx.ICON_INFORMATION | wx.OK) + else: + dlg = wx.MessageDialog(None, msg, "InVesalius 3 - Neuronavigator", + wx.ICON_INFORMATION | wx.OK) + dlg.ShowModal() + dlg.Destroy() + +def InvalidTargetID(): + msg = _("Sorry, you cannot use 'TARGET' ID") + if sys.platform == 'darwin': + dlg = wx.MessageDialog(None, "", msg, + wx.ICON_INFORMATION | wx.OK) + else: + dlg = wx.MessageDialog(None, msg, "InVesalius 3 - Neuronavigator", + wx.ICON_INFORMATION | wx.OK) + dlg.ShowModal() + dlg.Destroy() def EnterMarkerID(default): msg = _("Edit marker ID") @@ -831,18 +1019,22 @@ import invesalius.data.mask as mask import invesalius.project as prj - # Instead of calling wx.Dialog.__init__ we precreate the dialog - # so we can set an extra style that must be set before - # creation, and then we create the GUI object using the Create - # method. - pre = wx.PreDialog() - pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) - pre.Create(parent, ID, title, pos, (500,300), style) - - # This next step is the most important, it turns this Python - # object into the real wrapper of the dialog (instead of pre) - # as far as the wxPython extension is concerned. - self.PostCreate(pre) + try: + # Instead of calling wx.Dialog.__init__ we precreate the dialog + # so we can set an extra style that must be set before + # creation, and then we create the GUI object using the Create + # method. + pre = wx.PreDialog() + pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) + pre.Create(parent, ID, title, pos, (500,300), style) + # This next step is the most important, it turns this Python + # object into the real wrapper of the dialog (instead of pre) + # as far as the wxPython extension is concerned. + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, parent, ID, title, pos, (500,300), style) + self.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) + self.CenterOnScreen() @@ -869,8 +1061,7 @@ # Retrieve existing masks project = prj.Project() - thresh_list = project.threshold_modes.keys() - thresh_list.sort() + thresh_list = sorted(project.threshold_modes.keys()) default_index = thresh_list.index(_("Bone")) self.thresh_list = thresh_list @@ -990,7 +1181,7 @@ def SaveChangesDialog(filename, parent): current_dir = os.path.abspath(".") - msg = _("The project %s has been modified.\nSave changes?")%filename + msg = _(u"The project %s has been modified.\nSave changes?")%filename if sys.platform == 'darwin': dlg = wx.MessageDialog(None, "", msg, wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL) @@ -1036,7 +1227,7 @@ def ShowAboutDialog(parent): - info = wx.AboutDialogInfo() + info = AboutDialogInfo() info.Name = "InVesalius" info.Version = "3.1.1" info.Copyright = _("(c) 2007-2017 Center for Information Technology Renato Archer - CTI") @@ -1097,8 +1288,8 @@ info.Artists = [u"Otavio Henrique Junqueira Amorim"] - # Then we call wx.AboutBox providing its info object - wx.AboutBox(info) + # Then we call AboutBox providing its info object + AboutBox(info) def ShowSavePresetDialog(default_filename="raycasting"): @@ -1123,18 +1314,22 @@ import invesalius.data.surface as surface import invesalius.project as prj - # Instead of calling wx.Dialog.__init__ we precreate the dialog - # so we can set an extra style that must be set before - # creation, and then we create the GUI object using the Create - # method. - pre = wx.PreDialog() - pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) - pre.Create(parent, ID, title, pos, (500,300), style) - - # This next step is the most important, it turns this Python - # object into the real wrapper of the dialog (instead of pre) - # as far as the wxPython extension is concerned. - self.PostCreate(pre) + try: + # Instead of calling wx.Dialog.__init__ we precreate the dialog + # so we can set an extra style that must be set before + # creation, and then we create the GUI object using the Create + # method. + pre = wx.PreDialog() + pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) + pre.Create(parent, ID, title, pos, (500,300), style) + + # This next step is the most important, it turns this Python + # object into the real wrapper of the dialog (instead of pre) + # as far as the wxPython extension is concerned. + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, parent, ID, title, pos, (500,300), style) + self.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) self.CenterOnScreen() @@ -1161,8 +1356,7 @@ # Retrieve existing masks project = prj.Project() - index_list = project.mask_dict.keys() - index_list.sort() + index_list = sorted(project.mask_dict.keys()) self.mask_list = [project.mask_dict[index].name for index in index_list] @@ -1265,7 +1459,7 @@ project = proj.Project() project_name = "%s_%s" % (project.name, type_) - if not sys.platform in ('win32', 'linux2'): + if not sys.platform in ('win32', 'linux2', 'linux'): project_name += ".jpg" dlg = wx.FileDialog(None, @@ -1336,18 +1530,24 @@ # so we can set an extra style that must be set before # creation, and then we create the GUI object using the Create # method. - pre = wx.PreDialog() - pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) - pre.Create(parent, ID, title, pos, (500,300), style) - - # This extra style can be set after the UI object has been created. - if 'wxMac' in wx.PlatformInfo and useMetal: - self.SetExtraStyle(wx.DIALOG_EX_METAL) - - # This next step is the most important, it turns this Python - # object into the real wrapper of the dialog (instead of pre) - # as far as the wxPython extension is concerned. - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) + pre.Create(parent, ID, title, pos, (500,300), style) + + # This extra style can be set after the UI object has been created. + if 'wxMac' in wx.PlatformInfo and useMetal: + self.SetExtraStyle(wx.DIALOG_EX_METAL) + + # This next step is the most important, it turns this Python + # object into the real wrapper of the dialog (instead of pre) + # as far as the wxPython extension is concerned. + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, parent, ID, title, pos, size, style) + self.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) + if 'wxMac' in wx.PlatformInfo and useMetal: + self.SetExtraStyle(wx.DIALOG_EX_METAL) self.CenterOnScreen() @@ -1400,6 +1600,7 @@ import invesalius.constants as const import invesalius.data.surface as surface import invesalius.project as prj + import invesalius.data.slice_ as slc wx.Panel.__init__(self, parent, ID) @@ -1420,13 +1621,15 @@ #Retrieve existing masks project = prj.Project() index_list = project.mask_dict.keys() - index_list.sort() - self.mask_list = [project.mask_dict[index].name for index in index_list] + self.mask_list = [project.mask_dict[index].name for index in sorted(index_list)] + + active_mask = slc.Slice().current_mask.index + #active_mask = len(self.mask_list)-1 # Mask selection combo combo_mask = wx.ComboBox(self, -1, "", choices= self.mask_list, style=wx.CB_DROPDOWN|wx.CB_READONLY) - combo_mask.SetSelection(len(self.mask_list)-1) + combo_mask.SetSelection(active_mask) combo_mask.Bind(wx.EVT_COMBOBOX, self.OnSetMask) if sys.platform != 'win32': combo_mask.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) @@ -1462,7 +1665,7 @@ # LINES 4 and 5: Checkboxes check_box_holes = wx.CheckBox(self, -1, _("Fill holes")) - check_box_holes.SetValue(True) + check_box_holes.SetValue(False) self.check_box_holes = check_box_holes check_box_largest = wx.CheckBox(self, -1, _("Keep largest region")) self.check_box_largest = check_box_largest @@ -1644,9 +1847,12 @@ class ClutImagedataDialog(wx.Dialog): def __init__(self, histogram, init, end, nodes=None): - pre = wx.PreDialog() - pre.Create(wx.GetApp().GetTopWindow(), -1, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), -1, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) self.histogram = histogram self.init = init @@ -1674,12 +1880,12 @@ def OnClutChange(self, evt): Publisher.sendMessage('Change colour table from background image from widget', - evt.GetNodes()) + nodes=evt.GetNodes()) Publisher.sendMessage('Update window level text', - (self.clut_widget.window_width, - self.clut_widget.window_level)) + window=self.clut_widget.window_width, + level=self.clut_widget.window_level) - def _refresh_widget(self, pubsub_evt): + def _refresh_widget(self): self.clut_widget.Refresh() def Show(self, gen_evt=True, show=True): @@ -1744,10 +1950,13 @@ class WatershedOptionsDialog(wx.Dialog): - def __init__(self, config): - pre = wx.PreDialog() - pre.Create(wx.GetApp().GetTopWindow(), -1, _(u'Watershed'), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) - self.PostCreate(pre) + def __init__(self, config, ID=-1, title=_(u'Watershed'), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT): + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), ID, title=title, style=style) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), ID, title=title, style=style) self.config = config @@ -1785,10 +1994,13 @@ evt.Skip() class MaskBooleanDialog(wx.Dialog): - def __init__(self, masks): - pre = wx.PreDialog() - pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Boolean operations"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) - self.PostCreate(pre) + def __init__(self, masks, ID=-1, title=_(u"Boolean operations"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP): + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), ID, title=title, style=style) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), ID, title=title, style=style) self._init_gui(masks) self.CenterOnScreen() @@ -1814,7 +2026,7 @@ (_(u"Difference"), const.BOOLEAN_DIFF, 'bool_difference.png'), (_(u"Intersection"), const.BOOLEAN_AND, 'bool_intersection.png'), (_(u"Exclusive disjunction"), const.BOOLEAN_XOR, 'bool_disjunction.png')) - self.op_boolean = wx.combo.BitmapComboBox(self, -1, op_choices[0][0], choices=[]) + self.op_boolean = BitmapComboBox(self, -1, op_choices[0][0], choices=[]) for n, i, f in op_choices: bmp = wx.Bitmap(os.path.join(icon_folder, f), wx.BITMAP_TYPE_PNG) @@ -1856,7 +2068,8 @@ m1 = self.mask1.GetClientData(self.mask1.GetSelection()) m2 = self.mask2.GetClientData(self.mask2.GetSelection()) - Publisher.sendMessage('Do boolean operation', (op, m1, m2)) + Publisher.sendMessage('Do boolean operation', + operation=op, mask1=m1, mask2=m2) Publisher.sendMessage('Reload actual slice') Publisher.sendMessage('Refresh viewer') @@ -1865,10 +2078,13 @@ class ReorientImageDialog(wx.Dialog): - def __init__(self): - pre = wx.PreDialog() - pre.Create(wx.GetApp().GetTopWindow(), -1, _(u'Image reorientation'), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) - self.PostCreate(pre) + def __init__(self, ID=-1, title=_(u'Image reorientation'), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT): + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), ID, title=title, style=style) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), ID, title=title, style=style) self._closed = False @@ -1937,13 +2153,13 @@ self.btnapply.Bind(wx.EVT_BUTTON, self.apply_reorientation) self.Bind(wx.EVT_CLOSE, self.OnClose) - def _update_angles(self, pubsub_evt): - anglex, angley, anglez = pubsub_evt.data + def _update_angles(self, angles): + anglex, angley, anglez = angles self.anglex.SetValue("%.3f" % np.rad2deg(anglex)) self.angley.SetValue("%.3f" % np.rad2deg(angley)) self.anglez.SetValue("%.3f" % np.rad2deg(anglez)) - def _close_dialog(self, pubsub_evt): + def _close_dialog(self): self.Destroy() def apply_reorientation(self, evt): @@ -1952,13 +2168,13 @@ def OnClose(self, evt): self._closed = True - Publisher.sendMessage('Disable style', const.SLICE_STATE_REORIENT) - Publisher.sendMessage('Enable style', const.STATE_DEFAULT) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_REORIENT) + Publisher.sendMessage('Enable style', style=const.STATE_DEFAULT) self.Destroy() def OnSelect(self, evt): im_code = self.interp_method.GetClientData(self.interp_method.GetSelection()) - Publisher.sendMessage('Set interpolation method', im_code) + Publisher.sendMessage('Set interpolation method', interp_method=im_code) def OnSetFocus(self, evt): self._last_ax = self.anglex.GetValue() @@ -1976,29 +2192,32 @@ self.angley.SetValue(self._last_ay) self.anglez.SetValue(self._last_az) return - Publisher.sendMessage('Set reorientation angles', (ax, ay, az)) + Publisher.sendMessage('Set reorientation angles', angles=(ax, ay, az)) class ImportBitmapParameters(wx.Dialog): from os import sys def __init__(self): - pre = wx.PreDialog() - if sys.platform == 'win32': size=wx.Size(380,180) else: size=wx.Size(380,210) - pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Create project from bitmap"),size=size,\ - style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Create project from bitmap"),size=size, + style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, + _(u"Create project from bitmap"), + size=size, + style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) self.interval = 0 - - self.PostCreate(pre) self._init_gui() - self.bind_evts() self.CenterOnScreen() @@ -2029,13 +2248,19 @@ gbs.Add(stx_name, (0,0), flag=flag_labels) gbs.Add(tx_name, (0,1)) - gbs.AddStretchSpacer((1,0)) + try: + gbs.Add(0, 0, (1,0)) + except TypeError: + gbs.AddStretchSpacer((1,0)) gbs.Add(stx_orientation, (2,0), flag=flag_labels) gbs.Add(cb_orientation, (2,1)) gbs.Add(stx_spacing, (3,0)) - gbs.AddStretchSpacer((4,0)) + try: + gbs.Add(0, 0, (4,0)) + except TypeError: + gbs.AddStretchSpacer((4,0)) #--- spacing -------------- gbs_spacing = wx.GridBagSizer(2, 6) @@ -2085,17 +2310,23 @@ btn_cancel = wx.Button(p, wx.ID_CANCEL) - gbs_button.AddStretchSpacer((0,2)) + try: + gbs_button.Add(0, 0, (0,2)) + except TypeError: + gbs_button.AddStretchSpacer((0,2)) gbs_button.Add(btn_cancel, (1,2)) gbs_button.Add(btn_ok, (1,3)) - gbs_principal.AddSizer(gbs, (0,0), flag = wx.ALL|wx.EXPAND) - gbs_principal.AddSizer(gbs_spacing, (1,0), flag=wx.ALL|wx.EXPAND) - gbs_principal.AddStretchSpacer((2,0)) - gbs_principal.AddSizer(gbs_button, (3,0), flag = wx.ALIGN_RIGHT) + gbs_principal.Add(gbs, (0,0), flag = wx.ALL|wx.EXPAND) + gbs_principal.Add(gbs_spacing, (1,0), flag=wx.ALL|wx.EXPAND) + try: + gbs_principal.Add(0, 0, (2,0)) + except TypeError: + gbs_principal.AddStretchSpacer((2,0)) + gbs_principal.Add(gbs_button, (3,0), flag = wx.ALIGN_RIGHT) box = wx.BoxSizer() - box.AddSizer(gbs_principal, 1, wx.ALL|wx.EXPAND, 10) + box.Add(gbs_principal, 1, wx.ALL|wx.EXPAND, 10) p.SetSizer(box) box.Fit(self) @@ -2108,9 +2339,6 @@ self.interval = v def OnOk(self, evt): - self.Close() - self.Destroy() - orient_selection = self.cb_orientation.GetSelection() if(orient_selection == 1): @@ -2123,7 +2351,10 @@ values = [self.tx_name.GetValue(), orientation,\ self.fsp_spacing_x.GetValue(), self.fsp_spacing_y.GetValue(),\ self.fsp_spacing_z.GetValue(), self.interval] - Publisher.sendMessage('Open bitmap files', values) + Publisher.sendMessage('Open bitmap files', rec_data=values) + + self.Close() + self.Destroy() def BitmapNotSameSize(): @@ -2146,10 +2377,16 @@ sizer = wx.GridBagSizer(5, 5) - sizer.AddStretchSpacer((0, 0)) + try: + sizer.Add(0, 0, (0, 0)) + except TypeError: + sizer.AddStretchSpacer((0, 0)) sizer.Add(self.target_2d, (1, 0), (1, 6), flag=wx.LEFT, border=5) sizer.Add(self.target_3d, (2, 0), (1, 6), flag=wx.LEFT, border=5) - sizer.AddStretchSpacer((3, 0)) + try: + sizer.Add(0, 0, (3, 0)) + except TypeError: + sizer.AddStretchSpacer((3, 0)) self.SetSizer(sizer) sizer.Fit(self) @@ -2166,11 +2403,17 @@ sizer = wx.GridBagSizer(5, 5) - sizer.AddStretchSpacer((0, 0)) + try: + sizer.Add(0, 0, (0, 0)) + except TypeError: + sizer.AddStretchSpacer((0, 0)) sizer.Add(wx.StaticText(self, -1, _(u"2D Connectivity")), (1, 0), (1, 6), flag=wx.LEFT, border=5) sizer.Add(self.conect2D_4, (2, 0), flag=wx.LEFT, border=7) sizer.Add(self.conect2D_8, (2, 1), flag=wx.LEFT, border=7) - sizer.AddStretchSpacer((3, 0)) + try: + sizer.Add(0, 0, (3, 0)) + except TypeError: + sizer.AddStretchSpacer((3, 0)) if show_orientation: self.cmb_orientation = wx.ComboBox(self, -1, choices=(_(u"Axial"), _(u"Coronal"), _(u"Sagital")), style=wx.CB_READONLY) @@ -2178,7 +2421,10 @@ sizer.Add(wx.StaticText(self, -1, _(u"Orientation")), (4, 0), (1, 6), flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5) sizer.Add(self.cmb_orientation, (5, 0), (1, 10), flag=wx.LEFT|wx.RIGHT|wx.EXPAND, border=7) - sizer.AddStretchSpacer((6, 0)) + try: + sizer.Add(0, 0, (6, 0)) + except TypeError: + sizer.AddStretchSpacer((6, 0)) self.SetSizer(sizer) sizer.Fit(self) @@ -2212,12 +2458,18 @@ sizer = wx.GridBagSizer(5, 5) - sizer.AddStretchSpacer((0, 0)) + try: + sizer.Add(0, 0, (0, 0)) + except TypeError: + sizer.AddStretchSpacer((0, 0)) sizer.Add(wx.StaticText(self, -1, _(u"3D Connectivity")), (1, 0), (1, 6), flag=wx.LEFT, border=5) sizer.Add(self.conect3D_6, (2, 0), flag=wx.LEFT, border=9) sizer.Add(self.conect3D_18, (2, 1), flag=wx.LEFT, border=9) sizer.Add(self.conect3D_26, (2, 2), flag=wx.LEFT, border=9) - sizer.AddStretchSpacer((3, 0)) + try: + sizer.Add(0, 0, (3, 0)) + except TypeError: + sizer.AddStretchSpacer((3, 0)) self.SetSizer(sizer) sizer.Fit(self) @@ -2268,7 +2520,7 @@ def OnSlideChanged(self, evt): self.config.t0 = int(self.threshold.GetMinValue()) self.config.t1 = int(self.threshold.GetMaxValue()) - print self.config.t0, self.config.t1 + print(self.config.t0, self.config.t1) class PanelFFillDynamic(wx.Panel): @@ -2292,11 +2544,17 @@ sizer = wx.GridBagSizer(5, 5) - sizer.AddStretchSpacer((0, 0)) + try: + sizer.Add(0, 0, (0, 0)) + except TypeError: + sizer.AddStretchSpacer((0, 0)) sizer.Add(self.use_ww_wl, (1, 0), (1, 6), flag=wx.LEFT, border=5) - sizer.AddStretchSpacer((2, 0)) + try: + sizer.Add(0, 0, (2, 0)) + except TypeError: + sizer.AddStretchSpacer((2, 0)) sizer.Add(wx.StaticText(self, -1, _(u"Deviation")), (3, 0), (1, 6), flag=wx.LEFT, border=5) @@ -2306,7 +2564,10 @@ sizer.Add(wx.StaticText(self, -1, _(u"Max:")), (4, 2), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=9) sizer.Add(self.deviation_max, (4, 3)) - sizer.AddStretchSpacer((5, 0)) + try: + sizer.Add(0, 0, (5, 0)) + except TypeError: + sizer.AddStretchSpacer((5, 0)) self.SetSizer(sizer) sizer.Fit(self) @@ -2350,19 +2611,28 @@ sizer = wx.GridBagSizer(5, 5) - sizer.AddStretchSpacer((0, 0)) + try: + sizer.Add(0, 0, (0, 0)) + except TypeError: + sizer.AddStretchSpacer((0, 0)) sizer.Add(self.use_ww_wl, (1, 0), (1, 6), flag=wx.LEFT, border=5) - sizer.AddStretchSpacer((2, 0)) + try: + sizer.Add(0, 0, (2, 0)) + except TypeError: + sizer.AddStretchSpacer((2, 0)) sizer.Add(wx.StaticText(self, -1, _(u"Multiplier")), (3, 0), (1, 3), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=5) - sizer.Add(self.spin_mult, (3, 3), (1, 2)) + sizer.Add(self.spin_mult, (3, 3), (1, 3)) sizer.Add(wx.StaticText(self, -1, _(u"Iterations")), (4, 0), (1, 3), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=5) sizer.Add(self.spin_iters, (4, 3), (1, 2)) - sizer.AddStretchSpacer((5, 0)) + try: + sizer.Add(0, 0, (5, 0)) + except TypeError: + sizer.AddStretchSpacer((5, 0)) self.SetSizer(sizer) sizer.Fit(self) @@ -2384,9 +2654,12 @@ class FFillOptionsDialog(wx.Dialog): def __init__(self, title, config): - pre = wx.PreDialog() - pre.Create(wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) self.config = config @@ -2485,18 +2758,21 @@ self.config.con_3d = 26 def OnClose(self, evt): - print "ONCLOSE" + print("ONCLOSE") if self.config.dlg_visible: - Publisher.sendMessage('Disable style', const.SLICE_STATE_MASK_FFILL) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_MASK_FFILL) evt.Skip() self.Destroy() class SelectPartsOptionsDialog(wx.Dialog): def __init__(self, config): - pre = wx.PreDialog() - pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Select mask parts"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Select mask parts"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, _(u"Select mask parts"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) self.config = config @@ -2534,7 +2810,7 @@ btn_sizer.Add(self.btn_ok, 0, flag=wx.ALIGN_RIGHT, border=5) btn_sizer.Add(self.btn_cancel, 0, flag=wx.LEFT|wx.ALIGN_RIGHT, border=5) - sizer.AddSizer(btn_sizer, 0, flag=wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT, border=5) + sizer.Add(btn_sizer, 0, flag=wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT, border=5) sizer.AddSpacer(5) self.SetSizer(sizer) @@ -2570,15 +2846,18 @@ def OnClose(self, evt): if self.config.dlg_visible: - Publisher.sendMessage('Disable style', const.SLICE_STATE_SELECT_MASK_PARTS) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_SELECT_MASK_PARTS) evt.Skip() self.Destroy() class FFillSegmentationOptionsDialog(wx.Dialog): - def __init__(self, config): - pre = wx.PreDialog() - pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Region growing"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) - self.PostCreate(pre) + def __init__(self, config, ID=-1, title=_(u"Region growing"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT): + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), ID, title=title, style=style) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), ID, title=title, style=style) self.config = config @@ -2650,20 +2929,38 @@ # Sizer sizer = wx.GridBagSizer(2, 2) - sizer.AddStretchSpacer((0, 0)) + try: + sizer.Add(0, 0, (0, 0)) + except TypeError: + sizer.AddStretchSpacer((0, 0)) sizer.Add(wx.StaticText(self, -1, _(u"Parameters")), (1, 0), (1, 6), flag=wx.LEFT, border=5) - sizer.AddStretchSpacer((2, 0)) + try: + sizer.Add(0, 0, (2, 0)) + except TypeError: + sizer.AddStretchSpacer((2, 0)) sizer.Add(self.panel_target, (3, 0), (1, 6), flag=wx.LEFT|wx.RIGHT|wx.EXPAND, border=7) - sizer.AddStretchSpacer((4, 0)) + try: + sizer.Add(0, 0, (4, 0)) + except TypeError: + sizer.AddStretchSpacer((4, 0)) sizer.Add(self.panel2dcon, (5, 0), (1, 6), flag=wx.LEFT|wx.RIGHT|wx.EXPAND, border=7) - sizer.AddStretchSpacer((6, 0)) + try: + sizer.Add(0, 0, (6, 0)) + except TypeError: + sizer.AddStretchSpacer((6, 0)) sizer.Add(self.panel3dcon, (7, 0), (1, 6), flag=wx.LEFT|wx.RIGHT|wx.EXPAND, border=7) - sizer.AddStretchSpacer((8, 0)) + try: + sizer.Add(0, 0, (8, 0)) + except TypeError: + sizer.AddStretchSpacer((8, 0)) sizer.Add(wx.StaticText(self, -1, _(u"Method")), (9, 0), (1, 1), flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=7) sizer.Add(self.cmb_method, (9, 1), (1, 5), flag=wx.LEFT|wx.RIGHT|wx.EXPAND, border=7) - sizer.AddStretchSpacer((10, 0)) + try: + sizer.Add(0, 0, (10, 0)) + except TypeError: + sizer.AddStretchSpacer((10, 0)) if self.config.method == 'dynamic': self.cmb_method.SetSelection(0) @@ -2679,9 +2976,15 @@ sizer.Add(self.panel_ffill_threshold, (11, 0), (1, 6), flag=wx.LEFT|wx.RIGHT|wx.EXPAND, border=7) self.config.method = 'threshold' - sizer.AddStretchSpacer((12, 0)) + try: + sizer.Add(0, 0, (12, 0)) + except TypeError: + sizer.AddStretchSpacer((12, 0)) sizer.Add(self.close_btn, (13, 0), (1, 6), flag=wx.ALIGN_RIGHT|wx.RIGHT, border=5) - sizer.AddStretchSpacer((14, 0)) + try: + sizer.Add(0, 0, (14, 0)) + except TypeError: + sizer.AddStretchSpacer((14, 0)) self.SetSizer(sizer) sizer.Fit(self) @@ -2746,31 +3049,28 @@ def OnClose(self, evt): if self.config.dlg_visible: - Publisher.sendMessage('Disable style', const.SLICE_STATE_MASK_FFILL) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_MASK_FFILL) evt.Skip() self.Destroy() class CropOptionsDialog(wx.Dialog): - def __init__(self, config): + def __init__(self, config, ID=-1, title=_(u"Crop mask"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT): self.config = config - pre = wx.PreDialog() - - pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Crop mask"),\ - style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) - self.PostCreate(pre) + try: + pre = wx.PreDialog() - self._init_gui() + pre.Create(wx.GetApp().GetTopWindow(), ID, title=title, style=style) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), ID, title=title, style=style) - def UpdateValues(self, pubsub_evt): - if type(pubsub_evt) == list: - data = pubsub_evt - else: - data = pubsub_evt.data + self._init_gui() - xi, xf, yi, yf, zi, zf = data + def UpdateValues(self, limits): + xi, xf, yi, yf, zi, zf = limits self.tx_axial_i.SetValue(str(zi)) self.tx_axial_f.SetValue(str(zf)) @@ -2834,13 +3134,17 @@ gbs_button.Add(btn_cancel, (0,0)) gbs_button.Add(btn_ok, (0,1)) - gbs_principal.AddSizer(gbs, (0,0), flag = wx.ALL|wx.EXPAND) - gbs_principal.AddStretchSpacer((1,0)) - gbs_principal.AddStretchSpacer((2,0)) - gbs_principal.AddSizer(gbs_button, (3,0), flag = wx.ALIGN_RIGHT) + gbs_principal.Add(gbs, (0,0), flag = wx.ALL|wx.EXPAND) + try: + gbs_principal.Add(0, 0, (1, 0)) + gbs_principal.Add(0, 0, (2, 0)) + except TypeError: + gbs_principal.AddStretchSpacer((1, 0)) + gbs_principal.AddStretchSpacer((2, 0)) + gbs_principal.Add(gbs_button, (3,0), flag = wx.ALIGN_RIGHT) box = wx.BoxSizer() - box.AddSizer(gbs_principal, 1, wx.ALL|wx.EXPAND, 10) + box.Add(gbs_principal, 1, wx.ALL|wx.EXPAND, 10) p.SetSizer(box) box.Fit(p) @@ -2861,21 +3165,24 @@ def OnOk(self, evt): self.config.dlg_visible = False Publisher.sendMessage('Crop mask') - Publisher.sendMessage('Disable style', const.SLICE_STATE_CROP_MASK) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_CROP_MASK) evt.Skip() def OnClose(self, evt): self.config.dlg_visible = False - Publisher.sendMessage('Disable style', const.SLICE_STATE_CROP_MASK) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_CROP_MASK) evt.Skip() self.Destroy() class FillHolesAutoDialog(wx.Dialog): def __init__(self, title): - pre = wx.PreDialog() - pre.Create(wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) self._init_gui() @@ -2890,10 +3197,13 @@ self.panel2dcon = Panel2DConnectivity(self, show_orientation=True, style=border_style|wx.TAB_TRAVERSAL) self.panel3dcon = Panel3DConnectivity(self, style=border_style|wx.TAB_TRAVERSAL) - self.panel_target.target_2d.SetValue(1) self.panel2dcon.Enable(1) self.panel3dcon.Enable(0) + self.panel_target.target_2d.SetValue(1) + self.panel2dcon.conect2D_4.SetValue(1) + self.panel3dcon.conect3D_6.SetValue(1) + self.apply_btn = wx.Button(self, wx.ID_APPLY) self.close_btn = wx.Button(self, wx.ID_CLOSE) @@ -2923,7 +3233,7 @@ btn_sizer.Add(self.apply_btn, 0, flag=wx.ALIGN_RIGHT, border=5) btn_sizer.Add(self.close_btn, 0, flag=wx.LEFT|wx.ALIGN_RIGHT, border=5) - sizer.AddSizer(btn_sizer, 0, flag=wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT, border=5) + sizer.Add(btn_sizer, 0, flag=wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT, border=5) sizer.AddSpacer(5) @@ -2945,14 +3255,14 @@ conn = self.panel3dcon.GetConnSelected() orientation = 'VOLUME' - data = { + parameters = { 'target': target, 'conn': conn, 'orientation': orientation, 'size': self.spin_size.GetValue(), } - Publisher.sendMessage("Fill holes automatically", data) + Publisher.sendMessage("Fill holes automatically", parameters=parameters) def OnBtnClose(self, evt): @@ -2967,3 +3277,250 @@ else: self.panel3dcon.Enable(1) self.panel2dcon.Enable(0) + + +class ObjectCalibrationDialog(wx.Dialog): + + def __init__(self, nav_prop): + + self.tracker_id = nav_prop[0] + self.trk_init = nav_prop[1] + self.obj_ref_id = 0 + self.obj_name = None + + self.obj_fiducials = np.full([5, 3], np.nan) + self.obj_orients = np.full([5, 3], np.nan) + + try: + pre = wx.PreDialog() + pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Object calibration"), size=(450, 440), + style=wx.DEFAULT_DIALOG_STYLE | wx.FRAME_FLOAT_ON_PARENT) + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, _(u"Object calibration"), size=(450, 440), + style=wx.DEFAULT_DIALOG_STYLE | wx.FRAME_FLOAT_ON_PARENT) + + self._init_gui() + self.LoadObject() + + def _init_gui(self): + self.interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize()) + self.interactor.Enable(1) + self.ren = vtk.vtkRenderer() + self.interactor.GetRenderWindow().AddRenderer(self.ren) + + # Initialize list of buttons and txtctrls for wx objects + self.btns_coord = [None] * 5 + self.text_actors = [None] * 5 + self.ball_actors = [None] * 5 + self.txt_coord = [list(), list(), list(), list(), list()] + + # ComboBox for tracker reference mode + tooltip = wx.ToolTip(_(u"Choose the object reference mode")) + choice_ref = wx.ComboBox(self, -1, "", size=wx.Size(90, 23), + choices=const.REF_MODE, style=wx.CB_DROPDOWN | wx.CB_READONLY) + choice_ref.SetSelection(self.obj_ref_id) + choice_ref.SetToolTip(tooltip) + choice_ref.Bind(wx.EVT_COMBOBOX, self.OnChoiceRefMode) + choice_ref.Enable(0) + + # Buttons to finish or cancel object registration + tooltip = wx.ToolTip(_(u"Registration done")) + # btn_ok = wx.Button(self, -1, _(u"Done"), size=wx.Size(90, 30)) + btn_ok = wx.Button(self, wx.ID_OK, _(u"Done"), size=wx.Size(90, 30)) + btn_ok.SetToolTip(tooltip) + + extra_sizer = wx.FlexGridSizer(rows=2, cols=1, hgap=5, vgap=30) + extra_sizer.AddMany([choice_ref, + btn_ok]) + + # Push buttons for object fiducials + btns_obj = const.BTNS_OBJ + tips_obj = const.TIPS_OBJ + + for k in btns_obj: + n = list(btns_obj[k].keys())[0] + lab = list(btns_obj[k].values())[0] + self.btns_coord[n] = wx.Button(self, k, label=lab, size=wx.Size(60, 23)) + self.btns_coord[n].SetToolTip(wx.ToolTip(tips_obj[n])) + self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnGetObjectFiducials) + + for m in range(0, 5): + for n in range(0, 3): + self.txt_coord[m].append(wx.StaticText(self, -1, label='-', + style=wx.ALIGN_RIGHT, size=wx.Size(40, 23))) + + coord_sizer = wx.GridBagSizer(hgap=20, vgap=5) + + for m in range(0, 5): + coord_sizer.Add(self.btns_coord[m], pos=wx.GBPosition(m, 0)) + for n in range(0, 3): + coord_sizer.Add(self.txt_coord[m][n], pos=wx.GBPosition(m, n + 1), flag=wx.TOP, border=5) + + group_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=50, vgap=5) + group_sizer.AddMany([(coord_sizer, 0, wx.LEFT, 20), + (extra_sizer, 0, wx.LEFT, 10)]) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + main_sizer.Add(self.interactor, 0, wx.EXPAND) + main_sizer.Add(group_sizer, 0, + wx.EXPAND|wx.GROW|wx.LEFT|wx.TOP|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL, 10) + + self.SetSizer(main_sizer) + main_sizer.Fit(self) + + def ObjectImportDialog(self): + msg = _("Would like to use InVesalius default object?") + if sys.platform == 'darwin': + dlg = wx.MessageDialog(None, "", msg, + wx.ICON_QUESTION | wx.YES_NO) + else: + dlg = wx.MessageDialog(None, msg, + "InVesalius 3", + wx.ICON_QUESTION | wx.YES_NO) + answer = dlg.ShowModal() + dlg.Destroy() + + if answer == wx.ID_YES: + return 1 + else: # answer == wx.ID_NO: + return 0 + + def LoadObject(self): + default = self.ObjectImportDialog() + if not default: + filename = ShowImportMeshFilesDialog() + + if filename: + if filename.lower().endswith('.stl'): + reader = vtk.vtkSTLReader() + elif filename.lower().endswith('.ply'): + reader = vtk.vtkPLYReader() + elif filename.lower().endswith('.obj'): + reader = vtk.vtkOBJReader() + elif filename.lower().endswith('.vtp'): + reader = vtk.vtkXMLPolyDataReader() + else: + wx.MessageBox(_("File format not reconized by InVesalius"), _("Import surface error")) + return + else: + filename = os.path.join(const.OBJ_DIR, "magstim_fig8_coil.stl") + reader = vtk.vtkSTLReader() + else: + filename = os.path.join(const.OBJ_DIR, "magstim_fig8_coil.stl") + reader = vtk.vtkSTLReader() + + if _has_win32api: + self.obj_name = win32api.GetShortPathName(filename).encode(const.FS_ENCODE) + else: + self.obj_name = filename.encode(const.FS_ENCODE) + + reader.SetFileName(self.obj_name) + reader.Update() + polydata = reader.GetOutput() + + if polydata.GetNumberOfPoints() == 0: + wx.MessageBox(_("InVesalius was not able to import this surface"), _("Import surface error")) + + transform = vtk.vtkTransform() + transform.RotateZ(90) + + transform_filt = vtk.vtkTransformPolyDataFilter() + transform_filt.SetTransform(transform) + transform_filt.SetInputData(polydata) + transform_filt.Update() + + normals = vtk.vtkPolyDataNormals() + normals.SetInputData(transform_filt.GetOutput()) + normals.SetFeatureAngle(80) + normals.AutoOrientNormalsOn() + normals.Update() + + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputData(normals.GetOutput()) + mapper.ScalarVisibilityOff() + mapper.ImmediateModeRenderingOn() + + obj_actor = vtk.vtkActor() + obj_actor.SetMapper(mapper) + + self.ball_actors[0], self.text_actors[0] = self.OnCreateObjectText('Left', (0,55,0)) + self.ball_actors[1], self.text_actors[1] = self.OnCreateObjectText('Right', (0,-55,0)) + self.ball_actors[2], self.text_actors[2] = self.OnCreateObjectText('Anterior', (23,0,0)) + + self.ren.AddActor(obj_actor) + self.ren.ResetCamera() + + self.interactor.Render() + + def OnCreateObjectText(self, name, coord): + ball_source = vtk.vtkSphereSource() + ball_source.SetRadius(3) + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputConnection(ball_source.GetOutputPort()) + ball_actor = vtk.vtkActor() + ball_actor.SetMapper(mapper) + ball_actor.SetPosition(coord) + ball_actor.GetProperty().SetColor(1, 0, 0) + + textSource = vtk.vtkVectorText() + textSource.SetText(name) + + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputConnection(textSource.GetOutputPort()) + tactor = vtk.vtkFollower() + tactor.SetMapper(mapper) + tactor.GetProperty().SetColor(1.0, 0.0, 0.0) + tactor.SetScale(5) + ball_position = ball_actor.GetPosition() + tactor.SetPosition(ball_position[0]+5, ball_position[1]+5, ball_position[2]+10) + self.ren.AddActor(tactor) + tactor.SetCamera(self.ren.GetActiveCamera()) + self.ren.AddActor(ball_actor) + return ball_actor, tactor + + def OnGetObjectFiducials(self, evt): + btn_id = list(const.BTNS_OBJ[evt.GetId()].keys())[0] + + if self.trk_init and self.tracker_id: + coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, self.obj_ref_id) + if self.obj_ref_id and btn_id == 4: + coord = coord_raw[2, :] + else: + coord = coord_raw[0, :] + else: + NavigationTrackerWarning(0, 'choose') + + # Update text controls with tracker coordinates + if coord is not None or np.sum(coord) != 0.0: + self.obj_fiducials[btn_id, :] = coord[:3] + self.obj_orients[btn_id, :] = coord[3:] + for n in [0, 1, 2]: + self.txt_coord[btn_id][n].SetLabel(str(round(coord[n], 1))) + if self.text_actors[btn_id]: + self.text_actors[btn_id].GetProperty().SetColor(0.0, 1.0, 0.0) + self.ball_actors[btn_id].GetProperty().SetColor(0.0, 1.0, 0.0) + self.Refresh() + else: + NavigationTrackerWarning(0, 'choose') + + def OnChoiceRefMode(self, evt): + # When ref mode is changed the tracker coordinates are set to nan + # This is for Polhemus FASTRAK wrapper, where the sensor attached to the object can be the stylus (Static + # reference - Selection 0 - index 0 for coordinates) or can be a 3rd sensor (Dynamic reference - Selection 1 - + # index 2 for coordinates) + # I use the index 2 directly here to send to the coregistration module where it is possible to access without + # any conditional statement the correct index of coordinates. + + if evt.GetSelection(): + self.obj_ref_id = 2 + else: + self.obj_ref_id = 0 + for m in range(0, 5): + self.obj_fiducials[m, :] = np.full([1, 3], np.nan) + self.obj_orients[m, :] = np.full([1, 3], np.nan) + for n in range(0, 3): + self.txt_coord[m][n].SetLabel('-') + + def GetValue(self): + return self.obj_fiducials, self.obj_orients, self.obj_ref_id, self.obj_name diff -Nru invesalius-3.1.1/invesalius/gui/dicom_preview_panel.py invesalius-3.1.99991/invesalius/gui/dicom_preview_panel.py --- invesalius-3.1.1/invesalius/gui/dicom_preview_panel.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/dicom_preview_panel.py 2018-08-17 16:41:39.000000000 +0000 @@ -115,7 +115,7 @@ """ Keep the informations and the image used by preview. """ - def __init__(self, id, dicom, title, subtitle): + def __init__(self, id, dicom, title, subtitle, n=0): self.id = id self.dicom = dicom self.title = title @@ -123,12 +123,15 @@ self._preview = None self.selected = False self.filename = "" + self._slice = n @property def preview(self): - if not self._preview: - bmp = wx.Bitmap(self.dicom.image.thumbnail_path, wx.BITMAP_TYPE_PNG) + if isinstance(self.dicom.image.thumbnail_path, list): + bmp = wx.Bitmap(self.dicom.image.thumbnail_path[self._slice], wx.BITMAP_TYPE_PNG) + else: + bmp = wx.Bitmap(self.dicom.image.thumbnail_path, wx.BITMAP_TYPE_PNG) self._preview = bmp.ConvertToImage() return self._preview @@ -269,7 +272,10 @@ def OnEnter(self, evt): if not self.select_on: #c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DHILIGHT) - c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) + try: + c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) + except AttributeError: + c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) self.SetBackgroundColour(c) def OnLeave(self, evt): @@ -280,7 +286,7 @@ def OnSelect(self, evt): shift_pressed = False - if evt.m_shiftDown: + if evt.shiftDown: shift_pressed = True dicom_id = self.dicom_info.id @@ -317,7 +323,10 @@ def Select(self, on=True): if self.select_on: - c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) + try: + c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) + except AttributeError: + c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) else: c = (PREVIEW_BACKGROUND) self.SetBackgroundColour(c) @@ -352,10 +361,10 @@ self.grid = wx.GridSizer(rows=NROWS, cols=NCOLS, vgap=3, hgap=3) sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.AddSizer(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) + sizer.Add(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) background_sizer = wx.BoxSizer(wx.HORIZONTAL) - background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) + background_sizer.Add(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) self.SetSizer(background_sizer) background_sizer.Fit(self) @@ -371,8 +380,8 @@ def _Add_Panels_Preview(self): self.previews = [] - for i in xrange(NROWS): - for j in xrange(NCOLS): + for i in range(NROWS): + for j in range(NCOLS): p = Preview(self) p.Bind(EVT_PREVIEW_CLICK, self.OnSelect) #if (i == j == 0): @@ -429,7 +438,7 @@ initial = self.displayed_position * NCOLS final = initial + NUM_PREVIEWS if len(self.files) < final: - for i in xrange(final-len(self.files)): + for i in range(final-len(self.files)): try: self.previews[-i-1].Hide() except IndexError: @@ -438,7 +447,7 @@ self.nhidden_last_display = final-len(self.files) else: if self.nhidden_last_display: - for i in xrange(self.nhidden_last_display): + for i in range(self.nhidden_last_display): try: self.previews[-i-1].Show() except IndexError: @@ -489,10 +498,10 @@ self.grid = wx.GridSizer(rows=NROWS, cols=NCOLS, vgap=3, hgap=3) sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.AddSizer(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) + sizer.Add(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) background_sizer = wx.BoxSizer(wx.HORIZONTAL) - background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) + background_sizer.Add(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) self.SetSizer(background_sizer) background_sizer.Fit(self) @@ -508,8 +517,8 @@ def _Add_Panels_Preview(self): self.previews = [] - for i in xrange(NROWS): - for j in xrange(NCOLS): + for i in range(NROWS): + for j in range(NCOLS): p = Preview(self) p.Bind(EVT_PREVIEW_CLICK, self.OnPreviewClick) #p.Hide() @@ -539,11 +548,22 @@ dicom_files = group.GetHandSortedList() n = 0 for dicom in dicom_files: - info = DicomInfo(n, dicom, - _("Image %d") % (dicom.image.number), - "%.2f" % (dicom.image.position[2])) - self.files.append(info) - n+=1 + if isinstance(dicom.image.thumbnail_path, list): + _slice = 0 + for thumbnail in dicom.image.thumbnail_path: + print(thumbnail) + info = DicomInfo(n, dicom, + _("Image %d") % (n), + "%.2f" % (dicom.image.position[2]), _slice) + self.files.append(info) + n+=1 + _slice += 1 + else: + info = DicomInfo(n, dicom, + _("Image %d") % (dicom.image.number), + "%.2f" % (dicom.image.position[2])) + self.files.append(info) + n+=1 scroll_range = len(self.files)/NCOLS if scroll_range * NCOLS < len(self.files): @@ -560,12 +580,23 @@ dicom_files = group.GetHandSortedList() n = 0 for dicom in dicom_files: - info = DicomInfo(n, dicom, - _("Image %d") % (dicom.image.number), - "%.2f" % (dicom.image.position[2]), - ) - self.files.append(info) - n+=1 + if isinstance(dicom.image.thumbnail_path, list): + _slice = 0 + for thumbnail in dicom.image.thumbnail_path: + print(thumbnail) + info = DicomInfo(n, dicom, + _("Image %d") % int(n), + "%.2f" % (dicom.image.position[2]), _slice) + self.files.append(info) + n+=1 + _slice += 1 + else: + info = DicomInfo(n, dicom, + _("Image %d") % int(dicom.image.number), + "%.2f" % (dicom.image.position[2]), + ) + self.files.append(info) + n+=1 scroll_range = len(self.files)/NCOLS if scroll_range * NCOLS < len(self.files): @@ -578,7 +609,7 @@ initial = self.displayed_position * NCOLS final = initial + NUM_PREVIEWS if len(self.files) < final: - for i in xrange(final-len(self.files)): + for i in range(final-len(self.files)): try: self.previews[-i-1].Hide() except IndexError: @@ -586,7 +617,7 @@ self.nhidden_last_display = final-len(self.files) else: if self.nhidden_last_display: - for i in xrange(self.nhidden_last_display): + for i in range(self.nhidden_last_display): try: self.previews[-i-1].Show() except IndexError: @@ -623,7 +654,7 @@ self.first_selection = dicom_id self.last_selection = dicom_id - for i in xrange(len(self.files)): + for i in range(len(self.files)): if i == dicom_id: self.files[i].selected = True @@ -641,7 +672,7 @@ self.selected_panel.select_on = self.selected_panel is evt.GetEventObject() if self.first_selection != self.last_selection: - for i in xrange(len(self.files)): + for i in range(len(self.files)): if i >= self.first_selection and i <= self.last_selection: self.files[i].selected = True else: @@ -655,8 +686,8 @@ self.selected_dicom = self.selected_panel.dicom_info self.GetEventHandler().ProcessEvent(my_evt) - Publisher.sendMessage("Selected Import Images", [self.first_selection, \ - self.last_selection]) + Publisher.sendMessage("Selected Import Images", + selection=(self.first_selection, self.last_selection)) def OnScroll(self, evt=None): if evt: @@ -747,7 +778,7 @@ maxValue=99, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS) slider.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) - slider.SetTickFreq(1, 1) + slider.SetTickFreq(1) self.slider = slider checkbox = wx.CheckBox(self, -1, _("Auto-play")) @@ -803,14 +834,20 @@ def SetDicomGroup(self, group): self.dicom_list = group.GetHandSortedList() self.current_index = 0 - self.nimages = len(self.dicom_list) + if len(self.dicom_list) > 1: + self.nimages = len(self.dicom_list) + else: + self.nimages = self.dicom_list[0].image.number_of_frames # GUI self.slider.SetMax(self.nimages-1) self.slider.SetValue(0) self.ShowSlice() def ShowSlice(self, index = 0): - dicom = self.dicom_list[index] + try: + dicom = self.dicom_list[index] + except IndexError: + dicom = self.dicom_list[0] # UPDATE GUI ## Text related to size @@ -845,28 +882,41 @@ dicom.acquisition.time) self.text_acquisition.SetValue(value) - rdicom = vtkgdcm.vtkGDCMImageReader() - if _has_win32api: - rdicom.SetFileName(win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE)) + if isinstance(dicom.image.thumbnail_path, list): + reader = vtk.vtkPNGReader() + if _has_win32api: + reader.SetFileName(win32api.GetShortPathName(dicom.image.thumbnail_path[index]).encode(const.FS_ENCODE)) + else: + reader.SetFileName(dicom.image.thumbnail_path[index]) + reader.Update() + + image = reader.GetOutput() + else: - rdicom.SetFileName(dicom.image.file) - rdicom.Update() + rdicom = vtkgdcm.vtkGDCMImageReader() + if _has_win32api: + rdicom.SetFileName(win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE)) + else: + rdicom.SetFileName(dicom.image.file) + rdicom.Update() + + # ADJUST CONTRAST + window_level = dicom.image.level + window_width = dicom.image.window + colorer = vtk.vtkImageMapToWindowLevelColors() + colorer.SetInputConnection(rdicom.GetOutputPort()) + colorer.SetWindow(float(window_width)) + colorer.SetLevel(float(window_level)) + colorer.Update() - # ADJUST CONTRAST - window_level = dicom.image.level - window_width = dicom.image.window - colorer = vtk.vtkImageMapToWindowLevelColors() - colorer.SetInputConnection(rdicom.GetOutputPort()) - colorer.SetWindow(float(window_width)) - colorer.SetLevel(float(window_level)) - colorer.Update() + image = colorer.GetOutput() if self.actor is None: self.actor = vtk.vtkImageActor() self.renderer.AddActor(self.actor) # PLOT IMAGE INTO VIEWER - self.actor.SetInputData(colorer.GetOutput()) + self.actor.SetInputData(image) self.renderer.ResetCamera() self.interactor.Render() diff -Nru invesalius-3.1.1/invesalius/gui/frame.py invesalius-3.1.99991/invesalius/gui/frame.py --- invesalius-3.1.1/invesalius/gui/frame.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/frame.py 2018-08-17 16:41:39.000000000 +0000 @@ -24,6 +24,12 @@ import webbrowser import wx + +try: + from wx.adv import TaskBarIcon as wx_TaskBarIcon +except ImportError: + from wx import TaskBarIcon as wx_TaskBarIcon + import wx.aui from wx.lib.pubsub import pub as Publisher import wx.lib.agw.toasterbox as TB @@ -37,7 +43,7 @@ import invesalius.gui.dialogs as dlg import invesalius.gui.import_panel as imp import invesalius.gui.import_bitmap_panel as imp_bmp -import invesalius.gui.import_network_panel as imp_net +# import invesalius.gui.import_network_panel as imp_net import invesalius.project as prj import invesalius.session as ses import invesalius.utils as utils @@ -188,11 +194,11 @@ MaximizeButton(False).Floatable(True). Caption(caption).CaptionVisible(True)) - ncaption = _("Retrieve DICOM from PACS") - aui_manager.AddPane(imp_net.Panel(self), wx.aui.AuiPaneInfo(). - Name("Retrieve").Centre().Hide(). - MaximizeButton(True).Floatable(True). - Caption(ncaption).CaptionVisible(True)) + # ncaption = _("Retrieve DICOM from PACS") + # aui_manager.AddPane(imp_net.Panel(self), wx.aui.AuiPaneInfo(). + # Name("Retrieve").Centre().Hide(). + # MaximizeButton(True).Floatable(True). + # Caption(ncaption).CaptionVisible(True)) # Add toolbars to manager # This is pretty tricky -- order on win32 is inverted when @@ -242,14 +248,14 @@ # TODO: Allow saving and restoring perspectives self.perspective_all = aui_manager.SavePerspective() - def _BeginBusyCursor(self, pubsub_evt): + def _BeginBusyCursor(self): """ Start busy cursor. Note: _EndBusyCursor should be called after. """ wx.BeginBusyCursor() - def _EndBusyCursor(self, pubsub_evt): + def _EndBusyCursor(self): """ End busy cursor. Note: _BeginBusyCursor should have been called previously. @@ -260,15 +266,16 @@ #no matching wxBeginBusyCursor() for wxEndBusyCursor() pass - def _Exit(self, pubsub_evt): + def _Exit(self): """ Exit InVesalius. """ + self.aui_manager.UnInit() self.Destroy() if hasattr(sys,"frozen") and sys.platform == 'darwin': sys.exit(0) - def _HideContentPanel(self, pubsub_evt): + def _HideContentPanel(self): """ Hide data and tasks panels. """ @@ -277,7 +284,7 @@ aui_manager.GetPane("Tasks").Show(1) aui_manager.Update() - def _HideImportPanel(self, evt_pubsub): + def _HideImportPanel(self): """ Hide import panel and show tasks. """ @@ -287,39 +294,37 @@ aui_manager.GetPane("Tasks").Show(1) aui_manager.Update() - def _HideTask(self, pubsub_evt): + def _HideTask(self): """ Hide task panel. """ self.aui_manager.GetPane("Tasks").Hide() self.aui_manager.Update() - def _SetProjectName(self, pubsub_evt): + def _SetProjectName(self, proj_name=""): """ Set project name into frame's title. """ - proj_name = pubsub_evt.data - if not(proj_name): self.SetTitle("InVesalius 3") else: self.SetTitle("%s - InVesalius 3"%(proj_name)) - def _ShowContentPanel(self, evt_pubsub): + def _ShowContentPanel(self): """ Show viewers and task, hide import panel. """ Publisher.sendMessage("Set layout button full") aui_manager = self.aui_manager aui_manager.GetPane("Import").Show(0) - + aui_manager.GetPane("ImportBMP").Show(0) aui_manager.GetPane("Data").Show(1) aui_manager.GetPane("Tasks").Show(1) aui_manager.Update() - def _ShowImportNetwork(self, evt_pubsub): + def _ShowImportNetwork(self): """ Show viewers and task, hide import panel. """ @@ -331,7 +336,7 @@ aui_manager.GetPane("Import").Show(0) aui_manager.Update() - def _ShowImportBitmap(self, evt_pubsub): + def _ShowImportBitmap(self): """ Show viewers and task, hide import panel. """ @@ -343,15 +348,14 @@ aui_manager.GetPane("Import").Show(0) aui_manager.Update() - def _ShowHelpMessage(self, evt_pubsub): + def _ShowHelpMessage(self, message): aui_manager = self.aui_manager pos = aui_manager.GetPane("Data").window.GetScreenPosition() - msg = evt_pubsub.data - self.mw = MessageWatershed(self, msg) + self.mw = MessageWatershed(self, message) self.mw.SetPosition(pos) self.mw.Show() - def _ShowImportPanel(self, evt_pubsub): + def _ShowImportPanel(self): """ Show only DICOM import panel. as dicom """ Publisher.sendMessage("Set layout button data only") @@ -361,14 +365,14 @@ aui_manager.GetPane("Tasks").Show(0) aui_manager.Update() - def _ShowTask(self, pubsub_evt): + def _ShowTask(self): """ Show task panel. """ self.aui_manager.GetPane("Tasks").Show() self.aui_manager.Update() - def _UpdateAUI(self, pubsub_evt): + def _UpdateAUI(self): """ Refresh AUI panels/data. """ @@ -450,19 +454,19 @@ self.OnReorientImg() elif id == const.ID_THRESHOLD_SEGMENTATION: - Publisher.sendMessage("Show panel", const.ID_THRESHOLD_SEGMENTATION) + Publisher.sendMessage("Show panel", panel_id=const.ID_THRESHOLD_SEGMENTATION) Publisher.sendMessage('Disable actual style') - Publisher.sendMessage('Enable style', const.STATE_DEFAULT) + Publisher.sendMessage('Enable style', style=const.STATE_DEFAULT) elif id == const.ID_MANUAL_SEGMENTATION: - Publisher.sendMessage("Show panel", const.ID_MANUAL_SEGMENTATION) + Publisher.sendMessage("Show panel", panel_id=const.ID_MANUAL_SEGMENTATION) Publisher.sendMessage('Disable actual style') - Publisher.sendMessage('Enable style', const.SLICE_STATE_EDITOR) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_EDITOR) elif id == const.ID_WATERSHED_SEGMENTATION: - Publisher.sendMessage("Show panel", const.ID_WATERSHED_SEGMENTATION) + Publisher.sendMessage("Show panel", panel_id=const.ID_WATERSHED_SEGMENTATION) Publisher.sendMessage('Disable actual style') - Publisher.sendMessage('Enable style', const.SLICE_STATE_WATERSHED) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_WATERSHED) elif id == const.ID_FLOODFILL_MASK: self.OnFillHolesManually() @@ -493,14 +497,20 @@ elif id == const.ID_CROP_MASK: self.OnCropMask() + elif id == const.ID_CREATE_SURFACE: + Publisher.sendMessage('Open create surface dialog') + + elif id == const.ID_CREATE_MASK: + Publisher.sendMessage('New mask from shortcut') + def OnInterpolatedSlices(self, status): - Publisher.sendMessage('Set interpolated slices', status) + Publisher.sendMessage('Set interpolated slices', flag=status) def OnNavigationMode(self, status): if status and self._show_navigator_message and sys.platform != 'win32': wx.MessageBox(_('Currently the Navigation mode is only working on Windows'), 'Info', wx.OK | wx.ICON_INFORMATION) self._show_navigator_message = False - Publisher.sendMessage('Set navigation mode', status) + Publisher.sendMessage('Set navigation mode', status=status) if not status: Publisher.sendMessage('Remove sensors ID') @@ -538,9 +548,10 @@ ses.Session().surface_interpolation = values[const.SURFACE_INTERPOLATION] ses.Session().language = values[const.LANGUAGE] ses.Session().slice_interpolation = values[const.SLICE_INTERPOLATION] + ses.Session().WriteSessionFile() Publisher.sendMessage('Remove Volume') - Publisher.sendMessage('Reset Reaycasting') + Publisher.sendMessage('Reset Raycasting') Publisher.sendMessage('Update Slice Interpolation') Publisher.sendMessage('Update Slice Interpolation MenuBar') Publisher.sendMessage('Update Navigation Mode MenuBar') @@ -556,7 +567,7 @@ """ Save project. """ - Publisher.sendMessage('Show save dialog', False) + Publisher.sendMessage('Show save dialog', save_as=False) def ShowGettingStarted(self): """ @@ -582,7 +593,7 @@ """ Show import Analyze, NiFTI1 or PAR/REC dialog. """ - Publisher.sendMessage('Show import other files dialog', id_file) + Publisher.sendMessage('Show import other files dialog', id_type=id_file) def ShowRetrieveDicomPanel(self): Publisher.sendMessage('Show retrieve dicom panel') @@ -597,20 +608,20 @@ """ Show save as dialog. """ - Publisher.sendMessage('Show save dialog', True) + Publisher.sendMessage('Show save dialog', save_as=True) def ShowBitmapImporter(self): """ Tiff, BMP, JPEG and PNG """ - Publisher.sendMessage('Show bitmap dialog', True) + Publisher.sendMessage('Show bitmap dialog') def FlipVolume(self, axis): - Publisher.sendMessage('Flip volume', axis) + Publisher.sendMessage('Flip volume', axis=axis) Publisher.sendMessage('Reload actual slice') def SwapAxes(self, axes): - Publisher.sendMessage('Swap volume axes', axes) + Publisher.sendMessage('Swap volume axes', axes=axes) Publisher.sendMessage('Update scroll') Publisher.sendMessage('Reload actual slice') @@ -628,31 +639,31 @@ Publisher.sendMessage('Reload actual slice') def OnReorientImg(self): - Publisher.sendMessage('Enable style', const.SLICE_STATE_REORIENT) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_REORIENT) rdlg = dlg.ReorientImageDialog() rdlg.Show() def OnFillHolesManually(self): - Publisher.sendMessage('Enable style', const.SLICE_STATE_MASK_FFILL) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_MASK_FFILL) def OnFillHolesAutomatically(self): fdlg = dlg.FillHolesAutoDialog(_(u"Fill holes automatically")) fdlg.Show() def OnRemoveMaskParts(self): - Publisher.sendMessage('Enable style', const.SLICE_STATE_REMOVE_MASK_PARTS) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_REMOVE_MASK_PARTS) def OnSelectMaskParts(self): - Publisher.sendMessage('Enable style', const.SLICE_STATE_SELECT_MASK_PARTS) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_SELECT_MASK_PARTS) def OnFFillSegmentation(self): - Publisher.sendMessage('Enable style', const.SLICE_STATE_FFILL_SEGMENTATION) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_FFILL_SEGMENTATION) def OnInterpolatedSlices(self, status): - Publisher.sendMessage('Set interpolated slices', status) - + Publisher.sendMessage('Set interpolated slices', flag=status) + def OnCropMask(self): - Publisher.sendMessage('Enable style', const.SLICE_STATE_CROP_MASK) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_CROP_MASK) # ------------------------------------------------------------------ # ------------------------------------------------------------------ @@ -689,7 +700,9 @@ const.ID_MANUAL_SEGMENTATION, const.ID_WATERSHED_SEGMENTATION, const.ID_THRESHOLD_SEGMENTATION, - const.ID_FLOODFILL_SEGMENTATION,] + const.ID_FLOODFILL_SEGMENTATION, + const.ID_CREATE_SURFACE, + const.ID_CREATE_MASK] self.__init_items() self.__bind_events() @@ -707,7 +720,7 @@ sub(self.OnEnableState, "Enable state project") sub(self.OnEnableUndo, "Enable undo") sub(self.OnEnableRedo, "Enable redo") - sub(self.OnEnableNavigation, "Navigation Status") + sub(self.OnEnableNavigation, "Navigation status") sub(self.OnAddMask, "Add mask") sub(self.OnRemoveMasks, "Remove masks") @@ -738,7 +751,7 @@ file_menu.AppendMenu(const.ID_IMPORT_OTHERS_FILES, _("Import other files..."), others_file_menu) app(const.ID_PROJECT_OPEN, _("Open project...\tCtrl+O")) app(const.ID_PROJECT_SAVE, _("Save project\tCtrl+S")) - app(const.ID_PROJECT_SAVE_AS, _("Save project as...")) + app(const.ID_PROJECT_SAVE_AS, _("Save project as...\tCtrl+Shift+S")) app(const.ID_PROJECT_CLOSE, _("Close project")) file_menu.AppendSeparator() #app(const.ID_PROJECT_INFO, _("Project Information...")) @@ -780,7 +793,11 @@ # Mask Menu mask_menu = wx.Menu() - self.bool_op_menu = mask_menu.Append(const.ID_BOOLEAN_MASK, _(u"Boolean operations")) + + self.new_mask_menu = mask_menu.Append(const.ID_CREATE_MASK, _(u"New\tCtrl+Shift+M")) + self.new_mask_menu.Enable(False) + + self.bool_op_menu = mask_menu.Append(const.ID_BOOLEAN_MASK, _(u"Boolean operations\tCtrl+Shift+B")) self.bool_op_menu.Enable(False) self.clean_mask_menu = mask_menu.Append(const.ID_CLEAN_MASK, _(u"Clean Mask\tCtrl+Shift+A")) @@ -788,18 +805,18 @@ mask_menu.AppendSeparator() - self.fill_hole_mask_menu = mask_menu.Append(const.ID_FLOODFILL_MASK, _(u"Fill holes manually")) + self.fill_hole_mask_menu = mask_menu.Append(const.ID_FLOODFILL_MASK, _(u"Fill holes manually\tCtrl+Shift+H")) self.fill_hole_mask_menu.Enable(False) - self.fill_hole_auto_menu = mask_menu.Append(const.ID_FILL_HOLE_AUTO, _(u"Fill holes automatically")) + self.fill_hole_auto_menu = mask_menu.Append(const.ID_FILL_HOLE_AUTO, _(u"Fill holes automatically\tCtrl+Shift+J")) self.fill_hole_mask_menu.Enable(False) mask_menu.AppendSeparator() - self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts")) + self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts\tCtrl+Shift+K")) self.remove_mask_part_menu.Enable(False) - self.select_mask_part_menu = mask_menu.Append(const.ID_SELECT_MASK_PART, _(u"Select parts")) + self.select_mask_part_menu = mask_menu.Append(const.ID_SELECT_MASK_PART, _(u"Select parts\tCtrl+Shift+L")) self.select_mask_part_menu.Enable(False) mask_menu.AppendSeparator() @@ -809,12 +826,17 @@ # Segmentation Menu segmentation_menu = wx.Menu() - self.threshold_segmentation = segmentation_menu.Append(const.ID_THRESHOLD_SEGMENTATION, _(u"Threshold")) - self.manual_segmentation = segmentation_menu.Append(const.ID_MANUAL_SEGMENTATION, _(u"Manual segmentation")) - self.watershed_segmentation = segmentation_menu.Append(const.ID_WATERSHED_SEGMENTATION, _(u"Watershed")) - self.ffill_segmentation = segmentation_menu.Append(const.ID_FLOODFILL_SEGMENTATION, _(u"Region growing")) + self.threshold_segmentation = segmentation_menu.Append(const.ID_THRESHOLD_SEGMENTATION, _(u"Threshold\tCtrl+Shift+T")) + self.manual_segmentation = segmentation_menu.Append(const.ID_MANUAL_SEGMENTATION, _(u"Manual segmentation\tCtrl+Shift+E")) + self.watershed_segmentation = segmentation_menu.Append(const.ID_WATERSHED_SEGMENTATION, _(u"Watershed\tCtrl+Shift+W")) + self.ffill_segmentation = segmentation_menu.Append(const.ID_FLOODFILL_SEGMENTATION, _(u"Region growing\tCtrl+Shift+G")) self.ffill_segmentation.Enable(False) + # Surface Menu + surface_menu = wx.Menu() + self.create_surface = surface_menu.Append(const.ID_CREATE_SURFACE, (u"New\tCtrl+Shift+C")) + self.create_surface.Enable(False) + # Image menu image_menu = wx.Menu() @@ -837,7 +859,8 @@ reorient_menu.Enable(False) tools_menu.AppendMenu(-1, _(u'Image'), image_menu) tools_menu.AppendMenu(-1, _(u"Mask"), mask_menu) - tools_menu.AppendMenu(-1, _("Segmentation"), segmentation_menu) + tools_menu.AppendMenu(-1, _(u"Segmentation"), segmentation_menu) + tools_menu.AppendMenu(-1, _(u"Surface"), surface_menu) #View @@ -933,20 +956,19 @@ else: return False - def OnUpdateSliceInterpolation(self, pubsub_evt): + def OnUpdateSliceInterpolation(self): v = self.SliceInterpolationStatus() self.view_menu.Check(const.ID_VIEW_INTERPOLATED, v) - def OnUpdateNavigationMode(self, pubsub_evt): + def OnUpdateNavigationMode(self): v = self.NavigationModeStatus() self.mode_menu.Check(const.ID_MODE_NAVIGATION, v) - def OnEnableState(self, pubsub_evt): + def OnEnableState(self, state): """ Based on given state, enables or disables menu items which depend if project is open or not. """ - state = pubsub_evt.data if state: self.SetStateProjectOpen() else: @@ -966,41 +988,38 @@ for item in self.enable_items: self.Enable(item, True) - def OnEnableUndo(self, pubsub_evt): - value = pubsub_evt.data + def OnEnableUndo(self, value): if value: self.FindItemById(wx.ID_UNDO).Enable(True) else: self.FindItemById(wx.ID_UNDO).Enable(False) - def OnEnableRedo(self, pubsub_evt): - value = pubsub_evt.data + def OnEnableRedo(self, value): if value: self.FindItemById(wx.ID_REDO).Enable(True) else: self.FindItemById(wx.ID_REDO).Enable(False) - def OnEnableNavigation(self, pubsub_evt): + def OnEnableNavigation(self, status): """ Disable mode menu when navigation is on. - :param pubsub_evt: Navigation status + :param status: Navigation status """ - value = pubsub_evt.data + value = status if value: self.FindItemById(const.ID_MODE_NAVIGATION).Enable(False) else: self.FindItemById(const.ID_MODE_NAVIGATION).Enable(True) - def OnAddMask(self, pubsub_evt): + def OnAddMask(self, mask): self.num_masks += 1 self.bool_op_menu.Enable(self.num_masks >= 2) - def OnRemoveMasks(self, pubsub_evt): - self.num_masks -= len(pubsub_evt.data) + def OnRemoveMasks(self, mask_indexes): + self.num_masks -= len(mask_indexes) self.bool_op_menu.Enable(self.num_masks >= 2) - def OnShowMask(self, pubsub_evt): - index, value = pubsub_evt.data + def OnShowMask(self, index, value): self.clean_mask_menu.Enable(value) self.crop_mask_menu.Enable(value) @@ -1028,7 +1047,7 @@ sub = Publisher.subscribe sub(self._Layout, 'ProgressBar Reposition') - def _Layout(self, evt_pubsub=None): + def _Layout(self): """ Compute new size and position, according to parent resize """ @@ -1078,12 +1097,11 @@ sub(self._SetProgressValue, 'Update status in GUI') sub(self._SetProgressLabel, 'Update status text in GUI') - def _SetProgressValue(self, pubsub_evt): + def _SetProgressValue(self, value, label): """ Set both percentage value in gauge and text progress label in status. """ - value, label = pubsub_evt.data self.progress_bar.SetPercentage(value) self.SetStatusText(label, 0) if (int(value) >= 99): @@ -1097,18 +1115,17 @@ except(wx._core.PyAssertionError): utils.debug("wx._core.PyAssertionError") - def _SetProgressLabel(self, pubsub_evt): + def _SetProgressLabel(self, label): """ Set text progress label. """ - label = pubsub_evt.data self.SetStatusText(label, 0) # ------------------------------------------------------------------ # ------------------------------------------------------------------ # ------------------------------------------------------------------ -class TaskBarIcon(wx.TaskBarIcon): +class TaskBarIcon(wx_TaskBarIcon): """ TaskBarIcon has different behaviours according to the platform: - win32: Show icon on "Notification Area" (near clock) @@ -1116,7 +1133,7 @@ - linux2: Show icon on "Notification Area" (near clock) """ def __init__(self, parent=None): - wx.TaskBarIcon.__init__(self) + wx_TaskBarIcon.__init__(self) self.frame = parent icon = wx.Icon(os.path.join(const.ICON_DIR, "invesalius.ico"), @@ -1236,12 +1253,11 @@ # "Print medical image...", # BMP_PRINT) - def _EnableState(self, pubsub_evt): + def _EnableState(self, state): """ Based on given state, enable or disable menu items which depend if project is open or not. """ - state = pubsub_evt.data if state: self.SetStateProjectOpen() else: @@ -1289,7 +1305,7 @@ const.STATE_SPIN, const.STATE_ZOOM_SL, const.STATE_ZOOM, const.STATE_MEASURE_DISTANCE, - const.STATE_MEASURE_ANGLE,] + const.STATE_MEASURE_ANGLE] #const.STATE_ANNOTATE] self.__init_items() self.__bind_events() @@ -1419,19 +1435,18 @@ # bitmap = BMP_ANNOTATE, # kind = wx.ITEM_CHECK) - def _EnableState(self, pubsub_evt): + def _EnableState(self, state): """ Based on given state, enable or disable menu items which depend if project is open or not. """ - state = pubsub_evt.data if state: self.SetStateProjectOpen() else: self.SetStateProjectClose() self.Refresh() - def _UntoggleAllItems(self, pubsub_evt=None): + def _UntoggleAllItems(self): """ Untoggle all items on toolbar. """ @@ -1441,14 +1456,14 @@ self.ToggleTool(id, False) self.Refresh() - def _ToggleLinearMeasure(self, pubsub_evt): + def _ToggleLinearMeasure(self): """ Force measure distance tool to be toggled and bind pubsub events to other classes whici are interested on this. """ id = const.STATE_MEASURE_DISTANCE self.ToggleTool(id, True) - Publisher.sendMessage('Enable style', id) + Publisher.sendMessage('Enable style', style=id) Publisher.sendMessage('Untoggle slice toolbar items') for item in const.TOOL_STATES: state = self.GetToolToggled(item) @@ -1456,14 +1471,14 @@ self.ToggleTool(item, False) - def _ToggleAngularMeasure(self, pubsub_evt): + def _ToggleAngularMeasure(self): """ Force measure angle tool to be toggled and bind pubsub events to other classes which are interested on this. """ id = const.STATE_MEASURE_ANGLE self.ToggleTool(id, True) - Publisher.sendMessage('Enable style', id) + Publisher.sendMessage('Enable style', style=id) Publisher.sendMessage('Untoggle slice toolbar items') for item in const.TOOL_STATES: state = self.GetToolToggled(item) @@ -1482,10 +1497,10 @@ Publisher.sendMessage('Fold measure task') if state: - Publisher.sendMessage('Enable style', id) + Publisher.sendMessage('Enable style', style=id) Publisher.sendMessage('Untoggle slice toolbar items') else: - Publisher.sendMessage('Disable style', id) + Publisher.sendMessage('Disable style', style=id) for item in const.TOOL_STATES: state = self.GetToolToggled(item) @@ -1493,8 +1508,7 @@ self.ToggleTool(item, False) evt.Skip() - def ToggleItem(self, evt): - _id, value = evt.data + def ToggleItem(self, _id, value): if _id in self.enable_items: self.ToggleTool(_id, value) self.Refresh() @@ -1587,12 +1601,11 @@ """ self.Bind(wx.EVT_TOOL, self.OnToggle) - def _EnableState(self, pubsub_evt): + def _EnableState(self, state): """ Based on given state, enable or disable menu items which depend if project is open or not. """ - state = pubsub_evt.data if state: self.SetStateProjectOpen() else: @@ -1600,7 +1613,7 @@ self._UntoggleAllItems() self.Refresh() - def _UntoggleAllItems(self, pubsub_evt=None): + def _UntoggleAllItems(self): """ Untoggle all items on toolbar. """ @@ -1610,16 +1623,15 @@ self.ToggleTool(id, False) if id == const.SLICE_STATE_CROSS: msg = 'Set cross visibility' - Publisher.sendMessage(msg, 0) + Publisher.sendMessage(msg, visibility=0) self.Refresh() - def OnToggle(self, evt): + def OnToggle(self, evt=None, id=None): """ Update status of other items on toolbar (only one item should be toggle each time). """ - if hasattr(evt, 'data'): - id = evt.data + if id is not None: if not self.GetToolToggled(id): self.ToggleTool(id, True) self.Refresh() @@ -1630,10 +1642,10 @@ state = self.GetToolToggled(id) if state: - Publisher.sendMessage('Enable style', id) + Publisher.sendMessage('Enable style', style=id) Publisher.sendMessage('Untoggle object toolbar items') else: - Publisher.sendMessage('Disable style', id) + Publisher.sendMessage('Disable style', style=id) for item in self.enable_items: state = self.GetToolToggled(item) @@ -1645,8 +1657,7 @@ ##print ">>>", self.sst.IsToggled() #print ">>>", self.sst.GetState() - def ToggleItem(self, evt): - _id, value = evt.data + def ToggleItem(self, _id, value): if _id in self.enable_items: self.ToggleTool(_id, value) self.Refresh() @@ -1759,25 +1770,24 @@ wx.ITEM_NORMAL, short_help_string= _("Hide text")) - def _EnableState(self, pubsub_evt): + def _EnableState(self, state): """ Based on given state, enable or disable menu items which depend if project is open or not. """ - state = pubsub_evt.data if state: self.SetStateProjectOpen() else: self.SetStateProjectClose() self.Refresh() - def _SetLayoutWithoutTask(self, pubsub_evt): + def _SetLayoutWithoutTask(self): """ Set item bitmap to task panel hiden. """ self.SetToolNormalBitmap(ID_LAYOUT,self.BMP_WITHOUT_MENU) - def _SetLayoutWithTask(self, pubsub_evt): + def _SetLayoutWithTask(self): """ Set item bitmap to task panel shown. """ @@ -1932,25 +1942,24 @@ self.EnableTool(wx.ID_UNDO, False) self.EnableTool(wx.ID_REDO, False) - def _EnableState(self, pubsub_evt): + def _EnableState(self, state): """ Based on given state, enable or disable menu items which depend if project is open or not. """ - state = pubsub_evt.data if state: self.SetStateProjectOpen() else: self.SetStateProjectClose() self.Refresh() - def _SetLayoutWithoutTask(self, pubsub_evt): + def _SetLayoutWithoutTask(self): """ Set item bitmap to task panel hiden. """ self.SetToolNormalBitmap(ID_LAYOUT,self.BMP_WITHOUT_MENU) - def _SetLayoutWithTask(self, pubsub_evt): + def _SetLayoutWithTask(self): """ Set item bitmap to task panel shown. """ @@ -2028,16 +2037,14 @@ Publisher.sendMessage('Update AUI') self.ontool_text = True - def OnEnableUndo(self, pubsub_evt): - value = pubsub_evt.data + def OnEnableUndo(self, value): if value: self.EnableTool(wx.ID_UNDO, True) else: self.EnableTool(wx.ID_UNDO, False) self.Refresh() - def OnEnableRedo(self, pubsub_evt): - value = pubsub_evt.data + def OnEnableRedo(self, value): if value: self.EnableTool(wx.ID_REDO, True) else: diff -Nru invesalius-3.1.1/invesalius/gui/import_bitmap_panel.py invesalius-3.1.99991/invesalius/gui/import_bitmap_panel.py --- invesalius-3.1.1/invesalius/gui/import_bitmap_panel.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/import_bitmap_panel.py 2018-08-17 16:41:39.000000000 +0000 @@ -101,7 +101,7 @@ self.combo_interval.SetSelection(0) inner_sizer = wx.BoxSizer(wx.HORIZONTAL) - inner_sizer.AddSizer(btnsizer, 0, wx.LEFT|wx.TOP, 5) + inner_sizer.Add(btnsizer, 0, wx.LEFT|wx.TOP, 5) inner_sizer.Add(self.combo_interval, 0, wx.LEFT|wx.RIGHT|wx.TOP, 5) panel.SetSizer(inner_sizer) inner_sizer.Fit(panel) @@ -127,14 +127,13 @@ Publisher.subscribe(self.ShowBitmapPreview, "Load import bitmap panel") Publisher.subscribe(self.GetSelectedImages ,"Selected Import Images") - def ShowBitmapPreview(self, pubsub_evt): - data = pubsub_evt.data + def ShowBitmapPreview(self, data): #self.patients.extend(dicom_groups) self.text_panel.Populate(data) - def GetSelectedImages(self, pubsub_evt): - self.first_image_selection = pubsub_evt.data[0] - self.last_image_selection = pubsub_evt.data[1] + def GetSelectedImages(self, selection): + self.first_image_selection = selection[0] + self.last_image_selection = selection[1] def _bind_events(self): self.Bind(EVT_SELECT_SLICE, self.OnSelectSlice) @@ -192,7 +191,7 @@ wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.TR_ROW_LINES - | wx.TR_COLUMN_LINES + # | wx.TR_COLUMN_LINES | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_MULTIPLE | wx.TR_HIDE_ROOT @@ -230,23 +229,23 @@ data_size = len(bpr.BitmapData().GetData()) if index >= 0 and index < data_size: - Publisher.sendMessage('Set bitmap in preview panel', index) + Publisher.sendMessage('Set bitmap in preview panel', pos=index) elif index == data_size and data_size > 0: - Publisher.sendMessage('Set bitmap in preview panel', index - 1) + Publisher.sendMessage('Set bitmap in preview panel', pos=index - 1) elif data_size == 1: - Publisher.sendMessage('Set bitmap in preview panel', 0) + Publisher.sendMessage('Set bitmap in preview panel', pos=0) else: Publisher.sendMessage('Show black slice in single preview image') self.tree.Delete(selected_item) self.tree.Update() self.tree.Refresh() - Publisher.sendMessage('Remove preview panel', text_item) + Publisher.sendMessage('Remove preview panel', data=text_item) evt.Skip() - def SelectSeries(self, pubsub_evt): - group_index = pubsub_evt.data + def SelectSeries(self, group_index): + pass def Populate(self, data): tree = self.tree @@ -259,7 +258,7 @@ tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivate) tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged) - Publisher.sendMessage('Load bitmap into import panel', data) + Publisher.sendMessage('Load bitmap into import panel', data=data) def OnSelChanged(self, evt): self.selected_items = self.tree.GetSelections() @@ -269,7 +268,7 @@ text_item = self.tree.GetItemText(item) index = bpr.BitmapData().GetIndexByPath(text_item) - Publisher.sendMessage('Set bitmap in preview panel', index) + Publisher.sendMessage('Set bitmap in preview panel', pos=index) evt.Skip() @@ -308,7 +307,8 @@ splitter.SetOrientation(wx.HORIZONTAL) self.splitter = splitter - splitter.ContainingSizer = wx.BoxSizer(wx.HORIZONTAL) + # TODO: Rever isso + # splitter.ContainingSizer = wx.BoxSizer(wx.HORIZONTAL) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(splitter, 1, wx.EXPAND) @@ -368,14 +368,10 @@ def GetSelectedImagesRange(self): return [self.bitmap_preview.first_selected, self.dicom_preview_last_selection] - def SetBitmapFiles(self, pubsub_evt): - - - bitmap = pubsub_evt.data + def SetBitmapFiles(self, data): + bitmap = data self.thumbnail_preview.Show(1) - self.thumbnail_preview.SetBitmapFiles(bitmap) - self.Update() def OnSelectSerie(self, evt): @@ -419,8 +415,7 @@ self.SetAutoLayout(1) self.sizer = sizer - def SetBitmapFiles(self, pubsub_evt): - data = pubsub_evt.data + def SetBitmapFiles(self, data): self.bitmap_preview.SetBitmapFiles(data) self.sizer.Layout() self.Update() diff -Nru invesalius-3.1.1/invesalius/gui/import_network_panel.py invesalius-3.1.99991/invesalius/gui/import_network_panel.py --- invesalius-3.1.1/invesalius/gui/import_network_panel.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/import_network_panel.py 2018-08-17 16:41:39.000000000 +0000 @@ -110,7 +110,7 @@ self.combo_interval.SetSelection(0) inner_sizer = wx.BoxSizer(wx.HORIZONTAL) - inner_sizer.AddSizer(btnsizer, 0, wx.LEFT|wx.TOP, 5) + inner_sizer.Add(btnsizer, 0, wx.LEFT|wx.TOP, 5) inner_sizer.Add(self.combo_interval, 0, wx.LEFT|wx.RIGHT|wx.TOP, 5) panel.SetSizer(inner_sizer) inner_sizer.Fit(panel) @@ -230,7 +230,7 @@ wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.TR_ROW_LINES - | wx.TR_COLUMN_LINES + # | wx.TR_COLUMN_LINES | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_SINGLE ) @@ -266,8 +266,8 @@ self.root = tree.AddRoot(_("InVesalius Database")) self.tree = tree - def SelectSeries(self, pubsub_evt): - group_index = pubsub_evt.data + def SelectSeries(self, group_index): + pass def Populate(self, pubsub_evt): tree = self.tree @@ -507,8 +507,8 @@ sizer_txt_find.Add(self.btn_find) self.sizer.Add((0, 5), 0, wx.EXPAND|wx.HORIZONTAL) - self.sizer.AddSizer(sizer_word_label) - self.sizer.AddSizer(sizer_txt_find) + self.sizer.Add(sizer_word_label) + self.sizer.Add(sizer_txt_find) #self.sizer.Add(self.serie_preview, 1, wx.EXPAND | wx.ALL, 5) #self.sizer.Add(self.dicom_preview, 1, wx.EXPAND | wx.ALL, 5) @@ -568,7 +568,8 @@ splitter.SetOrientation(wx.HORIZONTAL) self.splitter = splitter - splitter.ContainingSizer = wx.BoxSizer(wx.HORIZONTAL) + # TODO: Rever isso + # splitter.ContainingSizer = wx.BoxSizer(wx.HORIZONTAL) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(splitter, 1, wx.EXPAND) @@ -673,7 +674,10 @@ self.tree_node.SetColumnWidth(4, 80) self.hosts[0] = [True, "localhost", "", "invesalius"] - index = self.tree_node.InsertStringItem(sys.maxint, "") + try: + index = self.tree_node.InsertStringItem(sys.maxsize, "") + except (OverflowError, AssertionError): + index = self.tree_node.InsertStringItem(sys.maxint, "") self.tree_node.SetStringItem(index, 1, "localhost") self.tree_node.SetStringItem(index, 2, "") self.tree_node.SetStringItem(index, 3, "invesalius") @@ -704,7 +708,7 @@ sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.tree_node, 85, wx.GROW|wx.EXPAND) - sizer.AddSizer(sizer_btn, 15) + sizer.Add(sizer_btn, 15) sizer.Fit(self) self.SetSizer(sizer) self.Layout() @@ -728,7 +732,7 @@ def OnButtonAdd(self, evt): #adiciona vazio a coluna de check - index = self.tree_node.InsertStringItem(sys.maxint, "") + index = self.tree_node.InsertStringItem(sys.maxsize, "") self.hosts[index] = [True, "localhost", "80", ""] self.tree_node.SetStringItem(index, 1, "localhost") diff -Nru invesalius-3.1.1/invesalius/gui/import_panel.py invesalius-3.1.99991/invesalius/gui/import_panel.py --- invesalius-3.1.1/invesalius/gui/import_panel.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/import_panel.py 2018-08-17 16:41:39.000000000 +0000 @@ -103,7 +103,7 @@ self.combo_interval.SetSelection(0) inner_sizer = wx.BoxSizer(wx.HORIZONTAL) - inner_sizer.AddSizer(btnsizer, 0, wx.LEFT|wx.TOP, 5) + inner_sizer.Add(btnsizer, 0, wx.LEFT|wx.TOP, 5) inner_sizer.Add(self.combo_interval, 0, wx.LEFT|wx.RIGHT|wx.TOP, 5) panel.SetSizer(inner_sizer) inner_sizer.Fit(panel) @@ -129,9 +129,9 @@ Publisher.subscribe(self.ShowDicomPreview, "Load import panel") Publisher.subscribe(self.GetSelectedImages ,"Selected Import Images") - def GetSelectedImages(self, pubsub_evt): - self.first_image_selection = pubsub_evt.data[0] - self.last_image_selection = pubsub_evt.data[1] + def GetSelectedImages(self, selection): + self.first_image_selection = selection[0] + self.last_image_selection = selection[1] def _bind_events(self): self.Bind(EVT_SELECT_SERIE, self.OnSelectSerie) @@ -141,8 +141,7 @@ self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnClickCancel) self.text_panel.Bind(EVT_SELECT_SERIE_TEXT, self.OnDblClickTextPanel) - def ShowDicomPreview(self, pubsub_evt): - dicom_groups = pubsub_evt.data + def ShowDicomPreview(self, dicom_groups): self.patients.extend(dicom_groups) self.text_panel.Populate(dicom_groups) @@ -187,8 +186,9 @@ nslices_result = slice_amont / (interval + 1) if (nslices_result > 1): - Publisher.sendMessage('Open DICOM group', (group, interval, - [self.first_image_selection, self.last_image_selection])) + Publisher.sendMessage('Open DICOM group', group=group, + interval=interval, + file_range=(self.first_image_selection, self.last_image_selection)) else: dlg.MissingFilesForReconstruction() @@ -215,7 +215,7 @@ wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.TR_ROW_LINES - | wx.TR_COLUMN_LINES + # | wx.TR_COLUMN_LINES | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_SINGLE ) @@ -251,8 +251,8 @@ self.root = tree.AddRoot(_("InVesalius Database")) self.tree = tree - def SelectSeries(self, pubsub_evt): - group_index = pubsub_evt.data + def SelectSeries(self, group_index): + pass def Populate(self, patient_list): tree = self.tree @@ -313,7 +313,7 @@ group = self.tree.GetItemPyData(item) if isinstance(group, dcm.DicomGroup): Publisher.sendMessage('Load group into import panel', - group) + group=group) elif isinstance(group, dcm.PatientGroup): id = group.GetDicomSample().patient.id @@ -322,7 +322,7 @@ self.GetEventHandler().ProcessEvent(my_evt) Publisher.sendMessage('Load patient into import panel', - group) + patient=group) else: parent_id = self.tree.GetItemParent(item) self.tree.Expand(parent_id) @@ -362,7 +362,8 @@ splitter.SetOrientation(wx.HORIZONTAL) self.splitter = splitter - splitter.ContainingSizer = wx.BoxSizer(wx.HORIZONTAL) + # TODO Rever isso + # splitter.ContainingSizer = wx.BoxSizer(wx.HORIZONTAL) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(splitter, 1, wx.EXPAND) @@ -429,8 +430,7 @@ self.serie_preview.Bind(dpp.EVT_CLICK_SERIE, self.OnSelectSerie) self.dicom_preview.Bind(dpp.EVT_CLICK_SLICE, self.OnSelectSlice) - def SetDicomSeries(self, pubsub_evt): - group = pubsub_evt.data + def SetDicomSeries(self, group): self.dicom_preview.SetDicomGroup(group) self.dicom_preview.Show(1) self.serie_preview.Show(0) @@ -440,9 +440,7 @@ def GetSelectedImagesRange(self): return [self.dicom_preview.first_selected, self.dicom_preview_last_selection] - def SetPatientSeries(self, pubsub_evt): - patient = pubsub_evt.data - + def SetPatientSeries(self, patient): self.dicom_preview.Show(0) self.serie_preview.Show(1) @@ -473,8 +471,7 @@ my_evt.SetItemData(evt.GetItemData()) self.GetEventHandler().ProcessEvent(my_evt) - def ShowDicomSeries(self, pubsub_evt): - patient = pubsub_evt.data + def ShowDicomSeries(self, patient): if isinstance(patient, dcm.PatientGroup): self.serie_preview.SetPatientGroups(patient) self.dicom_preview.SetPatientGroups(patient) @@ -504,21 +501,18 @@ self.SetAutoLayout(1) self.sizer = sizer - def SetPatientSeries(self, pubsub_evt): - patient = pubsub_evt.data + def SetPatientSeries(self, patient): group = patient.GetGroups()[0] self.dicom_preview.SetDicomGroup(group) self.sizer.Layout() self.Update() - def SetDicomSeries(self, evt): - group = evt.data + def SetDicomSeries(self, group): self.dicom_preview.SetDicomGroup(group) self.sizer.Layout() self.Update() - def ShowDicomSeries(self, pubsub_evt): - patient = pubsub_evt.data + def ShowDicomSeries(self, patient): group = patient.GetGroups()[0] self.dicom_preview.SetDicomGroup(group) self.sizer.Layout() diff -Nru invesalius-3.1.1/invesalius/gui/language_dialog.py invesalius-3.1.99991/invesalius/gui/language_dialog.py --- invesalius-3.1.1/invesalius/gui/language_dialog.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/language_dialog.py 2018-08-17 16:41:39.000000000 +0000 @@ -20,7 +20,10 @@ import os import sys import wx -import wx.combo +try: + from wx.adv import BitmapComboBox +except ImportError: + from wx.combo import BitmapComboBox import invesalius.i18n as i18n @@ -48,7 +51,7 @@ # Retrieve locales names and sort them self.locales = dict_locales.values() - self.locales.sort() + self.locales = sorted(self.locales) # Retrieve locales keys (eg: pt_BR for Portuguese(Brazilian)) self.locales_key = [dict_locales.get_key(value)[0] for value in self.locales] @@ -56,13 +59,16 @@ # Find out OS locale self.os_locale = i18n.GetLocaleOS() - os_lang = self.os_locale[0:2] + try: + os_lang = self.os_locale[0:2] + except TypeError: + os_lang = None # Default selection will be English selection = self.locales_key.index('en') # Create bitmap combo - self.bitmapCmb = bitmapCmb = wx.combo.BitmapComboBox(parent, style=wx.CB_READONLY) + self.bitmapCmb = bitmapCmb = BitmapComboBox(parent, style=wx.CB_READONLY) for key in self.locales_key: # Based on composed flag filename, get bitmap filepath = os.path.join(ICON_DIR, "%s.bmp"%(key)) @@ -70,7 +76,7 @@ # Add bitmap and info to Combo bitmapCmb.Append(dict_locales[key], bmp, key) # Set default combo item if available on the list - if key.startswith(os_lang): + if os_lang and key.startswith(os_lang): selection = self.locales_key.index(key) bitmapCmb.SetSelection(selection) @@ -114,7 +120,7 @@ # selection = self.locales_key.index('en') # # Create bitmap combo - # self.bitmapCmb = bitmapCmb = wx.combo.BitmapComboBox(self, style=wx.CB_READONLY) + # self.bitmapCmb = bitmapCmb = BitmapComboBox(self, style=wx.CB_READONLY) # for key in self.locales_key: # # Based on composed flag filename, get bitmap # filepath = os.path.join(ICON_DIR, "%s.bmp"%(key)) diff -Nru invesalius-3.1.1/invesalius/gui/preferences.py invesalius-3.1.99991/invesalius/gui/preferences.py --- invesalius-3.1.1/invesalius/gui/preferences.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/preferences.py 2018-08-17 16:41:39.000000000 +0000 @@ -17,11 +17,15 @@ def __init__( self, parent, id = ID, title = _("Preferences"), size=wx.DefaultSize,\ pos=wx.DefaultPosition, style=wx.DEFAULT_DIALOG_STYLE): - pre = wx.PreDialog() - pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) - pre.Create(parent, ID, title, pos, size, style) - - self.PostCreate(pre) + try: + pre = wx.PreDialog() + pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) + pre.Create(parent, ID, title, pos, size, style) + + self.PostCreate(pre) + except AttributeError: + wx.Dialog.__init__(self, parent, ID, title, pos, size, style) + self.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) sizer = wx.BoxSizer(wx.VERTICAL) @@ -55,7 +59,7 @@ btnsizer.Realize() - sizer.AddSizer(btnsizer, 10, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP|wx.BOTTOM, 5) + sizer.Add(btnsizer, 10, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP|wx.BOTTOM, 5) self.SetSizer(sizer) sizer.Fit(self) @@ -77,7 +81,7 @@ return values - def LoadPreferences(self, pub_evt): + def LoadPreferences(self): se = ses.Session() values = {const.RENDERING:se.rendering, diff -Nru invesalius-3.1.1/invesalius/gui/task_exporter.py invesalius-3.1.99991/invesalius/gui/task_exporter.py --- invesalius-3.1.1/invesalius/gui/task_exporter.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/task_exporter.py 2018-08-17 16:41:39.000000000 +0000 @@ -21,7 +21,12 @@ import sys import wx -import wx.lib.hyperlink as hl + +try: + import wx.lib.agw.hyperlink as hl +except ImportError: + import wx.lib.hyperlink as hl + import wx.lib.platebtn as pbtn from wx.lib.pubsub import pub as Publisher @@ -262,7 +267,7 @@ if value: filename, filetype = value Publisher.sendMessage('Export picture to file', - (id, filename, filetype)) + orientation=id, filename=filename, filetype=filetype) @@ -294,7 +299,8 @@ filename = filename + "."+ extension filetype = const.FILETYPE_IMAGEDATA Publisher.sendMessage('Export mask to file', - (filename, filetype)) + filename=filename, + filetype=filetype) def OnLinkExportSurface(self, evt=None): @@ -330,7 +336,7 @@ if filename.split(".")[-1] != extension: filename = filename + "."+ extension Publisher.sendMessage('Export surface to file', - (filename, filetype)) + filename=filename, filetype=filetype) if not os.path.exists(filename): dlg = wx.MessageDialog(None, _("It was not possible to save the surface."), diff -Nru invesalius-3.1.1/invesalius/gui/task_importer.py invesalius-3.1.99991/invesalius/gui/task_importer.py --- invesalius-3.1.1/invesalius/gui/task_importer.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/task_importer.py 2018-08-17 16:41:39.000000000 +0000 @@ -20,8 +20,12 @@ import sys import wx -import wx.lib.hyperlink as hl +try: + import wx.lib.agw.hyperlink as hl +except ImportError: + import wx.lib.hyperlink as hl import wx.lib.platebtn as pbtn + from wx.lib.pubsub import pub as Publisher import invesalius.constants as const @@ -222,7 +226,7 @@ def ImportPACS(self): - print "TODO: Send Signal - Import DICOM files from PACS" + print("TODO: Send Signal - Import DICOM files from PACS") ####### @@ -231,15 +235,15 @@ def OpenProject(self, path=None): if path: - Publisher.sendMessage('Open recent project', path) + Publisher.sendMessage('Open recent project', filepath=path) else: Publisher.sendMessage('Show open project dialog') def SaveAsProject(self): - Publisher.sendMessage('Show save dialog', True) + Publisher.sendMessage('Show save dialog', save_as=True) def SaveProject(self): - Publisher.sendMessage('Show save dialog', False) + Publisher.sendMessage('Show save dialog', save_as=False) def CloseProject(self): Publisher.sendMessage('Close Project') @@ -264,7 +268,7 @@ """ # Remove each project from sizer - for i in xrange(0, self.proj_count): + for i in range(0, self.proj_count): self.sizer.Remove(self.float_hyper_list[i]) # Delete hyperlinks diff -Nru invesalius-3.1.1/invesalius/gui/task_navigator.py invesalius-3.1.99991/invesalius/gui/task_navigator.py --- invesalius-3.1.1/invesalius/gui/task_navigator.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/task_navigator.py 2018-08-17 16:41:39.000000000 +0000 @@ -19,22 +19,40 @@ from functools import partial import sys +import os import numpy as np import wx -import wx.lib.hyperlink as hl + +try: + import wx.lib.agw.hyperlink as hl + import wx.lib.agw.foldpanelbar as fpb +except ImportError: + import wx.lib.hyperlink as hl + import wx.lib.foldpanelbar as fpb + import wx.lib.masked.numctrl -import wx.lib.foldpanelbar as fpb from wx.lib.pubsub import pub as Publisher import wx.lib.colourselect as csel +import wx.lib.platebtn as pbtn + +from math import cos, sin, pi +from time import sleep +import invesalius.data.transformations as tr import invesalius.constants as const import invesalius.data.bases as db import invesalius.data.coordinates as dco import invesalius.data.coregistration as dcr import invesalius.data.trackers as dt import invesalius.data.trigger as trig +import invesalius.data.record_coords as rec import invesalius.gui.dialogs as dlg +from invesalius import utils + +BTN_NEW = wx.NewId() +BTN_IMPORT_LOCAL = wx.NewId() + class TaskPanel(wx.Panel): def __init__(self, parent): @@ -71,7 +89,6 @@ fold_panel = FoldPanel(self) fold_panel.SetBackgroundColour(default_colour) - # Add line sizer into main sizer main_sizer = wx.BoxSizer(wx.VERTICAL) main_sizer.Add(txt_sizer, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT, 5) @@ -104,7 +121,10 @@ class InnerFoldPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) self.__bind_events() @@ -120,7 +140,7 @@ (10, 350), 0, fpb.FPB_SINGLE_FOLD) else: fold_panel = fpb.FoldPanelBar(self, -1, wx.DefaultPosition, - (10, 293), 0, fpb.FPB_SINGLE_FOLD) + (10, 320), 0, fpb.FPB_SINGLE_FOLD) # Fold panel style style = fpb.CaptionBarStyle() style.SetCaptionStyle(fpb.CAPTIONBAR_GRADIENT_V) @@ -132,11 +152,19 @@ ntw = NeuronavigationPanel(item) fold_panel.ApplyCaptionStyle(item, style) - fold_panel.AddFoldPanelWindow(item, ntw, spacing= 0, + fold_panel.AddFoldPanelWindow(item, ntw, spacing=0, leftSpacing=0, rightSpacing=0) fold_panel.Expand(fold_panel.GetFoldPanel(0)) - # Fold 2 - Markers panel + # Fold 2 - Object registration panel + item = fold_panel.AddFoldPanel(_("Object registration"), collapsed=True) + otw = ObjectRegistrationPanel(item) + + fold_panel.ApplyCaptionStyle(item, style) + fold_panel.AddFoldPanelWindow(item, otw, spacing=0, + leftSpacing=0, rightSpacing=0) + + # Fold 3 - Markers panel item = fold_panel.AddFoldPanel(_("Extra tools"), collapsed=True) mtw = MarkersPanel(item) @@ -147,26 +175,38 @@ # Check box for camera update in volume rendering during navigation tooltip = wx.ToolTip(_("Update camera in volume")) - checkcamera = wx.CheckBox(self, -1, _('Volume camera')) + checkcamera = wx.CheckBox(self, -1, _('Vol. camera')) checkcamera.SetToolTip(tooltip) - checkcamera.SetValue(True) - checkcamera.Bind(wx.EVT_CHECKBOX, partial(self.UpdateVolumeCamera, ctrl=checkcamera)) + checkcamera.SetValue(const.CAM_MODE) + checkcamera.Bind(wx.EVT_CHECKBOX, self.OnVolumeCamera) + self.checkcamera = checkcamera - # Check box for camera update in volume rendering during navigation + # Check box for trigger monitoring to create markers from serial port tooltip = wx.ToolTip(_("Enable external trigger for creating markers")) - checktrigger = wx.CheckBox(self, -1, _('External trigger')) + checktrigger = wx.CheckBox(self, -1, _('Ext. trigger')) checktrigger.SetToolTip(tooltip) checktrigger.SetValue(False) - checktrigger.Bind(wx.EVT_CHECKBOX, partial(self.UpdateExternalTrigger, ctrl=checktrigger)) + checktrigger.Bind(wx.EVT_CHECKBOX, partial(self.OnExternalTrigger, ctrl=checktrigger)) self.checktrigger = checktrigger + # Check box for object position and orientation update in volume rendering during navigation + tooltip = wx.ToolTip(_("Show and track TMS coil")) + checkobj = wx.CheckBox(self, -1, _('Show coil')) + checkobj.SetToolTip(tooltip) + checkobj.SetValue(False) + checkobj.Disable() + checkobj.Bind(wx.EVT_CHECKBOX, self.OnShowObject) + self.checkobj = checkobj + if sys.platform != 'win32': - checkcamera.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) + self.checkcamera.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) checktrigger.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) + checkobj.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) line_sizer = wx.BoxSizer(wx.HORIZONTAL) line_sizer.Add(checkcamera, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.LEFT, 5) - line_sizer.Add(checktrigger, 1,wx.ALIGN_RIGHT | wx.RIGHT | wx.LEFT, 5) + line_sizer.Add(checktrigger, 0, wx.ALIGN_CENTER) + line_sizer.Add(checkobj, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.LEFT, 5) line_sizer.Fit(self) # Panel sizer to expand fold panel @@ -175,33 +215,55 @@ sizer.Add(line_sizer, 1, wx.GROW | wx.EXPAND) sizer.Fit(self) + self.track_obj = False + self.SetSizer(sizer) self.Update() self.SetAutoLayout(1) - + def __bind_events(self): - Publisher.subscribe(self.OnTrigger, 'Navigation Status') + Publisher.subscribe(self.OnCheckStatus, 'Navigation status') + Publisher.subscribe(self.OnShowObject, 'Update track object state') + Publisher.subscribe(self.OnVolumeCamera, 'Target navigation mode') - def OnTrigger(self, pubsub_evt): - status = pubsub_evt.data + def OnCheckStatus(self, status): if status: self.checktrigger.Enable(False) + self.checkobj.Enable(False) else: self.checktrigger.Enable(True) + if self.track_obj: + self.checkobj.Enable(True) - def UpdateExternalTrigger(self, evt, ctrl): - Publisher.sendMessage('Update trigger state', ctrl.GetValue()) - - def UpdateVolumeCamera(self, evt, ctrl): - Publisher.sendMessage('Update volume camera state', ctrl.GetValue()) - + def OnExternalTrigger(self, evt, ctrl): + Publisher.sendMessage('Update trigger state', trigger_state=ctrl.GetValue()) + def OnShowObject(self, evt=None, flag=None, obj_name=None): + if flag: + self.checkobj.Enable(True) + self.track_obj = True + Publisher.sendMessage('Status target button', status=True) + else: + self.checkobj.Enable(False) + self.checkobj.SetValue(False) + self.track_obj = False + Publisher.sendMessage('Status target button', status=False) + + Publisher.sendMessage('Update show object state', state=self.checkobj.GetValue()) + + def OnVolumeCamera(self, evt=None, target_mode=None): + if target_mode is True: + self.checkcamera.SetValue(0) + Publisher.sendMessage('Update volume camera state', camera_state=self.checkcamera.GetValue()) class NeuronavigationPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) self.SetAutoLayout(1) @@ -215,6 +277,9 @@ self.trk_init = None self.trigger = None self.trigger_state = False + self.obj_reg = None + self.obj_reg_status = False + self.track_obj = False self.tracker_id = const.DEFAULT_TRACKER self.ref_mode_id = const.DEFAULT_REF_MODE @@ -230,6 +295,7 @@ choice_trck.SetToolTip(tooltip) choice_trck.SetSelection(const.DEFAULT_TRACKER) choice_trck.Bind(wx.EVT_COMBOBOX, partial(self.OnChoiceTracker, ctrl=choice_trck)) + self.choice_trck = choice_trck # ComboBox for tracker reference mode tooltip = wx.ToolTip(_("Choose the navigation reference mode")) @@ -244,10 +310,10 @@ tips_img = const.TIPS_IMG for k in btns_img: - n = btns_img[k].keys()[0] - lab = btns_img[k].values()[0] + n = list(btns_img[k].keys())[0] + lab = list(btns_img[k].values())[0] self.btns_coord[n] = wx.ToggleButton(self, k, label=lab, size=wx.Size(45, 23)) - self.btns_coord[n].SetToolTip(tips_img[n]) + self.btns_coord[n].SetToolTip(wx.ToolTip(tips_img[n])) self.btns_coord[n].Bind(wx.EVT_TOGGLEBUTTON, self.OnImageFiducials) # Push buttons for tracker fiducials @@ -255,11 +321,11 @@ tips_trk = const.TIPS_TRK for k in btns_trk: - n = btns_trk[k].keys()[0] - lab = btns_trk[k].values()[0] + n = list(btns_trk[k].keys())[0] + lab = list(btns_trk[k].values())[0] self.btns_coord[n] = wx.Button(self, k, label=lab, size=wx.Size(45, 23)) - self.btns_coord[n].SetToolTip(tips_trk[n-3]) - # Excepetion for event of button that set image coordinates + self.btns_coord[n].SetToolTip(wx.ToolTip(tips_trk[n-3])) + # Exception for event of button that set image coordinates if n == 6: self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnSetImageCoordinates) else: @@ -275,12 +341,13 @@ txtctrl_fre.SetBackgroundColour('WHITE') txtctrl_fre.SetEditable(0) txtctrl_fre.SetToolTip(tooltip) + self.txtctrl_fre = txtctrl_fre # Toggle button for neuronavigation tooltip = wx.ToolTip(_("Start navigation")) btn_nav = wx.ToggleButton(self, -1, _("Navigate"), size=wx.Size(80, -1)) btn_nav.SetToolTip(tooltip) - btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn=(btn_nav, choice_trck, choice_ref, txtctrl_fre))) + btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn=(btn_nav, choice_trck, choice_ref))) # Image and tracker coordinates number controls for m in range(0, 7): @@ -288,7 +355,7 @@ self.numctrls_coord[m].append( wx.lib.masked.numctrl.NumCtrl(parent=self, integerWidth=4, fractionWidth=1)) - # Sizers to group all GUI objects + # Sizer to group all GUI objects choice_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) choice_sizer.AddMany([(choice_trck, wx.LEFT), (choice_ref, wx.RIGHT)]) @@ -326,47 +393,60 @@ def __bind_events(self): Publisher.subscribe(self.LoadImageFiducials, 'Load image fiducials') Publisher.subscribe(self.UpdateTriggerState, 'Update trigger state') + Publisher.subscribe(self.UpdateTrackObjectState, 'Update track object state') Publisher.subscribe(self.UpdateImageCoordinates, 'Set ball reference position') Publisher.subscribe(self.OnDisconnectTracker, 'Disconnect tracker') + Publisher.subscribe(self.UpdateObjectRegistration, 'Update object registration') + Publisher.subscribe(self.OnCloseProject, 'Close project data') - def LoadImageFiducials(self, pubsub_evt): - marker_id = pubsub_evt.data[0] - coord = pubsub_evt.data[1] + def LoadImageFiducials(self, marker_id, coord): for n in const.BTNS_IMG_MKS: - btn_id = const.BTNS_IMG_MKS[n].keys()[0] - fid_id = const.BTNS_IMG_MKS[n].values()[0] + btn_id = list(const.BTNS_IMG_MKS[n].keys())[0] + fid_id = list(const.BTNS_IMG_MKS[n].values())[0] if marker_id == fid_id and not self.btns_coord[btn_id].GetValue(): self.btns_coord[btn_id].SetValue(True) self.fiducials[btn_id, :] = coord[0:3] for m in [0, 1, 2]: self.numctrls_coord[btn_id][m].SetValue(coord[m]) - def UpdateImageCoordinates(self, pubsub_evt): + def UpdateImageCoordinates(self, position): # TODO: Change from world coordinates to matrix coordinates. They are better for multi software communication. - self.current_coord = pubsub_evt.data + self.current_coord = position for m in [0, 1, 2, 6]: if m == 6 and self.btns_coord[m].IsEnabled(): for n in [0, 1, 2]: - self.numctrls_coord[m][n].SetValue(self.current_coord[n]) + self.numctrls_coord[m][n].SetValue(float(self.current_coord[n])) elif m != 6 and not self.btns_coord[m].GetValue(): # btn_state = self.btns_coord[m].GetValue() # if not btn_state: for n in [0, 1, 2]: - self.numctrls_coord[m][n].SetValue(self.current_coord[n]) + self.numctrls_coord[m][n].SetValue(float(self.current_coord[n])) - def UpdateTriggerState (self, pubsub_evt): - self.trigger_state = pubsub_evt.data + def UpdateObjectRegistration(self, data=None): + if data: + self.obj_reg = data + self.obj_reg_status = True + else: + self.obj_reg = None + self.obj_reg_status = False + + def UpdateTrackObjectState(self, evt=None, flag=None, obj_name=None): + self.track_obj = flag + + def UpdateTriggerState(self, trigger_state): + self.trigger_state = trigger_state - def OnDisconnectTracker(self, pubsub_evt): + def OnDisconnectTracker(self): if self.tracker_id: - dt.TrackerConnection(self.tracker_id, 'disconnect') + dt.TrackerConnection(self.tracker_id, self.trk_init[0], 'disconnect') def OnChoiceTracker(self, evt, ctrl): - Publisher.sendMessage('Update status text in GUI', _("Configuring tracker ...")) - if evt: + Publisher.sendMessage('Update status text in GUI', + label=_("Configuring tracker ...")) + if hasattr(evt, 'GetSelection'): choice = evt.GetSelection() else: - choice = self.tracker_id + choice = 6 if self.trk_init: trck = self.trk_init[0] @@ -377,27 +457,42 @@ # has been initialized before if trck and choice != 6: self.ResetTrackerFiducials() - self.trk_init = dt.TrackerConnection(self.tracker_id, 'disconnect') + Publisher.sendMessage('Update status text in GUI', + label=_("Disconnecting tracker...")) + Publisher.sendMessage('Remove sensors ID') + self.trk_init = dt.TrackerConnection(self.tracker_id, trck, 'disconnect') self.tracker_id = choice - if not self.trk_init[0]: - self.trk_init = dt.TrackerConnection(self.tracker_id, 'connect') + if not self.trk_init[0] and choice: + Publisher.sendMessage('Update status text in GUI', + label=_("Tracker disconnected successfully")) + self.trk_init = dt.TrackerConnection(self.tracker_id, None, 'connect') if not self.trk_init[0]: dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1]) ctrl.SetSelection(0) - print "Tracker not connected!" + print("Tracker not connected!") else: + Publisher.sendMessage('Update status text in GUI', + label=_("Ready")) ctrl.SetSelection(self.tracker_id) - print "Tracker connected!" + print("Tracker connected!") elif choice == 6: if trck: - self.trk_init = dt.TrackerConnection(self.tracker_id, 'disconnect') + Publisher.sendMessage('Update status text in GUI', + label=_("Disconnecting tracker ...")) + Publisher.sendMessage('Remove sensors ID') + self.trk_init = dt.TrackerConnection(self.tracker_id, trck, 'disconnect') if not self.trk_init[0]: - dlg.NavigationTrackerWarning(self.tracker_id, 'disconnect') + if evt is not False: + dlg.NavigationTrackerWarning(self.tracker_id, 'disconnect') self.tracker_id = 0 ctrl.SetSelection(self.tracker_id) - print "Tracker disconnected!" + Publisher.sendMessage('Update status text in GUI', + label=_("Tracker disconnected")) + print("Tracker disconnected!") else: - print "Tracker still connected!" + Publisher.sendMessage('Update status text in GUI', + label=_("Tracker still connected")) + print("Tracker still connected!") else: ctrl.SetSelection(self.tracker_id) @@ -405,59 +500,72 @@ # If trk_init is None try to connect. If doesn't succeed show dialog. if choice: self.tracker_id = choice - self.trk_init = dt.TrackerConnection(self.tracker_id, 'connect') + self.trk_init = dt.TrackerConnection(self.tracker_id, None, 'connect') if not self.trk_init[0]: dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1]) self.tracker_id = 0 ctrl.SetSelection(self.tracker_id) - Publisher.sendMessage('Update status text in GUI', _("Ready")) + else: + Publisher.sendMessage('Update status text in GUI', + label=_("Ready")) + + Publisher.sendMessage('Update tracker initializer', + nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id)) def OnChoiceRefMode(self, evt, ctrl): - # When ref mode is changed the tracker coords are set to zero + # When ref mode is changed the tracker coordinates are set to zero self.ref_mode_id = evt.GetSelection() self.ResetTrackerFiducials() # Some trackers do not accept restarting within this time window # TODO: Improve the restarting of trackers after changing reference mode # self.OnChoiceTracker(None, ctrl) - print "Reference mode changed!" + Publisher.sendMessage('Update tracker initializer', + nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id)) + print("Reference mode changed!") def OnSetImageCoordinates(self, evt): # FIXME: Cross does not update in last clicked slice, only on the other two - btn_id = const.BTNS_TRK[evt.GetId()].keys()[0] + btn_id = list(const.BTNS_TRK[evt.GetId()].keys())[0] - wx, wy, wz = self.numctrls_coord[btn_id][0].GetValue(), \ - self.numctrls_coord[btn_id][1].GetValue(), \ + ux, uy, uz = self.numctrls_coord[btn_id][0].GetValue(),\ + self.numctrls_coord[btn_id][1].GetValue(),\ self.numctrls_coord[btn_id][2].GetValue() - Publisher.sendMessage('Set ball reference position', (wx, wy, wz)) - Publisher.sendMessage('Set camera in volume', (wx, wy, wz)) - Publisher.sendMessage('Co-registered points', (wx, wy, wz)) - Publisher.sendMessage('Update cross position', (wx, wy, wz)) + Publisher.sendMessage('Set ball reference position', position=(ux, uy, uz)) + # Publisher.sendMessage('Set camera in volume', (ux, uy, uz)) + Publisher.sendMessage('Co-registered points', arg=(ux, uy, uz), position=(0., 0., 0.)) + Publisher.sendMessage('Update cross position', position=(ux, uy, uz)) def OnImageFiducials(self, evt): - btn_id = const.BTNS_IMG_MKS[evt.GetId()].keys()[0] - marker_id = const.BTNS_IMG_MKS[evt.GetId()].values()[0] + btn_id = list(const.BTNS_IMG_MKS[evt.GetId()].keys())[0] + marker_id = list(const.BTNS_IMG_MKS[evt.GetId()].values())[0] if self.btns_coord[btn_id].GetValue(): coord = self.numctrls_coord[btn_id][0].GetValue(),\ self.numctrls_coord[btn_id][1].GetValue(),\ - self.numctrls_coord[btn_id][2].GetValue() + self.numctrls_coord[btn_id][2].GetValue(), 0, 0, 0 self.fiducials[btn_id, :] = coord[0:3] - Publisher.sendMessage('Create marker', (coord, marker_id)) + Publisher.sendMessage('Create marker', coord=coord, marker_id=marker_id) else: for n in [0, 1, 2]: - self.numctrls_coord[btn_id][n].SetValue(self.current_coord[n]) + self.numctrls_coord[btn_id][n].SetValue(float(self.current_coord[n])) self.fiducials[btn_id, :] = np.nan - Publisher.sendMessage('Delete fiducial marker', marker_id) + Publisher.sendMessage('Delete fiducial marker', marker_id=marker_id) def OnTrackerFiducials(self, evt): - btn_id = const.BTNS_TRK[evt.GetId()].keys()[0] + btn_id = list(const.BTNS_TRK[evt.GetId()].keys())[0] coord = None if self.trk_init and self.tracker_id: - coord = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id) + coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id) + if self.ref_mode_id: + coord = dco.dynamic_reference_m(coord_raw[0, :], coord_raw[1, :]) + else: + coord = coord_raw[0, :] + coord[2] = -coord[2] + else: dlg.NavigationTrackerWarning(0, 'choose') @@ -471,7 +579,6 @@ btn_nav = btn[0] choice_trck = btn[1] choice_ref = btn[2] - txtctrl_fre = btn[3] nav_id = btn_nav.GetValue() if nav_id: @@ -479,6 +586,9 @@ dlg.InvalidFiducials() btn_nav.SetValue(False) + elif not self.trk_init[0]: + dlg.NavigationTrackerWarning(0, 'choose') + else: tooltip = wx.ToolTip(_("Stop neuronavigation")) btn_nav.SetToolTip(tooltip) @@ -489,28 +599,75 @@ for btn_c in self.btns_coord: btn_c.Enable(False) - m, q1, minv = db.base_creation(self.fiducials[0:3, :]) - n, q2, ninv = db.base_creation(self.fiducials[3::, :]) + # fids_head_img = np.zeros([3, 3]) + # for ic in range(0, 3): + # fids_head_img[ic, :] = np.asarray(db.flip_x_m(self.fiducials[ic, :])) + # + # m_head_aux, q_head_aux, m_inv_head_aux = db.base_creation(fids_head_img) + # m_head = np.asmatrix(np.identity(4)) + # m_head[:3, :3] = m_head_aux[:3, :3] + + m, q1, minv = db.base_creation_old(self.fiducials[:3, :]) + n, q2, ninv = db.base_creation_old(self.fiducials[3:, :]) + + m_change = tr.affine_matrix_from_points(self.fiducials[3:, :].T, self.fiducials[:3, :].T, + shear=False, scale=False) + + # coreg_data = [m_change, m_head] tracker_mode = self.trk_init, self.tracker_id, self.ref_mode_id # FIXME: FRE is taking long to calculate so it updates on GUI delayed to navigation - I think its fixed # TODO: Exhibit FRE in a warning dialog and only starts navigation after user clicks ok fre = db.calculate_fre(self.fiducials, minv, n, q1, q2) - txtctrl_fre.SetValue(str(round(fre, 2))) + self.txtctrl_fre.SetValue(str(round(fre, 2))) if fre <= 3: - txtctrl_fre.SetBackgroundColour('GREEN') + self.txtctrl_fre.SetBackgroundColour('GREEN') else: - txtctrl_fre.SetBackgroundColour('RED') + self.txtctrl_fre.SetBackgroundColour('RED') if self.trigger_state: self.trigger = trig.Trigger(nav_id) - Publisher.sendMessage("Navigation Status", True) - Publisher.sendMessage("Toggle Cross", const.SLICE_STATE_CROSS) + Publisher.sendMessage("Navigation status", status=True) + Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS) Publisher.sendMessage("Hide current mask") - self.correg = dcr.Coregistration((minv, n, q1, q2), nav_id, tracker_mode) + if self.track_obj: + if self.obj_reg_status: + # obj_reg[0] is object 3x3 fiducial matrix and obj_reg[1] is 3x3 orientation matrix + obj_fiducials, obj_orients, obj_ref_mode, obj_name = self.obj_reg + + if self.trk_init and self.tracker_id: + + coreg_data = [m_change, obj_ref_mode] + + if self.ref_mode_id: + coord_raw = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id) + obj_data = db.object_registration(obj_fiducials, obj_orients, coord_raw, m_change) + coreg_data.extend(obj_data) + + self.correg = dcr.CoregistrationObjectDynamic(coreg_data, nav_id, tracker_mode) + else: + coord_raw = np.array([None]) + obj_data = db.object_registration(obj_fiducials, obj_orients, coord_raw, m_change) + coreg_data.extend(obj_data) + + self.correg = dcr.CoregistrationObjectStatic(coreg_data, nav_id, tracker_mode) + + else: + dlg.NavigationTrackerWarning(0, 'choose') + + else: + dlg.InvalidObjectRegistration() + + else: + coreg_data = [m_change, 0] + if self.ref_mode_id: + # self.correg = dcr.CoregistrationDynamic_old(bases_coreg, nav_id, tracker_mode) + self.correg = dcr.CoregistrationDynamic(coreg_data, nav_id, tracker_mode) + else: + self.correg = dcr.CoregistrationStatic(coreg_data, nav_id, tracker_mode) else: tooltip = wx.ToolTip(_("Start neuronavigation")) @@ -527,27 +684,296 @@ self.correg.stop() - Publisher.sendMessage("Navigation Status", False) + Publisher.sendMessage("Navigation status", status=False) + + def ResetImageFiducials(self): + for m in range(0, 3): + self.btns_coord[m].SetValue(False) + self.fiducials[m, :] = [np.nan, np.nan, np.nan] + for n in range(0, 3): + self.numctrls_coord[m][n].SetValue(0.0) + + for n in range(0, 3): + self.numctrls_coord[6][n].SetValue(0.0) def ResetTrackerFiducials(self): for m in range(3, 6): + self.fiducials[m, :] = [np.nan, np.nan, np.nan] for n in range(0, 3): self.numctrls_coord[m][n].SetValue(0.0) + self.txtctrl_fre.SetValue('') + self.txtctrl_fre.SetBackgroundColour('WHITE') + + def OnCloseProject(self): + self.ResetTrackerFiducials() + self.ResetImageFiducials() + self.OnChoiceTracker(False, self.choice_trck) + Publisher.sendMessage('Update object registration') + Publisher.sendMessage('Update track object state', flag=False, obj_name=False) + Publisher.sendMessage('Delete all markers') + # TODO: Reset camera initial focus + Publisher.sendMessage('Reset cam clipping range') + + +class ObjectRegistrationPanel(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + self.SetBackgroundColour(default_colour) + + self.coil_list = const.COIL + + self.nav_prop = None + self.obj_fiducials = None + self.obj_orients = None + self.obj_ref_mode = None + self.obj_name = None + self.timestamp = const.TIMESTAMP + + self.SetAutoLayout(1) + self.__bind_events() + + # Button for creating new coil + tooltip = wx.ToolTip(_("Create new coil")) + btn_new = wx.Button(self, -1, _("New"), size=wx.Size(65, 23)) + btn_new.SetToolTip(tooltip) + btn_new.Enable(1) + btn_new.Bind(wx.EVT_BUTTON, self.OnLinkCreate) + self.btn_new = btn_new + + # Button for import config coil file + tooltip = wx.ToolTip(_("Load coil configuration file")) + btn_load = wx.Button(self, -1, _("Load"), size=wx.Size(65, 23)) + btn_load.SetToolTip(tooltip) + btn_load.Enable(1) + btn_load.Bind(wx.EVT_BUTTON, self.OnLinkLoad) + self.btn_load = btn_load + + # Save button for object registration + tooltip = wx.ToolTip(_(u"Save object registration file")) + btn_save = wx.Button(self, -1, _(u"Save"), size=wx.Size(65, 23)) + btn_save.SetToolTip(tooltip) + btn_save.Enable(1) + btn_save.Bind(wx.EVT_BUTTON, self.ShowSaveObjectDialog) + self.btn_save = btn_save + + # Create a horizontal sizer to represent button save + line_save = wx.BoxSizer(wx.HORIZONTAL) + line_save.Add(btn_new, 1, wx.LEFT | wx.TOP | wx.RIGHT, 4) + line_save.Add(btn_load, 1, wx.LEFT | wx.TOP | wx.RIGHT, 4) + line_save.Add(btn_save, 1, wx.LEFT | wx.TOP | wx.RIGHT, 4) + + # Change angles threshold + text_angles = wx.StaticText(self, -1, _("Angle threshold [degrees]:")) + spin_size_angles = wx.SpinCtrl(self, -1, "", size=wx.Size(50, 23)) + spin_size_angles.SetRange(1, 99) + spin_size_angles.SetValue(const.COIL_ANGLES_THRESHOLD) + spin_size_angles.Bind(wx.EVT_TEXT, partial(self.OnSelectAngleThreshold, ctrl=spin_size_angles)) + spin_size_angles.Bind(wx.EVT_SPINCTRL, partial(self.OnSelectAngleThreshold, ctrl=spin_size_angles)) + + # Change dist threshold + text_dist = wx.StaticText(self, -1, _("Distance threshold [mm]:")) + spin_size_dist = wx.SpinCtrl(self, -1, "", size=wx.Size(50, 23)) + spin_size_dist.SetRange(1, 99) + spin_size_dist.SetValue(const.COIL_ANGLES_THRESHOLD) + spin_size_dist.Bind(wx.EVT_TEXT, partial(self.OnSelectDistThreshold, ctrl=spin_size_dist)) + spin_size_dist.Bind(wx.EVT_SPINCTRL, partial(self.OnSelectDistThreshold, ctrl=spin_size_dist)) + + # Change timestamp interval + text_timestamp = wx.StaticText(self, -1, _("Timestamp interval [s]:")) + spin_timestamp_dist = wx.SpinCtrlDouble(self, -1, "", size=wx.Size(50, 23), inc = 0.1) + spin_timestamp_dist.SetRange(0.5, 60.0) + spin_timestamp_dist.SetValue(self.timestamp) + spin_timestamp_dist.Bind(wx.EVT_TEXT, partial(self.OnSelectTimestamp, ctrl=spin_timestamp_dist)) + spin_timestamp_dist.Bind(wx.EVT_SPINCTRL, partial(self.OnSelectTimestamp, ctrl=spin_timestamp_dist)) + self.spin_timestamp_dist = spin_timestamp_dist + + # Create a horizontal sizer to threshold configs + line_angle_threshold = wx.BoxSizer(wx.HORIZONTAL) + line_angle_threshold.AddMany([(text_angles, 1, wx.EXPAND | wx.GROW | wx.TOP| wx.RIGHT | wx.LEFT, 5), + (spin_size_angles, 0, wx.ALL | wx.EXPAND | wx.GROW, 5)]) + + line_dist_threshold = wx.BoxSizer(wx.HORIZONTAL) + line_dist_threshold.AddMany([(text_dist, 1, wx.EXPAND | wx.GROW | wx.TOP| wx.RIGHT | wx.LEFT, 5), + (spin_size_dist, 0, wx.ALL | wx.EXPAND | wx.GROW, 5)]) + + line_timestamp = wx.BoxSizer(wx.HORIZONTAL) + line_timestamp.AddMany([(text_timestamp, 1, wx.EXPAND | wx.GROW | wx.TOP| wx.RIGHT | wx.LEFT, 5), + (spin_timestamp_dist, 0, wx.ALL | wx.EXPAND | wx.GROW, 5)]) + + # Check box for trigger monitoring to create markers from serial port + checkrecordcoords = wx.CheckBox(self, -1, _('Record coordinates')) + checkrecordcoords.SetValue(False) + checkrecordcoords.Enable(0) + checkrecordcoords.Bind(wx.EVT_CHECKBOX, partial(self.OnRecordCoords, ctrl=checkrecordcoords)) + self.checkrecordcoords = checkrecordcoords + + # Check box to track object or simply the stylus + checktrack = wx.CheckBox(self, -1, _('Track object')) + checktrack.SetValue(False) + checktrack.Enable(0) + checktrack.Bind(wx.EVT_CHECKBOX, partial(self.OnTrackObject, ctrl=checktrack)) + self.checktrack = checktrack + + line_checks = wx.BoxSizer(wx.HORIZONTAL) + line_checks.Add(checkrecordcoords, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.LEFT, 5) + line_checks.Add(checktrack, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.LEFT, 5) + + # Add line sizers into main sizer + main_sizer = wx.BoxSizer(wx.VERTICAL) + main_sizer.Add(line_save, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_HORIZONTAL, 5) + main_sizer.Add(line_angle_threshold, 0, wx.GROW | wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + main_sizer.Add(line_dist_threshold, 0, wx.GROW | wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + main_sizer.Add(line_timestamp, 0, wx.GROW | wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) + main_sizer.Add(line_checks, 0, wx.GROW | wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, 10) + main_sizer.Fit(self) + + self.SetSizer(main_sizer) + self.Update() + + def __bind_events(self): + Publisher.subscribe(self.UpdateTrackerInit, 'Update tracker initializer') + Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status') + Publisher.subscribe(self.OnCloseProject, 'Close project data') + + def UpdateTrackerInit(self, nav_prop): + self.nav_prop = nav_prop + + def UpdateNavigationStatus(self, status): + nav_status = status + if nav_status: + self.checkrecordcoords.Enable(1) + self.checktrack.Enable(0) + self.btn_save.Enable(0) + self.btn_new.Enable(0) + self.btn_load.Enable(0) + else: + self.OnRecordCoords(nav_status, self.checkrecordcoords) + self.checkrecordcoords.SetValue(False) + self.checkrecordcoords.Enable(0) + self.btn_save.Enable(1) + self.btn_new.Enable(1) + self.btn_load.Enable(1) + if self.obj_fiducials is not None: + self.checktrack.Enable(1) + Publisher.sendMessage('Enable target button', True) + + def OnSelectAngleThreshold(self, evt, ctrl): + Publisher.sendMessage('Update angle threshold', angle=ctrl.GetValue()) + + def OnSelectDistThreshold(self, evt, ctrl): + Publisher.sendMessage('Update dist threshold', dist_threshold=ctrl.GetValue()) + + def OnSelectTimestamp(self, evt, ctrl): + self.timestamp = ctrl.GetValue() + + def OnRecordCoords(self, evt, ctrl): + if ctrl.GetValue() and evt: + self.spin_timestamp_dist.Enable(0) + self.thr_record = rec.Record(ctrl.GetValue(), self.timestamp) + elif (not ctrl.GetValue() and evt) or (ctrl.GetValue() and not evt) : + self.spin_timestamp_dist.Enable(1) + self.thr_record.stop() + elif not ctrl.GetValue() and not evt: + None + + def OnTrackObject(self, evt, ctrl): + Publisher.sendMessage('Update track object state', flag=evt.GetSelection(), obj_name=self.obj_name) + + def OnComboCoil(self, evt): + # coil_name = evt.GetString() + coil_index = evt.GetSelection() + Publisher.sendMessage('Change selected coil', self.coil_list[coil_index][1]) + + def OnLinkCreate(self, event=None): + + if self.nav_prop: + dialog = dlg.ObjectCalibrationDialog(self.nav_prop) + try: + if dialog.ShowModal() == wx.ID_OK: + self.obj_fiducials, self.obj_orients, self.obj_ref_mode, self.obj_name = dialog.GetValue() + if np.isfinite(self.obj_fiducials).all() and np.isfinite(self.obj_orients).all(): + self.checktrack.Enable(1) + Publisher.sendMessage('Update object registration', + data=(self.obj_fiducials, self.obj_orients, self.obj_ref_mode, self.obj_name)) + Publisher.sendMessage('Update status text in GUI', + label=_("Ready")) + + except wx._core.PyAssertionError: # TODO FIX: win64 + pass + + else: + dlg.NavigationTrackerWarning(0, 'choose') + + def OnLinkLoad(self, event=None): + filename = dlg.ShowLoadRegistrationDialog() + + if filename: + data = np.loadtxt(filename, delimiter='\t') + self.obj_fiducials = data[:, :3] + self.obj_orients = data[:, 3:] + + text_file = open(filename, "r") + header = text_file.readline().split('\t') + text_file.close() + + self.obj_name = header[1] + self.obj_ref_mode = int(header[-1]) + + self.checktrack.Enable(1) + Publisher.sendMessage('Update object registration', + data=(self.obj_fiducials, self.obj_orients, self.obj_ref_mode, self.obj_name)) + Publisher.sendMessage('Update status text in GUI', label=_("Ready")) + wx.MessageBox(_("Object file successfully loaded"), _("Load")) + + def ShowSaveObjectDialog(self, evt): + if np.isnan(self.obj_fiducials).any() or np.isnan(self.obj_orients).any(): + wx.MessageBox(_("Digitize all object fiducials before saving"), _("Save error")) + else: + filename = dlg.ShowSaveRegistrationDialog("object_registration.obr") + if filename: + hdr = 'Object' + "\t" + utils.decode(self.obj_name, const.FS_ENCODE) + "\t" + 'Reference' + "\t" + str('%d' % self.obj_ref_mode) + data = np.hstack([self.obj_fiducials, self.obj_orients]) + np.savetxt(filename, data, fmt='%.4f', delimiter='\t', newline='\n', header=hdr) + wx.MessageBox(_("Object file successfully saved"), _("Save")) + + def OnCloseProject(self): + self.checkrecordcoords.SetValue(False) + self.checkrecordcoords.Enable(0) + self.checktrack.SetValue(False) + self.checktrack.Enable(0) + + self.nav_prop = None + self.obj_fiducials = None + self.obj_orients = None + self.obj_ref_mode = None + self.obj_name = None + self.timestamp = const.TIMESTAMP + class MarkersPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) self.SetAutoLayout(1) self.__bind_events() - self.current_coord = 0, 0, 0 + self.current_coord = 0, 0, 0, 0, 0, 0 + self.current_angle = 0, 0, 0 self.list_coord = [] self.marker_ind = 0 + self.tgt_flag = self.tgt_index = None + self.nav_status = False self.marker_colour = (0.0, 0.0, 1.) self.marker_size = 4 @@ -608,8 +1034,8 @@ self.lc.SetColumnWidth(1, 50) self.lc.SetColumnWidth(2, 50) self.lc.SetColumnWidth(3, 50) - self.lc.SetColumnWidth(4, 50) - self.lc.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListEditMarkerId) + self.lc.SetColumnWidth(4, 60) + self.lc.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnMouseRightDown) self.lc.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemBlink) self.lc.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnStopItemBlink) @@ -625,54 +1051,114 @@ self.Update() def __bind_events(self): - Publisher.subscribe(self.UpdateCurrentCoord, 'Set ball reference position') + Publisher.subscribe(self.UpdateCurrentCoord, 'Co-registered points') Publisher.subscribe(self.OnDeleteSingleMarker, 'Delete fiducial marker') + Publisher.subscribe(self.OnDeleteAllMarkers, 'Delete all markers') Publisher.subscribe(self.OnCreateMarker, 'Create marker') + Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status') - def UpdateCurrentCoord(self, pubsub_evt): - self.current_coord = pubsub_evt.data + def UpdateCurrentCoord(self, arg, position): + self.current_coord = position[:] + #self.current_angle = pubsub_evt.data[1][3:] + + def UpdateNavigationStatus(self, status): + if not status: + sleep(0.5) + #self.current_coord[3:] = 0, 0, 0 + self.nav_status = False + else: + self.nav_status = True - def OnListEditMarkerId(self, evt): + def OnMouseRightDown(self, evt): + self.OnListEditMarkerId(self.nav_status) + + def OnListEditMarkerId(self, status): menu_id = wx.Menu() - menu_id.Append(-1, _('Edit ID')) - menu_id.Bind(wx.EVT_MENU, self.OnMenuEditMarkerId) + edit_id = menu_id.Append(0, _('Edit ID')) + menu_id.Bind(wx.EVT_MENU, self.OnMenuEditMarkerId, edit_id) + target_menu = menu_id.Append(1, _('Set as target')) + menu_id.Bind(wx.EVT_MENU, self.OnMenuSetTarget, target_menu) + target_menu.Enable(status) self.PopupMenu(menu_id) menu_id.Destroy() def OnItemBlink(self, evt): - Publisher.sendMessage('Blink Marker', self.lc.GetFocusedItem()) + Publisher.sendMessage('Blink Marker', index=self.lc.GetFocusedItem()) def OnStopItemBlink(self, evt): Publisher.sendMessage('Stop Blink Marker') def OnMenuEditMarkerId(self, evt): - id_label = dlg.EnterMarkerID(self.lc.GetItemText(self.lc.GetFocusedItem(), 4)) list_index = self.lc.GetFocusedItem() + if evt == 'TARGET': + id_label = evt + else: + id_label = dlg.EnterMarkerID(self.lc.GetItemText(list_index, 4)) + if id_label == 'TARGET': + id_label = '' + dlg.InvalidTargetID() self.lc.SetStringItem(list_index, 4, id_label) # Add the new ID to exported list - self.list_coord[list_index][7] = str(id_label) + if len(self.list_coord[list_index]) > 8: + self.list_coord[list_index][10] = str(id_label) + else: + self.list_coord[list_index][7] = str(id_label) + + def OnMenuSetTarget(self, evt): + if isinstance(evt, int): + self.lc.Focus(evt) + + if self.tgt_flag: + self.lc.SetItemBackgroundColour(self.tgt_index, 'white') + Publisher.sendMessage('Set target transparency', status=False, index=self.tgt_index) + self.lc.SetStringItem(self.tgt_index, 4, '') + # Add the new ID to exported list + if len(self.list_coord[self.tgt_index]) > 8: + self.list_coord[self.tgt_index][10] = str('') + else: + self.list_coord[self.tgt_index][7] = str('') - def OnDeleteAllMarkers(self, pubsub_evt): - result = dlg.DeleteAllMarkers() - if result == wx.ID_OK: - self.list_coord = [] - self.marker_ind = 0 - Publisher.sendMessage('Remove all markers', self.lc.GetItemCount()) - self.lc.DeleteAllItems() - Publisher.sendMessage('Stop Blink Marker', 'DeleteAll') + self.tgt_index = self.lc.GetFocusedItem() + self.lc.SetItemBackgroundColour(self.tgt_index, 'RED') - def OnDeleteSingleMarker(self, evt): + Publisher.sendMessage('Update target', coord=self.list_coord[self.tgt_index]) + Publisher.sendMessage('Set target transparency', status=True, index=self.tgt_index) + Publisher.sendMessage('Disable or enable coil tracker', status=True) + self.OnMenuEditMarkerId('TARGET') + self.tgt_flag = True + dlg.NewTarget() + + def OnDeleteAllMarkers(self, evt=None): + if self.list_coord: + if evt is None: + result = wx.ID_OK + else: + result = dlg.DeleteAllMarkers() + + if result == wx.ID_OK: + self.list_coord = [] + self.marker_ind = 0 + Publisher.sendMessage('Remove all markers', indexes=self.lc.GetItemCount()) + self.lc.DeleteAllItems() + Publisher.sendMessage('Stop Blink Marker', index='DeleteAll') + + if self.tgt_flag: + self.tgt_flag = self.tgt_index = None + Publisher.sendMessage('Disable or enable coil tracker', status=False) + if not hasattr(evt, 'data'): + dlg.DeleteTarget() + + def OnDeleteSingleMarker(self, evt=None, marker_id=None): # OnDeleteSingleMarker is used for both pubsub and button click events # Pubsub is used for fiducial handle and button click for all others - if hasattr(evt, 'data'): - marker_id = evt.data + if marker_id is not None: if self.lc.GetItemCount(): for id_n in range(self.lc.GetItemCount()): item = self.lc.GetItem(id_n, 4) if item.GetText() == marker_id: for i in const.BTNS_IMG_MKS: - if marker_id in const.BTNS_IMG_MKS[i].values()[0]: + if marker_id in list(const.BTNS_IMG_MKS[i].values())[0]: self.lc.Focus(item.GetId()) index = [self.lc.GetFocusedItem()] else: @@ -682,6 +1168,10 @@ index = None if index: + if self.tgt_flag and self.tgt_index == index[0]: + self.tgt_flag = self.tgt_index = None + Publisher.sendMessage('Disable or enable coil tracker', status=False) + dlg.DeleteTarget() self.DeleteMarker(index) else: dlg.NoMarkerSelected() @@ -693,14 +1183,14 @@ for n in range(0, self.lc.GetItemCount()): self.lc.SetStringItem(n, 0, str(n+1)) self.marker_ind -= 1 - Publisher.sendMessage('Remove marker', index) + Publisher.sendMessage('Remove marker', index=index) - def OnCreateMarker(self, evt): + def OnCreateMarker(self, evt=None, coord=None, marker_id=None): # OnCreateMarker is used for both pubsub and button click events # Pubsub is used for markers created with fiducial buttons, trigger and create marker button - if hasattr(evt, 'data'): - if evt.data is not None: - self.CreateMarker(evt.data[0], (0.0, 1.0, 0.0), self.marker_size, evt.data[1]) + if evt is None: + if coord: + self.CreateMarker(coord, (0.0, 1.0, 0.0), self.marker_size, marker_id) else: self.CreateMarker(self.current_coord, self.marker_colour, self.marker_size) else: @@ -711,30 +1201,52 @@ if filepath: try: + count_line = self.lc.GetItemCount() content = [s.rstrip() for s in open(filepath)] for data in content: + target = None line = [s for s in data.split()] - coord = float(line[0]), float(line[1]), float(line[2]) - colour = float(line[3]), float(line[4]), float(line[5]) - size = float(line[6]) + if len(line) > 8: + coord = float(line[0]), float(line[1]), float(line[2]), float(line[3]), float(line[4]), float(line[5]) + colour = float(line[6]), float(line[7]), float(line[8]) + size = float(line[9]) + + if len(line) == 11: + for i in const.BTNS_IMG_MKS: + if line[10] in list(const.BTNS_IMG_MKS[i].values())[0]: + Publisher.sendMessage('Load image fiducials', marker_id=line[10], coord=coord) + elif line[10] == 'TARGET': + target = count_line + else: + line.append("") + + self.CreateMarker(coord, colour, size, line[10]) + if target is not None: + self.OnMenuSetTarget(target) - if len(line) == 8: - for i in const.BTNS_IMG_MKS: - if line[7] in const.BTNS_IMG_MKS[i].values()[0]: - Publisher.sendMessage('Load image fiducials', (line[7], coord)) else: - line.append("") - self.CreateMarker(coord, colour, size, line[7]) + coord = float(line[0]), float(line[1]), float(line[2]), 0, 0, 0 + colour = float(line[3]), float(line[4]), float(line[5]) + size = float(line[6]) + + if len(line) == 8: + for i in const.BTNS_IMG_MKS: + if line[7] in list(const.BTNS_IMG_MKS[i].values())[0]: + Publisher.sendMessage('Load image fiducials', marker_id=line[7], coord=coord) + else: + line.append("") + self.CreateMarker(coord, colour, size, line[7]) + count_line += 1 except: dlg.InvalidMarkersFile() def OnMarkersVisibility(self, evt, ctrl): if ctrl.GetValue(): - Publisher.sendMessage('Hide all markers', self.lc.GetItemCount()) + Publisher.sendMessage('Hide all markers', indexes=self.lc.GetItemCount()) ctrl.SetLabel('Show') else: - Publisher.sendMessage('Show all markers', self.lc.GetItemCount()) + Publisher.sendMessage('Show all markers', indexes=self.lc.GetItemCount()) ctrl.SetLabel('Hide') def OnSaveMarkers(self, evt): @@ -744,14 +1256,16 @@ text_file = open(filename, "w") list_slice1 = self.list_coord[0] coord = str('%.3f' %self.list_coord[0][0]) + "\t" + str('%.3f' %self.list_coord[0][1]) + "\t" + str('%.3f' %self.list_coord[0][2]) - properties = str('%.3f' %list_slice1[3]) + "\t" + str('%.3f' %list_slice1[4]) + "\t" + str('%.3f' %list_slice1[5]) + "\t" + str('%.1f' %list_slice1[6]) + "\t" + list_slice1[7] - line = coord + "\t" + properties + "\n" + angles = str('%.3f' %self.list_coord[0][3]) + "\t" + str('%.3f' %self.list_coord[0][4]) + "\t" + str('%.3f' %self.list_coord[0][5]) + properties = str('%.3f' %list_slice1[6]) + "\t" + str('%.3f' %list_slice1[7]) + "\t" + str('%.3f' %list_slice1[8]) + "\t" + str('%.1f' %list_slice1[9]) + "\t" + list_slice1[10] + line = coord + "\t" + angles + "\t" + properties + "\n" list_slice = self.list_coord[1:] for value in list_slice: coord = str('%.3f' %value[0]) + "\t" + str('%.3f' %value[1]) + "\t" + str('%.3f' %value[2]) - properties = str('%.3f' %value[3]) + "\t" + str('%.3f' %value[4]) + "\t" + str('%.3f' %value[5]) + "\t" + str('%.1f' %value[6]) + "\t" + value[7] - line = line + coord + "\t" + properties + "\n" + angles = str('%.3f' % value[3]) + "\t" + str('%.3f' % value[4]) + "\t" + str('%.3f' % value[5]) + properties = str('%.3f' %value[6]) + "\t" + str('%.3f' %value[7]) + "\t" + str('%.3f' %value[8]) + "\t" + str('%.1f' %value[9]) + "\t" + value[10] + line = line + coord + "\t" + angles + "\t" +properties + "\n" text_file.writelines(line) text_file.close() @@ -766,12 +1280,13 @@ # TODO: Use matrix coordinates and not world coordinates as current method. # This makes easier for inter-software comprehension. - Publisher.sendMessage('Add marker', (self.marker_ind, size, colour, coord)) + Publisher.sendMessage('Add marker', ball_id=self.marker_ind, size=size, colour=colour, coord=coord[0:3]) self.marker_ind += 1 # List of lists with coordinates and properties of a marker - line = [coord[0], coord[1], coord[2], colour[0], colour[1], colour[2], self.marker_size, marker_id] + + line = [coord[0], coord[1], coord[2], coord[3], coord[4], coord[5], colour[0], colour[1], colour[2], size, marker_id] # Adding current line to a list of all markers already created if not self.list_coord: diff -Nru invesalius-3.1.1/invesalius/gui/task_slice.py invesalius-3.1.99991/invesalius/gui/task_slice.py --- invesalius-3.1.1/invesalius/gui/task_slice.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/task_slice.py 2018-08-17 16:41:39.000000000 +0000 @@ -21,9 +21,15 @@ import os import wx -import wx.lib.hyperlink as hl + +try: + import wx.lib.agw.hyperlink as hl + import wx.lib.agw.foldpanelbar as fpb +except ImportError: + import wx.lib.hyperlink as hl + import wx.lib.foldpanelbar as fpb + import wx.lib.platebtn as pbtn -import wx.lib.foldpanelbar as fpb import wx.lib.colourselect as csel from wx.lib.pubsub import pub as Publisher @@ -93,6 +99,8 @@ link_new_mask.AutoBrowse(False) link_new_mask.UpdateLink() link_new_mask.Bind(hl.EVT_HYPERLINK_LEFT, self.OnLinkNewMask) + + Publisher.subscribe(self.OnLinkNewMask, 'New mask from shortcut') # Create horizontal sizers to represent lines in the panel line_new = wx.BoxSizer(wx.HORIZONTAL) @@ -108,7 +116,10 @@ #print wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) #print wx.SystemSettings_GetColour(wx.SYS_COLOUR_SCROLLBAR) #print wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUHILIGHT) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) fold_panel = FoldPanel(self) fold_panel.SetBackgroundColour(default_colour) self.fold_panel = fold_panel @@ -134,7 +145,7 @@ main_sizer = wx.BoxSizer(wx.VERTICAL) main_sizer.Add(line_new, 0,wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 5) main_sizer.Add(fold_panel, 1, wx.GROW|wx.EXPAND|wx.ALL, 5) - main_sizer.AddSizer(line_sizer, 0, wx.GROW|wx.EXPAND) + main_sizer.Add(line_sizer, 0, wx.GROW|wx.EXPAND) main_sizer.AddSpacer(5) main_sizer.Fit(self) @@ -179,13 +190,20 @@ "overwrite": overwrite} Publisher.sendMessage('Create surface from index', - {'method': method, 'options': srf_options}) + surface_parameters={'method': method, 'options': srf_options}) Publisher.sendMessage('Fold surface task') else: dlg.InexistentMask() def OnLinkNewMask(self, evt=None): + + try: + evt.data + evt = None + except: + pass + dialog = dlg.NewMask() try: @@ -200,7 +218,9 @@ mask_name, thresh, colour = dialog.GetValue() if mask_name: Publisher.sendMessage('Create new mask', - (mask_name, thresh, colour)) + mask_name=mask_name, + thresh=thresh, + colour=colour) dialog.Destroy() def GetMaskSelected(self): @@ -229,7 +249,10 @@ class InnerFoldPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) # Fold panel and its style settings @@ -350,23 +373,21 @@ if self.__id_editor == id: if closed: - Publisher.sendMessage('Disable style', const.SLICE_STATE_EDITOR) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_EDITOR) self.last_style = None else: - Publisher.sendMessage('Enable style', - const.SLICE_STATE_EDITOR) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_EDITOR) self.last_style = const.SLICE_STATE_EDITOR elif self.__id_watershed == id: if closed: - Publisher.sendMessage('Disable style', - const.SLICE_STATE_WATERSHED) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_WATERSHED) self.last_style = None else: - Publisher.sendMessage('Enable style', const.SLICE_STATE_WATERSHED) - Publisher.sendMessage('Show help message', 'Mark the object and the background') + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_WATERSHED) + # Publisher.sendMessage('Show help message', 'Mark the object and the background') self.last_style = const.SLICE_STATE_WATERSHED else: - Publisher.sendMessage('Disable style', const.SLICE_STATE_EDITOR) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_EDITOR) self.last_style = None evt.Skip() @@ -378,18 +399,18 @@ self.fold_panel.SetMinSize((self.fold_panel.GetSize()[0], sizeNeeded )) self.fold_panel.SetSize((self.fold_panel.GetSize()[0], sizeNeeded)) - def OnRetrieveStyle(self, pubsub_evt): + def OnRetrieveStyle(self): if (self.last_style == const.SLICE_STATE_EDITOR): - Publisher.sendMessage('Enable style', const.SLICE_STATE_EDITOR) + Publisher.sendMessage('Enable style', style=const.SLICE_STATE_EDITOR) - def OnDisableStyle(self, pubsub_evt): + def OnDisableStyle(self): if (self.last_style == const.SLICE_STATE_EDITOR): - Publisher.sendMessage('Disable style', const.SLICE_STATE_EDITOR) + Publisher.sendMessage('Disable style', style=const.SLICE_STATE_EDITOR) - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.fold_panel.Expand(self.fold_panel.GetFoldPanel(0)) - def OnColapsePanel(self, pubsub_evt): + def OnColapsePanel(self, panel_id): panel_seg_id = { const.ID_THRESHOLD_SEGMENTATION: 0, const.ID_MANUAL_SEGMENTATION: 1, @@ -397,7 +418,7 @@ } try: - _id = panel_seg_id[pubsub_evt.data] + _id = panel_seg_id[panel_id] self.fold_panel.Expand(self.fold_panel.GetFoldPanel(_id)) except KeyError: pass @@ -494,20 +515,19 @@ Publisher.subscribe(self.OnCloseProject, 'Close project data') Publisher.subscribe(self.SetThresholdValues2, 'Set threshold values') - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.CloseProject() def CloseProject(self): n = self.combo_mask_name.GetCount() - for i in xrange(n-1, -1, -1): + for i in range(n-1, -1, -1): self.combo_mask_name.Delete(i) n = self.combo_thresh.GetCount() - for i in xrange(n-1, -1, -1): + for i in range(n-1, -1, -1): self.combo_thresh.Delete(i) - def OnRemoveMasks(self, pubsub_evt): - list_index = pubsub_evt.data - for i in list_index: + def OnRemoveMasks(self, mask_indexes): + for i in mask_indexes: self.combo_mask_name.Delete(i) if self.combo_mask_name.IsEmpty(): @@ -522,20 +542,18 @@ self.combo_mask_name.Bind(wx.EVT_COMBOBOX, self.OnComboName) self.button_colour.Bind(csel.EVT_COLOURSELECT, self.OnSelectColour) - def SelectMaskName(self, pubsub_evt): - index = pubsub_evt.data + def SelectMaskName(self, index): if index >= 0: self.combo_mask_name.SetSelection(index) else: self.combo_mask_name.SetValue('') - def ChangeMaskName(self, pubsub_evt): - index, name = pubsub_evt.data + def ChangeMaskName(self, index, name): self.combo_mask_name.SetString(index, name) self.combo_mask_name.Refresh() - def SetThresholdValues(self, pubsub_evt): - thresh_min, thresh_max = pubsub_evt.data + def SetThresholdValues(self, threshold_range): + thresh_min, thresh_max = threshold_range self.bind_evt_gradient = False self.gradient.SetMinValue(thresh_min) self.gradient.SetMaxValue(thresh_max) @@ -551,8 +569,8 @@ self.combo_thresh.SetSelection(index) Project().threshold_modes[_("Custom")] = (thresh_min, thresh_max) - def SetThresholdValues2(self, pubsub_evt): - thresh_min, thresh_max = pubsub_evt.data + def SetThresholdValues2(self, threshold_range): + thresh_min, thresh_max = threshold_range self.gradient.SetMinValue(thresh_min) self.gradient.SetMaxValue(thresh_max) thresh = (thresh_min, thresh_max) @@ -565,17 +583,16 @@ self.combo_thresh.SetSelection(index) Project().threshold_modes[_("Custom")] = (thresh_min, thresh_max) - def SetItemsColour(self, evt_pubsub): - colour = evt_pubsub.data + def SetItemsColour(self, colour): self.gradient.SetColour(colour) self.button_colour.SetColour(colour) - def AddMask(self, evt_pubsub): + def AddMask(self, mask): if self.combo_mask_name.IsEmpty(): self.Enable() - mask_name = evt_pubsub.data[1] - mask_thresh = evt_pubsub.data[2] - mask_colour = [int(c*255) for c in evt_pubsub.data[3]] + mask_name = mask.name + mask_thresh = mask.threshold_range + mask_colour = [int(c*255) for c in mask.colour] index = self.combo_mask_name.Append(mask_name) # self.combo_mask_name.SetSelection(index) # self.button_colour.SetColour(mask_colour) @@ -586,8 +603,7 @@ x = self.combo_mask_name.GetSelection() return self.combo_mask_name.GetSelection() - def SetThresholdModes(self, pubsub_evt): - (thresh_modes_names, default_thresh) = pubsub_evt.data + def SetThresholdModes(self, thresh_modes_names, default_thresh): self.combo_thresh.SetItems(thresh_modes_names) self.threshold_modes_names = thresh_modes_names proj = Project() @@ -614,17 +630,17 @@ self.gradient.SetMinValue(thresh_min) self.gradient.SetMaxValue(thresh_max) - def SetThresholdBounds(self, pubsub_evt): - thresh_min = pubsub_evt.data[0] - thresh_max = pubsub_evt.data[1] + def SetThresholdBounds(self, threshold_range): + thresh_min = threshold_range[0] + thresh_max = threshold_range[1] self.gradient.SetMinRange(thresh_min) self.gradient.SetMaxRange(thresh_max) def OnComboName(self, evt): mask_name = evt.GetString() mask_index = evt.GetSelection() - Publisher.sendMessage('Change mask selected', mask_index) - Publisher.sendMessage('Show mask', (mask_index, True)) + Publisher.sendMessage('Change mask selected', index=mask_index) + Publisher.sendMessage('Show mask', index=mask_index, value=True) def OnComboThresh(self, evt): (thresh_min, thresh_max) = Project().threshold_modes[evt.GetString()] @@ -637,7 +653,7 @@ thresh_min = self.gradient.GetMinValue() thresh_max = self.gradient.GetMaxValue() Publisher.sendMessage('Set threshold values', - (thresh_min, thresh_max)) + threshold_range=(thresh_min, thresh_max)) session = ses.Session() session.ChangeProject() @@ -645,19 +661,22 @@ thresh_min = self.gradient.GetMinValue() thresh_max = self.gradient.GetMaxValue() Publisher.sendMessage('Changing threshold values', - (thresh_min, thresh_max)) + threshold_range=(thresh_min, thresh_max)) session = ses.Session() session.ChangeProject() def OnSelectColour(self, evt): - colour = evt.GetValue() + colour = evt.GetValue()[:3] self.gradient.SetColour(colour) - Publisher.sendMessage('Change mask colour', colour) + Publisher.sendMessage('Change mask colour', colour=colour) class EditionTools(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) ## LINE 1 @@ -754,25 +773,23 @@ Publisher.subscribe(self.SetGradientColour, 'Add mask') Publisher.subscribe(self._set_brush_size, 'Set edition brush size') - def ChangeMaskColour(self, pubsub_evt): - colour = pubsub_evt.data + def ChangeMaskColour(self, colour): self.gradient_thresh.SetColour(colour) - def SetGradientColour(self, pubsub_evt): - vtk_colour = pubsub_evt.data[3] - wx_colour = [c*255 for c in vtk_colour] + def SetGradientColour(self, mask): + wx_colour = [c*255 for c in mask.colour] self.gradient_thresh.SetColour(wx_colour) - def SetThresholdValues(self, pubsub_evt): - thresh_min, thresh_max = pubsub_evt.data + def SetThresholdValues(self, threshold_range): + thresh_min, thresh_max = threshold_range self.bind_evt_gradient = False self.gradient_thresh.SetMinValue(thresh_min) self.gradient_thresh.SetMaxValue(thresh_max) self.bind_evt_gradient = True - def SetThresholdBounds(self, pubsub_evt): - thresh_min = pubsub_evt.data[0] - thresh_max = pubsub_evt.data[1] + def SetThresholdBounds(self, threshold_range): + thresh_min = threshold_range[0] + thresh_max = threshold_range[1] self.gradient_thresh.SetMinRange(thresh_min) self.gradient_thresh.SetMaxRange(thresh_max) self.gradient_thresh.SetMinValue(thresh_min) @@ -783,7 +800,7 @@ thresh_max = self.gradient_thresh.GetMaxValue() if self.bind_evt_gradient: Publisher.sendMessage('Set edition threshold values', - (thresh_min, thresh_max)) + threshold_range=(thresh_min, thresh_max)) def OnMenu(self, evt): SQUARE_BMP = wx.Bitmap(os.path.join(const.ICON_DIR, "brush_square.jpg"), wx.BITMAP_TYPE_JPEG) @@ -796,22 +813,21 @@ self.btn_brush_format.SetBitmap(bitmap[evt.GetId()]) - Publisher.sendMessage('Set brush format', brush[evt.GetId()]) + Publisher.sendMessage('Set brush format', cursor_format=brush[evt.GetId()]) def OnBrushSize(self, evt): """ """ # FIXME: Using wx.EVT_SPINCTRL in MacOS it doesnt capture changes only # in the text ctrl - so we are capturing only changes on text # Strangelly this is being called twice - Publisher.sendMessage('Set edition brush size',self.spin.GetValue()) + Publisher.sendMessage('Set edition brush size', size=self.spin.GetValue()) - def _set_brush_size(self, pubsub_evt): - size = pubsub_evt.data + def _set_brush_size(self, size): self.spin.SetValue(size) def OnComboBrushOp(self, evt): brush_op_id = evt.GetSelection() - Publisher.sendMessage('Set edition operation', brush_op_id) + Publisher.sendMessage('Set edition operation', operation=brush_op_id) if brush_op_id == const.BRUSH_THRESH: self.gradient_thresh.Enable() else: @@ -821,7 +837,10 @@ class WatershedTool(EditionTools): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) ## LINE 1 @@ -932,25 +951,24 @@ def __bind_pubsub_evt(self): Publisher.subscribe(self._set_brush_size, 'Set watershed brush size') - def ChangeMaskColour(self, pubsub_evt): - colour = pubsub_evt.data + def ChangeMaskColour(self, colour): self.gradient_thresh.SetColour(colour) - def SetGradientColour(self, pubsub_evt): - vtk_colour = pubsub_evt.data[3] + def SetGradientColour(self, mask): + vtk_colour = mask.colour wx_colour = [c*255 for c in vtk_colour] self.gradient_thresh.SetColour(wx_colour) - def SetThresholdValues(self, pubsub_evt): - thresh_min, thresh_max = pubsub_evt.data + def SetThresholdValues(self, threshold_range): + thresh_min, thresh_max = threshold_range self.bind_evt_gradient = False self.gradient_thresh.SetMinValue(thresh_min) self.gradient_thresh.SetMaxValue(thresh_max) self.bind_evt_gradient = True - def SetThresholdBounds(self, pubsub_evt): - thresh_min = pubsub_evt.data[0] - thresh_max = pubsub_evt.data[1] + def SetThresholdBounds(self, threshold_range): + thresh_min = threshold_range[0] + thresh_max = threshold_range[1] self.gradient_thresh.SetMinRange(thresh_min) self.gradient_thresh.SetMaxRange(thresh_max) self.gradient_thresh.SetMinValue(thresh_min) @@ -967,30 +985,29 @@ self.btn_brush_format.SetBitmap(bitmap[evt.GetId()]) - Publisher.sendMessage('Set watershed brush format', brush[evt.GetId()]) + Publisher.sendMessage('Set watershed brush format', brush_format=brush[evt.GetId()]) def OnBrushSize(self, evt): """ """ # FIXME: Using wx.EVT_SPINCTRL in MacOS it doesnt capture changes only # in the text ctrl - so we are capturing only changes on text # Strangelly this is being called twice - Publisher.sendMessage('Set watershed brush size',self.spin.GetValue()) + Publisher.sendMessage('Set watershed brush size', size=self.spin.GetValue()) - def _set_brush_size(self, pubsub_evt): - size = pubsub_evt.data + def _set_brush_size(self, size): self.spin.SetValue(size) def OnComboBrushOp(self, evt): brush_op = self.combo_brush_op.GetValue() - Publisher.sendMessage('Set watershed operation', brush_op) + Publisher.sendMessage('Set watershed operation', operation=brush_op) def OnCheckOverwriteMask(self, evt): value = self.check_box.GetValue() - Publisher.sendMessage('Set overwrite mask', value) + Publisher.sendMessage('Set overwrite mask', flag=value) def OnCheckWWWL(self, evt): value = self.ww_wl_cbox.GetValue() - Publisher.sendMessage('Set use ww wl', value) + Publisher.sendMessage('Set use ww wl', use_ww_wl=value) def OnConfig(self, evt): from invesalius.data.styles import WatershedConfig diff -Nru invesalius-3.1.1/invesalius/gui/task_surface.py invesalius-3.1.99991/invesalius/gui/task_surface.py --- invesalius-3.1.1/invesalius/gui/task_surface.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/task_surface.py 2018-08-17 16:41:39.000000000 +0000 @@ -20,9 +20,15 @@ import os import wx -import wx.lib.hyperlink as hl + +try: + import wx.lib.agw.hyperlink as hl + import wx.lib.agw.foldpanelbar as fpb +except ImportError: + import wx.lib.hyperlink as hl + import wx.lib.foldpanelbar as fpb + from wx.lib.pubsub import pub as Publisher -import wx.lib.foldpanelbar as fpb import wx.lib.colourselect as csel import invesalius.constants as const @@ -96,6 +102,7 @@ link_new_surface.UpdateLink() link_new_surface.Bind(hl.EVT_HYPERLINK_LEFT, self.OnLinkNewSurface) + Publisher.subscribe(self.OnLinkNewSurface, 'Open create surface dialog') # Create horizontal sizers to represent lines in the panel line_new = wx.BoxSizer(wx.HORIZONTAL) line_new.Add(link_new_surface, 1, wx.EXPAND|wx.GROW| wx.TOP|wx.RIGHT, 4) @@ -135,8 +142,22 @@ evt.Skip() def OnLinkNewSurface(self, evt=None): + + is_pubsub = True + + try: + evt = evt.data + evt = None + except: + pass + #import invesalius.gui.dialogs as dlg sl = slice_.Slice() + + if sl.current_mask == None: + dlg.InexistentMask() + return + dialog = dlg.SurfaceCreationDialog(None, -1, _('New surface'), mask_edited=sl.current_mask.was_edited) @@ -172,7 +193,8 @@ surface_options = dialog.GetValue() - Publisher.sendMessage('Create surface from index', surface_options) + Publisher.sendMessage('Create surface from index', + surface_parameters=surface_options) dialog.Destroy() if evt: evt.Skip() @@ -195,7 +217,10 @@ class InnerFoldPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) # Fold panel and its style settings @@ -264,7 +289,10 @@ class SurfaceTools(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) #self.SetBackgroundColour(wx.Colour(255,255,255)) @@ -395,13 +423,13 @@ self.EndSeeding() def StartSeeding(self): - print "Start Seeding" - Publisher.sendMessage('Enable style', const.VOLUME_STATE_SEED) + print("Start Seeding") + Publisher.sendMessage('Enable style', style=const.VOLUME_STATE_SEED) Publisher.sendMessage('Create surface by seeding - start') def EndSeeding(self): - print "End Seeding" - Publisher.sendMessage('Disable style', const.VOLUME_STATE_SEED) + print("End Seeding") + Publisher.sendMessage('Disable style', style=const.VOLUME_STATE_SEED) Publisher.sendMessage('Create surface by seeding - end') @@ -409,7 +437,10 @@ class SurfaceProperties(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) self.surface_list = [] @@ -484,8 +515,7 @@ Publisher.subscribe(self.OnRemoveSurfaces, 'Remove surfaces') - def OnRemoveSurfaces(self, pubsub_evt): - list_index = pubsub_evt.data + def OnRemoveSurfaces(self, surface_indexes): s = self.combo_surface_name.GetSelection() ns = 0 @@ -493,7 +523,7 @@ new_dict = [] i = 0 for n, (name, index) in enumerate(old_dict): - if n not in list_index: + if n not in surface_indexes: new_dict.append([name, i]) if s == n: ns = i @@ -505,25 +535,23 @@ if self.surface_list: self.combo_surface_name.SetSelection(ns) - def OnCloseProject(self, pubsub_evt): + def OnCloseProject(self): self.CloseProject() def CloseProject(self): n = self.combo_surface_name.GetCount() - for i in xrange(n-1, -1, -1): + for i in range(n-1, -1, -1): self.combo_surface_name.Delete(i) self.surface_list = [] - def ChangeSurfaceName(self, pubsub_evt): - index, name = pubsub_evt.data + def ChangeSurfaceName(self, index, name): self.surface_list[index][0] = name self.combo_surface_name.SetString(index, name) - def InsertNewSurface(self, pubsub_evt): - #not_update = len(pubsub_evt.data) == 5 - index = pubsub_evt.data[0] - name = pubsub_evt.data[1] - colour = [value*255 for value in pubsub_evt.data[2]] + def InsertNewSurface(self, surface): + index = surface.index + name = surface.name + colour = [value*255 for value in surface.colour] i = 0 try: i = self.surface_list.index([name, index]) @@ -539,21 +567,21 @@ self.combo_surface_name.SetItems([n[0] for n in self.surface_list]) self.combo_surface_name.SetSelection(i) - transparency = 100*pubsub_evt.data[5] + transparency = 100*surface.transparency self.button_colour.SetColour(colour) self.slider_transparency.SetValue(transparency) - Publisher.sendMessage('Update surface data', (index)) + # Publisher.sendMessage('Update surface data', (index)) def OnComboName(self, evt): surface_name = evt.GetString() surface_index = evt.GetSelection() - Publisher.sendMessage('Change surface selected', self.surface_list[surface_index][1]) + Publisher.sendMessage('Change surface selected', surface_index=self.surface_list[surface_index][1]) def OnSelectColour(self, evt): colour = [value/255.0 for value in evt.GetValue()] Publisher.sendMessage('Set surface colour', - (self.combo_surface_name.GetSelection(), - colour)) + surface_index=self.combo_surface_name.GetSelection(), + colour=colour) def OnTransparency(self, evt): transparency = evt.GetInt()/float(MAX_TRANSPARENCY) @@ -563,15 +591,18 @@ if (wx.Platform == "__WXMAC__"): transparency = evt.GetInt()/(0.96*float(MAX_TRANSPARENCY)) Publisher.sendMessage('Set surface transparency', - (self.combo_surface_name.GetSelection(), - transparency)) + surface_index=self.combo_surface_name.GetSelection(), + transparency=transparency) class QualityAdjustment(wx.Panel): def __init__(self, parent): import invesalius.constants as const wx.Panel.__init__(self, parent) - default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) + try: + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) + except AttributeError: + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) self.SetBackgroundColour(default_colour) # LINE 1 @@ -627,7 +658,7 @@ self.SetAutoLayout(1) def OnComboQuality(self, evt): - print "TODO: Send Signal - Change surface quality: %s" % (evt.GetString()) + print("TODO: Send Signal - Change surface quality: %s" % (evt.GetString())) def OnDecimate(self, evt): - print "TODO: Send Signal - Decimate: %s" % float(self.spin.GetValue())/100 + print("TODO: Send Signal - Decimate: %s" % float(self.spin.GetValue())/100) diff -Nru invesalius-3.1.1/invesalius/gui/task_tools.py invesalius-3.1.99991/invesalius/gui/task_tools.py --- invesalius-3.1.1/invesalius/gui/task_tools.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/task_tools.py 2018-08-17 16:41:39.000000000 +0000 @@ -20,7 +20,12 @@ import wx import os import wx.lib.embeddedimage as emb -import wx.lib.hyperlink as hl + +try: + import wx.lib.agw.hyperlink as hl +except ImportError: + import wx.lib.hyperlink as hl + import wx.lib.platebtn as pbtn from wx.lib.pubsub import pub as Publisher @@ -120,15 +125,13 @@ self.sizer = main_sizer def OnTextAnnotation(self, evt=None): - print "TODO: Send Signal - Add text annotation (both 2d and 3d)" + print("TODO: Send Signal - Add text annotation (both 2d and 3d)") def OnLinkLinearMeasure(self): - Publisher.sendMessage('Enable style', - constants.STATE_MEASURE_DISTANCE) + Publisher.sendMessage('Enable style', style=constants.STATE_MEASURE_DISTANCE) def OnLinkAngularMeasure(self): - Publisher.sendMessage('Enable style', - constants.STATE_MEASURE_ANGLE) + Publisher.sendMessage('Enable style', style=constants.STATE_MEASURE_ANGLE) def OnButton(self, evt): id = evt.GetId() diff -Nru invesalius-3.1.1/invesalius/gui/widgets/clut_imagedata.py invesalius-3.1.99991/invesalius/gui/widgets/clut_imagedata.py --- invesalius-3.1.1/invesalius/gui/widgets/clut_imagedata.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/widgets/clut_imagedata.py 2018-08-17 16:41:39.000000000 +0000 @@ -1,3 +1,4 @@ +import functools import math import wx @@ -33,6 +34,7 @@ EVT_CLUT_NODE_CHANGED = wx.PyEventBinder(myEVT_CLUT_NODE_CHANGED, 1) +@functools.total_ordering class Node(object): def __init__(self, value, colour): self.value = value @@ -41,6 +43,12 @@ def __cmp__(self, o): return cmp(self.value, o.value) + def __lt__(self, other): + return self.value < other.value + + def __eq__(self, other): + return self.value == other.value + def __repr__(self): return "(%d %s)" % (self.value, self.colour) @@ -147,7 +155,7 @@ prop_y = (h) * 1.0 / (y_end - y_init) self._d_hist = [] - for i in xrange(w): + for i in range(w): x = i / prop_x + x_init - 1 if self.i_init <= x < self.i_end: try: @@ -218,7 +226,7 @@ self.middle_pressed = False def OnClick(self, evt): - px, py = evt.GetPositionTuple() + px, py = evt.GetPosition() self.left_pressed = True self.selected_node = self.get_node_clicked(px, py) self.last_selected = self.selected_node @@ -231,7 +239,7 @@ def OnDoubleClick(self, evt): w, h = self.GetVirtualSize() - px, py = evt.GetPositionTuple() + px, py = evt.GetPosition() # Verifying if the user double-click in a node-colour. selected_node = self.get_node_clicked(px, py) @@ -240,7 +248,7 @@ # option to change the color from this node. colour_dialog = wx.GetColourFromUser(self, (0, 0, 0)) if colour_dialog.IsOk(): - r, g, b = colour_dialog.Get() + r, g, b = colour_dialog.Get()[:3] selected_node.colour = r, g, b self._generate_event() else: @@ -255,7 +263,7 @@ def OnRightClick(self, evt): w, h = self.GetVirtualSize() - px, py = evt.GetPositionTuple() + px, py = evt.GetPosition() selected_node = self.get_node_clicked(px, py) if selected_node: diff -Nru invesalius-3.1.1/invesalius/gui/widgets/clut_raycasting.py invesalius-3.1.99991/invesalius/gui/widgets/clut_raycasting.py --- invesalius-3.1.1/invesalius/gui/widgets/clut_raycasting.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/widgets/clut_raycasting.py 2018-08-17 16:41:39.000000000 +0000 @@ -97,8 +97,8 @@ """ Test if the button was clicked. """ - print self.position - print self.size + print(self.position) + print(self.size) m_x, m_y = position i_x, i_y = self.position w, h = self.size @@ -146,7 +146,7 @@ Se the range from hounsfield """ self.init, self.end = range - print "Range", range + print("Range", range) self.CalculatePixelPoints() def SetPadding(self, padding): @@ -169,12 +169,12 @@ pass def OnClick(self, evt): - x, y = evt.GetPositionTuple() - if self.save_button.HasClicked(evt.GetPositionTuple()): - print "Salvando" + x, y = evt.GetPosition() + if self.save_button.HasClicked((x, y)): + print("Salvando") filename = dialog.ShowSavePresetDialog() if filename: - Publisher.sendMessage('Save raycasting preset', filename) + Publisher.sendMessage('Save raycasting preset', preset_name=filename) point = self._has_clicked_in_a_point((x, y)) # A point has been selected. It can be dragged. if point: @@ -218,7 +218,7 @@ """ Used to change the colour of a point """ - point = self._has_clicked_in_a_point(evt.GetPositionTuple()) + point = self._has_clicked_in_a_point(evt.GetPosition()) if point: i, j = point actual_colour = self.curves[i].nodes[j].colour @@ -240,18 +240,18 @@ """ Used to remove a point """ - point = self._has_clicked_in_a_point(evt.GetPositionTuple()) + point = self._has_clicked_in_a_point(evt.GetPosition()) if point: i, j = point - print "RightClick", i, j + print("RightClick", i, j) self.RemovePoint(i, j) self.Refresh() nevt = CLUTEvent(myEVT_CLUT_POINT_RELEASE, self.GetId(), i) self.GetEventHandler().ProcessEvent(nevt) return - n_curve = self._has_clicked_in_selection_curve(evt.GetPositionTuple()) + n_curve = self._has_clicked_in_selection_curve(evt.GetPosition()) if n_curve is not None: - print "Removing a curve" + print("Removing a curve") self.RemoveCurve(n_curve) self.Refresh() nevt = CLUTEvent(myEVT_CLUT_POINT_RELEASE, self.GetId(), n_curve) @@ -280,7 +280,7 @@ direction = evt.GetWheelRotation() / evt.GetWheelDelta() init = self.init - RANGE * direction end = self.end + RANGE * direction - print direction, init, end + print(direction, init, end) self.SetRange((init, end)) self.Refresh() @@ -369,7 +369,7 @@ def _has_clicked_in_save(self, clicked_point): x, y = clicked_point - print x, y + print(x, y) if self.padding < x < self.padding + 24 and \ self.padding < y < self.padding + 24: return True @@ -559,7 +559,7 @@ def _draw_histogram(self, ctx, height): # The histogram x,y = self.Histogram.points[0] - print "=>", x,y + print("=>", x,y) ctx.SetPen(wx.Pen(HISTOGRAM_LINE_COLOUR, HISTOGRAM_LINE_WIDTH)) ctx.SetBrush(wx.Brush(HISTOGRAM_FILL_COLOUR)) @@ -567,7 +567,7 @@ path = ctx.CreatePath() path.MoveToPoint(x,y) for x,y in self.Histogram.points: - print x,y + print(x,y) path.AddLineToPoint(x, y) ctx.PushState() @@ -624,7 +624,7 @@ proportion_x = width * 1.0 / (x_end - x_init) proportion_y = height * 1.0 / (y_end - y_init) self.Histogram.points = [] - for i in xrange(0, len(self.histogram_array), 5): + for i in range(0, len(self.histogram_array), 5): if self.histogram_array[i]: y = math.log(self.histogram_array[i]) else: @@ -649,7 +649,7 @@ """ for n, (point, colour) in enumerate(zip(self.points, self.colours)): point_colour = zip(point, colour) - point_colour.sort(key=lambda x: x[0]['x']) + point_colour = sorted(point_colour, key=lambda x: x[0]['x']) self.points[n] = [i[0] for i in point_colour] self.colours[n] = [i[1] for i in point_colour] diff -Nru invesalius-3.1.1/invesalius/gui/widgets/gradient.py invesalius-3.1.99991/invesalius/gui/widgets/gradient.py --- invesalius-3.1.1/invesalius/gui/widgets/gradient.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/widgets/gradient.py 2018-08-17 16:41:39.000000000 +0000 @@ -121,8 +121,11 @@ dc.GradientFillLinear((x_init_gradient, y_init_gradient, width_gradient, height_gradient), (0, 0, 0), (255,255, 255)) - - n = wx.RendererNative_Get() + + try: + n = wx.RendererNative.Get() + except AttributeError: + n = wx.RendererNative_Get() # Drawing the push buttons n.DrawPushButton(self, dc, (x_init_push1, 0, PUSH_WIDTH, h)) @@ -327,7 +330,7 @@ self.max_range = maxRange self.minimun = minValue self.maximun = maxValue - self.colour = colour + self.colour = colour[:3] self.changed = False self._draw_controls() self._bind_events_wx() @@ -433,7 +436,7 @@ self._GenerateEvent(myEVT_THRESHOLD_CHANGING) def SetColour(self, colour): - colour = list(colour) + [90,] + colour = list(colour[:3]) + [90,] self.colour = colour self.gradient_slider.SetColour(colour) self.gradient_slider.Refresh() diff -Nru invesalius-3.1.1/invesalius/gui/widgets/listctrl.py invesalius-3.1.99991/invesalius/gui/widgets/listctrl.py --- invesalius-3.1.1/invesalius/gui/widgets/listctrl.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/widgets/listctrl.py 2018-08-17 16:41:39.000000000 +0000 @@ -63,7 +63,7 @@ self.SetColumnCount(numColumns) list = self.GetListCtrl() if not list: - raise ValueError, "No wx.ListCtrl available" + raise ValueError("No wx.ListCtrl available") list.Bind(wx.EVT_LIST_COL_CLICK, self.__OnColClick, list) @@ -644,19 +644,23 @@ # give the derived class a chance to Allow/Veto this edit. evt = wx.ListEvent(wx.wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, self.GetId()) - evt.m_itemIndex = row - evt.m_col = col + try: + evt.SetIndex(row) + evt.SetColumn(col) + except AttributeError: + evt.m_itemIndex = row + evt.m_col = col item = self.GetItem(row, col) - evt.m_item.SetId(item.GetId()) - evt.m_item.SetColumn(item.GetColumn()) - evt.m_item.SetData(item.GetData()) - evt.m_item.SetText(item.GetText()) + evt.Item.SetId(item.GetId()) + evt.Item.SetColumn(item.GetColumn()) + evt.Item.SetData(item.GetData()) + evt.Item.SetText(item.GetText()) ret = self.GetEventHandler().ProcessEvent(evt) if ret and not evt.IsAllowed(): return # user code doesn't allow the edit. - if self.GetColumn(col).m_format != self.col_style: - self.make_editor(self.GetColumn(col).m_format) + if self.GetColumn(col).Align != self.col_style: + self.make_editor(self.GetColumn(col).Align) x0 = self.col_locs[col] x1 = self.col_locs[col+1] - x0 @@ -717,13 +721,17 @@ # Event can be vetoed. It doesn't has SetEditCanceled(), what would # require passing extra argument to CloseEditor() evt = wx.ListEvent(wx.wxEVT_COMMAND_LIST_END_LABEL_EDIT, self.GetId()) - evt.m_itemIndex = self.curRow - evt.m_col = self.curCol + try: + evt.SetIndex(self.curRow) + evt.SetColumn(self.curCol) + except AttributeError: + evt.m_itemIndex = self.curRow + evt.m_col = self.curCol item = self.GetItem(self.curRow, self.curCol) - evt.m_item.SetId(item.GetId()) - evt.m_item.SetColumn(item.GetColumn()) - evt.m_item.SetData(item.GetData()) - evt.m_item.SetText(text) #should be empty string if editor was canceled + evt.Item.SetId(item.GetId()) + evt.Item.SetColumn(item.GetColumn()) + evt.Item.SetData(item.GetData()) + evt.Item.SetText(text) #should be empty string if editor was canceled ret = self.GetEventHandler().ProcessEvent(evt) if not ret or evt.IsAllowed(): if self.IsVirtual(): diff -Nru invesalius-3.1.1/invesalius/gui/widgets/slice_menu.py invesalius-3.1.99991/invesalius/gui/widgets/slice_menu.py --- invesalius-3.1.1/invesalius/gui/widgets/slice_menu.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/gui/widgets/slice_menu.py 2018-08-17 16:41:39.000000000 +0000 @@ -90,7 +90,7 @@ #------------ Sub menu of the pseudo colors ---------------- - if sys.platform == 'linux2': + if sys.platform.startswith('linux'): mkind = wx.ITEM_CHECK else: mkind = wx.ITEM_RADIO @@ -166,7 +166,7 @@ # It doesn't work in Linux self.Bind(wx.EVT_MENU, self.OnPopup) # In Linux the bind must be putted in the submenu - if sys.platform == 'linux2' or sys.platform == 'darwin': + if sys.platform.startswith('linux') or sys.platform == 'darwin': submenu_wl.Bind(wx.EVT_MENU, self.OnPopup) submenu_pseudo_colours.Bind(wx.EVT_MENU, self.OnPopup) submenu_image_tiling.Bind(wx.EVT_MENU, self.OnPopup) @@ -181,27 +181,26 @@ Publisher.subscribe(self._check_projection_menu, 'Check projection menu') - def FirstItemSelect(self, pusub_evt): + def FirstItemSelect(self): item = self.ID_TO_TOOL_ITEM[self.id_wl_first] - item.Check(1) + item.Check(True) for i in self.pseudo_color_items: it = self.pseudo_color_items[i] if it.IsChecked(): - it.Toggle() + it.Check(False) item = self.ID_TO_TOOL_ITEM[self.id_pseudo_first] - item.Check(1) + item.Check(True) - item = self.ID_TO_TOOL_ITEM[self.id_tiling_first] - item.Check(1) + # item = self.ID_TO_TOOL_ITEM[self.id_tiling_first] + # item.Check(True) - def CheckWindowLevelOther(self, pubsub_evt): + def CheckWindowLevelOther(self): item = self.ID_TO_TOOL_ITEM[self.other_wl_id] item.Check() - def _check_projection_menu(self, pubsub_evt): - p_id = pubsub_evt.data - item = self.projection_items[p_id] + def _check_projection_menu(self, projection_id): + item = self.projection_items[projection_id] item.Check() def OnPopup(self, evt): @@ -211,11 +210,12 @@ if(key in const.WINDOW_LEVEL.keys()): window, level = const.WINDOW_LEVEL[key] Publisher.sendMessage('Bright and contrast adjustment image', - (window, level)) - Publisher.sendMessage('Update window level value',\ - (window, level)) - Publisher.sendMessage('Update window and level text',\ - "WL: %d WW: %d"%(level, window)) + window=window, level=level) + Publisher.sendMessage('Update window level value', + window=window, + level=level) + # Publisher.sendMessage('Update window and level text', + # "WL: %d WW: %d"%(level, window)) Publisher.sendMessage('Update slice viewer') #Necessary update the slice plane in the volume case exists @@ -223,10 +223,10 @@ elif(key in const.SLICE_COLOR_TABLE.keys()): values = const.SLICE_COLOR_TABLE[key] - Publisher.sendMessage('Change colour table from background image', values) + Publisher.sendMessage('Change colour table from background image', values=values) Publisher.sendMessage('Update slice viewer') - if sys.platform == 'linux2': + if sys.platform.startswith('linux'): for i in self.pseudo_color_items: it = self.pseudo_color_items[i] if it.IsChecked(): @@ -238,10 +238,10 @@ elif key in self.plist_presets: values = presets.get_wwwl_preset_colours(self.plist_presets[key]) - Publisher.sendMessage('Change colour table from background image from plist', values) + Publisher.sendMessage('Change colour table from background image from plist', values=values) Publisher.sendMessage('Update slice viewer') - if sys.platform == 'linux2': + if sys.platform.startswith('linux'): for i in self.pseudo_color_items: it = self.pseudo_color_items[i] if it.IsChecked(): @@ -253,12 +253,12 @@ elif(key in const.IMAGE_TILING.keys()): values = const.IMAGE_TILING[key] - Publisher.sendMessage('Set slice viewer layout', values) + Publisher.sendMessage('Set slice viewer layout', layout=values) Publisher.sendMessage('Update slice viewer') elif key in PROJECTIONS_ID: pid = PROJECTIONS_ID[key] - Publisher.sendMessage('Set projection type', pid) + Publisher.sendMessage('Set projection type', projection_id=pid) Publisher.sendMessage('Reload actual slice') elif key == _('Custom'): @@ -273,7 +273,7 @@ else: self.cdialog.Show(self._gen_event) - if sys.platform == 'linux2': + if sys.platform.startswith('linux'): for i in self.pseudo_color_items: it = self.pseudo_color_items[i] if it.IsChecked(): @@ -290,7 +290,7 @@ if self.cdialog: self.cdialog.Hide() - def _close(self, pubsub_evt): + def _close(self): if self.cdialog: self.cdialog.Destroy() self.cdialog = None diff -Nru invesalius-3.1.1/invesalius/i18n.py invesalius-3.1.99991/invesalius/i18n.py --- invesalius-3.1.1/invesalius/i18n.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/i18n.py 2018-08-17 16:41:39.000000000 +0000 @@ -83,6 +83,10 @@ lang = gettext.translation('invesalius', language_dir,\ languages=[language], codeset='utf8') - # Using unicode - lang.install(unicode=1) - return lang.ugettext + # Using unicode + try: + lang.install(unicode=1) + return lang.ugettext + except TypeError: + lang.install() + return lang.gettext diff -Nru invesalius-3.1.1/invesalius/net/dicom.py invesalius-3.1.99991/invesalius/net/dicom.py --- invesalius-3.1.1/invesalius/net/dicom.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/net/dicom.py 2018-08-17 16:41:39.000000000 +0000 @@ -165,14 +165,14 @@ const char *call=NULL, const char *outputdir=NULL)""" - print ">>>>>", self.address, int(self.port), theQuery, 11112, self.aetitle,\ - self.aetitle_call, "/home/phamorim/Desktop/output/" + print(">>>>>", self.address, int(self.port), theQuery, 11112, self.aetitle, + self.aetitle_call, "/home/phamorim/Desktop/output/") cnf.CMove(self.address, int(self.port), theQuery, 11112, self.aetitle,\ self.aetitle_call, "/home/phamorim/Desktop/") - print "BAIXOUUUUUUUU" + print("BAIXOUUUUUUUU") #ret = gdcm.DataSetArrayType() #cnf.CFind(self.address, int(self.port), theQuery, ret, self.aetitle,\ diff -Nru invesalius-3.1.1/invesalius/presets.py invesalius-3.1.99991/invesalius/presets.py --- invesalius-3.1.1/invesalius/presets.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/presets.py 2018-08-17 16:41:39.000000000 +0000 @@ -43,7 +43,7 @@ _("Fat Tissue (Child)"):(-212,-72), _("Skin Tissue (Adult)"):(-718,-177), _("Skin Tissue (Child)"):(-766,-202), - _("Custom"):('', '') + _("Custom"):(0, 0) }) self.thresh_mri = TwoWaysDictionary({ @@ -61,23 +61,23 @@ _("Fat Tissue (Child)"):(812,952), _("Skin Tissue (Adult)"):(306,847), _("Skin Tissue (Child)"):(258,822), - _("Custom"):('', '') + _("Custom"):(0, 0) }) self.__bind_events() - + def __bind_events(self): Publisher.subscribe(self.UpdateThresholdModes, - 'Update threshold limits list') - - def UpdateThresholdModes(self, evt): - - thresh_min, thresh_max = evt.data + 'Update threshold limits list') + + def UpdateThresholdModes(self, threshold_range): + thresh_min, thresh_max = threshold_range presets_list = (self.thresh_ct, self.thresh_mri) for presets in presets_list: for key in presets: (t_min, t_max) = presets[key] + print(key, t_min, t_max) if (t_min is None) or (t_max is None): # setting custom preset t_min = thresh_min @@ -86,7 +86,7 @@ t_min = thresh_min if (t_max > thresh_max): t_max = thresh_max - + # This has happened in Analyze files # TODO: find a good solution for presets in Analyze files if (t_min > thresh_max): @@ -95,9 +95,9 @@ t_max = thresh_max presets[key] = (t_min, t_max) - - Publisher.sendMessage('Update threshold limits', (thresh_min, - thresh_max)) + + Publisher.sendMessage('Update threshold limits', + threshold_range=(thresh_min, thresh_max)) def SavePlist(self, filename): filename = "%s$%s" % (filename, 'presets.plist') @@ -181,7 +181,7 @@ preset = plistlib.readPlist(pfile) ncolours = len(preset['Blue']) colours = [] - for i in xrange(ncolours): + for i in range(ncolours): r = preset['Red'][i] g = preset['Green'][i] b = preset['Blue'][i] diff -Nru invesalius-3.1.1/invesalius/project.py invesalius-3.1.99991/invesalius/project.py --- invesalius-3.1.1/invesalius/project.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/project.py 2018-08-17 16:41:39.000000000 +0000 @@ -17,6 +17,8 @@ # detalhes. #-------------------------------------------------------------------------- +from six import with_metaclass + import datetime import glob import os @@ -33,7 +35,7 @@ import invesalius.constants as const import invesalius.data.polydata_utils as pu from invesalius.presets import Presets -from invesalius.utils import Singleton, debug, touch +from invesalius.utils import Singleton, debug, touch, decode import invesalius.version as version if sys.platform == 'win32': @@ -45,11 +47,9 @@ else: _has_win32api = False -class Project(object): - # Only one project will be initialized per time. Therefore, we use - # Singleton design pattern for implementing it - __metaclass__= Singleton - +# Only one project will be initialized per time. Therefore, we use +# Singleton design pattern for implementing it +class Project(with_metaclass(Singleton, object)): def __init__(self): # Patient/ acquistion information self.name = '' @@ -71,6 +71,8 @@ # TODO: Future ++ self.annotation_dict = {} + self.compress = False + # InVesalius related data # So we can find bugs and reproduce user-related problems self.invesalius_version = version.get_svn_revision() @@ -202,17 +204,20 @@ measures[str(m.index)] = item return measures - def SavePlistProject(self, dir_, filename): - dir_temp = tempfile.mkdtemp().decode(const.FS_ENCODE) + def SavePlistProject(self, dir_, filename, compress=False): + dir_temp = decode(tempfile.mkdtemp(), const.FS_ENCODE) + + self.compress = compress filename_tmp = os.path.join(dir_temp, u'matrix.dat') filelist = {} project = { # Format info - "format_version": 1, + "format_version": const.INVESALIUS_ACTUAL_FORMAT_VERSION, "invesalius_version": const.INVESALIUS_VERSION, "date": datetime.datetime.now().isoformat(), + "compress": self.compress, # case info "name": self.name, # patient's name @@ -267,7 +272,7 @@ # Compressing and generating the .inv3 file path = os.path.join(dir_,filename) - Compress(dir_temp, path, filelist) + Compress(dir_temp, path, filelist, compress) # Removing the temp folder. shutil.rmtree(dir_temp) @@ -295,6 +300,11 @@ main_plist = os.path.join(dirpath ,'main.plist') project = plistlib.readPlist(main_plist) + format_version = project["format_version"] + if format_version > const.INVESALIUS_ACTUAL_FORMAT_VERSION: + from invesalius.gui.dialogs import ImportOldFormatInvFile + ImportOldFormatInvFile() + # case info self.name = project["name"] self.modality = project["modality"] @@ -304,6 +314,8 @@ self.threshold_range = project["scalar_range"] self.spacing = project["spacing"] + self.compress = project.get("compress", True) + # Opening the matrix containing the slices filepath = os.path.join(dirpath, project["matrix"]["filename"]) self.matrix_filename = filepath @@ -337,7 +349,7 @@ measure.Load(measurements[index]) self.measurement_dict[int(index)] = measure -def Compress(folder, filename, filelist): +def Compress(folder, filename, filelist, compress=False): tmpdir, tmpdir_ = os.path.split(folder) current_dir = os.path.abspath(".") temp_inv3 = tempfile.mktemp() @@ -345,10 +357,13 @@ touch(temp_inv3) temp_inv3 = win32api.GetShortPathName(temp_inv3) - temp_inv3 = temp_inv3.decode(const.FS_ENCODE) + temp_inv3 = decode(temp_inv3, const.FS_ENCODE) #os.chdir(tmpdir) #file_list = glob.glob(os.path.join(tmpdir_,"*")) - tar = tarfile.open(temp_inv3, "w:gz") + if compress: + tar = tarfile.open(temp_inv3, "w:gz") + else: + tar = tarfile.open(temp_inv3, "w") for name in filelist: tar.add(name, arcname=os.path.join(tmpdir_, filelist[name])) tar.close() @@ -359,16 +374,16 @@ def Extract(filename, folder): if _has_win32api: folder = win32api.GetShortPathName(folder) - folder = folder.decode(const.FS_ENCODE) + folder = decode(folder, const.FS_ENCODE) - tar = tarfile.open(filename, "r:gz") - idir = os.path.split(tar.getnames()[0])[0].decode('utf8') + tar = tarfile.open(filename, "r") + idir = decode(os.path.split(tar.getnames()[0])[0], 'utf8') os.mkdir(os.path.join(folder, idir)) filelist = [] for t in tar.getmembers(): fsrc = tar.extractfile(t) - fname = os.path.join(folder, t.name.decode('utf-8')) - fdst = file(fname, 'wb') + fname = os.path.join(folder, decode(t.name, 'utf-8')) + fdst = open(fname, 'wb') shutil.copyfileobj(fsrc, fdst) filelist.append(fname) fsrc.close() diff -Nru invesalius-3.1.1/invesalius/reader/bitmap_reader.py invesalius-3.1.99991/invesalius/reader/bitmap_reader.py --- invesalius-3.1.1/invesalius/reader/bitmap_reader.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/reader/bitmap_reader.py 2018-08-17 16:41:39.000000000 +0000 @@ -17,7 +17,6 @@ # detalhes. #-------------------------------------------------------------------------- import os -import Queue import threading import tempfile import sys @@ -100,12 +99,12 @@ def RemoveFileByPath(self, path): for d in self.data: - if path.encode('utf-8') in d: + if path.encode(const.FS_ENCODE) in d: self.data.remove(d) def GetIndexByPath(self, path): for i, v in enumerate(self.data): - if path.encode('utf-8') in v: + if path.encode(const.FS_ENCODE) in v: return i class BitmapFiles: @@ -136,7 +135,7 @@ def __init__(self, bmp_file, filepath): self.bmp_file = bmp_file - self.filepath = filepath + self.filepath = utils.decode(filepath, const.FS_ENCODE) self.run() @@ -223,7 +222,7 @@ if recursive: for dirpath, dirnames, filenames in os.walk(directory): for name in filenames: - filepath = os.path.join(dirpath, name) + filepath = os.path.join(dirpath, name).encode(const.FS_ENCODE) counter += 1 if gui: yield (counter,nfiles) @@ -231,7 +230,7 @@ else: dirpath, dirnames, filenames = os.walk(directory) for name in filenames: - filepath = str(os.path.join(dirpath, name)) + filepath = str(os.path.join(dirpath, name)).encode(const.FS_ENCODE) counter += 1 if gui: yield (counter,nfiles) @@ -243,12 +242,12 @@ def __init__(self): Publisher.subscribe(self.CancelLoad, "Cancel bitmap load") - def CancelLoad(self, evt_pubsub): + def CancelLoad(self): self.running = False self.stoped = True def SetWindowEvent(self, frame): - self.frame = frame + self.frame = frame def SetDirectoryPath(self, path,recursive=True): self.running = True @@ -256,10 +255,10 @@ self.GetBitmaps(path,recursive) def UpdateLoadFileProgress(self,cont_progress): - Publisher.sendMessage("Update bitmap load", cont_progress) + Publisher.sendMessage("Update bitmap load", data=cont_progress) def EndLoadFile(self, bitmap_list): - Publisher.sendMessage("End bitmap load", bitmap_list) + Publisher.sendMessage("End bitmap load", data=bitmap_list) def GetBitmaps(self, path, recursive): @@ -323,10 +322,8 @@ else: return False - print ">>>> bmp reader", type(filepath) - reader.AddObserver("ErrorEvent", VtkErrorToPy) - reader.SetFileName(filepath.encode(const.FS_ENCODE)) + reader.SetFileName(filepath) reader.Update() if no_error: @@ -357,10 +354,12 @@ filepath = win32api.GetShortPathName(filepath) if t == False: - measures_info = GetPixelSpacingFromInfoFile(filepath) - + try: + measures_info = GetPixelSpacingFromInfoFile(filepath) + except UnicodeDecodeError: + measures_info = False if measures_info: - Publisher.sendMessage('Set bitmap spacing', measures_info) + Publisher.sendMessage('Set bitmap spacing', spacing=measures_info) return False @@ -379,6 +378,9 @@ def GetPixelSpacingFromInfoFile(filepath): + filepath = utils.decode(filepath, const.FS_ENCODE) + if filepath.endswith('.DS_Store'): + return False fi = open(filepath, 'r') lines = fi.readlines() measure_scale = 'mm' diff -Nru invesalius-3.1.1/invesalius/reader/dicom_grouper.py invesalius-3.1.99991/invesalius/reader/dicom_grouper.py --- invesalius-3.1.1/invesalius/reader/dicom_grouper.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/reader/dicom_grouper.py 2018-08-17 16:41:39.000000000 +0000 @@ -94,22 +94,22 @@ if not self.dicom: self.dicom = dicom - pos = tuple(dicom.image.position) - + pos = tuple(dicom.image.position) + #Case to test: \other\higroma - #condition created, if any dicom with the same + #condition created, if any dicom with the same #position, but 3D, leaving the same series. if not "DERIVED" in dicom.image.type: #if any dicom with the same position if pos not in self.slices_dict.keys(): self.slices_dict[pos] = dicom - self.nslices += 1 + self.nslices += dicom.image.number_of_frames return True else: return False else: self.slices_dict[dicom.image.number] = dicom - self.nslices += 1 + self.nslices += dicom.image.number_of_frames return True def GetList(self): @@ -124,7 +124,7 @@ # (interpolated) if _has_win32api: - filelist = [win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE) + filelist = [win32api.GetShortPathName(dicom.image.file) for dicom in self.slices_dict.values()] else: @@ -132,16 +132,19 @@ self.slices_dict.values()] # Sort slices using GDCM - if (self.dicom.image.orientation_label <> "CORONAL"): + if (self.dicom.image.orientation_label != "CORONAL"): #Organize reversed image sorter = gdcm.IPPSorter() sorter.SetComputeZSpacing(True) sorter.SetZSpacingTolerance(1e-10) - sorter.Sort(filelist) + try: + sorter.Sort([utils.encode(i, const.FS_ENCODE) for i in filelist]) + except TypeError: + sorter.Sort(filelist) filelist = sorter.GetFilenames() # for breast-CT of koning manufacturing (KBCT) - if self.slices_dict.values()[0].parser.GetManufacturerName() == "Koning": + if list(self.slices_dict.values())[0].parser.GetManufacturerName() == "Koning": filelist.sort() return filelist @@ -149,7 +152,7 @@ def GetHandSortedList(self): # This will be used to fix problem 1, after merging # single DicomGroups of same study_id and orientation - list_ = self.slices_dict.values() + list_ = list(self.slices_dict.values()) dicom = list_[0] axis = ORIENT_MAP[dicom.image.orientation_label] #list_ = sorted(list_, key = lambda dicom:dicom.image.position[axis]) @@ -173,7 +176,7 @@ def GetDicomSample(self): size = len(self.slices_dict) - dicom = self.GetHandSortedList()[size/2] + dicom = self.GetHandSortedList()[size//2] return dicom class PatientGroup: @@ -306,7 +309,7 @@ # 3rd STEP: CHECK DIFFERENCES axis = ORIENT_MAP[group_key[0]] # based on orientation - for index in xrange(len(sorted_list)-1): + for index in range(len(sorted_list)-1): current = sorted_list[index] next = sorted_list[index+1] diff -Nru invesalius-3.1.1/invesalius/reader/dicom.py invesalius-3.1.99991/invesalius/reader/dicom.py --- invesalius-3.1.1/invesalius/reader/dicom.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/reader/dicom.py 2018-08-17 16:41:39.000000000 +0000 @@ -1154,8 +1154,20 @@ if (data): return int(data) return "" - - + + def GetNumberOfFrames(self): + """ + Number of frames in a multi-frame image. + + DICOM standard tag (0x0028, 0x0008) was used. + """ + try: + data = self.data_image[str(0x028)][str(0x0008)] + except KeyError: + return 1 + return int(data) + + def GetPatientBirthDate(self): """ Return string containing the patient's birth date using the @@ -1228,7 +1240,7 @@ encoding = self.GetEncoding() try: # Returns a unicode decoded in the own dicom encoding - return name.decode(encoding, 'replace') + return utils.decode(name, encoding, 'replace') except(UnicodeEncodeError): return name @@ -1273,7 +1285,7 @@ try: # Returns a unicode decoded in the own dicom encoding - return name.decode(encoding, 'replace') + return utils.decode(name, encoding, 'replace') except(UnicodeEncodeError): return name @@ -1296,7 +1308,7 @@ encoding = self.GetEncoding() # Returns a unicode decoded in the own dicom encoding try: - return data.decode(encoding, 'replace') + return utils.decode(data, encoding, 'replace') except(UnicodeEncodeError): return data return "" @@ -1498,11 +1510,11 @@ try: data = self.data_image[str(0x0020)][str(0x0013)] except(KeyError): - return "" + return 0 if (data): return int(data) - return "" + return 0 def GetStudyDescription(self): """ @@ -1514,10 +1526,8 @@ try: data = self.data_image[str(0x0008)][str(0x1030)] if (data): - if isinstance(data, unicode): - return data encoding = self.GetEncoding() - return data.decode(encoding, 'replace') + return utils.decode(data, encoding, 'replace') except(KeyError): return "" @@ -1831,31 +1841,31 @@ fail_count = 0 total = 48 - for i in xrange(1,total+1): + for i in range(1,total+1): filename = "..//data//"+str(i)+".dcm" parser = Parser() if parser.SetFileName(filename): - print "p:", parser.GetPatientName() - print "l:", parser.GetImageLocation() - print "o:", parser.GetImagePatientOrientation() - print "t:", parser.GetImageThickness() - print "s:", parser.GetPixelSpacing() - print "x:", parser.GetDimensionX() - print "y:", parser.GetDimensionY() - print "z:", parser.GetDimensionZ() + print("p:", parser.GetPatientName()) + print("l:", parser.GetImageLocation()) + print("o:", parser.GetImagePatientOrientation()) + print("t:", parser.GetImageThickness()) + print("s:", parser.GetPixelSpacing()) + print("x:", parser.GetDimensionX()) + print("y:", parser.GetDimensionY()) + print("z:", parser.GetDimensionZ()) else: - print "--------------------------------------------------" + print("--------------------------------------------------") total-=1 fail_count+=1 - print "\nREPORT:" - print "failed: ", fail_count - print "sucess: ", total + print("\nREPORT:") + print("failed: ", fail_count) + print("sucess: ", total) # Example of how to use auxiliary functions total = 38 - for i in xrange(1,total+1): + for i in range(1,total+1): if (i==8) or (i==9) or (i==13): pass else: @@ -1954,6 +1964,8 @@ self.bits_allocad = parser._GetBitsAllocated() self.thumbnail_path = parser.thumbnail_path + self.number_of_frames = parser.GetNumberOfFrames() + if (parser.GetImageThickness()): try: spacing.append(parser.GetImageThickness()) diff -Nru invesalius-3.1.1/invesalius/reader/dicom_reader.py invesalius-3.1.99991/invesalius/reader/dicom_reader.py --- invesalius-3.1.1/invesalius/reader/dicom_reader.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/reader/dicom_reader.py 2018-08-17 16:41:39.000000000 +0000 @@ -17,7 +17,6 @@ # detalhes. #-------------------------------------------------------------------------- import os -import Queue import threading import tempfile import sys @@ -36,6 +35,8 @@ import glob import invesalius.utils as utils +from invesalius.data import imagedata_utils + import plistlib if sys.platform == 'win32': @@ -77,7 +78,7 @@ def SortFiles(filelist, dicom): # Sort slices # FIXME: Coronal Crash. necessary verify - if (dicom.image.orientation_label <> "CORONAL"): + if (dicom.image.orientation_label != "CORONAL"): ##Organize reversed image sorter = gdcm.IPPSorter() sorter.SetComputeZSpacing(True) @@ -94,35 +95,37 @@ dict_file = {} class LoadDicom: - def __init__(self, grouper, filepath): self.grouper = grouper - self.filepath = filepath - + self.filepath = utils.decode(filepath, const.FS_ENCODE) self.run() - + def run(self): grouper = self.grouper reader = gdcm.ImageReader() if _has_win32api: - reader.SetFileName(win32api.GetShortPathName(self.filepath).encode(const.FS_ENCODE)) + try: + reader.SetFileName(utils.encode(win32api.GetShortPathName(self.filepath), + const.FS_ENCODE)) + except TypeError: + reader.SetFileName(win32api.GetShortPathName(self.filepath)) else: - reader.SetFileName(self.filepath) - + try: + reader.SetFileName(utils.encode(self.filepath, const.FS_ENCODE)) + except TypeError: + reader.SetFileName(self.filepath) if (reader.Read()): file = reader.GetFile() - # Retrieve data set dataSet = file.GetDataSet() - # Retrieve header header = file.GetHeader() stf = gdcm.StringFilter() + stf.SetFile(file) field_dict = {} data_dict = {} - tag = gdcm.Tag(0x0008, 0x0005) ds = reader.GetFile().GetDataSet() if ds.FindDataElement(tag): @@ -138,105 +141,81 @@ else: encoding = "ISO_IR 100" - # Iterate through the Header iterator = header.GetDES().begin() while (not iterator.equal(header.GetDES().end())): dataElement = iterator.next() - stf.SetFile(file) - tag = dataElement.GetTag() - data = stf.ToStringPair(tag) - stag = tag.PrintAsPipeSeparatedString() - - group = str(tag.GetGroup()) - field = str(tag.GetElement()) - - tag_labels[stag] = data[0] - - if not group in data_dict.keys(): - data_dict[group] = {} - - if not(utils.VerifyInvalidPListCharacter(data[1])): - data_dict[group][field] = data[1].decode(encoding) - else: - data_dict[group][field] = "Invalid Character" + if not dataElement.IsUndefinedLength(): + tag = dataElement.GetTag() + data = stf.ToStringPair(tag) + stag = tag.PrintAsPipeSeparatedString() + + group = str(tag.GetGroup()) + field = str(tag.GetElement()) + + tag_labels[stag] = data[0] + + if not group in data_dict.keys(): + data_dict[group] = {} + + if not(utils.VerifyInvalidPListCharacter(data[1])): + data_dict[group][field] = utils.decode(data[1], encoding) + else: + data_dict[group][field] = "Invalid Character" - # Iterate through the Data set iterator = dataSet.GetDES().begin() while (not iterator.equal(dataSet.GetDES().end())): dataElement = iterator.next() - - stf.SetFile(file) - tag = dataElement.GetTag() - data = stf.ToStringPair(tag) - stag = tag.PrintAsPipeSeparatedString() - - group = str(tag.GetGroup()) - field = str(tag.GetElement()) - - tag_labels[stag] = data[0] - - if not group in data_dict.keys(): - data_dict[group] = {} - - if not(utils.VerifyInvalidPListCharacter(data[1])): - data_dict[group][field] = data[1].decode(encoding, 'replace') - else: - data_dict[group][field] = "Invalid Character" - + if not dataElement.IsUndefinedLength(): + tag = dataElement.GetTag() + # if (tag.GetGroup() == 0x0009 and tag.GetElement() == 0x10e3) \ + # or (tag.GetGroup() == 0x0043 and tag.GetElement() == 0x1027): + # continue + data = stf.ToStringPair(tag) + stag = tag.PrintAsPipeSeparatedString() + + group = str(tag.GetGroup()) + field = str(tag.GetElement()) + + tag_labels[stag] = data[0] + + if not group in data_dict.keys(): + data_dict[group] = {} + + if not(utils.VerifyInvalidPListCharacter(data[1])): + data_dict[group][field] = utils.decode(data[1], encoding, 'replace') + else: + data_dict[group][field] = "Invalid Character" # -------------- To Create DICOM Thumbnail ----------- - rvtk = vtkgdcm.vtkGDCMImageReader() - if _has_win32api: - print 'dicom', win32api.GetShortPathName(self.filepath) - rvtk.SetFileName(win32api.GetShortPathName(self.filepath).encode(const.FS_ENCODE)) - else: - rvtk.SetFileName(self.filepath) - rvtk.Update() - + try: data = data_dict[str(0x028)][str(0x1050)] level = [float(value) for value in data.split('\\')][0] data = data_dict[str(0x028)][str(0x1051)] window = [float(value) for value in data.split('\\')][0] except(KeyError, ValueError): - level = 300.0 - window = 2000.0 - - colorer = vtk.vtkImageMapToWindowLevelColors() - colorer.SetInputConnection(rvtk.GetOutputPort()) - colorer.SetWindow(float(window)) - colorer.SetLevel(float(level)) - colorer.SetOutputFormatToRGB() - colorer.Update() - - resample = vtk.vtkImageResample() - resample.SetInputConnection(colorer.GetOutputPort()) - resample.SetAxisMagnificationFactor ( 0, 0.25 ) - resample.SetAxisMagnificationFactor ( 1, 0.25 ) - resample.SetAxisMagnificationFactor ( 2, 1 ) - resample.Update() - - thumbnail_path = tempfile.mktemp() - - write_png = vtk.vtkPNGWriter() - write_png.SetInputConnection(resample.GetOutputPort()) - write_png.SetFileName(thumbnail_path) - write_png.Write() - + level = None + window = None + + if _has_win32api: + thumbnail_path = imagedata_utils.create_dicom_thumbnails(win32api.GetShortPathName(self.filepath), window, level) + else: + thumbnail_path = imagedata_utils.create_dicom_thumbnails(self.filepath, window, level) + #------ Verify the orientation -------------------------------- img = reader.GetImage() direc_cosines = img.GetDirectionCosines() orientation = gdcm.Orientation() try: - type = orientation.GetType(tuple(direc_cosines)) + _type = orientation.GetType(tuple(direc_cosines)) except TypeError: - type = orientation.GetType(direc_cosines) - label = orientation.GetLabel(type) + _type = orientation.GetType(direc_cosines) + label = orientation.GetLabel(_type) # ---------- Refactory -------------------------------------- @@ -327,14 +306,14 @@ def GetDicomGroups(directory, recursive=True): - return yGetDicomGroups(directory, recursive, gui=False).next() + return next(yGetDicomGroups(directory, recursive, gui=False)) class ProgressDicomReader: def __init__(self): Publisher.subscribe(self.CancelLoad, "Cancel DICOM load") - def CancelLoad(self, evt_pubsub): + def CancelLoad(self): self.running = False self.stoped = True @@ -347,15 +326,15 @@ self.GetDicomGroups(path,recursive) def UpdateLoadFileProgress(self,cont_progress): - Publisher.sendMessage("Update dicom load", cont_progress) + Publisher.sendMessage("Update dicom load", data=cont_progress) def EndLoadFile(self, patient_list): - Publisher.sendMessage("End dicom load", patient_list) + Publisher.sendMessage("End dicom load", patient_series=patient_list) def GetDicomGroups(self, path, recursive): if not const.VTK_WARNING: - log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt').encode(const.FS_ENCODE) + log_path = utils.encode(os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt'), const.FS_ENCODE) fow = vtk.vtkFileOutputWindow() fow.SetFileName(log_path) ow = vtk.vtkOutputWindow() @@ -363,12 +342,14 @@ y = yGetDicomGroups(path, recursive) for value_progress in y: + print(">>>>", value_progress) if not self.running: break if isinstance(value_progress, tuple): self.UpdateLoadFileProgress(value_progress) else: self.EndLoadFile(value_progress) + self.UpdateLoadFileProgress(None) #Is necessary in the case user cancel #the load, ensure that dicomdialog is closed diff -Nru invesalius-3.1.1/invesalius/session.py invesalius-3.1.99991/invesalius/session.py --- invesalius-3.1.1/invesalius/session.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/session.py 2018-08-17 16:41:39.000000000 +0000 @@ -17,6 +17,8 @@ # detalhes. #-------------------------------------------------------------------------- +from six import with_metaclass + try: import configparser as ConfigParser except(ImportError): @@ -33,7 +35,7 @@ from wx.lib.pubsub import pub as Publisher import wx -from invesalius.utils import Singleton, debug +from invesalius.utils import Singleton, debug, decode from random import randint FS_ENCODE = sys.getfilesystemencoding() @@ -43,9 +45,9 @@ try: USER_DIR = expand_user() except: - USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + USER_DIR = decode(os.path.expanduser('~'), FS_ENCODE) else: - USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + USER_DIR = decode(os.path.expanduser('~'), FS_ENCODE) USER_INV_DIR = os.path.join(USER_DIR, u'.invesalius') USER_PRESET_DIR = os.path.join(USER_INV_DIR, u'presets') @@ -55,10 +57,9 @@ SESSION_ENCODING = 'utf8' -class Session(object): - # Only one session will be initialized per time. Therefore, we use - # Singleton design pattern for implementing it - __metaclass__= Singleton +# Only one session will be initialized per time. Therefore, we use +# Singleton design pattern for implementing it +class Session(with_metaclass(Singleton, object)): def __init__(self): self.temp_item = False @@ -201,7 +202,10 @@ '.invesalius', 'config.cfg') configfile = codecs.open(path, 'wb', SESSION_ENCODING) - config.write(configfile) + try: + config.write(configfile) + except UnicodeDecodeError: + pass configfile.close() def __add_to_list(self, item): @@ -218,7 +222,7 @@ # Remove oldest projects from list if len(l)>const.PROJ_MAX: - for i in xrange(len(l)-const.PROJ_MAX): + for i in range(len(l)-const.PROJ_MAX): l.pop() def GetLanguage(self): diff -Nru invesalius-3.1.1/invesalius/style.py invesalius-3.1.99991/invesalius/style.py --- invesalius-3.1.1/invesalius/style.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/style.py 2018-08-17 16:41:39.000000000 +0000 @@ -64,7 +64,7 @@ #---------------------- -import constants as const +import invesalius.constants as const class StyleStateManager(object): # don't need to be singleton, only needs to be instantiated inside diff -Nru invesalius-3.1.1/invesalius/utils.py invesalius-3.1.99991/invesalius/utils.py --- invesalius-3.1.1/invesalius/utils.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/invesalius/utils.py 2018-08-17 16:41:39.000000000 +0000 @@ -76,6 +76,7 @@ from invesalius.session import Session session = Session() #if session.debug: + print(error_str) def next_copy_name(original_name, names_list): """ @@ -238,13 +239,13 @@ ram_total = psutil.phymem_usage().total swap_free = psutil.virtmem_usage().free except: - print "Exception! psutil version < 0.3 (not recommended)" + print("Exception! psutil version < 0.3 (not recommended)") ram_total = psutil.TOTAL_PHYMEM # this is for psutil < 0.3 ram_free = 0.8 * psutil.TOTAL_PHYMEM swap_free = psutil.avail_virtmem() - print "RAM_FREE=", ram_free - print "RAM_TOTAL=", ram_total + print("RAM_FREE=", ram_free) + print( "RAM_TOTAL=", ram_total) if (sys.platform == 'win32'): if (platform.architecture()[0] == '32bit'): @@ -253,7 +254,7 @@ if ram_total>1400000000: ram_total=1400000000 - if (sys.platform == 'linux2'): + if sys.platform.startswith('linux'): if (platform.architecture()[0] == '32bit'): if ram_free>3500000000: ram_free=3500000000 @@ -312,7 +313,7 @@ return (x/porcent, y/porcent) - elif(sys.platform == 'linux2'): + elif sys.platform.startswith('linux'): if (platform.architecture()[0] == '32bit'): # 839000000 = 800 MB @@ -370,8 +371,14 @@ def UpdateCheck(): - import urllib - import urllib2 + try: + from urllib.parse import urlencode + from urllib.request import urlopen, Request + from urllib.error import HTTPError + except ImportError: + from urllib import urlencode + from urllib2 import urlopen, Request, HTTPError + import wx import invesalius.session as ses def _show_update_info(): @@ -384,7 +391,7 @@ msgdlg.Show() #msgdlg.Destroy() - print "Checking updates..." + print("Checking updates...") # Check if there is a language set #import invesalius.i18n as i18n import invesalius.session as ses @@ -410,14 +417,14 @@ 'architecture' : platform.architecture()[0], 'language' : lang, 'random_id' : random_id } - data = urllib.urlencode(data) - req = urllib2.Request(url, data, headers) + data = urlencode(data).encode('utf8') + req = Request(url, data, headers) try: - response = urllib2.urlopen(req, timeout=10) + response = urlopen(req, timeout=10) except: return - last = response.readline().rstrip() - url = response.readline().rstrip() + last = response.readline().rstrip().decode('utf8') + url = response.readline().rstrip().decode('utf8') try: last_ver = LooseVersion(last) @@ -426,14 +433,14 @@ return if last_ver > actual_ver: - print " ...New update found!!! -> version:", last #, ", url=",url + print(" ...New update found!!! -> version:", last) #, ", url=",url wx.CallAfter(wx.CallLater, 1000, _show_update_info) def vtkarray_to_numpy(m): nm = np.zeros((4, 4)) - for i in xrange(4): - for j in xrange(4): + for i in range(4): + for j in range(4): nm[i, j] = m.GetElement(i, j) return nm @@ -441,3 +448,17 @@ def touch(fname): with open(fname, 'a'): pass + + +def decode(text, encoding, *args): + try: + return text.decode(encoding, *args) + except AttributeError: + return text + + +def encode(text, encoding, *args): + try: + return text.encode(encoding, *args) + except AttributeError: + return text Binary files /tmp/tmpzIbyXT/Keg7HLuK6t/invesalius-3.1.1/navigation/objects/aim.stl and /tmp/tmpzIbyXT/nBkSI9neJD/invesalius-3.1.99991/navigation/objects/aim.stl differ Binary files /tmp/tmpzIbyXT/Keg7HLuK6t/invesalius-3.1.1/navigation/objects/magstim_fig8_coil_no_handle.stl and /tmp/tmpzIbyXT/nBkSI9neJD/invesalius-3.1.99991/navigation/objects/magstim_fig8_coil_no_handle.stl differ Binary files /tmp/tmpzIbyXT/Keg7HLuK6t/invesalius-3.1.1/navigation/objects/magstim_fig8_coil.stl and /tmp/tmpzIbyXT/nBkSI9neJD/invesalius-3.1.99991/navigation/objects/magstim_fig8_coil.stl differ diff -Nru invesalius-3.1.1/setup.py invesalius-3.1.99991/setup.py --- invesalius-3.1.1/setup.py 2017-08-10 13:59:24.000000000 +0000 +++ invesalius-3.1.99991/setup.py 2018-08-17 16:41:39.000000000 +0000 @@ -8,7 +8,7 @@ import numpy -if sys.platform == 'linux2': +if sys.platform.startswith('linux'): setup( cmdclass = {'build_ext': build_ext}, ext_modules = cythonize([ Extension("invesalius.data.mips", ["invesalius/data/mips.pyx"],