--- a/src/eric7/WebBrowser/SpellCheck/ManageDictionariesDialog.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/WebBrowser/SpellCheck/ManageDictionariesDialog.py Wed Jul 13 14:55:47 2022 +0200 @@ -15,9 +15,7 @@ import contextlib from PyQt6.QtCore import pyqtSlot, Qt, QUrl -from PyQt6.QtWidgets import ( - QDialog, QDialogButtonBox, QAbstractButton, QListWidgetItem -) +from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton, QListWidgetItem from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkInformation from EricWidgets import EricMessageBox @@ -33,15 +31,16 @@ """ Class implementing a dialog to install spell checking dictionaries. """ + FilenameRole = Qt.ItemDataRole.UserRole UrlRole = Qt.ItemDataRole.UserRole + 1 DocumentationDirRole = Qt.ItemDataRole.UserRole + 2 LocalesRole = Qt.ItemDataRole.UserRole + 3 - + def __init__(self, writeableDirectories, parent=None): """ Constructor - + @param writeableDirectories list of writable directories @type list of str @param parent reference to the parent widget @@ -49,71 +48,73 @@ """ super().__init__(parent) self.setupUi(self) - + self.__refreshButton = self.buttonBox.addButton( - self.tr("Refresh"), QDialogButtonBox.ButtonRole.ActionRole) + self.tr("Refresh"), QDialogButtonBox.ButtonRole.ActionRole + ) self.__installButton = self.buttonBox.addButton( - self.tr("Install Selected"), - QDialogButtonBox.ButtonRole.ActionRole) + self.tr("Install Selected"), QDialogButtonBox.ButtonRole.ActionRole + ) self.__installButton.setEnabled(False) self.__uninstallButton = self.buttonBox.addButton( - self.tr("Uninstall Selected"), - QDialogButtonBox.ButtonRole.ActionRole) + self.tr("Uninstall Selected"), QDialogButtonBox.ButtonRole.ActionRole + ) self.__uninstallButton.setEnabled(False) self.__cancelButton = self.buttonBox.addButton( - self.tr("Cancel"), QDialogButtonBox.ButtonRole.ActionRole) + self.tr("Cancel"), QDialogButtonBox.ButtonRole.ActionRole + ) self.__cancelButton.setEnabled(False) - + self.locationComboBox.addItems(writeableDirectories) - + self.dictionariesUrlEdit.setText( - Preferences.getWebBrowser("SpellCheckDictionariesUrl")) - - if ( - Preferences.getUI("DynamicOnlineCheck") and - QNetworkInformation.load(QNetworkInformation.Feature.Reachability) + Preferences.getWebBrowser("SpellCheckDictionariesUrl") + ) + + if Preferences.getUI("DynamicOnlineCheck") and QNetworkInformation.load( + QNetworkInformation.Feature.Reachability ): - self.__reachabilityChanged( - QNetworkInformation.instance().reachability()) + self.__reachabilityChanged(QNetworkInformation.instance().reachability()) QNetworkInformation.instance().reachabilityChanged.connect( - self.__reachabilityChanged) + self.__reachabilityChanged + ) else: # assume to be 'always online' if no backend could be loaded or # dynamic online check is switched of self.__reachabilityChanged(QNetworkInformation.Reachability.Online) self.__replies = [] - + self.__downloadCancelled = False self.__dictionariesToDownload = [] - + self.__populateList() - + def __reachabilityChanged(self, reachability): """ Private slot handling reachability state changes. - + @param reachability new reachability state @type QNetworkInformation.Reachability """ online = reachability == QNetworkInformation.Reachability.Online self.__online = online - + self.__refreshButton.setEnabled(online) - + msg = ( self.tr("Internet Reachability Status: Reachable") - if online else - self.tr("Internet Reachability Status: Not Reachable") + if online + else self.tr("Internet Reachability Status: Not Reachable") ) self.statusLabel.setText(msg) - + self.on_dictionariesList_itemSelectionChanged() - + @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ Private slot to handle the click of a button of the button box. - + @param button reference to the button pressed @type QAbstractButton """ @@ -125,72 +126,75 @@ self.__installSelected() elif button == self.__uninstallButton: self.__uninstallSelected() - + @pyqtSlot() def on_dictionariesList_itemSelectionChanged(self): """ Private slot to handle a change of the selection. """ self.__installButton.setEnabled( - self.locationComboBox.count() > 0 and - len(self.dictionariesList.selectedItems()) > 0 and - self.__online + self.locationComboBox.count() > 0 + and len(self.dictionariesList.selectedItems()) > 0 + and self.__online ) - + self.__uninstallButton.setEnabled( - self.locationComboBox.count() > 0 and - len([itm - for itm in self.dictionariesList.selectedItems() - if itm.checkState() == Qt.CheckState.Checked - ]) + self.locationComboBox.count() > 0 + and len( + [ + itm + for itm in self.dictionariesList.selectedItems() + if itm.checkState() == Qt.CheckState.Checked + ] + ) ) - + @pyqtSlot(bool) def on_dictionariesUrlEditButton_toggled(self, checked): """ Private slot to set the read only status of the dictionaries URL line edit. - + @param checked state of the push button (boolean) """ self.dictionariesUrlEdit.setReadOnly(not checked) - + @pyqtSlot(str) def on_locationComboBox_currentTextChanged(self, txt): """ Private slot to handle a change of the installation location. - + @param txt installation location @type str """ self.__checkInstalledDictionaries() - + def __populateList(self): """ Private method to populate the list of available plugins. """ self.dictionariesList.clear() self.downloadProgress.setValue(0) - + url = self.dictionariesUrlEdit.text() - + if self.__online: self.__refreshButton.setEnabled(False) self.__installButton.setEnabled(False) self.__uninstallButton.setEnabled(False) self.__cancelButton.setEnabled(True) - + self.statusLabel.setText(url) - + self.__downloadCancelled = False - + request = QNetworkRequest(QUrl(url)) request.setAttribute( QNetworkRequest.Attribute.CacheLoadControlAttribute, - QNetworkRequest.CacheLoadControl.AlwaysNetwork) + QNetworkRequest.CacheLoadControl.AlwaysNetwork, + ) reply = WebBrowserWindow.networkManager().get(request) - reply.finished.connect( - lambda: self.__listFileDownloaded(reply)) + reply.finished.connect(lambda: self.__listFileDownloaded(reply)) reply.downloadProgress.connect(self.__downloadProgress) self.__replies.append(reply) else: @@ -200,25 +204,26 @@ self.tr( """<p>Could not download the dictionaries list""" """ from {0}.</p><p>Error: {1}</p>""" - ).format(url, self.tr("No connection to Internet."))) - + ).format(url, self.tr("No connection to Internet.")), + ) + def __listFileDownloaded(self, reply): """ Private method called, after the dictionaries list file has been downloaded from the Internet. - + @param reply reference to the network reply @type QNetworkReply """ self.__refreshButton.setEnabled(True) self.__cancelButton.setEnabled(False) - + self.downloadProgress.setValue(0) - + if reply in self.__replies: self.__replies.remove(reply) reply.deleteLater() - + if reply.error() != QNetworkReply.NetworkError.NoError: if not self.__downloadCancelled: EricMessageBox.warning( @@ -227,18 +232,16 @@ self.tr( """<p>Could not download the dictionaries list""" """ from {0}.</p><p>Error: {1}</p>""" - ).format(self.dictionariesUrlEdit.text(), - reply.errorString()) + ).format(self.dictionariesUrlEdit.text(), reply.errorString()), ) self.downloadProgress.setValue(0) return - + listFileData = reply.readAll() - + # extract the dictionaries - from EricXML.SpellCheckDictionariesReader import ( - SpellCheckDictionariesReader - ) + from EricXML.SpellCheckDictionariesReader import SpellCheckDictionariesReader + reader = SpellCheckDictionariesReader(listFileData, self.addEntry) reader.readXML() url = Preferences.getWebBrowser("SpellCheckDictionariesUrl") @@ -251,9 +254,9 @@ """The URL of the spell check dictionaries has""" """ changed. Select the "Refresh" button to get""" """ the new dictionaries list.""" - ) + ), ) - + if self.locationComboBox.count() == 0: # no writable locations available EricMessageBox.warning( @@ -263,11 +266,11 @@ """<p>None of the dictionary locations is writable by""" """ you. Please download required dictionaries manually""" """ and install them as administrator.</p>""" - ) + ), ) - + self.__checkInstalledDictionaries() - + def __downloadCancel(self): """ Private slot to cancel the current download. @@ -277,11 +280,11 @@ self.__downloadCancelled = True self.__dictionariesToDownload = [] reply.abort() - + def __downloadProgress(self, done, total): """ Private slot to show the download progress. - + @param done number of bytes downloaded so far @type int @param total total bytes to be downloaded @@ -290,11 +293,11 @@ if total: self.downloadProgress.setMaximum(total) self.downloadProgress.setValue(done) - + def addEntry(self, short, filename, url, documentationDir, locales): """ Public method to add an entry to the list. - + @param short data for the description field @type str @param filename data for the filename field @@ -308,20 +311,19 @@ @type list of str """ itm = QListWidgetItem( - self.tr("{0} ({1})").format(short, " ".join(locales)), - self.dictionariesList) + self.tr("{0} ({1})").format(short, " ".join(locales)), self.dictionariesList + ) itm.setCheckState(Qt.CheckState.Unchecked) - + itm.setData(ManageDictionariesDialog.FilenameRole, filename) itm.setData(ManageDictionariesDialog.UrlRole, url) - itm.setData(ManageDictionariesDialog.DocumentationDirRole, - documentationDir) + itm.setData(ManageDictionariesDialog.DocumentationDirRole, documentationDir) itm.setData(ManageDictionariesDialog.LocalesRole, locales) - + def __checkInstalledDictionaries(self): """ Private method to check all installed dictionaries. - + Note: A dictionary is assumed to be installed, if at least one of its binary dictionaries (*.bdic) is found in the selected dictionaries location. @@ -333,7 +335,7 @@ os.path.join(self.locationComboBox.currentText(), "*.bdic") ) } - + for row in range(self.dictionariesList.count()): itm = self.dictionariesList.item(row) locales = set(itm.data(ManageDictionariesDialog.LocalesRole)) @@ -345,7 +347,7 @@ for row in range(self.dictionariesList.count()): itm = self.dictionariesList.item(row) itm.setCheckState(Qt.CheckState.Unchecked) - + def __installSelected(self): """ Private method to install the selected dictionaries. @@ -355,16 +357,16 @@ itm.data(ManageDictionariesDialog.UrlRole) for itm in self.dictionariesList.selectedItems() ] - + self.__refreshButton.setEnabled(False) self.__installButton.setEnabled(False) self.__uninstallButton.setEnabled(False) self.__cancelButton.setEnabled(True) - + self.__downloadCancelled = False - + self.__downloadDictionary() - + def __downloadDictionary(self): """ Private slot to download a dictionary. @@ -373,16 +375,16 @@ if self.__dictionariesToDownload: url = self.__dictionariesToDownload.pop(0) self.statusLabel.setText(url) - + self.__downloadCancelled = False - + request = QNetworkRequest(QUrl(url)) request.setAttribute( QNetworkRequest.Attribute.CacheLoadControlAttribute, - QNetworkRequest.CacheLoadControl.AlwaysNetwork) + QNetworkRequest.CacheLoadControl.AlwaysNetwork, + ) reply = WebBrowserWindow.networkManager().get(request) - reply.finished.connect( - lambda: self.__installDictionary(reply)) + reply.finished.connect(lambda: self.__installDictionary(reply)) reply.downloadProgress.connect(self.__downloadProgress) self.__replies.append(reply) else: @@ -394,21 +396,22 @@ self.tr( """<p>Could not download the requested dictionary file""" """ from {0}.</p><p>Error: {1}</p>""" - ).format(url, self.tr("No connection to Internet."))) - + ).format(url, self.tr("No connection to Internet.")), + ) + self.__installationFinished() - + def __installDictionary(self, reply): """ Private slot to install the downloaded dictionary. - + @param reply reference to the network reply @type QNetworkReply """ if reply in self.__replies: self.__replies.remove(reply) reply.deleteLater() - + if reply.error() != QNetworkReply.NetworkError.NoError: if not self.__downloadCancelled: EricMessageBox.warning( @@ -417,11 +420,11 @@ self.tr( """<p>Could not download the requested dictionary""" """ file from {0}.</p><p>Error: {1}</p>""" - ).format(reply.url(), reply.errorString()) + ).format(reply.url(), reply.errorString()), ) self.downloadProgress.setValue(0) return - + archiveData = reply.readAll() archiveFile = io.BytesIO(bytes(archiveData)) archive = zipfile.ZipFile(archiveFile, "r") @@ -431,17 +434,18 @@ self.tr("Error downloading dictionary"), self.tr( """<p>The downloaded dictionary archive is invalid.""" - """ Skipping it.</p>""") + """ Skipping it.</p>""" + ), ) else: installDir = self.locationComboBox.currentText() archive.extractall(installDir) - + if self.__dictionariesToDownload: self.__downloadDictionary() else: self.__installationFinished() - + def __installationFinished(self): """ Private method called after all selected dictionaries have been @@ -449,12 +453,12 @@ """ self.__refreshButton.setEnabled(True) self.__cancelButton.setEnabled(False) - + self.dictionariesList.clearSelection() self.downloadProgress.setValue(0) - + self.__checkInstalledDictionaries() - + def __uninstallSelected(self): """ Private method to uninstall the selected dictionaries. @@ -462,24 +466,22 @@ installLocation = self.locationComboBox.currentText() if not installLocation: return - + itemsToDelete = [ itm for itm in self.dictionariesList.selectedItems() if itm.checkState() == Qt.CheckState.Checked ] for itm in itemsToDelete: - documentationDir = itm.data( - ManageDictionariesDialog.DocumentationDirRole) - shutil.rmtree(os.path.join(installLocation, documentationDir), - True) - + documentationDir = itm.data(ManageDictionariesDialog.DocumentationDirRole) + shutil.rmtree(os.path.join(installLocation, documentationDir), True) + locales = itm.data(ManageDictionariesDialog.LocalesRole) for locale in locales: bdic = os.path.join(installLocation, locale + ".bdic") with contextlib.suppress(OSError): os.remove(bdic) - + self.dictionariesList.clearSelection() - + self.__checkInstalledDictionaries()