src/eric7/PipInterface/PipLicensesDialog.py

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

eric ide

mercurial