src/eric7/Preferences/ConfigurationDialog.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8943
23f9c7b9e18e
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog for the configuration of eric.
8 """
9
10 import contextlib
11 import enum
12 import os
13 import time
14 import types
15
16 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QMetaObject, QRect
17 from PyQt6.QtGui import QPixmap
18 from PyQt6.QtWidgets import (
19 QSizePolicy, QSpacerItem, QWidget, QTreeWidget, QStackedWidget, QDialog,
20 QSplitter, QScrollArea, QApplication, QDialogButtonBox, QFrame,
21 QVBoxLayout, QTreeWidgetItem, QLabel, QAbstractScrollArea, QLineEdit
22 )
23
24 from EricWidgets.EricApplication import ericApp
25 from EricWidgets import EricMessageBox
26 from EricWidgets.EricMainWindow import EricMainWindow
27
28 from Globals import isMacPlatform, getWebBrowserSupport
29
30 import Preferences
31
32 import UI.PixmapCache
33
34 from eric7config import getConfig
35
36
37 class ConfigurationPageItem(QTreeWidgetItem):
38 """
39 Class implementing a QTreeWidgetItem holding the configuration page data.
40 """
41 def __init__(self, parent, text, pageName, iconFile):
42 """
43 Constructor
44
45 @param parent parent widget of the item (QTreeWidget or
46 QTreeWidgetItem)
47 @param text text to be displayed (string)
48 @param pageName name of the configuration page (string)
49 @param iconFile file name of the icon to be shown (string)
50 """
51 super().__init__(parent, [text])
52 self.setIcon(0, UI.PixmapCache.getIcon(iconFile))
53
54 self.__pageName = pageName
55
56 def getPageName(self):
57 """
58 Public method to get the name of the associated configuration page.
59
60 @return name of the configuration page (string)
61 """
62 return self.__pageName
63
64
65 class ConfigurationMode(enum.Enum):
66 """
67 Class defining the various modes of the configuration widget.
68 """
69 DEFAULTMODE = 0
70 TRAYSTARTERMODE = 1
71 HEXEDITORMODE = 2
72 WEBBROWSERMODE = 3
73 EDITORMODE = 4
74
75
76 class ConfigurationWidget(QWidget):
77 """
78 Class implementing a dialog for the configuration of eric.
79
80 @signal preferencesChanged() emitted after settings have been changed
81 @signal masterPasswordChanged(str, str) emitted after the master
82 password has been changed with the old and the new password
83 @signal accepted() emitted to indicate acceptance of the changes
84 @signal rejected() emitted to indicate rejection of the changes
85 """
86 preferencesChanged = pyqtSignal()
87 masterPasswordChanged = pyqtSignal(str, str)
88 accepted = pyqtSignal()
89 rejected = pyqtSignal()
90
91 def __init__(self, parent=None, fromEric=True,
92 displayMode=ConfigurationMode.DEFAULTMODE,
93 expandedEntries=None):
94 """
95 Constructor
96
97 @param parent reference to the parent widget
98 @type QWidget
99 @param fromEric flag indicating a dialog generation from within the
100 eric IDE
101 @type bool
102 @param displayMode mode of the configuration dialog
103 @type ConfigurationMode
104 @param expandedEntries list of entries to be shown expanded
105 @type list of str
106 """
107 super().__init__(parent)
108
109 self.fromEric = fromEric
110 self.displayMode = displayMode
111 self.__webEngine = getWebBrowserSupport() == "QtWebEngine"
112 expandedEntries = [] if expandedEntries is None else expandedEntries[:]
113
114 self.__setupUi()
115
116 self.itmDict = {}
117
118 if not fromEric:
119 from PluginManager.PluginManager import PluginManager
120 try:
121 self.pluginManager = ericApp().getObject("PluginManager")
122 except KeyError:
123 self.pluginManager = PluginManager(self)
124 ericApp().registerObject("PluginManager", self.pluginManager)
125
126 from VirtualEnv.VirtualenvManager import VirtualenvManager
127 try:
128 self.virtualenvManager = ericApp().getObject(
129 "VirtualEnvManager")
130 except KeyError:
131 self.virtualenvManager = VirtualenvManager(self)
132 ericApp().registerObject("VirtualEnvManager",
133 self.virtualenvManager)
134
135 if displayMode == ConfigurationMode.DEFAULTMODE:
136 self.configItems = {
137 # key : [display string, pixmap name, dialog module name or
138 # page creation function, parent key,
139 # reference to configuration page (must always be last)]
140 # The dialog module must have the module function 'create' to
141 # create the configuration page. This must have the method
142 # 'save' to save the settings.
143 "applicationPage":
144 [self.tr("Application"), "preferences-application",
145 "ApplicationPage", None, None],
146 "condaPage":
147 [self.tr("Conda"), "miniconda",
148 "CondaPage", None, None],
149 "cooperationPage":
150 [self.tr("Cooperation"), "preferences-cooperation",
151 "CooperationPage", None, None],
152 "corbaPage":
153 [self.tr("CORBA"), "preferences-orbit",
154 "CorbaPage", None, None],
155 "diffPage":
156 [self.tr("Diff"), "diffFiles",
157 "DiffColoursPage", None, None],
158 "emailPage":
159 [self.tr("Email"), "preferences-mail_generic",
160 "EmailPage", None, None],
161 "graphicsPage":
162 [self.tr("Graphics"), "preferences-graphics",
163 "GraphicsPage", None, None],
164 "hexEditorPage":
165 [self.tr("Hex Editor"), "hexEditor",
166 "HexEditorPage", None, None],
167 "iconsPage":
168 [self.tr("Icons"), "preferences-icons",
169 "IconsPage", None, None],
170 "ircPage":
171 [self.tr("IRC"), "irc",
172 "IrcPage", None, None],
173 "logViewerPage":
174 [self.tr("Log-Viewer"), "preferences-logviewer",
175 "LogViewerPage", None, None],
176 "microPythonPage":
177 [self.tr("MicroPython"), "micropython",
178 "MicroPythonPage", None, None],
179 "mimeTypesPage":
180 [self.tr("Mimetypes"), "preferences-mimetypes",
181 "MimeTypesPage", None, None],
182 "networkPage":
183 [self.tr("Network"), "preferences-network",
184 "NetworkPage", None, None],
185 "notificationsPage":
186 [self.tr("Notifications"),
187 "preferences-notifications",
188 "NotificationsPage", None, None],
189 "pipPage":
190 [self.tr("Python Package Management"), "pypi",
191 "PipPage", None, None],
192 "pluginManagerPage":
193 [self.tr("Plugin Manager"),
194 "preferences-pluginmanager",
195 "PluginManagerPage", None, None],
196 "printerPage":
197 [self.tr("Printer"), "preferences-printer",
198 "PrinterPage", None, None],
199 "protobufPage":
200 [self.tr("Protobuf"), "protobuf",
201 "ProtobufPage", None, None],
202 "pythonPage":
203 [self.tr("Python"), "preferences-python",
204 "PythonPage", None, None],
205 "qtPage":
206 [self.tr("Qt"), "preferences-qtlogo",
207 "QtPage", None, None],
208 "securityPage":
209 [self.tr("Security"), "preferences-security",
210 "SecurityPage", None, None],
211 "shellPage":
212 [self.tr("Shell"), "preferences-shell",
213 "ShellPage", None, None],
214 "tasksPage":
215 [self.tr("Tasks"), "task",
216 "TasksPage", None, None],
217 "templatesPage":
218 [self.tr("Templates"), "preferences-template",
219 "TemplatesPage", None, None],
220 "trayStarterPage":
221 [self.tr("Tray Starter"), "erict",
222 "TrayStarterPage", None, None],
223 "vcsPage":
224 [self.tr("Version Control Systems"),
225 "preferences-vcs",
226 "VcsPage", None, None],
227
228 "0debuggerPage":
229 [self.tr("Debugger"), "preferences-debugger",
230 None, None, None],
231 "debuggerGeneralPage":
232 [self.tr("General"), "preferences-debugger",
233 "DebuggerGeneralPage", "0debuggerPage", None],
234 "debuggerPython3Page":
235 [self.tr("Python3"), "preferences-pyDebugger",
236 "DebuggerPython3Page", "0debuggerPage", None],
237
238 "0editorPage":
239 [self.tr("Editor"), "preferences-editor",
240 None, None, None],
241 "editorAPIsPage":
242 [self.tr("APIs"), "preferences-api",
243 "EditorAPIsPage", "0editorPage", None],
244 "editorDocViewerPage":
245 [self.tr("Documentation Viewer"), "codeDocuViewer",
246 "EditorDocViewerPage", "0editorPage", None],
247 "editorGeneralPage":
248 [self.tr("General"), "preferences-general",
249 "EditorGeneralPage", "0editorPage", None],
250 "editorFilePage":
251 [self.tr("Filehandling"),
252 "preferences-filehandling",
253 "EditorFilePage", "0editorPage", None],
254 "editorSearchPage":
255 [self.tr("Searching"), "preferences-search",
256 "EditorSearchPage", "0editorPage", None],
257 "editorSpellCheckingPage":
258 [self.tr("Spell checking"),
259 "preferences-spellchecking",
260 "EditorSpellCheckingPage", "0editorPage", None],
261 "editorStylesPage":
262 [self.tr("Style"), "preferences-styles",
263 "EditorStylesPage", "0editorPage", None],
264 "editorSyntaxPage":
265 [self.tr("Code Checkers"), "preferences-debugger",
266 "EditorSyntaxPage", "0editorPage", None],
267 "editorTypingPage":
268 [self.tr("Typing"), "preferences-typing",
269 "EditorTypingPage", "0editorPage", None],
270 "editorExportersPage":
271 [self.tr("Exporters"), "preferences-exporters",
272 "EditorExportersPage", "0editorPage", None],
273
274 "1editorAutocompletionPage":
275 [self.tr("Autocompletion"),
276 "preferences-autocompletion",
277 "EditorAutocompletionPage", "0editorPage", None],
278 "editorAutocompletionQScintillaPage":
279 [self.tr("QScintilla"), "qscintilla",
280 "EditorAutocompletionQScintillaPage",
281 "1editorAutocompletionPage", None],
282 "editorAutocompletionJediPage":
283 [self.tr("Jedi"), "jedi",
284 "EditorAutoCompletionJediPage",
285 "1editorAutocompletionPage", None],
286
287 "1editorCalltipsPage":
288 [self.tr("Calltips"), "preferences-calltips",
289 "EditorCalltipsPage", "0editorPage", None],
290 "editorCalltipsQScintillaPage":
291 [self.tr("QScintilla"), "qscintilla",
292 "EditorCalltipsQScintillaPage", "1editorCalltipsPage", None],
293 "editorCalltipsJediPage":
294 [self.tr("Jedi"), "jedi",
295 "EditorCallTipsJediPage", "1editorCalltipsPage", None],
296
297 "1editorLexerPage":
298 [self.tr("Highlighters"),
299 "preferences-highlighting-styles",
300 None, "0editorPage", None],
301 "editorHighlightersPage":
302 [self.tr("Filetype Associations"),
303 "preferences-highlighter-association",
304 "EditorHighlightersPage", "1editorLexerPage", None],
305 "editorHighlightingStylesPage":
306 [self.tr("Styles"),
307 "preferences-highlighting-styles",
308 "EditorHighlightingStylesPage", "1editorLexerPage", None],
309 "editorKeywordsPage":
310 [self.tr("Keywords"), "preferences-keywords",
311 "EditorKeywordsPage", "1editorLexerPage", None],
312 "editorPropertiesPage":
313 [self.tr("Properties"), "preferences-properties",
314 "EditorPropertiesPage", "1editorLexerPage", None],
315
316 "1editorMouseClickHandlers":
317 [self.tr("Mouse Click Handlers"),
318 "preferences-mouse-click-handler",
319 "EditorMouseClickHandlerPage", "0editorPage", None],
320 "editorMouseClickHandlerJediPage":
321 [self.tr("Jedi"), "jedi",
322 "EditorMouseClickHandlerJediPage",
323 "1editorMouseClickHandlers", None],
324
325 "0helpPage":
326 [self.tr("Help"), "preferences-help",
327 None, None, None],
328 "helpDocumentationPage":
329 [self.tr("Help Documentation"),
330 "preferences-helpdocumentation",
331 "HelpDocumentationPage", "0helpPage", None],
332 "helpViewersPage":
333 [self.tr("Help Viewers"),
334 "preferences-helpviewers",
335 "HelpViewersPage", "0helpPage", None],
336
337 "0projectPage":
338 [self.tr("Project"), "preferences-project",
339 None, None, None],
340 "projectBrowserPage":
341 [self.tr("Project Viewer"), "preferences-project",
342 "ProjectBrowserPage", "0projectPage", None],
343 "projectPage":
344 [self.tr("Project"), "preferences-project",
345 "ProjectPage", "0projectPage", None],
346 "multiProjectPage":
347 [self.tr("Multiproject"),
348 "preferences-multiproject",
349 "MultiProjectPage", "0projectPage", None],
350
351 "0interfacePage":
352 [self.tr("Interface"), "preferences-interface",
353 None, None, None],
354 "interfacePage":
355 [self.tr("Interface"), "preferences-interface",
356 "InterfacePage", "0interfacePage", None],
357 "viewmanagerPage":
358 [self.tr("Viewmanager"), "preferences-viewmanager",
359 "ViewmanagerPage", "0interfacePage", None],
360 }
361 if self.__webEngine:
362 self.configItems.update({
363 "0webBrowserPage":
364 [self.tr("Web Browser"), "ericWeb",
365 None, None, None],
366 "webBrowserAppearancePage":
367 [self.tr("Appearance"), "preferences-styles",
368 "WebBrowserAppearancePage", "0webBrowserPage", None],
369 "webBrowserPage":
370 [self.tr("eric Web Browser"), "ericWeb",
371 "WebBrowserPage", "0webBrowserPage", None],
372 "webBrowserVirusTotalPage":
373 [self.tr("VirusTotal Interface"), "virustotal",
374 "WebBrowserVirusTotalPage", "0webBrowserPage", None],
375 "webBrowserSpellCheckingPage":
376 [self.tr("Spell checking"),
377 "preferences-spellchecking",
378 "WebBrowserSpellCheckingPage", "0webBrowserPage",
379 None],
380 })
381
382 self.configItems.update(
383 ericApp().getObject("PluginManager").getPluginConfigData())
384
385 elif displayMode == ConfigurationMode.EDITORMODE:
386 self.configItems = {
387 # key : [display string, pixmap name, dialog module name or
388 # page creation function, parent key,
389 # reference to configuration page (must always be last)]
390 # The dialog module must have the module function 'create' to
391 # create the configuration page. This must have the method
392 # 'save' to save the settings.
393 "iconsPage":
394 [self.tr("Icons"), "preferences-icons",
395 "IconsPage", None, None],
396 "interfacePage":
397 [self.tr("Interface"), "preferences-interface",
398 "InterfaceLightPage", None, None],
399 "printerPage":
400 [self.tr("Printer"), "preferences-printer",
401 "PrinterPage", None, None],
402
403 "0editorPage":
404 [self.tr("Editor"), "preferences-editor",
405 None, None, None],
406 "editorGeneralPage":
407 [self.tr("General"), "preferences-general",
408 "EditorGeneralPage", "0editorPage", None],
409 "editorFilePage":
410 [self.tr("Filehandling"),
411 "preferences-filehandling",
412 "EditorFilePage", "0editorPage", None],
413 "editorSearchPage":
414 [self.tr("Searching"), "preferences-search",
415 "EditorSearchPage", "0editorPage", None],
416 "editorSpellCheckingPage":
417 [self.tr("Spell checking"),
418 "preferences-spellchecking",
419 "EditorSpellCheckingPage", "0editorPage", None],
420 "editorStylesPage":
421 [self.tr("Style"), "preferences-styles",
422 "EditorStylesPage", "0editorPage", None],
423 "editorTypingPage":
424 [self.tr("Typing"), "preferences-typing",
425 "EditorTypingPage", "0editorPage", None],
426
427 "1editorLexerPage":
428 [self.tr("Highlighters"),
429 "preferences-highlighting-styles",
430 None, "0editorPage", None],
431 "editorHighlightersPage":
432 [self.tr("Filetype Associations"),
433 "preferences-highlighter-association",
434 "EditorHighlightersPage", "1editorLexerPage", None],
435 "editorHighlightingStylesPage":
436 [self.tr("Styles"),
437 "preferences-highlighting-styles",
438 "EditorHighlightingStylesPage", "1editorLexerPage", None],
439 "editorKeywordsPage":
440 [self.tr("Keywords"), "preferences-keywords",
441 "EditorKeywordsPage", "1editorLexerPage", None],
442 "editorPropertiesPage":
443 [self.tr("Properties"), "preferences-properties",
444 "EditorPropertiesPage", "1editorLexerPage", None],
445 }
446
447 elif displayMode == ConfigurationMode.WEBBROWSERMODE:
448 self.configItems = {
449 # key : [display string, pixmap name, dialog module name or
450 # page creation function, parent key,
451 # reference to configuration page (must always be last)]
452 # The dialog module must have the module function 'create' to
453 # create the configuration page. This must have the method
454 # 'save' to save the settings.
455 "iconsPage":
456 [self.tr("Icons"), "preferences-icons",
457 "IconsPage", None, None],
458 "interfacePage":
459 [self.tr("Interface"), "preferences-interface",
460 "InterfaceLightPage", None, None],
461 "networkPage":
462 [self.tr("Network"), "preferences-network",
463 "NetworkPage", None, None],
464 "printerPage":
465 [self.tr("Printer"), "preferences-printer",
466 "PrinterPage", None, None],
467 "securityPage":
468 [self.tr("Security"), "preferences-security",
469 "SecurityPage", None, None],
470
471 "helpDocumentationPage":
472 [self.tr("Help Documentation"),
473 "preferences-helpdocumentation",
474 "HelpDocumentationPage", None, None],
475
476 "webBrowserAppearancePage":
477 [self.tr("Appearance"), "preferences-styles",
478 "WebBrowserAppearancePage", None, None],
479 "webBrowserPage":
480 [self.tr("eric Web Browser"), "ericWeb",
481 "WebBrowserPage", None, None],
482
483 "webBrowserVirusTotalPage":
484 [self.tr("VirusTotal Interface"), "virustotal",
485 "WebBrowserVirusTotalPage", None, None],
486
487 "webBrowserSpellCheckingPage":
488 [self.tr("Spell checking"),
489 "preferences-spellchecking",
490 "WebBrowserSpellCheckingPage", None, None],
491 }
492
493 elif displayMode == ConfigurationMode.TRAYSTARTERMODE:
494 self.configItems = {
495 # key : [display string, pixmap name, dialog module name or
496 # page creation function, parent key,
497 # reference to configuration page (must always be last)]
498 # The dialog module must have the module function 'create' to
499 # create the configuration page. This must have the method
500 # 'save' to save the settings.
501 "trayStarterPage":
502 [self.tr("Tray Starter"), "erict",
503 "TrayStarterPage", None, None],
504 }
505
506 elif displayMode == ConfigurationMode.HEXEDITORMODE:
507 self.configItems = {
508 # key : [display string, pixmap name, dialog module name or
509 # page creation function, parent key,
510 # reference to configuration page (must always be last)]
511 # The dialog module must have the module function 'create' to
512 # create the configuration page. This must have the method
513 # 'save' to save the settings.
514 "iconsPage":
515 [self.tr("Icons"), "preferences-icons",
516 "IconsPage", None, None],
517 "interfacePage":
518 [self.tr("Interface"), "preferences-interface",
519 "InterfaceLightPage", None, None],
520 "hexEditorPage":
521 [self.tr("Hex Editor"), "hexEditor",
522 "HexEditorPage", None, None],
523 }
524
525 else:
526 # display mode for generic use
527 self.configItems = {
528 # key : [display string, pixmap name, dialog module name or
529 # page creation function, parent key,
530 # reference to configuration page (must always be last)]
531 # The dialog module must have the module function 'create' to
532 # create the configuration page. This must have the method
533 # 'save' to save the settings.
534 "iconsPage":
535 [self.tr("Icons"), "preferences-icons",
536 "IconsPage", None, None],
537 "interfacePage":
538 [self.tr("Interface"), "preferences-interface",
539 "InterfaceLightPage", None, None],
540 }
541
542 # generate the list entries
543 self.__expandedEntries = []
544 for key in sorted(self.configItems.keys()):
545 pageData = self.configItems[key]
546 if pageData[3]:
547 if pageData[3] in self.itmDict:
548 pitm = self.itmDict[pageData[3]] # get the parent item
549 else:
550 continue
551 else:
552 pitm = self.configList
553 self.itmDict[key] = ConfigurationPageItem(pitm, pageData[0], key,
554 pageData[1])
555 self.itmDict[key].setData(0, Qt.ItemDataRole.UserRole, key)
556 if (
557 not self.fromEric or
558 displayMode != ConfigurationMode.DEFAULTMODE or
559 key in expandedEntries
560 ):
561 self.itmDict[key].setExpanded(True)
562 self.configList.sortByColumn(0, Qt.SortOrder.AscendingOrder)
563
564 # set the initial size of the splitter
565 self.configSplitter.setSizes([200, 600])
566 self.configSplitter.splitterMoved.connect(self.__resizeConfigStack)
567
568 self.configList.itemActivated.connect(self.__showConfigurationPage)
569 self.configList.itemClicked.connect(self.__showConfigurationPage)
570 self.buttonBox.accepted.connect(self.accept)
571 self.buttonBox.rejected.connect(self.rejected)
572
573 if displayMode in [ConfigurationMode.TRAYSTARTERMODE,
574 ConfigurationMode.HEXEDITORMODE,
575 ConfigurationMode.WEBBROWSERMODE]:
576 self.configListSearch.hide()
577
578 if displayMode not in [ConfigurationMode.TRAYSTARTERMODE,
579 ConfigurationMode.HEXEDITORMODE]:
580 self.__initLexers()
581
582 def accept(self):
583 """
584 Public slot to accept the buttonBox accept signal.
585 """
586 if not isMacPlatform():
587 wdg = self.focusWidget()
588 if wdg == self.configList:
589 return
590
591 self.accepted.emit()
592
593 def __setupUi(self):
594 """
595 Private method to perform the general setup of the configuration
596 widget.
597 """
598 self.setObjectName("ConfigurationDialog")
599 self.resize(900, 750)
600 self.verticalLayout_2 = QVBoxLayout(self)
601 self.verticalLayout_2.setSpacing(6)
602 self.verticalLayout_2.setContentsMargins(6, 6, 6, 6)
603 self.verticalLayout_2.setObjectName("verticalLayout_2")
604
605 self.configSplitter = QSplitter(self)
606 self.configSplitter.setOrientation(Qt.Orientation.Horizontal)
607 self.configSplitter.setObjectName("configSplitter")
608
609 self.configListWidget = QWidget(self.configSplitter)
610 self.leftVBoxLayout = QVBoxLayout(self.configListWidget)
611 self.leftVBoxLayout.setContentsMargins(0, 0, 0, 0)
612 self.leftVBoxLayout.setSpacing(0)
613 self.leftVBoxLayout.setObjectName("leftVBoxLayout")
614 self.configListSearch = QLineEdit(self)
615 self.configListSearch.setPlaceholderText(
616 self.tr("Enter search text..."))
617 self.configListSearch.setClearButtonEnabled(True)
618 self.configListSearch.setObjectName("configListSearch")
619 self.configListSearch.setClearButtonEnabled(True)
620 self.leftVBoxLayout.addWidget(self.configListSearch)
621 self.configList = QTreeWidget()
622 self.configList.setObjectName("configList")
623 self.leftVBoxLayout.addWidget(self.configList)
624 self.configListSearch.textChanged.connect(self.__searchTextChanged)
625
626 self.scrollArea = QScrollArea(self.configSplitter)
627 self.scrollArea.setFrameShape(QFrame.Shape.NoFrame)
628 self.scrollArea.setVerticalScrollBarPolicy(
629 Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
630 self.scrollArea.setHorizontalScrollBarPolicy(
631 Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
632 self.scrollArea.setWidgetResizable(False)
633 self.scrollArea.setSizeAdjustPolicy(
634 QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents)
635 self.scrollArea.setObjectName("scrollArea")
636
637 self.configStack = QStackedWidget()
638 self.configStack.setFrameShape(QFrame.Shape.Box)
639 self.configStack.setFrameShadow(QFrame.Shadow.Sunken)
640 self.configStack.setObjectName("configStack")
641 self.scrollArea.setWidget(self.configStack)
642
643 self.emptyPage = QWidget()
644 self.emptyPage.setGeometry(QRect(0, 0, 372, 591))
645 self.emptyPage.setObjectName("emptyPage")
646 self.vboxlayout = QVBoxLayout(self.emptyPage)
647 self.vboxlayout.setSpacing(6)
648 self.vboxlayout.setContentsMargins(6, 6, 6, 6)
649 self.vboxlayout.setObjectName("vboxlayout")
650 spacerItem = QSpacerItem(
651 20, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
652 self.vboxlayout.addItem(spacerItem)
653 self.emptyPagePixmap = QLabel(self.emptyPage)
654 self.emptyPagePixmap.setAlignment(Qt.AlignmentFlag.AlignCenter)
655 self.emptyPagePixmap.setObjectName("emptyPagePixmap")
656 self.emptyPagePixmap.setPixmap(
657 QPixmap(os.path.join(getConfig('ericPixDir'), 'eric.png')))
658 self.vboxlayout.addWidget(self.emptyPagePixmap)
659 self.textLabel1 = QLabel(self.emptyPage)
660 self.textLabel1.setAlignment(Qt.AlignmentFlag.AlignCenter)
661 self.textLabel1.setObjectName("textLabel1")
662 self.vboxlayout.addWidget(self.textLabel1)
663 spacerItem1 = QSpacerItem(
664 20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
665 self.vboxlayout.addItem(spacerItem1)
666 self.configStack.addWidget(self.emptyPage)
667
668 self.verticalLayout_2.addWidget(self.configSplitter)
669
670 self.buttonBox = QDialogButtonBox(self)
671 self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
672 self.buttonBox.setStandardButtons(
673 QDialogButtonBox.StandardButton.Apply |
674 QDialogButtonBox.StandardButton.Cancel |
675 QDialogButtonBox.StandardButton.Ok |
676 QDialogButtonBox.StandardButton.Reset
677 )
678 self.buttonBox.setObjectName("buttonBox")
679 if (
680 not self.fromEric and
681 self.displayMode == ConfigurationMode.DEFAULTMODE
682 ):
683 self.buttonBox.button(QDialogButtonBox.StandardButton.Apply).hide()
684 self.buttonBox.button(
685 QDialogButtonBox.StandardButton.Apply).setEnabled(False)
686 self.buttonBox.button(
687 QDialogButtonBox.StandardButton.Reset).setEnabled(False)
688 self.verticalLayout_2.addWidget(self.buttonBox)
689
690 self.setWindowTitle(self.tr("Preferences"))
691
692 self.configList.header().hide()
693 self.configList.header().setSortIndicator(
694 0, Qt.SortOrder.AscendingOrder)
695 self.configList.setSortingEnabled(True)
696 self.textLabel1.setText(
697 self.tr("Please select an entry of the list \n"
698 "to display the configuration page."))
699
700 QMetaObject.connectSlotsByName(self)
701 self.setTabOrder(self.configList, self.configStack)
702
703 self.configStack.setCurrentWidget(self.emptyPage)
704
705 self.configList.setFocus()
706
707 def __searchTextChanged(self, text):
708 """
709 Private slot to handle a change of the search text.
710
711 @param text text to search for (string)
712 """
713 self.__searchChildItems(self.configList.invisibleRootItem(), text)
714
715 def __searchChildItems(self, parent, text):
716 """
717 Private method to enable child items based on a search string.
718
719 @param parent reference to the parent item (QTreeWidgetItem)
720 @param text text to search for (string)
721 @return flag indicating an enabled child item (boolean)
722 """
723 childEnabled = False
724 text = text.lower()
725 for index in range(parent.childCount()):
726 itm = parent.child(index)
727 enable = (
728 (self.__searchChildItems(itm, text) or
729 text == "" or
730 text in itm.text(0).lower())
731 if itm.childCount() > 0 else
732 (text == "" or text in itm.text(0).lower())
733 )
734 if enable:
735 childEnabled = True
736 itm.setDisabled(not enable)
737
738 return childEnabled
739
740 def __initLexers(self):
741 """
742 Private method to initialize the dictionary of preferences lexers.
743 """
744 import QScintilla.Lexers
745 from .PreferencesLexer import (
746 PreferencesLexer, PreferencesLexerLanguageError
747 )
748
749 self.lexers = {}
750 for language in QScintilla.Lexers.getSupportedLanguages():
751 if language not in self.lexers:
752 with contextlib.suppress(PreferencesLexerLanguageError):
753 self.lexers[language] = PreferencesLexer(language, self)
754
755 def __importConfigurationPage(self, name):
756 """
757 Private method to import a configuration page module.
758
759 @param name name of the configuration page module (string)
760 @return reference to the configuration page module
761 """
762 modName = "Preferences.ConfigurationPages.{0}".format(name)
763 try:
764 mod = __import__(modName)
765 components = modName.split('.')
766 for comp in components[1:]:
767 mod = getattr(mod, comp)
768 return mod
769 except ImportError:
770 EricMessageBox.critical(
771 self,
772 self.tr("Configuration Page Error"),
773 self.tr("""<p>The configuration page <b>{0}</b>"""
774 """ could not be loaded.</p>""").format(name))
775 return None
776
777 def __showConfigurationPage(self, itm, column):
778 """
779 Private slot to show a selected configuration page.
780
781 @param itm reference to the selected item (QTreeWidgetItem)
782 @param column column that was selected (integer) (ignored)
783 """
784 pageName = itm.getPageName()
785 self.showConfigurationPageByName(pageName, setCurrent=False)
786
787 def __initPage(self, pageData):
788 """
789 Private method to initialize a configuration page.
790
791 @param pageData data structure for the page to initialize
792 @return reference to the initialized page
793 """
794 page = None
795 if isinstance(pageData[2], types.FunctionType):
796 page = pageData[2](self)
797 else:
798 mod = self.__importConfigurationPage(pageData[2])
799 if mod:
800 page = mod.create(self)
801 if page is not None:
802 self.configStack.addWidget(page)
803 pageData[-1] = page
804 with contextlib.suppress(AttributeError):
805 page.setMode(self.displayMode)
806 return page
807
808 def showConfigurationPageByName(self, pageName, setCurrent=True):
809 """
810 Public slot to show a named configuration page.
811
812 @param pageName name of the configuration page to show (string)
813 @param setCurrent flag indicating to set the current item (boolean)
814 """
815 if pageName == "empty" or pageName not in self.configItems:
816 page = self.emptyPage
817 else:
818 pageData = self.configItems[pageName]
819 if pageData[-1] is None and pageData[2] is not None:
820 # the page was not loaded yet, create it
821 page = self.__initPage(pageData)
822 else:
823 page = pageData[-1]
824 if page is None:
825 page = self.emptyPage
826 elif setCurrent:
827 items = self.configList.findItems(
828 pageData[0],
829 Qt.MatchFlag.MatchFixedString |
830 Qt.MatchFlag.MatchRecursive)
831 for item in items:
832 if item.data(0, Qt.ItemDataRole.UserRole) == pageName:
833 self.configList.setCurrentItem(item)
834 self.configStack.setCurrentWidget(page)
835 self.__resizeConfigStack()
836
837 if page != self.emptyPage:
838 page.polishPage()
839 self.buttonBox.button(
840 QDialogButtonBox.StandardButton.Apply).setEnabled(True)
841 self.buttonBox.button(
842 QDialogButtonBox.StandardButton.Reset).setEnabled(True)
843 else:
844 self.buttonBox.button(
845 QDialogButtonBox.StandardButton.Apply).setEnabled(False)
846 self.buttonBox.button(
847 QDialogButtonBox.StandardButton.Reset).setEnabled(False)
848
849 # reset scrollbars
850 for sb in [self.scrollArea.horizontalScrollBar(),
851 self.scrollArea.verticalScrollBar()]:
852 if sb:
853 sb.setValue(0)
854
855 self.__currentConfigurationPageName = pageName
856
857 def resizeEvent(self, evt):
858 """
859 Protected method to handle the resizing of the widget.
860
861 @param evt reference to the event object
862 @type QResizeEvent
863 """
864 self.__resizeConfigStack()
865
866 def __resizeConfigStack(self):
867 """
868 Private method to resize the stack of configuration pages.
869 """
870 ssize = self.scrollArea.size()
871 if self.scrollArea.horizontalScrollBar():
872 ssize.setHeight(
873 ssize.height() -
874 self.scrollArea.horizontalScrollBar().height() - 2)
875 if self.scrollArea.verticalScrollBar():
876 ssize.setWidth(
877 ssize.width() -
878 self.scrollArea.verticalScrollBar().width() - 2)
879 psize = self.configStack.currentWidget().minimumSizeHint()
880 self.configStack.resize(max(ssize.width(), psize.width()),
881 max(ssize.height(), psize.height()))
882
883 def getConfigurationPageName(self):
884 """
885 Public method to get the page name of the current page.
886
887 @return page name of the current page (string)
888 """
889 return self.__currentConfigurationPageName
890
891 def calledFromEric(self):
892 """
893 Public method to check, if invoked from within eric.
894
895 @return flag indicating invocation from within eric (boolean)
896 """
897 return self.fromEric
898
899 def getPage(self, pageName):
900 """
901 Public method to get a reference to the named page.
902
903 @param pageName name of the configuration page (string)
904 @return reference to the page or None, indicating page was
905 not loaded yet
906 """
907 return self.configItems[pageName][-1]
908
909 def getLexers(self):
910 """
911 Public method to get a reference to the lexers dictionary.
912
913 @return reference to the lexers dictionary
914 """
915 return self.lexers
916
917 def setPreferences(self):
918 """
919 Public method called to store the selected values into the preferences
920 storage.
921 """
922 now = time.monotonic()
923 for pageData in self.configItems.values():
924 if pageData[-1]:
925 pageData[-1].save()
926 # page was loaded (and possibly modified)
927 if time.monotonic() - now > 0.01:
928 QApplication.processEvents() # ensure HMI is responsive
929 now = time.monotonic()
930
931 def on_buttonBox_clicked(self, button):
932 """
933 Private slot called by a button of the button box clicked.
934
935 @param button button that was clicked (QAbstractButton)
936 """
937 if button == self.buttonBox.button(
938 QDialogButtonBox.StandardButton.Apply
939 ):
940 self.on_applyButton_clicked()
941 elif button == self.buttonBox.button(
942 QDialogButtonBox.StandardButton.Reset
943 ):
944 self.on_resetButton_clicked()
945
946 @pyqtSlot()
947 def on_applyButton_clicked(self):
948 """
949 Private slot called to apply the settings of the current page.
950 """
951 if self.configStack.currentWidget() != self.emptyPage:
952 page = self.configStack.currentWidget()
953 savedState = page.saveState()
954 page.save()
955 self.preferencesChanged.emit()
956 if savedState is not None:
957 page.setState(savedState)
958 page.polishPage()
959
960 @pyqtSlot()
961 def on_resetButton_clicked(self):
962 """
963 Private slot called to reset the settings of the current page.
964 """
965 if self.configStack.currentWidget() != self.emptyPage:
966 currentPage = self.configStack.currentWidget()
967 savedState = currentPage.saveState()
968 pageName = self.configList.currentItem().getPageName()
969 self.configStack.removeWidget(currentPage)
970 if pageName == "editorHighlightingStylesPage":
971 self.__initLexers()
972 self.configItems[pageName][-1] = None
973
974 self.showConfigurationPageByName(pageName)
975 if savedState is not None:
976 self.configStack.currentWidget().setState(savedState)
977
978 def getExpandedEntries(self):
979 """
980 Public method to get a list of expanded entries.
981
982 @return list of expanded entries (list of string)
983 """
984 return self.__expandedEntries
985
986 @pyqtSlot(QTreeWidgetItem)
987 def on_configList_itemCollapsed(self, item):
988 """
989 Private slot handling a list entry being collapsed.
990
991 @param item reference to the collapsed item (QTreeWidgetItem)
992 """
993 pageName = item.data(0, Qt.ItemDataRole.UserRole)
994 if pageName in self.__expandedEntries:
995 self.__expandedEntries.remove(pageName)
996
997 @pyqtSlot(QTreeWidgetItem)
998 def on_configList_itemExpanded(self, item):
999 """
1000 Private slot handling a list entry being expanded.
1001
1002 @param item reference to the expanded item (QTreeWidgetItem)
1003 """
1004 pageName = item.data(0, Qt.ItemDataRole.UserRole)
1005 if pageName not in self.__expandedEntries:
1006 self.__expandedEntries.append(pageName)
1007
1008 def isUsingWebEngine(self):
1009 """
1010 Public method to get an indication, if QtWebEngine is being used.
1011
1012 @return flag indicating the use of QtWebEngine
1013 @rtype bool
1014 """
1015 return (
1016 self.__webEngine or
1017 self.displayMode == ConfigurationMode.WEBBROWSERMODE
1018 )
1019
1020
1021 class ConfigurationDialog(QDialog):
1022 """
1023 Class for the dialog variant.
1024
1025 @signal preferencesChanged() emitted after settings have been changed
1026 @signal masterPasswordChanged(str, str) emitted after the master
1027 password has been changed with the old and the new password
1028 """
1029 preferencesChanged = pyqtSignal()
1030 masterPasswordChanged = pyqtSignal(str, str)
1031
1032 def __init__(self, parent=None, name=None, modal=False,
1033 fromEric=True,
1034 displayMode=ConfigurationMode.DEFAULTMODE,
1035 expandedEntries=None):
1036 """
1037 Constructor
1038
1039 @param parent reference to the parent widget
1040 @type QWidget
1041 @param name name of the dialog
1042 @type str
1043 @param modal flag indicating a modal dialog
1044 @type bool
1045 @param fromEric flag indicating a dialog generation from within the
1046 eric IDE
1047 @type bool
1048 @param displayMode mode of the configuration dialog
1049 @type ConfigurationMode
1050 @param expandedEntries list of entries to be shown expanded
1051 @type list of str
1052 """
1053 super().__init__(parent)
1054 if name:
1055 self.setObjectName(name)
1056 self.setModal(modal)
1057 self.setWindowFlags(Qt.WindowType.Window)
1058
1059 self.layout = QVBoxLayout(self)
1060 self.layout.setContentsMargins(0, 0, 0, 0)
1061 self.layout.setSpacing(0)
1062
1063 self.cw = ConfigurationWidget(self, fromEric=fromEric,
1064 displayMode=displayMode,
1065 expandedEntries=expandedEntries)
1066 size = self.cw.size()
1067 self.layout.addWidget(self.cw)
1068 self.resize(size)
1069 self.setWindowTitle(self.cw.windowTitle())
1070
1071 self.cw.accepted.connect(self.accept)
1072 self.cw.rejected.connect(self.reject)
1073 self.cw.preferencesChanged.connect(self.__preferencesChanged)
1074 self.cw.masterPasswordChanged.connect(self.__masterPasswordChanged)
1075
1076 def __preferencesChanged(self):
1077 """
1078 Private slot to handle a change of the preferences.
1079 """
1080 self.preferencesChanged.emit()
1081
1082 def __masterPasswordChanged(self, oldPassword, newPassword):
1083 """
1084 Private slot to handle the change of the master password.
1085
1086 @param oldPassword current master password (string)
1087 @param newPassword new master password (string)
1088 """
1089 self.masterPasswordChanged.emit(oldPassword, newPassword)
1090
1091 def showConfigurationPageByName(self, pageName):
1092 """
1093 Public slot to show a named configuration page.
1094
1095 @param pageName name of the configuration page to show (string)
1096 """
1097 self.cw.showConfigurationPageByName(pageName)
1098
1099 def getConfigurationPageName(self):
1100 """
1101 Public method to get the page name of the current page.
1102
1103 @return page name of the current page (string)
1104 """
1105 return self.cw.getConfigurationPageName()
1106
1107 def getExpandedEntries(self):
1108 """
1109 Public method to get a list of expanded entries.
1110
1111 @return list of expanded entries (list of string)
1112 """
1113 return self.cw.getExpandedEntries()
1114
1115 def setPreferences(self):
1116 """
1117 Public method called to store the selected values into the preferences
1118 storage.
1119 """
1120 self.cw.setPreferences()
1121
1122 def accept(self):
1123 """
1124 Public method to accept the dialog.
1125 """
1126 super().accept()
1127
1128
1129 class ConfigurationWindow(EricMainWindow):
1130 """
1131 Main window class for the standalone dialog.
1132 """
1133 def __init__(self, parent=None):
1134 """
1135 Constructor
1136
1137 @param parent reference to the parent widget (QWidget)
1138 """
1139 super().__init__(parent)
1140
1141 self.cw = ConfigurationWidget(self, fromEric=False)
1142 size = self.cw.size()
1143 self.setCentralWidget(self.cw)
1144 self.resize(size)
1145 self.setWindowTitle(self.cw.windowTitle())
1146
1147 self.setStyle(Preferences.getUI("Style"),
1148 Preferences.getUI("StyleSheet"))
1149
1150 self.cw.accepted.connect(self.accept)
1151 self.cw.rejected.connect(self.close)
1152
1153 def showConfigurationPageByName(self, pageName):
1154 """
1155 Public slot to show a named configuration page.
1156
1157 @param pageName name of the configuration page to show (string)
1158 """
1159 self.cw.showConfigurationPageByName(pageName)
1160
1161 def accept(self):
1162 """
1163 Public slot called by the Ok button.
1164 """
1165 self.cw.setPreferences()
1166 Preferences.saveResetLayout()
1167 Preferences.syncPreferences()
1168 self.close()

eric ide

mercurial