|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the project properties dialog. |
|
8 """ |
|
9 |
|
10 import contextlib |
|
11 import os |
|
12 |
|
13 import trove_classifiers |
|
14 |
|
15 from PyQt6.QtCore import QDir, pyqtSlot |
|
16 from PyQt6.QtWidgets import QDialog, QDialogButtonBox |
|
17 |
|
18 from EricWidgets.EricApplication import ericApp |
|
19 from EricWidgets.EricPathPicker import EricPathPickerModes |
|
20 |
|
21 from .Ui_PropertiesDialog import Ui_PropertiesDialog |
|
22 |
|
23 from QScintilla.DocstringGenerator import getSupportedDocstringTypes |
|
24 |
|
25 from Testing.Interfaces import FrameworkNames |
|
26 |
|
27 import Utilities |
|
28 import Preferences |
|
29 import UI.PixmapCache |
|
30 |
|
31 |
|
32 class PropertiesDialog(QDialog, Ui_PropertiesDialog): |
|
33 """ |
|
34 Class implementing the project properties dialog. |
|
35 """ |
|
36 |
|
37 def __init__(self, project, new=True, parent=None, name=None): |
|
38 """ |
|
39 Constructor |
|
40 |
|
41 @param project reference to the project object |
|
42 @param new flag indicating the generation of a new project |
|
43 @param parent parent widget of this dialog (QWidget) |
|
44 @param name name of this dialog (string) |
|
45 """ |
|
46 super().__init__(parent) |
|
47 if name: |
|
48 self.setObjectName(name) |
|
49 self.setupUi(self) |
|
50 |
|
51 self.dirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) |
|
52 self.mainscriptPicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) |
|
53 |
|
54 self.makeButton.setIcon(UI.PixmapCache.getIcon("makefile")) |
|
55 |
|
56 self.docstringStyleComboBox.addItem(self.tr("None"), "") |
|
57 for docstringType, docstringStyle in sorted(getSupportedDocstringTypes()): |
|
58 self.docstringStyleComboBox.addItem(docstringStyle, docstringType) |
|
59 |
|
60 self.project = project |
|
61 self.newProject = new |
|
62 self.transPropertiesDlg = None |
|
63 self.spellPropertiesDlg = None |
|
64 self.makePropertiesDlg = None |
|
65 |
|
66 patterns = [] |
|
67 for pattern, filetype in self.project.pdata["FILETYPES"].items(): |
|
68 if filetype == "SOURCES": |
|
69 patterns.append(pattern) |
|
70 filters = self.tr("Source Files ({0});;All Files (*)").format( |
|
71 " ".join(sorted(patterns)) |
|
72 ) |
|
73 self.mainscriptPicker.setFilters(filters) |
|
74 |
|
75 self.languageComboBox.addItems(project.getProgrammingLanguages()) |
|
76 |
|
77 projectTypes = [] |
|
78 for projectTypeItem in project.getProjectTypes().items(): |
|
79 projectTypes.append((projectTypeItem[1], projectTypeItem[0])) |
|
80 self.projectTypeComboBox.clear() |
|
81 for projectType in sorted(projectTypes): |
|
82 self.projectTypeComboBox.addItem(projectType[0], projectType[1]) |
|
83 |
|
84 ipath = Preferences.getMultiProject("Workspace") or Utilities.getHomeDir() |
|
85 self.__initPaths = [ |
|
86 Utilities.fromNativeSeparators(ipath), |
|
87 Utilities.fromNativeSeparators(ipath) + "/", |
|
88 ] |
|
89 |
|
90 self.licenseComboBox.lineEdit().setClearButtonEnabled(True) |
|
91 self.__populateLicenseComboBox() |
|
92 |
|
93 if not new: |
|
94 name = os.path.splitext(self.project.pfile)[0] |
|
95 self.nameEdit.setText(os.path.basename(name)) |
|
96 self.languageComboBox.setCurrentIndex( |
|
97 self.languageComboBox.findText(self.project.pdata["PROGLANGUAGE"]) |
|
98 ) |
|
99 self.mixedLanguageCheckBox.setChecked(self.project.pdata["MIXEDLANGUAGE"]) |
|
100 curIndex = self.projectTypeComboBox.findData( |
|
101 self.project.pdata["PROJECTTYPE"] |
|
102 ) |
|
103 if curIndex == -1: |
|
104 curIndex = self.projectTypeComboBox.findData("PyQt6") |
|
105 self.projectTypeComboBox.setCurrentIndex(curIndex) |
|
106 self.dirPicker.setText(self.project.ppath) |
|
107 self.versionEdit.setText(self.project.pdata["VERSION"]) |
|
108 self.mainscriptPicker.setText(self.project.pdata["MAINSCRIPT"]) |
|
109 self.authorEdit.setText(self.project.pdata["AUTHOR"]) |
|
110 self.emailEdit.setText(self.project.pdata["EMAIL"]) |
|
111 self.descriptionEdit.setPlainText(self.project.pdata["DESCRIPTION"]) |
|
112 self.eolComboBox.setCurrentIndex(self.project.pdata["EOL"]) |
|
113 self.vcsLabel.show() |
|
114 if self.project.vcs is not None: |
|
115 vcsSystemsDict = ( |
|
116 ericApp() |
|
117 .getObject("PluginManager") |
|
118 .getPluginDisplayStrings("version_control") |
|
119 ) |
|
120 try: |
|
121 vcsSystemDisplay = vcsSystemsDict[self.project.pdata["VCS"]] |
|
122 except KeyError: |
|
123 vcsSystemDisplay = "None" |
|
124 self.vcsLabel.setText( |
|
125 self.tr("The project is version controlled by <b>{0}</b>.").format( |
|
126 vcsSystemDisplay |
|
127 ) |
|
128 ) |
|
129 self.vcsInfoButton.show() |
|
130 else: |
|
131 self.vcsLabel.setText(self.tr("The project is not version controlled.")) |
|
132 self.vcsInfoButton.hide() |
|
133 self.vcsCheckBox.hide() |
|
134 self.makeCheckBox.setChecked( |
|
135 self.project.pdata["MAKEPARAMS"]["MakeEnabled"] |
|
136 ) |
|
137 cindex = self.docstringStyleComboBox.findData( |
|
138 self.project.pdata["DOCSTRING"] |
|
139 ) |
|
140 self.docstringStyleComboBox.setCurrentIndex(cindex) |
|
141 with contextlib.suppress(KeyError): |
|
142 cindex = self.testingFrameworkComboBox.findData( |
|
143 self.project.pdata["TESTING_FRAMEWORK"] |
|
144 ) |
|
145 self.testingFrameworkComboBox.setCurrentIndex(cindex) |
|
146 with contextlib.suppress(KeyError): |
|
147 self.licenseComboBox.setCurrentText(self.project.pdata["LICENSE"]) |
|
148 else: |
|
149 self.languageComboBox.setCurrentText("Python3") |
|
150 self.projectTypeComboBox.setCurrentIndex( |
|
151 self.projectTypeComboBox.findData("PyQt6") |
|
152 ) |
|
153 self.dirPicker.setText(self.__initPaths[0]) |
|
154 self.versionEdit.setText("0.1") |
|
155 self.vcsLabel.hide() |
|
156 self.vcsInfoButton.hide() |
|
157 if not self.project.vcsSoftwareAvailable(): |
|
158 self.vcsCheckBox.hide() |
|
159 |
|
160 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled( |
|
161 bool(self.dirPicker.text()) |
|
162 and self.dirPicker.text() not in self.__initPaths |
|
163 ) |
|
164 |
|
165 def __populateLicenseComboBox(self): |
|
166 """ |
|
167 Private method to populate the license selector with the list of trove |
|
168 license types. |
|
169 """ |
|
170 self.licenseComboBox.addItem("") |
|
171 self.licenseComboBox.addItems( |
|
172 sorted( |
|
173 classifier.split("::")[-1].strip() |
|
174 for classifier in trove_classifiers.classifiers |
|
175 if classifier.startswith("License ::") |
|
176 ) |
|
177 ) |
|
178 |
|
179 @pyqtSlot(str) |
|
180 def on_languageComboBox_currentTextChanged(self, language): |
|
181 """ |
|
182 Private slot handling the selection of a programming language. |
|
183 |
|
184 @param language text of the current item |
|
185 @type str |
|
186 """ |
|
187 curProjectType = self.getProjectType() |
|
188 |
|
189 self.projectTypeComboBox.clear() |
|
190 for projectType in sorted( |
|
191 self.project.getProjectTypes(language).items(), key=lambda k: k[1] |
|
192 ): |
|
193 self.projectTypeComboBox.addItem(projectType[1], projectType[0]) |
|
194 |
|
195 index = self.projectTypeComboBox.findData(curProjectType) |
|
196 if index == -1: |
|
197 index = 0 |
|
198 self.projectTypeComboBox.setCurrentIndex(index) |
|
199 |
|
200 curTestingFramework = self.testingFrameworkComboBox.currentText() |
|
201 self.testingFrameworkComboBox.clear() |
|
202 self.testingFrameworkComboBox.addItem(self.tr("None"), "") |
|
203 with contextlib.suppress(KeyError): |
|
204 for framework in sorted(FrameworkNames[language]): |
|
205 self.testingFrameworkComboBox.addItem(framework, framework) |
|
206 self.testingFrameworkComboBox.setCurrentText(curTestingFramework) |
|
207 |
|
208 @pyqtSlot(str) |
|
209 def on_dirPicker_textChanged(self, txt): |
|
210 """ |
|
211 Private slot to handle a change of the project directory. |
|
212 |
|
213 @param txt name of the project directory (string) |
|
214 """ |
|
215 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled( |
|
216 bool(txt) and Utilities.fromNativeSeparators(txt) not in self.__initPaths |
|
217 ) |
|
218 |
|
219 @pyqtSlot() |
|
220 def on_spellPropertiesButton_clicked(self): |
|
221 """ |
|
222 Private slot to display the spelling properties dialog. |
|
223 """ |
|
224 if self.spellPropertiesDlg is None: |
|
225 from .SpellingPropertiesDialog import SpellingPropertiesDialog |
|
226 |
|
227 self.spellPropertiesDlg = SpellingPropertiesDialog( |
|
228 self.project, self.newProject, self |
|
229 ) |
|
230 res = self.spellPropertiesDlg.exec() |
|
231 if res == QDialog.DialogCode.Rejected: |
|
232 self.spellPropertiesDlg.initDialog() # reset the dialogs contents |
|
233 |
|
234 @pyqtSlot() |
|
235 def on_transPropertiesButton_clicked(self): |
|
236 """ |
|
237 Private slot to display the translations properties dialog. |
|
238 """ |
|
239 if self.transPropertiesDlg is None: |
|
240 from .TranslationPropertiesDialog import TranslationPropertiesDialog |
|
241 |
|
242 self.transPropertiesDlg = TranslationPropertiesDialog( |
|
243 self.project, self.newProject, self |
|
244 ) |
|
245 else: |
|
246 self.transPropertiesDlg.initFilters() |
|
247 res = self.transPropertiesDlg.exec() |
|
248 if res == QDialog.DialogCode.Rejected: |
|
249 self.transPropertiesDlg.initDialog() # reset the dialogs contents |
|
250 |
|
251 @pyqtSlot() |
|
252 def on_makeButton_clicked(self): |
|
253 """ |
|
254 Private slot to display the make properties dialog. |
|
255 """ |
|
256 if self.makePropertiesDlg is None: |
|
257 from .MakePropertiesDialog import MakePropertiesDialog |
|
258 |
|
259 self.makePropertiesDlg = MakePropertiesDialog( |
|
260 self.project, self.newProject, self |
|
261 ) |
|
262 res = self.makePropertiesDlg.exec() |
|
263 if res == QDialog.DialogCode.Rejected: |
|
264 self.makePropertiesDlg.initDialog() |
|
265 |
|
266 @pyqtSlot(str) |
|
267 def on_mainscriptPicker_pathSelected(self, script): |
|
268 """ |
|
269 Private slot to check the selected main script name. |
|
270 |
|
271 @param script name of the main script |
|
272 @type str |
|
273 """ |
|
274 if script: |
|
275 ppath = self.dirPicker.text() |
|
276 if ppath: |
|
277 ppath = QDir(ppath).absolutePath() + QDir.separator() |
|
278 script = script.replace(ppath, "") |
|
279 self.mainscriptPicker.setText(script) |
|
280 |
|
281 @pyqtSlot() |
|
282 def on_mainscriptPicker_aboutToShowPathPickerDialog(self): |
|
283 """ |
|
284 Private slot to perform actions before the main script selection dialog |
|
285 is shown. |
|
286 """ |
|
287 path = self.dirPicker.text() |
|
288 if not path: |
|
289 path = QDir.currentPath() |
|
290 self.mainscriptPicker.setDefaultDirectory(path) |
|
291 |
|
292 @pyqtSlot() |
|
293 def on_vcsInfoButton_clicked(self): |
|
294 """ |
|
295 Private slot to display a vcs information dialog. |
|
296 """ |
|
297 if self.project.vcs is None: |
|
298 return |
|
299 |
|
300 from VCS.RepositoryInfoDialog import VcsRepositoryInfoDialog |
|
301 |
|
302 info = self.project.vcs.vcsRepositoryInfos(self.project.ppath) |
|
303 dlg = VcsRepositoryInfoDialog(self, info) |
|
304 dlg.exec() |
|
305 |
|
306 def getProjectType(self): |
|
307 """ |
|
308 Public method to get the selected project type. |
|
309 |
|
310 @return selected UI type (string) |
|
311 """ |
|
312 return self.projectTypeComboBox.itemData( |
|
313 self.projectTypeComboBox.currentIndex() |
|
314 ) |
|
315 |
|
316 def getPPath(self): |
|
317 """ |
|
318 Public method to get the project path. |
|
319 |
|
320 @return data of the project directory edit (string) |
|
321 """ |
|
322 return os.path.abspath(self.dirPicker.text()) |
|
323 |
|
324 def storeData(self): |
|
325 """ |
|
326 Public method to store the entered/modified data. |
|
327 """ |
|
328 self.project.ppath = os.path.abspath(self.dirPicker.text()) |
|
329 fn = self.nameEdit.text() |
|
330 if fn: |
|
331 self.project.name = fn |
|
332 fn = "{0}.epj".format(fn) |
|
333 self.project.pfile = os.path.join(self.project.ppath, fn) |
|
334 else: |
|
335 self.project.pfile = "" |
|
336 self.project.pdata["VERSION"] = self.versionEdit.text() |
|
337 fn = self.mainscriptPicker.text() |
|
338 if fn: |
|
339 fn = self.project.getRelativePath(fn) |
|
340 self.project.pdata["MAINSCRIPT"] = fn |
|
341 self.project.translationsRoot = os.path.splitext(fn)[0] |
|
342 else: |
|
343 self.project.pdata["MAINSCRIPT"] = "" |
|
344 self.project.translationsRoot = "" |
|
345 self.project.pdata["AUTHOR"] = self.authorEdit.text() |
|
346 self.project.pdata["EMAIL"] = self.emailEdit.text() |
|
347 self.project.pdata["DESCRIPTION"] = self.descriptionEdit.toPlainText() |
|
348 self.project.pdata["PROGLANGUAGE"] = self.languageComboBox.currentText() |
|
349 self.project.pdata["MIXEDLANGUAGE"] = self.mixedLanguageCheckBox.isChecked() |
|
350 projectType = self.getProjectType() |
|
351 if projectType is not None: |
|
352 self.project.pdata["PROJECTTYPE"] = projectType |
|
353 self.project.pdata["EOL"] = self.eolComboBox.currentIndex() |
|
354 |
|
355 self.project.vcsRequested = self.vcsCheckBox.isChecked() |
|
356 |
|
357 if self.spellPropertiesDlg is not None: |
|
358 self.spellPropertiesDlg.storeData() |
|
359 |
|
360 if self.transPropertiesDlg is not None: |
|
361 self.transPropertiesDlg.storeData() |
|
362 |
|
363 self.project.pdata["MAKEPARAMS"]["MakeEnabled"] = self.makeCheckBox.isChecked() |
|
364 if self.makePropertiesDlg is not None: |
|
365 self.makePropertiesDlg.storeData() |
|
366 |
|
367 self.project.pdata["DOCSTRING"] = self.docstringStyleComboBox.currentData() |
|
368 |
|
369 self.project.pdata[ |
|
370 "TESTING_FRAMEWORK" |
|
371 ] = self.testingFrameworkComboBox.currentData() |
|
372 |
|
373 self.project.pdata["LICENSE"] = self.licenseComboBox.currentText() |