Repository: spyder-ide/spyder

Issue:
No cog menu in variable explorer widget toolbar when more than one Ipython console is opened
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [x] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [x] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [x] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [x] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [x] Tried basic troubleshooting (if a bug/error)
    * [x] Restarted Spyder
    * [x] Reset preferences with ``spyder --reset``
    * [x] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [x] Tried the other applicable steps from the Troubleshooting Guide
* [x] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below


## Problem Description

The cog menu appears only for the variable explorer associated with the last created IPython console.

### What steps reproduce the problem?

1. Create a new IPython console so that there is at least 2 consoles opened
2. The variable explorer associated with the last IPython console has a cog menu
3. The variable explorer associated with the previous IPython consoles does not have a cog menu

![cog_menu_not_duplicated](https://user-images.githubusercontent.com/10170372/44233382-baabd580-a171-11e8-8b38-edb3efe17b47.gif)

### What is the expected output? What do you see instead?

The cog menu should remain visible on the variable explorer toolbar for all the IPython consoles. 

## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version: Spyder 4.0.0.dev0
* Python version: 3.6.5 64 bits
* Qt version: 5.9.3
* PyQt version: 5.9.2
* Operating System name/version: Windows 10

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```
pyflakes >=0.6.0  :  1.6.0 (OK)
pycodestyle >=2.3 :  2.3.1 (OK)
pygments >=2.0    :  2.2.0 (OK)
sphinx >=0.6.6    :  1.7.1 (OK)
rope >=0.9.4      :  0.10.7 (OK)
jedi >=0.11.0     :  0.11.1 (OK)
nbconvert >=4.0   :  5.3.1 (OK)
pandas >=0.13.1   :  0.22.0 (OK)
numpy >=1.7       :  1.13.0 (OK)
sympy >=0.7.3     :  1.1.1 (OK)
cython >=0.21     :  0.27.3 (OK)
qtconsole >=4.2.0 :  4.4.0.dev (OK)
IPython >=4.0     :  6.2.1 (OK)
matplotlib >=2.0.0:  2.2.0 (OK)
pylint >=0.25     :  1.8.3 (OK)
```



Discussion:

Resolving patch:
diff --git a/spyder/plugins/variableexplorer.py b/spyder/plugins/variableexplorer.py
--- a/spyder/plugins/variableexplorer.py
+++ b/spyder/plugins/variableexplorer.py
@@ -140,6 +140,10 @@ def free_memory(self):
     # ----- Stack accesors ----------------------------------------------------
     def set_current_widget(self, nsb):
         self.stack.setCurrentWidget(nsb)
+        # We update the actions of the options button (cog menu) and we move
+        # it to the layout of the current widget.
+        self.refresh_actions()
+        nsb.setup_options_button()
 
     def current_widget(self):
         return self.stack.currentWidget()
@@ -164,9 +168,7 @@ def add_shellwidget(self, shellwidget):
         shellwidget_id = id(shellwidget)
         if shellwidget_id not in self.shellwidgets:
             self.options_button.setVisible(True)
-            nsb = NamespaceBrowser(self,
-                                   options_button=self.options_button,
-                                   plugin_actions=[self.undock_action])
+            nsb = NamespaceBrowser(self, options_button=self.options_button)
             nsb.set_shellwidget(shellwidget)
             nsb.setup(**self.get_settings())
             nsb.sig_option_changed.connect(self.change_option)
@@ -225,8 +227,8 @@ def refresh_plugin(self):
 
     def get_plugin_actions(self):
         """Return a list of actions related to plugin"""
-        return []
-    
+        return self.current_widget().actions if self.current_widget() else []
+
     def register_plugin(self):
         """Register plugin in Spyder's main window"""
         self.main.add_dockwidget(self)
diff --git a/spyder/widgets/variableexplorer/namespacebrowser.py b/spyder/widgets/variableexplorer/namespacebrowser.py
--- a/spyder/widgets/variableexplorer/namespacebrowser.py
+++ b/spyder/widgets/variableexplorer/namespacebrowser.py
@@ -33,7 +33,8 @@
 from spyder.utils.misc import getcwd_or_home
 from spyder.utils.programs import is_module_installed
 from spyder.utils.qthelpers import (add_actions, create_action,
-                                    create_toolbutton, create_plugin_layout)
+                                    create_toolbutton, create_plugin_layout,
+                                    MENU_SEPARATOR)
 from spyder.widgets.variableexplorer.collectionseditor import (
     RemoteCollectionsEditorTableView)
 from spyder.widgets.variableexplorer.importwizard import ImportWizard
@@ -48,8 +49,7 @@ class NamespaceBrowser(QWidget):
     sig_collapse = Signal()
     sig_free_memory = Signal()
 
-    def __init__(self, parent, options_button=None, menu=None,
-                 plugin_actions=None):
+    def __init__(self, parent, options_button=None, plugin_actions=[]):
         QWidget.__init__(self, parent)
         
         self.shellwidget = None
@@ -74,7 +74,6 @@ def __init__(self, parent, options_button=None, menu=None,
         self.exclude_capitalized_action = None
         self.exclude_unsupported_action = None
         self.options_button = options_button
-        self.menu = menu
         self.actions = None
         self.plugin_actions = plugin_actions
 
@@ -123,37 +122,21 @@ def setup(self, check_all=None, exclude_private=None,
         self.editor.sig_files_dropped.connect(self.import_data)
         self.editor.sig_free_memory.connect(self.sig_free_memory.emit)
 
-        # Setup layout
-        blayout = QHBoxLayout()
-        toolbar = self.setup_toolbar(exclude_private, exclude_uppercase,
-                                     exclude_capitalized, exclude_unsupported)
+        self.setup_option_actions(exclude_private, exclude_uppercase,
+                                  exclude_capitalized, exclude_unsupported)
+
+        # Setup toolbar layout.
+
+        self.tools_layout = QHBoxLayout()
+        toolbar = self.setup_toolbar()
         for widget in toolbar:
-            blayout.addWidget(widget)
+            self.tools_layout.addWidget(widget)
+        self.tools_layout.addStretch()
+        self.setup_options_button()
 
-        # Options menu
-        editor = self.editor
-        actions = [self.exclude_private_action, self.exclude_uppercase_action,
-                   self.exclude_capitalized_action,
-                   self.exclude_unsupported_action, None]
-        if is_module_installed('numpy'):
-            actions.append(editor.minmax_action)
-        if self.plugin_actions:
-            actions = actions + self.plugin_actions
-        self.actions = actions
-        if not self.options_button:
-            self.options_button = create_toolbutton(
-                                        self, text=_('Options'),
-                                        icon=ima.icon('tooloptions'))
-            self.options_button.setPopupMode(QToolButton.InstantPopup)
-        if not self.menu:
-            self.menu = QMenu(self)
-        add_actions(self.menu, self.actions)
-        self.options_button.setMenu(self.menu)
-
-        blayout.addStretch()
-        blayout.addWidget(self.options_button)
-
-        layout = create_plugin_layout(blayout, self.editor)
+        # Setup layout.
+
+        layout = create_plugin_layout(self.tools_layout, self.editor)
         self.setLayout(layout)
 
         self.sig_option_changed.connect(self.option_changed)
@@ -167,12 +150,8 @@ def get_actions(self):
         """Get actions of the widget."""
         return self.actions
 
-    def setup_toolbar(self, exclude_private, exclude_uppercase,
-                      exclude_capitalized, exclude_unsupported):
+    def setup_toolbar(self):
         """Setup toolbar"""
-        self.setup_in_progress = True
-        toolbar = []
-
         load_button = create_toolbutton(self, text=_('Import data'),
                                         icon=ima.icon('fileimport'),
                                         triggered=lambda: self.import_data())
@@ -188,8 +167,13 @@ def setup_toolbar(self, exclude_private, exclude_uppercase,
                 self, text=_("Remove all variables"),
                 icon=ima.icon('editdelete'), triggered=self.reset_namespace)
 
-        toolbar += [load_button, self.save_button, save_as_button,
-                    reset_namespace_button]
+        return [load_button, self.save_button, save_as_button,
+                reset_namespace_button]
+
+    def setup_option_actions(self, exclude_private, exclude_uppercase,
+                             exclude_capitalized, exclude_unsupported):
+        """Setup the actions to show in the cog menu."""
+        self.setup_in_progress = True
 
         self.exclude_private_action = create_action(self,
                 _("Exclude private references"),
@@ -221,10 +205,31 @@ def setup_toolbar(self, exclude_private, exclude_uppercase,
                 toggled=lambda state:
                 self.sig_option_changed.emit('exclude_unsupported', state))
         self.exclude_unsupported_action.setChecked(exclude_unsupported)
-        
+
+        self.actions = [
+            self.exclude_private_action, self.exclude_uppercase_action,
+            self.exclude_capitalized_action, self.exclude_unsupported_action]
+        if is_module_installed('numpy'):
+            self.actions.extend([MENU_SEPARATOR, self.editor.minmax_action])
+
         self.setup_in_progress = False
-        
-        return toolbar
+
+    def setup_options_button(self):
+        """Add the cog menu button to the toolbar."""
+        if not self.options_button:
+            self.options_button = create_toolbutton(
+                self, text=_('Options'), icon=ima.icon('tooloptions'))
+
+            actions = self.actions + [MENU_SEPARATOR] + self.plugin_actions
+            self.options_menu = QMenu(self)
+            add_actions(self.options_menu, actions)
+            self.options_button.setMenu(self.options_menu)
+
+        if self.tools_layout.itemAt(self.tools_layout.count() - 1) is None:
+            self.tools_layout.insertWidget(
+                self.tools_layout.count() - 1, self.options_button)
+        else:
+            self.tools_layout.addWidget(self.options_button)
 
     def option_changed(self, option, value):
         """Option has changed"""


Test patch: