Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Add the possibility to rename IPython consoles #4092

Merged
merged 19 commits into from
Jul 2, 2017

Conversation

tjolive
Copy link
Contributor

@tjolive tjolive commented Feb 2, 2017

Fixes #1962


Tabs and TabBar classes (spyder/widgets/tabs.py) accept now a parameter (rename_tabs) to enable the tab renaming - defaults no disable but IPythonConsole instantiates Tabs with rename_tabs=True. If activated, the TabBar creates a new object EditTabNamePopup which is triggered by right double clicking on a tab.

The EditTabNamePopup instantiates QLineEdit and places it on top of the tab when activated. While activated, if the user clicks outside the QLineEdit of presses the ESC key, the changes are discarded (i.e. QLineEdit gets invisible).

Structure: IPythonConsole > Tabs > TabBar > EditTabNamePopUp

Some details that could be further improved:

  • The tab names have a margin of 9. This number was reached by trial&error, could not find it anywhere on the QTabBar/QTabWidget config. It works but would like to get this value dynamically
  • The QLineEdit defaults to a white background so I didn't include any config to change this. Not sure if we would like to see this customizable
  • The IPythonConsole class (spyder/plugins/ipythonconsole.py) already has a rename_client_tab function which is not used. Not sure if we want to keep it

@goanpeca
Copy link
Member

goanpeca commented Feb 2, 2017

@tjolive awesome, thanks for the help!

Could you paste some screenshots of the process or maybe an animation? Maybe use something like http://www.cockos.com/licecap/

@goanpeca goanpeca added this to the v3.2 milestone Feb 2, 2017
@tjolive
Copy link
Contributor Author

tjolive commented Feb 2, 2017

here you are:
tab_rename

Realized now that I cannot see any cursor when editing but my windows is behaving strangely...

Coded and tested on:

  • Windows 7
  • Spyder 3.2.0.dev0
  • Python 3.5.1 32bits
  • Qt 5.7.1
  • PyQt5 5.7.1

Copy link
Member

@goanpeca goanpeca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some style and code comments ;-)

@@ -1509,3 +1515,8 @@ def _create_client_for_kernel(self, connection_file, hostname, sshkey,

# Register client
self.register_client(client)

def tab_name_editor(self):
"""Triggers the tab name editor"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add a . at the end a docstring is a sentence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trigger (without the s).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -48,6 +48,12 @@ def __init__(self, parent, ancestor):
self.setAcceptDrops(True)
self.setUsesScrollButtons(True)

# Tab name editor
self.rename_tabs = rename_tabs
if self.rename_tabs is True:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.rename_tabs is True this is redundant, we can just use

if self.rename_tabs:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually I use this as an implicit "assert isinstance(rename_tabs, bool)" but I'll align with your code style

self.rename_tabs = rename_tabs
if self.rename_tabs is True:
# Creates tab name editor
self.tab_name_editor = EditTabNamePopup(self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is only getting created if true, so maybe we should define a

self.tab_name_editor = None above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, missed that



def mouseDoubleClickEvent(self, event):
"""Override Qt method to trigge the tab name editor"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

. at the end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Member

@rlaverde rlaverde Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trigger (missing r)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

# Tab is valid, call tab name editor
self.tab_name_editor.edit_tab(index)
else:
# Even is not interessant, raise to parent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Event ? missed a t?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo, thanks for spotting

"""Popup on top of the tab to edit its name"""

def __init__(self, parent):
QLineEdit.__init__(self, parent=None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add the same docstring here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

self.setFrame(False)

# Align with tab name
self.setTextMargins(9, 0, 0, 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will need to check how this looks on other OS

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are these cross-platform tests usually performed?

Copy link
Member

@rlaverde rlaverde Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks nice on Linux 😄

I only noticed the QLineEdit a little oversized when the tab isn't active (you need to double-click a little fast in a non active tab)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you post some video? On OSX works also (see below comment).

But I can't see the cursor, will fix it.

self.setTextMargins(9, 0, 0, 0)

# Track with tab is being edited
self.tab_index = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have a similar structure to how other widgets work


# Instance variables

# Widgets

# Widget setup

# Layouts

# Signals and slots

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you have any template for such documentation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, but most of the widgets follow that convention

class SomeWidget():
    # Variables
    self.some_variable = None
  
    # Widgets
    self.super_widget = SomeSuperWidget()

    # Widget setup
    self.super_widget.setSomething(True)

    # Layouts
    layout = QVBoxLayout()
    layout.addWidget(self.super_widget)
    self.setLayout(layout)

   # Signals and slots
   self.super_widget.sig_some_signal.connect(self.some_other_method)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@goanpeca We need to document that somewhere.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will align the code with this.

Took the liberty to add to the wiki the docstring format as mentioned here:
https://github.com/spyder-ide/spyder/wiki/Dev:-Coding-Style#docstrings

self.tab_index = None

def eventFilter(self, widget, event):
"""Catches clicks outside the object and ESC key press"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

. at the end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catch (infinitive form)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

return QLineEdit.eventFilter(self, widget, event)

def edit_tab(self, index):
"""Activates the edit tab"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

. at the end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Activate (infinitive form)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@goanpeca goanpeca changed the title PR1962: rename IPython consoles PR: rename IPython consoles Feb 2, 2017
Copy link
Member

@rlaverde rlaverde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested on Linux and works well. I just left some style comments.

@@ -756,6 +756,10 @@ def get_plugin_actions(self):
_("Open a new IPython console connected to an existing kernel"),
triggered=self.create_client_for_kernel)

rename_tab_action = create_action(self, _("&Rename tab"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just "Rename tab"(without the &) is ok, this menu won't be accessible using shortcuts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -1509,3 +1515,8 @@ def _create_client_for_kernel(self, connection_file, hostname, sshkey,

# Register client
self.register_client(client)

def tab_name_editor(self):
"""Triggers the tab name editor"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trigger (without the s).



def mouseDoubleClickEvent(self, event):
"""Override Qt method to trigge the tab name editor"""
Copy link
Member

@rlaverde rlaverde Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trigger (missing r)

self.tab_index = None

def eventFilter(self, widget, event):
"""Catches clicks outside the object and ESC key press"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catch (infinitive form)

return QLineEdit.eventFilter(self, widget, event)

def edit_tab(self, index):
"""Activates the edit tab"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Activate (infinitive form)

@tjolive
Copy link
Contributor Author

tjolive commented Feb 3, 2017

Tested on OSX Sierra 10.12.2 (16C67, Python 2.7.13 64bits, Qt 5.6.2, PyQt5 5.6 on Darwin

on_osx

When editing, the text is not as aligned as in windows, maybe because the close icon is rendered on the left. In any way it looks nice and intuitive.

The cursor is also missing in OSX, did you have the same behaviour on linux?

@goanpeca
Copy link
Member

goanpeca commented Feb 3, 2017

This is looking great

@ccordoba12 ccordoba12 modified the milestones: v3.2, v3.3 Feb 14, 2017
@tjolive
Copy link
Contributor Author

tjolive commented Feb 17, 2017

Hi guys,

new commit addresses all feedback (I hope) and adds a blinking cursor, which I felt was missing.

But now the Travis CI is failing on something that I didn't change and was working on the first commit.

+for f in 'spyder/*/*/*.py'
+[[ spyder/utils/introspection/docstrings.py == *test*/test_* ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/external/*/*.py ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/utils/external/*.py ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/utils/help/*.py ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/utils/ipython/start_kernel.py ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/utils/ipython/spyder_kernel.py ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/utils/site/sitecustomize.py ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/utils/introspection/plugin_client.py ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/widgets/externalshell/systemshell.py ]]
+[[ spyder/utils/introspection/docstrings.py == spyder/widgets/ipythonconsole/__init__.py ]]
+python spyder/utils/introspection/docstrings.py
Traceback (most recent call last):
  File "spyder/utils/introspection/docstrings.py", line 26, in <module>
    from jedi.evaluate.iterable import Array, FakeSequence, AlreadyEvaluated
ImportError: cannot import name 'Array'
The command "./continuous_integration/travis/modules_test.sh" exited with 1.
Done. Your build exited with 1.

Any ideas?

Do you need any more changes on this PR?

Cheers,
Telmo

@ccordoba12 ccordoba12 changed the base branch from master to 3.x February 17, 2017 19:38
@ccordoba12
Copy link
Member

Please merge with the 3.x branch to fix that error.

@goanpeca
Copy link
Member

goanpeca commented Feb 21, 2017

@tjolive I think you made a mistake :-|, cause now the PR is $^%^

You were supposed to merge with 3.x not master :-(

@tjolive
Copy link
Contributor Author

tjolive commented Feb 21, 2017

Argh, I see, I tried to do via the github desktop app and it defaulted to master. Reverting and merging with 3.x...

@goanpeca
Copy link
Member

goanpeca commented Feb 21, 2017

If you are not sure how to fix this @tjolive, just start from scratch on a new PR and be more careful next time ;-)

@tjolive
Copy link
Contributor Author

tjolive commented Feb 22, 2017

Revert was ok. Merged with 3.x but it didn't trigger a re-check...

Will try something else, otherwise I'll issue a new PR

@goanpeca
Copy link
Member

goanpeca commented Feb 22, 2017

Even if reverted... your PR is introducing too many unneeded commits, so its not ok at this point

screen shot 2017-02-22 at 8 45 15 am

@tjolive
Copy link
Contributor Author

tjolive commented Jun 30, 2017 via email

@gaw89
Copy link

gaw89 commented Jun 30, 2017

@tjolive, I see. I asked you because of the conflicting files listed in the PR status. Is that your responsibility to figure out the conflict?

@ccordoba12, what needs to be done in order to get this PR accepted? If I can help, I'm happy to do so.

@tjolive
Copy link
Contributor Author

tjolive commented Jun 30, 2017 via email

@gaw89
Copy link

gaw89 commented Jun 30, 2017

@tjolive, I see. Perhaps the conflict cropped up since you submitted the PR. If there's anything I can do to help, let me know. This feature would be incredibly useful for me and a great addition to an already great IDE!

@ccordoba12
Copy link
Member

@tjolive, don't worry about the conflict. We need to fix a couple of things before merging your work, but we'll keep working on your branch.

@ccordoba12
Copy link
Member

@gaw89, this will be part of Spyder 3.2.

@gaw89
Copy link

gaw89 commented Jun 30, 2017

@ccordoba12, awesome! I'll keep an eye out for it. Is there a release date for 3.2?

@ccordoba12
Copy link
Member

Hopefully in a couple of weeks.

@ccordoba12 ccordoba12 changed the title PR: Rename IPython consoles PR: Add the possibility to rename IPython consoles Jul 2, 2017
@ccordoba12 ccordoba12 dismissed goanpeca’s stale review July 2, 2017 19:02

Comments were already addressed

@ccordoba12 ccordoba12 merged commit c92bf8f into spyder-ide:3.x Jul 2, 2017
ccordoba12 added a commit that referenced this pull request Jul 2, 2017
@peendebak
Copy link

@tjolive @ccordoba12 Is there an option to rename the tabs from the command line itself? E.g. import spyder; spyder.tags.rename('my name');

@ccordoba12
Copy link
Member

No, there isn't.

@peendebak
Copy link

@ccordoba12 Is it technically possible? E.g. can the spyder gui elements be controlled from the ipython console? If so, how, then I can make a PR to create this functionality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants