eric6/PluginManager/PluginUninstallDialog.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7023
b025e93cc27d
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog for plugin deinstallation.
8 """
9
10 from __future__ import unicode_literals
11
12 import sys
13 import os
14 import imp
15 import shutil
16 import glob
17
18 from PyQt5.QtCore import pyqtSlot, pyqtSignal
19 from PyQt5.QtWidgets import QWidget, QDialog, QDialogButtonBox, QVBoxLayout
20
21 from E5Gui import E5MessageBox
22 from E5Gui.E5MainWindow import E5MainWindow
23 from E5Gui.E5Application import e5App
24
25 from .Ui_PluginUninstallDialog import Ui_PluginUninstallDialog
26
27 import Preferences
28 import UI.PixmapCache
29
30
31 class PluginUninstallWidget(QWidget, Ui_PluginUninstallDialog):
32 """
33 Class implementing a dialog for plugin deinstallation.
34
35 @signal accepted() emitted to indicate the removal of a plug-in
36 """
37 accepted = pyqtSignal()
38
39 def __init__(self, pluginManager, parent=None):
40 """
41 Constructor
42
43 @param pluginManager reference to the plugin manager object
44 @param parent parent of this dialog (QWidget)
45 """
46 super(PluginUninstallWidget, self).__init__(parent)
47 self.setupUi(self)
48
49 if pluginManager is None:
50 # started as external plugin deinstaller
51 from .PluginManager import PluginManager
52 self.__pluginManager = PluginManager(doLoadPlugins=False)
53 self.__external = True
54 else:
55 self.__pluginManager = pluginManager
56 self.__external = False
57
58 self.pluginDirectoryCombo.addItem(
59 self.tr("User plugins directory"),
60 self.__pluginManager.getPluginDir("user"))
61
62 globalDir = self.__pluginManager.getPluginDir("global")
63 if globalDir is not None and os.access(globalDir, os.W_OK):
64 self.pluginDirectoryCombo.addItem(
65 self.tr("Global plugins directory"),
66 globalDir)
67
68 msh = self.minimumSizeHint()
69 self.resize(max(self.width(), msh.width()), msh.height())
70
71 @pyqtSlot(int)
72 def on_pluginDirectoryCombo_currentIndexChanged(self, index):
73 """
74 Private slot to populate the plugin name combo upon a change of the
75 plugin area.
76
77 @param index index of the selected item (integer)
78 """
79 pluginDirectory = self.pluginDirectoryCombo.itemData(index)
80 pluginNames = sorted(self.__pluginManager.getPluginModules(
81 pluginDirectory))
82 self.pluginNameCombo.clear()
83 for pluginName in pluginNames:
84 fname = "{0}.py".format(os.path.join(pluginDirectory, pluginName))
85 self.pluginNameCombo.addItem(pluginName, fname)
86 self.buttonBox.button(QDialogButtonBox.Ok)\
87 .setEnabled(self.pluginNameCombo.currentText() != "")
88
89 @pyqtSlot()
90 def on_buttonBox_accepted(self):
91 """
92 Private slot to handle the accepted signal of the button box.
93 """
94 if self.__uninstallPlugin():
95 self.accepted.emit()
96
97 def __uninstallPlugin(self):
98 """
99 Private slot to uninstall the selected plugin.
100
101 @return flag indicating success (boolean)
102 """
103 pluginDirectory = self.pluginDirectoryCombo\
104 .itemData(self.pluginDirectoryCombo.currentIndex())
105 pluginName = self.pluginNameCombo.currentText()
106 pluginFile = self.pluginNameCombo\
107 .itemData(self.pluginNameCombo.currentIndex())
108
109 if not self.__pluginManager.unloadPlugin(pluginName):
110 E5MessageBox.critical(
111 self,
112 self.tr("Plugin Uninstallation"),
113 self.tr(
114 """<p>The plugin <b>{0}</b> could not be unloaded."""
115 """ Aborting...</p>""").format(pluginName))
116 return False
117
118 if pluginDirectory not in sys.path:
119 sys.path.insert(2, pluginDirectory)
120 module = imp.load_source(pluginName, pluginFile)
121 if not hasattr(module, "packageName"):
122 E5MessageBox.critical(
123 self,
124 self.tr("Plugin Uninstallation"),
125 self.tr(
126 """<p>The plugin <b>{0}</b> has no 'packageName'"""
127 """ attribute. Aborting...</p>""").format(pluginName))
128 return False
129
130 package = getattr(module, "packageName")
131 if package is None:
132 package = "None"
133 packageDir = ""
134 else:
135 packageDir = os.path.join(pluginDirectory, package)
136 if hasattr(module, "prepareUninstall") and \
137 not self.keepConfigurationCheckBox.isChecked():
138 module.prepareUninstall()
139 internalPackages = []
140 if hasattr(module, "internalPackages"):
141 # it is a comma separated string
142 internalPackages = [p.strip() for p in
143 module.internalPackages.split(",")]
144 del module
145
146 # clean sys.modules
147 self.__pluginManager.removePluginFromSysModules(
148 pluginName, package, internalPackages)
149
150 try:
151 if packageDir and os.path.exists(packageDir):
152 shutil.rmtree(packageDir)
153
154 fnameo = "{0}o".format(pluginFile)
155 if os.path.exists(fnameo):
156 os.remove(fnameo)
157
158 fnamec = "{0}c".format(pluginFile)
159 if os.path.exists(fnamec):
160 os.remove(fnamec)
161
162 pluginDirCache = os.path.join(
163 os.path.dirname(pluginFile), "__pycache__")
164 if os.path.exists(pluginDirCache):
165 pluginFileName = os.path.splitext(
166 os.path.basename(pluginFile))[0]
167 for fnameo in glob.glob(os.path.join(
168 pluginDirCache, "{0}*.pyo".format(pluginFileName))):
169 os.remove(fnameo)
170 for fnamec in glob.glob(os.path.join(
171 pluginDirCache, "{0}*.pyc".format(pluginFileName))):
172 os.remove(fnamec)
173
174 os.remove(pluginFile)
175 except OSError as err:
176 E5MessageBox.critical(
177 self,
178 self.tr("Plugin Uninstallation"),
179 self.tr(
180 """<p>The plugin package <b>{0}</b> could not be"""
181 """ removed. Aborting...</p>"""
182 """<p>Reason: {1}</p>""").format(packageDir, str(err)))
183 return False
184
185 if not self.__external:
186 ui = e5App().getObject("UserInterface")
187 if ui.notificationsEnabled():
188 ui.showNotification(
189 UI.PixmapCache.getPixmap("plugin48.png"),
190 self.tr("Plugin Uninstallation"),
191 self.tr(
192 """<p>The plugin <b>{0}</b> was uninstalled"""
193 """ successfully from {1}.</p>""")
194 .format(pluginName, pluginDirectory))
195 return True
196
197 E5MessageBox.information(
198 self,
199 self.tr("Plugin Uninstallation"),
200 self.tr(
201 """<p>The plugin <b>{0}</b> was uninstalled successfully"""
202 """ from {1}.</p>""")
203 .format(pluginName, pluginDirectory))
204 return True
205
206
207 class PluginUninstallDialog(QDialog):
208 """
209 Class for the dialog variant.
210 """
211 def __init__(self, pluginManager, parent=None):
212 """
213 Constructor
214
215 @param pluginManager reference to the plugin manager object
216 @param parent reference to the parent widget (QWidget)
217 """
218 super(PluginUninstallDialog, self).__init__(parent)
219 self.setSizeGripEnabled(True)
220
221 self.__layout = QVBoxLayout(self)
222 self.__layout.setContentsMargins(0, 0, 0, 0)
223 self.setLayout(self.__layout)
224
225 self.cw = PluginUninstallWidget(pluginManager, self)
226 size = self.cw.size()
227 self.__layout.addWidget(self.cw)
228 self.resize(size)
229 self.setWindowTitle(self.cw.windowTitle())
230
231 self.cw.buttonBox.accepted.connect(self.accept)
232 self.cw.buttonBox.rejected.connect(self.reject)
233
234
235 class PluginUninstallWindow(E5MainWindow):
236 """
237 Main window class for the standalone dialog.
238 """
239 def __init__(self, parent=None):
240 """
241 Constructor
242
243 @param parent reference to the parent widget (QWidget)
244 """
245 super(PluginUninstallWindow, self).__init__(parent)
246 self.cw = PluginUninstallWidget(None, self)
247 size = self.cw.size()
248 self.setCentralWidget(self.cw)
249 self.resize(size)
250 self.setWindowTitle(self.cw.windowTitle())
251
252 self.setStyle(Preferences.getUI("Style"),
253 Preferences.getUI("StyleSheet"))
254
255 self.cw.buttonBox.accepted.connect(self.close)
256 self.cw.buttonBox.rejected.connect(self.close)

eric ide

mercurial