--- a/src/eric7/UI/UserInterface.py Wed Apr 02 10:51:50 2025 +0200 +++ b/src/eric7/UI/UserInterface.py Thu May 01 12:09:22 2025 +0200 @@ -49,7 +49,6 @@ ) from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkProxyFactory from PyQt6.QtWidgets import ( - QAbstractItemView, QApplication, QDialog, QDockWidget, @@ -80,7 +79,6 @@ from eric7.EricWidgets import EricErrorMessage, EricFileDialog, EricMessageBox from eric7.EricWidgets.EricApplication import ericApp from eric7.EricWidgets.EricClickableLabel import EricClickableLabel -from eric7.EricWidgets.EricListSelectionDialog import EricListSelectionDialog from eric7.EricWidgets.EricMainWindow import EricMainWindow from eric7.EricWidgets.EricSingleApplication import EricSingleApplicationServer from eric7.EricWidgets.EricToolBarManager import EricToolBarManager @@ -94,6 +92,7 @@ from eric7.Project.Project import Project from eric7.QScintilla.SpellChecker import SpellChecker from eric7.RemoteServerInterface.EricServerInterface import EricServerInterface +from eric7.Sessions.CrashedSessionsSelectionDialog import CrashedSessionsSelectionDialog from eric7.Sessions.SessionFile import SessionFile from eric7.SystemUtilities import ( DesktopUtilities, @@ -145,6 +144,7 @@ password has been changed with the old and the new password @signal onlineStateChanged(online) emitted to indicate a change of the network state + @signal shutdown() emitted to indicate a shutdown of the application """ appendStderr = pyqtSignal(str) @@ -154,6 +154,7 @@ showMenu = pyqtSignal(str, QMenu) mainPasswordChanged = pyqtSignal(str, str) onlineStateChanged = pyqtSignal(bool) + shutdown = pyqtSignal() maxFilePathLen = 100 maxMenuFilePathLen = 75 @@ -280,6 +281,12 @@ # register it early because it is needed very soon ericApp().registerObject("EricServer", self.__ericServerInterface) + # Generate the virtual environment manager + logging.getLogger(__name__).debug("Creating Virtual Environments Manager...") + self.virtualenvManager = VirtualenvManager(self) + # register it early because it is needed very soon + ericApp().registerObject("VirtualEnvManager", self.virtualenvManager) + # Generate the conda interface logging.getLogger(__name__).debug("Creating Conda Interface...") self.condaInterface = Conda(self) @@ -292,12 +299,6 @@ # register it early because it is needed very soon ericApp().registerObject("Pip", self.pipInterface) - # Generate the virtual environment manager - logging.getLogger(__name__).debug("Creating Virtual Environments Manager...") - self.virtualenvManager = VirtualenvManager(self) - # register it early because it is needed very soon - ericApp().registerObject("VirtualEnvManager", self.virtualenvManager) - # Generate an empty project object logging.getLogger(__name__).debug("Creating Project Manager...") self.project = Project(self, remoteServer=self.__ericServerInterface) @@ -556,9 +557,6 @@ if self.numbersViewer is not None: self.numbersViewer.insertNumber.connect(self.viewmanager.insertNumber) - if self.irc is not None: - self.irc.autoConnected.connect(self.__ircAutoConnected) - if self.pipWidget is not None: self.preferencesChanged.connect(self.pipWidget.preferencesChanged) @@ -620,8 +618,6 @@ ericApp().registerObject("ToolbarManager", self.toolbarManager) if self.cooperation is not None: ericApp().registerObject("Cooperation", self.cooperation) - if self.irc is not None: - ericApp().registerObject("IRC", self.irc) if self.symbolsViewer is not None: ericApp().registerObject("Symbols", self.symbolsViewer) if self.numbersViewer is not None: @@ -665,7 +661,7 @@ self.currentProfile = None - self.shutdownCalled = False + self.__shutdownCalled = False self.inCloseEvent = False # now fire up the single application server @@ -810,11 +806,11 @@ # Create previewer logging.getLogger(__name__).debug("Creating Previewer...") - self.__previewer = Previewer(self.viewmanager) + self.__previewer = Previewer(viewmanager=self.viewmanager, ui=self) # Create AST viewer logging.getLogger(__name__).debug("Creating Python AST Viewer") - self.__astViewer = PythonAstViewer(self.viewmanager) + self.__astViewer = PythonAstViewer(viewmanager=self.viewmanager, ui=self) # Create DIS viewer logging.getLogger(__name__).debug("Creating Python Disassembly Viewer") @@ -852,7 +848,7 @@ logging.getLogger(__name__).debug("Creating Template Viewer...") from eric7.Templates.TemplateViewer import TemplateViewer # noqa: I-101 - self.templateViewer = TemplateViewer(None, self.viewmanager) + self.templateViewer = TemplateViewer(self, self.viewmanager) else: logging.getLogger(__name__).debug("Template Viewer disabled") self.templateViewer = None @@ -921,16 +917,6 @@ logging.getLogger(__name__).debug("Chat Widget disabled") self.cooperation = None - if Preferences.getUI("ShowIrc"): - # Create the IRC part of the user interface - logging.getLogger(__name__).debug("Creating IRC Widget...") - from eric7.Network.IRC.IrcWidget import IrcWidget # noqa: I-101 - - self.irc = IrcWidget(self) - else: - logging.getLogger(__name__).debug("IRC Widget disabled") - self.irc = None - if Preferences.getUI("ShowMicroPython"): # Create the MicroPython part of the user interface logging.getLogger(__name__).debug("Creating MicroPython Widget...") @@ -1233,11 +1219,6 @@ self.tr("Cooperation"), ) - if self.irc: - self.rToolbox.addItem( - self.irc, EricPixmapCache.getIcon("irc"), self.tr("IRC") - ) - if self.microPythonWidget: self.rToolbox.addItem( self.microPythonWidget, @@ -1433,9 +1414,6 @@ self.tr("Cooperation"), ) - if self.irc: - sidebar.addTab(self.irc, EricPixmapCache.getIcon("sbIrc96"), self.tr("IRC")) - if self.microPythonWidget: sidebar.addTab( self.microPythonWidget, @@ -2371,28 +2349,6 @@ self.actions.append(self.cooperationViewerActivateAct) self.addAction(self.cooperationViewerActivateAct) - if self.irc is not None: - self.ircActivateAct = EricAction( - self.tr("IRC"), - self.tr("&IRC"), - QKeySequence(self.tr("Ctrl+Alt+Shift+I")), - 0, - self, - "irc_widget_activate", - ) - self.ircActivateAct.setStatusTip( - self.tr("Switch the input focus to the IRC window.") - ) - self.ircActivateAct.setWhatsThis( - self.tr( - """<b>Activate IRC</b>""" - """<p>This switches the input focus to the IRC window.</p>""" - ) - ) - self.ircActivateAct.triggered.connect(self.__activateIRC) - self.actions.append(self.ircActivateAct) - self.addAction(self.ircActivateAct) - if self.symbolsViewer is not None: self.symbolsViewerActivateAct = EricAction( self.tr("Symbols-Viewer"), @@ -3647,27 +3603,6 @@ """ Private slot to initialize the action to show the Qt documentation. """ - self.qt5DocAct = EricAction( - self.tr("Qt5 Documentation"), - self.tr("Qt5 Documentation"), - 0, - 0, - self, - "qt5_documentation", - ) - self.qt5DocAct.setStatusTip(self.tr("Open Qt5 Documentation")) - self.qt5DocAct.setWhatsThis( - self.tr( - """<b>Qt5 Documentation</b>""" - """<p>Display the Qt5 Documentation. Dependent upon your""" - """ settings, this will either show the help in Eric's internal""" - """ help viewer/web browser, or execute a web browser or Qt""" - """ Assistant. </p>""" - ) - ) - self.qt5DocAct.triggered.connect(lambda: self.__showQtDoc(5)) - self.actions.append(self.qt5DocAct) - self.qt6DocAct = EricAction( self.tr("Qt6 Documentation"), self.tr("Qt6 Documentation"), @@ -3689,27 +3624,6 @@ self.qt6DocAct.triggered.connect(lambda: self.__showQtDoc(6)) self.actions.append(self.qt6DocAct) - self.pyqt5DocAct = EricAction( - self.tr("PyQt5 Documentation"), - self.tr("PyQt5 Documentation"), - 0, - 0, - self, - "pyqt5_documentation", - ) - self.pyqt5DocAct.setStatusTip(self.tr("Open PyQt5 Documentation")) - self.pyqt5DocAct.setWhatsThis( - self.tr( - """<b>PyQt5 Documentation</b>""" - """<p>Display the PyQt5 Documentation. Dependent upon your""" - """ settings, this will either show the help in Eric's""" - """ internal help viewer/web browser, or execute a web""" - """ browser or Qt Assistant. </p>""" - ) - ) - self.pyqt5DocAct.triggered.connect(lambda: self.__showPyQtDoc(variant=5)) - self.actions.append(self.pyqt5DocAct) - self.pyqt6DocAct = EricAction( self.tr("PyQt6 Documentation"), self.tr("PyQt6 Documentation"), @@ -3788,32 +3702,6 @@ Private slot to initialize the actions to show the PySide documentation. """ - if QtUtilities.checkPyside(variant=2): - self.pyside2DocAct = EricAction( - self.tr("PySide2 Documentation"), - self.tr("PySide2 Documentation"), - 0, - 0, - self, - "pyside2_documentation", - ) - self.pyside2DocAct.setStatusTip(self.tr("Open PySide2 Documentation")) - self.pyside2DocAct.setWhatsThis( - self.tr( - """<b>PySide2 Documentation</b>""" - """<p>Display the PySide2 Documentation. Dependent upon your""" - """ settings, this will either show the help in Eric's""" - """ internal help viewer/web browser, or execute a web""" - """ browser or Qt Assistant. </p>""" - ) - ) - self.pyside2DocAct.triggered.connect( - lambda: self.__showPySideDoc(variant=2) - ) - self.actions.append(self.pyside2DocAct) - else: - self.pyside2DocAct = None - if QtUtilities.checkPyside(variant=6): self.pyside6DocAct = EricAction( self.tr("PySide6 Documentation"), @@ -4098,8 +3986,6 @@ self.__menus["subwindow"].addAction(self.condaWidgetActivateAct) if self.cooperation is not None: self.__menus["subwindow"].addAction(self.cooperationViewerActivateAct) - if self.irc is not None: - self.__menus["subwindow"].addAction(self.ircActivateAct) if self.microPythonWidget is not None: self.__menus["subwindow"].addAction(self.microPythonWidgetActivateAct) @@ -4139,12 +4025,8 @@ self.__menus["help"].addSeparator() self.__menus["help"].addAction(self.ericDocAct) self.__menus["help"].addAction(self.pythonDocAct) - self.__menus["help"].addAction(self.qt5DocAct) self.__menus["help"].addAction(self.qt6DocAct) - self.__menus["help"].addAction(self.pyqt5DocAct) self.__menus["help"].addAction(self.pyqt6DocAct) - if self.pyside2DocAct is not None: - self.__menus["help"].addAction(self.pyside2DocAct) if self.pyside6DocAct is not None: self.__menus["help"].addAction(self.pyside6DocAct) self.__menus["help"].addSeparator() @@ -5928,18 +5810,6 @@ self.activateLeftRightSidebarWidget(self.cooperation) self.cooperation.setFocus(Qt.FocusReason.ActiveWindowFocusReason) - def __activateIRC(self): - """ - Private slot to handle the activation of the IRC window. - """ - if self.irc is not None: - if self.__layoutType == "Toolboxes": - self.rToolboxDock.show() - self.rToolbox.setCurrentWidget(self.irc) - elif self.__layoutType == "Sidebars": - self.activateLeftRightSidebarWidget(self.irc) - self.irc.setFocus(Qt.FocusReason.ActiveWindowFocusReason) - def __activateSymbolsViewer(self): """ Private slot to handle the activation of the Symbols Viewer. @@ -6954,11 +6824,11 @@ else: self.__customViewer(home) - def __showPyQtDoc(self, variant=5): - """ - Private slot to show the PyQt5/6 documentation. - - @param variant PyQt variant to show documentation for (5 or 6) + def __showPyQtDoc(self, variant=6): + """ + Private slot to show the PyQt documentation. + + @param variant PyQt variant to show documentation for @type int or str """ pyqtDocDir = Preferences.getHelp("PyQt{0}DocDir".format(variant)) @@ -7072,11 +6942,11 @@ else: self.__customViewer(home) - def __showPySideDoc(self, variant=2): - """ - Private slot to show the PySide2/PySide6 documentation. - - @param variant PySide variant (2 or 6) + def __showPySideDoc(self, variant=6): + """ + Private slot to show the PySide documentation. + + @param variant PySide variant @type int or str """ pysideDocDir = Preferences.getHelp("PySide{0}DocDir".format(variant)) @@ -7958,19 +7828,11 @@ selectedCrashSessionFile = "" crashedSessionsList = self.__getCrashedSessions() if crashedSessionsList: - dlg = EricListSelectionDialog( - sorted(crashedSessionsList), - selectionMode=QAbstractItemView.SelectionMode.SingleSelection, - title=self.tr("Found Crash Sessions"), - message=self.tr( - "These crash session files were found. Select the one to" - " open. Select 'Cancel' to not open a crash session." - ), - doubleClickOk=True, - parent=self, + dlg = CrashedSessionsSelectionDialog( + sorted(crashedSessionsList), parent=self ) if dlg.exec() == QDialog.DialogCode.Accepted: - selectedCrashSessionFile = dlg.getSelection()[0] + selectedCrashSessionFile = dlg.getSelectedCrashSession() return selectedCrashSessionFile @@ -8029,20 +7891,14 @@ """ Private slot to clean all stale crash sessions. """ - from .DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog - crashedSessionsList = self.__getCrashedSessions() if crashedSessionsList: - dlg = DeleteFilesConfirmationDialog( - parent=self, - caption=self.tr("Clean stale crash sessions"), - message=self.tr( - "Do you really want to delete these stale crash session files?" - ), - files=sorted(crashedSessionsList), + dlg = CrashedSessionsSelectionDialog( + sorted(crashedSessionsList), deleteMode=True, parent=self ) if dlg.exec() == QDialog.DialogCode.Accepted: - for crashSession in crashedSessionsList: + selectedCrashSessionFiles = dlg.getSelectedCrashSessions() + for crashSession in selectedCrashSessionFiles: os.remove(crashSession) def showFindFileByNameDialog(self): @@ -8448,7 +8304,7 @@ @return flag indicating success @rtype bool """ - if self.shutdownCalled: + if self.__shutdownCalled: return True if not self.viewmanager.checkAllDirty(): @@ -8457,13 +8313,8 @@ if self.__webBrowserProcess is not None: self.__webBrowserShutdown() - if self.irc is not None and not self.irc.shutdown(): - return False - sessionCreated = self.__writeSession() - self.__astViewer.hide() - self.shell.closeShell() if not self.project.closeProject(shutdown=True): @@ -8482,37 +8333,12 @@ if sessionCreated and not self.__disableCrashSession: self.__deleteCrashSession() - if self.codeDocumentationViewer is not None: - self.codeDocumentationViewer.shutdown() - - self.__previewer.shutdown() - - self.__astViewer.shutdown() - self.__writeTasks() - if self.templateViewer is not None: - self.templateViewer.save() - if not self.debuggerUI.shutdownServer(): return False - self.debuggerUI.shutdown() - - self.backgroundService.shutdown() - - if self.cooperation is not None: - self.cooperation.shutdown() - - if self.__helpViewerWidget is not None: - self.__helpViewerWidget.shutdown() - - if self.microPythonWidget is not None: - self.microPythonWidget.shutdown() - - self.pipInterface.shutdown() - - self.pluginManager.doShutdown() - + + # stop the single application server if self.SAServer is not None: self.SAServer.shutdown() self.SAServer = None @@ -8531,7 +8357,10 @@ self.__saveCurrentViewProfile(True) Preferences.saveToolGroups(self.toolGroups, self.currentToolGroup) Preferences.syncPreferences() - self.shutdownCalled = True + + # emit the shutdown() signal to allow connected parts to perform their + # individual shutdown actions + self.shutdown.emit() # shut down the global file system watcher EricFileSystemWatcher.instance().shutdown() @@ -8540,6 +8369,8 @@ sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ + self.__shutdownCalled = True + return True def isOnline(self): @@ -8746,7 +8577,7 @@ def getOriginalPathString(self): """ Public method to get the original PATH environment variable - (i.e. before modifications by eric and PyQt5). + (i.e. before modifications by eric). @return original PATH environment variable @rtype str @@ -8804,23 +8635,6 @@ icon, heading, text, kind=kind, timeout=timeout ) - ######################### - ## Support for IRC below - ######################### - - def autoConnectIrc(self): - """ - Public method to initiate the IRC auto connection. - """ - if self.irc is not None: - self.irc.autoConnect() - - def __ircAutoConnected(self): - """ - Private slot handling the automatic connection of the IRC client. - """ - self.__activateIRC() - ############################################## ## Support for Code Documentation Viewer below ##############################################