- Author:
- rjag008 <rjag008@auckland.ac.nz>
- Date:
- 2018-08-18 17:01:42+12:00
- Desc:
- Final Release
- Permanent Source URI:
- https://models.physiomeproject.org/workspace/51d/rawfile/9ea33dda840d259caf68e26d21300bece4eeff3f/digitiser/qtdigitiser.py
'''
Created on 15/06/2018
@author: rjag008
'''
from digitiser.marker import PaintGraphicsEllipseItem
from digitiser.dataset import PaintDatasetItem
from digitiser.view import PaintGraphicsView
from digitiser.model import PaintModel
from digitiser.scene import PaintGraphicsScene
import sys
from os import path
from collections import OrderedDict
dir_path = path.dirname(path.realpath(sys.argv[0]))
if not hasattr(sys, 'frozen'): #For py2exe
dir_path = path.join(dir_path,"..")
uiFile = path.join(dir_path,"./digitiser/PaintUI.ui")
try:
from PySide import QtCore, QtGui
from pysideuiutils.uic import loadUi
class PainterWidgetBase(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
loadUi(uiFile, self)
except ImportError:
#from PyQt4 import QtCore, QtGui, uic
'''
form,base = uic.loadUiType(uiFile)
class PainterWidgetBase(base,form):
def __init__(self,parent=None):
super(base,self).__init__(parent)
self.setupUi(self)
'''
pass
class PaintView(object):
meshCoordinateScalingFactor = 250
def __init__(self, model, graphicScene, listDatasets):
self._model = model
self._graphicScene = graphicScene
self._listDatasets = listDatasets
self.PenColors = [QtGui.QColor(col) for col in [ QtCore.Qt.red, QtCore.Qt.green, QtCore.Qt.blue,\
QtCore.Qt.cyan, QtCore.Qt.magenta, QtCore.Qt.yellow]]
self.datasetColors = self._model.datasetColors
if len(self.datasetColors)==0:
self.datasetColors[0] = self.createPenAndMaterial(self.PenColors[0]) #Default items color
self.reset()
self._model.modelChanged.connect(self.modelChanged)
self.modelChanged()
self._listDatasets.itemChanged.connect(self.updateDatasetName)
self._listDatasets.itemDoubleClicked.connect(self.changeItemColor)
self._graphicsPaths = {}
self.meshSilhouette = None
def changeItemColor(self,item):
newColor = item.changeColor()
if not newColor is None:
ds = item._key
#Change the color of related path and points
self.datasetColors[ds] = self.createPenAndMaterial(newColor)
for k in self._pointItems:
item, dataset = self._pointItems[k]
if dataset==ds:
item.setColor(newColor)
self.updatePath(ds)
def setModel(self,model):
self._model = model
self.datasetColors = self._model.datasetColors
def createPenAndMaterial(self,color):
brush = QtGui.QBrush()
bcolor = QtGui.QColor(color)
bcolor.setAlpha(127)
brush.setColor(bcolor)
brush.setStyle(QtCore.Qt.CrossPattern)
pen = QtGui.QPen(color, 1, QtCore.Qt.SolidLine,QtCore.Qt.FlatCap, QtCore.Qt.MiterJoin)
return [pen,brush]
def reset(self):
self._graphicScene.clear()
self._listDatasets.clear()
background_file = self._model.getBackgroundFile()
if background_file!='Default':
pixmap = QtGui.QPixmap(background_file)
self._background = self._graphicScene.addPixmap(pixmap)
else:
self._graphicScene.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern))
self._background = None
self._pointItems = {}
self._datasetItems = {}
self._graphicsPaths = {}
self.meshSilhouette = None
def setMeshBackground(self,meshmapper,axialElements):
'''
Create a background based on the mesh
'''
leftwall,rightwall,ladder = meshmapper.getBoundaryPoints()
leftwall *=self.meshCoordinateScalingFactor
rightwall *=self.meshCoordinateScalingFactor
ladder *=self.meshCoordinateScalingFactor
#Create lines to represent mesh
#Create the brush
color = QtGui.QColor(QtCore.Qt.black)
pen = QtGui.QPen(color, 5, QtCore.Qt.SolidLine,QtCore.Qt.FlatCap, QtCore.Qt.MiterJoin)
if hasattr(self, 'meshSilhouette'):
if not self.meshSilhouette is None:
self._graphicScene.removeItem(self.meshSilhouette)
gpath = QtGui.QPainterPath()
#First create points
lpts = []
rpts = []
for i in range(axialElements+1):
p1 = QtCore.QPointF(ladder[i,0], ladder[i,1])
p2 = QtCore.QPointF(ladder[i,3], ladder[i,4])
lpts.append(p1)
rpts.append(p2)
#Draw the ladder
for i in range(axialElements+1):
gpath.moveTo(lpts[i])
gpath.quadTo(lpts[i],rpts[i])
#Left wall
lpts = []
for coord in leftwall:
p1 = QtCore.QPointF(coord[0],coord[1])
lpts.append(p1)
gpath.moveTo(lpts[0])
for i in range(1,len(lpts)):
gpath.quadTo(lpts[i-1], lpts[i])
#Interpolate of right
rrpts = []
for coord in rightwall:
p1 = QtCore.QPointF(coord[0],coord[1])
rrpts.append(p1)
gpath.moveTo(rrpts[0])
for i in range(1,len(rrpts)):
gpath.quadTo(rrpts[i-1], rrpts[i])
gitem = self._graphicScene.addPath(gpath,pen)
gitem.setEnabled(False)
gitem.setZValue(0)
self.meshSilhouette = gitem
self._model.setToScaffoldMeshScaling(self.meshCoordinateScalingFactor)
def updateBackground(self):
background_file = self._model.getBackgroundFile()
if background_file != 'Default':
pixmap = QtGui.QPixmap(background_file)
zValue = 0
if not self._background is None:
zValue = self._background.zValue()
self._graphicScene.removeItem(self._background)
if hasattr(self, 'meshSilhouette'):
if not self.meshSilhouette is None:
self._graphicScene.removeItem(self.meshSilhouette)
self.meshSilhouette = None
self._background = self._graphicScene.addPixmap(pixmap)
self._background.setZValue(zValue)
def modelChanged(self):
self.updatePointItems()
self.updateListDatasets()
def updatePointItems(self):
model_points = self._model.getPoints()
set_model_keys = set(model_points.keys())
set_view_keys = set(self._pointItems.keys())
set_intersect_keys = set_model_keys.intersection(set_view_keys)
# Added points
added_keys = set_model_keys - set_intersect_keys
for key in added_keys:
pos, dataset = model_points[key]
pos = QtCore.QPointF(pos[0], pos[1])
self.addPointItem(key, pos, dataset)
# Removed points
removed_keys = set_view_keys - set_intersect_keys
for key in removed_keys:
self.removePointItem(key)
# Potentially moved points
for key in set_intersect_keys:
model_pos = model_points[key][0]
model_pos = QtCore.QPointF(model_pos[0], model_pos[1])
item = self._pointItems[key][0]
dist = (model_pos - item.scenePos()).manhattanLength()
if dist > 0.001:
item.setPos(model_pos)
def addPointItem(self, key, pos, dataset):
if dataset in self.datasetColors:
pen = self.datasetColors[dataset][0]
else:
pen,brush = self.createPenAndMaterial(self.PenColors[dataset % len(self.PenColors)])
self.datasetColors[dataset] = [pen,brush]
item = PaintGraphicsEllipseItem(pen, key)
item.setZValue(2)
self._pointItems[key] = (item, dataset)
self._graphicScene.addItem(item)
item.setPos(pos)
def removePointItem(self, key):
item, _ = self._pointItems[key]
self._graphicScene.removeItem(item)
del self._pointItems[key]
def listSelectionChanged(self,dataset):
for k in self._pointItems:
item,ds = self._pointItems[k]
item.setEnabled(ds==dataset)
def updateDatasetName(self,item):
key = item._key
if item._name != str(item.text()):
item._name = str(item.text())
self._model.changeDatasetName(key,str(item.text()))
else:
self.listItemVisibilityToggled(item)
def updateListDatasets(self):
model_datasets = self._model.getDatasets()
set_model_keys = set(model_datasets.keys())
set_view_keys = set(self._datasetItems.keys())
set_intersect_keys = set_model_keys.intersection(set_view_keys)
#Set the names to match for common keys
def iterAllItems(self):
for i in range(self.count()):
yield self.item(i)
for item in iterAllItems(self._listDatasets):
if item._key in set_intersect_keys:
name = model_datasets[item._key]
item._name = name
item.setText(name)
# Added datasets
added_keys = set_model_keys - set_intersect_keys
for key in added_keys:
name = model_datasets[key]
self.addListItem(key, name)
# Removed datasets
removed_keys = set_view_keys - set_intersect_keys
for key in removed_keys:
self.removeListItem(key)
#Set current item
key = self._model.getCurrentDataset()
if not key is None:
item = self._datasetItems[key]
self._listDatasets.setCurrentItem(item)
def addListItem(self, key, name):
if key in self.datasetColors:
pen = self.datasetColors[key][0]
else:
pen,brush = self.createPenAndMaterial(self.PenColors[key % len(self.PenColors)])
self.datasetColors[key] = [pen,brush]
item = PaintDatasetItem(key, name, pen.color())
self._datasetItems[key] = item
self._listDatasets.addItem(item)
def listItemVisibilityToggled(self,item):
state = item.checkState()
key = item._key
if self.hasPath(key):
self._graphicsPaths[key].setVisible(state)
for k in self._pointItems:
item,ds = self._pointItems[k]
if ds==key:
item.setVisible(state)
def removeListItem(self, key):
# TODO: Iterate through items to find the one with the key
def iterAllItems(self):
for i in range(self.count()):
yield self.item(i)
for item in iterAllItems(self._listDatasets):
if item._key == key:
del item
break
if self.hasPath(key):
self._graphicScene.removeItem(self._graphicsPaths[key])
del self._datasetItems[key]
def hasPath(self,dataset):
return dataset in self._graphicsPaths
def updatePath(self,dataset):
try:
if dataset in self._graphicsPaths:
try:
self._graphicScene.removeItem(self._graphicsPaths[dataset])
except:
pass
del self._graphicsPaths[dataset]
mpts = self._model.getPointsForDataset(dataset)
modelPoints = [QtCore.QPointF(pos[0], pos[1]) for pos in mpts]
if len(modelPoints) >=2:
#zval = min([item.zValue() for item in items])
gpath = QtGui.QPainterPath()
gpath.moveTo(modelPoints[0])
for i in range(1,len(modelPoints)):
gpath.quadTo(modelPoints[i-1], modelPoints[i])
gpath.closeSubpath()
#gpath.quadTo(modelPoints[-1], modelPoints[0])
#pen,brush = self.fillBrushes[dataset % len(self.PenColors)]
pen,brush = self.datasetColors[dataset]
gitem = self._graphicScene.addPath(gpath,pen,brush)
gitem.setZValue(1)
self._graphicsPaths[dataset] = gitem
except:
import traceback
traceback.print_exc(file=sys.stdout)
class PainterWidget(PainterWidgetBase):
region, point = range(2)
flipped = False
def __init__(self,mode,parent=None,project=None):
super(PainterWidget,self).__init__(parent)
self.sceneLayout = QtGui.QVBoxLayout(self.graphicsViewHolder)
self.graphicsView = PaintGraphicsView()
self.sceneLayout.addWidget(self.graphicsView)
self.filename = None
self.model = PaintModel()
self.model.modelChanged.connect(self.updateInterface)
self._graphicsScene = PaintGraphicsScene(self)
self.graphicsView.setScene(self._graphicsScene)
self.addNewDataset.setIcon(self.style().standardIcon(getattr(QtGui.QStyle, 'SP_FileDialogNewFolder')))
self.removeCurrentDataset.setIcon(self.style().standardIcon(getattr(QtGui.QStyle, 'SP_MessageBoxCritical')))
self.moveUp.setIcon(self.style().standardIcon(getattr(QtGui.QStyle, 'SP_ArrowUp')))
self.moveDown.setIcon(self.style().standardIcon(getattr(QtGui.QStyle, 'SP_ArrowDown')))
self.saveData.setIcon(self.style().standardIcon(getattr(QtGui.QStyle, 'SP_DialogSaveButton')))
self.loadData.setIcon(self.style().standardIcon(getattr(QtGui.QStyle, 'SP_DialogOpenButton')))
self.addNewDataset.clicked.connect(self.addDataset)
self.removeCurrentDataset.clicked.connect(self.removeDataset)
#self.loadBackground.clicked.connect(self.newBackground)
self.saveData.clicked.connect(self.saveRegions)
self.loadData.clicked.connect(self.loadRegions)
self.moveUp.clicked.connect(self.moveDatasetItemUp)
self.moveDown.clicked.connect(self.moveDatasetItemDown)
# Actions
self.listDatasets.itemSelectionChanged.connect(self.selectedDatasetChanged)
self._mode = mode
if mode==self.region:
self.viewMousePressEvent = self.viewMousePressEventRegion
self.pointItemMoved = self.pointItemMovedRegion
else:
self.viewMousePressEvent = self.viewMousePressEventPoint
self.pointItemMoved = self.pointItemMovedPoint
# Mouse move
self._graphicsScene.mousePressed.connect(self.viewMousePressEvent)
self._graphicsScene.pointItemMoved.connect(self.pointItemMoved)
self.loadproject(project)
def moveDatasetItemUp(self):
numItems = self.listDatasets.count()
if numItems>1:
currentRow = self.listDatasets.currentRow()
if currentRow > 0:
ci = self.listDatasets.takeItem(currentRow)
self.listDatasets.insertItem(currentRow - 1, ci)
self.listDatasets.setCurrentRow(currentRow - 1)
def moveDatasetItemDown(self):
numItems = self.listDatasets.count()
if numItems>1:
currentRow = self.listDatasets.currentRow()
if currentRow < numItems-1:
ci = self.listDatasets.takeItem(currentRow)
self.listDatasets.insertItem(currentRow + 1, ci)
self.listDatasets.setCurrentRow(currentRow + 1)
def setupMeshBackground(self,backgroundMesh,axialElements):
'''
Load a mesh and create a background based on the node that lie on xy-plane
Assumes the standard Stomach Meshobject is used
'''
self.model.newDataSet('Default')
self.view = PaintView(self.model, self._graphicsScene, self.listDatasets)
self.view.setMeshBackground(backgroundMesh,axialElements)
#Rendering needs to be flipped along y, as qgraphicsscene coordinate system starts at top,left
if not self.flipped:
self.graphicsView.setflip(-1)
self.flipped = True
self.filename = None
self.updateInterface()
def getMarkersAndColors(self):
result = self.model.getDatasetInTransformedCoordinates()
#Order the result in the listed order
orderedResult = OrderedDict()
for row in range(self.listDatasets.count()):
item = self.listDatasets.item(row)
name = item._name
orderedResult[name] = result[name]
orderedColors = OrderedDict()
for k,name in self.model._datasets.items():
p,_ = self.model.datasetColors[k]
color = p.color()
orderedColors[name] = [color.red(),color.green(),color.blue()]
return orderedResult,orderedColors
def saveRegions(self,filename=None):
if hasattr(self, 'view'):
if filename is None:
filename = QtGui.QFileDialog.getSaveFileName(self,
'Dataset file', '.',
"Data Files (*.dat)")
if isinstance(filename,tuple): #Handle pyside
filename = str(filename[0])
if filename is not None and not str(filename) == "":
return self.model.saveDataset(filename)
return False
def loadRegions(self,filename=None):
if hasattr(self, 'view'):
if filename is None:
filename = QtGui.QFileDialog.getOpenFileName(self,
'Dataset file', '.',
"Data Files (*.dat)")
if isinstance(filename,tuple): #Handle pyside
filename = str(filename[0])
if filename is not None and not str(filename) == "":
self.model.loadDataset(filename)
self.view.setModel(self.model)
if self._mode==self.region:
for dataset in self.model._datasets:
self.view.updatePath(dataset)
def newBackground(self):
filename = QtGui.QFileDialog.getOpenFileName(self,
'Select background image', '.',
"Image Files (*.png *.jpg *.jpeg *.bmp *.tif)")
if isinstance(filename,tuple): #Handle pyside
filename = str(filename[0])
if filename is not None and not str(filename) == "":
# Clean previous state
if hasattr(self, 'view'):
if hasattr(self.view, 'meshSilhouette') and not self.view.meshSilhouette is None :
if self.flipped:
self.graphicsView.setflip(-1)
self.flipped = False
self.model.addImage(str(filename))
self.view.updateBackground()
else:
self.model.newDataSet(filename)
self.view = PaintView(self.model, self._graphicsScene, self.listDatasets)
self.filename = filename
else:
self.filename = None
self.updateInterface()
def defaultProject(self):
self.model.newDataSet('Default')
self.view = PaintView(self.model, self._graphicsScene, self.listDatasets)
self.filename = None
self.updateInterface()
def loadproject(self,filename):
if filename is not None and not str(filename) == "":
# Try to load project
self.model.loadDataset(filename)
self.view = PaintView(self.model, self._graphicsScene, self.listDatasets)
self.filename = filename
self.updateInterface()
def updateInterface(self):
self.graphicsView.setEnabled(True)
def addDataset(self):
self.model.addDataset()
def removeDataset(self):
row = self.listDatasets.currentRow()
item = self.listDatasets.takeItem(row)
self.model.removeDataset(item._key)
def selectedDatasetChanged(self):
if not self.listDatasets.currentItem() is None:
ds = self.listDatasets.currentItem()._key
self.model.changeCurrentDataset(ds)
if hasattr(self, 'view'):
self.view.listSelectionChanged(ds)
else:
self.model.changeCurrentDataset(None)
def viewMousePressEventRegion(self, event, item):
mouse_pos = event.scenePos();
mouse_pos = [mouse_pos.x(), mouse_pos.y()]
#mode = self.model._mode
currentDataset = self.model.getCurrentDataset()
if event.button() == QtCore.Qt.LeftButton:
if (item is None) or (item == self.view._background) or (item == self.view.meshSilhouette):
self.model.addPoint(mouse_pos)
elif isinstance(item, PaintGraphicsEllipseItem) and not self.view.hasPath(currentDataset):
self.view.updatePath(currentDataset)
elif event.button() == QtCore.Qt.RightButton:
if (not item is None) and (item != self.view._background) and (item != self.view.meshSilhouette):
if isinstance(item, PaintGraphicsEllipseItem):
key = item.getItemKey()
self.model.removePoint(key)
if self.view.hasPath(currentDataset):
self.view.updatePath(self.model.getCurrentDataset())
def viewMousePressEventPoint(self, event, item):
mouse_pos = event.scenePos();
mouse_pos = [mouse_pos.x(), mouse_pos.y()]
if event.button() == QtCore.Qt.LeftButton:
if (item is None) or (item == self.view._background):
self.model.addPoint(mouse_pos)
elif event.button() == QtCore.Qt.RightButton:
if (not item is None) and (item != self.view._background):
if isinstance(item, PaintGraphicsEllipseItem):
key = item.getItemKey()
self.model.removePoint(key)
def pointItemMovedRegion(self, key, pos):
self.model.movePoint(key, [pos.x(), pos.y()])
self.view.updatePath(self.model.getCurrentDataset())
def pointItemMovedPoint(self, key, pos):
self.model.movePoint(key, [pos.x(), pos.y()])
from digitiser.mapping import MeshMapper
if __name__ == '__main__':
# Entry point
application = QtGui.QApplication(sys.argv)
main_window = PainterWidget(0)
main_window.show()
#main_window.defaultProject()
backgroundMesh = MeshMapper()
#self.backgroundMesh.setupByFile(r'scaffoldmaker.ex2', 8,11)
backgroundMesh.setupByPickle('symmetricstomachsurface.pkl')
main_window.setupMeshBackground(backgroundMesh,11)
#main_window.newBackground()
sys.exit(application.exec_())