Skip to content
Permalink
Browse files

Merge pull request #4675 from alexbruy/processing-help

[processing] improve help system
  • Loading branch information
alexbruy committed Jun 6, 2017
2 parents 51e7efe + e89502f commit 80911c6e74c1dabff04500508f9f28fb2ff4101c
@@ -44,7 +44,7 @@
from processing.gui.ResultsDock import ResultsDock
from processing.gui.AlgorithmLocatorFilter import AlgorithmLocatorFilter
from processing.modeler.ModelerDialog import ModelerDialog
from processing.tools.system import tempFolder
from processing.tools.system import tempFolder, tempHelpFolder
from processing.gui.menus import removeMenus, initializeMenus, createMenus
from processing.core.ProcessingResults import resultsList

@@ -146,6 +146,11 @@ def unload(self):
if QDir(folder).exists():
shutil.rmtree(folder, True)

# also delete temporary help files
folder = tempHelpFolder()
if QDir(folder).exists():
shutil.rmtree(folder, True)

self.iface.unregisterMainWindowAction(self.toolboxAction)
self.iface.unregisterMainWindowAction(self.modelerAction)
self.iface.unregisterMainWindowAction(self.historyAction)
@@ -80,19 +80,15 @@ def processAlgorithm(self, parameters, context, feedback):
commands[i] = c
GdalUtils.runGdal(commands, feedback)

def shortHelpString(self):
def helpUrl(self):
helpPath = GdalUtils.gdalHelpPath()
if helpPath == '':
return
return None

if os.path.exists(helpPath):
url = QUrl.fromLocalFile(os.path.join(helpPath, '{}.html'.format(self.commandName()))).toString()
return QUrl.fromLocalFile(os.path.join(helpPath, '{}.html'.format(self.commandName()))).toString()
else:
url = helpPath + '{}.html'.format(self.commandName())

return '''This algorithm is based on the GDAL {} module.
For more info, see the <a href={}> module help</a>
'''.format(self.commandName(), url)
return helpPath + '{}.html'.format(self.commandName())

def commandName(self):
parameters = {}
@@ -25,6 +25,9 @@

__revision__ = '$Format:%H$'

from builtins import str
from builtins import object

import os.path
import traceback
import subprocess
@@ -39,9 +42,8 @@
QgsProcessingUtils,
QgsProcessingParameterDefinition,
QgsMessageLog)
from qgis.gui import QgsHelp

from builtins import str
from builtins import object
from processing.core.ProcessingConfig import ProcessingConfig
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterRaster, ParameterVector, ParameterMultipleInput, ParameterTable, Parameter
@@ -390,3 +392,7 @@ def executeAlgorithm(alg, parameters, context=None, feedback=None, model=None):
# lines.append(traceback.format_exc())
#QgsMessageLog.logMessage('\n'.join(lines), self.tr('Processing'), QgsMessageLog.CRITICAL)
#raise GeoAlgorithmExecutionException(str(e) + self.tr('\nSee log for more details'), lines, e)

def helpUrl(self):
return QgsHelp.helpUrl("processing_algs/{}/{}".format(
self.provider().id(), self.id())).toString()
@@ -157,7 +157,7 @@ def checkExtentCRS(self):
return hasExtent and unmatchingCRS

def accept(self):
self.settings.setValue("/Processing/dialogBase", self.saveGeometry())
super(AlgorithmDialog, self)._saveGeometry()

context = dataobjects.createContext()

@@ -273,7 +273,7 @@ def finish(self, result, context):
self.tr('HTML output has been generated by this algorithm.'
'\nOpen the results dialog to check it.'))

def closeEvent(self, evt):
def closeEvent(self, event):
QgsProject.instance().layerWasAdded.disconnect(self.mainWidget.layerRegistryChanged)
QgsProject.instance().layersWillBeRemoved.disconnect(self.mainWidget.layerRegistryChanged)
super(AlgorithmDialog, self).closeEvent(evt)
super(AlgorithmDialog, self).closeEvent(event)
@@ -30,13 +30,11 @@
import webbrowser

from qgis.PyQt import uic
from qgis.PyQt.QtCore import QCoreApplication, QByteArray, QUrl
from qgis.PyQt.QtWidgets import QApplication, QDialogButtonBox
from qgis.PyQt.QtNetwork import QNetworkRequest, QNetworkReply
from qgis.PyQt.QtCore import Qt, QCoreApplication, QByteArray, QUrl
from qgis.PyQt.QtWidgets import QApplication, QDialogButtonBox, QVBoxLayout, QToolButton

from qgis.utils import iface
from qgis.core import (QgsNetworkAccessManager,
QgsProject,
from qgis.core import (QgsProject,
QgsProcessingFeedback,
QgsSettings)

@@ -82,34 +80,56 @@ def __init__(self, alg):
super(AlgorithmDialogBase, self).__init__(iface.mainWindow())
self.setupUi(self)

# don't collapse parameters panel
self.splitter.setCollapsible(0, False)

# add collapse button to splitter
splitterHandle = self.splitter.handle(1)
handleLayout = QVBoxLayout()
handleLayout.setContentsMargins(0, 0, 0, 0)
self.btnCollapse = QToolButton(splitterHandle)
self.btnCollapse.setAutoRaise(True)
self.btnCollapse.setFixedSize(12, 12)
self.btnCollapse.setCursor(Qt.ArrowCursor)
handleLayout.addWidget(self.btnCollapse)
handleLayout.addStretch()
splitterHandle.setLayout(handleLayout)

self.feedback = AlgorithmDialogFeedback(self)
self.feedback.progressChanged.connect(self.setPercentage)
self.buttonCancel.clicked.connect(self.feedback.cancel)

self.settings = QgsSettings()
self.splitter.restoreState(self.settings.value("/Processing/dialogBaseSplitter", QByteArray()))
self.restoreGeometry(self.settings.value("/Processing/dialogBase", QByteArray()))
self.splitterState = self.splitter.saveState()
self.splitterChanged(0, 0)

self.executed = False
self.mainWidget = None
self.alg = alg

self.setWindowTitle(self.alg.displayName())

# Rename OK button to Run
self.btnRun = self.buttonBox.button(QDialogButtonBox.Ok)
self.btnRun.setText(self.tr('Run'))

self.buttonCancel.setEnabled(False)

self.btnClose = self.buttonBox.button(QDialogButtonBox.Close)
self.buttonBox.helpRequested.connect(self.openHelp)

self.setWindowTitle(self.alg.displayName())
self.btnCollapse.clicked.connect(self.toggleCollapsed)
self.splitter.splitterMoved.connect(self.splitterChanged)

# desktop = QDesktopWidget()
# if desktop.physicalDpiX() > 96:
# self.txtHelp.setZoomFactor(desktop.physicalDpiX() / 96)

algHelp = self.formatHelp(self.alg)
if algHelp is None:
self.textShortHelp.setVisible(False)
self.textShortHelp.hide()
else:
self.textShortHelp.document().setDefaultStyleSheet('''.summary { margin-left: 10px; margin-right: 10px; }
h2 { color: #555555; padding-bottom: 15px; }
@@ -119,30 +139,11 @@ def __init__(self, alg):
dl dd { margin-bottom: 5px; }''')
self.textShortHelp.setHtml(algHelp)

self.textShortHelp.setOpenLinks(False)

def linkClicked(url):
webbrowser.open(url.toString())

self.textShortHelp.anchorClicked.connect(linkClicked)

if self.alg.helpString() is not None:
try:
self.txtHelp.setHtml(self.alg.helpString())
except Exception:
self.tabWidget.removeTab(2)
elif self.alg.helpUrl() is not None:
try:
html = self.tr('<p>Downloading algorithm help... Please wait.</p>')
self.txtHelp.setHtml(html)
rq = QNetworkRequest(QUrl(self.alg.helpUrl()))
self.reply = QgsNetworkAccessManager.instance().get(rq)
self.reply.finished.connect(self.requestFinished)
except Exception:
self.tabWidget.removeTab(2)
else:
self.tabWidget.removeTab(2)

self.showDebug = ProcessingConfig.getSetting(
ProcessingConfig.SHOW_DEBUG_IN_DIALOG)

@@ -152,19 +153,9 @@ def formatHelp(self, alg):
return None
return "<h2>%s</h2>%s" % (alg.displayName(), "".join(["<p>%s</p>" % s for s in text.split("\n")]))

def requestFinished(self):
"""Change the webview HTML content"""
reply = self.sender()
if reply.error() != QNetworkReply.NoError:
html = self.tr('<h2>No help available for this algorithm</h2><p>{}</p>'.format(reply.errorString()))
else:
html = str(reply.readAll())
reply.deleteLater()
self.txtHelp.setHtml(html)

def closeEvent(self, evt):
self.settings.setValue("/Processing/dialogBase", self.saveGeometry())
super(AlgorithmDialogBase, self).closeEvent(evt)
def closeEvent(self, event):
self._saveGeometry()
super(AlgorithmDialogBase, self).closeEvent(event)

def setMainWidget(self, widget):
if self.mainWidget is not None:
@@ -228,9 +219,40 @@ def getParamValues(self):
def accept(self):
pass

def reject(self):
self._saveGeometry()
super(AlgorithmDialogBase, self).reject()

def finish(self, context):
pass

def toggleCollapsed(self):
if self.helpCollapsed:
self.splitter.restoreState(self.splitterState)
self.btnCollapse.setArrowType(Qt.RightArrow)
else:
self.splitterState = self.splitter.saveState()
self.splitter.setSizes([1, 0])
self.btnCollapse.setArrowType(Qt.LeftArrow)
self.helpCollapsed = not self.helpCollapsed

def splitterChanged(self, pos, index):
if self.splitter.sizes()[1] == 0:
self.helpCollapsed = True
self.btnCollapse.setArrowType(Qt.LeftArrow)
else:
self.helpCollapsed = False
self.btnCollapse.setArrowType(Qt.RightArrow)

def openHelp(self):
algHelp = self.alg.helpUrl()
if algHelp not in [None, ""]:
webbrowser.open(algHelp)

def _saveGeometry(self):
self.settings.setValue("/Processing/dialogBaseSplitter", self.splitter.saveState())
self.settings.setValue("/Processing/dialogBase", self.saveGeometry())

class InvalidParameterValue(Exception):

def __init__(self, param, widget):
@@ -27,11 +27,14 @@

__revision__ = '$Format:%H$'

from qgis.PyQt.QtCore import QCoreApplication
import os
import re
import json

from qgis.PyQt.QtCore import QCoreApplication, QUrl

from processing.tools import system

ALG_DESC = 'ALG_DESC'
ALG_CREATOR = 'ALG_CREATOR'
ALG_HELP_CREATOR = 'ALG_HELP_CREATOR'
@@ -63,7 +66,13 @@ def getHtmlFromHelpFile(alg, helpFile):
try:
with open(helpFile) as f:
descriptions = json.load(f)
return getHtmlFromDescriptionsDict(alg, descriptions)

content = getHtmlFromDescriptionsDict(alg, descriptions)
algGroup, algName = alg.id().split(':')
filePath = os.path.join(system.tempHelpFolder(), "{}_{}.html".format(algGroup, algName))
with open(filePath, 'w', encoding='utf-8') as f:
f.write(content)
return QUrl.fromLocalFile(filePath).toString()
except:
return None

@@ -539,7 +539,7 @@ def updateModelerView(self):
if self.modelerdialog:
self.modelerdialog.repaintModel()

def helpString(self):
def helpUrl(self):
try:
return getHtmlFromDescriptionsDict(self, self.helpContent)
except:
@@ -27,15 +27,14 @@

__revision__ = '$Format:%H$'

import webbrowser

from qgis.PyQt.QtCore import Qt, QUrl, QMetaObject
from qgis.PyQt.QtWidgets import (QDialog, QDialogButtonBox, QLabel, QLineEdit,
QFrame, QPushButton, QSizePolicy, QVBoxLayout,
QHBoxLayout, QTabWidget, QWidget,
QTextBrowser)
from qgis.PyQt.QtNetwork import QNetworkRequest, QNetworkReply
QHBoxLayout, QWidget)

from qgis.core import (QgsNetworkAccessManager,
QgsProcessingParameterDefinition)
from qgis.core import (QgsProcessingParameterDefinition)

from qgis.gui import (QgsMessageBar,
QgsScrollArea)
@@ -89,7 +88,8 @@ def setupUi(self):
self.buttonBox = QDialogButtonBox()
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel |
QDialogButtonBox.Ok)
+ QDialogButtonBox.Ok |
+ QDialogButtonBox.Help)
self.setSizePolicy(QSizePolicy.Expanding,
QSizePolicy.Expanding)
self.verticalLayout = QVBoxLayout()
@@ -182,53 +182,24 @@ def setupUi(self):
self.verticalLayout2 = QVBoxLayout()
self.verticalLayout2.setSpacing(2)
self.verticalLayout2.setMargin(0)
self.tabWidget = QTabWidget()
self.tabWidget.setMinimumWidth(300)

self.paramPanel = QWidget()
self.paramPanel.setLayout(self.verticalLayout)
self.scrollArea = QgsScrollArea()
self.scrollArea.setWidget(self.paramPanel)
self.scrollArea.setWidgetResizable(True)
self.tabWidget.addTab(self.scrollArea, self.tr('Parameters'))

self.txtHelp = QTextBrowser()

html = None
isText, algHelp = self._alg.help()
if algHelp is not None:
algHelp = algHelp if isText else QUrl(algHelp)
try:
if isText:
self.txtHelp.setHtml(algHelp)
else:
html = self.tr('<p>Downloading algorithm help... Please wait.</p>')
self.txtHelp.setHtml(html)
self.tabWidget.addTab(self.txtHelp, 'Help')
self.reply = QgsNetworkAccessManager.instance().get(QNetworkRequest(algHelp))
self.reply.finished.connect(self.requestFinished)
except:
pass

self.verticalLayout2.addWidget(self.tabWidget)
self.verticalLayout2.addWidget(self.scrollArea)
self.verticalLayout2.addWidget(self.buttonBox)
self.setLayout(self.verticalLayout2)
self.buttonBox.accepted.connect(self.okPressed)
self.buttonBox.rejected.connect(self.cancelPressed)
self.buttonBox.helpRequested.connect(self.openHelp)
QMetaObject.connectSlotsByName(self)

for wrapper in list(self.wrappers.values()):
wrapper.postInitialize(list(self.wrappers.values()))

def requestFinished(self):
"""Change the webview HTML content"""
reply = self.sender()
if reply.error() != QNetworkReply.NoError:
html = self.tr('<h2>No help available for this algorithm</h2><p>{}</p>'.format(reply.errorString()))
else:
html = str(reply.readAll())
reply.deleteLater()
self.txtHelp.setHtml(html)

def getAvailableDependencies(self): # spellok
if self._algName is None:
dependent = []
@@ -353,3 +324,8 @@ def okPressed(self):
def cancelPressed(self):
self.alg = None
self.close()

def openHelp(self):
algHelp = self._alg.help()
if algHelp is not None:
webbrowser.open(algHelp)

0 comments on commit 80911c6

Please sign in to comment.
You can’t perform that action at this time.