From ab04cc4e116ce5557d38105b26f8b5b776ebdfa9 Mon Sep 17 00:00:00 2001 From: Thiago Franco de Moraes Date: Tue, 28 Mar 2017 14:01:01 -0300 Subject: [PATCH] Import mesh (#68) --- invesalius/data/surface.py | 32 ++++++++++++++++++++++++++++++++ invesalius/gui/data_notebook.py | 32 +++++++++++++++++++++++++++++++- invesalius/gui/dialogs.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/invesalius/data/surface.py b/invesalius/data/surface.py index 72f8c60..8845085 100644 --- a/invesalius/data/surface.py +++ b/invesalius/data/surface.py @@ -150,6 +150,8 @@ class SurfaceManager(): Publisher.subscribe(self.OnRemove,"Remove surfaces") Publisher.subscribe(self.UpdateSurfaceInterpolation, 'Update Surface Interpolation') + Publisher.subscribe(self.OnImportSurfaceFile, 'Import surface file') + def OnDuplicate(self, pubsub_evt): selected_items = pubsub_evt.data proj = prj.Project() @@ -241,6 +243,36 @@ class SurfaceManager(): new_index = self.CreateSurfaceFromPolydata(new_polydata) Publisher.sendMessage('Show single surface', (new_index, True)) + def OnImportSurfaceFile(self, pubsub_evt): + """ + Creates a new surface from a surface file (STL, PLY, OBJ or VTP) + """ + filename = pubsub_evt.data + self.CreateSurfaceFromFile(filename) + + def CreateSurfaceFromFile(self, 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 + + reader.SetFileName(filename) + reader.Update() + polydata = reader.GetOutput() + + if polydata.GetNumberOfPoints() == 0: + wx.MessageBox(_("InVesalius was not able to import this surface"), _("Import surface error")) + else: + name = os.path.splitext(os.path.split(filename)[-1])[0] + self.CreateSurfaceFromPolydata(polydata, name=name) + def CreateSurfaceFromPolydata(self, polydata, overwrite=False, name=None, colour=None, transparency=None, volume=None, area=None): diff --git a/invesalius/gui/data_notebook.py b/invesalius/gui/data_notebook.py index e0bab5b..d6a28e3 100644 --- a/invesalius/gui/data_notebook.py +++ b/invesalius/gui/data_notebook.py @@ -40,7 +40,7 @@ import invesalius.gui.widgets.listctrl as listmix import invesalius.utils as ul -BTN_NEW, BTN_REMOVE, BTN_DUPLICATE = [wx.NewId() for i in xrange(3)] +BTN_NEW, BTN_REMOVE, BTN_DUPLICATE, BTN_OPEN = [wx.NewId() for i in xrange(4)] TYPE = {const.LINEAR: _(u"Linear"), const.ANGULAR: _(u"Angular"), @@ -162,15 +162,20 @@ class MeasureButtonControlPanel(wx.Panel): BMP_NEW, style=button_style, size = wx.Size(24, 20)) + button_new.SetToolTipString(_("Create a new measure")) self.button_new = button_new + button_remove = pbtn.PlateButton(self, BTN_REMOVE, "", BMP_REMOVE, style=button_style, size = wx.Size(24, 20)) + button_remove.SetToolTipString(_("Remove measure")) + button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "", BMP_DUPLICATE, style=button_style, size = wx.Size(24, 20)) + button_duplicate.SetToolTipString(_("Duplicate measure")) button_duplicate.Disable() # Add all controls to gui @@ -275,14 +280,19 @@ class ButtonControlPanel(wx.Panel): BMP_NEW, style=button_style, size = wx.Size(24, 20)) + button_new.SetToolTipString(_("Create a new mask")) + button_remove = pbtn.PlateButton(self, BTN_REMOVE, "", BMP_REMOVE, style=button_style, size = wx.Size(24, 20)) + button_remove.SetToolTipString(_("Remove mask")) + button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "", BMP_DUPLICATE, style=button_style, size = wx.Size(24, 20)) + button_duplicate.SetToolTipString(_("Duplicate mask")) # Add all controls to gui sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -593,6 +603,7 @@ class SurfaceButtonControlPanel(wx.Panel): wx.BITMAP_TYPE_PNG) BMP_DUPLICATE = wx.Bitmap(os.path.join(const.ICON_DIR, "data_duplicate.png"), wx.BITMAP_TYPE_PNG) + BMP_OPEN = wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, wx.ART_BUTTON, (18,18)) # Plate buttons based on previous bitmaps button_style = pbtn.PB_STYLE_SQUARE | pbtn.PB_STYLE_DEFAULT @@ -600,20 +611,32 @@ class SurfaceButtonControlPanel(wx.Panel): BMP_NEW, style=button_style, size = wx.Size(24, 20)) + button_new.SetToolTipString(_("Create a new surface")) + button_remove = pbtn.PlateButton(self, BTN_REMOVE, "", BMP_REMOVE, style=button_style, size = wx.Size(24, 20)) + button_remove.SetToolTipString(_("Remove surface")) + button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "", BMP_DUPLICATE, style=button_style, size = wx.Size(24, 20)) + button_duplicate.SetToolTipString(_("Duplicate surface")) + + button_open = pbtn.PlateButton(self, BTN_OPEN, "", + BMP_OPEN, + style=button_style, + size = wx.Size(24, 20)) + button_open.SetToolTipString(_("Import a surface file into InVesalius")) # Add all controls to gui sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(button_new, 0, wx.GROW|wx.EXPAND|wx.LEFT) sizer.Add(button_remove, 0, wx.GROW|wx.EXPAND) sizer.Add(button_duplicate, 0, wx.GROW|wx.EXPAND) + sizer.Add(button_open, 0, wx.GROW|wx.EXPAND) self.SetSizer(sizer) self.Fit() @@ -628,6 +651,8 @@ class SurfaceButtonControlPanel(wx.Panel): self.OnRemove() elif id == BTN_DUPLICATE: self.OnDuplicate() + elif id == BTN_OPEN: + self.OnOpenMesh() def OnNew(self): sl = slice_.Slice() @@ -659,6 +684,11 @@ class SurfaceButtonControlPanel(wx.Panel): else: dlg.SurfaceSelectionRequiredForDuplication() + def OnOpenMesh(self): + filename = dlg.ShowImportMeshFilesDialog() + if filename: + Publisher.sendMessage('Import surface file', filename) + class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): diff --git a/invesalius/gui/dialogs.py b/invesalius/gui/dialogs.py index ca99461..3017e40 100644 --- a/invesalius/gui/dialogs.py +++ b/invesalius/gui/dialogs.py @@ -223,6 +223,12 @@ WILDCARD_NIFTI = "NIfTI 1 (*.nii)|*.nii|" \ WILDCARD_PARREC = "PAR/REC (*.par)|*.par|" \ "All files (*.*)|*.*" +WILDCARD_MESH_FILES = "STL File format (*.stl)|*.stl|" \ + "Standard Polygon File Format (*.ply)|*.ply|" \ + "Alias Wavefront Object (*.obj)|*.obj|" \ + "VTK Polydata File Format (*.vtp)|*.vtp|" \ + "All files (*.*)|*.*" + def ShowOpenProjectDialog(): # Default system path @@ -376,6 +382,39 @@ def ShowImportOtherFilesDialog(id_type): return filename +def ShowImportMeshFilesDialog(): + # Default system path + current_dir = os.path.abspath(".") + dlg = wx.FileDialog(None, message=_("Import surface file"), + wildcard=WILDCARD_MESH_FILES, + style=wx.FD_OPEN | wx.FD_CHANGE_DIR) + + # stl filter is default + dlg.SetFilterIndex(0) + + # Show the dialog and retrieve the user response. If it is the OK response, + # process the data. + filename = None + try: + if dlg.ShowModal() == wx.ID_OK: + # GetPath returns in unicode, if a path has non-ascii characters a + # UnicodeEncodeError is raised. To avoid this, path is encoded in utf-8 + if sys.platform == "win32": + filename = dlg.GetPath() + else: + filename = dlg.GetPath().encode('utf-8') + + except(wx._core.PyAssertionError): # TODO: error win64 + if (dlg.GetPath()): + filename = 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 filename + + def ShowSaveAsProjectDialog(default_filename=None): current_dir = os.path.abspath(".") dlg = wx.FileDialog(None, -- libgit2 0.21.2