--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PipInterface/PipPackageDetailsDialog.py Thu Jul 07 11:23:56 2022 +0200 @@ -0,0 +1,266 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2015 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to show details about a package. +""" + +from PyQt6.QtCore import pyqtSlot, Qt, QLocale +from PyQt6.QtWidgets import ( + QDialog, QDialogButtonBox, QTreeWidgetItem, QLabel, QHeaderView, + QAbstractButton +) + +from .Ui_PipPackageDetailsDialog import Ui_PipPackageDetailsDialog + + +class PipPackageDetailsDialog(QDialog, Ui_PipPackageDetailsDialog): + """ + Class implementing a dialog to show details about a package. + """ + ButtonInstall = 1 + ButtonRemove = 2 + ButtonUpgrade = 4 + + def __init__(self, detailsData, buttonsMode=0, parent=None): + """ + Constructor + + @param detailsData package details + @type dict + @param buttonsMode flags telling which convenience buttons to enable + (defaults to 0) + @type int (optional) + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + self.setupUi(self) + self.setWindowFlags(Qt.WindowType.Window) + + self.__pipWidget = parent + + self.__installButton = self.buttonBox.addButton( + self.tr("Install"), QDialogButtonBox.ButtonRole.ActionRole) + self.__removeButton = self.buttonBox.addButton( + self.tr("Uninstall"), QDialogButtonBox.ButtonRole.ActionRole) + self.__upgradeButton = self.buttonBox.addButton( + self.tr("Upgrade"), QDialogButtonBox.ButtonRole.ActionRole) + + self.__locale = QLocale() + self.__packageTypeMap = { + "sdist": self.tr("Source"), + "bdist_wheel": self.tr("Python Wheel"), + "bdist_egg": self.tr("Python Egg"), + "bdist_wininst": self.tr("MS Windows Installer"), + "bdist_msi": self.tr("MS Windows Installer"), + "bdist_rpm": self.tr("Unix Installer"), + "bdist_deb": self.tr("Unix Installer"), + "bdist_dumb": self.tr("Archive"), + } + self.__packageName = detailsData["info"]["name"] + + self.__populateDetails(detailsData["info"]) + self.__populateDownloadUrls(detailsData["urls"]) + self.__populateRequiresProvides(detailsData["info"]) + + self.__installButton.setEnabled(buttonsMode & self.ButtonInstall) + self.__removeButton.setEnabled(buttonsMode & self.ButtonRemove) + self.__upgradeButton.setEnabled(buttonsMode & self.ButtonUpgrade) + + def __populateDetails(self, detailsData): + """ + Private method to populate the details tab. + + @param detailsData package details + @type dict + """ + self.packageNameLabel.setText( + "<h1>{0} {1}</h1".format(self.__sanitize(detailsData["name"]), + self.__sanitize(detailsData["version"]))) + self.summaryLabel.setText( + self.__sanitize(detailsData["summary"][:240])) + self.descriptionEdit.setPlainText( + self.__sanitize(detailsData["description"])) + self.authorLabel.setText(self.__sanitize(detailsData["author"])) + self.authorEmailLabel.setText( + '<a href="mailto:{0}">{0}</a>'.format( + self.__sanitize(detailsData["author_email"]))) + self.licenseLabel.setText(self.__sanitize(detailsData["license"])) + self.platformLabel.setText(self.__sanitize(detailsData["platform"])) + self.homePageLabel.setText( + '<a href="{0}">{0}</a>'.format( + self.__sanitize(detailsData["home_page"], forUrl=True))) + self.packageUrlLabel.setText( + '<a href="{0}">{0}</a>'.format( + self.__sanitize(detailsData["package_url"], forUrl=True))) + self.releaseUrlLabel.setText( + '<a href="{0}">{0}</a>'.format( + self.__sanitize(detailsData["release_url"], forUrl=True))) + self.docsUrlLabel.setText( + '<a href="{0}">{0}</a>'.format( + self.__sanitize(detailsData["docs_url"], forUrl=True))) + self.classifiersList.addItems(detailsData["classifiers"]) + + self.buttonBox.button( + QDialogButtonBox.StandardButton.Close).setDefault(True) + self.buttonBox.button( + QDialogButtonBox.StandardButton.Close).setFocus( + Qt.FocusReason.OtherFocusReason) + + def __populateDownloadUrls(self, downloadsData): + """ + Private method to populate the download URLs tab. + + @param downloadsData downloads information + @type dict + """ + index = self.infoWidget.indexOf(self.urls) + if downloadsData: + self.infoWidget.setTabEnabled(index, True) + for download in downloadsData: + itm = QTreeWidgetItem(self.downloadUrlsList, [ + "", + self.__packageTypeMap[download["packagetype"]] + if download["packagetype"] in self.__packageTypeMap + else "", + download["python_version"] + if download["python_version"] != "source" + else "", + self.__formatUploadDate(download["upload_time"]), + self.__formatSize(download["size"]), + ]) + pgpLink = ( + ' (<a href="{0}">pgp</a>)'.format(download["url"] + ".asc") + if download["has_sig"] else + "" + ) + urlLabel = QLabel('<a href="{0}#md5={2}">{1}</a>{3}'.format( + download["url"], download["filename"], + download["md5_digest"], pgpLink)) + urlLabel.setTextInteractionFlags( + Qt.TextInteractionFlag.LinksAccessibleByMouse) + urlLabel.setOpenExternalLinks(True) + self.downloadUrlsList.setItemWidget(itm, 0, urlLabel) + header = self.downloadUrlsList.header() + header.resizeSections(QHeaderView.ResizeMode.ResizeToContents) + else: + self.infoWidget.setTabEnabled(index, False) + + def __populateRequiresProvides(self, detailsData): + """ + Private method to populate the requires/provides tab. + + @param detailsData package details + @type dict + """ + populatedItems = 0 + + if "requires" in detailsData and detailsData["requires"]: + self.requiredPackagesList.addItems(detailsData["requires"]) + populatedItems += len(detailsData["requires"]) + if "requires_dist" in detailsData and detailsData["requires_dist"]: + self.requiredDistributionsList.addItems( + detailsData["requires_dist"]) + populatedItems += len(detailsData["requires_dist"]) + if "provides" in detailsData and detailsData["provides"]: + self.providedPackagesList.addItems(detailsData["provides"]) + populatedItems += len(detailsData["provides"]) + if "provides_dist" in detailsData and detailsData["provides_dist"]: + self.providedDistributionsList.addItems( + detailsData["provides_dist"]) + populatedItems += len(detailsData["provides_dist"]) + + index = self.infoWidget.indexOf(self.requires) + self.infoWidget.setTabEnabled(index, populatedItems > 0) + + def __sanitize(self, text, forUrl=False): + """ + Private method to clean-up the given text. + + @param text raw text + @type str + @param forUrl flag indicating to sanitize an URL text + @type bool + @return processed text + @rtype str + """ + if text == "UNKNOWN" or text is None: + text = "" + elif text == "any": + text = self.tr("any") + if forUrl and ( + not isinstance(text, str) or + not text.startswith(("http://", "https://", "ftp://")) + ): + # ignore if the schema is not one of the listed ones + text = "" + + return text + + def __formatUploadDate(self, datetime): + """ + Private method to format the upload date. + + @param datetime upload date and time + @type xmlrpc.DateTime or str + @return formatted date string + @rtype str + """ + if isinstance(datetime, str): + return datetime.split("T")[0] + else: + date = datetime.value.split("T")[0] + return "{0}-{1}-{2}".format(date[:4], date[4:6], date[6:]) + + def __formatSize(self, size): + """ + Private slot to format the size. + + @param size size to be formatted + @type int + @return formatted size + @rtype str + """ + unit = "" + if size < 1024: + unit = self.tr("B") + elif size < 1024 * 1024: + size /= 1024 + unit = self.tr("KB") + elif size < 1024 * 1024 * 1024: + size /= 1024 * 1024 + unit = self.tr("MB") + else: + size /= 1024 * 1024 * 1024 + unit = self.tr("GB") + return self.tr("{0:.1f} {1}", "value, unit").format(size, unit) + + @pyqtSlot(QAbstractButton) + def on_buttonBox_clicked(self, button): + """ + Private slot handling the user pressing an action button. + + @param button button activated by the user + @type QAbstractButton + """ + if button is self.__installButton: + self.__pipWidget.executeInstallPackages([self.__packageName]) + self.__installButton.setEnabled(False) + self.__removeButton.setEnabled(True) + self.__upgradeButton.setEnabled(False) + self.raise_() + elif button is self.__removeButton: + self.__pipWidget.executeUninstallPackages([self.__packageName]) + self.__installButton.setEnabled(True) + self.__removeButton.setEnabled(False) + self.__upgradeButton.setEnabled(False) + self.raise_() + elif button is self.__upgradeButton: + self.__pipWidget.executeUpgradePackages([self.__packageName]) + self.__installButton.setEnabled(False) + self.__removeButton.setEnabled(True) + self.__upgradeButton.setEnabled(False) + self.raise_()