src/eric7/PipInterface/PipLicensesDialog.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9108
19a57544f32c
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show the licenses of an environment.
8 """
9
10 import os
11 import re
12
13 from PyQt6.QtCore import pyqtSlot, Qt
14 from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QTreeWidgetItem
15
16 from EricGui.EricOverrideCursor import EricOverrideCursor
17 from EricWidgets import EricFileDialog, EricMessageBox
18
19 from .Ui_PipLicensesDialog import Ui_PipLicensesDialog
20
21
22 class PipLicensesDialog(QDialog, Ui_PipLicensesDialog):
23 """
24 Class implementing a dialog to show the licenses of an environment.
25 """
26 LicensesPackageColumn = 0
27 LicensesVersionColumn = 1
28 LicensesLicenseColumn = 2
29
30 SummaryCountColumn = 0
31 SummaryLicenseColumn = 1
32
33 def __init__(self, pip, environment, localPackages=True, usersite=False,
34 parent=None):
35 """
36 Constructor
37
38 @param pip reference to the pip interface object
39 @type Pip
40 @param environment name of the environment to show the licenses for
41 @type str
42 @param localPackages flag indicating to show the licenses for local
43 packages only
44 @type bool
45 @param usersite flag indicating to show the licenses for packages
46 installed in user-site directory only
47 @type bool
48 @param parent reference to the parent widget (defaults to None)
49 @type QWidget (optional)
50 """
51 super().__init__(parent)
52 self.setupUi(self)
53
54 self.__pip = pip
55 self.__environment = environment
56
57 self.__allFilter = self.tr("<All>")
58
59 self.__saveCSVButton = self.buttonBox.addButton(
60 self.tr("Save as CSV..."), QDialogButtonBox.ButtonRole.ActionRole)
61 self.__saveCSVButton.clicked.connect(self.__saveAsCSV)
62
63 self.buttonBox.button(
64 QDialogButtonBox.StandardButton.Close).setDefault(True)
65
66 self.localCheckBox.setChecked(localPackages)
67 self.userCheckBox.setChecked(usersite)
68
69 self.localCheckBox.toggled.connect(self.__refreshLicenses)
70 self.userCheckBox.toggled.connect(self.__refreshLicenses)
71
72 if environment:
73 self.environmentLabel.setText("<b>{0}</b>".format(
74 self.tr('Licenses of "{0}"').format(environment)
75 ))
76 else:
77 # That should never happen; play it safe.
78 self.environmentLabel.setText(self.tr("No environment specified."))
79
80 self.licenseFilterComboBox.currentTextChanged.connect(
81 self.__filterPackagesByLicense)
82
83 self.__refreshLicenses()
84
85 @pyqtSlot()
86 def __refreshLicenses(self):
87 """
88 Private slot to refresh the license lists.
89 """
90 with EricOverrideCursor():
91 self.licensesList.clear()
92 self.summaryList.clear()
93 self.licenseFilterComboBox.clear()
94
95 licensesForFilter = set()
96
97 # step 1: show the licenses per package
98 self.licensesList.setUpdatesEnabled(False)
99 licenses = self.__pip.getLicenses(
100 self.__environment,
101 localPackages=self.localCheckBox.isChecked(),
102 usersite=self.userCheckBox.isChecked(),
103 )
104 for lic in licenses:
105 QTreeWidgetItem(self.licensesList, [
106 lic["Name"],
107 lic["Version"],
108 lic["License"].replace("; ", "\n"),
109 ])
110
111 self.licensesList.sortItems(
112 PipLicensesDialog.LicensesPackageColumn,
113 Qt.SortOrder.AscendingOrder)
114 for col in range(self.licensesList.columnCount()):
115 self.licensesList.resizeColumnToContents(col)
116 self.licensesList.setUpdatesEnabled(True)
117
118 # step 2: show the licenses summary
119 self.summaryList.setUpdatesEnabled(False)
120 licenses = self.__pip.getLicensesSummary(
121 self.__environment,
122 localPackages=self.localCheckBox.isChecked(),
123 usersite=self.userCheckBox.isChecked(),
124 )
125 for lic in licenses:
126 QTreeWidgetItem(self.summaryList, [
127 "{0:4d}".format(lic["Count"]),
128 lic["License"].replace("; ", "\n"),
129 ])
130 licensesForFilter |= set(lic["License"].split("; "))
131
132 self.summaryList.sortItems(
133 PipLicensesDialog.SummaryLicenseColumn,
134 Qt.SortOrder.AscendingOrder)
135 for col in range(self.summaryList.columnCount()):
136 self.summaryList.resizeColumnToContents(col)
137 self.summaryList.setUpdatesEnabled(True)
138
139 self.licenseFilterComboBox.addItems(
140 [self.__allFilter] + sorted(licensesForFilter))
141
142 enable = bool(self.licensesList.topLevelItemCount())
143 self.__saveCSVButton.setEnabled(enable)
144
145 @pyqtSlot(str)
146 def __filterPackagesByLicense(self, licenseName):
147 """
148 Private slot to filter the list of packages by license.
149
150 @param licenseName license name
151 @type str
152 """
153 pattern = r"\b{0}".format(re.escape(licenseName))
154 if not licenseName.endswith((")", "]", "}")):
155 pattern += r"\b"
156 regexp = re.compile(pattern)
157 for row in range(self.licensesList.topLevelItemCount()):
158 itm = self.licensesList.topLevelItem(row)
159 if licenseName == self.__allFilter:
160 itm.setHidden(False)
161 else:
162 itm.setHidden(
163 regexp.search(
164 itm.text(PipLicensesDialog.LicensesLicenseColumn)
165 ) is None
166 )
167
168 @pyqtSlot()
169 def __saveAsCSV(self):
170 """
171 Private slot to save the license information as a CSV file.
172 """
173 import csv
174
175 fileName, selectedFilter = EricFileDialog.getSaveFileNameAndFilter(
176 self,
177 self.tr("Save as CSV"),
178 os.path.expanduser("~"),
179 self.tr("CSV Files (*.csv);;All Files (*)"),
180 None,
181 EricFileDialog.DontConfirmOverwrite
182 )
183 if fileName:
184 ext = os.path.splitext(fileName)[1]
185 if not ext:
186 ex = selectedFilter.split("(*")[1].split(")")[0]
187 if ex:
188 fileName += ex
189
190 try:
191 with open(fileName, "w", newline="",
192 encoding="utf-8") as csvFile:
193 fieldNames = ["Name", "Version", "License"]
194 writer = csv.DictWriter(csvFile, fieldnames=fieldNames)
195
196 writer.writeheader()
197 for row in range(self.licensesList.topLevelItemCount()):
198 itm = self.licensesList.topLevelItem(row)
199 writer.writerow({
200 "Name": itm.text(0),
201 "Version": itm.text(1),
202 "License": itm.text(2),
203 })
204 except OSError as err:
205 EricMessageBox.critical(
206 self,
207 self.tr("Save as CSV"),
208 self.tr(
209 """<p>The license information could not be saved"""
210 """ into the CSV file <b>{0}</b>.</p>"""
211 """<p>Reason: {1}</p>"""
212 ).format(fileName, str(err))
213 )

eric ide

mercurial