mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
843 lines
37 KiB
Python
843 lines
37 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Copyright (C) 2007-2008 Matthew Perry
|
|
Copyright (C) 2008-2009 Borys Jurgiel
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
"""
|
|
|
|
|
|
from PyQt4.QtCore import *
|
|
from PyQt4.QtGui import *
|
|
from qgis.core import QgsApplication, QgsContextHelp
|
|
import sys, time
|
|
from ui_qgsplugininstallerfetchingbase import Ui_QgsPluginInstallerFetchingDialogBase
|
|
from ui_qgsplugininstallerinstallingbase import Ui_QgsPluginInstallerInstallingDialogBase
|
|
from ui_qgsplugininstallerrepositorybase import Ui_QgsPluginInstallerRepositoryDetailsDialogBase
|
|
from ui_qgsplugininstallerpluginerrorbase import Ui_QgsPluginInstallerPluginErrorDialogBase
|
|
from ui_qgsplugininstallerbase import Ui_QgsPluginInstallerDialogBase
|
|
from installer_data import *
|
|
|
|
|
|
|
|
# --- common functions ------------------------------------------------------------------- #
|
|
def removeDir(path):
|
|
result = QString()
|
|
if not QFile(path).exists():
|
|
result = QCoreApplication.translate("QgsPluginInstaller","Nothing to remove! Plugin directory doesn't exist:")+"\n"+path
|
|
elif QFile(path).remove(): # if it is only link, just remove it without resolving.
|
|
pass
|
|
else:
|
|
fltr = QDir.Dirs | QDir.Files | QDir.Hidden
|
|
iterator = QDirIterator(path, fltr, QDirIterator.Subdirectories)
|
|
while iterator.hasNext():
|
|
item = iterator.next()
|
|
if QFile(item).remove():
|
|
pass
|
|
fltr = QDir.Dirs | QDir.Hidden
|
|
iterator = QDirIterator(path, fltr, QDirIterator.Subdirectories)
|
|
while iterator.hasNext():
|
|
item = iterator.next()
|
|
if QDir().rmpath(item):
|
|
pass
|
|
if QFile(path).exists():
|
|
result = QCoreApplication.translate("QgsPluginInstaller","Failed to remove the directory:")+"\n"+path+"\n"+QCoreApplication.translate("QgsPluginInstaller","Check permissions or remove it manually")
|
|
# restore plugin directory if removed by QDir().rmpath()
|
|
pluginDir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins"
|
|
if not QDir(pluginDir).exists():
|
|
QDir().mkpath(pluginDir)
|
|
return result
|
|
# --- /common functions ------------------------------------------------------------------ #
|
|
|
|
|
|
|
|
|
|
|
|
# --- class QgsPluginInstallerFetchingDialog --------------------------------------------------------------- #
|
|
class QgsPluginInstallerFetchingDialog(QDialog, Ui_QgsPluginInstallerFetchingDialogBase):
|
|
# ----------------------------------------- #
|
|
def __init__(self, parent):
|
|
QDialog.__init__(self, parent)
|
|
self.setupUi(self)
|
|
self.progressBar.setRange(0,len(repositories.allEnabled())*100)
|
|
self.itemProgress = {}
|
|
self.item = {}
|
|
for key in repositories.allEnabled():
|
|
self.item[key] = QTreeWidgetItem(self.treeWidget)
|
|
self.item[key].setText(0,key)
|
|
if repositories.all()[key]["state"] > 1:
|
|
self.itemProgress[key] = 100
|
|
self.displayState(key,0)
|
|
else:
|
|
self.itemProgress[key] = 0
|
|
self.displayState(key,2)
|
|
self.treeWidget.resizeColumnToContents(0)
|
|
QObject.connect(repositories, SIGNAL("repositoryFetched(QString)"), self.repositoryFetched)
|
|
QObject.connect(repositories, SIGNAL("anythingChanged(QString, int, int)"), self.displayState)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def displayState(self,key,state,state2=None):
|
|
messages=[self.tr("Success"),self.tr("Resolving host name..."),self.tr("Connecting..."),self.tr("Host connected. Sending request..."),self.tr("Downloading data..."),self.tr("Idle"),self.tr("Closing connection..."),self.tr("Error")]
|
|
message = messages[state]
|
|
if state2:
|
|
message += " (%s%%)" % state2
|
|
self.item[key].setText(1,message)
|
|
|
|
if state == 4 and state2:
|
|
self.itemProgress[key] = state2
|
|
totalProgress = sum(self.itemProgress.values())
|
|
self.progressBar.setValue(totalProgress)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def repositoryFetched(self, repoName):
|
|
self.itemProgress[repoName] = 100
|
|
if repositories.all()[repoName]["state"] == 2:
|
|
self.displayState(repoName,0)
|
|
else:
|
|
self.displayState(repoName,7)
|
|
if not repositories.fetchingInProgress():
|
|
self.close()
|
|
# --- /class QgsPluginInstallerFetchingDialog -------------------------------------------------------------- #
|
|
|
|
|
|
|
|
|
|
|
|
# --- class QgsPluginInstallerRepositoryDialog ------------------------------------------------------------- #
|
|
class QgsPluginInstallerRepositoryDialog(QDialog, Ui_QgsPluginInstallerRepositoryDetailsDialogBase):
|
|
# ----------------------------------------- #
|
|
def __init__(self, parent=None):
|
|
QDialog.__init__(self, parent)
|
|
self.setupUi(self)
|
|
self.editURL.setText("http://")
|
|
self.connect(self.editName, SIGNAL("textChanged(const QString &)"), self.textChanged)
|
|
self.connect(self.editURL, SIGNAL("textChanged(const QString &)"), self.textChanged)
|
|
self.textChanged(None)
|
|
|
|
# ----------------------------------------- #
|
|
def textChanged(self, string):
|
|
enable = (self.editName.text().count() > 0 and self.editURL.text().count() > 0)
|
|
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable)
|
|
# --- /class QgsPluginInstallerRepositoryDialog ------------------------------------------------------------ #
|
|
|
|
|
|
|
|
|
|
|
|
# --- class QgsPluginInstallerInstallingDialog --------------------------------------------------------------- #
|
|
class QgsPluginInstallerInstallingDialog(QDialog, Ui_QgsPluginInstallerInstallingDialogBase):
|
|
# ----------------------------------------- #
|
|
def __init__(self, parent, plugin):
|
|
QDialog.__init__(self, parent)
|
|
self.setupUi(self)
|
|
self.plugin = plugin
|
|
self.mResult = QString()
|
|
self.progressBar.setRange(0,0)
|
|
self.progressBar.setFormat(QString("%p%"))
|
|
self.labelName.setText(QString(plugin["name"]))
|
|
self.connect(self.buttonBox, SIGNAL("clicked(QAbstractButton*)"), self.abort)
|
|
|
|
url = QUrl(plugin["url"])
|
|
path = QString(url.toPercentEncoding(url.path(), "!$&'()*+,;=:/@"))
|
|
fileName = plugin["filename"]
|
|
tmpDir = QDir.tempPath()
|
|
tmpPath = QDir.cleanPath(tmpDir+"/"+fileName)
|
|
self.file = QFile(tmpPath)
|
|
port = url.port()
|
|
if port < 0:
|
|
port = 80
|
|
self.http = QPHttp(url.host(), port)
|
|
self.connect(self.http, SIGNAL("stateChanged ( int )"), self.stateChanged)
|
|
self.connect(self.http, SIGNAL("dataReadProgress ( int , int )"), self.readProgress)
|
|
self.connect(self.http, SIGNAL("requestFinished (int, bool)"), self.requestFinished)
|
|
self.httpGetId = self.http.get(path, self.file)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def result(self):
|
|
return self.mResult
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def stateChanged(self, state):
|
|
messages=[self.tr("Installing..."),self.tr("Resolving host name..."),self.tr("Connecting..."),self.tr("Host connected. Sending request..."),self.tr("Downloading data..."),self.tr("Idle"),self.tr("Closing connection..."),self.tr("Error")]
|
|
self.labelState.setText(messages[state])
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def readProgress(self, done, total):
|
|
self.progressBar.setMaximum(total)
|
|
self.progressBar.setValue(done)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def requestFinished(self, requestId, state):
|
|
if requestId != self.httpGetId:
|
|
return
|
|
self.buttonBox.setEnabled(False)
|
|
if state:
|
|
self.mResult = self.http.errorString()
|
|
self.reject()
|
|
return
|
|
self.file.close()
|
|
pluginDir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins"
|
|
tmpPath = self.file.fileName()
|
|
# make sure that the parent directory exists
|
|
if not QDir(pluginDir).exists():
|
|
QDir().mkpath(pluginDir)
|
|
# if the target directory already exists as a link, remove the link without resolving:
|
|
QFile(pluginDir+QString(QDir.separator())+self.plugin["localdir"]).remove()
|
|
try:
|
|
un = unzip()
|
|
un.extract(unicode(tmpPath), unicode(pluginDir)) # test extract. If fails, then exception will be raised and no removing occurs
|
|
# removing old plugin files if exist
|
|
removeDir(QDir.cleanPath(pluginDir+"/"+self.plugin["localdir"])) # remove old plugin if exists
|
|
un.extract(unicode(tmpPath), unicode(pluginDir)) # final extract.
|
|
except:
|
|
self.mResult = self.tr("Failed to unzip the plugin package. Probably it's broken or missing from the repository. You may also want to make sure that you have write permission to the plugin directory:") + "\n" + pluginDir
|
|
self.reject()
|
|
return
|
|
try:
|
|
# cleaning: removing the temporary zip file
|
|
QFile(tmpPath).remove()
|
|
except:
|
|
pass
|
|
self.close()
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def abort(self):
|
|
self.http.abort()
|
|
self.mResult = self.tr("Aborted by user")
|
|
self.reject()
|
|
# --- /class QgsPluginInstallerInstallingDialog ------------------------------------------------------------- #
|
|
|
|
|
|
|
|
|
|
|
|
# --- class QgsPluginInstallerPluginErrorDialog -------------------------------------------------------------- #
|
|
class QgsPluginInstallerPluginErrorDialog(QDialog, Ui_QgsPluginInstallerPluginErrorDialogBase):
|
|
# ----------------------------------------- #
|
|
def __init__(self, parent, errorMessage):
|
|
QDialog.__init__(self, parent)
|
|
self.setupUi(self)
|
|
if not errorMessage:
|
|
errorMessage = self.tr("no error message received")
|
|
self.textBrowser.setText(errorMessage)
|
|
# --- /class QgsPluginInstallerPluginErrorDialog ------------------------------------------------------------- #
|
|
|
|
|
|
|
|
|
|
|
|
# --- class QgsPluginInstallerDialog ------------------------------------------------------------------------- #
|
|
class QgsPluginInstallerDialog(QDialog, Ui_QgsPluginInstallerDialogBase):
|
|
# ----------------------------------------- #
|
|
def __init__(self, parent, fl):
|
|
QDialog.__init__(self, parent, fl)
|
|
self.setupUi(self)
|
|
self.reposGroup = "/Qgis/plugin-repos"
|
|
|
|
self.connect(self.lineFilter, SIGNAL("textChanged (QString)"), self.filterChanged)
|
|
self.connect(self.comboFilter1, SIGNAL("currentIndexChanged (int)"), self.filterChanged)
|
|
self.connect(self.comboFilter2, SIGNAL("currentIndexChanged (int)"), self.filterChanged)
|
|
# grab clicks on trees
|
|
self.connect(self.treePlugins, SIGNAL("itemSelectionChanged()"), self.pluginTreeClicked)
|
|
self.connect(self.treeRepositories, SIGNAL("itemSelectionChanged()"), self.repositoryTreeClicked)
|
|
# buttons
|
|
self.connect(self.buttonInstall, SIGNAL("clicked()"), self.installPlugin)
|
|
self.connect(self.buttonUninstall, SIGNAL("clicked()"), self.uninstallPlugin)
|
|
self.buttonInstall.setEnabled(False)
|
|
self.buttonUninstall.setEnabled(False)
|
|
# repositories handling
|
|
self.connect(self.treeRepositories, SIGNAL("doubleClicked(QModelIndex)"), self.editRepository)
|
|
self.connect(self.buttonFetchRepositories, SIGNAL("clicked()"), self.addKnownRepositories)
|
|
self.connect(self.buttonAddRep, SIGNAL("clicked()"), self.addRepository)
|
|
self.connect(self.buttonEditRep, SIGNAL("clicked()"), self.editRepository)
|
|
self.connect(self.buttonDeleteRep, SIGNAL("clicked()"), self.deleteRepository)
|
|
self.buttonEditRep.setEnabled(False)
|
|
self.buttonDeleteRep.setEnabled(False)
|
|
# configuration widgets
|
|
self.connect(self.checkUpdates, SIGNAL("toggled (bool)"), self.changeCheckingPolicy)
|
|
self.connect(self.comboInterval, SIGNAL("currentIndexChanged (int)"), self.changeCheckingInterval)
|
|
self.connect(self.radioPluginType0, SIGNAL("toggled (bool)"), self.changePluginPolicy)
|
|
self.connect(self.radioPluginType1, SIGNAL("toggled (bool)"), self.changePluginPolicy)
|
|
self.connect(self.radioPluginType2, SIGNAL("toggled (bool)"), self.changePluginPolicy)
|
|
if repositories.checkingOnStart():
|
|
self.checkUpdates.setChecked(Qt.Checked)
|
|
else:
|
|
self.checkUpdates.setChecked(Qt.Unchecked)
|
|
interval = repositories.checkingOnStartInterval()
|
|
intervals = [0,1,3,7,14,30] # days
|
|
if intervals.count(interval):
|
|
index = intervals.index(interval)
|
|
else:
|
|
index = 1
|
|
if QGIS_VER[0] == "0":
|
|
self.label_2.setText("<b>Note: This functionality requires QGIS 1.0</b>")
|
|
self.comboInterval.setCurrentIndex(index)
|
|
self.populateMostWidgets()
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def getAllAvailablePlugins(self):
|
|
""" fetch plugins from all repositories """
|
|
repositories.load()
|
|
plugins.getAllInstalled()
|
|
|
|
for key in repositories.allEnabled():
|
|
repositories.requestFetching(key)
|
|
|
|
if repositories.fetchingInProgress():
|
|
self.fetchDlg = QgsPluginInstallerFetchingDialog(self)
|
|
self.fetchDlg.exec_()
|
|
del self.fetchDlg
|
|
for key in repositories.all():
|
|
repositories.killConnection(key)
|
|
|
|
# display error messages for every unavailable reposioty, except the case if all repositories are unavailable!
|
|
if repositories.allUnavailable() and repositories.allUnavailable() != repositories.allEnabled():
|
|
for key in repositories.allUnavailable():
|
|
QMessageBox.warning(self, self.tr("QGIS Python Plugin Installer"), self.tr("Error reading repository:") + " " + key + "\n" + repositories.all()[key]["error"])
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def populateMostWidgets(self):
|
|
self.comboFilter1.clear()
|
|
self.comboFilter1.addItem(self.tr("all repositories"))
|
|
self.treeRepositories.clear()
|
|
for key in repositories.all():
|
|
a = QTreeWidgetItem(self.treeRepositories)
|
|
a.setText(1,key)
|
|
a.setText(2,repositories.all()[key]["url"])
|
|
if repositories.all()[key]["enabled"] and repositories.all()[key]["valid"]:
|
|
if repositories.all()[key]["state"] == 2:
|
|
a.setText(0,self.tr("connected"))
|
|
a.setIcon(0,QIcon(":/plugins/installer/repoConnected.png"))
|
|
a.setToolTip(0,self.tr("This repository is connected"))
|
|
self.comboFilter1.addItem(key)
|
|
else:
|
|
a.setText(0,self.tr("unavailable"))
|
|
a.setIcon(0,QIcon(":/plugins/installer/repoUnavailable.png"))
|
|
a.setToolTip(0,self.tr("This repository is enabled, but unavailable"))
|
|
self.comboFilter1.addItem(key)
|
|
else:
|
|
a.setText(0,self.tr("disabled"))
|
|
a.setIcon(0,QIcon(":/plugins/installer/repoDisabled.png"))
|
|
if repositories.all()[key]["valid"]:
|
|
a.setToolTip(0,self.tr("This repository is disabled"))
|
|
else:
|
|
a.setToolTip(0,self.tr("This repository is blocked due to incompatibility with your Quantum GIS version"))
|
|
for i in [0,1,2]:
|
|
a.setForeground(i,QBrush(QColor(Qt.gray)))
|
|
for i in [0,1,2]:
|
|
self.treeRepositories.resizeColumnToContents(i)
|
|
self.comboFilter1.addItem(self.tr("orphans"))
|
|
# fill the status filter comboBox
|
|
self.comboFilter2.clear()
|
|
self.comboFilter2.addItem(self.tr("any status"))
|
|
self.comboFilter2.addItem(self.tr("not installed", "plural"))
|
|
self.comboFilter2.addItem(self.tr("installed", "plural"))
|
|
if plugins.isThereAnythingNew():
|
|
self.comboFilter2.addItem(self.tr("upgradeable and news"))
|
|
#set configuration widgets (dependent on the repository list)
|
|
if len(repositories.all()) == 1 or QGIS_VER[0] == "0":
|
|
self.radioPluginType0.setEnabled(False)
|
|
self.radioPluginType1.setEnabled(False)
|
|
self.radioPluginType2.setEnabled(False)
|
|
else:
|
|
self.radioPluginType0.setEnabled(True)
|
|
self.radioPluginType1.setEnabled(True)
|
|
self.radioPluginType2.setEnabled(True)
|
|
settings = QSettings()
|
|
(i, ok) = settings.value(settingsGroup+"/allowedPluginType", QVariant(2)).toInt()
|
|
if QGIS_VER[0] == "0":
|
|
self.radioPluginType1.setChecked(Qt.Checked)
|
|
elif i == 1 or len(repositories.all()) == 1:
|
|
self.radioPluginType0.setChecked(Qt.Checked)
|
|
elif i == 3:
|
|
self.radioPluginType2.setChecked(Qt.Checked)
|
|
else:
|
|
self.radioPluginType1.setChecked(Qt.Checked)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def filterChanged(self,i):
|
|
""" one of the filter widgets has been changed """
|
|
self.populatePluginTree()
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def filterCheck(self,plugin):
|
|
""" the filter for the pluginsTree """
|
|
if self.comboFilter1.currentIndex() != 0 and self.comboFilter1.currentText() != self.tr("orphans"):
|
|
if self.comboFilter1.currentText() != plugin["repository"]:
|
|
return False
|
|
elif self.comboFilter1.currentText() == self.tr("orphans"):
|
|
if plugin["status"] != "orphan":
|
|
return False
|
|
if self.comboFilter2.currentIndex() == 1 and not plugin["status"] in ["not installed","new"]:
|
|
return False
|
|
if self.comboFilter2.currentIndex() == 2 and not plugin["status"] in ["installed","upgradeable","newer","orphan"]:
|
|
return False
|
|
if self.comboFilter2.currentIndex() == 3 and not plugin["status"] in ["upgradeable","new"]:
|
|
return False
|
|
if self.lineFilter.text() == "":
|
|
return True
|
|
else:
|
|
for i in ["name","version_inst","version_avail","desc_repo","desc_local","author","status","repository"]:
|
|
item = QString(plugin[i])
|
|
if item != None:
|
|
if item.contains(self.lineFilter.text(), Qt.CaseInsensitive):
|
|
return True
|
|
return False
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def populatePluginTree(self):
|
|
""" fill up the pluginTree """
|
|
statusTips={"not installed" : self.tr("This plugin is not installed"),
|
|
"installed" : self.tr("This plugin is installed"),
|
|
"upgradeable" : self.tr("This plugin is installed, but there is an updated version available"),
|
|
"orphan" : self.tr("This plugin is installed, but I can't find it in any enabled repository"),
|
|
"new" : self.tr("This plugin is not installed and is seen for the first time"),
|
|
"newer" : self.tr("This plugin is installed and is newer than its version available in a repository"),
|
|
"incompatible" : self.tr("This plugin is incompatible with your Quantum GIS version and probably won't work."),
|
|
"dependent" : self.tr("The required Python module is not installed.\nFor more information, please visit its homepage and Quantum GIS wiki."),
|
|
"broken" : self.tr("This plugin seems to be broken.\nIt has been installed but can't be loaded.\nHere is the error message:")}
|
|
statuses ={"not installed" : self.tr("not installed", "singular"),
|
|
"installed" : self.tr("installed", "singular"),
|
|
"upgradeable" : self.tr("upgradeable", "singular"),
|
|
"orphan" : self.tr("installed", "singular"),
|
|
"new" : self.tr("new!", "singular"),
|
|
"newer" : self.tr("installed", "singular"),
|
|
"incompatible" : self.tr("invalid", "singular"),
|
|
"dependent" : self.tr("invalid", "singular"),
|
|
"broken" : self.tr("invalid", "singular")}
|
|
orderInvalid = ["incompatible","broken","dependent"]
|
|
orderValid = ["upgradeable","new","not installed","installed","orphan","newer"]
|
|
def addItem(p):
|
|
if self.filterCheck(p):
|
|
statusTip = statusTips[p["status"]]
|
|
if p["read-only"]:
|
|
statusTip = statusTip + "\n" + self.tr("Note that it's an uninstallable core plugin")
|
|
installedVersion = p["version_inst"]
|
|
if not installedVersion:
|
|
installedVersion = "?"
|
|
availableVersion = p["version_avail"]
|
|
if not availableVersion:
|
|
availableVersion = "?"
|
|
if p["status"] == "upgradeable":
|
|
ver = installedVersion + " -> " + availableVersion
|
|
elif p["status"] == "newer":
|
|
ver = installedVersion + " (" + availableVersion + ")"
|
|
elif p["status"] in ["not installed", "new"]:
|
|
ver = availableVersion
|
|
else:
|
|
ver = installedVersion
|
|
if p["status"] in ["upgradeable","newer"] or p["error"]:
|
|
verTip = self.tr("installed version") + ": " + installedVersion + "\n" + self.tr("available version") + ": " + availableVersion
|
|
elif p["status"] in ["not installed", "new"]:
|
|
verTip = self.tr("available version") + ": " + availableVersion
|
|
elif p["status"] == "installed":
|
|
verTip = self.tr("installed version") + ": " + installedVersion + "\n" + self.tr("That's the newest available version")
|
|
elif p["status"] == "orphan":
|
|
verTip = self.tr("installed version") + ": " + installedVersion + "\n" + self.tr("There is no version available for download")
|
|
else:
|
|
verTip = ""
|
|
if p["error"] == "broken":
|
|
desc = self.tr("This plugin is broken")
|
|
descTip = statusTips[p["error"]] + "\n" + p["error_details"]
|
|
statusTip = descTip
|
|
elif p["error"] == "incompatible":
|
|
desc = self.tr("This plugin requires a newer version of Quantum GIS") + " (" + self.tr("at least")+ " " + p["error_details"] + ")"
|
|
descTip = statusTips[p["error"]]
|
|
statusTip = descTip
|
|
elif p["error"] == "dependent":
|
|
desc = self.tr("This plugin requires a missing module") + " (" + p["error_details"] + ")"
|
|
descTip = statusTips[p["error"]]
|
|
statusTip = descTip
|
|
else:
|
|
desc = p["desc_local"]
|
|
descTip = p["desc_repo"]
|
|
if not desc:
|
|
desc = descTip
|
|
if not p["repository"]:
|
|
repository = self.tr("only locally available")
|
|
else:
|
|
repository = p["repository"]
|
|
a = QTreeWidgetItem(self.treePlugins)
|
|
if p["error"]:
|
|
a.setText(0,statuses[p["error"]])
|
|
else:
|
|
a.setText(0,statuses[p["status"]])
|
|
a.setToolTip(0,statusTip)
|
|
a.setText(1,p["name"])
|
|
a.setText(2,ver)
|
|
a.setToolTip(2,verTip)
|
|
a.setText(3,desc)
|
|
a.setToolTip(3,descTip)
|
|
a.setText(4,p["author"])
|
|
if p["homepage"]:
|
|
a.setToolTip(4,p["homepage"])
|
|
else:
|
|
a.setToolTip(4,"")
|
|
a.setText(5,repository)
|
|
a.setToolTip(5,p["url"])
|
|
# set fonts and colours
|
|
for i in [0,1,2,3,4,5]:
|
|
if p["error"]:
|
|
a.setForeground(i,QBrush(QColor(Qt.red)))
|
|
if p["status"] in ["new","upgradeable"] or p["error"]:
|
|
font = QFont()
|
|
font.setWeight(QFont.Bold)
|
|
a.setFont(i,font)
|
|
# -------- #
|
|
if not plugins.all():
|
|
return
|
|
self.treePlugins.clear()
|
|
for i in orderInvalid:
|
|
for p in plugins.all().values():
|
|
if p["error"] == i:
|
|
addItem(p)
|
|
for i in orderValid:
|
|
for p in plugins.all().values():
|
|
if p["status"] == i and not p["error"]:
|
|
addItem(p)
|
|
# resize the columns
|
|
for i in [0,1,2,3,4,5]:
|
|
self.treePlugins.resizeColumnToContents(i)
|
|
for i in [0,1,2,4,5]:
|
|
if self.treePlugins.columnWidth(i) > 260:
|
|
self.treePlugins.setColumnWidth(i, 260)
|
|
if self.treePlugins.columnWidth(3) > 560:
|
|
self.treePlugins.setColumnWidth(3, 560)
|
|
# initially, keep order of inserting
|
|
self.treePlugins.sortItems(100,Qt.AscendingOrder)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def pluginTreeClicked(self):
|
|
""" the pluginsTree has been clicked """
|
|
buttons={"not installed":(True,False,self.tr("Install plugin")),
|
|
"installed":(True,True,self.tr("Reinstall plugin")),
|
|
"upgradeable":(True,True,self.tr("Upgrade plugin")),
|
|
"orphan":(False,True,self.tr("Install/upgrade plugin")),
|
|
"new":(True,False,self.tr("Install plugin")),
|
|
"newer":(True,True,self.tr("Downgrade plugin"))}
|
|
self.buttonInstall.setEnabled(False)
|
|
self.buttonInstall.setText(self.tr("Install/upgrade plugin"))
|
|
self.buttonUninstall.setEnabled(False)
|
|
if not self.treePlugins.selectedItems():
|
|
return
|
|
item = self.treePlugins.currentItem()
|
|
if not item:
|
|
return
|
|
key = plugins.keyByUrl(item.toolTip(5))
|
|
if not key:
|
|
return
|
|
plugin = plugins.all()[key]
|
|
if not plugin:
|
|
return
|
|
self.buttonInstall.setEnabled(buttons[plugin["status"]][0])
|
|
self.buttonUninstall.setEnabled(buttons[plugin["status"]][1])
|
|
self.buttonInstall.setText(buttons[plugin["status"]][2])
|
|
if plugin["read-only"]:
|
|
self.buttonUninstall.setEnabled(False)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def installPlugin(self):
|
|
""" install currently selected plugin """
|
|
if not self.treePlugins.currentItem():
|
|
return
|
|
infoString = ('','')
|
|
key = plugins.keyByUrl(self.treePlugins.currentItem().toolTip(5))
|
|
plugin = plugins.all()[key]
|
|
previousStatus = plugin["status"]
|
|
if not plugin:
|
|
return
|
|
if plugin["status"] == "newer" and not plugin["error"]: # ask for confirmation if user downgrades an usable plugin
|
|
if QMessageBox.warning(self, self.tr("QGIS Python Plugin Installer"), self.tr("Are you sure you want to downgrade the plugin to the latest available version? The installed one is newer!"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.No:
|
|
return
|
|
dlg = QgsPluginInstallerInstallingDialog(self,plugin)
|
|
dlg.exec_()
|
|
|
|
if dlg.result():
|
|
infoString = (self.tr("Plugin installation failed"), dlg.result())
|
|
elif not QDir(QDir.cleanPath(QgsApplication.qgisSettingsDirPath() + "/python/plugins/" + key)).exists():
|
|
infoString = (self.tr("Plugin has disappeared"), self.tr("The plugin seems to have been installed but I don't know where. Probably the plugin package contained a wrong named directory.\nPlease search the list of installed plugins. I'm nearly sure you'll find the plugin there, but I just can't determine which of them it is. It also means that I won't be able to determine if this plugin is installed and inform you about available updates. However the plugin may work. Please contact the plugin author and submit this issue."))
|
|
QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
plugins.getAllInstalled()
|
|
plugins.rebuild()
|
|
QApplication.restoreOverrideCursor()
|
|
else:
|
|
try:
|
|
exec ("sys.path_importer_cache.clear()")
|
|
exec ("import %s" % plugin["localdir"])
|
|
exec ("reload (%s)" % plugin["localdir"])
|
|
except:
|
|
pass
|
|
plugins.getAllInstalled()
|
|
plugins.rebuild()
|
|
plugin = plugins.all()[key]
|
|
if not plugin["error"]:
|
|
if previousStatus in ["not installed", "new"]:
|
|
infoString = (self.tr("Plugin installed successfully"), self.tr("Python plugin installed.\nNow you need to enable it in Plugin Manager."))
|
|
else:
|
|
infoString = (self.tr("Plugin reinstalled successfully"), self.tr("Python plugin reinstalled.\nYou need to restart Quantum GIS in order to reload it."))
|
|
else:
|
|
if plugin["error"] == "incompatible":
|
|
message = self.tr("The plugin is designed for a newer version of Quantum GIS. The minimum required version is:")
|
|
message += " <b>" + plugin["error_details"] + "</b>"
|
|
elif plugin["error"] == "dependent":
|
|
message = self.tr("The plugin depends on some components missing on your system. You need to install the following Python module in order to enable it:")
|
|
message += "<b> " + plugin["error_details"] + "</b>"
|
|
else:
|
|
message = self.tr("The plugin is broken. Python said:")
|
|
message += "<br><b>" + plugin["error_details"] + "</b>"
|
|
dlg = QgsPluginInstallerPluginErrorDialog(self,message)
|
|
dlg.exec_()
|
|
if dlg.result():
|
|
# revert installation
|
|
plugins.getAllInstalled()
|
|
plugins.rebuild()
|
|
pluginDir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins/" + plugin["localdir"]
|
|
removeDir(pluginDir)
|
|
if QDir(pluginDir).exists():
|
|
infoString = (self.tr("Plugin uninstall failed"), result)
|
|
try:
|
|
exec ("sys.path_importer_cache.clear()")
|
|
exec ("import %s" % plugin["localdir"])
|
|
exec ("reload (%s)" % plugin["localdir"])
|
|
except:
|
|
pass
|
|
else:
|
|
try:
|
|
exec ("del sys.modules[%s]" % plugin["localdir"])
|
|
except:
|
|
pass
|
|
plugins.getAllInstalled()
|
|
plugins.rebuild()
|
|
if plugins.all().has_key(key) and not plugins.all()[key]["status"] in ["not installed", "new"]:
|
|
if previousStatus in ["not installed", "new"]:
|
|
history.markChange(key,'A')
|
|
else:
|
|
history.markChange(key,'R')
|
|
|
|
self.populatePluginTree()
|
|
if infoString[0]:
|
|
QMessageBox.information(self, infoString[0], infoString[1])
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def uninstallPlugin(self):
|
|
""" uninstall currently selected plugin """
|
|
if not self.treePlugins.currentItem():
|
|
return
|
|
key = plugins.keyByUrl(self.treePlugins.currentItem().toolTip(5))
|
|
plugin = plugins.all()[key]
|
|
if not plugin:
|
|
return
|
|
warning = self.tr("Are you sure you want to uninstall the following plugin?") + "\n" + plugin["name"]
|
|
if plugin["status"] == "orphan" and not plugin["error"]:
|
|
warning += "\n\n"+self.tr("Warning: this plugin isn't available in any accessible repository!")
|
|
if QMessageBox.warning(self, self.tr("QGIS Python Plugin Installer"), warning , QMessageBox.Yes, QMessageBox.No) == QMessageBox.No:
|
|
return
|
|
pluginDir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins/" + plugin["localdir"]
|
|
result = removeDir(pluginDir)
|
|
if result:
|
|
QMessageBox.warning(self, self.tr("Plugin uninstall failed"), result)
|
|
else:
|
|
# safe remove
|
|
try:
|
|
exec ("plugins[%s].unload()" % plugin["localdir"])
|
|
exec ("del plugins[%s]" % plugin["localdir"])
|
|
except:
|
|
pass
|
|
try:
|
|
exec ("del sys.modules[%s]" % plugin["localdir"])
|
|
except:
|
|
pass
|
|
plugins.getAllInstalled()
|
|
plugins.rebuild()
|
|
self.populatePluginTree()
|
|
QMessageBox.information(self, self.tr("Plugin uninstalled successfully"), self.tr("Python plugin uninstalled. Note that you may need to restart Quantum GIS in order to remove it completely."))
|
|
history.markChange(key,'D')
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def repositoryTreeClicked(self):
|
|
""" the repositoryTree has been clicked """
|
|
if self.treeRepositories.selectedItems():
|
|
self.buttonEditRep.setEnabled(True)
|
|
self.buttonDeleteRep.setEnabled(True)
|
|
else:
|
|
self.buttonEditRep.setEnabled(False)
|
|
self.buttonDeleteRep.setEnabled(False)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def changeCheckingPolicy(self,policy):
|
|
""" the Checking On Start checkbox has been clicked """
|
|
if policy:
|
|
repositories.setCheckingOnStart(True)
|
|
else:
|
|
repositories.setCheckingOnStart(False)
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def changeCheckingInterval(self,interval):
|
|
""" the Checking on start interval combobox has been clicked """
|
|
intervals = [0,1,3,7,14,30]
|
|
repositories.setCheckingOnStartInterval(intervals[interval])
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def changePluginPolicy(self, state):
|
|
""" one of the plugin type radiobuttons has been clicked """
|
|
if not state: # radio button released
|
|
return
|
|
if self.radioPluginType0.isChecked():
|
|
i = 1
|
|
elif self.radioPluginType1.isChecked():
|
|
i = 2
|
|
else:
|
|
i = 3
|
|
settings = QSettings()
|
|
settings.setValue(settingsGroup+"/allowedPluginType", QVariant(i))
|
|
plugins.rebuild()
|
|
self.populatePluginTree()
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def addKnownRepositories(self):
|
|
""" update list of known repositories - in the future it will be replaced with an online fetching """
|
|
message = self.tr("You are about to add several plugin repositories that are neither authorized nor supported by the Quantum GIS team. Plugin authors generally make efforts to ensure that their work is useful and safe, however, we can assume no responsibility for them.")
|
|
if QMessageBox.question(self, self.tr("QGIS Python Plugin Installer"), message, QMessageBox.Ok, QMessageBox.Abort) == QMessageBox.Ok:
|
|
repositories.addKnownRepos()
|
|
# refresh lists and populate widgets
|
|
QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
self.getAllAvailablePlugins()
|
|
plugins.rebuild()
|
|
self.populateMostWidgets()
|
|
self.populatePluginTree()
|
|
QApplication.restoreOverrideCursor()
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def addRepository(self):
|
|
""" add repository button has been clicked """
|
|
dlg = QgsPluginInstallerRepositoryDialog(self)
|
|
dlg.checkBoxEnabled.setCheckState(Qt.Checked)
|
|
if not dlg.exec_():
|
|
return
|
|
for i in repositories.all().values():
|
|
if dlg.editURL.text().trimmed() == i["url"]:
|
|
QMessageBox.warning(self, self.tr("QGIS Python Plugin Installer"), self.tr("Unable to add another repository with the same URL!"))
|
|
return
|
|
settings = QSettings()
|
|
settings.beginGroup(self.reposGroup)
|
|
reposName = dlg.editName.text()
|
|
reposURL = dlg.editURL.text().trimmed()
|
|
if repositories.all().has_key(reposName):
|
|
reposName = reposName + "(2)"
|
|
# add to settings
|
|
settings.setValue(reposName+"/url", QVariant(reposURL))
|
|
settings.setValue(reposName+"/enabled", QVariant(bool(dlg.checkBoxEnabled.checkState())))
|
|
# refresh lists and populate widgets
|
|
QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
plugins.removeRepository(reposName)
|
|
self.getAllAvailablePlugins()
|
|
plugins.rebuild()
|
|
self.populateMostWidgets()
|
|
self.populatePluginTree()
|
|
QApplication.restoreOverrideCursor()
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def editRepository(self):
|
|
""" edit repository button has been clicked """
|
|
checkState={False:Qt.Unchecked,True:Qt.Checked}
|
|
current = self.treeRepositories.currentItem()
|
|
if current == None:
|
|
return
|
|
reposName = current.text(1)
|
|
dlg = QgsPluginInstallerRepositoryDialog(self)
|
|
dlg.editName.setText(reposName)
|
|
dlg.editURL.setText(repositories.all()[reposName]["url"])
|
|
dlg.checkBoxEnabled.setCheckState(checkState[repositories.all()[reposName]["enabled"]])
|
|
if repositories.all()[reposName]["valid"]:
|
|
dlg.checkBoxEnabled.setEnabled(True)
|
|
dlg.labelInfo.setText("")
|
|
else:
|
|
dlg.checkBoxEnabled.setEnabled(False)
|
|
dlg.labelInfo.setText(self.tr("This repository is blocked due to incompatibility with your Quantum GIS version"))
|
|
dlg.labelInfo.setFrameShape(QFrame.Box)
|
|
if not dlg.exec_():
|
|
return # nothing to do if cancelled
|
|
for i in repositories.all().values():
|
|
if dlg.editURL.text().trimmed() == i["url"] and dlg.editURL.text().trimmed() != repositories.all()[reposName]["url"]:
|
|
QMessageBox.warning(self, self.tr("QGIS Python Plugin Installer"), self.tr("Unable to add another repository with the same URL!"))
|
|
return
|
|
# delete old repo from QSettings and create new one
|
|
settings = QSettings()
|
|
settings.beginGroup(self.reposGroup)
|
|
settings.remove(reposName)
|
|
newName = dlg.editName.text()
|
|
if repositories.all().has_key(newName) and newName != reposName:
|
|
newName = newName + "(2)"
|
|
settings.setValue(newName+"/url", QVariant(dlg.editURL.text().trimmed()))
|
|
settings.setValue(newName+"/enabled", QVariant(bool(dlg.checkBoxEnabled.checkState())))
|
|
if dlg.editURL.text().trimmed() == repositories.all()[reposName]["url"] and dlg.checkBoxEnabled.checkState() == checkState[repositories.all()[reposName]["enabled"]]:
|
|
repositories.rename(reposName, newName)
|
|
self.populateMostWidgets()
|
|
return # nothing else to do if only repository name was changed
|
|
# refresh lists and populate widgets
|
|
QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
plugins.removeRepository(reposName)
|
|
self.getAllAvailablePlugins()
|
|
plugins.rebuild()
|
|
self.populateMostWidgets()
|
|
self.populatePluginTree()
|
|
QApplication.restoreOverrideCursor()
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def deleteRepository(self):
|
|
""" delete repository button has been clicked """
|
|
current = self.treeRepositories.currentItem()
|
|
if current == None:
|
|
return
|
|
warning = self.tr("Are you sure you want to remove the following repository?") + "\n" + current.text(1)
|
|
if QMessageBox.warning(self, self.tr("QGIS Python Plugin Installer"), warning , QMessageBox.Yes, QMessageBox.No) == QMessageBox.No:
|
|
return
|
|
reposName = current.text(1)
|
|
# delete from the settings, refresh data and repopulate all the widgets
|
|
settings = QSettings()
|
|
settings.beginGroup(self.reposGroup)
|
|
settings.remove(reposName)
|
|
repositories.remove(reposName)
|
|
plugins.removeRepository(reposName)
|
|
plugins.rebuild()
|
|
self.populateMostWidgets()
|
|
self.populatePluginTree()
|
|
|
|
|
|
# ----------------------------------------- #
|
|
def reject(self):
|
|
""" update the list of seen plugins before exit (both 'done' and 'x' buttons emit 'reject' signal) """
|
|
plugins.updateSeenPluginsList()
|
|
QDialog.reject(self)
|
|
# --- /class QgsPluginInstallerDialog ------------------------------------------------------------------------ #
|