class LessonFinishedDialog(QDialog):

    def __init__(self, lesson):
        super(LessonFinishedDialog, self).__init__(iface.mainWindow())
        self.reopen = False
        self.lesson = lesson
        self.setWindowTitle("Lesson finished")
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setSpacing(10)
        self.verticalLayout.setMargin(10)
        txt = "<p>Congratulations! You have correctly finished this lesson.</p>"

        if lesson.nextLessons:
            txt += "<p>We recommend you the following lessons to continue:</p><ul>"
            for i, nextLesson in enumerate(lesson.nextLessons):
                txt+="<li><a href='%i'>%s</a>" % (i, nextLesson[1])

        txt += "</ul><p>If you don't want to run more lessons, just <a href='exit'>close this dialog.</a></p>"
        txt += "<p>If you want to run another lesson, click <a href='reopen'>here</a> to reopen the lesson selector</p>"

        self.text = QTextBrowser()
        self.text.anchorClicked.connect(self.linkClicked)
        self.text.setHtml(txt)
        self.text.setOpenLinks(False)
        self.verticalLayout.addWidget(self.text)
        self.setLayout(self.verticalLayout)
        self.resize(400, 300)
        self.nextLesson = None

    def linkClicked(self, url):
        if url.path() not in ["exit", "reopen"]:
            self.nextLesson = lessonFromName(*self.lesson.nextLessons[int(url.path())])
        if url.path() =="reopen":
            self.reopen = True
        self.close()
class SessionFinishedDialog(QDialog):
    def __init__(self, session):
        super(SessionFinishedDialog, self).__init__(iface.mainWindow())
        self.reopen = False
        self.session = session
        self.setWindowTitle("Session finished")
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setSpacing(10)
        self.verticalLayout.setMargin(10)
        txt = "<p>Congratulations! You have correctly finished this session.</p>"

        if session.nextSessions:
            txt += "<p>We recommend you the following sessions to continue:</p><ul>"
            for i, nextSession in enumerate(session.nextSessions):
                txt += "<li><a href='%i'>%s</a>" % (i, nextSession[1])

        txt += "</ul><p>If you don't want to run more sessions, just <a href='exit'>close this dialog.</a></p>"
        txt += "<p>If you want to run another session, click <a href='reopen'>here</a> to reopen the session selector</p>"

        self.text = QTextBrowser()
        self.text.anchorClicked.connect(self.linkClicked)
        self.text.setHtml(txt)
        self.text.setOpenLinks(False)
        self.verticalLayout.addWidget(self.text)
        self.setLayout(self.verticalLayout)
        self.resize(400, 300)
        self.nextSession = None

    def linkClicked(self, url):
        if url.path() not in ["exit", "reopen"]:
            self.nextSession = sessionFromName(
                *self.session.nextSessions[int(url.path())])
        if url.path() == "reopen":
            self.reopen = True
        self.close()
 def __init__(self, pt):
     super().__init__(iface.mainWindow())
     self.pt = pt
     layout = QVBoxLayout()
     textbrowser = QTextBrowser()
     textbrowser.setOpenLinks(False)
     textbrowser.setOpenExternalLinks(False)
     textbrowser.anchorClicked.connect(self._link_clicked)
     url = (
         "https://learn.planet.com/sample-skysat.html?utm_source=defense-and-intelligence&amp;"
         "amp;utm_medium=website&amp;amp;utm_campaign=skysat-sample-imagery&amp;amp;"
         "utm_content=skysat-sample-imagery")
     text = f"""<p><strong>Complete your high resolution imagery order</strong></p>
             <p><br/>Your custom high resolution imagery order can be completed using
             Planet&rsquo;s Tasking Dashboard. The dashboard allows you to place an order
             to task our SkySat satellite and get high-resolution imagery for your area
             and time of interest.</p>
             <p>If you have not yet purchased the ability to order high-resolution imagery,
             you may download samples<a href="{url}"> here</a>
             and contact our sales team <a href="https://www.planet.com/contact-sales/">
             here</a>.</p>
             <p>&nbsp;</p>
             <p"><a href="dashboard">Take me to the Tasking Dashboard</a>&nbsp;</p>"""
     textbrowser.setHtml(text)
     layout.addWidget(textbrowser)
     self.setLayout(layout)
     self.setFixedSize(600, 400)
示例#4
0
    def tabChanged(self, tabIdx):
        THRESHOLD = 1500

        if tabIdx != 2:  # the full changed view
            return
        if self.fullHistory is not None:
            return
        if not self.fullDiffSummary:
            return
        if self.fullDiffSummaryTotal > THRESHOLD:
            text = QTextBrowser()
            text.setHtml(
                "<br><br><br><center><font size=+3><b>Too many changes to show - {:,} features changed</b></font>"
                .format(self.fullDiffSummaryTotal))
            layout = self.tabWidget.widget(2).layout()
            self.clearLayout(layout)
            #layout.setMargin(0)
            layout.addWidget(text)
            self.fullHistory = layout
            #self.tabWidget.widget(2).setLayout(layout)
            return
        # need to set it up
        layout = self.tabWidget.widget(2).layout()
        self.clearLayout(layout)

        #layout.setMargin(0)
        pr = self.prs[self.comboPr.currentText()]
        prid = pr["id"]
        total, diffsummary = self.server.diffSummaryPR(self.user, self.repo,
                                                       prid)
        if total is None:
            text = QTextBrowser()
            text.setHtml(
                "<br><br><br><center><font size=+3><b>PR is in Conflict - Please Synchronize</b></font>"
                .format(self.fullDiffSummaryTotal))
            layout = self.tabWidget.widget(2).layout()
            #layout.setMargin(0)
            self.clearLayout(layout)

            layout.addWidget(text)
            self.fullHistory = layout

            #self.tabWidget.widget(2).setLayout(layout)
            return
        layers = [l["path"] for l in diffsummary.values()]

        diffs = {
            layer: execute(lambda: list(
                self.server.diffPR(self.user, self.repo, layer, prid)[1]))
            for layer in layers
        }

        fullDiffView = DiffViewerWidget(diffs)
        fullDiffView.selectFirstChangedFeature()
        layout.addWidget(fullDiffView)
        self.fullHistory = layout
示例#5
0
class Info(QDialog):
    info = []

    @classmethod
    def showInfo(cls, plugin, html, gmlid, parent):
        info = Info(plugin, html, gmlid, parent)
        info.setAttribute(Qt.WA_DeleteOnClose)
        info.setModal(False)

        cls.info.append(info)

        info.show()

    def __init__(self, plugin, html, gmlid, parent):
        QDialog.__init__(self, parent)
        self.resize(QSize(740, 580))
        self.setWindowTitle(u"Flurstücksnachweis")

        self.plugin = plugin
        self.gmlid = gmlid

        self.tbEigentuemer = QTextBrowser(self)
        self.tbEigentuemer.setHtml(html)
        self.tbEigentuemer.setContextMenuPolicy(Qt.NoContextMenu)

        layout = QVBoxLayout(self)
        layout.addWidget(self.tbEigentuemer)
        self.setLayout(layout)

        self.restoreGeometry(QSettings("norBIT", "norGIS-ALKIS-Erweiterung").value("infogeom", QByteArray(), type=QByteArray))
        self.move(self.pos() + QPoint(16, 16) * len(self.info))

    def print_(self):
        printer = QPrinter()
        dlg = QPrintDialog(printer)
        if dlg.exec_() == QDialog.Accepted:
            self.tbEigentuemer.print_(printer)

    def contextMenuEvent(self, e):
        menu = QMenu(self)
        action = QAction("Drucken", self)
        action.triggered.connect(self.print_)
        menu.addAction(action)
        menu.exec_(e.globalPos())

    def closeEvent(self, e):
        QSettings("norBIT", "norGIS-ALKIS-Erweiterung").setValue("infogeom", self.saveGeometry())
        self.info.remove(self)
        self.plugin.clearHighlight()
        QDialog.closeEvent(self, e)

    def event(self, e):
        if e.type() == QEvent.WindowActivate:
            self.plugin.highlight(where="gml_id='{}'".format(self.gmlid))
        return QDialog.event(self, e)
示例#6
0
class Info(QDialog):
    info = []

    @classmethod
    def showInfo(cls, plugin, html, gmlid, parent):
        info = Info(plugin, html, gmlid, parent)
        info.setAttribute(Qt.WA_DeleteOnClose)
        info.setModal(False)

        cls.info.append(info)

        info.show()

    def __init__(self, plugin, html, gmlid, parent):
        QDialog.__init__(self, parent)
        self.resize(QSize(740, 580))
        self.setWindowTitle(u"Flurstücksnachweis")

        self.plugin = plugin
        self.gmlid = gmlid

        self.tbEigentuemer = QTextBrowser(self)
        self.tbEigentuemer.setHtml(html)
        self.tbEigentuemer.setContextMenuPolicy(Qt.NoContextMenu)

        layout = QVBoxLayout(self)
        layout.addWidget(self.tbEigentuemer)
        self.setLayout(layout)

        self.restoreGeometry(QSettings("norBIT", "norGIS-ALKIS-Erweiterung").value("infogeom", QByteArray(), type=QByteArray))
        self.move(self.pos() + QPoint(16, 16) * (len(self.info) - 1))

    def print_(self):
        printer = QPrinter()
        dlg = QPrintDialog(printer)
        if dlg.exec_() == QDialog.Accepted:
            self.tbEigentuemer.print_(printer)

    def contextMenuEvent(self, e):
        menu = QMenu(self)
        action = QAction("Drucken", self)
        action.triggered.connect(self.print_)
        menu.addAction(action)
        menu.exec_(e.globalPos())

    def closeEvent(self, e):
        QSettings("norBIT", "norGIS-ALKIS-Erweiterung").setValue("infogeom", self.saveGeometry())
        QDialog.closeEvent(self, e)
        self.info.remove(self)

    def event(self, e):
        if e.type() == QEvent.WindowActivate:
            self.plugin.highlight(where="gml_id='{}'".format(self.gmlid))
        return QDialog.event(self, e)
示例#7
0
class HtmlDialog(QDialog):
    def __init__(self, title, content, parent=None):
        super(HtmlDialog, self).__init__(parent)
        self.setWindowTitle(title)
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setSpacing(10)
        self.verticalLayout.setMargin(10)
        self.text = QTextBrowser()
        self.text.setHtml(content)
        self.verticalLayout.addWidget(self.text)
        self.setLayout(self.verticalLayout)
        self.resize(400, 400)
示例#8
0
文件: utils.py 项目: gem/oq-irmt-qgis
def _on_tb_btn_clicked(message, tb_text):
    vbox = QVBoxLayout()
    dlg = QDialog()
    dlg.setWindowTitle('Traceback')
    text_browser = QTextBrowser()
    unformatted_msg = message
    formatted_msg = highlight(tb_text, PythonLexer(), HtmlFormatter(full=True))
    text_browser.setHtml(unformatted_msg + formatted_msg)
    vbox.addWidget(text_browser)
    dlg.setLayout(vbox)
    dlg.setMinimumSize(700, 500)
    dlg.exec_()
示例#9
0
class RedistrictingDockWidget(QgsDockWidget):
    """
    Dock widget for display of redistricting statistics and operations
    """
    def __init__(self, _iface: QgisInterface = None):
        super().__init__()

        if _iface is not None:
            self.iface = _iface
        else:
            self.iface = iface

        dock_contents = QWidget()
        grid = QGridLayout(dock_contents)
        grid.setContentsMargins(0, 0, 0, 0)

        self._dock_toolbar = QToolBar(dock_contents)
        self._dock_toolbar.setFloatable(False)
        grid.addWidget(self._dock_toolbar, 0, 0, 1, 1)

        self._dock_toolbar.setIconSize(self.iface.iconSize(True))

        self.frame = QTextBrowser()
        self.frame.setOpenLinks(False)
        self.frame.anchorClicked.connect(self.anchor_clicked)
        grid.addWidget(self.frame, 1, 0, 1, 1)

        self.setWidget(dock_contents)

    def dock_toolbar(self):
        """
        Returns the dock toolbar
        """
        return self._dock_toolbar

    def show_message(self, html):
        """
        Shows a HTML formatted message in the dock, replacing
        its current contents
        :param html: HTML to show
        """
        self.frame.setHtml(html)
        self.setUserVisible(True)

    def anchor_clicked(self, link: QUrl):
        """
        Called on clicking an anchor link in the dock frame
        :param link: link clicked
        """
        pass  # pylint: disable=unnecessary-pass
示例#10
0
    def setHtml(self, html):
        # convert special tags :)
        html = str(html).replace(
            '<warning>', '<img src=":/db_manager/warning">&nbsp;&nbsp; ')

        # add default style
        html = u"""
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<style type="text/css">
        .section { margin-top: 25px; }
        table.header th { background-color: #dddddd; }
        table.header td { background-color: #f5f5f5; }
        table.header th, table.header td { padding: 0px 10px; }
        table td { padding-right: 20px; }
        .underline { text-decoration:underline; }
</style>
</head>
<body>
%s <br>
</body>
</html>
""" % html

        # print ">>>>>\n", html, "\n<<<<<<"
        return QTextBrowser.setHtml(self, html)
示例#11
0
    def setHtml(self, html):
        # convert special tags :)
        html = unicode(html).replace('<warning>', '<img src=":/db_manager/warning">&nbsp;&nbsp; ')

        # add default style
        html = u"""
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<style type="text/css">
        .section { margin-top: 25px; }
        table.header th { background-color: #dddddd; }
        table.header td { background-color: #f5f5f5; }
        table.header th, table.header td { padding: 0px 10px; }
        table td { padding-right: 20px; }
        .underline { text-decoration:underline; }
</style>
</head>
<body>
%s <br>
</body>
</html>
""" % html

        # print ">>>>>\n", html, "\n<<<<<<"
        return QTextBrowser.setHtml(self, html)
class ModelerParametersDialog(QDialog):

    ENTER_NAME = '[Enter name if this is a final result]'
    NOT_SELECTED = '[Not selected]'
    USE_MIN_COVERING_EXTENT = '[Use min covering extent]'

    def __init__(self, alg, model, algName=None):
        QDialog.__init__(self)
        self.setModal(True)
        # The algorithm to define in this dialog. It is an instance of GeoAlgorithm
        self._alg = alg
        # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class
        self.alg = None
        # The model this algorithm is going to be added to
        self.model = model
        # The name of the algorithm in the model, in case we are editing it and not defining it for the first time
        self._algName = algName
        self.setupUi()
        self.params = None

    def setupUi(self):
        self.labels = {}
        self.widgets = {}
        self.checkBoxes = {}
        self.showAdvanced = False
        self.wrappers = {}
        self.valueItems = {}
        self.dependentItems = {}
        self.resize(650, 450)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok)
        tooltips = self._alg.getParameterDescriptions()
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setSpacing(5)
        self.verticalLayout.setMargin(20)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.verticalLayout.addWidget(self.bar)

        hLayout = QHBoxLayout()
        hLayout.setSpacing(5)
        hLayout.setMargin(0)
        descriptionLabel = QLabel(self.tr("Description"))
        self.descriptionBox = QLineEdit()
        self.descriptionBox.setText(self._alg.displayName())
        hLayout.addWidget(descriptionLabel)
        hLayout.addWidget(self.descriptionBox)
        self.verticalLayout.addLayout(hLayout)
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.verticalLayout.addWidget(line)

        for param in self._alg.parameters:
            if param.isAdvanced:
                self.advancedButton = QPushButton()
                self.advancedButton.setText(
                    self.tr('Show advanced parameters'))
                self.advancedButton.clicked.connect(
                    self.showAdvancedParametersClicked)
                advancedButtonHLayout = QHBoxLayout()
                advancedButtonHLayout.addWidget(self.advancedButton)
                advancedButtonHLayout.addStretch()
                self.verticalLayout.addLayout(advancedButtonHLayout)
                break
        for param in self._alg.parameters:
            if param.hidden:
                continue
            desc = param.description
            if isinstance(param, ParameterExtent):
                desc += self.tr('(xmin, xmax, ymin, ymax)')
            if isinstance(param, ParameterPoint):
                desc += self.tr('(x, y)')
            if param.optional:
                desc += self.tr(' [optional]')
            label = QLabel(desc)
            self.labels[param.name] = label

            wrapper = param.wrapper(self)
            self.wrappers[param.name] = wrapper

            widget = wrapper.widget
            if widget is not None:
                self.valueItems[param.name] = widget
                if param.name in list(tooltips.keys()):
                    tooltip = tooltips[param.name]
                else:
                    tooltip = param.description
                label.setToolTip(tooltip)
                widget.setToolTip(tooltip)
                if param.isAdvanced:
                    label.setVisible(self.showAdvanced)
                    widget.setVisible(self.showAdvanced)
                    self.widgets[param.name] = widget

                self.verticalLayout.addWidget(label)
                self.verticalLayout.addWidget(widget)

        for output in self._alg.outputs:
            if output.hidden:
                continue
            if isinstance(output, (OutputRaster, OutputVector, OutputTable,
                                   OutputHTML, OutputFile, OutputDirectory)):
                label = QLabel(output.description + '<' +
                               output.__class__.__name__ + '>')
                item = QLineEdit()
                if hasattr(item, 'setPlaceholderText'):
                    item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME)
                self.verticalLayout.addWidget(label)
                self.verticalLayout.addWidget(item)
                self.valueItems[output.name] = item

        label = QLabel(' ')
        self.verticalLayout.addWidget(label)
        label = QLabel(self.tr('Parent algorithms'))
        self.dependenciesPanel = self.getDependenciesPanel()
        self.verticalLayout.addWidget(label)
        self.verticalLayout.addWidget(self.dependenciesPanel)
        self.verticalLayout.addStretch(1000)

        self.setPreviousValues()
        self.setWindowTitle(self._alg.displayName())
        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.buttonBox)
        self.setLayout(self.verticalLayout2)
        self.buttonBox.accepted.connect(self.okPressed)
        self.buttonBox.rejected.connect(self.cancelPressed)
        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 = []
        else:
            dependent = self.model.getDependentAlgorithms(self._algName)
        opts = []
        for alg in list(self.model.algs.values()):
            if alg.modeler_name not in dependent:
                opts.append(alg)
        return opts

    def getDependenciesPanel(self):
        return MultipleInputPanel([
            alg.description for alg in self.getAvailableDependencies()
        ])  # spellok

    def showAdvancedParametersClicked(self):
        self.showAdvanced = not self.showAdvanced
        if self.showAdvanced:
            self.advancedButton.setText(self.tr('Hide advanced parameters'))
        else:
            self.advancedButton.setText(self.tr('Show advanced parameters'))
        for param in self._alg.parameters:
            if param.isAdvanced:
                self.labels[param.name].setVisible(self.showAdvanced)
                self.widgets[param.name].setVisible(self.showAdvanced)

    def getAvailableValuesOfType(self, paramType, outType=None, dataType=None):
        # upgrade paramType to list
        if type(paramType) is not list:
            paramType = [paramType]

        values = []
        inputs = self.model.inputs
        for i in list(inputs.values()):
            param = i.param
            for t in paramType:
                if isinstance(param, t):
                    if dataType is not None:
                        if param.datatype in dataType:
                            values.append(ValueFromInput(param.name))
                    else:
                        values.append(ValueFromInput(param.name))
                    break
        if outType is None:
            return values
        if self._algName is None:
            dependent = []
        else:
            dependent = self.model.getDependentAlgorithms(self._algName)
        for alg in list(self.model.algs.values()):
            if alg.modeler_name not in dependent:
                for out in alg.algorithm.outputs:
                    if isinstance(out, outType):
                        if dataType is not None and out.datatype in dataType:
                            values.append(
                                ValueFromOutput(alg.modeler_name, out.name))
                        else:
                            values.append(
                                ValueFromOutput(alg.modeler_name, out.name))

        return values

    def resolveValueDescription(self, value):
        if isinstance(value, ValueFromInput):
            return self.model.inputs[value.name].param.description
        else:
            alg = self.model.algs[value.alg]
            return self.tr("'{0}' from algorithm '{1}'").format(
                alg.algorithm.getOutputFromName(value.output).description,
                alg.description)

    def setPreviousValues(self):
        if self._algName is not None:
            alg = self.model.algs[self._algName]
            self.descriptionBox.setText(alg.description)
            for param in alg.algorithm.parameters:
                if param.hidden:
                    continue
                if param.name in alg.params:
                    value = alg.params[param.name]
                else:
                    value = param.default
                self.wrappers[param.name].setValue(value)
            for name, out in list(alg.outputs.items()):
                self.valueItems[name].setText(out.description)

            selected = []
            dependencies = self.getAvailableDependencies()  # spellok
            for idx, dependency in enumerate(dependencies):
                if dependency.name in alg.dependencies:
                    selected.append(idx)

            self.dependenciesPanel.setSelectedItems(selected)

    def createAlgorithm(self):
        alg = Algorithm(self._alg.id())
        alg.setName(self.model)
        alg.description = self.descriptionBox.text()
        params = self._alg.parameters
        outputs = self._alg.outputs
        for param in params:
            if param.hidden:
                continue
            if not self.setParamValue(alg, param, self.wrappers[param.name]):
                self.bar.pushMessage(
                    "Error",
                    "Wrong or missing value for parameter '%s'" %
                    param.description,
                    level=QgsMessageBar.WARNING)
                return None
        for output in outputs:
            if not output.hidden:
                name = str(self.valueItems[output.name].text())
                if name.strip(
                ) != '' and name != ModelerParametersDialog.ENTER_NAME:
                    alg.outputs[output.name] = ModelerOutput(name)

        selectedOptions = self.dependenciesPanel.selectedoptions
        availableDependencies = self.getAvailableDependencies()  # spellok
        for selected in selectedOptions:
            alg.dependencies.append(
                availableDependencies[selected].name)  # spellok

        self._alg.processBeforeAddingToModeler(alg, self.model)
        return alg

    def setParamValue(self, alg, param, wrapper):
        try:
            if wrapper.widget:
                value = wrapper.value()
                alg.params[param.name] = value
            return True
        except InvalidParameterValue:
            return False

    def okPressed(self):
        self.alg = self.createAlgorithm()
        if self.alg is not None:
            self.close()

    def cancelPressed(self):
        self.alg = None
        self.close()
示例#13
0
class ModelerParametersDialog(QDialog):

    ENTER_NAME = '[Enter name if this is a final result]'
    NOT_SELECTED = '[Not selected]'
    USE_MIN_COVERING_EXTENT = '[Use min covering extent]'

    def __init__(self, alg, model, algName=None):
        QDialog.__init__(self)
        self.setModal(True)
        # The algorithm to define in this dialog. It is an instance of GeoAlgorithm
        self._alg = alg
        # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class
        self.alg = None
        # The model this algorithm is going to be added to
        self.model = model
        # The name of the algorithm in the model, in case we are editing it and not defining it for the first time
        self._algName = algName
        self.setupUi()
        self.params = None

    def setupUi(self):
        self.labels = {}
        self.widgets = {}
        self.checkBoxes = {}
        self.showAdvanced = False
        self.wrappers = {}
        self.valueItems = {}
        self.dependentItems = {}
        self.resize(650, 450)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok)
        tooltips = self._alg.getParameterDescriptions()
        self.setSizePolicy(QSizePolicy.Expanding,
                           QSizePolicy.Expanding)
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setSpacing(5)
        self.verticalLayout.setMargin(20)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.verticalLayout.addWidget(self.bar)

        hLayout = QHBoxLayout()
        hLayout.setSpacing(5)
        hLayout.setMargin(0)
        descriptionLabel = QLabel(self.tr("Description"))
        self.descriptionBox = QLineEdit()
        self.descriptionBox.setText(self._alg.name)
        hLayout.addWidget(descriptionLabel)
        hLayout.addWidget(self.descriptionBox)
        self.verticalLayout.addLayout(hLayout)
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.verticalLayout.addWidget(line)

        for param in self._alg.parameters:
            if param.isAdvanced:
                self.advancedButton = QPushButton()
                self.advancedButton.setText(self.tr('Show advanced parameters'))
                self.advancedButton.clicked.connect(
                    self.showAdvancedParametersClicked)
                advancedButtonHLayout = QHBoxLayout()
                advancedButtonHLayout.addWidget(self.advancedButton)
                advancedButtonHLayout.addStretch()
                self.verticalLayout.addLayout(advancedButtonHLayout)
                break
        for param in self._alg.parameters:
            if param.hidden:
                continue
            desc = param.description
            if isinstance(param, ParameterExtent):
                desc += self.tr('(xmin, xmax, ymin, ymax)')
            if isinstance(param, ParameterPoint):
                desc += self.tr('(x, y)')
            if param.optional:
                desc += self.tr(' [optional]')
            label = QLabel(desc)
            self.labels[param.name] = label

            wrapper = param.wrapper(self)
            self.wrappers[param.name] = wrapper

            widget = wrapper.widget
            if widget:
                self.valueItems[param.name] = widget
                if param.name in list(tooltips.keys()):
                    tooltip = tooltips[param.name]
                else:
                    tooltip = param.description
                label.setToolTip(tooltip)
                widget.setToolTip(tooltip)
                if param.isAdvanced:
                    label.setVisible(self.showAdvanced)
                    widget.setVisible(self.showAdvanced)
                    self.widgets[param.name] = widget

                self.verticalLayout.addWidget(label)
                self.verticalLayout.addWidget(widget)

        for output in self._alg.outputs:
            if output.hidden:
                continue
            if isinstance(output, (OutputRaster, OutputVector, OutputTable,
                                   OutputHTML, OutputFile, OutputDirectory)):
                label = QLabel(output.description + '<'
                               + output.__class__.__name__ + '>')
                item = QLineEdit()
                if hasattr(item, 'setPlaceholderText'):
                    item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME)
                self.verticalLayout.addWidget(label)
                self.verticalLayout.addWidget(item)
                self.valueItems[output.name] = item

        label = QLabel(' ')
        self.verticalLayout.addWidget(label)
        label = QLabel(self.tr('Parent algorithms'))
        self.dependenciesPanel = self.getDependenciesPanel()
        self.verticalLayout.addWidget(label)
        self.verticalLayout.addWidget(self.dependenciesPanel)
        self.verticalLayout.addStretch(1000)

        self.setPreviousValues()
        self.setWindowTitle(self._alg.name)
        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 = QScrollArea()
        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.buttonBox)
        self.setLayout(self.verticalLayout2)
        self.buttonBox.accepted.connect(self.okPressed)
        self.buttonBox.rejected.connect(self.cancelPressed)
        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):
        if self._algName is None:
            dependent = []
        else:
            dependent = self.model.getDependentAlgorithms(self._algName)
        opts = []
        for alg in list(self.model.algs.values()):
            if alg.name not in dependent:
                opts.append(alg)
        return opts

    def getDependenciesPanel(self):
        return MultipleInputPanel([alg.description for alg in self.getAvailableDependencies()])

    def showAdvancedParametersClicked(self):
        self.showAdvanced = not self.showAdvanced
        if self.showAdvanced:
            self.advancedButton.setText(self.tr('Hide advanced parameters'))
        else:
            self.advancedButton.setText(self.tr('Show advanced parameters'))
        for param in self._alg.parameters:
            if param.isAdvanced:
                self.labels[param.name].setVisible(self.showAdvanced)
                self.widgets[param.name].setVisible(self.showAdvanced)

    def getAvailableValuesOfType(self, paramType, outType=None, dataType=None):
        # upgrade paramType to list
        if type(paramType) is not list:
            paramType = [paramType]

        values = []
        inputs = self.model.inputs
        for i in list(inputs.values()):
            param = i.param
            for t in paramType:
                if isinstance(param, t):
                    if dataType is not None:
                        if param.datatype in dataType:
                            values.append(ValueFromInput(param.name))
                    else:
                        values.append(ValueFromInput(param.name))
                    break
        if outType is None:
            return values
        if self._algName is None:
            dependent = []
        else:
            dependent = self.model.getDependentAlgorithms(self._algName)
        for alg in list(self.model.algs.values()):
            if alg.name not in dependent:
                for out in alg.algorithm.outputs:
                    if isinstance(out, outType):
                        if dataType is not None and out.datatype in dataType:
                            values.append(ValueFromOutput(alg.name, out.name))
                        else:
                            values.append(ValueFromOutput(alg.name, out.name))

        return values

    def resolveValueDescription(self, value):
        if isinstance(value, ValueFromInput):
            return self.model.inputs[value.name].param.description
        else:
            alg = self.model.algs[value.alg]
            return self.tr("'%s' from algorithm '%s'") % (alg.algorithm.getOutputFromName(value.output).description, alg.description)

    def setPreviousValues(self):
        if self._algName is not None:
            alg = self.model.algs[self._algName]
            self.descriptionBox.setText(alg.description)
            for param in alg.algorithm.parameters:
                if param.hidden:
                    continue
                if param.name in alg.params:
                    value = alg.params[param.name]
                else:
                    value = param.default
                self.wrappers[param.name].setValue(value)
            for name, out in list(alg.outputs.items()):
                self.valueItems[name].setText(out.description)

            selected = []
            dependencies = self.getAvailableDependencies()
            for idx, dependency in enumerate(dependencies):
                if dependency.name in alg.dependencies:
                    selected.append(idx)

            self.dependenciesPanel.setSelectedItems(selected)

    def createAlgorithm(self):
        alg = Algorithm(self._alg.commandLineName())
        alg.setName(self.model)
        alg.description = self.descriptionBox.text()
        params = self._alg.parameters
        outputs = self._alg.outputs
        for param in params:
            if param.hidden:
                continue
            if not self.setParamValue(alg, param, self.wrappers[param.name]):
                self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description,
                                     level=QgsMessageBar.WARNING)
                return None
        for output in outputs:
            if not output.hidden:
                name = str(self.valueItems[output.name].text())
                if name.strip() != '' and name != ModelerParametersDialog.ENTER_NAME:
                    alg.outputs[output.name] = ModelerOutput(name)

        selectedOptions = self.dependenciesPanel.selectedoptions
        availableDependencies = self.getAvailableDependencies()
        for selected in selectedOptions:
            alg.dependencies.append(availableDependencies[selected].name)

        self._alg.processBeforeAddingToModeler(alg, self.model)
        return alg

    def setParamValue(self, alg, param, wrapper):
        try:
            if wrapper.widget:
                value = wrapper.value()
                alg.params[param.name] = value
            return True
        except InvalidParameterValue:
            return False

    def okPressed(self):
        self.alg = self.createAlgorithm()
        if self.alg is not None:
            self.close()

    def cancelPressed(self):
        self.alg = None
        self.close()
示例#14
0
class ModelerParametersDialog(QDialog):

    ENTER_NAME = '[Enter name if this is a final result]'
    NOT_SELECTED = '[Not selected]'
    USE_MIN_COVERING_EXTENT = '[Use min covering extent]'

    def __init__(self, alg, model, algName=None):
        QDialog.__init__(self)
        self.setModal(True)
        # The algorithm to define in this dialog. It is an instance of GeoAlgorithm
        self._alg = alg
        # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class
        self.alg = None
        # The model this algorithm is going to be added to
        self.model = model
        # The name of the algorithm in the model, in case we are editing it and not defining it for the first time
        self._algName = algName
        self.setupUi()
        self.params = None

    def setupUi(self):
        self.labels = {}
        self.widgets = {}
        self.checkBoxes = {}
        self.showAdvanced = False
        self.valueItems = {}
        self.dependentItems = {}
        self.resize(650, 450)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok)
        tooltips = self._alg.getParameterDescriptions()
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setSpacing(5)
        self.verticalLayout.setMargin(20)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.verticalLayout.addWidget(self.bar)

        hLayout = QHBoxLayout()
        hLayout.setSpacing(5)
        hLayout.setMargin(0)
        descriptionLabel = QLabel(self.tr("Description"))
        self.descriptionBox = QLineEdit()
        self.descriptionBox.setText(self._alg.name)
        hLayout.addWidget(descriptionLabel)
        hLayout.addWidget(self.descriptionBox)
        self.verticalLayout.addLayout(hLayout)
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.verticalLayout.addWidget(line)

        for param in self._alg.parameters:
            if param.isAdvanced:
                self.advancedButton = QPushButton()
                self.advancedButton.setText(
                    self.tr('Show advanced parameters'))
                self.advancedButton.clicked.connect(
                    self.showAdvancedParametersClicked)
                advancedButtonHLayout = QHBoxLayout()
                advancedButtonHLayout.addWidget(self.advancedButton)
                advancedButtonHLayout.addStretch()
                self.verticalLayout.addLayout(advancedButtonHLayout)
                break
        for param in self._alg.parameters:
            if param.hidden:
                continue
            desc = param.description
            if isinstance(param, ParameterExtent):
                desc += self.tr('(xmin, xmax, ymin, ymax)')
            if isinstance(param, ParameterPoint):
                desc += self.tr('(x, y)')
            label = QLabel(desc)
            self.labels[param.name] = label
            widget = self.getWidgetFromParameter(param)
            self.valueItems[param.name] = widget
            if param.name in tooltips.keys():
                tooltip = tooltips[param.name]
            else:
                tooltip = param.description
            label.setToolTip(tooltip)
            widget.setToolTip(tooltip)
            if param.isAdvanced:
                label.setVisible(self.showAdvanced)
                widget.setVisible(self.showAdvanced)
                self.widgets[param.name] = widget
            self.verticalLayout.addWidget(label)
            self.verticalLayout.addWidget(widget)

        for output in self._alg.outputs:
            if output.hidden:
                continue
            if isinstance(output, (OutputRaster, OutputVector, OutputTable,
                                   OutputHTML, OutputFile, OutputDirectory)):
                label = QLabel(output.description + '<' +
                               output.__class__.__name__ + '>')
                item = QLineEdit()
                if hasattr(item, 'setPlaceholderText'):
                    item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME)
                self.verticalLayout.addWidget(label)
                self.verticalLayout.addWidget(item)
                self.valueItems[output.name] = item

        label = QLabel(' ')
        self.verticalLayout.addWidget(label)
        label = QLabel(self.tr('Parent algorithms'))
        self.dependenciesPanel = self.getDependenciesPanel()
        self.verticalLayout.addWidget(label)
        self.verticalLayout.addWidget(self.dependenciesPanel)

        self.verticalLayout.addStretch(1000)
        self.setLayout(self.verticalLayout)

        self.setPreviousValues()
        self.setWindowTitle(self._alg.name)
        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 = QScrollArea()
        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.reply = QgsNetworkAccessManager.instance().get(
                        QNetworkRequest(algHelp))
                    self.reply.finished.connect(self.requestFinished)
            except:
                self.txtHelp.setHtml(
                    self.tr('<h2>No help available for this algorithm</h2>'))

        self.tabWidget.addTab(self.txtHelp, 'Help')

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

    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 = unicode(reply.readAll())
        reply.deleteLater()
        self.txtHelp.setHtml(html)

    def getAvailableDependencies(self):
        if self._algName is None:
            dependent = []
        else:
            dependent = self.model.getDependentAlgorithms(self._algName)
        opts = []
        for alg in self.model.algs.values():
            if alg.name not in dependent:
                opts.append(alg)
        return opts

    def getDependenciesPanel(self):
        return MultipleInputPanel(
            [alg.algorithm.name for alg in self.getAvailableDependencies()])

    def showAdvancedParametersClicked(self):
        self.showAdvanced = not self.showAdvanced
        if self.showAdvanced:
            self.advancedButton.setText(self.tr('Hide advanced parameters'))
        else:
            self.advancedButton.setText(self.tr('Show advanced parameters'))
        for param in self._alg.parameters:
            if param.isAdvanced:
                self.labels[param.name].setVisible(self.showAdvanced)
                self.widgets[param.name].setVisible(self.showAdvanced)

    def getAvailableValuesOfType(self, paramType, outType=None):
        values = []
        inputs = self.model.inputs
        for i in inputs.values():
            param = i.param
            if isinstance(param, paramType):
                values.append(ValueFromInput(param.name))
        if outType is None:
            return values
        if self._algName is None:
            dependent = []
        else:
            dependent = self.model.getDependentAlgorithms(self._algName)
        for alg in self.model.algs.values():
            if alg.name not in dependent:
                for out in alg.algorithm.outputs:
                    if isinstance(out, outType):
                        values.append(ValueFromOutput(alg.name, out.name))

        return values

    def resolveValueDescription(self, value):
        if isinstance(value, ValueFromInput):
            return self.model.inputs[value.name].param.description
        else:
            alg = self.model.algs[value.alg]
            return self.tr("'%s' from algorithm '%s'") % (
                alg.algorithm.getOutputFromName(
                    value.output).description, alg.description)

    def getWidgetFromParameter(self, param):
        if isinstance(param, ParameterRaster):
            item = QComboBox()
            layers = self.getAvailableValuesOfType(ParameterRaster,
                                                   OutputRaster)
            if param.optional:
                item.addItem(self.NOT_SELECTED, None)
            for layer in layers:
                item.addItem(self.resolveValueDescription(layer), layer)
        elif isinstance(param, ParameterVector):
            item = QComboBox()
            layers = self.getAvailableValuesOfType(ParameterVector,
                                                   OutputVector)
            if param.optional:
                item.addItem(self.NOT_SELECTED, None)
            for layer in layers:
                item.addItem(self.resolveValueDescription(layer), layer)
        elif isinstance(param, ParameterTable):
            item = QComboBox()
            tables = self.getAvailableValuesOfType(ParameterTable, OutputTable)
            layers = self.getAvailableValuesOfType(ParameterVector,
                                                   OutputVector)
            if param.optional:
                item.addItem(self.NOT_SELECTED, None)
            for table in tables:
                item.addItem(self.resolveValueDescription(table), table)
            for layer in layers:
                item.addItem(self.resolveValueDescription(layer), layer)
        elif isinstance(param, ParameterBoolean):
            item = QComboBox()
            item.addItem('Yes')
            item.addItem('No')
            bools = self.getAvailableValuesOfType(ParameterBoolean, None)
            for b in bools:
                item.addItem(self.resolveValueDescription(b), b)
            if param.default:
                item.setCurrentIndex(0)
            else:
                item.setCurrentIndex(1)
        elif isinstance(param, ParameterSelection):
            item = QComboBox()
            item.addItems(param.options)
            item.setCurrentIndex(param.default or 0)
        elif isinstance(param, ParameterFixedTable):
            item = FixedTablePanel(param)
        elif isinstance(param, ParameterRange):
            item = RangePanel(param)
        elif isinstance(param, ParameterMultipleInput):
            if param.datatype == ParameterMultipleInput.TYPE_VECTOR_ANY:
                options = self.getAvailableValuesOfType(
                    ParameterVector, OutputVector)
            else:
                options = self.getAvailableValuesOfType(
                    ParameterRaster, OutputRaster)
            opts = []
            for opt in options:
                opts.append(self.resolveValueDescription(opt))
            item = MultipleInputPanel(opts)
        elif isinstance(param, ParameterString):
            strings = self.getAvailableValuesOfType(ParameterString,
                                                    OutputString)
            options = [(self.resolveValueDescription(s), s) for s in strings]
            if param.multiline:
                item = MultilineTextPanel(options)
                item.setText(unicode(param.default or ""))
            else:
                item = QComboBox()
                item.setEditable(True)
                for desc, val in options:
                    item.addItem(desc, val)
                item.setEditText(unicode(param.default or ""))
        elif isinstance(param, ParameterTableField):
            item = QComboBox()
            item.setEditable(True)
            fields = self.getAvailableValuesOfType(ParameterTableField, None)
            for f in fields:
                item.addItem(self.resolveValueDescription(f), f)
        elif isinstance(param, ParameterTableMultipleField):
            item = QComboBox()
            item.setEditable(True)
            fields = self.getAvailableValuesOfType(ParameterTableMultipleField,
                                                   None)
            for f in fields:
                item.addItem(self.resolveValueDescription(f), f)
        elif isinstance(param, ParameterNumber):
            item = QComboBox()
            item.setEditable(True)
            numbers = self.getAvailableValuesOfType(ParameterNumber,
                                                    OutputNumber)
            for n in numbers:
                item.addItem(self.resolveValueDescription(n), n)
            item.setEditText(unicode(param.default))
        elif isinstance(param, ParameterCrs):
            item = CrsSelectionPanel(param.default)
        elif isinstance(param, ParameterExtent):
            item = QComboBox()
            item.setEditable(True)
            extents = self.getAvailableValuesOfType(ParameterExtent,
                                                    OutputExtent)
            if self.canUseAutoExtent():
                item.addItem(self.USE_MIN_COVERING_EXTENT, None)
            for ex in extents:
                item.addItem(self.resolveValueDescription(ex), ex)
            if not self.canUseAutoExtent():
                item.setEditText(unicode(param.default))
        elif isinstance(param, ParameterPoint):
            item = QComboBox()
            item.setEditable(True)
            points = self.getAvailableValuesOfType(ParameterPoint)
            for p in points:
                item.addItem(self.resolveValueDescription(p), p)
            item.setEditText(unicode(param.default))
        elif isinstance(param, ParameterFile):
            item = QComboBox()
            item.setEditable(True)
            files = self.getAvailableValuesOfType(ParameterFile, OutputFile)
            for f in files:
                item.addItem(self.resolveValueDescription(f), f)
        elif isinstance(param, ParameterGeometryPredicate):
            item = GeometryPredicateSelectionPanel(param.enabledPredicates)
        else:
            item = QLineEdit()
            try:
                item.setText(unicode(param.default))
            except:
                pass
        return item

    def canUseAutoExtent(self):
        for param in self._alg.parameters:
            if isinstance(
                    param,
                (ParameterRaster, ParameterVector, ParameterMultipleInput)):
                return True
        return False

    def setTableContent(self):
        params = self._alg.parameters
        outputs = self._alg.outputs
        visibleParams = [p for p in params if not p.hidden]
        visibleOutputs = [p for o in outputs if not o.hidden]
        self.tableWidget.setRowCount(len(visibleParams) + len(visibleOutputs))

        for i, param in visibleParams:
            item = QTableWidgetItem(param.description)
            item.setFlags(Qt.ItemIsEnabled)
            self.tableWidget.setItem(i, 0, item)
            item = self.getWidgetFromParameter(param)
            self.valueItems[param.name] = item
            self.tableWidget.setCellWidget(i, 1, item)
            self.tableWidget.setRowHeight(i, 22)

        for i, output in visibleOutputs:
            item = QTableWidgetItem(output.description + '<' +
                                    output.__module__.split('.')[-1] + '>')
            item.setFlags(Qt.ItemIsEnabled)
            self.tableWidget.setItem(i, 0, item)
            item = QLineEdit()
            if hasattr(item, 'setPlaceholderText'):
                item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME)
            self.valueItems[output.name] = item
            self.tableWidget.setCellWidget(i, 1, item)
            self.tableWidget.setRowHeight(i, 22)

    def setComboBoxValue(self, combo, value, param):
        if isinstance(value, list):
            value = value[0]
        items = [combo.itemData(i) for i in range(combo.count())]
        try:
            idx = items.index(value)
            combo.setCurrentIndex(idx)
            return
        except ValueError:
            pass
        if combo.isEditable():
            if value is not None:
                combo.setEditText(unicode(value))
        elif isinstance(param, ParameterSelection):
            combo.setCurrentIndex(int(value))
        elif isinstance(param, ParameterBoolean):
            if value:
                combo.setCurrentIndex(0)
            else:
                combo.setCurrentIndex(1)

    def setPreviousValues(self):
        if self._algName is not None:
            alg = self.model.algs[self._algName]
            self.descriptionBox.setText(alg.description)
            for param in alg.algorithm.parameters:
                if param.hidden:
                    continue
                widget = self.valueItems[param.name]
                if param.name in alg.params:
                    value = alg.params[param.name]
                else:
                    value = param.default
                if isinstance(
                        param,
                    (ParameterRaster, ParameterVector, ParameterTable,
                     ParameterTableField, ParameterSelection, ParameterNumber,
                     ParameterBoolean, ParameterExtent, ParameterFile,
                     ParameterPoint)):
                    self.setComboBoxValue(widget, value, param)
                elif isinstance(param, ParameterString):
                    if param.multiline:
                        widget.setValue(value)
                    else:
                        self.setComboBoxValue(widget, value, param)
                elif isinstance(param, ParameterCrs):
                    widget.setAuthId(value)
                elif isinstance(param, ParameterFixedTable):
                    pass  # TODO!
                elif isinstance(param, ParameterMultipleInput):
                    if param.datatype == ParameterMultipleInput.TYPE_VECTOR_ANY:
                        options = self.getAvailableValuesOfType(
                            ParameterVector, OutputVector)
                    else:
                        options = self.getAvailableValuesOfType(
                            ParameterRaster, OutputRaster)
                    selected = []
                    for i, opt in enumerate(options):
                        if opt in value:
                            selected.append(i)
                    widget.setSelectedItems(selected)
                elif isinstance(param, ParameterGeometryPredicate):
                    widget.setValue(value)

            for name, out in alg.outputs.iteritems():
                widget = self.valueItems[name].setText(out.description)

            selected = []
            dependencies = self.getAvailableDependencies()
            for idx, dependency in enumerate(dependencies):
                if dependency.name in alg.dependencies:
                    selected.append(idx)

            self.dependenciesPanel.setSelectedItems(selected)

    def createAlgorithm(self):
        alg = Algorithm(self._alg.commandLineName())
        alg.setName(self.model)
        alg.description = self.descriptionBox.text()
        params = self._alg.parameters
        outputs = self._alg.outputs
        for param in params:
            if param.hidden:
                continue
            if not self.setParamValue(alg, param, self.valueItems[param.name]):
                self.bar.pushMessage(
                    "Error",
                    "Wrong or missing value for parameter '%s'" %
                    param.description,
                    level=QgsMessageBar.WARNING)
                return None
        for output in outputs:
            if not output.hidden:
                name = unicode(self.valueItems[output.name].text())
                if name.strip(
                ) != '' and name != ModelerParametersDialog.ENTER_NAME:
                    alg.outputs[output.name] = ModelerOutput(name)

        selectedOptions = self.dependenciesPanel.selectedoptions
        availableDependencies = self.getAvailableDependencies()
        for selected in selectedOptions:
            alg.dependencies.append(availableDependencies[selected].name)

        return alg

    def setParamValueLayerOrTable(self, alg, param, widget):
        idx = widget.currentIndex()
        if idx < 0:
            return False
        else:
            value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
            return True

    def setParamTableFieldValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            s = unicode(widget.currentText()).strip()
            if s == '':
                if param.optional:
                    alg.params[param.name] = None
                    return True
                else:
                    return False
            else:
                alg.params[param.name] = s
                return True
        else:
            alg.params[param.name] = widget.itemData(widget.currentIndex())
        return True

    def setParamStringValue(self, alg, param, widget):
        if param.multiline:
            value = widget.getValue()
            option = widget.getOption()
            if option == MultilineTextPanel.USE_TEXT:
                if value == '':
                    if param.optional:
                        alg.params[param.name] = None
                        return True
                    else:
                        return False
                else:
                    alg.params[param.name] = value
            else:
                alg.params[param.name] = value
        else:
            idx = widget.findText(widget.currentText())
            if idx < 0:
                value = widget.currentText().strip()
                if value == '':
                    if param.optional:
                        alg.params[param.name] = None
                        return True
                    else:
                        return False
                else:
                    alg.params[param.name] = value
            else:
                alg.params[param.name] = widget.itemData(widget.currentIndex())
        return True

    def setParamFileValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            value = widget.currentText()
        else:
            value = widget.itemData(widget.currentIndex())
        alg.params[param.name] = value
        return True

    def setParamNumberValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            s = widget.currentText().strip()
            if s:
                try:
                    value = float(s)
                except:
                    return False
            elif param.optional:
                value = None
            else:
                return False
        else:
            value = widget.itemData(widget.currentIndex())
        alg.params[param.name] = value
        return True

    def setParamExtentValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            s = unicode(widget.currentText()).strip()
            if s:
                try:
                    tokens = s.split(',')
                    if len(tokens) != 4:
                        return False
                    for token in tokens:
                        float(token)
                except:
                    return False
            elif param.optional:
                s = None
            else:
                return False
            alg.params[param.name] = [s]
        else:
            value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
        return True

    def setParamPointValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            s = unicode(widget.currentText()).strip()
            if s:
                try:
                    tokens = s.split(',')
                    if len(tokens) != 2:
                        return False
                    for token in tokens:
                        float(token)
                except:
                    return False
            elif param.optional:
                s = None
            else:
                return False
            alg.params[param.name] = [s]
        else:
            value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
        return True

    def setParamValue(self, alg, param, widget):
        if isinstance(param,
                      (ParameterRaster, ParameterVector, ParameterTable)):
            return self.setParamValueLayerOrTable(alg, param, widget)
        elif isinstance(param, ParameterBoolean):
            if widget.currentIndex() < 2:
                value = widget.currentIndex() == 0
            else:
                value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
            return True
        elif isinstance(param, ParameterString):
            return self.setParamStringValue(alg, param, widget)
        elif isinstance(param, ParameterNumber):
            return self.setParamNumberValue(alg, param, widget)
        elif isinstance(param, ParameterExtent):
            return self.setParamExtentValue(alg, param, widget)
        elif isinstance(param, ParameterPoint):
            return self.setParamPointValue(alg, param, widget)
        elif isinstance(param, ParameterFile):
            return self.setParamFileValue(alg, param, widget)
        elif isinstance(param, ParameterSelection):
            alg.params[param.name] = widget.currentIndex()
            return True
        elif isinstance(param, ParameterRange):
            alg.params[param.name] = widget.getValue()
            return True
        elif isinstance(param, ParameterCrs):
            authid = widget.getValue()
            if authid is None and not param.optional:
                return False
            alg.params[param.name] = authid
            return True
        elif isinstance(param, ParameterFixedTable):
            table = widget.table
            if not bool(table) and not param.optional:
                return False
            alg.params[param.name] = ParameterFixedTable.tableToString(table)
            return True
        elif isinstance(param,
                        (ParameterTableField, ParameterTableMultipleField)):
            return self.setParamTableFieldValue(alg, param, widget)
        elif isinstance(param, ParameterMultipleInput):
            if param.datatype == ParameterMultipleInput.TYPE_VECTOR_ANY:
                options = self.getAvailableValuesOfType(
                    ParameterVector, OutputVector)
            else:
                options = self.getAvailableValuesOfType(
                    ParameterRaster, OutputRaster)
            values = [options[i] for i in widget.selectedoptions]
            if len(values) == 0 and not param.optional:
                return False
            alg.params[param.name] = values
            return True
        elif isinstance(param, ParameterGeometryPredicate):
            alg.params[param.name] = widget.value()
            return True
        else:
            alg.params[param.name] = unicode(widget.text())
            return True

    def okPressed(self):
        self.alg = self.createAlgorithm()
        if self.alg is not None:
            self.close()

    def cancelPressed(self):
        self.alg = None
        self.close()
class HistoryDiffViewerWidget(QWidget):

    def __init__(self, dialog, server, user, repo, graph, layer=None, initialSimplify=False):
        self.graph = graph
        self.dialog = dialog
        self.server = server
        self.user = user
        self.repo = repo 
        self.layer = layer
        self.afterLayer = None
        self.beforeLayer = None
        self.extraLayers = [] # layers for the "Map" tab
        QWidget.__init__(self, iface.mainWindow())
        self.setWindowFlags(Qt.Window)
        self.simplifyLog = initialSimplify
        self.initGui()
        self.tabWidget.setVisible(False)
        self.setLabelText("Select a commit to show its content")
        self.label.setVisible(False)
        if self.graph.commits:
            self.history.setCurrentItem(self.history.topLevelItem(0))
            self.itemChanged(self.history.topLevelItem(0), None)
        self.history.currentItemChanged.connect(self.itemChanged)

    def setShowPopup(self, show):
        self.history.showPopup = show 

    def initGui(self):
        layout = QVBoxLayout()
        splitter = QSplitter()
        splitter.setOrientation(Qt.Vertical)

        self.history = HistoryTree(self.dialog)
        self.history.updateContent(self.server, self.user, self.repo, self.graph, self.layer)
        self.historyWithFilter = HistoryTreeWrapper(self.history)
        if self.simplifyLog:
            self.historyWithFilter.simplify(True)
        splitter.addWidget(self.historyWithFilter)
        self.tabWidget = QTabWidget()
        self.tabCanvas = QWidget()
        tabLayout = QVBoxLayout()
        tabLayout.setMargin(0)        
        self.canvas = QgsMapCanvas(self.tabCanvas)
        self.canvas.setCanvasColor(Qt.white)
        self.panTool = QgsMapToolPan(self.canvas)
        self.canvas.setMapTool(self.panTool)
        tabLayout.addWidget(self.canvas)
        self.labelNoChanges = QLabel("This commit doesn't change any geometry")
        self.labelNoChanges.setAlignment(Qt.AlignCenter)
        self.labelNoChanges.setVisible(False)
        tabLayout.addWidget(self.labelNoChanges)
        self.tabCanvas.setLayout(tabLayout)
        self.summaryTextBrowser = QTextBrowser()
        self.summaryTextBrowser.setOpenLinks(False)
        self.summaryTextBrowser.anchorClicked.connect(self.summaryTextBrowserAnchorClicked)
        self.tabWidget.addTab(self.summaryTextBrowser, "Commit Summary")
        self.tabWidget.addTab(self.tabCanvas, "Map")
        tabLayout = QVBoxLayout()
        tabLayout.setMargin(0)
        self.tabDiffViewer = QWidget()
        self.diffViewer = DiffViewerWidget({})
        tabLayout.addWidget(self.diffViewer)
        self.tabDiffViewer.setLayout(tabLayout)
        self.tabWidget.addTab(self.tabDiffViewer, "Attributes")
        splitter.addWidget(self.tabWidget)
        self.label = QTextBrowser()
        self.label.setVisible(False)
        splitter.addWidget(self.label)
        self.tabWidget.setCurrentWidget(self.tabDiffViewer)

        layout.addWidget(splitter)
        self.setLayout(layout)

        exportDiffButton = QPushButton("Export this commit's DIFF for all layers")
        exportDiffButton.clicked.connect(self.exportDiffAllLayers)

        layout.addWidget(exportDiffButton)
        self.label.setMinimumHeight(self.tabWidget.height())        
        self.setWindowTitle("Repository history")

    def summaryTextBrowserAnchorClicked(self,url):
        url = url.url() #convert to string
        item = self.history.currentItem()
        if item is None:
            return
        commitid = item.commit.commitid

        cmd,layerName = url.split(".",1)
        if cmd == "addLive":
            execute(lambda: self.history.exportVersion(layerName,commitid,True))
        elif cmd == "addGeoPKG":
            self.history.exportVersion(layerName,commitid,False)
        elif cmd == "exportDiff":
            execute(lambda: self.history.exportDiff(item, None,layer=layerName))

    def exportDiffAllLayers(self):
        item = self.history.currentItem()
        if item is not None:
            self.history.exportDiff(item, None)

    def setLabelText(self,text):
        self.label.setHtml("<br><br><br><center><b>{}</b></center>".format(text))

    def setContent(self, server, user, repo, graph, layer = None):
        self.server = server
        self.user = user
        self.repo = repo
        self.layer = layer
        self.graph = graph
        self.historyWithFilter.updateContent(server, user, repo, graph, layer)
        if self.history.graph.commits:
            self.history.setCurrentItem(self.history.topLevelItem(0))

    def itemChanged(self, current, previous, THRESHOLD = 1500):
        item = self.history.currentItem()
        if item is not None:
            commit = self.graph.getById(item.ref)
            if commit is None:
                self.tabWidget.setVisible(False)
                self.setLabelText("Select a commit to show its content")
                self.label.setVisible(True)
                return

            commit2 = commit.commitid + "~1"

            if not item.commit.hasParents():
                commit2 = "0000000000000000"

            total,details = self.server.diffSummary(self.user, self.repo,  commit2,commit.commitid)
            tooLargeDiff = total > THRESHOLD
            if tooLargeDiff:
                 html = "<br><br><center><b><font size=+3>Commit <font size=-0.1><tt>{}</tt></font> DIFF is too large to be shown</b></font><br>".format(commit.commitid[:8])
            else:
                html = "<br><br><center><b><font size=+3>Commit <font size=-0.1><tt>{}</tt></font> Summary</b></font><br>".format(commit.commitid[:8])
            html += "<table>"
            html += "<tr><Td style='padding:5px'><b>Layer&nbsp;Name</b></td><td style='padding:5px'><b>Additions</b></td><td style='padding:5px'><b>Deletions</b></td><td style='padding:5px'><b>Modifications</b></td><td></td><td></td><td></td></tr>"
            for detail in details.values():
                html += "<tr><td style='padding:5px'>{}</td><td style='padding:5px'><center>{:,}</center></td><td style='padding:5px'><center>{:,}</center></td><td style='padding:5px'><center>{:,}</center></td><td style='padding:5px'>{}</td><td style='padding:5px'>{}</td><td style='padding:5px'>{}</td></tr>".format(
                    detail["path"],
                    int(detail["featuresAdded"]), int(detail["featuresRemoved"]),int(detail["featuresChanged"]),
                    "<a href='addLive.{}'>Add Live</a>".format(detail["path"]),
                    "<a href='addGeoPKG.{}'>Add GeoPKG</a>".format(detail["path"]),
                    "<a href='exportDiff.{}'>Export Diff</a>".format(detail["path"])
                )
            html += "<tr></tr>"
            html += "<tr><td colspan=4>There is a total of {:,} features changed</td></tr>".format(total)
            html += "</table>"
            # html += "<br><br>There is a total of {:,} features changed".format(total)
            self.summaryTextBrowser.setHtml(html)
            self.label.setVisible(False)
            self.tabWidget.setVisible(True)
            self.tabWidget.setTabEnabled(1,not tooLargeDiff)
            self.tabWidget.setTabEnabled(2,not tooLargeDiff)
            if not tooLargeDiff:
                self.setDiffContent(commit, commit2)
        else:
            self.tabWidget.setVisible(False)
            self.setLabelText("Select a commit to show its content")
            self.label.setVisible(True)

    def setDiffContent(self, commit, commit2):
        if self.layer is None:
            layers = set(self.server.layers(self.user, self.repo, commit.commitid))
            layers2 = set(self.server.layers(self.user, self.repo, commit2))
            layers = layers.union(layers2)
        else:
            layers = [self.layer]

        diffs = {layer: execute(lambda: self.server.diff(self.user, self.repo, layer, commit.commitid, commit2)) for layer in layers}
        diffs = {key:value for (key,value) in diffs.items() if len(value) !=0}
        layers = [l for l in diffs.keys()]
        self.diffViewer.setChanges(diffs)

        self.canvas.setLayers([])
        self.removeMapLayers()
        extent = QgsRectangle()
        for layer in layers:
            if not diffs[layer]:
                continue
            beforeLayer, afterLayer = execute(lambda: self._getLayers(diffs[layer]))
            if afterLayer is not None:
                resourcesPath =  os.path.join(os.path.dirname(__file__), os.pardir, "resources")
                oldStylePath = os.path.join(resourcesPath, "{}_before.qml".format(
                                            QgsWkbTypes.geometryDisplayString(beforeLayer.geometryType())))
                newStylePath = os.path.join(resourcesPath, "{}_after.qml".format(
                                            QgsWkbTypes.geometryDisplayString(afterLayer.geometryType())))

                beforeLayer.loadNamedStyle(oldStylePath)
                afterLayer.loadNamedStyle(newStylePath)

                QgsProject.instance().addMapLayer(beforeLayer, False)
                QgsProject.instance().addMapLayer(afterLayer, False)

                extent.combineExtentWith(beforeLayer.extent())
                extent.combineExtentWith(afterLayer.extent())
                self.extraLayers.append(beforeLayer)
                self.extraLayers.append(afterLayer)
        # make extent a bit bit (10%) bigger
        # this gives some margin around the dataset (not cut-off at edges)
        if not extent.isEmpty():
            widthDelta = extent.width() * 0.05
            heightDelta = extent.height() * 0.05
            extent = QgsRectangle(extent.xMinimum() - widthDelta,
                                  extent.yMinimum() - heightDelta,
                                  extent.xMaximum() + widthDelta,
                                  extent.yMaximum() + heightDelta)

        layers = self.extraLayers
        hasChanges = False
        for layer in layers:
            if layer is not None and layer.featureCount() > 0:
                hasChanges = True
                break
        self.canvas.setLayers(layers)
        self.canvas.setExtent(extent)
        self.canvas.refresh()
                
        self.canvas.setVisible(hasChanges)
        self.labelNoChanges.setVisible(not hasChanges)

    def _getLayers(self, changes):
        ADDED, MODIFIED, REMOVED,  = 0, 1, 2
        def _feature(g, changeType):
            feat = QgsFeature()
            if g is not None:
                feat.setGeometry(g)
            feat.setAttributes([changeType])
            return feat
        if changes:
            f = changes[0]
            new = f["new"]            
            old = f["old"]
            reference = new or old
            geomtype = QgsWkbTypes.displayString(reference.geometry().wkbType())
            oldLayer = loadLayerNoCrsDialog(geomtype + "?crs=epsg:4326&field=geogig.changeType:integer", "old", "memory")
            newLayer = loadLayerNoCrsDialog(geomtype + "?crs=epsg:4326&field=geogig.changeType:integer", "new", "memory")
            oldFeatures = []
            newFeatures = []
            for f in changes:            
                new = f["new"]        
                old = f["old"]                  
                newGeom = new.geometry() if new is not None else None
                oldGeom = old.geometry() if old is not None else None
                if oldGeom is None:
                    feature = _feature(newGeom, ADDED)
                    newFeatures.append(feature)
                elif newGeom is None:
                    feature = _feature(oldGeom, REMOVED)
                    oldFeatures.append(feature)
                elif oldGeom.asWkt() != newGeom.asWkt():
                    feature = _feature(oldGeom, MODIFIED)
                    oldFeatures.append(feature)
                    feature = _feature(newGeom, MODIFIED)
                    newFeatures.append(feature)
                else:
                    feature = _feature(newGeom, MODIFIED)
                    newFeatures.append(feature)
            oldLayer.dataProvider().addFeatures(oldFeatures)
            newLayer.dataProvider().addFeatures(newFeatures)
        else:
            oldLayer = None
            newLayer = None

        return oldLayer, newLayer

    def removeMapLayers(self):
        for layer in self.extraLayers:
            if layer is not None:
                    QgsProject.instance().removeMapLayer(layer.id())
        self.extraLayers = []
class ModelerParametersDialog(QDialog):

    ENTER_NAME = '[Enter name if this is a final result]'
    NOT_SELECTED = '[Not selected]'
    USE_MIN_COVERING_EXTENT = '[Use min covering extent]'

    def __init__(self, alg, model, algName=None):
        QDialog.__init__(self)
        self.setModal(True)
        # The algorithm to define in this dialog. It is an instance of GeoAlgorithm
        self._alg = alg
        # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class
        self.alg = None
        # The model this algorithm is going to be added to
        self.model = model
        # The name of the algorithm in the model, in case we are editing it and not defining it for the first time
        self._algName = algName
        self.setupUi()
        self.params = None

    def setupUi(self):
        self.labels = {}
        self.widgets = {}
        self.checkBoxes = {}
        self.showAdvanced = False
        self.valueItems = {}
        self.dependentItems = {}
        self.resize(650, 450)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok)
        tooltips = self._alg.getParameterDescriptions()
        self.setSizePolicy(QSizePolicy.Expanding,
                           QSizePolicy.Expanding)
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setSpacing(5)
        self.verticalLayout.setMargin(20)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.verticalLayout.addWidget(self.bar)

        hLayout = QHBoxLayout()
        hLayout.setSpacing(5)
        hLayout.setMargin(0)
        descriptionLabel = QLabel(self.tr("Description"))
        self.descriptionBox = QLineEdit()
        self.descriptionBox.setText(self._alg.name)
        hLayout.addWidget(descriptionLabel)
        hLayout.addWidget(self.descriptionBox)
        self.verticalLayout.addLayout(hLayout)
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        self.verticalLayout.addWidget(line)

        for param in self._alg.parameters:
            if param.isAdvanced:
                self.advancedButton = QPushButton()
                self.advancedButton.setText(self.tr('Show advanced parameters'))
                self.advancedButton.clicked.connect(
                    self.showAdvancedParametersClicked)
                advancedButtonHLayout = QHBoxLayout()
                advancedButtonHLayout.addWidget(self.advancedButton)
                advancedButtonHLayout.addStretch()
                self.verticalLayout.addLayout(advancedButtonHLayout)
                break
        for param in self._alg.parameters:
            if param.hidden:
                continue
            desc = param.description
            if isinstance(param, ParameterExtent):
                desc += self.tr('(xmin, xmax, ymin, ymax)')
            if isinstance(param, ParameterPoint):
                desc += self.tr('(x, y)')
            label = QLabel(desc)
            self.labels[param.name] = label
            widget = self.getWidgetFromParameter(param)
            self.valueItems[param.name] = widget
            if param.name in list(tooltips.keys()):
                tooltip = tooltips[param.name]
            else:
                tooltip = param.description
            label.setToolTip(tooltip)
            widget.setToolTip(tooltip)
            if param.isAdvanced:
                label.setVisible(self.showAdvanced)
                widget.setVisible(self.showAdvanced)
                self.widgets[param.name] = widget
            self.verticalLayout.addWidget(label)
            self.verticalLayout.addWidget(widget)

        for output in self._alg.outputs:
            if output.hidden:
                continue
            if isinstance(output, (OutputRaster, OutputVector, OutputTable,
                                   OutputHTML, OutputFile, OutputDirectory)):
                label = QLabel(output.description + '<'
                               + output.__class__.__name__ + '>')
                item = QLineEdit()
                if hasattr(item, 'setPlaceholderText'):
                    item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME)
                self.verticalLayout.addWidget(label)
                self.verticalLayout.addWidget(item)
                self.valueItems[output.name] = item

        label = QLabel(' ')
        self.verticalLayout.addWidget(label)
        label = QLabel(self.tr('Parent algorithms'))
        self.dependenciesPanel = self.getDependenciesPanel()
        self.verticalLayout.addWidget(label)
        self.verticalLayout.addWidget(self.dependenciesPanel)

        self.verticalLayout.addStretch(1000)
        self.setLayout(self.verticalLayout)

        self.setPreviousValues()
        self.setWindowTitle(self._alg.name)
        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 = QScrollArea()
        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.reply = QgsNetworkAccessManager.instance().get(QNetworkRequest(algHelp))
                    self.reply.finished.connect(self.requestFinished)
            except:
                self.txtHelp.setHtml(self.tr('<h2>No help available for this algorithm</h2>'))

        self.tabWidget.addTab(self.txtHelp, 'Help')

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

    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):
        if self._algName is None:
            dependent = []
        else:
            dependent = self.model.getDependentAlgorithms(self._algName)
        opts = []
        for alg in list(self.model.algs.values()):
            if alg.name not in dependent:
                opts.append(alg)
        return opts

    def getDependenciesPanel(self):
        return MultipleInputPanel([alg.algorithm.name for alg in self.getAvailableDependencies()])

    def showAdvancedParametersClicked(self):
        self.showAdvanced = not self.showAdvanced
        if self.showAdvanced:
            self.advancedButton.setText(self.tr('Hide advanced parameters'))
        else:
            self.advancedButton.setText(self.tr('Show advanced parameters'))
        for param in self._alg.parameters:
            if param.isAdvanced:
                self.labels[param.name].setVisible(self.showAdvanced)
                self.widgets[param.name].setVisible(self.showAdvanced)

    def getAvailableValuesOfType(self, paramType, outType=None, dataType=None):
        values = []
        inputs = self.model.inputs
        for i in list(inputs.values()):
            param = i.param
            if isinstance(param, paramType):
                if dataType is not None and param.datatype in dataType:
                    values.append(ValueFromInput(param.name))
                else:
                    values.append(ValueFromInput(param.name))
        if outType is None:
            return values
        if self._algName is None:
            dependent = []
        else:
            dependent = self.model.getDependentAlgorithms(self._algName)
        for alg in list(self.model.algs.values()):
            if alg.name not in dependent:
                for out in alg.algorithm.outputs:
                    if isinstance(out, outType):
                        if dataType is not None and out.datatype in dataType:
                            values.append(ValueFromOutput(alg.name, out.name))
                        else:
                            values.append(ValueFromOutput(alg.name, out.name))

        return values

    def resolveValueDescription(self, value):
        if isinstance(value, ValueFromInput):
            return self.model.inputs[value.name].param.description
        else:
            alg = self.model.algs[value.alg]
            return self.tr("'%s' from algorithm '%s'") % (alg.algorithm.getOutputFromName(value.output).description, alg.description)

    def getWidgetFromParameter(self, param):
        if isinstance(param, ParameterRaster):
            item = QComboBox()
            layers = self.getAvailableValuesOfType(ParameterRaster, OutputRaster)
            if param.optional:
                item.addItem(self.NOT_SELECTED, None)
            for layer in layers:
                item.addItem(self.resolveValueDescription(layer), layer)
        elif isinstance(param, ParameterVector):
            item = QComboBox()
            layers = self.getAvailableValuesOfType(ParameterVector, OutputVector)
            if param.optional:
                item.addItem(self.NOT_SELECTED, None)
            for layer in layers:
                item.addItem(self.resolveValueDescription(layer), layer)
        elif isinstance(param, ParameterTable):
            item = QComboBox()
            tables = self.getAvailableValuesOfType(ParameterTable, OutputTable)
            layers = self.getAvailableValuesOfType(ParameterVector, OutputVector)
            if param.optional:
                item.addItem(self.NOT_SELECTED, None)
            for table in tables:
                item.addItem(self.resolveValueDescription(table), table)
            for layer in layers:
                item.addItem(self.resolveValueDescription(layer), layer)
        elif isinstance(param, ParameterBoolean):
            item = QComboBox()
            item.addItem('Yes')
            item.addItem('No')
            bools = self.getAvailableValuesOfType(ParameterBoolean, None)
            for b in bools:
                item.addItem(self.resolveValueDescription(b), b)
            if param.default:
                item.setCurrentIndex(0)
            else:
                item.setCurrentIndex(1)
        elif isinstance(param, ParameterSelection):
            item = QComboBox()
            item.addItems(param.options)
            item.setCurrentIndex(param.default or 0)
        elif isinstance(param, ParameterFixedTable):
            item = FixedTablePanel(param)
        elif isinstance(param, ParameterRange):
            item = RangePanel(param)
        elif isinstance(param, ParameterMultipleInput):
            if param.datatype == dataobjects.TYPE_VECTOR_ANY:
                options = self.getAvailableValuesOfType(ParameterVector, OutputVector)
            elif param.datatype == dataobjects.TYPE_VECTOR_POINT:
                options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_POINT, dataobjects.TYPE_VECTOR_ANY])
            elif param.datatype == dataobjects.TYPE_VECTOR_LINE:
                options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_LINE, dataobjects.TYPE_VECTOR_ANY])
            elif param.datatype == dataobjects.TYPE_VECTOR_POLYGON:
                options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_ANY])
            elif param.datatype == dataobjects.TYPE_RASTER:
                options = self.getAvailableValuesOfType(ParameterRaster, OutputRaster)
            else:
                options = self.getAvailableValuesOfType(ParameterFile, OutputFile)
            opts = []
            for opt in options:
                opts.append(self.resolveValueDescription(opt))
            item = MultipleInputPanel(opts)
        elif isinstance(param, ParameterString):
            strings = self.getAvailableValuesOfType(ParameterString, OutputString)
            options = [(self.resolveValueDescription(s), s) for s in strings]
            if param.multiline:
                item = MultilineTextPanel(options)
                item.setText(str(param.default or ""))
            else:
                item = QComboBox()
                item.setEditable(True)
                for desc, val in options:
                    item.addItem(desc, val)
                item.setEditText(str(param.default or ""))
        elif isinstance(param, ParameterTableField):
            item = QComboBox()
            item.setEditable(True)
            fields = self.getAvailableValuesOfType(ParameterTableField, None)
            for f in fields:
                item.addItem(self.resolveValueDescription(f), f)
        elif isinstance(param, ParameterTableMultipleField):
            item = QComboBox()
            item.setEditable(True)
            fields = self.getAvailableValuesOfType(ParameterTableMultipleField, None)
            for f in fields:
                item.addItem(self.resolveValueDescription(f), f)
        elif isinstance(param, ParameterNumber):
            item = QComboBox()
            item.setEditable(True)
            numbers = self.getAvailableValuesOfType(ParameterNumber, OutputNumber)
            for n in numbers:
                item.addItem(self.resolveValueDescription(n), n)
            item.setEditText(str(param.default))
        elif isinstance(param, ParameterCrs):
            item = QComboBox()
            values = self.getAvailableValuesOfType(ParameterCrs, OutputCrs)
            for v in values:
                item.addItem(self.resolveValueDescription(v), v)
        elif isinstance(param, ParameterExtent):
            item = QComboBox()
            item.setEditable(True)
            extents = self.getAvailableValuesOfType(ParameterExtent, OutputExtent)
            if self.canUseAutoExtent():
                item.addItem(self.USE_MIN_COVERING_EXTENT, None)
            for ex in extents:
                item.addItem(self.resolveValueDescription(ex), ex)
            if not self.canUseAutoExtent():
                item.setEditText(str(param.default))
        elif isinstance(param, ParameterPoint):
            item = QComboBox()
            item.setEditable(True)
            points = self.getAvailableValuesOfType(ParameterPoint)
            for p in points:
                item.addItem(self.resolveValueDescription(p), p)
            item.setEditText(str(param.default))
        elif isinstance(param, ParameterFile):
            item = QComboBox()
            item.setEditable(True)
            files = self.getAvailableValuesOfType(ParameterFile, OutputFile)
            for f in files:
                item.addItem(self.resolveValueDescription(f), f)
        elif isinstance(param, ParameterGeometryPredicate):
            item = GeometryPredicateSelectionPanel(param.enabledPredicates)
        else:
            item = QLineEdit()
            try:
                item.setText(str(param.default))
            except:
                pass
        return item

    def canUseAutoExtent(self):
        for param in self._alg.parameters:
            if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)):
                return True
        return False

    def setTableContent(self):
        params = self._alg.parameters
        outputs = self._alg.outputs
        visibleParams = [p for p in params if not p.hidden]
        visibleOutputs = [p for o in outputs if not o.hidden]
        self.tableWidget.setRowCount(len(visibleParams) + len(visibleOutputs))

        for i, param in visibleParams:
            item = QTableWidgetItem(param.description)
            item.setFlags(Qt.ItemIsEnabled)
            self.tableWidget.setItem(i, 0, item)
            item = self.getWidgetFromParameter(param)
            self.valueItems[param.name] = item
            self.tableWidget.setCellWidget(i, 1, item)
            self.tableWidget.setRowHeight(i, 22)

        for i, output in visibleOutputs:
            item = QTableWidgetItem(output.description + '<'
                                    + output.__module__.split('.')[-1] + '>')
            item.setFlags(Qt.ItemIsEnabled)
            self.tableWidget.setItem(i, 0, item)
            item = QLineEdit()
            if hasattr(item, 'setPlaceholderText'):
                item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME)
            self.valueItems[output.name] = item
            self.tableWidget.setCellWidget(i, 1, item)
            self.tableWidget.setRowHeight(i, 22)

    def setComboBoxValue(self, combo, value, param):
        if isinstance(value, list):
            value = value[0]
        items = [combo.itemData(i) for i in range(combo.count())]
        try:
            idx = items.index(value)
            combo.setCurrentIndex(idx)
            return
        except ValueError:
            pass
        if combo.isEditable():
            if value is not None:
                combo.setEditText(str(value))
        elif isinstance(param, ParameterSelection):
            combo.setCurrentIndex(int(value))
        elif isinstance(param, ParameterBoolean):
            if value:
                combo.setCurrentIndex(0)
            else:
                combo.setCurrentIndex(1)

    def setPreviousValues(self):
        if self._algName is not None:
            alg = self.model.algs[self._algName]
            self.descriptionBox.setText(alg.description)
            for param in alg.algorithm.parameters:
                if param.hidden:
                    continue
                widget = self.valueItems[param.name]
                if param.name in alg.params:
                    value = alg.params[param.name]
                else:
                    value = param.default
                if isinstance(param, (
                        ParameterRaster,
                        ParameterVector,
                        ParameterTable,
                        ParameterTableField,
                        ParameterSelection,
                        ParameterNumber,
                        ParameterBoolean,
                        ParameterExtent,
                        ParameterFile,
                        ParameterPoint,
                        ParameterCrs
                )):
                    self.setComboBoxValue(widget, value, param)
                elif isinstance(param, ParameterString):
                    if param.multiline:
                        widget.setValue(value)
                    else:
                        self.setComboBoxValue(widget, value, param)
                elif isinstance(param, ParameterFixedTable):
                    pass  # TODO!
                elif isinstance(param, ParameterMultipleInput):
                    if param.datatype == dataobjects.TYPE_VECTOR_ANY:
                        options = self.getAvailableValuesOfType(ParameterVector, OutputVector)
                    elif param.datatype == dataobjects.TYPE_VECTOR_POINT:
                        options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_POINT, dataobjects.TYPE_VECTOR_ANY])
                    elif param.datatype == dataobjects.TYPE_VECTOR_LINE:
                        options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_LINE, dataobjects.TYPE_VECTOR_ANY])
                    elif param.datatype == dataobjects.TYPE_VECTOR_POLYGON:
                        options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_ANY])
                    elif param.datatype == dataobjects.TYPE_RASTER:
                        options = self.getAvailableValuesOfType(ParameterRaster, OutputRaster)
                    else:
                        options = self.getAvailableValuesOfType(ParameterFile, OutputFile)
                    selected = []
                    for i, opt in enumerate(options):
                        if opt in value:
                            selected.append(i)
                    widget.setSelectedItems(selected)
                elif isinstance(param, ParameterGeometryPredicate):
                    widget.setValue(value)

            for name, out in alg.outputs.items():
                widget = self.valueItems[name].setText(out.description)

            selected = []
            dependencies = self.getAvailableDependencies()
            for idx, dependency in enumerate(dependencies):
                if dependency.name in alg.dependencies:
                    selected.append(idx)

            self.dependenciesPanel.setSelectedItems(selected)

    def createAlgorithm(self):
        alg = Algorithm(self._alg.commandLineName())
        alg.setName(self.model)
        alg.description = self.descriptionBox.text()
        params = self._alg.parameters
        outputs = self._alg.outputs
        for param in params:
            if param.hidden:
                continue
            if not self.setParamValue(alg, param, self.valueItems[param.name]):
                self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description,
                                     level=QgsMessageBar.WARNING)
                return None
        for output in outputs:
            if not output.hidden:
                name = str(self.valueItems[output.name].text())
                if name.strip() != '' and name != ModelerParametersDialog.ENTER_NAME:
                    alg.outputs[output.name] = ModelerOutput(name)

        selectedOptions = self.dependenciesPanel.selectedoptions
        availableDependencies = self.getAvailableDependencies()
        for selected in selectedOptions:
            alg.dependencies.append(availableDependencies[selected].name)

        return alg

    def setParamValueLayerOrTable(self, alg, param, widget):
        idx = widget.currentIndex()
        if idx < 0:
            return False
        else:
            value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
            return True

    def setParamTableFieldValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            s = str(widget.currentText()).strip()
            if s == '':
                if param.optional:
                    alg.params[param.name] = None
                    return True
                else:
                    return False
            else:
                alg.params[param.name] = s
                return True
        else:
            alg.params[param.name] = widget.itemData(widget.currentIndex())
        return True

    def setParamStringValue(self, alg, param, widget):
        if param.multiline:
            value = widget.getValue()
            option = widget.getOption()
            if option == MultilineTextPanel.USE_TEXT:
                if value == '':
                    if param.optional:
                        alg.params[param.name] = None
                        return True
                    else:
                        return False
                else:
                    alg.params[param.name] = value
            else:
                alg.params[param.name] = value
        else:
            idx = widget.findText(widget.currentText())
            if idx < 0:
                value = widget.currentText().strip()
                if value == '':
                    if param.optional:
                        alg.params[param.name] = None
                        return True
                    else:
                        return False
                else:
                    alg.params[param.name] = value
            else:
                alg.params[param.name] = widget.itemData(widget.currentIndex())
        return True

    def setParamFileValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            value = widget.currentText()
        else:
            value = widget.itemData(widget.currentIndex())
        alg.params[param.name] = value
        return True

    def setParamNumberValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            s = widget.currentText().strip()
            if s:
                try:
                    value = float(s)
                except:
                    return False
            elif param.optional:
                value = None
            else:
                return False
        else:
            value = widget.itemData(widget.currentIndex())
        alg.params[param.name] = value
        return True

    def setParamExtentValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            s = str(widget.currentText()).strip()
            if s:
                try:
                    tokens = s.split(',')
                    if len(tokens) != 4:
                        return False
                    for token in tokens:
                        float(token)
                except:
                    return False
            elif param.optional:
                s = None
            else:
                return False
            alg.params[param.name] = [s]
        else:
            value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
        return True

    def setParamPointValue(self, alg, param, widget):
        idx = widget.findText(widget.currentText())
        if idx < 0:
            s = str(widget.currentText()).strip()
            if s:
                try:
                    tokens = s.split(',')
                    if len(tokens) != 2:
                        return False
                    for token in tokens:
                        float(token)
                except:
                    return False
            elif param.optional:
                s = None
            else:
                return False
            alg.params[param.name] = [s]
        else:
            value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
        return True

    def setParamCrsValue(self, alg, param, widget):
        idx = widget.currentIndex()
        if idx < 0:
            return False
        else:
            value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
            return True

    def setParamValue(self, alg, param, widget):
        if isinstance(param, (ParameterRaster, ParameterVector,
                              ParameterTable)):
            return self.setParamValueLayerOrTable(alg, param, widget)
        elif isinstance(param, ParameterBoolean):
            if widget.currentIndex() < 2:
                value = widget.currentIndex() == 0
            else:
                value = widget.itemData(widget.currentIndex())
            alg.params[param.name] = value
            return True
        elif isinstance(param, ParameterString):
            return self.setParamStringValue(alg, param, widget)
        elif isinstance(param, ParameterNumber):
            return self.setParamNumberValue(alg, param, widget)
        elif isinstance(param, ParameterExtent):
            return self.setParamExtentValue(alg, param, widget)
        elif isinstance(param, ParameterPoint):
            return self.setParamPointValue(alg, param, widget)
        elif isinstance(param, ParameterFile):
            return self.setParamFileValue(alg, param, widget)
        elif isinstance(param, ParameterSelection):
            alg.params[param.name] = widget.currentIndex()
            return True
        elif isinstance(param, ParameterRange):
            alg.params[param.name] = widget.getValue()
            return True
        elif isinstance(param, ParameterCrs):
            return self.setParamCrsValue(alg, param, widget)
        elif isinstance(param, ParameterFixedTable):
            table = widget.table
            if not bool(table) and not param.optional:
                return False
            alg.params[param.name] = ParameterFixedTable.tableToString(table)
            return True
        elif isinstance(param, (ParameterTableField,
                                ParameterTableMultipleField)):
            return self.setParamTableFieldValue(alg, param, widget)
        elif isinstance(param, ParameterMultipleInput):
            if param.datatype == dataobjects.TYPE_VECTOR_ANY:
                options = self.getAvailableValuesOfType(ParameterVector, OutputVector)
            elif param.datatype == dataobjects.TYPE_VECTOR_POINT:
                options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_POINT, dataobjects.TYPE_VECTOR_ANY])
            elif param.datatype == dataobjects.TYPE_VECTOR_LINE:
                options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_LINE, dataobjects.TYPE_VECTOR_ANY])
            elif param.datatype == dataobjects.TYPE_VECTOR_POLYGON:
                options = self.getAvailableValuesOfType(ParameterVector, OutputVector, [dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_ANY])
            elif param.datatype == dataobjects.TYPE_RASTER:
                options = self.getAvailableValuesOfType(ParameterRaster, OutputRaster)
            else:
                options = self.getAvailableValuesOfType(ParameterFile, OutputFile)
            values = [options[i] for i in widget.selectedoptions]
            if len(values) == 0 and not param.optional:
                return False
            alg.params[param.name] = values
            return True
        elif isinstance(param, ParameterGeometryPredicate):
            alg.params[param.name] = widget.value()
            return True
        else:
            alg.params[param.name] = str(widget.text())
            return True

    def okPressed(self):
        self.alg = self.createAlgorithm()
        if self.alg is not None:
            self.close()

    def cancelPressed(self):
        self.alg = None
        self.close()
class SelectedPopulationDockWidget(QgsDockWidget):
    """
    Dock widget for display of population of selected meshblocks
    """
    def __init__(self,
                 _iface: QgisInterface = None,
                 meshblock_layer: QgsVectorLayer = None):
        super().__init__()
        self.setWindowTitle(self.tr('Selected Meshblock Population'))
        self.meshblock_layer = meshblock_layer

        if _iface is not None:
            self.iface = _iface
        else:
            self.iface = iface

        dock_contents = QWidget()
        grid = QGridLayout(dock_contents)
        grid.setContentsMargins(0, 0, 0, 0)

        self.frame = QTextBrowser()
        self.frame.setOpenLinks(False)
        self.frame.anchorClicked.connect(self.anchor_clicked)
        grid.addWidget(self.frame, 1, 0, 1, 1)

        self.setWidget(dock_contents)

        self.meshblock_layer.selectionChanged.connect(self.selection_changed)
        self.task = None
        self.district_registry = None
        self.target_electorate = None
        self.quota = 0

    def reset(self):
        """
        Clears the current results shown in the dock
        """
        self.target_electorate = None
        self.frame.setHtml('')

    def update(self):
        """
        Refreshes the stats shown in the dock
        """
        self.selection_changed()

    def set_task(self, task: str):
        """
        Sets the current task to use when showing populations
        """
        self.task = task
        if self.district_registry:
            self.quota = self.district_registry.get_quota_for_district_type(
                self.task)

        self.target_electorate = None
        self.selection_changed()

    def set_district_registry(self, registry):
        """
        Sets the associated district registry
        """
        self.district_registry = registry

        if self.task:
            self.quota = self.district_registry.get_quota_for_district_type(
                self.task)

        self.selection_changed()

    def selection_changed(self):
        """
        Triggered when the selection in the meshblock layer changes
        """
        if not self.task or not self.district_registry:
            return

        request = QgsFeatureRequest().setFilterFids(
            self.meshblock_layer.selectedFeatureIds()).setFlags(
                QgsFeatureRequest.NoGeometry)

        counts = defaultdict(int)
        for f in self.meshblock_layer.getFeatures(request):
            electorate = f['staged_electorate']
            if self.task == 'GN':
                pop = f['offline_pop_gn']
            elif self.task == 'GS':
                pop = f['offline_pop_gs']
            else:
                pop = f['offline_pop_m']
            counts[electorate] += pop

        html = """<h3>Target Electorate: <a href="#">{}</a></h3><p>""".format(
            self.district_registry.get_district_title(self.target_electorate))

        request = QgsFeatureRequest()
        request.setFilterExpression(
            QgsExpression.createFieldEqualityExpression('type', self.task))
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes(
            ['electorate_id', 'estimated_pop', 'stats_nz_pop'],
            self.district_registry.source_layer.fields())
        original_populations = {}
        for f in self.district_registry.source_layer.getFeatures(request):
            estimated_pop = f['stats_nz_pop']
            if estimated_pop is None or estimated_pop == NULL:
                # otherwise just use existing estimated pop as starting point
                estimated_pop = f['estimated_pop']
            original_populations[f['electorate_id']] = estimated_pop

        overall = 0
        for electorate, pop in counts.items():
            if self.target_electorate:
                if electorate != self.target_electorate:
                    overall += pop

                    # use stats nz pop as initial estimate, if available
                    estimated_pop = original_populations[electorate]

                    estimated_pop -= pop
                    variance = LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
                        self.quota, estimated_pop)

                    html += """\n{}: <span style="font-weight:bold">-{}</span> (after: {}, {}{}%)<br>""".format(
                        self.district_registry.get_district_title(electorate),
                        pop, int(estimated_pop), '+' if variance > 0 else '',
                        variance)
            else:
                html += """\n{}: <span style="font-weight:bold">{}</span><br>""".format(
                    self.district_registry.get_district_title(electorate), pop)
        if self.target_electorate:
            estimated_pop = original_populations[self.target_electorate]

            estimated_pop += overall
            variance = LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
                self.quota, estimated_pop)

            html += """\n{}: <span style="font-weight:bold">+{}</span> (after: {}, {}{}%)<br>""".format(
                self.district_registry.get_district_title(
                    self.target_electorate), overall, int(estimated_pop),
                '+' if variance > 0 else '', variance)

        html += '</p>'

        self.frame.setHtml(html)

    def anchor_clicked(self):
        """
        Allows choice of "target" electorate
        """
        dlg = DistrictPicker(district_registry=self.district_registry,
                             parent=self.iface.mainWindow())
        if dlg.selected_district is None:
            return

        self.target_electorate = dlg.selected_district
        self.selection_changed()