src/eric7/PluginManager/PluginUninstallDialog.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
27 27
28 28
29 class PluginUninstallWidget(QWidget, Ui_PluginUninstallDialog): 29 class PluginUninstallWidget(QWidget, Ui_PluginUninstallDialog):
30 """ 30 """
31 Class implementing a dialog for plugin deinstallation. 31 Class implementing a dialog for plugin deinstallation.
32 32
33 @signal accepted() emitted to indicate the removal of a plug-in 33 @signal accepted() emitted to indicate the removal of a plug-in
34 """ 34 """
35
35 accepted = pyqtSignal() 36 accepted = pyqtSignal()
36 37
37 def __init__(self, pluginManager, parent=None): 38 def __init__(self, pluginManager, parent=None):
38 """ 39 """
39 Constructor 40 Constructor
40 41
41 @param pluginManager reference to the plugin manager object 42 @param pluginManager reference to the plugin manager object
42 @param parent parent of this dialog (QWidget) 43 @param parent parent of this dialog (QWidget)
43 """ 44 """
44 super().__init__(parent) 45 super().__init__(parent)
45 self.setupUi(self) 46 self.setupUi(self)
46 47
47 if pluginManager is None: 48 if pluginManager is None:
48 # started as external plugin deinstaller 49 # started as external plugin deinstaller
49 from .PluginManager import PluginManager 50 from .PluginManager import PluginManager
51
50 self.__pluginManager = PluginManager(doLoadPlugins=False) 52 self.__pluginManager = PluginManager(doLoadPlugins=False)
51 self.__external = True 53 self.__external = True
52 else: 54 else:
53 self.__pluginManager = pluginManager 55 self.__pluginManager = pluginManager
54 self.__external = False 56 self.__external = False
55 57
56 self.pluginDirectoryCombo.addItem( 58 self.pluginDirectoryCombo.addItem(
57 self.tr("User plugins directory"), 59 self.tr("User plugins directory"), self.__pluginManager.getPluginDir("user")
58 self.__pluginManager.getPluginDir("user")) 60 )
59 61
60 globalDir = self.__pluginManager.getPluginDir("global") 62 globalDir = self.__pluginManager.getPluginDir("global")
61 if globalDir is not None and os.access(globalDir, os.W_OK): 63 if globalDir is not None and os.access(globalDir, os.W_OK):
62 self.pluginDirectoryCombo.addItem( 64 self.pluginDirectoryCombo.addItem(
63 self.tr("Global plugins directory"), 65 self.tr("Global plugins directory"), globalDir
64 globalDir) 66 )
65 67
66 @pyqtSlot(int) 68 @pyqtSlot(int)
67 def on_pluginDirectoryCombo_currentIndexChanged(self, index): 69 def on_pluginDirectoryCombo_currentIndexChanged(self, index):
68 """ 70 """
69 Private slot to populate the plugin name combo upon a change of the 71 Private slot to populate the plugin name combo upon a change of the
70 plugin area. 72 plugin area.
71 73
72 @param index index of the selected item (integer) 74 @param index index of the selected item (integer)
73 """ 75 """
74 pluginDirectory = self.pluginDirectoryCombo.itemData(index) 76 pluginDirectory = self.pluginDirectoryCombo.itemData(index)
75 pluginNames = sorted(self.__pluginManager.getPluginModules( 77 pluginNames = sorted(self.__pluginManager.getPluginModules(pluginDirectory))
76 pluginDirectory)) 78
77
78 self.pluginsList.clear() 79 self.pluginsList.clear()
79 for pluginName in pluginNames: 80 for pluginName in pluginNames:
80 fname = "{0}.py".format(os.path.join(pluginDirectory, pluginName)) 81 fname = "{0}.py".format(os.path.join(pluginDirectory, pluginName))
81 itm = QListWidgetItem(pluginName) 82 itm = QListWidgetItem(pluginName)
82 itm.setData(Qt.ItemDataRole.UserRole, fname) 83 itm.setData(Qt.ItemDataRole.UserRole, fname)
83 itm.setFlags(Qt.ItemFlag.ItemIsEnabled | 84 itm.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable)
84 Qt.ItemFlag.ItemIsUserCheckable)
85 itm.setCheckState(Qt.CheckState.Unchecked) 85 itm.setCheckState(Qt.CheckState.Unchecked)
86 self.pluginsList.addItem(itm) 86 self.pluginsList.addItem(itm)
87 87
88 @pyqtSlot() 88 @pyqtSlot()
89 def on_buttonBox_accepted(self): 89 def on_buttonBox_accepted(self):
90 """ 90 """
91 Private slot to handle the accepted signal of the button box. 91 Private slot to handle the accepted signal of the button box.
92 """ 92 """
93 if self.__uninstallPlugins(): 93 if self.__uninstallPlugins():
94 self.accepted.emit() 94 self.accepted.emit()
95 95
96 def __getCheckedPlugins(self): 96 def __getCheckedPlugins(self):
97 """ 97 """
98 Private method to get the list of plugins to be uninstalled. 98 Private method to get the list of plugins to be uninstalled.
99 99
100 @return list of tuples with the plugin name and plugin file name 100 @return list of tuples with the plugin name and plugin file name
101 @rtype list of tuples of (str, str) 101 @rtype list of tuples of (str, str)
102 """ 102 """
103 plugins = [] 103 plugins = []
104 for row in range(self.pluginsList.count()): 104 for row in range(self.pluginsList.count()):
105 itm = self.pluginsList.item(row) 105 itm = self.pluginsList.item(row)
106 if itm.checkState() == Qt.CheckState.Checked: 106 if itm.checkState() == Qt.CheckState.Checked:
107 plugins.append((itm.text(), 107 plugins.append((itm.text(), itm.data(Qt.ItemDataRole.UserRole)))
108 itm.data(Qt.ItemDataRole.UserRole)))
109 return plugins 108 return plugins
110 109
111 def __uninstallPlugins(self): 110 def __uninstallPlugins(self):
112 """ 111 """
113 Private method to uninstall the selected plugins. 112 Private method to uninstall the selected plugins.
114 113
115 @return flag indicating success 114 @return flag indicating success
116 @rtype bool 115 @rtype bool
117 """ 116 """
118 checkedPlugins = self.__getCheckedPlugins() 117 checkedPlugins = self.__getCheckedPlugins()
119 uninstallCount = 0 118 uninstallCount = 0
120 for pluginName, pluginFile in checkedPlugins: 119 for pluginName, pluginFile in checkedPlugins:
121 if self.__uninstallPlugin(pluginName, pluginFile): 120 if self.__uninstallPlugin(pluginName, pluginFile):
122 uninstallCount += 1 121 uninstallCount += 1
123 return uninstallCount == len(checkedPlugins) 122 return uninstallCount == len(checkedPlugins)
124 123
125 def __uninstallPlugin(self, pluginName, pluginFile): 124 def __uninstallPlugin(self, pluginName, pluginFile):
126 """ 125 """
127 Private method to uninstall a given plugin. 126 Private method to uninstall a given plugin.
128 127
129 @param pluginName name of the plugin 128 @param pluginName name of the plugin
130 @type str 129 @type str
131 @param pluginFile file name of the plugin 130 @param pluginFile file name of the plugin
132 @type str 131 @type str
133 @return flag indicating success 132 @return flag indicating success
134 @rtype bool 133 @rtype bool
135 """ 134 """
136 pluginDirectory = self.pluginDirectoryCombo.itemData( 135 pluginDirectory = self.pluginDirectoryCombo.itemData(
137 self.pluginDirectoryCombo.currentIndex()) 136 self.pluginDirectoryCombo.currentIndex()
138 137 )
138
139 if not self.__pluginManager.unloadPlugin(pluginName): 139 if not self.__pluginManager.unloadPlugin(pluginName):
140 EricMessageBox.critical( 140 EricMessageBox.critical(
141 self, 141 self,
142 self.tr("Plugin Uninstallation"), 142 self.tr("Plugin Uninstallation"),
143 self.tr( 143 self.tr(
144 """<p>The plugin <b>{0}</b> could not be unloaded.""" 144 """<p>The plugin <b>{0}</b> could not be unloaded."""
145 """ Aborting...</p>""").format(pluginName)) 145 """ Aborting...</p>"""
146 ).format(pluginName),
147 )
146 return False 148 return False
147 149
148 if pluginDirectory not in sys.path: 150 if pluginDirectory not in sys.path:
149 sys.path.insert(2, pluginDirectory) 151 sys.path.insert(2, pluginDirectory)
150 spec = importlib.util.spec_from_file_location(pluginName, pluginFile) 152 spec = importlib.util.spec_from_file_location(pluginName, pluginFile)
151 module = importlib.util.module_from_spec(spec) 153 module = importlib.util.module_from_spec(spec)
152 spec.loader.exec_module(module) 154 spec.loader.exec_module(module)
154 EricMessageBox.critical( 156 EricMessageBox.critical(
155 self, 157 self,
156 self.tr("Plugin Uninstallation"), 158 self.tr("Plugin Uninstallation"),
157 self.tr( 159 self.tr(
158 """<p>The plugin <b>{0}</b> has no 'packageName'""" 160 """<p>The plugin <b>{0}</b> has no 'packageName'"""
159 """ attribute. Aborting...</p>""").format(pluginName)) 161 """ attribute. Aborting...</p>"""
162 ).format(pluginName),
163 )
160 return False 164 return False
161 165
162 package = getattr(module, "packageName", None) 166 package = getattr(module, "packageName", None)
163 if package is None: 167 if package is None:
164 package = "None" 168 package = "None"
165 packageDir = "" 169 packageDir = ""
166 else: 170 else:
167 packageDir = os.path.join(pluginDirectory, package) 171 packageDir = os.path.join(pluginDirectory, package)
168 if ( 172 if (
169 hasattr(module, "prepareUninstall") and 173 hasattr(module, "prepareUninstall")
170 not self.keepConfigurationCheckBox.isChecked() 174 and not self.keepConfigurationCheckBox.isChecked()
171 ): 175 ):
172 module.prepareUninstall() 176 module.prepareUninstall()
173 internalPackages = [] 177 internalPackages = []
174 if hasattr(module, "internalPackages"): 178 if hasattr(module, "internalPackages"):
175 # it is a comma separated string 179 # it is a comma separated string
176 internalPackages = [p.strip() for p in 180 internalPackages = [p.strip() for p in module.internalPackages.split(",")]
177 module.internalPackages.split(",")]
178 del module 181 del module
179 182
180 # clean sys.modules 183 # clean sys.modules
181 self.__pluginManager.removePluginFromSysModules( 184 self.__pluginManager.removePluginFromSysModules(
182 pluginName, package, internalPackages) 185 pluginName, package, internalPackages
183 186 )
187
184 try: 188 try:
185 if packageDir and os.path.exists(packageDir): 189 if packageDir and os.path.exists(packageDir):
186 shutil.rmtree(packageDir) 190 shutil.rmtree(packageDir)
187 191
188 fnameo = "{0}o".format(pluginFile) 192 fnameo = "{0}o".format(pluginFile)
189 if os.path.exists(fnameo): 193 if os.path.exists(fnameo):
190 os.remove(fnameo) 194 os.remove(fnameo)
191 195
192 fnamec = "{0}c".format(pluginFile) 196 fnamec = "{0}c".format(pluginFile)
193 if os.path.exists(fnamec): 197 if os.path.exists(fnamec):
194 os.remove(fnamec) 198 os.remove(fnamec)
195 199
196 pluginDirCache = os.path.join( 200 pluginDirCache = os.path.join(os.path.dirname(pluginFile), "__pycache__")
197 os.path.dirname(pluginFile), "__pycache__")
198 if os.path.exists(pluginDirCache): 201 if os.path.exists(pluginDirCache):
199 pluginFileName = os.path.splitext( 202 pluginFileName = os.path.splitext(os.path.basename(pluginFile))[0]
200 os.path.basename(pluginFile))[0] 203 for fnameo in glob.glob(
201 for fnameo in glob.glob(os.path.join( 204 os.path.join(pluginDirCache, "{0}*.pyo".format(pluginFileName))
202 pluginDirCache, "{0}*.pyo".format(pluginFileName))): 205 ):
203 os.remove(fnameo) 206 os.remove(fnameo)
204 for fnamec in glob.glob(os.path.join( 207 for fnamec in glob.glob(
205 pluginDirCache, "{0}*.pyc".format(pluginFileName))): 208 os.path.join(pluginDirCache, "{0}*.pyc".format(pluginFileName))
209 ):
206 os.remove(fnamec) 210 os.remove(fnamec)
207 211
208 os.remove(pluginFile) 212 os.remove(pluginFile)
209 except OSError as err: 213 except OSError as err:
210 EricMessageBox.critical( 214 EricMessageBox.critical(
211 self, 215 self,
212 self.tr("Plugin Uninstallation"), 216 self.tr("Plugin Uninstallation"),
213 self.tr( 217 self.tr(
214 """<p>The plugin package <b>{0}</b> could not be""" 218 """<p>The plugin package <b>{0}</b> could not be"""
215 """ removed. Aborting...</p>""" 219 """ removed. Aborting...</p>"""
216 """<p>Reason: {1}</p>""").format(packageDir, str(err))) 220 """<p>Reason: {1}</p>"""
221 ).format(packageDir, str(err)),
222 )
217 return False 223 return False
218 224
219 if not self.__external: 225 if not self.__external:
220 ui = ericApp().getObject("UserInterface") 226 ui = ericApp().getObject("UserInterface")
221 ui.showNotification( 227 ui.showNotification(
222 UI.PixmapCache.getPixmap("plugin48"), 228 UI.PixmapCache.getPixmap("plugin48"),
223 self.tr("Plugin Uninstallation"), 229 self.tr("Plugin Uninstallation"),
224 self.tr( 230 self.tr(
225 """<p>The plugin <b>{0}</b> was uninstalled""" 231 """<p>The plugin <b>{0}</b> was uninstalled"""
226 """ successfully from {1}.</p>""") 232 """ successfully from {1}.</p>"""
227 .format(pluginName, pluginDirectory)) 233 ).format(pluginName, pluginDirectory),
234 )
228 return True 235 return True
229 236
230 EricMessageBox.information( 237 EricMessageBox.information(
231 self, 238 self,
232 self.tr("Plugin Uninstallation"), 239 self.tr("Plugin Uninstallation"),
233 self.tr( 240 self.tr(
234 """<p>The plugin <b>{0}</b> was uninstalled successfully""" 241 """<p>The plugin <b>{0}</b> was uninstalled successfully"""
235 """ from {1}.</p>""") 242 """ from {1}.</p>"""
236 .format(pluginName, pluginDirectory)) 243 ).format(pluginName, pluginDirectory),
244 )
237 return True 245 return True
238 246
239 247
240 class PluginUninstallDialog(QDialog): 248 class PluginUninstallDialog(QDialog):
241 """ 249 """
242 Class for the dialog variant. 250 Class for the dialog variant.
243 """ 251 """
252
244 def __init__(self, pluginManager, parent=None): 253 def __init__(self, pluginManager, parent=None):
245 """ 254 """
246 Constructor 255 Constructor
247 256
248 @param pluginManager reference to the plugin manager object 257 @param pluginManager reference to the plugin manager object
249 @param parent reference to the parent widget (QWidget) 258 @param parent reference to the parent widget (QWidget)
250 """ 259 """
251 super().__init__(parent) 260 super().__init__(parent)
252 self.setSizeGripEnabled(True) 261 self.setSizeGripEnabled(True)
253 262
254 self.__layout = QVBoxLayout(self) 263 self.__layout = QVBoxLayout(self)
255 self.__layout.setContentsMargins(0, 0, 0, 0) 264 self.__layout.setContentsMargins(0, 0, 0, 0)
256 self.setLayout(self.__layout) 265 self.setLayout(self.__layout)
257 266
258 self.cw = PluginUninstallWidget(pluginManager, self) 267 self.cw = PluginUninstallWidget(pluginManager, self)
259 size = self.cw.size() 268 size = self.cw.size()
260 self.__layout.addWidget(self.cw) 269 self.__layout.addWidget(self.cw)
261 self.resize(size) 270 self.resize(size)
262 self.setWindowTitle(self.cw.windowTitle()) 271 self.setWindowTitle(self.cw.windowTitle())
263 272
264 self.cw.buttonBox.accepted.connect(self.accept) 273 self.cw.buttonBox.accepted.connect(self.accept)
265 self.cw.buttonBox.rejected.connect(self.reject) 274 self.cw.buttonBox.rejected.connect(self.reject)
266 275
267 276
268 class PluginUninstallWindow(EricMainWindow): 277 class PluginUninstallWindow(EricMainWindow):
269 """ 278 """
270 Main window class for the standalone dialog. 279 Main window class for the standalone dialog.
271 """ 280 """
281
272 def __init__(self, parent=None): 282 def __init__(self, parent=None):
273 """ 283 """
274 Constructor 284 Constructor
275 285
276 @param parent reference to the parent widget (QWidget) 286 @param parent reference to the parent widget (QWidget)
277 """ 287 """
278 super().__init__(parent) 288 super().__init__(parent)
279 self.cw = PluginUninstallWidget(None, self) 289 self.cw = PluginUninstallWidget(None, self)
280 size = self.cw.size() 290 size = self.cw.size()
281 self.setCentralWidget(self.cw) 291 self.setCentralWidget(self.cw)
282 self.resize(size) 292 self.resize(size)
283 self.setWindowTitle(self.cw.windowTitle()) 293 self.setWindowTitle(self.cw.windowTitle())
284 294
285 self.setStyle(Preferences.getUI("Style"), 295 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet"))
286 Preferences.getUI("StyleSheet")) 296
287
288 self.cw.buttonBox.accepted.connect(self.close) 297 self.cw.buttonBox.accepted.connect(self.close)
289 self.cw.buttonBox.rejected.connect(self.close) 298 self.cw.buttonBox.rejected.connect(self.close)

eric ide

mercurial