mirror of
				https://github.com/qgis/QGIS.git
				synced 2025-10-30 00:07:09 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			412 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			412 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| """
 | |
| ***************************************************************************
 | |
|     ModelerGraphicItem.py
 | |
|     ---------------------
 | |
|     Date                 : August 2012
 | |
|     Copyright            : (C) 2012 by Victor Olaya
 | |
|     Email                : volayaf at gmail dot com
 | |
| ***************************************************************************
 | |
| *                                                                         *
 | |
| *   This program is free software; you can redistribute it and/or modify  *
 | |
| *   it under the terms of the GNU General Public License as published by  *
 | |
| *   the Free Software Foundation; either version 2 of the License, or     *
 | |
| *   (at your option) any later version.                                   *
 | |
| *                                                                         *
 | |
| ***************************************************************************
 | |
| """
 | |
| 
 | |
| __author__ = 'Victor Olaya'
 | |
| __date__ = 'August 2012'
 | |
| __copyright__ = '(C) 2012, Victor Olaya'
 | |
| 
 | |
| # This will get replaced with a git SHA1 when you do a git archive
 | |
| 
 | |
| __revision__ = '$Format:%H$'
 | |
| 
 | |
| import os
 | |
| from PyQt4 import QtCore, QtGui
 | |
| from processing.core.GeoAlgorithm import GeoAlgorithm
 | |
| from processing.parameters.Parameter import Parameter
 | |
| from processing.modeler.ModelerParameterDefinitionDialog import \
 | |
|         ModelerParameterDefinitionDialog
 | |
| from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
 | |
| 
 | |
| 
 | |
| class ModelerGraphicItem(QtGui.QGraphicsItem):
 | |
| 
 | |
|     BOX_HEIGHT = 30
 | |
|     BOX_WIDTH = 200
 | |
| 
 | |
|     def __init__(self, element, elementIndex, model):
 | |
|         super(ModelerGraphicItem, self).__init__(None, None)
 | |
|         self.model = model
 | |
|         self.element = element
 | |
|         self.elementIndex = elementIndex
 | |
|         self.inputFolded = True
 | |
|         self.outputFolded = True
 | |
|         if isinstance(element, Parameter):
 | |
|             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:
 | |
|                 state = QtGui.QIcon.Off
 | |
|             self.text = element.name
 | |
|             self.pixmap = element.getIcon().pixmap(15, 15, state=state)
 | |
|         self.arrows = []
 | |
|         self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
 | |
|         self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
 | |
|         self.setZValue(1000)
 | |
| 
 | |
|         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()
 | |
|                 pt = QtCore.QPointF(x, pt.y() + 2)
 | |
|                 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() + 2)
 | |
|                 self.outButton = FoldButtonGraphicItem(pt, self.foldOutput)
 | |
|                 self.outButton.setParentItem(self)
 | |
| 
 | |
|     def foldInput(self, folded):
 | |
|         self.inputFolded = folded
 | |
|         self.prepareGeometryChange()
 | |
|         if self.element.outputs:
 | |
|             pt = self.getLinkPointForOutput(-1)
 | |
|             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()
 | |
| 
 | |
|     def addArrow(self, arrow):
 | |
|         self.arrows.append(arrow)
 | |
| 
 | |
|     def boundingRect(self):
 | |
|         font = QtGui.QFont('Verdana', 8)
 | |
|         fm = QtGui.QFontMetricsF(font)
 | |
|         numParams = (0 if self.inputFolded else len(self.element.parameters))
 | |
|         numOutputs = (0 if self.outputFolded else len(self.element.outputs))
 | |
|         hUp = fm.height() * 1.2 * (numParams + 2)
 | |
|         hDown = fm.height() * 1.2 * (numOutputs + 2)
 | |
|         rect = QtCore.QRectF(-(ModelerGraphicItem.BOX_WIDTH + 2) / 2,
 | |
|                              -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2 - hUp,
 | |
|                              ModelerGraphicItem.BOX_WIDTH + 2,
 | |
|                              ModelerGraphicItem.BOX_HEIGHT + hDown + hUp)
 | |
|         return rect
 | |
| 
 | |
|     def mouseDoubleClickEvent(self, event):
 | |
|         self.editElement()
 | |
| 
 | |
|     def contextMenuEvent(self, event):
 | |
|         popupmenu = QtGui.QMenu()
 | |
|         removeAction = popupmenu.addAction('Remove')
 | |
|         removeAction.triggered.connect(self.removeElement)
 | |
|         editAction = popupmenu.addAction('Edit')
 | |
|         editAction.triggered.connect(self.editElement)
 | |
|         if isinstance(self.element, GeoAlgorithm):
 | |
|             if self.elementIndex in self.model.deactivated:
 | |
|                 removeAction = popupmenu.addAction('Activate')
 | |
|                 removeAction.triggered.connect(self.activateAlgorithm)
 | |
|             else:
 | |
|                 deactivateAction = popupmenu.addAction('Deactivate')
 | |
|                 deactivateAction.triggered.connect(self.deactivateAlgorithm)
 | |
|         popupmenu.exec_(event.screenPos())
 | |
| 
 | |
|     def deactivateAlgorithm(self):
 | |
|         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.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.scene().getOutputPositions())
 | |
|         if isinstance(self.element, Parameter):
 | |
|             dlg = ModelerParameterDefinitionDialog(self.model,
 | |
|                     param=self.element)
 | |
|             dlg.exec_()
 | |
|             if dlg.param is not None:
 | |
|                 self.model.updateParameter(self.elementIndex, dlg.param)
 | |
|                 self.element = dlg.param
 | |
|                 self.text = self.element.description
 | |
|                 self.update()
 | |
|         elif isinstance(self.element, GeoAlgorithm):
 | |
|             dlg = self.element.getCustomModelerParametersDialog(self.model,
 | |
|                     self.elementIndex)
 | |
|             if not dlg:
 | |
|                 dlg = ModelerParametersDialog(self.element, self.model,
 | |
|                         self.elementIndex)
 | |
|             dlg.exec_()
 | |
|             if dlg.params is not None:
 | |
|                 self.model.updateAlgorithm(self.elementIndex, dlg.params,
 | |
|                         dlg.values, dlg.outputs, dlg.dependencies)
 | |
| 
 | |
|     def removeElement(self):
 | |
|         if isinstance(self.element, Parameter):
 | |
|             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.')
 | |
|         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.')
 | |
| 
 | |
|     def getAdjustedText(self, text):
 | |
|         font = QtGui.QFont('Verdana', 8)
 | |
|         fm = QtGui.QFontMetricsF(font)
 | |
|         w = fm.width(text)
 | |
|         if w < self.BOX_WIDTH - 25 - FlatButtonGraphicItem.WIDTH:
 | |
|             return text
 | |
| 
 | |
|         text = text[0:-3] + '...'
 | |
|         w = fm.width(text)
 | |
|         while w > self.BOX_WIDTH - 25 - FlatButtonGraphicItem.WIDTH:
 | |
|             text = text[0:-4] + '...'
 | |
|             w = fm.width(text)
 | |
|         return text
 | |
| 
 | |
|     def paint(self, painter, option, widget=None):
 | |
|         rect = QtCore.QRectF(-(ModelerGraphicItem.BOX_WIDTH + 2) / 2.0,
 | |
|                              -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2.0,
 | |
|                              ModelerGraphicItem.BOX_WIDTH + 2,
 | |
|                              ModelerGraphicItem.BOX_HEIGHT + 2)
 | |
|         painter.setPen(QtGui.QPen(QtCore.Qt.gray, 1))
 | |
|         color = QtGui.QColor(125, 232, 232)
 | |
|         if isinstance(self.element, Parameter):
 | |
|             color = QtGui.QColor(179, 179, 255)
 | |
|         elif isinstance(self.element, GeoAlgorithm):
 | |
|             color = QtCore.Qt.white
 | |
|         painter.setBrush(QtGui.QBrush(color, QtCore.Qt.SolidPattern))
 | |
|         painter.drawRect(rect)
 | |
|         font = QtGui.QFont('Verdana', 8)
 | |
|         painter.setFont(font)
 | |
|         painter.setPen(QtGui.QPen(QtCore.Qt.black))
 | |
|         if self.isSelected():
 | |
|             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))
 | |
|         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):
 | |
|             h = -(fm.height() * 1.2)
 | |
|             h = h - ModelerGraphicItem.BOX_HEIGHT / 2.0 + 5
 | |
|             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)
 | |
|                     h = -(fm.height() * 1.2) * (i + 1)
 | |
|                     h = h - ModelerGraphicItem.BOX_HEIGHT / 2.0 + 5
 | |
|                     pt = QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2
 | |
|                             + 33, h)
 | |
|                     painter.drawText(pt, text)
 | |
|                     i += 1
 | |
|             i = 1
 | |
|             h = fm.height() * 1.2
 | |
|             h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
 | |
|             pt = QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2 + 25, h)
 | |
|             painter.drawText(pt, 'Out')
 | |
|             if not self.outputFolded:
 | |
|                 for out in self.element.outputs:
 | |
|                     text = self.getAdjustedText(out.description)
 | |
|                     h = fm.height() * 1.2 * (i + 1)
 | |
|                     h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
 | |
|                     pt = QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2
 | |
|                             + 33, h)
 | |
|                     painter.drawText(pt, text)
 | |
|                     i += 1
 | |
|         if self.pixmap:
 | |
|             painter.drawPixmap(-(ModelerGraphicItem.BOX_WIDTH / 2.0) + 3, -8,
 | |
|                                self.pixmap)
 | |
| 
 | |
|     def getLinkPointForParameter(self, paramIndex):
 | |
|         offsetX = 25
 | |
|         if self.inputFolded:
 | |
|             paramIndex = -1
 | |
|             offsetX = 17
 | |
|         font = QtGui.QFont('Verdana', 8)
 | |
|         fm = QtGui.QFontMetricsF(font)
 | |
|         if isinstance(self.element, GeoAlgorithm):
 | |
|             h = -(fm.height() * 1.2) * (paramIndex + 2) - fm.height() / 2.0 + 8
 | |
|             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):
 | |
|             numParams = 0
 | |
|             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)
 | |
|             w = fm.width(text)
 | |
|             h = fm.height() * 1.2 * (outputIndex + 1) + fm.height() / 2.0
 | |
|             y = h + ModelerGraphicItem.BOX_HEIGHT / 2.0 + 5
 | |
|             x = (-ModelerGraphicItem.BOX_WIDTH / 2 + 33 + w
 | |
|                  + 5 if not self.outputFolded else 10)
 | |
|             return QtCore.QPointF(x, y)
 | |
|         else:
 | |
|             return QtCore.QPointF(0, 0)
 | |
| 
 | |
|     def itemChange(self, change, value):
 | |
|         if change == QtGui.QGraphicsItem.ItemPositionChange:
 | |
|             for arrow in self.arrows:
 | |
|                 arrow.updatePosition()
 | |
| 
 | |
|         return value
 | |
| 
 | |
|     def polygon(self):
 | |
|         font = QtGui.QFont('Verdana', 8)
 | |
|         fm = QtGui.QFontMetricsF(font)
 | |
|         hUp = fm.height() * 1.2 * (len(self.element.parameters) + 2)
 | |
|         hDown = fm.height() * 1.2 * (len(self.element.outputs) + 2)
 | |
|         pol = QtGui.QPolygonF([
 | |
|                 QtCore.QPointF(
 | |
|                         -(ModelerGraphicItem.BOX_WIDTH + 2) / 2,
 | |
|                         -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2 - hUp),
 | |
|                 QtCore.QPointF(
 | |
|                         -(ModelerGraphicItem.BOX_WIDTH + 2) / 2,
 | |
|                         (ModelerGraphicItem.BOX_HEIGHT + 2) / 2 + hDown),
 | |
|                 QtCore.QPointF(
 | |
|                         (ModelerGraphicItem.BOX_WIDTH + 2) / 2,
 | |
|                         (ModelerGraphicItem.BOX_HEIGHT + 2) / 2 + hDown),
 | |
|                 QtCore.QPointF(
 | |
|                         (ModelerGraphicItem.BOX_WIDTH + 2) / 2,
 | |
|                         -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2 - hUp),
 | |
|                 QtCore.QPointF(
 | |
|                         -(ModelerGraphicItem.BOX_WIDTH + 2) / 2,
 | |
|                         -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2 - hUp)])
 | |
|         return pol
 | |
| 
 | |
| 
 | |
| class FlatButtonGraphicItem(QtGui.QGraphicsItem):
 | |
| 
 | |
|     WIDTH = 16
 | |
|     HEIGHT = 16
 | |
| 
 | |
|     def __init__(self, icon, position, action):
 | |
|         super(FlatButtonGraphicItem, self).__init__(None, None)
 | |
|         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
 | |
| 
 | |
|     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)
 | |
|         if self.isIn:
 | |
|             painter.setPen(QtGui.QPen(QtCore.Qt.transparent, 1))
 | |
|             painter.setBrush(QtGui.QBrush(QtCore.Qt.lightGray,
 | |
|                              QtCore.Qt.SolidPattern))
 | |
|         else:
 | |
|             painter.setPen(QtGui.QPen(QtCore.Qt.transparent, 1))
 | |
|             painter.setBrush(QtGui.QBrush(QtCore.Qt.transparent,
 | |
|                              QtCore.Qt.SolidPattern))
 | |
|         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
 | |
| 
 | |
|     def hoverEnterEvent(self, event):
 | |
|         self.isIn = True
 | |
|         self.update()
 | |
| 
 | |
|     def hoverLeaveEvent(self, event):
 | |
|         self.isIn = False
 | |
|         self.update()
 | |
| 
 | |
| 
 | |
| class FoldButtonGraphicItem(FlatButtonGraphicItem):
 | |
| 
 | |
|     WIDTH = 11
 | |
|     HEIGHT = 11
 | |
| 
 | |
|     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)
 | |
| 
 | |
|     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)
 |