Repository: spyder-ide/spyder

Issue:
Closing all files in split pane view with at least 2 open then clicking any menu triggers RuntimeError
## Description

**What steps will reproduce the problem?**

1. 
2. 
3. 

**What is the expected output? What do you see instead?**


**Please provide any additional information below**

  File "/home/dhama/anaconda3/lib/python3.6/site-packages/spyder/app/mainwindow.py", line 2013, in update_edit_menu
    widget, textedit_properties = self.get_focus_widget_properties()
  File "/home/dhama/anaconda3/lib/python3.6/site-packages/spyder/app/mainwindow.py", line 2006, in get_focus_widget_properties
    not_readonly = not widget.isReadOnly()
RuntimeError: wrapped C/C++ object of type CodeEditor has been deleted


## Version and main components

* Spyder Version: 3.2.4 
* Python Version: 3.6.3
* Qt Versions: 5.6.2, PyQt5 5.6 on Linux

## Dependencies
```
pyflakes >=0.6.0 :  1.6.0 (OK)
pycodestyle >=2.3:  2.3.1 (OK)
pygments >=2.0   :  2.2.0 (OK)
pandas >=0.13.1  :  0.20.3 (OK)
numpy >=1.7      :  1.13.3 (OK)
sphinx >=0.6.6   :  1.6.3 (OK)
rope >=0.9.4     :  0.10.5 (OK)
jedi >=0.9.0     :  0.10.2 (OK)
psutil >=0.3     :  5.4.0 (OK)
nbconvert >=4.0  :  5.3.1 (OK)
sympy >=0.7.3    :  1.1.1 (OK)
cython >=0.21    :  0.26.1 (OK)
qtconsole >=4.2.0:  4.3.1 (OK)
IPython >=4.0    :  6.1.0 (OK)
pylint >=0.25    :  1.7.4 (OK)

```



Discussion:
Please describe step by step how you got this error so we can reproduce it in our side. If you fail to do that, we'll close this issue in a week from now.
@ccordoba12 As an OT aside (as I'm not sure how else to mention it), since this seems to be a stock response (great idea ofc), I might suggest a tweak to the following to maximize the chances of a positive response while minimizing the chance someone misinterprets it and gets rubbed the wrong way. (I hope _this_ suggestion doesn't get taken the wrong way, heh; I used to work as a copyeditor for an engineering magazine so it has been bugging me). Thus:

Please describe step by step how you got this error so we can reproduce it in our side. If _we don't hear from you,_ we'll _have to_ close this issue in a week from now. _Thanks._
Closed due to lack of response.
Okay @ccordoba12 , I managed to reproduce this by accident in Spyder 3.2.6 (and Python 3.6.4) on Windows, and isolated it to the following situation:

1. Necessary starting condition: Have at least two files open, and in a split pane view. If either condition is not met, it will not occur. Being in a project or not does not matter.
2. Selecting "Close All". or manually closing all open files, so that a new untitled file is shown
3. Clicking on any menu in the menu bar

Error message will pop up with the above/below traceback after a minimal delay of around 500 ms. Doing step 1 and 2, but clicking elsewhere in the UI will not trigger it, even if one then clicks on any menu after clicking elsewhere, and neither will immediately using Alt + Letter to bring up the menus. However, if the keyboard is used in step 2 but not step 3 (i.e. closing all windows with Ctrl-Shift-W) _will_ still trigger it just fine so long as the menus are actually clicked on. Also, to clarify, there are apparently no ill effects other than the error message.

Therefore, it seems it might have to do with something that only happens on a mouseClick event vs. the menu access itself? Unfortunately, as this issue seems to occur within the Qt C++ API I'm not exactly familiar with how it should be handled; my C++ experience is limited to a few simple programs in my spare time and nothing GUI related, so at the very least if I were to try to fix this myself I'd need some clear...pointers (heh).

The updated traceback (with 3.2.6 line numbers):

  File "C:\Anaconda3\lib\site-packages\spyder\app\mainwindow.py", line 2027, in update_edit_menu
    widget, textedit_properties = self.get_focus_widget_properties()
  File "C:\Anaconda3\lib\site-packages\spyder\app\mainwindow.py", line 2020, in get_focus_widget_properties
    not_readonly = not widget.isReadOnly()
RuntimeError: wrapped C/C++ object of type CodeEditor has been deleted
Thanks @CAM-Gerlach for investigating and for finding a way to reproduce it. I can handle this one if you don't.
Thanks @jnsebgosselin . My naive suspicion is that something, possibly triggered by a mouseClick event to any menu object, triggers that code path that references some old CodeEditor object that was closed and gc'ed already. However, that really is just an uninformed guess; I'd be curious to see what is actually going on.
@ccordoba12 Is closing the split editors but one when all files are closed the expected behavior? That is what is causing this bug to happen. The references to the closed split editor are not cleaned properly in python when it is closed this way. So when the mainwindow tries to get hold of the closed split editor to refresh its `Edit` menu, it fails because it has been deleted on the qt side.

I think split editors should not be closed when closing all files, either one-by-one or with the `close all` action.

![close_all_files](https://user-images.githubusercontent.com/10170372/36382828-521d5d2c-1558-11e8-9d1b-9240a5489128.gif)

> I think split editors should not be closed when closing all files, either one-by-one or with the close all action.

Agreed. Thanks for noticing the problem and helping us to fix it.
Alright. I'm going to fix this behavior then, which will take care of the bug.

For the records, this is also why split editors were closed when switching or opening a spyder project. So that is going to fix that also.
Nice! Two birds with one stone.
@ccordoba12 Just letting you know I'm working on this one, but it has proven to be more difficult than I had anticipated... so sorry for the delay with this one.
No worries, it's marked for 3.2.9 so there's no rush.
@jnsebgosselin, do you have time to address this one? It'd be handy to have it for 3.3.2 (which I hope to release next weekend).

Resolving patch:
diff --git a/spyder/plugins/editor.py b/spyder/plugins/editor.py
--- a/spyder/plugins/editor.py
+++ b/spyder/plugins/editor.py
@@ -413,10 +413,6 @@ def __init__(self, parent, ignore_last_opened_files=False):
         self.outlineexplorer = None
         self.help = None
 
-        self.editorstacks = None
-        self.editorwindows = None
-        self.editorwindows_to_be_created = None
-
         self.file_dependent_actions = []
         self.pythonfile_dependent_actions = []
         self.dock_toolbar_actions = None
@@ -446,7 +442,8 @@ def __init__(self, parent, ignore_last_opened_files=False):
         self.cursor_pos_history = []
         self.cursor_pos_index = None
         self.__ignore_cursor_position = True
-        
+
+        self.__first_open_files_setup = True
         self.editorstacks = []
         self.last_focus_editorstack = {}
         self.editorwindows = []
@@ -2690,30 +2687,31 @@ def set_open_filenames(self):
             if not self.projects.get_active_project():
                 filenames = self.get_open_filenames()
                 self.set_option('filenames', filenames)
- 
+
     def setup_open_files(self):
         """Open the list of saved files per project"""
         self.set_create_new_file_if_empty(False)
         active_project_path = None
         if self.projects is not None:
-             active_project_path = self.projects.get_active_project_path()
+            active_project_path = self.projects.get_active_project_path()
 
         if active_project_path:
             filenames = self.projects.get_project_filenames()
         else:
             filenames = self.get_option('filenames', default=[])
         self.close_all_files()
- 
+
         if filenames and any([osp.isfile(f) for f in filenames]):
             self.load(filenames)
-            layout = self.get_option('layout_settings', None)
-            if layout is not None:
-                self.editorsplitter.set_layout_settings(layout)
-            win_layout = self.get_option('windows_layout_settings', None)
-            if win_layout:
+            if self.__first_open_files_setup:
+                self.__first_open_files_setup = False
+                layout = self.get_option('layout_settings', None)
+                if layout is not None:
+                    self.editorsplitter.set_layout_settings(layout)
+                win_layout = self.get_option('windows_layout_settings', [])
                 for layout_settings in win_layout:
                     self.editorwindows_to_be_created.append(layout_settings)
-            self.set_last_focus_editorstack(self, self.editorstacks[0])
+                self.set_last_focus_editorstack(self, self.editorstacks[0])
         else:
             self.__load_temp_file()
         self.set_create_new_file_if_empty(True)
diff --git a/spyder/widgets/editor.py b/spyder/widgets/editor.py
--- a/spyder/widgets/editor.py
+++ b/spyder/widgets/editor.py
@@ -1430,11 +1430,6 @@ def close_file(self, index=None, force=False):
             # (see Issue 1094, Issue 1098)
             self.sig_close_file.emit(str(id(self)), filename)
 
-            if not self.data and self.is_closable:
-                # editortabwidget is empty: removing it
-                # (if it's not the first editortabwidget)
-                self.close()
-
             self.opened_files_list_changed.emit()
             self.update_code_analysis_actions.emit()
             self._refresh_outlineexplorer()


Test patch: