175 changes: 92 additions & 83 deletions python/plugins/sextante/modeler/ModelerGraphicItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
* *
***************************************************************************
"""
from sextante.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog
from sextante.modeler.ModelerParametersDialog import ModelerParametersDialog

__author__ = 'Victor Olaya'
__date__ = 'August 2012'
Expand All @@ -27,8 +29,6 @@
from sextante.parameters.Parameter import Parameter
import os
from sextante.core.GeoAlgorithm import GeoAlgorithm
from sextante.modeler.ModelerParametersDialog import ModelerParametersDialog
from sextante.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog

class ModelerGraphicItem(QtGui.QGraphicsItem):

Expand All @@ -46,6 +46,10 @@ def __init__(self, element, elementIndex, model):
icon = QtGui.QIcon(os.path.dirname(__file__) + "/../images/input.png")
self.pixmap = icon.pixmap(20, 20, state=QtGui.QIcon.On)
self.text = element.description
elif isinstance(element, basestring): #output name
icon = QtGui.QIcon(os.path.dirname(__file__) + "/../images/output.png")
self.pixmap = icon.pixmap(20, 20, state=QtGui.QIcon.On)
self.text = element
else:
state=QtGui.QIcon.On
if self.elementIndex in self.model.deactivated:
Expand All @@ -56,56 +60,57 @@ def __init__(self, element, elementIndex, model):
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
self.setZValue(1000)

icon = QtGui.QIcon(os.path.dirname(__file__) + "/../images/edit.png")
pt = QtCore.QPointF(ModelerGraphicItem.BOX_WIDTH / 2 - FlatButtonGraphicItem.WIDTH / 2, ModelerGraphicItem.BOX_HEIGHT / 2 - FlatButtonGraphicItem.HEIGHT / 2 + 1)
self.editButton = FlatButtonGraphicItem(icon, pt, self.editElement)
self.editButton.setParentItem(self)
icon = QtGui.QIcon(os.path.dirname(__file__) + "/../images/delete.png")
pt = QtCore.QPointF(ModelerGraphicItem.BOX_WIDTH / 2 - FlatButtonGraphicItem.WIDTH / 2, -ModelerGraphicItem.BOX_HEIGHT / 2 + FlatButtonGraphicItem.HEIGHT / 2 + 1)
self.deleteButton = FlatButtonGraphicItem(icon, pt, self.removeElement)
self.deleteButton.setParentItem(self)

if isinstance(element, GeoAlgorithm):
if element.parameters:

if not isinstance(element, basestring):
icon = QtGui.QIcon(os.path.dirname(__file__) + "/../images/edit.png")
pt = QtCore.QPointF(ModelerGraphicItem.BOX_WIDTH / 2 - FlatButtonGraphicItem.WIDTH / 2, ModelerGraphicItem.BOX_HEIGHT / 2 - FlatButtonGraphicItem.HEIGHT / 2 + 1)
self.editButton = FlatButtonGraphicItem(icon, pt, self.editElement)
self.editButton.setParentItem(self)
icon = QtGui.QIcon(os.path.dirname(__file__) + "/../images/delete.png")
pt = QtCore.QPointF(ModelerGraphicItem.BOX_WIDTH / 2 - FlatButtonGraphicItem.WIDTH / 2, -ModelerGraphicItem.BOX_HEIGHT / 2 + FlatButtonGraphicItem.HEIGHT / 2 + 1)
self.deleteButton = FlatButtonGraphicItem(icon, pt, self.removeElement)
self.deleteButton.setParentItem(self)

if isinstance(element, GeoAlgorithm):
if element.parameters:
pt = self.getLinkPointForParameter(-1)
x = self.getXPositionForFoldButton()
x = self.getXPositionForFoldButton()
pt = QtCore.QPointF(x, pt.y() + 3)
self.inButton = FoldButtonGraphicItem(pt, self.foldInput)
self.inButton.setParentItem(self)
if element.outputs:
pt = self.getLinkPointForOutput(-1)
x = self.getXPositionForFoldButton()
pt = QtCore.QPointF(x, pt.y() + 3)
x = self.getXPositionForFoldButton()
pt = QtCore.QPointF(x, pt.y() + 3)
self.outButton = FoldButtonGraphicItem(pt, self.foldOutput)
self.outButton.setParentItem(self)


def foldInput(self, folded):
self.inputFolded = folded
self.prepareGeometryChange()
if self.element.outputs:
self.prepareGeometryChange()
if self.element.outputs:
pt = self.getLinkPointForOutput(-1)
x = self.getXPositionForFoldButton()
x = self.getXPositionForFoldButton()
pt = QtCore.QPointF(x, pt.y())
self.outButton.position = pt
self.update()

def foldOutput(self, folded):
self.outputFolded = folded
self.prepareGeometryChange()
self.update()

self.outputFolded = folded
self.prepareGeometryChange()
self.update()
def addArrow(self, arrow):
self.arrows.append(arrow)

def boundingRect(self):
font = QtGui.QFont("Verdana", 8)
fm = QtGui.QFontMetricsF(font)
fm = QtGui.QFontMetricsF(font)
numParams = 0 if self.inputFolded else len(self.element.parameters)
numOutputs = 0 if self.outputFolded else len(self.element.outputs)
numElements = numParams + numOutputs + 3
h = (fm.height() * 1.2) * numElements
numOutputs = 0 if self.outputFolded else len(self.element.outputs)
numElements = numParams + numOutputs + 3
h = (fm.height() * 1.2) * numElements
rect = QtCore.QRectF(-(ModelerGraphicItem.BOX_WIDTH + 2)/2, -(ModelerGraphicItem.BOX_HEIGHT + 2)/2,
ModelerGraphicItem.BOX_WIDTH + 2, ModelerGraphicItem.BOX_HEIGHT + h)
return rect
Expand All @@ -129,17 +134,17 @@ def contextMenuEvent(self, event):
popupmenu.exec_(event.screenPos())

def deactivateAlgorithm(self):
self.model.setPositions(self.scene().getParameterPositions(), self.scene().getAlgorithmPositions())
self.model.setPositions(self.scene().getParameterPositions(), self.scene().getAlgorithmPositions(), self.scene().getOutputPositions())
self.model.deactivateAlgorithm(self.elementIndex, True)

def activateAlgorithm(self):
self.model.setPositions(self.scene().getParameterPositions(), self.scene().getAlgorithmPositions())
self.model.setPositions(self.scene().getParameterPositions(), self.scene().getAlgorithmPositions(), self.scene().getOutputPositions())
if not self.model.activateAlgorithm(self.elementIndex, True):
QtGui.QMessageBox.warning(None, "Could not activate Algorithm",
"The selected algorithm depends on other currently non-active algorithms.\nActivate them them before trying to activate it.")

def editElement(self):
self.model.setPositions(self.scene().getParameterPositions(), self.scene().getAlgorithmPositions())
self.model.setPositions(self.scene().getParameterPositions(), self.scene().getAlgorithmPositions(), self.scene().getOutputPositions())
if isinstance(self.element, Parameter):
dlg = ModelerParameterDefinitionDialog(self.model, param = self.element)
dlg.exec_()
Expand All @@ -148,8 +153,7 @@ def editElement(self):
self.element = dlg.param
self.text = self.element.description
self.update()

else:
elif isinstance(self.element, GeoAlgorithm):
dlg = self.element.getCustomModelerParametersDialog(self.model, self.elementIndex)
if not dlg:
dlg = ModelerParametersDialog(self.element, self.model, self.elementIndex)
Expand All @@ -162,7 +166,7 @@ def removeElement(self):
if not self.model.removeParameter(self.elementIndex):
QtGui.QMessageBox.warning(None, "Could not remove element",
"Other elements depend on the selected one.\nRemove them before trying to remove it.")
else:
elif isinstance(self.element, GeoAlgorithm):
if not self.model.removeAlgorithm(self.elementIndex):
QtGui.QMessageBox.warning(None, "Could not remove element",
"Other elements depend on the selected one.\nRemove them before trying to remove it.")
Expand Down Expand Up @@ -196,67 +200,71 @@ def paint(self, painter, option, widget=None):
painter.setPen(QtGui.QPen(QtCore.Qt.blue))
if isinstance(self.element, GeoAlgorithm):
if self.elementIndex in self.model.deactivated:
painter.setPen(QtGui.QPen(QtCore.Qt.lightGray))
painter.setPen(QtGui.QPen(QtCore.Qt.lightGray))
fm = QtGui.QFontMetricsF(font)
text = self.getAdjustedText(self.text)
h = fm.height()
pt = QtCore.QPointF(-(ModelerGraphicItem.BOX_WIDTH)/2 + 25, h/2.0)
painter.drawText(pt, text)
painter.setPen(QtGui.QPen(QtCore.Qt.black))
if isinstance(self.element, GeoAlgorithm):
if isinstance(self.element, GeoAlgorithm):
h = (fm.height() * 1.2)
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
pt = QtCore.QPointF(-(ModelerGraphicItem.BOX_WIDTH)/2 + 25, h)
painter.drawText(pt, "In")
i = 1
if not self.inputFolded:
for param in self.element.parameters:
text = self.getAdjustedText(param.description)
text = self.getAdjustedText(param.description)
h = (fm.height() * 1.2) * (i + 1)
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
pt = QtCore.QPointF(-(ModelerGraphicItem.BOX_WIDTH)/2 + 33, h)
painter.drawText(pt, text)
i += 1
i += 1
h = (fm.height() * 1.2) * (i + 1)
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
pt = QtCore.QPointF(-(ModelerGraphicItem.BOX_WIDTH)/2 + 25, h)
painter.drawText(pt, "Out")
i += 1
if not self.outputFolded:
for out in self.element.outputs:
text = self.getAdjustedText(out.description)
text = self.getAdjustedText(out.description)
h = (fm.height() * 1.2) * (i + 1)
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
pt = QtCore.QPointF(-(ModelerGraphicItem.BOX_WIDTH)/2 + 33, h)
painter.drawText(pt, text)
i += 1
painter.drawPixmap(-(ModelerGraphicItem.BOX_WIDTH / 2.0) + 3, -8, self.pixmap)
if self.pixmap:
painter.drawPixmap(-(ModelerGraphicItem.BOX_WIDTH / 2.0) + 3, -8, self.pixmap)

def getLinkPointForParameter(self, paramIndex):
offsetX = 30
if self.inputFolded:
paramIndex = -1
offsetX = 22
font = QtGui.QFont("Verdana", 8)
fm = QtGui.QFontMetricsF(font)
h = (fm.height() * 1.2) * (paramIndex + 2) - fm.height() / 2.0
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
fm = QtGui.QFontMetricsF(font)
if isinstance(self.element, GeoAlgorithm):
h = (fm.height() * 1.2) * (paramIndex + 2) - fm.height() / 2.0
h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
else:
h = 0
return QtCore.QPointF(-(ModelerGraphicItem.BOX_WIDTH)/2 + offsetX, h)


def getXPositionForFoldButton(self):
return 0

def getLinkPointForOutput(self, outputIndex):
if isinstance(self.element, GeoAlgorithm):
def getLinkPointForOutput(self, outputIndex):
if isinstance(self.element, GeoAlgorithm):
numParams = 0 if self.inputFolded else len(self.element.parameters)
outputIndex = outputIndex if not self.outputFolded else -1
text = self.getAdjustedText(self.element.outputs[outputIndex].description)
outputIndex = outputIndex if not self.outputFolded else -1
text = self.getAdjustedText(self.element.outputs[outputIndex].description)
font = QtGui.QFont("Verdana", 8)
fm = QtGui.QFontMetricsF(font)
fm = QtGui.QFontMetricsF(font)
w = fm.width(QtCore.QString(text))
h = (fm.height() * 1.2) * (outputIndex + 3 + numParams) - fm.height() / 2.0
y = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
y = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
x = -(ModelerGraphicItem.BOX_WIDTH)/2 + 33 + w + 5 if not self.outputFolded else 10
return QtCore.QPointF(x, y)
else:
Expand All @@ -271,9 +279,9 @@ def itemChange(self, change, value):

def polygon(self):
font = QtGui.QFont("Verdana", 8)
fm = QtGui.QFontMetricsF(font)
numElements = len(self.element.parameters) + len(self.element.outputs) + 3
h = (fm.height() * 1.2) * numElements
fm = QtGui.QFontMetricsF(font)
numElements = len(self.element.parameters) + len(self.element.outputs) + 3
h = (fm.height() * 1.2) * numElements
pol = QtGui.QPolygonF([
QtCore.QPointF(-(ModelerGraphicItem.BOX_WIDTH + 2)/2, -(ModelerGraphicItem.BOX_HEIGHT + 2)/2),
QtCore.QPointF(-(ModelerGraphicItem.BOX_WIDTH + 2)/2, (ModelerGraphicItem.BOX_HEIGHT + 2)/2 + h),
Expand All @@ -283,61 +291,62 @@ def polygon(self):
return pol

class FlatButtonGraphicItem(QtGui.QGraphicsItem):

WIDTH = 16
HEIGHT = 16

HEIGHT = 16
def __init__(self, icon, position, action):
super(FlatButtonGraphicItem, self).__init__(None, None)
self.setAcceptHoverEvents(True)
self.setAcceptHoverEvents(True)
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, False)
self.pixmap = icon.pixmap(self.WIDTH, self.HEIGHT, state=QtGui.QIcon.On)
self.position = position
self.isIn = False
self.action = action
self.isIn = False
self.action = action

def mousePressEvent(self, event):
self.action()

def paint(self, painter, option, widget=None):
pt = QtCore.QPointF(-self.WIDTH / 2, -self.HEIGHT / 2) + self.position
rect = QtCore.QRectF(pt.x(), pt.y(), self.WIDTH, self.HEIGHT)
rect = QtCore.QRectF(pt.x(), pt.y(), self.WIDTH, self.HEIGHT)
if self.isIn:
painter.setPen(QtGui.QPen(QtCore.Qt.transparent, 1))
painter.setBrush(QtGui.QBrush(QtCore.Qt.lightGray, QtCore.Qt.SolidPattern))
painter.setBrush(QtGui.QBrush(QtCore.Qt.lightGray, QtCore.Qt.SolidPattern))
else:
painter.setPen(QtGui.QPen(QtCore.Qt.transparent, 1))
painter.setPen(QtGui.QPen(QtCore.Qt.transparent, 1))
painter.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern))
painter.drawRect(rect)
painter.drawRect(rect)
painter.drawPixmap(pt.x(), pt.y(), self.pixmap)

def boundingRect(self):
rect = QtCore.QRectF(self.position.x() - self.WIDTH / 2, self.position.y() - self.HEIGHT / 2, self.WIDTH, self.HEIGHT)
return rect

return rect
def hoverEnterEvent(self, event):
self.isIn = True
self.isIn = True
self.update()

def hoverLeaveEvent(self, event):
self.isIn = False
self.isIn = False
self.update()

class FoldButtonGraphicItem(FlatButtonGraphicItem):

WIDTH = 11
HEIGHT = 11

icons = { True : QtGui.QIcon(os.path.dirname(__file__) + "/../images/plus.png"),
icons = { True : QtGui.QIcon(os.path.dirname(__file__) + "/../images/plus.png"),
False : QtGui.QIcon(os.path.dirname(__file__) + "/../images/minus.png")}

def __init__(self, position, action):
self.folded = True
icon = self.icons[True]
super(FoldButtonGraphicItem, self).__init__(icon, position, action)
super(FoldButtonGraphicItem, self).__init__(icon, position, action)

def mousePressEvent(self, event):
self.folded = not self.folded
icon = self.icons[self.folded]
self.pixmap = icon.pixmap(self.WIDTH, self.HEIGHT, state=QtGui.QIcon.On)
self.action(self.folded)

40 changes: 39 additions & 1 deletion python/plugins/sextante/modeler/ModelerScene.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(self, parent=None):
super(ModelerScene, self).__init__(parent)
self.paramItems = []
self.algItems = []
self.outputItems = []
self.setItemIndexMethod(QtGui.QGraphicsScene.NoIndex);

def getParameterPositions(self):
Expand All @@ -50,6 +51,18 @@ def getAlgorithmPositions(self):
for item in self.algItems:
pos.append(item.pos())
return pos

def getOutputPositions(self):
pos = []
for alg in self.outputItems:
outputPos = {}
for key,value in alg.iteritems():
if value is not None:
outputPos[key] = value.pos()
else:
outputPos[key] = None
pos.append(outputPos)
return pos

def getLastParameterItem(self):
return self.paramItems[-1]
Expand Down Expand Up @@ -93,6 +106,7 @@ def getItemsFromAAP(self, aap, isMultiple):
def paintModel(self, model):
self.model = model
i=0
#inputs
for param in model.parameters:
item = ModelerGraphicItem(param, i, model)
item.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
Expand All @@ -101,7 +115,7 @@ def paintModel(self, model):
item.setPos(model.paramPos[i])
self.paramItems.append(item)
i+=1
#first we add the algs
#we add the algs
iAlg=0
for alg in model.algs:
item = ModelerGraphicItem(alg, iAlg, model)
Expand All @@ -111,6 +125,7 @@ def paintModel(self, model):
item.setPos(model.algPos[iAlg])
self.algItems.append(item)
iAlg+=1

#and then the arrows
iAlg=0
for alg in model.algs:
Expand All @@ -128,6 +143,29 @@ def paintModel(self, model):
arrow = ModelerArrowItem(self.algItems[depend], -1, self.algItems[iAlg], -1)
self.addItem(arrow)
iAlg+=1

#and finally the outputs
for iAlg, alg in enumerate(model.algs):
outputs = model.algOutputs[iAlg]
outputItems = {}
for idx, key in enumerate(outputs.keys()):
out = outputs[key]
if out is not None:
item = ModelerGraphicItem(out, idx, model)
item.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
item.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
self.addItem(item)
pos = model.outputPos[iAlg][key]
if pos is None:
pos = self.algItems[iAlg].pos() + QtCore.QPointF(ModelerGraphicItem.BOX_WIDTH,0) + self.algItems[iAlg].getLinkPointForOutput(idx)
item.setPos(pos)
outputItems[key] = item
arrow = ModelerArrowItem(self.algItems[iAlg], idx, item, -1)
self.addItem(arrow)
else:
outputItems[key] = None
self.outputItems.append(outputItems)


def mousePressEvent(self, mouseEvent):
if (mouseEvent.button() != QtCore.Qt.LeftButton):
Expand Down