eric7/Plugins/WizardPlugins/SetupWizard/SetupWizardDialog.py

branch
eric7
changeset 8312
800c432b34c8
parent 8218
7c09585bd960
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2013 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the setup.py wizard dialog.
8 """
9
10 import os
11 import datetime
12
13 from PyQt5.QtCore import pyqtSlot, Qt, QUrl
14 from PyQt5.QtWidgets import (
15 QDialog, QDialogButtonBox, QTreeWidgetItem, QListWidgetItem
16 )
17 from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
18
19 from E5Gui.E5Application import e5App
20 from E5Gui import E5MessageBox, E5FileDialog
21 from E5Gui.E5Completers import E5DirCompleter
22 from E5Gui.E5OverrideCursor import E5OverrideCursor
23
24 from .Ui_SetupWizardDialog import Ui_SetupWizardDialog
25
26 import UI.PixmapCache
27 import Utilities
28 import Preferences
29
30
31 class SetupWizardDialog(QDialog, Ui_SetupWizardDialog):
32 """
33 Class implementing the setup.py wizard dialog.
34
35 It displays a dialog for entering the parameters
36 for the E5MessageBox code generator.
37 """
38 ClassifiersUrl = "https://pypi.org/pypi?%3Aaction=list_classifiers"
39
40 def __init__(self, parent=None):
41 """
42 Constructor
43
44 @param parent reference to the parent widget (QWidget)
45 """
46 super().__init__(parent)
47 self.setupUi(self)
48
49 self.__replies = []
50
51 self.dataTabWidget.setCurrentIndex(0)
52
53 self.__packageDirCompleter = E5DirCompleter(self.packageEdit)
54 self.__packageRootDirCompleter = E5DirCompleter(self.packageRootEdit)
55 self.__sourceDirCompleter = E5DirCompleter(self.sourceDirectoryEdit)
56
57 self.packageRootDirButton.setIcon(UI.PixmapCache.getIcon("open"))
58 self.packageDirButton.setIcon(UI.PixmapCache.getIcon("open"))
59 self.sourceDirectoryButton.setIcon(UI.PixmapCache.getIcon("open"))
60
61 self.variantComboBox.addItem(self.tr("distutils"), "distutils.core")
62 self.variantComboBox.addItem(self.tr("setuptools"), "setuptools")
63 self.variantComboBox.setCurrentIndex(1)
64
65 self.__mandatoryStyleSheet = "QLineEdit {border: 2px solid;}"
66 for lineEdit in [self.nameEdit, self.versionEdit,
67 self.homePageUrlEdit, self.authorEdit,
68 self.authorEmailEdit, self.maintainerEdit,
69 self.maintainerEmailEdit]:
70 lineEdit.setStyleSheet(self.__mandatoryStyleSheet)
71
72 self.__loadClassifiersFromPyPI()
73
74 self.__okButton = self.buttonBox.button(
75 QDialogButtonBox.StandardButton.Ok)
76 self.__okButton.setEnabled(False)
77
78 projectOpen = e5App().getObject("Project").isOpen()
79 self.projectButton.setEnabled(projectOpen)
80 self.autodiscoverPackagesButton.setEnabled(projectOpen)
81
82 self.homePageUrlEdit.textChanged.connect(self.__enableOkButton)
83 self.nameEdit.textChanged.connect(self.__enableOkButton)
84 self.versionEdit.textChanged.connect(self.__enableOkButton)
85 self.authorEdit.textChanged.connect(self.__enableOkButton)
86 self.authorEmailEdit.textChanged.connect(self.__enableOkButton)
87 self.maintainerEdit.textChanged.connect(self.__enableOkButton)
88 self.maintainerEmailEdit.textChanged.connect(self.__enableOkButton)
89
90 def __enableOkButton(self):
91 """
92 Private slot to set the state of the OK button.
93 """
94 enable = (
95 bool(self.nameEdit.text()) and
96 bool(self.versionEdit.text()) and
97 bool(self.homePageUrlEdit.text()) and
98 ((bool(self.authorEdit.text()) and
99 bool(self.authorEmailEdit.text())) or
100 (bool(self.maintainerEdit.text()) and
101 bool(self.maintainerEmailEdit.text()))) and
102 self.homePageUrlEdit.text().startswith(("http://", "https://"))
103 )
104
105 self.__okButton.setEnabled(enable)
106
107 def __loadClassifiersFromPyPI(self):
108 """
109 Private method to populate the classifiers list with data retrieved
110 from PyPI.
111 """
112 request = QNetworkRequest(QUrl(SetupWizardDialog.ClassifiersUrl))
113 request.setAttribute(
114 QNetworkRequest.Attribute.CacheLoadControlAttribute,
115 QNetworkRequest.CacheLoadControl.AlwaysNetwork)
116 reply = e5App().getObject("UserInterface").networkAccessManager().get(
117 request)
118 reply.finished.connect(lambda: self.__classifiersDownloadDone(reply))
119 self.__replies.append(reply)
120
121 @pyqtSlot()
122 def __classifiersDownloadDone(self, reply):
123 """
124 Private slot called, after the classifiers file has been downloaded
125 from the internet.
126
127 @param reply reference to the network reply
128 @type QNetworkReply
129 """
130 reply.deleteLater()
131 if reply in self.__replies:
132 self.__replies.remove(reply)
133 if reply.error() == QNetworkReply.NetworkError.NoError:
134 ioEncoding = Preferences.getSystem("IOEncoding")
135 lines = str(reply.readAll(), ioEncoding, 'replace').splitlines()
136
137 self.__populateClassifiers(lines)
138
139 reply.close()
140
141 @pyqtSlot()
142 def on_localClassifiersButton_clicked(self):
143 """
144 Private method to populate lists from the Trove list file.
145
146 Note: The trove list file was created from querying
147 "https://pypi.org/pypi?%3Aaction=list_classifiers".
148 """
149 filename = os.path.join(os.path.dirname(__file__),
150 "data", "trove_classifiers.txt")
151 try:
152 with open(filename, "r") as f:
153 lines = f.readlines()
154 except OSError as err:
155 E5MessageBox.warning(
156 self,
157 self.tr("Reading Trove Classifiers"),
158 self.tr("""<p>The Trove Classifiers file <b>{0}</b>"""
159 """ could not be read.</p><p>Reason: {1}</p>""")
160 .format(filename, str(err)))
161 return
162
163 self.__populateClassifiers(lines)
164
165 def __populateClassifiers(self, classifiers):
166 """
167 Private method to populate the classifiers.
168
169 @param classifiers list of classifiers read from a local file or
170 retrieved from PyPI
171 @type list of str
172 """
173 self.licenseClassifierComboBox.clear()
174 self.classifiersList.clear()
175 self.developmentStatusComboBox.clear()
176
177 self.developmentStatusComboBox.addItem("", "")
178
179 self.__classifiersDict = {}
180 for line in classifiers:
181 line = line.strip()
182 if line.startswith("License "):
183 self.licenseClassifierComboBox.addItem(
184 "/".join(line.split(" :: ")[1:]),
185 line
186 )
187 elif line.startswith("Development Status "):
188 self.developmentStatusComboBox.addItem(
189 line.split(" :: ")[1], line)
190 else:
191 self.__addClassifierEntry(line)
192 self.__classifiersDict = {}
193
194 self.licenseClassifierComboBox.setCurrentIndex(
195 self.licenseClassifierComboBox.findText(
196 "(GPLv3)",
197 Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchCaseSensitive
198 )
199 )
200
201 def __addClassifierEntry(self, line):
202 """
203 Private method to add a new entry to the list of trove classifiers.
204
205 @param line line containing the data for the entry (string)
206 """
207 itm = None
208 pitm = None
209 dataList = line.split(" :: ")
210 for index in range(len(dataList)):
211 key = " :: ".join(dataList[:index + 1])
212 if key not in self.__classifiersDict:
213 if pitm is None:
214 itm = QTreeWidgetItem(
215 self.classifiersList, [dataList[index]])
216 pitm = itm
217 else:
218 itm = QTreeWidgetItem(pitm, [dataList[index]])
219 itm.setExpanded(True)
220 self.__classifiersDict[key] = itm
221 else:
222 pitm = self.__classifiersDict[key]
223 itm.setCheckState(0, Qt.CheckState.Unchecked)
224 itm.setData(0, Qt.ItemDataRole.UserRole, line)
225
226 def __getLicenseText(self):
227 """
228 Private method to get the license text.
229
230 @return license text (string)
231 """
232 if not self.licenseClassifierCheckBox.isChecked():
233 return self.licenseEdit.text()
234 else:
235 lic = self.licenseClassifierComboBox.currentText()
236 if "(" in lic:
237 lic = lic.rsplit("(", 1)[1].split(")", 1)[0]
238 return lic
239
240 def getCode(self, indLevel, indString):
241 """
242 Public method to get the source code.
243
244 @param indLevel indentation level (int)
245 @param indString string used for indentation (space or tab) (string)
246 @return generated code (string)
247 """
248 # Note: all paths are created with '/'; setup will do the right thing
249
250 # calculate our indentation level and the indentation string
251 il = indLevel + 1
252 istring = il * indString
253 i1string = (il + 1) * indString
254 i2string = (il + 2) * indString
255 estring = os.linesep + indLevel * indString
256
257 # now generate the code
258 if self.introCheckBox.isChecked():
259 code = "#!/usr/bin/env python3{0}".format(os.linesep)
260 code += "# -*- coding: utf-8 -*-{0}{0}".format(os.linesep)
261 else:
262 code = ""
263
264 if self.metaDataCheckBox.isChecked():
265 code += '# metadata{0}'.format(os.linesep)
266 code += '"{0}"{1}'.format(
267 self.summaryEdit.text() or "Setup routine",
268 os.linesep
269 )
270 code += '__version__ = "{0}"{1}'.format(
271 self.versionEdit.text(), os.linesep)
272 code += '__license__ = "{0}"{1}'.format(
273 self.__getLicenseText(), os.linesep)
274 code += '__author__ = "{0}"{1}'.format(
275 self.authorEdit.text() or self.maintainerEdit.text(),
276 os.linesep)
277 code += '__email__ = "{0}"{1}'.format(
278 self.authorEmailEdit.text() or self.maintainerEmailEdit.text(),
279 os.linesep)
280 code += '__url__ = "{0}"{1}'.format(
281 self.homePageUrlEdit.text(), os.linesep)
282 code += '__date__ = "{0}"{1}'.format(
283 datetime.datetime.now().isoformat().split('.')[0], os.linesep)
284 code += '__prj__ = "{0}"{1}'.format(
285 self.nameEdit.text(), os.linesep)
286 code += os.linesep
287
288 if self.importCheckBox.isChecked():
289 variant = self.variantComboBox.itemData(
290 self.variantComboBox.currentIndex())
291 if variant == "setuptools":
292 additionalImport = ", find_packages"
293 else:
294 additionalImport = ""
295 code += "from {0} import setup{1}{2}".format(
296 variant, additionalImport, os.linesep)
297 if code:
298 code += "{0}{0}".format(os.linesep)
299
300 if self.descriptionFromFilesCheckBox.isChecked():
301 code += 'def get_long_description():{0}'.format(os.linesep)
302 code += '{0}descr = []{1}'.format(istring, os.linesep)
303 code += '{0}for fname in "{1}":{2}'.format(
304 istring,
305 '", "'.join(self.descriptionEdit.toPlainText().splitlines()),
306 os.linesep)
307 code += '{0}{0}with open(fname) as f:{1}'.format(
308 istring, os.linesep)
309 code += '{0}{0}{0}descr.append(f.read()){1}'.format(
310 istring, os.linesep)
311 code += '{0}return "\\n\\n".join(descr){1}'.format(
312 istring, os.linesep)
313 code += "{0}{0}".format(os.linesep)
314
315 code += 'setup({0}'.format(os.linesep)
316 code += '{0}name="{1}",{2}'.format(
317 istring, self.nameEdit.text(), os.linesep)
318 code += '{0}version="{1}",{2}'.format(
319 istring, self.versionEdit.text(), os.linesep)
320
321 if self.summaryEdit.text():
322 code += '{0}description="{1}",{2}'.format(
323 istring, self.summaryEdit.text(), os.linesep)
324
325 if self.descriptionFromFilesCheckBox.isChecked():
326 code += '{0}long_description=get_long_description(),{1}'.format(
327 istring, os.linesep)
328 elif self.descriptionEdit.toPlainText():
329 code += '{0}long_description="""{1}""",{2}'.format(
330 istring, self.descriptionEdit.toPlainText(), os.linesep)
331
332 if self.authorEdit.text():
333 code += '{0}author="{1}",{2}'.format(
334 istring, self.authorEdit.text(), os.linesep)
335 code += '{0}author_email="{1}",{2}'.format(
336 istring, self.authorEmailEdit.text(), os.linesep)
337
338 if self.maintainerEdit.text():
339 code += '{0}maintainer="{1}",{2}'.format(
340 istring, self.maintainerEdit.text(), os.linesep)
341 code += '{0}maintainer_email="{1}",{2}'.format(
342 istring, self.maintainerEmailEdit.text(), os.linesep)
343
344 code += '{0}url="{1}",{2}'.format(
345 istring, self.homePageUrlEdit.text(), os.linesep)
346 if self.downloadUrlEdit.text():
347 code += '{0}download_url="{1}",{2}'.format(
348 istring, self.downloadUrlEdit.text(), os.linesep)
349
350 classifiers = []
351 if not self.licenseClassifierCheckBox.isChecked():
352 code += '{0}license="{1}",{2}'.format(
353 istring, self.licenseEdit.text(), os.linesep)
354 else:
355 classifiers.append(
356 self.licenseClassifierComboBox.itemData(
357 self.licenseClassifierComboBox.currentIndex()))
358
359 platforms = self.platformsEdit.toPlainText().splitlines()
360 if platforms:
361 code += '{0}platforms=[{1}'.format(istring, os.linesep)
362 code += '{0}"{1}"{2}'.format(
363 i1string,
364 '",{0}{1}"'.format(os.linesep, i1string).join(platforms),
365 os.linesep)
366 code += '{0}],{1}'.format(istring, os.linesep)
367
368 if self.developmentStatusComboBox.currentIndex() != 0:
369 classifiers.append(
370 self.developmentStatusComboBox.itemData(
371 self.developmentStatusComboBox.currentIndex()))
372
373 itm = self.classifiersList.topLevelItem(0)
374 while itm:
375 itm.setExpanded(True)
376 if itm.checkState(0) == Qt.CheckState.Checked:
377 classifiers.append(itm.data(0, Qt.ItemDataRole.UserRole))
378 itm = self.classifiersList.itemBelow(itm)
379
380 # cleanup classifiers list - remove all invalid entries
381 classifiers = [c for c in classifiers if bool(c)]
382 if classifiers:
383 code += '{0}classifiers=[{1}'.format(istring, os.linesep)
384 code += '{0}"{1}"{2}'.format(
385 i1string,
386 '",{0}{1}"'.format(os.linesep, i1string).join(classifiers),
387 os.linesep)
388 code += '{0}],{1}'.format(istring, os.linesep)
389 del classifiers
390
391 if self.keywordsEdit.text():
392 code += '{0}keywords="{1}",{2}'.format(
393 istring, self.keywordsEdit.text(), os.linesep)
394
395 if self.variantComboBox.currentIndex() == 0:
396 # distutils
397 packages = []
398 for row in range(self.packagesList.count()):
399 packages.append(self.packagesList.item(row).text())
400 if packages:
401 code += '{0}packages=[{1}'.format(istring, os.linesep)
402 code += '{0}"{1}"{2}'.format(
403 i1string,
404 '",{0}{1}"'.format(os.linesep, i1string).join(packages),
405 os.linesep)
406 code += '{0}],{1}'.format(istring, os.linesep)
407 del packages
408 elif self.variantComboBox.currentIndex() == 1:
409 # setuptools
410 code += '{0}packages=find_packages('.format(istring)
411 src = Utilities.fromNativeSeparators(
412 self.sourceDirectoryEdit.text())
413 excludePatterns = []
414 for row in range(self.excludePatternList.count()):
415 excludePatterns.append(
416 self.excludePatternList.item(row).text())
417 if src:
418 code += '{0}{1}"{2}"'.format(os.linesep, i1string, src)
419 if excludePatterns:
420 code += ','
421 else:
422 code += '{0}{1}'.format(os.linesep, istring)
423 if excludePatterns:
424 code += '{0}{1}exclude=[{0}'.format(os.linesep, i1string)
425 code += '{0}"{1}"{2}'.format(
426 i2string,
427 '",{0}{1}"'.format(os.linesep, i2string)
428 .join(excludePatterns),
429 os.linesep)
430 code += '{0}]{1}{2}'.format(i1string, os.linesep, istring)
431 code += '),{0}'.format(os.linesep)
432
433 if self.includePackageDataCheckBox.isChecked():
434 code += '{0}include_package_data = True,{1}'.format(
435 istring, os.linesep)
436
437 modules = []
438 for row in range(self.modulesList.count()):
439 modules.append(self.modulesList.item(row).text())
440 if modules:
441 code += '{0}py_modules=[{1}'.format(istring, os.linesep)
442 code += '{0}"{1}"{2}'.format(
443 i1string,
444 '",{0}{1}"'.format(os.linesep, i1string).join(modules),
445 os.linesep)
446 code += '{0}],{1}'.format(istring, os.linesep)
447 del modules
448
449 scripts = []
450 for row in range(self.scriptsList.count()):
451 scripts.append(self.scriptsList.item(row).text())
452 if scripts:
453 code += '{0}scripts=[{1}'.format(istring, os.linesep)
454 code += '{0}"{1}"{2}'.format(
455 i1string,
456 '",{0}{1}"'.format(os.linesep, i1string).join(scripts),
457 os.linesep)
458 code += '{0}],{1}'.format(istring, os.linesep)
459 del scripts
460
461 code += "){0}".format(estring)
462 return code
463
464 @pyqtSlot()
465 def on_projectButton_clicked(self):
466 """
467 Private slot to populate some fields with data retrieved from the
468 current project.
469 """
470 project = e5App().getObject("Project")
471
472 self.nameEdit.setText(project.getProjectName())
473 try:
474 self.versionEdit.setText(project.getProjectVersion())
475 self.authorEdit.setText(project.getProjectAuthor())
476 self.authorEmailEdit.setText(project.getProjectAuthorEmail())
477 description = project.getProjectDescription()
478 except AttributeError:
479 self.versionEdit.setText(project.pdata["VERSION"][0])
480 self.authorEdit.setText(project.pdata["AUTHOR"][0])
481 self.authorEmailEdit.setText(project.pdata["EMAIL"][0])
482 description = project.pdata["DESCRIPTION"][0]
483
484 summary = (
485 description.split(".", 1)[0].replace("\r", "").replace("\n", "") +
486 "."
487 )
488 self.summaryEdit.setText(summary)
489 self.descriptionEdit.setPlainText(description)
490
491 self.packageRootEdit.setText(project.getProjectPath())
492
493 # prevent overwriting of entries by disabling the button
494 self.projectButton.setEnabled(False)
495
496 @pyqtSlot()
497 def on_packagesList_itemSelectionChanged(self):
498 """
499 Private slot to handle a change of selected items of the
500 packages list.
501 """
502 self.deletePackageButton.setEnabled(
503 len(self.packagesList.selectedItems()) > 0)
504
505 @pyqtSlot()
506 def on_deletePackageButton_clicked(self):
507 """
508 Private slot to delete the selected package items.
509 """
510 for itm in self.packagesList.selectedItems():
511 self.packagesList.takeItem(
512 self.packagesList.row(itm))
513 del itm
514
515 @pyqtSlot()
516 def on_addPackageButton_clicked(self):
517 """
518 Private slot to add a package to the list.
519 """
520 pkg = Utilities.toNativeSeparators(self.packageEdit.text())
521 self.__addPackage(pkg)
522
523 @pyqtSlot()
524 def on_packageEdit_returnPressed(self):
525 """
526 Private slot handling a press of the return button of the
527 package edit.
528 """
529 self.on_addPackageButton_clicked()
530
531 @pyqtSlot(str)
532 def on_packageEdit_textChanged(self, txt):
533 """
534 Private slot to handle a change of the package text.
535
536 @param txt text of the line edit (string)
537 """
538 self.addPackageButton.setEnabled(bool(txt))
539
540 @pyqtSlot()
541 def on_packageDirButton_clicked(self):
542 """
543 Private slot to select a package directory via a directory
544 selection dialog.
545 """
546 startDir = self.packageEdit.text()
547 if not startDir:
548 startDir = self.packageRootEdit.text() or self.__getStartDir()
549 packageDir = E5FileDialog.getExistingDirectory(
550 self,
551 self.tr("Package Directory"),
552 Utilities.fromNativeSeparators(startDir))
553 if packageDir:
554 self.packageEdit.setText(
555 Utilities.toNativeSeparators(packageDir))
556
557 @pyqtSlot()
558 def on_autodiscoverPackagesButton_clicked(self):
559 """
560 Private slot to discover packages automatically.
561 """
562 with E5OverrideCursor():
563 self.autodiscoverPackagesButton.setEnabled(False)
564 startDir = self.packageRootEdit.text() or self.__getStartDir()
565 if startDir:
566 self.packagesList.clear()
567 for dirpath, _dirnames, filenames in os.walk(startDir):
568 if "__init__.py" in filenames:
569 self.__addPackage(dirpath)
570 self.autodiscoverPackagesButton.setEnabled(True)
571
572 @pyqtSlot()
573 def on_packageRootDirButton_clicked(self):
574 """
575 Private slot to select the packages root directory via a
576 directory selection dialog.
577 """
578 startDir = self.packageRootEdit.text()
579 if not startDir:
580 startDir = self.__getStartDir()
581 packagesRootDir = E5FileDialog.getExistingDirectory(
582 self,
583 self.tr("Packages Root Directory"),
584 Utilities.fromNativeSeparators(startDir),
585 E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
586 if packagesRootDir:
587 self.packageRootEdit.setText(
588 Utilities.toNativeSeparators(packagesRootDir))
589
590 @pyqtSlot(str)
591 def on_packageRootEdit_textChanged(self, txt):
592 """
593 Private slot handling the entering of a packages root.
594
595 @param txt text of the line edit (string)
596 """
597 projectOpen = e5App().getObject("Project").isOpen()
598 validPackagesRoot = bool(txt) and os.path.exists(txt)
599 self.autodiscoverPackagesButton.setEnabled(
600 projectOpen or validPackagesRoot)
601
602 def __addPackage(self, pkgDir):
603 """
604 Private method to add a package to the list.
605
606 @param pkgDir name of the package directory (string)
607 """
608 if pkgDir:
609 if "\\" in pkgDir or "/" in pkgDir:
610 # It is a directory. Check for an __init__.py file.
611 if os.path.isabs(pkgDir):
612 prefix = ""
613 else:
614 prefix = self.packageRootEdit.text()
615 initName = os.path.join(
616 prefix,
617 Utilities.toNativeSeparators(pkgDir),
618 "__init__.py")
619 if not os.path.exists(initName):
620 res = E5MessageBox.information(
621 self,
622 self.tr("Add Package"),
623 self.tr("""<p>The directory <b>{0}</b> is not"""
624 """ a Python package.</p>""")
625 .format(pkgDir),
626 E5MessageBox.StandardButtons(
627 E5MessageBox.Ignore |
628 E5MessageBox.Ok))
629 if res == E5MessageBox.Ok:
630 return
631
632 pkg = pkgDir.replace(
633 Utilities.toNativeSeparators(self.packageRootEdit.text()), "")
634 if pkg.startswith(("\\", "/")):
635 pkg = pkg[1:]
636 if pkg:
637 QListWidgetItem(
638 pkg.replace("\\", ".").replace("/", "."),
639 self.packagesList)
640 self.packageEdit.clear()
641
642 def __getStartDir(self):
643 """
644 Private method to get the start directory for selection dialogs.
645
646 @return start directory (string)
647 """
648 return (Preferences.getMultiProject("Workspace") or
649 Utilities.getHomeDir())
650
651 @pyqtSlot()
652 def on_scriptsList_itemSelectionChanged(self):
653 """
654 Private slot to handle a change of selected items of the
655 scripts list.
656 """
657 self.deleteScriptButton.setEnabled(
658 len(self.scriptsList.selectedItems()) > 0)
659
660 @pyqtSlot()
661 def on_deleteScriptButton_clicked(self):
662 """
663 Private slot to delete the selected script items.
664 """
665 for itm in self.scriptsList.selectedItems():
666 self.scriptsList.takeItem(
667 self.scriptsList.row(itm))
668 del itm
669
670 @pyqtSlot()
671 def on_addScriptButton_clicked(self):
672 """
673 Private slot to add scripts to the list.
674 """
675 startDir = self.packageRootEdit.text() or self.__getStartDir()
676 scriptsList = E5FileDialog.getOpenFileNames(
677 self,
678 self.tr("Add Scripts"),
679 startDir,
680 self.tr("Python Files (*.py);;All Files(*)"))
681 for script in scriptsList:
682 script = script.replace(
683 Utilities.toNativeSeparators(startDir), "")
684 if script.startswith(("\\", "/")):
685 script = script[1:]
686 if script:
687 QListWidgetItem(Utilities.fromNativeSeparators(script),
688 self.scriptsList)
689
690 @pyqtSlot()
691 def on_modulesList_itemSelectionChanged(self):
692 """
693 Private slot to handle a change of selected items of the
694 modules list.
695 """
696 self.deleteModuleButton.setEnabled(
697 len(self.modulesList.selectedItems()) > 0)
698
699 @pyqtSlot()
700 def on_deleteModuleButton_clicked(self):
701 """
702 Private slot to delete the selected script items.
703 """
704 for itm in self.modulesList.selectedItems():
705 self.modulesList.takeItem(
706 self.modulesList.row(itm))
707 del itm
708
709 @pyqtSlot()
710 def on_addModuleButton_clicked(self):
711 """
712 Private slot to add Python modules to the list.
713 """
714 startDir = self.packageRootEdit.text() or self.__getStartDir()
715 modulesList = E5FileDialog.getOpenFileNames(
716 self,
717 self.tr("Add Python Modules"),
718 startDir,
719 self.tr("Python Files (*.py)"))
720 for module in modulesList:
721 module = module.replace(
722 Utilities.toNativeSeparators(startDir), "")
723 if module.startswith(("\\", "/")):
724 module = module[1:]
725 if module:
726 QListWidgetItem(os.path.splitext(module)[0]
727 .replace("\\", ".").replace("/", "."),
728 self.modulesList)
729
730 @pyqtSlot(int)
731 def on_variantComboBox_currentIndexChanged(self, index):
732 """
733 Private slot handling a change of the setup variant.
734
735 @param index index of the selected entry (integer)
736 """
737 self.packagesStackedWidget.setCurrentIndex(index)
738
739 @pyqtSlot()
740 def on_excludePatternList_itemSelectionChanged(self):
741 """
742 Private slot to handle a change of selected items of the
743 exclude pattern list.
744 """
745 self.deleteExcludePatternButton.setEnabled(
746 len(self.excludePatternList.selectedItems()) > 0)
747
748 @pyqtSlot()
749 def on_deleteExcludePatternButton_clicked(self):
750 """
751 Private slot to delete the selected exclude pattern items.
752 """
753 for itm in self.excludePatternList.selectedItems():
754 self.excludePatternList.takeItem(
755 self.excludePatternList.row(itm))
756 del itm
757
758 @pyqtSlot()
759 def on_addExludePatternButton_clicked(self):
760 """
761 Private slot to add an exclude pattern to the list.
762 """
763 pattern = (
764 self.excludePatternEdit.text().replace("\\", ".").replace("/", ".")
765 )
766 if not self.excludePatternList.findItems(
767 pattern,
768 Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive
769 ):
770 QListWidgetItem(pattern, self.excludePatternList)
771
772 @pyqtSlot(str)
773 def on_excludePatternEdit_textChanged(self, txt):
774 """
775 Private slot to handle a change of the exclude pattern text.
776
777 @param txt text of the line edit (string)
778 """
779 self.addExludePatternButton.setEnabled(bool(txt))
780
781 @pyqtSlot()
782 def on_excludePatternEdit_returnPressed(self):
783 """
784 Private slot handling a press of the return button of the
785 exclude pattern edit.
786 """
787 self.on_addExludePatternButton_clicked()
788
789 @pyqtSlot()
790 def on_sourceDirectoryButton_clicked(self):
791 """
792 Private slot to select the packages root directory via a
793 directory selection dialog.
794 """
795 startDir = self.sourceDirectoryEdit.text() or self.__getStartDir()
796 sourceDirectory = E5FileDialog.getExistingDirectory(
797 self,
798 self.tr("Source Directory"),
799 Utilities.fromNativeSeparators(startDir),
800 E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
801 if sourceDirectory:
802 self.sourceDirectoryEdit.setText(
803 Utilities.toNativeSeparators(sourceDirectory))

eric ide

mercurial