From 80acdf4a24c7a0a113a8d41c3f83f8b40b2f7c71 Mon Sep 17 00:00:00 2001
From: Joshua Steer <Joshua.Steer@soton.ac.uk>
Date: Thu, 9 Aug 2018 17:25:10 +0100
Subject: [PATCH] Modifications to the AmpScan GUI to better handle AmpObjects

---
 AmpScan/align.py   |  46 ++++++++-
 AmpScan/ampVis.py  |   3 +-
 GUIs/AmpScanGUI.py | 246 ++++++++++++++++++++++++++++++++++++---------
 3 files changed, 240 insertions(+), 55 deletions(-)

diff --git a/AmpScan/align.py b/AmpScan/align.py
index 3f2019e..bc947bb 100644
--- a/AmpScan/align.py
+++ b/AmpScan/align.py
@@ -6,10 +6,11 @@ Created on Thu Sep 14 13:15:30 2017
 """
 
 import numpy as np
+import vtk
 from scipy import spatial
 from scipy.optimize import minimize
 from .core import AmpObject
-
+from .ampVis import vtkRenWin
 
 class align(object):
     r"""
@@ -96,16 +97,19 @@ class align(object):
     def __init__(self, moving, static, method = 'P2P'):
         self.m = moving
         self.s = static
-        self.icp()
-        amp = AmpObject()
+        if method is not None:
+            getattr(self, method)()
+            
+        #self.icp()
+        #amp = AmpObject()
         
-    def icp():
+    def icp(self):
         """
         Automated alignment function between two meshes
         
         """
 
-        tTree = spatial.cKDTree(self.baseline.vert)
+        tTree = spatial.cKDTree(self.s.vert)
         rot = np.array([0,0,0], dtype=float)
         res = minimize(self.calcDistError, rot, method='BFGS',
                        options={'gtol':1e-6, 'disp':True})
@@ -131,5 +135,37 @@ class align(object):
         dist = tTree.query(self.vert, 10)[0]
         dist = dist.min(axis=1)
         return dist.sum()
+    
+    def display(self):
+        r"""
+        Function to display the two aligned meshes in 
+        """
+        if not hasattr(self.s, 'actor'):
+            self.s.addActor()
+        if not hasattr(self.m, 'actor'):
+            self.m.addActor()
+        # Generate a renderer window
+        win = vtkRenWin()
+        # Set the number of viewports
+        win.setnumViewports(1)
+        # Set the background colour
+        win.setBackground([1,1,1])
+        # Set camera projection 
+        renderWindowInteractor = vtk.vtkRenderWindowInteractor()
+        renderWindowInteractor.SetRenderWindow(win)
+        renderWindowInteractor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera())
+        # Set camera projection 
+        win.setView()
+        self.s.actor.setColor([1.0, 0.0, 0.0])
+        self.s.actor.setOpacity(0.5)
+        self.m.actor.setColor([0.0, 0.0, 1.0])
+        self.m.actor.setOpacity(0.5)
+        win.renderActors([self.s.actor, self.m.actor], shading=True)
+        win.Render()
+        win.rens[0].GetActiveCamera().Azimuth(180)
+        win.rens[0].GetActiveCamera().SetParallelProjection(True)
+        win.Render()
+        return win
+        
 
 
diff --git a/AmpScan/ampVis.py b/AmpScan/ampVis.py
index 63dc1a0..1b0b5d6 100644
--- a/AmpScan/ampVis.py
+++ b/AmpScan/ampVis.py
@@ -47,6 +47,7 @@ class vtkRenWin(vtk.vtkRenderWindow):
             self.rens[viewport].AddActor(actor)
         self.rens[viewport].ResetCamera()
         self.rens[viewport].GetActiveCamera().Zoom(zoom)
+        self.Render()
 
     def setScalarBar(self, actor, title=''):
         """
@@ -261,7 +262,7 @@ class visMixin(object):
         #self._v = numpy_support.numpy_to_vtk(self.vert, deep=0)
         self.actor.setVert(self.vert)
         self.actor.setFaces(self.faces)
-        #self.actor.setNorm()
+        self.actor.setNorm()
         # Test if values array is non-zero
         if self.values.any():
             self.actor.setValues(self.values)
diff --git a/GUIs/AmpScanGUI.py b/GUIs/AmpScanGUI.py
index c6d9607..751f456 100644
--- a/GUIs/AmpScanGUI.py
+++ b/GUIs/AmpScanGUI.py
@@ -1,8 +1,7 @@
-import AmpScan
 import sys
 import numpy as np
 from vtk.util import numpy_support
-from AmpScan.core import AmpObject
+from AmpScan import AmpObject
 from AmpScan.registration import registration
 from AmpScan.ampVis import qtVtkWindow
 from AmpScan.pressSens import pressSense
@@ -10,9 +9,9 @@ from PyQt5.QtCore import QPoint, QSize, Qt, QTimer, QRect, pyqtSignal
 from PyQt5.QtGui import (QColor, QFontMetrics, QImage, QPainter, QIcon,
                          QOpenGLVersionProfile)
 from PyQt5.QtWidgets import (QAction, QApplication, QGridLayout,
-                             QMainWindow, QMessageBox,
-                             QOpenGLWidget, QFileDialog,
-                             QSlider, QWidget)
+                             QMainWindow, QMessageBox, QComboBox,
+                             QOpenGLWidget, QFileDialog,QLabel,QPushButton,
+                             QSlider, QWidget, QTableWidget, QTableWidgetItem)
 
         
 class AmpScanGUI(QMainWindow):
@@ -35,7 +34,8 @@ class AmpScanGUI(QMainWindow):
         self.renWin = self.vtkWidget._RenderWindow
         self.renWin.setBackground()
         self.mainWidget = QWidget()
-        self.AmpObj = None
+        self.files = {}
+        self.filesDrop = list(self.files.keys())
 #        self.CMap = np.array([[212.0, 221.0, 225.0],
 #                              [31.0, 73.0, 125.0]])/255.0
         self.setCentralWidget(self.mainWidget)
@@ -47,6 +47,9 @@ class AmpScanGUI(QMainWindow):
         self.setWindowTitle("AmpScan Visualiser")
         self.resize(800, 800)
         self.show()
+        self.fileManager = fileManager(self)
+        self.fileManager.show()
+        self.fileManager.table.itemChanged.connect(self.display)
         
     def chooseOpenFile(self):
         """
@@ -60,68 +63,91 @@ class AmpScanGUI(QMainWindow):
         @Josh_Steer if no stl is selected then the window crashes!
 
         """
-        self.fname = QFileDialog.getOpenFileName(self, 'Open file',
+        fname = QFileDialog.getOpenFileName(self, 'Open file',
                                             filter="Meshes (*.stl)")
-        if self.AmpObj is not None:
-            self.renWin.renderActors([self.AmpObj.actor,])
-        self.AmpObj = AmpObject(self.fname[0], 'limb')
-        self.AmpObj.addActor()
+        if fname[0] == '':
+            return
+        name = fname[0][:-4].split('/')[-1]
+        self.files[name] = AmpObject(fname[0], 'limb')
+        amp = self.files[name]
+        amp.addActor()
+        self.fileManager.addRow(name, amp)
+        self.display()
+        self.filesDrop.append(name)
+        if hasattr(self, 'alCont'):
+            self.alCont.getNames()
+        if hasattr(self, 'regCont'):
+            self.regCont.getNames()
 #        self.AmpObj.lp_smooth()
-        self.renWin.setnumViewports(1)
-        self.renWin.setProjection()
-        self.renWin.renderActors([self.AmpObj.actor,])
         
-    def chooseSocket(self):
-        """
-        Button in GUI.
-
-        """
-        self.sockfname = QFileDialog.getOpenFileName(self, 'Open file',
-                                            filter="Meshes (*.stl)")
-        self.socket = AmpObject(self.sockfname[0], stype='socket')
-        self.socket.addActor()
-        self.socket.lp_smooth()
+    def display(self):
+        render = []
+        for r in range(self.fileManager.n):
+            [name, _, color, opacity, display] = self.fileManager.getRow(r)
+            if display == 2:
+                render.append(self.files[name].actor)
+            color = color[1:-1].split(',')
+            color = [float(c) for c in color]
+            self.files[name].actor.setColor(color)
+            self.files[name].actor.setOpacity(float(opacity))
+            self.renWin.renderActors(render)
+        
         
     def align(self):
         """
         Numpy style docstring.
 
         """
-        self.renWin.setnumViewports(2)
-        self.renWin.setView(view=[-1, 0, 0], viewport=1)
-        self.renWin.setProjection(True, 0)
-        self.renWin.setProjection(True, 1)
-#        self.renWin.render(self.AmpObj.actors, dispActors=['limb',])
-#        self.renWin.render(self.AmpObj.actors, dispActors=['socket',],
-#                              viewport=1)
-        self.renWin.renderActors([self.AmpObj.actor, self.socket.actor],
-                                 viewport=0)
-        self.renWin.renderActors([self.AmpObj.actor, self.socket.actor],
-                                 viewport=1)
-        self.AmpObj.actor.setColor([1.0, 0.0, 0.0])
-        self.AmpObj.actor.setOpacity(0.5)
-        self.socket.actor.setColor([0.0, 0.0, 1.0])
-        self.socket.actor.setOpacity(0.5)
+        self.alCont = AlignControls(self.filesDrop, self)
+        self.alCont.show()
+        self.alCont.icp.clicked.connect(self.runICP)
+#        self.renWin.setnumViewports(2)
+#        self.renWin.setView(view=[-1, 0, 0], viewport=1)
+#        self.renWin.setProjection(True, 0)
+#        self.renWin.setProjection(True, 1)
+##        self.renWin.render(self.AmpObj.actors, dispActors=['limb',])
+##        self.renWin.render(self.AmpObj.actors, dispActors=['socket',],
+##                              viewport=1)
+#        self.renWin.renderActors([self.AmpObj.actor, self.socket.actor],
+#                                 viewport=0)
+#        self.renWin.renderActors([self.AmpObj.actor, self.socket.actor],
+#                                 viewport=1)
+#        self.AmpObj.actor.setColor([1.0, 0.0, 0.0])
+#        self.AmpObj.actor.setOpacity(0.5)
+#        self.socket.actor.setColor([0.0, 0.0, 1.0])
+#        self.socket.actor.setOpacity(0.5)
+    
+    def runICP(self):
+        static = str(self.alCont.static.currentText())
+        moving = str(self.alCont.moving.currentText())
+        print('Run the ICP code between %s and %s' % (static, moving))
+
+    def runRegistration(self):
+        baseline = str(self.regCont.baseline.currentText())
+        target = str(self.regCont.target.currentText())
+        print('Run the Registration code between %s and %s' % (baseline, target))
         
     def register(self):
         """
         Numpy style docstring.
 
         """
+        self.regCont = RegistrationControls(self.filesDrop, self)
+        self.regCont.show()
+        self.regCont.reg.clicked.connect(self.runRegistration)
         
-        self.renWin.setnumViewports(1)
-        self.renWin.setProjection()
-        self.RegObj = registration(self.socket, self.AmpObj)
-        self.RegObj.addActor(CMap=self.AmpObj.CMapN2P)
-        self.renWin.renderActors([self.RegObj.actor,])
-        self.renWin.setScalarBar(self.RegObj.actor)
+#        self.renWin.setnumViewports(1)
+#        self.renWin.setProjection()
+#        self.RegObj = registration(self.socket, self.AmpObj)
+#        self.RegObj.addActor(CMap=self.AmpObj.CMapN2P)
+#        self.renWin.renderActors([self.RegObj.actor,])
+#        self.renWin.setScalarBar(self.RegObj.actor)
     
     def analyse(self):
         """
         Numpy style docstring.
 
         """
-
         #self.RegObj.plot_slices()
         self.AmpObj.vert[:, 0] *= 2
         self.AmpObj.actor.points.Modified()
@@ -173,8 +199,6 @@ class AmpScanGUI(QMainWindow):
         self.openFile = QAction(QIcon('open.png'), 'Open', self,
                                 shortcut='Ctrl+O',
                                 triggered=self.chooseOpenFile)
-        self.openSocket = QAction(QIcon('open.png'), 'Open Socket', self,
-                                triggered=self.chooseSocket)
         self.openFE = QAction(QIcon('open.png'), 'Open FE', self,
                                 triggered=self.chooseFE)
         self.openPress = QAction(QIcon('open.png'), 'Open Press', self,
@@ -195,7 +219,6 @@ class AmpScanGUI(QMainWindow):
         """
         self.fileMenu = self.menuBar().addMenu("&File")
         self.fileMenu.addAction(self.openFile)
-        self.fileMenu.addAction(self.openSocket)
         self.fileMenu.addSeparator()
         self.fileMenu.addAction(self.exitAct)
         self.alignMenu = self.menuBar().addMenu("&Align")
@@ -208,7 +231,132 @@ class AmpScanGUI(QMainWindow):
         self.analyseMenu.addAction(self.analyse)
         self.kineticMenu = self.menuBar().addMenu("&Kinetic Measurements")
         self.kineticMenu.addAction(self.openPress)
+        
+class fileManager(QMainWindow):
+    """
+    Controls to manage the displayed 
+    
+    Example
+    -------
+    Perhaps an example implementation:
+
+    >>> from AmpScan.AmpScanGUI import AmpScanGUI
+
+    """
+
+    def __init__(self, parent = None):
+        super(fileManager, self).__init__(parent)
+        self.main = QWidget()
+        self.table = QTableWidget()
+        self.setCentralWidget(self.main)
+        self.layout = QGridLayout()
+        self.layout.addWidget(self.table, 0, 0)
+        self.main.setLayout(self.layout)
+        self.setWindowTitle("AmpObject Manager")
+        self.table.setRowCount(0)
+        self.table.setColumnCount(5)
+        self.table.setHorizontalHeaderLabels(['Name', 'Type', 'Colour', 'Opacity', 'Display'])
+        self.n = self.table.rowCount()
+        
+    def addRow(self, name, amp):
+        self.table.insertRow(self.n)
+        self.table.setItem(self.n, 0, QTableWidgetItem(name))
+        self.table.setItem(self.n, 1, QTableWidgetItem(amp.stype))
+        self.table.setItem(self.n, 2, QTableWidgetItem(str(amp.actor.GetProperty().GetColor())))
+        self.table.setItem(self.n, 3, QTableWidgetItem(str(amp.actor.GetProperty().GetOpacity())))
+        chkBoxItem = QTableWidgetItem()
+        chkBoxItem.setTextAlignment(Qt.AlignCenter)
+        chkBoxItem.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
+        chkBoxItem.setCheckState(Qt.Checked)       
+        
+        self.table.setItem(self.n,4,chkBoxItem)
+        self.n = self.table.rowCount()
+        
+    def getRow(self, i):
+        row = []
+        for r in range(self.table.columnCount() - 1):
+            row.append(self.table.item(i, r).text())
+        row.append(self.table.item(i, r+1).checkState())
+        return row 
+
+class AlignControls(QMainWindow):
+    """
+    Pop up for controls to align the 
+    
+    Example
+    -------
+    Perhaps an example implementation:
+
+    >>> from AmpScan.AmpScanGUI import AmpScanGUI
+
+    """
+
+    def __init__(self, names, parent = None):
+        super(AlignControls, self).__init__(parent)
+        self.main = QWidget()
+        self.names = names
+        self.static = QComboBox()
+        self.moving = QComboBox()
+        self.icp = QPushButton("Run ICP")
+        self.setCentralWidget(self.main)
+        self.layout = QGridLayout()
+        self.layout.addWidget(QLabel('Static'), 0, 0)
+        self.layout.addWidget(QLabel('Moving'), 1, 0)
+        self.layout.addWidget(self.static, 0, 1)
+        self.layout.addWidget(self.moving, 1, 1)
+        self.layout.addWidget(self.icp, 2, 0, 1, -1)
+        self.main.setLayout(self.layout)
+        self.setWindowTitle("Alignment Manager")
+        self.getNames()
+    
+    def getNames(self):
+        """
+        """
+        self.static.clear()
+        self.static.addItems(self.names)
+        self.moving.clear()
+        self.moving.addItems(self.names)
            
+        
+class RegistrationControls(QMainWindow):
+    """
+    Pop up for controls to align the 
+    
+    Example
+    -------
+    Perhaps an example implementation:
+
+    >>> from AmpScan.AmpScanGUI import AmpScanGUI
+
+    """
+
+    def __init__(self, names, parent = None):
+        super(RegistrationControls, self).__init__(parent)
+        self.main = QWidget()
+        self.names = names
+        self.baseline = QComboBox()
+        self.target = QComboBox()
+        self.reg = QPushButton("Run Registration")
+        self.setCentralWidget(self.main)
+        self.layout = QGridLayout()
+        self.layout.addWidget(QLabel('Baseline'), 0, 0)
+        self.layout.addWidget(QLabel('Target'), 1, 0)
+        self.layout.addWidget(self.baseline, 0, 1)
+        self.layout.addWidget(self.target, 1, 1)
+        self.layout.addWidget(self.reg, 2, 0, 1, -1)
+        self.main.setLayout(self.layout)
+        self.setWindowTitle("Alignment Manager")
+        self.getNames()
+    
+    def getNames(self):
+        """
+        """
+        self.baseline.clear()
+        self.baseline.addItems(self.names)
+        self.target.clear()
+        self.target.addItems(self.names)
+
+
 if __name__ == "__main__":
     app = QApplication(sys.argv)
     mainWin = AmpScanGUI()
-- 
GitLab