2012-12-08 19:42:43 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
|
|
|
***************************************************************************
|
|
|
|
EditScriptDialog.py
|
|
|
|
---------------------
|
|
|
|
Date : December 2012
|
|
|
|
Copyright : (C) 2012 by Alexander Bruy
|
|
|
|
Email : alexander dot bruy 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. *
|
|
|
|
* *
|
|
|
|
***************************************************************************
|
|
|
|
"""
|
|
|
|
|
2015-01-22 13:06:39 +01:00
|
|
|
|
2012-12-08 19:42:43 +02:00
|
|
|
__author__ = 'Alexander Bruy'
|
|
|
|
__date__ = 'December 2012'
|
|
|
|
__copyright__ = '(C) 2012, Alexander Bruy'
|
2013-10-01 20:52:22 +03:00
|
|
|
|
2012-12-08 19:42:43 +02:00
|
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
2013-10-01 20:52:22 +03:00
|
|
|
|
2012-12-08 19:42:43 +02:00
|
|
|
__revision__ = '$Format:%H$'
|
|
|
|
|
2014-01-11 15:01:15 +01:00
|
|
|
import codecs
|
|
|
|
import sys
|
2014-05-31 18:53:43 +02:00
|
|
|
import json
|
2014-11-21 15:01:23 +01:00
|
|
|
import os
|
2012-12-08 19:42:43 +02:00
|
|
|
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
from PyQt4.QtCore import Qt
|
|
|
|
from PyQt4.QtGui import QDialog, QIcon, QMenu, QAction, QCursor, QMessageBox, QFileDialog, QApplication
|
2012-12-08 19:42:43 +02:00
|
|
|
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
from qgis.core import QgsApplication
|
2014-05-20 16:30:59 +02:00
|
|
|
from qgis.utils import iface
|
2012-12-08 19:42:43 +02:00
|
|
|
|
2014-11-21 15:01:23 +01:00
|
|
|
from processing.modeler.ModelerUtils import ModelerUtils
|
2014-11-12 11:42:29 +02:00
|
|
|
from processing.gui.AlgorithmDialog import AlgorithmDialog
|
2012-12-08 19:42:43 +02:00
|
|
|
from processing.gui.HelpEditionDialog import HelpEditionDialog
|
2014-04-17 01:41:25 +02:00
|
|
|
from processing.algs.r.RAlgorithm import RAlgorithm
|
|
|
|
from processing.algs.r.RUtils import RUtils
|
2012-12-08 19:42:43 +02:00
|
|
|
from processing.script.ScriptAlgorithm import ScriptAlgorithm
|
2015-01-22 13:06:39 +01:00
|
|
|
from processing.script.ScriptUtils import ScriptUtils
|
2012-12-08 19:42:43 +02:00
|
|
|
from processing.ui.ui_DlgScriptEditor import Ui_DlgScriptEditor
|
|
|
|
|
2015-02-03 11:22:34 +02:00
|
|
|
import processing.resources_rc
|
2012-12-08 19:42:43 +02:00
|
|
|
|
2013-10-01 20:52:22 +03:00
|
|
|
|
2012-12-08 19:42:43 +02:00
|
|
|
class ScriptEditorDialog(QDialog, Ui_DlgScriptEditor):
|
|
|
|
|
|
|
|
SCRIPT_PYTHON = 0
|
|
|
|
SCRIPT_R = 1
|
2014-05-02 10:32:37 +02:00
|
|
|
|
2014-04-24 17:26:00 +02:00
|
|
|
hasChanged = False
|
2012-12-08 19:42:43 +02:00
|
|
|
|
|
|
|
def __init__(self, algType, alg):
|
|
|
|
QDialog.__init__(self)
|
|
|
|
self.setupUi(self)
|
|
|
|
|
2013-12-05 09:45:49 +02:00
|
|
|
self.setWindowFlags(Qt.WindowMinimizeButtonHint |
|
|
|
|
Qt.WindowMaximizeButtonHint |
|
|
|
|
Qt.WindowCloseButtonHint)
|
2013-10-01 20:52:22 +03:00
|
|
|
# Set icons
|
2014-11-19 11:31:54 +02:00
|
|
|
self.btnOpen.setIcon(
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
QgsApplication.getThemeIcon('/mActionFileOpen.svg'))
|
2013-10-01 20:52:22 +03:00
|
|
|
self.btnSave.setIcon(
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
QgsApplication.getThemeIcon('/mActionFileSave.svg'))
|
2013-10-01 20:52:22 +03:00
|
|
|
self.btnSaveAs.setIcon(
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
QgsApplication.getThemeIcon('/mActionFileSaveAs.svg'))
|
2013-10-01 20:52:22 +03:00
|
|
|
self.btnEditHelp.setIcon(QIcon(':/processing/images/edithelp.png'))
|
|
|
|
self.btnRun.setIcon(QIcon(':/processing/images/runalgorithm.png'))
|
|
|
|
self.btnCut.setIcon(QgsApplication.getThemeIcon('/mActionEditCut.png'))
|
|
|
|
self.btnCopy.setIcon(
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
QgsApplication.getThemeIcon('/mActionEditCopy.png'))
|
2013-10-01 20:52:22 +03:00
|
|
|
self.btnPaste.setIcon(
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
QgsApplication.getThemeIcon('/mActionEditPaste.png'))
|
2013-10-01 20:52:22 +03:00
|
|
|
self.btnUndo.setIcon(QgsApplication.getThemeIcon('/mActionUndo.png'))
|
|
|
|
self.btnRedo.setIcon(QgsApplication.getThemeIcon('/mActionRedo.png'))
|
2014-11-21 15:01:23 +01:00
|
|
|
self.btnSnippets.setIcon(QgsApplication.getThemeIcon('/mActionHelpAPI.png'))
|
2013-10-01 20:52:22 +03:00
|
|
|
|
|
|
|
# Connect signals and slots
|
2014-11-19 11:31:54 +02:00
|
|
|
self.btnOpen.clicked.connect(self.openScript)
|
2012-12-08 19:42:43 +02:00
|
|
|
self.btnSave.clicked.connect(self.save)
|
|
|
|
self.btnSaveAs.clicked.connect(self.saveAs)
|
|
|
|
self.btnEditHelp.clicked.connect(self.editHelp)
|
|
|
|
self.btnRun.clicked.connect(self.runAlgorithm)
|
2014-11-21 15:01:23 +01:00
|
|
|
self.btnSnippets.clicked.connect(self.showSnippets)
|
2012-12-08 19:42:43 +02:00
|
|
|
self.btnCut.clicked.connect(self.editor.cut)
|
|
|
|
self.btnCopy.clicked.connect(self.editor.copy)
|
|
|
|
self.btnPaste.clicked.connect(self.editor.paste)
|
|
|
|
self.btnUndo.clicked.connect(self.editor.undo)
|
|
|
|
self.btnRedo.clicked.connect(self.editor.redo)
|
2014-04-24 17:26:00 +02:00
|
|
|
self.editor.textChanged.connect(lambda: self.setHasChanged(True))
|
2012-12-08 19:42:43 +02:00
|
|
|
|
|
|
|
self.alg = alg
|
|
|
|
self.algType = algType
|
|
|
|
|
2014-11-21 15:01:23 +01:00
|
|
|
self.snippets = {}
|
|
|
|
if self.algType == self.SCRIPT_PYTHON:
|
2015-01-22 13:06:39 +01:00
|
|
|
path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "script", "snippets.py")
|
2014-11-21 15:01:23 +01:00
|
|
|
with open(path) as f:
|
|
|
|
lines = f.readlines()
|
|
|
|
snippetlines = []
|
|
|
|
name = None
|
|
|
|
for line in lines:
|
|
|
|
if line.startswith("##"):
|
|
|
|
if snippetlines:
|
|
|
|
self.snippets[name] = "".join(snippetlines)
|
|
|
|
name = line[2:]
|
|
|
|
snippetlines = []
|
|
|
|
else:
|
|
|
|
snippetlines.append(line)
|
|
|
|
if snippetlines:
|
|
|
|
self.snippets[name] = "".join(snippetlines)
|
|
|
|
|
|
|
|
if not self.snippets:
|
|
|
|
self.btnSnippets.setVisible(False)
|
|
|
|
|
2012-12-08 19:42:43 +02:00
|
|
|
if self.alg is not None:
|
|
|
|
self.filename = self.alg.descriptionFile
|
|
|
|
self.editor.setText(self.alg.script)
|
|
|
|
else:
|
|
|
|
self.filename = None
|
|
|
|
|
|
|
|
self.update = False
|
|
|
|
self.help = None
|
2014-05-02 10:32:37 +02:00
|
|
|
|
2014-04-24 17:26:00 +02:00
|
|
|
self.setHasChanged(False)
|
2012-12-08 19:42:43 +02:00
|
|
|
|
|
|
|
self.editor.setLexerType(self.algType)
|
|
|
|
|
2014-11-21 15:01:23 +01:00
|
|
|
def showSnippets(self, evt):
|
|
|
|
popupmenu = QMenu()
|
|
|
|
for name, snippet in self.snippets.iteritems():
|
|
|
|
action = QAction(self.tr(name), self.btnSnippets)
|
|
|
|
action.triggered[()].connect(lambda snippet=snippet: self.editor.insert(snippet))
|
|
|
|
popupmenu.addAction(action)
|
|
|
|
popupmenu.exec_(QCursor.pos())
|
|
|
|
|
2014-11-19 11:31:54 +02:00
|
|
|
def closeEvent(self, evt):
|
|
|
|
if self.hasChanged:
|
|
|
|
ret = QMessageBox.question(self, self.tr('Unsaved changes'),
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
self.tr('There are unsaved changes in script. Continue?'),
|
|
|
|
QMessageBox.Yes | QMessageBox.No, QMessageBox.No
|
|
|
|
)
|
2014-11-19 11:31:54 +02:00
|
|
|
if ret == QMessageBox.Yes:
|
|
|
|
evt.accept()
|
|
|
|
else:
|
|
|
|
evt.ignore()
|
|
|
|
else:
|
|
|
|
evt.accept()
|
|
|
|
|
2012-12-08 19:42:43 +02:00
|
|
|
def editHelp(self):
|
|
|
|
if self.alg is None:
|
|
|
|
if self.algType == self.SCRIPT_PYTHON:
|
2014-05-05 16:27:07 +03:00
|
|
|
alg = ScriptAlgorithm(None, unicode(self.editor.text()))
|
2012-12-08 19:42:43 +02:00
|
|
|
elif self.algType == self.SCRIPT_R:
|
2014-05-05 16:27:07 +03:00
|
|
|
alg = RAlgorithm(None, unicode(self.editor.text()))
|
2012-12-08 19:42:43 +02:00
|
|
|
else:
|
|
|
|
alg = self.alg
|
|
|
|
|
|
|
|
dlg = HelpEditionDialog(alg)
|
|
|
|
dlg.exec_()
|
|
|
|
|
2013-10-01 20:52:22 +03:00
|
|
|
# We store the description string in case there were not saved
|
|
|
|
# because there was no filename defined yet
|
2012-12-08 19:42:43 +02:00
|
|
|
if self.alg is None and dlg.descriptions:
|
|
|
|
self.help = dlg.descriptions
|
|
|
|
|
2014-11-19 11:31:54 +02:00
|
|
|
def openScript(self):
|
|
|
|
if self.hasChanged:
|
|
|
|
ret = QMessageBox.warning(self, self.tr('Unsaved changes'),
|
|
|
|
self.tr('There are unsaved changes in script. Continue?'),
|
|
|
|
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
|
|
|
if ret == QMessageBox.No:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.algType == self.SCRIPT_PYTHON:
|
|
|
|
scriptDir = ScriptUtils.scriptsFolder()
|
|
|
|
filterName = self.tr('Python scripts (*.py)')
|
|
|
|
elif self.algType == self.SCRIPT_R:
|
|
|
|
scriptDir = RUtils.RScriptsFolder()
|
|
|
|
filterName = self.tr('Processing R script (*.rsx)')
|
|
|
|
|
|
|
|
self.filename = QFileDialog.getOpenFileName(
|
|
|
|
self, self.tr('Save script'), scriptDir, filterName)
|
|
|
|
|
|
|
|
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
|
|
|
|
with codecs.open(self.filename, 'r', encoding='utf-8') as f:
|
|
|
|
txt = f.read()
|
|
|
|
|
|
|
|
self.editor.setText(txt)
|
|
|
|
self.hasChanged = False
|
|
|
|
self.editor.setModified(False)
|
|
|
|
self.editor.recolor()
|
|
|
|
QApplication.restoreOverrideCursor()
|
|
|
|
|
2012-12-08 19:42:43 +02:00
|
|
|
def save(self):
|
|
|
|
self.saveScript(False)
|
|
|
|
|
|
|
|
def saveAs(self):
|
|
|
|
self.saveScript(True)
|
|
|
|
|
|
|
|
def saveScript(self, saveAs):
|
|
|
|
if self.filename is None or saveAs:
|
|
|
|
if self.algType == self.SCRIPT_PYTHON:
|
|
|
|
scriptDir = ScriptUtils.scriptsFolder()
|
2013-10-01 20:52:22 +03:00
|
|
|
filterName = self.tr('Python scripts (*.py)')
|
2012-12-08 19:42:43 +02:00
|
|
|
elif self.algType == self.SCRIPT_R:
|
|
|
|
scriptDir = RUtils.RScriptsFolder()
|
2013-10-01 20:52:22 +03:00
|
|
|
filterName = self.tr('Processing R script (*.rsx)')
|
2012-12-08 19:42:43 +02:00
|
|
|
|
|
|
|
self.filename = unicode(QFileDialog.getSaveFileName(self,
|
2013-10-01 20:52:22 +03:00
|
|
|
self.tr('Save script'), scriptDir,
|
|
|
|
filterName))
|
2012-12-08 19:42:43 +02:00
|
|
|
|
|
|
|
if self.filename:
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
if self.algType == self.SCRIPT_PYTHON and \
|
|
|
|
not self.filename.lower().endswith('.py'):
|
2013-10-01 20:52:22 +03:00
|
|
|
self.filename += '.py'
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
if self.algType == self.SCRIPT_R and \
|
|
|
|
not self.filename.lower().endswith('.rsx'):
|
2013-10-01 20:52:22 +03:00
|
|
|
self.filename += '.rsx'
|
2012-12-08 19:42:43 +02:00
|
|
|
|
|
|
|
text = unicode(self.editor.text())
|
|
|
|
if self.alg is not None:
|
|
|
|
self.alg.script = text
|
|
|
|
try:
|
2014-01-11 15:01:15 +01:00
|
|
|
with codecs.open(self.filename, 'w', encoding='utf-8') as fout:
|
|
|
|
fout.write(text)
|
2012-12-08 19:42:43 +02:00
|
|
|
except IOError:
|
2013-10-01 20:52:22 +03:00
|
|
|
QMessageBox.warning(self, self.tr('I/O error'),
|
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
|
|
|
self.tr('Unable to save edits. Reason:\n %s')
|
|
|
|
% unicode(sys.exc_info()[1])
|
|
|
|
)
|
2012-12-08 19:42:43 +02:00
|
|
|
return
|
|
|
|
self.update = True
|
|
|
|
|
2013-10-01 20:52:22 +03:00
|
|
|
# If help strings were defined before saving the script for
|
|
|
|
# the first time, we do it here
|
2012-12-08 19:42:43 +02:00
|
|
|
if self.help:
|
2014-05-31 18:53:43 +02:00
|
|
|
with open(self.filename + '.help', 'w') as f:
|
2014-06-13 09:03:15 +02:00
|
|
|
json.dump(self.help, f)
|
2012-12-08 19:42:43 +02:00
|
|
|
self.help = None
|
2014-04-24 17:26:00 +02:00
|
|
|
self.setHasChanged(False)
|
2012-12-08 19:42:43 +02:00
|
|
|
else:
|
|
|
|
self.filename = None
|
|
|
|
|
2014-04-24 17:26:00 +02:00
|
|
|
def setHasChanged(self, hasChanged):
|
|
|
|
self.hasChanged = hasChanged
|
|
|
|
self.btnSave.setEnabled(hasChanged)
|
2014-05-02 10:32:37 +02:00
|
|
|
|
2012-12-08 19:42:43 +02:00
|
|
|
def runAlgorithm(self):
|
|
|
|
if self.algType == self.SCRIPT_PYTHON:
|
|
|
|
alg = ScriptAlgorithm(None, unicode(self.editor.text()))
|
2014-06-08 00:21:12 +02:00
|
|
|
alg.provider = ModelerUtils.providers['script']
|
2012-12-08 19:42:43 +02:00
|
|
|
if self.algType == self.SCRIPT_R:
|
|
|
|
alg = RAlgorithm(None, unicode(self.editor.text()))
|
2014-06-08 00:21:12 +02:00
|
|
|
alg.provider = ModelerUtils.providers['r']
|
2012-12-08 19:42:43 +02:00
|
|
|
|
|
|
|
dlg = alg.getCustomParametersDialog()
|
|
|
|
if not dlg:
|
2014-11-12 11:42:29 +02:00
|
|
|
dlg = AlgorithmDialog(alg)
|
2012-12-08 19:42:43 +02:00
|
|
|
|
2014-05-20 16:30:59 +02:00
|
|
|
canvas = iface.mapCanvas()
|
2012-12-08 19:42:43 +02:00
|
|
|
prevMapTool = canvas.mapTool()
|
|
|
|
|
|
|
|
dlg.show()
|
|
|
|
dlg.exec_()
|
|
|
|
|
|
|
|
if canvas.mapTool() != prevMapTool:
|
|
|
|
try:
|
|
|
|
canvas.mapTool().reset()
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
canvas.setMapTool(prevMapTool)
|