From 117bed3e8c28019f912503a3cb8a1a9d7d00a8b8 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 2 Jul 2017 19:35:03 -0500 Subject: [PATCH 01/15] Run config: Use old dedicated options only for external terminals --- spyder/plugins/runconfig.py | 45 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/spyder/plugins/runconfig.py b/spyder/plugins/runconfig.py index 9b208e05437..69022ef7514 100644 --- a/spyder/plugins/runconfig.py +++ b/spyder/plugins/runconfig.py @@ -26,9 +26,9 @@ from spyder.utils import icon_manager as ima -CURRENT_INTERPRETER = _("Execute in current Python or IPython console") -DEDICATED_INTERPRETER = _("Execute in a new dedicated Python console") -SYSTERM_INTERPRETER = _("Execute in an external System terminal") +CURRENT_INTERPRETER = _("Execute in current console") +DEDICATED_INTERPRETER = _("Execute in a dedicated console") +SYSTERM_INTERPRETER = _("Execute in an external system terminal") CURRENT_INTERPRETER_OPTION = 'default/interpreter/current' DEDICATED_INTERPRETER_OPTION = 'default/interpreter/dedicated' @@ -41,11 +41,12 @@ ALWAYS_OPEN_FIRST_RUN = _("Always show %s on a first file run") ALWAYS_OPEN_FIRST_RUN_OPTION = 'open_on_firstrun' -CLEAR_ALL_VARIABLES = _("Clear all variables before execution (IPython " - "consoles)") +CLEAR_ALL_VARIABLES = _("Clear all variables before execution") + class RunConfiguration(object): """Run configuration""" + def __init__(self, fname=None): self.args = None self.args_enabled = None @@ -59,7 +60,9 @@ def __init__(self, fname=None): self.python_args = None self.python_args_enabled = None self.clear_namespace = None + self.set(CONF.get('run', 'defaultconfiguration', default={})) + if fname is not None and\ CONF.get('run', WDIR_USE_SCRIPT_DIR_OPTION, True): self.wdir = osp.dirname(fname) @@ -124,7 +127,7 @@ def get_python_arguments(self): else: return '' - + def _get_run_configurations(): history_count = CONF.get('run', 'history', 20) try: @@ -135,10 +138,12 @@ def _get_run_configurations(): CONF.set('run', 'configurations', []) return [] + def _set_run_configurations(configurations): history_count = CONF.get('run', 'history', 20) CONF.set('run', 'configurations', configurations[:history_count]) - + + def get_run_configuration(fname): """Return script *fname* run configuration""" configurations = _get_run_configurations() @@ -189,9 +194,7 @@ def __init__(self, parent=None): self.post_mortem_cb = QCheckBox(_("Enter debugging mode when " "errors appear during execution")) common_layout.addWidget(self.post_mortem_cb) - - # --- Interpreter --- interpreter_group = QGroupBox(_("Console")) interpreter_layout = QVBoxLayout() @@ -202,28 +205,30 @@ def __init__(self, parent=None): interpreter_layout.addWidget(self.dedicated_radio) self.systerm_radio = QRadioButton(SYSTERM_INTERPRETER) interpreter_layout.addWidget(self.systerm_radio) - - # --- Dedicated interpreter --- - new_group = QGroupBox(_("Dedicated Python console")) - self.current_radio.toggled.connect(new_group.setDisabled) - new_layout = QGridLayout() - new_group.setLayout(new_layout) + + # --- System terminal --- + external_group = QGroupBox(_("External system terminal")) + external_group.setDisabled(True) + self.systerm_radio.toggled.connect(external_group.setEnabled) + + external_layout = QGridLayout() + external_group.setLayout(external_layout) self.interact_cb = QCheckBox(_("Interact with the Python " "console after execution")) - new_layout.addWidget(self.interact_cb, 1, 0, 1, -1) + external_layout.addWidget(self.interact_cb, 1, 0, 1, -1) self.show_kill_warning_cb = QCheckBox(_("Show warning when killing" " running process")) + external_layout.addWidget(self.show_kill_warning_cb, 2, 0, 1, -1) - new_layout.addWidget(self.show_kill_warning_cb, 2, 0, 1, -1) self.pclo_cb = QCheckBox(_("Command line options:")) - new_layout.addWidget(self.pclo_cb, 3, 0) + external_layout.addWidget(self.pclo_cb, 3, 0) self.pclo_edit = QLineEdit() self.pclo_cb.toggled.connect(self.pclo_edit.setEnabled) self.pclo_edit.setEnabled(False) self.pclo_edit.setToolTip(_("-u is added to the " "other options you set here")) - new_layout.addWidget(self.pclo_edit, 3, 1) + external_layout.addWidget(self.pclo_edit, 3, 1) # Checkbox to preserve the old behavior, i.e. always open the dialog @@ -238,7 +243,7 @@ def __init__(self, parent=None): layout = QVBoxLayout() layout.addWidget(interpreter_group) layout.addWidget(common_group) - layout.addWidget(new_group) + layout.addWidget(external_group) layout.addWidget(hline) layout.addWidget(self.firstrun_cb) self.setLayout(layout) From fb010c327828ad6685290747a6e838cadb5d5dae Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 12:34:51 -0500 Subject: [PATCH 02/15] Preferences: Use old dedicated options for external terminals --- spyder/plugins/runconfig.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/spyder/plugins/runconfig.py b/spyder/plugins/runconfig.py index 69022ef7514..0ba0689225f 100644 --- a/spyder/plugins/runconfig.py +++ b/spyder/plugins/runconfig.py @@ -43,6 +43,9 @@ CLEAR_ALL_VARIABLES = _("Clear all variables before execution") +INTERACT = _("Interact with the Python console after execution") +SHOW_WARNING = _("Show warning when killing running processes") + class RunConfiguration(object): """Run configuration""" @@ -213,12 +216,10 @@ def __init__(self, parent=None): external_layout = QGridLayout() external_group.setLayout(external_layout) - self.interact_cb = QCheckBox(_("Interact with the Python " - "console after execution")) + self.interact_cb = QCheckBox(INTERACT) external_layout.addWidget(self.interact_cb, 1, 0, 1, -1) - self.show_kill_warning_cb = QCheckBox(_("Show warning when killing" - " running process")) + self.show_kill_warning_cb = QCheckBox(SHOW_WARNING) external_layout.addWidget(self.show_kill_warning_cb, 2, 0, 1, -1) self.pclo_cb = QCheckBox(_("Command line options:")) @@ -519,18 +520,15 @@ def setup_page(self): general_layout.addWidget(post_mortem) general_group.setLayout(general_layout) - dedicated_group = QGroupBox(_("Dedicated Python console")) - interact_after = self.create_checkbox( - _("Interact with the Python console after execution"), - 'interact', False) - show_warning = self.create_checkbox( - _("Show warning when killing running processes"), - 'show_kill_warning', True) + external_group = QGroupBox(_("External system terminal")) + interact_after = self.create_checkbox(INTERACT, 'interact', False) + show_warning = self.create_checkbox(SHOW_WARNING, 'show_kill_warning', + True) - dedicated_layout = QVBoxLayout() - dedicated_layout.addWidget(interact_after) - dedicated_layout.addWidget(show_warning) - dedicated_group.setLayout(dedicated_layout) + external_layout = QVBoxLayout() + external_layout.addWidget(interact_after) + external_layout.addWidget(show_warning) + external_group.setLayout(external_layout) firstrun_cb = self.create_checkbox( ALWAYS_OPEN_FIRST_RUN % _("Run Settings dialog"), @@ -541,7 +539,7 @@ def setup_page(self): vlayout.addSpacing(10) vlayout.addWidget(interpreter_group) vlayout.addWidget(general_group) - vlayout.addWidget(dedicated_group) + vlayout.addWidget(external_group) vlayout.addWidget(firstrun_cb) vlayout.addStretch(1) self.setLayout(vlayout) From 976e3f3d49d1a09d414a3c1227f12240f9979abe Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 2 Jul 2017 21:22:19 -0500 Subject: [PATCH 03/15] IPython console: Pass given name to rename_client_tab --- spyder/plugins/ipythonconsole.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index c8701b69bd2..bad13f6f8e4 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -1339,9 +1339,12 @@ def move_tab(self, index_from, index_to): self.clients.insert(index_to, client) self.update_plugin_title.emit() - def rename_client_tab(self, client): + def rename_client_tab(self, client, given_name): """Rename client's tab""" index = self.get_client_index_from_id(id(client)) + + if given_name is not None: + client.given_name = given_name self.tabwidget.setTabText(index, client.get_name()) def rename_tabs_after_change(self, given_name): @@ -1349,14 +1352,14 @@ def rename_tabs_after_change(self, given_name): # Rename current client tab to add str_id if client.allow_rename: - client.given_name = given_name - self.rename_client_tab(client) + self.rename_client_tab(client, given_name) + else: + self.rename_client_tab(client, None) # Rename related clients if client.allow_rename: for cl in self.get_related_clients(client): - cl.given_name = given_name - self.rename_client_tab(cl) + self.rename_client_tab(cl, given_name) def tab_name_editor(self): """Trigger the tab name editor.""" From a7a469e53c739adaf8d3751c966e1c06f448b9a9 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 2 Jul 2017 21:46:57 -0500 Subject: [PATCH 04/15] IPython console: Simplify create_client_from_path --- spyder/plugins/ipythonconsole.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index bad13f6f8e4..b932093342b 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -891,9 +891,7 @@ def write_to_stdin(self, line): @Slot() @Slot(bool) - @Slot(str) - @Slot(bool, str) - def create_new_client(self, give_focus=True, path=''): + def create_new_client(self, give_focus=True): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=to_text_string(self.master_clients), @@ -935,7 +933,7 @@ def create_new_client(self, give_focus=True, path=''): "conda install ipykernel")) return - self.connect_client_to_kernel(client, path) + self.connect_client_to_kernel(client) if client.shellwidget.kernel_manager is None: return self.register_client(client) @@ -951,7 +949,7 @@ def create_client_for_kernel(self): self._create_client_for_kernel(connection_file, hostname, sshkey, password) - def connect_client_to_kernel(self, client, path): + def connect_client_to_kernel(self, client): """Connect a client to its kernel""" connection_file = client.connection_file stderr_file = client.stderr_file @@ -971,9 +969,6 @@ def connect_client_to_kernel(self, client, path): shellwidget.kernel_manager = km shellwidget.kernel_client = kc - if path: - shellwidget.set_cwd(path) - def set_editor(self): """Set the editor used by the %edit magic""" python = sys.executable @@ -1246,8 +1241,10 @@ def set_spyder_breakpoints(self): @Slot(str) def create_client_from_path(self, path): - """Create a client with its cwd pointing to path""" - self.create_new_client(path=path) + """Create a client with its cwd pointing to path.""" + self.create_new_client() + sw = self.get_current_shellwidget() + sw.set_cwd(path) #------ Public API (for kernels) ------------------------------------------ def ssh_tunnel(self, *args, **kwargs): From 0b7eb27bfd982ea41e9870bb6d03ee6aa70cfbbf Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 12:18:28 -0500 Subject: [PATCH 05/15] IPython console: Initial implementation of dedicated consoles --- spyder/plugins/editor.py | 6 ++-- spyder/plugins/ipythonconsole.py | 47 ++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/spyder/plugins/editor.py b/spyder/plugins/editor.py index a856f768899..c15b793f40e 100644 --- a/spyder/plugins/editor.py +++ b/spyder/plugins/editor.py @@ -369,7 +369,7 @@ class Editor(SpyderPluginWidget): DISABLE_ACTIONS_WHEN_HIDDEN = False # SpyderPluginWidget class attribute # Signals - run_in_current_ipyclient = Signal(str, str, str, bool, bool, bool) + run_in_current_ipyclient = Signal(str, str, str, bool, bool, bool, bool) exec_in_extconsole = Signal(str, bool) redirect_stdio = Signal(bool) open_dir = Signal(str) @@ -2408,10 +2408,10 @@ def re_run_file(self): (fname, wdir, args, interact, debug, python, python_args, current, systerm, post_mortem, clear_namespace) = self.__last_ec_exec - if current: + if not systerm: self.run_in_current_ipyclient.emit(fname, wdir, args, debug, post_mortem, - clear_namespace) + current, clear_namespace) else: self.main.open_external_console(fname, wdir, args, interact, debug, python, python_args, diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index b932093342b..f46887db139 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -799,8 +799,7 @@ def register_plugin(self): self.editor.load(fname, lineno, word, processevents=processevents)) self.editor.breakpoints_saved.connect(self.set_spyder_breakpoints) - self.editor.run_in_current_ipyclient.connect( - self.run_script_in_current_client) + self.editor.run_in_current_ipyclient.connect(self.run_script) self.main.workingdirectory.set_current_console_wd.connect( self.set_current_client_working_directory) self.explorer.open_interpreter.connect(self.create_client_from_path) @@ -830,11 +829,20 @@ def get_current_shellwidget(self): if client is not None: return client.shellwidget - def run_script_in_current_client(self, filename, wdir, args, debug, - post_mortem, clear_variables): - """Run script in current client, if any""" + def run_script(self, filename, wdir, args, debug, post_mortem, + current_client, clear_variables): + """Run script in current or dedicated client""" norm = lambda text: remove_backslashes(to_text_string(text)) - client = self.get_current_client() + + # Select client to execute code on it + if current_client: + client = self.get_current_client() + else: + client = self.get_client_for_file(filename) + if client is None: + self.create_client_for_file(filename) + client = self.get_current_client() + if client is not None: # Internal kernels, use runfile if client.get_kernel() is not None: @@ -854,7 +862,7 @@ def run_script_in_current_client(self, filename, wdir, args, debug, line += "\"%s\"" % to_text_string(filename) if args: line += " %s" % norm(args) - self.execute_code(line, clear_variables) + self.execute_code(line, current_client, clear_variables) self.visibility_changed(True) self.raise_() else: @@ -871,14 +879,19 @@ def set_current_client_working_directory(self, directory): directory = encoding.to_unicode_from_fs(directory) shellwidget.set_cwd(directory) - def execute_code(self, lines, clear_variables=False): + def execute_code(self, lines, current_client, clear_variables=False): """Execute code instructions.""" sw = self.get_current_shellwidget() if sw is not None: if sw._reading: pass else: - if clear_variables: + if not current_client: + # Clear console and reset namespace for + # dedicated clients + sw.silent_execute('%clear') + sw.reset_namespace(force=True) + elif current_client and clear_variables: sw.reset_namespace(force=True) sw.execute(to_text_string(to_text_string(lines))) self.activateWindow() @@ -1246,6 +1259,22 @@ def create_client_from_path(self, path): sw = self.get_current_shellwidget() sw.set_cwd(path) + def create_client_for_file(self, filename): + """Create a client to execute code related to a file.""" + self.create_new_client() + client = self.get_current_client() + client.allow_rename = False + self.rename_client_tab(client, filename) + + def get_client_for_file(self, filename): + """Get client associated with a given file.""" + client = None + for cl in self.get_clients(): + if cl.given_name == filename: + client = cl + break + return client + #------ Public API (for kernels) ------------------------------------------ def ssh_tunnel(self, *args, **kwargs): if os.name == 'nt': From 74f06c09056b7afb459406639a18f24957e4c4e0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 12:48:25 -0500 Subject: [PATCH 06/15] Run config and preferences: Remove show_kill_warning option It was only used in the Python console. --- spyder/plugins/runconfig.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/spyder/plugins/runconfig.py b/spyder/plugins/runconfig.py index 0ba0689225f..a108cf22e86 100644 --- a/spyder/plugins/runconfig.py +++ b/spyder/plugins/runconfig.py @@ -42,9 +42,7 @@ ALWAYS_OPEN_FIRST_RUN_OPTION = 'open_on_firstrun' CLEAR_ALL_VARIABLES = _("Clear all variables before execution") - INTERACT = _("Interact with the Python console after execution") -SHOW_WARNING = _("Show warning when killing running processes") class RunConfiguration(object): @@ -58,7 +56,6 @@ def __init__(self, fname=None): self.current = None self.systerm = None self.interact = None - self.show_kill_warning =None self.post_mortem = None self.python_args = None self.python_args_enabled = None @@ -87,8 +84,6 @@ def set(self, options): CONF.get('run', SYSTERM_INTERPRETER_OPTION, False)) self.interact = options.get('interact', CONF.get('run', 'interact', False)) - self.show_kill_warning = options.get('show_kill_warning', - CONF.get('run', 'show_kill_warning', False)) self.post_mortem = options.get('post_mortem', CONF.get('run', 'post_mortem', False)) self.python_args = options.get('python_args', '') @@ -105,7 +100,6 @@ def get(self): 'current': self.current, 'systerm': self.systerm, 'interact': self.interact, - 'show_kill_warning': self.show_kill_warning, 'post_mortem': self.post_mortem, 'python_args/enabled': self.python_args_enabled, 'python_args': self.python_args, @@ -218,9 +212,6 @@ def __init__(self, parent=None): external_group.setLayout(external_layout) self.interact_cb = QCheckBox(INTERACT) external_layout.addWidget(self.interact_cb, 1, 0, 1, -1) - - self.show_kill_warning_cb = QCheckBox(SHOW_WARNING) - external_layout.addWidget(self.show_kill_warning_cb, 2, 0, 1, -1) self.pclo_cb = QCheckBox(_("Command line options:")) external_layout.addWidget(self.pclo_cb, 3, 0) @@ -272,7 +263,6 @@ def set(self, options): else: self.dedicated_radio.setChecked(True) self.interact_cb.setChecked(self.runconf.interact) - self.show_kill_warning_cb.setChecked(self.runconf.show_kill_warning) self.post_mortem_cb.setChecked(self.runconf.post_mortem) self.pclo_cb.setChecked(self.runconf.python_args_enabled) self.pclo_edit.setText(self.runconf.python_args) @@ -286,7 +276,6 @@ def get(self): self.runconf.current = self.current_radio.isChecked() self.runconf.systerm = self.systerm_radio.isChecked() self.runconf.interact = self.interact_cb.isChecked() - self.runconf.show_kill_warning = self.show_kill_warning_cb.isChecked() self.runconf.post_mortem = self.post_mortem_cb.isChecked() self.runconf.python_args_enabled = self.pclo_cb.isChecked() self.runconf.python_args = to_text_string(self.pclo_edit.text()) @@ -522,12 +511,9 @@ def setup_page(self): external_group = QGroupBox(_("External system terminal")) interact_after = self.create_checkbox(INTERACT, 'interact', False) - show_warning = self.create_checkbox(SHOW_WARNING, 'show_kill_warning', - True) external_layout = QVBoxLayout() external_layout.addWidget(interact_after) - external_layout.addWidget(show_warning) external_group.setLayout(external_layout) firstrun_cb = self.create_checkbox( From bb16b872620b4367a83f4dc4441588431e5814b7 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 18:13:14 -0500 Subject: [PATCH 07/15] IPython console: Catch OSError instead of FileNotFoundError because the latter doesn't exist in PY2 --- spyder/widgets/ipythonconsole/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py index 6ed5c7dd205..d767f36559c 100644 --- a/spyder/widgets/ipythonconsole/client.py +++ b/spyder/widgets/ipythonconsole/client.py @@ -432,7 +432,7 @@ def kernel_restarted_message(self, msg): stderr = self._read_stderr() except: stderr = None - except FileNotFoundError: + except OSError: stderr = None if stderr: From 62e189a5f5ab88bfe1d9e3ebef5dee26cb4e0b3e Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 18:14:27 -0500 Subject: [PATCH 08/15] IPython console: Make current_client arg of execute_code optional This fixes calls to execute_code in other places --- spyder/plugins/ipythonconsole.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index f46887db139..c9d4c4e76f6 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -879,7 +879,7 @@ def set_current_client_working_directory(self, directory): directory = encoding.to_unicode_from_fs(directory) shellwidget.set_cwd(directory) - def execute_code(self, lines, current_client, clear_variables=False): + def execute_code(self, lines, current_client=True, clear_variables=False): """Execute code instructions.""" sw = self.get_current_shellwidget() if sw is not None: From 205172de47240255b06c28a57bec614ef62eba6c Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 18:14:51 -0500 Subject: [PATCH 09/15] Main Window: Fix a docstring --- spyder/app/mainwindow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spyder/app/mainwindow.py b/spyder/app/mainwindow.py index ac31abbabca..7a080d38ea8 100644 --- a/spyder/app/mainwindow.py +++ b/spyder/app/mainwindow.py @@ -2433,8 +2433,8 @@ def open_external_console(self, fname, wdir, args, interact, debug, python, def execute_in_external_console(self, lines, focus_to_editor): """ - Execute lines in external or IPython console and eventually set focus - to the editor + Execute lines in IPython console and eventually set focus + to the Editor. """ console = self.ipyconsole console.visibility_changed(True) From 2d1cba501f87348d97b36d19dce6ce427f8faafe Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 22:39:18 -0500 Subject: [PATCH 10/15] Tabs: Add a split_char arg to better select text in IPython console tabs when renaming For those tabs '/' is our split char, and we have to let users know that they can only alter the text before that char. --- spyder/widgets/tabs.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/spyder/widgets/tabs.py b/spyder/widgets/tabs.py index ac08c2e53ec..8355b4f4a7b 100644 --- a/spyder/widgets/tabs.py +++ b/spyder/widgets/tabs.py @@ -35,13 +35,15 @@ class EditTabNamePopup(QLineEdit): """Popup on top of the tab to edit its name.""" - def __init__(self, parent): + def __init__(self, parent, split_char): """Popup on top of the tab to edit its name.""" # Variables # Parent (main) self.main = parent if parent is not None else self.parent() - # Track with tab is being edited + self.split_char = split_char + + # Track which tab is being edited self.tab_index = None # Widget setup @@ -104,7 +106,12 @@ def edit_tab(self, index): self.move(self.main.mapToGlobal(rect.topLeft())) # Copies tab name and selects all - self.setText(self.main.tabText(index)) + text = self.main.tabText(index) + text = text.replace(u'&', u'') + if self.split_char: + text = text.split(self.split_char)[0] + + self.setText(text) self.selectAll() if not self.isVisible(): @@ -128,7 +135,7 @@ class TabBar(QTabBar): sig_move_tab = Signal((int, int), (str, int, int)) sig_change_name = Signal(str) - def __init__(self, parent, ancestor, rename_tabs=False): + def __init__(self, parent, ancestor, rename_tabs=False, split_char=''): QTabBar.__init__(self, parent) self.ancestor = ancestor @@ -145,7 +152,7 @@ def __init__(self, parent, ancestor, rename_tabs=False): self.rename_tabs = rename_tabs if self.rename_tabs: # Creates tab name editor - self.tab_name_editor = EditTabNamePopup(self) + self.tab_name_editor = EditTabNamePopup(self, split_char) else: self.tab_name_editor = None @@ -399,10 +406,11 @@ class Tabs(BaseTabs): def __init__(self, parent, actions=None, menu=None, corner_widgets=None, menu_use_tooltips=False, - rename_tabs=False): + rename_tabs=False, split_char=''): BaseTabs.__init__(self, parent, actions, menu, corner_widgets, menu_use_tooltips) - tab_bar = TabBar(self, parent, rename_tabs=rename_tabs) + tab_bar = TabBar(self, parent, rename_tabs=rename_tabs, + split_char=split_char) tab_bar.sig_move_tab.connect(self.move_tab) tab_bar.sig_move_tab[(str, int, int)].connect( self.move_tab_from_another_tabwidget) From 9f6fb058211dc5f8fe2f3c2893adffa8cb500ae1 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 22:40:42 -0500 Subject: [PATCH 11/15] IPython console: Use split_char when renaming tabs We also forbid '/' to be part of a tab name. --- spyder/plugins/ipythonconsole.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index c9d4c4e76f6..d324f245770 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -621,7 +621,8 @@ def __init__(self, parent, testing=False): os.mkdir(programs.TEMPDIR) layout = QVBoxLayout() - self.tabwidget = Tabs(self, self.menu_actions, rename_tabs=True) + self.tabwidget = Tabs(self, self.menu_actions, rename_tabs=True, + split_char='/') if hasattr(self.tabwidget, 'setDocumentMode')\ and not sys.platform == 'darwin': # Don't set document mode to true on OSX because it generates @@ -1377,13 +1378,13 @@ def rename_tabs_after_change(self, given_name): client = self.get_current_client() # Rename current client tab to add str_id - if client.allow_rename: + if client.allow_rename and not u'/' in given_name: self.rename_client_tab(client, given_name) else: self.rename_client_tab(client, None) # Rename related clients - if client.allow_rename: + if client.allow_rename and not u'/' in given_name: for cl in self.get_related_clients(client): self.rename_client_tab(cl, given_name) From 9ffefce4ba1fb7ebed6e3b678ca06e17fb7cc8d0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 3 Jul 2017 22:55:50 -0500 Subject: [PATCH 12/15] Main window: Don't remove TEMPDIR for new instances Else new consoles can't be created in other window instances because they all share the same temp directory. --- spyder/app/mainwindow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spyder/app/mainwindow.py b/spyder/app/mainwindow.py index 7a080d38ea8..5d45a7bff5b 100644 --- a/spyder/app/mainwindow.py +++ b/spyder/app/mainwindow.py @@ -2107,7 +2107,8 @@ def set_splash(self, message): def remove_tmpdir(self): """Remove Spyder temporary directory""" - shutil.rmtree(programs.TEMPDIR, ignore_errors=True) + if CONF.get('main', 'single_instance') and not self.new_instance: + shutil.rmtree(programs.TEMPDIR, ignore_errors=True) def closeEvent(self, event): """closeEvent reimplementation""" From 1325542ca44c0b740406276d5ec8056bf159e4bf Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 4 Jul 2017 06:59:42 -0500 Subject: [PATCH 13/15] Tabs: Add a split_index arg to select what part of a tab name is selected --- spyder/plugins/ipythonconsole.py | 2 +- spyder/widgets/tabs.py | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index d324f245770..326431c3898 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -622,7 +622,7 @@ def __init__(self, parent, testing=False): layout = QVBoxLayout() self.tabwidget = Tabs(self, self.menu_actions, rename_tabs=True, - split_char='/') + split_char='/', split_index=0) if hasattr(self.tabwidget, 'setDocumentMode')\ and not sys.platform == 'darwin': # Don't set document mode to true on OSX because it generates diff --git a/spyder/widgets/tabs.py b/spyder/widgets/tabs.py index 8355b4f4a7b..8f5e2c28f22 100644 --- a/spyder/widgets/tabs.py +++ b/spyder/widgets/tabs.py @@ -35,13 +35,14 @@ class EditTabNamePopup(QLineEdit): """Popup on top of the tab to edit its name.""" - def __init__(self, parent, split_char): + def __init__(self, parent, split_char, split_index): """Popup on top of the tab to edit its name.""" # Variables # Parent (main) self.main = parent if parent is not None else self.parent() self.split_char = split_char + self.split_index = split_index # Track which tab is being edited self.tab_index = None @@ -109,7 +110,7 @@ def edit_tab(self, index): text = self.main.tabText(index) text = text.replace(u'&', u'') if self.split_char: - text = text.split(self.split_char)[0] + text = text.split(self.split_char)[self.split_index] self.setText(text) self.selectAll() @@ -135,7 +136,8 @@ class TabBar(QTabBar): sig_move_tab = Signal((int, int), (str, int, int)) sig_change_name = Signal(str) - def __init__(self, parent, ancestor, rename_tabs=False, split_char=''): + def __init__(self, parent, ancestor, rename_tabs=False, split_char='', + split_index=0): QTabBar.__init__(self, parent) self.ancestor = ancestor @@ -152,7 +154,8 @@ def __init__(self, parent, ancestor, rename_tabs=False, split_char=''): self.rename_tabs = rename_tabs if self.rename_tabs: # Creates tab name editor - self.tab_name_editor = EditTabNamePopup(self, split_char) + self.tab_name_editor = EditTabNamePopup(self, split_char, + split_index) else: self.tab_name_editor = None @@ -406,11 +409,14 @@ class Tabs(BaseTabs): def __init__(self, parent, actions=None, menu=None, corner_widgets=None, menu_use_tooltips=False, - rename_tabs=False, split_char=''): + rename_tabs=False, split_char='', + split_index=0): BaseTabs.__init__(self, parent, actions, menu, corner_widgets, menu_use_tooltips) - tab_bar = TabBar(self, parent, rename_tabs=rename_tabs, - split_char=split_char) + tab_bar = TabBar(self, parent, + rename_tabs=rename_tabs, + split_char=split_char, + split_index=split_index) tab_bar.sig_move_tab.connect(self.move_tab) tab_bar.sig_move_tab[(str, int, int)].connect( self.move_tab_from_another_tabwidget) From 4a409a2bcfc0d55ac1be24fb854e4121a15b348f Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 4 Jul 2017 10:18:17 -0500 Subject: [PATCH 14/15] IPython console: Close all Matplotlib figures when running in a dedicated console --- spyder/plugins/ipythonconsole.py | 2 ++ spyder/utils/ipython/spyder_kernel.py | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index 326431c3898..8fd50458d3a 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -891,6 +891,8 @@ def execute_code(self, lines, current_client=True, clear_variables=False): # Clear console and reset namespace for # dedicated clients sw.silent_execute('%clear') + sw.silent_execute( + 'get_ipython().kernel.close_all_mpl_figures()') sw.reset_namespace(force=True) elif current_client and clear_variables: sw.reset_namespace(force=True) diff --git a/spyder/utils/ipython/spyder_kernel.py b/spyder/utils/ipython/spyder_kernel.py index cf6214f321b..2c8ad8fc996 100644 --- a/spyder/utils/ipython/spyder_kernel.py +++ b/spyder/utils/ipython/spyder_kernel.py @@ -252,6 +252,15 @@ def get_env(self): """Get environment variables.""" return os.environ.copy() + def close_all_mpl_figures(self): + """Close all Matplotlib figures.""" + try: + import matplotlib.pyplot as plt + plt.close('all') + del plt + except: + pass + # -- Private API --------------------------------------------------- # --- For the Variable Explorer def _get_current_namespace(self, with_magics=False): From c967e9133504030ef0849c7cdbfe439edfa3f7b0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 4 Jul 2017 15:31:23 -0500 Subject: [PATCH 15/15] IPython console: Don't increase the count of master clients when creating dedicated ones --- spyder/plugins/ipythonconsole.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index 8fd50458d3a..03ab6b5d456 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -1264,7 +1264,13 @@ def create_client_from_path(self, path): def create_client_for_file(self, filename): """Create a client to execute code related to a file.""" + # Create client self.create_new_client() + + # Don't increase the count of master clients + self.master_clients -= 1 + + # Rename client tab with filename client = self.get_current_client() client.allow_rename = False self.rename_client_tab(client, filename)