From 27daa25c6fffad926c74fdd792ca0818840b5337 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 7 Jul 2021 00:38:59 -0500 Subject: [PATCH 1/5] Widgets: Make MessageCheckBox non-modal by default --- spyder/widgets/helperwidgets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spyder/widgets/helperwidgets.py b/spyder/widgets/helperwidgets.py index f2706c765b1..2a04be3c9d9 100644 --- a/spyder/widgets/helperwidgets.py +++ b/spyder/widgets/helperwidgets.py @@ -67,7 +67,8 @@ class MessageCheckBox(QMessageBox): def __init__(self, *args, **kwargs): super(MessageCheckBox, self).__init__(*args, **kwargs) - self._checkbox = QCheckBox() + self.setWindowModality(Qt.NonModal) + self._checkbox = QCheckBox(self) # Set layout to include checkbox size = 9 From 4f08055a1d1d89e07239b969d9f91b79d75d8b45 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 7 Jul 2021 00:58:04 -0500 Subject: [PATCH 2/5] Application: Delay check for a new Spyder version Also prevent message boxes to steal focus from the main window --- spyder/plugins/application/container.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/spyder/plugins/application/container.py b/spyder/plugins/application/container.py index f3dd0d7a43b..51d930863d7 100644 --- a/spyder/plugins/application/container.py +++ b/spyder/plugins/application/container.py @@ -15,8 +15,8 @@ import sys # Third party imports -from qtpy.QtCore import Qt, QThread, Slot -from qtpy.QtWidgets import QMessageBox, QAction +from qtpy.QtCore import Qt, QThread, QTimer, Slot +from qtpy.QtWidgets import QAction, QMessageBox # Local imports from spyder import ( @@ -174,6 +174,7 @@ def _check_updates_ready(self): box = MessageCheckBox(icon=QMessageBox.Information, parent=self) box.setWindowTitle(_("New Spyder version")) + box.setAttribute(Qt.WA_ShowWithoutActivating) box.set_checkbox_text(_("Check for updates at startup")) box.setStandardButtons(QMessageBox.Ok) box.setDefaultButton(QMessageBox.Ok) @@ -216,7 +217,7 @@ def _check_updates_ready(self): msg = header + content + footer box.setText(msg) box.set_check_visible(True) - box.exec_() + box.show() check_updates = box.is_checked() elif feedback: msg = _("Spyder is up to date.") @@ -249,7 +250,15 @@ def check_updates(self, startup=False): self.worker_updates.sig_ready.connect(self.thread_updates.quit) self.worker_updates.moveToThread(self.thread_updates) self.thread_updates.started.connect(self.worker_updates.start) - self.thread_updates.start() + + # Delay starting this check to avoid blocking the main window + # while loading. + # Fixes spyder-ide/spyder#15839 + updates_timer = QTimer(self) + updates_timer.setInterval(15000) + updates_timer.setSingleShot(True) + updates_timer.timeout.connect(self.thread_updates.start) + updates_timer.start() @Slot() def show_dependencies(self): @@ -304,6 +313,7 @@ def report_missing_dependencies(self): message_box = QMessageBox(self) message_box.setIcon(QMessageBox.Critical) message_box.setAttribute(Qt.WA_DeleteOnClose) + message_box.setAttribute(Qt.WA_ShowWithoutActivating) message_box.setStandardButtons(QMessageBox.Ok) message_box.setWindowModality(Qt.NonModal) message_box.setWindowTitle(_('Error')) From 133e92aa0275cb9488bc81c0e2ec4d419b08f7d8 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 7 Jul 2021 11:43:23 -0500 Subject: [PATCH 3/5] IPython console: Prevent instance of MessageCheckBox to be modal there --- spyder/plugins/ipythonconsole/widgets/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/plugins/ipythonconsole/widgets/shell.py b/spyder/plugins/ipythonconsole/widgets/shell.py index 55be657ab5b..d8125d10bc4 100644 --- a/spyder/plugins/ipythonconsole/widgets/shell.py +++ b/spyder/plugins/ipythonconsole/widgets/shell.py @@ -480,7 +480,7 @@ def reset_namespace(self, warning=False, message=False): box.set_check_visible(True) box.setText(warn_str) - answer = box.exec_() + answer = box.show() # Update checkbox based on user interaction self.set_conf( From 1d03eda11598b5d398a3622652b0fc6629466cbf Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 7 Jul 2021 13:26:12 -0500 Subject: [PATCH 4/5] Application: Move check for updates to on_mainwindow_visible --- spyder/plugins/application/container.py | 8 +------- spyder/plugins/application/plugin.py | 8 +++++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spyder/plugins/application/container.py b/spyder/plugins/application/container.py index 51d930863d7..0c4e258c291 100644 --- a/spyder/plugins/application/container.py +++ b/spyder/plugins/application/container.py @@ -24,7 +24,6 @@ from spyder import dependencies from spyder.api.translations import get_translation from spyder.api.widgets.main_container import PluginMainContainer -from spyder.config.base import DEV from spyder.config.utils import is_anaconda from spyder.utils.qthelpers import start_file, DialogManager from spyder.widgets.about import AboutDialog @@ -142,11 +141,6 @@ def setup(self): shortcut_context="_", register_shortcut=True) - # Initialize - if DEV is None and self.get_conf('check_updates_on_startup'): - self.give_updates_feedback = False - self.check_updates(startup=True) - def update_actions(self): pass @@ -255,7 +249,7 @@ def check_updates(self, startup=False): # while loading. # Fixes spyder-ide/spyder#15839 updates_timer = QTimer(self) - updates_timer.setInterval(15000) + updates_timer.setInterval(3000) updates_timer.setSingleShot(True) updates_timer.timeout.connect(self.thread_updates.start) updates_timer.start() diff --git a/spyder/plugins/application/plugin.py b/spyder/plugins/application/plugin.py index 1a9877a0dbd..95a92e743d4 100644 --- a/spyder/plugins/application/plugin.py +++ b/spyder/plugins/application/plugin.py @@ -92,9 +92,10 @@ def on_close(self): def on_mainwindow_visible(self): """Actions after the mainwindow in visible.""" + container = self.get_container() + # Show dialog with missing dependencies if not running_under_pytest(): - container = self.get_container() self.dependencies_thread.run = container.compute_dependencies self.dependencies_thread.finished.connect( container.report_missing_dependencies) @@ -106,6 +107,11 @@ def on_mainwindow_visible(self): dependencies_timer.timeout.connect(self.dependencies_thread.start) dependencies_timer.start() + # Check for updates + if DEV is None and self.get_conf('check_updates_on_startup'): + container.give_updates_feedback = False + container.check_updates(startup=True) + # --- Private methods # ------------------------------------------------------------------------ From ed787442e5bd3ac3cef36511f8fafef07884ffd3 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 7 Jul 2021 22:23:46 -0500 Subject: [PATCH 5/5] Application: Move logic to compute dependencies in a thread to container --- spyder/plugins/application/container.py | 15 ++++++++++++++- spyder/plugins/application/plugin.py | 24 ++++-------------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/spyder/plugins/application/container.py b/spyder/plugins/application/container.py index 0c4e258c291..86e1d2029db 100644 --- a/spyder/plugins/application/container.py +++ b/spyder/plugins/application/container.py @@ -63,7 +63,11 @@ class ApplicationActions: class ApplicationContainer(PluginMainContainer): + def setup(self): + # Compute dependencies in a thread to not block the interface. + self.dependencies_thread = QThread() + # Attributes self.dialog_manager = DialogManager() self.give_updates_feedback = False @@ -274,7 +278,16 @@ def show_windows_env_variables(self): def compute_dependencies(self): """Compute dependencies""" - dependencies.declare_dependencies() + self.dependencies_thread.run = dependencies.declare_dependencies + self.dependencies_thread.finished.connect( + self.report_missing_dependencies) + + # This avoids computing missing deps before the window is fully up + dependencies_timer = QTimer(self) + dependencies_timer.setInterval(10000) + dependencies_timer.setSingleShot(True) + dependencies_timer.timeout.connect(self.dependencies_thread.start) + dependencies_timer.start() @Slot() def report_missing_dependencies(self): diff --git a/spyder/plugins/application/plugin.py b/spyder/plugins/application/plugin.py index 95a92e743d4..662db07f7d7 100644 --- a/spyder/plugins/application/plugin.py +++ b/spyder/plugins/application/plugin.py @@ -14,7 +14,7 @@ import sys # Third party imports -from qtpy.QtCore import Qt, QThread, QTimer, Slot +from qtpy.QtCore import Slot from qtpy.QtWidgets import QMenu # Local imports @@ -43,12 +43,6 @@ class Application(SpyderPluginV2): CONF_FILE = False CONF_WIDGET_CLASS = ApplicationConfigPage - def __init__(self, parent, configuration=None): - super().__init__(parent, configuration) - - # Compute dependencies in a thread to not block the interface. - self.dependencies_thread = QThread() - def get_name(self): return _('Application') @@ -96,25 +90,15 @@ def on_mainwindow_visible(self): # Show dialog with missing dependencies if not running_under_pytest(): - self.dependencies_thread.run = container.compute_dependencies - self.dependencies_thread.finished.connect( - container.report_missing_dependencies) - - # This avoids computing missing deps before the window is fully up - dependencies_timer = QTimer(self) - dependencies_timer.setInterval(10000) - dependencies_timer.setSingleShot(True) - dependencies_timer.timeout.connect(self.dependencies_thread.start) - dependencies_timer.start() + container.compute_dependencies() # Check for updates if DEV is None and self.get_conf('check_updates_on_startup'): container.give_updates_feedback = False container.check_updates(startup=True) - # --- Private methods + # ---- Private methods # ------------------------------------------------------------------------ - def _populate_file_menu(self): mainmenu = self.get_plugin(Plugins.MainMenu) if mainmenu: @@ -178,7 +162,7 @@ def _populate_help_menu_about_section(self, mainmenu): menu_id=ApplicationMenus.Help, section=HelpMenuSections.About) - # --- Public API + # ---- Public API # ------------------------------------------------------------------------ def get_application_context_menu(self, parent=None): """