[processing] drop WebView dependency (follow up 38f6ace4c0)

This commit is contained in:
Alexander Bruy 2016-05-27 19:04:25 +03:00
parent dd69a59655
commit cc7eb27a27
23 changed files with 147 additions and 222 deletions

View File

@ -29,6 +29,7 @@ import os
from processing.core.Processing import Processing from processing.core.Processing import Processing
class ProcessingExampleScriptsPlugin: class ProcessingExampleScriptsPlugin:
def initGui(self): def initGui(self):

View File

@ -64,11 +64,11 @@ from processing.script.ScriptAlgorithmProvider import ScriptAlgorithmProvider
from processing.algs.taudem.TauDEMAlgorithmProvider import TauDEMAlgorithmProvider from processing.algs.taudem.TauDEMAlgorithmProvider import TauDEMAlgorithmProvider
from processing.preconfigured.PreconfiguredAlgorithmProvider import PreconfiguredAlgorithmProvider from processing.preconfigured.PreconfiguredAlgorithmProvider import PreconfiguredAlgorithmProvider
class Processing: class Processing:
providers = [] providers = []
# Same structure as algs in algList # Same structure as algs in algList
actions = {} actions = {}
@ -148,7 +148,6 @@ class Processing:
provider.externalAlgs.extend(scripts) provider.externalAlgs.extend(scripts)
Processing.reloadProvider("qgis") Processing.reloadProvider("qgis")
@staticmethod @staticmethod
def removeScripts(folder): def removeScripts(folder):
provider = Processing.getProviderFromName("qgis") provider = Processing.getProviderFromName("qgis")
@ -158,7 +157,6 @@ class Processing:
provider.externalAlgs.remove(alg) provider.externalAlgs.remove(alg)
Processing.reloadProvider("qgis") Processing.reloadProvider("qgis")
@staticmethod @staticmethod
def updateAlgsList(): def updateAlgsList():
"""Call this method when there has been any change that """Call this method when there has been any change that

View File

@ -27,6 +27,7 @@ __revision__ = '$Format:%H$'
from qgis.PyQt.QtCore import QObject, pyqtSignal from qgis.PyQt.QtCore import QObject, pyqtSignal
class AlgorithmList(QObject): class AlgorithmList(QObject):
providerAdded = pyqtSignal(str) providerAdded = pyqtSignal(str)

View File

@ -26,7 +26,6 @@ __copyright__ = '(C) 2016, Victor Olaya'
__revision__ = '$Format:%H$' __revision__ = '$Format:%H$'
def loadDefaultProviders(): def loadDefaultProviders():
# this is here just to "trigger" the above imports so providers are loaded # this is here just to "trigger" the above imports so providers are loaded
# and can be found by the Processing.initialize() method # and can be found by the Processing.initialize() method

View File

@ -31,6 +31,7 @@ import webbrowser
from qgis.PyQt import uic from qgis.PyQt import uic
from qgis.PyQt.QtCore import QCoreApplication, QSettings, QByteArray, QUrl from qgis.PyQt.QtCore import QCoreApplication, QSettings, QByteArray, QUrl
from qgis.PyQt.QtWidgets import QApplication, QDialogButtonBox, QDesktopWidget from qgis.PyQt.QtWidgets import QApplication, QDialogButtonBox, QDesktopWidget
from qgis.PyQt.QtNetwork import QNetworkRequest, QNetworkReply
from qgis.utils import iface from qgis.utils import iface
from qgis.core import QgsNetworkAccessManager from qgis.core import QgsNetworkAccessManager
@ -63,9 +64,9 @@ class AlgorithmDialogBase(BASE, WIDGET):
self.setWindowTitle(self.alg.displayName()) self.setWindowTitle(self.alg.displayName())
desktop = QDesktopWidget() #~ desktop = QDesktopWidget()
if desktop.physicalDpiX() > 96: #~ if desktop.physicalDpiX() > 96:
self.textHelp.setZoomFactor(desktop.physicalDpiX() / 96) #~ self.txtHelp.setZoomFactor(desktop.physicalDpiX() / 96)
algHelp = self.alg.shortHelp() algHelp = self.alg.shortHelp()
if algHelp is None: if algHelp is None:
@ -83,20 +84,22 @@ class AlgorithmDialogBase(BASE, WIDGET):
def linkClicked(url): def linkClicked(url):
webbrowser.open(url.toString()) webbrowser.open(url.toString())
self.textShortHelp.anchorClicked.connect(linkClicked)
self.textHelp.page().setNetworkAccessManager(QgsNetworkAccessManager.instance()) self.textShortHelp.anchorClicked.connect(linkClicked)
isText, algHelp = self.alg.help() isText, algHelp = self.alg.help()
if algHelp is not None: if algHelp is not None:
algHelp = algHelp if isText else QUrl(algHelp) algHelp = algHelp if isText else QUrl(algHelp)
try: try:
if isText: if isText:
self.textHelp.setHtml(algHelp) self.txtHelp.setHtml(algHelp)
else: else:
self.textHelp.settings().clearMemoryCaches() html = self.tr('<p>Downloading algorithm help... Please wait.</p>')
self.textHelp.load(algHelp) self.txtHelp.setHtml(html)
except: rq = QNetworkRequest(algHelp)
self.reply = QgsNetworkAccessManager.instance().get(rq)
self.reply.finished.connect(self.requestFinished)
except Exception, e:
self.tabWidget.removeTab(2) self.tabWidget.removeTab(2)
else: else:
self.tabWidget.removeTab(2) self.tabWidget.removeTab(2)
@ -104,6 +107,16 @@ class AlgorithmDialogBase(BASE, WIDGET):
self.showDebug = ProcessingConfig.getSetting( self.showDebug = ProcessingConfig.getSetting(
ProcessingConfig.SHOW_DEBUG_IN_DIALOG) ProcessingConfig.SHOW_DEBUG_IN_DIALOG)
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 closeEvent(self, evt): def closeEvent(self, evt):
self.settings.setValue("/Processing/dialogBase", self.saveGeometry()) self.settings.setValue("/Processing/dialogBase", self.saveGeometry())
super(AlgorithmDialogBase, self).closeEvent(evt) super(AlgorithmDialogBase, self).closeEvent(evt)

View File

@ -35,6 +35,7 @@ from processing.algs.r.RAlgorithm import RAlgorithm
from processing.script.ScriptAlgorithm import ScriptAlgorithm from processing.script.ScriptAlgorithm import ScriptAlgorithm
from processing.core.alglist import algList from processing.core.alglist import algList
class DeleteScriptAction(ContextAction): class DeleteScriptAction(ContextAction):
SCRIPT_PYTHON = 0 SCRIPT_PYTHON = 0

View File

@ -31,6 +31,7 @@ from processing.algs.r.RAlgorithm import RAlgorithm
from processing.script.ScriptAlgorithm import ScriptAlgorithm from processing.script.ScriptAlgorithm import ScriptAlgorithm
from processing.core.alglist import algList from processing.core.alglist import algList
class EditScriptAction(ContextAction): class EditScriptAction(ContextAction):
SCRIPT_PYTHON = 0 SCRIPT_PYTHON = 0

View File

@ -208,10 +208,10 @@ class GetScriptsAndModelsDialog(BASE, WIDGET):
self.tree.addTopLevelItem(self.notinstalledItem) self.tree.addTopLevelItem(self.notinstalledItem)
self.tree.addTopLevelItem(self.uptodateItem) self.tree.addTopLevelItem(self.uptodateItem)
self.webView.setHtml(self.HELP_TEXT) self.txtHelp.setHtml(self.HELP_TEXT)
def setHelp(self, reply, item): def setHelp(self, reply, item):
"""Change the webview HTML content""" """Change the HTML content"""
QApplication.restoreOverrideCursor() QApplication.restoreOverrideCursor()
if reply.error() != QNetworkReply.NoError: if reply.error() != QNetworkReply.NoError:
html = self.tr('<h2>No detailed description available for this script</h2>') html = self.tr('<h2>No detailed description available for this script</h2>')
@ -223,14 +223,14 @@ class GetScriptsAndModelsDialog(BASE, WIDGET):
html += self.tr('<p><b>Created by:</b> %s') % getDescription(ALG_CREATOR, descriptions) html += self.tr('<p><b>Created by:</b> %s') % getDescription(ALG_CREATOR, descriptions)
html += self.tr('<p><b>Version:</b> %s') % getDescription(ALG_VERSION, descriptions) html += self.tr('<p><b>Version:</b> %s') % getDescription(ALG_VERSION, descriptions)
reply.deleteLater() reply.deleteLater()
self.webView.setHtml(html) self.txtHelp.setHtml(html)
def currentItemChanged(self, item, prev): def currentItemChanged(self, item, prev):
if isinstance(item, TreeItem): if isinstance(item, TreeItem):
url = self.urlBase + item.filename.replace(' ', '%20') + '.help' url = self.urlBase + item.filename.replace(' ', '%20') + '.help'
self.grabHTTP(url, self.setHelp, item) self.grabHTTP(url, self.setHelp, item)
else: else:
self.webView.setHtml(self.HELP_TEXT) self.txtHelp.setHtml(self.HELP_TEXT)
def getTreeBranchForState(self, filename, version): def getTreeBranchForState(self, filename, version):
if not os.path.exists(os.path.join(self.folder, filename)): if not os.path.exists(os.path.join(self.folder, filename)):

View File

@ -16,8 +16,6 @@
* * * *
*************************************************************************** ***************************************************************************
""" """
from processing.modeler.ModelerAlgorithm import ModelerAlgorithm
__author__ = 'Victor Olaya' __author__ = 'Victor Olaya'
__date__ = 'August 2012' __date__ = 'August 2012'
@ -34,6 +32,7 @@ from qgis.PyQt import uic
from qgis.PyQt.QtWidgets import QDialog, QTreeWidgetItem from qgis.PyQt.QtWidgets import QDialog, QTreeWidgetItem
from processing.core.ProcessingLog import ProcessingLog from processing.core.ProcessingLog import ProcessingLog
from processing.modeler.ModelerAlgorithm import ModelerAlgorithm
pluginPath = os.path.split(os.path.dirname(__file__))[0] pluginPath = os.path.split(os.path.dirname(__file__))[0]
WIDGET, BASE = uic.loadUiType( WIDGET, BASE = uic.loadUiType(
@ -138,7 +137,7 @@ class HelpEditionDialog(BASE, WIDGET):
self.updateHtmlView() self.updateHtmlView()
def updateHtmlView(self): def updateHtmlView(self):
self.webView.setHtml(self.getHtml()) self.txtPreview.setHtml(self.getHtml())
def getDescription(self, name): def getDescription(self, name):
if name in self.descriptions: if name in self.descriptions:

View File

@ -26,6 +26,7 @@ __copyright__ = '(C) 2012, Victor Olaya'
__revision__ = '$Format:%H$' __revision__ = '$Format:%H$'
import os import os
import codecs
from qgis.PyQt import uic from qgis.PyQt import uic
from qgis.PyQt.QtCore import QUrl from qgis.PyQt.QtCore import QUrl
@ -53,7 +54,7 @@ class ResultsDialog(BASE, WIDGET):
self.fillTree() self.fillTree()
if self.lastUrl: if self.lastUrl:
self.webView.load(self.lastUrl) self.txtResults.setHtml(self.loadResults(self.lastUrl))
def fillTree(self): def fillTree(self):
elements = ProcessingResults.getResults() elements = ProcessingResults.getResults()
@ -64,13 +65,17 @@ class ResultsDialog(BASE, WIDGET):
item = TreeResultItem(element) item = TreeResultItem(element)
item.setIcon(0, self.keyIcon) item.setIcon(0, self.keyIcon)
self.tree.addTopLevelItem(item) self.tree.addTopLevelItem(item)
self.lastUrl = QUrl(elements[-1].filename) self.lastUrl = elements[-1].filename
def changeResult(self): def changeResult(self):
item = self.tree.currentItem() item = self.tree.currentItem()
if isinstance(item, TreeResultItem): if isinstance(item, TreeResultItem):
url = QUrl(item.filename) self.txtResults.setHtml(self.loadResults(item.filename))
self.webView.load(url)
def loadResults(self, fileName):
with codecs.open(fileName, encoding='utf-8') as f:
content = f.read()
return content
class TreeResultItem(QTreeWidgetItem): class TreeResultItem(QTreeWidgetItem):

View File

@ -31,6 +31,7 @@ from processing.gui.ContextAction import ContextAction
from processing.modeler.ModelerAlgorithm import ModelerAlgorithm from processing.modeler.ModelerAlgorithm import ModelerAlgorithm
from processing.core.alglist import algList from processing.core.alglist import algList
class DeleteModelAction(ContextAction): class DeleteModelAction(ContextAction):
def __init__(self): def __init__(self):

View File

@ -30,6 +30,7 @@ from processing.modeler.ModelerAlgorithm import ModelerAlgorithm
from processing.modeler.ModelerDialog import ModelerDialog from processing.modeler.ModelerDialog import ModelerDialog
from processing.core.alglist import algList from processing.core.alglist import algList
class EditModelAction(ContextAction): class EditModelAction(ContextAction):
def __init__(self): def __init__(self):

View File

@ -26,8 +26,13 @@ __copyright__ = '(C) 2012, Victor Olaya'
__revision__ = '$Format:%H$' __revision__ = '$Format:%H$'
from qgis.PyQt.QtCore import Qt, QUrl, QMetaObject from qgis.PyQt.QtCore import Qt, QUrl, QMetaObject
from qgis.PyQt.QtWidgets import QDialog, QDialogButtonBox, QLabel, QLineEdit, QFrame, QPushButton, QSizePolicy, QVBoxLayout, QHBoxLayout, QTabWidget, QWidget, QScrollArea, QComboBox, QTableWidgetItem, QMessageBox from qgis.PyQt.QtWidgets import (QDialog, QDialogButtonBox, QLabel, QLineEdit,
from qgis.core import QgsWebView QFrame, QPushButton, QSizePolicy, QVBoxLayout,
QHBoxLayout, QTabWidget, QWidget, QScrollArea,
QComboBox, QTableWidgetItem, QMessageBox)
from qgis.PyQt.QtNetwork import QNetworkRequest, QNetworkReply
from qgis.core import QgsNetworkAccessManager
from processing.gui.CrsSelectionPanel import CrsSelectionPanel from processing.gui.CrsSelectionPanel import CrsSelectionPanel
from processing.gui.MultipleInputPanel import MultipleInputPanel from processing.gui.MultipleInputPanel import MultipleInputPanel
@ -194,27 +199,27 @@ class ModelerParametersDialog(QDialog):
self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidget(self.paramPanel)
self.scrollArea.setWidgetResizable(True) self.scrollArea.setWidgetResizable(True)
self.tabWidget.addTab(self.scrollArea, self.tr('Parameters')) self.tabWidget.addTab(self.scrollArea, self.tr('Parameters'))
self.webView = QgsWebView()
self.txtHelp = QTextBrowser()
html = None html = None
url = None url = None
isText, help = self._alg.help() isText, algHelp = self._alg.help()
if help is not None: if algHelp is not None:
if isText: algHelp = algHelp if isText else QUrl(algHelp)
html = help
else:
url = QUrl(help)
else:
html = self.tr('<h2>Sorry, no help is available for this '
'algorithm.</h2>')
try: try:
if html: if isText:
self.webView.setHtml(html) self.txtHelp.setHtml(algHelp)
elif url: else:
self.webView.load(url) 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: except:
self.webView.setHtml(self.tr('<h2>Could not open help file :-( </h2>')) self.txtHelp.setHtml(self.tr('<h2>No help available for this algorithm</h2>'))
self.tabWidget.addTab(self.webView, 'Help')
self.tabWidget.addTab(self.txtHelp, 'Help')
self.verticalLayout2.addWidget(self.tabWidget) self.verticalLayout2.addWidget(self.tabWidget)
self.verticalLayout2.addWidget(self.buttonBox) self.verticalLayout2.addWidget(self.buttonBox)
self.setLayout(self.verticalLayout2) self.setLayout(self.verticalLayout2)
@ -222,6 +227,16 @@ class ModelerParametersDialog(QDialog):
self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.rejected.connect(self.cancelPressed)
QMetaObject.connectSlotsByName(self) 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): def getAvailableDependencies(self):
if self._algName is None: if self._algName is None:
dependent = [] dependent = []

View File

@ -31,6 +31,7 @@ from processing.gui.ContextAction import ContextAction
from processing.preconfigured.PreconfiguredAlgorithm import PreconfiguredAlgorithm from processing.preconfigured.PreconfiguredAlgorithm import PreconfiguredAlgorithm
from processing.core.alglist import algList from processing.core.alglist import algList
class DeletePreconfiguredAlgorithmAction(ContextAction): class DeletePreconfiguredAlgorithmAction(ContextAction):
def __init__(self): def __init__(self):

View File

@ -47,9 +47,6 @@
</property> </property>
<item> <item>
<widget class="QTextEdit" name="txtLog"> <widget class="QTextEdit" name="txtLog">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="readOnly"> <property name="readOnly">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -69,13 +66,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QgsWebView" name="textHelp"> <widget class="QTextBrowser" name="txtHelp"/>
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -131,13 +122,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>QgsWebView</class>
<extends>QWidget</extends>
<header>qgis.core</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>

View File

@ -6,32 +6,20 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>826</width> <width>504</width>
<height>520</height> <height>523</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Get scripts and models</string> <string>Get scripts and models</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QGridLayout" name="gridLayout">
<item> <item row="0" column="0" colspan="2">
<widget class="QSplitter" name="splitter"> <widget class="QSplitter" name="splitter">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<widget class="QTreeWidget" name="tree"> <widget class="QTreeWidget" name="tree">
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>100000</height>
</size>
</property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum> <enum>QAbstractItemView::SingleSelection</enum>
</property> </property>
@ -59,42 +47,10 @@
</property> </property>
</column> </column>
</widget> </widget>
<widget class="QFrame" name="frame"> <widget class="QTextEdit" name="txtHelp"/>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QgsWebView" name="webView">
<property name="maximumSize">
<size>
<width>10000</width>
<height>10000</height>
</size>
</property>
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget> </widget>
</item> </item>
</layout> <item row="1" column="0">
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QProgressBar" name="progressBar"> <widget class="QProgressBar" name="progressBar">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
@ -116,7 +72,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="1" column="1">
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
@ -139,16 +95,7 @@
</widget> </widget>
</item> </item>
</layout> </layout>
</item>
</layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>QgsWebView</class>
<extends>QWidget</extends>
<header>qgis.core</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View File

@ -20,18 +20,14 @@
<property name="margin"> <property name="margin">
<number>9</number> <number>9</number>
</property> </property>
<item>
<widget class="QTextEdit" name="txtPreview"/>
</item>
<item> <item>
<widget class="QSplitter" name="splitter_2"> <widget class="QSplitter" name="splitter_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<widget class="QgsWebView" name="webView">
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
<widget class="QSplitter" name="splitter"> <widget class="QSplitter" name="splitter">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -110,13 +106,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>QgsWebView</class>
<extends>QWidget</extends>
<header>qgis.core</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>

View File

@ -14,24 +14,12 @@
<string>Results</string> <string>Results</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item> <item>
<widget class="QSplitter" name="splitter"> <widget class="QSplitter" name="splitter">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<widget class="QTreeWidget" name="tree"> <widget class="QTreeWidget" name="tree">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<attribute name="headerVisible"> <attribute name="headerVisible">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
@ -41,19 +29,7 @@
</property> </property>
</column> </column>
</widget> </widget>
<widget class="QgsWebView" name="webView"> <widget class="QTextEdit" name="txtResults"/>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</widget> </widget>
</item> </item>
<item> <item>
@ -68,13 +44,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>QgsWebView</class>
<extends>QWidget</extends>
<header>qgis.core</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>