diff -Nru spyder-2.3.8+dfsg1/app_example/create_exe.py spyder-3.0.2+dfsg1/app_example/create_exe.py --- spyder-2.3.8+dfsg1/app_example/create_exe.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/app_example/create_exe.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Create a stand-alone executable""" - -try: - from guidata.disthelpers import Distribution -except ImportError: - raise ImportError("This script requires guidata 1.4+") - -import spyderlib - - -def create_executable(): - """Build executable using ``guidata.disthelpers``""" - dist = Distribution() - dist.setup(name="Example", version="1.1", - description="Embedding Spyder Qt shell", - script="example.pyw", target_name="example.exe") - spyderlib.add_to_distribution(dist) - #dist.add_modules('matplotlib') # Uncomment if you need matplotlib - dist.excludes += ['IPython'] - # Building executable - dist.build('cx_Freeze') - - -if __name__ == '__main__': - create_executable() diff -Nru spyder-2.3.8+dfsg1/app_example/example.pyw spyder-3.0.2+dfsg1/app_example/example.pyw --- spyder-2.3.8+dfsg1/app_example/example.pyw 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/app_example/example.pyw 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ - -from spyderlib.qt.QtGui import (QApplication, QMainWindow, QWidget, QLabel, - QLineEdit, QHBoxLayout, QDockWidget, QFont) -from spyderlib.qt.QtCore import Qt - -from spyderlib.widgets.internalshell import InternalShell - - -class MyWidget(QWidget): - def __init__(self): - QWidget.__init__(self) - label = QLabel("Imagine an extraordinary complex widget right here...") - self.edit = QLineEdit("Text") - layout = QHBoxLayout() - layout.addWidget(label) - layout.addWidget(self.edit) - self.setLayout(layout) - - def get_text(self): - """Return sample edit text""" - return self.edit.text() - - def set_text(self, text): - """Set sample edit text""" - self.edit.setText(text) - - -class MyWindow(QMainWindow): - def __init__(self): - QMainWindow.__init__(self) - - # Set this very simple widget as central widget - widget = MyWidget() - self.setCentralWidget(widget) - - # Create the console widget - font = QFont("Courier new") - font.setPointSize(10) - ns = {'win': self, 'widget': widget} - msg = "Try for example: widget.set_text('foobar') or win.close()" - # Note: by default, the internal shell is multithreaded which is safer - # but not compatible with graphical user interface creation. - # For example, if you need to plot data with Matplotlib, you will need - # to pass the option: multithreaded=False - self.console = cons = InternalShell(self, namespace=ns, message=msg) - - # Setup the console widget - cons.set_font(font) - cons.set_codecompletion_auto(True) - cons.set_calltips(True) - cons.setup_calltips(size=600, font=font) - cons.setup_completion(size=(300, 180), font=font) - console_dock = QDockWidget("Console", self) - console_dock.setWidget(cons) - - # Add the console widget to window as a dockwidget - self.addDockWidget(Qt.BottomDockWidgetArea, console_dock) - - self.resize(800, 600) - - def closeEvent(self, event): - self.console.exit_interpreter() - event.accept() - -def main(): - app = QApplication([]) - win = MyWindow() - win.show() - app.exec_() - -if __name__ == "__main__": - main() \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/bootstrap.py spyder-3.0.2+dfsg1/bootstrap.py --- spyder-2.3.8+dfsg1/bootstrap.py 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/bootstrap.py 2016-11-17 00:27:51.000000000 +0000 @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # # Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) +# (see spyder/__init__.py for details) """ Bootstrapping Spyder @@ -32,7 +32,8 @@ Type `python bootstrap.py -- --help` to read about Spyder options.""") parser.add_option('--gui', default=None, - help="GUI toolkit: pyqt (for PyQt4) or pyside (for PySide)") + help="GUI toolkit: pyqt5 (for PyQt5), pyqt (for PyQt4) or " + "pyside (for PySide, deprecated)") parser.add_option('--hide-console', action='store_true', default=False, help="Hide parent console window (Windows only)") parser.add_option('--test', dest="test", action='store_true', default=False, @@ -41,9 +42,13 @@ default=False, help="Disable Apport exception hook (Ubuntu)") parser.add_option('--debug', action='store_true', default=False, help="Run Spyder in debug mode") + options, args = parser.parse_args() -assert options.gui in (None, 'pyqt', 'pyside'), \ +# Store variable to be used in self.restart (restart spyder instance) +os.environ['SPYDER_BOOTSTRAP_ARGS'] = str(sys.argv[1:]) + +assert options.gui in (None, 'pyqt5', 'pyqt', 'pyside'), \ "Invalid GUI toolkit option '%s'" % options.gui # For testing purposes @@ -92,27 +97,28 @@ # --- Continue -from spyderlib.utils.vcs import get_hg_revision -print("Revision %s:%s, Branch: %s" % get_hg_revision(DEVPATH)) +from spyder.utils.vcs import get_git_revision +print("Revision %s, Branch: %s" % get_git_revision(DEVPATH)) sys.path.insert(0, DEVPATH) print("01. Patched sys.path with %s" % DEVPATH) -EXTPATH = osp.join(DEVPATH, 'external-py' + sys.version[0]) -if osp.isdir(EXTPATH): - sys.path.insert(0, EXTPATH) - print(" and %s" % EXTPATH) - -# Selecting the GUI toolkit: PySide if installed, otherwise PyQt4 +# Selecting the GUI toolkit: PyQt5 if installed, otherwise PySide or PyQt4 # (Note: PyQt4 is still the officially supported GUI toolkit for Spyder) if options.gui is None: try: - import PySide # analysis:ignore - print("02. PySide is detected, selecting (experimental)") - os.environ['QT_API'] = 'pyside' - except: - print("02. No PySide detected, using PyQt4 if available") + import PyQt5 # analysis:ignore + print("02. PyQt5 is detected, selecting") + os.environ['QT_API'] = 'pyqt5' + except ImportError: + try: + import PyQt4 # analysis:ignore + print("02. PyQt4 is detected, selecting") + os.environ['QT_API'] = 'pyqt' + except ImportError: + print("02. No PyQt5 or PyQt4 detected, using PySide if available " + "(deprecated)") else: print ("02. Skipping GUI toolkit detection") os.environ['QT_API'] = options.gui @@ -120,7 +126,7 @@ if options.debug: # safety check - Spyder config should not be imported at this point - if "spyderlib.baseconfig" in sys.modules: + if "spyder.config.base" in sys.modules: sys.exit("ERROR: Can't enable debug mode - Spyder is already imported") print("0x. Switching debug mode on") os.environ["SPYDER_DEBUG"] = "True" @@ -130,7 +136,7 @@ # Checking versions (among other things, this has the effect of setting the # QT_API environment variable if this has not yet been done just above) -from spyderlib import get_versions +from spyder import get_versions versions = get_versions(reporev=False) print("03. Imported Spyder %s" % versions['spyder']) print(" [Python %s %dbits, Qt %s, %s %s on %s]" % \ @@ -138,14 +144,22 @@ versions['qt_api'], versions['qt_api_ver'], versions['system'])) +# Check that we have the right qtpy version +from spyder.utils import programs +if not programs.is_module_installed('qtpy', '>=1.1.0'): + print("") + sys.exit("ERROR: Your qtpy version is outdated. Please install qtpy " + "1.1.0 or higher to be able to work with Spyder!") + + # --- Executing Spyder if not options.hide_console and os.name == 'nt': print("0x. Enforcing parent console (Windows only)") sys.argv.append("--show-console") # Windows only: show parent console -print("04. Executing spyder.main()") -from spyderlib import start_app +print("04. Running Spyder") +from spyder.app import start time_lapse = time.time()-time_start print("Bootstrap completed in " + @@ -153,4 +167,4 @@ # gmtime() converts float into tuple, but loses milliseconds ("%.4f" % time_lapse).split('.')[1]) -start_app.main() +start.main() diff -Nru spyder-2.3.8+dfsg1/debian/changelog spyder-3.0.2+dfsg1/debian/changelog --- spyder-2.3.8+dfsg1/debian/changelog 2016-02-16 14:45:31.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/changelog 2016-10-30 18:22:44.000000000 +0000 @@ -1,8 +1,55 @@ -spyder (2.3.8+dfsg1-1build1) xenial; urgency=medium +spyder (3.0.2+dfsg1-1) unstable; urgency=medium - * No change rebuild to drop python3.4 support. + * New upstream version 3.0.2+dfsg1 + * Move the locale data into the spyder-common package - -- Dimitri John Ledkov Tue, 16 Feb 2016 14:45:31 +0000 + -- Picca Frédéric-Emmanuel Sun, 30 Oct 2016 19:22:44 +0100 + +spyder (3.0.1+dfsg1-1) unstable; urgency=medium + + * New upstream version 3.0.1+dfsg1 + + -- Picca Frédéric-Emmanuel Sun, 30 Oct 2016 19:22:43 +0100 + +spyder (3.0.0+dfsg1-1~exp1) experimental; urgency=medium + + * New upstream version 3.0.0+dfsg1 + * Upstream renamed module spyderlib into spyder + * d/control + - New binary packages: python-spyder, python3-spyder, spyder-doc + - python-spyderlib, python3-spyderlib, python-spyder-doc are now + transitional packages. + + -- Picca Frédéric-Emmanuel Sun, 25 Sep 2016 13:21:06 +0200 + +spyder (3.0.0~b5+dfsg1-1~exp1) experimental; urgency=medium + + * Imported Upstream version 3.0.0~b5+dfsg1 + * debian/control + - Added Depends: python-pep8, python3-pep8 + + -- Picca Frédéric-Emmanuel Tue, 23 Aug 2016 22:24:06 +0200 + +spyder (3.0.0~b4+dfsg1-1~exp1) experimental; urgency=medium + + * Imported Upstream version 3.0.0~b4+dfsg1 + (Closes: #784613, #797069, #826042, #811339) + * debian/watch + - Use the Debian pypi redirector + * debian/copyright + - updated Files-Excluded + * debian/control + - update the Vcs-X fileds + - Remove Alexandre Fayolle and Ludovic Aubry from Uploaders + thanks for their work. (Closes: #833330) + - Added Depends: python-nbconvert, python-qtawesome, python-qtconsole, + python-qtpy, python-pickleshare, python-pyflakes, python-pygments, + python-zmq, + python3-nbconvert, python3-qtawesome, python3-qtconsole, + python3-qtpy, python3-pickleshare, python3-pyflakes, python3-pygments, + python3-zmq + + -- Picca Frédéric-Emmanuel Sun, 21 Aug 2016 11:52:26 +0200 spyder (2.3.8+dfsg1-1) unstable; urgency=medium diff -Nru spyder-2.3.8+dfsg1/debian/control spyder-3.0.2+dfsg1/debian/control --- spyder-2.3.8+dfsg1/debian/control 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/control 2016-10-30 18:22:44.000000000 +0000 @@ -1,8 +1,6 @@ Source: spyder Maintainer: Debian Science Maintainers -Uploaders: Ludovic Aubry , - Alexandre Fayolle , - Picca Frédéric-Emmanuel +Uploaders: Picca Frédéric-Emmanuel Section: science Priority: extra Build-Depends: debhelper (>= 9), @@ -11,14 +9,12 @@ python-setuptools, python-sphinx, python3-all, - python3-pyqt4, python3-setuptools, - python3-sip, python3-sphinx, xsltproc -Standards-Version: 3.9.6 -Vcs-Browser: http://anonscm.debian.org/cgit/debian-science/packages/spyder.git -Vcs-Git: git://anonscm.debian.org/debian-science/packages/spyder.git +Standards-Version: 3.9.8 +Vcs-Browser: https://anonscm.debian.org/cgit/debian-science/packages/spyder.git +Vcs-Git: https://anonscm.debian.org/git/debian-science/packages/spyder.git Homepage: https://github.com/spyder-ide/spyder X-Python-Version: >= 2.7 X-Python3-Version: >= 3.2 @@ -28,7 +24,7 @@ Section: devel Depends: ${misc:Depends}, ${python:Depends}, - python-spyderlib (= ${binary:Version}) + python-spyder (= ${binary:Version}) Breaks: ${python-Breaks}, python-spyderlib (<< 2.3.0+dfsg-2) Replaces: python-spyderlib (<< 2.3.0+dfsg-2) @@ -41,34 +37,51 @@ Package: python-spyderlib Architecture: all +Section: oldlibs +Depends: ${misc:Depends}, python-spyder +Description: transitional dummy package for python-spyder + This is a transitional package to ease upgrades to the + python-spyder package. It can be safely removed. + +Package: python-spyder +Architecture: all Section: python Depends: ${misc:Depends}, ${python:Depends}, libjs-jquery, libjs-mathjax, - python-qt4, - spyder-common -Recommends: ipython-qtconsole, - pep8, - pyflakes (>= 0.5.0), - pylint, - python-bs4, - python-jedi, - python-matplotlib, + pep8, + pylint, + python-bs4, + python-jedi, + python-nbconvert, + python-pep8, + python-psutil, + python-qtawesome, + python-qtconsole, + python-qtpy (>= 1.1.0), + python-pickleshare, + python-pyflakes, + python-pygments, + python-rope, + python-sphinx, + python-zmq, + spyder-common (= ${binary:Version}), +Recommends: python-matplotlib, python-numpy, python-pandas, - python-psutil (>= 0.3.0), - python-rope, python-scipy, - python-sphinx, - python-spyderlib-doc (= ${binary:Version}) + spyder-doc (= ${binary:Version}) Suggests: tortoisehg, gitk Breaks: ${python:Breaks}, spyder (<< 2.3.0+dfsg-2), - python-spyderlib-doc (<< 2.3.0+dfsg-4) -Provides: ${python:Provides} -Replaces: spyder (<< 2.0.12-1) + python-spyderlib-doc (<< 2.3.0+dfsg-4), + python-spyderlib (<< 3.0.0+dfsg1-1~) +Provides: ${python:Provides}, + python-spyderlib +Replaces: spyder (<< 2.0.12-1), + python-spyderlib (<< 3.0.0+dfsg1-1~) Description: Python IDE for scientists (Python 2 modules) Originally written to design Spyder (the Scientific PYthon Development EnviRonment), the spyderlib Python library provides @@ -82,6 +95,14 @@ Package: python-spyderlib-doc Architecture: all +Section: oldlibs +Depends: ${misc:Depends}, spyder-doc +Description: transitional dummy package for python-spyder-doc + This is a transitional package to ease upgrades to the + spyder-doc package. It can be safely removed. + +Package: spyder-doc +Architecture: all Section: doc Depends: ${misc:Depends}, ${sphinxdoc:Depends} @@ -103,7 +124,7 @@ Section: devel Depends: ${misc:Depends}, ${python3:Depends}, - python3-spyderlib (= ${binary:Version}) + python3-spyder (= ${binary:Version}) Breaks: ${python3-Breaks}, python3-spyderlib (<< 2.3.0+dfsg-2) Replaces: python3-spyderlib (<< 2.3.0+dfsg-2) @@ -116,31 +137,48 @@ Package: python3-spyderlib Architecture: all +Section: oldlibs +Depends: ${misc:Depends}, python3-spyder +Description: transitional dummy package for python3-spyder + This is a transitional package to ease upgrades to the + python3-spyder package. It can be safely removed. + +Package: python3-spyder +Architecture: all Section: python Depends: ${misc:Depends}, ${python3:Depends}, libjs-jquery, libjs-mathjax, - python3-pyqt4, - spyder-common -Recommends: ipython3-qtconsole, - python3-pep8, - pyflakes (>= 0.5.0), - pylint, - python3-bs4, - python3-jedi, - python3-matplotlib, + pep8, + pylint, + python3-bs4, + python3-jedi, + python3-nbconvert, + python3-pep8, + python3-psutil, + python3-qtawesome, + python3-qtconsole, + python3-qtpy (>= 1.1.0), + python3-pickleshare, + python3-pyflakes, + python3-pygments, + python3-sphinx, + python3-zmq, + spyder-common (= ${binary:Version}), +Recommends: python3-matplotlib, python3-numpy, python3-pandas, - python3-psutil, python3-scipy, - python3-sphinx, - python-spyderlib-doc (= ${binary:Version}) + spyder-doc (= ${binary:Version}) Suggests: tortoisehg, gitk -Provides: ${python3:Provides} Breaks: ${python3:Breaks}, - spyder3 (<< 2.3.0+dfsg-2) + spyder3 (<< 2.3.0+dfsg-2), + python3-spyderlib (<< 3.0.0+dfsg1-1~) +Provides: ${python3:Provides}, + python3-spyderlib +Replaces: python3-spyderlib (<< 3.0.0+dfsg1-1~) Description: Python IDE for scientists (Python 3 modules) Originally written to design Spyder (the Scientific PYthon Development EnviRonment), the spyderlib Python library provides @@ -154,7 +192,6 @@ Package: spyder-common Architecture: all -Section: python Depends: ${misc:Depends} Provides: ${python3:Provides} Description: Python IDE for scientists (common files) diff -Nru spyder-2.3.8+dfsg1/debian/copyright spyder-3.0.2+dfsg1/debian/copyright --- spyder-2.3.8+dfsg1/debian/copyright 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/copyright 2016-10-30 18:22:44.000000000 +0000 @@ -1,9 +1,9 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Pierre Raybaut Upstream-Contact: pierre.raybaut@gmail.com Source: https://github.com/spyder-ide/spyder/releases -Files-Excluded: spyderlib/utils/inspector/js/mathjax - spyderlib/utils/inspector/js/jquery.js +Files-Excluded: spyder/utils/help/js/mathjax + spyder/utils/help/js/jquery.js Files: debian/* Copyright: © 2009-2010 Ludovic Aubry @@ -15,19 +15,11 @@ © 2015 The Spyder development team License: Expat -Files: spyderlib/utils/external/path.py -Copyright: © 2007 Jason Orendorff -License: public-domain - -Files: spyderlib/utils/external/pickleshare.py -Copyright: © Ville Vainio -License: Expat - -Files: spyderlib/utils/inspector/js/collapse_sections.js +Files: spyder/utils/help/js/collapse_sections.js Copyright: 2011-2012 by Assurance Technologies License: BSD-3-clause -Files: spyderlib/utils/introspection/module_completion.py +Files: spyder/utils/introspection/module_completion.py Copyright: 2005,2008,2009,2010,2011 The IPython Development Team 2001-2007, Fernando Perez 2001, Janko Hauser @@ -39,217 +31,212 @@ 2005, Jörgen Stenarson License: BSD-3-clause -Files: spyderlib/utils/inspector/sphinxify.py +Files: spyder/utils/help/sphinxify.py Copyright: © 2009 Tim Dumol © 2010 Carlos Cordoba License: BSD-3-clause -Files: spyderplugins/p_profiler.py spyderplugins/widgets/profilergui.py +Files: spyder_profiler/* Copyright: © 2011 Pierre Raybaut © 2011 Santiago Jaramillo License: Expat -Files: spyderlib/widgets/externalshell/inputhooks.py +Files: spyder/utils/inputhooks.py Copyright: (C) The IPython Development Team License: BSD-3-clause -Files: spyderlib/images/arredit.png - spyderlib/images/dictedit.png - spyderlib/images/none.png - spyderlib/images/not_found.png - spyderlib/images/matplotlib.png - spyderlib/images/options.svg - spyderlib/images/set_workdir.png - spyderlib/images/splash.png - spyderlib/images/spyder.svg - spyderlib/images/spyder_light.svg - spyderlib/images/whole_words.png - spyderlib/images/win_env.png - spyderlib/images/actions/hist.png - spyderlib/images/actions/window_nofullscreen.png - spyderlib/images/console/ipython_console.png - spyderlib/images/console/ipython_console_t.png - spyderlib/images/editor/filelist.png - spyderlib/images/editor/function.png - spyderlib/images/editor/method.png - spyderlib/images/editor/private1.png - spyderlib/images/editor/private2.png - spyderlib/images/editor/cell.png - spyderlib/images/editor/blockcomment.png - spyderlib/images/editor/refactor.png +Files: spyder/images/arredit.png + spyder/images/dictedit.png + spyder/images/none.png + spyder/images/not_found.png + spyder/images/matplotlib.png + spyder/images/options.svg + spyder/images/set_workdir.png + spyder/images/spyder.svg + spyder/images/whole_words.png + spyder/images/win_env.png + spyder/images/actions/hist.png + spyder/images/actions/window_nofullscreen.png + spyder/images/console/ipython_console.png + spyder/images/console/ipython_console_t.png + spyder/images/editor/filelist.png + spyder/images/editor/function.png + spyder/images/editor/method.png + spyder/images/editor/private1.png + spyder/images/editor/private2.png + spyder/images/editor/cell.png + spyder/images/editor/blockcomment.png + spyder/images/editor/refactor.png Copyright: © 2009-2013 Pierre Raybaut License: SLA -Files: spyderlib/images/advanced.png - spyderlib/images/arrow.png - spyderlib/images/bold.png - spyderlib/images/browser.png - spyderlib/images/font.png - spyderlib/images/genprefs.png - spyderlib/images/inspector.png - spyderlib/images/italic.png - spyderlib/images/pythonpath_mgr.png - spyderlib/images/upper_lower.png - spyderlib/images/vcs_browse.png - spyderlib/images/vcs_commit.png - spyderlib/images/actions/1downarrow.png - spyderlib/images/actions/1uparrow.png - spyderlib/images/actions/2downarrow.png - spyderlib/images/actions/2uparrow.png - spyderlib/images/actions/imshow.png - spyderlib/images/actions/insert.png - spyderlib/images/actions/lock.png - spyderlib/images/actions/lock_open.png - spyderlib/images/actions/magnifier.png - spyderlib/images/actions/next.png - spyderlib/images/actions/options_less.png - spyderlib/images/actions/options_more.png - spyderlib/images/actions/plot.png - spyderlib/images/actions/previous.png - spyderlib/images/actions/redo.png - spyderlib/images/actions/reload.png - spyderlib/images/actions/rename.png - spyderlib/images/actions/replace.png - spyderlib/images/actions/show.png - spyderlib/images/actions/special_paste.png - spyderlib/images/actions/synchronize.png - spyderlib/images/actions/tooloptions.png - spyderlib/images/actions/undo.png - spyderlib/images/actions/up.png - spyderlib/images/actions/zoom_in.png - spyderlib/images/actions/zoom_out.png - spyderlib/images/console/clear.png - spyderlib/images/console/cmdprompt_t.png - spyderlib/images/console/console.png - spyderlib/images/console/environ.png - spyderlib/images/console/history.png - spyderlib/images/console/history24.png - spyderlib/images/console/kill.png - spyderlib/images/console/prompt.png - spyderlib/images/console/restart.png - spyderlib/images/console/syspath.png - spyderlib/images/editor/bug.png - spyderlib/images/editor/close_panel.png - spyderlib/images/editor/comment.png - spyderlib/images/editor/error.png - spyderlib/images/editor/file.png - spyderlib/images/editor/fromcursor.png - spyderlib/images/editor/gotoline.png - spyderlib/images/editor/highlight.png - spyderlib/images/editor/horsplit.png - spyderlib/images/editor/indent.png - spyderlib/images/editor/last_edit_location.png - spyderlib/images/editor/newwindow.png - spyderlib/images/editor/next_cursor.png - spyderlib/images/editor/next_wng.png - spyderlib/images/editor/outline_explorer.png - spyderlib/images/editor/outline_explorer_vis.png - spyderlib/images/editor/prev_cursor.png - spyderlib/images/editor/prev_wng.png - spyderlib/images/editor/select.png - spyderlib/images/editor/selectall.png - spyderlib/images/editor/todo_list.png - spyderlib/images/editor/uncomment.png - spyderlib/images/editor/unindent.png - spyderlib/images/editor/versplit.png - spyderlib/images/editor/wng_list.png - spyderlib/images/file/fileclose.png - spyderlib/images/file/filecloseall.png - spyderlib/images/file/fileimport.png - spyderlib/images/file/filenew.png - spyderlib/images/file/fileopen.png - spyderlib/images/file/filesave.png - spyderlib/images/file/filesaveas.png - spyderlib/images/file/print.png - spyderlib/images/file/save_all.png - spyderlib/images/filetypes/bat.png - spyderlib/images/filetypes/bmp.png - spyderlib/images/filetypes/c.png - spyderlib/images/filetypes/cc.png - spyderlib/images/filetypes/cfg.png - spyderlib/images/filetypes/chm.png - spyderlib/images/filetypes/cl.png - spyderlib/images/filetypes/cmd.png - spyderlib/images/filetypes/cpp.png - spyderlib/images/filetypes/css.png - spyderlib/images/filetypes/cxx.png - spyderlib/images/filetypes/diff.png - spyderlib/images/filetypes/doc.png - spyderlib/images/filetypes/exe.png - spyderlib/images/filetypes/f.png - spyderlib/images/filetypes/f77.png - spyderlib/images/filetypes/f90.png - spyderlib/images/filetypes/gif.png - spyderlib/images/filetypes/h.png - spyderlib/images/filetypes/hh.png - spyderlib/images/filetypes/hpp.png - spyderlib/images/filetypes/htm.png - spyderlib/images/filetypes/html.png - spyderlib/images/filetypes/hxx.png - spyderlib/images/filetypes/inf.png - spyderlib/images/filetypes/ini.png - spyderlib/images/filetypes/jpeg.png - spyderlib/images/filetypes/jpg.png - spyderlib/images/filetypes/js.png - spyderlib/images/filetypes/log.png - spyderlib/images/filetypes/nt.png - spyderlib/images/filetypes/patch.png - spyderlib/images/filetypes/pdf.png - spyderlib/images/filetypes/png.png - spyderlib/images/filetypes/pps.png - spyderlib/images/filetypes/properties.png - spyderlib/images/filetypes/ps.png - spyderlib/images/filetypes/pxd.png - spyderlib/images/filetypes/pxi.png - spyderlib/images/filetypes/pyx.png - spyderlib/images/filetypes/rar.png - spyderlib/images/filetypes/readme.png - spyderlib/images/filetypes/reg.png - spyderlib/images/filetypes/rej.png - spyderlib/images/filetypes/session.png - spyderlib/images/filetypes/tar.png - spyderlib/images/filetypes/tex.png - spyderlib/images/filetypes/tgz.png - spyderlib/images/filetypes/tif.png - spyderlib/images/filetypes/tiff.png - spyderlib/images/filetypes/txt.png - spyderlib/images/filetypes/xls.png - spyderlib/images/filetypes/xml.png - spyderlib/images/filetypes/zip.png - spyderlib/images/projects/add_to_path.png - spyderlib/images/projects/folder.png - spyderlib/images/projects/package.png - spyderlib/images/projects/pp_folder.png - spyderlib/images/projects/pp_package.png - spyderlib/images/projects/pp_project.png - spyderlib/images/projects/project.png - spyderlib/images/projects/project_closed.png - spyderlib/images/projects/pythonpath.png - spyderlib/images/projects/remove_from_path.png - spyderlib/images/projects/show_all.png +Files: spyder/images/advanced.png + spyder/images/arrow.png + spyder/images/bold.png + spyder/images/browser.png + spyder/images/font.png + spyder/images/genprefs.png + spyder/images/italic.png + spyder/images/upper_lower.png + spyder/images/vcs_browse.png + spyder/images/vcs_commit.png + spyder/images/actions/1downarrow.png + spyder/images/actions/1uparrow.png + spyder/images/actions/2downarrow.png + spyder/images/actions/2uparrow.png + spyder/images/actions/imshow.png + spyder/images/actions/insert.png + spyder/images/actions/lock.png + spyder/images/actions/lock_open.png + spyder/images/actions/magnifier.png + spyder/images/actions/next.png + spyder/images/actions/options_less.png + spyder/images/actions/options_more.png + spyder/images/actions/plot.png + spyder/images/actions/previous.png + spyder/images/actions/redo.png + spyder/images/actions/reload.png + spyder/images/actions/rename.png + spyder/images/actions/replace.png + spyder/images/actions/show.png + spyder/images/actions/special_paste.png + spyder/images/actions/synchronize.png + spyder/images/actions/tooloptions.png + spyder/images/actions/undo.png + spyder/images/actions/up.png + spyder/images/actions/zoom_in.png + spyder/images/actions/zoom_out.png + spyder/images/console/cmdprompt_t.png + spyder/images/console/console.png + spyder/images/console/environ.png + spyder/images/console/history.png + spyder/images/console/history24.png + spyder/images/console/kill.png + spyder/images/console/prompt.png + spyder/images/console/restart.png + spyder/images/console/syspath.png + spyder/images/editor/bug.png + spyder/images/editor/close_panel.png + spyder/images/editor/comment.png + spyder/images/editor/error.png + spyder/images/editor/file.png + spyder/images/editor/fromcursor.png + spyder/images/editor/gotoline.png + spyder/images/editor/highlight.png + spyder/images/editor/horsplit.png + spyder/images/editor/indent.png + spyder/images/editor/last_edit_location.png + spyder/images/editor/newwindow.png + spyder/images/editor/next_cursor.png + spyder/images/editor/next_wng.png + spyder/images/editor/outline_explorer.png + spyder/images/editor/outline_explorer_vis.png + spyder/images/editor/prev_cursor.png + spyder/images/editor/prev_wng.png + spyder/images/editor/select.png + spyder/images/editor/selectall.png + spyder/images/editor/todo_list.png + spyder/images/editor/uncomment.png + spyder/images/editor/unindent.png + spyder/images/editor/versplit.png + spyder/images/editor/wng_list.png + spyder/images/file/fileclose.png + spyder/images/file/filecloseall.png + spyder/images/file/fileimport.png + spyder/images/file/filenew.png + spyder/images/file/fileopen.png + spyder/images/file/filesave.png + spyder/images/file/filesaveas.png + spyder/images/file/print.png + spyder/images/file/save_all.png + spyder/images/filetypes/bat.png + spyder/images/filetypes/bmp.png + spyder/images/filetypes/c.png + spyder/images/filetypes/cc.png + spyder/images/filetypes/cfg.png + spyder/images/filetypes/chm.png + spyder/images/filetypes/cl.png + spyder/images/filetypes/cmd.png + spyder/images/filetypes/cpp.png + spyder/images/filetypes/css.png + spyder/images/filetypes/cxx.png + spyder/images/filetypes/diff.png + spyder/images/filetypes/doc.png + spyder/images/filetypes/exe.png + spyder/images/filetypes/f.png + spyder/images/filetypes/f77.png + spyder/images/filetypes/f90.png + spyder/images/filetypes/gif.png + spyder/images/filetypes/h.png + spyder/images/filetypes/hh.png + spyder/images/filetypes/hpp.png + spyder/images/filetypes/htm.png + spyder/images/filetypes/html.png + spyder/images/filetypes/hxx.png + spyder/images/filetypes/inf.png + spyder/images/filetypes/ini.png + spyder/images/filetypes/jpeg.png + spyder/images/filetypes/jpg.png + spyder/images/filetypes/js.png + spyder/images/filetypes/log.png + spyder/images/filetypes/nt.png + spyder/images/filetypes/patch.png + spyder/images/filetypes/pdf.png + spyder/images/filetypes/png.png + spyder/images/filetypes/pps.png + spyder/images/filetypes/properties.png + spyder/images/filetypes/ps.png + spyder/images/filetypes/pxd.png + spyder/images/filetypes/pxi.png + spyder/images/filetypes/pyx.png + spyder/images/filetypes/rar.png + spyder/images/filetypes/readme.png + spyder/images/filetypes/reg.png + spyder/images/filetypes/rej.png + spyder/images/filetypes/session.png + spyder/images/filetypes/tar.png + spyder/images/filetypes/tex.png + spyder/images/filetypes/tgz.png + spyder/images/filetypes/tif.png + spyder/images/filetypes/tiff.png + spyder/images/filetypes/txt.png + spyder/images/filetypes/xls.png + spyder/images/filetypes/xml.png + spyder/images/filetypes/zip.png + spyder/images/projects/add_to_path.png + spyder/images/projects/folder.png + spyder/images/projects/package.png + spyder/images/projects/pp_folder.png + spyder/images/projects/pp_package.png + spyder/images/projects/pp_project.png + spyder/images/projects/project.png + spyder/images/projects/project_closed.png + spyder/images/projects/pythonpath.png + spyder/images/projects/remove_from_path.png + spyder/images/projects/show_all.png Copyright: © 2006-2007 Everaldo Coelho, Crystal Project License: LGPL-2 -Files: spyderlib/images/qt.png - spyderlib/images/qtassistant.png - spyderlib/images/qtdesigner.png - spyderlib/images/qtlinguist.png - spyderlib/images/filetypes/ts.png - spyderlib/images/filetypes/ui.png +Files: spyder/images/qt.png + spyder/images/qtassistant.png + spyder/images/qtdesigner.png + spyder/images/qtlinguist.png + spyder/images/filetypes/ts.png + spyder/images/filetypes/ui.png Copyright: © 2008-2009 Nokia Corporation and/or its subsidiary(-ies). © 1994-2008 Trolltech ASA. License: LGPL-2.1 -Files: spyderlib/images/pythonxy.png - spyderlib/images/vitables.png +Files: spyder/images/pythonxy.png + spyder/images/vitables.png Copyright: © 2011 Pierre Raybaut License: GPL-3 -Files: spyderlib/images/winpython.svg +Files: spyder/images/winpython.svg Copyright: © 2011 Pierre Raybaut License: Expat -Files: spyderlib/images/scipy.png +Files: spyder/images/scipy.png Copyright: 1999-2005 Travis Oliphant 2001-2002 Enthought, Inc. 2002 Eric Jones @@ -274,69 +261,66 @@ 2009 Yosef Meller License: BSD-3-clause -Files: spyderlib/images/actions/collapse.png - spyderlib/images/actions/collapse_selection.png - spyderlib/images/actions/expand.png - spyderlib/images/actions/expand_selection.png - spyderlib/images/actions/restore.png - spyderlib/images/editor/class.png - spyderlib/images/editor/convention.png - spyderlib/images/editor/todo.png - spyderlib/images/editor/warning.png - spyderlib/images/filetypes/po.png - spyderlib/images/filetypes/pot.png +Files: spyder/images/actions/collapse.png + spyder/images/actions/collapse_selection.png + spyder/images/actions/expand.png + spyder/images/actions/expand_selection.png + spyder/images/actions/restore.png + spyder/images/editor/class.png + spyder/images/editor/convention.png + spyder/images/editor/todo.png + spyder/images/editor/warning.png + spyder/images/filetypes/po.png + spyder/images/filetypes/pot.png Copyright: © 2013 FamFamFamSilk icon set 1.3 Mark James License: CC-BY -Files: spyderlib/images/actions/arrow-continue.png - spyderlib/images/actions/arrow-step-in.png - spyderlib/images/actions/arrow-step-out.png - spyderlib/images/actions/arrow-step-over.png - spyderlib/images/actions/maximize.png - spyderlib/images/actions/stop_debug.png - spyderlib/images/actions/unmaximize.png - spyderlib/images/actions/window_fullscreen.png - spyderlib/images/editor/breakpoint_big.png - spyderlib/images/editor/breakpoint_cond_big.png - spyderlib/images/editor/breakpoint_cond_small.png - spyderlib/images/editor/breakpoint_small.png - spyderlib/images/editor/debug.png - spyderlib/images/console/terminated.png - spyderlib/images/actions/stop.png +Files: spyder/images/actions/arrow-continue.png + spyder/images/actions/arrow-step-in.png + spyder/images/actions/arrow-step-out.png + spyder/images/actions/arrow-step-over.png + spyder/images/actions/maximize.png + spyder/images/actions/stop_debug.png + spyder/images/actions/unmaximize.png + spyder/images/actions/window_fullscreen.png + spyder/images/editor/breakpoint_big.png + spyder/images/editor/breakpoint_cond_big.png + spyder/images/editor/breakpoint_cond_small.png + spyder/images/editor/breakpoint_small.png + spyder/images/editor/debug.png + spyder/images/console/terminated.png + spyder/images/actions/stop.png Copyright: (C) Yusuke Kamiyamane Icons License: CC-BY - FIXME -Files: - spyderlib/images/editor/run.png - spyderlib/images/editor/run_again.png - spyderlib/images/editor/run_cell.png - spyderlib/images/editor/run_cell_advance.png - spyderlib/images/editor/run_selection.png - spyderlib/images/editor/run_settings.png - spyderlib/images/console/run_small.png +Files: spyder/images/editor/run.png + spyder/images/editor/run_again.png + spyder/images/editor/run_cell.png + spyder/images/editor/run_cell_advance.png + spyder/images/editor/run_selection.png + spyder/images/editor/run_settings.png + spyder/images/console/run_small.png Copyright: (C) The Oxygen icon theme License: CC-BY - FIXME -Files: spyderlib/images/console/python.png - spyderlib/images/console/python_t.png - spyderlib/images/filetypes/py.png - spyderlib/images/filetypes/pyc.png - spyderlib/images/filetypes/pyw.png +Files: spyder/images/console/python.png + spyder/images/console/python_t.png + spyder/images/filetypes/py.png + spyder/images/filetypes/pyc.png + spyder/images/filetypes/pyw.png Copyright: © 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Python Software Foundation License: PSFL -Files: spyderlib/images/filetypes/nsh.png - spyderlib/images/filetypes/nsi.png +Files: spyder/images/filetypes/nsh.png + spyder/images/filetypes/nsi.png Copyright: © 2013, NullSoft License: zlib -Files: spyderlib/images/projects/pydev.png +Files: spyder/images/projects/pydev.png Copyright: © 2013, Red Hat, Inc. and other License: EPL-1 -Files: spyderlib/widgets/dataframeeditor.py +Files: spyder/widgets/variableexplorer/dataframeeditor.py Copyright: © 2014 Spyder development team License: BSD-3-clause @@ -554,7 +538,7 @@ Free Software Foundation; version 2 of the License. . On Debian systems, the complete text of version 2 of the GNU Library - Public License can be found in `/usr/share/common-licenses/LGPL-2'. + General Public License can be found in `/usr/share/common-licenses/LGPL-2'. License: LGPL-2.1 This program is free software; you can redistribute it and/or modify it @@ -562,7 +546,7 @@ Free Software Foundation; version 2.1 of the License. . On Debian systems, the complete text of version 2.1 of the GNU Lesser - Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. + General Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. License: PSFL A. HISTORY OF THE SOFTWARE @@ -860,18 +844,6 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE -License: public-domain - This was created by unknown people across unknown aeons, and - changed and updated by lots of more people. It was sung by - medieval bards, and touched by that Shakespeare dude, and - possibly even Mary Queen of Scots. Who knows? Nobody knows, - not even the Shadow knows! There is no evil lurking in the - minds of anyone here. - . - This work is therefore not copyrighted by anyone, and is as - much in the public domain as anything can be. Truly, utterly, - totally. Be happy. - License: zlib The zlib/libpng License . @@ -886,4 +858,3 @@ 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. . 3. This notice may not be removed or altered from any source distribution. - diff -Nru spyder-2.3.8+dfsg1/debian/gbp.conf spyder-3.0.2+dfsg1/debian/gbp.conf --- spyder-2.3.8+dfsg1/debian/gbp.conf 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/gbp.conf 2016-10-30 18:22:44.000000000 +0000 @@ -1,2 +1,3 @@ [DEFAULT] debian-branch=master +upstream-branch=upstream \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/debian/patches/0001-fix-spyderlib-path.patch spyder-3.0.2+dfsg1/debian/patches/0001-fix-spyderlib-path.patch --- spyder-2.3.8+dfsg1/debian/patches/0001-fix-spyderlib-path.patch 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/patches/0001-fix-spyderlib-path.patch 2016-10-30 18:22:44.000000000 +0000 @@ -7,16 +7,17 @@ spyderlib/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) -diff --git a/spyderlib/__init__.py b/spyderlib/__init__.py +diff --git a/spyder/__init__.py b/spyder/__init__.py index 46da56e..cf4b33a 100644 ---- a/spyderlib/__init__.py -+++ b/spyderlib/__init__.py -@@ -35,6 +35,10 @@ __forum_url__ = 'http://groups.google.com/group/spyderlib' +--- a/spyder/__init__.py ++++ b/spyder/__init__.py +@@ -35,6 +35,11 @@ __forum_url__ = 'http://groups.google.com/group/spyderlib' # Dear (Debian, RPM, ...) package makers, please feel free to customize the # following path to module's data (images) and translations: DATAPATH = LOCALEPATH = DOCPATH = MATHJAXPATH = JQUERYPATH = '' -+DATAPATH = '/usr/share/spyderlib/images' -+DOCPATH = '/usr/share/doc/python-spyderlib-doc/html' ++DATAPATH = '/usr/share/spyder/images' ++LOCALEPATH = '/usr/share/spyder/locale' ++DOCPATH = '/usr/share/doc/spyder-doc/html' +MATHJAXPATH = '/usr/share/javascript/mathjax' +JQUERYPATH = '/usr/share/javascript/jquery' diff -Nru spyder-2.3.8+dfsg1/debian/python3-spyder.bug-control spyder-3.0.2+dfsg1/debian/python3-spyder.bug-control --- spyder-2.3.8+dfsg1/debian/python3-spyder.bug-control 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python3-spyder.bug-control 2016-10-30 18:22:44.000000000 +0000 @@ -0,0 +1 @@ +Report-with: python3-pyqt5 diff -Nru spyder-2.3.8+dfsg1/debian/python3-spyderlib.bug-control spyder-3.0.2+dfsg1/debian/python3-spyderlib.bug-control --- spyder-2.3.8+dfsg1/debian/python3-spyderlib.bug-control 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python3-spyderlib.bug-control 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Report-with: python3-qt4 diff -Nru spyder-2.3.8+dfsg1/debian/python3-spyderlib.pyremove spyder-3.0.2+dfsg1/debian/python3-spyderlib.pyremove --- spyder-2.3.8+dfsg1/debian/python3-spyderlib.pyremove 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python3-spyderlib.pyremove 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -/spyderlib/images/* -/spyderplugins/images/* diff -Nru spyder-2.3.8+dfsg1/debian/python3-spyder.pyremove spyder-3.0.2+dfsg1/debian/python3-spyder.pyremove --- spyder-2.3.8+dfsg1/debian/python3-spyder.pyremove 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python3-spyder.pyremove 2016-10-30 18:22:44.000000000 +0000 @@ -0,0 +1,2 @@ +spyder/images +spyder/locale diff -Nru spyder-2.3.8+dfsg1/debian/python-spyder.bug-control spyder-3.0.2+dfsg1/debian/python-spyder.bug-control --- spyder-2.3.8+dfsg1/debian/python-spyder.bug-control 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python-spyder.bug-control 2016-10-30 18:22:44.000000000 +0000 @@ -0,0 +1 @@ +Report-with: python-pyqt5 diff -Nru spyder-2.3.8+dfsg1/debian/python-spyderlib.bug-control spyder-3.0.2+dfsg1/debian/python-spyderlib.bug-control --- spyder-2.3.8+dfsg1/debian/python-spyderlib.bug-control 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python-spyderlib.bug-control 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Report-with: python-qt4 diff -Nru spyder-2.3.8+dfsg1/debian/python-spyderlib-doc.doc-base spyder-3.0.2+dfsg1/debian/python-spyderlib-doc.doc-base --- spyder-2.3.8+dfsg1/debian/python-spyderlib-doc.doc-base 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python-spyderlib-doc.doc-base 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -Document: python-spyderlib-manual -Title: spyderlib documentation manual -Author: pierre.raybaut@cea.fr -Abstract: python-spyderlib_ - Spyder (previously known as Pydee) is a free open-source Python - development environment providing MATLAB-like features in a simple - and light-weighted software. -Section: Programming/Python - -Format: HTML -Index: /usr/share/doc/python-spyderlib-doc/html/index.html -Files: /usr/share/doc/python-spyderlib-doc/html/* diff -Nru spyder-2.3.8+dfsg1/debian/python-spyderlib-doc.docs spyder-3.0.2+dfsg1/debian/python-spyderlib-doc.docs --- spyder-2.3.8+dfsg1/debian/python-spyderlib-doc.docs 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python-spyderlib-doc.docs 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -build/html \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/debian/python-spyderlib.preinst spyder-3.0.2+dfsg1/debian/python-spyderlib.preinst --- spyder-2.3.8+dfsg1/debian/python-spyderlib.preinst 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python-spyderlib.preinst 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -#!/bin/sh - -set -e - -if [ "$1" = "upgrade" ] && dpkg --compare-versions "$2" lt-nl 2.1.0-1; then - if [ -L /usr/share/doc/python-spyderlib/html ]; then - rm -f /usr/share/doc/python-spyderlib/html - fi -fi - -#DEBHELPER# \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/debian/python-spyderlib.pyremove spyder-3.0.2+dfsg1/debian/python-spyderlib.pyremove --- spyder-2.3.8+dfsg1/debian/python-spyderlib.pyremove 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python-spyderlib.pyremove 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -spyderlib/images -spyderplugins/images diff -Nru spyder-2.3.8+dfsg1/debian/python-spyder.preinst spyder-3.0.2+dfsg1/debian/python-spyder.preinst --- spyder-2.3.8+dfsg1/debian/python-spyder.preinst 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python-spyder.preinst 2016-10-30 18:22:44.000000000 +0000 @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +if [ "$1" = "upgrade" ] && dpkg --compare-versions "$2" lt-nl 2.1.0-1; then + if [ -L /usr/share/doc/python-spyderlib/html ]; then + rm -f /usr/share/doc/python-spyderlib/html + fi +fi + +#DEBHELPER# \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/debian/python-spyder.pyremove spyder-3.0.2+dfsg1/debian/python-spyder.pyremove --- spyder-2.3.8+dfsg1/debian/python-spyder.pyremove 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/python-spyder.pyremove 2016-10-30 18:22:44.000000000 +0000 @@ -0,0 +1,2 @@ +spyder/images +spyder/locale \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/debian/rules spyder-3.0.2+dfsg1/debian/rules --- spyder-2.3.8+dfsg1/debian/rules 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/rules 2016-10-30 18:22:44.000000000 +0000 @@ -1,14 +1,13 @@ #!/usr/bin/make -f # -*- makefile -*- -# used only until dh_python3 pyremove is backported for wheezy -PY3VERS := $(shell py3versions -vr) - # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 -export PYBUILD_NAME=spyderlib +export PYBUILD_NAME=spyder export PYBUILD_BUILD_ARGS=--no-doc export PYBUILD_INSTALL_ARGS=--no-doc +export PYBUILD_AFTER_INSTALL=rm -rf {destdir}/usr/bin/ {destdir}/usr/share + %: dh $@ --with python2,python3,sphinxdoc --buildsystem=pybuild @@ -20,27 +19,20 @@ override_dh_auto_install: dh_auto_install + # install the spyder-common files - dh_install -p spyder-common spyderlib/images usr/share/spyderlib - dh_install -p spyder-common spyderplugins/images usr/share/spyderplugins + dh_install -p spyder-common spyder/images usr/share/spyder + dh_install -p spyder-common spyder/locale usr/share/spyder + # install the spyder files - dh_install --sourcedir=$(CURDIR)/debian/python-spyderlib -p spyder usr/bin - dh_install --sourcedir=$(CURDIR)/debian/python-spyderlib -p spyder usr/share/applications/spyder.desktop - dh_install --sourcedir=$(CURDIR)/debian/python-spyderlib -p spyder usr/share/pixmaps/spyder.png - rm -rf $(CURDIR)/debian/python-spyderlib/usr/bin - rm -rf $(CURDIR)/debian/python-spyderlib/usr/share/applications - rm -rf $(CURDIR)/debian/python-spyderlib/usr/share/pixmaps - rm -f $(CURDIR)/debian/spyder/usr/bin/*.py + python setup.py install_scripts -d debian/spyder/usr/bin + python setup.py install_data -d debian/spyder/usr + rm -f debian/spyder/usr/bin/spyder_win_post_install.py + # install the spyder3 files - dh_install --sourcedir=$(CURDIR)/debian/python3-spyderlib -p spyder3 usr/bin - dh_install --sourcedir=$(CURDIR)/debian/python3-spyderlib -p spyder3 usr/share/applications/spyder3.desktop - dh_install --sourcedir=$(CURDIR)/debian/python3-spyderlib -p spyder3 usr/share/pixmaps/spyder3.png - rm -rf $(CURDIR)/debian/python3-spyderlib/usr/bin - rm -rf $(CURDIR)/debian/python3-spyderlib/usr/share/applications - rm -rf $(CURDIR)/debian/python3-spyderlib/usr/share/pixmaps - rm -f $(CURDIR)/debian/spyder3/usr/bin/*.py - # remove unwanted files until dh_python3 pyremove is backported to wheezy - for v in $(PY3VERS); do \ - rm -rf $(CURDIR)/debian/python3-spyderlib/usr/lib/python$$v/dist-packages/spyderlib/images ;\ - rm -rf $(CURDIR)/debian/python3-spyderlib/usr/lib/python$$v/dist-packages/spyderplugins/images ;\ - done + python3 setup.py install_scripts -d debian/spyder3/usr/bin + python3 setup.py install_data -d debian/spyder3/usr + rm -f debian/spyder3/usr/bin/spyder_win_post_install.py + +# skip tests for now +override_dh_auto_test: diff -Nru spyder-2.3.8+dfsg1/debian/spyder3.bug-control spyder-3.0.2+dfsg1/debian/spyder3.bug-control --- spyder-2.3.8+dfsg1/debian/spyder3.bug-control 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/spyder3.bug-control 2016-10-30 18:22:44.000000000 +0000 @@ -1 +1 @@ -Report-with: python3-spyderlib python3-qt4 +Report-with: python3-spyder python3-pyqt5 diff -Nru spyder-2.3.8+dfsg1/debian/spyder3.links spyder-3.0.2+dfsg1/debian/spyder3.links --- spyder-2.3.8+dfsg1/debian/spyder3.links 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/spyder3.links 2016-10-30 18:22:44.000000000 +0000 @@ -1 +1 @@ -/usr/share/doc/python-spyderlib-doc/html /usr/share/doc/spyder3/html +/usr/share/doc/spyder-doc/html /usr/share/doc/spyder3/html diff -Nru spyder-2.3.8+dfsg1/debian/spyder3.menu spyder-3.0.2+dfsg1/debian/spyder3.menu --- spyder-2.3.8+dfsg1/debian/spyder3.menu 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/spyder3.menu 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -?package(spyder3):needs="X11" section="Applications/Programming"\ - title="spyder3" command="/usr/bin/spyder3" diff -Nru spyder-2.3.8+dfsg1/debian/spyder.bug-control spyder-3.0.2+dfsg1/debian/spyder.bug-control --- spyder-2.3.8+dfsg1/debian/spyder.bug-control 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/spyder.bug-control 2016-10-30 18:22:44.000000000 +0000 @@ -1 +1 @@ -Report-with: python-spyderlib python-qt4 +Report-with: python-spyder python-pyqt5 diff -Nru spyder-2.3.8+dfsg1/debian/spyder-doc.doc-base spyder-3.0.2+dfsg1/debian/spyder-doc.doc-base --- spyder-2.3.8+dfsg1/debian/spyder-doc.doc-base 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/spyder-doc.doc-base 2016-10-30 18:22:44.000000000 +0000 @@ -0,0 +1,12 @@ +Document: spyder-manual +Title: spyder documentation manual +Author: pierre.raybaut@cea.fr +Abstract: spyder_ + Spyder (previously known as Pydee) is a free open-source Python + development environment providing MATLAB-like features in a simple + and light-weighted software. +Section: Programming/Python + +Format: HTML +Index: /usr/share/doc/spyder-doc/html/index.html +Files: /usr/share/doc/spyder-doc/html/* diff -Nru spyder-2.3.8+dfsg1/debian/spyder-doc.docs spyder-3.0.2+dfsg1/debian/spyder-doc.docs --- spyder-2.3.8+dfsg1/debian/spyder-doc.docs 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/spyder-doc.docs 2016-10-30 18:22:44.000000000 +0000 @@ -0,0 +1 @@ +build/html \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/debian/spyder.links spyder-3.0.2+dfsg1/debian/spyder.links --- spyder-2.3.8+dfsg1/debian/spyder.links 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/spyder.links 2016-10-30 18:22:44.000000000 +0000 @@ -1 +1 @@ -/usr/share/doc/python-spyderlib-doc/html /usr/share/doc/spyder/html +/usr/share/doc/spyder-doc/html /usr/share/doc/spyder/html diff -Nru spyder-2.3.8+dfsg1/debian/spyder.menu spyder-3.0.2+dfsg1/debian/spyder.menu --- spyder-2.3.8+dfsg1/debian/spyder.menu 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/spyder.menu 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -?package(spyder):needs="X11" section="Applications/Programming"\ - title="spyder" command="/usr/bin/spyder" diff -Nru spyder-2.3.8+dfsg1/debian/watch spyder-3.0.2+dfsg1/debian/watch --- spyder-2.3.8+dfsg1/debian/watch 2015-11-01 14:00:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/debian/watch 2016-10-30 18:22:44.000000000 +0000 @@ -1,5 +1,5 @@ version=3 opts=dversionmangle=s/\+(debian|dfsg|ds|deb)(\.\d+)?$//,\ repacksuffix=+dfsg1,\ -uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/,\ - https://github.com/spyder-ide/spyder/releases .*/spyder-(\d\S*)\.zip +uversionmangle=s/(rc|a|b|c)/~$1/ \ +https://pypi.debian.net/spyder/spyder-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) diff -Nru spyder-2.3.8+dfsg1/doc/conf.py spyder-3.0.2+dfsg1/doc/conf.py --- spyder-2.3.8+dfsg1/doc/conf.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/conf.py 2016-11-16 15:40:01.000000000 +0000 @@ -38,16 +38,16 @@ # General information about the project. project = 'Spyder' -copyright = '2009, Pierre Raybaut' +copyright = 'The Spyder Project Contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '2.3' +version = '3' # The full version, including alpha/beta/rc tags. -release = '2.3' +release = '3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -Nru spyder-2.3.8+dfsg1/doc/console.rst spyder-3.0.2+dfsg1/doc/console.rst --- spyder-2.3.8+dfsg1/doc/console.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/console.rst 2016-11-16 15:40:01.000000000 +0000 @@ -30,32 +30,7 @@ Related plugins: -* :doc:`inspector` +* :doc:`help` * :doc:`historylog` * :doc:`editor` -* :doc:`explorer` - - -Reloading modules: the User Module Deleter (UMD) ------------------------------------------------- - -When working with Python scripts interactively, one must keep in mind that -Python import a module from its source code (on disk) only when parsing the -first corresponding import statement. During this first import, the byte code -is generated (.pyc file) if necessary and the imported module code object is -cached in `sys.modules`. Then, when re-importing the same module, this cached -code object will be directly used even if the source code file (.py[w] file) -has changed meanwhile. - -This behavior is sometimes unexpected when working with the Python interpreter -in interactive mode, because one must either always restart the interpreter -or remove manually the .pyc files to be sure that changes made in imported -modules were taken into account. - -The User Module Deleter (UMD) is a Spyder console's exclusive feature that -forces the Python interpreter to reload modules completely when executing -a Python script. - -For example, when UMD is turned on, one may test complex applications -within the same Python interpreter without having to restart it every time -(restart time may be relatively long when testing GUI-based applications). \ No newline at end of file +* :doc:`fileexplorer` diff -Nru spyder-2.3.8+dfsg1/doc/editor.rst spyder-3.0.2+dfsg1/doc/editor.rst --- spyder-2.3.8+dfsg1/doc/editor.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/editor.rst 2016-11-16 15:40:01.000000000 +0000 @@ -9,15 +9,27 @@ Function/class/method browser: +| + .. image:: images/editor1.png + :align: center +| Code analysis with `pyflakes`: +| + .. image:: images/editor2.png + :align: center +| Horizontal/vertical splitting feature: +| + .. image:: images/editor3.png + :align: center +| How to define a code cell -------------------------- @@ -37,5 +49,5 @@ Related plugins: * :doc:`console` -* :doc:`explorer` +* :doc:`fileexplorer` * :doc:`findinfiles` diff -Nru spyder-2.3.8+dfsg1/doc/explorer.rst spyder-3.0.2+dfsg1/doc/explorer.rst --- spyder-2.3.8+dfsg1/doc/explorer.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/explorer.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -Explorer -======== - -The explorer plugin is a file/directory browser allowing the user to open files -with the internal editor or with the appropriate application (Windows only). - -.. image:: images/explorer.png - -Context menus may be used to run a script, open a terminal window or run a -Windows explorer window (Windows only): - -.. image:: images/explorer_menu1.png - -.. image:: images/explorer_menu2.png - - -Related plugins: - -* :doc:`console` -* :doc:`editor` diff -Nru spyder-2.3.8+dfsg1/doc/fileexplorer.rst spyder-3.0.2+dfsg1/doc/fileexplorer.rst --- spyder-2.3.8+dfsg1/doc/fileexplorer.rst 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/fileexplorer.rst 2016-11-16 15:40:01.000000000 +0000 @@ -0,0 +1,33 @@ +File Explorer +============= + +The file explorer pane is a file/directory browser allowing the user to open +files with the internal editor or with the appropriate application (Windows +only). + +| + +.. image:: images/explorer.png + :align: center + +| + +Context menus may be used to run a script, open a terminal window or run a +Windows explorer window (Windows only): + +| + +.. image:: images/explorer_menu1.png + :align: center + +| + +.. image:: images/explorer_menu2.png + :align: center + +| + +Related plugins: + +* :doc:`ipythonconsole` +* :doc:`editor` diff -Nru spyder-2.3.8+dfsg1/doc/help.rst spyder-3.0.2+dfsg1/doc/help.rst --- spyder-2.3.8+dfsg1/doc/help.rst 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/help.rst 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,34 @@ +Help +==== + +The help plugin works together with the :doc:`console` and the +:doc:`editor`: it shows automatically documentation available when the +user is instantiating a class or calling a function (pressing the left +parenthesis key after a valid function or class name triggers a call +in the help pane). + +Note that this automatic link may be disabled by pressing the "Lock" button +(at the top right corner of the window). + +Of course, one can use the documentation viewer directly by entering an object +name in the editable combo box field, or by selecting old documentation requests +in the combo box. + +Plain text mode: + +.. image:: images/help_plain.png + +Rich text mode: + +.. image:: images/help_rich.png + +Sometimes, when docstrings are not available or not sufficient to document the +object, the documentation viewer can show the source code (if available, i.e. +if the object is pure Python): + +.. image:: images/help_source.png + +Related plugins: + +* :doc:`console` +* :doc:`editor` Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/arrayeditor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/arrayeditor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/console.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/console.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/dicteditor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/dicteditor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/editor1.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/editor1.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/editor2.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/editor2.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/editor3.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/editor3.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/explorer_menu1.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/explorer_menu1.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/explorer_menu2.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/explorer_menu2.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/explorer.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/explorer.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/findinfiles.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/findinfiles.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/git_install_dialog.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/git_install_dialog.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/help_plain.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/help_plain.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/help_rich.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/help_rich.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/help_source.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/help_source.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/historylog.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/historylog.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/inspector_plain.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/inspector_plain.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/inspector_rich.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/inspector_rich.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/inspector_source.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/inspector_source.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/internalconsole.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/internalconsole.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/ipythonconsolemenu.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/ipythonconsolemenu.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/ipythonconsole.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/ipythonconsole.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/ipythonkernelconnect.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/ipythonkernelconnect.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/lightmode.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/lightmode.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/listeditor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/listeditor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/new_project.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/new_project.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/onlinehelp.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/onlinehelp.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/projectexplorer2.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/projectexplorer2.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/projectexplorer.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/projectexplorer.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/pylint.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/pylint.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/texteditor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/texteditor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/variableexplorer1.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/variableexplorer1.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/variableexplorer2.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/variableexplorer2.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/variableexplorer-imshow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/variableexplorer-imshow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/images/variableexplorer-plot.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/images/variableexplorer-plot.png differ diff -Nru spyder-2.3.8+dfsg1/doc/index.rst spyder-3.0.2+dfsg1/doc/index.rst --- spyder-2.3.8+dfsg1/doc/index.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/index.rst 2016-11-16 15:40:01.000000000 +0000 @@ -30,19 +30,18 @@ overview installation options - lightmode - console + editor ipythonconsole debugging + console variableexplorer - inspector - onlinehelp + help + projects + pylint + fileexplorer historylog - explorer - projectexplorer - editor findinfiles - pylint + onlinehelp internalconsole Indices and tables: diff -Nru spyder-2.3.8+dfsg1/doc/inspector.rst spyder-3.0.2+dfsg1/doc/inspector.rst --- spyder-2.3.8+dfsg1/doc/inspector.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/inspector.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -Object inspector -================ - -The object inspector plugin works together with the :doc:`console` and the -:doc:`editor`: it shows automatically documentation available when the -user is instantiating a class or calling a function (pressing the left -parenthesis key after a valid function or class name triggers the object -inspector). - -Note that this automatic link may be disabled by pressing the "Lock" button -(at the top right corner of the window). - -Of course, one can use the documentation viewer directly by entering an object -name in the editable combo box field, or by selecting old documentation requests -in the combo box. - -Plain text mode: - -.. image:: images/inspector_plain.png - -Rich text mode: - -.. image:: images/inspector_rich.png - -Sometimes, when docstrings are not available or not sufficient to document the -object, the documentation viewer can show the source code (if available, i.e. -if the object is pure Python): - -.. image:: images/inspector_source.png - -Related plugins: - -* :doc:`console` -* :doc:`editor` diff -Nru spyder-2.3.8+dfsg1/doc/installation.rst spyder-3.0.2+dfsg1/doc/installation.rst --- spyder-2.3.8+dfsg1/doc/installation.rst 2015-10-12 00:00:38.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/installation.rst 2016-11-16 15:40:01.000000000 +0000 @@ -27,19 +27,13 @@ #. Install the essential requirements: - * `The Python language `_ - * `PyQt4 `_ + * `The Python programming language `_ + * `PyQt5 `_ (recommended) + or `PyQt4 `_ -#. Install optional modules: +#. Install Spyder and its dependencies by running this command:: - Please refer to the `Recommended modules`_ section to see what other packages - you might need. - -#. Installing Spyder itself: - - You need to download and install the .exe file that corresponds to your Python - version and architecture from - `this page `_. + pip install spyder Updating Spyder @@ -47,11 +41,16 @@ You can update Spyder by: -* Updating Anaconda, WinPython, Python(x,y). +* Updating Anaconda, WinPython or Python(x,y). + +* Or using this command (in case you *don't* use any of those scientific + distributions):: + + pip install --upgrade spyder + + .. note:: -* Installing a new .exe file from the page mentioned above (this will automatically - uninstall any previous version *only if* this version was installed with the same - kind of installer - i.e. not with an .msi installer). + This command will also update all Spyder dependencies | @@ -99,21 +98,20 @@ Installing on Linux ------------------- -Please refer to the `Recommended modules`_ section to see what other packages you +Please refer to the `Requirements`_ section to see what other packages you might need. #. **Ubuntu**: - + * Using the official package manager: ``sudo apt-get install spyder``. - + .. note:: - + This package could be slightly outdated. If you find that is the case, please use the Debian package mentioned below. - + * Using the `pip `_ package manager: - - * Requirements: ``sudo apt-get install python-qt4 python-sphinx`` + * Installing: ``sudo pip install spyder`` * Updating: ``sudo pip install -U spyder`` @@ -151,69 +149,81 @@ Requirements ~~~~~~~~~~~~ -The minimal requirements to run Spyder are - -* `Python `_ 2.6+ - -* `PyQt4 `_ >= v4.6 or - `PySide `_ >=1.2.0 (PyQt4 is recommended). - +The requirements to run Spyder are: -Recommended modules -~~~~~~~~~~~~~~~~~~~ +* `Python `_ 2.7 or >=3.3 -We recommend you to install these modules to get the most out of Spyder: +* `PyQt5 `_ >=5.2 or + `PyQt4 `_ >=4.6.0 + (PyQt5 is recommended). -* `IPython `_ 3.0 or less, or - `qtconsole `_ 4.0 or higher -- for an +* `Qtconsole `_ >=4.2.0 -- for an enhanced Python interpreter. - - .. note:: - - - On *Ubuntu* you need to install ``ipython-qtconsole``. - - On *Fedora*, ``ipython-gui`` - - And on *Gentoo* ``ipython`` with the ``qt4`` USE flag - -* `sphinx `_ >= v0.6 -- for the Object Inspector's rich - text mode and to get our documentation. -* `rope `_ 0.9.x (x>=0) -- for code completion, +* `Rope `_ >=0.9.4 and + `Jedi ` 0.8.1 -- for code completion, go-to-definition and calltips on the Editor. -* `pyflakes `_ 0.x (x>=5) -- for real-time +* `Pyflakes `_ -- for real-time code analysis. -* `pylint `_ -- for static code analysis. +* `Sphinx `_ -- for the Help pane rich text mode + and to get our documentation. -* `pep8 `_ -- for style analysis. +* `Pygments `_ >=2.0 -- for syntax highlighting and code + completion in the Editor of all file types it supports. -* `numpy `_ -- for N-dimensional arrays. +* `Pylint `_ -- for static code analysis. -* `scipy `_ -- for signal and image processing. +* `Pep8 `_ -- for style analysis. -* `matplotlib `_ -- for 2D and 3D plotting. - -* `psutil `_ -- for memory/CPU usage in the status +* `Psutil `_ -- for memory/CPU usage in the status bar. +* `Nbconvert `_ -- to manipulate Jupyter notebooks + on the Editor. + +* `Qtawesome `_ -- for an icon theme based on + FontAwesome. + +* Pickleshare -- To show import completions on the Editor and Consoles. + +* `PyZMQ `_ -- To run introspection services on the + Editor asynchronously. + +* `QtPy `_ >=1.1.0 -- To run Spyder with PyQt4 or + PyQt5 seamlessly. + + +Optional modules +~~~~~~~~~~~~~~~~ + +* `Matplotlib `_ >=1.0 -- for 2D and 3D plotting + in the consoles. + +* `Pandas `_ >=0.13.1 -- for view and editing DataFrames + and Series in the Variable Explorer. + +* `Numpy `_ -- for view and editing two or three + dimensional arrays in the Variable Explorer. + +* `Sympy `_ >=0.7.3 -- for working with symbolic mathematics + in the IPython console. + +* `Scipy `_ -- for importing Matlab workspace files in + the Variable Explorer. + Installation procedure ~~~~~~~~~~~~~~~~~~~~~~ -#. Download and unzip the source package (spyder-*version*.zip): -#. Change your current directory to the unzipped directory -#. Run: +1. If you use Anaconda, you need to run this command to install Spyder: - * ``sudo python setup.py install``, on Linux or MacOS X, or - * ``python setup.py install``, on Windows. + ``conda install spyder`` - .. warning:: +2. If you don't use Anaconda, you need to run: - This procedure does *not* uninstall previous versions of Spyder, it simply - copies files on top of an existing installation. When using this command, - it is thus highly recommended to uninstall manually any previous version of - Spyder by removing the associated directories (``spyderlib`` and - ``spyderplugins`` in your site-packages directory). + ``pip install --upgrade spyder`` Run without installing @@ -236,6 +246,8 @@ If you want to try the next Spyder version, you have to: +#. Install Spyder `requirements`_ + #. Install `Git `_, a powerful source control management tool. diff -Nru spyder-2.3.8+dfsg1/doc/internalconsole.rst spyder-3.0.2+dfsg1/doc/internalconsole.rst --- spyder-2.3.8+dfsg1/doc/internalconsole.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/internalconsole.rst 2016-11-16 15:40:01.000000000 +0000 @@ -7,41 +7,13 @@ process as Spyder's, but the Internal Console may be executed in a separate thread (this is optional and for example this is not the case in Spyder itself). +| + .. image:: images/internalconsole.png + :align: center + +| The internal console support the following features: * Code completion and calltips -* User Module Deleter (as in :doc:`console`) - -Special commands ----------------- - -The following special commands are supported by the interactive console. - -- Edit script - - ``edit foobar.py`` will open ``foobar.py`` with Spyder's editor. - ``xedit foobar.py`` will open ``foobar.py`` with the external editor. - -- Execute script - - ``run foobar.py`` will execute ``foobar.py`` in interactive console. - -- Remove references - - ``clear x, y`` will remove references named ``x`` and ``y``. - -- Shell commands - - ``!cmd`` will execute system command ``cmd`` (example ``!ls`` on Linux or - ``!dir`` on Windows). - -- Python help - - ``object?`` will show ``object``'s help in documentation viewer. - -- GUI-based editor - - ``oedit(object)`` will open an appropriate GUI-based editor to modify object - ``object`` and will return the result. diff -Nru spyder-2.3.8+dfsg1/doc/ipythonconsole.rst spyder-3.0.2+dfsg1/doc/ipythonconsole.rst --- spyder-2.3.8+dfsg1/doc/ipythonconsole.rst 2015-10-12 00:00:38.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/ipythonconsole.rst 2016-11-16 15:40:01.000000000 +0000 @@ -7,19 +7,32 @@ back end. Visit the IPython project website for full documentation of IPython's many features. +| + .. image:: images/ipythonconsole.png + :align: center +| From the Consoles menu, Spyder can launch **IPython Console** instances that attach to kernels that are managed by Spyder itself or it can connect to external kernels that are managed by IPython Qt Console sessions or the IPython Notebook. +| + .. image:: images/ipythonconsolemenu.png + :align: center + +| When "Connect to an existing kernel" is selected, Spyder prompts for the kernel connection file details: +| + .. image:: images/ipythonkernelconnect.png + :align: center +| **IPython Consoles** that are attached to kernels that were created by Spyder support the following features: @@ -40,9 +53,34 @@ debugging step commands to the kernel. Breakpoints must be set manually from the console command line. + +Reloading modules: the User Module Reloader (UMR) +------------------------------------------------- + +When working with Python scripts interactively, one must keep in mind that +Python import a module from its source code (on disk) only when parsing the +first corresponding import statement. During this first import, the byte code +is generated (.pyc file) if necessary and the imported module code object is +cached in `sys.modules`. Then, when re-importing the same module, this cached +code object will be directly used even if the source code file (.py[w] file) +has changed meanwhile. + +This behavior is sometimes unexpected when working with the Python interpreter +in interactive mode, because one must either always restart the interpreter +or remove manually the .pyc files to be sure that changes made in imported +modules were taken into account. + +The User Module Reloader (UMR) is a Spyder console's exclusive feature that +forces the Python interpreter to reload modules completely when executing +a Python script. + +For example, when UMR is turned on, one may test complex applications +within the same Python interpreter without having to restart it every time +(restart time may be relatively long when testing GUI-based applications). + + Related plugins: -* :doc:`inspector` +* :doc:`help` * :doc:`editor` -* :doc:`explorer` - +* :doc:`fileexplorer` diff -Nru spyder-2.3.8+dfsg1/doc/lightmode.rst spyder-3.0.2+dfsg1/doc/lightmode.rst --- spyder-2.3.8+dfsg1/doc/lightmode.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/lightmode.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -Light mode -========== - -Spyder may be started in *light mode* with the following command: - - ``python spyder.py --light`` - -or - - ``python spyder.py -l`` - - -The light mode is a very simple and light environment with the :doc:`console` -and the :doc:`variableexplorer`. - -.. image:: images/lightmode.png - -Related plugins: - -* :doc:`console` -* :doc:`variableexplorer` - diff -Nru spyder-2.3.8+dfsg1/doc/onlinehelp.rst spyder-3.0.2+dfsg1/doc/onlinehelp.rst --- spyder-2.3.8+dfsg1/doc/onlinehelp.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/onlinehelp.rst 2016-10-25 00:05:22.000000000 +0000 @@ -9,4 +9,4 @@ Related plugins: -* :doc:`inspector` +* :doc:`help` diff -Nru spyder-2.3.8+dfsg1/doc/options.rst spyder-3.0.2+dfsg1/doc/options.rst --- spyder-2.3.8+dfsg1/doc/options.rst 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/options.rst 2016-10-25 00:05:22.000000000 +0000 @@ -2,21 +2,21 @@ ==================== Spyder's command line options are the following: -(type 'python spyder.py --help' to show the text below) Options: -h, --help show this help message and exit - -l, --light Light version (all add-ons are disabled) - --session=STARTUP_SESSION - Startup session - --defaults Reset to configuration settings to defaults + --new-instance Run a new instance of Spyder, even if the single + instance mode has been turned on (default) + --defaults Reset configuration settings to defaults --reset Remove all configuration files! --optimize Optimize Spyder bytecode (this may require administrative privileges) -w WORKING_DIRECTORY, --workdir=WORKING_DIRECTORY Default working directory - -d, --debug Debug mode (stds are not redirected) + --show-console Do not hide parent console window (Windows) --multithread Internal console is executed in another thread (separate from main application thread) --profile Profile mode (internal test, not related with Python - profiling) \ No newline at end of file + profiling) + --window-title=WINDOW_TITLE + String to show in the main window title diff -Nru spyder-2.3.8+dfsg1/doc/overview.rst spyder-3.0.2+dfsg1/doc/overview.rst --- spyder-2.3.8+dfsg1/doc/overview.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/overview.rst 2016-11-16 15:40:01.000000000 +0000 @@ -16,7 +16,7 @@ * *preferences* dialog box: * keyboard shortcuts - * syntax coloring schemes (source editor, history log, object inspector) + * syntax coloring schemes (source editor, history log, help) * console: background color (black/white), automatic code completion, etc. * and a lot more... @@ -39,7 +39,7 @@ * *calltips* * *go-to-definition*: go to object (any symbol: function, class, attribute, etc.) definition by pressing Ctrl+Left mouse click on word or Ctrl+G (default shortcut) - * *occurence highlighting* + * *occurrence highlighting* * typing helpers (optional): * automatically insert closing parentheses, braces and brackets @@ -47,13 +47,13 @@ * *to-do* lists (TODO, FIXME, XXX) * errors/warnings (real-time *code analysis* provided by `pyflakes`) - * integrated *`pylint` code analysis* + * integrated static code analysis (using `pylint`) * direct link to `winpdb` external debugger * :doc:`console`: * *all consoles are executed in a separate process* - * *code completion*/calltips and automatic link to object inspector (see below) + * *code completion*/calltips and automatic link to help (see below) * open Python interpreters or basic terminal command windows * run Python scripts (see source editor features) * *variable explorer*: @@ -64,17 +64,17 @@ * data visualization * :doc:`historylog` -* :doc:`inspector`: +* :doc:`help`: * provide documentation or source code on any Python object (class, function, module, ...) * documentation may be displayed as an html page thanks to the rich text mode (powered by `sphinx`) * :doc:`onlinehelp`: automatically generated html documentation on installed Python modules -* :doc:`findinfiles`: find string occurences in a directory, a mercurial repository or directly in PYTHONPATH (support for regular expressions and included/excluded string lists) -* :doc:`explorer` -* :doc:`projectexplorer` (support Pydev project import) +* :doc:`findinfiles`: find string occurrences in a directory, a mercurial repository or directly in PYTHONPATH (support for regular expressions and included/excluded string lists) +* :doc:`fileexplorer` +* :doc:`projects` -Spyder may also be used as a PyQt4 or PySide extension library -(module 'spyderlib'). For example, the Python interactive shell widget -used in Spyder may be embedded in your own PyQt4 or PySide application. +Spyder may also be used as a PyQt5 or PyQt4 extension library +(module 'spyder'). For example, the Python interactive shell widget +used in Spyder may be embedded in your own PyQt5 or PyQt4 application. diff -Nru spyder-2.3.8+dfsg1/doc/projectexplorer.rst spyder-3.0.2+dfsg1/doc/projectexplorer.rst --- spyder-2.3.8+dfsg1/doc/projectexplorer.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/projectexplorer.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -Project Explorer -================ - -The project explorer plugin handles project management in Spyder with the -following main features: - -* import from existing Pydev (Eclipse) or Spyder projects -* add/remove project folders to/from Spyder's PYTHONPATH directly from - the context menu or manage these folders in a dedicated dialog box -* multiple file selection (for all available actions: open, rename, delete, - and so on) -* file type filters - -.. image:: images/projectexplorer.png - -.. image:: images/projectexplorer2.png - -Version Control Integration ---------------------------- - -Spyder has limited integration with Mercurial_ and Git_. Commit and browse -commands are available by right-clicking on relevant files that reside within -an already initialized repository. These menu picks -assume that certain commands are available on the system path. - -* For Mercurial repositories, TortoiseHG_ must be installed, and either ``thg`` - or ``hgtk`` must be on the system path. -* For git repositories, the commands ``git`` and ``gitk`` must be on the - system path. For Windows systems, the msysgit_ package provides a convenient - installer and the option to place common git commands on the system path without - creating conflicts with Windows system tools. - The second option in the dialog below is generally a safe approach. - -.. image:: images/git_install_dialog.png - -.. _Git: http://git-scm.com/ -.. _Mercurial: http://mercurial.selenic.com/ -.. _TortoiseHg: http://tortoisehg.bitbucket.org/ -.. _msysgit: https://code.google.com/p/msysgit/ diff -Nru spyder-2.3.8+dfsg1/doc/projects.rst spyder-3.0.2+dfsg1/doc/projects.rst --- spyder-2.3.8+dfsg1/doc/projects.rst 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/projects.rst 2016-11-16 15:40:01.000000000 +0000 @@ -0,0 +1,69 @@ +Projects +======== + +Spyder allows users to associate a given directory with a project. This has two +main advantages: + +1. Projects remember the list of open files in Editor. This permits to easily + work on several coding efforts at the same time. +2. The project's path is added to the list of paths Python looks modules for, so + that modules developed as part of a project can be easily imported in any + console. + +To create a project, it is necessary to select the *New Project* entry from the +*Projects* menu: + +| + +.. image:: images/new_project.png + :align: center + +| + +When a project is activated, the *Project explorer* pane is shown, which +presents a tree view structure of the current project + +| + +.. image:: images/projectexplorer.png + :align: center + +| + +Through this pane it is possible to make several operations on the files that +belong to project + +| + +.. image:: images/projectexplorer2.png + :align: center + +| + +.. note:: Projects are completely optional and not imposed on users, i.e. users + can work without creating any project. + + +Version Control Integration +--------------------------- + +Spyder has limited integration with Git_ and Mercurial_. Commit and browse +commands are available by right-clicking on relevant files that reside within +an already initialized repository. This menu assume that certain commands are +available on the system path. + +* For Mercurial repositories, TortoiseHG_ must be installed, and either ``thg`` + or ``hgtk`` must be on the system path. +* For git repositories, the commands ``git`` and ``gitk`` must be on the + system path. For Windows systems, the `Git for Windows`_ package provides a + convenient installer and the option to place common git commands on the + system path without creating conflicts with Windows system tools. + The second option in the dialog below is generally a safe approach. + +.. image:: images/git_install_dialog.png + :align: center + +.. _Git: http://git-scm.com/ +.. _Mercurial: http://mercurial.selenic.com/ +.. _TortoiseHg: http://tortoisehg.bitbucket.org/ +.. _Git for Windows: https://git-for-windows.github.io/ diff -Nru spyder-2.3.8+dfsg1/doc/pylint.rst spyder-3.0.2+dfsg1/doc/pylint.rst --- spyder-2.3.8+dfsg1/doc/pylint.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/pylint.rst 2016-11-16 15:40:01.000000000 +0000 @@ -1,12 +1,17 @@ -Pylint extension -================ +Static code analysis +==================== -Pylint extension may be used directly from the :doc:`editor`, or by entering -manually the Python module or package path - i.e. it works either with `.py` -(or `.pyw`) Python scripts or with whole Python packages (directories containing -an `__init__.py` script). +The static code analysis tool may be used directly from the :doc:`editor`, or +by entering manually the Python module or package path - i.e. it works either +with `.py` (or `.pyw`) Python scripts or with whole Python packages +(directories containing an `__init__.py` script). + +| .. image:: images/pylint.png + :align: center + +| Related plugins: Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/doc/spyder_bbg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/doc/spyder_bbg.png differ diff -Nru spyder-2.3.8+dfsg1/doc/variableexplorer.rst spyder-3.0.2+dfsg1/doc/variableexplorer.rst --- spyder-2.3.8+dfsg1/doc/variableexplorer.rst 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/doc/variableexplorer.rst 2016-11-16 15:40:01.000000000 +0000 @@ -1,40 +1,51 @@ Variable Explorer ================= -The variable explorer shows the `globals()` namespace contents (i.e. all global -object references) of the current console: it supports both the :doc:`console` -(Python interpreter running in a remote process) -and the :doc:`internalconsole`. +The variable explorer shows the namespace contents (i.e. all global object +references) of the current console + +| .. image:: images/variableexplorer1.png + :align: center + +| The following screenshots show some interesting features such as editing lists, strings, dictionaries, NumPy arrays, or plotting/showing NumPy arrays data. +| + .. image:: images/listeditor.png + :align: center + +| .. image:: images/texteditor.png + :align: center + +| .. image:: images/dicteditor.png + :align: center + +| .. image:: images/arrayeditor.png + :align: center -.. image:: images/variableexplorer-plot.png +| -.. image:: images/variableexplorer-imshow.png +.. image:: images/variableexplorer-plot.png + :align: center -The default variable explorer configuration allows to browse global variables -without slowing the console even with very large NumPy arrays, lists or -dictionaries. The trick is to truncate values, to hide collection contents -(i.e. showing '' instead of list contents) and to *not* show -mininum and maximum values for NumPy arrays (see context menu options on the -screenshot at the top of this page). +| -However, most of the time, choosing the opposite options won't have too much -effect on console's performance: +.. image:: images/variableexplorer-imshow.png + :align: center -.. image:: images/variableexplorer2.png +| Supported types @@ -57,5 +68,4 @@ Related plugins: -* :doc:`console` -* :doc:`internalconsole` +* :doc:`ipythonconsole` Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/img_src/spyder3.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/img_src/spyder3.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/img_src/spyder_light.ico and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/img_src/spyder_light.ico differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/img_src/spyder.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/img_src/spyder.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/img_src/spyder_reset.ico and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/img_src/spyder_reset.ico differ diff -Nru spyder-2.3.8+dfsg1/LICENSE spyder-3.0.2+dfsg1/LICENSE --- spyder-2.3.8+dfsg1/LICENSE 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/LICENSE 2016-10-25 00:05:22.000000000 +0000 @@ -1,7 +1,7 @@ Spyder License Agreement (MIT License) -------------------------------------- -Copyright (c) 2009-2013 Pierre Raybaut +Copyright (c) Spyder Project Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -31,35 +31,38 @@ License terms are included in any software used inside Spyder's source code. -Included images (spyderlib/images) +Included images (spyder/images) ---------------------------------- [1] Spyder License Agreement (see above) -spyderlib/images/arredit.png -spyderlib/images/dictedit.png -spyderlib/images/none.png -spyderlib/images/not_found.png -spyderlib/images/matplotlib.png -spyderlib/images/options.svg -spyderlib/images/set_workdir.png -spyderlib/images/splash.png -spyderlib/images/spyder.svg -spyderlib/images/spyder_light.svg -spyderlib/images/whole_words.png -spyderlib/images/win_env.png -spyderlib/images/actions/hist.png -spyderlib/images/actions/window_nofullscreen.png -spyderlib/images/editor/filelist.png -spyderlib/images/editor/function.png -spyderlib/images/editor/method.png -spyderlib/images/editor/private1.png -spyderlib/images/editor/private2.png -spyderlib/images/editor/cell.png -spyderlib/images/editor/blockcomment.png -spyderlib/images/editor/refactor.png -spyderlib/images/console/ipython_console.png -spyderlib/images/console/ipython_console_t.png +spyder/images/arredit.png +spyder/images/dictedit.png +spyder/images/none.png +spyder/images/not_found.png +spyder/images/matplotlib.png +spyder/images/options.svg +spyder/images/set_workdir.png +spyder/images/splash.png +spyder/images/spyder.svg +spyder/images/spyder_light.svg +spyder/images/whole_words.png +spyder/images/win_env.png +spyder/images/actions/hist.png +spyder/images/actions/window_nofullscreen.png +spyder/images/editor/attribute.png +spyder/images/editor/filelist.png +spyder/images/editor/function.png +spyder/images/editor/method.png +spyder/images/editor/module.png +spyder/images/editor/no_match.png +spyder/images/editor/private1.png +spyder/images/editor/private2.png +spyder/images/editor/cell.png +spyder/images/editor/blockcomment.png +spyder/images/editor/refactor.png +spyder/images/console/ipython_console.png +spyder/images/console/ipython_console_t.png [2] Crystal Project Icons @@ -68,276 +71,276 @@ Contact: everaldo@everaldo.com Copyright (c) 2006-2007 Everaldo Coelho. -spyderlib/images/advanced.png -spyderlib/images/arrow.png -spyderlib/images/bold.png -spyderlib/images/browser.png -spyderlib/images/ext_tools.png -spyderlib/images/font.png -spyderlib/images/genprefs.png -spyderlib/images/inspector.png -spyderlib/images/italic.png -spyderlib/images/pythonpath_mgr.png -spyderlib/images/upper_lower.png -spyderlib/images/vcs_browse.png -spyderlib/images/vcs_commit.png -spyderlib/images/actions/1downarrow.png -spyderlib/images/actions/1uparrow.png -spyderlib/images/actions/2downarrow.png -spyderlib/images/actions/2uparrow.png -spyderlib/images/actions/auto_reload.png -spyderlib/images/actions/browse_tab.png -spyderlib/images/actions/check.png -spyderlib/images/actions/cmdprompt.png -spyderlib/images/actions/configure.png -spyderlib/images/actions/copywop.png -spyderlib/images/actions/delete.png -spyderlib/images/actions/edit.png -spyderlib/images/actions/edit24.png -spyderlib/images/actions/editcopy.png -spyderlib/images/actions/editcut.png -spyderlib/images/actions/editdelete.png -spyderlib/images/actions/editpaste.png -spyderlib/images/actions/edit_add.png -spyderlib/images/actions/edit_remove.png -spyderlib/images/actions/eraser.png -spyderlib/images/actions/exit.png -spyderlib/images/actions/filter.png -spyderlib/images/actions/find.png -spyderlib/images/actions/findf.png -spyderlib/images/actions/findnext.png -spyderlib/images/actions/findprevious.png -spyderlib/images/actions/folder_new.png -spyderlib/images/actions/hide.png -spyderlib/images/actions/home.png -spyderlib/images/actions/imshow.png -spyderlib/images/actions/insert.png -spyderlib/images/actions/lock.png -spyderlib/images/actions/lock_open.png -spyderlib/images/actions/magnifier.png -spyderlib/images/actions/next.png -spyderlib/images/actions/options_less.png -spyderlib/images/actions/options_more.png -spyderlib/images/actions/plot.png -spyderlib/images/actions/previous.png -spyderlib/images/actions/redo.png -spyderlib/images/actions/reload.png -spyderlib/images/actions/rename.png -spyderlib/images/actions/replace.png -spyderlib/images/actions/show.png -spyderlib/images/actions/special_paste.png -spyderlib/images/actions/synchronize.png -spyderlib/images/actions/tooloptions.png -spyderlib/images/actions/undo.png -spyderlib/images/actions/up.png -spyderlib/images/actions/zoom_in.png -spyderlib/images/actions/zoom_out.png -spyderlib/images/console/clear.png -spyderlib/images/console/cmdprompt_t.png -spyderlib/images/console/console.png -spyderlib/images/console/environ.png -spyderlib/images/console/history.png -spyderlib/images/console/history24.png -spyderlib/images/console/kill.png -spyderlib/images/console/prompt.png -spyderlib/images/console/restart.png -spyderlib/images/console/syspath.png -spyderlib/images/editor/bug.png -spyderlib/images/editor/close_panel.png -spyderlib/images/editor/comment.png -spyderlib/images/editor/error.png -spyderlib/images/editor/file.png -spyderlib/images/editor/fromcursor.png -spyderlib/images/editor/gotoline.png -spyderlib/images/editor/highlight.png -spyderlib/images/editor/horsplit.png -spyderlib/images/editor/indent.png -spyderlib/images/editor/last_edit_location.png -spyderlib/images/editor/newwindow.png -spyderlib/images/editor/next_cursor.png -spyderlib/images/editor/next_wng.png -spyderlib/images/editor/outline_explorer.png -spyderlib/images/editor/outline_explorer_vis.png -spyderlib/images/editor/prev_cursor.png -spyderlib/images/editor/prev_wng.png -spyderlib/images/editor/select.png -spyderlib/images/editor/selectall.png -spyderlib/images/editor/todo_list.png -spyderlib/images/editor/uncomment.png -spyderlib/images/editor/unindent.png -spyderlib/images/editor/versplit.png -spyderlib/images/editor/wng_list.png -spyderlib/images/file/fileclose.png -spyderlib/images/file/filecloseall.png -spyderlib/images/file/fileimport.png -spyderlib/images/file/filenew.png -spyderlib/images/file/fileopen.png -spyderlib/images/file/filesave.png -spyderlib/images/file/filesaveas.png -spyderlib/images/file/print.png -spyderlib/images/file/save_all.png -spyderlib/images/filetypes/bat.png -spyderlib/images/filetypes/bmp.png -spyderlib/images/filetypes/c.png -spyderlib/images/filetypes/cc.png -spyderlib/images/filetypes/cfg.png -spyderlib/images/filetypes/chm.png -spyderlib/images/filetypes/cl.png -spyderlib/images/filetypes/cmd.png -spyderlib/images/filetypes/cpp.png -spyderlib/images/filetypes/css.png -spyderlib/images/filetypes/cxx.png -spyderlib/images/filetypes/diff.png -spyderlib/images/filetypes/doc.png -spyderlib/images/filetypes/exe.png -spyderlib/images/filetypes/f.png -spyderlib/images/filetypes/f77.png -spyderlib/images/filetypes/f90.png -spyderlib/images/filetypes/gif.png -spyderlib/images/filetypes/h.png -spyderlib/images/filetypes/hh.png -spyderlib/images/filetypes/hpp.png -spyderlib/images/filetypes/htm.png -spyderlib/images/filetypes/html.png -spyderlib/images/filetypes/hxx.png -spyderlib/images/filetypes/inf.png -spyderlib/images/filetypes/ini.png -spyderlib/images/filetypes/jpeg.png -spyderlib/images/filetypes/jpg.png -spyderlib/images/filetypes/js.png -spyderlib/images/filetypes/log.png -spyderlib/images/filetypes/nt.png -spyderlib/images/filetypes/patch.png -spyderlib/images/filetypes/pdf.png -spyderlib/images/filetypes/png.png -spyderlib/images/filetypes/pps.png -spyderlib/images/filetypes/properties.png -spyderlib/images/filetypes/ps.png -spyderlib/images/filetypes/pxd.png -spyderlib/images/filetypes/pxi.png -spyderlib/images/filetypes/pyx.png -spyderlib/images/filetypes/rar.png -spyderlib/images/filetypes/readme.png -spyderlib/images/filetypes/reg.png -spyderlib/images/filetypes/rej.png -spyderlib/images/filetypes/session.png -spyderlib/images/filetypes/tar.png -spyderlib/images/filetypes/tex.png -spyderlib/images/filetypes/tgz.png -spyderlib/images/filetypes/tif.png -spyderlib/images/filetypes/tiff.png -spyderlib/images/filetypes/txt.png -spyderlib/images/filetypes/xls.png -spyderlib/images/filetypes/xml.png -spyderlib/images/filetypes/zip.png -spyderlib/images/projects/add_to_path.png -spyderlib/images/projects/folder.png -spyderlib/images/projects/package.png -spyderlib/images/projects/pp_folder.png -spyderlib/images/projects/pp_package.png -spyderlib/images/projects/pp_project.png -spyderlib/images/projects/project.png -spyderlib/images/projects/project_closed.png -spyderlib/images/projects/pythonpath.png -spyderlib/images/projects/remove_from_path.png -spyderlib/images/projects/show_all.png +spyder/images/advanced.png +spyder/images/arrow.png +spyder/images/bold.png +spyder/images/browser.png +spyder/images/ext_tools.png +spyder/images/font.png +spyder/images/genprefs.png +spyder/images/help.png +spyder/images/italic.png +spyder/images/pythonpath_mgr.png +spyder/images/upper_lower.png +spyder/images/vcs_browse.png +spyder/images/vcs_commit.png +spyder/images/actions/1downarrow.png +spyder/images/actions/1uparrow.png +spyder/images/actions/2downarrow.png +spyder/images/actions/2uparrow.png +spyder/images/actions/auto_reload.png +spyder/images/actions/browse_tab.png +spyder/images/actions/check.png +spyder/images/actions/cmdprompt.png +spyder/images/actions/configure.png +spyder/images/actions/copywop.png +spyder/images/actions/delete.png +spyder/images/actions/edit.png +spyder/images/actions/edit24.png +spyder/images/actions/editcopy.png +spyder/images/actions/editcut.png +spyder/images/actions/editdelete.png +spyder/images/actions/editpaste.png +spyder/images/actions/edit_add.png +spyder/images/actions/edit_remove.png +spyder/images/actions/eraser.png +spyder/images/actions/exit.png +spyder/images/actions/filter.png +spyder/images/actions/find.png +spyder/images/actions/findf.png +spyder/images/actions/findnext.png +spyder/images/actions/findprevious.png +spyder/images/actions/folder_new.png +spyder/images/actions/hide.png +spyder/images/actions/home.png +spyder/images/actions/imshow.png +spyder/images/actions/insert.png +spyder/images/actions/lock.png +spyder/images/actions/lock_open.png +spyder/images/actions/magnifier.png +spyder/images/actions/next.png +spyder/images/actions/options_less.png +spyder/images/actions/options_more.png +spyder/images/actions/plot.png +spyder/images/actions/previous.png +spyder/images/actions/redo.png +spyder/images/actions/reload.png +spyder/images/actions/rename.png +spyder/images/actions/replace.png +spyder/images/actions/show.png +spyder/images/actions/special_paste.png +spyder/images/actions/synchronize.png +spyder/images/actions/tooloptions.png +spyder/images/actions/undo.png +spyder/images/actions/up.png +spyder/images/actions/zoom_in.png +spyder/images/actions/zoom_out.png +spyder/images/console/clear.png +spyder/images/console/cmdprompt_t.png +spyder/images/console/console.png +spyder/images/console/environ.png +spyder/images/console/history.png +spyder/images/console/history24.png +spyder/images/console/kill.png +spyder/images/console/prompt.png +spyder/images/console/restart.png +spyder/images/console/syspath.png +spyder/images/editor/bug.png +spyder/images/editor/close_panel.png +spyder/images/editor/comment.png +spyder/images/editor/error.png +spyder/images/editor/file.png +spyder/images/editor/fromcursor.png +spyder/images/editor/gotoline.png +spyder/images/editor/highlight.png +spyder/images/editor/horsplit.png +spyder/images/editor/indent.png +spyder/images/editor/last_edit_location.png +spyder/images/editor/newwindow.png +spyder/images/editor/next_cursor.png +spyder/images/editor/next_wng.png +spyder/images/editor/outline_explorer.png +spyder/images/editor/outline_explorer_vis.png +spyder/images/editor/prev_cursor.png +spyder/images/editor/prev_wng.png +spyder/images/editor/select.png +spyder/images/editor/selectall.png +spyder/images/editor/todo_list.png +spyder/images/editor/uncomment.png +spyder/images/editor/unindent.png +spyder/images/editor/versplit.png +spyder/images/editor/wng_list.png +spyder/images/file/fileclose.png +spyder/images/file/filecloseall.png +spyder/images/file/fileimport.png +spyder/images/file/filenew.png +spyder/images/file/fileopen.png +spyder/images/file/filesave.png +spyder/images/file/filesaveas.png +spyder/images/file/print.png +spyder/images/file/save_all.png +spyder/images/filetypes/bat.png +spyder/images/filetypes/bmp.png +spyder/images/filetypes/c.png +spyder/images/filetypes/cc.png +spyder/images/filetypes/cfg.png +spyder/images/filetypes/chm.png +spyder/images/filetypes/cl.png +spyder/images/filetypes/cmd.png +spyder/images/filetypes/cpp.png +spyder/images/filetypes/css.png +spyder/images/filetypes/cxx.png +spyder/images/filetypes/diff.png +spyder/images/filetypes/doc.png +spyder/images/filetypes/exe.png +spyder/images/filetypes/f.png +spyder/images/filetypes/f77.png +spyder/images/filetypes/f90.png +spyder/images/filetypes/gif.png +spyder/images/filetypes/h.png +spyder/images/filetypes/hh.png +spyder/images/filetypes/hpp.png +spyder/images/filetypes/htm.png +spyder/images/filetypes/html.png +spyder/images/filetypes/hxx.png +spyder/images/filetypes/inf.png +spyder/images/filetypes/ini.png +spyder/images/filetypes/jpeg.png +spyder/images/filetypes/jpg.png +spyder/images/filetypes/js.png +spyder/images/filetypes/log.png +spyder/images/filetypes/nt.png +spyder/images/filetypes/patch.png +spyder/images/filetypes/pdf.png +spyder/images/filetypes/png.png +spyder/images/filetypes/pps.png +spyder/images/filetypes/properties.png +spyder/images/filetypes/ps.png +spyder/images/filetypes/pxd.png +spyder/images/filetypes/pxi.png +spyder/images/filetypes/pyx.png +spyder/images/filetypes/rar.png +spyder/images/filetypes/readme.png +spyder/images/filetypes/reg.png +spyder/images/filetypes/rej.png +spyder/images/filetypes/session.png +spyder/images/filetypes/tar.png +spyder/images/filetypes/tex.png +spyder/images/filetypes/tgz.png +spyder/images/filetypes/tif.png +spyder/images/filetypes/tiff.png +spyder/images/filetypes/txt.png +spyder/images/filetypes/xls.png +spyder/images/filetypes/xml.png +spyder/images/filetypes/zip.png +spyder/images/projects/add_to_path.png +spyder/images/projects/folder.png +spyder/images/projects/package.png +spyder/images/projects/pp_folder.png +spyder/images/projects/pp_package.png +spyder/images/projects/pp_project.png +spyder/images/projects/project.png +spyder/images/projects/project_closed.png +spyder/images/projects/pythonpath.png +spyder/images/projects/remove_from_path.png +spyder/images/projects/show_all.png [3] GNU Lesser General Public License (Version 2.1) -spyderlib/images/qt.png -spyderlib/images/qtassistant.png -spyderlib/images/qtdesigner.png -spyderlib/images/qtlinguist.png -spyderlib/images/filetypes/ts.png -spyderlib/images/filetypes/ui.png +spyder/images/qt.png +spyder/images/qtassistant.png +spyder/images/qtdesigner.png +spyder/images/qtlinguist.png +spyder/images/filetypes/ts.png +spyder/images/filetypes/ui.png [4] GNU General Public License version 3 -spyderlib/images/pythonxy.png -spyderlib/images/vitables.png +spyder/images/pythonxy.png +spyder/images/vitables.png [5] MIT License -spyderlib/images/winpython.svg -spyderlib/images/chevron-left.png -spyderlib/images/chevron-right.png +spyder/images/winpython.svg +spyder/images/chevron-left.png +spyder/images/chevron-right.png [6] BSD License -spyderlib/images/scipy.png +spyder/images/scipy.png [7] Creative Commons Attribution 2.5 License (FamFamFam Silk icon set 1.3) -spyderlib/images/actions/collapse.png -spyderlib/images/actions/collapse_selection.png -spyderlib/images/actions/expand.png -spyderlib/images/actions/expand_selection.png -spyderlib/images/actions/restore.png -spyderlib/images/editor/class.png -spyderlib/images/editor/convention.png -spyderlib/images/editor/todo.png -spyderlib/images/editor/warning.png -spyderlib/images/filetypes/po.png -spyderlib/images/filetypes/pot.png +spyder/images/actions/collapse.png +spyder/images/actions/collapse_selection.png +spyder/images/actions/expand.png +spyder/images/actions/expand_selection.png +spyder/images/actions/restore.png +spyder/images/editor/class.png +spyder/images/editor/convention.png +spyder/images/editor/todo.png +spyder/images/editor/warning.png +spyder/images/filetypes/po.png +spyder/images/filetypes/pot.png [8] PSF LICENSE AGREEMENT FOR PYTHON -spyderlib/images/console/python.png -spyderlib/images/console/python_t.png -spyderlib/images/filetypes/py.png -spyderlib/images/filetypes/pyc.png -spyderlib/images/filetypes/pyw.png +spyder/images/console/python.png +spyder/images/console/python_t.png +spyder/images/filetypes/py.png +spyder/images/filetypes/pyc.png +spyder/images/filetypes/pyw.png [9] zlib/libpng license -spyderlib/images/filetypes/nsh.png -spyderlib/images/filetypes/nsi.png +spyder/images/filetypes/nsh.png +spyder/images/filetypes/nsi.png [10] Eclipse Public License -spyderlib/images/projects/pydev.png +spyder/images/projects/pydev.png [11] Creative Commons Attribution 3.0 License (Yusuke Kamiyamane Icons) -spyderlib/images/actions/arrow-continue.png -spyderlib/images/actions/arrow-step-in.png -spyderlib/images/actions/arrow-step-out.png -spyderlib/images/actions/arrow-step-over.png -spyderlib/images/actions/maximize.png -spyderlib/images/actions/stop_debug.png -spyderlib/images/actions/unmaximize.png -spyderlib/images/actions/window_fullscreen.png -spyderlib/images/editor/breakpoint_big.png -spyderlib/images/editor/breakpoint_cond_big.png -spyderlib/images/editor/breakpoint_cond_small.png -spyderlib/images/editor/breakpoint_small.png -spyderlib/images/editor/debug.png -spyderlib/images/console/terminated.png -spyderlib/images/actions/stop.png +spyder/images/actions/arrow-continue.png +spyder/images/actions/arrow-step-in.png +spyder/images/actions/arrow-step-out.png +spyder/images/actions/arrow-step-over.png +spyder/images/actions/maximize.png +spyder/images/actions/stop_debug.png +spyder/images/actions/unmaximize.png +spyder/images/actions/window_fullscreen.png +spyder/images/editor/breakpoint_big.png +spyder/images/editor/breakpoint_cond_big.png +spyder/images/editor/breakpoint_cond_small.png +spyder/images/editor/breakpoint_small.png +spyder/images/editor/debug.png +spyder/images/console/terminated.png +spyder/images/actions/stop.png [12] Creative Commons BY-SA license (The Oxygen icon theme) -spyderlib/images/editor/run.png -spyderlib/images/editor/run_again.png -spyderlib/images/editor/run_cell.png -spyderlib/images/editor/run_cell_advance.png -spyderlib/images/editor/run_selection.png -spyderlib/images/editor/run_settings.png -spyderlib/images/console/run_small.png +spyder/images/editor/run.png +spyder/images/editor/run_again.png +spyder/images/editor/run_cell.png +spyder/images/editor/run_cell_advance.png +spyder/images/editor/run_selection.png +spyder/images/editor/run_settings.png +spyder/images/console/run_small.png [13] http://preloaders.net/ (According to the website: "All animated GIF and APNG images are completely free to use in all projects (web and desktop applications, freeware and commercial projects)") -spyderlib/images/console/loading_sprites.png +spyder/images/console/loading_sprites.png diff -Nru spyder-2.3.8+dfsg1/MANIFEST.in spyder-3.0.2+dfsg1/MANIFEST.in --- spyder-2.3.8+dfsg1/MANIFEST.in 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/MANIFEST.in 2016-10-25 00:05:22.000000000 +0000 @@ -1,7 +1,10 @@ -recursive-include spyderlib *.pot *.po *.svg *.png *.css *.qss -recursive-include spyderlibplugins *.pot *.po *.svg *.png +recursive-include spyder *.pot *.po *.svg *.png *.css *.qss +recursive-include spyder_breakpoints *.pot *.po *.svg *.png +recursive-include spyder_profiler *.pot *.po *.svg *.png +recursive-include spyder_pylint *.pot *.po *.svg *.png recursive-include doc *.py *.rst *.png *.ico *. -recursive-include app_example *.py *.pyw *.bat *.qm *.svg *.png +include spyder/fonts/spyder.ttf +include spyder/fonts/spyder-charmap.json include scripts/* include img_src/*.ico include img_src/spyder.png diff -Nru spyder-2.3.8+dfsg1/PKG-INFO spyder-3.0.2+dfsg1/PKG-INFO --- spyder-2.3.8+dfsg1/PKG-INFO 2015-11-27 13:35:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/PKG-INFO 2016-11-20 22:54:14.000000000 +0000 @@ -1,31 +1,26 @@ Metadata-Version: 1.1 Name: spyder -Version: 2.3.8 +Version: 3.0.2 Summary: Scientific PYthon Development EnviRonment Home-page: https://github.com/spyder-ide/spyder -Author: Pierre Raybaut +Author: The Spyder Project Contributors Author-email: UNKNOWN License: MIT -Download-URL: https://github.com/spyder-ide/spyder/files/spyder-2.3.8.zip -Description: Spyder is an interactive Python development environment providing +Download-URL: https://github.com/spyder-ide/spyder/files/spyder-3.0.2.zip +Description: Spyder is an interactive Python development environment providing MATLAB-like features in a simple and light-weighted software. - It also provides ready-to-use pure-Python widgets to your PyQt4 or - PySide application: source code editor with syntax highlighting and - code introspection/analysis features, NumPy array editor, dictionary + It also provides ready-to-use pure-Python widgets to your PyQt5 or + PyQt4 application: source code editor with syntax highlighting and + code introspection/analysis features, NumPy array editor, dictionary editor, Python console, etc. -Keywords: PyQt4 PySide editor shell console widgets IDE +Keywords: PyQt5 PyQt4 editor shell console widgets IDE Platform: any Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: OS Independent -Classifier: Operating System :: POSIX -Classifier: Operating System :: Unix +Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 5 - Production/Stable Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development :: Widget Sets -Requires: rope (>=0.9.2) -Requires: sphinx (>=0.6.0) -Requires: PyQt4 (>=4.4) diff -Nru spyder-2.3.8+dfsg1/README.md spyder-3.0.2+dfsg1/README.md --- spyder-2.3.8+dfsg1/README.md 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/README.md 2016-11-16 15:40:01.000000000 +0000 @@ -1,9 +1,16 @@ # Spyder - The Scientific PYthon Development EnviRonment -Copyright © 2009-2013 Pierre Raybaut. -Licensed under the terms of the MIT License -(see `spyderlib/__init__.py` for details) +Copyright © Spyder Project Contributors. +## Project details +[![license](https://img.shields.io/pypi/l/spyder.svg)](./LICENSE) +[![pypi version](https://img.shields.io/pypi/v/spyder.svg)](https://pypi.python.org/pypi/spyder) +[![Join the chat at https://gitter.im/spyder-ide/public](https://badges.gitter.im/spyder-ide/spyder.svg)](https://gitter.im/spyder-ide/public) + +## Build status +[![Travis status](https://travis-ci.org/spyder-ide/spyder.svg?branch=master)](https://travis-ci.org/spyder-ide/spyder) +[![AppVeyor status](https://ci.appveyor.com/api/projects/status/awb92if4tl555fuy/branch/master?svg=true)](https://ci.appveyor.com/project/ccordoba12/spyder/branch/master) +[![Coverage Status](https://coveralls.io/repos/github/spyder-ide/spyder/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/spyder?branch=master) ## Overview @@ -11,40 +18,40 @@ Spyder is a Python development environment with a lot of features: -* Editor +* **Editor** Multi-language editor with function/class browser, code analysis features (pyflakes and pylint are currently supported), code completion, horizontal and vertical splitting, and goto definition. -* Interactive console +* **Interactive console** Python or IPython consoles with workspace and debugging support to instantly evaluate the code written in the Editor. It also comes with Matplotlib figures integration. -* Documentation viewer +* **Documentation viewer** Show documentation for any class or function call made either in the Editor or a Console. -* Variable explorer +* **Variable explorer** Explore variables created during the execution of a file. Editing them is also possible with several GUI based editors, like a dictionary and Numpy array ones. -* Find in files feature +* **Find in files** Supporting regular expressions and mercurial repositories -* File/directories explorer +* **File explorer** -* History log +* **History log** -Spyder may also be used as a PyQt4/PySide extension library (module -`spyderlib`). For example, the Python interactive shell widget used in -Spyder may be embedded in your own PyQt4/PySide application. +Spyder may also be used as a PyQt5/PyQt4 extension library (module +`spyder`). For example, the Python interactive shell widget used in +Spyder may be embedded in your own PyQt5/PyQt4 application. ## Documentation @@ -61,122 +68,112 @@ the `bootstrap` script (see next section). The easiest way to install Spyder is: - -* On Windows: - - Using one of our executable installers, which can be found - [here](https://github.com/spyder-ide/spyder/releases). - - Or using one of these scientific Python distributions: - 1. [Python(x,y)](http://pythonxy.googlecode.com) - 2. [WinPython](https://winpython.github.io/) - 3. [Anaconda](http://continuum.io/downloads) - -* On Mac OSX: - - - Using our DMG installer, which can be found - [here](https://github.com/spyder-ide/spyder/releases). - - Using the [Anaconda Distribution](http://continuum.io/downloads). - - Through [MacPorts](http://www.macports.org/). - -* On GNU/Linux - - - Through your distribution package manager (i.e. `apt-get`, `yum`, - etc). - - Using the [Anaconda Distribution](http://continuum.io/downloads). - - Installing from source (see below). - -### Installing from source - -You can also install Spyder from its zip source package. For that you need to -download and uncompress the file called `spyder-x.y.z.zip`, which can be -found [here](https://github.com/spyder-ide/spyder/releases). Then you need to -use the integrated `setup.py` script that comes with it and which is based -on the Python standard library `distutils` module, with the following command: - - python setup.py install - -Note that `distutils` does *not* uninstall previous versions of Python -packages: it simply copies files on top of an existing installation. -When using this command, it is thus highly recommended to uninstall -manually any previous version of Spyder by removing the associated -directories ('spyderlib' and 'spyderplugins') from your site-packages -directory). - -From the [Python package index](http://pypi.python.org/pypi), you also -may install Spyder *and* upgrade an existing installation using `pip` -with this command +### On Windows: - pip install --upgrade spyder +Using one (and only one) of these scientific Python distributions: -For more details on supported platforms, please go to -. +1. [Anaconda](http://continuum.io/downloads) +2. [WinPython](https://winpython.github.io/) +3. [Python(x,y)](http://python-xy.github.io) +### On Mac OSX: -## Dependencies +- Using our DMG installer, which can be found + [here](https://github.com/spyder-ide/spyder/releases). +- Using the [Anaconda Distribution](http://continuum.io/downloads). +- Through [MacPorts](http://www.macports.org/). -*Imnportant note*: Most if not all the dependencies listed below come -with Python(x,y), WinPython and Anaconda, so you don't need to install -them separately when installing one of these scientific Python -distributions. +### On GNU/Linux -### Build dependencies +- Through your distribution package manager (i.e. `apt-get`, `yum`, + etc). +- Using the [Anaconda Distribution](http://continuum.io/downloads). +- Installing from source (see below). -When installing Spyder from its source package (using the command -`python setup.py install`), the only requirements is to have a Python version -greater than 2.6. +### Cross-platform way from source -### Runtime dependencies +You can also install Spyder with the `pip` package manager, which comes by +default with most Python installations. For that you need to use the +command: -* Python 2.6+ + pip install spyder -* PyQt4 4.6+ or PySide 1.2.0+ (PyQt4 is recommended) - -### Recommended modules +To upgrade Spyder to its latest version, if it was installed before, you need +to run -* Rope v0.9.2+ (editor code completion, calltips and go-to-definition) + pip install --upgrade spyder -* Pyflakes v0.5.0+ (real-time code analysis) +For more details on supported platforms, please refer to our +[installation instructions](http://pythonhosted.org/spyder/installation.html). -* Sphinx v0.6+ (object inspector's rich text mode) +**Important note**: This does not install the graphical Python libraries (i.e. +PyQt5 or PyQt4) that Spyder depends on. Those have to be installed separately +after installing Python. -* Numpy (N-dimensional arrays) -* Scipy (signal/image processing) +## Running from source -* Matplotlib (2D/3D plotting) +The fastest way to run Spyder is to get the source code using git, install +PyQt5 or PyQt4, and run these commands: -* IPython 3.0- or qtconsole 4.0+ (enhanced Python interpreter) +1. Install our *runtime dependencies* (see below). +2. `cd /your/spyder/git-clone` +3. `python bootstrap.py` - In Ubuntu you need to install `ipython-qtconsole`, on Fedora - `ipython-gui` and on Gentoo `ipython` with the `qt4` USE flag. +You may want to do this for fixing bugs in Spyder, adding new +features, learning how Spyder works or just getting a taste of it. -### Optional modules -* Pygments (syntax highlighting for several file types). +## Dependencies -* Pylint (static code analysis). +**Important note**: Most if not all the dependencies listed below come +with *Anaconda*, *WinPython* and *Python(x,y)*, so you don't need to install +them separately when installing one of these Scientific Python +distributions. -* Pep8 (style analysis). +### Build dependencies -## Running from source +When installing Spyder from its source package, the only requirement is to have +a Python version greater than 2.7 (Python 3.2 is not supported anymore). -It is possible to run Spyder directly (i.e. without installation) -from the unpacked zip folder (see *Installing from source*) using -Spyder's bootstrap script like this: +### Runtime dependencies - python bootstrap.py +* **Python** 2.7 or 3.3+ +* **PyQt5** 5.2+ or **PyQt4** 4.6+: PyQt5 is recommended. +* **qtconsole** 4.2.0+: Enhanced Python interpreter. +* **Rope** and **Jedi**: Editor code completion, calltips + and go-to-definition. +* **Pyflakes**: Real-time code analysis. +* **Sphinx**: Rich text mode for the Help pane. +* **Pygments** 2.0+: Syntax highlighting for all file types it supports. +* **Pylint**: Static code analysis. +* **Pep8**: Style analysis. +* **Psutil**: CPU and memory usage on the status bar. +* **Nbconvert**: Manipulation of notebooks in the Editor. +* **Qtawesome**: To have an icon theme based on FontAwesome. +* **Pickleshare**: Show import completions on the Python consoles. +* **PyZMQ**: Run introspection services asynchronously. +* **QtPy** 1.1.0+: Abstracion layer for Python Qt bindings so that Spyder can run on PyQt4 + and PyQt5. + +### Optional dependencies + +* **Matplotlib**: 2D/3D plotting in the Python and IPython consoles. +* **Pandas**: View and edit DataFrames and Series in the Variable Explorer. +* **Numpy**: View and edit two or three dimensional arrays in the Variable Explorer. +* **SymPy**: Symbolic mathematics in the IPython console. +* **SciPy**: Import Matlab workspace files in the Variable Explorer. -This is especially useful for beta-testing, troubleshooting -and development of Spyder itself. ## More information -* For code development please go to +* For code development please go to: - -* For bug reports and feature requests - + +* For bug reports and feature requests: + * For discussions and troubleshooting: diff -Nru spyder-2.3.8+dfsg1/scripts/spyder spyder-3.0.2+dfsg1/scripts/spyder --- spyder-2.3.8+dfsg1/scripts/spyder 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/scripts/spyder 2016-10-25 00:05:22.000000000 +0000 @@ -1,3 +1,3 @@ #!/usr/bin/env python -from spyderlib import start_app -start_app.main() \ No newline at end of file +from spyder.app import start +start.main() diff -Nru spyder-2.3.8+dfsg1/scripts/spyder3 spyder-3.0.2+dfsg1/scripts/spyder3 --- spyder-2.3.8+dfsg1/scripts/spyder3 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/scripts/spyder3 2016-10-25 00:05:22.000000000 +0000 @@ -1,3 +1,3 @@ #! /usr/bin/python3 -from spyderlib import start_app -start_app.main() \ No newline at end of file +from spyder.app import start +start.main() diff -Nru spyder-2.3.8+dfsg1/scripts/spyder_win_post_install.py spyder-3.0.2+dfsg1/scripts/spyder_win_post_install.py --- spyder-2.3.8+dfsg1/scripts/spyder_win_post_install.py 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/scripts/spyder_win_post_install.py 2016-10-25 00:05:22.000000000 +0000 @@ -95,7 +95,7 @@ file=sys.stderr) sys.exit(1) from win32com.shell import shell, shellcon - + path_names = ['CSIDL_COMMON_STARTMENU', 'CSIDL_STARTMENU', 'CSIDL_COMMON_APPDATA', 'CSIDL_LOCAL_APPDATA', 'CSIDL_APPDATA', 'CSIDL_COMMON_DESKTOPDIRECTORY', @@ -131,10 +131,12 @@ python = osp.abspath(osp.join(sys.prefix, 'python.exe')) pythonw = osp.abspath(osp.join(sys.prefix, 'pythonw.exe')) script = osp.abspath(osp.join(sys.prefix, 'scripts', 'spyder')) + if not osp.exists(script): # if not installed to the site scripts dir + script = osp.abspath(osp.join(osp.dirname(osp.abspath(__file__)), 'spyder')) workdir = "%HOMEDRIVE%%HOMEPATH%" import distutils.sysconfig lib_dir = distutils.sysconfig.get_python_lib(plat_specific=1) - ico_dir = osp.join(lib_dir, 'spyderlib', 'windows') + ico_dir = osp.join(lib_dir, 'spyder', 'windows') # if user is running -install manually then icons are in Scripts/ if not osp.isdir(ico_dir): ico_dir = osp.dirname(osp.abspath(__file__)) @@ -145,13 +147,6 @@ osp.join(ico_dir, 'spyder.ico')) file_created(fname) - desc += '. Light configuration: console and variable explorer only.' - fname = osp.join(start_menu, 'Spyder (light).lnk') - create_shortcut(python, desc, fname, - '"%s" --light' % script, workdir, - osp.join(ico_dir, 'spyder_light.ico')) - file_created(fname) - fname = osp.join(start_menu, 'Spyder-Reset all settings.lnk') create_shortcut(python, 'Reset Spyder settings to defaults', fname, '"%s" --reset' % script, workdir) diff -Nru spyder-2.3.8+dfsg1/setup.cfg spyder-3.0.2+dfsg1/setup.cfg --- spyder-2.3.8+dfsg1/setup.cfg 2015-11-27 13:35:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/setup.cfg 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -[egg_info] -tag_date = 0 -tag_svn_revision = 0 -tag_build = - diff -Nru spyder-2.3.8+dfsg1/setup.py spyder-3.0.2+dfsg1/setup.py --- spyder-2.3.8+dfsg1/setup.py 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/setup.py 2016-10-25 00:05:22.000000000 +0000 @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # -# Copyright © 2009-2010 Pierre Raybaut +# Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) +# (see spyder/__init__.py for details) """ Spyder @@ -17,31 +17,42 @@ import os.path as osp import subprocess import sys -import re import shutil from distutils.core import setup from distutils.command.build import build +from distutils.command.install import install from distutils.command.install_data import install_data + +#============================================================================== # Check for Python 3 +#============================================================================== PY3 = sys.version_info[0] == 3 -# This is necessary to prevent an error while installing Spyder with pip -# See http://stackoverflow.com/a/18961843/438386 -with_setuptools = False -if 'USE_SETUPTOOLS' in os.environ or 'pip' in __file__ or \ - 'VIRTUAL_ENV' in os.environ: - try: - from setuptools.command.install import install - with_setuptools = True - except: - with_setuptools = False -if not with_setuptools: - from distutils.command.install import install # analysis:ignore +#============================================================================== +# Minimal Python version sanity check +# Taken from the notebook setup.py -- Modified BSD License +#============================================================================== +v = sys.version_info +if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)): + error = "ERROR: Spyder requires Python version 2.7 or 3.3 or above." + print(error, file=sys.stderr) + sys.exit(1) + + +#============================================================================== +# Constants +#============================================================================== +NAME = 'spyder' +LIBNAME = 'spyder' +from spyder import __version__, __project_url__ +#============================================================================== +# Auxiliary functions +#============================================================================== def get_package_data(name, extlist): """Return data files for package *name* with extensions in *extlist*""" flist = [] @@ -74,12 +85,28 @@ ('share/pixmaps', ['img_src/spyder.png'])] elif os.name == 'nt': data_files = [('scripts', ['img_src/spyder.ico', - 'img_src/spyder_light.ico'])] + 'img_src/spyder_reset.ico'])] else: data_files = [] return data_files +def get_packages(): + """Return package list""" + packages = ( + get_subpackages(LIBNAME) + + get_subpackages('spyder_breakpoints') + + get_subpackages('spyder_profiler') + + get_subpackages('spyder_pylint') + + get_subpackages('spyder_io_dcm') + + get_subpackages('spyder_io_hdf5') + ) + return packages + + +#============================================================================== +# Make Linux detect Spyder desktop file +#============================================================================== class MyInstallData(install_data): def run(self): install_data.run(self) @@ -92,7 +119,9 @@ CMDCLASS = {'install_data': MyInstallData} +#============================================================================== # Sphinx build (documentation) +#============================================================================== def get_html_help_exe(): """Return HTML Help Workshop executable path (Windows only)""" if os.name == 'nt': @@ -135,8 +164,8 @@ build = self.get_finalized_command('build') sys.path.insert(0, os.path.abspath(build.build_lib)) dirname = self.distribution.get_command_obj('build').build_purelib - self.builder_target_dir = osp.join(dirname, 'spyderlib', 'doc') - + self.builder_target_dir = osp.join(dirname, 'spyder', 'doc') + if not osp.exists(self.builder_target_dir): os.mkdir(self.builder_target_dir) @@ -152,13 +181,13 @@ "location (path with *only* ASCII characters).", file=sys.stderr) sys.path.pop(0) - + # Building chm doc, if HTML Help Workshop is installed if hhc_exe is not None: fname = osp.join(self.builder_target_dir, 'Spyderdoc.chm') subprocess.call('"%s" %s' % (hhc_exe, fname), shell=True) if osp.isfile(fname): - dest = osp.join(dirname, 'spyderlib') + dest = osp.join(dirname, 'spyder') try: shutil.move(fname, dest) except shutil.Error: @@ -171,39 +200,9 @@ 'is not installed', file=sys.stderr) -NAME = 'spyder' -LIBNAME = 'spyderlib' -from spyderlib import __version__, __project_url__ - - -JOINEDARGS = ''.join(sys.argv) -WINDOWS_INSTALLER = 'bdist_wininst' in JOINEDARGS or 'bdist_msi' in JOINEDARGS -TARGET_MATCH = re.search(r'--target-version=([0-9]*)\.([0-9]*)', JOINEDARGS) -if TARGET_MATCH: - TARGET_VERSION = TARGET_MATCH.groups() -else: - TARGET_VERSION = (str(sys.version_info[0]), str(sys.version_info[1])) - - -def get_packages(): - """Return package list""" - if WINDOWS_INSTALLER: - # Adding pyflakes and rope to the package if available in the - # repository (this is not conventional but Spyder really need - # those tools and there is not decent package manager on - # Windows platforms, so...) - import shutil - import atexit - extdir = 'external-py' + TARGET_VERSION[0] - for name in ('rope', 'pyflakes'): - srcdir = osp.join(extdir, name) - if osp.isdir(srcdir): - dstdir = osp.join(LIBNAME, 'utils', 'external', name) - shutil.copytree(srcdir, dstdir) - atexit.register(shutil.rmtree, osp.abspath(dstdir)) - packages = get_subpackages(LIBNAME)+get_subpackages('spyderplugins') - return packages - +#============================================================================== +# Main scripts +#============================================================================== # NOTE: the '[...]_win_post_install.py' script is installed even on non-Windows # platforms due to a bug in pip installation process (see Issue 1158) SCRIPTS = ['%s_win_post_install.py' % NAME] @@ -211,61 +210,95 @@ SCRIPTS.append('spyder3') else: SCRIPTS.append('spyder') + + +#============================================================================== +# Files added to the package +#============================================================================== EXTLIST = ['.mo', '.svg', '.png', '.css', '.html', '.js', '.chm', '.ini', - '.txt', '.rst', '.qss'] + '.txt', '.rst', '.qss', '.ttf', '.json'] if os.name == 'nt': SCRIPTS += ['spyder.bat'] EXTLIST += ['.ico'] -# Adding a message for the Windows installers -WININST_MSG = "" -if WINDOWS_INSTALLER: - WININST_MSG = \ -"""Please uninstall any previous version of Spyder before continue. - -""" - -setup(name=NAME, +#============================================================================== +# Setup arguments +#============================================================================== +setup_args = dict(name=NAME, version=__version__, description='Scientific PYthon Development EnviRonment', - long_description=WININST_MSG + \ -"""Spyder is an interactive Python development environment providing + long_description= +"""Spyder is an interactive Python development environment providing MATLAB-like features in a simple and light-weighted software. -It also provides ready-to-use pure-Python widgets to your PyQt4 or -PySide application: source code editor with syntax highlighting and -code introspection/analysis features, NumPy array editor, dictionary +It also provides ready-to-use pure-Python widgets to your PyQt5 or +PyQt4 application: source code editor with syntax highlighting and +code introspection/analysis features, NumPy array editor, dictionary editor, Python console, etc.""", download_url='%s/files/%s-%s.zip' % (__project_url__, NAME, __version__), - author="Pierre Raybaut", + author="The Spyder Project Contributors", url=__project_url__, license='MIT', - keywords='PyQt4 PySide editor shell console widgets IDE', + keywords='PyQt5 PyQt4 editor shell console widgets IDE', platforms=['any'], packages=get_packages(), package_data={LIBNAME: get_package_data(LIBNAME, EXTLIST), - 'spyderplugins': - get_package_data('spyderplugins', EXTLIST)}, - requires=["rope (>=0.9.2)", "sphinx (>=0.6.0)", "PyQt4 (>=4.4)"], + 'spyder_breakpoints': get_package_data('spyder_breakpoints', EXTLIST), + 'spyder_profiler': get_package_data('spyder_profiler', EXTLIST), + 'spyder_pylint': get_package_data('spyder_pylint', EXTLIST), + 'spyder_io_dcm': get_package_data('spyder_io_dcm', EXTLIST), + 'spyder_io_hdf5': get_package_data('spyder_io_hdf5', EXTLIST), + }, scripts=[osp.join('scripts', fname) for fname in SCRIPTS], data_files=get_data_files(), - options={"bdist_wininst": - {"install_script": "%s_win_post_install.py" % NAME, - "title": "%s %s" % (NAME.capitalize(), __version__), - "bitmap": osp.join('img_src', 'spyder-bdist_wininst.bmp'), - "target_version": '%s.%s' % TARGET_VERSION, - "user_access_control": "auto"}, - "bdist_msi": - {"install_script": "%s_win_post_install.py" % NAME}}, classifiers=['License :: OSI Approved :: MIT License', 'Operating System :: MacOS', 'Operating System :: Microsoft :: Windows', - 'Operating System :: OS Independent', - 'Operating System :: POSIX', - 'Operating System :: Unix', + 'Operating System :: POSIX :: Linux', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', 'Topic :: Scientific/Engineering', 'Topic :: Software Development :: Widget Sets'], cmdclass=CMDCLASS) + + +#============================================================================== +# Setuptools deps +#============================================================================== +if any(arg == 'bdist_wheel' for arg in sys.argv): + import setuptools # analysis:ignore + +install_requires = [ + 'rope_py3k' if PY3 else 'rope>=0.9.4', + 'jedi', + 'pyflakes', + 'pygments>=2.0', + 'qtconsole>=4.2.0', + 'nbconvert', + 'sphinx', + 'pep8', + 'pylint', + 'psutil', + 'qtawesome', + 'qtpy>=1.1.0', + 'pickleshare', + 'pyzmq' +] + +if 'setuptools' in sys.modules: + setup_args['install_requires'] = install_requires + + setup_args['entry_points'] = { + 'gui_scripts': [ + 'spyder = spyder.app.start:main' + ] + } + + setup_args.pop('scripts', None) + + +#============================================================================== +# Main setup +#============================================================================== +setup(**setup_args) diff -Nru spyder-2.3.8+dfsg1/spyder/app/cli_options.py spyder-3.0.2+dfsg1/spyder/app/cli_options.py --- spyder-2.3.8+dfsg1/spyder/app/cli_options.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/app/cli_options.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +import optparse + +def get_options(): + """ + Convert options into commands + return commands, message + """ + parser = optparse.OptionParser(usage="spyder [options] files") + parser.add_option('--new-instance', action='store_true', default=False, + help="Run a new instance of Spyder, even if the single " + "instance mode has been turned on (default)") + parser.add_option('--defaults', dest="reset_to_defaults", + action='store_true', default=False, + help="Reset configuration settings to defaults") + parser.add_option('--reset', dest="reset_config_files", + action='store_true', default=False, + help="Remove all configuration files!") + parser.add_option('--optimize', action='store_true', default=False, + help="Optimize Spyder bytecode (this may require " + "administrative privileges)") + parser.add_option('-w', '--workdir', dest="working_directory", default=None, + help="Default working directory") + parser.add_option('--show-console', action='store_true', default=False, + help="Do not hide parent console window (Windows)") + parser.add_option('--multithread', dest="multithreaded", + action='store_true', default=False, + help="Internal console is executed in another thread " + "(separate from main application thread)") + parser.add_option('--profile', action='store_true', default=False, + help="Profile mode (internal test, " + "not related with Python profiling)") + parser.add_option('--window-title', type=str, default=None, + help="String to show in the main window title") + options, args = parser.parse_args() + return options, args diff -Nru spyder-2.3.8+dfsg1/spyder/app/__init__.py spyder-3.0.2+dfsg1/spyder/app/__init__.py --- spyder-2.3.8+dfsg1/spyder/app/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/app/__init__.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.app +========== + +Modules related to starting and restarting the Spyder application +""" diff -Nru spyder-2.3.8+dfsg1/spyder/app/mac_stylesheet.qss spyder-3.0.2+dfsg1/spyder/app/mac_stylesheet.qss --- spyder-2.3.8+dfsg1/spyder/app/mac_stylesheet.qss 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/app/mac_stylesheet.qss 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,124 @@ +/* +* Qt Stylesheet for MacOS X +* Copyright (c) Spyder Project Contributors +*/ + + +/* ---------------- Dock widget and QSplitter separators --------------- */ + +QMainWindow::separator { + width: 3px; + height: 3px; + border: 1px solid lightgrey; + border-radius: 1px; +} + +QMainWindow::separator:hover { + background: darkgrey; +} + +QToolButton { + border: none; +} + +QSplitter::handle:horizontal { + border: 1px solid darkgrey; + width: 2px; +} + +QSplitter::handle:vertical { + border: 1px solid darkgrey; + height: 2px; +} + +QSplitter::handle:pressed { + background: darkgrey; +} + + +/* ----------------- Tabs ------------------ */ + +QWidget#tab-container { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #b1b1b1, stop: 0.07 #b3b3b3, + stop: 0.33 #b3b3b3, stop: 0.4 #b0b0b0, + stop: 0.47 #b3b3b3, stop: 1.0 #b2b2b2); +} + +QTabWidget::pane#plugin-tab { + border-top: 1px solid qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #b1b1b1, stop: 0.07 #b3b3b3, + stop: 0.33 #b3b3b3, stop: 0.4 #b0b0b0, + stop: 0.47 #b3b3b3, stop: 1.0 #b2b2b2); + border-bottom: 0px; + border-left: 0px; + border-right: 0px; +} + +QTabWidget::tab-bar#plugin-tab { + left: 5px; +} + +QTabBar::tab#plugin-tab { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #b1b1b1, stop: 0.07 #b3b3b3, + stop: 0.33 #b3b3b3, stop: 0.4 #b0b0b0, + stop: 0.47 #b3b3b3, stop: 1.0 #b2b2b2); + border: 1px solid #787878; + border-top-color: transparent; + border-bottom-color: transparent; + margin-left: -1px; + margin-right: -1px; + min-width: 15ex; + padding: 3px; +} + +QTabBar::tab:selected#plugin-tab { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #dfdfdf, stop: 0.1 #dddddd, + stop: 0.12 #dfdfdf, stop: 0.22 #e0e0e0, + stop: 0.33 #dedede, stop: 0.47 #dedede, + stop: 0.49 #e0e0e0, stop: 0.59 #dddddd, + stop: 0.61 #dfdfdf, stop: 0.73 #dedede, + stop: 0.80 #e0e0e0, stop: 1.0 #dedede); + border: 1px solid #787878; + border-top: 0px; + border-top-color: transparent; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} + +QTabBar::tab:first#plugin-tab { + margin-left: 0; +} + +QTabBar::tab:last#plugin-tab { + margin-right: 0; +} + +QTabBar::tab:only-one#plugin-tab { + margin: 0; +} + +QTabBar::scroller#plugin-tab { + width: 22px; +} + +QTabBar#plugin-tab QToolButton::left-arrow { + background: lightgrey; + border-right: 1px solid darkgrey; + image: url($IMAGE_PATH/chevron-left.png); +} + +QTabBar#plugin-tab QToolButton::right-arrow { + background: lightgrey; + image: url($IMAGE_PATH/chevron-right.png); +} + + +/* ------------------ Dock widgets ------------------- */ + +QDockWidget::close-button, QDockWidget::float-button { + padding: 0px; + margin: 2px; +} diff -Nru spyder-2.3.8+dfsg1/spyder/app/mainwindow.py spyder-3.0.2+dfsg1/spyder/app/mainwindow.py --- spyder-2.3.8+dfsg1/spyder/app/mainwindow.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/app/mainwindow.py 2016-11-17 03:39:39.000000000 +0000 @@ -0,0 +1,3016 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Spyder, the Scientific PYthon Development EnviRonment +===================================================== + +Developped and maintained by the Spyder Project +Contributors + +Copyright © Spyder Project Contributors +Licensed under the terms of the MIT License +(see spyder/__init__.py for details) +""" + +# ============================================================================= +# Stdlib imports +# ============================================================================= +from __future__ import print_function + +import atexit +import errno +import os +import os.path as osp +import re +import shutil +import signal +import socket +import subprocess +import sys +import threading +import traceback + + +#============================================================================== +# Keeping a reference to the original sys.exit before patching it +#============================================================================== +ORIGINAL_SYS_EXIT = sys.exit + + +#============================================================================== +# Check requirements +#============================================================================== +from spyder import requirements +requirements.check_path() +requirements.check_qt() + + +#============================================================================== +# Windows only: support for hiding console window when started with python.exe +#============================================================================== +set_attached_console_visible = None +is_attached_console_visible = None +set_windows_appusermodelid = None +if os.name == 'nt': + from spyder.utils.windows import (set_attached_console_visible, + is_attached_console_visible, + set_windows_appusermodelid) + + +#============================================================================== +# Workaround: importing rope.base.project here, otherwise this module can't +# be imported if Spyder was executed from another folder than spyder +#============================================================================== +try: + import rope.base.project # analysis:ignore +except ImportError: + pass + + +#============================================================================== +# Qt imports +#============================================================================== +from qtpy import API, PYQT5 +from qtpy.compat import from_qvariant +from qtpy.QtCore import (QByteArray, QCoreApplication, QPoint, QSize, Qt, + QThread, QTimer, QUrl, Signal, Slot) +from qtpy.QtGui import QColor, QDesktopServices, QKeySequence, QPixmap +from qtpy.QtWidgets import (QAction, QApplication, QDockWidget, QMainWindow, + QMenu, QMessageBox, QShortcut, QSplashScreen, + QStyleFactory) +# Avoid a "Cannot mix incompatible Qt library" error on Windows platforms +# when PySide is selected by the QT_API environment variable and when PyQt4 +# is also installed (or any other Qt-based application prepending a directory +# containing incompatible Qt DLLs versions in PATH): +from qtpy import QtSvg # analysis:ignore + +# Avoid a bug in Qt: https://bugreports.qt.io/browse/QTBUG-46720 +from qtpy import QtWebEngineWidgets # analysis:ignore + + +#============================================================================== +# Proper high DPI scaling is available in Qt >= 5.6.0. This attibute must +# be set before creating the application. +#============================================================================== +from spyder.config.main import CONF +if CONF.get('main', 'high_dpi_scaling'): + high_dpi_scaling = True +else: + high_dpi_scaling = False +if hasattr(Qt, 'AA_EnableHighDpiScaling'): + QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling, high_dpi_scaling) + + +#============================================================================== +# Create our QApplication instance here because it's needed to render the +# splash screen created below +#============================================================================== +from spyder.utils.qthelpers import qapplication +MAIN_APP = qapplication() + + +#============================================================================== +# Create splash screen out of MainWindow to reduce perceived startup time. +#============================================================================== +from spyder.config.base import _, get_image_path, DEV +SPLASH = QSplashScreen(QPixmap(get_image_path('splash.svg'), 'svg')) +SPLASH_FONT = SPLASH.font() +SPLASH_FONT.setPixelSize(10) +SPLASH.setFont(SPLASH_FONT) +SPLASH.show() +SPLASH.showMessage(_("Initializing..."), Qt.AlignBottom | Qt.AlignCenter | + Qt.AlignAbsolute, QColor(Qt.white)) +QApplication.processEvents() + + +#============================================================================== +# Local utility imports +#============================================================================== +from spyder import __version__, __project_url__, __forum_url__, get_versions +from spyder.config.base import (get_conf_path, get_module_data_path, + get_module_source_path, STDERR, DEBUG, + debug_print, MAC_APP_NAME, get_home_dir, + running_in_mac_app, get_module_path, + reset_config_files) +from spyder.config.main import OPEN_FILES_PORT +from spyder.config.utils import IMPORT_EXT, is_gtk_desktop +from spyder.app.cli_options import get_options +from spyder import dependencies +from spyder.config.ipython import QTCONSOLE_INSTALLED +from spyder.py3compat import (getcwd, is_text_string, to_text_string, + PY3, qbytearray_to_str, configparser as cp) +from spyder.utils import encoding, programs +from spyder.utils import icon_manager as ima +from spyder.utils.introspection import module_completion +from spyder.utils.programs import is_module_installed +from spyder.utils.misc import select_port + +#============================================================================== +# Local gui imports +#============================================================================== +# NOTE: Move (if possible) import's of widgets and plugins exactly where they +# are needed in MainWindow to speed up perceived startup time (i.e. the time +# from clicking the Spyder icon to showing the splash screen). +try: + from spyder.utils.environ import WinUserEnvDialog +except ImportError: + WinUserEnvDialog = None # analysis:ignore + +from spyder.utils.qthelpers import (create_action, add_actions, get_icon, + add_shortcut_to_tooltip, + create_module_bookmark_actions, + create_program_action, DialogManager, + create_python_script_action, file_uri) +from spyder.config.gui import get_shortcut +from spyder.otherplugins import get_spyderplugins_mods +from spyder.app import tour + + +#============================================================================== +# Get the cwd before initializing WorkingDirectory, which sets it to the one +# used in the last session +#============================================================================== +CWD = getcwd() + + +#============================================================================== +# Spyder's main window widgets utilities +#============================================================================== +def get_python_doc_path(): + """ + Return Python documentation path + (Windows: return the PythonXX.chm path if available) + """ + if os.name == 'nt': + doc_path = osp.join(sys.prefix, "Doc") + if not osp.isdir(doc_path): + return + python_chm = [path for path in os.listdir(doc_path) + if re.match(r"(?i)Python[0-9]{3,6}.chm", path)] + if python_chm: + return file_uri(osp.join(doc_path, python_chm[0])) + else: + vinf = sys.version_info + doc_path = '/usr/share/doc/python%d.%d/html' % (vinf[0], vinf[1]) + python_doc = osp.join(doc_path, "index.html") + if osp.isfile(python_doc): + return file_uri(python_doc) + + +def get_focus_python_shell(): + """Extract and return Python shell from widget + Return None if *widget* is not a Python shell (e.g. IPython kernel)""" + widget = QApplication.focusWidget() + from spyder.widgets.shell import PythonShellWidget + from spyder.widgets.externalshell.pythonshell import ExternalPythonShell + if isinstance(widget, PythonShellWidget): + return widget + elif isinstance(widget, ExternalPythonShell): + return widget.shell + + +def get_focus_widget_properties(): + """Get properties of focus widget + Returns tuple (widget, properties) where properties is a tuple of + booleans: (is_console, not_readonly, readwrite_editor)""" + widget = QApplication.focusWidget() + from spyder.widgets.shell import ShellBaseWidget + from spyder.widgets.editor import TextEditBaseWidget + textedit_properties = None + if isinstance(widget, (ShellBaseWidget, TextEditBaseWidget)): + console = isinstance(widget, ShellBaseWidget) + not_readonly = not widget.isReadOnly() + readwrite_editor = not_readonly and not console + textedit_properties = (console, not_readonly, readwrite_editor) + return widget, textedit_properties + + +#============================================================================== +# Main Window +#============================================================================== +class MainWindow(QMainWindow): + """Spyder main window""" + DOCKOPTIONS = QMainWindow.AllowTabbedDocks|QMainWindow.AllowNestedDocks + SPYDER_PATH = get_conf_path('path') + BOOKMARKS = ( + ('numpy', "http://docs.scipy.org/doc/", + _("Numpy and Scipy documentation")), + ('matplotlib', "http://matplotlib.sourceforge.net/contents.html", + _("Matplotlib documentation")), + ('PyQt4', + "http://pyqt.sourceforge.net/Docs/PyQt4/", + _("PyQt4 Reference Guide")), + ('PyQt4', + "http://pyqt.sourceforge.net/Docs/PyQt4/classes.html", + _("PyQt4 API Reference")), + ('xy', "http://code.google.com/p/pythonxy/", + _("Python(x,y)")), + ('winpython', "https://winpython.github.io/", + _("WinPython")) + ) + + # Signals + restore_scrollbar_position = Signal() + all_actions_defined = Signal() + sig_pythonpath_changed = Signal() + sig_open_external_file = Signal(str) + sig_resized = Signal("QResizeEvent") # related to interactive tour + sig_moved = Signal("QMoveEvent") # related to interactive tour + + def __init__(self, options=None): + QMainWindow.__init__(self) + + qapp = QApplication.instance() + if PYQT5: + # Enabling scaling for high dpi + qapp.setAttribute(Qt.AA_UseHighDpiPixmaps) + self.default_style = str(qapp.style().objectName()) + + self.dialog_manager = DialogManager() + + self.init_workdir = options.working_directory + self.profile = options.profile + self.multithreaded = options.multithreaded + self.new_instance = options.new_instance + + self.debug_print("Start of MainWindow constructor") + + def signal_handler(signum, frame=None): + """Handler for signals.""" + sys.stdout.write('Handling signal: %s\n' % signum) + sys.stdout.flush() + QApplication.quit() + + if os.name == "nt": + try: + import win32api + win32api.SetConsoleCtrlHandler(signal_handler, True) + except ImportError: + pass + else: + signal.signal(signal.SIGTERM, signal_handler) + + # Use a custom Qt stylesheet + if sys.platform == 'darwin': + spy_path = get_module_source_path('spyder') + img_path = osp.join(spy_path, 'images') + mac_style = open(osp.join(spy_path, 'app', 'mac_stylesheet.qss')).read() + mac_style = mac_style.replace('$IMAGE_PATH', img_path) + self.setStyleSheet(mac_style) + + # Shortcut management data + self.shortcut_data = [] + + # Loading Spyder path + self.path = [] + self.project_path = [] + if osp.isfile(self.SPYDER_PATH): + self.path, _x = encoding.readlines(self.SPYDER_PATH) + self.path = [name for name in self.path if osp.isdir(name)] + self.remove_path_from_sys_path() + self.add_path_to_sys_path() + + # Plugins + self.console = None + self.workingdirectory = None + self.editor = None + self.explorer = None + self.help = None + self.onlinehelp = None + self.projects = None + self.outlineexplorer = None + self.historylog = None + self.extconsole = None + self.ipyconsole = None + self.variableexplorer = None + self.findinfiles = None + self.thirdparty_plugins = [] + + # Tour # TODO: Should I consider it a plugin?? or? + self.tour = None + self.tours_available = None + + # Check for updates Thread and Worker, refereces needed to prevent + # segfaulting + self.check_updates_action = None + self.thread_updates = None + self.worker_updates = None + self.give_updates_feedback = True + + # Preferences + from spyder.plugins.configdialog import (MainConfigPage, + ColorSchemeConfigPage) + from spyder.plugins.shortcuts import ShortcutsConfigPage + from spyder.plugins.runconfig import RunConfigPage + from spyder.plugins.maininterpreter import MainInterpreterConfigPage + self.general_prefs = [MainConfigPage, ShortcutsConfigPage, + ColorSchemeConfigPage, MainInterpreterConfigPage, + RunConfigPage] + self.prefs_index = None + self.prefs_dialog_size = None + + # Quick Layouts and Dialogs + from spyder.plugins.layoutdialog import (LayoutSaveDialog, + LayoutSettingsDialog) + self.dialog_layout_save = LayoutSaveDialog + self.dialog_layout_settings = LayoutSettingsDialog + + # Actions + self.lock_dockwidgets_action = None + self.show_toolbars_action = None + self.close_dockwidget_action = None + self.undo_action = None + self.redo_action = None + self.copy_action = None + self.cut_action = None + self.paste_action = None + self.selectall_action = None + self.maximize_action = None + self.fullscreen_action = None + + # Menu bars + self.file_menu = None + self.file_menu_actions = [] + self.edit_menu = None + self.edit_menu_actions = [] + self.search_menu = None + self.search_menu_actions = [] + self.source_menu = None + self.source_menu_actions = [] + self.run_menu = None + self.run_menu_actions = [] + self.debug_menu = None + self.debug_menu_actions = [] + self.consoles_menu = None + self.consoles_menu_actions = [] + self.projects_menu = None + self.projects_menu_actions = [] + self.tools_menu = None + self.tools_menu_actions = [] + self.external_tools_menu = None # We must keep a reference to this, + # otherwise the external tools menu is lost after leaving setup method + self.external_tools_menu_actions = [] + self.view_menu = None + self.plugins_menu = None + self.plugins_menu_actions = [] + self.toolbars_menu = None + self.help_menu = None + self.help_menu_actions = [] + + # Status bar widgets + self.mem_status = None + self.cpu_status = None + + # Toolbars + self.visible_toolbars = [] + self.toolbarslist = [] + self.main_toolbar = None + self.main_toolbar_actions = [] + self.file_toolbar = None + self.file_toolbar_actions = [] + self.edit_toolbar = None + self.edit_toolbar_actions = [] + self.search_toolbar = None + self.search_toolbar_actions = [] + self.source_toolbar = None + self.source_toolbar_actions = [] + self.run_toolbar = None + self.run_toolbar_actions = [] + self.debug_toolbar = None + self.debug_toolbar_actions = [] + self.layout_toolbar = None + self.layout_toolbar_actions = [] + + + # Set Window title and icon + if DEV is not None: + title = "Spyder %s (Python %s.%s)" % (__version__, + sys.version_info[0], + sys.version_info[1]) + else: + title = "Spyder (Python %s.%s)" % (sys.version_info[0], + sys.version_info[1]) + if DEBUG: + title += " [DEBUG MODE %d]" % DEBUG + if options.window_title is not None: + title += ' -- ' + options.window_title + + self.base_title = title + self.update_window_title() + resample = os.name != 'nt' + icon = ima.icon('spyder', resample=resample) + # Resampling SVG icon only on non-Windows platforms (see Issue 1314): + self.setWindowIcon(icon) + if set_windows_appusermodelid != None: + res = set_windows_appusermodelid() + debug_print("appusermodelid: " + str(res)) + + # Setting QTimer if running in travis + test_travis = os.environ.get('TEST_CI_APP', None) + if test_travis is not None: + global MAIN_APP + timer_shutdown_time = 30000 + self.timer_shutdown = QTimer(self) + self.timer_shutdown.timeout.connect(MAIN_APP.quit) + self.timer_shutdown.start(timer_shutdown_time) + + # Showing splash screen + self.splash = SPLASH + if CONF.get('main', 'current_version', '') != __version__: + CONF.set('main', 'current_version', __version__) + # Execute here the actions to be performed only once after + # each update (there is nothing there for now, but it could + # be useful some day...) + + # List of satellite widgets (registered in add_dockwidget): + self.widgetlist = [] + + # Flags used if closing() is called by the exit() shell command + self.already_closed = False + self.is_starting_up = True + self.is_setting_up = True + + self.dockwidgets_locked = CONF.get('main', 'panes_locked') + self.floating_dockwidgets = [] + self.window_size = None + self.window_position = None + self.state_before_maximizing = None + self.current_quick_layout = None + self.previous_layout_settings = None # TODO: related to quick layouts + self.last_plugin = None + self.fullscreen_flag = None # isFullscreen does not work as expected + # The following flag remember the maximized state even when + # the window is in fullscreen mode: + self.maximized_flag = None + + # Track which console plugin type had last focus + # True: Console plugin + # False: IPython console plugin + self.last_console_plugin_focus_was_python = True + + # To keep track of the last focused widget + self.last_focused_widget = None + + # Server to open external files on a single instance + self.open_files_server = socket.socket(socket.AF_INET, + socket.SOCK_STREAM, + socket.IPPROTO_TCP) + + self.apply_settings() + self.debug_print("End of MainWindow constructor") + + def debug_print(self, message): + """Debug prints""" + debug_print(message) + + #---- Window setup + def create_toolbar(self, title, object_name, iconsize=24): + """Create and return toolbar with *title* and *object_name*""" + toolbar = self.addToolBar(title) + toolbar.setObjectName(object_name) + toolbar.setIconSize(QSize(iconsize, iconsize)) + self.toolbarslist.append(toolbar) + return toolbar + + def setup(self): + """Setup main window""" + self.debug_print("*** Start of MainWindow setup ***") + self.debug_print(" ..core actions") + self.close_dockwidget_action = create_action(self, + icon=ima.icon('DialogCloseButton'), + text=_("Close current pane"), + triggered=self.close_current_dockwidget, + context=Qt.ApplicationShortcut) + self.register_shortcut(self.close_dockwidget_action, "_", + "Close pane") + self.lock_dockwidgets_action = create_action(self, _("Lock panes"), + toggled=self.toggle_lock_dockwidgets, + context=Qt.ApplicationShortcut) + self.register_shortcut(self.lock_dockwidgets_action, "_", + "Lock unlock panes") + # custom layouts shortcuts + self.toggle_next_layout_action = create_action(self, + _("Use next layout"), + triggered=self.toggle_next_layout, + context=Qt.ApplicationShortcut) + self.toggle_previous_layout_action = create_action(self, + _("Use previous layout"), + triggered=self.toggle_previous_layout, + context=Qt.ApplicationShortcut) + self.register_shortcut(self.toggle_next_layout_action, "_", + "Use next layout") + self.register_shortcut(self.toggle_previous_layout_action, "_", + "Use previous layout") + + def create_edit_action(text, tr_text, icon): + textseq = text.split(' ') + method_name = textseq[0].lower()+"".join(textseq[1:]) + action = create_action(self, tr_text, + icon=icon, + triggered=self.global_callback, + data=method_name, + context=Qt.WidgetShortcut) + self.register_shortcut(action, "Editor", text) + return action + + self.undo_action = create_edit_action('Undo', _('Undo'), + ima.icon('undo')) + self.redo_action = create_edit_action('Redo', _('Redo'), + ima.icon('redo')) + self.copy_action = create_edit_action('Copy', _('Copy'), + ima.icon('editcopy')) + self.cut_action = create_edit_action('Cut', _('Cut'), + ima.icon('editcut')) + self.paste_action = create_edit_action('Paste', _('Paste'), + ima.icon('editpaste')) + self.selectall_action = create_edit_action("Select All", + _("Select All"), + ima.icon('selectall')) + + self.edit_menu_actions = [self.undo_action, self.redo_action, + None, self.cut_action, self.copy_action, + self.paste_action, self.selectall_action] + + namespace = None + self.debug_print(" ..toolbars") + # File menu/toolbar + self.file_menu = self.menuBar().addMenu(_("&File")) + self.file_toolbar = self.create_toolbar(_("File toolbar"), + "file_toolbar") + + # Edit menu/toolbar + self.edit_menu = self.menuBar().addMenu(_("&Edit")) + self.edit_toolbar = self.create_toolbar(_("Edit toolbar"), + "edit_toolbar") + + # Search menu/toolbar + self.search_menu = self.menuBar().addMenu(_("&Search")) + self.search_toolbar = self.create_toolbar(_("Search toolbar"), + "search_toolbar") + + # Source menu/toolbar + self.source_menu = self.menuBar().addMenu(_("Sour&ce")) + self.source_toolbar = self.create_toolbar(_("Source toolbar"), + "source_toolbar") + + # Run menu/toolbar + self.run_menu = self.menuBar().addMenu(_("&Run")) + self.run_toolbar = self.create_toolbar(_("Run toolbar"), + "run_toolbar") + + # Debug menu/toolbar + self.debug_menu = self.menuBar().addMenu(_("&Debug")) + self.debug_toolbar = self.create_toolbar(_("Debug toolbar"), + "debug_toolbar") + + # Consoles menu/toolbar + self.consoles_menu = self.menuBar().addMenu(_("C&onsoles")) + + # Projects menu + self.projects_menu = self.menuBar().addMenu(_("&Projects")) + + # Tools menu + self.tools_menu = self.menuBar().addMenu(_("&Tools")) + + # View menu + self.view_menu = self.menuBar().addMenu(_("&View")) + + # Help menu + self.help_menu = self.menuBar().addMenu(_("&Help")) + + # Status bar + status = self.statusBar() + status.setObjectName("StatusBar") + status.showMessage(_("Welcome to Spyder!"), 5000) + + + self.debug_print(" ..tools") + # Tools + External Tools + prefs_action = create_action(self, _("Pre&ferences"), + icon=ima.icon('configure'), + triggered=self.edit_preferences, + context=Qt.ApplicationShortcut) + self.register_shortcut(prefs_action, "_", "Preferences", + add_sc_to_tip=True) + spyder_path_action = create_action(self, + _("PYTHONPATH manager"), + None, icon=ima.icon('pythonpath'), + triggered=self.path_manager_callback, + tip=_("Python Path Manager"), + menurole=QAction.ApplicationSpecificRole) + update_modules_action = create_action(self, + _("Update module names list"), + triggered=lambda: + module_completion.reset(), + tip=_("Refresh list of module names " + "available in PYTHONPATH")) + reset_spyder_action = create_action( + self, _("Reset Spyder to factory defaults"), + triggered=self.reset_spyder) + self.tools_menu_actions = [prefs_action, spyder_path_action] + if WinUserEnvDialog is not None: + winenv_action = create_action(self, + _("Current user environment variables..."), + icon='win_env.png', + tip=_("Show and edit current user environment " + "variables in Windows registry " + "(i.e. for all sessions)"), + triggered=self.win_env) + self.tools_menu_actions.append(winenv_action) + self.tools_menu_actions += [reset_spyder_action, None, + update_modules_action] + + # External Tools submenu + self.external_tools_menu = QMenu(_("External Tools")) + self.external_tools_menu_actions = [] + # Python(x,y) launcher + self.xy_action = create_action(self, + _("Python(x,y) launcher"), + icon=get_icon('pythonxy.png'), + triggered=lambda: + programs.run_python_script('xy', 'xyhome')) + if os.name == 'nt' and is_module_installed('xy'): + self.external_tools_menu_actions.append(self.xy_action) + # WinPython control panel + self.wp_action = create_action(self, _("WinPython control panel"), + icon=get_icon('winpython.svg'), + triggered=lambda: + programs.run_python_script('winpython', 'controlpanel')) + if os.name == 'nt' and is_module_installed('winpython'): + self.external_tools_menu_actions.append(self.wp_action) + # Qt-related tools + additact = [] + for name in ("designer-qt4", "designer"): + qtdact = create_program_action(self, _("Qt Designer"), + name, 'qtdesigner.png') + if qtdact: + break + for name in ("linguist-qt4", "linguist"): + qtlact = create_program_action(self, _("Qt Linguist"), + "linguist", 'qtlinguist.png') + if qtlact: + break + args = ['-no-opengl'] if os.name == 'nt' else [] + qteact = create_python_script_action(self, + _("Qt examples"), 'qt.png', "PyQt4", + osp.join("examples", "demos", + "qtdemo", "qtdemo"), args) + for act in (qtdact, qtlact, qteact): + if act: + additact.append(act) + if additact and (is_module_installed('winpython') or \ + is_module_installed('xy')): + self.external_tools_menu_actions += [None] + additact + + # Guidata and Sift + self.debug_print(" ..sift?") + gdgq_act = [] + # Guidata and Guiqwt don't support PyQt5 yet and they fail + # with an AssertionError when imported using those bindings + # (see issue 2274) + try: + from guidata import configtools + from guidata import config # analysis:ignore + guidata_icon = configtools.get_icon('guidata.svg') + guidata_act = create_python_script_action(self, + _("guidata examples"), guidata_icon, + "guidata", + osp.join("tests", "__init__")) + gdgq_act += [guidata_act] + except (ImportError, AssertionError): + pass + try: + from guidata import configtools + from guiqwt import config # analysis:ignore + guiqwt_icon = configtools.get_icon('guiqwt.svg') + guiqwt_act = create_python_script_action(self, + _("guiqwt examples"), guiqwt_icon, "guiqwt", + osp.join("tests", "__init__")) + if guiqwt_act: + gdgq_act += [guiqwt_act] + sift_icon = configtools.get_icon('sift.svg') + sift_act = create_python_script_action(self, _("Sift"), + sift_icon, "guiqwt", osp.join("tests", "sift")) + if sift_act: + gdgq_act += [sift_act] + except (ImportError, AssertionError): + pass + if gdgq_act: + self.external_tools_menu_actions += [None] + gdgq_act + + # ViTables + vitables_act = create_program_action(self, _("ViTables"), + "vitables", 'vitables.png') + if vitables_act: + self.external_tools_menu_actions += [None, vitables_act] + + # Maximize current plugin + self.maximize_action = create_action(self, '', + triggered=self.maximize_dockwidget, + context=Qt.ApplicationShortcut) + self.register_shortcut(self.maximize_action, "_", "Maximize pane") + self.__update_maximize_action() + + # Fullscreen mode + self.fullscreen_action = create_action(self, + _("Fullscreen mode"), + triggered=self.toggle_fullscreen, + context=Qt.ApplicationShortcut) + self.register_shortcut(self.fullscreen_action, "_", + "Fullscreen mode", add_sc_to_tip=True) + + # Main toolbar + self.main_toolbar_actions = [self.maximize_action, + self.fullscreen_action, + None, + prefs_action, spyder_path_action] + + self.main_toolbar = self.create_toolbar(_("Main toolbar"), + "main_toolbar") + + # Internal console plugin + self.debug_print(" ..plugin: internal console") + from spyder.plugins.console import Console + self.console = Console(self, namespace, exitfunc=self.closing, + profile=self.profile, + multithreaded=self.multithreaded, + message=_("Spyder Internal Console\n\n" + "This console is used to report application\n" + "internal errors and to inspect Spyder\n" + "internals with the following commands:\n" + " spy.app, spy.window, dir(spy)\n\n" + "Please don't use it to run your code\n\n")) + self.console.register_plugin() + + # Working directory plugin + self.debug_print(" ..plugin: working directory") + from spyder.plugins.workingdirectory import WorkingDirectory + self.workingdirectory = WorkingDirectory(self, self.init_workdir, main=self) + self.workingdirectory.register_plugin() + self.toolbarslist.append(self.workingdirectory) + + # Help plugin + if CONF.get('help', 'enable'): + self.set_splash(_("Loading help...")) + from spyder.plugins.help import Help + self.help = Help(self) + self.help.register_plugin() + + # Outline explorer widget + if CONF.get('outline_explorer', 'enable'): + self.set_splash(_("Loading outline explorer...")) + from spyder.plugins.outlineexplorer import OutlineExplorer + fullpath_sorting = CONF.get('editor', 'fullpath_sorting', True) + self.outlineexplorer = OutlineExplorer(self, + fullpath_sorting=fullpath_sorting) + self.outlineexplorer.register_plugin() + + # Editor plugin + self.set_splash(_("Loading editor...")) + from spyder.plugins.editor import Editor + self.editor = Editor(self) + self.editor.register_plugin() + + # Populating file menu entries + quit_action = create_action(self, _("&Quit"), + icon=ima.icon('exit'), + tip=_("Quit"), + triggered=self.console.quit, + context=Qt.ApplicationShortcut) + self.register_shortcut(quit_action, "_", "Quit") + restart_action = create_action(self, _("&Restart"), + icon=ima.icon('restart'), + tip=_("Restart"), + triggered=self.restart, + context=Qt.ApplicationShortcut) + self.register_shortcut(restart_action, "_", "Restart") + + self.file_menu_actions += [None, restart_action, quit_action] + self.set_splash("") + + self.debug_print(" ..widgets") + # Find in files + if CONF.get('find_in_files', 'enable'): + from spyder.plugins.findinfiles import FindInFiles + self.findinfiles = FindInFiles(self) + self.findinfiles.register_plugin() + + # Explorer + if CONF.get('explorer', 'enable'): + self.set_splash(_("Loading file explorer...")) + from spyder.plugins.explorer import Explorer + self.explorer = Explorer(self) + self.explorer.register_plugin() + + # History log widget + if CONF.get('historylog', 'enable'): + self.set_splash(_("Loading history plugin...")) + from spyder.plugins.history import HistoryLog + self.historylog = HistoryLog(self) + self.historylog.register_plugin() + + # Online help widget + try: # Qt >= v4.4 + from spyder.plugins.onlinehelp import OnlineHelp + except ImportError: # Qt < v4.4 + OnlineHelp = None # analysis:ignore + if CONF.get('onlinehelp', 'enable') and OnlineHelp is not None: + self.set_splash(_("Loading online help...")) + self.onlinehelp = OnlineHelp(self) + self.onlinehelp.register_plugin() + + # Project explorer widget + self.set_splash(_("Loading project explorer...")) + from spyder.plugins.projects import Projects + self.projects = Projects(self) + self.projects.register_plugin() + self.project_path = self.projects.get_pythonpath(at_start=True) + + # External console + self.set_splash(_("Loading external console...")) + from spyder.plugins.externalconsole import ExternalConsole + self.extconsole = ExternalConsole(self) + self.extconsole.register_plugin() + + # Namespace browser + self.set_splash(_("Loading namespace browser...")) + from spyder.plugins.variableexplorer import VariableExplorer + self.variableexplorer = VariableExplorer(self) + self.variableexplorer.register_plugin() + + # IPython console + if QTCONSOLE_INSTALLED: + self.set_splash(_("Loading IPython console...")) + from spyder.plugins.ipythonconsole import IPythonConsole + self.ipyconsole = IPythonConsole(self) + self.ipyconsole.register_plugin() + + self.set_splash(_("Setting up main window...")) + + # Help menu + dep_action = create_action(self, _("Dependencies..."), + triggered=self.show_dependencies, + icon=ima.icon('advanced')) + report_action = create_action(self, + _("Report issue..."), + icon=ima.icon('bug'), + triggered=self.report_issue) + support_action = create_action(self, + _("Spyder support..."), + triggered=self.google_group) + self.check_updates_action = create_action(self, + _("Check for updates..."), + triggered=self.check_updates) + + # Spyder documentation + doc_path = get_module_data_path('spyder', relpath="doc", + attr_name='DOCPATH') + # * Trying to find the chm doc + spyder_doc = osp.join(doc_path, "Spyderdoc.chm") + if not osp.isfile(spyder_doc): + spyder_doc = osp.join(doc_path, os.pardir, "Spyderdoc.chm") + # * Trying to find the html doc + if not osp.isfile(spyder_doc): + spyder_doc = osp.join(doc_path, "index.html") + # * Trying to find the development-version html doc + if not osp.isfile(spyder_doc): + spyder_doc = osp.join(get_module_source_path('spyder'), + os.pardir, 'build', 'lib', 'spyder', + 'doc', "index.html") + # * If we totally fail, point to our web build + if not osp.isfile(spyder_doc): + spyder_doc = 'http://pythonhosted.org/spyder' + else: + spyder_doc = file_uri(spyder_doc) + doc_action = create_action( self, _("Spyder documentation"), shortcut="F1", + icon=ima.icon('DialogHelpButton'), + triggered=lambda : programs.start_file(spyder_doc)) + + if self.help is not None: + tut_action = create_action(self, _("Spyder tutorial"), + triggered=self.help.show_tutorial) + else: + tut_action = None + + #----- Tours + self.tour = tour.AnimatedTour(self) + self.tours_menu = QMenu(_("Interactive tours")) + self.tour_menu_actions = [] + # TODO: Only show intro tour for now. When we are close to finish + # 3.0, we will finish and show the other tour + self.tours_available = tour.get_tours(0) + + for i, tour_available in enumerate(self.tours_available): + self.tours_available[i]['last'] = 0 + tour_name = tour_available['name'] + + def trigger(i=i, self=self): # closure needed! + return lambda: self.show_tour(i) + + temp_action = create_action(self, tour_name, tip="", + triggered=trigger()) + self.tour_menu_actions += [temp_action] + + self.tours_menu.addActions(self.tour_menu_actions) + + if not DEV: + self.tours_menu = None + + self.help_menu_actions = [doc_action, tut_action, self.tours_menu, + None, report_action, dep_action, + self.check_updates_action, support_action, + None] + # Python documentation + if get_python_doc_path() is not None: + pydoc_act = create_action(self, _("Python documentation"), + triggered=lambda: + programs.start_file(get_python_doc_path())) + self.help_menu_actions.append(pydoc_act) + # IPython documentation + if self.ipyconsole is not None and self.help is not None: + ipython_menu = QMenu(_("IPython documentation"), self) + intro_action = create_action(self, _("Intro to IPython"), + triggered=self.ipyconsole.show_intro) + quickref_action = create_action(self, _("Quick reference"), + triggered=self.ipyconsole.show_quickref) + guiref_action = create_action(self, _("Console help"), + triggered=self.ipyconsole.show_guiref) + add_actions(ipython_menu, (intro_action, guiref_action, + quickref_action)) + self.help_menu_actions.append(ipython_menu) + # Windows-only: documentation located in sys.prefix/Doc + ipm_actions = [] + def add_ipm_action(text, path): + """Add installed Python module doc action to help submenu""" + # QAction.triggered works differently for PySide and PyQt + path = file_uri(path) + if not API == 'pyside': + slot=lambda _checked, path=path: programs.start_file(path) + else: + slot=lambda path=path: programs.start_file(path) + action = create_action(self, text, + icon='%s.png' % osp.splitext(path)[1][1:], + triggered=slot) + ipm_actions.append(action) + sysdocpth = osp.join(sys.prefix, 'Doc') + if osp.isdir(sysdocpth): # exists on Windows, except frozen dist. + for docfn in os.listdir(sysdocpth): + pt = r'([a-zA-Z\_]*)(doc)?(-dev)?(-ref)?(-user)?.(chm|pdf)' + match = re.match(pt, docfn) + if match is not None: + pname = match.groups()[0] + if pname not in ('Python', ): + add_ipm_action(pname, osp.join(sysdocpth, docfn)) + # Documentation provided by Python(x,y), if available + try: + from xy.config import DOC_PATH as xy_doc_path + xydoc = osp.join(xy_doc_path, "Libraries") + def add_xydoc(text, pathlist): + for path in pathlist: + if osp.exists(path): + add_ipm_action(text, path) + break + add_xydoc(_("Python(x,y) documentation folder"), + [xy_doc_path]) + add_xydoc(_("IPython documentation"), + [osp.join(xydoc, "IPython", "ipythondoc.chm")]) + add_xydoc(_("guidata documentation"), + [osp.join(xydoc, "guidata", "guidatadoc.chm"), + r"D:\Python\guidata\build\doc_chm\guidatadoc.chm"]) + add_xydoc(_("guiqwt documentation"), + [osp.join(xydoc, "guiqwt", "guiqwtdoc.chm"), + r"D:\Python\guiqwt\build\doc_chm\guiqwtdoc.chm"]) + add_xydoc(_("Matplotlib documentation"), + [osp.join(xydoc, "matplotlib", "Matplotlibdoc.chm"), + osp.join(xydoc, "matplotlib", "Matplotlib.pdf")]) + add_xydoc(_("NumPy documentation"), + [osp.join(xydoc, "NumPy", "numpy.chm")]) + add_xydoc(_("NumPy reference guide"), + [osp.join(xydoc, "NumPy", "numpy-ref.pdf")]) + add_xydoc(_("NumPy user guide"), + [osp.join(xydoc, "NumPy", "numpy-user.pdf")]) + add_xydoc(_("SciPy documentation"), + [osp.join(xydoc, "SciPy", "scipy.chm"), + osp.join(xydoc, "SciPy", "scipy-ref.pdf")]) + except (ImportError, KeyError, RuntimeError): + pass + # Installed Python modules submenu (Windows only) + if ipm_actions: + pymods_menu = QMenu(_("Installed Python modules"), self) + add_actions(pymods_menu, ipm_actions) + self.help_menu_actions.append(pymods_menu) + # Online documentation + web_resources = QMenu(_("Online documentation")) + webres_actions = create_module_bookmark_actions(self, + self.BOOKMARKS) + webres_actions.insert(2, None) + webres_actions.insert(5, None) + add_actions(web_resources, webres_actions) + self.help_menu_actions.append(web_resources) + # Qt assistant link + if sys.platform.startswith('linux') and not PYQT5: + qta_exe = "assistant-qt4" + else: + qta_exe = "assistant" + qta_act = create_program_action(self, _("Qt documentation"), + qta_exe) + if qta_act: + self.help_menu_actions += [qta_act, None] + # About Spyder + about_action = create_action(self, + _("About %s...") % "Spyder", + icon=ima.icon('MessageBoxInformation'), + triggered=self.about) + self.help_menu_actions += [None, about_action] + + # Status bar widgets + from spyder.widgets.status import MemoryStatus, CPUStatus + self.mem_status = MemoryStatus(self, status) + self.cpu_status = CPUStatus(self, status) + self.apply_statusbar_settings() + + # Third-party plugins + for mod in get_spyderplugins_mods(): + try: + plugin = mod.PLUGIN_CLASS(self) + self.thirdparty_plugins.append(plugin) + plugin.register_plugin() + except Exception as error: + print("%s: %s" % (mod, str(error)), file=STDERR) + traceback.print_exc(file=STDERR) + + + #----- View + # View menu + self.plugins_menu = QMenu(_("Panes"), self) + + self.toolbars_menu = QMenu(_("Toolbars"), self) + self.quick_layout_menu = QMenu(_("Window layouts"), self) + self.quick_layout_set_menu() + + self.view_menu.addMenu(self.plugins_menu) # Panes + add_actions(self.view_menu, (self.lock_dockwidgets_action, + self.close_dockwidget_action, + self.maximize_action, + None)) + self.show_toolbars_action = create_action(self, + _("Show toolbars"), + triggered=self.show_toolbars, + context=Qt.ApplicationShortcut) + self.register_shortcut(self.show_toolbars_action, "_", + "Show toolbars") + self.view_menu.addMenu(self.toolbars_menu) + self.view_menu.addAction(self.show_toolbars_action) + add_actions(self.view_menu, (None, + self.quick_layout_menu, + self.toggle_previous_layout_action, + self.toggle_next_layout_action, + None, + self.fullscreen_action)) + if set_attached_console_visible is not None: + cmd_act = create_action(self, + _("Attached console window (debugging)"), + toggled=set_attached_console_visible) + cmd_act.setChecked(is_attached_console_visible()) + add_actions(self.view_menu, (None, cmd_act)) + + # Adding external tools action to "Tools" menu + if self.external_tools_menu_actions: + external_tools_act = create_action(self, _("External Tools")) + external_tools_act.setMenu(self.external_tools_menu) + self.tools_menu_actions += [None, external_tools_act] + + # Filling out menu/toolbar entries: + add_actions(self.file_menu, self.file_menu_actions) + add_actions(self.edit_menu, self.edit_menu_actions) + add_actions(self.search_menu, self.search_menu_actions) + add_actions(self.source_menu, self.source_menu_actions) + add_actions(self.run_menu, self.run_menu_actions) + add_actions(self.debug_menu, self.debug_menu_actions) + add_actions(self.consoles_menu, self.consoles_menu_actions) + add_actions(self.projects_menu, self.projects_menu_actions) + add_actions(self.tools_menu, self.tools_menu_actions) + add_actions(self.external_tools_menu, + self.external_tools_menu_actions) + add_actions(self.help_menu, self.help_menu_actions) + + add_actions(self.main_toolbar, self.main_toolbar_actions) + add_actions(self.file_toolbar, self.file_toolbar_actions) + add_actions(self.edit_toolbar, self.edit_toolbar_actions) + add_actions(self.search_toolbar, self.search_toolbar_actions) + add_actions(self.source_toolbar, self.source_toolbar_actions) + add_actions(self.debug_toolbar, self.debug_toolbar_actions) + add_actions(self.run_toolbar, self.run_toolbar_actions) + + # Apply all defined shortcuts (plugins + 3rd-party plugins) + self.apply_shortcuts() + + # Emitting the signal notifying plugins that main window menu and + # toolbar actions are all defined: + self.all_actions_defined.emit() + + # Window set-up + self.debug_print("Setting up window...") + self.setup_layout(default=False) + + # Show and hide shortcuts in menus for Mac. + # This is a workaround because we can't disable shortcuts + # by setting context=Qt.WidgetShortcut there + if sys.platform == 'darwin': + for name in ['file', 'search', 'source', 'run', 'debug', + 'plugins']: + menu_object = getattr(self, name + '_menu') + menu_object.aboutToShow.connect( + lambda name=name: self.show_shortcuts(name)) + menu_object.aboutToHide.connect( + lambda name=name: self.hide_shortcuts(name)) + + self.splash.hide() + + # Enabling tear off for all menus except help menu + if CONF.get('main', 'tear_off_menus'): + for child in self.menuBar().children(): + if isinstance(child, QMenu) and child != self.help_menu: + child.setTearOffEnabled(True) + + # Menu about to show + for child in self.menuBar().children(): + if isinstance(child, QMenu): + child.aboutToShow.connect(self.update_edit_menu) + + self.debug_print("*** End of MainWindow setup ***") + self.is_starting_up = False + + def post_visible_setup(self): + """Actions to be performed only after the main window's `show` method + was triggered""" + self.restore_scrollbar_position.emit() + + # Remove our temporary dir + atexit.register(self.remove_tmpdir) + + # [Workaround for Issue 880] + # QDockWidget objects are not painted if restored as floating + # windows, so we must dock them before showing the mainwindow, + # then set them again as floating windows here. + for widget in self.floating_dockwidgets: + widget.setFloating(True) + + # In MacOS X 10.7 our app is not displayed after initialized (I don't + # know why because this doesn't happen when started from the terminal), + # so we need to resort to this hack to make it appear. + if running_in_mac_app(): + idx = __file__.index(MAC_APP_NAME) + app_path = __file__[:idx] + subprocess.call(['open', app_path + MAC_APP_NAME]) + + # Server to maintain just one Spyder instance and open files in it if + # the user tries to start other instances with + # $ spyder foo.py + if CONF.get('main', 'single_instance') and not self.new_instance: + t = threading.Thread(target=self.start_open_files_server) + t.setDaemon(True) + t.start() + + # Connect the window to the signal emmited by the previous server + # when it gets a client connected to it + self.sig_open_external_file.connect(self.open_external_file) + + # Create Plugins and toolbars submenus + self.create_plugins_menu() + self.create_toolbars_menu() + + self.extconsole.setMinimumHeight(0) + + # Update toolbar visibility status + self.toolbars_visible = CONF.get('main', 'toolbars_visible') + self.load_last_visible_toolbars() + + # Update lock status of dockidgets (panes) + self.lock_dockwidgets_action.setChecked(self.dockwidgets_locked) + self.apply_panes_settings() + + # Hide Internal Console so that people don't use it instead of + # the External or IPython ones + if self.console.dockwidget.isVisible() and DEV is None: + self.console.toggle_view_action.setChecked(False) + self.console.dockwidget.hide() + + # Show Help and Consoles by default + plugins_to_show = [] + if self.help is not None: + plugins_to_show.append(self.help) + if self.ipyconsole is not None: + if self.ipyconsole.isvisible: + plugins_to_show += [self.extconsole, self.ipyconsole] + else: + plugins_to_show += [self.ipyconsole, self.extconsole] + else: + plugins_to_show += [self.extconsole] + for plugin in plugins_to_show: + if plugin.dockwidget.isVisible(): + plugin.dockwidget.raise_() + + # Show history file if no console is visible + ipy_visible = self.ipyconsole is not None and self.ipyconsole.isvisible + if not self.extconsole.isvisible and not ipy_visible: + self.historylog.add_history(get_conf_path('history.py')) + + # Load last project if a project was active when Spyder + # was closed + self.projects.reopen_last_project() + + # If no project is active, load last session + if self.projects.get_active_project() is None: + self.editor.setup_open_files() + + # Check for spyder updates + if DEV is None and CONF.get('main', 'check_updates_on_startup'): + self.give_updates_feedback = False + self.check_updates() + + # Show dialog with missing dependencies + self.report_missing_dependencies() + + self.is_setting_up = False + + def update_window_title(self): + """Update main spyder window title based on projects.""" + title = self.base_title + if self.projects is not None: + path = self.projects.get_active_project_path() + if path: + path = path.replace(get_home_dir(), '~') + title = '{0} - {1}'.format(path, title) + self.setWindowTitle(title) + + def report_missing_dependencies(self): + """Show a QMessageBox with a list of missing hard dependencies""" + missing_deps = dependencies.missing_dependencies() + if missing_deps: + QMessageBox.critical(self, _('Error'), + _("You have missing dependencies!" + "

%s

" + "Please install them to avoid this message." + "

" + "Note: Spyder could work without some of these " + "dependencies, however to have a smooth experience when " + "using Spyder we strongly recommend you to install " + "all the listed missing dependencies.

" + "Failing to install these dependencies might result in bugs. " + "Please be sure that any found bugs are not the direct " + "result of missing dependencies, prior to reporting a new " + "issue." + ) % missing_deps, QMessageBox.Ok) + + def load_window_settings(self, prefix, default=False, section='main'): + """Load window layout settings from userconfig-based configuration + with *prefix*, under *section* + default: if True, do not restore inner layout""" + get_func = CONF.get_default if default else CONF.get + window_size = get_func(section, prefix+'size') + prefs_dialog_size = get_func(section, prefix+'prefs_dialog_size') + if default: + hexstate = None + else: + hexstate = get_func(section, prefix+'state', None) + pos = get_func(section, prefix+'position') + is_maximized = get_func(section, prefix+'is_maximized') + is_fullscreen = get_func(section, prefix+'is_fullscreen') + return hexstate, window_size, prefs_dialog_size, pos, is_maximized, \ + is_fullscreen + + def get_window_settings(self): + """Return current window settings + Symetric to the 'set_window_settings' setter""" + window_size = (self.window_size.width(), self.window_size.height()) + is_fullscreen = self.isFullScreen() + if is_fullscreen: + is_maximized = self.maximized_flag + else: + is_maximized = self.isMaximized() + pos = (self.window_position.x(), self.window_position.y()) + prefs_dialog_size = (self.prefs_dialog_size.width(), + self.prefs_dialog_size.height()) + hexstate = qbytearray_to_str(self.saveState()) + return (hexstate, window_size, prefs_dialog_size, pos, is_maximized, + is_fullscreen) + + def set_window_settings(self, hexstate, window_size, prefs_dialog_size, + pos, is_maximized, is_fullscreen): + """Set window settings + Symetric to the 'get_window_settings' accessor""" + self.setUpdatesEnabled(False) + self.window_size = QSize(window_size[0], window_size[1]) # width,height + self.prefs_dialog_size = QSize(prefs_dialog_size[0], + prefs_dialog_size[1]) # width,height + self.window_position = QPoint(pos[0], pos[1]) # x,y + self.setWindowState(Qt.WindowNoState) + self.resize(self.window_size) + self.move(self.window_position) + + # Window layout + if hexstate: + self.restoreState( QByteArray().fromHex( + str(hexstate).encode('utf-8')) ) + # [Workaround for Issue 880] + # QDockWidget objects are not painted if restored as floating + # windows, so we must dock them before showing the mainwindow. + for widget in self.children(): + if isinstance(widget, QDockWidget) and widget.isFloating(): + self.floating_dockwidgets.append(widget) + widget.setFloating(False) + + # Is fullscreen? + if is_fullscreen: + self.setWindowState(Qt.WindowFullScreen) + self.__update_fullscreen_action() + + # Is maximized? + if is_fullscreen: + self.maximized_flag = is_maximized + elif is_maximized: + self.setWindowState(Qt.WindowMaximized) + self.setUpdatesEnabled(True) + + def save_current_window_settings(self, prefix, section='main'): + """Save current window settings with *prefix* in + the userconfig-based configuration, under *section*""" + win_size = self.window_size + prefs_size = self.prefs_dialog_size + + CONF.set(section, prefix+'size', (win_size.width(), win_size.height())) + CONF.set(section, prefix+'prefs_dialog_size', + (prefs_size.width(), prefs_size.height())) + CONF.set(section, prefix+'is_maximized', self.isMaximized()) + CONF.set(section, prefix+'is_fullscreen', self.isFullScreen()) + pos = self.window_position + CONF.set(section, prefix+'position', (pos.x(), pos.y())) + self.maximize_dockwidget(restore=True)# Restore non-maximized layout + qba = self.saveState() + CONF.set(section, prefix+'state', qbytearray_to_str(qba)) + CONF.set(section, prefix+'statusbar', + not self.statusBar().isHidden()) + + def tabify_plugins(self, first, second): + """Tabify plugin dockwigdets""" + self.tabifyDockWidget(first.dockwidget, second.dockwidget) + + # --- Layouts + def setup_layout(self, default=False): + """Setup window layout""" + prefix = 'window' + '/' + settings = self.load_window_settings(prefix, default) + hexstate = settings[0] + + self.first_spyder_run = False + if hexstate is None: + # First Spyder execution: + self.setWindowState(Qt.WindowMaximized) + self.first_spyder_run = True + self.setup_default_layouts('default', settings) + self.extconsole.setMinimumHeight(250) + + # Now that the initial setup is done, copy the window settings, + # except for the hexstate in the quick layouts sections for the + # default layouts. + # Order and name of the default layouts is found in config.py + section = 'quick_layouts' + get_func = CONF.get_default if default else CONF.get + order = get_func(section, 'order') + + # restore the original defaults if reset layouts is called + if default: + CONF.set(section, 'active', order) + CONF.set(section, 'order', order) + CONF.set(section, 'names', order) + + for index, name, in enumerate(order): + prefix = 'layout_{0}/'.format(index) + self.save_current_window_settings(prefix, section) + CONF.set(section, prefix+'state', None) + + # store the initial layout as the default in spyder + prefix = 'layout_default/' + section = 'quick_layouts' + self.save_current_window_settings(prefix, section) + self.current_quick_layout = 'default' + CONF.set(section, prefix+'state', None) + + # Regenerate menu + self.quick_layout_set_menu() + self.set_window_settings(*settings) + + for plugin in self.widgetlist: + try: + plugin.initialize_plugin_in_mainwindow_layout() + except Exception as error: + print("%s: %s" % (plugin, str(error)), file=STDERR) + traceback.print_exc(file=STDERR) + + def setup_default_layouts(self, index, settings): + """Setup default layouts when run for the first time""" + self.set_window_settings(*settings) + self.setUpdatesEnabled(False) + + # IMPORTANT: order has to be the same as defined in the config file + MATLAB, RSTUDIO, VERTICAL, HORIZONTAL = range(4) + + # define widgets locally + editor = self.editor + console_ipy = self.ipyconsole + console_ext = self.extconsole + console_int = self.console + outline = self.outlineexplorer + explorer_project = self.projects + explorer_file = self.explorer + explorer_variable = self.variableexplorer + history = self.historylog + finder = self.findinfiles + help_plugin = self.help + helper = self.onlinehelp + plugins = self.thirdparty_plugins + + global_hidden_widgets = [finder, console_int, explorer_project, + helper] + plugins + global_hidden_toolbars = [self.source_toolbar, self.edit_toolbar, + self.search_toolbar] + # Layout definition + # layouts are organized by columns, each colum is organized by rows + # widths have to add 1.0, height per column have to add 1.0 + # Spyder Default Initial Layout + s_layout = {'widgets': [ + # column 0 + [[explorer_project]], + # column 1 + [[editor]], + # column 2 + [[outline]], + # column 3 + [[help_plugin, explorer_variable, helper, explorer_file, + finder] + plugins, + [console_int, console_ext, console_ipy, history]] + ], + 'width fraction': [0.0, # column 0 width + 0.55, # column 1 width + 0.0, # column 2 width + 0.45], # column 3 width + 'height fraction': [[1.0], # column 0, row heights + [1.0], # column 1, row heights + [1.0], # column 2, row heights + [0.46, 0.54]], # column 3, row heights + 'hidden widgets': [outline], + 'hidden toolbars': [], + } + r_layout = {'widgets': [ + # column 0 + [[editor], + [console_ipy, console_ext, console_int]], + # column 1 + [[explorer_variable, history, outline, finder] + plugins, + [explorer_file, explorer_project, help_plugin, helper]] + ], + 'width fraction': [0.55, # column 0 width + 0.45], # column 1 width + 'height fraction': [[0.55, 0.45], # column 0, row heights + [0.55, 0.45]], # column 1, row heights + 'hidden widgets': [outline], + 'hidden toolbars': [], + } + # Matlab + m_layout = {'widgets': [ + # column 0 + [[explorer_file, explorer_project], + [outline]], + # column 1 + [[editor], + [console_ipy, console_ext, console_int]], + # column 2 + [[explorer_variable, finder] + plugins, + [history, help_plugin, helper]] + ], + 'width fraction': [0.20, # column 0 width + 0.40, # column 1 width + 0.40], # column 2 width + 'height fraction': [[0.55, 0.45], # column 0, row heights + [0.55, 0.45], # column 1, row heights + [0.55, 0.45]], # column 2, row heights + 'hidden widgets': [], + 'hidden toolbars': [], + } + # Vertically split + v_layout = {'widgets': [ + # column 0 + [[editor], + [console_ipy, console_ext, console_int, explorer_file, + explorer_project, help_plugin, explorer_variable, + history, outline, finder, helper] + plugins] + ], + 'width fraction': [1.0], # column 0 width + 'height fraction': [[0.55, 0.45]], # column 0, row heights + 'hidden widgets': [outline], + 'hidden toolbars': [], + } + # Horizontally split + h_layout = {'widgets': [ + # column 0 + [[editor]], + # column 1 + [[console_ipy, console_ext, console_int, explorer_file, + explorer_project, help_plugin, explorer_variable, + history, outline, finder, helper] + plugins] + ], + 'width fraction': [0.55, # column 0 width + 0.45], # column 1 width + 'height fraction': [[1.0], # column 0, row heights + [1.0]], # column 1, row heights + 'hidden widgets': [outline], + 'hidden toolbars': [] + } + + # Layout selection + layouts = {'default': s_layout, + RSTUDIO: r_layout, + MATLAB: m_layout, + VERTICAL: v_layout, + HORIZONTAL: h_layout} + + layout = layouts[index] + + widgets_layout = layout['widgets'] + widgets = [] + for column in widgets_layout : + for row in column: + for widget in row: + if widget is not None: + widgets.append(widget) + + # Make every widget visible + for widget in widgets: + widget.toggle_view(True) + action = widget.toggle_view_action + action.setChecked(widget.dockwidget.isVisible()) + + # Set the widgets horizontally + for i in range(len(widgets) - 1): + first, second = widgets[i], widgets[i+1] + if first is not None and second is not None: + self.splitDockWidget(first.dockwidget, second.dockwidget, + Qt.Horizontal) + + # Arrange rows vertically + for column in widgets_layout : + for i in range(len(column) - 1): + first_row, second_row = column[i], column[i+1] + if first_row is not None and second_row is not None: + self.splitDockWidget(first_row[0].dockwidget, + second_row[0].dockwidget, + Qt.Vertical) + # Tabify + for column in widgets_layout : + for row in column: + for i in range(len(row) - 1): + first, second = row[i], row[i+1] + if first is not None and second is not None: + self.tabify_plugins(first, second) + + # Raise front widget per row + row[0].dockwidget.show() + row[0].dockwidget.raise_() + + # Hide toolbars + hidden_toolbars = global_hidden_toolbars + layout['hidden toolbars'] + for toolbar in hidden_toolbars: + if toolbar is not None: + toolbar.close() + + # Hide widgets + hidden_widgets = global_hidden_widgets + layout['hidden widgets'] + for widget in hidden_widgets: + if widget is not None: + widget.dockwidget.close() + + # set the width and height + self._layout_widget_info = [] + width, height = self.window_size.width(), self.window_size.height() + + # fix column width +# for c in range(len(widgets_layout)): +# widget = widgets_layout[c][0][0].dockwidget +# min_width, max_width = widget.minimumWidth(), widget.maximumWidth() +# info = {'widget': widget, +# 'min width': min_width, +# 'max width': max_width} +# self._layout_widget_info.append(info) +# new_width = int(layout['width fraction'][c] * width * 0.95) +# widget.setMinimumWidth(new_width) +# widget.setMaximumWidth(new_width) +# widget.updateGeometry() +# print(c, widgets_layout[c][0][0], new_width) + + # fix column height + for c, column in enumerate(widgets_layout): + for r in range(len(column) - 1): + widget = column[r][0] + dockwidget = widget.dockwidget + dock_min_h = dockwidget.minimumHeight() + dock_max_h = dockwidget.maximumHeight() + info = {'widget': widget, + 'dock min height': dock_min_h, + 'dock max height': dock_max_h} + self._layout_widget_info.append(info) + # The 0.95 factor is to adjust height based on usefull + # estimated area in the window + new_height = int(layout['height fraction'][c][r]*height*0.95) + dockwidget.setMinimumHeight(new_height) + dockwidget.setMaximumHeight(new_height) + + self._custom_layout_timer = QTimer(self) + self._custom_layout_timer.timeout.connect(self.layout_fix_timer) + self._custom_layout_timer.setSingleShot(True) + self._custom_layout_timer.start(5000) + + def layout_fix_timer(self): + """Fixes the height of docks after a new layout is set.""" + info = self._layout_widget_info + for i in info: + dockwidget = i['widget'].dockwidget + if 'dock min width' in i: + dockwidget.setMinimumWidth(i['dock min width']) + dockwidget.setMaximumWidth(i['dock max width']) + if 'dock min height' in i: + dockwidget.setMinimumHeight(i['dock min height']) + dockwidget.setMaximumHeight(i['dock max height']) + dockwidget.updateGeometry() + + self.setUpdatesEnabled(True) + + @Slot() + def toggle_previous_layout(self): + """ """ + self.toggle_layout('previous') + + @Slot() + def toggle_next_layout(self): + """ """ + self.toggle_layout('next') + + def toggle_layout(self, direction='next'): + """ """ + get = CONF.get + names = get('quick_layouts', 'names') + order = get('quick_layouts', 'order') + active = get('quick_layouts', 'active') + + if len(active) == 0: + return + + layout_index = ['default'] + for name in order: + if name in active: + layout_index.append(names.index(name)) + + current_layout = self.current_quick_layout + dic = {'next': 1, 'previous': -1} + + if current_layout is None: + # Start from default + current_layout = 'default' + + if current_layout in layout_index: + current_index = layout_index.index(current_layout) + else: + current_index = 0 + + new_index = (current_index + dic[direction]) % len(layout_index) + self.quick_layout_switch(layout_index[new_index]) + + def quick_layout_set_menu(self): + """ """ + get = CONF.get + names = get('quick_layouts', 'names') + order = get('quick_layouts', 'order') + active = get('quick_layouts', 'active') + + ql_actions = [] + + ql_actions = [create_action(self, _('Spyder Default Layout'), + triggered=lambda: + self.quick_layout_switch('default'))] + for name in order: + if name in active: + index = names.index(name) + + # closure required so lambda works with the default parameter + def trigger(i=index, self=self): + return lambda: self.quick_layout_switch(i) + + qli_act = create_action(self, name, triggered=trigger()) + # closure above replaces the following which stopped working + # qli_act = create_action(self, name, triggered=lambda i=index: + # self.quick_layout_switch(i) + + ql_actions += [qli_act] + + self.ql_save = create_action(self, _("Save current layout"), + triggered=lambda: + self.quick_layout_save(), + context=Qt.ApplicationShortcut) + self.ql_preferences = create_action(self, _("Layout preferences"), + triggered=lambda: + self.quick_layout_settings(), + context=Qt.ApplicationShortcut) + self.ql_reset = create_action(self, _('Reset to spyder default'), + triggered=self.reset_window_layout) + + self.register_shortcut(self.ql_save, "_", "Save current layout") + self.register_shortcut(self.ql_preferences, "_", "Layout preferences") + + ql_actions += [None] + ql_actions += [self.ql_save, self.ql_preferences, self.ql_reset] + + self.quick_layout_menu.clear() + add_actions(self.quick_layout_menu, ql_actions) + + if len(order) == 0: + self.ql_preferences.setEnabled(False) + else: + self.ql_preferences.setEnabled(True) + + @Slot() + def reset_window_layout(self): + """Reset window layout to default""" + answer = QMessageBox.warning(self, _("Warning"), + _("Window layout will be reset to default settings: " + "this affects window position, size and dockwidgets.\n" + "Do you want to continue?"), + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + self.setup_layout(default=True) + + def quick_layout_save(self): + """Save layout dialog""" + get = CONF.get + set_ = CONF.set + names = get('quick_layouts', 'names') + order = get('quick_layouts', 'order') + active = get('quick_layouts', 'active') + + dlg = self.dialog_layout_save(self, names) + + if dlg.exec_(): + name = dlg.combo_box.currentText() + + if name in names: + answer = QMessageBox.warning(self, _("Warning"), + _("Layout %s will be \ + overwritten. Do you want to \ + continue?") % name, + QMessageBox.Yes | QMessageBox.No) + index = order.index(name) + else: + answer = True + if None in names: + index = names.index(None) + names[index] = name + else: + index = len(names) + names.append(name) + order.append(name) + + # Always make active a new layout even if it overwrites an inactive + # layout + if name not in active: + active.append(name) + + if answer: + self.save_current_window_settings('layout_{}/'.format(index), + section='quick_layouts') + set_('quick_layouts', 'names', names) + set_('quick_layouts', 'order', order) + set_('quick_layouts', 'active', active) + self.quick_layout_set_menu() + + def quick_layout_settings(self): + """Layout settings dialog""" + get = CONF.get + set_ = CONF.set + + section = 'quick_layouts' + + names = get(section, 'names') + order = get(section, 'order') + active = get(section, 'active') + + dlg = self.dialog_layout_settings(self, names, order, active) + if dlg.exec_(): + set_(section, 'names', dlg.names) + set_(section, 'order', dlg.order) + set_(section, 'active', dlg.active) + self.quick_layout_set_menu() + + def quick_layout_switch(self, index): + """Switch to quick layout number *index*""" + section = 'quick_layouts' + + try: + settings = self.load_window_settings('layout_{}/'.format(index), + section=section) + (hexstate, window_size, prefs_dialog_size, pos, is_maximized, + is_fullscreen) = settings + + # The defaults layouts will alwyas be regenerated unless there was + # an overwrite, either by rewriting with same name, or by deleting + # and then creating a new one + if hexstate is None: + self.setup_default_layouts(index, settings) + except cp.NoOptionError: + QMessageBox.critical(self, _("Warning"), + _("Quick switch layout #%s has not yet " + "been defined.") % str(index)) + return + # TODO: is there any real use in calling the previous layout + # setting? + # self.previous_layout_settings = self.get_window_settings() + self.set_window_settings(*settings) + self.current_quick_layout = index + + # make sure the flags are correctly set for visible panes + for plugin in self.widgetlist: + action = plugin.toggle_view_action + action.setChecked(plugin.dockwidget.isVisible()) + + # --- Show/Hide toolbars + def _update_show_toolbars_action(self): + """Update the text displayed in the menu entry.""" + if self.toolbars_visible: + text = _("Hide toolbars") + tip = _("Hide toolbars") + else: + text = _("Show toolbars") + tip = _("Show toolbars") + self.show_toolbars_action.setText(text) + self.show_toolbars_action.setToolTip(tip) + + def save_visible_toolbars(self): + """Saves the name of the visible toolbars in the .ini file.""" + toolbars = [] + for toolbar in self.visible_toolbars: + toolbars.append(toolbar.objectName()) + CONF.set('main', 'last_visible_toolbars', toolbars) + + def get_visible_toolbars(self): + """Collects the visible toolbars.""" + toolbars = [] + for toolbar in self.toolbarslist: + if toolbar.toggleViewAction().isChecked(): + toolbars.append(toolbar) + self.visible_toolbars = toolbars + + def load_last_visible_toolbars(self): + """Loads the last visible toolbars from the .ini file.""" + toolbars_names = CONF.get('main', 'last_visible_toolbars', default=[]) + + if toolbars_names: + dic = {} + for toolbar in self.toolbarslist: + dic[toolbar.objectName()] = toolbar + + toolbars = [] + for name in toolbars_names: + if name in dic: + toolbars.append(dic[name]) + self.visible_toolbars = toolbars + else: + self.get_visible_toolbars() + self._update_show_toolbars_action() + + @Slot() + def show_toolbars(self): + """Show/Hides toolbars.""" + value = not self.toolbars_visible + CONF.set('main', 'toolbars_visible', value) + if value: + self.save_visible_toolbars() + else: + self.get_visible_toolbars() + + for toolbar in self.visible_toolbars: + toolbar.toggleViewAction().setChecked(value) + toolbar.setVisible(value) + + self.toolbars_visible = value + self._update_show_toolbars_action() + + # --- Other + def plugin_focus_changed(self): + """Focus has changed from one plugin to another""" + self.update_edit_menu() + self.update_search_menu() + + # Now deal with Python shell and IPython plugins + if self.ipyconsole is not None: + focus_client = self.ipyconsole.get_focus_client() + if focus_client is not None: + self.last_console_plugin_focus_was_python = False + else: + shell = get_focus_python_shell() + if shell is not None: + self.last_console_plugin_focus_was_python = True + + def show_shortcuts(self, menu): + """Show action shortcuts in menu""" + for element in getattr(self, menu + '_menu_actions'): + if element and isinstance(element, QAction): + if element._shown_shortcut is not None: + element.setShortcut(element._shown_shortcut) + + def hide_shortcuts(self, menu): + """Hide action shortcuts in menu""" + for element in getattr(self, menu + '_menu_actions'): + if element and isinstance(element, QAction): + if element._shown_shortcut is not None: + element.setShortcut(QKeySequence()) + + def update_edit_menu(self): + """Update edit menu""" + if self.menuBar().hasFocus(): + return + # Disabling all actions to begin with + for child in self.edit_menu.actions(): + child.setEnabled(False) + + widget, textedit_properties = get_focus_widget_properties() + if textedit_properties is None: # widget is not an editor/console + return + #!!! Below this line, widget is expected to be a QPlainTextEdit instance + console, not_readonly, readwrite_editor = textedit_properties + + # Editor has focus and there is no file opened in it + if not console and not_readonly and not self.editor.is_file_opened(): + return + + self.selectall_action.setEnabled(True) + + # Undo, redo + self.undo_action.setEnabled( readwrite_editor \ + and widget.document().isUndoAvailable() ) + self.redo_action.setEnabled( readwrite_editor \ + and widget.document().isRedoAvailable() ) + + # Copy, cut, paste, delete + has_selection = widget.has_selected_text() + self.copy_action.setEnabled(has_selection) + self.cut_action.setEnabled(has_selection and not_readonly) + self.paste_action.setEnabled(not_readonly) + + # Comment, uncomment, indent, unindent... + if not console and not_readonly: + # This is the editor and current file is writable + for action in self.editor.edit_menu_actions: + action.setEnabled(True) + + def update_search_menu(self): + """Update search menu""" + if self.menuBar().hasFocus(): + return + + widget, textedit_properties = get_focus_widget_properties() + for action in self.editor.search_menu_actions: + action.setEnabled(self.editor.isAncestorOf(widget)) + if textedit_properties is None: # widget is not an editor/console + return + #!!! Below this line, widget is expected to be a QPlainTextEdit instance + _x, _y, readwrite_editor = textedit_properties + # Disable the replace action for read-only files + self.search_menu_actions[3].setEnabled(readwrite_editor) + + def create_plugins_menu(self): + order = ['editor', 'console', 'ipython_console', 'variable_explorer', + 'help', None, 'explorer', 'outline_explorer', + 'project_explorer', 'find_in_files', None, 'historylog', + 'profiler', 'breakpoints', 'pylint', None, + 'onlinehelp', 'internal_console'] + for plugin in self.widgetlist: + action = plugin.toggle_view_action + action.setChecked(plugin.dockwidget.isVisible()) + try: + name = plugin.CONF_SECTION + pos = order.index(name) + except ValueError: + pos = None + if pos is not None: + order[pos] = action + else: + order.append(action) + actions = order[:] + for action in order: + if type(action) is str: + actions.remove(action) + self.plugins_menu_actions = actions + add_actions(self.plugins_menu, actions) + + def create_toolbars_menu(self): + order = ['file_toolbar', 'run_toolbar', 'debug_toolbar', + 'main_toolbar', 'Global working directory', None, + 'search_toolbar', 'edit_toolbar', 'source_toolbar'] + for toolbar in self.toolbarslist: + action = toolbar.toggleViewAction() + name = toolbar.objectName() + try: + pos = order.index(name) + except ValueError: + pos = None + if pos is not None: + order[pos] = action + else: + order.append(action) + add_actions(self.toolbars_menu, order) + + def createPopupMenu(self): + menu = QMenu('', self) + actions = self.help_menu_actions[:3] + \ + [None, self.help_menu_actions[-1]] + add_actions(menu, actions) + return menu + + def set_splash(self, message): + """Set splash message""" + if message: + self.debug_print(message) + self.splash.show() + self.splash.showMessage(message, Qt.AlignBottom | Qt.AlignCenter | + Qt.AlignAbsolute, QColor(Qt.white)) + QApplication.processEvents() + + def remove_tmpdir(self): + """Remove Spyder temporary directory""" + shutil.rmtree(programs.TEMPDIR, ignore_errors=True) + + def closeEvent(self, event): + """closeEvent reimplementation""" + if self.closing(True): + event.accept() + else: + event.ignore() + + def resizeEvent(self, event): + """Reimplement Qt method""" + if not self.isMaximized() and not self.fullscreen_flag: + self.window_size = self.size() + QMainWindow.resizeEvent(self, event) + + # To be used by the tour to be able to resize + self.sig_resized.emit(event) + + def moveEvent(self, event): + """Reimplement Qt method""" + if not self.isMaximized() and not self.fullscreen_flag: + self.window_position = self.pos() + QMainWindow.moveEvent(self, event) + + # To be used by the tour to be able to move + self.sig_moved.emit(event) + + def hideEvent(self, event): + """Reimplement Qt method""" + for plugin in self.widgetlist: + if plugin.isAncestorOf(self.last_focused_widget): + plugin.visibility_changed(True) + QMainWindow.hideEvent(self, event) + + def change_last_focused_widget(self, old, now): + """To keep track of to the last focused widget""" + if (now is None and QApplication.activeWindow() is not None): + QApplication.activeWindow().setFocus() + self.last_focused_widget = QApplication.focusWidget() + elif now is not None: + self.last_focused_widget = now + + def closing(self, cancelable=False): + """Exit tasks""" + if self.already_closed or self.is_starting_up: + return True + if cancelable and CONF.get('main', 'prompt_on_exit'): + reply = QMessageBox.critical(self, 'Spyder', + 'Do you really want to exit?', + QMessageBox.Yes, QMessageBox.No) + if reply == QMessageBox.No: + return False + prefix = 'window' + '/' + self.save_current_window_settings(prefix) + if CONF.get('main', 'single_instance'): + self.open_files_server.close() + for plugin in self.thirdparty_plugins: + if not plugin.closing_plugin(cancelable): + return False + for widget in self.widgetlist: + if not widget.closing_plugin(cancelable): + return False + self.dialog_manager.close_all() + if self.toolbars_visible: + self.save_visible_toolbars() + self.already_closed = True + return True + + def add_dockwidget(self, child): + """Add QDockWidget and toggleViewAction""" + dockwidget, location = child.create_dockwidget() + if CONF.get('main', 'vertical_dockwidget_titlebars'): + dockwidget.setFeatures(dockwidget.features()| + QDockWidget.DockWidgetVerticalTitleBar) + self.addDockWidget(location, dockwidget) + self.widgetlist.append(child) + + @Slot() + def close_current_dockwidget(self): + widget = QApplication.focusWidget() + for plugin in self.widgetlist: + if plugin.isAncestorOf(widget): + plugin.dockwidget.hide() + break + + def toggle_lock_dockwidgets(self, value): + """Lock/Unlock dockwidgets""" + self.dockwidgets_locked = value + self.apply_panes_settings() + CONF.set('main', 'panes_locked', value) + + def __update_maximize_action(self): + if self.state_before_maximizing is None: + text = _("Maximize current pane") + tip = _("Maximize current pane") + icon = ima.icon('maximize') + else: + text = _("Restore current pane") + tip = _("Restore pane to its original size") + icon = ima.icon('unmaximize') + self.maximize_action.setText(text) + self.maximize_action.setIcon(icon) + self.maximize_action.setToolTip(tip) + + @Slot() + def maximize_dockwidget(self, restore=False): + """Shortcut: Ctrl+Alt+Shift+M + First call: maximize current dockwidget + Second call (or restore=True): restore original window layout""" + if self.state_before_maximizing is None: + if restore: + return + # No plugin is currently maximized: maximizing focus plugin + self.state_before_maximizing = self.saveState() + focus_widget = QApplication.focusWidget() + for plugin in self.widgetlist: + plugin.dockwidget.hide() + if plugin.isAncestorOf(focus_widget): + self.last_plugin = plugin + self.last_plugin.dockwidget.toggleViewAction().setDisabled(True) + self.setCentralWidget(self.last_plugin) + self.last_plugin.ismaximized = True + # Workaround to solve an issue with editor's outline explorer: + # (otherwise the whole plugin is hidden and so is the outline explorer + # and the latter won't be refreshed if not visible) + self.last_plugin.show() + self.last_plugin.visibility_changed(True) + if self.last_plugin is self.editor: + # Automatically show the outline if the editor was maximized: + self.addDockWidget(Qt.RightDockWidgetArea, + self.outlineexplorer.dockwidget) + self.outlineexplorer.dockwidget.show() + else: + # Restore original layout (before maximizing current dockwidget) + self.last_plugin.dockwidget.setWidget(self.last_plugin) + self.last_plugin.dockwidget.toggleViewAction().setEnabled(True) + self.setCentralWidget(None) + self.last_plugin.ismaximized = False + self.restoreState(self.state_before_maximizing) + self.state_before_maximizing = None + self.last_plugin.get_focus_widget().setFocus() + self.__update_maximize_action() + + def __update_fullscreen_action(self): + if self.isFullScreen(): + icon = ima.icon('window_nofullscreen') + else: + icon = ima.icon('window_fullscreen') + if is_text_string(icon): + icon = get_icon(icon) + self.fullscreen_action.setIcon(icon) + + @Slot() + def toggle_fullscreen(self): + if self.isFullScreen(): + self.fullscreen_flag = False + self.showNormal() + if self.maximized_flag: + self.showMaximized() + else: + self.maximized_flag = self.isMaximized() + self.fullscreen_flag = True + self.showFullScreen() + self.__update_fullscreen_action() + + def add_to_toolbar(self, toolbar, widget): + """Add widget actions to toolbar""" + actions = widget.toolbar_actions + if actions is not None: + add_actions(toolbar, actions) + + @Slot() + def about(self): + """About Spyder""" + versions = get_versions() + # Show Mercurial revision for development version + revlink = '' + if versions['revision']: + rev = versions['revision'] + revlink = " (Commit: %s)" % (rev, rev) + QMessageBox.about(self, + _("About %s") % "Spyder", + """Spyder %s %s +
The Scientific PYthon Development EnviRonment +
Copyright © The Spyder Project Contributors +
Licensed under the terms of the MIT License +

Created by Pierre Raybaut. +
Developed and maintained by the + Spyder Project Contributors. +
Many thanks to all the Spyder beta testers and regular users. +

For bug reports and feature requests, please go + to our Github website. For discussions around the + project, please go to our Google Group +

This project is part of a larger effort to promote and + facilitate the use of Python for scientific and engineering + software development. The popular Python distributions + Anaconda, + WinPython and + Python(x,y) + also contribute to this plan. +

Python %s %dbits, Qt %s, %s %s on %s +

Most of the icons for the Spyder 2 theme come from the Crystal + Project (© 2006-2007 Everaldo Coelho). Other icons for that + theme come from Yusuke + Kamiyamane (all rights reserved) and from + + The Oxygen icon theme. + """ + % (versions['spyder'], revlink, __project_url__, + __project_url__, __forum_url__, versions['python'], + versions['bitness'], versions['qt'], versions['qt_api'], + versions['qt_api_ver'], versions['system'])) + + @Slot() + def show_dependencies(self): + """Show Spyder's Dependencies dialog box""" + from spyder.widgets.dependencies import DependenciesDialog + dlg = DependenciesDialog(None) + dlg.set_data(dependencies.DEPENDENCIES) + dlg.show() + dlg.exec_() + + @Slot() + def report_issue(self): + if PY3: + from urllib.parse import quote + else: + from urllib import quote # analysis:ignore + versions = get_versions() + # Get git revision for development version + revision = '' + if versions['revision']: + revision = versions['revision'] + issue_template = """\ +## 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** + + +## Version and main components + +* Spyder Version: %s %s +* Python Version: %s +* Qt Versions: %s, %s %s on %s + +## Dependencies +``` +%s +``` +""" % (versions['spyder'], + revision, + versions['python'], + versions['qt'], + versions['qt_api'], + versions['qt_api_ver'], + versions['system'], + dependencies.status()) + + url = QUrl("https://github.com/spyder-ide/spyder/issues/new") + if PYQT5: + from qtpy.QtCore import QUrlQuery + query = QUrlQuery() + query.addQueryItem("body", quote(issue_template)) + url.setQuery(query) + else: + url.addEncodedQueryItem("body", quote(issue_template)) + + QDesktopServices.openUrl(url) + + @Slot() + def google_group(self): + url = QUrl("http://groups.google.com/group/spyderlib") + QDesktopServices.openUrl(url) + + @Slot() + def global_callback(self): + """Global callback""" + widget = QApplication.focusWidget() + action = self.sender() + callback = from_qvariant(action.data(), to_text_string) + from spyder.widgets.editor import TextEditBaseWidget + if isinstance(widget, TextEditBaseWidget): + getattr(widget, callback)() + + def redirect_internalshell_stdio(self, state): + if state: + self.console.shell.interpreter.redirect_stds() + else: + self.console.shell.interpreter.restore_stds() + + def open_external_console(self, fname, wdir, args, interact, debug, python, + python_args, systerm, post_mortem=False): + """Open external console""" + if systerm: + # Running script in an external system terminal + try: + programs.run_python_script_in_terminal(fname, wdir, args, + interact, debug, python_args) + except NotImplementedError: + QMessageBox.critical(self, _("Run"), + _("Running an external system terminal " + "is not supported on platform %s." + ) % os.name) + else: + self.extconsole.visibility_changed(True) + self.extconsole.raise_() + self.extconsole.start( + fname=to_text_string(fname), wdir=to_text_string(wdir), + args=to_text_string(args), interact=interact, + debug=debug, python=python, post_mortem=post_mortem, + python_args=to_text_string(python_args) ) + + def execute_in_external_console(self, lines, focus_to_editor): + """ + Execute lines in external or IPython console and eventually set focus + to the editor + """ + console = self.extconsole + if self.ipyconsole is None or self.last_console_plugin_focus_was_python: + console = self.extconsole + else: + console = self.ipyconsole + console.visibility_changed(True) + console.raise_() + console.execute_code(lines) + if focus_to_editor: + self.editor.visibility_changed(True) + + def open_file(self, fname, external=False): + """ + Open filename with the appropriate application + Redirect to the right widget (txt -> editor, spydata -> workspace, ...) + or open file outside Spyder (if extension is not supported) + """ + fname = to_text_string(fname) + ext = osp.splitext(fname)[1] + if encoding.is_text_file(fname): + self.editor.load(fname) + elif self.variableexplorer is not None and ext in IMPORT_EXT: + self.variableexplorer.import_data(fname) + elif not external: + fname = file_uri(fname) + programs.start_file(fname) + + def open_external_file(self, fname): + """ + Open external files that can be handled either by the Editor or the + variable explorer inside Spyder. + """ + fname = encoding.to_unicode_from_fs(fname) + if osp.isfile(fname): + self.open_file(fname, external=True) + elif osp.isfile(osp.join(CWD, fname)): + self.open_file(osp.join(CWD, fname), external=True) + + #---- PYTHONPATH management, etc. + def get_spyder_pythonpath(self): + """Return Spyder PYTHONPATH""" + return self.path+self.project_path + + def add_path_to_sys_path(self): + """Add Spyder path to sys.path""" + for path in reversed(self.get_spyder_pythonpath()): + sys.path.insert(1, path) + + def remove_path_from_sys_path(self): + """Remove Spyder path from sys.path""" + sys_path = sys.path + while sys_path[1] in self.get_spyder_pythonpath(): + sys_path.pop(1) + + @Slot() + def path_manager_callback(self): + """Spyder path manager""" + from spyder.widgets.pathmanager import PathManager + self.remove_path_from_sys_path() + project_path = self.projects.get_pythonpath() + dialog = PathManager(self, self.path, project_path, sync=True) + dialog.redirect_stdio.connect(self.redirect_internalshell_stdio) + dialog.exec_() + self.add_path_to_sys_path() + encoding.writelines(self.path, self.SPYDER_PATH) # Saving path + self.sig_pythonpath_changed.emit() + + def pythonpath_changed(self): + """Projects PYTHONPATH contribution has changed""" + self.remove_path_from_sys_path() + self.project_path = self.projects.get_pythonpath() + self.add_path_to_sys_path() + self.sig_pythonpath_changed.emit() + + @Slot() + def win_env(self): + """Show Windows current user environment variables""" + self.dialog_manager.show(WinUserEnvDialog(self)) + + #---- Preferences + def apply_settings(self): + """Apply settings changed in 'Preferences' dialog box""" + qapp = QApplication.instance() + # Set 'gtk+' as the default theme in Gtk-based desktops + # Fixes Issue 2036 + if is_gtk_desktop() and ('GTK+' in QStyleFactory.keys()): + try: + qapp.setStyle('gtk+') + except: + pass + else: + qapp.setStyle(CONF.get('main', 'windows_style', + self.default_style)) + + default = self.DOCKOPTIONS + if CONF.get('main', 'vertical_tabs'): + default = default|QMainWindow.VerticalTabs + if CONF.get('main', 'animated_docks'): + default = default|QMainWindow.AnimatedDocks + self.setDockOptions(default) + + self.apply_panes_settings() + self.apply_statusbar_settings() + + def apply_panes_settings(self): + """Update dockwidgets features settings""" + # Update toggle action on menu + for child in self.widgetlist: + features = child.FEATURES + if CONF.get('main', 'vertical_dockwidget_titlebars'): + features = features | QDockWidget.DockWidgetVerticalTitleBar + if not self.dockwidgets_locked: + features = features | QDockWidget.DockWidgetMovable + child.dockwidget.setFeatures(features) + child.update_margins() + + def apply_statusbar_settings(self): + """Update status bar widgets settings""" + show_status_bar = CONF.get('main', 'show_status_bar') + self.statusBar().setVisible(show_status_bar) + + if show_status_bar: + for widget, name in ((self.mem_status, 'memory_usage'), + (self.cpu_status, 'cpu_usage')): + if widget is not None: + widget.setVisible(CONF.get('main', '%s/enable' % name)) + widget.set_interval(CONF.get('main', '%s/timeout' % name)) + else: + return + + @Slot() + def edit_preferences(self): + """Edit Spyder preferences""" + from spyder.plugins.configdialog import ConfigDialog + dlg = ConfigDialog(self) + dlg.size_change.connect(self.set_prefs_size) + if self.prefs_dialog_size is not None: + dlg.resize(self.prefs_dialog_size) + for PrefPageClass in self.general_prefs: + widget = PrefPageClass(dlg, main=self) + widget.initialize() + dlg.add_page(widget) + for plugin in [self.workingdirectory, self.editor, + self.projects, self.extconsole, self.ipyconsole, + self.historylog, self.help, self.variableexplorer, + self.onlinehelp, self.explorer, self.findinfiles + ]+self.thirdparty_plugins: + if plugin is not None: + try: + widget = plugin.create_configwidget(dlg) + if widget is not None: + dlg.add_page(widget) + except Exception: + traceback.print_exc(file=sys.stderr) + if self.prefs_index is not None: + dlg.set_current_index(self.prefs_index) + dlg.show() + dlg.check_all_settings() + dlg.pages_widget.currentChanged.connect(self.__preference_page_changed) + dlg.exec_() + + def __preference_page_changed(self, index): + """Preference page index has changed""" + self.prefs_index = index + + def set_prefs_size(self, size): + """Save preferences dialog size""" + self.prefs_dialog_size = size + + #---- Shortcuts + def register_shortcut(self, qaction_or_qshortcut, context, name, + add_sc_to_tip=False): + """ + Register QAction or QShortcut to Spyder main application, + with shortcut (context, name, default) + """ + self.shortcut_data.append( (qaction_or_qshortcut, context, + name, add_sc_to_tip) ) + + def apply_shortcuts(self): + """Apply shortcuts settings to all widgets/plugins""" + toberemoved = [] + for index, (qobject, context, name, + add_sc_to_tip) in enumerate(self.shortcut_data): + keyseq = QKeySequence( get_shortcut(context, name) ) + try: + if isinstance(qobject, QAction): + if sys.platform == 'darwin' and \ + qobject._shown_shortcut == 'missing': + qobject._shown_shortcut = keyseq + else: + qobject.setShortcut(keyseq) + if add_sc_to_tip: + add_shortcut_to_tooltip(qobject, context, name) + elif isinstance(qobject, QShortcut): + qobject.setKey(keyseq) + except RuntimeError: + # Object has been deleted + toberemoved.append(index) + for index in sorted(toberemoved, reverse=True): + self.shortcut_data.pop(index) + + # -- Open files server + def start_open_files_server(self): + self.open_files_server.setsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR, 1) + port = select_port(default_port=OPEN_FILES_PORT) + CONF.set('main', 'open_files_port', port) + self.open_files_server.bind(('127.0.0.1', port)) + self.open_files_server.listen(20) + while 1: # 1 is faster than True + try: + req, dummy = self.open_files_server.accept() + except socket.error as e: + # See Issue 1275 for details on why errno EINTR is + # silently ignored here. + eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR + # To avoid a traceback after closing on Windows + if e.args[0] == eintr: + continue + # handle a connection abort on close error + enotsock = (errno.WSAENOTSOCK if os.name == 'nt' + else errno.ENOTSOCK) + if e.args[0] in [errno.ECONNABORTED, enotsock]: + return + raise + fname = req.recv(1024) + fname = fname.decode('utf-8') + self.sig_open_external_file.emit(fname) + req.sendall(b' ') + + # ---- Quit and restart, and reset spyder defaults + @Slot() + def reset_spyder(self): + """ + Quit and reset Spyder and then Restart application. + """ + answer = QMessageBox.warning(self, _("Warning"), + _("Spyder will restart and reset to default settings:

" + "Do you want to continue?"), + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + self.restart(reset=True) + + @Slot() + def restart(self, reset=False): + """ + Quit and Restart Spyder application. + + If reset True it allows to reset spyder on restart. + """ + # Get start path to use in restart script + spyder_start_directory = get_module_path('spyder') + restart_script = osp.join(spyder_start_directory, 'app', 'restart.py') + + # Get any initial argument passed when spyder was started + # Note: Variables defined in bootstrap.py and spyder/app/start.py + env = os.environ.copy() + bootstrap_args = env.pop('SPYDER_BOOTSTRAP_ARGS', None) + spyder_args = env.pop('SPYDER_ARGS') + + # Get current process and python running spyder + pid = os.getpid() + python = sys.executable + + # Check if started with bootstrap.py + if bootstrap_args is not None: + spyder_args = bootstrap_args + is_bootstrap = True + else: + is_bootstrap = False + + # Pass variables as environment variables (str) to restarter subprocess + env['SPYDER_ARGS'] = spyder_args + env['SPYDER_PID'] = str(pid) + env['SPYDER_IS_BOOTSTRAP'] = str(is_bootstrap) + env['SPYDER_RESET'] = str(reset) + + if DEV: + if os.name == 'nt': + env['PYTHONPATH'] = ';'.join(sys.path) + else: + env['PYTHONPATH'] = ':'.join(sys.path) + + # Build the command and popen arguments depending on the OS + if os.name == 'nt': + # Hide flashing command prompt + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + shell = False + else: + startupinfo = None + shell = True + + command = '"{0}" "{1}"' + command = command.format(python, restart_script) + + try: + if self.closing(True): + subprocess.Popen(command, shell=shell, env=env, + startupinfo=startupinfo) + self.console.quit() + except Exception as error: + # If there is an error with subprocess, Spyder should not quit and + # the error can be inspected in the internal console + print(error) + print(command) + + # ---- Interactive Tours + def show_tour(self, index): + """ """ + frames = self.tours_available[index] + self.tour.set_tour(index, frames, self) + self.tour.start_tour() + + # ---- Check for Spyder Updates + def _check_updates_ready(self): + """Called by WorkerUpdates when ready""" + from spyder.widgets.helperwidgets import MessageCheckBox + + # feedback` = False is used on startup, so only positive feedback is + # given. `feedback` = True is used when after startup (when using the + # menu action, and gives feeback if updates are, or are not found. + feedback = self.give_updates_feedback + + # Get results from worker + update_available = self.worker_updates.update_available + latest_release = self.worker_updates.latest_release + error_msg = self.worker_updates.error + + url_r = 'https://github.com/spyder-ide/spyder/releases' + url_i = 'http://pythonhosted.org/spyder/installation.html' + + # Define the custom QMessageBox + box = MessageCheckBox() + box.setWindowTitle(_("Spyder updates")) + box.set_checkbox_text(_("Check for updates on startup")) + box.setStandardButtons(QMessageBox.Ok) + box.setDefaultButton(QMessageBox.Ok) + #The next line is commented because it freezes the dialog. + #For now there is then no info icon. This solves issue #3609. + #box.setIcon(QMessageBox.Information) + + # Adjust the checkbox depending on the stored configuration + section, option = 'main', 'check_updates_on_startup' + check_updates = CONF.get(section, option) + box.set_checked(check_updates) + + if error_msg is not None: + msg = error_msg + box.setText(msg) + box.set_check_visible(False) + box.exec_() + check_updates = box.is_checked() + else: + if update_available: + msg = _("Spyder %s is available!

Please use " + "your package manager to update Spyder or go to our " + "Releases page to download this " + "new version.

If you are not sure how to " + "proceed to update Spyder please refer to our " + " Installation instructions." + "") % (latest_release, url_r, url_i) + box.setText(msg) + box.set_check_visible(True) + box.exec_() + check_updates = box.is_checked() + elif feedback: + msg = _("Spyder is up to date.") + box.setText(msg) + box.set_check_visible(False) + box.exec_() + check_updates = box.is_checked() + + # Update checkbox based on user interaction + CONF.set(section, option, check_updates) + + # Enable check_updates_action after the thread has finished + self.check_updates_action.setDisabled(False) + + # Provide feeback when clicking menu if check on startup is on + self.give_updates_feedback = True + + @Slot() + def check_updates(self): + """ + Check for spyder updates on github releases using a QThread. + """ + from spyder.workers.updates import WorkerUpdates + + # Disable check_updates_action while the thread is working + self.check_updates_action.setDisabled(True) + + if self.thread_updates is not None: + self.thread_updates.terminate() + + self.thread_updates = QThread(self) + self.worker_updates = WorkerUpdates(self) + self.worker_updates.sig_ready.connect(self._check_updates_ready) + 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() + + +#============================================================================== +# Utilities to create the 'main' function +#============================================================================== +def initialize(): + """Initialize Qt, patching sys.exit and eventually setting up ETS""" + # This doesn't create our QApplication, just holds a reference to + # MAIN_APP, created above to show our splash screen as early as + # possible + app = qapplication() + + #----Monkey patching QApplication + class FakeQApplication(QApplication): + """Spyder's fake QApplication""" + def __init__(self, args): + self = app # analysis:ignore + @staticmethod + def exec_(): + """Do nothing because the Qt mainloop is already running""" + pass + from qtpy import QtWidgets + QtWidgets.QApplication = FakeQApplication + + #----Monkey patching rope + try: + from spyder import rope_patch + rope_patch.apply() + except ImportError: + # rope is not installed + pass + + #----Monkey patching sys.exit + def fake_sys_exit(arg=[]): + pass + sys.exit = fake_sys_exit + + #----Monkey patching sys.excepthook to avoid crashes in PyQt 5.5+ + if PYQT5: + def spy_excepthook(type_, value, tback): + sys.__excepthook__(type_, value, tback) + sys.excepthook = spy_excepthook + + # Removing arguments from sys.argv as in standard Python interpreter + sys.argv = [''] + + # Selecting Qt4 backend for Enthought Tool Suite (if installed) + try: + from enthought.etsconfig.api import ETSConfig + ETSConfig.toolkit = 'qt4' + except ImportError: + pass + + return app + + +class Spy(object): + """ + Inspect Spyder internals + + Attributes: + app Reference to main QApplication object + window Reference to spyder.MainWindow widget + """ + def __init__(self, app, window): + self.app = app + self.window = window + def __dir__(self): + return list(self.__dict__.keys()) +\ + [x for x in dir(self.__class__) if x[0] != '_'] + def versions(self): + return get_versions() + + +def run_spyder(app, options, args): + """ + Create and show Spyder's main window + Start QApplication event loop + """ + #TODO: insert here + # Main window + main = MainWindow(options) + try: + main.setup() + except BaseException: + if main.console is not None: + try: + main.console.shell.exit_interpreter() + except BaseException: + pass + raise + + main.show() + main.post_visible_setup() + + if main.console: + main.console.shell.interpreter.namespace['spy'] = \ + Spy(app=app, window=main) + + # Open external files passed as args + if args: + for a in args: + main.open_external_file(a) + + # Don't show icons in menus for Mac + if sys.platform == 'darwin': + QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, True) + + # Open external files with our Mac app + if running_in_mac_app(): + app.sig_open_external_file.connect(main.open_external_file) + + # To give focus again to the last focused widget after restoring + # the window + app.focusChanged.connect(main.change_last_focused_widget) + + app.exec_() + return main + + +#============================================================================== +# Main +#============================================================================== +def main(): + """Main function""" + + # **** Collect command line options **** + # Note regarding Options: + # It's important to collect options before monkey patching sys.exit, + # otherwise, optparse won't be able to exit if --help option is passed + options, args = get_options() + + if set_attached_console_visible is not None: + set_attached_console_visible(DEBUG or options.show_console \ + or options.reset_config_files \ + or options.reset_to_defaults \ + or options.optimize) + + app = initialize() + if options.reset_config_files: + # Remove all configuration files! + reset_config_files() + return + elif options.reset_to_defaults: + # Reset Spyder settings to defaults + CONF.reset_to_defaults(save=True) + return + elif options.optimize: + # Optimize the whole Spyder's source code directory + import spyder + programs.run_python_script(module="compileall", + args=[spyder.__path__[0]], p_args=['-O']) + return + + # Show crash dialog + if CONF.get('main', 'crash', False) and not DEV: + CONF.set('main', 'crash', False) + SPLASH.hide() + QMessageBox.information(None, "Spyder", + "Spyder crashed during last session.

" + "If Spyder does not start at all and before submitting a " + "bug report, please try to reset settings to defaults by " + "running Spyder with the command line option '--reset':
" + "python spyder --reset" + "

" + "Warning: " + "this command will remove all your Spyder configuration files " + "located in '%s').

" + "If restoring the default settings does not help, please take " + "the time to search for known bugs or " + "discussions matching your situation before " + "eventually creating a new issue here. " + "Your feedback will always be greatly appreciated." + "" % (get_conf_path(), __project_url__, + __forum_url__, __project_url__)) + + # Create main window + mainwindow = None + try: + mainwindow = run_spyder(app, options, args) + except BaseException: + CONF.set('main', 'crash', True) + import traceback + traceback.print_exc(file=STDERR) + traceback.print_exc(file=open('spyder_crash.log', 'w')) + if mainwindow is None: + # An exception occured + SPLASH.hide() + return + + ORIGINAL_SYS_EXIT() + + +if __name__ == "__main__": + main() diff -Nru spyder-2.3.8+dfsg1/spyder/app/restart.py spyder-3.0.2+dfsg1/spyder/app/restart.py --- spyder-2.3.8+dfsg1/spyder/app/restart.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/app/restart.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,279 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Restart Spyder + +A helper script that allows to restart (and also reset) Spyder from within the +running application. +""" + +# Standard library imports +import ast +import os +import os.path as osp +import subprocess +import sys +import time + +# Third party imports +from qtpy.QtCore import Qt, QTimer +from qtpy.QtGui import QColor, QPixmap +from qtpy.QtWidgets import QApplication, QMessageBox, QSplashScreen, QWidget + +# Local imports +from spyder.config.base import _, get_image_path +from spyder.py3compat import to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import qapplication + + +PY2 = sys.version[0] == '2' +IS_WINDOWS = os.name == 'nt' +SLEEP_TIME = 0.2 # Seconds for throttling control +CLOSE_ERROR, RESET_ERROR, RESTART_ERROR = [1, 2, 3] # Spyder error codes + + +def _is_pid_running_on_windows(pid): + """Check if a process is running on windows systems based on the pid.""" + pid = str(pid) + + # Hide flashing command prompt + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + + process = subprocess.Popen(r'tasklist /fi "PID eq {0}"'.format(pid), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + startupinfo=startupinfo) + stdoutdata, stderrdata = process.communicate() + stdoutdata = to_text_string(stdoutdata) + process.kill() + check = pid in stdoutdata + + return check + + +def _is_pid_running_on_unix(pid): + """Check if a process is running on unix systems based on the pid.""" + try: + # On unix systems os.kill with a 0 as second argument only pokes the + # process (if it exists) and does not kill it + os.kill(pid, 0) + except OSError: + return False + else: + return True + + +def is_pid_running(pid): + """Check if a process is running based on the pid.""" + # Select the correct function depending on the OS + if os.name == 'nt': + return _is_pid_running_on_windows(pid) + else: + return _is_pid_running_on_unix(pid) + + +class Restarter(QWidget): + """Widget in charge of displaying the splash information screen and the + error messages. + """ + def __init__(self): + super(Restarter, self).__init__() + self.ellipsis = ['', '.', '..', '...', '..', '.'] + + # Widgets + self.timer_ellipsis = QTimer(self) + self.splash = QSplashScreen(QPixmap(get_image_path('splash.svg'), + 'svg')) + + # Widget setup + self.setVisible(False) + + font = self.splash.font() + font.setPixelSize(10) + self.splash.setFont(font) + self.splash.show() + + self.timer_ellipsis.timeout.connect(self.animate_ellipsis) + + def _show_message(self, text): + """Show message on splash screen.""" + self.splash.showMessage(text, Qt.AlignBottom | Qt.AlignCenter | + Qt.AlignAbsolute, QColor(Qt.white)) + + def animate_ellipsis(self): + """Animate dots at the end of the splash screen message.""" + ellipsis = self.ellipsis.pop(0) + text = ' '*len(ellipsis) + self.splash_text + ellipsis + self.ellipsis.append(ellipsis) + self._show_message(text) + + def set_splash_message(self, text): + """Sets the text in the bottom of the Splash screen.""" + self.splash_text = text + self._show_message(text) + self.timer_ellipsis.start(500) + + def launch_error_message(self, error_type, error=None): + """Launch a message box with a predefined error message. + + Parameters + ---------- + error_type : int [CLOSE_ERROR, RESET_ERROR, RESTART_ERROR] + Possible error codes when restarting/reseting spyder. + error : Exception + Actual Python exception error caught. + """ + messages = {CLOSE_ERROR: _("It was not possible to close the previous " + "Spyder instance.\nRestart aborted."), + RESET_ERROR: _("Spyder could not reset to factory " + "defaults.\nRestart aborted."), + RESTART_ERROR: _("It was not possible to restart Spyder.\n" + "Operation aborted.")} + titles = {CLOSE_ERROR: _("Spyder exit error"), + RESET_ERROR: _("Spyder reset error"), + RESTART_ERROR: _("Spyder restart error")} + + if error: + e = error.__repr__() + message = messages[error_type] + "\n\n{0}".format(e) + else: + message = messages[error_type] + + title = titles[error_type] + self.splash.hide() + QMessageBox.warning(self, title, message, QMessageBox.Ok) + raise RuntimeError(message) + + +def main(): + # Splash screen + # ------------------------------------------------------------------------- + # Start Qt Splash to inform the user of the current status + app = qapplication() + restarter = Restarter() + resample = not IS_WINDOWS + # Resampling SVG icon only on non-Windows platforms (see Issue 1314): + icon = ima.icon('spyder', resample=resample) + app.setWindowIcon(icon) + restarter.set_splash_message(_('Closing Spyder')) + + # Get variables + # Note: Variables defined in app/spyder.py 'restart()' method + spyder_args = os.environ.pop('SPYDER_ARGS', None) + pid = os.environ.pop('SPYDER_PID', None) + is_bootstrap = os.environ.pop('SPYDER_IS_BOOTSTRAP', None) + reset = os.environ.pop('SPYDER_RESET', None) + + # Get the spyder base folder based on this file + this_folder = osp.split(osp.dirname(osp.abspath(__file__)))[0] + spyder_folder = osp.split(this_folder)[0] + + if not any([spyder_args, pid, is_bootstrap, reset]): + error = "This script can only be called from within a Spyder instance" + raise RuntimeError(error) + + # Variables were stored as string literals in the environment, so to use + # them we need to parse them in a safe manner. + is_bootstrap = ast.literal_eval(is_bootstrap) + pid = ast.literal_eval(pid) + args = ast.literal_eval(spyder_args) + reset = ast.literal_eval(reset) + + # Enforce the --new-instance flag when running spyder + if '--new-instance' not in args: + if is_bootstrap and '--' not in args: + args = args + ['--', '--new-instance'] + else: + args.append('--new-instance') + + # Create the arguments needed for reseting + if '--' in args: + args_reset = ['--', '--reset'] + else: + args_reset = ['--reset'] + + # Arrange arguments to be passed to the restarter and reset subprocess + args = ' '.join(args) + args_reset = ' '.join(args_reset) + + # Get python excutable running this script + python = sys.executable + + # Build the command + if is_bootstrap: + spyder = osp.join(spyder_folder, 'bootstrap.py') + else: + spyderdir = osp.join(spyder_folder, 'spyder') + spyder = osp.join(spyderdir, 'app', 'start.py') + + command = '"{0}" "{1}" {2}'.format(python, spyder, args) + + # Adjust the command and/or arguments to subprocess depending on the OS + shell = not IS_WINDOWS + + # Before launching a new Spyder instance we need to make sure that the + # previous one has closed. We wait for a fixed and "reasonable" amount of + # time and check, otherwise an error is launched + wait_time = 90 if IS_WINDOWS else 30 # Seconds + for counter in range(int(wait_time/SLEEP_TIME)): + if not is_pid_running(pid): + break + time.sleep(SLEEP_TIME) # Throttling control + QApplication.processEvents() # Needed to refresh the splash + else: + # The old spyder instance took too long to close and restart aborts + restarter.launch_error_message(error_type=CLOSE_ERROR) + + env = os.environ.copy() + + # Reset Spyder (if required) + # ------------------------------------------------------------------------- + if reset: + restarter.set_splash_message(_('Resetting Spyder to defaults')) + command_reset = '"{0}" "{1}" {2}'.format(python, spyder, args_reset) + + try: + p = subprocess.Popen(command_reset, shell=shell, env=env) + except Exception as error: + restarter.launch_error_message(error_type=RESET_ERROR, error=error) + else: + p.communicate() + pid_reset = p.pid + + # Before launching a new Spyder instance we need to make sure that the + # reset subprocess has closed. We wait for a fixed and "reasonable" + # amount of time and check, otherwise an error is launched. + wait_time = 20 # Seconds + for counter in range(int(wait_time/SLEEP_TIME)): + if not is_pid_running(pid_reset): + break + time.sleep(SLEEP_TIME) # Throttling control + QApplication.processEvents() # Needed to refresh the splash + else: + # The reset subprocess took too long and it is killed + try: + p.kill() + except OSError as error: + restarter.launch_error_message(error_type=RESET_ERROR, + error=error) + else: + restarter.launch_error_message(error_type=RESET_ERROR) + + # Restart + # ------------------------------------------------------------------------- + restarter.set_splash_message(_('Restarting')) + try: + subprocess.Popen(command, shell=shell, env=env) + except Exception as error: + restarter.launch_error_message(error_type=RESTART_ERROR, error=error) + + +if __name__ == '__main__': + main() diff -Nru spyder-2.3.8+dfsg1/spyder/app/start.py spyder-3.0.2+dfsg1/spyder/app/start.py --- spyder-2.3.8+dfsg1/spyder/app/start.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/app/start.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- + +# Std imports +import os +import os.path as osp +import random +import socket +import sys +import time + +# Local imports +from spyder.app.cli_options import get_options +from spyder.config.base import get_conf_path, running_in_mac_app +from spyder.config.main import CONF +from spyder.utils.external import lockfile +from spyder.py3compat import is_unicode + + +def send_args_to_spyder(args): + """ + Simple socket client used to send the args passed to the Spyder + executable to an already running instance. + + Args can be Python scripts or files with these extensions: .spydata, .mat, + .npy, or .h5, which can be imported by the Variable Explorer. + """ + port = CONF.get('main', 'open_files_port') + + # Wait ~50 secs for the server to be up + # Taken from http://stackoverflow.com/a/4766598/438386 + for _x in range(200): + try: + for arg in args: + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM, + socket.IPPROTO_TCP) + client.connect(("127.0.0.1", port)) + if is_unicode(arg): + arg = arg.encode('utf-8') + client.send(osp.abspath(arg)) + client.close() + except socket.error: + time.sleep(0.25) + continue + break + + +def main(): + """ + Start Spyder application. + + If single instance mode is turned on (default behavior) and an instance of + Spyder is already running, this will just parse and send command line + options to the application. + """ + # Parse command line options + options, args = get_options() + + # Store variable to be used in self.restart (restart spyder instance) + os.environ['SPYDER_ARGS'] = str(sys.argv[1:]) + + if CONF.get('main', 'single_instance') and not options.new_instance \ + and not options.reset_config_files and not running_in_mac_app(): + # Minimal delay (0.1-0.2 secs) to avoid that several + # instances started at the same time step in their + # own foots while trying to create the lock file + time.sleep(random.randrange(1000, 2000, 90)/10000.) + + # Lock file creation + lock_file = get_conf_path('spyder.lock') + lock = lockfile.FilesystemLock(lock_file) + + # Try to lock spyder.lock. If it's *possible* to do it, then + # there is no previous instance running and we can start a + # new one. If *not*, then there is an instance already + # running, which is locking that file + try: + lock_created = lock.lock() + except: + # If locking fails because of errors in the lockfile + # module, try to remove a possibly stale spyder.lock. + # This is reported to solve all problems with + # lockfile (See issue 2363) + try: + if os.name == 'nt': + if osp.isdir(lock_file): + import shutil + shutil.rmtree(lock_file, ignore_errors=True) + else: + if osp.islink(lock_file): + os.unlink(lock_file) + except: + pass + + # Then start Spyder as usual and *don't* continue + # executing this script because it doesn't make + # sense + from spyder.app import mainwindow + mainwindow.main() + return + + if lock_created: + # Start a new instance + from spyder.app import mainwindow + mainwindow.main() + else: + # Pass args to Spyder or print an informative + # message + if args: + send_args_to_spyder(args) + else: + print("Spyder is already running. If you want to open a new \n" + "instance, please pass to it the --new-instance option") + else: + from spyder.app import mainwindow + mainwindow.main() + + +if __name__ == "__main__": + main() diff -Nru spyder-2.3.8+dfsg1/spyder/app/tour.py spyder-3.0.2+dfsg1/spyder/app/tour.py --- spyder-2.3.8+dfsg1/spyder/app/tour.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/app/tour.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,1289 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Spyder interactive tours""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +from __future__ import division + +import sys + +# Third party imports +from qtpy.QtCore import (QEasingCurve, QPoint, QPropertyAnimation, QRectF, Qt, + Signal) +from qtpy.QtGui import (QBrush, QColor, QIcon, QPainter, QPainterPath, QPen, + QPixmap, QRegion) +from qtpy.QtWidgets import (QAction, QApplication, QComboBox, QDialog, + QGraphicsOpacityEffect, QHBoxLayout, QLabel, + QLayout, QMainWindow, QMenu, QPushButton, + QSpacerItem, QToolButton, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import _, get_image_path +from spyder.py3compat import to_binary_string +from spyder.utils.qthelpers import add_actions, create_action + +# FIXME: Known issues +# How to handle if an specific dockwidget does not exists/load, like ipython +# on python3.3, should that frame be removed? should it display a warning? + +class SpyderWidgets(object): + """List of supported widgets to highlight/decorate""" + # Panes + external_console = 'extconsole' + ipython_console = 'ipyconsole' + editor = 'editor' + editor_line_number_area = 'editor.get_current_editor().linenumberarea' + editor_scroll_flag_area = 'editor.get_current_editor().scrollflagarea' + file_explorer = 'explorer' + help_plugin = 'help' + variable_explorer = 'variableexplorer' + history_log = "historylog" + + # Toolbars + toolbars = '' + toolbars_active = '' + toolbar_file = '' + toolbar_edit = '' + toolbar_run = '' + toolbar_debug = '' + toolbar_main = '' + + status_bar = '' + menu_bar = '' + menu_file = '' + menu_edit = '' + + +def get_tours(index=None): + """ + Get the list of available tours (if index=None), or the your given by + index + """ + return get_tour(index) + + +def get_tour(index): + """ + This function generates a list of tours. + + The index argument is used to retrieve a particular tour. If None is + passed, it will return the full list of tours. If instead -1 is given, + this function will return a test tour + + To add more tours a new variable needs to be created to hold the list of + dicts and the tours variable at the bottom of this function needs to be + updated accordingly + """ + sw = SpyderWidgets + qtconsole_link = "http://ipython.org/ipython-doc/stable/interactive/qtconsole.html" + + # This test should serve as example of keys to use in the tour frame dics + test = [{'title': "Welcome to Spyder introduction tour", + 'content': "Spyder is an interactive development \ + environment. This tip panel supports rich text.
\ +
it also supports image insertion to the right so\ + far", + 'image': 'tour-spyder-logo.png'}, + + {'title': "Widget display", + 'content': ("This show how a widget is displayed. The tip panel " + "is adjusted based on the first widget in the list"), + 'widgets': ['button1'], + 'decoration': ['button2'], + 'interact': True}, + + {'title': "Widget display", + 'content': ("This show how a widget is displayed. The tip panel " + "is adjusted based on the first widget in the list"), + 'widgets': ['button1'], + 'decoration': ['button1'], + 'interact': True}, + + {'title': "Widget display", + 'content': ("This show how a widget is displayed. The tip panel " + "is adjusted based on the first widget in the list"), + 'widgets': ['button1'], + 'interact': True}, + + {'title': "Widget display and highlight", + 'content': "This shows how a highlighted widget looks", + 'widgets': ['button'], + 'decoration': ['button'], + 'interact': False}, + ] + + intro = [{'title': _("Welcome to the Introduction tour"), + 'content': _("Spyder is a powerful Interactive " + "Development Environment (or IDE) for the Python " + "programming language.

" + "Here we are going to guide you through its most " + "important features.

" + "Please use the arrow keys or click on the buttons " + "below to move along the tour."), + 'image': 'tour-spyder-logo.png'}, + + {'title': _("The Editor"), + 'content': _("This is the pane where you write Python code before " + "evaluating it. You can get automatic suggestions " + "and completions while writing, by pressing the " + "Tab key next to a given text.

" + "The Editor comes " + "with a line number area (highlighted here in red), " + "where Spyder shows warnings and syntax errors. They " + "can help you to detect potential problems before " + "running the code.

" + "You can also set debug breakpoints in the line " + "number area, by doing a double click next to " + "a non-empty line."), + 'widgets': [sw.editor], + 'decoration': [sw.editor_line_number_area]}, + + {'title': _("The IPython console"), + 'content': _("This is one of panes where you can run or " + "execute the code you wrote on the Editor. To do it " + "you need to press the F5 key.

" + "This console comes with several " + "useful features that greatly improve your " + "programming workflow (like syntax highlighting and " + "inline plots). If you want to know more about them, " + "please follow this link.

" + "Please click on the button below to run some simple " + "code in this console. This will be useful to show " + "you other important features.").format( + qtconsole_link), + 'widgets': [sw.ipython_console], + 'run': ["li = list(range(100))", "d = {'a': 1, 'b': 2}"] + }, + + {'title': _("The Variable Explorer"), + 'content': _("In this pane you can view and edit the variables " + "generated during the execution of a program, or " + "those entered directly in one of Spyder " + "consoles.

" + "As you can see, the Variable Explorer is showing " + "the variables generated during the last step of " + "this tour. By doing a double-click on any " + "of them, a new window will be opened, where you " + "can inspect and modify their contents."), + 'widgets': [sw.variable_explorer], + 'interact': True}, + + {'title': _("The Python console"), + 'content': _("You can also run your code on a Python console. " + "These consoles are useful because they let you " + "run a file in a console dedicated only to it." + "To select this behavior, please press the F6 " + "key.

" + "By pressing the button below and then focusing the " + "Variable Explorer, you will notice that " + "Python consoles are also connected to that pane, " + "and that the Variable Explorer only shows " + "the variables of the currently focused console."), + 'widgets': [sw.external_console], + 'run': ["a = 2", "s='Hello, world!'"], + }, + + {'title': _("Help"), + 'content': _("This pane displays documentation of the " + "functions, classes, methods or modules you are " + "currently using in the Editor or the Consoles.

" + "To use it, you need to press Ctrl+I in " + "front of an object. If that object has some " + "documentation associated with it, it will be " + "displayed here."), + 'widgets': [sw.help_plugin], + 'interact': True}, + + {'title': _("The File Explorer"), + 'content': _("This pane lets you navigate through the directories " + "and files present in your computer.

" + "You can also open any of these files with its " + "corresponding application, by doing a double " + "click on it.

" + "There is one exception to this rule: plain-text " + "files will always be opened in the Spyder Editor."), + 'widgets': [sw.file_explorer], + 'interact': True}, + + {'title': _("The History Log"), + 'content': _("This pane records all commands introduced in " + "the Python and IPython consoles."), + 'widgets': [sw.history_log], + 'interact': True}, + ] + +# ['The run toolbar', +# 'Should be short', +# ['self.run_toolbar'], None], +# ['The debug toolbar', +# '', +# ['self.debug_toolbar'], None], +# ['The main toolbar', +# '', +# ['self.main_toolbar'], None], +# ['The editor', +# 'Spyder has differnet bla bla bla', +# ['self.editor.dockwidget'], None], +# ['The editor', +# 'Spyder has differnet bla bla bla', +# ['self.outlineexplorer.dockwidget'], None], +# +# ['The menu bar', +# 'Spyder has differnet bla bla bla', +# ['self.menuBar()'], None], +# +# ['The menu bar', +# 'Spyder has differnet bla bla bla', +# ['self.statusBar()'], None], +# +# +# ['The toolbars!', +# 'Spyder has differnet bla bla bla', +# ['self.variableexplorer.dockwidget'], None], +# ['The toolbars MO!', +# 'Spyder has differnet bla bla bla', +# ['self.extconsole.dockwidget'], None], +# ['The whole window?!', +# 'Spyder has differnet bla bla bla', +# ['self'], None], +# ['Lets try something!', +# 'Spyder has differnet bla bla bla', +# ['self.extconsole.dockwidget', +# 'self.variableexplorer.dockwidget'], None] +# +# ] + + feat30 = [{'title': "New features in Spyder 3.0", + 'content': _("Spyder is an interactive development " + "environment based on bla"), + 'image': 'spyder.png'}, + + {'title': _("Welcome to Spyder introduction tour"), + 'content': _("Spyder is an interactive development environment " + "based on bla"), + 'widgets': ['variableexplorer']}, + ] + + tours = [{'name': _('Introduction tour'), 'tour': intro}, + {'name': _('New features in version 3.0'), 'tour': feat30}] + + if index is None: + return tours + elif index == -1: + return [test] + else: + return [tours[index]] + + +class FadingDialog(QDialog): + """A general fade in/fade out QDialog with some builtin functions""" + sig_key_pressed = Signal() + + def __init__(self, parent, opacity, duration, easing_curve): + super(FadingDialog, self).__init__(parent) + + self.parent = parent + self.opacity_min = min(opacity) + self.opacity_max = max(opacity) + self.duration_fadein = duration[0] + self.duration_fadeout = duration[-1] + self.easing_curve_in = easing_curve[0] + self.easing_curve_out = easing_curve[-1] + self.effect = None + self.anim = None + + self._fade_running = False + self._funcs_before_fade_in = [] + self._funcs_after_fade_in = [] + self._funcs_before_fade_out = [] + self._funcs_after_fade_out = [] + + self.setModal(False) + + def _run(self, funcs): + """ """ + for func in funcs: + func() + + def _run_before_fade_in(self): + """ """ + self._run(self._funcs_before_fade_in) + + def _run_after_fade_in(self): + """ """ + self._run(self._funcs_after_fade_in) + + def _run_before_fade_out(self): + """ """ + self._run(self._funcs_before_fade_out) + + def _run_after_fade_out(self): + """ """ + self._run(self._funcs_after_fade_out) + + def _set_fade_finished(self): + """ """ + self._fade_running = False + + def _fade_setup(self): + """ """ + self._fade_running = True + self.effect = QGraphicsOpacityEffect(self) + self.setGraphicsEffect(self.effect) + self.anim = QPropertyAnimation(self.effect, to_binary_string("opacity")) + + # --- public api + def fade_in(self, on_finished_connect): + """ """ + self._run_before_fade_in() + self._fade_setup() + self.show() + self.raise_() + self.anim.setEasingCurve(self.easing_curve_in) + self.anim.setStartValue(self.opacity_min) + self.anim.setEndValue(self.opacity_max) + self.anim.setDuration(self.duration_fadein) + self.anim.finished.connect(on_finished_connect) + self.anim.finished.connect(self._set_fade_finished) + self.anim.finished.connect(self._run_after_fade_in) + self.anim.start() + + def fade_out(self, on_finished_connect): + """ """ + self._run_before_fade_out() + self._fade_setup() + self.anim.setEasingCurve(self.easing_curve_out) + self.anim.setStartValue(self.opacity_max) + self.anim.setEndValue(self.opacity_min) + self.anim.setDuration(self.duration_fadeout) + self.anim.finished.connect(on_finished_connect) + self.anim.finished.connect(self._set_fade_finished) + self.anim.finished.connect(self._run_after_fade_out) + self.anim.start() + + def is_fade_running(self): + """ """ + return self._fade_running + + def set_funcs_before_fade_in(self, funcs): + """ """ + self._funcs_before_fade_in = funcs + + def set_funcs_after_fade_in(self, funcs): + """ """ + self._funcs_after_fade_in = funcs + + def set_funcs_before_fade_out(self, funcs): + """ """ + self._funcs_before_fade_out = funcs + + def set_funcs_after_fade_out(self, funcs): + """ """ + self._funcs_after_fade_out = funcs + + +class FadingCanvas(FadingDialog): + """The black semi transparent canvas that covers the application""" + def __init__(self, parent, opacity, duration, easing_curve, color): + super(FadingCanvas, self).__init__(parent, opacity, duration, + easing_curve) + self.parent = parent + + self.color = color # Canvas color + self.color_decoration = Qt.red # Decoration color + self.stroke_decoration = 2 # width in pixels for decoration + + self.region_mask = None + self.region_subtract = None + self.region_decoration = None + + self.widgets = None # The widget to uncover + self.decoration = None # The widget to draw decoration + self.interaction_on = False + + self.path_current = None + self.path_subtract = None + self.path_full = None + self.path_decoration = None + + # widget setup + self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint) + self.setAttribute(Qt.WA_TranslucentBackground) + self.setAttribute(Qt.WA_TransparentForMouseEvents) + self.setModal(False) + self.setFocusPolicy(Qt.NoFocus) + + self.set_funcs_before_fade_in([self.update_canvas]) + self.set_funcs_after_fade_out([lambda: self.update_widgets(None), + lambda: self.update_decoration(None)]) + + def set_interaction(self, value): + """ """ + self.interaction_on = value + + def update_canvas(self): + """ """ + w, h = self.parent.size().width(), self.parent.size().height() + + self.path_full = QPainterPath() + self.path_subtract = QPainterPath() + self.path_decoration = QPainterPath() + self.region_mask = QRegion(0, 0, w, h) + + self.path_full.addRect(0, 0, w, h) + + # Add the path + if self.widgets is not None: + for widget in self.widgets: + temp_path = QPainterPath() + # if widget is not found... find more general way to handle + if widget is not None: + widget.raise_() + widget.show() + geo = widget.frameGeometry() + width, height = geo.width(), geo.height() + point = widget.mapTo(self.parent, QPoint(0, 0)) + x, y = point.x(), point.y() + + temp_path.addRect(QRectF(x, y, width, height)) + + temp_region = QRegion(x, y, width, height) + + if self.interaction_on: + self.region_mask = self.region_mask.subtracted(temp_region) + self.path_subtract = self.path_subtract.united(temp_path) + + self.path_current = self.path_full.subtracted(self.path_subtract) + else: + self.path_current = self.path_full + + if self.decoration is not None: + for widget in self.decoration: + temp_path = QPainterPath() + widget.raise_() + widget.show() + geo = widget.frameGeometry() + width, height = geo.width(), geo.height() + point = widget.mapTo(self.parent, QPoint(0, 0)) + x, y = point.x(), point.y() + temp_path.addRect(QRectF(x, y, width, height)) + + temp_region_1 = QRegion(x-1, y-1, width+2, height+2) + temp_region_2 = QRegion(x+1, y+1, width-2, height-2) + temp_region = temp_region_1.subtracted(temp_region_2) + + if self.interaction_on: + self.region_mask = self.region_mask.united(temp_region) + + self.path_decoration = self.path_decoration.united(temp_path) + else: + self.path_decoration.addRect(0, 0, 0, 0) + + # Add a decoration stroke around widget + self.setMask(self.region_mask) + self.update() + self.repaint() + + def update_widgets(self, widgets): + """ """ + self.widgets = widgets + + def update_decoration(self, widgets): + """ """ + self.decoration = widgets + + def paintEvent(self, event): + """Override Qt method""" + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + # Decoration + painter.fillPath(self.path_current, QBrush(self.color)) + painter.strokePath(self.path_decoration, QPen(self.color_decoration, + self.stroke_decoration)) +# decoration_fill = QColor(self.color_decoration) +# decoration_fill.setAlphaF(0.25) +# painter.fillPath(self.path_decoration, decoration_fill) + + def reject(self): + """Override Qt method""" + if not self.is_fade_running(): + key = Qt.Key_Escape + self.key_pressed = key + self.sig_key_pressed.emit() + + def mousePressEvent(self, event): + """Override Qt method""" + pass + + +class FadingTipBox(FadingDialog): + """ """ + def __init__(self, parent, opacity, duration, easing_curve): + super(FadingTipBox, self).__init__(parent, opacity, duration, + easing_curve) + self.holder = self.anim # needed for qt to work + self.parent = parent + + self.frames = None + self.color_top = QColor.fromRgb(230, 230, 230) + self.color_back = QColor.fromRgb(255, 255, 255) + self.offset_shadow = 0 + self.fixed_width = 300 + + self.key_pressed = None + + self.setAttribute(Qt.WA_TranslucentBackground) + self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | + Qt.WindowStaysOnTopHint) + self.setModal(False) + + # Widgets + self.button_home = QPushButton("<<") + self.button_close = QPushButton("X") + self.button_previous = QPushButton(" < ") + self.button_end = QPushButton(">>") + self.button_next = QPushButton(" > ") + self.button_run = QPushButton(_('Run code')) + self.button_disable = None + self.button_current = QToolButton() + self.label_image = QLabel() + + self.label_title = QLabel() + self.combo_title = QComboBox() + self.label_current = QLabel() + self.label_content = QLabel() + + self.label_content.setMinimumWidth(self.fixed_width) + self.label_content.setMaximumWidth(self.fixed_width) + + self.label_current.setAlignment(Qt.AlignCenter) + + self.label_content.setWordWrap(True) + + self.widgets = [self.label_content, self.label_title, + self.label_current, self.combo_title, + self.button_close, self.button_run, self.button_next, + self.button_previous, self.button_end, + self.button_home, self.button_current] + + arrow = get_image_path('hide.png') + + self.stylesheet = '''QPushButton { + background-color: rgbs(200,200,200,100%); + color: rgbs(0,0,0,100%); + border-style: outset; + border-width: 1px; + border-radius: 3px; + border-color: rgbs(100,100,100,100%); + padding: 2px; + } + + QPushButton:hover { + background-color: rgbs(150, 150, 150, 100%); + } + + QPushButton:disabled { + background-color: rgbs(230,230,230,100%); + color: rgbs(200,200,200,100%); + border-color: rgbs(200,200,200,100%); + } + + QComboBox { + padding-left: 5px; + background-color: rgbs(230,230,230,100%); + border-width: 0px; + border-radius: 0px; + min-height:20px; + max-height:20px; + } + + QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top left; + border-width: 0px; + } + + QComboBox::down-arrow { + image: url(''' + arrow + '''); + } + + ''' + # Windows fix, slashes should be always in unix-style + self.stylesheet = self.stylesheet.replace('\\', '/') + + for widget in self.widgets: + widget.setFocusPolicy(Qt.NoFocus) + widget.setStyleSheet(self.stylesheet) + + layout_top = QHBoxLayout() + layout_top.addWidget(self.combo_title) + layout_top.addStretch() + layout_top.addWidget(self.button_close) + layout_top.addSpacerItem(QSpacerItem(self.offset_shadow, + self.offset_shadow)) + + layout_content = QHBoxLayout() + layout_content.addWidget(self.label_content) + layout_content.addWidget(self.label_image) + layout_content.addSpacerItem(QSpacerItem(5, 5)) + + layout_run = QHBoxLayout() + layout_run.addStretch() + layout_run.addWidget(self.button_run) + layout_run.addStretch() + layout_run.addSpacerItem(QSpacerItem(self.offset_shadow, + self.offset_shadow)) + + layout_navigation = QHBoxLayout() + layout_navigation.addWidget(self.button_home) + layout_navigation.addWidget(self.button_previous) + layout_navigation.addStretch() + layout_navigation.addWidget(self.label_current) + layout_navigation.addStretch() + layout_navigation.addWidget(self.button_next) + layout_navigation.addWidget(self.button_end) + layout_navigation.addSpacerItem(QSpacerItem(self.offset_shadow, + self.offset_shadow)) + + layout = QVBoxLayout() + layout.addLayout(layout_top) + layout.addStretch() + layout.addSpacerItem(QSpacerItem(15, 15)) + layout.addLayout(layout_content) + layout.addLayout(layout_run) + layout.addStretch() + layout.addSpacerItem(QSpacerItem(15, 15)) + layout.addLayout(layout_navigation) + layout.addSpacerItem(QSpacerItem(self.offset_shadow, + self.offset_shadow)) + + layout.setSizeConstraint(QLayout.SetFixedSize) + + self.setLayout(layout) + + self.set_funcs_before_fade_in([self._disable_widgets]) + self.set_funcs_after_fade_in([self._enable_widgets]) + self.set_funcs_before_fade_out([self._disable_widgets]) + + self.setContextMenuPolicy(Qt.CustomContextMenu) + + # signals and slots + # These are defined every time by the AnimatedTour Class + + def _disable_widgets(self): + """ """ + for widget in self.widgets: + widget.setDisabled(True) + + def _enable_widgets(self): + """ """ + self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | + Qt.WindowStaysOnTopHint) + for widget in self.widgets: + widget.setDisabled(False) + + if self.button_disable == 'previous': + self.button_previous.setDisabled(True) + self.button_home.setDisabled(True) + elif self.button_disable == 'next': + self.button_next.setDisabled(True) + self.button_end.setDisabled(True) + + def set_data(self, title, content, current, image, run, frames=None, + step=None): + """ """ + self.label_title.setText(title) + self.combo_title.clear() + self.combo_title.addItems(frames) + self.combo_title.setCurrentIndex(step) +# min_content_len = max([len(f) for f in frames]) +# self.combo_title.setMinimumContentsLength(min_content_len) + + # Fix and try to see how it looks with a combo box + self.label_current.setText(current) + self.button_current.setText(current) + self.label_content.setText(content) + self.image = image + + if image is None: + self.label_image.setFixedHeight(1) + self.label_image.setFixedWidth(1) + else: + extension = image.split('.')[-1] + self.image = QPixmap(get_image_path(image), extension) + self.label_image.setPixmap(self.image) + self.label_image.setFixedSize(self.image.size()) + + if run is None: + self.button_run.setVisible(False) + else: + self.button_run.setDisabled(False) + self.button_run.setVisible(True) + + # Refresh layout + self.layout().activate() + + def set_pos(self, x, y): + """ """ + self.x = x + self.y = y + self.move(QPoint(x, y)) + + def build_paths(self): + """ """ + geo = self.geometry() + radius = 30 + shadow = self.offset_shadow + x0, y0 = geo.x(), geo.y() + width, height = geo.width() - shadow, geo.height() - shadow + + left, top = 0, 0 + right, bottom = width, height + + self.round_rect_path = QPainterPath() + self.round_rect_path.moveTo(right, top + radius) + self.round_rect_path.arcTo(right-radius, top, radius, radius, 0.0, + 90.0) + self.round_rect_path.lineTo(left+radius, top) + self.round_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) + self.round_rect_path.lineTo(left, bottom-radius) + self.round_rect_path.arcTo(left, bottom-radius, radius, radius, 180.0, + 90.0) + self.round_rect_path.lineTo(right-radius, bottom) + self.round_rect_path.arcTo(right-radius, bottom-radius, radius, radius, + 270.0, 90.0) + self.round_rect_path.closeSubpath() + + # Top path + header = 36 + offset = 2 + left, top = offset, offset + right = width - (offset) + self.top_rect_path = QPainterPath() + self.top_rect_path.lineTo(right, top + radius) + self.top_rect_path.moveTo(right, top + radius) + self.top_rect_path.arcTo(right-radius, top, radius, radius, 0.0, 90.0) + self.top_rect_path.lineTo(left+radius, top) + self.top_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) + self.top_rect_path.lineTo(left, top + header) + self.top_rect_path.lineTo(right, top + header) + + def paintEvent(self, event): + """ """ + self.build_paths() + + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + + painter.fillPath(self.round_rect_path, self.color_back) + painter.fillPath(self.top_rect_path, self.color_top) + painter.strokePath(self.round_rect_path, QPen(Qt.gray, 1)) + + # TODO: Build the pointing arrow? + + def keyReleaseEvent(self, event): + """ """ + key = event.key() + self.key_pressed = key +# print(key) + keys = [Qt.Key_Right, Qt.Key_Left, Qt.Key_Down, Qt.Key_Up, + Qt.Key_Escape, Qt.Key_PageUp, Qt.Key_PageDown, + Qt.Key_Home, Qt.Key_End, Qt.Key_Menu] + + if key in keys: + if not self.is_fade_running(): + self.sig_key_pressed.emit() + + def mousePressEvent(self, event): + """override Qt method""" + # Raise the main application window on click + self.parent.raise_() + self.raise_() + + if event.button() == Qt.RightButton: + pass +# clicked_widget = self.childAt(event.x(), event.y()) +# if clicked_widget == self.label_current: +# self.context_menu_requested(event) + + def context_menu_requested(self, event): + """ """ + pos = QPoint(event.x(), event.y()) + menu = QMenu(self) + + actions = [] + action_title = create_action(self, _('Go to step: '), icon=QIcon()) + action_title.setDisabled(True) + actions.append(action_title) +# actions.append(create_action(self, _(': '), icon=QIcon())) + + add_actions(menu, actions) + + menu.popup(self.mapToGlobal(pos)) + + def reject(self): + """Qt method to handle escape key event""" + if not self.is_fade_running(): + key = Qt.Key_Escape + self.key_pressed = key + self.sig_key_pressed.emit() + + +class AnimatedTour(QWidget): + """ """ + + def __init__(self, parent): + QWidget.__init__(self, parent) + + self.parent = parent + + # Variables to adjust + self.duration_canvas = [666, 666] + self.duration_tips = [333, 333] + self.opacity_canvas = [0.0, 0.7] + self.opacity_tips = [0.0, 1.0] + self.color = Qt.black + self.easing_curve = [QEasingCurve.Linear] + + self.current_step = 0 + self.step_current = 0 + self.steps = 0 + self.canvas = None + self.tips = None + self.frames = None + self.spy_window = None + + self.widgets = None + self.dockwidgets = None + self.decoration = None + self.run = None + + self.is_tour_set = False + + # Widgets + self.canvas = FadingCanvas(self.parent, self.opacity_canvas, + self.duration_canvas, self.easing_curve, + self.color) + self.tips = FadingTipBox(self.parent, self.opacity_tips, + self.duration_tips, self.easing_curve) + + # Widgets setup + # Needed to fix issue #2204 + self.setAttribute(Qt.WA_TransparentForMouseEvents) + + # Signals and slots + self.tips.button_next.clicked.connect(self.next_step) + self.tips.button_previous.clicked.connect(self.previous_step) + self.tips.button_close.clicked.connect(self.close_tour) + self.tips.button_run.clicked.connect(self.run_code) + self.tips.button_home.clicked.connect(self.first_step) + self.tips.button_end.clicked.connect(self.last_step) + self.tips.button_run.clicked.connect( + lambda: self.tips.button_run.setDisabled(True)) + self.tips.combo_title.currentIndexChanged.connect(self.go_to_step) + + # Main window move or resize + self.parent.sig_resized.connect(self._resized) + self.parent.sig_moved.connect(self._moved) + + # To capture the arrow keys that allow moving the tour + self.tips.sig_key_pressed.connect(self._key_pressed) + + def _resized(self, event): + """ """ + size = event.size() + self.canvas.setFixedSize(size) + self.canvas.update_canvas() + + if self.is_tour_set: + self._set_data() + + def _moved(self, event): + """ """ + pos = event.pos() + self.canvas.move(QPoint(pos.x(), pos.y())) + + if self.is_tour_set: + self._set_data() + + def _close_canvas(self): + """ """ + self._set_modal(False, [self.tips]) + self.tips.hide() + self.canvas.fade_out(self.canvas.hide) + + def _clear_canvas(self): + """ """ + # TODO: Add option to also make it white... might be usefull? + # Make canvas black before transitions + self.canvas.update_widgets(None) + self.canvas.update_decoration(None) + self.canvas.update_canvas() + + def _move_step(self): + """ """ + self._set_data() + + # Show/raise the widget so it is located first! + widgets = self.dockwidgets + if widgets is not None: + widget = widgets[0] + if widget is not None: + widget.show() + widget.raise_() + + self._locate_tip_box() + + # Change in canvas only after fadein finishes, for visual aesthetics + self.tips.fade_in(self.canvas.update_canvas) + self.tips.raise_() + + def _set_modal(self, value, widgets): + """ """ + platform = sys.platform.lower() + + if 'linux' in platform: + pass + elif 'win' in platform: + for widget in widgets: + widget.setModal(value) + widget.hide() + widget.show() + elif 'darwin' in platform: + pass + else: + pass + + def _process_widgets(self, names, spy_window): + """ """ + widgets = [] + dockwidgets = [] + + for name in names: + base = name.split('.')[0] + temp = getattr(spy_window, base) + + # Check if it is the current editor + if 'get_current_editor()' in name: + temp = temp.get_current_editor() + temp = getattr(temp, name.split('.')[-1]) + + widgets.append(temp) + + # Check if it is a dockwidget and make the widget a dockwidget + # If not return the same widget + temp = getattr(temp, 'dockwidget', temp) + dockwidgets.append(temp) + + return widgets, dockwidgets + + def _set_data(self): + """ """ + step, steps, frames = self.step_current, self.steps, self.frames + current = '{0}/{1}'.format(step + 1, steps) + frame = frames[step] + + combobox_frames = [u"{0}. {1}".format(i+1, f['title']) + for i, f in enumerate(frames)] + + title, content, image = '', '', None + widgets, dockwidgets, decoration = None, None, None + run = None + + # Check if entry exists in dic and act accordingly + if 'title' in frame: + title = frame['title'] + + if 'content' in frame: + content = frame['content'] + + if 'widgets' in frame: + widget_names = frames[step]['widgets'] + # Get the widgets based on their name + widgets, dockwidgets = self._process_widgets(widget_names, + self.spy_window) + self.widgets = widgets + self.dockwidgets = dockwidgets + + if 'decoration' in frame: + widget_names = frames[step]['decoration'] + deco, decoration = self._process_widgets(widget_names, + self.spy_window) + self.decoration = decoration + + if 'image' in frame: + image = frames[step]['image'] + + if 'interact' in frame: + self.canvas.set_interaction(frame['interact']) + if frame['interact']: + self._set_modal(False, [self.tips]) + else: + self._set_modal(True, [self.tips]) + else: + self.canvas.set_interaction(False) + self._set_modal(True, [self.tips]) + + if 'run' in frame: + # Asume that the frist widget is the console + run = frame['run'] + self.run = run + + self.tips.set_data(title, content, current, image, run, + frames=combobox_frames, step=step) + self._check_buttons() + + # Make canvas black when starting a new place of decoration + self.canvas.update_widgets(dockwidgets) + self.canvas.update_decoration(decoration) + + def _locate_tip_box(self): + """ """ + dockwidgets = self.dockwidgets + + # Store the dimensions of the main window + geo = self.parent.frameGeometry() + x, y, width, height = geo.x(), geo.y(), geo.width(), geo.height() + self.width_main = width + self.height_main = height + self.x_main = x + self.y_main = y + + delta = 20 + + # Here is the tricky part to define the best position for the + # tip widget + if dockwidgets is not None: + if dockwidgets[0] is not None: + geo = dockwidgets[0].geometry() + x, y, width, height = geo.x(), geo.y(), geo.width(), geo.height() + + point = dockwidgets[0].mapToGlobal(QPoint(0, 0)) + x_glob, y_glob = point.x(), point.y() + + # Check if is too tall and put to the side + y_fac = (height / self.height_main) * 100 + + if y_fac > 60: # FIXME: + if x < self.tips.width(): + x = x_glob + width + delta + y = y_glob + height/2 - self.tips.height()/2 + else: + x = x_glob - self.tips.width() - delta + y = y_glob + height/2 - self.tips.height()/2 + else: + if y < self.tips.height(): + x = x_glob + width/2 - self.tips.width()/2 + y = y_glob + height + delta + else: + x = x_glob + width/2 - self.tips.width()/2 + y = y_glob - delta - self.tips.height() + else: + # Center on parent + x = self.x_main + self.width_main/2 - self.tips.width()/2 + y = self.y_main + self.height_main/2 - self.tips.height()/2 + + self.tips.set_pos(x, y) + + def _check_buttons(self): + """ """ + step, steps = self.step_current, self.steps + self.tips.button_disable = None + + if step == 0: + self.tips.button_disable = 'previous' + + if step == steps - 1: + self.tips.button_disable = 'next' + + def _key_pressed(self): + """ """ + key = self.tips.key_pressed + + if ((key == Qt.Key_Right or key == Qt.Key_Down or + key == Qt.Key_PageDown) and self.step_current != self.steps - 1): + self.next_step() + elif ((key == Qt.Key_Left or key == Qt.Key_Up or + key == Qt.Key_PageUp) and self.step_current != 0): + self.previous_step() + elif key == Qt.Key_Escape: + self.close_tour() + elif key == Qt.Key_Home and self.step_current != 0: + self.first_step() + elif key == Qt.Key_End and self.step_current != self.steps - 1: + self.last_step() + elif key == Qt.Key_Menu: + pos = self.tips.label_current.pos() + self.tips.context_menu_requested(pos) + + # --- public api + def run_code(self): + """ """ + codelines = self.run + console = self.widgets[0] + for codeline in codelines: + console.execute_code(codeline) + + def set_tour(self, index, frames, spy_window): + """ """ + self.spy_window = spy_window + self.active_tour_index = index + self.last_frame_active = frames['last'] + self.frames = frames['tour'] + self.steps = len(self.frames) + + self.is_tour_set = True + + def start_tour(self): + """ """ + geo = self.parent.geometry() + x, y, width, height = geo.x(), geo.y(), geo.width(), geo.height() +# self.parent_x = x +# self.parent_y = y +# self.parent_w = width +# self.parent_h = height + + # FIXME: reset step to last used value + # Reset step to begining + self.step_current = self.last_frame_active + + # Adjust the canvas size to match the main window size + self.canvas.setFixedSize(width, height) + self.canvas.move(QPoint(x, y)) + self.canvas.fade_in(self._move_step) + self._clear_canvas() + + def close_tour(self): + """ """ + self.tips.fade_out(self._close_canvas) + self.tips.show() + + try: + # set the last played frame by updating the available tours in + # parent. This info will be lost on restart. + self.parent.tours_available[self.active_tour_index]['last'] =\ + self.step_current + except: + pass + + def next_step(self): + """ """ + self._clear_canvas() + self.step_current += 1 + self.tips.fade_out(self._move_step) + + def previous_step(self): + """ """ + self._clear_canvas() + self.step_current -= 1 + self.tips.fade_out(self._move_step) + + def go_to_step(self, number, id_=None): + """ """ + self._clear_canvas() + self.step_current = number + self.tips.fade_out(self._move_step) + + def last_step(self): + """ """ + self.go_to_step(self.steps - 1) + + def first_step(self): + """ """ + self.go_to_step(0) + +# ---------------------------------------------------------------------------- +# Used for testing the functionality + + +class TestWindow(QMainWindow): + """ """ + sig_resized = Signal("QResizeEvent") + sig_moved = Signal("QMoveEvent") + + def __init__(self): + super(TestWindow, self).__init__() + self.setGeometry(300, 100, 400, 600) + self.setWindowTitle('Exploring QMainWindow') + + self.exit = QAction('Exit', self) + self.exit.setStatusTip('Exit program') + + # create the menu bar + menubar = self.menuBar() + file_ = menubar.addMenu('&File') + file_.addAction(self.exit) + + # create the status bar + self.statusBar() + + # QWidget or its instance needed for box layout + self.widget = QWidget(self) + + self.button = QPushButton('test') + self.button1 = QPushButton('1') + self.button2 = QPushButton('2') + + effect = QGraphicsOpacityEffect(self.button2) + self.button2.setGraphicsEffect(effect) + self.anim = QPropertyAnimation(effect, "opacity") + self.anim.setStartValue(0.01) + self.anim.setEndValue(1.0) + self.anim.setDuration(500) + + lay = QVBoxLayout() + lay.addWidget(self.button) + lay.addStretch() + lay.addWidget(self.button1) + lay.addWidget(self.button2) + + self.widget.setLayout(lay) + + self.setCentralWidget(self.widget) + self.button.clicked.connect(self.action1) + self.button1.clicked.connect(self.action2) + + self.tour = AnimatedTour(self) + + def action1(self): + """ """ + frames = get_tour('test') + index = 0 + dic = {'last': 0, 'tour': frames} + self.tour.set_tour(index, dic, self) + self.tour.start_tour() + + def action2(self): + """ """ + self.anim.start() + + def resizeEvent(self, event): + """Reimplement Qt method""" + QMainWindow.resizeEvent(self, event) + self.sig_resized.emit(event) + + def moveEvent(self, event): + """Reimplement Qt method""" + QMainWindow.moveEvent(self, event) + self.sig_moved.emit(event) + + +def test(): + """ """ + app = QApplication([]) + win = TestWindow() + win.show() + app.exec_() + + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/config/base.py spyder-3.0.2+dfsg1/spyder/config/base.py --- spyder-2.3.8+dfsg1/spyder/config/base.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/config/base.py 2016-11-19 00:31:32.000000000 +0000 @@ -0,0 +1,430 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Spyder base configuration management + +This file only deals with non-GUI configuration features +(in other words, we won't import any PyQt object here, avoiding any +sip API incompatibility issue in spyder's non-gui modules) +""" + +from __future__ import print_function + +import codecs +import locale +import os.path as osp +import os +import shutil +import sys + +# Local imports +from spyder.utils import encoding +from spyder.py3compat import (is_unicode, TEXT_TYPES, INT_TYPES, PY3, + to_text_string, is_text_string) + + +#============================================================================== +# Only for development +#============================================================================== +# To activate/deactivate certain things for development +# SPYDER_DEV is (and *only* has to be) set in bootstrap.py +DEV = os.environ.get('SPYDER_DEV') + +# For testing purposes +# SPYDER_TEST can be set using the --test option of bootstrap.py +TEST = os.environ.get('SPYDER_TEST') + + +#============================================================================== +# Debug helpers +#============================================================================== +# This is needed after restarting and using debug_print +STDOUT = sys.stdout if PY3 else codecs.getwriter('utf-8')(sys.stdout) +STDERR = sys.stderr +def _get_debug_env(): + debug_env = os.environ.get('SPYDER_DEBUG', '') + if not debug_env.isdigit(): + debug_env = bool(debug_env) + return int(debug_env) +DEBUG = _get_debug_env() + +def debug_print(*message): + """Output debug messages to stdout""" + if DEBUG: + ss = STDOUT + if PY3: + # This is needed after restarting and using debug_print + for m in message: + ss.buffer.write(str(m).encode('utf-8')) + print('', file=ss) + else: + print(*message, file=ss) + + +#============================================================================== +# Configuration paths +#============================================================================== +# Spyder settings dir +# NOTE: During the 2.x.x series this dir was named .spyder2, but +# since 3.0+ we've reverted back to use .spyder to simplify major +# updates in version (required when we change APIs by Linux +# packagers) +if sys.platform.startswith('linux'): + SUBFOLDER = 'spyder' +else: + SUBFOLDER = '.spyder' + + +# We can't have PY2 and PY3 settings in the same dir because: +# 1. This leads to ugly crashes and freezes (e.g. by trying to +# embed a PY2 interpreter in PY3) +# 2. We need to save the list of installed modules (for code +# completion) separately for each version +if PY3: + SUBFOLDER = SUBFOLDER + '-py3' + + +def get_home_dir(): + """ + Return user home directory + """ + try: + # expanduser() returns a raw byte string which needs to be + # decoded with the codec that the OS is using to represent file paths. + path = encoding.to_unicode_from_fs(osp.expanduser('~')) + except: + path = '' + for env_var in ('HOME', 'USERPROFILE', 'TMP'): + if osp.isdir(path): + break + # os.environ.get() returns a raw byte string which needs to be + # decoded with the codec that the OS is using to represent environment + # variables. + path = encoding.to_unicode_from_fs(os.environ.get(env_var, '')) + if path: + return path + else: + raise RuntimeError('Please define environment variable $HOME') + + +def get_conf_path(filename=None): + """Return absolute path for configuration file with specified filename""" + # This makes us follow the XDG standard to save our settings + # on Linux, as it was requested on Issue 2629 + if sys.platform.startswith('linux'): + xdg_config_home = os.environ.get('XDG_CONFIG_HOME', '') + if not xdg_config_home: + xdg_config_home = osp.join(get_home_dir(), '.config') + if not osp.isdir(xdg_config_home): + os.makedirs(xdg_config_home) + conf_dir = osp.join(xdg_config_home, SUBFOLDER) + else: + conf_dir = osp.join(get_home_dir(), SUBFOLDER) + if not osp.isdir(conf_dir): + os.mkdir(conf_dir) + if filename is None: + return conf_dir + else: + return osp.join(conf_dir, filename) + + +def get_module_path(modname): + """Return module *modname* base path""" + return osp.abspath(osp.dirname(sys.modules[modname].__file__)) + + +def get_module_data_path(modname, relpath=None, attr_name='DATAPATH'): + """Return module *modname* data path + Note: relpath is ignored if module has an attribute named *attr_name* + + Handles py2exe/cx_Freeze distributions""" + datapath = getattr(sys.modules[modname], attr_name, '') + if datapath: + return datapath + else: + datapath = get_module_path(modname) + parentdir = osp.join(datapath, osp.pardir) + if osp.isfile(parentdir): + # Parent directory is not a directory but the 'library.zip' file: + # this is either a py2exe or a cx_Freeze distribution + datapath = osp.abspath(osp.join(osp.join(parentdir, osp.pardir), + modname)) + if relpath is not None: + datapath = osp.abspath(osp.join(datapath, relpath)) + return datapath + + +def get_module_source_path(modname, basename=None): + """Return module *modname* source path + If *basename* is specified, return *modname.basename* path where + *modname* is a package containing the module *basename* + + *basename* is a filename (not a module name), so it must include the + file extension: .py or .pyw + + Handles py2exe/cx_Freeze distributions""" + srcpath = get_module_path(modname) + parentdir = osp.join(srcpath, osp.pardir) + if osp.isfile(parentdir): + # Parent directory is not a directory but the 'library.zip' file: + # this is either a py2exe or a cx_Freeze distribution + srcpath = osp.abspath(osp.join(osp.join(parentdir, osp.pardir), + modname)) + if basename is not None: + srcpath = osp.abspath(osp.join(srcpath, basename)) + return srcpath + + +def is_py2exe_or_cx_Freeze(): + """Return True if this is a py2exe/cx_Freeze distribution of Spyder""" + return osp.isfile(osp.join(get_module_path('spyder'), osp.pardir)) + + +SCIENTIFIC_STARTUP = get_module_source_path('spyder', 'scientific_startup.py') + + +#============================================================================== +# Image path list +#============================================================================== +IMG_PATH = [] +def add_image_path(path): + if not osp.isdir(path): + return + global IMG_PATH + IMG_PATH.append(path) + for dirpath, dirnames, _filenames in os.walk(path): + for dirname in dirnames: + IMG_PATH.append(osp.join(dirpath, dirname)) + +add_image_path(get_module_data_path('spyder', relpath='images')) + +def get_image_path(name, default="not_found.png"): + """Return image absolute path""" + for img_path in IMG_PATH: + full_path = osp.join(img_path, name) + if osp.isfile(full_path): + return osp.abspath(full_path) + if default is not None: + return osp.abspath(osp.join(img_path, default)) + + +#============================================================================== +# Translations +#============================================================================== +LANG_FILE = get_conf_path('langconfig') +DEFAULT_LANGUAGE = 'en' + +# This needs to be updated every time a new language is added to spyder, and is +# used by the Preferences configuration to populate the Language QComboBox +LANGUAGE_CODES = {'en': u'English', + 'fr': u'Français', + 'es': u'Español', + 'pt_BR': u'Português', + 'ru': u'Русский', + 'ja': u'日本語' + } + + +def get_available_translations(): + """ + List available translations for spyder based on the folders found in the + locale folder. This function checks if LANGUAGE_CODES contain the same + information that is found in the 'locale' folder to ensure that when a new + language is added, LANGUAGE_CODES is updated. + """ + locale_path = get_module_data_path("spyder", relpath="locale", + attr_name='LOCALEPATH') + listdir = os.listdir(locale_path) + langs = [d for d in listdir if osp.isdir(osp.join(locale_path, d))] + langs = [DEFAULT_LANGUAGE] + langs + + # Check that there is a language code available in case a new translation + # is added, to ensure LANGUAGE_CODES is updated. + for lang in langs: + if lang not in LANGUAGE_CODES: + error = _('Update LANGUAGE_CODES (inside config/base.py) if a new ' + 'translation has been added to Spyder') + raise Exception(error) + return langs + + +def get_interface_language(): + """ + If Spyder has a translation available for the locale language, it will + return the version provided by Spyder adjusted for language subdifferences, + otherwise it will return DEFAULT_LANGUAGE. + + Example: + 1.) Spyder provides ('en', 'fr', 'es' and 'pt_BR'), if the locale is + either 'en_US' or 'en' or 'en_UK', this function will return 'en' + + 2.) Spyder provides ('en', 'fr', 'es' and 'pt_BR'), if the locale is + either 'pt' or 'pt_BR', this function will return 'pt_BR' + """ + locale_language = locale.getdefaultlocale()[0] + + language = DEFAULT_LANGUAGE + + if locale_language is not None: + spyder_languages = get_available_translations() + for lang in spyder_languages: + if locale_language == lang: + language = locale_language + break + elif locale_language.startswith(lang) or \ + lang.startswith(locale_language): + language = lang + break + + return language + + +def save_lang_conf(value): + """Save language setting to language config file""" + with open(LANG_FILE, 'w') as f: + f.write(value) + + +def load_lang_conf(): + """ + Load language setting from language config file if it exists, otherwise + try to use the local settings if Spyder provides a translation, or + return the default if no translation provided. + """ + if osp.isfile(LANG_FILE): + with open(LANG_FILE, 'r') as f: + lang = f.read() + else: + lang = get_interface_language() + save_lang_conf(lang) + return lang + + +def get_translation(modname, dirname=None): + """Return translation callback for module *modname*""" + if dirname is None: + dirname = modname + locale_path = get_module_data_path(dirname, relpath="locale", + attr_name='LOCALEPATH') + # If LANG is defined in ubuntu, a warning message is displayed, so in unix + # systems we define the LANGUAGE variable. + language = load_lang_conf() + if os.name == 'nt': + os.environ["LANG"] = language # Works on Windows + else: + os.environ["LANGUAGE"] = language # Works on Linux + + import gettext + try: + _trans = gettext.translation(modname, locale_path, codeset="utf-8") + lgettext = _trans.lgettext + def translate_gettext(x): + if not PY3 and is_unicode(x): + x = x.encode("utf-8") + y = lgettext(x) + if is_text_string(y) and PY3: + return y + else: + return to_text_string(y, "utf-8") + return translate_gettext + except IOError as _e: # analysis:ignore + #print "Not using translations (%s)" % _e + def translate_dumb(x): + if not is_unicode(x): + return to_text_string(x, "utf-8") + return x + return translate_dumb + +# Translation callback +_ = get_translation("spyder") + + +#============================================================================== +# Namespace Browser (Variable Explorer) configuration management +#============================================================================== +def get_supported_types(): + """ + Return a dictionnary containing types lists supported by the + namespace browser: + dict(picklable=picklable_types, editable=editables_types) + + See: + get_remote_data function in spyder/widgets/variableexplorer/utils/monitor.py + + Note: + If you update this list, don't forget to update doc/variablexplorer.rst + """ + from datetime import date + editable_types = [int, float, complex, list, dict, tuple, date + ] + list(TEXT_TYPES) + list(INT_TYPES) + try: + from numpy import ndarray, matrix, generic + editable_types += [ndarray, matrix, generic] + except ImportError: + pass + try: + from pandas import DataFrame, Series + editable_types += [DataFrame, Series] + except ImportError: + pass + picklable_types = editable_types[:] + try: + from spyder.pil_patch import Image + editable_types.append(Image.Image) + except ImportError: + pass + return dict(picklable=picklable_types, editable=editable_types) + +# Variable explorer display / check all elements data types for sequences: +# (when saving the variable explorer contents, check_all is True, +# see widgets/variableexplorer/namespacebrowser.py:NamespaceBrowser.save_data) +CHECK_ALL = False #XXX: If True, this should take too much to compute... + +EXCLUDED_NAMES = ['nan', 'inf', 'infty', 'little_endian', 'colorbar_doc', + 'typecodes', '__builtins__', '__main__', '__doc__', 'NaN', + 'Inf', 'Infinity', 'sctypes', 'rcParams', 'rcParamsDefault', + 'sctypeNA', 'typeNA', 'False_', 'True_',] + + +#============================================================================== +# Mac application utilities +#============================================================================== +if PY3: + MAC_APP_NAME = 'Spyder.app' +else: + MAC_APP_NAME = 'Spyder-Py2.app' + +def running_in_mac_app(): + if sys.platform == "darwin" and MAC_APP_NAME in __file__: + return True + else: + return False + + +#============================================================================== +# Reset config files +#============================================================================== +SAVED_CONFIG_FILES = ('help', 'onlinehelp', 'path', 'pylint.results', + 'spyder.ini', 'temp.py', 'temp.spydata', 'template.py', + 'history.py', 'history_internal.py', 'workingdir', + '.projects', '.spyderproject', '.ropeproject', + 'monitor.log', 'monitor_debug.log', 'rope.log', + 'langconfig', 'spyder.lock') + + +def reset_config_files(): + """Remove all config files""" + print("*** Reset Spyder settings to defaults ***", file=STDERR) + for fname in SAVED_CONFIG_FILES: + cfg_fname = get_conf_path(fname) + if osp.isfile(cfg_fname) or osp.islink(cfg_fname): + os.remove(cfg_fname) + elif osp.isdir(cfg_fname): + shutil.rmtree(cfg_fname) + else: + continue + print("removing:", cfg_fname, file=STDERR) diff -Nru spyder-2.3.8+dfsg1/spyder/config/fonts.py spyder-3.0.2+dfsg1/spyder/config/fonts.py --- spyder-2.3.8+dfsg1/spyder/config/fonts.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/config/fonts.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Spyder font variables +""" + +import os +import sys + +from spyder.config.utils import is_ubuntu + + +#============================================================================== +# Main fonts +#============================================================================== +# Rich text fonts +SANS_SERIF = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', + 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', + 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', + 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] + +# Plan text fonts +MONOSPACE = ['Monospace', 'DejaVu Sans Mono', 'Consolas', + 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', + 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] + + +#============================================================================== +# Adjust font size per OS +#============================================================================== +if sys.platform == 'darwin': + MONOSPACE = ['Menlo'] + MONOSPACE + BIG = MEDIUM = SMALL = 12 +elif os.name == 'nt': + BIG = 12 + MEDIUM = 10 + SMALL = 9 +elif is_ubuntu(): + SANS_SERIF = ['Ubuntu'] + SANS_SERIF + MONOSPACE = ['Ubuntu Mono'] + MONOSPACE + BIG = 13 + MEDIUM = SMALL = 11 +else: + BIG = 12 + MEDIUM = SMALL = 9 + +DEFAULT_SMALL_DELTA = SMALL - MEDIUM +DEFAULT_LARGE_DELTA = SMALL - BIG diff -Nru spyder-2.3.8+dfsg1/spyder/config/gui.py spyder-3.0.2+dfsg1/spyder/config/gui.py --- spyder-2.3.8+dfsg1/spyder/config/gui.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/config/gui.py 2016-11-18 16:29:56.000000000 +0000 @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Spyder GUI-related configuration management +(for non-GUI configuration, see spyder/config/base.py) + +Important note regarding shortcuts: + For compatibility with QWERTZ keyboards, one must avoid using the following + shortcuts: + Ctrl + Alt + Q, W, F, G, Y, X, C, V, B, N +""" + +# Standard library imports +from collections import namedtuple +import sys + +# Third party imports +from qtpy.QtCore import Qt +from qtpy.QtGui import QFont, QFontDatabase, QKeySequence +from qtpy.QtWidgets import QShortcut + +# Local imports +from spyder.config.main import CONF +from spyder.config.user import NoDefault +from spyder.py3compat import to_text_string +from spyder.utils import syntaxhighlighters as sh + + +# Run cell shortcuts +if sys.platform == 'darwin': + RUN_CELL_SHORTCUT = Qt.META + Qt.Key_Return +else: + RUN_CELL_SHORTCUT = Qt.CTRL + Qt.Key_Return +RUN_CELL_AND_ADVANCE_SHORTCUT = Qt.SHIFT + Qt.Key_Return + + +# To save metadata about widget shortcuts (needed to build our +# preferences page) +Shortcut = namedtuple('Shortcut', 'data') + + +def font_is_installed(font): + """Check if font is installed""" + return [fam for fam in QFontDatabase().families() + if to_text_string(fam)==font] + + +def get_family(families): + """Return the first installed font family in family list""" + if not isinstance(families, list): + families = [ families ] + for family in families: + if font_is_installed(family): + return family + else: + print("Warning: None of the following fonts is installed: %r" % families) + return QFont().family() + + +FONT_CACHE = {} + +def get_font(section='main', option='font', font_size_delta=0): + """Get console font properties depending on OS and user options""" + font = FONT_CACHE.get((section, option)) + + if font is None: + families = CONF.get(section, option+"/family", None) + + if families is None: + return QFont() + + family = get_family(families) + weight = QFont.Normal + italic = CONF.get(section, option+'/italic', False) + + if CONF.get(section, option+'/bold', False): + weight = QFont.Bold + + size = CONF.get(section, option+'/size', 9) + font_size_delta + font = QFont(family, size, weight) + font.setItalic(italic) + FONT_CACHE[(section, option)] = font + + size = CONF.get(section, option+'/size', 9) + font_size_delta + font.setPointSize(size) + return font + + +def set_font(font, section='main', option='font'): + """Set font""" + CONF.set(section, option+'/family', to_text_string(font.family())) + CONF.set(section, option+'/size', float(font.pointSize())) + CONF.set(section, option+'/italic', int(font.italic())) + CONF.set(section, option+'/bold', int(font.bold())) + FONT_CACHE[(section, option)] = font + + +def get_shortcut(context, name): + """Get keyboard shortcut (key sequence string)""" + return CONF.get('shortcuts', '%s/%s' % (context, name)) + + +def set_shortcut(context, name, keystr): + """Set keyboard shortcut (key sequence string)""" + CONF.set('shortcuts', '%s/%s' % (context, name), keystr) + + +def fixed_shortcut(keystr, parent, action): + """Define a fixed shortcut according to a keysequence string""" + sc = QShortcut(QKeySequence(keystr), parent, action) + sc.setContext(Qt.WidgetWithChildrenShortcut) + return sc + + +def config_shortcut(action, context, name, parent): + """ + Create a Shortcut namedtuple for a widget + + The data contained in this tuple will be registered in + our shortcuts preferences page + """ + keystr = get_shortcut(context, name) + qsc = fixed_shortcut(keystr, parent, action) + sc = Shortcut(data=(qsc, context, name)) + return sc + + +def iter_shortcuts(): + """Iterate over keyboard shortcuts""" + for option in CONF.options('shortcuts'): + context, name = option.split("/", 1) + yield context, name, get_shortcut(context, name) + + +def reset_shortcuts(): + """Reset keyboard shortcuts to default values""" + CONF.reset_to_defaults(section='shortcuts') + + +def get_color_scheme(name): + """Get syntax color scheme""" + color_scheme = {} + for key in sh.COLOR_SCHEME_KEYS: + color_scheme[key] = CONF.get("color_schemes", "%s/%s" % (name, key)) + return color_scheme + + +def set_color_scheme(name, color_scheme, replace=True): + """Set syntax color scheme""" + section = "color_schemes" + names = CONF.get("color_schemes", "names", []) + for key in sh.COLOR_SCHEME_KEYS: + option = "%s/%s" % (name, key) + value = CONF.get(section, option, default=None) + if value is None or replace or name not in names: + CONF.set(section, option, color_scheme[key]) + names.append(to_text_string(name)) + CONF.set(section, "names", sorted(list(set(names)))) + + +def set_default_color_scheme(name, replace=True): + """Reset color scheme to default values""" + assert name in sh.COLOR_SCHEME_NAMES + set_color_scheme(name, sh.get_color_scheme(name), replace=replace) + + +for _name in sh.COLOR_SCHEME_NAMES: + set_default_color_scheme(_name, replace=False) diff -Nru spyder-2.3.8+dfsg1/spyder/config/ipython.py spyder-3.0.2+dfsg1/spyder/config/ipython.py --- spyder-2.3.8+dfsg1/spyder/config/ipython.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/config/ipython.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +IPython configuration variables needed by Spyder +""" + +from spyder.utils import programs +from spyder import dependencies +from spyder.config.base import _ + + +# Constants +QTCONSOLE_REQVER = ">=4.2.0" +ZMQ_REQVER = ">=13.0.0" +NBCONVERT_REQVER = ">=4.0" + + +# Dependencies +dependencies.add("qtconsole", _("Integrate the IPython console"), + required_version=QTCONSOLE_REQVER) +dependencies.add("nbconvert", _("Manipulate Jupyter notebooks on the Editor"), + required_version=NBCONVERT_REQVER) + + +# Auxiliary functions +def is_qtconsole_installed(): + pyzmq_installed = programs.is_module_installed('zmq', version=ZMQ_REQVER) + pygments_installed = programs.is_module_installed('pygments') + qtconsole_installed = programs.is_module_installed('qtconsole', + version=QTCONSOLE_REQVER) + + if pyzmq_installed and pygments_installed and qtconsole_installed: + return True + else: + return False + + +# Main check for IPython presence +QTCONSOLE_INSTALLED = is_qtconsole_installed() diff -Nru spyder-2.3.8+dfsg1/spyder/config/main.py spyder-3.0.2+dfsg1/spyder/config/main.py --- spyder-2.3.8+dfsg1/spyder/config/main.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/config/main.py 2016-11-17 03:39:39.000000000 +0000 @@ -0,0 +1,608 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Spyder configuration options + +Note: Leave this file free of Qt related imports, so that it can be used to +quickly load a user config file +""" + +import os +import sys +import os.path as osp + +# Local import +from spyder.config.base import (CHECK_ALL, EXCLUDED_NAMES, get_home_dir, + SUBFOLDER, TEST) +from spyder.config.fonts import BIG, MEDIUM, MONOSPACE, SANS_SERIF +from spyder.config.user import UserConfig +from spyder.config.utils import IMPORT_EXT +from spyder.utils import codeanalysis + + +#============================================================================== +# Main constants +#============================================================================== +# Find in files exclude patterns +EXCLUDE_PATTERNS = [r'\.pyc$|\.pyo$|\.orig$|\.hg|\.svn|\bbuild\b', + r'\.pyc$|\.pyo$|\.orig$|\.hg|\.svn'] + +# Extensions that should be visible in Spyder's file/project explorers +SHOW_EXT = ['.py', '.ipynb', '.txt', '.dat', '.pdf', '.png', '.svg'] + + +# Extensions supported by Spyder (Editor or Variable explorer) +USEFUL_EXT = IMPORT_EXT + SHOW_EXT + + +# Name filters for file/project explorers (excluding files without extension) +NAME_FILTERS = ['README', 'INSTALL', 'LICENSE', 'CHANGELOG'] + \ + ['*' + _ext for _ext in USEFUL_EXT if _ext] + + +# Port used to detect if there is a running instance and to communicate with +# it to open external files +OPEN_FILES_PORT = 21128 + + +# OS Specific +WIN = os.name == 'nt' +MAC = sys.platform == 'darwin' +CTRL = "Meta" if MAC else "Ctrl" + + +# ============================================================================= +# Defaults +# ============================================================================= +DEFAULTS = [ + ('main', + { + 'icon_theme': 'spyder 3', + 'single_instance': True, + 'open_files_port': OPEN_FILES_PORT, + 'tear_off_menus': False, + 'high_dpi_scaling': False, + 'vertical_dockwidget_titlebars': False, + 'vertical_tabs': False, + 'animated_docks': True, + 'prompt_on_exit': False, + 'panes_locked': True, + 'window/size': (1260, 740), + 'window/position': (10, 10), + 'window/is_maximized': True, + 'window/is_fullscreen': False, + 'window/prefs_dialog_size': (745, 411), + 'show_status_bar': True, + 'memory_usage/enable': True, + 'memory_usage/timeout': 2000, + 'cpu_usage/enable': False, + 'cpu_usage/timeout': 2000, + 'use_custom_margin': True, + 'custom_margin': 0, + 'show_internal_console_if_traceback': True, + 'check_updates_on_startup': True, + 'toolbars_visible': True, + # Global Spyder fonts + 'font/family': MONOSPACE, + 'font/size': MEDIUM, + 'font/italic': False, + 'font/bold': False, + 'rich_font/family': SANS_SERIF, + 'rich_font/size': BIG, + 'rich_font/italic': False, + 'rich_font/bold': False, + 'cursor/width': 2, + 'completion/size': (300, 180), + }), + ('quick_layouts', + { + 'place_holder': '', + 'names': ['Matlab layout', 'Rstudio layout', 'Vertical split', 'Horizontal split'], + 'order': ['Matlab layout', 'Rstudio layout', 'Vertical split', 'Horizontal split'], + 'active': ['Matlab layout', 'Rstudio layout', 'Vertical split', 'Horizontal split'], + }), + ('internal_console', + { + 'max_line_count': 300, + 'working_dir_history': 30, + 'working_dir_adjusttocontents': False, + 'wrap': True, + 'calltips': True, + 'codecompletion/auto': False, + 'codecompletion/enter_key': True, + 'codecompletion/case_sensitive': True, + 'external_editor/path': 'SciTE', + 'external_editor/gotoline': '-goto:', + 'light_background': True, + }), + ('main_interpreter', + { + 'default': True, + 'custom': False, + 'umr/enabled': True, + 'umr/verbose': True, + 'umr/namelist': [], + }), + ('console', + { + 'max_line_count': 500, + 'wrap': True, + 'single_tab': True, + 'calltips': True, + 'codecompletion/auto': True, + 'codecompletion/enter_key': True, + 'codecompletion/case_sensitive': True, + 'show_elapsed_time': False, + 'show_icontext': False, + 'monitor/enabled': True, + 'qt/api': 'default', + 'matplotlib/backend/value': 0, + 'light_background': True, + 'merge_output_channels': os.name != 'nt', + 'colorize_sys_stderr': os.name != 'nt', + 'pythonstartup/default': True, + 'pythonstartup/custom': False, + 'ets_backend': 'qt4' + }), + ('ipython_console', + { + 'show_banner': True, + 'completion_type': 0, + 'use_pager': False, + 'show_calltips': True, + 'ask_before_closing': False, + 'buffer_size': 500, + 'pylab': True, + 'pylab/autoload': False, + 'pylab/backend': 0, + 'pylab/inline/figure_format': 0, + 'pylab/inline/resolution': 72, + 'pylab/inline/width': 6, + 'pylab/inline/height': 4, + 'startup/run_lines': '', + 'startup/use_run_file': False, + 'startup/run_file': '', + 'greedy_completer': False, + 'autocall': 0, + 'symbolic_math': False, + 'in_prompt': '', + 'out_prompt': '', + 'light_color': True, + 'dark_color': False + }), + ('variable_explorer', + { + 'autorefresh': False, + 'autorefresh/timeout': 2000, + 'check_all': CHECK_ALL, + 'excluded_names': EXCLUDED_NAMES, + 'exclude_private': True, + 'exclude_uppercase': True, + 'exclude_capitalized': False, + 'exclude_unsupported': True, + 'truncate': True, + 'minmax': False, + 'remote_editing': False, + }), + ('editor', + { + 'printer_header/font/family': SANS_SERIF, + 'printer_header/font/size': MEDIUM, + 'printer_header/font/italic': False, + 'printer_header/font/bold': False, + 'wrap': False, + 'wrapflag': True, + 'code_analysis/pyflakes': True, + 'code_analysis/pep8': False, + 'todo_list': True, + 'realtime_analysis': True, + 'realtime_analysis/timeout': 2500, + 'outline_explorer': True, + 'line_numbers': True, + 'blank_spaces': False, + 'edge_line': True, + 'edge_line_column': 79, + 'toolbox_panel': True, + 'calltips': True, + 'go_to_definition': True, + 'close_parentheses': True, + 'close_quotes': False, + 'add_colons': True, + 'auto_unindent': True, + 'indent_chars': '* *', + 'tab_stop_width': 40, + 'codecompletion/auto': True, + 'codecompletion/enter_key': True, + 'codecompletion/case_sensitive': True, + 'check_eol_chars': True, + 'tab_always_indent': False, + 'intelligent_backspace': True, + 'highlight_current_line': True, + 'highlight_current_cell': True, + 'occurrence_highlighting': True, + 'occurrence_highlighting/timeout': 1500, + 'always_remove_trailing_spaces': False, + 'fullpath_sorting': True, + 'show_tab_bar': True, + 'max_recent_files': 20, + 'save_all_before_run': True, + 'focus_to_editor': True, + 'onsave_analysis': False + }), + ('historylog', + { + 'enable': True, + 'max_entries': 100, + 'wrap': True, + 'go_to_eof': True, + }), + ('help', + { + 'enable': True, + 'max_history_entries': 20, + 'wrap': True, + 'connect/editor': False, + 'connect/python_console': False, + 'connect/ipython_console': False, + 'math': True, + 'automatic_import': True, + }), + ('onlinehelp', + { + 'enable': True, + 'zoom_factor': .8, + 'max_history_entries': 20, + }), + ('outline_explorer', + { + 'enable': True, + 'show_fullpath': False, + 'show_all_files': False, + 'show_comments': True, + }), + ('project_explorer', + { + 'name_filters': NAME_FILTERS, + 'show_all': True, + 'show_hscrollbar': True + }), + ('explorer', + { + 'enable': True, + 'wrap': True, + 'name_filters': NAME_FILTERS, + 'show_hidden': True, + 'show_all': True, + 'show_icontext': False, + }), + ('find_in_files', + { + 'enable': True, + 'supported_encodings': ["utf-8", "iso-8859-1", "cp1252"], + 'include': '', + 'include_regexp': True, + 'exclude': EXCLUDE_PATTERNS, + 'exclude_regexp': True, + 'search_text_regexp': True, + 'search_text': [''], + 'search_text_samples': [codeanalysis.TASKS_PATTERN], + 'in_python_path': False, + 'more_options': False, + }), + ('workingdir', + { + 'editor/open/browse_scriptdir': True, + 'editor/open/browse_workdir': False, + 'editor/new/browse_scriptdir': False, + 'editor/new/browse_workdir': True, + 'editor/open/auto_set_to_basedir': False, + 'editor/save/auto_set_to_basedir': False, + 'working_dir_adjusttocontents': False, + 'working_dir_history': 20, + 'startup/use_last_directory': True, + }), + ('shortcuts', + { + # ---- Global ---- + # -- In app/spyder.py + '_/close pane': "Shift+Ctrl+F4", + '_/lock unlock panes': "Shift+Ctrl+F5", + '_/use next layout': "Shift+Alt+PgDown", + '_/use previous layout': "Shift+Alt+PgUp", + '_/preferences': "Ctrl+Alt+Shift+P", + '_/maximize pane': "Ctrl+Alt+Shift+M", + '_/fullscreen mode': "F11", + '_/save current layout': "Shift+Alt+S", + '_/layout preferences': "Shift+Alt+P", + '_/show toolbars': "Alt+Shift+T", + '_/restart': "Shift+Alt+R", + '_/quit': "Ctrl+Q", + # -- In plugins/editor + '_/file switcher': 'Ctrl+P', + '_/debug': "Ctrl+F5", + '_/debug step over': "Ctrl+F10", + '_/debug continue': "Ctrl+F12", + '_/debug step into': "Ctrl+F11", + '_/debug step return': "Ctrl+Shift+F11", + '_/debug exit': "Ctrl+Shift+F12", + '_/run': "F5", + '_/configure': "Ctrl+F6", + '_/re-run last script': "F6", + # -- In plugins/init + '_/switch to help': "Ctrl+Shift+H", + '_/switch to outline_explorer': "Ctrl+Shift+O", + '_/switch to editor': "Ctrl+Shift+E", + '_/switch to historylog': "Ctrl+Shift+L", + '_/switch to onlinehelp': "Ctrl+Shift+D", + '_/switch to project_explorer': "Ctrl+Shift+P", + '_/switch to console': "Ctrl+Shift+C", + '_/switch to ipython_console': "Ctrl+Shift+I", + '_/switch to variable_explorer': "Ctrl+Shift+V", + '_/switch to find_in_files': "Ctrl+Shift+F", + '_/switch to explorer': "Ctrl+Shift+X", + # -- In widgets/findreplace.py + '_/find text': "Ctrl+F", + '_/find next': "F3", + '_/find previous': "Shift+F3", + '_/replace text': "Ctrl+R", + # ---- Editor ---- + # -- In widgets/sourcecode/codeeditor.py + 'editor/code completion': CTRL+'+Space', + 'editor/duplicate line': "Ctrl+Alt+Up" if WIN else \ + "Shift+Alt+Up", + 'editor/copy line': "Ctrl+Alt+Down" if WIN else \ + "Shift+Alt+Down", + 'editor/delete line': 'Ctrl+D', + 'editor/transform to uppercase': 'Ctrl+Shift+U', + 'editor/transform to lowercase': 'Ctrl+U', + 'editor/move line up': "Alt+Up", + 'editor/move line down': "Alt+Down", + 'editor/go to definition': "Ctrl+G", + 'editor/toggle comment': "Ctrl+1", + 'editor/blockcomment': "Ctrl+4", + 'editor/unblockcomment': "Ctrl+5", + 'editor/start of line': "Meta+A", + 'editor/end of line': "Meta+E", + 'editor/previous line': "Meta+P", + 'editor/next line': "Meta+N", + 'editor/previous char': "Meta+B", + 'editor/next char': "Meta+F", + 'editor/previous word': "Meta+Left", + 'editor/next word': "Meta+Right", + 'editor/kill to line end': "Meta+K", + 'editor/kill to line start': "Meta+U", + 'editor/yank': 'Meta+Y', + 'editor/rotate kill ring': 'Shift+Meta+Y', + 'editor/kill previous word': 'Meta+Backspace', + 'editor/kill next word': 'Meta+D', + 'editor/start of document': 'Ctrl+Up', + 'editor/end of document': 'Ctrl+Down', + 'editor/undo': 'Ctrl+Z', + 'editor/redo': 'Ctrl+Shift+Z', + 'editor/cut': 'Ctrl+X', + 'editor/copy': 'Ctrl+C', + 'editor/paste': 'Ctrl+V', + 'editor/delete': 'Delete', + 'editor/select all': "Ctrl+A", + # -- In widgets/editor.py + 'editor/inspect current object': 'Ctrl+I', + 'editor/breakpoint': 'F12', + 'editor/conditional breakpoint': 'Shift+F12', + 'editor/run selection': "F9", + 'editor/go to line': 'Ctrl+L', + 'editor/go to previous file': 'Ctrl+Tab', + 'editor/go to next file': 'Ctrl+Shift+Tab', + 'editor/new file': "Ctrl+N", + 'editor/open file': "Ctrl+O", + 'editor/save file': "Ctrl+S", + 'editor/save all': "Ctrl+Alt+S", + 'editor/save as': 'Ctrl+Shift+S', + 'editor/close all': "Ctrl+Shift+W", + 'editor/last edit location': "Ctrl+Alt+Shift+Left", + 'editor/previous cursor position': "Ctrl+Alt+Left", + 'editor/next cursor position': "Ctrl+Alt+Right", + # -- In plugins/editor.py + 'editor/show/hide outline': "Ctrl+Alt+O", + 'editor/show/hide project explorer': "Ctrl+Alt+P", + # -- In Breakpoints + '_/switch to breakpoints': "Ctrl+Shift+B", + # ---- Consoles (in widgets/shell) ---- + 'console/inspect current object': "Ctrl+I", + 'console/clear shell': "Ctrl+L", + 'console/clear line': "Shift+Escape", + # ---- In Pylint ---- + 'pylint/run analysis': "F8", + # ---- In Profiler ---- + 'profiler/run profiler': "F10" + }), + ('color_schemes', + { + 'names': ['emacs', 'idle', 'monokai', 'pydev', 'scintilla', + 'spyder', 'spyder/dark', 'zenburn'], + 'selected': 'spyder', + # ---- Emacs ---- + 'emacs/name': "Emacs", + # Name Color Bold Italic + 'emacs/background': "#000000", + 'emacs/currentline': "#2b2b43", + 'emacs/currentcell': "#1c1c2d", + 'emacs/occurrence': "#abab67", + 'emacs/ctrlclick': "#0000ff", + 'emacs/sideareas': "#555555", + 'emacs/matched_p': "#009800", + 'emacs/unmatched_p': "#c80000", + 'emacs/normal': ('#ffffff', False, False), + 'emacs/keyword': ('#3c51e8', False, False), + 'emacs/builtin': ('#900090', False, False), + 'emacs/definition': ('#ff8040', True, False), + 'emacs/comment': ('#005100', False, False), + 'emacs/string': ('#00aa00', False, True), + 'emacs/number': ('#800000', False, False), + 'emacs/instance': ('#ffffff', False, True), + # ---- IDLE ---- + 'idle/name': "IDLE", + # Name Color Bold Italic + 'idle/background': "#ffffff", + 'idle/currentline': "#f2e6f3", + 'idle/currentcell': "#feefff", + 'idle/occurrence': "#e8f2fe", + 'idle/ctrlclick': "#0000ff", + 'idle/sideareas': "#efefef", + 'idle/matched_p': "#99ff99", + 'idle/unmatched_p': "#ff9999", + 'idle/normal': ('#000000', False, False), + 'idle/keyword': ('#ff7700', True, False), + 'idle/builtin': ('#900090', False, False), + 'idle/definition': ('#0000ff', False, False), + 'idle/comment': ('#dd0000', False, True), + 'idle/string': ('#00aa00', False, False), + 'idle/number': ('#924900', False, False), + 'idle/instance': ('#777777', True, True), + # ---- Monokai ---- + 'monokai/name': "Monokai", + # Name Color Bold Italic + 'monokai/background': "#2a2b24", + 'monokai/currentline': "#484848", + 'monokai/currentcell': "#3d3d3d", + 'monokai/occurrence': "#666666", + 'monokai/ctrlclick': "#0000ff", + 'monokai/sideareas': "#2a2b24", + 'monokai/matched_p': "#688060", + 'monokai/unmatched_p': "#bd6e76", + 'monokai/normal': ("#ddddda", False, False), + 'monokai/keyword': ("#f92672", False, False), + 'monokai/builtin': ("#ae81ff", False, False), + 'monokai/definition': ("#a6e22e", False, False), + 'monokai/comment': ("#75715e", False, True), + 'monokai/string': ("#e6db74", False, False), + 'monokai/number': ("#ae81ff", False, False), + 'monokai/instance': ("#ddddda", False, True), + # ---- Pydev ---- + 'pydev/name': "Pydev", + # Name Color Bold Italic + 'pydev/background': "#ffffff", + 'pydev/currentline': "#e8f2fe", + 'pydev/currentcell': "#eff8fe", + 'pydev/occurrence': "#ffff99", + 'pydev/ctrlclick': "#0000ff", + 'pydev/sideareas': "#efefef", + 'pydev/matched_p': "#99ff99", + 'pydev/unmatched_p': "#ff99992", + 'pydev/normal': ('#000000', False, False), + 'pydev/keyword': ('#0000ff', False, False), + 'pydev/builtin': ('#900090', False, False), + 'pydev/definition': ('#000000', True, False), + 'pydev/comment': ('#c0c0c0', False, False), + 'pydev/string': ('#00aa00', False, True), + 'pydev/number': ('#800000', False, False), + 'pydev/instance': ('#000000', False, True), + # ---- Scintilla ---- + 'scintilla/name': "Scintilla", + # Name Color Bold Italic + 'scintilla/background': "#ffffff", + 'scintilla/currentline': "#e1f0d1", + 'scintilla/currentcell': "#edfcdc", + 'scintilla/occurrence': "#ffff99", + 'scintilla/ctrlclick': "#0000ff", + 'scintilla/sideareas': "#efefef", + 'scintilla/matched_p': "#99ff99", + 'scintilla/unmatched_p': "#ff9999", + 'scintilla/normal': ('#000000', False, False), + 'scintilla/keyword': ('#00007f', True, False), + 'scintilla/builtin': ('#000000', False, False), + 'scintilla/definition': ('#007f7f', True, False), + 'scintilla/comment': ('#007f00', False, False), + 'scintilla/string': ('#7f007f', False, False), + 'scintilla/number': ('#007f7f', False, False), + 'scintilla/instance': ('#000000', False, True), + # ---- Spyder ---- + 'spyder/name': "Spyder", + # Name Color Bold Italic + 'spyder/background': "#ffffff", + 'spyder/currentline': "#f7ecf8", + 'spyder/currentcell': "#fdfdde", + 'spyder/occurrence': "#ffff99", + 'spyder/ctrlclick': "#0000ff", + 'spyder/sideareas': "#efefef", + 'spyder/matched_p': "#99ff99", + 'spyder/unmatched_p': "#ff9999", + 'spyder/normal': ('#000000', False, False), + 'spyder/keyword': ('#0000ff', False, False), + 'spyder/builtin': ('#900090', False, False), + 'spyder/definition': ('#000000', True, False), + 'spyder/comment': ('#adadad', False, True), + 'spyder/string': ('#00aa00', False, False), + 'spyder/number': ('#800000', False, False), + 'spyder/instance': ('#924900', False, True), + # ---- Spyder/Dark ---- + 'spyder/dark/name': "Spyder Dark", + # Name Color Bold Italic + 'spyder/dark/background': "#131926", + 'spyder/dark/currentline': "#2b2b43", + 'spyder/dark/currentcell': "#31314e", + 'spyder/dark/occurrence': "#abab67", + 'spyder/dark/ctrlclick': "#0000ff", + 'spyder/dark/sideareas': "#282828", + 'spyder/dark/matched_p': "#009800", + 'spyder/dark/unmatched_p': "#c80000", + 'spyder/dark/normal': ('#ffffff', False, False), + 'spyder/dark/keyword': ('#558eff', False, False), + 'spyder/dark/builtin': ('#aa00aa', False, False), + 'spyder/dark/definition': ('#ffffff', True, False), + 'spyder/dark/comment': ('#7f7f7f', False, False), + 'spyder/dark/string': ('#11a642', False, True), + 'spyder/dark/number': ('#c80000', False, False), + 'spyder/dark/instance': ('#be5f00', False, True), + # ---- Zenburn ---- + 'zenburn/name': "Zenburn", + # Name Color Bold Italic + 'zenburn/background': "#3f3f3f", + 'zenburn/currentline': "#333333", + 'zenburn/currentcell': "#2c2c2c", + 'zenburn/occurrence': "#7a738f", + 'zenburn/ctrlclick': "#0000ff", + 'zenburn/sideareas': "#3f3f3f", + 'zenburn/matched_p': "#688060", + 'zenburn/unmatched_p': "#bd6e76", + 'zenburn/normal': ('#dcdccc', False, False), + 'zenburn/keyword': ('#dfaf8f', True, False), + 'zenburn/builtin': ('#efef8f', False, False), + 'zenburn/definition': ('#efef8f', False, False), + 'zenburn/comment': ('#7f9f7f', False, True), + 'zenburn/string': ('#cc9393', False, False), + 'zenburn/number': ('#8cd0d3', False, False), + 'zenburn/instance': ('#dcdccc', False, True) + }) + ] + + +#============================================================================== +# Config instance +#============================================================================== +# IMPORTANT NOTES: +# 1. If you want to *change* the default value of a current option, you need to +# do a MINOR update in config version, e.g. from 3.0.0 to 3.1.0 +# 2. If you want to *remove* options that are no longer needed in our codebase, +# or if you want to *rename* options, then you need to do a MAJOR update in +# version, e.g. from 3.0.0 to 4.0.0 +# 3. You don't need to touch this value if you're just adding a new option +CONF_VERSION = '29.0.0' + +# Main configuration instance +try: + CONF = UserConfig('spyder', defaults=DEFAULTS, load=(not TEST), + version=CONF_VERSION, subfolder=SUBFOLDER, backup=True, + raw_mode=True) +except: + CONF = UserConfig('spyder', defaults=DEFAULTS, load=False, + version=CONF_VERSION, subfolder=SUBFOLDER, backup=True, + raw_mode=True) + +# Removing old .spyder.ini location: +old_location = osp.join(get_home_dir(), '.spyder.ini') +if osp.isfile(old_location): + os.remove(old_location) diff -Nru spyder-2.3.8+dfsg1/spyder/config/user.py spyder-3.0.2+dfsg1/spyder/config/user.py --- spyder-2.3.8+dfsg1/spyder/config/user.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/config/user.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,445 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +This module provides user configuration file management features for Spyder + +It's based on the ConfigParser module (present in the standard library). +""" + +from __future__ import print_function + +# Std imports +import ast +import os +import re +import os.path as osp +import shutil +import time + +# Local imports +from spyder.config.base import (get_conf_path, get_home_dir, + get_module_source_path, TEST) +from spyder.utils.programs import check_version +from spyder.py3compat import configparser as cp +from spyder.py3compat import PY2, is_text_string, to_text_string + +# Std imports for Python 2 +if PY2: + import codecs + + +#============================================================================== +# Auxiliary classes +#============================================================================== +class NoDefault: + pass + + +#============================================================================== +# Defaults class +#============================================================================== +class DefaultsConfig(cp.ConfigParser): + """ + Class used to save defaults to a file and as base class for + UserConfig + """ + def __init__(self, name, subfolder): + cp.ConfigParser.__init__(self) + self.name = name + self.subfolder = subfolder + + def _write(self, fp): + """ + Private write method for Python 2 + The one from configparser fails for non-ascii Windows accounts + """ + if self._defaults: + fp.write("[%s]\n" % cp.DEFAULTSECT) + for (key, value) in self._defaults.items(): + fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) + fp.write("\n") + for section in self._sections: + fp.write("[%s]\n" % section) + for (key, value) in self._sections[section].items(): + if key == "__name__": + continue + if (value is not None) or (self._optcre == self.OPTCRE): + value = to_text_string(value) + key = " = ".join((key, value.replace('\n', '\n\t'))) + fp.write("%s\n" % (key)) + fp.write("\n") + + def _set(self, section, option, value, verbose): + """ + Private set method + """ + if not self.has_section(section): + self.add_section( section ) + if not is_text_string(value): + value = repr( value ) + if verbose: + print('%s[ %s ] = %s' % (section, option, value)) + cp.ConfigParser.set(self, section, option, value) + + def _save(self): + """ + Save config into the associated .ini file + """ + # Don't save settings if we are on testing mode + if TEST: + return + + # See Issue 1086 and 1242 for background on why this + # method contains all the exception handling. + fname = self.filename() + + def _write_file(fname): + if PY2: + # Python 2 + with codecs.open(fname, 'w', encoding='utf-8') as configfile: + self._write(configfile) + else: + # Python 3 + with open(fname, 'w', encoding='utf-8') as configfile: + self.write(configfile) + + try: # the "easy" way + _write_file(fname) + except IOError: + try: # the "delete and sleep" way + if osp.isfile(fname): + os.remove(fname) + time.sleep(0.05) + _write_file(fname) + except Exception as e: + print("Failed to write user configuration file.") + print("Please submit a bug report.") + raise(e) + + def filename(self): + """Defines the name of the configuration file to use.""" + # Needs to be done this way to be used by the project config. + # To fix on a later PR + self._filename = getattr(self, '_filename', None) + self._root_path = getattr(self, '_root_path', None) + + if self._filename is None and self._root_path is None: + return self._filename_global() + else: + return self._filename_projects() + + def _filename_projects(self): + """Create a .ini filename located in the current project directory. + This .ini files stores the specific project preferences for each + project created with spyder. + """ + return osp.join(self._root_path, self._filename) + + def _filename_global(self): + """Create a .ini filename located in user home directory. + This .ini files stores the global spyder preferences. + """ + if self.subfolder is None: + config_file = osp.join(get_home_dir(), '.%s.ini' % self.name) + return config_file + else: + folder = get_conf_path() + # Save defaults in a "defaults" dir of .spyder2 to not pollute it + if 'defaults' in self.name: + folder = osp.join(folder, 'defaults') + if not osp.isdir(folder): + os.mkdir(folder) + config_file = osp.join(folder, '%s.ini' % self.name) + return config_file + + def set_defaults(self, defaults): + for section, options in defaults: + for option in options: + new_value = options[ option ] + self._set(section, option, new_value, False) + + +#============================================================================== +# User config class +#============================================================================== +class UserConfig(DefaultsConfig): + """ + UserConfig class, based on ConfigParser + name: name of the config + defaults: dictionnary containing options + *or* list of tuples (section_name, options) + version: version of the configuration file (X.Y.Z format) + subfolder: configuration file will be saved in %home%/subfolder/%name%.ini + + Note that 'get' and 'set' arguments number and type + differ from the overriden methods + """ + DEFAULT_SECTION_NAME = 'main' + def __init__(self, name, defaults=None, load=True, version=None, + subfolder=None, backup=False, raw_mode=False, + remove_obsolete=False): + DefaultsConfig.__init__(self, name, subfolder) + self.raw = 1 if raw_mode else 0 + if (version is not None) and (re.match('^(\d+).(\d+).(\d+)$', version) is None): + raise ValueError("Version number %r is incorrect - must be in X.Y.Z format" % version) + if isinstance(defaults, dict): + defaults = [ (self.DEFAULT_SECTION_NAME, defaults) ] + self.defaults = defaults + if defaults is not None: + self.reset_to_defaults(save=False) + fname = self.filename() + if backup: + try: + shutil.copyfile(fname, "%s.bak" % fname) + except IOError: + pass + if load: + # If config file already exists, it overrides Default options: + self.load_from_ini() + old_ver = self.get_version(version) + _major = lambda _t: _t[:_t.find('.')] + _minor = lambda _t: _t[:_t.rfind('.')] + # Save new defaults + self._save_new_defaults(defaults, version, subfolder) + # Updating defaults only if major/minor version is different + if _minor(version) != _minor(old_ver): + if backup: + try: + shutil.copyfile(fname, "%s-%s.bak" % (fname, old_ver)) + except IOError: + pass + if check_version(old_ver, '2.4.0', '<'): + self.reset_to_defaults(save=False) + else: + self._update_defaults(defaults, old_ver) + # Remove deprecated options if major version has changed + if remove_obsolete or _major(version) != _major(old_ver): + self._remove_deprecated_options(old_ver) + # Set new version number + self.set_version(version, save=False) + if defaults is None: + # If no defaults are defined, set .ini file settings as default + self.set_as_defaults() + + def get_version(self, version='0.0.0'): + """Return configuration (not application!) version""" + return self.get(self.DEFAULT_SECTION_NAME, 'version', version) + + def set_version(self, version='0.0.0', save=True): + """Set configuration (not application!) version""" + self.set(self.DEFAULT_SECTION_NAME, 'version', version, save=save) + + def load_from_ini(self): + """ + Load config from the associated .ini file + """ + try: + if PY2: + # Python 2 + fname = self.filename() + if osp.isfile(fname): + try: + with codecs.open(fname, encoding='utf-8') as configfile: + self.readfp(configfile) + except IOError: + print("Failed reading file", fname) + else: + # Python 3 + self.read(self.filename(), encoding='utf-8') + except cp.MissingSectionHeaderError: + print("Warning: File contains no section headers.") + + def _load_old_defaults(self, old_version): + """Read old defaults""" + old_defaults = cp.ConfigParser() + if check_version(old_version, '3.0.0', '<='): + path = get_module_source_path('spyder') + else: + path = osp.dirname(self.filename()) + path = osp.join(path, 'defaults') + old_defaults.read(osp.join(path, 'defaults-'+old_version+'.ini')) + return old_defaults + + def _save_new_defaults(self, defaults, new_version, subfolder): + """Save new defaults""" + new_defaults = DefaultsConfig(name='defaults-'+new_version, + subfolder=subfolder) + if not osp.isfile(new_defaults.filename()): + new_defaults.set_defaults(defaults) + new_defaults._save() + + def _update_defaults(self, defaults, old_version, verbose=False): + """Update defaults after a change in version""" + old_defaults = self._load_old_defaults(old_version) + for section, options in defaults: + for option in options: + new_value = options[ option ] + try: + old_value = old_defaults.get(section, option) + except (cp.NoSectionError, cp.NoOptionError): + old_value = None + if old_value is None or \ + to_text_string(new_value) != old_value: + self._set(section, option, new_value, verbose) + + def _remove_deprecated_options(self, old_version): + """ + Remove options which are present in the .ini file but not in defaults + """ + old_defaults = self._load_old_defaults(old_version) + for section in old_defaults.sections(): + for option, _ in old_defaults.items(section, raw=self.raw): + if self.get_default(section, option) is NoDefault: + try: + self.remove_option(section, option) + if len(self.items(section, raw=self.raw)) == 0: + self.remove_section(section) + except cp.NoSectionError: + self.remove_section(section) + + def cleanup(self): + """ + Remove .ini file associated to config + """ + os.remove(self.filename()) + + def set_as_defaults(self): + """ + Set defaults from the current config + """ + self.defaults = [] + for section in self.sections(): + secdict = {} + for option, value in self.items(section, raw=self.raw): + secdict[option] = value + self.defaults.append( (section, secdict) ) + + def reset_to_defaults(self, save=True, verbose=False, section=None): + """ + Reset config to Default values + """ + for sec, options in self.defaults: + if section == None or section == sec: + for option in options: + value = options[ option ] + self._set(sec, option, value, verbose) + if save: + self._save() + + def _check_section_option(self, section, option): + """ + Private method to check section and option types + """ + if section is None: + section = self.DEFAULT_SECTION_NAME + elif not is_text_string(section): + raise RuntimeError("Argument 'section' must be a string") + if not is_text_string(option): + raise RuntimeError("Argument 'option' must be a string") + return section + + def get_default(self, section, option): + """ + Get Default value for a given (section, option) + -> useful for type checking in 'get' method + """ + section = self._check_section_option(section, option) + for sec, options in self.defaults: + if sec == section: + if option in options: + return options[ option ] + else: + return NoDefault + + def get(self, section, option, default=NoDefault): + """ + Get an option + section=None: attribute a default section name + default: default value (if not specified, an exception + will be raised if option doesn't exist) + """ + section = self._check_section_option(section, option) + + if not self.has_section(section): + if default is NoDefault: + raise cp.NoSectionError(section) + else: + self.add_section(section) + + if not self.has_option(section, option): + if default is NoDefault: + raise cp.NoOptionError(option, section) + else: + self.set(section, option, default) + return default + + value = cp.ConfigParser.get(self, section, option, raw=self.raw) + # Use type of default_value to parse value correctly + default_value = self.get_default(section, option) + if isinstance(default_value, bool): + value = ast.literal_eval(value) + elif isinstance(default_value, float): + value = float(value) + elif isinstance(default_value, int): + value = int(value) + else: + if PY2 and is_text_string(default_value): + try: + value = value.decode('utf-8') + except (UnicodeEncodeError, UnicodeDecodeError): + pass + try: + # lists, tuples, ... + value = ast.literal_eval(value) + except (SyntaxError, ValueError): + pass + return value + + def set_default(self, section, option, default_value): + """ + Set Default value for a given (section, option) + -> called when a new (section, option) is set and no default exists + """ + section = self._check_section_option(section, option) + for sec, options in self.defaults: + if sec == section: + options[ option ] = default_value + + def set(self, section, option, value, verbose=False, save=True): + """ + Set an option + section=None: attribute a default section name + """ + section = self._check_section_option(section, option) + default_value = self.get_default(section, option) + if default_value is NoDefault: + # This let us save correctly string value options with + # no config default that contain non-ascii chars in + # Python 2 + if PY2 and is_text_string(value): + value = repr(value) + default_value = value + self.set_default(section, option, default_value) + if isinstance(default_value, bool): + value = bool(value) + elif isinstance(default_value, float): + value = float(value) + elif isinstance(default_value, int): + value = int(value) + elif not is_text_string(default_value): + value = repr(value) + self._set(section, option, value, verbose) + if save: + self._save() + + def remove_section(self, section): + cp.ConfigParser.remove_section(self, section) + self._save() + + def remove_option(self, section, option): + cp.ConfigParser.remove_option(self, section, option) + self._save() diff -Nru spyder-2.3.8+dfsg1/spyder/config/utils.py spyder-3.0.2+dfsg1/spyder/config/utils.py --- spyder-2.3.8+dfsg1/spyder/config/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/config/utils.py 2016-11-16 16:11:22.000000000 +0000 @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Utilities to define configuration values +""" + +import os +import os.path as osp +import sys + +from spyder.config.base import _ +from spyder.utils import iofuncs + + +#============================================================================== +# Constants +#============================================================================== +# File types supported by the Editor up to Spyder 2.3 +EDIT_FILETYPES = [ + (_("Python files"), ('.py', '.pyw', '.ipy')), + (_("Cython/Pyrex files"), ('.pyx', '.pxd', '.pxi')), + (_("C files"), ('.c', '.h')), + (_("C++ files"), ('.cc', '.cpp', '.cxx', '.h', '.hh', '.hpp', '.hxx')), + (_("OpenCL files"), ('.cl', )), + (_("Fortran files"), ('.f', '.for', '.f77', '.f90', '.f95', '.f2k')), + (_("IDL files"), ('.pro', )), + (_("MATLAB files"), ('.m', )), + (_("Julia files"), ('.jl',)), + (_("Yaml files"), ('.yaml','.yml',)), + (_("Patch and diff files"), ('.patch', '.diff', '.rej')), + (_("Batch files"), ('.bat', '.cmd')), + (_("Text files"), ('.txt',)), + (_("reStructuredText files"), ('.txt', '.rst')), + (_("gettext files"), ('.po', '.pot')), + (_("NSIS files"), ('.nsi', '.nsh')), + (_("Web page files"), ('.scss', '.css', '.htm', '.html',)), + (_("XML files"), ('.xml',)), + (_("Javascript files"), ('.js',)), + (_("Json files"), ('.json',)), + (_("IPython notebooks"), ('.ipynb',)), + (_("Enaml files"), ('.enaml',)), + (_("Configuration files"), ('.properties', '.session', '.ini', '.inf', + '.reg', '.cfg', '.desktop')), +] + +# Filter for all files +ALL_FILTER = "%s (*)" % _("All files") + +# Extensions supported by Spyder's Variable explorer +IMPORT_EXT = list(iofuncs.iofunctions.load_extensions.values()) + + +#============================================================================== +# Auxiliary functions +#============================================================================== +def _create_filter(title, ftypes): + return "%s (*%s)" % (title, " *".join(ftypes)) + + +def _get_filters(filetypes): + filters = [] + for title, ftypes in filetypes: + filters.append(_create_filter(title, ftypes)) + filters.append(ALL_FILTER) + return ";;".join(filters) + + +def _get_extensions(filetypes): + ftype_list = [] + for _title, ftypes in filetypes: + ftype_list += list(ftypes) + return ftype_list + + +def _get_pygments_extensions(): + """Return all file type extensions supported by Pygments""" + # NOTE: Leave this import here to keep startup process fast! + import pygments.lexers as lexers + + extensions = [] + for lx in lexers.get_all_lexers(): + lexer_exts = lx[2] + + if lexer_exts: + # Reference: This line was included for leaving untrimmed the + # extensions not starting with `*` + other_exts = [le for le in lexer_exts if not le.startswith('*')] + # Reference: This commented line was replaced by the following one + # to trim only extensions that start with '*' + # lexer_exts = [le[1:] for le in lexer_exts] + lexer_exts = [le[1:] for le in lexer_exts if le.startswith('*')] + lexer_exts = [le for le in lexer_exts if not le.endswith('_*')] + extensions = extensions + list(lexer_exts) + list(other_exts) + + return sorted(list(set(extensions))) + + +#============================================================================== +# Main functions +#============================================================================== +def get_filter(filetypes, ext): + """Return filter associated to file extension""" + if not ext: + return ALL_FILTER + for title, ftypes in filetypes: + if ext in ftypes: + return _create_filter(title, ftypes) + else: + return '' + + +def get_edit_filetypes(): + """Get all file types supported by the Editor""" + pygments_exts = _get_pygments_extensions() + other_exts = ['.ipynb', '.md'] + all_exts = tuple(pygments_exts + other_exts) + text_filetypes = (_("Supported text files"), all_exts) + return [text_filetypes] + EDIT_FILETYPES + + +def get_edit_filters(): + """ + Return filters associated with the file types + supported by the Editor + """ + edit_filetypes = get_edit_filetypes() + return _get_filters(edit_filetypes) + + +def get_edit_extensions(): + """ + Return extensions associated with the file types + supported by the Editor + """ + edit_filetypes = get_edit_filetypes() + return _get_extensions(edit_filetypes)+[''] + + +#============================================================================== +# Detection of OS specific versions +#============================================================================== +def is_ubuntu(): + "Detect if we are running in an Ubuntu-based distribution" + if sys.platform.startswith('linux') and osp.isfile('/etc/lsb-release'): + release_info = open('/etc/lsb-release').read() + if 'Ubuntu' in release_info: + return True + else: + return False + else: + return False + + +def is_gtk_desktop(): + "Detect if we are running in a Gtk-based desktop" + if sys.platform.startswith('linux'): + xdg_desktop = os.environ.get('XDG_CURRENT_DESKTOP', '') + if xdg_desktop: + gtk_desktops = ['Unity', 'GNOME', 'XFCE'] + if any([xdg_desktop.startswith(d) for d in gtk_desktops]): + return True + else: + return False + else: + return False + else: + return False diff -Nru spyder-2.3.8+dfsg1/spyder/defaults/defaults-2.4.0.ini spyder-3.0.2+dfsg1/spyder/defaults/defaults-2.4.0.ini --- spyder-2.3.8+dfsg1/spyder/defaults/defaults-2.4.0.ini 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/defaults/defaults-2.4.0.ini 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,505 @@ +[main] +lightwindow/is_fullscreen = False +memory_usage/timeout = 2000 +custom_margin = 0 +vertical_dockwidget_titlebars = False +lightwindow/size = (650, 400) +show_internal_console_if_traceback = True +memory_usage/enable = True +single_instance = True +window/is_maximized = False +cpu_usage/enable = False +lightwindow/is_maximized = False +animated_docks = True +window/is_fullscreen = False +cpu_usage/timeout = 2000 +window/size = (1260, 740) +open_files_port = 21128 +lightwindow/prefs_dialog_size = (745, 411) +window/prefs_dialog_size = (745, 411) +window/position = (10, 10) +lightwindow/position = (30, 30) +tear_off_menus = False +vertical_tabs = False +use_custom_margin = True + +[quick_layouts] +place_holder = + +[editor_appearance] +completion/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +calltips/font/size = 9 +calltips/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +cursor/width = 2 +calltips/font/italic = False +completion/font/size = 9 +completion/size = (300, 180) +completion/font/bold = False +calltips/size = 600 +calltips/font/bold = False +completion/font/italic = False + +[shell_appearance] +completion/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +calltips/font/size = 9 +calltips/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +cursor/width = 2 +calltips/font/italic = False +completion/font/size = 9 +completion/size = (300, 180) +completion/font/bold = False +calltips/size = 600 +calltips/font/bold = False +completion/font/italic = False + +[internal_console] +working_dir_adjusttocontents = False +external_editor/gotoline = -goto: +font/italic = False +calltips = True +working_dir_history = 30 +external_editor/path = SciTE +max_line_count = 300 +shortcut = None +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +codecompletion/enter_key = True +font/bold = False +font/size = 9 +codecompletion/auto = False +wrap = True +codecompletion/case_sensitive = True +light_background = True +codecompletion/show_single = False + +[console] +pythonexecutable/default = True +colorize_sys_stderr = True +umd/enabled = True +show_icontext = False +calltips = True +matplotlib/backend/value = Qt4Agg +single_tab = True +qt/install_inputhook = True +max_line_count = 10000 +pythonstartup/default = False +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +pyqt/ignore_sip_setapi_errors = False +qt/api = default +pythonexecutable/custom = False +font/size = 9 +codecompletion/auto = True +wrap = True +umd/verbose = True +matplotlib/patch = True +codecompletion/show_single = False +matplotlib/backend/enabled = True +monitor/enabled = True +pythonstartup/custom = True +light_background = True +font/italic = False +codecompletion/enter_key = True +ets_backend = qt4 +merge_output_channels = True +show_elapsed_time = True +pyqt/api_version = 0 +shortcut = Ctrl+Shift+C +open_python_at_startup = True +font/bold = False +umd/namelist = ['guidata', 'guiqwt'] +codecompletion/case_sensitive = True +object_inspector = True + +[ipython_console] +show_calltips = False +pylab = True +symbolic_math = False +pylab/inline/height = 4 +open_ipython_at_startup = False +out_prompt = +autocall = 0 +in_prompt = +shortcut = None +font/bold = False +startup/run_lines = +startup/run_file = +pylab/inline/figure_format = 0 +greedy_completer = False +pylab/inline/resolution = 72 +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +dark_color = False +ask_before_closing = True +pylab/backend = 0 +font/size = 9 +light_color = True +buffer_size = 10000 +show_banner = True +font/italic = False +pylab/inline/width = 6 +use_gui_completion = True +use_pager = True +startup/use_run_file = False +object_inspector = True +pylab/autoload = True + +[variable_explorer] +collvalue = False +truncate = True +exclude_unsupported = True +minmax = False +exclude_uppercase = True +check_all = False +exclude_private = True +autorefresh = True +inplace = False +shortcut = Ctrl+Shift+V +excluded_names = ['nan', 'inf', 'infty', 'little_endian', 'colorbar_doc', 'typecodes', '__builtins__', '__main__', '__doc__', 'NaN', 'Inf', 'Infinity', 'sctypes', 'rcParams', 'rcParamsDefault', 'sctypeNA', 'typeNA', 'False_', 'True_'] +autorefresh/timeout = 2000 +exclude_capitalized = False +remote_editing = False + +[editor] +wrapflag = True +edge_line = True +add_colons = True +always_remove_trailing_spaces = False +auto_unindent = True +max_recent_files = 20 +onsave_analysis = False +wrap = False +indent_chars = * * +outline_explorer = True +show_tab_bar = True +shortcut = Ctrl+Shift+E +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +codecompletion/auto = True +fullpath_sorting = True +font/italic = False +check_eol_chars = True +intelligent_backspace = True +realtime_analysis/timeout = 2500 +todo_list = True +close_quotes = False +occurence_highlighting = True +object_inspector = True +go_to_definition = True +tab_stop_width = 40 +tab_always_indent = False +printer_header/font/bold = False +codecompletion/show_single = False +printer_header/font/italic = False +realtime_analysis = True +font/bold = False +printer_header/font/family = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] +toolbox_panel = True +calltips = True +highlight_current_line = True +font/size = 9 +edge_line_column = 79 +close_parentheses = True +save_all_before_run = True +code_analysis/pyflakes = True +line_numbers = True +codecompletion/enter_key = True +code_analysis/pep8 = False +printer_header/font/size = 9 +codecompletion/case_sensitive = True +occurence_highlighting/timeout = 1500 + +[historylog] +max_entries = 100 +go_to_eof = True +font/bold = False +enable = True +font/size = 9 +font/italic = False +wrap = True +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +shortcut = Ctrl+Shift+H + +[inspector] +max_history_entries = 20 +enable = True +font/italic = False +rich_text/font/italic = False +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +shortcut = Ctrl+Shift+I +automatic_import = True +connect/ipython_console = False +font/bold = False +rich_text/font/size = 12 +font/size = 9 +connect/python_console = False +wrap = True +connect/editor = False +rich_text/font/family = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] +rich_text/font/bold = False +math = True + +[onlinehelp] +max_history_entries = 20 +enable = True +zoom_factor = 0.8 +shortcut = Ctrl+Shift+D + +[outline_explorer] +show_comments = True +show_fullpath = False +enable = True +shortcut = Ctrl+Shift+O +show_all_files = False + +[project_explorer] +show_all = False +name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*.spydata', '*.tif', '*.jpg', '*.npy', '*.gif', '*.csv', '*.png', '*.ico', '*.svg', 'README', 'INSTALL', 'LICENSE', 'CHANGELOG'] +enable = True +shortcut = Ctrl+Shift+P +show_hscrollbar = True + +[arrayeditor] +font/bold = False +font/size = 9 +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +font/italic = False + +[texteditor] +font/bold = False +font/size = 9 +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +font/italic = False + +[dicteditor] +font/bold = False +font/size = 9 +font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +font/italic = False + +[explorer] +enable = True +show_hidden = True +show_icontext = False +wrap = True +name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*.spydata', '*.tif', '*.jpg', '*.npy', '*.gif', '*.csv', '*.png', '*.ico', '*.svg', 'README', 'INSTALL', 'LICENSE', 'CHANGELOG'] +show_all = False +shortcut = None +show_toolbar = True + +[find_in_files] +enable = True +exclude_regexp = True +in_python_path = False +exclude = ['\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn|\\bbuild\\b', '\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn'] +search_text_regexp = True +more_options = True +search_text = [''] +supported_encodings = ['utf-8', 'iso-8859-1', 'cp1252'] +search_text_samples = ['(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP)([^#]*)'] +shortcut = None +include_regexp = True +include = ['\\.py$|\\.pyw$|\\.ipy$|\\.pyx$|\\.pxd$|\\.pxi$|\\.c$|\\.h$|\\.cc$|\\.cpp$|\\.cxx$|\\.h$|\\.hh$|\\.hpp$|\\.hxx$|\\.cl$|\\.f$|\\.for$|\\.f77$|\\.f90$|\\.f95$|\\.f2k$|\\.pro$|\\.m$|\\.patch$|\\.diff$|\\.rej$|\\.bat$|\\.cmd$|\\.txt$|\\.txt$|\\.rst$|\\.po$|\\.pot$|\\.nsi$|\\.nsh$|\\.css$|\\.htm$|\\.html$|\\.xml$|\\.js$|\\.enaml$|\\.properties$|\\.session$|\\.ini$|\\.inf$|\\.reg$|\\.cfg$|\\.desktop$|README|INSTALL', '\\.pyw?$|\\.ipy$|\\.txt$|\\.rst$', '.'] + +[workingdir] +working_dir_adjusttocontents = False +editor/new/browse_scriptdir = False +editor/open/auto_set_to_basedir = False +working_dir_history = 20 +editor/open/browse_scriptdir = True +editor/new/browse_workdir = True +editor/open/browse_workdir = False +startup/use_last_directory = True +editor/save/auto_set_to_basedir = False + +[shortcuts] +editor/duplicate line = Shift+Alt+Up +editor/go to next file = Ctrl+Shift+Tab +console/clear line = Shift+Escape +_/switch to outline_explorer = Ctrl+Shift+O +editor/show/hide outline = Ctrl+Alt+O +_/fullscreen mode = F11 +_/maximize plugin = Ctrl+Alt+Shift+M +_/maximize dockwidget = Ctrl+Alt+Shift+M +_/close plugin = Shift+Ctrl+F4 +_/close dockwidget = Shift+Ctrl+F4 +_/switch to inspector = Ctrl+Shift+I +profiler/run profiler = F10 +editor/move line down = Alt+Down +console/clear shell = Ctrl+L +pylint/run analysis = F8 +_/switch to onlinehelp = Ctrl+Shift+D +_/switch to editor = Ctrl+Shift+E +editor/code completion = Ctrl+Space +_/switch to variable_explorer = Ctrl+Shift+V +_/switch to/from layout 3 = Shift+Alt+F3 +_/preferences = Ctrl+Alt+Shift+P +_/switch to/from layout 1 = Shift+Alt+F1 +editor/run selection = F9 +_/debug step into = Ctrl+F11 +editor/toggle comment = Ctrl+1 +editor/go to definition = Ctrl+G +editor/show/hide project explorer = Ctrl+Alt+P +_/debug step return = Ctrl+Shift+F11 +editor/new file = Ctrl+N +_/debug step over = Ctrl+F10 +editor/save all = Ctrl+Shift+S +editor/unblockcomment = Ctrl+5 +_/debug exit = Ctrl+Shift+F12 +editor/go to previous file = Ctrl+Tab +editor/next cursor position = Ctrl+Alt+Right +editor/debug = Ctrl+F5 +editor/copy line = Shift+Alt+Down +editor/file list management = Ctrl+E +editor/debug with winpdb = F7 +_/quit = Ctrl+Q +editor/find next = F3 +editor/move line up = Alt+Up +console/inspect current object = Ctrl+I +editor/find previous = Shift+F3 +_/set layout 2 = Ctrl+Shift+Alt+F2 +_/set layout 3 = Ctrl+Shift+Alt+F3 +_/set layout 1 = Ctrl+Shift+Alt+F1 +_/switch to console = Ctrl+Shift+C +editor/re-run last script = Ctrl+F6 +editor/previous cursor position = Ctrl+Alt+Left +_/switch to project_explorer = Ctrl+Shift+P +editor/open file = Ctrl+O +editor/inspect current object = Ctrl+I +editor/last edit location = Ctrl+Alt+Shift+Left +editor/print = Ctrl+P +editor/configure = F6 +editor/breakpoint = F12 +editor/find text = Ctrl+F +editor/list breakpoints = Ctrl+B +editor/run = F5 +editor/close all = Ctrl+Shift+W +_/debug continue = Ctrl+F12 +editor/blockcomment = Ctrl+4 +editor/close file = Ctrl+W +editor/conditional breakpoint = Shift+F12 +_/switch to/from layout 2 = Shift+Alt+F2 +editor/replace text = Ctrl+H +editor/save file = Ctrl+S +editor/go to line = Ctrl+L +_/switch to historylog = Ctrl+Shift+H +editor/delete line = Ctrl+D + +[color_schemes] +names = ['Emacs', 'IDLE', 'Monokai', 'Pydev', 'Scintilla', 'Spyder', 'Spyder/Dark', 'Zenburn'] +monokai/background = #2a2b24 +monokai/currentline = #484848 +monokai/occurence = #666666 +monokai/ctrlclick = #0000ff +monokai/sideareas = #2a2b24 +monokai/matched_p = #688060 +monokai/unmatched_p = #bd6e76 +monokai/normal = ('#ddddda', False, False) +monokai/keyword = ('#f92672', False, False) +monokai/builtin = ('#ae81ff', False, False) +monokai/definition = ('#a6e22e', False, False) +monokai/comment = ('#75715e', False, True) +monokai/string = ('#e6db74', False, False) +monokai/number = ('#ae81ff', False, False) +monokai/instance = ('#ddddda', False, True) +idle/background = #ffffff +idle/currentline = #eeffdd +idle/occurence = #e8f2fe +idle/ctrlclick = #0000ff +idle/sideareas = #efefef +idle/matched_p = #99ff99 +idle/unmatched_p = #ff9999 +idle/normal = ('#000000', False, False) +idle/keyword = ('#ff7700', True, False) +idle/builtin = ('#900090', False, False) +idle/definition = ('#0000ff', False, False) +idle/comment = ('#dd0000', False, True) +idle/string = ('#00aa00', False, False) +idle/number = ('#924900', False, False) +idle/instance = ('#777777', True, True) +emacs/background = #000000 +emacs/currentline = #2b2b43 +emacs/occurence = #abab67 +emacs/ctrlclick = #0000ff +emacs/sideareas = #555555 +emacs/matched_p = #009800 +emacs/unmatched_p = #c80000 +emacs/normal = ('#ffffff', False, False) +emacs/keyword = ('#3c51e8', False, False) +emacs/builtin = ('#900090', False, False) +emacs/definition = ('#ff8040', True, False) +emacs/comment = ('#005100', False, False) +emacs/string = ('#00aa00', False, True) +emacs/number = ('#800000', False, False) +emacs/instance = ('#ffffff', False, True) +zenburn/background = #3f3f3f +zenburn/currentline = #333333 +zenburn/occurence = #7a738f +zenburn/ctrlclick = #0000ff +zenburn/sideareas = #3f3f3f +zenburn/matched_p = #688060 +zenburn/unmatched_p = #bd6e76 +zenburn/normal = ('#dcdccc', False, False) +zenburn/keyword = ('#dfaf8f', True, False) +zenburn/builtin = ('#efef8f', False, False) +zenburn/definition = ('#efef8f', False, False) +zenburn/comment = ('#7f9f7f', False, True) +zenburn/string = ('#cc9393', False, False) +zenburn/number = ('#8cd0d3', False, False) +zenburn/instance = ('#dcdccc', False, True) +spyder/dark/background = #131926 +spyder/dark/currentline = #2b2b43 +spyder/dark/occurence = #abab67 +spyder/dark/ctrlclick = #0000ff +spyder/dark/sideareas = #282828 +spyder/dark/matched_p = #009800 +spyder/dark/unmatched_p = #c80000 +spyder/dark/normal = ('#ffffff', False, False) +spyder/dark/keyword = ('#558eff', False, False) +spyder/dark/builtin = ('#aa00aa', False, False) +spyder/dark/definition = ('#ffffff', True, False) +spyder/dark/comment = ('#7f7f7f', False, False) +spyder/dark/string = ('#11a642', False, True) +spyder/dark/number = ('#c80000', False, False) +spyder/dark/instance = ('#be5f00', False, True) +scintilla/background = #ffffff +scintilla/currentline = #eeffdd +scintilla/occurence = #ffff99 +scintilla/ctrlclick = #0000ff +scintilla/sideareas = #efefef +scintilla/matched_p = #99ff99 +scintilla/unmatched_p = #ff9999 +scintilla/normal = ('#000000', False, False) +scintilla/keyword = ('#00007f', True, False) +scintilla/builtin = ('#000000', False, False) +scintilla/definition = ('#007f7f', True, False) +scintilla/comment = ('#007f00', False, False) +scintilla/string = ('#7f007f', False, False) +scintilla/number = ('#007f7f', False, False) +scintilla/instance = ('#000000', False, True) +pydev/background = #ffffff +pydev/currentline = #e8f2fe +pydev/occurence = #ffff99 +pydev/ctrlclick = #0000ff +pydev/sideareas = #efefef +pydev/matched_p = #99ff99 +pydev/unmatched_p = #ff9999 +pydev/normal = ('#000000', False, False) +pydev/keyword = ('#0000ff', False, False) +pydev/builtin = ('#900090', False, False) +pydev/definition = ('#000000', True, False) +pydev/comment = ('#c0c0c0', False, False) +pydev/string = ('#00aa00', False, True) +pydev/number = ('#800000', False, False) +pydev/instance = ('#000000', False, True) +spyder/background = #ffffff +spyder/currentline = #feefff +spyder/occurence = #ffff99 +spyder/ctrlclick = #0000ff +spyder/sideareas = #efefef +spyder/matched_p = #99ff99 +spyder/unmatched_p = #ff9999 +spyder/normal = ('#000000', False, False) +spyder/keyword = ('#0000ff', False, False) +spyder/builtin = ('#900090', False, False) +spyder/definition = ('#000000', True, False) +spyder/comment = ('#adadad', False, True) +spyder/string = ('#00aa00', False, False) +spyder/number = ('#800000', False, False) +spyder/instance = ('#924900', False, True) diff -Nru spyder-2.3.8+dfsg1/spyder/defaults/defaults-3.0.0.ini spyder-3.0.2+dfsg1/spyder/defaults/defaults-3.0.0.ini --- spyder-2.3.8+dfsg1/spyder/defaults/defaults-3.0.0.ini 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/defaults/defaults-3.0.0.ini 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,487 @@ +[main] +lightwindow/is_fullscreen = False +memory_usage/timeout = 2000 +custom_margin = 0 +vertical_dockwidget_titlebars = False +lightwindow/size = (650, 400) +show_internal_console_if_traceback = True +memory_usage/enable = True +single_instance = True +window/is_maximized = True +cpu_usage/enable = False +lightwindow/is_maximized = False +animated_docks = True +window/is_fullscreen = False +cpu_usage/timeout = 2000 +window/size = (1260, 740) +open_files_port = 21128 +lightwindow/prefs_dialog_size = (745, 411) +window/prefs_dialog_size = (745, 411) +window/position = (10, 10) +lightwindow/position = (30, 30) +tear_off_menus = False +vertical_tabs = False +use_custom_margin = True + +[quick_layouts] +place_holder = + +[editor_appearance] +completion/size = (300, 180) +cursor/width = 2 + +[shell_appearance] +completion/size = (300, 180) +cursor/width = 2 + +[internal_console] +working_dir_adjusttocontents = False +external_editor/gotoline = -goto: +font/italic = False +calltips = True +working_dir_history = 30 +external_editor/path = SciTE +max_line_count = 300 +shortcut = None +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +codecompletion/enter_key = True +font/bold = False +font/size = 10 +codecompletion/auto = False +wrap = True +codecompletion/case_sensitive = True +light_background = True +codecompletion/show_single = False + +[console] +pythonexecutable/default = True +colorize_sys_stderr = True +umd/enabled = True +show_icontext = False +calltips = True +matplotlib/backend/value = Qt4Agg +single_tab = True +qt/install_inputhook = True +max_line_count = 500 +pythonstartup/default = True +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +pyqt/ignore_sip_setapi_errors = False +qt/api = default +pythonexecutable/custom = False +font/size = 10 +codecompletion/auto = True +wrap = True +umd/verbose = True +matplotlib/patch = True +codecompletion/show_single = False +matplotlib/backend/enabled = True +monitor/enabled = True +pythonstartup/custom = False +light_background = True +font/italic = False +codecompletion/enter_key = True +ets_backend = qt4 +merge_output_channels = True +show_elapsed_time = False +pyqt/api_version = 0 +shortcut = Ctrl+Shift+C +open_python_at_startup = True +font/bold = False +umd/namelist = ['guidata', 'guiqwt'] +codecompletion/case_sensitive = True +object_inspector = True + +[ipython_console] +show_calltips = True +pylab = True +symbolic_math = False +pylab/inline/height = 4 +open_ipython_at_startup = True +out_prompt = +autocall = 0 +in_prompt = +shortcut = None +font/bold = False +startup/run_lines = +startup/run_file = +pylab/inline/figure_format = 0 +greedy_completer = False +pylab/inline/resolution = 72 +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +dark_color = False +ask_before_closing = True +pylab/backend = 0 +font/size = 10 +light_color = True +buffer_size = 500 +show_banner = True +font/italic = False +pylab/inline/width = 6 +use_gui_completion = True +use_pager = True +startup/use_run_file = False +object_inspector = True +pylab/autoload = False + +[variable_explorer] +collvalue = False +truncate = True +exclude_unsupported = True +minmax = False +exclude_uppercase = True +check_all = False +exclude_private = True +autorefresh = True +inplace = False +shortcut = Ctrl+Shift+V +excluded_names = ['nan', 'inf', 'infty', 'little_endian', 'colorbar_doc', 'typecodes', '__builtins__', '__main__', '__doc__', 'NaN', 'Inf', 'Infinity', 'sctypes', 'rcParams', 'rcParamsDefault', 'sctypeNA', 'typeNA', 'False_', 'True_'] +autorefresh/timeout = 2000 +exclude_capitalized = False +remote_editing = False + +[editor] +wrapflag = True +edge_line = True +add_colons = True +always_remove_trailing_spaces = False +auto_unindent = True +max_recent_files = 20 +onsave_analysis = False +wrap = False +indent_chars = * * +outline_explorer = True +show_tab_bar = True +shortcut = Ctrl+Shift+E +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +codecompletion/auto = True +fullpath_sorting = True +font/italic = False +check_eol_chars = True +intelligent_backspace = True +realtime_analysis/timeout = 2500 +todo_list = True +close_quotes = False +occurence_highlighting = True +object_inspector = True +go_to_definition = True +tab_stop_width = 40 +tab_always_indent = False +printer_header/font/bold = False +codecompletion/show_single = False +printer_header/font/italic = False +realtime_analysis = True +font/bold = False +printer_header/font/family = ['Ubuntu', 'Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] +toolbox_panel = True +calltips = True +highlight_current_line = True +font/size = 10 +edge_line_column = 79 +close_parentheses = True +save_all_before_run = True +code_analysis/pyflakes = True +line_numbers = True +codecompletion/enter_key = True +code_analysis/pep8 = False +printer_header/font/size = 10 +codecompletion/case_sensitive = True +occurence_highlighting/timeout = 1500 + +[historylog] +max_entries = 100 +go_to_eof = True +font/bold = False +enable = True +font/size = 10 +font/italic = False +wrap = True +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +shortcut = Ctrl+Shift+H + +[inspector] +max_history_entries = 20 +enable = True +font/italic = False +rich_text/font/italic = False +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +shortcut = Ctrl+Shift+I +automatic_import = True +connect/ipython_console = False +font/bold = False +rich_text/font/size = 13 +font/size = 10 +connect/python_console = False +wrap = True +connect/editor = False +rich_text/font/family = ['Ubuntu', 'Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] +rich_text/font/bold = False +math = True + +[onlinehelp] +max_history_entries = 20 +enable = True +zoom_factor = 0.8 +shortcut = Ctrl+Shift+D + +[outline_explorer] +show_comments = True +show_fullpath = False +enable = True +shortcut = Ctrl+Shift+O +show_all_files = False + +[project_explorer] +show_all = False +name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.jl', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*.spydata', '*.tif', '*.jpg', '*.npy', '*.gif', '*.csv', '*.png', '*.ico', '*.svg', 'README', 'INSTALL', 'LICENSE', 'CHANGELOG'] +enable = True +shortcut = Ctrl+Shift+P +show_hscrollbar = True + +[arrayeditor] +font/bold = False +font/size = 10 +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +font/italic = False + +[texteditor] +font/bold = False +font/size = 10 +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +font/italic = False + +[dicteditor] +font/bold = False +font/size = 10 +font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] +font/italic = False + +[explorer] +enable = True +show_hidden = True +show_icontext = False +wrap = True +name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.jl', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*.spydata', '*.tif', '*.jpg', '*.npy', '*.gif', '*.csv', '*.png', '*.ico', '*.svg', 'README', 'INSTALL', 'LICENSE', 'CHANGELOG'] +show_all = False +shortcut = None +show_toolbar = True + +[find_in_files] +enable = True +exclude_regexp = True +in_python_path = False +exclude = ['\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn|\\bbuild\\b', '\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn'] +search_text_regexp = True +more_options = True +search_text = [''] +supported_encodings = ['utf-8', 'iso-8859-1', 'cp1252'] +search_text_samples = ['(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP|@todo)([^#]*)'] +shortcut = None +include_regexp = True +include = ['\\.py$|\\.pyw$|\\.ipy$|\\.pyx$|\\.pxd$|\\.pxi$|\\.c$|\\.h$|\\.cc$|\\.cpp$|\\.cxx$|\\.h$|\\.hh$|\\.hpp$|\\.hxx$|\\.cl$|\\.f$|\\.for$|\\.f77$|\\.f90$|\\.f95$|\\.f2k$|\\.pro$|\\.m$|\\.jl$|\\.patch$|\\.diff$|\\.rej$|\\.bat$|\\.cmd$|\\.txt$|\\.txt$|\\.rst$|\\.po$|\\.pot$|\\.nsi$|\\.nsh$|\\.css$|\\.htm$|\\.html$|\\.xml$|\\.js$|\\.enaml$|\\.properties$|\\.session$|\\.ini$|\\.inf$|\\.reg$|\\.cfg$|\\.desktop$|README|INSTALL', '\\.pyw?$|\\.ipy$|\\.txt$|\\.rst$', '.'] + +[workingdir] +working_dir_adjusttocontents = False +editor/new/browse_scriptdir = False +editor/open/auto_set_to_basedir = False +working_dir_history = 20 +editor/open/browse_scriptdir = True +editor/new/browse_workdir = True +editor/open/browse_workdir = False +startup/use_last_directory = True +editor/save/auto_set_to_basedir = False + +[shortcuts] +editor/duplicate line = Shift+Alt+Up +editor/go to next file = Ctrl+Shift+Tab +console/clear line = Shift+Escape +_/switch to outline_explorer = Ctrl+Shift+O +editor/show/hide outline = Ctrl+Alt+O +editor/blockcomment = Ctrl+4 +_/maximize plugin = Ctrl+Alt+Shift+M +_/maximize dockwidget = Ctrl+Alt+Shift+M +_/close plugin = Shift+Ctrl+F4 +_/close dockwidget = Shift+Ctrl+F4 +_/switch to inspector = Ctrl+Shift+I +profiler/run profiler = F10 +editor/move line down = Alt+Down +console/clear shell = Ctrl+L +pylint/run analysis = F8 +_/switch to onlinehelp = Ctrl+Shift+D +_/switch to editor = Ctrl+Shift+E +editor/code completion = Ctrl+Space +_/switch to variable_explorer = Ctrl+Shift+V +_/switch to/from layout 3 = Shift+Alt+F3 +_/preferences = Ctrl+Alt+Shift+P +_/switch to/from layout 1 = Shift+Alt+F1 +editor/run selection = F9 +_/debug step into = Ctrl+F11 +_/fullscreen mode = F11 +editor/toggle comment = Ctrl+1 +editor/go to definition = Ctrl+G +editor/show/hide project explorer = Ctrl+Alt+P +_/debug step return = Ctrl+Shift+F11 +editor/new file = Ctrl+N +_/debug step over = Ctrl+F10 +editor/save all = Ctrl+Shift+S +editor/unblockcomment = Ctrl+5 +_/debug exit = Ctrl+Shift+F12 +editor/go to previous file = Ctrl+Tab +editor/next cursor position = Ctrl+Alt+Right +editor/debug = Ctrl+F5 +editor/copy line = Shift+Alt+Down +editor/file list management = Ctrl+E +editor/debug with winpdb = F7 +_/quit = Ctrl+Q +editor/find next = F3 +editor/move line up = Alt+Up +console/inspect current object = Ctrl+I +editor/find previous = Shift+F3 +_/set layout 2 = Ctrl+Shift+Alt+F2 +_/set layout 3 = Ctrl+Shift+Alt+F3 +_/set layout 1 = Ctrl+Shift+Alt+F1 +_/switch to console = Ctrl+Shift+C +editor/re-run last script = Ctrl+F6 +editor/previous cursor position = Ctrl+Alt+Left +_/switch to project_explorer = Ctrl+Shift+P +editor/open file = Ctrl+O +editor/inspect current object = Ctrl+I +editor/last edit location = Ctrl+Alt+Shift+Left +editor/print = Ctrl+P +editor/breakpoint = F12 +editor/find text = Ctrl+F +editor/list breakpoints = Ctrl+B +editor/run = F5 +editor/close all = Ctrl+Shift+W +_/debug continue = Ctrl+F12 +editor/configure = F6 +editor/close file = Ctrl+W +editor/conditional breakpoint = Shift+F12 +_/switch to/from layout 2 = Shift+Alt+F2 +editor/replace text = Ctrl+H +editor/save file = Ctrl+S +editor/go to line = Ctrl+L +_/switch to historylog = Ctrl+Shift+H +editor/delete line = Ctrl+D + +[color_schemes] +names = ['Emacs', 'IDLE', 'Monokai', 'Pydev', 'Scintilla', 'Spyder', 'Spyder/Dark', 'Zenburn'] +monokai/background = #2a2b24 +monokai/currentline = #484848 +monokai/occurence = #666666 +monokai/ctrlclick = #0000ff +monokai/sideareas = #2a2b24 +monokai/matched_p = #688060 +monokai/unmatched_p = #bd6e76 +monokai/normal = ('#ddddda', False, False) +monokai/keyword = ('#f92672', False, False) +monokai/builtin = ('#ae81ff', False, False) +monokai/definition = ('#a6e22e', False, False) +monokai/comment = ('#75715e', False, True) +monokai/string = ('#e6db74', False, False) +monokai/number = ('#ae81ff', False, False) +monokai/instance = ('#ddddda', False, True) +idle/background = #ffffff +idle/currentline = #eeffdd +idle/occurence = #e8f2fe +idle/ctrlclick = #0000ff +idle/sideareas = #efefef +idle/matched_p = #99ff99 +idle/unmatched_p = #ff9999 +idle/normal = ('#000000', False, False) +idle/keyword = ('#ff7700', True, False) +idle/builtin = ('#900090', False, False) +idle/definition = ('#0000ff', False, False) +idle/comment = ('#dd0000', False, True) +idle/string = ('#00aa00', False, False) +idle/number = ('#924900', False, False) +idle/instance = ('#777777', True, True) +emacs/background = #000000 +emacs/currentline = #2b2b43 +emacs/occurence = #abab67 +emacs/ctrlclick = #0000ff +emacs/sideareas = #555555 +emacs/matched_p = #009800 +emacs/unmatched_p = #c80000 +emacs/normal = ('#ffffff', False, False) +emacs/keyword = ('#3c51e8', False, False) +emacs/builtin = ('#900090', False, False) +emacs/definition = ('#ff8040', True, False) +emacs/comment = ('#005100', False, False) +emacs/string = ('#00aa00', False, True) +emacs/number = ('#800000', False, False) +emacs/instance = ('#ffffff', False, True) +zenburn/background = #3f3f3f +zenburn/currentline = #333333 +zenburn/occurence = #7a738f +zenburn/ctrlclick = #0000ff +zenburn/sideareas = #3f3f3f +zenburn/matched_p = #688060 +zenburn/unmatched_p = #bd6e76 +zenburn/normal = ('#dcdccc', False, False) +zenburn/keyword = ('#dfaf8f', True, False) +zenburn/builtin = ('#efef8f', False, False) +zenburn/definition = ('#efef8f', False, False) +zenburn/comment = ('#7f9f7f', False, True) +zenburn/string = ('#cc9393', False, False) +zenburn/number = ('#8cd0d3', False, False) +zenburn/instance = ('#dcdccc', False, True) +spyder/dark/background = #131926 +spyder/dark/currentline = #2b2b43 +spyder/dark/occurence = #abab67 +spyder/dark/ctrlclick = #0000ff +spyder/dark/sideareas = #282828 +spyder/dark/matched_p = #009800 +spyder/dark/unmatched_p = #c80000 +spyder/dark/normal = ('#ffffff', False, False) +spyder/dark/keyword = ('#558eff', False, False) +spyder/dark/builtin = ('#aa00aa', False, False) +spyder/dark/definition = ('#ffffff', True, False) +spyder/dark/comment = ('#7f7f7f', False, False) +spyder/dark/string = ('#11a642', False, True) +spyder/dark/number = ('#c80000', False, False) +spyder/dark/instance = ('#be5f00', False, True) +scintilla/background = #ffffff +scintilla/currentline = #eeffdd +scintilla/occurence = #ffff99 +scintilla/ctrlclick = #0000ff +scintilla/sideareas = #efefef +scintilla/matched_p = #99ff99 +scintilla/unmatched_p = #ff9999 +scintilla/normal = ('#000000', False, False) +scintilla/keyword = ('#00007f', True, False) +scintilla/builtin = ('#000000', False, False) +scintilla/definition = ('#007f7f', True, False) +scintilla/comment = ('#007f00', False, False) +scintilla/string = ('#7f007f', False, False) +scintilla/number = ('#007f7f', False, False) +scintilla/instance = ('#000000', False, True) +pydev/background = #ffffff +pydev/currentline = #e8f2fe +pydev/occurence = #ffff99 +pydev/ctrlclick = #0000ff +pydev/sideareas = #efefef +pydev/matched_p = #99ff99 +pydev/unmatched_p = #ff9999 +pydev/normal = ('#000000', False, False) +pydev/keyword = ('#0000ff', False, False) +pydev/builtin = ('#900090', False, False) +pydev/definition = ('#000000', True, False) +pydev/comment = ('#c0c0c0', False, False) +pydev/string = ('#00aa00', False, True) +pydev/number = ('#800000', False, False) +pydev/instance = ('#000000', False, True) +spyder/background = #ffffff +spyder/currentline = #feefff +spyder/occurence = #ffff99 +spyder/ctrlclick = #0000ff +spyder/sideareas = #efefef +spyder/matched_p = #99ff99 +spyder/unmatched_p = #ff9999 +spyder/normal = ('#000000', False, False) +spyder/keyword = ('#0000ff', False, False) +spyder/builtin = ('#900090', False, False) +spyder/definition = ('#000000', True, False) +spyder/comment = ('#adadad', False, True) +spyder/string = ('#00aa00', False, False) +spyder/number = ('#800000', False, False) +spyder/instance = ('#924900', False, True) diff -Nru spyder-2.3.8+dfsg1/spyder/defaults/Readme.txt spyder-3.0.2+dfsg1/spyder/defaults/Readme.txt --- spyder-2.3.8+dfsg1/spyder/defaults/Readme.txt 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/defaults/Readme.txt 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,25 @@ +Copyright (c) Spyder Project Contributors +Licensed under the terms of the MIT License +(see spyder/__init__.py for details) + +What is the purpose of this directory? +====================================== + +The files present here (licensed also MIT) are used to cleanly update user +configuration options from Spyder versions previous to 2.3. They way they did +an update was by resetting *all* config options to new defaults, which was +quite bad from a usability point of view. Now we compare new defaults against +a copy of their previous values and only change those that are different in +the user config file. This way almost all his/her values remain intact. + +In particular: + +* defaults-2.4.0.ini is used to do the update when the previous used version + is between 2.1.9 and 2.3.0beta3 + +* defaults-3.0.0.ini is used when the previous version is 2.3.0beta4 + +Notes +===== + +1. Please don't add more files here, unless you know what you're doing. diff -Nru spyder-2.3.8+dfsg1/spyder/dependencies.py spyder-3.0.2+dfsg1/spyder/dependencies.py --- spyder-2.3.8+dfsg1/spyder/dependencies.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/dependencies.py 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Module checking Spyder runtime dependencies""" + + +import os + +# Local imports +from spyder.utils import programs + + +class Dependency(object): + """Spyder's dependency + + version may starts with =, >=, > or < to specify the exact requirement ; + multiple conditions may be separated by ';' (e.g. '>=0.13;<1.0')""" + + OK = 'OK' + NOK = 'NOK' + + def __init__(self, modname, features, required_version, + installed_version=None, optional=False): + self.modname = modname + self.features = features + self.required_version = required_version + self.optional = optional + if installed_version is None: + try: + self.installed_version = programs.get_module_version(modname) + except: + # NOTE: Don't add any exception type here! + # Modules can fail to import in several ways besides + # ImportError + self.installed_version = None + else: + self.installed_version = installed_version + + def check(self): + """Check if dependency is installed""" + return programs.is_module_installed(self.modname, + self.required_version, + self.installed_version) + + def get_installed_version(self): + """Return dependency status (string)""" + if self.check(): + return '%s (%s)' % (self.installed_version, self.OK) + else: + return '%s (%s)' % (self.installed_version, self.NOK) + + def get_status(self): + """Return dependency status (string)""" + if self.check(): + return self.OK + else: + return self.NOK + + +DEPENDENCIES = [] + + +def add(modname, features, required_version, installed_version=None, + optional=False): + """Add Spyder dependency""" + global DEPENDENCIES + for dependency in DEPENDENCIES: + if dependency.modname == modname: + raise ValueError("Dependency has already been registered: %s"\ + % modname) + DEPENDENCIES += [Dependency(modname, features, required_version, + installed_version, optional)] + + +def check(modname): + """Check if required dependency is installed""" + for dependency in DEPENDENCIES: + if dependency.modname == modname: + return dependency.check() + else: + raise RuntimeError("Unkwown dependency %s" % modname) + + +def status(deps=DEPENDENCIES, linesep=os.linesep): + """Return a status of dependencies""" + maxwidth = 0 + col1 = [] + col2 = [] + for dependency in deps: + title1 = dependency.modname + title1 += ' ' + dependency.required_version + col1.append(title1) + maxwidth = max([maxwidth, len(title1)]) + col2.append(dependency.get_installed_version()) + text = "" + for index in range(len(deps)): + text += col1[index].ljust(maxwidth) + ': ' + col2[index] + linesep + return text + + +def missing_dependencies(): + """Return the status of missing dependencies (if any)""" + missing_deps = [] + for dependency in DEPENDENCIES: + if not dependency.check() and not dependency.optional: + missing_deps.append(dependency) + if missing_deps: + return status(deps=missing_deps, linesep='
') + else: + return "" diff -Nru spyder-2.3.8+dfsg1/spyder/fonts/spyder-charmap.json spyder-3.0.2+dfsg1/spyder/fonts/spyder-charmap.json --- spyder-2.3.8+dfsg1/spyder/fonts/spyder-charmap.json 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/fonts/spyder-charmap.json 2016-10-25 00:05:22.000000000 +0000 @@ -0,0 +1,49 @@ +{ + "continue": "0xE000", + "debug": "0xE001", + "run-cell": "0xE003", + "run-cell-inplace": "0xE004", + "step-forward": "0xE005", + "step-into": "0xE006", + "step-out": "0xE007", + "step-over": "0xE008", + "stop": "0xE009", + "cell": "0xE00A", + "cell-code": "0xE03B", + "cell-play": "0xE03C", + "cell-next": "0xE03D", + "cell-border": "0xE03E", + "run-one-inplace": "0xE00B", + "run-one": "0xE00C", + "python-logo": "0xE00D", + "python-logo-up": "0xE00E", + "python-logo-down": "0xE00F", + "spyder-logo-web": "0xE010", + "spyder-logo-snake": "0xE011", + "spyder-logo-background": "0xE012", + "inward": "0xE013", + "rows": "0xE014", + "window": "0xE015", + "maximize-pane": "0xE016", + "minimize-pane": "0xE017", + "ipython-logo": "0xE018", + "ipython-logo-alt": "0xE042", + "jupyter-logo": "0xE043", + "run-selection": "0xE019", + "text-select-all": "0xE01A", + "treeview": "0xE01B", + "circle-letter-a": "0xE01F", + "circle-letter-c": "0xE020", + "circle-letter-f": "0xE021", + "circle-hash": "0xE022", + "circle-letter-m": "0xE023", + "circle-percent": "0xE024", + "circle-letter-r": "0xE025", + "circle-underscore": "0xE026", + "cube-front": "0xE055", + "cube-bottom": "0xE056", + "cube-right": "0xE057", + "cube-left": "0xE058", + "cube-rear": "0xE059", + "cube-top": "0xE060" +} Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/fonts/spyder.ttf and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/fonts/spyder.ttf differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/1downarrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/1downarrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/1uparrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/1uparrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/2downarrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/2downarrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/2uparrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/2uparrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/arrow-continue.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/arrow-continue.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/arrow-step-in.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/arrow-step-in.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/arrow-step-out.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/arrow-step-out.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/arrow-step-over.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/arrow-step-over.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/auto_reload.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/auto_reload.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/browse_tab.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/browse_tab.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/check.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/check.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/cmdprompt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/cmdprompt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/collapse.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/collapse.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/collapse_selection.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/collapse_selection.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/configure.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/configure.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/copywop.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/copywop.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/delete.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/delete.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/edit24.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/edit24.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/edit_add.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/edit_add.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/editcopy.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/editcopy.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/editcut.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/editcut.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/editdelete.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/editdelete.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/editpaste.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/editpaste.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/edit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/edit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/edit_remove.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/edit_remove.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/eraser.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/eraser.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/exit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/exit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/expand.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/expand.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/expand_selection.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/expand_selection.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/filter.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/filter.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/findf.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/findf.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/findnext.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/findnext.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/find.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/find.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/findprevious.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/findprevious.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/folder_new.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/folder_new.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/hide.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/hide.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/hist.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/hist.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/home.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/home.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/imshow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/imshow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/insert.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/insert.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/lock_open.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/lock_open.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/lock.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/lock.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/magnifier.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/magnifier.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/maximize.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/maximize.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/next.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/next.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/options_less.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/options_less.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/options_more.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/options_more.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/plot.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/plot.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/previous.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/previous.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/redo.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/redo.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/reload.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/reload.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/rename.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/rename.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/replace.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/replace.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/restore.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/restore.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/show.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/show.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/special_paste.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/special_paste.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/stop_debug.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/stop_debug.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/stop.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/stop.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/synchronize.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/synchronize.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/tooloptions.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/tooloptions.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/undo.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/undo.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/unmaximize.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/unmaximize.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/up.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/up.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/window_fullscreen.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/window_fullscreen.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/window_nofullscreen.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/window_nofullscreen.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/zoom_in.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/zoom_in.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/actions/zoom_out.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/actions/zoom_out.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/advanced.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/advanced.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/arredit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/arredit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/arrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/arrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/bold.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/bold.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/browser.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/browser.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/chevron-left.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/chevron-left.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/chevron-right.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/chevron-right.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/cmdprompt_t.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/cmdprompt_t.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/console.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/console.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/editclear.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/editclear.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/environ.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/environ.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/history24.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/history24.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/history.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/history.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/ipython_console.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/ipython_console.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/ipython_console_t.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/ipython_console_t.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/kill.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/kill.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/loading_sprites.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/loading_sprites.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/prompt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/prompt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/python.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/python.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/python_t.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/python_t.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/restart.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/restart.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/run_small.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/run_small.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/syspath.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/syspath.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/console/terminated.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/console/terminated.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/dictedit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/dictedit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/attribute.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/attribute.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/blockcomment.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/blockcomment.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/breakpoint_big.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/breakpoint_big.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/breakpoint_cond_big.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/breakpoint_cond_big.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/breakpoint_cond_small.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/breakpoint_cond_small.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/breakpoint_small.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/breakpoint_small.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/bug.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/bug.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/cell.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/cell.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/class.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/class.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/close_panel.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/close_panel.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/comment.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/comment.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/convention.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/convention.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/debug.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/debug.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/error.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/error.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/filelist.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/filelist.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/file.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/file.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/fromcursor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/fromcursor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/function.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/function.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/gotoline.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/gotoline.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/highlight.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/highlight.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/horsplit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/horsplit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/indent.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/indent.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/last_edit_location.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/last_edit_location.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/method.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/method.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/module.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/module.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/newwindow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/newwindow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/next_cursor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/next_cursor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/next_wng.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/next_wng.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/no_match.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/no_match.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/outline_explorer.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/outline_explorer.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/outline_explorer_vis.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/outline_explorer_vis.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/prev_cursor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/prev_cursor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/prev_wng.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/prev_wng.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/private1.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/private1.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/private2.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/private2.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/refactor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/refactor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/run_again.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/run_again.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/run_cell_advance.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/run_cell_advance.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/run_cell.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/run_cell.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/run.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/run.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/run_selection.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/run_selection.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/run_settings.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/run_settings.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/selectall.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/selectall.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/select.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/select.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/todo_list.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/todo_list.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/todo.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/todo.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/uncomment.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/uncomment.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/unindent.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/unindent.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/versplit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/versplit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/warning.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/warning.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/editor/wng_list.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/editor/wng_list.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/eyedropper.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/eyedropper.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/filecloseall.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/filecloseall.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/fileclose.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/fileclose.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/fileimport.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/fileimport.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/filenew.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/filenew.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/fileopen.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/fileopen.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/filesaveas.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/filesaveas.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/filesave.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/filesave.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/print.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/print.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/file/save_all.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/file/save_all.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/bat.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/bat.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/bmp.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/bmp.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/cc.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/cc.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/cfg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/cfg.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/chm.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/chm.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/cl.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/cl.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/cmd.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/cmd.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/c.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/c.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/cpp.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/cpp.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/css.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/css.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/cxx.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/cxx.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/diff.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/diff.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/doc.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/doc.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/enaml.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/enaml.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/exe.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/exe.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/f77.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/f77.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/f90.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/f90.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/f.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/f.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/gif.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/gif.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/hh.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/hh.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/h.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/h.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/hpp.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/hpp.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/html.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/html.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/htm.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/htm.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/hxx.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/hxx.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/inf.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/inf.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/ini.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/ini.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/jl.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/jl.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/jpeg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/jpeg.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/jpg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/jpg.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/js.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/js.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/log.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/log.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/nsh.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/nsh.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/nsi.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/nsi.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/nt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/nt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/patch.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/patch.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/pdf.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/pdf.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/png.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/png.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/po.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/po.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/pot.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/pot.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/pps.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/pps.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/properties.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/properties.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/ps.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/ps.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/pxd.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/pxd.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/pxi.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/pxi.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/pyc.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/pyc.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/py.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/py.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/pyw.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/pyw.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/pyx.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/pyx.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/rar.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/rar.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/readme.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/readme.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/reg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/reg.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/rej.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/rej.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/scss.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/scss.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/session.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/session.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/tar.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/tar.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/tex.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/tex.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/tgz.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/tgz.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/tiff.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/tiff.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/tif.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/tif.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/ts.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/ts.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/txt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/txt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/ui.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/ui.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/xls.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/xls.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/xml.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/xml.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/filetypes/zip.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/filetypes/zip.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/font.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/font.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/genprefs.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/genprefs.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/help.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/help.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/italic.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/italic.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/keyboard.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/keyboard.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/matplotlib.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/matplotlib.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/none.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/none.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/not_found.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/not_found.png differ diff -Nru spyder-2.3.8+dfsg1/spyder/images/options.svg spyder-3.0.2+dfsg1/spyder/images/options.svg --- spyder-2.3.8+dfsg1/spyder/images/options.svg 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/images/options.svg 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/add_to_path.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/add_to_path.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/folder.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/folder.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/package.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/package.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/pp_folder.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/pp_folder.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/pp_package.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/pp_package.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/pp_project.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/pp_project.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/project_closed.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/project_closed.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/project.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/project.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/pydev.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/pydev.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/pythonpath.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/pythonpath.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/remove_from_path.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/remove_from_path.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/projects/show_all.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/projects/show_all.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/pythonpath.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/pythonpath.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/pythonxy.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/pythonxy.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/qtassistant.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/qtassistant.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/qtdesigner.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/qtdesigner.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/qtlinguist.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/qtlinguist.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/qt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/qt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/scipy.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/scipy.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/set_workdir.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/set_workdir.png differ diff -Nru spyder-2.3.8+dfsg1/spyder/images/splash.svg spyder-3.0.2+dfsg1/spyder/images/splash.svg --- spyder-2.3.8+dfsg1/spyder/images/splash.svg 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/images/splash.svg 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,3788 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + spyder + 3 + + Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/spyder.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/spyder.png differ diff -Nru spyder-2.3.8+dfsg1/spyder/images/spyder.svg spyder-3.0.2+dfsg1/spyder/images/spyder.svg --- spyder-2.3.8+dfsg1/spyder/images/spyder.svg 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/images/spyder.svg 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,384 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/tour-spyder-logo.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/tour-spyder-logo.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/upper_lower.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/upper_lower.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/vcs_browse.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/vcs_browse.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/vcs_commit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/vcs_commit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/vitables.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/vitables.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/whole_words.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/whole_words.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/images/win_env.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/images/win_env.png differ diff -Nru spyder-2.3.8+dfsg1/spyder/images/winpython.svg spyder-3.0.2+dfsg1/spyder/images/winpython.svg --- spyder-2.3.8+dfsg1/spyder/images/winpython.svg 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/images/winpython.svg 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,444 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru spyder-2.3.8+dfsg1/spyder/__init__.py spyder-3.0.2+dfsg1/spyder/__init__.py --- spyder-2.3.8+dfsg1/spyder/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/__init__.py 2016-11-20 22:51:59.000000000 +0000 @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +Spyder License Agreement (MIT License) +-------------------------------------- + +Copyright (c) Spyder Project Contributors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +""" + +version_info = (3, 0, 2) + +__version__ = '.'.join(map(str, version_info)) +__license__ = __doc__ +__project_url__ = 'https://github.com/spyder-ide/spyder' +__forum_url__ = 'http://groups.google.com/group/spyderlib' + +# Dear (Debian, RPM, ...) package makers, please feel free to customize the +# following path to module's data (images) and translations: +DATAPATH = LOCALEPATH = DOCPATH = MATHJAXPATH = JQUERYPATH = '' + + +import os +# Directory of the current file +__dir__ = os.path.dirname(os.path.abspath(__file__)) + + +def add_to_distribution(dist): + """Add package to py2exe/cx_Freeze distribution object + Extension to guidata.disthelpers""" + try: + dist.add_qt_bindings() + except AttributeError: + raise ImportError("This script requires guidata 1.5+") + for _modname in ('spyder', 'spyderplugins'): + dist.add_module_data_files(_modname, ("", ), + ('.png', '.svg', '.html', '.png', '.txt', + '.js', '.inv', '.ico', '.css', '.doctree', + '.qm', '.py',), + copy_to_root=False) + + +def get_versions(reporev=True): + """Get version information for components used by Spyder""" + import sys + import platform + + import qtpy + import qtpy.QtCore + + revision = None + if reporev: + from spyder.utils import vcs + revision, branch = vcs.get_git_revision(os.path.dirname(__dir__)) + + if not sys.platform == 'darwin': # To avoid a crash with our Mac app + system = platform.system() + else: + system = 'Darwin' + + return { + 'spyder': __version__, + 'python': platform.python_version(), # "2.7.3" + 'bitness': 64 if sys.maxsize > 2**32 else 32, + 'qt': qtpy.QtCore.__version__, + 'qt_api': qtpy.API_NAME, # PyQt5 or PyQt4 + 'qt_api_ver': qtpy.PYQT_VERSION, + 'system': system, # Linux, Windows, ... + 'revision': revision, # '9fdf926eccce' + } diff -Nru spyder-2.3.8+dfsg1/spyder/interpreter.py spyder-3.0.2+dfsg1/spyder/interpreter.py --- spyder-2.3.8+dfsg1/spyder/interpreter.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/interpreter.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,335 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Shell Interpreter""" + +from __future__ import print_function + +import sys +import atexit +import threading +import ctypes +import os +import re +import os.path as osp +import pydoc +from code import InteractiveConsole + +# Local imports: +from spyder.utils.dochelpers import isdefined +from spyder.utils import encoding, programs +from spyder.py3compat import is_text_string, getcwd +from spyder.utils.misc import remove_backslashes + +# Force Python to search modules in the current directory first: +sys.path.insert(0, '') + + +def guess_filename(filename): + """Guess filename""" + if osp.isfile(filename): + return filename + if not filename.endswith('.py'): + filename += '.py' + for path in [getcwd()] + sys.path: + fname = osp.join(path, filename) + if osp.isfile(fname): + return fname + elif osp.isfile(fname+'.py'): + return fname+'.py' + elif osp.isfile(fname+'.pyw'): + return fname+'.pyw' + return filename + +class Interpreter(InteractiveConsole, threading.Thread): + """Interpreter, executed in a separate thread""" + p1 = ">>> " + p2 = "... " + def __init__(self, namespace=None, exitfunc=None, + Output=None, WidgetProxy=None, debug=False): + """ + namespace: locals send to InteractiveConsole object + commands: list of commands executed at startup + """ + InteractiveConsole.__init__(self, namespace) + threading.Thread.__init__(self) + + self._id = None + + self.exit_flag = False + self.debug = debug + + # Execution Status + self.more = False + + if exitfunc is not None: + atexit.register(exitfunc) + + self.namespace = self.locals + self.namespace['__name__'] = '__main__' + self.namespace['execfile'] = self.execfile + self.namespace['runfile'] = self.runfile + self.namespace['raw_input'] = self.raw_input_replacement + self.namespace['help'] = self.help_replacement + + # Capture all interactive input/output + self.initial_stdout = sys.stdout + self.initial_stderr = sys.stderr + self.initial_stdin = sys.stdin + + # Create communication pipes + pr, pw = os.pipe() + self.stdin_read = os.fdopen(pr, "r") + self.stdin_write = os.fdopen(pw, "wb", 0) + self.stdout_write = Output() + self.stderr_write = Output() + + self.input_condition = threading.Condition() + self.widget_proxy = WidgetProxy(self.input_condition) + + self.redirect_stds() + + + #------ Standard input/output + def redirect_stds(self): + """Redirects stds""" + if not self.debug: + sys.stdout = self.stdout_write + sys.stderr = self.stderr_write + sys.stdin = self.stdin_read + + def restore_stds(self): + """Restore stds""" + if not self.debug: + sys.stdout = self.initial_stdout + sys.stderr = self.initial_stderr + sys.stdin = self.initial_stdin + + def raw_input_replacement(self, prompt=''): + """For raw_input builtin function emulation""" + self.widget_proxy.wait_input(prompt) + self.input_condition.acquire() + while not self.widget_proxy.data_available(): + self.input_condition.wait() + inp = self.widget_proxy.input_data + self.input_condition.release() + return inp + + def help_replacement(self, text=None, interactive=False): + """For help builtin function emulation""" + if text is not None and not interactive: + return pydoc.help(text) + elif text is None: + pyver = "%d.%d" % (sys.version_info[0], sys.version_info[1]) + self.write(""" +Welcome to Python %s! This is the online help utility. + +If this is your first time using Python, you should definitely check out +the tutorial on the Internet at http://www.python.org/doc/tut/. + +Enter the name of any module, keyword, or topic to get help on writing +Python programs and using Python modules. To quit this help utility and +return to the interpreter, just type "quit". + +To get a list of available modules, keywords, or topics, type "modules", +"keywords", or "topics". Each module also comes with a one-line summary +of what it does; to list the modules whose summaries contain a given word +such as "spam", type "modules spam". +""" % pyver) + else: + text = text.strip() + try: + eval("pydoc.help(%s)" % text) + except (NameError, SyntaxError): + print("no Python documentation found for '%r'" % text) + self.write(os.linesep) + self.widget_proxy.new_prompt("help> ") + inp = self.raw_input_replacement() + if inp.strip(): + self.help_replacement(inp, interactive=True) + else: + self.write(""" +You are now leaving help and returning to the Python interpreter. +If you want to ask for help on a particular object directly from the +interpreter, you can type "help(object)". Executing "help('string')" +has the same effect as typing a particular string at the help> prompt. +""") + + def run_command(self, cmd, new_prompt=True): + """Run command in interpreter""" + if cmd == 'exit()': + self.exit_flag = True + self.write('\n') + return + # -- Special commands type I + # (transformed into commands executed in the interpreter) + # ? command + special_pattern = r"^%s (?:r\')?(?:u\')?\"?\'?([a-zA-Z0-9_\.]+)" + run_match = re.match(special_pattern % 'run', cmd) + help_match = re.match(r'^([a-zA-Z0-9_\.]+)\?$', cmd) + cd_match = re.match(r"^\!cd \"?\'?([a-zA-Z0-9_ \.]+)", cmd) + if help_match: + cmd = 'help(%s)' % help_match.group(1) + # run command + elif run_match: + filename = guess_filename(run_match.groups()[0]) + cmd = "runfile('%s', args=None)" % remove_backslashes(filename) + # !cd system command + elif cd_match: + cmd = 'import os; os.chdir(r"%s")' % cd_match.groups()[0].strip() + # -- End of Special commands type I + + # -- Special commands type II + # (don't need code execution in interpreter) + xedit_match = re.match(special_pattern % 'xedit', cmd) + edit_match = re.match(special_pattern % 'edit', cmd) + clear_match = re.match(r"^clear ([a-zA-Z0-9_, ]+)", cmd) + # (external) edit command + if xedit_match: + filename = guess_filename(xedit_match.groups()[0]) + self.widget_proxy.edit(filename, external_editor=True) + # local edit command + elif edit_match: + filename = guess_filename(edit_match.groups()[0]) + if osp.isfile(filename): + self.widget_proxy.edit(filename) + else: + self.stderr_write.write( + "No such file or directory: %s\n" % filename) + # remove reference (equivalent to MATLAB's clear command) + elif clear_match: + varnames = clear_match.groups()[0].replace(' ', '').split(',') + for varname in varnames: + try: + self.namespace.pop(varname) + except KeyError: + pass + # Execute command + elif cmd.startswith('!'): + # System ! command + pipe = programs.run_shell_command(cmd[1:]) + txt_out = encoding.transcode( pipe.stdout.read().decode() ) + txt_err = encoding.transcode( pipe.stderr.read().decode().rstrip() ) + if txt_err: + self.stderr_write.write(txt_err) + if txt_out: + self.stdout_write.write(txt_out) + self.stdout_write.write('\n') + self.more = False + # -- End of Special commands type II + else: + # Command executed in the interpreter +# self.widget_proxy.set_readonly(True) + self.more = self.push(cmd) +# self.widget_proxy.set_readonly(False) + + if new_prompt: + self.widget_proxy.new_prompt(self.p2 if self.more else self.p1) + if not self.more: + self.resetbuffer() + + def run(self): + """Wait for input and run it""" + while not self.exit_flag: + self.run_line() + + def run_line(self): + line = self.stdin_read.readline() + if self.exit_flag: + return + # Remove last character which is always '\n': + self.run_command(line[:-1]) + + def get_thread_id(self): + """Return thread id""" + if self._id is None: + for thread_id, obj in list(threading._active.items()): + if obj is self: + self._id = thread_id + return self._id + + def raise_keyboard_interrupt(self): + if self.isAlive(): + ctypes.pythonapi.PyThreadState_SetAsyncExc(self.get_thread_id(), + ctypes.py_object(KeyboardInterrupt)) + return True + else: + return False + + + def closing(self): + """Actions to be done before restarting this interpreter""" + pass + + def execfile(self, filename): + """Exec filename""" + source = open(filename, 'r').read() + try: + try: + name = filename.encode('ascii') + except UnicodeEncodeError: + name = '' + code = compile(source, name, "exec") + except (OverflowError, SyntaxError): + InteractiveConsole.showsyntaxerror(self, filename) + else: + self.runcode(code) + + def runfile(self, filename, args=None): + """ + Run filename + args: command line arguments (string) + """ + if args is not None and not is_text_string(args): + raise TypeError("expected a character buffer object") + self.namespace['__file__'] = filename + sys.argv = [filename] + if args is not None: + for arg in args.split(): + sys.argv.append(arg) + self.execfile(filename) + sys.argv = [''] + self.namespace.pop('__file__') + + def eval(self, text): + """ + Evaluate text and return (obj, valid) + where *obj* is the object represented by *text* + and *valid* is True if object evaluation did not raise any exception + """ + assert is_text_string(text) + try: + return eval(text, self.locals), True + except: + return None, False + + def is_defined(self, objtxt, force_import=False): + """Return True if object is defined""" + return isdefined(objtxt, force_import=force_import, + namespace=self.locals) + + #=========================================================================== + # InteractiveConsole API + #=========================================================================== + def push(self, line): + """ + Push a line of source text to the interpreter + + The line should not have a trailing newline; it may have internal + newlines. The line is appended to a buffer and the interpreter’s + runsource() method is called with the concatenated contents of the + buffer as source. If this indicates that the command was executed + or invalid, the buffer is reset; otherwise, the command is incomplete, + and the buffer is left as it was after the line was appended. + The return value is True if more input is required, False if the line + was dealt with in some way (this is the same as runsource()). + """ + return InteractiveConsole.push(self, "#coding=utf-8\n" + line) + + def resetbuffer(self): + """Remove any unhandled source text from the input buffer""" + InteractiveConsole.resetbuffer(self) + Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/locale/es/LC_MESSAGES/spyder.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/locale/es/LC_MESSAGES/spyder.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder/locale/es/LC_MESSAGES/spyder.po spyder-3.0.2+dfsg1/spyder/locale/es/LC_MESSAGES/spyder.po --- spyder-2.3.8+dfsg1/spyder/locale/es/LC_MESSAGES/spyder.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/locale/es/LC_MESSAGES/spyder.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,6080 @@ +# -*- coding: utf-8 -*- +# Spyder's spanish translation file +# Copyright (C) 2011 Spyder Development team +# +msgid "" +msgstr "" +"Project-Id-Version: 2.1\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2016-11-11 18:52-0500\n" +"Last-Translator: Carlos Cordoba \n" +"Language-Team: Python\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Poedit-SourceCharset: utf-8\n" +"X-Poedit-Basepath: ../../../..\n" +"X-Generator: Poedit 1.8.9\n" + +#: spyder/app/mainwindow.py:125 +msgid "Initializing..." +msgstr "Inicializando..." + +#: spyder/app/mainwindow.py:241 +msgid "Numpy and Scipy documentation" +msgstr "Documentación de Numpy y Scipy" + +#: spyder/app/mainwindow.py:243 spyder/app/mainwindow.py:1027 +msgid "Matplotlib documentation" +msgstr "Documentación de Matplotlib" + +#: spyder/app/mainwindow.py:246 +msgid "PyQt4 Reference Guide" +msgstr "Manual de referencia de PyQt4" + +#: spyder/app/mainwindow.py:249 +msgid "PyQt4 API Reference" +msgstr "Referencia del API de PyQt4" + +#: spyder/app/mainwindow.py:251 +msgid "Python(x,y)" +msgstr "Python(x,y)" + +#: spyder/app/mainwindow.py:253 +msgid "WinPython" +msgstr "WinPython" + +#: spyder/app/mainwindow.py:525 +msgid "Close current pane" +msgstr "Cerrar panel actual" + +#: spyder/app/mainwindow.py:530 +msgid "Lock panes" +msgstr "Bloquear los paneles" + +#: spyder/app/mainwindow.py:537 +msgid "Use next layout" +msgstr "Utilizar la siguiente disposición" + +#: spyder/app/mainwindow.py:541 +msgid "Use previous layout" +msgstr "Utilizar la disposición anterior" + +#: spyder/app/mainwindow.py:560 spyder/widgets/sourcecode/codeeditor.py:2505 +msgid "Undo" +msgstr "Deshacer" + +#: spyder/app/mainwindow.py:562 spyder/widgets/sourcecode/codeeditor.py:2508 +msgid "Redo" +msgstr "Rehacer" + +#: spyder/app/mainwindow.py:564 spyder/widgets/shell.py:121 +#: spyder/widgets/sourcecode/codeeditor.py:2514 +#: spyder/widgets/variableexplorer/arrayeditor.py:453 +#: spyder/widgets/variableexplorer/collectionseditor.py:649 +#: spyder/widgets/variableexplorer/dataframeeditor.py:445 +msgid "Copy" +msgstr "Copiar" + +#: spyder/app/mainwindow.py:566 spyder/widgets/shell.py:117 +#: spyder/widgets/sourcecode/codeeditor.py:2511 +msgid "Cut" +msgstr "Cortar" + +#: spyder/app/mainwindow.py:568 spyder/widgets/shell.py:125 +#: spyder/widgets/sourcecode/codeeditor.py:2517 +#: spyder/widgets/variableexplorer/collectionseditor.py:646 +msgid "Paste" +msgstr "Pegar" + +#: spyder/app/mainwindow.py:571 spyder/widgets/shell.py:138 +#: spyder/widgets/sourcecode/codeeditor.py:2520 +msgid "Select All" +msgstr "Seleccionar todo" + +#: spyder/app/mainwindow.py:581 spyder/plugins/editor.py:1353 +msgid "&File" +msgstr "&Archivo" + +#: spyder/app/mainwindow.py:582 spyder/plugins/editor.py:1345 +msgid "File toolbar" +msgstr "Barra de archivo" + +#: spyder/app/mainwindow.py:586 spyder/plugins/editor.py:1354 +msgid "&Edit" +msgstr "&Editar" + +#: spyder/app/mainwindow.py:587 spyder/plugins/editor.py:1350 +msgid "Edit toolbar" +msgstr "Barra de edición" + +#: spyder/app/mainwindow.py:591 spyder/plugins/editor.py:1355 +msgid "&Search" +msgstr "&Buscar" + +#: spyder/app/mainwindow.py:592 spyder/plugins/editor.py:1346 +msgid "Search toolbar" +msgstr "Barra de búsqueda" + +#: spyder/app/mainwindow.py:596 spyder/plugins/editor.py:1356 +msgid "Sour&ce" +msgstr "&Código fuente" + +#: spyder/app/mainwindow.py:597 spyder/plugins/editor.py:1347 +msgid "Source toolbar" +msgstr "Barra de código fuente" + +#: spyder/app/mainwindow.py:601 spyder/plugins/editor.py:780 +#: spyder/plugins/editor.py:1357 +msgid "&Run" +msgstr "E&jecutar" + +#: spyder/app/mainwindow.py:602 spyder/plugins/editor.py:1348 +msgid "Run toolbar" +msgstr "Barra de ejecución" + +#: spyder/app/mainwindow.py:606 spyder/plugins/editor.py:739 +msgid "&Debug" +msgstr "&Depurar" + +#: spyder/app/mainwindow.py:607 spyder/plugins/editor.py:1349 +msgid "Debug toolbar" +msgstr "Barra de depuración" + +#: spyder/app/mainwindow.py:611 +msgid "C&onsoles" +msgstr "&Terminales" + +#: spyder/app/mainwindow.py:614 +msgid "&Projects" +msgstr "&Proyectos" + +#: spyder/app/mainwindow.py:617 spyder/plugins/editor.py:1358 +msgid "&Tools" +msgstr "&Herramientas" + +#: spyder/app/mainwindow.py:620 +msgid "&View" +msgstr "&Ver" + +#: spyder/app/mainwindow.py:623 +msgid "&Help" +msgstr "A&yuda" + +#: spyder/app/mainwindow.py:628 +msgid "Welcome to Spyder!" +msgstr "Bienvenido a Spyder!" + +#: spyder/app/mainwindow.py:633 +msgid "Pre&ferences" +msgstr "Pre&ferencias" + +#: spyder/app/mainwindow.py:640 spyder/widgets/pathmanager.py:49 +msgid "PYTHONPATH manager" +msgstr "Administrador del PYTHONPATH" + +#: spyder/app/mainwindow.py:643 +msgid "Python Path Manager" +msgstr "Manejador de rutas de Python" + +#: spyder/app/mainwindow.py:646 +msgid "Update module names list" +msgstr "Actualizar la lista de nombres de módulos" + +#: spyder/app/mainwindow.py:649 +msgid "Refresh list of module names available in PYTHONPATH" +msgstr "" +"Actualiza la lista de nombres de los módulos disponibles en su PYTHONPATH" + +#: spyder/app/mainwindow.py:652 +msgid "Reset Spyder to factory defaults" +msgstr "Restaurar Spyder a su configuración por defecto" + +#: spyder/app/mainwindow.py:657 +msgid "Current user environment variables..." +msgstr "Variables de entorno del usuario actual..." + +#: spyder/app/mainwindow.py:659 +msgid "" +"Show and edit current user environment variables in Windows registry (i.e. " +"for all sessions)" +msgstr "" +"Mostrar y editar las variables de\n" +"entorno del usuario actual en el\n" +"registro de Windows (es decir,\n" +"para todas las sesiones)" + +#: spyder/app/mainwindow.py:668 spyder/app/mainwindow.py:1123 +msgid "External Tools" +msgstr "Herramientas externas" + +#: spyder/app/mainwindow.py:672 +msgid "Python(x,y) launcher" +msgstr "Lanzador de Python(x,y)" + +#: spyder/app/mainwindow.py:679 +msgid "WinPython control panel" +msgstr "Panel de control de WinPython" + +#: spyder/app/mainwindow.py:688 +msgid "Qt Designer" +msgstr "Diseñador de interfaces de Qt" + +#: spyder/app/mainwindow.py:693 +msgid "Qt Linguist" +msgstr "Traductor de aplicaciones de Qt" + +#: spyder/app/mainwindow.py:699 +msgid "Qt examples" +msgstr "Ejemplos de Qt" + +#: spyder/app/mainwindow.py:720 +msgid "guidata examples" +msgstr "Ejemplos de guidata" + +#: spyder/app/mainwindow.py:731 +msgid "guiqwt examples" +msgstr "Ejemplos de guiqwt" + +#: spyder/app/mainwindow.py:736 +msgid "Sift" +msgstr "Sift" + +#: spyder/app/mainwindow.py:746 +msgid "ViTables" +msgstr "ViTables" + +#: spyder/app/mainwindow.py:760 +msgid "Fullscreen mode" +msgstr "Modo a pantalla completa" + +#: spyder/app/mainwindow.py:772 +msgid "Main toolbar" +msgstr "Barra principal" + +#: spyder/app/mainwindow.py:781 +msgid "" +"Spyder Internal Console\n" +"\n" +"This console is used to report application\n" +"internal errors and to inspect Spyder\n" +"internals with the following commands:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Please don't use it to run your code\n" +"\n" +msgstr "" +"Terminal interna de Spyder!\n" +"\n" +"Esta terminal se utiliza para reportar errores de la\n" +"aplicación y para inspeccionar las características\n" +"internas de Spyder con los siguientes comandos:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Por favor no ejecuta su código en esta terminal\n" + +#: spyder/app/mainwindow.py:798 +msgid "Loading help..." +msgstr "Cargando la ayuda..." + +#: spyder/app/mainwindow.py:805 +msgid "Loading outline explorer..." +msgstr "Cargando el explorador de código..." + +#: spyder/app/mainwindow.py:813 +msgid "Loading editor..." +msgstr "Cargando el editor..." + +#: spyder/app/mainwindow.py:819 spyder/plugins/console.py:134 +#: spyder/widgets/ipythonconsole/client.py:280 +msgid "&Quit" +msgstr "&Salir" + +#: spyder/app/mainwindow.py:821 spyder/plugins/console.py:136 +msgid "Quit" +msgstr "Salir" + +#: spyder/app/mainwindow.py:825 +msgid "&Restart" +msgstr "&Reiniciar" + +#: spyder/app/mainwindow.py:827 +msgid "Restart" +msgstr "Reiniciar" + +#: spyder/app/mainwindow.py:844 +msgid "Loading file explorer..." +msgstr "Cargando el explorador de archivos..." + +#: spyder/app/mainwindow.py:851 +msgid "Loading history plugin..." +msgstr "Cargando el historial..." + +#: spyder/app/mainwindow.py:862 +msgid "Loading online help..." +msgstr "Cargando la ayuda en línea..." + +#: spyder/app/mainwindow.py:867 +msgid "Loading project explorer..." +msgstr "Cargando el explorador de proyectos..." + +#: spyder/app/mainwindow.py:874 +msgid "Loading external console..." +msgstr "Cargando la terminal externa..." + +#: spyder/app/mainwindow.py:880 +msgid "Loading namespace browser..." +msgstr "Cargando el explorador de variables..." + +#: spyder/app/mainwindow.py:887 +msgid "Loading IPython console..." +msgstr "Cargando la terminal de IPython..." + +#: spyder/app/mainwindow.py:892 +msgid "Setting up main window..." +msgstr "Construyendo la ventana principal..." + +#: spyder/app/mainwindow.py:895 +msgid "Dependencies..." +msgstr "Dependencias..." + +#: spyder/app/mainwindow.py:899 +msgid "Report issue..." +msgstr "Reportar un problema..." + +#: spyder/app/mainwindow.py:903 +msgid "Spyder support..." +msgstr "Obtener soporte para Spyder" + +#: spyder/app/mainwindow.py:906 +msgid "Check for updates..." +msgstr "Buscar actualizaciones..." + +#: spyder/app/mainwindow.py:929 +msgid "Spyder documentation" +msgstr "Documentación de Spyder" + +#: spyder/app/mainwindow.py:934 +msgid "Spyder tutorial" +msgstr "Tutorial de Spyder" + +#: spyder/app/mainwindow.py:941 +msgid "Interactive tours" +msgstr "Tours interactivos" + +#: spyder/app/mainwindow.py:969 +msgid "Python documentation" +msgstr "Documentación de Python" + +#: spyder/app/mainwindow.py:975 spyder/app/mainwindow.py:1019 +msgid "IPython documentation" +msgstr "Documentación de IPython" + +#: spyder/app/mainwindow.py:976 +msgid "Intro to IPython" +msgstr "Ayuda básica" + +#: spyder/app/mainwindow.py:978 +msgid "Quick reference" +msgstr "Referencia rápida" + +#: spyder/app/mainwindow.py:980 +msgid "Console help" +msgstr "Ayuda de la terminal" + +#: spyder/app/mainwindow.py:1017 +msgid "Python(x,y) documentation folder" +msgstr "Carpeta de documentación de Python(x,y)" + +#: spyder/app/mainwindow.py:1021 +msgid "guidata documentation" +msgstr "Documentación de guidata" + +#: spyder/app/mainwindow.py:1024 +msgid "guiqwt documentation" +msgstr "Documentación de guiqwt" + +#: spyder/app/mainwindow.py:1030 +msgid "NumPy documentation" +msgstr "Documentación de NumPy" + +#: spyder/app/mainwindow.py:1032 +msgid "NumPy reference guide" +msgstr "Manual de referencia de NumPy" + +#: spyder/app/mainwindow.py:1034 +msgid "NumPy user guide" +msgstr "Guía del usuario de Numpy" + +#: spyder/app/mainwindow.py:1036 +msgid "SciPy documentation" +msgstr "Documentación de SciPy" + +#: spyder/app/mainwindow.py:1043 +msgid "Installed Python modules" +msgstr "Módulos instalados de Python" + +#: spyder/app/mainwindow.py:1047 +msgid "Online documentation" +msgstr "Documentación en línea" + +#: spyder/app/mainwindow.py:1059 +msgid "Qt documentation" +msgstr "Documentación de Qt" + +#: spyder/app/mainwindow.py:1065 +msgid "About %s..." +msgstr "Acerca de %s..." + +#: spyder/app/mainwindow.py:1089 +msgid "Panes" +msgstr "Paneles" + +#: spyder/app/mainwindow.py:1091 +msgid "Toolbars" +msgstr "Barras de herramientas" + +#: spyder/app/mainwindow.py:1092 +msgid "Window layouts" +msgstr "Disposiciones de componentes" + +#: spyder/app/mainwindow.py:1101 spyder/app/mainwindow.py:1894 +#: spyder/app/mainwindow.py:1895 +msgid "Show toolbars" +msgstr "Mostrar barras de herramientas" + +#: spyder/app/mainwindow.py:1116 +msgid "Attached console window (debugging)" +msgstr "Ventana de terminal anexa (para depuración)" + +#: spyder/app/mainwindow.py:1295 spyder/plugins/projects.py:244 +#: spyder/widgets/explorer.py:639 spyder/widgets/explorer.py:744 +#: spyder/widgets/externalshell/pythonshell.py:533 +#: spyder/widgets/externalshell/systemshell.py:105 +#: spyder/widgets/variableexplorer/arrayeditor.py:573 +#: spyder/widgets/variableexplorer/collectionseditor.py:417 +#: spyder/widgets/variableexplorer/dataframeeditor.py:593 +msgid "Error" +msgstr "Error" + +#: spyder/app/mainwindow.py:1296 +msgid "" +"You have missing dependencies!

%s

Please " +"install them to avoid this message.

Note: Spyder could " +"work without some of these dependencies, however to have a smooth experience " +"when using Spyder we strongly recommend you to install all the listed " +"missing dependencies.

Failing to install these dependencies might " +"result in bugs. Please be sure that any found bugs are not the direct result " +"of missing dependencies, prior to reporting a new issue." +msgstr "" +"Algunas dependencias no están instaladas!

%s

Por favor instálelas para evitar este mensaje.

Nota: Spyder puede funcionar sin algunas de estas " +"dependencias. Sin embargo, para no tener problemas al usar Spyder nosotros " +"firmemente le recomendamos instalar todas las dependencias faltantes." +"

El no instalar estas dependencias puede resultar en errores. Por " +"favor asegúrese de que cualquier error que encuentre no sea el resultado " +"directo de las dependencias faltantes, antes de reportar un nuevo problema." + +#: spyder/app/mainwindow.py:1741 +msgid "Spyder Default Layout" +msgstr "Disposición por defecto" + +#: spyder/app/mainwindow.py:1759 +msgid "Save current layout" +msgstr "Guardar la disposición actual" + +#: spyder/app/mainwindow.py:1763 +msgid "Layout preferences" +msgstr "Preferencias de disposición" + +#: spyder/app/mainwindow.py:1767 +msgid "Reset to spyder default" +msgstr "Restaurar a la disposición por defecto" + +#: spyder/app/mainwindow.py:1787 spyder/app/mainwindow.py:1809 +#: spyder/app/mainwindow.py:1872 spyder/app/mainwindow.py:2656 +#: spyder/plugins/configdialog.py:1254 spyder/plugins/externalconsole.py:446 +#: spyder/plugins/ipythonconsole.py:119 spyder/plugins/ipythonconsole.py:835 +#: spyder/plugins/maininterpreter.py:167 spyder/plugins/maininterpreter.py:195 +#: spyder/utils/environ.py:100 spyder/utils/environ.py:113 +#: spyder/widgets/variableexplorer/arrayeditor.py:496 +#: spyder/widgets/variableexplorer/collectionseditor.py:408 +#: spyder/widgets/variableexplorer/collectionseditor.py:1055 +msgid "Warning" +msgstr "Advertencia" + +#: spyder/app/mainwindow.py:1788 +msgid "" +"Window layout will be reset to default settings: this affects window " +"position, size and dockwidgets.\n" +"Do you want to continue?" +msgstr "" +"La disposición de componentes será restablecida a los ajustes por defecto. " +"Esto afecta a la posición y tamaño de la ventana y los componentes.\n" +"¿Desea continuar?" + +#: spyder/app/mainwindow.py:1810 +msgid "" +"Layout %s will be " +"overwritten. Do you want to " +"continue?" +msgstr "La disposición %s será sobrescrita. Desea continuar?" + +#: spyder/app/mainwindow.py:1873 +msgid "Quick switch layout #%s has not yet been defined." +msgstr "Aún no se ha definido la disposición de componentes #%d" + +#: spyder/app/mainwindow.py:1891 spyder/app/mainwindow.py:1892 +msgid "Hide toolbars" +msgstr "Ocultar las barras de herramientas" + +#: spyder/app/mainwindow.py:2185 spyder/app/mainwindow.py:2186 +msgid "Maximize current pane" +msgstr "Maximizar el panel actual" + +#: spyder/app/mainwindow.py:2189 +msgid "Restore current pane" +msgstr "Restaurar el panel actual" + +#: spyder/app/mainwindow.py:2190 +msgid "Restore pane to its original size" +msgstr "Restaurar el panel a su tamaño original" + +#: spyder/app/mainwindow.py:2274 +msgid "About %s" +msgstr "Acerca de %s" + +#: spyder/app/mainwindow.py:2401 spyder/plugins/editor.py:158 +#: spyder/plugins/runconfig.py:322 spyder/plugins/runconfig.py:444 +#: spyder/plugins/runconfig.py:449 spyder/utils/programs.py:285 +#: spyder/widgets/explorer.py:271 spyder/widgets/externalshell/baseshell.py:127 +msgid "Run" +msgstr "Ejecutar" + +#: spyder/app/mainwindow.py:2402 +msgid "Running an external system terminal is not supported on platform %s." +msgstr "" +"Ejecutar en una terminal externa del sistema no está soportado en la " +"plataforma %s." + +#: spyder/app/mainwindow.py:2657 +msgid "" +"Spyder will restart and reset to default settings:

Do you want to " +"continue?" +msgstr "" +"Spyder se reiniciará y volverá a su configuración por defecto: " +"

¿Desea continuar?" + +#: spyder/app/mainwindow.py:2754 spyder/widgets/helperwidgets.py:250 +msgid "Spyder updates" +msgstr "Actualizaciones de Spyder" + +#: spyder/app/mainwindow.py:2755 spyder/plugins/configdialog.py:819 +msgid "Check for updates on startup" +msgstr "Buscar actualizaciones al inicio" + +#: spyder/app/mainwindow.py:2775 +msgid "" +"Spyder %s is available!

Please use your package manager to " +"update Spyder or go to our Releases page to download this " +"new version.

If you are not sure how to proceed to update Spyder " +"please refer to our Installation instructions." +msgstr "" +"Spyder %s está disponible!

Por favor utilice su instalador de " +"paquetes para actualizar Spyder o vaya a nuestra página de Lanzamientos para descargar esta nueva versión.

Si no está " +"seguro de cómo proceder para actualizar Spyder, por favor lea nuestra " +"instrucciones de Instalación (en inglés)." + +#: spyder/app/mainwindow.py:2787 +msgid "Spyder is up to date." +msgstr "Spyder está actualizado." + +#: spyder/app/restart.py:133 +msgid "" +"It was not possible to close the previous Spyder instance.\n" +"Restart aborted." +msgstr "" +"No fue posible cerrar la instancia anterior de Spyder.\n" +"Reinicialización abortada." + +#: spyder/app/restart.py:135 +msgid "" +"Spyder could not reset to factory defaults.\n" +"Restart aborted." +msgstr "" +"Spyder no pudo volver a la configuración por defecto.\n" +"Reinicialización abortada." + +#: spyder/app/restart.py:137 +msgid "" +"It was not possible to restart Spyder.\n" +"Operation aborted." +msgstr "" +"No fue posible reiniciar Spyder.\n" +"Operación abortada." + +#: spyder/app/restart.py:139 +msgid "Spyder exit error" +msgstr "Error de salida de de Spyder" + +#: spyder/app/restart.py:140 +msgid "Spyder reset error" +msgstr "Error de restauración de Spyder" + +#: spyder/app/restart.py:141 +msgid "Spyder restart error" +msgstr "Error de reinicio de Spyder" + +#: spyder/app/restart.py:165 +msgid "Closing Spyder" +msgstr "Cerrando Spyder" + +#: spyder/app/restart.py:239 +msgid "Resetting Spyder to defaults" +msgstr "Restaurando a los valores por defecto" + +#: spyder/app/restart.py:271 +msgid "Restarting" +msgstr "Reiniciando" + +#: spyder/app/tour.py:124 +msgid "Welcome to the Introduction tour" +msgstr "Bienvenido al Tour introductorio!" + +#: spyder/app/tour.py:125 +msgid "" +"Spyder is a powerful Interactive Development Environment (or IDE) for " +"the Python programming language.

Here we are going to guide you " +"through its most important features.

Please use the arrow keys or " +"click on the buttons below to move along the tour." +msgstr "" +"Spyder es un potente entorno de desarrollo integrado para el lenguaje " +"de programación Python.

A continuación vamos a guiarlo a través de " +"sus características más importantes.

Por favor utilice las teclas de " +"flechas o haga click en los botones que aparecen abajo para moverse a lo " +"largo del tour." + +#: spyder/app/tour.py:134 +msgid "The Editor" +msgstr "El Editor" + +#: spyder/app/tour.py:135 +msgid "" +"This is the pane where you write Python code before evaluating it. You can " +"get automatic suggestions and completions while writing, by pressing the " +"Tab key next to a given text.

The Editor comes with a line " +"number area (highlighted here in red), where Spyder shows warnings and " +"syntax errors. They can help you to detect potential problems before running " +"the code.

You can also set debug breakpoints in the line number area, " +"by doing a double click next to a non-empty line." +msgstr "" +"Este es el panel en el que puede escribir su código de Python antes de " +"evaluarlo. Para obtener sugerencias automáticas de completado mientras " +"escribe, puede presionar la tecla Tab junto a un texto dado." +"

El Editor viene con un área para los números de línea (resaltada " +"aquí en rojo), donde Spyder también muestra advertencias y mensajes de " +"error. Estos pueden ayudarle a detectar posibles problemas antes de correr " +"el código.

También se pueden establecer puntos de breakpoint en esta " +"área al hacer doble click junto a una línea que no esté vacía." + +#: spyder/app/tour.py:150 +msgid "The IPython console" +msgstr "La Terminal de IPython" + +#: spyder/app/tour.py:151 +msgid "" +"This is one of panes where you can run or execute the code you wrote on the " +"Editor. To do it you need to press the F5 key.

This console " +"comes with several useful features that greatly improve your programming " +"workflow (like syntax highlighting and inline plots). If you want to know " +"more about them, please follow this link.

Please " +"click on the button below to run some simple code in this console. This will " +"be useful to show you other important features." +msgstr "" +"Este es uno de los paneles en los que es posible ejecutar el código escrito " +"en el Editor. Para hacerlo es necesario presionar la teclar F5." +"

Esta terminal viene dotada con varias características que mejoran " +"significativamente la experiencia de programar en Python (como el resaltado " +"de sintaxis). Si desea saber más al respecto, por favor siga este enlace.

Por favor oprima el botón que aparece abajo para " +"ejecutar un código muy sencillo en esta terminal. Esto será útil para " +"mostrarle otras características más adelante." + +#: spyder/app/tour.py:167 +msgid "The Variable Explorer" +msgstr "El Explorador de variables" + +#: spyder/app/tour.py:168 +msgid "" +"In this pane you can view and edit the variables generated during the " +"execution of a program, or those entered directly in one of Spyder consoles." +"

As you can see, the Variable Explorer is showing the variables " +"generated during the last step of this tour. By doing a double-click on any " +"of them, a new window will be opened, where you can inspect and modify their " +"contents." +msgstr "" +"En este panel se pueden ver y editar las variables generadas durante la " +"ejecución de un programa, o de los comandos introducidos directamente en " +"alguna de las terminales de Spyder.

Como se puede observar, el " +"Explorador de Variables está mostrando las variables generadas durante el " +"último paso de este tour. Al hacer doble click en alguna de ellas, se " +"mostrará una nueva ventana, en la cual se pueden inspeccionar y modificar " +"sus contenidos." + +#: spyder/app/tour.py:180 +msgid "The Python console" +msgstr "La Terminal de Python" + +#: spyder/app/tour.py:181 +msgid "" +"You can also run your code on a Python console. These consoles are useful " +"because they let you run a file in a console dedicated only to it.To select " +"this behavior, please press the F6 key.

By pressing the button " +"below and then focusing the Variable Explorer, you will notice that Python " +"consoles are also connected to that pane, and that the Variable Explorer " +"only shows the variables of the currently focused console." +msgstr "" +"También es posible ejecutar su código en una terminal de Python. Estas " +"terminales son útiles porque le permiten ejecutar un archivo en una terminal " +"exclusivamente dedicada al mismo. Por favor oprima la tecla Ctrl+F6 " +"para seleccionar este comportamiento.

Al oprimir el botón que aparece " +"abajo y luego enfocar el Explorador de Variables, podrá notar que las " +"terminales de Python están también conectadas a ese panel, y que el " +"Explorador de Variables sólo muestra las variables de la terminal que " +"actualmente está enfocada." + +#: spyder/app/tour.py:195 spyder/plugins/help.py:485 spyder/plugins/help.py:929 +#: spyder/widgets/internalshell.py:270 +msgid "Help" +msgstr "Ayuda" + +#: spyder/app/tour.py:196 +msgid "" +"This pane displays documentation of the functions, classes, methods or " +"modules you are currently using in the Editor or the Consoles.

To use " +"it, you need to press Ctrl+I in front of an object. If that object " +"has some documentation associated with it, it will be displayed here." +msgstr "" +"Este panel muestra la documentación de las funciones, clases, métodos o " +"módulos que se estén usando en el Editor o las Terminales.

Para " +"usarlo, por favor oprima la tecla Ctrl+I frente a un objeto dado. Si " +"ese objeto cuenta con documentación asociada, ésta se mostrará aquí." + +#: spyder/app/tour.py:206 +msgid "The File Explorer" +msgstr "El Explorador de archivos" + +#: spyder/app/tour.py:207 +msgid "" +"This pane lets you navigate through the directories and files present in " +"your computer.

You can also open any of these files with its " +"corresponding application, by doing a double click on it.

There is " +"one exception to this rule: plain-text files will always be opened in the " +"Spyder Editor." +msgstr "" +"Este panel le permite navegar a través de los directorios y archivos " +"presentes en su computador.

También es posible abrir cualquier " +"archivo desde este panel con su aplicación correspondiente, al hacer doble " +"click en el mismo.

Sin embargo, existe una excepción a esta regla: " +"todos los archivos de texto plano siempre se abrirán en el Editor de Spyder." + +#: spyder/app/tour.py:217 +msgid "The History Log" +msgstr "El Historial de comandos" + +#: spyder/app/tour.py:218 +msgid "" +"This pane records all commands introduced in the Python and IPython consoles." +msgstr "" +"Este panel guarda un registro de todos los comandos introducidos en las " +"terminales de IPython y Python." + +#: spyder/app/tour.py:266 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" + +#: spyder/app/tour.py:270 +msgid "Welcome to Spyder introduction tour" +msgstr "Bienvenido al Tour Introductorio de Spyder!" + +#: spyder/app/tour.py:271 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" + +#: spyder/app/tour.py:276 +msgid "Introduction tour" +msgstr "Tour Introductorio" + +#: spyder/app/tour.py:277 +msgid "New features in version 3.0" +msgstr "" + +#: spyder/app/tour.py:555 spyder/plugins/ipythonconsole.py:431 +msgid "Run code" +msgstr "Ejecutar código" + +#: spyder/app/tour.py:824 +msgid "Go to step: " +msgstr "Ir al paso:" + +#: spyder/config/base.py:249 +msgid "" +"Update LANGUAGE_CODES (inside config/base.py) if a new translation has been " +"added to Spyder" +msgstr "" +"Actualice LANGUAGE_CODES (en config/base.py) si una nueva traducción se " +"añadió a Spyder" + +#: spyder/config/ipython.py:23 +msgid "Integrate the IPython console" +msgstr "Integración con la Terminal de IPython" + +#: spyder/config/ipython.py:25 +msgid "Manipulate Jupyter notebooks on the Editor" +msgstr "Manipular notebooks de Jupyter en el Editor" + +#: spyder/config/utils.py:24 +msgid "Python files" +msgstr "Archivos Python" + +#: spyder/config/utils.py:25 +msgid "Cython/Pyrex files" +msgstr "Archivos Cython/Pyrex" + +#: spyder/config/utils.py:26 +msgid "C files" +msgstr "Archivos C" + +#: spyder/config/utils.py:27 +msgid "C++ files" +msgstr "Archivos C++" + +#: spyder/config/utils.py:28 +msgid "OpenCL files" +msgstr "Archivos OpenCL" + +#: spyder/config/utils.py:29 +msgid "Fortran files" +msgstr "Archivos Fortran" + +#: spyder/config/utils.py:30 +msgid "IDL files" +msgstr "Archivos IDL" + +#: spyder/config/utils.py:31 +msgid "MATLAB files" +msgstr "Archivos MATLAB" + +#: spyder/config/utils.py:32 +msgid "Julia files" +msgstr "Archivos Julia" + +#: spyder/config/utils.py:33 +msgid "Yaml files" +msgstr "Archivos Yaml" + +#: spyder/config/utils.py:34 +msgid "Patch and diff files" +msgstr "Archivos Patch y diff" + +#: spyder/config/utils.py:35 +msgid "Batch files" +msgstr "Archivos Batch" + +#: spyder/config/utils.py:36 spyder/utils/iofuncs.py:426 +msgid "Text files" +msgstr "Archivos de Texto" + +#: spyder/config/utils.py:37 +msgid "reStructuredText files" +msgstr "Archivos de Texto reStructurado" + +#: spyder/config/utils.py:38 +msgid "gettext files" +msgstr "Archivos gettext" + +#: spyder/config/utils.py:39 +msgid "NSIS files" +msgstr "Archivos NSIS" + +#: spyder/config/utils.py:40 +msgid "Web page files" +msgstr "Archivos de Páginas web" + +#: spyder/config/utils.py:41 +msgid "XML files" +msgstr "Archivos XML" + +#: spyder/config/utils.py:42 +msgid "Javascript files" +msgstr "Archivos Javascript" + +#: spyder/config/utils.py:43 +msgid "Json files" +msgstr "Archivos Json" + +#: spyder/config/utils.py:44 +msgid "IPython notebooks" +msgstr "Notebooks de IPython" + +#: spyder/config/utils.py:45 +msgid "Enaml files" +msgstr "Archivos Enaml" + +#: spyder/config/utils.py:46 +msgid "Configuration files" +msgstr "Archivos de Configuración" + +#: spyder/config/utils.py:51 spyder/widgets/explorer.py:712 +msgid "All files" +msgstr "Todos los archivos" + +#: spyder/config/utils.py:121 +msgid "Supported text files" +msgstr "Archivos de texto soportados" + +#: spyder/plugins/__init__.py:508 spyder/plugins/editor.py:98 +#: spyder/plugins/editor.py:545 spyder/plugins/editor.py:1729 +#: spyder/plugins/help.py:118 spyder/plugins/help.py:385 +#: spyder/widgets/editor.py:371 spyder/widgets/sourcecode/codeeditor.py:97 +#: spyder/widgets/sourcecode/codeeditor.py:3001 +msgid "Editor" +msgstr "Editor" + +#: spyder/plugins/configdialog.py:139 +msgid "Reset to defaults" +msgstr "Restaurar los valores por defecto" + +#: spyder/plugins/configdialog.py:151 +msgid "Preferences" +msgstr "Preferencias" + +#: spyder/plugins/configdialog.py:491 +msgid "Invalid directory path" +msgstr "Ruta de directorio inválida" + +#: spyder/plugins/configdialog.py:494 spyder/plugins/configdialog.py:509 +#: spyder/plugins/runconfig.py:177 spyder/plugins/runconfig.py:241 +#: spyder/plugins/workingdirectory.py:291 spyder/widgets/explorer.py:626 +#: spyder/widgets/externalshell/pythonshell.py:616 +#: spyder/widgets/findinfiles.py:504 spyder/widgets/pathmanager.py:224 +#: spyder/widgets/projects/projectdialog.py:155 +msgid "Select directory" +msgstr "Seleccionar directorio" + +#: spyder/plugins/configdialog.py:521 +msgid "Invalid file path" +msgstr "Ruta de archivo inválida" + +#: spyder/plugins/configdialog.py:524 spyder/plugins/configdialog.py:541 +msgid "Select file" +msgstr "Seleccionar archivo" + +#: spyder/plugins/configdialog.py:540 +msgid "All files (*)" +msgstr "Todos los archivos (*)" + +#: spyder/plugins/configdialog.py:613 spyder/widgets/formlayout.py:215 +msgid "Bold" +msgstr "Negrita" + +#: spyder/plugins/configdialog.py:616 spyder/widgets/formlayout.py:210 +msgid "Italic" +msgstr "Cursiva" + +#: spyder/plugins/configdialog.py:670 +msgid "Font: " +msgstr "Tipo de letra" + +#: spyder/plugins/configdialog.py:676 +msgid "Size: " +msgstr "Tamaño:" + +#: spyder/plugins/configdialog.py:695 +msgid "Font style" +msgstr "Fuente" + +#: spyder/plugins/configdialog.py:772 +msgid "Spyder needs to restart to change the following setting:" +msgstr "Spyder necesita reiniciar para cambiar la siguiente configuración:" + +#: spyder/plugins/configdialog.py:775 +msgid "Spyder needs to restart to change the following settings:" +msgstr "Spyder necesita reiniciar para cambiar las siguientes configuraciones:" + +#: spyder/plugins/configdialog.py:777 +msgid "Do you wish to restart now?" +msgstr "¿Desea reiniciar ahora?" + +#: spyder/plugins/configdialog.py:783 +msgid "Information" +msgstr "Información" + +#: spyder/plugins/configdialog.py:797 spyder/plugins/configdialog.py:804 +#: spyder/widgets/projects/configdialog.py:74 +msgid "General" +msgstr "General" + +#: spyder/plugins/configdialog.py:807 +msgid "Language" +msgstr "Lenguaje" + +#: spyder/plugins/configdialog.py:810 +msgid "Use a single instance" +msgstr "Utilizar una única instancia" + +#: spyder/plugins/configdialog.py:812 +msgid "" +"Set this to open external
Python files in an already running instance " +"(Requires a restart)" +msgstr "" +"Seleccione esta opción
para abrir archivos externos de Python en la " +"ventana actual (Requiere reiniciar)" + +#: spyder/plugins/configdialog.py:815 +msgid "Prompt when exiting" +msgstr "Preguntar antes de salir" + +#: spyder/plugins/configdialog.py:816 +msgid "Pop up internal console when internal errors appear" +msgstr "Mostrar la terminal interna cuando se produzcan errores" + +#: spyder/plugins/configdialog.py:836 spyder/plugins/editor.py:107 +#: spyder/plugins/externalconsole.py:57 spyder/plugins/ipythonconsole.py:273 +#: spyder/widgets/projects/configdialog.py:81 +msgid "Interface" +msgstr "Interfaz" + +#: spyder/plugins/configdialog.py:844 +msgid "Qt windows style" +msgstr "Estilo de Qt" + +#: spyder/plugins/configdialog.py:850 +msgid "Icon theme" +msgstr "Tema de iconos" + +#: spyder/plugins/configdialog.py:854 +msgid "Vertical title bars in panes" +msgstr "Barras de título verticales para los paneles" + +#: spyder/plugins/configdialog.py:856 +msgid "Vertical tabs in panes" +msgstr "Pestañas verticales en los paneles" + +#: spyder/plugins/configdialog.py:858 +msgid "Animated toolbars and panes" +msgstr "Barras de herramientas y paneles animados" + +#: spyder/plugins/configdialog.py:860 +msgid "Tear off menus" +msgstr "Separar los menús" + +#: spyder/plugins/configdialog.py:861 +msgid "Set this to detach any
menu from the main window" +msgstr "" +"Establezca esta opción
si desea separar los menús de la ventana principal" + +#: spyder/plugins/configdialog.py:863 +msgid "Enable high DPI scaling" +msgstr "Activar el escalado para pantallas de alta resolución" + +#: spyder/plugins/configdialog.py:865 +msgid "Set this for high DPI displays" +msgstr "" +"Activar esta opción para escalar la interfaz \n" +"a pantallas de alta resolución" + +#: spyder/plugins/configdialog.py:866 +msgid "Custom margin for panes:" +msgstr "Márgenes personalizadas para los paneles:" + +#: spyder/plugins/configdialog.py:868 spyder/plugins/editor.py:209 +msgid "pixels" +msgstr "pixels" + +#: spyder/plugins/configdialog.py:897 +msgid "Status bar" +msgstr "Barra de estado" + +#: spyder/plugins/configdialog.py:898 +msgid "Show status bar" +msgstr "Mostrar la barra de estatus" + +#: spyder/plugins/configdialog.py:900 +msgid "Show memory usage every" +msgstr "Mostrar la memoria usada cada" + +#: spyder/plugins/configdialog.py:902 spyder/plugins/configdialog.py:911 +#: spyder/plugins/editor.py:133 spyder/plugins/editor.py:261 +#: spyder/plugins/variableexplorer.py:30 +msgid " ms" +msgstr "ms" + +#: spyder/plugins/configdialog.py:909 +msgid "Show CPU usage every" +msgstr "Mostrar el uso de CPU cada" + +#: spyder/plugins/configdialog.py:944 +msgid "Plain text font" +msgstr "Texto plano" + +#: spyder/plugins/configdialog.py:950 +msgid "Rich text font" +msgstr "Texto enriquecido" + +#: spyder/plugins/configdialog.py:953 +msgid "Fonts" +msgstr "Tipo de letra" + +#: spyder/plugins/configdialog.py:967 +msgid "Appearance" +msgstr "Apariencia" + +#: spyder/plugins/configdialog.py:969 spyder/plugins/ipythonconsole.py:564 +msgid "Advanced Settings" +msgstr "Opciones avanzadas" + +#: spyder/plugins/configdialog.py:1005 +msgid "Syntax coloring" +msgstr "Coloreado de sintaxis" + +#: spyder/plugins/configdialog.py:1018 +msgid "" +"Here you can select the color scheme used in the Editor and all other Spyder " +"plugins.

You can also edit the color schemes provided by Spyder or " +"create your own ones by using the options provided below.
" +msgstr "" +"Aquí puede seleccionar el esquema de colores usado en el Editor y todos los " +"demás paneles de Spyder.

También puede editar los esquemas de colores " +"que vienen con Spyder o crear los suyos al usar las opciones que aparecen a " +"continuación.
" + +#: spyder/plugins/configdialog.py:1023 +msgid "Edit selected" +msgstr "Editar esquema" + +#: spyder/plugins/configdialog.py:1024 +msgid "Create new scheme" +msgstr "Crear nuevo esquema" + +#: spyder/plugins/configdialog.py:1025 spyder/widgets/explorer.py:512 +#: spyder/widgets/projects/explorer.py:243 spyder/widgets/shell.py:134 +msgid "Delete" +msgstr "Eliminar" + +#: spyder/plugins/configdialog.py:1028 +msgid "Reset" +msgstr "Restaurar" + +#: spyder/plugins/configdialog.py:1035 +msgid "Scheme:" +msgstr "Esquema:" + +#: spyder/plugins/configdialog.py:1066 +msgid "Manage color schemes" +msgstr "Manejar esquemas de coloreado" + +#: spyder/plugins/configdialog.py:1255 +msgid "Are you sure you want to delete this scheme?" +msgstr "¿Está seguro de que borrar este esquema?" + +#: spyder/plugins/configdialog.py:1372 +msgid "Text" +msgstr "Texto" + +#: spyder/plugins/configdialog.py:1374 +msgid "Highlight" +msgstr "Resaltar" + +#: spyder/plugins/configdialog.py:1376 +msgid "Background" +msgstr "Fondo" + +#: spyder/plugins/configdialog.py:1380 +msgid "Scheme name:" +msgstr "Nombre del esquema:" + +#: spyder/plugins/configdialog.py:1387 +msgid "Color scheme editor" +msgstr "Editor de esquemas" + +#: spyder/plugins/console.py:109 +msgid "Internal console" +msgstr "Terminal interna" + +#: spyder/plugins/console.py:139 spyder/plugins/externalconsole.py:743 +msgid "&Run..." +msgstr "E&jecutar..." + +#: spyder/plugins/console.py:141 spyder/plugins/externalconsole.py:744 +msgid "Run a Python script" +msgstr "Ejecutar un archivo de Python" + +#: spyder/plugins/console.py:144 +msgid "Environment variables..." +msgstr "Variables de entorno..." + +#: spyder/plugins/console.py:146 +msgid "Show and edit environment variables (for current session)" +msgstr "Muestra y edita las variables de entorno (para la sesión actual)" + +#: spyder/plugins/console.py:150 +msgid "Show sys.path contents..." +msgstr "Contenidos del sys.path" + +#: spyder/plugins/console.py:152 +msgid "Show (read-only) sys.path" +msgstr "Muestra los contenidos del sys.path en modo lectura" + +#: spyder/plugins/console.py:155 +msgid "Buffer..." +msgstr "Mostrar líneas..." + +#: spyder/plugins/console.py:156 spyder/plugins/externalconsole.py:75 +#: spyder/plugins/history.py:43 +msgid "Set maximum line count" +msgstr "Establece el máximo número de líneas a mostrar en la terminal" + +#: spyder/plugins/console.py:159 +msgid "External editor path..." +msgstr "Ruta del editor externo..." + +#: spyder/plugins/console.py:160 +msgid "Set external editor executable path" +msgstr "Establece la ruta del editor externo" + +#: spyder/plugins/console.py:163 spyder/plugins/editor.py:139 +#: spyder/plugins/externalconsole.py:76 spyder/plugins/help.py:157 +#: spyder/plugins/help.py:360 spyder/plugins/history.py:46 +#: spyder/plugins/history.py:157 +msgid "Wrap lines" +msgstr "Ajuste de línea automático" + +#: spyder/plugins/console.py:166 spyder/plugins/editor.py:175 +#: spyder/plugins/externalconsole.py:121 spyder/plugins/ipythonconsole.py:283 +msgid "Display balloon tips" +msgstr "Mostrar globos de sugerencias" + +#: spyder/plugins/console.py:170 spyder/plugins/editor.py:169 +#: spyder/plugins/externalconsole.py:115 +msgid "Automatic code completion" +msgstr "Completar código automáticamente" + +#: spyder/plugins/console.py:174 spyder/plugins/editor.py:173 +#: spyder/plugins/externalconsole.py:119 +msgid "Enter key selects completion" +msgstr "La tecla Enter selecciona el resultado a completar" + +#: spyder/plugins/console.py:179 +msgid "Internal console settings" +msgstr "Opciones" + +#: spyder/plugins/console.py:232 spyder/plugins/externalconsole.py:918 +msgid "Run Python script" +msgstr "Ejecutar archivo de Python" + +#: spyder/plugins/console.py:233 spyder/plugins/externalconsole.py:146 +#: spyder/plugins/externalconsole.py:919 spyder/widgets/explorer.py:727 +msgid "Python scripts" +msgstr "Archivos de Python" + +#: spyder/plugins/console.py:278 +msgid "Buffer" +msgstr "Mostrar" + +#: spyder/plugins/console.py:279 +msgid "Maximum line count" +msgstr "Máximo número de líneas a mostrar" + +#: spyder/plugins/console.py:289 +msgid "External editor" +msgstr "Editor externo" + +#: spyder/plugins/console.py:290 +msgid "External editor executable path:" +msgstr "Ruta ejecutable del editor externo:" + +#: spyder/plugins/editor.py:104 +msgid "Edit template for new modules" +msgstr "Editar la plantilla para nuevos módulos" + +#: spyder/plugins/editor.py:109 +msgid "Sort files according to full path" +msgstr "Ordenar archivos según su ruta completa" + +#: spyder/plugins/editor.py:111 +msgid "Show tab bar" +msgstr "Mostrar barra de pestañas" + +#: spyder/plugins/editor.py:118 spyder/plugins/editor.py:189 +#: spyder/plugins/externalconsole.py:71 spyder/plugins/externalconsole.py:114 +#: spyder/plugins/help.py:156 spyder/plugins/history.py:45 +#: spyder/plugins/ipythonconsole.py:317 +msgid "Source code" +msgstr "Código fuente" + +#: spyder/plugins/editor.py:119 +msgid "Show line numbers" +msgstr "Mostrar números de líneas" + +#: spyder/plugins/editor.py:120 spyder/plugins/editor.py:947 +msgid "Show blank spaces" +msgstr "Mostrar espacios en blanco" + +#: spyder/plugins/editor.py:121 +msgid "Show vertical line after" +msgstr "Mostrar una línea vertical después de" + +#: spyder/plugins/editor.py:122 +msgid "characters" +msgstr "caracteres" + +#: spyder/plugins/editor.py:127 +msgid "Highlight current line" +msgstr "Resaltar la línea actual" + +#: spyder/plugins/editor.py:129 +msgid "Highlight current cell" +msgstr "Resaltar la celda actual" + +#: spyder/plugins/editor.py:131 +msgid "Highlight occurrences after" +msgstr "Resaltar ocurrencias después de" + +#: spyder/plugins/editor.py:159 +msgid "Save all files before running script" +msgstr "Guardar todo antes de ejecutar un archivo" + +#: spyder/plugins/editor.py:162 +msgid "Run selection" +msgstr "Ejecutar selección" + +#: spyder/plugins/editor.py:163 +msgid "Maintain focus in the Editor after running cells or selections" +msgstr "Mantener el foco en el Editor después de ejecutar celdas o selecciones" + +#: spyder/plugins/editor.py:166 spyder/plugins/externalconsole.py:252 +msgid "Introspection" +msgstr "Introspección" + +#: spyder/plugins/editor.py:171 spyder/plugins/externalconsole.py:117 +msgid "Case sensitive code completion" +msgstr "Diferenciar entre mayúsculas y minúsculas al completar código" + +#: spyder/plugins/editor.py:176 +msgid "Link to object definition" +msgstr "Enlazar a la definición de un objeto" + +#: spyder/plugins/editor.py:178 +msgid "" +"If this option is enabled, clicking on an object\n" +"name (left-click + Ctrl key) will go this object\n" +"definition (if resolved)." +msgstr "" +"Si está opción está activada, al hacer click\n" +"sobre el nombre de un objeto (click-izquierdo +\n" +"la tecla Ctrl), el Editor se ubicará en la definición\n" +"del mismo (de poder resolverse el nombre)." + +#: spyder/plugins/editor.py:182 +msgid "" +"Warning:
The Python module rope is not installed on this " +"computer: calltips, code completion and go-to-definition features won't be " +"available." +msgstr "" +"Advertencia:
El módulo de Pythonrope no está instalado en " +"este computador. Por tanto el completado de código, el ir a la definición de " +"una función o método y los globos de sugerencias se encontrarán desactivados." + +#: spyder/plugins/editor.py:190 +msgid "Automatic insertion of parentheses, braces and brackets" +msgstr "Inserción automática de paréntesis, llaves y corchetes" + +#: spyder/plugins/editor.py:193 +msgid "Automatic insertion of closing quotes" +msgstr "Inserción automática de comillas" + +#: spyder/plugins/editor.py:195 +msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" +msgstr "Inserción automática de ':' después de 'for', 'if', 'def', etc" + +#: spyder/plugins/editor.py:198 +msgid "Automatic indentation after 'else', 'elif', etc." +msgstr "Indentación automática después de 'else', 'elif', etc." + +#: spyder/plugins/editor.py:200 +msgid "Indentation characters: " +msgstr "Caracteres de indentación:" + +#: spyder/plugins/editor.py:201 +msgid "2 spaces" +msgstr "2 espacios" + +#: spyder/plugins/editor.py:202 +msgid "3 spaces" +msgstr "3 espacios" + +#: spyder/plugins/editor.py:203 +msgid "4 spaces" +msgstr "4 espacios" + +#: spyder/plugins/editor.py:204 +msgid "5 spaces" +msgstr "5 espacios" + +#: spyder/plugins/editor.py:205 +msgid "6 spaces" +msgstr "6 espacios" + +#: spyder/plugins/editor.py:206 +msgid "7 spaces" +msgstr "7 espacios" + +#: spyder/plugins/editor.py:207 +msgid "8 spaces" +msgstr "8 espacios" + +#: spyder/plugins/editor.py:208 +msgid "Tabulations" +msgstr "Tabulaciones" + +#: spyder/plugins/editor.py:209 +msgid "Tab stop width:" +msgstr "Ancho de las tabulaciones:" + +#: spyder/plugins/editor.py:211 +msgid "Tab always indent" +msgstr "Siempre indentar con la tecla Tab" + +#: spyder/plugins/editor.py:213 +msgid "" +"If enabled, pressing Tab will always indent,\n" +"even when the cursor is not at the beginning\n" +"of a line (when this option is enabled, code\n" +"completion may be triggered using the alternate\n" +"shortcut: Ctrl+Space)" +msgstr "" +"Si esta opción está activada, el oprimir Tab\n" +"siempre indentará el código, aún cuando el\n" +"cursor no esté al principio de una línea\n" +"(de seleccionar esta opción, se puede usar\n" +"la combinación de teclas Ctrl+Espacio para\n" +"activar el completado de código)" + +#: spyder/plugins/editor.py:218 +msgid "Intelligent backspace" +msgstr "Tecla de retroceso (\"backspace\") inteligente" + +#: spyder/plugins/editor.py:220 +msgid "Automatically remove trailing spaces when saving files" +msgstr "Eliminar automáticamente espacios en blanco al guardar un archivo" + +#: spyder/plugins/editor.py:224 +msgid "Analysis" +msgstr "Análisis" + +#: spyder/plugins/editor.py:226 +msgid "(Refer to the {} page)" +msgstr "(Mirar la página {})" + +#: spyder/plugins/editor.py:230 +msgid "Real-time code analysis" +msgstr "Análisis del código en tiempo real en el Editor" + +#: spyder/plugins/editor.py:232 +msgid "" +"

If enabled, Python source code will be analyzed using pyflakes, lines " +"containing errors or warnings will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore code analysis warnings.

" +msgstr "" +"

Si esta opción está activada, los archivos de Python serán analizados " +"automáticamente y y las líneas que contengan errores o advertencias serán " +"resaltadas.

Nota: Puede añadir analysis:ignore en un " +"comentario para ignorar estas advertencias.

" + +#: spyder/plugins/editor.py:240 +msgid "Code analysis requires pyflakes %s+" +msgstr "El análisis del código requiere pyflakes %s+" + +#: spyder/plugins/editor.py:242 +msgid "Real-time code style analysis" +msgstr "Análisis de estilo del código en el Editor" + +#: spyder/plugins/editor.py:244 +msgid "" +"

If enabled, Python source code will be analyzedusing pep8, lines that are " +"not following PEP8 style guide will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore style analysis warnings.

" +msgstr "" +"

Si esta opción está activada, los archivos de Python serán analizados " +"con PEP8 y las líneas que no sigan esta guía de estilo serán resaltadas.

Nota: Añada analysis:ignore en un comentario para ignorar " +"estas advertencias.

" + +#: spyder/plugins/editor.py:251 +msgid "Code annotations (TODO, FIXME, XXX, HINT, TIP, @todo)" +msgstr "Anotaciones (TODO, FIXME, XXX, HINT, TIP, @todo)" + +#: spyder/plugins/editor.py:254 +msgid "Perform analysis when saving file and every" +msgstr "Realizar los análisis al guardar el archivo y cada" + +#: spyder/plugins/editor.py:258 +msgid "Perform analysis only when saving file" +msgstr "Realizar análisis sólo cuando se guarde el archivo" + +#: spyder/plugins/editor.py:317 +msgid "End-of-line characters" +msgstr "Caracteres de fin de línea" + +#: spyder/plugins/editor.py:318 +msgid "" +"When opening a text file containing mixed end-of-line characters (this may " +"raise syntax errors in the consoles on Windows platforms), Spyder may fix " +"the file automatically." +msgstr "" +"Cuando se abra un archivo de texto que contenga varios tipos de caracteres " +"de fin de línea (lo cual puede dar lugar a errores en Windows), Spyder puede " +"arreglar el archivo automáticamente." + +#: spyder/plugins/editor.py:324 +msgid "Fix automatically and show warning message box" +msgstr "Arreglar automáticamente y mostrar un mensaje de advertencia" + +#: spyder/plugins/editor.py:335 spyder/plugins/externalconsole.py:250 +#: spyder/plugins/ipythonconsole.py:558 spyder/plugins/variableexplorer.py:43 +msgid "Display" +msgstr "Visualización" + +#: spyder/plugins/editor.py:337 +msgid "Code Introspection/Analysis" +msgstr "Análisis e introspección de código" + +#: spyder/plugins/editor.py:340 spyder/plugins/externalconsole.py:253 +msgid "Advanced settings" +msgstr "Opciones avanzadas" + +#: spyder/plugins/editor.py:613 spyder/widgets/editortools.py:510 +msgid "Show/hide outline explorer" +msgstr "" +"Mostrar u ocultar el\n" +"explorador de código" + +#: spyder/plugins/editor.py:619 +msgid "Show/hide project explorer" +msgstr "Mostrar/cerrar el explorador de proyectos" + +#: spyder/plugins/editor.py:627 +msgid "&New file..." +msgstr "&Nuevo" + +#: spyder/plugins/editor.py:628 spyder/plugins/workingdirectory.py:83 +#: spyder/widgets/explorer.py:704 spyder/widgets/explorer.py:711 +msgid "New file" +msgstr "Nuevo archivo" + +#: spyder/plugins/editor.py:634 +msgid "&Open..." +msgstr "&Abrir" + +#: spyder/plugins/editor.py:635 spyder/plugins/editor.py:1778 +#: spyder/plugins/workingdirectory.py:70 +msgid "Open file" +msgstr "Abrir archivo" + +#: spyder/plugins/editor.py:641 spyder/widgets/editor.py:347 +msgid "File switcher..." +msgstr "Cambiador de archivos..." + +#: spyder/plugins/editor.py:643 +msgid "Fast switch between files" +msgstr "Cambiar rápidamente entre archivos" + +#: spyder/plugins/editor.py:649 +msgid "&Revert" +msgstr "&Restaurar" + +#: spyder/plugins/editor.py:650 +msgid "Revert file from disk" +msgstr "Restaurar archivo desde el disco" + +#: spyder/plugins/editor.py:653 +msgid "&Save" +msgstr "&Guardar" + +#: spyder/plugins/editor.py:654 spyder/widgets/editor.py:1306 +msgid "Save file" +msgstr "Guardar archivo" + +#: spyder/plugins/editor.py:660 +msgid "Sav&e all" +msgstr "Guardar t&odo" + +#: spyder/plugins/editor.py:661 +msgid "Save all files" +msgstr "Guardar todos los archivos" + +#: spyder/plugins/editor.py:667 +msgid "Save &as..." +msgstr "Gu&ardar como..." + +#: spyder/plugins/editor.py:668 +msgid "Save current file as..." +msgstr "Guardar el archivo actual como..." + +#: spyder/plugins/editor.py:673 spyder/plugins/editor.py:674 +msgid "Print preview..." +msgstr "Presentación preliminar..." + +#: spyder/plugins/editor.py:675 +msgid "&Print..." +msgstr "Im&primir" + +#: spyder/plugins/editor.py:676 +msgid "Print current file..." +msgstr "Imprimir el archivo actual..." + +#: spyder/plugins/editor.py:679 +msgid "&Close" +msgstr "&Cerrar" + +#: spyder/plugins/editor.py:680 +msgid "Close current file" +msgstr "Cerrar el archivo actual" + +#: spyder/plugins/editor.py:683 +msgid "C&lose all" +msgstr "C&errar todo" + +#: spyder/plugins/editor.py:684 +msgid "Close all opened files" +msgstr "Cerrar todos los archivos abiertos" + +#: spyder/plugins/editor.py:691 +msgid "&Find text" +msgstr "&Buscar texto" + +#: spyder/plugins/editor.py:697 +msgid "Find &next" +msgstr "Buscar &siguiente" + +#: spyder/plugins/editor.py:703 +msgid "Find &previous" +msgstr "Buscar &anterior" + +#: spyder/plugins/editor.py:709 +msgid "&Replace text" +msgstr "&Reemplazar texto" + +#: spyder/plugins/editor.py:718 +msgid "Set/Clear breakpoint" +msgstr "Añadir o eliminar un punto de interrupción" + +#: spyder/plugins/editor.py:725 +msgid "Set/Edit conditional breakpoint" +msgstr "Añadir o editar un punto de interrupción condicional" + +#: spyder/plugins/editor.py:732 +msgid "Clear breakpoints in all files" +msgstr "Eliminar los puntos de interrupción de todos los archivos" + +#: spyder/plugins/editor.py:734 +msgid "Debug with winpdb" +msgstr "Depurar con winpdb" + +#: spyder/plugins/editor.py:741 +msgid "Debug file" +msgstr "Depurar archivo" + +#: spyder/plugins/editor.py:746 +msgid "Step" +msgstr "Ejecutar línea" + +#: spyder/plugins/editor.py:747 +msgid "Run current line" +msgstr "Ejecutar la línea seleccionada" + +#: spyder/plugins/editor.py:752 +msgid "Continue" +msgstr "Continuar" + +#: spyder/plugins/editor.py:754 +msgid "Continue execution until next breakpoint" +msgstr "Continuar con la ejecución hasta el siguiente punto de interrupción" + +#: spyder/plugins/editor.py:759 +msgid "Step Into" +msgstr "Ingresar en la función/método" + +#: spyder/plugins/editor.py:761 +msgid "Step into function or method of current line" +msgstr "Ingresar en la función o método de la línea actual" + +#: spyder/plugins/editor.py:766 +msgid "Step Return" +msgstr "Salir de la función/método" + +#: spyder/plugins/editor.py:768 +msgid "Run until current function or method returns" +msgstr "Ejecutar hasta que la función o método actual termine" + +#: spyder/plugins/editor.py:773 spyder/widgets/findinfiles.py:333 +#: spyder/widgets/ipythonconsole/client.py:238 +msgid "Stop" +msgstr "Detener" + +#: spyder/plugins/editor.py:774 +msgid "Stop debugging" +msgstr "Detener la depuración" + +#: spyder/plugins/editor.py:781 +msgid "Run file" +msgstr "Ejecutar archivo" + +#: spyder/plugins/editor.py:786 +msgid "&Configure..." +msgstr "&Configurar..." + +#: spyder/plugins/editor.py:788 spyder/widgets/externalshell/pythonshell.py:304 +msgid "Run settings" +msgstr "Ajustes de ejecución" + +#: spyder/plugins/editor.py:794 +msgid "Re-run &last script" +msgstr "Ejecutar de &nuevo el último archivo" + +#: spyder/plugins/editor.py:796 +msgid "Run again last file" +msgstr "Ejecutar de nuevo el mismo archivo" + +#: spyder/plugins/editor.py:802 spyder/widgets/sourcecode/codeeditor.py:2548 +msgid "Run &selection or current line" +msgstr "Ejecutar la &selección o la línea actual" + +#: spyder/plugins/editor.py:805 +msgid "Run selection or current line" +msgstr "Ejecutar la &selección o línea actual" + +#: spyder/plugins/editor.py:813 spyder/widgets/sourcecode/codeeditor.py:2540 +msgid "Run cell" +msgstr "Ejecutar la celda" + +#: spyder/plugins/editor.py:816 +msgid "" +"Run current cell (Ctrl+Enter)\n" +"[Use #%% to create cells]" +msgstr "" +"Ejecutar la celda actual (Ctrl+Enter)\n" +"[Usar #%% para crear celdas]" + +#: spyder/plugins/editor.py:822 spyder/widgets/sourcecode/codeeditor.py:2544 +msgid "Run cell and advance" +msgstr "Ejecutar la celda y avanzar" + +#: spyder/plugins/editor.py:825 +msgid "Run current cell and go to the next one (Shift+Enter)" +msgstr "Ejecutar la celda actual y avanzar a la siguiente (Shift+Enter)" + +#: spyder/plugins/editor.py:832 +msgid "Show todo list" +msgstr "Mostrar lista de TODO's" + +#: spyder/plugins/editor.py:833 +msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" +msgstr "" +"Mostrar la lista de comentarios de\n" +"los TODO/FIXME/XXX/HINT/TIP/@todo" + +#: spyder/plugins/editor.py:840 +msgid "Show warning/error list" +msgstr "" +"Mostrar la lista de errores\n" +"y advertencias" + +#: spyder/plugins/editor.py:841 +msgid "Show code analysis warnings/errors" +msgstr "" +"Mostrar errores o advertencias\n" +"del análisis del código" + +#: spyder/plugins/editor.py:847 +msgid "Previous warning/error" +msgstr "Anterior advertencia o error" + +#: spyder/plugins/editor.py:848 +msgid "Go to previous code analysis warning/error" +msgstr "" +"Ir a la línea anterior de\n" +"advertencia o error" + +#: spyder/plugins/editor.py:851 +msgid "Next warning/error" +msgstr "Siguiente advertencia o error" + +#: spyder/plugins/editor.py:852 +msgid "Go to next code analysis warning/error" +msgstr "" +"Ir a la próxima línea de\n" +"advertencia o error" + +#: spyder/plugins/editor.py:856 +msgid "Last edit location" +msgstr "Última posición de edición" + +#: spyder/plugins/editor.py:857 +msgid "Go to last edit location" +msgstr "" +"Ir a la anterior posición\n" +"de edición" + +#: spyder/plugins/editor.py:865 +msgid "Previous cursor position" +msgstr "Anterior posición del cursor" + +#: spyder/plugins/editor.py:866 +msgid "Go to previous cursor position" +msgstr "Ir a la anterior posición del cursor" + +#: spyder/plugins/editor.py:874 +msgid "Next cursor position" +msgstr "Siguiente posición del cursor" + +#: spyder/plugins/editor.py:875 +msgid "Go to next cursor position" +msgstr "Ir a la siguiente posición del cursor" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Comment" +msgstr "Comentar" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Uncomment" +msgstr "Descomentar" + +#: spyder/plugins/editor.py:886 +msgid "Comment current line or selection" +msgstr "Comentar la línea o selección actual" + +#: spyder/plugins/editor.py:890 +msgid "Add &block comment" +msgstr "Añadir comentario de &bloque" + +#: spyder/plugins/editor.py:891 +msgid "Add block comment around current line or selection" +msgstr "" +"Añadir un comentario de bloque alrededor de la línea o selección actual" + +#: spyder/plugins/editor.py:897 +msgid "R&emove block comment" +msgstr "&Eliminar comentario de bloque" + +#: spyder/plugins/editor.py:898 +msgid "Remove comment block around current line or selection" +msgstr "Eliminar comentario de bloque alrededor de la línea o selección actual" + +#: spyder/plugins/editor.py:909 +msgid "Indent" +msgstr "Indentar" + +#: spyder/plugins/editor.py:910 +msgid "Indent current line or selection" +msgstr "Indentar la línea o selección actual" + +#: spyder/plugins/editor.py:913 +msgid "Unindent" +msgstr "Quitar indentación" + +#: spyder/plugins/editor.py:914 +msgid "Unindent current line or selection" +msgstr "Quitar indentación de la línea o selección actual" + +#: spyder/plugins/editor.py:918 +msgid "Toggle Uppercase" +msgstr "Cambiar a mayúsculas" + +#: spyder/plugins/editor.py:919 +msgid "Change to uppercase current line or selection" +msgstr "Cambiar a mayúsculas la línea o selección actual" + +#: spyder/plugins/editor.py:923 +msgid "Toggle Lowercase" +msgstr "Cambiar a minúsculas" + +#: spyder/plugins/editor.py:924 +msgid "Change to lowercase current line or selection" +msgstr "Cambiar a minúsculas la línea o selección actual" + +#: spyder/plugins/editor.py:929 +msgid "Carriage return and line feed (Windows)" +msgstr "Retorno de carro y salto de línea (Windows)" + +#: spyder/plugins/editor.py:932 +msgid "Line feed (UNIX)" +msgstr "Salto de línea (UNIX)" + +#: spyder/plugins/editor.py:935 +msgid "Carriage return (Mac)" +msgstr "Retorno de carro (Mac)" + +#: spyder/plugins/editor.py:941 +msgid "Convert end-of-line characters" +msgstr "Convertir caracteres de fin de línea" + +#: spyder/plugins/editor.py:945 +msgid "Remove trailing spaces" +msgstr "Eliminar espacios en blanco" + +#: spyder/plugins/editor.py:949 +msgid "Fix indentation" +msgstr "Corregir la indentación" + +#: spyder/plugins/editor.py:950 +msgid "Replace tab characters by space characters" +msgstr "Reemplazar caracteres de tabulación por espacios" + +#: spyder/plugins/editor.py:953 +msgid "Go to line..." +msgstr "Ir a la línea..." + +#: spyder/plugins/editor.py:961 +msgid "Set console working directory" +msgstr "Establecer directorio de trabajo" + +#: spyder/plugins/editor.py:963 +msgid "" +"Set current console (and file explorer) working directory to current script " +"directory" +msgstr "" +"Fija el directorio de trabajo para la terminal actual como el directorio del " +"archivo actual" + +#: spyder/plugins/editor.py:968 +msgid "Maximum number of recent files..." +msgstr "Máximo número de archivos recientes..." + +#: spyder/plugins/editor.py:971 +msgid "Clear recent files list" +msgstr "Limpiar la lista de archivos recientes" + +#: spyder/plugins/editor.py:971 spyder/plugins/projects.py:100 +msgid "Clear this list" +msgstr "Limpiar esta lista" + +#: spyder/plugins/editor.py:975 +msgid "Open &recent" +msgstr "Abrir &reciente" + +#: spyder/plugins/editor.py:1359 +msgid "?" +msgstr "?" + +#: spyder/plugins/editor.py:1586 +msgid "Spyder Editor" +msgstr "Editor de Spyder" + +#: spyder/plugins/editor.py:1587 +msgid "This is a temporary script file." +msgstr "Este es un archivo temporal" + +#: spyder/plugins/editor.py:1656 +msgid "untitled" +msgstr "Sin título " + +#: spyder/plugins/editor.py:1730 +msgid "Maximum number of recent files" +msgstr "Máximo número de archivos recientes" + +#: spyder/plugins/editor.py:1863 +msgid "Printing..." +msgstr "Imprimir..." + +#: spyder/plugins/explorer.py:53 +msgid "File explorer" +msgstr "Explorador de archivos" + +#: spyder/plugins/externalconsole.py:46 +msgid "Interactive data plotting in the consoles" +msgstr "Graficar datos interactivamente en la terminal" + +#: spyder/plugins/externalconsole.py:54 spyder/plugins/externalconsole.py:709 +msgid "Python console" +msgstr "Terminal de Python" + +#: spyder/plugins/externalconsole.py:59 +msgid "One tab per script" +msgstr "Una pestaña por archivo" + +#: spyder/plugins/externalconsole.py:60 +#: spyder/widgets/externalshell/baseshell.py:160 +msgid "Show elapsed time" +msgstr "Mostrar el tiempo transcurrido" + +#: spyder/plugins/externalconsole.py:61 spyder/widgets/explorer.py:1094 +msgid "Show icons and text" +msgstr "Mostrar iconos y texto " + +#: spyder/plugins/externalconsole.py:73 +msgid "Buffer: " +msgstr "Mostrar:" + +#: spyder/plugins/externalconsole.py:73 spyder/plugins/ipythonconsole.py:319 +msgid " lines" +msgstr "líneas" + +#: spyder/plugins/externalconsole.py:78 +msgid "Merge process standard output/error channels" +msgstr "Combinar los canales de salida y error estándar del proceso" + +#: spyder/plugins/externalconsole.py:80 +msgid "" +"Merging the output channels of the process means that\n" +"the standard error won't be written in red anymore,\n" +"but this has the effect of speeding up display." +msgstr "" +"Combinar los canales de salida del proceso quiere decir que\n" +"el error estándar no será escrito en rojo , pero esto ayuda a\n" +"mejorar la velocidad en que aparece el texto en la terminal." + +#: spyder/plugins/externalconsole.py:84 +msgid "Colorize standard error channel using ANSI escape codes" +msgstr "Colorear el canal de error estándar usando códigos de escape ANSI " + +#: spyder/plugins/externalconsole.py:86 +msgid "" +"This method is the only way to have colorized standard\n" +"error channel when the output channels have been merged." +msgstr "" +"Éste método es la única forma de darle color al canal de error\n" +"estándar cuando los canales de salida han sido combinados." + +#: spyder/plugins/externalconsole.py:102 spyder/plugins/ipythonconsole.py:306 +#: spyder/widgets/variableexplorer/arrayeditor.py:540 +#: spyder/widgets/variableexplorer/dataframeeditor.py:552 +msgid "Background color" +msgstr "Color de fondo" + +#: spyder/plugins/externalconsole.py:103 +msgid "" +"This option will be applied the next time a Python console or a terminal is " +"opened." +msgstr "" +"Esta opción será aplicada la próxima vez que una terminal de Python o una " +"consola sea abierta." + +#: spyder/plugins/externalconsole.py:106 +msgid "Light background (white color)" +msgstr "Fondo claro (color blanco)" + +#: spyder/plugins/externalconsole.py:131 +msgid "PYTHONSTARTUP replacement" +msgstr "Reemplazo de PYTHONSTARTUP" + +#: spyder/plugins/externalconsole.py:133 +msgid "" +"This option will override the PYTHONSTARTUP environment variable which\n" +"defines the script to be executed during the Python console startup." +msgstr "" +"Esta opción modificará la variable de entorno PYTHONSTARTUP, la cual\n" +"define el archivo que es ejecutado durante el arranque de la terminal de\n" +"Python." + +#: spyder/plugins/externalconsole.py:138 +msgid "Default PYTHONSTARTUP script" +msgstr "Utilizar el archivo por defecto de PYTHONSTARTUP" + +#: spyder/plugins/externalconsole.py:142 +msgid "Use the following startup script:" +msgstr "Utilizar el siguiente archivo de arranque:" + +#: spyder/plugins/externalconsole.py:159 +msgid "Monitor" +msgstr "Monitor" + +#: spyder/plugins/externalconsole.py:160 +msgid "" +"The monitor provides introspection features to console: code completion, " +"calltips and variable explorer. Because it relies on several modules, " +"disabling the monitor may be useful to accelerate console startup." +msgstr "" +"El monitor es el que brinda características de introspección a la terminal: " +"completado del código, globos de sugerencias y el explorador de variables. " +"Dado que depende de varios módulos adicionales, desactivar el monitor puede " +"acelerar el arranque de la terminal." + +#: spyder/plugins/externalconsole.py:167 +msgid "Enable monitor" +msgstr "Activar el monitor" + +#: spyder/plugins/externalconsole.py:180 +msgid "Default library" +msgstr "Librería por defecto" + +#: spyder/plugins/externalconsole.py:185 +msgid "Qt-Python Bindings" +msgstr "Librerías de enlace entre Qt y Python" + +#: spyder/plugins/externalconsole.py:187 +msgid "Library:" +msgstr "Librería" + +#: spyder/plugins/externalconsole.py:189 +msgid "" +"This option will act on
libraries such as Matplotlib, guidata or ETS" +msgstr "" +"Esta opción tendrá efecto
en librerías como Matplotlib, guidata o ETS" + +#: spyder/plugins/externalconsole.py:198 spyder/plugins/ipythonconsole.py:560 +msgid "Graphics" +msgstr "Gráficas" + +#: spyder/plugins/externalconsole.py:199 +msgid "" +"Decide which backend to use to display graphics. If unsure, please select " +"the Automatic backend.

Note: We support a very limited " +"number of backends in our Python consoles. If you prefer to work with a " +"different one, please use an IPython console." +msgstr "" +"Decidir que salida gráfica utilizar para mostrar gráficas. Si no está " +"seguro, por favor seleccione la salida Automática.

Nota: La terminal de Python soporta un número muy limitado de salidas gráficas. " +"Si prefiere trabajar con alguna salida diferente, por favor utilice una " +"terminal de IPython." + +#: spyder/plugins/externalconsole.py:208 +msgid "None" +msgstr "Ninguno" + +#: spyder/plugins/externalconsole.py:208 spyder/plugins/ipythonconsole.py:351 +msgid "Automatic" +msgstr "Automático" + +#: spyder/plugins/externalconsole.py:213 spyder/plugins/ipythonconsole.py:373 +msgid "Backend:" +msgstr "Salida:" + +#: spyder/plugins/externalconsole.py:215 spyder/plugins/ipythonconsole.py:375 +msgid "This option will be applied the next time a console is opened." +msgstr "Esta opción será aplicada la próxima vez que una terminal sea abierta." + +#: spyder/plugins/externalconsole.py:226 +msgid "Enthought Tool Suite" +msgstr "Enthought Tool Suite" + +#: spyder/plugins/externalconsole.py:227 +msgid "" +"Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical " +"user interfaces." +msgstr "" +"Enthought Tool Suite (ETS) funciona con las librerías gráficas PyQt4 (qt4) " +"y \n" +"wxPython (wx). Esta opción establece cual desea utilizar el usuario." + +#: spyder/plugins/externalconsole.py:231 +msgid "ETS_TOOLKIT:" +msgstr "ETS_TOOLKIT:" + +#: spyder/plugins/externalconsole.py:255 +msgid "External modules" +msgstr "Módulos externos" + +#: spyder/plugins/externalconsole.py:447 +msgid "" +"No Python console is currently selected to run %s.

Please " +"select or open a new Python console and try again." +msgstr "" +"No existe una terminal de Python para ejecutar %s.

Por favor " +"abra una nueva y pruebe otra vez." + +#: spyder/plugins/externalconsole.py:518 +msgid "" +"%s is already running in a separate process.\n" +"Do you want to kill the process before starting a new one?" +msgstr "" +"%s ya se está ejecutando en proceso aparte.\n" +"¿Desea terminar este proceso antes de empezar uno nuevo?" + +#: spyder/plugins/externalconsole.py:647 +msgid "Command Window" +msgstr "Símbolo" + +#: spyder/plugins/externalconsole.py:649 spyder/plugins/ipythonconsole.py:297 +msgid "Terminal" +msgstr "Terminal" + +#: spyder/plugins/externalconsole.py:731 +msgid "Open a &Python console" +msgstr "Abrir una terminal de Python" + +#: spyder/plugins/externalconsole.py:735 +msgid "Open &command prompt" +msgstr "Abrir &símbolo del sistema" + +#: spyder/plugins/externalconsole.py:736 +msgid "Open a Windows command prompt" +msgstr "Abre el símbolo del sistema de Windows" + +#: spyder/plugins/externalconsole.py:738 +msgid "Open a &terminal" +msgstr "Abrir &terminal de comandos" + +#: spyder/plugins/externalconsole.py:739 +msgid "Open a terminal window" +msgstr "Abre una terminal del sistema" + +#: spyder/plugins/findinfiles.py:127 spyder/widgets/findinfiles.py:689 +msgid "Find in files" +msgstr "Buscar en archivos" + +#: spyder/plugins/findinfiles.py:148 +msgid "&Find in files" +msgstr "Bus&car en archivos" + +#: spyder/plugins/findinfiles.py:151 +msgid "Search text in multiple files" +msgstr "Buscar en varios archivos a la vez" + +#: spyder/plugins/help.py:44 +msgid "Show help for objects in the Editor and Consoles in a dedicated pane" +msgstr "Mostrar ayuda para los objetos del Editor y las terminales" + +#: spyder/plugins/help.py:110 +msgid "Automatic connections" +msgstr "Conexiones automáticas" + +#: spyder/plugins/help.py:111 +msgid "" +"This pane can automatically show an object's help information after a left " +"parenthesis is written next to it. Below you can decide to which plugin you " +"want to connect it to turn on this feature." +msgstr "" +"Este panel puede mostrar automáticamente la ayuda de un objeto después de " +"escribir un paréntesis junto al mismo. A continuación puede decidir a que " +"panel desea conectarlo para activar esta característica." + +#: spyder/plugins/help.py:123 +msgid "" +"This feature requires the Rope or Jedi libraries.\n" +"It seems you don't have either installed." +msgstr "" +"Esta característica requiere las librerías Rope o Jedi.\n" +"Al parecer no tiene ninguna instalada." + +#: spyder/plugins/help.py:126 +msgid "Python Console" +msgstr "Terminal de IPython" + +#: spyder/plugins/help.py:128 +msgid "IPython Console" +msgstr "Terminal de IPython" + +#: spyder/plugins/help.py:140 +msgid "Additional features" +msgstr "Características adicionales" + +#: spyder/plugins/help.py:141 +msgid "Render mathematical equations" +msgstr "Renderizar ecuaciones matemáticas" + +#: spyder/plugins/help.py:147 +msgid "This feature requires Sphinx 1.1 or superior." +msgstr "Esta característica requiere Sphinx 1.1 o superior." + +#: spyder/plugins/help.py:148 +msgid "Sphinx %s is currently installed." +msgstr "Sphinx %s está instalado actualmente." + +#: spyder/plugins/help.py:309 +msgid "No further documentation available" +msgstr "No existe más documentación disponible" + +#: spyder/plugins/help.py:347 +msgid "No documentation available" +msgstr "No existe documentación disponible" + +#: spyder/plugins/help.py:378 +msgid "Source" +msgstr "Origen" + +#: spyder/plugins/help.py:385 spyder/plugins/runconfig.py:186 +#: spyder/plugins/runconfig.py:456 spyder/widgets/externalshell/baseshell.py:94 +#: spyder/widgets/ipythonconsole/client.py:202 +msgid "Console" +msgstr "Terminal" + +#: spyder/plugins/help.py:393 +#: spyder/widgets/variableexplorer/collectionseditor.py:133 +msgid "Object" +msgstr "Objeto" + +#: spyder/plugins/help.py:407 +msgid "Plain Text" +msgstr "Texto plano" + +#: spyder/plugins/help.py:411 +msgid "Show Source" +msgstr "Mostrar código fuente" + +#: spyder/plugins/help.py:415 +msgid "Rich Text" +msgstr "Texto enriquecido" + +#: spyder/plugins/help.py:425 +msgid "Automatic import" +msgstr "Importar automáticamente" + +#: spyder/plugins/help.py:437 spyder/plugins/history.py:106 +#: spyder/widgets/editor.py:504 spyder/widgets/explorer.py:1105 +#: spyder/widgets/externalshell/baseshell.py:140 +#: spyder/widgets/ipythonconsole/client.py:251 +#: spyder/widgets/variableexplorer/namespacebrowser.py:160 +msgid "Options" +msgstr "Opciones" + +#: spyder/plugins/help.py:695 +msgid "" +"Here you can get help of any object by pressing %s in front of it, either on " +"the Editor or the Console.%sHelp can also be shown automatically after " +"writing a left parenthesis next to an object. You can activate this behavior " +"in %s." +msgstr "" +"En este panel es posible obtener la ayuda de cualquier objeto al oprimir %s " +"estando al frente del mismo, bien sea en el Editor o en la Terminal.%sEsta " +"ayuda también se puede mostrar automáticamente después de escribir un " +"paréntesis junto a un objeto. Este comportamiento puede activarse en %s." + +#: spyder/plugins/help.py:701 +msgid "Preferences > Help" +msgstr "Preferencias > Ayuda" + +#: spyder/plugins/help.py:708 +msgid "Usage" +msgstr "Uso" + +#: spyder/plugins/help.py:709 +msgid "New to Spyder? Read our" +msgstr "Nuevo en Spyder? Lee nuestro" + +#: spyder/plugins/help.py:710 +msgid "tutorial" +msgstr "tutorial" + +#: spyder/plugins/help.py:717 +msgid "" +"Please consider installing Sphinx to get documentation rendered in rich text." +msgstr "" +"Por favor considere instalar Sphinx para obtener la documentación en texto " +"enriquecido " + +#: spyder/plugins/help.py:886 +msgid "Lock" +msgstr "Bloquear" + +#: spyder/plugins/help.py:886 +msgid "Unlock" +msgstr "Desbloquear" + +#: spyder/plugins/help.py:930 +msgid "" +"The following error occured when calling Sphinx %s.
Incompatible " +"Sphinx version or doc string decoding failed.

Error message:
%s" +msgstr "" +"Ocurrió el siguiente error cuando se trató de utilizar Sphinx %s." +"
Ello se debe a una versión incompatible de Sphinx o bien a que no fue " +"posible leer la documentación solicitada.

Mensaje de error:
%s" + +#: spyder/plugins/help.py:974 +msgid "No source code available." +msgstr "No está disponible el código fuente" + +#: spyder/plugins/history.py:39 +msgid "Settings" +msgstr "Ajustes" + +#: spyder/plugins/history.py:41 +msgid " entries" +msgstr "entradas" + +#: spyder/plugins/history.py:41 +msgid "History depth: " +msgstr "Longitud del historial" + +#: spyder/plugins/history.py:48 +msgid "Scroll automatically to last entry" +msgstr "Desplazarse automáticamente a la última entrada" + +#: spyder/plugins/history.py:126 +msgid "History log" +msgstr "Historial de comandos" + +#: spyder/plugins/history.py:153 +msgid "History..." +msgstr "Historial..." + +#: spyder/plugins/history.py:155 +msgid "Set history maximum entries" +msgstr "Establece el máximo número de entradas a almacenar" + +#: spyder/plugins/history.py:260 +msgid "History" +msgstr "Historial" + +#: spyder/plugins/history.py:261 +msgid "Maximum entries" +msgstr "Máximo número de entradas" + +#: spyder/plugins/ipythonconsole.py:64 +msgid "Symbolic mathematics in the IPython Console" +msgstr "Matemática simbólica en la terminal de IPython" + +#: spyder/plugins/ipythonconsole.py:116 +msgid "" +"The authenticity of host %s can't be established. Are you sure you " +"want to continue connecting?" +msgstr "" +"La autenticidad del servidor %s no puede ser establecida. ¿Está " +"seguro de que desea continuar conectándose?" + +#: spyder/plugins/ipythonconsole.py:128 +msgid "The authenticity of the host can't be established" +msgstr "La autenticidad del servidor no puede ser establecida" + +#: spyder/plugins/ipythonconsole.py:135 +msgid "Tunnel '%s' failed to start" +msgstr "El túnel '%s' falló en ser iniciado" + +#: spyder/plugins/ipythonconsole.py:140 +msgid "Could not connect to remote host" +msgstr "No fue posible conectarse al servidor remoto" + +#: spyder/plugins/ipythonconsole.py:157 spyder/plugins/ipythonconsole.py:747 +msgid "Connect to an existing kernel" +msgstr "Conectarse a un núcleo existente" + +#: spyder/plugins/ipythonconsole.py:159 +msgid "" +"Please enter the connection info of the kernel you want to connect to. For " +"that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for " +"example kernel-3764.json or just 3764)." +msgstr "" +"Por favor introduzca la información de conexión del núcleo al cual desea " +"conectarse. Para ello puede seleccionar su archivo de conexión JSON, usando " +"el botón Seleccionar, o escribir directamente su id, en caso de que " +"sea un núcleo local (por ejemplo, kernel-3764.json o sólo 3764)" + +#: spyder/plugins/ipythonconsole.py:170 +msgid "Connection info:" +msgstr "Información de conexión:" + +#: spyder/plugins/ipythonconsole.py:172 +msgid "Path to connection file or kernel id" +msgstr "Ruta al archivo de conexión o id del núcleo" + +#: spyder/plugins/ipythonconsole.py:174 spyder/plugins/ipythonconsole.py:191 +msgid "Browse" +msgstr "Seleccionar" + +#: spyder/plugins/ipythonconsole.py:183 +msgid "This is a remote kernel" +msgstr "Este es un núcleo remoto" + +#: spyder/plugins/ipythonconsole.py:187 +msgid "username@hostname:port" +msgstr "usuario@servidor:puerto" + +#: spyder/plugins/ipythonconsole.py:190 +msgid "Path to ssh key file" +msgstr "Ruta al archivo de clave ssh" + +#: spyder/plugins/ipythonconsole.py:199 +msgid "Password or ssh key passphrase" +msgstr "Contraseña o frase de contraseña de la clave ssh" + +#: spyder/plugins/ipythonconsole.py:203 +msgid "Host name" +msgstr "Servidor" + +#: spyder/plugins/ipythonconsole.py:204 +msgid "Ssh key" +msgstr "Clave ssh" + +#: spyder/plugins/ipythonconsole.py:205 +msgid "Password" +msgstr "Contraseña" + +#: spyder/plugins/ipythonconsole.py:234 +msgid "Open connection file" +msgstr "Abrir un archivo de conexión" + +#: spyder/plugins/ipythonconsole.py:239 +msgid "Select ssh key" +msgstr "Seleccionar clave ssh" + +#: spyder/plugins/ipythonconsole.py:267 spyder/plugins/ipythonconsole.py:686 +msgid "IPython console" +msgstr "Terminal de IPython" + +#: spyder/plugins/ipythonconsole.py:274 +msgid "Display initial banner" +msgstr "Mostrar el banner inicial" + +#: spyder/plugins/ipythonconsole.py:275 +msgid "" +"This option lets you hide the message shown at\n" +"the top of the console when it's opened." +msgstr "" +"Esta opción le permite ocultar el mensaje que \n" +"aparece al principio de la terminal cuando se abre\n" +"por primera vez." + +#: spyder/plugins/ipythonconsole.py:277 +msgid "Use a pager to display additional text inside the console" +msgstr "Usar un paginador para mostrar textos dentro de la terminal" + +#: spyder/plugins/ipythonconsole.py:279 +msgid "" +"Useful if you don't want to fill the console with long help or completion " +"texts.\n" +"Note: Use the Q key to get out of the pager." +msgstr "" +"Es útil si no desea llenar la terminal con largos textos de ayuda.\n" +"Nota: Debe usar la tecla Q para salir del paginador" + +#: spyder/plugins/ipythonconsole.py:284 +msgid "Ask for confirmation before closing" +msgstr "Mostrar un diálogo de confirmación antes de cerrar una terminal" + +#: spyder/plugins/ipythonconsole.py:294 +msgid "Completion Type" +msgstr "Tipo de completado" + +#: spyder/plugins/ipythonconsole.py:295 +msgid "Decide what type of completion to use" +msgstr "Decidir que tipo de completado utilizar" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Graphical" +msgstr "Gráfico" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Plain" +msgstr "Plano" + +#: spyder/plugins/ipythonconsole.py:298 +msgid "Completion:" +msgstr "Completado:" + +#: spyder/plugins/ipythonconsole.py:307 +msgid "Light background" +msgstr "Fondo claro" + +#: spyder/plugins/ipythonconsole.py:309 +msgid "Dark background" +msgstr "Fondo oscuro" + +#: spyder/plugins/ipythonconsole.py:319 +msgid "Buffer: " +msgstr "Mostrar" + +#: spyder/plugins/ipythonconsole.py:321 +msgid "" +"Set the maximum number of lines of text shown in the\n" +"console before truncation. Specifying -1 disables it\n" +"(not recommended!)" +msgstr "" +"Establece el máximo número de líneas que se mostrarán\n" +"en la terminal en cualquier momento. Si se introduce -1 se\n" +"mostrarán todas las líneas (no se recomienda!)" + +#: spyder/plugins/ipythonconsole.py:330 +msgid "Support for graphics (Matplotlib)" +msgstr "Soporte para crear gráficas (Matplotlib)" + +#: spyder/plugins/ipythonconsole.py:331 +msgid "Activate support" +msgstr "Activar el soporte" + +#: spyder/plugins/ipythonconsole.py:332 +msgid "Automatically load Pylab and NumPy modules" +msgstr "Cargar automáticamente los módulos de Pylab y NumPy" + +#: spyder/plugins/ipythonconsole.py:335 +msgid "" +"This lets you load graphics support without importing \n" +"the commands to do plots. Useful to work with other\n" +"plotting libraries different to Matplotlib or to develop \n" +"GUIs with Spyder." +msgstr "" +"Esto le permite cargar el soporte gráfico sin importar\n" +"los comandos para crear figuras. Es útil para trabajar con\n" +"otras librerías gráficas diferentes a Matplotlib o para\n" +"desarrollar interfaces gráficas con Spyder." + +#: spyder/plugins/ipythonconsole.py:350 +msgid "Inline" +msgstr "En línea" + +#: spyder/plugins/ipythonconsole.py:352 +msgid "Graphics backend" +msgstr "Salida gráfica:" + +#: spyder/plugins/ipythonconsole.py:353 +msgid "" +"Decide how graphics are going to be displayed in the console. If unsure, " +"please select %s to put graphics inside the console or %s to " +"interact with them (through zooming and panning) in a separate window." +msgstr "" +"Decidir como se mostrarán las gráficas en la terminal. Si no está seguro, " +"por favor seleccione %s para colocar las gráficas en la consola o " +"%s para interactuar con ellas (a través de acercamientos y paneos) en " +"una ventana aparte." + +#: spyder/plugins/ipythonconsole.py:386 +msgid "Inline backend" +msgstr "Salida en línea:" + +#: spyder/plugins/ipythonconsole.py:387 +msgid "Decide how to render the figures created by this backend" +msgstr "" +"Decida como renderizar las figuras creadas por este tipo de salida gráfica" + +#: spyder/plugins/ipythonconsole.py:391 +msgid "Format:" +msgstr "Formato:" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "Resolution:" +msgstr "Resolución:" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "dpi" +msgstr "dpi" + +#: spyder/plugins/ipythonconsole.py:396 +msgid "Only used when the format is PNG. Default is 72" +msgstr "Sólo se usa cuando el formato es PNG. Por defecto es 72." + +#: spyder/plugins/ipythonconsole.py:399 +msgid "Width:" +msgstr "Ancho:" + +#: spyder/plugins/ipythonconsole.py:399 spyder/plugins/ipythonconsole.py:403 +msgid "inches" +msgstr "pulgadas" + +#: spyder/plugins/ipythonconsole.py:401 +msgid "Default is 6" +msgstr "Por defecto es 6" + +#: spyder/plugins/ipythonconsole.py:403 +msgid "Height:" +msgstr "Alto:" + +#: spyder/plugins/ipythonconsole.py:405 +msgid "Default is 4" +msgstr "Por defecto es 4" + +#: spyder/plugins/ipythonconsole.py:432 +msgid "" +"You can run several lines of code when a console is started. Please " +"introduce each one separated by commas, for example:
import os, import " +"sys" +msgstr "" +"Se pueden ejecutar varias líneas de código al abrir una terminal. Por favor " +"introduzca cada una separada por comas, por ejemplo:
import os, import " +"sys" + +#: spyder/plugins/ipythonconsole.py:438 +msgid "Lines:" +msgstr "Líneas:" + +#: spyder/plugins/ipythonconsole.py:447 +msgid "Run a file" +msgstr "Ejecutar un archivo" + +#: spyder/plugins/ipythonconsole.py:448 +msgid "" +"You can also run a whole file at startup instead of just some lines (This is " +"similar to have a PYTHONSTARTUP file)." +msgstr "" +"También se puede ejecutar un archivo completo al inicio, en lugar de unas " +"pocas líneas (Esto es similar a tener un archivo PYTHONSTARTUP)." + +#: spyder/plugins/ipythonconsole.py:452 +msgid "Use the following file:" +msgstr "Usar el siguiente archivo:" + +#: spyder/plugins/ipythonconsole.py:466 +msgid "Greedy completion" +msgstr "Completado ambicioso" + +#: spyder/plugins/ipythonconsole.py:467 +msgid "" +"Enable Tab completion on elements of lists, results of function " +"calls, etc, without assigning them to a variable.
For example, you " +"can get completions on things like li[0].<Tab> or ins." +"meth().<Tab>" +msgstr "" +"Habilita el completado usando la tecla Tab en elementos de listas, " +"resultados de llamadas de funciones, etc, sin asignarlos a una " +"variable.
De esta forma se pueden obtener sugerencias de completado en " +"cosas como li[0].<Tab> o ins.meth().<Tab>" + +#: spyder/plugins/ipythonconsole.py:475 +msgid "Use the greedy completer" +msgstr "Usar el completado ambicioso" + +#: spyder/plugins/ipythonconsole.py:486 +msgid "Autocall" +msgstr "Autollamar" + +#: spyder/plugins/ipythonconsole.py:487 +msgid "" +"Autocall makes IPython automatically call any callable object even if you " +"didn't type explicit parentheses.
For example, if you type str 43 " +"it becomes str(43) automatically." +msgstr "" +"Esta opción hace que IPython llame automáticamente cualquier objeto " +"\"llamable\" (callable) aún si no se escriben paréntesis a su alrededor." +"
Por ejemplo, al escribir str 43, se convertirá automáticamente en " +"str(43)." + +#: spyder/plugins/ipythonconsole.py:494 +msgid "Smart" +msgstr "Inteligente" + +#: spyder/plugins/ipythonconsole.py:495 +msgid "Full" +msgstr "Total" + +#: spyder/plugins/ipythonconsole.py:496 +msgid "Off" +msgstr "Desactivado" + +#: spyder/plugins/ipythonconsole.py:498 +msgid "Autocall: " +msgstr "Autollamar:" + +#: spyder/plugins/ipythonconsole.py:499 +msgid "" +"On %s mode, Autocall is not applied if there are no arguments after " +"the callable. On %s mode, all callable objects are automatically " +"called (even if no arguments are present)." +msgstr "" +"En modo %s, Autollamar no se usa si no hay argumentos después del " +"objeto llamable. En modo %s, todos los objetos llamables son llamados " +"automáticamente (aún si no hay argumentos presentes)." + +#: spyder/plugins/ipythonconsole.py:511 +msgid "Symbolic Mathematics" +msgstr "Matemática simbólica" + +#: spyder/plugins/ipythonconsole.py:512 +msgid "" +"Perfom symbolic operations in the console (e.g. integrals, derivatives, " +"vector calculus, etc) and get the outputs in a beautifully printed style (it " +"requires the Sympy module)." +msgstr "" +"Realice operaciones simbólicas en la terminal (integrales, derivadas o " +"cálculo vectorial) y obtenga los resultados en un bello estilo impreso " +"(requiere el módulo Sympy)." + +#: spyder/plugins/ipythonconsole.py:517 +msgid "Use symbolic math" +msgstr "Usar matemática simbólica" + +#: spyder/plugins/ipythonconsole.py:518 +msgid "" +"This option loads the Sympy library to work with.
Please refer to its " +"documentation to learn how to use it." +msgstr "" +"Esta opción carga la librería Sympy para trabajar
con ella. Por favor lea " +"su documentación para aprender como usarla." + +#: spyder/plugins/ipythonconsole.py:528 +msgid "Prompts" +msgstr "Prompts" + +#: spyder/plugins/ipythonconsole.py:529 +msgid "Modify how Input and Output prompts are shown in the console." +msgstr "" +"Modifique como se muestran los prompts de entrada y salida en la terminal." + +#: spyder/plugins/ipythonconsole.py:532 +msgid "Input prompt:" +msgstr "Prompt de entrada:" + +#: spyder/plugins/ipythonconsole.py:534 +msgid "" +"Default is
In [<span class=\"in-prompt-number\">%i</span>]:" +msgstr "" +"Por defecto es
In [<span class=\"in-prompt-number\">%i</" +"span>]:" + +#: spyder/plugins/ipythonconsole.py:538 +msgid "Output prompt:" +msgstr "Prompt de salida:" + +#: spyder/plugins/ipythonconsole.py:540 +msgid "" +"Default is
Out[<span class=\"out-prompt-number\">%i</span>]:" +msgstr "" +"Por defecto es
Out[<span class=\"out-prompt-number\">%i</" +"span>]:" + +#: spyder/plugins/ipythonconsole.py:562 spyder/plugins/workingdirectory.py:45 +msgid "Startup" +msgstr "Inicialización" + +#: spyder/plugins/ipythonconsole.py:734 +msgid "Open an &IPython console" +msgstr "Abrir una terminal de IPython" + +#: spyder/plugins/ipythonconsole.py:737 +msgid "Use %s+T when the console is selected to open a new one" +msgstr "Usar %s+T para abrir una nueva terminal" + +#: spyder/plugins/ipythonconsole.py:740 +msgid "Open a new console" +msgstr "Abrir una nueva terminal" + +#: spyder/plugins/ipythonconsole.py:748 +msgid "Open a new IPython console connected to an existing kernel" +msgstr "Abrir una nueva terminal de IPython conectada a un núcleo existente" + +#: spyder/plugins/ipythonconsole.py:836 +msgid "" +"No IPython console is currently available to run %s.

Please " +"open a new one and try again." +msgstr "" +"No existe un intérprete de IPython para ejecutar %s.

Por favor " +"abra uno nuevo e intente otra vez." + +#: spyder/plugins/ipythonconsole.py:880 +msgid "" +"Your Python environment or installation doesn't have the ipykernel " +"module installed on it. Without this module is not possible for Spyder to " +"create a console for you.

You can install ipykernel by " +"running in a terminal:

pip install ipykernel

or

conda install ipykernel" +msgstr "" +"Su entorno o instalación de Python no cuenta con el módulo ipykernel. Sin este módulo no es posible para Spyder el crear una terminal." +"

Puede instalar ipykernel al ejecutar en una consola de " +"comandos:

pip install ipykernel

o

conda " +"install ipykernel" + +#: spyder/plugins/ipythonconsole.py:1105 +msgid "Do you want to close this console?" +msgstr "¿Desea cerrar esta terminal?" + +#: spyder/plugins/ipythonconsole.py:1111 +msgid "" +"Do you want to close all other consoles connected to the same kernel as this " +"one?" +msgstr "" +"¿Desea cerrar todas las otras terminales conectadas al mismo núcleo que ésta?" + +#: spyder/plugins/ipythonconsole.py:1382 +msgid "IPython" +msgstr "IPython" + +#: spyder/plugins/ipythonconsole.py:1383 +msgid "Unable to connect to %s" +msgstr "No se pudo establecer conexión con `%s`" + +#: spyder/plugins/ipythonconsole.py:1443 +msgid "Connection error" +msgstr "Error de conexión" + +#: spyder/plugins/ipythonconsole.py:1444 +msgid "" +"Could not open ssh tunnel. The error was:\n" +"\n" +msgstr "" +"No fue posible crear un túnel ssh. El error fue:\n" +"\n" + +#: spyder/plugins/layoutdialog.py:177 +msgid "Move Up" +msgstr "Mover arriba" + +#: spyder/plugins/layoutdialog.py:178 +msgid "Move Down" +msgstr "Mover abajo" + +#: spyder/plugins/layoutdialog.py:179 +msgid "Delete Layout" +msgstr "Eliminar disposición" + +#: spyder/plugins/layoutdialog.py:183 +msgid "Layout Display and Order" +msgstr "Orden y visualización de las disposiciones" + +#: spyder/plugins/maininterpreter.py:31 spyder/plugins/maininterpreter.py:66 +msgid "Python interpreter" +msgstr "Intérprete de Python" + +#: spyder/plugins/maininterpreter.py:68 +msgid "Select the Python interpreter for all Spyder consoles" +msgstr "" +"Seleccionar el intérprete de Python para todas las terminales de Spyder" + +#: spyder/plugins/maininterpreter.py:71 +msgid "Default (i.e. the same as Spyder's)" +msgstr "Por defecto (es decir, el mismo de Spyder)" + +#: spyder/plugins/maininterpreter.py:74 +msgid "Use the following Python interpreter:" +msgstr "Usar el siguiente intérprete:" + +#: spyder/plugins/maininterpreter.py:77 +msgid "Executables" +msgstr "Ejecutables" + +#: spyder/plugins/maininterpreter.py:94 +msgid "User Module Reloader (UMR)" +msgstr "Recargador de Módulos del Usuario (RMU)" + +#: spyder/plugins/maininterpreter.py:95 +msgid "" +"UMR forces Python to reload modules which were imported when executing a " +"file in a Python or IPython console with the runfile function." +msgstr "" +"El RMU obliga a Python a recargar los módulos que fueron importados durante " +"la ejecución de un archivo en la terminal con la función 'runfile'." + +#: spyder/plugins/maininterpreter.py:100 +msgid "Enable UMR" +msgstr "Activar el RMU" + +#: spyder/plugins/maininterpreter.py:101 +msgid "" +"This option will enable the User Module Reloader (UMR) in Python/IPython " +"consoles. UMR forces Python to reload deeply modules during import when " +"running a Python script using the Spyder's builtin function runfile." +"

1. UMR may require to restart the console in which it will be " +"called (otherwise only newly imported modules will be reloaded when " +"executing files).

2. If errors occur when re-running a PyQt-" +"based program, please check that the Qt objects are properly destroyed (e.g. " +"you may have to use the attribute Qt.WA_DeleteOnClose on your main " +"window, using the setAttribute method)" +msgstr "" +"Esta opción activará el Recargador de Módulos del Usuario (RMU) en las " +"terminales de Python y IPython. El RMU obliga a Python a recargar " +"profundamente los módulos que fueron importados al ejecutar un archivo de " +"Python, usando la función incorporada de Spyder llamada runfile." +"

1. El RMU puede requerir que se reinicie la terminal en la " +"cual será utilizado (de otra forma, sólo los módulos que fueron importados " +"en último lugar serán recargados al ejecutar un archivo).

2. " +"Si ocurre algún error al re-ejecutar programas basados en PyQt, por favor " +"verifique que los objetos de Qt sean destruidos apropiadamente (por ejemplo, " +"puede tener que usar el atributo Qt.WA_DeleteOnClose en su ventana " +"principal, utilizando para ello el método setAttribute)." + +#: spyder/plugins/maininterpreter.py:117 +msgid "Show reloaded modules list" +msgstr "Mostrar la lista de módulos que fueron recargados" + +#: spyder/plugins/maininterpreter.py:118 +msgid "Please note that these changes will be applied only to new consoles" +msgstr "" +"Por favor tenga en cuenta que estos cambios sólo se aplicarán a nuevas " +"terminales" + +#: spyder/plugins/maininterpreter.py:122 +msgid "Set UMR excluded (not reloaded) modules" +msgstr "Establecer la lista de módulos excluidos por el RMU" + +#: spyder/plugins/maininterpreter.py:168 +msgid "" +"You selected a Python %d interpreter for the console but Spyder is " +"running on Python %d!.

Although this is possible, we recommend " +"you to install and run Spyder directly with your selected interpreter, to " +"avoid seeing false warnings and errors due to the incompatible syntax " +"between these two Python versions." +msgstr "" +"Usted seleccionó un intérprete de Python %d para la terminal, pero " +"Spyder está corriendo bajo Python %d!.

Aunque esto es posible, " +"le recomendamos instalar y correr Spyder directamente con el intérprete " +"seleccionado, para evitar ver falsos errores y alarmas en el Editor debido a " +"la sintaxis incompatible entre estas dos versiones de Python." + +#: spyder/plugins/maininterpreter.py:178 spyder/plugins/maininterpreter.py:205 +#: spyder/plugins/maininterpreter.py:209 +msgid "UMR" +msgstr "RMU" + +#: spyder/plugins/maininterpreter.py:179 +msgid "Set the list of excluded modules as this: numpy, scipy" +msgstr "Establezca la lista de módulos excluidos como: numpy, scipy" + +#: spyder/plugins/maininterpreter.py:196 +msgid "" +"You are working with Python 2, this means that you can not import a module " +"that contains non-ascii characters." +msgstr "" +"No es posible importar un módulo con caracteres que no son ascii en Python " +"2. Por favor introduzca un módulo diferente." + +#: spyder/plugins/maininterpreter.py:206 +msgid "" +"The following modules are not installed on your machine:\n" +"%s" +msgstr "" +"Los siguientes módulos no están instalados en su computador:\n" +"%s" + +#: spyder/plugins/maininterpreter.py:210 +msgid "" +"Please note that these changes will be applied only to new Python/IPython " +"consoles" +msgstr "" +"Por favor tenga en cuenta que estos cambios sólo se aplicarán a nuevas " +"terminales de IPython y Python" + +#: spyder/plugins/onlinehelp.py:70 +msgid "Online help" +msgstr "Ayuda en línea" + +#: spyder/plugins/outlineexplorer.py:49 spyder/widgets/editortools.py:195 +msgid "Outline" +msgstr "Explorador de código" + +#: spyder/plugins/projects.py:76 spyder/widgets/projects/explorer.py:113 +#: spyder/widgets/projects/explorer.py:127 +msgid "Project explorer" +msgstr "Explorador de proyectos" + +#: spyder/plugins/projects.py:88 +msgid "New Project..." +msgstr "Nuevo proyecto..." + +#: spyder/plugins/projects.py:91 +msgid "Open Project..." +msgstr "Abrir proyecto..." + +#: spyder/plugins/projects.py:94 +msgid "Close Project" +msgstr "Cerrar proyecto" + +#: spyder/plugins/projects.py:97 +msgid "Delete Project" +msgstr "Eliminar proyecto" + +#: spyder/plugins/projects.py:103 +msgid "Project Preferences" +msgstr "Preferencias del proyecto" + +#: spyder/plugins/projects.py:105 +msgid "Recent Projects" +msgstr "Proyectos recientes" + +#: spyder/plugins/projects.py:240 +msgid "Open project" +msgstr "Abrir proyecto" + +#: spyder/plugins/projects.py:245 +msgid "%s is not a Spyder project!" +msgstr "%s no es un proyecto de Spyder!" + +#: spyder/plugins/runconfig.py:29 +msgid "Execute in current Python or IPython console" +msgstr "Ejecutar en la terminal actual de Python o IPython" + +#: spyder/plugins/runconfig.py:30 +msgid "Execute in a new dedicated Python console" +msgstr "Ejecutar en una terminal de Python dedicada" + +#: spyder/plugins/runconfig.py:31 +msgid "Execute in an external System terminal" +msgstr "Ejecutar en una terminal de comandos del sistema" + +#: spyder/plugins/runconfig.py:41 +msgid "Always show %s on a first file run" +msgstr "Siempre muestre %s en una primera ejecución" + +#: spyder/plugins/runconfig.py:160 spyder/plugins/runconfig.py:474 +msgid "General settings" +msgstr "Ajustes generales" + +#: spyder/plugins/runconfig.py:163 spyder/plugins/runconfig.py:209 +msgid "Command line options:" +msgstr "Opciones de línea de comandos:" + +#: spyder/plugins/runconfig.py:169 +msgid "Working directory:" +msgstr "Directorio de trabajo:" + +#: spyder/plugins/runconfig.py:181 spyder/plugins/runconfig.py:492 +msgid "Enter debugging mode when errors appear during execution" +msgstr "Entrar en el modo de depuración cuando aparecen errores" + +#: spyder/plugins/runconfig.py:197 spyder/plugins/runconfig.py:502 +msgid "Dedicated Python console" +msgstr "Terminal de Python dedicada" + +#: spyder/plugins/runconfig.py:201 spyder/plugins/runconfig.py:504 +msgid "Interact with the Python console after execution" +msgstr "Interactuar con la terminal después de la ejecución" + +#: spyder/plugins/runconfig.py:205 +msgid "Show warning when killing running process" +msgstr "Mostrar una advertencia cuando se termine el proceso" + +#: spyder/plugins/runconfig.py:214 +msgid "-u is added to the other options you set here" +msgstr "La opción -u se añade a estas opciones" + +#: spyder/plugins/runconfig.py:224 +msgid "this dialog" +msgstr "este diálogo" + +#: spyder/plugins/runconfig.py:283 +msgid "Run configuration" +msgstr "Opciones de ejecución" + +#: spyder/plugins/runconfig.py:284 +msgid "The following working directory is not valid:
%s" +msgstr "El siguiente directorio de trabajo no es válido:
%s" + +#: spyder/plugins/runconfig.py:362 +msgid "Run settings for %s" +msgstr "Ajustes de ejecución para %s" + +#: spyder/plugins/runconfig.py:394 +msgid "Select a run configuration:" +msgstr "Seleccionar una configuración de ejecución:" + +#: spyder/plugins/runconfig.py:423 spyder/plugins/runconfig.py:448 +msgid "Run Settings" +msgstr "Ajustes de ejecución" + +#: spyder/plugins/runconfig.py:450 +msgid "" +"The following are the default %s. These options may be overriden " +"using the %s dialog box (see the %s menu)" +msgstr "" +"Las siguientes son los %s por defecto. Estos ajustes pueden " +"modificarse usando el diálogo %s (ver el menú %s)" + +#: spyder/plugins/runconfig.py:476 +msgid "Default working directory is:" +msgstr "Directorio de trabajo:" + +#: spyder/plugins/runconfig.py:478 +msgid "the script directory" +msgstr "El directorio en el que se encuentra el archivo actual" + +#: spyder/plugins/runconfig.py:481 spyder/plugins/workingdirectory.py:57 +msgid "the following directory:" +msgstr "el siguiente directorio:" + +#: spyder/plugins/runconfig.py:507 +msgid "Show warning when killing running processes" +msgstr "Mostrar una advertencia cuando se termine el proceso" + +#: spyder/plugins/runconfig.py:516 +msgid "Run Settings dialog" +msgstr "Ajustes de ejecución" + +#: spyder/plugins/shortcuts.py:137 +msgid "" +"Press the new shortcut and select 'Ok': \n" +"(Press 'Tab' once to switch focus between the shortcut entry \n" +"and the buttons below it)" +msgstr "" +"Teclee el nuevo atajo y seleccione 'Ok': \n" +"(Presione 'Tab' una vez para cambiar el foco entre la entrada del atajo \n" +"y los botones que aparecen abajo)" + +#: spyder/plugins/shortcuts.py:140 +msgid "Current shortcut:" +msgstr "Atajo seleccionado:" + +#: spyder/plugins/shortcuts.py:142 +msgid "New shortcut:" +msgstr "Nuevo atajo:" + +#: spyder/plugins/shortcuts.py:155 +msgid "Shortcut: {0}" +msgstr "Atajo: {0}" + +#: spyder/plugins/shortcuts.py:276 +msgid "Please introduce a different shortcut" +msgstr "Por favor introduzca un nuevo atajo" + +#: spyder/plugins/shortcuts.py:313 +msgid "The new shorcut conflicts with:" +msgstr "El nuevo atajo tiene coincide con:" + +#: spyder/plugins/shortcuts.py:324 +msgid "" +"A compound sequence can have {break} a maximum of 4 subsequences.{break}" +msgstr "" +"Una secuencia compuesta puede tener {break} un máximo de 4 sub-secuencias." +"{break}" + +#: spyder/plugins/shortcuts.py:329 +msgid "Invalid key entered" +msgstr "Tecla inválida" + +#: spyder/plugins/shortcuts.py:531 +msgid "Context" +msgstr "Contexto" + +#: spyder/plugins/shortcuts.py:533 +#: spyder/widgets/variableexplorer/collectionseditor.py:118 +msgid "Name" +msgstr "Nombre" + +#: spyder/plugins/shortcuts.py:535 +msgid "Shortcut" +msgstr "Atajo" + +#: spyder/plugins/shortcuts.py:537 +msgid "Score" +msgstr "Puntaje" + +#: spyder/plugins/shortcuts.py:697 +msgid "Conflicts" +msgstr "Conflicto con" + +#: spyder/plugins/shortcuts.py:698 +msgid "The following conflicts have been detected:" +msgstr "Los siguientes conflictos han sido detectados:" + +#: spyder/plugins/shortcuts.py:783 +msgid "Keyboard shortcuts" +msgstr "Atajos de teclado" + +#: spyder/plugins/shortcuts.py:791 +msgid "Search: " +msgstr "Buscar:" + +#: spyder/plugins/shortcuts.py:792 +msgid "Reset to default values" +msgstr "Restaurar los valores por defecto" + +#: spyder/plugins/variableexplorer.py:26 +msgid "Autorefresh" +msgstr "Actualizar automáticamente" + +#: spyder/plugins/variableexplorer.py:27 +msgid "Enable autorefresh" +msgstr "Activar las actualizaciones automáticas" + +#: spyder/plugins/variableexplorer.py:29 +msgid "Refresh interval: " +msgstr "Intervalo de actualización" + +#: spyder/plugins/variableexplorer.py:33 +msgid "Filter" +msgstr "Filtrar" + +#: spyder/plugins/variableexplorer.py:35 +#: spyder/widgets/variableexplorer/namespacebrowser.py:226 +msgid "Exclude private references" +msgstr "Excluir variables privadas" + +#: spyder/plugins/variableexplorer.py:36 +#: spyder/widgets/variableexplorer/namespacebrowser.py:241 +msgid "Exclude capitalized references" +msgstr "Excluir variables que comienzan en mayúsculas" + +#: spyder/plugins/variableexplorer.py:37 +#: spyder/widgets/variableexplorer/namespacebrowser.py:234 +msgid "Exclude all-uppercase references" +msgstr "Excluir variables en mayúsculas" + +#: spyder/plugins/variableexplorer.py:38 +#: spyder/widgets/variableexplorer/namespacebrowser.py:249 +msgid "Exclude unsupported data types" +msgstr "Excluir tipos de datos no soportados" + +#: spyder/plugins/variableexplorer.py:46 +#: spyder/widgets/variableexplorer/collectionseditor.py:677 +msgid "Show arrays min/max" +msgstr "Mostrar el máximo y mínimo de arreglos" + +#: spyder/plugins/variableexplorer.py:48 +msgid "Edit data in the remote process" +msgstr "Editar los datos en un proceso remoto" + +#: spyder/plugins/variableexplorer.py:49 +msgid "" +"Editors are opened in the remote process for NumPy arrays, PIL images, " +"lists, tuples and dictionaries.\n" +"This avoids transfering large amount of data between the remote process and " +"Spyder (through the socket)." +msgstr "" +"Esta opción permite modificar arreglos de NumPy, imágenes de\n" +"de PIL/Pillow, Dataframes, y listas, tuplas y diccionarios en el\n" +"proceso remoto. Esto impide transferir grandes cantidades de\n" +"datos entre el proceso remoto y Spyder." + +#: spyder/plugins/variableexplorer.py:185 +msgid "Variable explorer" +msgstr "Explorador de variables" + +#: spyder/plugins/workingdirectory.py:38 +msgid "" +"The global working directory is the working directory for newly " +"opened consoles (Python/IPython consoles and terminals), for the " +"file explorer, for the find in files plugin and for new files " +"created in the editor." +msgstr "" +"El directorio de trabajo global es el directorio de trabajo para las " +"terminales que se abran de aquí en adelante (de IPython y Python, y " +"terminales de comandos), para el Explorador de archivos y Buscar " +"en archivos y para los nuevos archivos creados en el Editor." + +#: spyder/plugins/workingdirectory.py:47 +msgid "At startup, the global working directory is:" +msgstr "Al inicio, el directorio de trabajo global es:" + +#: spyder/plugins/workingdirectory.py:51 +msgid "the same as in last session" +msgstr "El mismo de la última sesión" + +#: spyder/plugins/workingdirectory.py:53 +msgid "At startup, Spyder will restore the global directory from last session" +msgstr "Al inicio Spyder restaurará el directorio global de la última sesión" + +#: spyder/plugins/workingdirectory.py:59 +msgid "At startup, the global working directory will be the specified path" +msgstr "Al inicio el directorio de trabajo global será el siguiente" + +#: spyder/plugins/workingdirectory.py:71 +msgid "Files are opened from:" +msgstr "Los archivos deben abrirse desde:" + +#: spyder/plugins/workingdirectory.py:75 spyder/plugins/workingdirectory.py:88 +msgid "the current file directory" +msgstr "El directorio en el que se encuentra el archivo actual" + +#: spyder/plugins/workingdirectory.py:79 spyder/plugins/workingdirectory.py:92 +msgid "the global working directory" +msgstr "El directorio de trabajo global" + +#: spyder/plugins/workingdirectory.py:84 +msgid "Files are created in:" +msgstr "Los archivos son creados en:" + +#: spyder/plugins/workingdirectory.py:98 +msgid "Change to file base directory" +msgstr "Cambiarse al directorio base de un archivo" + +#: spyder/plugins/workingdirectory.py:100 +msgid "When opening a file" +msgstr "Cuando se abra un archivo" + +#: spyder/plugins/workingdirectory.py:102 +msgid "When saving a file" +msgstr "Cuando se guarde un archivo" + +#: spyder/plugins/workingdirectory.py:172 +msgid "Back" +msgstr "Anterior" + +#: spyder/plugins/workingdirectory.py:180 spyder/widgets/explorer.py:1099 +#: spyder/widgets/variableexplorer/importwizard.py:529 +msgid "Next" +msgstr "Siguiente" + +#: spyder/plugins/workingdirectory.py:191 +msgid "" +"This is the working directory for newly\n" +"opened consoles (Python/IPython consoles and\n" +"terminals), for the file explorer, for the\n" +"find in files plugin and for new files\n" +"created in the editor" +msgstr "" +"Este es el directorio de trabajo para las\n" +"terminales que se abran de aquí en\n" +"adelante (de IPython y Python y terminales de\n" +"comandos), para el Explorador de archivos,\n" +"y Buscar en archivos y para los nuevos\n" +"archivos creados en el Editor" + +#: spyder/plugins/workingdirectory.py:219 +msgid "Browse a working directory" +msgstr "Seleccionar un directorio de trabajo" + +#: spyder/plugins/workingdirectory.py:226 +msgid "Change to parent directory" +msgstr "Moverse al directorio superior" + +#: spyder/plugins/workingdirectory.py:233 +msgid "Global working directory" +msgstr "Directorio de trabajo global" + +#: spyder/utils/codeanalysis.py:91 +msgid "Real-time code analysis on the Editor" +msgstr "Análisis del código en tiempo real en el Editor" + +#: spyder/utils/codeanalysis.py:95 +msgid "Real-time code style analysis on the Editor" +msgstr "Análisis de estilo del código en el Editor" + +#: spyder/utils/environ.py:101 +msgid "" +"Module pywin32 was not found.
Please restart this Windows " +"session (not the computer) for changes to take effect." +msgstr "" +"No se pudo encontrar el módulo pywin32.
Por favor reinicie esta " +"sesión de Windows (no el computador) para que los cambios surtan " +"efecto." + +#: spyder/utils/environ.py:114 +msgid "" +"If you accept changes, this will modify the current user environment " +"variables directly in Windows registry. Use it with precautions, at " +"your own risks.

Note that for changes to take effect, you will need " +"to restart the parent process of this application (simply restart Spyder if " +"you have executed it from a Windows shortcut, otherwise restart any " +"application from which you may have executed it, like Python(x,y) Home for example)" +msgstr "" +"Si acepta los cambios, se modificarán las variables de entorno del usuario " +"actual directamente en el registro de Windows. Hágalo con precaución " +"y bajo su propio riesgo.

Tenga en cuenta que para que los cambios " +"tengan efecto, deberá reiniciar el proceso padre de esta aplicación " +"(simplemente reinicie Spyder si lo ejecutó desde un acceso directo, de otra " +"forma reinicie la aplicación desde la cual lo inició, como por ejemplo " +"Python(x,y) Home)" + +#: spyder/utils/help/sphinxify.py:217 spyder/utils/help/sphinxify.py:227 +msgid "" +"It was not possible to generate rich text help for this object.
Please " +"see it in plain text." +msgstr "" +"No fue posible generar ayuda en texto enriquecido para este objeto.
Por " +"favor véala en texto plano." + +#: spyder/utils/introspection/manager.py:33 +#: spyder/utils/introspection/manager.py:38 +msgid "Editor's code completion, go-to-definition and help" +msgstr "Completado del código y ayuda en el Editor" + +#: spyder/utils/iofuncs.py:408 +msgid "Supported files" +msgstr "Archivos soportados" + +#: spyder/utils/iofuncs.py:410 +msgid "All files (*.*)" +msgstr "Todos los archivos (*.*)" + +#: spyder/utils/iofuncs.py:420 +msgid "Spyder data files" +msgstr "Archivos de datos de Spyder" + +#: spyder/utils/iofuncs.py:422 +#: spyder/widgets/variableexplorer/collectionseditor.py:1021 +msgid "NumPy arrays" +msgstr "Arreglos de NumPy" + +#: spyder/utils/iofuncs.py:423 +msgid "NumPy zip arrays" +msgstr "Arreglos comprimidos de NumPy" + +#: spyder/utils/iofuncs.py:424 +msgid "Matlab files" +msgstr "Archivos de Matlab" + +#: spyder/utils/iofuncs.py:425 +msgid "CSV text files" +msgstr "Archivos de texto CSV" + +#: spyder/utils/iofuncs.py:427 +msgid "JPEG images" +msgstr "Imágenes JPEG" + +#: spyder/utils/iofuncs.py:428 +msgid "PNG images" +msgstr "Imágenes PNG" + +#: spyder/utils/iofuncs.py:429 +msgid "GIF images" +msgstr "Imágenes GIF" + +#: spyder/utils/iofuncs.py:430 +msgid "TIFF images" +msgstr "Imágenes TIFF" + +#: spyder/utils/iofuncs.py:431 spyder/utils/iofuncs.py:432 +msgid "Pickle files" +msgstr "Archivos pickle" + +#: spyder/utils/iofuncs.py:433 +msgid "JSON files" +msgstr "Archivos JSON" + +#: spyder/utils/iofuncs.py:452 spyder/utils/iofuncs.py:459 +msgid "Unsupported file type '%s'" +msgstr "Tipo de archivo no soportado '%s'" + +#: spyder/utils/programs.py:286 +msgid "It was not possible to run this file in an external terminal" +msgstr "No fue posible ejecutar este archivo en una terminal del sistema" + +#: spyder/utils/syntaxhighlighters.py:33 +msgid "Syntax highlighting for Matlab, Julia and other file types" +msgstr "" +"Coloreado del código para archivos tipo Matlab, Julia y varios otros tipos" + +#: spyder/utils/syntaxhighlighters.py:42 +msgid "Background:" +msgstr "Fondo:" + +#: spyder/utils/syntaxhighlighters.py:43 +#: spyder/widgets/sourcecode/codeeditor.py:106 +msgid "Current line:" +msgstr "Línea seleccionada:" + +#: spyder/utils/syntaxhighlighters.py:44 +msgid "Current cell:" +msgstr "Celda seleccionada:" + +#: spyder/utils/syntaxhighlighters.py:45 +msgid "Occurrence:" +msgstr "Ocurrencia:" + +#: spyder/utils/syntaxhighlighters.py:46 +msgid "Link:" +msgstr "Enlace:" + +#: spyder/utils/syntaxhighlighters.py:47 +msgid "Side areas:" +msgstr "Áreas laterales:" + +#: spyder/utils/syntaxhighlighters.py:48 +msgid "Matched
parens:" +msgstr "Paréntesis
emparejados:" + +#: spyder/utils/syntaxhighlighters.py:49 +msgid "Unmatched
parens:" +msgstr "Paréntesis
desemparejados:" + +#: spyder/utils/syntaxhighlighters.py:50 +msgid "Normal text:" +msgstr "Texto normal:" + +#: spyder/utils/syntaxhighlighters.py:51 +msgid "Keyword:" +msgstr "Palabra clave:" + +#: spyder/utils/syntaxhighlighters.py:52 +msgid "Builtin:" +msgstr "Objeto integrado:" + +#: spyder/utils/syntaxhighlighters.py:53 +msgid "Definition:" +msgstr "Definición:" + +#: spyder/utils/syntaxhighlighters.py:54 +msgid "Comment:" +msgstr "Comentario:" + +#: spyder/utils/syntaxhighlighters.py:55 +msgid "String:" +msgstr "Cadena:" + +#: spyder/utils/syntaxhighlighters.py:56 +msgid "Number:" +msgstr "Número:" + +#: spyder/utils/syntaxhighlighters.py:57 +msgid "Instance:" +msgstr "Instancia:" + +#: spyder/widgets/arraybuilder.py:179 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Type an array in Matlab : [1 2;3 4]
\n" +" or Spyder simplified syntax : 1 2;3 4\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two spaces or two tabs to generate a ';'.\n" +" " +msgstr "" +"\n" +" Ayuda para arreglos de Numpy
\n" +" Escriba un arreglo en Matlab : [1 2;3 4]
\n" +" o sintaxis simplificada de Spyder : 1 2;3 4\n" +"

\n" +" Oprima 'Enter' para un arreglo o 'Ctrl+Enter' para una matriz.\n" +"

\n" +" Consejo:
\n" +" Use dos espacios o tabs para generar un ';'.\n" +" " + +#: spyder/widgets/arraybuilder.py:190 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Enter an array in the table.
\n" +" Use Tab to move between cells.\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two tabs at the end of a row to move to the next row.\n" +" " +msgstr "" +"\n" +" Ayuda para arreglos de Numpy
\n" +" Introduzca un arreglo en la tabla.
\n" +" Use Tab para moverse entre las celdas.\n" +"

\n" +" Oprima 'Enter' para un arreglo o 'Ctrl+Enter' para una matriz.\n" +"

\n" +" Consejo:
\n" +" Use dos tabs al final de una fila para moverse a la siguiente.\n" +" " + +#: spyder/widgets/arraybuilder.py:365 +msgid "Array dimensions not valid" +msgstr "Las dimensiones del arreglo no son válidas" + +#: spyder/widgets/browser.py:54 spyder/widgets/sourcecode/codeeditor.py:2559 +msgid "Zoom out" +msgstr "Alejar" + +#: spyder/widgets/browser.py:57 spyder/widgets/sourcecode/codeeditor.py:2555 +msgid "Zoom in" +msgstr "Acercar" + +#: spyder/widgets/browser.py:177 +msgid "Home" +msgstr "Página de inicio" + +#: spyder/widgets/browser.py:213 +msgid "Find text" +msgstr "Encontrar texto" + +#: spyder/widgets/browser.py:231 +msgid "Address:" +msgstr "Dirección:" + +#: spyder/widgets/browser.py:267 +msgid "Unable to load page" +msgstr "No fue posible cargar la página" + +#: spyder/widgets/comboboxes.py:165 +msgid "Press enter to validate this entry" +msgstr "Presione Enter para validar esta entrada" + +#: spyder/widgets/comboboxes.py:166 +msgid "This entry is incorrect" +msgstr "Esta entrada es incorrecta" + +#: spyder/widgets/comboboxes.py:209 +msgid "Press enter to validate this path" +msgstr "Presione Enter para validar esta ruta" + +#: spyder/widgets/dependencies.py:63 +msgid " Required " +msgstr "Requerido" + +#: spyder/widgets/dependencies.py:63 +msgid "Module" +msgstr "Módulo" + +#: spyder/widgets/dependencies.py:64 +msgid " Installed " +msgstr "Instalado" + +#: spyder/widgets/dependencies.py:64 +msgid "Provided features" +msgstr "Características proporcionadas" + +#: spyder/widgets/dependencies.py:134 +msgid "Dependencies" +msgstr "Dependencias" + +#: spyder/widgets/dependencies.py:141 +msgid "" +"Spyder depends on several Python modules to provide the right functionality " +"for all its panes. The table below shows the required and installed versions " +"(if any) of all of them.

Note: You can safely use Spyder " +"without the following modules installed: %s and %s." +"

Please also note that new dependencies or changed ones will be " +"correctly detected only after Spyder is restarted." +msgstr "" +"Spyder depende de varios módulos de Python para proveer la funcionalidad " +"correcta para todos sus paneles. La tabla que aparece a continuación muestra " +"las versiones requeridas e instaladas (de existir) de todos ellos." +"

Nota: Usted puede utilizar Spyder sin problemas si no cuenta " +"con los siguientes módulos instalados: %s y %s.

Por " +"favor también tenga en cuenta que dependencias nuevas o que hayan sido " +"cambiadas en esta lista, sólo serán detectadas correctamente después de que " +"Spyder sea reiniciado." + +#: spyder/widgets/dependencies.py:157 +msgid "Copy to clipboard" +msgstr "Copiar al portapapeles" + +#: spyder/widgets/editor.py:350 +msgid "Copy path to clipboard" +msgstr "Copiar la ruta al portapapeles" + +#: spyder/widgets/editor.py:354 +msgid "Close all to the right" +msgstr "Cerrar todos a la derecha" + +#: spyder/widgets/editor.py:356 +msgid "Close all but this" +msgstr "Cerrar todos menos éste" + +#: spyder/widgets/editor.py:959 +msgid "Temporary file" +msgstr "Archivo temporal" + +#: spyder/widgets/editor.py:1054 +msgid "New window" +msgstr "Nueva ventana" + +#: spyder/widgets/editor.py:1055 +msgid "Create a new editor window" +msgstr "Crear una nueva ventana de edición" + +#: spyder/widgets/editor.py:1058 +msgid "Split vertically" +msgstr "Dividir verticalmente" + +#: spyder/widgets/editor.py:1060 +msgid "Split vertically this editor window" +msgstr "Dividir verticalmente esta panel o ventana de edición" + +#: spyder/widgets/editor.py:1062 +msgid "Split horizontally" +msgstr "Dividir horizontalmente" + +#: spyder/widgets/editor.py:1064 +msgid "Split horizontally this editor window" +msgstr "Dividir horizontalmente esta ventana o panel de edición" + +#: spyder/widgets/editor.py:1066 +msgid "Close this panel" +msgstr "Cerrar este panel" + +#: spyder/widgets/editor.py:1223 +msgid "%s has been modified.
Do you want to save changes?" +msgstr "%s ha sido modificado.
¿Desea guardar los cambios?" + +#: spyder/widgets/editor.py:1285 +msgid "Save" +msgstr "Guardar" + +#: spyder/widgets/editor.py:1286 +msgid "Unable to save script '%s'

Error message:
%s" +msgstr "" +"No fue posible guardar el archivo '%s'

Mensaje de error:
%s" + +#: spyder/widgets/editor.py:1523 +msgid "" +"%s is unavailable (this file may have been removed, moved or renamed " +"outside Spyder).
Do you want to close it?" +msgstr "" +"%s no está disponible (el archivo puede haber sido eliminado, movido " +"o renombrado por fuera de Spyder).
¿Desea cerrarlo?" + +#: spyder/widgets/editor.py:1543 +msgid "" +"%s has been modified outside Spyder.
Do you want to reload it and " +"lose all your changes?" +msgstr "" +"%s fue modificado por fuera de Spyder.
¿Desea recargarlo y perder " +"todos sus cambios?" + +#: spyder/widgets/editor.py:1639 +msgid "" +"All changes to %s will be lost.
Do you want to revert file from " +"disk?" +msgstr "" +"Todos los cambios a %s se perderán.
Desea revertir el archivo del " +"disco?" + +#: spyder/widgets/editor.py:1779 +msgid "Loading %s..." +msgstr "Cargando %s..." + +#: spyder/widgets/editor.py:1789 +msgid "" +"%s contains mixed end-of-line characters.
Spyder will fix this " +"automatically." +msgstr "" +"%s contiene varios tipos de caracteres de fin de línea.
Spyder lo " +"arreglará automáticamente." + +#: spyder/widgets/editor.py:2171 +msgid "Close window" +msgstr "Cerrar ventana" + +#: spyder/widgets/editor.py:2173 +msgid "Close this window" +msgstr "Cierra esta ventana" + +#: spyder/widgets/editortools.py:94 spyder/widgets/editortools.py:130 +msgid "Line %s" +msgstr "Línea %s" + +#: spyder/widgets/editortools.py:99 +msgid "Class defined at line %s" +msgstr "Clase definida en la línea %s" + +#: spyder/widgets/editortools.py:107 +msgid "Method defined at line %s" +msgstr "Método definido en la línea %s" + +#: spyder/widgets/editortools.py:117 +msgid "Function defined at line %s" +msgstr "Función definida en la línea %s" + +#: spyder/widgets/editortools.py:149 +msgid "Cell starts at line %s" +msgstr "La celda empieza en la línea %s" + +#: spyder/widgets/editortools.py:202 spyder/widgets/editortools.py:539 +msgid "Go to cursor position" +msgstr "Ir a la posición del cursor" + +#: spyder/widgets/editortools.py:205 +msgid "Show absolute path" +msgstr "Mostrar la ruta completa" + +#: spyder/widgets/editortools.py:208 spyder/widgets/explorer.py:210 +msgid "Show all files" +msgstr "Mostrar todos los archivos" + +#: spyder/widgets/editortools.py:211 +msgid "Show special comments" +msgstr "Mostrar comentarios especiales" + +#: spyder/widgets/explorer.py:206 +msgid "Edit filename filters..." +msgstr "Editar filtros..." + +#: spyder/widgets/explorer.py:220 +msgid "Edit filename filters" +msgstr "Editar los filtros para nombres de archivo" + +#: spyder/widgets/explorer.py:221 +msgid "Name filters:" +msgstr "Nombres de los filtros:" + +#: spyder/widgets/explorer.py:240 +msgid "File..." +msgstr "Archivo..." + +#: spyder/widgets/explorer.py:244 +msgid "Module..." +msgstr "Módulo" + +#: spyder/widgets/explorer.py:248 +msgid "Folder..." +msgstr "Carpeta..." + +#: spyder/widgets/explorer.py:252 +msgid "Package..." +msgstr "Paquete..." + +#: spyder/widgets/explorer.py:273 +#: spyder/widgets/variableexplorer/collectionseditor.py:652 +msgid "Edit" +msgstr "Editar" + +#: spyder/widgets/explorer.py:275 +msgid "Move..." +msgstr "Mover a..." + +#: spyder/widgets/explorer.py:278 +msgid "Delete..." +msgstr "Eliminar..." + +#: spyder/widgets/explorer.py:281 +msgid "Rename..." +msgstr "Renombrar..." + +#: spyder/widgets/explorer.py:284 +msgid "Open" +msgstr "Abrir" + +#: spyder/widgets/explorer.py:285 spyder/widgets/sourcecode/codeeditor.py:2531 +msgid "Convert to Python script" +msgstr "Convertir a un archivo de Python" + +#: spyder/widgets/explorer.py:319 +msgid "Commit" +msgstr "Consignar" + +#: spyder/widgets/explorer.py:322 +msgid "Browse repository" +msgstr "Explorar repositorio " + +#: spyder/widgets/explorer.py:333 +msgid "Open command prompt here" +msgstr "Abrir símbolo del sistema aquí" + +#: spyder/widgets/explorer.py:335 +msgid "Open terminal here" +msgstr "Abrir terminal del sistema aquí" + +#: spyder/widgets/explorer.py:340 +msgid "Open Python console here" +msgstr "Abrir una terminal de Python aquí" + +#: spyder/widgets/explorer.py:354 +msgid "New" +msgstr "Crear nuevo" + +#: spyder/widgets/explorer.py:362 +msgid "Import" +msgstr "Importar" + +#: spyder/widgets/explorer.py:513 +msgid "Do you really want to delete %s?" +msgstr "¿Realmente desea eliminar %s?" + +#: spyder/widgets/explorer.py:531 +msgid "delete" +msgstr "eliminar" + +#: spyder/widgets/explorer.py:532 spyder/widgets/projects/explorer.py:149 +#: spyder/widgets/projects/explorer.py:256 +msgid "Project Explorer" +msgstr "Explorador de proyectos" + +#: spyder/widgets/explorer.py:533 spyder/widgets/projects/explorer.py:150 +msgid "Unable to %s %s

Error message:
%s" +msgstr "No fue posible %s %s

Mensaje de error:
%s" + +#: spyder/widgets/explorer.py:548 +msgid "File Explorer" +msgstr "Explorador de archivos" + +#: spyder/widgets/explorer.py:549 +msgid "" +"The current directory contains a project.

If you want to delete the " +"project, please go to Projects » Delete Project" +msgstr "" +"El directorio actual contiene un proyecto.

Si desea eliminar este " +"proyecto, por favor diríjase a Proyectos » Eliminar " +"proyecto" + +#: spyder/widgets/explorer.py:566 spyder/widgets/sourcecode/codeeditor.py:2018 +msgid "Conversion error" +msgstr "Error de conversión" + +#: spyder/widgets/explorer.py:567 spyder/widgets/sourcecode/codeeditor.py:2019 +msgid "" +"It was not possible to convert this notebook. The error is:\n" +"\n" +msgstr "" +"No fue posible convertir este notebook. El error es:\n" +"\n" + +#: spyder/widgets/explorer.py:584 spyder/widgets/explorer.py:592 +#: spyder/widgets/explorer.py:603 +#: spyder/widgets/variableexplorer/collectionseditor.py:681 +#: spyder/widgets/variableexplorer/collectionseditor.py:916 +msgid "Rename" +msgstr "Renombrar" + +#: spyder/widgets/explorer.py:585 +msgid "New name:" +msgstr "Nuevo nombre:" + +#: spyder/widgets/explorer.py:593 +msgid "" +"Do you really want to rename %s and overwrite the existing file " +"%s?" +msgstr "" +"¿Realmente desea renombrar %s y sobrescribir el archivo existente " +"%s?" + +#: spyder/widgets/explorer.py:604 +msgid "Unable to rename file %s

Error message:
%s" +msgstr "" +"No fue posible renombrar el archivo %s

Mensaje de error:" +"
%s" + +#: spyder/widgets/explorer.py:640 +msgid "Unable to move %s

Error message:
%s" +msgstr "No fue posible mover %s

Mensaje de error:
%s" + +#: spyder/widgets/explorer.py:658 +msgid "Unable to create folder %s

Error message:
%s" +msgstr "" +"No fue posible crear la carpeta %s

Mensaje de error:
" +"%s" + +#: spyder/widgets/explorer.py:671 spyder/widgets/explorer.py:705 +msgid "Unable to create file %s

Error message:
%s" +msgstr "" +"No fue posible crear el archivo %s

Mensaje de error:
" +"%s" + +#: spyder/widgets/explorer.py:679 +msgid "New folder" +msgstr "Nueva carpeta" + +#: spyder/widgets/explorer.py:680 +msgid "Folder name:" +msgstr "Nombre de la carpeta:" + +#: spyder/widgets/explorer.py:685 +msgid "New package" +msgstr "Nuevo paquete" + +#: spyder/widgets/explorer.py:686 +msgid "Package name:" +msgstr "Nombre del paquete:" + +#: spyder/widgets/explorer.py:726 +msgid "New module" +msgstr "Nuevo módulo" + +#: spyder/widgets/explorer.py:741 +msgid "" +"For %s support, please install one of the
following tools:

%s" +msgstr "" +"Para contar con soporte de %s, por favor instale una de
las siguientes " +"herramientas:

%s" + +#: spyder/widgets/explorer.py:745 +msgid "Unable to find external program.

%s" +msgstr "No fue posible encontrar el programa externo.

%s" + +#: spyder/widgets/explorer.py:966 +msgid "Show current directory only" +msgstr "Mostrar sólo el directorio actual" + +#: spyder/widgets/explorer.py:1066 +msgid "You don't have the right permissions to open this directory" +msgstr "No tiene con permisos para abrir este directorio" + +#: spyder/widgets/explorer.py:1096 +#: spyder/widgets/variableexplorer/importwizard.py:525 +msgid "Previous" +msgstr "Anterior" + +#: spyder/widgets/explorer.py:1102 +msgid "Parent" +msgstr "Directorio superior" + +#: spyder/widgets/externalshell/baseshell.py:129 +msgid "Run again this program" +msgstr "Ejecutar de nuevo este programa" + +#: spyder/widgets/externalshell/baseshell.py:132 +msgid "Kill" +msgstr "Terminar" + +#: spyder/widgets/externalshell/baseshell.py:134 +msgid "Kills the current process, causing it to exit immediately" +msgstr "" +"Termina el proceso actual, provocando\n" +"que culmine inmediatamente" + +#: spyder/widgets/externalshell/baseshell.py:206 +msgid "Running..." +msgstr "Corriendo..." + +#: spyder/widgets/externalshell/baseshell.py:213 +msgid "Terminated." +msgstr "Terminado." + +#: spyder/widgets/externalshell/baseshell.py:238 +#: spyder/widgets/ipythonconsole/help.py:125 spyder/widgets/mixins.py:661 +msgid "Arguments" +msgstr "Argumentos" + +#: spyder/widgets/externalshell/baseshell.py:239 +msgid "Command line arguments:" +msgstr "Argumentos de la línea de comandos:" + +#: spyder/widgets/externalshell/pythonshell.py:277 +msgid "Variables" +msgstr "Variables" + +#: spyder/widgets/externalshell/pythonshell.py:278 +msgid "Show/hide global variables explorer" +msgstr "" +"Mostrar u ocultar el\n" +"explorador de variables" + +#: spyder/widgets/externalshell/pythonshell.py:282 +msgid "Terminate" +msgstr "Interrumpir" + +#: spyder/widgets/externalshell/pythonshell.py:283 +msgid "" +"Attempts to stop the process. The process\n" +"may not exit as a result of clicking this\n" +"button (it is given the chance to prompt\n" +"the user for any unsaved files, etc)." +msgstr "" +"Intenta interrumpir este proceso, pero éste\n" +"puede no concluir tras oprimir este botón\n" +"(pues es posible que se le pida al usuario\n" +"guardar archivos sin salvar, etc)." + +#: spyder/widgets/externalshell/pythonshell.py:296 +msgid "Interact" +msgstr "Interactuar" + +#: spyder/widgets/externalshell/pythonshell.py:298 +msgid "Debug" +msgstr "Depurar" + +#: spyder/widgets/externalshell/pythonshell.py:300 +#: spyder/widgets/externalshell/pythonshell.py:366 +msgid "Arguments..." +msgstr "Argumentos..." + +#: spyder/widgets/externalshell/pythonshell.py:302 +msgid "Post Mortem Debug" +msgstr "Depuración Post Mortem" + +#: spyder/widgets/externalshell/pythonshell.py:308 +msgid "Working directory" +msgstr "Directorio de trabajo" + +#: spyder/widgets/externalshell/pythonshell.py:310 +msgid "Set current working directory" +msgstr "Establece el directorio de trabajo" + +#: spyder/widgets/externalshell/pythonshell.py:312 +msgid "Environment variables" +msgstr "Variables de entorno" + +#: spyder/widgets/externalshell/pythonshell.py:316 +msgid "Show sys.path contents" +msgstr "Contenidos del sys.path" + +#: spyder/widgets/externalshell/pythonshell.py:362 +msgid "Arguments: %s" +msgstr "Argumentos: %s" + +#: spyder/widgets/externalshell/pythonshell.py:364 +msgid "No argument" +msgstr "Sin argumentos" + +#: spyder/widgets/externalshell/pythonshell.py:534 +msgid "A Python console failed to start!" +msgstr "No se pudo iniciar una terminal de Python!" + +#: spyder/widgets/externalshell/systemshell.py:106 +msgid "Process failed to start" +msgstr "El proceso falló al empezar" + +#: spyder/widgets/fileswitcher.py:109 +msgid "unsaved file" +msgstr "Archivo sin guardar" + +#: spyder/widgets/fileswitcher.py:230 +msgid "" +"Press Enter to switch files or Esc to cancel.

Type to " +"filter filenames.

Use :number to go to a line, e.g. " +"main:42
Use @symbol_text to go to a symbol, e." +"g. @init

Press Ctrl+W to close current " +"tab.
" +msgstr "" +"Presione Enter para cambiar entre archivos o Esc para cancelar." +"

Escriba un texto para filtrar los nombres de archivos.

Use " +":número para ir a una línea dada, e.g. main:42
Use @símbolo para ir a un símbolo, e.g. @init

Presione Ctrl+W para cerrar la pestaña actual.
" + +#: spyder/widgets/fileswitcher.py:508 +msgid "lines" +msgstr "líneas" + +#: spyder/widgets/findinfiles.py:158 +msgid "Unexpected error: see internal console" +msgstr "Error inesperado. Por favor vea la terminal interna." + +#: spyder/widgets/findinfiles.py:209 spyder/widgets/findinfiles.py:233 +#: spyder/widgets/findinfiles.py:280 +msgid "invalid regular expression" +msgstr "expresión regular inválida" + +#: spyder/widgets/findinfiles.py:278 +msgid "permission denied errors were encountered" +msgstr "permiso denegado, se encontraron errores" + +#: spyder/widgets/findinfiles.py:315 +msgid "Search pattern" +msgstr "Patrón de búsqueda" + +#: spyder/widgets/findinfiles.py:318 spyder/widgets/findinfiles.py:352 +#: spyder/widgets/findinfiles.py:364 spyder/widgets/findreplace.py:81 +msgid "Regular expression" +msgstr "Expresión regular" + +#: spyder/widgets/findinfiles.py:327 +msgid "Search" +msgstr "Buscar" + +#: spyder/widgets/findinfiles.py:330 +msgid "Start search" +msgstr "Comenzar la búsqueda" + +#: spyder/widgets/findinfiles.py:336 +msgid "Stop search" +msgstr "Detener la búsqueda" + +#: spyder/widgets/findinfiles.py:346 +msgid "Included filenames pattern" +msgstr "" +"Patrones de nombres de\n" +"archivo a incluir" + +#: spyder/widgets/findinfiles.py:355 +msgid "Include:" +msgstr "Incluir:" + +#: spyder/widgets/findinfiles.py:358 +msgid "Excluded filenames pattern" +msgstr "" +"Patrones de nombres de\n" +"archivo a excluir" + +#: spyder/widgets/findinfiles.py:367 +msgid "Exclude:" +msgstr "Excluir:" + +#: spyder/widgets/findinfiles.py:377 +msgid "PYTHONPATH" +msgstr "PYTHONPATH" + +#: spyder/widgets/findinfiles.py:379 +msgid "" +"Search in all directories listed in sys.path which are outside the Python " +"installation directory" +msgstr "" +"Buscar en todos los directorios del\n" +"sys.path que están por fuera del\n" +"directorio de instalación de Python" + +#: spyder/widgets/findinfiles.py:382 +msgid "Hg repository" +msgstr "Repositorio Hg" + +#: spyder/widgets/findinfiles.py:385 +msgid "Search in current directory hg repository" +msgstr "" +"Buscar en el repositorio\n" +"actual de Mercurial" + +#: spyder/widgets/findinfiles.py:386 +msgid "Here:" +msgstr "Aquí" + +#: spyder/widgets/findinfiles.py:390 +msgid "Search recursively in this directory" +msgstr "" +"Buscar recursivamente\n" +"en este directorio" + +#: spyder/widgets/findinfiles.py:395 +msgid "Browse a search directory" +msgstr "" +"Seleccionar el directorio\n" +"de búsqueda" + +#: spyder/widgets/findinfiles.py:425 +msgid "Hide advanced options" +msgstr "Ocultar opciones avanzadas" + +#: spyder/widgets/findinfiles.py:428 +msgid "Show advanced options" +msgstr "Mostrar opciones avanzadas" + +#: spyder/widgets/findinfiles.py:569 +msgid "Search canceled" +msgstr "Búsqueda cancelada" + +#: spyder/widgets/findinfiles.py:573 +msgid "String not found" +msgstr "Texto no encontrado" + +#: spyder/widgets/findinfiles.py:575 +msgid "matches in" +msgstr "coincidencias en" + +#: spyder/widgets/findinfiles.py:576 +msgid "file" +msgstr "archivo" + +#: spyder/widgets/findinfiles.py:584 +msgid "interrupted" +msgstr "interrumpido" + +#: spyder/widgets/findreplace.py:63 +msgid "Search string" +msgstr "Buscar texto" + +#: spyder/widgets/findreplace.py:87 +msgid "Case Sensitive" +msgstr "" +"Distinguir mayúsculas\n" +"de minúsculas" + +#: spyder/widgets/findreplace.py:93 +msgid "Whole words" +msgstr "" +"Solamente palabras\n" +"completas" + +#: spyder/widgets/findreplace.py:99 +msgid "Highlight matches" +msgstr "Resaltar coincidencias" + +#: spyder/widgets/findreplace.py:113 +msgid "Replace with:" +msgstr "Reemplazar con:" + +#: spyder/widgets/findreplace.py:115 +msgid "Replace string" +msgstr "Reemplazar texto" + +#: spyder/widgets/findreplace.py:118 +msgid "Replace/find" +msgstr "Buscar/reemplazar" + +#: spyder/widgets/findreplace.py:125 +msgid "Replace all" +msgstr "Reemplazar todo" + +#: spyder/widgets/internalshell.py:262 +msgid "Help..." +msgstr "Ayuda..." + +#: spyder/widgets/internalshell.py:279 +msgid "Shell special commands:" +msgstr "Comandos especiales:" + +#: spyder/widgets/internalshell.py:280 +msgid "Internal editor:" +msgstr "Editor interno:" + +#: spyder/widgets/internalshell.py:281 +msgid "External editor:" +msgstr "Editor externo:" + +#: spyder/widgets/internalshell.py:282 +msgid "Run script:" +msgstr "Ejecutar un archivo:" + +#: spyder/widgets/internalshell.py:283 +msgid "Remove references:" +msgstr "Eliminar referencias:" + +#: spyder/widgets/internalshell.py:284 +msgid "System commands:" +msgstr "Comandos del sistema:" + +#: spyder/widgets/internalshell.py:285 +msgid "Python help:" +msgstr "Ayuda de Python:" + +#: spyder/widgets/internalshell.py:286 +msgid "GUI-based editor:" +msgstr "Editor gráfico:" + +#: spyder/widgets/ipythonconsole/client.py:189 +msgid "An error ocurred while starting the kernel" +msgstr "Ocurrió un error mientras iniciaba el núcleo" + +#: spyder/widgets/ipythonconsole/client.py:220 +msgid "Restart kernel" +msgstr "Reiniciar el núcleo" + +#: spyder/widgets/ipythonconsole/client.py:240 +msgid "Stop the current command" +msgstr "Detener el comando actual" + +#: spyder/widgets/ipythonconsole/client.py:263 +msgid "Inspect current object" +msgstr "Inspeccionar objeto" + +#: spyder/widgets/ipythonconsole/client.py:268 +msgid "Clear line or block" +msgstr "Limpiar línea o bloque" + +#: spyder/widgets/ipythonconsole/client.py:272 +msgid "Reset namespace" +msgstr "Restaurar el namespace" + +#: spyder/widgets/ipythonconsole/client.py:275 +msgid "Clear console" +msgstr "Limpiar la terminal" + +#: spyder/widgets/ipythonconsole/client.py:316 +msgid "Are you sure you want to restart the kernel?" +msgstr "Está seguro de que desea reiniciar el núcleo?" + +#: spyder/widgets/ipythonconsole/client.py:318 +msgid "Restart kernel?" +msgstr "Reiniciar el núcleo?" + +#: spyder/widgets/ipythonconsole/client.py:327 +msgid "Error restarting kernel: %s\n" +msgstr "Ocurrió un error al reiniciar el núcleo: %s\n" + +#: spyder/widgets/ipythonconsole/client.py:331 +msgid "" +"
Restarting kernel...\n" +"

" +msgstr "" +"
Reiniciando el núcleo...\n" +"

" + +#: spyder/widgets/ipythonconsole/client.py:336 +msgid "Cannot restart a kernel not started by Spyder\n" +msgstr "No es posible reiniciar un núcleo que no fue iniciado por Spyder\n" + +#: spyder/widgets/ipythonconsole/client.py:376 +msgid "Changing backend to Qt for Mayavi" +msgstr "Cambiando la salida gráfica a Qt por Mayavi" + +#: spyder/widgets/ipythonconsole/client.py:387 +msgid "Connecting to kernel..." +msgstr "Conectándose al núcleo..." + +#: spyder/widgets/ipythonconsole/namespacebrowser.py:76 +msgid "" +"Inspecting and setting values while debugging in IPython consoles is not " +"supported yet by Spyder." +msgstr "" +"Spyder aún no soporta inspeccionar y establecer valores mientras se está " +"depurando." + +#: spyder/widgets/ipythonconsole/shell.py:149 +msgid "Reset IPython namespace" +msgstr "Reiniciar el espacio de trabajo de IPython" + +#: spyder/widgets/ipythonconsole/shell.py:150 +msgid "" +"All user-defined variables will be removed.
Are you sure you want to " +"reset the namespace?" +msgstr "" +"Todas las variables definidas por el usuario serán eliminadas. ¿Está seguro " +"de que desea restaurar el namespace?" + +#: spyder/widgets/onecolumntree.py:52 +msgid "Collapse all" +msgstr "Colapsar resultados" + +#: spyder/widgets/onecolumntree.py:56 +msgid "Expand all" +msgstr "Expandir resultados" + +#: spyder/widgets/onecolumntree.py:60 +msgid "Restore" +msgstr "Restaurar" + +#: spyder/widgets/onecolumntree.py:61 +msgid "Restore original tree layout" +msgstr "Restaurar la disposición original" + +#: spyder/widgets/onecolumntree.py:65 +msgid "Collapse selection" +msgstr "Colapsar selección" + +#: spyder/widgets/onecolumntree.py:69 +msgid "Expand selection" +msgstr "Expandir selección" + +#: spyder/widgets/pathmanager.py:87 +msgid "Move to top" +msgstr "Mover al principio" + +#: spyder/widgets/pathmanager.py:93 +msgid "Move up" +msgstr "Mover arriba" + +#: spyder/widgets/pathmanager.py:99 +msgid "Move down" +msgstr "Mover abajo" + +#: spyder/widgets/pathmanager.py:105 +msgid "Move to bottom" +msgstr "Mover al final" + +#: spyder/widgets/pathmanager.py:116 spyder/widgets/pathmanager.py:231 +msgid "Add path" +msgstr "Añadir ruta" + +#: spyder/widgets/pathmanager.py:121 spyder/widgets/pathmanager.py:214 +msgid "Remove path" +msgstr "Eliminar ruta" + +#: spyder/widgets/pathmanager.py:131 +msgid "Synchronize..." +msgstr "Sincronizar..." + +#: spyder/widgets/pathmanager.py:133 +msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" +msgstr "" +"Sincronizar la lista de rutas de Spyder con la variable\n" +"de entorno PYTHONPATH" + +#: spyder/widgets/pathmanager.py:145 +msgid "Synchronize" +msgstr "Sincronizar" + +#: spyder/widgets/pathmanager.py:146 +msgid "" +"This will synchronize Spyder's path list with PYTHONPATH environment " +"variable for current user, allowing you to run your Python modules outside " +"Spyder without having to configure sys.path.
Do you want to clear " +"contents of PYTHONPATH before adding Spyder's path list?" +msgstr "" +"Esta acción sincronizará la lista de rutas de Spyder con la variable de " +"entorno PYTHONPATH para el usuario actual, permitiéndole ejecutar sus " +"módulos de Python por fuera de Spyder sin tener que configurar sys.path." +"
¿Desea borrar los contenidos del PYTHONPATH antes de añadir la lista de " +"rutas de Spyder?" + +#: spyder/widgets/pathmanager.py:215 +msgid "Do you really want to remove selected path?" +msgstr "¿Realmente desea eliminar la ruta seleccionada?" + +#: spyder/widgets/pathmanager.py:232 +msgid "" +"This directory is already included in Spyder path list.
Do you want to " +"move it to the top of the list?" +msgstr "" +"Este directorio ya está incluido en la lista de rutas de Spyder.
¿Desea " +"moverlo al principio de la lista?" + +#: spyder/widgets/projects/configdialog.py:30 +msgid "Project preferences" +msgstr "Preferencias del proyecto" + +#: spyder/widgets/projects/configdialog.py:82 +#: spyder/widgets/projects/configdialog.py:119 +msgid "Restore data on startup" +msgstr "Restaurar datos al inicio" + +#: spyder/widgets/projects/configdialog.py:84 +#: spyder/widgets/projects/configdialog.py:121 +msgid "Save data on exit" +msgstr "Guardar datos al salir" + +#: spyder/widgets/projects/configdialog.py:86 +#: spyder/widgets/projects/configdialog.py:123 +msgid "Save history" +msgstr "Guardar el historial" + +#: spyder/widgets/projects/configdialog.py:88 +#: spyder/widgets/projects/configdialog.py:125 +msgid "Save non project files opened" +msgstr "Guardar archivos abiertos que no pertenecen al proyecto" + +#: spyder/widgets/projects/configdialog.py:111 +msgid "Code" +msgstr "Código" + +#: spyder/widgets/projects/configdialog.py:118 +msgid "Workspace" +msgstr "Espacio de trabajo" + +#: spyder/widgets/projects/configdialog.py:148 +#: spyder/widgets/projects/configdialog.py:155 +msgid "Version control" +msgstr "Control de versiones" + +#: spyder/widgets/projects/configdialog.py:156 +msgid "Use version control" +msgstr "Usar control de versiones" + +#: spyder/widgets/projects/configdialog.py:161 +msgid "Version control system" +msgstr "Sistema de versión de controles" + +#: spyder/widgets/projects/explorer.py:52 +msgid "Show horizontal scrollbar" +msgstr "Mostrar una barra de desplazamiento horizontal" + +#: spyder/widgets/projects/explorer.py:114 +msgid "File %s already exists.
Do you want to overwrite it?" +msgstr "El archivo %s ya existe.
¿Desea sobrescribirlo?" + +#: spyder/widgets/projects/explorer.py:128 +msgid "Folder %s already exists." +msgstr "La carpeta %s ya existe." + +#: spyder/widgets/projects/explorer.py:146 +msgid "copy" +msgstr "copiar" + +#: spyder/widgets/projects/explorer.py:148 +msgid "move" +msgstr "mover" + +#: spyder/widgets/projects/explorer.py:244 +msgid "" +"Do you really want to delete {filename}?

Note: This " +"action will only delete the project. Its files are going to be preserved on " +"disk." +msgstr "" +"¿Realmente desea eliminar el proyecto {filename}?

Nota: " +"Esta acción sólo eliminará el proyecto. Los archivos del mismo no serán " +"eliminados del disco." + +#: spyder/widgets/projects/explorer.py:257 +msgid "" +"Unable to delete {varpath}

The error message was:" +"
{error}" +msgstr "" +"No fue posible eliminar {varpath}

El mensaje de error " +"fue:
{error}" + +#: spyder/widgets/projects/projectdialog.py:69 +msgid "New directory" +msgstr "Nuevo directorio" + +#: spyder/widgets/projects/projectdialog.py:70 +msgid "Existing directory" +msgstr "Un directorio existente" + +#: spyder/widgets/projects/projectdialog.py:72 +msgid "Project name" +msgstr "Nombre del proyecto" + +#: spyder/widgets/projects/projectdialog.py:73 +msgid "Location" +msgstr "Ubicación" + +#: spyder/widgets/projects/projectdialog.py:74 +msgid "Project type" +msgstr "Tipo del proyecto" + +#: spyder/widgets/projects/projectdialog.py:75 +msgid "Python version" +msgstr "Versión de Python" + +#: spyder/widgets/projects/projectdialog.py:83 +#: spyder/widgets/variableexplorer/importwizard.py:519 +msgid "Cancel" +msgstr "Cancelar" + +#: spyder/widgets/projects/projectdialog.py:84 +msgid "Create" +msgstr "Crear" + +#: spyder/widgets/projects/projectdialog.py:102 +msgid "Create new project" +msgstr "Crear nuevo proyecto" + +#: spyder/widgets/projects/type/__init__.py:215 +msgid "Empty project" +msgstr "Proyecto vacío" + +#: spyder/widgets/projects/type/python.py:20 +msgid "Python project" +msgstr "Proyecto de Python" + +#: spyder/widgets/projects/type/python.py:76 +msgid "Python package" +msgstr "Paquete de Python" + +#: spyder/widgets/pydocgui.py:110 +msgid "Module or package:" +msgstr "Módulo o paquete:" + +#: spyder/widgets/shell.py:129 +msgid "Save history log..." +msgstr "Guardar el historial..." + +#: spyder/widgets/shell.py:131 +msgid "Save current history log (i.e. all inputs and outputs) in a text file" +msgstr "" +"Guardar el historial actual (es decir\n" +"todas las entradas y salidas) en un\n" +"archivo de texto" + +#: spyder/widgets/shell.py:257 +msgid "Save history log" +msgstr "Guardar el historial" + +#: spyder/widgets/shell.py:260 +msgid "History logs" +msgstr "Historiales" + +#: spyder/widgets/shell.py:271 +msgid "Unable to save file '%s'

Error message:
%s" +msgstr "" +"No fue posible guardar el archivo '%s'

Mensaje de error:
%s" + +#: spyder/widgets/shell.py:713 +msgid "Copy without prompts" +msgstr "Copiar sin los prompts" + +#: spyder/widgets/shell.py:716 spyder/widgets/shell.py:720 +msgid "Clear line" +msgstr "Limpiar línea" + +#: spyder/widgets/shell.py:722 +msgid "Clear shell" +msgstr "Limpiar la terminal" + +#: spyder/widgets/shell.py:726 +msgid "Clear shell contents ('cls' command)" +msgstr "Limpia los contenidos de la terminal (equivalente al comando 'cls')" + +#: spyder/widgets/sourcecode/codeeditor.py:100 +msgid "Go to line:" +msgstr "Ir a la línea" + +#: spyder/widgets/sourcecode/codeeditor.py:108 +msgid "Line count:" +msgstr "Número total de líneas:" + +#: spyder/widgets/sourcecode/codeeditor.py:1305 +msgid "Breakpoint" +msgstr "Punto de interrupción" + +#: spyder/widgets/sourcecode/codeeditor.py:1306 +msgid "Condition:" +msgstr "Condición:" + +#: spyder/widgets/sourcecode/codeeditor.py:1712 +msgid "Code analysis" +msgstr "Análisis del código" + +#: spyder/widgets/sourcecode/codeeditor.py:1766 +msgid "To do" +msgstr "To do" + +#: spyder/widgets/sourcecode/codeeditor.py:2005 +msgid "Removal error" +msgstr "Error de remoción" + +#: spyder/widgets/sourcecode/codeeditor.py:2006 +msgid "" +"It was not possible to remove outputs from this notebook. The error is:\n" +"\n" +msgstr "" +"No fue posible remover las outputs de este notebook. El error es:\n" +"\n" + +#: spyder/widgets/sourcecode/codeeditor.py:2528 +msgid "Clear all ouput" +msgstr "Eliminar todas las salidas" + +#: spyder/widgets/sourcecode/codeeditor.py:2534 +msgid "Go to definition" +msgstr "Ir a la definición" + +#: spyder/widgets/sourcecode/codeeditor.py:2563 +msgid "Zoom reset" +msgstr "Restaurar" + +#: spyder/widgets/status.py:25 +msgid "CPU and memory usage info in the status bar" +msgstr "Uso de memoria y CPU en la barra de estado" + +#: spyder/widgets/status.py:94 +msgid "Memory:" +msgstr "Memoria:" + +#: spyder/widgets/status.py:95 +msgid "" +"Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows " +"platforms" +msgstr "" +"Para reportar el uso de memoria se requiere de\n" +"la librería `psutil` (>=0.3) en plataformas distintas\n" +"a Windows" + +#: spyder/widgets/status.py:107 +msgid "CPU:" +msgstr "CPU:" + +#: spyder/widgets/status.py:108 +msgid "CPU usage status: requires the `psutil` (>=v0.3) library" +msgstr "" +"Para reportar el estado del CPU se requiere\n" +"de la librería `psutil` (>=v0.3)" + +#: spyder/widgets/status.py:130 +msgid "Permissions:" +msgstr "Permisos:" + +#: spyder/widgets/status.py:144 +msgid "End-of-lines:" +msgstr "Fin de línea:" + +#: spyder/widgets/status.py:158 +msgid "Encoding:" +msgstr "Codificación:" + +#: spyder/widgets/status.py:171 +msgid "Line:" +msgstr "Línea:" + +#: spyder/widgets/status.py:175 +msgid "Column:" +msgstr "Columna:" + +#: spyder/widgets/tabs.py:146 +msgid "Browse tabs" +msgstr "" +"Navegar por\n" +"las pestañas" + +#: spyder/widgets/tabs.py:275 +msgid "Close current tab" +msgstr "Cerrar pestaña actual" + +#: spyder/widgets/variableexplorer/arrayeditor.py:497 +msgid "It was not possible to copy values for this array" +msgstr "No fue posible copiar valores para este arreglo" + +#: spyder/widgets/variableexplorer/arrayeditor.py:532 +#: spyder/widgets/variableexplorer/arrayeditor.py:565 +#: spyder/widgets/variableexplorer/dataframeeditor.py:544 +#: spyder/widgets/variableexplorer/dataframeeditor.py:584 +msgid "Format" +msgstr "Formato" + +#: spyder/widgets/variableexplorer/arrayeditor.py:537 +#: spyder/widgets/variableexplorer/dataframeeditor.py:548 +msgid "Resize" +msgstr "Redimensionar" + +#: spyder/widgets/variableexplorer/arrayeditor.py:566 +#: spyder/widgets/variableexplorer/dataframeeditor.py:585 +msgid "Float formatting" +msgstr "Formato de punto flotante" + +#: spyder/widgets/variableexplorer/arrayeditor.py:574 +#: spyder/widgets/variableexplorer/dataframeeditor.py:594 +msgid "Format (%s) is incorrect" +msgstr "El formato (%s) es incorrecto" + +#: spyder/widgets/variableexplorer/arrayeditor.py:609 +msgid "Array is empty" +msgstr "El arreglo está vacío" + +#: spyder/widgets/variableexplorer/arrayeditor.py:612 +msgid "Arrays with more than 3 dimensions are not supported" +msgstr "Los arreglos de más de tres dimensiones no están soportados" + +#: spyder/widgets/variableexplorer/arrayeditor.py:615 +msgid "The 'xlabels' argument length do no match array column number" +msgstr "" +"El argumento de longitud 'xlabels' no coincide con el número de columnas del " +"arreglo" + +#: spyder/widgets/variableexplorer/arrayeditor.py:619 +msgid "The 'ylabels' argument length do no match array row number" +msgstr "" +"El argumento de longitud 'ylabels' no coincide con el número de filas del " +"arreglo" + +#: spyder/widgets/variableexplorer/arrayeditor.py:626 +msgid "%s arrays" +msgstr "Los arreglos %s" + +#: spyder/widgets/variableexplorer/arrayeditor.py:627 +msgid "%s are currently not supported" +msgstr "%s no están soportados por el momento" + +#: spyder/widgets/variableexplorer/arrayeditor.py:634 +msgid "NumPy array" +msgstr "Arreglo de NumPy" + +#: spyder/widgets/variableexplorer/arrayeditor.py:636 +#: spyder/widgets/variableexplorer/arrayeditor.py:790 +msgid "Array editor" +msgstr "Editor de arreglos" + +#: spyder/widgets/variableexplorer/arrayeditor.py:638 +msgid "read only" +msgstr "sólo lectura" + +#: spyder/widgets/variableexplorer/arrayeditor.py:668 +msgid "Record array fields:" +msgstr "Campos del arreglo de records:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Data" +msgstr "Datos" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Mask" +msgstr "Máscara" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Masked data" +msgstr "Datos enmascarados" + +#: spyder/widgets/variableexplorer/arrayeditor.py:691 +msgid "Axis:" +msgstr "Eje:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:696 +msgid "Index:" +msgstr "Índice:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:709 +msgid "Warning: changes are applied separately" +msgstr "Advertencia: los cambios son aplicados de forma separada" + +#: spyder/widgets/variableexplorer/arrayeditor.py:710 +msgid "" +"For performance reasons, changes applied to masked array won't be reflected " +"in array's data (and vice-versa)." +msgstr "" +"Por razones de rendimiento, los cambios\n" +"aplicados a arreglos enmascarados no se\n" +"verán reflejados en los datos del arreglo\n" +"(y viceversa)." + +#: spyder/widgets/variableexplorer/collectionseditor.py:116 +msgid "Index" +msgstr "Índice" + +#: spyder/widgets/variableexplorer/collectionseditor.py:121 +msgid "Tuple" +msgstr "Tupla" + +#: spyder/widgets/variableexplorer/collectionseditor.py:124 +msgid "List" +msgstr "Lista" + +#: spyder/widgets/variableexplorer/collectionseditor.py:127 +msgid "Dictionary" +msgstr "Diccionario" + +#: spyder/widgets/variableexplorer/collectionseditor.py:129 +msgid "Key" +msgstr "Clave/Tecla" + +#: spyder/widgets/variableexplorer/collectionseditor.py:135 +msgid "Attribute" +msgstr "Atributo" + +#: spyder/widgets/variableexplorer/collectionseditor.py:137 +msgid "elements" +msgstr "elementos" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Size" +msgstr "Tamaño" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Type" +msgstr "Tipo" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Value" +msgstr "Valor" + +#: spyder/widgets/variableexplorer/collectionseditor.py:409 +msgid "" +"Opening this variable can be slow\n" +"\n" +"Do you want to continue anyway?" +msgstr "" +"Abrir e inspeccionar esta variable puede tomar mucho tiempo\n" +"\n" +"¿Desea continuar de todas formas?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:418 +msgid "" +"Spyder was unable to retrieve the value of this variable from the console." +"

The error mesage was:
%s" +msgstr "" +"Spyder no fue capaz de extraer el valor de esta variable de la terminal." +"

El mensaje de error fue:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:583 +msgid "Edit item" +msgstr "Editar ítem" + +#: spyder/widgets/variableexplorer/collectionseditor.py:584 +msgid "Unable to assign data to item.

Error message:
%s" +msgstr "" +"No fue posible asignarle los datos al ítem.

Mensaje de error:" +"
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:644 +msgid "Resize rows to contents" +msgstr "Ajustar filas a los contenidos" + +#: spyder/widgets/variableexplorer/collectionseditor.py:655 +#: spyder/widgets/variableexplorer/collectionseditor.py:990 +#: spyder/widgets/variableexplorer/collectionseditor.py:1007 +msgid "Plot" +msgstr "Graficar" + +#: spyder/widgets/variableexplorer/collectionseditor.py:659 +msgid "Histogram" +msgstr "Histograma" + +#: spyder/widgets/variableexplorer/collectionseditor.py:663 +msgid "Show image" +msgstr "Mostrar como imagen" + +#: spyder/widgets/variableexplorer/collectionseditor.py:667 +#: spyder/widgets/variableexplorer/collectionseditor.py:1015 +msgid "Save array" +msgstr "Guardar arreglo" + +#: spyder/widgets/variableexplorer/collectionseditor.py:671 +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Insert" +msgstr "Insertar" + +#: spyder/widgets/variableexplorer/collectionseditor.py:674 +#: spyder/widgets/variableexplorer/collectionseditor.py:898 +msgid "Remove" +msgstr "Eliminar" + +#: spyder/widgets/variableexplorer/collectionseditor.py:684 +#: spyder/widgets/variableexplorer/collectionseditor.py:919 +msgid "Duplicate" +msgstr "Duplicar" + +#: spyder/widgets/variableexplorer/collectionseditor.py:896 +msgid "Do you want to remove the selected item?" +msgstr "¿Desea eliminar la variable seleccionada?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:897 +msgid "Do you want to remove all selected items?" +msgstr "¿Desea eliminar todas las variables seleccionadas?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:917 +msgid "New variable name:" +msgstr "Nuevo nombre de variable:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:920 +msgid "Variable name:" +msgstr "Nombre de variable:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +msgid "Key:" +msgstr "Nombre:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Value:" +msgstr "Valor:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:978 +msgid "Import error" +msgstr "Error de importación" + +#: spyder/widgets/variableexplorer/collectionseditor.py:979 +msgid "Please install matplotlib or guiqwt." +msgstr "Por favor instale Matplotlib o guiqwt." + +#: spyder/widgets/variableexplorer/collectionseditor.py:991 +msgid "Unable to plot data.

Error message:
%s" +msgstr "" +"No fue posible graficar los datos.

Mensaje de error:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1008 +msgid "Unable to show image.

Error message:
%s" +msgstr "" +"No fue posible generar la gráfica.

Mensaje de error:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1031 +msgid "Unable to save array

Error message:
%s" +msgstr "" +"No fue posible guardar el arreglo

Mensaje de error:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1056 +msgid "It was not possible to copy this array" +msgstr "No fue posible copiar este arreglo" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1081 +msgid "Clipboard contents" +msgstr "Contenidos del portapapeles" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1096 +msgid "Import from clipboard" +msgstr "Importar desde el portapapeles" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1098 +msgid "Empty clipboard" +msgstr "Vaciar el portapapeles" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1099 +msgid "Nothing to be imported from clipboard." +msgstr "No hay nada para importar desde el portapapeles." + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To bool" +msgstr "A booleano" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To complex" +msgstr "A complejo" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To float" +msgstr "A flotante" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To int" +msgstr "A entero" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:452 +msgid "To str" +msgstr "A cadena" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:526 +msgid "%s editor" +msgstr "Editor de %s" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:558 +msgid "Column min/max" +msgstr "Min/max de columna" + +#: spyder/widgets/variableexplorer/importwizard.py:116 +#: spyder/widgets/variableexplorer/importwizard.py:431 +msgid "Import as" +msgstr "Importar como" + +#: spyder/widgets/variableexplorer/importwizard.py:118 +msgid "data" +msgstr "datos" + +#: spyder/widgets/variableexplorer/importwizard.py:122 +msgid "code" +msgstr "código" + +#: spyder/widgets/variableexplorer/importwizard.py:125 +#: spyder/widgets/variableexplorer/importwizard.py:504 +msgid "text" +msgstr "texto" + +#: spyder/widgets/variableexplorer/importwizard.py:138 +msgid "Column separator:" +msgstr "Separador de columnas:" + +#: spyder/widgets/variableexplorer/importwizard.py:142 +msgid "Tab" +msgstr "Tabulación" + +#: spyder/widgets/variableexplorer/importwizard.py:145 +#: spyder/widgets/variableexplorer/importwizard.py:163 +msgid "other" +msgstr "otro" + +#: spyder/widgets/variableexplorer/importwizard.py:156 +msgid "Row separator:" +msgstr "Separador de filas:" + +#: spyder/widgets/variableexplorer/importwizard.py:160 +msgid "EOL" +msgstr "Fin de línea" + +#: spyder/widgets/variableexplorer/importwizard.py:175 +msgid "Additional options" +msgstr "Opciones adicionales" + +#: spyder/widgets/variableexplorer/importwizard.py:179 +msgid "Skip rows:" +msgstr "Saltar filas:" + +#: spyder/widgets/variableexplorer/importwizard.py:190 +msgid "Comments:" +msgstr "Comentarios:" + +#: spyder/widgets/variableexplorer/importwizard.py:196 +msgid "Transpose" +msgstr "Trasponer" + +#: spyder/widgets/variableexplorer/importwizard.py:434 +msgid "array" +msgstr "arreglo" + +#: spyder/widgets/variableexplorer/importwizard.py:439 +msgid "list" +msgstr "lista" + +#: spyder/widgets/variableexplorer/importwizard.py:444 +msgid "DataFrame" +msgstr "DataFrame" + +#: spyder/widgets/variableexplorer/importwizard.py:487 +#: spyder/widgets/variableexplorer/importwizard.py:571 +msgid "Import wizard" +msgstr "Asistente de importación" + +#: spyder/widgets/variableexplorer/importwizard.py:492 +msgid "Raw text" +msgstr "Texto sin formato" + +#: spyder/widgets/variableexplorer/importwizard.py:495 +msgid "variable_name" +msgstr "nombre_de_variable" + +#: spyder/widgets/variableexplorer/importwizard.py:506 +msgid "table" +msgstr "tabla" + +#: spyder/widgets/variableexplorer/importwizard.py:507 +msgid "Preview" +msgstr "Vista previa" + +#: spyder/widgets/variableexplorer/importwizard.py:511 +msgid "Variable Name" +msgstr "Nombre de variable" + +#: spyder/widgets/variableexplorer/importwizard.py:534 +msgid "Done" +msgstr "Hecho" + +#: spyder/widgets/variableexplorer/importwizard.py:572 +msgid "" +"Unable to proceed to next step

Please check your entries." +"

Error message:
%s" +msgstr "" +"No fue posible pasar al siguiente paso

Por favor revise sus " +"daros.

Mensaje de error:
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:196 +msgid "Refresh" +msgstr "Actualizar" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:200 +msgid "Refresh periodically" +msgstr "" +"Actualizar\n" +"periódicamente" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:207 +#: spyder/widgets/variableexplorer/namespacebrowser.py:487 +msgid "Import data" +msgstr "Importar datos" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:210 +#: spyder/widgets/variableexplorer/namespacebrowser.py:565 +#: spyder/widgets/variableexplorer/namespacebrowser.py:584 +msgid "Save data" +msgstr "Guardar datos" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:215 +msgid "Save data as..." +msgstr "Guardar datos como..." + +#: spyder/widgets/variableexplorer/namespacebrowser.py:227 +msgid "Exclude references which name starts with an underscore" +msgstr "" +"Excluir variables cuyo nombre comienza\n" +"con un guión abajo" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:235 +msgid "Exclude references which name is uppercase" +msgstr "" +"Excluir variables cuyo nombre está\n" +"por completo en mayúsculas" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:242 +msgid "Exclude references which name starts with an uppercase character" +msgstr "Excluir variables cuyo nombre comienza en mayúsculas" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:250 +msgid "" +"Exclude references to unsupported data types (i.e. which won't be handled/" +"saved correctly)" +msgstr "" +"Excluir variables que referencian tipos de datos no soportados\n" +"(es decir aquellos que no pueden manejarse y guardarse\n" +"correctamente)" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:345 +msgid "Object %s is not picklable" +msgstr "El objeto %s no es picklable" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:507 +msgid "" +"Unsupported file extension '%s'

Would you like to import it " +"anyway (by selecting a known file format)?" +msgstr "" +"Extensión de archivo no soportada: '%s'

¿Desea importar el " +"archivo de todas formas (seleccionando un formato conocido)?" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:515 +msgid "Open file as:" +msgstr "Abrir archivo como:" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:553 +msgid "Unable to load '%s'

Error message:
%s" +msgstr "No fue posible cargar '%s'

Mensaje de error:
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:585 +msgid "Unable to save current workspace

Error message:
%s" +msgstr "" +"No fue posible guardar el espacio de trabajo actual

Mensaje de " +"error:
%s" + +#: spyder/widgets/variableexplorer/texteditor.py:74 +msgid "Text editor" +msgstr "Editor de texto" + +#: spyder/widgets/variableexplorer/utils.py:29 +msgid "View and edit DataFrames and Series in the Variable Explorer" +msgstr "Ver y editar DataFrames y Series en el Explorador de Variables" + +#: spyder/widgets/variableexplorer/utils.py:34 +msgid "View and edit two and three dimensional arrays in the Variable Explorer" +msgstr "" +"Ver y editar arreglos bi y tridimensionales en el Explorador de Variables" + +#: spyder/workers/updates.py:89 spyder/workers/updates.py:91 +msgid "Unable to retrieve information." +msgstr "No fue posible recuperar la información" + +#: spyder/workers/updates.py:93 +msgid "" +"Unable to connect to the internet.

Make sure the connection is " +"working properly." +msgstr "" +"No fue posible conectarse a Internet.

Por favor asegúrese de que la " +"conexión está funcionando apropiadamente." + +#: spyder/workers/updates.py:96 +msgid "Unable to check for updates." +msgstr "No fue posible buscar actualizaciones" + +#~ msgid "Truncate values" +#~ msgstr "Abreviar valores" + +#~ msgid "Reload last session" +#~ msgstr "Recargar la última sesión" + +#~ msgid "Load session..." +#~ msgstr "Cargar sesión..." + +#~ msgid "Load Spyder session" +#~ msgstr "Cargar sesión de Spyder" + +#~ msgid "Save session..." +#~ msgstr "Guardar sesión..." + +#~ msgid "Save current session and quit application" +#~ msgstr "Guardar sesión actual y salir de la aplicación" + +#~ msgid "Open session" +#~ msgstr "Abrir sesión" + +#~ msgid "Spyder sessions" +#~ msgstr "Sesiones de Spyder" + +#~ msgid "Save session" +#~ msgstr "Guardar sesión" + +#~ msgid "Jupyter Qtconsole integration" +#~ msgstr "Integración con la terminal de IPython" + +#~ msgid "Python executable" +#~ msgstr "Archivo ejecutable de Python" + +#~ msgid "Trying to kill a kernel?" +#~ msgstr "Desea cerrar un núcleo?" + +#~ msgid "" +#~ "You can't close this kernel because it has one or more consoles connected " +#~ "to it.

You need to close them instead or you can kill the kernel " +#~ "using the second button from right to left." +#~ msgstr "" +#~ "No puede cerrar este núcleo porque tiene una o más terminales conectadas " +#~ "a él.

Debe cerrarlas previamente o puede terminar el proceso " +#~ "usando el segundo botón ubicado de derecha a izquierda." + +#~ msgid "Kernel" +#~ msgstr "Núcleo" + +#~ msgid "" +#~ "Either:
  1. Your IPython frontend and kernel versions are " +#~ "incompatible or
  2. You don't have IPython installed in " +#~ "your external interpreter.
In any case, we're sorry but we can't " +#~ "create a console for you." +#~ msgstr "" +#~ "O bien:
  1. Sus versiones del núcleo y la interfaz gráfica de IPython " +#~ "son incompatibles o
  2. Usted no tiene IPython " +#~ "instalado en su intérprete externo.
Lo lamentamos, pero en " +#~ "cualquier caso no podemos crear una terminal de IPython para usted." + +#~ msgid "Kernel %s" +#~ msgstr "Núcleo %s" + +#~ msgid "Open an IPython console" +#~ msgstr "Abrir una terminal de IPython" + +#~ msgid "" +#~ "The console monitor was disabled: the IPython kernel will be started as " +#~ "expected, but an IPython console will have to be connected manually to " +#~ "the kernel." +#~ msgstr "" +#~ "El monitor está desactivado, por tanto se creará un kernel de IPython " +#~ "pero usted deberá conectar manualmente un intérprete al mismo." + +#~ msgid "" +#~ "UMR excluded modules:\n" +#~ "(example: guidata, guiqwt)" +#~ msgstr "" +#~ "Módulos excluidos del RMU:\n" +#~ "(por ejemplo: guidata, guiqwt)" + +#~ msgid "" +#~ "This feature requires the Matplotlib library.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "Esta característica requiere la librería Matplotlib.\n" +#~ "Al parecer no la tiene instalada." + +#~ msgid "" +#~ "This feature requires the Sympy library.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "Esta característica requiere la librería Sympy.\n" +#~ "Al parecer no la tiene instalada." + +#~ msgid "&Font..." +#~ msgstr "&Tipo de letra..." + +#~ msgid "Set font style" +#~ msgstr "Establece el tipo de fuente" + +#~ msgid "Select a new font" +#~ msgstr "Seleccionar una nueva fuente" + +#~ msgid "" +#~ "The kernel failed to start!! That's all we know... Please close this " +#~ "console and open a new one." +#~ msgstr "" +#~ "No fue posible crear un núcleo!! Es todo lo que sabemos... Por favor " +#~ "cierre esta terminal y abra una nueva." + +#~ msgid "" +#~ "It seems the kernel died unexpectedly. Use 'Restart kernel' to continue " +#~ "using this console." +#~ msgstr "" +#~ "Al parecer el núcleo murió de forma inesperada. Use 'Reiniciar el núcleo' " +#~ "para continuar usando esta terminal." + +#~ msgid "Kernel process is either remote or unspecified. Cannot interrupt" +#~ msgstr "" +#~ "El núcleo es remoto o no está especificado. Por ello no se puede " +#~ "interrumpir." + +#~ msgid "Kernel process is either remote or unspecified. Cannot restart." +#~ msgstr "" +#~ "El núcleo es remoto o no está especificado. Por ello no se puede " +#~ "reiniciar." + +#~ msgid "its own configuration file" +#~ msgstr "su propio archivo de configuración" + +#~ msgid " and " +#~ msgstr " y" + +#~ msgid "the following projects:
%s" +#~ msgstr "los siguientes proyectos:
%s" + +#~ msgid "Existing Pydev project" +#~ msgstr "Un proyecto de Pydev existente" + +#~ msgid "Close unrelated projects" +#~ msgstr "Cerrar proyectos no relacionados" + +#~ msgid "Edit related projects" +#~ msgstr "Editar proyectos relacionados" + +#~ msgid "Add to PYTHONPATH" +#~ msgstr "Añadir al PYTHONPATH" + +#~ msgid "Remove from PYTHONPATH" +#~ msgstr "Eliminar del PYTHONPATH" + +#~ msgid "Properties" +#~ msgstr "Propiedades" + +#~ msgid "" +#~ "The workspace was unable to load or save %s

Please check if you " +#~ "have the permission to write the associated configuration files." +#~ msgstr "" +#~ "No fue posible cargar o guardar el espacio de trabajo%s

Por favor " +#~ "verifique si cuenta con los permisos necesarios para escribir los " +#~ "archivos de configuración del proyecto al disco." + +#~ msgid "Import directory" +#~ msgstr "Importar directorio" + +#~ msgid "" +#~ "The following directory is not in workspace:
%s

Do you " +#~ "want to continue (and copy the directory to workspace)?" +#~ msgstr "" +#~ "El siguiente directorio no está en el espacio de trabajo:
%s

¿Desea continuar (y copiar el directorio al espacio de trabajo)?" + +#~ msgid "The project %s is already opened!" +#~ msgstr "El proyecto %s ya está abierto!" + +#~ msgid "" +#~ "The project root path directory is inside the workspace but not as the " +#~ "expected tree level. It is not a directory of the workspace:
%s" +#~ msgstr "" +#~ "La ruta del directorio del proyecto está dentro del espacio de trabajo " +#~ "pero no al nivel esperado de profundidad, pues no es un directorio sino " +#~ "un subdirectorio del espacio de trabajo:
%s" + +#~ msgid "A project named %s already exists" +#~ msgstr "Un proyecto llamado %s ya existe" + +#~ msgid "" +#~ "Invalid project name.

Name must match the following regular " +#~ "expression:
%s" +#~ msgstr "" +#~ "Nombre de proyecto inválido.

El nombre debe ajustarse a una " +#~ "expresión del tipo:
%s" + +#~ msgid "" +#~ "The following directory is not empty:
%s

Do you want to " +#~ "continue?" +#~ msgstr "" +#~ "El siguiente directorio no está vacío:
%s

¿Desear " +#~ "continuar?" + +#~ msgid "New project" +#~ msgstr "Nuevo proyecto" + +#~ msgid "" +#~ "The current workspace has not been configured yet.\n" +#~ "Do you want to do this now?" +#~ msgstr "" +#~ "El siguiente espacio de trabajo no ha sido configurado.\n" +#~ "¿Desea hacerlo ahora?" + +#~ msgid "Import existing project" +#~ msgstr "Importar proyecto existente" + +#~ msgid "Select projects to import" +#~ msgstr "Seleccionar proyectos a importar" + +#~ msgid "The folder %s does not contain a valid %s project" +#~ msgstr "La carpeta %s no contiene un proyecto de %s válido" + +#~ msgid "Import existing Pydev project" +#~ msgstr "Importar un proyecto existente de Pydev" + +#~ msgid "" +#~ "Unable to read Pydev project %s

Error message:
%s" +#~ msgstr "" +#~ "No fue posible cargar el proyecto de Pydev %s

Mensaje de error:
%s" + +#~ msgid "Select projects which are related to %s" +#~ msgstr "Seleccionar los proyectos que están relacionados a %s" + +#~ msgid "" +#~ "Statistics on source files only:
(Python, Cython, IPython, Enaml,C/C+" +#~ "+, Fortran)

%s files.
%s lines of code." +#~ msgstr "" +#~ "Estadísticas para los archivos de código únicamente:
(Python, Cython, " +#~ "IPython, Enaml, C/C++, Fortran)

%s archivos.
%s " +#~ "líneas de código." + +#~ msgid "Select an existing workspace directory, or create a new one" +#~ msgstr "" +#~ "Seleccionar un directorio existente para que\n" +#~ "sea el espacio de trabajo, o crear uno nuevo" + +#~ msgid "" +#~ "What is the workspace?

A Spyder workspace is " +#~ "a directory on your filesystem that contains Spyder projects and ." +#~ "spyderworkspace configuration file.

A Spyder project is " +#~ "a directory with source code (and other related files) and a " +#~ "configuration file (named .spyderproject) with project settings " +#~ "(PYTHONPATH, linked projects, ...).
" +#~ msgstr "" +#~ "¿Qué es un espacio de trabajo?

Un espacio de " +#~ "trabajo de Spyder es un directorio en su sistema de archivos que " +#~ "contiene un proyecto de Spyder y un archivo de configuración ." +#~ "spyderworkspace.

Un proyecto de Spyder es un directorio " +#~ "con código fuente (y otros archivos relacionados) y un archivo de " +#~ "configuración (llamado .spyderproject) con los ajustes del " +#~ "proyecto (PYTHONPATH, proyectos referenciados, etc).
" + +#~ msgid "This is the current workspace directory" +#~ msgstr "Este es el directorio actual del espacio de trabajo" + +#~ msgid "" +#~ "The following directory is not a Spyder workspace:
%s

Do you " +#~ "want to create a new workspace in this directory?" +#~ msgstr "" +#~ "El siguiente directorio no es un espacio de trabajo de Spyder:
" +#~ "%s

¿Desea crear un nuevo espacio de trabajo en este directorio?" + +#~ msgid "Unable to retrieve data.

Error message:
%s" +#~ msgstr "" +#~ "No fue posible obtener los datos.

Mensaje de error:
%s" + +#~ msgid "Set shell font style" +#~ msgstr "Establece el tipo de fuente de la terminal" + +#~ msgid "Text and margin font style" +#~ msgstr "Tipo de letra para el texto y las márgenes" + +#~ msgid "tab" +#~ msgstr "tabulador" + +#~ msgid "Breakpoints" +#~ msgstr "Puntos de interrupción (Breakpoints)" + +#~ msgid "Exit" +#~ msgstr "Terminar" + +#~ msgid "Exit Debug" +#~ msgstr "Terminar depuración" + +#~ msgid "Rich text help on the Object Inspector" +#~ msgstr "Ayuda en texto enriquecido en el Inspector de Objetos" + +#~ msgid "Object inspector" +#~ msgstr "Inspector de objetos" + +#~ msgid "Preferences > Object Inspector" +#~ msgstr "Preferencias > Inspector de objetos" + +#~ msgid "Set as current console's working directory" +#~ msgstr "Establece el directorio de trabajo para la terminal actual" + +#~ msgid "Save session and quit..." +#~ msgstr "Guardar sesión y salir..." + +#~ msgid "Loading object inspector..." +#~ msgstr "Cargando el inspector de objetos..." + +#, fuzzy +#~ msgid "The Object Inspector" +#~ msgstr "Inspector de objetos" + +#~ msgid "(Experimental) Editor's code completion, go-to-definition and help" +#~ msgstr "(Experimental) Completado del código y ayuda en el Editor" + +#~ msgid "" +#~ "This path is incorrect.\n" +#~ "Enter a correct directory path,\n" +#~ "then press enter to validate" +#~ msgstr "" +#~ "Esta ruta es incorrecta.\n" +#~ "Introduzca una ruta de\n" +#~ "directorio correcta y después\n" +#~ "presione Enter para validarla" + +#~ msgid "Save Python script" +#~ msgstr "Guardar archivo de Python" + +#~ msgid "PyQt" +#~ msgstr "PyQt" + +#~ msgid "API selection for QString and QVariant objects:" +#~ msgstr "Selección del API para objetos QString and QVariant:" + +#~ msgid "API #1" +#~ msgstr "API #1" + +#~ msgid "API #2" +#~ msgstr "API #2" + +#~ msgid "Default API" +#~ msgstr "API por defecto" + +#~ msgid "" +#~ "PyQt API #1 is the default
API for Python 2. PyQt API #2 is the " +#~ "default API for Python 3 and is compatible with PySide." +#~ msgstr "" +#~ "El API #1 de PyQt es el API por
defecto para Python 2. El API #2 de " +#~ "PyQt es el API por defecto para Python 3 y es compatible con PySide." + +#~ msgid "Ignore API change errors (sip.setapi)" +#~ msgstr "Ignorar los errores de cambio de API (sip.setapi)" + +#~ msgid "" +#~ "Enabling this option will ignore
errors when changing PyQt API. As " +#~ "PyQt does not support dynamic API changes, it is strongly recommended to " +#~ "use this feature wisely, e.g. for debugging purpose." +#~ msgstr "" +#~ "Al activar esta opción se ignorarán
los errores generados al cambiar " +#~ "de API de PyQt. Dado que PyQt no soporta los cambios dinámicos de API, se " +#~ "recomienda usar esta característica con sumo cuidado, por ejemplo, para " +#~ "propósitos de depuración." + +#~ msgid "Matplotlib" +#~ msgstr "Matplotlib" + +#~ msgid "GUI backend:" +#~ msgstr "Salida gráfica:" + +#~ msgid "" +#~ "Set the GUI toolkit used by
Matplotlib to show figures (default: " +#~ "Qt4Agg)" +#~ msgstr "" +#~ "Establecer la librería gráfica
utilizada para generar las gráficas de " +#~ "Matplotlib (por defecto: Qt4Agg)" + +#~ msgid "Mod1" +#~ msgstr "Mod1" + +#~ msgid "Mod2" +#~ msgstr "Mod2" + +#~ msgid "Mod3" +#~ msgstr "Mod3" + +#, fuzzy +#~ msgid "The Internal Console" +#~ msgstr "Terminal interna" + +#~ msgid "File list management" +#~ msgstr "Gestión de la lista de archivos" + +#~ msgid "Filter:" +#~ msgstr "Filtro:" + +#~ msgid "(press Enter to edit file)" +#~ msgstr "(Oprimir Enter para editar)" + +#~ msgid "&Edit file" +#~ msgstr "Editar" + +#~ msgid "&Close file" +#~ msgstr "Cerrar" + +#~ msgid "Hint: press Alt to show accelerators" +#~ msgstr "Sugerencia: oprimir Alt para mostrar aceleradores" + +#~ msgid "Vertical dockwidget tabs" +#~ msgstr "Componentes en pestañas verticales" + +#~ msgid "Custom dockwidget margin:" +#~ msgstr "Márgenes de componente personalizadas:" + +#~ msgid "" +#~ "Note: add analysis:ignore in a comment to ignore code/style " +#~ "analysis warnings. For more informations on style guide for Python code, " +#~ "please refer to the %s page." +#~ msgstr "" +#~ "Nota: Añada analysis:ignore a un comentario para ignorar " +#~ "advertencias de análisis de código o estilo. Para más información sobre " +#~ "una guía de estilo para escribir código en Python, por favor refiérase a " +#~ "la página de %s (en inglés).
" + +#~ msgid "Style analysis" +#~ msgstr "Análisis de estilo" + +#~ msgid "Qt (PyQt/PySide)" +#~ msgstr "Qt (PyQt/PySide)" + +#~ msgid "Use a completion widget" +#~ msgstr "Usar un widget para completar texto" + +#~ msgid "Use a widget instead of plain text output for tab completion" +#~ msgstr "" +#~ "Puede decidir si usar un widget en lugar de texto plano \n" +#~ "para mostrar las posibilidades de completado usando la\n" +#~ "tecla Tab." + +#~ msgid "Switch to/from layout %d" +#~ msgstr "Cambiarse a la disposición %d" + +#~ msgid "Set layout %d" +#~ msgstr "Establecer la disposición %d" + +#~ msgid "" +#~ "%s will be closed.\n" +#~ "Do you want to kill the associated kernel and all of its clients?" +#~ msgstr "" +#~ "La %s será cerrada.\n" +#~ "Desea cerrar el núcleo asociado y todos sus clientes?" + +#~ msgid "Install Spyder's input hook for Qt" +#~ msgstr "Instalar el \"input hook\" de Spyder para Qt" + +#~ msgid "" +#~ "PyQt installs an input hook that allows
creating and interacting with " +#~ "Qt widgets in an interactive console without blocking it. On Windows " +#~ "platforms, it is strongly recommended to replace it by Spyder's. " +#~ "Regarding PySide, note that it does not install an input hook, so it is " +#~ "required to enable this feature in order to be able to manipulate PySide/" +#~ "Qtobjects interactively." +#~ msgstr "" +#~ "PyQt instala un mecanismo de \"input hook\"
que permite crear e " +#~ "interactuar con widgets de Qt en una terminal sin bloquearla. En Windows, " +#~ "es altamente recomendable reemplazar este mecanismo por el de Spyder. Con " +#~ "respecto a PySide, debe decirse que no instala un \"inputhook\", así que " +#~ "se requiere activar esta característica para poder manipular objectos de " +#~ "PySide/Qt interactivamente." + +#~ msgid "Could not open ssh tunnel\n" +#~ msgstr "No fue posible crear un túnel ssh\n" + +#~ msgid "Mismatch between kernel and frontend" +#~ msgstr "Incompatibilidad entre el núcleo y la interfaz gráfica" + +#~ msgid "" +#~ "Your IPython frontend and kernel versions are incompatible!!

We're sorry but we can't create an IPython console for you." +#~ msgstr "" +#~ "Sus versiones del núcleo y la interfaz gráfica de IPython son " +#~ "incompatibles!!

Lo lamentamos pero no podemos crear una " +#~ "terminal de IPython para usted." + +#~ msgid "Always edit in-place" +#~ msgstr "Siempre editar en línea" + +#~ msgid "Show collection contents" +#~ msgstr "Mostrar contenidos de listas" + +#~ msgid "" +#~ "Resizing cells of a table of such size could take a long time.\n" +#~ "Do you want to continue anyway?" +#~ msgstr "" +#~ "Redimensionar las celdas de una tabla de este tamaño puede\n" +#~ "tomar mucho tiempo.\n" +#~ "\n" +#~ "¿Desea continuar de todas formas?" + +#~ msgid "Interrupt kernel" +#~ msgstr "Interrumpir el núcleo" + +#~ msgid "Run &selection" +#~ msgstr "Ejecutar &selección" + +#~ msgid "Patch Matplotlib figures" +#~ msgstr "Parchear las gráficas de Matplotlib" + +#~ msgid "" +#~ "Patching Matplotlib library will add a button to customize figure options " +#~ "(Qt4Agg only) and fix some issues." +#~ msgstr "" +#~ "Al aplicar este parche a la librería Matplotlib se añadirá un botón (sólo " +#~ "con\n" +#~ "el backend Qt4Agg) para ajustar las opciones de curvas y gráficas y para\n" +#~ "corregir algunos problemas." + +#~ msgid "(for example: kernel-3764.json, or simply 3764)" +#~ msgstr "(por ejemplo: `kernel-3764.json`, o simplemente `3764`)" + +#~ msgid "Provide an IPython kernel connection file:" +#~ msgstr "Provea un archivo de conexión para un núcleo de IPython" + +#~ msgid "Interpreter" +#~ msgstr "Intérprete" + +#~ msgid "Dedicated Python interpreter" +#~ msgstr "Intérprete de Python dedicado" + +#~ msgid "Open Python interpreter here" +#~ msgstr "Abrir intérprete de Python aquí" + +#~ msgid "Import as array" +#~ msgstr "Importar como arreglo" + +#~ msgid "(not installed)" +#~ msgstr "(no está instalado)" + +#~ msgid "" +#~ "This feature requires the pywin32 module.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "Esta característica requiere la librería pywin32.\n" +#~ "Al parecer no la tiene instalada." + +#~ msgid "Balloon tips" +#~ msgstr "Mostrar globos de sugerencias" + +#~ msgid "Show single completion" +#~ msgstr "" +#~ "Mostrar la lista de resultados a completar aún con una sola elección" + +#~ msgid "Automatic notification to object inspector" +#~ msgstr "Notificación automática al Inspector de objetos" + +#~ msgid "" +#~ "If this option is enabled, object inspector\n" +#~ "will automatically show informations on functions\n" +#~ "entered in editor (this is triggered when entering\n" +#~ "a left parenthesis after a valid function name)" +#~ msgstr "" +#~ "Si esta opción está activada, el Inspector de\n" +#~ "objetos mostrará automáticamente información\n" +#~ "sobre las funciones introducidas en el Editor\n" +#~ "(esto se activa cuando se introduce un paréntesis\n" +#~ "izquierdo después de un nombre de función válido" + +#~ msgid "" +#~ "If this option is enabled, object inspector\n" +#~ "will automatically show informations on functions\n" +#~ "entered in console (this is triggered when entering\n" +#~ "a left parenthesis after a valid function name)" +#~ msgstr "" +#~ "Si esta opción está activada, el Inspector de\n" +#~ "objetos mostrará automáticamente información\n" +#~ "sobre funciones introducidas en la Terminal \n" +#~ "(esto se activa cuando se introduce un paréntesis\n" +#~ "izquierdo después de un nombre de función válido)" + +#~ msgid "Open a Python interpreter at startup" +#~ msgstr "Abrir un intérprete de Python al inicio" + +#~ msgid "
Installed version: %s" +#~ msgstr "
Versión instalada: %s" + +#~ msgid "" +#~ "Unable to open IPython console because no supported IPython version was " +#~ "found.

Supported IPython versions: %s" +#~ msgstr "" +#~ "No es posible abrir un intérprete de IPython porque no se encontró " +#~ "ninguna versión de IPython soportada por Spyder.

Versiones " +#~ "soportadas son: %s" + +#~ msgid "Open an IPython console at startup" +#~ msgstr "Abrir una terminal de IPython al inicio" + +#~ msgid "IPython Help" +#~ msgstr "Ayuda de IPython" + +#~ msgid "Close current dockwidget" +#~ msgstr "Cerrar el componente actual" + +#~ msgid "&Interpreters" +#~ msgstr "&Intérpretes" + +#~ msgid "Qt Assistant" +#~ msgstr "Documentación de Qt" + +#~ msgid "Web Resources" +#~ msgstr "Recursos en la Web" + +#~ msgid "Windows and toolbars" +#~ msgstr "Componentes y barras de herramientas" + +#~ msgid "Create a new Python script" +#~ msgstr "Crear un nuevo archivo de Python" + +#~ msgid "Open text file" +#~ msgstr "Abrir archivo de texto" + +#~ msgid "" +#~ "Run selection or current \n" +#~ "block of lines" +#~ msgstr "" +#~ "Ejecutar la selección \n" +#~ "o el bloque actual" + +#~ msgid "Open Spyder path manager" +#~ msgstr "Abrir el administrador de rutas de Spyder" + +#~ msgid "Maximize current plugin to fit the whole application window" +#~ msgstr "" +#~ "Maximizar el componente actual\n" +#~ "para que ocupe toda la ventana" + +#~ msgid "" +#~ "Restore current plugin to its original size and position within the " +#~ "application window" +#~ msgstr "" +#~ "Restaurar el componente actual\n" +#~ "a su tamaño y posición originales\n" +#~ "dentro de la ventana principal" + +#~ msgid "Step Over" +#~ msgstr "Saltar paso" + +#~ msgid "Debug Step Over" +#~ msgstr "Depurar - Saltar paso" + +#~ msgid "Debug Continue" +#~ msgstr "Continuar depurando" + +#~ msgid "Debug Step Into" +#~ msgstr "Depurar - Ingresar en el paso" + +#~ msgid "Debug Step Return" +#~ msgstr "Depurar - Salir del paso" + +#~ msgid "Run active script in a new Python interpreter" +#~ msgstr "Ejecutar el archivo actual en un nuevo intérprete de Python" + +#~ msgid "" +#~ "Debug current script in external console\n" +#~ "(external console is executed in a separate process)" +#~ msgstr "" +#~ "Depurar el archivo actual en una terminal\n" +#~ "externa, la cual es ejecutada en proceso\n" +#~ "separado" + +#~ msgid "Edit run configurations" +#~ msgstr "Editar las opciones de ejecución" + +#~ msgid "Run again last script in external console with the same options" +#~ msgstr "" +#~ "Ejecutar de nuevo el último archivo en un\n" +#~ "intérprete externo con las mismas opciones" + +#~ msgid "" +#~ "Run selected text or current block of lines \n" +#~ "inside current external console's interpreter" +#~ msgstr "" +#~ "Ejecutar el texto seleccionado o el bloque\n" +#~ "de líneas actual dentro del intérprete en uso" + +#~ msgid "Run %s" +#~ msgstr "Ejecutar %s" + +#~ msgid "Run configurations" +#~ msgstr "Opciones de ejecución" + +#~ msgid "Type \"copyright\", \"credits\" or \"license\" for more information." +#~ msgstr "" +#~ "Escriba \"copyright\", \"credits\" o \"license\" para más información." + +#~ msgid "Start an IPython kernel at startup" +#~ msgstr "Abrir un núcleo de IPython al inicio" + +#~ msgid "This option is not available for IPython versions prior to v0.12." +#~ msgstr "" +#~ "Esta opción no está disponible para versiones de IPython\n" +#~ "anteriores a la 0.12" + +#~ msgid "Format: " +#~ msgstr "Formato:" + +#, fuzzy +#~ msgid " Source" +#~ msgstr "Origen" + +#~ msgid "Builtin Modules: spy.app, spy.window" +#~ msgstr "Módulos integrados: spy.app, spy.window" + +#~ msgid "Open &interpreter" +#~ msgstr "Abrir un &intérprete de Python" + +#~ msgid "Set the appropriate IPython color option" +#~ msgstr "Ajustar la opción apropiada de color para IPython" + +#~ msgid "IPython interpreter command line options" +#~ msgstr "Opciones de línea de comandos del intérprete de IPython" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/locale/fr/LC_MESSAGES/spyder.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/locale/fr/LC_MESSAGES/spyder.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder/locale/fr/LC_MESSAGES/spyder.po spyder-3.0.2+dfsg1/spyder/locale/fr/LC_MESSAGES/spyder.po --- spyder-2.3.8+dfsg1/spyder/locale/fr/LC_MESSAGES/spyder.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/locale/fr/LC_MESSAGES/spyder.po 2016-11-19 00:32:09.000000000 +0000 @@ -0,0 +1,6409 @@ +# -*- coding: utf-8 -*- +# Spyder's french translation file +# Copyright (C) 2009-2011 Pierre Raybaut +# +msgid "" +msgstr "" +"Project-Id-Version: 2.1\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2016-11-17 19:16-0500\n" +"Last-Translator: Sylvain Corlay \n" +"Language-Team: Python\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.8.7.1\n" + +#: spyder/app/mainwindow.py:125 +msgid "Initializing..." +msgstr "Initialisation..." + +#: spyder/app/mainwindow.py:241 +msgid "Numpy and Scipy documentation" +msgstr "Documentation de Numpy et Scipy" + +#: spyder/app/mainwindow.py:243 spyder/app/mainwindow.py:1027 +msgid "Matplotlib documentation" +msgstr "Documentation de Matplotlib" + +#: spyder/app/mainwindow.py:246 +msgid "PyQt4 Reference Guide" +msgstr "Guide de référence de PyQt4" + +#: spyder/app/mainwindow.py:249 +msgid "PyQt4 API Reference" +msgstr "Documentation de l'API de PyQt4" + +#: spyder/app/mainwindow.py:251 +msgid "Python(x,y)" +msgstr "Python(x,y)" + +#: spyder/app/mainwindow.py:253 +msgid "WinPython" +msgstr "WinPython" + +#: spyder/app/mainwindow.py:525 +msgid "Close current pane" +msgstr "Fermer le volet courant" + +#: spyder/app/mainwindow.py:530 +msgid "Lock panes" +msgstr "Verrouiller les onglets" + +#: spyder/app/mainwindow.py:537 +msgid "Use next layout" +msgstr "Utiliser la disposition suivante" + +#: spyder/app/mainwindow.py:541 +msgid "Use previous layout" +msgstr "Utiliser la disposition précédente" + +#: spyder/app/mainwindow.py:560 spyder/widgets/sourcecode/codeeditor.py:2505 +msgid "Undo" +msgstr "Annuler" + +#: spyder/app/mainwindow.py:562 spyder/widgets/sourcecode/codeeditor.py:2508 +msgid "Redo" +msgstr "Répéter" + +#: spyder/app/mainwindow.py:564 spyder/widgets/shell.py:121 +#: spyder/widgets/sourcecode/codeeditor.py:2514 +#: spyder/widgets/variableexplorer/arrayeditor.py:453 +#: spyder/widgets/variableexplorer/collectionseditor.py:649 +#: spyder/widgets/variableexplorer/dataframeeditor.py:445 +msgid "Copy" +msgstr "Copier" + +#: spyder/app/mainwindow.py:566 spyder/widgets/shell.py:117 +#: spyder/widgets/sourcecode/codeeditor.py:2511 +msgid "Cut" +msgstr "Couper" + +#: spyder/app/mainwindow.py:568 spyder/widgets/shell.py:125 +#: spyder/widgets/sourcecode/codeeditor.py:2517 +#: spyder/widgets/variableexplorer/collectionseditor.py:646 +msgid "Paste" +msgstr "Coller" + +#: spyder/app/mainwindow.py:571 spyder/widgets/shell.py:138 +#: spyder/widgets/sourcecode/codeeditor.py:2520 +msgid "Select All" +msgstr "Sélectionner tout" + +#: spyder/app/mainwindow.py:581 spyder/plugins/editor.py:1353 +msgid "&File" +msgstr "&Fichier" + +#: spyder/app/mainwindow.py:582 spyder/plugins/editor.py:1345 +msgid "File toolbar" +msgstr "Barre d'outil fichiers" + +#: spyder/app/mainwindow.py:586 spyder/plugins/editor.py:1354 +msgid "&Edit" +msgstr "&Édition" + +#: spyder/app/mainwindow.py:587 spyder/plugins/editor.py:1350 +msgid "Edit toolbar" +msgstr "Barre d'outil édition" + +#: spyder/app/mainwindow.py:591 spyder/plugins/editor.py:1355 +msgid "&Search" +msgstr "&Recherche" + +#: spyder/app/mainwindow.py:592 spyder/plugins/editor.py:1346 +msgid "Search toolbar" +msgstr "Barre d'outil de recherche" + +#: spyder/app/mainwindow.py:596 spyder/plugins/editor.py:1356 +msgid "Sour&ce" +msgstr "Sour&ce" + +#: spyder/app/mainwindow.py:597 spyder/plugins/editor.py:1347 +msgid "Source toolbar" +msgstr "Barre d'outils code source" + +#: spyder/app/mainwindow.py:601 spyder/plugins/editor.py:780 +#: spyder/plugins/editor.py:1357 +msgid "&Run" +msgstr "E&xécution" + +#: spyder/app/mainwindow.py:602 spyder/plugins/editor.py:1348 +msgid "Run toolbar" +msgstr "Barre d'outil exécution" + +#: spyder/app/mainwindow.py:606 spyder/plugins/editor.py:739 +msgid "&Debug" +msgstr "&Déboguer" + +#: spyder/app/mainwindow.py:607 spyder/plugins/editor.py:1349 +msgid "Debug toolbar" +msgstr "Barre d'outil de débogage" + +#: spyder/app/mainwindow.py:611 +msgid "C&onsoles" +msgstr "C&onsoles" + +#: spyder/app/mainwindow.py:614 +msgid "&Projects" +msgstr "&Projets" + +#: spyder/app/mainwindow.py:617 spyder/plugins/editor.py:1358 +msgid "&Tools" +msgstr "Ou&tils" + +#: spyder/app/mainwindow.py:620 +msgid "&View" +msgstr "&Affichage" + +#: spyder/app/mainwindow.py:623 +msgid "&Help" +msgstr "A&ide" + +#: spyder/app/mainwindow.py:628 +msgid "Welcome to Spyder!" +msgstr "Bienvenue dans Spyder !" + +#: spyder/app/mainwindow.py:633 +msgid "Pre&ferences" +msgstr "Pré&férences" + +#: spyder/app/mainwindow.py:640 spyder/widgets/pathmanager.py:49 +msgid "PYTHONPATH manager" +msgstr "Gestionnaire de PYTHONPATH" + +#: spyder/app/mainwindow.py:643 +msgid "Python Path Manager" +msgstr "Gestionnaire de chemins d'accès Python" + +#: spyder/app/mainwindow.py:646 +msgid "Update module names list" +msgstr "Mise à jour de la liste des modules" + +#: spyder/app/mainwindow.py:649 +msgid "Refresh list of module names available in PYTHONPATH" +msgstr "" +"Mise à jour de la liste des modules disponibles notamment à travers " +"PYTHONPATH" + +#: spyder/app/mainwindow.py:652 +msgid "Reset Spyder to factory defaults" +msgstr "Rétablir les valeurs par défaut" + +#: spyder/app/mainwindow.py:657 +msgid "Current user environment variables..." +msgstr "Variables d'environnement de l'utilisateur..." + +#: spyder/app/mainwindow.py:659 +msgid "" +"Show and edit current user environment variables in Windows registry (i.e. " +"for all sessions)" +msgstr "" +"Afficher et modifier les variables d'environnement de l'utilisateur courant " +"dans Windows (c'est-à-dire directement dans la base de registre)" + +#: spyder/app/mainwindow.py:668 spyder/app/mainwindow.py:1123 +msgid "External Tools" +msgstr "Outils externes" + +#: spyder/app/mainwindow.py:672 +msgid "Python(x,y) launcher" +msgstr "Accueil de Python(x,y)" + +#: spyder/app/mainwindow.py:679 +msgid "WinPython control panel" +msgstr "Panneau de contrôle WinPython" + +#: spyder/app/mainwindow.py:688 +msgid "Qt Designer" +msgstr "Qt Designer" + +#: spyder/app/mainwindow.py:693 +msgid "Qt Linguist" +msgstr "Qt Linguist" + +#: spyder/app/mainwindow.py:699 +msgid "Qt examples" +msgstr "Exemples Qt" + +#: spyder/app/mainwindow.py:720 +msgid "guidata examples" +msgstr "Exemples guidata" + +#: spyder/app/mainwindow.py:731 +msgid "guiqwt examples" +msgstr "Exemples guiqwt" + +#: spyder/app/mainwindow.py:736 +msgid "Sift" +msgstr "Sift" + +#: spyder/app/mainwindow.py:746 +msgid "ViTables" +msgstr "ViTables" + +#: spyder/app/mainwindow.py:760 +msgid "Fullscreen mode" +msgstr "Mode plein écran" + +#: spyder/app/mainwindow.py:772 +msgid "Main toolbar" +msgstr "Barre d'outil principale" + +#: spyder/app/mainwindow.py:781 +msgid "" +"Spyder Internal Console\n" +"\n" +"This console is used to report application\n" +"internal errors and to inspect Spyder\n" +"internals with the following commands:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Please don't use it to run your code\n" +"\n" +msgstr "" +"Console interne de Spyder\n" +"\n" +"Il s'agit d'une console de débogage\n" +"utilisée par Spyder pour signaler des erreurs\n" +"internes ou pour inspecter les entrailles de\n" +"l'application avec les commandes ci-dessous :\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Ne l'utilisez pas pour exécuter votre propre code.\n" +"\n" + +#: spyder/app/mainwindow.py:798 +msgid "Loading help..." +msgstr "Chargement de l'aide en ligne..." + +#: spyder/app/mainwindow.py:805 +msgid "Loading outline explorer..." +msgstr "Chargement de l'explorateur de structure..." + +#: spyder/app/mainwindow.py:813 +msgid "Loading editor..." +msgstr "Chargement de l'éditeur..." + +#: spyder/app/mainwindow.py:819 spyder/plugins/console.py:134 +#: spyder/widgets/ipythonconsole/client.py:280 +msgid "&Quit" +msgstr "&Quitter" + +#: spyder/app/mainwindow.py:821 spyder/plugins/console.py:136 +msgid "Quit" +msgstr "Quitter" + +#: spyder/app/mainwindow.py:825 +msgid "&Restart" +msgstr "&Redémarrer" + +#: spyder/app/mainwindow.py:827 +msgid "Restart" +msgstr "Redémarrer" + +#: spyder/app/mainwindow.py:844 +msgid "Loading file explorer..." +msgstr "Chargement de l'explorateur de fichiers..." + +#: spyder/app/mainwindow.py:851 +msgid "Loading history plugin..." +msgstr "Chargement du journal d'historique..." + +#: spyder/app/mainwindow.py:862 +msgid "Loading online help..." +msgstr "Chargement de l'aide en ligne..." + +#: spyder/app/mainwindow.py:867 +msgid "Loading project explorer..." +msgstr "Chargement de l'explorateur de projet..." + +#: spyder/app/mainwindow.py:874 +msgid "Loading external console..." +msgstr "Chargement de la console externe..." + +#: spyder/app/mainwindow.py:880 +msgid "Loading namespace browser..." +msgstr "Chargement de l'explorateur d'espace de noms..." + +#: spyder/app/mainwindow.py:887 +msgid "Loading IPython console..." +msgstr "Chargement de la console IPython..." + +#: spyder/app/mainwindow.py:892 +msgid "Setting up main window..." +msgstr "Configuration de la fenêtre principale..." + +#: spyder/app/mainwindow.py:895 +msgid "Dependencies..." +msgstr "Dépendances..." + +#: spyder/app/mainwindow.py:899 +msgid "Report issue..." +msgstr "Rapport d'erreur..." + +#: spyder/app/mainwindow.py:903 +msgid "Spyder support..." +msgstr "Support technique de Spyder..." + +#: spyder/app/mainwindow.py:906 +msgid "Check for updates..." +msgstr "Rechercher les mises à jour" + +#: spyder/app/mainwindow.py:929 +msgid "Spyder documentation" +msgstr "Documentation de Spyder" + +#: spyder/app/mainwindow.py:934 +msgid "Spyder tutorial" +msgstr "Tutoriel de Spyder" + +#: spyder/app/mainwindow.py:941 +msgid "Interactive tours" +msgstr "Visite interactive" + +#: spyder/app/mainwindow.py:969 +msgid "Python documentation" +msgstr "Documentation de Python" + +#: spyder/app/mainwindow.py:975 spyder/app/mainwindow.py:1019 +msgid "IPython documentation" +msgstr "Documentation de IPython" + +#: spyder/app/mainwindow.py:976 +msgid "Intro to IPython" +msgstr "Introduction à IPython" + +#: spyder/app/mainwindow.py:978 +msgid "Quick reference" +msgstr "Référence rapide" + +#: spyder/app/mainwindow.py:980 +msgid "Console help" +msgstr "Aide sur la console" + +#: spyder/app/mainwindow.py:1017 +msgid "Python(x,y) documentation folder" +msgstr "Dossier de documentation Python(x,y)" + +#: spyder/app/mainwindow.py:1021 +msgid "guidata documentation" +msgstr "Documentation de guidata" + +#: spyder/app/mainwindow.py:1024 +msgid "guiqwt documentation" +msgstr "Documentation de guiqwt" + +#: spyder/app/mainwindow.py:1030 +msgid "NumPy documentation" +msgstr "Documentation de NumPy" + +#: spyder/app/mainwindow.py:1032 +msgid "NumPy reference guide" +msgstr "Manuel de référence de NumPy" + +#: spyder/app/mainwindow.py:1034 +msgid "NumPy user guide" +msgstr "Manuel de l'utilisateur de NumPy" + +#: spyder/app/mainwindow.py:1036 +msgid "SciPy documentation" +msgstr "Documentation de SciPy" + +#: spyder/app/mainwindow.py:1043 +msgid "Installed Python modules" +msgstr "Modules Python installés" + +#: spyder/app/mainwindow.py:1047 +msgid "Online documentation" +msgstr "Documentation en ligne" + +#: spyder/app/mainwindow.py:1059 +msgid "Qt documentation" +msgstr "Documentation de Qt" + +#: spyder/app/mainwindow.py:1065 +msgid "About %s..." +msgstr "À propos de %s..." + +#: spyder/app/mainwindow.py:1089 +msgid "Panes" +msgstr "Volets" + +#: spyder/app/mainwindow.py:1091 +msgid "Toolbars" +msgstr "Barres d'outils" + +#: spyder/app/mainwindow.py:1092 +msgid "Window layouts" +msgstr "Dispositions de fenêtres personnalisées" + +#: spyder/app/mainwindow.py:1101 spyder/app/mainwindow.py:1894 +#: spyder/app/mainwindow.py:1895 +msgid "Show toolbars" +msgstr "Afficher les barres d'outils" + +#: spyder/app/mainwindow.py:1116 +msgid "Attached console window (debugging)" +msgstr "Invite de commandes attaché (débogage)" + +#: spyder/app/mainwindow.py:1295 spyder/plugins/projects.py:244 +#: spyder/widgets/explorer.py:639 spyder/widgets/explorer.py:744 +#: spyder/widgets/externalshell/pythonshell.py:533 +#: spyder/widgets/externalshell/systemshell.py:105 +#: spyder/widgets/variableexplorer/arrayeditor.py:573 +#: spyder/widgets/variableexplorer/collectionseditor.py:417 +#: spyder/widgets/variableexplorer/dataframeeditor.py:593 +msgid "Error" +msgstr "Erreur" + +#: spyder/app/mainwindow.py:1296 +msgid "" +"You have missing dependencies!

%s

Please " +"install them to avoid this message.

Note: Spyder could " +"work without some of these dependencies, however to have a smooth experience " +"when using Spyder we strongly recommend you to install all the listed " +"missing dependencies.

Failing to install these dependencies might " +"result in bugs. Please be sure that any found bugs are not the direct result " +"of missing dependencies, prior to reporting a new issue." +msgstr "" +"Spyder manque certaines dépendances

%s
Veuillez " +"les installer pour éviter ce message

Remarque: Spyder " +"pourrait fonctionner sans certaines de ces dépendances, mais pour avoir une " +"bonne expérience lors de l'utilisation de Spyder nous fortement vous " +"recommandons d'installer toutes les dépendances manquantes.

" +"L'impossibilité d'installer ces dépendances peut entraîner des bogues. " +"Assurez-vous que les bogues trouvés ne résultent pas directement des " +"dépendances manquantes avant de signaler un nouveau problème." + +#: spyder/app/mainwindow.py:1741 +msgid "Spyder Default Layout" +msgstr "Rétablir la disposition de fenêtres par défaut" + +#: spyder/app/mainwindow.py:1759 +msgid "Save current layout" +msgstr "Enregistrer la disposition actuelle" + +#: spyder/app/mainwindow.py:1763 +msgid "Layout preferences" +msgstr "Préférences" + +#: spyder/app/mainwindow.py:1767 +msgid "Reset to spyder default" +msgstr "Rétablir les valeurs par défaut" + +#: spyder/app/mainwindow.py:1787 spyder/app/mainwindow.py:1809 +#: spyder/app/mainwindow.py:1872 spyder/app/mainwindow.py:2656 +#: spyder/plugins/configdialog.py:1254 spyder/plugins/externalconsole.py:446 +#: spyder/plugins/ipythonconsole.py:119 spyder/plugins/ipythonconsole.py:835 +#: spyder/plugins/maininterpreter.py:167 spyder/plugins/maininterpreter.py:195 +#: spyder/utils/environ.py:100 spyder/utils/environ.py:113 +#: spyder/widgets/variableexplorer/arrayeditor.py:496 +#: spyder/widgets/variableexplorer/collectionseditor.py:408 +#: spyder/widgets/variableexplorer/collectionseditor.py:1055 +msgid "Warning" +msgstr "Attention" + +#: spyder/app/mainwindow.py:1788 +msgid "" +"Window layout will be reset to default settings: this affects window " +"position, size and dockwidgets.\n" +"Do you want to continue?" +msgstr "" +"La disposition des fenêtres sera réinitialisée selon les réglages par " +"défaut.\n" +"Souhaitez-vous continuer ?" + +#: spyder/app/mainwindow.py:1810 +msgid "" +"Layout %s will be " +"overwritten. Do you want to " +"continue?" +msgstr "" +"La disposisiton de fenêtres%s sera réinitialisée. Souhaitez-vous " +"continuer ?" + +#: spyder/app/mainwindow.py:1873 +msgid "Quick switch layout #%s has not yet been defined." +msgstr "" +"La disposition de fenêtre personnalisée #%s n'a pas encore été définie." + +#: spyder/app/mainwindow.py:1891 spyder/app/mainwindow.py:1892 +msgid "Hide toolbars" +msgstr "Masquer les barres d'outils" + +#: spyder/app/mainwindow.py:2185 spyder/app/mainwindow.py:2186 +msgid "Maximize current pane" +msgstr "Agrandir le volet courant" + +#: spyder/app/mainwindow.py:2189 +msgid "Restore current pane" +msgstr "Restaurer le volet courant" + +#: spyder/app/mainwindow.py:2190 +msgid "Restore pane to its original size" +msgstr "Restaurer le volet à sa taille d'origine" + +#: spyder/app/mainwindow.py:2274 +msgid "About %s" +msgstr "À propos de %s" + +#: spyder/app/mainwindow.py:2401 spyder/plugins/editor.py:158 +#: spyder/plugins/runconfig.py:322 spyder/plugins/runconfig.py:444 +#: spyder/plugins/runconfig.py:449 spyder/utils/programs.py:285 +#: spyder/widgets/explorer.py:271 spyder/widgets/externalshell/baseshell.py:127 +msgid "Run" +msgstr "Exécuter" + +#: spyder/app/mainwindow.py:2402 +msgid "Running an external system terminal is not supported on platform %s." +msgstr "" +"L'exécution dans un terminal système externe n'est pas prise en charge sur " +"la plateforme %s." + +#: spyder/app/mainwindow.py:2657 +msgid "" +"Spyder will restart and reset to default settings:

Do you want to " +"continue?" +msgstr "" +"Spyder va redémarrer et réinitialiser les paramètres par " +"défaut

Souhaitez-vous néanmoins continuer?" + +#: spyder/app/mainwindow.py:2754 spyder/widgets/helperwidgets.py:250 +msgid "Spyder updates" +msgstr "Mises à jour de Spyder" + +#: spyder/app/mainwindow.py:2755 spyder/plugins/configdialog.py:819 +msgid "Check for updates on startup" +msgstr "Vérifier le mises à jour au démarrage" + +#: spyder/app/mainwindow.py:2775 +msgid "" +"Spyder %s is available!

Please use your package manager to " +"update Spyder or go to our Releases page to download this " +"new version.

If you are not sure how to proceed to update Spyder " +"please refer to our Installation instructions." +msgstr "" +"Spyder %s est disponible!

Utiliser le gestionnaire de paquets " +"pour mettre Spyder à ou consultez le site pour " +"télécharger le programme directement.

Si vous ne savez pas comment " +"procéder pour mettre Spyder à jour, veuillez consulter notre documentation." + +#: spyder/app/mainwindow.py:2787 +msgid "Spyder is up to date." +msgstr "Spyder à jour" + +#: spyder/app/restart.py:133 +msgid "" +"It was not possible to close the previous Spyder instance.\n" +"Restart aborted." +msgstr "" +"Il n'a pas été possible de fermer l'instance précédente de Spyder.\n" +"Redémarrage interrompu." + +#: spyder/app/restart.py:135 +msgid "" +"Spyder could not reset to factory defaults.\n" +"Restart aborted." +msgstr "" +"Impossible de rétablir les valeurs par défaut.\n" +"Redémarrage interrompu." + +#: spyder/app/restart.py:137 +msgid "" +"It was not possible to restart Spyder.\n" +"Operation aborted." +msgstr "" +"Il n'a pas été possible de redémarrer Spyder.\n" +"Opération annulée." + +#: spyder/app/restart.py:139 +msgid "Spyder exit error" +msgstr "Erreur de sortie de Spyder" + +#: spyder/app/restart.py:140 +msgid "Spyder reset error" +msgstr "Erreur de réinitialisation de Spyder" + +#: spyder/app/restart.py:141 +msgid "Spyder restart error" +msgstr "Erreur de démarrage de Spyder" + +#: spyder/app/restart.py:165 +msgid "Closing Spyder" +msgstr "Fermeture du programme" + +#: spyder/app/restart.py:239 +msgid "Resetting Spyder to defaults" +msgstr "Réinitialisation des valeurs par défaut" + +#: spyder/app/restart.py:271 +msgid "Restarting" +msgstr "Redémarrage" + +#: spyder/app/tour.py:124 +msgid "Welcome to the Introduction tour" +msgstr "Bienvenue à la visite interactive de Spyder !" + +#: spyder/app/tour.py:125 +msgid "" +"Spyder is a powerful Interactive Development Environment (or IDE) for " +"the Python programming language.

Here we are going to guide you " +"through its most important features.

Please use the arrow keys or " +"click on the buttons below to move along the tour." +msgstr "" +"Spyder est un environnement de développement interactif (ou IDE) pour " +"le langage de programmation Python.

Nous allons vous guider à " +"travers ses caractéristiques les plus importantes. Utilisez les touches " +"fléchées ou cliquez sur les boutons ci-dessous pour vous déplacer le long de " +"la visite interactive." + +#: spyder/app/tour.py:134 +msgid "The Editor" +msgstr "Éditeur de texte" + +#: spyder/app/tour.py:135 +msgid "" +"This is the pane where you write Python code before evaluating it. You can " +"get automatic suggestions and completions while writing, by pressing the " +"Tab key next to a given text.

The Editor comes with a line " +"number area (highlighted here in red), where Spyder shows warnings and " +"syntax errors. They can help you to detect potential problems before running " +"the code.

You can also set debug breakpoints in the line number area, " +"by doing a double click next to a non-empty line." +msgstr "" +"Dans ce volet, vous pouvez écrire le code Python avant de l'évaluer. Vous " +"pouvez obtenir des suggestions et des complétions de code automatiques " +"pendant l'écriture, en appuyant sur la touche Tab située à côté d'un " +"texte donné. Spyder affiche des avertissements et des erreurs de syntaxe. " +"Ils peuvent vous aider à détecter les problèmes potentiels avant d'exécuter " +"le code.

Vous pouvez également définir des points d'arrêt de " +"débogage dans la zone de numéro de ligne, en faisant un double clic à côté " +"d'une ligne non vide." + +#: spyder/app/tour.py:150 +msgid "The IPython console" +msgstr "Console IPython" + +#: spyder/app/tour.py:151 +msgid "" +"This is one of panes where you can run or execute the code you wrote on the " +"Editor. To do it you need to press the F5 key.

This console " +"comes with several useful features that greatly improve your programming " +"workflow (like syntax highlighting and inline plots). If you want to know " +"more about them, please follow this link.

Please " +"click on the button below to run some simple code in this console. This will " +"be useful to show you other important features." +msgstr "" +"C'est l'un des volets où vous pouvez exécuter le code que vous avez écrit " +"sur l'éditeur. Pour ce faire, vous devez appuyer sur la touche F5. " +"

Cette console est livrée avec plusieurs fonctionnalités utiles qui " +"améliorent considérablement votre flux de travail de programmation (comme la " +"coloration syntaxique et les graphiques en ligne). Si vous voulez en savoir " +"plus à ce sujet, s'il vous plaît suivez ce lien, S'il " +"vous plaît cliquer sur le bouton ci-dessous pour exécuter un code simple " +"dans cette console. Ce sera utile pour vous montrer d'autres fonctionnalités " +"importantes." + +#: spyder/app/tour.py:167 +msgid "The Variable Explorer" +msgstr "Explorateur de variables" + +#: spyder/app/tour.py:168 +msgid "" +"In this pane you can view and edit the variables generated during the " +"execution of a program, or those entered directly in one of Spyder consoles." +"

As you can see, the Variable Explorer is showing the variables " +"generated during the last step of this tour. By doing a double-click on any " +"of them, a new window will be opened, where you can inspect and modify their " +"contents." +msgstr "" +"Dans ce volet, vous pouvez visualiser et éditer les variables générées lors " +"de l'exécution d'un programme, ou celles entrées directement dans une des " +"consoles Spyder. Comme vous pouvez le voir, l'Explorateur de variables " +"affiche les variables générées au cours de la dernière étape de la visite " +"interactive. En faisant un double-clic sur l'une d'elles, une nouvelle " +"fenêtre s'ouvre, où vous pouvez inspecter et modifier leur contenu." + +#: spyder/app/tour.py:180 +msgid "The Python console" +msgstr "Console IPython" + +#: spyder/app/tour.py:181 +msgid "" +"You can also run your code on a Python console. These consoles are useful " +"because they let you run a file in a console dedicated only to it.To select " +"this behavior, please press the F6 key.

By pressing the button " +"below and then focusing the Variable Explorer, you will notice that Python " +"consoles are also connected to that pane, and that the Variable Explorer " +"only shows the variables of the currently focused console." +msgstr "" +"Vous pouvez également exécuter votre code sur une console Python. Ces " +"consoles sont utiles car elles vous permettent d'exécuter un fichier dans " +"une console dédiée uniquement à celui-ci. Pour sélectionner ce comportement, " +"appuyez sur la touche F6.

En appuyant sur le bouton ci-" +"dessous, puis En se concentrant sur l'Explorateur de variables, vous " +"remarquerez que les consoles Python sont également connectées à ce volet et " +"que l'Explorateur de variables ne montre que les variables de la console " +"actuellement sélectionnée." + +#: spyder/app/tour.py:195 spyder/plugins/help.py:485 spyder/plugins/help.py:929 +#: spyder/widgets/internalshell.py:270 +msgid "Help" +msgstr "Aide" + +#: spyder/app/tour.py:196 +msgid "" +"This pane displays documentation of the functions, classes, methods or " +"modules you are currently using in the Editor or the Consoles.

To use " +"it, you need to press Ctrl+I in front of an object. If that object " +"has some documentation associated with it, it will be displayed here." +msgstr "" +"Ce volet affiche la documentation des fonctions, classes, méthodes ou " +"modules que vous utilisez actuellement dans l'Editeur ou dans les Consoles. " +"

Pour l'utiliser, vous devez appuyer sur Ctrl + I D'un objet. " +"Si cet objet possède une documentation associée, il sera affiché ici." + +#: spyder/app/tour.py:206 +msgid "The File Explorer" +msgstr "Explorateur de fichiers" + +#: spyder/app/tour.py:207 +msgid "" +"This pane lets you navigate through the directories and files present in " +"your computer.

You can also open any of these files with its " +"corresponding application, by doing a double click on it.

There is " +"one exception to this rule: plain-text files will always be opened in the " +"Spyder Editor." +msgstr "" +"Ce volet vous permet de naviguer dans les répertoires et les fichiers " +"présents dans votre ordinateur.

Vous pouvez également ouvrir l'un " +"de ces fichiers avec l'application correspondante, en double-cliquant " +"dessus. Une exception à cette règle: les fichiers texte sont toujours " +"ouverts dans l'éditeur Spyder." + +#: spyder/app/tour.py:217 +msgid "The History Log" +msgstr "Historique" + +#: spyder/app/tour.py:218 +msgid "" +"This pane records all commands introduced in the Python and IPython consoles." +msgstr "" +"Ce volet enregistre toutes les commandes introduites dans les consoles " +"Python et IPython." + +#: spyder/app/tour.py:266 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" +"Spyder est un environnement de développement interactif basé sur ..." + +#: spyder/app/tour.py:270 +msgid "Welcome to Spyder introduction tour" +msgstr "Bienvenue à la visite interactive de Spyder" + +#: spyder/app/tour.py:271 +msgid "Spyder is an interactive development environment based on bla" +msgstr "Spyder est un environnement de développement interactif basé sur ..." + +#: spyder/app/tour.py:276 +msgid "Introduction tour" +msgstr "Visite d'introduction" + +#: spyder/app/tour.py:277 +msgid "New features in version 3.0" +msgstr "Les nouveautés de la version 3.0" + +#: spyder/app/tour.py:555 spyder/plugins/ipythonconsole.py:431 +msgid "Run code" +msgstr "Exécuter du code" + +#: spyder/app/tour.py:824 +msgid "Go to step: " +msgstr "Allez à l'étape: " + +#: spyder/config/base.py:249 +msgid "" +"Update LANGUAGE_CODES (inside config/base.py) if a new translation has been " +"added to Spyder" +msgstr "" +"Mettre à jour LANGUAGE_CODES (à l'intérieur de config/ base.py) si une " +"nouvelle traduction a été ajoutée à Spyder" + +#: spyder/config/ipython.py:23 +msgid "Integrate the IPython console" +msgstr "Intégrer la console IPython" + +#: spyder/config/ipython.py:25 +msgid "Manipulate Jupyter notebooks on the Editor" +msgstr "Utiliser les Jupyter Notebooks sur l'éditeur" + +#: spyder/config/utils.py:24 +msgid "Python files" +msgstr "Fichiers Python" + +#: spyder/config/utils.py:25 +msgid "Cython/Pyrex files" +msgstr "Fichiers Cython/Pyrex" + +#: spyder/config/utils.py:26 +msgid "C files" +msgstr "Fichiers C" + +#: spyder/config/utils.py:27 +msgid "C++ files" +msgstr "Fichiers C++" + +#: spyder/config/utils.py:28 +msgid "OpenCL files" +msgstr "Fichiers OpenCL" + +#: spyder/config/utils.py:29 +msgid "Fortran files" +msgstr "Fichiers Fortran" + +#: spyder/config/utils.py:30 +msgid "IDL files" +msgstr "Fichiers IDL" + +#: spyder/config/utils.py:31 +msgid "MATLAB files" +msgstr "Fichiers MATLAB" + +#: spyder/config/utils.py:32 +msgid "Julia files" +msgstr "Fichiers Julia" + +#: spyder/config/utils.py:33 +msgid "Yaml files" +msgstr "Fichiers Yaml" + +#: spyder/config/utils.py:34 +msgid "Patch and diff files" +msgstr "Fichiers patch et diff" + +#: spyder/config/utils.py:35 +msgid "Batch files" +msgstr "Fichiers Batch" + +#: spyder/config/utils.py:36 spyder/utils/iofuncs.py:426 +msgid "Text files" +msgstr "Fichiers texte" + +#: spyder/config/utils.py:37 +msgid "reStructuredText files" +msgstr "Fichiers reST" + +#: spyder/config/utils.py:38 +msgid "gettext files" +msgstr "Fichiers gettext" + +#: spyder/config/utils.py:39 +msgid "NSIS files" +msgstr "Fichiers NSIS" + +#: spyder/config/utils.py:40 +msgid "Web page files" +msgstr "Fichiers web" + +#: spyder/config/utils.py:41 +msgid "XML files" +msgstr "Fichiers XML" + +#: spyder/config/utils.py:42 +msgid "Javascript files" +msgstr "Fichiers Javascript" + +#: spyder/config/utils.py:43 +msgid "Json files" +msgstr "Fichiers Json" + +#: spyder/config/utils.py:44 +msgid "IPython notebooks" +msgstr "Documents IPython " + +#: spyder/config/utils.py:45 +msgid "Enaml files" +msgstr "Fichiers Enaml" + +#: spyder/config/utils.py:46 +msgid "Configuration files" +msgstr "Configurations" + +#: spyder/config/utils.py:51 spyder/widgets/explorer.py:712 +msgid "All files" +msgstr "Tous les fichiers" + +#: spyder/config/utils.py:121 +msgid "Supported text files" +msgstr "Fichiers compatibles" + +#: spyder/plugins/__init__.py:508 spyder/plugins/editor.py:98 +#: spyder/plugins/editor.py:545 spyder/plugins/editor.py:1729 +#: spyder/plugins/help.py:118 spyder/plugins/help.py:385 +#: spyder/widgets/editor.py:371 spyder/widgets/sourcecode/codeeditor.py:97 +#: spyder/widgets/sourcecode/codeeditor.py:3001 +msgid "Editor" +msgstr "Éditeur" + +#: spyder/plugins/configdialog.py:139 +msgid "Reset to defaults" +msgstr "Rétablir les valeurs par défaut" + +#: spyder/plugins/configdialog.py:151 +msgid "Preferences" +msgstr "Préférences" + +#: spyder/plugins/configdialog.py:491 +msgid "Invalid directory path" +msgstr "Chemin d'accès de répertoire incorrect" + +#: spyder/plugins/configdialog.py:494 spyder/plugins/configdialog.py:509 +#: spyder/plugins/runconfig.py:177 spyder/plugins/runconfig.py:241 +#: spyder/plugins/workingdirectory.py:291 spyder/widgets/explorer.py:626 +#: spyder/widgets/externalshell/pythonshell.py:616 +#: spyder/widgets/findinfiles.py:504 spyder/widgets/pathmanager.py:224 +#: spyder/widgets/projects/projectdialog.py:155 +msgid "Select directory" +msgstr "Sélectionner un répertoire" + +#: spyder/plugins/configdialog.py:521 +msgid "Invalid file path" +msgstr "Chemin d'accès de fichier incorrect" + +#: spyder/plugins/configdialog.py:524 spyder/plugins/configdialog.py:541 +msgid "Select file" +msgstr "Sélectionner un fichier" + +#: spyder/plugins/configdialog.py:540 +msgid "All files (*)" +msgstr "Tous les fichiers (*)" + +#: spyder/plugins/configdialog.py:613 spyder/widgets/formlayout.py:215 +msgid "Bold" +msgstr "Gras" + +#: spyder/plugins/configdialog.py:616 spyder/widgets/formlayout.py:210 +msgid "Italic" +msgstr "Italique" + +#: spyder/plugins/configdialog.py:670 +msgid "Font: " +msgstr "Police : " + +#: spyder/plugins/configdialog.py:676 +msgid "Size: " +msgstr "Taille : " + +#: spyder/plugins/configdialog.py:695 +msgid "Font style" +msgstr "Police d'écriture" + +#: spyder/plugins/configdialog.py:772 +msgid "Spyder needs to restart to change the following setting:" +msgstr "Il faut redémarrer Spyder pour changer l'option suivante" + +#: spyder/plugins/configdialog.py:775 +msgid "Spyder needs to restart to change the following settings:" +msgstr "Il faut redémarrer Spyder pour changer les options suivantes" + +#: spyder/plugins/configdialog.py:777 +msgid "Do you wish to restart now?" +msgstr "Souhaitez-vous redémarrer Spyder?" + +#: spyder/plugins/configdialog.py:783 +msgid "Information" +msgstr "Information" + +#: spyder/plugins/configdialog.py:797 spyder/plugins/configdialog.py:804 +#: spyder/widgets/projects/configdialog.py:74 +msgid "General" +msgstr "Général" + +#: spyder/plugins/configdialog.py:807 +msgid "Language" +msgstr "Langue" + +#: spyder/plugins/configdialog.py:810 +msgid "Use a single instance" +msgstr "Ouvrir une seule instance de Spyder" + +#: spyder/plugins/configdialog.py:812 +msgid "" +"Set this to open external
Python files in an already running instance " +"(Requires a restart)" +msgstr "" +"Activer cette option afin que les fichiers ouverts
depuis l'extérieur " +"soit affichés dans une instance unique de Spyder
(redémarrage requis)" + +#: spyder/plugins/configdialog.py:815 +msgid "Prompt when exiting" +msgstr "Demander à la sortie" + +#: spyder/plugins/configdialog.py:816 +msgid "Pop up internal console when internal errors appear" +msgstr "Afficher la console interne en cas d'erreur inattendue" + +#: spyder/plugins/configdialog.py:836 spyder/plugins/editor.py:107 +#: spyder/plugins/externalconsole.py:57 spyder/plugins/ipythonconsole.py:273 +#: spyder/widgets/projects/configdialog.py:81 +msgid "Interface" +msgstr "Interface" + +#: spyder/plugins/configdialog.py:844 +msgid "Qt windows style" +msgstr "Style de fenêtres Qt" + +#: spyder/plugins/configdialog.py:850 +msgid "Icon theme" +msgstr "Thème des icônes" + +#: spyder/plugins/configdialog.py:854 +msgid "Vertical title bars in panes" +msgstr "Barres de titre orientées verticalement" + +#: spyder/plugins/configdialog.py:856 +msgid "Vertical tabs in panes" +msgstr "Languettes verticales dans onglets" + +#: spyder/plugins/configdialog.py:858 +msgid "Animated toolbars and panes" +msgstr "Volets et barres d'outils animés" + +#: spyder/plugins/configdialog.py:860 +msgid "Tear off menus" +msgstr "Menus détachables" + +#: spyder/plugins/configdialog.py:861 +msgid "Set this to detach any
menu from the main window" +msgstr "" +"Activer cette option rend détachables tous les menus de la fenêtre principale" + +#: spyder/plugins/configdialog.py:863 +msgid "Enable high DPI scaling" +msgstr "Activer la mise à l'échèle pour les écrans à haute résolution" + +#: spyder/plugins/configdialog.py:865 +msgid "Set this for high DPI displays" +msgstr "Sélectionner pour les écrans à haute résolution" + +#: spyder/plugins/configdialog.py:866 +msgid "Custom margin for panes:" +msgstr "Marges personnalisées pour les onglets:" + +#: spyder/plugins/configdialog.py:868 spyder/plugins/editor.py:209 +msgid "pixels" +msgstr "pixels" + +#: spyder/plugins/configdialog.py:897 +msgid "Status bar" +msgstr "Barre d'état" + +#: spyder/plugins/configdialog.py:898 +msgid "Show status bar" +msgstr "Afficher la barre d'état" + +#: spyder/plugins/configdialog.py:900 +msgid "Show memory usage every" +msgstr "Afficher l'occupation mémoire toutes les" + +#: spyder/plugins/configdialog.py:902 spyder/plugins/configdialog.py:911 +#: spyder/plugins/editor.py:133 spyder/plugins/editor.py:261 +#: spyder/plugins/variableexplorer.py:30 +msgid " ms" +msgstr " ms" + +#: spyder/plugins/configdialog.py:909 +msgid "Show CPU usage every" +msgstr "Afficher la charge du CPU toutes les" + +#: spyder/plugins/configdialog.py:944 +msgid "Plain text font" +msgstr "Police d'écriture du texte brut" + +#: spyder/plugins/configdialog.py:950 +msgid "Rich text font" +msgstr "Police d'écriture du texte enrichi" + +#: spyder/plugins/configdialog.py:953 +msgid "Fonts" +msgstr "Police de caractères" + +#: spyder/plugins/configdialog.py:967 +msgid "Appearance" +msgstr "Apparence" + +#: spyder/plugins/configdialog.py:969 spyder/plugins/ipythonconsole.py:564 +msgid "Advanced Settings" +msgstr "Options avancées" + +#: spyder/plugins/configdialog.py:1005 +msgid "Syntax coloring" +msgstr "Coloration syntaxique" + +#: spyder/plugins/configdialog.py:1018 +msgid "" +"Here you can select the color scheme used in the Editor and all other Spyder " +"plugins.

You can also edit the color schemes provided by Spyder or " +"create your own ones by using the options provided below.
" +msgstr "" +"Ici, vous pouvez sélectionner le theme de couleurs utilisé dans l'éditeur et " +"tous les autres plugins Spyder.

Vous pouvez également modifier les " +"couleurs fournies par Spyder ou créer les vôtres en utilisant les options " +"fournies ci-dessous.
" + +#: spyder/plugins/configdialog.py:1023 +msgid "Edit selected" +msgstr "Modifier" + +#: spyder/plugins/configdialog.py:1024 +msgid "Create new scheme" +msgstr "Créer un nouveau thème" + +#: spyder/plugins/configdialog.py:1025 spyder/widgets/explorer.py:512 +#: spyder/widgets/projects/explorer.py:243 spyder/widgets/shell.py:134 +msgid "Delete" +msgstr "Supprimer" + +#: spyder/plugins/configdialog.py:1028 +msgid "Reset" +msgstr "Réinitialiser" + +#: spyder/plugins/configdialog.py:1035 +msgid "Scheme:" +msgstr "Thème de coloration:" + +#: spyder/plugins/configdialog.py:1066 +msgid "Manage color schemes" +msgstr "Gérer les thèmes de coloration syntaxique" + +#: spyder/plugins/configdialog.py:1255 +msgid "Are you sure you want to delete this scheme?" +msgstr "Souhaitez-vous supprimer ce thème?" + +#: spyder/plugins/configdialog.py:1372 +msgid "Text" +msgstr "Texte" + +#: spyder/plugins/configdialog.py:1374 +msgid "Highlight" +msgstr "Surligner" + +#: spyder/plugins/configdialog.py:1376 +msgid "Background" +msgstr "Fond" + +#: spyder/plugins/configdialog.py:1380 +msgid "Scheme name:" +msgstr "Nom du thème:" + +#: spyder/plugins/configdialog.py:1387 +msgid "Color scheme editor" +msgstr "Editeur de coloration syntaxique" + +#: spyder/plugins/console.py:109 +msgid "Internal console" +msgstr "Console interne" + +#: spyder/plugins/console.py:139 spyder/plugins/externalconsole.py:743 +msgid "&Run..." +msgstr "Exécute&r..." + +#: spyder/plugins/console.py:141 spyder/plugins/externalconsole.py:744 +msgid "Run a Python script" +msgstr "Exécuter un script Python" + +#: spyder/plugins/console.py:144 +msgid "Environment variables..." +msgstr "Variables d'environnement..." + +#: spyder/plugins/console.py:146 +msgid "Show and edit environment variables (for current session)" +msgstr "" +"Afficher et modifier les variables d'environnement (pour la session en cours)" + +#: spyder/plugins/console.py:150 +msgid "Show sys.path contents..." +msgstr "Afficher le contenu de sys.path..." + +#: spyder/plugins/console.py:152 +msgid "Show (read-only) sys.path" +msgstr "Afficher le contenu de sys.path (lecture seule)" + +#: spyder/plugins/console.py:155 +msgid "Buffer..." +msgstr "Tampon..." + +#: spyder/plugins/console.py:156 spyder/plugins/externalconsole.py:75 +#: spyder/plugins/history.py:43 +msgid "Set maximum line count" +msgstr "Modifier le nombre maximum de lignes" + +#: spyder/plugins/console.py:159 +msgid "External editor path..." +msgstr "Éditeur externe..." + +#: spyder/plugins/console.py:160 +msgid "Set external editor executable path" +msgstr "Modifier le chemin d'accès de l'éditeur externe" + +#: spyder/plugins/console.py:163 spyder/plugins/editor.py:139 +#: spyder/plugins/externalconsole.py:76 spyder/plugins/help.py:157 +#: spyder/plugins/help.py:360 spyder/plugins/history.py:46 +#: spyder/plugins/history.py:157 +msgid "Wrap lines" +msgstr "Retour à la ligne automatique" + +#: spyder/plugins/console.py:166 spyder/plugins/editor.py:175 +#: spyder/plugins/externalconsole.py:121 spyder/plugins/ipythonconsole.py:283 +msgid "Display balloon tips" +msgstr "Afficher des info-bulles" + +#: spyder/plugins/console.py:170 spyder/plugins/editor.py:169 +#: spyder/plugins/externalconsole.py:115 +msgid "Automatic code completion" +msgstr "Complétion de code automatique" + +#: spyder/plugins/console.py:174 spyder/plugins/editor.py:173 +#: spyder/plugins/externalconsole.py:119 +msgid "Enter key selects completion" +msgstr "Entrée valide la complétion de code" + +#: spyder/plugins/console.py:179 +msgid "Internal console settings" +msgstr "Options de la console interne" + +#: spyder/plugins/console.py:232 spyder/plugins/externalconsole.py:918 +msgid "Run Python script" +msgstr "Exécuter un script Python" + +#: spyder/plugins/console.py:233 spyder/plugins/externalconsole.py:146 +#: spyder/plugins/externalconsole.py:919 spyder/widgets/explorer.py:727 +msgid "Python scripts" +msgstr "Scripts Python" + +#: spyder/plugins/console.py:278 +msgid "Buffer" +msgstr "Tampon" + +#: spyder/plugins/console.py:279 +msgid "Maximum line count" +msgstr "Nombre maximum de lignes" + +#: spyder/plugins/console.py:289 +msgid "External editor" +msgstr "Éditeur externe" + +#: spyder/plugins/console.py:290 +msgid "External editor executable path:" +msgstr "Chemin d'accès de l'exécutable :" + +#: spyder/plugins/editor.py:104 +msgid "Edit template for new modules" +msgstr "Modifier le modèle (nouveaux modules)" + +#: spyder/plugins/editor.py:109 +msgid "Sort files according to full path" +msgstr "Classer les fichiers suivant leur chemin complet" + +#: spyder/plugins/editor.py:111 +msgid "Show tab bar" +msgstr "Afficher la barre d'onglets" + +#: spyder/plugins/editor.py:118 spyder/plugins/editor.py:189 +#: spyder/plugins/externalconsole.py:71 spyder/plugins/externalconsole.py:114 +#: spyder/plugins/help.py:156 spyder/plugins/history.py:45 +#: spyder/plugins/ipythonconsole.py:317 +msgid "Source code" +msgstr "Code source" + +#: spyder/plugins/editor.py:119 +msgid "Show line numbers" +msgstr "Afficher les numéros de ligne" + +#: spyder/plugins/editor.py:120 spyder/plugins/editor.py:947 +msgid "Show blank spaces" +msgstr "Afficher les espaces" + +#: spyder/plugins/editor.py:121 +msgid "Show vertical line after" +msgstr "Afficher une ligne verticale après" + +#: spyder/plugins/editor.py:122 +msgid "characters" +msgstr "caractères" + +#: spyder/plugins/editor.py:127 +msgid "Highlight current line" +msgstr "Surligner la ligne en cours d'édition" + +#: spyder/plugins/editor.py:129 +msgid "Highlight current cell" +msgstr "Surligner la cellule en cours d'édition" + +#: spyder/plugins/editor.py:131 +msgid "Highlight occurrences after" +msgstr "Surligner les occurrences après" + +#: spyder/plugins/editor.py:159 +msgid "Save all files before running script" +msgstr "Enregistrer tous les fichiers avant l'exécution d'un script" + +#: spyder/plugins/editor.py:162 +msgid "Run selection" +msgstr "Exécuter la sélection" + +#: spyder/plugins/editor.py:163 +msgid "Maintain focus in the Editor after running cells or selections" +msgstr "" +"Garder le focus dans l'éditeur après l'exécution d'une cellule ou d'une " +"sélection" + +#: spyder/plugins/editor.py:166 spyder/plugins/externalconsole.py:252 +msgid "Introspection" +msgstr "Introspection" + +#: spyder/plugins/editor.py:171 spyder/plugins/externalconsole.py:117 +msgid "Case sensitive code completion" +msgstr "Complétion de code sensible à la casse" + +#: spyder/plugins/editor.py:176 +msgid "Link to object definition" +msgstr "Lien vers la définition d'un objet" + +#: spyder/plugins/editor.py:178 +msgid "" +"If this option is enabled, clicking on an object\n" +"name (left-click + Ctrl key) will go this object\n" +"definition (if resolved)." +msgstr "" +"Si cette option est activée, cliquer sur le nom\n" +"d'un objet (click gauche + touche Ctrl) ira à la\n" +"définition de cet objet (si celle-ci est trouvée)." + +#: spyder/plugins/editor.py:182 +msgid "" +"Warning:
The Python module rope is not installed on this " +"computer: calltips, code completion and go-to-definition features won't be " +"available." +msgstr "" +"Avertissement :
Le module Python rope n'est pas installé " +"sur cet ordinateur : les fonctionnalités telles que la complétion de code ou " +"le lien vers la définition d'un objet ne sont donc pas accessibles." + +#: spyder/plugins/editor.py:190 +msgid "Automatic insertion of parentheses, braces and brackets" +msgstr "Insertion automatique des parenthèses, crochets et accolades" + +#: spyder/plugins/editor.py:193 +msgid "Automatic insertion of closing quotes" +msgstr "Insertion automatique de guillemets de clôture" + +#: spyder/plugins/editor.py:195 +msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" +msgstr "Insertion automatique de ':' après 'for', 'if', 'def', etc." + +#: spyder/plugins/editor.py:198 +msgid "Automatic indentation after 'else', 'elif', etc." +msgstr "Indentation automatique après 'else', 'elif', etc." + +#: spyder/plugins/editor.py:200 +msgid "Indentation characters: " +msgstr "Caractères d'indentation : " + +#: spyder/plugins/editor.py:201 +msgid "2 spaces" +msgstr "2 espaces" + +#: spyder/plugins/editor.py:202 +msgid "3 spaces" +msgstr "3 espaces" + +#: spyder/plugins/editor.py:203 +msgid "4 spaces" +msgstr "4 espaces" + +#: spyder/plugins/editor.py:204 +msgid "5 spaces" +msgstr "5 espaces" + +#: spyder/plugins/editor.py:205 +msgid "6 spaces" +msgstr "6 espaces" + +#: spyder/plugins/editor.py:206 +msgid "7 spaces" +msgstr "7 espaces" + +#: spyder/plugins/editor.py:207 +msgid "8 spaces" +msgstr "8 espaces" + +#: spyder/plugins/editor.py:208 +msgid "Tabulations" +msgstr "Tabulations" + +#: spyder/plugins/editor.py:209 +msgid "Tab stop width:" +msgstr "Largeur des tabulations :" + +#: spyder/plugins/editor.py:211 +msgid "Tab always indent" +msgstr "Toujours indenter avec la touche Tab" + +#: spyder/plugins/editor.py:213 +msgid "" +"If enabled, pressing Tab will always indent,\n" +"even when the cursor is not at the beginning\n" +"of a line (when this option is enabled, code\n" +"completion may be triggered using the alternate\n" +"shortcut: Ctrl+Space)" +msgstr "" +"Si cette option est activée, presser la touche Tab\n" +"provoquera toujours l'indentation de la ligne,\n" +"quelle que soit la position du curseur (lorsque cette\n" +"option est activée, la complétion de code reste \n" +"accessible via le raccourci Ctrl+Espace)" + +#: spyder/plugins/editor.py:218 +msgid "Intelligent backspace" +msgstr "Retour arrière (\"backspace\") intelligent" + +#: spyder/plugins/editor.py:220 +msgid "Automatically remove trailing spaces when saving files" +msgstr "" +"Supprimer automatiquement les espaces en fin de ligne lors de " +"l'enregistrement" + +#: spyder/plugins/editor.py:224 +msgid "Analysis" +msgstr "Analyse" + +#: spyder/plugins/editor.py:226 +msgid "(Refer to the {} page)" +msgstr "(référer à la page {})" + +#: spyder/plugins/editor.py:230 +msgid "Real-time code analysis" +msgstr "Analyse de code en temps réel dans l'éditeur" + +#: spyder/plugins/editor.py:232 +msgid "" +"

If enabled, Python source code will be analyzed using pyflakes, lines " +"containing errors or warnings will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore code analysis warnings.

" +msgstr "" +"

Si cette option est activée, le code source Python sera analysé avec des " +"outils l'outil d'introspection de code pyflakes, et les lignes contenant des " +"erreurs seront indiquées.

Note: rajouter analysis:ignore " +"dans un commentaire pour ignorer les avertissements d'analyse de code.

" + +#: spyder/plugins/editor.py:240 +msgid "Code analysis requires pyflakes %s+" +msgstr "L'analyse de code requiert pyflakes %s+" + +#: spyder/plugins/editor.py:242 +msgid "Real-time code style analysis" +msgstr "Analyse de code en temps réel dans l'éditeur" + +#: spyder/plugins/editor.py:244 +msgid "" +"

If enabled, Python source code will be analyzedusing pep8, lines that are " +"not following PEP8 style guide will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore style analysis warnings.

" +msgstr "" +"

Si cette option est activée, le style du code source Python sera analysé " +"avec l'outil d'introspection de code pep8 et les lignes ne suivant pas les " +"recommandations officielles seront indiquées.

Note: rajouter " +"analysis:ignore dans un commentaire pour ignorer les avertissements " +"d'analyse de code.

" + +#: spyder/plugins/editor.py:251 +msgid "Code annotations (TODO, FIXME, XXX, HINT, TIP, @todo)" +msgstr "Tâches (TODO, FIXME, XXX, HINT, TIP, @todo)" + +#: spyder/plugins/editor.py:254 +msgid "Perform analysis when saving file and every" +msgstr "Analyser lors de l'enregistrement et toutes les" + +#: spyder/plugins/editor.py:258 +msgid "Perform analysis only when saving file" +msgstr "Analyser uniquement lors de l'enregistrement" + +#: spyder/plugins/editor.py:317 +msgid "End-of-line characters" +msgstr "Caractères de fin de ligne" + +#: spyder/plugins/editor.py:318 +msgid "" +"When opening a text file containing mixed end-of-line characters (this may " +"raise syntax errors in the consoles on Windows platforms), Spyder may fix " +"the file automatically." +msgstr "" +"Lors de l'ouverture d'un fichier contenant différents types de caractères de " +"fin de ligne (ce qui peut se traduire par des erreurs de syntaxe dans les " +"consoles Python sous Windows), Spyder peut réparer le fichier " +"automatiquement." + +#: spyder/plugins/editor.py:324 +msgid "Fix automatically and show warning message box" +msgstr "Réparer automatiquement et afficher un message d'avertissement" + +#: spyder/plugins/editor.py:335 spyder/plugins/externalconsole.py:250 +#: spyder/plugins/ipythonconsole.py:558 spyder/plugins/variableexplorer.py:43 +msgid "Display" +msgstr "Affichage" + +#: spyder/plugins/editor.py:337 +msgid "Code Introspection/Analysis" +msgstr "Introspection et analyse de code" + +#: spyder/plugins/editor.py:340 spyder/plugins/externalconsole.py:253 +msgid "Advanced settings" +msgstr "Options avancées" + +#: spyder/plugins/editor.py:613 spyder/widgets/editortools.py:510 +msgid "Show/hide outline explorer" +msgstr "Afficher/masquer l'explorateur de structure" + +#: spyder/plugins/editor.py:619 +msgid "Show/hide project explorer" +msgstr "Afficher/masquer l'explorateur de projets" + +#: spyder/plugins/editor.py:627 +msgid "&New file..." +msgstr "&Nouveau fichier..." + +#: spyder/plugins/editor.py:628 spyder/plugins/workingdirectory.py:83 +#: spyder/widgets/explorer.py:704 spyder/widgets/explorer.py:711 +msgid "New file" +msgstr "Nouveau fichier" + +#: spyder/plugins/editor.py:634 +msgid "&Open..." +msgstr "&Ouvrir..." + +#: spyder/plugins/editor.py:635 spyder/plugins/editor.py:1778 +#: spyder/plugins/workingdirectory.py:70 +msgid "Open file" +msgstr "Ouvrir un fichier" + +#: spyder/plugins/editor.py:641 spyder/widgets/editor.py:347 +msgid "File switcher..." +msgstr "Commutateur de fichiers..." + +#: spyder/plugins/editor.py:643 +msgid "Fast switch between files" +msgstr "Commutateur de fichiers" + +#: spyder/plugins/editor.py:649 +msgid "&Revert" +msgstr "&Réinitialiser" + +#: spyder/plugins/editor.py:650 +msgid "Revert file from disk" +msgstr "Revenir à la version du fichier enregistrée sur le disque" + +#: spyder/plugins/editor.py:653 +msgid "&Save" +msgstr "&Enregistrer" + +#: spyder/plugins/editor.py:654 spyder/widgets/editor.py:1306 +msgid "Save file" +msgstr "Enregitrer un fichier" + +#: spyder/plugins/editor.py:660 +msgid "Sav&e all" +msgstr "Enregistrer &tout" + +#: spyder/plugins/editor.py:661 +msgid "Save all files" +msgstr "Enregistrer tous les fichiers" + +#: spyder/plugins/editor.py:667 +msgid "Save &as..." +msgstr "Enregistrer &sous..." + +#: spyder/plugins/editor.py:668 +msgid "Save current file as..." +msgstr "Enregistrer le fichier en cours d'édition sous un autre nom..." + +#: spyder/plugins/editor.py:673 spyder/plugins/editor.py:674 +msgid "Print preview..." +msgstr "Aperçu avant impression..." + +#: spyder/plugins/editor.py:675 +msgid "&Print..." +msgstr "Im&primer..." + +#: spyder/plugins/editor.py:676 +msgid "Print current file..." +msgstr "Imprimer le fichier en cours d'édition..." + +#: spyder/plugins/editor.py:679 +msgid "&Close" +msgstr "&Fermer" + +#: spyder/plugins/editor.py:680 +msgid "Close current file" +msgstr "Fermer le fichier en cours d'édition" + +#: spyder/plugins/editor.py:683 +msgid "C&lose all" +msgstr "Fermer t&out" + +#: spyder/plugins/editor.py:684 +msgid "Close all opened files" +msgstr "Fermer tous les fichiers ouverts" + +#: spyder/plugins/editor.py:691 +msgid "&Find text" +msgstr "Rec&hercher" + +#: spyder/plugins/editor.py:697 +msgid "Find &next" +msgstr "Rechercher le &suivant" + +#: spyder/plugins/editor.py:703 +msgid "Find &previous" +msgstr "Rechercher le &précédent" + +#: spyder/plugins/editor.py:709 +msgid "&Replace text" +msgstr "&Remplacer" + +#: spyder/plugins/editor.py:718 +msgid "Set/Clear breakpoint" +msgstr "Ajouter/supprimer un point d'arrêt" + +#: spyder/plugins/editor.py:725 +msgid "Set/Edit conditional breakpoint" +msgstr "Ajouter/modifier un point d'arrêt conditionnel" + +#: spyder/plugins/editor.py:732 +msgid "Clear breakpoints in all files" +msgstr "Supprimer les points d'arrêt dans tous les fichiers" + +#: spyder/plugins/editor.py:734 +msgid "Debug with winpdb" +msgstr "Déboguer avec winpdb" + +#: spyder/plugins/editor.py:741 +msgid "Debug file" +msgstr "Déboguer le script" + +#: spyder/plugins/editor.py:746 +msgid "Step" +msgstr "Pas" + +#: spyder/plugins/editor.py:747 +msgid "Run current line" +msgstr "Exécuter la ligne en cours" + +#: spyder/plugins/editor.py:752 +msgid "Continue" +msgstr "Continuer" + +#: spyder/plugins/editor.py:754 +msgid "Continue execution until next breakpoint" +msgstr "Continuer l'exécution jusqu'au prochain point d'arrêt" + +#: spyder/plugins/editor.py:759 +msgid "Step Into" +msgstr "Pas vers l'intérieur" + +#: spyder/plugins/editor.py:761 +msgid "Step into function or method of current line" +msgstr "" +"Avancer dans la fonction, méthode \n" +"ou classe de la ligne en cours" + +#: spyder/plugins/editor.py:766 +msgid "Step Return" +msgstr "Pas vers l'extérieur" + +#: spyder/plugins/editor.py:768 +msgid "Run until current function or method returns" +msgstr "Exécuter jusqu'au retour de la fonction ou méthode" + +#: spyder/plugins/editor.py:773 spyder/widgets/findinfiles.py:333 +#: spyder/widgets/ipythonconsole/client.py:238 +msgid "Stop" +msgstr "Arrêter" + +#: spyder/plugins/editor.py:774 +msgid "Stop debugging" +msgstr "Arrêter le débogage" + +#: spyder/plugins/editor.py:781 +msgid "Run file" +msgstr "Exécuter le fichier" + +#: spyder/plugins/editor.py:786 +msgid "&Configure..." +msgstr "&Configurer..." + +#: spyder/plugins/editor.py:788 spyder/widgets/externalshell/pythonshell.py:304 +msgid "Run settings" +msgstr "Options d'exécution" + +#: spyder/plugins/editor.py:794 +msgid "Re-run &last script" +msgstr "Exécuter de nouveau le &dernier script" + +#: spyder/plugins/editor.py:796 +msgid "Run again last file" +msgstr "Exécuter de nouveau le dernier fichier" + +#: spyder/plugins/editor.py:802 spyder/widgets/sourcecode/codeeditor.py:2548 +msgid "Run &selection or current line" +msgstr "Exécuter la &sélection ou la ligne courante" + +#: spyder/plugins/editor.py:805 +msgid "Run selection or current line" +msgstr "Exécuter la sélection ou le bloc de lignes" + +#: spyder/plugins/editor.py:813 spyder/widgets/sourcecode/codeeditor.py:2540 +msgid "Run cell" +msgstr "Exécuter la cellule" + +#: spyder/plugins/editor.py:816 +msgid "" +"Run current cell (Ctrl+Enter)\n" +"[Use #%% to create cells]" +msgstr "" +"Exécuter la cellule courante \n" +"[Utiliser #%% pour délimiter les cellules]" + +#: spyder/plugins/editor.py:822 spyder/widgets/sourcecode/codeeditor.py:2544 +msgid "Run cell and advance" +msgstr "Exécuter la cellule et avancer" + +#: spyder/plugins/editor.py:825 +msgid "Run current cell and go to the next one (Shift+Enter)" +msgstr "" +"Exécuter la cellule en cours d'édition et aller à la suivante\n" +"(voir la documentation de l'Editeur, pour plus de détails sur les cellules)" + +#: spyder/plugins/editor.py:832 +msgid "Show todo list" +msgstr "Afficher la liste des tâches" + +#: spyder/plugins/editor.py:833 +msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" +msgstr "" +"Afficher la liste des commentaires du type TODO/FIXME/XXX/HINT/TIP/@todo" + +#: spyder/plugins/editor.py:840 +msgid "Show warning/error list" +msgstr "Afficher la liste des avertissements/erreurs" + +#: spyder/plugins/editor.py:841 +msgid "Show code analysis warnings/errors" +msgstr "" +"Afficher la liste des avertissements/erreurs provenant de l'analyse de code" + +#: spyder/plugins/editor.py:847 +msgid "Previous warning/error" +msgstr "Avertissement suivant" + +#: spyder/plugins/editor.py:848 +msgid "Go to previous code analysis warning/error" +msgstr "Afficher le message d'avertissement ou d'erreur suivant" + +#: spyder/plugins/editor.py:851 +msgid "Next warning/error" +msgstr "Avertissement précédent" + +#: spyder/plugins/editor.py:852 +msgid "Go to next code analysis warning/error" +msgstr "Afficher le message d'avertissement ou d'erreur précédent" + +#: spyder/plugins/editor.py:856 +msgid "Last edit location" +msgstr "Dernière position d'édition" + +#: spyder/plugins/editor.py:857 +msgid "Go to last edit location" +msgstr "Aller à la dernière position d'édition" + +#: spyder/plugins/editor.py:865 +msgid "Previous cursor position" +msgstr "Position suivante du curseur" + +#: spyder/plugins/editor.py:866 +msgid "Go to previous cursor position" +msgstr "Aller à la position précédente du curseur" + +#: spyder/plugins/editor.py:874 +msgid "Next cursor position" +msgstr "Position suivante du curseur" + +#: spyder/plugins/editor.py:875 +msgid "Go to next cursor position" +msgstr "Aller à la position suivante du curseur" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Comment" +msgstr "Commenter" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Uncomment" +msgstr "Décommenter" + +#: spyder/plugins/editor.py:886 +msgid "Comment current line or selection" +msgstr "Commenter la sélection ou la ligne en cours d'édition" + +#: spyder/plugins/editor.py:890 +msgid "Add &block comment" +msgstr "Ajouter un &bloc de commentaires" + +#: spyder/plugins/editor.py:891 +msgid "Add block comment around current line or selection" +msgstr "" +"Ajouter un bloc de commentaires autour de la sélection ou de la ligne en " +"cours d'édition" + +#: spyder/plugins/editor.py:897 +msgid "R&emove block comment" +msgstr "&Supprimer un bloc de commentaires" + +#: spyder/plugins/editor.py:898 +msgid "Remove comment block around current line or selection" +msgstr "" +"Supprimer le bloc de commentaires autour de la ligne en cours d'édition" + +#: spyder/plugins/editor.py:909 +msgid "Indent" +msgstr "Indenter" + +#: spyder/plugins/editor.py:910 +msgid "Indent current line or selection" +msgstr "Indenter la sélection ou la ligne en cours d'édition" + +#: spyder/plugins/editor.py:913 +msgid "Unindent" +msgstr "Désindenter" + +#: spyder/plugins/editor.py:914 +msgid "Unindent current line or selection" +msgstr "Désindenter la sélection ou la ligne en cours d'édition" + +#: spyder/plugins/editor.py:918 +msgid "Toggle Uppercase" +msgstr "Convertir en majuscule" + +#: spyder/plugins/editor.py:919 +msgid "Change to uppercase current line or selection" +msgstr "Convertir en majuscule la sélection ou le mot en cours d'édition" + +#: spyder/plugins/editor.py:923 +msgid "Toggle Lowercase" +msgstr "Convertir en minuscule" + +#: spyder/plugins/editor.py:924 +msgid "Change to lowercase current line or selection" +msgstr "Convertir en minuscule la sélection ou le mot en cours d'édition" + +#: spyder/plugins/editor.py:929 +msgid "Carriage return and line feed (Windows)" +msgstr "Retour chariot et retour à la ligne (Windows)" + +#: spyder/plugins/editor.py:932 +msgid "Line feed (UNIX)" +msgstr "Retour à la ligne (UNIX)" + +#: spyder/plugins/editor.py:935 +msgid "Carriage return (Mac)" +msgstr "Retour chariot (Mac)" + +#: spyder/plugins/editor.py:941 +msgid "Convert end-of-line characters" +msgstr "Convertir les caractères de fin de ligne" + +#: spyder/plugins/editor.py:945 +msgid "Remove trailing spaces" +msgstr "Supprimer les espaces en fin de ligne" + +#: spyder/plugins/editor.py:949 +msgid "Fix indentation" +msgstr "Corriger l'indentation" + +#: spyder/plugins/editor.py:950 +msgid "Replace tab characters by space characters" +msgstr "Remplacer les caractères de tabulation par des espaces" + +#: spyder/plugins/editor.py:953 +msgid "Go to line..." +msgstr "Aller à la ligne..." + +#: spyder/plugins/editor.py:961 +msgid "Set console working directory" +msgstr "Répertoire de travail de la console" + +#: spyder/plugins/editor.py:963 +msgid "" +"Set current console (and file explorer) working directory to current script " +"directory" +msgstr "" +"Choisir le répertoire du script comme répertoire de travail de la console " +"courante (et de l'explorateur de fichier)" + +#: spyder/plugins/editor.py:968 +msgid "Maximum number of recent files..." +msgstr "Nombre maximum de fichiers récents..." + +#: spyder/plugins/editor.py:971 +msgid "Clear recent files list" +msgstr "Effacer la liste des fichiers récents" + +#: spyder/plugins/editor.py:971 spyder/plugins/projects.py:100 +msgid "Clear this list" +msgstr "Effacer cette liste" + +#: spyder/plugins/editor.py:975 +msgid "Open &recent" +msgstr "Fichiers &récents" + +#: spyder/plugins/editor.py:1359 +msgid "?" +msgstr "?" + +#: spyder/plugins/editor.py:1586 +msgid "Spyder Editor" +msgstr "Éditeur de Spyder" + +#: spyder/plugins/editor.py:1587 +msgid "This is a temporary script file." +msgstr "Ceci est un script temporaire." + +#: spyder/plugins/editor.py:1656 +msgid "untitled" +msgstr "sanstitre" + +#: spyder/plugins/editor.py:1730 +msgid "Maximum number of recent files" +msgstr "Nombre maximum de fichiers récents" + +#: spyder/plugins/editor.py:1863 +msgid "Printing..." +msgstr "Impression en cours..." + +#: spyder/plugins/explorer.py:53 +msgid "File explorer" +msgstr "Explorateur de fichiers" + +#: spyder/plugins/externalconsole.py:46 +msgid "Interactive data plotting in the consoles" +msgstr "Visualisation interactive de données" + +#: spyder/plugins/externalconsole.py:54 spyder/plugins/externalconsole.py:709 +msgid "Python console" +msgstr "Console Python" + +#: spyder/plugins/externalconsole.py:59 +msgid "One tab per script" +msgstr "Un onglet par script" + +#: spyder/plugins/externalconsole.py:60 +#: spyder/widgets/externalshell/baseshell.py:160 +msgid "Show elapsed time" +msgstr "Afficher le temps écoulé" + +#: spyder/plugins/externalconsole.py:61 spyder/widgets/explorer.py:1094 +msgid "Show icons and text" +msgstr "Afficher icônes et textes" + +#: spyder/plugins/externalconsole.py:73 +msgid "Buffer: " +msgstr "Tampon : " + +#: spyder/plugins/externalconsole.py:73 spyder/plugins/ipythonconsole.py:319 +msgid " lines" +msgstr " lignes" + +#: spyder/plugins/externalconsole.py:78 +msgid "Merge process standard output/error channels" +msgstr "Fusionner les canaux de sortie et d'erreur du processus" + +#: spyder/plugins/externalconsole.py:80 +msgid "" +"Merging the output channels of the process means that\n" +"the standard error won't be written in red anymore,\n" +"but this has the effect of speeding up display." +msgstr "" +"Fusionner les canaux de sortie et d'erreur du processus\n" +"signifie que les erreurs ne seront plus affichées en rouge,\n" +"mais cela entraînera également une amélioration des performances\n" +"d'affichage et une meilleure réactivité de la console." + +#: spyder/plugins/externalconsole.py:84 +msgid "Colorize standard error channel using ANSI escape codes" +msgstr "Coloriser le canal d'erreur standard (codes d'échappement ANSI)" + +#: spyder/plugins/externalconsole.py:86 +msgid "" +"This method is the only way to have colorized standard\n" +"error channel when the output channels have been merged." +msgstr "" +"Cette méthode est le seul moyen de coloriser le canal\n" +"d'erreur standard lorsque les canaux de sorties ont été fusionnés." + +#: spyder/plugins/externalconsole.py:102 spyder/plugins/ipythonconsole.py:306 +#: spyder/widgets/variableexplorer/arrayeditor.py:540 +#: spyder/widgets/variableexplorer/dataframeeditor.py:552 +msgid "Background color" +msgstr "Couleur de fond" + +#: spyder/plugins/externalconsole.py:103 +msgid "" +"This option will be applied the next time a Python console or a terminal is " +"opened." +msgstr "" +"Cette option sera prise en compte lors de la prochaine ouverture de console " +"(interpréteur Python ou terminal)." + +#: spyder/plugins/externalconsole.py:106 +msgid "Light background (white color)" +msgstr "Fond blanc" + +#: spyder/plugins/externalconsole.py:131 +msgid "PYTHONSTARTUP replacement" +msgstr "Substitution de PYTHONSTARTUP" + +#: spyder/plugins/externalconsole.py:133 +msgid "" +"This option will override the PYTHONSTARTUP environment variable which\n" +"defines the script to be executed during the Python console startup." +msgstr "" +"Cette option permet de remplacer le script de démarrage des consoles Python " +"défini par la\n" +"variable d'environnement PYTHONSTARTUP." + +#: spyder/plugins/externalconsole.py:138 +msgid "Default PYTHONSTARTUP script" +msgstr "Script PYTHONSTARTUP par défaut" + +#: spyder/plugins/externalconsole.py:142 +msgid "Use the following startup script:" +msgstr "Utiliser le script de démarrage suivant :" + +#: spyder/plugins/externalconsole.py:159 +msgid "Monitor" +msgstr "Moniteur" + +#: spyder/plugins/externalconsole.py:160 +msgid "" +"The monitor provides introspection features to console: code completion, " +"calltips and variable explorer. Because it relies on several modules, " +"disabling the monitor may be useful to accelerate console startup." +msgstr "" +"Le moniteur fournit à la console des fonctionnalités d'introspection telles " +"que la complétion de code, les info-bulles et l'explorateur de variables. " +"Parce qu'il nécessite l'import de nombreux modules, désactiver le moniteur " +"permet d'accélérer le démarrage de la console." + +#: spyder/plugins/externalconsole.py:167 +msgid "Enable monitor" +msgstr "Activer le moniteur" + +#: spyder/plugins/externalconsole.py:180 +msgid "Default library" +msgstr "Bibliothèque par défaut" + +#: spyder/plugins/externalconsole.py:185 +msgid "Qt-Python Bindings" +msgstr "Sélection de la bibliothèque d'interfaçage Qt" + +#: spyder/plugins/externalconsole.py:187 +msgid "Library:" +msgstr "Module:" + +#: spyder/plugins/externalconsole.py:189 +msgid "" +"This option will act on
libraries such as Matplotlib, guidata or ETS" +msgstr "" +"Cette option est prise en charge par les bibliothèques telles que " +"Matplotlib, guidata ou ETS" + +#: spyder/plugins/externalconsole.py:198 spyder/plugins/ipythonconsole.py:560 +msgid "Graphics" +msgstr "Graphiques" + +#: spyder/plugins/externalconsole.py:199 +msgid "" +"Decide which backend to use to display graphics. If unsure, please select " +"the Automatic backend.

Note: We support a very limited " +"number of backends in our Python consoles. If you prefer to work with a " +"different one, please use an IPython console." +msgstr "" +"Décidez quel backend utiliser pour afficher les graphiques. Si vous n'êtes " +"pas sûr, sélectionnez le backend Automatique.
Remarque: " +"Nous soutenons un nombre très limité de backends dans nos consoles Python. " +"Si vous préférez travailler avec un autre, utilisez une console IPython." + +#: spyder/plugins/externalconsole.py:208 +msgid "None" +msgstr "Aucun" + +#: spyder/plugins/externalconsole.py:208 spyder/plugins/ipythonconsole.py:351 +msgid "Automatic" +msgstr "Automatique" + +#: spyder/plugins/externalconsole.py:213 spyder/plugins/ipythonconsole.py:373 +msgid "Backend:" +msgstr "Sortie :" + +#: spyder/plugins/externalconsole.py:215 spyder/plugins/ipythonconsole.py:375 +msgid "This option will be applied the next time a console is opened." +msgstr "" +"Cette option sera prise en compte lors de la prochaine ouverture de console." + +#: spyder/plugins/externalconsole.py:226 +msgid "Enthought Tool Suite" +msgstr "Enthought Tool Suite" + +#: spyder/plugins/externalconsole.py:227 +msgid "" +"Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical " +"user interfaces." +msgstr "" +"Le logiciel Enthought Tool Suite (ETS) prend en charge les interfaces " +"graphiques PyQt4 (qt4) et wxPython (wx)." + +#: spyder/plugins/externalconsole.py:231 +msgid "ETS_TOOLKIT:" +msgstr "ETS_TOOLKIT:" + +#: spyder/plugins/externalconsole.py:255 +msgid "External modules" +msgstr "Modules externes" + +#: spyder/plugins/externalconsole.py:447 +msgid "" +"No Python console is currently selected to run %s.

Please " +"select or open a new Python console and try again." +msgstr "" +"Aucune console Python n'est actuellement sélectionnée pour exécuter %s.

Merci de sélectionner ou d'ouvrir une nouvelle console Python et " +"de réessayer." + +#: spyder/plugins/externalconsole.py:518 +msgid "" +"%s is already running in a separate process.\n" +"Do you want to kill the process before starting a new one?" +msgstr "" +"%s est déjà en cours d'exécution dans un processus séparé.\n" +"Souhaitez-vous tuer ce processus avant d'en démarrer un autre ?" + +#: spyder/plugins/externalconsole.py:647 +msgid "Command Window" +msgstr "Invite de commandes" + +#: spyder/plugins/externalconsole.py:649 spyder/plugins/ipythonconsole.py:297 +msgid "Terminal" +msgstr "Terminal" + +#: spyder/plugins/externalconsole.py:731 +msgid "Open a &Python console" +msgstr "Ouvrir une console &Python" + +#: spyder/plugins/externalconsole.py:735 +msgid "Open &command prompt" +msgstr "Ouvrir un invite de &commandes" + +#: spyder/plugins/externalconsole.py:736 +msgid "Open a Windows command prompt" +msgstr "Ouvrir un invite de commandes Windows" + +#: spyder/plugins/externalconsole.py:738 +msgid "Open a &terminal" +msgstr "Ouvrir un &terminal" + +#: spyder/plugins/externalconsole.py:739 +msgid "Open a terminal window" +msgstr "Ouvrir un terminal de commandes dans Spyder" + +#: spyder/plugins/findinfiles.py:127 spyder/widgets/findinfiles.py:689 +msgid "Find in files" +msgstr "Recherche dans des fichiers" + +#: spyder/plugins/findinfiles.py:148 +msgid "&Find in files" +msgstr "Rechercher dans des &fichiers" + +#: spyder/plugins/findinfiles.py:151 +msgid "Search text in multiple files" +msgstr "Rechercher une chaîne de caractères dans plusieurs fichiers à la fois" + +#: spyder/plugins/help.py:44 +msgid "Show help for objects in the Editor and Consoles in a dedicated pane" +msgstr "" +"Afficher l'aide pour les objets dans l'éditeur et les consoles dans un volet " +"dédié" + +#: spyder/plugins/help.py:110 +msgid "Automatic connections" +msgstr "Connexions automatiques" + +#: spyder/plugins/help.py:111 +msgid "" +"This pane can automatically show an object's help information after a left " +"parenthesis is written next to it. Below you can decide to which plugin you " +"want to connect it to turn on this feature." +msgstr "" +"L'inspecteur d'objet peut automatiquement afficher l'aide sur un objet dès " +"l'ouverture d'une parenthèse. Ci-dessous vous pouvez décider à quel plugin " +"vous voulez le connecter pour activer cette fonctionnalité." + +#: spyder/plugins/help.py:123 +msgid "" +"This feature requires the Rope or Jedi libraries.\n" +"It seems you don't have either installed." +msgstr "" +"Cette fonctionnalité nécessite Rope ou Jedi.\n" +"Il semble qu'aucun de ces modules ne soit présent." + +#: spyder/plugins/help.py:126 +msgid "Python Console" +msgstr "Console Python" + +#: spyder/plugins/help.py:128 +msgid "IPython Console" +msgstr "Console IPython" + +#: spyder/plugins/help.py:140 +msgid "Additional features" +msgstr "Options supplémentaires" + +#: spyder/plugins/help.py:141 +msgid "Render mathematical equations" +msgstr "Styliser les équations mathématiques" + +#: spyder/plugins/help.py:147 +msgid "This feature requires Sphinx 1.1 or superior." +msgstr "Cette fonctionnalité nécessite l'installation de Sphinx 1.1+." + +#: spyder/plugins/help.py:148 +msgid "Sphinx %s is currently installed." +msgstr "Sphinx %s n'est pas installé." + +#: spyder/plugins/help.py:309 +msgid "No further documentation available" +msgstr "Aucune documentation disponible" + +#: spyder/plugins/help.py:347 +msgid "No documentation available" +msgstr "Aucune documentation disponible" + +#: spyder/plugins/help.py:378 +msgid "Source" +msgstr "Source" + +#: spyder/plugins/help.py:385 spyder/plugins/runconfig.py:186 +#: spyder/plugins/runconfig.py:456 spyder/widgets/externalshell/baseshell.py:94 +#: spyder/widgets/ipythonconsole/client.py:202 +msgid "Console" +msgstr "Console" + +#: spyder/plugins/help.py:393 +#: spyder/widgets/variableexplorer/collectionseditor.py:133 +msgid "Object" +msgstr "Objet" + +#: spyder/plugins/help.py:407 +msgid "Plain Text" +msgstr "Texte brut" + +#: spyder/plugins/help.py:411 +msgid "Show Source" +msgstr "Afficher les sources" + +#: spyder/plugins/help.py:415 +msgid "Rich Text" +msgstr "Texte enrichi" + +#: spyder/plugins/help.py:425 +msgid "Automatic import" +msgstr "Import automatique" + +#: spyder/plugins/help.py:437 spyder/plugins/history.py:106 +#: spyder/widgets/editor.py:504 spyder/widgets/explorer.py:1105 +#: spyder/widgets/externalshell/baseshell.py:140 +#: spyder/widgets/ipythonconsole/client.py:251 +#: spyder/widgets/variableexplorer/namespacebrowser.py:160 +msgid "Options" +msgstr "Options" + +#: spyder/plugins/help.py:695 +msgid "" +"Here you can get help of any object by pressing %s in front of it, either on " +"the Editor or the Console.%sHelp can also be shown automatically after " +"writing a left parenthesis next to an object. You can activate this behavior " +"in %s." +msgstr "" +"Pour obtenir de l'aide ici, sélectionner (ou placer le curseur sur) un objet " +"dans l'éditeur ou la console, puis appuyer sur %s.%sL'aide apparaît " +"automatiquement après la saisie d'une parenthèse gauche après un objet si " +"l'option correspondante est activée dans %s." + +#: spyder/plugins/help.py:701 +msgid "Preferences > Help" +msgstr "Préférences > Aide" + +#: spyder/plugins/help.py:708 +msgid "Usage" +msgstr "Utilisation" + +#: spyder/plugins/help.py:709 +msgid "New to Spyder? Read our" +msgstr "Vous découvrez Spyder ? Lisez notre" + +#: spyder/plugins/help.py:710 +msgid "tutorial" +msgstr "Tutoriel" + +#: spyder/plugins/help.py:717 +msgid "" +"Please consider installing Sphinx to get documentation rendered in rich text." +msgstr "" +"Merci d'installer Sphinx pour obtenir la documentation en texte enrichi." + +#: spyder/plugins/help.py:886 +msgid "Lock" +msgstr "Verrouiller" + +#: spyder/plugins/help.py:886 +msgid "Unlock" +msgstr "Déverrouiller" + +#: spyder/plugins/help.py:930 +msgid "" +"The following error occured when calling Sphinx %s.
Incompatible " +"Sphinx version or doc string decoding failed.

Error message:
%s" +msgstr "" +"L'erreur suivante s'est produite lors de l'exécution de Sphinx %s. " +"
Veuillez vérifier si cette version de Sphinx est bien prise en charge " +"par Spyder.

Message d'erreur :
%s" + +#: spyder/plugins/help.py:974 +msgid "No source code available." +msgstr "Aucun code source disponible." + +#: spyder/plugins/history.py:39 +msgid "Settings" +msgstr "Options" + +#: spyder/plugins/history.py:41 +msgid " entries" +msgstr " lignes" + +#: spyder/plugins/history.py:41 +msgid "History depth: " +msgstr "Taille de l'historique : " + +#: spyder/plugins/history.py:48 +msgid "Scroll automatically to last entry" +msgstr "Défiler automatiquement jusqu'à la dernière ligne" + +#: spyder/plugins/history.py:126 +msgid "History log" +msgstr "Historique" + +#: spyder/plugins/history.py:153 +msgid "History..." +msgstr "Historique..." + +#: spyder/plugins/history.py:155 +msgid "Set history maximum entries" +msgstr "Modifier le nombre d'entrées maximum de l'historique" + +#: spyder/plugins/history.py:260 +msgid "History" +msgstr "Historique" + +#: spyder/plugins/history.py:261 +msgid "Maximum entries" +msgstr "Nombre maximum d'entrées" + +#: spyder/plugins/ipythonconsole.py:64 +msgid "Symbolic mathematics in the IPython Console" +msgstr "Mathématiques symboliques pour la console IPython" + +#: spyder/plugins/ipythonconsole.py:116 +msgid "" +"The authenticity of host %s can't be established. Are you sure you " +"want to continue connecting?" +msgstr "" +"L'identité du serveur %s ne peut pas être confirmée. Êtes-vous sûr de " +"vouloir poursuivre ?" + +#: spyder/plugins/ipythonconsole.py:128 +msgid "The authenticity of the host can't be established" +msgstr "L'identité du serveur ne peut pas être confirmée" + +#: spyder/plugins/ipythonconsole.py:135 +msgid "Tunnel '%s' failed to start" +msgstr "Impossible d'ouvrir le tunnel '%s'" + +#: spyder/plugins/ipythonconsole.py:140 +msgid "Could not connect to remote host" +msgstr "Impossible d'établir la connection au serveur distant" + +#: spyder/plugins/ipythonconsole.py:157 spyder/plugins/ipythonconsole.py:747 +msgid "Connect to an existing kernel" +msgstr "Connecter à un noyau existant" + +#: spyder/plugins/ipythonconsole.py:159 +msgid "" +"Please enter the connection info of the kernel you want to connect to. For " +"that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for " +"example kernel-3764.json or just 3764)." +msgstr "" +"Entrez les informations de connexion du noyau auquel vous voulez vous " +"connecter. Pour cela vous pouvez soit sélectionner son fichier de connexion " +"JSON en utilisant le bouton Parcourir, ou écrivez directement son " +"identifiant si c'est un noyau local (Exemple : kernel-3764.json ou " +"juste 3764)." + +#: spyder/plugins/ipythonconsole.py:170 +msgid "Connection info:" +msgstr "Information de connexion :" + +#: spyder/plugins/ipythonconsole.py:172 +msgid "Path to connection file or kernel id" +msgstr "Chemin vers un fichier de connexion ou identifiant de noyau" + +#: spyder/plugins/ipythonconsole.py:174 spyder/plugins/ipythonconsole.py:191 +msgid "Browse" +msgstr "Parcourir" + +#: spyder/plugins/ipythonconsole.py:183 +msgid "This is a remote kernel" +msgstr "Noyau distant" + +#: spyder/plugins/ipythonconsole.py:187 +msgid "username@hostname:port" +msgstr "utilisateur@hôte:port" + +#: spyder/plugins/ipythonconsole.py:190 +msgid "Path to ssh key file" +msgstr "Chemin vers la clé ssh" + +#: spyder/plugins/ipythonconsole.py:199 +msgid "Password or ssh key passphrase" +msgstr "Mot de passe ou passphrase de clé ssh" + +#: spyder/plugins/ipythonconsole.py:203 +msgid "Host name" +msgstr "Nom d'hôte" + +#: spyder/plugins/ipythonconsole.py:204 +msgid "Ssh key" +msgstr "Clé ssh" + +#: spyder/plugins/ipythonconsole.py:205 +msgid "Password" +msgstr "Mot de passe" + +#: spyder/plugins/ipythonconsole.py:234 +msgid "Open connection file" +msgstr "Ouvrir un fichier de connexion IPython" + +#: spyder/plugins/ipythonconsole.py:239 +msgid "Select ssh key" +msgstr "Sélectionner une clé ssh" + +#: spyder/plugins/ipythonconsole.py:267 spyder/plugins/ipythonconsole.py:686 +msgid "IPython console" +msgstr "Console IPython" + +#: spyder/plugins/ipythonconsole.py:274 +msgid "Display initial banner" +msgstr "Afficher le message d'accueil" + +#: spyder/plugins/ipythonconsole.py:275 +msgid "" +"This option lets you hide the message shown at\n" +"the top of the console when it's opened." +msgstr "" +"Cette option permet de masquer la message d'accueil\n" +"qui s'affiche à l'ouverture de la console." + +#: spyder/plugins/ipythonconsole.py:277 +msgid "Use a pager to display additional text inside the console" +msgstr "Utiliser un pager pour afficher l'aide" + +#: spyder/plugins/ipythonconsole.py:279 +msgid "" +"Useful if you don't want to fill the console with long help or completion " +"texts.\n" +"Note: Use the Q key to get out of the pager." +msgstr "" +"Le pager permet d'éviter de remplir la console de texte d'aide.\n" +"Remarque : utiliser la touche Q pour quitter le pager." + +#: spyder/plugins/ipythonconsole.py:284 +msgid "Ask for confirmation before closing" +msgstr "Demander confirmation avant de fermer une console" + +#: spyder/plugins/ipythonconsole.py:294 +msgid "Completion Type" +msgstr "Type de complétion de code" + +#: spyder/plugins/ipythonconsole.py:295 +msgid "Decide what type of completion to use" +msgstr "Sélectionner le type de completion de code a utiliser" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Graphical" +msgstr "Graphique" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Plain" +msgstr "Simple" + +#: spyder/plugins/ipythonconsole.py:298 +msgid "Completion:" +msgstr "Completion de code:" + +#: spyder/plugins/ipythonconsole.py:307 +msgid "Light background" +msgstr "Fond blanc" + +#: spyder/plugins/ipythonconsole.py:309 +msgid "Dark background" +msgstr "Fond noir" + +#: spyder/plugins/ipythonconsole.py:319 +msgid "Buffer: " +msgstr "Tampon : " + +#: spyder/plugins/ipythonconsole.py:321 +msgid "" +"Set the maximum number of lines of text shown in the\n" +"console before truncation. Specifying -1 disables it\n" +"(not recommended!)" +msgstr "" +"Nombre maximum de lignes de texte affichées dans la console avant troncature " +"(saisir -1 désactive cette dernière, ce qui est fortement déconseillé)." + +#: spyder/plugins/ipythonconsole.py:330 +msgid "Support for graphics (Matplotlib)" +msgstr "Prise en charge des graphes (Matplotlib)" + +#: spyder/plugins/ipythonconsole.py:331 +msgid "Activate support" +msgstr "Activer" + +#: spyder/plugins/ipythonconsole.py:332 +msgid "Automatically load Pylab and NumPy modules" +msgstr "Importer automatiquement Pylab et NumPy" + +#: spyder/plugins/ipythonconsole.py:335 +msgid "" +"This lets you load graphics support without importing \n" +"the commands to do plots. Useful to work with other\n" +"plotting libraries different to Matplotlib or to develop \n" +"GUIs with Spyder." +msgstr "" +"Import automatique de toutes les fonctions de représentation graphique et de " +"calcul numérique" + +#: spyder/plugins/ipythonconsole.py:350 +msgid "Inline" +msgstr "En ligne" + +#: spyder/plugins/ipythonconsole.py:352 +msgid "Graphics backend" +msgstr "Sortie graphique" + +#: spyder/plugins/ipythonconsole.py:353 +msgid "" +"Decide how graphics are going to be displayed in the console. If unsure, " +"please select %s to put graphics inside the console or %s to " +"interact with them (through zooming and panning) in a separate window." +msgstr "" +"Cette option permet de régler la manière dont les graphes seront affichés " +"dans la console. Par exemple, le mode %s permet l'affichage en ligne " +"des graphes tandis que le mode %s permet d'interagir avec (zoom/pan) " +"dans une fenêtre séparée." + +#: spyder/plugins/ipythonconsole.py:386 +msgid "Inline backend" +msgstr "Backend intégré" + +#: spyder/plugins/ipythonconsole.py:387 +msgid "Decide how to render the figures created by this backend" +msgstr "Option relative au rendu des figures dans ce backend" + +#: spyder/plugins/ipythonconsole.py:391 +msgid "Format:" +msgstr "Format :" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "Resolution:" +msgstr "Résolution :" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "dpi" +msgstr "ppp" + +#: spyder/plugins/ipythonconsole.py:396 +msgid "Only used when the format is PNG. Default is 72" +msgstr "Utilisé uniquement dans le cas du format PNG. Par défaut: 72" + +#: spyder/plugins/ipythonconsole.py:399 +msgid "Width:" +msgstr "Largeur :" + +#: spyder/plugins/ipythonconsole.py:399 spyder/plugins/ipythonconsole.py:403 +msgid "inches" +msgstr "pouces" + +#: spyder/plugins/ipythonconsole.py:401 +msgid "Default is 6" +msgstr "Par défaut : 6" + +#: spyder/plugins/ipythonconsole.py:403 +msgid "Height:" +msgstr "Hauteur :" + +#: spyder/plugins/ipythonconsole.py:405 +msgid "Default is 4" +msgstr "Par défaut : 4" + +#: spyder/plugins/ipythonconsole.py:432 +msgid "" +"You can run several lines of code when a console is started. Please " +"introduce each one separated by commas, for example:
import os, import " +"sys" +msgstr "" +"Plusieurs lignes de code peuvent être exécutées lors du démarrage de la " +"console. Veuillez séparer deux lignes consécutives par une virgule - par " +"exemple :
import os, import sys" + +#: spyder/plugins/ipythonconsole.py:438 +msgid "Lines:" +msgstr "Lignes :" + +#: spyder/plugins/ipythonconsole.py:447 +msgid "Run a file" +msgstr "Exécuter un fichier" + +#: spyder/plugins/ipythonconsole.py:448 +msgid "" +"You can also run a whole file at startup instead of just some lines (This is " +"similar to have a PYTHONSTARTUP file)." +msgstr "Option similaire à PYTHONSTARTUP pour un interpréteur standard" + +#: spyder/plugins/ipythonconsole.py:452 +msgid "Use the following file:" +msgstr "Utiliser le fichier suivant :" + +#: spyder/plugins/ipythonconsole.py:466 +msgid "Greedy completion" +msgstr "Complétion avancée" + +#: spyder/plugins/ipythonconsole.py:467 +msgid "" +"Enable Tab completion on elements of lists, results of function " +"calls, etc, without assigning them to a variable.
For example, you " +"can get completions on things like li[0].<Tab> or ins." +"meth().<Tab>" +msgstr "" +"Active la complétion par Tab sur les éléments de liste, les " +"résultats de fonctions, etc... sans les avoir assignés à une variable." +"
Par exemple vous pourrez avoir la complétion pour des expressions du " +"type li[0].<Tab> ou ins.meth().<Tab>." + +#: spyder/plugins/ipythonconsole.py:475 +msgid "Use the greedy completer" +msgstr "Utiliser la complétion avancée" + +#: spyder/plugins/ipythonconsole.py:486 +msgid "Autocall" +msgstr "Appel automatique" + +#: spyder/plugins/ipythonconsole.py:487 +msgid "" +"Autocall makes IPython automatically call any callable object even if you " +"didn't type explicit parentheses.
For example, if you type str 43 " +"it becomes str(43) automatically." +msgstr "" +"L'appel automatique dans IPython va automatiquement appeler les objets " +"appelables même sans parenthèses explicites.
Par exemple str 43 " +"deviendra automatiquement str(43)." + +#: spyder/plugins/ipythonconsole.py:494 +msgid "Smart" +msgstr "Intelligent" + +#: spyder/plugins/ipythonconsole.py:495 +msgid "Full" +msgstr "Toujours" + +#: spyder/plugins/ipythonconsole.py:496 +msgid "Off" +msgstr "Désactivé" + +#: spyder/plugins/ipythonconsole.py:498 +msgid "Autocall: " +msgstr "Appel automatique :" + +#: spyder/plugins/ipythonconsole.py:499 +msgid "" +"On %s mode, Autocall is not applied if there are no arguments after " +"the callable. On %s mode, all callable objects are automatically " +"called (even if no arguments are present)." +msgstr "" +"En mode %s, l'appel automatique n'est pas activé si il n'y a pas " +"d'arguments. En mode %s, tous les objets appelables sont " +"automatiquement appelés (même s'il n'y a pas d'arguments)" + +#: spyder/plugins/ipythonconsole.py:511 +msgid "Symbolic Mathematics" +msgstr "Calcul formel" + +#: spyder/plugins/ipythonconsole.py:512 +msgid "" +"Perfom symbolic operations in the console (e.g. integrals, derivatives, " +"vector calculus, etc) and get the outputs in a beautifully printed style (it " +"requires the Sympy module)." +msgstr "" +"Utilise le calcul formel pour réaliser des opérations dans la console (par " +"exemple intégrales, dérivées, calcul vectoriel, etc...) et affiche les " +"résultats de manière élégante." + +#: spyder/plugins/ipythonconsole.py:517 +msgid "Use symbolic math" +msgstr "Utiliser le calcul formel" + +#: spyder/plugins/ipythonconsole.py:518 +msgid "" +"This option loads the Sympy library to work with.
Please refer to its " +"documentation to learn how to use it." +msgstr "" +"Activer cette option permet de travailler avec la bibliothèque Sympy." +"
Merci de consulter la documentation pour savoir comment l'utiliser." + +#: spyder/plugins/ipythonconsole.py:528 +msgid "Prompts" +msgstr "Invites de commande" + +#: spyder/plugins/ipythonconsole.py:529 +msgid "Modify how Input and Output prompts are shown in the console." +msgstr "" +"Change l'affichage des invites de commande d'entrée et de sortie de la " +"console." + +#: spyder/plugins/ipythonconsole.py:532 +msgid "Input prompt:" +msgstr "En entrée :" + +#: spyder/plugins/ipythonconsole.py:534 +msgid "" +"Default is
In [<span class=\"in-prompt-number\">%i</span>]:" +msgstr "" +"Par défaut :
In [<span class=\"in-prompt-number\">%i</span>]:" + +#: spyder/plugins/ipythonconsole.py:538 +msgid "Output prompt:" +msgstr "En sortie :" + +#: spyder/plugins/ipythonconsole.py:540 +msgid "" +"Default is
Out[<span class=\"out-prompt-number\">%i</span>]:" +msgstr "" +"Par défaut :
Out[<span class=\"out-prompt-number\">%i</span>]:" + +#: spyder/plugins/ipythonconsole.py:562 spyder/plugins/workingdirectory.py:45 +msgid "Startup" +msgstr "Démarrage" + +#: spyder/plugins/ipythonconsole.py:734 +msgid "Open an &IPython console" +msgstr "Ouvrir une console &IPython" + +#: spyder/plugins/ipythonconsole.py:737 +msgid "Use %s+T when the console is selected to open a new one" +msgstr "" +"Quand la console est sélectionnée, utiliser %s+T pour ouvrir une nouvelle " +"console" + +#: spyder/plugins/ipythonconsole.py:740 +msgid "Open a new console" +msgstr "Ouvrir une nouvelle console" + +#: spyder/plugins/ipythonconsole.py:748 +msgid "Open a new IPython console connected to an existing kernel" +msgstr "Ouvrir une nouvelle console IPython connecté à un noyau existant" + +#: spyder/plugins/ipythonconsole.py:836 +msgid "" +"No IPython console is currently available to run %s.

Please " +"open a new one and try again." +msgstr "" +"Aucun client IPython n'est actuellement sélectionné pour exécuter %s." +"

Merci d'ouvrir un nouveau client IPython et de réessayer." + +#: spyder/plugins/ipythonconsole.py:880 +msgid "" +"Your Python environment or installation doesn't have the ipykernel " +"module installed on it. Without this module is not possible for Spyder to " +"create a console for you.

You can install ipykernel by " +"running in a terminal:

pip install ipykernel

or

conda install ipykernel" +msgstr "" +"Votre environnement Python ou l'installation ne dispose pas du module " +"ipykernel installé sur celui-ci. Sans ce module, Spyder ne peut " +"pas créer une console pour vous.

Vous pouvez installer " +"ipykernel en exécutant un terminal:

pip install " +"ipykernel

ou

conda installer ipykernel" + +#: spyder/plugins/ipythonconsole.py:1105 +msgid "Do you want to close this console?" +msgstr "Souhaitez-vous fermer cette console?" + +#: spyder/plugins/ipythonconsole.py:1111 +msgid "" +"Do you want to close all other consoles connected to the same kernel as this " +"one?" +msgstr "" +"Voulez-vous fermer les toutes les autres consoles connectées au même noyau " +"que celle-ci ?" + +#: spyder/plugins/ipythonconsole.py:1382 +msgid "IPython" +msgstr "IPython" + +#: spyder/plugins/ipythonconsole.py:1383 +msgid "Unable to connect to %s" +msgstr "Impossible de se connecter au noyau IPython %s" + +#: spyder/plugins/ipythonconsole.py:1443 +msgid "Connection error" +msgstr "Erreur de connexion" + +#: spyder/plugins/ipythonconsole.py:1444 +msgid "" +"Could not open ssh tunnel. The error was:\n" +"\n" +msgstr "" +"Impossible d'ouvrir un tunnel ssh. L'erreur rencontrée est:\n" +"\n" + +#: spyder/plugins/layoutdialog.py:177 +msgid "Move Up" +msgstr "Monter" + +#: spyder/plugins/layoutdialog.py:178 +msgid "Move Down" +msgstr "Descendre" + +#: spyder/plugins/layoutdialog.py:179 +msgid "Delete Layout" +msgstr "Supprimer disposition de fenêtre" + +#: spyder/plugins/layoutdialog.py:183 +msgid "Layout Display and Order" +msgstr "Configurer l'affichage et l'ordre" + +#: spyder/plugins/maininterpreter.py:31 spyder/plugins/maininterpreter.py:66 +msgid "Python interpreter" +msgstr "Interpréteur Python" + +#: spyder/plugins/maininterpreter.py:68 +msgid "Select the Python interpreter for all Spyder consoles" +msgstr "Sélectionner l'interpréteur Python utilisé pour exécuter des scripts" + +#: spyder/plugins/maininterpreter.py:71 +msgid "Default (i.e. the same as Spyder's)" +msgstr "" +"Par défaut (interpréteur identique à celui dans lequel Spyder est exécuté)" + +#: spyder/plugins/maininterpreter.py:74 +msgid "Use the following Python interpreter:" +msgstr "Utiliser l'interpréteur Python suivant :" + +#: spyder/plugins/maininterpreter.py:77 +msgid "Executables" +msgstr "Exécutables" + +#: spyder/plugins/maininterpreter.py:94 +msgid "User Module Reloader (UMR)" +msgstr "User Module Reloader (UMR)" + +#: spyder/plugins/maininterpreter.py:95 +msgid "" +"UMR forces Python to reload modules which were imported when executing a " +"file in a Python or IPython console with the runfile function." +msgstr "" +"L'UMR force Python à recharger les modules importés lors de l'exécution d'un " +"script dans la console interne avec la fonction 'runfile'." + +#: spyder/plugins/maininterpreter.py:100 +msgid "Enable UMR" +msgstr "Activer l'UMR" + +#: spyder/plugins/maininterpreter.py:101 +msgid "" +"This option will enable the User Module Reloader (UMR) in Python/IPython " +"consoles. UMR forces Python to reload deeply modules during import when " +"running a Python script using the Spyder's builtin function runfile." +"

1. UMR may require to restart the console in which it will be " +"called (otherwise only newly imported modules will be reloaded when " +"executing files).

2. If errors occur when re-running a PyQt-" +"based program, please check that the Qt objects are properly destroyed (e.g. " +"you may have to use the attribute Qt.WA_DeleteOnClose on your main " +"window, using the setAttribute method)" +msgstr "" +"Cette option active le User Module Deleter (UMR) dans les consoles Python. " +"L'UMR force Python à recharger complètement les modules lors de leur " +"importation, dans le cadre de l'exécution d'un script Python avec la " +"fonction Spyder runfile.

1. UMR peut nécessiter le " +"redémarrage de la console Python dans lequel il va être utilisé (dans le cas " +"contraire, seuls les modules importés après activation de l'UMR seront " +"rechargés complètement lors de l'exécution de scripts).

2. Si " +"des erreurs survenaient lors de la réexécution de programmes utilisant PyQt, " +"veuillez vérifier que les objets Qt sont correctement détruits à la sortie " +"du programme (par exemple, il sera probablement nécessaire d'utiliser " +"l'attribut Qt.WA_DeleteOnClose sur votre objet fenêtre principale " +"grâce à la méthode setAttribute)" + +#: spyder/plugins/maininterpreter.py:117 +msgid "Show reloaded modules list" +msgstr "Afficher les modules rechargés" + +#: spyder/plugins/maininterpreter.py:118 +msgid "Please note that these changes will be applied only to new consoles" +msgstr "" +"Veuillez noter que ces changements ne seront pris en compte que dans les " +"nouvelles consoles Python" + +#: spyder/plugins/maininterpreter.py:122 +msgid "Set UMR excluded (not reloaded) modules" +msgstr "Définir la liste des modules non rechargés par l'UMR" + +#: spyder/plugins/maininterpreter.py:168 +msgid "" +"You selected a Python %d interpreter for the console but Spyder is " +"running on Python %d!.

Although this is possible, we recommend " +"you to install and run Spyder directly with your selected interpreter, to " +"avoid seeing false warnings and errors due to the incompatible syntax " +"between these two Python versions." +msgstr "" +"Vous avez sélectionné un interpréteur Python %d pour la console. Or " +"cette version de Spyder est exécutée par Python %d!.

Même si " +"c'est techniquement possible, il est recommandé d'installer et d'exécuter " +"Spyder directement dans la version de l'interpréteur sélectionnée, afin " +"d'éviter l'apparition d'avertissements ou d'erreurs liées à une syntaxe " +"incompatible entre ces deux versions de Python" + +#: spyder/plugins/maininterpreter.py:178 spyder/plugins/maininterpreter.py:205 +#: spyder/plugins/maininterpreter.py:209 +msgid "UMR" +msgstr "UMR" + +#: spyder/plugins/maininterpreter.py:179 +msgid "Set the list of excluded modules as this: numpy, scipy" +msgstr "Définissez la liste des modules exclus comme ceci: numpy, scipy" + +#: spyder/plugins/maininterpreter.py:196 +msgid "" +"You are working with Python 2, this means that you can not import a module " +"that contains non-ascii characters." +msgstr "" +"Vous travaillez avec Python 2, cela signifie que vous ne pouvez pas importer " +"un module qui contient des caractères non ASCII." + +#: spyder/plugins/maininterpreter.py:206 +msgid "" +"The following modules are not installed on your machine:\n" +"%s" +msgstr "" +"Les modules suivants ne sont pas installés sur votre ordinateur :\n" +"%s" + +#: spyder/plugins/maininterpreter.py:210 +msgid "" +"Please note that these changes will be applied only to new Python/IPython " +"consoles" +msgstr "" +"Veuillez noter que ces changements ne seront pris en compte que dans les " +"nouvelles consoles Python/IPython" + +#: spyder/plugins/onlinehelp.py:70 +msgid "Online help" +msgstr "Aide en ligne" + +#: spyder/plugins/outlineexplorer.py:49 spyder/widgets/editortools.py:195 +msgid "Outline" +msgstr "Structure" + +#: spyder/plugins/projects.py:76 spyder/widgets/projects/explorer.py:113 +#: spyder/widgets/projects/explorer.py:127 +msgid "Project explorer" +msgstr "Explorateur de projets" + +#: spyder/plugins/projects.py:88 +msgid "New Project..." +msgstr "Nouveau Projet..." + +#: spyder/plugins/projects.py:91 +msgid "Open Project..." +msgstr "Ouvrir un Projet..." + +#: spyder/plugins/projects.py:94 +msgid "Close Project" +msgstr "Fermer le projet" + +#: spyder/plugins/projects.py:97 +msgid "Delete Project" +msgstr "Supprimer projet" + +#: spyder/plugins/projects.py:103 +msgid "Project Preferences" +msgstr "Préférences du projet" + +#: spyder/plugins/projects.py:105 +msgid "Recent Projects" +msgstr "Projets récents" + +#: spyder/plugins/projects.py:240 +msgid "Open project" +msgstr "Ouvrir le projet" + +#: spyder/plugins/projects.py:245 +msgid "%s is not a Spyder project!" +msgstr "%s n'est pas un projet Spyder!" + +#: spyder/plugins/runconfig.py:29 +msgid "Execute in current Python or IPython console" +msgstr "Exécuter dans la console Python ou IPython active" + +#: spyder/plugins/runconfig.py:30 +msgid "Execute in a new dedicated Python console" +msgstr "Exécuter dans une nouvelle console Python dédiée" + +#: spyder/plugins/runconfig.py:31 +msgid "Execute in an external System terminal" +msgstr "Exécuter dans un terminal système externe" + +#: spyder/plugins/runconfig.py:41 +msgid "Always show %s on a first file run" +msgstr "Toujours afficher %s lors de la première exécution d'un script" + +#: spyder/plugins/runconfig.py:160 spyder/plugins/runconfig.py:474 +msgid "General settings" +msgstr "Options générales" + +#: spyder/plugins/runconfig.py:163 spyder/plugins/runconfig.py:209 +msgid "Command line options:" +msgstr "Options en ligne de commande :" + +#: spyder/plugins/runconfig.py:169 +msgid "Working directory:" +msgstr "Répertoire de travail :" + +#: spyder/plugins/runconfig.py:181 spyder/plugins/runconfig.py:492 +msgid "Enter debugging mode when errors appear during execution" +msgstr "" +"Entrer dans le mode de débogage si des erreurs apparaissent pendant " +"l'exécution" + +#: spyder/plugins/runconfig.py:197 spyder/plugins/runconfig.py:502 +msgid "Dedicated Python console" +msgstr "Console Python dédiée" + +#: spyder/plugins/runconfig.py:201 spyder/plugins/runconfig.py:504 +msgid "Interact with the Python console after execution" +msgstr "Interagir avec la console Python après l'exécution" + +#: spyder/plugins/runconfig.py:205 +msgid "Show warning when killing running process" +msgstr "Afficher un avertissement à l'interruption d'un processus" + +#: spyder/plugins/runconfig.py:214 +msgid "-u is added to the other options you set here" +msgstr "L'option -u est ajoutée aux autres options spécifiées ici" + +#: spyder/plugins/runconfig.py:224 +msgid "this dialog" +msgstr "cette fenêtre" + +#: spyder/plugins/runconfig.py:283 +msgid "Run configuration" +msgstr "Configuration d'exécution" + +#: spyder/plugins/runconfig.py:284 +msgid "The following working directory is not valid:
%s" +msgstr "Le répertoire de travail suivant n'est pas valide :
%s" + +#: spyder/plugins/runconfig.py:362 +msgid "Run settings for %s" +msgstr "Options d'exécution pour %s" + +#: spyder/plugins/runconfig.py:394 +msgid "Select a run configuration:" +msgstr "Sélectionner une configuration d'exécution :" + +#: spyder/plugins/runconfig.py:423 spyder/plugins/runconfig.py:448 +msgid "Run Settings" +msgstr "Options d'exécution" + +#: spyder/plugins/runconfig.py:450 +msgid "" +"The following are the default %s. These options may be overriden " +"using the %s dialog box (see the %s menu)" +msgstr "" +"La page suivante présente les réglages par défaut pour les %s. Ces " +"réglages peuvent être remplacés à tout moment en utilisant la boîte de " +"dialogue %s (voir le menu %s)" + +#: spyder/plugins/runconfig.py:476 +msgid "Default working directory is:" +msgstr "Le répertoire de travail par défaut est :" + +#: spyder/plugins/runconfig.py:478 +msgid "the script directory" +msgstr "le répertoire du fichier à exécuter" + +#: spyder/plugins/runconfig.py:481 spyder/plugins/workingdirectory.py:57 +msgid "the following directory:" +msgstr "le répertoire suivant :" + +#: spyder/plugins/runconfig.py:507 +msgid "Show warning when killing running processes" +msgstr "Afficher un avertissement à l'interruption d'un processus" + +#: spyder/plugins/runconfig.py:516 +msgid "Run Settings dialog" +msgstr "la fenêtre Options d'exécution" + +#: spyder/plugins/shortcuts.py:137 +msgid "" +"Press the new shortcut and select 'Ok': \n" +"(Press 'Tab' once to switch focus between the shortcut entry \n" +"and the buttons below it)" +msgstr "" +"Ecrivez le nouveau raccourci et sélectionnez 'OK':\n" +"(Appuyez sur la touche 'Tab' une fois pour changer de focus entre l'entrée " +"de raccourci\n" +"et les boutons ci-dessous)" + +#: spyder/plugins/shortcuts.py:140 +msgid "Current shortcut:" +msgstr "Raccourci actuelle :" + +#: spyder/plugins/shortcuts.py:142 +msgid "New shortcut:" +msgstr "Nouveau raccourci:" + +#: spyder/plugins/shortcuts.py:155 +msgid "Shortcut: {0}" +msgstr "Raccourci clavier: {0}" + +#: spyder/plugins/shortcuts.py:276 +msgid "Please introduce a different shortcut" +msgstr "Merci de utiliser un raccourci clavier différent" + +#: spyder/plugins/shortcuts.py:313 +msgid "The new shorcut conflicts with:" +msgstr "Le nouveau raccourci " + +#: spyder/plugins/shortcuts.py:324 +msgid "" +"A compound sequence can have {break} a maximum of 4 subsequences.{break}" +msgstr "" +"A compound sequence can have {break} a maximum of 4 subsequences.{break}" + +#: spyder/plugins/shortcuts.py:329 +msgid "Invalid key entered" +msgstr "Vous avez entré une clé non valide" + +#: spyder/plugins/shortcuts.py:531 +msgid "Context" +msgstr "Contexte" + +#: spyder/plugins/shortcuts.py:533 +#: spyder/widgets/variableexplorer/collectionseditor.py:118 +msgid "Name" +msgstr "Nom" + +#: spyder/plugins/shortcuts.py:535 +msgid "Shortcut" +msgstr "Raccourci clavier" + +#: spyder/plugins/shortcuts.py:537 +msgid "Score" +msgstr "Fuzzy points" + +#: spyder/plugins/shortcuts.py:697 +msgid "Conflicts" +msgstr "Conflits" + +#: spyder/plugins/shortcuts.py:698 +msgid "The following conflicts have been detected:" +msgstr "Les conflits suivants ont été détectés :" + +#: spyder/plugins/shortcuts.py:783 +msgid "Keyboard shortcuts" +msgstr "Raccourcis clavier" + +#: spyder/plugins/shortcuts.py:791 +msgid "Search: " +msgstr "Rechercher:" + +#: spyder/plugins/shortcuts.py:792 +msgid "Reset to default values" +msgstr "Rétablir les valeurs par défaut" + +#: spyder/plugins/variableexplorer.py:26 +msgid "Autorefresh" +msgstr "Rafraîchissement automatique" + +#: spyder/plugins/variableexplorer.py:27 +msgid "Enable autorefresh" +msgstr "Activer le rafraîchissement automatique" + +#: spyder/plugins/variableexplorer.py:29 +msgid "Refresh interval: " +msgstr "Période de rafraîchissement : " + +#: spyder/plugins/variableexplorer.py:33 +msgid "Filter" +msgstr "Filtre" + +#: spyder/plugins/variableexplorer.py:35 +#: spyder/widgets/variableexplorer/namespacebrowser.py:226 +msgid "Exclude private references" +msgstr "Exclure les références privées" + +#: spyder/plugins/variableexplorer.py:36 +#: spyder/widgets/variableexplorer/namespacebrowser.py:241 +msgid "Exclude capitalized references" +msgstr "Exclure les références commençant par une majuscule" + +#: spyder/plugins/variableexplorer.py:37 +#: spyder/widgets/variableexplorer/namespacebrowser.py:234 +msgid "Exclude all-uppercase references" +msgstr "Exclure les références en lettres capitales" + +#: spyder/plugins/variableexplorer.py:38 +#: spyder/widgets/variableexplorer/namespacebrowser.py:249 +msgid "Exclude unsupported data types" +msgstr "Exclure les types non supportés" + +#: spyder/plugins/variableexplorer.py:46 +#: spyder/widgets/variableexplorer/collectionseditor.py:677 +msgid "Show arrays min/max" +msgstr "Afficher les min/max des tableaux" + +#: spyder/plugins/variableexplorer.py:48 +msgid "Edit data in the remote process" +msgstr "Éditeurs dans le processus distant" + +#: spyder/plugins/variableexplorer.py:49 +msgid "" +"Editors are opened in the remote process for NumPy arrays, PIL images, " +"lists, tuples and dictionaries.\n" +"This avoids transfering large amount of data between the remote process and " +"Spyder (through the socket)." +msgstr "" +"Les tableaux NumPy, images PIL, listes, tuples et dictionnaires seront " +"modifiés dans un éditeur exécuté dans le processus distant.\n" +"Cela permet d'éviter de transférer de gros volumes de données entre le " +"processus distant et Spyder (à travers le socket)." + +#: spyder/plugins/variableexplorer.py:185 +msgid "Variable explorer" +msgstr "Explorateur de variables" + +#: spyder/plugins/workingdirectory.py:38 +msgid "" +"The global working directory is the working directory for newly " +"opened consoles (Python/IPython consoles and terminals), for the " +"file explorer, for the find in files plugin and for new files " +"created in the editor." +msgstr "" +"Le répertoire de travail global est le répertoire de travail utilisé " +"pour les nouvelles consoles (consoles Python/IPython et terminaux), " +"pour l'explorateur de fichiers, pour la recherche dans les " +"fichiers et pour les fichiers créés dans l'éditeur." + +#: spyder/plugins/workingdirectory.py:47 +msgid "At startup, the global working directory is:" +msgstr "Au démarrage, le répertoire de travail global est :" + +#: spyder/plugins/workingdirectory.py:51 +msgid "the same as in last session" +msgstr "celui utilisé lors de la dernière session" + +#: spyder/plugins/workingdirectory.py:53 +msgid "At startup, Spyder will restore the global directory from last session" +msgstr "" +"Au démarrage, Spyder reprendra le répertoire de travail global de la " +"dernière session" + +#: spyder/plugins/workingdirectory.py:59 +msgid "At startup, the global working directory will be the specified path" +msgstr "" +"Au démarrage, le répertoire de travail global sera le chemin d'accès " +"spécifié ici" + +#: spyder/plugins/workingdirectory.py:71 +msgid "Files are opened from:" +msgstr "Les fichiers sont ouverts depuis :" + +#: spyder/plugins/workingdirectory.py:75 spyder/plugins/workingdirectory.py:88 +msgid "the current file directory" +msgstr "le répertoire du fichier en cours d'édition" + +#: spyder/plugins/workingdirectory.py:79 spyder/plugins/workingdirectory.py:92 +msgid "the global working directory" +msgstr "le répertoire de travail global" + +#: spyder/plugins/workingdirectory.py:84 +msgid "Files are created in:" +msgstr "Les fichiers sont créés dans :" + +#: spyder/plugins/workingdirectory.py:98 +msgid "Change to file base directory" +msgstr "Sélectionner le répertoire de base du fichier" + +#: spyder/plugins/workingdirectory.py:100 +msgid "When opening a file" +msgstr "Lors de l'ouverture d'un fichier" + +#: spyder/plugins/workingdirectory.py:102 +msgid "When saving a file" +msgstr "Lors de l'enregistrement d'un fichier" + +#: spyder/plugins/workingdirectory.py:172 +msgid "Back" +msgstr "Retour" + +#: spyder/plugins/workingdirectory.py:180 spyder/widgets/explorer.py:1099 +#: spyder/widgets/variableexplorer/importwizard.py:529 +msgid "Next" +msgstr "Suivant" + +#: spyder/plugins/workingdirectory.py:191 +msgid "" +"This is the working directory for newly\n" +"opened consoles (Python/IPython consoles and\n" +"terminals), for the file explorer, for the\n" +"find in files plugin and for new files\n" +"created in the editor" +msgstr "" +"Ceci est le répertoire de travail utilisé pour\n" +"les nouvelles consoles (consoles Python/IPython\n" +"et fenêtres de commandes), pour l'explorateur\n" +"de fichiers, pour la recherche dans les fichiers\n" +"et pour les fichiers créés dans l'éditeur" + +#: spyder/plugins/workingdirectory.py:219 +msgid "Browse a working directory" +msgstr "Sélectionner un répertoire de travail" + +#: spyder/plugins/workingdirectory.py:226 +msgid "Change to parent directory" +msgstr "Aller au répertoire parent" + +#: spyder/plugins/workingdirectory.py:233 +msgid "Global working directory" +msgstr "Répertoire de travail global" + +#: spyder/utils/codeanalysis.py:91 +msgid "Real-time code analysis on the Editor" +msgstr "Analyse de code temps réel dans l'éditeur" + +#: spyder/utils/codeanalysis.py:95 +msgid "Real-time code style analysis on the Editor" +msgstr "Analyse de code temps réel dans l'éditeur" + +#: spyder/utils/environ.py:101 +msgid "" +"Module pywin32 was not found.
Please restart this Windows " +"session (not the computer) for changes to take effect." +msgstr "" +"Le module pywin32 n'est pas installé.
Merci de redémarrer la " +"session en cours (et non l'ordinateur) pour que les changements " +"effectués prennent effet." + +#: spyder/utils/environ.py:114 +msgid "" +"If you accept changes, this will modify the current user environment " +"variables directly in Windows registry. Use it with precautions, at " +"your own risks.

Note that for changes to take effect, you will need " +"to restart the parent process of this application (simply restart Spyder if " +"you have executed it from a Windows shortcut, otherwise restart any " +"application from which you may have executed it, like Python(x,y) Home for example)" +msgstr "" +"Si vous acceptez les changements effectués, cela modifiera les variables " +"d'environnement de l'utilisateur courant directement dans la base de " +"registre Windows. Utilisez cette fonctionnalité avec précautions et à " +"vos risques et périls.

Notez que pour que les changements effectués " +"prennent effet, il sera nécessaire de redémarrer le processus parent de " +"cette application (redémarrez simplement Spyder si vous l'avez exécuté à " +"partir d'un raccourci Windows, sinon redémarrez toute application ayant " +"servie à exécuter Spyder : Python(x,y) Home ou un invite de commandes " +"par exemple)" + +#: spyder/utils/help/sphinxify.py:217 spyder/utils/help/sphinxify.py:227 +msgid "" +"It was not possible to generate rich text help for this object.
Please " +"see it in plain text." +msgstr "" +"Le processus de génération de l'aide sous la forme de texte enrichi a échoué." +"
Merci d'activer le mode d'affichage en texte brut." + +#: spyder/utils/introspection/manager.py:33 +#: spyder/utils/introspection/manager.py:38 +msgid "Editor's code completion, go-to-definition and help" +msgstr "Editeur : complétion de code, aller à la définition, etc." + +#: spyder/utils/iofuncs.py:408 +msgid "Supported files" +msgstr "Fichiers compatibles" + +#: spyder/utils/iofuncs.py:410 +msgid "All files (*.*)" +msgstr "Tous les fichiers (*.*)" + +#: spyder/utils/iofuncs.py:420 +msgid "Spyder data files" +msgstr "Fichiers Spyder" + +#: spyder/utils/iofuncs.py:422 +#: spyder/widgets/variableexplorer/collectionseditor.py:1021 +msgid "NumPy arrays" +msgstr "Tableaux NumPy" + +#: spyder/utils/iofuncs.py:423 +msgid "NumPy zip arrays" +msgstr "Tableaux NumPy compressés" + +#: spyder/utils/iofuncs.py:424 +msgid "Matlab files" +msgstr "Fichiers Matlab" + +#: spyder/utils/iofuncs.py:425 +msgid "CSV text files" +msgstr "Fichiers texte CSV" + +#: spyder/utils/iofuncs.py:427 +msgid "JPEG images" +msgstr "Images JPEG" + +#: spyder/utils/iofuncs.py:428 +msgid "PNG images" +msgstr "Images PNG" + +#: spyder/utils/iofuncs.py:429 +msgid "GIF images" +msgstr "Images GIF" + +#: spyder/utils/iofuncs.py:430 +msgid "TIFF images" +msgstr "Images TIFF" + +#: spyder/utils/iofuncs.py:431 spyder/utils/iofuncs.py:432 +msgid "Pickle files" +msgstr "Fichiers pickle" + +#: spyder/utils/iofuncs.py:433 +msgid "JSON files" +msgstr "Fichiers JSON" + +#: spyder/utils/iofuncs.py:452 spyder/utils/iofuncs.py:459 +msgid "Unsupported file type '%s'" +msgstr "Type de fichier non pris en charge '%s'" + +#: spyder/utils/programs.py:286 +msgid "It was not possible to run this file in an external terminal" +msgstr "Impossible d'exécuter ce fichier dans un terminal externe" + +#: spyder/utils/syntaxhighlighters.py:33 +msgid "Syntax highlighting for Matlab, Julia and other file types" +msgstr "Coloration syntaxique pour Matlab, Julia et d'autres types de fichier" + +#: spyder/utils/syntaxhighlighters.py:42 +msgid "Background:" +msgstr "Fond :" + +#: spyder/utils/syntaxhighlighters.py:43 +#: spyder/widgets/sourcecode/codeeditor.py:106 +msgid "Current line:" +msgstr "Ligne actuelle :" + +#: spyder/utils/syntaxhighlighters.py:44 +msgid "Current cell:" +msgstr "Cellule actuelle :" + +#: spyder/utils/syntaxhighlighters.py:45 +msgid "Occurrence:" +msgstr "Occurrence :" + +#: spyder/utils/syntaxhighlighters.py:46 +msgid "Link:" +msgstr "Lien :" + +#: spyder/utils/syntaxhighlighters.py:47 +msgid "Side areas:" +msgstr "Zones latérales :" + +#: spyder/utils/syntaxhighlighters.py:48 +msgid "Matched
parens:" +msgstr "Parenthèses correspondantes:" + +#: spyder/utils/syntaxhighlighters.py:49 +msgid "Unmatched
parens:" +msgstr "Parenthèse isolée" + +#: spyder/utils/syntaxhighlighters.py:50 +msgid "Normal text:" +msgstr "Texte normal :" + +#: spyder/utils/syntaxhighlighters.py:51 +msgid "Keyword:" +msgstr "Mot-clé :" + +#: spyder/utils/syntaxhighlighters.py:52 +msgid "Builtin:" +msgstr "Objet intégré :" + +#: spyder/utils/syntaxhighlighters.py:53 +msgid "Definition:" +msgstr "Définition :" + +#: spyder/utils/syntaxhighlighters.py:54 +msgid "Comment:" +msgstr "Commentaire :" + +#: spyder/utils/syntaxhighlighters.py:55 +msgid "String:" +msgstr "Chaîne :" + +#: spyder/utils/syntaxhighlighters.py:56 +msgid "Number:" +msgstr "Nombre :" + +#: spyder/utils/syntaxhighlighters.py:57 +msgid "Instance:" +msgstr "Instance :" + +#: spyder/widgets/arraybuilder.py:179 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Type an array in Matlab : [1 2;3 4]
\n" +" or Spyder simplified syntax : 1 2;3 4\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two spaces or two tabs to generate a ';'.\n" +" " +msgstr "" +"\n" +" Constructeur de Tableau/Matrice Numpy
\n" +" Tapez un tableau dans Matlab: [1 2; 3 4]
\n" +" ou la syntaxe simplifiée de Spyder: 1 2; 3 4 \n" +"

\n" +" Appuyez sur la touche 'Entrée' pour le tableau Numpy ou 'Ctrl" +"+Entrée' pour la matrice Numpy.\n" +"

\n" +" Conseil:
\n" +" Utilisez deux espaces ou deux onglets pour générer un ';'.\n" +" " + +#: spyder/widgets/arraybuilder.py:190 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Enter an array in the table.
\n" +" Use Tab to move between cells.\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two tabs at the end of a row to move to the next row.\n" +" " +msgstr "" +"\n" +" Constructeur de Tableau/Matrice Numpy
\n" +" Entrez un tableau Numpy dans le tableau.
\n" +" Appyuyez sur la touche 'Tab' pour vous déplacer entre les " +"cellules.\n" +"

\n" +" Appuyez sur la touche 'Entrée' pour le tableau ou 'Ctrl + Entrée' " +"pour la matrice.\n" +"

\n" +" Conseil: appuyez deux fois sur la touche 'Tab' à la fin " +"d'une ligne pour passer à la ligne suivante.\n" +" " + +#: spyder/widgets/arraybuilder.py:365 +msgid "Array dimensions not valid" +msgstr "Dimensions du tableau non valides" + +#: spyder/widgets/browser.py:54 spyder/widgets/sourcecode/codeeditor.py:2559 +msgid "Zoom out" +msgstr "Réduire" + +#: spyder/widgets/browser.py:57 spyder/widgets/sourcecode/codeeditor.py:2555 +msgid "Zoom in" +msgstr "Agrandir" + +#: spyder/widgets/browser.py:177 +msgid "Home" +msgstr "Accueil" + +#: spyder/widgets/browser.py:213 +msgid "Find text" +msgstr "Rechercher" + +#: spyder/widgets/browser.py:231 +msgid "Address:" +msgstr "Adresse :" + +#: spyder/widgets/browser.py:267 +msgid "Unable to load page" +msgstr "Impossible de charger la page" + +#: spyder/widgets/comboboxes.py:165 +msgid "Press enter to validate this entry" +msgstr "Appuyer sur Entrée pour valider cette saisie" + +#: spyder/widgets/comboboxes.py:166 +msgid "This entry is incorrect" +msgstr "Cette saisie n'est pas correcte" + +#: spyder/widgets/comboboxes.py:209 +msgid "Press enter to validate this path" +msgstr "Appuyez sur Entrée pour valider ce chemin d'accès" + +#: spyder/widgets/dependencies.py:63 +msgid " Required " +msgstr " Requis " + +#: spyder/widgets/dependencies.py:63 +msgid "Module" +msgstr "Module" + +#: spyder/widgets/dependencies.py:64 +msgid " Installed " +msgstr " Installé " + +#: spyder/widgets/dependencies.py:64 +msgid "Provided features" +msgstr "Fonctionnalités associées" + +#: spyder/widgets/dependencies.py:134 +msgid "Dependencies" +msgstr "Dépendances" + +#: spyder/widgets/dependencies.py:141 +msgid "" +"Spyder depends on several Python modules to provide the right functionality " +"for all its panes. The table below shows the required and installed versions " +"(if any) of all of them.

Note: You can safely use Spyder " +"without the following modules installed: %s and %s." +"

Please also note that new dependencies or changed ones will be " +"correctly detected only after Spyder is restarted." +msgstr "" +"Spyder dépend de nombreux modules Python pour disposer de l'intégralité des " +"fonctionnalités potentiellement fournies par ses plugins. Le tableau suivant " +"montre les versions requises et installées (le cas échéant) de toutes ces " +"dépendances.

Même si Spyder est parfaitement fonctionnel sans tout " +"ces modules, il est néanmoins fortement recommandé d'installer au minimum " +"%s et %s afin de bénéficier des fonctionnalités les plus avancées." +"

Les nouvelles dépendances ou celles modifiées ne seront détectées " +"correctement qu'après redémarrage de Spyder." + +#: spyder/widgets/dependencies.py:157 +msgid "Copy to clipboard" +msgstr "Copier dans le presse-papier" + +#: spyder/widgets/editor.py:350 +msgid "Copy path to clipboard" +msgstr "Copier le chemin d'accès dans le presse-papier" + +#: spyder/widgets/editor.py:354 +msgid "Close all to the right" +msgstr "Fermer tous les onglets vers la droite" + +#: spyder/widgets/editor.py:356 +msgid "Close all but this" +msgstr "Fermer tout sauf cet onglet" + +#: spyder/widgets/editor.py:959 +msgid "Temporary file" +msgstr "Fichier temporaire" + +#: spyder/widgets/editor.py:1054 +msgid "New window" +msgstr "Nouvelle fenêtre" + +#: spyder/widgets/editor.py:1055 +msgid "Create a new editor window" +msgstr "Créer une nouvelle fenêtre d'édition" + +#: spyder/widgets/editor.py:1058 +msgid "Split vertically" +msgstr "Séparation verticale" + +#: spyder/widgets/editor.py:1060 +msgid "Split vertically this editor window" +msgstr "Séparer en deux verticalement cette fenêtre d'édition" + +#: spyder/widgets/editor.py:1062 +msgid "Split horizontally" +msgstr "Séparation horizontale" + +#: spyder/widgets/editor.py:1064 +msgid "Split horizontally this editor window" +msgstr "Séparer en deux horizontalement cette fenêtre d'édition" + +#: spyder/widgets/editor.py:1066 +msgid "Close this panel" +msgstr "Fermer ce volet" + +#: spyder/widgets/editor.py:1223 +msgid "%s has been modified.
Do you want to save changes?" +msgstr "" +"%s a été modifié.
Souhaitez-vous enregistrer ces changements ?" + +#: spyder/widgets/editor.py:1285 +msgid "Save" +msgstr "Enregistrer" + +#: spyder/widgets/editor.py:1286 +msgid "Unable to save script '%s'

Error message:
%s" +msgstr "" +"Impossible d'enregistrer le script '%s'

Message d'erreur :
" +"%s" + +#: spyder/widgets/editor.py:1523 +msgid "" +"%s is unavailable (this file may have been removed, moved or renamed " +"outside Spyder).
Do you want to close it?" +msgstr "" +"%s n'est pas accessible (ce fichier a peut-être été supprimé, déplacé " +"ou renommé en dehors de Spyder).
Souhaitez-vous le fermer ?" + +#: spyder/widgets/editor.py:1543 +msgid "" +"%s has been modified outside Spyder.
Do you want to reload it and " +"lose all your changes?" +msgstr "" +"%s a été modifié en dehors de Spyder.
Souhaitez-vous le recharger " +"et perdre ainsi vos modifications ?" + +#: spyder/widgets/editor.py:1639 +msgid "" +"All changes to %s will be lost.
Do you want to revert file from " +"disk?" +msgstr "" +"Toutes les modifications effectuées sur %s seront perdues." +"
Souhaitez-vous revenir à la version du fichier enregistrée sur le " +"disque ?" + +#: spyder/widgets/editor.py:1779 +msgid "Loading %s..." +msgstr "Chargement de \"%s\" en cours..." + +#: spyder/widgets/editor.py:1789 +msgid "" +"%s contains mixed end-of-line characters.
Spyder will fix this " +"automatically." +msgstr "" +"%s contient des caractères de fin de ligne mélangés.
Spyder va " +"corriger ceci automatiquement." + +#: spyder/widgets/editor.py:2171 +msgid "Close window" +msgstr "Fermer la fenêtre" + +#: spyder/widgets/editor.py:2173 +msgid "Close this window" +msgstr "Fermer cette fenêtre d'édition" + +#: spyder/widgets/editortools.py:94 spyder/widgets/editortools.py:130 +msgid "Line %s" +msgstr "Ligne %s" + +#: spyder/widgets/editortools.py:99 +msgid "Class defined at line %s" +msgstr "Classe déclarée ligne %s" + +#: spyder/widgets/editortools.py:107 +msgid "Method defined at line %s" +msgstr "Méthode déclarée ligne %s" + +#: spyder/widgets/editortools.py:117 +msgid "Function defined at line %s" +msgstr "Fonction déclarée ligne %s" + +#: spyder/widgets/editortools.py:149 +msgid "Cell starts at line %s" +msgstr "Cellule commençant ligne %s" + +#: spyder/widgets/editortools.py:202 spyder/widgets/editortools.py:539 +msgid "Go to cursor position" +msgstr "Aller à la position du curseur" + +#: spyder/widgets/editortools.py:205 +msgid "Show absolute path" +msgstr "Afficher les chemins complets" + +#: spyder/widgets/editortools.py:208 spyder/widgets/explorer.py:210 +msgid "Show all files" +msgstr "Afficher tous les fichiers" + +#: spyder/widgets/editortools.py:211 +msgid "Show special comments" +msgstr "Afficher les commentaires spéciaux" + +#: spyder/widgets/explorer.py:206 +msgid "Edit filename filters..." +msgstr "Modifier les filtres..." + +#: spyder/widgets/explorer.py:220 +msgid "Edit filename filters" +msgstr "Modifier les filtres" + +#: spyder/widgets/explorer.py:221 +msgid "Name filters:" +msgstr "Filtres sur les noms de fichiers :" + +#: spyder/widgets/explorer.py:240 +msgid "File..." +msgstr "Fichier..." + +#: spyder/widgets/explorer.py:244 +msgid "Module..." +msgstr "Module..." + +#: spyder/widgets/explorer.py:248 +msgid "Folder..." +msgstr "Dossier..." + +#: spyder/widgets/explorer.py:252 +msgid "Package..." +msgstr "Paquet..." + +#: spyder/widgets/explorer.py:273 +#: spyder/widgets/variableexplorer/collectionseditor.py:652 +msgid "Edit" +msgstr "Modifier" + +#: spyder/widgets/explorer.py:275 +msgid "Move..." +msgstr "Déplacer..." + +#: spyder/widgets/explorer.py:278 +msgid "Delete..." +msgstr "Supprimer..." + +#: spyder/widgets/explorer.py:281 +msgid "Rename..." +msgstr "Renommer..." + +#: spyder/widgets/explorer.py:284 +msgid "Open" +msgstr "Ouvrir" + +#: spyder/widgets/explorer.py:285 spyder/widgets/sourcecode/codeeditor.py:2531 +msgid "Convert to Python script" +msgstr "Convertir en fichier Python" + +#: spyder/widgets/explorer.py:319 +msgid "Commit" +msgstr "Commiter" + +#: spyder/widgets/explorer.py:322 +msgid "Browse repository" +msgstr "Explorer le dépôt" + +#: spyder/widgets/explorer.py:333 +msgid "Open command prompt here" +msgstr "Ouvrir un invite de commandes ici" + +#: spyder/widgets/explorer.py:335 +msgid "Open terminal here" +msgstr "Ouvrir un terminal ici" + +#: spyder/widgets/explorer.py:340 +msgid "Open Python console here" +msgstr "Ouvrir une console &Python ici" + +#: spyder/widgets/explorer.py:354 +msgid "New" +msgstr "Nouveau" + +#: spyder/widgets/explorer.py:362 +msgid "Import" +msgstr "Import" + +#: spyder/widgets/explorer.py:513 +msgid "Do you really want to delete %s?" +msgstr "Souhaitez-vous réellement supprimer %s ?" + +#: spyder/widgets/explorer.py:531 +msgid "delete" +msgstr "supprimer" + +#: spyder/widgets/explorer.py:532 spyder/widgets/projects/explorer.py:149 +#: spyder/widgets/projects/explorer.py:256 +msgid "Project Explorer" +msgstr "Explorateur de projets" + +#: spyder/widgets/explorer.py:533 spyder/widgets/projects/explorer.py:150 +msgid "Unable to %s %s

Error message:
%s" +msgstr "Impossible de %s %s

Message d'erreur :
%s" + +#: spyder/widgets/explorer.py:548 +msgid "File Explorer" +msgstr "Explorateur de Fichiers" + +#: spyder/widgets/explorer.py:549 +msgid "" +"The current directory contains a project.

If you want to delete the " +"project, please go to Projects » Delete Project" +msgstr "" +"Le dossier actuel contient un projet.

Si vous souhaitez supprimer le " +"projet, allez dans Projets & raquo; Supprimer le projet" + +#: spyder/widgets/explorer.py:566 spyder/widgets/sourcecode/codeeditor.py:2018 +msgid "Conversion error" +msgstr "Erreur de conversion" + +#: spyder/widgets/explorer.py:567 spyder/widgets/sourcecode/codeeditor.py:2019 +msgid "" +"It was not possible to convert this notebook. The error is:\n" +"\n" +msgstr "" +"Impossible de convertir ce notebook. Message d'erreur:\n" +"\n" + +#: spyder/widgets/explorer.py:584 spyder/widgets/explorer.py:592 +#: spyder/widgets/explorer.py:603 +#: spyder/widgets/variableexplorer/collectionseditor.py:681 +#: spyder/widgets/variableexplorer/collectionseditor.py:916 +msgid "Rename" +msgstr "Renommer" + +#: spyder/widgets/explorer.py:585 +msgid "New name:" +msgstr "Nouveau nom :" + +#: spyder/widgets/explorer.py:593 +msgid "" +"Do you really want to rename %s and overwrite the existing file " +"%s?" +msgstr "" +"Souhaitez-vous réellement renommer %s et remplacer le ficher existant " +"%s ?" + +#: spyder/widgets/explorer.py:604 +msgid "Unable to rename file %s

Error message:
%s" +msgstr "" +"Impossible de renommer l'élément %s

Message d'erreur :" +"
%s" + +#: spyder/widgets/explorer.py:640 +msgid "Unable to move %s

Error message:
%s" +msgstr "" +"Impossible de déplacer %s

Message d'erreur :
%s" + +#: spyder/widgets/explorer.py:658 +msgid "Unable to create folder %s

Error message:
%s" +msgstr "" +"Impossible de créer le répertoire %s

Message d'erreur :" +"
%s" + +#: spyder/widgets/explorer.py:671 spyder/widgets/explorer.py:705 +msgid "Unable to create file %s

Error message:
%s" +msgstr "" +"Impossible de créer le fichier %s

Message d'erreur :
" +"%s" + +#: spyder/widgets/explorer.py:679 +msgid "New folder" +msgstr "Nouveau répertoire" + +#: spyder/widgets/explorer.py:680 +msgid "Folder name:" +msgstr "Nom du dossier :" + +#: spyder/widgets/explorer.py:685 +msgid "New package" +msgstr "Nouveau paquet" + +#: spyder/widgets/explorer.py:686 +msgid "Package name:" +msgstr "Nom du paquet :" + +#: spyder/widgets/explorer.py:726 +msgid "New module" +msgstr "Nouveau module" + +#: spyder/widgets/explorer.py:741 +msgid "" +"For %s support, please install one of the
following tools:

%s" +msgstr "" +"Pour ajouter la prise en charge de %s, merci d'installer
l'un des " +"outils suivants :

%s" + +#: spyder/widgets/explorer.py:745 +msgid "Unable to find external program.

%s" +msgstr "Impossible de trouver un programme externe.

%s" + +#: spyder/widgets/explorer.py:966 +msgid "Show current directory only" +msgstr "Afficher uniquement le répertoire courant" + +#: spyder/widgets/explorer.py:1066 +msgid "You don't have the right permissions to open this directory" +msgstr "" +"Vous n'avez pas les permissions nécessaires pour afficher le contenu de ce " +"dossier" + +#: spyder/widgets/explorer.py:1096 +#: spyder/widgets/variableexplorer/importwizard.py:525 +msgid "Previous" +msgstr "Précédent" + +#: spyder/widgets/explorer.py:1102 +msgid "Parent" +msgstr "Parent" + +#: spyder/widgets/externalshell/baseshell.py:129 +msgid "Run again this program" +msgstr "Exécuter de nouveau ce programme" + +#: spyder/widgets/externalshell/baseshell.py:132 +msgid "Kill" +msgstr "Terminer" + +#: spyder/widgets/externalshell/baseshell.py:134 +msgid "Kills the current process, causing it to exit immediately" +msgstr "" +"Tue le processus, entraînant une sortie brutale et immédiate du programme" + +#: spyder/widgets/externalshell/baseshell.py:206 +msgid "Running..." +msgstr "En cours d'exécution..." + +#: spyder/widgets/externalshell/baseshell.py:213 +msgid "Terminated." +msgstr "Terminé." + +#: spyder/widgets/externalshell/baseshell.py:238 +#: spyder/widgets/ipythonconsole/help.py:125 spyder/widgets/mixins.py:661 +msgid "Arguments" +msgstr "Arguments" + +#: spyder/widgets/externalshell/baseshell.py:239 +msgid "Command line arguments:" +msgstr "Arguments en ligne de commande :" + +#: spyder/widgets/externalshell/pythonshell.py:277 +msgid "Variables" +msgstr "Variables" + +#: spyder/widgets/externalshell/pythonshell.py:278 +msgid "Show/hide global variables explorer" +msgstr "Afficher/masquer l'explorateur de variables globales" + +#: spyder/widgets/externalshell/pythonshell.py:282 +msgid "Terminate" +msgstr "Quitter" + +#: spyder/widgets/externalshell/pythonshell.py:283 +msgid "" +"Attempts to stop the process. The process\n" +"may not exit as a result of clicking this\n" +"button (it is given the chance to prompt\n" +"the user for any unsaved files, etc)." +msgstr "" +"Tentative de fermeture du processus. Il est possible que\n" +"le processus ne s'arrête pas suite à cette tentative,\n" +"et propose à l'utilisateur de sauvegarder\n" +"les fichiers non-enregistrés, etc..." + +#: spyder/widgets/externalshell/pythonshell.py:296 +msgid "Interact" +msgstr "Interagir" + +#: spyder/widgets/externalshell/pythonshell.py:298 +msgid "Debug" +msgstr "Déboguer" + +#: spyder/widgets/externalshell/pythonshell.py:300 +#: spyder/widgets/externalshell/pythonshell.py:366 +msgid "Arguments..." +msgstr "Arguments..." + +#: spyder/widgets/externalshell/pythonshell.py:302 +msgid "Post Mortem Debug" +msgstr "Débogage Post Mortem" + +#: spyder/widgets/externalshell/pythonshell.py:308 +msgid "Working directory" +msgstr "Répertoire de travail" + +#: spyder/widgets/externalshell/pythonshell.py:310 +msgid "Set current working directory" +msgstr "Changer le répertoire de travail" + +#: spyder/widgets/externalshell/pythonshell.py:312 +msgid "Environment variables" +msgstr "Variables d'environnement" + +#: spyder/widgets/externalshell/pythonshell.py:316 +msgid "Show sys.path contents" +msgstr "Afficher le contenu de sys.path" + +#: spyder/widgets/externalshell/pythonshell.py:362 +msgid "Arguments: %s" +msgstr "Arguments : %s" + +#: spyder/widgets/externalshell/pythonshell.py:364 +msgid "No argument" +msgstr "Aucun argument" + +#: spyder/widgets/externalshell/pythonshell.py:534 +msgid "A Python console failed to start!" +msgstr "Le démarrage d'une console Python a échoué !" + +#: spyder/widgets/externalshell/systemshell.py:106 +msgid "Process failed to start" +msgstr "Le processus n'a pas pu démarrer" + +#: spyder/widgets/fileswitcher.py:109 +msgid "unsaved file" +msgstr "fichier non enregistré" + +#: spyder/widgets/fileswitcher.py:230 +msgid "" +"Press Enter to switch files or Esc to cancel.

Type to " +"filter filenames.

Use :number to go to a line, e.g. " +"main:42
Use @symbol_text to go to a symbol, e." +"g. @init

Press Ctrl+W to close current " +"tab.
" +msgstr "" +"Appuyez sur la touche Entrée pour changer de fichier ou Esc " +"pour annuler.

Tapez pour filtrer les noms de fichiers. " +"

Utilisez:numberpour aller à une ligne, par exemple " +"main: 42
Utilisez@symbol_text pour accéder à un " +"symbole, par example @init

Appuyez sur Ctrl + " +"W pour fermer l'onglet actuel." + +#: spyder/widgets/fileswitcher.py:508 +msgid "lines" +msgstr "lignes" + +#: spyder/widgets/findinfiles.py:158 +msgid "Unexpected error: see internal console" +msgstr "Erreur inattendue : voir console interne" + +#: spyder/widgets/findinfiles.py:209 spyder/widgets/findinfiles.py:233 +#: spyder/widgets/findinfiles.py:280 +msgid "invalid regular expression" +msgstr "expression régulière incorrecte" + +#: spyder/widgets/findinfiles.py:278 +msgid "permission denied errors were encountered" +msgstr "des erreurs d'autorisation d'accès ont été rencontrées" + +#: spyder/widgets/findinfiles.py:315 +msgid "Search pattern" +msgstr "Expression recherchée" + +#: spyder/widgets/findinfiles.py:318 spyder/widgets/findinfiles.py:352 +#: spyder/widgets/findinfiles.py:364 spyder/widgets/findreplace.py:81 +msgid "Regular expression" +msgstr "Expression régulière" + +#: spyder/widgets/findinfiles.py:327 +msgid "Search" +msgstr "Rechercher" + +#: spyder/widgets/findinfiles.py:330 +msgid "Start search" +msgstr "Démarrer la recherche" + +#: spyder/widgets/findinfiles.py:336 +msgid "Stop search" +msgstr "Arrêter la recherche" + +#: spyder/widgets/findinfiles.py:346 +msgid "Included filenames pattern" +msgstr "Expression des noms de fichier à inclure" + +#: spyder/widgets/findinfiles.py:355 +msgid "Include:" +msgstr "Inclure :" + +#: spyder/widgets/findinfiles.py:358 +msgid "Excluded filenames pattern" +msgstr "Expression des noms de fichier à exclure" + +#: spyder/widgets/findinfiles.py:367 +msgid "Exclude:" +msgstr "Exclure :" + +#: spyder/widgets/findinfiles.py:377 +msgid "PYTHONPATH" +msgstr "PYTHONPATH" + +#: spyder/widgets/findinfiles.py:379 +msgid "" +"Search in all directories listed in sys.path which are outside the Python " +"installation directory" +msgstr "" +"Rechercher dans tous les répertoires listés dans sys.path qui sont situés en " +"dehors du répertoire d'installation de Python" + +#: spyder/widgets/findinfiles.py:382 +msgid "Hg repository" +msgstr "Dépôt Mercurial" + +#: spyder/widgets/findinfiles.py:385 +msgid "Search in current directory hg repository" +msgstr "Rechercher dans le dépôt Mercurial du répertoire courant" + +#: spyder/widgets/findinfiles.py:386 +msgid "Here:" +msgstr "Ici :" + +#: spyder/widgets/findinfiles.py:390 +msgid "Search recursively in this directory" +msgstr "Rechercher de manière récursive dans ce répertoire" + +#: spyder/widgets/findinfiles.py:395 +msgid "Browse a search directory" +msgstr "Sélectionner un répertoire de recherche" + +#: spyder/widgets/findinfiles.py:425 +msgid "Hide advanced options" +msgstr "Masquer les options avancées" + +#: spyder/widgets/findinfiles.py:428 +msgid "Show advanced options" +msgstr "Afficher les options avancées" + +#: spyder/widgets/findinfiles.py:569 +msgid "Search canceled" +msgstr "Recherche annulée" + +#: spyder/widgets/findinfiles.py:573 +msgid "String not found" +msgstr "Chaîne de caractères non trouvée" + +#: spyder/widgets/findinfiles.py:575 +msgid "matches in" +msgstr "correspondances trouvées dans" + +#: spyder/widgets/findinfiles.py:576 +msgid "file" +msgstr "fichier" + +#: spyder/widgets/findinfiles.py:584 +msgid "interrupted" +msgstr "interrompu" + +#: spyder/widgets/findreplace.py:63 +msgid "Search string" +msgstr "Chaîne de caractères à rechercher" + +#: spyder/widgets/findreplace.py:87 +msgid "Case Sensitive" +msgstr "Respecter la casse" + +#: spyder/widgets/findreplace.py:93 +msgid "Whole words" +msgstr "Mots entiers" + +#: spyder/widgets/findreplace.py:99 +msgid "Highlight matches" +msgstr "Surligner les résultats" + +#: spyder/widgets/findreplace.py:113 +msgid "Replace with:" +msgstr "Remplacer par :" + +#: spyder/widgets/findreplace.py:115 +msgid "Replace string" +msgstr "Chaîne de caractères de remplacement" + +#: spyder/widgets/findreplace.py:118 +msgid "Replace/find" +msgstr "Remplacer/rechercher" + +#: spyder/widgets/findreplace.py:125 +msgid "Replace all" +msgstr "Remplacer tout" + +#: spyder/widgets/internalshell.py:262 +msgid "Help..." +msgstr "Aide..." + +#: spyder/widgets/internalshell.py:279 +msgid "Shell special commands:" +msgstr "Commandes spéciales de la console :" + +#: spyder/widgets/internalshell.py:280 +msgid "Internal editor:" +msgstr "Éditeur interne :" + +#: spyder/widgets/internalshell.py:281 +msgid "External editor:" +msgstr "Éditeur externe :" + +#: spyder/widgets/internalshell.py:282 +msgid "Run script:" +msgstr "Exécuter un script :" + +#: spyder/widgets/internalshell.py:283 +msgid "Remove references:" +msgstr "Supprimer des références :" + +#: spyder/widgets/internalshell.py:284 +msgid "System commands:" +msgstr "Commandes systèmes :" + +#: spyder/widgets/internalshell.py:285 +msgid "Python help:" +msgstr "Aide Python :" + +#: spyder/widgets/internalshell.py:286 +msgid "GUI-based editor:" +msgstr "Éditeur graphique :" + +#: spyder/widgets/ipythonconsole/client.py:189 +msgid "An error ocurred while starting the kernel" +msgstr "Une erreur est survenue lors du démarrage du noyau." + +#: spyder/widgets/ipythonconsole/client.py:220 +msgid "Restart kernel" +msgstr "Redémarrer le noyau" + +#: spyder/widgets/ipythonconsole/client.py:240 +msgid "Stop the current command" +msgstr "Interrompre la commande en cours" + +#: spyder/widgets/ipythonconsole/client.py:263 +msgid "Inspect current object" +msgstr "Inspecter l'onglet courant" + +#: spyder/widgets/ipythonconsole/client.py:268 +msgid "Clear line or block" +msgstr "Effacer la ligne ou le bloc" + +#: spyder/widgets/ipythonconsole/client.py:272 +msgid "Reset namespace" +msgstr "Réinitialiser l'espace de noms" + +#: spyder/widgets/ipythonconsole/client.py:275 +msgid "Clear console" +msgstr "Effacer la console" + +#: spyder/widgets/ipythonconsole/client.py:316 +msgid "Are you sure you want to restart the kernel?" +msgstr "Souhaitez-vous vraiment redémarrer le noyau ?" + +#: spyder/widgets/ipythonconsole/client.py:318 +msgid "Restart kernel?" +msgstr "Redémarrer le noyau" + +#: spyder/widgets/ipythonconsole/client.py:327 +msgid "Error restarting kernel: %s\n" +msgstr "Une erreur est survenue lors du démarrage du noyau: %s\n" + +#: spyder/widgets/ipythonconsole/client.py:331 +msgid "" +"
Restarting kernel...\n" +"



" +msgstr "" +"
Redémarrage du noyau...\n" +"

" + +#: spyder/widgets/ipythonconsole/client.py:336 +msgid "Cannot restart a kernel not started by Spyder\n" +msgstr "Impossible de redémarrer un noyau non démarré par Spyder\n" + +#: spyder/widgets/ipythonconsole/client.py:376 +msgid "Changing backend to Qt for Mayavi" +msgstr "Utilisation du backend Qt pour Mayavi" + +#: spyder/widgets/ipythonconsole/client.py:387 +msgid "Connecting to kernel..." +msgstr "Connexion au noyau..." + +#: spyder/widgets/ipythonconsole/namespacebrowser.py:76 +msgid "" +"Inspecting and setting values while debugging in IPython consoles is not " +"supported yet by Spyder." +msgstr "" +"L'inspection et la définition des valeurs lors du débogage dans les consoles " +"IPython n'est pas encore prise en charge par Spyder." + +#: spyder/widgets/ipythonconsole/shell.py:149 +msgid "Reset IPython namespace" +msgstr "Réinitialiser l'espace de noms de la console IPython" + +#: spyder/widgets/ipythonconsole/shell.py:150 +msgid "" +"All user-defined variables will be removed.
Are you sure you want to " +"reset the namespace?" +msgstr "" +"Toutes les variables définies par l'utilisateur seront supprimées." +"
Souhaitez-vous néanmoins réinitialiser l'espace de noms?" + +#: spyder/widgets/onecolumntree.py:52 +msgid "Collapse all" +msgstr "Replier tout" + +#: spyder/widgets/onecolumntree.py:56 +msgid "Expand all" +msgstr "Déplier tout" + +#: spyder/widgets/onecolumntree.py:60 +msgid "Restore" +msgstr "Restaurer" + +#: spyder/widgets/onecolumntree.py:61 +msgid "Restore original tree layout" +msgstr "Restaurer l'organisation initiale de l'arbre" + +#: spyder/widgets/onecolumntree.py:65 +msgid "Collapse selection" +msgstr "Replier la sélection" + +#: spyder/widgets/onecolumntree.py:69 +msgid "Expand selection" +msgstr "Déplier la sélection" + +#: spyder/widgets/pathmanager.py:87 +msgid "Move to top" +msgstr "Placer en premier" + +#: spyder/widgets/pathmanager.py:93 +msgid "Move up" +msgstr "Monter" + +#: spyder/widgets/pathmanager.py:99 +msgid "Move down" +msgstr "Descendre" + +#: spyder/widgets/pathmanager.py:105 +msgid "Move to bottom" +msgstr "Placer en dernier" + +#: spyder/widgets/pathmanager.py:116 spyder/widgets/pathmanager.py:231 +msgid "Add path" +msgstr "Ajouter un chemin" + +#: spyder/widgets/pathmanager.py:121 spyder/widgets/pathmanager.py:214 +msgid "Remove path" +msgstr "Supprimer" + +#: spyder/widgets/pathmanager.py:131 +msgid "Synchronize..." +msgstr "Synchroniser..." + +#: spyder/widgets/pathmanager.py:133 +msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" +msgstr "" +"Synchronise la liste des chemins d'accès de Spyder avec celle de la variable " +"d'environnement PYTHONPATH" + +#: spyder/widgets/pathmanager.py:145 +msgid "Synchronize" +msgstr "Synchroniser" + +#: spyder/widgets/pathmanager.py:146 +msgid "" +"This will synchronize Spyder's path list with PYTHONPATH environment " +"variable for current user, allowing you to run your Python modules outside " +"Spyder without having to configure sys.path.
Do you want to clear " +"contents of PYTHONPATH before adding Spyder's path list?" +msgstr "" +"Ceci synchronisera la liste des chemins d'accès de Spyder avec celle de la " +"variable d'environnement PYTHONPATH pour l'utilisateur courant, vous " +"permettant ainsi d'exécuter vos modules Python en dehors de Spyder sans " +"avoir besoin de configurer sys.path.
Souhaitez-vous effacer le contenu " +"de PYTHONPATH avant d'y ajouter la liste des chemins d'accès de Spyder ?" + +#: spyder/widgets/pathmanager.py:215 +msgid "Do you really want to remove selected path?" +msgstr "Souhaitez-vous vraiment supprimer le chemin sélectionné ?" + +#: spyder/widgets/pathmanager.py:232 +msgid "" +"This directory is already included in Spyder path list.
Do you want to " +"move it to the top of the list?" +msgstr "" +"Ce répertoire est déjà inclus dans la liste des chemins d'accès de Spyder." +"
Souhaitez-vous le placer en début de liste ?" + +#: spyder/widgets/projects/configdialog.py:30 +msgid "Project preferences" +msgstr "Préférences du projet" + +#: spyder/widgets/projects/configdialog.py:82 +#: spyder/widgets/projects/configdialog.py:119 +msgid "Restore data on startup" +msgstr "Restaurer les données au démarrage" + +#: spyder/widgets/projects/configdialog.py:84 +#: spyder/widgets/projects/configdialog.py:121 +msgid "Save data on exit" +msgstr "Enregistrer les données" + +#: spyder/widgets/projects/configdialog.py:86 +#: spyder/widgets/projects/configdialog.py:123 +msgid "Save history" +msgstr "Enregistrer l'historique" + +#: spyder/widgets/projects/configdialog.py:88 +#: spyder/widgets/projects/configdialog.py:125 +msgid "Save non project files opened" +msgstr "Enregistrer les fichiers ouverts en dehors du projet" + +#: spyder/widgets/projects/configdialog.py:111 +msgid "Code" +msgstr "Code" + +#: spyder/widgets/projects/configdialog.py:118 +msgid "Workspace" +msgstr "Espace de travail" + +#: spyder/widgets/projects/configdialog.py:148 +#: spyder/widgets/projects/configdialog.py:155 +msgid "Version control" +msgstr "Gestion de versions" + +#: spyder/widgets/projects/configdialog.py:156 +msgid "Use version control" +msgstr "Utiliser la gestion de version" + +#: spyder/widgets/projects/configdialog.py:161 +msgid "Version control system" +msgstr "Système de gestion de versions" + +#: spyder/widgets/projects/explorer.py:52 +msgid "Show horizontal scrollbar" +msgstr "Barre de défilement horizontal" + +#: spyder/widgets/projects/explorer.py:114 +msgid "File %s already exists.
Do you want to overwrite it?" +msgstr "Le fichier %s existe déjà.
Souhaitez-vous le remplacer ?" + +#: spyder/widgets/projects/explorer.py:128 +msgid "Folder %s already exists." +msgstr "Le dossier %s existe déjà." + +#: spyder/widgets/projects/explorer.py:146 +msgid "copy" +msgstr "copier" + +#: spyder/widgets/projects/explorer.py:148 +msgid "move" +msgstr "déplacer" + +#: spyder/widgets/projects/explorer.py:244 +msgid "" +"Do you really want to delete {filename}?

Note: This " +"action will only delete the project. Its files are going to be preserved on " +"disk." +msgstr "" +"Souhaitez-vous supprimer le projet %s ?

Notez que les fichiers " +"et répertoires du projet ne seront pas supprimés du disque." + +#: spyder/widgets/projects/explorer.py:257 +msgid "" +"Unable to delete {varpath}

The error message was:" +"
{error}" +msgstr "" +"Impossible de supprimer {varpath

Message d'erreur :" +"
{error}" + +#: spyder/widgets/projects/projectdialog.py:69 +msgid "New directory" +msgstr "Sélectionner un répertoire" + +#: spyder/widgets/projects/projectdialog.py:70 +msgid "Existing directory" +msgstr "Répertoire existant" + +#: spyder/widgets/projects/projectdialog.py:72 +msgid "Project name" +msgstr "Nom du projet" + +#: spyder/widgets/projects/projectdialog.py:73 +msgid "Location" +msgstr "Répertoire" + +#: spyder/widgets/projects/projectdialog.py:74 +msgid "Project type" +msgstr "Type de projet" + +#: spyder/widgets/projects/projectdialog.py:75 +msgid "Python version" +msgstr "Python" + +#: spyder/widgets/projects/projectdialog.py:83 +#: spyder/widgets/variableexplorer/importwizard.py:519 +msgid "Cancel" +msgstr "Annuler" + +#: spyder/widgets/projects/projectdialog.py:84 +msgid "Create" +msgstr "Créer" + +#: spyder/widgets/projects/projectdialog.py:102 +msgid "Create new project" +msgstr "Créer un nouveau projet" + +#: spyder/widgets/projects/type/__init__.py:215 +msgid "Empty project" +msgstr "Projet vide" + +#: spyder/widgets/projects/type/python.py:20 +msgid "Python project" +msgstr "Projet Python" + +#: spyder/widgets/projects/type/python.py:76 +msgid "Python package" +msgstr "Paquet Python" + +#: spyder/widgets/pydocgui.py:110 +msgid "Module or package:" +msgstr "Module ou paquet :" + +#: spyder/widgets/shell.py:129 +msgid "Save history log..." +msgstr "Enregistrer l'historique..." + +#: spyder/widgets/shell.py:131 +msgid "Save current history log (i.e. all inputs and outputs) in a text file" +msgstr "" +"Enregistrer l'historique complet (toutes les entrées et sorties) dans un " +"fichier texte" + +#: spyder/widgets/shell.py:257 +msgid "Save history log" +msgstr "Enregistrer l'historique" + +#: spyder/widgets/shell.py:260 +msgid "History logs" +msgstr "Fichiers d'historique" + +#: spyder/widgets/shell.py:271 +msgid "Unable to save file '%s'

Error message:
%s" +msgstr "" +"Impossible d'enregistrer le fichier '%s'

Message d'erreur :
" +"%s" + +#: spyder/widgets/shell.py:713 +msgid "Copy without prompts" +msgstr "Copier sans les préfixes" + +#: spyder/widgets/shell.py:716 spyder/widgets/shell.py:720 +msgid "Clear line" +msgstr "Effacer la ligne" + +#: spyder/widgets/shell.py:722 +msgid "Clear shell" +msgstr "Effacer la console" + +#: spyder/widgets/shell.py:726 +msgid "Clear shell contents ('cls' command)" +msgstr "Effacer le contenu de la console" + +#: spyder/widgets/sourcecode/codeeditor.py:100 +msgid "Go to line:" +msgstr "Aller à la ligne :" + +#: spyder/widgets/sourcecode/codeeditor.py:108 +msgid "Line count:" +msgstr "Nombre de lignes :" + +#: spyder/widgets/sourcecode/codeeditor.py:1305 +msgid "Breakpoint" +msgstr "Point d'arrêt" + +#: spyder/widgets/sourcecode/codeeditor.py:1306 +msgid "Condition:" +msgstr "Condition :" + +#: spyder/widgets/sourcecode/codeeditor.py:1712 +msgid "Code analysis" +msgstr "Analyse de code" + +#: spyder/widgets/sourcecode/codeeditor.py:1766 +msgid "To do" +msgstr "À faire" + +#: spyder/widgets/sourcecode/codeeditor.py:2005 +msgid "Removal error" +msgstr "Erreur de suppression" + +#: spyder/widgets/sourcecode/codeeditor.py:2006 +msgid "" +"It was not possible to remove outputs from this notebook. The error is:\n" +"\n" +msgstr "" +"Impossible d'effacer les résultats de ce notebook:\n" +"\n" + +#: spyder/widgets/sourcecode/codeeditor.py:2528 +msgid "Clear all ouput" +msgstr "Effacer tous les résultats" + +#: spyder/widgets/sourcecode/codeeditor.py:2534 +msgid "Go to definition" +msgstr "Aller à la définition de l'objet" + +#: spyder/widgets/sourcecode/codeeditor.py:2563 +msgid "Zoom reset" +msgstr "Réinitialisation du zoom" + +#: spyder/widgets/status.py:25 +msgid "CPU and memory usage info in the status bar" +msgstr "" +"Informations sur l'utilisation processeur et mémoire dans la barre d'état" + +#: spyder/widgets/status.py:94 +msgid "Memory:" +msgstr "Mémoire :" + +#: spyder/widgets/status.py:95 +msgid "" +"Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows " +"platforms" +msgstr "" +"Occupation mémoire : requiert la bibliothèque `psutil` (>=v0.3) sur les " +"plateformes autres que Windows" + +#: spyder/widgets/status.py:107 +msgid "CPU:" +msgstr "CPU :" + +#: spyder/widgets/status.py:108 +msgid "CPU usage status: requires the `psutil` (>=v0.3) library" +msgstr "Occupation CPU : requiert la bibliothèque `psutil` (>=v0.3)" + +#: spyder/widgets/status.py:130 +msgid "Permissions:" +msgstr "Droits d'accès :" + +#: spyder/widgets/status.py:144 +msgid "End-of-lines:" +msgstr "Fins de ligne :" + +#: spyder/widgets/status.py:158 +msgid "Encoding:" +msgstr "Encodage :" + +#: spyder/widgets/status.py:171 +msgid "Line:" +msgstr "Ligne :" + +#: spyder/widgets/status.py:175 +msgid "Column:" +msgstr "Colonne :" + +#: spyder/widgets/tabs.py:146 +msgid "Browse tabs" +msgstr "Naviguer dans les onglets" + +#: spyder/widgets/tabs.py:275 +msgid "Close current tab" +msgstr "Fermer l'onglet" + +#: spyder/widgets/variableexplorer/arrayeditor.py:497 +msgid "It was not possible to copy values for this array" +msgstr "Impossible de copier les valeurs de ce tableau" + +#: spyder/widgets/variableexplorer/arrayeditor.py:532 +#: spyder/widgets/variableexplorer/arrayeditor.py:565 +#: spyder/widgets/variableexplorer/dataframeeditor.py:544 +#: spyder/widgets/variableexplorer/dataframeeditor.py:584 +msgid "Format" +msgstr "Format" + +#: spyder/widgets/variableexplorer/arrayeditor.py:537 +#: spyder/widgets/variableexplorer/dataframeeditor.py:548 +msgid "Resize" +msgstr "Ajuster" + +#: spyder/widgets/variableexplorer/arrayeditor.py:566 +#: spyder/widgets/variableexplorer/dataframeeditor.py:585 +msgid "Float formatting" +msgstr "Format de flottant" + +#: spyder/widgets/variableexplorer/arrayeditor.py:574 +#: spyder/widgets/variableexplorer/dataframeeditor.py:594 +msgid "Format (%s) is incorrect" +msgstr "Le format (%s) n'est pas valide" + +#: spyder/widgets/variableexplorer/arrayeditor.py:609 +msgid "Array is empty" +msgstr "Ce tableau est vide" + +#: spyder/widgets/variableexplorer/arrayeditor.py:612 +msgid "Arrays with more than 3 dimensions are not supported" +msgstr "Les tableaux de plus de trois dimensions ne sont pas pris en charge" + +#: spyder/widgets/variableexplorer/arrayeditor.py:615 +msgid "The 'xlabels' argument length do no match array column number" +msgstr "" +"La taille de 'xlabels' ne correspond pas au nombre de colonnes du tableau" + +#: spyder/widgets/variableexplorer/arrayeditor.py:619 +msgid "The 'ylabels' argument length do no match array row number" +msgstr "" +"La taille de 'ylabels' ne correspond pas au nombre de lignes du tableau" + +#: spyder/widgets/variableexplorer/arrayeditor.py:626 +msgid "%s arrays" +msgstr "Les tableaux %s" + +#: spyder/widgets/variableexplorer/arrayeditor.py:627 +msgid "%s are currently not supported" +msgstr "%s ne sont actuellement pas pris en charge" + +#: spyder/widgets/variableexplorer/arrayeditor.py:634 +msgid "NumPy array" +msgstr "Tableaux NumPy" + +#: spyder/widgets/variableexplorer/arrayeditor.py:636 +#: spyder/widgets/variableexplorer/arrayeditor.py:790 +msgid "Array editor" +msgstr "Éditeur de tableaux" + +#: spyder/widgets/variableexplorer/arrayeditor.py:638 +msgid "read only" +msgstr "lecture seule" + +#: spyder/widgets/variableexplorer/arrayeditor.py:668 +msgid "Record array fields:" +msgstr "Champs du tableau : " + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Data" +msgstr "Données" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Mask" +msgstr "Masque" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Masked data" +msgstr "Données masquées" + +#: spyder/widgets/variableexplorer/arrayeditor.py:691 +msgid "Axis:" +msgstr "Axe :" + +#: spyder/widgets/variableexplorer/arrayeditor.py:696 +msgid "Index:" +msgstr "Indice :" + +#: spyder/widgets/variableexplorer/arrayeditor.py:709 +msgid "Warning: changes are applied separately" +msgstr "Attention: les changements seront pris en compte séparément" + +#: spyder/widgets/variableexplorer/arrayeditor.py:710 +msgid "" +"For performance reasons, changes applied to masked array won't be reflected " +"in array's data (and vice-versa)." +msgstr "" +"Pour des questions de performance, les changements effectués sur les données " +"masquées ne seront pas reflétées sur les données du tableau (et " +"réciproquement)." + +#: spyder/widgets/variableexplorer/collectionseditor.py:116 +msgid "Index" +msgstr "Indice" + +#: spyder/widgets/variableexplorer/collectionseditor.py:121 +msgid "Tuple" +msgstr "Tuple" + +#: spyder/widgets/variableexplorer/collectionseditor.py:124 +msgid "List" +msgstr "Liste" + +#: spyder/widgets/variableexplorer/collectionseditor.py:127 +msgid "Dictionary" +msgstr "Dictionnaire" + +#: spyder/widgets/variableexplorer/collectionseditor.py:129 +msgid "Key" +msgstr "Clé" + +#: spyder/widgets/variableexplorer/collectionseditor.py:135 +msgid "Attribute" +msgstr "Attribut" + +#: spyder/widgets/variableexplorer/collectionseditor.py:137 +msgid "elements" +msgstr "éléments" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Size" +msgstr "Taille" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Type" +msgstr "Type" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Value" +msgstr "Valeur" + +#: spyder/widgets/variableexplorer/collectionseditor.py:409 +msgid "" +"Opening this variable can be slow\n" +"\n" +"Do you want to continue anyway?" +msgstr "" +"Afficher cette variable peut prendre du temps.\n" +"\n" +"Souhaitez-vous néanmoins continuer ?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:418 +msgid "" +"Spyder was unable to retrieve the value of this variable from the console." +"

The error mesage was:
%s" +msgstr "" +"Impossible de récupérer la valeur de cette variable depuis la console. " +"

Message d'erreur:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:583 +msgid "Edit item" +msgstr "Modifier" + +#: spyder/widgets/variableexplorer/collectionseditor.py:584 +msgid "Unable to assign data to item.

Error message:
%s" +msgstr "" +"Impossible d'assigner la valeur de l'objet.

Message d'erreur :" +"
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:644 +msgid "Resize rows to contents" +msgstr "Ajuster la hauteur des lignes" + +#: spyder/widgets/variableexplorer/collectionseditor.py:655 +#: spyder/widgets/variableexplorer/collectionseditor.py:990 +#: spyder/widgets/variableexplorer/collectionseditor.py:1007 +msgid "Plot" +msgstr "Tracer" + +#: spyder/widgets/variableexplorer/collectionseditor.py:659 +msgid "Histogram" +msgstr "Histogramme" + +#: spyder/widgets/variableexplorer/collectionseditor.py:663 +msgid "Show image" +msgstr "Afficher l'image" + +#: spyder/widgets/variableexplorer/collectionseditor.py:667 +#: spyder/widgets/variableexplorer/collectionseditor.py:1015 +msgid "Save array" +msgstr "Enregistrer le tableau" + +#: spyder/widgets/variableexplorer/collectionseditor.py:671 +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Insert" +msgstr "Insérer" + +#: spyder/widgets/variableexplorer/collectionseditor.py:674 +#: spyder/widgets/variableexplorer/collectionseditor.py:898 +msgid "Remove" +msgstr "Supprimer" + +#: spyder/widgets/variableexplorer/collectionseditor.py:684 +#: spyder/widgets/variableexplorer/collectionseditor.py:919 +msgid "Duplicate" +msgstr "Dupliquer" + +#: spyder/widgets/variableexplorer/collectionseditor.py:896 +msgid "Do you want to remove the selected item?" +msgstr "Souhaitez-vous supprimer l'élément sélectionné?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:897 +msgid "Do you want to remove all selected items?" +msgstr "Souhaitez-vous supprimer les éléments sélectionnés ?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:917 +msgid "New variable name:" +msgstr "Nouveau nom de variable" + +#: spyder/widgets/variableexplorer/collectionseditor.py:920 +msgid "Variable name:" +msgstr "Nom de variable:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +msgid "Key:" +msgstr "Clé :" + +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Value:" +msgstr "Valeur :" + +#: spyder/widgets/variableexplorer/collectionseditor.py:978 +msgid "Import error" +msgstr "Erreur d'import" + +#: spyder/widgets/variableexplorer/collectionseditor.py:979 +msgid "Please install matplotlib or guiqwt." +msgstr "Merci d'installer matplotlib ou guiqwt." + +#: spyder/widgets/variableexplorer/collectionseditor.py:991 +msgid "Unable to plot data.

Error message:
%s" +msgstr "" +"Impossible d'afficher les données

Message d'erreur :
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1008 +msgid "Unable to show image.

Error message:
%s" +msgstr "Impossible d'afficher l'image

Message d'erreur :
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1031 +msgid "Unable to save array

Error message:
%s" +msgstr "" +"Impossible d'enregistrer le tableau

Message d'erreur :
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1056 +msgid "It was not possible to copy this array" +msgstr "Impossible de copier cet tableaux" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1081 +msgid "Clipboard contents" +msgstr "Contenu du presse-papiers" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1096 +msgid "Import from clipboard" +msgstr "Importer depuis le presse-papiers" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1098 +msgid "Empty clipboard" +msgstr "Presse-papiers vide" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1099 +msgid "Nothing to be imported from clipboard." +msgstr "Aucune donnée ne peut être importée depuis le presse-papiers." + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To bool" +msgstr "Booléen" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To complex" +msgstr "Complexe" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To float" +msgstr "Flottant" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To int" +msgstr "Entier" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:452 +msgid "To str" +msgstr "Caractères" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:526 +msgid "%s editor" +msgstr "Éditeur de %s" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:558 +msgid "Column min/max" +msgstr "Colonne min/max" + +#: spyder/widgets/variableexplorer/importwizard.py:116 +#: spyder/widgets/variableexplorer/importwizard.py:431 +msgid "Import as" +msgstr "Importer en tant que" + +#: spyder/widgets/variableexplorer/importwizard.py:118 +msgid "data" +msgstr "données" + +#: spyder/widgets/variableexplorer/importwizard.py:122 +msgid "code" +msgstr "code" + +#: spyder/widgets/variableexplorer/importwizard.py:125 +#: spyder/widgets/variableexplorer/importwizard.py:504 +msgid "text" +msgstr "texte" + +#: spyder/widgets/variableexplorer/importwizard.py:138 +msgid "Column separator:" +msgstr "Séparateur de colonne :" + +#: spyder/widgets/variableexplorer/importwizard.py:142 +msgid "Tab" +msgstr "Tab" + +#: spyder/widgets/variableexplorer/importwizard.py:145 +#: spyder/widgets/variableexplorer/importwizard.py:163 +msgid "other" +msgstr "autre" + +#: spyder/widgets/variableexplorer/importwizard.py:156 +msgid "Row separator:" +msgstr "Séparateur de ligne :" + +#: spyder/widgets/variableexplorer/importwizard.py:160 +msgid "EOL" +msgstr "EOL" + +#: spyder/widgets/variableexplorer/importwizard.py:175 +msgid "Additional options" +msgstr "Options supplémentaires" + +#: spyder/widgets/variableexplorer/importwizard.py:179 +msgid "Skip rows:" +msgstr "Sauter des lignes :" + +#: spyder/widgets/variableexplorer/importwizard.py:190 +msgid "Comments:" +msgstr "Commentaires :" + +#: spyder/widgets/variableexplorer/importwizard.py:196 +msgid "Transpose" +msgstr "Transposer" + +#: spyder/widgets/variableexplorer/importwizard.py:434 +msgid "array" +msgstr "tableau" + +#: spyder/widgets/variableexplorer/importwizard.py:439 +msgid "list" +msgstr "liste" + +#: spyder/widgets/variableexplorer/importwizard.py:444 +msgid "DataFrame" +msgstr "DataFrame" + +#: spyder/widgets/variableexplorer/importwizard.py:487 +#: spyder/widgets/variableexplorer/importwizard.py:571 +msgid "Import wizard" +msgstr "Assistant d'importation" + +#: spyder/widgets/variableexplorer/importwizard.py:492 +msgid "Raw text" +msgstr "Text brut" + +#: spyder/widgets/variableexplorer/importwizard.py:495 +msgid "variable_name" +msgstr "nom_de_variable" + +#: spyder/widgets/variableexplorer/importwizard.py:506 +msgid "table" +msgstr "tableau" + +#: spyder/widgets/variableexplorer/importwizard.py:507 +msgid "Preview" +msgstr "Aperçu" + +#: spyder/widgets/variableexplorer/importwizard.py:511 +msgid "Variable Name" +msgstr "Nom de variable" + +#: spyder/widgets/variableexplorer/importwizard.py:534 +msgid "Done" +msgstr "Terminer" + +#: spyder/widgets/variableexplorer/importwizard.py:572 +msgid "" +"Unable to proceed to next step

Please check your entries." +"

Error message:
%s" +msgstr "" +"Impossible de passer à l'étape suivante

Merci de vérifier " +"votre saisie.

Message d'erreur :
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:196 +msgid "Refresh" +msgstr "Rafraîchir" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:200 +msgid "Refresh periodically" +msgstr "Rafraîchir périodiquement" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:207 +#: spyder/widgets/variableexplorer/namespacebrowser.py:487 +msgid "Import data" +msgstr "Importer des données" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:210 +#: spyder/widgets/variableexplorer/namespacebrowser.py:565 +#: spyder/widgets/variableexplorer/namespacebrowser.py:584 +msgid "Save data" +msgstr "Enregistrer les données" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:215 +msgid "Save data as..." +msgstr "Enregistrer les données sous..." + +#: spyder/widgets/variableexplorer/namespacebrowser.py:227 +msgid "Exclude references which name starts with an underscore" +msgstr "Exclure les références dont le nom commence par un tiret bas" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:235 +msgid "Exclude references which name is uppercase" +msgstr "Exclure les références dont le nom s'écrit en lettres capitales" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:242 +msgid "Exclude references which name starts with an uppercase character" +msgstr "Exclure les références dont le nom commence par une lettre majuscule" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:250 +msgid "" +"Exclude references to unsupported data types (i.e. which won't be handled/" +"saved correctly)" +msgstr "" +"Exclure les références dont le type n'est pas supporté par l'espace de " +"travail (en particulier, l'enregistrement ne fonctionnera pas)" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:345 +msgid "Object %s is not picklable" +msgstr "" +"L'objet %s n'est pas pris en charge par le protocole de sérialisation " +"de Pickle" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:507 +msgid "" +"Unsupported file extension '%s'

Would you like to import it " +"anyway (by selecting a known file format)?" +msgstr "" +"Extension de fichier non pris en charge '%s'

Souhaitez-vous " +"néanmoins ouvrir ce fichier (en choisissant un format de fichier connu) ?" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:515 +msgid "Open file as:" +msgstr "Ouvrir le fichier en tant que :" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:553 +msgid "Unable to load '%s'

Error message:
%s" +msgstr "Impossible d'ouvrir '%s'

Message d'erreur :
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:585 +msgid "Unable to save current workspace

Error message:
%s" +msgstr "" +"Impossible d'enregistrer l'espace de travail

Message d'erreur :" +"
%s" + +#: spyder/widgets/variableexplorer/texteditor.py:74 +msgid "Text editor" +msgstr "Éditeur de texte" + +#: spyder/widgets/variableexplorer/utils.py:29 +msgid "View and edit DataFrames and Series in the Variable Explorer" +msgstr "" +"Voir et éditer les Dataframes et les Series dans l'explorateur de variables" + +#: spyder/widgets/variableexplorer/utils.py:34 +msgid "View and edit two and three dimensional arrays in the Variable Explorer" +msgstr "" +"Voir et éditer Tableaux de deux et trois dimensions dans l'explorateur de " +"variables" + +#: spyder/workers/updates.py:89 spyder/workers/updates.py:91 +msgid "Unable to retrieve information." +msgstr "Impossible de charger l'information" + +#: spyder/workers/updates.py:93 +msgid "" +"Unable to connect to the internet.

Make sure the connection is " +"working properly." +msgstr "" +"impossible de se connecter à internet.

Vérifiez que votre accès à " +"Internet fonctionne bien" + +#: spyder/workers/updates.py:96 +msgid "Unable to check for updates." +msgstr "Impossible de vérifier les mises à jour" + +#~ msgid "Truncate values" +#~ msgstr "Tronquer les valeurs" + +#~ msgid "Reload last session" +#~ msgstr "Recharger la session précédente" + +#~ msgid "Load session..." +#~ msgstr "Charger une session..." + +#~ msgid "Load Spyder session" +#~ msgstr "Charger une session Spyder" + +#, fuzzy +#~ msgid "Save session..." +#~ msgstr "Enregistrer la session" + +#~ msgid "Save current session and quit application" +#~ msgstr "Enregistrer la session en cours et quitter l'application" + +#~ msgid "Open session" +#~ msgstr "Ouvrir une session" + +#~ msgid "Spyder sessions" +#~ msgstr "Sessions Spyder" + +#~ msgid "Save session" +#~ msgstr "Enregistrer la session" + +#, fuzzy +#~ msgid "Jupyter Qtconsole integration" +#~ msgstr "Intégration de la console IPython" + +#~ msgid "Python executable" +#~ msgstr "Exécutable Python" + +#~ msgid "Trying to kill a kernel?" +#~ msgstr "Tentative d'arrêt d'un noyau" + +#~ msgid "" +#~ "You can't close this kernel because it has one or more consoles connected " +#~ "to it.

You need to close them instead or you can kill the kernel " +#~ "using the second button from right to left." +#~ msgstr "" +#~ "Le noyau ne peut pas être fermé car au moins une console y est connectée. " +#~ "

Veuillez soit fermer toutes les consoles connectées ou appuyer " +#~ "sur le second bouton en partant de la droite pour tuer le processus du " +#~ "noyau." + +#~ msgid "Kernel" +#~ msgstr "Noyau" + +#~ msgid "" +#~ "Either:
  1. Your IPython frontend and kernel versions are " +#~ "incompatible or
  2. You don't have IPython installed in " +#~ "your external interpreter.
In any case, we're sorry but we can't " +#~ "create a console for you." +#~ msgstr "" +#~ "
  1. Soit les versions de votre interface IPython et du noyau sont " +#~ "incompatibles,
  2. soit IPython n'est pas installé pour " +#~ "votre interpréteur externe.
Dans tous les cas nous sommes " +#~ "désolés mais nous ne pouvons ouvrir une console pour vous." + +#~ msgid "Kernel %s" +#~ msgstr "Noyau %s" + +#~ msgid "Open an IPython console" +#~ msgstr "Ouvrir une console IPython" + +#~ msgid "" +#~ "The console monitor was disabled: the IPython kernel will be started as " +#~ "expected, but an IPython console will have to be connected manually to " +#~ "the kernel." +#~ msgstr "" +#~ "Le moniteur (console) a été désactivé. Par conséquent, le noyau IPython " +#~ "sera démarré mais la console IPython devra y être connectée manuellement." + +#~ msgid "" +#~ "UMR excluded modules:\n" +#~ "(example: guidata, guiqwt)" +#~ msgstr "" +#~ "Modules non rechargés par l'UMR :\n" +#~ "(exemple: guidata, guiqwt)" + +#~ msgid "" +#~ "This feature requires the Matplotlib library.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "Cette fonctionnalité nécessite l'installation du module Matplotlib.\n" +#~ "Ce dernier n'est apparemment pas installé." + +#~ msgid "" +#~ "This feature requires the Sympy library.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "Cette fonctionnalité nécessite l'installation du module Sympy.\n" +#~ "Ce dernier n'est apparemment pas installé." + +#~ msgid "&Font..." +#~ msgstr "&Police..." + +#~ msgid "Set font style" +#~ msgstr "Changer la police d'écriture" + +#~ msgid "Select a new font" +#~ msgstr "Sélectionner une police d'écriture" + +#~ msgid "" +#~ "The kernel failed to start!! That's all we know... Please close this " +#~ "console and open a new one." +#~ msgstr "" +#~ "Le démarrage du noyau a échoué ! C'est malheureusement tout ce que nous " +#~ "savons... Merci de fermer cette console et d'en ouvrir une nouvelle." + +#~ msgid "" +#~ "It seems the kernel died unexpectedly. Use 'Restart kernel' to continue " +#~ "using this console." +#~ msgstr "" +#~ "Le noyau a été arrêté de façon inattendue. Redémarrez le noyau pour " +#~ "continuer d'utiliser cette console." + +#~ msgid "Kernel process is either remote or unspecified. Cannot interrupt" +#~ msgstr "" +#~ "Le processus du noyau est soit distant, soit non spécifié : impossible " +#~ "d'arrêter le noyau." + +#~ msgid "Kernel process is either remote or unspecified. Cannot restart." +#~ msgstr "" +#~ "Le processus du noyau est soit distant, soit non spécifié : impossible de " +#~ "redémarrer le noyau." + +#~ msgid "its own configuration file" +#~ msgstr "son propre fichier de configuration" + +#~ msgid " and " +#~ msgstr " et " + +#~ msgid "the following projects:
%s" +#~ msgstr "les projets suivants :
%s" + +#~ msgid "Project..." +#~ msgstr "Projet..." + +#~ msgid "Existing Spyder project" +#~ msgstr "Projet Spyder existant" + +#~ msgid "Existing Pydev project" +#~ msgstr "Projet Pydev existant" + +#~ msgid "Close unrelated projects" +#~ msgstr "Fermer les projets non associés" + +#~ msgid "Edit related projects" +#~ msgstr "Modifier les projets associés" + +#~ msgid "Add to PYTHONPATH" +#~ msgstr "Ajouter à PYTHONPATH" + +#~ msgid "Remove from PYTHONPATH" +#~ msgstr "Retirer de PYTHONPATH" + +#~ msgid "Properties" +#~ msgstr "Propriétés" + +#~ msgid "" +#~ "The workspace was unable to load or save %s

Please check if you " +#~ "have the permission to write the associated configuration files." +#~ msgstr "" +#~ "L'espace de travail n'est pas parvenu à ouvrir ou enregistrer " +#~ "%s

Veuillez vérifier que vous disposez bien des droits d'écriture " +#~ "sur les fichiers de configuration associés." + +#~ msgid "Import directory" +#~ msgstr "Importer un répertoire" + +#~ msgid "" +#~ "The following directory is not in workspace:
%s

Do you " +#~ "want to continue (and copy the directory to workspace)?" +#~ msgstr "" +#~ "Le répertoire suivant n'est pas dans l'espace de travail :
%s

Souhaitez-vous continuer (et copier ce répertoire dans l'espace " +#~ "de travail) ?" + +#~ msgid "The project %s is already opened!" +#~ msgstr "Le projet %s est déjà ouvert !" + +#~ msgid "" +#~ "The project root path directory is inside the workspace but not as the " +#~ "expected tree level. It is not a directory of the workspace:
%s" +#~ msgstr "" +#~ "Le répertoire racine du projet est bien à l'intérieur de l'espace de " +#~ "travail mais pas au niveau d'arborescence attendu. Ce n'est pas un " +#~ "répertoire de l'espace de travail :
%s" + +#~ msgid "A project named %s already exists" +#~ msgstr "Un projet nommé %s existe déjà" + +#~ msgid "" +#~ "Invalid project name.

Name must match the following regular " +#~ "expression:
%s" +#~ msgstr "" +#~ "Nom de projet incorrect.

Le nom doit respecter l'expression " +#~ "régulière suivante :
%s" + +#~ msgid "" +#~ "The following directory is not empty:
%s

Do you want to " +#~ "continue?" +#~ msgstr "" +#~ "Le répertoire suivant n'est pas vide :
%s

Souhaitez-vous " +#~ "néanmoins continuer ?" + +#~ msgid "New project" +#~ msgstr "Nouveau projet" + +#~ msgid "" +#~ "The current workspace has not been configured yet.\n" +#~ "Do you want to do this now?" +#~ msgstr "" +#~ "L'espace de travail n'a pas encore été défini.\n" +#~ "Souhaitez-vous le faire maintenant ?" + +#~ msgid "Import existing project" +#~ msgstr "Importer un projet existant" + +#~ msgid "Select projects to import" +#~ msgstr "Sélectionner les projets à importer" + +#~ msgid "The folder %s does not contain a valid %s project" +#~ msgstr "Le dossier %s ne contient pas de projet %s valide" + +#~ msgid "Import existing Pydev project" +#~ msgstr "Importer un projet Pydev" + +#~ msgid "" +#~ "Unable to read Pydev project %s

Error message:
%s" +#~ msgstr "" +#~ "Impossible d'ouvrir le projet Pydev %s

Message " +#~ "d'erreur :
%s" + +#~ msgid "Select projects which are related to %s" +#~ msgstr "Sélectionner les projets à associer à %s" + +#, fuzzy +#~ msgid "" +#~ "Statistics on source files only:
(Python, Cython, IPython, Enaml,C/C+" +#~ "+, Fortran)

%s files.
%s lines of code." +#~ msgstr "" +#~ "Statistique sur les fichiers source uniquement:
(Python, C/C++, " +#~ "Fortran)

%s fichiers.
%s lignes de code." + +#~ msgid "Select an existing workspace directory, or create a new one" +#~ msgstr "Sélectionner un espace de travail existant, ou en créer un nouveau" + +#~ msgid "" +#~ "What is the workspace?

A Spyder workspace is " +#~ "a directory on your filesystem that contains Spyder projects and ." +#~ "spyderworkspace configuration file.

A Spyder project is " +#~ "a directory with source code (and other related files) and a " +#~ "configuration file (named .spyderproject) with project settings " +#~ "(PYTHONPATH, linked projects, ...).
" +#~ msgstr "" +#~ "Qu'est-ce que l'espace de travail ?

L'espace de " +#~ "travail Spyder est un répertoire de votre système de fichiers qui " +#~ "contient les projets Spyder et le fichier de configuration ." +#~ "spyderworkspace.

Un projet Spyder est un répertoire " +#~ "contenant du code source (et d'autres fichiers) ainsi qu'un fichier de " +#~ "configuration (nommé .spyderproject) dans lequel sont stockés les " +#~ "réglages du projet (PYTHONPATH, projets liés, ...).
" + +#~ msgid "This is the current workspace directory" +#~ msgstr "Ceci est l'espace de travail actif" + +#~ msgid "" +#~ "The following directory is not a Spyder workspace:
%s

Do you " +#~ "want to create a new workspace in this directory?" +#~ msgstr "" +#~ "Le répertoire suivant n'est pas un espace de travail Spyder :
%s

Souhaitez-vous créer un nouvel espace de travail dans ce " +#~ "répertoire ?" + +#~ msgid "Unable to retrieve data.

Error message:
%s" +#~ msgstr "" +#~ "Impossible d'accéder aux données

Message d'erreur :
%s" + +#~ msgid "Matched parentheses:" +#~ msgstr "Parenthèse fermée :" + +#~ msgid "Unmatched parentheses:" +#~ msgstr "Parenthèse non fermée :" + +#~ msgid "Set shell font style" +#~ msgstr "Changer la police d'écriture de la console" + +#~ msgid "Text and margin font style" +#~ msgstr "Police d'écriture du texte et de la marge" + +#~ msgid "tab" +#~ msgstr "tabulation" + +#~ msgid "Breakpoints" +#~ msgstr "Points d'arrêt" + +#~ msgid "Exit" +#~ msgstr "Sortir" + +#~ msgid "Exit Debug" +#~ msgstr "Quitter le débogage" + +#~ msgid "Rich text help on the Object Inspector" +#~ msgstr "Texte enrichi dans l'inspecteur d'objets" + +#~ msgid "Object inspector" +#~ msgstr "Inspecteur d'objets" + +#~ msgid "Preferences > Object Inspector" +#~ msgstr "Préférences > Inspecteur d'objets" + +#~ msgid "Set as current console's working directory" +#~ msgstr "Changer le répertoire de travail de la console actuelle" + +#~ msgid "Save session and quit..." +#~ msgstr "Enregistrer la session et quitter..." + +#~ msgid "Loading object inspector..." +#~ msgstr "Chargement de l'inspecteur d'objet..." + +#, fuzzy +#~ msgid "The Object Inspector" +#~ msgstr "Inspecteur d'objets" + +#~ msgid "(Experimental) Editor's code completion, go-to-definition and help" +#~ msgstr "(Expérimental) : autocomplétion, aller à la définition, aide." + +#~ msgid "" +#~ "This path is incorrect.\n" +#~ "Enter a correct directory path,\n" +#~ "then press enter to validate" +#~ msgstr "" +#~ "Ce chemin d'accès n'est pas valide :\n" +#~ "veuillez entrer un chemin d'accès correct,\n" +#~ "puis appuyer sur Entrée pour le valider" + +#~ msgid "Save Python script" +#~ msgstr "Enregistrer le script Python" + +#~ msgid "PyQt" +#~ msgstr "PyQt" + +#~ msgid "API selection for QString and QVariant objects:" +#~ msgstr "Sélection de l'API des objets QString et QVariant :" + +#~ msgid "API #1" +#~ msgstr "API n°1" + +#~ msgid "API #2" +#~ msgstr "API n°2" + +#~ msgid "Default API" +#~ msgstr "API par défaut" + +#~ msgid "" +#~ "PyQt API #1 is the default
API for Python 2. PyQt API #2 is the " +#~ "default API for Python 3 and is compatible with PySide." +#~ msgstr "" +#~ "L'API n°1 de PyQt est l'API par défaut pour Python 2. L'API n°2 est l'API " +#~ "par défaut pour Python 3 : c'est l'API qui est compatible avec PySide." + +#~ msgid "Ignore API change errors (sip.setapi)" +#~ msgstr "Ignorer les erreurs de changement d'API (sip.setapi)" + +#~ msgid "" +#~ "Enabling this option will ignore
errors when changing PyQt API. As " +#~ "PyQt does not support dynamic API changes, it is strongly recommended to " +#~ "use this feature wisely, e.g. for debugging purpose." +#~ msgstr "" +#~ "L'activation de cette option permet d'ignorer les erreurs liées aux " +#~ "changements\n" +#~ "d'API de PyQt. Vu que PyQt ne prend pas en charge le changement " +#~ "dynamique\n" +#~ " d'API, il est fortement recommandé d'utiliser cette fonctionnalité " +#~ "exceptionnellement,\n" +#~ "par exemple pour du débogage." + +#~ msgid "Matplotlib" +#~ msgstr "Matplotlib" + +#~ msgid "GUI backend:" +#~ msgstr "Backend graphique :" + +#~ msgid "" +#~ "Set the GUI toolkit used by
Matplotlib to show figures (default: " +#~ "Qt4Agg)" +#~ msgstr "" +#~ "Spécifie la bibliothèque d'interfaces graphiques à utiliser pour " +#~ "l'affichage des figures Matplotlib (par défaut : Qt4Agg)" + +#~ msgid "Mod1" +#~ msgstr "Mod1" + +#~ msgid "Mod2" +#~ msgstr "Mod2" + +#~ msgid "Mod3" +#~ msgstr "Mod3" + +#, fuzzy +#~ msgid "The Internal Console" +#~ msgstr "Console interne" + +#~ msgid "File list management" +#~ msgstr "Gestionnaire de fichiers" + +#~ msgid "Filter:" +#~ msgstr "Filtre :" + +#~ msgid "(press Enter to edit file)" +#~ msgstr "(appuyer sur Entrée pour modifier le fichier)" + +#~ msgid "&Edit file" +#~ msgstr "Modifi&er le fichier" + +#~ msgid "&Close file" +#~ msgstr "&Fermer le fichier" + +#~ msgid "Hint: press Alt to show accelerators" +#~ msgstr "Astuce : la touche Alt affiche les accélérateurs" + +#~ msgid "Vertical dockwidget tabs" +#~ msgstr "Onglets distribués verticalement" + +#~ msgid "Custom dockwidget margin:" +#~ msgstr "Marges personnalisées :" + +#~ msgid "" +#~ "Note: add analysis:ignore in a comment to ignore code/style " +#~ "analysis warnings. For more informations on style guide for Python code, " +#~ "please refer to the %s page." +#~ msgstr "" +#~ "Note: ajouter analysis:ignore dans un commentaire pour " +#~ "ignorer les résultats de l'analyse de code ou de style. Pour plus " +#~ "d'informations sur les recommandations officielles de style d'écriture " +#~ "avec le langage Python, veuillez visiter la page de la %s." + +#~ msgid "Style analysis" +#~ msgstr "Analyse de style" + +#~ msgid "Qt (PyQt/PySide)" +#~ msgstr "Qt (PyQt/PySide)" + +#~ msgid "Use a completion widget" +#~ msgstr "Utiliser un widget de complétion de code" + +#~ msgid "Use a widget instead of plain text output for tab completion" +#~ msgstr "Utiliser un widget de complétion au lieu d'une liste en texte brut" + +#~ msgid "Switch to/from layout %d" +#~ msgstr "Basculer vers/depuis la disposition %d" + +#~ msgid "Set layout %d" +#~ msgstr "Définir la disposition %d" + +#~ msgid "" +#~ "%s will be closed.\n" +#~ "Do you want to kill the associated kernel and all of its clients?" +#~ msgstr "" +#~ "%s va être fermé.\n" +#~ "Souhaitez-vous fermer également le noyau associé et tous ses autres " +#~ "clients ?" + +#~ msgid "Install Spyder's input hook for Qt" +#~ msgstr "Installer le \"input hook\" de Spyder pour Qt" + +#~ msgid "" +#~ "PyQt installs an input hook that allows
creating and interacting with " +#~ "Qt widgets in an interactive console without blocking it. On Windows " +#~ "platforms, it is strongly recommended to replace it by Spyder's. " +#~ "Regarding PySide, note that it does not install an input hook, so it is " +#~ "required to enable this feature in order to be able to manipulate PySide/" +#~ "Qtobjects interactively." +#~ msgstr "" +#~ "PyQt installe un \"input hook\", mécanisme permettant d'interagir avec " +#~ "des widgets Qt dans une console Python sans bloquer cette dernière. Sous " +#~ "Windows, il est fortement conseillé de le remplacer par celui de Spyder. " +#~ "Concernant PySide, aucun \"input hook\" n'étant implémenté, il est " +#~ "également recommandé d'activer cette option pour pouvoir manipuler des " +#~ "objets Qt de manière interactive." + +#~ msgid "Could not open ssh tunnel\n" +#~ msgstr "Impossible d'ouvrir un tunnel ssh\n" + +#~ msgid "Mismatch between kernel and frontend" +#~ msgstr "Incompatibilité entre le noyau et l'interface" + +#~ msgid "" +#~ "Your IPython frontend and kernel versions are incompatible!!

We're sorry but we can't create an IPython console for you." +#~ msgstr "" +#~ "Les versions d'IPython de votre noyau et de l'interface sont " +#~ "incompatibles !

Nous sommes désolés, mais nous ne pouvons " +#~ "pas vous ouvrir une console IPython." + +#~ msgid "Always edit in-place" +#~ msgstr "Édition en ligne pour tous les types" + +#~ msgid "Show collection contents" +#~ msgstr "Afficher le contenu des séquences" + +#~ msgid "" +#~ "Resizing cells of a table of such size could take a long time.\n" +#~ "Do you want to continue anyway?" +#~ msgstr "" +#~ "Redimensionner les cellules d'un tableau d'une telle taille peut prendre " +#~ "du temps.\n" +#~ "Souhaitez-vous néanmoins continuer ?" + +#~ msgid "Interrupt kernel" +#~ msgstr "Interrompre le noyau" + +#~ msgid "Run &selection" +#~ msgstr "Exécuter la &sélection" + +#~ msgid "Patch Matplotlib figures" +#~ msgstr "Patcher les figures Matplotlib" + +#~ msgid "" +#~ "Patching Matplotlib library will add a button to customize figure options " +#~ "(Qt4Agg only) and fix some issues." +#~ msgstr "" +#~ "Appliquer un patch à la bibliothèque Matplotlib permet d'ajouter un " +#~ "bouton (au backend Qt4Agg) pour modifier les options des courbes et " +#~ "images affichées et de corriger quelques bogues." + +#~ msgid "(for example: kernel-3764.json, or simply 3764)" +#~ msgstr "(exemple: `kernel-3764.json`, ou simplement `3764`)" + +#~ msgid "Provide an IPython kernel connection file:" +#~ msgstr "Fichier de connexion du noyau IPython :" + +#~ msgid "Interpreter" +#~ msgstr "Interpréteur" + +#~ msgid "Dedicated Python interpreter" +#~ msgstr "Interpréteur Python dédié" + +#~ msgid "Open Python interpreter here" +#~ msgstr "Ouvrir un interpréteur Python ici" + +#~ msgid "Import as array" +#~ msgstr "Importer en tant que tableau" + +#~ msgid "(not installed)" +#~ msgstr "(non installé)" + +#~ msgid "Show single completion" +#~ msgstr "Afficher les listes de complétion avec un choix unique" + +#~ msgid "Open a Python interpreter at startup" +#~ msgstr "Ouvrir un interpréteur Python au démarrage" + +#~ msgid "Open an IPython console at startup" +#~ msgstr "Ouvrir une console IPython au démarrage" + +#~ msgid "Close current plugin" +#~ msgstr "Fermer la fenêtre courante" + +#~ msgid "Windows" +#~ msgstr "Fenêtres" + +#, fuzzy +#~ msgid "Plugins" +#~ msgstr " lignes" + +#~ msgid "Web resources" +#~ msgstr "Ressources en ligne" + +#, fuzzy +#~ msgid "Qt help" +#~ msgstr "Aide Qt" + +#, fuzzy +#~ msgid "IPython help" +#~ msgstr "Aide Python :" + +#~ msgid "Balloon tips" +#~ msgstr "Info-bulles" + +#~ msgid "Close current dockwidget" +#~ msgstr "Fermer le panneau actif" + +#~ msgid "IPython Help" +#~ msgstr "Aide IPython" + +#~ msgid "Windows and toolbars" +#~ msgstr "Fenêtres et barres d'outils" + +#~ msgid "Automatic notification to object inspector" +#~ msgstr "Notification automatique à l'inspecteur d'objets" + +#~ msgid "" +#~ "If this option is enabled, object inspector\n" +#~ "will automatically show informations on functions\n" +#~ "entered in editor (this is triggered when entering\n" +#~ "a left parenthesis after a valid function name)" +#~ msgstr "" +#~ "Si cette option est activée, l'inspecteur d'objet\n" +#~ "affichera automatiquement des informations\n" +#~ "sur les fonctions saisies dans l'éditeur\n" +#~ "(le mécanisme est déclenché par la saisie d'une\n" +#~ "parenthèse gauche après un nom valide de fonction)" + +#~ msgid "Create a new Python script" +#~ msgstr "Créer un nouveau script Python" + +#~ msgid "Open text file" +#~ msgstr "Ouvrir un fichier texte" + +#~ msgid "" +#~ "Run current cell \n" +#~ "(see Editor documentation \n" +#~ "for more details on cells)" +#~ msgstr "" +#~ "Exécuter la cellule en cours d'édition\n" +#~ "(voir la documentation de l'Editeur, pour plus de détails sur les " +#~ "cellules)" + +#~ msgid "" +#~ "If this option is enabled, object inspector\n" +#~ "will automatically show informations on functions\n" +#~ "entered in console (this is triggered when entering\n" +#~ "a left parenthesis after a valid function name)" +#~ msgstr "" +#~ "Si cette option est activée, l'inspecteur d'objet affichera " +#~ "automatiquement des informations sur les fonctions saisies dans la " +#~ "console (le mécanisme est déclenché par la saisie d'une parenthèse gauche " +#~ "après un nom valide de fonction)" + +#~ msgid "Open a Python &interpreter" +#~ msgstr "Ouvrir un &interpréteur Python" + +#~ msgid "
Installed version: %s" +#~ msgstr "
Version installée: %s" + +#~ msgid "" +#~ "Unable to open IPython console because no supported IPython version was " +#~ "found.

Supported IPython versions: %s" +#~ msgstr "" +#~ "Impossible d'ouvrir une console IPython car aucune version prise en " +#~ "charge n'est installée.

Versions IPython prises en charge: " +#~ "%s" + +#~ msgid "&Interpreters" +#~ msgstr "&Interpréteurs" + +#~ msgid "Open Spyder path manager" +#~ msgstr "Ouvre le gestionnaire de chemin d'accès de Spyder" + +#~ msgid "Qt Assistant" +#~ msgstr "Qt Assistant (documentation Qt)" + +#~ msgid "Maximize current plugin to fit the whole application window" +#~ msgstr "" +#~ "Agrandir la fenêtre courante sur toute la surface de la fenêtre principale" + +#~ msgid "" +#~ "Restore current plugin to its original size and position within the " +#~ "application window" +#~ msgstr "" +#~ "Réduire la fenêtre courante à sa taille et position d'origine au sein de " +#~ "la fenêtre principale" + +#~ msgid "" +#~ "Run current block \n" +#~ "(see Editor section in documentation \n" +#~ "for more details on blocks) \n" +#~ "and advance to the next block" +#~ msgstr "" +#~ "Exécuter le bloc (voir la section 'Editor' de la documentation) \n" +#~ "et avancer jusqu'au bloc suivant" + +#~ msgid "Version" +#~ msgstr "Version" + +#~ msgid "Status" +#~ msgstr "Etat" + +#~ msgid "" +#~ "This feature requires the pywin32 module.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "Cette fonctionnalité nécessite l'installation du module pywin32.\n" +#~ "Ce dernier n'est apparemment pas installé." + +#~ msgid "Step Over" +#~ msgstr "Pas en avant" + +#~ msgid "" +#~ "Run selection or current \n" +#~ "block of lines" +#~ msgstr "Exécuter la sélection ou le bloc de lignes" + +#~ msgid "Debug current script in selected console" +#~ msgstr "Déboguer le script courant dans la console sélectionnée" + +#~ msgid "Debug Step Over" +#~ msgstr "Pas en avant (débogage)" + +#~ msgid "Debug Continue" +#~ msgstr "Continuer (débogage)" + +#~ msgid "Debug Step Into" +#~ msgstr "Pas vers l'intérieur (débogage)" + +#~ msgid "Debug Step Return" +#~ msgstr "Pas vers l'extérieur (débogage)" + +#~ msgid "" +#~ "Run selected script in\n" +#~ "current console" +#~ msgstr "Exécuter le script sélectionné dans la console courante" + +#~ msgid "Edit Run settings" +#~ msgstr "Modifier les options d'exécution" + +#~ msgid "" +#~ "Run again last script in current\n" +#~ "console with the same options" +#~ msgstr "" +#~ "Exécuter de nouveau le dernier script dans la console courante avec les " +#~ "mêmes options" + +#~ msgid "" +#~ "Run selected text or current block\n" +#~ "of lines inside current console" +#~ msgstr "" +#~ "Exécuter le texte sélectionné ou le bloc de lignes \n" +#~ "dans l'interpréteur de la console courante" + +#~ msgid "Run active script in a new Python interpreter" +#~ msgstr "Exécuter le script actuel dans un nouvel interpréteur Python" + +#~ msgid "" +#~ "Debug current script in external console\n" +#~ "(external console is executed in a separate process)" +#~ msgstr "" +#~ "Déboguer le script en cours d'édition dans la console externe\n" +#~ "(la console externe est exécutée dans un processus séparé)" + +#~ msgid "Edit run configurations" +#~ msgstr "Modifier les configurations d'exécution des scripts récents" + +#~ msgid "Run %s" +#~ msgstr "Exécution de %s" + +#~ msgid "Run configurations" +#~ msgstr "Configurations d'exécution" + +#~ msgid "Type \"copyright\", \"credits\" or \"license\" for more information." +#~ msgstr "" +#~ "Type \"copyright\", \"credits\" or \"license\" for more information." + +#~ msgid "Start an IPython kernel at startup" +#~ msgstr "Démarrer un noyau IPython au démarrage" + +#~ msgid "This option is not available for IPython versions prior to v0.12." +#~ msgstr "" +#~ "Cette option est désactivée pour les versions de IPython antérieures à " +#~ "v0.12." + +#, fuzzy +#~ msgid "Format: " +#~ msgstr "Format" + +#, fuzzy +#~ msgid " Source" +#~ msgstr "Source" + +#~ msgid "Open &interpreter" +#~ msgstr "Ouvrir un &interpréteur" + +#~ msgid "Start a new IPython kernel" +#~ msgstr "Démarrer un nouveau noyau IPython" + +#~ msgid "Client" +#~ msgstr "Client" + +#~ msgid "New IPython client..." +#~ msgstr "Nouveau client IPython..." + +#~ msgid "Qt" +#~ msgstr "Qt" + +#~ msgid "OS X" +#~ msgstr "OS X" + +#~ msgid "Gtk" +#~ msgstr "Gtk" + +#~ msgid "Wx" +#~ msgstr "Wx" + +#~ msgid "Tkinter" +#~ msgstr "Tkinter" + +#~ msgid "IPython kernels" +#~ msgstr "Noyaux IPython" + +#~ msgid "" +#~ "Note:
IPython >=v0.12 is not installed on this computer." +#~ msgstr "" +#~ "Note :
IPython >=v0.12 n'est pas installé sur cet " +#~ "ordinateur." + +#~ msgid "Set the appropriate IPython color option" +#~ msgstr "Utiliser le réglage de couleur IPython approprié" + +#~ msgid "Open an IPython interpreter at startup" +#~ msgstr "Ouvrir un interpréteur IPython au démarrage" + +#~ msgid "" +#~ "This option is not available for IPython\n" +#~ "versions which are not fully supported\n" +#~ "through Spyder's console (i.e. IPython v0.11+)." +#~ msgstr "" +#~ "Cette option est désactivée pour les versions de IPython\n" +#~ "qui ne sont entièrement prises en charge par Spyder\n" +#~ "à travers la console (i.e. IPython v0.11+)." + +#~ msgid "IPython interpreter command line options" +#~ msgstr "IPython : options en ligne de commande" + +#~ msgid "Open an IPython interpreter" +#~ msgstr "Ouvrir un interpréteur IPython" + +#~ msgid "Open IPython here" +#~ msgstr "Ouvrir IPython ici" + +#~ msgid "Please install the %s tool named '%s'" +#~ msgstr "Merci d'installer l'outil %s appelé '%s'" + +#~ msgid "Replace PyQt input hook by Spyder's" +#~ msgstr "Remplacer le \"input hook\" de PyQt par celui de Spyder" + +#~ msgid "What is the workspace?" +#~ msgstr "Qu'est-ce que l'espace de travail ?" + +#~ msgid "" +#~ "A Spyder project is a folder with source code files (and any other kind " +#~ "of related files) and a configuration file (named .spyderproject) " +#~ "which stores the project settings (PYTHONPATH, related projects, ...)." +#~ "

The workspace is a directory, which contains Spyder projects " +#~ "(top level subdirectories) and a configuration file (named ." +#~ "spyderworkspace). " +#~ msgstr "" +#~ "Un projet Spyder est un répertoire contenant des fichiers source (et tout " +#~ "autre type de fichier) et un fichier de configuration (nommé ." +#~ "spyderproject) qui stocke les paramètres du projet (PYTHONPATH, " +#~ "projets associés, etc.).

L'espace de travail est un répertoire " +#~ "contenant des projets Spyder (sous-répertoires uniquement) et un " +#~ "fichier de configuration (nommé .spyderworkspace)." + +#~ msgid "Matplotlib backend (default: Qt4Agg):" +#~ msgstr "Backend Matplotlib (valeur par défaut: Qt4Agg) :" + +#~ msgid "ETS_TOOLKIT (default value: qt4):" +#~ msgstr "ETS_TOOLKIT (valeur par défaut: qt4) :" + +#~ msgid "&Interact" +#~ msgstr "&Interagir" + +#~ msgid "" +#~ "The project explorer shows a tree view of projects: the root of this tree " +#~ "is called the workspace.

Each project is associated to a simple " +#~ "source code folder containing a configuration file (named ." +#~ "spyderproject) which stores the project settings (PYTHONPATH, related " +#~ "projects, ...). The workspace is also associated to a folder containing a " +#~ "configuration file (named .spyderworkspace) and the folders " +#~ "associated to its projects.

In other words, the workspace is " +#~ "nothing but a list of projects whose associated folder share the same " +#~ "parent directory." +#~ msgstr "" +#~ "L'explorateur de projet affiche une arborescence de projets dont la " +#~ "racine est appelée l'espace de travail.

Chaque projet est associé " +#~ "à un simple dossier de code source contenant un fichier de configuration " +#~ "(nommé .spyderproject) qui stocke les paramètres du projet " +#~ "(PYTHONPATH, projets associés, etc.). L'espace de travail est aussi " +#~ "associé à un dossier qui contient un fichier de configuration (nommé ." +#~ "spyderworkspace) et tous les dossiers associés à ses projets." +#~ "

En d'autres termes, l'espace de travail est simplement une liste " +#~ "de projets dont les dossiers associés ont le même répertoire parent." + +#~ msgid "Create a new workspace directory" +#~ msgstr "Créer un nouvel espace de travail" + +#~ msgid "New folder..." +#~ msgstr "Nouveau répertoire..." + +#~ msgid "New file..." +#~ msgstr "Nouveau fichier..." + +#~ msgid "Unable to delete selected file

Error message:
%s" +#~ msgstr "" +#~ "Impossible de supprimer le fichier sélectionné

Message " +#~ "d'erreur :
%s" + +#~ msgid "Unable to rename selected file

Error message:
%s" +#~ msgstr "" +#~ "Impossible de renommer le fichier sélectionné

Message " +#~ "d'erreur :
%s" + +#~ msgid "Folder name" +#~ msgstr "Nom du répertoire" + +#~ msgid "Select project root path" +#~ msgstr "Sélectionner la racine du projet" + +#~ msgid "Edit filename filter" +#~ msgstr "Modifier le filtre des types de fichier affichés" + +#~ msgid "regular expressions" +#~ msgstr "expressions régulières" + +#~ msgid "global patterns" +#~ msgstr "syntaxe globale" + +#~ msgid "Include" +#~ msgstr "Inclure" + +#~ msgid "Exclude" +#~ msgstr "Exclure" + +#~ msgid "Warning:" +#~ msgstr "Attention :" + +#~ msgid "" +#~ "%s is not properly installed
(opening a terminal and typing " +#~ "\"%s script.py\" do not work)" +#~ msgstr "" +#~ "%s n'est pas installé correctement
(l'exécution dans un " +#~ "terminal de \"%s script.py\" ne fonctionne pas)" + +#~ msgid "More informations on style guide for Python code: %s." +#~ msgstr "" +#~ "Pour plus d'informations sur les recommandations de style d'écriture du " +#~ "langage Python : %s." + +#~ msgid "unknown" +#~ msgstr "inconnu" + +#~ msgid "Startup script:" +#~ msgstr "Script de démarrage :" + +#~ msgid "Print" +#~ msgstr "Impression" + +#~ msgid "Unable to print document '%s'" +#~ msgstr "Impossible d'imprimer le document '%s'" + +#~ msgid "Show outline explorer" +#~ msgstr "Afficher l'explorateur de structure" + +#~ msgid "Co&mment" +#~ msgstr "Co&mmenter" + +#~ msgid "&Uncomment" +#~ msgstr "&Décommenter" + +#~ msgid "Uncomment current line or selection" +#~ msgstr "Décommenter la sélection ou la ligne en cours d'édition" + +#~ msgid "Please install matplotlib." +#~ msgstr "Merci d'installer matplotlib." + +#~ msgid "" +#~ "Remote editing for NumPy arrays, PIL images, lists, tuples and " +#~ "dictionaries" +#~ msgstr "" +#~ "Les tableaux NumPy, images PIL, listes, tuples et dictionnaires seront " +#~ "modifiés dans un éditeur exécuté dans le processus distant" + +#~ msgid "" +#~ "Editors are opened in the remote process for NumPy arrays, PIL images, " +#~ "lists, tuples and dictionaries" +#~ msgstr "" +#~ "Les tableaux NumPy, images PIL, listes, tuples et dictionnaires seront " +#~ "modifiés dans un éditeur exécuté dans le processus distant" + +#~ msgid "Open..." +#~ msgstr "Ouvrir..." + +#~ msgid "Save as..." +#~ msgstr "Enregistrer sous..." + +#~ msgid "Close" +#~ msgstr "Fermer" + +#~ msgid "Add block comment" +#~ msgstr "Ajouter un bloc de commentaires" + +#~ msgid "Remove block comment" +#~ msgstr "Supprimer un bloc de commentaires" + +#~ msgid "Save all" +#~ msgstr "Enregistrer tout" + +#~ msgid "Print..." +#~ msgstr "Imprimer..." + +#~ msgid "Re-run last script" +#~ msgstr "Exécuter de nouveau le dernier script" + +#~ msgid "Close file" +#~ msgstr "Fermer le fichier" + +#~ msgid "Show TODO/FIXME/XXX comments list" +#~ msgstr "Afficher la liste des commentaires du type TODO/FIXME/XXX" + +#~ msgid "Configure..." +#~ msgstr "Configurer..." + +#~ msgid "Previous file" +#~ msgstr "Fichier précédent" + +#~ msgid "Next file" +#~ msgstr "Fichier suivant" + +#~ msgid "Revert" +#~ msgstr "Réinitialiser" + +#~ msgid "Add &block comment around current line or selection" +#~ msgstr "" +#~ "Ajouter un &bloc de commentaires autour de la sélection ou de la ligne en " +#~ "cours d'édition" + +#~ msgid "Tasks (TODO, FIXME, XXX)" +#~ msgstr "Tâches (TODO, FIXME, XXX)" + +#~ msgid "" +#~ "IPython interpreter command line options:\n" +#~ "(Qt4 support: -q4thread)\n" +#~ "(Qt4 and matplotlib support: -q4thread -pylab)" +#~ msgstr "" +#~ "Options en ligne de commande de IPython :\n" +#~ "(support Qt4 : -q4thread)\n" +#~ "(support Qt4 et matplotlib : -q4thread -pylab)" + +#~ msgid "" +#~ "PyQt installs an input hook that processes events when an interactive " +#~ "interpreter is waiting for user input, thus allowing to interact with " +#~ "widgets without blocking the Python shell. Unfortunately, this is not " +#~ "working well on Windows platforms." +#~ msgstr "" +#~ "PyQt installe un mécanisme (\"input hook\") qui permet d'interagir avec " +#~ "des widgets dans un interpréteur Python sans bloquer ce dernier. " +#~ "Malheureusement, ce mécanisme ne fonctionne pas parfaitement sous Windows." + +#~ msgid "Replace text" +#~ msgstr "Remplacer" + +#~ msgid "Find next" +#~ msgstr "Rechercher le suivant" + +#~ msgid "Find previous" +#~ msgstr "Rechercher le précédent" + +#~ msgid "Edit filename filter..." +#~ msgstr "Modifier le filtre des types de fichier affichés" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/locale/ja/LC_MESSAGES/spyder.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/locale/ja/LC_MESSAGES/spyder.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder/locale/ja/LC_MESSAGES/spyder.po spyder-3.0.2+dfsg1/spyder/locale/ja/LC_MESSAGES/spyder.po --- spyder-2.3.8+dfsg1/spyder/locale/ja/LC_MESSAGES/spyder.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/locale/ja/LC_MESSAGES/spyder.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,5537 @@ +# Japanese translations for PACKAGE package +# PACKAGE パッケージに対する英訳. +# Copyright (C) 2016 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Ohnishi Seiki , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: Spyder3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2016-07-12 09:04+0900\n" +"Last-Translator: \n" +"Language-Team: Japanese \n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 1.6.10\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: spyder/app/mainwindow.py:125 +msgid "Initializing..." +msgstr "初期化中..." + +#: spyder/app/mainwindow.py:241 +msgid "Numpy and Scipy documentation" +msgstr "NumpyとScipyのドキュメント" + +#: spyder/app/mainwindow.py:243 spyder/app/mainwindow.py:1027 +msgid "Matplotlib documentation" +msgstr "Matplotlibドキュメント" + +#: spyder/app/mainwindow.py:246 +msgid "PyQt4 Reference Guide" +msgstr "PyQt4リファレンスガイド" + +#: spyder/app/mainwindow.py:249 +msgid "PyQt4 API Reference" +msgstr "PyQt4 APIリファレンス" + +#: spyder/app/mainwindow.py:251 +msgid "Python(x,y)" +msgstr "Python(x,y)" + +#: spyder/app/mainwindow.py:253 +msgid "WinPython" +msgstr "WinPython" + +#: spyder/app/mainwindow.py:525 +msgid "Close current pane" +msgstr "現在のペインを閉じる" + +#: spyder/app/mainwindow.py:530 +msgid "Lock panes" +msgstr "ペインをロック" + +#: spyder/app/mainwindow.py:537 +msgid "Use next layout" +msgstr "次のレイアウトを使用" + +#: spyder/app/mainwindow.py:541 +msgid "Use previous layout" +msgstr "前のレイアウトを使用" + +#: spyder/app/mainwindow.py:560 spyder/widgets/sourcecode/codeeditor.py:2505 +msgid "Undo" +msgstr "元に戻す" + +#: spyder/app/mainwindow.py:562 spyder/widgets/sourcecode/codeeditor.py:2508 +msgid "Redo" +msgstr "やり直す" + +#: spyder/app/mainwindow.py:564 spyder/widgets/shell.py:121 +#: spyder/widgets/sourcecode/codeeditor.py:2514 +#: spyder/widgets/variableexplorer/arrayeditor.py:453 +#: spyder/widgets/variableexplorer/collectionseditor.py:649 +#: spyder/widgets/variableexplorer/dataframeeditor.py:445 +msgid "Copy" +msgstr "コピー" + +#: spyder/app/mainwindow.py:566 spyder/widgets/shell.py:117 +#: spyder/widgets/sourcecode/codeeditor.py:2511 +msgid "Cut" +msgstr "切り取り" + +#: spyder/app/mainwindow.py:568 spyder/widgets/shell.py:125 +#: spyder/widgets/sourcecode/codeeditor.py:2517 +#: spyder/widgets/variableexplorer/collectionseditor.py:646 +msgid "Paste" +msgstr "貼り付け" + +#: spyder/app/mainwindow.py:571 spyder/widgets/shell.py:138 +#: spyder/widgets/sourcecode/codeeditor.py:2520 +msgid "Select All" +msgstr "全て選択" + +#: spyder/app/mainwindow.py:581 spyder/plugins/editor.py:1353 +msgid "&File" +msgstr "ファイル(&A)" + +#: spyder/app/mainwindow.py:582 spyder/plugins/editor.py:1345 +msgid "File toolbar" +msgstr "ファイルツールバー" + +#: spyder/app/mainwindow.py:586 spyder/plugins/editor.py:1354 +msgid "&Edit" +msgstr "編集(&E)" + +#: spyder/app/mainwindow.py:587 spyder/plugins/editor.py:1350 +msgid "Edit toolbar" +msgstr "編集ツールバー" + +#: spyder/app/mainwindow.py:591 spyder/plugins/editor.py:1355 +msgid "&Search" +msgstr "検索(&S)" + +#: spyder/app/mainwindow.py:592 spyder/plugins/editor.py:1346 +msgid "Search toolbar" +msgstr "検索ツールバー" + +#: spyder/app/mainwindow.py:596 spyder/plugins/editor.py:1356 +msgid "Sour&ce" +msgstr "ソース(&C)" + +#: spyder/app/mainwindow.py:597 spyder/plugins/editor.py:1347 +msgid "Source toolbar" +msgstr "ソースツールバー" + +#: spyder/app/mainwindow.py:601 spyder/plugins/editor.py:780 +#: spyder/plugins/editor.py:1357 +msgid "&Run" +msgstr "実行(&R)" + +#: spyder/app/mainwindow.py:602 spyder/plugins/editor.py:1348 +msgid "Run toolbar" +msgstr "実行ツールバー" + +#: spyder/app/mainwindow.py:606 spyder/plugins/editor.py:739 +msgid "&Debug" +msgstr "デバッグ(&D)" + +#: spyder/app/mainwindow.py:607 spyder/plugins/editor.py:1349 +msgid "Debug toolbar" +msgstr "デバッグツールバー" + +#: spyder/app/mainwindow.py:611 +msgid "C&onsoles" +msgstr "コンソール(&O)" + +#: spyder/app/mainwindow.py:614 +#, fuzzy +msgid "&Projects" +msgstr "プロジェクト..." + +#: spyder/app/mainwindow.py:617 spyder/plugins/editor.py:1358 +msgid "&Tools" +msgstr "ツール(&T)" + +#: spyder/app/mainwindow.py:620 +msgid "&View" +msgstr "表示(&V)" + +#: spyder/app/mainwindow.py:623 +msgid "&Help" +msgstr "ヘルプ(&H)" + +#: spyder/app/mainwindow.py:628 +msgid "Welcome to Spyder!" +msgstr "Spyderへようこそ!" + +#: spyder/app/mainwindow.py:633 +msgid "Pre&ferences" +msgstr "設定(&F)" + +#: spyder/app/mainwindow.py:640 spyder/widgets/pathmanager.py:49 +msgid "PYTHONPATH manager" +msgstr "PYTHONPATHマネージャ" + +#: spyder/app/mainwindow.py:643 +msgid "Python Path Manager" +msgstr "Pythonパスマネージャ" + +#: spyder/app/mainwindow.py:646 +msgid "Update module names list" +msgstr "モジュール名リストの更新" + +#: spyder/app/mainwindow.py:649 +msgid "Refresh list of module names available in PYTHONPATH" +msgstr "PYTHONPATH上で利用可能なモジュールリストの更新" + +#: spyder/app/mainwindow.py:652 +msgid "Reset Spyder to factory defaults" +msgstr "Spyderをデフォルト設定へ戻す" + +#: spyder/app/mainwindow.py:657 +msgid "Current user environment variables..." +msgstr "現在のユーザー環境変数..." + +#: spyder/app/mainwindow.py:659 +msgid "" +"Show and edit current user environment variables in Windows registry (i.e. " +"for all sessions)" +msgstr "レジストリ内のユーザー環境変数を表示・編集する" + +#: spyder/app/mainwindow.py:668 spyder/app/mainwindow.py:1123 +msgid "External Tools" +msgstr "外部ツール" + +#: spyder/app/mainwindow.py:672 +msgid "Python(x,y) launcher" +msgstr "Python(x,y)ランチャー" + +#: spyder/app/mainwindow.py:679 +msgid "WinPython control panel" +msgstr "WinPythonコントロールパネル" + +#: spyder/app/mainwindow.py:688 +msgid "Qt Designer" +msgstr "Qt Designer" + +#: spyder/app/mainwindow.py:693 +msgid "Qt Linguist" +msgstr "Qt Linguist" + +#: spyder/app/mainwindow.py:699 +msgid "Qt examples" +msgstr "Qt examples" + +#: spyder/app/mainwindow.py:720 +msgid "guidata examples" +msgstr "guidata examples" + +#: spyder/app/mainwindow.py:731 +msgid "guiqwt examples" +msgstr "guiqwt examples" + +#: spyder/app/mainwindow.py:736 +msgid "Sift" +msgstr "選別する" + +#: spyder/app/mainwindow.py:746 +msgid "ViTables" +msgstr "Viテーブル" + +#: spyder/app/mainwindow.py:760 +msgid "Fullscreen mode" +msgstr "フルスクリーンモード" + +#: spyder/app/mainwindow.py:772 +msgid "Main toolbar" +msgstr "メインツールバー" + +#: spyder/app/mainwindow.py:781 +msgid "" +"Spyder Internal Console\n" +"\n" +"This console is used to report application\n" +"internal errors and to inspect Spyder\n" +"internals with the following commands:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Please don't use it to run your code\n" +"\n" +msgstr "" +"Spyder 内部コンソール\n" +"\n" +"このコンソールは下記コマンド\n" +" spy.app, spy.window, dir(spy)\n" +"により、アプリケーション内部エラーの報告と\n" +"Spyder内部検査のために使われます。\n" +"\n" +"ユーザーのpythonコードを実行するために使わないでください。\n" +"\n" + +#: spyder/app/mainwindow.py:798 +msgid "Loading help..." +msgstr "ヘルプをロードしています..." + +#: spyder/app/mainwindow.py:805 +msgid "Loading outline explorer..." +msgstr "アウトラインエクスプローラーをロードしています..." + +#: spyder/app/mainwindow.py:813 +msgid "Loading editor..." +msgstr "エディタをロードしています..." + +#: spyder/app/mainwindow.py:819 spyder/plugins/console.py:134 +#: spyder/widgets/ipythonconsole/client.py:280 +msgid "&Quit" +msgstr "終了(&Q)" + +#: spyder/app/mainwindow.py:821 spyder/plugins/console.py:136 +msgid "Quit" +msgstr "終了" + +#: spyder/app/mainwindow.py:825 +msgid "&Restart" +msgstr "再起動(&R)" + +#: spyder/app/mainwindow.py:827 +msgid "Restart" +msgstr "再起動" + +#: spyder/app/mainwindow.py:844 +msgid "Loading file explorer..." +msgstr "ファイルエクスプローラーをロードしています..." + +#: spyder/app/mainwindow.py:851 +msgid "Loading history plugin..." +msgstr "ヒストリプラグインをロードしています..." + +#: spyder/app/mainwindow.py:862 +msgid "Loading online help..." +msgstr "オンラインヘルプをロードしています..." + +#: spyder/app/mainwindow.py:867 +msgid "Loading project explorer..." +msgstr "プロジェクトエクスプローラーをロード中..." + +#: spyder/app/mainwindow.py:874 +msgid "Loading external console..." +msgstr "外部コンソールをロード中..." + +#: spyder/app/mainwindow.py:880 +msgid "Loading namespace browser..." +msgstr "名前空間ブラウザをロード中..." + +#: spyder/app/mainwindow.py:887 +msgid "Loading IPython console..." +msgstr "IPythonコンソールをロード中..." + +#: spyder/app/mainwindow.py:892 +msgid "Setting up main window..." +msgstr "メインウインドウを設定中..." + +#: spyder/app/mainwindow.py:895 +msgid "Dependencies..." +msgstr "依存性..." + +#: spyder/app/mainwindow.py:899 +msgid "Report issue..." +msgstr "問題をレポート..." + +#: spyder/app/mainwindow.py:903 +msgid "Spyder support..." +msgstr "Spyderサポート..." + +#: spyder/app/mainwindow.py:906 +msgid "Check for updates..." +msgstr "更新をチェック..." + +#: spyder/app/mainwindow.py:929 +msgid "Spyder documentation" +msgstr "spyderドキュメント" + +#: spyder/app/mainwindow.py:934 +msgid "Spyder tutorial" +msgstr "Spyderチュートリアル" + +#: spyder/app/mainwindow.py:941 +msgid "Interactive tours" +msgstr "インタラクティブツアー" + +#: spyder/app/mainwindow.py:969 +msgid "Python documentation" +msgstr "Pythonドキュメント" + +#: spyder/app/mainwindow.py:975 spyder/app/mainwindow.py:1019 +msgid "IPython documentation" +msgstr "IPythonドキュメント" + +#: spyder/app/mainwindow.py:976 +msgid "Intro to IPython" +msgstr "IPythonの紹介" + +#: spyder/app/mainwindow.py:978 +msgid "Quick reference" +msgstr "クイックリファレンス" + +#: spyder/app/mainwindow.py:980 +msgid "Console help" +msgstr "コンソールヘルプ" + +#: spyder/app/mainwindow.py:1017 +msgid "Python(x,y) documentation folder" +msgstr "Python(x,y) ドキュメントフォルダ" + +#: spyder/app/mainwindow.py:1021 +msgid "guidata documentation" +msgstr "guidataドキュメント" + +#: spyder/app/mainwindow.py:1024 +msgid "guiqwt documentation" +msgstr "guiqwtドキュメント" + +#: spyder/app/mainwindow.py:1030 +msgid "NumPy documentation" +msgstr "NumPyドキュメント" + +#: spyder/app/mainwindow.py:1032 +msgid "NumPy reference guide" +msgstr "NumPyリファレンスガイド" + +#: spyder/app/mainwindow.py:1034 +msgid "NumPy user guide" +msgstr "NumPyユーザーガイド" + +#: spyder/app/mainwindow.py:1036 +msgid "SciPy documentation" +msgstr "SciPyドキュメント" + +#: spyder/app/mainwindow.py:1043 +msgid "Installed Python modules" +msgstr "インストールされたPythonモジュール" + +#: spyder/app/mainwindow.py:1047 +msgid "Online documentation" +msgstr "オンラインドキュメント" + +#: spyder/app/mainwindow.py:1059 +msgid "Qt documentation" +msgstr "Qtドキュメント" + +#: spyder/app/mainwindow.py:1065 +msgid "About %s..." +msgstr "%s について..." + +#: spyder/app/mainwindow.py:1089 +msgid "Panes" +msgstr "ペイン" + +#: spyder/app/mainwindow.py:1091 +msgid "Toolbars" +msgstr "ツールバー" + +#: spyder/app/mainwindow.py:1092 +msgid "Window layouts" +msgstr "ウインドウレイアウト" + +#: spyder/app/mainwindow.py:1101 spyder/app/mainwindow.py:1894 +#: spyder/app/mainwindow.py:1895 +msgid "Show toolbars" +msgstr "ツールバーを表示" + +#: spyder/app/mainwindow.py:1116 +msgid "Attached console window (debugging)" +msgstr "接続されたコンソールウインドウ(デバッグ)" + +#: spyder/app/mainwindow.py:1295 spyder/plugins/projects.py:244 +#: spyder/widgets/explorer.py:639 spyder/widgets/explorer.py:744 +#: spyder/widgets/externalshell/pythonshell.py:533 +#: spyder/widgets/externalshell/systemshell.py:105 +#: spyder/widgets/variableexplorer/arrayeditor.py:573 +#: spyder/widgets/variableexplorer/collectionseditor.py:417 +#: spyder/widgets/variableexplorer/dataframeeditor.py:593 +msgid "Error" +msgstr "エラー" + +#: spyder/app/mainwindow.py:1296 +msgid "" +"You have missing dependencies!

%s

Please " +"install them to avoid this message.

Note: Spyder could " +"work without some of these dependencies, however to have a smooth experience " +"when using Spyder we strongly recommend you to install all the listed " +"missing dependencies.

Failing to install these dependencies might " +"result in bugs. Please be sure that any found bugs are not the direct result " +"of missing dependencies, prior to reporting a new issue." +msgstr "" +"依存パッケージが見つかりません!

%s

このメッ" +"セージ表示を避けるにはインストールしてください。

: Spyder" +"はこれらの依存性を満たさなくても動作しますが円滑な動作のためには、これらリス" +"ト記載のパッケージをインストールし欠如を解消することが強く 推奨されま" +"す。
これらの依存パッケージのインストールされていない場合不具合が発生する" +"可能性があります。新しい問題を報告する前には、 発生した不具合が依存性欠如の直" +"接の結果ではないことを確認してください。" + +#: spyder/app/mainwindow.py:1741 +msgid "Spyder Default Layout" +msgstr "Spyderデフォルトレイアウト" + +#: spyder/app/mainwindow.py:1759 +msgid "Save current layout" +msgstr "現在のレイアウトを保存" + +#: spyder/app/mainwindow.py:1763 +msgid "Layout preferences" +msgstr "レイアウト設定" + +#: spyder/app/mainwindow.py:1767 +msgid "Reset to spyder default" +msgstr "Spyderのデフォルト値に戻す" + +#: spyder/app/mainwindow.py:1787 spyder/app/mainwindow.py:1809 +#: spyder/app/mainwindow.py:1872 spyder/app/mainwindow.py:2656 +#: spyder/plugins/configdialog.py:1254 spyder/plugins/externalconsole.py:446 +#: spyder/plugins/ipythonconsole.py:119 spyder/plugins/ipythonconsole.py:835 +#: spyder/plugins/maininterpreter.py:167 spyder/plugins/maininterpreter.py:195 +#: spyder/utils/environ.py:100 spyder/utils/environ.py:113 +#: spyder/widgets/variableexplorer/arrayeditor.py:496 +#: spyder/widgets/variableexplorer/collectionseditor.py:408 +#: spyder/widgets/variableexplorer/collectionseditor.py:1055 +msgid "Warning" +msgstr "警告" + +#: spyder/app/mainwindow.py:1788 +msgid "" +"Window layout will be reset to default settings: this affects window " +"position, size and dockwidgets.\n" +"Do you want to continue?" +msgstr "" +"ウインドウレイアウトはデフォルト状態にリセットされます:ウインドウ位置、サイ" +"ズ及びドックウィジェットが影響されます。\n" +"続行しますか?" + +#: spyder/app/mainwindow.py:1810 +msgid "" +"Layout %s will be " +"overwritten. Do you want to " +"continue?" +msgstr "" +"レイアウト %sは 上書き" +"されます。 続行しますか?" + +#: spyder/app/mainwindow.py:1873 +msgid "Quick switch layout #%s has not yet been defined." +msgstr "クイックスイッチレイアウト #%s は未定義です。" + +#: spyder/app/mainwindow.py:1891 spyder/app/mainwindow.py:1892 +msgid "Hide toolbars" +msgstr "ツールバーを隠す" + +#: spyder/app/mainwindow.py:2185 spyder/app/mainwindow.py:2186 +msgid "Maximize current pane" +msgstr "現在のペインを最大化" + +#: spyder/app/mainwindow.py:2189 +msgid "Restore current pane" +msgstr "現在のペインを復元" + +#: spyder/app/mainwindow.py:2190 +msgid "Restore pane to its original size" +msgstr "ペインをオリジナルサイズに復元" + +#: spyder/app/mainwindow.py:2274 +msgid "About %s" +msgstr "%s について" + +#: spyder/app/mainwindow.py:2401 spyder/plugins/editor.py:158 +#: spyder/plugins/runconfig.py:322 spyder/plugins/runconfig.py:444 +#: spyder/plugins/runconfig.py:449 spyder/utils/programs.py:285 +#: spyder/widgets/explorer.py:271 spyder/widgets/externalshell/baseshell.py:127 +msgid "Run" +msgstr "実行" + +#: spyder/app/mainwindow.py:2402 +msgid "Running an external system terminal is not supported on platform %s." +msgstr "" +"プラットフォーム %s では外部システムターミナルの実行はサポートされていませ" +"ん。" + +#: spyder/app/mainwindow.py:2657 +msgid "" +"Spyder will restart and reset to default settings:

Do you want to " +"continue?" +msgstr "Spyderを再起動して設定をデフォルトに戻します:

続行しますか?" + +#: spyder/app/mainwindow.py:2754 spyder/widgets/helperwidgets.py:250 +msgid "Spyder updates" +msgstr "Spyderアップデート" + +#: spyder/app/mainwindow.py:2755 spyder/plugins/configdialog.py:819 +msgid "Check for updates on startup" +msgstr "起動時に更新をチェック" + +#: spyder/app/mainwindow.py:2775 +msgid "" +"Spyder %s is available!

Please use your package manager to " +"update Spyder or go to our Releases page to download this " +"new version.

If you are not sure how to proceed to update Spyder " +"please refer to our Installation instructions." +msgstr "" +"Spyder %s is available!

Please use your package manager to " +"update Spyder or go to our Releases page to download this " +"new version.

If you are not sure how to proceed to update Spyder " +"please refer to our Installation instructions." + +#: spyder/app/mainwindow.py:2787 +msgid "Spyder is up to date." +msgstr "Spyderは最新の状態です。" + +#: spyder/app/restart.py:133 +msgid "" +"It was not possible to close the previous Spyder instance.\n" +"Restart aborted." +msgstr "" +"前回実行したSpyderを終了できませんでした。\n" +"再起動を終了します。" + +#: spyder/app/restart.py:135 +msgid "" +"Spyder could not reset to factory defaults.\n" +"Restart aborted." +msgstr "" +"デフォルト設定を適用できませんでした。\n" +"再起動を終了します。" + +#: spyder/app/restart.py:137 +msgid "" +"It was not possible to restart Spyder.\n" +"Operation aborted." +msgstr "" +"再起動できませんでした。\n" +"オペレーションを終了します。" + +#: spyder/app/restart.py:139 +msgid "Spyder exit error" +msgstr "Spyder実行エラー" + +#: spyder/app/restart.py:140 +msgid "Spyder reset error" +msgstr "Spyderリセットエラー" + +#: spyder/app/restart.py:141 +msgid "Spyder restart error" +msgstr "Spyder再起動エラー" + +#: spyder/app/restart.py:165 +msgid "Closing Spyder" +msgstr "Spyderを閉じる" + +#: spyder/app/restart.py:239 +msgid "Resetting Spyder to defaults" +msgstr "Spyderをデフォルト状態に戻しています" + +#: spyder/app/restart.py:271 +msgid "Restarting" +msgstr "再起動中。" + +#: spyder/app/tour.py:124 +msgid "Welcome to the Introduction tour" +msgstr "紹介ツアーへようこそ" + +#: spyder/app/tour.py:125 +msgid "" +"Spyder is a powerful Interactive Development Environment (or IDE) for " +"the Python programming language.

Here we are going to guide you " +"through its most important features.

Please use the arrow keys or " +"click on the buttons below to move along the tour." +msgstr "" +"Spyder はPythonプログラミングのためのパワフルな対話型開発環境" +"(Interactive Development Environment, IDE) です。

これはもっとも重要" +"な機能の紹介です。

ツアーにそって進むには矢印キーを押すか下のボタンを" +"クリックしてください。" + +#: spyder/app/tour.py:134 +msgid "The Editor" +msgstr "エディタ" + +#: spyder/app/tour.py:135 +msgid "" +"This is the pane where you write Python code before evaluating it. You can " +"get automatic suggestions and completions while writing, by pressing the " +"Tab key next to a given text.

The Editor comes with a line " +"number area (highlighted here in red), where Spyder shows warnings and " +"syntax errors. They can help you to detect potential problems before running " +"the code.

You can also set debug breakpoints in the line number area, " +"by doing a double click next to a non-empty line." +msgstr "" +"これは評価(evaluate)前にPythonコードを書くためのペインです。 入力したテキスト" +"の横でタブキーを押すことにより、自動サジェスト及び補完機能を使うことが" +"できます。

エディタには行番号エリア(ここでは赤で強調)があり、 警告や文" +"法エラーが表示されます。 これらはコード実行前に潜在的な問題を発見する助けとな" +"ります。

また行番号エリアの空行以外の場所でダブルクリックすることによ" +"り、デバッグのためのブレークポイントを設置することができます。" + +#: spyder/app/tour.py:150 +msgid "The IPython console" +msgstr "IPythonコンソール" + +#: spyder/app/tour.py:151 +msgid "" +"This is one of panes where you can run or execute the code you wrote on the " +"Editor. To do it you need to press the F5 key.

This console " +"comes with several useful features that greatly improve your programming " +"workflow (like syntax highlighting and inline plots). If you want to know " +"more about them, please follow this link.

Please " +"click on the button below to run some simple code in this console. This will " +"be useful to show you other important features." +msgstr "" +"これはエディタで入力したコードを実行できるペインの一つです。 コードを実行する" +"には F5 キーを押す必要があります。

このコンソールにはプログラミ" +"ングを改善するいくつかの有用な機能(構文の強調や行内プロット等)があります。こ" +"れらについてもっと知りたい場合はリンク先を参照してくださ" +"い。

このコンソール内の単純なコードを動かすために下のボタンをクリック" +"してください。 他の重要な機能を見るために役立ちます。" + +#: spyder/app/tour.py:167 +msgid "The Variable Explorer" +msgstr "変数エクスプローラー" + +#: spyder/app/tour.py:168 +msgid "" +"In this pane you can view and edit the variables generated during the " +"execution of a program, or those entered directly in one of Spyder consoles." +"

As you can see, the Variable Explorer is showing the variables " +"generated during the last step of this tour. By doing a double-click on any " +"of them, a new window will be opened, where you can inspect and modify their " +"contents." +msgstr "" +"このペインではプログラム実行中に生成されたり、Spyderコンソールで直接入力され" +"たりした変数の表示・編集ができます。

見ての通り変数エクスプローラーは" +"このツアー最後のステップで生成された変数を表示しています。表示された変数をダ" +"ブルクリックすると新しいウィンドウが開き、変数の中身を調査したり変更したりす" +"ることができます。" + +#: spyder/app/tour.py:180 +msgid "The Python console" +msgstr "Pythonコンソール" + +#: spyder/app/tour.py:181 +msgid "" +"You can also run your code on a Python console. These consoles are useful " +"because they let you run a file in a console dedicated only to it.To select " +"this behavior, please press the F6 key.

By pressing the button " +"below and then focusing the Variable Explorer, you will notice that Python " +"consoles are also connected to that pane, and that the Variable Explorer " +"only shows the variables of the currently focused console." +msgstr "" +"Pythonコンソールでもpythonコードを実行することができます。これらのコンソール" +"はファイルに書かれたコードを実行することに特化しており有用です。挙動を選択す" +"るためには F6 キーを押してください。

下のボタンを押し変数エクス" +"プローラーをフォーカスすると、Pythonコンソールもペインに連結されていることや" +"変数エクスプローラーは現在フォーカスされているコンソールの変数のみを表示する" +"ということがわかります。" + +#: spyder/app/tour.py:195 spyder/plugins/help.py:485 spyder/plugins/help.py:929 +#: spyder/widgets/internalshell.py:270 +msgid "Help" +msgstr "ヘルプ" + +#: spyder/app/tour.py:196 +msgid "" +"This pane displays documentation of the functions, classes, methods or " +"modules you are currently using in the Editor or the Consoles.

To use " +"it, you need to press Ctrl+I in front of an object. If that object " +"has some documentation associated with it, it will be displayed here." +msgstr "" +"このペインは現在エディタやコンソルーで使っている関数、クラス、メソッドあるい" +"はモジュールを表示します。

このペインを使うにはオブジェクトの前で " +"Ctrl+I を押す必要があります。もしオブジェクトに関連付けられたドキュメ" +"ントがある場合ここに表示されます。" + +#: spyder/app/tour.py:206 +msgid "The File Explorer" +msgstr "ファイルエクスプローラー" + +#: spyder/app/tour.py:207 +msgid "" +"This pane lets you navigate through the directories and files present in " +"your computer.

You can also open any of these files with its " +"corresponding application, by doing a double click on it.

There is " +"one exception to this rule: plain-text files will always be opened in the " +"Spyder Editor." +msgstr "" +"このペインはコンピュータ内のディレクトリとファイルをナビゲートします。" +"

ペイン上でダブルクリックすることにより、任意のファイルを対応したアプ" +"リケーションで開くことができます。

但しプレーンテキストは常にSpyder " +"Editorで開かれるという例外はあります。" + +#: spyder/app/tour.py:217 +msgid "The History Log" +msgstr "履歴ログ" + +#: spyder/app/tour.py:218 +msgid "" +"This pane records all commands introduced in the Python and IPython consoles." +msgstr "" +"このペインはPython及びIPythonコンソール内で入力された全てののコマンドを記録し" +"ます。" + +#: spyder/app/tour.py:266 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" +"Spyder はなにか(bla)に基づいた対話型開発環境(Interactive development " +"environment)です。" + +#: spyder/app/tour.py:270 +msgid "Welcome to Spyder introduction tour" +msgstr "Spyder紹介ツアーへようこそ" + +#: spyder/app/tour.py:271 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" +"Spyderはなにか(bla)に基づいた対話型開発環境(Interactive development " +"environment)です。" + +#: spyder/app/tour.py:276 +msgid "Introduction tour" +msgstr "紹介ツアー" + +#: spyder/app/tour.py:277 +msgid "New features in version 3.0" +msgstr "バージョン3.0の新機能" + +#: spyder/app/tour.py:555 spyder/plugins/ipythonconsole.py:431 +msgid "Run code" +msgstr "コードを実行" + +#: spyder/app/tour.py:824 +msgid "Go to step: " +msgstr "ステップへ移動" + +#: spyder/config/base.py:249 +msgid "" +"Update LANGUAGE_CODES (inside config/base.py) if a new translation has been " +"added to Spyder" +msgstr "" +"新しい翻訳がSpyderに追加された場合LANGUAGE_CODES (config/base.py内)を更新" + +#: spyder/config/ipython.py:23 +#, fuzzy +msgid "Integrate the IPython console" +msgstr "IPythonコンソール" + +#: spyder/config/ipython.py:25 +msgid "Manipulate Jupyter notebooks on the Editor" +msgstr "エディタ上でJupyter notebookを操作" + +#: spyder/config/utils.py:24 +msgid "Python files" +msgstr "Pythonファイル" + +#: spyder/config/utils.py:25 +msgid "Cython/Pyrex files" +msgstr "Cython/Pyrexファイル" + +#: spyder/config/utils.py:26 +msgid "C files" +msgstr "Cファイル" + +#: spyder/config/utils.py:27 +msgid "C++ files" +msgstr "C++ファイル" + +#: spyder/config/utils.py:28 +msgid "OpenCL files" +msgstr "OpenCLファイル" + +#: spyder/config/utils.py:29 +msgid "Fortran files" +msgstr "Fortranファイル" + +#: spyder/config/utils.py:30 +msgid "IDL files" +msgstr "IDLファイル" + +#: spyder/config/utils.py:31 +msgid "MATLAB files" +msgstr "MATLABファイル" + +#: spyder/config/utils.py:32 +msgid "Julia files" +msgstr "Juliaファイル" + +#: spyder/config/utils.py:33 +msgid "Yaml files" +msgstr "Yamlファイル" + +#: spyder/config/utils.py:34 +msgid "Patch and diff files" +msgstr "Patch and diffファイル" + +#: spyder/config/utils.py:35 +msgid "Batch files" +msgstr "Batchファイル" + +#: spyder/config/utils.py:36 spyder/utils/iofuncs.py:426 +msgid "Text files" +msgstr "テキストファイル" + +#: spyder/config/utils.py:37 +msgid "reStructuredText files" +msgstr "reStructuredTextファイル" + +#: spyder/config/utils.py:38 +msgid "gettext files" +msgstr "gettextファイル" + +#: spyder/config/utils.py:39 +msgid "NSIS files" +msgstr "NSISファイル" + +#: spyder/config/utils.py:40 +msgid "Web page files" +msgstr "Webページファイル" + +#: spyder/config/utils.py:41 +msgid "XML files" +msgstr "XMLファイル" + +#: spyder/config/utils.py:42 +msgid "Javascript files" +msgstr "Javascriptファイル" + +#: spyder/config/utils.py:43 +msgid "Json files" +msgstr "Jsonファイル" + +#: spyder/config/utils.py:44 +msgid "IPython notebooks" +msgstr "IPython notebooks" + +#: spyder/config/utils.py:45 +msgid "Enaml files" +msgstr "Enamlファイル" + +#: spyder/config/utils.py:46 +msgid "Configuration files" +msgstr "Configurationファイル" + +#: spyder/config/utils.py:51 spyder/widgets/explorer.py:712 +msgid "All files" +msgstr "すべてのファイル" + +#: spyder/config/utils.py:121 +msgid "Supported text files" +msgstr "サポートされたテキストファイル" + +#: spyder/plugins/__init__.py:508 spyder/plugins/editor.py:98 +#: spyder/plugins/editor.py:545 spyder/plugins/editor.py:1729 +#: spyder/plugins/help.py:118 spyder/plugins/help.py:385 +#: spyder/widgets/editor.py:371 spyder/widgets/sourcecode/codeeditor.py:97 +#: spyder/widgets/sourcecode/codeeditor.py:3001 +msgid "Editor" +msgstr "エディタ" + +#: spyder/plugins/configdialog.py:139 +msgid "Reset to defaults" +msgstr "デフォルト設定へ戻す" + +#: spyder/plugins/configdialog.py:151 +msgid "Preferences" +msgstr "設定" + +#: spyder/plugins/configdialog.py:491 +msgid "Invalid directory path" +msgstr "不正なディレクトリパス" + +#: spyder/plugins/configdialog.py:494 spyder/plugins/configdialog.py:509 +#: spyder/plugins/runconfig.py:177 spyder/plugins/runconfig.py:241 +#: spyder/plugins/workingdirectory.py:291 spyder/widgets/explorer.py:626 +#: spyder/widgets/externalshell/pythonshell.py:616 +#: spyder/widgets/findinfiles.py:504 spyder/widgets/pathmanager.py:224 +#: spyder/widgets/projects/projectdialog.py:155 +msgid "Select directory" +msgstr "ディレクトリを選択" + +#: spyder/plugins/configdialog.py:521 +msgid "Invalid file path" +msgstr "不正なファイルパス" + +#: spyder/plugins/configdialog.py:524 spyder/plugins/configdialog.py:541 +msgid "Select file" +msgstr "ファイル選択" + +#: spyder/plugins/configdialog.py:540 +msgid "All files (*)" +msgstr "全てのファイル (*)" + +#: spyder/plugins/configdialog.py:613 spyder/widgets/formlayout.py:215 +msgid "Bold" +msgstr "ボールド" + +#: spyder/plugins/configdialog.py:616 spyder/widgets/formlayout.py:210 +msgid "Italic" +msgstr "イタリック" + +#: spyder/plugins/configdialog.py:670 +msgid "Font: " +msgstr "フォント:" + +#: spyder/plugins/configdialog.py:676 +msgid "Size: " +msgstr "サイズ:" + +#: spyder/plugins/configdialog.py:695 +msgid "Font style" +msgstr "フォントスタイル" + +#: spyder/plugins/configdialog.py:772 +msgid "Spyder needs to restart to change the following setting:" +msgstr "Spyderは以下の設定を適用するために再起動が必要となります:" + +#: spyder/plugins/configdialog.py:775 +msgid "Spyder needs to restart to change the following settings:" +msgstr "Spyderは以下の設定を適用するために再起動が必要となります:" + +#: spyder/plugins/configdialog.py:777 +msgid "Do you wish to restart now?" +msgstr "直ちに再起動しますか?" + +#: spyder/plugins/configdialog.py:783 +msgid "Information" +msgstr "情報" + +#: spyder/plugins/configdialog.py:797 spyder/plugins/configdialog.py:804 +#: spyder/widgets/projects/configdialog.py:74 +msgid "General" +msgstr "一般" + +#: spyder/plugins/configdialog.py:807 +msgid "Language" +msgstr "言語" + +#: spyder/plugins/configdialog.py:810 +msgid "Use a single instance" +msgstr "単一インスタンスを使用" + +#: spyder/plugins/configdialog.py:812 +msgid "" +"Set this to open external
Python files in an already running instance " +"(Requires a restart)" +msgstr "" +"外部Pythonファイルを実行中の
インスタンスで開くためにこれをセット (再起動" +"が必要)" + +#: spyder/plugins/configdialog.py:815 +msgid "Prompt when exiting" +msgstr "終了時に確認する" + +#: spyder/plugins/configdialog.py:816 +msgid "Pop up internal console when internal errors appear" +msgstr "内部エラー発生時に内部コンソールをポップアップさせる" + +#: spyder/plugins/configdialog.py:836 spyder/plugins/editor.py:107 +#: spyder/plugins/externalconsole.py:57 spyder/plugins/ipythonconsole.py:273 +#: spyder/widgets/projects/configdialog.py:81 +msgid "Interface" +msgstr "インターフェイス" + +#: spyder/plugins/configdialog.py:844 +msgid "Qt windows style" +msgstr "Qt windowスタイル" + +#: spyder/plugins/configdialog.py:850 +msgid "Icon theme" +msgstr "アイコンテーマ" + +#: spyder/plugins/configdialog.py:854 +msgid "Vertical title bars in panes" +msgstr "ペインのタイトルバーを縦にする" + +#: spyder/plugins/configdialog.py:856 +msgid "Vertical tabs in panes" +msgstr "ペインのタブを縦にする" + +#: spyder/plugins/configdialog.py:858 +msgid "Animated toolbars and panes" +msgstr "ツールバーとペインをアニメーションさせる" + +#: spyder/plugins/configdialog.py:860 +msgid "Tear off menus" +msgstr "メニューを切り離す" + +#: spyder/plugins/configdialog.py:861 +msgid "Set this to detach any
menu from the main window" +msgstr "メインウインドウから
メニューを切り離すために設定する" + +#: spyder/plugins/configdialog.py:863 +msgid "Enable high DPI scaling" +msgstr "" + +#: spyder/plugins/configdialog.py:865 +msgid "Set this for high DPI displays" +msgstr "" + +#: spyder/plugins/configdialog.py:866 +msgid "Custom margin for panes:" +msgstr "ペインのマージン調整" + +#: spyder/plugins/configdialog.py:868 spyder/plugins/editor.py:209 +msgid "pixels" +msgstr "ピクセル" + +#: spyder/plugins/configdialog.py:897 +msgid "Status bar" +msgstr "ステータスバー" + +#: spyder/plugins/configdialog.py:898 +msgid "Show status bar" +msgstr "ステータスバーを表示" + +#: spyder/plugins/configdialog.py:900 +msgid "Show memory usage every" +msgstr "メモリ使用状況を以下の間隔で更新" + +#: spyder/plugins/configdialog.py:902 spyder/plugins/configdialog.py:911 +#: spyder/plugins/editor.py:133 spyder/plugins/editor.py:261 +#: spyder/plugins/variableexplorer.py:30 +msgid " ms" +msgstr "ms" + +#: spyder/plugins/configdialog.py:909 +msgid "Show CPU usage every" +msgstr "CPU使用状況を以下の間隔で更新" + +#: spyder/plugins/configdialog.py:944 +msgid "Plain text font" +msgstr "プレーンテキスト" + +#: spyder/plugins/configdialog.py:950 +msgid "Rich text font" +msgstr "リッチテキスト" + +#: spyder/plugins/configdialog.py:953 +msgid "Fonts" +msgstr "フォント" + +#: spyder/plugins/configdialog.py:967 +msgid "Appearance" +msgstr "外見" + +#: spyder/plugins/configdialog.py:969 spyder/plugins/ipythonconsole.py:564 +msgid "Advanced Settings" +msgstr "詳細設定" + +#: spyder/plugins/configdialog.py:1005 +msgid "Syntax coloring" +msgstr "構文強調の配色" + +#: spyder/plugins/configdialog.py:1018 +msgid "" +"Here you can select the color scheme used in the Editor and all other Spyder " +"plugins.

You can also edit the color schemes provided by Spyder or " +"create your own ones by using the options provided below.
" +msgstr "" +"エディタ及びその他全Spyderプラグインで利用する色スキームを選択できます。" +"

以下のオプションにより定義済みの色スキームの編集及び独自色スキーム作" +"成が実行できます。
" + +#: spyder/plugins/configdialog.py:1023 +msgid "Edit selected" +msgstr "選択を編集" + +#: spyder/plugins/configdialog.py:1024 +msgid "Create new scheme" +msgstr "新しいスキームを作成" + +#: spyder/plugins/configdialog.py:1025 spyder/widgets/explorer.py:512 +#: spyder/widgets/projects/explorer.py:243 spyder/widgets/shell.py:134 +msgid "Delete" +msgstr "削除" + +#: spyder/plugins/configdialog.py:1028 +msgid "Reset" +msgstr "リセット" + +#: spyder/plugins/configdialog.py:1035 +msgid "Scheme:" +msgstr "スキーム:" + +#: spyder/plugins/configdialog.py:1066 +msgid "Manage color schemes" +msgstr "色スキームのマネージ" + +#: spyder/plugins/configdialog.py:1255 +msgid "Are you sure you want to delete this scheme?" +msgstr "本当にこのスキームを削除しますか?" + +#: spyder/plugins/configdialog.py:1372 +msgid "Text" +msgstr "テキスト" + +#: spyder/plugins/configdialog.py:1374 +msgid "Highlight" +msgstr "強調" + +#: spyder/plugins/configdialog.py:1376 +msgid "Background" +msgstr "背景" + +#: spyder/plugins/configdialog.py:1380 +msgid "Scheme name:" +msgstr "スキーム名:" + +#: spyder/plugins/configdialog.py:1387 +msgid "Color scheme editor" +msgstr "色スキームエディタ" + +#: spyder/plugins/console.py:109 +msgid "Internal console" +msgstr "内部コンソール" + +#: spyder/plugins/console.py:139 spyder/plugins/externalconsole.py:743 +msgid "&Run..." +msgstr "実行(&R)" + +#: spyder/plugins/console.py:141 spyder/plugins/externalconsole.py:744 +msgid "Run a Python script" +msgstr "Pythonスクリプトを実行" + +#: spyder/plugins/console.py:144 +msgid "Environment variables..." +msgstr "環境変数.." + +#: spyder/plugins/console.py:146 +msgid "Show and edit environment variables (for current session)" +msgstr "(現在のセッションのため)環境変数を表示・編集する" + +#: spyder/plugins/console.py:150 +msgid "Show sys.path contents..." +msgstr "sys.pathの中身を表示..." + +#: spyder/plugins/console.py:152 +msgid "Show (read-only) sys.path" +msgstr "sys.pathを読み取り専用で表示" + +#: spyder/plugins/console.py:155 +msgid "Buffer..." +msgstr "バッファー..." + +#: spyder/plugins/console.py:156 spyder/plugins/externalconsole.py:75 +#: spyder/plugins/history.py:43 +msgid "Set maximum line count" +msgstr "最大行数を設定" + +#: spyder/plugins/console.py:159 +msgid "External editor path..." +msgstr "外部エディタのパス..." + +#: spyder/plugins/console.py:160 +msgid "Set external editor executable path" +msgstr "外部エディタ実行パスを設定" + +#: spyder/plugins/console.py:163 spyder/plugins/editor.py:139 +#: spyder/plugins/externalconsole.py:76 spyder/plugins/help.py:157 +#: spyder/plugins/help.py:360 spyder/plugins/history.py:46 +#: spyder/plugins/history.py:157 +msgid "Wrap lines" +msgstr "行の折り返し" + +#: spyder/plugins/console.py:166 spyder/plugins/editor.py:175 +#: spyder/plugins/externalconsole.py:121 spyder/plugins/ipythonconsole.py:283 +msgid "Display balloon tips" +msgstr "バルーンチップを表示" + +#: spyder/plugins/console.py:170 spyder/plugins/editor.py:169 +#: spyder/plugins/externalconsole.py:115 +msgid "Automatic code completion" +msgstr "自動コード補完" + +#: spyder/plugins/console.py:174 spyder/plugins/editor.py:173 +#: spyder/plugins/externalconsole.py:119 +msgid "Enter key selects completion" +msgstr "Enterキーで補完候補を選択" + +#: spyder/plugins/console.py:179 +msgid "Internal console settings" +msgstr "内部コンソール設定" + +#: spyder/plugins/console.py:232 spyder/plugins/externalconsole.py:918 +msgid "Run Python script" +msgstr "Pythonスクリプトを実行" + +#: spyder/plugins/console.py:233 spyder/plugins/externalconsole.py:146 +#: spyder/plugins/externalconsole.py:919 spyder/widgets/explorer.py:727 +msgid "Python scripts" +msgstr "Pythonスクリプト" + +#: spyder/plugins/console.py:278 +msgid "Buffer" +msgstr "バッファー" + +#: spyder/plugins/console.py:279 +msgid "Maximum line count" +msgstr "最大行数" + +#: spyder/plugins/console.py:289 +msgid "External editor" +msgstr "外部エディタ" + +#: spyder/plugins/console.py:290 +msgid "External editor executable path:" +msgstr "外部エディタ実行パス:" + +#: spyder/plugins/editor.py:104 +msgid "Edit template for new modules" +msgstr "新規モジュール用テンプレートの編集" + +#: spyder/plugins/editor.py:109 +msgid "Sort files according to full path" +msgstr "フルパスでファイルをソート" + +#: spyder/plugins/editor.py:111 +msgid "Show tab bar" +msgstr "タブバーを表示" + +#: spyder/plugins/editor.py:118 spyder/plugins/editor.py:189 +#: spyder/plugins/externalconsole.py:71 spyder/plugins/externalconsole.py:114 +#: spyder/plugins/help.py:156 spyder/plugins/history.py:45 +#: spyder/plugins/ipythonconsole.py:317 +msgid "Source code" +msgstr "ソースコード" + +#: spyder/plugins/editor.py:119 +msgid "Show line numbers" +msgstr "行番号を表示" + +#: spyder/plugins/editor.py:120 spyder/plugins/editor.py:947 +msgid "Show blank spaces" +msgstr "空白スペースを表示" + +#: spyder/plugins/editor.py:121 +msgid "Show vertical line after" +msgstr "縦線を以下の後に表示" + +#: spyder/plugins/editor.py:122 +msgid "characters" +msgstr "文字" + +#: spyder/plugins/editor.py:127 +msgid "Highlight current line" +msgstr "現在行を強調" + +#: spyder/plugins/editor.py:129 +msgid "Highlight current cell" +msgstr "現在セルを強調" + +#: spyder/plugins/editor.py:131 +msgid "Highlight occurrences after" +msgstr "以下の事象を強調" + +#: spyder/plugins/editor.py:159 +msgid "Save all files before running script" +msgstr "スクリプトを実行前に保存" + +#: spyder/plugins/editor.py:162 +msgid "Run selection" +msgstr "選択を実行" + +#: spyder/plugins/editor.py:163 +msgid "Maintain focus in the Editor after running cells or selections" +msgstr "エディタ内で実行後、選択後もセルのフォーカスを維持する" + +#: spyder/plugins/editor.py:166 spyder/plugins/externalconsole.py:252 +msgid "Introspection" +msgstr "イントロスペクション" + +#: spyder/plugins/editor.py:171 spyder/plugins/externalconsole.py:117 +msgid "Case sensitive code completion" +msgstr "コード補完で大文字/小文字を区別" + +#: spyder/plugins/editor.py:176 +msgid "Link to object definition" +msgstr "オブジェクトの定義にリンク" + +#: spyder/plugins/editor.py:178 +msgid "" +"If this option is enabled, clicking on an object\n" +"name (left-click + Ctrl key) will go this object\n" +"definition (if resolved)." +msgstr "" +"このオプションを有効にした場合、オブジェクト名の上で\n" +"クリック(left-click + Ctrl key) するとオブジェクトの定義へ\n" +"(定義位置が解決できていれば)移動する。" + +#: spyder/plugins/editor.py:182 +msgid "" +"Warning:
The Python module rope is not installed on this " +"computer: calltips, code completion and go-to-definition features won't be " +"available." +msgstr "" +"警告:
パイソンモジュール rope がこのコンピューターにインス" +"トールされていません: 呼び出し情報の表示、コード補完及びgo-to-definition機能" +"は利用できません。" + +#: spyder/plugins/editor.py:190 +msgid "Automatic insertion of parentheses, braces and brackets" +msgstr "括弧、中括弧、大かっこを自動的に挿入" + +#: spyder/plugins/editor.py:193 +msgid "Automatic insertion of closing quotes" +msgstr "引用符を自動的に閉じる" + +#: spyder/plugins/editor.py:195 +msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" +msgstr "'for', 'if', 'def'等の後にコロンを自動挿入する" + +#: spyder/plugins/editor.py:198 +msgid "Automatic indentation after 'else', 'elif', etc." +msgstr "'else'や'elif'の後を自動的にインデントする" + +#: spyder/plugins/editor.py:200 +msgid "Indentation characters: " +msgstr "インデント文字:" + +#: spyder/plugins/editor.py:201 +msgid "2 spaces" +msgstr "空白2個" + +#: spyder/plugins/editor.py:202 +msgid "3 spaces" +msgstr "空白3個" + +#: spyder/plugins/editor.py:203 +msgid "4 spaces" +msgstr "空白4個" + +#: spyder/plugins/editor.py:204 +msgid "5 spaces" +msgstr "空白5個" + +#: spyder/plugins/editor.py:205 +msgid "6 spaces" +msgstr "空白6個" + +#: spyder/plugins/editor.py:206 +msgid "7 spaces" +msgstr "空白7個" + +#: spyder/plugins/editor.py:207 +msgid "8 spaces" +msgstr "空白8個" + +#: spyder/plugins/editor.py:208 +msgid "Tabulations" +msgstr "一覧表" + +#: spyder/plugins/editor.py:209 +msgid "Tab stop width:" +msgstr "タブ幅:" + +#: spyder/plugins/editor.py:211 +msgid "Tab always indent" +msgstr "常にインデントにタブを使う" + +#: spyder/plugins/editor.py:213 +msgid "" +"If enabled, pressing Tab will always indent,\n" +"even when the cursor is not at the beginning\n" +"of a line (when this option is enabled, code\n" +"completion may be triggered using the alternate\n" +"shortcut: Ctrl+Space)" +msgstr "" +"これを有効にすると、タブキーを押すとカーソルが\n" +"行頭になくても常にインデントされる。\n" +"(このオプションを有効化した場合、コード補完\n" +"のトリガーは別のショートカット\n" +"Ctrl+Spaceに変更される)" + +#: spyder/plugins/editor.py:218 +msgid "Intelligent backspace" +msgstr "インテリジェントなバックスペース" + +#: spyder/plugins/editor.py:220 +msgid "Automatically remove trailing spaces when saving files" +msgstr "ファイル保存時に後続スペースを削除する" + +#: spyder/plugins/editor.py:224 +msgid "Analysis" +msgstr "分析" + +#: spyder/plugins/editor.py:226 +msgid "(Refer to the {} page)" +msgstr "( {} ページ参照)" + +#: spyder/plugins/editor.py:230 +msgid "Real-time code analysis" +msgstr "リアルタイムコード分析" + +#: spyder/plugins/editor.py:232 +msgid "" +"

If enabled, Python source code will be analyzed using pyflakes, lines " +"containing errors or warnings will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore code analysis warnings.

" +msgstr "" +"

有効にした場合Pythonソースコードはpyflakesで分析され、エラーや警告のある行" +"は強調表示されます。

: analysis:ignore をコメントで追加" +"すると警告は無視されます。

" + +#: spyder/plugins/editor.py:240 +msgid "Code analysis requires pyflakes %s+" +msgstr "コード分析にはpyflakes %s+ が必要です" + +#: spyder/plugins/editor.py:242 +msgid "Real-time code style analysis" +msgstr "リアルタイムコードスタイル分析" + +#: spyder/plugins/editor.py:244 +msgid "" +"

If enabled, Python source code will be analyzedusing pep8, lines that are " +"not following PEP8 style guide will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore style analysis warnings.

" +msgstr "" +"

有効にした場合、Pythonソースコードはpep8を使って分析されます。PEP8スタイル" +"ガイドに適合していない行は強調表示されます。

: 警告を抑制する" +"には、analysis:ignore をコメント内に追加してください。

" + +#: spyder/plugins/editor.py:251 +msgid "Code annotations (TODO, FIXME, XXX, HINT, TIP, @todo)" +msgstr "コード注釈(TODO, FIXME, XXX, HINT, TIP, @todo)" + +#: spyder/plugins/editor.py:254 +msgid "Perform analysis when saving file and every" +msgstr "コード分析をファイル保存時と以下の間隔で実施" + +#: spyder/plugins/editor.py:258 +msgid "Perform analysis only when saving file" +msgstr "コード分析をファイル保存時のみ実施" + +#: spyder/plugins/editor.py:317 +msgid "End-of-line characters" +msgstr "改行コード" + +#: spyder/plugins/editor.py:318 +msgid "" +"When opening a text file containing mixed end-of-line characters (this may " +"raise syntax errors in the consoles on Windows platforms), Spyder may fix " +"the file automatically." +msgstr "" +"複数種の改行コードを含むテキストファイル(Windows環境ではコンソールで文法エ" +"ラーを起こす可能性があります)を開く場合、Spyderは自動的にこれを修正します。" + +#: spyder/plugins/editor.py:324 +msgid "Fix automatically and show warning message box" +msgstr "自動的に修正し、警告をメッセージボックスで表示する" + +#: spyder/plugins/editor.py:335 spyder/plugins/externalconsole.py:250 +#: spyder/plugins/ipythonconsole.py:558 spyder/plugins/variableexplorer.py:43 +msgid "Display" +msgstr "表示" + +#: spyder/plugins/editor.py:337 +msgid "Code Introspection/Analysis" +msgstr "コード イントロスペクション/分析" + +#: spyder/plugins/editor.py:340 spyder/plugins/externalconsole.py:253 +msgid "Advanced settings" +msgstr "詳細設定" + +#: spyder/plugins/editor.py:613 spyder/widgets/editortools.py:510 +msgid "Show/hide outline explorer" +msgstr "アウトラインエクスプローラーの表示/隠す" + +#: spyder/plugins/editor.py:619 +msgid "Show/hide project explorer" +msgstr "プロジェクトエクスプローラーの表示/隠す" + +#: spyder/plugins/editor.py:627 +msgid "&New file..." +msgstr "新規ファイル(&N)..." + +#: spyder/plugins/editor.py:628 spyder/plugins/workingdirectory.py:83 +#: spyder/widgets/explorer.py:704 spyder/widgets/explorer.py:711 +msgid "New file" +msgstr "新規ファイル" + +#: spyder/plugins/editor.py:634 +msgid "&Open..." +msgstr "開く(&O)..." + +#: spyder/plugins/editor.py:635 spyder/plugins/editor.py:1778 +#: spyder/plugins/workingdirectory.py:70 +msgid "Open file" +msgstr "ファイルを開く" + +#: spyder/plugins/editor.py:641 spyder/widgets/editor.py:347 +msgid "File switcher..." +msgstr "ファイル切り替え..." + +#: spyder/plugins/editor.py:643 +msgid "Fast switch between files" +msgstr "フィルを高速で切り替え" + +#: spyder/plugins/editor.py:649 +msgid "&Revert" +msgstr "復帰(&R)" + +#: spyder/plugins/editor.py:650 +msgid "Revert file from disk" +msgstr "ディスクからファイルを復帰" + +#: spyder/plugins/editor.py:653 +msgid "&Save" +msgstr "保存(&S)" + +#: spyder/plugins/editor.py:654 spyder/widgets/editor.py:1306 +msgid "Save file" +msgstr "ファイルを保存" + +#: spyder/plugins/editor.py:660 +msgid "Sav&e all" +msgstr "全て保存(&E)" + +#: spyder/plugins/editor.py:661 +msgid "Save all files" +msgstr "すべてのファイルを保存" + +#: spyder/plugins/editor.py:667 +msgid "Save &as..." +msgstr "形式を指定して保存(&A)" + +#: spyder/plugins/editor.py:668 +msgid "Save current file as..." +msgstr "現在のファイルを形式を指定して保存..." + +#: spyder/plugins/editor.py:673 spyder/plugins/editor.py:674 +msgid "Print preview..." +msgstr "プレビューを表示..." + +#: spyder/plugins/editor.py:675 +msgid "&Print..." +msgstr "印刷(&P)" + +#: spyder/plugins/editor.py:676 +msgid "Print current file..." +msgstr "現在のファイルを印刷..." + +#: spyder/plugins/editor.py:679 +msgid "&Close" +msgstr "閉じる(&C)" + +#: spyder/plugins/editor.py:680 +msgid "Close current file" +msgstr "現在のファイルを閉じる" + +#: spyder/plugins/editor.py:683 +msgid "C&lose all" +msgstr "すべて閉じる(&C)" + +#: spyder/plugins/editor.py:684 +msgid "Close all opened files" +msgstr "開いているファイルを全て閉じる" + +#: spyder/plugins/editor.py:691 +msgid "&Find text" +msgstr "テキストを検索(&F)" + +#: spyder/plugins/editor.py:697 +msgid "Find &next" +msgstr "次を検索(&N)" + +#: spyder/plugins/editor.py:703 +msgid "Find &previous" +msgstr "前を検索(&F)" + +#: spyder/plugins/editor.py:709 +msgid "&Replace text" +msgstr "テキストを置換(&R)" + +#: spyder/plugins/editor.py:718 +msgid "Set/Clear breakpoint" +msgstr "ブレークポイントのセット/クリア" + +#: spyder/plugins/editor.py:725 +msgid "Set/Edit conditional breakpoint" +msgstr "条件付きブレークポイントをセット/編集" + +#: spyder/plugins/editor.py:732 +msgid "Clear breakpoints in all files" +msgstr "全てのファイルのブレークポイントをクリア" + +#: spyder/plugins/editor.py:734 +msgid "Debug with winpdb" +msgstr "winpdbでデバッグ" + +#: spyder/plugins/editor.py:741 +msgid "Debug file" +msgstr "ファイルをデバッグ開始" + +#: spyder/plugins/editor.py:746 +msgid "Step" +msgstr "Step" + +#: spyder/plugins/editor.py:747 +msgid "Run current line" +msgstr "現在行を実行" + +#: spyder/plugins/editor.py:752 +msgid "Continue" +msgstr "Continue" + +#: spyder/plugins/editor.py:754 +msgid "Continue execution until next breakpoint" +msgstr "次のブレークポイントまで実行を続ける" + +#: spyder/plugins/editor.py:759 +msgid "Step Into" +msgstr "Step Into" + +#: spyder/plugins/editor.py:761 +msgid "Step into function or method of current line" +msgstr "現在行のメソッド/関数までStep inする" + +#: spyder/plugins/editor.py:766 +msgid "Step Return" +msgstr "Step Return" + +#: spyder/plugins/editor.py:768 +msgid "Run until current function or method returns" +msgstr "現在行のメソッド/関数までRunする" + +#: spyder/plugins/editor.py:773 spyder/widgets/findinfiles.py:333 +#: spyder/widgets/ipythonconsole/client.py:238 +msgid "Stop" +msgstr "停止" + +#: spyder/plugins/editor.py:774 +msgid "Stop debugging" +msgstr "" + +#: spyder/plugins/editor.py:781 +msgid "Run file" +msgstr "ファイルを実行" + +#: spyder/plugins/editor.py:786 +msgid "&Configure..." +msgstr "設定(&C)..." + +#: spyder/plugins/editor.py:788 spyder/widgets/externalshell/pythonshell.py:304 +msgid "Run settings" +msgstr "設定の実行" + +#: spyder/plugins/editor.py:794 +msgid "Re-run &last script" +msgstr "最後のスクリプトを再度実行する(&L)" + +#: spyder/plugins/editor.py:796 +msgid "Run again last file" +msgstr "最後のファイルを再度実行する" + +#: spyder/plugins/editor.py:802 spyder/widgets/sourcecode/codeeditor.py:2548 +msgid "Run &selection or current line" +msgstr "選択範囲あるいは現在のカーソル行を実行(&S)" + +#: spyder/plugins/editor.py:805 +msgid "Run selection or current line" +msgstr "選択範囲あるいは現在行を実行" + +#: spyder/plugins/editor.py:813 spyder/widgets/sourcecode/codeeditor.py:2540 +msgid "Run cell" +msgstr "cellを実行" + +#: spyder/plugins/editor.py:816 +msgid "" +"Run current cell (Ctrl+Enter)\n" +"[Use #%% to create cells]" +msgstr "" +"現在のセルを実行する (Ctrl+Enter)\n" +"[Use #%% to create cells]" + +#: spyder/plugins/editor.py:822 spyder/widgets/sourcecode/codeeditor.py:2544 +msgid "Run cell and advance" +msgstr "cellを実行し進む" + +#: spyder/plugins/editor.py:825 +msgid "Run current cell and go to the next one (Shift+Enter)" +msgstr "現在のセルを実行し次のセルへ(Shift+Enter)" + +#: spyder/plugins/editor.py:832 +msgid "Show todo list" +msgstr "TODOリストを表示" + +#: spyder/plugins/editor.py:833 +msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" +msgstr "TODO/FIXME/XXX/HINT/TIP/@todo リストを表示" + +#: spyder/plugins/editor.py:840 +msgid "Show warning/error list" +msgstr "警告/エラー リストを表示" + +#: spyder/plugins/editor.py:841 +msgid "Show code analysis warnings/errors" +msgstr "コード分析の 警告/エラー を表示" + +#: spyder/plugins/editor.py:847 +msgid "Previous warning/error" +msgstr "前の 警告/表示" + +#: spyder/plugins/editor.py:848 +msgid "Go to previous code analysis warning/error" +msgstr "前のコード分析 警告/エラー へ移動" + +#: spyder/plugins/editor.py:851 +msgid "Next warning/error" +msgstr "次の 警告/エラー" + +#: spyder/plugins/editor.py:852 +msgid "Go to next code analysis warning/error" +msgstr "次のコード分析 警告/エラー へ移動" + +#: spyder/plugins/editor.py:856 +msgid "Last edit location" +msgstr "最後に編集した箇所" + +#: spyder/plugins/editor.py:857 +msgid "Go to last edit location" +msgstr "最後に編集した箇所へ移動" + +#: spyder/plugins/editor.py:865 +msgid "Previous cursor position" +msgstr "前のカーソル位置" + +#: spyder/plugins/editor.py:866 +msgid "Go to previous cursor position" +msgstr "前のカーソル位置へ移動" + +#: spyder/plugins/editor.py:874 +msgid "Next cursor position" +msgstr "次のカーソル位置" + +#: spyder/plugins/editor.py:875 +msgid "Go to next cursor position" +msgstr "次のカーソル位置へ移動" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Comment" +msgstr "コメント" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Uncomment" +msgstr "コメント解除" + +#: spyder/plugins/editor.py:886 +msgid "Comment current line or selection" +msgstr "現在行あるいは選択範囲をコメントアウト" + +#: spyder/plugins/editor.py:890 +msgid "Add &block comment" +msgstr "ブロックコメントを追加(&B)" + +#: spyder/plugins/editor.py:891 +msgid "Add block comment around current line or selection" +msgstr "ブロックコメントを現在行あるいは選択範囲の周りに追加" + +#: spyder/plugins/editor.py:897 +msgid "R&emove block comment" +msgstr "ブロックコメントを削除(&R)" + +#: spyder/plugins/editor.py:898 +msgid "Remove comment block around current line or selection" +msgstr "現在行あるいは選択範囲の周りのブロックコメントを削除" + +#: spyder/plugins/editor.py:909 +msgid "Indent" +msgstr "インデント" + +#: spyder/plugins/editor.py:910 +msgid "Indent current line or selection" +msgstr "現在行あるいは選択範囲をインデント" + +#: spyder/plugins/editor.py:913 +msgid "Unindent" +msgstr "インデント解除" + +#: spyder/plugins/editor.py:914 +msgid "Unindent current line or selection" +msgstr "現在行あるいは選択範囲をインデント解除" + +#: spyder/plugins/editor.py:918 +msgid "Toggle Uppercase" +msgstr "" + +#: spyder/plugins/editor.py:919 +#, fuzzy +msgid "Change to uppercase current line or selection" +msgstr "現在行あるいは選択範囲をインデント" + +#: spyder/plugins/editor.py:923 +msgid "Toggle Lowercase" +msgstr "" + +#: spyder/plugins/editor.py:924 +#, fuzzy +msgid "Change to lowercase current line or selection" +msgstr "現在行あるいは選択範囲をインデント" + +#: spyder/plugins/editor.py:929 +msgid "Carriage return and line feed (Windows)" +msgstr "CR+LF (Windows)" + +#: spyder/plugins/editor.py:932 +msgid "Line feed (UNIX)" +msgstr "LF (UNIX)" + +#: spyder/plugins/editor.py:935 +msgid "Carriage return (Mac)" +msgstr "CR (Mac)" + +#: spyder/plugins/editor.py:941 +msgid "Convert end-of-line characters" +msgstr "改行コードを変換する" + +#: spyder/plugins/editor.py:945 +msgid "Remove trailing spaces" +msgstr "無駄な後続スペースを削除" + +#: spyder/plugins/editor.py:949 +msgid "Fix indentation" +msgstr "インデントを修正" + +#: spyder/plugins/editor.py:950 +msgid "Replace tab characters by space characters" +msgstr "タブ文字を空白に置換" + +#: spyder/plugins/editor.py:953 +msgid "Go to line..." +msgstr "行へ移動..." + +#: spyder/plugins/editor.py:961 +msgid "Set console working directory" +msgstr "コンソールの作業ディレクトリをセット" + +#: spyder/plugins/editor.py:963 +msgid "" +"Set current console (and file explorer) working directory to current script " +"directory" +msgstr "" +"コンソール(とファイルエクスプローラー)の作業ディレクトリを現在のスクリプトの" +"ディレクトリにセット" + +#: spyder/plugins/editor.py:968 +msgid "Maximum number of recent files..." +msgstr "「最近使用したファイル」の最大履歴数..." + +#: spyder/plugins/editor.py:971 +msgid "Clear recent files list" +msgstr "最近使用したファイルのリストをクリア" + +#: spyder/plugins/editor.py:971 spyder/plugins/projects.py:100 +msgid "Clear this list" +msgstr "このリストをクリア" + +#: spyder/plugins/editor.py:975 +msgid "Open &recent" +msgstr "最近使用したファイル(&R)" + +#: spyder/plugins/editor.py:1359 +msgid "?" +msgstr "?" + +#: spyder/plugins/editor.py:1586 +msgid "Spyder Editor" +msgstr "Spyderエディタ" + +#: spyder/plugins/editor.py:1587 +msgid "This is a temporary script file." +msgstr "これは一時的なスクリプトファイルです" + +#: spyder/plugins/editor.py:1656 +msgid "untitled" +msgstr "タイトル無し" + +#: spyder/plugins/editor.py:1730 +msgid "Maximum number of recent files" +msgstr "「最近使用したファイル」の最大履歴数" + +#: spyder/plugins/editor.py:1863 +msgid "Printing..." +msgstr "印刷中..." + +#: spyder/plugins/explorer.py:53 +msgid "File explorer" +msgstr "ファイルエクスプローラー" + +#: spyder/plugins/externalconsole.py:46 +msgid "Interactive data plotting in the consoles" +msgstr "コンソールでの対話的データプロット" + +#: spyder/plugins/externalconsole.py:54 spyder/plugins/externalconsole.py:709 +#, fuzzy +msgid "Python console" +msgstr "IPythonコンソール" + +#: spyder/plugins/externalconsole.py:59 +msgid "One tab per script" +msgstr "スクリプトあたりのタブを1つ" + +#: spyder/plugins/externalconsole.py:60 +#: spyder/widgets/externalshell/baseshell.py:160 +msgid "Show elapsed time" +msgstr "実行時間を表示" + +#: spyder/plugins/externalconsole.py:61 spyder/widgets/explorer.py:1094 +msgid "Show icons and text" +msgstr "アイコンとテキストを表示" + +#: spyder/plugins/externalconsole.py:73 +msgid "Buffer: " +msgstr "バッファー:" + +#: spyder/plugins/externalconsole.py:73 spyder/plugins/ipythonconsole.py:319 +msgid " lines" +msgstr "行" + +#: spyder/plugins/externalconsole.py:78 +msgid "Merge process standard output/error channels" +msgstr "プロセス標準出力/エラーチャンネルをマージ" + +#: spyder/plugins/externalconsole.py:80 +msgid "" +"Merging the output channels of the process means that\n" +"the standard error won't be written in red anymore,\n" +"but this has the effect of speeding up display." +msgstr "" +"プロセスの出力チャンネルをマージした場合\n" +"標準エラーは赤色で表示されなくなりますが、\n" +"表示は高速化されます。" + +#: spyder/plugins/externalconsole.py:84 +msgid "Colorize standard error channel using ANSI escape codes" +msgstr "標準エラーチャンネルをANSIエスケープコードで色分け" + +#: spyder/plugins/externalconsole.py:86 +msgid "" +"This method is the only way to have colorized standard\n" +"error channel when the output channels have been merged." +msgstr "" +"出力チャンネルがマージされた場合、これは標準エラーチャンネルを\n" +"色分けする唯一の方法となります。" + +#: spyder/plugins/externalconsole.py:102 spyder/plugins/ipythonconsole.py:306 +#: spyder/widgets/variableexplorer/arrayeditor.py:540 +#: spyder/widgets/variableexplorer/dataframeeditor.py:552 +msgid "Background color" +msgstr "背景色" + +#: spyder/plugins/externalconsole.py:103 +msgid "" +"This option will be applied the next time a Python console or a terminal is " +"opened." +msgstr "" +"このオプションは次回Pythonコンソールかターミナルが開かれた時適用されます。" + +#: spyder/plugins/externalconsole.py:106 +msgid "Light background (white color)" +msgstr "明るい背景(白色)" + +#: spyder/plugins/externalconsole.py:131 +msgid "PYTHONSTARTUP replacement" +msgstr "PYTHONSTARTUP代替物" + +#: spyder/plugins/externalconsole.py:133 +msgid "" +"This option will override the PYTHONSTARTUP environment variable which\n" +"defines the script to be executed during the Python console startup." +msgstr "" +"このオプションはPYTHONSTARTUP環境変数(Pythonコンソール起動時に実行する\n" +"スクリプトを指定する)を上書きします。" + +#: spyder/plugins/externalconsole.py:138 +msgid "Default PYTHONSTARTUP script" +msgstr "デフォルトPYTHONSTARTUPスクリプト" + +#: spyder/plugins/externalconsole.py:142 +msgid "Use the following startup script:" +msgstr "以下のスタートアップスクリプトを実行:" + +#: spyder/plugins/externalconsole.py:159 +msgid "Monitor" +msgstr "モニター" + +#: spyder/plugins/externalconsole.py:160 +msgid "" +"The monitor provides introspection features to console: code completion, " +"calltips and variable explorer. Because it relies on several modules, " +"disabling the monitor may be useful to accelerate console startup." +msgstr "" +"モニターによりコンソールでコード補完、 コールティップス及び変数エクスプロー" +"ラー等のintrospection機能が利用できます。モニターは複数のモジュールに依存する" +"ため、無効化した場合コンソールのスタートアップを高速化できます。" + +#: spyder/plugins/externalconsole.py:167 +msgid "Enable monitor" +msgstr "モニターを有効化" + +#: spyder/plugins/externalconsole.py:180 +msgid "Default library" +msgstr "デフォルトライブラリ" + +#: spyder/plugins/externalconsole.py:185 +msgid "Qt-Python Bindings" +msgstr "Qt-Pythonバインディング" + +#: spyder/plugins/externalconsole.py:187 +msgid "Library:" +msgstr "ライブラリ:" + +#: spyder/plugins/externalconsole.py:189 +msgid "" +"This option will act on
libraries such as Matplotlib, guidata or ETS" +msgstr "このオプションは
Matplotlib、guidata、ETS等のライブラリで機能します" + +#: spyder/plugins/externalconsole.py:198 spyder/plugins/ipythonconsole.py:560 +msgid "Graphics" +msgstr "グラフィックス" + +#: spyder/plugins/externalconsole.py:199 +msgid "" +"Decide which backend to use to display graphics. If unsure, please select " +"the Automatic backend.

Note: We support a very limited " +"number of backends in our Python consoles. If you prefer to work with a " +"different one, please use an IPython console." +msgstr "" +"画像を表示するためにどのバックエンドを使うか選択してください。よくわからない" +"場合Automatic バックエンドを選んでください。

注: Python" +"コンソールでサポートされるバックエンドは非常に限られています。もし別のバック" +"エンドを使いたい場合IPythonコンソールを使ってください。" + +#: spyder/plugins/externalconsole.py:208 +msgid "None" +msgstr "" + +#: spyder/plugins/externalconsole.py:208 spyder/plugins/ipythonconsole.py:351 +msgid "Automatic" +msgstr "自動" + +#: spyder/plugins/externalconsole.py:213 spyder/plugins/ipythonconsole.py:373 +msgid "Backend:" +msgstr "バックエンド:" + +#: spyder/plugins/externalconsole.py:215 spyder/plugins/ipythonconsole.py:375 +msgid "This option will be applied the next time a console is opened." +msgstr "このオプションは次にコンソールを開いた時に適用されます" + +#: spyder/plugins/externalconsole.py:226 +msgid "Enthought Tool Suite" +msgstr "Enthoughtツールスイート" + +#: spyder/plugins/externalconsole.py:227 +msgid "" +"Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical " +"user interfaces." +msgstr "" +"Enthoughtツールスイート(ETS)はPyQt4(qt4)とwxPython(wx)GUIをサポートします。" + +#: spyder/plugins/externalconsole.py:231 +msgid "ETS_TOOLKIT:" +msgstr "ETS_TOOLKIT:" + +#: spyder/plugins/externalconsole.py:255 +msgid "External modules" +msgstr "外部モジュール" + +#: spyder/plugins/externalconsole.py:447 +msgid "" +"No Python console is currently selected to run %s.

Please " +"select or open a new Python console and try again." +msgstr "" +"実行対象として選ばれているPythonコンソールがありません: %s

選" +"択するか新規Pythonコンソールを開いて再試行してください。" + +#: spyder/plugins/externalconsole.py:518 +msgid "" +"%s is already running in a separate process.\n" +"Do you want to kill the process before starting a new one?" +msgstr "" +"%s は既に別プロセスで実行されています。\n" +"新しいプロセスを開始する前にそのプロセスを殺しますか?" + +#: spyder/plugins/externalconsole.py:647 +msgid "Command Window" +msgstr "コマンドウインドウ" + +#: spyder/plugins/externalconsole.py:649 spyder/plugins/ipythonconsole.py:297 +msgid "Terminal" +msgstr "ターミナル" + +#: spyder/plugins/externalconsole.py:731 +msgid "Open a &Python console" +msgstr "Pythonコンソールを開く(&O)" + +#: spyder/plugins/externalconsole.py:735 +msgid "Open &command prompt" +msgstr "コマンドプロンプトを開く(&C)" + +#: spyder/plugins/externalconsole.py:736 +msgid "Open a Windows command prompt" +msgstr "Windowsコマンドプロンプトを開く" + +#: spyder/plugins/externalconsole.py:738 +msgid "Open a &terminal" +msgstr "ターミナルを開く(&T)" + +#: spyder/plugins/externalconsole.py:739 +msgid "Open a terminal window" +msgstr "ターミナルウインドウを開く" + +#: spyder/plugins/findinfiles.py:127 spyder/widgets/findinfiles.py:689 +msgid "Find in files" +msgstr "ファイルの検索" + +#: spyder/plugins/findinfiles.py:148 +msgid "&Find in files" +msgstr "ファイル内を検索(&F)" + +#: spyder/plugins/findinfiles.py:151 +msgid "Search text in multiple files" +msgstr "複数のファイルからテキストを検索" + +#: spyder/plugins/help.py:44 +msgid "Show help for objects in the Editor and Consoles in a dedicated pane" +msgstr "専用ペインでエディタ・コンソール中のオブジェクトのヘルプを表示する" + +#: spyder/plugins/help.py:110 +msgid "Automatic connections" +msgstr "自動接続" + +#: spyder/plugins/help.py:111 +msgid "" +"This pane can automatically show an object's help information after a left " +"parenthesis is written next to it. Below you can decide to which plugin you " +"want to connect it to turn on this feature." +msgstr "" +"このペインは括弧の左側が入力されるとそのオブジェクトのヘルプ情報を自動的に表" +"示します。この機能を有効にするために接続したいプラグインを選べます。" + +#: spyder/plugins/help.py:123 +msgid "" +"This feature requires the Rope or Jedi libraries.\n" +"It seems you don't have either installed." +msgstr "" +"この機能はRopeあるいはJediライブラリを必要とします。\n" +"どちらもインストールされていないようです。" + +#: spyder/plugins/help.py:126 +msgid "Python Console" +msgstr "Pythonコンソール" + +#: spyder/plugins/help.py:128 +msgid "IPython Console" +msgstr "IPythonコンソール" + +#: spyder/plugins/help.py:140 +msgid "Additional features" +msgstr "追加機能" + +#: spyder/plugins/help.py:141 +msgid "Render mathematical equations" +msgstr "数式を描画" + +#: spyder/plugins/help.py:147 +msgid "This feature requires Sphinx 1.1 or superior." +msgstr "この機能はSphinx 1.1以上を必要とします。" + +#: spyder/plugins/help.py:148 +msgid "Sphinx %s is currently installed." +msgstr "Sphinx %s が現在インストールされています。" + +#: spyder/plugins/help.py:309 +msgid "No further documentation available" +msgstr "これ以上のドキュメントはありません" + +#: spyder/plugins/help.py:347 +#, fuzzy +msgid "No documentation available" +msgstr "これ以上のドキュメントはありません" + +#: spyder/plugins/help.py:378 +msgid "Source" +msgstr "ソース" + +#: spyder/plugins/help.py:385 spyder/plugins/runconfig.py:186 +#: spyder/plugins/runconfig.py:456 spyder/widgets/externalshell/baseshell.py:94 +#: spyder/widgets/ipythonconsole/client.py:202 +msgid "Console" +msgstr "コンソール" + +#: spyder/plugins/help.py:393 +#: spyder/widgets/variableexplorer/collectionseditor.py:133 +msgid "Object" +msgstr "オブジェクト" + +#: spyder/plugins/help.py:407 +msgid "Plain Text" +msgstr "プレーンテキスト" + +#: spyder/plugins/help.py:411 +msgid "Show Source" +msgstr "ソースを表示" + +#: spyder/plugins/help.py:415 +msgid "Rich Text" +msgstr "リッチテキスト" + +#: spyder/plugins/help.py:425 +msgid "Automatic import" +msgstr "自動インポート" + +#: spyder/plugins/help.py:437 spyder/plugins/history.py:106 +#: spyder/widgets/editor.py:504 spyder/widgets/explorer.py:1105 +#: spyder/widgets/externalshell/baseshell.py:140 +#: spyder/widgets/ipythonconsole/client.py:251 +#: spyder/widgets/variableexplorer/namespacebrowser.py:160 +msgid "Options" +msgstr "オプション" + +#: spyder/plugins/help.py:695 +msgid "" +"Here you can get help of any object by pressing %s in front of it, either on " +"the Editor or the Console.%sHelp can also be shown automatically after " +"writing a left parenthesis next to an object. You can activate this behavior " +"in %s." +msgstr "" +"エディタあるいはコンソールでは %s を直前で押すことで直後のオブジェクトのヘル" +"プが得られます。%s ヘルプはオブジェクトの隣で左括弧を入力することでも自動的に" +"表示されます。%sでこの機能を有効化できます。" + +#: spyder/plugins/help.py:701 +msgid "Preferences > Help" +msgstr "設定 > ヘルプ" + +#: spyder/plugins/help.py:708 +msgid "Usage" +msgstr "使用法" + +#: spyder/plugins/help.py:709 +msgid "New to Spyder? Read our" +msgstr "Spypderは初めてですか? 読んでください。" + +#: spyder/plugins/help.py:710 +msgid "tutorial" +msgstr "チュートリアル" + +#: spyder/plugins/help.py:717 +msgid "" +"Please consider installing Sphinx to get documentation rendered in rich text." +msgstr "" +"ドキュメントをリッチテキストで表示するためにSphinxをインストールした方が良い" +"でしょう。" + +#: spyder/plugins/help.py:886 +msgid "Lock" +msgstr "ロック" + +#: spyder/plugins/help.py:886 +msgid "Unlock" +msgstr "アンロック" + +#: spyder/plugins/help.py:930 +msgid "" +"The following error occured when calling Sphinx %s.
Incompatible " +"Sphinx version or doc string decoding failed.

Error message:
%s" +msgstr "" +"以下のエラーは Sphinx %s を呼んだ時に発生しました。
Sphinxのバー" +"ジョンが合っていないか文字列のデコードに失敗しました。

エラーメッセー" +"ジ:
%s" + +#: spyder/plugins/help.py:974 +msgid "No source code available." +msgstr "ソースコードが利用できません" + +#: spyder/plugins/history.py:39 +msgid "Settings" +msgstr "設定" + +#: spyder/plugins/history.py:41 +msgid " entries" +msgstr " エントリ" + +#: spyder/plugins/history.py:41 +msgid "History depth: " +msgstr "ヒストリー階層:" + +#: spyder/plugins/history.py:48 +msgid "Scroll automatically to last entry" +msgstr "最後のエントリーまで自動的にスクロール" + +#: spyder/plugins/history.py:126 +msgid "History log" +msgstr "ヒストリログ" + +#: spyder/plugins/history.py:153 +msgid "History..." +msgstr "ヒストリ..." + +#: spyder/plugins/history.py:155 +msgid "Set history maximum entries" +msgstr "ヒストリの最大エントリ数" + +#: spyder/plugins/history.py:260 +msgid "History" +msgstr "ヒストリ" + +#: spyder/plugins/history.py:261 +msgid "Maximum entries" +msgstr "最大エントリ数" + +#: spyder/plugins/ipythonconsole.py:64 +msgid "Symbolic mathematics in the IPython Console" +msgstr "IPythonコンソールでの記号数学" + +#: spyder/plugins/ipythonconsole.py:116 +msgid "" +"The authenticity of host %s can't be established. Are you sure you " +"want to continue connecting?" +msgstr "ホスト %s の信頼性が確立できません。本当に接続を続けますか?" + +#: spyder/plugins/ipythonconsole.py:128 +msgid "The authenticity of the host can't be established" +msgstr "ホストの信頼性が確立できません" + +#: spyder/plugins/ipythonconsole.py:135 +msgid "Tunnel '%s' failed to start" +msgstr "Tunnel '%s' は開始できませんでした" + +#: spyder/plugins/ipythonconsole.py:140 +msgid "Could not connect to remote host" +msgstr "リモートホストへ接続できませんでした" + +#: spyder/plugins/ipythonconsole.py:157 spyder/plugins/ipythonconsole.py:747 +msgid "Connect to an existing kernel" +msgstr "既存のカーネルに接続" + +#: spyder/plugins/ipythonconsole.py:159 +msgid "" +"Please enter the connection info of the kernel you want to connect to. For " +"that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for " +"example kernel-3764.json or just 3764)." +msgstr "" +"接続したいカーネルの接続情報を入力してください。 表示 ボタンを押し、" +"JSON接続ファイルを選ぶか、ローカルカーネルの場合IDを入力してください (例: " +"kernel-3764.json あるいは 3764)。" + +#: spyder/plugins/ipythonconsole.py:170 +msgid "Connection info:" +msgstr "接続情報:" + +#: spyder/plugins/ipythonconsole.py:172 +msgid "Path to connection file or kernel id" +msgstr "接続ファイルへのパスあるいはカーネルID" + +#: spyder/plugins/ipythonconsole.py:174 spyder/plugins/ipythonconsole.py:191 +msgid "Browse" +msgstr "表示" + +#: spyder/plugins/ipythonconsole.py:183 +msgid "This is a remote kernel" +msgstr "これはリモートカーネルです" + +#: spyder/plugins/ipythonconsole.py:187 +msgid "username@hostname:port" +msgstr "username@hostname:port" + +#: spyder/plugins/ipythonconsole.py:190 +msgid "Path to ssh key file" +msgstr "sshキーファイルへのパス" + +#: spyder/plugins/ipythonconsole.py:199 +msgid "Password or ssh key passphrase" +msgstr "パスワードあるいはsshキーのパスフレーズ" + +#: spyder/plugins/ipythonconsole.py:203 +msgid "Host name" +msgstr "ホスト名" + +#: spyder/plugins/ipythonconsole.py:204 +msgid "Ssh key" +msgstr "sshキー" + +#: spyder/plugins/ipythonconsole.py:205 +msgid "Password" +msgstr "パスワード" + +#: spyder/plugins/ipythonconsole.py:234 +#, fuzzy +msgid "Open connection file" +msgstr "IPython接続ファイルを開く" + +#: spyder/plugins/ipythonconsole.py:239 +msgid "Select ssh key" +msgstr "sshキーを選択" + +#: spyder/plugins/ipythonconsole.py:267 spyder/plugins/ipythonconsole.py:686 +msgid "IPython console" +msgstr "IPythonコンソール" + +#: spyder/plugins/ipythonconsole.py:274 +msgid "Display initial banner" +msgstr "初期バナーの表示" + +#: spyder/plugins/ipythonconsole.py:275 +msgid "" +"This option lets you hide the message shown at\n" +"the top of the console when it's opened." +msgstr "" +"このオプションによりコンソールを開いた時に表示される\n" +"一番上のメッセージを隠すことができます。" + +#: spyder/plugins/ipythonconsole.py:277 +msgid "Use a pager to display additional text inside the console" +msgstr "コンソール内で追加テキストを表示するためにページャーを使用" + +#: spyder/plugins/ipythonconsole.py:279 +msgid "" +"Useful if you don't want to fill the console with long help or completion " +"texts.\n" +"Note: Use the Q key to get out of the pager." +msgstr "" +"補完テキストや長いヘルプでコンソールを埋めたくない場合有効です。\n" +"注: このページャーから出るには Qキーを押してください。" + +#: spyder/plugins/ipythonconsole.py:284 +msgid "Ask for confirmation before closing" +msgstr "閉じる前に確認するようにする" + +#: spyder/plugins/ipythonconsole.py:294 +msgid "Completion Type" +msgstr "補完タイプ" + +#: spyder/plugins/ipythonconsole.py:295 +msgid "Decide what type of completion to use" +msgstr "どのタイプの補完を使うか選択" + +#: spyder/plugins/ipythonconsole.py:297 +#, fuzzy +msgid "Graphical" +msgstr "グラフィックス" + +#: spyder/plugins/ipythonconsole.py:297 +#, fuzzy +msgid "Plain" +msgstr "プレーンテキスト" + +#: spyder/plugins/ipythonconsole.py:298 +msgid "Completion:" +msgstr "補完:" + +#: spyder/plugins/ipythonconsole.py:307 +msgid "Light background" +msgstr "明るい背景" + +#: spyder/plugins/ipythonconsole.py:309 +msgid "Dark background" +msgstr "暗い背景" + +#: spyder/plugins/ipythonconsole.py:319 +msgid "Buffer: " +msgstr "バッファー:" + +#: spyder/plugins/ipythonconsole.py:321 +msgid "" +"Set the maximum number of lines of text shown in the\n" +"console before truncation. Specifying -1 disables it\n" +"(not recommended!)" +msgstr "" +"コンソール内で表示するテキストの最大行数を設定。\n" +"-1 を設定すると無効になります\n" +"(非推奨です!)" + +#: spyder/plugins/ipythonconsole.py:330 +msgid "Support for graphics (Matplotlib)" +msgstr "画像のサポート(Matplotlib)" + +#: spyder/plugins/ipythonconsole.py:331 +msgid "Activate support" +msgstr "サポートを有効化" + +#: spyder/plugins/ipythonconsole.py:332 +msgid "Automatically load Pylab and NumPy modules" +msgstr "Pylab, NumPyモジュールを自動的にロード" + +#: spyder/plugins/ipythonconsole.py:335 +msgid "" +"This lets you load graphics support without importing \n" +"the commands to do plots. Useful to work with other\n" +"plotting libraries different to Matplotlib or to develop \n" +"GUIs with Spyder." +msgstr "" +"プロットコマンドをインポートすることなしに画像サポートを\n" +"有効にします。Matplotlib以外のプロットライブラリを利用\n" +"している場合や、SpyderでGUIを開発している場合、\n" +"有用です。" + +#: spyder/plugins/ipythonconsole.py:350 +msgid "Inline" +msgstr "インライン" + +#: spyder/plugins/ipythonconsole.py:352 +msgid "Graphics backend" +msgstr "グラフィックスのバックエンド" + +#: spyder/plugins/ipythonconsole.py:353 +msgid "" +"Decide how graphics are going to be displayed in the console. If unsure, " +"please select %s to put graphics inside the console or %s to " +"interact with them (through zooming and panning) in a separate window." +msgstr "" +"コンソール内でどのように画像を表示するか選んでください。よくわからなければ、" +"コンソール内で画像を表示したい場合 %s を選び、別ウィンドウで(ズーム、" +"パン等)操作したい場合は %s を選んでください。" + +#: spyder/plugins/ipythonconsole.py:386 +msgid "Inline backend" +msgstr "インラインのバックエンド" + +#: spyder/plugins/ipythonconsole.py:387 +msgid "Decide how to render the figures created by this backend" +msgstr "このバックエンドで生成された画像をどのように描画するか選択" + +#: spyder/plugins/ipythonconsole.py:391 +msgid "Format:" +msgstr "フォーマット:" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "Resolution:" +msgstr "解像度:" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "dpi" +msgstr "dpi" + +#: spyder/plugins/ipythonconsole.py:396 +msgid "Only used when the format is PNG. Default is 72" +msgstr "PNGフォーマットの場合のみ使われます。デフォルトは72" + +#: spyder/plugins/ipythonconsole.py:399 +msgid "Width:" +msgstr "幅:" + +#: spyder/plugins/ipythonconsole.py:399 spyder/plugins/ipythonconsole.py:403 +msgid "inches" +msgstr "インチ" + +#: spyder/plugins/ipythonconsole.py:401 +msgid "Default is 6" +msgstr "デフォルトは6です" + +#: spyder/plugins/ipythonconsole.py:403 +msgid "Height:" +msgstr "高さ:" + +#: spyder/plugins/ipythonconsole.py:405 +msgid "Default is 4" +msgstr "デフォルトは4です" + +#: spyder/plugins/ipythonconsole.py:432 +msgid "" +"You can run several lines of code when a console is started. Please " +"introduce each one separated by commas, for example:
import os, import " +"sys" +msgstr "" +"コンソールを開始した時にコードを実行できます。それぞれをコンマで区切ってくだ" +"さい。例:
import os, import sys" + +#: spyder/plugins/ipythonconsole.py:438 +msgid "Lines:" +msgstr "行:" + +#: spyder/plugins/ipythonconsole.py:447 +msgid "Run a file" +msgstr "ファイルを実行" + +#: spyder/plugins/ipythonconsole.py:448 +msgid "" +"You can also run a whole file at startup instead of just some lines (This is " +"similar to have a PYTHONSTARTUP file)." +msgstr "" +"行の一部だけではなくファイル全体をスタート時に実行できます(PYTHONSTARTUPファ" +"イルと類似)" + +#: spyder/plugins/ipythonconsole.py:452 +msgid "Use the following file:" +msgstr "以下のファイルを使う:" + +#: spyder/plugins/ipythonconsole.py:466 +msgid "Greedy completion" +msgstr "Greedy completion" + +#: spyder/plugins/ipythonconsole.py:467 +msgid "" +"Enable Tab completion on elements of lists, results of function " +"calls, etc, without assigning them to a variable.
For example, you " +"can get completions on things like li[0].<Tab> or ins." +"meth().<Tab>" +msgstr "" +"リスト要素、関数呼び出し結果等を変数に割り当てずにTab 補完" +"を有効にする。
例えば li[0].<Tab>ins.meth().<" +"Tab> のように補完できる" + +#: spyder/plugins/ipythonconsole.py:475 +msgid "Use the greedy completer" +msgstr "greedy completerを利用する" + +#: spyder/plugins/ipythonconsole.py:486 +msgid "Autocall" +msgstr "Autocall" + +#: spyder/plugins/ipythonconsole.py:487 +msgid "" +"Autocall makes IPython automatically call any callable object even if you " +"didn't type explicit parentheses.
For example, if you type str 43 " +"it becomes str(43) automatically." +msgstr "" +"明示的に括弧を省略している場合でも、IPythonで呼び出し可能なオブジェクトを" +"Autocallによって自動的に呼ぶ。
例えば、str 43 と入力すると自動的" +"に str(43) となる。" + +#: spyder/plugins/ipythonconsole.py:494 +msgid "Smart" +msgstr "Smart" + +#: spyder/plugins/ipythonconsole.py:495 +msgid "Full" +msgstr "Full" + +#: spyder/plugins/ipythonconsole.py:496 +msgid "Off" +msgstr "オフPff" + +#: spyder/plugins/ipythonconsole.py:498 +msgid "Autocall: " +msgstr "Autocall:" + +#: spyder/plugins/ipythonconsole.py:499 +msgid "" +"On %s mode, Autocall is not applied if there are no arguments after " +"the callable. On %s mode, all callable objects are automatically " +"called (even if no arguments are present)." +msgstr "" +"%s モードでは、 呼び出し可能オブジェクトに引数がない場合Autocallは適用" +"されません。 %s モードでは、(引数がない場合でも)全ての呼び出し可能オブ" +"ジェクトが自動的に呼ばれます。" + +#: spyder/plugins/ipythonconsole.py:511 +msgid "Symbolic Mathematics" +msgstr "記号数学" + +#: spyder/plugins/ipythonconsole.py:512 +#, fuzzy +msgid "" +"Perfom symbolic operations in the console (e.g. integrals, derivatives, " +"vector calculus, etc) and get the outputs in a beautifully printed style (it " +"requires the Sympy module)." +msgstr "" +"記号演算をコンソール内で実行し(例:積分、微分、ベクトル演算等)、整えられた印刷" +"可能スタイルで出力する。" + +#: spyder/plugins/ipythonconsole.py:517 +msgid "Use symbolic math" +msgstr "記号演算を使う" + +#: spyder/plugins/ipythonconsole.py:518 +msgid "" +"This option loads the Sympy library to work with.
Please refer to its " +"documentation to learn how to use it." +msgstr "" +"このオプションはSympyライブラリをロードします。
Sympyドキュメントを参照" +"し、使用方法を学んでください。" + +#: spyder/plugins/ipythonconsole.py:528 +msgid "Prompts" +msgstr "プロンプト" + +#: spyder/plugins/ipythonconsole.py:529 +msgid "Modify how Input and Output prompts are shown in the console." +msgstr "プロンプトの入力と出力のコンソール内での表示を変更する。" + +#: spyder/plugins/ipythonconsole.py:532 +msgid "Input prompt:" +msgstr "入力プロンプト:" + +#: spyder/plugins/ipythonconsole.py:534 +msgid "" +"Default is
In [<span class=\"in-prompt-number\">%i</span>]:" +msgstr "" +"デフォルトは
In [<span class=\"in-prompt-number\">%i</span>]:" + +#: spyder/plugins/ipythonconsole.py:538 +msgid "Output prompt:" +msgstr "出力プロンプト:" + +#: spyder/plugins/ipythonconsole.py:540 +msgid "" +"Default is
Out[<span class=\"out-prompt-number\">%i</span>]:" +msgstr "" +"デフォルトは
Out[<span class=\"out-prompt-number\">%i</span>]:" + +#: spyder/plugins/ipythonconsole.py:562 spyder/plugins/workingdirectory.py:45 +msgid "Startup" +msgstr "スタートアップ" + +#: spyder/plugins/ipythonconsole.py:734 +msgid "Open an &IPython console" +msgstr "IPythonコンソールを開く(&I)" + +#: spyder/plugins/ipythonconsole.py:737 +msgid "Use %s+T when the console is selected to open a new one" +msgstr "コンソールを新しく開く場合 %s+T を使う" + +#: spyder/plugins/ipythonconsole.py:740 +msgid "Open a new console" +msgstr "新規コンソールを開く" + +#: spyder/plugins/ipythonconsole.py:748 +msgid "Open a new IPython console connected to an existing kernel" +msgstr "既存カーネルに接続された新規IPythonコンソールを開く" + +#: spyder/plugins/ipythonconsole.py:836 +msgid "" +"No IPython console is currently available to run %s.

Please " +"open a new one and try again." +msgstr "" +"現在%s実行可能なIPythonコンソールがありません。.

新規に開いて再" +"試行してください。" + +#: spyder/plugins/ipythonconsole.py:880 +msgid "" +"Your Python environment or installation doesn't have the ipykernel " +"module installed on it. Without this module is not possible for Spyder to " +"create a console for you.

You can install ipykernel by " +"running in a terminal:

pip install ipykernel

or

conda install ipykernel" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:1105 +#, fuzzy +msgid "Do you want to close this console?" +msgstr "本当にこのスキームを削除しますか?" + +#: spyder/plugins/ipythonconsole.py:1111 +msgid "" +"Do you want to close all other consoles connected to the same kernel as this " +"one?" +msgstr "同じカーネルに接続している他の全てのコンソールを閉じますか?" + +#: spyder/plugins/ipythonconsole.py:1382 +msgid "IPython" +msgstr "IPython" + +#: spyder/plugins/ipythonconsole.py:1383 +#, fuzzy +msgid "Unable to connect to %s" +msgstr "IPythonに接続できませんでした。 %s" + +#: spyder/plugins/ipythonconsole.py:1443 +msgid "Connection error" +msgstr "接続エラー" + +#: spyder/plugins/ipythonconsole.py:1444 +msgid "" +"Could not open ssh tunnel. The error was:\n" +"\n" +msgstr "" +"sshトンネルを開けませんでした。エラー:\n" +"\n" + +#: spyder/plugins/layoutdialog.py:177 +msgid "Move Up" +msgstr "上へ移動" + +#: spyder/plugins/layoutdialog.py:178 +msgid "Move Down" +msgstr "下へ移動" + +#: spyder/plugins/layoutdialog.py:179 +msgid "Delete Layout" +msgstr "レイアウトの削除" + +#: spyder/plugins/layoutdialog.py:183 +msgid "Layout Display and Order" +msgstr "レイアウトの表示と順序" + +#: spyder/plugins/maininterpreter.py:31 spyder/plugins/maininterpreter.py:66 +#, fuzzy +msgid "Python interpreter" +msgstr "以下のPythonインタープリターを使う" + +#: spyder/plugins/maininterpreter.py:68 +#, fuzzy +msgid "Select the Python interpreter for all Spyder consoles" +msgstr "" +"Spyderにスクリプトを実行させる(Pythonインタープリター)実行可能ファイルを選ぶ:" + +#: spyder/plugins/maininterpreter.py:71 +msgid "Default (i.e. the same as Spyder's)" +msgstr "デフォルト(=Spyderと同じ)" + +#: spyder/plugins/maininterpreter.py:74 +msgid "Use the following Python interpreter:" +msgstr "以下のPythonインタープリターを使う" + +#: spyder/plugins/maininterpreter.py:77 +msgid "Executables" +msgstr "実行可能ファイル" + +#: spyder/plugins/maininterpreter.py:94 +msgid "User Module Reloader (UMR)" +msgstr "モジュールリローダーを使用(UMR)" + +#: spyder/plugins/maininterpreter.py:95 +#, fuzzy +msgid "" +"UMR forces Python to reload modules which were imported when executing a " +"file in a Python or IPython console with the runfile function." +msgstr "" +"外部コンソールで'runfile'機能を使うスクリプトを実行する時、\n" +"UMRはモジュール再読み込みを強制します。" + +#: spyder/plugins/maininterpreter.py:100 +msgid "Enable UMR" +msgstr "UMRを有効化" + +#: spyder/plugins/maininterpreter.py:101 +#, fuzzy +msgid "" +"This option will enable the User Module Reloader (UMR) in Python/IPython " +"consoles. UMR forces Python to reload deeply modules during import when " +"running a Python script using the Spyder's builtin function runfile." +"

1. UMR may require to restart the console in which it will be " +"called (otherwise only newly imported modules will be reloaded when " +"executing files).

2. If errors occur when re-running a PyQt-" +"based program, please check that the Qt objects are properly destroyed (e.g. " +"you may have to use the attribute Qt.WA_DeleteOnClose on your main " +"window, using the setAttribute method)" +msgstr "" +"このオプションはPython/IPython コンソールでユーザーモジュールリローダー (UMR)" +"を有効化します。UMRはSpyderの組み込み関数runfileを使うPythonスクリプト" +"を実行する場合インポート時にモジュールの再読み込みを強制します。

1." +" UMRは実行するコンソールの再起動を必要とする場合が有ります(あるいはスクリ" +"プト実行時に新しくインポートされたモジュールのみが再読み込みされます)。" +"

2. PyQtを使ったプログラムの再実行時にエラーが発生した場合、Qt" +"オブジェクトが適切に破棄されているか確認してください。(例: setAttributeメソッドを使ってアトリビュートQt.WA_DeleteOnClose をメインウインドウ" +"に適用するなど)" + +#: spyder/plugins/maininterpreter.py:117 +msgid "Show reloaded modules list" +msgstr "再読込されたモジュールのリストを表示" + +#: spyder/plugins/maininterpreter.py:118 +msgid "Please note that these changes will be applied only to new consoles" +msgstr "これらの変更は新規コンソールのみに適用されることに注意してください" + +#: spyder/plugins/maininterpreter.py:122 +msgid "Set UMR excluded (not reloaded) modules" +msgstr "UMRから排除される(=再読込されない)モジュールを設定" + +#: spyder/plugins/maininterpreter.py:168 +msgid "" +"You selected a Python %d interpreter for the console but Spyder is " +"running on Python %d!.

Although this is possible, we recommend " +"you to install and run Spyder directly with your selected interpreter, to " +"avoid seeing false warnings and errors due to the incompatible syntax " +"between these two Python versions." +msgstr "" +"コンソール用にPython %dインタープリターが選択されましたがSpyder上では" +"Python %dが実行されています!

この選択は可能ですが、Pythonバー" +"ジョン間での構文非互換性による偽の警告/エラーを避けるため、Spyder上でも選択し" +"たバージョンのインタープリターをインストール・実行することが推奨されます。" + +#: spyder/plugins/maininterpreter.py:178 spyder/plugins/maininterpreter.py:205 +#: spyder/plugins/maininterpreter.py:209 +msgid "UMR" +msgstr "UMR" + +#: spyder/plugins/maininterpreter.py:179 +msgid "Set the list of excluded modules as this: numpy, scipy" +msgstr "" + +#: spyder/plugins/maininterpreter.py:196 +msgid "" +"You are working with Python 2, this means that you can not import a module " +"that contains non-ascii characters." +msgstr "" + +#: spyder/plugins/maininterpreter.py:206 +msgid "" +"The following modules are not installed on your machine:\n" +"%s" +msgstr "" +"以下のモジュールがコンピュータにインストールされていません:\n" +"%s" + +#: spyder/plugins/maininterpreter.py:210 +msgid "" +"Please note that these changes will be applied only to new Python/IPython " +"consoles" +msgstr "" +"これらの変更は新規Python/IPythonコンソールにのみ適用されることに注意してくだ" +"さい" + +#: spyder/plugins/onlinehelp.py:70 +msgid "Online help" +msgstr "オンラインヘルプ" + +#: spyder/plugins/outlineexplorer.py:49 spyder/widgets/editortools.py:195 +msgid "Outline" +msgstr "アウトライン" + +#: spyder/plugins/projects.py:76 spyder/widgets/projects/explorer.py:113 +#: spyder/widgets/projects/explorer.py:127 +msgid "Project explorer" +msgstr "プロジェクトエクスプローラー" + +#: spyder/plugins/projects.py:88 +#, fuzzy +msgid "New Project..." +msgstr "新規プロジェクト..." + +#: spyder/plugins/projects.py:91 +#, fuzzy +msgid "Open Project..." +msgstr "プロジェクトを開く" + +#: spyder/plugins/projects.py:94 +#, fuzzy +msgid "Close Project" +msgstr "プロジェクトを閉じる" + +#: spyder/plugins/projects.py:97 +#, fuzzy +msgid "Delete Project" +msgstr "関係するプロジェクト" + +#: spyder/plugins/projects.py:103 +#, fuzzy +msgid "Project Preferences" +msgstr "設定" + +#: spyder/plugins/projects.py:105 +#, fuzzy +msgid "Recent Projects" +msgstr "関係するプロジェクト" + +#: spyder/plugins/projects.py:240 +msgid "Open project" +msgstr "プロジェクトを開く" + +#: spyder/plugins/projects.py:245 +#, fuzzy +msgid "%s is not a Spyder project!" +msgstr "存在するSpyderプロジェクト" + +#: spyder/plugins/runconfig.py:29 +msgid "Execute in current Python or IPython console" +msgstr "現在のPython/IPythonコンソールで実行" + +#: spyder/plugins/runconfig.py:30 +msgid "Execute in a new dedicated Python console" +msgstr "Pythonに特化した新規コンソールで実行" + +#: spyder/plugins/runconfig.py:31 +msgid "Execute in an external System terminal" +msgstr "外部システムターミナルで実行" + +#: spyder/plugins/runconfig.py:41 +msgid "Always show %s on a first file run" +msgstr "ファイルの先頭に %s を常に表示" + +#: spyder/plugins/runconfig.py:160 spyder/plugins/runconfig.py:474 +msgid "General settings" +msgstr "一般設定" + +#: spyder/plugins/runconfig.py:163 spyder/plugins/runconfig.py:209 +msgid "Command line options:" +msgstr "コマンドラインオプション:" + +#: spyder/plugins/runconfig.py:169 +msgid "Working directory:" +msgstr "作業ディレクトリ:" + +#: spyder/plugins/runconfig.py:181 spyder/plugins/runconfig.py:492 +msgid "Enter debugging mode when errors appear during execution" +msgstr "実行中にエラーが発生した場合デバッグモードに入る" + +#: spyder/plugins/runconfig.py:197 spyder/plugins/runconfig.py:502 +msgid "Dedicated Python console" +msgstr "Dedicated Pythonコンソール" + +#: spyder/plugins/runconfig.py:201 spyder/plugins/runconfig.py:504 +msgid "Interact with the Python console after execution" +msgstr "実行後Pythonコンソールと相互作用する" + +#: spyder/plugins/runconfig.py:205 +msgid "Show warning when killing running process" +msgstr "実行中プロセスが殺された場合には警告を表示" + +#: spyder/plugins/runconfig.py:214 +msgid "-u is added to the other options you set here" +msgstr "ここで設定した以外に、-u がオプションに追加されました。" + +#: spyder/plugins/runconfig.py:224 +msgid "this dialog" +msgstr "このダイアログ" + +#: spyder/plugins/runconfig.py:283 +msgid "Run configuration" +msgstr "設定を実行" + +#: spyder/plugins/runconfig.py:284 +msgid "The following working directory is not valid:
%s" +msgstr "以下の作業ディレクトリは不正です:
%s" + +#: spyder/plugins/runconfig.py:362 +msgid "Run settings for %s" +msgstr "%s の設定を実行" + +#: spyder/plugins/runconfig.py:394 +msgid "Select a run configuration:" +msgstr "実行する設定を選択" + +#: spyder/plugins/runconfig.py:423 spyder/plugins/runconfig.py:448 +msgid "Run Settings" +msgstr "設定を実行" + +#: spyder/plugins/runconfig.py:450 +msgid "" +"The following are the default %s. These options may be overriden " +"using the %s dialog box (see the %s menu)" +msgstr "" +"以下はデフォルトです %s。 これらのオプションは %s ダイアログ" +"ボックスで上書き可能です (see the %s メニュー)" + +#: spyder/plugins/runconfig.py:476 +msgid "Default working directory is:" +msgstr "デフォルト作業ディレクトリは:" + +#: spyder/plugins/runconfig.py:478 +msgid "the script directory" +msgstr "スクリプトディレクトリ" + +#: spyder/plugins/runconfig.py:481 spyder/plugins/workingdirectory.py:57 +msgid "the following directory:" +msgstr "以下のディレクトリ:" + +#: spyder/plugins/runconfig.py:507 +msgid "Show warning when killing running processes" +msgstr "実行中のプロセスが殺された場合警告を表示" + +#: spyder/plugins/runconfig.py:516 +msgid "Run Settings dialog" +msgstr "設定ダイアログを実行" + +#: spyder/plugins/shortcuts.py:137 +msgid "" +"Press the new shortcut and select 'Ok': \n" +"(Press 'Tab' once to switch focus between the shortcut entry \n" +"and the buttons below it)" +msgstr "" +"新しショートカットを押し、 'Ok'を選択してください: \n" +"(ショートカットエントリ間やボタン間のフォーカス移動には\n" +"'Tab'を押してください)" + +#: spyder/plugins/shortcuts.py:140 +msgid "Current shortcut:" +msgstr "現在のショートカット:" + +#: spyder/plugins/shortcuts.py:142 +msgid "New shortcut:" +msgstr "新規ショートカット:" + +#: spyder/plugins/shortcuts.py:155 +msgid "Shortcut: {0}" +msgstr "ショートカット: {0}" + +#: spyder/plugins/shortcuts.py:276 +msgid "Please introduce a different shortcut" +msgstr "異なるショートカットを設定してください" + +#: spyder/plugins/shortcuts.py:313 +msgid "The new shorcut conflicts with:" +msgstr "新しいショートカットは以下と競合しています:" + +#: spyder/plugins/shortcuts.py:324 +msgid "" +"A compound sequence can have {break} a maximum of 4 subsequences.{break}" +msgstr "複合シーケンスは{break} 最大4個のサブシーケンス{break}を持てます。" + +#: spyder/plugins/shortcuts.py:329 +msgid "Invalid key entered" +msgstr "不正なキー入力" + +#: spyder/plugins/shortcuts.py:531 +msgid "Context" +msgstr "コンテキスト" + +#: spyder/plugins/shortcuts.py:533 +#: spyder/widgets/variableexplorer/collectionseditor.py:118 +msgid "Name" +msgstr "名前" + +#: spyder/plugins/shortcuts.py:535 +msgid "Shortcut" +msgstr "ショートカット" + +#: spyder/plugins/shortcuts.py:537 +msgid "Score" +msgstr "スコア" + +#: spyder/plugins/shortcuts.py:697 +msgid "Conflicts" +msgstr "競合" + +#: spyder/plugins/shortcuts.py:698 +msgid "The following conflicts have been detected:" +msgstr "以下の競合が検知されました:" + +#: spyder/plugins/shortcuts.py:783 +msgid "Keyboard shortcuts" +msgstr "キーボード・ショートカット" + +#: spyder/plugins/shortcuts.py:791 +msgid "Search: " +msgstr "検索:" + +#: spyder/plugins/shortcuts.py:792 +msgid "Reset to default values" +msgstr "デフォルト値に戻す" + +#: spyder/plugins/variableexplorer.py:26 +msgid "Autorefresh" +msgstr "自動リフレッシュ" + +#: spyder/plugins/variableexplorer.py:27 +msgid "Enable autorefresh" +msgstr "自動リフレッシュを有効化" + +#: spyder/plugins/variableexplorer.py:29 +msgid "Refresh interval: " +msgstr "リフレッシュ間隔:" + +#: spyder/plugins/variableexplorer.py:33 +msgid "Filter" +msgstr "フィルター" + +#: spyder/plugins/variableexplorer.py:35 +#: spyder/widgets/variableexplorer/namespacebrowser.py:226 +msgid "Exclude private references" +msgstr "privateな参照を除外" + +#: spyder/plugins/variableexplorer.py:36 +#: spyder/widgets/variableexplorer/namespacebrowser.py:241 +msgid "Exclude capitalized references" +msgstr "大文字で始まる参照を除外" + +#: spyder/plugins/variableexplorer.py:37 +#: spyder/widgets/variableexplorer/namespacebrowser.py:234 +msgid "Exclude all-uppercase references" +msgstr "名前が全て大文字の参照を除外" + +#: spyder/plugins/variableexplorer.py:38 +#: spyder/widgets/variableexplorer/namespacebrowser.py:249 +msgid "Exclude unsupported data types" +msgstr "非サポートのデータ型を除外" + +#: spyder/plugins/variableexplorer.py:46 +#: spyder/widgets/variableexplorer/collectionseditor.py:677 +msgid "Show arrays min/max" +msgstr "配列の最小/最大を表示" + +#: spyder/plugins/variableexplorer.py:48 +msgid "Edit data in the remote process" +msgstr "リモートプロセスのデータを編集する" + +#: spyder/plugins/variableexplorer.py:49 +msgid "" +"Editors are opened in the remote process for NumPy arrays, PIL images, " +"lists, tuples and dictionaries.\n" +"This avoids transfering large amount of data between the remote process and " +"Spyder (through the socket)." +msgstr "" +"エディタはリモートプロセスで開かれています(NumPy配列、PILイメージ、リストタプ" +"ル及び辞書型のため)。\n" +"これによりリモートプロセスとSpyder間のsokectによる膨大なデータ転送を避けるこ" +"とができます。" + +#: spyder/plugins/variableexplorer.py:185 +msgid "Variable explorer" +msgstr "変数エクスプローラー" + +#: spyder/plugins/workingdirectory.py:38 +msgid "" +"The global working directory is the working directory for newly " +"opened consoles (Python/IPython consoles and terminals), for the " +"file explorer, for the find in files plugin and for new files " +"created in the editor." +msgstr "" +"グローバル作業ディレクトリファイルエクスプローラーファ" +"イル検索プラグイン、コンソール(Python/IPython コンソールとターミナ" +"ル) を新しく開いた場合、エディタで新規ファイルを開いた場合の作業ディレ" +"クトリです。 " + +#: spyder/plugins/workingdirectory.py:47 +msgid "At startup, the global working directory is:" +msgstr "スタートアップ時のグローバル作業ディレクトリは:" + +#: spyder/plugins/workingdirectory.py:51 +msgid "the same as in last session" +msgstr "最後のセッションと同じ" + +#: spyder/plugins/workingdirectory.py:53 +msgid "At startup, Spyder will restore the global directory from last session" +msgstr "" +"Spyderはスタートアップ時に最後のセッションからグローバル作業ディレクトリを復" +"元します" + +#: spyder/plugins/workingdirectory.py:59 +msgid "At startup, the global working directory will be the specified path" +msgstr "グローバル作業ディレクトリはスタートアップ時にパスにより決定されます" + +#: spyder/plugins/workingdirectory.py:71 +msgid "Files are opened from:" +msgstr "ファイルは以下から開かれています:" + +#: spyder/plugins/workingdirectory.py:75 spyder/plugins/workingdirectory.py:88 +msgid "the current file directory" +msgstr "現在ファイルのディレクトリ" + +#: spyder/plugins/workingdirectory.py:79 spyder/plugins/workingdirectory.py:92 +msgid "the global working directory" +msgstr "グローバル作業ディレクトリ" + +#: spyder/plugins/workingdirectory.py:84 +msgid "Files are created in:" +msgstr "ファイルは以下で作成されています:" + +#: spyder/plugins/workingdirectory.py:98 +msgid "Change to file base directory" +msgstr "ファイルの基本ディレクトリに変更" + +#: spyder/plugins/workingdirectory.py:100 +msgid "When opening a file" +msgstr "ファイルを開いた時" + +#: spyder/plugins/workingdirectory.py:102 +msgid "When saving a file" +msgstr "ファイルを保存する時" + +#: spyder/plugins/workingdirectory.py:172 +msgid "Back" +msgstr "戻る" + +#: spyder/plugins/workingdirectory.py:180 spyder/widgets/explorer.py:1099 +#: spyder/widgets/variableexplorer/importwizard.py:529 +msgid "Next" +msgstr "次" + +#: spyder/plugins/workingdirectory.py:191 +msgid "" +"This is the working directory for newly\n" +"opened consoles (Python/IPython consoles and\n" +"terminals), for the file explorer, for the\n" +"find in files plugin and for new files\n" +"created in the editor" +msgstr "" +"これは、ファイルエクスプローラー、\n" +"ファイル検索プラグイン、新規コンソール\n" +"(Python/IPythonコンソール及びターミナル)\n" +"及びエディタで作成した新規ファイルの\n" +"作業ディレクトリです。" + +#: spyder/plugins/workingdirectory.py:219 +msgid "Browse a working directory" +msgstr "作業ディレクトリを表示" + +#: spyder/plugins/workingdirectory.py:226 +msgid "Change to parent directory" +msgstr "親ディレクトリへ移動" + +#: spyder/plugins/workingdirectory.py:233 +msgid "Global working directory" +msgstr "グローバル作業ディレクトリ" + +#: spyder/utils/codeanalysis.py:91 +msgid "Real-time code analysis on the Editor" +msgstr "エディタ上でのリアルタイムコード分析" + +#: spyder/utils/codeanalysis.py:95 +msgid "Real-time code style analysis on the Editor" +msgstr "エディタ上でのリアルタイムコードスタイル分析" + +#: spyder/utils/environ.py:101 +msgid "" +"Module pywin32 was not found.
Please restart this Windows " +"session (not the computer) for changes to take effect." +msgstr "" +"モジュール pywin32 は見つかりませんでした
変更を反映させるには(コ" +"ンピュータではなく)このWindowsセッション を再起動して。" + +#: spyder/utils/environ.py:114 +msgid "" +"If you accept changes, this will modify the current user environment " +"variables directly in Windows registry. Use it with precautions, at " +"your own risks.

Note that for changes to take effect, you will need " +"to restart the parent process of this application (simply restart Spyder if " +"you have executed it from a Windows shortcut, otherwise restart any " +"application from which you may have executed it, like Python(x,y) Home for example)" +msgstr "" +"変更を受諾すると Windowsレジストリ内の現在のユーザー環境変数が直接修正" +"されます。 各自のリスクで注意深く使用すること。

変更が効果を及ぼすには" +"このアプリケーションの親プロセスを再起動する必要があります。(Windowsショート" +"カットから実行している場合単にspyderを再起動すれば良い。そうでなければ例えば" +"Python(x,y) HomeのようにSpyderを実行しているアプリケーションを全て再起" +"動する)" + +#: spyder/utils/help/sphinxify.py:217 spyder/utils/help/sphinxify.py:227 +msgid "" +"It was not possible to generate rich text help for this object.
Please " +"see it in plain text." +msgstr "" +"このオブジェクトに対してリッチテキストのヘルプを生成できませんでした。
プ" +"レーンテキストを参照してください。" + +#: spyder/utils/introspection/manager.py:33 +#: spyder/utils/introspection/manager.py:38 +msgid "Editor's code completion, go-to-definition and help" +msgstr "エディタのコード補完、go-to-definition及びヘルプ" + +#: spyder/utils/iofuncs.py:408 +msgid "Supported files" +msgstr "サポートされているファイル" + +#: spyder/utils/iofuncs.py:410 +msgid "All files (*.*)" +msgstr "すべてのファイル (*.*)" + +#: spyder/utils/iofuncs.py:420 +msgid "Spyder data files" +msgstr "Spyderデータファイル" + +#: spyder/utils/iofuncs.py:422 +#: spyder/widgets/variableexplorer/collectionseditor.py:1021 +msgid "NumPy arrays" +msgstr "NumPy配列" + +#: spyder/utils/iofuncs.py:423 +msgid "NumPy zip arrays" +msgstr "NumPy zip配列" + +#: spyder/utils/iofuncs.py:424 +msgid "Matlab files" +msgstr "Matlabファイル" + +#: spyder/utils/iofuncs.py:425 +msgid "CSV text files" +msgstr "CSVテキストファイル" + +#: spyder/utils/iofuncs.py:427 +msgid "JPEG images" +msgstr "JPEG画像" + +#: spyder/utils/iofuncs.py:428 +msgid "PNG images" +msgstr "PNG画像" + +#: spyder/utils/iofuncs.py:429 +msgid "GIF images" +msgstr "GIF画像" + +#: spyder/utils/iofuncs.py:430 +msgid "TIFF images" +msgstr "TIFF画像" + +#: spyder/utils/iofuncs.py:431 spyder/utils/iofuncs.py:432 +msgid "Pickle files" +msgstr "Pickleファイル" + +#: spyder/utils/iofuncs.py:433 +msgid "JSON files" +msgstr "JSONファイル" + +#: spyder/utils/iofuncs.py:452 spyder/utils/iofuncs.py:459 +msgid "Unsupported file type '%s'" +msgstr "非サポートファイルタイプ '%s'" + +#: spyder/utils/programs.py:286 +msgid "It was not possible to run this file in an external terminal" +msgstr "このファイルを外部ターミナルで実行できませんでした" + +#: spyder/utils/syntaxhighlighters.py:33 +msgid "Syntax highlighting for Matlab, Julia and other file types" +msgstr "Matbal、Julia及びたのファイルタイプの構文強調" + +#: spyder/utils/syntaxhighlighters.py:42 +msgid "Background:" +msgstr "バックグラウンド:" + +#: spyder/utils/syntaxhighlighters.py:43 +#: spyder/widgets/sourcecode/codeeditor.py:106 +msgid "Current line:" +msgstr "現在行:" + +#: spyder/utils/syntaxhighlighters.py:44 +msgid "Current cell:" +msgstr "現在セル:" + +#: spyder/utils/syntaxhighlighters.py:45 +msgid "Occurrence:" +msgstr "事象:" + +#: spyder/utils/syntaxhighlighters.py:46 +msgid "Link:" +msgstr "リンク:" + +#: spyder/utils/syntaxhighlighters.py:47 +msgid "Side areas:" +msgstr "サイドエリア:" + +#: spyder/utils/syntaxhighlighters.py:48 +msgid "Matched
parens:" +msgstr "閉じた
括弧:" + +#: spyder/utils/syntaxhighlighters.py:49 +msgid "Unmatched
parens:" +msgstr "閉じていない
括弧:" + +#: spyder/utils/syntaxhighlighters.py:50 +msgid "Normal text:" +msgstr "通常テキスト:" + +#: spyder/utils/syntaxhighlighters.py:51 +msgid "Keyword:" +msgstr "キーワード:" + +#: spyder/utils/syntaxhighlighters.py:52 +msgid "Builtin:" +msgstr "組み込み:" + +#: spyder/utils/syntaxhighlighters.py:53 +msgid "Definition:" +msgstr "定義:" + +#: spyder/utils/syntaxhighlighters.py:54 +msgid "Comment:" +msgstr "コメント:" + +#: spyder/utils/syntaxhighlighters.py:55 +msgid "String:" +msgstr "文字列:" + +#: spyder/utils/syntaxhighlighters.py:56 +msgid "Number:" +msgstr "数字:" + +#: spyder/utils/syntaxhighlighters.py:57 +msgid "Instance:" +msgstr "インスタンス:" + +#: spyder/widgets/arraybuilder.py:179 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Type an array in Matlab : [1 2;3 4]
\n" +" or Spyder simplified syntax : 1 2;3 4\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two spaces or two tabs to generate a ';'.\n" +" " +msgstr "" +"\n" +" Numpy 配列/行列 ヘルパー
\n" +" 以下の形式で入力 Matlab形式 : [1 2;3 4]
\n" +" あるいはSpyder構文 : 1 2;3 4\n" +"

\n" +" 配列入力には'Enter'を、行列入力には 'Ctrl+Enter'を使う。\n" +"

\n" +" ヒント:
\n" +" 空白2つあるいはタブ2つは';'入力になる。\n" +" " + +#: spyder/widgets/arraybuilder.py:190 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Enter an array in the table.
\n" +" Use Tab to move between cells.\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two tabs at the end of a row to move to the next row.\n" +" " +msgstr "" +"\n" +" Numpy 配列/行列 ヘルパー
\n" +" テーブルに配列を入力してください。
\n" +" セル間の移動にはタブを使います。\n" +"

\n" +" 配列入力には'Enter'を、行列入力には 'Ctrl+Enter'を。\n" +"

\n" +" ヒント:
\n" +" 行末で次の行へ移動するにはタブを2回入力すれば良い。\n" +" " + +#: spyder/widgets/arraybuilder.py:365 +msgid "Array dimensions not valid" +msgstr "配列の次元が不正" + +#: spyder/widgets/browser.py:54 spyder/widgets/sourcecode/codeeditor.py:2559 +msgid "Zoom out" +msgstr "ズームアウト" + +#: spyder/widgets/browser.py:57 spyder/widgets/sourcecode/codeeditor.py:2555 +msgid "Zoom in" +msgstr "ズームイン" + +#: spyder/widgets/browser.py:177 +msgid "Home" +msgstr "ホーム" + +#: spyder/widgets/browser.py:213 +msgid "Find text" +msgstr "テキスト検索" + +#: spyder/widgets/browser.py:231 +msgid "Address:" +msgstr "アドレス:" + +#: spyder/widgets/browser.py:267 +msgid "Unable to load page" +msgstr "ページをロードできません" + +#: spyder/widgets/comboboxes.py:165 +msgid "Press enter to validate this entry" +msgstr "入力を検証するにはエンターキーを押してください" + +#: spyder/widgets/comboboxes.py:166 +msgid "This entry is incorrect" +msgstr "この入力は不正です" + +#: spyder/widgets/comboboxes.py:209 +msgid "Press enter to validate this path" +msgstr "パスを検証するにはエンターキーを押してください" + +#: spyder/widgets/dependencies.py:63 +msgid " Required " +msgstr "必須" + +#: spyder/widgets/dependencies.py:63 +msgid "Module" +msgstr "モジュール" + +#: spyder/widgets/dependencies.py:64 +msgid " Installed " +msgstr "インストール済み" + +#: spyder/widgets/dependencies.py:64 +msgid "Provided features" +msgstr "利用できる機能" + +#: spyder/widgets/dependencies.py:134 +msgid "Dependencies" +msgstr "依存性" + +#: spyder/widgets/dependencies.py:141 +#, fuzzy +msgid "" +"Spyder depends on several Python modules to provide the right functionality " +"for all its panes. The table below shows the required and installed versions " +"(if any) of all of them.

Note: You can safely use Spyder " +"without the following modules installed: %s and %s." +"

Please also note that new dependencies or changed ones will be " +"correctly detected only after Spyder is restarted." +msgstr "" +"全てのペインが正しく機能するために、SpyderはいくつかのPythonモジュールに依存" +"しています。 下記のテーブルには必要なモジュールとインストール済みモジュールの" +"バージョンを示します。

: 以下のモジュールはインストールしなく" +"てもSpyderを安全に使用できます。: %s and %s" + +#: spyder/widgets/dependencies.py:157 +msgid "Copy to clipboard" +msgstr "クリップボードにコピー" + +#: spyder/widgets/editor.py:350 +msgid "Copy path to clipboard" +msgstr "パスをクリップボードにコピー" + +#: spyder/widgets/editor.py:354 +msgid "Close all to the right" +msgstr "全体を右側へ閉じる" + +#: spyder/widgets/editor.py:356 +msgid "Close all but this" +msgstr "他を閉じる" + +#: spyder/widgets/editor.py:959 +msgid "Temporary file" +msgstr "一時ファイル" + +#: spyder/widgets/editor.py:1054 +msgid "New window" +msgstr "新しいウインドウ" + +#: spyder/widgets/editor.py:1055 +msgid "Create a new editor window" +msgstr "新しいエディタウインドウを作成する" + +#: spyder/widgets/editor.py:1058 +msgid "Split vertically" +msgstr "縦に分割する" + +#: spyder/widgets/editor.py:1060 +msgid "Split vertically this editor window" +msgstr "このエディタウインドウを縦に分割する" + +#: spyder/widgets/editor.py:1062 +msgid "Split horizontally" +msgstr "横に分割" + +#: spyder/widgets/editor.py:1064 +msgid "Split horizontally this editor window" +msgstr "このエディタウインドウを横に分割" + +#: spyder/widgets/editor.py:1066 +msgid "Close this panel" +msgstr "このパネルを閉じる" + +#: spyder/widgets/editor.py:1223 +msgid "%s has been modified.
Do you want to save changes?" +msgstr "%s は変更されています。
変更を保存しますか?" + +#: spyder/widgets/editor.py:1285 +msgid "Save" +msgstr "保存" + +#: spyder/widgets/editor.py:1286 +msgid "Unable to save script '%s'

Error message:
%s" +msgstr "スクリプト '%s' を保存できません

エラーメッセージ:
%s" + +#: spyder/widgets/editor.py:1523 +msgid "" +"%s is unavailable (this file may have been removed, moved or renamed " +"outside Spyder).
Do you want to close it?" +msgstr "" +"%s は利用できません (このファイルはSpypder外で削除、移動あるいは名前変" +"更されています)。
閉じますか?" + +#: spyder/widgets/editor.py:1543 +msgid "" +"%s has been modified outside Spyder.
Do you want to reload it and " +"lose all your changes?" +msgstr "" +"%s はSpyderの外で変更されています。
変更を破棄して再読込しますか?" + +#: spyder/widgets/editor.py:1639 +msgid "" +"All changes to %s will be lost.
Do you want to revert file from " +"disk?" +msgstr "" +"%s へのすべての変更は失われます。
ファイルをディスクから復帰させま" +"すか?" + +#: spyder/widgets/editor.py:1779 +msgid "Loading %s..." +msgstr "%s をロードしています..." + +#: spyder/widgets/editor.py:1789 +msgid "" +"%s contains mixed end-of-line characters.
Spyder will fix this " +"automatically." +msgstr "" +"%s は複数種の改行コードを含んでいます。
Spyderは自動的に修正しま" +"す。" + +#: spyder/widgets/editor.py:2171 +msgid "Close window" +msgstr "ウインドウを閉じる" + +#: spyder/widgets/editor.py:2173 +msgid "Close this window" +msgstr "このウインドウを閉じる" + +#: spyder/widgets/editortools.py:94 spyder/widgets/editortools.py:130 +msgid "Line %s" +msgstr " %s 行" + +#: spyder/widgets/editortools.py:99 +msgid "Class defined at line %s" +msgstr "%s 行で定義されているクラス" + +#: spyder/widgets/editortools.py:107 +msgid "Method defined at line %s" +msgstr "%s 行で定義されているメソッド" + +#: spyder/widgets/editortools.py:117 +msgid "Function defined at line %s" +msgstr "%s 行で定義されている関数" + +#: spyder/widgets/editortools.py:149 +msgid "Cell starts at line %s" +msgstr "%s 行から始まるセル" + +#: spyder/widgets/editortools.py:202 spyder/widgets/editortools.py:539 +msgid "Go to cursor position" +msgstr "カーソル位置へ移動" + +#: spyder/widgets/editortools.py:205 +msgid "Show absolute path" +msgstr "絶対パスを表示" + +#: spyder/widgets/editortools.py:208 spyder/widgets/explorer.py:210 +msgid "Show all files" +msgstr "全てのファイルを表示" + +#: spyder/widgets/editortools.py:211 +msgid "Show special comments" +msgstr "特別なコメントを表示" + +#: spyder/widgets/explorer.py:206 +msgid "Edit filename filters..." +msgstr "ファイル名フィルターを編集..." + +#: spyder/widgets/explorer.py:220 +msgid "Edit filename filters" +msgstr "ファイル名フィルターを編集" + +#: spyder/widgets/explorer.py:221 +msgid "Name filters:" +msgstr "フィルター:" + +#: spyder/widgets/explorer.py:240 +msgid "File..." +msgstr "ファイル..." + +#: spyder/widgets/explorer.py:244 +msgid "Module..." +msgstr "モジュール..." + +#: spyder/widgets/explorer.py:248 +msgid "Folder..." +msgstr "フォルダー..." + +#: spyder/widgets/explorer.py:252 +msgid "Package..." +msgstr "パッケージ..." + +#: spyder/widgets/explorer.py:273 +#: spyder/widgets/variableexplorer/collectionseditor.py:652 +msgid "Edit" +msgstr "編集" + +#: spyder/widgets/explorer.py:275 +msgid "Move..." +msgstr "移動..." + +#: spyder/widgets/explorer.py:278 +msgid "Delete..." +msgstr "削除..." + +#: spyder/widgets/explorer.py:281 +msgid "Rename..." +msgstr "リネーム..." + +#: spyder/widgets/explorer.py:284 +msgid "Open" +msgstr "開く" + +#: spyder/widgets/explorer.py:285 spyder/widgets/sourcecode/codeeditor.py:2531 +msgid "Convert to Python script" +msgstr "Pythonスクリプトへ変換" + +#: spyder/widgets/explorer.py:319 +msgid "Commit" +msgstr "コミット" + +#: spyder/widgets/explorer.py:322 +msgid "Browse repository" +msgstr "レポジトリを表示" + +#: spyder/widgets/explorer.py:333 +msgid "Open command prompt here" +msgstr "コマンドプロンプトをここで開く" + +#: spyder/widgets/explorer.py:335 +msgid "Open terminal here" +msgstr "ターミナルをここで開く" + +#: spyder/widgets/explorer.py:340 +msgid "Open Python console here" +msgstr "Pythonコンソールをここで開く" + +#: spyder/widgets/explorer.py:354 +msgid "New" +msgstr "新規" + +#: spyder/widgets/explorer.py:362 +msgid "Import" +msgstr "インポート" + +#: spyder/widgets/explorer.py:513 +msgid "Do you really want to delete %s?" +msgstr "本当に %s を削除しますか?" + +#: spyder/widgets/explorer.py:531 +msgid "delete" +msgstr "削除" + +#: spyder/widgets/explorer.py:532 spyder/widgets/projects/explorer.py:149 +#: spyder/widgets/projects/explorer.py:256 +msgid "Project Explorer" +msgstr "プロジェクトエクスプローラー" + +#: spyder/widgets/explorer.py:533 spyder/widgets/projects/explorer.py:150 +msgid "Unable to %s %s

Error message:
%s" +msgstr "" +"%s が %s に実行できませんでした。

エラーメッセージ:
" +"%s" + +#: spyder/widgets/explorer.py:548 +#, fuzzy +msgid "File Explorer" +msgstr "ファイルエクスプローラー" + +#: spyder/widgets/explorer.py:549 +msgid "" +"The current directory contains a project.

If you want to delete the " +"project, please go to Projects » Delete Project" +msgstr "" + +#: spyder/widgets/explorer.py:566 spyder/widgets/sourcecode/codeeditor.py:2018 +msgid "Conversion error" +msgstr "変換エラー" + +#: spyder/widgets/explorer.py:567 spyder/widgets/sourcecode/codeeditor.py:2019 +msgid "" +"It was not possible to convert this notebook. The error is:\n" +"\n" +msgstr "" +"notebookが変換できませんでした。エラー:\n" +"\n" + +#: spyder/widgets/explorer.py:584 spyder/widgets/explorer.py:592 +#: spyder/widgets/explorer.py:603 +#: spyder/widgets/variableexplorer/collectionseditor.py:681 +#: spyder/widgets/variableexplorer/collectionseditor.py:916 +msgid "Rename" +msgstr "リネーム" + +#: spyder/widgets/explorer.py:585 +msgid "New name:" +msgstr "新しい名前:" + +#: spyder/widgets/explorer.py:593 +msgid "" +"Do you really want to rename %s and overwrite the existing file " +"%s?" +msgstr "" +"本当に %s をリネームし、既存ファイル %s を上書きしますか?" + +#: spyder/widgets/explorer.py:604 +msgid "Unable to rename file %s

Error message:
%s" +msgstr "" +"ファイル %s をリネームできません

エラーメッセージ:
%s" + +#: spyder/widgets/explorer.py:640 +msgid "Unable to move %s

Error message:
%s" +msgstr " %s を移動できません

エラーメッセージ:
%s" + +#: spyder/widgets/explorer.py:658 +msgid "Unable to create folder %s

Error message:
%s" +msgstr "" +"フォルダ %s を作成できません

エラーメッセージ:
%s" + +#: spyder/widgets/explorer.py:671 spyder/widgets/explorer.py:705 +msgid "Unable to create file %s

Error message:
%s" +msgstr "" +"ファイル %s を作成できません

エラーメッセージ:
%s" + +#: spyder/widgets/explorer.py:679 +msgid "New folder" +msgstr "新規フォルダー" + +#: spyder/widgets/explorer.py:680 +msgid "Folder name:" +msgstr "フォルダ名:" + +#: spyder/widgets/explorer.py:685 +msgid "New package" +msgstr "新規パッケージ" + +#: spyder/widgets/explorer.py:686 +msgid "Package name:" +msgstr "パッケージ名:" + +#: spyder/widgets/explorer.py:726 +msgid "New module" +msgstr "新規モジュール" + +#: spyder/widgets/explorer.py:741 +msgid "" +"For %s support, please install one of the
following tools:

%s" +msgstr "" +" %s をサポートするためには以下のツールをインストールしてください。
ツー" +"ル:

%s" + +#: spyder/widgets/explorer.py:745 +msgid "Unable to find external program.

%s" +msgstr "外部プログラムが見つかりません。

%s" + +#: spyder/widgets/explorer.py:966 +msgid "Show current directory only" +msgstr "現在のディレクトリのみを表示" + +#: spyder/widgets/explorer.py:1066 +msgid "You don't have the right permissions to open this directory" +msgstr "このディレクトリを開く権限がありません" + +#: spyder/widgets/explorer.py:1096 +#: spyder/widgets/variableexplorer/importwizard.py:525 +msgid "Previous" +msgstr "前" + +#: spyder/widgets/explorer.py:1102 +msgid "Parent" +msgstr "親" + +#: spyder/widgets/externalshell/baseshell.py:129 +msgid "Run again this program" +msgstr "プログラムを再度実行する" + +#: spyder/widgets/externalshell/baseshell.py:132 +msgid "Kill" +msgstr "殺す" + +#: spyder/widgets/externalshell/baseshell.py:134 +msgid "Kills the current process, causing it to exit immediately" +msgstr "現在のプロセスを殺し、直ちにexit" + +#: spyder/widgets/externalshell/baseshell.py:206 +msgid "Running..." +msgstr "実行中..." + +#: spyder/widgets/externalshell/baseshell.py:213 +msgid "Terminated." +msgstr "終了。" + +#: spyder/widgets/externalshell/baseshell.py:238 +#: spyder/widgets/ipythonconsole/help.py:125 spyder/widgets/mixins.py:661 +msgid "Arguments" +msgstr "引数" + +#: spyder/widgets/externalshell/baseshell.py:239 +msgid "Command line arguments:" +msgstr "コマンドライン引数:" + +#: spyder/widgets/externalshell/pythonshell.py:277 +msgid "Variables" +msgstr "変数" + +#: spyder/widgets/externalshell/pythonshell.py:278 +msgid "Show/hide global variables explorer" +msgstr "変数エクスプローラの表示/隠す" + +#: spyder/widgets/externalshell/pythonshell.py:282 +msgid "Terminate" +msgstr "終了" + +#: spyder/widgets/externalshell/pythonshell.py:283 +msgid "" +"Attempts to stop the process. The process\n" +"may not exit as a result of clicking this\n" +"button (it is given the chance to prompt\n" +"the user for any unsaved files, etc)." +msgstr "" +"プロセスを停止させる。 このボタンを\n" +"押しただけではプロセスはまだ停止\n" +"しません。(未保存のファイルの扱いを\n" +"尋ねる,etc)" + +#: spyder/widgets/externalshell/pythonshell.py:296 +msgid "Interact" +msgstr "相互作用" + +#: spyder/widgets/externalshell/pythonshell.py:298 +msgid "Debug" +msgstr "デバッグ" + +#: spyder/widgets/externalshell/pythonshell.py:300 +#: spyder/widgets/externalshell/pythonshell.py:366 +msgid "Arguments..." +msgstr "引数..." + +#: spyder/widgets/externalshell/pythonshell.py:302 +msgid "Post Mortem Debug" +msgstr "事後デバッグ" + +#: spyder/widgets/externalshell/pythonshell.py:308 +msgid "Working directory" +msgstr "作業ディレクトリ" + +#: spyder/widgets/externalshell/pythonshell.py:310 +msgid "Set current working directory" +msgstr "現在のワーキングディレクトリを設定" + +#: spyder/widgets/externalshell/pythonshell.py:312 +msgid "Environment variables" +msgstr "環境変数" + +#: spyder/widgets/externalshell/pythonshell.py:316 +msgid "Show sys.path contents" +msgstr "sys.pathの中身を表示" + +#: spyder/widgets/externalshell/pythonshell.py:362 +msgid "Arguments: %s" +msgstr "引数: %s" + +#: spyder/widgets/externalshell/pythonshell.py:364 +msgid "No argument" +msgstr "引数なし" + +#: spyder/widgets/externalshell/pythonshell.py:534 +msgid "A Python console failed to start!" +msgstr "Pythonコンソールを開始できませんでした!" + +#: spyder/widgets/externalshell/systemshell.py:106 +msgid "Process failed to start" +msgstr "プロセスを開始できませんでした" + +#: spyder/widgets/fileswitcher.py:109 +msgid "unsaved file" +msgstr "保存されていないファイル" + +#: spyder/widgets/fileswitcher.py:230 +msgid "" +"Press Enter to switch files or Esc to cancel.

Type to " +"filter filenames.

Use :number to go to a line, e.g. " +"main:42
Use @symbol_text to go to a symbol, e." +"g. @init

Press Ctrl+W to close current " +"tab.
" +msgstr "" +"ファイルを切り替えるために Enter を押すか Esc でキャンセルして" +"ください。.

ファイルをフィルターするために入力してください。

行" +"移動には:数字 を使ってください。例: main:42
シ" +"ンボルへの移動は@symbol_text を使ってください。例: @init

現在のタブを閉じるには Ctrl+W を押してください。
" + +#: spyder/widgets/fileswitcher.py:508 +msgid "lines" +msgstr "行" + +#: spyder/widgets/findinfiles.py:158 +msgid "Unexpected error: see internal console" +msgstr "予期しないエラー: 内部コンソールを参照してください" + +#: spyder/widgets/findinfiles.py:209 spyder/widgets/findinfiles.py:233 +#: spyder/widgets/findinfiles.py:280 +msgid "invalid regular expression" +msgstr "無効な正規表現" + +#: spyder/widgets/findinfiles.py:278 +msgid "permission denied errors were encountered" +msgstr "エラーが発生しました(権限がありません)" + +#: spyder/widgets/findinfiles.py:315 +msgid "Search pattern" +msgstr "パターンを検索" + +#: spyder/widgets/findinfiles.py:318 spyder/widgets/findinfiles.py:352 +#: spyder/widgets/findinfiles.py:364 spyder/widgets/findreplace.py:81 +msgid "Regular expression" +msgstr "正規表現" + +#: spyder/widgets/findinfiles.py:327 +msgid "Search" +msgstr "検索" + +#: spyder/widgets/findinfiles.py:330 +msgid "Start search" +msgstr "検索開始" + +#: spyder/widgets/findinfiles.py:336 +msgid "Stop search" +msgstr "検索終了" + +#: spyder/widgets/findinfiles.py:346 +msgid "Included filenames pattern" +msgstr "含まれるファイル名パターン" + +#: spyder/widgets/findinfiles.py:355 +msgid "Include:" +msgstr "含む:" + +#: spyder/widgets/findinfiles.py:358 +msgid "Excluded filenames pattern" +msgstr "除外されるファイル名パターン" + +#: spyder/widgets/findinfiles.py:367 +msgid "Exclude:" +msgstr "除外:" + +#: spyder/widgets/findinfiles.py:377 +msgid "PYTHONPATH" +msgstr "PYTHONPATH" + +#: spyder/widgets/findinfiles.py:379 +msgid "" +"Search in all directories listed in sys.path which are outside the Python " +"installation directory" +msgstr "" +"Pythonインストールディレクトリ外のsys.pathにリストアップされたディレクトリ内" +"を検索" + +#: spyder/widgets/findinfiles.py:382 +msgid "Hg repository" +msgstr "Hgレポジトリ" + +#: spyder/widgets/findinfiles.py:385 +msgid "Search in current directory hg repository" +msgstr "カレントディレクトリのHgレポジトリを検索" + +#: spyder/widgets/findinfiles.py:386 +msgid "Here:" +msgstr "ここ:" + +#: spyder/widgets/findinfiles.py:390 +msgid "Search recursively in this directory" +msgstr "ディレクトリ内を再帰的に検索" + +#: spyder/widgets/findinfiles.py:395 +msgid "Browse a search directory" +msgstr "検索ディレクトリを表示" + +#: spyder/widgets/findinfiles.py:425 +msgid "Hide advanced options" +msgstr "詳細オプションを隠す" + +#: spyder/widgets/findinfiles.py:428 +msgid "Show advanced options" +msgstr "詳細オプションを表示" + +#: spyder/widgets/findinfiles.py:569 +msgid "Search canceled" +msgstr "検索をキャンセル" + +#: spyder/widgets/findinfiles.py:573 +msgid "String not found" +msgstr "文字列が見つかりませんでした" + +#: spyder/widgets/findinfiles.py:575 +msgid "matches in" +msgstr "一致" + +#: spyder/widgets/findinfiles.py:576 +msgid "file" +msgstr "ファイル" + +#: spyder/widgets/findinfiles.py:584 +msgid "interrupted" +msgstr "中断" + +#: spyder/widgets/findreplace.py:63 +msgid "Search string" +msgstr "検索文字列" + +#: spyder/widgets/findreplace.py:87 +msgid "Case Sensitive" +msgstr "大文字/小文字を区別" + +#: spyder/widgets/findreplace.py:93 +msgid "Whole words" +msgstr "単語全体" + +#: spyder/widgets/findreplace.py:99 +msgid "Highlight matches" +msgstr "一致箇所を強調" + +#: spyder/widgets/findreplace.py:113 +msgid "Replace with:" +msgstr "以下で置換:" + +#: spyder/widgets/findreplace.py:115 +msgid "Replace string" +msgstr "置換文字列" + +#: spyder/widgets/findreplace.py:118 +msgid "Replace/find" +msgstr "置換/検索" + +#: spyder/widgets/findreplace.py:125 +msgid "Replace all" +msgstr "全て置換" + +#: spyder/widgets/internalshell.py:262 +msgid "Help..." +msgstr "ヘルプ..." + +#: spyder/widgets/internalshell.py:279 +msgid "Shell special commands:" +msgstr "特別なシェルコマンド:" + +#: spyder/widgets/internalshell.py:280 +msgid "Internal editor:" +msgstr "内部エディタ" + +#: spyder/widgets/internalshell.py:281 +msgid "External editor:" +msgstr "外部エディタ" + +#: spyder/widgets/internalshell.py:282 +msgid "Run script:" +msgstr "スクリプトを実行:" + +#: spyder/widgets/internalshell.py:283 +msgid "Remove references:" +msgstr "参照を削除" + +#: spyder/widgets/internalshell.py:284 +msgid "System commands:" +msgstr "システムコマンド:" + +#: spyder/widgets/internalshell.py:285 +msgid "Python help:" +msgstr "Pythonヘルプ:" + +#: spyder/widgets/internalshell.py:286 +msgid "GUI-based editor:" +msgstr "GUIエディタ:" + +#: spyder/widgets/ipythonconsole/client.py:189 +msgid "An error ocurred while starting the kernel" +msgstr "カーネルを開始中にエラーが発生しました" + +#: spyder/widgets/ipythonconsole/client.py:220 +msgid "Restart kernel" +msgstr "カーネルの再起動" + +#: spyder/widgets/ipythonconsole/client.py:240 +msgid "Stop the current command" +msgstr "現在のコマンドを停止する" + +#: spyder/widgets/ipythonconsole/client.py:263 +msgid "Inspect current object" +msgstr "現在のオブジェクトを調査する" + +#: spyder/widgets/ipythonconsole/client.py:268 +msgid "Clear line or block" +msgstr "ラインやブロックをクリアする" + +#: spyder/widgets/ipythonconsole/client.py:272 +msgid "Reset namespace" +msgstr "名前空間をリセットする" + +#: spyder/widgets/ipythonconsole/client.py:275 +msgid "Clear console" +msgstr "コンソールをクリアする" + +#: spyder/widgets/ipythonconsole/client.py:316 +msgid "Are you sure you want to restart the kernel?" +msgstr "本当にカーネルを再起動しますか?" + +#: spyder/widgets/ipythonconsole/client.py:318 +msgid "Restart kernel?" +msgstr "カーネルを再起動しますか?" + +#: spyder/widgets/ipythonconsole/client.py:327 +#, fuzzy +msgid "Error restarting kernel: %s\n" +msgstr "カーネルを開始中にエラーが発生しました" + +#: spyder/widgets/ipythonconsole/client.py:331 +msgid "" +"
Restarting kernel...\n" +"

" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:336 +msgid "Cannot restart a kernel not started by Spyder\n" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:376 +msgid "Changing backend to Qt for Mayavi" +msgstr "MayaviのバックエンドをQtに変更中" + +#: spyder/widgets/ipythonconsole/client.py:387 +msgid "Connecting to kernel..." +msgstr "カーネルへ接続中..." + +#: spyder/widgets/ipythonconsole/namespacebrowser.py:76 +msgid "" +"Inspecting and setting values while debugging in IPython consoles is not " +"supported yet by Spyder." +msgstr "" + +#: spyder/widgets/ipythonconsole/shell.py:149 +msgid "Reset IPython namespace" +msgstr "IPythonの名前空間をリセット" + +#: spyder/widgets/ipythonconsole/shell.py:150 +msgid "" +"All user-defined variables will be removed.
Are you sure you want to " +"reset the namespace?" +msgstr "" +"全てのユーザー定義変数は削除されます。
本当に名前空間をリセットしますか?" + +#: spyder/widgets/onecolumntree.py:52 +msgid "Collapse all" +msgstr "全て折りたたむ" + +#: spyder/widgets/onecolumntree.py:56 +msgid "Expand all" +msgstr "すべて展開" + +#: spyder/widgets/onecolumntree.py:60 +msgid "Restore" +msgstr "復元" + +#: spyder/widgets/onecolumntree.py:61 +msgid "Restore original tree layout" +msgstr "元のツリーレイアウトを復元" + +#: spyder/widgets/onecolumntree.py:65 +msgid "Collapse selection" +msgstr "現在の選択を折りたたむ" + +#: spyder/widgets/onecolumntree.py:69 +msgid "Expand selection" +msgstr "現在の選択を展開する" + +#: spyder/widgets/pathmanager.py:87 +msgid "Move to top" +msgstr "一番上へ移動" + +#: spyder/widgets/pathmanager.py:93 +msgid "Move up" +msgstr "上へ移動" + +#: spyder/widgets/pathmanager.py:99 +msgid "Move down" +msgstr "下へ移動" + +#: spyder/widgets/pathmanager.py:105 +msgid "Move to bottom" +msgstr "一番下へ移動" + +#: spyder/widgets/pathmanager.py:116 spyder/widgets/pathmanager.py:231 +msgid "Add path" +msgstr "パスを追加" + +#: spyder/widgets/pathmanager.py:121 spyder/widgets/pathmanager.py:214 +msgid "Remove path" +msgstr "パスを削除" + +#: spyder/widgets/pathmanager.py:131 +msgid "Synchronize..." +msgstr "同期..." + +#: spyder/widgets/pathmanager.py:133 +msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" +msgstr "SpyderのパスリストをPYTHONPATH環境変数に同期させる" + +#: spyder/widgets/pathmanager.py:145 +msgid "Synchronize" +msgstr "同期" + +#: spyder/widgets/pathmanager.py:146 +msgid "" +"This will synchronize Spyder's path list with PYTHONPATH environment " +"variable for current user, allowing you to run your Python modules outside " +"Spyder without having to configure sys.path.
Do you want to clear " +"contents of PYTHONPATH before adding Spyder's path list?" +msgstr "" +"sys.pathを変更することなくSpypder外でPythonモジュールを実行できるように、" +"Spyderのパスはユーザーの PYTHONPATH 環境変数に同期されます。 " +"
Spyderのパスリストを追加する前にPYTHONPATHの値をクリアしますか?" + +#: spyder/widgets/pathmanager.py:215 +msgid "Do you really want to remove selected path?" +msgstr "本当に選択したパスを削除しますか?" + +#: spyder/widgets/pathmanager.py:232 +msgid "" +"This directory is already included in Spyder path list.
Do you want to " +"move it to the top of the list?" +msgstr "" +"このディレクトリは既にSpyderのパスリストに含まれています。
リストの一番上" +"にパスを移動させますか?" + +#: spyder/widgets/projects/configdialog.py:30 +#, fuzzy +msgid "Project preferences" +msgstr "レイアウト設定" + +#: spyder/widgets/projects/configdialog.py:82 +#: spyder/widgets/projects/configdialog.py:119 +#, fuzzy +msgid "Restore data on startup" +msgstr "起動時に更新をチェック" + +#: spyder/widgets/projects/configdialog.py:84 +#: spyder/widgets/projects/configdialog.py:121 +#, fuzzy +msgid "Save data on exit" +msgstr "データを保存" + +#: spyder/widgets/projects/configdialog.py:86 +#: spyder/widgets/projects/configdialog.py:123 +#, fuzzy +msgid "Save history" +msgstr "ヒストリログを保存" + +#: spyder/widgets/projects/configdialog.py:88 +#: spyder/widgets/projects/configdialog.py:125 +msgid "Save non project files opened" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:111 +msgid "Code" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:118 +msgid "Workspace" +msgstr "ワークスペース" + +#: spyder/widgets/projects/configdialog.py:148 +#: spyder/widgets/projects/configdialog.py:155 +#, fuzzy +msgid "Version control" +msgstr "デバッグコントロール" + +#: spyder/widgets/projects/configdialog.py:156 +#, fuzzy +msgid "Use version control" +msgstr "デバッグコントロール" + +#: spyder/widgets/projects/configdialog.py:161 +msgid "Version control system" +msgstr "" + +#: spyder/widgets/projects/explorer.py:52 +msgid "Show horizontal scrollbar" +msgstr "水平スクロールバーを表示" + +#: spyder/widgets/projects/explorer.py:114 +msgid "File %s already exists.
Do you want to overwrite it?" +msgstr "ファイル %s は既に存在します。
上書きしますか?" + +#: spyder/widgets/projects/explorer.py:128 +msgid "Folder %s already exists." +msgstr "フォルダ %s は既に存在します。" + +#: spyder/widgets/projects/explorer.py:146 +msgid "copy" +msgstr "コピー" + +#: spyder/widgets/projects/explorer.py:148 +msgid "move" +msgstr "移動" + +#: spyder/widgets/projects/explorer.py:244 +#, fuzzy +msgid "" +"Do you really want to delete {filename}?

Note: This " +"action will only delete the project. Its files are going to be preserved on " +"disk." +msgstr "" +"本当にプロジェクト%sを削除しますか?

注: プロジェクトファイルは" +"ディスク上からは削除されません。" + +#: spyder/widgets/projects/explorer.py:257 +#, fuzzy +msgid "" +"Unable to delete {varpath}

The error message was:" +"
{error}" +msgstr " %s を移動できません

エラーメッセージ:
%s" + +#: spyder/widgets/projects/projectdialog.py:69 +#, fuzzy +msgid "New directory" +msgstr "ディレクトリを選択" + +#: spyder/widgets/projects/projectdialog.py:70 +msgid "Existing directory" +msgstr "存在するディレクトリ" + +#: spyder/widgets/projects/projectdialog.py:72 +#, fuzzy +msgid "Project name" +msgstr "プロジェクト名:" + +#: spyder/widgets/projects/projectdialog.py:73 +#, fuzzy +msgid "Location" +msgstr "情報" + +#: spyder/widgets/projects/projectdialog.py:74 +#, fuzzy +msgid "Project type" +msgstr "プロジェクト名:" + +#: spyder/widgets/projects/projectdialog.py:75 +#, fuzzy +msgid "Python version" +msgstr "Pythonファイル" + +#: spyder/widgets/projects/projectdialog.py:83 +#: spyder/widgets/variableexplorer/importwizard.py:519 +msgid "Cancel" +msgstr "キャンセル" + +#: spyder/widgets/projects/projectdialog.py:84 +msgid "Create" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:102 +#, fuzzy +msgid "Create new project" +msgstr "関係するプロジェクト" + +#: spyder/widgets/projects/type/__init__.py:215 +#, fuzzy +msgid "Empty project" +msgstr "プロジェクトを開く" + +#: spyder/widgets/projects/type/python.py:20 +#, fuzzy +msgid "Python project" +msgstr "プロジェクトを開く" + +#: spyder/widgets/projects/type/python.py:76 +#, fuzzy +msgid "Python package" +msgstr "Pythonパスマネージャ" + +#: spyder/widgets/pydocgui.py:110 +msgid "Module or package:" +msgstr "モジュールあるいはパッケージ:" + +#: spyder/widgets/shell.py:129 +msgid "Save history log..." +msgstr "ヒストリログを保存..." + +#: spyder/widgets/shell.py:131 +msgid "Save current history log (i.e. all inputs and outputs) in a text file" +msgstr "現在のヒストリログ(全ての入出力)をテキストファイルに保存" + +#: spyder/widgets/shell.py:257 +msgid "Save history log" +msgstr "ヒストリログを保存" + +#: spyder/widgets/shell.py:260 +msgid "History logs" +msgstr "ヒストリログ" + +#: spyder/widgets/shell.py:271 +msgid "Unable to save file '%s'

Error message:
%s" +msgstr "ファイル '%s' を保存できません

エラーメッセージ:
%s" + +#: spyder/widgets/shell.py:713 +msgid "Copy without prompts" +msgstr "プロンプトなしでコピー" + +#: spyder/widgets/shell.py:716 spyder/widgets/shell.py:720 +msgid "Clear line" +msgstr "行をクリア" + +#: spyder/widgets/shell.py:722 +msgid "Clear shell" +msgstr "シェルをクリア" + +#: spyder/widgets/shell.py:726 +msgid "Clear shell contents ('cls' command)" +msgstr "シェルの中身をクリア(clsコマンド)" + +#: spyder/widgets/sourcecode/codeeditor.py:100 +msgid "Go to line:" +msgstr "行へ移動:" + +#: spyder/widgets/sourcecode/codeeditor.py:108 +msgid "Line count:" +msgstr "行数:" + +#: spyder/widgets/sourcecode/codeeditor.py:1305 +msgid "Breakpoint" +msgstr "ブレークポイント" + +#: spyder/widgets/sourcecode/codeeditor.py:1306 +msgid "Condition:" +msgstr "条件:" + +#: spyder/widgets/sourcecode/codeeditor.py:1712 +msgid "Code analysis" +msgstr "コード分析" + +#: spyder/widgets/sourcecode/codeeditor.py:1766 +msgid "To do" +msgstr "To do" + +#: spyder/widgets/sourcecode/codeeditor.py:2005 +msgid "Removal error" +msgstr "削除エラー" + +#: spyder/widgets/sourcecode/codeeditor.py:2006 +msgid "" +"It was not possible to remove outputs from this notebook. The error is:\n" +"\n" +msgstr "" +"notebookからoutputを削除できませんでした。エラー:\n" +"\n" + +#: spyder/widgets/sourcecode/codeeditor.py:2528 +msgid "Clear all ouput" +msgstr "全ての出力をクリア" + +#: spyder/widgets/sourcecode/codeeditor.py:2534 +msgid "Go to definition" +msgstr "定義へ移動" + +#: spyder/widgets/sourcecode/codeeditor.py:2563 +msgid "Zoom reset" +msgstr "ズームリセット" + +#: spyder/widgets/status.py:25 +msgid "CPU and memory usage info in the status bar" +msgstr "CPUとメモリの使用状況をステータスバーに表示" + +#: spyder/widgets/status.py:94 +msgid "Memory:" +msgstr "メモリ:" + +#: spyder/widgets/status.py:95 +msgid "" +"Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows " +"platforms" +msgstr "" +"メモリ使用状況の表示には非Windows環境では`psutil` (>=v0.3) が必要になります" + +#: spyder/widgets/status.py:107 +msgid "CPU:" +msgstr "CPU:" + +#: spyder/widgets/status.py:108 +msgid "CPU usage status: requires the `psutil` (>=v0.3) library" +msgstr "" +"CPU使用状況の表示には非Windows環境では`psutil` (>=v0.3) が必要になります" + +#: spyder/widgets/status.py:130 +msgid "Permissions:" +msgstr "権限:" + +#: spyder/widgets/status.py:144 +msgid "End-of-lines:" +msgstr "改行:" + +#: spyder/widgets/status.py:158 +msgid "Encoding:" +msgstr "エンコード" + +#: spyder/widgets/status.py:171 +msgid "Line:" +msgstr "行:" + +#: spyder/widgets/status.py:175 +msgid "Column:" +msgstr "列:" + +#: spyder/widgets/tabs.py:146 +msgid "Browse tabs" +msgstr "tabを表示" + +#: spyder/widgets/tabs.py:275 +msgid "Close current tab" +msgstr "現在のタブを閉じる" + +#: spyder/widgets/variableexplorer/arrayeditor.py:497 +msgid "It was not possible to copy values for this array" +msgstr "配列の値をコピーできませんでした" + +#: spyder/widgets/variableexplorer/arrayeditor.py:532 +#: spyder/widgets/variableexplorer/arrayeditor.py:565 +#: spyder/widgets/variableexplorer/dataframeeditor.py:544 +#: spyder/widgets/variableexplorer/dataframeeditor.py:584 +msgid "Format" +msgstr "フォーマット" + +#: spyder/widgets/variableexplorer/arrayeditor.py:537 +#: spyder/widgets/variableexplorer/dataframeeditor.py:548 +msgid "Resize" +msgstr "リサイズ" + +#: spyder/widgets/variableexplorer/arrayeditor.py:566 +#: spyder/widgets/variableexplorer/dataframeeditor.py:585 +msgid "Float formatting" +msgstr "浮動小数点フォーマット" + +#: spyder/widgets/variableexplorer/arrayeditor.py:574 +#: spyder/widgets/variableexplorer/dataframeeditor.py:594 +msgid "Format (%s) is incorrect" +msgstr "フォーマット (%s) が不正です" + +#: spyder/widgets/variableexplorer/arrayeditor.py:609 +msgid "Array is empty" +msgstr "配列が空です" + +#: spyder/widgets/variableexplorer/arrayeditor.py:612 +msgid "Arrays with more than 3 dimensions are not supported" +msgstr "3次元以上の配列はサポートされていません" + +#: spyder/widgets/variableexplorer/arrayeditor.py:615 +msgid "The 'xlabels' argument length do no match array column number" +msgstr "'xlabels'引数の長さが配列の列数に一致していません" + +#: spyder/widgets/variableexplorer/arrayeditor.py:619 +msgid "The 'ylabels' argument length do no match array row number" +msgstr "'ylabels'引数の長さが配列の行数に一致していません" + +#: spyder/widgets/variableexplorer/arrayeditor.py:626 +msgid "%s arrays" +msgstr "%s 配列" + +#: spyder/widgets/variableexplorer/arrayeditor.py:627 +msgid "%s are currently not supported" +msgstr "%s は現在サポートされていません" + +#: spyder/widgets/variableexplorer/arrayeditor.py:634 +msgid "NumPy array" +msgstr "NumPy配列" + +#: spyder/widgets/variableexplorer/arrayeditor.py:636 +#: spyder/widgets/variableexplorer/arrayeditor.py:790 +msgid "Array editor" +msgstr "配列エディタ" + +#: spyder/widgets/variableexplorer/arrayeditor.py:638 +msgid "read only" +msgstr "読み取り専用" + +#: spyder/widgets/variableexplorer/arrayeditor.py:668 +msgid "Record array fields:" +msgstr "配列のフィールドを記録:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Data" +msgstr "データ" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Mask" +msgstr "マスク" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Masked data" +msgstr "Maskedデータ" + +#: spyder/widgets/variableexplorer/arrayeditor.py:691 +msgid "Axis:" +msgstr "軸:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:696 +msgid "Index:" +msgstr "インデックス:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:709 +msgid "Warning: changes are applied separately" +msgstr "警告: 変更は別々に適用されます" + +#: spyder/widgets/variableexplorer/arrayeditor.py:710 +msgid "" +"For performance reasons, changes applied to masked array won't be reflected " +"in array's data (and vice-versa)." +msgstr "" +"実行性能に関する理由により、masked配列に適用された変更は配列内のデータには反" +"映されません(逆もまた同様)。" + +#: spyder/widgets/variableexplorer/collectionseditor.py:116 +msgid "Index" +msgstr "インデックス" + +#: spyder/widgets/variableexplorer/collectionseditor.py:121 +msgid "Tuple" +msgstr "タプル" + +#: spyder/widgets/variableexplorer/collectionseditor.py:124 +msgid "List" +msgstr "リスト" + +#: spyder/widgets/variableexplorer/collectionseditor.py:127 +msgid "Dictionary" +msgstr "辞書" + +#: spyder/widgets/variableexplorer/collectionseditor.py:129 +msgid "Key" +msgstr "キー" + +#: spyder/widgets/variableexplorer/collectionseditor.py:135 +msgid "Attribute" +msgstr "属性" + +#: spyder/widgets/variableexplorer/collectionseditor.py:137 +msgid "elements" +msgstr "要素" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Size" +msgstr "サイズ" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Type" +msgstr "型" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Value" +msgstr "値" + +#: spyder/widgets/variableexplorer/collectionseditor.py:409 +msgid "" +"Opening this variable can be slow\n" +"\n" +"Do you want to continue anyway?" +msgstr "" +"この変数を開くには時間がかかる場合があります\n" +"\n" +"続行しますか?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:418 +msgid "" +"Spyder was unable to retrieve the value of this variable from the console." +"

The error mesage was:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:583 +msgid "Edit item" +msgstr "アイテムを編集" + +#: spyder/widgets/variableexplorer/collectionseditor.py:584 +msgid "Unable to assign data to item.

Error message:
%s" +msgstr "" +"アイテムにデータを割り当てられません。

エラーメッセージ:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:644 +msgid "Resize rows to contents" +msgstr "行数を中身に応じて変更" + +#: spyder/widgets/variableexplorer/collectionseditor.py:655 +#: spyder/widgets/variableexplorer/collectionseditor.py:990 +#: spyder/widgets/variableexplorer/collectionseditor.py:1007 +msgid "Plot" +msgstr "プロット" + +#: spyder/widgets/variableexplorer/collectionseditor.py:659 +msgid "Histogram" +msgstr "ヒストグラム" + +#: spyder/widgets/variableexplorer/collectionseditor.py:663 +msgid "Show image" +msgstr "画像を表示" + +#: spyder/widgets/variableexplorer/collectionseditor.py:667 +#: spyder/widgets/variableexplorer/collectionseditor.py:1015 +msgid "Save array" +msgstr "配列を保存" + +#: spyder/widgets/variableexplorer/collectionseditor.py:671 +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Insert" +msgstr "挿入" + +#: spyder/widgets/variableexplorer/collectionseditor.py:674 +#: spyder/widgets/variableexplorer/collectionseditor.py:898 +msgid "Remove" +msgstr "除去" + +#: spyder/widgets/variableexplorer/collectionseditor.py:684 +#: spyder/widgets/variableexplorer/collectionseditor.py:919 +msgid "Duplicate" +msgstr "複製" + +#: spyder/widgets/variableexplorer/collectionseditor.py:896 +#, fuzzy +msgid "Do you want to remove the selected item?" +msgstr "選択されたアイテムを除去しますか?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:897 +msgid "Do you want to remove all selected items?" +msgstr "選択された全てのアイテムを除去しますか?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:917 +#, fuzzy +msgid "New variable name:" +msgstr "variable_name" + +#: spyder/widgets/variableexplorer/collectionseditor.py:920 +#, fuzzy +msgid "Variable name:" +msgstr "変数名" + +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +msgid "Key:" +msgstr "キー:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Value:" +msgstr "値:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:978 +msgid "Import error" +msgstr "インポートエラー" + +#: spyder/widgets/variableexplorer/collectionseditor.py:979 +msgid "Please install matplotlib or guiqwt." +msgstr "matplotlibguiqwt をインストールしてください。" + +#: spyder/widgets/variableexplorer/collectionseditor.py:991 +msgid "Unable to plot data.

Error message:
%s" +msgstr "データをプロットできません。

エラーメッセージ:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1008 +msgid "Unable to show image.

Error message:
%s" +msgstr "画像を表示できません。

エラーメッセージ:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1031 +msgid "Unable to save array

Error message:
%s" +msgstr "配列を保存できません

エラーメッセージ:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1056 +msgid "It was not possible to copy this array" +msgstr "配列をコピーできませんでした" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1081 +msgid "Clipboard contents" +msgstr "クリップボードの内容" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1096 +msgid "Import from clipboard" +msgstr "クリップボードからインポート" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1098 +msgid "Empty clipboard" +msgstr "クリップボードを空にする" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1099 +msgid "Nothing to be imported from clipboard." +msgstr "クリップボードからインポートするものはありません。" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To bool" +msgstr "真偽値へ" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To complex" +msgstr "複素数へ" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To float" +msgstr "浮動小数点数へ" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To int" +msgstr "整数へ" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:452 +msgid "To str" +msgstr "文字列へ" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:526 +msgid "%s editor" +msgstr "%s エディタ" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:558 +msgid "Column min/max" +msgstr "列の 最小/最大" + +#: spyder/widgets/variableexplorer/importwizard.py:116 +#: spyder/widgets/variableexplorer/importwizard.py:431 +msgid "Import as" +msgstr "形式を指定してインポート" + +#: spyder/widgets/variableexplorer/importwizard.py:118 +msgid "data" +msgstr "データ" + +#: spyder/widgets/variableexplorer/importwizard.py:122 +msgid "code" +msgstr "コード" + +#: spyder/widgets/variableexplorer/importwizard.py:125 +#: spyder/widgets/variableexplorer/importwizard.py:504 +msgid "text" +msgstr "テキスト" + +#: spyder/widgets/variableexplorer/importwizard.py:138 +msgid "Column separator:" +msgstr "列セパレータ" + +#: spyder/widgets/variableexplorer/importwizard.py:142 +msgid "Tab" +msgstr "タブ" + +#: spyder/widgets/variableexplorer/importwizard.py:145 +#: spyder/widgets/variableexplorer/importwizard.py:163 +msgid "other" +msgstr "その他" + +#: spyder/widgets/variableexplorer/importwizard.py:156 +msgid "Row separator:" +msgstr "行セパレーター" + +#: spyder/widgets/variableexplorer/importwizard.py:160 +msgid "EOL" +msgstr "EOL" + +#: spyder/widgets/variableexplorer/importwizard.py:175 +msgid "Additional options" +msgstr "追加オプション" + +#: spyder/widgets/variableexplorer/importwizard.py:179 +msgid "Skip rows:" +msgstr "行を飛ばす:" + +#: spyder/widgets/variableexplorer/importwizard.py:190 +msgid "Comments:" +msgstr "コメント:" + +#: spyder/widgets/variableexplorer/importwizard.py:196 +msgid "Transpose" +msgstr "転置" + +#: spyder/widgets/variableexplorer/importwizard.py:434 +msgid "array" +msgstr "配列" + +#: spyder/widgets/variableexplorer/importwizard.py:439 +msgid "list" +msgstr "リスト" + +#: spyder/widgets/variableexplorer/importwizard.py:444 +msgid "DataFrame" +msgstr "DataFrame" + +#: spyder/widgets/variableexplorer/importwizard.py:487 +#: spyder/widgets/variableexplorer/importwizard.py:571 +msgid "Import wizard" +msgstr "インポートウィザード" + +#: spyder/widgets/variableexplorer/importwizard.py:492 +msgid "Raw text" +msgstr "Rawテキスト" + +#: spyder/widgets/variableexplorer/importwizard.py:495 +msgid "variable_name" +msgstr "variable_name" + +#: spyder/widgets/variableexplorer/importwizard.py:506 +msgid "table" +msgstr "テーブル" + +#: spyder/widgets/variableexplorer/importwizard.py:507 +msgid "Preview" +msgstr "プレビュー" + +#: spyder/widgets/variableexplorer/importwizard.py:511 +msgid "Variable Name" +msgstr "変数名" + +#: spyder/widgets/variableexplorer/importwizard.py:534 +msgid "Done" +msgstr "完了" + +#: spyder/widgets/variableexplorer/importwizard.py:572 +msgid "" +"Unable to proceed to next step

Please check your entries." +"

Error message:
%s" +msgstr "" +"次のステップを実行できません

入力をチェックしてください。" +"

エラーメッセージ:
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:196 +msgid "Refresh" +msgstr "リフレッシュ" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:200 +msgid "Refresh periodically" +msgstr "定期的にリフレッシュ" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:207 +#: spyder/widgets/variableexplorer/namespacebrowser.py:487 +msgid "Import data" +msgstr "データをインポート" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:210 +#: spyder/widgets/variableexplorer/namespacebrowser.py:565 +#: spyder/widgets/variableexplorer/namespacebrowser.py:584 +msgid "Save data" +msgstr "データを保存" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:215 +msgid "Save data as..." +msgstr "形式を指定してデータ保存" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:227 +msgid "Exclude references which name starts with an underscore" +msgstr "アンダースコアで始まる参照を除外" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:235 +msgid "Exclude references which name is uppercase" +msgstr "名前が大文字の参照を除外" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:242 +msgid "Exclude references which name starts with an uppercase character" +msgstr "大文字で始まる参照を除外" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:250 +msgid "" +"Exclude references to unsupported data types (i.e. which won't be handled/" +"saved correctly)" +msgstr "非サポートデータ型(例:正しくhandle/saveがができない型)への参照を除外 " + +#: spyder/widgets/variableexplorer/namespacebrowser.py:345 +msgid "Object %s is not picklable" +msgstr "オブジェクト %s はpickle化できません" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:507 +msgid "" +"Unsupported file extension '%s'

Would you like to import it " +"anyway (by selecting a known file format)?" +msgstr "" +"サポートされていないファイル拡張 '%s'

(既知のファイルフォーマッ" +"トを選択して)とにかくインポートしますか ?" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:515 +msgid "Open file as:" +msgstr "形式を指定して開く:" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:553 +msgid "Unable to load '%s'

Error message:
%s" +msgstr " '%s' をロードできません

エラーメッセージ:
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:585 +msgid "Unable to save current workspace

Error message:
%s" +msgstr "" +"現在のワークスペースを保存できません

エラーメッセージ:
%s" + +#: spyder/widgets/variableexplorer/texteditor.py:74 +msgid "Text editor" +msgstr "テキストエディタ" + +#: spyder/widgets/variableexplorer/utils.py:29 +msgid "View and edit DataFrames and Series in the Variable Explorer" +msgstr "変数エクスプローラーでのDataFrameとSeriesの表示・編集" + +#: spyder/widgets/variableexplorer/utils.py:34 +msgid "View and edit two and three dimensional arrays in the Variable Explorer" +msgstr "変数エクスプローラーでの2, 3次元配列の表示・編集" + +#: spyder/workers/updates.py:89 spyder/workers/updates.py:91 +msgid "Unable to retrieve information." +msgstr "情報が取得できません" + +#: spyder/workers/updates.py:93 +msgid "" +"Unable to connect to the internet.

Make sure the connection is " +"working properly." +msgstr "" +"インターネットに接続できません。

接続が正しく動作しているか確認してく" +"ださい。" + +#: spyder/workers/updates.py:96 +msgid "Unable to check for updates." +msgstr "更新をチェックできません" + +#~ msgid "Truncate values" +#~ msgstr "値を切り捨て" + +#~ msgid "" +#~ "\n" +#~ "\n" +#~ "{0}" +#~ msgstr "" +#~ "\n" +#~ "\n" +#~ "{0}" + +#~ msgid "Reload last session" +#~ msgstr "前回のセッションをリロードする" + +#~ msgid "Load session..." +#~ msgstr "セッションをロード..." + +#~ msgid "Load Spyder session" +#~ msgstr "Spyderのセッションをロード" + +#, fuzzy +#~ msgid "Save session..." +#~ msgstr "セッションを保存" + +#~ msgid "Save current session and quit application" +#~ msgstr "現在のセッションを保存しアプリケーションを終了" + +#~ msgid "Open session" +#~ msgstr "セッションを開く" + +#~ msgid "Spyder sessions" +#~ msgstr "Spyderセッション" + +#~ msgid "Save session" +#~ msgstr "セッションを保存" + +#~ msgid "Jupyter Qtconsole integration" +#~ msgstr "Jupyter Qtconsole 統合" + +#~ msgid "Python executable" +#~ msgstr "Python実行可能ファイル" + +#~ msgid "Trying to kill a kernel?" +#~ msgstr "カーネルをキルしますか?" + +#~ msgid "" +#~ "You can't close this kernel because it has one or more consoles connected " +#~ "to it.

You need to close them instead or you can kill the kernel " +#~ "using the second button from right to left." +#~ msgstr "" +#~ "接続されているコンソールがあるためカーネルを閉じることができません。" +#~ "

接続済みコンソールを閉じるか、右から2番めのボタンを押してカーネル" +#~ "をキルしてください。" + +#~ msgid "Kernel" +#~ msgstr "カーネル" + +#~ msgid "" +#~ "Either:
  1. Your IPython frontend and kernel versions are " +#~ "incompatible or
  2. You don't have IPython installed in " +#~ "your external interpreter.
In any case, we're sorry but we can't " +#~ "create a console for you." +#~ msgstr "" +#~ "どちらか:
  1. IPythonフロントエンドとカーネルのバージョンは 非互換
  2. 外部インタープリターとしてIPythonがインストールされていま" +#~ "せん
いずれの理由にせよコンソールを作成できません。" + +#~ msgid "Kernel %s" +#~ msgstr "カーネル %s" + +#~ msgid "Open an IPython console" +#~ msgstr "IPythonコンソールを開く" + +#~ msgid "" +#~ "The console monitor was disabled: the IPython kernel will be started as " +#~ "expected, but an IPython console will have to be connected manually to " +#~ "the kernel." +#~ msgstr "" +#~ "コンソールモニターは無効化されています:IPythonカーネルは開始されましたが" +#~ "IPythonコンソールは手動でカーネルに接続する必要が有ります。" + +#~ msgid "" +#~ "UMR excluded modules:\n" +#~ "(example: guidata, guiqwt)" +#~ msgstr "" +#~ "UMRから除外するモジュール:\n" +#~ "(例: guidata, guiqwt)" + +#~ msgid "" +#~ "This feature requires the Matplotlib library.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "この機能にはMatplotlibライブラリが必要です。\n" +#~ "Matplotlibはインストールされていないようです。" + +#~ msgid "" +#~ "This feature requires the Sympy library.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "この機能にはSympyライブラリが必要です。\n" +#~ "インストールされていないようです。" + +#~ msgid "&Font..." +#~ msgstr "フォント(&F)..." + +#~ msgid "Set font style" +#~ msgstr "フォントスタイルを設定" + +#~ msgid "Select a new font" +#~ msgstr "新規フォントを選択" + +#~ msgid "" +#~ "The kernel failed to start!! That's all we know... Please close this " +#~ "console and open a new one." +#~ msgstr "" +#~ "カーネルは開始できませんでした!!これがわかっていることの全てです...この" +#~ "コンソールを閉じて再度新しく開いてください。" + +#~ msgid "" +#~ "It seems the kernel died unexpectedly. Use 'Restart kernel' to continue " +#~ "using this console." +#~ msgstr "" +#~ "カーネルが予期せず停止したようです。このコンソールを続行するには\"カーネル" +#~ "の再起動\"を利用してください。" + +#~ msgid "Kernel process is either remote or unspecified. Cannot interrupt" +#~ msgstr "カーネルプロセスはリモートあるいは詳細不明です。中断できません" + +#~ msgid "Kernel process is either remote or unspecified. Cannot restart." +#~ msgstr "カーネルプロセスはリモートあるいは詳細不明です。再起動できません。" + +#~ msgid "its own configuration file" +#~ msgstr "独自の設定ファイル" + +#~ msgid " and " +#~ msgstr "及び" + +#~ msgid "the following projects:
%s" +#~ msgstr "以下のプロジェクト:
%s" + +#~ msgid "Existing Pydev project" +#~ msgstr "存在するPydevプロジェクト" + +#~ msgid "Close unrelated projects" +#~ msgstr "関係のないプロジェクトを閉じる" + +#~ msgid "Edit related projects" +#~ msgstr "関係するプロジェクトを編集" + +#~ msgid "Add to PYTHONPATH" +#~ msgstr "PYTHONPATHに追加" + +#~ msgid "Remove from PYTHONPATH" +#~ msgstr "PYTHONPATHから削除" + +#~ msgid "Properties" +#~ msgstr "プロパティ" + +#~ msgid "" +#~ "The workspace was unable to load or save %s

Please check if you " +#~ "have the permission to write the associated configuration files." +#~ msgstr "" +#~ "ワークスペースは保存あるいは復元できませんでした。 %s

関係づけられ" +#~ "ている設定ファイルへの書き込み権限があるかチェックしてください。" + +#~ msgid "Import directory" +#~ msgstr "ディレクトリのインポート" + +#~ msgid "" +#~ "The following directory is not in workspace:
%s

Do you " +#~ "want to continue (and copy the directory to workspace)?" +#~ msgstr "" +#~ "以下のディレクトリがワークスーペース内にありません。
%s

(ディレクトリをワークスペースにコピーして)続行しますか?" + +#~ msgid "The project %s is already opened!" +#~ msgstr "プロジェクト%sは既に開いています!" + +#~ msgid "" +#~ "The project root path directory is inside the workspace but not as the " +#~ "expected tree level. It is not a directory of the workspace:
%s" +#~ msgstr "" +#~ "プロジェクトのルートディレクトリはワークスペース内に有りますが想定された階" +#~ "層にありません。 ワークスペース:
%sのディレクトリではありません" + +#~ msgid "A project named %s already exists" +#~ msgstr "%sという名前のプロジェクトは既に存在します" + +#~ msgid "" +#~ "Invalid project name.

Name must match the following regular " +#~ "expression:
%s" +#~ msgstr "" +#~ "無効なプロジェクト名。

プロジェクト名は以下の正規表現に従ってくださ" +#~ "い:
%s" + +#~ msgid "" +#~ "The following directory is not empty:
%s

Do you want to " +#~ "continue?" +#~ msgstr "" +#~ "以下のディレクトリは空ではありません:
%s

続行しますか?" + +#~ msgid "New project" +#~ msgstr "New project_" + +#~ msgid "" +#~ "The current workspace has not been configured yet.\n" +#~ "Do you want to do this now?" +#~ msgstr "" +#~ "現在のワークスペースはまだ設定がされていません。\n" +#~ "すぐに設定しますか?" + +#~ msgid "Import existing project" +#~ msgstr "既存のプロジェクトをインポート" + +#~ msgid "Select projects to import" +#~ msgstr "インポートするプロジェクトを選択" + +#~ msgid "The folder %s does not contain a valid %s project" +#~ msgstr "フォルダー %s は有効な %s プロジェクトを含んでいません" + +#~ msgid "Import existing Pydev project" +#~ msgstr "既存のPydevプロジェクトをインポート" + +#~ msgid "" +#~ "Unable to read Pydev project %s

Error message:
%s" +#~ msgstr "" +#~ "Pydevプロジェクト%sを読み取れません

エラーメッセージ:" +#~ "
%s" + +#~ msgid "Select projects which are related to %s" +#~ msgstr "%sに関連するプロジェクトを選択" + +#~ msgid "" +#~ "Statistics on source files only:
(Python, Cython, IPython, Enaml,C/C+" +#~ "+, Fortran)

%s files.
%s lines of code." +#~ msgstr "" +#~ "ソースファイルのみの統計:
(Python, Cython, IPython, Enaml,C/C++, " +#~ "Fortran)

%s ファイル。
%s 行のコード。" + +#~ msgid "Select an existing workspace directory, or create a new one" +#~ msgstr "既存のワークスペースを選択するか新規に作成する" + +#~ msgid "" +#~ "What is the workspace?

A Spyder workspace is " +#~ "a directory on your filesystem that contains Spyder projects and ." +#~ "spyderworkspace configuration file.

A Spyder project is " +#~ "a directory with source code (and other related files) and a " +#~ "configuration file (named .spyderproject) with project settings " +#~ "(PYTHONPATH, linked projects, ...).
" +#~ msgstr "" +#~ "ワークスペースとは何か?

Spyderワークスペース " +#~ "とはファイルシステム上のSpyderプロジェクトと.spyderworkspace設定" +#~ "ファイルを含むディレクトリである。

Spyderプロジェクト は" +#~ "ソースコード(とその他関連ファイル)や(PYTHONPATHや関連プロジェクト.等の)設" +#~ "定ファイル(.spyderproject)を保存しているディレクトリである。
" + +#~ msgid "This is the current workspace directory" +#~ msgstr "これは現在のワークスペースディレクトリです" + +#~ msgid "" +#~ "The following directory is not a Spyder workspace:
%s

Do you " +#~ "want to create a new workspace in this directory?" +#~ msgstr "" +#~ "以下のディレクトリはSpyderワークスペースではありません:
%s

この" +#~ "ディレクトリに新規ワークスペースを作成しますか?" + +#~ msgid "Unable to retrieve data.

Error message:
%s" +#~ msgstr "データを取得できません。

エラーメッセージ:
%s" + +#~ msgid "Save session and quit..." +#~ msgstr "セッションを保存して終了" + +#~ msgid "Breakpoints" +#~ msgstr "ブレークポイント" + +#~ msgid "Exit" +#~ msgstr "Exit" + +#~ msgid "Exit Debug" +#~ msgstr "デバッグを終了する" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/locale/pt_BR/LC_MESSAGES/spyder.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/locale/pt_BR/LC_MESSAGES/spyder.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder/locale/pt_BR/LC_MESSAGES/spyder.po spyder-3.0.2+dfsg1/spyder/locale/pt_BR/LC_MESSAGES/spyder.po --- spyder-2.3.8+dfsg1/spyder/locale/pt_BR/LC_MESSAGES/spyder.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/locale/pt_BR/LC_MESSAGES/spyder.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,5305 @@ +# -*- coding: utf-8 -*- +# Spyder's brazilian portuguese translation file +# Valter Nazianzeno , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: 3.0POT-Creation-Date: 2016-09-21 11:43-0300\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2016-09-24 07:03-0300\n" +"Last-Translator: Valter Nazianzeno \n" +"Language-Team: pt_BR\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.8.9\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: spyder/app/mainwindow.py:125 +msgid "Initializing..." +msgstr "Iniciando..." + +#: spyder/app/mainwindow.py:241 +msgid "Numpy and Scipy documentation" +msgstr "Documentação do Numpy e Scipy" + +#: spyder/app/mainwindow.py:243 spyder/app/mainwindow.py:1027 +msgid "Matplotlib documentation" +msgstr "Documentação do Matplotlib" + +#: spyder/app/mainwindow.py:246 +msgid "PyQt4 Reference Guide" +msgstr "Guia de referência do PyQt4" + +#: spyder/app/mainwindow.py:249 +msgid "PyQt4 API Reference" +msgstr "Referência da API do PyQt4" + +#: spyder/app/mainwindow.py:251 +msgid "Python(x,y)" +msgstr "Python(x,y)" + +#: spyder/app/mainwindow.py:253 +msgid "WinPython" +msgstr "WinPython" + +#: spyder/app/mainwindow.py:525 +msgid "Close current pane" +msgstr "Fechar painel atual" + +#: spyder/app/mainwindow.py:530 +msgid "Lock panes" +msgstr "Travar painéis" + +#: spyder/app/mainwindow.py:537 +msgid "Use next layout" +msgstr "Usar próximo layout" + +#: spyder/app/mainwindow.py:541 +msgid "Use previous layout" +msgstr "Usar layout anterior" + +#: spyder/app/mainwindow.py:560 spyder/widgets/sourcecode/codeeditor.py:2505 +msgid "Undo" +msgstr "Desfazer" + +#: spyder/app/mainwindow.py:562 spyder/widgets/sourcecode/codeeditor.py:2508 +msgid "Redo" +msgstr "Refazer" + +#: spyder/app/mainwindow.py:564 spyder/widgets/shell.py:121 +#: spyder/widgets/sourcecode/codeeditor.py:2514 +#: spyder/widgets/variableexplorer/arrayeditor.py:453 +#: spyder/widgets/variableexplorer/collectionseditor.py:649 +#: spyder/widgets/variableexplorer/dataframeeditor.py:445 +msgid "Copy" +msgstr "Copiar" + +#: spyder/app/mainwindow.py:566 spyder/widgets/shell.py:117 +#: spyder/widgets/sourcecode/codeeditor.py:2511 +msgid "Cut" +msgstr "Recortar" + +#: spyder/app/mainwindow.py:568 spyder/widgets/shell.py:125 +#: spyder/widgets/sourcecode/codeeditor.py:2517 +#: spyder/widgets/variableexplorer/collectionseditor.py:646 +msgid "Paste" +msgstr "Colar" + +#: spyder/app/mainwindow.py:571 spyder/widgets/shell.py:138 +#: spyder/widgets/sourcecode/codeeditor.py:2520 +msgid "Select All" +msgstr "Selecionar tudo" + +#: spyder/app/mainwindow.py:581 spyder/plugins/editor.py:1353 +msgid "&File" +msgstr "&Arquivo" + +#: spyder/app/mainwindow.py:582 spyder/plugins/editor.py:1345 +msgid "File toolbar" +msgstr "Barra de arquivo" + +#: spyder/app/mainwindow.py:586 spyder/plugins/editor.py:1354 +msgid "&Edit" +msgstr "&Editar" + +#: spyder/app/mainwindow.py:587 spyder/plugins/editor.py:1350 +msgid "Edit toolbar" +msgstr "Barra de edição" + +#: spyder/app/mainwindow.py:591 spyder/plugins/editor.py:1355 +msgid "&Search" +msgstr "&Pesquisar" + +#: spyder/app/mainwindow.py:592 spyder/plugins/editor.py:1346 +msgid "Search toolbar" +msgstr "Barra de pesquisa" + +#: spyder/app/mainwindow.py:596 spyder/plugins/editor.py:1356 +msgid "Sour&ce" +msgstr "Códi&go" + +#: spyder/app/mainwindow.py:597 spyder/plugins/editor.py:1347 +msgid "Source toolbar" +msgstr "Barra de código" + +#: spyder/app/mainwindow.py:601 spyder/plugins/editor.py:780 +#: spyder/plugins/editor.py:1357 +msgid "&Run" +msgstr "&Executar" + +#: spyder/app/mainwindow.py:602 spyder/plugins/editor.py:1348 +msgid "Run toolbar" +msgstr "Barra de execução " + +#: spyder/app/mainwindow.py:606 spyder/plugins/editor.py:739 +msgid "&Debug" +msgstr "&Depurar" + +#: spyder/app/mainwindow.py:607 spyder/plugins/editor.py:1349 +msgid "Debug toolbar" +msgstr "Barra de depuração" + +#: spyder/app/mainwindow.py:611 +msgid "C&onsoles" +msgstr "C&onsoles" + +#: spyder/app/mainwindow.py:614 +msgid "&Projects" +msgstr "&Projetos" + +#: spyder/app/mainwindow.py:617 spyder/plugins/editor.py:1358 +msgid "&Tools" +msgstr "&Ferramentas" + +#: spyder/app/mainwindow.py:620 +msgid "&View" +msgstr "&Ver" + +#: spyder/app/mainwindow.py:623 +msgid "&Help" +msgstr "A&juda" + +#: spyder/app/mainwindow.py:628 +msgid "Welcome to Spyder!" +msgstr "Bem-vindo ao Spyder!" + +#: spyder/app/mainwindow.py:633 +msgid "Pre&ferences" +msgstr "Pre&ferências" + +#: spyder/app/mainwindow.py:640 spyder/widgets/pathmanager.py:49 +msgid "PYTHONPATH manager" +msgstr "Gerenciar PYTHONPATH" + +#: spyder/app/mainwindow.py:643 +msgid "Python Path Manager" +msgstr "Gerenciador de caminhos do Python" + +#: spyder/app/mainwindow.py:646 +msgid "Update module names list" +msgstr "Atualizar a lista de nomes dos módulos" + +#: spyder/app/mainwindow.py:649 +msgid "Refresh list of module names available in PYTHONPATH" +msgstr "Atualizar a lista de nomes dos módulos disponíveis no PYTHONPATH" + +#: spyder/app/mainwindow.py:652 +msgid "Reset Spyder to factory defaults" +msgstr "Resetar configuração padrão do Spyder" + +#: spyder/app/mainwindow.py:657 +msgid "Current user environment variables..." +msgstr "Variáveis de ambiente do usuário atual..." + +#: spyder/app/mainwindow.py:659 +msgid "" +"Show and edit current user environment variables in Windows registry (i.e. " +"for all sessions)" +msgstr "" +"Mostrar e editar as variáveis de\n" +"ambiente do usuário atual no\n" +"registro do Windows (para todas\n" +"as sessões)" + +#: spyder/app/mainwindow.py:668 spyder/app/mainwindow.py:1123 +msgid "External Tools" +msgstr "Ferramentas externas" + +#: spyder/app/mainwindow.py:672 +msgid "Python(x,y) launcher" +msgstr "Lançador do Python(x,y)" + +#: spyder/app/mainwindow.py:679 +msgid "WinPython control panel" +msgstr "Painel de controle WinPython" + +#: spyder/app/mainwindow.py:688 +msgid "Qt Designer" +msgstr "Qt Designer" + +#: spyder/app/mainwindow.py:693 +msgid "Qt Linguist" +msgstr "Qt Linguist" + +#: spyder/app/mainwindow.py:699 +msgid "Qt examples" +msgstr "Exemplos do Qt" + +#: spyder/app/mainwindow.py:720 +msgid "guidata examples" +msgstr "Exemplos do guidata" + +#: spyder/app/mainwindow.py:731 +msgid "guiqwt examples" +msgstr "Exemplos do guiqwt" + +#: spyder/app/mainwindow.py:736 +msgid "Sift" +msgstr "Sift" + +#: spyder/app/mainwindow.py:746 +msgid "ViTables" +msgstr "ViTables" + +#: spyder/app/mainwindow.py:760 +msgid "Fullscreen mode" +msgstr "Modo tela cheia" + +#: spyder/app/mainwindow.py:772 +msgid "Main toolbar" +msgstr "Barra principal" + +#: spyder/app/mainwindow.py:781 +msgid "" +"Spyder Internal Console\n" +"\n" +"This console is used to report application\n" +"internal errors and to inspect Spyder\n" +"internals with the following commands:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Please don't use it to run your code\n" +"\n" +msgstr "" +"Console interno do Spyder!\n" +"\n" +"Este console é usado para reportar erros\n" +"internos do aplicativo e para inspecionar\n" +"as características internas do Spyder com\n" +"os seguintes comandos:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Por favor não execute seu código neste console\n" + +#: spyder/app/mainwindow.py:798 +msgid "Loading help..." +msgstr "Carregando ajuda..." + +#: spyder/app/mainwindow.py:805 +msgid "Loading outline explorer..." +msgstr "Carregando o explorador de código..." + +#: spyder/app/mainwindow.py:813 +msgid "Loading editor..." +msgstr "Carregando editor..." + +#: spyder/app/mainwindow.py:819 spyder/plugins/console.py:134 +#: spyder/widgets/ipythonconsole/client.py:280 +msgid "&Quit" +msgstr "&Sair" + +#: spyder/app/mainwindow.py:821 spyder/plugins/console.py:136 +msgid "Quit" +msgstr "Sair" + +#: spyder/app/mainwindow.py:825 +msgid "&Restart" +msgstr "&Reiniciar" + +#: spyder/app/mainwindow.py:827 +msgid "Restart" +msgstr "Reiniciar" + +#: spyder/app/mainwindow.py:844 +msgid "Loading file explorer..." +msgstr "Carregando explorador de arquivos..." + +#: spyder/app/mainwindow.py:851 +msgid "Loading history plugin..." +msgstr "Carregando histórico..." + +#: spyder/app/mainwindow.py:862 +msgid "Loading online help..." +msgstr "Carregando ajuda online..." + +#: spyder/app/mainwindow.py:867 +msgid "Loading project explorer..." +msgstr "Carregando explorador de projetos..." + +#: spyder/app/mainwindow.py:874 +msgid "Loading external console..." +msgstr "Carregando console externo..." + +#: spyder/app/mainwindow.py:880 +msgid "Loading namespace browser..." +msgstr "Carregando explorador de variáveis..." + +#: spyder/app/mainwindow.py:887 +msgid "Loading IPython console..." +msgstr "Carregando console IPython..." + +#: spyder/app/mainwindow.py:892 +msgid "Setting up main window..." +msgstr "Configurando tela principal..." + +#: spyder/app/mainwindow.py:895 +msgid "Dependencies..." +msgstr "Dependências..." + +#: spyder/app/mainwindow.py:899 +msgid "Report issue..." +msgstr "Reportar problema..." + +#: spyder/app/mainwindow.py:903 +msgid "Spyder support..." +msgstr "Suporte do Spyder..." + +#: spyder/app/mainwindow.py:906 +msgid "Check for updates..." +msgstr "Verificar por atualizações..." + +#: spyder/app/mainwindow.py:929 +msgid "Spyder documentation" +msgstr "Documentação do Spyder" + +#: spyder/app/mainwindow.py:934 +msgid "Spyder tutorial" +msgstr "Tutorial do Spyder" + +#: spyder/app/mainwindow.py:941 +msgid "Interactive tours" +msgstr "Tours interativas" + +#: spyder/app/mainwindow.py:969 +msgid "Python documentation" +msgstr "Documentação do Python" + +#: spyder/app/mainwindow.py:975 spyder/app/mainwindow.py:1019 +msgid "IPython documentation" +msgstr "Documentação do IPython" + +#: spyder/app/mainwindow.py:976 +msgid "Intro to IPython" +msgstr "Introdução ao IPython" + +#: spyder/app/mainwindow.py:978 +msgid "Quick reference" +msgstr "Referência rápida" + +#: spyder/app/mainwindow.py:980 +msgid "Console help" +msgstr "Ajuda do console" + +#: spyder/app/mainwindow.py:1017 +msgid "Python(x,y) documentation folder" +msgstr "Pasta de documentação do Python(x,y)" + +#: spyder/app/mainwindow.py:1021 +msgid "guidata documentation" +msgstr "Documentação do guidata " + +#: spyder/app/mainwindow.py:1024 +msgid "guiqwt documentation" +msgstr "Documentação do guiqwt" + +#: spyder/app/mainwindow.py:1030 +msgid "NumPy documentation" +msgstr "Documentação do NumPy" + +#: spyder/app/mainwindow.py:1032 +msgid "NumPy reference guide" +msgstr "Guia de referência do NumPy" + +#: spyder/app/mainwindow.py:1034 +msgid "NumPy user guide" +msgstr "Guia do usuário do NumPy" + +#: spyder/app/mainwindow.py:1036 +msgid "SciPy documentation" +msgstr "Documentação do SciPy" + +#: spyder/app/mainwindow.py:1043 +msgid "Installed Python modules" +msgstr "Módulos instalados do Python" + +#: spyder/app/mainwindow.py:1047 +msgid "Online documentation" +msgstr "Documentação online" + +#: spyder/app/mainwindow.py:1059 +msgid "Qt documentation" +msgstr "Documentação do Qt" + +#: spyder/app/mainwindow.py:1065 +msgid "About %s..." +msgstr "Sobre o %s..." + +#: spyder/app/mainwindow.py:1089 +msgid "Panes" +msgstr "Painéis" + +#: spyder/app/mainwindow.py:1091 +msgid "Toolbars" +msgstr "Barra de ferramentas" + +#: spyder/app/mainwindow.py:1092 +msgid "Window layouts" +msgstr "Layouts da janela" + +#: spyder/app/mainwindow.py:1101 spyder/app/mainwindow.py:1894 +#: spyder/app/mainwindow.py:1895 +msgid "Show toolbars" +msgstr "Mostrar barra de ferramentas" + +#: spyder/app/mainwindow.py:1116 +msgid "Attached console window (debugging)" +msgstr "Janela do console anexada (depuração)" + +#: spyder/app/mainwindow.py:1295 spyder/plugins/projects.py:244 +#: spyder/widgets/explorer.py:639 spyder/widgets/explorer.py:744 +#: spyder/widgets/externalshell/pythonshell.py:533 +#: spyder/widgets/externalshell/systemshell.py:105 +#: spyder/widgets/variableexplorer/arrayeditor.py:573 +#: spyder/widgets/variableexplorer/collectionseditor.py:417 +#: spyder/widgets/variableexplorer/dataframeeditor.py:593 +msgid "Error" +msgstr "Erro" + +#: spyder/app/mainwindow.py:1296 +msgid "" +"You have missing dependencies!

%s

Please " +"install them to avoid this message.

Note: Spyder could " +"work without some of these dependencies, however to have a smooth experience " +"when using Spyder we strongly recommend you to install all the listed " +"missing dependencies.

Failing to install these dependencies might " +"result in bugs. Please be sure that any found bugs are not the direct result " +"of missing dependencies, prior to reporting a new issue." +msgstr "" +"Você tem dependências faltando!

%s

Por " +"favor instale elas para evitar esta mensagem.

Nota: O " +"Spyder pode funcionar sem algumas dependências, porém para ter uma " +"experiência melhor ao utilizar o software recomendamos fortemente a " +"instalação das dependências listadas que estão faltando.

A não " +"instalação destas dependências pode gerar bugs. Antes de relatar um novo " +"problema certifique-se de que quaisquer bugs encontrados não são o resultado " +"das dependências que estão faltando." + +#: spyder/app/mainwindow.py:1741 +msgid "Spyder Default Layout" +msgstr "Layout padrão do Spyder" + +#: spyder/app/mainwindow.py:1759 +msgid "Save current layout" +msgstr "Salvar layout atual" + +#: spyder/app/mainwindow.py:1763 +msgid "Layout preferences" +msgstr "Preferências do Layout" + +#: spyder/app/mainwindow.py:1767 +msgid "Reset to spyder default" +msgstr "Resetar para o layout padrão do Spyder" + +#: spyder/app/mainwindow.py:1787 spyder/app/mainwindow.py:1809 +#: spyder/app/mainwindow.py:1872 spyder/app/mainwindow.py:2656 +#: spyder/plugins/configdialog.py:1254 spyder/plugins/externalconsole.py:446 +#: spyder/plugins/ipythonconsole.py:119 spyder/plugins/ipythonconsole.py:835 +#: spyder/plugins/maininterpreter.py:167 spyder/plugins/maininterpreter.py:195 +#: spyder/utils/environ.py:100 spyder/utils/environ.py:113 +#: spyder/widgets/variableexplorer/arrayeditor.py:496 +#: spyder/widgets/variableexplorer/collectionseditor.py:408 +#: spyder/widgets/variableexplorer/collectionseditor.py:1055 +msgid "Warning" +msgstr "Aviso" + +#: spyder/app/mainwindow.py:1788 +msgid "" +"Window layout will be reset to default settings: this affects window " +"position, size and dockwidgets.\n" +"Do you want to continue?" +msgstr "" +"O esquema das janelas será reiniciado para as configurações padrões: isso " +"afeta a posição, tamanho e componentes da janela.\n" +"Deseja continuar?" + +#: spyder/app/mainwindow.py:1810 +msgid "" +"Layout %s will be " +"overwritten. Do you want to " +"continue?" +msgstr "" +"Layout %s será " +"sobrescrito. Você deseja " +"Continuar?" + +#: spyder/app/mainwindow.py:1873 +msgid "Quick switch layout #%s has not yet been defined." +msgstr "Troca rápida do layout #%s ainda não foi definida." + +#: spyder/app/mainwindow.py:1891 spyder/app/mainwindow.py:1892 +msgid "Hide toolbars" +msgstr "Esconder barra de ferramentas" + +#: spyder/app/mainwindow.py:2185 spyder/app/mainwindow.py:2186 +msgid "Maximize current pane" +msgstr "Maximizar painel atual" + +#: spyder/app/mainwindow.py:2189 +msgid "Restore current pane" +msgstr "Restaurar painel atual" + +#: spyder/app/mainwindow.py:2190 +msgid "Restore pane to its original size" +msgstr "Restaurar painel ao seu tamanho original" + +#: spyder/app/mainwindow.py:2274 +msgid "About %s" +msgstr "Sobre o %s" + +#: spyder/app/mainwindow.py:2401 spyder/plugins/editor.py:158 +#: spyder/plugins/runconfig.py:322 spyder/plugins/runconfig.py:444 +#: spyder/plugins/runconfig.py:449 spyder/utils/programs.py:285 +#: spyder/widgets/explorer.py:271 spyder/widgets/externalshell/baseshell.py:127 +msgid "Run" +msgstr "Executar" + +#: spyder/app/mainwindow.py:2402 +msgid "Running an external system terminal is not supported on platform %s." +msgstr "Executar um terminal externo não é suportado na plataforma %s." + +#: spyder/app/mainwindow.py:2657 +msgid "" +"Spyder will restart and reset to default settings:

Do you want to " +"continue?" +msgstr "" +"O Spyder será reiniciado e resetado para suas definições padrões: " +"

Deseja continuar?" + +#: spyder/app/mainwindow.py:2754 spyder/widgets/helperwidgets.py:250 +msgid "Spyder updates" +msgstr "Atualizações do Spyder" + +#: spyder/app/mainwindow.py:2755 spyder/plugins/configdialog.py:819 +msgid "Check for updates on startup" +msgstr "Verificar por atualizações na inicialização" + +#: spyder/app/mainwindow.py:2775 +msgid "" +"Spyder %s is available!

Please use your package manager to " +"update Spyder or go to our Releases page to download this " +"new version.

If you are not sure how to proceed to update Spyder " +"please refer to our Installation instructions." +msgstr "" +"Spyder %s está disponível!

Por favor utilize o seu " +"gerenciador de pacotes ou visite a página Releases para " +"baixar a nova versão.

Se você não sabe como atualizar o Spyder " +"visite a página de instruções Installation." + +#: spyder/app/mainwindow.py:2787 +msgid "Spyder is up to date." +msgstr "O Spyder está atualizado." + +#: spyder/app/restart.py:133 +msgid "" +"It was not possible to close the previous Spyder instance.\n" +"Restart aborted." +msgstr "" +"Não foi possível fechar a última instância do Spyder.\n" +"Reiniciamento abortado." + +#: spyder/app/restart.py:135 +msgid "" +"Spyder could not reset to factory defaults.\n" +"Restart aborted." +msgstr "" +"Não é possível resetar o Spyder para suas definições padrões.\n" +"Reiniciamento abortado." + +#: spyder/app/restart.py:137 +msgid "" +"It was not possible to restart Spyder.\n" +"Operation aborted." +msgstr "" +"Não foi possível reiniciar o Spyder.\n" +"Operação abortada." + +#: spyder/app/restart.py:139 +msgid "Spyder exit error" +msgstr "Erro de saída do Spyder" + +#: spyder/app/restart.py:140 +msgid "Spyder reset error" +msgstr "Erro ao resetar" + +#: spyder/app/restart.py:141 +msgid "Spyder restart error" +msgstr "Erro de reiniciamento" + +#: spyder/app/restart.py:165 +msgid "Closing Spyder" +msgstr "Fechando o Spyder" + +#: spyder/app/restart.py:239 +msgid "Resetting Spyder to defaults" +msgstr "Resetando definições padrões do Spyder" + +#: spyder/app/restart.py:271 +msgid "Restarting" +msgstr "Reiniciando" + +#: spyder/app/tour.py:124 +msgid "Welcome to the Introduction tour" +msgstr "Bem-vindo ao tour de introdução" + +#: spyder/app/tour.py:125 +msgid "" +"Spyder is a powerful Interactive Development Environment (or IDE) for " +"the Python programming language.

Here we are going to guide you " +"through its most important features.

Please use the arrow keys or " +"click on the buttons below to move along the tour." +msgstr "" +"Spyder é um poderoso Ambiente de Desenvolvimento Integrado (ou IDE) " +"para a linguagem de programação Python.

Aqui vamos guiar você nas " +"mais importantes características e funcionalidades do Spyder.

Por " +"favor use as teclas de seta ou clique nos botões abaixo para se mover " +"durante a tour." + +#: spyder/app/tour.py:134 +msgid "The Editor" +msgstr "O Editor" + +#: spyder/app/tour.py:135 +msgid "" +"This is the pane where you write Python code before evaluating it. You can " +"get automatic suggestions and completions while writing, by pressing the " +"Tab key next to a given text.

The Editor comes with a line " +"number area (highlighted here in red), where Spyder shows warnings and " +"syntax errors. They can help you to detect potential problems before running " +"the code.

You can also set debug breakpoints in the line number area, " +"by doing a double click next to a non-empty line." +msgstr "" +"Este é o painel aonde você escreve seu código Python antes de testa-lo. " +"Enquanto digita você pode receber sugestões automáticas e completação " +"apertando a tecla Tab depois do texto inserido.

O Editor " +"possui uma área com uma linha numérica (destacada aqui em vermelho), nesta " +"mesma área o Spyder mostra avisos e erros de sintaxe. Isso pode ajudar você " +"a detectar potencial problemas antes de executar o código.

Você " +"também pode definir pontos de interrupção para debugação nesta área." + +#: spyder/app/tour.py:150 +msgid "The IPython console" +msgstr "O console IPython" + +#: spyder/app/tour.py:151 +msgid "" +"This is one of panes where you can run or execute the code you wrote on the " +"Editor. To do it you need to press the F5 key.

This console " +"comes with several useful features that greatly improve your programming " +"workflow (like syntax highlighting and inline plots). If you want to know " +"more about them, please follow this link.

Please " +"click on the button below to run some simple code in this console. This will " +"be useful to show you other important features." +msgstr "" +"Este é um dos painéis aonde você consegue executar o código que você " +"escreveu no Editor. Para fazer isso pressione a tecla F5.

Este " +"console possui diversas funcionalidades úteis que podem improvisar sua " +"experiência programando (como sintaxe colorida). Se você quiser saber mais " +"sobre isso, visite link.

Por favor clique no " +"botão abaixo para executar um simples código neste console. Isso será útil " +"para mostrar para você outras funcionalidades importantes." + +#: spyder/app/tour.py:167 +msgid "The Variable Explorer" +msgstr "O Explorador de Variáveis" + +#: spyder/app/tour.py:168 +msgid "" +"In this pane you can view and edit the variables generated during the " +"execution of a program, or those entered directly in one of Spyder consoles." +"

As you can see, the Variable Explorer is showing the variables " +"generated during the last step of this tour. By doing a double-click on any " +"of them, a new window will be opened, where you can inspect and modify their " +"contents." +msgstr "" +"Neste painel você pode ver e editar as variáveis geradas durante a execução " +"de um programa ou quando digitadas diretamente em um dos consoles do Spyder." +"

Como você pode ver, o Explorador de Variáveis está mostrando as " +"variáveis geradas durante o passo anterior desta tour. Clicando duas vezes " +"em qualquer uma delas uma nova janela será aberta, aonde você poderá " +"inspecionar e modificar seus valores." + +#: spyder/app/tour.py:180 +msgid "The Python console" +msgstr "O Console Python" + +#: spyder/app/tour.py:181 +msgid "" +"You can also run your code on a Python console. These consoles are useful " +"because they let you run a file in a console dedicated only to it.To select " +"this behavior, please press the F6 key.

By pressing the button " +"below and then focusing the Variable Explorer, you will notice that Python " +"consoles are also connected to that pane, and that the Variable Explorer " +"only shows the variables of the currently focused console." +msgstr "" +"Você também pode executar seu código em um console Python. Estes consoles " +"são úteis porque permitem que você execute um arquivo em um console dedicado " +"apenas para ele. Para selecionar este comportamento por favor pressione a " +"tecla F6.

Apertando o botão abaixo e indo no Explorador de " +"Variáveis você irá notar que os consoles Python também estão conectados a " +"este painel e que o Explorador de Variáveis só mostra as variáveis do " +"console atual." + +#: spyder/app/tour.py:195 spyder/plugins/help.py:485 spyder/plugins/help.py:929 +#: spyder/widgets/internalshell.py:270 +msgid "Help" +msgstr "Ajuda" + +#: spyder/app/tour.py:196 +msgid "" +"This pane displays documentation of the functions, classes, methods or " +"modules you are currently using in the Editor or the Consoles.

To use " +"it, you need to press Ctrl+I in front of an object. If that object " +"has some documentation associated with it, it will be displayed here." +msgstr "" +"Este painel mostra a documentação de funções, classes, métodos ou módulos " +"que você está usando no Editor ou nos Consoles.

Para usar esta " +"funcionalidade você deve pressionar Ctrl+I na frente de um objeto. Se " +"este objeto possuir alguma documentação associada a ele, ela será mostrada " +"aqui." + +#: spyder/app/tour.py:206 +msgid "The File Explorer" +msgstr "O Explorador de Arquivos" + +#: spyder/app/tour.py:207 +msgid "" +"This pane lets you navigate through the directories and files present in " +"your computer.

You can also open any of these files with its " +"corresponding application, by doing a double click on it.

There is " +"one exception to this rule: plain-text files will always be opened in the " +"Spyder Editor." +msgstr "" +"Este painel permite você navegar entre os diretórios e arquivos presentes no " +"seu computador.

Você também pode abrir qualquer arquivo com a " +"aplicação correspondente ao mesmo clicando duas vezes nele.

Existe " +"apenas uma exceção para isso: arquivos de texto simples, esses sempre serão " +"abertos no editor do Spyder." + +#: spyder/app/tour.py:217 +msgid "The History Log" +msgstr "O Log do Histórico" + +#: spyder/app/tour.py:218 +msgid "" +"This pane records all commands introduced in the Python and IPython consoles." +msgstr "" +"Este painel grava todos os comandos executados nos consoles do Python e " +"IPython." + +#: spyder/app/tour.py:266 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" + +#: spyder/app/tour.py:270 +msgid "Welcome to Spyder introduction tour" +msgstr "Bem-vindo ao tour de introdução ao Spyder" + +#: spyder/app/tour.py:271 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" + +#: spyder/app/tour.py:276 +msgid "Introduction tour" +msgstr "Tour de introdução" + +#: spyder/app/tour.py:277 +msgid "New features in version 3.0" +msgstr "Novidades na versão 3.0" + +#: spyder/app/tour.py:555 spyder/plugins/ipythonconsole.py:431 +msgid "Run code" +msgstr "Executar código" + +#: spyder/app/tour.py:824 +msgid "Go to step: " +msgstr "Vá para a etapa: " + +#: spyder/config/base.py:249 +msgid "" +"Update LANGUAGE_CODES (inside config/base.py) if a new translation has been " +"added to Spyder" +msgstr "" +"Atualize LANGUAGE_CODES (config/base.py) se um novo idioma for adicionado ao " +"Spyder" + +#: spyder/config/ipython.py:23 +msgid "Integrate the IPython console" +msgstr "Integração com o console IPython" + +#: spyder/config/ipython.py:25 +msgid "Manipulate Jupyter notebooks on the Editor" +msgstr "Permite manipular Jupyter notebooks no Editor" + +#: spyder/config/utils.py:24 +msgid "Python files" +msgstr "Arquivos Python" + +#: spyder/config/utils.py:25 +msgid "Cython/Pyrex files" +msgstr "Arquivos Cython/Pyrex" + +#: spyder/config/utils.py:26 +msgid "C files" +msgstr "Arquivos C" + +#: spyder/config/utils.py:27 +msgid "C++ files" +msgstr "Arquivos C++" + +#: spyder/config/utils.py:28 +msgid "OpenCL files" +msgstr "Arquivos OpenCL" + +#: spyder/config/utils.py:29 +msgid "Fortran files" +msgstr "Arquivos Fortran" + +#: spyder/config/utils.py:30 +msgid "IDL files" +msgstr "Arquivos IDL" + +#: spyder/config/utils.py:31 +msgid "MATLAB files" +msgstr "Arquivos MATLAB" + +#: spyder/config/utils.py:32 +msgid "Julia files" +msgstr "Arquivos Julia" + +#: spyder/config/utils.py:33 +msgid "Yaml files" +msgstr "Arquivos Yaml" + +#: spyder/config/utils.py:34 +msgid "Patch and diff files" +msgstr "Arquivos Patch e diff" + +#: spyder/config/utils.py:35 +msgid "Batch files" +msgstr "Arquivos Batch" + +#: spyder/config/utils.py:36 spyder/utils/iofuncs.py:426 +msgid "Text files" +msgstr "Arquivos de texto" + +#: spyder/config/utils.py:37 +msgid "reStructuredText files" +msgstr "Arquivos de texto reeStruturado" + +#: spyder/config/utils.py:38 +msgid "gettext files" +msgstr "Arquivos gettext " + +#: spyder/config/utils.py:39 +msgid "NSIS files" +msgstr "Arquivos NSIS " + +#: spyder/config/utils.py:40 +msgid "Web page files" +msgstr "Arquivos de Páginas web" + +#: spyder/config/utils.py:41 +msgid "XML files" +msgstr "Arquivos XML" + +#: spyder/config/utils.py:42 +msgid "Javascript files" +msgstr "Arquivos Javascript" + +#: spyder/config/utils.py:43 +msgid "Json files" +msgstr "Arquivos Json" + +#: spyder/config/utils.py:44 +msgid "IPython notebooks" +msgstr "Notebooks do IPython" + +#: spyder/config/utils.py:45 +msgid "Enaml files" +msgstr "Arquivos Enaml" + +#: spyder/config/utils.py:46 +msgid "Configuration files" +msgstr "Arquivos de Configuração" + +#: spyder/config/utils.py:51 spyder/widgets/explorer.py:712 +msgid "All files" +msgstr "Todos os arquivos" + +#: spyder/config/utils.py:121 +msgid "Supported text files" +msgstr "Arquivos de texto suportados" + +#: spyder/plugins/__init__.py:508 spyder/plugins/editor.py:98 +#: spyder/plugins/editor.py:545 spyder/plugins/editor.py:1729 +#: spyder/plugins/help.py:118 spyder/plugins/help.py:385 +#: spyder/widgets/editor.py:371 spyder/widgets/sourcecode/codeeditor.py:97 +#: spyder/widgets/sourcecode/codeeditor.py:3001 +msgid "Editor" +msgstr "Editor" + +#: spyder/plugins/configdialog.py:139 +msgid "Reset to defaults" +msgstr "Redefinir Preferências" + +#: spyder/plugins/configdialog.py:151 +msgid "Preferences" +msgstr "Preferências" + +#: spyder/plugins/configdialog.py:491 +msgid "Invalid directory path" +msgstr "Caminho de diretório inválido" + +#: spyder/plugins/configdialog.py:494 spyder/plugins/configdialog.py:509 +#: spyder/plugins/runconfig.py:177 spyder/plugins/runconfig.py:241 +#: spyder/plugins/workingdirectory.py:291 spyder/widgets/explorer.py:626 +#: spyder/widgets/externalshell/pythonshell.py:616 +#: spyder/widgets/findinfiles.py:504 spyder/widgets/pathmanager.py:224 +#: spyder/widgets/projects/projectdialog.py:155 +msgid "Select directory" +msgstr "Selecionar diretório" + +#: spyder/plugins/configdialog.py:521 +msgid "Invalid file path" +msgstr "Caminho de arquivo inválido " + +#: spyder/plugins/configdialog.py:524 spyder/plugins/configdialog.py:541 +msgid "Select file" +msgstr "Selecionar arquivo" + +#: spyder/plugins/configdialog.py:540 +msgid "All files (*)" +msgstr "Todos os arquivos (*)" + +#: spyder/plugins/configdialog.py:613 spyder/widgets/formlayout.py:215 +msgid "Bold" +msgstr "Negrito" + +#: spyder/plugins/configdialog.py:616 spyder/widgets/formlayout.py:210 +msgid "Italic" +msgstr "Itálico" + +#: spyder/plugins/configdialog.py:670 +msgid "Font: " +msgstr "Fonte:" + +#: spyder/plugins/configdialog.py:676 +msgid "Size: " +msgstr "Tamanho:" + +#: spyder/plugins/configdialog.py:695 +msgid "Font style" +msgstr "Estilo de fonte" + +#: spyder/plugins/configdialog.py:772 +msgid "Spyder needs to restart to change the following setting:" +msgstr "O Spyder precisa ser reiniciado para mudança de tal configuração:" + +#: spyder/plugins/configdialog.py:775 +msgid "Spyder needs to restart to change the following settings:" +msgstr "O Spyder precisa ser reiniciado para mudança de tais configurações:" + +#: spyder/plugins/configdialog.py:777 +msgid "Do you wish to restart now?" +msgstr "Você deseja reiniciar agora?" + +#: spyder/plugins/configdialog.py:783 +msgid "Information" +msgstr "Informação" + +#: spyder/plugins/configdialog.py:797 spyder/plugins/configdialog.py:804 +#: spyder/widgets/projects/configdialog.py:74 +msgid "General" +msgstr "Geral" + +#: spyder/plugins/configdialog.py:807 +msgid "Language" +msgstr "Idioma" + +#: spyder/plugins/configdialog.py:810 +msgid "Use a single instance" +msgstr "Usar uma única instância " + +#: spyder/plugins/configdialog.py:812 +msgid "" +"Set this to open external
Python files in an already running instance " +"(Requires a restart)" +msgstr "" +"Selecione esta opção
para abrir arquivos externos de Python nessa " +"instância atual (Requer que seja reiniciado)" + +#: spyder/plugins/configdialog.py:815 +msgid "Prompt when exiting" +msgstr "Mostrar mensagem de confirmação ao sair" + +#: spyder/plugins/configdialog.py:816 +msgid "Pop up internal console when internal errors appear" +msgstr "Mostrar o terminal interno quando houver erros" + +#: spyder/plugins/configdialog.py:836 spyder/plugins/editor.py:107 +#: spyder/plugins/externalconsole.py:57 spyder/plugins/ipythonconsole.py:273 +#: spyder/widgets/projects/configdialog.py:81 +msgid "Interface" +msgstr "Interface" + +#: spyder/plugins/configdialog.py:844 +msgid "Qt windows style" +msgstr "Tema do Qt" + +#: spyder/plugins/configdialog.py:850 +msgid "Icon theme" +msgstr "Tema dos icones" + +#: spyder/plugins/configdialog.py:854 +msgid "Vertical title bars in panes" +msgstr "Barra de titulo em vertical" + +#: spyder/plugins/configdialog.py:856 +msgid "Vertical tabs in panes" +msgstr "Abas dos painéis na vertical" + +#: spyder/plugins/configdialog.py:858 +msgid "Animated toolbars and panes" +msgstr "Barras e painéis animados" + +#: spyder/plugins/configdialog.py:860 +msgid "Tear off menus" +msgstr "Separar os menus" + +#: spyder/plugins/configdialog.py:861 +msgid "Set this to detach any
menu from the main window" +msgstr "Habilite esta opção
se deseja separar os menus da janela principal" + +#: spyder/plugins/configdialog.py:863 +msgid "Enable high DPI scaling" +msgstr "" + +#: spyder/plugins/configdialog.py:865 +msgid "Set this for high DPI displays" +msgstr "" + +#: spyder/plugins/configdialog.py:866 +msgid "Custom margin for panes:" +msgstr "Margem personalizada para os painéis:" + +#: spyder/plugins/configdialog.py:868 spyder/plugins/editor.py:209 +msgid "pixels" +msgstr "pixels" + +#: spyder/plugins/configdialog.py:897 +msgid "Status bar" +msgstr "Barra de status" + +#: spyder/plugins/configdialog.py:898 +msgid "Show status bar" +msgstr "Mostrar barra de status" + +#: spyder/plugins/configdialog.py:900 +msgid "Show memory usage every" +msgstr "Mostrar uso da memória a cada" + +#: spyder/plugins/configdialog.py:902 spyder/plugins/configdialog.py:911 +#: spyder/plugins/editor.py:133 spyder/plugins/editor.py:261 +#: spyder/plugins/variableexplorer.py:30 +msgid " ms" +msgstr "ms" + +#: spyder/plugins/configdialog.py:909 +msgid "Show CPU usage every" +msgstr "Mostrar uso da CPU a cada" + +#: spyder/plugins/configdialog.py:944 +msgid "Plain text font" +msgstr "Texto simples" + +#: spyder/plugins/configdialog.py:950 +msgid "Rich text font" +msgstr "Texto formatado" + +#: spyder/plugins/configdialog.py:953 +msgid "Fonts" +msgstr "Fontes" + +#: spyder/plugins/configdialog.py:967 +msgid "Appearance" +msgstr "Aparência" + +#: spyder/plugins/configdialog.py:969 spyder/plugins/ipythonconsole.py:564 +msgid "Advanced Settings" +msgstr "Configurações avançadas" + +#: spyder/plugins/configdialog.py:1005 +msgid "Syntax coloring" +msgstr "Sintaxe colorida" + +#: spyder/plugins/configdialog.py:1018 +msgid "" +"Here you can select the color scheme used in the Editor and all other Spyder " +"plugins.

You can also edit the color schemes provided by Spyder or " +"create your own ones by using the options provided below.
" +msgstr "" +"Aqui você pode escolher o esquema de cor usado no Editor e em todos outros " +"plugins do Spyder.

Você pode também editar os esquemas padrões do " +"Spyder ou criar os seus próprios utilizando as opções abaixo.
" + +#: spyder/plugins/configdialog.py:1023 +msgid "Edit selected" +msgstr "Editar" + +#: spyder/plugins/configdialog.py:1024 +msgid "Create new scheme" +msgstr "Criar novo esquema" + +#: spyder/plugins/configdialog.py:1025 spyder/widgets/explorer.py:512 +#: spyder/widgets/projects/explorer.py:243 spyder/widgets/shell.py:134 +msgid "Delete" +msgstr "Deletar" + +#: spyder/plugins/configdialog.py:1028 +msgid "Reset" +msgstr "Restaurar" + +#: spyder/plugins/configdialog.py:1035 +msgid "Scheme:" +msgstr "Esquema:" + +#: spyder/plugins/configdialog.py:1066 +msgid "Manage color schemes" +msgstr "Gerenciar esquemas de cores" + +#: spyder/plugins/configdialog.py:1255 +msgid "Are you sure you want to delete this scheme?" +msgstr "Você tem certeza que deseja deletar esse esquema?" + +#: spyder/plugins/configdialog.py:1372 +msgid "Text" +msgstr "Texto" + +#: spyder/plugins/configdialog.py:1374 +msgid "Highlight" +msgstr "Destaque" + +#: spyder/plugins/configdialog.py:1376 +msgid "Background" +msgstr "Plano de fundo" + +#: spyder/plugins/configdialog.py:1380 +msgid "Scheme name:" +msgstr "Nome do esquema:" + +#: spyder/plugins/configdialog.py:1387 +msgid "Color scheme editor" +msgstr "Edição do esquema de cores" + +#: spyder/plugins/console.py:109 +msgid "Internal console" +msgstr "Console interno" + +#: spyder/plugins/console.py:139 spyder/plugins/externalconsole.py:743 +msgid "&Run..." +msgstr "&Executar..." + +#: spyder/plugins/console.py:141 spyder/plugins/externalconsole.py:744 +msgid "Run a Python script" +msgstr "Executar um script Python" + +#: spyder/plugins/console.py:144 +msgid "Environment variables..." +msgstr "Variáveis de ambiente..." + +#: spyder/plugins/console.py:146 +msgid "Show and edit environment variables (for current session)" +msgstr "Mostrar e editar as variáveis de ambiente (para a sessão atual)" + +#: spyder/plugins/console.py:150 +msgid "Show sys.path contents..." +msgstr "Mostrar conteúdo da sys.path..." + +#: spyder/plugins/console.py:152 +msgid "Show (read-only) sys.path" +msgstr "Mostrar conteúdo da sys.path em modo de leitura" + +#: spyder/plugins/console.py:155 +msgid "Buffer..." +msgstr "Buffer..." + +#: spyder/plugins/console.py:156 spyder/plugins/externalconsole.py:75 +#: spyder/plugins/history.py:43 +msgid "Set maximum line count" +msgstr "Definir número máximo de linhas" + +#: spyder/plugins/console.py:159 +msgid "External editor path..." +msgstr "Caminho de editor externo:" + +#: spyder/plugins/console.py:160 +msgid "Set external editor executable path" +msgstr "Definir caminho de editor externo" + +#: spyder/plugins/console.py:163 spyder/plugins/editor.py:139 +#: spyder/plugins/externalconsole.py:76 spyder/plugins/help.py:157 +#: spyder/plugins/help.py:360 spyder/plugins/history.py:46 +#: spyder/plugins/history.py:157 +msgid "Wrap lines" +msgstr "Ajuste de linha automático" + +#: spyder/plugins/console.py:166 spyder/plugins/editor.py:175 +#: spyder/plugins/externalconsole.py:121 spyder/plugins/ipythonconsole.py:283 +msgid "Display balloon tips" +msgstr "Mostrar sugestões" + +#: spyder/plugins/console.py:170 spyder/plugins/editor.py:169 +#: spyder/plugins/externalconsole.py:115 +msgid "Automatic code completion" +msgstr "Completar código automáticamente" + +#: spyder/plugins/console.py:174 spyder/plugins/editor.py:173 +#: spyder/plugins/externalconsole.py:119 +msgid "Enter key selects completion" +msgstr "A tecla Enter seleciona o resultado a completar" + +#: spyder/plugins/console.py:179 +msgid "Internal console settings" +msgstr "Configurações do console interno" + +#: spyder/plugins/console.py:232 spyder/plugins/externalconsole.py:918 +msgid "Run Python script" +msgstr "Executar script Python" + +#: spyder/plugins/console.py:233 spyder/plugins/externalconsole.py:146 +#: spyder/plugins/externalconsole.py:919 spyder/widgets/explorer.py:727 +msgid "Python scripts" +msgstr "Scripts Python" + +#: spyder/plugins/console.py:278 +msgid "Buffer" +msgstr "Buffer" + +#: spyder/plugins/console.py:279 +msgid "Maximum line count" +msgstr "Número máximo de linhas" + +#: spyder/plugins/console.py:289 +msgid "External editor" +msgstr "Editor externo" + +#: spyder/plugins/console.py:290 +msgid "External editor executable path:" +msgstr "Caminho do executável do editor externo:" + +#: spyder/plugins/editor.py:104 +msgid "Edit template for new modules" +msgstr "Editar template para novos módulos" + +#: spyder/plugins/editor.py:109 +msgid "Sort files according to full path" +msgstr "Ordenar arquivos seguindo seu caminho completo" + +#: spyder/plugins/editor.py:111 +msgid "Show tab bar" +msgstr "Mostrar barra de abas" + +#: spyder/plugins/editor.py:118 spyder/plugins/editor.py:189 +#: spyder/plugins/externalconsole.py:71 spyder/plugins/externalconsole.py:114 +#: spyder/plugins/help.py:156 spyder/plugins/history.py:45 +#: spyder/plugins/ipythonconsole.py:317 +msgid "Source code" +msgstr "Código fonte" + +#: spyder/plugins/editor.py:119 +msgid "Show line numbers" +msgstr "Mostrar número de linhas" + +#: spyder/plugins/editor.py:120 spyder/plugins/editor.py:947 +msgid "Show blank spaces" +msgstr "Mostrar espaços em branco" + +#: spyder/plugins/editor.py:121 +msgid "Show vertical line after" +msgstr "Mostrar uma linha vertical depois de" + +#: spyder/plugins/editor.py:122 +msgid "characters" +msgstr "caracteres" + +#: spyder/plugins/editor.py:127 +msgid "Highlight current line" +msgstr "Realçar linha atual" + +#: spyder/plugins/editor.py:129 +msgid "Highlight current cell" +msgstr "Realçar célula atual" + +#: spyder/plugins/editor.py:131 +msgid "Highlight occurrences after" +msgstr "Realçar ocorrências depois de" + +#: spyder/plugins/editor.py:159 +msgid "Save all files before running script" +msgstr "Salvar tudo antes de executar um script" + +#: spyder/plugins/editor.py:162 +msgid "Run selection" +msgstr "Executar seleção" + +#: spyder/plugins/editor.py:163 +msgid "Maintain focus in the Editor after running cells or selections" +msgstr "Manter o foco no editor depois de executar células ou seleções" + +#: spyder/plugins/editor.py:166 spyder/plugins/externalconsole.py:252 +msgid "Introspection" +msgstr "Introspecção" + +#: spyder/plugins/editor.py:171 spyder/plugins/externalconsole.py:117 +msgid "Case sensitive code completion" +msgstr "Diferenciar entre maiúsculas e minusculas ao completar código" + +#: spyder/plugins/editor.py:176 +msgid "Link to object definition" +msgstr "Link para a definição do objeto" + +#: spyder/plugins/editor.py:178 +msgid "" +"If this option is enabled, clicking on an object\n" +"name (left-click + Ctrl key) will go this object\n" +"definition (if resolved)." +msgstr "" +"Se esta opção estiver ativada, clicando no nome\n" +"de um objeto (clique-esquerdo + Ctrl) o editor irá\n" +"mostrar a definição do mesmo." + +#: spyder/plugins/editor.py:182 +msgid "" +"Warning:
The Python module rope is not installed on this " +"computer: calltips, code completion and go-to-definition features won't be " +"available." +msgstr "" +"Aviso:
O módulo rope não está instalado neste computador: " +"Por tanto a completação de código, balões de dicas e o método ir para função " +"não estão disponíveis." + +#: spyder/plugins/editor.py:190 +msgid "Automatic insertion of parentheses, braces and brackets" +msgstr "Inserção automática de parênteses, chaves e colchetes" + +#: spyder/plugins/editor.py:193 +msgid "Automatic insertion of closing quotes" +msgstr "Inserção automática de aspas" + +#: spyder/plugins/editor.py:195 +msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" +msgstr "Inserção automática de \":\" depois de 'for', 'if', 'def', etc" + +#: spyder/plugins/editor.py:198 +msgid "Automatic indentation after 'else', 'elif', etc." +msgstr "Indentação automática depois do 'else', 'elif', etc." + +#: spyder/plugins/editor.py:200 +msgid "Indentation characters: " +msgstr "Caracteres de indentação:" + +#: spyder/plugins/editor.py:201 +msgid "2 spaces" +msgstr "2 espaços" + +#: spyder/plugins/editor.py:202 +msgid "3 spaces" +msgstr "3 espaços" + +#: spyder/plugins/editor.py:203 +msgid "4 spaces" +msgstr "4 espaços" + +#: spyder/plugins/editor.py:204 +msgid "5 spaces" +msgstr "5 espaços" + +#: spyder/plugins/editor.py:205 +msgid "6 spaces" +msgstr "6 espaços" + +#: spyder/plugins/editor.py:206 +msgid "7 spaces" +msgstr "7 espaços" + +#: spyder/plugins/editor.py:207 +msgid "8 spaces" +msgstr "8 espaços" + +#: spyder/plugins/editor.py:208 +msgid "Tabulations" +msgstr "Tabulações" + +#: spyder/plugins/editor.py:209 +msgid "Tab stop width:" +msgstr "Largura da tabulação:" + +#: spyder/plugins/editor.py:211 +msgid "Tab always indent" +msgstr "Sempre indentar com a tecla Tab" + +#: spyder/plugins/editor.py:213 +msgid "" +"If enabled, pressing Tab will always indent,\n" +"even when the cursor is not at the beginning\n" +"of a line (when this option is enabled, code\n" +"completion may be triggered using the alternate\n" +"shortcut: Ctrl+Space)" +msgstr "" +"Se ativada, pressionando Tab o código\n" +"será indentado, mesmo se o cursor não estiver\n" +"no inicio da linha (se essa opção for ativada a\n" +"completação de código poderá ser usada\n" +"pressionando Ctrl+Espaço)" + +#: spyder/plugins/editor.py:218 +msgid "Intelligent backspace" +msgstr "Tecla de retorno (\"backspace\") inteligente" + +#: spyder/plugins/editor.py:220 +msgid "Automatically remove trailing spaces when saving files" +msgstr "Remover automaticamente espaços em branco ao salvar arquivos" + +#: spyder/plugins/editor.py:224 +msgid "Analysis" +msgstr "Análise" + +#: spyder/plugins/editor.py:226 +msgid "(Refer to the {} page)" +msgstr "(Guia de estilo {})" + +#: spyder/plugins/editor.py:230 +msgid "Real-time code analysis" +msgstr "Análise de código em tempo real" + +#: spyder/plugins/editor.py:232 +msgid "" +"

If enabled, Python source code will be analyzed using pyflakes, lines " +"containing errors or warnings will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore code analysis warnings.

" +msgstr "" +"

Se ativado, o código fonte será analisado utilizando o pyflakes, linhas " +"contendo erros ou avisos serão destacadas.

Nota: add " +"analysis:ignore em um comentário para ignorar avisos da analise de " +"estilo.

" + +#: spyder/plugins/editor.py:240 +msgid "Code analysis requires pyflakes %s+" +msgstr "A análise de código requer pyflakes %s+" + +#: spyder/plugins/editor.py:242 +msgid "Real-time code style analysis" +msgstr "Análise do estilo de código em tempo real" + +#: spyder/plugins/editor.py:244 +msgid "" +"

If enabled, Python source code will be analyzedusing pep8, lines that are " +"not following PEP8 style guide will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore style analysis warnings.

" +msgstr "" +"

Se ativado, o código fonte será analisado utilizando o pep8, linhas que " +"não seguem o guia de estilo PEP8 serão destacadas.

Nota: add " +"analysis:ignore em um comentário para ignorar avisos da analise de " +"estilo.

" + +#: spyder/plugins/editor.py:251 +msgid "Code annotations (TODO, FIXME, XXX, HINT, TIP, @todo)" +msgstr "Anotações (TODO, FIXME, XXX, HINT, TIP, @todo)" + +#: spyder/plugins/editor.py:254 +msgid "Perform analysis when saving file and every" +msgstr "Fazer análise de código ao guardar arquivo e a cada" + +#: spyder/plugins/editor.py:258 +msgid "Perform analysis only when saving file" +msgstr "Fazer análise só quando o arquivo for salvo" + +#: spyder/plugins/editor.py:317 +msgid "End-of-line characters" +msgstr "Caracteres de fim de linha" + +#: spyder/plugins/editor.py:318 +msgid "" +"When opening a text file containing mixed end-of-line characters (this may " +"raise syntax errors in the consoles on Windows platforms), Spyder may fix " +"the file automatically." +msgstr "" +"Quando for aberto um arquivo de texto que contenha vários tipos de " +"caracteres de fim de linha (o qual pode dar erros no Windows). O Spyder pode " +"consertar o arquivo automaticamente." + +#: spyder/plugins/editor.py:324 +msgid "Fix automatically and show warning message box" +msgstr "Corrigir automaticamente e exibir uma mensagem de aviso" + +#: spyder/plugins/editor.py:335 spyder/plugins/externalconsole.py:250 +#: spyder/plugins/ipythonconsole.py:558 spyder/plugins/variableexplorer.py:43 +msgid "Display" +msgstr "Visualização" + +#: spyder/plugins/editor.py:337 +msgid "Code Introspection/Analysis" +msgstr "Análise e introspecção de código" + +#: spyder/plugins/editor.py:340 spyder/plugins/externalconsole.py:253 +msgid "Advanced settings" +msgstr "Configurações avançadas" + +#: spyder/plugins/editor.py:613 spyder/widgets/editortools.py:510 +msgid "Show/hide outline explorer" +msgstr "Mostrar/esconder o explorador de código" + +#: spyder/plugins/editor.py:619 +msgid "Show/hide project explorer" +msgstr "Mostrar/esconder o explorador de projetos" + +#: spyder/plugins/editor.py:627 +msgid "&New file..." +msgstr "&Novo arquivo..." + +#: spyder/plugins/editor.py:628 spyder/plugins/workingdirectory.py:83 +#: spyder/widgets/explorer.py:704 spyder/widgets/explorer.py:711 +msgid "New file" +msgstr "Novo arquivo" + +#: spyder/plugins/editor.py:634 +msgid "&Open..." +msgstr "&Abrir..." + +#: spyder/plugins/editor.py:635 spyder/plugins/editor.py:1778 +#: spyder/plugins/workingdirectory.py:70 +msgid "Open file" +msgstr "Abrir arquivo" + +#: spyder/plugins/editor.py:641 spyder/widgets/editor.py:347 +msgid "File switcher..." +msgstr "Seletor de arquivo..." + +#: spyder/plugins/editor.py:643 +msgid "Fast switch between files" +msgstr "Troca rápida de seleção entre arquivos" + +#: spyder/plugins/editor.py:649 +msgid "&Revert" +msgstr "Resta&urar" + +#: spyder/plugins/editor.py:650 +msgid "Revert file from disk" +msgstr "Reverter arquivo do disco" + +#: spyder/plugins/editor.py:653 +msgid "&Save" +msgstr "&Salvar" + +#: spyder/plugins/editor.py:654 spyder/widgets/editor.py:1306 +msgid "Save file" +msgstr "Salvar arquivo" + +#: spyder/plugins/editor.py:660 +msgid "Sav&e all" +msgstr "Sal&var tudo" + +#: spyder/plugins/editor.py:661 +msgid "Save all files" +msgstr "Salvar todos os arquivos" + +#: spyder/plugins/editor.py:667 +msgid "Save &as..." +msgstr "Salvar c&omo..." + +#: spyder/plugins/editor.py:668 +msgid "Save current file as..." +msgstr "Salvar arquivo atual como..." + +#: spyder/plugins/editor.py:673 spyder/plugins/editor.py:674 +msgid "Print preview..." +msgstr "Pré-visualizar impressão..." + +#: spyder/plugins/editor.py:675 +msgid "&Print..." +msgstr "&Imprimir..." + +#: spyder/plugins/editor.py:676 +msgid "Print current file..." +msgstr "Imprimir o arquivo atual..." + +#: spyder/plugins/editor.py:679 +msgid "&Close" +msgstr "&Fechar" + +#: spyder/plugins/editor.py:680 +msgid "Close current file" +msgstr "Fechar arquivo atual" + +#: spyder/plugins/editor.py:683 +msgid "C&lose all" +msgstr "Fechar &tudo" + +#: spyder/plugins/editor.py:684 +msgid "Close all opened files" +msgstr "Fechar todos os arquivos abertos" + +#: spyder/plugins/editor.py:691 +msgid "&Find text" +msgstr "&Procurar" + +#: spyder/plugins/editor.py:697 +msgid "Find &next" +msgstr "P&rocurar próximo" + +#: spyder/plugins/editor.py:703 +msgid "Find &previous" +msgstr "Procurar &anterior" + +#: spyder/plugins/editor.py:709 +msgid "&Replace text" +msgstr "&Substituir" + +#: spyder/plugins/editor.py:718 +msgid "Set/Clear breakpoint" +msgstr "Adicionar ou limpar um ponto de interrupção" + +#: spyder/plugins/editor.py:725 +msgid "Set/Edit conditional breakpoint" +msgstr "Adicionar ou editar um ponto de interrupção condicional" + +#: spyder/plugins/editor.py:732 +msgid "Clear breakpoints in all files" +msgstr "Limpar pontos de interrupção em todos os arquivos" + +#: spyder/plugins/editor.py:734 +msgid "Debug with winpdb" +msgstr "Depurar com winpdb" + +#: spyder/plugins/editor.py:741 +msgid "Debug file" +msgstr "Depurar arquivo" + +#: spyder/plugins/editor.py:746 +msgid "Step" +msgstr "Executar linha" + +#: spyder/plugins/editor.py:747 +msgid "Run current line" +msgstr "Executar linha selecionada" + +#: spyder/plugins/editor.py:752 +msgid "Continue" +msgstr "Continuar" + +#: spyder/plugins/editor.py:754 +msgid "Continue execution until next breakpoint" +msgstr "Continuar a execução até o próximo ponto de interrupção" + +#: spyder/plugins/editor.py:759 +msgid "Step Into" +msgstr "Ingressar na função/método" + +#: spyder/plugins/editor.py:761 +msgid "Step into function or method of current line" +msgstr "Ingressar na função ou método da linha atual" + +#: spyder/plugins/editor.py:766 +msgid "Step Return" +msgstr "Sair da função/método" + +#: spyder/plugins/editor.py:768 +msgid "Run until current function or method returns" +msgstr "Executar até que a função ou método atual terminem" + +#: spyder/plugins/editor.py:773 spyder/widgets/findinfiles.py:333 +#: spyder/widgets/ipythonconsole/client.py:238 +msgid "Stop" +msgstr "Parar" + +#: spyder/plugins/editor.py:774 +msgid "Stop debugging" +msgstr "Parar depuração" + +#: spyder/plugins/editor.py:781 +msgid "Run file" +msgstr "Executar arquivo" + +#: spyder/plugins/editor.py:786 +msgid "&Configure..." +msgstr "&Configurar..." + +#: spyder/plugins/editor.py:788 spyder/widgets/externalshell/pythonshell.py:304 +msgid "Run settings" +msgstr "Executar configurações" + +#: spyder/plugins/editor.py:794 +msgid "Re-run &last script" +msgstr "Executar &novamente o último script" + +#: spyder/plugins/editor.py:796 +msgid "Run again last file" +msgstr "Executar novamente o mesmo arquivo" + +#: spyder/plugins/editor.py:802 spyder/widgets/sourcecode/codeeditor.py:2548 +msgid "Run &selection or current line" +msgstr "Executar &seleção ou linha atual" + +#: spyder/plugins/editor.py:805 +msgid "Run selection or current line" +msgstr "Executar seleção ou linha atual" + +#: spyder/plugins/editor.py:813 spyder/widgets/sourcecode/codeeditor.py:2540 +msgid "Run cell" +msgstr "Executar célula" + +#: spyder/plugins/editor.py:816 +msgid "" +"Run current cell (Ctrl+Enter)\n" +"[Use #%% to create cells]" +msgstr "" +"Executar a célula atual (Ctrl+Enter)\n" +"[Usar #%% para criar células]" + +#: spyder/plugins/editor.py:822 spyder/widgets/sourcecode/codeeditor.py:2544 +msgid "Run cell and advance" +msgstr "Executar célula e avançar" + +#: spyder/plugins/editor.py:825 +msgid "Run current cell and go to the next one (Shift+Enter)" +msgstr "Executar célula atual e ir para a próxima (Shift+Enter)" + +#: spyder/plugins/editor.py:832 +msgid "Show todo list" +msgstr "Mostrar lista de tarefas" + +#: spyder/plugins/editor.py:833 +msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" +msgstr "" +"Mostrar a lista de comentários dos\n" +"TODO/FIXME/XXX/HINT/TIP/@todo" + +#: spyder/plugins/editor.py:840 +msgid "Show warning/error list" +msgstr "Mostrar lista de erros e avisos" + +#: spyder/plugins/editor.py:841 +msgid "Show code analysis warnings/errors" +msgstr "Mostrar erros e avisos da análise de código" + +#: spyder/plugins/editor.py:847 +msgid "Previous warning/error" +msgstr "Aviso ou erro anterior" + +#: spyder/plugins/editor.py:848 +msgid "Go to previous code analysis warning/error" +msgstr "Ir para linha anterior de aviso ou erro" + +#: spyder/plugins/editor.py:851 +msgid "Next warning/error" +msgstr "Próximo aviso ou erro" + +#: spyder/plugins/editor.py:852 +msgid "Go to next code analysis warning/error" +msgstr "Ir para próxima linha de aviso ou erro" + +#: spyder/plugins/editor.py:856 +msgid "Last edit location" +msgstr "Última posição da edição" + +#: spyder/plugins/editor.py:857 +msgid "Go to last edit location" +msgstr "Ir para posição anterior da edição" + +#: spyder/plugins/editor.py:865 +msgid "Previous cursor position" +msgstr "Posição anterior do cursor" + +#: spyder/plugins/editor.py:866 +msgid "Go to previous cursor position" +msgstr "Ir a posição anterior do cursor" + +#: spyder/plugins/editor.py:874 +msgid "Next cursor position" +msgstr "Próxima posição do cursor" + +#: spyder/plugins/editor.py:875 +msgid "Go to next cursor position" +msgstr "Ir para a próxima posição do cursor" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Comment" +msgstr "Comentar" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Uncomment" +msgstr "Descomentar" + +#: spyder/plugins/editor.py:886 +msgid "Comment current line or selection" +msgstr "Comentar linha ou seleção atual" + +#: spyder/plugins/editor.py:890 +msgid "Add &block comment" +msgstr "Adicionar &bloco de comentário " + +#: spyder/plugins/editor.py:891 +msgid "Add block comment around current line or selection" +msgstr "Adicionar bloco de comentário ao redor da linha ou seleção atual" + +#: spyder/plugins/editor.py:897 +msgid "R&emove block comment" +msgstr "R&emover bloco de comentário" + +#: spyder/plugins/editor.py:898 +msgid "Remove comment block around current line or selection" +msgstr "Remover bloco de comentário ao redor da linha ou seleção atual" + +#: spyder/plugins/editor.py:909 +msgid "Indent" +msgstr "Indentar" + +#: spyder/plugins/editor.py:910 +msgid "Indent current line or selection" +msgstr "Indentar a linha ou seleção atual" + +#: spyder/plugins/editor.py:913 +msgid "Unindent" +msgstr "Desfazer indentação" + +#: spyder/plugins/editor.py:914 +msgid "Unindent current line or selection" +msgstr "Desfazer indentação da linha ou seleção atual" + +#: spyder/plugins/editor.py:918 +msgid "Toggle Uppercase" +msgstr "" + +#: spyder/plugins/editor.py:919 +#, fuzzy +msgid "Change to uppercase current line or selection" +msgstr "Indentar a linha ou seleção atual" + +#: spyder/plugins/editor.py:923 +msgid "Toggle Lowercase" +msgstr "" + +#: spyder/plugins/editor.py:924 +#, fuzzy +msgid "Change to lowercase current line or selection" +msgstr "Indentar a linha ou seleção atual" + +#: spyder/plugins/editor.py:929 +msgid "Carriage return and line feed (Windows)" +msgstr "Carriage return e salto de linha (Windows)" + +#: spyder/plugins/editor.py:932 +msgid "Line feed (UNIX)" +msgstr "Salto de linha (UNIX)" + +#: spyder/plugins/editor.py:935 +msgid "Carriage return (Mac)" +msgstr "Carriage return (Mac)" + +#: spyder/plugins/editor.py:941 +msgid "Convert end-of-line characters" +msgstr "Converter caracteres de fim de linha" + +#: spyder/plugins/editor.py:945 +msgid "Remove trailing spaces" +msgstr "Remover espaços em branco" + +#: spyder/plugins/editor.py:949 +msgid "Fix indentation" +msgstr "Consertar indentação" + +#: spyder/plugins/editor.py:950 +msgid "Replace tab characters by space characters" +msgstr "Substituir caracteres de tabulação por espaços" + +#: spyder/plugins/editor.py:953 +msgid "Go to line..." +msgstr "Ir para linha..." + +#: spyder/plugins/editor.py:961 +msgid "Set console working directory" +msgstr "Definir diretório de trabalho" + +#: spyder/plugins/editor.py:963 +msgid "" +"Set current console (and file explorer) working directory to current script " +"directory" +msgstr "" +"Definir o diretório de trabalho do console (e do explorador de arquivos) " +"para o diretório do script atual." + +#: spyder/plugins/editor.py:968 +msgid "Maximum number of recent files..." +msgstr "Número máximo de arquivos recentes..." + +#: spyder/plugins/editor.py:971 +msgid "Clear recent files list" +msgstr "Limpar lista de arquivos recentes" + +#: spyder/plugins/editor.py:971 spyder/plugins/projects.py:100 +msgid "Clear this list" +msgstr "Limpar esta lista" + +#: spyder/plugins/editor.py:975 +msgid "Open &recent" +msgstr "Abrir &recente" + +#: spyder/plugins/editor.py:1359 +msgid "?" +msgstr "?" + +#: spyder/plugins/editor.py:1586 +msgid "Spyder Editor" +msgstr "Spyder Editor" + +#: spyder/plugins/editor.py:1587 +msgid "This is a temporary script file." +msgstr "Este é um arquivo de script temporário." + +#: spyder/plugins/editor.py:1656 +msgid "untitled" +msgstr "Sem título" + +#: spyder/plugins/editor.py:1730 +msgid "Maximum number of recent files" +msgstr "Número máximo de arquivos recentes" + +#: spyder/plugins/editor.py:1863 +msgid "Printing..." +msgstr "Imprimindo..." + +#: spyder/plugins/explorer.py:53 +msgid "File explorer" +msgstr "Explorador de arquivos" + +#: spyder/plugins/externalconsole.py:46 +msgid "Interactive data plotting in the consoles" +msgstr "Mostra dados interativos no console" + +#: spyder/plugins/externalconsole.py:54 spyder/plugins/externalconsole.py:709 +msgid "Python console" +msgstr "Console Python" + +#: spyder/plugins/externalconsole.py:59 +msgid "One tab per script" +msgstr "Uma aba por script" + +#: spyder/plugins/externalconsole.py:60 +#: spyder/widgets/externalshell/baseshell.py:160 +msgid "Show elapsed time" +msgstr "Mostrar tempo de execução" + +#: spyder/plugins/externalconsole.py:61 spyder/widgets/explorer.py:1094 +msgid "Show icons and text" +msgstr "Mostrar ícones e texto" + +#: spyder/plugins/externalconsole.py:73 +msgid "Buffer: " +msgstr "Buffer:" + +#: spyder/plugins/externalconsole.py:73 spyder/plugins/ipythonconsole.py:319 +msgid " lines" +msgstr "linhas" + +#: spyder/plugins/externalconsole.py:78 +msgid "Merge process standard output/error channels" +msgstr "Combinar os canais de saída/erro do processo" + +#: spyder/plugins/externalconsole.py:80 +msgid "" +"Merging the output channels of the process means that\n" +"the standard error won't be written in red anymore,\n" +"but this has the effect of speeding up display." +msgstr "" +"Combinar os canais de saída do processo significa que\n" +"o erro padrão não será escrito em vermelho, mas isso ajuda a\n" +"melhorar a velocidade em que aparece o texto no console." + +#: spyder/plugins/externalconsole.py:84 +msgid "Colorize standard error channel using ANSI escape codes" +msgstr "Colorir o canal de error padrão utilizando códigos de escape ANSI " + +#: spyder/plugins/externalconsole.py:86 +msgid "" +"This method is the only way to have colorized standard\n" +"error channel when the output channels have been merged." +msgstr "" +"Este método é a única forma de ter cor no canal de erro\n" +"padrão quando os canais de saída forem combinados." + +#: spyder/plugins/externalconsole.py:102 spyder/plugins/ipythonconsole.py:306 +#: spyder/widgets/variableexplorer/arrayeditor.py:540 +#: spyder/widgets/variableexplorer/dataframeeditor.py:552 +msgid "Background color" +msgstr "Cor de fundo" + +#: spyder/plugins/externalconsole.py:103 +msgid "" +"This option will be applied the next time a Python console or a terminal is " +"opened." +msgstr "" +"Esta opção será ativada na próxima vez que um console Python ou um terminal " +"for aberto." + +#: spyder/plugins/externalconsole.py:106 +msgid "Light background (white color)" +msgstr "Fundo claro (cor branca)" + +#: spyder/plugins/externalconsole.py:131 +msgid "PYTHONSTARTUP replacement" +msgstr "Substituto do PYTHONSTARTUP" + +#: spyder/plugins/externalconsole.py:133 +msgid "" +"This option will override the PYTHONSTARTUP environment variable which\n" +"defines the script to be executed during the Python console startup." +msgstr "" +"Esta opção modificará a variável de ambiente PYTHONSTARTUP, a qual\n" +"define o script que é executado durante a inicialização do console\n" +"Python." + +#: spyder/plugins/externalconsole.py:138 +msgid "Default PYTHONSTARTUP script" +msgstr "Script PYTHONSTARTUP padrão" + +#: spyder/plugins/externalconsole.py:142 +msgid "Use the following startup script:" +msgstr "Usar o seguinte script de inicialização:" + +#: spyder/plugins/externalconsole.py:159 +msgid "Monitor" +msgstr "Monitor" + +#: spyder/plugins/externalconsole.py:160 +msgid "" +"The monitor provides introspection features to console: code completion, " +"calltips and variable explorer. Because it relies on several modules, " +"disabling the monitor may be useful to accelerate console startup." +msgstr "" +"O monitor fornece características de introspecção para o console: " +"completador de código, sugestões e o explorador de variáveis. Porque ele " +"depende de vários módulos adicionais, desativando o monitor é possível " +"acelerar a inicialização do console." + +#: spyder/plugins/externalconsole.py:167 +msgid "Enable monitor" +msgstr "Ativar monitor" + +#: spyder/plugins/externalconsole.py:180 +msgid "Default library" +msgstr "Biblioteca padrão" + +#: spyder/plugins/externalconsole.py:185 +msgid "Qt-Python Bindings" +msgstr "Integração Qt-Python" + +#: spyder/plugins/externalconsole.py:187 +msgid "Library:" +msgstr "Biblioteca:" + +#: spyder/plugins/externalconsole.py:189 +msgid "" +"This option will act on
libraries such as Matplotlib, guidata or ETS" +msgstr "" +"Esta opção trará efeitos
em bibliotecas como Matplotlib, guidata ou ETS" + +#: spyder/plugins/externalconsole.py:198 spyder/plugins/ipythonconsole.py:560 +msgid "Graphics" +msgstr "Gráficos" + +#: spyder/plugins/externalconsole.py:199 +msgid "" +"Decide which backend to use to display graphics. If unsure, please select " +"the Automatic backend.

Note: We support a very limited " +"number of backends in our Python consoles. If you prefer to work with a " +"different one, please use an IPython console." +msgstr "" +"Decidir qual saída usar para mostrar gráficos. Se não possui certeza, por " +"favor selecione a saída Automática.

Nota: Suportamos um " +"número muito limitado de saídas nos consoles Python. Se você preferir " +"preferir trabalhar com uma diferente utilize um console IPython." + +#: spyder/plugins/externalconsole.py:208 +msgid "None" +msgstr "Nenhum" + +#: spyder/plugins/externalconsole.py:208 spyder/plugins/ipythonconsole.py:351 +msgid "Automatic" +msgstr "Automático" + +#: spyder/plugins/externalconsole.py:213 spyder/plugins/ipythonconsole.py:373 +msgid "Backend:" +msgstr "Saída:" + +#: spyder/plugins/externalconsole.py:215 spyder/plugins/ipythonconsole.py:375 +msgid "This option will be applied the next time a console is opened." +msgstr "Esta opção será aplicada na próxima vez que um console for aberto." + +#: spyder/plugins/externalconsole.py:226 +msgid "Enthought Tool Suite" +msgstr "Enthought Tool Suite" + +#: spyder/plugins/externalconsole.py:227 +msgid "" +"Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical " +"user interfaces." +msgstr "" +"Enthought Tool Suite (ETS) suporta as bibliotecas gráficas PyQt4 (qt4) e \n" +"wxPython (wx)." + +#: spyder/plugins/externalconsole.py:231 +msgid "ETS_TOOLKIT:" +msgstr "ETS_TOOLKIT:" + +#: spyder/plugins/externalconsole.py:255 +msgid "External modules" +msgstr "Módulos externos" + +#: spyder/plugins/externalconsole.py:447 +msgid "" +"No Python console is currently selected to run %s.

Please " +"select or open a new Python console and try again." +msgstr "" +"Nenhum console Python foi selecionado para executar %s.

Por " +"favor selecione ou abra um novo console Python e tente novamente." + +#: spyder/plugins/externalconsole.py:518 +msgid "" +"%s is already running in a separate process.\n" +"Do you want to kill the process before starting a new one?" +msgstr "" +"%s já está sendo executado em um processo separado.\n" +"Você deseja matar o processo antes de iniciar um novo?" + +#: spyder/plugins/externalconsole.py:647 +msgid "Command Window" +msgstr "Janela de comando" + +#: spyder/plugins/externalconsole.py:649 spyder/plugins/ipythonconsole.py:297 +msgid "Terminal" +msgstr "Terminal" + +#: spyder/plugins/externalconsole.py:731 +msgid "Open a &Python console" +msgstr "Abrir um console &Python" + +#: spyder/plugins/externalconsole.py:735 +msgid "Open &command prompt" +msgstr "Abrir &prompt de comando" + +#: spyder/plugins/externalconsole.py:736 +msgid "Open a Windows command prompt" +msgstr "Abrir o prompt de comando do Windows" + +#: spyder/plugins/externalconsole.py:738 +msgid "Open a &terminal" +msgstr "Abrir um &terminal" + +#: spyder/plugins/externalconsole.py:739 +msgid "Open a terminal window" +msgstr "Abrir uma janela de terminal" + +#: spyder/plugins/findinfiles.py:127 spyder/widgets/findinfiles.py:689 +msgid "Find in files" +msgstr "Procurar em arquivos" + +#: spyder/plugins/findinfiles.py:148 +msgid "&Find in files" +msgstr "Procurar em arqui&vos" + +#: spyder/plugins/findinfiles.py:151 +msgid "Search text in multiple files" +msgstr "Buscar em vários arquivos" + +#: spyder/plugins/help.py:44 +msgid "Show help for objects in the Editor and Consoles in a dedicated pane" +msgstr "Mostra ajuda para objetos do Editor e Consoles em um painel dedicado" + +#: spyder/plugins/help.py:110 +msgid "Automatic connections" +msgstr "Conexões automáticas " + +#: spyder/plugins/help.py:111 +msgid "" +"This pane can automatically show an object's help information after a left " +"parenthesis is written next to it. Below you can decide to which plugin you " +"want to connect it to turn on this feature." +msgstr "" +"Este painel pode mostrar automaticamente a ajuda de um objeto depois de " +"escrever um parênteses no mesmo. A seguir você pode decidir a que painel/" +"componente você deseja conectar para ativar esta característica." + +#: spyder/plugins/help.py:123 +msgid "" +"This feature requires the Rope or Jedi libraries.\n" +"It seems you don't have either installed." +msgstr "" +"Esta funcionalidade requer as bibliotecas Rope ou Jedi.\n" +"Parece que você não possui elas instalada." + +#: spyder/plugins/help.py:126 +msgid "Python Console" +msgstr "Console Python" + +#: spyder/plugins/help.py:128 +msgid "IPython Console" +msgstr "Console IPython" + +#: spyder/plugins/help.py:140 +msgid "Additional features" +msgstr "Funcionalidades adicionais" + +#: spyder/plugins/help.py:141 +msgid "Render mathematical equations" +msgstr "Renderizar equações matemáticas" + +#: spyder/plugins/help.py:147 +msgid "This feature requires Sphinx 1.1 or superior." +msgstr "Esta funcionalidade requer Sphinx 1.1 ou superior." + +#: spyder/plugins/help.py:148 +msgid "Sphinx %s is currently installed." +msgstr "Sphinx %s está instalado atualmente." + +#: spyder/plugins/help.py:309 +msgid "No further documentation available" +msgstr "Nenhuma documentação adicional disponível" + +#: spyder/plugins/help.py:347 +msgid "No documentation available" +msgstr "Nenhuma documentação disponível" + +#: spyder/plugins/help.py:378 +msgid "Source" +msgstr "Origem" + +#: spyder/plugins/help.py:385 spyder/plugins/runconfig.py:186 +#: spyder/plugins/runconfig.py:456 spyder/widgets/externalshell/baseshell.py:94 +#: spyder/widgets/ipythonconsole/client.py:202 +msgid "Console" +msgstr "Console" + +#: spyder/plugins/help.py:393 +#: spyder/widgets/variableexplorer/collectionseditor.py:133 +msgid "Object" +msgstr "Objeto" + +#: spyder/plugins/help.py:407 +msgid "Plain Text" +msgstr "Texto Normal" + +#: spyder/plugins/help.py:411 +msgid "Show Source" +msgstr "Mostrar código fonte" + +#: spyder/plugins/help.py:415 +msgid "Rich Text" +msgstr "Texto Formatado" + +#: spyder/plugins/help.py:425 +msgid "Automatic import" +msgstr "Importar automaticamente" + +#: spyder/plugins/help.py:437 spyder/plugins/history.py:106 +#: spyder/widgets/editor.py:504 spyder/widgets/explorer.py:1105 +#: spyder/widgets/externalshell/baseshell.py:140 +#: spyder/widgets/ipythonconsole/client.py:251 +#: spyder/widgets/variableexplorer/namespacebrowser.py:160 +msgid "Options" +msgstr "Opções" + +#: spyder/plugins/help.py:695 +msgid "" +"Here you can get help of any object by pressing %s in front of it, either on " +"the Editor or the Console.%sHelp can also be shown automatically after " +"writing a left parenthesis next to an object. You can activate this behavior " +"in %s." +msgstr "" +"Neste painel é possível obter a ajuda de qualquer objeto ao pressionar %s " +"estando na frente do mesmo, tanto no Editor quanto no Console.%sEssa ajuda " +"também pode ser mostrada automaticamente depois de escrever um parênteses " +"junto a um objeto. Você pode ativar este comportamento em %s." + +#: spyder/plugins/help.py:701 +msgid "Preferences > Help" +msgstr "Preferências > Ajuda" + +#: spyder/plugins/help.py:708 +msgid "Usage" +msgstr "Uso" + +#: spyder/plugins/help.py:709 +msgid "New to Spyder? Read our" +msgstr "Novo no Spyder? Leia nosso" + +#: spyder/plugins/help.py:710 +msgid "tutorial" +msgstr "tutorial" + +#: spyder/plugins/help.py:717 +msgid "" +"Please consider installing Sphinx to get documentation rendered in rich text." +msgstr "" +"Por favor considere instalar o Sphinx para obter a documentação em texto " +"formatado." + +#: spyder/plugins/help.py:886 +msgid "Lock" +msgstr "Trancar" + +#: spyder/plugins/help.py:886 +msgid "Unlock" +msgstr "Destrancar" + +#: spyder/plugins/help.py:930 +msgid "" +"The following error occured when calling Sphinx %s.
Incompatible " +"Sphinx version or doc string decoding failed.

Error message:
%s" +msgstr "" +"Ocorreu o seguinte erro quando o Sphinx %s tentou ser utilizado. " +"
Isso se deve a uma versão incompatível do Sphinx ou não foi possível ler " +"a documentação solicitada.

Mensagem de erro:
%s" + +#: spyder/plugins/help.py:974 +msgid "No source code available." +msgstr "O código fonte não está disponível" + +#: spyder/plugins/history.py:39 +msgid "Settings" +msgstr "Configurações" + +#: spyder/plugins/history.py:41 +msgid " entries" +msgstr "entradas" + +#: spyder/plugins/history.py:41 +msgid "History depth: " +msgstr "Profundidade do histórico: " + +#: spyder/plugins/history.py:48 +msgid "Scroll automatically to last entry" +msgstr "Ir automaticamente para a última entrada" + +#: spyder/plugins/history.py:126 +msgid "History log" +msgstr "Log do histórico " + +#: spyder/plugins/history.py:153 +msgid "History..." +msgstr "Histórico..." + +#: spyder/plugins/history.py:155 +msgid "Set history maximum entries" +msgstr "Definir número máximo de entradas para armazenar" + +#: spyder/plugins/history.py:260 +msgid "History" +msgstr "Histórico" + +#: spyder/plugins/history.py:261 +msgid "Maximum entries" +msgstr "Número máximo de entradas" + +#: spyder/plugins/ipythonconsole.py:64 +msgid "Symbolic mathematics in the IPython Console" +msgstr "Matemática simbólica no console IPython" + +#: spyder/plugins/ipythonconsole.py:116 +msgid "" +"The authenticity of host %s can't be established. Are you sure you " +"want to continue connecting?" +msgstr "" +"A autenticidade do servidor %s não pode ser estabelecida. Você tem " +"certeza que deseja continuar conectando?" + +#: spyder/plugins/ipythonconsole.py:128 +msgid "The authenticity of the host can't be established" +msgstr "A autenticidade do servidor não pode ser estabelecida" + +#: spyder/plugins/ipythonconsole.py:135 +msgid "Tunnel '%s' failed to start" +msgstr "O túnel '%s' falhou ao ser iniciado" + +#: spyder/plugins/ipythonconsole.py:140 +msgid "Could not connect to remote host" +msgstr "Não foi possível conectar ao servidor remoto" + +#: spyder/plugins/ipythonconsole.py:157 spyder/plugins/ipythonconsole.py:747 +msgid "Connect to an existing kernel" +msgstr "Conectar a um kernel já existente" + +#: spyder/plugins/ipythonconsole.py:159 +msgid "" +"Please enter the connection info of the kernel you want to connect to. For " +"that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for " +"example kernel-3764.json or just 3764)." +msgstr "" +"Por favor introduza a informação de conexão do kernel ao qual deseja se " +"conectar. Para isso você pode selecionar seu arquivo de conexão JSON, usando " +"o botão Selecionar, ou escrever diretamente seu id, em caso de que " +"seja um kernel local (por exemplo, kernel-3764.json ou só 3764)." + +#: spyder/plugins/ipythonconsole.py:170 +msgid "Connection info:" +msgstr "Informação da conexão:" + +#: spyder/plugins/ipythonconsole.py:172 +msgid "Path to connection file or kernel id" +msgstr "Caminho para arquivo de conexão ou id do kernel" + +#: spyder/plugins/ipythonconsole.py:174 spyder/plugins/ipythonconsole.py:191 +msgid "Browse" +msgstr "Navegar" + +#: spyder/plugins/ipythonconsole.py:183 +msgid "This is a remote kernel" +msgstr "Este é um kernel remoto" + +#: spyder/plugins/ipythonconsole.py:187 +msgid "username@hostname:port" +msgstr "usuário@servidor:porta" + +#: spyder/plugins/ipythonconsole.py:190 +msgid "Path to ssh key file" +msgstr "Caminho do arquivo de chave ssh" + +#: spyder/plugins/ipythonconsole.py:199 +msgid "Password or ssh key passphrase" +msgstr "Senha" + +#: spyder/plugins/ipythonconsole.py:203 +msgid "Host name" +msgstr "Nome do host " + +#: spyder/plugins/ipythonconsole.py:204 +msgid "Ssh key" +msgstr "Chave ssh" + +#: spyder/plugins/ipythonconsole.py:205 +msgid "Password" +msgstr "Senha" + +#: spyder/plugins/ipythonconsole.py:234 +msgid "Open connection file" +msgstr "Abrir arquivo de conexão" + +#: spyder/plugins/ipythonconsole.py:239 +msgid "Select ssh key" +msgstr "Selecionar chave ssh" + +#: spyder/plugins/ipythonconsole.py:267 spyder/plugins/ipythonconsole.py:686 +msgid "IPython console" +msgstr "Console IPython" + +#: spyder/plugins/ipythonconsole.py:274 +msgid "Display initial banner" +msgstr "Mostrar o banner inicial" + +#: spyder/plugins/ipythonconsole.py:275 +msgid "" +"This option lets you hide the message shown at\n" +"the top of the console when it's opened." +msgstr "" +"Esta opção permite ocultar a mensagem que \n" +"aparece no topo do console quando se abre\n" +"ele pela primeira vez." + +#: spyder/plugins/ipythonconsole.py:277 +msgid "Use a pager to display additional text inside the console" +msgstr "Usar um paginador para mostrar textos dentro do console" + +#: spyder/plugins/ipythonconsole.py:279 +msgid "" +"Useful if you don't want to fill the console with long help or completion " +"texts.\n" +"Note: Use the Q key to get out of the pager." +msgstr "" +"Isso é útil se não deseja preencher o console com grandes textos de ajuda.\n" +"Nota: Use a tecla Q para sair do paginador." + +#: spyder/plugins/ipythonconsole.py:284 +msgid "Ask for confirmation before closing" +msgstr "Mostrar janela de confirmação antes de fechar" + +#: spyder/plugins/ipythonconsole.py:294 +msgid "Completion Type" +msgstr "Tipo de completação" + +#: spyder/plugins/ipythonconsole.py:295 +msgid "Decide what type of completion to use" +msgstr "Decidir qual tipo de completação usar" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Graphical" +msgstr "Gráfico" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Plain" +msgstr "Simples" + +#: spyder/plugins/ipythonconsole.py:298 +msgid "Completion:" +msgstr "Completação:" + +#: spyder/plugins/ipythonconsole.py:307 +msgid "Light background" +msgstr "Fundo claro" + +#: spyder/plugins/ipythonconsole.py:309 +msgid "Dark background" +msgstr "Fundo escuro" + +#: spyder/plugins/ipythonconsole.py:319 +msgid "Buffer: " +msgstr "Buffer: " + +#: spyder/plugins/ipythonconsole.py:321 +msgid "" +"Set the maximum number of lines of text shown in the\n" +"console before truncation. Specifying -1 disables it\n" +"(not recommended!)" +msgstr "" +"Define o número máximo de linhas que serão mostradas\n" +"no console em qualquer momento. Especificando -1 serão\n" +"mostradas todas as linhas (não é recomendável!)" + +#: spyder/plugins/ipythonconsole.py:330 +msgid "Support for graphics (Matplotlib)" +msgstr "Suporte para criação de gráficos (Matplotlib)" + +#: spyder/plugins/ipythonconsole.py:331 +msgid "Activate support" +msgstr "Ativar suporte" + +#: spyder/plugins/ipythonconsole.py:332 +msgid "Automatically load Pylab and NumPy modules" +msgstr "Carregar automaticamente os módulos do Pylab e do NumPy" + +#: spyder/plugins/ipythonconsole.py:335 +msgid "" +"This lets you load graphics support without importing \n" +"the commands to do plots. Useful to work with other\n" +"plotting libraries different to Matplotlib or to develop \n" +"GUIs with Spyder." +msgstr "" +"Isso lhe permite carregar o suporte gráfico sem importar\n" +"os comandos para criar imagens. É útil para trabalhar com\n" +"outras bibliotecas gráficas diferentes da Matplotlib ou para\n" +"desenvolver interfaces gráficas com o Spyder." + +#: spyder/plugins/ipythonconsole.py:350 +msgid "Inline" +msgstr "Em linha" + +#: spyder/plugins/ipythonconsole.py:352 +msgid "Graphics backend" +msgstr "Saída gráfica" + +#: spyder/plugins/ipythonconsole.py:353 +msgid "" +"Decide how graphics are going to be displayed in the console. If unsure, " +"please select %s to put graphics inside the console or %s to " +"interact with them (through zooming and panning) in a separate window." +msgstr "" +"Decidir como serão mostrados os gráficos no console. Se está inseguro, por " +"favor selecione %s para colocar os gráficos no console ou %s " +"para interagir com eles (através de zoom) em uma janela separada." + +#: spyder/plugins/ipythonconsole.py:386 +msgid "Inline backend" +msgstr "Saída em linha" + +#: spyder/plugins/ipythonconsole.py:387 +msgid "Decide how to render the figures created by this backend" +msgstr "" +"Decida como renderizar as imagens criados por este tipo de saída gráfica" + +#: spyder/plugins/ipythonconsole.py:391 +msgid "Format:" +msgstr "Formato:" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "Resolution:" +msgstr "Resolução:" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "dpi" +msgstr "dpi" + +#: spyder/plugins/ipythonconsole.py:396 +msgid "Only used when the format is PNG. Default is 72" +msgstr "Só se usa quando o formato for PNG. O padrão é 72" + +#: spyder/plugins/ipythonconsole.py:399 +msgid "Width:" +msgstr "Largura:" + +#: spyder/plugins/ipythonconsole.py:399 spyder/plugins/ipythonconsole.py:403 +msgid "inches" +msgstr "polegadas" + +#: spyder/plugins/ipythonconsole.py:401 +msgid "Default is 6" +msgstr "O padrão é 6" + +#: spyder/plugins/ipythonconsole.py:403 +msgid "Height:" +msgstr "Altura:" + +#: spyder/plugins/ipythonconsole.py:405 +msgid "Default is 4" +msgstr "O padrão é 4" + +#: spyder/plugins/ipythonconsole.py:432 +msgid "" +"You can run several lines of code when a console is started. Please " +"introduce each one separated by commas, for example:
import os, import " +"sys" +msgstr "" +"Você pode executar várias linhas de código ao abrir um terminal. Por favor " +"introduza cada uma separada por vírgulas, por exemplo:
import os, " +"import sys" + +#: spyder/plugins/ipythonconsole.py:438 +msgid "Lines:" +msgstr "Linhas:" + +#: spyder/plugins/ipythonconsole.py:447 +msgid "Run a file" +msgstr "Executar arquivo" + +#: spyder/plugins/ipythonconsole.py:448 +msgid "" +"You can also run a whole file at startup instead of just some lines (This is " +"similar to have a PYTHONSTARTUP file)." +msgstr "" +"Você também pode executar um arquivo por inteiro ao inicializar, em vez de " +"poucas linhas (Isso é similar a ter um arquivo PYTHONSTARTUP)." + +#: spyder/plugins/ipythonconsole.py:452 +msgid "Use the following file:" +msgstr "Use o seguinte arquivo:" + +#: spyder/plugins/ipythonconsole.py:466 +msgid "Greedy completion" +msgstr "Completação gulosa" + +#: spyder/plugins/ipythonconsole.py:467 +msgid "" +"Enable Tab completion on elements of lists, results of function " +"calls, etc, without assigning them to a variable.
For example, you " +"can get completions on things like li[0].<Tab> or ins." +"meth().<Tab>" +msgstr "" +"Habilita o completador usando a tecla Tab em elementos de listas, " +"resultados de chamadas de funções, etc, sem atribuí-los a uma " +"variável.
Dessa forma pode-se obter sugestões de completação em coisas " +"como li[0].<Tab> ou ins.meth().<Tab>" + +#: spyder/plugins/ipythonconsole.py:475 +msgid "Use the greedy completer" +msgstr "Usar o completador guloso" + +#: spyder/plugins/ipythonconsole.py:486 +msgid "Autocall" +msgstr "Autocall" + +#: spyder/plugins/ipythonconsole.py:487 +msgid "" +"Autocall makes IPython automatically call any callable object even if you " +"didn't type explicit parentheses.
For example, if you type str 43 " +"it becomes str(43) automatically." +msgstr "" +"Esta opção faz com que o Python chame automaticamente qualquer objeto mesmo " +"se você não tenha digitado os parenteses ao seu redor.
Por exemplo, ao " +"escrever str 43, irá virar automaticamente str(43)." + +#: spyder/plugins/ipythonconsole.py:494 +msgid "Smart" +msgstr "Inteligente" + +#: spyder/plugins/ipythonconsole.py:495 +msgid "Full" +msgstr "Total" + +#: spyder/plugins/ipythonconsole.py:496 +msgid "Off" +msgstr "Desativado" + +#: spyder/plugins/ipythonconsole.py:498 +msgid "Autocall: " +msgstr "Autocall: " + +#: spyder/plugins/ipythonconsole.py:499 +msgid "" +"On %s mode, Autocall is not applied if there are no arguments after " +"the callable. On %s mode, all callable objects are automatically " +"called (even if no arguments are present)." +msgstr "" +"No modo %s, Auto-chamada não é aplicada se não houver argumentos " +"depois do objeto clamável. No modo %s, todos os objetos chamáveis são " +"chamados automaticamente (mesmo se não houver argumentos presentes)." + +#: spyder/plugins/ipythonconsole.py:511 +msgid "Symbolic Mathematics" +msgstr "Matemática simbólica" + +#: spyder/plugins/ipythonconsole.py:512 +msgid "" +"Perfom symbolic operations in the console (e.g. integrals, derivatives, " +"vector calculus, etc) and get the outputs in a beautifully printed style (it " +"requires the Sympy module)." +msgstr "" +"Realiza operações simbólicas no console (integrais, derivadas, cálculo " +"vetoria e etcl) tendo os resultados em um belo estilo impresso (requer o " +"módulo Sympy)." + +#: spyder/plugins/ipythonconsole.py:517 +msgid "Use symbolic math" +msgstr "Usar matemática simbólica" + +#: spyder/plugins/ipythonconsole.py:518 +msgid "" +"This option loads the Sympy library to work with.
Please refer to its " +"documentation to learn how to use it." +msgstr "" +"Esta opção carrega a biblioteca Sympy para trabalhar
com ela. Por favor " +"leia sua documentação para aprender a usá-la. " + +#: spyder/plugins/ipythonconsole.py:528 +msgid "Prompts" +msgstr "Prompts" + +#: spyder/plugins/ipythonconsole.py:529 +msgid "Modify how Input and Output prompts are shown in the console." +msgstr "Modifique como são mostradas as saídas e entradas no console." + +#: spyder/plugins/ipythonconsole.py:532 +msgid "Input prompt:" +msgstr "Prompt de entrada:" + +#: spyder/plugins/ipythonconsole.py:534 +msgid "" +"Default is
In [<span class=\"in-prompt-number\">%i</span>]:" +msgstr "" +"O Padrão é
In [<span class=\"in-prompt-number\">%i</span>]:" + +#: spyder/plugins/ipythonconsole.py:538 +msgid "Output prompt:" +msgstr "Prompt de saída:" + +#: spyder/plugins/ipythonconsole.py:540 +msgid "" +"Default is
Out[<span class=\"out-prompt-number\">%i</span>]:" +msgstr "" +"O Padrão é
Out[<span class=\"out-prompt-number\">%i</span>]: " + +#: spyder/plugins/ipythonconsole.py:562 spyder/plugins/workingdirectory.py:45 +msgid "Startup" +msgstr "Inicialização" + +#: spyder/plugins/ipythonconsole.py:734 +msgid "Open an &IPython console" +msgstr "Abrir um console &IPython " + +#: spyder/plugins/ipythonconsole.py:737 +msgid "Use %s+T when the console is selected to open a new one" +msgstr "Utilize %s+T quando o console estiver selecionado para abrir um novo" + +#: spyder/plugins/ipythonconsole.py:740 +msgid "Open a new console" +msgstr "Abrir um novo console" + +#: spyder/plugins/ipythonconsole.py:748 +msgid "Open a new IPython console connected to an existing kernel" +msgstr "Abrir um novo console do IPython conectado a um kernel existente." + +#: spyder/plugins/ipythonconsole.py:836 +msgid "" +"No IPython console is currently available to run %s.

Please " +"open a new one and try again." +msgstr "" +"Não existe um console IPython para ser executado %s.

Por favor " +"abra um novo e tente novamente." + +#: spyder/plugins/ipythonconsole.py:880 +msgid "" +"Your Python environment or installation doesn't have the ipykernel " +"module installed on it. Without this module is not possible for Spyder to " +"create a console for you.

You can install ipykernel by " +"running in a terminal:

pip install ipykernel

or

conda install ipykernel" +msgstr "" +"Sua instalação do Python não possui o módulo ipykernel instalado. " +"Sem esse módulo não é possível que o Spyder crie um console.

Você " +"pode instalar o ipykernel executando no terminal tal comando:" +"

pip install ipykernel

ou

conda install " +"ipykernel" + +#: spyder/plugins/ipythonconsole.py:1105 +msgid "Do you want to close this console?" +msgstr "Tem certeza que deseja fechar esse console?" + +#: spyder/plugins/ipythonconsole.py:1111 +msgid "" +"Do you want to close all other consoles connected to the same kernel as this " +"one?" +msgstr "" +"Você deseja fechar todos os outros consoles conectados ao mesmo kernel como " +"esse?" + +#: spyder/plugins/ipythonconsole.py:1382 +msgid "IPython" +msgstr "IPython" + +#: spyder/plugins/ipythonconsole.py:1383 +msgid "Unable to connect to %s" +msgstr "Não foi possível conectar a %s" + +#: spyder/plugins/ipythonconsole.py:1443 +msgid "Connection error" +msgstr "Erro de conexão" + +#: spyder/plugins/ipythonconsole.py:1444 +msgid "" +"Could not open ssh tunnel. The error was:\n" +"\n" +msgstr "" +"Não foi possível criar um túnel ssh. O erro foi:\n" +"\n" + +#: spyder/plugins/layoutdialog.py:177 +msgid "Move Up" +msgstr "Mover para Cima" + +#: spyder/plugins/layoutdialog.py:178 +msgid "Move Down" +msgstr "Mover para baixo" + +#: spyder/plugins/layoutdialog.py:179 +msgid "Delete Layout" +msgstr "Deletar Layout" + +#: spyder/plugins/layoutdialog.py:183 +msgid "Layout Display and Order" +msgstr "Ordem dos layouts" + +#: spyder/plugins/maininterpreter.py:31 spyder/plugins/maininterpreter.py:66 +msgid "Python interpreter" +msgstr "Interpretador Python" + +#: spyder/plugins/maininterpreter.py:68 +msgid "Select the Python interpreter for all Spyder consoles" +msgstr "Selecione o interpretador Python para todos os consoles do Spyder" + +#: spyder/plugins/maininterpreter.py:71 +msgid "Default (i.e. the same as Spyder's)" +msgstr "Padrão (o mesmo do Spyder)" + +#: spyder/plugins/maininterpreter.py:74 +msgid "Use the following Python interpreter:" +msgstr "Usar o seguinte interpretador:" + +#: spyder/plugins/maininterpreter.py:77 +msgid "Executables" +msgstr "Executáveis " + +#: spyder/plugins/maininterpreter.py:94 +msgid "User Module Reloader (UMR)" +msgstr "Recarregador de Módulos do Usuário (RMU)" + +#: spyder/plugins/maininterpreter.py:95 +msgid "" +"UMR forces Python to reload modules which were imported when executing a " +"file in a Python or IPython console with the runfile function." +msgstr "" +"O RMU força o Python a recarregar os módulos que foram importados durante a " +"execução de um arquivo no console com a função runfile." + +#: spyder/plugins/maininterpreter.py:100 +msgid "Enable UMR" +msgstr "Ativar RMU" + +#: spyder/plugins/maininterpreter.py:101 +msgid "" +"This option will enable the User Module Reloader (UMR) in Python/IPython " +"consoles. UMR forces Python to reload deeply modules during import when " +"running a Python script using the Spyder's builtin function runfile." +"

1. UMR may require to restart the console in which it will be " +"called (otherwise only newly imported modules will be reloaded when " +"executing files).

2. If errors occur when re-running a PyQt-" +"based program, please check that the Qt objects are properly destroyed (e.g. " +"you may have to use the attribute Qt.WA_DeleteOnClose on your main " +"window, using the setAttribute method)" +msgstr "" +"Esta opção ativará o Recarregador de Módulos do Usuário (RMU) nos consoles " +"Python/IPython. O RMU força o Python a recarregar profundamente os módulos " +"que foram importados ao executar um arquivo do Python, usando a função " +"incorporada do Spyder chamada runfile.

1. O RMU pode " +"necessitar que se reinicie o console em o qual será utilizado (de outra " +"forma, só os módulos que foram importados em último lugar serão recarregados " +"ao executar um arquivo).

2. Se ocorrer algum erro ao re-" +"executar um programa baseado no PyQt, por favor verifique se os objetos do " +"Qt foram destruídos apropriadamente (por exemplo, você pode ter que utilizar " +"o atributo Qt.WA_DeleteOnClose em sua janela principal, utilizando o " +"método setAttribute)" + +#: spyder/plugins/maininterpreter.py:117 +msgid "Show reloaded modules list" +msgstr "Mostrar lista de módulos que foram recarregados" + +#: spyder/plugins/maininterpreter.py:118 +msgid "Please note that these changes will be applied only to new consoles" +msgstr "" +"Por favor tenha em nota que as mudanças só serão aplicadas em novos consoles" + +#: spyder/plugins/maininterpreter.py:122 +msgid "Set UMR excluded (not reloaded) modules" +msgstr "Definir lista de módulos excluídos pelo RMU" + +#: spyder/plugins/maininterpreter.py:168 +msgid "" +"You selected a Python %d interpreter for the console but Spyder is " +"running on Python %d!.

Although this is possible, we recommend " +"you to install and run Spyder directly with your selected interpreter, to " +"avoid seeing false warnings and errors due to the incompatible syntax " +"between these two Python versions." +msgstr "" +"Você selecionou um interpretador Python %d para o console, mas o " +"Spyder está sendo executado em Python %d!.

Embora isso seja " +"possível, nós recomendamos instalar e executar o Spyder diretamente com o " +"interpretador selecionado, para evitar ver falsos erros e avisos no Editor " +"devido a sintaxe incompatível entre estas duas versões do Python." + +#: spyder/plugins/maininterpreter.py:178 spyder/plugins/maininterpreter.py:205 +#: spyder/plugins/maininterpreter.py:209 +msgid "UMR" +msgstr "RMU" + +#: spyder/plugins/maininterpreter.py:179 +msgid "Set the list of excluded modules as this: numpy, scipy" +msgstr "Defina a lista de módulos excluídos desta forma: numpy, scipy" + +#: spyder/plugins/maininterpreter.py:196 +msgid "" +"You are working with Python 2, this means that you can not import a module " +"that contains non-ascii characters." +msgstr "" + +#: spyder/plugins/maininterpreter.py:206 +msgid "" +"The following modules are not installed on your machine:\n" +"%s" +msgstr "" +"Os seguintes módulos não estão instalados no seu computador:\n" +"%s" + +#: spyder/plugins/maininterpreter.py:210 +msgid "" +"Please note that these changes will be applied only to new Python/IPython " +"consoles" +msgstr "" +"Por favor, note que estas mudanças só serão aplicadas apenas aos novos " +"consoles do Python/IPython" + +#: spyder/plugins/onlinehelp.py:70 +msgid "Online help" +msgstr "Ajuda online" + +#: spyder/plugins/outlineexplorer.py:49 spyder/widgets/editortools.py:195 +msgid "Outline" +msgstr "Explorador de código" + +#: spyder/plugins/projects.py:76 spyder/widgets/projects/explorer.py:113 +#: spyder/widgets/projects/explorer.py:127 +msgid "Project explorer" +msgstr "Explorador de projetos" + +#: spyder/plugins/projects.py:88 +msgid "New Project..." +msgstr "Novo Projeto..." + +#: spyder/plugins/projects.py:91 +msgid "Open Project..." +msgstr "Abrir Projeto..." + +#: spyder/plugins/projects.py:94 +msgid "Close Project" +msgstr "Fechar Projeto" + +#: spyder/plugins/projects.py:97 +#, fuzzy +msgid "Delete Project" +msgstr "Projetos Recentes" + +#: spyder/plugins/projects.py:103 +msgid "Project Preferences" +msgstr "Preferências do Projeto" + +#: spyder/plugins/projects.py:105 +msgid "Recent Projects" +msgstr "Projetos Recentes" + +#: spyder/plugins/projects.py:240 +msgid "Open project" +msgstr "Abrir projeto" + +#: spyder/plugins/projects.py:245 +msgid "%s is not a Spyder project!" +msgstr "%s não é um projeto do Spyder!" + +#: spyder/plugins/runconfig.py:29 +msgid "Execute in current Python or IPython console" +msgstr "Executar no console atual do Python ou IPython" + +#: spyder/plugins/runconfig.py:30 +msgid "Execute in a new dedicated Python console" +msgstr "Executar em um novo console dedicado do Python" + +#: spyder/plugins/runconfig.py:31 +msgid "Execute in an external System terminal" +msgstr "Executar em um terminal externo do Sistema" + +#: spyder/plugins/runconfig.py:41 +msgid "Always show %s on a first file run" +msgstr "Sempre mostrar %s na primeira execução" + +#: spyder/plugins/runconfig.py:160 spyder/plugins/runconfig.py:474 +msgid "General settings" +msgstr "Configurações gerais" + +#: spyder/plugins/runconfig.py:163 spyder/plugins/runconfig.py:209 +msgid "Command line options:" +msgstr "Opções de linha de comando: " + +#: spyder/plugins/runconfig.py:169 +msgid "Working directory:" +msgstr "Diretório de trabalho:" + +#: spyder/plugins/runconfig.py:181 spyder/plugins/runconfig.py:492 +msgid "Enter debugging mode when errors appear during execution" +msgstr "Entrar no modo de depuração quando erros aparecerem durante a execução" + +#: spyder/plugins/runconfig.py:197 spyder/plugins/runconfig.py:502 +msgid "Dedicated Python console" +msgstr "Console do Python dedicado" + +#: spyder/plugins/runconfig.py:201 spyder/plugins/runconfig.py:504 +msgid "Interact with the Python console after execution" +msgstr "Interagir com o console depois da execução" + +#: spyder/plugins/runconfig.py:205 +msgid "Show warning when killing running process" +msgstr "Mostrar um aviso quando matar o processo" + +#: spyder/plugins/runconfig.py:214 +msgid "-u is added to the other options you set here" +msgstr "A opção -u foi adicionada a essas opções" + +#: spyder/plugins/runconfig.py:224 +msgid "this dialog" +msgstr "este dialogo " + +#: spyder/plugins/runconfig.py:283 +msgid "Run configuration" +msgstr "Opções de execução" + +#: spyder/plugins/runconfig.py:284 +msgid "The following working directory is not valid:
%s" +msgstr "O seguinte diretório de trabalho não é válido:
%s" + +#: spyder/plugins/runconfig.py:362 +msgid "Run settings for %s" +msgstr "Ajustar configuração para %s" + +#: spyder/plugins/runconfig.py:394 +msgid "Select a run configuration:" +msgstr "Selecionar um arquivo de execução:" + +#: spyder/plugins/runconfig.py:423 spyder/plugins/runconfig.py:448 +msgid "Run Settings" +msgstr "Opções de execução" + +#: spyder/plugins/runconfig.py:450 +msgid "" +"The following are the default %s. These options may be overriden " +"using the %s dialog box (see the %s menu)" +msgstr "" +"As seguintes %s são padrões. Estas opções podem ser modificadas " +"usando o diálogo %s (ver o menu %s)" + +#: spyder/plugins/runconfig.py:476 +msgid "Default working directory is:" +msgstr "O diretório de trabalho padrão é:" + +#: spyder/plugins/runconfig.py:478 +msgid "the script directory" +msgstr "Diretório do script atual" + +#: spyder/plugins/runconfig.py:481 spyder/plugins/workingdirectory.py:57 +msgid "the following directory:" +msgstr "O seguinte diretório:" + +#: spyder/plugins/runconfig.py:507 +msgid "Show warning when killing running processes" +msgstr "Mostrar aviso quando um processo em execução for morto" + +#: spyder/plugins/runconfig.py:516 +msgid "Run Settings dialog" +msgstr "Configurações de execução" + +#: spyder/plugins/shortcuts.py:137 +msgid "" +"Press the new shortcut and select 'Ok': \n" +"(Press 'Tab' once to switch focus between the shortcut entry \n" +"and the buttons below it)" +msgstr "" +"Pressione o novo atalho e selecione 'Ok': \n" +"(Pressione 'Tab' uma vez para mudar o foco entre a entrada do atalho \n" +"e os botões abaixo dele)" + +#: spyder/plugins/shortcuts.py:140 +msgid "Current shortcut:" +msgstr "Atalho atual:" + +#: spyder/plugins/shortcuts.py:142 +msgid "New shortcut:" +msgstr "Novo atalho:" + +#: spyder/plugins/shortcuts.py:155 +msgid "Shortcut: {0}" +msgstr "Atalho: {0}" + +#: spyder/plugins/shortcuts.py:276 +msgid "Please introduce a different shortcut" +msgstr "Por favor escolha um atalho diferente" + +#: spyder/plugins/shortcuts.py:313 +msgid "The new shorcut conflicts with:" +msgstr "O novo atalho conflita com:" + +#: spyder/plugins/shortcuts.py:324 +msgid "" +"A compound sequence can have {break} a maximum of 4 subsequences.{break}" +msgstr "" +"Uma sequência composta pode ter {break} um máximo de 4 subsequências.{break}" + +#: spyder/plugins/shortcuts.py:329 +msgid "Invalid key entered" +msgstr "Chave inválida inserida" + +#: spyder/plugins/shortcuts.py:531 +msgid "Context" +msgstr "Contexto" + +#: spyder/plugins/shortcuts.py:533 +#: spyder/widgets/variableexplorer/collectionseditor.py:118 +msgid "Name" +msgstr "Nome" + +#: spyder/plugins/shortcuts.py:535 +msgid "Shortcut" +msgstr "Atalho" + +#: spyder/plugins/shortcuts.py:537 +msgid "Score" +msgstr "Valor" + +#: spyder/plugins/shortcuts.py:697 +msgid "Conflicts" +msgstr "Conflitos" + +#: spyder/plugins/shortcuts.py:698 +msgid "The following conflicts have been detected:" +msgstr "Os seguintes conflitos foram detectados:" + +#: spyder/plugins/shortcuts.py:783 +msgid "Keyboard shortcuts" +msgstr "Teclas de atalho" + +#: spyder/plugins/shortcuts.py:791 +msgid "Search: " +msgstr "Procurar: " + +#: spyder/plugins/shortcuts.py:792 +msgid "Reset to default values" +msgstr "Restaurar os valores padrões." + +#: spyder/plugins/variableexplorer.py:26 +msgid "Autorefresh" +msgstr "Atualizar automaticamente" + +#: spyder/plugins/variableexplorer.py:27 +msgid "Enable autorefresh" +msgstr "Ativar atualização automatica" + +#: spyder/plugins/variableexplorer.py:29 +msgid "Refresh interval: " +msgstr "Intervalo de atualização" + +#: spyder/plugins/variableexplorer.py:33 +msgid "Filter" +msgstr "Filtro" + +#: spyder/plugins/variableexplorer.py:35 +#: spyder/widgets/variableexplorer/namespacebrowser.py:226 +msgid "Exclude private references" +msgstr "Excluir variáveis privadas" + +#: spyder/plugins/variableexplorer.py:36 +#: spyder/widgets/variableexplorer/namespacebrowser.py:241 +msgid "Exclude capitalized references" +msgstr "Excluir variáveis que começam em maiúsculas" + +#: spyder/plugins/variableexplorer.py:37 +#: spyder/widgets/variableexplorer/namespacebrowser.py:234 +msgid "Exclude all-uppercase references" +msgstr "Excluir variáveis em maiúscula " + +#: spyder/plugins/variableexplorer.py:38 +#: spyder/widgets/variableexplorer/namespacebrowser.py:249 +msgid "Exclude unsupported data types" +msgstr "Excluir tipos de dados não suportados" + +#: spyder/plugins/variableexplorer.py:46 +#: spyder/widgets/variableexplorer/collectionseditor.py:677 +msgid "Show arrays min/max" +msgstr "Mostrar o minimo e máximo de matrizes" + +#: spyder/plugins/variableexplorer.py:48 +msgid "Edit data in the remote process" +msgstr "Editar dados em um processo remoto" + +#: spyder/plugins/variableexplorer.py:49 +msgid "" +"Editors are opened in the remote process for NumPy arrays, PIL images, " +"lists, tuples and dictionaries.\n" +"This avoids transfering large amount of data between the remote process and " +"Spyder (through the socket)." +msgstr "" +"Esta opção permite modificar matrizes do NumPY, imagens do PIL, listas, " +"tuplas e\n" +"dicionários em um processo remoto. Isto impede a transferência de grandes " +"quantidades de dados\n" +"entre o processo remoto e o Spyder." + +#: spyder/plugins/variableexplorer.py:185 +msgid "Variable explorer" +msgstr "Explorador de variáveis" + +#: spyder/plugins/workingdirectory.py:38 +msgid "" +"The global working directory is the working directory for newly " +"opened consoles (Python/IPython consoles and terminals), for the " +"file explorer, for the find in files plugin and for new files " +"created in the editor." +msgstr "" +"O diretório de trabalho global é o diretório de trabalho para os " +"consoles recém abertos (do IPython/Python e dos terminais), para o " +"Explorador de arquivos, Buscar em arquivos e para os novos " +"arquivos criados no Editor." + +#: spyder/plugins/workingdirectory.py:47 +msgid "At startup, the global working directory is:" +msgstr "Ao iniciar, o diretório de trabalho global é:" + +#: spyder/plugins/workingdirectory.py:51 +msgid "the same as in last session" +msgstr "O mesmo da última sessão" + +#: spyder/plugins/workingdirectory.py:53 +msgid "At startup, Spyder will restore the global directory from last session" +msgstr "Ao iniciar o Spyder irá restaurar o diretório global da última sessão" + +#: spyder/plugins/workingdirectory.py:59 +msgid "At startup, the global working directory will be the specified path" +msgstr "Ao iniciar o diretório de trabalho será o seguinte" + +#: spyder/plugins/workingdirectory.py:71 +msgid "Files are opened from:" +msgstr "Os arquivos são abertos de:" + +#: spyder/plugins/workingdirectory.py:75 spyder/plugins/workingdirectory.py:88 +msgid "the current file directory" +msgstr "O diretório do arquivo atual" + +#: spyder/plugins/workingdirectory.py:79 spyder/plugins/workingdirectory.py:92 +msgid "the global working directory" +msgstr "O diretório de trabalho global" + +#: spyder/plugins/workingdirectory.py:84 +msgid "Files are created in:" +msgstr "Os arquivos são criados em:" + +#: spyder/plugins/workingdirectory.py:98 +msgid "Change to file base directory" +msgstr "Mudar para diretório base do arquivo" + +#: spyder/plugins/workingdirectory.py:100 +msgid "When opening a file" +msgstr "Ao abrir um arquivo" + +#: spyder/plugins/workingdirectory.py:102 +msgid "When saving a file" +msgstr "Ao salvar um arquivo" + +#: spyder/plugins/workingdirectory.py:172 +msgid "Back" +msgstr "Anterior" + +#: spyder/plugins/workingdirectory.py:180 spyder/widgets/explorer.py:1099 +#: spyder/widgets/variableexplorer/importwizard.py:529 +msgid "Next" +msgstr "Próximo" + +#: spyder/plugins/workingdirectory.py:191 +msgid "" +"This is the working directory for newly\n" +"opened consoles (Python/IPython consoles and\n" +"terminals), for the file explorer, for the\n" +"find in files plugin and for new files\n" +"created in the editor" +msgstr "" +"Este é o diretório de trabalho para os\n" +"consoles recém abertos \n" +"(do IPython/Python e terminais), \n" +"para o Explorador de arquivos,\n" +"Buscar em arquivos e para os novos\n" +"arquivos criados no Editor" + +#: spyder/plugins/workingdirectory.py:219 +msgid "Browse a working directory" +msgstr "Selecionar um diretório de trabalho" + +#: spyder/plugins/workingdirectory.py:226 +msgid "Change to parent directory" +msgstr "Mudar para diretório superior" + +#: spyder/plugins/workingdirectory.py:233 +msgid "Global working directory" +msgstr "Diretório de trabalho global" + +#: spyder/utils/codeanalysis.py:91 +msgid "Real-time code analysis on the Editor" +msgstr "Análise de código em tempo real no editor" + +#: spyder/utils/codeanalysis.py:95 +msgid "Real-time code style analysis on the Editor" +msgstr "Análise de estilo em tempo real no editor." + +#: spyder/utils/environ.py:101 +msgid "" +"Module pywin32 was not found.
Please restart this Windows " +"session (not the computer) for changes to take effect." +msgstr "" +"O módulo pywin32 não foi encontrado.
Por favor reinicie esta " +"sessão do Windows (não do computador) para que as mudanças tenham " +"efeito." + +#: spyder/utils/environ.py:114 +msgid "" +"If you accept changes, this will modify the current user environment " +"variables directly in Windows registry. Use it with precautions, at " +"your own risks.

Note that for changes to take effect, you will need " +"to restart the parent process of this application (simply restart Spyder if " +"you have executed it from a Windows shortcut, otherwise restart any " +"application from which you may have executed it, like Python(x,y) Home for example)" +msgstr "" +"Se você aceitar, as variáveis de ambiente atuais serão modificadas no " +"registro do Windows. Use com precaução, em seu próprio risco." +"

Tenha em mente que para que as mudanças tenham efeito, você deverá " +"reiniciar o processo pai deste aplicativo (simplesmente reinicie o Spyder se " +"você executou ele de um atalho, caso contrário reinicie qualquer aplicativo " +"pelo qual você você executou ele, como o Python(x,y) Home)" + +#: spyder/utils/help/sphinxify.py:217 spyder/utils/help/sphinxify.py:227 +msgid "" +"It was not possible to generate rich text help for this object.
Please " +"see it in plain text." +msgstr "" +"Não foi possível gerar ajuda em texto formatado para este objeto.
Por " +"favor veja em texto normal." + +#: spyder/utils/introspection/manager.py:33 +#: spyder/utils/introspection/manager.py:38 +msgid "Editor's code completion, go-to-definition and help" +msgstr "Completador de código e ajuda no Editor" + +#: spyder/utils/iofuncs.py:408 +msgid "Supported files" +msgstr "Arquivos suportados" + +#: spyder/utils/iofuncs.py:410 +msgid "All files (*.*)" +msgstr "Todos os arquivos (*.*)" + +#: spyder/utils/iofuncs.py:420 +msgid "Spyder data files" +msgstr "Arquivos data do Spyder" + +#: spyder/utils/iofuncs.py:422 +#: spyder/widgets/variableexplorer/collectionseditor.py:1021 +msgid "NumPy arrays" +msgstr "Matrizes do NumPy" + +#: spyder/utils/iofuncs.py:423 +msgid "NumPy zip arrays" +msgstr "Matrizes comprimidas do NumPy" + +#: spyder/utils/iofuncs.py:424 +msgid "Matlab files" +msgstr "Arquivos do Matlab" + +#: spyder/utils/iofuncs.py:425 +msgid "CSV text files" +msgstr "Arquivos de texto CSV" + +#: spyder/utils/iofuncs.py:427 +msgid "JPEG images" +msgstr "Imagens JPEG" + +#: spyder/utils/iofuncs.py:428 +msgid "PNG images" +msgstr "Imagens PNG" + +#: spyder/utils/iofuncs.py:429 +msgid "GIF images" +msgstr "Imagens GIF" + +#: spyder/utils/iofuncs.py:430 +msgid "TIFF images" +msgstr "Imagens TIFF" + +#: spyder/utils/iofuncs.py:431 spyder/utils/iofuncs.py:432 +msgid "Pickle files" +msgstr "Arquivos Pickle" + +#: spyder/utils/iofuncs.py:433 +msgid "JSON files" +msgstr "Arquivos JSON" + +#: spyder/utils/iofuncs.py:452 spyder/utils/iofuncs.py:459 +msgid "Unsupported file type '%s'" +msgstr "Tipo de arquivo não suportado '%s'" + +#: spyder/utils/programs.py:286 +msgid "It was not possible to run this file in an external terminal" +msgstr "Não foi possível executar este arquivo em um terminal externo" + +#: spyder/utils/syntaxhighlighters.py:33 +msgid "Syntax highlighting for Matlab, Julia and other file types" +msgstr "Sintaxe colorida para arquivos do tipo Matlab, Julia e outros tipos" + +#: spyder/utils/syntaxhighlighters.py:42 +msgid "Background:" +msgstr "Fundo:" + +#: spyder/utils/syntaxhighlighters.py:43 +#: spyder/widgets/sourcecode/codeeditor.py:106 +msgid "Current line:" +msgstr "Linha atual:" + +#: spyder/utils/syntaxhighlighters.py:44 +msgid "Current cell:" +msgstr "Célula selecionada:" + +#: spyder/utils/syntaxhighlighters.py:45 +msgid "Occurrence:" +msgstr "Ocorrência:" + +#: spyder/utils/syntaxhighlighters.py:46 +msgid "Link:" +msgstr "Link:" + +#: spyder/utils/syntaxhighlighters.py:47 +msgid "Side areas:" +msgstr "Áreas laterais:" + +#: spyder/utils/syntaxhighlighters.py:48 +msgid "Matched
parens:" +msgstr "Parênteses fechados:" + +#: spyder/utils/syntaxhighlighters.py:49 +msgid "Unmatched
parens:" +msgstr "Parênteses abertos:" + +#: spyder/utils/syntaxhighlighters.py:50 +msgid "Normal text:" +msgstr "Texto normal:" + +#: spyder/utils/syntaxhighlighters.py:51 +msgid "Keyword:" +msgstr "Palavra chave:" + +#: spyder/utils/syntaxhighlighters.py:52 +msgid "Builtin:" +msgstr "Objeto integrado:" + +#: spyder/utils/syntaxhighlighters.py:53 +msgid "Definition:" +msgstr "Definição:" + +#: spyder/utils/syntaxhighlighters.py:54 +msgid "Comment:" +msgstr "Comentário:" + +#: spyder/utils/syntaxhighlighters.py:55 +msgid "String:" +msgstr "String:" + +#: spyder/utils/syntaxhighlighters.py:56 +msgid "Number:" +msgstr "Número:" + +#: spyder/utils/syntaxhighlighters.py:57 +msgid "Instance:" +msgstr "Instância:" + +#: spyder/widgets/arraybuilder.py:179 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Type an array in Matlab : [1 2;3 4]
\n" +" or Spyder simplified syntax : 1 2;3 4\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two spaces or two tabs to generate a ';'.\n" +" " +msgstr "" +"\n" +" Ajuda no Numpy Array/Matriz
\n" +" Definir uma matriz no Matlab : [1 2;3 4]
\n" +" ou na sintaxe simplificada do Spyder : 1 2;3 4\n" +"

\n" +" Aperte 'Enter' para array ou 'Ctrl+Enter' para matriz.\n" +"

\n" +" Dica:
\n" +" Use dois espaços ou dois tabs para gerar um ';'.\n" +" " + +#: spyder/widgets/arraybuilder.py:190 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Enter an array in the table.
\n" +" Use Tab to move between cells.\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two tabs at the end of a row to move to the next row.\n" +" " +msgstr "" +"\n" +" Ajuda no Numpy Array/Matriz
\n" +" Digite um array na tabela.
\n" +" Use Tab para se mover entre as células.\n" +"

\n" +" Aperte 'Enter' para array ou 'Ctrl+Enter' para matriz.\n" +"

\n" +" Dica:
\n" +" Use dois Tabs no fim de uma coluna para se mover até a próxima " +"coluna.\n" +" " + +#: spyder/widgets/arraybuilder.py:365 +msgid "Array dimensions not valid" +msgstr "Dimensões da matriz não é válida" + +#: spyder/widgets/browser.py:54 spyder/widgets/sourcecode/codeeditor.py:2559 +msgid "Zoom out" +msgstr "Menos zoom" + +#: spyder/widgets/browser.py:57 spyder/widgets/sourcecode/codeeditor.py:2555 +msgid "Zoom in" +msgstr "Mais zoom" + +#: spyder/widgets/browser.py:177 +msgid "Home" +msgstr "Página inicial" + +#: spyder/widgets/browser.py:213 +msgid "Find text" +msgstr "Buscar texto" + +#: spyder/widgets/browser.py:231 +msgid "Address:" +msgstr "Endereço:" + +#: spyder/widgets/browser.py:267 +msgid "Unable to load page" +msgstr "Não foi possível carregar a página" + +#: spyder/widgets/comboboxes.py:165 +msgid "Press enter to validate this entry" +msgstr "Aperte enter para validar esta entrada" + +#: spyder/widgets/comboboxes.py:166 +msgid "This entry is incorrect" +msgstr "Esta entrada é incorreta" + +#: spyder/widgets/comboboxes.py:209 +msgid "Press enter to validate this path" +msgstr "Aperte enter para validar esse caminho" + +#: spyder/widgets/dependencies.py:63 +msgid " Required " +msgstr "Requerido" + +#: spyder/widgets/dependencies.py:63 +msgid "Module" +msgstr "Módulo " + +#: spyder/widgets/dependencies.py:64 +msgid " Installed " +msgstr "Instalado" + +#: spyder/widgets/dependencies.py:64 +msgid "Provided features" +msgstr "Características proporcionadas" + +#: spyder/widgets/dependencies.py:134 +msgid "Dependencies" +msgstr "Dependências" + +#: spyder/widgets/dependencies.py:141 +#, fuzzy +msgid "" +"Spyder depends on several Python modules to provide the right functionality " +"for all its panes. The table below shows the required and installed versions " +"(if any) of all of them.

Note: You can safely use Spyder " +"without the following modules installed: %s and %s." +"

Please also note that new dependencies or changed ones will be " +"correctly detected only after Spyder is restarted." +msgstr "" +"O Spyder depende de vários módulos do Python para fornecer suas " +"funcionalidades corretamente em todos os painéis. A tabela abaixo mostra as " +"versões requeridas e instaladas (se houver) de todos eles.

Nota: Você pode usar o Spyder normalmente sem os seguintes módulos instalados: " +"%se %s" + +#: spyder/widgets/dependencies.py:157 +msgid "Copy to clipboard" +msgstr "Copiar para área transferência " + +#: spyder/widgets/editor.py:350 +msgid "Copy path to clipboard" +msgstr "Copiar caminho para área de transferência" + +#: spyder/widgets/editor.py:354 +msgid "Close all to the right" +msgstr "Fechar todos à direita" + +#: spyder/widgets/editor.py:356 +msgid "Close all but this" +msgstr "Fechar todos, menos este" + +#: spyder/widgets/editor.py:959 +msgid "Temporary file" +msgstr "Arquivo temporário " + +#: spyder/widgets/editor.py:1054 +msgid "New window" +msgstr "Nova janela" + +#: spyder/widgets/editor.py:1055 +msgid "Create a new editor window" +msgstr "Criar uma nova janela de edição" + +#: spyder/widgets/editor.py:1058 +msgid "Split vertically" +msgstr "Dividir verticalmente" + +#: spyder/widgets/editor.py:1060 +msgid "Split vertically this editor window" +msgstr "Dividir verticalmente esta janela de edição" + +#: spyder/widgets/editor.py:1062 +msgid "Split horizontally" +msgstr "Dividir horizontalmente" + +#: spyder/widgets/editor.py:1064 +msgid "Split horizontally this editor window" +msgstr "Dividir horizontalmente esta janela de edição" + +#: spyder/widgets/editor.py:1066 +msgid "Close this panel" +msgstr "Fechar este painel" + +#: spyder/widgets/editor.py:1223 +msgid "%s has been modified.
Do you want to save changes?" +msgstr "%s foi modificado.
Deseja salvar as mudanças?" + +#: spyder/widgets/editor.py:1285 +msgid "Save" +msgstr "Salvar" + +#: spyder/widgets/editor.py:1286 +msgid "Unable to save script '%s'

Error message:
%s" +msgstr "" +"Não foi possível salvar o script '%s'

Mensagem de erro:
%s" + +#: spyder/widgets/editor.py:1523 +msgid "" +"%s is unavailable (this file may have been removed, moved or renamed " +"outside Spyder).
Do you want to close it?" +msgstr "" +"%s está indisponível (o arquivo pode ter sido removido, movido ou " +"renomeado fora do Spyder).
Deseja fechar?" + +#: spyder/widgets/editor.py:1543 +msgid "" +"%s has been modified outside Spyder.
Do you want to reload it and " +"lose all your changes?" +msgstr "" +"%s foi modificado fora do Spyder.
Você deseja recarregar ele e " +"perder todas as modificações?" + +#: spyder/widgets/editor.py:1639 +msgid "" +"All changes to %s will be lost.
Do you want to revert file from " +"disk?" +msgstr "" +"Todas as modificações do %s foram perdidas.
Você deseja reverter o " +"arquivo do disco?" + +#: spyder/widgets/editor.py:1779 +msgid "Loading %s..." +msgstr "Carregando %s..." + +#: spyder/widgets/editor.py:1789 +msgid "" +"%s contains mixed end-of-line characters.
Spyder will fix this " +"automatically." +msgstr "" +"%s contém vários tipos de caracteres de fim de linha.
O Spyder irá " +"corrigir automaticamente." + +#: spyder/widgets/editor.py:2171 +msgid "Close window" +msgstr "Fechar janela" + +#: spyder/widgets/editor.py:2173 +msgid "Close this window" +msgstr "Fechar esta janela" + +#: spyder/widgets/editortools.py:94 spyder/widgets/editortools.py:130 +msgid "Line %s" +msgstr "Linha %s" + +#: spyder/widgets/editortools.py:99 +msgid "Class defined at line %s" +msgstr "Classe definida na linha %s" + +#: spyder/widgets/editortools.py:107 +msgid "Method defined at line %s" +msgstr "Método definido na linha %s" + +#: spyder/widgets/editortools.py:117 +msgid "Function defined at line %s" +msgstr "Função definida na linha %s" + +#: spyder/widgets/editortools.py:149 +msgid "Cell starts at line %s" +msgstr "A célula inicia na linha %s" + +#: spyder/widgets/editortools.py:202 spyder/widgets/editortools.py:539 +msgid "Go to cursor position" +msgstr "Ir para a posição do cursor " + +#: spyder/widgets/editortools.py:205 +msgid "Show absolute path" +msgstr "Mostrar o caminho completo" + +#: spyder/widgets/editortools.py:208 spyder/widgets/explorer.py:210 +msgid "Show all files" +msgstr "Mostrar todos os arquivos" + +#: spyder/widgets/editortools.py:211 +msgid "Show special comments" +msgstr "Mostrar comentários especiais" + +#: spyder/widgets/explorer.py:206 +msgid "Edit filename filters..." +msgstr "Editar filtros para nomes de arquivos..." + +#: spyder/widgets/explorer.py:220 +msgid "Edit filename filters" +msgstr "Editar filtros para nomes de arquivos" + +#: spyder/widgets/explorer.py:221 +msgid "Name filters:" +msgstr "Nome dos filtros:" + +#: spyder/widgets/explorer.py:240 +msgid "File..." +msgstr "Arquivo..." + +#: spyder/widgets/explorer.py:244 +msgid "Module..." +msgstr "Módulo..." + +#: spyder/widgets/explorer.py:248 +msgid "Folder..." +msgstr "Pasta..." + +#: spyder/widgets/explorer.py:252 +msgid "Package..." +msgstr "Pacote..." + +#: spyder/widgets/explorer.py:273 +#: spyder/widgets/variableexplorer/collectionseditor.py:652 +msgid "Edit" +msgstr "Editar" + +#: spyder/widgets/explorer.py:275 +msgid "Move..." +msgstr "Mover..." + +#: spyder/widgets/explorer.py:278 +msgid "Delete..." +msgstr "Deletar..." + +#: spyder/widgets/explorer.py:281 +msgid "Rename..." +msgstr "Renomear..." + +#: spyder/widgets/explorer.py:284 +msgid "Open" +msgstr "Abrir" + +#: spyder/widgets/explorer.py:285 spyder/widgets/sourcecode/codeeditor.py:2531 +msgid "Convert to Python script" +msgstr "Converter para um script Python" + +#: spyder/widgets/explorer.py:319 +msgid "Commit" +msgstr "Enviar" + +#: spyder/widgets/explorer.py:322 +msgid "Browse repository" +msgstr "Explorar repositório " + +#: spyder/widgets/explorer.py:333 +msgid "Open command prompt here" +msgstr "Abrir prompt de comando aqui" + +#: spyder/widgets/explorer.py:335 +msgid "Open terminal here" +msgstr "Abrir terminal aqui" + +#: spyder/widgets/explorer.py:340 +msgid "Open Python console here" +msgstr "Abrir console Python aqui" + +#: spyder/widgets/explorer.py:354 +msgid "New" +msgstr "Novo" + +#: spyder/widgets/explorer.py:362 +msgid "Import" +msgstr "Importar" + +#: spyder/widgets/explorer.py:513 +msgid "Do you really want to delete %s?" +msgstr "Deseja mesmo deletar %s?" + +#: spyder/widgets/explorer.py:531 +msgid "delete" +msgstr "deletar" + +#: spyder/widgets/explorer.py:532 spyder/widgets/projects/explorer.py:149 +#: spyder/widgets/projects/explorer.py:256 +msgid "Project Explorer" +msgstr "Explorador de projetos" + +#: spyder/widgets/explorer.py:533 spyder/widgets/projects/explorer.py:150 +msgid "Unable to %s %s

Error message:
%s" +msgstr "Não foi possível %s %s

Mensagem de erro:
%s" + +#: spyder/widgets/explorer.py:548 +#, fuzzy +msgid "File Explorer" +msgstr "Explorador de arquivos" + +#: spyder/widgets/explorer.py:549 +msgid "" +"The current directory contains a project.

If you want to delete the " +"project, please go to Projects » Delete Project" +msgstr "" + +#: spyder/widgets/explorer.py:566 spyder/widgets/sourcecode/codeeditor.py:2018 +msgid "Conversion error" +msgstr "Erro de conversão" + +#: spyder/widgets/explorer.py:567 spyder/widgets/sourcecode/codeeditor.py:2019 +msgid "" +"It was not possible to convert this notebook. The error is:\n" +"\n" +msgstr "" +"Não foi possível converter esse notebook. O erro é:\n" +"\n" + +#: spyder/widgets/explorer.py:584 spyder/widgets/explorer.py:592 +#: spyder/widgets/explorer.py:603 +#: spyder/widgets/variableexplorer/collectionseditor.py:681 +#: spyder/widgets/variableexplorer/collectionseditor.py:916 +msgid "Rename" +msgstr "Renomear" + +#: spyder/widgets/explorer.py:585 +msgid "New name:" +msgstr "Novo nome:" + +#: spyder/widgets/explorer.py:593 +msgid "" +"Do you really want to rename %s and overwrite the existing file " +"%s?" +msgstr "" +"Você deseja mesmo renomear %s e sobrescrever o arquivo já existente " +"%s? " + +#: spyder/widgets/explorer.py:604 +msgid "Unable to rename file %s

Error message:
%s" +msgstr "" +"Não foi possível renomear arquivo %s

Mensagem de erro:" +"
%s" + +#: spyder/widgets/explorer.py:640 +msgid "Unable to move %s

Error message:
%s" +msgstr "Não foi possível mover %s

Mensagem de erro:
%s" + +#: spyder/widgets/explorer.py:658 +msgid "Unable to create folder %s

Error message:
%s" +msgstr "" +"Não foi possível criar pasta %s

Mensagem de erro:
%s" + +#: spyder/widgets/explorer.py:671 spyder/widgets/explorer.py:705 +msgid "Unable to create file %s

Error message:
%s" +msgstr "" +"Não foi possível criar arquivo %s

Mensagem de erro:
" +"%s" + +#: spyder/widgets/explorer.py:679 +msgid "New folder" +msgstr "Nova pasta" + +#: spyder/widgets/explorer.py:680 +msgid "Folder name:" +msgstr "Nome da pasta:" + +#: spyder/widgets/explorer.py:685 +msgid "New package" +msgstr "Novo pacote" + +#: spyder/widgets/explorer.py:686 +msgid "Package name:" +msgstr "Nome do pacote:" + +#: spyder/widgets/explorer.py:726 +msgid "New module" +msgstr "Novo módulo " + +#: spyder/widgets/explorer.py:741 +msgid "" +"For %s support, please install one of the
following tools:

%s" +msgstr "" +"Para ter suporte ao %s, por favor instale as
seguintes ferramentas:

%s" + +#: spyder/widgets/explorer.py:745 +msgid "Unable to find external program.

%s" +msgstr "Não foi possível encontrar o programa externo.

%s" + +#: spyder/widgets/explorer.py:966 +msgid "Show current directory only" +msgstr "Mostrar somente o diretório atual" + +#: spyder/widgets/explorer.py:1066 +msgid "You don't have the right permissions to open this directory" +msgstr "Você não tem permissão para abrir esse diretório" + +#: spyder/widgets/explorer.py:1096 +#: spyder/widgets/variableexplorer/importwizard.py:525 +msgid "Previous" +msgstr "Anterior" + +#: spyder/widgets/explorer.py:1102 +msgid "Parent" +msgstr "Superior" + +#: spyder/widgets/externalshell/baseshell.py:129 +msgid "Run again this program" +msgstr "Executar novamente este programa" + +#: spyder/widgets/externalshell/baseshell.py:132 +msgid "Kill" +msgstr "Matar" + +#: spyder/widgets/externalshell/baseshell.py:134 +msgid "Kills the current process, causing it to exit immediately" +msgstr "Matar o processo atual, fazendo ele fechar imediatamente" + +#: spyder/widgets/externalshell/baseshell.py:206 +msgid "Running..." +msgstr "Executando..." + +#: spyder/widgets/externalshell/baseshell.py:213 +msgid "Terminated." +msgstr "Finalizado." + +#: spyder/widgets/externalshell/baseshell.py:238 +#: spyder/widgets/ipythonconsole/help.py:125 spyder/widgets/mixins.py:661 +msgid "Arguments" +msgstr "Argumentos" + +#: spyder/widgets/externalshell/baseshell.py:239 +msgid "Command line arguments:" +msgstr "Argumentos da linha de comando:" + +#: spyder/widgets/externalshell/pythonshell.py:277 +msgid "Variables" +msgstr "Variáveis" + +#: spyder/widgets/externalshell/pythonshell.py:278 +msgid "Show/hide global variables explorer" +msgstr "Mostrar/esconder o explorador de variáveis " + +#: spyder/widgets/externalshell/pythonshell.py:282 +msgid "Terminate" +msgstr "Finalizar" + +#: spyder/widgets/externalshell/pythonshell.py:283 +msgid "" +"Attempts to stop the process. The process\n" +"may not exit as a result of clicking this\n" +"button (it is given the chance to prompt\n" +"the user for any unsaved files, etc)." +msgstr "" +"Tentativa de parar este processo, mas este\n" +"pode não ser concluído se esse botão for pressionado\n" +"(pode ser possível que se peça ao usuário\n" +"para guardar arquivos ainda não salvos, etc)." + +#: spyder/widgets/externalshell/pythonshell.py:296 +msgid "Interact" +msgstr "Interagir" + +#: spyder/widgets/externalshell/pythonshell.py:298 +msgid "Debug" +msgstr "Depurar" + +#: spyder/widgets/externalshell/pythonshell.py:300 +#: spyder/widgets/externalshell/pythonshell.py:366 +msgid "Arguments..." +msgstr "Argumentos..." + +#: spyder/widgets/externalshell/pythonshell.py:302 +msgid "Post Mortem Debug" +msgstr "Post Mortem Debug" + +#: spyder/widgets/externalshell/pythonshell.py:308 +msgid "Working directory" +msgstr "Diretório de trabalho" + +#: spyder/widgets/externalshell/pythonshell.py:310 +msgid "Set current working directory" +msgstr "Definir diretório de trabalho" + +#: spyder/widgets/externalshell/pythonshell.py:312 +msgid "Environment variables" +msgstr "Variáveis de ambiente" + +#: spyder/widgets/externalshell/pythonshell.py:316 +msgid "Show sys.path contents" +msgstr "Mostrar conteúdo do sys.path" + +#: spyder/widgets/externalshell/pythonshell.py:362 +msgid "Arguments: %s" +msgstr "Argumentos: %s" + +#: spyder/widgets/externalshell/pythonshell.py:364 +msgid "No argument" +msgstr "Sem argumento" + +#: spyder/widgets/externalshell/pythonshell.py:534 +msgid "A Python console failed to start!" +msgstr "Não foi possível iniciar um console Python!" + +#: spyder/widgets/externalshell/systemshell.py:106 +msgid "Process failed to start" +msgstr "O processo falhou ao iniciar." + +#: spyder/widgets/fileswitcher.py:109 +msgid "unsaved file" +msgstr "arquivo não salvo" + +#: spyder/widgets/fileswitcher.py:230 +msgid "" +"Press Enter to switch files or Esc to cancel.

Type to " +"filter filenames.

Use :number to go to a line, e.g. " +"main:42
Use @symbol_text to go to a symbol, e." +"g. @init

Press Ctrl+W to close current " +"tab.
" +msgstr "" +"Pressione Enter para trocar de arquivo ou Esc para cancelar." +"

Type to filter filenames.

Utilize :número para ir numa " +"linha, ex: main:42
Use @texto_do_simbolo para " +"ir em um simbolo, ex: @init

Pressione Ctrl+W para fechar a aba atual.
" + +#: spyder/widgets/fileswitcher.py:508 +msgid "lines" +msgstr "linhas" + +#: spyder/widgets/findinfiles.py:158 +msgid "Unexpected error: see internal console" +msgstr "Erro inesperado: veja o console interno" + +#: spyder/widgets/findinfiles.py:209 spyder/widgets/findinfiles.py:233 +#: spyder/widgets/findinfiles.py:280 +msgid "invalid regular expression" +msgstr "expressão regular inválida" + +#: spyder/widgets/findinfiles.py:278 +msgid "permission denied errors were encountered" +msgstr "permissão negada, foram encontrados erros" + +#: spyder/widgets/findinfiles.py:315 +msgid "Search pattern" +msgstr "Padrão de pesquisa " + +#: spyder/widgets/findinfiles.py:318 spyder/widgets/findinfiles.py:352 +#: spyder/widgets/findinfiles.py:364 spyder/widgets/findreplace.py:81 +msgid "Regular expression" +msgstr "Expressão regular" + +#: spyder/widgets/findinfiles.py:327 +msgid "Search" +msgstr "Procurar" + +#: spyder/widgets/findinfiles.py:330 +msgid "Start search" +msgstr "Iniciar busca" + +#: spyder/widgets/findinfiles.py:336 +msgid "Stop search" +msgstr "Parar busca" + +#: spyder/widgets/findinfiles.py:346 +msgid "Included filenames pattern" +msgstr "" +"Incluir padrões de nomes\n" +"do arquivo" + +#: spyder/widgets/findinfiles.py:355 +msgid "Include:" +msgstr "Incluir:" + +#: spyder/widgets/findinfiles.py:358 +msgid "Excluded filenames pattern" +msgstr "" +"Excluir padrões de nomes\n" +"do arquivo" + +#: spyder/widgets/findinfiles.py:367 +msgid "Exclude:" +msgstr "Excluir:" + +#: spyder/widgets/findinfiles.py:377 +msgid "PYTHONPATH" +msgstr "PYTHONPATH" + +#: spyder/widgets/findinfiles.py:379 +msgid "" +"Search in all directories listed in sys.path which are outside the Python " +"installation directory" +msgstr "" +"Procurar em todos diretórios listados na sys.path\n" +"que estão fora do diretório de instalação do Python" + +#: spyder/widgets/findinfiles.py:382 +msgid "Hg repository" +msgstr "Repositório Hg" + +#: spyder/widgets/findinfiles.py:385 +msgid "Search in current directory hg repository" +msgstr "Buscar no repositório" + +#: spyder/widgets/findinfiles.py:386 +msgid "Here:" +msgstr "Aqui:" + +#: spyder/widgets/findinfiles.py:390 +msgid "Search recursively in this directory" +msgstr "Buscar recursivamente neste diretório" + +#: spyder/widgets/findinfiles.py:395 +msgid "Browse a search directory" +msgstr "Selecionar um diretório de busca" + +#: spyder/widgets/findinfiles.py:425 +msgid "Hide advanced options" +msgstr "Esconder opções avançadas" + +#: spyder/widgets/findinfiles.py:428 +msgid "Show advanced options" +msgstr "Mostrar opções avançadas " + +#: spyder/widgets/findinfiles.py:569 +msgid "Search canceled" +msgstr "Busca cancelada" + +#: spyder/widgets/findinfiles.py:573 +msgid "String not found" +msgstr "String não encontrada" + +#: spyder/widgets/findinfiles.py:575 +msgid "matches in" +msgstr "correspondências em" + +#: spyder/widgets/findinfiles.py:576 +msgid "file" +msgstr "arquivo" + +#: spyder/widgets/findinfiles.py:584 +msgid "interrupted" +msgstr "interrompido" + +#: spyder/widgets/findreplace.py:63 +msgid "Search string" +msgstr "Procurar string" + +#: spyder/widgets/findreplace.py:87 +msgid "Case Sensitive" +msgstr "" +"Diferenciar maiúsculas\n" +"de minúsculas" + +#: spyder/widgets/findreplace.py:93 +msgid "Whole words" +msgstr "Palavras inteiras" + +#: spyder/widgets/findreplace.py:99 +msgid "Highlight matches" +msgstr "Destacar correspondências " + +#: spyder/widgets/findreplace.py:113 +msgid "Replace with:" +msgstr "Substituir com:" + +#: spyder/widgets/findreplace.py:115 +msgid "Replace string" +msgstr "Substituir string" + +#: spyder/widgets/findreplace.py:118 +msgid "Replace/find" +msgstr "Substituir/buscar" + +#: spyder/widgets/findreplace.py:125 +msgid "Replace all" +msgstr "Substituir tudo" + +#: spyder/widgets/internalshell.py:262 +msgid "Help..." +msgstr "Ajuda..." + +#: spyder/widgets/internalshell.py:279 +msgid "Shell special commands:" +msgstr "Comando especiais: " + +#: spyder/widgets/internalshell.py:280 +msgid "Internal editor:" +msgstr "Editor interno:" + +#: spyder/widgets/internalshell.py:281 +msgid "External editor:" +msgstr "Editor externo:" + +#: spyder/widgets/internalshell.py:282 +msgid "Run script:" +msgstr "Executar script:" + +#: spyder/widgets/internalshell.py:283 +msgid "Remove references:" +msgstr "Remover referências:" + +#: spyder/widgets/internalshell.py:284 +msgid "System commands:" +msgstr "Comandos do sistema:" + +#: spyder/widgets/internalshell.py:285 +msgid "Python help:" +msgstr "Ajuda do Python:" + +#: spyder/widgets/internalshell.py:286 +msgid "GUI-based editor:" +msgstr "Editor gráfico:" + +#: spyder/widgets/ipythonconsole/client.py:189 +msgid "An error ocurred while starting the kernel" +msgstr "Um erro ocorreu enquanto o kernel era inicializado" + +#: spyder/widgets/ipythonconsole/client.py:220 +msgid "Restart kernel" +msgstr "Reiniciar kernel" + +#: spyder/widgets/ipythonconsole/client.py:240 +msgid "Stop the current command" +msgstr "Parar o comando atual" + +#: spyder/widgets/ipythonconsole/client.py:263 +msgid "Inspect current object" +msgstr "Inspecionar objeto" + +#: spyder/widgets/ipythonconsole/client.py:268 +msgid "Clear line or block" +msgstr "Limpar linha ou bloco" + +#: spyder/widgets/ipythonconsole/client.py:272 +msgid "Reset namespace" +msgstr "Resetar namespace" + +#: spyder/widgets/ipythonconsole/client.py:275 +msgid "Clear console" +msgstr "Limpar console" + +#: spyder/widgets/ipythonconsole/client.py:316 +msgid "Are you sure you want to restart the kernel?" +msgstr "Deseja mesmo reiniciar o kernel?" + +#: spyder/widgets/ipythonconsole/client.py:318 +msgid "Restart kernel?" +msgstr "Reiniciar kernel?" + +#: spyder/widgets/ipythonconsole/client.py:327 +msgid "Error restarting kernel: %s\n" +msgstr "Erro ao reiniciar kernel: %s\n" + +#: spyder/widgets/ipythonconsole/client.py:331 +msgid "" +"
Restarting kernel...\n" +"

" +msgstr "" +"
Reiniciando kernel..\n" +"

" + +#: spyder/widgets/ipythonconsole/client.py:336 +msgid "Cannot restart a kernel not started by Spyder\n" +msgstr "Não é possível reiniciar um kernel que não foi iniciado pelo Spyder\n" + +#: spyder/widgets/ipythonconsole/client.py:376 +msgid "Changing backend to Qt for Mayavi" +msgstr "Mudando a saída gráfica do Qt para Mayavi" + +#: spyder/widgets/ipythonconsole/client.py:387 +msgid "Connecting to kernel..." +msgstr "Conectando ao kernel..." + +#: spyder/widgets/ipythonconsole/namespacebrowser.py:76 +msgid "" +"Inspecting and setting values while debugging in IPython consoles is not " +"supported yet by Spyder." +msgstr "" +"Inspecionar e introduzir valores enquanto se estar debugando em consoles " +"IPython ainda não é suportado pelo Spyder." + +#: spyder/widgets/ipythonconsole/shell.py:149 +msgid "Reset IPython namespace" +msgstr "Resetar IPython namespace" + +#: spyder/widgets/ipythonconsole/shell.py:150 +msgid "" +"All user-defined variables will be removed.
Are you sure you want to " +"reset the namespace?" +msgstr "" +"Todas as variáveis definidas pelo usuário serão removidas.
Você tem " +"certeza que deseja resetar o namespace?" + +#: spyder/widgets/onecolumntree.py:52 +msgid "Collapse all" +msgstr "Reduzir tudo" + +#: spyder/widgets/onecolumntree.py:56 +msgid "Expand all" +msgstr "Expandir tudo" + +#: spyder/widgets/onecolumntree.py:60 +msgid "Restore" +msgstr "Restaurar" + +#: spyder/widgets/onecolumntree.py:61 +msgid "Restore original tree layout" +msgstr "Restaurar a disposição original" + +#: spyder/widgets/onecolumntree.py:65 +msgid "Collapse selection" +msgstr "Reduzir seleção" + +#: spyder/widgets/onecolumntree.py:69 +msgid "Expand selection" +msgstr "Expandir seleção" + +#: spyder/widgets/pathmanager.py:87 +msgid "Move to top" +msgstr "Mover para o inicio" + +#: spyder/widgets/pathmanager.py:93 +msgid "Move up" +msgstr "Mover para cima" + +#: spyder/widgets/pathmanager.py:99 +msgid "Move down" +msgstr "Mover para baixo" + +#: spyder/widgets/pathmanager.py:105 +msgid "Move to bottom" +msgstr "Mover para o final" + +#: spyder/widgets/pathmanager.py:116 spyder/widgets/pathmanager.py:231 +msgid "Add path" +msgstr "Adicionar caminho" + +#: spyder/widgets/pathmanager.py:121 spyder/widgets/pathmanager.py:214 +msgid "Remove path" +msgstr "Remover caminho" + +#: spyder/widgets/pathmanager.py:131 +msgid "Synchronize..." +msgstr "Sincronizar..." + +#: spyder/widgets/pathmanager.py:133 +msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" +msgstr "" +"Sincronizar a lista de caminhos do Spyder com a variável\n" +"de ambiente PYTHONPATH" + +#: spyder/widgets/pathmanager.py:145 +msgid "Synchronize" +msgstr "Sincronizar" + +#: spyder/widgets/pathmanager.py:146 +msgid "" +"This will synchronize Spyder's path list with PYTHONPATH environment " +"variable for current user, allowing you to run your Python modules outside " +"Spyder without having to configure sys.path.
Do you want to clear " +"contents of PYTHONPATH before adding Spyder's path list?" +msgstr "" +"Isso irá sincronizar a lista de caminhos do Spyder com a variável de " +"ambiente PYTHONPATHpara o usuário atual, permitindo executar seus " +"módulos de Python fora do Spyder sem precisar configurar o sys.path. " +"
Deseja limpar o conteúdo da PYTHONPATH antes de adicionar a lista de " +"caminhos do Spyder?" + +#: spyder/widgets/pathmanager.py:215 +msgid "Do you really want to remove selected path?" +msgstr "Você deseja mesmo remover o caminho selecionado?" + +#: spyder/widgets/pathmanager.py:232 +msgid "" +"This directory is already included in Spyder path list.
Do you want to " +"move it to the top of the list?" +msgstr "" +"Este diretório já está incluído na lista de caminhos do Spyder.
Deseja " +"mover ele para o topo da lista?" + +#: spyder/widgets/projects/configdialog.py:30 +msgid "Project preferences" +msgstr "Preferências do projeto" + +#: spyder/widgets/projects/configdialog.py:82 +#: spyder/widgets/projects/configdialog.py:119 +msgid "Restore data on startup" +msgstr "Restaurar data na inicialização" + +#: spyder/widgets/projects/configdialog.py:84 +#: spyder/widgets/projects/configdialog.py:121 +msgid "Save data on exit" +msgstr "Salvar data ao sair" + +#: spyder/widgets/projects/configdialog.py:86 +#: spyder/widgets/projects/configdialog.py:123 +msgid "Save history" +msgstr "Salvar histórico" + +#: spyder/widgets/projects/configdialog.py:88 +#: spyder/widgets/projects/configdialog.py:125 +msgid "Save non project files opened" +msgstr "Salvar arquivos que não são de projetos, atualmente abertos" + +#: spyder/widgets/projects/configdialog.py:111 +msgid "Code" +msgstr "Código" + +#: spyder/widgets/projects/configdialog.py:118 +msgid "Workspace" +msgstr "Espaço de trabalho" + +#: spyder/widgets/projects/configdialog.py:148 +#: spyder/widgets/projects/configdialog.py:155 +msgid "Version control" +msgstr "Controlador de versão" + +#: spyder/widgets/projects/configdialog.py:156 +msgid "Use version control" +msgstr "Usar controlador de versão" + +#: spyder/widgets/projects/configdialog.py:161 +msgid "Version control system" +msgstr "Sistema de Controle de Versão" + +#: spyder/widgets/projects/explorer.py:52 +msgid "Show horizontal scrollbar" +msgstr "Mostrar barra de rolagem horizontal" + +#: spyder/widgets/projects/explorer.py:114 +msgid "File %s already exists.
Do you want to overwrite it?" +msgstr "O arquivo %s já existe
Deseja sobrescrever ele?" + +#: spyder/widgets/projects/explorer.py:128 +msgid "Folder %s already exists." +msgstr "A Pasta %s já existe." + +#: spyder/widgets/projects/explorer.py:146 +msgid "copy" +msgstr "copiar" + +#: spyder/widgets/projects/explorer.py:148 +msgid "move" +msgstr "mover" + +#: spyder/widgets/projects/explorer.py:244 +msgid "" +"Do you really want to delete {filename}?

Note: This " +"action will only delete the project. Its files are going to be preserved on " +"disk." +msgstr "" + +#: spyder/widgets/projects/explorer.py:257 +#, fuzzy +msgid "" +"Unable to delete {varpath}

The error message was:" +"
{error}" +msgstr "Não foi possível mover %s

Mensagem de erro:
%s" + +#: spyder/widgets/projects/projectdialog.py:69 +msgid "New directory" +msgstr "Novo diretório" + +#: spyder/widgets/projects/projectdialog.py:70 +msgid "Existing directory" +msgstr "Diretório existente" + +#: spyder/widgets/projects/projectdialog.py:72 +msgid "Project name" +msgstr "Nome do projeto" + +#: spyder/widgets/projects/projectdialog.py:73 +msgid "Location" +msgstr "Localização" + +#: spyder/widgets/projects/projectdialog.py:74 +msgid "Project type" +msgstr "Tipo do projeto" + +#: spyder/widgets/projects/projectdialog.py:75 +msgid "Python version" +msgstr "Versão do Python" + +#: spyder/widgets/projects/projectdialog.py:83 +#: spyder/widgets/variableexplorer/importwizard.py:519 +msgid "Cancel" +msgstr "Cancelar" + +#: spyder/widgets/projects/projectdialog.py:84 +msgid "Create" +msgstr "Criar" + +#: spyder/widgets/projects/projectdialog.py:102 +msgid "Create new project" +msgstr "Criar um novo projeto" + +#: spyder/widgets/projects/type/__init__.py:215 +msgid "Empty project" +msgstr "Projeto vazio" + +#: spyder/widgets/projects/type/python.py:20 +msgid "Python project" +msgstr "Projeto Python" + +#: spyder/widgets/projects/type/python.py:76 +msgid "Python package" +msgstr "Pacote Python" + +#: spyder/widgets/pydocgui.py:110 +msgid "Module or package:" +msgstr "Módulo ou pacote:" + +#: spyder/widgets/shell.py:129 +msgid "Save history log..." +msgstr "Salvar histórico..." + +#: spyder/widgets/shell.py:131 +msgid "Save current history log (i.e. all inputs and outputs) in a text file" +msgstr "" +"Salvar o histórico atual (todas entradas e saídas) em um arquivo de texto" + +#: spyder/widgets/shell.py:257 +msgid "Save history log" +msgstr "Salvar histórico" + +#: spyder/widgets/shell.py:260 +msgid "History logs" +msgstr "Histórico de comandos" + +#: spyder/widgets/shell.py:271 +msgid "Unable to save file '%s'

Error message:
%s" +msgstr "" +"Não foi possível salvar arquivo '%s'

Mensagem de erro:
%s" + +#: spyder/widgets/shell.py:713 +msgid "Copy without prompts" +msgstr "Copiar sem os prompts" + +#: spyder/widgets/shell.py:716 spyder/widgets/shell.py:720 +msgid "Clear line" +msgstr "Limpar linha" + +#: spyder/widgets/shell.py:722 +msgid "Clear shell" +msgstr "Limpar shell" + +#: spyder/widgets/shell.py:726 +msgid "Clear shell contents ('cls' command)" +msgstr "Limpar conteúdo do shell (comando 'cls')" + +#: spyder/widgets/sourcecode/codeeditor.py:100 +msgid "Go to line:" +msgstr "Ir para a linha:" + +#: spyder/widgets/sourcecode/codeeditor.py:108 +msgid "Line count:" +msgstr "Número de linhas:" + +#: spyder/widgets/sourcecode/codeeditor.py:1305 +msgid "Breakpoint" +msgstr "Ponto de interrupção" + +#: spyder/widgets/sourcecode/codeeditor.py:1306 +msgid "Condition:" +msgstr "Condição:" + +#: spyder/widgets/sourcecode/codeeditor.py:1712 +msgid "Code analysis" +msgstr "Análise de código" + +#: spyder/widgets/sourcecode/codeeditor.py:1766 +msgid "To do" +msgstr "To do" + +#: spyder/widgets/sourcecode/codeeditor.py:2005 +msgid "Removal error" +msgstr "Erro de remoção" + +#: spyder/widgets/sourcecode/codeeditor.py:2006 +msgid "" +"It was not possible to remove outputs from this notebook. The error is:\n" +"\n" +msgstr "" +"Não foi possível remover as saídas deste notebook. O erro é:\n" +"\n" + +#: spyder/widgets/sourcecode/codeeditor.py:2528 +msgid "Clear all ouput" +msgstr "Limpar todas as saídas " + +#: spyder/widgets/sourcecode/codeeditor.py:2534 +msgid "Go to definition" +msgstr "Ver definição" + +#: spyder/widgets/sourcecode/codeeditor.py:2563 +msgid "Zoom reset" +msgstr "Resetar Zoom" + +#: spyder/widgets/status.py:25 +msgid "CPU and memory usage info in the status bar" +msgstr "Uso de CPU e memória na barra de status" + +#: spyder/widgets/status.py:94 +msgid "Memory:" +msgstr "Memória:" + +#: spyder/widgets/status.py:95 +msgid "" +"Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows " +"platforms" +msgstr "" +"Status de uso da memória: requer a biblioteca `psutil` (>=v0.3) em outras " +"plataformas" + +#: spyder/widgets/status.py:107 +msgid "CPU:" +msgstr "CPU:" + +#: spyder/widgets/status.py:108 +msgid "CPU usage status: requires the `psutil` (>=v0.3) library" +msgstr "Status de uso da CPU: requer a biblioteca `psutil` (>=v0.3)" + +#: spyder/widgets/status.py:130 +msgid "Permissions:" +msgstr "Permissões:" + +#: spyder/widgets/status.py:144 +msgid "End-of-lines:" +msgstr "Fim de linha:" + +#: spyder/widgets/status.py:158 +msgid "Encoding:" +msgstr "Codificação:" + +#: spyder/widgets/status.py:171 +msgid "Line:" +msgstr "Linha:" + +#: spyder/widgets/status.py:175 +msgid "Column:" +msgstr "Coluna:" + +#: spyder/widgets/tabs.py:146 +msgid "Browse tabs" +msgstr "Navegar pelas abas" + +#: spyder/widgets/tabs.py:275 +msgid "Close current tab" +msgstr "Fechar aba atual" + +#: spyder/widgets/variableexplorer/arrayeditor.py:497 +msgid "It was not possible to copy values for this array" +msgstr "Não foi possível copiar os valores dessa matriz" + +#: spyder/widgets/variableexplorer/arrayeditor.py:532 +#: spyder/widgets/variableexplorer/arrayeditor.py:565 +#: spyder/widgets/variableexplorer/dataframeeditor.py:544 +#: spyder/widgets/variableexplorer/dataframeeditor.py:584 +msgid "Format" +msgstr "Formato" + +#: spyder/widgets/variableexplorer/arrayeditor.py:537 +#: spyder/widgets/variableexplorer/dataframeeditor.py:548 +msgid "Resize" +msgstr "Redimensionar" + +#: spyder/widgets/variableexplorer/arrayeditor.py:566 +#: spyder/widgets/variableexplorer/dataframeeditor.py:585 +msgid "Float formatting" +msgstr "Formatação de ponto flutuante" + +#: spyder/widgets/variableexplorer/arrayeditor.py:574 +#: spyder/widgets/variableexplorer/dataframeeditor.py:594 +msgid "Format (%s) is incorrect" +msgstr "O formato (%s) está incorreto" + +#: spyder/widgets/variableexplorer/arrayeditor.py:609 +msgid "Array is empty" +msgstr "A matriz está vazia" + +#: spyder/widgets/variableexplorer/arrayeditor.py:612 +msgid "Arrays with more than 3 dimensions are not supported" +msgstr "Matrizes com mais de 3 dimensões não são suportadas" + +#: spyder/widgets/variableexplorer/arrayeditor.py:615 +msgid "The 'xlabels' argument length do no match array column number" +msgstr "" +"O argumento de comprimento 'xlabels' não corresponde com o número de colunas " +"da matriz" + +#: spyder/widgets/variableexplorer/arrayeditor.py:619 +msgid "The 'ylabels' argument length do no match array row number" +msgstr "" +"O argumento de comprimento 'ylabels' não corresponde com o número de linhas " +"da matriz" + +#: spyder/widgets/variableexplorer/arrayeditor.py:626 +msgid "%s arrays" +msgstr "%s matrizes" + +#: spyder/widgets/variableexplorer/arrayeditor.py:627 +msgid "%s are currently not supported" +msgstr "%s não é suportado no momento" + +#: spyder/widgets/variableexplorer/arrayeditor.py:634 +msgid "NumPy array" +msgstr "Matriz NumPy" + +#: spyder/widgets/variableexplorer/arrayeditor.py:636 +#: spyder/widgets/variableexplorer/arrayeditor.py:790 +msgid "Array editor" +msgstr "Editor de matrizes" + +#: spyder/widgets/variableexplorer/arrayeditor.py:638 +msgid "read only" +msgstr "somente leitura" + +#: spyder/widgets/variableexplorer/arrayeditor.py:668 +msgid "Record array fields:" +msgstr "Campos de matrizes registradas:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Data" +msgstr "Dados" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Mask" +msgstr "Máscara" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Masked data" +msgstr "Dados mascarados" + +#: spyder/widgets/variableexplorer/arrayeditor.py:691 +msgid "Axis:" +msgstr "Eixo:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:696 +msgid "Index:" +msgstr "Índice:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:709 +msgid "Warning: changes are applied separately" +msgstr "Aviso: mudanças são aplicadas de forma separada" + +#: spyder/widgets/variableexplorer/arrayeditor.py:710 +msgid "" +"For performance reasons, changes applied to masked array won't be reflected " +"in array's data (and vice-versa)." +msgstr "" +"Por motivos de desempenho, as mudanças\n" +"aplicadas a matrizes mascaradas não serão\n" +"refletidas nos dados da matriz\n" +"(e vice-versa)." + +#: spyder/widgets/variableexplorer/collectionseditor.py:116 +msgid "Index" +msgstr "Índice" + +#: spyder/widgets/variableexplorer/collectionseditor.py:121 +msgid "Tuple" +msgstr "Tupla" + +#: spyder/widgets/variableexplorer/collectionseditor.py:124 +msgid "List" +msgstr "Lista" + +#: spyder/widgets/variableexplorer/collectionseditor.py:127 +msgid "Dictionary" +msgstr "Dicionário " + +#: spyder/widgets/variableexplorer/collectionseditor.py:129 +msgid "Key" +msgstr "Chave" + +#: spyder/widgets/variableexplorer/collectionseditor.py:135 +msgid "Attribute" +msgstr "Atributo" + +#: spyder/widgets/variableexplorer/collectionseditor.py:137 +msgid "elements" +msgstr "elementos" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Size" +msgstr "Tamanho" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Type" +msgstr "Tipo" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Value" +msgstr "Valor" + +#: spyder/widgets/variableexplorer/collectionseditor.py:409 +msgid "" +"Opening this variable can be slow\n" +"\n" +"Do you want to continue anyway?" +msgstr "" +"Abrir essa variável pode ser um processo lento\n" +"\n" +"Deseja continuar mesmo assim?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:418 +msgid "" +"Spyder was unable to retrieve the value of this variable from the console." +"

The error mesage was:
%s" +msgstr "" +"O Spyder não foi capaz de receber o valor desta variável do console." +"

A mensagem de erro foi:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:583 +msgid "Edit item" +msgstr "Editar item" + +#: spyder/widgets/variableexplorer/collectionseditor.py:584 +msgid "Unable to assign data to item.

Error message:
%s" +msgstr "" +"Não foi possível atribuir dados ao item.

Mensagem de erro:
" +"%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:644 +msgid "Resize rows to contents" +msgstr "Redimensionar linhas para o conteúdo " + +#: spyder/widgets/variableexplorer/collectionseditor.py:655 +#: spyder/widgets/variableexplorer/collectionseditor.py:990 +#: spyder/widgets/variableexplorer/collectionseditor.py:1007 +msgid "Plot" +msgstr "Gráfico" + +#: spyder/widgets/variableexplorer/collectionseditor.py:659 +msgid "Histogram" +msgstr "Histograma" + +#: spyder/widgets/variableexplorer/collectionseditor.py:663 +msgid "Show image" +msgstr "Mostrar imagem" + +#: spyder/widgets/variableexplorer/collectionseditor.py:667 +#: spyder/widgets/variableexplorer/collectionseditor.py:1015 +msgid "Save array" +msgstr "Salvar matriz" + +#: spyder/widgets/variableexplorer/collectionseditor.py:671 +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Insert" +msgstr "Inserir" + +#: spyder/widgets/variableexplorer/collectionseditor.py:674 +#: spyder/widgets/variableexplorer/collectionseditor.py:898 +msgid "Remove" +msgstr "Remover" + +#: spyder/widgets/variableexplorer/collectionseditor.py:684 +#: spyder/widgets/variableexplorer/collectionseditor.py:919 +msgid "Duplicate" +msgstr "Duplicar" + +#: spyder/widgets/variableexplorer/collectionseditor.py:896 +msgid "Do you want to remove the selected item?" +msgstr "Você deseja remover o item selecionado?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:897 +msgid "Do you want to remove all selected items?" +msgstr "Você deseja remover todos os itens selecionados?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:917 +msgid "New variable name:" +msgstr "Novo nome da variável:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:920 +msgid "Variable name:" +msgstr "Nome da variável:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +msgid "Key:" +msgstr "Chave:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Value:" +msgstr "Valor:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:978 +msgid "Import error" +msgstr "Erro de importação" + +#: spyder/widgets/variableexplorer/collectionseditor.py:979 +msgid "Please install matplotlib or guiqwt." +msgstr "Por favor instale matplotlib ou guiqwt." + +#: spyder/widgets/variableexplorer/collectionseditor.py:991 +msgid "Unable to plot data.

Error message:
%s" +msgstr "" +"Não foi possível mostrar os dados.

Mensagem de erro:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1008 +msgid "Unable to show image.

Error message:
%s" +msgstr "" +"Não foi possível mostrar o gráfico.

Mensagem de erro:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1031 +msgid "Unable to save array

Error message:
%s" +msgstr "Não foi possível salvar matriz

Mensagem de erro:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1056 +msgid "It was not possible to copy this array" +msgstr "Não foi possível copiar essa matriz" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1081 +msgid "Clipboard contents" +msgstr "Conteúdo da área de transferência " + +#: spyder/widgets/variableexplorer/collectionseditor.py:1096 +msgid "Import from clipboard" +msgstr "Importar da área de transferência " + +#: spyder/widgets/variableexplorer/collectionseditor.py:1098 +msgid "Empty clipboard" +msgstr "Área de transferência vazia" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1099 +msgid "Nothing to be imported from clipboard." +msgstr "Não há nada para ser importado da área de transferência." + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To bool" +msgstr "Para booleano" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To complex" +msgstr "Para complexo" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To float" +msgstr "Para flutuante" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To int" +msgstr "Para inteiro" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:452 +msgid "To str" +msgstr "Para string" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:526 +msgid "%s editor" +msgstr "Editor de %s" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:558 +msgid "Column min/max" +msgstr "Min/max de coluna" + +#: spyder/widgets/variableexplorer/importwizard.py:116 +#: spyder/widgets/variableexplorer/importwizard.py:431 +msgid "Import as" +msgstr "Importar como" + +#: spyder/widgets/variableexplorer/importwizard.py:118 +msgid "data" +msgstr "dados" + +#: spyder/widgets/variableexplorer/importwizard.py:122 +msgid "code" +msgstr "código" + +#: spyder/widgets/variableexplorer/importwizard.py:125 +#: spyder/widgets/variableexplorer/importwizard.py:504 +msgid "text" +msgstr "texto" + +#: spyder/widgets/variableexplorer/importwizard.py:138 +msgid "Column separator:" +msgstr "Separador de colunas:" + +#: spyder/widgets/variableexplorer/importwizard.py:142 +msgid "Tab" +msgstr "Tabulação" + +#: spyder/widgets/variableexplorer/importwizard.py:145 +#: spyder/widgets/variableexplorer/importwizard.py:163 +msgid "other" +msgstr "outro" + +#: spyder/widgets/variableexplorer/importwizard.py:156 +msgid "Row separator:" +msgstr "Separador de linha:" + +#: spyder/widgets/variableexplorer/importwizard.py:160 +msgid "EOL" +msgstr "Fim de linha" + +#: spyder/widgets/variableexplorer/importwizard.py:175 +msgid "Additional options" +msgstr "Opções adicionais" + +#: spyder/widgets/variableexplorer/importwizard.py:179 +msgid "Skip rows:" +msgstr "Pular linhas:" + +#: spyder/widgets/variableexplorer/importwizard.py:190 +msgid "Comments:" +msgstr "Comentários:" + +#: spyder/widgets/variableexplorer/importwizard.py:196 +msgid "Transpose" +msgstr "Transpor" + +#: spyder/widgets/variableexplorer/importwizard.py:434 +msgid "array" +msgstr "matriz" + +#: spyder/widgets/variableexplorer/importwizard.py:439 +msgid "list" +msgstr "lista" + +#: spyder/widgets/variableexplorer/importwizard.py:444 +msgid "DataFrame" +msgstr "DataFrame" + +#: spyder/widgets/variableexplorer/importwizard.py:487 +#: spyder/widgets/variableexplorer/importwizard.py:571 +msgid "Import wizard" +msgstr "Assistente de importação" + +#: spyder/widgets/variableexplorer/importwizard.py:492 +msgid "Raw text" +msgstr "Texto sem formatação" + +#: spyder/widgets/variableexplorer/importwizard.py:495 +msgid "variable_name" +msgstr "nome_da_variável" + +#: spyder/widgets/variableexplorer/importwizard.py:506 +msgid "table" +msgstr "tabela" + +#: spyder/widgets/variableexplorer/importwizard.py:507 +msgid "Preview" +msgstr "Pré-Visualização" + +#: spyder/widgets/variableexplorer/importwizard.py:511 +msgid "Variable Name" +msgstr "Nome da variável" + +#: spyder/widgets/variableexplorer/importwizard.py:534 +msgid "Done" +msgstr "Feito" + +#: spyder/widgets/variableexplorer/importwizard.py:572 +msgid "" +"Unable to proceed to next step

Please check your entries." +"

Error message:
%s" +msgstr "" +"Não foi possível prosseguir para o próximo passo

Por favor " +"revise seus dados

Mensagem de erro:
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:196 +msgid "Refresh" +msgstr "Atualizar" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:200 +msgid "Refresh periodically" +msgstr "Atualizar periodicamente" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:207 +#: spyder/widgets/variableexplorer/namespacebrowser.py:487 +msgid "Import data" +msgstr "Importar data" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:210 +#: spyder/widgets/variableexplorer/namespacebrowser.py:565 +#: spyder/widgets/variableexplorer/namespacebrowser.py:584 +msgid "Save data" +msgstr "Salvar data" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:215 +msgid "Save data as..." +msgstr "Salvar data como..." + +#: spyder/widgets/variableexplorer/namespacebrowser.py:227 +msgid "Exclude references which name starts with an underscore" +msgstr "Excluir variáveis cujo nomes comecem com sublinhado" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:235 +msgid "Exclude references which name is uppercase" +msgstr "" +"Excluir variáveis cujo nome esteja\n" +"completamente em maiúsculas" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:242 +msgid "Exclude references which name starts with an uppercase character" +msgstr "Excluir variáveis cujo nome comece com maiúsculas. " + +#: spyder/widgets/variableexplorer/namespacebrowser.py:250 +msgid "" +"Exclude references to unsupported data types (i.e. which won't be handled/" +"saved correctly)" +msgstr "" +"Excluir variáveis que guardam tipos de dados não suportados\n" +"(aqueles que não podem ser manipulados e armazenados\n" +" corretamente)" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:345 +msgid "Object %s is not picklable" +msgstr "O objeto %s não é picklable" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:507 +msgid "" +"Unsupported file extension '%s'

Would you like to import it " +"anyway (by selecting a known file format)?" +msgstr "" +"Tipo de arquivo não suportado '%s'

Deseja importar mesmo assim " +"(selecionando um formato conhecido)?" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:515 +msgid "Open file as:" +msgstr "Abrir arquivo como:" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:553 +msgid "Unable to load '%s'

Error message:
%s" +msgstr "Não foi possível carregar '%s'

Mensagem de erro:
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:585 +msgid "Unable to save current workspace

Error message:
%s" +msgstr "" +"Não foi possível salvar o espaço de trabalho atual

Mensagem de " +"erro:
%s" + +#: spyder/widgets/variableexplorer/texteditor.py:74 +msgid "Text editor" +msgstr "Editor de texto" + +#: spyder/widgets/variableexplorer/utils.py:29 +msgid "View and edit DataFrames and Series in the Variable Explorer" +msgstr "Ver e editar DataFrames e Series no Explorador de Variáveis" + +#: spyder/widgets/variableexplorer/utils.py:34 +msgid "View and edit two and three dimensional arrays in the Variable Explorer" +msgstr "" +"Ver e editar duas e três matrizes dimensionais no Explorador de Variáveis" + +#: spyder/workers/updates.py:89 spyder/workers/updates.py:91 +msgid "Unable to retrieve information." +msgstr "Não foi possível receber a informação." + +#: spyder/workers/updates.py:93 +msgid "" +"Unable to connect to the internet.

Make sure the connection is " +"working properly." +msgstr "" +"Não foi possível conectar a internet.

Tenha certeza se a conexão " +"está realmente funcionando." + +#: spyder/workers/updates.py:96 +msgid "Unable to check for updates." +msgstr "Não foi possível verificar por atualizações." + +#~ msgid "" +#~ "\n" +#~ "\n" +#~ "{0}" +#~ msgstr "" +#~ "\n" +#~ "\n" +#~ "{0}" + +#~ msgid "Truncate values" +#~ msgstr "Abreviar valores" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/locale/ru/LC_MESSAGES/spyder.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/locale/ru/LC_MESSAGES/spyder.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder/locale/ru/LC_MESSAGES/spyder.po spyder-3.0.2+dfsg1/spyder/locale/ru/LC_MESSAGES/spyder.po --- spyder-2.3.8+dfsg1/spyder/locale/ru/LC_MESSAGES/spyder.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/locale/ru/LC_MESSAGES/spyder.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,5683 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# FULL NAME , 2014. +# Zgarbul Andrey , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: 3.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2016-11-11 18:43+0300\n" +"Last-Translator: Roman Kulagin \n" +"Language-Team: русский \n" +"Language: ru_RU\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.5.4\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: spyder/app/mainwindow.py:125 +msgid "Initializing..." +msgstr "Инициализация..." + +#: spyder/app/mainwindow.py:241 +msgid "Numpy and Scipy documentation" +msgstr "Документация Numpy и Scipy" + +#: spyder/app/mainwindow.py:243 spyder/app/mainwindow.py:1027 +msgid "Matplotlib documentation" +msgstr "Документация Matplotlib" + +#: spyder/app/mainwindow.py:246 +msgid "PyQt4 Reference Guide" +msgstr "Справочное руководство по PyQt4" + +#: spyder/app/mainwindow.py:249 +msgid "PyQt4 API Reference" +msgstr "Справка по API PyQt4" + +#: spyder/app/mainwindow.py:251 +msgid "Python(x,y)" +msgstr "Python(x,y)" + +#: spyder/app/mainwindow.py:253 +msgid "WinPython" +msgstr "WinPython" + +#: spyder/app/mainwindow.py:525 +msgid "Close current pane" +msgstr "Закрыть текущую панель" + +#: spyder/app/mainwindow.py:530 +msgid "Lock panes" +msgstr "Закрепить панели" + +#: spyder/app/mainwindow.py:537 +msgid "Use next layout" +msgstr "Исп. следующую компоновку" + +#: spyder/app/mainwindow.py:541 +msgid "Use previous layout" +msgstr "Исп. предыдущую компоновку" + +#: spyder/app/mainwindow.py:560 spyder/widgets/sourcecode/codeeditor.py:2505 +msgid "Undo" +msgstr "Отменить" + +#: spyder/app/mainwindow.py:562 spyder/widgets/sourcecode/codeeditor.py:2508 +msgid "Redo" +msgstr "Вернуть" + +#: spyder/app/mainwindow.py:564 spyder/widgets/shell.py:121 +#: spyder/widgets/sourcecode/codeeditor.py:2514 +#: spyder/widgets/variableexplorer/arrayeditor.py:453 +#: spyder/widgets/variableexplorer/collectionseditor.py:649 +#: spyder/widgets/variableexplorer/dataframeeditor.py:445 +msgid "Copy" +msgstr "Копировать" + +#: spyder/app/mainwindow.py:566 spyder/widgets/shell.py:117 +#: spyder/widgets/sourcecode/codeeditor.py:2511 +msgid "Cut" +msgstr "Вырезать" + +#: spyder/app/mainwindow.py:568 spyder/widgets/shell.py:125 +#: spyder/widgets/sourcecode/codeeditor.py:2517 +#: spyder/widgets/variableexplorer/collectionseditor.py:646 +msgid "Paste" +msgstr "Вставить" + +#: spyder/app/mainwindow.py:571 spyder/widgets/shell.py:138 +#: spyder/widgets/sourcecode/codeeditor.py:2520 +msgid "Select All" +msgstr "Выделить всё" + +#: spyder/app/mainwindow.py:581 spyder/plugins/editor.py:1353 +msgid "&File" +msgstr "&Файл" + +#: spyder/app/mainwindow.py:582 spyder/plugins/editor.py:1345 +msgid "File toolbar" +msgstr "Панель файлов" + +#: spyder/app/mainwindow.py:586 spyder/plugins/editor.py:1354 +msgid "&Edit" +msgstr "&Правка" + +#: spyder/app/mainwindow.py:587 spyder/plugins/editor.py:1350 +msgid "Edit toolbar" +msgstr "Панель правки" + +#: spyder/app/mainwindow.py:591 spyder/plugins/editor.py:1355 +msgid "&Search" +msgstr "П&оиск" + +#: spyder/app/mainwindow.py:592 spyder/plugins/editor.py:1346 +msgid "Search toolbar" +msgstr "Панель поиска" + +#: spyder/app/mainwindow.py:596 spyder/plugins/editor.py:1356 +msgid "Sour&ce" +msgstr "&Документ" + +#: spyder/app/mainwindow.py:597 spyder/plugins/editor.py:1347 +msgid "Source toolbar" +msgstr "Панель кода" + +#: spyder/app/mainwindow.py:601 spyder/plugins/editor.py:780 +#: spyder/plugins/editor.py:1357 +msgid "&Run" +msgstr "&Запуск" + +#: spyder/app/mainwindow.py:602 spyder/plugins/editor.py:1348 +msgid "Run toolbar" +msgstr "Панель запуска" + +#: spyder/app/mainwindow.py:606 spyder/plugins/editor.py:739 +msgid "&Debug" +msgstr "&Отладка" + +#: spyder/app/mainwindow.py:607 spyder/plugins/editor.py:1349 +msgid "Debug toolbar" +msgstr "Панель отладки" + +#: spyder/app/mainwindow.py:611 +msgid "C&onsoles" +msgstr "&Консоли" + +#: spyder/app/mainwindow.py:614 +msgid "&Projects" +msgstr "&Проекты" + +#: spyder/app/mainwindow.py:617 spyder/plugins/editor.py:1358 +msgid "&Tools" +msgstr "&Инструменты" + +#: spyder/app/mainwindow.py:620 +msgid "&View" +msgstr "&Вид" + +#: spyder/app/mainwindow.py:623 +msgid "&Help" +msgstr "Спр&авка" + +#: spyder/app/mainwindow.py:628 +msgid "Welcome to Spyder!" +msgstr "Добро пожаловать в Spyder!" + +#: spyder/app/mainwindow.py:633 +msgid "Pre&ferences" +msgstr "&Параметры" + +#: spyder/app/mainwindow.py:640 spyder/widgets/pathmanager.py:49 +msgid "PYTHONPATH manager" +msgstr "Менеджер PYTHONPATH" + +#: spyder/app/mainwindow.py:643 +msgid "Python Path Manager" +msgstr "Менеджер путей Python" + +#: spyder/app/mainwindow.py:646 +msgid "Update module names list" +msgstr "Обновить список модулей" + +#: spyder/app/mainwindow.py:649 +msgid "Refresh list of module names available in PYTHONPATH" +msgstr "Обновить список имён модулей, доступных в PYTHONPATH" + +#: spyder/app/mainwindow.py:652 +msgid "Reset Spyder to factory defaults" +msgstr "Вернуть Spyder к заводским настройкам" + +#: spyder/app/mainwindow.py:657 +msgid "Current user environment variables..." +msgstr "Переменные cреды текущего пользователя..." + +#: spyder/app/mainwindow.py:659 +msgid "" +"Show and edit current user environment variables in Windows registry (i.e. " +"for all sessions)" +msgstr "" +"Показывать и редактировать переменные среды в реестре Windows (т.е. для всех " +"сессий)" + +#: spyder/app/mainwindow.py:668 spyder/app/mainwindow.py:1123 +msgid "External Tools" +msgstr "Внешние инструменты" + +#: spyder/app/mainwindow.py:672 +msgid "Python(x,y) launcher" +msgstr "Загрузчик Python(x,y)" + +#: spyder/app/mainwindow.py:679 +msgid "WinPython control panel" +msgstr "контрольная панель WinPython" + +#: spyder/app/mainwindow.py:688 +msgid "Qt Designer" +msgstr "Qt дизайнер" + +#: spyder/app/mainwindow.py:693 +msgid "Qt Linguist" +msgstr "Qt Linguist" + +#: spyder/app/mainwindow.py:699 +msgid "Qt examples" +msgstr "Примеры Qt" + +#: spyder/app/mainwindow.py:720 +msgid "guidata examples" +msgstr "примеры guidata" + +#: spyder/app/mainwindow.py:731 +msgid "guiqwt examples" +msgstr "примеры guiqwt" + +#: spyder/app/mainwindow.py:736 +msgid "Sift" +msgstr "Sift" + +#: spyder/app/mainwindow.py:746 +msgid "ViTables" +msgstr "ViTables" + +#: spyder/app/mainwindow.py:760 +msgid "Fullscreen mode" +msgstr "Полноэкранный режим" + +#: spyder/app/mainwindow.py:772 +msgid "Main toolbar" +msgstr "Главная панель" + +#: spyder/app/mainwindow.py:781 +msgid "" +"Spyder Internal Console\n" +"\n" +"This console is used to report application\n" +"internal errors and to inspect Spyder\n" +"internals with the following commands:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Please don't use it to run your code\n" +"\n" +msgstr "" +"Встроенная консоль Spyder\n" +"\n" +"Эта консоль предназначена для вывода внутренних\n" +"ошибок приложени и изучения внутреннего\n" +"состояния Spyder следующими командами:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Пожалуйста, не используйте её для\n" +"запуска своего кода\n" + +#: spyder/app/mainwindow.py:798 +msgid "Loading help..." +msgstr "Загрузка справки..." + +#: spyder/app/mainwindow.py:805 +msgid "Loading outline explorer..." +msgstr "Загузка менеджера структуры..." + +#: spyder/app/mainwindow.py:813 +msgid "Loading editor..." +msgstr "Загрузка редактора..." + +#: spyder/app/mainwindow.py:819 spyder/plugins/console.py:134 +#: spyder/widgets/ipythonconsole/client.py:280 +msgid "&Quit" +msgstr "В&ыход" + +#: spyder/app/mainwindow.py:821 spyder/plugins/console.py:136 +msgid "Quit" +msgstr "Выход" + +#: spyder/app/mainwindow.py:825 +msgid "&Restart" +msgstr "Пе&резапуск" + +#: spyder/app/mainwindow.py:827 +msgid "Restart" +msgstr "Перезапуск" + +#: spyder/app/mainwindow.py:844 +msgid "Loading file explorer..." +msgstr "Загрузка файлового менеджера..." + +#: spyder/app/mainwindow.py:851 +msgid "Loading history plugin..." +msgstr "Загрузка модуля истории..." + +#: spyder/app/mainwindow.py:862 +msgid "Loading online help..." +msgstr "Загрузка онлайн-помощи..." + +#: spyder/app/mainwindow.py:867 +msgid "Loading project explorer..." +msgstr "Загрузка менеджера проектов..." + +#: spyder/app/mainwindow.py:874 +msgid "Loading external console..." +msgstr "Загрузка внешней консоли..." + +#: spyder/app/mainwindow.py:880 +msgid "Loading namespace browser..." +msgstr "Загрузка менеджера пространств имён..." + +#: spyder/app/mainwindow.py:887 +msgid "Loading IPython console..." +msgstr "Загрузка консоли IPython..." + +#: spyder/app/mainwindow.py:892 +msgid "Setting up main window..." +msgstr "Создание главного окна..." + +#: spyder/app/mainwindow.py:895 +msgid "Dependencies..." +msgstr "Зависимости..." + +#: spyder/app/mainwindow.py:899 +msgid "Report issue..." +msgstr "Отправить отчёт о проблеме..." + +#: spyder/app/mainwindow.py:903 +msgid "Spyder support..." +msgstr "Поддержка Spyder..." + +#: spyder/app/mainwindow.py:906 +msgid "Check for updates..." +msgstr "Проверить обновления..." + +#: spyder/app/mainwindow.py:929 +msgid "Spyder documentation" +msgstr "Документация Spyder" + +#: spyder/app/mainwindow.py:934 +msgid "Spyder tutorial" +msgstr "Руководство Spyder" + +#: spyder/app/mainwindow.py:941 +msgid "Interactive tours" +msgstr "Интерактивные туры" + +#: spyder/app/mainwindow.py:969 +msgid "Python documentation" +msgstr "Документация Python" + +#: spyder/app/mainwindow.py:975 spyder/app/mainwindow.py:1019 +msgid "IPython documentation" +msgstr "Документация IPython" + +#: spyder/app/mainwindow.py:976 +msgid "Intro to IPython" +msgstr "Введение в IPython" + +#: spyder/app/mainwindow.py:978 +msgid "Quick reference" +msgstr "Краткий справочник" + +#: spyder/app/mainwindow.py:980 +msgid "Console help" +msgstr "Консольная справка" + +#: spyder/app/mainwindow.py:1017 +msgid "Python(x,y) documentation folder" +msgstr "Каталог с документацией Python(x,y)" + +#: spyder/app/mainwindow.py:1021 +msgid "guidata documentation" +msgstr "Документация guidata" + +#: spyder/app/mainwindow.py:1024 +msgid "guiqwt documentation" +msgstr "Документация guiqwt" + +#: spyder/app/mainwindow.py:1030 +msgid "NumPy documentation" +msgstr "Документация NumPy" + +#: spyder/app/mainwindow.py:1032 +msgid "NumPy reference guide" +msgstr "Справочное руководство NumPy" + +#: spyder/app/mainwindow.py:1034 +msgid "NumPy user guide" +msgstr "Руководство пользователя NumPy" + +#: spyder/app/mainwindow.py:1036 +msgid "SciPy documentation" +msgstr "Документация SciPy" + +#: spyder/app/mainwindow.py:1043 +msgid "Installed Python modules" +msgstr "Установленные модули Python" + +#: spyder/app/mainwindow.py:1047 +msgid "Online documentation" +msgstr "Онлайн документация" + +#: spyder/app/mainwindow.py:1059 +msgid "Qt documentation" +msgstr "Документация Qt" + +#: spyder/app/mainwindow.py:1065 +msgid "About %s..." +msgstr "О %s..." + +#: spyder/app/mainwindow.py:1089 +msgid "Panes" +msgstr "Плавающие окна" + +#: spyder/app/mainwindow.py:1091 +msgid "Toolbars" +msgstr "Панели инструментов" + +#: spyder/app/mainwindow.py:1092 +msgid "Window layouts" +msgstr "Компоновка окон" + +#: spyder/app/mainwindow.py:1101 spyder/app/mainwindow.py:1894 +#: spyder/app/mainwindow.py:1895 +msgid "Show toolbars" +msgstr "Показывать панели инструментов" + +#: spyder/app/mainwindow.py:1116 +msgid "Attached console window (debugging)" +msgstr "Прикреплённая консоль (отладка)" + +#: spyder/app/mainwindow.py:1295 spyder/plugins/projects.py:244 +#: spyder/widgets/explorer.py:639 spyder/widgets/explorer.py:744 +#: spyder/widgets/externalshell/pythonshell.py:533 +#: spyder/widgets/externalshell/systemshell.py:105 +#: spyder/widgets/variableexplorer/arrayeditor.py:573 +#: spyder/widgets/variableexplorer/collectionseditor.py:417 +#: spyder/widgets/variableexplorer/dataframeeditor.py:593 +msgid "Error" +msgstr "Ошибка" + +#: spyder/app/mainwindow.py:1296 +msgid "" +"You have missing dependencies!

%s

Please " +"install them to avoid this message.

Note: Spyder could " +"work without some of these dependencies, however to have a smooth experience " +"when using Spyder we strongly recommend you to install all the listed " +"missing dependencies.

Failing to install these dependencies might " +"result in bugs. Please be sure that any found bugs are not the direct result " +"of missing dependencies, prior to reporting a new issue." +msgstr "" +"У Вас есть недостающие зависимости!

%s

Пожалуйста, установите их, чтобы это сообщение исчезло.

Примечание: Возможно, Spyder сможет работать без некоторых " +"из них, однако для беспроблемного использования Spyder мы настоятельно рекомендуем установить все приведённые недостающие зависимости." +"

Неудачная установка этих зависимостей могла привести к ошибкам. " +"Пожалуйста, прежде чем сообщать о новой проблеме, убедитесь, что любые " +"найденные ошибки не являются прямым результатот отсутствия некоторых " +"зависимостей." + +#: spyder/app/mainwindow.py:1741 +msgid "Spyder Default Layout" +msgstr "Стандартная компоновка Spyder" + +#: spyder/app/mainwindow.py:1759 +msgid "Save current layout" +msgstr "Сохранить текущую компоновку" + +#: spyder/app/mainwindow.py:1763 +msgid "Layout preferences" +msgstr "Параметры компоновки" + +#: spyder/app/mainwindow.py:1767 +msgid "Reset to spyder default" +msgstr "Вернуть компоновку по умолчанию" + +#: spyder/app/mainwindow.py:1787 spyder/app/mainwindow.py:1809 +#: spyder/app/mainwindow.py:1872 spyder/app/mainwindow.py:2656 +#: spyder/plugins/configdialog.py:1254 spyder/plugins/externalconsole.py:446 +#: spyder/plugins/ipythonconsole.py:119 spyder/plugins/ipythonconsole.py:835 +#: spyder/plugins/maininterpreter.py:167 spyder/plugins/maininterpreter.py:195 +#: spyder/utils/environ.py:100 spyder/utils/environ.py:113 +#: spyder/widgets/variableexplorer/arrayeditor.py:496 +#: spyder/widgets/variableexplorer/collectionseditor.py:408 +#: spyder/widgets/variableexplorer/collectionseditor.py:1055 +msgid "Warning" +msgstr "Предупреждение" + +#: spyder/app/mainwindow.py:1788 +msgid "" +"Window layout will be reset to default settings: this affects window " +"position, size and dockwidgets.\n" +"Do you want to continue?" +msgstr "" +"Компоновка окон будет восстановлена по умолчанию: это отразится на позиции, " +"размере и объединении окон.\n" +"Хотите продолжить?" + +#: spyder/app/mainwindow.py:1810 +msgid "" +"Layout %s will be " +"overwritten. Do you want to " +"continue?" +msgstr "Компоновка %s будет перезаписана. Хотите продолжить?" + +#: spyder/app/mainwindow.py:1873 +msgid "Quick switch layout #%s has not yet been defined." +msgstr "Быстрое переключение компоновки #%s не было определено." + +#: spyder/app/mainwindow.py:1891 spyder/app/mainwindow.py:1892 +msgid "Hide toolbars" +msgstr "Скрыть панели инструментов" + +#: spyder/app/mainwindow.py:2185 spyder/app/mainwindow.py:2186 +msgid "Maximize current pane" +msgstr "Развернуть текущую панель" + +#: spyder/app/mainwindow.py:2189 +msgid "Restore current pane" +msgstr "Восстановить текущую панель" + +#: spyder/app/mainwindow.py:2190 +msgid "Restore pane to its original size" +msgstr "Вернуть панель к первоначальному размеру" + +#: spyder/app/mainwindow.py:2274 +msgid "About %s" +msgstr "О %s" + +#: spyder/app/mainwindow.py:2401 spyder/plugins/editor.py:158 +#: spyder/plugins/runconfig.py:322 spyder/plugins/runconfig.py:444 +#: spyder/plugins/runconfig.py:449 spyder/utils/programs.py:285 +#: spyder/widgets/explorer.py:271 +#: spyder/widgets/externalshell/baseshell.py:127 +msgid "Run" +msgstr "Запуск" + +#: spyder/app/mainwindow.py:2402 +msgid "Running an external system terminal is not supported on platform %s." +msgstr "" +"Запуск внешнего системного терминала не поддерживается на платформе %s." + +#: spyder/app/mainwindow.py:2657 +msgid "" +"Spyder will restart and reset to default settings:

Do you want to " +"continue?" +msgstr "" +"Spyder будет перезапущен, настройки возвращены по умолчанию:

Хотите " +"продолжить?" + +#: spyder/app/mainwindow.py:2754 spyder/widgets/helperwidgets.py:250 +msgid "Spyder updates" +msgstr "Обновления Spyder" + +#: spyder/app/mainwindow.py:2755 spyder/plugins/configdialog.py:819 +msgid "Check for updates on startup" +msgstr "Проверять обновления при старте" + +#: spyder/app/mainwindow.py:2775 +msgid "" +"Spyder %s is available!

Please use your package manager to " +"update Spyder or go to our Releases page to download this " +"new version.

If you are not sure how to proceed to update Spyder " +"please refer to our Installation instructions." +msgstr "" +"Доступен Spyder %s!

Пожалйста, используйте свой пакетный " +"менеджер или перейдите на нашу страницу Релизов для " +"загрузки новой версии.

Если Вы не уверены, как произвести обновление " +"Spyder, ознакомьтесь с нашими инструкциями по Установке." + +#: spyder/app/mainwindow.py:2787 +msgid "Spyder is up to date." +msgstr "Spyder обновлён." + +#: spyder/app/restart.py:133 +msgid "" +"It was not possible to close the previous Spyder instance.\n" +"Restart aborted." +msgstr "" +"Не удалось закрыть предыдущий экземпляр Spyder.\n" +"Перезапуск прерван." + +#: spyder/app/restart.py:135 +msgid "" +"Spyder could not reset to factory defaults.\n" +"Restart aborted." +msgstr "" +"Невозможно сбросить настройки Spyder в значения по умолчанию.\n" +"Операция прервана." + +#: spyder/app/restart.py:137 +msgid "" +"It was not possible to restart Spyder.\n" +"Operation aborted." +msgstr "" +"Невозможно перезапустить Spyder.\n" +"Операция прервана." + +#: spyder/app/restart.py:139 +msgid "Spyder exit error" +msgstr "Ошибка выхода из Spyder" + +#: spyder/app/restart.py:140 +msgid "Spyder reset error" +msgstr "Ошибка сброса настроек Spyder" + +#: spyder/app/restart.py:141 +msgid "Spyder restart error" +msgstr "Ошибка перезапуска Spyder" + +#: spyder/app/restart.py:165 +msgid "Closing Spyder" +msgstr "Закрытие Spyder" + +#: spyder/app/restart.py:239 +msgid "Resetting Spyder to defaults" +msgstr "Восстановить настройки по умолчанию" + +#: spyder/app/restart.py:271 +msgid "Restarting" +msgstr "Перезапуск" + +#: spyder/app/tour.py:124 +msgid "Welcome to the Introduction tour" +msgstr "Добро пожаловать в обучающий тур" + +#: spyder/app/tour.py:125 +msgid "" +"Spyder is a powerful Interactive Development Environment (or IDE) for " +"the Python programming language.

Here we are going to guide you " +"through its most important features.

Please use the arrow keys or " +"click on the buttons below to move along the tour." +msgstr "" +"Spyder - мощная Интерактивная Среда Разработки (или IDE) для языка " +"программирования Python.

Здесь мы ознакомим Вас с наиболее важными " +"возможностями.

Пожалуйста, используйте клавиши со стрелками или " +"кнопки внизу, чтобы двигаться вдоль тура." + +#: spyder/app/tour.py:134 +msgid "The Editor" +msgstr "Текстовый редактор" + +#: spyder/app/tour.py:135 +msgid "" +"This is the pane where you write Python code before evaluating it. You can " +"get automatic suggestions and completions while writing, by pressing the " +"Tab key next to a given text.

The Editor comes with a line " +"number area (highlighted here in red), where Spyder shows warnings and " +"syntax errors. They can help you to detect potential problems before running " +"the code.

You can also set debug breakpoints in the line number area, " +"by doing a double click next to a non-empty line." +msgstr "" +"Это панель, в которой Вы пишете код Python перед его выполнением. Вы можете " +"получать предложения и автодополнение по ходу ввода, нажимая клавишу Tab после ввода текста.

В Редактор входит панель номеров строк " +"(подсвечена здесь красным), на которой Spyder показывает предупреждения и " +"синтаксические ошибки. Они могут помочь Вам найти потенциальные проблемы " +"перед запуском кода.

Также Вы можете установить отладочные точки " +"останова на панели номеров строк двойным щелчком рядом с непустой строкой." + +#: spyder/app/tour.py:150 +msgid "The IPython console" +msgstr "Консоль IPython" + +#: spyder/app/tour.py:151 +msgid "" +"This is one of panes where you can run or execute the code you wrote on the " +"Editor. To do it you need to press the F5 key.

This console " +"comes with several useful features that greatly improve your programming " +"workflow (like syntax highlighting and inline plots). If you want to know " +"more about them, please follow this link.

Please " +"click on the button below to run some simple code in this console. This will " +"be useful to show you other important features." +msgstr "" +"Это одна из панелей, где можно запустить или выполнить код, написанный в " +"Редакторе. Чтобы это сделать нужно нажать клавишуF5.

В этой " +"консоли есть несколько полезных функций, которые значительно улучшают Ваш " +"процесс программирования (такие как подсветка синтаксиса и встроенные " +"графики). Если вы хотите узнать о них больше, перейдите по этой ссылке.

Пожалуйста, нажмите на кнопку ниже, чтобы " +"запустить простой код в этой консоли. Это будет полезно, чтобы показать Вам " +"другие важные функции." + +#: spyder/app/tour.py:167 +msgid "The Variable Explorer" +msgstr "Менеджер Переменных" + +#: spyder/app/tour.py:168 +msgid "" +"In this pane you can view and edit the variables generated during the " +"execution of a program, or those entered directly in one of Spyder consoles." +"

As you can see, the Variable Explorer is showing the variables " +"generated during the last step of this tour. By doing a double-click on any " +"of them, a new window will be opened, where you can inspect and modify their " +"contents." +msgstr "" +"В этой панели можно изучать и редактировать переменные, созданные в ходе " +"работы программы или введенные напрямую в консолях Spyder.

Как можно " +"увидеть, Менеджер Переменных показывает переменные, созданные на предыдущем " +"шаге тура. Двойным щелчком на любом из них открывается окно, где можно " +"тщательно изучить и изменить содержимое." + +#: spyder/app/tour.py:180 +msgid "The Python console" +msgstr "Консоль Python" + +#: spyder/app/tour.py:181 +msgid "" +"You can also run your code on a Python console. These consoles are useful " +"because they let you run a file in a console dedicated only to it.To select " +"this behavior, please press the F6 key.

By pressing the button " +"below and then focusing the Variable Explorer, you will notice that Python " +"consoles are also connected to that pane, and that the Variable Explorer " +"only shows the variables of the currently focused console." +msgstr "" +"Вы также можете запустить свой код в консоли Python. Эти консоли полезны, " +"поскольку они позволяют запускать файл в консоли, предназначенной только для " +"него. Чтобы выбрать такое поведение, нажмите клавишу F6.

Нажав " +"на кнопку ниже и перейдя к Менеджеру Переменных, Вы заметите, что консоли " +"Python также подключены к этой панели, и что Менеджер Переменных отображает " +"только переменные текущей выбранной консоли." + +#: spyder/app/tour.py:195 spyder/plugins/help.py:485 +#: spyder/plugins/help.py:929 spyder/widgets/internalshell.py:270 +msgid "Help" +msgstr "Справка" + +#: spyder/app/tour.py:196 +msgid "" +"This pane displays documentation of the functions, classes, methods or " +"modules you are currently using in the Editor or the Consoles.

To use " +"it, you need to press Ctrl+I in front of an object. If that object " +"has some documentation associated with it, it will be displayed here." +msgstr "" +"Эта панель показывает документацию по функциям, классам, методам и модулям, " +"используемым сейчас в Редакторе или Консолях.

Чтобы воспользоваться " +"ею, нажмите Ctrl+I напротив объекта. Если у объекта есть какая-либо " +"документация, она будет показана здесь." + +#: spyder/app/tour.py:206 +msgid "The File Explorer" +msgstr "Файловый менеджер" + +#: spyder/app/tour.py:207 +msgid "" +"This pane lets you navigate through the directories and files present in " +"your computer.

You can also open any of these files with its " +"corresponding application, by doing a double click on it.

There is " +"one exception to this rule: plain-text files will always be opened in the " +"Spyder Editor." +msgstr "" +"Эта панель позволяет перемещаться по директориям и файлам на Вашем " +"компьютере.

Вы также можете открыть любой из файлов соответствующим " +"ему приложением двойным щелчком по нему.

Существует одно исключение " +"из этого правила: простые текстовые файлы всегда будут открыты в редакторе " +"Spyder." + +#: spyder/app/tour.py:217 +msgid "The History Log" +msgstr "Журнал истории" + +#: spyder/app/tour.py:218 +msgid "" +"This pane records all commands introduced in the Python and IPython consoles." +msgstr "" +"Эта панель записывает все команды, введённые в консолях Python и IPython." + +# Такое ощущение, что фраза не окончена и bla здесь (оно же bla-bla-bla) используется вместо '...' +#: spyder/app/tour.py:266 +msgid "Spyder is an interactive development environment based on bla" +msgstr "Spyder - интерактивная среда разработки" + +#: spyder/app/tour.py:270 +msgid "Welcome to Spyder introduction tour" +msgstr "Добро пожаловать в обучающий тур Spyder" + +# Такое ощущение, что фраза не окончена и bla здесь (оно же bla-bla-bla) используется вместо '...' +#: spyder/app/tour.py:271 +msgid "Spyder is an interactive development environment based on bla" +msgstr "Spyder - интерактивная среда разработки" + +#: spyder/app/tour.py:276 +msgid "Introduction tour" +msgstr "Обучающий тур" + +#: spyder/app/tour.py:277 +msgid "New features in version 3.0" +msgstr "Новые возможности в версии 3.0" + +#: spyder/app/tour.py:555 spyder/plugins/ipythonconsole.py:431 +msgid "Run code" +msgstr "Выполнить код" + +#: spyder/app/tour.py:824 +msgid "Go to step: " +msgstr "Перейти к шагу:" + +#: spyder/config/base.py:249 +msgid "" +"Update LANGUAGE_CODES (inside config/base.py) if a new translation has been " +"added to Spyder" +msgstr "" +"Обновите LANGUAGE_CODES (в файле config/base.py) если добавили новый перевод " +"в Spyder" + +#: spyder/config/ipython.py:23 +msgid "Integrate the IPython console" +msgstr "Интегрирует консоли IPython" + +#: spyder/config/ipython.py:25 +msgid "Manipulate Jupyter notebooks on the Editor" +msgstr "Работает с блокнотами Jupyter в Редакторе" + +#: spyder/config/utils.py:24 +msgid "Python files" +msgstr "Файлы Python" + +#: spyder/config/utils.py:25 +msgid "Cython/Pyrex files" +msgstr "Файлы Cython/Pyrex" + +#: spyder/config/utils.py:26 +msgid "C files" +msgstr "Файлы C" + +#: spyder/config/utils.py:27 +msgid "C++ files" +msgstr "Файлы C++" + +#: spyder/config/utils.py:28 +msgid "OpenCL files" +msgstr "Файлы OpenCL" + +#: spyder/config/utils.py:29 +msgid "Fortran files" +msgstr "Файлы Fortran" + +#: spyder/config/utils.py:30 +msgid "IDL files" +msgstr "Файлы IDL" + +#: spyder/config/utils.py:31 +msgid "MATLAB files" +msgstr "Файлы MATLAB" + +#: spyder/config/utils.py:32 +msgid "Julia files" +msgstr "Файлы Julia" + +#: spyder/config/utils.py:33 +msgid "Yaml files" +msgstr "Файлы Yaml" + +#: spyder/config/utils.py:34 +msgid "Patch and diff files" +msgstr "Файлы патчей и diff" + +#: spyder/config/utils.py:35 +msgid "Batch files" +msgstr "Файлы Batch" + +#: spyder/config/utils.py:36 spyder/utils/iofuncs.py:426 +msgid "Text files" +msgstr "Текстовые файлы" + +#: spyder/config/utils.py:37 +msgid "reStructuredText files" +msgstr "Файлы reStructuredText" + +#: spyder/config/utils.py:38 +msgid "gettext files" +msgstr "Файлы gettext" + +#: spyder/config/utils.py:39 +msgid "NSIS files" +msgstr "Файлы NSIS" + +#: spyder/config/utils.py:40 +msgid "Web page files" +msgstr "Файлы Веб-страниц" + +#: spyder/config/utils.py:41 +msgid "XML files" +msgstr "Файлы XML" + +#: spyder/config/utils.py:42 +msgid "Javascript files" +msgstr "Файлы Javascript" + +#: spyder/config/utils.py:43 +msgid "Json files" +msgstr "Файлы Json" + +#: spyder/config/utils.py:44 +msgid "IPython notebooks" +msgstr "Блокноты IPython" + +#: spyder/config/utils.py:45 +msgid "Enaml files" +msgstr "Файлы Enaml" + +#: spyder/config/utils.py:46 +msgid "Configuration files" +msgstr "Файлы настроек" + +#: spyder/config/utils.py:51 spyder/widgets/explorer.py:712 +msgid "All files" +msgstr "Все файлы" + +#: spyder/config/utils.py:121 +msgid "Supported text files" +msgstr "Поддерживаемые текстовые файлы" + +#: spyder/plugins/__init__.py:508 spyder/plugins/editor.py:98 +#: spyder/plugins/editor.py:545 spyder/plugins/editor.py:1729 +#: spyder/plugins/help.py:118 spyder/plugins/help.py:385 +#: spyder/widgets/editor.py:371 spyder/widgets/sourcecode/codeeditor.py:97 +#: spyder/widgets/sourcecode/codeeditor.py:3001 +msgid "Editor" +msgstr "Редактор" + +#: spyder/plugins/configdialog.py:139 +msgid "Reset to defaults" +msgstr "Восстановить значения по умолчанию" + +#: spyder/plugins/configdialog.py:151 +msgid "Preferences" +msgstr "Параметры" + +#: spyder/plugins/configdialog.py:491 +msgid "Invalid directory path" +msgstr "Неверный путь каталога" + +#: spyder/plugins/configdialog.py:494 spyder/plugins/configdialog.py:509 +#: spyder/plugins/runconfig.py:177 spyder/plugins/runconfig.py:241 +#: spyder/plugins/workingdirectory.py:291 spyder/widgets/explorer.py:626 +#: spyder/widgets/externalshell/pythonshell.py:616 +#: spyder/widgets/findinfiles.py:504 spyder/widgets/pathmanager.py:224 +#: spyder/widgets/projects/projectdialog.py:155 +msgid "Select directory" +msgstr "Выберите каталог" + +#: spyder/plugins/configdialog.py:521 +msgid "Invalid file path" +msgstr "Неверный путь файла" + +#: spyder/plugins/configdialog.py:524 spyder/plugins/configdialog.py:541 +msgid "Select file" +msgstr "Выберите файл" + +#: spyder/plugins/configdialog.py:540 +msgid "All files (*)" +msgstr "Все файлы (*)" + +#: spyder/plugins/configdialog.py:613 spyder/widgets/formlayout.py:215 +msgid "Bold" +msgstr "Жирный" + +#: spyder/plugins/configdialog.py:616 spyder/widgets/formlayout.py:210 +msgid "Italic" +msgstr "Курсив" + +#: spyder/plugins/configdialog.py:670 +msgid "Font: " +msgstr "Шрифт: " + +#: spyder/plugins/configdialog.py:676 +msgid "Size: " +msgstr "Размер: " + +#: spyder/plugins/configdialog.py:695 +msgid "Font style" +msgstr "Стиль шрифта" + +#: spyder/plugins/configdialog.py:772 +msgid "Spyder needs to restart to change the following setting:" +msgstr "Необходим перезапуск Spyder для изменения параметра:" + +#: spyder/plugins/configdialog.py:775 +msgid "Spyder needs to restart to change the following settings:" +msgstr "Необходим перезапуск Spyder для изменения настроек:" + +#: spyder/plugins/configdialog.py:777 +msgid "Do you wish to restart now?" +msgstr "Хотите выполнить перезапуск сейчас?" + +#: spyder/plugins/configdialog.py:783 +msgid "Information" +msgstr "Информация" + +#: spyder/plugins/configdialog.py:797 spyder/plugins/configdialog.py:804 +#: spyder/widgets/projects/configdialog.py:74 +msgid "General" +msgstr "Основные" + +#: spyder/plugins/configdialog.py:807 +msgid "Language" +msgstr "Язык" + +#: spyder/plugins/configdialog.py:810 +msgid "Use a single instance" +msgstr "Запускать только одну копию" + +#: spyder/plugins/configdialog.py:812 +msgid "" +"Set this to open external
Python files in an already running instance " +"(Requires a restart)" +msgstr "" +"Отметьте для открытия внешних
файлов Python в уже запущенном окружении " +"(требуется перезапуск)" + +#: spyder/plugins/configdialog.py:815 +msgid "Prompt when exiting" +msgstr "Спрашиваить подтверждение при закрытии" + +#: spyder/plugins/configdialog.py:816 +msgid "Pop up internal console when internal errors appear" +msgstr "Переходить во встроенную консоль при появлении внутренних ошибок" + +#: spyder/plugins/configdialog.py:836 spyder/plugins/editor.py:107 +#: spyder/plugins/externalconsole.py:57 spyder/plugins/ipythonconsole.py:273 +#: spyder/widgets/projects/configdialog.py:81 +msgid "Interface" +msgstr "Интерфейс" + +#: spyder/plugins/configdialog.py:844 +msgid "Qt windows style" +msgstr "Стиль окон Qt" + +#: spyder/plugins/configdialog.py:850 +msgid "Icon theme" +msgstr "Тема значков" + +#: spyder/plugins/configdialog.py:854 +msgid "Vertical title bars in panes" +msgstr "Названия плавающих окон - вертикально" + +#: spyder/plugins/configdialog.py:856 +msgid "Vertical tabs in panes" +msgstr "Вертикальные вкладки на плавающих окнах" + +#: spyder/plugins/configdialog.py:858 +msgid "Animated toolbars and panes" +msgstr "Анимировать панели инструментов и плавающие окна" + +#: spyder/plugins/configdialog.py:860 +msgid "Tear off menus" +msgstr "Открепляемые меню" + +#: spyder/plugins/configdialog.py:861 +msgid "Set this to detach any
menu from the main window" +msgstr "Отметьте для возможности отделить
любое меню от главного окна" + +#: spyder/plugins/configdialog.py:863 +msgid "Enable high DPI scaling" +msgstr "Включить масштабирование" + +#: spyder/plugins/configdialog.py:865 +msgid "Set this for high DPI displays" +msgstr "Используется для мониторов с высоким разрешением" + +#: spyder/plugins/configdialog.py:866 +msgid "Custom margin for panes:" +msgstr "Настроить отступы панелей:" + +#: spyder/plugins/configdialog.py:868 spyder/plugins/editor.py:209 +msgid "pixels" +msgstr "пикселей" + +#: spyder/plugins/configdialog.py:897 +msgid "Status bar" +msgstr "Панель состояния" + +#: spyder/plugins/configdialog.py:898 +msgid "Show status bar" +msgstr "Показывать панель состояния" + +#: spyder/plugins/configdialog.py:900 +msgid "Show memory usage every" +msgstr "Показывать использование памяти каждые" + +#: spyder/plugins/configdialog.py:902 spyder/plugins/configdialog.py:911 +#: spyder/plugins/editor.py:133 spyder/plugins/editor.py:261 +#: spyder/plugins/variableexplorer.py:30 +msgid " ms" +msgstr " мс" + +#: spyder/plugins/configdialog.py:909 +msgid "Show CPU usage every" +msgstr "Показывать загрузку ЦП каждые" + +#: spyder/plugins/configdialog.py:944 +msgid "Plain text font" +msgstr "Шрифт текста" + +#: spyder/plugins/configdialog.py:950 +msgid "Rich text font" +msgstr "Шрифт форматированного текста" + +#: spyder/plugins/configdialog.py:953 +msgid "Fonts" +msgstr "Шрифты" + +#: spyder/plugins/configdialog.py:967 +msgid "Appearance" +msgstr "Внешний вид" + +#: spyder/plugins/configdialog.py:969 spyder/plugins/ipythonconsole.py:564 +msgid "Advanced Settings" +msgstr "Расширенные настройки" + +#: spyder/plugins/configdialog.py:1005 +msgid "Syntax coloring" +msgstr "Подсветка синтаксиса" + +#: spyder/plugins/configdialog.py:1018 +msgid "" +"Here you can select the color scheme used in the Editor and all other Spyder " +"plugins.

You can also edit the color schemes provided by Spyder or " +"create your own ones by using the options provided below.
" +msgstr "" +"Здесь можно выбрать цветовую схему, используемую в Редакторе и всех других " +"модулях Spyder.

Вы можете редактировать цветовые схемы, предлагаемые " +"Spyder, или создать новую с помощью опций, приведённых ниже." + +#: spyder/plugins/configdialog.py:1023 +msgid "Edit selected" +msgstr "Редактировать выбранную" + +#: spyder/plugins/configdialog.py:1024 +msgid "Create new scheme" +msgstr "Создать новую схему" + +#: spyder/plugins/configdialog.py:1025 spyder/widgets/explorer.py:512 +#: spyder/widgets/projects/explorer.py:243 spyder/widgets/shell.py:134 +msgid "Delete" +msgstr "Удалить" + +#: spyder/plugins/configdialog.py:1028 +msgid "Reset" +msgstr "Восстановить" + +#: spyder/plugins/configdialog.py:1035 +msgid "Scheme:" +msgstr "Схема: " + +#: spyder/plugins/configdialog.py:1066 +msgid "Manage color schemes" +msgstr "Настроить цветовые схемы" + +#: spyder/plugins/configdialog.py:1255 +msgid "Are you sure you want to delete this scheme?" +msgstr "Вы уверены, что хотите удалить цветовую схему?" + +#: spyder/plugins/configdialog.py:1372 +msgid "Text" +msgstr "Текст" + +#: spyder/plugins/configdialog.py:1374 +msgid "Highlight" +msgstr "Подсветка" + +#: spyder/plugins/configdialog.py:1376 +msgid "Background" +msgstr "Фон" + +#: spyder/plugins/configdialog.py:1380 +msgid "Scheme name:" +msgstr "Название схемы:" + +#: spyder/plugins/configdialog.py:1387 +msgid "Color scheme editor" +msgstr "Редактор цветовой схемы" + +#: spyder/plugins/console.py:109 +msgid "Internal console" +msgstr "Встроенная консоль" + +#: spyder/plugins/console.py:139 spyder/plugins/externalconsole.py:743 +msgid "&Run..." +msgstr "&Выполнить..." + +#: spyder/plugins/console.py:141 spyder/plugins/externalconsole.py:744 +msgid "Run a Python script" +msgstr "Выполнить скрипт Python" + +#: spyder/plugins/console.py:144 +msgid "Environment variables..." +msgstr "Переменные среды..." + +#: spyder/plugins/console.py:146 +msgid "Show and edit environment variables (for current session)" +msgstr "Показать и редактировать переменные среды (для текущей сессии)" + +#: spyder/plugins/console.py:150 +msgid "Show sys.path contents..." +msgstr "Показать содержимое sys.path..." + +#: spyder/plugins/console.py:152 +msgid "Show (read-only) sys.path" +msgstr "Показать (только чтение) sys.path" + +#: spyder/plugins/console.py:155 +msgid "Buffer..." +msgstr "Буфер..." + +#: spyder/plugins/console.py:156 spyder/plugins/externalconsole.py:75 +#: spyder/plugins/history.py:43 +msgid "Set maximum line count" +msgstr "Установить максимальное число строк" + +#: spyder/plugins/console.py:159 +msgid "External editor path..." +msgstr "Путь к внешнему редактору..." + +#: spyder/plugins/console.py:160 +msgid "Set external editor executable path" +msgstr "Установить путь запуска внешнего редактора" + +#: spyder/plugins/console.py:163 spyder/plugins/editor.py:139 +#: spyder/plugins/externalconsole.py:76 spyder/plugins/help.py:157 +#: spyder/plugins/help.py:360 spyder/plugins/history.py:46 +#: spyder/plugins/history.py:157 +msgid "Wrap lines" +msgstr "Переносить строки" + +#: spyder/plugins/console.py:166 spyder/plugins/editor.py:175 +#: spyder/plugins/externalconsole.py:121 spyder/plugins/ipythonconsole.py:283 +msgid "Display balloon tips" +msgstr "Показывать всплывающие подсказки" + +#: spyder/plugins/console.py:170 spyder/plugins/editor.py:169 +#: spyder/plugins/externalconsole.py:115 +msgid "Automatic code completion" +msgstr "Автоматическое дополнение кода" + +#: spyder/plugins/console.py:174 spyder/plugins/editor.py:173 +#: spyder/plugins/externalconsole.py:119 +msgid "Enter key selects completion" +msgstr "Клавиша Ввод/Enter выбирает автодополнение" + +#: spyder/plugins/console.py:179 +msgid "Internal console settings" +msgstr "Настройки встроенной консоли" + +#: spyder/plugins/console.py:232 spyder/plugins/externalconsole.py:918 +msgid "Run Python script" +msgstr "Запуск скрипта Python" + +#: spyder/plugins/console.py:233 spyder/plugins/externalconsole.py:146 +#: spyder/plugins/externalconsole.py:919 spyder/widgets/explorer.py:727 +msgid "Python scripts" +msgstr "Скрипты Python" + +#: spyder/plugins/console.py:278 +msgid "Buffer" +msgstr "Буфер" + +#: spyder/plugins/console.py:279 +msgid "Maximum line count" +msgstr "Максимальное количество строк" + +#: spyder/plugins/console.py:289 +msgid "External editor" +msgstr "Внешний редактор" + +#: spyder/plugins/console.py:290 +msgid "External editor executable path:" +msgstr "Путь запуска внешнего редактора:" + +#: spyder/plugins/editor.py:104 +msgid "Edit template for new modules" +msgstr "Редактировать шаблон для новых модулей" + +#: spyder/plugins/editor.py:109 +msgid "Sort files according to full path" +msgstr "Сортировать файлы по полному пути" + +#: spyder/plugins/editor.py:111 +msgid "Show tab bar" +msgstr "Показывать панель вкладок" + +#: spyder/plugins/editor.py:118 spyder/plugins/editor.py:189 +#: spyder/plugins/externalconsole.py:71 spyder/plugins/externalconsole.py:114 +#: spyder/plugins/help.py:156 spyder/plugins/history.py:45 +#: spyder/plugins/ipythonconsole.py:317 +msgid "Source code" +msgstr "Исходный код" + +#: spyder/plugins/editor.py:119 +msgid "Show line numbers" +msgstr "Показывать номера строк" + +#: spyder/plugins/editor.py:120 spyder/plugins/editor.py:947 +msgid "Show blank spaces" +msgstr "Показывать отступы" + +#: spyder/plugins/editor.py:121 +msgid "Show vertical line after" +msgstr "Рисовать вертикальную линию после" + +#: spyder/plugins/editor.py:122 +msgid "characters" +msgstr "символов" + +#: spyder/plugins/editor.py:127 +msgid "Highlight current line" +msgstr "Подсвечивать текущую строку" + +#: spyder/plugins/editor.py:129 +msgid "Highlight current cell" +msgstr "Подсвечивать текущий блок" + +#: spyder/plugins/editor.py:131 +msgid "Highlight occurrences after" +msgstr "Подсвечивать вхождения слова через" + +#: spyder/plugins/editor.py:159 +msgid "Save all files before running script" +msgstr "Сохранить все файлы перед выполнением скрипта" + +#: spyder/plugins/editor.py:162 +msgid "Run selection" +msgstr "Выполнить выделение" + +#: spyder/plugins/editor.py:163 +msgid "Maintain focus in the Editor after running cells or selections" +msgstr "Сохранять фокус в Редакторе после запуска блоков или выделений" + +#: spyder/plugins/editor.py:166 spyder/plugins/externalconsole.py:252 +msgid "Introspection" +msgstr "Интроспекция" + +#: spyder/plugins/editor.py:171 spyder/plugins/externalconsole.py:117 +msgid "Case sensitive code completion" +msgstr "Чувствительное к регистру автодополнение" + +#: spyder/plugins/editor.py:176 +msgid "Link to object definition" +msgstr "Ссылка на место определения объекта" + +#: spyder/plugins/editor.py:178 +msgid "" +"If this option is enabled, clicking on an object\n" +"name (left-click + Ctrl key) will go this object\n" +"definition (if resolved)." +msgstr "" +"Если эта опция активна, при нажатии на имя объекта\n" +"(левая кнопка мыши + клавиша Ctrl) произойдёт\n" +"переход в место его определения (если возможно)." + +#: spyder/plugins/editor.py:182 +msgid "" +"Warning:
The Python module rope is not installed on this " +"computer: calltips, code completion and go-to-definition features won't be " +"available." +msgstr "" +"Предупреждение:
Модуль Python rope не установлен на этом " +"компьютере: подсказки вызова функций, автодополнение кода и переход к " +"определению будут недоступны." + +#: spyder/plugins/editor.py:190 +msgid "Automatic insertion of parentheses, braces and brackets" +msgstr "Автоматическая вставка круглых, фигурных и квадратных скобок" + +#: spyder/plugins/editor.py:193 +msgid "Automatic insertion of closing quotes" +msgstr "Автоматическая вставка закрывающих кавычек" + +#: spyder/plugins/editor.py:195 +msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" +msgstr "Автоматическая вставка двоеточия после 'for', 'if', 'def' и т.п." + +#: spyder/plugins/editor.py:198 +msgid "Automatic indentation after 'else', 'elif', etc." +msgstr "Автоматическая вставка отступа после 'else', 'elif' и т.п." + +#: spyder/plugins/editor.py:200 +msgid "Indentation characters: " +msgstr "Символы отступа: " + +#: spyder/plugins/editor.py:201 +msgid "2 spaces" +msgstr "2 пробела" + +#: spyder/plugins/editor.py:202 +msgid "3 spaces" +msgstr "3 пробела" + +#: spyder/plugins/editor.py:203 +msgid "4 spaces" +msgstr "4 пробела" + +#: spyder/plugins/editor.py:204 +msgid "5 spaces" +msgstr "5 пробелов" + +#: spyder/plugins/editor.py:205 +msgid "6 spaces" +msgstr "6 пробелов" + +#: spyder/plugins/editor.py:206 +msgid "7 spaces" +msgstr "7 пробелов" + +#: spyder/plugins/editor.py:207 +msgid "8 spaces" +msgstr "8 пробелов" + +#: spyder/plugins/editor.py:208 +msgid "Tabulations" +msgstr "Табуляция" + +#: spyder/plugins/editor.py:209 +msgid "Tab stop width:" +msgstr "Ширина табуляции:" + +#: spyder/plugins/editor.py:211 +msgid "Tab always indent" +msgstr "Клавиша Tab - всегда отступ" + +#: spyder/plugins/editor.py:213 +msgid "" +"If enabled, pressing Tab will always indent,\n" +"even when the cursor is not at the beginning\n" +"of a line (when this option is enabled, code\n" +"completion may be triggered using the alternate\n" +"shortcut: Ctrl+Space)" +msgstr "" +"Если активно, нажатие Tab будет делать отступ\n" +"всегда, даже когда курсор не в начале строки\n" +"(когда эта опция включена, автодополнение\n" +"кода можно будет включить альтернативной\n" +"комбинацией: Ctrl+Space)" + +#: spyder/plugins/editor.py:218 +msgid "Intelligent backspace" +msgstr "Умная клавиша backspace" + +#: spyder/plugins/editor.py:220 +msgid "Automatically remove trailing spaces when saving files" +msgstr "" +"Автоматически удалять лишние пробелы в конце строк при сохранении файлов" + +#: spyder/plugins/editor.py:224 +msgid "Analysis" +msgstr "Анализ" + +#: spyder/plugins/editor.py:226 +msgid "(Refer to the {} page)" +msgstr "(смотреть стр. {})" + +#: spyder/plugins/editor.py:230 +msgid "Real-time code analysis" +msgstr "Анализ кода в реальном времени" + +#: spyder/plugins/editor.py:232 +msgid "" +"

If enabled, Python source code will be analyzed using pyflakes, lines " +"containing errors or warnings will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore code analysis warnings.

" +msgstr "" +"

Если активно, код Python будет анализироваться с помощью pyflakes, строки " +"с ошибками или предупреждениями будут подсвечены.

Примечание: " +"добавьте analysis:ignore в комментарий, чтобы игнорировать " +"предупреждения анализатора кода.

" + +#: spyder/plugins/editor.py:240 +msgid "Code analysis requires pyflakes %s+" +msgstr "Для анализа кода требуется pyflakes %s+" + +#: spyder/plugins/editor.py:242 +msgid "Real-time code style analysis" +msgstr "Анализ стиля кода в реальном времени" + +#: spyder/plugins/editor.py:244 +msgid "" +"

If enabled, Python source code will be analyzedusing pep8, lines that are " +"not following PEP8 style guide will be highlighted.

Note: add " +"analysis:ignore in a comment to ignore style analysis warnings.

" +msgstr "" +"

Если активно, код Python будет анализироваться с помощью pep8, строки не " +"соответсвующие стилю PEP8, будут подсвечены.

Примечание: " +"добавьте analysis:ignore в комментарий, чтобы игнорировать " +"предупреждения анализатора стиля.

" + +#: spyder/plugins/editor.py:251 +msgid "Code annotations (TODO, FIXME, XXX, HINT, TIP, @todo)" +msgstr "Аннотации кода (TODO, FIXME, XXX, HINT, TIP, @todo)" + +#: spyder/plugins/editor.py:254 +msgid "Perform analysis when saving file and every" +msgstr "Выполнять анализ при сохранении файла и каждые" + +#: spyder/plugins/editor.py:258 +msgid "Perform analysis only when saving file" +msgstr "Выполнять анализ только при сохранении файла" + +#: spyder/plugins/editor.py:317 +msgid "End-of-line characters" +msgstr "Символы конца строки" + +#: spyder/plugins/editor.py:318 +msgid "" +"When opening a text file containing mixed end-of-line characters (this may " +"raise syntax errors in the consoles on Windows platforms), Spyder may fix " +"the file automatically." +msgstr "" +"Когда открывается текстовый файл, содержащий смешанные символы конца строки " +"(это может увеличить число синтаксических ошибок в консолях на платформах " +"Windows), Spyder может исправить файл автоматически." + +#: spyder/plugins/editor.py:324 +msgid "Fix automatically and show warning message box" +msgstr "Автоматически исправлять и показывать окно предупреждений" + +#: spyder/plugins/editor.py:335 spyder/plugins/externalconsole.py:250 +#: spyder/plugins/ipythonconsole.py:558 spyder/plugins/variableexplorer.py:43 +msgid "Display" +msgstr "Отображение" + +#: spyder/plugins/editor.py:337 +msgid "Code Introspection/Analysis" +msgstr "Интроспекция/анализ кода" + +#: spyder/plugins/editor.py:340 spyder/plugins/externalconsole.py:253 +msgid "Advanced settings" +msgstr "Дополнительные настройки" + +#: spyder/plugins/editor.py:613 spyder/widgets/editortools.py:510 +msgid "Show/hide outline explorer" +msgstr "Показать/скрыть менеджер структуры" + +#: spyder/plugins/editor.py:619 +msgid "Show/hide project explorer" +msgstr "Показать/скрыть менеджер проектов" + +#: spyder/plugins/editor.py:627 +msgid "&New file..." +msgstr "&Новый файл" + +#: spyder/plugins/editor.py:628 spyder/plugins/workingdirectory.py:83 +#: spyder/widgets/explorer.py:704 spyder/widgets/explorer.py:711 +msgid "New file" +msgstr "Новый файл" + +#: spyder/plugins/editor.py:634 +msgid "&Open..." +msgstr "&Открыть" + +#: spyder/plugins/editor.py:635 spyder/plugins/editor.py:1778 +#: spyder/plugins/workingdirectory.py:70 +msgid "Open file" +msgstr "Открыть файл" + +#: spyder/plugins/editor.py:641 spyder/widgets/editor.py:347 +msgid "File switcher..." +msgstr "Переключение по файлам..." + +#: spyder/plugins/editor.py:643 +msgid "Fast switch between files" +msgstr "Быстрое переключение между файлами" + +#: spyder/plugins/editor.py:649 +msgid "&Revert" +msgstr "За&грузить заново" + +#: spyder/plugins/editor.py:650 +msgid "Revert file from disk" +msgstr "Загрузить файл с диска" + +#: spyder/plugins/editor.py:653 +msgid "&Save" +msgstr "&Сохранить" + +#: spyder/plugins/editor.py:654 spyder/widgets/editor.py:1306 +msgid "Save file" +msgstr "Сохранить файл" + +#: spyder/plugins/editor.py:660 +msgid "Sav&e all" +msgstr "Сохранить &все" + +#: spyder/plugins/editor.py:661 +msgid "Save all files" +msgstr "Сохранить все файлы" + +#: spyder/plugins/editor.py:667 +msgid "Save &as..." +msgstr "Сохранить &как..." + +#: spyder/plugins/editor.py:668 +msgid "Save current file as..." +msgstr "Сохранить текущий файл как..." + +#: spyder/plugins/editor.py:673 spyder/plugins/editor.py:674 +msgid "Print preview..." +msgstr "Предварительный просмотр..." + +#: spyder/plugins/editor.py:675 +msgid "&Print..." +msgstr "&Печать" + +#: spyder/plugins/editor.py:676 +msgid "Print current file..." +msgstr "Печатать текущий файл" + +#: spyder/plugins/editor.py:679 +msgid "&Close" +msgstr "&Закрыть" + +#: spyder/plugins/editor.py:680 +msgid "Close current file" +msgstr "Закрыть текущий файл" + +#: spyder/plugins/editor.py:683 +msgid "C&lose all" +msgstr "Закрыть вс&е" + +#: spyder/plugins/editor.py:684 +msgid "Close all opened files" +msgstr "Закрыть все открытые файлы" + +#: spyder/plugins/editor.py:691 +msgid "&Find text" +msgstr "&Найти текст" + +#: spyder/plugins/editor.py:697 +msgid "Find &next" +msgstr "Найти &следующий" + +#: spyder/plugins/editor.py:703 +msgid "Find &previous" +msgstr "Найти &предыдущий" + +#: spyder/plugins/editor.py:709 +msgid "&Replace text" +msgstr "&Заменить текст" + +#: spyder/plugins/editor.py:718 +msgid "Set/Clear breakpoint" +msgstr "Поставить/Убрать точку останова" + +#: spyder/plugins/editor.py:725 +msgid "Set/Edit conditional breakpoint" +msgstr "Поставить/Редактировать условную точку останова" + +#: spyder/plugins/editor.py:732 +msgid "Clear breakpoints in all files" +msgstr "Очистить точки останова во всех файлах" + +#: spyder/plugins/editor.py:734 +msgid "Debug with winpdb" +msgstr "Отладка с помощью winpdb" + +#: spyder/plugins/editor.py:741 +msgid "Debug file" +msgstr "Отладка файла" + +#: spyder/plugins/editor.py:746 +msgid "Step" +msgstr "Шаг" + +#: spyder/plugins/editor.py:747 +msgid "Run current line" +msgstr "Выполнить текущую строку" + +#: spyder/plugins/editor.py:752 +msgid "Continue" +msgstr "Продолжить" + +#: spyder/plugins/editor.py:754 +msgid "Continue execution until next breakpoint" +msgstr "Продолжить выполнение до следующей точки останова" + +#: spyder/plugins/editor.py:759 +msgid "Step Into" +msgstr "Шаг в функцию" + +#: spyder/plugins/editor.py:761 +msgid "Step into function or method of current line" +msgstr "Войти в функцию или метод текущей линии" + +#: spyder/plugins/editor.py:766 +msgid "Step Return" +msgstr "Шаг из функции" + +#: spyder/plugins/editor.py:768 +msgid "Run until current function or method returns" +msgstr "Выполнять пока функция или метод не завершатся" + +#: spyder/plugins/editor.py:773 spyder/widgets/findinfiles.py:333 +#: spyder/widgets/ipythonconsole/client.py:238 +msgid "Stop" +msgstr "Стоп" + +#: spyder/plugins/editor.py:774 +msgid "Stop debugging" +msgstr "Завершить отладку" + +#: spyder/plugins/editor.py:781 +msgid "Run file" +msgstr "Выполнить файл" + +#: spyder/plugins/editor.py:786 +msgid "&Configure..." +msgstr "&Настроить" + +#: spyder/plugins/editor.py:788 +#: spyder/widgets/externalshell/pythonshell.py:304 +msgid "Run settings" +msgstr "Настройки Запуска" + +#: spyder/plugins/editor.py:794 +msgid "Re-run &last script" +msgstr "Перезапустить последний скрипт" + +#: spyder/plugins/editor.py:796 +msgid "Run again last file" +msgstr "Выполнить снова последний файл" + +#: spyder/plugins/editor.py:802 spyder/widgets/sourcecode/codeeditor.py:2548 +msgid "Run &selection or current line" +msgstr "Выполнить в&ыделенное или текущую строку" + +#: spyder/plugins/editor.py:805 +msgid "Run selection or current line" +msgstr "Выполнить выделенное или текущую строку" + +#: spyder/plugins/editor.py:813 spyder/widgets/sourcecode/codeeditor.py:2540 +msgid "Run cell" +msgstr "Выполнить &блок" + +#: spyder/plugins/editor.py:816 +msgid "" +"Run current cell (Ctrl+Enter)\n" +"[Use #%% to create cells]" +msgstr "" +"Выполнить текущий блок (Ctrl+Enter)\n" +"[Используйте #%% для создания блоков]" + +#: spyder/plugins/editor.py:822 spyder/widgets/sourcecode/codeeditor.py:2544 +msgid "Run cell and advance" +msgstr "Выполнить блок и перейти далее" + +#: spyder/plugins/editor.py:825 +msgid "Run current cell and go to the next one (Shift+Enter)" +msgstr "Выполнить текущий блок и перейти к следующему (Shift+Enter)" + +#: spyder/plugins/editor.py:832 +msgid "Show todo list" +msgstr "Показать список задач" + +#: spyder/plugins/editor.py:833 +msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" +msgstr "Показать список комментариев TODO/FIXME/XXX/HINT/TIP/@todo" + +#: spyder/plugins/editor.py:840 +msgid "Show warning/error list" +msgstr "Показать список предупреждений/ошибок" + +#: spyder/plugins/editor.py:841 +msgid "Show code analysis warnings/errors" +msgstr "Показать предупреждения/ошибки анализа кода" + +#: spyder/plugins/editor.py:847 +msgid "Previous warning/error" +msgstr "Предыдущее предупреждение/ошибка" + +#: spyder/plugins/editor.py:848 +msgid "Go to previous code analysis warning/error" +msgstr "Перейти к предыдущему предупреждению/ошибке анализа кода" + +#: spyder/plugins/editor.py:851 +msgid "Next warning/error" +msgstr "Следующее предупреждение/ошибка" + +#: spyder/plugins/editor.py:852 +msgid "Go to next code analysis warning/error" +msgstr "Перейти к следующему предупреждению/ошибке анализа кода" + +#: spyder/plugins/editor.py:856 +msgid "Last edit location" +msgstr "Последнее место редактирования" + +#: spyder/plugins/editor.py:857 +msgid "Go to last edit location" +msgstr "Перейти к последнему месту редактирования" + +#: spyder/plugins/editor.py:865 +msgid "Previous cursor position" +msgstr "Предыдущая позиция курсора" + +#: spyder/plugins/editor.py:866 +msgid "Go to previous cursor position" +msgstr "Перейти к предыдущей позиции курсора" + +#: spyder/plugins/editor.py:874 +msgid "Next cursor position" +msgstr "Следующая позиция курсора" + +#: spyder/plugins/editor.py:875 +msgid "Go to next cursor position" +msgstr "Перейти к следующей позиции курсора" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Comment" +msgstr "Комментировать" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Uncomment" +msgstr "Раскомментировать" + +#: spyder/plugins/editor.py:886 +msgid "Comment current line or selection" +msgstr "Комментировать текущую строку/выделение" + +#: spyder/plugins/editor.py:890 +msgid "Add &block comment" +msgstr "Добавить &блок комментариев" + +#: spyder/plugins/editor.py:891 +msgid "Add block comment around current line or selection" +msgstr "Добавить блок комментариев вокруг текущей строки/выделения" + +#: spyder/plugins/editor.py:897 +msgid "R&emove block comment" +msgstr "У&далить блок комментариев" + +#: spyder/plugins/editor.py:898 +msgid "Remove comment block around current line or selection" +msgstr "Убрать блок комментариев вокруг текущей строки/выделения" + +#: spyder/plugins/editor.py:909 +msgid "Indent" +msgstr "Добавить отступ" + +#: spyder/plugins/editor.py:910 +msgid "Indent current line or selection" +msgstr "Добавить отступ к текущей строке или выделению" + +#: spyder/plugins/editor.py:913 +msgid "Unindent" +msgstr "Убрать отступ" + +#: spyder/plugins/editor.py:914 +msgid "Unindent current line or selection" +msgstr "Убрать отступ у текущей строки/выделения" + +#: spyder/plugins/editor.py:918 +msgid "Toggle Uppercase" +msgstr "Сделать Прописными" + +#: spyder/plugins/editor.py:919 +msgid "Change to uppercase current line or selection" +msgstr "Перевести текущую строчку или выделение в верхний регистр" + +#: spyder/plugins/editor.py:923 +msgid "Toggle Lowercase" +msgstr "Сделать строчными" + +#: spyder/plugins/editor.py:924 +msgid "Change to lowercase current line or selection" +msgstr "Перевести текущую строчку или выделение в нижний регистр" + +#: spyder/plugins/editor.py:929 +msgid "Carriage return and line feed (Windows)" +msgstr "Возврат каретки и перевод строки (Windows)" + +#: spyder/plugins/editor.py:932 +msgid "Line feed (UNIX)" +msgstr "Перевод строки (UNIX)" + +#: spyder/plugins/editor.py:935 +msgid "Carriage return (Mac)" +msgstr "Возврат каретки (Mac)" + +#: spyder/plugins/editor.py:941 +msgid "Convert end-of-line characters" +msgstr "Преобразовать символы конца строки" + +#: spyder/plugins/editor.py:945 +msgid "Remove trailing spaces" +msgstr "Удалить лишние пробелы в конце строк" + +#: spyder/plugins/editor.py:949 +msgid "Fix indentation" +msgstr "Исправить отступы" + +#: spyder/plugins/editor.py:950 +msgid "Replace tab characters by space characters" +msgstr "Заменить символы табуляции пробелами" + +#: spyder/plugins/editor.py:953 +msgid "Go to line..." +msgstr "Перейти к строке..." + +#: spyder/plugins/editor.py:961 +msgid "Set console working directory" +msgstr "Установить рабочий каталог консоли" + +#: spyder/plugins/editor.py:963 +msgid "" +"Set current console (and file explorer) working directory to current script " +"directory" +msgstr "" +"Установить каталог текущего скрипта как рабочий для текущей консоли (и " +"файлового менеджера) " + +#: spyder/plugins/editor.py:968 +msgid "Maximum number of recent files..." +msgstr "Максимальное количество недавних файлов..." + +#: spyder/plugins/editor.py:971 +msgid "Clear recent files list" +msgstr "Очистить список недавних файлов" + +#: spyder/plugins/editor.py:971 spyder/plugins/projects.py:100 +msgid "Clear this list" +msgstr "Очистить этот список" + +#: spyder/plugins/editor.py:975 +msgid "Open &recent" +msgstr "Открыть не&давние" + +#: spyder/plugins/editor.py:1359 +msgid "?" +msgstr "?" + +#: spyder/plugins/editor.py:1586 +msgid "Spyder Editor" +msgstr "Редактор Spyder" + +#: spyder/plugins/editor.py:1587 +msgid "This is a temporary script file." +msgstr "Это временный скриптовый файл." + +#: spyder/plugins/editor.py:1656 +msgid "untitled" +msgstr "untitled" + +#: spyder/plugins/editor.py:1730 +msgid "Maximum number of recent files" +msgstr "Максимальное количество недавних файлов" + +#: spyder/plugins/editor.py:1863 +msgid "Printing..." +msgstr "Печать..." + +#: spyder/plugins/explorer.py:53 +msgid "File explorer" +msgstr "Файловый менеджер" + +#: spyder/plugins/externalconsole.py:46 +msgid "Interactive data plotting in the consoles" +msgstr "Интерактивные графики в консолях" + +#: spyder/plugins/externalconsole.py:54 spyder/plugins/externalconsole.py:709 +msgid "Python console" +msgstr "Консоль Python" + +#: spyder/plugins/externalconsole.py:59 +msgid "One tab per script" +msgstr "Одна вкладка на скрипт" + +#: spyder/plugins/externalconsole.py:60 +#: spyder/widgets/externalshell/baseshell.py:160 +msgid "Show elapsed time" +msgstr "Показывать прошедшее время" + +#: spyder/plugins/externalconsole.py:61 spyder/widgets/explorer.py:1094 +msgid "Show icons and text" +msgstr "Показывать иконки и текст" + +#: spyder/plugins/externalconsole.py:73 +msgid "Buffer: " +msgstr "Буфер: " + +#: spyder/plugins/externalconsole.py:73 spyder/plugins/ipythonconsole.py:319 +msgid " lines" +msgstr " строк" + +#: spyder/plugins/externalconsole.py:78 +msgid "Merge process standard output/error channels" +msgstr "Объединить стандартные потоки вывода/ошибок" + +#: spyder/plugins/externalconsole.py:80 +msgid "" +"Merging the output channels of the process means that\n" +"the standard error won't be written in red anymore,\n" +"but this has the effect of speeding up display." +msgstr "" +"Объединение потоков вывода процесса означает, что\n" +"стандартные ошибки не будут более выделяться красным,\n" +"но это увеличит скорость отображения на дисплее." + +#: spyder/plugins/externalconsole.py:84 +msgid "Colorize standard error channel using ANSI escape codes" +msgstr "Выделить цветом стандартный поток ошибок управляющими кодами ANSI" + +#: spyder/plugins/externalconsole.py:86 +msgid "" +"This method is the only way to have colorized standard\n" +"error channel when the output channels have been merged." +msgstr "" +"Это единственный способ цветового выделения стандартного\n" +"потока ошибок, когда потоки вывода объединены." + +#: spyder/plugins/externalconsole.py:102 spyder/plugins/ipythonconsole.py:306 +#: spyder/widgets/variableexplorer/arrayeditor.py:540 +#: spyder/widgets/variableexplorer/dataframeeditor.py:552 +msgid "Background color" +msgstr "Цвет фона" + +#: spyder/plugins/externalconsole.py:103 +msgid "" +"This option will be applied the next time a Python console or a terminal is " +"opened." +msgstr "" +"Эта опция будет применена при следующем открытии консоли Python
или " +"терминала." + +#: spyder/plugins/externalconsole.py:106 +msgid "Light background (white color)" +msgstr "Светлый фон (белый цвет)" + +#: spyder/plugins/externalconsole.py:131 +msgid "PYTHONSTARTUP replacement" +msgstr "Замена PYTHONSTARTUP" + +#: spyder/plugins/externalconsole.py:133 +msgid "" +"This option will override the PYTHONSTARTUP environment variable which\n" +"defines the script to be executed during the Python console startup." +msgstr "" +"Эта опция перезапишет переменную среды PYTHONSTARTUP, которая\n" +"определяет скрипт, запускаемый при старте консоли Python." + +#: spyder/plugins/externalconsole.py:138 +msgid "Default PYTHONSTARTUP script" +msgstr "Скрипт PYTHONSTARTUP по умолчанию" + +#: spyder/plugins/externalconsole.py:142 +msgid "Use the following startup script:" +msgstr "Использовать следующий скрипт запуска:" + +#: spyder/plugins/externalconsole.py:159 +msgid "Monitor" +msgstr "Монитор" + +#: spyder/plugins/externalconsole.py:160 +msgid "" +"The monitor provides introspection features to console: code completion, " +"calltips and variable explorer. Because it relies on several modules, " +"disabling the monitor may be useful to accelerate console startup." +msgstr "" +"Монитор обеспечивает возможность самоанализа в консоли: автодополнение, " +"подсказки вызова функций и менеджер переменных. Так как он зависит от " +"нескольких модулей, отключение монитора может быть полезно для ускорения " +"запуска консоли." + +#: spyder/plugins/externalconsole.py:167 +msgid "Enable monitor" +msgstr "Включить монитор" + +#: spyder/plugins/externalconsole.py:180 +msgid "Default library" +msgstr "Библиотека по умолчанию" + +#: spyder/plugins/externalconsole.py:185 +msgid "Qt-Python Bindings" +msgstr "Привязка Qt-Python" + +#: spyder/plugins/externalconsole.py:187 +msgid "Library:" +msgstr "Библиотека:" + +#: spyder/plugins/externalconsole.py:189 +msgid "" +"This option will act on
libraries such as Matplotlib, guidata or ETS" +msgstr "" +"Эта опция будет действовать на
\n" +"библиотеки, такие как Matplotlib, guidata или ETS" + +#: spyder/plugins/externalconsole.py:198 spyder/plugins/ipythonconsole.py:560 +msgid "Graphics" +msgstr "Графика" + +#: spyder/plugins/externalconsole.py:199 +msgid "" +"Decide which backend to use to display graphics. If unsure, please select " +"the Automatic backend.

Note: We support a very limited " +"number of backends in our Python consoles. If you prefer to work with a " +"different one, please use an IPython console." +msgstr "" +"Выберите какой бэкенд использовать для отображения графики. Если не уверены, " +"выберите Автоматически.

Примечание: Мы поддерживаем " +"очень ограниченный набор бэкендов в консолях Python. Если Вы предпочитаете " +"работать с другим, пожалуйста используйте консоль IPython." + +#: spyder/plugins/externalconsole.py:208 +msgid "None" +msgstr "Отсутствует" + +#: spyder/plugins/externalconsole.py:208 spyder/plugins/ipythonconsole.py:351 +msgid "Automatic" +msgstr "Автоматически" + +#: spyder/plugins/externalconsole.py:213 spyder/plugins/ipythonconsole.py:373 +msgid "Backend:" +msgstr "Бэкенд:" + +#: spyder/plugins/externalconsole.py:215 spyder/plugins/ipythonconsole.py:375 +msgid "This option will be applied the next time a console is opened." +msgstr "Эта опция будет принята при следующем открытии консоли." + +#: spyder/plugins/externalconsole.py:226 +msgid "Enthought Tool Suite" +msgstr "Набор инструментов Enthought" + +#: spyder/plugins/externalconsole.py:227 +msgid "" +"Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical " +"user interfaces." +msgstr "" +"Enthought Tool Suite (ETS) поддерживает графические пользовательские " +"интерфейсы PyQt4 (qt4) и wxPython (wx)." + +#: spyder/plugins/externalconsole.py:231 +msgid "ETS_TOOLKIT:" +msgstr "ETS_TOOLKIT:" + +#: spyder/plugins/externalconsole.py:255 +msgid "External modules" +msgstr "Внешние модули" + +#: spyder/plugins/externalconsole.py:447 +msgid "" +"No Python console is currently selected to run %s.

Please " +"select or open a new Python console and try again." +msgstr "" +"Сейчас нет доступных консолей Python для запуска %s." +"

Пожалуйста, откройте новую и попытайтесь снова." + +#: spyder/plugins/externalconsole.py:518 +msgid "" +"%s is already running in a separate process.\n" +"Do you want to kill the process before starting a new one?" +msgstr "" +"%s всегда запускается в отдельном процессе.\n" +"Хотите убить процесс перед запуском нового?" + +#: spyder/plugins/externalconsole.py:647 +msgid "Command Window" +msgstr "Командное окно" + +#: spyder/plugins/externalconsole.py:649 spyder/plugins/ipythonconsole.py:297 +msgid "Terminal" +msgstr "Терминал" + +#: spyder/plugins/externalconsole.py:731 +msgid "Open a &Python console" +msgstr "Открыть консоль &Python" + +#: spyder/plugins/externalconsole.py:735 +msgid "Open &command prompt" +msgstr "Открыть &командную строку" + +#: spyder/plugins/externalconsole.py:736 +msgid "Open a Windows command prompt" +msgstr "Открыть командную строку Windows" + +#: spyder/plugins/externalconsole.py:738 +msgid "Open a &terminal" +msgstr "Открыть &терминал" + +#: spyder/plugins/externalconsole.py:739 +msgid "Open a terminal window" +msgstr "Открыть окно терминала" + +#: spyder/plugins/findinfiles.py:127 spyder/widgets/findinfiles.py:689 +msgid "Find in files" +msgstr "Найти в файлах" + +#: spyder/plugins/findinfiles.py:148 +msgid "&Find in files" +msgstr "Найти в &файлах" + +#: spyder/plugins/findinfiles.py:151 +msgid "Search text in multiple files" +msgstr "Искать текст в нескольких файлах" + +#: spyder/plugins/help.py:44 +msgid "Show help for objects in the Editor and Consoles in a dedicated pane" +msgstr "" +"Показывать справку об объектах в Редакторе и Консолях в специальной панели" + +#: spyder/plugins/help.py:110 +msgid "Automatic connections" +msgstr "Автоматические соединения" + +#: spyder/plugins/help.py:111 +msgid "" +"This pane can automatically show an object's help information after a left " +"parenthesis is written next to it. Below you can decide to which plugin you " +"want to connect it to turn on this feature." +msgstr "" +"Инспектор объектов может автоматически показывать справочную информацию по " +"объекту после ввода открывающей круглой скобки вслед за ним. Ниже вы можете " +"решить, в каком модуле использовать эту возможность." + +#: spyder/plugins/help.py:123 +msgid "" +"This feature requires the Rope or Jedi libraries.\n" +"It seems you don't have either installed." +msgstr "" +"Эта функция требует библиотеки Rope или Jedi.\n" +"Похоже, Вы их не установили." + +#: spyder/plugins/help.py:126 +msgid "Python Console" +msgstr "Консоль Python" + +#: spyder/plugins/help.py:128 +msgid "IPython Console" +msgstr "Консоль IPython" + +#: spyder/plugins/help.py:140 +msgid "Additional features" +msgstr "Дополнительные возможности" + +#: spyder/plugins/help.py:141 +msgid "Render mathematical equations" +msgstr "Отрисовывать математические выражения" + +#: spyder/plugins/help.py:147 +msgid "This feature requires Sphinx 1.1 or superior." +msgstr "Эта возможность требует Sphinx 1.1 или выше." + +#: spyder/plugins/help.py:148 +msgid "Sphinx %s is currently installed." +msgstr "Sphinx %s на данный момент установлено." + +#: spyder/plugins/help.py:309 +msgid "No further documentation available" +msgstr "Дополнительная документация отсутствует" + +#: spyder/plugins/help.py:347 +msgid "No documentation available" +msgstr "Дополнительная документация отсутствует" + +#: spyder/plugins/help.py:378 +msgid "Source" +msgstr "Исходник" + +#: spyder/plugins/help.py:385 spyder/plugins/runconfig.py:186 +#: spyder/plugins/runconfig.py:456 +#: spyder/widgets/externalshell/baseshell.py:94 +#: spyder/widgets/ipythonconsole/client.py:202 +msgid "Console" +msgstr "Консоль" + +#: spyder/plugins/help.py:393 +#: spyder/widgets/variableexplorer/collectionseditor.py:133 +msgid "Object" +msgstr "Объект" + +#: spyder/plugins/help.py:407 +msgid "Plain Text" +msgstr "Простой текст" + +#: spyder/plugins/help.py:411 +msgid "Show Source" +msgstr "Показать исходник" + +#: spyder/plugins/help.py:415 +msgid "Rich Text" +msgstr "Форматированный текст" + +#: spyder/plugins/help.py:425 +msgid "Automatic import" +msgstr "Автоматический импорт" + +#: spyder/plugins/help.py:437 spyder/plugins/history.py:106 +#: spyder/widgets/editor.py:504 spyder/widgets/explorer.py:1105 +#: spyder/widgets/externalshell/baseshell.py:140 +#: spyder/widgets/ipythonconsole/client.py:251 +#: spyder/widgets/variableexplorer/namespacebrowser.py:160 +msgid "Options" +msgstr "Опции" + +#: spyder/plugins/help.py:695 +msgid "" +"Here you can get help of any object by pressing %s in front of it, either on " +"the Editor or the Console.%sHelp can also be shown automatically after " +"writing a left parenthesis next to an object. You can activate this behavior " +"in %s." +msgstr "" +"Здесь Вы можете получить справку по любому объекту, наведя текстовый курсор " +"на него и нажав %s, как в Редакторе, так и в Консоли.%sСправка также может " +"автоматически показываться при вводе открывающей круглой скобки после " +"объекта. Вы можете активировать эту возможность в %s." + +#: spyder/plugins/help.py:701 +msgid "Preferences > Help" +msgstr "Параметры > Справка" + +#: spyder/plugins/help.py:708 +msgid "Usage" +msgstr "Использование" + +#: spyder/plugins/help.py:709 +msgid "New to Spyder? Read our" +msgstr "Используете Spyder впервые? Прочитайте наше" + +#: spyder/plugins/help.py:710 +msgid "tutorial" +msgstr "руководство" + +#: spyder/plugins/help.py:717 +msgid "" +"Please consider installing Sphinx to get documentation rendered in rich text." +msgstr "" +"Пожалуйста, подумайте об установке Sphinx для просмотра документации в " +"форматированном виде." + +#: spyder/plugins/help.py:886 +msgid "Lock" +msgstr "Заблокировать" + +#: spyder/plugins/help.py:886 +msgid "Unlock" +msgstr "Разблокировать" + +#: spyder/plugins/help.py:930 +msgid "" +"The following error occured when calling Sphinx %s.
Incompatible " +"Sphinx version or doc string decoding failed.

Error message:
%s" +msgstr "" +"Следующая ошибка появляется при вызове Sphinx %s.
Несовместимая " +"версия Sphinx или ошибка декодирования строки документации.

Сообщение " +"об ошибке:
%s" + +#: spyder/plugins/help.py:974 +msgid "No source code available." +msgstr "Исходный код не доступен." + +#: spyder/plugins/history.py:39 +msgid "Settings" +msgstr "Настройки" + +#: spyder/plugins/history.py:41 +msgid " entries" +msgstr " записей" + +#: spyder/plugins/history.py:41 +msgid "History depth: " +msgstr "Глубина истории: " + +#: spyder/plugins/history.py:48 +msgid "Scroll automatically to last entry" +msgstr "Автоматически пролистывать к последней записи" + +#: spyder/plugins/history.py:126 +msgid "History log" +msgstr "Журнал истории" + +#: spyder/plugins/history.py:153 +msgid "History..." +msgstr "История..." + +#: spyder/plugins/history.py:155 +msgid "Set history maximum entries" +msgstr "Установить максимум записей истории" + +#: spyder/plugins/history.py:260 +msgid "History" +msgstr "История" + +#: spyder/plugins/history.py:261 +msgid "Maximum entries" +msgstr "Максимум записей" + +#: spyder/plugins/ipythonconsole.py:64 +msgid "Symbolic mathematics in the IPython Console" +msgstr "Символьная математика в консоли Python" + +#: spyder/plugins/ipythonconsole.py:116 +msgid "" +"The authenticity of host %s can't be established. Are you sure you " +"want to continue connecting?" +msgstr "" +"Подлинность хоста %s не может быть установлена. Вы уверены, что " +"хотите продолжить соединение?" + +#: spyder/plugins/ipythonconsole.py:128 +msgid "The authenticity of the host can't be established" +msgstr "Подлинность хоста не может быть установлена" + +#: spyder/plugins/ipythonconsole.py:135 +msgid "Tunnel '%s' failed to start" +msgstr "Туннель '%s' не удалось запустить" + +#: spyder/plugins/ipythonconsole.py:140 +msgid "Could not connect to remote host" +msgstr "Не удалось подключиться к удалённому хосту" + +#: spyder/plugins/ipythonconsole.py:157 spyder/plugins/ipythonconsole.py:747 +msgid "Connect to an existing kernel" +msgstr "Подключиться к работающему ядру" + +#: spyder/plugins/ipythonconsole.py:159 +msgid "" +"Please enter the connection info of the kernel you want to connect to. For " +"that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for " +"example kernel-3764.json or just 3764)." +msgstr "" +"Пожалуйста, введите информацию о соединении для ядра, к которому пытаетесь " +"подключиться. Для этого либо выберите JSON-файл соединения кнопкой " +"Обзор, либо, если ядро локальное, введите его id (напр. " +"kernel-3764.json или просто 3764)." + +#: spyder/plugins/ipythonconsole.py:170 +msgid "Connection info:" +msgstr "Информация о соединении:" + +#: spyder/plugins/ipythonconsole.py:172 +msgid "Path to connection file or kernel id" +msgstr "Путь к файлу соединения или id ядра" + +#: spyder/plugins/ipythonconsole.py:174 spyder/plugins/ipythonconsole.py:191 +msgid "Browse" +msgstr "Обзор" + +#: spyder/plugins/ipythonconsole.py:183 +msgid "This is a remote kernel" +msgstr "Это удалённо-управляемое ядро" + +#: spyder/plugins/ipythonconsole.py:187 +msgid "username@hostname:port" +msgstr "username@hostname:port" + +#: spyder/plugins/ipythonconsole.py:190 +msgid "Path to ssh key file" +msgstr "Путь к ключу ssh" + +#: spyder/plugins/ipythonconsole.py:199 +msgid "Password or ssh key passphrase" +msgstr "Пароль или кодовая фраза ключа ssh" + +#: spyder/plugins/ipythonconsole.py:203 +msgid "Host name" +msgstr "Имя хоста" + +#: spyder/plugins/ipythonconsole.py:204 +msgid "Ssh key" +msgstr "Ключ ssh" + +#: spyder/plugins/ipythonconsole.py:205 +msgid "Password" +msgstr "Пароль" + +#: spyder/plugins/ipythonconsole.py:234 +msgid "Open connection file" +msgstr "Открыть файл соединения IPython" + +#: spyder/plugins/ipythonconsole.py:239 +msgid "Select ssh key" +msgstr "Выберите ключ ssh" + +#: spyder/plugins/ipythonconsole.py:267 spyder/plugins/ipythonconsole.py:686 +msgid "IPython console" +msgstr "Консоль IPython" + +#: spyder/plugins/ipythonconsole.py:274 +msgid "Display initial banner" +msgstr "Показывать стартовый баннер" + +#: spyder/plugins/ipythonconsole.py:275 +msgid "" +"This option lets you hide the message shown at\n" +"the top of the console when it's opened." +msgstr "" +"Эта обция позволяет Вам спрятать сообщение,\n" +"показываемое вверху консоли при открытии." + +#: spyder/plugins/ipythonconsole.py:277 +msgid "Use a pager to display additional text inside the console" +msgstr "" +"Использовать постраничный вывод для показа дополнительного текста в консоли" + +#: spyder/plugins/ipythonconsole.py:279 +msgid "" +"Useful if you don't want to fill the console with long help or completion " +"texts.\n" +"Note: Use the Q key to get out of the pager." +msgstr "" +"Полезно, если Вы не хотите захламлять консоль длинной справкой или сплошным " +"текстом.\n" +"Примечание: Используйте клавишу Q для выхода из постраничного вывода." + +#: spyder/plugins/ipythonconsole.py:284 +msgid "Ask for confirmation before closing" +msgstr "Спрашивать подтверждение перед закрытием" + +#: spyder/plugins/ipythonconsole.py:294 +msgid "Completion Type" +msgstr "Тип автодополнения" + +#: spyder/plugins/ipythonconsole.py:295 +msgid "Decide what type of completion to use" +msgstr "Выберите какой тип автодополнения использовать" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Graphical" +msgstr "Графический" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Plain" +msgstr "Простой" + +#: spyder/plugins/ipythonconsole.py:298 +msgid "Completion:" +msgstr "Автодополнение:" + +#: spyder/plugins/ipythonconsole.py:307 +msgid "Light background" +msgstr "Светлый фон" + +#: spyder/plugins/ipythonconsole.py:309 +msgid "Dark background" +msgstr "Тёмный фон" + +#: spyder/plugins/ipythonconsole.py:319 +msgid "Buffer: " +msgstr "Буфер: " + +#: spyder/plugins/ipythonconsole.py:321 +msgid "" +"Set the maximum number of lines of text shown in the\n" +"console before truncation. Specifying -1 disables it\n" +"(not recommended!)" +msgstr "" +"Установите максимальное количество линий, показываемых\n" +"в консоли перед усечением. Значение -1 отключает функцию\n" +"(не рекомендуется!)" + +#: spyder/plugins/ipythonconsole.py:330 +msgid "Support for graphics (Matplotlib)" +msgstr "Поддержка графики (Matplotlib)" + +#: spyder/plugins/ipythonconsole.py:331 +msgid "Activate support" +msgstr "Активировать поддержку" + +#: spyder/plugins/ipythonconsole.py:332 +msgid "Automatically load Pylab and NumPy modules" +msgstr "Автоматически загружать модули Pylab и NumPy" + +#: spyder/plugins/ipythonconsole.py:335 +msgid "" +"This lets you load graphics support without importing \n" +"the commands to do plots. Useful to work with other\n" +"plotting libraries different to Matplotlib or to develop \n" +"GUIs with Spyder." +msgstr "" +"Это позволит загружать поддержку графики без импорта\n" +"команд построения графиков. Полезно для работы с\n" +"графическими библиотеками, отличными от Matplotlib или\n" +"разработки GUI в Spyder." + +#: spyder/plugins/ipythonconsole.py:350 +msgid "Inline" +msgstr "Встроенный" + +#: spyder/plugins/ipythonconsole.py:352 +msgid "Graphics backend" +msgstr "Графический бэкенд" + +#: spyder/plugins/ipythonconsole.py:353 +msgid "" +"Decide how graphics are going to be displayed in the console. If unsure, " +"please select %s to put graphics inside the console or %s to " +"interact with them (through zooming and panning) in a separate window." +msgstr "" +"Выберите как графика будет отображаться в консоли. Если не уверены, " +"пожалуйста выберите %s для вставки графики в консоль или %s " +"для взаимодействия с ними (через масштабирование и панорамирование) в " +"отдельном окне." + +#: spyder/plugins/ipythonconsole.py:386 +msgid "Inline backend" +msgstr "Встроенный бекэнд" + +#: spyder/plugins/ipythonconsole.py:387 +msgid "Decide how to render the figures created by this backend" +msgstr "Выберите как рендерить графики, созданные этим бэкендом" + +#: spyder/plugins/ipythonconsole.py:391 +msgid "Format:" +msgstr "Формат:" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "Resolution:" +msgstr "Разрешение:" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "dpi" +msgstr "dpi" + +#: spyder/plugins/ipythonconsole.py:396 +msgid "Only used when the format is PNG. Default is 72" +msgstr "Используется только для формата PNG. 72 по умолчанию" + +#: spyder/plugins/ipythonconsole.py:399 +msgid "Width:" +msgstr "Ширина:" + +#: spyder/plugins/ipythonconsole.py:399 spyder/plugins/ipythonconsole.py:403 +msgid "inches" +msgstr "дюймов" + +#: spyder/plugins/ipythonconsole.py:401 +msgid "Default is 6" +msgstr "6 по умолчанию" + +#: spyder/plugins/ipythonconsole.py:403 +msgid "Height:" +msgstr "Высота:" + +#: spyder/plugins/ipythonconsole.py:405 +msgid "Default is 4" +msgstr "4 по умолчанию" + +#: spyder/plugins/ipythonconsole.py:432 +msgid "" +"You can run several lines of code when a console is started. Please " +"introduce each one separated by commas, for example:
import os, import " +"sys" +msgstr "" +"Вы можете выполнить несколько строк кода при запуске консоли. Пожалуйста, " +"введите их через запятую, например:
import os, import sys" + +#: spyder/plugins/ipythonconsole.py:438 +msgid "Lines:" +msgstr "Строки:" + +#: spyder/plugins/ipythonconsole.py:447 +msgid "Run a file" +msgstr "Выполнить файл" + +#: spyder/plugins/ipythonconsole.py:448 +msgid "" +"You can also run a whole file at startup instead of just some lines (This is " +"similar to have a PYTHONSTARTUP file)." +msgstr "" +"При старте Вы также можете запустить весь файл вместо нескольких строк " +"(Подобно использованию файла PYTHONSTARTUP)." + +#: spyder/plugins/ipythonconsole.py:452 +msgid "Use the following file:" +msgstr "Использовать следующий файл:" + +#: spyder/plugins/ipythonconsole.py:466 +msgid "Greedy completion" +msgstr "Скупое автодополнение" + +#: spyder/plugins/ipythonconsole.py:467 +msgid "" +"Enable Tab completion on elements of lists, results of function " +"calls, etc, without assigning them to a variable.
For example, you " +"can get completions on things like li[0].<Tab> or ins.meth" +"().<Tab>" +msgstr "" +"Включает автодополнение по Tab для элементов списков, результатов " +"вызова функций, и т.п. без присвоения их переменной.
Например, Вы " +"можете сделать автодополнение для таких вещей, как li[0].<Tab> или ins.meth().<Tab>" + +#: spyder/plugins/ipythonconsole.py:475 +msgid "Use the greedy completer" +msgstr "Включить скупое автодополнение" + +#: spyder/plugins/ipythonconsole.py:486 +msgid "Autocall" +msgstr "Автовызов" + +#: spyder/plugins/ipythonconsole.py:487 +msgid "" +"Autocall makes IPython automatically call any callable object even if you " +"didn't type explicit parentheses.
For example, if you type str 43 " +"it becomes str(43) automatically." +msgstr "" +"Автовызов позволяет IPython автоматически вызывать объекты без явного ввода " +"скобок.
Например, если Вы введете str 43, будет выполнено str" +"(43) автоматически." + +#: spyder/plugins/ipythonconsole.py:494 +msgid "Smart" +msgstr "Умный" + +#: spyder/plugins/ipythonconsole.py:495 +msgid "Full" +msgstr "Все" + +#: spyder/plugins/ipythonconsole.py:496 +msgid "Off" +msgstr "Отключён" + +#: spyder/plugins/ipythonconsole.py:498 +msgid "Autocall: " +msgstr "Автовызов: " + +#: spyder/plugins/ipythonconsole.py:499 +msgid "" +"On %s mode, Autocall is not applied if there are no arguments after " +"the callable. On %s mode, all callable objects are automatically " +"called (even if no arguments are present)." +msgstr "" +"В режиме %s Автовызов не происходит если отсутствуют аргументы у " +"вызываемого. В режиме %s все вызываемые объекты вызываются (даже если " +"аргументы не переданы)." + +#: spyder/plugins/ipythonconsole.py:511 +msgid "Symbolic Mathematics" +msgstr "Символьная математика" + +#: spyder/plugins/ipythonconsole.py:512 +msgid "" +"Perfom symbolic operations in the console (e.g. integrals, derivatives, " +"vector calculus, etc) and get the outputs in a beautifully printed style (it " +"requires the Sympy module)." +msgstr "" +"Производит символьные операции в консоли (напр. интегралы, производные, " +"векторное исчисление и др.) и выводит результат в красивом виде (требуется " +"модуль Sympy)." + +#: spyder/plugins/ipythonconsole.py:517 +msgid "Use symbolic math" +msgstr "Использовать символьную математику" + +#: spyder/plugins/ipythonconsole.py:518 +msgid "" +"This option loads the Sympy library to work with.
Please refer to its " +"documentation to learn how to use it." +msgstr "" +"Эта опция подгружает для работы библиотеку Sympy.
Пожалуйста ознакомьтесь " +"с документацией для её использования." + +#: spyder/plugins/ipythonconsole.py:528 +msgid "Prompts" +msgstr "Приглашения к вводу/выводу" + +#: spyder/plugins/ipythonconsole.py:529 +msgid "Modify how Input and Output prompts are shown in the console." +msgstr "Определите как показывать приглашения Ввода и Вывода в консоли." + +#: spyder/plugins/ipythonconsole.py:532 +msgid "Input prompt:" +msgstr "Строка ввода:" + +#: spyder/plugins/ipythonconsole.py:534 +msgid "" +"Default is
In [<span class=\"in-prompt-number\">%i</span>]:" +msgstr "" +"По умолчанию:
In [<span class=\"in-prompt-number\">%i</" +"span>]:" + +#: spyder/plugins/ipythonconsole.py:538 +msgid "Output prompt:" +msgstr "Строка вывода:" + +#: spyder/plugins/ipythonconsole.py:540 +msgid "" +"Default is
Out[<span class=\"out-prompt-number\">%i</span>]:" +msgstr "" +"По умолчанию:
Out [<span class=\"out-prompt-number\">%i</" +"span>]:" + +#: spyder/plugins/ipythonconsole.py:562 spyder/plugins/workingdirectory.py:45 +msgid "Startup" +msgstr "Запуск" + +#: spyder/plugins/ipythonconsole.py:734 +msgid "Open an &IPython console" +msgstr "Открыть консоль &IPython" + +#: spyder/plugins/ipythonconsole.py:737 +msgid "Use %s+T when the console is selected to open a new one" +msgstr "Используйте %s+T когда консоль выбрана, чтобы открыть новую" + +#: spyder/plugins/ipythonconsole.py:740 +msgid "Open a new console" +msgstr "Открыть новую консоль" + +#: spyder/plugins/ipythonconsole.py:748 +msgid "Open a new IPython console connected to an existing kernel" +msgstr "Открыть новую консоль IPython, подключенную к существующему ядру" + +#: spyder/plugins/ipythonconsole.py:836 +msgid "" +"No IPython console is currently available to run %s.

Please " +"open a new one and try again." +msgstr "" +"Сейчас нет доступных консолей IPython для запуска %s." +"

Пожалуйста, откройте новую и попытайтесь снова." + +#: spyder/plugins/ipythonconsole.py:880 +msgid "" +"Your Python environment or installation doesn't have the ipykernel " +"module installed on it. Without this module is not possible for Spyder to " +"create a console for you.

You can install ipykernel by " +"running in a terminal:

pip install ipykernel

or

conda install ipykernel" +msgstr "" +"В вашем Python окружении или инсталяции отсутствует модуль ipykernel. Без этого модуля Spyder не может создать консоль для Вас.

Вы " +"можете установить ipykernel, запустив в терминале команду: " +"

pip install ipykernel

или

conda install " +"ipykernel" + +#: spyder/plugins/ipythonconsole.py:1105 +msgid "Do you want to close this console?" +msgstr "Вы уверены, что хотите закрыть эту консоль?" + +#: spyder/plugins/ipythonconsole.py:1111 +msgid "" +"Do you want to close all other consoles connected to the same kernel as this " +"one?" +msgstr "Хотите закрыть все другие консоли, подключённые к тому же ядру?" + +#: spyder/plugins/ipythonconsole.py:1382 +msgid "IPython" +msgstr "IPython" + +#: spyder/plugins/ipythonconsole.py:1383 +msgid "Unable to connect to %s" +msgstr "Не удалось подключиться к %s" + +#: spyder/plugins/ipythonconsole.py:1443 +msgid "Connection error" +msgstr "Ошибка соединения" + +#: spyder/plugins/ipythonconsole.py:1444 +msgid "" +"Could not open ssh tunnel. The error was:\n" +"\n" +msgstr "" +"Не удалось открыть ssh-туннель. Произошла ошибка:\n" +"\n" + +#: spyder/plugins/layoutdialog.py:177 +msgid "Move Up" +msgstr "Вверх" + +#: spyder/plugins/layoutdialog.py:178 +msgid "Move Down" +msgstr "Вниз" + +#: spyder/plugins/layoutdialog.py:179 +msgid "Delete Layout" +msgstr "Удалить" + +#: spyder/plugins/layoutdialog.py:183 +msgid "Layout Display and Order" +msgstr "Показывать и Выбирать компоновку" + +#: spyder/plugins/maininterpreter.py:31 spyder/plugins/maininterpreter.py:66 +msgid "Python interpreter" +msgstr "Интепретатор Python" + +#: spyder/plugins/maininterpreter.py:68 +msgid "Select the Python interpreter for all Spyder consoles" +msgstr "" +"Выберите исполняемый файл интерпретатора Python\n" +"для всех консолей Spyder" + +#: spyder/plugins/maininterpreter.py:71 +msgid "Default (i.e. the same as Spyder's)" +msgstr "По умолчанию (тот же, что для Spyder)" + +#: spyder/plugins/maininterpreter.py:74 +msgid "Use the following Python interpreter:" +msgstr "Использовать следующий интепретатор Python:" + +#: spyder/plugins/maininterpreter.py:77 +msgid "Executables" +msgstr "Исполняемые файлы" + +#: spyder/plugins/maininterpreter.py:94 +msgid "User Module Reloader (UMR)" +msgstr "Перезагрузка модулей пользователя (UMR)" + +#: spyder/plugins/maininterpreter.py:95 +msgid "" +"UMR forces Python to reload modules which were imported when executing a " +"file in a Python or IPython console with the runfile function." +msgstr "" +"UMR заставляет Python перезагружать модули, импортированные при выполнении " +"скрипта во внешней консоли функцией runfile." + +#: spyder/plugins/maininterpreter.py:100 +msgid "Enable UMR" +msgstr "Включить UMR" + +#: spyder/plugins/maininterpreter.py:101 +msgid "" +"This option will enable the User Module Reloader (UMR) in Python/IPython " +"consoles. UMR forces Python to reload deeply modules during import when " +"running a Python script using the Spyder's builtin function runfile." +"

1. UMR may require to restart the console in which it will be " +"called (otherwise only newly imported modules will be reloaded when " +"executing files).

2. If errors occur when re-running a PyQt-" +"based program, please check that the Qt objects are properly destroyed (e.g. " +"you may have to use the attribute Qt.WA_DeleteOnClose on your main " +"window, using the setAttribute method)" +msgstr "" +"Эта опция включает Перезагрузчик Модулей Пользователя (UMR) в консолях " +"Python/IPython. UMR заставляет Python рекурсивно перезагружать все модули " +"при запуске скрипта Python встроенной функцией Spyder runfile." +"

1. UMR может потребовать перезапустить консоль, в которой был " +"вызван (в противном случае только вновь импортируемые модули будут " +"перезагружены при выполнении скриптов).

2.Если при перезапуске " +"программ на PyQt происходят ошибки, убедитесь, что Qt-объекты уничтожены " +"должным образом (напр. можно установить атрибут Qt.WA_DeleteOnClose " +"для Вашего главного окна с помощью метода setAttribute)" + +#: spyder/plugins/maininterpreter.py:117 +msgid "Show reloaded modules list" +msgstr "Показать список перезагруженных модулей" + +#: spyder/plugins/maininterpreter.py:118 +msgid "Please note that these changes will be applied only to new consoles" +msgstr "" +"Обратите внимание, что эти изменения будут применяться только к новым " +"консолям" + +#: spyder/plugins/maininterpreter.py:122 +msgid "Set UMR excluded (not reloaded) modules" +msgstr "Установить модули-исключения для UMR (не перезагружаемые)" + +#: spyder/plugins/maininterpreter.py:168 +msgid "" +"You selected a Python %d interpreter for the console but Spyder is " +"running on Python %d!.

Although this is possible, we recommend " +"you to install and run Spyder directly with your selected interpreter, to " +"avoid seeing false warnings and errors due to the incompatible syntax " +"between these two Python versions." +msgstr "" +"Вы выбрали интерпретатор Python %d для консоли, но Spyder запущен на " +"Python %d!.

Мы рекомендуем, если возможно, установить и " +"запустить Spyder непосредственно выбранным интерпретатором, чтобы избежать " +"появления ложных предупреждений и ошибок, связанных с несовместимостями в " +"синтаксисе между этими двумя версиями Python." + +#: spyder/plugins/maininterpreter.py:178 spyder/plugins/maininterpreter.py:205 +#: spyder/plugins/maininterpreter.py:209 +msgid "UMR" +msgstr "UMR" + +#: spyder/plugins/maininterpreter.py:179 +msgid "Set the list of excluded modules as this: numpy, scipy" +msgstr "Задайте список модулей-исключений вот так: numpy, scipy" + +#: spyder/plugins/maininterpreter.py:196 +msgid "" +"You are working with Python 2, this means that you can not import a module " +"that contains non-ascii characters." +msgstr "" +"Вы работаете с Python 2, поэтому вы не можете ипортировать модули, " +"содержащие не-ascii символы." + +#: spyder/plugins/maininterpreter.py:206 +msgid "" +"The following modules are not installed on your machine:\n" +"%s" +msgstr "" +"Следующие модули не установлены на Вашей машине:\n" +"%s" + +#: spyder/plugins/maininterpreter.py:210 +msgid "" +"Please note that these changes will be applied only to new Python/IPython " +"consoles" +msgstr "" +"Обратите внимание, что эти изменения будут применяться только к новым " +"консолям Python/IPython" + +#: spyder/plugins/onlinehelp.py:70 +msgid "Online help" +msgstr "Онлайн справка" + +#: spyder/plugins/outlineexplorer.py:49 spyder/widgets/editortools.py:195 +msgid "Outline" +msgstr "Структура" + +#: spyder/plugins/projects.py:76 spyder/widgets/projects/explorer.py:113 +#: spyder/widgets/projects/explorer.py:127 +msgid "Project explorer" +msgstr "Менеджер проектов" + +#: spyder/plugins/projects.py:88 +msgid "New Project..." +msgstr "Новый проект..." + +#: spyder/plugins/projects.py:91 +msgid "Open Project..." +msgstr "Открыть проект..." + +#: spyder/plugins/projects.py:94 +msgid "Close Project" +msgstr "Закрыть проект" + +#: spyder/plugins/projects.py:97 +msgid "Delete Project" +msgstr "Удалить проект" + +#: spyder/plugins/projects.py:103 +msgid "Project Preferences" +msgstr "Параметры проекта" + +#: spyder/plugins/projects.py:105 +msgid "Recent Projects" +msgstr "Недавние проекты" + +#: spyder/plugins/projects.py:240 +msgid "Open project" +msgstr "Открыть проект" + +#: spyder/plugins/projects.py:245 +msgid "%s is not a Spyder project!" +msgstr "%s - не является проектом Spyder!" + +#: spyder/plugins/runconfig.py:29 +msgid "Execute in current Python or IPython console" +msgstr "Выполнить в текущей консоли Python или IPython" + +#: spyder/plugins/runconfig.py:30 +msgid "Execute in a new dedicated Python console" +msgstr "Выполнить в новой отдельной консоли Python" + +#: spyder/plugins/runconfig.py:31 +msgid "Execute in an external System terminal" +msgstr "Выполнить во внешнем системном терминале" + +#: spyder/plugins/runconfig.py:41 +msgid "Always show %s on a first file run" +msgstr "Всегда показывать %s при первом запуске файла" + +#: spyder/plugins/runconfig.py:160 spyder/plugins/runconfig.py:474 +msgid "General settings" +msgstr "Основные настройки" + +#: spyder/plugins/runconfig.py:163 spyder/plugins/runconfig.py:209 +msgid "Command line options:" +msgstr "Опции командной строки:" + +#: spyder/plugins/runconfig.py:169 +msgid "Working directory:" +msgstr "Рабочий каталог:" + +#: spyder/plugins/runconfig.py:181 spyder/plugins/runconfig.py:492 +msgid "Enter debugging mode when errors appear during execution" +msgstr "Запускать отладочный режим, если происходят ошибки в ходе выполнения" + +#: spyder/plugins/runconfig.py:197 spyder/plugins/runconfig.py:502 +msgid "Dedicated Python console" +msgstr "Отдельная консоль Python" + +#: spyder/plugins/runconfig.py:201 spyder/plugins/runconfig.py:504 +msgid "Interact with the Python console after execution" +msgstr "Перейти в консоль Python после выполнения" + +#: spyder/plugins/runconfig.py:205 +msgid "Show warning when killing running process" +msgstr "Показывать предупреждение при завершении работающего процесса" + +#: spyder/plugins/runconfig.py:214 +msgid "-u is added to the other options you set here" +msgstr "-u добавлен к другим опциям, которые Вы здесь установили" + +#: spyder/plugins/runconfig.py:224 +msgid "this dialog" +msgstr "это диалоговое окно" + +#: spyder/plugins/runconfig.py:283 +msgid "Run configuration" +msgstr "Запустить конфигурацию" + +#: spyder/plugins/runconfig.py:284 +msgid "The following working directory is not valid:
%s" +msgstr "Следующий рабочий каталог не применим:
%s" + +#: spyder/plugins/runconfig.py:362 +msgid "Run settings for %s" +msgstr "Настройки Запуска для %s" + +#: spyder/plugins/runconfig.py:394 +msgid "Select a run configuration:" +msgstr "Выберите конфигурацию запуска:" + +#: spyder/plugins/runconfig.py:423 spyder/plugins/runconfig.py:448 +msgid "Run Settings" +msgstr "Настройки Запуска" + +#: spyder/plugins/runconfig.py:450 +msgid "" +"The following are the default %s. These options may be overriden " +"using the %s dialog box (see the %s menu)" +msgstr "" +"Далее - стандартные %s. Эти опции можно перезаписать с помощью " +"диалогового окна %s (см. меню %s)" + +#: spyder/plugins/runconfig.py:476 +msgid "Default working directory is:" +msgstr "Рабочий каталог по умолчанию:" + +#: spyder/plugins/runconfig.py:478 +msgid "the script directory" +msgstr "каталог скриптов" + +#: spyder/plugins/runconfig.py:481 spyder/plugins/workingdirectory.py:57 +msgid "the following directory:" +msgstr "указанный каталог:" + +#: spyder/plugins/runconfig.py:507 +msgid "Show warning when killing running processes" +msgstr "Показывать предупреждение при завершении работающих процессов" + +#: spyder/plugins/runconfig.py:516 +msgid "Run Settings dialog" +msgstr "диалог Настроек Запуска" + +#: spyder/plugins/shortcuts.py:137 +msgid "" +"Press the new shortcut and select 'Ok': \n" +"(Press 'Tab' once to switch focus between the shortcut entry \n" +"and the buttons below it)" +msgstr "" +"Нажмите новую комбинацию клавиш и выберите 'Ok': \n" +"(Нажатие 'Tab' один раз переключает фокус между\n" +"полем ввода комбинации клавиш и кнопками под ним)" + +#: spyder/plugins/shortcuts.py:140 +msgid "Current shortcut:" +msgstr "Текущая комбинация клавиш:" + +#: spyder/plugins/shortcuts.py:142 +msgid "New shortcut:" +msgstr "Новая комбинация клавиш:" + +#: spyder/plugins/shortcuts.py:155 +msgid "Shortcut: {0}" +msgstr "Комбинация клавиш: {0}" + +#: spyder/plugins/shortcuts.py:276 +msgid "Please introduce a different shortcut" +msgstr "Пожалуйста, введите другую комбинацию клавиш" + +#: spyder/plugins/shortcuts.py:313 +msgid "The new shorcut conflicts with:" +msgstr "Новая комбинация клавиш конфликтует с:" + +#: spyder/plugins/shortcuts.py:324 +msgid "" +"A compound sequence can have {break} a maximum of 4 subsequences.{break}" +msgstr "Не более 4 комбинаций клавиш {break} для одного действия.{break}" + +#: spyder/plugins/shortcuts.py:329 +msgid "Invalid key entered" +msgstr "Введен неверный ключ" + +#: spyder/plugins/shortcuts.py:531 +msgid "Context" +msgstr "Контекст" + +#: spyder/plugins/shortcuts.py:533 +#: spyder/widgets/variableexplorer/collectionseditor.py:118 +msgid "Name" +msgstr "Имя" + +#: spyder/plugins/shortcuts.py:535 +msgid "Shortcut" +msgstr "Комбинация клавиш" + +# Не понятно, зачем переводить, если колонка с этим именем не видна +#: spyder/plugins/shortcuts.py:537 +msgid "Score" +msgstr "Метка" + +#: spyder/plugins/shortcuts.py:697 +msgid "Conflicts" +msgstr "Конфликты" + +#: spyder/plugins/shortcuts.py:698 +msgid "The following conflicts have been detected:" +msgstr "Обнаружены следующие конфликты:" + +#: spyder/plugins/shortcuts.py:783 +msgid "Keyboard shortcuts" +msgstr "Комбинации клавиш" + +#: spyder/plugins/shortcuts.py:791 +msgid "Search: " +msgstr "Поиск:" + +#: spyder/plugins/shortcuts.py:792 +msgid "Reset to default values" +msgstr "Восстановить значения по умолчанию" + +#: spyder/plugins/variableexplorer.py:26 +msgid "Autorefresh" +msgstr "Автообновление" + +#: spyder/plugins/variableexplorer.py:27 +msgid "Enable autorefresh" +msgstr "Включить автообновление" + +#: spyder/plugins/variableexplorer.py:29 +msgid "Refresh interval: " +msgstr "Интервал обновления: " + +#: spyder/plugins/variableexplorer.py:33 +msgid "Filter" +msgstr "Фильтр" + +#: spyder/plugins/variableexplorer.py:35 +#: spyder/widgets/variableexplorer/namespacebrowser.py:226 +msgid "Exclude private references" +msgstr "Исключить приватные ссылки" + +#: spyder/plugins/variableexplorer.py:36 +#: spyder/widgets/variableexplorer/namespacebrowser.py:241 +msgid "Exclude capitalized references" +msgstr "Исключить ссылки с прописной буквы" + +#: spyder/plugins/variableexplorer.py:37 +#: spyder/widgets/variableexplorer/namespacebrowser.py:234 +msgid "Exclude all-uppercase references" +msgstr "Исключить ссылки со всеми буквами в верхнем регистре" + +#: spyder/plugins/variableexplorer.py:38 +#: spyder/widgets/variableexplorer/namespacebrowser.py:249 +msgid "Exclude unsupported data types" +msgstr "Исключить неподдерживаемые типы данных" + +#: spyder/plugins/variableexplorer.py:46 +#: spyder/widgets/variableexplorer/collectionseditor.py:677 +msgid "Show arrays min/max" +msgstr "Показывать min/max массивов" + +#: spyder/plugins/variableexplorer.py:48 +msgid "Edit data in the remote process" +msgstr "Редактировать данные в удалённом процессе" + +#: spyder/plugins/variableexplorer.py:49 +msgid "" +"Editors are opened in the remote process for NumPy arrays, PIL images, " +"lists, tuples and dictionaries.\n" +"This avoids transfering large amount of data between the remote process and " +"Spyder (through the socket)." +msgstr "" +"Редакторы открыты в удалённом процессе для массивов NumPy, изображений PIL, " +"списков, кортежей и словарей.\n" +"Это позволяет избежать передачи больших объёмов данных между удалённым " +"процессом и Spyder (через сокет)." + +#: spyder/plugins/variableexplorer.py:185 +msgid "Variable explorer" +msgstr "Менеджер переменных" + +#: spyder/plugins/workingdirectory.py:38 +msgid "" +"The global working directory is the working directory for newly " +"opened consoles (Python/IPython consoles and terminals), for the " +"file explorer, for the find in files plugin and for new files " +"created in the editor." +msgstr "" +"Глобальный рабочий каталог - рабочий каталог для новых консолей (консоли и терминалы Python/IPython), для файлового менеджера, для " +"модуля поиск по файлам и для новых файлов в редакторе." + +#: spyder/plugins/workingdirectory.py:47 +msgid "At startup, the global working directory is:" +msgstr "Глобальный рабочий каталог при загрузке:" + +#: spyder/plugins/workingdirectory.py:51 +msgid "the same as in last session" +msgstr "такой же как в последней сессии" + +#: spyder/plugins/workingdirectory.py:53 +msgid "At startup, Spyder will restore the global directory from last session" +msgstr "При загрузке Spyder восстановит глобальный каталог из прошлой сессии" + +#: spyder/plugins/workingdirectory.py:59 +msgid "At startup, the global working directory will be the specified path" +msgstr "При запуске глобальный рабочий каталог будет иметь указанный путь" + +#: spyder/plugins/workingdirectory.py:71 +msgid "Files are opened from:" +msgstr "Файлы открываются из:" + +#: spyder/plugins/workingdirectory.py:75 spyder/plugins/workingdirectory.py:88 +msgid "the current file directory" +msgstr "текущий каталог" + +#: spyder/plugins/workingdirectory.py:79 spyder/plugins/workingdirectory.py:92 +msgid "the global working directory" +msgstr "глобальный рабочий каталог" + +#: spyder/plugins/workingdirectory.py:84 +msgid "Files are created in:" +msgstr "Файлы создаются в:" + +#: spyder/plugins/workingdirectory.py:98 +msgid "Change to file base directory" +msgstr "Изменить на каталог файла" + +#: spyder/plugins/workingdirectory.py:100 +msgid "When opening a file" +msgstr "При открытии файла" + +#: spyder/plugins/workingdirectory.py:102 +msgid "When saving a file" +msgstr "При сохранении файла" + +#: spyder/plugins/workingdirectory.py:172 +msgid "Back" +msgstr "Назад" + +#: spyder/plugins/workingdirectory.py:180 spyder/widgets/explorer.py:1099 +#: spyder/widgets/variableexplorer/importwizard.py:529 +msgid "Next" +msgstr "Далее" + +#: spyder/plugins/workingdirectory.py:191 +msgid "" +"This is the working directory for newly\n" +"opened consoles (Python/IPython consoles and\n" +"terminals), for the file explorer, for the\n" +"find in files plugin and for new files\n" +"created in the editor" +msgstr "" +"Это - рабочий каталог для новосозданных\n" +"консолей (консолей и терминалов Python/IPython),\n" +"для файлового менеджера, для модуля\n" +"поиска в файлах и для новых файлов,\n" +"созданных в редакторе" + +#: spyder/plugins/workingdirectory.py:219 +msgid "Browse a working directory" +msgstr "Просмотреть рабочий каталог" + +#: spyder/plugins/workingdirectory.py:226 +msgid "Change to parent directory" +msgstr "Изменить на родительский каталог" + +#: spyder/plugins/workingdirectory.py:233 +msgid "Global working directory" +msgstr "Глобальный рабочий каталог" + +#: spyder/utils/codeanalysis.py:91 +msgid "Real-time code analysis on the Editor" +msgstr "Анализ кода в Редакторе в реальном времени" + +#: spyder/utils/codeanalysis.py:95 +msgid "Real-time code style analysis on the Editor" +msgstr "Анализ стиля кода в Редакторе в реальном времени" + +#: spyder/utils/environ.py:101 +msgid "" +"Module pywin32 was not found.
Please restart this Windows " +"session (not the computer) for changes to take effect." +msgstr "" +"Модуль pywin32 не найден.
Пожалуйста, перезапустите сессию " +"Windows (не компьютер), чтобы изменения вступили в силу." + +#: spyder/utils/environ.py:114 +msgid "" +"If you accept changes, this will modify the current user environment " +"variables directly in Windows registry. Use it with precautions, at " +"your own risks.

Note that for changes to take effect, you will need " +"to restart the parent process of this application (simply restart Spyder if " +"you have executed it from a Windows shortcut, otherwise restart any " +"application from which you may have executed it, like Python(x,y) Home for example)" +msgstr "" +"Если Вы примете изменения, это переопределит переменные среды для текущего " +"пользователя непосредственно в реестре Windows. Используйте с " +"осторожностью, на свой риск.

Имейте в виду, чтобы изменения вступили " +"в силу, необходимо перезагрузить родительский процесс приложения (просто " +"перезапустите Spyder, если запускали его ярлыком Windows, или перезапустите " +"приложение из которого Вы его запускали, например Python(x,y) Home)" + +#: spyder/utils/help/sphinxify.py:217 spyder/utils/help/sphinxify.py:227 +msgid "" +"It was not possible to generate rich text help for this object.
Please " +"see it in plain text." +msgstr "" +"Невозможно создать форматированную текстовую справку по объекту.Пожалуйста, просмотрите в виде обычного текста." + +#: spyder/utils/introspection/manager.py:33 +#: spyder/utils/introspection/manager.py:38 +msgid "Editor's code completion, go-to-definition and help" +msgstr "Автодополнение кода редактора, переход к определению и справка" + +#: spyder/utils/iofuncs.py:408 +msgid "Supported files" +msgstr "Поддерживаемые файлы" + +#: spyder/utils/iofuncs.py:410 +msgid "All files (*.*)" +msgstr "Все файлы (*.*)" + +#: spyder/utils/iofuncs.py:420 +msgid "Spyder data files" +msgstr "Файлы данных Spyder" + +#: spyder/utils/iofuncs.py:422 +#: spyder/widgets/variableexplorer/collectionseditor.py:1021 +msgid "NumPy arrays" +msgstr "Массивы NumPy" + +#: spyder/utils/iofuncs.py:423 +msgid "NumPy zip arrays" +msgstr "Сжатые массивы NumPy" + +#: spyder/utils/iofuncs.py:424 +msgid "Matlab files" +msgstr "Файлы Matlab" + +#: spyder/utils/iofuncs.py:425 +msgid "CSV text files" +msgstr "Текстовые файлы CSV" + +#: spyder/utils/iofuncs.py:427 +msgid "JPEG images" +msgstr "Изображения JPEG" + +#: spyder/utils/iofuncs.py:428 +msgid "PNG images" +msgstr "Изображения PNG" + +#: spyder/utils/iofuncs.py:429 +msgid "GIF images" +msgstr "Изображения GIF" + +#: spyder/utils/iofuncs.py:430 +msgid "TIFF images" +msgstr "Изображения TIFF" + +#: spyder/utils/iofuncs.py:431 spyder/utils/iofuncs.py:432 +msgid "Pickle files" +msgstr "Архивы Pickle" + +#: spyder/utils/iofuncs.py:433 +msgid "JSON files" +msgstr "Файлы JSON" + +#: spyder/utils/iofuncs.py:452 spyder/utils/iofuncs.py:459 +msgid "Unsupported file type '%s'" +msgstr "Неподдерживаемый тип файла '%s'" + +#: spyder/utils/programs.py:286 +msgid "It was not possible to run this file in an external terminal" +msgstr "Невозможно запустить файл во внешнем терминале" + +#: spyder/utils/syntaxhighlighters.py:33 +msgid "Syntax highlighting for Matlab, Julia and other file types" +msgstr "Подсветка синтаксиса для Matlab, Julia и других типов файлов" + +#: spyder/utils/syntaxhighlighters.py:42 +msgid "Background:" +msgstr "Фон:" + +#: spyder/utils/syntaxhighlighters.py:43 +#: spyder/widgets/sourcecode/codeeditor.py:106 +msgid "Current line:" +msgstr "Текущая строка:" + +#: spyder/utils/syntaxhighlighters.py:44 +msgid "Current cell:" +msgstr "Текущий блок:" + +#: spyder/utils/syntaxhighlighters.py:45 +msgid "Occurrence:" +msgstr "Вхождения слова:" + +#: spyder/utils/syntaxhighlighters.py:46 +msgid "Link:" +msgstr "Ссылка:" + +#: spyder/utils/syntaxhighlighters.py:47 +msgid "Side areas:" +msgstr "Боковые панели:" + +#: spyder/utils/syntaxhighlighters.py:48 +msgid "Matched
parens:" +msgstr "Парные
скобки:" + +#: spyder/utils/syntaxhighlighters.py:49 +msgid "Unmatched
parens:" +msgstr "Скобки
без пары:" + +#: spyder/utils/syntaxhighlighters.py:50 +msgid "Normal text:" +msgstr "Обычный текст:" + +#: spyder/utils/syntaxhighlighters.py:51 +msgid "Keyword:" +msgstr "Ключевое слово:" + +#: spyder/utils/syntaxhighlighters.py:52 +msgid "Builtin:" +msgstr "Встроенные:" + +#: spyder/utils/syntaxhighlighters.py:53 +msgid "Definition:" +msgstr "Определение:" + +#: spyder/utils/syntaxhighlighters.py:54 +msgid "Comment:" +msgstr "Комментарий:" + +#: spyder/utils/syntaxhighlighters.py:55 +msgid "String:" +msgstr "Строка:" + +#: spyder/utils/syntaxhighlighters.py:56 +msgid "Number:" +msgstr "Число:" + +#: spyder/utils/syntaxhighlighters.py:57 +msgid "Instance:" +msgstr "Экземпляр класса:" + +#: spyder/widgets/arraybuilder.py:179 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Type an array in Matlab : [1 2;3 4]
\n" +" or Spyder simplified syntax : 1 2;3 4\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two spaces or two tabs to generate a ';'.\n" +" " +msgstr "" +"\n" +" Помощник Массивов/Матриц Numpy
\n" +" Введите массив в Matlab- : [1 2;3 4]
\n" +" или Spyder-стиле : 1 2;3 4\n" +"

\n" +" Нажмите 'Enter' для массива или 'Ctrl+Enter' для матрицы.\n" +"

\n" +" Трюк:
\n" +" Два пробели или две табуляции образуют ';'.\n" +" " + +#: spyder/widgets/arraybuilder.py:190 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Enter an array in the table.
\n" +" Use Tab to move between cells.\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two tabs at the end of a row to move to the next row.\n" +" " +msgstr "" +"\n" +" Помощник Массивов/Матриц Numpy
\n" +" Введите массив в таблицу.
\n" +" Используйте Tab для перемещения по ячейкам.\n" +"

\n" +"\n" +" Нажмите 'Enter' для массива или 'Ctrl+Enter' для матрицы.\n" +"

\n" +" Трюк:
\n" +" Две табуляции в конце строки перемещают на следующую строку.\n" +" " + +#: spyder/widgets/arraybuilder.py:365 +msgid "Array dimensions not valid" +msgstr "Неверная размерность массива" + +#: spyder/widgets/browser.py:54 spyder/widgets/sourcecode/codeeditor.py:2559 +msgid "Zoom out" +msgstr "Уменьшить" + +#: spyder/widgets/browser.py:57 spyder/widgets/sourcecode/codeeditor.py:2555 +msgid "Zoom in" +msgstr "Увеличить" + +#: spyder/widgets/browser.py:177 +msgid "Home" +msgstr "Домой" + +#: spyder/widgets/browser.py:213 +msgid "Find text" +msgstr "Найти текст" + +#: spyder/widgets/browser.py:231 +msgid "Address:" +msgstr "Адрес:" + +#: spyder/widgets/browser.py:267 +msgid "Unable to load page" +msgstr "Не удалось загрузить страницу" + +#: spyder/widgets/comboboxes.py:165 +msgid "Press enter to validate this entry" +msgstr "Нажмите Enter для подтверждения этого поля" + +#: spyder/widgets/comboboxes.py:166 +msgid "This entry is incorrect" +msgstr "Это поле неверно" + +#: spyder/widgets/comboboxes.py:209 +msgid "Press enter to validate this path" +msgstr "Нажмите Enter для подтверждения этого пути" + +#: spyder/widgets/dependencies.py:63 +msgid " Required " +msgstr " требуется" + +#: spyder/widgets/dependencies.py:63 +msgid "Module" +msgstr "Модуль" + +#: spyder/widgets/dependencies.py:64 +msgid " Installed " +msgstr " установлен" + +#: spyder/widgets/dependencies.py:64 +msgid "Provided features" +msgstr "Предоставляемые возможности" + +#: spyder/widgets/dependencies.py:134 +msgid "Dependencies" +msgstr "Зависимости" + +#: spyder/widgets/dependencies.py:141 +msgid "" +"Spyder depends on several Python modules to provide the right functionality " +"for all its panes. The table below shows the required and installed versions " +"(if any) of all of them.

Note: You can safely use Spyder " +"without the following modules installed: %s and %s." +"

Please also note that new dependencies or changed ones will be " +"correctly detected only after Spyder is restarted." +msgstr "" +"Spyder зависит от нескольких модулей Python, обеспечивающих правильное " +"функционирование всех панелей. В таблице ниже показаны требуемые и " +"установленные версии (если таковые имеются) каждого из пакетов." +"

Примечание: Вы можете безопасно работать в Spyder без " +"следующих модулей: %s и %s. Также, пожалуйста, учтите, что " +"новые пакеты или изменения в них будут корректно учтены только после " +"перезапуска Spyder." + +#: spyder/widgets/dependencies.py:157 +msgid "Copy to clipboard" +msgstr "Копировать в буфер обмена" + +#: spyder/widgets/editor.py:350 +msgid "Copy path to clipboard" +msgstr "Копировать путь в буфер обмена" + +#: spyder/widgets/editor.py:354 +msgid "Close all to the right" +msgstr "Закрыть все правее" + +#: spyder/widgets/editor.py:356 +msgid "Close all but this" +msgstr "Закрыть все кроме этой" + +#: spyder/widgets/editor.py:959 +msgid "Temporary file" +msgstr "Временный файл" + +#: spyder/widgets/editor.py:1054 +msgid "New window" +msgstr "Новое окно" + +#: spyder/widgets/editor.py:1055 +msgid "Create a new editor window" +msgstr "Создать новое окно редактора" + +#: spyder/widgets/editor.py:1058 +msgid "Split vertically" +msgstr "Разделить по вертикали" + +#: spyder/widgets/editor.py:1060 +msgid "Split vertically this editor window" +msgstr "Разделить окна редактора по вертикали" + +#: spyder/widgets/editor.py:1062 +msgid "Split horizontally" +msgstr "Разделить по горизонтали" + +#: spyder/widgets/editor.py:1064 +msgid "Split horizontally this editor window" +msgstr "Разделить окна редактора по горизонтали" + +#: spyder/widgets/editor.py:1066 +msgid "Close this panel" +msgstr "Закрыть эту панель" + +#: spyder/widgets/editor.py:1223 +msgid "%s has been modified.
Do you want to save changes?" +msgstr "%s был изменён.
Хотите сохранить изменения?" + +#: spyder/widgets/editor.py:1285 +msgid "Save" +msgstr "Сохранить" + +#: spyder/widgets/editor.py:1286 +msgid "Unable to save script '%s'

Error message:
%s" +msgstr "" +"Не удалось сохранить скрипт '%s'

Сообщение об ошибке:
%s" + +#: spyder/widgets/editor.py:1523 +msgid "" +"%s is unavailable (this file may have been removed, moved or renamed " +"outside Spyder).
Do you want to close it?" +msgstr "" +"%s недоступен (этот файл мог быть удален, перемещён или переименован " +"вне Spyder).
Хотите его закрыть?" + +#: spyder/widgets/editor.py:1543 +msgid "" +"%s has been modified outside Spyder.
Do you want to reload it and " +"lose all your changes?" +msgstr "" +"%s был изменён вне Spyder.
Хотите перегрузить его и потерять все " +"изменения?" + +#: spyder/widgets/editor.py:1639 +msgid "" +"All changes to %s will be lost.
Do you want to revert file from " +"disk?" +msgstr "" +"Все изменения для %s были потеряны.
Хотите перегрузить файл с " +"диска?" + +#: spyder/widgets/editor.py:1779 +msgid "Loading %s..." +msgstr "Загрузка %s..." + +#: spyder/widgets/editor.py:1789 +msgid "" +"%s contains mixed end-of-line characters.
Spyder will fix this " +"automatically." +msgstr "" +"%s содержит смешанные символы конца строки.
Spyder исправит это " +"автоматически." + +#: spyder/widgets/editor.py:2171 +msgid "Close window" +msgstr "Закрыть окно" + +#: spyder/widgets/editor.py:2173 +msgid "Close this window" +msgstr "Закрыть это окно" + +#: spyder/widgets/editortools.py:94 spyder/widgets/editortools.py:130 +msgid "Line %s" +msgstr "Строка %s" + +#: spyder/widgets/editortools.py:99 +msgid "Class defined at line %s" +msgstr "Класс определён в строке %s" + +#: spyder/widgets/editortools.py:107 +msgid "Method defined at line %s" +msgstr "Метод определён в строке %s" + +#: spyder/widgets/editortools.py:117 +msgid "Function defined at line %s" +msgstr "Функция определена в строке %s" + +#: spyder/widgets/editortools.py:149 +msgid "Cell starts at line %s" +msgstr "Блок начинается в строке %s" + +#: spyder/widgets/editortools.py:202 spyder/widgets/editortools.py:539 +msgid "Go to cursor position" +msgstr "Перейти к позиции курсора" + +#: spyder/widgets/editortools.py:205 +msgid "Show absolute path" +msgstr "Показать полный путь" + +#: spyder/widgets/editortools.py:208 spyder/widgets/explorer.py:210 +msgid "Show all files" +msgstr "Показать все файлы" + +#: spyder/widgets/editortools.py:211 +msgid "Show special comments" +msgstr "Показать специальные комментарии" + +#: spyder/widgets/explorer.py:206 +msgid "Edit filename filters..." +msgstr "Редактировать фильтры имён файлов..." + +#: spyder/widgets/explorer.py:220 +msgid "Edit filename filters" +msgstr "Редактировать фильтры имён файлов" + +#: spyder/widgets/explorer.py:221 +msgid "Name filters:" +msgstr "Фильтры имён:" + +#: spyder/widgets/explorer.py:240 +msgid "File..." +msgstr "Файл..." + +#: spyder/widgets/explorer.py:244 +msgid "Module..." +msgstr "Модуль..." + +#: spyder/widgets/explorer.py:248 +msgid "Folder..." +msgstr "Каталог..." + +#: spyder/widgets/explorer.py:252 +msgid "Package..." +msgstr "Пакет..." + +#: spyder/widgets/explorer.py:273 +#: spyder/widgets/variableexplorer/collectionseditor.py:652 +msgid "Edit" +msgstr "Редактировать" + +#: spyder/widgets/explorer.py:275 +msgid "Move..." +msgstr "Переместить..." + +#: spyder/widgets/explorer.py:278 +msgid "Delete..." +msgstr "Удалить..." + +#: spyder/widgets/explorer.py:281 +msgid "Rename..." +msgstr "Переименовать..." + +#: spyder/widgets/explorer.py:284 +msgid "Open" +msgstr "Открыть" + +#: spyder/widgets/explorer.py:285 spyder/widgets/sourcecode/codeeditor.py:2531 +msgid "Convert to Python script" +msgstr "Сохранить как скрипт Python" + +# Варинаты: +# - оставить без перевода +# - использовать кальку: "коммитить" +# - воспользоваться переводом из источников про Git (например, книга ProGit): "зафиксирвать изменения/индекс". +#: spyder/widgets/explorer.py:319 +msgid "Commit" +msgstr "Зафиксировать" + +#: spyder/widgets/explorer.py:322 +msgid "Browse repository" +msgstr "Просмотр репозитария" + +#: spyder/widgets/explorer.py:333 +msgid "Open command prompt here" +msgstr "Открыть командную строку здесь" + +#: spyder/widgets/explorer.py:335 +msgid "Open terminal here" +msgstr "Открыть терминал здесь" + +#: spyder/widgets/explorer.py:340 +msgid "Open Python console here" +msgstr "Открыть консоль Python здесь" + +#: spyder/widgets/explorer.py:354 +msgid "New" +msgstr "Новый" + +#: spyder/widgets/explorer.py:362 +msgid "Import" +msgstr "Импортировать" + +#: spyder/widgets/explorer.py:513 +msgid "Do you really want to delete %s?" +msgstr "Вы точно хотите удалить %s?" + +#: spyder/widgets/explorer.py:531 +msgid "delete" +msgstr "удалить" + +#: spyder/widgets/explorer.py:532 spyder/widgets/projects/explorer.py:149 +#: spyder/widgets/projects/explorer.py:256 +msgid "Project Explorer" +msgstr "Менеджер проектов" + +#: spyder/widgets/explorer.py:533 spyder/widgets/projects/explorer.py:150 +msgid "Unable to %s %s

Error message:
%s" +msgstr "Невозможно %s %s

Сообщение об ошибке:
%s" + +#: spyder/widgets/explorer.py:548 +msgid "File Explorer" +msgstr "Файловый менеджер" + +#: spyder/widgets/explorer.py:549 +msgid "" +"The current directory contains a project.

If you want to delete the " +"project, please go to Projects » Delete Project" +msgstr "" +"В текущий каталоге находится проект.

Если вы хотите удалить проект, " +"пожалуйста, воспользуйтесь меню Проекты » Удалить проект" + +#: spyder/widgets/explorer.py:566 spyder/widgets/sourcecode/codeeditor.py:2018 +msgid "Conversion error" +msgstr "Ошибка преобразования" + +#: spyder/widgets/explorer.py:567 spyder/widgets/sourcecode/codeeditor.py:2019 +msgid "" +"It was not possible to convert this notebook. The error is:\n" +"\n" +msgstr "" +"Не удалось сконвертировать этот блокнот. Ошибка:\n" +"\n" + +#: spyder/widgets/explorer.py:584 spyder/widgets/explorer.py:592 +#: spyder/widgets/explorer.py:603 +#: spyder/widgets/variableexplorer/collectionseditor.py:681 +#: spyder/widgets/variableexplorer/collectionseditor.py:916 +msgid "Rename" +msgstr "Переименовать" + +#: spyder/widgets/explorer.py:585 +msgid "New name:" +msgstr "Новое имя:" + +#: spyder/widgets/explorer.py:593 +msgid "" +"Do you really want to rename %s and overwrite the existing file " +"%s?" +msgstr "" +"Вы действительно хотите переименовать %s или перезаписать " +"существующий файл %s?" + +#: spyder/widgets/explorer.py:604 +msgid "Unable to rename file %s

Error message:
%s" +msgstr "" +"Невозможно переименовать файл %s

Сообщение об ошибке:" +"
%s" + +#: spyder/widgets/explorer.py:640 +msgid "Unable to move %s

Error message:
%s" +msgstr "" +"Невозможно переместить файл %s

Сообщение об ошибке:
" +"%s" + +#: spyder/widgets/explorer.py:658 +msgid "Unable to create folder %s

Error message:
%s" +msgstr "" +"Невозможно создать каталог %s

Сообщение об ошибке:
%s" + +#: spyder/widgets/explorer.py:671 spyder/widgets/explorer.py:705 +msgid "Unable to create file %s

Error message:
%s" +msgstr "" +"Невозможно создать файл %s

Сообщение об ошибке:
%s" + +#: spyder/widgets/explorer.py:679 +msgid "New folder" +msgstr "Новый каталог" + +#: spyder/widgets/explorer.py:680 +msgid "Folder name:" +msgstr "Имя каталога:" + +#: spyder/widgets/explorer.py:685 +msgid "New package" +msgstr "Новый пакет" + +#: spyder/widgets/explorer.py:686 +msgid "Package name:" +msgstr "Имя пакета:" + +#: spyder/widgets/explorer.py:726 +msgid "New module" +msgstr "Новый модуль" + +#: spyder/widgets/explorer.py:741 +msgid "" +"For %s support, please install one of the
following tools:

%s" +msgstr "" +"Для поддержки %s, установите, пожалуйста,
один из следующих " +"инструментов:

%s" + +#: spyder/widgets/explorer.py:745 +msgid "Unable to find external program.

%s" +msgstr "Не удалось найти внешнюю программу.

%s" + +#: spyder/widgets/explorer.py:966 +msgid "Show current directory only" +msgstr "Показывать только текущий каталог" + +#: spyder/widgets/explorer.py:1066 +msgid "You don't have the right permissions to open this directory" +msgstr "У Вас нет прав на открытие этой директории" + +#: spyder/widgets/explorer.py:1096 +#: spyder/widgets/variableexplorer/importwizard.py:525 +msgid "Previous" +msgstr "Предыдущая" + +#: spyder/widgets/explorer.py:1102 +msgid "Parent" +msgstr "Родительская" + +#: spyder/widgets/externalshell/baseshell.py:129 +msgid "Run again this program" +msgstr "Запустить эту программу снова" + +#: spyder/widgets/externalshell/baseshell.py:132 +msgid "Kill" +msgstr "Убить" + +#: spyder/widgets/externalshell/baseshell.py:134 +msgid "Kills the current process, causing it to exit immediately" +msgstr "Убивает текущий процесс, заставляя его немедленно завершиться" + +#: spyder/widgets/externalshell/baseshell.py:206 +msgid "Running..." +msgstr "Запуск..." + +#: spyder/widgets/externalshell/baseshell.py:213 +msgid "Terminated." +msgstr "Прекращено." + +#: spyder/widgets/externalshell/baseshell.py:238 +#: spyder/widgets/ipythonconsole/help.py:125 spyder/widgets/mixins.py:661 +msgid "Arguments" +msgstr "Аргументы" + +#: spyder/widgets/externalshell/baseshell.py:239 +msgid "Command line arguments:" +msgstr "Аргументы командной строки:" + +#: spyder/widgets/externalshell/pythonshell.py:277 +msgid "Variables" +msgstr "Значения" + +#: spyder/widgets/externalshell/pythonshell.py:278 +msgid "Show/hide global variables explorer" +msgstr "Показать/скрыть менеджер глобальных переменных" + +#: spyder/widgets/externalshell/pythonshell.py:282 +msgid "Terminate" +msgstr "Прекратить" + +#: spyder/widgets/externalshell/pythonshell.py:283 +msgid "" +"Attempts to stop the process. The process\n" +"may not exit as a result of clicking this\n" +"button (it is given the chance to prompt\n" +"the user for any unsaved files, etc)." +msgstr "" +"Попытка остановить процесс. Процесс\n" +"имеет право не завершится после нажатия этой\n" +"кнопки (например, чтобы спросить у пользователя\n" +"о несохранённых файлах и т.п.)." + +#: spyder/widgets/externalshell/pythonshell.py:296 +msgid "Interact" +msgstr "Интерактивный" + +#: spyder/widgets/externalshell/pythonshell.py:298 +msgid "Debug" +msgstr "Отладка" + +#: spyder/widgets/externalshell/pythonshell.py:300 +#: spyder/widgets/externalshell/pythonshell.py:366 +msgid "Arguments..." +msgstr "Аргументы..." + +#: spyder/widgets/externalshell/pythonshell.py:302 +msgid "Post Mortem Debug" +msgstr "Отладка в случае падения" + +#: spyder/widgets/externalshell/pythonshell.py:308 +msgid "Working directory" +msgstr "Рабочий каталог" + +#: spyder/widgets/externalshell/pythonshell.py:310 +msgid "Set current working directory" +msgstr "Установить текущий рабочий каталог" + +#: spyder/widgets/externalshell/pythonshell.py:312 +msgid "Environment variables" +msgstr "Переменные среды" + +#: spyder/widgets/externalshell/pythonshell.py:316 +msgid "Show sys.path contents" +msgstr "Показать содержимое sys.path" + +#: spyder/widgets/externalshell/pythonshell.py:362 +msgid "Arguments: %s" +msgstr "Аргументы: %s" + +#: spyder/widgets/externalshell/pythonshell.py:364 +msgid "No argument" +msgstr "Нет аргументов" + +#: spyder/widgets/externalshell/pythonshell.py:534 +msgid "A Python console failed to start!" +msgstr "Консоль Python не удалось запустить!" + +#: spyder/widgets/externalshell/systemshell.py:106 +msgid "Process failed to start" +msgstr "Процесс не удалось запустить" + +#: spyder/widgets/fileswitcher.py:109 +msgid "unsaved file" +msgstr "несохранённый файл" + +#: spyder/widgets/fileswitcher.py:230 +msgid "" +"Press Enter to switch files or Esc to cancel.

Type to " +"filter filenames.

Use :number to go to a line, e.g. " +"main:42
Use @symbol_text to go to a symbol, e." +"g. @init

Press Ctrl+W to close current " +"tab.
" +msgstr "" +"Нажмите Enter для перехода к файлу или Esc для отмены." +"

Введите текст для фильтрации имен файлов.

Используйте :" +"номер для перехода к строке, напр. main:42
Используйте @имя для перехода к идентификатору, напр. " +"@init

Нажмите Ctrl+W для закрытия текущей " +"вкладки." + +#: spyder/widgets/fileswitcher.py:508 +msgid "lines" +msgstr " строк" + +#: spyder/widgets/findinfiles.py:158 +msgid "Unexpected error: see internal console" +msgstr "Неожиданная ошибка: смотрите во встроенной консоли" + +#: spyder/widgets/findinfiles.py:209 spyder/widgets/findinfiles.py:233 +#: spyder/widgets/findinfiles.py:280 +msgid "invalid regular expression" +msgstr "неверное регулярное выражение" + +#: spyder/widgets/findinfiles.py:278 +msgid "permission denied errors were encountered" +msgstr "обнаружены ошибки отказа в доступе" + +#: spyder/widgets/findinfiles.py:315 +msgid "Search pattern" +msgstr "Найти по шаблону" + +#: spyder/widgets/findinfiles.py:318 spyder/widgets/findinfiles.py:352 +#: spyder/widgets/findinfiles.py:364 spyder/widgets/findreplace.py:81 +msgid "Regular expression" +msgstr "Регулярное выражение" + +#: spyder/widgets/findinfiles.py:327 +msgid "Search" +msgstr "Найти" + +#: spyder/widgets/findinfiles.py:330 +msgid "Start search" +msgstr "Начать поиск" + +#: spyder/widgets/findinfiles.py:336 +msgid "Stop search" +msgstr "Остановить поиск" + +#: spyder/widgets/findinfiles.py:346 +msgid "Included filenames pattern" +msgstr "Шаблон имён влючаемых файлов" + +#: spyder/widgets/findinfiles.py:355 +msgid "Include:" +msgstr "Включить:" + +#: spyder/widgets/findinfiles.py:358 +msgid "Excluded filenames pattern" +msgstr "Шаблон имён исключаемых файлов" + +#: spyder/widgets/findinfiles.py:367 +msgid "Exclude:" +msgstr "Исключить:" + +#: spyder/widgets/findinfiles.py:377 +msgid "PYTHONPATH" +msgstr "PYTHONPATH" + +#: spyder/widgets/findinfiles.py:379 +msgid "" +"Search in all directories listed in sys.path which are outside the Python " +"installation directory" +msgstr "" +"Искать во всех каталогах, указанных в sys.path, которые отсутствуют в " +"установочном каталоге Python" + +#: spyder/widgets/findinfiles.py:382 +msgid "Hg repository" +msgstr "Hg-репозиторий" + +#: spyder/widgets/findinfiles.py:385 +msgid "Search in current directory hg repository" +msgstr "Поиск в текущем каталоге hg-репозитория" + +#: spyder/widgets/findinfiles.py:386 +msgid "Here:" +msgstr "Здесь:" + +#: spyder/widgets/findinfiles.py:390 +msgid "Search recursively in this directory" +msgstr "Искать рекурсивно в каталоге" + +#: spyder/widgets/findinfiles.py:395 +msgid "Browse a search directory" +msgstr "Выбрать каталог поиска" + +#: spyder/widgets/findinfiles.py:425 +msgid "Hide advanced options" +msgstr "Скрыть дополнительные опции" + +#: spyder/widgets/findinfiles.py:428 +msgid "Show advanced options" +msgstr "Показать дополнительные опции" + +#: spyder/widgets/findinfiles.py:569 +msgid "Search canceled" +msgstr "Поиск отменён" + +#: spyder/widgets/findinfiles.py:573 +msgid "String not found" +msgstr "Строка не найдена" + +#: spyder/widgets/findinfiles.py:575 +msgid "matches in" +msgstr "совпадений в" + +#: spyder/widgets/findinfiles.py:576 +msgid "file" +msgstr "файл" + +#: spyder/widgets/findinfiles.py:584 +msgid "interrupted" +msgstr "прервано" + +#: spyder/widgets/findreplace.py:63 +msgid "Search string" +msgstr "Найти строку" + +#: spyder/widgets/findreplace.py:87 +msgid "Case Sensitive" +msgstr "С учетом регистра" + +#: spyder/widgets/findreplace.py:93 +msgid "Whole words" +msgstr "Слова целиком" + +#: spyder/widgets/findreplace.py:99 +msgid "Highlight matches" +msgstr "Подсвечивать найденные" + +#: spyder/widgets/findreplace.py:113 +msgid "Replace with:" +msgstr "Заменить на:" + +#: spyder/widgets/findreplace.py:115 +msgid "Replace string" +msgstr "Заменить строку" + +#: spyder/widgets/findreplace.py:118 +msgid "Replace/find" +msgstr "Заменить/найти" + +#: spyder/widgets/findreplace.py:125 +msgid "Replace all" +msgstr "Заменить все" + +#: spyder/widgets/internalshell.py:262 +msgid "Help..." +msgstr "Справка..." + +#: spyder/widgets/internalshell.py:279 +msgid "Shell special commands:" +msgstr "Специальные команды оболочки:" + +#: spyder/widgets/internalshell.py:280 +msgid "Internal editor:" +msgstr "Встроенный редактор:" + +#: spyder/widgets/internalshell.py:281 +msgid "External editor:" +msgstr "Внешний редактор:" + +#: spyder/widgets/internalshell.py:282 +msgid "Run script:" +msgstr "Выполнить скрипт:" + +#: spyder/widgets/internalshell.py:283 +msgid "Remove references:" +msgstr "Удалить ссылки:" + +#: spyder/widgets/internalshell.py:284 +msgid "System commands:" +msgstr "Системные команды:" + +#: spyder/widgets/internalshell.py:285 +msgid "Python help:" +msgstr "Справка Python:" + +#: spyder/widgets/internalshell.py:286 +msgid "GUI-based editor:" +msgstr "Визуальный редактор:" + +#: spyder/widgets/ipythonconsole/client.py:189 +msgid "An error ocurred while starting the kernel" +msgstr "При запуске ядра произошла ошибка" + +#: spyder/widgets/ipythonconsole/client.py:220 +msgid "Restart kernel" +msgstr "Перезапустить ядро" + +#: spyder/widgets/ipythonconsole/client.py:240 +msgid "Stop the current command" +msgstr "Остановить текущую команду" + +#: spyder/widgets/ipythonconsole/client.py:263 +msgid "Inspect current object" +msgstr "Проверить текущий объект" + +#: spyder/widgets/ipythonconsole/client.py:268 +msgid "Clear line or block" +msgstr "Очистить строку или блок" + +#: spyder/widgets/ipythonconsole/client.py:272 +msgid "Reset namespace" +msgstr "Перезагрузить пространство имён" + +#: spyder/widgets/ipythonconsole/client.py:275 +msgid "Clear console" +msgstr "Очистить консоль" + +#: spyder/widgets/ipythonconsole/client.py:316 +msgid "Are you sure you want to restart the kernel?" +msgstr "Вы уверены, что хотите перезапустить ядро?" + +#: spyder/widgets/ipythonconsole/client.py:318 +msgid "Restart kernel?" +msgstr "Перезапустить ядро?" + +#: spyder/widgets/ipythonconsole/client.py:327 +msgid "Error restarting kernel: %s\n" +msgstr "Ошибка при перезапуске ядра: %s\n" + +#: spyder/widgets/ipythonconsole/client.py:331 +msgid "" +"
Restarting kernel...\n" +"

" +msgstr "" +"
Перезапуск ядра...\n" +"

" + +#: spyder/widgets/ipythonconsole/client.py:336 +msgid "Cannot restart a kernel not started by Spyder\n" +msgstr "Не удается перезапусть ядро, запущенное не из Spyder\n" + +#: spyder/widgets/ipythonconsole/client.py:376 +msgid "Changing backend to Qt for Mayavi" +msgstr "Изменить бэкенд для Mayavi на Qt" + +#: spyder/widgets/ipythonconsole/client.py:387 +msgid "Connecting to kernel..." +msgstr "Подключение к ядру..." + +#: spyder/widgets/ipythonconsole/namespacebrowser.py:76 +msgid "" +"Inspecting and setting values while debugging in IPython consoles is not " +"supported yet by Spyder." +msgstr "" +"Spyder не поддерживет просмотр и установку значений во время отладки в " +"консоли IPython." + +#: spyder/widgets/ipythonconsole/shell.py:149 +msgid "Reset IPython namespace" +msgstr "Перезагрузить пространство имён IPython" + +#: spyder/widgets/ipythonconsole/shell.py:150 +msgid "" +"All user-defined variables will be removed.
Are you sure you want to " +"reset the namespace?" +msgstr "" +"Все определённые пользователем переменные будут удалены.
Вы уверены, что " +"хотите переустановить пространство имён?" + +#: spyder/widgets/onecolumntree.py:52 +msgid "Collapse all" +msgstr "Свернуть всё" + +#: spyder/widgets/onecolumntree.py:56 +msgid "Expand all" +msgstr "Развернуть всё" + +#: spyder/widgets/onecolumntree.py:60 +msgid "Restore" +msgstr "Восстановить" + +#: spyder/widgets/onecolumntree.py:61 +msgid "Restore original tree layout" +msgstr "Восстановить оригинальную структуру дерева" + +#: spyder/widgets/onecolumntree.py:65 +msgid "Collapse selection" +msgstr "Свернуть выделенное" + +#: spyder/widgets/onecolumntree.py:69 +msgid "Expand selection" +msgstr "Развернуть выделенное" + +#: spyder/widgets/pathmanager.py:87 +msgid "Move to top" +msgstr "Переместить вверх" + +#: spyder/widgets/pathmanager.py:93 +msgid "Move up" +msgstr "Переместить выше" + +#: spyder/widgets/pathmanager.py:99 +msgid "Move down" +msgstr "Переместить ниже" + +#: spyder/widgets/pathmanager.py:105 +msgid "Move to bottom" +msgstr "Переместить вниз" + +#: spyder/widgets/pathmanager.py:116 spyder/widgets/pathmanager.py:231 +msgid "Add path" +msgstr "Добавить путь" + +#: spyder/widgets/pathmanager.py:121 spyder/widgets/pathmanager.py:214 +msgid "Remove path" +msgstr "Удалить путь" + +#: spyder/widgets/pathmanager.py:131 +msgid "Synchronize..." +msgstr "Синхронизация..." + +#: spyder/widgets/pathmanager.py:133 +msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" +msgstr "Синхронизировать список путей Spyder с переменной среды PYTHONPATH" + +#: spyder/widgets/pathmanager.py:145 +msgid "Synchronize" +msgstr "Синхронизировать" + +#: spyder/widgets/pathmanager.py:146 +msgid "" +"This will synchronize Spyder's path list with PYTHONPATH environment " +"variable for current user, allowing you to run your Python modules outside " +"Spyder without having to configure sys.path.
Do you want to clear " +"contents of PYTHONPATH before adding Spyder's path list?" +msgstr "" +"Будет синхронизирован список путей Spyder с переменной среды PYTHONPATH для " +"текущего пользователя, позволяя Вам запускать Ваши модули Python вне Spyder " +"без необходимости настраивать sys.path.
Хотите очистить содержимое " +"PYTHONPATH перед добавлением списка путей Spyder?" + +#: spyder/widgets/pathmanager.py:215 +msgid "Do you really want to remove selected path?" +msgstr "Вы действительно хотите удалить выбранный путь?" + +#: spyder/widgets/pathmanager.py:232 +msgid "" +"This directory is already included in Spyder path list.
Do you want to " +"move it to the top of the list?" +msgstr "" +"Этот каталог уже имеется в списке путей Spyder.
Хотите переместить его на " +"вершину списка?" + +#: spyder/widgets/projects/configdialog.py:30 +msgid "Project preferences" +msgstr "Параметры проекта" + +#: spyder/widgets/projects/configdialog.py:82 +#: spyder/widgets/projects/configdialog.py:119 +msgid "Restore data on startup" +msgstr "Восстанавливать данные при запуске" + +#: spyder/widgets/projects/configdialog.py:84 +#: spyder/widgets/projects/configdialog.py:121 +msgid "Save data on exit" +msgstr "Сохранять данные перед выходом" + +#: spyder/widgets/projects/configdialog.py:86 +#: spyder/widgets/projects/configdialog.py:123 +msgid "Save history" +msgstr "Сохранять журнал истории" + +#: spyder/widgets/projects/configdialog.py:88 +#: spyder/widgets/projects/configdialog.py:125 +msgid "Save non project files opened" +msgstr "Сохранять файлы не из проекта" + +#: spyder/widgets/projects/configdialog.py:111 +msgid "Code" +msgstr "Код" + +#: spyder/widgets/projects/configdialog.py:118 +msgid "Workspace" +msgstr "Рабочая область" + +#: spyder/widgets/projects/configdialog.py:148 +#: spyder/widgets/projects/configdialog.py:155 +msgid "Version control" +msgstr "Контроль версий" + +#: spyder/widgets/projects/configdialog.py:156 +msgid "Use version control" +msgstr "Использовать контроль версий" + +#: spyder/widgets/projects/configdialog.py:161 +msgid "Version control system" +msgstr "Система контроля версий" + +#: spyder/widgets/projects/explorer.py:52 +msgid "Show horizontal scrollbar" +msgstr "Показывать горизонтальную полосу прокрутки" + +#: spyder/widgets/projects/explorer.py:114 +msgid "File %s already exists.
Do you want to overwrite it?" +msgstr "Файл %s уже существует.
Хотите его перезаписать?" + +#: spyder/widgets/projects/explorer.py:128 +msgid "Folder %s already exists." +msgstr "Каталог %s уже существует." + +#: spyder/widgets/projects/explorer.py:146 +msgid "copy" +msgstr "скопировать" + +#: spyder/widgets/projects/explorer.py:148 +msgid "move" +msgstr "переместить" + +#: spyder/widgets/projects/explorer.py:244 +msgid "" +"Do you really want to delete {filename}?

Note: This " +"action will only delete the project. Its files are going to be preserved on " +"disk." +msgstr "" +"Вы действительно хотите удалить проект {filename}?" +"

Примечание:Файлы, входящие в проект, при этом останутся на " +"диске." + +#: spyder/widgets/projects/explorer.py:257 +msgid "" +"Unable to delete {varpath}

The error message was:
" +"{error}" +msgstr "" +"Невозможно удалить {varpath}

Сообщение об ошибке:
" +"{error}" + +#: spyder/widgets/projects/projectdialog.py:69 +msgid "New directory" +msgstr "Новый каталог" + +#: spyder/widgets/projects/projectdialog.py:70 +msgid "Existing directory" +msgstr "Существующий каталог" + +#: spyder/widgets/projects/projectdialog.py:72 +msgid "Project name" +msgstr "Имя проекта" + +#: spyder/widgets/projects/projectdialog.py:73 +msgid "Location" +msgstr "Расположение" + +#: spyder/widgets/projects/projectdialog.py:74 +msgid "Project type" +msgstr "Тип проекта" + +#: spyder/widgets/projects/projectdialog.py:75 +msgid "Python version" +msgstr "Версия Python" + +#: spyder/widgets/projects/projectdialog.py:83 +#: spyder/widgets/variableexplorer/importwizard.py:519 +msgid "Cancel" +msgstr "Отмена" + +#: spyder/widgets/projects/projectdialog.py:84 +msgid "Create" +msgstr "Создать" + +#: spyder/widgets/projects/projectdialog.py:102 +msgid "Create new project" +msgstr "Создать новый проект" + +#: spyder/widgets/projects/type/__init__.py:215 +msgid "Empty project" +msgstr "Пустой проект" + +#: spyder/widgets/projects/type/python.py:20 +msgid "Python project" +msgstr "Проект Python" + +#: spyder/widgets/projects/type/python.py:76 +msgid "Python package" +msgstr "Пакет Python" + +#: spyder/widgets/pydocgui.py:110 +msgid "Module or package:" +msgstr "Модуль или пакет:" + +#: spyder/widgets/shell.py:129 +msgid "Save history log..." +msgstr "Сохранить журнал истории..." + +#: spyder/widgets/shell.py:131 +msgid "Save current history log (i.e. all inputs and outputs) in a text file" +msgstr "" +"Сохранить текущий журнал истории (т.е. все вводы и выводы) в текстовом файле" + +#: spyder/widgets/shell.py:257 +msgid "Save history log" +msgstr "Сохранить журнал истории" + +#: spyder/widgets/shell.py:260 +msgid "History logs" +msgstr "Журналы истории" + +#: spyder/widgets/shell.py:271 +msgid "Unable to save file '%s'

Error message:
%s" +msgstr "" +"Невозможно сохранить файл %s

Сообщение об ошибке:
%s" + +#: spyder/widgets/shell.py:713 +msgid "Copy without prompts" +msgstr "Копировать без подтверждения" + +#: spyder/widgets/shell.py:716 spyder/widgets/shell.py:720 +msgid "Clear line" +msgstr "Очистить строку" + +#: spyder/widgets/shell.py:722 +msgid "Clear shell" +msgstr "Очистить оболочку" + +#: spyder/widgets/shell.py:726 +msgid "Clear shell contents ('cls' command)" +msgstr "Очистить содержание оболочки (команда 'cls')" + +#: spyder/widgets/sourcecode/codeeditor.py:100 +msgid "Go to line:" +msgstr "Перейти к строке:" + +#: spyder/widgets/sourcecode/codeeditor.py:108 +msgid "Line count:" +msgstr "Количество строк:" + +#: spyder/widgets/sourcecode/codeeditor.py:1305 +msgid "Breakpoint" +msgstr "Точка останова" + +#: spyder/widgets/sourcecode/codeeditor.py:1306 +msgid "Condition:" +msgstr "Условие:" + +#: spyder/widgets/sourcecode/codeeditor.py:1712 +msgid "Code analysis" +msgstr "Анализ кода" + +#: spyder/widgets/sourcecode/codeeditor.py:1766 +msgid "To do" +msgstr "Список задач" + +#: spyder/widgets/sourcecode/codeeditor.py:2005 +msgid "Removal error" +msgstr "Ошибка удаления" + +#: spyder/widgets/sourcecode/codeeditor.py:2006 +msgid "" +"It was not possible to remove outputs from this notebook. The error is:\n" +"\n" +msgstr "" +"Невозможно удалить результаты из блокнота. Ошибка:\n" +"\n" + +#: spyder/widgets/sourcecode/codeeditor.py:2528 +msgid "Clear all ouput" +msgstr "Очистить вывод" + +#: spyder/widgets/sourcecode/codeeditor.py:2534 +msgid "Go to definition" +msgstr "Перейти к определению" + +#: spyder/widgets/sourcecode/codeeditor.py:2563 +msgid "Zoom reset" +msgstr "Восстановить масштаб" + +#: spyder/widgets/status.py:25 +msgid "CPU and memory usage info in the status bar" +msgstr "Информация об использовании ЦП и памяти в строке состояния" + +#: spyder/widgets/status.py:94 +msgid "Memory:" +msgstr "Память:" + +#: spyder/widgets/status.py:95 +msgid "" +"Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows " +"platforms" +msgstr "" +"Состояние использования памяти: требуется библиотека `psutil` (>=v0.3) на не-" +"Windows платформах" + +#: spyder/widgets/status.py:107 +msgid "CPU:" +msgstr "ЦП:" + +#: spyder/widgets/status.py:108 +msgid "CPU usage status: requires the `psutil` (>=v0.3) library" +msgstr "Состояние использования ЦП: требуется библиотека `psutil` (>=v0.3)" + +#: spyder/widgets/status.py:130 +msgid "Permissions:" +msgstr "Доступ:" + +#: spyder/widgets/status.py:144 +msgid "End-of-lines:" +msgstr "Конец строки:" + +#: spyder/widgets/status.py:158 +msgid "Encoding:" +msgstr "Кодировка:" + +#: spyder/widgets/status.py:171 +msgid "Line:" +msgstr "Строка:" + +#: spyder/widgets/status.py:175 +msgid "Column:" +msgstr "Столбец:" + +#: spyder/widgets/tabs.py:146 +msgid "Browse tabs" +msgstr "Обзор вкладок" + +#: spyder/widgets/tabs.py:275 +msgid "Close current tab" +msgstr "Закрыть текущую вкладку" + +#: spyder/widgets/variableexplorer/arrayeditor.py:497 +msgid "It was not possible to copy values for this array" +msgstr "Не удалось скопировать значения этого массива" + +#: spyder/widgets/variableexplorer/arrayeditor.py:532 +#: spyder/widgets/variableexplorer/arrayeditor.py:565 +#: spyder/widgets/variableexplorer/dataframeeditor.py:544 +#: spyder/widgets/variableexplorer/dataframeeditor.py:584 +msgid "Format" +msgstr "Форматировать" + +#: spyder/widgets/variableexplorer/arrayeditor.py:537 +#: spyder/widgets/variableexplorer/dataframeeditor.py:548 +msgid "Resize" +msgstr "Изменить размер" + +#: spyder/widgets/variableexplorer/arrayeditor.py:566 +#: spyder/widgets/variableexplorer/dataframeeditor.py:585 +msgid "Float formatting" +msgstr "Форматирование чисел" + +#: spyder/widgets/variableexplorer/arrayeditor.py:574 +#: spyder/widgets/variableexplorer/dataframeeditor.py:594 +msgid "Format (%s) is incorrect" +msgstr "Формат (%s) неверный" + +#: spyder/widgets/variableexplorer/arrayeditor.py:609 +msgid "Array is empty" +msgstr "Массив пустой" + +#: spyder/widgets/variableexplorer/arrayeditor.py:612 +msgid "Arrays with more than 3 dimensions are not supported" +msgstr "Массивы с размерностью более 3 не поддерживаются" + +#: spyder/widgets/variableexplorer/arrayeditor.py:615 +msgid "The 'xlabels' argument length do no match array column number" +msgstr "Длина аргумента 'xlabels' не соответствует числу столбцов массива" + +#: spyder/widgets/variableexplorer/arrayeditor.py:619 +msgid "The 'ylabels' argument length do no match array row number" +msgstr "Длина аргумента 'ylabels' не соответствует числу строк массива" + +#: spyder/widgets/variableexplorer/arrayeditor.py:626 +msgid "%s arrays" +msgstr "%s массивы" + +#: spyder/widgets/variableexplorer/arrayeditor.py:627 +msgid "%s are currently not supported" +msgstr "%s на данный момент не поддерживается" + +#: spyder/widgets/variableexplorer/arrayeditor.py:634 +msgid "NumPy array" +msgstr "Массив NumPy" + +#: spyder/widgets/variableexplorer/arrayeditor.py:636 +#: spyder/widgets/variableexplorer/arrayeditor.py:790 +msgid "Array editor" +msgstr "Редактор массивов" + +#: spyder/widgets/variableexplorer/arrayeditor.py:638 +msgid "read only" +msgstr "только чтение" + +#: spyder/widgets/variableexplorer/arrayeditor.py:668 +msgid "Record array fields:" +msgstr "Заполнить поля массива:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Data" +msgstr "Данные" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Mask" +msgstr "Маска" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Masked data" +msgstr "Скрытые данные" + +#: spyder/widgets/variableexplorer/arrayeditor.py:691 +msgid "Axis:" +msgstr "Оси:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:696 +msgid "Index:" +msgstr "Индекс:" + +#: spyder/widgets/variableexplorer/arrayeditor.py:709 +msgid "Warning: changes are applied separately" +msgstr "Предупреждение: изменения применяются раздельно" + +#: spyder/widgets/variableexplorer/arrayeditor.py:710 +msgid "" +"For performance reasons, changes applied to masked array won't be reflected " +"in array's data (and vice-versa)." +msgstr "" +"По соображениям производительности, изменения принятые для скрытых массивов " +"не будут отражены в данных массивов (и наоборот)." + +#: spyder/widgets/variableexplorer/collectionseditor.py:116 +msgid "Index" +msgstr "Индекс" + +#: spyder/widgets/variableexplorer/collectionseditor.py:121 +msgid "Tuple" +msgstr "Кортеж" + +#: spyder/widgets/variableexplorer/collectionseditor.py:124 +msgid "List" +msgstr "Список" + +#: spyder/widgets/variableexplorer/collectionseditor.py:127 +msgid "Dictionary" +msgstr "Словарь" + +#: spyder/widgets/variableexplorer/collectionseditor.py:129 +msgid "Key" +msgstr "Ключ" + +#: spyder/widgets/variableexplorer/collectionseditor.py:135 +msgid "Attribute" +msgstr "Атрибут" + +#: spyder/widgets/variableexplorer/collectionseditor.py:137 +msgid "elements" +msgstr "элементов" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Size" +msgstr "Размер" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Type" +msgstr "Тип" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Value" +msgstr "Значение" + +#: spyder/widgets/variableexplorer/collectionseditor.py:409 +msgid "" +"Opening this variable can be slow\n" +"\n" +"Do you want to continue anyway?" +msgstr "" +"Открытие этой переменной может занять время\n" +"\n" +"Желаете продолжить?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:418 +msgid "" +"Spyder was unable to retrieve the value of this variable from the console." +"

The error mesage was:
%s" +msgstr "" +"Spyder не смог извлечь значение этой переменной из консоли. " +"

Сообщение об ошибке:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:583 +msgid "Edit item" +msgstr "Редактировать элемент" + +#: spyder/widgets/variableexplorer/collectionseditor.py:584 +msgid "Unable to assign data to item.

Error message:
%s" +msgstr "" +"Невозможно назначить данные элементу.

Сообщение об ошибке:
" +"%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:644 +msgid "Resize rows to contents" +msgstr "Размер строк по содержимому" + +#: spyder/widgets/variableexplorer/collectionseditor.py:655 +#: spyder/widgets/variableexplorer/collectionseditor.py:990 +#: spyder/widgets/variableexplorer/collectionseditor.py:1007 +msgid "Plot" +msgstr "График" + +#: spyder/widgets/variableexplorer/collectionseditor.py:659 +msgid "Histogram" +msgstr "Гистограмма" + +#: spyder/widgets/variableexplorer/collectionseditor.py:663 +msgid "Show image" +msgstr "Показать изображение" + +#: spyder/widgets/variableexplorer/collectionseditor.py:667 +#: spyder/widgets/variableexplorer/collectionseditor.py:1015 +msgid "Save array" +msgstr "Сохранить массив" + +#: spyder/widgets/variableexplorer/collectionseditor.py:671 +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Insert" +msgstr "Вставить" + +#: spyder/widgets/variableexplorer/collectionseditor.py:674 +#: spyder/widgets/variableexplorer/collectionseditor.py:898 +msgid "Remove" +msgstr "Удалить" + +#: spyder/widgets/variableexplorer/collectionseditor.py:684 +#: spyder/widgets/variableexplorer/collectionseditor.py:919 +msgid "Duplicate" +msgstr "Создать копию" + +#: spyder/widgets/variableexplorer/collectionseditor.py:896 +msgid "Do you want to remove the selected item?" +msgstr "Вы хотите удалить выбранный элемент?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:897 +msgid "Do you want to remove all selected items?" +msgstr "Вы хотите удалить все выбранные элементы?" + +#: spyder/widgets/variableexplorer/collectionseditor.py:917 +msgid "New variable name:" +msgstr "Новое имя переменной:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:920 +msgid "Variable name:" +msgstr "Имя переменной:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +msgid "Key:" +msgstr "Ключ:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Value:" +msgstr "Значение:" + +#: spyder/widgets/variableexplorer/collectionseditor.py:978 +msgid "Import error" +msgstr "Ошибка импорта" + +#: spyder/widgets/variableexplorer/collectionseditor.py:979 +msgid "Please install matplotlib or guiqwt." +msgstr "Пожалуйста, установите matplotlib или guiqwt." + +#: spyder/widgets/variableexplorer/collectionseditor.py:991 +msgid "Unable to plot data.

Error message:
%s" +msgstr "" +"Не удалось построить график по данным.

Сообщение об ошибке:
" +"%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1008 +msgid "Unable to show image.

Error message:
%s" +msgstr "" +"Не удалось показать изображение.

Сообщение об ошибке:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1031 +msgid "Unable to save array

Error message:
%s" +msgstr "Не удалось сохранить массив

Сообщение об ошибке:
%s" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1056 +msgid "It was not possible to copy this array" +msgstr "Не удалось скопировать этот массив" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1081 +msgid "Clipboard contents" +msgstr "Содержимое буфера обмена" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1096 +msgid "Import from clipboard" +msgstr "Импортировать из буфера обмена" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1098 +msgid "Empty clipboard" +msgstr "Очистить буфер обмена" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1099 +msgid "Nothing to be imported from clipboard." +msgstr "Нечего импортировать из буфера обмена." + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To bool" +msgstr "В булево" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To complex" +msgstr "В комплексное" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To float" +msgstr "В вещественное" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To int" +msgstr "В целое" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:452 +msgid "To str" +msgstr "В строку" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:526 +msgid "%s editor" +msgstr "%s редактор" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:558 +msgid "Column min/max" +msgstr "Min/max столбца" + +#: spyder/widgets/variableexplorer/importwizard.py:116 +#: spyder/widgets/variableexplorer/importwizard.py:431 +msgid "Import as" +msgstr "Импортировать как" + +#: spyder/widgets/variableexplorer/importwizard.py:118 +msgid "data" +msgstr "данные" + +#: spyder/widgets/variableexplorer/importwizard.py:122 +msgid "code" +msgstr "код" + +#: spyder/widgets/variableexplorer/importwizard.py:125 +#: spyder/widgets/variableexplorer/importwizard.py:504 +msgid "text" +msgstr "текст" + +#: spyder/widgets/variableexplorer/importwizard.py:138 +msgid "Column separator:" +msgstr "Разделитель столбцов:" + +#: spyder/widgets/variableexplorer/importwizard.py:142 +msgid "Tab" +msgstr "табуляция" + +#: spyder/widgets/variableexplorer/importwizard.py:145 +#: spyder/widgets/variableexplorer/importwizard.py:163 +msgid "other" +msgstr "другой" + +#: spyder/widgets/variableexplorer/importwizard.py:156 +msgid "Row separator:" +msgstr "Разделитель строк:" + +#: spyder/widgets/variableexplorer/importwizard.py:160 +msgid "EOL" +msgstr "Конец строки" + +#: spyder/widgets/variableexplorer/importwizard.py:175 +msgid "Additional options" +msgstr "Дополнительные опции" + +#: spyder/widgets/variableexplorer/importwizard.py:179 +msgid "Skip rows:" +msgstr "Пропустить строки:" + +#: spyder/widgets/variableexplorer/importwizard.py:190 +msgid "Comments:" +msgstr "Комментарии:" + +#: spyder/widgets/variableexplorer/importwizard.py:196 +msgid "Transpose" +msgstr "Транспонировать" + +#: spyder/widgets/variableexplorer/importwizard.py:434 +msgid "array" +msgstr "массив" + +#: spyder/widgets/variableexplorer/importwizard.py:439 +msgid "list" +msgstr "список" + +#: spyder/widgets/variableexplorer/importwizard.py:444 +msgid "DataFrame" +msgstr "DataFrame" + +#: spyder/widgets/variableexplorer/importwizard.py:487 +#: spyder/widgets/variableexplorer/importwizard.py:571 +msgid "Import wizard" +msgstr "Мастер импорта" + +#: spyder/widgets/variableexplorer/importwizard.py:492 +msgid "Raw text" +msgstr "Необработанный текст" + +#: spyder/widgets/variableexplorer/importwizard.py:495 +msgid "variable_name" +msgstr "имя_переменной" + +#: spyder/widgets/variableexplorer/importwizard.py:506 +msgid "table" +msgstr "таблица" + +#: spyder/widgets/variableexplorer/importwizard.py:507 +msgid "Preview" +msgstr "Предварительный просмотр" + +#: spyder/widgets/variableexplorer/importwizard.py:511 +msgid "Variable Name" +msgstr "Имя переменной" + +#: spyder/widgets/variableexplorer/importwizard.py:534 +msgid "Done" +msgstr "Сделано" + +#: spyder/widgets/variableexplorer/importwizard.py:572 +msgid "" +"Unable to proceed to next step

Please check your entries." +"

Error message:
%s" +msgstr "" +"Невозможно перейти к следующему шагу

Пожалуйста, проверьте " +"введенные данные.

Сообщение об ошибке:
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:196 +msgid "Refresh" +msgstr "Обновить" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:200 +msgid "Refresh periodically" +msgstr "Обновлять периодически" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:207 +#: spyder/widgets/variableexplorer/namespacebrowser.py:487 +msgid "Import data" +msgstr "Импортировать данные" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:210 +#: spyder/widgets/variableexplorer/namespacebrowser.py:565 +#: spyder/widgets/variableexplorer/namespacebrowser.py:584 +msgid "Save data" +msgstr "Сохранить данные" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:215 +msgid "Save data as..." +msgstr "Сохранить данные как..." + +#: spyder/widgets/variableexplorer/namespacebrowser.py:227 +msgid "Exclude references which name starts with an underscore" +msgstr "Исключить ссылки, имена которых начинаются с подчёркивания" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:235 +msgid "Exclude references which name is uppercase" +msgstr "Исключить ссылки с именем в верхнем регистре" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:242 +msgid "Exclude references which name starts with an uppercase character" +msgstr "" +"Исключить ссылки, имена которых начинаются с символа в верхнем регистре" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:250 +msgid "" +"Exclude references to unsupported data types (i.e. which won't be handled/" +"saved correctly)" +msgstr "" +"Исключить ссылки на неподдерживаемые типы данных (т.е. которые не будут " +"правильно обработаны/сохранены)" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:345 +msgid "Object %s is not picklable" +msgstr "Объект %s не сериализуем" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:507 +msgid "" +"Unsupported file extension '%s'

Would you like to import it " +"anyway (by selecting a known file format)?" +msgstr "" +"Неподдерживаемое расширение файла '%s'

Хотите его " +"импортировать (выбрав известный формат файла)?" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:515 +msgid "Open file as:" +msgstr "Открыть файл как:" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:553 +msgid "Unable to load '%s'

Error message:
%s" +msgstr "Не удалось загрузить '%s'

Сообщение об ошибке:
%s" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:585 +msgid "Unable to save current workspace

Error message:
%s" +msgstr "" +"Невозможно сохранить текущую рабочую область

Сообщение об " +"ошибке:
%s" + +#: spyder/widgets/variableexplorer/texteditor.py:74 +msgid "Text editor" +msgstr "Текстовый редактор" + +#: spyder/widgets/variableexplorer/utils.py:29 +msgid "View and edit DataFrames and Series in the Variable Explorer" +msgstr "" +"Показ и редактирование структур DataFrame и Serie в Менеджере Переменных" + +#: spyder/widgets/variableexplorer/utils.py:34 +msgid "View and edit two and three dimensional arrays in the Variable Explorer" +msgstr "" +"Просмотр и редактирование 2-х и 3-мерных массивов в Менеджере Переменных" + +#: spyder/workers/updates.py:89 spyder/workers/updates.py:91 +msgid "Unable to retrieve information." +msgstr "Невозможно получить информацию." + +#: spyder/workers/updates.py:93 +msgid "" +"Unable to connect to the internet.

Make sure the connection is " +"working properly." +msgstr "" +"Не удалось подключиться к интернету.

Убедитесь, что соединение " +"работает должным образом." + +#: spyder/workers/updates.py:96 +msgid "Unable to check for updates." +msgstr "Не удалось проверить обновления." + +#~ msgid "Truncate values" +#~ msgstr "Обрезать значения" + +#~ msgid "" +#~ "\n" +#~ "\n" +#~ "{0}" +#~ msgstr "" +#~ "\n" +#~ "\n" +#~ "{0}" + +#~ msgid "Reload last session" +#~ msgstr "Перезапустить последнюю сессию" + +#~ msgid "Load session..." +#~ msgstr "Загрузить сессию..." + +#~ msgid "Load Spyder session" +#~ msgstr "Загрузить сессию Spyder" + +#, fuzzy +#~ msgid "Save session..." +#~ msgstr "Сохранить сессию" + +#~ msgid "Save current session and quit application" +#~ msgstr "Сохранить текущую сессию и закрыть приложение" + +#~ msgid "Open session" +#~ msgstr "Открыть сессию" + +#~ msgid "Spyder sessions" +#~ msgstr "Сессии Spyder" + +#~ msgid "Save session" +#~ msgstr "Сохранить сессию" + +#~ msgid "Jupyter Qtconsole integration" +#~ msgstr "Интеграция Qt-консоли Jupyter" + +#~ msgid "Python executable" +#~ msgstr "Исполняемый файл Python" + +#~ msgid "Trying to kill a kernel?" +#~ msgstr "Попытаться убить ядро?" + +#~ msgid "" +#~ "You can't close this kernel because it has one or more consoles connected " +#~ "to it.

You need to close them instead or you can kill the kernel " +#~ "using the second button from right to left." +#~ msgstr "" +#~ "Вы не можете закрыть это ядро, т.к. к нему подключены одна или более " +#~ "консолей

Сначала Вам нужно их закрыть или можете убить ядро второй " +#~ "кнопкой справа." + +#~ msgid "Kernel" +#~ msgstr "Ядро" + +#, fuzzy +#~ msgid "" +#~ "Either:
  1. Your IPython frontend and kernel versions are " +#~ "incompatible or
  2. You don't have IPython installed in " +#~ "your external interpreter.
In any case, we're sorry but we can't " +#~ "create a console for you." +#~ msgstr "" +#~ "Либо:
  1. версии Ваших пользовательского и ядерного IPython " +#~ "несовместимы, либо
  2. у Вас не установлен IPython в " +#~ "Вашем внешнем интерпретаторе.
В любом случае, мы сожалеем, что " +#~ "не можем создать консоль для Вас." + +#~ msgid "Kernel %s" +#~ msgstr "Ядро %s" + +#~ msgid "Open an IPython console" +#~ msgstr "Открыть консоль IPython" + +#~ msgid "" +#~ "The console monitor was disabled: the IPython kernel will be started as " +#~ "expected, but an IPython console will have to be connected manually to " +#~ "the kernel." +#~ msgstr "" +#~ "Монитор консоли был отключен: ядро IPython будет запущено как ожидалось, " +#~ "но консоль IPython нужно будет подключить к ядру вручную." + +#~ msgid "" +#~ "UMR excluded modules:\n" +#~ "(example: guidata, guiqwt)" +#~ msgstr "" +#~ "Модули-исключения для UMR:\n" +#~ "(например: guidata, guiqwt)" + +#~ msgid "" +#~ "This feature requires the Matplotlib library.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "Эта функция требует библиотеку Matplotlib.\n" +#~ "Похоже, Вы её не установили." + +#~ msgid "" +#~ "This feature requires the Sympy library.\n" +#~ "It seems you don't have it installed." +#~ msgstr "" +#~ "Эта функция требует библиотеку Sympy.\n" +#~ "По-видимому, она не установлена." + +#~ msgid "&Font..." +#~ msgstr "&Шрифт" + +#~ msgid "Set font style" +#~ msgstr "Установить стиль шрифта" + +#~ msgid "Select a new font" +#~ msgstr "Выбрать новый шрифт" + +#~ msgid "" +#~ "The kernel failed to start!! That's all we know... Please close this " +#~ "console and open a new one." +#~ msgstr "" +#~ "Ошибка запуска ядра!! Это всё, что известно... Пожалуйста, закройте " +#~ "консоль и откройте новую." + +#~ msgid "" +#~ "It seems the kernel died unexpectedly. Use 'Restart kernel' to continue " +#~ "using this console." +#~ msgstr "" +#~ "По видимому ядро неожиданно умерло. Используйте 'Перезапустить ядро' для " +#~ "продолжения работы в консоли." + +#~ msgid "Kernel process is either remote or unspecified. Cannot interrupt" +#~ msgstr "" +#~ "Процесс ядра либо подключён удалённо, либо неопределим. Невозможно " +#~ "прервать" + +#~ msgid "Kernel process is either remote or unspecified. Cannot restart." +#~ msgstr "" +#~ "Процесс ядра либо подключён удалённо, либо неопределим. Невозможно " +#~ "перезапустить." + +#~ msgid "its own configuration file" +#~ msgstr "его собственный файл конфигурации" + +#~ msgid " and " +#~ msgstr " и " + +#~ msgid "the following projects:
%s" +#~ msgstr "следующие проекты:
%s" + +#~ msgid "Existing Pydev project" +#~ msgstr "Существующий проект Pydev" + +#~ msgid "Close unrelated projects" +#~ msgstr "Закрыть несвязанные проекты" + +#~ msgid "Edit related projects" +#~ msgstr "Редактировать связанные проекты" + +#~ msgid "Add to PYTHONPATH" +#~ msgstr "Добавить в PYTHONPATH" + +#~ msgid "Remove from PYTHONPATH" +#~ msgstr "Удалить из PYTHONPATH" + +#~ msgid "Properties" +#~ msgstr "Свойства" + +#~ msgid "" +#~ "The workspace was unable to load or save %s

Please check if you " +#~ "have the permission to write the associated configuration files." +#~ msgstr "" +#~ "Рабочую область не удалось загрузить или сохранить %s

Пожалуйста, " +#~ "проверьте, достаточно ли у Вас прав для записи соответствующих файлов " +#~ "конфигурации." + +#~ msgid "Import directory" +#~ msgstr "Импортировать каталог" + +#~ msgid "" +#~ "The following directory is not in workspace:
%s

Do you " +#~ "want to continue (and copy the directory to workspace)?" +#~ msgstr "" +#~ "Выбранного каталога нет в рабочей области:
%s

Хотите " +#~ "продолжить (и скопировать каталог в область)?" + +#~ msgid "The project %s is already opened!" +#~ msgstr "Проект %s уже открыт!" + +#~ msgid "" +#~ "The project root path directory is inside the workspace but not as the " +#~ "expected tree level. It is not a directory of the workspace:
%s" +#~ msgstr "" +#~ "Корневой каталог проекта находится в рабочей области, но не в качестве " +#~ "ожидаемого уровня дерева. Он не является каталогом рабочей области:
" +#~ "%s" + +#~ msgid "A project named %s already exists" +#~ msgstr "Проект с именем %s уже существует" + +#~ msgid "" +#~ "Invalid project name.

Name must match the following regular " +#~ "expression:
%s" +#~ msgstr "" +#~ "Неверное имя проекта.

Название должно соответствовать регулярному " +#~ "выражению:
%s" + +#~ msgid "" +#~ "The following directory is not empty:
%s

Do you want to " +#~ "continue?" +#~ msgstr "Выбранный каталог не пуст:
%s

Хотите продолжить?" + +#~ msgid "New project" +#~ msgstr "Новый проект" + +#~ msgid "" +#~ "The current workspace has not been configured yet.\n" +#~ "Do you want to do this now?" +#~ msgstr "" +#~ "Текущая рабочая область ещё не сконфигурирована.\n" +#~ "Сделать это сейчас?" + +#~ msgid "Import existing project" +#~ msgstr "Импортировать существующий проект" + +#~ msgid "Select projects to import" +#~ msgstr "Выбрать проекты для импорта" + +#~ msgid "The folder %s does not contain a valid %s project" +#~ msgstr "Каталог %s не содержит подходящего проекта %s" + +#~ msgid "Import existing Pydev project" +#~ msgstr "Импортировать существующий проект Pydev" + +#~ msgid "" +#~ "Unable to read Pydev project %s

Error message:
%s" +#~ msgstr "" +#~ "Не удалось прочитать проект Pydev %s

Сообщение об " +#~ "ошибке:
%s" + +#~ msgid "Select projects which are related to %s" +#~ msgstr "Выбранные проекты связаны с %s" + +#~ msgid "" +#~ "Statistics on source files only:
(Python, Cython, IPython, Enaml,C/C+" +#~ "+, Fortran)

%s files.
%s lines of code." +#~ msgstr "" +#~ "Статистика по исходному коду только:
(Python, Cython, IPython, Enaml,C/" +#~ "C++, Fortran)

%s файлов.
%s строк кода." + +#~ msgid "Select an existing workspace directory, or create a new one" +#~ msgstr "Выберите каталог рабочей области или создайте новый" + +#~ msgid "" +#~ "What is the workspace?

A Spyder workspace is " +#~ "a directory on your filesystem that contains Spyder projects and ." +#~ "spyderworkspace configuration file.

A Spyder project is " +#~ "a directory with source code (and other related files) and a " +#~ "configuration file (named .spyderproject) with project settings " +#~ "(PYTHONPATH, linked projects, ...).
" +#~ msgstr "" +#~ "Что такое рабочая область?

A Рабочая область " +#~ "Spyder - каталог в вашей файловой системе, содержащий проекты Spyder " +#~ "и файл конфигурации .spyderworkspace.

A Проект Spyder - каталог с исходным кодом (и другими связанными файлами) и " +#~ "конфигурационным файлом (с расширением .spyderproject), содержащим " +#~ "настройки проекта (PYTHONPATH, взаимосвязанные проекты и т.п.)." + +#~ msgid "This is the current workspace directory" +#~ msgstr "Это текущий каталог рабочей области" + +#~ msgid "" +#~ "The following directory is not a Spyder workspace:
%s

Do you " +#~ "want to create a new workspace in this directory?" +#~ msgstr "" +#~ "Выбранный каталог не является рабочей областью Spyder:
" +#~ "%s

Хотите создать новую рабочую область в этом каталоге?" + +#~ msgid "Unable to retrieve data.

Error message:
%s" +#~ msgstr "Не удалось вывести данные.

Сообщение об ошибке:
%s" + +#~ msgid "Save session and quit..." +#~ msgstr "Сохранить сессию и выйти..." + +#~ msgid "Breakpoints" +#~ msgstr "Точки останова" + +#~ msgid "Exit" +#~ msgstr "Выход" + +#~ msgid "Exit Debug" +#~ msgstr "Завершить отладку" + +#~ msgid "Updates" +#~ msgstr "Обновления" + +#~ msgid "Set shell font style" +#~ msgstr "Установить стиль шрифта среды" + +#~ msgid "Text and margin font style" +#~ msgstr "Стиль шрифта текста и отступов" + +#~ msgid "tab" +#~ msgstr "табуляция" + +#~ msgid "Rich text help on the Object Inspector" +#~ msgstr "Форматированная справка в Инспекторе объектов" + +#~ msgid "Object inspector" +#~ msgstr "Инспектор объектов" + +#~ msgid "Preferences > Object Inspector" +#~ msgstr "Параметры > Инспектор объектов" + +#~ msgid "Set as current console's working directory" +#~ msgstr "Установить рабочую папку для текущей консоли" + +#~ msgid "Loading object inspector..." +#~ msgstr "Загрузка инспектора объектов..." + +#~ msgid "The Object Inspector" +#~ msgstr "Инспектор объектов" + +#~ msgid "(Experimental) Editor's code completion, go-to-definition and help" +#~ msgstr "" +#~ "(Experimental) Автодополнение кода редактора, переход к определению и " +#~ "справка" + +#~ msgid "" +#~ "This path is incorrect.\n" +#~ "Enter a correct directory path,\n" +#~ "then press enter to validate" +#~ msgstr "" +#~ "Этот путь неверный.\n" +#~ "Введите правильный путь к папке и\n" +#~ "нажмите Enter для подтверждения." + +#~ msgid "Save Python script" +#~ msgstr "Сохранить скрипт Python" + +#~ msgid "Vertical dockwidget tabs" +#~ msgstr "Разместить вертикально вкладки плавающих окон" + +#~ msgid "Custom dockwidget margin:" +#~ msgstr "Дополнительный отступ между плавающими окнами:" + +#~ msgid "" +#~ "Note: add analysis:ignore in a comment to ignore code/style " +#~ "analysis warnings. For more informations on style guide for Python code, " +#~ "please refer to the %s page." +#~ msgstr "" +#~ "Примечание: добавлять analysis:ignore в комментарий для " +#~ "игнорирования предупреждений анализа кода/стиля. Для дополнительной " +#~ "информации по стилю кода Python, перейдите на страницу %s." + +#~ msgid "Run &selection" +#~ msgstr "Выполнить в&ыделение" + +#~ msgid "(for example: kernel-3764.json, or simply 3764)" +#~ msgstr "(например: kernel-3764.json, или просто 3764)" + +#~ msgid "Mismatch between kernel and frontend" +#~ msgstr "Несовместимость ядра и интерфейса" + +#~ msgid "" +#~ "Your IPython frontend and kernel versions are incompatible!!

We're sorry but we can't create an IPython console for you." +#~ msgstr "" +#~ "Версии Ваших интерфейса и ядра IPython несовместимы!!

Мы " +#~ "извиняемся, но мы не можем создать консоль IPython." + +#~ msgid "" +#~ "%s will be closed.\n" +#~ "Do you want to kill the associated kernel and all of its clients?" +#~ msgstr "" +#~ "%s была закрыта.\n" +#~ "Хотите убить связанное ядро и все его клиенты?" + +#~ msgid "Filter:" +#~ msgstr "Фильтр:" + +#~ msgid "(press Enter to edit file)" +#~ msgstr "(нажмите Enter для редактирования файла)" + +#~ msgid "&Edit file" +#~ msgstr "&Редактировать файл" + +#~ msgid "&Close file" +#~ msgstr "&Закрыть файл" + +#~ msgid "Hint: press Alt to show accelerators" +#~ msgstr "Совет: нажмите Alt, чтобы увидеть акселераторы" + +#~ msgid "Import as array" +#~ msgstr "Импортировать как массив" + +#~ msgid "Interrupt kernel" +#~ msgstr "Прервать ядро" diff -Nru spyder-2.3.8+dfsg1/spyder/locale/spyder.pot spyder-3.0.2+dfsg1/spyder/locale/spyder.pot --- spyder-2.3.8+dfsg1/spyder/locale/spyder.pot 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/locale/spyder.pot 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,4668 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: spyder/app/mainwindow.py:125 +msgid "Initializing..." +msgstr "" + +#: spyder/app/mainwindow.py:241 +msgid "Numpy and Scipy documentation" +msgstr "" + +#: spyder/app/mainwindow.py:243 spyder/app/mainwindow.py:1027 +msgid "Matplotlib documentation" +msgstr "" + +#: spyder/app/mainwindow.py:246 +msgid "PyQt4 Reference Guide" +msgstr "" + +#: spyder/app/mainwindow.py:249 +msgid "PyQt4 API Reference" +msgstr "" + +#: spyder/app/mainwindow.py:251 +msgid "Python(x,y)" +msgstr "" + +#: spyder/app/mainwindow.py:253 +msgid "WinPython" +msgstr "" + +#: spyder/app/mainwindow.py:525 +msgid "Close current pane" +msgstr "" + +#: spyder/app/mainwindow.py:530 +msgid "Lock panes" +msgstr "" + +#: spyder/app/mainwindow.py:537 +msgid "Use next layout" +msgstr "" + +#: spyder/app/mainwindow.py:541 +msgid "Use previous layout" +msgstr "" + +#: spyder/app/mainwindow.py:560 spyder/widgets/sourcecode/codeeditor.py:2505 +msgid "Undo" +msgstr "" + +#: spyder/app/mainwindow.py:562 spyder/widgets/sourcecode/codeeditor.py:2508 +msgid "Redo" +msgstr "" + +#: spyder/app/mainwindow.py:564 spyder/widgets/shell.py:121 +#: spyder/widgets/sourcecode/codeeditor.py:2514 +#: spyder/widgets/variableexplorer/arrayeditor.py:453 +#: spyder/widgets/variableexplorer/collectionseditor.py:649 +#: spyder/widgets/variableexplorer/dataframeeditor.py:445 +msgid "Copy" +msgstr "" + +#: spyder/app/mainwindow.py:566 spyder/widgets/shell.py:117 +#: spyder/widgets/sourcecode/codeeditor.py:2511 +msgid "Cut" +msgstr "" + +#: spyder/app/mainwindow.py:568 spyder/widgets/shell.py:125 +#: spyder/widgets/sourcecode/codeeditor.py:2517 +#: spyder/widgets/variableexplorer/collectionseditor.py:646 +msgid "Paste" +msgstr "" + +#: spyder/app/mainwindow.py:571 spyder/widgets/shell.py:138 +#: spyder/widgets/sourcecode/codeeditor.py:2520 +msgid "Select All" +msgstr "" + +#: spyder/app/mainwindow.py:581 spyder/plugins/editor.py:1353 +msgid "&File" +msgstr "" + +#: spyder/app/mainwindow.py:582 spyder/plugins/editor.py:1345 +msgid "File toolbar" +msgstr "" + +#: spyder/app/mainwindow.py:586 spyder/plugins/editor.py:1354 +msgid "&Edit" +msgstr "" + +#: spyder/app/mainwindow.py:587 spyder/plugins/editor.py:1350 +msgid "Edit toolbar" +msgstr "" + +#: spyder/app/mainwindow.py:591 spyder/plugins/editor.py:1355 +msgid "&Search" +msgstr "" + +#: spyder/app/mainwindow.py:592 spyder/plugins/editor.py:1346 +msgid "Search toolbar" +msgstr "" + +#: spyder/app/mainwindow.py:596 spyder/plugins/editor.py:1356 +msgid "Sour&ce" +msgstr "" + +#: spyder/app/mainwindow.py:597 spyder/plugins/editor.py:1347 +msgid "Source toolbar" +msgstr "" + +#: spyder/app/mainwindow.py:601 spyder/plugins/editor.py:780 +#: spyder/plugins/editor.py:1357 +msgid "&Run" +msgstr "" + +#: spyder/app/mainwindow.py:602 spyder/plugins/editor.py:1348 +msgid "Run toolbar" +msgstr "" + +#: spyder/app/mainwindow.py:606 spyder/plugins/editor.py:739 +msgid "&Debug" +msgstr "" + +#: spyder/app/mainwindow.py:607 spyder/plugins/editor.py:1349 +msgid "Debug toolbar" +msgstr "" + +#: spyder/app/mainwindow.py:611 +msgid "C&onsoles" +msgstr "" + +#: spyder/app/mainwindow.py:614 +msgid "&Projects" +msgstr "" + +#: spyder/app/mainwindow.py:617 spyder/plugins/editor.py:1358 +msgid "&Tools" +msgstr "" + +#: spyder/app/mainwindow.py:620 +msgid "&View" +msgstr "" + +#: spyder/app/mainwindow.py:623 +msgid "&Help" +msgstr "" + +#: spyder/app/mainwindow.py:628 +msgid "Welcome to Spyder!" +msgstr "" + +#: spyder/app/mainwindow.py:633 +msgid "Pre&ferences" +msgstr "" + +#: spyder/app/mainwindow.py:640 spyder/widgets/pathmanager.py:49 +msgid "PYTHONPATH manager" +msgstr "" + +#: spyder/app/mainwindow.py:643 +msgid "Python Path Manager" +msgstr "" + +#: spyder/app/mainwindow.py:646 +msgid "Update module names list" +msgstr "" + +#: spyder/app/mainwindow.py:649 +msgid "Refresh list of module names available in PYTHONPATH" +msgstr "" + +#: spyder/app/mainwindow.py:652 +msgid "Reset Spyder to factory defaults" +msgstr "" + +#: spyder/app/mainwindow.py:657 +msgid "Current user environment variables..." +msgstr "" + +#: spyder/app/mainwindow.py:659 +msgid "Show and edit current user environment variables in Windows registry (i.e. for all sessions)" +msgstr "" + +#: spyder/app/mainwindow.py:668 spyder/app/mainwindow.py:1123 +msgid "External Tools" +msgstr "" + +#: spyder/app/mainwindow.py:672 +msgid "Python(x,y) launcher" +msgstr "" + +#: spyder/app/mainwindow.py:679 +msgid "WinPython control panel" +msgstr "" + +#: spyder/app/mainwindow.py:688 +msgid "Qt Designer" +msgstr "" + +#: spyder/app/mainwindow.py:693 +msgid "Qt Linguist" +msgstr "" + +#: spyder/app/mainwindow.py:699 +msgid "Qt examples" +msgstr "" + +#: spyder/app/mainwindow.py:720 +msgid "guidata examples" +msgstr "" + +#: spyder/app/mainwindow.py:731 +msgid "guiqwt examples" +msgstr "" + +#: spyder/app/mainwindow.py:736 +msgid "Sift" +msgstr "" + +#: spyder/app/mainwindow.py:746 +msgid "ViTables" +msgstr "" + +#: spyder/app/mainwindow.py:760 +msgid "Fullscreen mode" +msgstr "" + +#: spyder/app/mainwindow.py:772 +msgid "Main toolbar" +msgstr "" + +#: spyder/app/mainwindow.py:781 +msgid "" +"Spyder Internal Console\n" +"\n" +"This console is used to report application\n" +"internal errors and to inspect Spyder\n" +"internals with the following commands:\n" +" spy.app, spy.window, dir(spy)\n" +"\n" +"Please don't use it to run your code\n" +"\n" +msgstr "" + +#: spyder/app/mainwindow.py:798 +msgid "Loading help..." +msgstr "" + +#: spyder/app/mainwindow.py:805 +msgid "Loading outline explorer..." +msgstr "" + +#: spyder/app/mainwindow.py:813 +msgid "Loading editor..." +msgstr "" + +#: spyder/app/mainwindow.py:819 spyder/plugins/console.py:134 +#: spyder/widgets/ipythonconsole/client.py:280 +msgid "&Quit" +msgstr "" + +#: spyder/app/mainwindow.py:821 spyder/plugins/console.py:136 +msgid "Quit" +msgstr "" + +#: spyder/app/mainwindow.py:825 +msgid "&Restart" +msgstr "" + +#: spyder/app/mainwindow.py:827 +msgid "Restart" +msgstr "" + +#: spyder/app/mainwindow.py:844 +msgid "Loading file explorer..." +msgstr "" + +#: spyder/app/mainwindow.py:851 +msgid "Loading history plugin..." +msgstr "" + +#: spyder/app/mainwindow.py:862 +msgid "Loading online help..." +msgstr "" + +#: spyder/app/mainwindow.py:867 +msgid "Loading project explorer..." +msgstr "" + +#: spyder/app/mainwindow.py:874 +msgid "Loading external console..." +msgstr "" + +#: spyder/app/mainwindow.py:880 +msgid "Loading namespace browser..." +msgstr "" + +#: spyder/app/mainwindow.py:887 +msgid "Loading IPython console..." +msgstr "" + +#: spyder/app/mainwindow.py:892 +msgid "Setting up main window..." +msgstr "" + +#: spyder/app/mainwindow.py:895 +msgid "Dependencies..." +msgstr "" + +#: spyder/app/mainwindow.py:899 +msgid "Report issue..." +msgstr "" + +#: spyder/app/mainwindow.py:903 +msgid "Spyder support..." +msgstr "" + +#: spyder/app/mainwindow.py:906 +msgid "Check for updates..." +msgstr "" + +#: spyder/app/mainwindow.py:929 +msgid "Spyder documentation" +msgstr "" + +#: spyder/app/mainwindow.py:934 +msgid "Spyder tutorial" +msgstr "" + +#: spyder/app/mainwindow.py:941 +msgid "Interactive tours" +msgstr "" + +#: spyder/app/mainwindow.py:969 +msgid "Python documentation" +msgstr "" + +#: spyder/app/mainwindow.py:975 spyder/app/mainwindow.py:1019 +msgid "IPython documentation" +msgstr "" + +#: spyder/app/mainwindow.py:976 +msgid "Intro to IPython" +msgstr "" + +#: spyder/app/mainwindow.py:978 +msgid "Quick reference" +msgstr "" + +#: spyder/app/mainwindow.py:980 +msgid "Console help" +msgstr "" + +#: spyder/app/mainwindow.py:1017 +msgid "Python(x,y) documentation folder" +msgstr "" + +#: spyder/app/mainwindow.py:1021 +msgid "guidata documentation" +msgstr "" + +#: spyder/app/mainwindow.py:1024 +msgid "guiqwt documentation" +msgstr "" + +#: spyder/app/mainwindow.py:1030 +msgid "NumPy documentation" +msgstr "" + +#: spyder/app/mainwindow.py:1032 +msgid "NumPy reference guide" +msgstr "" + +#: spyder/app/mainwindow.py:1034 +msgid "NumPy user guide" +msgstr "" + +#: spyder/app/mainwindow.py:1036 +msgid "SciPy documentation" +msgstr "" + +#: spyder/app/mainwindow.py:1043 +msgid "Installed Python modules" +msgstr "" + +#: spyder/app/mainwindow.py:1047 +msgid "Online documentation" +msgstr "" + +#: spyder/app/mainwindow.py:1059 +msgid "Qt documentation" +msgstr "" + +#: spyder/app/mainwindow.py:1065 +msgid "About %s..." +msgstr "" + +#: spyder/app/mainwindow.py:1089 +msgid "Panes" +msgstr "" + +#: spyder/app/mainwindow.py:1091 +msgid "Toolbars" +msgstr "" + +#: spyder/app/mainwindow.py:1092 +msgid "Window layouts" +msgstr "" + +#: spyder/app/mainwindow.py:1101 spyder/app/mainwindow.py:1894 +#: spyder/app/mainwindow.py:1895 +msgid "Show toolbars" +msgstr "" + +#: spyder/app/mainwindow.py:1116 +msgid "Attached console window (debugging)" +msgstr "" + +#: spyder/app/mainwindow.py:1295 spyder/plugins/projects.py:244 +#: spyder/widgets/explorer.py:639 spyder/widgets/explorer.py:744 +#: spyder/widgets/externalshell/pythonshell.py:533 +#: spyder/widgets/externalshell/systemshell.py:105 +#: spyder/widgets/variableexplorer/arrayeditor.py:573 +#: spyder/widgets/variableexplorer/collectionseditor.py:417 +#: spyder/widgets/variableexplorer/dataframeeditor.py:593 +msgid "Error" +msgstr "" + +#: spyder/app/mainwindow.py:1296 +msgid "You have missing dependencies!

%s

Please install them to avoid this message.

Note: Spyder could work without some of these dependencies, however to have a smooth experience when using Spyder we strongly recommend you to install all the listed missing dependencies.

Failing to install these dependencies might result in bugs. Please be sure that any found bugs are not the direct result of missing dependencies, prior to reporting a new issue." +msgstr "" + +#: spyder/app/mainwindow.py:1741 +msgid "Spyder Default Layout" +msgstr "" + +#: spyder/app/mainwindow.py:1759 +msgid "Save current layout" +msgstr "" + +#: spyder/app/mainwindow.py:1763 +msgid "Layout preferences" +msgstr "" + +#: spyder/app/mainwindow.py:1767 +msgid "Reset to spyder default" +msgstr "" + +#: spyder/app/mainwindow.py:1787 spyder/app/mainwindow.py:1809 +#: spyder/app/mainwindow.py:1872 spyder/app/mainwindow.py:2656 +#: spyder/plugins/configdialog.py:1254 spyder/plugins/externalconsole.py:446 +#: spyder/plugins/ipythonconsole.py:119 spyder/plugins/ipythonconsole.py:835 +#: spyder/plugins/maininterpreter.py:167 spyder/plugins/maininterpreter.py:195 +#: spyder/utils/environ.py:100 spyder/utils/environ.py:113 +#: spyder/widgets/variableexplorer/arrayeditor.py:496 +#: spyder/widgets/variableexplorer/collectionseditor.py:408 +#: spyder/widgets/variableexplorer/collectionseditor.py:1055 +msgid "Warning" +msgstr "" + +#: spyder/app/mainwindow.py:1788 +msgid "" +"Window layout will be reset to default settings: this affects window position, size and dockwidgets.\n" +"Do you want to continue?" +msgstr "" + +#: spyder/app/mainwindow.py:1810 +msgid "Layout %s will be overwritten. Do you want to continue?" +msgstr "" + +#: spyder/app/mainwindow.py:1873 +msgid "Quick switch layout #%s has not yet been defined." +msgstr "" + +#: spyder/app/mainwindow.py:1891 spyder/app/mainwindow.py:1892 +msgid "Hide toolbars" +msgstr "" + +#: spyder/app/mainwindow.py:2185 spyder/app/mainwindow.py:2186 +msgid "Maximize current pane" +msgstr "" + +#: spyder/app/mainwindow.py:2189 +msgid "Restore current pane" +msgstr "" + +#: spyder/app/mainwindow.py:2190 +msgid "Restore pane to its original size" +msgstr "" + +#: spyder/app/mainwindow.py:2274 +msgid "About %s" +msgstr "" + +#: spyder/app/mainwindow.py:2401 spyder/plugins/editor.py:158 +#: spyder/plugins/runconfig.py:322 spyder/plugins/runconfig.py:444 +#: spyder/plugins/runconfig.py:449 spyder/utils/programs.py:285 +#: spyder/widgets/explorer.py:271 +#: spyder/widgets/externalshell/baseshell.py:127 +msgid "Run" +msgstr "" + +#: spyder/app/mainwindow.py:2402 +msgid "Running an external system terminal is not supported on platform %s." +msgstr "" + +#: spyder/app/mainwindow.py:2657 +msgid "Spyder will restart and reset to default settings:

Do you want to continue?" +msgstr "" + +#: spyder/app/mainwindow.py:2754 spyder/widgets/helperwidgets.py:250 +msgid "Spyder updates" +msgstr "" + +#: spyder/app/mainwindow.py:2755 spyder/plugins/configdialog.py:819 +msgid "Check for updates on startup" +msgstr "" + +#: spyder/app/mainwindow.py:2775 +msgid "Spyder %s is available!

Please use your package manager to update Spyder or go to our Releases page to download this new version.

If you are not sure how to proceed to update Spyder please refer to our Installation instructions." +msgstr "" + +#: spyder/app/mainwindow.py:2787 +msgid "Spyder is up to date." +msgstr "" + +#: spyder/app/restart.py:133 +msgid "" +"It was not possible to close the previous Spyder instance.\n" +"Restart aborted." +msgstr "" + +#: spyder/app/restart.py:135 +msgid "" +"Spyder could not reset to factory defaults.\n" +"Restart aborted." +msgstr "" + +#: spyder/app/restart.py:137 +msgid "" +"It was not possible to restart Spyder.\n" +"Operation aborted." +msgstr "" + +#: spyder/app/restart.py:139 +msgid "Spyder exit error" +msgstr "" + +#: spyder/app/restart.py:140 +msgid "Spyder reset error" +msgstr "" + +#: spyder/app/restart.py:141 +msgid "Spyder restart error" +msgstr "" + +#: spyder/app/restart.py:165 +msgid "Closing Spyder" +msgstr "" + +#: spyder/app/restart.py:239 +msgid "Resetting Spyder to defaults" +msgstr "" + +#: spyder/app/restart.py:271 +msgid "Restarting" +msgstr "" + +#: spyder/app/tour.py:124 +msgid "Welcome to the Introduction tour" +msgstr "" + +#: spyder/app/tour.py:125 +msgid "Spyder is a powerful Interactive Development Environment (or IDE) for the Python programming language.

Here we are going to guide you through its most important features.

Please use the arrow keys or click on the buttons below to move along the tour." +msgstr "" + +#: spyder/app/tour.py:134 +msgid "The Editor" +msgstr "" + +#: spyder/app/tour.py:135 +msgid "This is the pane where you write Python code before evaluating it. You can get automatic suggestions and completions while writing, by pressing the Tab key next to a given text.

The Editor comes with a line number area (highlighted here in red), where Spyder shows warnings and syntax errors. They can help you to detect potential problems before running the code.

You can also set debug breakpoints in the line number area, by doing a double click next to a non-empty line." +msgstr "" + +#: spyder/app/tour.py:150 +msgid "The IPython console" +msgstr "" + +#: spyder/app/tour.py:151 +msgid "This is one of panes where you can run or execute the code you wrote on the Editor. To do it you need to press the F5 key.

This console comes with several useful features that greatly improve your programming workflow (like syntax highlighting and inline plots). If you want to know more about them, please follow this link.

Please click on the button below to run some simple code in this console. This will be useful to show you other important features." +msgstr "" + +#: spyder/app/tour.py:167 +msgid "The Variable Explorer" +msgstr "" + +#: spyder/app/tour.py:168 +msgid "In this pane you can view and edit the variables generated during the execution of a program, or those entered directly in one of Spyder consoles.

As you can see, the Variable Explorer is showing the variables generated during the last step of this tour. By doing a double-click on any of them, a new window will be opened, where you can inspect and modify their contents." +msgstr "" + +#: spyder/app/tour.py:180 +msgid "The Python console" +msgstr "" + +#: spyder/app/tour.py:181 +msgid "You can also run your code on a Python console. These consoles are useful because they let you run a file in a console dedicated only to it.To select this behavior, please press the F6 key.

By pressing the button below and then focusing the Variable Explorer, you will notice that Python consoles are also connected to that pane, and that the Variable Explorer only shows the variables of the currently focused console." +msgstr "" + +#: spyder/app/tour.py:195 spyder/plugins/help.py:485 +#: spyder/plugins/help.py:929 spyder/widgets/internalshell.py:270 +msgid "Help" +msgstr "" + +#: spyder/app/tour.py:196 +msgid "This pane displays documentation of the functions, classes, methods or modules you are currently using in the Editor or the Consoles.

To use it, you need to press Ctrl+I in front of an object. If that object has some documentation associated with it, it will be displayed here." +msgstr "" + +#: spyder/app/tour.py:206 +msgid "The File Explorer" +msgstr "" + +#: spyder/app/tour.py:207 +msgid "This pane lets you navigate through the directories and files present in your computer.

You can also open any of these files with its corresponding application, by doing a double click on it.

There is one exception to this rule: plain-text files will always be opened in the Spyder Editor." +msgstr "" + +#: spyder/app/tour.py:217 +msgid "The History Log" +msgstr "" + +#: spyder/app/tour.py:218 +msgid "This pane records all commands introduced in the Python and IPython consoles." +msgstr "" + +#: spyder/app/tour.py:266 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" + +#: spyder/app/tour.py:270 +msgid "Welcome to Spyder introduction tour" +msgstr "" + +#: spyder/app/tour.py:271 +msgid "Spyder is an interactive development environment based on bla" +msgstr "" + +#: spyder/app/tour.py:276 +msgid "Introduction tour" +msgstr "" + +#: spyder/app/tour.py:277 +msgid "New features in version 3.0" +msgstr "" + +#: spyder/app/tour.py:555 spyder/plugins/ipythonconsole.py:431 +msgid "Run code" +msgstr "" + +#: spyder/app/tour.py:824 +msgid "Go to step: " +msgstr "" + +#: spyder/config/base.py:249 +msgid "Update LANGUAGE_CODES (inside config/base.py) if a new translation has been added to Spyder" +msgstr "" + +#: spyder/config/ipython.py:23 +msgid "Integrate the IPython console" +msgstr "" + +#: spyder/config/ipython.py:25 +msgid "Manipulate Jupyter notebooks on the Editor" +msgstr "" + +#: spyder/config/utils.py:24 +msgid "Python files" +msgstr "" + +#: spyder/config/utils.py:25 +msgid "Cython/Pyrex files" +msgstr "" + +#: spyder/config/utils.py:26 +msgid "C files" +msgstr "" + +#: spyder/config/utils.py:27 +msgid "C++ files" +msgstr "" + +#: spyder/config/utils.py:28 +msgid "OpenCL files" +msgstr "" + +#: spyder/config/utils.py:29 +msgid "Fortran files" +msgstr "" + +#: spyder/config/utils.py:30 +msgid "IDL files" +msgstr "" + +#: spyder/config/utils.py:31 +msgid "MATLAB files" +msgstr "" + +#: spyder/config/utils.py:32 +msgid "Julia files" +msgstr "" + +#: spyder/config/utils.py:33 +msgid "Yaml files" +msgstr "" + +#: spyder/config/utils.py:34 +msgid "Patch and diff files" +msgstr "" + +#: spyder/config/utils.py:35 +msgid "Batch files" +msgstr "" + +#: spyder/config/utils.py:36 spyder/utils/iofuncs.py:426 +msgid "Text files" +msgstr "" + +#: spyder/config/utils.py:37 +msgid "reStructuredText files" +msgstr "" + +#: spyder/config/utils.py:38 +msgid "gettext files" +msgstr "" + +#: spyder/config/utils.py:39 +msgid "NSIS files" +msgstr "" + +#: spyder/config/utils.py:40 +msgid "Web page files" +msgstr "" + +#: spyder/config/utils.py:41 +msgid "XML files" +msgstr "" + +#: spyder/config/utils.py:42 +msgid "Javascript files" +msgstr "" + +#: spyder/config/utils.py:43 +msgid "Json files" +msgstr "" + +#: spyder/config/utils.py:44 +msgid "IPython notebooks" +msgstr "" + +#: spyder/config/utils.py:45 +msgid "Enaml files" +msgstr "" + +#: spyder/config/utils.py:46 +msgid "Configuration files" +msgstr "" + +#: spyder/config/utils.py:51 spyder/widgets/explorer.py:712 +msgid "All files" +msgstr "" + +#: spyder/config/utils.py:121 +msgid "Supported text files" +msgstr "" + +#: spyder/plugins/__init__.py:508 spyder/plugins/editor.py:98 +#: spyder/plugins/editor.py:545 spyder/plugins/editor.py:1729 +#: spyder/plugins/help.py:118 spyder/plugins/help.py:385 +#: spyder/widgets/editor.py:371 spyder/widgets/sourcecode/codeeditor.py:97 +#: spyder/widgets/sourcecode/codeeditor.py:3001 +msgid "Editor" +msgstr "" + +#: spyder/plugins/configdialog.py:139 +msgid "Reset to defaults" +msgstr "" + +#: spyder/plugins/configdialog.py:151 +msgid "Preferences" +msgstr "" + +#: spyder/plugins/configdialog.py:491 +msgid "Invalid directory path" +msgstr "" + +#: spyder/plugins/configdialog.py:494 spyder/plugins/configdialog.py:509 +#: spyder/plugins/runconfig.py:177 spyder/plugins/runconfig.py:241 +#: spyder/plugins/workingdirectory.py:291 spyder/widgets/explorer.py:626 +#: spyder/widgets/externalshell/pythonshell.py:616 +#: spyder/widgets/findinfiles.py:504 spyder/widgets/pathmanager.py:224 +#: spyder/widgets/projects/projectdialog.py:155 +msgid "Select directory" +msgstr "" + +#: spyder/plugins/configdialog.py:521 +msgid "Invalid file path" +msgstr "" + +#: spyder/plugins/configdialog.py:524 spyder/plugins/configdialog.py:541 +msgid "Select file" +msgstr "" + +#: spyder/plugins/configdialog.py:540 +msgid "All files (*)" +msgstr "" + +#: spyder/plugins/configdialog.py:613 spyder/widgets/formlayout.py:215 +msgid "Bold" +msgstr "" + +#: spyder/plugins/configdialog.py:616 spyder/widgets/formlayout.py:210 +msgid "Italic" +msgstr "" + +#: spyder/plugins/configdialog.py:670 +msgid "Font: " +msgstr "" + +#: spyder/plugins/configdialog.py:676 +msgid "Size: " +msgstr "" + +#: spyder/plugins/configdialog.py:695 +msgid "Font style" +msgstr "" + +#: spyder/plugins/configdialog.py:772 +msgid "Spyder needs to restart to change the following setting:" +msgstr "" + +#: spyder/plugins/configdialog.py:775 +msgid "Spyder needs to restart to change the following settings:" +msgstr "" + +#: spyder/plugins/configdialog.py:777 +msgid "Do you wish to restart now?" +msgstr "" + +#: spyder/plugins/configdialog.py:783 +msgid "Information" +msgstr "" + +#: spyder/plugins/configdialog.py:797 spyder/plugins/configdialog.py:804 +#: spyder/widgets/projects/configdialog.py:74 +msgid "General" +msgstr "" + +#: spyder/plugins/configdialog.py:807 +msgid "Language" +msgstr "" + +#: spyder/plugins/configdialog.py:810 +msgid "Use a single instance" +msgstr "" + +#: spyder/plugins/configdialog.py:812 +msgid "Set this to open external
Python files in an already running instance (Requires a restart)" +msgstr "" + +#: spyder/plugins/configdialog.py:815 +msgid "Prompt when exiting" +msgstr "" + +#: spyder/plugins/configdialog.py:816 +msgid "Pop up internal console when internal errors appear" +msgstr "" + +#: spyder/plugins/configdialog.py:836 spyder/plugins/editor.py:107 +#: spyder/plugins/externalconsole.py:57 spyder/plugins/ipythonconsole.py:273 +#: spyder/widgets/projects/configdialog.py:81 +msgid "Interface" +msgstr "" + +#: spyder/plugins/configdialog.py:844 +msgid "Qt windows style" +msgstr "" + +#: spyder/plugins/configdialog.py:850 +msgid "Icon theme" +msgstr "" + +#: spyder/plugins/configdialog.py:854 +msgid "Vertical title bars in panes" +msgstr "" + +#: spyder/plugins/configdialog.py:856 +msgid "Vertical tabs in panes" +msgstr "" + +#: spyder/plugins/configdialog.py:858 +msgid "Animated toolbars and panes" +msgstr "" + +#: spyder/plugins/configdialog.py:860 +msgid "Tear off menus" +msgstr "" + +#: spyder/plugins/configdialog.py:861 +msgid "Set this to detach any
menu from the main window" +msgstr "" + +#: spyder/plugins/configdialog.py:863 +msgid "Enable high DPI scaling" +msgstr "" + +#: spyder/plugins/configdialog.py:865 +msgid "Set this for high DPI displays" +msgstr "" + +#: spyder/plugins/configdialog.py:866 +msgid "Custom margin for panes:" +msgstr "" + +#: spyder/plugins/configdialog.py:868 spyder/plugins/editor.py:209 +msgid "pixels" +msgstr "" + +#: spyder/plugins/configdialog.py:897 +msgid "Status bar" +msgstr "" + +#: spyder/plugins/configdialog.py:898 +msgid "Show status bar" +msgstr "" + +#: spyder/plugins/configdialog.py:900 +msgid "Show memory usage every" +msgstr "" + +#: spyder/plugins/configdialog.py:902 spyder/plugins/configdialog.py:911 +#: spyder/plugins/editor.py:133 spyder/plugins/editor.py:261 +#: spyder/plugins/variableexplorer.py:30 +msgid " ms" +msgstr "" + +#: spyder/plugins/configdialog.py:909 +msgid "Show CPU usage every" +msgstr "" + +#: spyder/plugins/configdialog.py:944 +msgid "Plain text font" +msgstr "" + +#: spyder/plugins/configdialog.py:950 +msgid "Rich text font" +msgstr "" + +#: spyder/plugins/configdialog.py:953 +msgid "Fonts" +msgstr "" + +#: spyder/plugins/configdialog.py:967 +msgid "Appearance" +msgstr "" + +#: spyder/plugins/configdialog.py:969 spyder/plugins/ipythonconsole.py:564 +msgid "Advanced Settings" +msgstr "" + +#: spyder/plugins/configdialog.py:1005 +msgid "Syntax coloring" +msgstr "" + +#: spyder/plugins/configdialog.py:1018 +msgid "Here you can select the color scheme used in the Editor and all other Spyder plugins.

You can also edit the color schemes provided by Spyder or create your own ones by using the options provided below.
" +msgstr "" + +#: spyder/plugins/configdialog.py:1023 +msgid "Edit selected" +msgstr "" + +#: spyder/plugins/configdialog.py:1024 +msgid "Create new scheme" +msgstr "" + +#: spyder/plugins/configdialog.py:1025 spyder/widgets/explorer.py:512 +#: spyder/widgets/projects/explorer.py:243 spyder/widgets/shell.py:134 +msgid "Delete" +msgstr "" + +#: spyder/plugins/configdialog.py:1028 +msgid "Reset" +msgstr "" + +#: spyder/plugins/configdialog.py:1035 +msgid "Scheme:" +msgstr "" + +#: spyder/plugins/configdialog.py:1066 +msgid "Manage color schemes" +msgstr "" + +#: spyder/plugins/configdialog.py:1255 +msgid "Are you sure you want to delete this scheme?" +msgstr "" + +#: spyder/plugins/configdialog.py:1372 +msgid "Text" +msgstr "" + +#: spyder/plugins/configdialog.py:1374 +msgid "Highlight" +msgstr "" + +#: spyder/plugins/configdialog.py:1376 +msgid "Background" +msgstr "" + +#: spyder/plugins/configdialog.py:1380 +msgid "Scheme name:" +msgstr "" + +#: spyder/plugins/configdialog.py:1387 +msgid "Color scheme editor" +msgstr "" + +#: spyder/plugins/console.py:109 +msgid "Internal console" +msgstr "" + +#: spyder/plugins/console.py:139 spyder/plugins/externalconsole.py:743 +msgid "&Run..." +msgstr "" + +#: spyder/plugins/console.py:141 spyder/plugins/externalconsole.py:744 +msgid "Run a Python script" +msgstr "" + +#: spyder/plugins/console.py:144 +msgid "Environment variables..." +msgstr "" + +#: spyder/plugins/console.py:146 +msgid "Show and edit environment variables (for current session)" +msgstr "" + +#: spyder/plugins/console.py:150 +msgid "Show sys.path contents..." +msgstr "" + +#: spyder/plugins/console.py:152 +msgid "Show (read-only) sys.path" +msgstr "" + +#: spyder/plugins/console.py:155 +msgid "Buffer..." +msgstr "" + +#: spyder/plugins/console.py:156 spyder/plugins/externalconsole.py:75 +#: spyder/plugins/history.py:43 +msgid "Set maximum line count" +msgstr "" + +#: spyder/plugins/console.py:159 +msgid "External editor path..." +msgstr "" + +#: spyder/plugins/console.py:160 +msgid "Set external editor executable path" +msgstr "" + +#: spyder/plugins/console.py:163 spyder/plugins/editor.py:139 +#: spyder/plugins/externalconsole.py:76 spyder/plugins/help.py:157 +#: spyder/plugins/help.py:360 spyder/plugins/history.py:46 +#: spyder/plugins/history.py:157 +msgid "Wrap lines" +msgstr "" + +#: spyder/plugins/console.py:166 spyder/plugins/editor.py:175 +#: spyder/plugins/externalconsole.py:121 spyder/plugins/ipythonconsole.py:283 +msgid "Display balloon tips" +msgstr "" + +#: spyder/plugins/console.py:170 spyder/plugins/editor.py:169 +#: spyder/plugins/externalconsole.py:115 +msgid "Automatic code completion" +msgstr "" + +#: spyder/plugins/console.py:174 spyder/plugins/editor.py:173 +#: spyder/plugins/externalconsole.py:119 +msgid "Enter key selects completion" +msgstr "" + +#: spyder/plugins/console.py:179 +msgid "Internal console settings" +msgstr "" + +#: spyder/plugins/console.py:232 spyder/plugins/externalconsole.py:918 +msgid "Run Python script" +msgstr "" + +#: spyder/plugins/console.py:233 spyder/plugins/externalconsole.py:146 +#: spyder/plugins/externalconsole.py:919 spyder/widgets/explorer.py:727 +msgid "Python scripts" +msgstr "" + +#: spyder/plugins/console.py:278 +msgid "Buffer" +msgstr "" + +#: spyder/plugins/console.py:279 +msgid "Maximum line count" +msgstr "" + +#: spyder/plugins/console.py:289 +msgid "External editor" +msgstr "" + +#: spyder/plugins/console.py:290 +msgid "External editor executable path:" +msgstr "" + +#: spyder/plugins/editor.py:104 +msgid "Edit template for new modules" +msgstr "" + +#: spyder/plugins/editor.py:109 +msgid "Sort files according to full path" +msgstr "" + +#: spyder/plugins/editor.py:111 +msgid "Show tab bar" +msgstr "" + +#: spyder/plugins/editor.py:118 spyder/plugins/editor.py:189 +#: spyder/plugins/externalconsole.py:71 spyder/plugins/externalconsole.py:114 +#: spyder/plugins/help.py:156 spyder/plugins/history.py:45 +#: spyder/plugins/ipythonconsole.py:317 +msgid "Source code" +msgstr "" + +#: spyder/plugins/editor.py:119 +msgid "Show line numbers" +msgstr "" + +#: spyder/plugins/editor.py:120 spyder/plugins/editor.py:947 +msgid "Show blank spaces" +msgstr "" + +#: spyder/plugins/editor.py:121 +msgid "Show vertical line after" +msgstr "" + +#: spyder/plugins/editor.py:122 +msgid "characters" +msgstr "" + +#: spyder/plugins/editor.py:127 +msgid "Highlight current line" +msgstr "" + +#: spyder/plugins/editor.py:129 +msgid "Highlight current cell" +msgstr "" + +#: spyder/plugins/editor.py:131 +msgid "Highlight occurrences after" +msgstr "" + +#: spyder/plugins/editor.py:159 +msgid "Save all files before running script" +msgstr "" + +#: spyder/plugins/editor.py:162 +msgid "Run selection" +msgstr "" + +#: spyder/plugins/editor.py:163 +msgid "Maintain focus in the Editor after running cells or selections" +msgstr "" + +#: spyder/plugins/editor.py:166 spyder/plugins/externalconsole.py:252 +msgid "Introspection" +msgstr "" + +#: spyder/plugins/editor.py:171 spyder/plugins/externalconsole.py:117 +msgid "Case sensitive code completion" +msgstr "" + +#: spyder/plugins/editor.py:176 +msgid "Link to object definition" +msgstr "" + +#: spyder/plugins/editor.py:178 +msgid "" +"If this option is enabled, clicking on an object\n" +"name (left-click + Ctrl key) will go this object\n" +"definition (if resolved)." +msgstr "" + +#: spyder/plugins/editor.py:182 +msgid "Warning:
The Python module rope is not installed on this computer: calltips, code completion and go-to-definition features won't be available." +msgstr "" + +#: spyder/plugins/editor.py:190 +msgid "Automatic insertion of parentheses, braces and brackets" +msgstr "" + +#: spyder/plugins/editor.py:193 +msgid "Automatic insertion of closing quotes" +msgstr "" + +#: spyder/plugins/editor.py:195 +msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" +msgstr "" + +#: spyder/plugins/editor.py:198 +msgid "Automatic indentation after 'else', 'elif', etc." +msgstr "" + +#: spyder/plugins/editor.py:200 +msgid "Indentation characters: " +msgstr "" + +#: spyder/plugins/editor.py:201 +msgid "2 spaces" +msgstr "" + +#: spyder/plugins/editor.py:202 +msgid "3 spaces" +msgstr "" + +#: spyder/plugins/editor.py:203 +msgid "4 spaces" +msgstr "" + +#: spyder/plugins/editor.py:204 +msgid "5 spaces" +msgstr "" + +#: spyder/plugins/editor.py:205 +msgid "6 spaces" +msgstr "" + +#: spyder/plugins/editor.py:206 +msgid "7 spaces" +msgstr "" + +#: spyder/plugins/editor.py:207 +msgid "8 spaces" +msgstr "" + +#: spyder/plugins/editor.py:208 +msgid "Tabulations" +msgstr "" + +#: spyder/plugins/editor.py:209 +msgid "Tab stop width:" +msgstr "" + +#: spyder/plugins/editor.py:211 +msgid "Tab always indent" +msgstr "" + +#: spyder/plugins/editor.py:213 +msgid "" +"If enabled, pressing Tab will always indent,\n" +"even when the cursor is not at the beginning\n" +"of a line (when this option is enabled, code\n" +"completion may be triggered using the alternate\n" +"shortcut: Ctrl+Space)" +msgstr "" + +#: spyder/plugins/editor.py:218 +msgid "Intelligent backspace" +msgstr "" + +#: spyder/plugins/editor.py:220 +msgid "Automatically remove trailing spaces when saving files" +msgstr "" + +#: spyder/plugins/editor.py:224 +msgid "Analysis" +msgstr "" + +#: spyder/plugins/editor.py:226 +msgid "(Refer to the {} page)" +msgstr "" + +#: spyder/plugins/editor.py:230 +msgid "Real-time code analysis" +msgstr "" + +#: spyder/plugins/editor.py:232 +msgid "

If enabled, Python source code will be analyzed using pyflakes, lines containing errors or warnings will be highlighted.

Note: add analysis:ignore in a comment to ignore code analysis warnings.

" +msgstr "" + +#: spyder/plugins/editor.py:240 +msgid "Code analysis requires pyflakes %s+" +msgstr "" + +#: spyder/plugins/editor.py:242 +msgid "Real-time code style analysis" +msgstr "" + +#: spyder/plugins/editor.py:244 +msgid "

If enabled, Python source code will be analyzedusing pep8, lines that are not following PEP8 style guide will be highlighted.

Note: add analysis:ignore in a comment to ignore style analysis warnings.

" +msgstr "" + +#: spyder/plugins/editor.py:251 +msgid "Code annotations (TODO, FIXME, XXX, HINT, TIP, @todo)" +msgstr "" + +#: spyder/plugins/editor.py:254 +msgid "Perform analysis when saving file and every" +msgstr "" + +#: spyder/plugins/editor.py:258 +msgid "Perform analysis only when saving file" +msgstr "" + +#: spyder/plugins/editor.py:317 +msgid "End-of-line characters" +msgstr "" + +#: spyder/plugins/editor.py:318 +msgid "When opening a text file containing mixed end-of-line characters (this may raise syntax errors in the consoles on Windows platforms), Spyder may fix the file automatically." +msgstr "" + +#: spyder/plugins/editor.py:324 +msgid "Fix automatically and show warning message box" +msgstr "" + +#: spyder/plugins/editor.py:335 spyder/plugins/externalconsole.py:250 +#: spyder/plugins/ipythonconsole.py:558 spyder/plugins/variableexplorer.py:43 +msgid "Display" +msgstr "" + +#: spyder/plugins/editor.py:337 +msgid "Code Introspection/Analysis" +msgstr "" + +#: spyder/plugins/editor.py:340 spyder/plugins/externalconsole.py:253 +msgid "Advanced settings" +msgstr "" + +#: spyder/plugins/editor.py:613 spyder/widgets/editortools.py:510 +msgid "Show/hide outline explorer" +msgstr "" + +#: spyder/plugins/editor.py:619 +msgid "Show/hide project explorer" +msgstr "" + +#: spyder/plugins/editor.py:627 +msgid "&New file..." +msgstr "" + +#: spyder/plugins/editor.py:628 spyder/plugins/workingdirectory.py:83 +#: spyder/widgets/explorer.py:704 spyder/widgets/explorer.py:711 +msgid "New file" +msgstr "" + +#: spyder/plugins/editor.py:634 +msgid "&Open..." +msgstr "" + +#: spyder/plugins/editor.py:635 spyder/plugins/editor.py:1778 +#: spyder/plugins/workingdirectory.py:70 +msgid "Open file" +msgstr "" + +#: spyder/plugins/editor.py:641 spyder/widgets/editor.py:347 +msgid "File switcher..." +msgstr "" + +#: spyder/plugins/editor.py:643 +msgid "Fast switch between files" +msgstr "" + +#: spyder/plugins/editor.py:649 +msgid "&Revert" +msgstr "" + +#: spyder/plugins/editor.py:650 +msgid "Revert file from disk" +msgstr "" + +#: spyder/plugins/editor.py:653 +msgid "&Save" +msgstr "" + +#: spyder/plugins/editor.py:654 spyder/widgets/editor.py:1306 +msgid "Save file" +msgstr "" + +#: spyder/plugins/editor.py:660 +msgid "Sav&e all" +msgstr "" + +#: spyder/plugins/editor.py:661 +msgid "Save all files" +msgstr "" + +#: spyder/plugins/editor.py:667 +msgid "Save &as..." +msgstr "" + +#: spyder/plugins/editor.py:668 +msgid "Save current file as..." +msgstr "" + +#: spyder/plugins/editor.py:673 spyder/plugins/editor.py:674 +msgid "Print preview..." +msgstr "" + +#: spyder/plugins/editor.py:675 +msgid "&Print..." +msgstr "" + +#: spyder/plugins/editor.py:676 +msgid "Print current file..." +msgstr "" + +#: spyder/plugins/editor.py:679 +msgid "&Close" +msgstr "" + +#: spyder/plugins/editor.py:680 +msgid "Close current file" +msgstr "" + +#: spyder/plugins/editor.py:683 +msgid "C&lose all" +msgstr "" + +#: spyder/plugins/editor.py:684 +msgid "Close all opened files" +msgstr "" + +#: spyder/plugins/editor.py:691 +msgid "&Find text" +msgstr "" + +#: spyder/plugins/editor.py:697 +msgid "Find &next" +msgstr "" + +#: spyder/plugins/editor.py:703 +msgid "Find &previous" +msgstr "" + +#: spyder/plugins/editor.py:709 +msgid "&Replace text" +msgstr "" + +#: spyder/plugins/editor.py:718 +msgid "Set/Clear breakpoint" +msgstr "" + +#: spyder/plugins/editor.py:725 +msgid "Set/Edit conditional breakpoint" +msgstr "" + +#: spyder/plugins/editor.py:732 +msgid "Clear breakpoints in all files" +msgstr "" + +#: spyder/plugins/editor.py:734 +msgid "Debug with winpdb" +msgstr "" + +#: spyder/plugins/editor.py:741 +msgid "Debug file" +msgstr "" + +#: spyder/plugins/editor.py:746 +msgid "Step" +msgstr "" + +#: spyder/plugins/editor.py:747 +msgid "Run current line" +msgstr "" + +#: spyder/plugins/editor.py:752 +msgid "Continue" +msgstr "" + +#: spyder/plugins/editor.py:754 +msgid "Continue execution until next breakpoint" +msgstr "" + +#: spyder/plugins/editor.py:759 +msgid "Step Into" +msgstr "" + +#: spyder/plugins/editor.py:761 +msgid "Step into function or method of current line" +msgstr "" + +#: spyder/plugins/editor.py:766 +msgid "Step Return" +msgstr "" + +#: spyder/plugins/editor.py:768 +msgid "Run until current function or method returns" +msgstr "" + +#: spyder/plugins/editor.py:773 spyder/widgets/findinfiles.py:333 +#: spyder/widgets/ipythonconsole/client.py:238 +msgid "Stop" +msgstr "" + +#: spyder/plugins/editor.py:774 +msgid "Stop debugging" +msgstr "" + +#: spyder/plugins/editor.py:781 +msgid "Run file" +msgstr "" + +#: spyder/plugins/editor.py:786 +msgid "&Configure..." +msgstr "" + +#: spyder/plugins/editor.py:788 +#: spyder/widgets/externalshell/pythonshell.py:304 +msgid "Run settings" +msgstr "" + +#: spyder/plugins/editor.py:794 +msgid "Re-run &last script" +msgstr "" + +#: spyder/plugins/editor.py:796 +msgid "Run again last file" +msgstr "" + +#: spyder/plugins/editor.py:802 spyder/widgets/sourcecode/codeeditor.py:2548 +msgid "Run &selection or current line" +msgstr "" + +#: spyder/plugins/editor.py:805 +msgid "Run selection or current line" +msgstr "" + +#: spyder/plugins/editor.py:813 spyder/widgets/sourcecode/codeeditor.py:2540 +msgid "Run cell" +msgstr "" + +#: spyder/plugins/editor.py:816 +msgid "" +"Run current cell (Ctrl+Enter)\n" +"[Use #%% to create cells]" +msgstr "" + +#: spyder/plugins/editor.py:822 spyder/widgets/sourcecode/codeeditor.py:2544 +msgid "Run cell and advance" +msgstr "" + +#: spyder/plugins/editor.py:825 +msgid "Run current cell and go to the next one (Shift+Enter)" +msgstr "" + +#: spyder/plugins/editor.py:832 +msgid "Show todo list" +msgstr "" + +#: spyder/plugins/editor.py:833 +msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" +msgstr "" + +#: spyder/plugins/editor.py:840 +msgid "Show warning/error list" +msgstr "" + +#: spyder/plugins/editor.py:841 +msgid "Show code analysis warnings/errors" +msgstr "" + +#: spyder/plugins/editor.py:847 +msgid "Previous warning/error" +msgstr "" + +#: spyder/plugins/editor.py:848 +msgid "Go to previous code analysis warning/error" +msgstr "" + +#: spyder/plugins/editor.py:851 +msgid "Next warning/error" +msgstr "" + +#: spyder/plugins/editor.py:852 +msgid "Go to next code analysis warning/error" +msgstr "" + +#: spyder/plugins/editor.py:856 +msgid "Last edit location" +msgstr "" + +#: spyder/plugins/editor.py:857 +msgid "Go to last edit location" +msgstr "" + +#: spyder/plugins/editor.py:865 +msgid "Previous cursor position" +msgstr "" + +#: spyder/plugins/editor.py:866 +msgid "Go to previous cursor position" +msgstr "" + +#: spyder/plugins/editor.py:874 +msgid "Next cursor position" +msgstr "" + +#: spyder/plugins/editor.py:875 +msgid "Go to next cursor position" +msgstr "" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Comment" +msgstr "" + +#: spyder/plugins/editor.py:885 spyder/widgets/sourcecode/codeeditor.py:2524 +msgid "Uncomment" +msgstr "" + +#: spyder/plugins/editor.py:886 +msgid "Comment current line or selection" +msgstr "" + +#: spyder/plugins/editor.py:890 +msgid "Add &block comment" +msgstr "" + +#: spyder/plugins/editor.py:891 +msgid "Add block comment around current line or selection" +msgstr "" + +#: spyder/plugins/editor.py:897 +msgid "R&emove block comment" +msgstr "" + +#: spyder/plugins/editor.py:898 +msgid "Remove comment block around current line or selection" +msgstr "" + +#: spyder/plugins/editor.py:909 +msgid "Indent" +msgstr "" + +#: spyder/plugins/editor.py:910 +msgid "Indent current line or selection" +msgstr "" + +#: spyder/plugins/editor.py:913 +msgid "Unindent" +msgstr "" + +#: spyder/plugins/editor.py:914 +msgid "Unindent current line or selection" +msgstr "" + +#: spyder/plugins/editor.py:918 +msgid "Toggle Uppercase" +msgstr "" + +#: spyder/plugins/editor.py:919 +msgid "Change to uppercase current line or selection" +msgstr "" + +#: spyder/plugins/editor.py:923 +msgid "Toggle Lowercase" +msgstr "" + +#: spyder/plugins/editor.py:924 +msgid "Change to lowercase current line or selection" +msgstr "" + +#: spyder/plugins/editor.py:929 +msgid "Carriage return and line feed (Windows)" +msgstr "" + +#: spyder/plugins/editor.py:932 +msgid "Line feed (UNIX)" +msgstr "" + +#: spyder/plugins/editor.py:935 +msgid "Carriage return (Mac)" +msgstr "" + +#: spyder/plugins/editor.py:941 +msgid "Convert end-of-line characters" +msgstr "" + +#: spyder/plugins/editor.py:945 +msgid "Remove trailing spaces" +msgstr "" + +#: spyder/plugins/editor.py:949 +msgid "Fix indentation" +msgstr "" + +#: spyder/plugins/editor.py:950 +msgid "Replace tab characters by space characters" +msgstr "" + +#: spyder/plugins/editor.py:953 +msgid "Go to line..." +msgstr "" + +#: spyder/plugins/editor.py:961 +msgid "Set console working directory" +msgstr "" + +#: spyder/plugins/editor.py:963 +msgid "Set current console (and file explorer) working directory to current script directory" +msgstr "" + +#: spyder/plugins/editor.py:968 +msgid "Maximum number of recent files..." +msgstr "" + +#: spyder/plugins/editor.py:971 +msgid "Clear recent files list" +msgstr "" + +#: spyder/plugins/editor.py:971 spyder/plugins/projects.py:100 +msgid "Clear this list" +msgstr "" + +#: spyder/plugins/editor.py:975 +msgid "Open &recent" +msgstr "" + +#: spyder/plugins/editor.py:1359 +msgid "?" +msgstr "" + +#: spyder/plugins/editor.py:1586 +msgid "Spyder Editor" +msgstr "" + +#: spyder/plugins/editor.py:1587 +msgid "This is a temporary script file." +msgstr "" + +#: spyder/plugins/editor.py:1656 +msgid "untitled" +msgstr "" + +#: spyder/plugins/editor.py:1730 +msgid "Maximum number of recent files" +msgstr "" + +#: spyder/plugins/editor.py:1863 +msgid "Printing..." +msgstr "" + +#: spyder/plugins/explorer.py:53 +msgid "File explorer" +msgstr "" + +#: spyder/plugins/externalconsole.py:46 +msgid "Interactive data plotting in the consoles" +msgstr "" + +#: spyder/plugins/externalconsole.py:54 spyder/plugins/externalconsole.py:709 +msgid "Python console" +msgstr "" + +#: spyder/plugins/externalconsole.py:59 +msgid "One tab per script" +msgstr "" + +#: spyder/plugins/externalconsole.py:60 +#: spyder/widgets/externalshell/baseshell.py:160 +msgid "Show elapsed time" +msgstr "" + +#: spyder/plugins/externalconsole.py:61 spyder/widgets/explorer.py:1094 +msgid "Show icons and text" +msgstr "" + +#: spyder/plugins/externalconsole.py:73 +msgid "Buffer: " +msgstr "" + +#: spyder/plugins/externalconsole.py:73 spyder/plugins/ipythonconsole.py:319 +msgid " lines" +msgstr "" + +#: spyder/plugins/externalconsole.py:78 +msgid "Merge process standard output/error channels" +msgstr "" + +#: spyder/plugins/externalconsole.py:80 +msgid "" +"Merging the output channels of the process means that\n" +"the standard error won't be written in red anymore,\n" +"but this has the effect of speeding up display." +msgstr "" + +#: spyder/plugins/externalconsole.py:84 +msgid "Colorize standard error channel using ANSI escape codes" +msgstr "" + +#: spyder/plugins/externalconsole.py:86 +msgid "" +"This method is the only way to have colorized standard\n" +"error channel when the output channels have been merged." +msgstr "" + +#: spyder/plugins/externalconsole.py:102 spyder/plugins/ipythonconsole.py:306 +#: spyder/widgets/variableexplorer/arrayeditor.py:540 +#: spyder/widgets/variableexplorer/dataframeeditor.py:552 +msgid "Background color" +msgstr "" + +#: spyder/plugins/externalconsole.py:103 +msgid "This option will be applied the next time a Python console or a terminal is opened." +msgstr "" + +#: spyder/plugins/externalconsole.py:106 +msgid "Light background (white color)" +msgstr "" + +#: spyder/plugins/externalconsole.py:131 +msgid "PYTHONSTARTUP replacement" +msgstr "" + +#: spyder/plugins/externalconsole.py:133 +msgid "" +"This option will override the PYTHONSTARTUP environment variable which\n" +"defines the script to be executed during the Python console startup." +msgstr "" + +#: spyder/plugins/externalconsole.py:138 +msgid "Default PYTHONSTARTUP script" +msgstr "" + +#: spyder/plugins/externalconsole.py:142 +msgid "Use the following startup script:" +msgstr "" + +#: spyder/plugins/externalconsole.py:159 +msgid "Monitor" +msgstr "" + +#: spyder/plugins/externalconsole.py:160 +msgid "The monitor provides introspection features to console: code completion, calltips and variable explorer. Because it relies on several modules, disabling the monitor may be useful to accelerate console startup." +msgstr "" + +#: spyder/plugins/externalconsole.py:167 +msgid "Enable monitor" +msgstr "" + +#: spyder/plugins/externalconsole.py:180 +msgid "Default library" +msgstr "" + +#: spyder/plugins/externalconsole.py:185 +msgid "Qt-Python Bindings" +msgstr "" + +#: spyder/plugins/externalconsole.py:187 +msgid "Library:" +msgstr "" + +#: spyder/plugins/externalconsole.py:189 +msgid "This option will act on
libraries such as Matplotlib, guidata or ETS" +msgstr "" + +#: spyder/plugins/externalconsole.py:198 spyder/plugins/ipythonconsole.py:560 +msgid "Graphics" +msgstr "" + +#: spyder/plugins/externalconsole.py:199 +msgid "Decide which backend to use to display graphics. If unsure, please select the Automatic backend.

Note: We support a very limited number of backends in our Python consoles. If you prefer to work with a different one, please use an IPython console." +msgstr "" + +#: spyder/plugins/externalconsole.py:208 +msgid "None" +msgstr "" + +#: spyder/plugins/externalconsole.py:208 spyder/plugins/ipythonconsole.py:351 +msgid "Automatic" +msgstr "" + +#: spyder/plugins/externalconsole.py:213 spyder/plugins/ipythonconsole.py:373 +msgid "Backend:" +msgstr "" + +#: spyder/plugins/externalconsole.py:215 spyder/plugins/ipythonconsole.py:375 +msgid "This option will be applied the next time a console is opened." +msgstr "" + +#: spyder/plugins/externalconsole.py:226 +msgid "Enthought Tool Suite" +msgstr "" + +#: spyder/plugins/externalconsole.py:227 +msgid "Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical user interfaces." +msgstr "" + +#: spyder/plugins/externalconsole.py:231 +msgid "ETS_TOOLKIT:" +msgstr "" + +#: spyder/plugins/externalconsole.py:255 +msgid "External modules" +msgstr "" + +#: spyder/plugins/externalconsole.py:447 +msgid "No Python console is currently selected to run %s.

Please select or open a new Python console and try again." +msgstr "" + +#: spyder/plugins/externalconsole.py:518 +msgid "" +"%s is already running in a separate process.\n" +"Do you want to kill the process before starting a new one?" +msgstr "" + +#: spyder/plugins/externalconsole.py:647 +msgid "Command Window" +msgstr "" + +#: spyder/plugins/externalconsole.py:649 spyder/plugins/ipythonconsole.py:297 +msgid "Terminal" +msgstr "" + +#: spyder/plugins/externalconsole.py:731 +msgid "Open a &Python console" +msgstr "" + +#: spyder/plugins/externalconsole.py:735 +msgid "Open &command prompt" +msgstr "" + +#: spyder/plugins/externalconsole.py:736 +msgid "Open a Windows command prompt" +msgstr "" + +#: spyder/plugins/externalconsole.py:738 +msgid "Open a &terminal" +msgstr "" + +#: spyder/plugins/externalconsole.py:739 +msgid "Open a terminal window" +msgstr "" + +#: spyder/plugins/findinfiles.py:127 spyder/widgets/findinfiles.py:689 +msgid "Find in files" +msgstr "" + +#: spyder/plugins/findinfiles.py:148 +msgid "&Find in files" +msgstr "" + +#: spyder/plugins/findinfiles.py:151 +msgid "Search text in multiple files" +msgstr "" + +#: spyder/plugins/help.py:44 +msgid "Show help for objects in the Editor and Consoles in a dedicated pane" +msgstr "" + +#: spyder/plugins/help.py:110 +msgid "Automatic connections" +msgstr "" + +#: spyder/plugins/help.py:111 +msgid "This pane can automatically show an object's help information after a left parenthesis is written next to it. Below you can decide to which plugin you want to connect it to turn on this feature." +msgstr "" + +#: spyder/plugins/help.py:123 +msgid "" +"This feature requires the Rope or Jedi libraries.\n" +"It seems you don't have either installed." +msgstr "" + +#: spyder/plugins/help.py:126 +msgid "Python Console" +msgstr "" + +#: spyder/plugins/help.py:128 +msgid "IPython Console" +msgstr "" + +#: spyder/plugins/help.py:140 +msgid "Additional features" +msgstr "" + +#: spyder/plugins/help.py:141 +msgid "Render mathematical equations" +msgstr "" + +#: spyder/plugins/help.py:147 +msgid "This feature requires Sphinx 1.1 or superior." +msgstr "" + +#: spyder/plugins/help.py:148 +msgid "Sphinx %s is currently installed." +msgstr "" + +#: spyder/plugins/help.py:309 +msgid "No further documentation available" +msgstr "" + +#: spyder/plugins/help.py:347 +msgid "No documentation available" +msgstr "" + +#: spyder/plugins/help.py:378 +msgid "Source" +msgstr "" + +#: spyder/plugins/help.py:385 spyder/plugins/runconfig.py:186 +#: spyder/plugins/runconfig.py:456 +#: spyder/widgets/externalshell/baseshell.py:94 +#: spyder/widgets/ipythonconsole/client.py:202 +msgid "Console" +msgstr "" + +#: spyder/plugins/help.py:393 +#: spyder/widgets/variableexplorer/collectionseditor.py:133 +msgid "Object" +msgstr "" + +#: spyder/plugins/help.py:407 +msgid "Plain Text" +msgstr "" + +#: spyder/plugins/help.py:411 +msgid "Show Source" +msgstr "" + +#: spyder/plugins/help.py:415 +msgid "Rich Text" +msgstr "" + +#: spyder/plugins/help.py:425 +msgid "Automatic import" +msgstr "" + +#: spyder/plugins/help.py:437 spyder/plugins/history.py:106 +#: spyder/widgets/editor.py:504 spyder/widgets/explorer.py:1105 +#: spyder/widgets/externalshell/baseshell.py:140 +#: spyder/widgets/ipythonconsole/client.py:251 +#: spyder/widgets/variableexplorer/namespacebrowser.py:160 +msgid "Options" +msgstr "" + +#: spyder/plugins/help.py:695 +msgid "Here you can get help of any object by pressing %s in front of it, either on the Editor or the Console.%sHelp can also be shown automatically after writing a left parenthesis next to an object. You can activate this behavior in %s." +msgstr "" + +#: spyder/plugins/help.py:701 +msgid "Preferences > Help" +msgstr "" + +#: spyder/plugins/help.py:708 +msgid "Usage" +msgstr "" + +#: spyder/plugins/help.py:709 +msgid "New to Spyder? Read our" +msgstr "" + +#: spyder/plugins/help.py:710 +msgid "tutorial" +msgstr "" + +#: spyder/plugins/help.py:717 +msgid "Please consider installing Sphinx to get documentation rendered in rich text." +msgstr "" + +#: spyder/plugins/help.py:886 +msgid "Lock" +msgstr "" + +#: spyder/plugins/help.py:886 +msgid "Unlock" +msgstr "" + +#: spyder/plugins/help.py:930 +msgid "The following error occured when calling Sphinx %s.
Incompatible Sphinx version or doc string decoding failed.

Error message:
%s" +msgstr "" + +#: spyder/plugins/help.py:974 +msgid "No source code available." +msgstr "" + +#: spyder/plugins/history.py:39 +msgid "Settings" +msgstr "" + +#: spyder/plugins/history.py:41 +msgid " entries" +msgstr "" + +#: spyder/plugins/history.py:41 +msgid "History depth: " +msgstr "" + +#: spyder/plugins/history.py:48 +msgid "Scroll automatically to last entry" +msgstr "" + +#: spyder/plugins/history.py:126 +msgid "History log" +msgstr "" + +#: spyder/plugins/history.py:153 +msgid "History..." +msgstr "" + +#: spyder/plugins/history.py:155 +msgid "Set history maximum entries" +msgstr "" + +#: spyder/plugins/history.py:260 +msgid "History" +msgstr "" + +#: spyder/plugins/history.py:261 +msgid "Maximum entries" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:64 +msgid "Symbolic mathematics in the IPython Console" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:116 +msgid "The authenticity of host %s can't be established. Are you sure you want to continue connecting?" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:128 +msgid "The authenticity of the host can't be established" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:135 +msgid "Tunnel '%s' failed to start" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:140 +msgid "Could not connect to remote host" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:157 spyder/plugins/ipythonconsole.py:747 +msgid "Connect to an existing kernel" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:159 +msgid "Please enter the connection info of the kernel you want to connect to. For that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for example kernel-3764.json or just 3764)." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:170 +msgid "Connection info:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:172 +msgid "Path to connection file or kernel id" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:174 spyder/plugins/ipythonconsole.py:191 +msgid "Browse" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:183 +msgid "This is a remote kernel" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:187 +msgid "username@hostname:port" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:190 +msgid "Path to ssh key file" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:199 +msgid "Password or ssh key passphrase" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:203 +msgid "Host name" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:204 +msgid "Ssh key" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:205 +msgid "Password" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:234 +msgid "Open connection file" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:239 +msgid "Select ssh key" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:267 spyder/plugins/ipythonconsole.py:686 +msgid "IPython console" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:274 +msgid "Display initial banner" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:275 +msgid "" +"This option lets you hide the message shown at\n" +"the top of the console when it's opened." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:277 +msgid "Use a pager to display additional text inside the console" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:279 +msgid "" +"Useful if you don't want to fill the console with long help or completion texts.\n" +"Note: Use the Q key to get out of the pager." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:284 +msgid "Ask for confirmation before closing" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:294 +msgid "Completion Type" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:295 +msgid "Decide what type of completion to use" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Graphical" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:297 +msgid "Plain" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:298 +msgid "Completion:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:307 +msgid "Light background" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:309 +msgid "Dark background" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:319 +msgid "Buffer: " +msgstr "" + +#: spyder/plugins/ipythonconsole.py:321 +msgid "" +"Set the maximum number of lines of text shown in the\n" +"console before truncation. Specifying -1 disables it\n" +"(not recommended!)" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:330 +msgid "Support for graphics (Matplotlib)" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:331 +msgid "Activate support" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:332 +msgid "Automatically load Pylab and NumPy modules" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:335 +msgid "" +"This lets you load graphics support without importing \n" +"the commands to do plots. Useful to work with other\n" +"plotting libraries different to Matplotlib or to develop \n" +"GUIs with Spyder." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:350 +msgid "Inline" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:352 +msgid "Graphics backend" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:353 +msgid "Decide how graphics are going to be displayed in the console. If unsure, please select %s to put graphics inside the console or %s to interact with them (through zooming and panning) in a separate window." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:386 +msgid "Inline backend" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:387 +msgid "Decide how to render the figures created by this backend" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:391 +msgid "Format:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "Resolution:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:394 +msgid "dpi" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:396 +msgid "Only used when the format is PNG. Default is 72" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:399 +msgid "Width:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:399 spyder/plugins/ipythonconsole.py:403 +msgid "inches" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:401 +msgid "Default is 6" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:403 +msgid "Height:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:405 +msgid "Default is 4" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:432 +msgid "You can run several lines of code when a console is started. Please introduce each one separated by commas, for example:
import os, import sys" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:438 +msgid "Lines:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:447 +msgid "Run a file" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:448 +msgid "You can also run a whole file at startup instead of just some lines (This is similar to have a PYTHONSTARTUP file)." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:452 +msgid "Use the following file:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:466 +msgid "Greedy completion" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:467 +msgid "Enable Tab completion on elements of lists, results of function calls, etc, without assigning them to a variable.
For example, you can get completions on things like li[0].<Tab> or ins.meth().<Tab>" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:475 +msgid "Use the greedy completer" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:486 +msgid "Autocall" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:487 +msgid "Autocall makes IPython automatically call any callable object even if you didn't type explicit parentheses.
For example, if you type str 43 it becomes str(43) automatically." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:494 +msgid "Smart" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:495 +msgid "Full" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:496 +msgid "Off" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:498 +msgid "Autocall: " +msgstr "" + +#: spyder/plugins/ipythonconsole.py:499 +msgid "On %s mode, Autocall is not applied if there are no arguments after the callable. On %s mode, all callable objects are automatically called (even if no arguments are present)." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:511 +msgid "Symbolic Mathematics" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:512 +msgid "Perfom symbolic operations in the console (e.g. integrals, derivatives, vector calculus, etc) and get the outputs in a beautifully printed style (it requires the Sympy module)." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:517 +msgid "Use symbolic math" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:518 +msgid "This option loads the Sympy library to work with.
Please refer to its documentation to learn how to use it." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:528 +msgid "Prompts" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:529 +msgid "Modify how Input and Output prompts are shown in the console." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:532 +msgid "Input prompt:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:534 +msgid "Default is
In [<span class=\"in-prompt-number\">%i</span>]:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:538 +msgid "Output prompt:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:540 +msgid "Default is
Out[<span class=\"out-prompt-number\">%i</span>]:" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:562 spyder/plugins/workingdirectory.py:45 +msgid "Startup" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:734 +msgid "Open an &IPython console" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:737 +msgid "Use %s+T when the console is selected to open a new one" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:740 +msgid "Open a new console" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:748 +msgid "Open a new IPython console connected to an existing kernel" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:836 +msgid "No IPython console is currently available to run %s.

Please open a new one and try again." +msgstr "" + +#: spyder/plugins/ipythonconsole.py:880 +msgid "Your Python environment or installation doesn't have the ipykernel module installed on it. Without this module is not possible for Spyder to create a console for you.

You can install ipykernel by running in a terminal:

pip install ipykernel

or

conda install ipykernel" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:1105 +msgid "Do you want to close this console?" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:1111 +msgid "Do you want to close all other consoles connected to the same kernel as this one?" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:1382 +msgid "IPython" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:1383 +msgid "Unable to connect to %s" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:1443 +msgid "Connection error" +msgstr "" + +#: spyder/plugins/ipythonconsole.py:1444 +msgid "" +"Could not open ssh tunnel. The error was:\n" +"\n" +msgstr "" + +#: spyder/plugins/layoutdialog.py:177 +msgid "Move Up" +msgstr "" + +#: spyder/plugins/layoutdialog.py:178 +msgid "Move Down" +msgstr "" + +#: spyder/plugins/layoutdialog.py:179 +msgid "Delete Layout" +msgstr "" + +#: spyder/plugins/layoutdialog.py:183 +msgid "Layout Display and Order" +msgstr "" + +#: spyder/plugins/maininterpreter.py:31 spyder/plugins/maininterpreter.py:66 +msgid "Python interpreter" +msgstr "" + +#: spyder/plugins/maininterpreter.py:68 +msgid "Select the Python interpreter for all Spyder consoles" +msgstr "" + +#: spyder/plugins/maininterpreter.py:71 +msgid "Default (i.e. the same as Spyder's)" +msgstr "" + +#: spyder/plugins/maininterpreter.py:74 +msgid "Use the following Python interpreter:" +msgstr "" + +#: spyder/plugins/maininterpreter.py:77 +msgid "Executables" +msgstr "" + +#: spyder/plugins/maininterpreter.py:94 +msgid "User Module Reloader (UMR)" +msgstr "" + +#: spyder/plugins/maininterpreter.py:95 +msgid "UMR forces Python to reload modules which were imported when executing a file in a Python or IPython console with the runfile function." +msgstr "" + +#: spyder/plugins/maininterpreter.py:100 +msgid "Enable UMR" +msgstr "" + +#: spyder/plugins/maininterpreter.py:101 +msgid "This option will enable the User Module Reloader (UMR) in Python/IPython consoles. UMR forces Python to reload deeply modules during import when running a Python script using the Spyder's builtin function runfile.

1. UMR may require to restart the console in which it will be called (otherwise only newly imported modules will be reloaded when executing files).

2. If errors occur when re-running a PyQt-based program, please check that the Qt objects are properly destroyed (e.g. you may have to use the attribute Qt.WA_DeleteOnClose on your main window, using the setAttribute method)" +msgstr "" + +#: spyder/plugins/maininterpreter.py:117 +msgid "Show reloaded modules list" +msgstr "" + +#: spyder/plugins/maininterpreter.py:118 +msgid "Please note that these changes will be applied only to new consoles" +msgstr "" + +#: spyder/plugins/maininterpreter.py:122 +msgid "Set UMR excluded (not reloaded) modules" +msgstr "" + +#: spyder/plugins/maininterpreter.py:168 +msgid "You selected a Python %d interpreter for the console but Spyder is running on Python %d!.

Although this is possible, we recommend you to install and run Spyder directly with your selected interpreter, to avoid seeing false warnings and errors due to the incompatible syntax between these two Python versions." +msgstr "" + +#: spyder/plugins/maininterpreter.py:178 spyder/plugins/maininterpreter.py:205 +#: spyder/plugins/maininterpreter.py:209 +msgid "UMR" +msgstr "" + +#: spyder/plugins/maininterpreter.py:179 +msgid "Set the list of excluded modules as this: numpy, scipy" +msgstr "" + +#: spyder/plugins/maininterpreter.py:196 +msgid "You are working with Python 2, this means that you can not import a module that contains non-ascii characters." +msgstr "" + +#: spyder/plugins/maininterpreter.py:206 +msgid "" +"The following modules are not installed on your machine:\n" +"%s" +msgstr "" + +#: spyder/plugins/maininterpreter.py:210 +msgid "Please note that these changes will be applied only to new Python/IPython consoles" +msgstr "" + +#: spyder/plugins/onlinehelp.py:70 +msgid "Online help" +msgstr "" + +#: spyder/plugins/outlineexplorer.py:49 spyder/widgets/editortools.py:195 +msgid "Outline" +msgstr "" + +#: spyder/plugins/projects.py:76 spyder/widgets/projects/explorer.py:113 +#: spyder/widgets/projects/explorer.py:127 +msgid "Project explorer" +msgstr "" + +#: spyder/plugins/projects.py:88 +msgid "New Project..." +msgstr "" + +#: spyder/plugins/projects.py:91 +msgid "Open Project..." +msgstr "" + +#: spyder/plugins/projects.py:94 +msgid "Close Project" +msgstr "" + +#: spyder/plugins/projects.py:97 +msgid "Delete Project" +msgstr "" + +#: spyder/plugins/projects.py:103 +msgid "Project Preferences" +msgstr "" + +#: spyder/plugins/projects.py:105 +msgid "Recent Projects" +msgstr "" + +#: spyder/plugins/projects.py:240 +msgid "Open project" +msgstr "" + +#: spyder/plugins/projects.py:245 +msgid "%s is not a Spyder project!" +msgstr "" + +#: spyder/plugins/runconfig.py:29 +msgid "Execute in current Python or IPython console" +msgstr "" + +#: spyder/plugins/runconfig.py:30 +msgid "Execute in a new dedicated Python console" +msgstr "" + +#: spyder/plugins/runconfig.py:31 +msgid "Execute in an external System terminal" +msgstr "" + +#: spyder/plugins/runconfig.py:41 +msgid "Always show %s on a first file run" +msgstr "" + +#: spyder/plugins/runconfig.py:160 spyder/plugins/runconfig.py:474 +msgid "General settings" +msgstr "" + +#: spyder/plugins/runconfig.py:163 spyder/plugins/runconfig.py:209 +msgid "Command line options:" +msgstr "" + +#: spyder/plugins/runconfig.py:169 +msgid "Working directory:" +msgstr "" + +#: spyder/plugins/runconfig.py:181 spyder/plugins/runconfig.py:492 +msgid "Enter debugging mode when errors appear during execution" +msgstr "" + +#: spyder/plugins/runconfig.py:197 spyder/plugins/runconfig.py:502 +msgid "Dedicated Python console" +msgstr "" + +#: spyder/plugins/runconfig.py:201 spyder/plugins/runconfig.py:504 +msgid "Interact with the Python console after execution" +msgstr "" + +#: spyder/plugins/runconfig.py:205 +msgid "Show warning when killing running process" +msgstr "" + +#: spyder/plugins/runconfig.py:214 +msgid "-u is added to the other options you set here" +msgstr "" + +#: spyder/plugins/runconfig.py:224 +msgid "this dialog" +msgstr "" + +#: spyder/plugins/runconfig.py:283 +msgid "Run configuration" +msgstr "" + +#: spyder/plugins/runconfig.py:284 +msgid "The following working directory is not valid:
%s" +msgstr "" + +#: spyder/plugins/runconfig.py:362 +msgid "Run settings for %s" +msgstr "" + +#: spyder/plugins/runconfig.py:394 +msgid "Select a run configuration:" +msgstr "" + +#: spyder/plugins/runconfig.py:423 spyder/plugins/runconfig.py:448 +msgid "Run Settings" +msgstr "" + +#: spyder/plugins/runconfig.py:450 +msgid "The following are the default %s. These options may be overriden using the %s dialog box (see the %s menu)" +msgstr "" + +#: spyder/plugins/runconfig.py:476 +msgid "Default working directory is:" +msgstr "" + +#: spyder/plugins/runconfig.py:478 +msgid "the script directory" +msgstr "" + +#: spyder/plugins/runconfig.py:481 spyder/plugins/workingdirectory.py:57 +msgid "the following directory:" +msgstr "" + +#: spyder/plugins/runconfig.py:507 +msgid "Show warning when killing running processes" +msgstr "" + +#: spyder/plugins/runconfig.py:516 +msgid "Run Settings dialog" +msgstr "" + +#: spyder/plugins/shortcuts.py:137 +msgid "" +"Press the new shortcut and select 'Ok': \n" +"(Press 'Tab' once to switch focus between the shortcut entry \n" +"and the buttons below it)" +msgstr "" + +#: spyder/plugins/shortcuts.py:140 +msgid "Current shortcut:" +msgstr "" + +#: spyder/plugins/shortcuts.py:142 +msgid "New shortcut:" +msgstr "" + +#: spyder/plugins/shortcuts.py:155 +msgid "Shortcut: {0}" +msgstr "" + +#: spyder/plugins/shortcuts.py:276 +msgid "Please introduce a different shortcut" +msgstr "" + +#: spyder/plugins/shortcuts.py:313 +msgid "The new shorcut conflicts with:" +msgstr "" + +#: spyder/plugins/shortcuts.py:324 +msgid "A compound sequence can have {break} a maximum of 4 subsequences.{break}" +msgstr "" + +#: spyder/plugins/shortcuts.py:329 +msgid "Invalid key entered" +msgstr "" + +#: spyder/plugins/shortcuts.py:531 +msgid "Context" +msgstr "" + +#: spyder/plugins/shortcuts.py:533 +#: spyder/widgets/variableexplorer/collectionseditor.py:118 +msgid "Name" +msgstr "" + +#: spyder/plugins/shortcuts.py:535 +msgid "Shortcut" +msgstr "" + +#: spyder/plugins/shortcuts.py:537 +msgid "Score" +msgstr "" + +#: spyder/plugins/shortcuts.py:697 +msgid "Conflicts" +msgstr "" + +#: spyder/plugins/shortcuts.py:698 +msgid "The following conflicts have been detected:" +msgstr "" + +#: spyder/plugins/shortcuts.py:783 +msgid "Keyboard shortcuts" +msgstr "" + +#: spyder/plugins/shortcuts.py:791 +msgid "Search: " +msgstr "" + +#: spyder/plugins/shortcuts.py:792 +msgid "Reset to default values" +msgstr "" + +#: spyder/plugins/variableexplorer.py:26 +msgid "Autorefresh" +msgstr "" + +#: spyder/plugins/variableexplorer.py:27 +msgid "Enable autorefresh" +msgstr "" + +#: spyder/plugins/variableexplorer.py:29 +msgid "Refresh interval: " +msgstr "" + +#: spyder/plugins/variableexplorer.py:33 +msgid "Filter" +msgstr "" + +#: spyder/plugins/variableexplorer.py:35 +#: spyder/widgets/variableexplorer/namespacebrowser.py:226 +msgid "Exclude private references" +msgstr "" + +#: spyder/plugins/variableexplorer.py:36 +#: spyder/widgets/variableexplorer/namespacebrowser.py:241 +msgid "Exclude capitalized references" +msgstr "" + +#: spyder/plugins/variableexplorer.py:37 +#: spyder/widgets/variableexplorer/namespacebrowser.py:234 +msgid "Exclude all-uppercase references" +msgstr "" + +#: spyder/plugins/variableexplorer.py:38 +#: spyder/widgets/variableexplorer/namespacebrowser.py:249 +msgid "Exclude unsupported data types" +msgstr "" + +#: spyder/plugins/variableexplorer.py:46 +#: spyder/widgets/variableexplorer/collectionseditor.py:677 +msgid "Show arrays min/max" +msgstr "" + +#: spyder/plugins/variableexplorer.py:48 +msgid "Edit data in the remote process" +msgstr "" + +#: spyder/plugins/variableexplorer.py:49 +msgid "" +"Editors are opened in the remote process for NumPy arrays, PIL images, lists, tuples and dictionaries.\n" +"This avoids transfering large amount of data between the remote process and Spyder (through the socket)." +msgstr "" + +#: spyder/plugins/variableexplorer.py:185 +msgid "Variable explorer" +msgstr "" + +#: spyder/plugins/workingdirectory.py:38 +msgid "The global working directory is the working directory for newly opened consoles (Python/IPython consoles and terminals), for the file explorer, for the find in files plugin and for new files created in the editor." +msgstr "" + +#: spyder/plugins/workingdirectory.py:47 +msgid "At startup, the global working directory is:" +msgstr "" + +#: spyder/plugins/workingdirectory.py:51 +msgid "the same as in last session" +msgstr "" + +#: spyder/plugins/workingdirectory.py:53 +msgid "At startup, Spyder will restore the global directory from last session" +msgstr "" + +#: spyder/plugins/workingdirectory.py:59 +msgid "At startup, the global working directory will be the specified path" +msgstr "" + +#: spyder/plugins/workingdirectory.py:71 +msgid "Files are opened from:" +msgstr "" + +#: spyder/plugins/workingdirectory.py:75 spyder/plugins/workingdirectory.py:88 +msgid "the current file directory" +msgstr "" + +#: spyder/plugins/workingdirectory.py:79 spyder/plugins/workingdirectory.py:92 +msgid "the global working directory" +msgstr "" + +#: spyder/plugins/workingdirectory.py:84 +msgid "Files are created in:" +msgstr "" + +#: spyder/plugins/workingdirectory.py:98 +msgid "Change to file base directory" +msgstr "" + +#: spyder/plugins/workingdirectory.py:100 +msgid "When opening a file" +msgstr "" + +#: spyder/plugins/workingdirectory.py:102 +msgid "When saving a file" +msgstr "" + +#: spyder/plugins/workingdirectory.py:172 +msgid "Back" +msgstr "" + +#: spyder/plugins/workingdirectory.py:180 spyder/widgets/explorer.py:1099 +#: spyder/widgets/variableexplorer/importwizard.py:529 +msgid "Next" +msgstr "" + +#: spyder/plugins/workingdirectory.py:191 +msgid "" +"This is the working directory for newly\n" +"opened consoles (Python/IPython consoles and\n" +"terminals), for the file explorer, for the\n" +"find in files plugin and for new files\n" +"created in the editor" +msgstr "" + +#: spyder/plugins/workingdirectory.py:219 +msgid "Browse a working directory" +msgstr "" + +#: spyder/plugins/workingdirectory.py:226 +msgid "Change to parent directory" +msgstr "" + +#: spyder/plugins/workingdirectory.py:233 +msgid "Global working directory" +msgstr "" + +#: spyder/utils/codeanalysis.py:91 +msgid "Real-time code analysis on the Editor" +msgstr "" + +#: spyder/utils/codeanalysis.py:95 +msgid "Real-time code style analysis on the Editor" +msgstr "" + +#: spyder/utils/environ.py:101 +msgid "Module pywin32 was not found.
Please restart this Windows session (not the computer) for changes to take effect." +msgstr "" + +#: spyder/utils/environ.py:114 +msgid "If you accept changes, this will modify the current user environment variables directly in Windows registry. Use it with precautions, at your own risks.

Note that for changes to take effect, you will need to restart the parent process of this application (simply restart Spyder if you have executed it from a Windows shortcut, otherwise restart any application from which you may have executed it, like Python(x,y) Home for example)" +msgstr "" + +#: spyder/utils/help/sphinxify.py:217 spyder/utils/help/sphinxify.py:227 +msgid "It was not possible to generate rich text help for this object.
Please see it in plain text." +msgstr "" + +#: spyder/utils/introspection/manager.py:33 +#: spyder/utils/introspection/manager.py:38 +msgid "Editor's code completion, go-to-definition and help" +msgstr "" + +#: spyder/utils/iofuncs.py:408 +msgid "Supported files" +msgstr "" + +#: spyder/utils/iofuncs.py:410 +msgid "All files (*.*)" +msgstr "" + +#: spyder/utils/iofuncs.py:420 +msgid "Spyder data files" +msgstr "" + +#: spyder/utils/iofuncs.py:422 +#: spyder/widgets/variableexplorer/collectionseditor.py:1021 +msgid "NumPy arrays" +msgstr "" + +#: spyder/utils/iofuncs.py:423 +msgid "NumPy zip arrays" +msgstr "" + +#: spyder/utils/iofuncs.py:424 +msgid "Matlab files" +msgstr "" + +#: spyder/utils/iofuncs.py:425 +msgid "CSV text files" +msgstr "" + +#: spyder/utils/iofuncs.py:427 +msgid "JPEG images" +msgstr "" + +#: spyder/utils/iofuncs.py:428 +msgid "PNG images" +msgstr "" + +#: spyder/utils/iofuncs.py:429 +msgid "GIF images" +msgstr "" + +#: spyder/utils/iofuncs.py:430 +msgid "TIFF images" +msgstr "" + +#: spyder/utils/iofuncs.py:431 spyder/utils/iofuncs.py:432 +msgid "Pickle files" +msgstr "" + +#: spyder/utils/iofuncs.py:433 +msgid "JSON files" +msgstr "" + +#: spyder/utils/iofuncs.py:452 spyder/utils/iofuncs.py:459 +msgid "Unsupported file type '%s'" +msgstr "" + +#: spyder/utils/programs.py:286 +msgid "It was not possible to run this file in an external terminal" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:33 +msgid "Syntax highlighting for Matlab, Julia and other file types" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:42 +msgid "Background:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:43 +#: spyder/widgets/sourcecode/codeeditor.py:106 +msgid "Current line:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:44 +msgid "Current cell:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:45 +msgid "Occurrence:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:46 +msgid "Link:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:47 +msgid "Side areas:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:48 +msgid "Matched
parens:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:49 +msgid "Unmatched
parens:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:50 +msgid "Normal text:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:51 +msgid "Keyword:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:52 +msgid "Builtin:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:53 +msgid "Definition:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:54 +msgid "Comment:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:55 +msgid "String:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:56 +msgid "Number:" +msgstr "" + +#: spyder/utils/syntaxhighlighters.py:57 +msgid "Instance:" +msgstr "" + +#: spyder/widgets/arraybuilder.py:179 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Type an array in Matlab : [1 2;3 4]
\n" +" or Spyder simplified syntax : 1 2;3 4\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two spaces or two tabs to generate a ';'.\n" +" " +msgstr "" + +#: spyder/widgets/arraybuilder.py:190 +msgid "" +"\n" +" Numpy Array/Matrix Helper
\n" +" Enter an array in the table.
\n" +" Use Tab to move between cells.\n" +"

\n" +" Hit 'Enter' for array or 'Ctrl+Enter' for matrix.\n" +"

\n" +" Hint:
\n" +" Use two tabs at the end of a row to move to the next row.\n" +" " +msgstr "" + +#: spyder/widgets/arraybuilder.py:365 +msgid "Array dimensions not valid" +msgstr "" + +#: spyder/widgets/browser.py:54 spyder/widgets/sourcecode/codeeditor.py:2559 +msgid "Zoom out" +msgstr "" + +#: spyder/widgets/browser.py:57 spyder/widgets/sourcecode/codeeditor.py:2555 +msgid "Zoom in" +msgstr "" + +#: spyder/widgets/browser.py:177 +msgid "Home" +msgstr "" + +#: spyder/widgets/browser.py:213 +msgid "Find text" +msgstr "" + +#: spyder/widgets/browser.py:231 +msgid "Address:" +msgstr "" + +#: spyder/widgets/browser.py:267 +msgid "Unable to load page" +msgstr "" + +#: spyder/widgets/comboboxes.py:165 +msgid "Press enter to validate this entry" +msgstr "" + +#: spyder/widgets/comboboxes.py:166 +msgid "This entry is incorrect" +msgstr "" + +#: spyder/widgets/comboboxes.py:209 +msgid "Press enter to validate this path" +msgstr "" + +#: spyder/widgets/dependencies.py:63 +msgid " Required " +msgstr "" + +#: spyder/widgets/dependencies.py:63 +msgid "Module" +msgstr "" + +#: spyder/widgets/dependencies.py:64 +msgid " Installed " +msgstr "" + +#: spyder/widgets/dependencies.py:64 +msgid "Provided features" +msgstr "" + +#: spyder/widgets/dependencies.py:134 +msgid "Dependencies" +msgstr "" + +#: spyder/widgets/dependencies.py:141 +msgid "Spyder depends on several Python modules to provide the right functionality for all its panes. The table below shows the required and installed versions (if any) of all of them.

Note: You can safely use Spyder without the following modules installed: %s and %s.

Please also note that new dependencies or changed ones will be correctly detected only after Spyder is restarted." +msgstr "" + +#: spyder/widgets/dependencies.py:157 +msgid "Copy to clipboard" +msgstr "" + +#: spyder/widgets/editor.py:350 +msgid "Copy path to clipboard" +msgstr "" + +#: spyder/widgets/editor.py:354 +msgid "Close all to the right" +msgstr "" + +#: spyder/widgets/editor.py:356 +msgid "Close all but this" +msgstr "" + +#: spyder/widgets/editor.py:959 +msgid "Temporary file" +msgstr "" + +#: spyder/widgets/editor.py:1054 +msgid "New window" +msgstr "" + +#: spyder/widgets/editor.py:1055 +msgid "Create a new editor window" +msgstr "" + +#: spyder/widgets/editor.py:1058 +msgid "Split vertically" +msgstr "" + +#: spyder/widgets/editor.py:1060 +msgid "Split vertically this editor window" +msgstr "" + +#: spyder/widgets/editor.py:1062 +msgid "Split horizontally" +msgstr "" + +#: spyder/widgets/editor.py:1064 +msgid "Split horizontally this editor window" +msgstr "" + +#: spyder/widgets/editor.py:1066 +msgid "Close this panel" +msgstr "" + +#: spyder/widgets/editor.py:1223 +msgid "%s has been modified.
Do you want to save changes?" +msgstr "" + +#: spyder/widgets/editor.py:1285 +msgid "Save" +msgstr "" + +#: spyder/widgets/editor.py:1286 +msgid "Unable to save script '%s'

Error message:
%s" +msgstr "" + +#: spyder/widgets/editor.py:1523 +msgid "%s is unavailable (this file may have been removed, moved or renamed outside Spyder).
Do you want to close it?" +msgstr "" + +#: spyder/widgets/editor.py:1543 +msgid "%s has been modified outside Spyder.
Do you want to reload it and lose all your changes?" +msgstr "" + +#: spyder/widgets/editor.py:1639 +msgid "All changes to %s will be lost.
Do you want to revert file from disk?" +msgstr "" + +#: spyder/widgets/editor.py:1779 +msgid "Loading %s..." +msgstr "" + +#: spyder/widgets/editor.py:1789 +msgid "%s contains mixed end-of-line characters.
Spyder will fix this automatically." +msgstr "" + +#: spyder/widgets/editor.py:2171 +msgid "Close window" +msgstr "" + +#: spyder/widgets/editor.py:2173 +msgid "Close this window" +msgstr "" + +#: spyder/widgets/editortools.py:94 spyder/widgets/editortools.py:130 +msgid "Line %s" +msgstr "" + +#: spyder/widgets/editortools.py:99 +msgid "Class defined at line %s" +msgstr "" + +#: spyder/widgets/editortools.py:107 +msgid "Method defined at line %s" +msgstr "" + +#: spyder/widgets/editortools.py:117 +msgid "Function defined at line %s" +msgstr "" + +#: spyder/widgets/editortools.py:149 +msgid "Cell starts at line %s" +msgstr "" + +#: spyder/widgets/editortools.py:202 spyder/widgets/editortools.py:539 +msgid "Go to cursor position" +msgstr "" + +#: spyder/widgets/editortools.py:205 +msgid "Show absolute path" +msgstr "" + +#: spyder/widgets/editortools.py:208 spyder/widgets/explorer.py:210 +msgid "Show all files" +msgstr "" + +#: spyder/widgets/editortools.py:211 +msgid "Show special comments" +msgstr "" + +#: spyder/widgets/explorer.py:206 +msgid "Edit filename filters..." +msgstr "" + +#: spyder/widgets/explorer.py:220 +msgid "Edit filename filters" +msgstr "" + +#: spyder/widgets/explorer.py:221 +msgid "Name filters:" +msgstr "" + +#: spyder/widgets/explorer.py:240 +msgid "File..." +msgstr "" + +#: spyder/widgets/explorer.py:244 +msgid "Module..." +msgstr "" + +#: spyder/widgets/explorer.py:248 +msgid "Folder..." +msgstr "" + +#: spyder/widgets/explorer.py:252 +msgid "Package..." +msgstr "" + +#: spyder/widgets/explorer.py:273 +#: spyder/widgets/variableexplorer/collectionseditor.py:652 +msgid "Edit" +msgstr "" + +#: spyder/widgets/explorer.py:275 +msgid "Move..." +msgstr "" + +#: spyder/widgets/explorer.py:278 +msgid "Delete..." +msgstr "" + +#: spyder/widgets/explorer.py:281 +msgid "Rename..." +msgstr "" + +#: spyder/widgets/explorer.py:284 +msgid "Open" +msgstr "" + +#: spyder/widgets/explorer.py:285 spyder/widgets/sourcecode/codeeditor.py:2531 +msgid "Convert to Python script" +msgstr "" + +#: spyder/widgets/explorer.py:319 +msgid "Commit" +msgstr "" + +#: spyder/widgets/explorer.py:322 +msgid "Browse repository" +msgstr "" + +#: spyder/widgets/explorer.py:333 +msgid "Open command prompt here" +msgstr "" + +#: spyder/widgets/explorer.py:335 +msgid "Open terminal here" +msgstr "" + +#: spyder/widgets/explorer.py:340 +msgid "Open Python console here" +msgstr "" + +#: spyder/widgets/explorer.py:354 +msgid "New" +msgstr "" + +#: spyder/widgets/explorer.py:362 +msgid "Import" +msgstr "" + +#: spyder/widgets/explorer.py:513 +msgid "Do you really want to delete %s?" +msgstr "" + +#: spyder/widgets/explorer.py:531 +msgid "delete" +msgstr "" + +#: spyder/widgets/explorer.py:532 spyder/widgets/projects/explorer.py:149 +#: spyder/widgets/projects/explorer.py:256 +msgid "Project Explorer" +msgstr "" + +#: spyder/widgets/explorer.py:533 spyder/widgets/projects/explorer.py:150 +msgid "Unable to %s %s

Error message:
%s" +msgstr "" + +#: spyder/widgets/explorer.py:548 +msgid "File Explorer" +msgstr "" + +#: spyder/widgets/explorer.py:549 +msgid "The current directory contains a project.

If you want to delete the project, please go to Projects » Delete Project" +msgstr "" + +#: spyder/widgets/explorer.py:566 spyder/widgets/sourcecode/codeeditor.py:2018 +msgid "Conversion error" +msgstr "" + +#: spyder/widgets/explorer.py:567 spyder/widgets/sourcecode/codeeditor.py:2019 +msgid "" +"It was not possible to convert this notebook. The error is:\n" +"\n" +msgstr "" + +#: spyder/widgets/explorer.py:584 spyder/widgets/explorer.py:592 +#: spyder/widgets/explorer.py:603 +#: spyder/widgets/variableexplorer/collectionseditor.py:681 +#: spyder/widgets/variableexplorer/collectionseditor.py:916 +msgid "Rename" +msgstr "" + +#: spyder/widgets/explorer.py:585 +msgid "New name:" +msgstr "" + +#: spyder/widgets/explorer.py:593 +msgid "Do you really want to rename %s and overwrite the existing file %s?" +msgstr "" + +#: spyder/widgets/explorer.py:604 +msgid "Unable to rename file %s

Error message:
%s" +msgstr "" + +#: spyder/widgets/explorer.py:640 +msgid "Unable to move %s

Error message:
%s" +msgstr "" + +#: spyder/widgets/explorer.py:658 +msgid "Unable to create folder %s

Error message:
%s" +msgstr "" + +#: spyder/widgets/explorer.py:671 spyder/widgets/explorer.py:705 +msgid "Unable to create file %s

Error message:
%s" +msgstr "" + +#: spyder/widgets/explorer.py:679 +msgid "New folder" +msgstr "" + +#: spyder/widgets/explorer.py:680 +msgid "Folder name:" +msgstr "" + +#: spyder/widgets/explorer.py:685 +msgid "New package" +msgstr "" + +#: spyder/widgets/explorer.py:686 +msgid "Package name:" +msgstr "" + +#: spyder/widgets/explorer.py:726 +msgid "New module" +msgstr "" + +#: spyder/widgets/explorer.py:741 +msgid "For %s support, please install one of the
following tools:

%s" +msgstr "" + +#: spyder/widgets/explorer.py:745 +msgid "Unable to find external program.

%s" +msgstr "" + +#: spyder/widgets/explorer.py:966 +msgid "Show current directory only" +msgstr "" + +#: spyder/widgets/explorer.py:1066 +msgid "You don't have the right permissions to open this directory" +msgstr "" + +#: spyder/widgets/explorer.py:1096 +#: spyder/widgets/variableexplorer/importwizard.py:525 +msgid "Previous" +msgstr "" + +#: spyder/widgets/explorer.py:1102 +msgid "Parent" +msgstr "" + +#: spyder/widgets/externalshell/baseshell.py:129 +msgid "Run again this program" +msgstr "" + +#: spyder/widgets/externalshell/baseshell.py:132 +msgid "Kill" +msgstr "" + +#: spyder/widgets/externalshell/baseshell.py:134 +msgid "Kills the current process, causing it to exit immediately" +msgstr "" + +#: spyder/widgets/externalshell/baseshell.py:206 +msgid "Running..." +msgstr "" + +#: spyder/widgets/externalshell/baseshell.py:213 +msgid "Terminated." +msgstr "" + +#: spyder/widgets/externalshell/baseshell.py:238 +#: spyder/widgets/ipythonconsole/help.py:125 spyder/widgets/mixins.py:661 +msgid "Arguments" +msgstr "" + +#: spyder/widgets/externalshell/baseshell.py:239 +msgid "Command line arguments:" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:277 +msgid "Variables" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:278 +msgid "Show/hide global variables explorer" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:282 +msgid "Terminate" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:283 +msgid "" +"Attempts to stop the process. The process\n" +"may not exit as a result of clicking this\n" +"button (it is given the chance to prompt\n" +"the user for any unsaved files, etc)." +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:296 +msgid "Interact" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:298 +msgid "Debug" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:300 +#: spyder/widgets/externalshell/pythonshell.py:366 +msgid "Arguments..." +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:302 +msgid "Post Mortem Debug" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:308 +msgid "Working directory" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:310 +msgid "Set current working directory" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:312 +msgid "Environment variables" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:316 +msgid "Show sys.path contents" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:362 +msgid "Arguments: %s" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:364 +msgid "No argument" +msgstr "" + +#: spyder/widgets/externalshell/pythonshell.py:534 +msgid "A Python console failed to start!" +msgstr "" + +#: spyder/widgets/externalshell/systemshell.py:106 +msgid "Process failed to start" +msgstr "" + +#: spyder/widgets/fileswitcher.py:109 +msgid "unsaved file" +msgstr "" + +#: spyder/widgets/fileswitcher.py:230 +msgid "Press Enter to switch files or Esc to cancel.

Type to filter filenames.

Use :number to go to a line, e.g. main:42
Use @symbol_text to go to a symbol, e.g. @init

Press Ctrl+W to close current tab.
" +msgstr "" + +#: spyder/widgets/fileswitcher.py:508 +msgid "lines" +msgstr "" + +#: spyder/widgets/findinfiles.py:158 +msgid "Unexpected error: see internal console" +msgstr "" + +#: spyder/widgets/findinfiles.py:209 spyder/widgets/findinfiles.py:233 +#: spyder/widgets/findinfiles.py:280 +msgid "invalid regular expression" +msgstr "" + +#: spyder/widgets/findinfiles.py:278 +msgid "permission denied errors were encountered" +msgstr "" + +#: spyder/widgets/findinfiles.py:315 +msgid "Search pattern" +msgstr "" + +#: spyder/widgets/findinfiles.py:318 spyder/widgets/findinfiles.py:352 +#: spyder/widgets/findinfiles.py:364 spyder/widgets/findreplace.py:81 +msgid "Regular expression" +msgstr "" + +#: spyder/widgets/findinfiles.py:327 +msgid "Search" +msgstr "" + +#: spyder/widgets/findinfiles.py:330 +msgid "Start search" +msgstr "" + +#: spyder/widgets/findinfiles.py:336 +msgid "Stop search" +msgstr "" + +#: spyder/widgets/findinfiles.py:346 +msgid "Included filenames pattern" +msgstr "" + +#: spyder/widgets/findinfiles.py:355 +msgid "Include:" +msgstr "" + +#: spyder/widgets/findinfiles.py:358 +msgid "Excluded filenames pattern" +msgstr "" + +#: spyder/widgets/findinfiles.py:367 +msgid "Exclude:" +msgstr "" + +#: spyder/widgets/findinfiles.py:377 +msgid "PYTHONPATH" +msgstr "" + +#: spyder/widgets/findinfiles.py:379 +msgid "Search in all directories listed in sys.path which are outside the Python installation directory" +msgstr "" + +#: spyder/widgets/findinfiles.py:382 +msgid "Hg repository" +msgstr "" + +#: spyder/widgets/findinfiles.py:385 +msgid "Search in current directory hg repository" +msgstr "" + +#: spyder/widgets/findinfiles.py:386 +msgid "Here:" +msgstr "" + +#: spyder/widgets/findinfiles.py:390 +msgid "Search recursively in this directory" +msgstr "" + +#: spyder/widgets/findinfiles.py:395 +msgid "Browse a search directory" +msgstr "" + +#: spyder/widgets/findinfiles.py:425 +msgid "Hide advanced options" +msgstr "" + +#: spyder/widgets/findinfiles.py:428 +msgid "Show advanced options" +msgstr "" + +#: spyder/widgets/findinfiles.py:569 +msgid "Search canceled" +msgstr "" + +#: spyder/widgets/findinfiles.py:573 +msgid "String not found" +msgstr "" + +#: spyder/widgets/findinfiles.py:575 +msgid "matches in" +msgstr "" + +#: spyder/widgets/findinfiles.py:576 +msgid "file" +msgstr "" + +#: spyder/widgets/findinfiles.py:584 +msgid "interrupted" +msgstr "" + +#: spyder/widgets/findreplace.py:63 +msgid "Search string" +msgstr "" + +#: spyder/widgets/findreplace.py:87 +msgid "Case Sensitive" +msgstr "" + +#: spyder/widgets/findreplace.py:93 +msgid "Whole words" +msgstr "" + +#: spyder/widgets/findreplace.py:99 +msgid "Highlight matches" +msgstr "" + +#: spyder/widgets/findreplace.py:113 +msgid "Replace with:" +msgstr "" + +#: spyder/widgets/findreplace.py:115 +msgid "Replace string" +msgstr "" + +#: spyder/widgets/findreplace.py:118 +msgid "Replace/find" +msgstr "" + +#: spyder/widgets/findreplace.py:125 +msgid "Replace all" +msgstr "" + +#: spyder/widgets/internalshell.py:262 +msgid "Help..." +msgstr "" + +#: spyder/widgets/internalshell.py:279 +msgid "Shell special commands:" +msgstr "" + +#: spyder/widgets/internalshell.py:280 +msgid "Internal editor:" +msgstr "" + +#: spyder/widgets/internalshell.py:281 +msgid "External editor:" +msgstr "" + +#: spyder/widgets/internalshell.py:282 +msgid "Run script:" +msgstr "" + +#: spyder/widgets/internalshell.py:283 +msgid "Remove references:" +msgstr "" + +#: spyder/widgets/internalshell.py:284 +msgid "System commands:" +msgstr "" + +#: spyder/widgets/internalshell.py:285 +msgid "Python help:" +msgstr "" + +#: spyder/widgets/internalshell.py:286 +msgid "GUI-based editor:" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:189 +msgid "An error ocurred while starting the kernel" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:220 +msgid "Restart kernel" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:240 +msgid "Stop the current command" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:263 +msgid "Inspect current object" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:268 +msgid "Clear line or block" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:272 +msgid "Reset namespace" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:275 +msgid "Clear console" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:316 +msgid "Are you sure you want to restart the kernel?" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:318 +msgid "Restart kernel?" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:327 +msgid "" +"Error restarting kernel: %s\n" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:331 +msgid "" +"
Restarting kernel...\n" +"

" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:336 +msgid "" +"Cannot restart a kernel not started by Spyder\n" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:376 +msgid "Changing backend to Qt for Mayavi" +msgstr "" + +#: spyder/widgets/ipythonconsole/client.py:387 +msgid "Connecting to kernel..." +msgstr "" + +#: spyder/widgets/ipythonconsole/namespacebrowser.py:76 +msgid "Inspecting and setting values while debugging in IPython consoles is not supported yet by Spyder." +msgstr "" + +#: spyder/widgets/ipythonconsole/shell.py:149 +msgid "Reset IPython namespace" +msgstr "" + +#: spyder/widgets/ipythonconsole/shell.py:150 +msgid "All user-defined variables will be removed.
Are you sure you want to reset the namespace?" +msgstr "" + +#: spyder/widgets/onecolumntree.py:52 +msgid "Collapse all" +msgstr "" + +#: spyder/widgets/onecolumntree.py:56 +msgid "Expand all" +msgstr "" + +#: spyder/widgets/onecolumntree.py:60 +msgid "Restore" +msgstr "" + +#: spyder/widgets/onecolumntree.py:61 +msgid "Restore original tree layout" +msgstr "" + +#: spyder/widgets/onecolumntree.py:65 +msgid "Collapse selection" +msgstr "" + +#: spyder/widgets/onecolumntree.py:69 +msgid "Expand selection" +msgstr "" + +#: spyder/widgets/pathmanager.py:87 +msgid "Move to top" +msgstr "" + +#: spyder/widgets/pathmanager.py:93 +msgid "Move up" +msgstr "" + +#: spyder/widgets/pathmanager.py:99 +msgid "Move down" +msgstr "" + +#: spyder/widgets/pathmanager.py:105 +msgid "Move to bottom" +msgstr "" + +#: spyder/widgets/pathmanager.py:116 spyder/widgets/pathmanager.py:231 +msgid "Add path" +msgstr "" + +#: spyder/widgets/pathmanager.py:121 spyder/widgets/pathmanager.py:214 +msgid "Remove path" +msgstr "" + +#: spyder/widgets/pathmanager.py:131 +msgid "Synchronize..." +msgstr "" + +#: spyder/widgets/pathmanager.py:133 +msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" +msgstr "" + +#: spyder/widgets/pathmanager.py:145 +msgid "Synchronize" +msgstr "" + +#: spyder/widgets/pathmanager.py:146 +msgid "This will synchronize Spyder's path list with PYTHONPATH environment variable for current user, allowing you to run your Python modules outside Spyder without having to configure sys.path.
Do you want to clear contents of PYTHONPATH before adding Spyder's path list?" +msgstr "" + +#: spyder/widgets/pathmanager.py:215 +msgid "Do you really want to remove selected path?" +msgstr "" + +#: spyder/widgets/pathmanager.py:232 +msgid "This directory is already included in Spyder path list.
Do you want to move it to the top of the list?" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:30 +msgid "Project preferences" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:82 +#: spyder/widgets/projects/configdialog.py:119 +msgid "Restore data on startup" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:84 +#: spyder/widgets/projects/configdialog.py:121 +msgid "Save data on exit" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:86 +#: spyder/widgets/projects/configdialog.py:123 +msgid "Save history" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:88 +#: spyder/widgets/projects/configdialog.py:125 +msgid "Save non project files opened" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:111 +msgid "Code" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:118 +msgid "Workspace" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:148 +#: spyder/widgets/projects/configdialog.py:155 +msgid "Version control" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:156 +msgid "Use version control" +msgstr "" + +#: spyder/widgets/projects/configdialog.py:161 +msgid "Version control system" +msgstr "" + +#: spyder/widgets/projects/explorer.py:52 +msgid "Show horizontal scrollbar" +msgstr "" + +#: spyder/widgets/projects/explorer.py:114 +msgid "File %s already exists.
Do you want to overwrite it?" +msgstr "" + +#: spyder/widgets/projects/explorer.py:128 +msgid "Folder %s already exists." +msgstr "" + +#: spyder/widgets/projects/explorer.py:146 +msgid "copy" +msgstr "" + +#: spyder/widgets/projects/explorer.py:148 +msgid "move" +msgstr "" + +#: spyder/widgets/projects/explorer.py:244 +msgid "Do you really want to delete {filename}?

Note: This action will only delete the project. Its files are going to be preserved on disk." +msgstr "" + +#: spyder/widgets/projects/explorer.py:257 +msgid "Unable to delete {varpath}

The error message was:
{error}" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:69 +msgid "New directory" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:70 +msgid "Existing directory" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:72 +msgid "Project name" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:73 +msgid "Location" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:74 +msgid "Project type" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:75 +msgid "Python version" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:83 +#: spyder/widgets/variableexplorer/importwizard.py:519 +msgid "Cancel" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:84 +msgid "Create" +msgstr "" + +#: spyder/widgets/projects/projectdialog.py:102 +msgid "Create new project" +msgstr "" + +#: spyder/widgets/projects/type/__init__.py:215 +msgid "Empty project" +msgstr "" + +#: spyder/widgets/projects/type/python.py:20 +msgid "Python project" +msgstr "" + +#: spyder/widgets/projects/type/python.py:76 +msgid "Python package" +msgstr "" + +#: spyder/widgets/pydocgui.py:110 +msgid "Module or package:" +msgstr "" + +#: spyder/widgets/shell.py:129 +msgid "Save history log..." +msgstr "" + +#: spyder/widgets/shell.py:131 +msgid "Save current history log (i.e. all inputs and outputs) in a text file" +msgstr "" + +#: spyder/widgets/shell.py:257 +msgid "Save history log" +msgstr "" + +#: spyder/widgets/shell.py:260 +msgid "History logs" +msgstr "" + +#: spyder/widgets/shell.py:271 +msgid "Unable to save file '%s'

Error message:
%s" +msgstr "" + +#: spyder/widgets/shell.py:713 +msgid "Copy without prompts" +msgstr "" + +#: spyder/widgets/shell.py:716 spyder/widgets/shell.py:720 +msgid "Clear line" +msgstr "" + +#: spyder/widgets/shell.py:722 +msgid "Clear shell" +msgstr "" + +#: spyder/widgets/shell.py:726 +msgid "Clear shell contents ('cls' command)" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:100 +msgid "Go to line:" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:108 +msgid "Line count:" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:1305 +msgid "Breakpoint" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:1306 +msgid "Condition:" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:1712 +msgid "Code analysis" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:1766 +msgid "To do" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:2005 +msgid "Removal error" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:2006 +msgid "" +"It was not possible to remove outputs from this notebook. The error is:\n" +"\n" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:2528 +msgid "Clear all ouput" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:2534 +msgid "Go to definition" +msgstr "" + +#: spyder/widgets/sourcecode/codeeditor.py:2563 +msgid "Zoom reset" +msgstr "" + +#: spyder/widgets/status.py:25 +msgid "CPU and memory usage info in the status bar" +msgstr "" + +#: spyder/widgets/status.py:94 +msgid "Memory:" +msgstr "" + +#: spyder/widgets/status.py:95 +msgid "Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows platforms" +msgstr "" + +#: spyder/widgets/status.py:107 +msgid "CPU:" +msgstr "" + +#: spyder/widgets/status.py:108 +msgid "CPU usage status: requires the `psutil` (>=v0.3) library" +msgstr "" + +#: spyder/widgets/status.py:130 +msgid "Permissions:" +msgstr "" + +#: spyder/widgets/status.py:144 +msgid "End-of-lines:" +msgstr "" + +#: spyder/widgets/status.py:158 +msgid "Encoding:" +msgstr "" + +#: spyder/widgets/status.py:171 +msgid "Line:" +msgstr "" + +#: spyder/widgets/status.py:175 +msgid "Column:" +msgstr "" + +#: spyder/widgets/tabs.py:146 +msgid "Browse tabs" +msgstr "" + +#: spyder/widgets/tabs.py:275 +msgid "Close current tab" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:497 +msgid "It was not possible to copy values for this array" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:532 +#: spyder/widgets/variableexplorer/arrayeditor.py:565 +#: spyder/widgets/variableexplorer/dataframeeditor.py:544 +#: spyder/widgets/variableexplorer/dataframeeditor.py:584 +msgid "Format" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:537 +#: spyder/widgets/variableexplorer/dataframeeditor.py:548 +msgid "Resize" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:566 +#: spyder/widgets/variableexplorer/dataframeeditor.py:585 +msgid "Float formatting" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:574 +#: spyder/widgets/variableexplorer/dataframeeditor.py:594 +msgid "Format (%s) is incorrect" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:609 +msgid "Array is empty" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:612 +msgid "Arrays with more than 3 dimensions are not supported" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:615 +msgid "The 'xlabels' argument length do no match array column number" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:619 +msgid "The 'ylabels' argument length do no match array row number" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:626 +msgid "%s arrays" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:627 +msgid "%s are currently not supported" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:634 +msgid "NumPy array" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:636 +#: spyder/widgets/variableexplorer/arrayeditor.py:790 +msgid "Array editor" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:638 +msgid "read only" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:668 +msgid "Record array fields:" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Data" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Mask" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:680 +msgid "Masked data" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:691 +msgid "Axis:" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:696 +msgid "Index:" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:709 +msgid "Warning: changes are applied separately" +msgstr "" + +#: spyder/widgets/variableexplorer/arrayeditor.py:710 +msgid "For performance reasons, changes applied to masked array won't be reflected in array's data (and vice-versa)." +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:116 +msgid "Index" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:121 +msgid "Tuple" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:124 +msgid "List" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:127 +msgid "Dictionary" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:129 +msgid "Key" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:135 +msgid "Attribute" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:137 +msgid "elements" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Size" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Type" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:311 +msgid "Value" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:409 +msgid "" +"Opening this variable can be slow\n" +"\n" +"Do you want to continue anyway?" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:418 +msgid "Spyder was unable to retrieve the value of this variable from the console.

The error mesage was:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:583 +msgid "Edit item" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:584 +msgid "Unable to assign data to item.

Error message:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:644 +msgid "Resize rows to contents" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:655 +#: spyder/widgets/variableexplorer/collectionseditor.py:990 +#: spyder/widgets/variableexplorer/collectionseditor.py:1007 +msgid "Plot" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:659 +msgid "Histogram" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:663 +msgid "Show image" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:667 +#: spyder/widgets/variableexplorer/collectionseditor.py:1015 +msgid "Save array" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:671 +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Insert" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:674 +#: spyder/widgets/variableexplorer/collectionseditor.py:898 +msgid "Remove" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:684 +#: spyder/widgets/variableexplorer/collectionseditor.py:919 +msgid "Duplicate" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:896 +msgid "Do you want to remove the selected item?" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:897 +msgid "Do you want to remove all selected items?" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:917 +msgid "New variable name:" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:920 +msgid "Variable name:" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:954 +msgid "Key:" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:962 +msgid "Value:" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:978 +msgid "Import error" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:979 +msgid "Please install matplotlib or guiqwt." +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:991 +msgid "Unable to plot data.

Error message:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1008 +msgid "Unable to show image.

Error message:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1031 +msgid "Unable to save array

Error message:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1056 +msgid "It was not possible to copy this array" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1081 +msgid "Clipboard contents" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1096 +msgid "Import from clipboard" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1098 +msgid "Empty clipboard" +msgstr "" + +#: spyder/widgets/variableexplorer/collectionseditor.py:1099 +msgid "Nothing to be imported from clipboard." +msgstr "" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To bool" +msgstr "" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:450 +msgid "To complex" +msgstr "" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To float" +msgstr "" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:451 +msgid "To int" +msgstr "" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:452 +msgid "To str" +msgstr "" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:526 +msgid "%s editor" +msgstr "" + +#: spyder/widgets/variableexplorer/dataframeeditor.py:558 +msgid "Column min/max" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:116 +#: spyder/widgets/variableexplorer/importwizard.py:431 +msgid "Import as" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:118 +msgid "data" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:122 +msgid "code" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:125 +#: spyder/widgets/variableexplorer/importwizard.py:504 +msgid "text" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:138 +msgid "Column separator:" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:142 +msgid "Tab" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:145 +#: spyder/widgets/variableexplorer/importwizard.py:163 +msgid "other" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:156 +msgid "Row separator:" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:160 +msgid "EOL" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:175 +msgid "Additional options" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:179 +msgid "Skip rows:" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:190 +msgid "Comments:" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:196 +msgid "Transpose" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:434 +msgid "array" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:439 +msgid "list" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:444 +msgid "DataFrame" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:487 +#: spyder/widgets/variableexplorer/importwizard.py:571 +msgid "Import wizard" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:492 +msgid "Raw text" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:495 +msgid "variable_name" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:506 +msgid "table" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:507 +msgid "Preview" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:511 +msgid "Variable Name" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:534 +msgid "Done" +msgstr "" + +#: spyder/widgets/variableexplorer/importwizard.py:572 +msgid "Unable to proceed to next step

Please check your entries.

Error message:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:196 +msgid "Refresh" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:200 +msgid "Refresh periodically" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:207 +#: spyder/widgets/variableexplorer/namespacebrowser.py:487 +msgid "Import data" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:210 +#: spyder/widgets/variableexplorer/namespacebrowser.py:565 +#: spyder/widgets/variableexplorer/namespacebrowser.py:584 +msgid "Save data" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:215 +msgid "Save data as..." +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:227 +msgid "Exclude references which name starts with an underscore" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:235 +msgid "Exclude references which name is uppercase" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:242 +msgid "Exclude references which name starts with an uppercase character" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:250 +msgid "Exclude references to unsupported data types (i.e. which won't be handled/saved correctly)" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:345 +msgid "Object %s is not picklable" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:507 +msgid "Unsupported file extension '%s'

Would you like to import it anyway (by selecting a known file format)?" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:515 +msgid "Open file as:" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:553 +msgid "Unable to load '%s'

Error message:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/namespacebrowser.py:585 +msgid "Unable to save current workspace

Error message:
%s" +msgstr "" + +#: spyder/widgets/variableexplorer/texteditor.py:74 +msgid "Text editor" +msgstr "" + +#: spyder/widgets/variableexplorer/utils.py:29 +msgid "View and edit DataFrames and Series in the Variable Explorer" +msgstr "" + +#: spyder/widgets/variableexplorer/utils.py:34 +msgid "View and edit two and three dimensional arrays in the Variable Explorer" +msgstr "" + +#: spyder/workers/updates.py:89 spyder/workers/updates.py:91 +msgid "Unable to retrieve information." +msgstr "" + +#: spyder/workers/updates.py:93 +msgid "Unable to connect to the internet.

Make sure the connection is working properly." +msgstr "" + +#: spyder/workers/updates.py:96 +msgid "Unable to check for updates." +msgstr "" + diff -Nru spyder-2.3.8+dfsg1/spyder/otherplugins.py spyder-3.0.2+dfsg1/spyder/otherplugins.py --- spyder-2.3.8+dfsg1/spyder/otherplugins.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/otherplugins.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Spyder third-party plugins configuration management +""" + +import os +import os.path as osp +import sys +import traceback + +# Local imports +from spyder.config.base import get_conf_path +from spyder.py3compat import PY2 + +if PY2: + import imp +else: + import importlib + + +USER_PLUGIN_DIR = "plugins" +PLUGIN_PREFIX = "spyder_" +IO_PREFIX = PLUGIN_PREFIX + "io_" + + +def get_spyderplugins_mods(io=False): + """Import modules from plugins package and return the list""" + # Create user directory + user_plugin_path = osp.join(get_conf_path(), USER_PLUGIN_DIR) + if not osp.isdir(user_plugin_path): + os.makedirs(user_plugin_path) + + modlist, modnames = [], [] + + # The user plugins directory is given the priority when looking for modules + for plugin_path in [user_plugin_path] + sys.path: + _get_spyderplugins(plugin_path, io, modnames, modlist) + return modlist + + +def _get_spyderplugins(plugin_path, is_io, modnames, modlist): + """Scan the directory `plugin_path` for plugin packages and loads them.""" + if not osp.isdir(plugin_path): + return + + for name in os.listdir(plugin_path): + if is_io and not name.startswith(IO_PREFIX): + continue + if not name.startswith(PLUGIN_PREFIX) or name.startswith(IO_PREFIX): + continue + + # Import the plugin + _import_plugin(name, plugin_path, modnames, modlist) + + +def _import_plugin(module_name, plugin_path, modnames, modlist): + """Import the plugin `module_name` from `plugin_path`, add it to `modlist` + and adds its name to `modnames`. + """ + if module_name in modnames: + return + try: + # First add a mock module with the LOCALEPATH attribute so that the + # helper method can find the locale on import + mock = _ModuleMock() + mock.LOCALEPATH = osp.join(plugin_path, module_name, 'locale') + sys.modules[module_name] = mock + + if osp.isdir(osp.join(plugin_path, module_name)): + module = _import_module_from_path(module_name, plugin_path) + else: + module = None + + # Then restore the actual loaded module instead of the mock + if module: + sys.modules[module_name] = module + modlist.append(module) + modnames.append(module_name) + except Exception: + sys.stderr.write("ERROR: 3rd party plugin import failed for " + "`{0}`\n".format(module_name)) + traceback.print_exc(file=sys.stderr) + + +def _import_module_from_path(module_name, plugin_path): + """Imports `module_name` from `plugin_path`. + + Return None if no module is found. + """ + module = None + if PY2: + info = imp.find_module(module_name, [plugin_path]) + if info: + module = imp.load_module(module_name, *info) + elif sys.version_info[0:2] <= (3, 3): + loader = importlib.machinery.PathFinder.find_module( + module_name, + [plugin_path]) + if loader: + module = loader.load_module(module_name) + else: # Python 3.4+ + spec = importlib.machinery.PathFinder.find_spec( + module_name, + [plugin_path]) + if spec: + module = spec.loader.load_module(module_name) + return module + + +class _ModuleMock(): + """This mock module is added to sys.modules on plugin load to add the + location of the LOCALEDATA so that the module loads succesfully. + Once loaded the module is replaced by the actual loaded module object. + """ + pass diff -Nru spyder-2.3.8+dfsg1/spyder/pil_patch.py spyder-3.0.2+dfsg1/spyder/pil_patch.py --- spyder-2.3.8+dfsg1/spyder/pil_patch.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/pil_patch.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Patching PIL (Python Imaging Library) to avoid triggering the error: +AccessInit: hash collision: 3 for both 1 and 1 + +This error is occuring because of a bug in the PIL import mechanism. + +How to reproduce this bug in a standard Python interpreter outside Spyder? +By importing PIL by two different mechanisms + +Example on Windows: +=============================================================================== +C:\Python27\Lib\site-packages>python +Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32 +Type "help", "copyright", "credits" or "license" for more information. +>>> import Image +>>> from PIL import Image +AccessInit: hash collision: 3 for both 1 and 1 +=============================================================================== + +Another example on Windows (actually that's the same, but this is the exact +case encountered with Spyder when the global working directory is the +site-packages directory): +=============================================================================== +C:\Python27\Lib\site-packages>python +Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32 +Type "help", "copyright", "credits" or "license" for more information. +>>> import scipy +>>> from pylab import * +AccessInit: hash collision: 3 for both 1 and 1 +=============================================================================== + +The solution to this fix is the following patch: +=============================================================================== +C:\Python27\Lib\site-packages>python +Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win +32 +Type "help", "copyright", "credits" or "license" for more information. +>>> import Image +>>> import PIL +>>> PIL.Image = Image +>>> from PIL import Image +>>> +=============================================================================== +""" + +try: + # For Pillow compatibility + from PIL import Image + import PIL + PIL.Image = Image +except ImportError: + # For PIL + import Image + import PIL + PIL.Image = Image diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/configdialog.py spyder-3.0.2+dfsg1/spyder/plugins/configdialog.py --- spyder-2.3.8+dfsg1/spyder/plugins/configdialog.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/configdialog.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,1463 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Configuration dialog / Preferences. +""" + +# Standard library imports +import os.path as osp + +# Third party imports +from qtpy import API +from qtpy.compat import (getexistingdirectory, getopenfilename, from_qvariant, + to_qvariant) +from qtpy.QtCore import QSize, Qt, Signal, Slot +from qtpy.QtGui import QColor +from qtpy.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QDialog, + QDialogButtonBox, QDoubleSpinBox, QFontComboBox, + QGridLayout, QGroupBox, QHBoxLayout, QLabel, + QLineEdit, QListView, QListWidget, QListWidgetItem, + QMessageBox, QPushButton, QRadioButton, + QScrollArea, QSpinBox, QSplitter, QStackedWidget, + QStyleFactory, QTabWidget, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import (_, LANGUAGE_CODES, load_lang_conf, + running_in_mac_app, save_lang_conf) +from spyder.config.gui import get_font, set_font +from spyder.config.main import CONF +from spyder.config.user import NoDefault +from spyder.config.utils import is_gtk_desktop +from spyder.py3compat import to_text_string, is_text_string, getcwd +from spyder.utils import icon_manager as ima +from spyder.utils import syntaxhighlighters +from spyder.widgets.colors import ColorLayout +from spyder.widgets.sourcecode.codeeditor import CodeEditor + + +class ConfigAccessMixin(object): + """Namespace for methods that access config storage""" + CONF_SECTION = None + + def set_option(self, option, value): + CONF.set(self.CONF_SECTION, option, value) + + def get_option(self, option, default=NoDefault): + return CONF.get(self.CONF_SECTION, option, default) + + +class ConfigPage(QWidget): + """Base class for configuration page in Preferences""" + + # Signals + apply_button_enabled = Signal(bool) + show_this_page = Signal() + + def __init__(self, parent, apply_callback=None): + QWidget.__init__(self, parent) + self.apply_callback = apply_callback + self.is_modified = False + + def initialize(self): + """ + Initialize configuration page: + * setup GUI widgets + * load settings and change widgets accordingly + """ + self.setup_page() + self.load_from_conf() + + def get_name(self): + """Return configuration page name""" + raise NotImplementedError + + def get_icon(self): + """Return configuration page icon (24x24)""" + raise NotImplementedError + + def setup_page(self): + """Setup configuration page widget""" + raise NotImplementedError + + def set_modified(self, state): + self.is_modified = state + self.apply_button_enabled.emit(state) + + def is_valid(self): + """Return True if all widget contents are valid""" + raise NotImplementedError + + def apply_changes(self): + """Apply changes callback""" + if self.is_modified: + self.save_to_conf() + if self.apply_callback is not None: + self.apply_callback() + + # Since the language cannot be retrieved by CONF and the language + # is needed before loading CONF, this is an extra method needed to + # ensure that when changes are applied, they are copied to a + # specific file storing the language value. This only applies to + # the main section config. + if self.CONF_SECTION == u'main': + self._save_lang() + + for restart_option in self.restart_options: + if restart_option in self.changed_options: + self.prompt_restart_required() + break # Ensure a single popup is displayed + self.set_modified(False) + + def load_from_conf(self): + """Load settings from configuration file""" + raise NotImplementedError + + def save_to_conf(self): + """Save settings to configuration file""" + raise NotImplementedError + + +class ConfigDialog(QDialog): + """Spyder configuration ('Preferences') dialog box""" + + # Signals + check_settings = Signal() + size_change = Signal(QSize) + + def __init__(self, parent=None): + QDialog.__init__(self, parent) + + self.main = parent + + # Widgets + self.pages_widget = QStackedWidget() + self.contents_widget = QListWidget() + self.button_reset = QPushButton(_('Reset to defaults')) + + bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply | + QDialogButtonBox.Cancel) + self.apply_btn = bbox.button(QDialogButtonBox.Apply) + + # Widgets setup + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + self.setWindowTitle(_('Preferences')) + self.setWindowIcon(ima.icon('configure')) + self.contents_widget.setMovement(QListView.Static) + self.contents_widget.setSpacing(1) + self.contents_widget.setCurrentRow(0) + + # Layout + hsplitter = QSplitter() + hsplitter.addWidget(self.contents_widget) + hsplitter.addWidget(self.pages_widget) + + btnlayout = QHBoxLayout() + btnlayout.addWidget(self.button_reset) + btnlayout.addStretch(1) + btnlayout.addWidget(bbox) + + vlayout = QVBoxLayout() + vlayout.addWidget(hsplitter) + vlayout.addLayout(btnlayout) + + self.setLayout(vlayout) + + # Signals and slots + if self.main: + self.button_reset.clicked.connect(self.main.reset_spyder) + self.pages_widget.currentChanged.connect(self.current_page_changed) + self.contents_widget.currentRowChanged.connect( + self.pages_widget.setCurrentIndex) + bbox.accepted.connect(self.accept) + bbox.rejected.connect(self.reject) + bbox.clicked.connect(self.button_clicked) + + # Ensures that the config is present on spyder first run + CONF.set('main', 'interface_language', load_lang_conf()) + + def get_current_index(self): + """Return current page index""" + return self.contents_widget.currentRow() + + def set_current_index(self, index): + """Set current page index""" + self.contents_widget.setCurrentRow(index) + + def get_page(self, index=None): + """Return page widget""" + if index is None: + widget = self.pages_widget.currentWidget() + else: + widget = self.pages_widget.widget(index) + return widget.widget() + + @Slot() + def accept(self): + """Reimplement Qt method""" + for index in range(self.pages_widget.count()): + configpage = self.get_page(index) + if not configpage.is_valid(): + return + configpage.apply_changes() + QDialog.accept(self) + + def button_clicked(self, button): + if button is self.apply_btn: + # Apply button was clicked + configpage = self.get_page() + if not configpage.is_valid(): + return + configpage.apply_changes() + + def current_page_changed(self, index): + widget = self.get_page(index) + self.apply_btn.setVisible(widget.apply_callback is not None) + self.apply_btn.setEnabled(widget.is_modified) + + def add_page(self, widget): + self.check_settings.connect(widget.check_settings) + widget.show_this_page.connect(lambda row=self.contents_widget.count(): + self.contents_widget.setCurrentRow(row)) + widget.apply_button_enabled.connect(self.apply_btn.setEnabled) + scrollarea = QScrollArea(self) + scrollarea.setWidgetResizable(True) + scrollarea.setWidget(widget) + self.pages_widget.addWidget(scrollarea) + item = QListWidgetItem(self.contents_widget) + try: + item.setIcon(widget.get_icon()) + except TypeError: + pass + item.setText(widget.get_name()) + item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) + item.setSizeHint(QSize(0, 25)) + + def check_all_settings(self): + """This method is called to check all configuration page settings + after configuration dialog has been shown""" + self.check_settings.emit() + + def resizeEvent(self, event): + """ + Reimplement Qt method to be able to save the widget's size from the + main application + """ + QDialog.resizeEvent(self, event) + self.size_change.emit(self.size()) + + +class SpyderConfigPage(ConfigPage, ConfigAccessMixin): + """Plugin configuration dialog box page widget""" + CONF_SECTION = None + + def __init__(self, parent): + ConfigPage.__init__(self, parent, + apply_callback=lambda: + self.apply_settings(self.changed_options)) + self.checkboxes = {} + self.radiobuttons = {} + self.lineedits = {} + self.validate_data = {} + self.spinboxes = {} + self.comboboxes = {} + self.fontboxes = {} + self.coloredits = {} + self.scedits = {} + self.changed_options = set() + self.restart_options = dict() # Dict to store name and localized text + self.default_button_group = None + + def apply_settings(self, options): + raise NotImplementedError + + def check_settings(self): + """This method is called to check settings after configuration + dialog has been shown""" + pass + + def set_modified(self, state): + ConfigPage.set_modified(self, state) + if not state: + self.changed_options = set() + + def is_valid(self): + """Return True if all widget contents are valid""" + for lineedit in self.lineedits: + if lineedit in self.validate_data and lineedit.isEnabled(): + validator, invalid_msg = self.validate_data[lineedit] + text = to_text_string(lineedit.text()) + if not validator(text): + QMessageBox.critical(self, self.get_name(), + "%s:
%s" % (invalid_msg, text), + QMessageBox.Ok) + return False + return True + + def load_from_conf(self): + """Load settings from configuration file""" + for checkbox, (option, default) in list(self.checkboxes.items()): + checkbox.setChecked(self.get_option(option, default)) + # QAbstractButton works differently for PySide and PyQt + if not API == 'pyside': + checkbox.clicked.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + else: + checkbox.clicked.connect(lambda opt=option: + self.has_been_modified(opt)) + for radiobutton, (option, default) in list(self.radiobuttons.items()): + radiobutton.setChecked(self.get_option(option, default)) + radiobutton.toggled.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + for lineedit, (option, default) in list(self.lineedits.items()): + lineedit.setText(self.get_option(option, default)) + lineedit.textChanged.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + for spinbox, (option, default) in list(self.spinboxes.items()): + spinbox.setValue(self.get_option(option, default)) + spinbox.valueChanged.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + for combobox, (option, default) in list(self.comboboxes.items()): + value = self.get_option(option, default) + for index in range(combobox.count()): + data = from_qvariant(combobox.itemData(index), to_text_string) + # For PyQt API v2, it is necessary to convert `data` to + # unicode in case the original type was not a string, like an + # integer for example (see qtpy.compat.from_qvariant): + if to_text_string(data) == to_text_string(value): + break + combobox.setCurrentIndex(index) + combobox.currentIndexChanged.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + if combobox.restart_required: + self.restart_options[option] = combobox.label_text + + for (fontbox, sizebox), option in list(self.fontboxes.items()): + font = self.get_font(option) + fontbox.setCurrentFont(font) + sizebox.setValue(font.pointSize()) + if option is None: + property = 'plugin_font' + else: + property = option + fontbox.currentIndexChanged.connect(lambda _foo, opt=property: + self.has_been_modified(opt)) + sizebox.valueChanged.connect(lambda _foo, opt=property: + self.has_been_modified(opt)) + for clayout, (option, default) in list(self.coloredits.items()): + property = to_qvariant(option) + edit = clayout.lineedit + btn = clayout.colorbtn + edit.setText(self.get_option(option, default)) + # QAbstractButton works differently for PySide and PyQt + if not API == 'pyside': + btn.clicked.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + else: + btn.clicked.connect(lambda opt=option: + self.has_been_modified(opt)) + edit.textChanged.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + for (clayout, cb_bold, cb_italic + ), (option, default) in list(self.scedits.items()): + edit = clayout.lineedit + btn = clayout.colorbtn + color, bold, italic = self.get_option(option, default) + edit.setText(color) + cb_bold.setChecked(bold) + cb_italic.setChecked(italic) + edit.textChanged.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + # QAbstractButton works differently for PySide and PyQt + if not API == 'pyside': + btn.clicked.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + cb_bold.clicked.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + cb_italic.clicked.connect(lambda _foo, opt=option: + self.has_been_modified(opt)) + else: + btn.clicked.connect(lambda opt=option: + self.has_been_modified(opt)) + cb_bold.clicked.connect(lambda opt=option: + self.has_been_modified(opt)) + cb_italic.clicked.connect(lambda opt=option: + self.has_been_modified(opt)) + + def save_to_conf(self): + """Save settings to configuration file""" + for checkbox, (option, _default) in list(self.checkboxes.items()): + self.set_option(option, checkbox.isChecked()) + for radiobutton, (option, _default) in list(self.radiobuttons.items()): + self.set_option(option, radiobutton.isChecked()) + for lineedit, (option, _default) in list(self.lineedits.items()): + self.set_option(option, to_text_string(lineedit.text())) + for spinbox, (option, _default) in list(self.spinboxes.items()): + self.set_option(option, spinbox.value()) + for combobox, (option, _default) in list(self.comboboxes.items()): + data = combobox.itemData(combobox.currentIndex()) + self.set_option(option, from_qvariant(data, to_text_string)) + for (fontbox, sizebox), option in list(self.fontboxes.items()): + font = fontbox.currentFont() + font.setPointSize(sizebox.value()) + self.set_font(font, option) + for clayout, (option, _default) in list(self.coloredits.items()): + self.set_option(option, to_text_string(clayout.lineedit.text())) + for (clayout, cb_bold, cb_italic), (option, _default) in list(self.scedits.items()): + color = to_text_string(clayout.lineedit.text()) + bold = cb_bold.isChecked() + italic = cb_italic.isChecked() + self.set_option(option, (color, bold, italic)) + + @Slot(str) + def has_been_modified(self, option): + self.set_modified(True) + self.changed_options.add(option) + + def create_checkbox(self, text, option, default=NoDefault, + tip=None, msg_warning=None, msg_info=None, + msg_if_enabled=False): + checkbox = QCheckBox(text) + if tip is not None: + checkbox.setToolTip(tip) + self.checkboxes[checkbox] = (option, default) + if msg_warning is not None or msg_info is not None: + def show_message(is_checked=False): + if is_checked or not msg_if_enabled: + if msg_warning is not None: + QMessageBox.warning(self, self.get_name(), + msg_warning, QMessageBox.Ok) + if msg_info is not None: + QMessageBox.information(self, self.get_name(), + msg_info, QMessageBox.Ok) + checkbox.clicked.connect(show_message) + return checkbox + + def create_radiobutton(self, text, option, default=NoDefault, + tip=None, msg_warning=None, msg_info=None, + msg_if_enabled=False, button_group=None): + radiobutton = QRadioButton(text) + if button_group is None: + if self.default_button_group is None: + self.default_button_group = QButtonGroup(self) + button_group = self.default_button_group + button_group.addButton(radiobutton) + if tip is not None: + radiobutton.setToolTip(tip) + self.radiobuttons[radiobutton] = (option, default) + if msg_warning is not None or msg_info is not None: + def show_message(is_checked): + if is_checked or not msg_if_enabled: + if msg_warning is not None: + QMessageBox.warning(self, self.get_name(), + msg_warning, QMessageBox.Ok) + if msg_info is not None: + QMessageBox.information(self, self.get_name(), + msg_info, QMessageBox.Ok) + radiobutton.toggled.connect(show_message) + return radiobutton + + def create_lineedit(self, text, option, default=NoDefault, + tip=None, alignment=Qt.Vertical): + label = QLabel(text) + label.setWordWrap(True) + edit = QLineEdit() + layout = QVBoxLayout() if alignment == Qt.Vertical else QHBoxLayout() + layout.addWidget(label) + layout.addWidget(edit) + layout.setContentsMargins(0, 0, 0, 0) + if tip: + edit.setToolTip(tip) + self.lineedits[edit] = (option, default) + widget = QWidget(self) + widget.label = label + widget.textbox = edit + widget.setLayout(layout) + return widget + + def create_browsedir(self, text, option, default=NoDefault, tip=None): + widget = self.create_lineedit(text, option, default, + alignment=Qt.Horizontal) + for edit in self.lineedits: + if widget.isAncestorOf(edit): + break + msg = _("Invalid directory path") + self.validate_data[edit] = (osp.isdir, msg) + browse_btn = QPushButton(ima.icon('DirOpenIcon'), '', self) + browse_btn.setToolTip(_("Select directory")) + browse_btn.clicked.connect(lambda: self.select_directory(edit)) + layout = QHBoxLayout() + layout.addWidget(widget) + layout.addWidget(browse_btn) + layout.setContentsMargins(0, 0, 0, 0) + browsedir = QWidget(self) + browsedir.setLayout(layout) + return browsedir + + def select_directory(self, edit): + """Select directory""" + basedir = to_text_string(edit.text()) + if not osp.isdir(basedir): + basedir = getcwd() + title = _("Select directory") + directory = getexistingdirectory(self, title, basedir) + if directory: + edit.setText(directory) + + def create_browsefile(self, text, option, default=NoDefault, tip=None, + filters=None): + widget = self.create_lineedit(text, option, default, + alignment=Qt.Horizontal) + for edit in self.lineedits: + if widget.isAncestorOf(edit): + break + msg = _('Invalid file path') + self.validate_data[edit] = (osp.isfile, msg) + browse_btn = QPushButton(ima.icon('FileIcon'), '', self) + browse_btn.setToolTip(_("Select file")) + browse_btn.clicked.connect(lambda: self.select_file(edit, filters)) + layout = QHBoxLayout() + layout.addWidget(widget) + layout.addWidget(browse_btn) + layout.setContentsMargins(0, 0, 0, 0) + browsedir = QWidget(self) + browsedir.setLayout(layout) + return browsedir + + def select_file(self, edit, filters=None): + """Select File""" + basedir = osp.dirname(to_text_string(edit.text())) + if not osp.isdir(basedir): + basedir = getcwd() + if filters is None: + filters = _("All files (*)") + title = _("Select file") + filename, _selfilter = getopenfilename(self, title, basedir, filters) + if filename: + edit.setText(filename) + + def create_spinbox(self, prefix, suffix, option, default=NoDefault, + min_=None, max_=None, step=None, tip=None): + widget = QWidget(self) + if prefix: + plabel = QLabel(prefix) + widget.plabel = plabel + else: + plabel = None + if suffix: + slabel = QLabel(suffix) + widget.slabel = slabel + else: + slabel = None + if step is not None: + if type(step) is int: + spinbox = QSpinBox() + else: + spinbox = QDoubleSpinBox() + spinbox.setDecimals(1) + spinbox.setSingleStep(step) + else: + spinbox = QSpinBox() + if min_ is not None: + spinbox.setMinimum(min_) + if max_ is not None: + spinbox.setMaximum(max_) + if tip is not None: + spinbox.setToolTip(tip) + self.spinboxes[spinbox] = (option, default) + layout = QHBoxLayout() + for subwidget in (plabel, spinbox, slabel): + if subwidget is not None: + layout.addWidget(subwidget) + layout.addStretch(1) + layout.setContentsMargins(0, 0, 0, 0) + widget.spinbox = spinbox + widget.setLayout(layout) + return widget + + def create_coloredit(self, text, option, default=NoDefault, tip=None, + without_layout=False): + label = QLabel(text) + clayout = ColorLayout(QColor(Qt.black), self) + clayout.lineedit.setMaximumWidth(80) + if tip is not None: + clayout.setToolTip(tip) + self.coloredits[clayout] = (option, default) + if without_layout: + return label, clayout + layout = QHBoxLayout() + layout.addWidget(label) + layout.addLayout(clayout) + layout.addStretch(1) + layout.setContentsMargins(0, 0, 0, 0) + widget = QWidget(self) + widget.setLayout(layout) + return widget + + def create_scedit(self, text, option, default=NoDefault, tip=None, + without_layout=False): + label = QLabel(text) + clayout = ColorLayout(QColor(Qt.black), self) + clayout.lineedit.setMaximumWidth(80) + if tip is not None: + clayout.setToolTip(tip) + cb_bold = QCheckBox() + cb_bold.setIcon(ima.icon('bold')) + cb_bold.setToolTip(_("Bold")) + cb_italic = QCheckBox() + cb_italic.setIcon(ima.icon('italic')) + cb_italic.setToolTip(_("Italic")) + self.scedits[(clayout, cb_bold, cb_italic)] = (option, default) + if without_layout: + return label, clayout, cb_bold, cb_italic + layout = QHBoxLayout() + layout.addWidget(label) + layout.addLayout(clayout) + layout.addSpacing(10) + layout.addWidget(cb_bold) + layout.addWidget(cb_italic) + layout.addStretch(1) + layout.setContentsMargins(0, 0, 0, 0) + widget = QWidget(self) + widget.setLayout(layout) + return widget + + def create_combobox(self, text, choices, option, default=NoDefault, + tip=None, restart=False): + """choices: couples (name, key)""" + label = QLabel(text) + combobox = QComboBox() + if tip is not None: + combobox.setToolTip(tip) + for name, key in choices: + if not (name is None and key is None): + combobox.addItem(name, to_qvariant(key)) + # Insert separators + count = 0 + for index, item in enumerate(choices): + name, key = item + if name is None and key is None: + combobox.insertSeparator(index + count) + count += 1 + self.comboboxes[combobox] = (option, default) + layout = QHBoxLayout() + layout.addWidget(label) + layout.addWidget(combobox) + layout.addStretch(1) + layout.setContentsMargins(0, 0, 0, 0) + widget = QWidget(self) + widget.label = label + widget.combobox = combobox + widget.setLayout(layout) + combobox.restart_required = restart + combobox.label_text = text + return widget + + def create_fontgroup(self, option=None, text=None, title=None, + tip=None, fontfilters=None, without_group=False): + """Option=None -> setting plugin font""" + + if title: + fontlabel = QLabel(title) + else: + fontlabel = QLabel(_("Font: ")) + fontbox = QFontComboBox() + + if fontfilters is not None: + fontbox.setFontFilters(fontfilters) + + sizelabel = QLabel(" "+_("Size: ")) + sizebox = QSpinBox() + sizebox.setRange(7, 100) + self.fontboxes[(fontbox, sizebox)] = option + layout = QHBoxLayout() + + for subwidget in (fontlabel, fontbox, sizelabel, sizebox): + layout.addWidget(subwidget) + layout.addStretch(1) + + widget = QWidget(self) + widget.fontlabel = fontlabel + widget.sizelabel = sizelabel + widget.fontbox = fontbox + widget.sizebox = sizebox + widget.setLayout(layout) + + if not without_group: + if text is None: + text = _("Font style") + + group = QGroupBox(text) + group.setLayout(layout) + + if tip is not None: + group.setToolTip(tip) + + return group + else: + return widget + + def create_button(self, text, callback): + btn = QPushButton(text) + btn.clicked.connect(callback) + btn.clicked.connect(lambda checked=False, opt='': self.has_been_modified(opt)) + return btn + + def create_tab(self, *widgets): + """Create simple tab widget page: widgets added in a vertical layout""" + widget = QWidget() + layout = QVBoxLayout() + for widg in widgets: + layout.addWidget(widg) + layout.addStretch(1) + widget.setLayout(layout) + return widget + + +class PluginConfigPage(SpyderConfigPage): + """Plugin configuration dialog box page widget""" + def __init__(self, plugin, parent): + self.plugin = plugin + self.get_option = plugin.get_option + self.set_option = plugin.set_option + self.get_font = plugin.get_plugin_font + self.apply_settings = plugin.apply_plugin_settings + SpyderConfigPage.__init__(self, parent) + + def get_name(self): + return self.plugin.get_plugin_title() + + def get_icon(self): + return self.plugin.get_plugin_icon() + + +class GeneralConfigPage(SpyderConfigPage): + """Config page that maintains reference to main Spyder window + and allows to specify page name and icon declaratively + """ + CONF_SECTION = None + + NAME = None # configuration page name, e.g. _("General") + ICON = None # name of icon resource (24x24) + + def __init__(self, parent, main): + SpyderConfigPage.__init__(self, parent) + self.main = main + + def get_name(self): + """Configuration page name""" + return self.NAME + + def get_icon(self): + """Loads page icon named by self.ICON""" + return self.ICON + + def apply_settings(self, options): + raise NotImplementedError + + def prompt_restart_required(self): + """Prompt the user with a request to restart.""" + restart_opts = self.restart_options + changed_opts = self.changed_options + options = [restart_opts[o] for o in changed_opts if o in restart_opts] + + if len(options) == 1: + msg_start = _("Spyder needs to restart to change the following " + "setting:") + else: + msg_start = _("Spyder needs to restart to change the following " + "settings:") + msg_end = _("Do you wish to restart now?") + + msg_options = u"" + for option in options: + msg_options += u"
  • {0}
  • ".format(option) + + msg_title = _("Information") + msg = u"{0}
      {1}

    {2}".format(msg_start, msg_options, msg_end) + answer = QMessageBox.information(self, msg_title, msg, + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + self.restart() + + def restart(self): + """Restart Spyder.""" + self.main.restart() + + +class MainConfigPage(GeneralConfigPage): + CONF_SECTION = "main" + NAME = _("General") + + def setup_page(self): + self.ICON = ima.icon('genprefs') + newcb = self.create_checkbox + + # --- Interface + general_group = QGroupBox(_("General")) + languages = LANGUAGE_CODES.items() + language_choices = sorted([(val, key) for key, val in languages]) + language_combo = self.create_combobox(_('Language'), language_choices, + 'interface_language', + restart=True) + single_instance_box = newcb(_("Use a single instance"), + 'single_instance', + tip=_("Set this to open external
    " + "Python files in an already running " + "instance (Requires a restart)")) + prompt_box = newcb(_("Prompt when exiting"), 'prompt_on_exit') + popup_console_box = newcb(_("Pop up internal console when internal " + "errors appear"), + 'show_internal_console_if_traceback') + check_updates = newcb(_("Check for updates on startup"), + 'check_updates_on_startup') + + # Decide if it's possible to activate or not single instance mode + if running_in_mac_app(): + self.set_option("single_instance", True) + single_instance_box.setEnabled(False) + + general_layout = QVBoxLayout() + general_layout.addWidget(language_combo) + general_layout.addWidget(single_instance_box) + general_layout.addWidget(prompt_box) + general_layout.addWidget(popup_console_box) + general_layout.addWidget(check_updates) + general_group.setLayout(general_layout) + + # --- Theme + interface_group = QGroupBox(_("Interface")) + styles = [str(txt) for txt in list(QStyleFactory.keys())] + # Don't offer users the possibility to change to a different + # style in Gtk-based desktops + # Fixes Issue 2036 + if is_gtk_desktop() and ('GTK+' in styles): + styles = ['GTK+'] + choices = list(zip(styles, [style.lower() for style in styles])) + style_combo = self.create_combobox(_('Qt windows style'), choices, + 'windows_style', + default=self.main.default_style) + + themes = ['Spyder 2', 'Spyder 3'] + icon_choices = list(zip(themes, [theme.lower() for theme in themes])) + icons_combo = self.create_combobox(_('Icon theme'), icon_choices, + 'icon_theme', restart=True) + + + vertdock_box = newcb(_("Vertical title bars in panes"), + 'vertical_dockwidget_titlebars') + verttabs_box = newcb(_("Vertical tabs in panes"), + 'vertical_tabs') + animated_box = newcb(_("Animated toolbars and panes"), + 'animated_docks') + tear_off_box = newcb(_("Tear off menus"), 'tear_off_menus', + tip=_("Set this to detach any
    " + "menu from the main window")) + high_dpi_scaling_box = newcb(_("Enable high DPI scaling"), + 'high_dpi_scaling', + tip=_("Set this for high DPI displays")) + margin_box = newcb(_("Custom margin for panes:"), + 'use_custom_margin') + margin_spin = self.create_spinbox("", _("pixels"), 'custom_margin', + 0, 0, 30) + margin_box.toggled.connect(margin_spin.setEnabled) + margin_spin.setEnabled(self.get_option('use_custom_margin')) + margins_layout = QHBoxLayout() + margins_layout.addWidget(margin_box) + margins_layout.addWidget(margin_spin) + + # Layout interface + comboboxes_layout = QHBoxLayout() + cbs_layout = QGridLayout() + cbs_layout.addWidget(style_combo.label, 0, 0) + cbs_layout.addWidget(style_combo.combobox, 0, 1) + cbs_layout.addWidget(icons_combo.label, 1, 0) + cbs_layout.addWidget(icons_combo.combobox, 1, 1) + comboboxes_layout.addLayout(cbs_layout) + comboboxes_layout.addStretch(1) + + interface_layout = QVBoxLayout() + interface_layout.addLayout(comboboxes_layout) + interface_layout.addWidget(vertdock_box) + interface_layout.addWidget(verttabs_box) + interface_layout.addWidget(animated_box) + interface_layout.addWidget(tear_off_box) + interface_layout.addWidget(high_dpi_scaling_box) + interface_layout.addLayout(margins_layout) + interface_group.setLayout(interface_layout) + + # --- Status bar + sbar_group = QGroupBox(_("Status bar")) + show_status_bar = newcb(_("Show status bar"), 'show_status_bar') + + memory_box = newcb(_("Show memory usage every"), 'memory_usage/enable', + tip=self.main.mem_status.toolTip()) + memory_spin = self.create_spinbox("", _(" ms"), 'memory_usage/timeout', + min_=100, max_=1000000, step=100) + memory_box.toggled.connect(memory_spin.setEnabled) + memory_spin.setEnabled(self.get_option('memory_usage/enable')) + memory_box.setEnabled(self.main.mem_status.is_supported()) + memory_spin.setEnabled(self.main.mem_status.is_supported()) + + cpu_box = newcb(_("Show CPU usage every"), 'cpu_usage/enable', + tip=self.main.cpu_status.toolTip()) + cpu_spin = self.create_spinbox("", _(" ms"), 'cpu_usage/timeout', + min_=100, max_=1000000, step=100) + cpu_box.toggled.connect(cpu_spin.setEnabled) + cpu_spin.setEnabled(self.get_option('cpu_usage/enable')) + + cpu_box.setEnabled(self.main.cpu_status.is_supported()) + cpu_spin.setEnabled(self.main.cpu_status.is_supported()) + + status_bar_o = self.get_option('show_status_bar') + show_status_bar.toggled.connect(memory_box.setEnabled) + show_status_bar.toggled.connect(memory_spin.setEnabled) + show_status_bar.toggled.connect(cpu_box.setEnabled) + show_status_bar.toggled.connect(cpu_spin.setEnabled) + memory_box.setEnabled(status_bar_o) + memory_spin.setEnabled(status_bar_o) + cpu_box.setEnabled(status_bar_o) + cpu_spin.setEnabled(status_bar_o) + + # Layout status bar + cpu_memory_layout = QGridLayout() + cpu_memory_layout.addWidget(memory_box, 0, 0) + cpu_memory_layout.addWidget(memory_spin, 0, 1) + cpu_memory_layout.addWidget(cpu_box, 1, 0) + cpu_memory_layout.addWidget(cpu_spin, 1, 1) + + sbar_layout = QVBoxLayout() + sbar_layout.addWidget(show_status_bar) + sbar_layout.addLayout(cpu_memory_layout) + sbar_group.setLayout(sbar_layout) + + # --- Theme and fonts + plain_text_font = self.create_fontgroup( + option='font', + title=_("Plain text font"), + fontfilters=QFontComboBox.MonospacedFonts, + without_group=True) + + rich_text_font = self.create_fontgroup( + option='rich_font', + title=_("Rich text font"), + without_group=True) + + fonts_group = QGroupBox(_("Fonts")) + fonts_layout = QGridLayout() + fonts_layout.addWidget(plain_text_font.fontlabel, 0, 0) + fonts_layout.addWidget(plain_text_font.fontbox, 0, 1) + fonts_layout.addWidget(plain_text_font.sizelabel, 0, 2) + fonts_layout.addWidget(plain_text_font.sizebox, 0, 3) + fonts_layout.addWidget(rich_text_font.fontlabel, 1, 0) + fonts_layout.addWidget(rich_text_font.fontbox, 1, 1) + fonts_layout.addWidget(rich_text_font.sizelabel, 1, 2) + fonts_layout.addWidget(rich_text_font.sizebox, 1, 3) + fonts_group.setLayout(fonts_layout) + + tabs = QTabWidget() + tabs.addTab(self.create_tab(fonts_group, interface_group), + _("Appearance")) + tabs.addTab(self.create_tab(general_group, sbar_group), + _("Advanced Settings")) + + vlayout = QVBoxLayout() + vlayout.addWidget(tabs) + self.setLayout(vlayout) + + def get_font(self, option): + """Return global font used in Spyder.""" + return get_font(option=option) + + def set_font(self, font, option): + """Set global font used in Spyder.""" + # Update fonts in all plugins + set_font(font, option=option) + plugins = self.main.widgetlist + self.main.thirdparty_plugins + for plugin in plugins: + plugin.update_font() + + def apply_settings(self, options): + self.main.apply_settings() + + def _save_lang(self): + """ + Get selected language setting and save to language configuration file. + """ + for combobox, (option, _default) in list(self.comboboxes.items()): + if option == 'interface_language': + data = combobox.itemData(combobox.currentIndex()) + value = from_qvariant(data, to_text_string) + break + save_lang_conf(value) + self.set_option('interface_language', value) + + +class ColorSchemeConfigPage(GeneralConfigPage): + CONF_SECTION = "color_schemes" + NAME = _("Syntax coloring") + + def setup_page(self): + self.ICON = ima.icon('eyedropper') + + names = self.get_option("names") + try: + names.pop(names.index(u'Custom')) + except ValueError: + pass + custom_names = self.get_option("custom_names", []) + + # Widgets + about_label = QLabel(_("Here you can select the color scheme used in " + "the Editor and all other Spyder plugins.

    " + "You can also edit the color schemes provided " + "by Spyder or create your own ones by using " + "the options provided below.
    ")) + edit_button = QPushButton(_("Edit selected")) + create_button = QPushButton(_("Create new scheme")) + self.delete_button = QPushButton(_("Delete")) + self.preview_editor = CodeEditor(self) + self.stacked_widget = QStackedWidget(self) + self.reset_button = QPushButton(_("Reset")) + self.scheme_editor_dialog = SchemeEditor(parent=self, + stack=self.stacked_widget) + + # Widget setup + self.scheme_choices_dict = {} + about_label.setWordWrap(True) + schemes_combobox_widget = self.create_combobox(_('Scheme:'), + [('', '')], + 'selected') + self.schemes_combobox = schemes_combobox_widget.combobox + + # Layouts + vlayout = QVBoxLayout() + + manage_layout = QVBoxLayout() + manage_layout.addWidget(about_label) + + combo_layout = QHBoxLayout() + combo_layout.addWidget(schemes_combobox_widget.label) + combo_layout.addWidget(schemes_combobox_widget.combobox) + + buttons_layout = QVBoxLayout() + buttons_layout.addLayout(combo_layout) + buttons_layout.addWidget(edit_button) + buttons_layout.addWidget(self.reset_button) + buttons_layout.addWidget(self.delete_button) + buttons_layout.addStretch(1) + buttons_layout.addWidget(create_button) + + preview_layout = QVBoxLayout() + preview_layout.addWidget(self.preview_editor) + + buttons_preview_layout = QHBoxLayout() + buttons_preview_layout.addLayout(buttons_layout) + buttons_preview_layout.addLayout(preview_layout) + + manage_layout.addLayout(buttons_preview_layout) + manage_group = QGroupBox(_("Manage color schemes")) + manage_group.setLayout(manage_layout) + + vlayout.addWidget(manage_group) + self.setLayout(vlayout) + + # Signals and slots + create_button.clicked.connect(self.create_new_scheme) + edit_button.clicked.connect(self.edit_scheme) + self.reset_button.clicked.connect(self.reset_to_default) + self.delete_button.clicked.connect(self.delete_scheme) + self.schemes_combobox.currentIndexChanged.connect(self.update_preview) + self.schemes_combobox.currentIndexChanged.connect(self.update_buttons) + + # Setup + for name in names: + self.scheme_editor_dialog.add_color_scheme_stack(name) + + for name in custom_names: + self.scheme_editor_dialog.add_color_scheme_stack(name, custom=True) + + self.update_combobox() + self.update_preview() + + def apply_settings(self, options): + self.set_option('selected', self.current_scheme) + self.main.editor.apply_plugin_settings(['color_scheme_name']) + if self.main.historylog is not None: + self.main.historylog.apply_plugin_settings(['color_scheme_name']) + if self.main.help is not None: + self.main.help.apply_plugin_settings(['color_scheme_name']) + self.update_combobox() + self.update_preview() + + # Helpers + # ------------------------------------------------------------------------- + @property + def current_scheme_name(self): + return self.schemes_combobox.currentText() + + @property + def current_scheme(self): + return self.scheme_choices_dict[self.current_scheme_name] + + @property + def current_scheme_index(self): + return self.schemes_combobox.currentIndex() + + def update_combobox(self): + """Recreates the combobox contents.""" + index = self.current_scheme_index + self.schemes_combobox.blockSignals(True) + names = self.get_option("names") + try: + names.pop(names.index(u'Custom')) + except ValueError: + pass + custom_names = self.get_option("custom_names", []) + + # Useful for retrieving the actual data + for n in names + custom_names: + self.scheme_choices_dict[self.get_option('{0}/name'.format(n))] = n + + if custom_names: + choices = names + [None] + custom_names + else: + choices = names + + combobox = self.schemes_combobox + combobox.clear() + + for name in choices: + if name is None: + continue + combobox.addItem(self.get_option('{0}/name'.format(name)), name) + + if custom_names: + combobox.insertSeparator(len(names)) + + self.schemes_combobox.blockSignals(False) + self.schemes_combobox.setCurrentIndex(index) + + def update_buttons(self): + """Updates the enable status of delete and reset buttons.""" + current_scheme = self.current_scheme + names = self.get_option("names") + try: + names.pop(names.index(u'Custom')) + except ValueError: + pass + delete_enabled = current_scheme not in names + self.delete_button.setEnabled(delete_enabled) + self.reset_button.setEnabled(not delete_enabled) + + def update_preview(self, index=None, scheme_name=None): + """ + Update the color scheme of the preview editor and adds text. + + Note + ---- + 'index' is needed, because this is triggered by a signal that sends + the selected index. + """ + text = ('"""A string"""\n\n' + '# A comment\n\n' + '# %% A cell\n\n' + 'class Foo(object):\n' + ' def __init__(self):\n' + ' bar = 42\n' + ' print(bar)\n' + ) + show_blanks = CONF.get('editor', 'blank_spaces') + if scheme_name is None: + scheme_name = self.current_scheme + self.preview_editor.setup_editor(linenumbers=True, + markers=True, + tab_mode=False, + font=get_font(), + show_blanks=show_blanks, + color_scheme=scheme_name) + self.preview_editor.set_text(text) + self.preview_editor.set_language('Python') + + # Actions + # ------------------------------------------------------------------------- + def create_new_scheme(self): + """Creates a new color scheme with a custom name.""" + names = self.get_option('names') + custom_names = self.get_option('custom_names', []) + + # Get the available number this new color scheme + counter = len(custom_names) - 1 + custom_index = [int(n.split('-')[-1]) for n in custom_names] + for i in range(len(custom_names)): + if custom_index[i] != i: + counter = i - 1 + break + custom_name = "custom-{0}".format(counter+1) + + # Add the config settings, based on the current one. + custom_names.append(custom_name) + self.set_option('custom_names', custom_names) + for key in syntaxhighlighters.COLOR_SCHEME_KEYS: + name = "{0}/{1}".format(custom_name, key) + default_name = "{0}/{1}".format(self.current_scheme, key) + option = self.get_option(default_name) + self.set_option(name, option) + self.set_option('{0}/name'.format(custom_name), custom_name) + + # Now they need to be loaded! how to make a partial load_from_conf? + dlg = self.scheme_editor_dialog + dlg.add_color_scheme_stack(custom_name, custom=True) + dlg.set_scheme(custom_name) + self.load_from_conf() + + if dlg.exec_(): + # This is needed to have the custom name updated on the combobox + name = dlg.get_scheme_name() + self.set_option('{0}/name'.format(custom_name), name) + + # The +1 is needed because of the separator in the combobox + index = (names + custom_names).index(custom_name) + 1 + self.update_combobox() + self.schemes_combobox.setCurrentIndex(index) + else: + # Delete the config .... + custom_names.remove(custom_name) + self.set_option('custom_names', custom_names) + dlg.delete_color_scheme_stack(custom_name) + + def edit_scheme(self): + """Edit current scheme.""" + dlg = self.scheme_editor_dialog + dlg.set_scheme(self.current_scheme) + + if dlg.exec_(): + # Update temp scheme to reflect instant edits on the preview + temporal_color_scheme = dlg.get_edited_color_scheme() + for key in temporal_color_scheme: + option = "temp/{0}".format(key) + value = temporal_color_scheme[key] + self.set_option(option, value) + self.update_preview(scheme_name='temp') + + def delete_scheme(self): + """Deletes the currently selected custom color scheme.""" + scheme_name = self.current_scheme + + answer = QMessageBox.warning(self, _("Warning"), + _("Are you sure you want to delete " + "this scheme?"), + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + # Put the combobox in Spyder by default, when deleting a scheme + names = self.get_option('names') + self.set_scheme('spyder') + self.schemes_combobox.setCurrentIndex(names.index('spyder')) + self.set_option('selected', 'spyder') + + # Delete from custom_names + custom_names = self.get_option('custom_names', []) + if scheme_name in custom_names: + custom_names.remove(scheme_name) + self.set_option('custom_names', custom_names) + + # Delete config options + for key in syntaxhighlighters.COLOR_SCHEME_KEYS: + option = "{0}/{1}".format(scheme_name, key) + CONF.remove_option(self.CONF_SECTION, option) + CONF.remove_option(self.CONF_SECTION, "{0}/name".format(scheme_name)) + + self.update_combobox() + self.update_preview() + + def set_scheme(self, scheme_name): + """ + Set the current stack in the dialog to the scheme with 'scheme_name'. + """ + dlg = self.scheme_editor_dialog + dlg.set_scheme(scheme_name) + + @Slot() + def reset_to_default(self): + """Restore initial values for default color schemes.""" + # Checks that this is indeed a default scheme + scheme = self.current_scheme + names = self.get_option('names') + if scheme in names: + for key in syntaxhighlighters.COLOR_SCHEME_KEYS: + option = "{0}/{1}".format(scheme, key) + value = CONF.get_default(self.CONF_SECTION, option) + self.set_option(option, value) + + self.load_from_conf() + + +class SchemeEditor(QDialog): + """A color scheme editor dialog.""" + def __init__(self, parent=None, stack=None): + super(SchemeEditor, self).__init__(parent) + self.parent = parent + self.stack = stack + self.order = [] # Uses scheme names + + # Needed for self.get_edited_color_scheme() + self.widgets = {} + self.scheme_name_textbox = {} + self.last_edited_color_scheme = None + self.last_used_scheme = None + + # Widgets + bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + + # Layout + layout = QVBoxLayout() + layout.addWidget(self.stack) + layout.addWidget(bbox) + self.setLayout(layout) + + # Signals + bbox.accepted.connect(self.accept) + bbox.accepted.connect(self.get_edited_color_scheme) + bbox.rejected.connect(self.reject) + + # Helpers + # ------------------------------------------------------------------------- + def set_scheme(self, scheme_name): + """Set the current stack by 'scheme_name'.""" + self.stack.setCurrentIndex(self.order.index(scheme_name)) + self.last_used_scheme = scheme_name + + def get_scheme_name(self): + """ + Returns the edited scheme name, needed to update the combobox on + scheme creation. + """ + return self.scheme_name_textbox[self.last_used_scheme].text() + + def get_edited_color_scheme(self): + """ + Get the values of the last edited color scheme to be used in an instant + preview in the preview editor, without using `apply`. + """ + color_scheme = {} + scheme_name = self.last_used_scheme + + for key in self.widgets[scheme_name]: + items = self.widgets[scheme_name][key] + + if len(items) == 1: + # ColorLayout + value = items[0].text() + else: + # ColorLayout + checkboxes + value = (items[0].text(), items[1].isChecked(), + items[2].isChecked()) + + color_scheme[key] = value + + return color_scheme + + # Actions + # ------------------------------------------------------------------------- + def add_color_scheme_stack(self, scheme_name, custom=False): + """Add a stack for a given scheme and connects the CONF values.""" + color_scheme_groups = [ + (_('Text'), ["normal", "comment", "string", "number", "keyword", + "builtin", "definition", "instance", ]), + (_('Highlight'), ["currentcell", "currentline", "occurrence", + "matched_p", "unmatched_p", "ctrlclick"]), + (_('Background'), ["background", "sideareas"]) + ] + + parent = self.parent + line_edit = parent.create_lineedit(_("Scheme name:"), + '{0}/name'.format(scheme_name)) + + self.widgets[scheme_name] = {} + + # Widget setup + line_edit.label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + self.setWindowTitle(_('Color scheme editor')) + + # Layout + name_layout = QHBoxLayout() + name_layout.addWidget(line_edit.label) + name_layout.addWidget(line_edit.textbox) + self.scheme_name_textbox[scheme_name] = line_edit.textbox + + if not custom: + line_edit.textbox.setDisabled(True) + + cs_layout = QVBoxLayout() + cs_layout.addLayout(name_layout) + + h_layout = QHBoxLayout() + v_layout = QVBoxLayout() + + for index, item in enumerate(color_scheme_groups): + group_name, keys = item + group_layout = QGridLayout() + + for row, key in enumerate(keys): + option = "{0}/{1}".format(scheme_name, key) + value = self.parent.get_option(option) + name = syntaxhighlighters.COLOR_SCHEME_KEYS[key] + + if is_text_string(value): + label, clayout = parent.create_coloredit( + name, + option, + without_layout=True, + ) + label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + group_layout.addWidget(label, row+1, 0) + group_layout.addLayout(clayout, row+1, 1) + + # Needed to update temp scheme to obtain instant preview + self.widgets[scheme_name][key] = [clayout] + else: + label, clayout, cb_bold, cb_italic = parent.create_scedit( + name, + option, + without_layout=True, + ) + label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + group_layout.addWidget(label, row+1, 0) + group_layout.addLayout(clayout, row+1, 1) + group_layout.addWidget(cb_bold, row+1, 2) + group_layout.addWidget(cb_italic, row+1, 3) + + # Needed to update temp scheme to obtain instant preview + self.widgets[scheme_name][key] = [clayout, cb_bold, + cb_italic] + + group_box = QGroupBox(group_name) + group_box.setLayout(group_layout) + + if index == 0: + h_layout.addWidget(group_box) + else: + v_layout.addWidget(group_box) + + h_layout.addLayout(v_layout) + cs_layout.addLayout(h_layout) + + stackitem = QWidget() + stackitem.setLayout(cs_layout) + self.stack.addWidget(stackitem) + self.order.append(scheme_name) + + def delete_color_scheme_stack(self, scheme_name): + """Remove stack widget by 'scheme_name'.""" + self.set_scheme(scheme_name) + widget = self.stack.currentWidget() + self.stack.removeWidget(widget) + index = self.order.index(scheme_name) + self.order.pop(index) diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/console.py spyder-3.0.2+dfsg1/spyder/plugins/console.py --- spyder-2.3.8+dfsg1/spyder/plugins/console.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/console.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,344 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Internal Console Plugin""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os +import os.path as osp +import sys + +# Third party imports +from qtpy import PYQT5 +from qtpy.compat import getopenfilename +from qtpy.QtCore import Signal, Slot +from qtpy.QtWidgets import QInputDialog, QLineEdit, QMenu, QVBoxLayout + +# Local imports +from spyder.config.base import _, debug_print +from spyder.config.main import CONF +from spyder.utils import icon_manager as ima +from spyder.utils.environ import EnvDialog +from spyder.utils.misc import get_error_match, remove_backslashes +from spyder.utils.qthelpers import (add_actions, create_action, + DialogManager, mimedata2url) +from spyder.widgets.internalshell import InternalShell +from spyder.widgets.findreplace import FindReplace +from spyder.widgets.variableexplorer.collectionseditor import CollectionsEditor +from spyder.plugins import SpyderPluginWidget +from spyder.py3compat import getcwd, to_text_string + + +class Console(SpyderPluginWidget): + """ + Console widget + """ + CONF_SECTION = 'internal_console' + focus_changed = Signal() + redirect_stdio = Signal(bool) + edit_goto = Signal(str, int, str) + + def __init__(self, parent=None, namespace=None, commands=[], message=None, + exitfunc=None, profile=False, multithreaded=False): + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) + + debug_print(" ..internal console: initializing") + self.dialog_manager = DialogManager() + + # Shell + light_background = self.get_option('light_background') + self.shell = InternalShell(parent, namespace, commands, message, + self.get_option('max_line_count'), + self.get_plugin_font(), exitfunc, profile, + multithreaded, + light_background=light_background) + self.shell.status.connect(lambda msg: self.show_message.emit(msg, 0)) + self.shell.go_to_error.connect(self.go_to_error) + self.shell.focus_changed.connect(lambda: self.focus_changed.emit()) + + # Redirecting some signals: + self.shell.redirect_stdio.connect(lambda state: + self.redirect_stdio.emit(state)) + + # Initialize plugin + self.initialize_plugin() + + # Find/replace widget + self.find_widget = FindReplace(self) + self.find_widget.set_editor(self.shell) + self.find_widget.hide() + self.register_widget_shortcuts(self.find_widget) + + # Main layout + layout = QVBoxLayout() + layout.addWidget(self.shell) + layout.addWidget(self.find_widget) + self.setLayout(layout) + + # Parameters + self.shell.toggle_wrap_mode(self.get_option('wrap')) + + # Accepting drops + self.setAcceptDrops(True) + + #------ Private API -------------------------------------------------------- + def set_historylog(self, historylog): + """Bind historylog instance to this console + Not used anymore since v2.0""" + historylog.add_history(self.shell.history_filename) + self.shell.append_to_history.connect(historylog.append_to_history) + + def set_help(self, help_plugin): + """Bind help instance to this console""" + self.shell.help = help_plugin + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _('Internal console') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.shell + + def update_font(self): + """Update font from Preferences""" + font = self.get_plugin_font() + self.shell.set_font(font) + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + self.dialog_manager.close_all() + self.shell.exit_interpreter() + return True + + def refresh_plugin(self): + pass + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + quit_action = create_action(self, _("&Quit"), + icon=ima.icon('exit'), + tip=_("Quit"), + triggered=self.quit) + self.register_shortcut(quit_action, "_", "Quit", "Ctrl+Q") + run_action = create_action(self, _("&Run..."), None, + ima.icon('run_small'), + _("Run a Python script"), + triggered=self.run_script) + environ_action = create_action(self, + _("Environment variables..."), + icon=ima.icon('environ'), + tip=_("Show and edit environment variables" + " (for current session)"), + triggered=self.show_env) + syspath_action = create_action(self, + _("Show sys.path contents..."), + icon=ima.icon('syspath'), + tip=_("Show (read-only) sys.path"), + triggered=self.show_syspath) + buffer_action = create_action(self, + _("Buffer..."), None, + tip=_("Set maximum line count"), + triggered=self.change_max_line_count) + exteditor_action = create_action(self, + _("External editor path..."), None, None, + _("Set external editor executable path"), + triggered=self.change_exteditor) + wrap_action = create_action(self, + _("Wrap lines"), + toggled=self.toggle_wrap_mode) + wrap_action.setChecked(self.get_option('wrap')) + calltips_action = create_action(self, _("Display balloon tips"), + toggled=self.toggle_calltips) + calltips_action.setChecked(self.get_option('calltips')) + codecompletion_action = create_action(self, + _("Automatic code completion"), + toggled=self.toggle_codecompletion) + codecompletion_action.setChecked(self.get_option('codecompletion/auto')) + codecompenter_action = create_action(self, + _("Enter key selects completion"), + toggled=self.toggle_codecompletion_enter) + codecompenter_action.setChecked(self.get_option( + 'codecompletion/enter_key')) + + option_menu = QMenu(_('Internal console settings'), self) + option_menu.setIcon(ima.icon('tooloptions')) + add_actions(option_menu, (buffer_action, wrap_action, + calltips_action, codecompletion_action, + codecompenter_action, exteditor_action)) + + plugin_actions = [None, run_action, environ_action, syspath_action, + option_menu, None, quit_action] + + # Add actions to context menu + add_actions(self.shell.menu, plugin_actions) + + return plugin_actions + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.focus_changed.connect(self.main.plugin_focus_changed) + self.main.add_dockwidget(self) + # Connecting the following signal once the dockwidget has been created: + self.shell.traceback_available.connect(self.traceback_available) + + def traceback_available(self): + """Traceback is available in the internal console: showing the + internal console automatically to warn the user""" + if CONF.get('main', 'show_internal_console_if_traceback', False): + self.dockwidget.show() + self.dockwidget.raise_() + + #------ Public API --------------------------------------------------------- + @Slot() + def quit(self): + """Quit mainwindow""" + self.main.close() + + @Slot() + def show_env(self): + """Show environment variables""" + self.dialog_manager.show(EnvDialog()) + + @Slot() + def show_syspath(self): + """Show sys.path""" + editor = CollectionsEditor() + editor.setup(sys.path, title="sys.path", readonly=True, + width=600, icon=ima.icon('syspath')) + self.dialog_manager.show(editor) + + @Slot() + def run_script(self, filename=None, silent=False, set_focus=False, + args=None): + """Run a Python script""" + if filename is None: + self.shell.interpreter.restore_stds() + filename, _selfilter = getopenfilename(self, _("Run Python script"), + getcwd(), _("Python scripts")+" (*.py ; *.pyw ; *.ipy)") + self.shell.interpreter.redirect_stds() + if filename: + os.chdir( osp.dirname(filename) ) + filename = osp.basename(filename) + else: + return + debug_print(args) + filename = osp.abspath(filename) + rbs = remove_backslashes + command = "runfile('%s', args='%s')" % (rbs(filename), rbs(args)) + if set_focus: + self.shell.setFocus() + if self.dockwidget and not self.ismaximized: + self.dockwidget.setVisible(True) + self.dockwidget.raise_() + self.shell.write(command+'\n') + self.shell.run_command(command) + + + def go_to_error(self, text): + """Go to error if relevant""" + match = get_error_match(to_text_string(text)) + if match: + fname, lnb = match.groups() + self.edit_script(fname, int(lnb)) + + def edit_script(self, filename=None, goto=-1): + """Edit script""" + # Called from InternalShell + if not hasattr(self, 'main') \ + or not hasattr(self.main, 'editor'): + self.shell.external_editor(filename, goto) + return + if filename is not None: + self.edit_goto.emit(osp.abspath(filename), goto, '') + + def execute_lines(self, lines): + """Execute lines and give focus to shell""" + self.shell.execute_lines(to_text_string(lines)) + self.shell.setFocus() + + @Slot() + def change_max_line_count(self): + "Change maximum line count""" + mlc, valid = QInputDialog.getInt(self, _('Buffer'), + _('Maximum line count'), + self.get_option('max_line_count'), + 0, 1000000) + if valid: + self.shell.setMaximumBlockCount(mlc) + self.set_option('max_line_count', mlc) + + @Slot() + def change_exteditor(self): + """Change external editor path""" + path, valid = QInputDialog.getText(self, _('External editor'), + _('External editor executable path:'), + QLineEdit.Normal, + self.get_option('external_editor/path')) + if valid: + self.set_option('external_editor/path', to_text_string(path)) + + @Slot(bool) + def toggle_wrap_mode(self, checked): + """Toggle wrap mode""" + self.shell.toggle_wrap_mode(checked) + self.set_option('wrap', checked) + + @Slot(bool) + def toggle_calltips(self, checked): + """Toggle calltips""" + self.shell.set_calltips(checked) + self.set_option('calltips', checked) + + @Slot(bool) + def toggle_codecompletion(self, checked): + """Toggle automatic code completion""" + self.shell.set_codecompletion_auto(checked) + self.set_option('codecompletion/auto', checked) + + @Slot(bool) + def toggle_codecompletion_enter(self, checked): + """Toggle Enter key for code completion""" + self.shell.set_codecompletion_enter(checked) + self.set_option('codecompletion/enter_key', checked) + + #----Drag and drop + def dragEnterEvent(self, event): + """Reimplement Qt method + Inform Qt about the types of data that the widget accepts""" + source = event.mimeData() + if source.hasUrls(): + if mimedata2url(source): + event.acceptProposedAction() + else: + event.ignore() + elif source.hasText(): + event.acceptProposedAction() + + def dropEvent(self, event): + """Reimplement Qt method + Unpack dropped data and handle it""" + source = event.mimeData() + if source.hasUrls(): + pathlist = mimedata2url(source) + self.shell.drop_pathlist(pathlist) + elif source.hasText(): + lines = to_text_string(source.text()) + self.shell.set_cursor_position('eof') + self.shell.execute_lines(lines) + event.acceptProposedAction() diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/editor.py spyder-3.0.2+dfsg1/spyder/plugins/editor.py --- spyder-2.3.8+dfsg1/spyder/plugins/editor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/editor.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,2590 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Editor Plugin""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os +import os.path as osp +import re +import time + +# Third party imports +from qtpy import API, PYQT5 +from qtpy.compat import from_qvariant, getopenfilenames, to_qvariant +from qtpy.QtCore import QByteArray, Qt, Signal, Slot +from qtpy.QtGui import QKeySequence +from qtpy.QtPrintSupport import QAbstractPrintDialog, QPrintDialog, QPrinter +from qtpy.QtWidgets import (QAction, QActionGroup, QApplication, QDialog, + QFileDialog, QGridLayout, QGroupBox, QHBoxLayout, + QInputDialog, QLabel, QMenu, QSplitter, QTabWidget, + QToolBar, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import _, get_conf_path +from spyder.config.gui import (RUN_CELL_SHORTCUT, + RUN_CELL_AND_ADVANCE_SHORTCUT) +from spyder.config.main import CONF +from spyder.config.utils import (get_edit_filetypes, get_edit_filters, + get_filter) +from spyder.py3compat import getcwd, PY2, qbytearray_to_str, to_text_string +from spyder.utils import codeanalysis, encoding, programs, sourcecode +from spyder.utils import icon_manager as ima +from spyder.utils.introspection.manager import IntrospectionManager +from spyder.utils.qthelpers import add_actions, create_action +from spyder.widgets.findreplace import FindReplace +from spyder.widgets.editor import (EditorMainWindow, EditorSplitter, + EditorStack, Printer) +from spyder.widgets.sourcecode.codeeditor import CodeEditor +from spyder.widgets.status import (CursorPositionStatus, EncodingStatus, + EOLStatus, ReadWriteStatus) +from spyder.plugins import SpyderPluginWidget +from spyder.plugins.configdialog import PluginConfigPage +from spyder.plugins.runconfig import (ALWAYS_OPEN_FIRST_RUN_OPTION, + get_run_configuration, + RunConfigDialog, RunConfigOneDialog) + + +def _load_all_breakpoints(): + bp_dict = CONF.get('run', 'breakpoints', {}) + for filename in list(bp_dict.keys()): + if not osp.isfile(filename): + bp_dict.pop(filename) + return bp_dict + + +def load_breakpoints(filename): + breakpoints = _load_all_breakpoints().get(filename, []) + if breakpoints and isinstance(breakpoints[0], int): + # Old breakpoints format + breakpoints = [(lineno, None) for lineno in breakpoints] + return breakpoints + + +def save_breakpoints(filename, breakpoints): + if not osp.isfile(filename): + return + bp_dict = _load_all_breakpoints() + bp_dict[filename] = breakpoints + CONF.set('run', 'breakpoints', bp_dict) + + +def clear_all_breakpoints(): + CONF.set('run', 'breakpoints', {}) + + +def clear_breakpoint(filename, lineno): + breakpoints = load_breakpoints(filename) + if breakpoints: + for breakpoint in breakpoints[:]: + if breakpoint[0] == lineno: + breakpoints.remove(breakpoint) + save_breakpoints(filename, breakpoints) + + +WINPDB_PATH = programs.find_program('winpdb') + + +class EditorConfigPage(PluginConfigPage): + def get_name(self): + return _("Editor") + + def get_icon(self): + return ima.icon('edit') + + def setup_page(self): + template_btn = self.create_button(_("Edit template for new modules"), + self.plugin.edit_template) + + interface_group = QGroupBox(_("Interface")) + newcb = self.create_checkbox + fpsorting_box = newcb(_("Sort files according to full path"), + 'fullpath_sorting') + showtabbar_box = newcb(_("Show tab bar"), 'show_tab_bar') + + interface_layout = QVBoxLayout() + interface_layout.addWidget(fpsorting_box) + interface_layout.addWidget(showtabbar_box) + interface_group.setLayout(interface_layout) + + display_group = QGroupBox(_("Source code")) + linenumbers_box = newcb(_("Show line numbers"), 'line_numbers') + blanks_box = newcb(_("Show blank spaces"), 'blank_spaces') + edgeline_box = newcb(_("Show vertical line after"), 'edge_line') + edgeline_spin = self.create_spinbox("", _("characters"), + 'edge_line_column', 79, 1, 500) + edgeline_box.toggled.connect(edgeline_spin.setEnabled) + edgeline_spin.setEnabled(self.get_option('edge_line')) + + currentline_box = newcb(_("Highlight current line"), + 'highlight_current_line') + currentcell_box = newcb(_("Highlight current cell"), + 'highlight_current_cell') + occurrence_box = newcb(_("Highlight occurrences after"), + 'occurrence_highlighting') + occurrence_spin = self.create_spinbox("", _(" ms"), + 'occurrence_highlighting/timeout', + min_=100, max_=1000000, step=100) + occurrence_box.toggled.connect(occurrence_spin.setEnabled) + occurrence_spin.setEnabled(self.get_option('occurrence_highlighting')) + + wrap_mode_box = newcb(_("Wrap lines"), 'wrap') + + display_layout = QGridLayout() + display_layout.addWidget(linenumbers_box, 0, 0) + display_layout.addWidget(blanks_box, 1, 0) + display_layout.addWidget(edgeline_box, 2, 0) + display_layout.addWidget(edgeline_spin.spinbox, 2, 1) + display_layout.addWidget(edgeline_spin.slabel, 2, 2) + display_layout.addWidget(currentline_box, 3, 0) + display_layout.addWidget(currentcell_box, 4, 0) + display_layout.addWidget(occurrence_box, 5, 0) + display_layout.addWidget(occurrence_spin.spinbox, 5, 1) + display_layout.addWidget(occurrence_spin.slabel, 5, 2) + display_layout.addWidget(wrap_mode_box, 6, 0) + display_h_layout = QHBoxLayout() + display_h_layout.addLayout(display_layout) + display_h_layout.addStretch(1) + display_group.setLayout(display_h_layout) + + run_group = QGroupBox(_("Run")) + saveall_box = newcb(_("Save all files before running script"), + 'save_all_before_run') + + run_selection_group = QGroupBox(_("Run selection")) + focus_box = newcb(_("Maintain focus in the Editor after running cells " + "or selections"), 'focus_to_editor') + + introspection_group = QGroupBox(_("Introspection")) + rope_is_installed = programs.is_module_installed('rope') + if rope_is_installed: + completion_box = newcb(_("Automatic code completion"), + 'codecompletion/auto') + case_comp_box = newcb(_("Case sensitive code completion"), + 'codecompletion/case_sensitive') + comp_enter_box = newcb(_("Enter key selects completion"), + 'codecompletion/enter_key') + calltips_box = newcb(_("Display balloon tips"), 'calltips') + gotodef_box = newcb(_("Link to object definition"), + 'go_to_definition', + tip=_("If this option is enabled, clicking on an object\n" + "name (left-click + Ctrl key) will go this object\n" + "definition (if resolved).")) + else: + rope_label = QLabel(_("Warning:
    " + "The Python module rope is not " + "installed on this computer: calltips, " + "code completion and go-to-definition " + "features won't be available.")) + rope_label.setWordWrap(True) + + sourcecode_group = QGroupBox(_("Source code")) + closepar_box = newcb(_("Automatic insertion of parentheses, braces " + "and brackets"), + 'close_parentheses') + close_quotes_box = newcb(_("Automatic insertion of closing quotes"), + 'close_quotes') + add_colons_box = newcb(_("Automatic insertion of colons after 'for', " + "'if', 'def', etc"), + 'add_colons') + autounindent_box = newcb(_("Automatic indentation after 'else', " + "'elif', etc."), 'auto_unindent') + indent_chars_box = self.create_combobox(_("Indentation characters: "), + ((_("2 spaces"), '* *'), + (_("3 spaces"), '* *'), + (_("4 spaces"), '* *'), + (_("5 spaces"), '* *'), + (_("6 spaces"), '* *'), + (_("7 spaces"), '* *'), + (_("8 spaces"), '* *'), + (_("Tabulations"), '*\t*')), 'indent_chars') + tabwidth_spin = self.create_spinbox(_("Tab stop width:"), _("pixels"), + 'tab_stop_width', 40, 10, 1000, 10) + tab_mode_box = newcb(_("Tab always indent"), + 'tab_always_indent', default=False, + tip=_("If enabled, pressing Tab will always indent,\n" + "even when the cursor is not at the beginning\n" + "of a line (when this option is enabled, code\n" + "completion may be triggered using the alternate\n" + "shortcut: Ctrl+Space)")) + ibackspace_box = newcb(_("Intelligent backspace"), + 'intelligent_backspace', default=True) + removetrail_box = newcb(_("Automatically remove trailing spaces " + "when saving files"), + 'always_remove_trailing_spaces', default=False) + + analysis_group = QGroupBox(_("Analysis")) + pep_url = 'PEP8' + pep8_label = QLabel(_("(Refer to the {} page)").format(pep_url)) + pep8_label.setOpenExternalLinks(True) + is_pyflakes = codeanalysis.is_pyflakes_installed() + is_pep8 = codeanalysis.get_checker_executable('pep8') is not None + pyflakes_box = newcb(_("Real-time code analysis"), + 'code_analysis/pyflakes', default=True, + tip=_("

    If enabled, Python source code will be analyzed " + "using pyflakes, lines containing errors or " + "warnings will be highlighted.

    " + "

    Note: add analysis:ignore in " + "a comment to ignore code analysis " + "warnings.

    ")) + pyflakes_box.setEnabled(is_pyflakes) + if not is_pyflakes: + pyflakes_box.setToolTip(_("Code analysis requires pyflakes %s+") % + codeanalysis.PYFLAKES_REQVER) + pep8_box = newcb(_("Real-time code style analysis"), + 'code_analysis/pep8', default=False, + tip=_("

    If enabled, Python source code will be analyzed" + "using pep8, lines that are not following PEP8 " + "style guide will be highlighted.

    " + "

    Note: add analysis:ignore in " + "a comment to ignore style analysis " + "warnings.

    ")) + pep8_box.setEnabled(is_pep8) + todolist_box = newcb(_("Code annotations (TODO, FIXME, XXX, HINT, TIP, @todo)"), + 'todo_list', default=True) + realtime_radio = self.create_radiobutton( + _("Perform analysis when " + "saving file and every"), + 'realtime_analysis', True) + saveonly_radio = self.create_radiobutton( + _("Perform analysis only " + "when saving file"), + 'onsave_analysis') + af_spin = self.create_spinbox("", _(" ms"), 'realtime_analysis/timeout', + min_=100, max_=1000000, step=100) + af_layout = QHBoxLayout() + af_layout.addWidget(realtime_radio) + af_layout.addWidget(af_spin) + + run_layout = QVBoxLayout() + run_layout.addWidget(saveall_box) + run_group.setLayout(run_layout) + + run_selection_layout = QVBoxLayout() + run_selection_layout.addWidget(focus_box) + run_selection_group.setLayout(run_selection_layout) + + introspection_layout = QVBoxLayout() + if rope_is_installed: + introspection_layout.addWidget(calltips_box) + introspection_layout.addWidget(completion_box) + introspection_layout.addWidget(case_comp_box) + introspection_layout.addWidget(comp_enter_box) + introspection_layout.addWidget(gotodef_box) + else: + introspection_layout.addWidget(rope_label) + introspection_group.setLayout(introspection_layout) + + analysis_layout = QVBoxLayout() + analysis_layout.addWidget(pyflakes_box) + analysis_pep_layout = QHBoxLayout() + analysis_pep_layout.addWidget(pep8_box) + analysis_pep_layout.addWidget(pep8_label) + analysis_layout.addLayout(analysis_pep_layout) + analysis_layout.addWidget(todolist_box) + analysis_layout.addLayout(af_layout) + analysis_layout.addWidget(saveonly_radio) + analysis_group.setLayout(analysis_layout) + + sourcecode_layout = QVBoxLayout() + sourcecode_layout.addWidget(closepar_box) + sourcecode_layout.addWidget(autounindent_box) + sourcecode_layout.addWidget(add_colons_box) + sourcecode_layout.addWidget(close_quotes_box) + indent_tab_layout = QHBoxLayout() + indent_tab_grid_layout = QGridLayout() + indent_tab_grid_layout.addWidget(indent_chars_box.label, 0, 0) + indent_tab_grid_layout.addWidget(indent_chars_box.combobox, 0, 1) + indent_tab_grid_layout.addWidget(tabwidth_spin.plabel, 1, 0) + indent_tab_grid_layout.addWidget(tabwidth_spin.spinbox, 1, 1) + indent_tab_grid_layout.addWidget(tabwidth_spin.slabel, 1, 2) + indent_tab_layout.addLayout(indent_tab_grid_layout) + indent_tab_layout.addStretch(1) + sourcecode_layout.addLayout(indent_tab_layout) + sourcecode_layout.addWidget(tab_mode_box) + sourcecode_layout.addWidget(ibackspace_box) + sourcecode_layout.addWidget(removetrail_box) + sourcecode_group.setLayout(sourcecode_layout) + + eol_group = QGroupBox(_("End-of-line characters")) + eol_label = QLabel(_("When opening a text file containing " + "mixed end-of-line characters (this may " + "raise syntax errors in the consoles " + "on Windows platforms), Spyder may fix the " + "file automatically.")) + eol_label.setWordWrap(True) + check_eol_box = newcb(_("Fix automatically and show warning " + "message box"), + 'check_eol_chars', default=True) + + eol_layout = QVBoxLayout() + eol_layout.addWidget(eol_label) + eol_layout.addWidget(check_eol_box) + eol_group.setLayout(eol_layout) + + tabs = QTabWidget() + tabs.addTab(self.create_tab(interface_group, display_group), + _("Display")) + tabs.addTab(self.create_tab(introspection_group, analysis_group), + _("Code Introspection/Analysis")) + tabs.addTab(self.create_tab(template_btn, run_group, run_selection_group, + sourcecode_group, eol_group), + _("Advanced settings")) + + vlayout = QVBoxLayout() + vlayout.addWidget(tabs) + self.setLayout(vlayout) + + +class Editor(SpyderPluginWidget): + """ + Multi-file Editor widget + """ + CONF_SECTION = 'editor' + CONFIGWIDGET_CLASS = EditorConfigPage + TEMPFILE_PATH = get_conf_path('temp.py') + TEMPLATE_PATH = get_conf_path('template.py') + DISABLE_ACTIONS_WHEN_HIDDEN = False # SpyderPluginWidget class attribute + + # Signals + run_in_current_ipyclient = Signal(str, str, str, bool, bool) + exec_in_extconsole = Signal(str, bool) + redirect_stdio = Signal(bool) + open_dir = Signal(str) + breakpoints_saved = Signal() + run_in_current_extconsole = Signal(str, str, str, bool, bool) + + def __init__(self, parent, ignore_last_opened_files=False): + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main=parent) + else: + SpyderPluginWidget.__init__(self, parent) + + self.__set_eol_chars = True + + # Creating template if it doesn't already exist + if not osp.isfile(self.TEMPLATE_PATH): + if os.name == "nt": + shebang = [] + else: + shebang = ['#!/usr/bin/env python' + ('2' if PY2 else '3')] + header = shebang + [ + '# -*- coding: utf-8 -*-', + '"""', 'Created on %(date)s', '', + '@author: %(username)s', '"""', ''] + encoding.write(os.linesep.join(header), self.TEMPLATE_PATH, 'utf-8') + + self.projects = None + 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 + self.edit_menu_actions = None #XXX: find another way to notify Spyder + # (see spyder.py: 'update_edit_menu' method) + self.search_menu_actions = None #XXX: same thing ('update_search_menu') + self.stack_menu_actions = None + + # Initialize plugin + self.initialize_plugin() + + # Configuration dialog size + self.dialog_size = None + + statusbar = self.main.statusBar() + self.readwrite_status = ReadWriteStatus(self, statusbar) + self.eol_status = EOLStatus(self, statusbar) + self.encoding_status = EncodingStatus(self, statusbar) + self.cursorpos_status = CursorPositionStatus(self, statusbar) + + layout = QVBoxLayout() + self.dock_toolbar = QToolBar(self) + add_actions(self.dock_toolbar, self.dock_toolbar_actions) + layout.addWidget(self.dock_toolbar) + + self.last_edit_cursor_pos = None + self.cursor_pos_history = [] + self.cursor_pos_index = None + self.__ignore_cursor_position = True + + self.editorstacks = [] + self.last_focus_editorstack = {} + self.editorwindows = [] + self.editorwindows_to_be_created = [] + self.toolbar_list = None + self.menu_list = None + + self.introspector = IntrospectionManager() + + # Setup new windows: + self.main.all_actions_defined.connect(self.setup_other_windows) + + # Change module completions when PYTHONPATH changes + self.main.sig_pythonpath_changed.connect(self.set_path) + + # Find widget + self.find_widget = FindReplace(self, enable_replace=True) + self.find_widget.hide() + self.find_widget.visibility_changed.connect( + lambda vs: self.rehighlight_cells()) + self.register_widget_shortcuts(self.find_widget) + + # Tabbed editor widget + Find/Replace widget + editor_widgets = QWidget(self) + editor_layout = QVBoxLayout() + editor_layout.setContentsMargins(0, 0, 0, 0) + editor_widgets.setLayout(editor_layout) + self.editorsplitter = EditorSplitter(self, self, + self.stack_menu_actions, first=True) + editor_layout.addWidget(self.editorsplitter) + editor_layout.addWidget(self.find_widget) + + # Splitter: editor widgets (see above) + outline explorer + self.splitter = QSplitter(self) + self.splitter.setContentsMargins(0, 0, 0, 0) + self.splitter.addWidget(editor_widgets) + self.splitter.setStretchFactor(0, 5) + self.splitter.setStretchFactor(1, 1) + layout.addWidget(self.splitter) + self.setLayout(layout) + + # Editor's splitter state + state = self.get_option('splitter_state', None) + if state is not None: + self.splitter.restoreState( QByteArray().fromHex( + str(state).encode('utf-8')) ) + + self.recent_files = self.get_option('recent_files', []) + self.untitled_num = 0 + + # Parameters of last file execution: + self.__last_ic_exec = None # internal console + self.__last_ec_exec = None # external console + + # File types and filters used by the Open dialog + self.edit_filetypes = None + self.edit_filters = None + + self.__ignore_cursor_position = False + current_editor = self.get_current_editor() + if current_editor is not None: + filename = self.get_current_filename() + position = current_editor.get_position('cursor') + self.add_cursor_position_to_history(filename, position) + self.update_cursorpos_actions() + self.set_path() + + def set_projects(self, projects): + self.projects = projects + + @Slot() + def show_hide_projects(self): + if self.projects is not None: + dw = self.projects.dockwidget + if dw.isVisible(): + dw.hide() + else: + dw.show() + dw.raise_() + self.switch_to_plugin() + + def set_outlineexplorer(self, outlineexplorer): + self.outlineexplorer = outlineexplorer + for editorstack in self.editorstacks: + editorstack.set_outlineexplorer(self.outlineexplorer) + self.editorstacks[0].initialize_outlineexplorer() + self.outlineexplorer.edit_goto.connect( + lambda filenames, goto, word: + self.load(filenames=filenames, goto=goto, word=word, + editorwindow=self)) + self.outlineexplorer.edit.connect( + lambda filenames: + self.load(filenames=filenames, editorwindow=self)) + + @Slot() + def show_hide_outline_explorer(self): + if self.outlineexplorer is not None: + dw = self.outlineexplorer.dockwidget + if dw.isVisible(): + dw.hide() + else: + dw.show() + dw.raise_() + self.switch_to_plugin() + + def set_help(self, help_plugin): + self.help = help_plugin + for editorstack in self.editorstacks: + editorstack.set_help(self.help) + + #------ Private API -------------------------------------------------------- + def restore_scrollbar_position(self): + """Restoring scrollbar position after main window is visible""" + # Widget is now visible, we may center cursor on top level editor: + try: + self.get_current_editor().centerCursor() + except AttributeError: + pass + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + title = _('Editor') + filename = self.get_current_filename() + if filename: + title += ' - '+to_text_string(filename) + return title + + def get_plugin_icon(self): + """Return widget icon""" + return ima.icon('edit') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.get_current_editor() + + def visibility_changed(self, enable): + """DockWidget visibility has changed""" + SpyderPluginWidget.visibility_changed(self, enable) + if self.dockwidget.isWindow(): + self.dock_toolbar.show() + else: + self.dock_toolbar.hide() + if enable: + self.refresh_plugin() + + def refresh_plugin(self): + """Refresh editor plugin""" + editorstack = self.get_current_editorstack() + editorstack.refresh() + self.refresh_save_all_action() + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + state = self.splitter.saveState() + self.set_option('splitter_state', qbytearray_to_str(state)) + filenames = [] + editorstack = self.editorstacks[0] + + active_project_path = None + if self.projects is not None: + active_project_path = self.projects.get_active_project_path() + if not active_project_path: + self.set_open_filenames() + else: + self.projects.set_project_filenames( + [finfo.filename for finfo in editorstack.data]) + + self.set_option('layout_settings', + self.editorsplitter.get_layout_settings()) + self.set_option('windows_layout_settings', + [win.get_layout_settings() for win in self.editorwindows]) +# self.set_option('filenames', filenames) + self.set_option('recent_files', self.recent_files) + try: + if not editorstack.save_if_changed(cancelable) and cancelable: + return False + else: + for win in self.editorwindows[:]: + win.close() + return True + except IndexError: + return True + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + self.toggle_outline_action = create_action(self, + _("Show/hide outline explorer"), + triggered=self.show_hide_outline_explorer, + context=Qt.WidgetWithChildrenShortcut) + self.register_shortcut(self.toggle_outline_action, context="Editor", + name="Show/hide outline") + self.toggle_project_action = create_action(self, + _("Show/hide project explorer"), + triggered=self.show_hide_projects, + context=Qt.WidgetWithChildrenShortcut) + self.register_shortcut(self.toggle_project_action, context="Editor", + name="Show/hide project explorer") + self.addActions([self.toggle_outline_action, self.toggle_project_action]) + + # ---- File menu and toolbar ---- + self.new_action = create_action(self, _("&New file..."), + icon=ima.icon('filenew'), tip=_("New file"), + triggered=self.new, + context=Qt.WidgetShortcut) + self.register_shortcut(self.new_action, context="Editor", + name="New file", add_sc_to_tip=True) + + self.open_action = create_action(self, _("&Open..."), + icon=ima.icon('fileopen'), tip=_("Open file"), + triggered=self.load, + context=Qt.WidgetShortcut) + self.register_shortcut(self.open_action, context="Editor", + name="Open file", add_sc_to_tip=True) + + self.file_switcher_action = create_action(self, _('File switcher...'), + icon=ima.icon('filelist'), + tip=_('Fast switch between files'), + triggered=self.call_file_switcher, + context=Qt.ApplicationShortcut) + self.register_shortcut(self.file_switcher_action, context="_", + name="File switcher", add_sc_to_tip=True) + + self.revert_action = create_action(self, _("&Revert"), + icon=ima.icon('revert'), tip=_("Revert file from disk"), + triggered=self.revert) + + self.save_action = create_action(self, _("&Save"), + icon=ima.icon('filesave'), tip=_("Save file"), + triggered=self.save, + context=Qt.WidgetShortcut) + self.register_shortcut(self.save_action, context="Editor", + name="Save file", add_sc_to_tip=True) + + self.save_all_action = create_action(self, _("Sav&e all"), + icon=ima.icon('save_all'), tip=_("Save all files"), + triggered=self.save_all, + context=Qt.WidgetShortcut) + self.register_shortcut(self.save_all_action, context="Editor", + name="Save all", add_sc_to_tip=True) + + save_as_action = create_action(self, _("Save &as..."), None, + ima.icon('filesaveas'), tip=_("Save current file as..."), + triggered=self.save_as, + context=Qt.WidgetShortcut) + self.register_shortcut(save_as_action, "Editor", "Save As") + + print_preview_action = create_action(self, _("Print preview..."), + tip=_("Print preview..."), triggered=self.print_preview) + self.print_action = create_action(self, _("&Print..."), + icon=ima.icon('print'), tip=_("Print current file..."), + triggered=self.print_file) + # Shortcut for close_action is defined in widgets/editor.py + self.close_action = create_action(self, _("&Close"), + icon=ima.icon('fileclose'), tip=_("Close current file"), + triggered=self.close_file) + + self.close_all_action = create_action(self, _("C&lose all"), + icon=ima.icon('filecloseall'), tip=_("Close all opened files"), + triggered=self.close_all_files, + context=Qt.WidgetShortcut) + self.register_shortcut(self.close_all_action, context="Editor", + name="Close all") + + # ---- Find menu and toolbar ---- + _text = _("&Find text") + find_action = create_action(self, _text, icon=ima.icon('find'), + tip=_text, triggered=self.find, + context=Qt.WidgetShortcut) + self.register_shortcut(find_action, context="_", + name="Find text", add_sc_to_tip=True) + find_next_action = create_action(self, _("Find &next"), + icon=ima.icon('findnext'), + triggered=self.find_next, + context=Qt.WidgetShortcut) + self.register_shortcut(find_next_action, context="_", + name="Find next") + find_previous_action = create_action(self, _("Find &previous"), + icon=ima.icon('findprevious'), + triggered=self.find_previous, + context=Qt.WidgetShortcut) + self.register_shortcut(find_previous_action, context="_", + name="Find previous") + _text = _("&Replace text") + replace_action = create_action(self, _text, icon=ima.icon('replace'), + tip=_text, triggered=self.replace, + context=Qt.WidgetShortcut) + self.register_shortcut(replace_action, context="_", + name="Replace text") + + # ---- Debug menu and toolbar ---- + set_clear_breakpoint_action = create_action(self, + _("Set/Clear breakpoint"), + icon=ima.icon('breakpoint_big'), + triggered=self.set_or_clear_breakpoint, + context=Qt.WidgetShortcut) + self.register_shortcut(set_clear_breakpoint_action, context="Editor", + name="Breakpoint") + set_cond_breakpoint_action = create_action(self, + _("Set/Edit conditional breakpoint"), + icon=ima.icon('breakpoint_cond_big'), + triggered=self.set_or_edit_conditional_breakpoint, + context=Qt.WidgetShortcut) + self.register_shortcut(set_cond_breakpoint_action, context="Editor", + name="Conditional breakpoint") + clear_all_breakpoints_action = create_action(self, + _('Clear breakpoints in all files'), + triggered=self.clear_all_breakpoints) + self.winpdb_action = create_action(self, _("Debug with winpdb"), + triggered=self.run_winpdb) + self.winpdb_action.setEnabled(WINPDB_PATH is not None and PY2) + + # --- Debug toolbar --- + debug_action = create_action(self, _("&Debug"), + icon=ima.icon('debug'), + tip=_("Debug file"), + triggered=self.debug_file) + self.register_shortcut(debug_action, context="_", name="Debug", + add_sc_to_tip=True) + + debug_next_action = create_action(self, _("Step"), + icon=ima.icon('arrow-step-over'), tip=_("Run current line"), + triggered=lambda: self.debug_command("next")) + self.register_shortcut(debug_next_action, "_", "Debug Step Over", + add_sc_to_tip=True) + + debug_continue_action = create_action(self, _("Continue"), + icon=ima.icon('arrow-continue'), + tip=_("Continue execution until next breakpoint"), + triggered=lambda: self.debug_command("continue")) + self.register_shortcut(debug_continue_action, "_", "Debug Continue", + add_sc_to_tip=True) + + debug_step_action = create_action(self, _("Step Into"), + icon=ima.icon('arrow-step-in'), + tip=_("Step into function or method of current line"), + triggered=lambda: self.debug_command("step")) + self.register_shortcut(debug_step_action, "_", "Debug Step Into", + add_sc_to_tip=True) + + debug_return_action = create_action(self, _("Step Return"), + icon=ima.icon('arrow-step-out'), + tip=_("Run until current function or method returns"), + triggered=lambda: self.debug_command("return")) + self.register_shortcut(debug_return_action, "_", "Debug Step Return", + add_sc_to_tip=True) + + debug_exit_action = create_action(self, _("Stop"), + icon=ima.icon('stop_debug'), tip=_("Stop debugging"), + triggered=lambda: self.debug_command("exit")) + self.register_shortcut(debug_exit_action, "_", "Debug Exit", + add_sc_to_tip=True) + + # --- Run toolbar --- + run_action = create_action(self, _("&Run"), icon=ima.icon('run'), + tip=_("Run file"), + triggered=self.run_file) + self.register_shortcut(run_action, context="_", name="Run", + add_sc_to_tip=True) + + configure_action = create_action(self, _("&Configure..."), + icon=ima.icon('run_settings'), + tip=_("Run settings"), + menurole=QAction.NoRole, + triggered=self.edit_run_configurations) + self.register_shortcut(configure_action, context="_", + name="Configure", add_sc_to_tip=True) + + re_run_action = create_action(self, _("Re-run &last script"), + icon=ima.icon('run_again'), + tip=_("Run again last file"), + triggered=self.re_run_file) + self.register_shortcut(re_run_action, context="_", + name="Re-run last script", + add_sc_to_tip=True) + + run_selected_action = create_action(self, _("Run &selection or " + "current line"), + icon=ima.icon('run_selection'), + tip=_("Run selection or " + "current line"), + triggered=self.run_selection, + context=Qt.WidgetShortcut) + self.register_shortcut(run_selected_action, context="Editor", + name="Run selection") + + run_cell_action = create_action(self, + _("Run cell"), + icon=ima.icon('run_cell'), + shortcut=QKeySequence(RUN_CELL_SHORTCUT), + tip=_("Run current cell (Ctrl+Enter)\n" + "[Use #%% to create cells]"), + triggered=self.run_cell, + context=Qt.WidgetShortcut) + + run_cell_advance_action = create_action(self, + _("Run cell and advance"), + icon=ima.icon('run_cell_advance'), + shortcut=QKeySequence(RUN_CELL_AND_ADVANCE_SHORTCUT), + tip=_("Run current cell and go to the next one " + "(Shift+Enter)"), + triggered=self.run_cell_and_advance, + context=Qt.WidgetShortcut) + + # --- Source code Toolbar --- + self.todo_list_action = create_action(self, + _("Show todo list"), icon=ima.icon('todo_list'), + tip=_("Show TODO/FIXME/XXX/HINT/TIP/@todo comments list"), + triggered=self.go_to_next_todo) + self.todo_menu = QMenu(self) + self.todo_list_action.setMenu(self.todo_menu) + self.todo_menu.aboutToShow.connect(self.update_todo_menu) + + self.warning_list_action = create_action(self, + _("Show warning/error list"), icon=ima.icon('wng_list'), + tip=_("Show code analysis warnings/errors"), + triggered=self.go_to_next_warning) + self.warning_menu = QMenu(self) + self.warning_list_action.setMenu(self.warning_menu) + self.warning_menu.aboutToShow.connect(self.update_warning_menu) + self.previous_warning_action = create_action(self, + _("Previous warning/error"), icon=ima.icon('prev_wng'), + tip=_("Go to previous code analysis warning/error"), + triggered=self.go_to_previous_warning) + self.next_warning_action = create_action(self, + _("Next warning/error"), icon=ima.icon('next_wng'), + tip=_("Go to next code analysis warning/error"), + triggered=self.go_to_next_warning) + + self.previous_edit_cursor_action = create_action(self, + _("Last edit location"), icon=ima.icon('last_edit_location'), + tip=_("Go to last edit location"), + triggered=self.go_to_last_edit_location, + context=Qt.WidgetShortcut) + self.register_shortcut(self.previous_edit_cursor_action, + context="Editor", + name="Last edit location", + add_sc_to_tip=True) + self.previous_cursor_action = create_action(self, + _("Previous cursor position"), icon=ima.icon('prev_cursor'), + tip=_("Go to previous cursor position"), + triggered=self.go_to_previous_cursor_position, + context=Qt.WidgetShortcut) + self.register_shortcut(self.previous_cursor_action, + context="Editor", + name="Previous cursor position", + add_sc_to_tip=True) + self.next_cursor_action = create_action(self, + _("Next cursor position"), icon=ima.icon('next_cursor'), + tip=_("Go to next cursor position"), + triggered=self.go_to_next_cursor_position, + context=Qt.WidgetShortcut) + self.register_shortcut(self.next_cursor_action, + context="Editor", + name="Next cursor position", + add_sc_to_tip=True) + + # --- Edit Toolbar --- + self.toggle_comment_action = create_action(self, + _("Comment")+"/"+_("Uncomment"), icon=ima.icon('comment'), + tip=_("Comment current line or selection"), + triggered=self.toggle_comment, context=Qt.WidgetShortcut) + self.register_shortcut(self.toggle_comment_action, context="Editor", + name="Toggle comment") + blockcomment_action = create_action(self, _("Add &block comment"), + tip=_("Add block comment around " + "current line or selection"), + triggered=self.blockcomment, context=Qt.WidgetShortcut) + self.register_shortcut(blockcomment_action, context="Editor", + name="Blockcomment") + unblockcomment_action = create_action(self, + _("R&emove block comment"), + tip = _("Remove comment block around " + "current line or selection"), + triggered=self.unblockcomment, context=Qt.WidgetShortcut) + self.register_shortcut(unblockcomment_action, context="Editor", + name="Unblockcomment") + + # ---------------------------------------------------------------------- + # The following action shortcuts are hard-coded in CodeEditor + # keyPressEvent handler (the shortcut is here only to inform user): + # (context=Qt.WidgetShortcut -> disable shortcut for other widgets) + self.indent_action = create_action(self, + _("Indent"), "Tab", icon=ima.icon('indent'), + tip=_("Indent current line or selection"), + triggered=self.indent, context=Qt.WidgetShortcut) + self.unindent_action = create_action(self, + _("Unindent"), "Shift+Tab", icon=ima.icon('unindent'), + tip=_("Unindent current line or selection"), + triggered=self.unindent, context=Qt.WidgetShortcut) + + self.text_uppercase_action = create_action(self, + _("Toggle Uppercase"), "Ctrl+Shift+U", + tip=_("Change to uppercase current line or selection"), + triggered=self.text_uppercase, context=Qt.WidgetShortcut) + + self.text_lowercase_action = create_action(self, + _("Toggle Lowercase"), "Ctrl+U", + tip=_("Change to lowercase current line or selection"), + triggered=self.text_lowercase, context=Qt.WidgetShortcut) + # ---------------------------------------------------------------------- + + self.win_eol_action = create_action(self, + _("Carriage return and line feed (Windows)"), + toggled=lambda: self.toggle_eol_chars('nt')) + self.linux_eol_action = create_action(self, + _("Line feed (UNIX)"), + toggled=lambda: self.toggle_eol_chars('posix')) + self.mac_eol_action = create_action(self, + _("Carriage return (Mac)"), + toggled=lambda: self.toggle_eol_chars('mac')) + eol_action_group = QActionGroup(self) + eol_actions = (self.win_eol_action, self.linux_eol_action, + self.mac_eol_action) + add_actions(eol_action_group, eol_actions) + eol_menu = QMenu(_("Convert end-of-line characters"), self) + add_actions(eol_menu, eol_actions) + + trailingspaces_action = create_action(self, + _("Remove trailing spaces"), + triggered=self.remove_trailing_spaces) + self.showblanks_action = create_action(self, _("Show blank spaces"), + toggled=self.toggle_show_blanks) + fixindentation_action = create_action(self, _("Fix indentation"), + tip=_("Replace tab characters by space characters"), + triggered=self.fix_indentation) + + gotoline_action = create_action(self, _("Go to line..."), + icon=ima.icon('gotoline'), + triggered=self.go_to_line, + context=Qt.WidgetShortcut) + self.register_shortcut(gotoline_action, context="Editor", + name="Go to line") + + workdir_action = create_action(self, + _("Set console working directory"), + icon=ima.icon('DirOpenIcon'), + tip=_("Set current console (and file explorer) working " + "directory to current script directory"), + triggered=self.__set_workdir) + + self.max_recent_action = create_action(self, + _("Maximum number of recent files..."), + triggered=self.change_max_recent_files) + self.clear_recent_action = create_action(self, + _("Clear this list"), tip=_("Clear recent files list"), + triggered=self.clear_recent_files) + + # ---- File menu/toolbar construction ---- + self.recent_file_menu = QMenu(_("Open &recent"), self) + self.recent_file_menu.aboutToShow.connect(self.update_recent_file_menu) + + file_menu_actions = [self.new_action, + None, + self.open_action, + self.recent_file_menu, + None, + None, + self.save_action, + self.save_all_action, + save_as_action, + self.file_switcher_action, + self.revert_action, + None, + print_preview_action, + self.print_action, + None, + self.close_action, + self.close_all_action, + None] + + self.main.file_menu_actions += file_menu_actions + file_toolbar_actions = [self.new_action, self.open_action, + self.save_action, self.save_all_action, + self.file_switcher_action] + self.main.file_toolbar_actions += file_toolbar_actions + + # ---- Find menu/toolbar construction ---- + self.main.search_menu_actions = [find_action, + find_next_action, + find_previous_action, + replace_action] + self.main.search_toolbar_actions = [find_action, + find_next_action, + replace_action] + + # ---- Edit menu/toolbar construction ---- + self.edit_menu_actions = [self.toggle_comment_action, + blockcomment_action, unblockcomment_action, + self.indent_action, self.unindent_action, + self.text_uppercase_action, self.text_lowercase_action] + self.main.edit_menu_actions += [None]+self.edit_menu_actions + edit_toolbar_actions = [self.toggle_comment_action, + self.unindent_action, self.indent_action] + self.main.edit_toolbar_actions += edit_toolbar_actions + + # ---- Search menu/toolbar construction ---- + self.search_menu_actions = [gotoline_action] + self.main.search_menu_actions += self.search_menu_actions + self.main.search_toolbar_actions += [gotoline_action] + + # ---- Run menu/toolbar construction ---- + run_menu_actions = [run_action, run_cell_action, + run_cell_advance_action, None, run_selected_action, + re_run_action, configure_action, None] + self.main.run_menu_actions += run_menu_actions + run_toolbar_actions = [run_action, run_cell_action, + run_cell_advance_action, re_run_action, + configure_action] + self.main.run_toolbar_actions += run_toolbar_actions + + # ---- Debug menu/toolbar construction ---- + # NOTE: 'list_breakpoints' is used by the breakpoints + # plugin to add its "List breakpoints" action to this + # menu + debug_menu_actions = [debug_action, + debug_next_action, + debug_step_action, + debug_return_action, + debug_continue_action, + debug_exit_action, + None, + set_clear_breakpoint_action, + set_cond_breakpoint_action, + clear_all_breakpoints_action, + 'list_breakpoints', + None, + self.winpdb_action] + self.main.debug_menu_actions += debug_menu_actions + debug_toolbar_actions = [debug_action, debug_next_action, + debug_step_action, debug_return_action, + debug_continue_action, debug_exit_action] + self.main.debug_toolbar_actions += debug_toolbar_actions + + # ---- Source menu/toolbar construction ---- + source_menu_actions = [eol_menu, + self.showblanks_action, + trailingspaces_action, + fixindentation_action, + None, + self.todo_list_action, + self.warning_list_action, + self.previous_warning_action, + self.next_warning_action, + None, + self.previous_edit_cursor_action, + self.previous_cursor_action, + self.next_cursor_action] + self.main.source_menu_actions += source_menu_actions + + source_toolbar_actions = [self.todo_list_action, + self.warning_list_action, + self.previous_warning_action, + self.next_warning_action, + None, + self.previous_edit_cursor_action, + self.previous_cursor_action, + self.next_cursor_action] + self.main.source_toolbar_actions += source_toolbar_actions + + # ---- Dock widget and file dependent actions ---- + self.dock_toolbar_actions = file_toolbar_actions + [None] + \ + source_toolbar_actions + [None] + \ + run_toolbar_actions + [None] + \ + debug_toolbar_actions + [None] + \ + edit_toolbar_actions + self.pythonfile_dependent_actions = [run_action, configure_action, + set_clear_breakpoint_action, set_cond_breakpoint_action, + debug_action, run_selected_action, run_cell_action, + run_cell_advance_action, blockcomment_action, + unblockcomment_action, self.winpdb_action] + self.file_dependent_actions = self.pythonfile_dependent_actions + \ + [self.save_action, save_as_action, print_preview_action, + self.print_action, self.save_all_action, gotoline_action, + workdir_action, self.close_action, self.close_all_action, + self.toggle_comment_action, self.revert_action, + self.indent_action, self.unindent_action] + self.stack_menu_actions = [gotoline_action, workdir_action] + + return self.file_dependent_actions + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.main.restore_scrollbar_position.connect( + self.restore_scrollbar_position) + self.main.console.edit_goto.connect(self.load) + self.exec_in_extconsole.connect(self.main.execute_in_external_console) + self.redirect_stdio.connect(self.main.redirect_internalshell_stdio) + self.open_dir.connect(self.main.workingdirectory.chdir) + self.set_help(self.main.help) + if self.main.outlineexplorer is not None: + self.set_outlineexplorer(self.main.outlineexplorer) + editorstack = self.get_current_editorstack() + if not editorstack.data: + self.__load_temp_file() + self.main.add_dockwidget(self) + + def update_font(self): + """Update font from Preferences""" + font = self.get_plugin_font() + color_scheme = self.get_color_scheme() + for editorstack in self.editorstacks: + editorstack.set_default_font(font, color_scheme) + completion_size = CONF.get('main', 'completion/size') + for finfo in editorstack.data: + comp_widget = finfo.editor.completion_widget + comp_widget.setup_appearance(completion_size, font) + + #------ Focus tabwidget + def __get_focus_editorstack(self): + fwidget = QApplication.focusWidget() + if isinstance(fwidget, EditorStack): + return fwidget + else: + for editorstack in self.editorstacks: + if editorstack.isAncestorOf(fwidget): + return editorstack + + def set_last_focus_editorstack(self, editorwindow, editorstack): + self.last_focus_editorstack[editorwindow] = editorstack + self.last_focus_editorstack[None] = editorstack # very last editorstack + + def get_last_focus_editorstack(self, editorwindow=None): + return self.last_focus_editorstack[editorwindow] + + def remove_last_focus_editorstack(self, editorstack): + for editorwindow, widget in list(self.last_focus_editorstack.items()): + if widget is editorstack: + self.last_focus_editorstack[editorwindow] = None + + def save_focus_editorstack(self): + editorstack = self.__get_focus_editorstack() + if editorstack is not None: + for win in [self]+self.editorwindows: + if win.isAncestorOf(editorstack): + self.set_last_focus_editorstack(win, editorstack) + + #------ Handling editorstacks + def register_editorstack(self, editorstack): + self.editorstacks.append(editorstack) + self.register_widget_shortcuts(editorstack) + + if self.isAncestorOf(editorstack): + # editorstack is a child of the Editor plugin + self.set_last_focus_editorstack(self, editorstack) + editorstack.set_closable( len(self.editorstacks) > 1 ) + if self.outlineexplorer is not None: + editorstack.set_outlineexplorer(self.outlineexplorer) + editorstack.set_find_widget(self.find_widget) + editorstack.reset_statusbar.connect(self.readwrite_status.hide) + editorstack.reset_statusbar.connect(self.encoding_status.hide) + editorstack.reset_statusbar.connect(self.cursorpos_status.hide) + editorstack.readonly_changed.connect( + self.readwrite_status.readonly_changed) + editorstack.encoding_changed.connect( + self.encoding_status.encoding_changed) + editorstack.sig_editor_cursor_position_changed.connect( + self.cursorpos_status.cursor_position_changed) + editorstack.refresh_eol_chars.connect(self.eol_status.eol_changed) + + editorstack.set_help(self.help) + editorstack.set_io_actions(self.new_action, self.open_action, + self.save_action, self.revert_action) + editorstack.set_tempfile_path(self.TEMPFILE_PATH) + editorstack.set_introspector(self.introspector) + + settings = ( + ('set_pyflakes_enabled', 'code_analysis/pyflakes'), + ('set_pep8_enabled', 'code_analysis/pep8'), + ('set_todolist_enabled', 'todo_list'), + ('set_realtime_analysis_enabled', 'realtime_analysis'), + ('set_realtime_analysis_timeout', 'realtime_analysis/timeout'), + ('set_blanks_enabled', 'blank_spaces'), + ('set_linenumbers_enabled', 'line_numbers'), + ('set_edgeline_enabled', 'edge_line'), + ('set_edgeline_column', 'edge_line_column'), + ('set_codecompletion_auto_enabled', 'codecompletion/auto'), + ('set_codecompletion_case_enabled', 'codecompletion/case_sensitive'), + ('set_codecompletion_enter_enabled', 'codecompletion/enter_key'), + ('set_calltips_enabled', 'calltips'), + ('set_go_to_definition_enabled', 'go_to_definition'), + ('set_focus_to_editor', 'focus_to_editor'), + ('set_close_parentheses_enabled', 'close_parentheses'), + ('set_close_quotes_enabled', 'close_quotes'), + ('set_add_colons_enabled', 'add_colons'), + ('set_auto_unindent_enabled', 'auto_unindent'), + ('set_indent_chars', 'indent_chars'), + ('set_tab_stop_width', 'tab_stop_width'), + ('set_wrap_enabled', 'wrap'), + ('set_tabmode_enabled', 'tab_always_indent'), + ('set_intelligent_backspace_enabled', 'intelligent_backspace'), + ('set_highlight_current_line_enabled', 'highlight_current_line'), + ('set_highlight_current_cell_enabled', 'highlight_current_cell'), + ('set_occurrence_highlighting_enabled', 'occurrence_highlighting'), + ('set_occurrence_highlighting_timeout', 'occurrence_highlighting/timeout'), + ('set_checkeolchars_enabled', 'check_eol_chars'), + ('set_fullpath_sorting_enabled', 'fullpath_sorting'), + ('set_tabbar_visible', 'show_tab_bar'), + ('set_always_remove_trailing_spaces', 'always_remove_trailing_spaces'), + ) + for method, setting in settings: + getattr(editorstack, method)(self.get_option(setting)) + editorstack.set_help_enabled(CONF.get('help', 'connect/editor')) + color_scheme = self.get_color_scheme() + editorstack.set_default_font(self.get_plugin_font(), color_scheme) + + editorstack.starting_long_process.connect(self.starting_long_process) + editorstack.ending_long_process.connect(self.ending_long_process) + + # Redirect signals + editorstack.redirect_stdio.connect( + lambda state: self.redirect_stdio.emit(state)) + editorstack.exec_in_extconsole.connect( + lambda text, option: + self.exec_in_extconsole.emit(text, option)) + editorstack.update_plugin_title.connect( + lambda: self.update_plugin_title.emit()) + editorstack.editor_focus_changed.connect(self.save_focus_editorstack) + editorstack.editor_focus_changed.connect(self.set_editorstack_for_introspection) + editorstack.editor_focus_changed.connect(self.main.plugin_focus_changed) + editorstack.zoom_in.connect(lambda: self.zoom(1)) + editorstack.zoom_out.connect(lambda: self.zoom(-1)) + editorstack.zoom_reset.connect(lambda: self.zoom(0)) + editorstack.sig_new_file.connect(lambda s: self.new(text=s)) + editorstack.sig_new_file[()].connect(self.new) + editorstack.sig_close_file.connect(self.close_file_in_all_editorstacks) + editorstack.file_saved.connect(self.file_saved_in_editorstack) + editorstack.file_renamed_in_data.connect( + self.file_renamed_in_data_in_editorstack) + editorstack.create_new_window.connect(self.create_new_window) + editorstack.opened_files_list_changed.connect( + self.opened_files_list_changed) + editorstack.analysis_results_changed.connect( + self.analysis_results_changed) + editorstack.todo_results_changed.connect(self.todo_results_changed) + editorstack.update_code_analysis_actions.connect( + self.update_code_analysis_actions) + editorstack.update_code_analysis_actions.connect( + self.update_todo_actions) + editorstack.refresh_file_dependent_actions.connect( + self.refresh_file_dependent_actions) + editorstack.refresh_save_all_action.connect(self.refresh_save_all_action) + editorstack.refresh_eol_chars.connect(self.refresh_eol_chars) + editorstack.save_breakpoints.connect(self.save_breakpoints) + editorstack.text_changed_at.connect(self.text_changed_at) + editorstack.current_file_changed.connect(self.current_file_changed) + editorstack.plugin_load.connect(self.load) + editorstack.plugin_load[()].connect(self.load) + editorstack.edit_goto.connect(self.load) + editorstack.sig_save_as.connect(self.save_as) + editorstack.sig_prev_edit_pos.connect(self.go_to_last_edit_location) + editorstack.sig_prev_cursor.connect(self.go_to_previous_cursor_position) + editorstack.sig_next_cursor.connect(self.go_to_next_cursor_position) + + def unregister_editorstack(self, editorstack): + """Removing editorstack only if it's not the last remaining""" + self.remove_last_focus_editorstack(editorstack) + if len(self.editorstacks) > 1: + index = self.editorstacks.index(editorstack) + self.editorstacks.pop(index) + return True + else: + # editorstack was not removed! + return False + + def clone_editorstack(self, editorstack): + editorstack.clone_from(self.editorstacks[0]) + for finfo in editorstack.data: + self.register_widget_shortcuts(finfo.editor) + + @Slot(str, int) + def close_file_in_all_editorstacks(self, editorstack_id_str, index): + for editorstack in self.editorstacks: + if str(id(editorstack)) != editorstack_id_str: + editorstack.blockSignals(True) + editorstack.close_file(index, force=True) + editorstack.blockSignals(False) + + @Slot(str, int, str) + def file_saved_in_editorstack(self, editorstack_id_str, index, filename): + """A file was saved in editorstack, this notifies others""" + for editorstack in self.editorstacks: + if str(id(editorstack)) != editorstack_id_str: + editorstack.file_saved_in_other_editorstack(index, filename) + + @Slot(str, int, str) + def file_renamed_in_data_in_editorstack(self, editorstack_id_str, + index, filename): + """A file was renamed in data in editorstack, this notifies others""" + for editorstack in self.editorstacks: + if str(id(editorstack)) != editorstack_id_str: + editorstack.rename_in_data(index, filename) + + def set_editorstack_for_introspection(self): + """ + Set the current editorstack to be used by the IntrospectionManager + instance + """ + editorstack = self.__get_focus_editorstack() + if editorstack is not None: + self.introspector.set_editor_widget(editorstack) + + # Disconnect active signals + try: + self.introspector.send_to_help.disconnect() + self.introspector.edit_goto.disconnect() + except TypeError: + pass + + # Reconnect signals again + self.introspector.send_to_help.connect(editorstack.send_to_help) + self.introspector.edit_goto.connect( + lambda fname, lineno, name: + editorstack.edit_goto.emit(fname, lineno, name)) + + #------ Handling editor windows + def setup_other_windows(self): + """Setup toolbars and menus for 'New window' instances""" + self.toolbar_list = ( + (_("File toolbar"), self.main.file_toolbar_actions), + (_("Search toolbar"), self.main.search_menu_actions), + (_("Source toolbar"), self.main.source_toolbar_actions), + (_("Run toolbar"), self.main.run_toolbar_actions), + (_("Debug toolbar"), self.main.debug_toolbar_actions), + (_("Edit toolbar"), self.main.edit_toolbar_actions), + ) + self.menu_list = ( + (_("&File"), self.main.file_menu_actions), + (_("&Edit"), self.main.edit_menu_actions), + (_("&Search"), self.main.search_menu_actions), + (_("Sour&ce"), self.main.source_menu_actions), + (_("&Run"), self.main.run_menu_actions), + (_("&Tools"), self.main.tools_menu_actions), + (_("?"), self.main.help_menu_actions), + ) + # Create pending new windows: + for layout_settings in self.editorwindows_to_be_created: + win = self.create_new_window() + win.set_layout_settings(layout_settings) + + def create_new_window(self): + oe_options = self.outlineexplorer.get_options() + fullpath_sorting=self.get_option('fullpath_sorting', True), + window = EditorMainWindow(self, self.stack_menu_actions, + self.toolbar_list, self.menu_list, + show_fullpath=oe_options['show_fullpath'], + fullpath_sorting=fullpath_sorting, + show_all_files=oe_options['show_all_files'], + show_comments=oe_options['show_comments']) + window.resize(self.size()) + window.show() + self.register_editorwindow(window) + window.destroyed.connect(lambda: self.unregister_editorwindow(window)) + return window + + def register_editorwindow(self, window): + self.editorwindows.append(window) + + def unregister_editorwindow(self, window): + self.editorwindows.pop(self.editorwindows.index(window)) + + + #------ Accessors + def get_filenames(self): + return [finfo.filename for finfo in self.editorstacks[0].data] + + def get_filename_index(self, filename): + return self.editorstacks[0].has_filename(filename) + + def get_current_editorstack(self, editorwindow=None): + if self.editorstacks is not None: + if len(self.editorstacks) == 1: + return self.editorstacks[0] + else: + editorstack = self.__get_focus_editorstack() + if editorstack is None or editorwindow is not None: + return self.get_last_focus_editorstack(editorwindow) + return editorstack + + def get_current_editor(self): + editorstack = self.get_current_editorstack() + if editorstack is not None: + return editorstack.get_current_editor() + + def get_current_finfo(self): + editorstack = self.get_current_editorstack() + if editorstack is not None: + return editorstack.get_current_finfo() + + def get_current_filename(self): + editorstack = self.get_current_editorstack() + if editorstack is not None: + return editorstack.get_current_filename() + + def is_file_opened(self, filename=None): + return self.editorstacks[0].is_file_opened(filename) + + def set_current_filename(self, filename, editorwindow=None): + """Set focus to *filename* if this file has been opened + Return the editor instance associated to *filename*""" + editorstack = self.get_current_editorstack(editorwindow) + return editorstack.set_current_filename(filename) + + def set_path(self): + for finfo in self.editorstacks[0].data: + finfo.path = self.main.get_spyder_pythonpath() + + #------ Refresh methods + def refresh_file_dependent_actions(self): + """Enable/disable file dependent actions + (only if dockwidget is visible)""" + if self.dockwidget and self.dockwidget.isVisible(): + enable = self.get_current_editor() is not None + for action in self.file_dependent_actions: + action.setEnabled(enable) + + def refresh_save_all_action(self): + """Enable 'Save All' if there are files to be saved""" + editorstack = self.get_current_editorstack() + state = any(finfo.editor.document().isModified() + for finfo in editorstack.data) + self.save_all_action.setEnabled(state) + + def update_warning_menu(self): + """Update warning list menu""" + editorstack = self.get_current_editorstack() + check_results = editorstack.get_analysis_results() + self.warning_menu.clear() + filename = self.get_current_filename() + for message, line_number in check_results: + error = 'syntax' in message + text = message[:1].upper()+message[1:] + icon = ima.icon('error') if error else ima.icon('warning') + # QAction.triggered works differently for PySide and PyQt + if not API == 'pyside': + slot = lambda _checked, _l=line_number: self.load(filename, goto=_l) + else: + slot = lambda _l=line_number: self.load(filename, goto=_l) + action = create_action(self, text=text, icon=icon, triggered=slot) + self.warning_menu.addAction(action) + + def analysis_results_changed(self): + """ + Synchronize analysis results between editorstacks + Refresh analysis navigation buttons + """ + editorstack = self.get_current_editorstack() + results = editorstack.get_analysis_results() + index = editorstack.get_stack_index() + if index != -1: + for other_editorstack in self.editorstacks: + if other_editorstack is not editorstack: + other_editorstack.set_analysis_results(index, results) + self.update_code_analysis_actions() + + def update_todo_menu(self): + """Update todo list menu""" + editorstack = self.get_current_editorstack() + results = editorstack.get_todo_results() + self.todo_menu.clear() + filename = self.get_current_filename() + for text, line0 in results: + icon = ima.icon('todo') + # QAction.triggered works differently for PySide and PyQt + if not API == 'pyside': + slot = lambda _checked, _l=line0: self.load(filename, goto=_l) + else: + slot = lambda _l=line0: self.load(filename, goto=_l) + action = create_action(self, text=text, icon=icon, triggered=slot) + self.todo_menu.addAction(action) + self.update_todo_actions() + + def todo_results_changed(self): + """ + Synchronize todo results between editorstacks + Refresh todo list navigation buttons + """ + editorstack = self.get_current_editorstack() + results = editorstack.get_todo_results() + index = editorstack.get_stack_index() + if index != -1: + for other_editorstack in self.editorstacks: + if other_editorstack is not editorstack: + other_editorstack.set_todo_results(index, results) + self.update_todo_actions() + + def refresh_eol_chars(self, os_name): + os_name = to_text_string(os_name) + self.__set_eol_chars = False + if os_name == 'nt': + self.win_eol_action.setChecked(True) + elif os_name == 'posix': + self.linux_eol_action.setChecked(True) + else: + self.mac_eol_action.setChecked(True) + self.__set_eol_chars = True + + + #------ Slots + def opened_files_list_changed(self): + """ + Opened files list has changed: + --> open/close file action + --> modification ('*' added to title) + --> current edited file has changed + """ + # Refresh Python file dependent actions: + editor = self.get_current_editor() + if editor: + enable = editor.is_python() + for action in self.pythonfile_dependent_actions: + if action is self.winpdb_action: + action.setEnabled(enable and WINPDB_PATH is not None) + else: + action.setEnabled(enable) + + def update_code_analysis_actions(self): + editorstack = self.get_current_editorstack() + results = editorstack.get_analysis_results() + + # Update code analysis buttons + state = (self.get_option('code_analysis/pyflakes') \ + or self.get_option('code_analysis/pep8')) \ + and results is not None and len(results) + for action in (self.warning_list_action, self.previous_warning_action, + self.next_warning_action): + action.setEnabled(state) + + def update_todo_actions(self): + editorstack = self.get_current_editorstack() + results = editorstack.get_todo_results() + state = self.get_option('todo_list') \ + and results is not None and len(results) + self.todo_list_action.setEnabled(state) + + def rehighlight_cells(self): + """Rehighlight cells of current editor""" + editor = self.get_current_editor() + editor.rehighlight_cells() + QApplication.processEvents() + + + #------ Breakpoints + def save_breakpoints(self, filename, breakpoints): + filename = to_text_string(filename) + breakpoints = to_text_string(breakpoints) + filename = osp.normpath(osp.abspath(filename)) + if breakpoints: + breakpoints = eval(breakpoints) + else: + breakpoints = [] + save_breakpoints(filename, breakpoints) + self.breakpoints_saved.emit() + + #------ File I/O + def __load_temp_file(self): + """Load temporary file from a text file in user home directory""" + if not osp.isfile(self.TEMPFILE_PATH): + # Creating temporary file + default = ['# -*- coding: utf-8 -*-', + '"""', _("Spyder Editor"), '', + _("This is a temporary script file."), + '"""', '', ''] + text = os.linesep.join([encoding.to_unicode(qstr) + for qstr in default]) + encoding.write(to_text_string(text), self.TEMPFILE_PATH, 'utf-8') + self.load(self.TEMPFILE_PATH) + + @Slot() + def __set_workdir(self): + """Set current script directory as working directory""" + fname = self.get_current_filename() + if fname is not None: + directory = osp.dirname(osp.abspath(fname)) + self.open_dir.emit(directory) + + def __add_recent_file(self, fname): + """Add to recent file list""" + if fname is None: + return + if fname in self.recent_files: + self.recent_files.remove(fname) + self.recent_files.insert(0, fname) + if len(self.recent_files) > self.get_option('max_recent_files'): + self.recent_files.pop(-1) + + def _clone_file_everywhere(self, finfo): + """Clone file (*src_editor* widget) in all editorstacks + Cloning from the first editorstack in which every single new editor + is created (when loading or creating a new file)""" + for editorstack in self.editorstacks[1:]: + editor = editorstack.clone_editor_from(finfo, set_current=False) + self.register_widget_shortcuts(editor) + + @Slot() + @Slot(str) + def new(self, fname=None, editorstack=None, text=None): + """ + Create a new file - Untitled + + fname=None --> fname will be 'untitledXX.py' but do not create file + fname= --> create file + """ + # If no text is provided, create default content + if text is None: + default_content = True + text, enc = encoding.read(self.TEMPLATE_PATH) + enc_match = re.search('-*- coding: ?([a-z0-9A-Z\-]*) -*-', text) + if enc_match: + enc = enc_match.group(1) + # Initialize template variables + # Windows + username = encoding.to_unicode_from_fs(os.environ.get('USERNAME', + '')) + # Linux, Mac OS X + if not username: + username = encoding.to_unicode_from_fs(os.environ.get('USER', + '-')) + VARS = { + 'date': time.ctime(), + 'username': username, + } + try: + text = text % VARS + except: + pass + else: + default_content = False + enc = encoding.read(self.TEMPLATE_PATH)[1] + + create_fname = lambda n: to_text_string(_("untitled")) + ("%d.py" % n) + # Creating editor widget + if editorstack is None: + current_es = self.get_current_editorstack() + else: + current_es = editorstack + created_from_here = fname is None + if created_from_here: + while True: + fname = create_fname(self.untitled_num) + self.untitled_num += 1 + if not osp.isfile(fname): + break + basedir = getcwd() + if CONF.get('workingdir', 'editor/new/browse_scriptdir'): + c_fname = self.get_current_filename() + if c_fname is not None and c_fname != self.TEMPFILE_PATH: + basedir = osp.dirname(c_fname) + fname = osp.abspath(osp.join(basedir, fname)) + else: + # QString when triggered by a Qt signal + fname = osp.abspath(to_text_string(fname)) + index = current_es.has_filename(fname) + if index and not current_es.close_file(index): + return + + # Creating the editor widget in the first editorstack (the one that + # can't be destroyed), then cloning this editor widget in all other + # editorstacks: + finfo = self.editorstacks[0].new(fname, enc, text, default_content) + finfo.path = self.main.get_spyder_pythonpath() + self._clone_file_everywhere(finfo) + current_editor = current_es.set_current_filename(finfo.filename) + self.register_widget_shortcuts(current_editor) + if not created_from_here: + self.save(force=True) + + def edit_template(self): + """Edit new file template""" + self.load(self.TEMPLATE_PATH) + + @Slot() + def call_file_switcher(self): + if self.editorstacks: + self.get_current_editorstack().open_fileswitcher_dlg() + + def update_recent_file_menu(self): + """Update recent file menu""" + recent_files = [] + for fname in self.recent_files: + if not self.is_file_opened(fname) and osp.isfile(fname): + recent_files.append(fname) + self.recent_file_menu.clear() + if recent_files: + for fname in recent_files: + action = create_action(self, fname, + icon=ima.icon('FileIcon'), + triggered=self.load) + action.setData(to_qvariant(fname)) + self.recent_file_menu.addAction(action) + self.clear_recent_action.setEnabled(len(recent_files) > 0) + add_actions(self.recent_file_menu, (None, self.max_recent_action, + self.clear_recent_action)) + + @Slot() + def clear_recent_files(self): + """Clear recent files list""" + self.recent_files = [] + + @Slot() + def change_max_recent_files(self): + "Change max recent files entries""" + editorstack = self.get_current_editorstack() + mrf, valid = QInputDialog.getInt(editorstack, _('Editor'), + _('Maximum number of recent files'), + self.get_option('max_recent_files'), 1, 35) + if valid: + self.set_option('max_recent_files', mrf) + + @Slot() + @Slot(str) + @Slot(str, int, str) + @Slot(str, int, str, object) + def load(self, filenames=None, goto=None, word='', editorwindow=None, + processevents=True): + """ + Load a text file + editorwindow: load in this editorwindow (useful when clicking on + outline explorer with multiple editor windows) + processevents: determines if processEvents() should be called at the + end of this method (set to False to prevent keyboard events from + creeping through to the editor during debugging) + """ + editor0 = self.get_current_editor() + if editor0 is not None: + position0 = editor0.get_position('cursor') + filename0 = self.get_current_filename() + else: + position0, filename0 = None, None + if not filenames: + # Recent files action + action = self.sender() + if isinstance(action, QAction): + filenames = from_qvariant(action.data(), to_text_string) + if not filenames: + basedir = getcwd() + if self.edit_filetypes is None: + self.edit_filetypes = get_edit_filetypes() + if self.edit_filters is None: + self.edit_filters = get_edit_filters() + if CONF.get('workingdir', 'editor/open/browse_scriptdir'): + c_fname = self.get_current_filename() + if c_fname is not None and c_fname != self.TEMPFILE_PATH: + basedir = osp.dirname(c_fname) + self.redirect_stdio.emit(False) + parent_widget = self.get_current_editorstack() + if filename0 is not None: + selectedfilter = get_filter(self.edit_filetypes, + osp.splitext(filename0)[1]) + else: + selectedfilter = '' + filenames, _sf = getopenfilenames(parent_widget, + _("Open file"), basedir, + self.edit_filters, + selectedfilter=selectedfilter, + options=QFileDialog.HideNameFilterDetails) + self.redirect_stdio.emit(True) + if filenames: + filenames = [osp.normpath(fname) for fname in filenames] + if CONF.get('workingdir', 'editor/open/auto_set_to_basedir'): + directory = osp.dirname(filenames[0]) + self.open_dir.emit(directory) + else: + return + + focus_widget = QApplication.focusWidget() + if self.dockwidget and not self.ismaximized and\ + (not self.dockwidget.isAncestorOf(focus_widget)\ + and not isinstance(focus_widget, CodeEditor)): + self.dockwidget.setVisible(True) + self.dockwidget.setFocus() + self.dockwidget.raise_() + + def _convert(fname): + fname = osp.abspath(encoding.to_unicode_from_fs(fname)) + if os.name == 'nt' and len(fname) >= 2 and fname[1] == ':': + fname = fname[0].upper()+fname[1:] + return fname + + if hasattr(filenames, 'replaceInStrings'): + # This is a QStringList instance (PyQt API #1), converting to list: + filenames = list(filenames) + if not isinstance(filenames, list): + filenames = [_convert(filenames)] + else: + filenames = [_convert(fname) for fname in list(filenames)] + if isinstance(goto, int): + goto = [goto] + elif goto is not None and len(goto) != len(filenames): + goto = None + + for index, filename in enumerate(filenames): + # -- Do not open an already opened file + current_editor = self.set_current_filename(filename, editorwindow) + if current_editor is None: + # -- Not a valid filename: + if not osp.isfile(filename): + continue + # -- + current_es = self.get_current_editorstack(editorwindow) + + # Creating the editor widget in the first editorstack (the one + # that can't be destroyed), then cloning this editor widget in + # all other editorstacks: + finfo = self.editorstacks[0].load(filename, set_current=False) + finfo.path = self.main.get_spyder_pythonpath() + self._clone_file_everywhere(finfo) + current_editor = current_es.set_current_filename(filename) + current_editor.set_breakpoints(load_breakpoints(filename)) + self.register_widget_shortcuts(current_editor) + + current_es.analyze_script() + self.__add_recent_file(filename) + if goto is not None: # 'word' is assumed to be None as well + current_editor.go_to_line(goto[index], word=word) + position = current_editor.get_position('cursor') + self.cursor_moved(filename0, position0, filename, position) + current_editor.clearFocus() + current_editor.setFocus() + current_editor.window().raise_() + if processevents: + QApplication.processEvents() + + @Slot() + def print_file(self): + """Print current file""" + editor = self.get_current_editor() + filename = self.get_current_filename() + printer = Printer(mode=QPrinter.HighResolution, + header_font=self.get_plugin_font('printer_header')) + printDialog = QPrintDialog(printer, editor) + if editor.has_selected_text(): + printDialog.addEnabledOption(QAbstractPrintDialog.PrintSelection) + self.redirect_stdio.emit(False) + answer = printDialog.exec_() + self.redirect_stdio.emit(True) + if answer == QDialog.Accepted: + self.starting_long_process(_("Printing...")) + printer.setDocName(filename) + editor.print_(printer) + self.ending_long_process() + + @Slot() + def print_preview(self): + """Print preview for current file""" + from qtpy.QtPrintSupport import QPrintPreviewDialog + + editor = self.get_current_editor() + printer = Printer(mode=QPrinter.HighResolution, + header_font=self.get_plugin_font('printer_header')) + preview = QPrintPreviewDialog(printer, self) + preview.setWindowFlags(Qt.Window) + preview.paintRequested.connect(lambda printer: editor.print_(printer)) + self.redirect_stdio.emit(False) + preview.exec_() + self.redirect_stdio.emit(True) + + @Slot() + def close_file(self): + """Close current file""" + editorstack = self.get_current_editorstack() + editorstack.close_file() + + @Slot() + def close_all_files(self): + """Close all opened scripts""" + self.editorstacks[0].close_all_files() + + @Slot() + def save(self, index=None, force=False): + """Save file""" + editorstack = self.get_current_editorstack() + return editorstack.save(index=index, force=force) + + @Slot() + def save_as(self): + """Save *as* the currently edited file""" + editorstack = self.get_current_editorstack() + if editorstack.save_as(): + fname = editorstack.get_current_filename() + if CONF.get('workingdir', 'editor/save/auto_set_to_basedir'): + self.open_dir.emit(osp.dirname(fname)) + self.__add_recent_file(fname) + + @Slot() + def save_all(self): + """Save all opened files""" + self.get_current_editorstack().save_all() + + @Slot() + def revert(self): + """Revert the currently edited file from disk""" + editorstack = self.get_current_editorstack() + editorstack.revert() + + @Slot() + def find(self): + """Find slot""" + editorstack = self.get_current_editorstack() + editorstack.find_widget.show() + editorstack.find_widget.search_text.setFocus() + + @Slot() + def find_next(self): + """Fnd next slot""" + editorstack = self.get_current_editorstack() + editorstack.find_widget.find_next() + + @Slot() + def find_previous(self): + """Find previous slot""" + editorstack = self.get_current_editorstack() + editorstack.find_widget.find_previous() + + @Slot() + def replace(self): + """Replace slot""" + editorstack = self.get_current_editorstack() + editorstack.find_widget.show_replace() + + #------ Explorer widget + def close_file_from_name(self, filename): + """Close file from its name""" + filename = osp.abspath(to_text_string(filename)) + index = self.editorstacks[0].has_filename(filename) + if index is not None: + self.editorstacks[0].close_file(index) + + def removed(self, filename): + """File was removed in file explorer widget or in project explorer""" + self.close_file_from_name(filename) + + def removed_tree(self, dirname): + """Directory was removed in project explorer widget""" + dirname = osp.abspath(to_text_string(dirname)) + for fname in self.get_filenames(): + if osp.abspath(fname).startswith(dirname): + self.close_file_from_name(fname) + + def renamed(self, source, dest): + """File was renamed in file explorer widget or in project explorer""" + filename = osp.abspath(to_text_string(source)) + index = self.editorstacks[0].has_filename(filename) + if index is not None: + for editorstack in self.editorstacks: + editorstack.rename_in_data(index, + new_filename=to_text_string(dest)) + + + #------ Source code + @Slot() + def indent(self): + """Indent current line or selection""" + editor = self.get_current_editor() + if editor is not None: + editor.indent() + + @Slot() + def unindent(self): + """Unindent current line or selection""" + editor = self.get_current_editor() + if editor is not None: + editor.unindent() + + @Slot() + def text_uppercase (self): + """Change current line or selection to uppercase.""" + editor = self.get_current_editor() + if editor is not None: + editor.transform_to_uppercase() + + @Slot() + def text_lowercase(self): + """Change current line or selection to lowercase.""" + editor = self.get_current_editor() + if editor is not None: + editor.transform_to_lowercase() + + @Slot() + def toggle_comment(self): + """Comment current line or selection""" + editor = self.get_current_editor() + if editor is not None: + editor.toggle_comment() + + @Slot() + def blockcomment(self): + """Block comment current line or selection""" + editor = self.get_current_editor() + if editor is not None: + editor.blockcomment() + + @Slot() + def unblockcomment(self): + """Un-block comment current line or selection""" + editor = self.get_current_editor() + if editor is not None: + editor.unblockcomment() + @Slot() + def go_to_next_todo(self): + editor = self.get_current_editor() + position = editor.go_to_next_todo() + filename = self.get_current_filename() + self.add_cursor_position_to_history(filename, position) + + @Slot() + def go_to_next_warning(self): + editor = self.get_current_editor() + position = editor.go_to_next_warning() + filename = self.get_current_filename() + self.add_cursor_position_to_history(filename, position) + + @Slot() + def go_to_previous_warning(self): + editor = self.get_current_editor() + position = editor.go_to_previous_warning() + filename = self.get_current_filename() + self.add_cursor_position_to_history(filename, position) + + @Slot() + def run_winpdb(self): + """Run winpdb to debug current file""" + if self.save(): + fname = self.get_current_filename() + runconf = get_run_configuration(fname) + if runconf is None: + args = [] + wdir = None + else: + args = runconf.get_arguments().split() + wdir = runconf.get_working_directory() + # Handle the case where wdir comes back as an empty string + # when the working directory dialog checkbox is unchecked. + # (subprocess "cwd" default is None, so empty str + # must be changed to None in this case.) + programs.run_program(WINPDB_PATH, [fname] + args, cwd=wdir or None) + + def toggle_eol_chars(self, os_name): + editor = self.get_current_editor() + if self.__set_eol_chars: + editor.set_eol_chars(sourcecode.get_eol_chars_from_os_name(os_name)) + + @Slot(bool) + def toggle_show_blanks(self, checked): + editor = self.get_current_editor() + editor.set_blanks_enabled(checked) + + @Slot() + def remove_trailing_spaces(self): + editorstack = self.get_current_editorstack() + editorstack.remove_trailing_spaces() + + @Slot() + def fix_indentation(self): + editorstack = self.get_current_editorstack() + editorstack.fix_indentation() + + #------ Cursor position history management + def update_cursorpos_actions(self): + self.previous_edit_cursor_action.setEnabled( + self.last_edit_cursor_pos is not None) + self.previous_cursor_action.setEnabled( + self.cursor_pos_index is not None and self.cursor_pos_index > 0) + self.next_cursor_action.setEnabled(self.cursor_pos_index is not None \ + and self.cursor_pos_index < len(self.cursor_pos_history)-1) + + def add_cursor_position_to_history(self, filename, position, fc=False): + if self.__ignore_cursor_position: + return + for index, (fname, pos) in enumerate(self.cursor_pos_history[:]): + if fname == filename: + if pos == position or pos == 0: + if fc: + self.cursor_pos_history[index] = (filename, position) + self.cursor_pos_index = index + self.update_cursorpos_actions() + return + else: + if self.cursor_pos_index >= index: + self.cursor_pos_index -= 1 + self.cursor_pos_history.pop(index) + break + if self.cursor_pos_index is not None: + self.cursor_pos_history = \ + self.cursor_pos_history[:self.cursor_pos_index+1] + self.cursor_pos_history.append((filename, position)) + self.cursor_pos_index = len(self.cursor_pos_history)-1 + self.update_cursorpos_actions() + + def cursor_moved(self, filename0, position0, filename1, position1): + """Cursor was just moved: 'go to'""" + if position0 is not None: + self.add_cursor_position_to_history(filename0, position0) + self.add_cursor_position_to_history(filename1, position1) + + def text_changed_at(self, filename, position): + self.last_edit_cursor_pos = (to_text_string(filename), position) + + def current_file_changed(self, filename, position): + self.add_cursor_position_to_history(to_text_string(filename), position, + fc=True) + + @Slot() + def go_to_last_edit_location(self): + if self.last_edit_cursor_pos is not None: + filename, position = self.last_edit_cursor_pos + if not osp.isfile(filename): + self.last_edit_cursor_pos = None + return + else: + self.load(filename) + editor = self.get_current_editor() + if position < editor.document().characterCount(): + editor.set_cursor_position(position) + + def __move_cursor_position(self, index_move): + if self.cursor_pos_index is None: + return + filename, _position = self.cursor_pos_history[self.cursor_pos_index] + self.cursor_pos_history[self.cursor_pos_index] = ( filename, + self.get_current_editor().get_position('cursor') ) + self.__ignore_cursor_position = True + old_index = self.cursor_pos_index + self.cursor_pos_index = min([ + len(self.cursor_pos_history)-1, + max([0, self.cursor_pos_index+index_move]) + ]) + filename, position = self.cursor_pos_history[self.cursor_pos_index] + if not osp.isfile(filename): + self.cursor_pos_history.pop(self.cursor_pos_index) + if self.cursor_pos_index < old_index: + old_index -= 1 + self.cursor_pos_index = old_index + else: + self.load(filename) + editor = self.get_current_editor() + if position < editor.document().characterCount(): + editor.set_cursor_position(position) + self.__ignore_cursor_position = False + self.update_cursorpos_actions() + + @Slot() + def go_to_previous_cursor_position(self): + self.__move_cursor_position(-1) + + @Slot() + def go_to_next_cursor_position(self): + self.__move_cursor_position(1) + + @Slot() + def go_to_line(self): + """Open 'go to line' dialog""" + editorstack = self.get_current_editorstack() + if editorstack is not None: + editorstack.go_to_line() + + @Slot() + def set_or_clear_breakpoint(self): + """Set/Clear breakpoint""" + editorstack = self.get_current_editorstack() + if editorstack is not None: + editorstack.set_or_clear_breakpoint() + + @Slot() + def set_or_edit_conditional_breakpoint(self): + """Set/Edit conditional breakpoint""" + editorstack = self.get_current_editorstack() + if editorstack is not None: + editorstack.set_or_edit_conditional_breakpoint() + + @Slot() + def clear_all_breakpoints(self): + """Clear breakpoints in all files""" + clear_all_breakpoints() + self.breakpoints_saved.emit() + editorstack = self.get_current_editorstack() + if editorstack is not None: + for data in editorstack.data: + data.editor.clear_breakpoints() + self.refresh_plugin() + + def clear_breakpoint(self, filename, lineno): + """Remove a single breakpoint""" + clear_breakpoint(filename, lineno) + self.breakpoints_saved.emit() + editorstack = self.get_current_editorstack() + if editorstack is not None: + index = self.is_file_opened(filename) + if index is not None: + editorstack.data[index].editor.add_remove_breakpoint(lineno) + + def debug_command(self, command): + """Debug actions""" + if self.main.ipyconsole is not None: + if self.main.last_console_plugin_focus_was_python: + self.main.extconsole.execute_code(command) + else: + self.main.ipyconsole.write_to_stdin(command) + focus_widget = self.main.ipyconsole.get_focus_widget() + if focus_widget: + focus_widget.setFocus() + else: + self.main.extconsole.execute_code(command) + + #------ Run Python script + @Slot() + def edit_run_configurations(self): + dialog = RunConfigDialog(self) + dialog.size_change.connect(lambda s: self.set_dialog_size(s)) + if self.dialog_size is not None: + dialog.resize(self.dialog_size) + fname = osp.abspath(self.get_current_filename()) + dialog.setup(fname) + if dialog.exec_(): + fname = dialog.file_to_run + if fname is not None: + self.load(fname) + self.run_file() + + @Slot() + def run_file(self, debug=False): + """Run script inside current interpreter or in a new one""" + editorstack = self.get_current_editorstack() + if editorstack.save(): + editor = self.get_current_editor() + fname = osp.abspath(self.get_current_filename()) + + # Escape single and double quotes in fname (Fixes Issue 2158) + fname = fname.replace("'", r"\'") + fname = fname.replace('"', r'\"') + + runconf = get_run_configuration(fname) + if runconf is None: + dialog = RunConfigOneDialog(self) + dialog.size_change.connect(lambda s: self.set_dialog_size(s)) + if self.dialog_size is not None: + dialog.resize(self.dialog_size) + dialog.setup(fname) + if CONF.get('run', 'open_at_least_once', True): + # Open Run Config dialog at least once: the first time + # a script is ever run in Spyder, so that the user may + # see it at least once and be conscious that it exists + show_dlg = True + CONF.set('run', 'open_at_least_once', False) + else: + # Open Run Config dialog only + # if ALWAYS_OPEN_FIRST_RUN_OPTION option is enabled + show_dlg = CONF.get('run', ALWAYS_OPEN_FIRST_RUN_OPTION) + if show_dlg and not dialog.exec_(): + return + runconf = dialog.get_configuration() + + wdir = runconf.get_working_directory() + args = runconf.get_arguments() + python_args = runconf.get_python_arguments() + interact = runconf.interact + post_mortem = runconf.post_mortem + current = runconf.current + systerm = runconf.systerm + + python = True # Note: in the future, it may be useful to run + # something in a terminal instead of a Python interp. + self.__last_ec_exec = (fname, wdir, args, interact, debug, + python, python_args, current, systerm, post_mortem) + self.re_run_file() + if not interact and not debug: + # If external console dockwidget is hidden, it will be + # raised in top-level and so focus will be given to the + # current external shell automatically + # (see SpyderPluginWidget.visibility_changed method) + editor.setFocus() + + def set_dialog_size(self, size): + self.dialog_size = size + + @Slot() + def debug_file(self): + """Debug current script""" + self.run_file(debug=True) + # Fixes 2034 + # FIXME: Stop doing this for now because it breaks debugging + # for IPython consoles + #editor = self.get_current_editor() + #if editor.get_breakpoints(): + # time.sleep(0.5) + # self.debug_command('continue') + + @Slot() + def re_run_file(self): + """Re-run last script""" + if self.get_option('save_all_before_run'): + self.save_all() + if self.__last_ec_exec is None: + return + (fname, wdir, args, interact, debug, + python, python_args, current, systerm, post_mortem) = self.__last_ec_exec + if current: + if self.main.ipyconsole is not None: + if self.main.last_console_plugin_focus_was_python: + self.run_in_current_extconsole.emit(fname, wdir, args, + debug, post_mortem) + else: + self.run_in_current_ipyclient.emit(fname, wdir, args, + debug, post_mortem) + else: + self.run_in_current_extconsole.emit(fname, wdir, args, debug, + post_mortem) + else: + self.main.open_external_console(fname, wdir, args, interact, + debug, python, python_args, + systerm, post_mortem) + + @Slot() + def run_selection(self): + """Run selection or current line in external console""" + editorstack = self.get_current_editorstack() + editorstack.run_selection() + + @Slot() + def run_cell(self): + """Run current cell""" + editorstack = self.get_current_editorstack() + editorstack.run_cell() + + @Slot() + def run_cell_and_advance(self): + """Run current cell and advance to the next one""" + editorstack = self.get_current_editorstack() + editorstack.run_cell_and_advance() + + #------ Zoom in/out/reset + def zoom(self, factor): + """Zoom in/out/reset""" + editor = self.get_current_editorstack().get_current_editor() + if factor == 0: + font = self.get_plugin_font() + editor.set_font(font) + else: + font = editor.font() + size = font.pointSize() + factor + if size > 0: + font.setPointSize(size) + editor.set_font(font) + + #------ Options + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + # toggle_fullpath_sorting + if self.editorstacks is not None: + # --- syntax highlight and text rendering settings + color_scheme_n = 'color_scheme_name' + color_scheme_o = self.get_color_scheme() + currentline_n = 'highlight_current_line' + currentline_o = self.get_option(currentline_n) + currentcell_n = 'highlight_current_cell' + currentcell_o = self.get_option(currentcell_n) + occurrence_n = 'occurrence_highlighting' + occurrence_o = self.get_option(occurrence_n) + occurrence_timeout_n = 'occurrence_highlighting/timeout' + occurrence_timeout_o = self.get_option(occurrence_timeout_n) + focus_to_editor_n = 'focus_to_editor' + focus_to_editor_o = self.get_option(focus_to_editor_n) + + for editorstack in self.editorstacks: + if color_scheme_n in options: + editorstack.set_color_scheme(color_scheme_o) + if currentline_n in options: + editorstack.set_highlight_current_line_enabled( + currentline_o) + if currentcell_n in options: + editorstack.set_highlight_current_cell_enabled( + currentcell_o) + if occurrence_n in options: + editorstack.set_occurrence_highlighting_enabled(occurrence_o) + if occurrence_timeout_n in options: + editorstack.set_occurrence_highlighting_timeout( + occurrence_timeout_o) + if focus_to_editor_n in options: + editorstack.set_focus_to_editor(focus_to_editor_o) + + # --- everything else + fpsorting_n = 'fullpath_sorting' + fpsorting_o = self.get_option(fpsorting_n) + tabbar_n = 'show_tab_bar' + tabbar_o = self.get_option(tabbar_n) + linenb_n = 'line_numbers' + linenb_o = self.get_option(linenb_n) + blanks_n = 'blank_spaces' + blanks_o = self.get_option(blanks_n) + edgeline_n = 'edge_line' + edgeline_o = self.get_option(edgeline_n) + edgelinecol_n = 'edge_line_column' + edgelinecol_o = self.get_option(edgelinecol_n) + wrap_n = 'wrap' + wrap_o = self.get_option(wrap_n) + tabindent_n = 'tab_always_indent' + tabindent_o = self.get_option(tabindent_n) + ibackspace_n = 'intelligent_backspace' + ibackspace_o = self.get_option(ibackspace_n) + removetrail_n = 'always_remove_trailing_spaces' + removetrail_o = self.get_option(removetrail_n) + autocomp_n = 'codecompletion/auto' + autocomp_o = self.get_option(autocomp_n) + case_comp_n = 'codecompletion/case_sensitive' + case_comp_o = self.get_option(case_comp_n) + enter_key_n = 'codecompletion/enter_key' + enter_key_o = self.get_option(enter_key_n) + calltips_n = 'calltips' + calltips_o = self.get_option(calltips_n) + gotodef_n = 'go_to_definition' + gotodef_o = self.get_option(gotodef_n) + closepar_n = 'close_parentheses' + closepar_o = self.get_option(closepar_n) + close_quotes_n = 'close_quotes' + close_quotes_o = self.get_option(close_quotes_n) + add_colons_n = 'add_colons' + add_colons_o = self.get_option(add_colons_n) + autounindent_n = 'auto_unindent' + autounindent_o = self.get_option(autounindent_n) + indent_chars_n = 'indent_chars' + indent_chars_o = self.get_option(indent_chars_n) + tab_stop_width_n = 'tab_stop_width' + tab_stop_width_o = self.get_option(tab_stop_width_n) + help_n = 'connect_to_oi' + help_o = CONF.get('help', 'connect/editor') + todo_n = 'todo_list' + todo_o = self.get_option(todo_n) + pyflakes_n = 'code_analysis/pyflakes' + pyflakes_o = self.get_option(pyflakes_n) + pep8_n = 'code_analysis/pep8' + pep8_o = self.get_option(pep8_n) + rt_analysis_n = 'realtime_analysis' + rt_analysis_o = self.get_option(rt_analysis_n) + rta_timeout_n = 'realtime_analysis/timeout' + rta_timeout_o = self.get_option(rta_timeout_n) + finfo = self.get_current_finfo() + if fpsorting_n in options: + if self.outlineexplorer is not None: + self.outlineexplorer.set_fullpath_sorting(fpsorting_o) + for window in self.editorwindows: + window.editorwidget.outlineexplorer.set_fullpath_sorting( + fpsorting_o) + for editorstack in self.editorstacks: + if fpsorting_n in options: + editorstack.set_fullpath_sorting_enabled(fpsorting_o) + if tabbar_n in options: + editorstack.set_tabbar_visible(tabbar_o) + if linenb_n in options: + editorstack.set_linenumbers_enabled(linenb_o, + current_finfo=finfo) + if blanks_n in options: + editorstack.set_blanks_enabled(blanks_o) + self.showblanks_action.setChecked(blanks_o) + if edgeline_n in options: + editorstack.set_edgeline_enabled(edgeline_o) + if edgelinecol_n in options: + editorstack.set_edgeline_column(edgelinecol_o) + if wrap_n in options: + editorstack.set_wrap_enabled(wrap_o) + if tabindent_n in options: + editorstack.set_tabmode_enabled(tabindent_o) + if ibackspace_n in options: + editorstack.set_intelligent_backspace_enabled(ibackspace_o) + if removetrail_n in options: + editorstack.set_always_remove_trailing_spaces(removetrail_o) + if autocomp_n in options: + editorstack.set_codecompletion_auto_enabled(autocomp_o) + if case_comp_n in options: + editorstack.set_codecompletion_case_enabled(case_comp_o) + if enter_key_n in options: + editorstack.set_codecompletion_enter_enabled(enter_key_o) + if calltips_n in options: + editorstack.set_calltips_enabled(calltips_o) + if gotodef_n in options: + editorstack.set_go_to_definition_enabled(gotodef_o) + if closepar_n in options: + editorstack.set_close_parentheses_enabled(closepar_o) + if close_quotes_n in options: + editorstack.set_close_quotes_enabled(close_quotes_o) + if add_colons_n in options: + editorstack.set_add_colons_enabled(add_colons_o) + if autounindent_n in options: + editorstack.set_auto_unindent_enabled(autounindent_o) + if indent_chars_n in options: + editorstack.set_indent_chars(indent_chars_o) + if tab_stop_width_n in options: + editorstack.set_tab_stop_width(tab_stop_width_o) + if help_n in options: + editorstack.set_help_enabled(help_o) + if todo_n in options: + editorstack.set_todolist_enabled(todo_o, + current_finfo=finfo) + if pyflakes_n in options: + editorstack.set_pyflakes_enabled(pyflakes_o, + current_finfo=finfo) + if pep8_n in options: + editorstack.set_pep8_enabled(pep8_o, current_finfo=finfo) + if rt_analysis_n in options: + editorstack.set_realtime_analysis_enabled(rt_analysis_o) + if rta_timeout_n in options: + editorstack.set_realtime_analysis_timeout(rta_timeout_o) + # We must update the current editor after the others: + # (otherwise, code analysis buttons state would correspond to the + # last editor instead of showing the one of the current editor) + if finfo is not None: + if todo_n in options and todo_o: + finfo.run_todo_finder() + if pyflakes_n in options or pep8_n in options: + finfo.run_code_analysis(pyflakes_o, pep8_o) + + # --- Open files + def get_open_filenames(self): + """Get the list of open files in the current stack""" + editorstack = self.editorstacks[0] + filenames = [] + filenames += [finfo.filename for finfo in editorstack.data] + return filenames + + def set_open_filenames(self): + """ + Set the recent opened files on editor based on active project. + + If no project is active, then editor filenames are saved, otherwise + the opened filenames are stored in the project config info. + """ + if self.projects is not None: + 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() + + 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: + for layout_settings in win_layout: + self.editorwindows_to_be_created.append(layout_settings) + self.set_last_focus_editorstack(self, self.editorstacks[0]) + else: + self.__load_temp_file() + self.set_create_new_file_if_empty(True) + + def save_open_files(self): + """Save the list of open files""" + self.set_option('filenames', self.get_open_filenames()) + + def set_create_new_file_if_empty(self, value): + """Change the value of create_new_file_if_empty""" + for editorstack in self.editorstacks: + editorstack.create_new_file_if_empty = value diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/explorer.py spyder-3.0.2+dfsg1/spyder/plugins/explorer.py --- spyder-2.3.8+dfsg1/spyder/plugins/explorer.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/explorer.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Files and Directories Explorer Plugin""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os.path as osp + +# Third party imports +from qtpy.QtCore import Signal + +# Local imports +from spyder.config.base import _ +from spyder.plugins import SpyderPluginMixin +from spyder.py3compat import to_text_string +from spyder.widgets.explorer import ExplorerWidget + + +class Explorer(ExplorerWidget, SpyderPluginMixin): + """File and Directories Explorer DockWidget""" + CONF_SECTION = 'explorer' + open_terminal = Signal(str) + open_interpreter = Signal(str) + edit = Signal(str) + removed = Signal(str) + removed_tree = Signal(str) + renamed = Signal(str, str) + create_module = Signal(str) + run = Signal(str) + open_dir = Signal(str) + + def __init__(self, parent=None): + ExplorerWidget.__init__(self, parent=parent, + name_filters=self.get_option('name_filters'), + show_all=self.get_option('show_all'), + show_icontext=self.get_option('show_icontext')) + SpyderPluginMixin.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _("File explorer") + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.treewidget + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + return [] + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.main.add_dockwidget(self) + self.edit.connect(self.main.editor.load) + self.removed.connect(self.main.editor.removed) + self.removed_tree.connect(self.main.editor.removed_tree) + self.renamed.connect(self.main.editor.renamed) + self.main.editor.open_dir.connect(self.chdir) + self.create_module.connect(self.main.editor.new) + self.run.connect( + lambda fname: + self.main.open_external_console(to_text_string(fname), + osp.dirname(to_text_string(fname)), + '', False, False, True, '', False)) + # Signal "set_explorer_cwd(QString)" will refresh only the + # contents of path passed by the signal in explorer: + self.main.workingdirectory.set_explorer_cwd.connect( + lambda directory: self.refresh_plugin(new_path=directory, + force_current=True)) + self.open_dir.connect( + lambda dirname: + self.main.workingdirectory.chdir(dirname, + refresh_explorer=False)) + + self.sig_open_file.connect(self.main.open_file) + self.sig_new_file.connect(lambda t: self.main.editor.new(text=t)) + + def refresh_plugin(self, new_path=None, force_current=True): + """Refresh explorer widget""" + self.treewidget.update_history(new_path) + self.treewidget.refresh(new_path, force_current=force_current) + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + return True + + #------ Public API --------------------------------------------------------- + def chdir(self, directory): + """Set working directory""" + self.treewidget.chdir(directory) diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/externalconsole.py spyder-3.0.2+dfsg1/spyder/plugins/externalconsole.py --- spyder-2.3.8+dfsg1/spyder/plugins/externalconsole.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/externalconsole.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,972 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""External Console plugin""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os +import os.path as osp +import sys + +# Third party imports +from qtpy import PYQT5 +from qtpy.compat import getopenfilename +from qtpy.QtCore import Qt, Signal, Slot +from qtpy.QtWidgets import (QButtonGroup, QGroupBox, QHBoxLayout, QLabel, + QMessageBox, QTabWidget, QVBoxLayout, QWidget) + +# Local imports +from spyder import dependencies +from spyder.config.base import _, SCIENTIFIC_STARTUP +from spyder.config.main import CONF +from spyder.utils import encoding, programs +from spyder.utils import icon_manager as ima +from spyder.utils.misc import (get_error_match, get_python_executable, + is_python_script, remove_backslashes) +from spyder.utils.qthelpers import create_action, mimedata2url +from spyder.plugins import SpyderPluginWidget +from spyder.plugins.configdialog import PluginConfigPage +from spyder.plugins.runconfig import get_run_configuration +from spyder.py3compat import to_text_string, is_text_string, getcwd +from spyder.widgets.externalshell.pythonshell import ExternalPythonShell +from spyder.widgets.externalshell.systemshell import ExternalSystemShell +from spyder.widgets.findreplace import FindReplace +from spyder.widgets.tabs import Tabs + + +MPL_REQVER = '>=1.0' +dependencies.add("matplotlib", _("Interactive data plotting in the consoles"), + required_version=MPL_REQVER, optional=True) + + +class ExternalConsoleConfigPage(PluginConfigPage): + + def __init__(self, plugin, parent): + PluginConfigPage.__init__(self, plugin, parent) + self.get_name = lambda: _("Python console") + + def setup_page(self): + interface_group = QGroupBox(_("Interface")) + newcb = self.create_checkbox + singletab_box = newcb(_("One tab per script"), 'single_tab') + showtime_box = newcb(_("Show elapsed time"), 'show_elapsed_time') + icontext_box = newcb(_("Show icons and text"), 'show_icontext') + + # Interface Group + interface_layout = QVBoxLayout() + interface_layout.addWidget(singletab_box) + interface_layout.addWidget(showtime_box) + interface_layout.addWidget(icontext_box) + interface_group.setLayout(interface_layout) + + # Source Code Group + display_group = QGroupBox(_("Source code")) + buffer_spin = self.create_spinbox( + _("Buffer: "), _(" lines"), + 'max_line_count', min_=0, max_=1000000, step=100, + tip=_("Set maximum line count")) + wrap_mode_box = newcb(_("Wrap lines"), 'wrap') + merge_channels_box = newcb( + _("Merge process standard output/error channels"), + 'merge_output_channels', + tip=_("Merging the output channels of the process means that\n" + "the standard error won't be written in red anymore,\n" + "but this has the effect of speeding up display.")) + colorize_sys_stderr_box = newcb( + _("Colorize standard error channel using ANSI escape codes"), + 'colorize_sys_stderr', + tip=_("This method is the only way to have colorized standard\n" + "error channel when the output channels have been " + "merged.")) + merge_channels_box.toggled.connect(colorize_sys_stderr_box.setEnabled) + merge_channels_box.toggled.connect(colorize_sys_stderr_box.setChecked) + colorize_sys_stderr_box.setEnabled( + self.get_option('merge_output_channels')) + + display_layout = QVBoxLayout() + display_layout.addWidget(buffer_spin) + display_layout.addWidget(wrap_mode_box) + display_layout.addWidget(merge_channels_box) + display_layout.addWidget(colorize_sys_stderr_box) + display_group.setLayout(display_layout) + + # Background Color Group + bg_group = QGroupBox(_("Background color")) + bg_label = QLabel(_("This option will be applied the next time " + "a Python console or a terminal is opened.")) + bg_label.setWordWrap(True) + lightbg_box = newcb(_("Light background (white color)"), + 'light_background') + bg_layout = QVBoxLayout() + bg_layout.addWidget(bg_label) + bg_layout.addWidget(lightbg_box) + bg_group.setLayout(bg_layout) + + # Advanced settings + source_group = QGroupBox(_("Source code")) + completion_box = newcb(_("Automatic code completion"), + 'codecompletion/auto') + case_comp_box = newcb(_("Case sensitive code completion"), + 'codecompletion/case_sensitive') + comp_enter_box = newcb(_("Enter key selects completion"), + 'codecompletion/enter_key') + calltips_box = newcb(_("Display balloon tips"), 'calltips') + + source_layout = QVBoxLayout() + source_layout.addWidget(completion_box) + source_layout.addWidget(case_comp_box) + source_layout.addWidget(comp_enter_box) + source_layout.addWidget(calltips_box) + source_group.setLayout(source_layout) + + # PYTHONSTARTUP replacement + pystartup_group = QGroupBox(_("PYTHONSTARTUP replacement")) + pystartup_bg = QButtonGroup(pystartup_group) + pystartup_label = QLabel(_("This option will override the " + "PYTHONSTARTUP environment variable which\n" + "defines the script to be executed during " + "the Python console startup.")) + def_startup_radio = self.create_radiobutton( + _("Default PYTHONSTARTUP script"), + 'pythonstartup/default', + button_group=pystartup_bg) + cus_startup_radio = self.create_radiobutton( + _("Use the following startup script:"), + 'pythonstartup/custom', + button_group=pystartup_bg) + pystartup_file = self.create_browsefile('', 'pythonstartup', '', + filters=_("Python scripts")+\ + " (*.py)") + def_startup_radio.toggled.connect(pystartup_file.setDisabled) + cus_startup_radio.toggled.connect(pystartup_file.setEnabled) + + pystartup_layout = QVBoxLayout() + pystartup_layout.addWidget(pystartup_label) + pystartup_layout.addWidget(def_startup_radio) + pystartup_layout.addWidget(cus_startup_radio) + pystartup_layout.addWidget(pystartup_file) + pystartup_group.setLayout(pystartup_layout) + + # Monitor Group + monitor_group = QGroupBox(_("Monitor")) + monitor_label = QLabel(_("The monitor provides introspection " + "features to console: code completion, " + "calltips and variable explorer. " + "Because it relies on several modules, " + "disabling the monitor may be useful " + "to accelerate console startup.")) + monitor_label.setWordWrap(True) + monitor_box = newcb(_("Enable monitor"), 'monitor/enabled') + for obj in (completion_box, case_comp_box, comp_enter_box, + calltips_box): + monitor_box.toggled.connect(obj.setEnabled) + obj.setEnabled(self.get_option('monitor/enabled')) + + monitor_layout = QVBoxLayout() + monitor_layout.addWidget(monitor_label) + monitor_layout.addWidget(monitor_box) + monitor_group.setLayout(monitor_layout) + + # Qt Group + opts = [ + (_("Default library"), 'default'), + ('PyQt5', 'pyqt5'), + ('PyQt4', 'pyqt'), + ('PySide', 'pyside'), + ] + qt_group = QGroupBox(_("Qt-Python Bindings")) + qt_setapi_box = self.create_combobox( + _("Library:") + " ", opts, + 'qt/api', default='default', + tip=_("This option will act on
    " + "libraries such as Matplotlib, guidata " + "or ETS")) + + qt_layout = QVBoxLayout() + qt_layout.addWidget(qt_setapi_box) + qt_group.setLayout(qt_layout) + + # Matplotlib Group + mpl_group = QGroupBox(_("Graphics")) + mpl_label = QLabel(_("Decide which backend to use to display graphics. " + "If unsure, please select the Automatic " + "backend.

    " + "Note: We support a very limited number " + "of backends in our Python consoles. If you " + "prefer to work with a different one, please use " + "an IPython console.")) + mpl_label.setWordWrap(True) + + backends = [(_("Automatic"), 0), (_("None"), 1)] + if not os.name == 'nt' and programs.is_module_installed('_tkinter'): + backends.append( ("Tkinter", 2) ) + backends = tuple(backends) + + mpl_backend_box = self.create_combobox( _("Backend:")+" ", backends, + 'matplotlib/backend/value', + tip=_("This option will be applied the " + "next time a console is opened.")) + + mpl_installed = programs.is_module_installed('matplotlib') + mpl_layout = QVBoxLayout() + mpl_layout.addWidget(mpl_label) + mpl_layout.addWidget(mpl_backend_box) + mpl_group.setLayout(mpl_layout) + mpl_group.setEnabled(mpl_installed) + + # ETS Group + ets_group = QGroupBox(_("Enthought Tool Suite")) + ets_label = QLabel(_("Enthought Tool Suite (ETS) supports " + "PyQt4 (qt4) and wxPython (wx) graphical " + "user interfaces.")) + ets_label.setWordWrap(True) + ets_edit = self.create_lineedit(_("ETS_TOOLKIT:"), 'ets_backend', + alignment=Qt.Horizontal) + + ets_layout = QVBoxLayout() + ets_layout.addWidget(ets_label) + ets_layout.addWidget(ets_edit) + ets_group.setLayout(ets_layout) + + if CONF.get('main_interpreter','default'): + interpreter = get_python_executable() + else: + interpreter = CONF.get('main_interpreter', 'executable') + ets_group.setEnabled(programs.is_module_installed( + "enthought.etsconfig.api", + interpreter=interpreter)) + + tabs = QTabWidget() + tabs.addTab(self.create_tab(interface_group, display_group, + bg_group), + _("Display")) + tabs.addTab(self.create_tab(monitor_group, source_group), + _("Introspection")) + tabs.addTab(self.create_tab(pystartup_group), _("Advanced settings")) + tabs.addTab(self.create_tab(qt_group, mpl_group, ets_group), + _("External modules")) + + vlayout = QVBoxLayout() + vlayout.addWidget(tabs) + self.setLayout(vlayout) + + +class ExternalConsole(SpyderPluginWidget): + """ + Console widget + """ + CONF_SECTION = 'console' + CONFIGWIDGET_CLASS = ExternalConsoleConfigPage + DISABLE_ACTIONS_WHEN_HIDDEN = False + + edit_goto = Signal((str, int, str), (str, int, str, bool)) + focus_changed = Signal() + redirect_stdio = Signal(bool) + + def __init__(self, parent): + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) + self.tabwidget = None + self.menu_actions = None + + self.help = None # Help plugin + self.historylog = None # History log plugin + self.variableexplorer = None # Variable explorer plugin + + self.python_count = 0 + self.terminal_count = 0 + + # Python startup file selection + if not osp.isfile(self.get_option('pythonstartup', '')): + self.set_option('pythonstartup', SCIENTIFIC_STARTUP) + # default/custom settings are mutually exclusive: + self.set_option('pythonstartup/custom', + not self.get_option('pythonstartup/default')) + + self.shellwidgets = [] + self.filenames = [] + self.icons = [] + self.runfile_args = "" + + # Initialize plugin + self.initialize_plugin() + + layout = QVBoxLayout() + self.tabwidget = Tabs(self, self.menu_actions) + if hasattr(self.tabwidget, 'setDocumentMode')\ + and not sys.platform == 'darwin': + # Don't set document mode to true on OSX because it generates + # a crash when the console is detached from the main window + # Fixes Issue 561 + self.tabwidget.setDocumentMode(True) + self.tabwidget.currentChanged.connect(self.refresh_plugin) + self.tabwidget.move_data.connect(self.move_tab) + self.main.sig_pythonpath_changed.connect(self.set_path) + + self.tabwidget.set_close_function(self.close_console) + + if sys.platform == 'darwin': + tab_container = QWidget() + tab_container.setObjectName('tab-container') + tab_layout = QHBoxLayout(tab_container) + tab_layout.setContentsMargins(0, 0, 0, 0) + tab_layout.addWidget(self.tabwidget) + layout.addWidget(tab_container) + else: + layout.addWidget(self.tabwidget) + + # Find/replace widget + self.find_widget = FindReplace(self) + self.find_widget.hide() + self.register_widget_shortcuts(self.find_widget) + + layout.addWidget(self.find_widget) + + self.setLayout(layout) + + # Accepting drops + self.setAcceptDrops(True) + + def move_tab(self, index_from, index_to): + """ + Move tab (tabs themselves have already been moved by the tabwidget) + """ + filename = self.filenames.pop(index_from) + shell = self.shellwidgets.pop(index_from) + icons = self.icons.pop(index_from) + + self.filenames.insert(index_to, filename) + self.shellwidgets.insert(index_to, shell) + self.icons.insert(index_to, icons) + self.update_plugin_title.emit() + + def get_shell_index_from_id(self, shell_id): + """Return shellwidget index from id""" + for index, shell in enumerate(self.shellwidgets): + if id(shell) == shell_id: + return index + + def close_console(self, index=None): + """Close console tab from index or widget (or close current tab)""" + # Get tab index + if not self.tabwidget.count(): + return + if index is None: + index = self.tabwidget.currentIndex() + + # Closing logic + self.tabwidget.widget(index).close() + self.tabwidget.removeTab(index) + self.filenames.pop(index) + self.shellwidgets.pop(index) + self.icons.pop(index) + self.update_plugin_title.emit() + + def set_variableexplorer(self, variableexplorer): + """Set variable explorer plugin""" + self.variableexplorer = variableexplorer + + def set_path(self): + """Set consoles PYTHONPATH if changed by the user""" + from spyder.widgets.externalshell import pythonshell + for sw in self.shellwidgets: + if isinstance(sw, pythonshell.ExternalPythonShell): + if sw.is_interpreter and sw.is_running(): + sw.path = self.main.get_spyder_pythonpath() + sw.shell.path = sw.path + + def __find_python_shell(self, interpreter_only=False): + current_index = self.tabwidget.currentIndex() + if current_index == -1: + return + from spyder.widgets.externalshell import pythonshell + for index in [current_index]+list(range(self.tabwidget.count())): + shellwidget = self.tabwidget.widget(index) + if isinstance(shellwidget, pythonshell.ExternalPythonShell): + if interpreter_only and not shellwidget.is_interpreter: + continue + elif not shellwidget.is_running(): + continue + else: + self.tabwidget.setCurrentIndex(index) + return shellwidget + + def get_current_shell(self): + """ + Called by Help to retrieve the current shell instance + """ + shellwidget = self.__find_python_shell() + return shellwidget.shell + + def get_running_python_shell(self): + """ + Called by Help to retrieve a running Python shell instance + """ + current_index = self.tabwidget.currentIndex() + if current_index == -1: + return + from spyder.widgets.externalshell import pythonshell + shellwidgets = [self.tabwidget.widget(index) + for index in range(self.tabwidget.count())] + shellwidgets = [_w for _w in shellwidgets + if isinstance(_w, pythonshell.ExternalPythonShell) \ + and _w.is_running()] + if shellwidgets: + # First, iterate on interpreters only: + for shellwidget in shellwidgets: + if shellwidget.is_interpreter: + return shellwidget.shell + else: + return shellwidgets[0].shell + + def run_script_in_current_shell(self, filename, wdir, args, debug, + post_mortem): + """Run script in current shell, if any""" + norm = lambda text: remove_backslashes(to_text_string(text)) + line = "%s('%s'" % ('debugfile' if debug else 'runfile', + norm(filename)) + if args: + line += ", args='%s'" % norm(args) + if wdir: + line += ", wdir='%s'" % norm(wdir) + if post_mortem: + line += ', post_mortem=True' + line += ")" + if not self.execute_code(line, interpreter_only=True): + QMessageBox.warning(self, _('Warning'), + _("No Python console is currently selected to run %s." + "

    Please select or open a new Python console " + "and try again." + ) % osp.basename(norm(filename)), QMessageBox.Ok) + else: + self.visibility_changed(True) + self.raise_() + + def set_current_shell_working_directory(self, directory): + """Set current shell working directory""" + shellwidget = self.__find_python_shell() + if shellwidget is not None: + directory = encoding.to_unicode_from_fs(directory) + shellwidget.shell.set_cwd(directory) + + def execute_code(self, lines, interpreter_only=False): + """Execute code in an already opened Python interpreter""" + shellwidget = self.__find_python_shell( + interpreter_only=interpreter_only) + if shellwidget is not None: + shellwidget.shell.execute_lines(to_text_string(lines)) + self.activateWindow() + shellwidget.shell.setFocus() + return True + else: + return False + + def pdb_has_stopped(self, fname, lineno, shellwidget): + """Python debugger has just stopped at frame (fname, lineno)""" + # This is a unique form of the edit_goto signal that is intended to + # prevent keyboard input from accidentally entering the editor + # during repeated, rapid entry of debugging commands. + self.edit_goto[str, int, str, bool].emit(fname, lineno, '', False) + self.activateWindow() + shellwidget.shell.setFocus() + + def set_spyder_breakpoints(self): + """Set all Spyder breakpoints into all shells""" + for shellwidget in self.shellwidgets: + shellwidget.shell.set_spyder_breakpoints() + + def start(self, fname, wdir=None, args='', interact=False, debug=False, + python=True, python_args='', post_mortem=True): + """ + Start new console + + fname: + string: filename of script to run + None: open an interpreter + wdir: working directory + args: command line options of the Python script + interact: inspect script interactively after its execution + debug: run pdb + python: True: Python interpreter, False: terminal + python_args: additionnal Python interpreter command line options + (option "-u" is mandatory, see widgets.externalshell package) + """ + # Note: fname is None <=> Python interpreter + if fname is not None and not is_text_string(fname): + fname = to_text_string(fname) + if wdir is not None and not is_text_string(wdir): + wdir = to_text_string(wdir) + + if fname is not None and fname in self.filenames: + index = self.filenames.index(fname) + if self.get_option('single_tab'): + old_shell = self.shellwidgets[index] + if old_shell.is_running(): + runconfig = get_run_configuration(fname) + if runconfig is None or runconfig.show_kill_warning: + answer = QMessageBox.question(self, self.get_plugin_title(), + _("%s is already running in a separate process.\n" + "Do you want to kill the process before starting " + "a new one?") % osp.basename(fname), + QMessageBox.Yes | QMessageBox.Cancel) + else: + answer = QMessageBox.Yes + + if answer == QMessageBox.Yes: + old_shell.process.kill() + old_shell.process.waitForFinished() + else: + return + self.close_console(index) + else: + index = self.tabwidget.count() + + # Creating a new external shell + pythonpath = self.main.get_spyder_pythonpath() + light_background = self.get_option('light_background') + show_elapsed_time = self.get_option('show_elapsed_time') + if python: + if CONF.get('main_interpreter', 'default'): + pythonexecutable = get_python_executable() + external_interpreter = False + else: + pythonexecutable = CONF.get('main_interpreter', 'executable') + external_interpreter = True + if self.get_option('pythonstartup/default'): + pythonstartup = None + else: + pythonstartup = self.get_option('pythonstartup', None) + monitor_enabled = self.get_option('monitor/enabled') + mpl_backend = self.get_option('matplotlib/backend/value') + ets_backend = self.get_option('ets_backend') + qt_api = self.get_option('qt/api') + if qt_api not in ('pyqt', 'pyside', 'pyqt5'): + qt_api = None + merge_output_channels = self.get_option('merge_output_channels') + colorize_sys_stderr = self.get_option('colorize_sys_stderr') + umr_enabled = CONF.get('main_interpreter', 'umr/enabled') + umr_namelist = CONF.get('main_interpreter', 'umr/namelist') + umr_verbose = CONF.get('main_interpreter', 'umr/verbose') + ar_timeout = CONF.get('variable_explorer', 'autorefresh/timeout') + ar_state = CONF.get('variable_explorer', 'autorefresh') + + sa_settings = None + shellwidget = ExternalPythonShell(self, fname, wdir, + interact, debug, post_mortem=post_mortem, + path=pythonpath, + python_args=python_args, + arguments=args, stand_alone=sa_settings, + pythonstartup=pythonstartup, + pythonexecutable=pythonexecutable, + external_interpreter=external_interpreter, + umr_enabled=umr_enabled, umr_namelist=umr_namelist, + umr_verbose=umr_verbose, ets_backend=ets_backend, + monitor_enabled=monitor_enabled, + mpl_backend=mpl_backend, qt_api=qt_api, + merge_output_channels=merge_output_channels, + colorize_sys_stderr=colorize_sys_stderr, + autorefresh_timeout=ar_timeout, + autorefresh_state=ar_state, + light_background=light_background, + menu_actions=self.menu_actions, + show_buttons_inside=False, + show_elapsed_time=show_elapsed_time) + shellwidget.sig_pdb.connect( + lambda fname, lineno, shellwidget=shellwidget: + self.pdb_has_stopped(fname, lineno, shellwidget)) + self.register_widget_shortcuts(shellwidget.shell) + else: + if os.name == 'posix': + cmd = 'gnome-terminal' + args = [] + if programs.is_program_installed(cmd): + if wdir: + args.extend(['--working-directory=%s' % wdir]) + programs.run_program(cmd, args) + return + cmd = 'konsole' + if programs.is_program_installed(cmd): + if wdir: + args.extend(['--workdir', wdir]) + programs.run_program(cmd, args) + return + shellwidget = ExternalSystemShell(self, wdir, path=pythonpath, + light_background=light_background, + menu_actions=self.menu_actions, + show_buttons_inside=False, + show_elapsed_time=show_elapsed_time) + + # Code completion / calltips + shellwidget.shell.setMaximumBlockCount( + self.get_option('max_line_count') ) + shellwidget.shell.set_font( self.get_plugin_font() ) + shellwidget.shell.toggle_wrap_mode( self.get_option('wrap') ) + shellwidget.shell.set_calltips( self.get_option('calltips') ) + shellwidget.shell.set_codecompletion_auto( + self.get_option('codecompletion/auto') ) + shellwidget.shell.set_codecompletion_case( + self.get_option('codecompletion/case_sensitive') ) + shellwidget.shell.set_codecompletion_enter( + self.get_option('codecompletion/enter_key') ) + if python and self.help is not None: + shellwidget.shell.set_help(self.help) + shellwidget.shell.set_help_enabled( + CONF.get('help', 'connect/python_console')) + if self.historylog is not None: + self.historylog.add_history(shellwidget.shell.history_filename) + shellwidget.shell.append_to_history.connect( + self.historylog.append_to_history) + shellwidget.shell.go_to_error.connect(self.go_to_error) + shellwidget.shell.focus_changed.connect( + lambda: self.focus_changed.emit()) + if python: + if self.main.editor is not None: + shellwidget.open_file.connect(self.open_file_in_spyder) + if fname is None: + self.python_count += 1 + tab_name = "Python %d" % self.python_count + tab_icon1 = ima.icon('python') + tab_icon2 = ima.icon('python_t') + else: + tab_name = osp.basename(fname) + tab_icon1 = ima.icon('run') + tab_icon2 = ima.icon('terminated') + else: + fname = id(shellwidget) + if os.name == 'nt': + tab_name = _("Command Window") + else: + tab_name = _("Terminal") + self.terminal_count += 1 + tab_name += (" %d" % self.terminal_count) + tab_icon1 = ima.icon('cmdprompt') + tab_icon2 = ima.icon('cmdprompt_t') + self.shellwidgets.insert(index, shellwidget) + self.filenames.insert(index, fname) + self.icons.insert(index, (tab_icon1, tab_icon2)) + if index is None: + index = self.tabwidget.addTab(shellwidget, tab_name) + else: + self.tabwidget.insertTab(index, shellwidget, tab_name) + + shellwidget.started.connect( + lambda sid=id(shellwidget): self.process_started(sid)) + shellwidget.sig_finished.connect( + lambda sid=id(shellwidget): self.process_finished(sid)) + self.find_widget.set_editor(shellwidget.shell) + self.tabwidget.setTabToolTip(index, fname if wdir is None else wdir) + self.tabwidget.setCurrentIndex(index) + if self.dockwidget and not self.ismaximized: + self.dockwidget.setVisible(True) + self.dockwidget.raise_() + + shellwidget.set_icontext_visible(self.get_option('show_icontext')) + + # Start process and give focus to console + shellwidget.start_shell() + + def open_file_in_spyder(self, fname, lineno): + """Open file in Spyder's editor from remote process""" + self.main.editor.activateWindow() + self.main.editor.raise_() + self.main.editor.load(fname, lineno) + + #------ Private API ------------------------------------------------------- + def process_started(self, shell_id): + index = self.get_shell_index_from_id(shell_id) + shell = self.shellwidgets[index] + icon, _icon = self.icons[index] + self.tabwidget.setTabIcon(index, icon) + if self.help is not None: + self.help.set_shell(shell.shell) + if self.variableexplorer is not None: + self.variableexplorer.add_shellwidget(shell) + + def process_finished(self, shell_id): + index = self.get_shell_index_from_id(shell_id) + if index is not None: + # Not sure why it happens, but sometimes the shellwidget has + # already been removed, so that's not bad if we can't change + # the tab icon... + _icon, icon = self.icons[index] + self.tabwidget.setTabIcon(index, icon) + if self.variableexplorer is not None: + self.variableexplorer.remove_shellwidget(shell_id) + + #------ SpyderPluginWidget API -------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + title = _('Python console') + if self.filenames: + index = self.tabwidget.currentIndex() + fname = self.filenames[index] + if fname: + title += ' - ' + to_text_string(fname) + return title + + def get_plugin_icon(self): + """Return widget icon""" + return ima.icon('console') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.tabwidget.currentWidget() + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + interpreter_action = create_action(self, + _("Open a &Python console"), None, + ima.icon('python'), + triggered=self.open_interpreter) + if os.name == 'nt': + text = _("Open &command prompt") + tip = _("Open a Windows command prompt") + else: + text = _("Open a &terminal") + tip = _("Open a terminal window") + terminal_action = create_action(self, text, None, None, tip, + triggered=self.open_terminal) + run_action = create_action(self, + _("&Run..."), None, + ima.icon('run_small'), _("Run a Python script"), + triggered=self.run_script) + + consoles_menu_actions = [interpreter_action] + tools_menu_actions = [terminal_action] + self.menu_actions = [interpreter_action, terminal_action, run_action] + + self.main.consoles_menu_actions += consoles_menu_actions + self.main.tools_menu_actions += tools_menu_actions + + return self.menu_actions+consoles_menu_actions+tools_menu_actions + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.main.add_dockwidget(self) + self.help = self.main.help + self.historylog = self.main.historylog + self.edit_goto.connect(self.main.editor.load) + self.edit_goto[str, int, str, bool].connect( + lambda fname, lineno, word, processevents: + self.main.editor.load(fname, lineno, word, + processevents=processevents)) + self.main.editor.run_in_current_extconsole.connect( + self.run_script_in_current_shell) + self.main.editor.breakpoints_saved.connect( + self.set_spyder_breakpoints) + self.main.editor.open_dir.connect( + self.set_current_shell_working_directory) + self.main.workingdirectory.set_current_console_wd.connect( + self.set_current_shell_working_directory) + self.focus_changed.connect( + self.main.plugin_focus_changed) + self.redirect_stdio.connect( + self.main.redirect_internalshell_stdio) + expl = self.main.explorer + if expl is not None: + expl.open_terminal.connect(self.open_terminal) + expl.open_interpreter.connect(self.open_interpreter) + pexpl = self.main.projects + if pexpl is not None: + pexpl.open_terminal.connect(self.open_terminal) + pexpl.open_interpreter.connect(self.open_interpreter) + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + for shellwidget in self.shellwidgets: + shellwidget.close() + return True + + def restart(self): + """ + Restart the console + + This is needed when we switch project to update PYTHONPATH + and the selected interpreter + """ + self.python_count = 0 + for i in range(len(self.shellwidgets)): + self.close_console() + self.open_interpreter() + + def refresh_plugin(self): + """Refresh tabwidget""" + shellwidget = None + if self.tabwidget.count(): + shellwidget = self.tabwidget.currentWidget() + editor = shellwidget.shell + editor.setFocus() + widgets = [shellwidget.create_time_label(), 5 + ]+shellwidget.get_toolbar_buttons()+[5] + else: + editor = None + widgets = [] + self.find_widget.set_editor(editor) + self.tabwidget.set_corner_widgets({Qt.TopRightCorner: widgets}) + if shellwidget: + shellwidget.update_time_label_visibility() + self.variableexplorer.set_shellwidget_from_id(id(shellwidget)) + self.help.set_shell(shellwidget.shell) + self.main.last_console_plugin_focus_was_python = True + self.update_plugin_title.emit() + + def update_font(self): + """Update font from Preferences""" + font = self.get_plugin_font() + for shellwidget in self.shellwidgets: + shellwidget.shell.set_font(font) + completion_size = CONF.get('main', 'completion/size') + comp_widget = shellwidget.shell.completion_widget + comp_widget.setup_appearance(completion_size, font) + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + showtime_n = 'show_elapsed_time' + showtime_o = self.get_option(showtime_n) + icontext_n = 'show_icontext' + icontext_o = self.get_option(icontext_n) + calltips_n = 'calltips' + calltips_o = self.get_option(calltips_n) + help_n = 'connect_to_oi' + help_o = CONF.get('help', 'connect/python_console') + wrap_n = 'wrap' + wrap_o = self.get_option(wrap_n) + compauto_n = 'codecompletion/auto' + compauto_o = self.get_option(compauto_n) + case_comp_n = 'codecompletion/case_sensitive' + case_comp_o = self.get_option(case_comp_n) + compenter_n = 'codecompletion/enter_key' + compenter_o = self.get_option(compenter_n) + mlc_n = 'max_line_count' + mlc_o = self.get_option(mlc_n) + for shellwidget in self.shellwidgets: + if showtime_n in options: + shellwidget.set_elapsed_time_visible(showtime_o) + if icontext_n in options: + shellwidget.set_icontext_visible(icontext_o) + if calltips_n in options: + shellwidget.shell.set_calltips(calltips_o) + if help_n in options: + if isinstance(shellwidget, ExternalPythonShell): + shellwidget.shell.set_help_enabled(help_o) + if wrap_n in options: + shellwidget.shell.toggle_wrap_mode(wrap_o) + if compauto_n in options: + shellwidget.shell.set_codecompletion_auto(compauto_o) + if case_comp_n in options: + shellwidget.shell.set_codecompletion_case(case_comp_o) + if compenter_n in options: + shellwidget.shell.set_codecompletion_enter(compenter_o) + if mlc_n in options: + shellwidget.shell.setMaximumBlockCount(mlc_o) + + #------ SpyderPluginMixin API --------------------------------------------- + def toggle_view(self, checked): + """Toggle view""" + if checked: + self.dockwidget.show() + self.dockwidget.raise_() + # Start a console in case there are none shown + from spyder.widgets.externalshell import pythonshell + consoles = None + for sw in self.shellwidgets: + if isinstance(sw, pythonshell.ExternalPythonShell): + consoles = True + break + if not consoles: + self.open_interpreter() + else: + self.dockwidget.hide() + + #------ Public API --------------------------------------------------------- + @Slot(bool) + @Slot(str) + def open_interpreter(self, wdir=None): + """Open interpreter""" + if not wdir: + wdir = getcwd() + self.visibility_changed(True) + self.start(fname=None, wdir=to_text_string(wdir), args='', + interact=True, debug=False, python=True) + + @Slot(bool) + @Slot(str) + def open_terminal(self, wdir=None): + """Open terminal""" + if not wdir: + wdir = getcwd() + self.start(fname=None, wdir=to_text_string(wdir), args='', + interact=True, debug=False, python=False) + + @Slot() + def run_script(self): + """Run a Python script""" + self.redirect_stdio.emit(False) + filename, _selfilter = getopenfilename(self, _("Run Python script"), + getcwd(), _("Python scripts")+" (*.py ; *.pyw ; *.ipy)") + self.redirect_stdio.emit(True) + if filename: + self.start(fname=filename, wdir=None, args='', + interact=False, debug=False) + + def go_to_error(self, text): + """Go to error if relevant""" + match = get_error_match(to_text_string(text)) + if match: + fname, lnb = match.groups() + self.edit_goto.emit(osp.abspath(fname), int(lnb), '') + + #----Drag and drop + def dragEnterEvent(self, event): + """Reimplement Qt method + Inform Qt about the types of data that the widget accepts""" + source = event.mimeData() + if source.hasUrls(): + if mimedata2url(source): + pathlist = mimedata2url(source) + shellwidget = self.tabwidget.currentWidget() + if all([is_python_script(to_text_string(qstr)) + for qstr in pathlist]): + event.acceptProposedAction() + elif shellwidget is None or not shellwidget.is_running(): + event.ignore() + else: + event.acceptProposedAction() + else: + event.ignore() + elif source.hasText(): + event.acceptProposedAction() + + def dropEvent(self, event): + """Reimplement Qt method + Unpack dropped data and handle it""" + source = event.mimeData() + shellwidget = self.tabwidget.currentWidget() + if source.hasText(): + qstr = source.text() + if is_python_script(to_text_string(qstr)): + self.start(qstr) + elif shellwidget: + shellwidget.shell.insert_text(qstr) + elif source.hasUrls(): + pathlist = mimedata2url(source) + if all([is_python_script(to_text_string(qstr)) + for qstr in pathlist]): + for fname in pathlist: + self.start(fname) + elif shellwidget: + shellwidget.shell.drop_pathlist(pathlist) + event.acceptProposedAction() diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/findinfiles.py spyder-3.0.2+dfsg1/spyder/plugins/findinfiles.py --- spyder-2.3.8+dfsg1/spyder/plugins/findinfiles.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/findinfiles.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Find in Files Plugin""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import sys + +# Third party imports +from qtpy.QtWidgets import QApplication +from qtpy.QtCore import Signal, Slot + +# Local imports +from spyder.config.base import _ +from spyder.config.utils import get_edit_extensions +from spyder.py3compat import getcwd +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import create_action +from spyder.widgets.findinfiles import FindInFilesWidget +from spyder.plugins import SpyderPluginMixin + + +class FindInFiles(FindInFilesWidget, SpyderPluginMixin): + """Find in files DockWidget""" + CONF_SECTION = 'find_in_files' + sig_option_changed = Signal(str, object) + toggle_visibility = Signal(bool) + edit_goto = Signal(str, int, str) + redirect_stdio = Signal(bool) + + def __init__(self, parent=None): + supported_encodings = self.get_option('supported_encodings') + + search_path = self.get_option('search_path', None) + self.search_text_samples = self.get_option('search_text_samples') + search_text = self.get_option('search_text') + search_text = [txt for txt in search_text \ + if txt not in self.search_text_samples] + search_text += self.search_text_samples + + search_text_regexp = self.get_option('search_text_regexp') + include = self.get_option('include') + if not include: + include = self.include_patterns() + include_idx = self.get_option('include_idx', None) + include_regexp = self.get_option('include_regexp') + exclude = self.get_option('exclude') + exclude_idx = self.get_option('exclude_idx', None) + exclude_regexp = self.get_option('exclude_regexp') + in_python_path = self.get_option('in_python_path') + more_options = self.get_option('more_options') + FindInFilesWidget.__init__(self, parent, + search_text, search_text_regexp, search_path, + include, include_idx, include_regexp, + exclude, exclude_idx, exclude_regexp, + supported_encodings, + in_python_path, more_options) + SpyderPluginMixin.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + + self.toggle_visibility.connect(self.toggle) + + def toggle(self, state): + """Toggle widget visibility""" + if self.dockwidget: + self.dockwidget.setVisible(state) + + def refreshdir(self): + """Refresh search directory""" + self.find_options.set_directory(getcwd()) + + @Slot() + def findinfiles_callback(self): + """Find in files callback""" + widget = QApplication.focusWidget() + if not self.ismaximized: + self.dockwidget.setVisible(True) + self.dockwidget.raise_() + text = '' + try: + if widget.has_selected_text(): + text = widget.get_selected_text() + except AttributeError: + # This is not a text widget deriving from TextEditBaseWidget + pass + self.set_search_text(text) + if text: + self.find() + + @staticmethod + def include_patterns(): + """Generate regex common usage patterns to include section.""" + # Change special characters, like + and . to convert into valid re + clean_exts = [] + for ext in get_edit_extensions(): + ext = ext.replace('.', r'\.') + ext = ext.replace('+', r'\+') + clean_exts.append(ext) + + patterns = [r'|'.join([ext + r'$' for ext in clean_exts if ext]) + + r'|README|INSTALL', + r'\.ipy$|\.pyw?$|\.rst$|\.txt$', + '.', + ] + return patterns + + #------ SpyderPluginMixin API --------------------------------------------- + def switch_to_plugin(self): + """Switch to plugin + This method is called when pressing plugin's shortcut key""" + self.findinfiles_callback() # Necessary at least with PyQt5 on Windows + SpyderPluginMixin.switch_to_plugin(self) + + #------ SpyderPluginWidget API -------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _("Find in files") + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.find_options.search_text + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + return [] + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.get_pythonpath_callback = self.main.get_spyder_pythonpath + self.main.add_dockwidget(self) + self.edit_goto.connect(self.main.editor.load) + self.redirect_stdio.connect(self.main.redirect_internalshell_stdio) + self.main.workingdirectory.refresh_findinfiles.connect(self.refreshdir) + + findinfiles_action = create_action(self, _("&Find in files"), + icon=ima.icon('findf'), + triggered=self.findinfiles_callback, + tip=_("Search text in multiple files")) + + self.main.search_menu_actions += [None, findinfiles_action] + self.main.search_toolbar_actions += [None, findinfiles_action] + + def refresh_plugin(self): + """Refresh widget""" + pass + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + self.closing_widget() # stop search thread and clean-up + options = self.find_options.get_options(all=True) + if options is not None: + search_text, text_re, search_path, \ + include, include_idx, include_re, \ + exclude, exclude_idx, exclude_re, \ + in_python_path, more_options = options + hist_limit = 15 + search_text = search_text[:hist_limit] + search_path = search_path[:hist_limit] + include = include[:hist_limit] + exclude = exclude[:hist_limit] + self.set_option('search_text', search_text) + self.set_option('search_text_regexp', text_re) + self.set_option('search_path', search_path) + self.set_option('include', include) + self.set_option('include_idx', include_idx) + self.set_option('include_regexp', include_re) + self.set_option('exclude', exclude) + self.set_option('exclude_idx', exclude_idx) + self.set_option('exclude_regexp', exclude_re) + self.set_option('in_python_path', in_python_path) + self.set_option('more_options', more_options) + return True + + +def test(): + from spyder.utils.qthelpers import qapplication + app = qapplication() + widget = FindInFiles() + widget.show() + sys.exit(app.exec_()) + + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/help.py spyder-3.0.2+dfsg1/spyder/plugins/help.py --- spyder-2.3.8+dfsg1/spyder/plugins/help.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/help.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,980 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Help Plugin""" + +# Standard library imports +import re +import os.path as osp +import socket +import sys + +# Third party imports +from qtpy import PYQT5 +from qtpy.QtCore import QThread, QUrl, Signal, Slot +from qtpy.QtWidgets import (QActionGroup, QComboBox, QGroupBox, QHBoxLayout, + QLabel, QLineEdit, QMenu, QMessageBox, QSizePolicy, + QToolButton, QVBoxLayout, QWidget) +from qtpy.QtWebEngineWidgets import QWebEnginePage, WEBENGINE + +# Local imports +from spyder import dependencies +from spyder.config.base import _, get_conf_path, get_module_source_path +from spyder.config.fonts import DEFAULT_SMALL_DELTA +from spyder.config.ipython import QTCONSOLE_INSTALLED +from spyder.plugins import SpyderPluginWidget +from spyder.plugins.configdialog import PluginConfigPage +from spyder.py3compat import get_meth_class_inst, to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils import programs +from spyder.utils.help.sphinxify import (CSS_PATH, generate_context, + sphinxify, usage, warning) +from spyder.utils.qthelpers import (add_actions, create_action, + create_toolbutton) +from spyder.widgets.browser import FrameWebView +from spyder.widgets.comboboxes import EditableComboBox +from spyder.widgets.findreplace import FindReplace +from spyder.widgets.sourcecode import codeeditor + + +# Sphinx dependency +dependencies.add("sphinx", _("Show help for objects in the Editor and " + "Consoles in a dedicated pane"), + required_version='>=0.6.6') + + + +class ObjectComboBox(EditableComboBox): + """ + QComboBox handling object names + """ + # Signals + valid = Signal(bool, bool) + + def __init__(self, parent): + EditableComboBox.__init__(self, parent) + self.help = parent + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.tips = {True: '', False: ''} + + def is_valid(self, qstr=None): + """Return True if string is valid""" + if not self.help.source_is_console(): + return True + if qstr is None: + qstr = self.currentText() + if not re.search('^[a-zA-Z0-9_\.]*$', str(qstr), 0): + return False + objtxt = to_text_string(qstr) + if self.help.get_option('automatic_import'): + shell = self.help.internal_shell + if shell is not None: + return shell.is_defined(objtxt, force_import=True) + shell = self.help.get_shell() + if shell is not None: + try: + return shell.is_defined(objtxt) + except socket.error: + shell = self.help.get_shell() + try: + return shell.is_defined(objtxt) + except socket.error: + # Well... too bad! + pass + + def validate_current_text(self): + self.validate(self.currentText()) + + def validate(self, qstr, editing=True): + """Reimplemented to avoid formatting actions""" + valid = self.is_valid(qstr) + if self.hasFocus() and valid is not None: + if editing: + # Combo box text is being modified: invalidate the entry + self.show_tip(self.tips[valid]) + self.valid.emit(False, False) + else: + # A new item has just been selected + if valid: + self.selected() + else: + self.valid.emit(False, False) + + +class HelpConfigPage(PluginConfigPage): + def setup_page(self): + # Connections group + connections_group = QGroupBox(_("Automatic connections")) + connections_label = QLabel(_("This pane can automatically " + "show an object's help information after " + "a left parenthesis is written next to it. " + "Below you can decide to which plugin " + "you want to connect it to turn on this " + "feature.")) + connections_label.setWordWrap(True) + editor_box = self.create_checkbox(_("Editor"), 'connect/editor') + rope_installed = programs.is_module_installed('rope') + jedi_installed = programs.is_module_installed('jedi', '>=0.8.1') + editor_box.setEnabled(rope_installed or jedi_installed) + if not rope_installed and not jedi_installed: + editor_tip = _("This feature requires the Rope or Jedi libraries.\n" + "It seems you don't have either installed.") + editor_box.setToolTip(editor_tip) + python_box = self.create_checkbox(_("Python Console"), + 'connect/python_console') + ipython_box = self.create_checkbox(_("IPython Console"), + 'connect/ipython_console') + ipython_box.setEnabled(QTCONSOLE_INSTALLED) + + connections_layout = QVBoxLayout() + connections_layout.addWidget(connections_label) + connections_layout.addWidget(editor_box) + connections_layout.addWidget(python_box) + connections_layout.addWidget(ipython_box) + connections_group.setLayout(connections_layout) + + # Features group + features_group = QGroupBox(_("Additional features")) + math_box = self.create_checkbox(_("Render mathematical equations"), + 'math') + req_sphinx = programs.is_module_installed('sphinx', '>=1.1') + math_box.setEnabled(req_sphinx) + if not req_sphinx: + sphinx_ver = programs.get_module_version('sphinx') + sphinx_tip = _("This feature requires Sphinx 1.1 or superior.") + sphinx_tip += "\n" + _("Sphinx %s is currently installed.") % sphinx_ver + math_box.setToolTip(sphinx_tip) + + features_layout = QVBoxLayout() + features_layout.addWidget(math_box) + features_group.setLayout(features_layout) + + # Source code group + sourcecode_group = QGroupBox(_("Source code")) + wrap_mode_box = self.create_checkbox(_("Wrap lines"), 'wrap') + + sourcecode_layout = QVBoxLayout() + sourcecode_layout.addWidget(wrap_mode_box) + sourcecode_group.setLayout(sourcecode_layout) + + # Final layout + vlayout = QVBoxLayout() + vlayout.addWidget(connections_group) + vlayout.addWidget(features_group) + vlayout.addWidget(sourcecode_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + +class RichText(QWidget): + """ + WebView widget with find dialog + """ + def __init__(self, parent): + QWidget.__init__(self, parent) + + self.webview = FrameWebView(self) + self.find_widget = FindReplace(self) + self.find_widget.set_editor(self.webview.web_widget) + self.find_widget.hide() + + layout = QVBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(self.webview) + layout.addWidget(self.find_widget) + self.setLayout(layout) + + def set_font(self, font, fixed_font=None): + """Set font""" + self.webview.set_font(font, fixed_font=fixed_font) + + def set_html(self, html_text, base_url): + """Set html text""" + self.webview.setHtml(html_text, base_url) + + def clear(self): + self.set_html('', self.webview.url()) + + +class PlainText(QWidget): + """ + Read-only editor widget with find dialog + """ + # Signals + focus_changed = Signal() + + def __init__(self, parent): + QWidget.__init__(self, parent) + self.editor = None + + # Read-only editor + self.editor = codeeditor.CodeEditor(self) + self.editor.setup_editor(linenumbers=False, language='py', + scrollflagarea=False, edge_line=False) + self.editor.focus_changed.connect(lambda: self.focus_changed.emit()) + self.editor.setReadOnly(True) + + # Find/replace widget + self.find_widget = FindReplace(self) + self.find_widget.set_editor(self.editor) + self.find_widget.hide() + + layout = QVBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(self.editor) + layout.addWidget(self.find_widget) + self.setLayout(layout) + + def set_font(self, font, color_scheme=None): + """Set font""" + self.editor.set_font(font, color_scheme=color_scheme) + + def set_color_scheme(self, color_scheme): + """Set color scheme""" + self.editor.set_color_scheme(color_scheme) + + def set_text(self, text, is_code): + self.editor.set_highlight_current_line(is_code) + self.editor.set_occurrence_highlighting(is_code) + if is_code: + self.editor.set_language('py') + else: + self.editor.set_language(None) + self.editor.set_text(text) + self.editor.set_cursor_position('sof') + + def clear(self): + self.editor.clear() + + +class SphinxThread(QThread): + """ + A worker thread for handling rich text rendering. + + Parameters + ---------- + doc : str or dict + A string containing a raw rst text or a dict containing + the doc string components to be rendered. + See spyder.utils.dochelpers.getdoc for description. + context : dict + A dict containing the substitution variables for the + layout template + html_text_no_doc : unicode + Text to be rendered if doc string cannot be extracted. + math_option : bool + Use LaTeX math rendering. + + """ + # Signals + error_msg = Signal(str) + html_ready = Signal(str) + + def __init__(self, html_text_no_doc=''): + super(SphinxThread, self).__init__() + self.doc = None + self.context = None + self.html_text_no_doc = html_text_no_doc + self.math_option = False + + def render(self, doc, context=None, math_option=False, img_path=''): + """Start thread to render a given documentation""" + # If the thread is already running wait for it to finish before + # starting it again. + if self.wait(): + self.doc = doc + self.context = context + self.math_option = math_option + self.img_path = img_path + # This causes run() to be executed in separate thread + self.start() + + def run(self): + html_text = self.html_text_no_doc + doc = self.doc + if doc is not None: + if type(doc) is dict and 'docstring' in doc.keys(): + try: + context = generate_context(name=doc['name'], + argspec=doc['argspec'], + note=doc['note'], + math=self.math_option, + img_path=self.img_path) + html_text = sphinxify(doc['docstring'], context) + if doc['docstring'] == '' and \ + any([doc['name'], doc['argspec'], doc['note']]): + msg = _("No further documentation available") + html_text += '
    ' + html_text += '
    %s
    ' % msg + except Exception as error: + self.error_msg.emit(to_text_string(error)) + return + elif self.context is not None: + try: + html_text = sphinxify(doc, self.context) + except Exception as error: + self.error_msg.emit(to_text_string(error)) + return + self.html_ready.emit(html_text) + + +class Help(SpyderPluginWidget): + """ + Docstrings viewer widget + """ + CONF_SECTION = 'help' + CONFIGWIDGET_CLASS = HelpConfigPage + LOG_PATH = get_conf_path(CONF_SECTION) + FONT_SIZE_DELTA = DEFAULT_SMALL_DELTA + + # Signals + focus_changed = Signal() + + def __init__(self, parent): + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) + + self.internal_shell = None + + # Initialize plugin + self.initialize_plugin() + + self.no_doc_string = _("No documentation available") + + self._last_console_cb = None + self._last_editor_cb = None + + self.plain_text = PlainText(self) + self.rich_text = RichText(self) + + color_scheme = self.get_color_scheme() + self.set_plain_text_font(self.get_plugin_font(), color_scheme) + self.plain_text.editor.toggle_wrap_mode(self.get_option('wrap')) + + # Add entries to read-only editor context-menu + self.wrap_action = create_action(self, _("Wrap lines"), + toggled=self.toggle_wrap_mode) + self.wrap_action.setChecked(self.get_option('wrap')) + self.plain_text.editor.readonly_menu.addSeparator() + add_actions(self.plain_text.editor.readonly_menu, (self.wrap_action,)) + + self.set_rich_text_font(self.get_plugin_font('rich_text')) + + self.shell = None + + # locked = disable link with Console + self.locked = False + self._last_texts = [None, None] + self._last_editor_doc = None + + # Object name + layout_edit = QHBoxLayout() + layout_edit.setContentsMargins(0, 0, 0, 0) + txt = _("Source") + if sys.platform == 'darwin': + source_label = QLabel(" " + txt) + else: + source_label = QLabel(txt) + layout_edit.addWidget(source_label) + self.source_combo = QComboBox(self) + self.source_combo.addItems([_("Console"), _("Editor")]) + self.source_combo.currentIndexChanged.connect(self.source_changed) + if (not programs.is_module_installed('rope') and + not programs.is_module_installed('jedi', '>=0.8.1')): + self.source_combo.hide() + source_label.hide() + layout_edit.addWidget(self.source_combo) + layout_edit.addSpacing(10) + layout_edit.addWidget(QLabel(_("Object"))) + self.combo = ObjectComboBox(self) + layout_edit.addWidget(self.combo) + self.object_edit = QLineEdit(self) + self.object_edit.setReadOnly(True) + layout_edit.addWidget(self.object_edit) + self.combo.setMaxCount(self.get_option('max_history_entries')) + self.combo.addItems( self.load_history() ) + self.combo.setItemText(0, '') + self.combo.valid.connect(lambda valid: self.force_refresh()) + + # Plain text docstring option + self.docstring = True + self.rich_help = self.get_option('rich_mode', True) + self.plain_text_action = create_action(self, _("Plain Text"), + toggled=self.toggle_plain_text) + + # Source code option + self.show_source_action = create_action(self, _("Show Source"), + toggled=self.toggle_show_source) + + # Rich text option + self.rich_text_action = create_action(self, _("Rich Text"), + toggled=self.toggle_rich_text) + + # Add the help actions to an exclusive QActionGroup + help_actions = QActionGroup(self) + help_actions.setExclusive(True) + help_actions.addAction(self.plain_text_action) + help_actions.addAction(self.rich_text_action) + + # Automatic import option + self.auto_import_action = create_action(self, _("Automatic import"), + toggled=self.toggle_auto_import) + auto_import_state = self.get_option('automatic_import') + self.auto_import_action.setChecked(auto_import_state) + + # Lock checkbox + self.locked_button = create_toolbutton(self, + triggered=self.toggle_locked) + layout_edit.addWidget(self.locked_button) + self._update_lock_icon() + + # Option menu + options_button = create_toolbutton(self, text=_('Options'), + icon=ima.icon('tooloptions')) + options_button.setPopupMode(QToolButton.InstantPopup) + menu = QMenu(self) + add_actions(menu, [self.rich_text_action, self.plain_text_action, + self.show_source_action, None, + self.auto_import_action]) + options_button.setMenu(menu) + layout_edit.addWidget(options_button) + + if self.rich_help: + self.switch_to_rich_text() + else: + self.switch_to_plain_text() + self.plain_text_action.setChecked(not self.rich_help) + self.rich_text_action.setChecked(self.rich_help) + self.source_changed() + + # Main layout + layout = QVBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.addLayout(layout_edit) + layout.addWidget(self.plain_text) + layout.addWidget(self.rich_text) + self.setLayout(layout) + + # Add worker thread for handling rich text rendering + self._sphinx_thread = SphinxThread( + html_text_no_doc=warning(self.no_doc_string)) + self._sphinx_thread.html_ready.connect( + self._on_sphinx_thread_html_ready) + self._sphinx_thread.error_msg.connect(self._on_sphinx_thread_error_msg) + + # Handle internal and external links + view = self.rich_text.webview + if not WEBENGINE: + view.page().setLinkDelegationPolicy(QWebEnginePage.DelegateAllLinks) + view.linkClicked.connect(self.handle_link_clicks) + + self._starting_up = True + + #------ SpyderPluginWidget API --------------------------------------------- + def on_first_registration(self): + """Action to be performed on first plugin registration""" + self.main.tabify_plugins(self.main.variableexplorer, self) + + def get_plugin_title(self): + """Return widget title""" + return _('Help') + + def get_plugin_icon(self): + """Return widget icon""" + return ima.icon('help') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + self.combo.lineEdit().selectAll() + return self.combo + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + return [] + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.focus_changed.connect(self.main.plugin_focus_changed) + self.main.add_dockwidget(self) + self.main.console.set_help(self) + self.internal_shell = self.main.console.shell + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + return True + + def refresh_plugin(self): + """Refresh widget""" + if self._starting_up: + self._starting_up = False + self.switch_to_rich_text() + self.show_intro_message() + + def update_font(self): + """Update font from Preferences""" + color_scheme = self.get_color_scheme() + font = self.get_plugin_font() + rich_font = self.get_plugin_font(rich_text=True) + + self.set_plain_text_font(font, color_scheme=color_scheme) + self.set_rich_text_font(rich_font) + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + color_scheme_n = 'color_scheme_name' + color_scheme_o = self.get_color_scheme() + connect_n = 'connect_to_oi' + wrap_n = 'wrap' + wrap_o = self.get_option(wrap_n) + self.wrap_action.setChecked(wrap_o) + math_n = 'math' + math_o = self.get_option(math_n) + + if color_scheme_n in options: + self.set_plain_text_color_scheme(color_scheme_o) + if wrap_n in options: + self.toggle_wrap_mode(wrap_o) + if math_n in options: + self.toggle_math_mode(math_o) + + # To make auto-connection changes take place instantly + self.main.editor.apply_plugin_settings(options=[connect_n]) + self.main.extconsole.apply_plugin_settings(options=[connect_n]) + if self.main.ipyconsole is not None: + self.main.ipyconsole.apply_plugin_settings(options=[connect_n]) + + #------ Public API (related to Help's source) ------------------------- + def source_is_console(self): + """Return True if source is Console""" + return self.source_combo.currentIndex() == 0 + + def switch_to_editor_source(self): + self.source_combo.setCurrentIndex(1) + + def switch_to_console_source(self): + self.source_combo.setCurrentIndex(0) + + def source_changed(self, index=None): + if self.source_is_console(): + # Console + self.combo.show() + self.object_edit.hide() + self.show_source_action.setEnabled(True) + self.auto_import_action.setEnabled(True) + else: + # Editor + self.combo.hide() + self.object_edit.show() + self.show_source_action.setDisabled(True) + self.auto_import_action.setDisabled(True) + self.restore_text() + + def save_text(self, callback): + if self.source_is_console(): + self._last_console_cb = callback + else: + self._last_editor_cb = callback + + def restore_text(self): + if self.source_is_console(): + cb = self._last_console_cb + else: + cb = self._last_editor_cb + if cb is None: + if self.is_plain_text_mode(): + self.plain_text.clear() + else: + self.rich_text.clear() + else: + func = cb[0] + args = cb[1:] + func(*args) + if get_meth_class_inst(func) is self.rich_text: + self.switch_to_rich_text() + else: + self.switch_to_plain_text() + + #------ Public API (related to rich/plain text widgets) -------------------- + @property + def find_widget(self): + if self.plain_text.isVisible(): + return self.plain_text.find_widget + else: + return self.rich_text.find_widget + + def set_rich_text_font(self, font): + """Set rich text mode font""" + self.rich_text.set_font(font, fixed_font=self.get_plugin_font()) + + def set_plain_text_font(self, font, color_scheme=None): + """Set plain text mode font""" + self.plain_text.set_font(font, color_scheme=color_scheme) + + def set_plain_text_color_scheme(self, color_scheme): + """Set plain text mode color scheme""" + self.plain_text.set_color_scheme(color_scheme) + + @Slot(bool) + def toggle_wrap_mode(self, checked): + """Toggle wrap mode""" + self.plain_text.editor.toggle_wrap_mode(checked) + self.set_option('wrap', checked) + + def toggle_math_mode(self, checked): + """Toggle math mode""" + self.set_option('math', checked) + + def is_plain_text_mode(self): + """Return True if plain text mode is active""" + return self.plain_text.isVisible() + + def is_rich_text_mode(self): + """Return True if rich text mode is active""" + return self.rich_text.isVisible() + + def switch_to_plain_text(self): + """Switch to plain text mode""" + self.rich_help = False + self.plain_text.show() + self.rich_text.hide() + self.plain_text_action.setChecked(True) + + def switch_to_rich_text(self): + """Switch to rich text mode""" + self.rich_help = True + self.plain_text.hide() + self.rich_text.show() + self.rich_text_action.setChecked(True) + self.show_source_action.setChecked(False) + + def set_plain_text(self, text, is_code): + """Set plain text docs""" + + # text is coming from utils.dochelpers.getdoc + if type(text) is dict: + name = text['name'] + if name: + rst_title = ''.join(['='*len(name), '\n', name, '\n', + '='*len(name), '\n\n']) + else: + rst_title = '' + + if text['argspec']: + definition = ''.join(['Definition: ', name, text['argspec'], + '\n']) + else: + definition = '' + + if text['note']: + note = ''.join(['Type: ', text['note'], '\n\n----\n\n']) + else: + note = '' + + full_text = ''.join([rst_title, definition, note, + text['docstring']]) + else: + full_text = text + + self.plain_text.set_text(full_text, is_code) + self.save_text([self.plain_text.set_text, full_text, is_code]) + + def set_rich_text_html(self, html_text, base_url): + """Set rich text""" + self.rich_text.set_html(html_text, base_url) + self.save_text([self.rich_text.set_html, html_text, base_url]) + + def show_intro_message(self): + intro_message = _("Here you can get help of any object by pressing " + "%s in front of it, either on the Editor or the " + "Console.%s" + "Help can also be shown automatically after writing " + "a left parenthesis next to an object. You can " + "activate this behavior in %s.") + prefs = _("Preferences > Help") + if sys.platform == 'darwin': + shortcut = "Cmd+I" + else: + shortcut = "Ctrl+I" + + if self.is_rich_text_mode(): + title = _("Usage") + tutorial_message = _("New to Spyder? Read our") + tutorial = _("tutorial") + intro_message = intro_message % (""+shortcut+"", "

    ", + ""+prefs+"") + self.set_rich_text_html(usage(title, intro_message, + tutorial_message, tutorial), + QUrl.fromLocalFile(CSS_PATH)) + else: + install_sphinx = "\n\n%s" % _("Please consider installing Sphinx " + "to get documentation rendered in " + "rich text.") + intro_message = intro_message % (shortcut, "\n\n", prefs) + intro_message += install_sphinx + self.set_plain_text(intro_message, is_code=False) + + def show_rich_text(self, text, collapse=False, img_path=''): + """Show text in rich mode""" + self.visibility_changed(True) + self.raise_() + self.switch_to_rich_text() + context = generate_context(collapse=collapse, img_path=img_path) + self.render_sphinx_doc(text, context) + + def show_plain_text(self, text): + """Show text in plain mode""" + self.visibility_changed(True) + self.raise_() + self.switch_to_plain_text() + self.set_plain_text(text, is_code=False) + + @Slot() + def show_tutorial(self): + tutorial_path = get_module_source_path('spyder.utils.help') + tutorial = osp.join(tutorial_path, 'tutorial.rst') + text = open(tutorial).read() + self.show_rich_text(text, collapse=True) + + def handle_link_clicks(self, url): + url = to_text_string(url.toString()) + if url == "spy://tutorial": + self.show_tutorial() + elif url.startswith('http'): + programs.start_file(url) + else: + self.rich_text.webview.load(QUrl(url)) + + #------ Public API --------------------------------------------------------- + def force_refresh(self): + if self.source_is_console(): + self.set_object_text(None, force_refresh=True) + elif self._last_editor_doc is not None: + self.set_editor_doc(self._last_editor_doc, force_refresh=True) + + def set_object_text(self, text, force_refresh=False, ignore_unknown=False): + """Set object analyzed by Help""" + if (self.locked and not force_refresh): + return + self.switch_to_console_source() + + add_to_combo = True + if text is None: + text = to_text_string(self.combo.currentText()) + add_to_combo = False + + found = self.show_help(text, ignore_unknown=ignore_unknown) + if ignore_unknown and not found: + return + + if add_to_combo: + self.combo.add_text(text) + if found: + self.save_history() + + if self.dockwidget is not None: + self.dockwidget.blockSignals(True) + self.__eventually_raise_help(text, force=force_refresh) + if self.dockwidget is not None: + self.dockwidget.blockSignals(False) + + def set_editor_doc(self, doc, force_refresh=False): + """ + Use the help plugin to show docstring dictionary computed + with introspection plugin from the Editor plugin + """ + if (self.locked and not force_refresh): + return + self.switch_to_editor_source() + self._last_editor_doc = doc + self.object_edit.setText(doc['obj_text']) + + if self.rich_help: + self.render_sphinx_doc(doc) + else: + self.set_plain_text(doc, is_code=False) + + if self.dockwidget is not None: + self.dockwidget.blockSignals(True) + self.__eventually_raise_help(doc['docstring'], force=force_refresh) + if self.dockwidget is not None: + self.dockwidget.blockSignals(False) + + def __eventually_raise_help(self, text, force=False): + index = self.source_combo.currentIndex() + if hasattr(self.main, 'tabifiedDockWidgets'): + # 'QMainWindow.tabifiedDockWidgets' was introduced in PyQt 4.5 + if self.dockwidget and (force or self.dockwidget.isVisible()) \ + and not self.ismaximized \ + and (force or text != self._last_texts[index]): + dockwidgets = self.main.tabifiedDockWidgets(self.dockwidget) + if self.main.console.dockwidget not in dockwidgets and \ + (hasattr(self.main, 'extconsole') and \ + self.main.extconsole.dockwidget not in dockwidgets): + self.dockwidget.show() + self.dockwidget.raise_() + self._last_texts[index] = text + + def load_history(self, obj=None): + """Load history from a text file in user home directory""" + if osp.isfile(self.LOG_PATH): + history = [line.replace('\n', '') + for line in open(self.LOG_PATH, 'r').readlines()] + else: + history = [] + return history + + def save_history(self): + """Save history to a text file in user home directory""" + open(self.LOG_PATH, 'w').write("\n".join( \ + [to_text_string(self.combo.itemText(index)) + for index in range(self.combo.count())] )) + + @Slot(bool) + def toggle_plain_text(self, checked): + """Toggle plain text docstring""" + if checked: + self.docstring = checked + self.switch_to_plain_text() + self.force_refresh() + self.set_option('rich_mode', not checked) + + @Slot(bool) + def toggle_show_source(self, checked): + """Toggle show source code""" + if checked: + self.switch_to_plain_text() + self.docstring = not checked + self.force_refresh() + self.set_option('rich_mode', not checked) + + @Slot(bool) + def toggle_rich_text(self, checked): + """Toggle between sphinxified docstrings or plain ones""" + if checked: + self.docstring = not checked + self.switch_to_rich_text() + self.set_option('rich_mode', checked) + + @Slot(bool) + def toggle_auto_import(self, checked): + """Toggle automatic import feature""" + self.combo.validate_current_text() + self.set_option('automatic_import', checked) + self.force_refresh() + + @Slot() + def toggle_locked(self): + """ + Toggle locked state + locked = disable link with Console + """ + self.locked = not self.locked + self._update_lock_icon() + + def _update_lock_icon(self): + """Update locked state icon""" + icon = ima.icon('lock') if self.locked else ima.icon('lock_open') + self.locked_button.setIcon(icon) + tip = _("Unlock") if self.locked else _("Lock") + self.locked_button.setToolTip(tip) + + def set_shell(self, shell): + """Bind to shell""" + self.shell = shell + + def get_shell(self): + """ + Return shell which is currently bound to Help, + or another running shell if it has been terminated + """ + if not hasattr(self.shell, 'get_doc') or not self.shell.is_running(): + self.shell = None + if self.main.ipyconsole is not None: + shell = self.main.ipyconsole.get_current_shellwidget() + if shell is not None and shell.kernel_client is not None: + self.shell = shell + if self.shell is None and self.main.extconsole is not None: + self.shell = self.main.extconsole.get_running_python_shell() + if self.shell is None: + self.shell = self.internal_shell + return self.shell + + def render_sphinx_doc(self, doc, context=None): + """Transform doc string dictionary to HTML and show it""" + # Math rendering option could have changed + fname = self.parent().parent().editor.get_current_filename() + dname = osp.dirname(fname) + self._sphinx_thread.render(doc, context, self.get_option('math'), + dname) + + def _on_sphinx_thread_html_ready(self, html_text): + """Set our sphinx documentation based on thread result""" + self._sphinx_thread.wait() + self.set_rich_text_html(html_text, QUrl.fromLocalFile(CSS_PATH)) + + def _on_sphinx_thread_error_msg(self, error_msg): + """ Display error message on Sphinx rich text failure""" + self._sphinx_thread.wait() + self.plain_text_action.setChecked(True) + sphinx_ver = programs.get_module_version('sphinx') + QMessageBox.critical(self, + _('Help'), + _("The following error occured when calling " + "Sphinx %s.
    Incompatible Sphinx " + "version or doc string decoding failed." + "

    Error message:
    %s" + ) % (sphinx_ver, error_msg)) + + def show_help(self, obj_text, ignore_unknown=False): + """Show help""" + shell = self.get_shell() + if shell is None: + return + obj_text = to_text_string(obj_text) + + if not shell.is_defined(obj_text): + if self.get_option('automatic_import') and \ + self.internal_shell.is_defined(obj_text, force_import=True): + shell = self.internal_shell + else: + shell = None + doc = None + source_text = None + + if shell is not None: + doc = shell.get_doc(obj_text) + source_text = shell.get_source(obj_text) + + is_code = False + + if self.rich_help: + self.render_sphinx_doc(doc) + return doc is not None + elif self.docstring: + hlp_text = doc + if hlp_text is None: + hlp_text = source_text + if hlp_text is None: + hlp_text = self.no_doc_string + if ignore_unknown: + return False + else: + hlp_text = source_text + if hlp_text is None: + hlp_text = doc + if hlp_text is None: + hlp_text = _("No source code available.") + if ignore_unknown: + return False + else: + is_code = True + self.set_plain_text(hlp_text, is_code=is_code) + return True diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/history.py spyder-3.0.2+dfsg1/spyder/plugins/history.py --- spyder-2.3.8+dfsg1/spyder/plugins/history.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/history.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,274 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Console History Plugin""" + +# Standard library imports +import os.path as osp +import sys + +# Third party imports +from qtpy import PYQT5 +from qtpy.QtCore import Signal, Slot +from qtpy.QtWidgets import (QGroupBox, QHBoxLayout, QInputDialog, QMenu, + QToolButton, QVBoxLayout, QWidget) + + +# Local imports +from spyder.utils import encoding +from spyder.config.base import _ +from spyder.plugins import SpyderPluginWidget +from spyder.plugins.configdialog import PluginConfigPage +from spyder.py3compat import is_text_string, to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import (add_actions, create_action, + create_toolbutton) +from spyder.widgets.tabs import Tabs +from spyder.widgets.sourcecode import codeeditor +from spyder.widgets.findreplace import FindReplace + + +class HistoryConfigPage(PluginConfigPage): + def get_icon(self): + return ima.icon('history') + + def setup_page(self): + settings_group = QGroupBox(_("Settings")) + hist_spin = self.create_spinbox( + _("History depth: "), _(" entries"), + 'max_entries', min_=10, max_=10000, step=10, + tip=_("Set maximum line count")) + + sourcecode_group = QGroupBox(_("Source code")) + wrap_mode_box = self.create_checkbox(_("Wrap lines"), 'wrap') + go_to_eof_box = self.create_checkbox( + _("Scroll automatically to last entry"), 'go_to_eof') + + settings_layout = QVBoxLayout() + settings_layout.addWidget(hist_spin) + settings_group.setLayout(settings_layout) + + sourcecode_layout = QVBoxLayout() + sourcecode_layout.addWidget(wrap_mode_box) + sourcecode_layout.addWidget(go_to_eof_box) + sourcecode_group.setLayout(sourcecode_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(settings_group) + vlayout.addWidget(sourcecode_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + +class HistoryLog(SpyderPluginWidget): + """ + History log widget + """ + CONF_SECTION = 'historylog' + CONFIGWIDGET_CLASS = HistoryConfigPage + focus_changed = Signal() + + def __init__(self, parent): + self.tabwidget = None + self.menu_actions = None + self.dockviewer = None + self.wrap_action = None + + self.editors = [] + self.filenames = [] + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + + layout = QVBoxLayout() + self.tabwidget = Tabs(self, self.menu_actions) + self.tabwidget.currentChanged.connect(self.refresh_plugin) + self.tabwidget.move_data.connect(self.move_tab) + + if sys.platform == 'darwin': + tab_container = QWidget() + tab_container.setObjectName('tab-container') + tab_layout = QHBoxLayout(tab_container) + tab_layout.setContentsMargins(0, 0, 0, 0) + tab_layout.addWidget(self.tabwidget) + layout.addWidget(tab_container) + else: + layout.addWidget(self.tabwidget) + + # Menu as corner widget + options_button = create_toolbutton(self, text=_('Options'), + icon=ima.icon('tooloptions')) + options_button.setPopupMode(QToolButton.InstantPopup) + menu = QMenu(self) + add_actions(menu, self.menu_actions) + options_button.setMenu(menu) + self.tabwidget.setCornerWidget(options_button) + + # Find/replace widget + self.find_widget = FindReplace(self) + self.find_widget.hide() + self.register_widget_shortcuts(self.find_widget) + + layout.addWidget(self.find_widget) + + self.setLayout(layout) + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _('History log') + + def get_plugin_icon(self): + """Return widget icon""" + return ima.icon('history') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.tabwidget.currentWidget() + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + return True + + def refresh_plugin(self): + """Refresh tabwidget""" + if self.tabwidget.count(): + editor = self.tabwidget.currentWidget() + else: + editor = None + self.find_widget.set_editor(editor) + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + history_action = create_action(self, _("History..."), + None, ima.icon('history'), + _("Set history maximum entries"), + triggered=self.change_history_depth) + self.wrap_action = create_action(self, _("Wrap lines"), + toggled=self.toggle_wrap_mode) + self.wrap_action.setChecked( self.get_option('wrap') ) + self.menu_actions = [history_action, self.wrap_action] + return self.menu_actions + + def on_first_registration(self): + """Action to be performed on first plugin registration""" + self.main.tabify_plugins(self.main.extconsole, self) + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.focus_changed.connect(self.main.plugin_focus_changed) + self.main.add_dockwidget(self) +# self.main.console.set_historylog(self) + self.main.console.shell.refresh.connect(self.refresh_plugin) + + def update_font(self): + """Update font from Preferences""" + color_scheme = self.get_color_scheme() + font = self.get_plugin_font() + for editor in self.editors: + editor.set_font(font, color_scheme) + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + color_scheme_n = 'color_scheme_name' + color_scheme_o = self.get_color_scheme() + font_n = 'plugin_font' + font_o = self.get_plugin_font() + wrap_n = 'wrap' + wrap_o = self.get_option(wrap_n) + self.wrap_action.setChecked(wrap_o) + for editor in self.editors: + if font_n in options: + scs = color_scheme_o if color_scheme_n in options else None + editor.set_font(font_o, scs) + elif color_scheme_n in options: + editor.set_color_scheme(color_scheme_o) + if wrap_n in options: + editor.toggle_wrap_mode(wrap_o) + + #------ Private API -------------------------------------------------------- + def move_tab(self, index_from, index_to): + """ + Move tab (tabs themselves have already been moved by the tabwidget) + """ + filename = self.filenames.pop(index_from) + editor = self.editors.pop(index_from) + + self.filenames.insert(index_to, filename) + self.editors.insert(index_to, editor) + + #------ Public API --------------------------------------------------------- + def add_history(self, filename): + """ + Add new history tab + Slot for add_history signal emitted by shell instance + """ + filename = encoding.to_unicode_from_fs(filename) + if filename in self.filenames: + return + editor = codeeditor.CodeEditor(self) + if osp.splitext(filename)[1] == '.py': + language = 'py' + else: + language = 'bat' + editor.setup_editor(linenumbers=False, language=language, + scrollflagarea=False) + editor.focus_changed.connect(lambda: self.focus_changed.emit()) + editor.setReadOnly(True) + color_scheme = self.get_color_scheme() + editor.set_font( self.get_plugin_font(), color_scheme ) + editor.toggle_wrap_mode( self.get_option('wrap') ) + + text, _ = encoding.read(filename) + editor.set_text(text) + editor.set_cursor_position('eof') + + self.editors.append(editor) + self.filenames.append(filename) + index = self.tabwidget.addTab(editor, osp.basename(filename)) + self.find_widget.set_editor(editor) + self.tabwidget.setTabToolTip(index, filename) + self.tabwidget.setCurrentIndex(index) + + def append_to_history(self, filename, command): + """ + Append an entry to history filename + Slot for append_to_history signal emitted by shell instance + """ + if not is_text_string(filename): # filename is a QString + filename = to_text_string(filename.toUtf8(), 'utf-8') + command = to_text_string(command) + index = self.filenames.index(filename) + self.editors[index].append(command) + if self.get_option('go_to_eof'): + self.editors[index].set_cursor_position('eof') + self.tabwidget.setCurrentIndex(index) + + @Slot() + def change_history_depth(self): + "Change history max entries""" + depth, valid = QInputDialog.getInt(self, _('History'), + _('Maximum entries'), + self.get_option('max_entries'), + 10, 10000) + if valid: + self.set_option('max_entries', depth) + + @Slot(bool) + def toggle_wrap_mode(self, checked): + """Toggle wrap mode""" + if self.tabwidget is None: + return + for editor in self.editors: + editor.toggle_wrap_mode(checked) + self.set_option('wrap', checked) diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/__init__.py spyder-3.0.2+dfsg1/spyder/plugins/__init__.py --- spyder-2.3.8+dfsg1/spyder/plugins/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/__init__.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,593 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.plugins +============== + +Here, 'plugins' are widgets designed specifically for Spyder +These plugins inherit the following classes +(SpyderPluginMixin & SpyderPluginWidget) +""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import inspect +import os + +# Third party imports +from qtpy import PYQT5 +from qtpy.QtCore import QEvent, QObject, QPoint, Qt, Signal +from qtpy.QtGui import QCursor, QKeySequence +from qtpy.QtWidgets import (QApplication, QDockWidget, QMainWindow, + QShortcut, QTabBar, QWidget) + +# Local imports +from spyder.config.base import _ +from spyder.config.gui import get_color_scheme, get_font +from spyder.config.main import CONF +from spyder.config.user import NoDefault +from spyder.py3compat import configparser, is_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import create_action, toggle_actions + + +class TabFilter(QObject): + """ + Filter event attached to each QTabBar that holds 2 or more dockwidgets in + charge of handling tab rearangement. + + This filter also holds the methods needed for the detection of a drag and + the movement of tabs. + """ + def __init__(self, dock_tabbar, main): + QObject.__init__(self) + self.dock_tabbar = dock_tabbar + self.main = main + self.moving = False + self.from_index = None + self.to_index = None + + # Helper methods + def _get_plugin(self, index): + """Get plugin reference based on tab index.""" + for plugin in self.main.widgetlist: + tab_text = self.dock_tabbar.tabText(index).replace('&', '') + if plugin.get_plugin_title() == tab_text: + return plugin + + def _get_plugins(self): + """ + Get a list of all plugin references in the QTabBar to which this + event filter is attached. + """ + plugins = [] + for index in range(self.dock_tabbar.count()): + plugin = self._get_plugin(index) + plugins.append(plugin) + return plugins + + def _fix_cursor(self, from_index, to_index): + """Fix mouse cursor position to adjust for different tab sizes.""" + # The direction is +1 (moving to the right) or -1 (moving to the left) + direction = abs(to_index - from_index)/(to_index - from_index) + + tab_width = self.dock_tabbar.tabRect(to_index).width() + tab_x_min = self.dock_tabbar.tabRect(to_index).x() + tab_x_max = tab_x_min + tab_width + previous_width = self.dock_tabbar.tabRect(to_index - direction).width() + + delta = previous_width - tab_width + if delta > 0: + delta = delta * direction + else: + delta = 0 + cursor = QCursor() + pos = self.dock_tabbar.mapFromGlobal(cursor.pos()) + x, y = pos.x(), pos.y() + if x < tab_x_min or x > tab_x_max: + new_pos = self.dock_tabbar.mapToGlobal(QPoint(x + delta, y)) + cursor.setPos(new_pos) + + def eventFilter(self, obj, event): + """Filter mouse press events. + + Events that are captured and not propagated return True. Events that + are not captured and are propagated return False. + """ + event_type = event.type() + if event_type == QEvent.MouseButtonPress: + self.tab_pressed(event) + return False + if event_type == QEvent.MouseMove: + self.tab_moved(event) + return True + if event_type == QEvent.MouseButtonRelease: + self.tab_released(event) + return True + return False + + def tab_pressed(self, event): + """Method called when a tab from a QTabBar has been pressed.""" + self.from_index = self.dock_tabbar.tabAt(event.pos()) + self.dock_tabbar.setCurrentIndex(self.from_index) + + if event.button() == Qt.RightButton: + if self.from_index == -1: + self.show_nontab_menu(event) + else: + self.show_tab_menu(event) + + def tab_moved(self, event): + """Method called when a tab from a QTabBar has been moved.""" + # If the left button isn't pressed anymore then return + if not event.buttons() & Qt.LeftButton: + self.to_index = None + return + + self.to_index = self.dock_tabbar.tabAt(event.pos()) + + if not self.moving and self.from_index != -1 and self.to_index != -1: + QApplication.setOverrideCursor(Qt.ClosedHandCursor) + self.moving = True + + if self.to_index == -1: + self.to_index = self.from_index + + from_index, to_index = self.from_index, self.to_index + if from_index != to_index and from_index != -1 and to_index != -1: + self.move_tab(from_index, to_index) + self._fix_cursor(from_index, to_index) + self.from_index = to_index + + def tab_released(self, event): + """Method called when a tab from a QTabBar has been released.""" + QApplication.restoreOverrideCursor() + self.moving = False + + def move_tab(self, from_index, to_index): + """Move a tab from a given index to a given index position.""" + plugins = self._get_plugins() + from_plugin = self._get_plugin(from_index) + to_plugin = self._get_plugin(to_index) + + from_idx = plugins.index(from_plugin) + to_idx = plugins.index(to_plugin) + + plugins[from_idx], plugins[to_idx] = plugins[to_idx], plugins[from_idx] + + for i in range(len(plugins)-1): + self.main.tabify_plugins(plugins[i], plugins[i+1]) + from_plugin.dockwidget.raise_() + + def show_tab_menu(self, event): + """Show the context menu assigned to tabs.""" + self.show_nontab_menu(event) + + def show_nontab_menu(self, event): + """Show the context menu assigned to nontabs section.""" + menu = self.main.createPopupMenu() + menu.exec_(self.dock_tabbar.mapToGlobal(event.pos())) + + +class SpyderDockWidget(QDockWidget): + """Subclass to override needed methods""" + plugin_closed = Signal() + + def __init__(self, title, parent): + super(SpyderDockWidget, self).__init__(title, parent) + + # Needed for the installation of the event filter + self.title = title + self.main = parent + self.dock_tabbar = None + + # To track dockwidget changes the filter is installed when dockwidget + # visibility changes. This installs the filter on startup and also + # on dockwidgets that are undocked and then docked to a new location. + self.visibilityChanged.connect(self.install_tab_event_filter) + + def closeEvent(self, event): + """ + Reimplement Qt method to send a signal on close so that "Panes" main + window menu can be updated correctly + """ + self.plugin_closed.emit() + + def install_tab_event_filter(self, value): + """ + Install an event filter to capture mouse events in the tabs of a + QTabBar holding tabified dockwidgets. + """ + dock_tabbar = None + tabbars = self.main.findChildren(QTabBar) + for tabbar in tabbars: + for tab in range(tabbar.count()): + title = tabbar.tabText(tab) + if title == self.title: + dock_tabbar = tabbar + break + if dock_tabbar is not None: + self.dock_tabbar = dock_tabbar + # Install filter only once per QTabBar + if getattr(self.dock_tabbar, 'filter', None) is None: + self.dock_tabbar.filter = TabFilter(self.dock_tabbar, + self.main) + self.dock_tabbar.installEventFilter(self.dock_tabbar.filter) + + +class SpyderPluginMixin(object): + """ + Useful methods to bind widgets to the main window + See SpyderPluginWidget class for required widget interface + + Signals: + * sig_option_changed + Example: + plugin.sig_option_changed.emit('show_all', checked) + * show_message + * update_plugin_title + """ + CONF_SECTION = None + CONFIGWIDGET_CLASS = None + FONT_SIZE_DELTA = 0 + RICH_FONT_SIZE_DELTA = 0 + IMG_PATH = 'images' + ALLOWED_AREAS = Qt.AllDockWidgetAreas + LOCATION = Qt.LeftDockWidgetArea + FEATURES = QDockWidget.DockWidgetClosable | QDockWidget.DockWidgetFloatable + DISABLE_ACTIONS_WHEN_HIDDEN = True + + # Signals + sig_option_changed = None + show_message = None + update_plugin_title = None + + def __init__(self, main=None, **kwds): + """Bind widget to a QMainWindow instance""" + super(SpyderPluginMixin, self).__init__(**kwds) + assert self.CONF_SECTION is not None + self.PLUGIN_PATH = os.path.dirname(inspect.getfile(self.__class__)) + self.main = main + self.default_margins = None + self.plugin_actions = None + self.dockwidget = None + self.mainwindow = None + self.ismaximized = False + self.isvisible = False + + # NOTE: Don't use the default option of CONF.get to assign a + # None shortcut to plugins that don't have one. That will mess + # the creation of our Keyboard Shortcuts prefs page + try: + self.shortcut = CONF.get('shortcuts', '_/switch to %s' % \ + self.CONF_SECTION) + except configparser.NoOptionError: + self.shortcut = None + + # We decided to create our own toggle action instead of using + # the one that comes with dockwidget because it's not possible + # to raise and focus the plugin with it. + self.toggle_view_action = None + + def initialize_plugin(self): + """Initialize plugin: connect signals, setup actions, ...""" + self.create_toggle_view_action() + self.plugin_actions = self.get_plugin_actions() + if self.show_message is not None: + self.show_message.connect(self.__show_message) + if self.update_plugin_title is not None: + self.update_plugin_title.connect(self.__update_plugin_title) + if self.sig_option_changed is not None: + self.sig_option_changed.connect(self.set_option) + self.setWindowTitle(self.get_plugin_title()) + + def on_first_registration(self): + """Action to be performed on first plugin registration""" + # Was written to handle the very first plugin position in Spyder's + # main window layout, but this could also be used for other things + # (see for example the IPython console plugin for which this method + # had to be written to handle the fact that this plugin was + # introduced between v2.1 and v2.2) + raise NotImplementedError + + def initialize_plugin_in_mainwindow_layout(self): + """If this is the first time the plugin is shown, perform actions to + initialize plugin position in Spyder's window layout""" + if self.get_option('first_time', True): + try: + self.on_first_registration() + except NotImplementedError: + return + self.set_option('first_time', False) + + def update_margins(self): + layout = self.layout() + if self.default_margins is None: + self.default_margins = layout.getContentsMargins() + if CONF.get('main', 'use_custom_margin'): + margin = CONF.get('main', 'custom_margin') + layout.setContentsMargins(*[margin]*4) + else: + layout.setContentsMargins(*self.default_margins) + + def __update_plugin_title(self): + """Update plugin title, i.e. dockwidget or mainwindow title""" + if self.dockwidget is not None: + win = self.dockwidget + elif self.mainwindow is not None: + win = self.mainwindow + else: + return + win.setWindowTitle(self.get_plugin_title()) + + def create_dockwidget(self): + """Add to parent QMainWindow as a dock widget""" + + # This is not clear yet why the following do not work... + # (see Issue #880) +## # Using Qt.Window window flags solves Issue #880 (detached dockwidgets +## # are not painted after restarting Spyder and restoring their hexstate) +## # but it does not work with PyQt <=v4.7 (dockwidgets can't be docked) +## # or non-Windows platforms (lot of warnings are printed out) +## # (so in those cases, we use the default window flags: Qt.Widget): +## flags = Qt.Widget if is_old_pyqt or os.name != 'nt' else Qt.Window + dock = SpyderDockWidget(self.get_plugin_title(), self.main)#, flags) + + dock.setObjectName(self.__class__.__name__+"_dw") + dock.setAllowedAreas(self.ALLOWED_AREAS) + dock.setFeatures(self.FEATURES) + dock.setWidget(self) + self.update_margins() + dock.visibilityChanged.connect(self.visibility_changed) + dock.plugin_closed.connect(self.plugin_closed) + self.dockwidget = dock + if self.shortcut is not None: + sc = QShortcut(QKeySequence(self.shortcut), self.main, + self.switch_to_plugin) + self.register_shortcut(sc, "_", "Switch to %s" % self.CONF_SECTION) + return (dock, self.LOCATION) + + def create_mainwindow(self): + """ + Create a QMainWindow instance containing this plugin + Note: this method is currently not used + """ + self.mainwindow = mainwindow = QMainWindow() + mainwindow.setAttribute(Qt.WA_DeleteOnClose) + icon = self.get_plugin_icon() + if is_text_string(icon): + icon = self.get_icon(icon) + mainwindow.setWindowIcon(icon) + mainwindow.setWindowTitle(self.get_plugin_title()) + mainwindow.setCentralWidget(self) + self.refresh_plugin() + return mainwindow + + def create_configwidget(self, parent): + """Create configuration dialog box page widget""" + if self.CONFIGWIDGET_CLASS is not None: + configwidget = self.CONFIGWIDGET_CLASS(self, parent) + configwidget.initialize() + return configwidget + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + raise NotImplementedError + + def register_shortcut(self, qaction_or_qshortcut, context, name, + add_sc_to_tip=False): + """ + Register QAction or QShortcut to Spyder main application + + if add_sc_to_tip is True, the shortcut is added to the + action's tooltip + """ + self.main.register_shortcut(qaction_or_qshortcut, context, + name, add_sc_to_tip) + + def register_widget_shortcuts(self, widget): + """ + Register widget shortcuts + widget interface must have a method called 'get_shortcut_data' + """ + for qshortcut, context, name in widget.get_shortcut_data(): + self.register_shortcut(qshortcut, context, name) + + def switch_to_plugin(self): + """Switch to plugin + This method is called when pressing plugin's shortcut key""" + if not self.ismaximized: + self.dockwidget.show() + if not self.toggle_view_action.isChecked(): + self.toggle_view_action.setChecked(True) + self.visibility_changed(True) + + def visibility_changed(self, enable): + """DockWidget visibility has changed""" + if enable: + self.dockwidget.raise_() + widget = self.get_focus_widget() + if widget is not None: + widget.setFocus() + visible = self.dockwidget.isVisible() or self.ismaximized + if self.DISABLE_ACTIONS_WHEN_HIDDEN: + toggle_actions(self.plugin_actions, visible) + self.isvisible = enable and visible + if self.isvisible: + self.refresh_plugin() # To give focus to the plugin's widget + + def plugin_closed(self): + """DockWidget was closed""" + self.toggle_view_action.setChecked(False) + + def set_option(self, option, value): + """ + Set a plugin option in configuration file + Use a SIGNAL to call it, e.g.: + plugin.sig_option_changed.emit('show_all', checked) + """ + CONF.set(self.CONF_SECTION, str(option), value) + + def get_option(self, option, default=NoDefault): + """Get a plugin option from configuration file""" + return CONF.get(self.CONF_SECTION, option, default) + + def get_plugin_font(self, rich_text=False): + """ + Return plugin font option. + + All plugins in Spyder use a global font. This is a convenience method + in case some plugins will have a delta size based on the default size. + """ + + if rich_text: + option = 'rich_font' + font_size_delta = self.RICH_FONT_SIZE_DELTA + else: + option = 'font' + font_size_delta = self.FONT_SIZE_DELTA + + return get_font(option=option, font_size_delta=font_size_delta) + + def set_plugin_font(self): + """ + Set plugin font option. + + Note: All plugins in Spyder use a global font. To define a different + size, the plugin must define a 'FONT_SIZE_DELTA' class variable. + """ + raise Exception("Plugins font is based on the general settings, " + "and cannot be set directly on the plugin." + "This method is deprecated.") + + def update_font(self): + """ + This has to be reimplemented by plugins that need to adjust + their fonts + """ + pass + + def __show_message(self, message, timeout=0): + """Show message in main window's status bar""" + self.main.statusBar().showMessage(message, timeout) + + def starting_long_process(self, message): + """ + Showing message in main window's status bar + and changing mouse cursor to Qt.WaitCursor + """ + self.__show_message(message) + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + QApplication.processEvents() + + def ending_long_process(self, message=""): + """ + Clearing main window's status bar + and restoring mouse cursor + """ + QApplication.restoreOverrideCursor() + self.__show_message(message, timeout=2000) + QApplication.processEvents() + + def get_color_scheme(self): + """Get current color scheme""" + return get_color_scheme(CONF.get('color_schemes', 'selected')) + + def create_toggle_view_action(self): + """Associate a toggle view action with each plugin""" + title = self.get_plugin_title() + if self.CONF_SECTION == 'editor': + title = _('Editor') + if self.shortcut is not None: + action = create_action(self, title, + toggled=lambda checked: self.toggle_view(checked), + shortcut=QKeySequence(self.shortcut), + context=Qt.WidgetShortcut) + else: + action = create_action(self, title, toggled=lambda checked: + self.toggle_view(checked)) + self.toggle_view_action = action + + def toggle_view(self, checked): + """Toggle view""" + if not self.dockwidget: + return + if checked: + self.dockwidget.show() + self.dockwidget.raise_() + else: + self.dockwidget.hide() + + +class SpyderPluginWidget(QWidget, SpyderPluginMixin): + """ + Spyder base widget class + Spyder's widgets either inherit this class or reimplement its interface + """ + sig_option_changed = Signal(str, object) + show_message = Signal(str, int) + update_plugin_title = Signal() + + if PYQT5: + def __init__(self, parent, **kwds): + super(SpyderPluginWidget, self).__init__(parent, **kwds) + else: + def __init__(self, parent): + QWidget.__init__(self, parent) + SpyderPluginMixin.__init__(self, parent) + + def get_plugin_title(self): + """ + Return plugin title + Note: after some thinking, it appears that using a method + is more flexible here than using a class attribute + """ + raise NotImplementedError + + def get_plugin_icon(self): + """ + Return plugin icon (QIcon instance) + Note: this is required for plugins creating a main window + (see SpyderPluginMixin.create_mainwindow) + and for configuration dialog widgets creation + """ + return ima.icon('outline_explorer') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + pass + + def closing_plugin(self, cancelable=False): + """ + Perform actions before parent main window is closed + Return True or False whether the plugin may be closed immediately or not + Note: returned value is ignored if *cancelable* is False + """ + return True + + def refresh_plugin(self): + """Refresh widget""" + raise NotImplementedError + + def get_plugin_actions(self): + """ + Return a list of actions related to plugin + Note: these actions will be enabled when plugin's dockwidget is visible + and they will be disabled when it's hidden + """ + raise NotImplementedError + + def register_plugin(self): + """Register plugin in Spyder's main window""" + raise NotImplementedError diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/ipythonconsole.py spyder-3.0.2+dfsg1/spyder/plugins/ipythonconsole.py --- spyder-2.3.8+dfsg1/spyder/plugins/ipythonconsole.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/ipythonconsole.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,1461 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +IPython Console plugin based on QtConsole +""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import atexit +import os +import os.path as osp +import uuid +import sys + +# Third party imports +from jupyter_client.connect import find_connection_file +from jupyter_client.kernelspec import KernelSpec +from jupyter_core.paths import jupyter_config_dir, jupyter_runtime_dir +from qtconsole.client import QtKernelClient +from qtconsole.manager import QtKernelManager +from qtpy import PYQT5 +from qtpy.compat import getopenfilename +from qtpy.QtCore import Qt, Signal, Slot +from qtpy.QtGui import QKeySequence +from qtpy.QtWidgets import (QApplication, QCheckBox, QDialog, QDialogButtonBox, + QFormLayout, QGridLayout, QGroupBox, QHBoxLayout, + QLabel, QLineEdit, QMessageBox, QPushButton, + QTabWidget, QVBoxLayout, QWidget) +from traitlets.config.loader import Config, load_pyconfig_files +from zmq.ssh import tunnel as zmqtunnel +try: + import pexpect +except ImportError: + pexpect = None + +# Local imports +from spyder import dependencies +from spyder.config.base import (_, DEV, get_home_dir, get_module_path, + get_module_source_path) +from spyder.config.main import CONF +from spyder.plugins import SpyderPluginWidget +from spyder.plugins.configdialog import PluginConfigPage +from spyder.py3compat import (iteritems, PY2, to_binary_string, + to_text_string) +from spyder.utils.qthelpers import create_action +from spyder.utils import icon_manager as ima +from spyder.utils import encoding, programs +from spyder.utils.misc import (add_pathlist_to_PYTHONPATH, get_error_match, + get_python_executable, remove_backslashes) +from spyder.widgets.findreplace import FindReplace +from spyder.widgets.ipythonconsole import ClientWidget +from spyder.widgets.tabs import Tabs + + +SYMPY_REQVER = '>=0.7.3' +dependencies.add("sympy", _("Symbolic mathematics in the IPython Console"), + required_version=SYMPY_REQVER, optional=True) + + +#------------------------------------------------------------------------------ +# Existing kernels +#------------------------------------------------------------------------------ +# Replacing pyzmq openssh_tunnel method to work around the issue +# https://github.com/zeromq/pyzmq/issues/589 which was solved in pyzmq +# https://github.com/zeromq/pyzmq/pull/615 +def _stop_tunnel(cmd): + pexpect.run(cmd) + +def openssh_tunnel(self, lport, rport, server, remoteip='127.0.0.1', + keyfile=None, password=None, timeout=0.4): + if pexpect is None: + raise ImportError("pexpect unavailable, use paramiko_tunnel") + ssh="ssh " + if keyfile: + ssh += "-i " + keyfile + + if ':' in server: + server, port = server.split(':') + ssh += " -p %s" % port + + cmd = "%s -O check %s" % (ssh, server) + (output, exitstatus) = pexpect.run(cmd, withexitstatus=True) + if not exitstatus: + pid = int(output[output.find("(pid=")+5:output.find(")")]) + cmd = "%s -O forward -L 127.0.0.1:%i:%s:%i %s" % ( + ssh, lport, remoteip, rport, server) + (output, exitstatus) = pexpect.run(cmd, withexitstatus=True) + if not exitstatus: + atexit.register(_stop_tunnel, cmd.replace("-O forward", + "-O cancel", + 1)) + return pid + cmd = "%s -f -S none -L 127.0.0.1:%i:%s:%i %s sleep %i" % ( + ssh, lport, remoteip, rport, server, timeout) + + # pop SSH_ASKPASS from env + env = os.environ.copy() + env.pop('SSH_ASKPASS', None) + + ssh_newkey = 'Are you sure you want to continue connecting' + tunnel = pexpect.spawn(cmd, env=env) + failed = False + while True: + try: + i = tunnel.expect([ssh_newkey, '[Pp]assword:'], timeout=.1) + if i==0: + host = server.split('@')[-1] + question = _("The authenticity of host %s can't be " + "established. Are you sure you want to continue " + "connecting?") % host + reply = QMessageBox.question(self, _('Warning'), question, + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No) + if reply == QMessageBox.Yes: + tunnel.sendline('yes') + continue + else: + tunnel.sendline('no') + raise RuntimeError( + _("The authenticity of the host can't be established")) + if i==1 and password is not None: + tunnel.sendline(password) + except pexpect.TIMEOUT: + continue + except pexpect.EOF: + if tunnel.exitstatus: + raise RuntimeError(_("Tunnel '%s' failed to start") % cmd) + else: + return tunnel.pid + else: + if failed or password is None: + raise RuntimeError(_("Could not connect to remote host")) + # TODO: Use this block when pyzmq bug #620 is fixed + # # Prompt a passphrase dialog to the user for a second attempt + # password, ok = QInputDialog.getText(self, _('Password'), + # _('Enter password for: ') + server, + # echo=QLineEdit.Password) + # if ok is False: + # raise RuntimeError('Could not connect to remote host.') + tunnel.sendline(password) + failed = True + + +class KernelConnectionDialog(QDialog): + """Dialog to connect to existing kernels (either local or remote)""" + + def __init__(self, parent=None): + super(KernelConnectionDialog, self).__init__(parent) + self.setWindowTitle(_('Connect to an existing kernel')) + + main_label = QLabel(_("Please enter the connection info of the kernel " + "you want to connect to. For that you can " + "either select its JSON connection file using " + "the Browse button, or write directly " + "its id, in case it's a local kernel (for " + "example kernel-3764.json or just " + "3764).")) + main_label.setWordWrap(True) + main_label.setAlignment(Qt.AlignJustify) + + # connection file + cf_label = QLabel(_('Connection info:')) + self.cf = QLineEdit() + self.cf.setPlaceholderText(_('Path to connection file or kernel id')) + self.cf.setMinimumWidth(250) + cf_open_btn = QPushButton(_('Browse')) + cf_open_btn.clicked.connect(self.select_connection_file) + + cf_layout = QHBoxLayout() + cf_layout.addWidget(cf_label) + cf_layout.addWidget(self.cf) + cf_layout.addWidget(cf_open_btn) + + # remote kernel checkbox + self.rm_cb = QCheckBox(_('This is a remote kernel')) + + # ssh connection + self.hn = QLineEdit() + self.hn.setPlaceholderText(_('username@hostname:port')) + + self.kf = QLineEdit() + self.kf.setPlaceholderText(_('Path to ssh key file')) + kf_open_btn = QPushButton(_('Browse')) + kf_open_btn.clicked.connect(self.select_ssh_key) + + kf_layout = QHBoxLayout() + kf_layout.addWidget(self.kf) + kf_layout.addWidget(kf_open_btn) + + self.pw = QLineEdit() + self.pw.setPlaceholderText(_('Password or ssh key passphrase')) + self.pw.setEchoMode(QLineEdit.Password) + + ssh_form = QFormLayout() + ssh_form.addRow(_('Host name'), self.hn) + ssh_form.addRow(_('Ssh key'), kf_layout) + ssh_form.addRow(_('Password'), self.pw) + + # Ok and Cancel buttons + accept_btns = QDialogButtonBox( + QDialogButtonBox.Ok | QDialogButtonBox.Cancel, + Qt.Horizontal, self) + + accept_btns.accepted.connect(self.accept) + accept_btns.rejected.connect(self.reject) + + # Dialog layout + layout = QVBoxLayout(self) + layout.addWidget(main_label) + layout.addLayout(cf_layout) + layout.addWidget(self.rm_cb) + layout.addLayout(ssh_form) + layout.addWidget(accept_btns) + + # remote kernel checkbox enables the ssh_connection_form + def ssh_set_enabled(state): + for wid in [self.hn, self.kf, kf_open_btn, self.pw]: + wid.setEnabled(state) + for i in range(ssh_form.rowCount()): + ssh_form.itemAt(2 * i).widget().setEnabled(state) + + ssh_set_enabled(self.rm_cb.checkState()) + self.rm_cb.stateChanged.connect(ssh_set_enabled) + + def select_connection_file(self): + cf = getopenfilename(self, _('Open connection file'), + jupyter_runtime_dir(), '*.json;;*.*')[0] + self.cf.setText(cf) + + def select_ssh_key(self): + kf = getopenfilename(self, _('Select ssh key'), + get_home_dir(), '*.pem;;*.*')[0] + self.kf.setText(kf) + + @staticmethod + def get_connection_parameters(parent=None): + dialog = KernelConnectionDialog(parent) + result = dialog.exec_() + is_remote = bool(dialog.rm_cb.checkState()) + accepted = result == QDialog.Accepted + if is_remote: + falsy_to_none = lambda arg: arg if arg else None + return (dialog.cf.text(), # connection file + falsy_to_none(dialog.hn.text()), # host name + falsy_to_none(dialog.kf.text()), # ssh key file + falsy_to_none(dialog.pw.text()), # ssh password + accepted) # ok + else: + return (dialog.cf.text(), None, None, None, accepted) + + +#------------------------------------------------------------------------------ +# Config page +#------------------------------------------------------------------------------ +class IPythonConsoleConfigPage(PluginConfigPage): + + def __init__(self, plugin, parent): + PluginConfigPage.__init__(self, plugin, parent) + self.get_name = lambda: _("IPython console") + + def setup_page(self): + newcb = self.create_checkbox + + # Interface Group + interface_group = QGroupBox(_("Interface")) + banner_box = newcb(_("Display initial banner"), 'show_banner', + tip=_("This option lets you hide the message shown at\n" + "the top of the console when it's opened.")) + pager_box = newcb(_("Use a pager to display additional text inside " + "the console"), 'use_pager', + tip=_("Useful if you don't want to fill the " + "console with long help or completion texts.\n" + "Note: Use the Q key to get out of the " + "pager.")) + calltips_box = newcb(_("Display balloon tips"), 'show_calltips') + ask_box = newcb(_("Ask for confirmation before closing"), + 'ask_before_closing') + + interface_layout = QVBoxLayout() + interface_layout.addWidget(banner_box) + interface_layout.addWidget(pager_box) + interface_layout.addWidget(calltips_box) + interface_layout.addWidget(ask_box) + interface_group.setLayout(interface_layout) + + comp_group = QGroupBox(_("Completion Type")) + comp_label = QLabel(_("Decide what type of completion to use")) + comp_label.setWordWrap(True) + completers = [(_("Graphical"), 0), (_("Terminal"), 1), (_("Plain"), 2)] + comp_box = self.create_combobox(_("Completion:")+" ", completers, + 'completion_type') + comp_layout = QVBoxLayout() + comp_layout.addWidget(comp_label) + comp_layout.addWidget(comp_box) + comp_group.setLayout(comp_layout) + + # Background Color Group + bg_group = QGroupBox(_("Background color")) + light_radio = self.create_radiobutton(_("Light background"), + 'light_color') + dark_radio = self.create_radiobutton(_("Dark background"), + 'dark_color') + bg_layout = QVBoxLayout() + bg_layout.addWidget(light_radio) + bg_layout.addWidget(dark_radio) + bg_group.setLayout(bg_layout) + + # Source Code Group + source_code_group = QGroupBox(_("Source code")) + buffer_spin = self.create_spinbox( + _("Buffer: "), _(" lines"), + 'buffer_size', min_=-1, max_=1000000, step=100, + tip=_("Set the maximum number of lines of text shown in the\n" + "console before truncation. Specifying -1 disables it\n" + "(not recommended!)")) + source_code_layout = QVBoxLayout() + source_code_layout.addWidget(buffer_spin) + source_code_group.setLayout(source_code_layout) + + # --- Graphics --- + # Pylab Group + pylab_group = QGroupBox(_("Support for graphics (Matplotlib)")) + pylab_box = newcb(_("Activate support"), 'pylab') + autoload_pylab_box = newcb(_("Automatically load Pylab and NumPy " + "modules"), + 'pylab/autoload', + tip=_("This lets you load graphics support " + "without importing \nthe commands to do " + "plots. Useful to work with other\n" + "plotting libraries different to " + "Matplotlib or to develop \nGUIs with " + "Spyder.")) + autoload_pylab_box.setEnabled(self.get_option('pylab')) + pylab_box.toggled.connect(autoload_pylab_box.setEnabled) + + pylab_layout = QVBoxLayout() + pylab_layout.addWidget(pylab_box) + pylab_layout.addWidget(autoload_pylab_box) + pylab_group.setLayout(pylab_layout) + + # Pylab backend Group + inline = _("Inline") + automatic = _("Automatic") + backend_group = QGroupBox(_("Graphics backend")) + bend_label = QLabel(_("Decide how graphics are going to be displayed " + "in the console. If unsure, please select " + "%s to put graphics inside the " + "console or %s to interact with " + "them (through zooming and panning) in a " + "separate window.") % (inline, automatic)) + bend_label.setWordWrap(True) + + backends = [(inline, 0), (automatic, 1), ("Qt5", 2), ("Qt4", 3)] + + if sys.platform == 'darwin': + backends.append( ("OS X", 4) ) + if sys.platform.startswith('linux'): + backends.append( ("Gtk3", 5) ) + backends.append( ("Gtk", 6) ) + if PY2: + backends.append( ("Wx", 7) ) + backends.append( ("Tkinter", 8) ) + backends = tuple(backends) + + backend_box = self.create_combobox( _("Backend:")+" ", backends, + 'pylab/backend', default=0, + tip=_("This option will be applied the " + "next time a console is opened.")) + + backend_layout = QVBoxLayout() + backend_layout.addWidget(bend_label) + backend_layout.addWidget(backend_box) + backend_group.setLayout(backend_layout) + backend_group.setEnabled(self.get_option('pylab')) + pylab_box.toggled.connect(backend_group.setEnabled) + + # Inline backend Group + inline_group = QGroupBox(_("Inline backend")) + inline_label = QLabel(_("Decide how to render the figures created by " + "this backend")) + inline_label.setWordWrap(True) + formats = (("PNG", 0), ("SVG", 1)) + format_box = self.create_combobox(_("Format:")+" ", formats, + 'pylab/inline/figure_format', default=0) + resolution_spin = self.create_spinbox( + _("Resolution:")+" ", " "+_("dpi"), + 'pylab/inline/resolution', min_=50, max_=150, step=0.1, + tip=_("Only used when the format is PNG. Default is " + "72")) + width_spin = self.create_spinbox( + _("Width:")+" ", " "+_("inches"), + 'pylab/inline/width', min_=2, max_=20, step=1, + tip=_("Default is 6")) + height_spin = self.create_spinbox( + _("Height:")+" ", " "+_("inches"), + 'pylab/inline/height', min_=1, max_=20, step=1, + tip=_("Default is 4")) + + inline_v_layout = QVBoxLayout() + inline_v_layout.addWidget(inline_label) + inline_layout = QGridLayout() + inline_layout.addWidget(format_box.label, 1, 0) + inline_layout.addWidget(format_box.combobox, 1, 1) + inline_layout.addWidget(resolution_spin.plabel, 2, 0) + inline_layout.addWidget(resolution_spin.spinbox, 2, 1) + inline_layout.addWidget(resolution_spin.slabel, 2, 2) + inline_layout.addWidget(width_spin.plabel, 3, 0) + inline_layout.addWidget(width_spin.spinbox, 3, 1) + inline_layout.addWidget(width_spin.slabel, 3, 2) + inline_layout.addWidget(height_spin.plabel, 4, 0) + inline_layout.addWidget(height_spin.spinbox, 4, 1) + inline_layout.addWidget(height_spin.slabel, 4, 2) + inline_h_layout = QHBoxLayout() + inline_h_layout.addLayout(inline_layout) + inline_h_layout.addStretch(1) + inline_v_layout.addLayout(inline_h_layout) + inline_group.setLayout(inline_v_layout) + inline_group.setEnabled(self.get_option('pylab')) + pylab_box.toggled.connect(inline_group.setEnabled) + + # --- Startup --- + # Run lines Group + run_lines_group = QGroupBox(_("Run code")) + run_lines_label = QLabel(_("You can run several lines of code when " + "a console is started. Please introduce " + "each one separated by commas, for " + "example:
    " + "import os, import sys")) + run_lines_label.setWordWrap(True) + run_lines_edit = self.create_lineedit(_("Lines:"), 'startup/run_lines', + '', alignment=Qt.Horizontal) + + run_lines_layout = QVBoxLayout() + run_lines_layout.addWidget(run_lines_label) + run_lines_layout.addWidget(run_lines_edit) + run_lines_group.setLayout(run_lines_layout) + + # Run file Group + run_file_group = QGroupBox(_("Run a file")) + run_file_label = QLabel(_("You can also run a whole file at startup " + "instead of just some lines (This is " + "similar to have a PYTHONSTARTUP file).")) + run_file_label.setWordWrap(True) + file_radio = newcb(_("Use the following file:"), + 'startup/use_run_file', False) + run_file_browser = self.create_browsefile('', 'startup/run_file', '') + run_file_browser.setEnabled(False) + file_radio.toggled.connect(run_file_browser.setEnabled) + + run_file_layout = QVBoxLayout() + run_file_layout.addWidget(run_file_label) + run_file_layout.addWidget(file_radio) + run_file_layout.addWidget(run_file_browser) + run_file_group.setLayout(run_file_layout) + + # ---- Advanced settings ---- + # Greedy completer group + greedy_group = QGroupBox(_("Greedy completion")) + greedy_label = QLabel(_("Enable Tab completion on elements " + "of lists, results of function calls, etc, " + "without assigning them to a " + "variable.
    " + "For example, you can get completions on " + "things like li[0].<Tab> or " + "ins.meth().<Tab>")) + greedy_label.setWordWrap(True) + greedy_box = newcb(_("Use the greedy completer"), "greedy_completer", + tip="Warning: It can be unsafe because the " + "code is actually evaluated when you press " + "Tab.") + + greedy_layout = QVBoxLayout() + greedy_layout.addWidget(greedy_label) + greedy_layout.addWidget(greedy_box) + greedy_group.setLayout(greedy_layout) + + # Autocall group + autocall_group = QGroupBox(_("Autocall")) + autocall_label = QLabel(_("Autocall makes IPython automatically call " + "any callable object even if you didn't type " + "explicit parentheses.
    " + "For example, if you type str 43 it " + "becomes str(43) automatically.")) + autocall_label.setWordWrap(True) + + smart = _('Smart') + full = _('Full') + autocall_opts = ((_('Off'), 0), (smart, 1), (full, 2)) + autocall_box = self.create_combobox( + _("Autocall: "), autocall_opts, 'autocall', default=0, + tip=_("On %s mode, Autocall is not applied if " + "there are no arguments after the callable. On " + "%s mode, all callable objects are " + "automatically called (even if no arguments are " + "present).") % (smart, full)) + + autocall_layout = QVBoxLayout() + autocall_layout.addWidget(autocall_label) + autocall_layout.addWidget(autocall_box) + autocall_group.setLayout(autocall_layout) + + # Sympy group + sympy_group = QGroupBox(_("Symbolic Mathematics")) + sympy_label = QLabel(_("Perfom symbolic operations in the console " + "(e.g. integrals, derivatives, vector calculus, " + "etc) and get the outputs in a beautifully " + "printed style (it requires the Sympy module).")) + sympy_label.setWordWrap(True) + sympy_box = newcb(_("Use symbolic math"), "symbolic_math", + tip=_("This option loads the Sympy library to work " + "with.
    Please refer to its documentation to " + "learn how to use it.")) + + sympy_layout = QVBoxLayout() + sympy_layout.addWidget(sympy_label) + sympy_layout.addWidget(sympy_box) + sympy_group.setLayout(sympy_layout) + + # Prompts group + prompts_group = QGroupBox(_("Prompts")) + prompts_label = QLabel(_("Modify how Input and Output prompts are " + "shown in the console.")) + prompts_label.setWordWrap(True) + in_prompt_edit = self.create_lineedit(_("Input prompt:"), + 'in_prompt', '', + _('Default is
    ' + 'In [<span class="in-prompt-number">' + '%i</span>]:'), + alignment=Qt.Horizontal) + out_prompt_edit = self.create_lineedit(_("Output prompt:"), + 'out_prompt', '', + _('Default is
    ' + 'Out[<span class="out-prompt-number">' + '%i</span>]:'), + alignment=Qt.Horizontal) + + prompts_layout = QVBoxLayout() + prompts_layout.addWidget(prompts_label) + prompts_g_layout = QGridLayout() + prompts_g_layout.addWidget(in_prompt_edit.label, 0, 0) + prompts_g_layout.addWidget(in_prompt_edit.textbox, 0, 1) + prompts_g_layout.addWidget(out_prompt_edit.label, 1, 0) + prompts_g_layout.addWidget(out_prompt_edit.textbox, 1, 1) + prompts_layout.addLayout(prompts_g_layout) + prompts_group.setLayout(prompts_layout) + + # --- Tabs organization --- + tabs = QTabWidget() + tabs.addTab(self.create_tab(interface_group, comp_group, + bg_group, source_code_group), _("Display")) + tabs.addTab(self.create_tab(pylab_group, backend_group, inline_group), + _("Graphics")) + tabs.addTab(self.create_tab(run_lines_group, run_file_group), + _("Startup")) + tabs.addTab(self.create_tab(greedy_group, autocall_group, sympy_group, + prompts_group), _("Advanced Settings")) + + vlayout = QVBoxLayout() + vlayout.addWidget(tabs) + self.setLayout(vlayout) + + +#------------------------------------------------------------------------------ +# Plugin widget +#------------------------------------------------------------------------------ +class IPythonConsole(SpyderPluginWidget): + """ + IPython Console plugin + + This is a widget with tabs where each one is a ClientWidget + """ + CONF_SECTION = 'ipython_console' + CONFIGWIDGET_CLASS = IPythonConsoleConfigPage + DISABLE_ACTIONS_WHEN_HIDDEN = False + + # Signals + focus_changed = Signal() + edit_goto = Signal((str, int, str), (str, int, str, bool)) + + def __init__(self, parent): + if PYQT5: + SpyderPluginWidget.__init__(self, parent, main = parent) + else: + SpyderPluginWidget.__init__(self, parent) + + self.tabwidget = None + self.menu_actions = None + + self.extconsole = None # External console plugin + self.help = None # Help plugin + self.historylog = None # History log plugin + self.variableexplorer = None # Variable explorer plugin + self.editor = None # Editor plugin + + self.master_clients = 0 + self.clients = [] + self.mainwindow_close = False + self.create_new_client_if_empty = True + + # Initialize plugin + self.initialize_plugin() + + layout = QVBoxLayout() + self.tabwidget = Tabs(self, self.menu_actions) + if hasattr(self.tabwidget, 'setDocumentMode')\ + and not sys.platform == 'darwin': + # Don't set document mode to true on OSX because it generates + # a crash when the console is detached from the main window + # Fixes Issue 561 + self.tabwidget.setDocumentMode(True) + self.tabwidget.currentChanged.connect(self.refresh_plugin) + self.tabwidget.move_data.connect(self.move_tab) + + self.tabwidget.set_close_function(self.close_client) + + if sys.platform == 'darwin': + tab_container = QWidget() + tab_container.setObjectName('tab-container') + tab_layout = QHBoxLayout(tab_container) + tab_layout.setContentsMargins(0, 0, 0, 0) + tab_layout.addWidget(self.tabwidget) + layout.addWidget(tab_container) + else: + layout.addWidget(self.tabwidget) + + # Find/replace widget + self.find_widget = FindReplace(self) + self.find_widget.hide() + self.register_widget_shortcuts(self.find_widget) + layout.addWidget(self.find_widget) + + self.setLayout(layout) + + # Accepting drops + self.setAcceptDrops(True) + + #------ SpyderPluginMixin API --------------------------------------------- + def on_first_registration(self): + """Action to be performed on first plugin registration""" + self.main.tabify_plugins(self.main.extconsole, self) + + def update_font(self): + """Update font from Preferences""" + font = self.get_plugin_font() + for client in self.clients: + client.set_font(font) + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + font_n = 'plugin_font' + font_o = self.get_plugin_font() + help_n = 'connect_to_oi' + help_o = CONF.get('help', 'connect/ipython_console') + for client in self.clients: + control = client.get_control() + if font_n in options: + client.set_font(font_o) + if help_n in options and control is not None: + control.set_help_enabled(help_o) + + def toggle_view(self, checked): + """Toggle view""" + if checked: + self.dockwidget.show() + self.dockwidget.raise_() + # Start a client in case there are none shown + if not self.clients: + if self.main.is_setting_up: + self.create_new_client(give_focus=False) + else: + self.create_new_client(give_focus=True) + else: + self.dockwidget.hide() + + #------ SpyderPluginWidget API -------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _('IPython console') + + def get_plugin_icon(self): + """Return widget icon""" + return ima.icon('ipython_console') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + client = self.tabwidget.currentWidget() + if client is not None: + return client.get_control() + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + self.mainwindow_close = True + for client in self.clients: + client.shutdown() + client.close() + return True + + def refresh_plugin(self): + """Refresh tabwidget""" + client = None + if self.tabwidget.count(): + # Give focus to the control widget of the selected tab + client = self.tabwidget.currentWidget() + control = client.get_control() + control.setFocus() + widgets = client.get_toolbar_buttons()+[5] + else: + control = None + widgets = [] + self.find_widget.set_editor(control) + self.tabwidget.set_corner_widgets({Qt.TopRightCorner: widgets}) + if client: + sw = client.shellwidget + self.variableexplorer.set_shellwidget_from_id(id(sw)) + self.help.set_shell(sw) + self.main.last_console_plugin_focus_was_python = False + self.update_plugin_title.emit() + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + ctrl = "Cmd" if sys.platform == "darwin" else "Ctrl" + main_create_client_action = create_action(self, + _("Open an &IPython console"), + None, ima.icon('ipython_console'), + triggered=self.create_new_client, + tip=_("Use %s+T when the console is selected " + "to open a new one") % ctrl) + create_client_action = create_action(self, + _("Open a new console"), + QKeySequence("Ctrl+T"), + ima.icon('ipython_console'), + triggered=self.create_new_client, + context=Qt.WidgetWithChildrenShortcut) + + connect_to_kernel_action = create_action(self, + _("Connect to an existing kernel"), None, None, + _("Open a new IPython console connected to an existing kernel"), + triggered=self.create_client_for_kernel) + + # Add the action to the 'Consoles' menu on the main window + main_consoles_menu = self.main.consoles_menu_actions + main_consoles_menu.insert(0, main_create_client_action) + main_consoles_menu += [None, connect_to_kernel_action] + + # Plugin actions + self.menu_actions = [create_client_action, connect_to_kernel_action] + + return self.menu_actions + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.main.add_dockwidget(self) + + self.extconsole = self.main.extconsole + self.help = self.main.help + self.historylog = self.main.historylog + self.variableexplorer = self.main.variableexplorer + self.editor = self.main.editor + + self.focus_changed.connect(self.main.plugin_focus_changed) + self.edit_goto.connect(self.editor.load) + self.edit_goto[str, int, str, bool].connect( + lambda fname, lineno, word, processevents: + self.editor.load(fname, lineno, word, + processevents=processevents)) + self.editor.run_in_current_ipyclient.connect( + self.run_script_in_current_client) + self.main.workingdirectory.set_current_console_wd.connect( + self.set_current_client_working_directory) + + #------ Public API (for clients) ------------------------------------------ + def get_clients(self): + """Return clients list""" + return [cl for cl in self.clients if isinstance(cl, ClientWidget)] + + def get_focus_client(self): + """Return current client with focus, if any""" + widget = QApplication.focusWidget() + for client in self.get_clients(): + if widget is client or widget is client.get_control(): + return client + + def get_current_client(self): + """Return the currently selected client""" + client = self.tabwidget.currentWidget() + if client is not None: + return client + + def get_current_shellwidget(self): + """Return the shellwidget of the current client""" + client = self.get_current_client() + if client is not None: + return client.shellwidget + + def run_script_in_current_client(self, filename, wdir, args, debug, + post_mortem): + """Run script in current client, if any""" + norm = lambda text: remove_backslashes(to_text_string(text)) + client = self.get_current_client() + if client is not None: + # Internal kernels, use runfile + if client.get_kernel() is not None: + line = "%s('%s'" % ('debugfile' if debug else 'runfile', + norm(filename)) + if args: + line += ", args='%s'" % norm(args) + if wdir: + line += ", wdir='%s'" % norm(wdir) + if post_mortem: + line += ", post_mortem=True" + line += ")" + else: # External kernels, use %run + line = "%run " + if debug: + line += "-d " + line += "\"%s\"" % to_text_string(filename) + if args: + line += " %s" % norm(args) + self.execute_code(line) + self.visibility_changed(True) + self.raise_() + else: + #XXX: not sure it can really happen + QMessageBox.warning(self, _('Warning'), + _("No IPython console is currently available to run %s." + "

    Please open a new one and try again." + ) % osp.basename(filename), QMessageBox.Ok) + + def set_current_client_working_directory(self, directory): + """Set current client working directory.""" + shellwidget = self.get_current_shellwidget() + if shellwidget is not None: + directory = encoding.to_unicode_from_fs(directory) + shellwidget.set_cwd(directory) + + def execute_code(self, lines): + sw = self.get_current_shellwidget() + if sw is not None: + sw.execute(to_text_string(lines)) + self.activateWindow() + self.get_current_client().get_control().setFocus() + + def write_to_stdin(self, line): + sw = self.get_current_shellwidget() + if sw is not None: + sw.write_to_stdin(line) + + @Slot() + def create_new_client(self, give_focus=True): + """Create a new client""" + self.master_clients += 1 + name = "%d/A" % self.master_clients + client = ClientWidget(self, name=name, + history_filename='history.py', + config_options=self.config_options(), + additional_options=self.additional_options(), + interpreter_versions=self.interpreter_versions(), + connection_file=self._new_connection_file(), + menu_actions=self.menu_actions) + self.add_tab(client, name=client.get_name()) + + # Check if ipykernel is present in the external interpreter. + # Else we won't be able to create a client + if not CONF.get('main_interpreter', 'default'): + pyexec = CONF.get('main_interpreter', 'executable') + ipykernel_present = programs.is_module_installed('ipykernel', + interpreter=pyexec) + if not ipykernel_present: + client.show_kernel_error(_("Your Python environment or " + "installation doesn't " + "have the ipykernel module " + "installed on it. Without this module is " + "not possible for Spyder to create a " + "console for you.

    " + "You can install ipykernel by " + "running in a terminal:

    " + "pip install ipykernel

    " + "or

    " + "conda install ipykernel")) + return + + self.connect_client_to_kernel(client) + self.register_client(client) + + @Slot() + def create_client_for_kernel(self): + """Create a client connected to an existing kernel""" + connect_output = KernelConnectionDialog.get_connection_parameters(self) + (connection_file, hostname, sshkey, password, ok) = connect_output + if not ok: + return + else: + self._create_client_for_kernel(connection_file, hostname, sshkey, + password) + + def connect_client_to_kernel(self, client): + """Connect a client to its kernel""" + connection_file = client.connection_file + km, kc = self.create_kernel_manager_and_kernel_client(connection_file) + + kc.started_channels.connect(lambda c=client: self.process_started(c)) + kc.stopped_channels.connect(lambda c=client: self.process_finished(c)) + kc.start_channels(shell=True, iopub=True) + + shellwidget = client.shellwidget + shellwidget.kernel_manager = km + shellwidget.kernel_client = kc + + def set_editor(self): + """Set the editor used by the %edit magic""" + python = sys.executable + if DEV: + spyder_start_directory = get_module_path('spyder') + bootstrap_script = osp.join(osp.dirname(spyder_start_directory), + 'bootstrap.py') + editor = u'{0} {1} --'.format(python, bootstrap_script) + else: + import1 = "import sys" + import2 = "from spyder.app.start import send_args_to_spyder" + code = "send_args_to_spyder([sys.argv[-1]])" + editor = u"{0} -c '{1}; {2}; {3}'".format(python, + import1, + import2, + code) + return to_text_string(editor) + + def config_options(self): + """ + Generate a Trailets Config instance for shell widgets using our + config system + + This lets us create each widget with its own config + """ + # ---- Jupyter config ---- + try: + full_cfg = load_pyconfig_files(['jupyter_qtconsole_config.py'], + jupyter_config_dir()) + + # From the full config we only select the JupyterWidget section + # because the others have no effect here. + cfg = Config({'JupyterWidget': full_cfg.JupyterWidget}) + except: + cfg = Config() + + # ---- Spyder config ---- + spy_cfg = Config() + + # Make the pager widget a rich one (i.e a QTextEdit) + spy_cfg.JupyterWidget.kind = 'rich' + + # Gui completion widget + completion_type_o = self.get_option('completion_type') + completions = {0: "droplist", 1: "ncurses", 2: "plain"} + spy_cfg.JupyterWidget.gui_completion = completions[completion_type_o] + + # Pager + pager_o = self.get_option('use_pager') + if pager_o: + spy_cfg.JupyterWidget.paging = 'inside' + else: + spy_cfg.JupyterWidget.paging = 'none' + + # Calltips + calltips_o = self.get_option('show_calltips') + spy_cfg.JupyterWidget.enable_calltips = calltips_o + + # Buffer size + buffer_size_o = self.get_option('buffer_size') + spy_cfg.JupyterWidget.buffer_size = buffer_size_o + + # Prompts + in_prompt_o = self.get_option('in_prompt') + out_prompt_o = self.get_option('out_prompt') + if in_prompt_o: + spy_cfg.JupyterWidget.in_prompt = in_prompt_o + if out_prompt_o: + spy_cfg.JupyterWidget.out_prompt = out_prompt_o + + # Editor for %edit + if CONF.get('main', 'single_instance'): + spy_cfg.JupyterWidget.editor = self.set_editor() + + # Merge QtConsole and Spyder configs. Spyder prefs will have + # prevalence over QtConsole ones + cfg._merge(spy_cfg) + return cfg + + def interpreter_versions(self): + """Python and IPython versions used by clients""" + if CONF.get('main_interpreter', 'default'): + from IPython.core import release + versions = dict( + python_version = sys.version.split("\n")[0].strip(), + ipython_version = release.version + ) + else: + import subprocess + versions = {} + pyexec = CONF.get('main_interpreter', 'executable') + py_cmd = "%s -c 'import sys; print(sys.version.split(\"\\n\")[0])'" % \ + pyexec + ipy_cmd = "%s -c 'import IPython.core.release as r; print(r.version)'" \ + % pyexec + for cmd in [py_cmd, ipy_cmd]: + try: + proc = programs.run_shell_command(cmd) + output, _err = proc.communicate() + except subprocess.CalledProcessError: + output = '' + output = output.decode().split('\n')[0].strip() + if 'IPython' in cmd: + versions['ipython_version'] = output + else: + versions['python_version'] = output + + return versions + + def additional_options(self): + """ + Additional options for shell widgets that are not defined + in JupyterWidget config options + """ + options = dict( + pylab = self.get_option('pylab'), + autoload_pylab = self.get_option('pylab/autoload'), + sympy = self.get_option('symbolic_math'), + light_color = self.get_option('light_color'), + show_banner = self.get_option('show_banner') + ) + + return options + + def register_client(self, client, give_focus=True): + """Register new client""" + client.configure_shellwidget(give_focus=give_focus) + + # Local vars + shellwidget = client.shellwidget + control = shellwidget._control + page_control = shellwidget._page_control + + # Create new clients with Ctrl+T shortcut + shellwidget.new_client.connect(self.create_new_client) + + # For tracebacks + control.go_to_error.connect(self.go_to_error) + + shellwidget.sig_pdb_step.connect( + lambda fname, lineno, shellwidget=shellwidget: + self.pdb_has_stopped(fname, lineno, shellwidget)) + + # Connect text widget to Help + if self.help is not None: + control.set_help(self.help) + control.set_help_enabled(CONF.get('help', 'connect/ipython_console')) + + # Connect client to our history log + if self.historylog is not None: + self.historylog.add_history(client.history_filename) + client.append_to_history.connect(self.historylog.append_to_history) + + # Set font for client + client.set_font( self.get_plugin_font() ) + + # Connect focus signal to client's control widget + control.focus_changed.connect(lambda: self.focus_changed.emit()) + + # Update the find widget if focus changes between control and + # page_control + self.find_widget.set_editor(control) + if page_control: + page_control.focus_changed.connect(lambda: self.focus_changed.emit()) + control.visibility_changed.connect(self.refresh_plugin) + page_control.visibility_changed.connect(self.refresh_plugin) + page_control.show_find_widget.connect(self.find_widget.show) + + def close_client(self, index=None, client=None, force=False): + """Close client tab from index or widget (or close current tab)""" + if not self.tabwidget.count(): + return + if client is not None: + index = self.tabwidget.indexOf(client) + if index is None and client is None: + index = self.tabwidget.currentIndex() + if index is not None: + client = self.tabwidget.widget(index) + + # Check if related clients or kernels are opened + # and eventually ask before closing them + if not self.mainwindow_close and not force: + close_all = True + if self.get_option('ask_before_closing'): + close = QMessageBox.question(self, self.get_plugin_title(), + _("Do you want to close this console?"), + QMessageBox.Yes | QMessageBox.No) + if close == QMessageBox.No: + return + if len(self.get_related_clients(client)) > 0: + close_all = QMessageBox.question(self, self.get_plugin_title(), + _("Do you want to close all other consoles connected " + "to the same kernel as this one?"), + QMessageBox.Yes | QMessageBox.No) + client.shutdown() + if close_all == QMessageBox.Yes: + self.close_related_clients(client) + client.close() + + # Note: client index may have changed after closing related widgets + self.tabwidget.removeTab(self.tabwidget.indexOf(client)) + self.clients.remove(client) + if not self.tabwidget.count() and self.create_new_client_if_empty: + self.create_new_client() + self.update_plugin_title.emit() + + def get_client_index_from_id(self, client_id): + """Return client index from id""" + for index, client in enumerate(self.clients): + if id(client) == client_id: + return index + + def rename_client_tab(self, client): + """Rename client's tab""" + index = self.get_client_index_from_id(id(client)) + self.tabwidget.setTabText(index, client.get_name()) + + def get_related_clients(self, client): + """ + Get all other clients that are connected to the same kernel as `client` + """ + related_clients = [] + for cl in self.get_clients(): + if cl.connection_file == client.connection_file and \ + cl is not client: + related_clients.append(cl) + return related_clients + + def close_related_clients(self, client): + """Close all clients related to *client*, except itself""" + related_clients = self.get_related_clients(client) + for cl in related_clients: + self.close_client(client=cl, force=True) + + def restart(self): + """ + Restart the console + + This is needed when we switch projects to update PYTHONPATH + and the selected interpreter + """ + self.master_clients = 0 + self.create_new_client_if_empty = False + for i in range(len(self.clients)): + client = self.clients[-1] + client.shutdown() + self.close_client(client=client, force=True) + self.create_new_client(give_focus=False) + self.create_new_client_if_empty = True + + def pdb_has_stopped(self, fname, lineno, shellwidget): + """Python debugger has just stopped at frame (fname, lineno)""" + # This is a unique form of the edit_goto signal that is intended to + # prevent keyboard input from accidentally entering the editor + # during repeated, rapid entry of debugging commands. + self.edit_goto[str, int, str, bool].emit(fname, lineno, '', False) + self.activateWindow() + shellwidget._control.setFocus() + + #------ Public API (for kernels) ------------------------------------------ + def ssh_tunnel(self, *args, **kwargs): + if os.name == 'nt': + return zmqtunnel.paramiko_tunnel(*args, **kwargs) + else: + return openssh_tunnel(self, *args, **kwargs) + + def tunnel_to_kernel(self, connection_info, hostname, sshkey=None, + password=None, timeout=10): + """ + Tunnel connections to a kernel via ssh. + + Remote ports are specified in the connection info ci. + """ + lports = zmqtunnel.select_random_ports(4) + rports = (connection_info['shell_port'], connection_info['iopub_port'], + connection_info['stdin_port'], connection_info['hb_port']) + remote_ip = connection_info['ip'] + for lp, rp in zip(lports, rports): + self.ssh_tunnel(lp, rp, hostname, remote_ip, sshkey, password, + timeout) + return tuple(lports) + + def create_kernel_spec(self): + """Create a kernel spec for our own kernels""" + # Paths that we need to add to PYTHONPATH: + # 1. sc_path: Path to our sitecustomize + # 2. spy_path: Path to our main module, so we can use our config + # system to configure kernels started by exterrnal interpreters + # 3. spy_pythonpath: Paths saved by our users with our PYTHONPATH + # manager + spy_path = get_module_source_path('spyder') + sc_path = osp.join(spy_path, 'utils', 'site') + spy_pythonpath = self.main.get_spyder_pythonpath() + + default_interpreter = CONF.get('main_interpreter', 'default') + if default_interpreter: + pathlist = [sc_path] + spy_pythonpath + else: + pathlist = [sc_path, spy_path] + spy_pythonpath + pypath = add_pathlist_to_PYTHONPATH([], pathlist, ipyconsole=True, + drop_env=(not default_interpreter)) + + # Python interpreter used to start kernels + if default_interpreter: + pyexec = get_python_executable() + else: + # Avoid IPython adding the virtualenv on which Spyder is running + # to the kernel sys.path + os.environ.pop('VIRTUAL_ENV', None) + pyexec = CONF.get('main_interpreter', 'executable') + + # Fixes Issue #3427 + if os.name == 'nt': + dir_pyexec = osp.dirname(pyexec) + pyexec_w = osp.join(dir_pyexec, 'pythonw.exe') + if osp.isfile(pyexec_w): + pyexec = pyexec_w + + # Command used to start kernels + utils_path = osp.join(spy_path, 'utils', 'ipython') + kernel_cmd = [ + pyexec, + osp.join("%s" % utils_path, "start_kernel.py"), + '-f', + '{connection_file}' + ] + + # Environment variables that we need to pass to our sitecustomize + umr_namelist = CONF.get('main_interpreter', 'umr/namelist') + + if PY2: + original_list = umr_namelist[:] + for umr_n in umr_namelist: + try: + umr_n.encode('utf-8') + except UnicodeDecodeError: + umr_namelist.remove(umr_n) + if original_list != umr_namelist: + CONF.set('main_interpreter', 'umr/namelist', umr_namelist) + + env_vars = { + 'IPYTHON_KERNEL': 'True', + 'EXTERNAL_INTERPRETER': not default_interpreter, + 'UMR_ENABLED': CONF.get('main_interpreter', 'umr/enabled'), + 'UMR_VERBOSE': CONF.get('main_interpreter', 'umr/verbose'), + 'UMR_NAMELIST': ','.join(umr_namelist) + } + + # Add our PYTHONPATH to env_vars + env_vars.update(pypath) + + # Making all env_vars strings + for k,v in iteritems(env_vars): + if PY2: + uv = to_text_string(v) + env_vars[k] = to_binary_string(uv, encoding='utf-8') + else: + env_vars[k] = to_text_string(v) + + # Dict for our kernel spec + kernel_dict = { + 'argv': kernel_cmd, + 'display_name': 'Spyder', + 'language': 'python', + 'env': env_vars + } + + return KernelSpec(resource_dir='', **kernel_dict) + + def create_kernel_manager_and_kernel_client(self, connection_file): + """Create kernel manager and client""" + # Kernel manager + kernel_manager = QtKernelManager(connection_file=connection_file, + config=None, autorestart=True) + kernel_manager._kernel_spec = self.create_kernel_spec() + kernel_manager.start_kernel() + + # Kernel client + kernel_client = kernel_manager.client() + + # Increase time to detect if a kernel is alive + # See Issue 3444 + kernel_client.hb_channel.time_to_dead = 6.0 + + return kernel_manager, kernel_client + + #------ Public API (for tabs) --------------------------------------------- + def add_tab(self, widget, name): + """Add tab""" + self.clients.append(widget) + index = self.tabwidget.addTab(widget, name) + self.tabwidget.setCurrentIndex(index) + if self.dockwidget and not self.ismaximized: + self.dockwidget.setVisible(True) + self.dockwidget.raise_() + self.activateWindow() + widget.get_control().setFocus() + + def move_tab(self, index_from, index_to): + """ + Move tab (tabs themselves have already been moved by the tabwidget) + """ + client = self.clients.pop(index_from) + self.clients.insert(index_to, client) + self.update_plugin_title.emit() + + #------ Public API (for help) --------------------------------------------- + def go_to_error(self, text): + """Go to error if relevant""" + match = get_error_match(to_text_string(text)) + if match: + fname, lnb = match.groups() + self.edit_goto.emit(osp.abspath(fname), int(lnb), '') + + @Slot() + def show_intro(self): + """Show intro to IPython help""" + from IPython.core.usage import interactive_usage + self.help.show_rich_text(interactive_usage) + + @Slot() + def show_guiref(self): + """Show qtconsole help""" + from qtconsole.usage import gui_reference + self.help.show_rich_text(gui_reference, collapse=True) + + @Slot() + def show_quickref(self): + """Show IPython Cheat Sheet""" + from IPython.core.usage import quick_reference + self.help.show_plain_text(quick_reference) + + #------ Private API ------------------------------------------------------- + def _new_connection_file(self): + """ + Generate a new connection file + + Taken from jupyter_client/console_app.py + Licensed under the BSD license + """ + # Check if jupyter_runtime_dir exists (Spyder addition) + if not osp.isdir(jupyter_runtime_dir()): + os.makedirs(jupyter_runtime_dir()) + cf = '' + while not cf: + ident = str(uuid.uuid4()).split('-')[-1] + cf = os.path.join(jupyter_runtime_dir(), 'kernel-%s.json' % ident) + cf = cf if not os.path.exists(cf) else '' + return cf + + def process_started(self, client): + if self.help is not None: + self.help.set_shell(client.shellwidget) + if self.variableexplorer is not None: + self.variableexplorer.add_shellwidget(client.shellwidget) + + def process_finished(self, client): + if self.variableexplorer is not None: + self.variableexplorer.remove_shellwidget(id(client.shellwidget)) + + def _create_client_for_kernel(self, connection_file, hostname, sshkey, + password): + # Verifying if the connection file exists + try: + connection_file = find_connection_file(osp.basename(connection_file)) + except (IOError, UnboundLocalError): + QMessageBox.critical(self, _('IPython'), + _("Unable to connect to " + "%s") % connection_file) + return + + # Getting the master name that corresponds to the client + # (i.e. the i in i/A) + master_name = None + external_kernel = False + slave_ord = ord('A') - 1 + kernel_manager = None + for cl in self.get_clients(): + if connection_file in cl.connection_file: + if cl.get_kernel() is not None: + kernel_manager = cl.get_kernel() + connection_file = cl.connection_file + if master_name is None: + master_name = cl.name.split('/')[0] + new_slave_ord = ord(cl.name.split('/')[1]) + if new_slave_ord > slave_ord: + slave_ord = new_slave_ord + + # If we couldn't find a client with the same connection file, + # it means this is a new master client + if master_name is None: + self.master_clients += 1 + master_name = to_text_string(self.master_clients) + external_kernel = True + + # Set full client name + name = master_name + '/' + chr(slave_ord + 1) + + # Creating the client + client = ClientWidget(self, name=name, + history_filename='history.py', + config_options=self.config_options(), + additional_options=self.additional_options(), + interpreter_versions=self.interpreter_versions(), + connection_file=connection_file, + menu_actions=self.menu_actions, + hostname=hostname, + external_kernel=external_kernel, + slave=True) + + # Create kernel client + kernel_client = QtKernelClient(connection_file=connection_file) + kernel_client.load_connection_file() + if hostname is not None: + try: + connection_info = dict(ip = kernel_client.ip, + shell_port = kernel_client.shell_port, + iopub_port = kernel_client.iopub_port, + stdin_port = kernel_client.stdin_port, + hb_port = kernel_client.hb_port) + newports = self.tunnel_to_kernel(connection_info, hostname, + sshkey, password) + (kernel_client.shell_port, + kernel_client.iopub_port, + kernel_client.stdin_port, + kernel_client.hb_port) = newports + except Exception as e: + QMessageBox.critical(self, _('Connection error'), + _("Could not open ssh tunnel. The " + "error was:\n\n") + to_text_string(e)) + return + kernel_client.start_channels() + + # Assign kernel manager and client to shellwidget + client.shellwidget.kernel_client = kernel_client + client.shellwidget.kernel_manager = kernel_manager + + # Adding a new tab for the client + self.add_tab(client, name=client.get_name()) + + # Register client + self.register_client(client) diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/layoutdialog.py spyder-3.0.2+dfsg1/spyder/plugins/layoutdialog.py --- spyder-2.3.8+dfsg1/spyder/plugins/layoutdialog.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/layoutdialog.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,344 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Layout dialogs""" + +# Standard library imports +import sys + +# Third party imports +from qtpy.QtCore import QAbstractTableModel, QModelIndex, QSize, Qt +from qtpy.compat import from_qvariant, to_qvariant +from qtpy.QtWidgets import (QAbstractItemView, QComboBox, QDialog, + QDialogButtonBox, QGroupBox, QHBoxLayout, + QPushButton, QTableView, QVBoxLayout) + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import to_text_string + + +class LayoutModel(QAbstractTableModel): + """ """ + def __init__(self, parent, order, active): + super(LayoutModel, self).__init__(parent) + + # variables + self._parent = parent + self.order = order + self.active = active + self._rows = [] + self.set_data(order, active) + + def set_data(self, order, active): + """ """ + self._rows = [] + self.order = order + self.active = active + for name in order: + if name in active: + row = [name, True] + else: + row = [name, False] + self._rows.append(row) + + def flags(self, index): + """Override Qt method""" + if not index.isValid(): + return Qt.ItemIsEnabled + column = index.column() + if column in [0]: + return Qt.ItemFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | + Qt.ItemIsUserCheckable | Qt.ItemIsEditable) + else: + return Qt.ItemFlags(Qt.ItemIsEnabled) + + def data(self, index, role=Qt.DisplayRole): + """Override Qt method""" + if not index.isValid() or not 0 <= index.row() < len(self._rows): + return to_qvariant() + row = index.row() + column = index.column() + + name, state = self.row(row) + + if role == Qt.DisplayRole or role == Qt.EditRole: + if column == 0: + return to_qvariant(name) + elif role == Qt.CheckStateRole: + if column == 0: + if state: + return Qt.Checked + else: + return Qt.Unchecked + if column == 1: + return to_qvariant(state) + return to_qvariant() + + def setData(self, index, value, role): + """Override Qt method""" + row = index.row() + name, state = self.row(row) + + if role == Qt.CheckStateRole: + self.set_row(row, [name, not state]) + self._parent.setCurrentIndex(index) + self._parent.setFocus() + self.dataChanged.emit(index, index) + return True + elif role == Qt.EditRole: + self.set_row(row, [from_qvariant(value, to_text_string), state]) + self.dataChanged.emit(index, index) + return True + return True + + def rowCount(self, index=QModelIndex()): + """Override Qt method""" + return len(self._rows) + + def columnCount(self, index=QModelIndex()): + """Override Qt method""" + return 2 + + def row(self, rownum): + """ """ + if self._rows == []: + return [None, None] + else: + return self._rows[rownum] + + def set_row(self, rownum, value): + """ """ + self._rows[rownum] = value + + +class LayoutSaveDialog(QDialog): + """ """ + def __init__(self, parent, order): + super(LayoutSaveDialog, self).__init__(parent) + + # variables + self._parent = parent + + # widgets + self.combo_box = QComboBox(self) + self.combo_box.addItems(order) + self.combo_box.setEditable(True) + self.combo_box.clearEditText() + self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Cancel, + Qt.Horizontal, self) + self.button_ok = self.button_box.button(QDialogButtonBox.Ok) + self.button_cancel = self.button_box.button(QDialogButtonBox.Cancel) + + # widget setup + self.button_ok.setEnabled(False) + self.dialog_size = QSize(300, 100) + self.setWindowTitle('Save layout as') + self.setModal(True) + self.setMinimumSize(self.dialog_size) + self.setFixedSize(self.dialog_size) + + # layouts + self.layout = QVBoxLayout() + self.layout.addWidget(self.combo_box) + self.layout.addWidget(self.button_box) + self.setLayout(self.layout) + + # signals and slots + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.close) + self.combo_box.editTextChanged.connect(self.check_text) + + def check_text(self, text): + """Disable empty layout name possibility""" + if to_text_string(text) == u'': + self.button_ok.setEnabled(False) + else: + self.button_ok.setEnabled(True) + + +class LayoutSettingsDialog(QDialog): + """Layout settings dialog""" + def __init__(self, parent, names, order, active): + super(LayoutSettingsDialog, self).__init__(parent) + + # variables + self._parent = parent + self._selection_model = None + self.names = names + self.order = order + self.active = active + + # widgets + self.button_move_up = QPushButton(_('Move Up')) + self.button_move_down = QPushButton(_('Move Down')) + self.button_delete = QPushButton(_('Delete Layout')) + self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Cancel, + Qt.Horizontal, self) + self.group_box = QGroupBox(_("Layout Display and Order")) + self.table = QTableView(self) + self.ok_button = self.button_box.button(QDialogButtonBox.Ok) + self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) + self.cancel_button.setDefault(True) + self.cancel_button.setAutoDefault(True) + + # widget setup + self.dialog_size = QSize(300, 200) + self.setMinimumSize(self.dialog_size) + self.setFixedSize(self.dialog_size) + self.setWindowTitle('Layout Settings') + + self.table.setModel(LayoutModel(self.table, order, active)) + self.table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.table.setSelectionMode(QAbstractItemView.SingleSelection) + self.table.verticalHeader().hide() + self.table.horizontalHeader().hide() + self.table.setAlternatingRowColors(True) + self.table.setShowGrid(False) + self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.table.horizontalHeader().setStretchLastSection(True) + self.table.setColumnHidden(1, True) + + # need to keep a reference for pyside not to segfault! + self._selection_model = self.table.selectionModel() + + # layout + buttons_layout = QVBoxLayout() + buttons_layout.addWidget(self.button_move_up) + buttons_layout.addWidget(self.button_move_down) + buttons_layout.addStretch() + buttons_layout.addWidget(self.button_delete) + + group_layout = QHBoxLayout() + group_layout.addWidget(self.table) + group_layout.addLayout(buttons_layout) + self.group_box.setLayout(group_layout) + + layout = QVBoxLayout() + layout.addWidget(self.group_box) + layout.addWidget(self.button_box) + + self.setLayout(layout) + + # signals and slots + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.close) + self.button_delete.clicked.connect(self.delete_layout) + self.button_move_up.clicked.connect(lambda: self.move_layout(True)) + self.button_move_down.clicked.connect(lambda: self.move_layout(False)) + self.table.model().dataChanged.connect( + lambda: self.selection_changed(None, None)) + self._selection_model.selectionChanged.connect( + lambda: self.selection_changed(None, None)) + + # focus table + index = self.table.model().index(0, 0) + self.table.setCurrentIndex(index) + self.table.setFocus() + + def delete_layout(self): + """ """ + names, order, active = self.names, self.order, self.order + name = from_qvariant(self.table.selectionModel().currentIndex().data(), + to_text_string) + + if name in names: + index = names.index(name) + # In case nothing has focus in the table + if index != -1: + order.remove(name) + names[index] = None + if name in active: + active.remove(name) + self.names, self.order, self.active = names, order, active + self.table.model().set_data(order, active) + index = self.table.model().index(0, 0) + self.table.setCurrentIndex(index) + self.table.setFocus() + self.selection_changed(None, None) + if len(order) == 0: + self.button_move_up.setDisabled(True) + self.button_move_down.setDisabled(True) + self.button_delete.setDisabled(True) + + def move_layout(self, up=True): + """ """ + names, order, active = self.names, self.order, self.active + row = self.table.selectionModel().currentIndex().row() + row_new = row + + if up: + row_new -= 1 + else: + row_new += 1 + + order[row], order[row_new] = order[row_new], order[row] + + self.order = order + self.table.model().set_data(order, active) + index = self.table.model().index(row_new, 0) + self.table.setCurrentIndex(index) + self.table.setFocus() + self.selection_changed(None, None) + + def selection_changed(self, selection, deselection): + """ """ + model = self.table.model() + index = self.table.currentIndex() + row = index.row() + order, names, active = self.order, self.names, self.active + + state = model.row(row)[1] + name = model.row(row)[0] + + # Check if name changed + if name not in names: # Did changed + if row != -1: # row == -1, means no items left to delete + old_name = order[row] + order[row] = name + names[names.index(old_name)] = name + if old_name in active: + active[active.index(old_name)] = name + + # Check if checbox clicked + if state: + if name not in active: + active.append(name) + else: + if name in active: + active.remove(name) + + self.active = active + self.button_move_up.setDisabled(False) + self.button_move_down.setDisabled(False) + + if row == 0: + self.button_move_up.setDisabled(True) + if row == len(names) - 1: + self.button_move_down.setDisabled(True) + if len(names) == 0: + self.button_move_up.setDisabled(True) + self.button_move_down.setDisabled(True) + + +def test(): + """Run layout test widget test""" + from spyder.utils.qthelpers import qapplication + + app = qapplication() + names = ['test', 'tester', '20', '30', '40'] + order = ['test', 'tester', '20', '30', '40'] + active = ['test', 'tester'] + widget_1 = LayoutSettingsDialog(None, names, order, active) + widget_2 = LayoutSaveDialog(None, order) + widget_1.show() + widget_2.show() + sys.exit(app.exec_()) + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/maininterpreter.py spyder-3.0.2+dfsg1/spyder/plugins/maininterpreter.py --- spyder-2.3.8+dfsg1/spyder/plugins/maininterpreter.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/maininterpreter.py 2016-11-17 03:39:40.000000000 +0000 @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Shortcut management""" + +# Standard library imports +from __future__ import print_function + +import os +import os.path as osp +import sys + +# Third party imports +from qtpy.QtWidgets import (QButtonGroup, QGroupBox, QInputDialog, QLabel, + QLineEdit, QMessageBox, QPushButton, QVBoxLayout) + +# Local imports +from spyder.config.base import _ +from spyder.plugins.configdialog import GeneralConfigPage +from spyder.py3compat import PY2, is_text_string, to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.misc import get_python_executable +from spyder.utils import programs + + +class MainInterpreterConfigPage(GeneralConfigPage): + CONF_SECTION = "main_interpreter" + NAME = _("Python interpreter") + ICON = ima.icon('python') + + def __init__(self, parent, main): + GeneralConfigPage.__init__(self, parent, main) + self.cus_exec_radio = None + self.pyexec_edit = None + + # Python executable selection (initializing default values as well) + executable = self.get_option('executable', get_python_executable()) + if self.get_option('default'): + executable = get_python_executable() + + if not osp.isfile(executable): + # This is absolutely necessary, in case the Python interpreter + # executable has been moved since last Spyder execution (following + # a Python distribution upgrade for example) + self.set_option('executable', get_python_executable()) + elif executable.endswith('pythonw.exe'): + # That should not be necessary because this case is already taken + # care of by the `get_python_executable` function but, this was + # implemented too late, so we have to fix it here too, in case + # the Python executable has already been set with pythonw.exe: + self.set_option('executable', + executable.replace("pythonw.exe", "python.exe")) + + def initialize(self): + GeneralConfigPage.initialize(self) + self.pyexec_edit.textChanged.connect(self.python_executable_changed) + self.cus_exec_radio.toggled.connect(self.python_executable_switched) + + def setup_page(self): + newcb = self.create_checkbox + + # Python executable Group + pyexec_group = QGroupBox(_("Python interpreter")) + pyexec_bg = QButtonGroup(pyexec_group) + pyexec_label = QLabel(_("Select the Python interpreter for all Spyder " + "consoles")) + def_exec_radio = self.create_radiobutton( + _("Default (i.e. the same as Spyder's)"), + 'default', button_group=pyexec_bg) + self.cus_exec_radio = self.create_radiobutton( + _("Use the following Python interpreter:"), + 'custom', button_group=pyexec_bg) + if os.name == 'nt': + filters = _("Executables")+" (*.exe)" + else: + filters = None + pyexec_file = self.create_browsefile('', 'executable', filters=filters) + for le in self.lineedits: + if self.lineedits[le][0] == 'executable': + self.pyexec_edit = le + def_exec_radio.toggled.connect(pyexec_file.setDisabled) + self.cus_exec_radio.toggled.connect(pyexec_file.setEnabled) + pyexec_layout = QVBoxLayout() + pyexec_layout.addWidget(pyexec_label) + pyexec_layout.addWidget(def_exec_radio) + pyexec_layout.addWidget(self.cus_exec_radio) + pyexec_layout.addWidget(pyexec_file) + pyexec_group.setLayout(pyexec_layout) + + # UMR Group + umr_group = QGroupBox(_("User Module Reloader (UMR)")) + umr_label = QLabel(_("UMR forces Python to reload modules which were " + "imported when executing a file in a Python or " + "IPython console with the runfile " + "function.")) + umr_label.setWordWrap(True) + umr_enabled_box = newcb(_("Enable UMR"), 'umr/enabled', + msg_if_enabled=True, msg_warning=_( + "This option will enable the User Module Reloader (UMR) " + "in Python/IPython consoles. UMR forces Python to " + "reload deeply modules during import when running a " + "Python script using the Spyder's builtin function " + "runfile." + "

    1. UMR may require to restart the " + "console in which it will be called " + "(otherwise only newly imported modules will be " + "reloaded when executing files)." + "

    2. If errors occur when re-running a " + "PyQt-based program, please check that the Qt objects " + "are properly destroyed (e.g. you may have to use the " + "attribute Qt.WA_DeleteOnClose on your main " + "window, using the setAttribute method)"), + ) + umr_verbose_box = newcb(_("Show reloaded modules list"), + 'umr/verbose', msg_info=_( + "Please note that these changes will " + "be applied only to new consoles")) + umr_namelist_btn = QPushButton( + _("Set UMR excluded (not reloaded) modules")) + umr_namelist_btn.clicked.connect(self.set_umr_namelist) + + umr_layout = QVBoxLayout() + umr_layout.addWidget(umr_label) + umr_layout.addWidget(umr_enabled_box) + umr_layout.addWidget(umr_verbose_box) + umr_layout.addWidget(umr_namelist_btn) + umr_group.setLayout(umr_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(pyexec_group) + vlayout.addWidget(umr_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + def python_executable_changed(self, pyexec): + """Custom Python executable value has been changed""" + if not self.cus_exec_radio.isChecked(): + return + if not is_text_string(pyexec): + pyexec = to_text_string(pyexec.toUtf8(), 'utf-8') + self.warn_python_compatibility(pyexec) + + def python_executable_switched(self, custom): + """Python executable default/custom radio button has been toggled""" + def_pyexec = get_python_executable() + cust_pyexec = self.pyexec_edit.text() + if not is_text_string(cust_pyexec): + cust_pyexec = to_text_string(cust_pyexec.toUtf8(), 'utf-8') + if def_pyexec != cust_pyexec: + if custom: + self.warn_python_compatibility(cust_pyexec) + + def warn_python_compatibility(self, pyexec): + if not osp.isfile(pyexec): + return + spyder_version = sys.version_info[0] + try: + args = ["-c", "import sys; print(sys.version_info[0])"] + proc = programs.run_program(pyexec, args) + console_version = int(proc.communicate()[0]) + except IOError: + console_version = spyder_version + if spyder_version != console_version: + QMessageBox.warning(self, _('Warning'), + _("You selected a Python %d interpreter for the console " + "but Spyder is running on Python %d!.

    " + "Although this is possible, we recommend you to install and " + "run Spyder directly with your selected interpreter, to avoid " + "seeing false warnings and errors due to the incompatible " + "syntax between these two Python versions." + ) % (console_version, spyder_version), QMessageBox.Ok) + + def set_umr_namelist(self): + """Set UMR excluded modules name list""" + arguments, valid = QInputDialog.getText(self, _('UMR'), + _("Set the list of excluded modules as " + "this: numpy, scipy"), + QLineEdit.Normal, + ", ".join(self.get_option('umr/namelist'))) + if valid: + arguments = to_text_string(arguments) + if arguments: + namelist = arguments.replace(' ', '').split(',') + fixed_namelist = [] + non_ascii_namelist = [] + for module_name in namelist: + if PY2: + if all(ord(c) < 128 for c in module_name): + if programs.is_module_installed(module_name): + fixed_namelist.append(module_name) + else: + QMessageBox.warning(self, _('Warning'), + _("You are working with Python 2, this means that " + "you can not import a module that contains non-" + "ascii characters."), QMessageBox.Ok) + non_ascii_namelist.append(module_name) + elif programs.is_module_installed(module_name): + fixed_namelist.append(module_name) + invalid = ", ".join(set(namelist)-set(fixed_namelist)- + set(non_ascii_namelist)) + if invalid: + QMessageBox.warning(self, _('UMR'), + _("The following modules are not " + "installed on your machine:\n%s" + ) % invalid, QMessageBox.Ok) + QMessageBox.information(self, _('UMR'), + _("Please note that these changes will " + "be applied only to new Python/IPython " + "consoles"), QMessageBox.Ok) + else: + fixed_namelist = [] + self.set_option('umr/namelist', fixed_namelist) + + def apply_settings(self, options): + self.main.apply_settings() diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/onlinehelp.py spyder-3.0.2+dfsg1/spyder/plugins/onlinehelp.py --- spyder-2.3.8+dfsg1/spyder/plugins/onlinehelp.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/onlinehelp.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Online Help Plugin""" + +# Standard library imports +import os.path as osp + +# Third party imports +from qtpy.QtCore import Signal + +# Local imports +from spyder.config.base import _, get_conf_path +from spyder.plugins import SpyderPluginMixin +from spyder.py3compat import to_text_string +from spyder.widgets.pydocgui import PydocBrowser + + +class OnlineHelp(PydocBrowser, SpyderPluginMixin): + """ + Online Help Plugin + """ + sig_option_changed = Signal(str, object) + CONF_SECTION = 'onlinehelp' + LOG_PATH = get_conf_path(CONF_SECTION) + + def __init__(self, parent): + self.main = parent + PydocBrowser.__init__(self, parent) + SpyderPluginMixin.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + + self.register_widget_shortcuts(self.find_widget) + + self.webview.set_zoom_factor(self.get_option('zoom_factor')) + self.url_combo.setMaxCount(self.get_option('max_history_entries')) + self.url_combo.addItems( self.load_history() ) + + #------ Public API --------------------------------------------------------- + def load_history(self, obj=None): + """Load history from a text file in user home directory""" + if osp.isfile(self.LOG_PATH): + history = [line.replace('\n', '') + for line in open(self.LOG_PATH, 'r').readlines()] + else: + history = [] + return history + + def save_history(self): + """Save history to a text file in user home directory""" + open(self.LOG_PATH, 'w').write("\n".join( \ + [to_text_string(self.url_combo.itemText(index)) + for index in range(self.url_combo.count())] )) + + #------ SpyderPluginMixin API --------------------------------------------- + def visibility_changed(self, enable): + """DockWidget visibility has changed""" + SpyderPluginMixin.visibility_changed(self, enable) + if enable and not self.is_server_running(): + self.initialize() + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _('Online help') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + self.url_combo.lineEdit().selectAll() + return self.url_combo + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + self.save_history() + self.set_option('zoom_factor', self.webview.get_zoom_factor()) + return True + + def refresh_plugin(self): + """Refresh widget""" + pass + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + return [] + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.main.add_dockwidget(self) + \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/outlineexplorer.py spyder-3.0.2+dfsg1/spyder/plugins/outlineexplorer.py --- spyder-2.3.8+dfsg1/spyder/plugins/outlineexplorer.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/outlineexplorer.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Outline Explorer Plugin + +Data for outline are provided by method .get_outlineexplorer_data() of +highlighter of assigned editor. For example, for Python files code editor uses +highlighter spyder.utils.syntaxhighlighters.PythonSH +""" + +# Third party imports +from qtpy.QtCore import Signal + +# Local imports +from spyder.config.base import _ +from spyder.plugins import SpyderPluginMixin +from spyder.py3compat import is_text_string +from spyder.utils import icon_manager as ima +from spyder.widgets.editortools import OutlineExplorerWidget + + +class OutlineExplorer(OutlineExplorerWidget, SpyderPluginMixin): + CONF_SECTION = 'outline_explorer' + sig_option_changed = Signal(str, object) + + def __init__(self, parent=None, fullpath_sorting=True): + show_fullpath = self.get_option('show_fullpath') + show_all_files = self.get_option('show_all_files') + show_comments = self.get_option('show_comments') + OutlineExplorerWidget.__init__(self, parent=parent, + show_fullpath=show_fullpath, + fullpath_sorting=fullpath_sorting, + show_all_files=show_all_files, + show_comments=show_comments) + SpyderPluginMixin.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + + self.treewidget.header().hide() + self.load_config() + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _("Outline") + + def get_plugin_icon(self): + """Return widget icon""" + return ima.icon('outline_explorer') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.treewidget + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + return [] + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.main.restore_scrollbar_position.connect( + self.restore_scrollbar_position) + self.main.add_dockwidget(self) + + def refresh_plugin(self): + """Refresh project explorer widget""" + pass + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + self.save_config() + return True + + #------ SpyderPluginMixin API --------------------------------------------- + def visibility_changed(self, enable): + """DockWidget visibility has changed""" + SpyderPluginMixin.visibility_changed(self, enable) + if enable: + self.outlineexplorer_is_visible.emit() + + #------ Public API --------------------------------------------------------- + def restore_scrollbar_position(self): + """Restoring scrollbar position after main window is visible""" + scrollbar_pos = self.get_option('scrollbar_position', None) + if scrollbar_pos is not None: + self.treewidget.set_scrollbar_position(scrollbar_pos) + + def save_config(self): + """Save configuration: tree widget state""" + for option, value in list(self.get_options().items()): + self.set_option(option, value) + self.set_option('expanded_state', self.treewidget.get_expanded_state()) + self.set_option('scrollbar_position', + self.treewidget.get_scrollbar_position()) + + def load_config(self): + """Load configuration: tree widget state""" + expanded_state = self.get_option('expanded_state', None) + # Sometimes the expanded state option may be truncated in .ini file + # (for an unknown reason), in this case it would be converted to a + # string by 'userconfig': + if is_text_string(expanded_state): + expanded_state = None + if expanded_state is not None: + self.treewidget.set_expanded_state(expanded_state) diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/projects.py spyder-3.0.2+dfsg1/spyder/plugins/projects.py --- spyder-2.3.8+dfsg1/spyder/plugins/projects.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/projects.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,403 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Projects Plugin + +It handles closing, opening and switching among projetcs and also +updating the file tree explorer associated with a project +""" + +# Standard library imports +import os.path as osp + +# Third party imports +from qtpy.compat import getexistingdirectory +from qtpy.QtCore import Signal, Slot +from qtpy.QtWidgets import QMenu, QMessageBox + +# Local imports +from spyder.config.base import _, get_home_dir +from spyder.plugins import SpyderPluginMixin +from spyder.py3compat import is_text_string, getcwd +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import add_actions, create_action +from spyder.widgets.projects.explorer import ProjectExplorerWidget +from spyder.widgets.projects.projectdialog import ProjectDialog +from spyder.widgets.projects import EmptyProject + + +class Projects(ProjectExplorerWidget, SpyderPluginMixin): + """Projects plugin""" + + CONF_SECTION = 'project_explorer' + + open_terminal = Signal(str) + open_interpreter = Signal(str) + pythonpath_changed = Signal() + + # File operations + create_module = Signal(str) + edit = Signal(str) + removed = Signal(str) + removed_tree = Signal(str) + renamed = Signal(str, str) + redirect_stdio = Signal(bool) + + # Project handling + sig_project_created = Signal(object, object, object) + sig_project_loaded = Signal(object) + sig_project_closed = Signal(object) + + def __init__(self, parent=None): + ProjectExplorerWidget.__init__(self, parent=parent, + name_filters=self.get_option('name_filters'), + show_all=self.get_option('show_all'), + show_hscrollbar=self.get_option('show_hscrollbar')) + SpyderPluginMixin.__init__(self, parent) + + self.recent_projects = self.get_option('recent_projects', default=[]) + self.current_active_project = None + self.latest_project = None + + self.editor = None + self.workingdirectory = None + + # Initialize plugin + self.initialize_plugin() + self.setup_project(self.get_active_project_path()) + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _("Project explorer") + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.treewidget + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + self.new_project_action = create_action(self, + _("New Project..."), + triggered=self.create_new_project) + self.open_project_action = create_action(self, + _("Open Project..."), + triggered=lambda v: self.open_project()) + self.close_project_action = create_action(self, + _("Close Project"), + triggered=self.close_project) + self.delete_project_action = create_action(self, + _("Delete Project"), + triggered=self.delete_project) + self.clear_recent_projects_action =\ + create_action(self, _("Clear this list"), + triggered=self.clear_recent_projects) + self.edit_project_preferences_action =\ + create_action(self, _("Project Preferences"), + triggered=self.edit_project_preferences) + self.recent_project_menu = QMenu(_("Recent Projects"), self) + explorer_action = self.toggle_view_action + + self.main.projects_menu_actions += [self.new_project_action, + None, + self.open_project_action, + self.close_project_action, + self.delete_project_action, + None, + self.recent_project_menu, + explorer_action] + + self.setup_menu_actions() + return [] + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.editor = self.main.editor + self.workingdirectory = self.main.workingdirectory + + self.main.pythonpath_changed() + self.main.restore_scrollbar_position.connect( + self.restore_scrollbar_position) + self.pythonpath_changed.connect(self.main.pythonpath_changed) + self.create_module.connect(self.editor.new) + self.edit.connect(self.editor.load) + self.removed.connect(self.editor.removed) + self.removed_tree.connect(self.editor.removed_tree) + self.renamed.connect(self.editor.renamed) + self.editor.set_projects(self) + self.main.add_dockwidget(self) + + self.sig_open_file.connect(self.main.open_file) + + # New project connections. Order matters! + self.sig_project_loaded.connect( + lambda v: self.workingdirectory.chdir(v)) + self.sig_project_loaded.connect( + lambda v: self.main.update_window_title()) + self.sig_project_loaded.connect( + lambda v: self.editor.setup_open_files()) + self.sig_project_loaded.connect(self.update_explorer) + self.sig_project_closed[object].connect( + lambda v: self.workingdirectory.chdir(self.get_last_working_dir())) + self.sig_project_closed.connect( + lambda v: self.main.update_window_title()) + self.sig_project_closed.connect( + lambda v: self.editor.setup_open_files()) + self.recent_project_menu.aboutToShow.connect(self.setup_menu_actions) + + def refresh_plugin(self): + """Refresh project explorer widget""" + pass + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + self.save_config() + self.closing_widget() + return True + + #------ Public API --------------------------------------------------------- + def setup_menu_actions(self): + """Setup and update the menu actions.""" + self.recent_project_menu.clear() + self.recent_projects_actions = [] + if self.recent_projects: + for project in self.recent_projects: + if self.is_valid_project(project): + name = project.replace(get_home_dir(), '~') + action = create_action(self, + name, + icon = ima.icon('project'), + triggered=lambda v, path=project: self.open_project(path=path)) + self.recent_projects_actions.append(action) + else: + self.recent_projects.remove(project) + self.recent_projects_actions += [None, + self.clear_recent_projects_action] + else: + self.recent_projects_actions = [self.clear_recent_projects_action] + add_actions(self.recent_project_menu, self.recent_projects_actions) + self.update_project_actions() + + def update_project_actions(self): + """Update actions of the Projects menu""" + if self.recent_projects: + self.clear_recent_projects_action.setEnabled(True) + else: + self.clear_recent_projects_action.setEnabled(False) + + active = bool(self.get_active_project_path()) + self.close_project_action.setEnabled(active) + self.delete_project_action.setEnabled(active) + self.edit_project_preferences_action.setEnabled(active) + + def edit_project_preferences(self): + """Edit Spyder active project preferences""" + from spyder.widgets.projects.configdialog import ProjectPreferences + if self.project_active: + active_project = self.project_list[0] + dlg = ProjectPreferences(self, active_project) +# dlg.size_change.connect(self.set_project_prefs_size) +# if self.projects_prefs_dialog_size is not None: +# dlg.resize(self.projects_prefs_dialog_size) + dlg.show() +# dlg.check_all_settings() +# dlg.pages_widget.currentChanged.connect(self.__preference_page_changed) + dlg.exec_() + + @Slot() + def create_new_project(self): + """Create new project""" + active_project = self.current_active_project + dlg = ProjectDialog(self) + dlg.sig_project_creation_requested.connect(self._create_project) + dlg.sig_project_creation_requested.connect(self.sig_project_created) + if dlg.exec_(): + pass + if active_project is None: + self.show_explorer() + self.pythonpath_changed.emit() + self.restart_consoles() + + def _create_project(self, path): + """Create a new project.""" + self.open_project(path=path) + self.setup_menu_actions() + self.add_to_recent(path) + + def open_project(self, path=None, restart_consoles=True, + save_previous_files=True): + """Open the project located in `path`""" + if path is None: + basedir = get_home_dir() + path = getexistingdirectory(parent=self, + caption=_("Open project"), + basedir=basedir) + if not self.is_valid_project(path): + if path: + QMessageBox.critical(self, _('Error'), + _("%s is not a Spyder project!") % path) + return + else: + self.add_to_recent(path) + + # A project was not open before + if self.current_active_project is None: + if save_previous_files: + self.editor.save_open_files() + self.editor.set_option('last_working_dir', getcwd()) + self.show_explorer() + else: # we are switching projects + self.set_project_filenames(self.editor.get_open_filenames()) + + self.current_active_project = EmptyProject(path) + self.latest_project = EmptyProject(path) + self.set_option('current_project_path', self.get_active_project_path()) + self.setup_menu_actions() + self.sig_project_loaded.emit(path) + self.pythonpath_changed.emit() + if restart_consoles: + self.restart_consoles() + + def close_project(self): + """ + Close current project and return to a window without an active + project + """ + if self.current_active_project: + path = self.current_active_project.root_path + self.set_project_filenames(self.editor.get_open_filenames()) + self.current_active_project = None + self.set_option('current_project_path', None) + self.setup_menu_actions() + self.sig_project_closed.emit(path) + self.pythonpath_changed.emit() + self.dockwidget.close() + self.clear() + self.restart_consoles() + + def clear_recent_projects(self): + """Clear the list of recent projects""" + self.recent_projects = [] + self.setup_menu_actions() + + def get_active_project(self): + """Get the active project""" + return self.current_active_project + + def reopen_last_project(self): + """ + Reopen the active project when Spyder was closed last time, if any + """ + current_project_path = self.get_option('current_project_path', + default=None) + + # Needs a safer test of project existence! + if current_project_path and \ + self.is_valid_project(current_project_path): + self.open_project(path=current_project_path, + restart_consoles=False, + save_previous_files=False) + self.load_config() + + def get_project_filenames(self): + """Get the list of recent filenames of a project""" + recent_files = [] + if self.current_active_project: + recent_files = self.current_active_project.get_recent_files() + elif self.latest_project: + recent_files = self.latest_project.get_recent_files() + return recent_files + + def set_project_filenames(self, recent_files): + """Set the list of open file names in a project""" + if self.current_active_project: + self.current_active_project.set_recent_files(recent_files) + + def get_active_project_path(self): + """Get path of the active project""" + active_project_path = None + if self.current_active_project: + active_project_path = self.current_active_project.root_path + return active_project_path + + def get_pythonpath(self, at_start=False): + """Get project path as a list to be added to PYTHONPATH""" + if at_start: + current_path = self.get_option('current_project_path', + default=None) + else: + current_path = self.get_active_project_path() + if current_path is None: + return [] + else: + return [current_path] + + def get_last_working_dir(self): + """Get the path of the last working directory""" + return self.editor.get_option('last_working_dir', default=getcwd()) + + def save_config(self): + """Save configuration: opened projects & tree widget state""" + self.set_option('recent_projects', self.recent_projects) + self.set_option('expanded_state', self.treewidget.get_expanded_state()) + self.set_option('scrollbar_position', + self.treewidget.get_scrollbar_position()) + + def load_config(self): + """Load configuration: opened projects & tree widget state""" + expanded_state = self.get_option('expanded_state', None) + # Sometimes the expanded state option may be truncated in .ini file + # (for an unknown reason), in this case it would be converted to a + # string by 'userconfig': + if is_text_string(expanded_state): + expanded_state = None + if expanded_state is not None: + self.treewidget.set_expanded_state(expanded_state) + + def restore_scrollbar_position(self): + """Restoring scrollbar position after main window is visible""" + scrollbar_pos = self.get_option('scrollbar_position', None) + if scrollbar_pos is not None: + self.treewidget.set_scrollbar_position(scrollbar_pos) + + def update_explorer(self): + """Update explorer tree""" + self.setup_project(self.get_active_project_path()) + + def show_explorer(self): + """Show the explorer""" + if self.dockwidget.isHidden(): + self.dockwidget.show() + self.dockwidget.raise_() + self.dockwidget.update() + + def restart_consoles(self): + """Restart consoles when closing, opening and switching projects""" + self.main.extconsole.restart() + if self.main.ipyconsole: + self.main.ipyconsole.restart() + + def is_valid_project(self, path): + """Check if a directory is a valid Spyder project""" + spy_project_dir = osp.join(path, '.spyproject') + if osp.isdir(path) and osp.isdir(spy_project_dir): + return True + else: + return False + + def add_to_recent(self, project): + """ + Add an entry to recent projetcs + + We only maintain the list of the 10 most recent projects + """ + if project not in self.recent_projects: + self.recent_projects.insert(0, project) + self.recent_projects = self.recent_projects[:10] diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/runconfig.py spyder-3.0.2+dfsg1/spyder/plugins/runconfig.py --- spyder-2.3.8+dfsg1/spyder/plugins/runconfig.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/runconfig.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,530 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Run configurations related dialogs and widgets and data models""" + +# Standard library imports +import os.path as osp + +# Third party imports +from qtpy.compat import getexistingdirectory +from qtpy.QtCore import QSize, Qt, Signal, Slot +from qtpy.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QDialog, + QDialogButtonBox, QFrame, QGridLayout, QGroupBox, + QHBoxLayout, QLabel, QLineEdit, QMessageBox, + QPushButton, QRadioButton, QSizePolicy, + QStackedWidget, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import _ +from spyder.config.main import CONF +from spyder.plugins.configdialog import GeneralConfigPage +from spyder.py3compat import getcwd, to_text_string +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_OPTION = 'default/interpreter/current' +DEDICATED_INTERPRETER_OPTION = 'default/interpreter/dedicated' +SYSTERM_INTERPRETER_OPTION = 'default/interpreter/systerm' + +WDIR_USE_SCRIPT_DIR_OPTION = 'default/wdir/use_script_directory' +WDIR_USE_FIXED_DIR_OPTION = 'default/wdir/use_fixed_directory' +WDIR_FIXED_DIR_OPTION = 'default/wdir/fixed_directory' + +ALWAYS_OPEN_FIRST_RUN = _("Always show %s on a first file run") +ALWAYS_OPEN_FIRST_RUN_OPTION = 'open_on_firstrun' + + +class RunConfiguration(object): + """Run configuration""" + def __init__(self, fname=None): + self.args = None + self.args_enabled = None + self.wdir = None + self.wdir_enabled = 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 + 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) + self.wdir_enabled = True + + def set(self, options): + self.args = options.get('args', '') + self.args_enabled = options.get('args/enabled', False) + if CONF.get('run', WDIR_USE_FIXED_DIR_OPTION, False): + default_wdir = CONF.get('run', WDIR_FIXED_DIR_OPTION, getcwd()) + self.wdir = options.get('workdir', default_wdir) + self.wdir_enabled = True + else: + self.wdir = options.get('workdir', getcwd()) + self.wdir_enabled = options.get('workdir/enabled', False) + self.current = options.get('current', + CONF.get('run', CURRENT_INTERPRETER_OPTION, True)) + self.systerm = options.get('systerm', + 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', '') + self.python_args_enabled = options.get('python_args/enabled', False) + + def get(self): + return { + 'args/enabled': self.args_enabled, + 'args': self.args, + 'workdir/enabled': self.wdir_enabled, + 'workdir': self.wdir, + '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, + } + + def get_working_directory(self): + if self.wdir_enabled: + return self.wdir + else: + return '' + + def get_arguments(self): + if self.args_enabled: + return self.args + else: + return '' + + def get_python_arguments(self): + if self.python_args_enabled: + return self.python_args + else: + return '' + + +def _get_run_configurations(): + history_count = CONF.get('run', 'history', 20) + try: + return [(filename, options) + for filename, options in CONF.get('run', 'configurations', []) + if osp.isfile(filename)][:history_count] + except ValueError: + 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() + for filename, options in configurations: + if fname == filename: + runconf = RunConfiguration() + runconf.set(options) + return runconf + + +class RunConfigOptions(QWidget): + """Run configuration options""" + def __init__(self, parent=None): + QWidget.__init__(self, parent) + + self.current_radio = None + self.dedicated_radio = None + self.systerm_radio = None + + self.runconf = RunConfiguration() + + firstrun_o = CONF.get('run', ALWAYS_OPEN_FIRST_RUN_OPTION, False) + + # --- General settings ---- + common_group = QGroupBox(_("General settings")) + common_layout = QGridLayout() + common_group.setLayout(common_layout) + self.clo_cb = QCheckBox(_("Command line options:")) + common_layout.addWidget(self.clo_cb, 0, 0) + self.clo_edit = QLineEdit() + self.clo_cb.toggled.connect(self.clo_edit.setEnabled) + self.clo_edit.setEnabled(False) + common_layout.addWidget(self.clo_edit, 0, 1) + self.wd_cb = QCheckBox(_("Working directory:")) + common_layout.addWidget(self.wd_cb, 1, 0) + wd_layout = QHBoxLayout() + self.wd_edit = QLineEdit() + self.wd_cb.toggled.connect(self.wd_edit.setEnabled) + self.wd_edit.setEnabled(False) + wd_layout.addWidget(self.wd_edit) + browse_btn = QPushButton(ima.icon('DirOpenIcon'), '', self) + browse_btn.setToolTip(_("Select directory")) + browse_btn.clicked.connect(self.select_directory) + wd_layout.addWidget(browse_btn) + common_layout.addLayout(wd_layout, 1, 1) + 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() + interpreter_group.setLayout(interpreter_layout) + self.current_radio = QRadioButton(CURRENT_INTERPRETER) + interpreter_layout.addWidget(self.current_radio) + self.dedicated_radio = QRadioButton(DEDICATED_INTERPRETER) + 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) + self.interact_cb = QCheckBox(_("Interact with the Python " + "console after execution")) + new_layout.addWidget(self.interact_cb, 1, 0, 1, -1) + + self.show_kill_warning_cb = QCheckBox(_("Show warning when killing" + " running process")) + + 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) + 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) + + + # Checkbox to preserve the old behavior, i.e. always open the dialog + # on first run + hline = QFrame() + hline.setFrameShape(QFrame.HLine) + hline.setFrameShadow(QFrame.Sunken) + self.firstrun_cb = QCheckBox(ALWAYS_OPEN_FIRST_RUN % _("this dialog")) + self.firstrun_cb.clicked.connect(self.set_firstrun_o) + self.firstrun_cb.setChecked(firstrun_o) + + layout = QVBoxLayout() + layout.addWidget(interpreter_group) + layout.addWidget(common_group) + layout.addWidget(new_group) + layout.addWidget(hline) + layout.addWidget(self.firstrun_cb) + self.setLayout(layout) + + def select_directory(self): + """Select directory""" + basedir = to_text_string(self.wd_edit.text()) + if not osp.isdir(basedir): + basedir = getcwd() + directory = getexistingdirectory(self, _("Select directory"), basedir) + if directory: + self.wd_edit.setText(directory) + self.wd_cb.setChecked(True) + + def set(self, options): + self.runconf.set(options) + self.clo_cb.setChecked(self.runconf.args_enabled) + self.clo_edit.setText(self.runconf.args) + self.wd_cb.setChecked(self.runconf.wdir_enabled) + self.wd_edit.setText(self.runconf.wdir) + if self.runconf.current: + self.current_radio.setChecked(True) + elif self.runconf.systerm: + self.systerm_radio.setChecked(True) + 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) + + def get(self): + self.runconf.args_enabled = self.clo_cb.isChecked() + self.runconf.args = to_text_string(self.clo_edit.text()) + self.runconf.wdir_enabled = self.wd_cb.isChecked() + self.runconf.wdir = to_text_string(self.wd_edit.text()) + 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()) + return self.runconf.get() + + def is_valid(self): + wdir = to_text_string(self.wd_edit.text()) + if not self.wd_cb.isChecked() or osp.isdir(wdir): + return True + else: + QMessageBox.critical(self, _("Run configuration"), + _("The following working directory is " + "not valid:
    %s") % wdir) + return False + + def set_firstrun_o(self): + CONF.set('run', ALWAYS_OPEN_FIRST_RUN_OPTION, + self.firstrun_cb.isChecked()) + + +class BaseRunConfigDialog(QDialog): + """Run configuration dialog box, base widget""" + size_change = Signal(QSize) + + def __init__(self, parent=None): + QDialog.__init__(self, parent) + + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + + self.setWindowIcon(ima.icon('run_settings')) + layout = QVBoxLayout() + self.setLayout(layout) + + def add_widgets(self, *widgets_or_spacings): + """Add widgets/spacing to dialog vertical layout""" + layout = self.layout() + for widget_or_spacing in widgets_or_spacings: + if isinstance(widget_or_spacing, int): + layout.addSpacing(widget_or_spacing) + else: + layout.addWidget(widget_or_spacing) + + def add_button_box(self, stdbtns): + """Create dialog button box and add it to the dialog layout""" + bbox = QDialogButtonBox(stdbtns) + run_btn = bbox.addButton(_("Run"), QDialogButtonBox.AcceptRole) + run_btn.clicked.connect(self.run_btn_clicked) + bbox.accepted.connect(self.accept) + bbox.rejected.connect(self.reject) + btnlayout = QHBoxLayout() + btnlayout.addStretch(1) + btnlayout.addWidget(bbox) + self.layout().addLayout(btnlayout) + + def resizeEvent(self, event): + """ + Reimplement Qt method to be able to save the widget's size from the + main application + """ + QDialog.resizeEvent(self, event) + self.size_change.emit(self.size()) + + def run_btn_clicked(self): + """Run button was just clicked""" + pass + + def setup(self, fname): + """Setup Run Configuration dialog with filename *fname*""" + raise NotImplementedError + + +class RunConfigOneDialog(BaseRunConfigDialog): + """Run configuration dialog box: single file version""" + def __init__(self, parent=None): + BaseRunConfigDialog.__init__(self, parent) + self.filename = None + self.runconfigoptions = None + + def setup(self, fname): + """Setup Run Configuration dialog with filename *fname*""" + self.filename = fname + self.runconfigoptions = RunConfigOptions(self) + self.runconfigoptions.set(RunConfiguration(fname).get()) + self.add_widgets(self.runconfigoptions) + self.add_button_box(QDialogButtonBox.Cancel) + self.setWindowTitle(_("Run settings for %s") % osp.basename(fname)) + + @Slot() + def accept(self): + """Reimplement Qt method""" + if not self.runconfigoptions.is_valid(): + return + configurations = _get_run_configurations() + configurations.insert(0, (self.filename, self.runconfigoptions.get())) + _set_run_configurations(configurations) + QDialog.accept(self) + + def get_configuration(self): + # It is import to avoid accessing Qt C++ object as it has probably + # already been destroyed, due to the Qt.WA_DeleteOnClose attribute + return self.runconfigoptions.runconf + + +class RunConfigDialog(BaseRunConfigDialog): + """Run configuration dialog box: multiple file version""" + def __init__(self, parent=None): + BaseRunConfigDialog.__init__(self, parent) + self.file_to_run = None + self.combo = None + self.stack = None + + def run_btn_clicked(self): + """Run button was just clicked""" + self.file_to_run = to_text_string(self.combo.currentText()) + + def setup(self, fname): + """Setup Run Configuration dialog with filename *fname*""" + combo_label = QLabel(_("Select a run configuration:")) + self.combo = QComboBox() + self.combo.setMaxVisibleItems(20) + self.combo.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) + self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + + self.stack = QStackedWidget() + + configurations = _get_run_configurations() + for index, (filename, options) in enumerate(configurations): + if fname == filename: + break + else: + # There is no run configuration for script *fname*: + # creating a temporary configuration that will be kept only if + # dialog changes are accepted by the user + configurations.insert(0, (fname, RunConfiguration(fname).get())) + index = 0 + for filename, options in configurations: + widget = RunConfigOptions(self) + widget.set(options) + self.combo.addItem(filename) + self.stack.addWidget(widget) + self.combo.currentIndexChanged.connect(self.stack.setCurrentIndex) + self.combo.setCurrentIndex(index) + + self.add_widgets(combo_label, self.combo, 10, self.stack) + self.add_button_box(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + + self.setWindowTitle(_("Run Settings")) + + def accept(self): + """Reimplement Qt method""" + configurations = [] + for index in range(self.stack.count()): + filename = to_text_string(self.combo.itemText(index)) + runconfigoptions = self.stack.widget(index) + if index == self.stack.currentIndex() and\ + not runconfigoptions.is_valid(): + return + options = runconfigoptions.get() + configurations.append( (filename, options) ) + _set_run_configurations(configurations) + QDialog.accept(self) + + +class RunConfigPage(GeneralConfigPage): + """Default Run Settings configuration page""" + CONF_SECTION = "run" + + NAME = _("Run") + ICON = ima.icon('run') + + def setup_page(self): + run_dlg = _("Run Settings") + run_menu = _("Run") + about_label = QLabel(_("The following are the default %s. "\ + "These options may be overriden using the "\ + "%s dialog box (see the %s menu)"\ + ) % (run_dlg, run_dlg, run_menu)) + about_label.setWordWrap(True) + + interpreter_group = QGroupBox(_("Console")) + interpreter_bg = QButtonGroup(interpreter_group) + self.current_radio = self.create_radiobutton(CURRENT_INTERPRETER, + CURRENT_INTERPRETER_OPTION, True, + button_group=interpreter_bg) + self.dedicated_radio = self.create_radiobutton(DEDICATED_INTERPRETER, + DEDICATED_INTERPRETER_OPTION, False, + button_group=interpreter_bg) + self.systerm_radio = self.create_radiobutton(SYSTERM_INTERPRETER, + SYSTERM_INTERPRETER_OPTION, False, + button_group=interpreter_bg) + + interpreter_layout = QVBoxLayout() + interpreter_group.setLayout(interpreter_layout) + interpreter_layout.addWidget(self.current_radio) + interpreter_layout.addWidget(self.dedicated_radio) + interpreter_layout.addWidget(self.systerm_radio) + + general_group = QGroupBox(_("General settings")) + wdir_bg = QButtonGroup(general_group) + wdir_label = QLabel(_("Default working directory is:")) + wdir_label.setWordWrap(True) + dirname_radio = self.create_radiobutton(_("the script directory"), + WDIR_USE_SCRIPT_DIR_OPTION, True, + button_group=wdir_bg) + thisdir_radio = self.create_radiobutton(_("the following directory:"), + WDIR_USE_FIXED_DIR_OPTION, False, + button_group=wdir_bg) + thisdir_bd = self.create_browsedir("", WDIR_FIXED_DIR_OPTION, getcwd()) + thisdir_radio.toggled.connect(thisdir_bd.setEnabled) + dirname_radio.toggled.connect(thisdir_bd.setDisabled) + thisdir_layout = QHBoxLayout() + thisdir_layout.addWidget(thisdir_radio) + thisdir_layout.addWidget(thisdir_bd) + + post_mortem = self.create_checkbox( + _("Enter debugging mode when errors appear during execution"), + 'post_mortem', False) + + general_layout = QVBoxLayout() + general_layout.addWidget(wdir_label) + general_layout.addWidget(dirname_radio) + general_layout.addLayout(thisdir_layout) + 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) + + dedicated_layout = QVBoxLayout() + dedicated_layout.addWidget(interact_after) + dedicated_layout.addWidget(show_warning) + dedicated_group.setLayout(dedicated_layout) + + firstrun_cb = self.create_checkbox( + ALWAYS_OPEN_FIRST_RUN % _("Run Settings dialog"), + ALWAYS_OPEN_FIRST_RUN_OPTION, False) + + vlayout = QVBoxLayout() + vlayout.addWidget(about_label) + vlayout.addSpacing(10) + vlayout.addWidget(interpreter_group) + vlayout.addWidget(general_group) + vlayout.addWidget(dedicated_group) + vlayout.addWidget(firstrun_cb) + vlayout.addStretch(1) + self.setLayout(vlayout) + + def apply_settings(self, options): + pass diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/shortcuts.py spyder-3.0.2+dfsg1/spyder/plugins/shortcuts.py --- spyder-2.3.8+dfsg1/spyder/plugins/shortcuts.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/shortcuts.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,842 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Shortcut management""" + +# Standard library imports +from __future__ import print_function +import os +import re +import sys + +# Third party imports +from qtpy import PYQT5 +from qtpy.compat import from_qvariant, to_qvariant +from qtpy.QtCore import (QAbstractTableModel, QModelIndex, QRegExp, + QSortFilterProxyModel, Qt) +from qtpy.QtGui import (QKeySequence, QRegExpValidator) +from qtpy.QtWidgets import (QAbstractItemView, QApplication, QDialog, + QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel, + QLineEdit, QMessageBox, QPushButton, QSpacerItem, + QTableView, QVBoxLayout) + +# Local imports +from spyder.config.base import _, debug_print +from spyder.config.gui import (get_shortcut, iter_shortcuts, + reset_shortcuts, set_shortcut) +from spyder.plugins.configdialog import GeneralConfigPage +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import get_std_icon +from spyder.utils.stringmatching import get_search_scores, get_search_regex +from spyder.widgets.helperwidgets import HTMLDelegate +from spyder.widgets.helperwidgets import HelperToolButton + + +MODIFIERS = {Qt.Key_Shift: Qt.SHIFT, + Qt.Key_Control: Qt.CTRL, + Qt.Key_Alt: Qt.ALT, + Qt.Key_Meta: Qt.META} + +# Valid shortcut keys +SINGLE_KEYS = ["F{}".format(_i) for _i in range(1, 36)] + ["Delete", "Escape"] +KEYSTRINGS = ["Tab", "Backtab", "Backspace", "Return", "Enter", + "Pause", "Print", "Clear", "Home", "End", "Left", + "Up", "Right", "Down", "PageUp", "PageDown"] + \ + ["Space", "Exclam", "QuoteDbl", "NumberSign", "Dollar", + "Percent", "Ampersand", "Apostrophe", "ParenLeft", + "ParenRight", "Asterisk", "Plus", "Comma", "Minus", + "Period", "Slash"] + \ + [str(_i) for _i in range(10)] + \ + ["Colon", "Semicolon", "Less", "Equal", "Greater", + "Question", "At"] + [chr(_i) for _i in range(65, 91)] + \ + ["BracketLeft", "Backslash", "BracketRight", "Underscore", + "Control", "Alt", "Shift", "Meta"] +VALID_SINGLE_KEYS = [getattr(Qt, 'Key_{0}'.format(k)) for k in SINGLE_KEYS] +VALID_KEYS = [getattr(Qt, 'Key_{0}'.format(k)) for k in KEYSTRINGS+SINGLE_KEYS] + +# Valid finder chars. To be improved +VALID_ACCENT_CHARS = "ÁÉÍOÚáéíúóàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜñÑ" +VALID_FINDER_CHARS = "[A-Za-z\s{0}]".format(VALID_ACCENT_CHARS) + + +class CustomLineEdit(QLineEdit): + """QLineEdit that filters its key press and release events.""" + def __init__(self, parent): + super(CustomLineEdit, self).__init__(parent) + self.setReadOnly(True) + self.setFocusPolicy(Qt.NoFocus) + + def keyPressEvent(self, e): + """Qt Override""" + self.parent().keyPressEvent(e) + + def keyReleaseEvent(self, e): + """Qt Override""" + self.parent().keyReleaseEvent(e) + + +class ShortcutFinder(QLineEdit): + """Textbox for filtering listed shortcuts in the table.""" + def __init__(self, parent, callback=None): + super(ShortcutFinder, self).__init__(parent) + self._parent = parent + + # Widget setup + regex = QRegExp(VALID_FINDER_CHARS + "{100}") + self.setValidator(QRegExpValidator(regex)) + + # Signals + if callback: + self.textChanged.connect(callback) + + def set_text(self, text): + """Set the filter text.""" + text = text.strip() + new_text = self.text() + text + self.setText(new_text) + + def keyPressEvent(self, event): + """Qt Override.""" + key = event.key() + if key in [Qt.Key_Up]: + self._parent.previous_row() + elif key in [Qt.Key_Down]: + self._parent.next_row() + elif key in [Qt.Key_Enter, Qt.Key_Return]: + self._parent.show_editor() + else: + super(ShortcutFinder, self).keyPressEvent(event) + + +# Error codes for the shortcut editor dialog +NO_WARNING, SEQUENCE_LENGTH, SEQUENCE_CONFLICT, INVALID_KEY = [0, 1, 2, 3] + + +class ShortcutEditor(QDialog): + """A dialog for entering key sequences.""" + def __init__(self, parent, context, name, sequence, shortcuts): + super(ShortcutEditor, self).__init__(parent) + self._parent = parent + + self.context = context + self.npressed = 0 + self.keys = set() + self.key_modifiers = set() + self.key_non_modifiers = list() + self.key_text = list() + self.sequence = sequence + self.new_sequence = None + self.edit_state = True + self.shortcuts = shortcuts + + # Widgets + self.label_info = QLabel() + self.label_info.setText(_("Press the new shortcut and select 'Ok': \n" + "(Press 'Tab' once to switch focus between the shortcut entry \n" + "and the buttons below it)")) + self.label_current_sequence = QLabel(_("Current shortcut:")) + self.text_current_sequence = QLabel(sequence) + self.label_new_sequence = QLabel(_("New shortcut:")) + self.text_new_sequence = CustomLineEdit(self) + self.text_new_sequence.setPlaceholderText(sequence) + self.helper_button = HelperToolButton() + self.helper_button.hide() + self.label_warning = QLabel() + self.label_warning.hide() + + bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + self.button_ok = bbox.button(QDialogButtonBox.Ok) + self.button_cancel = bbox.button(QDialogButtonBox.Cancel) + + # Setup widgets + self.setWindowTitle(_('Shortcut: {0}').format(name)) + self.button_ok.setFocusPolicy(Qt.NoFocus) + self.button_ok.setEnabled(False) + self.button_cancel.setFocusPolicy(Qt.NoFocus) + self.helper_button.setToolTip('') + self.helper_button.setFocusPolicy(Qt.NoFocus) + style = """ + QToolButton { + margin:1px; + border: 0px solid grey; + padding:0px; + border-radius: 0px; + }""" + self.helper_button.setStyleSheet(style) + self.text_new_sequence.setFocusPolicy(Qt.NoFocus) + self.label_warning.setFocusPolicy(Qt.NoFocus) + + # Layout + spacing = 5 + layout_sequence = QGridLayout() + layout_sequence.addWidget(self.label_info, 0, 0, 1, 3) + layout_sequence.addItem(QSpacerItem(spacing, spacing), 1, 0, 1, 2) + layout_sequence.addWidget(self.label_current_sequence, 2, 0) + layout_sequence.addWidget(self.text_current_sequence, 2, 2) + layout_sequence.addWidget(self.label_new_sequence, 3, 0) + layout_sequence.addWidget(self.helper_button, 3, 1) + layout_sequence.addWidget(self.text_new_sequence, 3, 2) + layout_sequence.addWidget(self.label_warning, 4, 2, 1, 2) + + layout = QVBoxLayout() + layout.addLayout(layout_sequence) + layout.addSpacing(spacing) + layout.addWidget(bbox) + self.setLayout(layout) + + # Signals + bbox.accepted.connect(self.accept) + bbox.rejected.connect(self.reject) + + def keyPressEvent(self, e): + """Qt override.""" + key = e.key() + # Check if valid keys + if key not in VALID_KEYS: + self.invalid_key_flag = True + return + + self.npressed += 1 + self.key_non_modifiers.append(key) + self.key_modifiers.add(key) + self.key_text.append(e.text()) + self.invalid_key_flag = False + + debug_print('key {0}, npressed: {1}'.format(key, self.npressed)) + + if key == Qt.Key_unknown: + return + + # The user clicked just and only the special keys + # Ctrl, Shift, Alt, Meta. + if (key == Qt.Key_Control or + key == Qt.Key_Shift or + key == Qt.Key_Alt or + key == Qt.Key_Meta): + return + + modifiers = e.modifiers() + if modifiers & Qt.ShiftModifier: + key += Qt.SHIFT + if modifiers & Qt.ControlModifier: + key += Qt.CTRL + if sys.platform == 'darwin': + self.npressed -= 1 + debug_print('decrementing') + if modifiers & Qt.AltModifier: + key += Qt.ALT + if modifiers & Qt.MetaModifier: + key += Qt.META + + self.keys.add(key) + + def toggle_state(self): + """Switch between shortcut entry and Accept/Cancel shortcut mode.""" + self.edit_state = not self.edit_state + + if not self.edit_state: + self.text_new_sequence.setEnabled(False) + if self.button_ok.isEnabled(): + self.button_ok.setFocus() + else: + self.button_cancel.setFocus() + else: + self.text_new_sequence.setEnabled(True) + self.text_new_sequence.setFocus() + + def nonedit_keyrelease(self, e): + """Key release event for non-edit state.""" + key = e.key() + if key in [Qt.Key_Escape]: + self.close() + return + + if key in [Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, + Qt.Key_Down]: + if self.button_ok.hasFocus(): + self.button_cancel.setFocus() + else: + self.button_ok.setFocus() + + def keyReleaseEvent(self, e): + """Qt override.""" + self.npressed -= 1 + if self.npressed <= 0: + key = e.key() + + if len(self.keys) == 1 and key == Qt.Key_Tab: + self.toggle_state() + return + + if len(self.keys) == 1 and key == Qt.Key_Escape: + self.set_sequence('') + self.label_warning.setText(_("Please introduce a different " + "shortcut")) + + if len(self.keys) == 1 and key in [Qt.Key_Return, Qt.Key_Enter]: + self.toggle_state() + return + + if not self.edit_state: + self.nonedit_keyrelease(e) + else: + debug_print('keys: {}'.format(self.keys)) + if self.keys and key != Qt.Key_Escape: + self.validate_sequence() + self.keys = set() + self.key_modifiers = set() + self.key_non_modifiers = list() + self.key_text = list() + self.npressed = 0 + + def check_conflicts(self): + """Check shortcuts for conflicts.""" + conflicts = [] + for index, shortcut in enumerate(self.shortcuts): + sequence = str(shortcut.key) + if sequence == self.new_sequence and \ + (shortcut.context == self.context or shortcut.context == '_' or + self.context == '_'): + conflicts.append(shortcut) + return conflicts + + def update_warning(self, warning_type=NO_WARNING, conflicts=[]): + """Update warning label to reflect conflict status of new shortcut""" + if warning_type == NO_WARNING: + warn = False + tip = 'This shortcut is correct!' + elif warning_type == SEQUENCE_CONFLICT: + template = '{0}{1}' + tip_title = _('The new shorcut conflicts with:') + '
    ' + tip_body = '' + for s in conflicts: + tip_body += ' - {0}: {1}
    '.format(s.context, s.name) + tip_body = tip_body[:-4] # Removing last
    + tip = template.format(tip_title, tip_body) + warn = True + elif warning_type == SEQUENCE_LENGTH: + # Sequences with 5 keysequences (i.e. Ctrl+1, Ctrl+2, Ctrl+3, + # Ctrl+4, Ctrl+5) are invalid + template = '{0}' + tip = _('A compound sequence can have {break} a maximum of ' + '4 subsequences.{break}').format(**{'break': '
    '}) + warn = True + elif warning_type == INVALID_KEY: + template = '{0}' + tip = _('Invalid key entered') + '
    ' + warn = True + + self.helper_button.show() + if warn: + self.label_warning.show() + self.helper_button.setIcon(get_std_icon('MessageBoxWarning')) + self.button_ok.setEnabled(False) + else: + self.helper_button.setIcon(get_std_icon('DialogApplyButton')) + + self.label_warning.setText(tip) + + def set_sequence(self, sequence): + """Set the new shortcut and update buttons.""" + if not sequence or self.sequence == sequence: + self.button_ok.setEnabled(False) + different_sequence = False + else: + self.button_ok.setEnabled(True) + different_sequence = True + + if sys.platform == 'darwin': + if 'Meta+Ctrl' in sequence: + shown_sequence = sequence.replace('Meta+Ctrl', 'Ctrl+Cmd') + elif 'Ctrl+Meta' in sequence: + shown_sequence = sequence.replace('Ctrl+Meta', 'Cmd+Ctrl') + elif 'Ctrl' in sequence: + shown_sequence = sequence.replace('Ctrl', 'Cmd') + elif 'Meta' in sequence: + shown_sequence = sequence.replace('Meta', 'Ctrl') + else: + shown_sequence = sequence + else: + shown_sequence = sequence + self.text_new_sequence.setText(shown_sequence) + self.new_sequence = sequence + + conflicts = self.check_conflicts() + if conflicts and different_sequence: + warning_type = SEQUENCE_CONFLICT + else: + warning_type = NO_WARNING + + self.update_warning(warning_type=warning_type, conflicts=conflicts) + + def validate_sequence(self): + """Provide additional checks for accepting or rejecting shortcuts.""" + if self.invalid_key_flag: + self.update_warning(warning_type=INVALID_KEY) + return + + for mod in MODIFIERS: + non_mod = set(self.key_non_modifiers) + non_mod.discard(mod) + if mod in self.key_non_modifiers: + self.key_non_modifiers.remove(mod) + + self.key_modifiers = self.key_modifiers - non_mod + + while u'' in self.key_text: + self.key_text.remove(u'') + + self.key_text = [k.upper() for k in self.key_text] + + # Fix Backtab, Tab issue + if os.name == 'nt': + if Qt.Key_Backtab in self.key_non_modifiers: + idx = self.key_non_modifiers.index(Qt.Key_Backtab) + self.key_non_modifiers[idx] = Qt.Key_Tab + + if len(self.key_modifiers) == 0: + # Filter single key allowed + if self.key_non_modifiers[0] not in VALID_SINGLE_KEYS: + return + # Filter + elif len(self.key_non_modifiers) > 1: + return + + # QKeySequence accepts a maximum of 4 different sequences + if len(self.keys) > 4: + # Update warning + self.update_warning(warning_type=SEQUENCE_LENGTH) + return + + keys = [] + for i in range(len(self.keys)): + key_seq = 0 + for m in self.key_modifiers: + key_seq += MODIFIERS[m] + key_seq += self.key_non_modifiers[i] + keys.append(key_seq) + + sequence = QKeySequence(*keys) + + self.set_sequence(sequence.toString()) + + +class Shortcut(object): + """Shortcut convenience class for holding shortcut context, name, + original ordering index, key sequence for the shortcut and localized text. + """ + def __init__(self, context, name, key=None): + self.index = 0 # Sorted index. Populated when loading shortcuts + self.context = context + self.name = name + self.key = key + + def __str__(self): + return "{0}/{1}: {2}".format(self.context, self.name, self.key) + + def load(self): + self.key = get_shortcut(self.context, self.name) + + def save(self): + set_shortcut(self.context, self.name, self.key) + + +CONTEXT, NAME, SEQUENCE, SEARCH_SCORE = [0, 1, 2, 3] + + +class ShortcutsModel(QAbstractTableModel): + def __init__(self, parent): + QAbstractTableModel.__init__(self) + self._parent = parent + + self.shortcuts = [] + self.scores = [] + self.rich_text = [] + self.normal_text = [] + self.letters = '' + self.label = QLabel() + self.widths = [] + + # Needed to compensate for the HTMLDelegate color selection unawarness + palette = parent.palette() + self.text_color = palette.text().color().name() + self.text_color_highlight = palette.highlightedText().color().name() + + def current_index(self): + """Get the currently selected index in the parent table view.""" + i = self._parent.proxy_model.mapToSource(self._parent.currentIndex()) + return i + + def sortByName(self): + """Qt Override.""" + self.shortcuts = sorted(self.shortcuts, + key=lambda x: x.context+'/'+x.name) + self.reset() + + def flags(self, index): + """Qt Override.""" + if not index.isValid(): + return Qt.ItemIsEnabled + return Qt.ItemFlags(QAbstractTableModel.flags(self, index)) + + def data(self, index, role=Qt.DisplayRole): + """Qt Override.""" + row = index.row() + if not index.isValid() or not (0 <= row < len(self.shortcuts)): + return to_qvariant() + + shortcut = self.shortcuts[row] + key = shortcut.key + column = index.column() + + if role == Qt.DisplayRole: + if column == CONTEXT: + return to_qvariant(shortcut.context) + elif column == NAME: + color = self.text_color + if self._parent == QApplication.focusWidget(): + if self.current_index().row() == row: + color = self.text_color_highlight + else: + color = self.text_color + text = self.rich_text[row] + text = '

    {1}

    '.format(color, text) + return to_qvariant(text) + elif column == SEQUENCE: + text = QKeySequence(key).toString(QKeySequence.NativeText) + return to_qvariant(text) + elif column == SEARCH_SCORE: + # Treating search scores as a table column simplifies the + # sorting once a score for a specific string in the finder + # has been defined. This column however should always remain + # hidden. + return to_qvariant(self.scores[row]) + elif role == Qt.TextAlignmentRole: + return to_qvariant(int(Qt.AlignHCenter | Qt.AlignVCenter)) + return to_qvariant() + + def headerData(self, section, orientation, role=Qt.DisplayRole): + """Qt Override.""" + if role == Qt.TextAlignmentRole: + if orientation == Qt.Horizontal: + return to_qvariant(int(Qt.AlignHCenter | Qt.AlignVCenter)) + return to_qvariant(int(Qt.AlignRight | Qt.AlignVCenter)) + if role != Qt.DisplayRole: + return to_qvariant() + if orientation == Qt.Horizontal: + if section == CONTEXT: + return to_qvariant(_("Context")) + elif section == NAME: + return to_qvariant(_("Name")) + elif section == SEQUENCE: + return to_qvariant(_("Shortcut")) + elif section == SEARCH_SCORE: + return to_qvariant(_("Score")) + return to_qvariant() + + def rowCount(self, index=QModelIndex()): + """Qt Override.""" + return len(self.shortcuts) + + def columnCount(self, index=QModelIndex()): + """Qt Override.""" + return 4 + + def setData(self, index, value, role=Qt.EditRole): + """Qt Override.""" + if index.isValid() and 0 <= index.row() < len(self.shortcuts): + shortcut = self.shortcuts[index.row()] + column = index.column() + text = from_qvariant(value, str) + if column == SEQUENCE: + shortcut.key = text + self.dataChanged.emit(index, index) + return True + return False + + def update_search_letters(self, text): + """Update search letters with text input in search box.""" + self.letters = text + names = [shortcut.name for shortcut in self.shortcuts] + results = get_search_scores(text, names, template='{0}') + self.normal_text, self.rich_text, self.scores = zip(*results) + self.reset() + + def update_active_row(self): + """Update active row to update color in selected text.""" + self.data(self.current_index()) + + def row(self, row_num): + """Get row based on model index. Needed for the custom proxy model.""" + return self.shortcuts[row_num] + + def reset(self): + """"Reset model to take into account new search letters.""" + self.beginResetModel() + self.endResetModel() + + +class CustomSortFilterProxy(QSortFilterProxyModel): + """Custom column filter based on regex.""" + def __init__(self, parent=None): + super(CustomSortFilterProxy, self).__init__(parent) + self._parent = parent + self.pattern = re.compile(u'') + + def set_filter(self, text): + """Set regular expression for filter.""" + self.pattern = get_search_regex(text) + if self.pattern: + self._parent.setSortingEnabled(False) + else: + self._parent.setSortingEnabled(True) + self.invalidateFilter() + + def filterAcceptsRow(self, row_num, parent): + """Qt override. + + Reimplemented from base class to allow the use of custom filtering. + """ + model = self.sourceModel() + name = model.row(row_num).name + r = re.search(self.pattern, name) + + if r is None: + return False + else: + return True + + +class ShortcutsTable(QTableView): + def __init__(self, parent=None): + QTableView.__init__(self, parent) + self._parent = parent + self.finder = None + + self.source_model = ShortcutsModel(self) + self.proxy_model = CustomSortFilterProxy(self) + self.last_regex = '' + + self.proxy_model.setSourceModel(self.source_model) + self.proxy_model.setDynamicSortFilter(True) + self.proxy_model.setFilterKeyColumn(NAME) + self.proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) + self.setModel(self.proxy_model) + + self.hideColumn(SEARCH_SCORE) + self.setItemDelegateForColumn(NAME, HTMLDelegate(self, margin=9)) + self.setSelectionBehavior(QAbstractItemView.SelectRows) + self.setSelectionMode(QAbstractItemView.SingleSelection) + self.setSortingEnabled(True) + self.setEditTriggers(QAbstractItemView.AllEditTriggers) + self.selectionModel().selectionChanged.connect(self.selection) + + self.verticalHeader().hide() + self.load_shortcuts() + + def focusOutEvent(self, e): + """Qt Override.""" + self.source_model.update_active_row() + super(ShortcutsTable, self).focusOutEvent(e) + + def focusInEvent(self, e): + """Qt Override.""" + super(ShortcutsTable, self).focusInEvent(e) + self.selectRow(self.currentIndex().row()) + + def selection(self, index): + """Update selected row.""" + self.update() + self.isActiveWindow() + + def adjust_cells(self): + """Adjust column size based on contents.""" + self.resizeColumnsToContents() + fm = self.horizontalHeader().fontMetrics() + names = [fm.width(s.name + ' '*9) for s in self.source_model.shortcuts] + self.setColumnWidth(NAME, max(names)) + self.horizontalHeader().setStretchLastSection(True) + + def load_shortcuts(self): + """Load shortcuts and assign to table model.""" + shortcuts = [] + for context, name, keystr in iter_shortcuts(): + shortcut = Shortcut(context, name, keystr) + shortcuts.append(shortcut) + shortcuts = sorted(shortcuts, key=lambda x: x.context+x.name) + # Store the original order of shortcuts + for i, shortcut in enumerate(shortcuts): + shortcut.index = i + self.source_model.shortcuts = shortcuts + self.source_model.scores = [0]*len(shortcuts) + self.source_model.rich_text = [s.name for s in shortcuts] + self.source_model.reset() + self.adjust_cells() + self.sortByColumn(CONTEXT, Qt.AscendingOrder) + + def check_shortcuts(self): + """Check shortcuts for conflicts.""" + conflicts = [] + for index, sh1 in enumerate(self.source_model.shortcuts): + if index == len(self.source_model.shortcuts)-1: + break + for sh2 in self.source_model.shortcuts[index+1:]: + if sh2 is sh1: + continue + if str(sh2.key) == str(sh1.key) \ + and (sh1.context == sh2.context or sh1.context == '_' or + sh2.context == '_'): + conflicts.append((sh1, sh2)) + if conflicts: + self.parent().show_this_page.emit() + cstr = "\n".join(['%s <---> %s' % (sh1, sh2) + for sh1, sh2 in conflicts]) + QMessageBox.warning(self, _("Conflicts"), + _("The following conflicts have been " + "detected:")+"\n"+cstr, QMessageBox.Ok) + + def save_shortcuts(self): + """Save shortcuts from table model.""" + self.check_shortcuts() + for shortcut in self.source_model.shortcuts: + shortcut.save() + + def show_editor(self): + """Create, setup and display the shortcut editor dialog.""" + index = self.proxy_model.mapToSource(self.currentIndex()) + row, column = index.row(), index.column() + shortcuts = self.source_model.shortcuts + context = shortcuts[row].context + name = shortcuts[row].name + + sequence_index = self.source_model.index(row, SEQUENCE) + sequence = sequence_index.data() + + dialog = ShortcutEditor(self, context, name, sequence, shortcuts) + + if dialog.exec_(): + new_sequence = dialog.new_sequence + self.source_model.setData(sequence_index, new_sequence) + + def set_regex(self, regex=None, reset=False): + """Update the regex text for the shortcut finder.""" + if reset: + text = '' + else: + text = self.finder.text().replace(' ', '').lower() + + self.proxy_model.set_filter(text) + self.source_model.update_search_letters(text) + self.sortByColumn(SEARCH_SCORE, Qt.AscendingOrder) + + if self.last_regex != regex: + self.selectRow(0) + self.last_regex = regex + + def next_row(self): + """Move to next row from currently selected row.""" + row = self.currentIndex().row() + rows = self.proxy_model.rowCount() + if row + 1 == rows: + row = -1 + self.selectRow(row + 1) + + def previous_row(self): + """Move to previous row from currently selected row.""" + row = self.currentIndex().row() + rows = self.proxy_model.rowCount() + if row == 0: + row = rows + self.selectRow(row - 1) + + def keyPressEvent(self, event): + """Qt Override.""" + key = event.key() + if key in [Qt.Key_Enter, Qt.Key_Return]: + self.show_editor() + elif key in [Qt.Key_Tab]: + self.finder.setFocus() + elif key in [Qt.Key_Backtab]: + self.parent().reset_btn.setFocus() + elif key in [Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right]: + super(ShortcutsTable, self).keyPressEvent(event) + elif key not in [Qt.Key_Escape, Qt.Key_Space]: + text = event.text() + if text: + if re.search(VALID_FINDER_CHARS, text) is not None: + self.finder.setFocus() + self.finder.set_text(text) + elif key in [Qt.Key_Escape]: + self.finder.keyPressEvent(event) + + def mouseDoubleClickEvent(self, event): + """Qt Override.""" + self.show_editor() + + +class ShortcutsConfigPage(GeneralConfigPage): + CONF_SECTION = "shortcuts" + + NAME = _("Keyboard shortcuts") + ICON = ima.icon('keyboard') + + def setup_page(self): + # Widgets + self.table = ShortcutsTable(self) + self.finder = ShortcutFinder(self.table, self.table.set_regex) + self.table.finder = self.finder + self.label_finder = QLabel(_('Search: ')) + self.reset_btn = QPushButton(_("Reset to default values")) + + # Layout + hlayout = QHBoxLayout() + vlayout = QVBoxLayout() + hlayout.addWidget(self.label_finder) + hlayout.addWidget(self.finder) + vlayout.addWidget(self.table) + vlayout.addLayout(hlayout) + vlayout.addWidget(self.reset_btn) + self.setLayout(vlayout) + + self.setTabOrder(self.table, self.finder) + self.setTabOrder(self.finder, self.reset_btn) + + # Signals and slots + if PYQT5: + # Qt5 'dataChanged' has 3 parameters + self.table.proxy_model.dataChanged.connect( + lambda i1, i2, roles, opt='': self.has_been_modified(opt)) + else: + self.table.proxy_model.dataChanged.connect( + lambda i1, i2, opt='': self.has_been_modified(opt)) + self.reset_btn.clicked.connect(self.reset_to_default) + + def check_settings(self): + self.table.check_shortcuts() + + def reset_to_default(self): + reset_shortcuts() + self.main.apply_shortcuts() + self.table.load_shortcuts() + self.load_from_conf() + self.set_modified(False) + + def apply_settings(self, options): + self.table.save_shortcuts() + self.main.apply_shortcuts() + + +def test(): + from spyder.utils.qthelpers import qapplication + app = qapplication() + table = ShortcutsTable() + table.show() + app.exec_() + print([str(s) for s in table.source_model.shortcuts]) + table.check_shortcuts() + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/variableexplorer.py spyder-3.0.2+dfsg1/spyder/plugins/variableexplorer.py --- spyder-2.3.8+dfsg1/spyder/plugins/variableexplorer.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/variableexplorer.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Variable Explorer Plugin""" + +# Third party imports +from qtpy.QtCore import Signal +from qtpy.QtWidgets import QGroupBox, QStackedWidget, QVBoxLayout, QWidget + +# Local imports +from spyder.config.base import _ +from spyder.config.main import CONF +from spyder.plugins import SpyderPluginMixin +from spyder.plugins.configdialog import PluginConfigPage +from spyder.utils import programs +from spyder.utils import icon_manager as ima +from spyder.widgets.variableexplorer.namespacebrowser import NamespaceBrowser +from spyder.widgets.variableexplorer.utils import REMOTE_SETTINGS + + +class VariableExplorerConfigPage(PluginConfigPage): + def setup_page(self): + ar_group = QGroupBox(_("Autorefresh")) + ar_box = self.create_checkbox(_("Enable autorefresh"), + 'autorefresh') + ar_spin = self.create_spinbox(_("Refresh interval: "), + _(" ms"), 'autorefresh/timeout', + min_=100, max_=1000000, step=100) + + filter_group = QGroupBox(_("Filter")) + filter_data = [ + ('exclude_private', _("Exclude private references")), + ('exclude_capitalized', _("Exclude capitalized references")), + ('exclude_uppercase', _("Exclude all-uppercase references")), + ('exclude_unsupported', _("Exclude unsupported data types")), + ] + filter_boxes = [self.create_checkbox(text, option) + for option, text in filter_data] + + display_group = QGroupBox(_("Display")) + display_data = [] + if programs.is_module_installed('numpy'): + display_data.append(('minmax', _("Show arrays min/max"), '')) + display_data.append( + ('remote_editing', _("Edit data in the remote process"), + _("Editors are opened in the remote process for NumPy " + "arrays, PIL images, lists, tuples and dictionaries.\n" + "This avoids transfering large amount of data between " + "the remote process and Spyder (through the socket).")) + ) + display_boxes = [self.create_checkbox(text, option, tip=tip) + for option, text, tip in display_data] + + ar_layout = QVBoxLayout() + ar_layout.addWidget(ar_box) + ar_layout.addWidget(ar_spin) + ar_group.setLayout(ar_layout) + + filter_layout = QVBoxLayout() + for box in filter_boxes: + filter_layout.addWidget(box) + filter_group.setLayout(filter_layout) + + display_layout = QVBoxLayout() + for box in display_boxes: + display_layout.addWidget(box) + display_group.setLayout(display_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(ar_group) + vlayout.addWidget(filter_group) + vlayout.addWidget(display_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + +class VariableExplorer(QWidget, SpyderPluginMixin): + """ + Variable Explorer Plugin + """ + CONF_SECTION = 'variable_explorer' + CONFIGWIDGET_CLASS = VariableExplorerConfigPage + sig_option_changed = Signal(str, object) + + def __init__(self, parent): + QWidget.__init__(self, parent) + SpyderPluginMixin.__init__(self, parent) + + # Widgets + self.stack = QStackedWidget(self) + self.shellwidgets = {} + + # Layout + layout = QVBoxLayout() + layout.addWidget(self.stack) + self.setLayout(layout) + + # Initialize plugin + self.initialize_plugin() + + @staticmethod + def get_settings(): + """ + Return Variable Explorer settings dictionary + (i.e. namespace browser settings according to Spyder's configuration file) + """ + settings = {} +# CONF.load_from_ini() # necessary only when called from another process + for name in REMOTE_SETTINGS: + settings[name] = CONF.get(VariableExplorer.CONF_SECTION, name) + return settings + + # ----- Stack accesors ---------------------------------------------------- + def set_current_widget(self, nsb): + self.stack.setCurrentWidget(nsb) + + def current_widget(self): + return self.stack.currentWidget() + + def count(self): + return self.stack.count() + + def remove_widget(self, nsb): + self.stack.removeWidget(nsb) + + def add_widget(self, nsb): + self.stack.addWidget(nsb) + + # ----- Public API -------------------------------------------------------- + def add_shellwidget(self, shellwidget): + shellwidget_id = id(shellwidget) + # Add shell only once: this method may be called two times in a row + # by the External console plugin (dev. convenience) + from spyder.widgets.externalshell import systemshell + if isinstance(shellwidget, systemshell.ExternalSystemShell): + return + if shellwidget_id not in self.shellwidgets: + nsb = NamespaceBrowser(self) + nsb.set_shellwidget(shellwidget) + nsb.setup(**VariableExplorer.get_settings()) + nsb.sig_option_changed.connect(self.sig_option_changed.emit) + self.add_widget(nsb) + self.shellwidgets[shellwidget_id] = nsb + self.set_shellwidget_from_id(shellwidget_id) + return nsb + + def remove_shellwidget(self, shellwidget_id): + # If shellwidget_id is not in self.shellwidgets, it simply means + # that shell was not a Python-based console (it was a terminal) + if shellwidget_id in self.shellwidgets: + nsb = self.shellwidgets.pop(shellwidget_id) + self.remove_widget(nsb) + nsb.close() + + def set_shellwidget_from_id(self, shellwidget_id): + if shellwidget_id in self.shellwidgets: + nsb = self.shellwidgets[shellwidget_id] + self.set_current_widget(nsb) + if self.isvisible: + nsb.visibility_changed(True) + + def import_data(self, fname): + """Import data in current namespace""" + if self.count(): + nsb = self.current_widget() + nsb.refresh_table() + nsb.import_data(filenames=fname) + if self.dockwidget and not self.ismaximized: + self.dockwidget.setVisible(True) + self.dockwidget.raise_() + + #------ SpyderPluginMixin API --------------------------------------------- + def visibility_changed(self, enable): + """DockWidget visibility has changed""" + SpyderPluginMixin.visibility_changed(self, enable) + for nsb in list(self.shellwidgets.values()): + nsb.visibility_changed(enable and nsb is self.current_widget()) + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _('Variable explorer') + + def get_plugin_icon(self): + """Return plugin icon""" + return ima.icon('dictedit') + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.current_widget() + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + return True + + def refresh_plugin(self): + """Refresh widget""" + pass + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + return [] + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.main.extconsole.set_variableexplorer(self) + self.main.add_dockwidget(self) + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + for nsb in list(self.shellwidgets.values()): + nsb.setup(**VariableExplorer.get_settings()) + ar_timeout = self.get_option('autorefresh/timeout') + for shellwidget in self.main.extconsole.shellwidgets: + shellwidget.set_autorefresh_timeout(ar_timeout) diff -Nru spyder-2.3.8+dfsg1/spyder/plugins/workingdirectory.py spyder-3.0.2+dfsg1/spyder/plugins/workingdirectory.py --- spyder-2.3.8+dfsg1/spyder/plugins/workingdirectory.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/plugins/workingdirectory.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,346 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Working Directory Plugin""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os +import os.path as osp + +# Third party imports +from qtpy import PYQT5 +from qtpy.compat import getexistingdirectory +from qtpy.QtCore import QSize, Signal, Slot +from qtpy.QtWidgets import (QButtonGroup, QGroupBox, QHBoxLayout, QLabel, + QToolBar, QVBoxLayout) + +# Local imports +from spyder.config.base import _, get_conf_path, get_home_dir +from spyder.plugins import SpyderPluginMixin +from spyder.plugins.configdialog import PluginConfigPage +from spyder.py3compat import to_text_string, getcwd +from spyder.utils import encoding +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import create_action +from spyder.widgets.comboboxes import PathComboBox + + +class WorkingDirectoryConfigPage(PluginConfigPage): + def setup_page(self): + about_label = QLabel(_("The global working directory is " + "the working directory for newly opened consoles " + "(Python/IPython consoles and terminals), for the " + "file explorer, for the find in files " + "plugin and for new files created in the editor.")) + about_label.setWordWrap(True) + + startup_group = QGroupBox(_("Startup")) + startup_bg = QButtonGroup(startup_group) + startup_label = QLabel(_("At startup, the global working " + "directory is:")) + startup_label.setWordWrap(True) + lastdir_radio = self.create_radiobutton( + _("the same as in last session"), + 'startup/use_last_directory', True, + _("At startup, Spyder will restore the " + "global directory from last session"), + button_group=startup_bg) + thisdir_radio = self.create_radiobutton( + _("the following directory:"), + 'startup/use_fixed_directory', False, + _("At startup, the global working " + "directory will be the specified path"), + button_group=startup_bg) + thisdir_bd = self.create_browsedir("", 'startup/fixed_directory', + getcwd()) + thisdir_radio.toggled.connect(thisdir_bd.setEnabled) + lastdir_radio.toggled.connect(thisdir_bd.setDisabled) + thisdir_layout = QHBoxLayout() + thisdir_layout.addWidget(thisdir_radio) + thisdir_layout.addWidget(thisdir_bd) + + editor_o_group = QGroupBox(_("Open file")) + editor_o_label = QLabel(_("Files are opened from:")) + editor_o_label.setWordWrap(True) + editor_o_bg = QButtonGroup(editor_o_group) + editor_o_radio1 = self.create_radiobutton( + _("the current file directory"), + 'editor/open/browse_scriptdir', + button_group=editor_o_bg) + editor_o_radio2 = self.create_radiobutton( + _("the global working directory"), + 'editor/open/browse_workdir', + button_group=editor_o_bg) + + editor_n_group = QGroupBox(_("New file")) + editor_n_label = QLabel(_("Files are created in:")) + editor_n_label.setWordWrap(True) + editor_n_bg = QButtonGroup(editor_n_group) + editor_n_radio1 = self.create_radiobutton( + _("the current file directory"), + 'editor/new/browse_scriptdir', + button_group=editor_n_bg) + editor_n_radio2 = self.create_radiobutton( + _("the global working directory"), + 'editor/new/browse_workdir', + button_group=editor_n_bg) + # Note: default values for the options above are set in plugin's + # constructor (see below) + + other_group = QGroupBox(_("Change to file base directory")) + newcb = self.create_checkbox + open_box = newcb(_("When opening a file"), + 'editor/open/auto_set_to_basedir') + save_box = newcb(_("When saving a file"), + 'editor/save/auto_set_to_basedir') + + startup_layout = QVBoxLayout() + startup_layout.addWidget(startup_label) + startup_layout.addWidget(lastdir_radio) + startup_layout.addLayout(thisdir_layout) + startup_group.setLayout(startup_layout) + + editor_o_layout = QVBoxLayout() + editor_o_layout.addWidget(editor_o_label) + editor_o_layout.addWidget(editor_o_radio1) + editor_o_layout.addWidget(editor_o_radio2) + editor_o_group.setLayout(editor_o_layout) + + editor_n_layout = QVBoxLayout() + editor_n_layout.addWidget(editor_n_label) + editor_n_layout.addWidget(editor_n_radio1) + editor_n_layout.addWidget(editor_n_radio2) + editor_n_group.setLayout(editor_n_layout) + + other_layout = QVBoxLayout() + other_layout.addWidget(open_box) + other_layout.addWidget(save_box) + other_group.setLayout(other_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(about_label) + vlayout.addSpacing(10) + vlayout.addWidget(startup_group) + vlayout.addWidget(editor_o_group) + vlayout.addWidget(editor_n_group) + vlayout.addWidget(other_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + +class WorkingDirectory(QToolBar, SpyderPluginMixin): + """ + Working directory changer widget + """ + CONF_SECTION = 'workingdir' + CONFIGWIDGET_CLASS = WorkingDirectoryConfigPage + LOG_PATH = get_conf_path(CONF_SECTION) + + sig_option_changed = Signal(str, object) + set_previous_enabled = Signal(bool) + set_next_enabled = Signal(bool) + redirect_stdio = Signal(bool) + set_explorer_cwd = Signal(str) + refresh_findinfiles = Signal() + set_current_console_wd = Signal(str) + + def __init__(self, parent, workdir=None, **kwds): + if PYQT5: + super(WorkingDirectory, self).__init__(parent, **kwds) + else: + QToolBar.__init__(self, parent) + SpyderPluginMixin.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + + self.setWindowTitle(self.get_plugin_title()) # Toolbar title + self.setObjectName(self.get_plugin_title()) # Used to save Window state + + # Previous dir action + self.history = [] + self.histindex = None + self.previous_action = create_action(self, "previous", None, + ima.icon('previous'), _('Back'), + triggered=self.previous_directory) + self.addAction(self.previous_action) + + # Next dir action + self.history = [] + self.histindex = None + self.next_action = create_action(self, "next", None, + ima.icon('next'), _('Next'), + triggered=self.next_directory) + self.addAction(self.next_action) + + # Enable/disable previous/next actions + self.set_previous_enabled.connect(self.previous_action.setEnabled) + self.set_next_enabled.connect(self.next_action.setEnabled) + + # Path combo box + adjust = self.get_option('working_dir_adjusttocontents') + self.pathedit = PathComboBox(self, adjust_to_contents=adjust) + self.pathedit.setToolTip(_("This is the working directory for newly\n" + "opened consoles (Python/IPython consoles and\n" + "terminals), for the file explorer, for the\n" + "find in files plugin and for new files\n" + "created in the editor")) + self.pathedit.open_dir.connect(self.chdir) + self.pathedit.activated[str].connect(self.chdir) + self.pathedit.setMaxCount(self.get_option('working_dir_history')) + wdhistory = self.load_wdhistory(workdir) + if workdir is None: + if self.get_option('startup/use_last_directory'): + if wdhistory: + workdir = wdhistory[0] + else: + workdir = "." + else: + workdir = self.get_option('startup/fixed_directory', ".") + if not osp.isdir(workdir): + workdir = "." + self.chdir(workdir) + self.pathedit.addItems(wdhistory) + self.pathedit.selected_text = self.pathedit.currentText() + self.refresh_plugin() + self.addWidget(self.pathedit) + + # Browse action + browse_action = create_action(self, "browse", None, + ima.icon('DirOpenIcon'), + _('Browse a working directory'), + triggered=self.select_directory) + self.addAction(browse_action) + + # Parent dir action + parent_action = create_action(self, "parent", None, + ima.icon('up'), + _('Change to parent directory'), + triggered=self.parent_directory) + self.addAction(parent_action) + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _('Global working directory') + + def get_plugin_icon(self): + """Return widget icon""" + return ima.icon('DirOpenIcon') + + def get_plugin_actions(self): + """Setup actions""" + return (None, None) + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.redirect_stdio.connect(self.main.redirect_internalshell_stdio) + self.main.console.shell.refresh.connect(self.refresh_plugin) + iconsize = 24 + self.setIconSize(QSize(iconsize, iconsize)) + self.main.addToolBar(self) + + def refresh_plugin(self): + """Refresh widget""" + curdir = getcwd() + self.pathedit.add_text(curdir) + self.save_wdhistory() + self.set_previous_enabled.emit( + self.histindex is not None and self.histindex > 0) + self.set_next_enabled.emit(self.histindex is not None and \ + self.histindex < len(self.history)-1) + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + pass + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + return True + + #------ Public API --------------------------------------------------------- + def load_wdhistory(self, workdir=None): + """Load history from a text file in user home directory""" + if osp.isfile(self.LOG_PATH): + wdhistory, _ = encoding.readlines(self.LOG_PATH) + wdhistory = [name for name in wdhistory if os.path.isdir(name)] + else: + if workdir is None: + workdir = get_home_dir() + wdhistory = [ workdir ] + return wdhistory + + def save_wdhistory(self): + """Save history to a text file in user home directory""" + text = [ to_text_string( self.pathedit.itemText(index) ) \ + for index in range(self.pathedit.count()) ] + encoding.writelines(text, self.LOG_PATH) + + @Slot() + def select_directory(self): + """Select directory""" + self.redirect_stdio.emit(False) + directory = getexistingdirectory(self.main, _("Select directory"), + getcwd()) + if directory: + self.chdir(directory) + self.redirect_stdio.emit(True) + + @Slot() + def previous_directory(self): + """Back to previous directory""" + self.histindex -= 1 + self.chdir(directory='', browsing_history=True) + + @Slot() + def next_directory(self): + """Return to next directory""" + self.histindex += 1 + self.chdir(directory='', browsing_history=True) + + @Slot() + def parent_directory(self): + """Change working directory to parent directory""" + self.chdir(os.path.join(getcwd(), os.path.pardir)) + + @Slot(str) + @Slot(str, bool) + @Slot(str, bool, bool) + def chdir(self, directory, browsing_history=False, + refresh_explorer=True): + """Set directory as working directory""" + if directory: + directory = osp.abspath(to_text_string(directory)) + + # Working directory history management + if browsing_history: + directory = self.history[self.histindex] + elif directory in self.history: + self.histindex = self.history.index(directory) + else: + if self.histindex is None: + self.history = [] + else: + self.history = self.history[:self.histindex+1] + self.history.append(directory) + self.histindex = len(self.history)-1 + + # Changing working directory + os.chdir( to_text_string(directory) ) + self.refresh_plugin() + if refresh_explorer: + self.set_explorer_cwd.emit(directory) + self.set_as_current_console_wd() + self.refresh_findinfiles.emit() + + def set_as_current_console_wd(self): + """Set as current console working directory""" + self.set_current_console_wd.emit(getcwd()) diff -Nru spyder-2.3.8+dfsg1/spyder/py3compat.py spyder-3.0.2+dfsg1/spyder/py3compat.py --- spyder-2.3.8+dfsg1/spyder/py3compat.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/py3compat.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,289 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.py3compat +---------------- + +Transitional module providing compatibility functions intended to help +migrating from Python 2 to Python 3. + +This module should be fully compatible with: + * Python >=v2.6 + * Python 3 +""" + +from __future__ import print_function + +import operator +import os +import sys + +PY2 = sys.version[0] == '2' +PY3 = sys.version[0] == '3' + +#============================================================================== +# Data types +#============================================================================== +if PY2: + # Python 2 + TEXT_TYPES = (str, unicode) + INT_TYPES = (int, long) +else: + # Python 3 + TEXT_TYPES = (str,) + INT_TYPES = (int,) +NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex]) + + +#============================================================================== +# Renamed/Reorganized modules +#============================================================================== +if PY2: + # Python 2 + import __builtin__ as builtins + import ConfigParser as configparser + try: + import _winreg as winreg + except ImportError: + pass + from sys import maxint as maxsize + try: + import CStringIO as io + except ImportError: + import StringIO as io + try: + import cPickle as pickle + except ImportError: + import pickle + from UserDict import DictMixin as MutableMapping + import thread as _thread + import repr as reprlib + import Queue +else: + # Python 3 + import builtins + import configparser + try: + import winreg + except ImportError: + pass + from sys import maxsize + import io + import pickle + from collections import MutableMapping + import _thread + import reprlib + import queue as Queue + +#============================================================================== +# Strings +#============================================================================== +if PY2: + # Python 2 + import codecs + def u(obj): + """Make unicode object""" + return codecs.unicode_escape_decode(obj)[0] +else: + # Python 3 + def u(obj): + """Return string as it is""" + return obj + +def is_text_string(obj): + """Return True if `obj` is a text string, False if it is anything else, + like binary data (Python 3) or QString (Python 2, PyQt API #1)""" + if PY2: + # Python 2 + return isinstance(obj, basestring) + else: + # Python 3 + return isinstance(obj, str) + +def is_binary_string(obj): + """Return True if `obj` is a binary string, False if it is anything else""" + if PY2: + # Python 2 + return isinstance(obj, str) + else: + # Python 3 + return isinstance(obj, bytes) + +def is_string(obj): + """Return True if `obj` is a text or binary Python string object, + False if it is anything else, like a QString (Python 2, PyQt API #1)""" + return is_text_string(obj) or is_binary_string(obj) + +def is_unicode(obj): + """Return True if `obj` is unicode""" + if PY2: + # Python 2 + return isinstance(obj, unicode) + else: + # Python 3 + return isinstance(obj, str) + +def to_text_string(obj, encoding=None): + """Convert `obj` to (unicode) text string""" + if PY2: + # Python 2 + if encoding is None: + return unicode(obj) + else: + return unicode(obj, encoding) + else: + # Python 3 + if encoding is None: + return str(obj) + elif isinstance(obj, str): + # In case this function is not used properly, this could happen + return obj + else: + return str(obj, encoding) + +def to_binary_string(obj, encoding=None): + """Convert `obj` to binary string (bytes in Python 3, str in Python 2)""" + if PY2: + # Python 2 + if encoding is None: + return str(obj) + else: + return obj.encode(encoding) + else: + # Python 3 + return bytes(obj, 'utf-8' if encoding is None else encoding) + + +#============================================================================== +# Function attributes +#============================================================================== +def get_func_code(func): + """Return function code object""" + if PY2: + # Python 2 + return func.func_code + else: + # Python 3 + return func.__code__ + +def get_func_name(func): + """Return function name""" + if PY2: + # Python 2 + return func.func_name + else: + # Python 3 + return func.__name__ + +def get_func_defaults(func): + """Return function default argument values""" + if PY2: + # Python 2 + return func.func_defaults + else: + # Python 3 + return func.__defaults__ + + +#============================================================================== +# Special method attributes +#============================================================================== +def get_meth_func(obj): + """Return method function object""" + if PY2: + # Python 2 + return obj.im_func + else: + # Python 3 + return obj.__func__ + +def get_meth_class_inst(obj): + """Return method class instance""" + if PY2: + # Python 2 + return obj.im_self + else: + # Python 3 + return obj.__self__ + +def get_meth_class(obj): + """Return method class""" + if PY2: + # Python 2 + return obj.im_class + else: + # Python 3 + return obj.__self__.__class__ + + +#============================================================================== +# Misc. +#============================================================================== +if PY2: + # Python 2 + input = raw_input + getcwd = os.getcwdu + cmp = cmp + import string + str_lower = string.lower + from itertools import izip_longest as zip_longest +else: + # Python 3 + input = input + getcwd = os.getcwd + def cmp(a, b): + return (a > b) - (a < b) + str_lower = str.lower + from itertools import zip_longest + +def qbytearray_to_str(qba): + """Convert QByteArray object to str in a way compatible with Python 2/3""" + return str(bytes(qba.toHex().data()).decode()) + +# ============================================================================= +# Dict funcs +# ============================================================================= +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + + +if __name__ == '__main__': + pass diff -Nru spyder-2.3.8+dfsg1/spyder/pyplot.py spyder-3.0.2+dfsg1/spyder/pyplot.py --- spyder-2.3.8+dfsg1/spyder/pyplot.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/pyplot.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,9 @@ +# -*- coding:utf-8 -*- +""" +Importing guiqwt's pyplot module or matplotlib's pyplot +""" + +try: + from guiqwt.pyplot import * +except (ImportError, AssertionError): + from matplotlib.pyplot import * diff -Nru spyder-2.3.8+dfsg1/spyder/requirements.py spyder-3.0.2+dfsg1/spyder/requirements.py --- spyder-2.3.8+dfsg1/spyder/requirements.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/requirements.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Module checking Spyder installation requirements""" + +import sys +import os.path as osp +from distutils.version import LooseVersion + + +def show_warning(message): + """Show warning using Tkinter if available""" + try: + # If Tkinter is installed (highly probable), showing an error pop-up + import Tkinter, tkMessageBox + root = Tkinter.Tk() + root.withdraw() + tkMessageBox.showerror("Spyder", message) + except ImportError: + pass + raise RuntimeError(message) + +def check_path(): + """Check sys.path: is Spyder properly installed?""" + dirname = osp.abspath(osp.join(osp.dirname(__file__), osp.pardir)) + if dirname not in sys.path: + show_warning("Spyder must be installed properly " + "(e.g. from source: 'python setup.py install'),\n" + "or directory '%s' must be in PYTHONPATH " + "environment variable." % dirname) + +def check_qt(): + """Check Qt binding requirements""" + qt_infos = dict(pyqt5=("PyQt5", "5.2"), pyqt=("PyQt4", "4.6")) + try: + import qtpy + package_name, required_ver = qt_infos[qtpy.API] + actual_ver = qtpy.PYQT_VERSION + if LooseVersion(actual_ver) < LooseVersion(required_ver): + show_warning("Please check Spyder installation requirements:\n" + "%s %s+ is required (found v%s)." + % (package_name, required_ver, actual_ver)) + except ImportError: + show_warning("Failed to import qtpy.\n" + "Please check Spyder installation requirements:\n\n" + "qtpy 1.1.0+ and either\n" + "%s %s+ or\n" + "%s %s+\n\n" + "are required to run Spyder." + % (qt_infos['pyqt5'] + qt_infos['pyqt'])) diff -Nru spyder-2.3.8+dfsg1/spyder/rope_patch.py spyder-3.0.2+dfsg1/spyder/rope_patch.py --- spyder-2.3.8+dfsg1/spyder/rope_patch.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/rope_patch.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Patching rope: + +[1] For compatibility with Spyder's standalone version, built with py2exe or + cx_Freeze + +[2] For better performance, see this thread: + http://groups.google.com/group/rope-dev/browse_thread/thread/57de5731f202537a + +[3] To avoid considering folders without __init__.py as Python packages, thus + avoiding side effects as non-working introspection features on a Python + module or package when a folder in current directory has the same name. + See this thread: + http://groups.google.com/group/rope-dev/browse_thread/thread/924c4b5a6268e618 + +[4] To avoid rope adding a 2 spaces indent to every docstring it gets, because + it breaks the work of Sphinx on the Help plugin. Also, to better + control how to get calltips and docstrings of forced builtin objects. + +[5] To make matplotlib return its docstrings in proper rst, instead of a mix + of rst and plain text. +""" + +def apply(): + """Monkey patching rope + + See [1], [2], [3], [4] and [5] in module docstring.""" + from spyder.utils.programs import is_module_installed + if is_module_installed('rope', '<0.9.4'): + import rope + raise ImportError("rope %s can't be patched" % rope.VERSION) + + # [1] Patching project.Project for compatibility with py2exe/cx_Freeze + # distributions + from spyder.config.base import is_py2exe_or_cx_Freeze + if is_py2exe_or_cx_Freeze(): + from rope.base import project + class PatchedProject(project.Project): + def _default_config(self): + # py2exe/cx_Freeze distribution + from spyder.config.base import get_module_source_path + fname = get_module_source_path('spyder', + 'default_config.py') + return open(fname, 'rb').read() + project.Project = PatchedProject + + # Patching pycore.PyCore... + from rope.base import pycore + class PatchedPyCore(pycore.PyCore): + # [2] ...so that forced builtin modules (i.e. modules that were + # declared as 'extension_modules' in rope preferences) will be indeed + # recognized as builtins by rope, as expected + # + # This patch is included in rope 0.9.4+ but applying it anyway is ok + def get_module(self, name, folder=None): + """Returns a `PyObject` if the module was found.""" + # check if this is a builtin module + pymod = self._builtin_module(name) + if pymod is not None: + return pymod + module = self.find_module(name, folder) + if module is None: + raise pycore.ModuleNotFoundError( + 'Module %s not found' % name) + return self.resource_to_pyobject(module) + # [3] ...to avoid considering folders without __init__.py as Python + # packages + def _find_module_in_folder(self, folder, modname): + module = folder + packages = modname.split('.') + for pkg in packages[:-1]: + if module.is_folder() and module.has_child(pkg): + module = module.get_child(pkg) + else: + return None + if module.is_folder(): + if module.has_child(packages[-1]) and \ + module.get_child(packages[-1]).is_folder() and \ + module.get_child(packages[-1]).has_child('__init__.py'): + return module.get_child(packages[-1]) + elif module.has_child(packages[-1] + '.py') and \ + not module.get_child(packages[-1] + '.py').is_folder(): + return module.get_child(packages[-1] + '.py') + pycore.PyCore = PatchedPyCore + + # [2] Patching BuiltinName for the go to definition feature to simply work + # with forced builtins + from rope.base import builtins, libutils, pyobjects + import inspect + import os.path as osp + class PatchedBuiltinName(builtins.BuiltinName): + def _pycore(self): + p = self.pyobject + while p.parent is not None: + p = p.parent + if isinstance(p, builtins.BuiltinModule) and p.pycore is not None: + return p.pycore + def get_definition_location(self): + if not inspect.isbuiltin(self.pyobject): + _lines, lineno = inspect.getsourcelines(self.pyobject.builtin) + path = inspect.getfile(self.pyobject.builtin) + if path.endswith('pyc') and osp.isfile(path[:-1]): + path = path[:-1] + pycore = self._pycore() + if pycore and pycore.project: + resource = libutils.path_to_resource(pycore.project, path) + module = pyobjects.PyModule(pycore, None, resource) + return (module, lineno) + return (None, None) + builtins.BuiltinName = PatchedBuiltinName + + # [4] Patching several PyDocExtractor methods: + # 1. get_doc: + # To force rope to return the docstring of any object which has one, even + # if it's not an instance of AbstractFunction, AbstractClass, or + # AbstractModule. + # Also, to use utils.dochelpers.getdoc to get docs from forced builtins. + # + # 2. _get_class_docstring and _get_single_function_docstring: + # To not let rope add a 2 spaces indentation to every docstring, which was + # breaking our rich text mode. The only value that we are modifying is the + # 'indents' keyword of those methods, from 2 to 0. + # + # 3. get_calltip + # To easily get calltips of forced builtins + from rope.contrib import codeassist + from spyder.utils.dochelpers import getdoc + from rope.base import exceptions + class PatchedPyDocExtractor(codeassist.PyDocExtractor): + def get_builtin_doc(self, pyobject): + buitin = pyobject.builtin + return getdoc(buitin) + + def get_doc(self, pyobject): + if hasattr(pyobject, 'builtin'): + doc = self.get_builtin_doc(pyobject) + return doc + elif isinstance(pyobject, builtins.BuiltinModule): + docstring = pyobject.get_doc() + if docstring is not None: + docstring = self._trim_docstring(docstring) + else: + docstring = '' + # TODO: Add a module_name key, so that the name could appear + # on the OI text filed but not be used by sphinx to render + # the page + doc = {'name': '', + 'argspec': '', + 'note': '', + 'docstring': docstring + } + return doc + elif isinstance(pyobject, pyobjects.AbstractFunction): + return self._get_function_docstring(pyobject) + elif isinstance(pyobject, pyobjects.AbstractClass): + return self._get_class_docstring(pyobject) + elif isinstance(pyobject, pyobjects.AbstractModule): + return self._trim_docstring(pyobject.get_doc()) + elif pyobject.get_doc() is not None: # Spyder patch + return self._trim_docstring(pyobject.get_doc()) + return None + + def get_calltip(self, pyobject, ignore_unknown=False, remove_self=False): + if hasattr(pyobject, 'builtin'): + doc = self.get_builtin_doc(pyobject) + return doc['name'] + doc['argspec'] + try: + if isinstance(pyobject, pyobjects.AbstractClass): + pyobject = pyobject['__init__'].get_object() + if not isinstance(pyobject, pyobjects.AbstractFunction): + pyobject = pyobject['__call__'].get_object() + except exceptions.AttributeNotFoundError: + return None + if ignore_unknown and not isinstance(pyobject, pyobjects.PyFunction): + return + if isinstance(pyobject, pyobjects.AbstractFunction): + result = self._get_function_signature(pyobject, add_module=True) + if remove_self and self._is_method(pyobject): + return result.replace('(self)', '()').replace('(self, ', '(') + return result + + def _get_class_docstring(self, pyclass): + contents = self._trim_docstring(pyclass.get_doc(), indents=0) + supers = [super.get_name() for super in pyclass.get_superclasses()] + doc = 'class %s(%s):\n\n' % (pyclass.get_name(), ', '.join(supers)) + contents + + if '__init__' in pyclass: + init = pyclass['__init__'].get_object() + if isinstance(init, pyobjects.AbstractFunction): + doc += '\n\n' + self._get_single_function_docstring(init) + return doc + + def _get_single_function_docstring(self, pyfunction): + docs = pyfunction.get_doc() + docs = self._trim_docstring(docs, indents=0) + return docs + codeassist.PyDocExtractor = PatchedPyDocExtractor + + + # [5] Get the right matplotlib docstrings for Help + try: + import matplotlib as mpl + mpl.rcParams['docstring.hardcopy'] = True + except: + pass diff -Nru spyder-2.3.8+dfsg1/spyder/scientific_startup.py spyder-3.0.2+dfsg1/spyder/scientific_startup.py --- spyder-2.3.8+dfsg1/spyder/scientific_startup.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/scientific_startup.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Scientific Python startup script + +Requires NumPy, SciPy and Matplotlib +""" + +# Need a temporary print function that is Python version agnostic. +import sys + +def exec_print(string="", end_space=False): + if sys.version[0] == '2': + if end_space: + exec("print '" + string + "',") + else: + exec("print '" + string + "'") + else: + if end_space: + exec("print('" + string + "', end=' ')") + else: + exec("print('" + string + "')") + +__has_numpy = True +__has_scipy = True +__has_matplotlib = True + +#============================================================================== +# Pollute the namespace but also provide MATLAB-like experience +#============================================================================== +try: + from pylab import * #analysis:ignore + # Enable Matplotlib's interactive mode: + ion() +except ImportError: + pass + +# Import modules following official guidelines: +try: + import numpy as np +except ImportError: + __has_numpy = False + +try: + import scipy as sp +except ImportError: + __has_scipy = False + +try: + import matplotlib as mpl + import matplotlib.pyplot as plt #analysis:ignore +except ImportError: + __has_matplotlib = False + +#============================================================================== +# Print what modules have been imported +#============================================================================== +__imports = "" +if __has_numpy: + __imports += "Imported NumPy %s" % np.__version__ +if __has_scipy: + __imports += ", SciPy %s" % sp.__version__ +if __has_matplotlib: + __imports += ", Matplotlib %s" % mpl.__version__ + +exec_print("") +if __imports: + exec_print(__imports) + +import os +if os.environ.get('QT_API') != 'pyside': + try: + import guiqwt + import guiqwt.pyplot as plt_ + import guidata + plt_.ion() + exec_print("+ guidata %s, guiqwt %s" % (guidata.__version__, + guiqwt.__version__)) + except (ImportError, AssertionError): + exec_print() + +#============================================================================== +# Add help about the "scientific" command +#============================================================================== +def setscientific(): + """Set 'scientific' in __builtin__""" + infos = "" + + if __has_numpy: + infos += """ +This is a standard Python interpreter with preloaded tools for scientific +computing and visualization. It tries to import the following modules: + +>>> import numpy as np # NumPy (multidimensional arrays, linear algebra, ...)""" + + if __has_scipy: + infos += """ +>>> import scipy as sp # SciPy (signal and image processing library)""" + + if __has_matplotlib: + infos += """ +>>> import matplotlib as mpl # Matplotlib (2D/3D plotting library) +>>> import matplotlib.pyplot as plt # Matplotlib's pyplot: MATLAB-like syntax +>>> from pylab import * # Matplotlib's pylab interface +>>> ion() # Turned on Matplotlib's interactive mode""" + + try: + import guiqwt #analysis:ignore + infos += """ +>>> import guidata # GUI generation for easy dataset editing and display + +>>> import guiqwt # Efficient 2D data-plotting features +>>> import guiqwt.pyplot as plt_ # guiqwt's pyplot: MATLAB-like syntax +>>> plt_.ion() # Turned on guiqwt's interactive mode""" + except ImportError: + pass + + if __has_numpy: + infos += "\n" + + infos += """ +Within Spyder, this interpreter also provides: + * special commands (e.g. %ls, %cd, %pwd, %clear) + - %ls: List files in the current directory + - %cd dir: Change to directory dir + - %pwd: Show current directory + - %clear x: Remove variable x from namespace +""" + try: + # Python 2 + import __builtin__ as builtins + except ImportError: + # Python 3 + import builtins + try: + from site import _Printer + except ImportError: + # Python 3.4 + from _sitebuiltins import _Printer + builtins.scientific = _Printer("scientific", infos) + + +setscientific() +exec_print('Type "scientific" for more details.') + +#============================================================================== +# Delete temp vars +#============================================================================== +del setscientific, __has_numpy, __has_scipy, __has_matplotlib, __imports, exec_print diff -Nru spyder-2.3.8+dfsg1/spyder/utils/bsdsocket.py spyder-3.0.2+dfsg1/spyder/utils/bsdsocket.py --- spyder-2.3.8+dfsg1/spyder/utils/bsdsocket.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/bsdsocket.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""BSD socket interface communication utilities""" + +# Be extra careful here. The interface is used to communicate with subprocesses +# by redirecting output streams through a socket. Any exception in this module +# and failure to read out buffers will most likely lock up Spyder. + +import os +import socket +import struct +import threading +import errno +import traceback + +# Local imports +from spyder.config.base import DEBUG, STDERR +DEBUG_EDITOR = DEBUG >= 3 +from spyder.py3compat import pickle +PICKLE_HIGHEST_PROTOCOL = 2 + + +def temp_fail_retry(error, fun, *args): + """Retry to execute function, ignoring EINTR error (interruptions)""" + while 1: + try: + return fun(*args) + except error as e: + eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR + if e.args[0] == eintr: + continue + raise + + +SZ = struct.calcsize("l") + + +def write_packet(sock, data, already_pickled=False): + """Write *data* to socket *sock*""" + if already_pickled: + sent_data = data + else: + sent_data = pickle.dumps(data, PICKLE_HIGHEST_PROTOCOL) + sent_data = struct.pack("l", len(sent_data)) + sent_data + nsend = len(sent_data) + while nsend > 0: + nsend -= temp_fail_retry(socket.error, sock.send, sent_data) + + +def read_packet(sock, timeout=None): + """ + Read data from socket *sock* + Returns None if something went wrong + """ + sock.settimeout(timeout) + dlen, data = None, None + try: + if os.name == 'nt': + # Windows implementation + datalen = sock.recv(SZ) + dlen, = struct.unpack("l", datalen) + data = b'' + while len(data) < dlen: + data += sock.recv(dlen) + else: + # Linux/MacOSX implementation + # Thanks to eborisch: + # See issue 1106 + datalen = temp_fail_retry(socket.error, sock.recv, + SZ, socket.MSG_WAITALL) + if len(datalen) == SZ: + dlen, = struct.unpack("l", datalen) + data = temp_fail_retry(socket.error, sock.recv, + dlen, socket.MSG_WAITALL) + except socket.timeout: + raise + except socket.error: + data = None + finally: + sock.settimeout(None) + if data is not None: + try: + return pickle.loads(data) + except Exception: + # Catch all exceptions to avoid locking spyder + if DEBUG_EDITOR: + traceback.print_exc(file=STDERR) + return + + +# Using a lock object to avoid communication issues described in Issue 857 +COMMUNICATE_LOCK = threading.Lock() + +# * Old com implementation * +# See solution (1) in Issue 434, comment 13: +def communicate(sock, command, settings=[]): + """Communicate with monitor""" + try: + COMMUNICATE_LOCK.acquire() + write_packet(sock, command) + for option in settings: + write_packet(sock, option) + return read_packet(sock) + finally: + COMMUNICATE_LOCK.release() + +## new com implementation: +## See solution (2) in Issue 434, comment 13: +#def communicate(sock, command, settings=[], timeout=None): +# """Communicate with monitor""" +# write_packet(sock, command) +# for option in settings: +# write_packet(sock, option) +# if timeout == 0.: +# # non blocking socket is not really supported: +# # setting timeout to 0. here is equivalent (in current monitor's +# # implementation) to say 'I don't need to receive anything in return' +# return +# while True: +# output = read_packet(sock, timeout=timeout) +# if output is None: +# return +# output_command, output_data = output +# if command == output_command: +# return output_data +# elif DEBUG: +# logging.debug("###### communicate/warning /Begin ######") +# logging.debug("was expecting '%s', received '%s'" \ +# % (command, output_command)) +# logging.debug("###### communicate/warning /End ######") + + +class PacketNotReceived(object): + pass + +PACKET_NOT_RECEIVED = PacketNotReceived() + + +if __name__ == '__main__': + if not os.name == 'nt': + # socket read/write testing - client and server in one thread + + # (techtonik): the stuff below is placed into public domain + print("-- Testing standard Python socket interface --") + + address = ("127.0.0.1", 9999) + + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setblocking(0) + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.bind( address ) + server.listen(2) + + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.connect( address ) + + client.send("data to be catched".encode('utf-8')) + # accepted server socket is the one we can read from + # note that it is different from server socket + accsock, addr = server.accept() + print('..got "%s" from %s' % (accsock.recv(4096), addr)) + + # accsock.close() + # client.send("more data for recv") + #socket.error: [Errno 9] Bad file descriptor + # accsock, addr = server.accept() + #socket.error: [Errno 11] Resource temporarily unavailable + + + print("-- Testing BSD socket write_packet/read_packet --") + + write_packet(client, "a tiny piece of data") + print('..got "%s" from read_packet()' % (read_packet(accsock))) + + client.close() + server.close() + + print("-- Done.") diff -Nru spyder-2.3.8+dfsg1/spyder/utils/codeanalysis.py spyder-3.0.2+dfsg1/spyder/utils/codeanalysis.py --- spyder-2.3.8+dfsg1/spyder/utils/codeanalysis.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/codeanalysis.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Source code analysis utilities +""" + +import sys +import re +import os +import tempfile +import traceback + +# Local import +from spyder.config.base import _, DEBUG +from spyder.utils import programs, encoding +from spyder.py3compat import to_text_string, to_binary_string, PY3 +from spyder import dependencies +DEBUG_EDITOR = DEBUG >= 3 + +#============================================================================== +# Pyflakes/pep8 code analysis +#============================================================================== +TASKS_PATTERN = r"(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP|@todo)([^#]*)" + +#TODO: this is a test for the following function +def find_tasks(source_code): + """Find tasks in source code (TODO, FIXME, XXX, ...)""" + results = [] + for line, text in enumerate(source_code.splitlines()): + for todo in re.findall(TASKS_PATTERN, text): + results.append((todo[-1].strip().capitalize(), line+1)) + return results + + +def check_with_pyflakes(source_code, filename=None): + """Check source code with pyflakes + Returns an empty list if pyflakes is not installed""" + try: + if filename is None: + filename = '' + try: + source_code += '\n' + except TypeError: + # Python 3 + source_code += to_binary_string('\n') + + import _ast + from pyflakes.checker import Checker + # First, compile into an AST and handle syntax errors. + try: + tree = compile(source_code, filename, "exec", _ast.PyCF_ONLY_AST) + except SyntaxError as value: + # If there's an encoding problem with the file, the text is None. + if value.text is None: + results = [] + else: + results = [(value.args[0], value.lineno)] + except (ValueError, TypeError): + # Example of ValueError: file contains invalid \x escape character + # (see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=674797) + # Example of TypeError: file contains null character + # (see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=674796) + results = [] + else: + # Okay, it's syntactically valid. Now check it. + w = Checker(tree, filename) + w.messages.sort(key=lambda x: x.lineno) + results = [] + coding = encoding.get_coding(source_code) + lines = source_code.splitlines() + for warning in w.messages: + if 'analysis:ignore' not in \ + to_text_string(lines[warning.lineno-1], coding): + results.append((warning.message % warning.message_args, + warning.lineno)) + except Exception: + # Never return None to avoid lock in spyder/widgets/editor.py + # See Issue 1547 + results = [] + if DEBUG_EDITOR: + traceback.print_exc() # Print exception in internal console + return results + +# Required version: +# Why 0.5 (Python2)? Because it's based on _ast (thread-safe) +PYFLAKES_REQVER = '>=0.6.0' if PY3 else '>=0.5.0' +dependencies.add("pyflakes", _("Real-time code analysis on the Editor"), + required_version=PYFLAKES_REQVER) + +PEP8_REQVER = '>=0.6' +dependencies.add("pep8", _("Real-time code style analysis on the Editor"), + required_version=PEP8_REQVER) + + +def is_pyflakes_installed(): + """Return True if pyflakes required version is installed""" + return programs.is_module_installed('pyflakes', PYFLAKES_REQVER) + + +def get_checker_executable(name): + """Return checker executable in the form of a list of arguments + for subprocess.Popen""" + if programs.is_program_installed(name): + # Checker is properly installed + return [name] + else: + path1 = programs.python_script_exists(package=None, + module=name+'_script') + path2 = programs.python_script_exists(package=None, module=name) + if path1 is not None: # checker_script.py is available + # Checker script is available but has not been installed + # (this may work with pyflakes) + return [sys.executable, path1] + elif path2 is not None: # checker.py is available + # Checker package is available but its script has not been + # installed (this works with pep8 but not with pyflakes) + return [sys.executable, path2] + + +def check(args, source_code, filename=None, options=None): + """Check source code with checker defined with *args* (list) + Returns an empty list if checker is not installed""" + if args is None: + return [] + if options is not None: + args += options + if any(['pyflakes' in arg for arg in args]): + # Pyflakes requires an ending new line (pep8 don't! -- see Issue 1123) + # Note: this code is not used right now as it is faster to invoke + # pyflakes in current Python interpreter (see `check_with_pyflakes` + # function above) than calling it through a subprocess + source_code += '\n' + if filename is None: + # Creating a temporary file because file does not exist yet + # or is not up-to-date + tempfd = tempfile.NamedTemporaryFile(suffix=".py", delete=False) + tempfd.write(source_code) + tempfd.close() + args.append(tempfd.name) + else: + args.append(filename) + cmd = args[0] + cmdargs = args[1:] + proc = programs.run_program(cmd, cmdargs) + output = proc.communicate()[0].strip().decode().splitlines() + if filename is None: + os.unlink(tempfd.name) + results = [] + coding = encoding.get_coding(source_code) + lines = source_code.splitlines() + for line in output: + lineno = int(re.search(r'(\:[\d]+\:)', line).group()[1:-1]) + try: + text = to_text_string(lines[lineno-1], coding) + except TypeError: + text = to_text_string(lines[lineno-1]) + if 'analysis:ignore' not in text: + message = line[line.find(': ')+2:] + results.append((message, lineno)) + return results + + +def check_with_pep8(source_code, filename=None): + """Check source code with pep8""" + try: + args = get_checker_executable('pep8') + results = check(args, source_code, filename=filename, options=['-r']) + except Exception: + # Never return None to avoid lock in spyder/widgets/editor.py + # See Issue 1547 + results = [] + if DEBUG_EDITOR: + traceback.print_exc() # Print exception in internal console + return results + + +if __name__ == '__main__': +# fname = __file__ + fname = os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, 'bootstrap.py') + code = open(fname).read() + check_results = check_with_pyflakes(code, fname)+\ + check_with_pep8(code, fname)+find_tasks(code) +# check_results = check_with_pep8(code, fname) + for message, line in check_results: + sys.stdout.write("Message: %s -- Line: %s\n" % (message, line)) diff -Nru spyder-2.3.8+dfsg1/spyder/utils/debug.py spyder-3.0.2+dfsg1/spyder/utils/debug.py --- spyder-2.3.8+dfsg1/spyder/utils/debug.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/debug.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Debug utilities that are independent of Spyder code. + +See spyder.config.base for other helpers. +""" + +from __future__ import print_function + +import inspect +import traceback +import time + + +def log_time(fd): + timestr = "Logging time: %s" % time.ctime(time.time()) + print("="*len(timestr), file=fd) + print(timestr, file=fd) + print("="*len(timestr), file=fd) + print("", file=fd) + +def log_last_error(fname, context=None): + """Log last error in filename *fname* -- *context*: string (optional)""" + fd = open(fname, 'a') + log_time(fd) + if context: + print("Context", file=fd) + print("-------", file=fd) + print("", file=fd) + print(context, file=fd) + print("", file=fd) + print("Traceback", file=fd) + print("---------", file=fd) + print("", file=fd) + traceback.print_exc(file=fd) + print("", file=fd) + print("", file=fd) + +def log_dt(fname, context, t0): + fd = open(fname, 'a') + log_time(fd) + print("%s: %d ms" % (context, 10*round(1e2*(time.time()-t0))), file=fd) + print("", file=fd) + print("", file=fd) + +def caller_name(skip=2): + """ + Get name of a caller in the format module.class.method + + `skip` specifies how many levels of call stack to skip for caller's name. + skip=1 means "who calls me", skip=2 "who calls my caller" etc. + + An empty string is returned if skipped levels exceed stack height + """ + stack = inspect.stack() + start = 0 + skip + if len(stack) < start + 1: + return '' + parentframe = stack[start][0] + + name = [] + module = inspect.getmodule(parentframe) + # `modname` can be None when frame is executed directly in console + # TODO(techtonik): consider using __main__ + if module: + name.append(module.__name__) + # detect classname + if 'self' in parentframe.f_locals: + # I don't know any way to detect call from the object method + # XXX: there seems to be no way to detect static method call - it will + # be just a function call + name.append(parentframe.f_locals['self'].__class__.__name__) + codename = parentframe.f_code.co_name + if codename != '': # top level usually + name.append( codename ) # function or a method + del parentframe + return ".".join(name) + +def get_class_that_defined(method): + for cls in inspect.getmro(method.im_class): + if method.__name__ in cls.__dict__: + return cls.__name__ + +def log_methods_calls(fname, some_class, prefix=None): + """ + Hack `some_class` to log all method calls into `fname` file. + If `prefix` format is not set, each log entry is prefixed with: + --[ asked / called / defined ] -- + asked - name of `some_class` + called - name of class for which a method is called + defined - name of class where method is defined + + Must be used carefully, because it monkeypatches __getattribute__ call. + + Example: log_methods_calls('log.log', ShellBaseWidget) + """ + # test if file is writable + open(fname, 'a').close() + FILENAME = fname + CLASS = some_class + + PREFIX = "--[ %(asked)s / %(called)s / %(defined)s ]--" + if prefix != None: + PREFIX = prefix + MAXWIDTH = {'o_O': 10} # hack with editable closure dict, to align names + + def format_prefix(method, methodobj): + """ + --[ ShellBase / Internal / BaseEdit ]------- get_position + """ + classnames = { + 'asked': CLASS.__name__, + 'called': methodobj.__class__.__name__, + 'defined': get_class_that_defined(method) + } + line = PREFIX % classnames + MAXWIDTH['o_O'] = max(len(line), MAXWIDTH['o_O']) + return line.ljust(MAXWIDTH['o_O'], '-') + + import types + def __getattribute__(self, name): + attr = object.__getattribute__(self, name) + if type(attr) is not types.MethodType: + return attr + else: + def newfunc(*args, **kwargs): + log = open(FILENAME, 'a') + prefix = format_prefix(attr, self) + log.write('%s %s\n' % (prefix, name)) + log.close() + result = attr(*args, **kwargs) + return result + return newfunc + + some_class.__getattribute__ = __getattribute__ + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/dochelpers.py spyder-3.0.2+dfsg1/spyder/utils/dochelpers.py --- spyder-2.3.8+dfsg1/spyder/utils/dochelpers.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/dochelpers.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,337 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Utilities and wrappers around inspect module""" + +from __future__ import print_function + +import inspect +import re + +# Local imports: +from spyder.utils import encoding +from spyder.py3compat import (is_text_string, builtins, get_meth_func, + get_meth_class_inst, get_meth_class, + get_func_defaults, to_text_string) + + +SYMBOLS = r"[^\'\"a-zA-Z0-9_.]" + +def getobj(txt, last=False): + """Return the last valid object name in string""" + txt_end = "" + for startchar, endchar in ["[]", "()"]: + if txt.endswith(endchar): + pos = txt.rfind(startchar) + if pos: + txt_end = txt[pos:] + txt = txt[:pos] + tokens = re.split(SYMBOLS, txt) + token = None + try: + while token is None or re.match(SYMBOLS, token): + token = tokens.pop() + if token.endswith('.'): + token = token[:-1] + if token.startswith('.'): + # Invalid object name + return None + if last: + #XXX: remove this statement as well as the "last" argument + token += txt[ txt.rfind(token) + len(token) ] + token += txt_end + if token: + return token + except IndexError: + return None + + +def getobjdir(obj): + """ + For standard objects, will simply return dir(obj) + In special cases (e.g. WrapITK package), will return only string elements + of result returned by dir(obj) + """ + return [item for item in dir(obj) if is_text_string(item)] + + +def getdoc(obj): + """ + Return text documentation from an object. This comes in a form of + dictionary with four keys: + + name: + The name of the inspected object + argspec: + It's argspec + note: + A phrase describing the type of object (function or method) we are + inspecting, and the module it belongs to. + docstring: + It's docstring + """ + + docstring = inspect.getdoc(obj) or inspect.getcomments(obj) or '' + + # Most of the time doc will only contain ascii characters, but there are + # some docstrings that contain non-ascii characters. Not all source files + # declare their encoding in the first line, so querying for that might not + # yield anything, either. So assume the most commonly used + # multi-byte file encoding (which also covers ascii). + try: + docstring = to_text_string(docstring) + except: + pass + + # Doc dict keys + doc = {'name': '', + 'argspec': '', + 'note': '', + 'docstring': docstring} + + if callable(obj): + try: + name = obj.__name__ + except AttributeError: + doc['docstring'] = docstring + return doc + if inspect.ismethod(obj): + imclass = get_meth_class(obj) + if get_meth_class_inst(obj) is not None: + doc['note'] = 'Method of %s instance' \ + % get_meth_class_inst(obj).__class__.__name__ + else: + doc['note'] = 'Unbound %s method' % imclass.__name__ + obj = get_meth_func(obj) + elif hasattr(obj, '__module__'): + doc['note'] = 'Function of %s module' % obj.__module__ + else: + doc['note'] = 'Function' + doc['name'] = obj.__name__ + if inspect.isfunction(obj): + args, varargs, varkw, defaults = inspect.getargspec(obj) + doc['argspec'] = inspect.formatargspec(args, varargs, varkw, + defaults, + formatvalue=lambda o:'='+repr(o)) + if name == '': + doc['name'] = name + ' lambda ' + doc['argspec'] = doc['argspec'][1:-1] # remove parentheses + else: + argspec = getargspecfromtext(doc['docstring']) + if argspec: + doc['argspec'] = argspec + # Many scipy and numpy docstrings begin with a function + # signature on the first line. This ends up begin redundant + # when we are using title and argspec to create the + # rich text "Definition:" field. We'll carefully remove this + # redundancy but only under a strict set of conditions: + # Remove the starting charaters of the 'doc' portion *iff* + # the non-whitespace characters on the first line + # match *exactly* the combined function title + # and argspec we determined above. + signature = doc['name'] + doc['argspec'] + docstring_blocks = doc['docstring'].split("\n\n") + first_block = docstring_blocks[0].strip() + if first_block == signature: + doc['docstring'] = doc['docstring'].replace( + signature, '', 1).lstrip() + else: + doc['argspec'] = '(...)' + + # Remove self from argspec + argspec = doc['argspec'] + doc['argspec'] = argspec.replace('(self)', '()').replace('(self, ', '(') + + return doc + + +def getsource(obj): + """Wrapper around inspect.getsource""" + try: + try: + src = encoding.to_unicode( inspect.getsource(obj) ) + except TypeError: + if hasattr(obj, '__class__'): + src = encoding.to_unicode( inspect.getsource(obj.__class__) ) + else: + # Bindings like VTK or ITK require this case + src = getdoc(obj) + return src + except (TypeError, IOError): + return + + +def getsignaturefromtext(text, objname): + """Get object signatures from text (object documentation) + Return a list containing a single string in most cases + Example of multiple signatures: PyQt4 objects""" + if isinstance(text, dict): + text = text.get('docstring', '') + # Regexps + oneline_re = objname + r'\([^\)].+?(?<=[\w\]\}\'"])\)(?!,)' + multiline_re = objname + r'\([^\)]+(?<=[\w\]\}\'"])\)(?!,)' + multiline_end_parenleft_re = r'(%s\([^\)]+(\),\n.+)+(?<=[\w\]\}\'"])\))' + # Grabbing signatures + if not text: + text = '' + sigs_1 = re.findall(oneline_re + '|' + multiline_re, text) + sigs_2 = [g[0] for g in re.findall(multiline_end_parenleft_re % objname, text)] + all_sigs = sigs_1 + sigs_2 + # The most relevant signature is usually the first one. There could be + # others in doctests but those are not so important + if all_sigs: + return all_sigs[0] + else: + return '' + +# Fix for Issue 1953 +# TODO: Add more signatures and remove this hack in 2.4 +getsignaturesfromtext = getsignaturefromtext + + +def getargspecfromtext(text): + """ + Try to get the formatted argspec of a callable from the first block of its + docstring + + This will return something like + '(foo, bar, k=1)' + """ + blocks = text.split("\n\n") + first_block = blocks[0].strip() + return getsignaturefromtext(first_block, '') + + +def getargsfromtext(text, objname): + """Get arguments from text (object documentation)""" + signature = getsignaturefromtext(text, objname) + if signature: + argtxt = signature[signature.find('(')+1:-1] + return argtxt.split(',') + + +def getargsfromdoc(obj): + """Get arguments from object doc""" + if obj.__doc__ is not None: + return getargsfromtext(obj.__doc__, obj.__name__) + + +def getargs(obj): + """Get the names and default values of a function's arguments""" + if inspect.isfunction(obj) or inspect.isbuiltin(obj): + func_obj = obj + elif inspect.ismethod(obj): + func_obj = get_meth_func(obj) + elif inspect.isclass(obj) and hasattr(obj, '__init__'): + func_obj = getattr(obj, '__init__') + else: + return [] + if not hasattr(func_obj, 'func_code'): + # Builtin: try to extract info from doc + args = getargsfromdoc(func_obj) + if args is not None: + return args + else: + # Example: PyQt4 + return getargsfromdoc(obj) + args, _, _ = inspect.getargs(func_obj.func_code) + if not args: + return getargsfromdoc(obj) + + # Supporting tuple arguments in def statement: + for i_arg, arg in enumerate(args): + if isinstance(arg, list): + args[i_arg] = "(%s)" % ", ".join(arg) + + defaults = get_func_defaults(func_obj) + if defaults is not None: + for index, default in enumerate(defaults): + args[index+len(args)-len(defaults)] += '='+repr(default) + if inspect.isclass(obj) or inspect.ismethod(obj): + if len(args) == 1: + return None + if 'self' in args: + args.remove('self') + return args + + +def getargtxt(obj, one_arg_per_line=True): + """ + Get the names and default values of a function's arguments + Return list with separators (', ') formatted for calltips + """ + args = getargs(obj) + if args: + sep = ', ' + textlist = None + for i_arg, arg in enumerate(args): + if textlist is None: + textlist = [''] + textlist[-1] += arg + if i_arg < len(args)-1: + textlist[-1] += sep + if len(textlist[-1]) >= 32 or one_arg_per_line: + textlist.append('') + if inspect.isclass(obj) or inspect.ismethod(obj): + if len(textlist) == 1: + return None + if 'self'+sep in textlist: + textlist.remove('self'+sep) + return textlist + + +def isdefined(obj, force_import=False, namespace=None): + """Return True if object is defined in namespace + If namespace is None --> namespace = locals()""" + if namespace is None: + namespace = locals() + attr_list = obj.split('.') + base = attr_list.pop(0) + if len(base) == 0: + return False + if base not in builtins.__dict__ and base not in namespace: + if force_import: + try: + module = __import__(base, globals(), namespace) + if base not in globals(): + globals()[base] = module + namespace[base] = module + except (ImportError, SyntaxError): + return False + else: + return False + for attr in attr_list: + try: + attr_not_found = not hasattr(eval(base, namespace), attr) + except SyntaxError: + return False + if attr_not_found: + if force_import: + try: + __import__(base+'.'+attr, globals(), namespace) + except (ImportError, SyntaxError): + return False + else: + return False + base += '.'+attr + return True + + +if __name__ == "__main__": + class Test(object): + def method(self, x, y=2): + pass + print(getargtxt(Test.__init__)) + print(getargtxt(Test.method)) + print(isdefined('numpy.take', force_import=True)) + print(isdefined('__import__')) + print(isdefined('.keys', force_import=True)) + print(getobj('globals')) + print(getobj('globals().keys')) + print(getobj('+scipy.signal.')) + print(getobj('4.')) + print(getdoc(sorted)) + print(getargtxt(sorted)) diff -Nru spyder-2.3.8+dfsg1/spyder/utils/encoding.py spyder-3.0.2+dfsg1/spyder/utils/encoding.py --- spyder-2.3.8+dfsg1/spyder/utils/encoding.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/encoding.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Text encoding utilities, text file I/O + +Functions 'get_coding', 'decode', 'encode' and 'to_unicode' come from Eric4 +source code (Utilities/__init___.py) Copyright © 2003-2009 Detlev Offenbach +""" + +import re +import os +import locale +import sys +from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF32, getincrementaldecoder + +# Local imports +from spyder.py3compat import (is_string, to_text_string, is_binary_string, + is_unicode) + + +PREFERRED_ENCODING = locale.getpreferredencoding() + +def transcode(text, input=PREFERRED_ENCODING, output=PREFERRED_ENCODING): + """Transcode a text string""" + try: + return text.decode("cp437").encode("cp1252") + except UnicodeError: + try: + return text.decode("cp437").encode(output) + except UnicodeError: + return text + +#------------------------------------------------------------------------------ +# Functions for encoding and decoding bytes that come from +# the *file system*. +#------------------------------------------------------------------------------ + +# The default encoding for file paths and environment variables should be set +# to match the default encoding that the OS is using. +def getfilesystemencoding(): + """ + Query the filesystem for the encoding used to encode filenames + and environment variables. + """ + encoding = sys.getfilesystemencoding() + if encoding is None: + # Must be Linux or Unix and nl_langinfo(CODESET) failed. + encoding = PREFERRED_ENCODING + return encoding + +FS_ENCODING = getfilesystemencoding() + +def to_unicode_from_fs(string): + """ + Return a unicode version of string decoded using the file system encoding. + """ + if not is_string(string): # string is a QString + string = to_text_string(string.toUtf8(), 'utf-8') + else: + if is_binary_string(string): + try: + unic = string.decode(FS_ENCODING) + except (UnicodeError, TypeError): + pass + else: + return unic + return string + +def to_fs_from_unicode(unic): + """ + Return a byte string version of unic encoded using the file + system encoding. + """ + if is_unicode(unic): + try: + string = unic.encode(FS_ENCODING) + except (UnicodeError, TypeError): + pass + else: + return string + return unic + +#------------------------------------------------------------------------------ +# Functions for encoding and decoding *text data* itself, usually originating +# from or destined for the *contents* of a file. +#------------------------------------------------------------------------------ + +# Codecs for working with files and text. +CODING_RE = re.compile(r"coding[:=]\s*([-\w_.]+)") +CODECS = ['utf-8', 'iso8859-1', 'iso8859-15', 'ascii', 'koi8-r', + 'koi8-u', 'iso8859-2', 'iso8859-3', 'iso8859-4', 'iso8859-5', + 'iso8859-6', 'iso8859-7', 'iso8859-8', 'iso8859-9', + 'iso8859-10', 'iso8859-13', 'iso8859-14', 'latin-1', + 'utf-16'] + +def get_coding(text): + """ + Function to get the coding of a text. + @param text text to inspect (string) + @return coding string + """ + for line in text.splitlines()[:2]: + result = CODING_RE.search(to_text_string(line)) + if result: + codec = result.group(1) + # sometimes we find a false encoding that can result in errors + if codec in CODECS: + return codec + return None + +def decode(text): + """ + Function to decode a text. + @param text text to decode (string) + @return decoded text and encoding + """ + try: + if text.startswith(BOM_UTF8): + # UTF-8 with BOM + return to_text_string(text[len(BOM_UTF8):], 'utf-8'), 'utf-8-bom' + elif text.startswith(BOM_UTF16): + # UTF-16 with BOM + return to_text_string(text[len(BOM_UTF16):], 'utf-16'), 'utf-16' + elif text.startswith(BOM_UTF32): + # UTF-32 with BOM + return to_text_string(text[len(BOM_UTF32):], 'utf-32'), 'utf-32' + coding = get_coding(text) + if coding: + return to_text_string(text, coding), coding + except (UnicodeError, LookupError): + pass + # Assume UTF-8 + try: + return to_text_string(text, 'utf-8'), 'utf-8-guessed' + except (UnicodeError, LookupError): + pass + # Assume Latin-1 (behaviour before 3.7.1) + return to_text_string(text, "latin-1"), 'latin-1-guessed' + +def encode(text, orig_coding): + """ + Function to encode a text. + @param text text to encode (string) + @param orig_coding type of the original coding (string) + @return encoded text and encoding + """ + if orig_coding == 'utf-8-bom': + return BOM_UTF8 + text.encode("utf-8"), 'utf-8-bom' + + # Try declared coding spec + coding = get_coding(text) + if coding: + try: + return text.encode(coding), coding + except (UnicodeError, LookupError): + raise RuntimeError("Incorrect encoding (%s)" % coding) + if orig_coding and orig_coding.endswith('-default') or \ + orig_coding.endswith('-guessed'): + coding = orig_coding.replace("-default", "") + coding = orig_coding.replace("-guessed", "") + try: + return text.encode(coding), coding + except (UnicodeError, LookupError): + pass + + # Try saving as ASCII + try: + return text.encode('ascii'), 'ascii' + except UnicodeError: + pass + + # Save as UTF-8 without BOM + return text.encode('utf-8'), 'utf-8' + +def to_unicode(string): + """Convert a string to unicode""" + if not is_unicode(string): + for codec in CODECS: + try: + unic = to_text_string(string, codec) + except UnicodeError: + pass + except TypeError: + break + else: + return unic + return string + + +def write(text, filename, encoding='utf-8', mode='wb'): + """ + Write 'text' to file ('filename') assuming 'encoding' + Return (eventually new) encoding + """ + text, encoding = encode(text, encoding) + with open(filename, mode) as textfile: + textfile.write(text) + return encoding + +def writelines(lines, filename, encoding='utf-8', mode='wb'): + """ + Write 'lines' to file ('filename') assuming 'encoding' + Return (eventually new) encoding + """ + return write(os.linesep.join(lines), filename, encoding, mode) + +def read(filename, encoding='utf-8'): + """ + Read text from file ('filename') + Return text and encoding + """ + text, encoding = decode( open(filename, 'rb').read() ) + return text, encoding + +def readlines(filename, encoding='utf-8'): + """ + Read lines from file ('filename') + Return lines and encoding + """ + text, encoding = read(filename, encoding) + return text.split(os.linesep), encoding + + +def is_text_file(filename): + """ + Test if the given path is a text-like file. + + Adapted from: http://stackoverflow.com/a/3002505 + + Original Authors: Trent Mick + Jorge Orpinel + """ + try: + open(filename) + except Exception: + return False + with open(filename, 'rb') as fid: + try: + CHUNKSIZE = 1024 + chunk = fid.read(CHUNKSIZE) + # check for a UTF BOM + for bom in [BOM_UTF8, BOM_UTF16, BOM_UTF32]: + if chunk.startswith(bom): + return True + + decoder = getincrementaldecoder('utf-8')() + while 1: + is_final = len(chunk) < CHUNKSIZE + chunk = decoder.decode(chunk, final=is_final) + if '\0' in chunk: # found null byte + return False + if is_final: + break # done + chunk = fid.read(CHUNKSIZE) + except UnicodeDecodeError: + return False + except Exception: + pass + return True diff -Nru spyder-2.3.8+dfsg1/spyder/utils/environ.py spyder-3.0.2+dfsg1/spyder/utils/environ.py --- spyder-2.3.8+dfsg1/spyder/utils/environ.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/environ.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Environment variable utilities +""" + +# Standard library imports +import os + +# Third party imports +from qtpy.QtWidgets import QDialog, QMessageBox + +# Local imports +from spyder.config.base import _ +from spyder.utils import icon_manager as ima +from spyder.widgets.variableexplorer.collectionseditor import CollectionsEditor + + +def envdict2listdict(envdict): + """Dict --> Dict of lists""" + sep = os.path.pathsep + for key in envdict: + if sep in envdict[key]: + envdict[key] = [path.strip() for path in envdict[key].split(sep)] + return envdict + + +def listdict2envdict(listdict): + """Dict of lists --> Dict""" + for key in listdict: + if isinstance(listdict[key], list): + listdict[key] = os.path.pathsep.join(listdict[key]) + return listdict + + +class RemoteEnvDialog(CollectionsEditor): + """Remote process environment variables Dialog""" + def __init__(self, get_environ_func, set_environ_func, parent=None): + super(RemoteEnvDialog, self).__init__(parent) + self.setup(envdict2listdict(get_environ_func()), + title="os.environ", width=600, icon=ima.icon('environ')) + self.set_environ = set_environ_func + def accept(self): + """Reimplement Qt method""" + self.set_environ(listdict2envdict(self.get_value())) + QDialog.accept(self) + + +class EnvDialog(RemoteEnvDialog): + """Environment variables Dialog""" + def __init__(self): + def get_environ_func(): + return dict(os.environ) + def set_environ_func(env): + os.environ = env + RemoteEnvDialog.__init__(self, get_environ_func, set_environ_func) + + +# For Windows only +try: + from spyder.py3compat import winreg + + def get_user_env(): + """Return HKCU (current user) environment variables""" + reg = dict() + key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment") + for index in range(0, winreg.QueryInfoKey(key)[1]): + try: + value = winreg.EnumValue(key, index) + reg[value[0]] = value[1] + except: + break + return envdict2listdict(reg) + + def set_user_env(reg, parent=None): + """Set HKCU (current user) environment variables""" + reg = listdict2envdict(reg) + types = dict() + key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment") + for name in reg: + try: + _x, types[name] = winreg.QueryValueEx(key, name) + except WindowsError: + types[name] = winreg.REG_EXPAND_SZ + key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0, + winreg.KEY_SET_VALUE) + for name in reg: + winreg.SetValueEx(key, name, 0, types[name], reg[name]) + try: + from win32gui import SendMessageTimeout + from win32con import (HWND_BROADCAST, WM_SETTINGCHANGE, + SMTO_ABORTIFHUNG) + SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, + "Environment", SMTO_ABORTIFHUNG, 5000) + except ImportError: + QMessageBox.warning(parent, _("Warning"), + _("Module pywin32 was not found.
    " + "Please restart this Windows session " + "(not the computer) for changes to take effect.")) + + class WinUserEnvDialog(CollectionsEditor): + """Windows User Environment Variables Editor""" + def __init__(self, parent=None): + super(WinUserEnvDialog, self).__init__(parent) + self.setup(get_user_env(), + title="HKEY_CURRENT_USER\Environment", width=600) + if parent is None: + parent = self + QMessageBox.warning(parent, _("Warning"), + _("If you accept changes, " + "this will modify the current user environment " + "variables directly in Windows registry. " + "Use it with precautions, at your own risks.
    " + "
    Note that for changes to take effect, you will " + "need to restart the parent process of this applica" + "tion (simply restart Spyder if you have executed it " + "from a Windows shortcut, otherwise restart any " + "application from which you may have executed it, " + "like Python(x,y) Home for example)")) + + def accept(self): + """Reimplement Qt method""" + set_user_env( listdict2envdict(self.get_value()), parent=self ) + QDialog.accept(self) + +except ImportError: + pass + +def main(): + """Run Windows environment variable editor""" + from spyder.utils.qthelpers import qapplication + app = qapplication() + if os.name == 'nt': + dialog = WinUserEnvDialog() + else: + dialog = EnvDialog() + dialog.show() + app.exec_() + +if __name__ == "__main__": + main() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/external/__init__.py spyder-3.0.2+dfsg1/spyder/utils/external/__init__.py --- spyder-2.3.8+dfsg1/spyder/utils/external/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/external/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.utils.external +===================== + +External libraries needed for Spyder to work. +Put here only untouched libraries, else put them in utils. +""" + +import os + +# Hack to be able to use our own versions of rope and pyflakes, +# included in our Windows installers +if os.name == 'nt': + import os.path as osp + import sys + from spyder.config.base import get_module_source_path + + dirname = get_module_source_path(__name__) + if osp.isdir(osp.join(dirname, 'rope')): + sys.path.insert(0, dirname) diff -Nru spyder-2.3.8+dfsg1/spyder/utils/external/lockfile.py spyder-3.0.2+dfsg1/spyder/utils/external/lockfile.py --- spyder-2.3.8+dfsg1/spyder/utils/external/lockfile.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/external/lockfile.py 2016-11-07 19:00:18.000000000 +0000 @@ -0,0 +1,231 @@ +# Copyright (c) 2005 Divmod, Inc. +# Copyright (c) Twisted Matrix Laboratories. +# Copyright (c) Spyder Project Contributors +# Twisted is distributed under the MIT license. + +""" +Filesystem-based interprocess mutex. + +Changes by the Spyder Team to the original Twisted file: +-. Rewrite kill Windows function to make it more reliable +""" + +__metaclass__ = type + +import errno, os + +from time import time as _uniquefloat + +from spyder.py3compat import PY2, to_binary_string + +def unique(): + if PY2: + return str(long(_uniquefloat() * 1000)) + else: + return str(int(_uniquefloat() * 1000)) + +from os import rename +if not os.name == 'nt': + from os import kill + from os import symlink + from os import readlink + from os import remove as rmlink + _windows = False +else: + _windows = True + + import ctypes + from ctypes import wintypes + + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx + PROCESS_QUERY_INFORMATION = 0x400 + + # GetExitCodeProcess uses a special exit code to indicate that the + # process is still running. + STILL_ACTIVE = 259 + + def _is_pid_running(pid): + """Taken from http://www.madebuild.org/blog/?p=30""" + kernel32 = ctypes.windll.kernel32 + handle = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid) + if handle == 0: + return False + + # If the process exited recently, a pid may still exist for the + # handle. So, check if we can get the exit code. + exit_code = wintypes.DWORD() + retval = kernel32.GetExitCodeProcess(handle, + ctypes.byref(exit_code)) + is_running = (retval == 0) + kernel32.CloseHandle(handle) + + # See if we couldn't get the exit code or the exit code indicates + # that the process is still running. + return is_running or exit_code.value == STILL_ACTIVE + + def kill(pid, signal): # analysis:ignore + if not _is_pid_running(pid): + raise OSError(errno.ESRCH, None) + else: + return + + _open = open + + # XXX Implement an atomic thingamajig for win32 + def symlink(value, filename): #analysis:ignore + newlinkname = filename+"."+unique()+'.newlink' + newvalname = os.path.join(newlinkname, "symlink") + os.mkdir(newlinkname) + f = _open(newvalname, 'wb') + f.write(to_binary_string(value)) + f.flush() + f.close() + try: + rename(newlinkname, filename) + except: + os.remove(newvalname) + os.rmdir(newlinkname) + raise + + def readlink(filename): #analysis:ignore + try: + fObj = _open(os.path.join(filename, 'symlink'), 'rb') + except IOError as e: + if e.errno == errno.ENOENT or e.errno == errno.EIO: + raise OSError(e.errno, None) + raise + else: + result = fObj.read().decode() + fObj.close() + return result + + def rmlink(filename): #analysis:ignore + os.remove(os.path.join(filename, 'symlink')) + os.rmdir(filename) + + + +class FilesystemLock: + """ + A mutex. + + This relies on the filesystem property that creating + a symlink is an atomic operation and that it will + fail if the symlink already exists. Deleting the + symlink will release the lock. + + @ivar name: The name of the file associated with this lock. + + @ivar clean: Indicates whether this lock was released cleanly by its + last owner. Only meaningful after C{lock} has been called and + returns True. + + @ivar locked: Indicates whether the lock is currently held by this + object. + """ + + clean = None + locked = False + + def __init__(self, name): + self.name = name + + def lock(self): + """ + Acquire this lock. + + @rtype: C{bool} + @return: True if the lock is acquired, false otherwise. + + @raise: Any exception os.symlink() may raise, other than + EEXIST. + """ + clean = True + while True: + try: + symlink(str(os.getpid()), self.name) + except OSError as e: + if _windows and e.errno in (errno.EACCES, errno.EIO): + # The lock is in the middle of being deleted because we're + # on Windows where lock removal isn't atomic. Give up, we + # don't know how long this is going to take. + return False + if e.errno == errno.EEXIST: + try: + pid = readlink(self.name) + except OSError as e: + if e.errno == errno.ENOENT: + # The lock has vanished, try to claim it in the + # next iteration through the loop. + continue + raise + except IOError as e: + if _windows and e.errno == errno.EACCES: + # The lock is in the middle of being + # deleted because we're on Windows where + # lock removal isn't atomic. Give up, we + # don't know how long this is going to + # take. + return False + raise + try: + if kill is not None: + kill(int(pid), 0) + except OSError as e: + if e.errno == errno.ESRCH: + # The owner has vanished, try to claim it in the next + # iteration through the loop. + try: + rmlink(self.name) + except OSError as e: + if e.errno == errno.ENOENT: + # Another process cleaned up the lock. + # Race them to acquire it in the next + # iteration through the loop. + continue + raise + clean = False + continue + raise + return False + raise + self.locked = True + self.clean = clean + return True + + def unlock(self): + """ + Release this lock. + + This deletes the directory with the given name. + + @raise: Any exception os.readlink() may raise, or + ValueError if the lock is not owned by this process. + """ + pid = readlink(self.name) + if int(pid) != os.getpid(): + raise ValueError("Lock %r not owned by this process" % (self.name,)) + rmlink(self.name) + self.locked = False + + +def isLocked(name): + """Determine if the lock of the given name is held or not. + + @type name: C{str} + @param name: The filesystem path to the lock to test + + @rtype: C{bool} + @return: True if the lock is held, False otherwise. + """ + l = FilesystemLock(name) + result = None + try: + result = l.lock() + finally: + if result: + l.unlock() + return not result + + +__all__ = ['FilesystemLock', 'isLocked'] diff -Nru spyder-2.3.8+dfsg1/spyder/utils/fixtures.py spyder-3.0.2+dfsg1/spyder/utils/fixtures.py --- spyder-2.3.8+dfsg1/spyder/utils/fixtures.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/fixtures.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# + +""" +Testing utilities to be used with pytest. +""" + +# Standard library imports +import shutil +import tempfile + +# Third party imports +import pytest + +# Local imports +from spyder.config.user import UserConfig +from spyder.config.main import CONF_VERSION, DEFAULTS + + +@pytest.fixture +def tmpconfig(request): + """ + Fixtures that returns a temporary CONF element. + """ + SUBFOLDER = tempfile.mkdtemp() + CONF = UserConfig('spyder-test', + defaults=DEFAULTS, + version=CONF_VERSION, + subfolder=SUBFOLDER, + raw_mode=True, + ) + + def fin(): + """ + Fixture finalizer to delete the temporary CONF element. + """ + shutil.rmtree(SUBFOLDER) + + request.addfinalizer(fin) + return CONF diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/conf.py spyder-3.0.2+dfsg1/spyder/utils/help/conf.py --- spyder-2.3.8+dfsg1/spyder/utils/help/conf.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/conf.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009 Tim Dumol +# Copyright (C) Spyder Project Contributors +# Distributed under the terms of the BSD License + +"""Sphinx conf file for the Help plugin rich text mode""" + +# 3rd party imports +from sphinx import __version__ as sphinx_version + +# Local imports +from spyder.config.main import CONF +from spyder.py3compat import u + +#============================================================================== +# General configuration +#============================================================================== + +# If your extensions are in another directory, add it here. If the directory +# is relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. + +# We need jsmath to get pretty plain-text latex in docstrings +math = CONF.get('help', 'math', '') + +if sphinx_version < "1.1" or not math: + extensions = ['sphinx.ext.jsmath'] +else: + extensions = ['sphinx.ext.mathjax'] + +# For scipy and matplotlib docstrings, which need this extension to +# be rendered correctly (see Issue 1138) +extensions.append('sphinx.ext.autosummary') + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['templates'] + +# MathJax load path (doesn't have effect for sphinx 1.0-) +mathjax_path = 'MathJax/MathJax.js' + +# JsMath load path (doesn't have effect for sphinx 1.1+) +jsmath_path = 'easy/load.js' + +# The suffix of source filenames. +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'docstring' + +# General information about the project. +project = u("Spyder Help plugin") +copyright = u('The Spyder Project Contributors') + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['.build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +# +# TODO: This role has to be set on a per project basis, i.e. numpy, sympy, +# mpmath, etc, use different default_role's which give different rendered +# docstrings. Setting this to None until it's solved. +default_role = 'None' + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +#============================================================================== +# Options for HTML output +#============================================================================== + +# The style sheet to use for HTML and HTML Help pages. A file of that name +# must exist either in Sphinx' static/ path, or in one of the custom paths +# given in html_static_path. +html_style = 'default.css' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +html_short_title = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static'] + +# A dictionary of values to pass into the template engine’s context for all +# pages +html_context = {} + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# If false, no module index is generated. +html_use_modindex = False + +# If false, no index is generated. +html_use_index = False + +# If true, the index is split into individual pages for each letter. +html_split_index = False + +# If true, the reST sources are included in the HTML build as _sources/. +html_copy_source = False + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/__init__.py spyder-3.0.2+dfsg1/spyder/utils/help/__init__.py --- spyder-2.3.8+dfsg1/spyder/utils/help/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT or BSD Licenses +# (See every file for its license) + +""" +spyder.utils.help +================= + +Configuration files for the Help plugin rich text mode +""" + +import sys +from spyder.config.base import get_module_source_path +sys.path.insert(0, get_module_source_path(__name__)) diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/js/collapse_sections.js spyder-3.0.2+dfsg1/spyder/utils/help/js/collapse_sections.js --- spyder-2.3.8+dfsg1/spyder/utils/help/js/collapse_sections.js 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/js/collapse_sections.js 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,65 @@ +//---------------------------------------------------------------------------- +// Toggleable sections +// +// Added expand/collapse functionality to RST sections. +// Code from the Cloud Sphinx theme +// +// Copyright 2011-2012 by Assurance Technologies +// +// Distributed under the terms of the BSD License +//---------------------------------------------------------------------------- + +//============================================================================ +// On document ready +//============================================================================ + +$(document).ready(function (){ + function init(){ + // get header & section, and add static classes + var header = $(this); + var section = header.parent(); + header.addClass("html-toggle-button"); + + // helper to test if url hash is within this section + function contains_hash(){ + var hash = document.location.hash; + return hash && (section[0].id == hash.substr(1) || + section.find(hash.replace(/\./g,"\\.")).length>0); + } + + // helper to control toggle state + function set_state(expanded){ + if(expanded){ + section.addClass("expanded").removeClass("collapsed"); + section.children().show(); + }else{ + section.addClass("collapsed").removeClass("expanded"); + section.children().hide(); + section.children("span:first-child:empty").show(); /* for :ref: span tag */ + header.show(); + } + } + + // initialize state + set_state(section.hasClass("expanded") || contains_hash()); + + // bind toggle callback + header.click(function (){ + section.children().next().slideToggle(300); + set_state(!section.hasClass("expanded")); + $(window).trigger('cloud-section-toggled', section[0]); + }); + + // open section if user jumps to it from w/in page + $(window).bind("hashchange", function () { + if(contains_hash()) { + var link = document.location.hash; + $(link).parents().each(set_state, [true]); + set_state(true); + $('html, body').animate({ scrollTop: $(link).offset().top }, 'fast'); + } + }); + } + + $(".section > h2, .section > h3, .section > h4").each(init); +}); diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/js/copy_button.js spyder-3.0.2+dfsg1/spyder/utils/help/js/copy_button.js --- spyder-2.3.8+dfsg1/spyder/utils/help/js/copy_button.js 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/js/copy_button.js 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,68 @@ +//---------------------------------------------------------------------------- +// Copy Button +// +// Add a [>>>] button on the top-right corner of code samples to hide +// the >>> and ... prompts and the output and thus make the code +// copyable. +// +// Taken from http://docs.python.org/_static/copybutton.js +//---------------------------------------------------------------------------- + +//============================================================================ +// On document ready +//============================================================================ + +$(document).ready(function() { + var div = $('.highlight-python .highlight,' + + '.highlight-python3 .highlight') + var pre = div.find('pre'); + + // get the styles from the current theme + pre.parent().parent().css('position', 'relative'); + var hide_text = 'Hide the prompts and output'; + var show_text = 'Show the prompts and output'; + var border_width = pre.css('border-top-width'); + var border_style = pre.css('border-top-style'); + var border_color = pre.css('border-top-color'); + var button_styles = { + 'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0', + 'border-color': border_color, 'border-style': border_style, + 'border-width': border_width, 'color': border_color, 'text-size': '75%', + 'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em', + 'margin-right': '10px', 'border-top-right-radius': '4px' + } + + // create and add the button to all the code blocks that contain >>> + div.each(function(index) { + var jthis = $(this); + if (jthis.find('.gp').length > 0) { + var button = $('>>>'); + button.css(button_styles) + button.attr('title', hide_text); + jthis.prepend(button); + } + // tracebacks (.gt) contain bare text elements that need to be + // wrapped in a span to work with .nextUntil() (see later) + jthis.find('pre:has(.gt)').contents().filter(function() { + return ((this.nodeType == 3) && (this.data.trim().length > 0)); + }).wrap(''); + }); + + // define the behavior of the button when it's clicked + $('.copybutton').toggle( + function() { + var button = $(this); + button.parent().find('.go, .gp, .gt').hide(); + button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden'); + button.css('text-decoration', 'line-through'); + button.attr('title', show_text); + }, + function() { + var button = $(this); + button.parent().find('.go, .gp, .gt').show(); + button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible'); + button.css('text-decoration', 'none'); + button.attr('title', hide_text); + }); +}); + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/js/fix_image_paths.js spyder-3.0.2+dfsg1/spyder/utils/help/js/fix_image_paths.js --- spyder-2.3.8+dfsg1/spyder/utils/help/js/fix_image_paths.js 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/js/fix_image_paths.js 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,18 @@ +//---------------------------------------------------------------------------- +// Set absolute path for images +// +// Copyright (c) Spyder Project Contributors +// +// Distributed under the terms of the MIT License +//---------------------------------------------------------------------------- + +//============================================================================ +// On document ready +//============================================================================ +$(document).ready(function () { + $('img').attr('src', function(index, attr){ + var path = attr.split('/') + var img_name = path.reverse()[0] + return 'file:///{{img_path}}' + '/' + img_name + }); +}); diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/js/math_config.js spyder-3.0.2+dfsg1/spyder/utils/help/js/math_config.js --- spyder-2.3.8+dfsg1/spyder/utils/help/js/math_config.js 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/js/math_config.js 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,74 @@ +//---------------------------------------------------------------------------- +// Math configuration options and hacks +// +// Copyright (C) Spyder Project Contributors +// +// Distributed under the terms of the MIT License. +//---------------------------------------------------------------------------- + +//============================================================================ +// On document ready +//============================================================================ + +{% if right_sphinx_version and math_on %} + +$(document).ready(function () { + + // MathJax config + // -------------- + MathJax.Hub.Config({ + // We are using SVG instead of HTML-CSS because the last one gives + // troubles on QtWebkit. See this thread: + // https://groups.google.com/forum/?fromgroups#!topic/mathjax-users/HKA2lNqv-OQ + jax: ["input/TeX", "output/SVG"], + + // Menu options are not working. It would be useful to have 'Show TeX + // commands', but it opens an external browser pointing to css_path. + // I don't know why that's happening + showMathMenu: false, + messageStyle: "none", + "SVG": { + blacker: 1 + }, + + {% if platform == 'win32' %} + // Change math preview size so that it doesn't look too big while + // redendered + styles: { + ".MathJax_Preview": { + color: "#888", + "font-size": "55%" + } + } + {% endif %} + }); + + // MathJax Hooks + // ------------- + // Put here any code that needs to be evaluated after MathJax has been + // fully loaded + MathJax.Hub.Register.StartupHook("End", function () { + // Eliminate unnecessary margin-bottom for inline math + $('span.math svg').css('margin-bottom', '0px'); + }); + + {% if platform == 'win32' %} + // Windows fix + // ----------- + // Increase font size of math elements because they appear too small + // compared to the surrounding text. + // Use this hack because MathJax 'scale' option seems to not be working + // for SVG. + $('.math').css("color", "transparent"); + $('.math').css("fontSize", "213%"); + {% endif %} +}); + +{% else %} + +$(document).ready(function () { + // Show math in monospace + $('.math').css('font-family', 'monospace'); +}); + +{% endif %} diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/js/move_outline.js spyder-3.0.2+dfsg1/spyder/utils/help/js/move_outline.js --- spyder-2.3.8+dfsg1/spyder/utils/help/js/move_outline.js 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/js/move_outline.js 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,16 @@ +//---------------------------------------------------------------------------- +// Move Outline section to be the first one +// +// Copyright (c) Spyder Project Contributors +// +// Distributed under the terms of the MIT License +//---------------------------------------------------------------------------- + +//============================================================================ +// On document ready +//============================================================================ + +$(document).ready(function (){ + var first_section_id = $(".section")[0].id; + $("#outline").insertBefore("#" + first_section_id); +}); diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/js/utils.js spyder-3.0.2+dfsg1/spyder/utils/help/js/utils.js --- spyder-2.3.8+dfsg1/spyder/utils/help/js/utils.js 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/js/utils.js 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,34 @@ +//---------------------------------------------------------------------------- +// Several utility functions to modify docstring webpages while they are +// rendered +// +// Copyright (C) Spyder Project Contributors +// +// Distributed under the terms of the MIT License. +//---------------------------------------------------------------------------- + +//============================================================================ +// On document ready +//============================================================================ + +$(document).ready(function () { + // Remove anchor header links. + // They're used by Sphinx to create crossrefs, so we don't need them + $('a.headerlink').remove(); + + // If the first child in the docstring div is a section, change its class + // to title. This means that the docstring has a real title and we need + // to use it. + // This is really useful to show module docstrings. + var first_doc_child = $('div.docstring').children(':first-child'); + if( first_doc_child.is('div.section') && $('div.title').length == 0 ) { + first_doc_child.removeClass('section').addClass('title'); + }; + + // Change docstring headers from h1 to h2 + // It can only be an h1 and that's the page title + // Taken from http://forum.jquery.com/topic/how-to-replace-h1-h2 + $('div.docstring').find('div.section h1').replaceWith(function () { + return '

    ' + $(this).text() + '

    '; + }); +}); diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/sphinxify.py spyder-3.0.2+dfsg1/spyder/utils/help/sphinxify.py --- spyder-2.3.8+dfsg1/spyder/utils/help/sphinxify.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/sphinxify.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,261 @@ +# -*- coding: utf-8 -* + +""" +Process docstrings with Sphinx + +AUTHORS: +- Tim Joseph Dumol (2009-09-29): initial version +- The Spyder Project Contributors: Several changes to make it work with Spyder + +Copyright (C) 2009 Tim Dumol +Copyright (C) Spyder Project Contributors +Distributed under the terms of the BSD License + +Taken from the Sage project (www.sagemath.org). +See here for the original version: +www.sagemath.org/doc/reference/sagenb/misc/sphinxify.html +""" + +# Stdlib imports +import codecs +import os +import os.path as osp +import shutil +import sys +from tempfile import mkdtemp +from xml.sax.saxutils import escape + +# 3rd party imports +from docutils.utils import SystemMessage as SystemMessage +from jinja2 import Environment, FileSystemLoader +import sphinx +from sphinx.application import Sphinx + +# Local imports +from spyder.config.base import (_, get_module_data_path, + get_module_source_path) +from spyder.utils import encoding + + +#----------------------------------------------------------------------------- +# Globals and constants +#----------------------------------------------------------------------------- + +# Note: we do not use __file__ because it won't be working in the stand-alone +# version of Spyder (i.e. the py2exe or cx_Freeze build) +CONFDIR_PATH = get_module_source_path('spyder.utils.help') +CSS_PATH = osp.join(CONFDIR_PATH, 'static', 'css') +JS_PATH = osp.join(CONFDIR_PATH, 'js') + +# To let Debian packagers redefine the MathJax and JQuery locations so they can +# use their own packages for them. See Issue 1230, comment #7. +MATHJAX_PATH = get_module_data_path('spyder', + relpath=osp.join('utils', 'help', + JS_PATH, 'mathjax'), + attr_name='MATHJAXPATH') + +JQUERY_PATH = get_module_data_path('spyder', + relpath=osp.join('utils', 'help', + JS_PATH), + attr_name='JQUERYPATH') + +#----------------------------------------------------------------------------- +# Utility functions +#----------------------------------------------------------------------------- + +def is_sphinx_markup(docstring): + """Returns whether a string contains Sphinx-style ReST markup.""" + # this could be made much more clever + return ("`" in docstring or "::" in docstring) + + +def warning(message): + """Print a warning message on the rich text view""" + env = Environment() + env.loader = FileSystemLoader(osp.join(CONFDIR_PATH, 'templates')) + warning = env.get_template("warning.html") + return warning.render(css_path=CSS_PATH, text=message) + + +def usage(title, message, tutorial_message, tutorial): + """Print a usage message on the rich text view""" + env = Environment() + env.loader = FileSystemLoader(osp.join(CONFDIR_PATH, 'templates')) + usage = env.get_template("usage.html") + return usage.render(css_path=CSS_PATH, title=title, intro_message=message, + tutorial_message=tutorial_message, tutorial=tutorial) + + +def generate_context(name='', argspec='', note='', math=False, collapse=False, + img_path=''): + """ + Generate the html_context dictionary for our Sphinx conf file. + + This is a set of variables to be passed to the Jinja template engine and + that are used to control how the webpage is rendered in connection with + Sphinx + + Parameters + ---------- + name : str + Object's name. + note : str + A note describing what type has the function or method being + introspected + argspec : str + Argspec of the the function or method being introspected + math : bool + Turn on/off Latex rendering on the OI. If False, Latex will be shown in + plain text. + collapse : bool + Collapse sections + img_path : str + Path for images relative to the file containing the docstring + + Returns + ------- + A dict of strings to be used by Jinja to generate the webpage + """ + + if img_path and os.name == 'nt': + img_path = img_path.replace('\\', '/') + + context = \ + { + # Arg dependent variables + 'math_on': 'true' if math else '', + 'name': name, + 'argspec': argspec, + 'note': note, + 'collapse': collapse, + 'img_path': img_path, + + # Static variables + 'css_path': CSS_PATH, + 'js_path': JS_PATH, + 'jquery_path': JQUERY_PATH, + 'mathjax_path': MATHJAX_PATH, + 'right_sphinx_version': '' if sphinx.__version__ < "1.1" else 'true', + 'platform': sys.platform + } + + return context + + +def sphinxify(docstring, context, buildername='html'): + """ + Runs Sphinx on a docstring and outputs the processed documentation. + + Parameters + ---------- + docstring : str + a ReST-formatted docstring + + context : dict + Variables to be passed to the layout template to control how its + rendered (through the Sphinx variable *html_context*). + + buildername: str + It can be either `html` or `text`. + + Returns + ------- + An Sphinx-processed string, in either HTML or plain text format, depending + on the value of `buildername` + """ + + srcdir = mkdtemp() + srcdir = encoding.to_unicode_from_fs(srcdir) + + base_name = osp.join(srcdir, 'docstring') + rst_name = base_name + '.rst' + + if buildername == 'html': + suffix = '.html' + else: + suffix = '.txt' + output_name = base_name + suffix + + # This is needed so users can type \\ on latex eqnarray envs inside raw + # docstrings + if context['right_sphinx_version'] and context['math_on']: + docstring = docstring.replace('\\\\', '\\\\\\\\') + + # Add a class to several characters on the argspec. This way we can + # highlight them using css, in a similar way to what IPython does. + # NOTE: Before doing this, we escape common html chars so that they + # don't interfere with the rest of html present in the page + argspec = escape(context['argspec']) + for char in ['=', ',', '(', ')', '*', '**']: + argspec = argspec.replace(char, + '' + char + '') + context['argspec'] = argspec + + doc_file = codecs.open(rst_name, 'w', encoding='utf-8') + doc_file.write(docstring) + doc_file.close() + + temp_confdir = False + if temp_confdir: + # TODO: This may be inefficient. Find a faster way to do it. + confdir = mkdtemp() + confdir = encoding.to_unicode_from_fs(confdir) + generate_configuration(confdir) + else: + confdir = osp.join(get_module_source_path('spyder.utils.help')) + + confoverrides = {'html_context': context} + + doctreedir = osp.join(srcdir, 'doctrees') + + sphinx_app = Sphinx(srcdir, confdir, srcdir, doctreedir, buildername, + confoverrides, status=None, warning=None, + freshenv=True, warningiserror=False, tags=None) + try: + sphinx_app.build(None, [rst_name]) + except SystemMessage: + output = _("It was not possible to generate rich text help for this " + "object.
    " + "Please see it in plain text.") + return warning(output) + + # TODO: Investigate if this is necessary/important for us + if osp.exists(output_name): + output = codecs.open(output_name, 'r', encoding='utf-8').read() + output = output.replace('
    ', '
    ')
    +    else:
    +        output = _("It was not possible to generate rich text help for this "
    +                    "object.
    " + "Please see it in plain text.") + return warning(output) + + if temp_confdir: + shutil.rmtree(confdir, ignore_errors=True) + shutil.rmtree(srcdir, ignore_errors=True) + + return output + + +def generate_configuration(directory): + """ + Generates a Sphinx configuration in `directory`. + + Parameters + ---------- + directory : str + Base directory to use + """ + + # conf.py file for Sphinx + conf = osp.join(get_module_source_path('spyder.utils.help'), + 'conf.py') + + # Docstring layout page (in Jinja): + layout = osp.join(osp.join(CONFDIR_PATH, 'templates'), 'layout.html') + + os.makedirs(osp.join(directory, 'templates')) + os.makedirs(osp.join(directory, 'static')) + shutil.copy(conf, directory) + shutil.copy(layout, osp.join(directory, 'templates')) + open(osp.join(directory, '__init__.py'), 'w').write('') + open(osp.join(directory, 'static', 'empty'), 'w').write('') diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/static/css/default.css spyder-3.0.2+dfsg1/spyder/utils/help/static/css/default.css --- spyder-2.3.8+dfsg1/spyder/utils/help/static/css/default.css 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/static/css/default.css 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,415 @@ +body { + background-color: white; + color: rgb(51, 51, 51); + margin: 0px 25px 15px 25px; +} + + +/* --- Title style --- */ +div.title h1 { + font-size: 180%; + font-family: 'Trebuchet MS', sans-serif; + background-color: #6487DC; + background-image: -webkit-gradient( + linear, + 0 0, + 0 100%, + from(#54b4eb), + color-stop(60%, #2fa4e7), + to(#1d9ce5) + ); + text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2); + font-weight: normal; + padding: 6px 0px 6px 20px; + margin: 0px -25px; + color: #FFFFFF; +} + +/* + * The next two styles are needed to + * modify the anchors present on the + * title of pages like scipy.stats or + * scipy.io + */ +div.title h1 a { + color: transparent; + cursor: default; +} + +div.title h1 tt { + font-size: 95%; + background-color: transparent; + color: #FFFFFF; +} + + +/* --- Metadata style --- */ +div.metadata { + margin-top: 10px; + margin-bottom: 15px; + margin-right: 1px; + padding: 1px; + background-color: #EEEEEE; + border: 1px solid #C9C9C9; + border-radius: 6px 6px 6px 6px; + box-shadow: 1px 1px 7px #CACACA; +} + +div.metadata p { + margin: 7px 0px 7px 10px; +} + +span.def { + font-family: monospace; + font-size: 90%; +} + +span.argspec-highlight { + color: red; + font-size: 110%; + font-weight: 900; +} + + +/* --- Docstring div style --- */ +div.docstring { + margin-top: -1px; +} + +div.docstring p { + padding: 0px 2px 0px; +} + + +/* --- Headers style --- */ +h2, h3, h4 { + font-family: 'Helvetica', sans-serif; + color: rgb(49, 126, 172); + margin-top: 20px; + margin-bottom: 10px; +} + +h2 { + font-size: 140%; + font-weight: normal; + border-bottom: 1px solid rgb(220, 220, 220); + padding: 4px 0px 4px 0px; +} + +h3 { + font-size: 115%; +} + +h4 { + font-size: 100%; + margin-top: 14px; + font-weight: normal; +} + +h2.html-toggle-button, h3.html-toggle-button, h4.html-toggle-button { + padding-left: 20px; +} + +.collapsed > h2, .collapsed > h3, .collapsed > h4, .expanded > h2, .expanded > h3, .expanded > h4 { + background-color: transparent; + background-image: url(../images/collapse_expand.png); + background-repeat: no-repeat; + background-attachment: scroll; + cursor: pointer; +} + +.collapsed > h2 { + background-position: 2px 7px; +} + +.collapsed > h3 { + background-position: 2px 2px; +} + +.collapsed > h4 { + background-position: 2px 0px; +} + +.expanded > h2 { + background-position: 0px -31px; +} + +.expanded > h3 { + background-position: 0px -38px; +} + +.expanded > h4 { + background-position: 0px -39px; +} + +dl.docutils { + padding: 0px 10px 0px; +} + +div.section p { + padding: 0px 2px 0px; +} + +#warning { + margin-top: 5px; + background-color: #FFE4E4; + border: 1px solid #F66; + padding: 4px 8px 4px 8px; + text-align: center; +} + +#doc-warning { + margin-top: 16px; + width: 45%; + margin-left: auto; + margin-right: auto; + color: rgb(185, 74, 72); + background-color: rgb(242, 222, 222); + border: 1px solid rgb(238, 211, 215); + border-radius: 4px 4px 4px 4px; + padding: 15px; + text-align: center; + font-weight: bold; + font-size: 105%; +} + + +/* --- Links --- */ +a { + text-decoration: none; + color: rgba(40, 130, 180, 1); +} + +a:hover { + text-decoration: underline; +} + + +/* --- Images --- */ +img { + box-shadow: 0px 2px 6px #cacaca; + border: 1px solid #c9c9c9; +} + +img.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + + +/* --- Lists style --- */ +ol.arabic { + margin-left: -10px; +} + +ul { + margin-left: -5px; +} + +/* --- Literal blocks style --- */ +pre.literal-block { + padding-left: 35px; + font-size: 95%; +} + + +/* --- Docutils table style --- */ +table.docutils { + border-collapse: collapse; + border-spacing: 0; + border: #DDDDDD; + margin-left: auto; + margin-right: auto; + margin-top: 17px; + margin-bottom: 17px; + width: 90%; +} + +table.docutils td { + padding: 5px; +} + +table.docutils tr.row-odd { + background-color: rgb(249, 249, 249); +} + + +/* --- Docutils table headers --- */ +table.docutils th { + background-color: #EEEEEE; + border-bottom-color: #DDDDDD; + border-bottom-style: solid; + border-bottom-width: 1px; + border-top-color: #DDDDDD; + border-top-style: solid; + border-top-width: 1px; + font-weight: bold; + text-align: center; + padding: 6px 0px 6px 8px; + color: rgb(65, 65, 65); +} + + +/* --- Field-list table style --- */ +table.docutils.field-list { + font-size: 80%; + border-collapse: collapse; + border-left: transparent; + border-right: transparent; + margin-top: 15px; + margin-left: 40px; + width: 83%; +} + + +/* --- Field-list table headers --- */ +table.docutils.field-list th { + background-color: transparent; + border-top: transparent; + border-bottom: transparent; + color: black; + font-weight: bold; + text-align: left; + padding: 4px 0px 4px 8px; +} + + +/* --- Spacing around example code --- */ +div.highlight pre { + padding: 9px 14px; + background-color: rgb(247, 247, 249); + border-radius: 4px 4px 4px 4px; + border: 1px solid rgb(225, 225, 232); +} + +div.highlight { + padding: 0px 10px 0px; +} + +dt { + font-weight: bold; + /*font-size: 16px;*/ +} + +.classifier { + /*font-size: 10pt;*/ + font-weight: normal; +} + +tt { + background-color: #ECF0F3; + /*font-size: 95%;*/ + padding: 0px 1px; +} + + + +div.admonition.note { + font-size: 0.95em; + margin: 1.3em; + border: 1px solid #BCE8F1; + background-color: #D9EDF7; + padding: 0px 5px 0 5px; + color: #3A87AD; +} + +div.admonition p.admonition-title { + font-size: 1em; + margin-top: 7px; + font-weight: bold; +} + + +/* ----------- Panels ----------- */ + +.panel { + margin-top: 15px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 4px; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} + +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 110%; + color: rgb(255,255,255); +} + +.panel-body { + padding: 15px; +} + +.panel-usage { + border-color: #2fa4e7; + margin-top: 15px; + width: 60%; + margin-left: auto; + margin-right: auto; +} + +.panel-usage > .panel-heading { + color: #ffffff; + background-color: #2fa4e7; + border-color: #2fa4e7; +} + +.panel-usage > .panel-body > br { + display: block; + margin: 12px 0; + content: ""; +} + +.hr { + background-color: rgb(200, 200, 200); + height: 1px; +} + + +/* ----------- IPython console styles ----------- */ + +/* --- Loading --- */ +.loading { + position: absolute; + margin: -20px 0 0 -95px; + width: 180px; + height: auto; + left: 50%; + top: 50%; + background-color: #EEEEEE; + border: 1px solid #C9C9C9; + border-radius: 6px; + box-shadow: 0px 0px 7px #CACACA; + color: #333333; + padding: 12px; + text-align: center; +} + +#loading-image { + float: left; +} + +#loading-message { + margin-left: 23px; +} + +/* --- Kernel error messages --- */ +.panel-danger { + border-color: #eed3d7; +} + +.panel-danger > .panel-heading { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; + background-color: rgb(199, 28, 34); +} diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/static/css/pygments.css spyder-3.0.2+dfsg1/spyder/utils/help/static/css/pygments.css --- spyder-2.3.8+dfsg1/spyder/utils/help/static/css/pygments.css 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/static/css/pygments.css 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,61 @@ +.hll { background-color: #ffffcc } +.c { color: #408090; font-style: italic } /* Comment */ +.err { border: 1px solid #FF0000 } /* Error */ +.k { color: #007020; font-weight: bold } /* Keyword */ +.o { color: #666666 } /* Operator */ +.cm { color: #408090; font-style: italic } /* Comment.Multiline */ +.cp { color: #007020 } /* Comment.Preproc */ +.c1 { color: #408090; font-style: italic } /* Comment.Single */ +.cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ +.gd { color: #A00000 } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #FF0000 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #00A000 } /* Generic.Inserted */ +.go { color: #303030 } /* Generic.Output */ +.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #0040D0 } /* Generic.Traceback */ +.kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +.kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #007020 } /* Keyword.Pseudo */ +.kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #902000 } /* Keyword.Type */ +.m { color: #208050 } /* Literal.Number */ +.s { color: #4070a0 } /* Literal.String */ +.na { color: #4070a0 } /* Name.Attribute */ +.nb { color: #007020 } /* Name.Builtin */ +.nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +.no { color: #60add5 } /* Name.Constant */ +.nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.ni { color: #d55537; font-weight: bold } /* Name.Entity */ +.ne { color: #007020 } /* Name.Exception */ +.nf { color: #06287e } /* Name.Function */ +.nl { color: #002070; font-weight: bold } /* Name.Label */ +.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.nt { color: #062873; font-weight: bold } /* Name.Tag */ +.nv { color: #bb60d5 } /* Name.Variable */ +.ow { color: #007020; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mf { color: #208050 } /* Literal.Number.Float */ +.mh { color: #208050 } /* Literal.Number.Hex */ +.mi { color: #208050 } /* Literal.Number.Integer */ +.mo { color: #208050 } /* Literal.Number.Oct */ +.sb { color: #4070a0 } /* Literal.String.Backtick */ +.sc { color: #4070a0 } /* Literal.String.Char */ +.sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +.s2 { color: #4070a0 } /* Literal.String.Double */ +.se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +.sh { color: #4070a0 } /* Literal.String.Heredoc */ +.si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +.sx { color: #c65d09 } /* Literal.String.Other */ +.sr { color: #235388 } /* Literal.String.Regex */ +.s1 { color: #4070a0 } /* Literal.String.Single */ +.ss { color: #517918 } /* Literal.String.Symbol */ +.bp { color: #007020 } /* Name.Builtin.Pseudo */ +.vc { color: #bb60d5 } /* Name.Variable.Class */ +.vg { color: #bb60d5 } /* Name.Variable.Global */ +.vi { color: #bb60d5 } /* Name.Variable.Instance */ +.il { color: #208050 } /* Literal.Number.Integer.Long */ \ No newline at end of file Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/utils/help/static/images/collapse_expand.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/utils/help/static/images/collapse_expand.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/utils/help/static/images/debug-continue.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/utils/help/static/images/debug-continue.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/utils/help/static/images/debug-step-in.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/utils/help/static/images/debug-step-in.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/utils/help/static/images/debug-step-out.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/utils/help/static/images/debug-step-out.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/utils/help/static/images/debug-step-over.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/utils/help/static/images/debug-step-over.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/utils/help/static/images/spyder-hello-docstring.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/utils/help/static/images/spyder-hello-docstring.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/utils/help/static/images/spyder-nice-docstring-rendering.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/utils/help/static/images/spyder-nice-docstring-rendering.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder/utils/help/static/images/spyder-sympy-example.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder/utils/help/static/images/spyder-sympy-example.png differ diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/templates/layout.html spyder-3.0.2+dfsg1/spyder/utils/help/templates/layout.html --- spyder-2.3.8+dfsg1/spyder/utils/help/templates/layout.html 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/templates/layout.html 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,85 @@ +{# + layout.html + ~~~~~~~~~~~ + + Layout template for the Help plugin + + :copyright: Copyright (c) Spyder Project Contributors. + :copyright: Copyright 2009 by Tim Dumol + :license: BSD license +#} + + + + + + + + + + + + {% if right_sphinx_version and math_on %} + {# DON'T try to load MathJax from the net. It's slow and sometimes gives + errors. See this thread for more info: + http://tex.stackexchange.com/questions/2692/comparing-mathjax-and-mathml + #} + + {% endif %} + + + + + + + + +{% if collapse %} + + +{% endif %} + +{% if img_path %} + +{% endif %} + + + + {# Docstring header #} + {% if name %} +

    {{name}}

    + + {% if argspec or note %} + + {% endif %} + + {% endif %} + + {# Docstring text #} +
    + {% block body %}{% endblock %} + {% if collapse %} +
    +

    Outline

    + {{ toc }} +
    + {% endif %} +
    + + + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/templates/usage.html spyder-3.0.2+dfsg1/spyder/utils/help/templates/usage.html --- spyder-2.3.8+dfsg1/spyder/utils/help/templates/usage.html 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/templates/usage.html 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,39 @@ +{# + usage.html + ~~~~~~~~~~ + + A simple page to inform users how to get help on the Help plugin + + :copyright: Copyright (c) Spyder Project Contributors. + :license: MIT license +#} + + + + + + + + + + + +
    +
    +
    {{title}}
    +
    +
    + {{intro_message}} +

    +
    +
    +
    + {{tutorial_message}} + {{tutorial}} +
    +
    +
    + + + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/templates/warning.html spyder-3.0.2+dfsg1/spyder/utils/help/templates/warning.html --- spyder-2.3.8+dfsg1/spyder/utils/help/templates/warning.html 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/templates/warning.html 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,26 @@ +{# + warning.html + ~~~~~~~~~~~ + + A simple page to emit a warning when no docstring text was found for the + one a user was looking for + + :copyright: Copyright (c) Spyder Project Contributors + :license: MIT license +#} + + + + + + + + + + + +
    {{text}}
    + + + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/help/tutorial.rst spyder-3.0.2+dfsg1/spyder/utils/help/tutorial.rst --- spyder-2.3.8+dfsg1/spyder/utils/help/tutorial.rst 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/help/tutorial.rst 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,950 @@ +====================================================== +Spyder - the Scientific PYthon Development EnviRonment +====================================================== + +*Spyder* is an Integrated Development Environment (IDE) for scientific +computing using the Python programming language. It comes with an +Editor to write code, a Console to evaluate it and see its results at +any time, a Variable Explorer to see what variables have been defined +during evaluation, and several other facilities to help you to +effectively develop the programs you need as a scientist. + + +This tutorial is authored by +`Hans Fangohr `__ from the +University of Southampton (UK) (see `historical note`_ for more +detail). + + +First steps with Spyder +####################### + +This section is aimed at Python and Spyder beginners. If you find it too +simple, please continue to the next section. + +Execute a given program +----------------------- + +* We are going to use this program as a first example: + + .. code-block:: python + + # Demo file for Spyder Tutorial + # Hans Fangohr, University of Southampton, UK + + def hello(): + """Print "Hello World" and return None""" + print("Hello World") + + # main program starts here + hello() + +* To use this program, please create a new file in the Spyder editor pane. Then copy + and paste the code inside the box above on the file, and the save it with the name + ``hello.py``. + +* To execute the program, select ``Run > Run`` from the menu (or press F5), and + confirm the ``Run settings`` if required. + + If this is your first time, you should see an output like this:: + + In [1]: runfile('/Users/fangohr/Desktop/hello.py', wdir=r'/Users/fangohr/Desktop') + Hello World + + In [2]: + + If so, then you have just run your first Python program - well done. + + .. note:: + + The particular path shown next to ``runfile`` will depend on where you have saved + the file, but this is inserted by Spyder automatically. + + +Use the IPython Console +~~~~~~~~~~~~~~~~~~~~~~~ + +Before we proceed, we recommend you to use the IPython console. This console can do a +little more than the standard Python console, and we suggest to use it as the default +console here. + + +What happens when you execute the program? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Python reads the file line by line, ignoring comments (i.e. lines starting + with the ``#`` symbol). + +* When it comes across the ``def`` keyword, it knows that a function + is DEFined in this and the next (one or more) lines. All *indented* lines + following ``def hello():`` belong to the function body. + + Note that the function object is just created at this point in the + file, but the function is not yet called (i.e. not executed). + +* When Python comes across commands (other than ``def ...`` and a few + other keywords) that are written in the left-most column, it will + execute these immediately. In the ``hello.py`` file this is only the + line reading ``hello()`` which will actually call (i.e. *execute*) + the function with name ``hello``. + + If you comment or remove the line ``hello()`` from the program and run + the whole file again (by pressing F5, or selecting ``Run > Run``), nothing + will be printed (because the function ``hello`` is defined, but not called, + i.e. not executed). + + +Now you should know how to execute a Python program that you have in +the editor pane in Spyder using the IPython console. + +If you are just starting to learn Python, this is probably a good +point to return to your text book / course and look at more basic +examples. + + +The next section gives more detailed information how you can execute +*parts* of the code in the editor in the IPython console, and thus +update parts of your definitions in the editor. This is a more +advanced technique but can be very useful. (You may also be interested +in the option to execute chunks (so-called "cells") of code that are +separated by delimiters -- see `Shortcuts for useful functions`_.) + + + +Call existing functions in the console +-------------------------------------- + +Once you have executed the ``hello.py`` program, the function object ``hello`` +is defined and known to the IPython console. We can thus call the function from +the console like this: + +* Type ``hello()`` in the console (next to ``In [?]`` prompt, where + the question mark can be any positive integer number), and press the + ``Enter`` key. + + You should find that the ``hello()`` function is executed again, + i.e. ``Hello World`` is printed again. Your function call at the + console together with the output should look like this:: + + In [ ]: hello() + Hello World + +* Can you see how this differs from executing the whole program again? + + When we execute the whole program (by pressing F5), Python goes + through the file, creates the ``hello`` function object (overriding + the previous object), reaches the ``hello()`` line and calls the + function. + + When we call ``hello()`` in the console, we only call the + function object ``hello`` that has been defined in the IPython + console when we executed the whole ``hello.py`` file earlier (by + pressing F5). + + This will become clearer over time and also when we work with + slightly larger examples. You may want to return to this tutorial at + a slightly later stage. + + +Inspecting objects defined in the console +----------------------------------------- + +* Python provides a function that displays all known objects in the + current name space of the console. It is called ``dir()``: when you + type ``dir()`` at the console, you get a list of known objects. Ignore + everything starting with an underscore for now. Can you see ``hello`` + in the list? + + .. note:: + + If you get a long list of defined objects, then Spyder may have + done some convenience imports for you already. To address this you + may want to: + + - `Reset the name space`_ + + - Execute ``hello.py`` again by pressing F5 + + Then run ``dir()`` as suggested above. + +* Once an object is visible in the current name space (as is ``hello`` + in this example), we can use the ``help`` function as follows to + learn about it: typing ``help(hello)`` at the console prompt, you + should see an output like this:: + + In [ ]: help(hello) + Help on function hello in module __main__: + + hello() + Print "Hello World" and return None + + + Where does Python take the information from? Some of it (like the + number of input arguments and names of those variables; here we have + no input arguments) Python can find through inspecting its objects, + additional information comes from the documentation string provided + for the function object ``hello``. The documentation string is the + first string immediately below the line ``def hello():``. + + These strings are special, and they are called *docstrings* which is short for + *documentation strings*. As they usually extend over multiple lines, there + are enclosed by triple single quotes (``'''``) or triple double quotes + (``"""``). + +* The Spyder environment also provides a ``Help`` pane which + by default is located in the top right corner. + + While the cursor is on the name of an object, + press ``CTRL+i`` (or ``CMD+i`` on Mac), and you should find that + the same information as we obtained from ``help(hello)`` is provided + automatically in the Help: + + .. image:: images/spyder-hello-docstring.png + :align: center + + This works in the console and in the editor. + +Updating objects +---------------- + +Simple strategy: re-execute whole program +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* In the Editor window, change the function ``hello`` so + that it prints ``Good Bye World`` rather than ``Hello World``. + +* Press F5 (to execute the whole program) and check that the output of the + program is now:: + + Good Bye World + +What has happened when you pressed F5 is this: Python has gone through +the ``hello.py`` file and created a new function object ``hello`` +(overriding the function object ``hello`` we had defined before) and +then executed the function. + + +Looking at the details +~~~~~~~~~~~~~~~~~~~~~~ + +We need to start with a clearly defined state. To do this, please change the +function ``hello()`` back so that it prints ``Hello World``, then press +F5 to run the whole program and check that it prints ``Hello World``. + +* Call the function ``hello()`` from the command prompt (as described + in `Call existing functions in the console`_). You + should see ``Hello World`` printed. + +* Now change the function definition so that it would print ``Laters + World``, and save the file (but do NOT execute the program, i.e. do + NOT press F5 yet). + +* Call the function ``hello()`` in the console again. You + should find that the text printed reads ``Hello World``, like here + :: + + In [ ]: hello() + Hello World + + Why is this so? Because the ``hello`` function object in the console + is the old one which prints ``Hello World``. So far, we have + changed the file ``hello.py`` (and replaced ``Hello World`` in there with + ``Laters World``) in the editor but this has not affected the objects that + have previously been created in the console. + +Here are two possibilities to use our modified version of the ``hello`` +function: + +* Option 1: execute the whole file ``hello.py`` again by pressing F5: + this creates a new function object ``hello`` (and overrides the old + one). You should find that if you press F5, and then call + ``hello()`` at the prompt, the new text ``Laters World`` is printed. + +* Option 2: select the region you have changed (in this case the whole + function ``hello``, starting from the line ``def hello():`` down to + ``print("Laters Wold")``, and then select ``Run > Run selection``. + + This will update the ``hello`` object in the console without + having to execute the whole ``hello.py`` file:: + + In [ ]: def hello(): + ...: """Print "Hello World" and return None""" + ...: print("Laters world") + ...: + + If we now type ``hello()``, we see the update response:: + + In [ ]: hello() + Laters world + +The ability to execute *parts of the code* to update some objects in +the console (in the example above, we updated the function object +``hello``), is of great use when developing and debugging more complex +codes, and when creating objects/data in the console session take +time. For example, by modifying only the functions (or +classes/objects, etc.) that we are actually developing or debugging, we +can keep re-using the data and other objects that are defined in the +console session. + + + +Recommended first steps for Python beginners +############################################ + +To teach and learn Python programming, we recommend here to use IPython +instead of the normal Python console. This accepts IPython as the +de-facto standard in the scientific Python community. + +Switch to an IPython console +---------------------------- + +If you already have an IPython console active, you can ignore this section, +and make it visible by clicking on the "IPython console" rider. + +In the console window (lower right corner by default), you see by +default a prompt with three greater than signs, i.e. ``>>>``. This +shows that we are using the ``console`` -- basically a normal Python +console session (with some added functionality from Spyder). + +Instead, we would like to use an *Interactive Python* console, short *IPython* +from the `IPython project `__. To do this, select +``Consoles > Open an IPython Console``. + +You should see in the consolse window a new shell appearing, and the +IPython prompt ``In [1]:`` should be displayed. + +Reset the name space +-------------------- + +The `name space `__ +(i.e. the collection of objects defined in the console at any given time) +can be cleared in IPython using the ``%reset`` command. Type ``%reset`` +and press return, then confirm with ``y``:: + + In [1]: %reset + + Once deleted, variables cannot be recovered. Proceed (y/[n])? y + + In [2]: + +That's all. + +We discuss this a little further, but you can skip the following if +you are not interested: after issuing the ``%reset`` command, we +should have only a few objects defined in the name space of that +session. We can list all of them using the ``dir()`` command:: + + In [2]: dir() + Out[2]: + ['In', + 'Out', + '__builtin__', + '__builtins__', + '__name__', + '_dh', + '_i', + '_i2', + '_ih', + '_ii', + '_iii', + '_oh', + '_sh', + 'exit', + 'get_ipython', + 'help', + 'quit'] + +Finally, if you like to skip the confirmation step of the ``reset`` +command, use can use ``%reset -f`` instead of ``%reset``. + +Strive for PEP8 Compliance +-------------------------- + +In addition to the syntax that is enforced by the Python programming +language, there are additional conventions regarding the layout of +the source code, in particular the `Style Guide for Python source code +`__ known as "PEP8". By +following this guide and writing code in the same style as almost all +Python programmers do, it becomes easier to read, and thus easier to +debug and re-use -- both for the original author and others. + +You should change Spyders settings to +`Warn if PEP8 coding guidelines are violated`_. + + + +Selected Preferences +#################### + +Where are the preferences? +-------------------------- + +A lot of Spyder's behaviour can be configured through it's +Preferences. Where this is located in the menu depends on your +operating system: + +* On Windows and Linux, go to ``Tools > Preferences`` + +* On Mac OS, go to ``Python/Spyder > Preferences`` + +Warn if PEP8 coding guidelines are violated +------------------------------------------- + +Go to ``Preferences > Editor > Code +Introspection/Analysis`` and +select the tickbox next to ``Style analysis (PEP8)`` + +Automatic Symbolic Python +------------------------- + +Through ``Preferences > IPython console > Advanced Settings > Use +symbolic math`` we can activate IPython's SYMbolic PYthon (sympy) mode that is +provided by the `sympy `__ module. This mode +in Spyder allows nicely rendered mathematical output (LaTeX style) and also +imports some sympy objects automatically when the IPython console starts, and +reports what it has done. + +.. code-block:: python + + These commands were executed: + >>> from __future__ import division + >>> from sympy import * + >>> x, y, z, t = symbols('x y z t') + >>> k, m, n = symbols('k m n', integer=True) + >>> f, g, h = symbols('f g h', cls=Function) + +We can then use the variables ``x``, ``y``, for example like this: + +.. image:: images/spyder-sympy-example.png + :align: center + + + +Shortcuts for useful functions +############################## + +- ``F5`` executes the current file + +- ``F9`` executes the currently highlighted chunk of code: this is very useful + to update definitions of functions (say) in the console session without + having to run the whole file again. If nothing is selected ``F9`` executes + the current line. + +- ``Tab`` auto-completes commands, function names, variable + names, methods in the Console (both Python and IPython) and in the + Editor. This feature is very useful, and should be used + routinely. Do try it now if auto-completion is new to you. + Assume you have defined a variable:: + + mylongvariablename = 42 + + Suppose we need to write code that computes ``mylongvariablename + + 100``, we can simply type ``my`` and then press the ``Tab`` key. The + full variable name will be completed and inserted at the cursor + position if the name is unique, and then we can carry on and type + ``+ 100``. If the name is not uniquely identifiable given the + letters ``my``, a list field will be displayed from which the right + variable can be chosen. Choosing from the list can be done with the + ```` key and ```` key and the ``Enter`` + key to select, or by typing more letters of the name in question + (the selection will update automatically) and confirming by pressing + ``Enter`` when the right name is identified. + +- ``Ctrl+Enter`` executes the current cell (menu entry ``Run > Run + cell``). A cell is defined as the code between two lines which start with + the agreed tag ``#%%``. + +- ``Shift+Enter`` executes the current cell and advances the + cursor to the next cell (menu entry ``Run > Run cell and + advance``). + + Cells are useful to execute a large file/code segment in smaller + units. (It is a little bit like a cell in an IPython notebook, in + that chunks of code can be run independently.) + +- ``Alt+`` moves the current line up. If multiple lines are + highlighted, they are moved up together. ``Alt+`` + works correspondingly moving line(s) down. + +- ``Ctrl+Left Mouse Click`` on a function/method in the source, opens a new + editor windows showing the definition of that function. + +- ``Shift+Ctrl+Alt+M`` maximizes the current window (or changes the + size back to normal if pressed in a maximized window) + +- ``Ctrl+Shift+F`` activates the search pane across all files. + +- ``Cmd + +`` (On MacOS X) or ``Ctrl + +`` (otherwise) will increase the font + size in the Editor, whereas ``Cmd + -`` (``Ctrl + -``) will decrease it. + Also works in the IPython Console. + + The font size for the Help, the Python console etc. can be set + individually via ``Preferences > Help`` etc. + + I couldn't find a way of changing the font size in the variable explorer. + +- ``Cmd+S`` (on MacOS X) and ``Ctrl+S`` (otherwise) *in the Editor* + pane saves the file + currently being edited. This also forces various warning triangles + in the left column of the Editor to be updated (otherwise they + update every 2 to 3 seconds by default). + +- ``Cmd+S`` (on MacOS X) and ``Ctrl+S`` (otherwise) *in the IPython console* + pane saves the current IPython session as an HTML file, including + any figures that may be displayed inline. This is useful as a quick + way of recording what has been done in a session. + + (It is not + possible to load this saved record back into the session - if you + need functionality like this, look for the IPython Notebook.) + +- ``Cmd+I`` (on Mac OS X) and ``Ctrl+I`` (otherwise) when pressed + while the cursor is on an object, opens documentation for that + object in the help pane. + + + +Run Settings +############ + +These are the settings that define how the code in the editor is +executed if we select ``Run > Run`` or press F5. + +By default, the settings box will appear the first time we try to execute a +file. If we want to change the settings at any other time, they can be +found under ``Run > Configure`` or by pressing F6. + +There are three choices for the console to use, of which I'll discuss the +first two. Let's assume we have a program ``hello.py`` in the editor which +reads + +.. code-block:: python + + def hello(name): + """Given an object 'name', print 'Hello ' and the object.""" + print("Hello {}".format(name)) + + + i = 42 + if __name__ == "__main__": + hello(i) + +Execute in current Python or IPython console +-------------------------------------------- + +This is the default suggestion, and also generally a good choice. + +Persistence of objects I (after code execution) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Choosing ``Execute in current Python or IPython console`` +setting under ``Run > Configure`` means that + +1. When the execution of ``hello.py`` is completed, we can interact + with the console in which the program ran, and we can use the + convenient IPython console for this (rather than the default + Python console). + + In particular, + +2. we can inspect and interact with objects that the execution of + our program created, such as ``i`` and ``hello()``. + +This is generally very useful for incremental coding, testing and +debugging: we can call ``hello()`` directly from the console +prompt, and don't need to execute the whole ``hello.py`` for this +(although if we change the function ``hello()``, we need to execute +the file, or at least the function definition, to make the new +version of ``hello()`` visible at the console; either by +executing the whole buffer or via ``Run > Run Selection``.) + +Persistence of objects II (from before code execution) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +However, executing the code in the editor in the current console +also means that + +3. the code that executes can see other (global) objects that were + defined in the console session. + +*This* persistence of objects is easily forgotten and usually not +required when working on small programs (although it can be of great +value occasionally). These objects could come from previous execution +of code, from interactive work in the console, or from convenience +imports such as ``from pylab import *`` (Spyder may do some of those +convenience imports automatically). + +This visibility of objects in the console name space to the +code we execute may also result in coding mistakes if the code +inadvertently relies on these objects. + +Here is an example: imagine that + +* we run the code ``hello.py``. Subsequently, the variable ``i`` + is known in the console as a global variable. + +* we edit the ``hello.py`` source and accidentally delete the line + ``i = 42`` + +* we execute the buffer containing ``hello.py`` again. At this + point, the call of ``hello(i)`` will *not* fail because the + console has an object of name ``i`` defined, although this is + not defined in the source of ``hello.py``. + +At this point, we could save ``hello.py`` and (falsely) think it +would execute correctly. However, running it in a new (I)Python +console session (or via ``python hello.py`` in a terminal, say) +would result in an error, because ``i`` is not defined. + +The problem arises because the code makes use of an object (here +``i``) without creating it. This also affects importing of modules: if +we had imported ``pylab`` at the IPython prompt, then our program will +see that when executed in this IPython console session. + +To learn how we can double check that our code does not depend on such +existing objects, see `How to double check your code executes correctly "on +its own"`_ . + +Execute in new dedicated Python console +--------------------------------------- + +Choosing ``Execute in new dedicated Python console`` under ``Run +> Configure`` will start *a new Python console every time* the +``hello.py`` program is executed. The major advantage of this mode +over `Execute in current Python or IPython console`_ is that we +can be certain that there are no global objects defined in this +console which originate from debugging and repeated execution of +our code: every time we run the code in the editor, the python +console in which the code runs is restarted. + +This is a safe option, but provides less flexibility and cannot use +the IPython console. + +How to double check your code executes correctly "on its own" +------------------------------------------------------------- + +Assuming you have chosen for your code to +`Execute in current Python or IPython console`_, +then you have two options to check that your code does work on its own +(i.e. it does not depend on undefined variables, unimported modules +and commands etc.) + +(i) Switch from `Execute in current Python or IPython console`_ + to `Execute in new dedicated Python console`_, + and execute the code in the editor in this dedicated Python console. + + Alternatively, if you want to stay with the current IPython + console, you can + +(ii) Use IPython's magic ``%reset`` command which will remove all + objects (such as ``i`` in the example above) from the current name + space, and then execute the code in the editor. + +Recommendation +-------------- + +My recommendation for beginners would be to +`Execute in current Python or IPython console`_, *and* +to choose the IPython console for this. + +Once you have completed a piece of code, double check that it executes +independently using one of the options explained in +`How to double check your code executes correctly "on its own"`_\ . + + + +Other observations +################## + +Multiple files +-------------- + +When multiple files are opened in the Editor, the +corresponding tabs at the top of the window area are arranged in +alphabetical order of the filename from left to right. + +On the left of the tabs, there is as icon that shows ``Browse tabs`` +if the mouse hovers over it. It is useful to jump to a particular +file directly, if many files are open. + +Environment variables +--------------------- + +Environment variables can be displayed from the Python Console window (bottom +right window in default layout). Click on the ``Options`` icon (the tooltip is +``Options``), then select ``Environment variables``. + +Reset all customization +----------------------- + +All customization saved on disk can be reset by calling Spyder from +the command line with the switch ``--reset``, i.e. a command like +``spyder --reset``. + +Objects in the variable explorer +-------------------------------- + +Right-clicking on arrays in the variable explorer gives options to +plot and analyze these further. + +Double clicking on a dictionary object opens a new window that +displays the dictionary nicely. + +You can also show and edit the contents of numpy arrays, lists, numbers +and strings. + + + +Documentation string formatting +############################### + +If you want to add documentation for the code you are developing, we recommend +you to write documentation strings (or *docstrings*) for it, using a special +format called reStructuredText (`quick reference +`__). This format +also needs to follow a set of conventions called the `Numpydoc standard +`__ + +If you follow those guidelines, you can obtain beautifully formatted docstrings +in Spyder. + +For example, to get an ``average()`` function look like this in the +Spyder Help pane: + +.. image:: images/spyder-nice-docstring-rendering.png + :align: center + +you need to format the documentation string as follows + +.. code-block:: python + + def average(a, b): + """ + Given two numbers a and b, return their average value. + + Parameters + ---------- + a : number + A number + b : number + Another number + + Returns + ------- + res : number + The average of a and b, computed using 0.5*(a + b) + + Example + ------- + >>> average(5, 10) + 7.5 + + """ + + return (a + b) * 0.5 + +What matters here, is that the word ``Parameters`` is used, and +underlined. The line ``a : number`` shows us that the type of the +parameter ``a`` is ``number``. In the next line, which is indented, we +can write a more extended explanation of what this variable represents, +what conditions the allowed types have to fulfill, etc. + +The same for all Parameters, and also for the returned value. + +Often it is a good idea to include an example too, as shown. + + + +Debugging +######### + +Line by line step execution of code +----------------------------------- + +Activating the debug mode (with the ``Debug > Debug`` menu option or Ctrl+F5) +starts the Python debugger (Pdb) if the Python console is active, or the IPython +debugger (ipdb) if the IPython console is active. After doing that, the +Editor pane will highlight the line that is about to be executed, and the +Variable Explorer will display variables in the current context of the point +of program execution. (It only displays 'numerical' and array type of variables, +i.e. not function or class objects) + +After entering debug mode, you can execute the code line by line using the +``Step`` button of the Debug toolbar: + +.. image:: images/debug-step-over.png + :align: center + +or the shortcut Ctrl+F10. You can also inspect how a particular function is +working by stepping into it with the ``Step into`` button + +.. image:: images/debug-step-in.png + :align: center + +or the shortcut Ctrl+F11. Finally, to get out of a function and continue with +the next line you need to use the ``Step return`` button + +.. image:: images/debug-step-out.png + :align: center + +or the shortcut Ctrl+F12. + +If you prefer to inspect your program at a specific point, you need to insert a +*breakpoint* by pressing F12 on the line on which you want to stop. After +that a red dot will be placed next to the line and you can press the ``Continue`` +button + +.. image:: images/debug-continue.png + :align: center + +(after entering debug mode) to stop the execution at that line. + +.. note:: + + You can also control the debugging process by issuing these commands in the + console prompt: + + * ``n`` to move to the Next statement. + + * ``s`` to Step into the current statement. If this is a function + call, step into that function. + + * ``r`` to complete all statements in the current function and Return + from that function before returning control. + + * ``p`` to print values of variables, for example ``p x`` will print the + value of the variable ``x``. + +At the debugger prompt, you can also *change* values of variables. For +example, to modify a variable ``x`` at the IPython debugger prompt, you can say +``ipdb > x = 42`` and the debugger will carry on with ``x`` being bound to ``42``. +You can also call functions, and do many others things. Try this example:: + + def demo(x): + for i in range(5): + print("i={}, x={}".format(i, x)) + x = x + 1 + + demo(0) + +If we execute this (``Run > Run``), we should see the output:: + + i=0, x=0 + i=1, x=1 + i=2, x=2 + i=3, x=3 + i=4, x=4 + +Now execute this using the debugger (``Debug > Debug``), press the +``Step button`` until the highlighted line reaches the ``demo(0)`` +function call, then press the ``Step into`` to inspect this function. +Keep pressing the ``Step button`` to execute the next lines. Then, +modify ``x`` by typing ``x=10`` in the debugger prompt. You see x +changing in the Variable Explorer. You should also see ``x`` changing +when its value is printed as part of the ``demo()`` function. (The +printed output appears between your debugger commands and responses.) + +This debugging ability to execute code line by line, to inspect variables as +they change, and to modify them manually is a powerful tool to +understand what a piece of code is doing (and to correct it if desired). + +To leave the debugging mode, you can type ``exit`` or select from the +menu ``Debug > Debugging Control > Exit`` + +Debugging once an exception has occurred with IPython +----------------------------------------------------- + +In the IPython console, we can call ``%debug`` +straight after an exception has been raised: this will start the +IPython debug mode, which allows inspection of local variables at the +point where the exception occurred as described above. This is a lot +more efficient than adding ``print`` statements to the code an +running it again. + +If you use this, you may also want to use the commands ``up`` +(i.e. press ``u`` at the debugger) and ``down`` (i.e. press ``d``) which +navigate the inspection point up and down the stack. (Up the stack means +to the functions that have called the current function; down is the +opposite direction.) + + + +Plotting +######## + +Plotting with the IPython console +--------------------------------- + +Assuming we use an IPython console with version >= 1.0.0, we can +decide whether figures created with matplotlib/pylab will show + +1. *inline*, i.e. inside the IPython console, or whether they should + +2. appear inside a new window. + +Option 1 is convenient to save a record of the interactive session +(section `Shortcuts for useful functions`_ lists a shortcut to save +the IPython console to an html file). + +Option 2 allows to interactively zoom into the figure, manipulate it a little, +and save the figure to different file formats via a menu the window it +contains has. + +The command to get the figures to appear *inline* in the IPython +console is:: + + In [3]: %matplotlib inline + +The command to get figures appear in their own window (which +technically is a Qt window) is:: + + In [4]: %matplotlib qt + +The Spyder preferences can be used to customize the default behavior +(in particular ``Preferences > IPython Console > Graphics > +Activate Support`` to switch into inline plotting). + +Here are two lines you can use to quickly create a plot and test +this:: + + In [5]: import pylab + In [6]: pylab.plot(range(10), 'o') + + +Plotting with the Python console +-------------------------------- + +If we use the Python console, all plots will appear in a new window +(there is no way of making it appear inline inside the Python +console - this only works for the IPython Console). + +Here is a brief example that you can use to create and display a +plot:: + + >>> import pylab + >>> pylab.plot(range(10), 'o') + +If you execute your code in a dedicated console, you need to use +matplotlib's or pylab's ``show()`` command in your code to make a plot +appear, like this: ``pylab.show()``. + +Note that the ``show()`` command will bind the focus to new window +that has appeared, and that you will need to close that window before +Spyder can accept any further commands or respond to interaction. If +you cannot see the new window, check whether it may have appeared behind +the Spyder window, or be partly hidden. + + + +Historical note +############### + +This tutorial is based on `notes +`__ +by `Hans Fangohr `__, that are +used at the `University of Southampton `__ to +`teach Python for computational modelling +`__ to +undergraduate engineers and postgraduate PhD students for the +`Next Generation Computational Modelling `__ +doctoral training centre. diff -Nru spyder-2.3.8+dfsg1/spyder/utils/icon_manager.py spyder-3.0.2+dfsg1/spyder/utils/icon_manager.py --- spyder-2.3.8+dfsg1/spyder/utils/icon_manager.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/icon_manager.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,267 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +# Standard library imports +import os.path as osp + +# Third party imports +from qtpy.QtGui import QIcon +from qtpy.QtWidgets import QStyle, QWidget + +# Local imports +from spyder.config.base import get_image_path +from spyder.config.main import CONF +import qtawesome as qta + + +_resource = { + 'directory': osp.join(osp.dirname(osp.realpath(__file__)), '../fonts'), + 'loaded': False, +} + +_qtaargs = { + 'log': [('fa.file-text-o',), {}], + 'configure': [('fa.wrench',), {}], + 'bold': [('fa.bold',), {}], + 'italic': [('fa.italic',), {}], + 'genprefs': [('fa.cogs',), {}], + 'exit': [('fa.power-off',), {}], + 'run_small': [('fa.play',), {'color': 'green'}], + 'stop': [('fa.stop',), {'color': 'darkred'}], + 'syspath': [('fa.cogs',), {}], + 'font': [('fa.font',), {}], + 'keyboard': [('fa.keyboard-o',), {}], + 'eyedropper': [('fa.eyedropper',), {}], + 'tooloptions': [('fa.cog',), {'color': '#333333'}], + 'edit24': [('fa.edit',), {}], + 'edit': [('fa.edit',), {}], + 'filenew': [('fa.file-o',), {}], + 'fileopen': [('fa.folder-open',), {}], + 'revert': [('fa.undo',), {}], + 'filesave': [('fa.save',), {}], + 'save_all': [('fa.save', 'fa.save'), {'options': [{'offset': (-0.2, -0.2), 'scale_factor': 0.6}, {'offset': (0.2, 0.2), 'scale_factor': 0.6}]}], + 'filesaveas': [('fa.save', 'fa.pencil'), {'options': [{'offset': (-0.2, -0.2), 'scale_factor': 0.6}, {'offset': (0.2, 0.2), 'scale_factor': 0.6}]}], + 'print': [('fa.print',), {}], + 'fileclose': [('fa.close',), {}], + 'filecloseall': [('fa.close', 'fa.close', 'fa.close'), {'options': [{'scale_factor': 0.6, 'offset': (0.3, -0.3)}, {'scale_factor': 0.6, 'offset': (-0.3, -0.3)}, {'scale_factor': 0.6, 'offset': (0.3, 0.3)}]}], + 'breakpoint_big': [('fa.circle',), {'color': 'darkred'} ], + 'breakpoint_cond_big': [('fa.question-circle',), {'color': 'darkred'},], + 'debug': [('spyder.debug',), {'color': '#3775a9'}], + 'arrow-step-over': [('spyder.step-forward',), {'color': '#3775a9'}], + 'arrow-continue': [('spyder.continue',), {'color': '#3775a9'}], + 'arrow-step-in': [('spyder.step-into',), {'color': '#3775a9'}], + 'arrow-step-out': [('spyder.step-out',), {'color': '#3775a9'}], + 'stop_debug': [('fa.stop',), {'color': '#3775a9'}], + 'run': [('fa.play',), {'color': 'green'}], + 'run_settings': [('fa.wrench', 'fa.play'), {'options': [{'offset':(0.0, -0.1)}, {'offset': (0.2, 0.125), 'color': 'green', 'scale_factor': 0.8}]}], + 'run_again': [('fa.repeat', 'fa.play'), {'options': [{'offset':(0.0, -0.1)}, {'offset': (0.2, 0.125), 'color': 'green', 'scale_factor': 0.8}]}], + 'run_selection': [('spyder.run-selection',), {}], + 'run_cell': [('spyder.cell-code', 'spyder.cell-border', 'spyder.cell-play'), + {'options': [{'color': '#fff683'}, {}, {'color': 'green'}]}], + 'run_cell_advance': [('spyder.cell-code', 'spyder.cell-border', 'spyder.cell-play', 'spyder.cell-next'), + {'options': [{'color': '#fff683'}, {}, {'color': 'green'}, {'color': 'red'}]}], + 'todo_list': [('fa.th-list', 'fa.check'), {'options': [{'color': '#999999'}, {'offset': (0.0, 0.2), 'color': '#3775a9', 'color_disabled': '#748fa6'}]}], + 'wng_list': [('fa.th-list', 'fa.warning'), {'options': [{'color': '#999999'}, {'offset': (0.0, 0.2), 'scale_factor': 0.75, 'color': 'orange', 'color_disabled': '#face7e'}]}], + 'prev_wng': [('fa.arrow-left', 'fa.warning'), {'options': [{'color': '#999999'}, {'offset': (0.0, 0.2), 'scale_factor': 0.75, 'color': 'orange', 'color_disabled': '#face7e'}]}], + 'next_wng': [('fa.arrow-right', 'fa.warning'), {'options': [{'color': '999999'}, {'offset': (0.0, 0.2), 'scale_factor': 0.75, 'color': 'orange', 'color_disabled': '#face7e'}]}], + 'last_edit_location': [('fa.caret-up',), {}], + 'prev_cursor': [('fa.hand-o-left',), {}], + 'next_cursor': [('fa.hand-o-right',), {}], + 'comment': [('fa.comment',), {}], + 'indent': [('fa.indent',), {}], + 'unindent': [('fa.outdent',), {}], + 'gotoline': [('fa.sort-numeric-asc',), {}], + 'error': [('fa.times-circle',), {'color': 'darkred'}], + 'warning': [('fa.warning',), {'color': 'orange'}], + 'todo': [('fa.check',), {'color': '#3775a9'}], + 'ipython_console': [('spyder.ipython-logo-alt',), {}], + 'ipython_console_t': [('spyder.ipython-logo-alt',), {'color':'gray'}], + 'python': [('spyder.python-logo-up', 'spyder.python-logo-down'), {'options': [{'color': '#3775a9'}, {'color': '#ffd444'}]}], + 'python_t': [('spyder.python-logo',), {'color':'gray'}], + 'pythonpath': [('spyder.python-logo-up', 'spyder.python-logo-down'), {'options': [{'color': '#3775a9'}, {'color': '#ffd444'}]}], + 'terminated': [('fa.circle',), {}], + 'cmdprompt': [('fa.terminal',), {}], + 'cmdprompt_t': [('fa.terminal',), {'color':'gray'}], + 'console': [('spyder.python-logo-up', 'spyder.python-logo-down'), {'options': [{'color': '#3775a9'}, {'color': '#ffd444'}]}], + 'findf': [('fa.file-o', 'fa.search'), {'options': [{'scale_factor': 1.0}, {'scale_factor': 0.6}]}], + 'history24': [('fa.history',), {}], + 'history': [('fa.history',), {}], + 'help': [('fa.question-circle',), {}], + 'lock': [('fa.lock',), {}], + 'lock_open': [('fa.unlock-alt',), {}], + 'outline_explorer': [('spyder.treeview',), {}], + 'project_expanded': [('fa.plus',), {}], + 'dictedit': [('fa.th-list',), {}], + 'previous': [('fa.arrow-left',), {}], + 'next': [('fa.arrow-right',), {}], + 'set_workdir': [('fa.check',), {}], + 'up': [('fa.arrow-up',), {}], + 'down': [('fa.arrow-down',), {}], + 'filesaveas2': [('fa.save', 'fa.close'), {'options': [{'scale_factor': 0.8, 'offset': (-0.1, -0.1)}, {'offset': (0.2, 0.2)}]}], # save_session_action + 'spyder': [('spyder.spyder-logo-background', 'spyder.spyder-logo-web', 'spyder.spyder-logo-snake'), {'options': [{'color': '#414141'}, {'color': '#fafafa'}, {'color': '#ee0000'}]}], + 'find': [('fa.search',), {}], + 'findnext': [('fa.search', 'fa.long-arrow-down'), {'options':[{'scale_factor': 0.6, 'offset': (0.3, 0.0)}, {'offset': (-0.3, 0.0)}]}], + 'findprevious': [('fa.search', 'fa.long-arrow-up'), {'options':[{'scale_factor': 0.6, 'offset': (0.3, 0.0)}, {'offset': (-0.3, 0.0)}]}], + 'replace': [('fa.exchange',), {}], + 'undo': [('fa.undo',), {}], + 'redo': [('fa.repeat',), {}], + 'restart': [('fa.repeat',), {'çolor': '#3775a9'}], + 'editcopy': [('fa.copy',), {}], + 'editcut': [('fa.scissors',), {}], + 'editpaste': [('fa.clipboard',), {}], + 'editdelete': [('fa.eraser',), {}], + 'editclear': [('fa.times',), {}], + 'selectall': [('spyder.text-select-all',), {}], + 'pythonpath_mgr': [('spyder.python-logo-up', 'spyder.python-logo-down'), {'options': [{'color': '#3775a9'}, {'color': '#ffd444'}]}], + 'exit': [('fa.power-off',), {'color': 'darkred'}], + 'advanced': [('fa.gear',), {}], + 'bug': [('fa.bug',), {}], + 'maximize': [('spyder.maximize-pane',), {}], + 'unmaximize': [('spyder.minimize-pane',), {}], + 'window_nofullscreen': [('spyder.inward',), {}], + 'window_fullscreen': [('fa.arrows-alt',), {}], + 'MessageBoxWarning': [('fa.warning',), {}], + 'arredit': [('fa.table',), {}], + 'zoom_out': [('fa.search-minus',), {}], + 'zoom_in': [('fa.search-plus',), {}], + 'home': [('fa.home',), {}], + 'find': [('fa.search',), {}], + 'plot': [('fa.line-chart',), {}], + 'hist': [('fa.bar-chart',), {}], + 'imshow': [('fa.image',), {}], + 'insert': [('fa.sign-in',), {}], + 'rename': [('fa.pencil',), {}], + 'edit_add': [('fa.plus',), {}], + 'edit_remove': [('fa.minus',), {}], + 'browse_tab': [('fa.folder-o',), {}], + 'filelist': [('fa.list',), {}], + 'newwindow': [('spyder.window',), {}], + 'versplit': [('spyder.rows',), {}], + 'horsplit': [('fa.columns',), {}], + 'close_panel': [('fa.close',), {}], + 'class': [('spyder.circle-letter-c',), {'color':'#3775a9'}], + 'private2': [('spyder.circle-underscore',), {'color':'#e69c9c'}], + 'private1': [('spyder.circle-underscore',), {'color':'#e69c9c'}], + 'method': [('spyder.circle-letter-m',), {'color':'#7ea67e'}], + 'function': [('spyder.circle-letter-f',), {'color':'orange'}], + 'blockcomment': [('spyder.circle-hash',), {'color':'grey'}], + 'cell': [('spyder.circle-percent',), {'color':'red'}], + 'fromcursor': [('fa.hand-o-right',), {}], + 'filter': [('fa.filter',), {}], + 'folder_new': [('fa.folder-o', 'fa.plus'), {'options': [{}, {'scale_factor': 0.5, 'offset': (0.0, 0.1)}]}], + 'package_new': [('fa.folder-o', 'spyder.python-logo'), {'options': [{'offset': (0.0, -0.125)}, {'offset': (0.0, 0.125)}]}], + 'vcs_commit': [('fa.check',), {'color': 'green'}], + 'vcs_browse': [('fa.search',), {'color': 'green'}], + 'kill': [('fa.warning',), {}], + 'reload': [('fa.repeat',), {}], + 'auto_reload': [('fa.repeat', 'fa.clock-o'), {'options': [{'scale_factor': 0.75, 'offset': (-0.1, -0.1)}, {'scale_factor': 0.5, 'offset': (0.25, 0.25)}]}], + 'fileimport': [('fa.download',), {}], + 'environ': [('fa.th-list',), {}], + 'options_less': [('fa.minus-square',), {}], + 'options_more': [('fa.plus-square',), {}], + 'ArrowDown': [('fa.arrow-circle-down',), {}], + 'ArrowUp': [('fa.arrow-circle-up',), {}], + 'ArrowBack': [('fa.arrow-circle-left',), {}], + 'ArrowForward': [('fa.arrow-circle-right',), {}], + 'DialogApplyButton': [('fa.check',), {}], + 'DialogCloseButton': [('fa.close',), {}], + 'DirClosedIcon': [('fa.folder-o',), {}], + 'DialogHelpButton': [('fa.life-ring',), {'color': 'darkred'}], + 'MessageBoxInformation': [('fa.info',), {'color': '3775a9'}], + 'DirOpenIcon': [('fa.folder-open',), {}], + 'FileIcon': [('fa.file-o',), {}], + 'project': [('fa.folder-open-o',), {}], + 'DriveHDIcon': [('fa.hdd-o',), {}], + 'arrow': [('fa.arrow-right',), {}], + 'collapse': [('spyder.inward',), {}], + 'expand': [('fa.arrows-alt',), {}], + 'restore': [('fa.level-up',), {}], + 'collapse_selection': [('fa.minus-square-o',), {}], + 'expand_selection': [('fa.plus-square-o',), {}], + 'copywop': [('fa.terminal',), {}], + 'editpaste': [('fa.paste',), {}], + 'editcopy': [('fa.copy',), {}], + 'edit': [('fa.edit',), {}], + 'convention': [('spyder.circle-letter-c',), {'color':'#3775a9'}], + 'refactor': [('spyder.circle-letter-r',), {'color':'#3775a9'}], + '2uparrow': [('fa.angle-double-up',), {}], + '1uparrow': [('fa.angle-up',), {}], + '2downarrow': [('fa.angle-double-down',), {}], + '1downarrow': [('fa.angle-down',), {}], + 'attribute': [('spyder.circle-letter-a',), {'color': 'magenta'}], + 'module': [('spyder.circle-letter-m',), {'color': '#daa520'}], + 'no_match': [('fa.circle',), {'color': 'gray'}], + 'no_match': [('fa.circle',), {'color': 'gray'}], + # --- Third party plugins ------------------------------------------------ + 'profiler': [('fa.clock-o',), {}], + 'pylint': [('fa.search', 'fa.check'), {'options': [{}, {'offset': (0.125, 0.125), 'color': 'orange'}]}], + 'condapackages': [('fa.archive',), {}], + 'spyder.example': [('fa.eye',), {}], + 'spyder.autopep8': [('fa.eye',), {}], + 'spyder.memory_profiler': [('fa.eye',), {}], + 'spyder.line_profiler': [('fa.eye',), {}], +} + + +def get_std_icon(name, size=None): + """Get standard platform icon + Call 'show_std_icons()' for details""" + if not name.startswith('SP_'): + name = 'SP_' + name + icon = QWidget().style().standardIcon(getattr(QStyle, name)) + if size is None: + return icon + else: + return QIcon(icon.pixmap(size, size)) + + +def get_icon(name, default=None, resample=False): + """Return image inside a QIcon object. + + default: default image name or icon + resample: if True, manually resample icon pixmaps for usual sizes + (16, 24, 32, 48, 96, 128, 256). This is recommended for QMainWindow icons + created from SVG images on non-Windows platforms due to a Qt bug (see + Issue 1314). + """ + + icon_path = get_image_path(name, default=None) + if icon_path is not None: + icon = QIcon(icon_path) + elif isinstance(default, QIcon): + icon = default + elif default is None: + try: + icon = get_std_icon(name[:-4]) + except AttributeError: + icon = QIcon(get_image_path(name, default)) + else: + icon = QIcon(get_image_path(name, default)) + if resample: + icon0 = QIcon() + for size in (16, 24, 32, 48, 96, 128, 256, 512): + icon0.addPixmap(icon.pixmap(size, size)) + return icon0 + else: + return icon + + +def icon(name, resample=False, icon_path=None): + theme = CONF.get('main', 'icon_theme') + if theme == 'spyder 3': + if not _resource['loaded']: + qta.load_font('spyder', 'spyder.ttf', 'spyder-charmap.json', + directory=_resource['directory']) + _resource['loaded'] = True + args, kwargs = _qtaargs[name] + return qta.icon(*args, **kwargs) + elif theme == 'spyder 2': + icon = get_icon(name + '.png', resample=resample) + if icon_path: + icon_path = osp.join(icon_path, name + '.png') + if osp.isfile(icon_path): + icon = QIcon(icon_path) + return icon if icon is not None else QIcon() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/__init__.py spyder-3.0.2+dfsg1/spyder/utils/__init__.py --- spyder-2.3.8+dfsg1/spyder/utils/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.utils +============ + +Spyder utilities +""" diff -Nru spyder-2.3.8+dfsg1/spyder/utils/inputhooks.py spyder-3.0.2+dfsg1/spyder/utils/inputhooks.py --- spyder-2.3.8+dfsg1/spyder/utils/inputhooks.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/inputhooks.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +""" +Inputhook management for GUI event loop integration + +Copyright (C) The IPython Development Team +Distributed under the terms of the modified BSD license +""" + +# Stdlib imports +import ctypes +import os +import sys + +QT_API = os.environ["QT_API"] + +# Qt imports +if QT_API == 'pyqt5': + from PyQt5 import QtCore +elif QT_API == 'pyqt': + from PyQt4 import QtCore +elif QT_API == 'pyside': + from PySide import QtCore + + +#----------------------------------------------------------------------------- +# Utilities +#----------------------------------------------------------------------------- +def _stdin_ready_posix(): + """Return True if there's something to read on stdin (posix version).""" + infds, outfds, erfds = select.select([sys.stdin],[],[],0) + return bool(infds) + +def _stdin_ready_nt(): + """Return True if there's something to read on stdin (nt version).""" + return msvcrt.kbhit() + +def _stdin_ready_other(): + """Return True, assuming there's something to read on stdin.""" + return True + + +def _ignore_CTRL_C_posix(): + """Ignore CTRL+C (SIGINT).""" + signal.signal(signal.SIGINT, signal.SIG_IGN) + +def _allow_CTRL_C_posix(): + """Take CTRL+C into account (SIGINT).""" + signal.signal(signal.SIGINT, signal.default_int_handler) + +def _ignore_CTRL_C_other(): + """Ignore CTRL+C (not implemented).""" + pass + +def _allow_CTRL_C_other(): + """Take CTRL+C into account (not implemented).""" + pass + + +if os.name == 'posix': + import select + import signal + stdin_ready = _stdin_ready_posix + ignore_CTRL_C = _ignore_CTRL_C_posix + allow_CTRL_C = _allow_CTRL_C_posix +elif os.name == 'nt': + import msvcrt + stdin_ready = _stdin_ready_nt + ignore_CTRL_C = _ignore_CTRL_C_other + allow_CTRL_C = _allow_CTRL_C_other +else: + stdin_ready = _stdin_ready_other + ignore_CTRL_C = _ignore_CTRL_C_other + allow_CTRL_C = _allow_CTRL_C_other + + +def clear_inputhook(): + """Set PyOS_InputHook to NULL and return the previous one""" + pyos_inputhook_ptr = ctypes.c_void_p.in_dll(ctypes.pythonapi, + "PyOS_InputHook") + pyos_inputhook_ptr.value = ctypes.c_void_p(None).value + allow_CTRL_C() + +def get_pyos_inputhook(): + """Return the current PyOS_InputHook as a ctypes.c_void_p.""" + return ctypes.c_void_p.in_dll(ctypes.pythonapi, "PyOS_InputHook") + +def set_pyft_callback(callback): + callback = ctypes.PYFUNCTYPE(ctypes.c_int)(callback) + return callback + +def remove_pyqt_inputhook(): + if QT_API == 'pyqt' or QT_API == 'pyqt5': + QtCore.pyqtRemoveInputHook() + else: + pass + + +#------------------------------------------------------------------------------ +# Input hooks +#------------------------------------------------------------------------------ +def qt4(): + """PyOS_InputHook python hook for Qt4. + + Process pending Qt events and if there's no pending keyboard + input, spend a short slice of time (50ms) running the Qt event + loop. + + As a Python ctypes callback can't raise an exception, we catch + the KeyboardInterrupt and temporarily deactivate the hook, + which will let a *second* CTRL+C be processed normally and go + back to a clean prompt line. + """ + try: + allow_CTRL_C() + app = QtCore.QCoreApplication.instance() + if not app: + return 0 + app.processEvents(QtCore.QEventLoop.AllEvents, 300) + if not stdin_ready(): + # Generally a program would run QCoreApplication::exec() + # from main() to enter and process the Qt event loop until + # quit() or exit() is called and the program terminates. + # + # For our input hook integration, we need to repeatedly + # enter and process the Qt event loop for only a short + # amount of time (say 50ms) to ensure that Python stays + # responsive to other user inputs. + # + # A naive approach would be to repeatedly call + # QCoreApplication::exec(), using a timer to quit after a + # short amount of time. Unfortunately, QCoreApplication + # emits an aboutToQuit signal before stopping, which has + # the undesirable effect of closing all modal windows. + # + # To work around this problem, we instead create a + # QEventLoop and call QEventLoop::exec(). Other than + # setting some state variables which do not seem to be + # used anywhere, the only thing QCoreApplication adds is + # the aboutToQuit signal which is precisely what we are + # trying to avoid. + timer = QtCore.QTimer() + event_loop = QtCore.QEventLoop() + timer.timeout.connect(event_loop.quit) + while not stdin_ready(): + timer.start(50) + event_loop.exec_() + timer.stop() + except KeyboardInterrupt: + print("\nKeyboardInterrupt - Press Enter for new prompt") + except: # NO exceptions are allowed to escape from a ctypes callback + ignore_CTRL_C() + from traceback import print_exc + print_exc() + print("Got exception from inputhook, unregistering.") + clear_inputhook() + finally: + allow_CTRL_C() + return 0 diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/fallback_plugin.py spyder-3.0.2+dfsg1/spyder/utils/introspection/fallback_plugin.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/fallback_plugin.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/fallback_plugin.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,399 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Introspection utilities used by Spyder +""" + +from __future__ import print_function +import imp +import os +import os.path as osp +import re +import time + +from pygments.token import Token + +from spyder.utils.debug import log_dt +from spyder.utils import sourcecode, encoding +from spyder.utils.introspection.manager import ( + DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin) +from spyder.utils.introspection.utils import ( + get_parent_until, memoize, find_lexer_for_filename, get_keywords) + + +class FallbackPlugin(IntrospectionPlugin): + """Basic Introspection Plugin for Spyder""" + + # ---- IntrospectionPlugin API -------------------------------------------- + name = 'fallback' + + def get_completions(self, info): + """Return a list of (completion, type) tuples + + Simple completion based on python-like identifiers and whitespace + """ + if not info['obj']: + return + items = [] + obj = info['obj'] + if info['context']: + lexer = find_lexer_for_filename(info['filename']) + # get a list of token matches for the current object + tokens = lexer.get_tokens(info['source_code']) + for (context, token) in tokens: + token = token.strip() + if (context in info['context'] and + token.startswith(obj) and + obj != token): + items.append(token) + # add in keywords if not in a string + if context not in Token.Literal.String: + try: + keywords = get_keywords(lexer) + items.extend(k for k in keywords if k.startswith(obj)) + except Exception: + pass + else: + tokens = set(re.findall(info['id_regex'], info['source_code'])) + items = [item for item in tokens if + item.startswith(obj) and len(item) > len(obj)] + if '.' in obj: + start = obj.rfind('.') + 1 + else: + start = 0 + + items = [i[start:len(obj)] + i[len(obj):].split('.')[0] + for i in items] + # get path completions + # get last word back to a space or a quote character + match = re.search('''[ "\']([\w\.\\\\/]+)\Z''', info['line']) + if match: + items += _complete_path(match.groups()[0]) + return [(i, '') for i in sorted(items)] + + def get_definition(self, info): + """ + Find the definition for an object within a set of source code + + This is used to find the path of python-like modules + (e.g. cython and enaml) for a goto definition + """ + if not info['is_python_like']: + return + token = info['obj'] + lines = info['lines'] + source_code = info['source_code'] + filename = info['filename'] + + line_nr = None + if token is None: + return + if '.' in token: + token = token.split('.')[-1] + + line_nr = get_definition_with_regex(source_code, token, + len(lines)) + if line_nr is None: + return + line = info['line'] + exts = python_like_exts() + if not osp.splitext(filename)[-1] in exts: + return filename, line_nr + if line.startswith('import ') or line.startswith('from '): + alt_path = osp.dirname(filename) + source_file = python_like_mod_finder(line, alt_path=alt_path, + stop_token=token) + if (not source_file or + not osp.splitext(source_file)[-1] in exts): + line_nr = get_definition_with_regex(source_code, token, + line_nr) + return filename, line_nr + mod_name = osp.basename(source_file).split('.')[0] + if mod_name == token or mod_name == '__init__': + return source_file, 1 + else: + with open(filename, 'rb') as fid: + code = fid.read() + code = encoding.decode(code)[0] + line_nr = get_definition_with_regex(code, token) + + return filename, line_nr + + def get_info(self, info): + """Get a formatted calltip and docstring from Fallback""" + if info['docstring']: + if info['filename']: + filename = os.path.basename(info['filename']) + filename = os.path.splitext(filename)[0] + else: + filename = '' + resp = dict(docstring=info['docstring'], + name=filename, + note='', + argspec='', + calltip=None) + return resp + + +@memoize +def python_like_mod_finder(import_line, alt_path=None, + stop_token=None): + """ + Locate a module path based on an import line in an python-like file + + import_line is the line of source code containing the import + alt_path specifies an alternate base path for the module + stop_token specifies the desired name to stop on + + This is used to a find the path to python-like modules + (e.g. cython and enaml) for a goto definition. + """ + if stop_token and '.' in stop_token: + stop_token = stop_token.split('.')[-1] + tokens = re.split(r'\W', import_line) + if tokens[0] in ['from', 'import']: + # find the base location + try: + _, path, _ = imp.find_module(tokens[1]) + except ImportError: + if alt_path: + path = osp.join(alt_path, tokens[1]) + else: + path = None + if path: + path = osp.realpath(path) + if not tokens[1] == stop_token: + for part in tokens[2:]: + if part in ['import', 'cimport', 'as']: + break + path = osp.join(path, part) + if part == stop_token: + break + # from package import module + if stop_token and not stop_token in path: + for ext in python_like_exts(): + fname = '%s%s' % (stop_token, ext) + if osp.exists(osp.join(path, fname)): + return osp.join(path, fname) + # from module import name + for ext in python_like_exts(): + fname = '%s%s' % (path, ext) + if osp.exists(fname): + return fname + # if it is a file, return it + if osp.exists(path) and not osp.isdir(path): + return path + # default to the package file + path = osp.join(path, '__init__.py') + if osp.exists(path): + return path + + +def get_definition_with_regex(source, token, start_line=-1): + """ + Find the definition of an object within a source closest to a given line + """ + if not token: + return None + if DEBUG_EDITOR: + t0 = time.time() + patterns = [ # python / cython keyword definitions + '^c?import.*\W{0}{1}', + 'from.*\W{0}\W.*c?import ', + 'from .* c?import.*\W{0}{1}', + 'class\s*{0}{1}', + 'c?p?def[^=]*\W{0}{1}', + 'cdef.*\[.*\].*\W{0}{1}', + # enaml keyword definitions + 'enamldef.*\W{0}{1}', + 'attr.*\W{0}{1}', + 'event.*\W{0}{1}', + 'id\s*:.*\W{0}{1}'] + + matches = get_matches(patterns, source, token, start_line) + + if not matches: + patterns = ['.*\Wself.{0}{1}[^=!<>]*=[^=]', + '.*\W{0}{1}[^=!<>]*=[^=]', + 'self.{0}{1}[^=!<>]*=[^=]', + '{0}{1}[^=!<>]*=[^=]'] + matches = get_matches(patterns, source, token, start_line) + # find the one closest to the start line (prefer before the start line) + if matches: + min_dist = len(source.splitlines()) + best_ind = 0 + for match in matches: + dist = abs(start_line - match) + if match <= start_line or not best_ind: + if dist < min_dist: + min_dist = dist + best_ind = match + if matches: + if DEBUG_EDITOR: + log_dt(LOG_FILENAME, 'regex definition match', t0) + return best_ind + else: + if DEBUG_EDITOR: + log_dt(LOG_FILENAME, 'regex definition failed match', t0) + return None + + +def get_matches(patterns, source, token, start_line): + patterns = [pattern.format(token, r'[^0-9a-zA-Z.[]') + for pattern in patterns] + pattern = re.compile('|^'.join(patterns)) + # add the trailing space to allow some regexes to match + lines = [line.strip() + ' ' for line in source.splitlines()] + if start_line == -1: + start_line = len(lines) + matches = [] + for (index, line) in enumerate(lines): + if re.match(pattern, line): + matches.append(index + 1) + return matches + + +def python_like_exts(): + """Return a list of all python-like extensions""" + exts = [] + for lang in sourcecode.PYTHON_LIKE_LANGUAGES: + exts.extend(list(sourcecode.ALL_LANGUAGES[lang])) + return ['.' + ext for ext in exts] + + +def all_editable_exts(): + """Return a list of all editable extensions""" + exts = [] + for (language, extensions) in sourcecode.ALL_LANGUAGES.items(): + exts.extend(list(extensions)) + return ['.' + ext for ext in exts] + + +def _listdir(root): + "List directory 'root' appending the path separator to subdirs." + res = [] + root = os.path.expanduser(root) + try: + for name in os.listdir(root): + path = os.path.join(root, name) + if os.path.isdir(path): + name += os.sep + res.append(name) + except: + pass # no need to report invalid paths + return res + + +def _complete_path(path=None): + """Perform completion of filesystem path. + http://stackoverflow.com/questions/5637124/tab-completion-in-pythons-raw-input + """ + if not path: + return _listdir('.') + dirname, rest = os.path.split(path) + tmp = dirname if dirname else '.' + res = [p for p in _listdir(tmp) if p.startswith(rest)] + # more than one match, or single match which does not exist (typo) + if len(res) > 1 or not os.path.exists(path): + return res + # resolved to a single directory, so return list of files below it + if os.path.isdir(path): + return [p for p in _listdir(path)] + # exact file match terminates this completion + return [path + ' '] + + +if __name__ == '__main__': + from spyder.utils.introspection.manager import CodeInfo + + p = FallbackPlugin() + + with open(__file__, 'rb') as fid: + code = fid.read().decode('utf-8') + code += '\nlog_dt' + + path, line = p.get_definition(CodeInfo('definition', code, len(code), + __file__, is_python_like=True)) + assert path.endswith('fallback_plugin.py') + + code += '\np.get_completions' + path, line = p.get_definition(CodeInfo('definition', code, len(code), + 'dummy.py', is_python_like=True)) + assert path == 'dummy.py' + assert 'def get_completions(' in code.splitlines()[line - 1] + + code += '\npython_like_mod_finder' + path, line = p.get_definition(CodeInfo('definition', code, len(code), + 'dummy.py', is_python_like=True)) + assert path == 'dummy.py' + # FIXME: we need to prioritize def over = + assert 'def python_like_mod_finder' in code.splitlines()[line - 1] + + code += 'python_like_mod_finder' + resp = p.get_definition(CodeInfo('definition', code, len(code), + 'dummy.py')) + assert resp is None + + code = """ + class Test(object): + def __init__(self): + self.foo = bar + + t = Test() + t.foo""" + path, line = p.get_definition(CodeInfo('definition', code, len(code), + 'dummy.py', is_python_like=True)) + assert line == 4 + + ext = python_like_exts() + assert '.py' in ext and '.pyx' in ext + + ext = all_editable_exts() + assert '.cpp' in ext and '.html' in ext + + path = get_parent_until(os.path.abspath(__file__)) + assert path == 'spyder.utils.introspection.fallback_plugin' + + line = 'from spyder.widgets.sourcecode.codeeditor import CodeEditor' + path = python_like_mod_finder(line) + assert path.endswith('codeeditor.py') + path = python_like_mod_finder(line, stop_token='sourcecode') + assert path.endswith('__init__.py') and 'sourcecode' in path + + path = osp.expanduser(r'~/.spyder2/temp.py') + if os.path.exists(path): + path = get_parent_until(path) + assert path == '.spyder2.temp', path + + code = 'import re\n\nre' + path, line = p.get_definition(CodeInfo('definition', code, len(code), + 'dummy.py', is_python_like=True)) + assert path == 'dummy.py' and line == 1 + + code = 'self.proxy.widget; self.p' + comp = p.get_completions(CodeInfo('completions', code, len(code), 'dummy.py')) + assert ('proxy', '') in comp, comp + + code = 'self.sigMessageReady.emit; self.s' + comp = p.get_completions(CodeInfo('completions', code, len(code), 'dummy.py')) + assert ('sigMessageReady', '') in comp + + code = 'bob = 1; bo' + comp = p.get_completions(CodeInfo('completions', code, len(code), 'dummy.m')) + assert ('bob', '') in comp + + code = 'functi' + comp = p.get_completions(CodeInfo('completions', code, len(code), 'dummy.sh')) + assert ('function', '') in comp, comp + + code = ''' +def test(a, b): + pass +test(1,''' + path, line = p.get_definition(CodeInfo('definition', code, len(code), + 'dummy.py', is_python_like=True)) + assert line == 2 diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/__init__.py spyder-3.0.2+dfsg1/spyder/utils/introspection/__init__.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Introspection utilities used by Spyder +""" diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/jedi_plugin.py spyder-3.0.2+dfsg1/spyder/utils/introspection/jedi_plugin.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/jedi_plugin.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/jedi_plugin.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,273 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Jedi Introspection Plugin +""" +import re +import os.path as osp +import sys +import time + +from spyder.config.base import debug_print +from spyder.utils import programs +from spyder.utils.debug import log_last_error, log_dt +from spyder.utils.dochelpers import getsignaturefromtext +from spyder.utils.introspection.manager import ( + DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin) +from spyder.utils.introspection.utils import get_parent_until +from spyder.utils.introspection.manager import JEDI_REQVER + +try: + import jedi +except ImportError: + jedi = None + + +class JediPlugin(IntrospectionPlugin): + """ + Jedi based introspection plugin for jedi + + Experimental Editor's code completion, go-to-definition and help + """ + + # ---- IntrospectionPlugin API -------------------------------------------- + name = 'jedi' + + def load_plugin(self): + """Load the Jedi introspection plugin""" + if not programs.is_module_installed('jedi', JEDI_REQVER): + raise ImportError('Requires Jedi %s' % JEDI_REQVER) + jedi.settings.case_insensitive_completion = False + for lib in ['numpy', 'matplotlib']: + jedi.preload_module(lib) + + def get_completions(self, info): + """Return a list of (completion, type) tuples""" + completions = self.get_jedi_object('completions', info) + completions = [(c.name, c.type) for c in completions] + debug_print(str(completions)[:100]) + return completions + + def get_info(self, info): + """ + Find the calltip and docs + + Returns a dict like the following: + {'note': 'Function of numpy.core.numeric...', + 'argspec': "(shape, dtype=None, order='C')' + 'docstring': 'Return an array of given...' + 'name': 'ones', + 'calltip': 'ones(shape, dtype=None, order='C')'} + """ + call_def = self.get_jedi_object('goto_definitions', info) + for cd in call_def: + if cd.doc and not cd.doc.rstrip().endswith(')'): + call_def = cd + break + else: + call_def = call_def[0] + name = call_def.name + if name is None: + return + if call_def.module_path: + mod_name = get_parent_until(call_def.module_path) + else: + mod_name = None + if not mod_name: + mod_name = call_def.module_name + if call_def.doc.startswith(name + '('): + calltip = getsignaturefromtext(call_def.doc, name) + argspec = calltip[calltip.find('('):] + docstring = call_def.doc[call_def.doc.find(')') + 3:] + elif '(' in call_def.doc.splitlines()[0]: + calltip = call_def.doc.splitlines()[0] + name = call_def.doc.split('(')[0] + docstring = call_def.doc[call_def.doc.find(')') + 3:] + argspec = calltip[calltip.find('('):] + else: + calltip = name + '(...)' + argspec = '()' + docstring = call_def.doc + if call_def.type == 'module': + note = 'Module %s' % mod_name + argspec = '' + calltip = name + elif call_def.type == 'class': + note = 'Class in %s module' % mod_name + elif call_def.doc.startswith('%s(self' % name): + class_name = call_def.full_name.split('.')[-2] + note = 'Method of %s class in %s module' % ( + class_name.capitalize(), mod_name) + else: + note = '%s in %s module' % (call_def.type.capitalize(), + mod_name) + argspec = argspec.replace(' = ', '=') + calltip = calltip.replace(' = ', '=') + debug_print(call_def.name) + + doc_info = dict(name=name, argspec=argspec, + note=note, docstring=docstring, calltip=calltip) + return doc_info + + def get_definition(self, info): + """ + Find a definition location using Jedi + + Follows gotos until a definition is found, or it reaches a builtin + module. Falls back on token lookup if it is in an enaml file or does + not find a match + """ + line, filename = info['line_num'], info['filename'] + def_info, module_path, line_nr = None, None, None + gotos = self.get_jedi_object('goto_assignments', info) + + if gotos: + def_info = self.get_definition_info(gotos[0]) + if def_info and def_info['goto_next']: + defns = self.get_jedi_object('goto_definitions', info) + if defns: + new_info = self.get_definition_info(defns[0]) + if not new_info['in_builtin']: + def_info = new_info + elif not def_info: + return + # handle builtins -> try and find the module + if def_info and def_info['in_builtin']: + module_path, line_nr = self.find_in_builtin(def_info) + elif def_info: + module_path = def_info['module_path'] + line_nr = def_info['line_nr'] + if module_path == filename and line_nr == line: + return + return module_path, line_nr + + # ---- Private API ------------------------------------------------------- + + def get_jedi_object(self, func_name, info, use_filename=True): + """Call a desired function on a Jedi Script and return the result""" + if not jedi: + return + if DEBUG_EDITOR: + t0 = time.time() + # override IPython qt_loaders ImportDenier behavior + metas = sys.meta_path + for meta in metas: + if (meta.__class__.__name__ == 'ImportDenier' + and hasattr(meta, 'forbid')): + sys.meta_path.remove(meta) + + if use_filename: + filename = info['filename'] + else: + filename = None + + try: + script = jedi.Script(info['source_code'], info['line_num'], + info['column'], filename) + func = getattr(script, func_name) + val = func() + except Exception as e: + val = None + debug_print('Jedi error (%s)' % func_name) + debug_print(str(e)) + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, str(e)) + if DEBUG_EDITOR: + log_dt(LOG_FILENAME, func_name, t0) + if not val and filename: + return self.get_jedi_object(func_name, info, False) + else: + return val + + @staticmethod + def get_definition_info(defn): + """Extract definition information from the Jedi definition object""" + try: + module_path = defn.module_path + name = defn.name + if hasattr(defn, 'line_nr'): + line_nr = defn.line_nr + else: + line_nr = defn.line + description = defn.description + in_builtin = defn.in_builtin_module() + except Exception as e: + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, 'Get Defintion: %s' % e) + return None + pattern = 'class\s+{0}|def\s+{0}|self.{0}\s*=|{0}\s*='.format(name) + if not re.match(pattern, description): + goto_next = True + else: + goto_next = False + return dict(module_path=module_path, line_nr=line_nr, + description=description, name=name, in_builtin=in_builtin, + goto_next=goto_next) + + def find_in_builtin(self, info): + """Find a definition in a builtin file""" + module_path = info['module_path'] + line_nr = info['line_nr'] + ext = osp.splitext(info['module_path'])[1] + desc = info['description'] + name = info['name'] + if ext in self.python_like_exts() and ( + desc.startswith('import ') or desc.startswith('from ')): + path = self.python_like_mod_finder(desc, + osp.dirname(module_path), name) + if path: + info['module_path'] = module_path = path + info['line_nr'] = line_nr = 1 + if ext in self.all_editable_exts(): + pattern = 'from.*\W{0}\W?.*c?import|import.*\W{0}' + if not re.match(pattern.format(info['name']), desc): + line_nr = self.get_definition_from_file(module_path, name, + line_nr) + if not line_nr: + module_path = None + if not ext in self.all_editable_exts(): + line_nr = None + return module_path, line_nr + +if __name__ == '__main__': + + from spyder.utils.introspection.manager import CodeInfo + + p = JediPlugin() + p.load_plugin() + + source_code = "import numpy; numpy.ones(" + docs = p.get_info(CodeInfo('info', source_code, len(source_code))) + + assert docs['calltip'].startswith('ones(') and docs['name'] == 'ones' + + source_code = "import n" + completions = p.get_completions(CodeInfo('completions', source_code, + len(source_code))) + assert ('numpy', 'module') in completions + + source_code = "import pandas as pd; pd.DataFrame" + path, line_nr = p.get_definition(CodeInfo('definition', source_code, + len(source_code))) + assert 'frame.py' in path + + source_code = 'from .utils import CodeInfo' + path, line_nr = p.get_definition(CodeInfo('definition', source_code, + len(source_code), __file__)) + assert 'utils.py' in path and 'introspection' in path + + code = ''' +def test(a, b): + """Test docstring""" + pass +test(1,''' + path, line = p.get_definition(CodeInfo('definition', code, len(code), + 'dummy.txt', is_python_like=True)) + assert line == 2 + + docs = p.get_info(CodeInfo('info', code, len(code), __file__)) + assert 'Test docstring' in docs['docstring'] diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/manager.py spyder-3.0.2+dfsg1/spyder/utils/introspection/manager.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/manager.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/manager.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,354 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +# Standard library imports +from __future__ import print_function +from collections import OrderedDict +import time + +# Third party imports +from qtpy.QtCore import QObject, QTimer, Signal +from qtpy.QtWidgets import QApplication + +# Local imports +from spyder import dependencies +from spyder.config.base import _, DEBUG, debug_print, get_conf_path +from spyder.utils import sourcecode +from spyder.utils.introspection.plugin_client import PluginClient +from spyder.utils.introspection.utils import CodeInfo + + +PLUGINS = ['rope', 'jedi', 'fallback'] + +LOG_FILENAME = get_conf_path('introspection.log') +DEBUG_EDITOR = DEBUG >= 3 +LEAD_TIME_SEC = 0.25 + + +ROPE_REQVER = '>=0.9.4' +dependencies.add('rope', + _("Editor's code completion, go-to-definition and help"), + required_version=ROPE_REQVER) + +JEDI_REQVER = '>=0.8.1' +dependencies.add('jedi', + _("Editor's code completion, go-to-definition and help"), + required_version=JEDI_REQVER) + + +class PluginManager(QObject): + + introspection_complete = Signal(object) + + def __init__(self, executable): + super(PluginManager, self).__init__() + plugins = OrderedDict() + for name in PLUGINS: + try: + plugin = PluginClient(name, executable) + plugin.run() + except Exception as e: + debug_print('Introspection Plugin Failed: %s' % name) + debug_print(str(e)) + continue + debug_print('Introspection Plugin Loaded: %s' % name) + plugins[name] = plugin + plugin.received.connect(self.handle_response) + self.plugins = plugins + self.timer = QTimer() + self.desired = [] + self.ids = dict() + self.info = None + self.request = None + self.pending = None + self.pending_request = None + self.waiting = False + + def send_request(self, info): + """Handle an incoming request from the user.""" + if self.waiting: + if info.serialize() != self.info.serialize(): + self.pending_request = info + else: + debug_print('skipping duplicate request') + return + debug_print('%s request' % info.name) + desired = None + self.info = info + editor = info.editor + if (info.name == 'completion' and 'jedi' not in self.plugins and + info.line.lstrip().startswith(('import ', 'from '))): + desired = 'fallback' + + if ((not editor.is_python_like()) or + sourcecode.is_keyword(info.obj) or + (editor.in_comment_or_string() and info.name != 'info')): + desired = 'fallback' + + plugins = self.plugins.values() + if desired: + plugins = [self.plugins[desired]] + self.desired = [desired] + elif (info.name == 'definition' and not info.editor.is_python() or + info.name == 'info'): + self.desired = list(self.plugins.keys()) + else: + # Use all but the fallback + plugins = list(self.plugins.values())[:-1] + self.desired = list(self.plugins.keys())[:-1] + + self._start_time = time.time() + self.waiting = True + method = 'get_%s' % info.name + value = info.serialize() + self.ids = dict() + for plugin in plugins: + request_id = plugin.request(method, value) + self.ids[request_id] = plugin.name + self.timer.stop() + self.timer.singleShot(LEAD_TIME_SEC * 1000, self._handle_timeout) + + def validate(self): + for plugin in self.plugins.values(): + plugin.request('validate') + + def handle_response(self, response): + name = self.ids.get(response['request_id'], None) + if not name: + return + if response.get('error', None): + debug_print('Response error:', response['error']) + return + if name == self.desired[0] or not self.waiting: + if response.get('result', None): + self._finalize(response) + else: + self.pending = response + + def close(self): + [plugin.close() for plugin in self.plugins] + + def _finalize(self, response): + self.waiting = False + self.pending = None + if self.info: + delta = time.time() - self._start_time + debug_print('%s request from %s finished: "%s" in %.1f sec' + % (self.info.name, response['name'], + str(response['result'])[:100], delta)) + response['info'] = self.info + self.introspection_complete.emit(response) + self.info = None + if self.pending_request: + info = self.pending_request + self.pending_request = None + self.send_request(info) + + def _handle_timeout(self): + self.waiting = False + if self.pending: + self._finalize(self.pending) + else: + debug_print('No valid responses acquired') + + +class IntrospectionManager(QObject): + + send_to_help = Signal(str, str, str, str, bool) + edit_goto = Signal(str, int, str) + + def __init__(self, executable=None): + super(IntrospectionManager, self).__init__() + self.editor_widget = None + self.pending = None + self.plugin_manager = PluginManager(executable) + self.plugin_manager.introspection_complete.connect( + self._introspection_complete) + + def change_executable(self, executable): + self.plugin_manager.close() + self.plugin_manager = PluginManager(executable) + self.plugin_manager.introspection_complete.connect( + self._introspection_complete) + + def set_editor_widget(self, editor_widget): + self.editor_widget = editor_widget + + def _get_code_info(self, name, position=None, **kwargs): + + editor = self.editor_widget.get_current_editor() + finfo = self.editor_widget.get_current_finfo() + in_comment_or_string = editor.in_comment_or_string() + + if position is None: + position = editor.get_position('cursor') + + kwargs['editor'] = editor + kwargs['finfo'] = finfo + kwargs['editor_widget'] = self.editor_widget + + return CodeInfo(name, finfo.get_source_code(), position, + finfo.filename, editor.is_python_like, in_comment_or_string, + **kwargs) + + def get_completions(self, automatic): + """Get code completion""" + info = self._get_code_info('completions', automatic=automatic) + self.plugin_manager.send_request(info) + + def go_to_definition(self, position): + """Go to definition""" + info = self._get_code_info('definition', position) + self.plugin_manager.send_request(info) + + def show_object_info(self, position, auto=True): + """Show signature calltip and/or docstring in the Help plugin""" + # auto is True means that this method was called automatically, + # i.e. the user has just entered an opening parenthesis -- in that + # case, we don't want to force Help to be visible, to avoid polluting + # the window layout + info = self._get_code_info('info', position, auto=auto) + self.plugin_manager.send_request(info) + + def validate(self): + """Validate the plugins""" + self.plugin_manager.validate() + + def is_editor_ready(self): + """Check if the main app is starting up""" + if self.editor_widget: + window = self.editor_widget.window() + if hasattr(window, 'is_starting_up') and not window.is_starting_up: + return True + + def _introspection_complete(self, response): + """ + Handle an introspection response completion. + + Route the response to the correct handler. + """ + result = response.get('result', None) + if result is None: + return + info = response['info'] + current = self._get_code_info(response['info']['name']) + + if result and current.filename == info.filename: + func = getattr(self, '_handle_%s_result' % info.name) + try: + func(result, current, info) + except Exception as e: + debug_print(e) + + def _handle_completions_result(self, comp_list, info, prev_info): + """ + Handle a `completions` result. + + Only handle the response if we are on the same line of text and + on the same `obj` as the original request. + """ + if info.line_num != prev_info.line_num: + return + completion_text = info.obj + prev_text = prev_info.obj + + if prev_info.obj is None: + completion_text = '' + prev_text = '' + + if not completion_text.startswith(prev_text): + return + + if info.full_obj and len(info.full_obj) > len(info.obj): + new_list = [(c, t) for (c, t) in comp_list + if c.startswith(info.full_obj)] + if new_list: + pos = info.editor.get_position('cursor') + new_pos = pos + len(info.full_obj) - len(info.obj) + info.editor.set_cursor_position(new_pos) + completion_text = info.full_obj + comp_list = new_list + + if '.' in completion_text: + completion_text = completion_text.split('.')[-1] + + comp_list = [(c.split('.')[-1], t) for (c, t) in comp_list] + comp_list = [(c, t) for (c, t) in comp_list + if c.startswith(completion_text)] + + info.editor.show_completion_list(comp_list, completion_text, + prev_info.automatic) + + def _handle_info_result(self, resp, info, prev_info): + """ + Handle an `info` result, triggering a calltip and/or docstring. + + Only handle the response if we are on the same line of text as + when the request was initiated. + """ + if info.line_num != prev_info.line_num: + return + + if resp['calltip']: + info.editor.show_calltip('Arguments', resp['calltip'], + signature=True, + at_position=prev_info.position) + + if resp['name']: + self.send_to_help.emit( + resp['name'], resp['argspec'], + resp['note'], resp['docstring'], + not prev_info.auto) + + def _handle_definition_result(self, resp, info, prev_info): + """Handle a `definition` result""" + fname, lineno = resp + self.edit_goto.emit(fname, lineno, "") + + def _post_message(self, message, timeout=60000): + """ + Post a message to the main window status bar with a timeout in ms + """ + if self.editor_widget: + try: + statusbar = self.editor_widget.window().statusBar() + statusbar.showMessage(message, timeout) + QApplication.processEvents() + except AttributeError: + pass + + +class IntrospectionPlugin(object): + + def load_plugin(self): + """Initialize the plugin""" + pass + + def get_completions(self, info): + """Get a list of completions""" + pass + + def get_info(self, info): + """ + Find the calltip and docs + + Returns a dict like the following: + {'note': 'Function of numpy.core.numeric...', + 'argspec': "(shape, dtype=None, order='C')' + 'docstring': 'Return an array of given...' + 'name': 'ones', + 'calltip': 'ones(shape, dtype=None, order='C')'} + """ + pass + + def get_definition(self, info): + """Get a (filename, line_num) location for a definition""" + pass + + def validate(self): + """Validate the plugin""" + pass + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/module_completion.py spyder-3.0.2+dfsg1/spyder/utils/introspection/module_completion.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/module_completion.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/module_completion.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,334 @@ +# -*- coding: utf-8 -*- +"""Module completion auxiliary functions""" + +#------------------------------------------------------------------------------ +# +# Most functions on this file were taken from the file core/completerlib, +# which belongs to the IPython project (v0.13). They were added here because +# a) IPython is not an Spyder runtime dependency, and b) we want to perfom +# module completion not only on our Python console, but also on our source +# code editor. +# +# Several of these functions were modified to make it work according to our +# needs +# +# Distributed under the terms of the BSD License. +# Copyright (C) 2010-2011 The IPython Development Team. +# Copyright (C) Spyder Project Contributors +# +#------------------------------------------------------------------------------ + +import imp +import inspect +import os.path +import pkgutil +import re +from time import time +import sys +from zipimport import zipimporter + +from spyder.config.base import get_conf_path, running_in_mac_app +from spyder.py3compat import PY3 + +from pickleshare import PickleShareDB + +#----------------------------------------------------------------------------- +# Globals and constants +#----------------------------------------------------------------------------- + +# Path to the modules database +MODULES_PATH = get_conf_path('db') + +# Time in seconds after which we give up +if os.name == 'nt': + TIMEOUT_GIVEUP = 30 +else: + TIMEOUT_GIVEUP = 20 + +# Py2app only uses .pyc files for the stdlib when optimize=0, +# so we need to add it as another suffix here +if running_in_mac_app(): + suffixes = imp.get_suffixes() + [('.pyc', 'rb', '2')] +else: + suffixes = imp.get_suffixes() + +# Regular expression for the python import statement +import_re = re.compile(r'(?P[a-zA-Z_][a-zA-Z0-9_]*?)' + r'(?P[/\\]__init__)?' + r'(?P%s)$' % + r'|'.join(re.escape(s[0]) for s in suffixes)) + +# Modules database +modules_db = PickleShareDB(MODULES_PATH) + +#----------------------------------------------------------------------------- +# Utility functions +#----------------------------------------------------------------------------- + +def module_list(path): + """ + Return the list containing the names of the modules available in the given + folder. + """ + # sys.path has the cwd as an empty string, but isdir/listdir need it as '.' + if path == '': + path = '.' + + # A few local constants to be used in loops below + pjoin = os.path.join + + if os.path.isdir(path): + # Build a list of all files in the directory and all files + # in its subdirectories. For performance reasons, do not + # recurse more than one level into subdirectories. + files = [] + for root, dirs, nondirs in os.walk(path): + subdir = root[len(path)+1:] + if subdir: + files.extend(pjoin(subdir, f) for f in nondirs) + dirs[:] = [] # Do not recurse into additional subdirectories. + else: + files.extend(nondirs) + else: + try: + files = list(zipimporter(path)._files.keys()) + except: + files = [] + + # Build a list of modules which match the import_re regex. + modules = [] + for f in files: + m = import_re.match(f) + if m: + modules.append(m.group('name')) + return list(set(modules)) + + +def get_root_modules(paths): + """ + Returns list of names of all modules from PYTHONPATH folders. + + paths : list + A list of additional paths that Spyder adds to PYTHONPATH. They are + comming from our PYTHONPATH manager and from the currently selected + project. + """ + modules = [] + spy_modules = [] + + for path in paths: + spy_modules += module_list(path) + spy_modules = set(spy_modules) + if '__init__' in spy_modules: + spy_modules.remove('__init__') + spy_modules = list(spy_modules) + + if 'rootmodules' in modules_db: + return spy_modules + modules_db['rootmodules'] + + t = time() + modules = list(sys.builtin_module_names) + # TODO: Change this sys.path for console's interpreter sys.path + for path in sys.path: + modules += module_list(path) + if time() - t > TIMEOUT_GIVEUP: + print("Module list generation is taking too long, we give up.\n") + modules_db['rootmodules'] = [] + return [] + + modules = set(modules) + excluded_modules = ['__init__'] + spy_modules + for mod in excluded_modules: + if mod in modules: + modules.remove(mod) + modules = list(modules) + + modules_db['rootmodules'] = modules + return spy_modules + modules + + +def get_submodules(mod): + """Get all submodules of a given module""" + def catch_exceptions(module): + pass + try: + m = __import__(mod) + submodules = [mod] + submods = pkgutil.walk_packages(m.__path__, m.__name__ + '.', + catch_exceptions) + for sm in submods: + sm_name = sm[1] + submodules.append(sm_name) + except ImportError: + return [] + except: + return [mod] + + return submodules + + +def is_importable(module, attr, only_modules): + if only_modules: + return inspect.ismodule(getattr(module, attr)) + else: + return not(attr[:2] == '__' and attr[-2:] == '__') + + +def try_import(mod, only_modules=False): + try: + m = __import__(mod) + except: + return [] + mods = mod.split('.') + for module in mods[1:]: + m = getattr(m, module) + + m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__ + + completions = [] + if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init: + completions.extend([attr for attr in dir(m) if + is_importable(m, attr, only_modules)]) + + completions.extend(getattr(m, '__all__', [])) + if m_is_init: + completions.extend(module_list(os.path.dirname(m.__file__))) + completions = set(completions) + if '__init__' in completions: + completions.remove('__init__') + return list(completions) + + +def dot_completion(mod, paths): + if len(mod) < 2: + return [x for x in get_root_modules(paths) if x.startswith(mod[0])] + completion_list = try_import('.'.join(mod[:-1]), True) + completion_list = [x for x in completion_list if x.startswith(mod[-1])] + completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list] + return completion_list + +#----------------------------------------------------------------------------- +# Main functions +#----------------------------------------------------------------------------- + +def module_completion(line, paths=[]): + """ + Returns a list containing the completion possibilities for an import line. + + The line looks like this : + 'import xml.d' + 'from xml.dom import' + """ + + words = line.split(' ') + nwords = len(words) + + # from whatever -> 'import ' + if nwords == 3 and words[0] == 'from': + if words[2].startswith('i') or words[2] == '': + return ['import '] + else: + return [] + + # 'import xy or import xy, ' + if words[0] == 'import': + if nwords == 2 and words[1] == '': + return get_root_modules(paths) + if ',' == words[-1][-1]: + return [' '] + mod = words[-1].split('.') + return dot_completion(mod, paths) + + # 'from xy' + if nwords < 3 and (words[0] == 'from'): + if nwords == 1: + return get_root_modules(paths) + mod = words[1].split('.') + return dot_completion(mod, paths) + + # 'from xyz import abc' + if nwords >= 3 and words[0] == 'from': + mod = words[1] + completion_list = try_import(mod) + if words[2] == 'import' and words[3] != '': + if '(' in words[-1]: + words = words[:-2] + words[-1].split('(') + if ',' in words[-1]: + words = words[:-2] + words[-1].split(',') + return [x for x in completion_list if x.startswith(words[-1])] + else: + return completion_list + + return [] + + +def reset(): + """Clear root modules database""" + if 'rootmodules' in modules_db: + del modules_db['rootmodules'] + + +def get_preferred_submodules(): + """ + Get all submodules of the main scientific modules and others of our + interest + """ + if 'submodules' in modules_db: + return modules_db['submodules'] + + mods = ['numpy', 'scipy', 'sympy', 'pandas', 'networkx', 'statsmodels', + 'matplotlib', 'sklearn', 'skimage', 'mpmath', 'os', 'PIL', + 'OpenGL', 'array', 'audioop', 'binascii', 'cPickle', 'cStringIO', + 'cmath', 'collections', 'datetime', 'errno', 'exceptions', 'gc', + 'imageop', 'imp', 'itertools', 'marshal', 'math', 'mmap', 'msvcrt', + 'nt', 'operator', 'parser', 'rgbimg', 'signal', 'strop', 'sys', + 'thread', 'time', 'wx', 'xxsubtype', 'zipimport', 'zlib', 'nose', + 'PyQt4', 'PySide', 'os.path'] + + submodules = [] + + for m in mods: + submods = get_submodules(m) + submodules += submods + + modules_db['submodules'] = submodules + return submodules + +#----------------------------------------------------------------------------- +# Tests +#----------------------------------------------------------------------------- + +if __name__ == "__main__": + # Some simple tests. + # Sort operations are done by the completion widget, so we have to + # replicate them here. + # We've chosen to use xml on most tests because it's on the standard + # library. This way we can ensure they work on all plataforms. + + assert sorted(module_completion('import xml.')) == \ + ['xml.dom', 'xml.etree', 'xml.parsers', 'xml.sax'] + + assert sorted(module_completion('import xml.d')) == ['xml.dom'] + + assert module_completion('from xml.etree ') == ['import '] + + assert sorted(module_completion('from xml.etree import '), key=str.lower) ==\ + ['cElementTree', 'ElementInclude', 'ElementPath', 'ElementTree'] + + assert module_completion('import sys, zl') == ['zlib'] + + s = 'from xml.etree.ElementTree import ' + assert module_completion(s + 'V') == ['VERSION'] + + if PY3: + assert sorted(module_completion(s + 'VERSION, XM')) == \ + ['XML', 'XMLID', 'XMLParser', 'XMLPullParser'] + else: + assert sorted(module_completion(s + 'VERSION, XM')) == \ + ['XML', 'XMLID', 'XMLParser', 'XMLTreeBuilder'] + + assert module_completion(s + '(dum') == ['dump'] + + assert module_completion(s + '(dump, Su') == ['SubElement'] + + assert 'os.path' in get_preferred_submodules() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/plugin_client.py spyder-3.0.2+dfsg1/spyder/utils/introspection/plugin_client.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/plugin_client.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/plugin_client.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,227 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +# Local imports +import imp +import os +import os.path as osp +import sys +import uuid + +# Third party imports +from qtpy.QtCore import (QObject, QProcess, QProcessEnvironment, + QSocketNotifier, QTimer, Signal) +from qtpy.QtWidgets import QApplication +import zmq + +# Local imports +from spyder.config.base import debug_print, DEV, get_module_path + + +# Heartbeat timer in milliseconds +HEARTBEAT = 1000 + + +class AsyncClient(QObject): + + """ + A class which handles a connection to a client through a QProcess. + """ + + # Emitted when the client has initialized. + initialized = Signal() + + # Emitted when the client errors. + errored = Signal() + + # Emitted when a request response is received. + received = Signal(object) + + def __init__(self, target, executable=None, name=None, + extra_args=None, libs=None, cwd=None, env=None): + super(AsyncClient, self).__init__() + self.executable = executable or sys.executable + self.extra_args = extra_args + self.target = target + self.name = name or self + self.libs = libs + self.cwd = cwd + self.env = env + self.is_initialized = False + self.closing = False + self.context = zmq.Context() + QApplication.instance().aboutToQuit.connect(self.close) + + # Set up the heartbeat timer. + self.timer = QTimer(self) + self.timer.timeout.connect(self._heartbeat) + + def run(self): + """Handle the connection with the server. + """ + # Set up the zmq port. + self.socket = self.context.socket(zmq.PAIR) + self.port = self.socket.bind_to_random_port('tcp://*') + + # Set up the process. + self.process = QProcess(self) + if self.cwd: + self.process.setWorkingDirectory(self.cwd) + p_args = ['-u', self.target, str(self.port)] + if self.extra_args is not None: + p_args += self.extra_args + + # Set up environment variables. + processEnvironment = QProcessEnvironment() + env = self.process.systemEnvironment() + if (self.env and 'PYTHONPATH' not in self.env) or DEV: + python_path = osp.dirname(get_module_path('spyder')) + # Add the libs to the python path. + for lib in self.libs: + try: + path = osp.dirname(imp.find_module(lib)[1]) + python_path = osp.pathsep.join([python_path, path]) + except ImportError: + pass + env.append("PYTHONPATH=%s" % python_path) + if self.env: + env.update(self.env) + for envItem in env: + envName, separator, envValue = envItem.partition('=') + processEnvironment.insert(envName, envValue) + self.process.setProcessEnvironment(processEnvironment) + + # Start the process and wait for started. + self.process.start(self.executable, p_args) + self.process.finished.connect(self._on_finished) + running = self.process.waitForStarted() + if not running: + raise IOError('Could not start %s' % self) + + # Set up the socket notifer. + fid = self.socket.getsockopt(zmq.FD) + self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self) + self.notifier.activated.connect(self._on_msg_received) + + def request(self, func_name, *args, **kwargs): + """Send a request to the server. + + The response will be a dictionary the 'request_id' and the + 'func_name' as well as a 'result' field with the object returned by + the function call or or an 'error' field with a traceback. + """ + if not self.is_initialized: + return + request_id = uuid.uuid4().hex + request = dict(func_name=func_name, + args=args, + kwargs=kwargs, + request_id=request_id) + self._send(request) + return request_id + + def close(self): + """Cleanly close the connection to the server. + """ + self.closing = True + self.is_initialized = False + self.timer.stop() + self.notifier.activated.disconnect(self._on_msg_received) + self.notifier.setEnabled(False) + del self.notifier + self.request('server_quit') + self.process.waitForFinished(1000) + self.process.close() + self.context.destroy() + + def _on_finished(self): + """Handle a finished signal from the process. + """ + if self.closing: + return + if self.is_initialized: + debug_print('Restarting %s' % self.name) + debug_print(self.process.readAllStandardOutput()) + debug_print(self.process.readAllStandardError()) + self.is_initialized = False + self.notifier.setEnabled(False) + self.run() + else: + debug_print('Errored %s' % self.name) + debug_print(self.process.readAllStandardOutput()) + debug_print(self.process.readAllStandardError()) + self.errored.emit() + + def _on_msg_received(self): + """Handle a message trigger from the socket. + """ + self.notifier.setEnabled(False) + while 1: + try: + resp = self.socket.recv_pyobj(flags=zmq.NOBLOCK) + except zmq.ZMQError: + self.notifier.setEnabled(True) + return + if not self.is_initialized: + self.is_initialized = True + debug_print('Initialized %s' % self.name) + self.initialized.emit() + self.timer.start(HEARTBEAT) + continue + resp['name'] = self.name + self.received.emit(resp) + + def _heartbeat(self): + """Send a heartbeat to keep the server alive. + """ + self._send(dict(func_name='server_heartbeat')) + + def _send(self, obj): + """Send an object to the server. + """ + try: + self.socket.send_pyobj(obj, zmq.NOBLOCK) + except Exception as e: + debug_print(e) + self.is_initialized = False + self._on_finished() + + +class PluginClient(AsyncClient): + + def __init__(self, plugin_name, executable=None, env=None): + cwd = os.path.dirname(__file__) + super(PluginClient, self).__init__('plugin_server.py', + executable=executable, cwd=cwd, env=env, + extra_args=[plugin_name], libs=[plugin_name]) + self.name = plugin_name + + +if __name__ == '__main__': + app = QApplication(sys.argv) + plugin = PluginClient('jedi') + plugin.run() + + def handle_return(value): + print(value) + if value['func_name'] == 'foo': + app.quit() + else: + plugin.request('foo') + + def handle_errored(): + print('errored') + sys.exit(1) + + def start(): + print('start') + plugin.request('validate') + + plugin.errored.connect(handle_errored) + plugin.received.connect(handle_return) + plugin.initialized.connect(start) + + app.exec_() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/plugin_server.py spyder-3.0.2+dfsg1/spyder/utils/introspection/plugin_server.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/plugin_server.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/plugin_server.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +import sys +import time +import traceback + +import zmq + + +# Timeout in milliseconds +TIMEOUT = 10000 + + +class AsyncServer(object): + + """ + Introspection server, provides a separate process + for interacting with an object. + """ + + def __init__(self, port, *args): + self.port = port + self.object = self.initialize(*args) + self.context = zmq.Context() + self.socket = self.context.socket(zmq.PAIR) + self.socket.connect("tcp://localhost:%s" % port) + self.socket.send_pyobj(port) + + def initialize(self, plugin_name): + """Initialize the object and return it. + """ + return object() + + def run(self): + """Handle requests from the client. + """ + t0 = time.time() + initialized = False + timed_out = False + while 1: + # Poll for events, handling a timeout. + try: + events = self.socket.poll(TIMEOUT) + except KeyboardInterrupt: + time.sleep(0.1) + continue + if events == 0 and initialized: + if timed_out: + delta = int(time.time() - t0) + print('Timed out after %s sec' % delta) + return + timed_out = True + continue + timed_out = False + initialized = True + # Drain all exising requests, handling quit and heartbeat. + requests = [] + while 1: + try: + request = self.socket.recv_pyobj() + except KeyboardInterrupt: + time.sleep(0.1) + continue + if request['func_name'] == 'server_quit': + print('Quitting') + sys.stdout.flush() + return + elif request['func_name'] != 'server_heartbeat': + requests.append(request) + else: + print('Got heartbeat') + try: + events = self.socket.poll(0) + except KeyboardInterrupt: + time.sleep(0.1) + continue + if events == 0: + break + # Select the most recent request. + if not requests: + continue + request = requests[-1] + + # Gather the response + response = dict(func_name=request['func_name'], + request_id=request['request_id']) + try: + func = getattr(self.object, request['func_name']) + args = request.get('args', []) + kwargs = request.get('kwargs', {}) + response['result'] = func(*args, **kwargs) + except Exception: + response['error'] = traceback.format_exc() + + # Send the response to the client. + self.socket.send_pyobj(response) + + +class PluginServer(AsyncServer): + + """ + Introspection plugin server, provides a separate process + for interacting with a plugin. + """ + + def initialize(self, plugin_name): + """Initialize the object and return it. + """ + mod_name = plugin_name + '_plugin' + mod = __import__('spyder.utils.introspection.' + mod_name, + fromlist=[mod_name]) + cls = getattr(mod, '%sPlugin' % plugin_name.capitalize()) + plugin = cls() + plugin.load_plugin() + return plugin + + +if __name__ == '__main__': + args = sys.argv[1:] + if not len(args) == 2: + print('Usage: plugin_server.py client_port plugin_name') + sys.exit(0) + plugin = PluginServer(*args) + print('Started') + plugin.run() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/rope_plugin.py spyder-3.0.2+dfsg1/spyder/utils/introspection/rope_plugin.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/rope_plugin.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/rope_plugin.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,319 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Rope introspection plugin +""" + +import time +import imp + +from spyder.config.base import get_conf_path, STDERR +from spyder.utils import encoding, programs +from spyder.py3compat import PY2 +from spyder.utils.dochelpers import getsignaturefromtext +from spyder.utils import sourcecode +from spyder.utils.debug import log_last_error, log_dt +from spyder.utils.introspection.manager import ( + DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin) +from spyder.utils.introspection.module_completion import ( + get_preferred_submodules) +from spyder.utils.introspection.manager import ROPE_REQVER + +try: + try: + from spyder import rope_patch + rope_patch.apply() + except ImportError: + # rope is not installed + pass + import rope.base.libutils + import rope.contrib.codeassist +except ImportError: + pass + + +#TODO: The following preferences should be customizable in the future +ROPE_PREFS = {'ignore_syntax_errors': True, + 'ignore_bad_imports': True, + 'soa_followed_calls': 2, + 'extension_modules': [], + } + + +class RopePlugin(IntrospectionPlugin): + """ + Rope based introspection plugin for jedi + + Editor's code completion, go-to-definition and help + """ + + project = None + + # ---- IntrospectionPlugin API -------------------------------------------- + name = 'rope' + + def load_plugin(self): + """Load the Rope introspection plugin""" + if not programs.is_module_installed('rope', ROPE_REQVER): + raise ImportError('Requires Rope %s' % ROPE_REQVER) + self.project = None + self.create_rope_project(root_path=get_conf_path()) + submods = get_preferred_submodules() + actual = [] + for submod in submods: + try: + imp.find_module(submod) + actual.append(submod) + except ImportError: + pass + if self.project is not None: + self.project.prefs.set('extension_modules', actual) + + def get_completions(self, info): + """Get a list of (completion, type) tuples using Rope""" + if self.project is None: + return [] + filename = info['filename'] + source_code = info['source_code'] + offset = info['position'] + + # Prevent Rope from returning import completions because + # it can't handle them. Only Jedi can do it! + lines = sourcecode.split_source(source_code[:offset]) + last_line = lines[-1].lstrip() + if (last_line.startswith('import ') or last_line.startswith('from ')) \ + and not ';' in last_line: + return [] + + if PY2: + filename = filename.encode('utf-8') + else: + #TODO: test if this is working without any further change in + # Python 3 with a user account containing unicode characters + pass + try: + resource = rope.base.libutils.path_to_resource(self.project, + filename) + except Exception as _error: + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, "path_to_resource: %r" % filename) + resource = None + try: + if DEBUG_EDITOR: + t0 = time.time() + proposals = rope.contrib.codeassist.code_assist(self.project, + source_code, offset, resource, maxfixes=3) + proposals = rope.contrib.codeassist.sorted_proposals(proposals) + if DEBUG_EDITOR: + log_dt(LOG_FILENAME, "code_assist/sorted_proposals", t0) + return [(proposal.name, proposal.type) for proposal in proposals] + except Exception as _error: #analysis:ignore + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, "get_completion_list") + return [] + + def get_info(self, info): + """Get a formatted calltip and docstring from Rope""" + if self.project is None: + return + filename = info['filename'] + source_code = info['source_code'] + offset = info['position'] + + if PY2: + filename = filename.encode('utf-8') + else: + #TODO: test if this is working without any further change in + # Python 3 with a user account containing unicode characters + pass + try: + resource = rope.base.libutils.path_to_resource(self.project, + filename) + except Exception as _error: + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, "path_to_resource: %r" % filename) + resource = None + try: + if DEBUG_EDITOR: + t0 = time.time() + cts = rope.contrib.codeassist.get_calltip( + self.project, source_code, offset, resource, + ignore_unknown=False, remove_self=True, maxfixes=3) + if DEBUG_EDITOR: + log_dt(LOG_FILENAME, "get_calltip", t0) + if cts is not None: + while '..' in cts: + cts = cts.replace('..', '.') + if '(.)' in cts: + cts = cts.replace('(.)', '(...)') + try: + doc_text = rope.contrib.codeassist.get_doc(self.project, + source_code, offset, resource, maxfixes=3) + if DEBUG_EDITOR: + log_dt(LOG_FILENAME, "get_doc", t0) + except Exception as _error: + doc_text = '' + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, "get_doc") + return self.handle_info(cts, doc_text, source_code, offset) + except Exception as _error: #analysis:ignore + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, "get_calltip_text") + + def handle_info(self, cts, doc_text, source_code, offset): + + obj_fullname = '' + calltip = '' + argspec = '' + note = '' + + if cts: + cts = cts.replace('.__init__', '') + parpos = cts.find('(') + if parpos: + obj_fullname = cts[:parpos] + obj_name = obj_fullname.split('.')[-1] + cts = cts.replace(obj_fullname, obj_name) + calltip = cts + if ('()' in cts) or ('(...)' in cts): + # Either inspected object has no argument, or it's + # a builtin or an extension -- in this last case + # the following attempt may succeed: + calltip = getsignaturefromtext(doc_text, obj_name) + if not obj_fullname: + obj_fullname = sourcecode.get_primary_at(source_code, offset) + if obj_fullname and not obj_fullname.startswith('self.'): + # doc_text was generated by utils.dochelpers.getdoc + if type(doc_text) is dict: + obj_fullname = doc_text['name'] or obj_fullname + argspec = doc_text['argspec'] + note = doc_text['note'] + doc_text = doc_text['docstring'] + elif calltip: + argspec_st = calltip.find('(') + argspec = calltip[argspec_st:] + module_end = obj_fullname.rfind('.') + module = obj_fullname[:module_end] + note = 'Present in %s module' % module + + if not doc_text and not calltip: + return + + return dict(name=obj_fullname, argspec=argspec, note=note, + docstring=doc_text, calltip=calltip) + + def get_definition(self, info): + """Find a definition location using Rope""" + if self.project is None: + return + + filename = info['filename'] + source_code = info['source_code'] + offset = info['position'] + + if PY2: + filename = filename.encode('utf-8') + else: + #TODO: test if this is working without any further change in + # Python 3 with a user account containing unicode characters + pass + try: + resource = rope.base.libutils.path_to_resource(self.project, + filename) + except Exception as _error: + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, "path_to_resource: %r" % filename) + resource = None + try: + if DEBUG_EDITOR: + t0 = time.time() + resource, lineno = rope.contrib.codeassist.get_definition_location( + self.project, source_code, offset, resource, maxfixes=3) + if DEBUG_EDITOR: + log_dt(LOG_FILENAME, "get_definition_location", t0) + if resource is not None: + filename = resource.real_path + if filename and lineno: + return filename, lineno + except Exception as _error: #analysis:ignore + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, "get_definition_location") + + def validate(self): + """Validate the Rope project""" + if self.project is not None: + try: + self.project.validate(self.project.root) + except RuntimeError: + pass + + # ---- Private API ------------------------------------------------------- + + def create_rope_project(self, root_path): + """Create a Rope project on a desired path""" + if PY2: + root_path = encoding.to_fs_from_unicode(root_path) + else: + #TODO: test if this is working without any further change in + # Python 3 with a user account containing unicode characters + pass + try: + import rope.base.project + self.project = rope.base.project.Project(root_path, **ROPE_PREFS) + except ImportError: + print >>STDERR, 'project error' + self.project = None + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, + "create_rope_project: %r" % root_path) + except TypeError: + self.project = None + if DEBUG_EDITOR: + log_last_error(LOG_FILENAME, + "create_rope_project: %r" % root_path) + self.validate() + + def close_rope_project(self): + """Close the Rope project""" + if self.project is not None: + self.project.close() + + +if __name__ == '__main__': + + from spyder.utils.introspection.manager import CodeInfo + + p = RopePlugin() + p.load_plugin() + + source_code = "import numpy; numpy.ones" + docs = p.get_info(CodeInfo('info', source_code, len(source_code), + __file__)) + assert 'ones(' in docs['calltip'] and 'ones(' in docs['docstring'] + + source_code = "import numpy; n" + completions = p.get_completions(CodeInfo('completions', source_code, + len(source_code), __file__)) + assert ('numpy', 'module') in completions + + source_code = "import a" + completions = p.get_completions(CodeInfo('completions', source_code, + len(source_code), __file__)) + assert not completions + + code = ''' +def test(a, b): + """Test docstring""" + pass +test(1,''' + path, line = p.get_definition(CodeInfo('definition', code, len(code), + 'dummy.txt', is_python_like=True)) + assert line == 2 + + docs = p.get_info(CodeInfo('info', code, len(code), __file__, + is_python_like=True)) + assert 'Test docstring' in docs['docstring'] diff -Nru spyder-2.3.8+dfsg1/spyder/utils/introspection/utils.py spyder-3.0.2+dfsg1/spyder/utils/introspection/utils.py --- spyder-2.3.8+dfsg1/spyder/utils/introspection/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/introspection/utils.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Introspection utilities used by Spyder +""" + +import imp +import os +import pickle +import os.path as osp +import re + +from spyder.utils.misc import memoize + +from spyder.utils.syntaxhighlighters import ( + custom_extension_lexer_mapping +) + +from pygments.lexer import words +from pygments.lexers import ( + get_lexer_for_filename, get_lexer_by_name, TextLexer +) +from pygments.util import ClassNotFound +from pygments.token import Token + + +class CodeInfo(object): + + id_regex = re.compile(r'[^\d\W][\w\.]*', re.UNICODE) + func_call_regex = re.compile(r'([^\d\W][\w\.]*)\([^\)\()]*\Z', + re.UNICODE) + + def __init__(self, name, source_code, position, filename=None, + is_python_like=False, in_comment_or_string=False, **kwargs): + self.__dict__.update(kwargs) + self.name = name + self.filename = filename + self.source_code = source_code + self.is_python_like = is_python_like + self.in_comment_or_string = in_comment_or_string + + self.position = position + + # if in a comment, look for the previous definition + if in_comment_or_string: + # if this is a docstring, find it, set as our + self.docstring = self._get_docstring() + # backtrack and look for a line that starts with def or class + if name != 'completions': + while position: + base = self.source_code[position: position + 6] + if base.startswith('def ') or base.startswith('class '): + position += base.index(' ') + 1 + break + position -= 1 + else: + self.docstring = '' + + self.position = position + + if position == 0: + self.lines = [] + self.column = 0 + self.line_num = 0 + self.line = '' + self.obj = '' + self.full_obj = '' + else: + self._get_info() + + def _get_info(self): + + self.lines = self.source_code[:self.position].splitlines() + self.line_num = len(self.lines) + + self.line = self.lines[-1] + self.column = len(self.lines[-1]) + + full_line = self.source_code.splitlines()[self.line_num - 1] + + lexer = find_lexer_for_filename(self.filename) + + # check for a text-based lexer that doesn't split tokens + if len(list(lexer.get_tokens('a b'))) == 1: + # Use regex to get the information + tokens = re.findall(self.id_regex, self.line) + if tokens and self.line.endswith(tokens[-1]): + self.obj = tokens[-1] + else: + self.obj = None + + self.full_obj = self.obj + + if self.obj: + full_line = self.source_code.splitlines()[self.line_num - 1] + rest = full_line[self.column:] + match = re.match(self.id_regex, rest) + if match: + self.full_obj = self.obj + match.group() + + self.context = None + else: + # Use lexer to get the information + pos = 0 + line_tokens = lexer.get_tokens(full_line) + for (context, token) in line_tokens: + pos += len(token) + if pos >= self.column: + self.obj = token[:len(token) - (pos - self.column)] + self.full_obj = token + if context in Token.Literal.String: + context = Token.Literal.String + self.context = context + break + + if (self.name in ['info', 'definition'] and (not self.context in Token.Name) + and self.is_python_like): + func_call = re.findall(self.func_call_regex, self.line) + if func_call: + self.obj = func_call[-1] + self.column = self.line.index(self.obj) + len(self.obj) + self.position = self.position - len(self.line) + self.column + + def _get_docstring(self): + """Find the docstring we are currently in""" + left = self.position + while left: + if self.source_code[left: left + 3] in ['"""', "'''"]: + left += 3 + break + left -= 1 + right = self.position + while right < len(self.source_code): + if self.source_code[right - 3: right] in ['"""', "'''"]: + right -= 3 + break + right += 1 + if left and right < len(self.source_code): + return self.source_code[left: right] + return '' + + def __eq__(self, other): + try: + return self.serialize() == other.serialize() + except Exception: + return False + + def __getitem__(self, item): + """Allow dictionary-like access""" + return getattr(self, item) + + def serialize(self): + state = {} + for (key, value) in self.__dict__.items(): + try: + pickle.dumps(value) + state[key] = value + except Exception: + pass + state['id_regex'] = self.id_regex + state['func_call_regex'] = self.func_call_regex + return state + + +def find_lexer_for_filename(filename): + """Get a Pygments Lexer given a filename. + """ + filename = filename or '' + root, ext = os.path.splitext(filename) + if ext in custom_extension_lexer_mapping: + lexer = get_lexer_by_name(custom_extension_lexer_mapping[ext]) + else: + try: + lexer = get_lexer_for_filename(filename) + except ClassNotFound: + return TextLexer() + return lexer + + +def get_keywords(lexer): + """Get the keywords for a given lexer. + """ + if not hasattr(lexer, 'tokens'): + return [] + if 'keywords' in lexer.tokens: + try: + return lexer.tokens['keywords'][0][0].words + except: + pass + keywords = [] + for vals in lexer.tokens.values(): + for val in vals: + try: + if isinstance(val[0], words): + keywords.extend(val[0].words) + else: + ini_val = val[0] + if ')\\b' in val[0] or ')(\\s+)' in val[0]: + val = re.sub(r'\\.', '', val[0]) + val = re.sub('[^0-9a-zA-Z|]+', '', val) + if '|' in ini_val: + keywords.extend(val.split('|')) + else: + keywords.append(val) + except Exception: + continue + return keywords + + +@memoize +def get_parent_until(path): + """ + Given a file path, determine the full module path + + e.g. '/usr/lib/python2.7/dist-packages/numpy/core/__init__.pyc' yields + 'numpy.core' + """ + dirname = osp.dirname(path) + try: + mod = osp.basename(path) + mod = osp.splitext(mod)[0] + imp.find_module(mod, [dirname]) + except ImportError: + return + items = [mod] + while 1: + items.append(osp.basename(dirname)) + try: + dirname = osp.dirname(dirname) + imp.find_module('__init__', [dirname + os.sep]) + except ImportError: + break + return '.'.join(reversed(items)) + + +if __name__ == '__main__': + code = 'import numpy' + test = CodeInfo('test', code, len(code) - 2) + assert test.obj == 'num' + assert test.full_obj == 'numpy' + test2 = CodeInfo('test', code, len(code) - 2) + assert test == test2 + test3 = pickle.loads(pickle.dumps(test2.__dict__)) + assert test3['full_obj'] == 'numpy' diff -Nru spyder-2.3.8+dfsg1/spyder/utils/iofuncs.py spyder-3.0.2+dfsg1/spyder/utils/iofuncs.py --- spyder-2.3.8+dfsg1/spyder/utils/iofuncs.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/iofuncs.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,501 @@ +# -*- coding:utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Input/Output Utilities + +Note: 'load' functions has to return a dictionary from which a globals() + namespace may be updated +""" + +from __future__ import print_function + +import sys +import os +import tarfile +import os.path as osp +import shutil +import warnings +import json +import inspect +import dis + +# - If pandas fails to import here (for any reason), Spyder +# will crash at startup (e.g. see Issue 2300) +# - This also prevents Spyder to start IPython kernels +# (see Issue 2456) +try: + import pandas as pd +except: + pd = None #analysis:ignore + +# Local imports +from spyder.config.base import _, get_conf_path +from spyder.py3compat import pickle, to_text_string, getcwd, PY2 + + +class MatlabStruct(dict): + """ + Matlab style struct, enhanced. + + Supports dictionary and attribute style access. Can be pickled, + and supports code completion in a REPL. + + Examples + ======== + >>> from spyder.utils.iofuncs import MatlabStruct + >>> a = MatlabStruct() + >>> a.b = 'spam' # a["b"] == 'spam' + >>> a.c["d"] = 'eggs' # a.c.d == 'eggs' + >>> print(a) + {'c': {'d': 'eggs'}, 'b': 'spam'} + + """ + def __getattr__(self, attr): + """Access the dictionary keys for unknown attributes.""" + try: + return self[attr] + except KeyError: + msg = "'MatlabStruct' object has no attribute %s" % attr + raise AttributeError(msg) + + def __getitem__(self, attr): + """ + Get a dict value; create a MatlabStruct if requesting a submember. + + Do not create a key if the attribute starts with an underscore. + """ + if attr in self.keys() or attr.startswith('_'): + return dict.__getitem__(self, attr) + frame = inspect.currentframe() + # step into the function that called us + if frame.f_back.f_back and self._is_allowed(frame.f_back.f_back): + dict.__setitem__(self, attr, MatlabStruct()) + elif self._is_allowed(frame.f_back): + dict.__setitem__(self, attr, MatlabStruct()) + return dict.__getitem__(self, attr) + + def _is_allowed(self, frame): + """Check for allowed op code in the calling frame""" + allowed = [dis.opmap['STORE_ATTR'], dis.opmap['LOAD_CONST'], + dis.opmap.get('STOP_CODE', 0)] + bytecode = frame.f_code.co_code + instruction = bytecode[frame.f_lasti + 3] + instruction = ord(instruction) if PY2 else instruction + return instruction in allowed + + __setattr__ = dict.__setitem__ + __delattr__ = dict.__delitem__ + + @property + def __dict__(self): + """Allow for code completion in a REPL""" + return self.copy() + + +def get_matlab_value(val): + """ + Extract a value from a Matlab file + + From the oct2py project, see + http://pythonhosted.org/oct2py/conversions.html + """ + import numpy as np + if not isinstance(val, np.ndarray): + return val + # check for objects + if "'|O" in str(val.dtype) or "O'" in str(val.dtype): + data = MatlabStruct() + for key in val.dtype.fields.keys(): + data[key] = get_matlab_value(val[key][0]) + return data + # handle cell arrays + if val.dtype == np.object: + if val.size == 1: + val = val[0] + if "'|O" in str(val.dtype) or "O'" in str(val.dtype): + val = get_matlab_value(val) + if isinstance(val, MatlabStruct): + return val + if val.size == 1: + val = val.flatten() + if val.dtype == np.object: + if len(val.shape) > 2: + val = val.T + val = np.array([get_matlab_value(val[i].T) + for i in range(val.shape[0])]) + if len(val.shape) > 1: + if len(val.shape) == 2: + val = val.T + try: + return val.astype(val[0][0].dtype) + except ValueError: + # dig into the cell type + for row in range(val.shape[0]): + for i in range(val[row].size): + if not np.isscalar(val[row][i]): + if val[row][i].size > 1: + val[row][i] = val[row][i].squeeze() + else: + val[row][i] = val[row][i][0] + else: + val = np.array([get_matlab_value(val[i]) + for i in range(val.size)]) + if len(val.shape) == 1 or val.shape[0] == 1 or val.shape[1] == 1: + val = val.flatten() + val = val.tolist() + elif val.size == 1: + if hasattr(val, 'flatten'): + val = val.flatten()[0] + if isinstance(val, MatlabStruct) and isinstance(val.size, MatlabStruct): + del val['size'] + del val['dtype'] + return val + + +try: + import numpy as np + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + import scipy.io as spio + except AttributeError: + # Python 2.5: warnings.catch_warnings was introduced in Python 2.6 + import scipy.io as spio # analysis:ignore + except: + spio = None + + if spio is None: + load_matlab = None + save_matlab = None + else: + def load_matlab(filename): + try: + out = spio.loadmat(filename) + for key, value in list(out.items()): + out[key] = get_matlab_value(value) + return out, None + except Exception as error: + return None, str(error) + + def save_matlab(data, filename): + try: + spio.savemat(filename, data, oned_as='row') + except Exception as error: + return str(error) +except: + load_matlab = None + save_matlab = None + + +try: + import numpy as np # analysis:ignore + + def load_array(filename): + try: + name = osp.splitext(osp.basename(filename))[0] + data = np.load(filename) + if hasattr(data, 'keys'): + return data, None + else: + return {name: data}, None + except Exception as error: + return None, str(error) + + def __save_array(data, basename, index): + """Save numpy array""" + fname = basename + '_%04d.npy' % index + np.save(fname, data) + return fname +except: + load_array = None + + +try: + from spyder.pil_patch import Image + + if sys.byteorder == 'little': + _ENDIAN = '<' + else: + _ENDIAN = '>' + DTYPES = { + "1": ('|b1', None), + "L": ('|u1', None), + "I": ('%si4' % _ENDIAN, None), + "F": ('%sf4' % _ENDIAN, None), + "I;16": ('|u2', None), + "I;16S": ('%si2' % _ENDIAN, None), + "P": ('|u1', None), + "RGB": ('|u1', 3), + "RGBX": ('|u1', 4), + "RGBA": ('|u1', 4), + "CMYK": ('|u1', 4), + "YCbCr": ('|u1', 4), + } + def __image_to_array(filename): + img = Image.open(filename) + try: + dtype, extra = DTYPES[img.mode] + except KeyError: + raise RuntimeError("%s mode is not supported" % img.mode) + shape = (img.size[1], img.size[0]) + if extra is not None: + shape += (extra,) + return np.array(img.getdata(), dtype=np.dtype(dtype)).reshape(shape) + + def load_image(filename): + try: + name = osp.splitext(osp.basename(filename))[0] + return {name: __image_to_array(filename)}, None + except Exception as error: + return None, str(error) +except: + load_image = None + + +def load_pickle(filename): + """Load a pickle file as a dictionary""" + try: + if pd: + return pd.read_pickle(filename), None + else: + with open(filename, 'rb') as fid: + data = pickle.load(fid) + return data, None + except Exception as err: + return None, str(err) + + +def load_json(filename): + """Load a json file as a dictionary""" + try: + if PY2: + args = 'rb' + else: + args = 'r' + with open(filename, args) as fid: + data = json.load(fid) + return data, None + except Exception as err: + return None, str(err) + + +def save_dictionary(data, filename): + """Save dictionary in a single file .spydata file""" + filename = osp.abspath(filename) + old_cwd = getcwd() + os.chdir(osp.dirname(filename)) + error_message = None + try: + saved_arrays = {} + if load_array is not None: + # Saving numpy arrays with np.save + arr_fname = osp.splitext(filename)[0] + for name in list(data.keys()): + if isinstance(data[name], np.ndarray) and data[name].size > 0: + # Saving arrays at data root + fname = __save_array(data[name], arr_fname, + len(saved_arrays)) + saved_arrays[(name, None)] = osp.basename(fname) + data.pop(name) + elif isinstance(data[name], (list, dict)): + # Saving arrays nested in lists or dictionaries + if isinstance(data[name], list): + iterator = enumerate(data[name]) + else: + iterator = iter(list(data[name].items())) + to_remove = [] + for index, value in iterator: + if isinstance(value, np.ndarray) and value.size > 0: + fname = __save_array(value, arr_fname, + len(saved_arrays)) + saved_arrays[(name, index)] = osp.basename(fname) + to_remove.append(index) + for index in sorted(to_remove, reverse=True): + data[name].pop(index) + if saved_arrays: + data['__saved_arrays__'] = saved_arrays + pickle_filename = osp.splitext(filename)[0]+'.pickle' + with open(pickle_filename, 'wb') as fdesc: + pickle.dump(data, fdesc, 2) + tar = tarfile.open(filename, "w") + for fname in [pickle_filename]+[fn for fn in list(saved_arrays.values())]: + tar.add(osp.basename(fname)) + os.remove(fname) + tar.close() + if saved_arrays: + data.pop('__saved_arrays__') + except (RuntimeError, pickle.PicklingError, TypeError) as error: + error_message = to_text_string(error) + os.chdir(old_cwd) + return error_message + + +def load_dictionary(filename): + """Load dictionary from .spydata file""" + filename = osp.abspath(filename) + old_cwd = getcwd() + os.chdir(osp.dirname(filename)) + data = None + error_message = None + try: + tar = tarfile.open(filename, "r") + tar.extractall() + pickle_filename = osp.splitext(filename)[0]+'.pickle' + try: + # Old format (Spyder 2.0-2.1 for Python 2) + with open(pickle_filename, 'U') as fdesc: + data = pickle.loads(fdesc.read()) + except (pickle.PickleError, TypeError, UnicodeDecodeError): + # New format (Spyder >=2.2 for Python 2 and Python 3) + with open(pickle_filename, 'rb') as fdesc: + data = pickle.loads(fdesc.read()) + saved_arrays = {} + if load_array is not None: + # Loading numpy arrays saved with np.save + try: + saved_arrays = data.pop('__saved_arrays__') + for (name, index), fname in list(saved_arrays.items()): + arr = np.load( osp.join(osp.dirname(filename), fname) ) + if index is None: + data[name] = arr + elif isinstance(data[name], dict): + data[name][index] = arr + else: + data[name].insert(index, arr) + except KeyError: + pass + for fname in [pickle_filename]+[fn for fn in list(saved_arrays.values())]: + os.remove(fname) + except (EOFError, ValueError) as error: + error_message = to_text_string(error) + os.chdir(old_cwd) + return data, error_message + + +class IOFunctions(object): + def __init__(self): + self.load_extensions = None + self.save_extensions = None + self.load_filters = None + self.save_filters = None + self.load_funcs = None + self.save_funcs = None + + def setup(self): + iofuncs = self.get_internal_funcs()+self.get_3rd_party_funcs() + load_extensions = {} + save_extensions = {} + load_funcs = {} + save_funcs = {} + load_filters = [] + save_filters = [] + load_ext = [] + for ext, name, loadfunc, savefunc in iofuncs: + filter_str = to_text_string(name + " (*%s)" % ext) + if loadfunc is not None: + load_filters.append(filter_str) + load_extensions[filter_str] = ext + load_funcs[ext] = loadfunc + load_ext.append(ext) + if savefunc is not None: + save_extensions[filter_str] = ext + save_filters.append(filter_str) + save_funcs[ext] = savefunc + load_filters.insert(0, to_text_string(_("Supported files")+" (*"+\ + " *".join(load_ext)+")")) + load_filters.append(to_text_string(_("All files (*.*)"))) + self.load_filters = "\n".join(load_filters) + self.save_filters = "\n".join(save_filters) + self.load_funcs = load_funcs + self.save_funcs = save_funcs + self.load_extensions = load_extensions + self.save_extensions = save_extensions + + def get_internal_funcs(self): + return [ + ('.spydata', _("Spyder data files"), + load_dictionary, save_dictionary), + ('.npy', _("NumPy arrays"), load_array, None), + ('.npz', _("NumPy zip arrays"), load_array, None), + ('.mat', _("Matlab files"), load_matlab, save_matlab), + ('.csv', _("CSV text files"), 'import_wizard', None), + ('.txt', _("Text files"), 'import_wizard', None), + ('.jpg', _("JPEG images"), load_image, None), + ('.png', _("PNG images"), load_image, None), + ('.gif', _("GIF images"), load_image, None), + ('.tif', _("TIFF images"), load_image, None), + ('.pkl', _("Pickle files"), load_pickle, None), + ('.pickle', _("Pickle files"), load_pickle, None), + ('.json', _("JSON files"), load_json, None), + ] + + def get_3rd_party_funcs(self): + other_funcs = [] + from spyder.otherplugins import get_spyderplugins_mods + for mod in get_spyderplugins_mods(io=True): + try: + other_funcs.append((mod.FORMAT_EXT, mod.FORMAT_NAME, + mod.FORMAT_LOAD, mod.FORMAT_SAVE)) + except AttributeError as error: + print("%s: %s" % (mod, str(error)), file=STDERR) + return other_funcs + + def save(self, data, filename): + ext = osp.splitext(filename)[1].lower() + if ext in self.save_funcs: + return self.save_funcs[ext](data, filename) + else: + return _("Unsupported file type '%s'") % ext + + def load(self, filename): + ext = osp.splitext(filename)[1].lower() + if ext in self.load_funcs: + return self.load_funcs[ext](filename) + else: + return None, _("Unsupported file type '%s'") % ext + +iofunctions = IOFunctions() +iofunctions.setup() + + +def save_auto(data, filename): + """Save data into filename, depending on file extension""" + pass + + +if __name__ == "__main__": + from spyder.py3compat import u + import datetime + testdict = {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]} + testdate = datetime.date(1945, 5, 8) + example = {'str': 'kjkj kj k j j kj k jkj', + 'unicode': u('éù'), + 'list': [1, 3, [4, 5, 6], 'kjkj', None], + 'tuple': ([1, testdate, testdict], 'kjkj', None), + 'dict': testdict, + 'float': 1.2233, + 'array': np.random.rand(4000, 400), + 'empty_array': np.array([]), + 'date': testdate, + 'datetime': datetime.datetime(1945, 5, 8), + } + import time + t0 = time.time() + save_dictionary(example, "test.spydata") + print(" Data saved in %.3f seconds" % (time.time()-t0)) + t0 = time.time() + example2, ok = load_dictionary("test.spydata") + print("Data loaded in %.3f seconds" % (time.time()-t0)) +# for key in example: +# print key, ":", example[key] == example2[key] + + a = MatlabStruct() + a.b = 'spam' + assert a["b"] == 'spam' + a.c["d"] = 'eggs' + assert a.c.d == 'eggs' + assert a == {'c': {'d': 'eggs'}, 'b': 'spam'} diff -Nru spyder-2.3.8+dfsg1/spyder/utils/ipython/__init__.py spyder-3.0.2+dfsg1/spyder/utils/ipython/__init__.py --- spyder-2.3.8+dfsg1/spyder/utils/ipython/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/ipython/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Utilities for the IPython console +""" diff -Nru spyder-2.3.8+dfsg1/spyder/utils/ipython/spyder_kernel.py spyder-3.0.2+dfsg1/spyder/utils/ipython/spyder_kernel.py --- spyder-2.3.8+dfsg1/spyder/utils/ipython/spyder_kernel.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/ipython/spyder_kernel.py 2016-11-16 16:12:04.000000000 +0000 @@ -0,0 +1,340 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Spyder kernel for Jupyter +""" + +# Standard library imports +import os + +# Third-party imports +from ipykernel.datapub import publish_data +from ipykernel.ipkernel import IPythonKernel +import ipykernel.pickleutil +from ipykernel.pickleutil import CannedObject +from ipykernel.serialize import deserialize_object + +# Check if we are running under an external interpreter +IS_EXT_INTERPRETER = os.environ.get('EXTERNAL_INTERPRETER', '').lower() == "true" + +# Local imports +if not IS_EXT_INTERPRETER: + from spyder.py3compat import is_text_string + from spyder.utils.dochelpers import isdefined, getdoc, getsource + from spyder.utils.iofuncs import iofunctions + from spyder.utils.misc import fix_reference_name + from spyder.widgets.variableexplorer.utils import (get_remote_data, + make_remote_view) +else: + # We add "spyder" to sys.path for external interpreters, so this works! + # See create_kernel_spec of plugins/ipythonconsole + from py3compat import is_text_string + from utils.dochelpers import isdefined, getdoc, getsource + from utils.iofuncs import iofunctions + from utils.misc import fix_reference_name + from widgets.variableexplorer.utils import (get_remote_data, + make_remote_view) + + +# XXX --- Disable canning for Numpy arrays for now --- +# This allows getting values between a Python 3 frontend +# and a Python 2 kernel, and viceversa, for several types of +# arrays. +# See this link for interesting ideas on how to solve this +# in the future: +# http://stackoverflow.com/q/30698004/438386 +ipykernel.pickleutil.can_map.pop('numpy.ndarray') + + +class SpyderKernel(IPythonKernel): + """Spyder kernel for Jupyter""" + + def __init__(self, *args, **kwargs): + super(SpyderKernel, self).__init__(*args, **kwargs) + + self.namespace_view_settings = {} + self._pdb_obj = None + self._pdb_step = None + + @property + def _pdb_frame(self): + """Return current Pdb frame if there is any""" + if self._pdb_obj is not None and self._pdb_obj.curframe is not None: + return self._pdb_obj.curframe + + @property + def _pdb_locals(self): + """ + Return current Pdb frame locals if available. Otherwise + return an empty dictionary + """ + if self._pdb_frame: + return self._pdb_obj.curframe_locals + else: + return {} + + # -- Public API --------------------------------------------------- + # For the Variable Explorer + def get_namespace_view(self): + """ + Return the namespace view + + This is a dictionary with the following structure + + {'a': {'color': '#800000', 'size': 1, 'type': 'str', 'view': '1'}} + + Here: + * 'a' is the variable name + * 'color' is the color used to show it + * 'size' and 'type' are self-evident + * and'view' is its value or the text shown in the last column + """ + settings = self.namespace_view_settings + if settings: + ns = self._get_current_namespace() + more_excluded_names = ['In', 'Out'] + view = make_remote_view(ns, settings, more_excluded_names) + return view + + def get_var_properties(self): + """ + Get some properties of the variables in the current + namespace + """ + settings = self.namespace_view_settings + if settings: + ns = self._get_current_namespace() + data = get_remote_data(ns, settings, mode='editable', + more_excluded_names=['In', 'Out']) + + properties = {} + for name, value in list(data.items()): + properties[name] = { + 'is_list': isinstance(value, (tuple, list)), + 'is_dict': isinstance(value, dict), + 'len': self._get_len(value), + 'is_array': self._is_array(value), + 'is_image': self._is_image(value), + 'is_data_frame': self._is_data_frame(value), + 'is_series': self._is_series(value), + 'array_shape': self._get_array_shape(value), + 'array_ndim': self._get_array_ndim(value) + } + + return properties + else: + return {} + + def get_value(self, name): + """Get the value of a variable""" + ns = self._get_current_namespace() + value = ns[name] + publish_data({'__spy_data__': value}) + + def set_value(self, name, value): + """Set the value of a variable""" + ns = self._get_reference_namespace(name) + value = deserialize_object(value)[0] + if isinstance(value, CannedObject): + value = value.get_object() + ns[name] = value + + def remove_value(self, name): + """Remove a variable""" + ns = self._get_reference_namespace(name) + ns.pop(name) + + def copy_value(self, orig_name, new_name): + """Copy a variable""" + ns = self._get_reference_namespace(orig_name) + ns[new_name] = ns[orig_name] + + def load_data(self, filename, ext): + """Load data from filename""" + glbs = self._mglobals() + + load_func = iofunctions.load_funcs[ext] + data, error_message = load_func(filename) + + if error_message: + return error_message + + for key in list(data.keys()): + new_key = fix_reference_name(key, blacklist=list(glbs.keys())) + if new_key != key: + data[new_key] = data.pop(key) + + try: + glbs.update(data) + except Exception as error: + return str(error) + + return None + + def save_namespace(self, filename): + """Save namespace into filename""" + ns = self._get_current_namespace() + settings = self.namespace_view_settings + data = get_remote_data(ns, settings, mode='picklable', + more_excluded_names=['In', 'Out']).copy() + return iofunctions.save(data, filename) + + # --- For Pdb + def get_pdb_step(self): + """Return info about pdb current frame""" + return self._pdb_step + + # --- For the Help plugin + def is_defined(self, obj, force_import=False): + """Return True if object is defined in current namespace""" + ns = self._get_current_namespace(with_magics=True) + return isdefined(obj, force_import=force_import, namespace=ns) + + def get_doc(self, objtxt): + """Get object documentation dictionary""" + obj, valid = self._eval(objtxt) + if valid: + return getdoc(obj) + + def get_source(self, objtxt): + """Get object source""" + obj, valid = self._eval(objtxt) + if valid: + return getsource(obj) + + def set_cwd(self, dirname): + """Set current working directory.""" + return os.chdir(dirname) + + # -- Private API --------------------------------------------------- + # --- For the Variable Explorer + def _get_current_namespace(self, with_magics=False): + """ + Return current namespace + + This is globals() if not debugging, or a dictionary containing + both locals() and globals() for current frame when debugging + """ + ns = {} + glbs = self._mglobals() + + if self._pdb_frame is None: + ns.update(glbs) + else: + ns.update(glbs) + ns.update(self._pdb_locals) + + # Add magics to ns so we can show help about them on the Help + # plugin + if with_magics: + line_magics = self.shell.magics_manager.magics['line'] + cell_magics = self.shell.magics_manager.magics['cell'] + ns.update(line_magics) + ns.update(cell_magics) + + return ns + + def _get_reference_namespace(self, name): + """ + Return namespace where reference name is defined + + It returns the globals() if reference has not yet been defined + """ + glbs = self._mglobals() + if self._pdb_frame is None: + return glbs + else: + lcls = self._pdb_locals + if name in lcls: + return lcls + else: + return glbs + + def _mglobals(self): + """Return current globals -- handles Pdb frames""" + if self._pdb_frame is not None: + return self._pdb_frame.f_globals + else: + return self.shell.user_ns + + def _get_len(self, var): + """Return sequence length""" + try: + return len(var) + except TypeError: + return None + + def _is_array(self, var): + """Return True if variable is a NumPy array""" + try: + import numpy + return isinstance(var, numpy.ndarray) + except ImportError: + return False + + def _is_image(self, var): + """Return True if variable is a PIL.Image image""" + try: + from PIL import Image + return isinstance(var, Image.Image) + except ImportError: + return False + + def _is_data_frame(self, var): + """Return True if variable is a DataFrame""" + try: + from pandas import DataFrame + return isinstance(var, DataFrame) + except: + return False + + def _is_series(self, var): + """Return True if variable is a Series""" + try: + from pandas import Series + return isinstance(var, Series) + except: + return False + + def _get_array_shape(self, var): + """Return array's shape""" + try: + if self._is_array(var): + return var.shape + else: + return None + except AttributeError: + return None + + def _get_array_ndim(self, var): + """Return array's ndim""" + try: + if self._is_array(var): + return var.ndim + else: + return None + except AttributeError: + return None + + # --- For Pdb + def _register_pdb_session(self, pdb_obj): + """Register Pdb session to use it later""" + self._pdb_obj = pdb_obj + + # --- For the Help plugin + def _eval(self, text): + """ + Evaluate text and return (obj, valid) + where *obj* is the object represented by *text* + and *valid* is True if object evaluation did not raise any exception + """ + assert is_text_string(text) + ns = self._get_current_namespace(with_magics=True) + try: + return eval(text, ns), True + except: + return None, False diff -Nru spyder-2.3.8+dfsg1/spyder/utils/ipython/start_kernel.py spyder-3.0.2+dfsg1/spyder/utils/ipython/start_kernel.py --- spyder-2.3.8+dfsg1/spyder/utils/ipython/start_kernel.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/ipython/start_kernel.py 2016-11-13 20:36:52.000000000 +0000 @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +File used to start kernels for the IPython Console +""" + +# Standard library imports +import os +import os.path as osp +import sys + + +# Check if we are running under an external interpreter +IS_EXT_INTERPRETER = os.environ.get('EXTERNAL_INTERPRETER', '').lower() == "true" + + +def sympy_config(mpl_backend): + """Sympy configuration""" + if mpl_backend is not None: + lines = """ +from sympy.interactive import init_session +init_session() +%matplotlib {0} +""".format(mpl_backend) + else: + lines = """ +from sympy.interactive import init_session +init_session() +""" + + return lines + + +def kernel_config(): + """Create a config object with IPython kernel options""" + from IPython.core.application import get_ipython_dir + from traitlets.config.loader import Config, load_pyconfig_files + if not IS_EXT_INTERPRETER: + from spyder.config.main import CONF + from spyder.utils.programs import is_module_installed + else: + # We add "spyder" to sys.path for external interpreters, + # so this works! + # See create_kernel_spec of plugins/ipythonconsole + from config.main import CONF + from utils.programs import is_module_installed + + # ---- IPython config ---- + try: + profile_path = osp.join(get_ipython_dir(), 'profile_default') + cfg = load_pyconfig_files(['ipython_config.py', + 'ipython_kernel_config.py'], + profile_path) + except: + cfg = Config() + + # ---- Spyder config ---- + spy_cfg = Config() + + # Until we implement Issue 1052 + spy_cfg.InteractiveShell.xmode = 'Plain' + + # Run lines of code at startup + run_lines_o = CONF.get('ipython_console', 'startup/run_lines') + if run_lines_o: + spy_cfg.IPKernelApp.exec_lines = [x.strip() for x in run_lines_o.split(',')] + else: + spy_cfg.IPKernelApp.exec_lines = [] + + # Pylab configuration + mpl_backend = None + mpl_installed = is_module_installed('matplotlib') + pylab_o = CONF.get('ipython_console', 'pylab') + + if mpl_installed and pylab_o: + # Get matplotlib backend + backend_o = CONF.get('ipython_console', 'pylab/backend') + if backend_o == 1: + if is_module_installed('PyQt5'): + auto_backend = 'qt5' + elif is_module_installed('PyQt4'): + auto_backend = 'qt4' + elif is_module_installed('_tkinter'): + auto_backend = 'tk' + else: + auto_backend = 'inline' + else: + auto_backend = '' + backends = {0: 'inline', 1: auto_backend, 2: 'qt5', 3: 'qt4', + 4: 'osx', 5: 'gtk3', 6: 'gtk', 7: 'wx', 8: 'tk'} + mpl_backend = backends[backend_o] + + # Automatically load Pylab and Numpy, or only set Matplotlib + # backend + autoload_pylab_o = CONF.get('ipython_console', 'pylab/autoload') + if autoload_pylab_o: + spy_cfg.IPKernelApp.exec_lines.append( + "%pylab {0}".format(mpl_backend)) + else: + spy_cfg.IPKernelApp.exec_lines.append( + "%matplotlib {0}".format(mpl_backend)) + + # Inline backend configuration + if mpl_backend == 'inline': + # Figure format + format_o = CONF.get('ipython_console', + 'pylab/inline/figure_format', 0) + formats = {0: 'png', 1: 'svg'} + spy_cfg.InlineBackend.figure_format = formats[format_o] + + # Resolution + spy_cfg.InlineBackend.rc = {'figure.figsize': (6.0, 4.0), + 'savefig.dpi': 72, + 'font.size': 10, + 'figure.subplot.bottom': .125, + 'figure.facecolor': 'white', + 'figure.edgecolor': 'white' + } + resolution_o = CONF.get('ipython_console', + 'pylab/inline/resolution') + spy_cfg.InlineBackend.rc['savefig.dpi'] = resolution_o + + # Figure size + width_o = float(CONF.get('ipython_console', 'pylab/inline/width')) + height_o = float(CONF.get('ipython_console', 'pylab/inline/height')) + spy_cfg.InlineBackend.rc['figure.figsize'] = (width_o, height_o) + + # Run a file at startup + use_file_o = CONF.get('ipython_console', 'startup/use_run_file') + run_file_o = CONF.get('ipython_console', 'startup/run_file') + if use_file_o and run_file_o: + spy_cfg.IPKernelApp.file_to_run = run_file_o + + # Autocall + autocall_o = CONF.get('ipython_console', 'autocall') + spy_cfg.ZMQInteractiveShell.autocall = autocall_o + + # To handle the banner by ourselves in IPython 3+ + spy_cfg.ZMQInteractiveShell.banner1 = '' + + # Greedy completer + greedy_o = CONF.get('ipython_console', 'greedy_completer') + spy_cfg.IPCompleter.greedy = greedy_o + + # Sympy loading + sympy_o = CONF.get('ipython_console', 'symbolic_math') + if sympy_o and is_module_installed('sympy'): + lines = sympy_config(mpl_backend) + spy_cfg.IPKernelApp.exec_lines.append(lines) + + # Merge IPython and Spyder configs. Spyder prefs will have prevalence + # over IPython ones + cfg._merge(spy_cfg) + return cfg + + +def varexp(line): + """ + Spyder's variable explorer magic + + Used to generate plots, histograms and images of the variables displayed + on it. + """ + ip = get_ipython() #analysis:ignore + funcname, name = line.split() + import spyder.pyplot + __fig__ = spyder.pyplot.figure(); + __items__ = getattr(spyder.pyplot, funcname[2:])(ip.user_ns[name]) + spyder.pyplot.show() + del __fig__, __items__ + + +def main(): + # Remove this module's path from sys.path: + try: + sys.path.remove(osp.dirname(__file__)) + except ValueError: + pass + + try: + locals().pop('__file__') + except KeyError: + pass + __doc__ = '' + __name__ = '__main__' + + # Add current directory to sys.path (like for any standard Python interpreter + # executed in interactive mode): + sys.path.insert(0, '') + + # Fire up the kernel instance. + from ipykernel.kernelapp import IPKernelApp + + if not IS_EXT_INTERPRETER: + from spyder.utils.ipython.spyder_kernel import SpyderKernel + else: + # We add "spyder" to sys.path for external interpreters, + # so this works! + # See create_kernel_spec of plugins/ipythonconsole + from utils.ipython.spyder_kernel import SpyderKernel + + kernel = IPKernelApp.instance() + kernel.kernel_class = SpyderKernel + try: + kernel.config = kernel_config() + except: + pass + kernel.initialize() + + # NOTE: Leave this and other magic modifications *after* setting + # __ipythonkernel__ to not have problems while starting kernels + kernel.shell.register_magic_function(varexp) + + # Start the (infinite) kernel event loop. + kernel.start() + + +if __name__ == '__main__': + main() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/ipython/templates/blank.html spyder-3.0.2+dfsg1/spyder/utils/ipython/templates/blank.html --- spyder-2.3.8+dfsg1/spyder/utils/ipython/templates/blank.html 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/ipython/templates/blank.html 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,20 @@ + + + + + + + + + + + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/ipython/templates/kernel_error.html spyder-3.0.2+dfsg1/spyder/utils/ipython/templates/kernel_error.html --- spyder-2.3.8+dfsg1/spyder/utils/ipython/templates/kernel_error.html 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/ipython/templates/kernel_error.html 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,32 @@ + + + + + + + + + + + + +
    +
    +
    ${message}
    +
    +
    + ${error} +
    +
    + + + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/ipython/templates/loading.html spyder-3.0.2+dfsg1/spyder/utils/ipython/templates/loading.html --- spyder-2.3.8+dfsg1/spyder/utils/ipython/templates/loading.html 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/ipython/templates/loading.html 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + +
    +
    +
    ${message}
    +
    + + + diff -Nru spyder-2.3.8+dfsg1/spyder/utils/misc.py spyder-3.0.2+dfsg1/spyder/utils/misc.py --- spyder-2.3.8+dfsg1/spyder/utils/misc.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/misc.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,301 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Miscellaneous utilities""" + +import functools +import os +import os.path as osp +import sys +import stat + +from spyder.py3compat import is_text_string + + +def __remove_pyc_pyo(fname): + """Eventually remove .pyc and .pyo files associated to a Python script""" + if osp.splitext(fname)[1] == '.py': + for ending in ('c', 'o'): + if osp.exists(fname+ending): + os.remove(fname+ending) + + +def rename_file(source, dest): + """ + Rename file from *source* to *dest* + If file is a Python script, also rename .pyc and .pyo files if any + """ + os.rename(source, dest) + __remove_pyc_pyo(source) + + +def remove_file(fname): + """ + Remove file *fname* + If file is a Python script, also rename .pyc and .pyo files if any + """ + os.remove(fname) + __remove_pyc_pyo(fname) + + +def move_file(source, dest): + """ + Move file from *source* to *dest* + If file is a Python script, also rename .pyc and .pyo files if any + """ + import shutil + shutil.copy(source, dest) + remove_file(source) + + +def onerror(function, path, excinfo): + """Error handler for `shutil.rmtree`. + + If the error is due to an access error (read-only file), it + attempts to add write permission and then retries. + If the error is for another reason, it re-raises the error. + + Usage: `shutil.rmtree(path, onerror=onerror)""" + if not os.access(path, os.W_OK): + # Is the error an access error? + os.chmod(path, stat.S_IWUSR) + function(path) + else: + raise + + +def select_port(default_port=20128): + """Find and return a non used port""" + import socket + while True: + try: + sock = socket.socket(socket.AF_INET, + socket.SOCK_STREAM, + socket.IPPROTO_TCP) +# sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind( ("127.0.0.1", default_port) ) + except socket.error as _msg: # analysis:ignore + default_port += 1 + else: + break + finally: + sock.close() + sock = None + return default_port + + +def count_lines(path, extensions=None, excluded_dirnames=None): + """Return number of source code lines for all filenames in subdirectories + of *path* with names ending with *extensions* + Directory names *excluded_dirnames* will be ignored""" + if extensions is None: + extensions = ['.py', '.pyw', '.ipy', '.enaml', '.c', '.h', '.cpp', + '.hpp', '.inc', '.', '.hh', '.hxx', '.cc', '.cxx', + '.cl', '.f', '.for', '.f77', '.f90', '.f95', '.f2k'] + if excluded_dirnames is None: + excluded_dirnames = ['build', 'dist', '.hg', '.svn'] + def get_filelines(path): + dfiles, dlines = 0, 0 + if osp.splitext(path)[1] in extensions: + dfiles = 1 + with open(path, 'rb') as textfile: + dlines = len(textfile.read().strip().splitlines()) + return dfiles, dlines + lines = 0 + files = 0 + if osp.isdir(path): + for dirpath, dirnames, filenames in os.walk(path): + for d in dirnames[:]: + if d in excluded_dirnames: + dirnames.remove(d) + if excluded_dirnames is None or \ + osp.dirname(dirpath) not in excluded_dirnames: + for fname in filenames: + dfiles, dlines = get_filelines(osp.join(dirpath, fname)) + files += dfiles + lines += dlines + else: + dfiles, dlines = get_filelines(path) + files += dfiles + lines += dlines + return files, lines + + +def fix_reference_name(name, blacklist=None): + """Return a syntax-valid Python reference name from an arbitrary name""" + import re + name = "".join(re.split(r'[^0-9a-zA-Z_]', name)) + while name and not re.match(r'([a-zA-Z]+[0-9a-zA-Z_]*)$', name): + if not re.match(r'[a-zA-Z]', name[0]): + name = name[1:] + continue + name = str(name) + if not name: + name = "data" + if blacklist is not None and name in blacklist: + get_new_name = lambda index: name+('%03d' % index) + index = 0 + while get_new_name(index) in blacklist: + index += 1 + name = get_new_name(index) + return name + + +def remove_backslashes(path): + """Remove backslashes in *path* + + For Windows platforms only. + Returns the path unchanged on other platforms. + + This is especially useful when formatting path strings on + Windows platforms for which folder paths may contain backslashes + and provoke unicode decoding errors in Python 3 (or in Python 2 + when future 'unicode_literals' symbol has been imported).""" + if os.name == 'nt': + # Removing trailing single backslash + if path.endswith('\\') and not path.endswith('\\\\'): + path = path[:-1] + # Replacing backslashes by slashes + path = path.replace('\\', '/') + return path + + +def get_error_match(text): + """Return error match""" + import re + return re.match(r' File "(.*)", line (\d*)', text) + + +def get_python_executable(): + """Return path to Python executable""" + executable = sys.executable.replace("pythonw.exe", "python.exe") + if executable.endswith("spyder.exe"): + # py2exe distribution + executable = "python.exe" + return executable + + +def monkeypatch_method(cls, patch_name): + # This function's code was inspired from the following thread: + # "[Python-Dev] Monkeypatching idioms -- elegant or ugly?" + # by Robert Brewer + # (Tue Jan 15 19:13:25 CET 2008) + """ + Add the decorated method to the given class; replace as needed. + + If the named method already exists on the given class, it will + be replaced, and a reference to the old method is created as + cls._old. If the "_old__" attribute + already exists, KeyError is raised. + """ + def decorator(func): + fname = func.__name__ + old_func = getattr(cls, fname, None) + if old_func is not None: + # Add the old func to a list of old funcs. + old_ref = "_old_%s_%s" % (patch_name, fname) + #print old_ref, old_func + old_attr = getattr(cls, old_ref, None) + if old_attr is None: + setattr(cls, old_ref, old_func) + else: + raise KeyError("%s.%s already exists." + % (cls.__name__, old_ref)) + setattr(cls, fname, func) + return func + return decorator + + +def is_python_script(fname): + """Is it a valid Python script?""" + return osp.isfile(fname) and fname.endswith(('.py', '.pyw', '.ipy')) + + +def abspardir(path): + """Return absolute parent dir""" + return osp.abspath(osp.join(path, os.pardir)) + + +def get_common_path(pathlist): + """Return common path for all paths in pathlist""" + common = osp.normpath(osp.commonprefix(pathlist)) + if len(common) > 1: + if not osp.isdir(common): + return abspardir(common) + else: + for path in pathlist: + if not osp.isdir(osp.join(common, path[len(common)+1:])): + # `common` is not the real common prefix + return abspardir(common) + else: + return osp.abspath(common) + + +def add_pathlist_to_PYTHONPATH(env, pathlist, drop_env=False, + ipyconsole=False): + # PyQt API 1/2 compatibility-related tests: + assert isinstance(env, list) + assert all([is_text_string(path) for path in env]) + + pypath = "PYTHONPATH" + pathstr = os.pathsep.join(pathlist) + if os.environ.get(pypath) is not None and not drop_env: + old_pypath = os.environ[pypath] + if not ipyconsole: + for index, var in enumerate(env[:]): + if var.startswith(pypath+'='): + env[index] = var.replace(pypath+'=', + pypath+'='+pathstr+os.pathsep) + env.append('OLD_PYTHONPATH='+old_pypath) + else: + pypath = {'PYTHONPATH': pathstr + os.pathsep + old_pypath, + 'OLD_PYTHONPATH': old_pypath} + return pypath + else: + if not ipyconsole: + env.append(pypath+'='+pathstr) + else: + return {'PYTHONPATH': pathstr} + + +def memoize(obj): + """ + Memoize objects to trade memory for execution speed + + Use a limited size cache to store the value, which takes into account + The calling args and kwargs + + See https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize + """ + cache = obj.cache = {} + + @functools.wraps(obj) + def memoizer(*args, **kwargs): + key = str(args) + str(kwargs) + if key not in cache: + cache[key] = obj(*args, **kwargs) + # only keep the most recent 100 entries + if len(cache) > 100: + cache.popitem(last=False) + return cache[key] + return memoizer + + +if __name__ == '__main__': + if os.name == 'nt': + assert get_common_path([ + 'D:\\Python\\spyder-v21\\spyder\\widgets', + 'D:\\Python\\spyder\\spyder\\utils', + 'D:\\Python\\spyder\\spyder\\widgets', + 'D:\\Python\\spyder-v21\\spyder\\utils', + ]) == 'D:\\Python' + else: + assert get_common_path([ + '/Python/spyder-v21/spyder.widgets', + '/Python/spyder/spyder.utils', + '/Python/spyder/spyder.widgets', + '/Python/spyder-v21/spyder.utils', + ]) == '/Python' diff -Nru spyder-2.3.8+dfsg1/spyder/utils/programs.py spyder-3.0.2+dfsg1/spyder/utils/programs.py --- spyder-2.3.8+dfsg1/spyder/utils/programs.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/programs.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,480 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Running programs utilities""" + +from __future__ import print_function + +from distutils.version import LooseVersion +import imp +import inspect +import os +import os.path as osp +import re +import subprocess +import sys +import tempfile + +# Local imports +from spyder.utils import encoding +from spyder.py3compat import PY2, is_text_string + + +class ProgramError(Exception): + pass + + +if os.name == 'nt': + TEMPDIR = tempfile.gettempdir() + osp.sep + 'spyder' +else: + username = encoding.to_unicode_from_fs(os.environ.get('USER')) + TEMPDIR = tempfile.gettempdir() + osp.sep + 'spyder-' + username + + +def is_program_installed(basename): + """ + Return program absolute path if installed in PATH. + + Otherwise, return None + """ + for path in os.environ["PATH"].split(os.pathsep): + abspath = osp.join(path, basename) + if osp.isfile(abspath): + return abspath + + +def find_program(basename): + """ + Find program in PATH and return absolute path + + Try adding .exe or .bat to basename on Windows platforms + (return None if not found) + """ + names = [basename] + if os.name == 'nt': + # Windows platforms + extensions = ('.exe', '.bat', '.cmd') + if not basename.endswith(extensions): + names = [basename+ext for ext in extensions]+[basename] + for name in names: + path = is_program_installed(name) + if path: + return path + + +def alter_subprocess_kwargs_by_platform(**kwargs): + """ + Given a dict, populate kwargs to create a generally + useful default setup for running subprocess processes + on different platforms. For example, `close_fds` is + set on posix and creation of a new console window is + disabled on Windows. + + This function will alter the given kwargs and return + the modified dict. + """ + kwargs.setdefault('close_fds', os.name == 'posix') + if os.name == 'nt': + CONSOLE_CREATION_FLAGS = 0 # Default value + # See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863%28v=vs.85%29.aspx + CREATE_NO_WINDOW = 0x08000000 + # We "or" them together + CONSOLE_CREATION_FLAGS |= CREATE_NO_WINDOW + kwargs.setdefault('creationflags', CONSOLE_CREATION_FLAGS) + return kwargs + + +def run_shell_command(cmdstr, **subprocess_kwargs): + """ + Execute the given shell command. + + Note that *args and **kwargs will be passed to the subprocess call. + + If 'shell' is given in subprocess_kwargs it must be True, + otherwise ProgramError will be raised. + . + If 'executable' is not given in subprocess_kwargs, it will + be set to the value of the SHELL environment variable. + + Note that stdin, stdout and stderr will be set by default + to PIPE unless specified in subprocess_kwargs. + + :str cmdstr: The string run as a shell command. + :subprocess_kwargs: These will be passed to subprocess.Popen. + """ + if 'shell' in subprocess_kwargs and not subprocess_kwargs['shell']: + raise ProgramError( + 'The "shell" kwarg may be omitted, but if ' + 'provided it must be True.') + else: + subprocess_kwargs['shell'] = True + + if 'executable' not in subprocess_kwargs: + subprocess_kwargs['executable'] = os.getenv('SHELL') + + for stream in ['stdin', 'stdout', 'stderr']: + subprocess_kwargs.setdefault(stream, subprocess.PIPE) + subprocess_kwargs = alter_subprocess_kwargs_by_platform( + **subprocess_kwargs) + return subprocess.Popen(cmdstr, **subprocess_kwargs) + + +def run_program(program, args=None, **subprocess_kwargs): + """ + Run program in a separate process. + + NOTE: returns the process object created by + `subprocess.Popen()`. This can be used with + `proc.communicate()` for example. + + If 'shell' appears in the kwargs, it must be False, + otherwise ProgramError will be raised. + + If only the program name is given and not the full path, + a lookup will be performed to find the program. If the + lookup fails, ProgramError will be raised. + + Note that stdin, stdout and stderr will be set by default + to PIPE unless specified in subprocess_kwargs. + + :str program: The name of the program to run. + :list args: The program arguments. + :subprocess_kwargs: These will be passed to subprocess.Popen. + """ + if 'shell' in subprocess_kwargs and subprocess_kwargs['shell']: + raise ProgramError( + "This function is only for non-shell programs, " + "use run_shell_command() instead.") + fullcmd = find_program(program) + if not fullcmd: + raise ProgramError("Program %s was not found" % program) + # As per subprocess, we make a complete list of prog+args + fullcmd = [fullcmd] + (args or []) + for stream in ['stdin', 'stdout', 'stderr']: + subprocess_kwargs.setdefault(stream, subprocess.PIPE) + subprocess_kwargs = alter_subprocess_kwargs_by_platform( + **subprocess_kwargs) + return subprocess.Popen(fullcmd, **subprocess_kwargs) + + +def start_file(filename): + """ + Generalized os.startfile for all platforms supported by Qt + + This function is simply wrapping QDesktopServices.openUrl + + Returns True if successfull, otherwise returns False. + """ + from qtpy.QtCore import QUrl + from qtpy.QtGui import QDesktopServices + + # We need to use setUrl instead of setPath because this is the only + # cross-platform way to open external files. setPath fails completely on + # Mac and doesn't open non-ascii files on Linux. + # Fixes Issue 740 + url = QUrl() + url.setUrl(filename) + return QDesktopServices.openUrl(url) + + +def python_script_exists(package=None, module=None): + """ + Return absolute path if Python script exists (otherwise, return None) + package=None -> module is in sys.path (standard library modules) + """ + assert module is not None + try: + if package is None: + path = imp.find_module(module)[1] + else: + path = osp.join(imp.find_module(package)[1], module)+'.py' + except ImportError: + return + if not osp.isfile(path): + path += 'w' + if osp.isfile(path): + return path + + +def run_python_script(package=None, module=None, args=[], p_args=[]): + """ + Run Python script in a separate process + package=None -> module is in sys.path (standard library modules) + """ + assert module is not None + assert isinstance(args, (tuple, list)) and isinstance(p_args, (tuple, list)) + path = python_script_exists(package, module) + run_program(sys.executable, p_args + [path] + args) + + +def shell_split(text): + """ + Split the string `text` using shell-like syntax + + This avoids breaking single/double-quoted strings (e.g. containing + strings with spaces). This function is almost equivalent to the shlex.split + function (see standard library `shlex`) except that it is supporting + unicode strings (shlex does not support unicode until Python 2.7.3). + """ + assert is_text_string(text) # in case a QString is passed... + pattern = r'(\s+|(?': + return LooseVersion(actver) > LooseVersion(version) + elif cmp_op == '>=': + return LooseVersion(actver) >= LooseVersion(version) + elif cmp_op == '=': + return LooseVersion(actver) == LooseVersion(version) + elif cmp_op == '<': + return LooseVersion(actver) < LooseVersion(version) + elif cmp_op == '<=': + return LooseVersion(actver) <= LooseVersion(version) + else: + return False + except TypeError: + return True + + +def get_module_version(module_name): + """Return module version or None if version can't be retrieved.""" + mod = __import__(module_name) + return getattr(mod, '__version__', getattr(mod, 'VERSION', None)) + + +def is_module_installed(module_name, version=None, installed_version=None, + interpreter=None): + """ + Return True if module *module_name* is installed + + If version is not None, checking module version + (module must have an attribute named '__version__') + + version may starts with =, >=, > or < to specify the exact requirement ; + multiple conditions may be separated by ';' (e.g. '>=0.13;<1.0') + + interpreter: check if a module is installed with a given version + in a determined interpreter + """ + if interpreter: + if not osp.isdir(TEMPDIR): + os.mkdir(TEMPDIR) + + if osp.isfile(interpreter) and ('python' in interpreter): + checkver = inspect.getsource(check_version) + get_modver = inspect.getsource(get_module_version) + stable_ver = inspect.getsource(is_stable_version) + ismod_inst = inspect.getsource(is_module_installed) + fd, script = tempfile.mkstemp(suffix='.py', dir=TEMPDIR) + with os.fdopen(fd, 'w') as f: + f.write("# -*- coding: utf-8 -*-" + "\n\n") + f.write("from distutils.version import LooseVersion" + "\n") + f.write("import re" + "\n\n") + f.write(stable_ver + "\n") + f.write(checkver + "\n") + f.write(get_modver + "\n") + f.write(ismod_inst + "\n") + if version: + f.write("print(is_module_installed('%s','%s'))"\ + % (module_name, version)) + else: + f.write("print(is_module_installed('%s'))" % module_name) + try: + proc = run_program(interpreter, [script]) + output, _err = proc.communicate() + except subprocess.CalledProcessError: + return True + if output: # TODO: Check why output could be empty! + return eval(output.decode()) + else: + return False + else: + # Try to not take a wrong decision if there is no interpreter + # available (needed for the change_pystartup method of ExtConsole + # config page) + return True + else: + if installed_version is None: + try: + actver = get_module_version(module_name) + except ImportError: + # Module is not installed + return False + else: + actver = installed_version + if actver is None and version is not None: + return False + elif version is None: + return True + else: + if ';' in version: + output = True + for ver in version.split(';'): + output = output and is_module_installed(module_name, ver) + return output + match = re.search('[0-9]', version) + assert match is not None, "Invalid version number" + symb = version[:match.start()] + if not symb: + symb = '=' + assert symb in ('>=', '>', '=', '<', '<='),\ + "Invalid version condition '%s'" % symb + version = version[match.start():] + + return check_version(actver, version, symb) + + +def test_programs(): + assert find_program('git') + assert shell_split('-q -o -a') == ['-q', '-o', '-a'] + assert shell_split('-q "d:\\Python de xxxx\\t.txt" -o -a') == \ + ['-q', 'd:\\Python de xxxx\\t.txt', '-o', '-a'] + assert check_version('0.9.4-1', '0.9.4', '>=') + assert check_version('3.0.0rc1', '3.0.0', '<') + assert check_version('1.0', '1.0b2', '>') + assert is_module_installed('qtconsole', '>=4.0') + assert not is_module_installed('IPython', '>=1.0;<3.0') + assert is_module_installed('jedi', '>=0.7.0') + + +if __name__ == '__main__': + test_programs() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/qthelpers.py spyder-3.0.2+dfsg1/spyder/utils/qthelpers.py --- spyder-2.3.8+dfsg1/spyder/utils/qthelpers.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/qthelpers.py 2016-11-16 15:40:01.000000000 +0000 @@ -0,0 +1,503 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Qt utilities""" + +# Standard library imports +import os +import os.path as osp +import re +import sys + +# Third party imports +from qtpy.compat import to_qvariant, from_qvariant +from qtpy.QtCore import (QEvent, QLibraryInfo, QLocale, QObject, Qt, QTimer, + QTranslator, Signal, Slot) +from qtpy.QtGui import QIcon, QKeyEvent, QKeySequence, QPixmap +from qtpy.QtWidgets import (QAction, QApplication, QHBoxLayout, QLabel, + QLineEdit, QMenu, QStyle, QToolBar, QToolButton, + QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import get_image_path, running_in_mac_app +from spyder.config.gui import get_shortcut +from spyder.utils import programs +from spyder.utils import icon_manager as ima +from spyder.utils.icon_manager import get_icon, get_std_icon +from spyder.py3compat import is_text_string, to_text_string + +# Note: How to redirect a signal from widget *a* to widget *b* ? +# ---- +# It has to be done manually: +# * typing 'SIGNAL("clicked()")' works +# * typing 'signalstr = "clicked()"; SIGNAL(signalstr)' won't work +# Here is an example of how to do it: +# (self.listwidget is widget *a* and self is widget *b*) +# self.connect(self.listwidget, SIGNAL('option_changed'), +# lambda *args: self.emit(SIGNAL('option_changed'), *args)) + + + +def get_image_label(name, default="not_found.png"): + """Return image inside a QLabel object""" + label = QLabel() + label.setPixmap(QPixmap(get_image_path(name, default))) + return label + + +class MacApplication(QApplication): + """Subclass to be able to open external files with our Mac app""" + sig_open_external_file = Signal(str) + + def __init__(self, *args): + QApplication.__init__(self, *args) + + def event(self, event): + if event.type() == QEvent.FileOpen: + fname = str(event.file()) + self.sig_open_external_file.emit(fname) + return QApplication.event(self, event) + + +def qapplication(translate=True, test_time=3): + """ + Return QApplication instance + Creates it if it doesn't already exist + + test_time: Time to maintain open the application when testing. It's given + in seconds + """ + if running_in_mac_app(): + SpyderApplication = MacApplication + else: + SpyderApplication = QApplication + + app = SpyderApplication.instance() + if app is None: + # Set Application name for Gnome 3 + # https://groups.google.com/forum/#!topic/pyside/24qxvwfrRDs + app = SpyderApplication(['Spyder']) + + # Set application name for KDE (See issue 2207) + app.setApplicationName('Spyder') + if translate: + install_translator(app) + + test_ci = os.environ.get('TEST_CI_WIDGETS', None) + if test_ci is not None: + timer_shutdown = QTimer(app) + timer_shutdown.timeout.connect(app.quit) + timer_shutdown.start(test_time*1000) + return app + + +def file_uri(fname): + """Select the right file uri scheme according to the operating system""" + if os.name == 'nt': + # Local file + if re.search(r'^[a-zA-Z]:', fname): + return 'file:///' + fname + # UNC based path + else: + return 'file://' + fname + else: + return 'file://' + fname + + +QT_TRANSLATOR = None +def install_translator(qapp): + """Install Qt translator to the QApplication instance""" + global QT_TRANSLATOR + if QT_TRANSLATOR is None: + qt_translator = QTranslator() + if qt_translator.load("qt_"+QLocale.system().name(), + QLibraryInfo.location(QLibraryInfo.TranslationsPath)): + QT_TRANSLATOR = qt_translator # Keep reference alive + if QT_TRANSLATOR is not None: + qapp.installTranslator(QT_TRANSLATOR) + + +def keybinding(attr): + """Return keybinding""" + ks = getattr(QKeySequence, attr) + return from_qvariant(QKeySequence.keyBindings(ks)[0], str) + + +def _process_mime_path(path, extlist): + if path.startswith(r"file://"): + if os.name == 'nt': + # On Windows platforms, a local path reads: file:///c:/... + # and a UNC based path reads like: file://server/share + if path.startswith(r"file:///"): # this is a local path + path=path[8:] + else: # this is a unc path + path = path[5:] + else: + path = path[7:] + path = path.replace('%5C' , os.sep) # Transforming backslashes + if osp.exists(path): + if extlist is None or osp.splitext(path)[1] in extlist: + return path + + +def mimedata2url(source, extlist=None): + """ + Extract url list from MIME data + extlist: for example ('.py', '.pyw') + """ + pathlist = [] + if source.hasUrls(): + for url in source.urls(): + path = _process_mime_path(to_text_string(url.toString()), extlist) + if path is not None: + pathlist.append(path) + elif source.hasText(): + for rawpath in to_text_string(source.text()).splitlines(): + path = _process_mime_path(rawpath, extlist) + if path is not None: + pathlist.append(path) + if pathlist: + return pathlist + + +def keyevent2tuple(event): + """Convert QKeyEvent instance into a tuple""" + return (event.type(), event.key(), event.modifiers(), event.text(), + event.isAutoRepeat(), event.count()) + + +def tuple2keyevent(past_event): + """Convert tuple into a QKeyEvent instance""" + return QKeyEvent(*past_event) + + +def restore_keyevent(event): + if isinstance(event, tuple): + _, key, modifiers, text, _, _ = event + event = tuple2keyevent(event) + else: + text = event.text() + modifiers = event.modifiers() + key = event.key() + ctrl = modifiers & Qt.ControlModifier + shift = modifiers & Qt.ShiftModifier + return event, text, key, ctrl, shift + + +def create_toolbutton(parent, text=None, shortcut=None, icon=None, tip=None, + toggled=None, triggered=None, + autoraise=True, text_beside_icon=False): + """Create a QToolButton""" + button = QToolButton(parent) + if text is not None: + button.setText(text) + if icon is not None: + if is_text_string(icon): + icon = get_icon(icon) + button.setIcon(icon) + if text is not None or tip is not None: + button.setToolTip(text if tip is None else tip) + if text_beside_icon: + button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + button.setAutoRaise(autoraise) + if triggered is not None: + button.clicked.connect(triggered) + if toggled is not None: + button.toggled.connect(toggled) + button.setCheckable(True) + if shortcut is not None: + button.setShortcut(shortcut) + return button + + +def action2button(action, autoraise=True, text_beside_icon=False, parent=None): + """Create a QToolButton directly from a QAction object""" + if parent is None: + parent = action.parent() + button = QToolButton(parent) + button.setDefaultAction(action) + button.setAutoRaise(autoraise) + if text_beside_icon: + button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + return button + + +def toggle_actions(actions, enable): + """Enable/disable actions""" + if actions is not None: + for action in actions: + if action is not None: + action.setEnabled(enable) + + +def create_action(parent, text, shortcut=None, icon=None, tip=None, + toggled=None, triggered=None, data=None, menurole=None, + context=Qt.WindowShortcut): + """Create a QAction""" + action = SpyderAction(text, parent) + if triggered is not None: + action.triggered.connect(triggered) + if toggled is not None: + action.toggled.connect(toggled) + action.setCheckable(True) + if icon is not None: + if is_text_string(icon): + icon = get_icon(icon) + action.setIcon(icon) + if tip is not None: + action.setToolTip(tip) + action.setStatusTip(tip) + if data is not None: + action.setData(to_qvariant(data)) + if menurole is not None: + action.setMenuRole(menurole) + + # Workround for Mac because setting context=Qt.WidgetShortcut + # there doesn't have any effect + if sys.platform == 'darwin': + action._shown_shortcut = None + if context == Qt.WidgetShortcut: + if shortcut is not None: + action._shown_shortcut = shortcut + else: + # This is going to be filled by + # main.register_shortcut + action._shown_shortcut = 'missing' + else: + if shortcut is not None: + action.setShortcut(shortcut) + action.setShortcutContext(context) + else: + if shortcut is not None: + action.setShortcut(shortcut) + action.setShortcutContext(context) + + return action + + +def add_shortcut_to_tooltip(action, context, name): + """Add the shortcut associated with a given action to its tooltip""" + action.setToolTip(action.toolTip() + ' (%s)' % + get_shortcut(context=context, name=name)) + + +def add_actions(target, actions, insert_before=None): + """Add actions to a QMenu or a QToolBar.""" + previous_action = None + target_actions = list(target.actions()) + if target_actions: + previous_action = target_actions[-1] + if previous_action.isSeparator(): + previous_action = None + for action in actions: + if (action is None) and (previous_action is not None): + if insert_before is None: + target.addSeparator() + else: + target.insertSeparator(insert_before) + elif isinstance(action, QMenu): + if insert_before is None: + target.addMenu(action) + else: + target.insertMenu(insert_before, action) + elif isinstance(action, SpyderAction): + if isinstance(target, QMenu) or not isinstance(target, QToolBar): + action = action.no_icon_action + if insert_before is None: + target.addAction(action) + else: + target.insertAction(insert_before, action) + previous_action = action + + +def get_item_user_text(item): + """Get QTreeWidgetItem user role string""" + return from_qvariant(item.data(0, Qt.UserRole), to_text_string) + + +def set_item_user_text(item, text): + """Set QTreeWidgetItem user role string""" + item.setData(0, Qt.UserRole, to_qvariant(text)) + + +def create_bookmark_action(parent, url, title, icon=None, shortcut=None): + """Create bookmark action""" + + @Slot() + def open_url(): + return programs.start_file(url) + + return create_action( parent, title, shortcut=shortcut, icon=icon, + triggered=open_url) + + +def create_module_bookmark_actions(parent, bookmarks): + """ + Create bookmark actions depending on module installation: + bookmarks = ((module_name, url, title), ...) + """ + actions = [] + for key, url, title in bookmarks: + # Create actions for scientific distros only if Spyder is installed + # under them + create_act = True + if key == 'xy' or key == 'winpython': + if not programs.is_module_installed(key): + create_act = False + if create_act: + act = create_bookmark_action(parent, url, title) + actions.append(act) + return actions + + +def create_program_action(parent, text, name, icon=None, nt_name=None): + """Create action to run a program""" + if is_text_string(icon): + icon = get_icon(icon) + if os.name == 'nt' and nt_name is not None: + name = nt_name + path = programs.find_program(name) + if path is not None: + return create_action(parent, text, icon=icon, + triggered=lambda: programs.run_program(name)) + + +def create_python_script_action(parent, text, icon, package, module, args=[]): + """Create action to run a GUI based Python script""" + if is_text_string(icon): + icon = get_icon(icon) + if programs.python_script_exists(package, module): + return create_action(parent, text, icon=icon, + triggered=lambda: + programs.run_python_script(package, module, args)) + + +class DialogManager(QObject): + """ + Object that keep references to non-modal dialog boxes for another QObject, + typically a QMainWindow or any kind of QWidget + """ + def __init__(self): + QObject.__init__(self) + self.dialogs = {} + + def show(self, dialog): + """Generic method to show a non-modal dialog and keep reference + to the Qt C++ object""" + for dlg in list(self.dialogs.values()): + if to_text_string(dlg.windowTitle()) \ + == to_text_string(dialog.windowTitle()): + dlg.show() + dlg.raise_() + break + else: + dialog.show() + self.dialogs[id(dialog)] = dialog + dialog.accepted.connect( + lambda eid=id(dialog): self.dialog_finished(eid)) + dialog.rejected.connect( + lambda eid=id(dialog): self.dialog_finished(eid)) + + def dialog_finished(self, dialog_id): + """Manage non-modal dialog boxes""" + return self.dialogs.pop(dialog_id) + + def close_all(self): + """Close all opened dialog boxes""" + for dlg in list(self.dialogs.values()): + dlg.reject() + + +def get_filetype_icon(fname): + """Return file type icon""" + ext = osp.splitext(fname)[1] + if ext.startswith('.'): + ext = ext[1:] + return get_icon( "%s.png" % ext, ima.icon('FileIcon') ) + + +class SpyderAction(QAction): + """Spyder QAction class wrapper to handle cross platform patches.""" + + def __init__(self, *args, **kwargs): + """Spyder QAction class wrapper to handle cross platform patches.""" + super(SpyderAction, self).__init__(*args, **kwargs) + self._action_no_icon = None + + if sys.platform == 'darwin': + self._action_no_icon = QAction(*args, **kwargs) + self._action_no_icon.setIcon(QIcon()) + self._action_no_icon.triggered.connect(self.triggered) + self._action_no_icon.toggled.connect(self.toggled) + self._action_no_icon.changed.connect(self.changed) + self._action_no_icon.hovered.connect(self.hovered) + else: + self._action_no_icon = self + + def __getattribute__(self, name): + """Intercept method calls and apply to both actions, except signals.""" + attr = super(SpyderAction, self).__getattribute__(name) + + if hasattr(attr, '__call__') and name not in ['triggered', 'toggled', + 'changed', 'hovered']: + def newfunc(*args, **kwargs): + result = attr(*args, **kwargs) + if name not in ['setIcon']: + action_no_icon = self.__dict__['_action_no_icon'] + attr_no_icon = super(QAction, + action_no_icon).__getattribute__(name) + attr_no_icon(*args, **kwargs) + return result + return newfunc + else: + return attr + + @property + def no_icon_action(self): + """Return the action without an Icon.""" + return self._action_no_icon + + +class ShowStdIcons(QWidget): + """ + Dialog showing standard icons + """ + def __init__(self, parent): + QWidget.__init__(self, parent) + layout = QHBoxLayout() + row_nb = 14 + cindex = 0 + for child in dir(QStyle): + if child.startswith('SP_'): + if cindex == 0: + col_layout = QVBoxLayout() + icon_layout = QHBoxLayout() + icon = get_std_icon(child) + label = QLabel() + label.setPixmap(icon.pixmap(32, 32)) + icon_layout.addWidget( label ) + icon_layout.addWidget( QLineEdit(child.replace('SP_', '')) ) + col_layout.addLayout(icon_layout) + cindex = (cindex+1) % row_nb + if cindex == 0: + layout.addLayout(col_layout) + self.setLayout(layout) + self.setWindowTitle('Standard Platform Icons') + self.setWindowIcon(get_std_icon('TitleBarMenuButton')) + + +def show_std_icons(): + """ + Show all standard Icons + """ + app = qapplication() + dialog = ShowStdIcons(None) + dialog.show() + sys.exit(app.exec_()) + + +if __name__ == "__main__": + show_std_icons() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/site/__init__.py spyder-3.0.2+dfsg1/spyder/utils/site/__init__.py --- spyder-2.3.8+dfsg1/spyder/utils/site/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/site/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.utils.site +================= + +Site packages for Spyder consoles + +NOTE: This package shouldn't be imported at **any** place. + It's only used to set additional functionality for + our consoles. +""" diff -Nru spyder-2.3.8+dfsg1/spyder/utils/site/osx_app_site.py spyder-3.0.2+dfsg1/spyder/utils/site/osx_app_site.py --- spyder-2.3.8+dfsg1/spyder/utils/site/osx_app_site.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/site/osx_app_site.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,163 @@ +# +# IMPORTANT NOTE: Don't add a coding line here! It's not necessary for +# site files +# +# Spyder's MacOS X App site.py additions +# +# It includes missing variables and paths that are not added by +# py2app to its own site.py +# +# These functions were taken verbatim from Python 2.7.3 site.py +# + +import sys +import os +try: + import __builtin__ as builtins +except ImportError: + # Python 3 + import builtins + +# for distutils.commands.install +# These values are initialized by the getuserbase() and getusersitepackages() +# functions. +USER_SITE = None +USER_BASE = None + + +def getuserbase(): + """Returns the `user base` directory path. + + The `user base` directory can be used to store data. If the global + variable ``USER_BASE`` is not initialized yet, this function will also set + it. + """ + global USER_BASE + if USER_BASE is not None: + return USER_BASE + from sysconfig import get_config_var + USER_BASE = get_config_var('userbase') + return USER_BASE + +def getusersitepackages(): + """Returns the user-specific site-packages directory path. + + If the global variable ``USER_SITE`` is not initialized yet, this + function will also set it. + """ + global USER_SITE + user_base = getuserbase() # this will also set USER_BASE + + if USER_SITE is not None: + return USER_SITE + + from sysconfig import get_path + + if sys.platform == 'darwin': + from sysconfig import get_config_var + if get_config_var('PYTHONFRAMEWORK'): + USER_SITE = get_path('purelib', 'osx_framework_user') + return USER_SITE + + USER_SITE = get_path('purelib', '%s_user' % os.name) + return USER_SITE + + +class _Printer(object): + """interactive prompt objects for printing the license text, a list of + contributors and the copyright notice.""" + + MAXLINES = 23 + + def __init__(self, name, data, files=(), dirs=()): + self.__name = name + self.__data = data + self.__files = files + self.__dirs = dirs + self.__lines = None + + def __setup(self): + if self.__lines: + return + data = None + for dir in self.__dirs: + for filename in self.__files: + filename = os.path.join(dir, filename) + try: + fp = open(filename, "rU") + data = fp.read() + fp.close() + break + except IOError: + pass + if data: + break + if not data: + data = self.__data + self.__lines = data.split('\n') + self.__linecnt = len(self.__lines) + + def __repr__(self): + self.__setup() + if len(self.__lines) <= self.MAXLINES: + return "\n".join(self.__lines) + else: + return "Type %s() to see the full %s text" % ((self.__name,)*2) + + def __call__(self): + self.__setup() + prompt = 'Hit Return for more, or q (and Return) to quit: ' + lineno = 0 + while 1: + try: + for i in range(lineno, lineno + self.MAXLINES): + print(self.__lines[i]) + except IndexError: + break + else: + lineno += self.MAXLINES + key = None + while key is None: + try: + key = raw_input(prompt) + except NameError: + # Python 3 + key = input(prompt) + if key not in ('', 'q'): + key = None + if key == 'q': + break + +def setcopyright(): + """Set 'copyright' and 'credits' in builtins""" + builtins.copyright = _Printer("copyright", sys.copyright) + if sys.platform[:4] == 'java': + builtins.credits = _Printer( + "credits", + "Jython is maintained by the Jython developers (www.jython.org).") + else: + builtins.credits = _Printer("credits", """\ + Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands + for supporting Python development. See www.python.org for more information.""") + here = os.path.dirname(os.__file__) + builtins.license = _Printer( + "license", "See http://www.python.org/%.3s/license.html" % sys.version, + ["LICENSE.txt", "LICENSE"], + [os.path.join(here, os.pardir), here, os.curdir]) + + +class _Helper(object): + """Define the builtin 'help'. + This is a wrapper around pydoc.help (with a twist). + + """ + + def __repr__(self): + return "Type help() for interactive help, " \ + "or help(object) for help about object." + def __call__(self, *args, **kwds): + import pydoc + return pydoc.help(*args, **kwds) + +def sethelper(): + builtins.help = _Helper() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/site/sitecustomize.py spyder-3.0.2+dfsg1/spyder/utils/site/sitecustomize.py --- spyder-2.3.8+dfsg1/spyder/utils/site/sitecustomize.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/site/sitecustomize.py 2016-11-16 15:40:01.000000000 +0000 @@ -0,0 +1,947 @@ +# +# Copyright (c) Spyder Project Contributors) +# Licensed under the terms of the MIT License) +# (see spyder/__init__.py for details) +# +# IMPORTANT NOTE: Don't add a coding line here! It's not necessary for +# site files +# +# Spyder consoles sitecustomize +# + +import sys +import os +import os.path as osp +import pdb +import bdb +import time +import traceback +import shlex + + +PY2 = sys.version[0] == '2' + + +#============================================================================== +# sys.argv can be missing when Python is embedded, taking care of it. +# Fixes Issue 1473 and other crazy crashes with IPython 0.13 trying to +# access it. +#============================================================================== +if not hasattr(sys, 'argv'): + sys.argv = [''] + + +#============================================================================== +# Main constants +#============================================================================== +IS_IPYKERNEL = os.environ.get("IPYTHON_KERNEL", "").lower() == "true" +IS_EXT_INTERPRETER = os.environ.get('EXTERNAL_INTERPRETER', '').lower() == "true" + + +#============================================================================== +# Important Note: +# +# We avoid importing spyder here, so we are handling Python 3 compatiblity +# by hand. +#============================================================================== +def _print(*objects, **options): + end = options.get('end', '\n') + file = options.get('file', sys.stdout) + sep = options.get('sep', ' ') + string = sep.join([str(obj) for obj in objects]) + if not PY2: + # Python 3 + local_dict = {} + exec('printf = print', local_dict) # to avoid syntax error in Python 2 + local_dict['printf'](string, file=file, end=end, sep=sep) + else: + # Python 2 + if end: + print >>file, string + else: + print >>file, string, + + +#============================================================================== +# Execfile functions +# +# The definitions for Python 2 on Windows were taken from the IPython project +# Copyright (C) The IPython Development Team +# Distributed under the terms of the modified BSD license +#============================================================================== +try: + # Python 2 + import __builtin__ as builtins + if os.name == 'nt': + def encode(u): + return u.encode('utf8', 'replace') + def execfile(fname, glob=None, loc=None): + loc = loc if (loc is not None) else glob + scripttext = builtins.open(fname).read()+ '\n' + # compile converts unicode filename to str assuming + # ascii. Let's do the conversion before calling compile + if isinstance(fname, unicode): + filename = encode(fname) + else: + filename = fname + exec(compile(scripttext, filename, 'exec'), glob, loc) + else: + def execfile(fname, *where): + if isinstance(fname, unicode): + filename = fname.encode(sys.getfilesystemencoding()) + else: + filename = fname + builtins.execfile(filename, *where) +except ImportError: + # Python 3 + import builtins + basestring = (str,) + def execfile(filename, namespace): + # Open a source file correctly, whatever its encoding is + with open(filename, 'rb') as f: + exec(compile(f.read(), filename, 'exec'), namespace) + + +#============================================================================== +# Colorization of sys.stderr (standard Python interpreter) +#============================================================================== +if os.environ.get("COLORIZE_SYS_STDERR", "").lower() == "true": + class StderrProxy(object): + """Proxy to sys.stderr file object overriding only the `write` method + to provide red colorization for the whole stream, and blue-underlined + for traceback file links""" + def __init__(self): + self.old_stderr = sys.stderr + self.__buffer = '' + sys.stderr = self + + def __getattr__(self, name): + return getattr(self.old_stderr, name) + + def write(self, text): + if os.name == 'nt' and '\n' not in text: + self.__buffer += text + return + for text in (self.__buffer+text).splitlines(True): + if text.startswith(' File') \ + and not text.startswith(' File "<'): + # Show error links in blue underlined text + colored_text = ' '+'\x1b[4;34m'+text[2:]+'\x1b[0m' + else: + # Show error messages in red + colored_text = '\x1b[31m'+text+'\x1b[0m' + self.old_stderr.write(colored_text) + self.__buffer = '' + + stderrproxy = StderrProxy() + + +#============================================================================== +# Prepending this spyder package's path to sys.path to be sure +# that another version of spyder won't be imported instead: +#============================================================================== +spyder_path = osp.dirname(__file__) +while not osp.isdir(osp.join(spyder_path, 'spyder')): + spyder_path = osp.abspath(osp.join(spyder_path, os.pardir)) +if not spyder_path.startswith(sys.prefix): + # Spyder is not installed: moving its parent directory to the top of + # sys.path to be sure that this spyder package will be imported in + # the remote process (instead of another installed version of Spyder) + while spyder_path in sys.path: + sys.path.remove(spyder_path) + sys.path.insert(0, spyder_path) +os.environ['SPYDER_PARENT_DIR'] = spyder_path + + +#============================================================================== +# Setting console encoding (otherwise Python does not recognize encoding) +# for Windows platforms +#============================================================================== +if os.name == 'nt' and PY2: + try: + import locale, ctypes + _t, _cp = locale.getdefaultlocale('LANG') + try: + _cp = int(_cp[2:]) + ctypes.windll.kernel32.SetConsoleCP(_cp) + ctypes.windll.kernel32.SetConsoleOutputCP(_cp) + except (ValueError, TypeError): + # Code page number in locale is not valid + pass + except ImportError: + pass + + +#============================================================================== +# Settings for our MacOs X app +#============================================================================== +if sys.platform == 'darwin': + from spyder.config.base import MAC_APP_NAME + if MAC_APP_NAME in __file__: + if IS_EXT_INTERPRETER: + # Add a minimal library (with spyder) at the end of sys.path to + # be able to connect our monitor to the external console + py_ver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) + app_pythonpath = '%s/Contents/Resources/lib/python%s' % (MAC_APP_NAME, + py_ver) + full_pythonpath = [p for p in sys.path if p.endswith(app_pythonpath)] + if full_pythonpath: + sys.path.remove(full_pythonpath[0]) + sys.path.append(full_pythonpath[0] + osp.sep + 'minimal-lib') + else: + # Add missing variables and methods to the app's site module + import site + import osx_app_site + osx_app_site.setcopyright() + osx_app_site.sethelper() + site._Printer = osx_app_site._Printer + site.USER_BASE = osx_app_site.getuserbase() + site.USER_SITE = osx_app_site.getusersitepackages() + + +#============================================================================== +# Importing user's sitecustomize +#============================================================================== +try: + import sitecustomize #analysis:ignore +except ImportError: + pass + + +#============================================================================== +# Add default filesystem encoding on Linux to avoid an error with +# Matplotlib 1.5 in Python 2 (Fixes Issue 2793) +#============================================================================== +if PY2 and sys.platform.startswith('linux'): + def _getfilesystemencoding_wrapper(): + return 'utf-8' + + sys.getfilesystemencoding = _getfilesystemencoding_wrapper + + +#============================================================================== +# Set PyQt API to #2 +#============================================================================== +if os.environ["QT_API"] == 'pyqt': + try: + import sip + for qtype in ('QString', 'QVariant', 'QDate', 'QDateTime', + 'QTextStream', 'QTime', 'QUrl'): + sip.setapi(qtype, 2) + except: + pass + + +#============================================================================== +# Importing matplotlib before creating the monitor. +# This prevents a kernel crash with the inline backend in our IPython +# consoles on Linux and Python 3 (Fixes Issue 2257) +#============================================================================== +try: + import matplotlib +except ImportError: + matplotlib = None # analysis:ignore + + +#============================================================================== +# Monitor-based functionality +#============================================================================== +if IS_IPYKERNEL or os.environ.get('SPYDER_SHELL_ID') is None: + monitor = None +else: + from spyder.widgets.externalshell.monitor import Monitor + monitor = Monitor("127.0.0.1", + int(os.environ['SPYDER_I_PORT']), + int(os.environ['SPYDER_N_PORT']), + os.environ['SPYDER_SHELL_ID'], + float(os.environ['SPYDER_AR_TIMEOUT']), + os.environ["SPYDER_AR_STATE"].lower() == "true") + monitor.start() + + def open_in_spyder(source, lineno=1): + """ + Open a source file in Spyder's editor (it could be a filename or a + Python module/package). + + If you want to use IPython's %edit use %ed instead + """ + try: + source = sys.modules[source] + except KeyError: + source = source + if not isinstance(source, basestring): + try: + source = source.__file__ + except AttributeError: + raise ValueError("source argument must be either " + "a string or a module object") + if source.endswith('.pyc'): + source = source[:-1] + source = osp.abspath(source) + if osp.exists(source): + monitor.notify_open_file(source, lineno=lineno) + else: + _print("Can't open file %s" % source, file=sys.stderr) + builtins.open_in_spyder = open_in_spyder + + # Our own input hook, monitor based and for Windows only + if os.name == 'nt' and matplotlib and not IS_IPYKERNEL: + # Qt imports + if os.environ["QT_API"] == 'pyqt5': + from PyQt5 import QtCore + from PyQt5 import QtWidgets + elif os.environ["QT_API"] == 'pyqt': + from PyQt4 import QtCore # analysis:ignore + from PyQt4 import QtGui as QtWidgets + elif os.environ["QT_API"] == 'pyside': + from PySide import QtCore # analysis:ignore + from PySide import QtGui as QtWidgets + + def qt_nt_inputhook(): + """Qt input hook for Windows + + This input hook wait for available stdin data (notified by + ExternalPythonShell through the monitor's inputhook_flag + attribute), and in the meantime it processes Qt events. + """ + # Refreshing variable explorer, except on first input hook call: + # (otherwise, on slow machines, this may freeze Spyder) + monitor.refresh_from_inputhook() + + # NOTE: This is making the inputhoook to fail completely!! + # That's why it's commented. + #try: + # This call fails for Python without readline support + # (or on Windows platforms) when PyOS_InputHook is called + # for the second consecutive time, because the 100-bytes + # stdin buffer is full. + # For more details, see the `PyOS_StdioReadline` function + # in Python source code (Parser/myreadline.c) + # sys.stdin.tell() + #except IOError: + # return 0 + + # Input hook + app = QtCore.QCoreApplication.instance() + if app is None: + app = QtWidgets.QApplication([" "]) + if app and app.thread() is QtCore.QThread.currentThread(): + try: + timer = QtCore.QTimer() + timer.timeout.connect(app.quit) + monitor.toggle_inputhook_flag(False) + while not monitor.inputhook_flag: + timer.start(50) + QtCore.QCoreApplication.exec_() + timer.stop() + except KeyboardInterrupt: + _print("\nKeyboardInterrupt - Press Enter for new prompt") + + # Socket-based alternative: + #socket = QtNetwork.QLocalSocket() + #socket.connectToServer(os.environ['SPYDER_SHELL_ID']) + #socket.waitForConnected(-1) + #while not socket.waitForReadyRead(10): + # timer.start(50) + # QtCore.QCoreApplication.exec_() + # timer.stop() + #socket.read(3) + #socket.disconnectFromServer() + return 0 + + +#============================================================================== +# Matplotlib settings +#============================================================================== +if matplotlib is not None: + if not IS_IPYKERNEL: + mpl_backend = os.environ.get("SPY_MPL_BACKEND", "") + mpl_ion = os.environ.get("MATPLOTLIB_ION", "") + + # Setting no backend if the user asks for it + if not mpl_backend or mpl_backend.lower() == 'none': + mpl_backend = "" + + # Set backend automatically + if mpl_backend.lower() == 'automatic': + if not IS_EXT_INTERPRETER: + if os.environ["QT_API"] == 'pyqt5': + mpl_backend = 'Qt5Agg' + else: + mpl_backend = 'Qt4Agg' + else: + # Test for backend libraries on external interpreters + def set_mpl_backend(backend): + mod, bend, qt_api = backend + try: + if mod: + __import__(mod) + if qt_api and (os.environ["QT_API"] != qt_api): + return None + else: + matplotlib.use(bend) + return bend + except (ImportError, ValueError): + return None + + backends = [('PyQt5', 'Qt5Agg', 'pyqt5'), + ('PyQt4', 'Qt4Agg', 'pyqt'), + ('PySide', 'Qt4Agg', 'pyqt')] + if not os.name == 'nt': + backends.append( ('_tkinter', 'TkAgg', None) ) + + for b in backends: + mpl_backend = set_mpl_backend(b) + if mpl_backend: + break + + if not mpl_backend: + _print("NOTE: No suitable Matplotlib backend was found!\n" + " You won't be able to create plots\n") + + # To have mpl docstrings as rst + matplotlib.rcParams['docstring.hardcopy'] = True + + # Activate interactive mode when needed + if mpl_ion.lower() == "true": + matplotlib.rcParams['interactive'] = True + + from spyder.utils import inputhooks + if mpl_backend: + import ctypes + + # Grab QT_API + qt_api = os.environ["QT_API"] + + # Setting the user defined backend + if not IS_EXT_INTERPRETER: + matplotlib.use(mpl_backend) + + # Setting the right input hook according to mpl_backend, + # IMPORTANT NOTE: Don't try to abstract the steps to set a PyOS + # input hook callback in a function. It will **crash** the + # interpreter!! + if (mpl_backend == "Qt4Agg" or mpl_backend == "Qt5Agg") and \ + os.name == 'nt' and monitor is not None: + # Removing PyQt4 input hook which is not working well on + # Windows since opening a subprocess does not attach a real + # console to it (with keyboard events...) + if qt_api == 'pyqt' or qt_api == 'pyqt5': + inputhooks.remove_pyqt_inputhook() + # Using our own input hook + # NOTE: it's not working correctly for some configurations + # (See issue 1831) + callback = inputhooks.set_pyft_callback(qt_nt_inputhook) + pyos_ih = inputhooks.get_pyos_inputhook() + pyos_ih.value = ctypes.cast(callback, ctypes.c_void_p).value + elif mpl_backend == "Qt4Agg" and qt_api == 'pyside': + # PySide doesn't have an input hook, so we need to install one + # to be able to show plots + # Note: This only works well for Posix systems + callback = inputhooks.set_pyft_callback(inputhooks.qt4) + pyos_ih = inputhooks.get_pyos_inputhook() + pyos_ih.value = ctypes.cast(callback, ctypes.c_void_p).value + elif (mpl_backend != "Qt4Agg" and qt_api == 'pyqt') \ + or (mpl_backend != "Qt5Agg" and qt_api == 'pyqt5'): + # Matplotlib backends install their own input hooks, so we + # need to remove the PyQt one to make them work + inputhooks.remove_pyqt_inputhook() + else: + inputhooks.remove_pyqt_inputhook() + else: + # To have mpl docstrings as rst + matplotlib.rcParams['docstring.hardcopy'] = True + + +#============================================================================== +# IPython kernel adjustments +#============================================================================== +if IS_IPYKERNEL: + # Use ipydb as the debugger to patch on IPython consoles + from IPython.core.debugger import Pdb as ipyPdb + pdb.Pdb = ipyPdb + + # Patch unittest.main so that errors are printed directly in the console. + # See http://comments.gmane.org/gmane.comp.python.ipython.devel/10557 + # Fixes Issue 1370 + import unittest + from unittest import TestProgram + class IPyTesProgram(TestProgram): + def __init__(self, *args, **kwargs): + test_runner = unittest.TextTestRunner(stream=sys.stderr) + kwargs['testRunner'] = kwargs.pop('testRunner', test_runner) + kwargs['exit'] = False + TestProgram.__init__(self, *args, **kwargs) + unittest.main = IPyTesProgram + + +#============================================================================== +# Pandas adjustments +#============================================================================== +try: + # Make Pandas recognize our Jupyter consoles as proper qtconsoles + # Fixes Issue 2015 + def in_qtconsole(): + return True + import pandas as pd + pd.core.common.in_qtconsole = in_qtconsole + + # Set Pandas output encoding + pd.options.display.encoding = 'utf-8' + + # Filter warning that appears for DataFrames with np.nan values + # Example: + # >>> import pandas as pd, numpy as np + # >>> pd.Series([np.nan,np.nan,np.nan],index=[1,2,3]) + # Fixes Issue 2991 + import warnings + # For 0.18- + warnings.filterwarnings(action='ignore', category=RuntimeWarning, + module='pandas.core.format', + message=".*invalid value encountered in.*") + # For 0.18.1+ + warnings.filterwarnings(action='ignore', category=RuntimeWarning, + module='pandas.formats.format', + message=".*invalid value encountered in.*") +except (ImportError, AttributeError): + pass + + +#============================================================================== +# Pdb adjustments +#============================================================================== +class SpyderPdb(pdb.Pdb): + send_initial_notification = True + + def set_spyder_breakpoints(self): + self.clear_all_breaks() + #------Really deleting all breakpoints: + for bp in bdb.Breakpoint.bpbynumber: + if bp: + bp.deleteMe() + bdb.Breakpoint.next = 1 + bdb.Breakpoint.bplist = {} + bdb.Breakpoint.bpbynumber = [None] + #------ + from spyder.config.main import CONF + CONF.load_from_ini() + if CONF.get('run', 'breakpoints/enabled', True): + breakpoints = CONF.get('run', 'breakpoints', {}) + i = 0 + for fname, data in list(breakpoints.items()): + for linenumber, condition in data: + i += 1 + self.set_break(self.canonic(fname), linenumber, + cond=condition) + + def notify_spyder(self, frame): + if not frame: + return + fname = self.canonic(frame.f_code.co_filename) + if PY2: + try: + fname = unicode(fname, "utf-8") + except TypeError: + pass + lineno = frame.f_lineno + if isinstance(fname, basestring) and isinstance(lineno, int): + if osp.isfile(fname): + if IS_IPYKERNEL: + from IPython.core.getipython import get_ipython + ipython_shell = get_ipython() + if ipython_shell: + step = dict(fname=fname, lineno=lineno) + ipython_shell.kernel._pdb_step = step + elif monitor is not None: + monitor.notify_pdb_step(fname, lineno) + time.sleep(0.1) + +pdb.Pdb = SpyderPdb + +#XXX: I know, this function is now also implemented as is in utils/misc.py but +# I'm kind of reluctant to import spyder in sitecustomize, even if this +# import is very clean. +def monkeypatch_method(cls, patch_name): + # This function's code was inspired from the following thread: + # "[Python-Dev] Monkeypatching idioms -- elegant or ugly?" + # by Robert Brewer + # (Tue Jan 15 19:13:25 CET 2008) + """ + Add the decorated method to the given class; replace as needed. + + If the named method already exists on the given class, it will + be replaced, and a reference to the old method is created as + cls._old. If the "_old__" attribute + already exists, KeyError is raised. + """ + def decorator(func): + fname = func.__name__ + old_func = getattr(cls, fname, None) + if old_func is not None: + # Add the old func to a list of old funcs. + old_ref = "_old_%s_%s" % (patch_name, fname) + #print(old_ref, old_func) + old_attr = getattr(cls, old_ref, None) + if old_attr is None: + setattr(cls, old_ref, old_func) + else: + raise KeyError("%s.%s already exists." + % (cls.__name__, old_ref)) + setattr(cls, fname, func) + return func + return decorator + +@monkeypatch_method(pdb.Pdb, 'Pdb') +def user_return(self, frame, return_value): + """This function is called when a return trap is set here.""" + # This is useful when debugging in an active interpreter (otherwise, + # the debugger will stop before reaching the target file) + if self._wait_for_mainpyfile: + if (self.mainpyfile != self.canonic(frame.f_code.co_filename) + or frame.f_lineno<= 0): + return + self._wait_for_mainpyfile = 0 + self._old_Pdb_user_return(frame, return_value) + +@monkeypatch_method(pdb.Pdb, 'Pdb') +def interaction(self, frame, traceback): + self.setup(frame, traceback) + if self.send_initial_notification: + self.notify_spyder(frame) #-----Spyder-specific----------------------- + self.print_stack_entry(self.stack[self.curindex]) + self.cmdloop() + self.forget() + +@monkeypatch_method(pdb.Pdb, 'Pdb') +def reset(self): + self._old_Pdb_reset() + if IS_IPYKERNEL: + from IPython.core.getipython import get_ipython + ipython_shell = get_ipython() + if ipython_shell: + ipython_shell.kernel._register_pdb_session(self) + elif monitor is not None: + monitor.register_pdb_session(self) + self.set_spyder_breakpoints() + +#XXX: notify spyder on any pdb command (is that good or too lazy? i.e. is more +# specific behaviour desired?) +@monkeypatch_method(pdb.Pdb, 'Pdb') +def postcmd(self, stop, line): + self.notify_spyder(self.curframe) + return self._old_Pdb_postcmd(stop, line) + +# Breakpoints don't work for files with non-ascii chars in Python 2 +# Fixes Issue 1484 +if PY2: + @monkeypatch_method(pdb.Pdb, 'Pdb') + def break_here(self, frame): + from bdb import effective + filename = self.canonic(frame.f_code.co_filename) + try: + filename = unicode(filename, "utf-8") + except TypeError: + pass + if not filename in self.breaks: + return False + lineno = frame.f_lineno + if not lineno in self.breaks[filename]: + # The line itself has no breakpoint, but maybe the line is the + # first line of a function with breakpoint set by function name. + lineno = frame.f_code.co_firstlineno + if not lineno in self.breaks[filename]: + return False + + # flag says ok to delete temp. bp + (bp, flag) = effective(filename, lineno, frame) + if bp: + self.currentbp = bp.number + if (flag and bp.temporary): + self.do_clear(str(bp.number)) + return True + else: + return False + + +#============================================================================== +# Restoring (almost) original sys.path: +# +# NOTE: do not remove spyder_path from sys.path because if Spyder has been +# installed using python setup.py install, then this could remove the +# 'site-packages' directory from sys.path! +#============================================================================== +try: + sys.path.remove(osp.join(spyder_path, "spyder", "widgets", + "externalshell")) +except ValueError: + pass + + +#============================================================================== +# User module reloader +#============================================================================== +class UserModuleReloader(object): + """ + User Module Reloader (UMR) aims at deleting user modules + to force Python to deeply reload them during import + + pathlist [list]: blacklist in terms of module path + namelist [list]: blacklist in terms of module name + """ + def __init__(self, namelist=None, pathlist=None): + if namelist is None: + namelist = [] + spy_modules = ['sitecustomize', 'spyder', 'spyderplugins'] + mpl_modules = ['matplotlib', 'tkinter', 'Tkinter'] + self.namelist = namelist + spy_modules + mpl_modules + + if pathlist is None: + pathlist = [] + self.pathlist = pathlist + self.previous_modules = list(sys.modules.keys()) + + def is_module_blacklisted(self, modname, modpath): + for path in [sys.prefix]+self.pathlist: + if modpath.startswith(path): + return True + else: + return set(modname.split('.')) & set(self.namelist) + + def run(self, verbose=False): + """ + Del user modules to force Python to deeply reload them + + Do not del modules which are considered as system modules, i.e. + modules installed in subdirectories of Python interpreter's binary + Do not del C modules + """ + log = [] + for modname, module in list(sys.modules.items()): + if modname not in self.previous_modules: + modpath = getattr(module, '__file__', None) + if modpath is None: + # *module* is a C module that is statically linked into the + # interpreter. There is no way to know its path, so we + # choose to ignore it. + continue + if not self.is_module_blacklisted(modname, modpath): + log.append(modname) + del sys.modules[modname] + if verbose and log: + _print("\x1b[4;33m%s\x1b[24m%s\x1b[0m"\ + % ("Reloaded modules", ": "+", ".join(log))) + +__umr__ = None + + +#============================================================================== +# Handle Post Mortem Debugging and Traceback Linkage to Spyder +#============================================================================== +def clear_post_mortem(): + """ + Remove the post mortem excepthook and replace with a standard one. + """ + if IS_IPYKERNEL: + from IPython.core.getipython import get_ipython + ipython_shell = get_ipython() + if ipython_shell: + ipython_shell.set_custom_exc((), None) + else: + sys.excepthook = sys.__excepthook__ + + +def post_mortem_excepthook(type, value, tb): + """ + For post mortem exception handling, print a banner and enable post + mortem debugging. + """ + clear_post_mortem() + if IS_IPYKERNEL: + from IPython.core.getipython import get_ipython + ipython_shell = get_ipython() + ipython_shell.showtraceback((type, value, tb)) + p = pdb.Pdb(ipython_shell.colors) + else: + traceback.print_exception(type, value, tb, file=sys.stderr) + p = pdb.Pdb() + + if not type == SyntaxError: + # wait for stderr to print (stderr.flush does not work in this case) + time.sleep(0.1) + _print('*' * 40) + _print('Entering post mortem debugging...') + _print('*' * 40) + # add ability to move between frames + p.send_initial_notification = False + p.reset() + frame = tb.tb_frame + prev = frame + while frame.f_back: + prev = frame + frame = frame.f_back + frame = prev + # wait for stdout to print + time.sleep(0.1) + p.interaction(frame, tb) + + +def set_post_mortem(): + """ + Enable the post mortem debugging excepthook. + """ + if IS_IPYKERNEL: + from IPython.core.getipython import get_ipython + def ipython_post_mortem_debug(shell, etype, evalue, tb, + tb_offset=None): + post_mortem_excepthook(etype, evalue, tb) + ipython_shell = get_ipython() + ipython_shell.set_custom_exc((Exception,), ipython_post_mortem_debug) + else: + sys.excepthook = post_mortem_excepthook + +# Add post mortem debugging if requested and in a dedicated interpreter +# existing interpreters use "runfile" below +if "SPYDER_EXCEPTHOOK" in os.environ: + set_post_mortem() + + +#============================================================================== +# runfile and debugfile commands +#============================================================================== +def _get_globals(): + """Return current Python interpreter globals namespace""" + from __main__ import __dict__ as namespace + shell = namespace.get('__ipythonshell__') + if shell is not None and hasattr(shell, 'user_ns'): + # IPython 0.13+ kernel + return shell.user_ns + else: + # Python interpreter + return namespace + return namespace + + +def runfile(filename, args=None, wdir=None, namespace=None, post_mortem=False): + """ + Run filename + args: command line arguments (string) + wdir: working directory + post_mortem: boolean, whether to enter post-mortem mode on error + """ + try: + filename = filename.decode('utf-8') + except (UnicodeError, TypeError, AttributeError): + # UnicodeError, TypeError --> eventually raised in Python 2 + # AttributeError --> systematically raised in Python 3 + pass + global __umr__ + if os.environ.get("UMR_ENABLED", "").lower() == "true": + if __umr__ is None: + namelist = os.environ.get("UMR_NAMELIST", None) + if namelist is not None: + namelist = namelist.split(',') + __umr__ = UserModuleReloader(namelist=namelist) + else: + verbose = os.environ.get("UMR_VERBOSE", "").lower() == "true" + __umr__.run(verbose=verbose) + if args is not None and not isinstance(args, basestring): + raise TypeError("expected a character buffer object") + if namespace is None: + namespace = _get_globals() + namespace['__file__'] = filename + sys.argv = [filename] + if args is not None: + for arg in shlex.split(args): + sys.argv.append(arg) + if wdir is not None: + try: + wdir = wdir.decode('utf-8') + except (UnicodeError, TypeError, AttributeError): + # UnicodeError, TypeError --> eventually raised in Python 2 + # AttributeError --> systematically raised in Python 3 + pass + os.chdir(wdir) + if post_mortem: + set_post_mortem() + execfile(filename, namespace) + clear_post_mortem() + sys.argv = [''] + namespace.pop('__file__') + +builtins.runfile = runfile + + +def debugfile(filename, args=None, wdir=None, post_mortem=False): + """ + Debug filename + args: command line arguments (string) + wdir: working directory + post_mortem: boolean, included for compatiblity with runfile + """ + debugger = pdb.Pdb() + filename = debugger.canonic(filename) + debugger._wait_for_mainpyfile = 1 + debugger.mainpyfile = filename + debugger._user_requested_quit = 0 + if os.name == 'nt': + filename = filename.replace('\\', '/') + debugger.run("runfile(%r, args=%r, wdir=%r)" % (filename, args, wdir)) + +builtins.debugfile = debugfile + + +#============================================================================== +# Evaluate external commands +#============================================================================== +def evalsc(command): + """Evaluate special commands + (analog to IPython's magic commands but far less powerful/complete)""" + assert command.startswith('%') + from spyder.utils import programs + + namespace = _get_globals() + command = command[1:].strip() # Remove leading % + + import re + clear_match = re.match(r"^clear ([a-zA-Z0-9_, ]+)", command) + cd_match = re.match(r"^cd \"?\'?([a-zA-Z0-9_\ \:\\\/\.]+)", command) + + if cd_match: + os.chdir(eval('r"%s"' % cd_match.groups()[0].strip())) + elif clear_match: + varnames = clear_match.groups()[0].replace(' ', '').split(',') + for varname in varnames: + try: + namespace.pop(varname) + except KeyError: + pass + elif command in ('cd', 'pwd'): + try: + _print(os.getcwdu()) + except AttributeError: + _print(os.getcwd()) + elif command == 'ls': + if os.name == 'nt': + programs.run_shell_command('dir') + _print('\n') + else: + programs.run_shell_command('ls') + _print('\n') + elif command == 'scientific': + from spyder.config import base + execfile(base.SCIENTIFIC_STARTUP, namespace) + else: + raise NotImplementedError("Unsupported command: '%s'" % command) + +builtins.evalsc = evalsc + + +#============================================================================== +# Restoring original PYTHONPATH +#============================================================================== +try: + os.environ['PYTHONPATH'] = os.environ['OLD_PYTHONPATH'] + del os.environ['OLD_PYTHONPATH'] +except KeyError: + if os.environ.get('PYTHONPATH') is not None: + del os.environ['PYTHONPATH'] diff -Nru spyder-2.3.8+dfsg1/spyder/utils/sourcecode.py spyder-3.0.2+dfsg1/spyder/utils/sourcecode.py --- spyder-2.3.8+dfsg1/spyder/utils/sourcecode.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/sourcecode.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Source code text utilities +""" + +import re + + +# Order is important: +EOL_CHARS = (("\r\n", 'nt'), ("\n", 'posix'), ("\r", 'mac')) + +ALL_LANGUAGES = { + 'Python': ('py', 'pyw', 'python', 'ipy'), + 'Cython': ('pyx', 'pxi', 'pxd'), + 'Enaml': ('enaml',), + 'Fortran77': ('f', 'for', 'f77'), + 'Fortran': ('f90', 'f95', 'f2k'), + 'Idl': ('pro',), + 'Diff': ('diff', 'patch', 'rej'), + 'GetText': ('po', 'pot'), + 'Nsis': ('nsi', 'nsh'), + 'Html': ('htm', 'html'), + 'Cpp': ('c', 'cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'hxx'), + 'OpenCL': ('cl',), + 'Yaml':('yaml','yml'), + } + +PYTHON_LIKE_LANGUAGES = ('Python', 'Cython', 'Enaml') + +CELL_LANGUAGES = {'Python': ('#%%', '# %%', '# ', '# In[')} + + +def get_eol_chars(text): + """Get text EOL characters""" + for eol_chars, _os_name in EOL_CHARS: + if text.find(eol_chars) > -1: + return eol_chars + + +def get_os_name_from_eol_chars(eol_chars): + """Return OS name from EOL characters""" + for chars, os_name in EOL_CHARS: + if eol_chars == chars: + return os_name + + +def get_eol_chars_from_os_name(os_name): + """Return EOL characters from OS name""" + for eol_chars, name in EOL_CHARS: + if name == os_name: + return eol_chars + + +def has_mixed_eol_chars(text): + """Detect if text has mixed EOL characters""" + eol_chars = get_eol_chars(text) + if eol_chars is None: + return False + correct_text = eol_chars.join((text+eol_chars).splitlines()) + return repr(correct_text) != repr(text) + + +def fix_indentation(text): + """Replace tabs by spaces""" + return text.replace('\t', ' '*4) + + +def is_builtin(text): + """Test if passed string is the name of a Python builtin object""" + from spyder.py3compat import builtins + return text in [str(name) for name in dir(builtins) + if not name.startswith('_')] + + +def is_keyword(text): + """Test if passed string is the name of a Python keyword""" + import keyword + return text in keyword.kwlist + + +def get_primary_at(source_code, offset, retry=True): + """Return Python object in *source_code* at *offset* + Periods to the left of the cursor are carried forward + e.g. 'functools.par^tial' would yield 'functools.partial' + Retry prevents infinite recursion: retry only once + """ + obj = '' + left = re.split(r"[^0-9a-zA-Z_.]", source_code[:offset]) + if left and left[-1]: + obj = left[-1] + right = re.split(r"\W", source_code[offset:]) + if right and right[0]: + obj += right[0] + if obj and obj[0].isdigit(): + obj = '' + # account for opening chars with no text to the right + if not obj and retry and offset and source_code[offset - 1] in '([.': + return get_primary_at(source_code, offset - 1, retry=False) + return obj + + +def split_source(source_code): + '''Split source code into lines + ''' + eol_chars = get_eol_chars(source_code) + if eol_chars: + return source_code.split(eol_chars) + else: + return [source_code] + + +def get_identifiers(source_code): + '''Split source code into python identifier-like tokens''' + tokens = set(re.split(r"[^0-9a-zA-Z_.]", source_code)) + valid = re.compile(r'[a-zA-Z_]') + return [token for token in tokens if re.match(valid, token)] + + +if __name__ == '__main__': + code = 'import functools\nfunctools.partial' + assert get_primary_at(code, len(code)) == 'functools.partial' + assert set(get_identifiers(code)) == set(['import', 'functools', + 'functools.partial']) + assert split_source(code) == ['import functools', 'functools.partial'] + code = code.replace('\n', '\r\n') + assert split_source(code) == ['import functools', 'functools.partial'] diff -Nru spyder-2.3.8+dfsg1/spyder/utils/stringmatching.py spyder-3.0.2+dfsg1/spyder/utils/stringmatching.py --- spyder-2.3.8+dfsg1/spyder/utils/stringmatching.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/stringmatching.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,280 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +String search and match utilities usefull when filtering a list of texts. +""" + +import re + + +NOT_FOUND_SCORE = -1 +NO_SCORE = 0 + + +def get_search_regex(query, ignore_case=True): + """Returns a compiled regex pattern to search for query letters in order. + + Parameters + ---------- + query : str + String to search in another string (in order of character occurrence). + ignore_case : True + Optional value perform a case insensitive search (True by default). + + Returns + ------- + pattern : SRE_Pattern + + Notes + ----- + This function adds '.*' between the query characters and compiles the + resulting regular expression. + """ + regex_text = [char for char in query if char != ' '] + regex_text = '.*'.join(regex_text) + + regex = '({0})'.format(regex_text) + + if ignore_case: + pattern = re.compile(regex, re.IGNORECASE) + else: + pattern = re.compile(regex) + + return pattern + + +def get_search_score(query, choice, ignore_case=True, apply_regex=True, + template='{}'): + """Returns a tuple with the enriched text (if a template is provided) and + a score for the match. + + Parameters + ---------- + query : str + String with letters to search in choice (in order of appearance). + choice : str + Sentence/words in which to search for the 'query' letters. + ignore_case : bool, optional + Optional value perform a case insensitive search (True by default). + apply_regex : bool, optional + Optional value (True by default) to perform a regex search. Useful + when this function is called directly. + template : str, optional + Optional template string to surround letters found in choices. This is + useful when using a rich text editor ('{}' by default). + Examples: '{}', '{}', '{}' + + Returns + ------- + results : tuple + Tuples where the first item is the text (enriched if a template was + used) and the second item is a search score. + + Notes + ----- + The score is given according the following precedence (high to low): + + - Letters in one word and no spaces with exact match. + Example: 'up' in 'up stroke' + - Letters in one word and no spaces with partial match. + Example: 'up' in 'upstream stroke' + - Letters in one word but with skip letters. + Example: 'cls' in 'close up' + - Letters in two or more words + Example: 'cls' in 'car lost' + """ + original_choice = choice + result = (original_choice, NOT_FOUND_SCORE) + + # Handle empty string case + if not query: + return result + + if ignore_case: + query = query.lower() + choice = choice.lower() + + if apply_regex: + pattern = get_search_regex(query, ignore_case=ignore_case) + r = re.search(pattern, choice) + if r is None: + return result + else: + sep = u'-' # Matches will be replaced by this character + let = u'x' # Nonmatches (except spaed) will be replaced by this + score = 0 + + exact_words = [query == word for word in choice.split(u' ')] + partial_words = [query in word for word in choice.split(u' ')] + + if any(exact_words) or any(partial_words): + pos_start = choice.find(query) + pos_end = pos_start + len(query) + score += pos_start + text = choice.replace(query, sep*len(query), 1) + + enriched_text = original_choice[:pos_start] +\ + template.format(original_choice[pos_start:pos_end]) +\ + original_choice[pos_end:] + + if any(exact_words): + # Check if the query words exists in a word with exact match + score += 1 + elif any(partial_words): + # Check if the query words exists in a word with partial match + score += 100 + else: + # Check letter by letter + text = [l for l in original_choice] + if ignore_case: + temp_text = [l.lower() for l in original_choice] + else: + temp_text = text[:] + + # Give points to start of string + score += temp_text.index(query[0]) + + # Find the query letters and replace them by `sep`, also apply + # template as needed for enricching the letters in the text + enriched_text = text[:] + for char in query: + if char != u'' and char in temp_text: + index = temp_text.index(char) + enriched_text[index] = template.format(text[index]) + text[index] = sep + temp_text = [u' ']*(index + 1) + temp_text[index+1:] + + enriched_text = u''.join(enriched_text) + + patterns_text = [] + for i, char in enumerate(text): + if char != u' ' and char != sep: + new_char = let + else: + new_char = char + patterns_text.append(new_char) + patterns_text = u''.join(patterns_text) + for i in reversed(range(1, len(query) + 1)): + score += (len(query) - patterns_text.count(sep*i))*100000 + + temp = patterns_text.split(sep) + while u'' in temp: + temp.remove(u'') + if not patterns_text.startswith(sep): + temp = temp[1:] + if not patterns_text.endswith(sep): + temp = temp[:-1] + + for pat in temp: + score += pat.count(u' ')*10000 + score += pat.count(let)*100 + + return original_choice, enriched_text, score + + +def get_search_scores(query, choices, ignore_case=True, template='{}', + valid_only=False, sort=False): + """Search for query inside choices and return a list of tuples. + + Returns a list of tuples of text with the enriched text (if a template is + provided) and a score for the match. Lower scores imply a better match. + + Parameters + ---------- + query : str + String with letters to search in each choice (in order of appearance). + choices : list of str + List of sentences/words in which to search for the 'query' letters. + ignore_case : bool, optional + Optional value perform a case insensitive search (True by default). + template : str, optional + Optional template string to surround letters found in choices. This is + useful when using a rich text editor ('{}' by default). + Examples: '{}', '{}', '{}' + + Returns + ------- + results : list of tuples + List of tuples where the first item is the text (enriched if a + template was used) and a search score. Lower scores means better match. + """ + # First remove spaces from query + query = query.replace(' ', '') + pattern = get_search_regex(query, ignore_case) + results = [] + + for choice in choices: + r = re.search(pattern, choice) + if query and r: + result = get_search_score(query, choice, ignore_case=ignore_case, + apply_regex=False, template=template) + else: + if query: + result = (choice, choice, NOT_FOUND_SCORE) + else: + result = (choice, choice, NO_SCORE) + + if valid_only: + if result[-1] != NOT_FOUND_SCORE: + results.append(result) + else: + results.append(result) + + if sort: + results = sorted(results, key=lambda row: row[-1]) + + return results + + +def test(): + template = '{0}' + names = ['close pane', 'debug continue', 'debug exit', 'debug step into', + 'debug step over', 'debug step return', 'fullscreen mode', + 'layout preferences', 'lock unlock panes', 'maximize pane', + 'preferences', 'quit', 'restart', 'save current layout', + 'switch to breakpoints', 'switch to console', 'switch to editor', + 'switch to explorer', 'switch to find_in_files', + 'switch to historylog', 'switch to help', + 'switch to ipython_console', 'switch to onlinehelp', + 'switch to outline_explorer', 'switch to project_explorer', + 'switch to variable_explorer', + 'use next layout', 'use previous layout', 'clear line', + 'clear shell', 'inspect current object', 'blockcomment', + 'breakpoint', 'close all', 'code completion', + 'conditional breakpoint', 'configure', 'copy', 'copy line', 'cut', + 'debug', 'debug with winpdb', 'delete', 'delete line', + 'duplicate line', 'end of document', 'end of line', + 'file list management', 'find next', 'find previous', 'find text', + 'go to definition', 'go to line', 'go to next file', + 'go to previous file', 'inspect current object', 'kill next word', + 'kill previous word', 'kill to line end', 'kill to line start', + 'last edit location', 'move line down', 'move line up', + 'new file', 'next char', 'next cursor position', 'next line', + 'next word', 'open file', 'paste', 'previous char', + 'previous cursor position', 'previous line', 'previous word', + 'print', 're-run last script', 'redo', 'replace text', + 'rotate kill ring', 'run', 'run selection', 'save all', 'save as', + 'save file', 'select all', 'show/hide outline', + 'show/hide project explorer', 'start of document', + 'start of line', 'toggle comment', 'unblockcomment', 'undo', + 'yank', 'run profiler', 'run analysis'] + + a = get_search_scores('lay', names, template=template, ) + b = get_search_scores('lay', names, template=template, valid_only=True, + sort=True) + # Full results + for r in a: + print(r) + + # Ordered and filtered results + print('\n') + + for r in b: + print(r) + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/utils/syntaxhighlighters.py spyder-3.0.2+dfsg1/spyder/utils/syntaxhighlighters.py --- spyder-2.3.8+dfsg1/spyder/utils/syntaxhighlighters.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/syntaxhighlighters.py 2016-11-16 16:12:04.000000000 +0000 @@ -0,0 +1,998 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Editor widget syntax highlighters based on QtGui.QSyntaxHighlighter +(Python syntax highlighting rules are inspired from idlelib) +""" + +# Standard library imports +from __future__ import print_function +import keyword +import os +import re + +# Third party imports +from qtpy.QtCore import Qt +from qtpy.QtGui import (QColor, QCursor, QFont, QSyntaxHighlighter, + QTextCharFormat, QTextOption) +from qtpy.QtWidgets import QApplication + +# Local imports +from spyder import dependencies +from spyder.config.base import _ +from spyder.config.main import CONF +from spyder.py3compat import builtins, is_text_string, to_text_string +from spyder.utils.sourcecode import CELL_LANGUAGES + + +PYGMENTS_REQVER = '>=2.0' +dependencies.add("pygments", _("Syntax highlighting for Matlab, Julia and " + "other file types"), + required_version=PYGMENTS_REQVER) + + +# ============================================================================= +# Constants +# ============================================================================= +COLOR_SCHEME_KEYS = { + "background": _("Background:"), + "currentline": _("Current line:"), + "currentcell": _("Current cell:"), + "occurrence": _("Occurrence:"), + "ctrlclick": _("Link:"), + "sideareas": _("Side areas:"), + "matched_p": _("Matched
    parens:"), + "unmatched_p": _("Unmatched
    parens:"), + "normal": _("Normal text:"), + "keyword": _("Keyword:"), + "builtin": _("Builtin:"), + "definition": _("Definition:"), + "comment": _("Comment:"), + "string": _("String:"), + "number": _("Number:"), + "instance": _("Instance:"), + } +COLOR_SCHEME_NAMES = CONF.get('color_schemes', 'names') +# Mapping for file extensions that use Pygments highlighting but should use +# different lexers than Pygments' autodetection suggests. Keys are file +# extensions or tuples of extensions, values are Pygments lexer names. +CUSTOM_EXTENSION_LEXER = {'.ipynb': 'json', + '.txt': 'text', + '.nt': 'bat', + '.scss': 'css', + '.m': 'matlab', + ('.properties', '.session', '.inf', '.reg', '.url', + '.cfg', '.cnf', '.aut', '.iss'): 'ini'} +# Convert custom extensions into a one-to-one mapping for easier lookup. +custom_extension_lexer_mapping = {} +for key, value in CUSTOM_EXTENSION_LEXER.items(): + # Single key is mapped unchanged. + if is_text_string(key): + custom_extension_lexer_mapping[key] = value + # Tuple of keys is iterated over and each is mapped to value. + else: + for k in key: + custom_extension_lexer_mapping[k] = value + + +#============================================================================== +# Auxiliary functions +#============================================================================== +def get_color_scheme(name): + """Get a color scheme from config using its name""" + name = name.lower() + scheme = {} + for key in COLOR_SCHEME_KEYS: + try: + scheme[key] = CONF.get('color_schemes', name+'/'+key) + except: + scheme[key] = CONF.get('color_schemes', 'spyder/'+key) + return scheme + + +#============================================================================== +# Syntax highlighting color schemes +#============================================================================== +class BaseSH(QSyntaxHighlighter): + """Base Syntax Highlighter Class""" + # Syntax highlighting rules: + PROG = None + BLANKPROG = re.compile("\s+") + # Syntax highlighting states (from one text block to another): + NORMAL = 0 + # Syntax highlighting parameters. + BLANK_ALPHA_FACTOR = 0.31 + + def __init__(self, parent, font=None, color_scheme='Spyder'): + QSyntaxHighlighter.__init__(self, parent) + + self.outlineexplorer_data = {} + + self.font = font + if is_text_string(color_scheme): + self.color_scheme = get_color_scheme(color_scheme) + else: + self.color_scheme = color_scheme + + self.background_color = None + self.currentline_color = None + self.currentcell_color = None + self.occurrence_color = None + self.ctrlclick_color = None + self.sideareas_color = None + self.matched_p_color = None + self.unmatched_p_color = None + + self.formats = None + self.setup_formats(font) + + self.cell_separators = None + + def get_background_color(self): + return QColor(self.background_color) + + def get_foreground_color(self): + """Return foreground ('normal' text) color""" + return self.formats["normal"].foreground().color() + + def get_currentline_color(self): + return QColor(self.currentline_color) + + def get_currentcell_color(self): + return QColor(self.currentcell_color) + + def get_occurrence_color(self): + return QColor(self.occurrence_color) + + def get_ctrlclick_color(self): + return QColor(self.ctrlclick_color) + + def get_sideareas_color(self): + return QColor(self.sideareas_color) + + def get_matched_p_color(self): + return QColor(self.matched_p_color) + + def get_unmatched_p_color(self): + return QColor(self.unmatched_p_color) + + def get_comment_color(self): + """ Return color for the comments """ + return self.formats['comment'].foreground().color() + + def get_color_name(self, fmt): + """Return color name assigned to a given format""" + return self.formats[fmt].foreground().color().name() + + def setup_formats(self, font=None): + base_format = QTextCharFormat() + if font is not None: + self.font = font + if self.font is not None: + base_format.setFont(self.font) + self.formats = {} + colors = self.color_scheme.copy() + self.background_color = colors.pop("background") + self.currentline_color = colors.pop("currentline") + self.currentcell_color = colors.pop("currentcell") + self.occurrence_color = colors.pop("occurrence") + self.ctrlclick_color = colors.pop("ctrlclick") + self.sideareas_color = colors.pop("sideareas") + self.matched_p_color = colors.pop("matched_p") + self.unmatched_p_color = colors.pop("unmatched_p") + for name, (color, bold, italic) in list(colors.items()): + format = QTextCharFormat(base_format) + format.setForeground(QColor(color)) + format.setBackground(QColor(self.background_color)) + if bold: + format.setFontWeight(QFont.Bold) + format.setFontItalic(italic) + self.formats[name] = format + + def set_color_scheme(self, color_scheme): + if is_text_string(color_scheme): + self.color_scheme = get_color_scheme(color_scheme) + else: + self.color_scheme = color_scheme + self.setup_formats() + self.rehighlight() + + def highlightBlock(self, text): + raise NotImplementedError + + def highlight_spaces(self, text, offset=0): + """ + Make blank space less apparent by setting the foreground alpha. + This only has an effect when 'Show blank space' is turned on. + Derived classes could call this function at the end of + highlightBlock(). + """ + flags_text = self.document().defaultTextOption().flags() + show_blanks = flags_text & QTextOption.ShowTabsAndSpaces + if show_blanks: + format_leading = self.formats.get("leading", None) + format_trailing = self.formats.get("trailing", None) + match = self.BLANKPROG.search(text, offset) + while match: + start, end = match.span() + start = max([0, start+offset]) + end = max([0, end+offset]) + # Format trailing spaces at the end of the line. + if end == len(text) and format_trailing is not None: + self.setFormat(start, end, format_trailing) + # Format leading spaces, e.g. indentation. + if start == 0 and format_leading is not None: + self.setFormat(start, end, format_leading) + format = self.format(start) + color_foreground = format.foreground().color() + alpha_new = self.BLANK_ALPHA_FACTOR * color_foreground.alphaF() + color_foreground.setAlphaF(alpha_new) + self.setFormat(start, end-start, color_foreground) + match = self.BLANKPROG.search(text, match.end()) + + def get_outlineexplorer_data(self): + return self.outlineexplorer_data + + def rehighlight(self): + self.outlineexplorer_data = {} + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + QSyntaxHighlighter.rehighlight(self) + QApplication.restoreOverrideCursor() + + +class TextSH(BaseSH): + """Simple Text Syntax Highlighter Class (only highlight spaces)""" + def highlightBlock(self, text): + self.highlight_spaces(text) + + +class GenericSH(BaseSH): + """Generic Syntax Highlighter""" + # Syntax highlighting rules: + PROG = None # to be redefined in child classes + def highlightBlock(self, text): + text = to_text_string(text) + self.setFormat(0, len(text), self.formats["normal"]) + + match = self.PROG.search(text) + index = 0 + while match: + for key, value in list(match.groupdict().items()): + if value: + start, end = match.span(key) + index += end-start + self.setFormat(start, end-start, self.formats[key]) + + match = self.PROG.search(text, match.end()) + + self.highlight_spaces(text) + + +#============================================================================== +# Python syntax highlighter +#============================================================================== +def any(name, alternates): + "Return a named group pattern matching list of alternates." + return "(?P<%s>" % name + "|".join(alternates) + ")" + +def make_python_patterns(additional_keywords=[], additional_builtins=[]): + "Strongly inspired from idlelib.ColorDelegator.make_pat" + kwlist = keyword.kwlist + additional_keywords + builtinlist = [str(name) for name in dir(builtins) + if not name.startswith('_')] + additional_builtins + repeated = set(kwlist) & set(builtinlist) + for repeated_element in repeated: + kwlist.remove(repeated_element) + kw = r"\b" + any("keyword", kwlist) + r"\b" + builtin = r"([^.'\"\\#]\b|^)" + any("builtin", builtinlist) + r"\b" + comment = any("comment", [r"#[^\n]*"]) + instance = any("instance", [r"\bself\b"]) + number = any("number", + [r"\b[+-]?[0-9]+[lLjJ]?\b", + r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", + r"\b[+-]?0[oO][0-7]+[lL]?\b", + r"\b[+-]?0[bB][01]+[lL]?\b", + r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?[jJ]?\b"]) + sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' + uf_sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*(\\)$(?!')$" + uf_dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*(\\)$(?!")$' + sq3string = r"(\b[rRuU])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" + dq3string = r'(\b[rRuU])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' + uf_sq3string = r"(\b[rRuU])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(\\)?(?!''')$" + uf_dq3string = r'(\b[rRuU])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(\\)?(?!""")$' + string = any("string", [sq3string, dq3string, sqstring, dqstring]) + ufstring1 = any("uf_sqstring", [uf_sqstring]) + ufstring2 = any("uf_dqstring", [uf_dqstring]) + ufstring3 = any("uf_sq3string", [uf_sq3string]) + ufstring4 = any("uf_dq3string", [uf_dq3string]) + return "|".join([instance, kw, builtin, comment, + ufstring1, ufstring2, ufstring3, ufstring4, string, + number, any("SYNC", [r"\n"])]) + +class OutlineExplorerData(object): + CLASS, FUNCTION, STATEMENT, COMMENT, CELL = list(range(5)) + FUNCTION_TOKEN = 'def' + CLASS_TOKEN = 'class' + + def __init__(self): + self.text = None + self.fold_level = None + self.def_type = None + self.def_name = None + + def is_not_class_nor_function(self): + return self.def_type not in (self.CLASS, self.FUNCTION) + + def is_class_or_function(self): + return self.def_type in (self.CLASS, self.FUNCTION) + + def is_comment(self): + return self.def_type in (self.COMMENT, self.CELL) + + def get_class_name(self): + if self.def_type == self.CLASS: + return self.def_name + + def get_function_name(self): + if self.def_type == self.FUNCTION: + return self.def_name + + def get_token(self): + if self.def_type == self.FUNCTION: + token = self.FUNCTION_TOKEN + elif self.def_type == self.CLASS: + token = self.CLASS_TOKEN + + return token + + +class PythonSH(BaseSH): + """Python Syntax Highlighter""" + # Syntax highlighting rules: + PROG = re.compile(make_python_patterns(), re.S) + IDPROG = re.compile(r"\s+(\w+)", re.S) + ASPROG = re.compile(r".*?\b(as)\b") + # Syntax highlighting states (from one text block to another): + (NORMAL, INSIDE_SQ3STRING, INSIDE_DQ3STRING, + INSIDE_SQSTRING, INSIDE_DQSTRING) = list(range(5)) + DEF_TYPES = {"def": OutlineExplorerData.FUNCTION, + "class": OutlineExplorerData.CLASS} + # Comments suitable for Outline Explorer + OECOMMENT = re.compile('^(# ?--[-]+|##[#]+ )[ -]*[^- ]+') + + def __init__(self, parent, font=None, color_scheme='Spyder'): + BaseSH.__init__(self, parent, font, color_scheme) + self.import_statements = {} + self.found_cell_separators = False + self.cell_separators = CELL_LANGUAGES['Python'] + + def highlightBlock(self, text): + text = to_text_string(text) + prev_state = self.previousBlockState() + if prev_state == self.INSIDE_DQ3STRING: + offset = -4 + text = r'""" '+text + elif prev_state == self.INSIDE_SQ3STRING: + offset = -4 + text = r"''' "+text + elif prev_state == self.INSIDE_DQSTRING: + offset = -2 + text = r'" '+text + elif prev_state == self.INSIDE_SQSTRING: + offset = -2 + text = r"' "+text + else: + offset = 0 + prev_state = self.NORMAL + + oedata = None + import_stmt = None + + self.setFormat(0, len(text), self.formats["normal"]) + + state = self.NORMAL + match = self.PROG.search(text) + while match: + for key, value in list(match.groupdict().items()): + if value: + start, end = match.span(key) + start = max([0, start+offset]) + end = max([0, end+offset]) + if key == "uf_sq3string": + self.setFormat(start, end-start, + self.formats["string"]) + state = self.INSIDE_SQ3STRING + elif key == "uf_dq3string": + self.setFormat(start, end-start, + self.formats["string"]) + state = self.INSIDE_DQ3STRING + elif key == "uf_sqstring": + self.setFormat(start, end-start, + self.formats["string"]) + state = self.INSIDE_SQSTRING + elif key == "uf_dqstring": + self.setFormat(start, end-start, + self.formats["string"]) + state = self.INSIDE_DQSTRING + else: + self.setFormat(start, end-start, self.formats[key]) + if key == "comment": + if text.lstrip().startswith(self.cell_separators): + self.found_cell_separators = True + oedata = OutlineExplorerData() + oedata.text = to_text_string(text).strip() + oedata.fold_level = start + oedata.def_type = OutlineExplorerData.CELL + oedata.def_name = text.strip() + elif self.OECOMMENT.match(text.lstrip()): + oedata = OutlineExplorerData() + oedata.text = to_text_string(text).strip() + oedata.fold_level = start + oedata.def_type = OutlineExplorerData.COMMENT + oedata.def_name = text.strip() + elif key == "keyword": + if value in ("def", "class"): + match1 = self.IDPROG.match(text, end) + if match1: + start1, end1 = match1.span(1) + self.setFormat(start1, end1-start1, + self.formats["definition"]) + oedata = OutlineExplorerData() + oedata.text = to_text_string(text) + oedata.fold_level = start + oedata.def_type = self.DEF_TYPES[ + to_text_string(value)] + oedata.def_name = text[start1:end1] + oedata.color = self.formats["definition"] + elif value in ("elif", "else", "except", "finally", + "for", "if", "try", "while", + "with"): + if text.lstrip().startswith(value): + oedata = OutlineExplorerData() + oedata.text = to_text_string(text).strip() + oedata.fold_level = start + oedata.def_type = \ + OutlineExplorerData.STATEMENT + oedata.def_name = text.strip() + elif value == "import": + import_stmt = text.strip() + # color all the "as" words on same line, except + # if in a comment; cheap approximation to the + # truth + if '#' in text: + endpos = text.index('#') + else: + endpos = len(text) + while True: + match1 = self.ASPROG.match(text, end, + endpos) + if not match1: + break + start, end = match1.span(1) + self.setFormat(start, end-start, + self.formats["keyword"]) + + match = self.PROG.search(text, match.end()) + + self.setCurrentBlockState(state) + + # Use normal format for indentation and trailing spaces. + self.formats['leading'] = self.formats['normal'] + self.formats['trailing'] = self.formats['normal'] + self.highlight_spaces(text, offset) + + if oedata is not None: + block_nb = self.currentBlock().blockNumber() + self.outlineexplorer_data[block_nb] = oedata + self.outlineexplorer_data['found_cell_separators'] = self.found_cell_separators + if import_stmt is not None: + block_nb = self.currentBlock().blockNumber() + self.import_statements[block_nb] = import_stmt + + def get_import_statements(self): + return list(self.import_statements.values()) + + def rehighlight(self): + self.import_statements = {} + self.found_cell_separators = False + BaseSH.rehighlight(self) + + +#============================================================================== +# Cython syntax highlighter +#============================================================================== +C_TYPES = 'bool char double enum float int long mutable short signed struct unsigned void' + +class CythonSH(PythonSH): + """Cython Syntax Highlighter""" + ADDITIONAL_KEYWORDS = ["cdef", "ctypedef", "cpdef", "inline", "cimport", + "DEF"] + ADDITIONAL_BUILTINS = C_TYPES.split() + PROG = re.compile(make_python_patterns(ADDITIONAL_KEYWORDS, + ADDITIONAL_BUILTINS), re.S) + IDPROG = re.compile(r"\s+([\w\.]+)", re.S) + + +#============================================================================== +# Enaml syntax highlighter +#============================================================================== +class EnamlSH(PythonSH): + """Enaml Syntax Highlighter""" + ADDITIONAL_KEYWORDS = ["enamldef", "template", "attr", "event", "const", "alias", + "func"] + ADDITIONAL_BUILTINS = [] + PROG = re.compile(make_python_patterns(ADDITIONAL_KEYWORDS, + ADDITIONAL_BUILTINS), re.S) + IDPROG = re.compile(r"\s+([\w\.]+)", re.S) + + +#============================================================================== +# C/C++ syntax highlighter +#============================================================================== +C_KEYWORDS1 = 'and and_eq bitand bitor break case catch const const_cast continue default delete do dynamic_cast else explicit export extern for friend goto if inline namespace new not not_eq operator or or_eq private protected public register reinterpret_cast return sizeof static static_cast switch template throw try typedef typeid typename union using virtual while xor xor_eq' +C_KEYWORDS2 = 'a addindex addtogroup anchor arg attention author b brief bug c class code date def defgroup deprecated dontinclude e em endcode endhtmlonly ifdef endif endlatexonly endlink endverbatim enum example exception f$ file fn hideinitializer htmlinclude htmlonly if image include ingroup internal invariant interface latexonly li line link mainpage name namespace nosubgrouping note overload p page par param post pre ref relates remarks return retval sa section see showinitializer since skip skipline subsection test throw todo typedef union until var verbatim verbinclude version warning weakgroup' +C_KEYWORDS3 = 'asm auto class compl false true volatile wchar_t' + +def make_generic_c_patterns(keywords, builtins, + instance=None, define=None, comment=None): + "Strongly inspired from idlelib.ColorDelegator.make_pat" + kw = r"\b" + any("keyword", keywords.split()) + r"\b" + builtin = r"\b" + any("builtin", builtins.split()+C_TYPES.split()) + r"\b" + if comment is None: + comment = any("comment", [r"//[^\n]*", r"\/\*(.*?)\*\/"]) + comment_start = any("comment_start", [r"\/\*"]) + comment_end = any("comment_end", [r"\*\/"]) + if instance is None: + instance = any("instance", [r"\bthis\b"]) + number = any("number", + [r"\b[+-]?[0-9]+[lL]?\b", + r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", + r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) + sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' + string = any("string", [sqstring, dqstring]) + if define is None: + define = any("define", [r"#[^\n]*"]) + return "|".join([instance, kw, comment, string, number, + comment_start, comment_end, builtin, + define, any("SYNC", [r"\n"])]) + +def make_cpp_patterns(): + return make_generic_c_patterns(C_KEYWORDS1+' '+C_KEYWORDS2, C_KEYWORDS3) + +class CppSH(BaseSH): + """C/C++ Syntax Highlighter""" + # Syntax highlighting rules: + PROG = re.compile(make_cpp_patterns(), re.S) + # Syntax highlighting states (from one text block to another): + NORMAL = 0 + INSIDE_COMMENT = 1 + def __init__(self, parent, font=None, color_scheme=None): + BaseSH.__init__(self, parent, font, color_scheme) + + def highlightBlock(self, text): + text = to_text_string(text) + inside_comment = self.previousBlockState() == self.INSIDE_COMMENT + self.setFormat(0, len(text), + self.formats["comment" if inside_comment else "normal"]) + + match = self.PROG.search(text) + index = 0 + while match: + for key, value in list(match.groupdict().items()): + if value: + start, end = match.span(key) + index += end-start + if key == "comment_start": + inside_comment = True + self.setFormat(start, len(text)-start, + self.formats["comment"]) + elif key == "comment_end": + inside_comment = False + self.setFormat(start, end-start, + self.formats["comment"]) + elif inside_comment: + self.setFormat(start, end-start, + self.formats["comment"]) + elif key == "define": + self.setFormat(start, end-start, + self.formats["number"]) + else: + self.setFormat(start, end-start, self.formats[key]) + + match = self.PROG.search(text, match.end()) + + self.highlight_spaces(text) + + last_state = self.INSIDE_COMMENT if inside_comment else self.NORMAL + self.setCurrentBlockState(last_state) + + +def make_opencl_patterns(): + # Keywords: + kwstr1 = 'cl_char cl_uchar cl_short cl_ushort cl_int cl_uint cl_long cl_ulong cl_half cl_float cl_double cl_platform_id cl_device_id cl_context cl_command_queue cl_mem cl_program cl_kernel cl_event cl_sampler cl_bool cl_bitfield cl_device_type cl_platform_info cl_device_info cl_device_address_info cl_device_fp_config cl_device_mem_cache_type cl_device_local_mem_type cl_device_exec_capabilities cl_command_queue_properties cl_context_properties cl_context_info cl_command_queue_info cl_channel_order cl_channel_type cl_mem_flags cl_mem_object_type cl_mem_info cl_image_info cl_addressing_mode cl_filter_mode cl_sampler_info cl_map_flags cl_program_info cl_program_build_info cl_build_status cl_kernel_info cl_kernel_work_group_info cl_event_info cl_command_type cl_profiling_info cl_image_format' + # Constants: + kwstr2 = 'CL_FALSE, CL_TRUE, CL_PLATFORM_PROFILE, CL_PLATFORM_VERSION, CL_PLATFORM_NAME, CL_PLATFORM_VENDOR, CL_PLATFORM_EXTENSIONS, CL_DEVICE_TYPE_DEFAULT , CL_DEVICE_TYPE_CPU, CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ACCELERATOR, CL_DEVICE_TYPE_ALL, CL_DEVICE_TYPE, CL_DEVICE_VENDOR_ID, CL_DEVICE_MAX_COMPUTE_UNITS, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, CL_DEVICE_MAX_WORK_GROUP_SIZE, CL_DEVICE_MAX_WORK_ITEM_SIZES, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, CL_DEVICE_MAX_CLOCK_FREQUENCY, CL_DEVICE_ADDRESS_BITS, CL_DEVICE_MAX_READ_IMAGE_ARGS, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, CL_DEVICE_MAX_MEM_ALLOC_SIZE, CL_DEVICE_IMAGE2D_MAX_WIDTH, CL_DEVICE_IMAGE2D_MAX_HEIGHT, CL_DEVICE_IMAGE3D_MAX_WIDTH, CL_DEVICE_IMAGE3D_MAX_HEIGHT, CL_DEVICE_IMAGE3D_MAX_DEPTH, CL_DEVICE_IMAGE_SUPPORT, CL_DEVICE_MAX_PARAMETER_SIZE, CL_DEVICE_MAX_SAMPLERS, CL_DEVICE_MEM_BASE_ADDR_ALIGN, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, CL_DEVICE_SINGLE_FP_CONFIG, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, CL_DEVICE_GLOBAL_MEM_SIZE, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, CL_DEVICE_MAX_CONSTANT_ARGS, CL_DEVICE_LOCAL_MEM_TYPE, CL_DEVICE_LOCAL_MEM_SIZE, CL_DEVICE_ERROR_CORRECTION_SUPPORT, CL_DEVICE_PROFILING_TIMER_RESOLUTION, CL_DEVICE_ENDIAN_LITTLE, CL_DEVICE_AVAILABLE, CL_DEVICE_COMPILER_AVAILABLE, CL_DEVICE_EXECUTION_CAPABILITIES, CL_DEVICE_QUEUE_PROPERTIES, CL_DEVICE_NAME, CL_DEVICE_VENDOR, CL_DRIVER_VERSION, CL_DEVICE_PROFILE, CL_DEVICE_VERSION, CL_DEVICE_EXTENSIONS, CL_DEVICE_PLATFORM, CL_FP_DENORM, CL_FP_INF_NAN, CL_FP_ROUND_TO_NEAREST, CL_FP_ROUND_TO_ZERO, CL_FP_ROUND_TO_INF, CL_FP_FMA, CL_NONE, CL_READ_ONLY_CACHE, CL_READ_WRITE_CACHE, CL_LOCAL, CL_GLOBAL, CL_EXEC_KERNEL, CL_EXEC_NATIVE_KERNEL, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, CL_QUEUE_PROFILING_ENABLE, CL_CONTEXT_REFERENCE_COUNT, CL_CONTEXT_DEVICES, CL_CONTEXT_PROPERTIES, CL_CONTEXT_PLATFORM, CL_QUEUE_CONTEXT, CL_QUEUE_DEVICE, CL_QUEUE_REFERENCE_COUNT, CL_QUEUE_PROPERTIES, CL_MEM_READ_WRITE, CL_MEM_WRITE_ONLY, CL_MEM_READ_ONLY, CL_MEM_USE_HOST_PTR, CL_MEM_ALLOC_HOST_PTR, CL_MEM_COPY_HOST_PTR, CL_R, CL_A, CL_RG, CL_RA, CL_RGB, CL_RGBA, CL_BGRA, CL_ARGB, CL_INTENSITY, CL_LUMINANCE, CL_SNORM_INT8, CL_SNORM_INT16, CL_UNORM_INT8, CL_UNORM_INT16, CL_UNORM_SHORT_565, CL_UNORM_SHORT_555, CL_UNORM_INT_101010, CL_SIGNED_INT8, CL_SIGNED_INT16, CL_SIGNED_INT32, CL_UNSIGNED_INT8, CL_UNSIGNED_INT16, CL_UNSIGNED_INT32, CL_HALF_FLOAT, CL_FLOAT, CL_MEM_OBJECT_BUFFER, CL_MEM_OBJECT_IMAGE2D, CL_MEM_OBJECT_IMAGE3D, CL_MEM_TYPE, CL_MEM_FLAGS, CL_MEM_SIZECL_MEM_HOST_PTR, CL_MEM_HOST_PTR, CL_MEM_MAP_COUNT, CL_MEM_REFERENCE_COUNT, CL_MEM_CONTEXT, CL_IMAGE_FORMAT, CL_IMAGE_ELEMENT_SIZE, CL_IMAGE_ROW_PITCH, CL_IMAGE_SLICE_PITCH, CL_IMAGE_WIDTH, CL_IMAGE_HEIGHT, CL_IMAGE_DEPTH, CL_ADDRESS_NONE, CL_ADDRESS_CLAMP_TO_EDGE, CL_ADDRESS_CLAMP, CL_ADDRESS_REPEAT, CL_FILTER_NEAREST, CL_FILTER_LINEAR, CL_SAMPLER_REFERENCE_COUNT, CL_SAMPLER_CONTEXT, CL_SAMPLER_NORMALIZED_COORDS, CL_SAMPLER_ADDRESSING_MODE, CL_SAMPLER_FILTER_MODE, CL_MAP_READ, CL_MAP_WRITE, CL_PROGRAM_REFERENCE_COUNT, CL_PROGRAM_CONTEXT, CL_PROGRAM_NUM_DEVICES, CL_PROGRAM_DEVICES, CL_PROGRAM_SOURCE, CL_PROGRAM_BINARY_SIZES, CL_PROGRAM_BINARIES, CL_PROGRAM_BUILD_STATUS, CL_PROGRAM_BUILD_OPTIONS, CL_PROGRAM_BUILD_LOG, CL_BUILD_SUCCESS, CL_BUILD_NONE, CL_BUILD_ERROR, CL_BUILD_IN_PROGRESS, CL_KERNEL_FUNCTION_NAME, CL_KERNEL_NUM_ARGS, CL_KERNEL_REFERENCE_COUNT, CL_KERNEL_CONTEXT, CL_KERNEL_PROGRAM, CL_KERNEL_WORK_GROUP_SIZE, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, CL_KERNEL_LOCAL_MEM_SIZE, CL_EVENT_COMMAND_QUEUE, CL_EVENT_COMMAND_TYPE, CL_EVENT_REFERENCE_COUNT, CL_EVENT_COMMAND_EXECUTION_STATUS, CL_COMMAND_NDRANGE_KERNEL, CL_COMMAND_TASK, CL_COMMAND_NATIVE_KERNEL, CL_COMMAND_READ_BUFFER, CL_COMMAND_WRITE_BUFFER, CL_COMMAND_COPY_BUFFER, CL_COMMAND_READ_IMAGE, CL_COMMAND_WRITE_IMAGE, CL_COMMAND_COPY_IMAGE, CL_COMMAND_COPY_IMAGE_TO_BUFFER, CL_COMMAND_COPY_BUFFER_TO_IMAGE, CL_COMMAND_MAP_BUFFER, CL_COMMAND_MAP_IMAGE, CL_COMMAND_UNMAP_MEM_OBJECT, CL_COMMAND_MARKER, CL_COMMAND_ACQUIRE_GL_OBJECTS, CL_COMMAND_RELEASE_GL_OBJECTS, command execution status, CL_COMPLETE, CL_RUNNING, CL_SUBMITTED, CL_QUEUED, CL_PROFILING_COMMAND_QUEUED, CL_PROFILING_COMMAND_SUBMIT, CL_PROFILING_COMMAND_START, CL_PROFILING_COMMAND_END, CL_CHAR_BIT, CL_SCHAR_MAX, CL_SCHAR_MIN, CL_CHAR_MAX, CL_CHAR_MIN, CL_UCHAR_MAX, CL_SHRT_MAX, CL_SHRT_MIN, CL_USHRT_MAX, CL_INT_MAX, CL_INT_MIN, CL_UINT_MAX, CL_LONG_MAX, CL_LONG_MIN, CL_ULONG_MAX, CL_FLT_DIG, CL_FLT_MANT_DIG, CL_FLT_MAX_10_EXP, CL_FLT_MAX_EXP, CL_FLT_MIN_10_EXP, CL_FLT_MIN_EXP, CL_FLT_RADIX, CL_FLT_MAX, CL_FLT_MIN, CL_FLT_EPSILON, CL_DBL_DIG, CL_DBL_MANT_DIG, CL_DBL_MAX_10_EXP, CL_DBL_MAX_EXP, CL_DBL_MIN_10_EXP, CL_DBL_MIN_EXP, CL_DBL_RADIX, CL_DBL_MAX, CL_DBL_MIN, CL_DBL_EPSILON, CL_SUCCESS, CL_DEVICE_NOT_FOUND, CL_DEVICE_NOT_AVAILABLE, CL_COMPILER_NOT_AVAILABLE, CL_MEM_OBJECT_ALLOCATION_FAILURE, CL_OUT_OF_RESOURCES, CL_OUT_OF_HOST_MEMORY, CL_PROFILING_INFO_NOT_AVAILABLE, CL_MEM_COPY_OVERLAP, CL_IMAGE_FORMAT_MISMATCH, CL_IMAGE_FORMAT_NOT_SUPPORTED, CL_BUILD_PROGRAM_FAILURE, CL_MAP_FAILURE, CL_INVALID_VALUE, CL_INVALID_DEVICE_TYPE, CL_INVALID_PLATFORM, CL_INVALID_DEVICE, CL_INVALID_CONTEXT, CL_INVALID_QUEUE_PROPERTIES, CL_INVALID_COMMAND_QUEUE, CL_INVALID_HOST_PTR, CL_INVALID_MEM_OBJECT, CL_INVALID_IMAGE_FORMAT_DESCRIPTOR, CL_INVALID_IMAGE_SIZE, CL_INVALID_SAMPLER, CL_INVALID_BINARY, CL_INVALID_BUILD_OPTIONS, CL_INVALID_PROGRAM, CL_INVALID_PROGRAM_EXECUTABLE, CL_INVALID_KERNEL_NAME, CL_INVALID_KERNEL_DEFINITION, CL_INVALID_KERNEL, CL_INVALID_ARG_INDEX, CL_INVALID_ARG_VALUE, CL_INVALID_ARG_SIZE, CL_INVALID_KERNEL_ARGS, CL_INVALID_WORK_DIMENSION, CL_INVALID_WORK_GROUP_SIZE, CL_INVALID_WORK_ITEM_SIZE, CL_INVALID_GLOBAL_OFFSET, CL_INVALID_EVENT_WAIT_LIST, CL_INVALID_EVENT, CL_INVALID_OPERATION, CL_INVALID_GL_OBJECT, CL_INVALID_BUFFER_SIZE, CL_INVALID_MIP_LEVEL, CL_INVALID_GLOBAL_WORK_SIZE' + # Functions: + builtins = 'clGetPlatformIDs, clGetPlatformInfo, clGetDeviceIDs, clGetDeviceInfo, clCreateContext, clCreateContextFromType, clReleaseContext, clGetContextInfo, clCreateCommandQueue, clRetainCommandQueue, clReleaseCommandQueue, clGetCommandQueueInfo, clSetCommandQueueProperty, clCreateBuffer, clCreateImage2D, clCreateImage3D, clRetainMemObject, clReleaseMemObject, clGetSupportedImageFormats, clGetMemObjectInfo, clGetImageInfo, clCreateSampler, clRetainSampler, clReleaseSampler, clGetSamplerInfo, clCreateProgramWithSource, clCreateProgramWithBinary, clRetainProgram, clReleaseProgram, clBuildProgram, clUnloadCompiler, clGetProgramInfo, clGetProgramBuildInfo, clCreateKernel, clCreateKernelsInProgram, clRetainKernel, clReleaseKernel, clSetKernelArg, clGetKernelInfo, clGetKernelWorkGroupInfo, clWaitForEvents, clGetEventInfo, clRetainEvent, clReleaseEvent, clGetEventProfilingInfo, clFlush, clFinish, clEnqueueReadBuffer, clEnqueueWriteBuffer, clEnqueueCopyBuffer, clEnqueueReadImage, clEnqueueWriteImage, clEnqueueCopyImage, clEnqueueCopyImageToBuffer, clEnqueueCopyBufferToImage, clEnqueueMapBuffer, clEnqueueMapImage, clEnqueueUnmapMemObject, clEnqueueNDRangeKernel, clEnqueueTask, clEnqueueNativeKernel, clEnqueueMarker, clEnqueueWaitForEvents, clEnqueueBarrier' + # Qualifiers: + qualifiers = '__global __local __constant __private __kernel' + keyword_list = C_KEYWORDS1+' '+C_KEYWORDS2+' '+kwstr1+' '+kwstr2 + builtin_list = C_KEYWORDS3+' '+builtins+' '+qualifiers + return make_generic_c_patterns(keyword_list, builtin_list) + +class OpenCLSH(CppSH): + """OpenCL Syntax Highlighter""" + PROG = re.compile(make_opencl_patterns(), re.S) + + +#============================================================================== +# Fortran Syntax Highlighter +#============================================================================== + +def make_fortran_patterns(): + "Strongly inspired from idlelib.ColorDelegator.make_pat" + kwstr = 'access action advance allocatable allocate apostrophe assign assignment associate asynchronous backspace bind blank blockdata call case character class close common complex contains continue cycle data deallocate decimal delim default dimension direct do dowhile double doubleprecision else elseif elsewhere encoding end endassociate endblockdata enddo endfile endforall endfunction endif endinterface endmodule endprogram endselect endsubroutine endtype endwhere entry eor equivalence err errmsg exist exit external file flush fmt forall form format formatted function go goto id if implicit in include inout integer inquire intent interface intrinsic iomsg iolength iostat kind len logical module name named namelist nextrec nml none nullify number only open opened operator optional out pad parameter pass pause pending pointer pos position precision print private program protected public quote read readwrite real rec recl recursive result return rewind save select selectcase selecttype sequential sign size stat status stop stream subroutine target then to type unformatted unit use value volatile wait where while write' + bistr1 = 'abs achar acos acosd adjustl adjustr aimag aimax0 aimin0 aint ajmax0 ajmin0 akmax0 akmin0 all allocated alog alog10 amax0 amax1 amin0 amin1 amod anint any asin asind associated atan atan2 atan2d atand bitest bitl bitlr bitrl bjtest bit_size bktest break btest cabs ccos cdabs cdcos cdexp cdlog cdsin cdsqrt ceiling cexp char clog cmplx conjg cos cosd cosh count cpu_time cshift csin csqrt dabs dacos dacosd dasin dasind datan datan2 datan2d datand date date_and_time dble dcmplx dconjg dcos dcosd dcosh dcotan ddim dexp dfloat dflotk dfloti dflotj digits dim dimag dint dlog dlog10 dmax1 dmin1 dmod dnint dot_product dprod dreal dsign dsin dsind dsinh dsqrt dtan dtand dtanh eoshift epsilon errsns exp exponent float floati floatj floatk floor fraction free huge iabs iachar iand ibclr ibits ibset ichar idate idim idint idnint ieor ifix iiabs iiand iibclr iibits iibset iidim iidint iidnnt iieor iifix iint iior iiqint iiqnnt iishft iishftc iisign ilen imax0 imax1 imin0 imin1 imod index inint inot int int1 int2 int4 int8 iqint iqnint ior ishft ishftc isign isnan izext jiand jibclr jibits jibset jidim jidint jidnnt jieor jifix jint jior jiqint jiqnnt jishft jishftc jisign jmax0 jmax1 jmin0 jmin1 jmod jnint jnot jzext kiabs kiand kibclr kibits kibset kidim kidint kidnnt kieor kifix kind kint kior kishft kishftc kisign kmax0 kmax1 kmin0 kmin1 kmod knint knot kzext lbound leadz len len_trim lenlge lge lgt lle llt log log10 logical lshift malloc matmul max max0 max1 maxexponent maxloc maxval merge min min0 min1 minexponent minloc minval mod modulo mvbits nearest nint not nworkers number_of_processors pack popcnt poppar precision present product radix random random_number random_seed range real repeat reshape rrspacing rshift scale scan secnds selected_int_kind selected_real_kind set_exponent shape sign sin sind sinh size sizeof sngl snglq spacing spread sqrt sum system_clock tan tand tanh tiny transfer transpose trim ubound unpack verify' + bistr2 = 'cdabs cdcos cdexp cdlog cdsin cdsqrt cotan cotand dcmplx dconjg dcotan dcotand decode dimag dll_export dll_import doublecomplex dreal dvchk encode find flen flush getarg getcharqq getcl getdat getenv gettim hfix ibchng identifier imag int1 int2 int4 intc intrup invalop iostat_msg isha ishc ishl jfix lacfar locking locnear map nargs nbreak ndperr ndpexc offset ovefl peekcharqq precfill prompt qabs qacos qacosd qasin qasind qatan qatand qatan2 qcmplx qconjg qcos qcosd qcosh qdim qexp qext qextd qfloat qimag qlog qlog10 qmax1 qmin1 qmod qreal qsign qsin qsind qsinh qsqrt qtan qtand qtanh ran rand randu rewrite segment setdat settim system timer undfl unlock union val virtual volatile zabs zcos zexp zlog zsin zsqrt' + kw = r"\b" + any("keyword", kwstr.split()) + r"\b" + builtin = r"\b" + any("builtin", bistr1.split()+bistr2.split()) + r"\b" + comment = any("comment", [r"\![^\n]*"]) + number = any("number", + [r"\b[+-]?[0-9]+[lL]?\b", + r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", + r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) + sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' + string = any("string", [sqstring, dqstring]) + return "|".join([kw, comment, string, number, builtin, + any("SYNC", [r"\n"])]) + +class FortranSH(BaseSH): + """Fortran Syntax Highlighter""" + # Syntax highlighting rules: + PROG = re.compile(make_fortran_patterns(), re.S|re.I) + IDPROG = re.compile(r"\s+(\w+)", re.S) + # Syntax highlighting states (from one text block to another): + NORMAL = 0 + def __init__(self, parent, font=None, color_scheme=None): + BaseSH.__init__(self, parent, font, color_scheme) + + def highlightBlock(self, text): + text = to_text_string(text) + self.setFormat(0, len(text), self.formats["normal"]) + + match = self.PROG.search(text) + index = 0 + while match: + for key, value in list(match.groupdict().items()): + if value: + start, end = match.span(key) + index += end-start + self.setFormat(start, end-start, self.formats[key]) + if value.lower() in ("subroutine", "module", "function"): + match1 = self.IDPROG.match(text, end) + if match1: + start1, end1 = match1.span(1) + self.setFormat(start1, end1-start1, + self.formats["definition"]) + + match = self.PROG.search(text, match.end()) + + self.highlight_spaces(text) + +class Fortran77SH(FortranSH): + """Fortran 77 Syntax Highlighter""" + def highlightBlock(self, text): + text = to_text_string(text) + if text.startswith(("c", "C")): + self.setFormat(0, len(text), self.formats["comment"]) + self.highlight_spaces(text) + else: + FortranSH.highlightBlock(self, text) + self.setFormat(0, 5, self.formats["comment"]) + self.setFormat(73, max([73, len(text)]), + self.formats["comment"]) + + +#============================================================================== +# IDL highlighter +# +# Contribution from Stuart Mumford (Littlemumford) - 02/02/2012 +# See Issue #850 +#============================================================================== +def make_idl_patterns(): + "Strongly inspired from idlelib.ColorDelegator.make_pat" + kwstr = 'begin of pro function endfor endif endwhile endrep endcase endswitch end if then else for do while repeat until break case switch common continue exit return goto help message print read retall stop' + bistr1 = 'a_correlate abs acos adapt_hist_equal alog alog10 amoeba arg_present arra_equal array_indices ascii_template asin assoc atan beseli beselj besel k besely beta bilinear bin_date binary_template dinfgen dinomial blk_con broyden bytarr byte bytscl c_correlate call_external call_function ceil chebyshev check_math chisqr_cvf chisqr_pdf choldc cholsol cindgen clust_wts cluster color_quan colormap_applicable comfit complex complexarr complexround compute_mesh_normals cond congrid conj convert_coord convol coord2to3 correlate cos cosh cramer create_struct crossp crvlength ct_luminance cti_test curvefit cv_coord cvttobm cw_animate cw_arcball cw_bgroup cw_clr_index cw_colorsel cw_defroi cw_field cw_filesel cw_form cw_fslider cw_light_editor cw_orient cw_palette_editor cw_pdmenu cw_rgbslider cw_tmpl cw_zoom dblarr dcindgen dcomplexarr defroi deriv derivsig determ diag_matrix dialog_message dialog_pickfile pialog_printersetup dialog_printjob dialog_read_image dialog_write_image digital_filter dilate dindgen dist double eigenql eigenvec elmhes eof erode erf erfc erfcx execute exp expand_path expint extrac extract_slice f_cvf f_pdf factorial fft file_basename file_dirname file_expand_path file_info file_same file_search file_test file_which filepath findfile findgen finite fix float floor fltarr format_axis_values fstat fulstr fv_test fx_root fz_roots gamma gauss_cvf gauss_pdf gauss2dfit gaussfit gaussint get_drive_list get_kbrd get_screen_size getenv grid_tps grid3 griddata gs_iter hanning hdf_browser hdf_read hilbert hist_2d hist_equal histogram hough hqr ibeta identity idl_validname idlitsys_createtool igamma imaginary indgen int_2d int_3d int_tabulated intarr interpol interpolate invert ioctl ishft julday keword_set krig2d kurtosis kw_test l64indgen label_date label_region ladfit laguerre la_cholmprove la_cholsol la_Determ la_eigenproblem la_eigenql la_eigenvec la_elmhes la_gm_linear_model la_hqr la_invert la_least_square_equality la_least_squares la_linear_equation la_lumprove la_lusol la_trimprove la_trisol leefit legendre linbcg lindgen linfit ll_arc_distance lmfit lmgr lngamma lnp_test locale_get logical_and logical_or logical_true lon64arr lonarr long long64 lsode lu_complex lumprove lusol m_correlate machar make_array map_2points map_image map_patch map_proj_forward map_proj_init map_proj_inverse matrix_multiply matrix_power max md_test mean meanabsdev median memory mesh_clip mesh_decimate mesh_issolid mesh_merge mesh_numtriangles mesh_smooth mesh_surfacearea mesh_validate mesh_volume min min_curve_surf moment morph_close morph_distance morph_gradient morph_histormiss morph_open morph_thin morph_tophat mpeg_open msg_cat_open n_elements n_params n_tags newton norm obj_class obj_isa obj_new obj_valid objarr p_correlate path_sep pcomp pnt_line polar_surface poly poly_2d poly_area poly_fit polyfillv ployshade primes product profile profiles project_vol ptr_new ptr_valid ptrarr qgrid3 qromb qromo qsimp query_bmp query_dicom query_image query_jpeg query_mrsid query_pict query_png query_ppm query_srf query_tiff query_wav r_correlate r_test radon randomn randomu ranks read_ascii read_binary read_bmp read_dicom read_image read_mrsid read_png read_spr read_sylk read_tiff read_wav read_xwd real_part rebin recall_commands recon3 reform region_grow regress replicate reverse rk4 roberts rot rotate round routine_info rs_test s_test savgol search2d search3d sfit shift shmdebug shmvar simplex sin sindgen sinh size skewness smooth sobel sort sph_scat spher_harm spl_init spl_interp spline spline_p sprsab sprsax sprsin sprstp sqrt standardize stddev strarr strcmp strcompress stregex string strjoin strlen strlowcase strmatch strmessage strmid strpos strsplit strtrim strupcase svdfit svsol swap_endian systime t_cvf t_pdf tag_names tan tanh temporary tetra_clip tetra_surface tetra_volume thin timegen tm_test total trace transpose tri_surf trigrid trisol ts_coef ts_diff ts_fcast ts_smooth tvrd uindgen unit uintarr ul64indgen ulindgen ulon64arr ulonarr ulong ulong64 uniq value_locate variance vert_t3d voigt voxel_proj warp_tri watershed where widget_actevix widget_base widget_button widget_combobox widget_draw widget_droplist widget_event widget_info widget_label widget_list widget_propertsheet widget_slider widget_tab widget_table widget_text widget_tree write_sylk wtn xfont xregistered xsq_test' + bistr2 = 'annotate arrow axis bar_plot blas_axpy box_cursor breakpoint byteorder caldata calendar call_method call_procedure catch cd cir_3pnt close color_convert compile_opt constrained_min contour copy_lun cpu create_view cursor cw_animate_getp cw_animate_load cw_animate_run cw_light_editor_get cw_light_editor_set cw_palette_editor_get cw_palette_editor_set define_key define_msgblk define_msgblk_from_file defsysv delvar device dfpmin dissolve dlm_load doc_librar draw_roi efont empty enable_sysrtn erase errplot expand file_chmod file_copy file_delete file_lines file_link file_mkdir file_move file_readlink flick flow3 flush forward_function free_lun funct gamma_ct get_lun grid_input h_eq_ct h_eq_int heap_free heap_gc hls hsv icontour iimage image_cont image_statistics internal_volume iplot isocontour isosurface isurface itcurrent itdelete itgetcurrent itregister itreset ivolume journal la_choldc la_ludc la_svd la_tridc la_triql la_trired linkimage loadct ludc make_dll map_continents map_grid map_proj_info map_set mesh_obj mk_html_help modifyct mpeg_close mpeg_put mpeg_save msg_cat_close msg_cat_compile multi obj_destroy on_error on_ioerror online_help openr openw openu oplot oploterr particle_trace path_cache plot plot_3dbox plot_field ploterr plots point_lun polar_contour polyfill polywarp popd powell printf printd ps_show_fonts psafm pseudo ptr_free pushd qhull rdpix readf read_interfile read_jpeg read_pict read_ppm read_srf read_wave read_x11_bitmap reads readu reduce_colors register_cursor replicate_inplace resolve_all resolve_routine restore save scale3 scale3d set_plot set_shading setenv setup_keys shade_surf shade_surf_irr shade_volume shmmap show3 showfont skip_lun slicer3 slide_image socket spawn sph_4pnt streamline stretch strput struct_assign struct_hide surface surfr svdc swap_enian_inplace t3d tek_color threed time_test2 triangulate triql trired truncate_lun tv tvcrs tvlct tvscl usersym vector_field vel velovect voronoi wait wdelete wf_draw widget_control widget_displaycontextmenu window write_bmp write_image write_jpeg write_nrif write_pict write_png write_ppm write_spr write_srf write_tiff write_wav write_wave writeu wset wshow xbm_edit xdisplayfile xdxf xinteranimate xloadct xmanager xmng_tmpl xmtool xobjview xobjview_rotate xobjview_write_image xpalette xpcolo xplot3d xroi xsurface xvaredit xvolume xyouts zoom zoom_24' + kw = r"\b" + any("keyword", kwstr.split()) + r"\b" + builtin = r"\b" + any("builtin", bistr1.split()+bistr2.split()) + r"\b" + comment = any("comment", [r"\;[^\n]*"]) + number = any("number", + [r"\b[+-]?[0-9]+[lL]?\b", + r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", + r"\b\.[0-9]d0|\.d0+[lL]?\b", + r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) + sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' + string = any("string", [sqstring, dqstring]) + return "|".join([kw, comment, string, number, builtin, + any("SYNC", [r"\n"])]) + +class IdlSH(GenericSH): + """IDL Syntax Highlighter""" + PROG = re.compile(make_idl_patterns(), re.S|re.I) + + +#============================================================================== +# Diff/Patch highlighter +#============================================================================== + +class DiffSH(BaseSH): + """Simple Diff/Patch Syntax Highlighter Class""" + def highlightBlock(self, text): + text = to_text_string(text) + if text.startswith("+++"): + self.setFormat(0, len(text), self.formats["keyword"]) + elif text.startswith("---"): + self.setFormat(0, len(text), self.formats["keyword"]) + elif text.startswith("+"): + self.setFormat(0, len(text), self.formats["string"]) + elif text.startswith("-"): + self.setFormat(0, len(text), self.formats["number"]) + elif text.startswith("@"): + self.setFormat(0, len(text), self.formats["builtin"]) + + self.highlight_spaces(text) + +#============================================================================== +# NSIS highlighter +#============================================================================== + +def make_nsis_patterns(): + "Strongly inspired from idlelib.ColorDelegator.make_pat" + kwstr1 = 'Abort AddBrandingImage AddSize AllowRootDirInstall AllowSkipFiles AutoCloseWindow BGFont BGGradient BrandingText BringToFront Call CallInstDLL Caption ClearErrors CompletedText ComponentText CopyFiles CRCCheck CreateDirectory CreateFont CreateShortCut Delete DeleteINISec DeleteINIStr DeleteRegKey DeleteRegValue DetailPrint DetailsButtonText DirText DirVar DirVerify EnableWindow EnumRegKey EnumRegValue Exec ExecShell ExecWait Exch ExpandEnvStrings File FileBufSize FileClose FileErrorText FileOpen FileRead FileReadByte FileSeek FileWrite FileWriteByte FindClose FindFirst FindNext FindWindow FlushINI Function FunctionEnd GetCurInstType GetCurrentAddress GetDlgItem GetDLLVersion GetDLLVersionLocal GetErrorLevel GetFileTime GetFileTimeLocal GetFullPathName GetFunctionAddress GetInstDirError GetLabelAddress GetTempFileName Goto HideWindow ChangeUI CheckBitmap Icon IfAbort IfErrors IfFileExists IfRebootFlag IfSilent InitPluginsDir InstallButtonText InstallColors InstallDir InstallDirRegKey InstProgressFlags InstType InstTypeGetText InstTypeSetText IntCmp IntCmpU IntFmt IntOp IsWindow LangString LicenseBkColor LicenseData LicenseForceSelection LicenseLangString LicenseText LoadLanguageFile LogSet LogText MessageBox MiscButtonText Name OutFile Page PageCallbacks PageEx PageExEnd Pop Push Quit ReadEnvStr ReadINIStr ReadRegDWORD ReadRegStr Reboot RegDLL Rename ReserveFile Return RMDir SearchPath Section SectionEnd SectionGetFlags SectionGetInstTypes SectionGetSize SectionGetText SectionIn SectionSetFlags SectionSetInstTypes SectionSetSize SectionSetText SendMessage SetAutoClose SetBrandingImage SetCompress SetCompressor SetCompressorDictSize SetCtlColors SetCurInstType SetDatablockOptimize SetDateSave SetDetailsPrint SetDetailsView SetErrorLevel SetErrors SetFileAttributes SetFont SetOutPath SetOverwrite SetPluginUnload SetRebootFlag SetShellVarContext SetSilent ShowInstDetails ShowUninstDetails ShowWindow SilentInstall SilentUnInstall Sleep SpaceTexts StrCmp StrCpy StrLen SubCaption SubSection SubSectionEnd UninstallButtonText UninstallCaption UninstallIcon UninstallSubCaption UninstallText UninstPage UnRegDLL Var VIAddVersionKey VIProductVersion WindowIcon WriteINIStr WriteRegBin WriteRegDWORD WriteRegExpandStr WriteRegStr WriteUninstaller XPStyle' + kwstr2 = 'all alwaysoff ARCHIVE auto both bzip2 components current custom details directory false FILE_ATTRIBUTE_ARCHIVE FILE_ATTRIBUTE_HIDDEN FILE_ATTRIBUTE_NORMAL FILE_ATTRIBUTE_OFFLINE FILE_ATTRIBUTE_READONLY FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_TEMPORARY force grey HIDDEN hide IDABORT IDCANCEL IDIGNORE IDNO IDOK IDRETRY IDYES ifdiff ifnewer instfiles instfiles lastused leave left level license listonly lzma manual MB_ABORTRETRYIGNORE MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3 MB_DEFBUTTON4 MB_ICONEXCLAMATION MB_ICONINFORMATION MB_ICONQUESTION MB_ICONSTOP MB_OK MB_OKCANCEL MB_RETRYCANCEL MB_RIGHT MB_SETFOREGROUND MB_TOPMOST MB_YESNO MB_YESNOCANCEL nevershow none NORMAL off OFFLINE on READONLY right RO show silent silentlog SYSTEM TEMPORARY text textonly true try uninstConfirm windows zlib' + kwstr3 = 'MUI_ABORTWARNING MUI_ABORTWARNING_CANCEL_DEFAULT MUI_ABORTWARNING_TEXT MUI_BGCOLOR MUI_COMPONENTSPAGE_CHECKBITMAP MUI_COMPONENTSPAGE_NODESC MUI_COMPONENTSPAGE_SMALLDESC MUI_COMPONENTSPAGE_TEXT_COMPLIST MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_INFO MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_TITLE MUI_COMPONENTSPAGE_TEXT_INSTTYPE MUI_COMPONENTSPAGE_TEXT_TOP MUI_CUSTOMFUNCTION_ABORT MUI_CUSTOMFUNCTION_GUIINIT MUI_CUSTOMFUNCTION_UNABORT MUI_CUSTOMFUNCTION_UNGUIINIT MUI_DESCRIPTION_TEXT MUI_DIRECTORYPAGE_BGCOLOR MUI_DIRECTORYPAGE_TEXT_DESTINATION MUI_DIRECTORYPAGE_TEXT_TOP MUI_DIRECTORYPAGE_VARIABLE MUI_DIRECTORYPAGE_VERIFYONLEAVE MUI_FINISHPAGE_BUTTON MUI_FINISHPAGE_CANCEL_ENABLED MUI_FINISHPAGE_LINK MUI_FINISHPAGE_LINK_COLOR MUI_FINISHPAGE_LINK_LOCATION MUI_FINISHPAGE_NOAUTOCLOSE MUI_FINISHPAGE_NOREBOOTSUPPORT MUI_FINISHPAGE_REBOOTLATER_DEFAULT MUI_FINISHPAGE_RUN MUI_FINISHPAGE_RUN_FUNCTION MUI_FINISHPAGE_RUN_NOTCHECKED MUI_FINISHPAGE_RUN_PARAMETERS MUI_FINISHPAGE_RUN_TEXT MUI_FINISHPAGE_SHOWREADME MUI_FINISHPAGE_SHOWREADME_FUNCTION MUI_FINISHPAGE_SHOWREADME_NOTCHECKED MUI_FINISHPAGE_SHOWREADME_TEXT MUI_FINISHPAGE_TEXT MUI_FINISHPAGE_TEXT_LARGE MUI_FINISHPAGE_TEXT_REBOOT MUI_FINISHPAGE_TEXT_REBOOTLATER MUI_FINISHPAGE_TEXT_REBOOTNOW MUI_FINISHPAGE_TITLE MUI_FINISHPAGE_TITLE_3LINES MUI_FUNCTION_DESCRIPTION_BEGIN MUI_FUNCTION_DESCRIPTION_END MUI_HEADER_TEXT MUI_HEADER_TRANSPARENT_TEXT MUI_HEADERIMAGE MUI_HEADERIMAGE_BITMAP MUI_HEADERIMAGE_BITMAP_NOSTRETCH MUI_HEADERIMAGE_BITMAP_RTL MUI_HEADERIMAGE_BITMAP_RTL_NOSTRETCH MUI_HEADERIMAGE_RIGHT MUI_HEADERIMAGE_UNBITMAP MUI_HEADERIMAGE_UNBITMAP_NOSTRETCH MUI_HEADERIMAGE_UNBITMAP_RTL MUI_HEADERIMAGE_UNBITMAP_RTL_NOSTRETCH MUI_HWND MUI_ICON MUI_INSTALLCOLORS MUI_INSTALLOPTIONS_DISPLAY MUI_INSTALLOPTIONS_DISPLAY_RETURN MUI_INSTALLOPTIONS_EXTRACT MUI_INSTALLOPTIONS_EXTRACT_AS MUI_INSTALLOPTIONS_INITDIALOG MUI_INSTALLOPTIONS_READ MUI_INSTALLOPTIONS_SHOW MUI_INSTALLOPTIONS_SHOW_RETURN MUI_INSTALLOPTIONS_WRITE MUI_INSTFILESPAGE_ABORTHEADER_SUBTEXT MUI_INSTFILESPAGE_ABORTHEADER_TEXT MUI_INSTFILESPAGE_COLORS MUI_INSTFILESPAGE_FINISHHEADER_SUBTEXT MUI_INSTFILESPAGE_FINISHHEADER_TEXT MUI_INSTFILESPAGE_PROGRESSBAR MUI_LANGDLL_ALLLANGUAGES MUI_LANGDLL_ALWAYSSHOW MUI_LANGDLL_DISPLAY MUI_LANGDLL_INFO MUI_LANGDLL_REGISTRY_KEY MUI_LANGDLL_REGISTRY_ROOT MUI_LANGDLL_REGISTRY_VALUENAME MUI_LANGDLL_WINDOWTITLE MUI_LANGUAGE MUI_LICENSEPAGE_BGCOLOR MUI_LICENSEPAGE_BUTTON MUI_LICENSEPAGE_CHECKBOX MUI_LICENSEPAGE_CHECKBOX_TEXT MUI_LICENSEPAGE_RADIOBUTTONS MUI_LICENSEPAGE_RADIOBUTTONS_TEXT_ACCEPT MUI_LICENSEPAGE_RADIOBUTTONS_TEXT_DECLINE MUI_LICENSEPAGE_TEXT_BOTTOM MUI_LICENSEPAGE_TEXT_TOP MUI_PAGE_COMPONENTS MUI_PAGE_CUSTOMFUNCTION_LEAVE MUI_PAGE_CUSTOMFUNCTION_PRE MUI_PAGE_CUSTOMFUNCTION_SHOW MUI_PAGE_DIRECTORY MUI_PAGE_FINISH MUI_PAGE_HEADER_SUBTEXT MUI_PAGE_HEADER_TEXT MUI_PAGE_INSTFILES MUI_PAGE_LICENSE MUI_PAGE_STARTMENU MUI_PAGE_WELCOME MUI_RESERVEFILE_INSTALLOPTIONS MUI_RESERVEFILE_LANGDLL MUI_SPECIALINI MUI_STARTMENU_GETFOLDER MUI_STARTMENU_WRITE_BEGIN MUI_STARTMENU_WRITE_END MUI_STARTMENUPAGE_BGCOLOR MUI_STARTMENUPAGE_DEFAULTFOLDER MUI_STARTMENUPAGE_NODISABLE MUI_STARTMENUPAGE_REGISTRY_KEY MUI_STARTMENUPAGE_REGISTRY_ROOT MUI_STARTMENUPAGE_REGISTRY_VALUENAME MUI_STARTMENUPAGE_TEXT_CHECKBOX MUI_STARTMENUPAGE_TEXT_TOP MUI_UI MUI_UI_COMPONENTSPAGE_NODESC MUI_UI_COMPONENTSPAGE_SMALLDESC MUI_UI_HEADERIMAGE MUI_UI_HEADERIMAGE_RIGHT MUI_UNABORTWARNING MUI_UNABORTWARNING_CANCEL_DEFAULT MUI_UNABORTWARNING_TEXT MUI_UNCONFIRMPAGE_TEXT_LOCATION MUI_UNCONFIRMPAGE_TEXT_TOP MUI_UNFINISHPAGE_NOAUTOCLOSE MUI_UNFUNCTION_DESCRIPTION_BEGIN MUI_UNFUNCTION_DESCRIPTION_END MUI_UNGETLANGUAGE MUI_UNICON MUI_UNPAGE_COMPONENTS MUI_UNPAGE_CONFIRM MUI_UNPAGE_DIRECTORY MUI_UNPAGE_FINISH MUI_UNPAGE_INSTFILES MUI_UNPAGE_LICENSE MUI_UNPAGE_WELCOME MUI_UNWELCOMEFINISHPAGE_BITMAP MUI_UNWELCOMEFINISHPAGE_BITMAP_NOSTRETCH MUI_UNWELCOMEFINISHPAGE_INI MUI_WELCOMEFINISHPAGE_BITMAP MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH MUI_WELCOMEFINISHPAGE_CUSTOMFUNCTION_INIT MUI_WELCOMEFINISHPAGE_INI MUI_WELCOMEPAGE_TEXT MUI_WELCOMEPAGE_TITLE MUI_WELCOMEPAGE_TITLE_3LINES' + bistr = 'addincludedir addplugindir AndIf cd define echo else endif error execute If ifdef ifmacrodef ifmacrondef ifndef include insertmacro macro macroend onGUIEnd onGUIInit onInit onInstFailed onInstSuccess onMouseOverSection onRebootFailed onSelChange onUserAbort onVerifyInstDir OrIf packhdr system undef verbose warning' + instance = any("instance", [r'\$\{.*?\}', r'\$[A-Za-z0-9\_]*']) + define = any("define", [r"\![^\n]*"]) + comment = any("comment", [r"\;[^\n]*", r"\#[^\n]*", r"\/\*(.*?)\*\/"]) + return make_generic_c_patterns(kwstr1+' '+kwstr2+' '+kwstr3, bistr, + instance=instance, define=define, + comment=comment) + +class NsisSH(CppSH): + """NSIS Syntax Highlighter""" + # Syntax highlighting rules: + PROG = re.compile(make_nsis_patterns(), re.S) + + +#============================================================================== +# gettext highlighter +#============================================================================== + +def make_gettext_patterns(): + "Strongly inspired from idlelib.ColorDelegator.make_pat" + kwstr = 'msgid msgstr' + kw = r"\b" + any("keyword", kwstr.split()) + r"\b" + fuzzy = any("builtin", [r"#,[^\n]*"]) + links = any("normal", [r"#:[^\n]*"]) + comment = any("comment", [r"#[^\n]*"]) + number = any("number", + [r"\b[+-]?[0-9]+[lL]?\b", + r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", + r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) + sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' + string = any("string", [sqstring, dqstring]) + return "|".join([kw, string, number, fuzzy, links, comment, + any("SYNC", [r"\n"])]) + +class GetTextSH(GenericSH): + """gettext Syntax Highlighter""" + # Syntax highlighting rules: + PROG = re.compile(make_gettext_patterns(), re.S) + +#============================================================================== +# yaml highlighter +#============================================================================== + +def make_yaml_patterns(): + "Strongly inspired from sublime highlighter " + kw = any("keyword", [r":|>|-|\||\[|\]|[A-Za-z][\w\s\-\_ ]+(?=:)"]) + links = any("normal", [r"#:[^\n]*"]) + comment = any("comment", [r"#[^\n]*"]) + number = any("number", + [r"\b[+-]?[0-9]+[lL]?\b", + r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", + r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) + sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' + string = any("string", [sqstring, dqstring]) + return "|".join([kw, string, number, links, comment, + any("SYNC", [r"\n"])]) + +class YamlSH(GenericSH): + """yaml Syntax Highlighter""" + # Syntax highlighting rules: + PROG = re.compile(make_yaml_patterns(), re.S) + + +#============================================================================== +# HTML highlighter +#============================================================================== + +class BaseWebSH(BaseSH): + """Base class for CSS and HTML syntax highlighters""" + NORMAL = 0 + COMMENT = 1 + + def __init__(self, parent, font=None, color_scheme=None): + BaseSH.__init__(self, parent, font, color_scheme) + + def highlightBlock(self, text): + text = to_text_string(text) + previous_state = self.previousBlockState() + + if previous_state == self.COMMENT: + self.setFormat(0, len(text), self.formats["comment"]) + else: + previous_state = self.NORMAL + self.setFormat(0, len(text), self.formats["normal"]) + + self.setCurrentBlockState(previous_state) + match = self.PROG.search(text) + + match_count = 0 + n_characters = len(text) + # There should never be more matches than characters in the text. + while match and match_count < n_characters: + match_dict = match.groupdict() + for key, value in list(match_dict.items()): + if value: + start, end = match.span(key) + if previous_state == self.COMMENT: + if key == "multiline_comment_end": + self.setCurrentBlockState(self.NORMAL) + self.setFormat(end, len(text), + self.formats["normal"]) + else: + self.setCurrentBlockState(self.COMMENT) + self.setFormat(0, len(text), + self.formats["comment"]) + else: + if key == "multiline_comment_start": + self.setCurrentBlockState(self.COMMENT) + self.setFormat(start, len(text), + self.formats["comment"]) + else: + self.setCurrentBlockState(self.NORMAL) + try: + self.setFormat(start, end-start, + self.formats[key]) + except KeyError: + # happens with unmatched end-of-comment; + # see issue 1462 + pass + + match = self.PROG.search(text, match.end()) + match_count += 1 + + self.highlight_spaces(text) + +def make_html_patterns(): + """Strongly inspired from idlelib.ColorDelegator.make_pat """ + tags = any("builtin", [r"<", r"[\?/]?>", r"(?<=<).*?(?=[ >])"]) + keywords = any("keyword", [r" [\w:-]*?(?==)"]) + string = any("string", [r'".*?"']) + comment = any("comment", [r""]) + multiline_comment_start = any("multiline_comment_start", [r""]) + return "|".join([comment, multiline_comment_start, + multiline_comment_end, tags, keywords, string]) + +class HtmlSH(BaseWebSH): + """HTML Syntax Highlighter""" + PROG = re.compile(make_html_patterns(), re.S) + + +#============================================================================== +# Pygments based omni-parser +#============================================================================== + +# IMPORTANT NOTE: +# -------------- +# Do not be tempted to generalize the use of PygmentsSH (that is tempting +# because it would lead to more generic and compact code, and not only in +# this very module) because this generic syntax highlighter is far slower +# than the native ones (all classes above). For example, a Python syntax +# highlighter based on PygmentsSH would be 2 to 3 times slower than the +# current native PythonSH syntax highlighter. + +class PygmentsSH(BaseSH): + """ Generic Pygments syntax highlighter """ + # Store the language name and a ref to the lexer + _lang_name = None + _lexer = None + # Syntax highlighting states (from one text block to another): + NORMAL = 0 + def __init__(self, parent, font=None, color_scheme=None): + # Warning: do not move out those import statements + # (pygments is an optional dependency) + from pygments.lexers import get_lexer_by_name + from pygments.token import (Text, Other, Keyword, Name, String, Number, + Comment, Generic, Token) + # Map Pygments tokens to Spyder tokens + self._tokmap = {Text: "normal", + Generic: "normal", + Other: "normal", + Keyword: "keyword", + Token.Operator: "normal", + Name.Builtin: "builtin", + Name: "normal", + Comment: "comment", + String: "string", + Number: "number"} + # Load Pygments' Lexer + if self._lang_name is not None: + self._lexer = get_lexer_by_name(self._lang_name) + BaseSH.__init__(self, parent, font, color_scheme) + + def get_fmt(self, typ): + """ Get the format code for this type """ + # Exact matches first + for key in self._tokmap: + if typ is key: + return self._tokmap[key] + # Partial (parent-> child) matches + for key in self._tokmap: + if typ in key.subtypes: + return self._tokmap[key] + return 'normal' + + def highlightBlock(self, text): + """ Actually highlight the block """ + text = to_text_string(text) + lextree = self._lexer.get_tokens(text) + ct = 0 + for item in lextree: + typ, val = item + key = self.get_fmt(typ) + start = ct + ct += len(val) + self.setFormat(start, ct-start, self.formats[key]) + + self.highlight_spaces(text) + +def guess_pygments_highlighter(filename): + """Factory to generate syntax highlighter for the given filename. + + If a syntax highlighter is not available for a particular file, this + function will attempt to generate one based on the lexers in Pygments. If + Pygments is not available or does not have an appropriate lexer, TextSH + will be returned instead. + + """ + try: + from pygments.lexers import get_lexer_for_filename, get_lexer_by_name + from pygments.util import ClassNotFound + except ImportError: + return TextSH + root, ext = os.path.splitext(filename) + if ext in custom_extension_lexer_mapping: + lexer = get_lexer_by_name(custom_extension_lexer_mapping[ext]) + else: + try: + lexer = get_lexer_for_filename(filename) + except ClassNotFound: + return TextSH + class GuessedPygmentsSH(PygmentsSH): + _lexer = lexer + return GuessedPygmentsSH diff -Nru spyder-2.3.8+dfsg1/spyder/utils/system.py spyder-3.0.2+dfsg1/spyder/utils/system.py --- spyder-2.3.8+dfsg1/spyder/utils/system.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/system.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Operating system utilities""" + + +import os + +# Local imports +from spyder.utils import programs + + +def windows_memory_usage(): + """Return physical memory usage (float) + Works on Windows platforms only""" + from ctypes import windll, Structure, c_uint64, sizeof, byref + from ctypes.wintypes import DWORD + class MemoryStatus(Structure): + _fields_ = [('dwLength', DWORD), + ('dwMemoryLoad',DWORD), + ('ullTotalPhys', c_uint64), + ('ullAvailPhys', c_uint64), + ('ullTotalPageFile', c_uint64), + ('ullAvailPageFile', c_uint64), + ('ullTotalVirtual', c_uint64), + ('ullAvailVirtual', c_uint64), + ('ullAvailExtendedVirtual', c_uint64),] + memorystatus = MemoryStatus() + # MSDN documetation states that dwLength must be set to MemoryStatus + # size before calling GlobalMemoryStatusEx + # http://msdn.microsoft.com/en-us/library/aa366770(v=vs.85) + memorystatus.dwLength = sizeof(memorystatus) + windll.kernel32.GlobalMemoryStatusEx(byref(memorystatus)) + return float(memorystatus.dwMemoryLoad) + +def psutil_phymem_usage(): + """ + Return physical memory usage (float) + Requires the cross-platform psutil (>=v0.3) library + (http://code.google.com/p/psutil/) + """ + import psutil + # This is needed to avoid a deprecation warning error with + # newer psutil versions + try: + percent = psutil.virtual_memory().percent + except: + percent = psutil.phymem_usage().percent + return percent + +if programs.is_module_installed('psutil', '>=0.3.0'): + # Function `psutil.phymem_usage` was introduced in psutil v0.3.0 + memory_usage = psutil_phymem_usage +elif os.name == 'nt': + # Backup plan for Windows platforms + memory_usage = windows_memory_usage +else: + raise ImportError("Feature requires psutil 0.3+ on non Windows platforms") + + +if __name__ == '__main__': + print("*"*80) + print(memory_usage.__doc__) + print(memory_usage()) + if os.name == 'nt': + # windll can only be imported if os.name = 'nt' or 'ce' + print("*"*80) + print(windows_memory_usage.__doc__) + print(windows_memory_usage()) \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyder/utils/vcs.py spyder-3.0.2+dfsg1/spyder/utils/vcs.py --- spyder-2.3.8+dfsg1/spyder/utils/vcs.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/vcs.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Utilities for version control systems""" + +from __future__ import print_function + +import sys +import os.path as osp +import subprocess + +# Local imports +from spyder.utils import programs +from spyder.utils.misc import abspardir +from spyder.py3compat import PY3 + + +SUPPORTED = [ +{ + 'name': 'Mercurial', + 'rootdir': '.hg', + 'actions': dict( + commit=( ('thg', ['commit']), + ('hgtk', ['commit']) ), + browse=( ('thg', ['log']), + ('hgtk', ['log']) )) +}, { + 'name': 'Git', + 'rootdir': '.git', + 'actions': dict( + commit=( ('git', ['gui']), ), + browse=( ('gitk', []), )) +}] + + +class ActionToolNotFound(RuntimeError): + """Exception to transmit information about supported tools for + failed attempt to execute given action""" + + def __init__(self, vcsname, action, tools): + RuntimeError.__init__(self) + self.vcsname = vcsname + self.action = action + self.tools = tools + + +def get_vcs_info(path): + """Return support status dict if path is under VCS root""" + for info in SUPPORTED: + vcs_path = osp.join(path, info['rootdir']) + if osp.isdir(vcs_path): + return info + + +def get_vcs_root(path): + """Return VCS root directory path + Return None if path is not within a supported VCS repository""" + previous_path = path + while get_vcs_info(path) is None: + path = abspardir(path) + if path == previous_path: + return + else: + previous_path = path + return osp.abspath(path) + + +def is_vcs_repository(path): + """Return True if path is a supported VCS repository""" + return get_vcs_root(path) is not None + + +def run_vcs_tool(path, action): + """If path is a valid VCS repository, run the corresponding VCS tool + Supported VCS actions: 'commit', 'browse' + Return False if the VCS tool is not installed""" + info = get_vcs_info(get_vcs_root(path)) + tools = info['actions'][action] + for tool, args in tools: + if programs.find_program(tool): + programs.run_program(tool, args, cwd=path) + return + else: + cmdnames = [name for name, args in tools] + raise ActionToolNotFound(info['name'], action, cmdnames) + +def is_hg_installed(): + """Return True if Mercurial is installed""" + return programs.find_program('hg') is not None + + +def get_hg_revision(repopath): + """Return Mercurial revision for the repository located at repopath + Result is a tuple (global, local, branch), with None values on error + For example: + >>> get_hg_revision(".") + ('eba7273c69df+', '2015+', 'default') + """ + try: + assert osp.isdir(osp.join(repopath, '.hg')) + proc = programs.run_program('hg', ['id', '-nib', repopath]) + output, _err = proc.communicate() + # output is now: ('eba7273c69df+ 2015+ default\n', None) + # Split 2 times max to allow spaces in branch names. + return tuple(output.decode().strip().split(None, 2)) + except (subprocess.CalledProcessError, AssertionError, AttributeError): + # print("Error: Failed to get revision number from Mercurial - %s" % exc) + return (None, None, None) + + +def get_git_revision(repopath): + """ + Return Git revision for the repository located at repopath + + Result is a tuple (latest commit hash, branch), with None values on + error + """ + try: + git = programs.find_program('git') + assert git is not None and osp.isdir(osp.join(repopath, '.git')) + commit = programs.run_program(git, ['rev-parse', '--short', 'HEAD'], + cwd=repopath).communicate() + commit = commit[0].strip() + if PY3: + commit = commit.decode(sys.getdefaultencoding()) + + # Branch + branches = programs.run_program(git, ['branch'], + cwd=repopath).communicate() + branches = branches[0] + if PY3: + branches = branches.decode(sys.getdefaultencoding()) + branches = branches.split('\n') + active_branch = [b for b in branches if b.startswith('*')] + if len(active_branch) != 1: + branch = None + else: + branch = active_branch[0].split(None, 1)[1] + + return commit, branch + except (subprocess.CalledProcessError, AssertionError, AttributeError): + return None, None + + +if __name__ == '__main__': + print(get_vcs_root(osp.dirname(__file__))) + print(get_vcs_root(r'D:\Python\ipython\IPython\kernel')) + #run_vcs_tool(r'D:\Python\userconfig\userconfig', 'commit') + print(get_git_revision(osp.dirname(__file__)+"/../..")) + print(get_git_revision('/')) diff -Nru spyder-2.3.8+dfsg1/spyder/utils/windows.py spyder-3.0.2+dfsg1/spyder/utils/windows.py --- spyder-2.3.8+dfsg1/spyder/utils/windows.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/utils/windows.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Windows-specific utilities""" + + +from ctypes import windll + + +# --- Window control --- + +SW_SHOW = 5 # activate and display +SW_SHOWNA = 8 # show without activation +SW_HIDE = 0 + +GetConsoleWindow = windll.kernel32.GetConsoleWindow +ShowWindow = windll.user32.ShowWindow +IsWindowVisible = windll.user32.IsWindowVisible + +# Handle to console window associated with current Python +# interpreter procss, 0 if there is no window +console_window_handle = GetConsoleWindow() + +def set_attached_console_visible(state): + """Show/hide system console window attached to current process. + Return it's previous state. + + Availability: Windows""" + flag = {True: SW_SHOW, False: SW_HIDE} + return bool(ShowWindow(console_window_handle, flag[state])) + +def is_attached_console_visible(): + """Return True if attached console window is visible""" + return IsWindowVisible(console_window_handle) + +def set_windows_appusermodelid(): + """Make sure correct icon is used on Windows 7 taskbar""" + try: + return windll.shell32.SetCurrentProcessExplicitAppUserModelID("spyder.Spyder") + except AttributeError: + return "SetCurrentProcessExplicitAppUserModelID not found" + + +# [ ] the console state asks for a storage container +# [ ] reopen console on exit - better die open than become a zombie diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/arraybuilder.py spyder-3.0.2+dfsg1/spyder/widgets/arraybuilder.py --- spyder-2.3.8+dfsg1/spyder/widgets/arraybuilder.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/arraybuilder.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,403 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Numpy Matrix/Array Builder Widget. +""" + +# TODO: +# -Set font based on caller? editor console? and adjust size of widget +# -Fix positioning +# -Use the same font as editor/console? + +# Standard library imports +from __future__ import division +import re + +# Third party imports +from qtpy.QtCore import QEvent, QPoint, Qt +from qtpy.QtWidgets import (QDialog, QHBoxLayout, QLineEdit, QTableWidget, + QTableWidgetItem, QToolButton, QToolTip, + QWidget) + +# Local imports +from spyder.config.base import _ +from spyder.utils import icon_manager as ima +from spyder.widgets.helperwidgets import HelperToolButton + + +# Constants +SHORTCUT_TABLE = "Ctrl+M" +SHORTCUT_INLINE = "Ctrl+Alt+M" +ELEMENT_SEPARATOR = ', ' +ROW_SEPARATOR = ';' +BRACES = '], [' +NAN_VALUES = ['nan', 'NAN', 'NaN', 'Na', 'NA', 'na'] + + +class NumpyArrayInline(QLineEdit): + def __init__(self, parent): + QLineEdit.__init__(self, parent) + self._parent = parent + + def keyPressEvent(self, event): + """ + Qt override. + """ + if event.key() in [Qt.Key_Enter, Qt.Key_Return]: + self._parent.process_text() + if self._parent.is_valid(): + self._parent.keyPressEvent(event) + else: + QLineEdit.keyPressEvent(self, event) + + # to catch the Tab key event + def event(self, event): + """ + Qt override. + + This is needed to be able to intercept the Tab key press event. + """ + if event.type() == QEvent.KeyPress: + if (event.key() == Qt.Key_Tab or event.key() == Qt.Key_Space): + text = self.text() + cursor = self.cursorPosition() + # fix to include in "undo/redo" history + if cursor != 0 and text[cursor-1] == ' ': + text = text[:cursor-1] + ROW_SEPARATOR + ' ' +\ + text[cursor:] + else: + text = text[:cursor] + ' ' + text[cursor:] + self.setCursorPosition(cursor) + self.setText(text) + self.setCursorPosition(cursor + 1) + return False + return QWidget.event(self, event) + + +class NumpyArrayTable(QTableWidget): + def __init__(self, parent): + QTableWidget.__init__(self, parent) + self._parent = parent + self.setRowCount(2) + self.setColumnCount(2) + self.reset_headers() + + # signals + self.cellChanged.connect(self.cell_changed) + + def keyPressEvent(self, event): + """ + Qt override. + """ + if event.key() in [Qt.Key_Enter, Qt.Key_Return]: + QTableWidget.keyPressEvent(self, event) + # To avoid having to enter one final tab + self.setDisabled(True) + self.setDisabled(False) + self._parent.keyPressEvent(event) + else: + QTableWidget.keyPressEvent(self, event) + + def cell_changed(self, row, col): + """ + """ + item = self.item(row, col) + value = None + + if item: + rows = self.rowCount() + cols = self.columnCount() + value = item.text() + + if value: + if row == rows - 1: + self.setRowCount(rows + 1) + if col == cols - 1: + self.setColumnCount(cols + 1) + self.reset_headers() + + def reset_headers(self): + """ + Update the column and row numbering in the headers. + """ + rows = self.rowCount() + cols = self.columnCount() + + for r in range(rows): + self.setVerticalHeaderItem(r, QTableWidgetItem(str(r))) + for c in range(cols): + self.setHorizontalHeaderItem(c, QTableWidgetItem(str(c))) + self.setColumnWidth(c, 40) + + def text(self): + """ + Return the entered array in a parseable form. + """ + text = [] + rows = self.rowCount() + cols = self.columnCount() + + # handle empty table case + if rows == 2 and cols == 2: + item = self.item(0, 0) + if item is None: + return '' + + for r in range(rows - 1): + for c in range(cols - 1): + item = self.item(r, c) + if item is not None: + value = item.text() + else: + value = '0' + + if not value.strip(): + value = '0' + + text.append(' ') + text.append(value) + text.append(ROW_SEPARATOR) + + return ''.join(text[:-1]) # to remove the final uneeded ; + + +class NumpyArrayDialog(QDialog): + def __init__(self, parent=None, inline=True, offset=0, force_float=False): + QDialog.__init__(self, parent=parent) + self._parent = parent + self._text = None + self._valid = None + self._offset = offset + + # TODO: add this as an option in the General Preferences? + self._force_float = force_float + + self._help_inline = _(""" + Numpy Array/Matrix Helper
    + Type an array in Matlab : [1 2;3 4]
    + or Spyder simplified syntax : 1 2;3 4 +

    + Hit 'Enter' for array or 'Ctrl+Enter' for matrix. +

    + Hint:
    + Use two spaces or two tabs to generate a ';'. + """) + + self._help_table = _(""" + Numpy Array/Matrix Helper
    + Enter an array in the table.
    + Use Tab to move between cells. +

    + Hit 'Enter' for array or 'Ctrl+Enter' for matrix. +

    + Hint:
    + Use two tabs at the end of a row to move to the next row. + """) + + # Widgets + self._button_warning = QToolButton() + self._button_help = HelperToolButton() + self._button_help.setIcon(ima.icon('MessageBoxInformation')) + + style = """ + QToolButton { + border: 1px solid grey; + padding:0px; + border-radius: 2px; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #f6f7fa, stop: 1 #dadbde); + } + """ + self._button_help.setStyleSheet(style) + + if inline: + self._button_help.setToolTip(self._help_inline) + self._text = NumpyArrayInline(self) + self._widget = self._text + else: + self._button_help.setToolTip(self._help_table) + self._table = NumpyArrayTable(self) + self._widget = self._table + + style = """ + QDialog { + margin:0px; + border: 1px solid grey; + padding:0px; + border-radius: 2px; + }""" + self.setStyleSheet(style) + + style = """ + QToolButton { + margin:1px; + border: 0px solid grey; + padding:0px; + border-radius: 0px; + }""" + self._button_warning.setStyleSheet(style) + + # widget setup + self.setWindowFlags(Qt.Window | Qt.Dialog | Qt.FramelessWindowHint) + self.setModal(True) + self.setWindowOpacity(0.90) + self._widget.setMinimumWidth(200) + + # layout + self._layout = QHBoxLayout() + self._layout.addWidget(self._widget) + self._layout.addWidget(self._button_warning, 1, Qt.AlignTop) + self._layout.addWidget(self._button_help, 1, Qt.AlignTop) + self.setLayout(self._layout) + + self._widget.setFocus() + + def keyPressEvent(self, event): + """ + Qt override. + """ + QToolTip.hideText() + ctrl = event.modifiers() & Qt.ControlModifier + + if event.key() in [Qt.Key_Enter, Qt.Key_Return]: + if ctrl: + self.process_text(array=False) + else: + self.process_text(array=True) + self.accept() + else: + QDialog.keyPressEvent(self, event) + + def event(self, event): + """ + Qt Override. + + Usefull when in line edit mode. + """ + if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab: + return False + return QWidget.event(self, event) + + def process_text(self, array=True): + """ + Construct the text based on the entered content in the widget. + """ + if array: + prefix = 'np.array([[' + else: + prefix = 'np.matrix([[' + + suffix = ']])' + values = self._widget.text().strip() + + if values != '': + # cleans repeated spaces + exp = r'(\s*)' + ROW_SEPARATOR + r'(\s*)' + values = re.sub(exp, ROW_SEPARATOR, values) + values = re.sub("\s+", " ", values) + values = re.sub("]$", "", values) + values = re.sub("^\[", "", values) + values = re.sub(ROW_SEPARATOR + r'*$', '', values) + + # replaces spaces by commas + values = values.replace(' ', ELEMENT_SEPARATOR) + + # iterate to find number of rows and columns + new_values = [] + rows = values.split(ROW_SEPARATOR) + nrows = len(rows) + ncols = [] + for row in rows: + new_row = [] + elements = row.split(ELEMENT_SEPARATOR) + ncols.append(len(elements)) + for e in elements: + num = e + + # replaces not defined values + if num in NAN_VALUES: + num = 'np.nan' + + # Convert numbers to floating point + if self._force_float: + try: + num = str(float(e)) + except: + pass + new_row.append(num) + new_values.append(ELEMENT_SEPARATOR.join(new_row)) + new_values = ROW_SEPARATOR.join(new_values) + values = new_values + + # Check validity + if len(set(ncols)) == 1: + self._valid = True + else: + self._valid = False + + # Single rows are parsed as 1D arrays/matrices + if nrows == 1: + prefix = prefix[:-1] + suffix = suffix.replace("]])", "])") + + # Fix offset + offset = self._offset + braces = BRACES.replace(' ', '\n' + ' '*(offset + len(prefix) - 1)) + + values = values.replace(ROW_SEPARATOR, braces) + text = "{0}{1}{2}".format(prefix, values, suffix) + + self._text = text + else: + self._text = '' + self.update_warning() + + def update_warning(self): + """ + Updates the icon and tip based on the validity of the array content. + """ + widget = self._button_warning + if not self.is_valid(): + tip = _('Array dimensions not valid') + widget.setIcon(ima.icon('MessageBoxWarning')) + widget.setToolTip(tip) + QToolTip.showText(self._widget.mapToGlobal(QPoint(0, 5)), tip) + else: + self._button_warning.setToolTip('') + + def is_valid(self): + """ + Return if the current array state is valid. + """ + return self._valid + + def text(self): + """ + Return the parsed array/matrix text. + """ + return self._text + + @property + def array_widget(self): + """ + Return the array builder widget. + """ + return self._widget + + +def test(): # pragma: no cover + from spyder.utils.qthelpers import qapplication + app = qapplication() + dlg_table = NumpyArrayDialog(None, inline=False) + dlg_inline = NumpyArrayDialog(None, inline=True) + dlg_table.show() + dlg_inline.show() + app.exec_() + + +if __name__ == "__main__": # pragma: no cover + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/browser.py spyder-3.0.2+dfsg1/spyder/widgets/browser.py --- spyder-2.3.8+dfsg1/spyder/widgets/browser.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/browser.py 2016-11-16 15:40:01.000000000 +0000 @@ -0,0 +1,333 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Simple web browser widget""" + +# Standard library imports +import sys + +# Third party imports +from qtpy.QtCore import QUrl, Signal, Slot +from qtpy.QtWidgets import (QFrame, QHBoxLayout, QLabel, QProgressBar, QMenu, + QVBoxLayout, QWidget) +from qtpy.QtWebEngineWidgets import (QWebEnginePage, QWebEngineSettings, + QWebEngineView, WEBENGINE) + +# Local imports +from spyder.config.base import _, DEV +from spyder.py3compat import is_text_string, to_text_string +from spyder.utils.qthelpers import (action2button, add_actions, + create_action, create_toolbutton) +from spyder.utils import icon_manager as ima +from spyder.widgets.comboboxes import UrlComboBox +from spyder.widgets.findreplace import FindReplace + + +class WebPage(QWebEnginePage): + """ + Web page subclass to manage hyperlinks for WebEngine + + Note: This can't be used for WebKit because the + acceptNavigationRequest method has a different + functionality for it. + """ + linkClicked = Signal(QUrl) + + def acceptNavigationRequest(self, url, navigation_type, isMainFrame): + """ + Overloaded method to handle links ourselves + """ + if navigation_type == QWebEnginePage.NavigationTypeLinkClicked: + self.linkClicked.emit(url) + return False + return True + + +class WebView(QWebEngineView): + """Web view""" + def __init__(self, parent): + QWebEngineView.__init__(self, parent) + self.zoom_factor = 1. + self.zoom_out_action = create_action(self, _("Zoom out"), + icon=ima.icon('zoom_out'), + triggered=self.zoom_out) + self.zoom_in_action = create_action(self, _("Zoom in"), + icon=ima.icon('zoom_in'), + triggered=self.zoom_in) + if WEBENGINE: + web_page = WebPage(self) + self.setPage(web_page) + + def find_text(self, text, changed=True, + forward=True, case=False, words=False, + regexp=False): + """Find text""" + if not WEBENGINE: + findflag = QWebEnginePage.FindWrapsAroundDocument + else: + findflag = 0 + + if not forward: + findflag = findflag | QWebEnginePage.FindBackward + if case: + findflag = findflag | QWebEnginePage.FindCaseSensitively + + return self.findText(text, QWebEnginePage.FindFlags(findflag)) + + def get_selected_text(self): + """Return text selected by current text cursor""" + return self.selectedText() + + def set_font(self, font, fixed_font=None): + settings = self.page().settings() + for fontfamily in (settings.StandardFont, settings.SerifFont, + settings.SansSerifFont, settings.CursiveFont, + settings.FantasyFont): + settings.setFontFamily(fontfamily, font.family()) + if fixed_font is not None: + settings.setFontFamily(settings.FixedFont, fixed_font.family()) + size = font.pointSize() + settings.setFontSize(settings.DefaultFontSize, size) + settings.setFontSize(settings.DefaultFixedFontSize, size) + + def apply_zoom_factor(self): + """Apply zoom factor""" + if hasattr(self, 'setZoomFactor'): + # Assuming Qt >=v4.5 + self.setZoomFactor(self.zoom_factor) + else: + # Qt v4.4 + self.setTextSizeMultiplier(self.zoom_factor) + + def set_zoom_factor(self, zoom_factor): + """Set zoom factor""" + self.zoom_factor = zoom_factor + self.apply_zoom_factor() + + def get_zoom_factor(self): + """Return zoom factor""" + return self.zoom_factor + + @Slot() + def zoom_out(self): + """Zoom out""" + self.zoom_factor = max(.1, self.zoom_factor-.1) + self.apply_zoom_factor() + + @Slot() + def zoom_in(self): + """Zoom in""" + self.zoom_factor += .1 + self.apply_zoom_factor() + + #------ QWebEngineView API ------------------------------------------------------- + def createWindow(self, webwindowtype): + import webbrowser + webbrowser.open(to_text_string(self.url().toString())) + + def contextMenuEvent(self, event): + menu = QMenu(self) + actions = [self.pageAction(QWebEnginePage.Back), + self.pageAction(QWebEnginePage.Forward), None, + self.pageAction(QWebEnginePage.SelectAll), + self.pageAction(QWebEnginePage.Copy), None, + self.zoom_in_action, self.zoom_out_action] + if DEV and not WEBENGINE: + settings = self.page().settings() + settings.setAttribute(QWebEngineSettings.DeveloperExtrasEnabled, True) + actions += [None, self.pageAction(QWebEnginePage.InspectElement)] + add_actions(menu, actions) + menu.popup(event.globalPos()) + event.accept() + + def setHtml(self, html, baseUrl=QUrl()): + """ + Reimplement Qt method to prevent WebEngine to steal focus + when setting html on the page + + Solution taken from + https://bugreports.qt.io/browse/QTBUG-52999 + """ + if WEBENGINE: + self.setEnabled(False) + super(WebView, self).setHtml(html, baseUrl) + self.setEnabled(True) + else: + super(WebView, self).setHtml(html, baseUrl) + + +class WebBrowser(QWidget): + """ + Web browser widget + """ + def __init__(self, parent=None): + QWidget.__init__(self, parent) + + self.home_url = None + + self.webview = WebView(self) + self.webview.loadFinished.connect(self.load_finished) + self.webview.titleChanged.connect(self.setWindowTitle) + self.webview.urlChanged.connect(self.url_changed) + + home_button = create_toolbutton(self, icon=ima.icon('home'), + tip=_("Home"), + triggered=self.go_home) + + zoom_out_button = action2button(self.webview.zoom_out_action) + zoom_in_button = action2button(self.webview.zoom_in_action) + + pageact2btn = lambda prop: action2button(self.webview.pageAction(prop), + parent=self.webview) + refresh_button = pageact2btn(QWebEnginePage.Reload) + stop_button = pageact2btn(QWebEnginePage.Stop) + previous_button = pageact2btn(QWebEnginePage.Back) + next_button = pageact2btn(QWebEnginePage.Forward) + + stop_button.setEnabled(False) + self.webview.loadStarted.connect(lambda: stop_button.setEnabled(True)) + self.webview.loadFinished.connect(lambda: stop_button.setEnabled(False)) + + progressbar = QProgressBar(self) + progressbar.setTextVisible(False) + progressbar.hide() + self.webview.loadStarted.connect(progressbar.show) + self.webview.loadProgress.connect(progressbar.setValue) + self.webview.loadFinished.connect(lambda _state: progressbar.hide()) + + label = QLabel(self.get_label()) + + self.url_combo = UrlComboBox(self) + self.url_combo.valid.connect(self.url_combo_activated) + if not WEBENGINE: + self.webview.iconChanged.connect(self.icon_changed) + + self.find_widget = FindReplace(self) + self.find_widget.set_editor(self.webview) + self.find_widget.hide() + + find_button = create_toolbutton(self, icon=ima.icon('find'), + tip=_("Find text"), + toggled=self.toggle_find_widget) + self.find_widget.visibility_changed.connect(find_button.setChecked) + + hlayout = QHBoxLayout() + for widget in (previous_button, next_button, home_button, find_button, + label, self.url_combo, zoom_out_button, zoom_in_button, + refresh_button, progressbar, stop_button): + hlayout.addWidget(widget) + + layout = QVBoxLayout() + layout.addLayout(hlayout) + layout.addWidget(self.webview) + layout.addWidget(self.find_widget) + self.setLayout(layout) + + def get_label(self): + """Return address label text""" + return _("Address:") + + def set_home_url(self, text): + """Set home URL""" + self.home_url = QUrl(text) + + def set_url(self, url): + """Set current URL""" + self.url_changed(url) + self.go_to(url) + + def go_to(self, url_or_text): + """Go to page *address*""" + if is_text_string(url_or_text): + url = QUrl(url_or_text) + else: + url = url_or_text + self.webview.load(url) + + @Slot() + def go_home(self): + """Go to home page""" + if self.home_url is not None: + self.set_url(self.home_url) + + def text_to_url(self, text): + """Convert text address into QUrl object""" + return QUrl(text) + + def url_combo_activated(self, valid): + """Load URL from combo box first item""" + text = to_text_string(self.url_combo.currentText()) + self.go_to(self.text_to_url(text)) + + def load_finished(self, ok): + if not ok: + self.webview.setHtml(_("Unable to load page")) + + def url_to_text(self, url): + """Convert QUrl object to displayed text in combo box""" + return url.toString() + + def url_changed(self, url): + """Displayed URL has changed -> updating URL combo box""" + self.url_combo.add_text(self.url_to_text(url)) + + def icon_changed(self): + self.url_combo.setItemIcon(self.url_combo.currentIndex(), + self.webview.icon()) + self.setWindowIcon(self.webview.icon()) + + @Slot(bool) + def toggle_find_widget(self, state): + if state: + self.find_widget.show() + else: + self.find_widget.hide() + + +class FrameWebView(QFrame): + """ + Framed QWebEngineView for UI consistency in Spyder. + """ + linkClicked = Signal(QUrl) + + def __init__(self, parent): + QFrame.__init__(self, parent) + + self._webview = WebView(self) + + layout = QHBoxLayout() + layout.addWidget(self._webview) + layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(layout) + + self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) + + if WEBENGINE: + self._webview.page().linkClicked.connect(self.linkClicked) + else: + self._webview.linkClicked.connect(self.linkClicked) + + def __getattr__(self, name): + return getattr(self._webview, name) + + @property + def web_widget(self): + return self._webview + + +def test(): + """Run web browser""" + from spyder.utils.qthelpers import qapplication + app = qapplication(test_time=8) + widget = WebBrowser() + widget.show() + widget.set_home_url('http://www.google.com/') + widget.go_home() + sys.exit(app.exec_()) + + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/calltip.py spyder-3.0.2+dfsg1/spyder/widgets/calltip.py --- spyder-2.3.8+dfsg1/spyder/widgets/calltip.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/calltip.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,295 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with IPython. +#----------------------------------------------------------------------------- + +""" +Calltip widget used only to show signatures +""" + +# Standard library imports +from unicodedata import category + +# Third party imports +from qtpy.QtCore import QBasicTimer, QCoreApplication, QEvent, Qt +from qtpy.QtGui import QCursor, QPalette +from qtpy.QtWidgets import (QFrame, QLabel, QTextEdit, QPlainTextEdit, QStyle, + QStyleOptionFrame, QStylePainter, QToolTip) + +# Local imports +from spyder.py3compat import to_text_string + + +class CallTipWidget(QLabel): + """ Shows call tips by parsing the current text of Q[Plain]TextEdit. + """ + + #-------------------------------------------------------------------------- + # 'QObject' interface + #-------------------------------------------------------------------------- + + def __init__(self, text_edit, hide_timer_on=False): + """ Create a call tip manager that is attached to the specified Qt + text edit widget. + """ + assert isinstance(text_edit, (QTextEdit, QPlainTextEdit)) + super(CallTipWidget, self).__init__(None, Qt.ToolTip) + self.app = QCoreApplication.instance() + + self.hide_timer_on = hide_timer_on + self._hide_timer = QBasicTimer() + self._text_edit = text_edit + + self.setFont(text_edit.document().defaultFont()) + self.setForegroundRole(QPalette.ToolTipText) + self.setBackgroundRole(QPalette.ToolTipBase) + self.setPalette(QToolTip.palette()) + + self.setAlignment(Qt.AlignLeft) + self.setIndent(1) + self.setFrameStyle(QFrame.NoFrame) + self.setMargin(1 + self.style().pixelMetric( + QStyle.PM_ToolTipLabelFrameWidth, None, self)) + + def eventFilter(self, obj, event): + """ Reimplemented to hide on certain key presses and on text edit focus + changes. + """ + if obj == self._text_edit: + etype = event.type() + + if etype == QEvent.KeyPress: + key = event.key() + if key in (Qt.Key_Enter, Qt.Key_Return, + Qt.Key_Down): + self.hide() + elif key == Qt.Key_Escape: + self.hide() + return True + + elif etype == QEvent.FocusOut: + self.hide() + + elif etype == QEvent.Enter: + if (self._hide_timer.isActive() and + self.app.topLevelAt(QCursor.pos()) == self): + self._hide_timer.stop() + + elif etype == QEvent.Leave: + self._leave_event_hide() + + return super(CallTipWidget, self).eventFilter(obj, event) + + def timerEvent(self, event): + """ Reimplemented to hide the widget when the hide timer fires. + """ + if event.timerId() == self._hide_timer.timerId(): + self._hide_timer.stop() + self.hide() + + #-------------------------------------------------------------------------- + # 'QWidget' interface + #-------------------------------------------------------------------------- + + def enterEvent(self, event): + """ Reimplemented to cancel the hide timer. + """ + super(CallTipWidget, self).enterEvent(event) + if (self._hide_timer.isActive() and + self.app.topLevelAt(QCursor.pos()) == self): + self._hide_timer.stop() + + def hideEvent(self, event): + """ Reimplemented to disconnect signal handlers and event filter. + """ + super(CallTipWidget, self).hideEvent(event) + self._text_edit.cursorPositionChanged.disconnect( + self._cursor_position_changed) + self._text_edit.removeEventFilter(self) + + def leaveEvent(self, event): + """ Reimplemented to start the hide timer. + """ + super(CallTipWidget, self).leaveEvent(event) + self._leave_event_hide() + + def mousePressEvent(self, event): + super(CallTipWidget, self).mousePressEvent(event) + self.hide() + + def paintEvent(self, event): + """ Reimplemented to paint the background panel. + """ + painter = QStylePainter(self) + option = QStyleOptionFrame() + option.initFrom(self) + painter.drawPrimitive(QStyle.PE_PanelTipLabel, option) + painter.end() + + super(CallTipWidget, self).paintEvent(event) + + def setFont(self, font): + """ Reimplemented to allow use of this method as a slot. + """ + super(CallTipWidget, self).setFont(font) + + def showEvent(self, event): + """ Reimplemented to connect signal handlers and event filter. + """ + super(CallTipWidget, self).showEvent(event) + self._text_edit.cursorPositionChanged.connect( + self._cursor_position_changed) + self._text_edit.installEventFilter(self) + + #-------------------------------------------------------------------------- + # 'CallTipWidget' interface + #-------------------------------------------------------------------------- + + def show_tip(self, point, tip, wrapped_tiplines): + """ Attempts to show the specified tip at the current cursor location. + """ + # Attempt to find the cursor position at which to show the call tip. + text_edit = self._text_edit + cursor = text_edit.textCursor() + search_pos = cursor.position() - 1 + self._start_position, _ = self._find_parenthesis(search_pos, + forward=False) + if self._start_position == -1: + return False + + if self.hide_timer_on: + self._hide_timer.stop() + # Logic to decide how much time to show the calltip depending + # on the amount of text present + if len(wrapped_tiplines) == 1: + args = wrapped_tiplines[0].split('(')[1] + nargs = len(args.split(',')) + if nargs == 1: + hide_time = 1400 + elif nargs == 2: + hide_time = 1600 + else: + hide_time = 1800 + elif len(wrapped_tiplines) == 2: + args1 = wrapped_tiplines[1].strip() + nargs1 = len(args1.split(',')) + if nargs1 == 1: + hide_time = 2500 + else: + hide_time = 2800 + else: + hide_time = 3500 + self._hide_timer.start(hide_time, self) + + # Set the text and resize the widget accordingly. + self.setText(tip) + self.resize(self.sizeHint()) + + # Locate and show the widget. Place the tip below the current line + # unless it would be off the screen. In that case, decide the best + # location based trying to minimize the area that goes off-screen. + padding = 3 # Distance in pixels between cursor bounds and tip box. + cursor_rect = text_edit.cursorRect(cursor) + screen_rect = self.app.desktop().screenGeometry(text_edit) + point.setY(point.y() + padding) + tip_height = self.size().height() + tip_width = self.size().width() + + vertical = 'bottom' + horizontal = 'Right' + if point.y() + tip_height > screen_rect.height() + screen_rect.y(): + point_ = text_edit.mapToGlobal(cursor_rect.topRight()) + # If tip is still off screen, check if point is in top or bottom + # half of screen. + if point_.y() - tip_height < padding: + # If point is in upper half of screen, show tip below it. + # otherwise above it. + if 2*point.y() < screen_rect.height(): + vertical = 'bottom' + else: + vertical = 'top' + else: + vertical = 'top' + if point.x() + tip_width > screen_rect.width() + screen_rect.x(): + point_ = text_edit.mapToGlobal(cursor_rect.topRight()) + # If tip is still off-screen, check if point is in the right or + # left half of the screen. + if point_.x() - tip_width < padding: + if 2*point.x() < screen_rect.width(): + horizontal = 'Right' + else: + horizontal = 'Left' + else: + horizontal = 'Left' + pos = getattr(cursor_rect, '%s%s' %(vertical, horizontal)) + adjusted_point = text_edit.mapToGlobal(pos()) + if vertical == 'top': + point.setY(adjusted_point.y() - tip_height - padding) + if horizontal == 'Left': + point.setX(adjusted_point.x() - tip_width - padding) + + self.move(point) + self.show() + return True + + #-------------------------------------------------------------------------- + # Protected interface + #-------------------------------------------------------------------------- + + def _find_parenthesis(self, position, forward=True): + """ If 'forward' is True (resp. False), proceed forwards + (resp. backwards) through the line that contains 'position' until an + unmatched closing (resp. opening) parenthesis is found. Returns a + tuple containing the position of this parenthesis (or -1 if it is + not found) and the number commas (at depth 0) found along the way. + """ + commas = depth = 0 + document = self._text_edit.document() + char = to_text_string(document.characterAt(position)) + # Search until a match is found or a non-printable character is + # encountered. + while category(char) != 'Cc' and position > 0: + if char == ',' and depth == 0: + commas += 1 + elif char == ')': + if forward and depth == 0: + break + depth += 1 + elif char == '(': + if not forward and depth == 0: + break + depth -= 1 + position += 1 if forward else -1 + char = to_text_string(document.characterAt(position)) + else: + position = -1 + return position, commas + + def _leave_event_hide(self): + """ Hides the tooltip after some time has passed (assuming the cursor is + not over the tooltip). + """ + if (not self._hide_timer.isActive() and + # If Enter events always came after Leave events, we wouldn't need + # this check. But on Mac OS, it sometimes happens the other way + # around when the tooltip is created. + self.app.topLevelAt(QCursor.pos()) != self): + self._hide_timer.start(800, self) + + #------ Signal handlers ---------------------------------------------------- + + def _cursor_position_changed(self): + """ Updates the tip based on user cursor movement. + """ + cursor = self._text_edit.textCursor() + if cursor.position() <= self._start_position: + self.hide() + else: + if not self.hide_timer_on: + position, commas = self._find_parenthesis(self._start_position + 1) + if position != -1: + self.hide() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/colors.py spyder-3.0.2+dfsg1/spyder/widgets/colors.py --- spyder-2.3.8+dfsg1/spyder/widgets/colors.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/colors.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +# Third party imports +from qtpy.QtCore import Property, QSize, Signal, Slot +from qtpy.QtGui import QColor, QIcon, QPixmap +from qtpy.QtWidgets import QColorDialog, QHBoxLayout, QLineEdit, QPushButton + +# Local imports +from spyder.py3compat import is_text_string + + +class ColorButton(QPushButton): + """ + Color choosing push button + """ + colorChanged = Signal(QColor) + + def __init__(self, parent=None): + QPushButton.__init__(self, parent) + self.setFixedSize(20, 20) + self.setIconSize(QSize(12, 12)) + self.clicked.connect(self.choose_color) + self._color = QColor() + + def choose_color(self): + color = QColorDialog.getColor(self._color, self.parentWidget(), + 'Select Color', + QColorDialog.ShowAlphaChannel) + if color.isValid(): + self.set_color(color) + + def get_color(self): + return self._color + + @Slot(QColor) + def set_color(self, color): + if color != self._color: + self._color = color + self.colorChanged.emit(self._color) + pixmap = QPixmap(self.iconSize()) + pixmap.fill(color) + self.setIcon(QIcon(pixmap)) + + color = Property("QColor", get_color, set_color) + + +def text_to_qcolor(text): + """ + Create a QColor from specified string + Avoid warning from Qt when an invalid QColor is instantiated + """ + color = QColor() + text = str(text) + if not is_text_string(text): + return color + if text.startswith('#') and len(text)==7: + correct = '#0123456789abcdef' + for char in text: + if char.lower() not in correct: + return color + elif text not in list(QColor.colorNames()): + return color + color.setNamedColor(text) + return color + + +class ColorLayout(QHBoxLayout): + """Color-specialized QLineEdit layout""" + def __init__(self, color, parent=None): + QHBoxLayout.__init__(self) + assert isinstance(color, QColor) + self.lineedit = QLineEdit(color.name(), parent) + self.lineedit.textChanged.connect(self.update_color) + self.addWidget(self.lineedit) + self.colorbtn = ColorButton(parent) + self.colorbtn.color = color + self.colorbtn.colorChanged.connect(self.update_text) + self.addWidget(self.colorbtn) + + def update_color(self, text): + color = text_to_qcolor(text) + if color.isValid(): + self.colorbtn.color = color + + def update_text(self, color): + self.lineedit.setText(color.name()) + + def text(self): + return self.lineedit.text() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/comboboxes.py spyder-3.0.2+dfsg1/spyder/widgets/comboboxes.py --- spyder-2.3.8+dfsg1/spyder/widgets/comboboxes.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/comboboxes.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,315 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Customized combobox widgets.""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + + +# Standard library imports +import glob +import os +import os.path as osp + +# Third party imports +from qtpy.QtCore import QEvent, Qt, QTimer, QUrl, Signal +from qtpy.QtGui import QFont +from qtpy.QtWidgets import QComboBox, QCompleter, QSizePolicy, QToolTip + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import to_text_string +from spyder.widgets.helperwidgets import IconLineEdit + + +class BaseComboBox(QComboBox): + """Editable combo box base class""" + valid = Signal(bool, bool) + sig_tab_pressed = Signal(bool) + sig_double_tab_pressed = Signal(bool) + + def __init__(self, parent): + QComboBox.__init__(self, parent) + self.setEditable(True) + self.setCompleter(QCompleter(self)) + self.numpress = 0 + self.selected_text = self.currentText() + + # --- Qt overrides + def event(self, event): + """Qt Override. + + Filter tab keys and process double tab keys. + """ + if (event.type() == QEvent.KeyPress) and (event.key() == Qt.Key_Tab): + self.sig_tab_pressed.emit(True) + self.numpress += 1 + if self.numpress == 1: + self.presstimer = QTimer.singleShot(400, self.handle_keypress) + return True + return QComboBox.event(self, event) + + def keyPressEvent(self, event): + """Qt Override. + + Handle key press events. + """ + if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: + if self.add_current_text_if_valid(): + self.selected() + self.hide_completer() + elif event.key() == Qt.Key_Escape: + self.set_current_text(self.selected_text) + self.hide_completer() + else: + QComboBox.keyPressEvent(self, event) + + # --- own methods + def handle_keypress(self): + """When hitting tab, it handles if single or double tab""" + if self.numpress == 2: + self.sig_double_tab_pressed.emit(True) + self.numpress = 0 + + def is_valid(self, qstr): + """ + Return True if string is valid + Return None if validation can't be done + """ + pass + + def selected(self): + """Action to be executed when a valid item has been selected""" + self.valid.emit(True, True) + + def add_text(self, text): + """Add text to combo box: add a new item if text is not found in + combo box items.""" + index = self.findText(text) + while index != -1: + self.removeItem(index) + index = self.findText(text) + self.insertItem(0, text) + index = self.findText('') + if index != -1: + self.removeItem(index) + self.insertItem(0, '') + if text != '': + self.setCurrentIndex(1) + else: + self.setCurrentIndex(0) + else: + self.setCurrentIndex(0) + + def set_current_text(self, text): + """Sets the text of the QLineEdit of the QComboBox.""" + self.lineEdit().setText(to_text_string(text)) + + def add_current_text(self): + """Add current text to combo box history (convenient method)""" + text = self.currentText() + if osp.isdir(text): + if text[-1] == os.sep: + text = text[:-1] + self.add_text(text) + + def add_current_text_if_valid(self): + """Add current text to combo box history if valid""" + valid = self.is_valid(self.currentText()) + if valid or valid is None: + self.add_current_text() + return True + else: + self.set_current_text(self.selected_text) + + def hide_completer(self): + """Hides the completion widget.""" + self.setCompleter(QCompleter([], self)) + + +class PatternComboBox(BaseComboBox): + """Search pattern combo box""" + def __init__(self, parent, items=None, tip=None, + adjust_to_minimum=True): + BaseComboBox.__init__(self, parent) + if adjust_to_minimum: + self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + if items is not None: + self.addItems(items) + if tip is not None: + self.setToolTip(tip) + + +class EditableComboBox(BaseComboBox): + """ + Editable combo box + Validate + """ + + def __init__(self, parent): + BaseComboBox.__init__(self, parent) + self.font = QFont() + self.selected_text = self.currentText() + + # Widget setup + self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) + + # Signals + self.editTextChanged.connect(self.validate) + self.tips = {True: _("Press enter to validate this entry"), + False: _('This entry is incorrect')} + + def show_tip(self, tip=""): + """Show tip""" + QToolTip.showText(self.mapToGlobal(self.pos()), tip, self) + + def selected(self): + """Action to be executed when a valid item has been selected""" + BaseComboBox.selected(self) + self.selected_text = self.currentText() + + def validate(self, qstr, editing=True): + """Validate entered path""" + if self.selected_text == qstr: + self.valid.emit(True, True) + return + + valid = self.is_valid(qstr) + if editing: + if valid: + self.valid.emit(True, False) + else: + self.valid.emit(False, False) + + +class PathComboBox(EditableComboBox): + """ + QComboBox handling path locations + """ + open_dir = Signal(str) + + def __init__(self, parent, adjust_to_contents=False): + EditableComboBox.__init__(self, parent) + + # Replace the default lineedit by a custom one with icon display + lineedit = IconLineEdit(self) + + # Widget setup + if adjust_to_contents: + self.setSizeAdjustPolicy(QComboBox.AdjustToContents) + else: + self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + self.tips = {True: _("Press enter to validate this path"), + False: ''} + self.setLineEdit(lineedit) + + # Signals + self.sig_tab_pressed.connect(self.tab_complete) + self.sig_double_tab_pressed.connect(self.double_tab_complete) + self.valid.connect(lineedit.update_status) + + # --- Qt overrides + def focusInEvent(self, event): + """Handle focus in event restoring to display the status icon.""" + show_status = getattr(self.lineEdit(), 'show_status_icon', None) + if show_status: + show_status() + QComboBox.focusInEvent(self, event) + + def focusOutEvent(self, event): + """Handle focus out event restoring the last valid selected path.""" + # Calling asynchronously the 'add_current_text' to avoid crash + # https://groups.google.com/group/spyderlib/browse_thread/thread/2257abf530e210bd + if not self.is_valid(): + lineedit = self.lineEdit() + QTimer.singleShot(50, lambda: lineedit.setText(self.selected_text)) + + hide_status = getattr(self.lineEdit(), 'hide_status_icon', None) + if hide_status: + hide_status() + QComboBox.focusOutEvent(self, event) + + # --- Own methods + def _complete_options(self): + """Find available completion options.""" + text = to_text_string(self.currentText()) + opts = glob.glob(text + "*") + opts = sorted([opt for opt in opts if osp.isdir(opt)]) + self.setCompleter(QCompleter(opts, self)) + return opts + + def double_tab_complete(self): + """If several options available a double tab displays options.""" + opts = self._complete_options() + if len(opts) > 1: + self.completer().complete() + + def tab_complete(self): + """ + If there is a single option available one tab completes the option. + """ + opts = self._complete_options() + if len(opts) == 1: + self.set_current_text(opts[0] + os.sep) + self.hide_completer() + + def is_valid(self, qstr=None): + """Return True if string is valid""" + if qstr is None: + qstr = self.currentText() + return osp.isdir(to_text_string(qstr)) + + def selected(self): + """Action to be executed when a valid item has been selected""" + self.selected_text = self.currentText() + self.valid.emit(True, True) + self.open_dir.emit(self.selected_text) + + +class UrlComboBox(PathComboBox): + """ + QComboBox handling urls + """ + def __init__(self, parent, adjust_to_contents=False): + PathComboBox.__init__(self, parent, adjust_to_contents) + self.editTextChanged.disconnect(self.validate) + + def is_valid(self, qstr=None): + """Return True if string is valid""" + if qstr is None: + qstr = self.currentText() + return QUrl(qstr).isValid() + + +def is_module_or_package(path): + """Return True if path is a Python module/package""" + is_module = osp.isfile(path) and osp.splitext(path)[1] in ('.py', '.pyw') + is_package = osp.isdir(path) and osp.isfile(osp.join(path, '__init__.py')) + return is_module or is_package + + +class PythonModulesComboBox(PathComboBox): + """ + QComboBox handling Python modules or packages path + (i.e. .py, .pyw files *and* directories containing __init__.py) + """ + def __init__(self, parent, adjust_to_contents=False): + PathComboBox.__init__(self, parent, adjust_to_contents) + + def is_valid(self, qstr=None): + """Return True if string is valid""" + if qstr is None: + qstr = self.currentText() + return is_module_or_package(to_text_string(qstr)) + + def selected(self): + """Action to be executed when a valid item has been selected""" + EditableComboBox.selected(self) + self.open_dir.emit(self.currentText()) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/dependencies.py spyder-3.0.2+dfsg1/spyder/widgets/dependencies.py --- spyder-2.3.8+dfsg1/spyder/widgets/dependencies.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/dependencies.py 2016-11-16 16:12:04.000000000 +0000 @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Module checking Spyder runtime dependencies""" + +# Standard library imports +import sys + +# Third party imports +from qtpy.compat import to_qvariant +from qtpy.QtCore import Qt, QModelIndex, QAbstractTableModel +from qtpy.QtGui import QColor +from qtpy.QtWidgets import (QApplication, QDialog, QDialogButtonBox, + QHBoxLayout, QItemDelegate, QLabel, QPushButton, + QTableView, QVBoxLayout) + +# Local imports +from spyder import __version__ +from spyder.config.base import _ +from spyder.utils import icon_manager as ima + + +class DependenciesTableModel(QAbstractTableModel): + def __init__(self, parent, dependencies): + QAbstractTableModel.__init__(self, parent) + self.dependencies = None + self.set_data(dependencies) + + def set_data(self, dependencies): + """Set model data""" + self.dependencies = dependencies + self.reset() + + def rowCount(self, qindex=QModelIndex()): + """Array row number""" + return len(self.dependencies) + + def columnCount(self, qindex=QModelIndex()): + """Array column count""" + return 4 + + def sort(self, column, order=Qt.DescendingOrder): + """Overriding sort method""" + if column == 0: + self.dependencies.sort(key=lambda dep: getattr(dep, 'modname')) + elif column == 1: + pass + elif column == 2: + pass + elif column == 3: + pass + self.reset() + + def headerData(self, section, orientation, role=Qt.DisplayRole): + """Overriding method headerData""" + if role != Qt.DisplayRole: + return to_qvariant() + i_column = int(section) + if orientation == Qt.Horizontal: + headers = (_("Module"), _(" Required "), + _(" Installed "), _("Provided features")) + return to_qvariant( headers[i_column] ) + else: + return to_qvariant() + + def get_value(self, index): + """Return current value""" + dep = self.dependencies[index.row()] + return (dep.modname, dep.required_version, + dep.get_installed_version(), dep.features)[index.column()] + + def data(self, index, role=Qt.DisplayRole): + """Return data at table index""" + if not index.isValid(): + return to_qvariant() + dep = self.dependencies[index.row()] + if role == Qt.DisplayRole: + if index.column() == 0: + value = self.get_value(index) + return to_qvariant(value) + else: + value = self.get_value(index) + return to_qvariant(value) + elif role == Qt.TextAlignmentRole: + return to_qvariant(int(Qt.AlignLeft|Qt.AlignVCenter)) + elif role == Qt.BackgroundColorRole: + from spyder.dependencies import Dependency + status = dep.get_status() + if status == Dependency.NOK: + color = QColor(Qt.red) + color.setAlphaF(.25) + return to_qvariant(color) + + def reset(self): + self.beginResetModel() + self.endResetModel() + + +class DependenciesDelegate(QItemDelegate): + def __init__(self, parent=None): + QItemDelegate.__init__(self, parent) + + +class DependenciesTableView(QTableView): + def __init__(self, parent, data): + QTableView.__init__(self, parent) + self.model = DependenciesTableModel(self, data) + self.setModel(self.model) + self.delegate = DependenciesDelegate(self) + self.setItemDelegate(self.delegate) + self.setup_table() + + def setup_table(self): + """Setup table""" + self.horizontalHeader().setStretchLastSection(True) + self.adjust_columns() + self.columnAt(0) + # Sorting columns + self.setSortingEnabled(False) + self.sortByColumn(0, Qt.DescendingOrder) + + def adjust_columns(self): + """Resize three first columns to contents""" + for col in range(3): + self.resizeColumnToContents(col) + +class DependenciesDialog(QDialog): + def __init__(self, parent): + QDialog.__init__(self, parent) + self.setWindowTitle("Spyder %s: %s" % (__version__, + _("Dependencies"))) + self.setWindowIcon(ima.icon('tooloptions')) + self.setModal(True) + + self.view = DependenciesTableView(self, []) + + opt_mods = ['NumPy', 'Matplotlib', 'Pandas', 'SymPy'] + self.label = QLabel(_("Spyder depends on several Python modules to " + "provide the right functionality for all its " + "panes. The table below shows the required " + "and installed versions (if any) of all of " + "them.

    " + "Note: You can safely use Spyder " + "without the following modules installed: " + "%s and %s.

    " + "Please also note that new " + "dependencies or changed ones will be correctly " + "detected only after Spyder is restarted.") + % (', '.join(opt_mods[:-1]), opt_mods[-1])) + self.label.setWordWrap(True) + self.label.setAlignment(Qt.AlignJustify) + self.label.setContentsMargins(5, 8, 12, 10) + + btn = QPushButton(_("Copy to clipboard"), ) + btn.clicked.connect(self.copy_to_clipboard) + bbox = QDialogButtonBox(QDialogButtonBox.Ok) + bbox.accepted.connect(self.accept) + hlayout = QHBoxLayout() + hlayout.addWidget(btn) + hlayout.addStretch() + hlayout.addWidget(bbox) + + vlayout = QVBoxLayout() + vlayout.addWidget(self.label) + vlayout.addWidget(self.view) + vlayout.addLayout(hlayout) + + self.setLayout(vlayout) + self.resize(630, 420) + + def set_data(self, dependencies): + self.view.model.set_data(dependencies) + self.view.adjust_columns() + self.view.sortByColumn(0, Qt.DescendingOrder) + + def copy_to_clipboard(self): + from spyder.dependencies import status + QApplication.clipboard().setText(status()) + + +def test(): + """Run dependency widget test""" + from spyder import dependencies + + # Test sample + dependencies.add("IPython", "Enhanced Python interpreter", ">=0.13") + dependencies.add("matplotlib", "Interactive data plotting", ">=1.0") + dependencies.add("sympy", "Symbolic Mathematics", ">=10.0") + dependencies.add("foo", "Non-existent module", ">=1.0") + + from spyder.utils.qthelpers import qapplication + app = qapplication() + dlg = DependenciesDialog(None) + dlg.set_data(dependencies.DEPENDENCIES) + dlg.show() + sys.exit(dlg.exec_()) + + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/editor.py spyder-3.0.2+dfsg1/spyder/widgets/editor.py --- spyder-2.3.8+dfsg1/spyder/widgets/editor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/editor.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,2414 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Editor Widget""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Local imports +from __future__ import print_function +import os +import os.path as osp +import sys + +# Third party imports +from qtpy import is_pyqt46 +from qtpy.compat import getsavefilename +from qtpy.QtCore import (QByteArray, QFileInfo, QObject, QPoint, QSize, Qt, + QThread, QTimer, Signal, Slot) +from qtpy.QtGui import QFont, QKeySequence +from qtpy.QtWidgets import (QAction, QApplication, QHBoxLayout, QMainWindow, + QMessageBox, QMenu, QSplitter, QVBoxLayout, + QWidget) + +# Local imports +from spyder.config.base import _, DEBUG, STDERR, STDOUT +from spyder.config.gui import (config_shortcut, fixed_shortcut, + RUN_CELL_SHORTCUT, + RUN_CELL_AND_ADVANCE_SHORTCUT) +from spyder.config.utils import (get_edit_filetypes, get_edit_filters, + get_filter) +from spyder.py3compat import qbytearray_to_str, to_text_string, u +from spyder.utils import icon_manager as ima +from spyder.utils import (codeanalysis, encoding, sourcecode, + syntaxhighlighters) +from spyder.utils.qthelpers import (add_actions, create_action, + create_toolbutton, mimedata2url) +from spyder.widgets.editortools import OutlineExplorerWidget +from spyder.widgets.fileswitcher import FileSwitcher +from spyder.widgets.findreplace import FindReplace +from spyder.widgets.sourcecode import codeeditor +from spyder.widgets.sourcecode.base import TextEditBaseWidget # analysis:ignore +from spyder.widgets.sourcecode.codeeditor import Printer # analysis:ignore +from spyder.widgets.sourcecode.codeeditor import get_file_language +from spyder.widgets.status import (CursorPositionStatus, EncodingStatus, + EOLStatus, ReadWriteStatus) +from spyder.widgets.tabs import BaseTabs + +DEBUG_EDITOR = DEBUG >= 3 + + +class AnalysisThread(QThread): + """Analysis thread""" + def __init__(self, parent, checker, source_code): + super(AnalysisThread, self).__init__(parent) + self.checker = checker + self.results = None + self.source_code = source_code + + def run(self): + """Run analysis""" + try: + self.results = self.checker(self.source_code) + except Exception: + if DEBUG_EDITOR: + import traceback + traceback.print_exc(file=STDERR) + + +class ThreadManager(QObject): + """Analysis thread manager""" + def __init__(self, parent, max_simultaneous_threads=2): + super(ThreadManager, self).__init__(parent) + self.max_simultaneous_threads = max_simultaneous_threads + self.started_threads = {} + self.pending_threads = [] + self.end_callbacks = {} + + def close_threads(self, parent): + """Close threads associated to parent_id""" + if DEBUG_EDITOR: + print("Call to 'close_threads'", file=STDOUT) + if parent is None: + # Closing all threads + self.pending_threads = [] + threadlist = [] + for threads in list(self.started_threads.values()): + threadlist += threads + else: + parent_id = id(parent) + self.pending_threads = [(_th, _id) for (_th, _id) + in self.pending_threads + if _id != parent_id] + threadlist = self.started_threads.get(parent_id, []) + for thread in threadlist: + if DEBUG_EDITOR: + print("Waiting for thread %r to finish" % thread, file=STDOUT) + while thread.isRunning(): + # We can't terminate thread safely, so we simply wait... + QApplication.processEvents() + + def close_all_threads(self): + """Close all threads""" + if DEBUG_EDITOR: + print("Call to 'close_all_threads'", file=STDOUT) + self.close_threads(None) + + def add_thread(self, checker, end_callback, source_code, parent): + """Add thread to queue""" + parent_id = id(parent) + thread = AnalysisThread(self, checker, source_code) + self.end_callbacks[id(thread)] = end_callback + self.pending_threads.append((thread, parent_id)) + if DEBUG_EDITOR: + print("Added thread %r to queue" % thread, file=STDOUT) + QTimer.singleShot(50, self.update_queue) + + def update_queue(self): + """Update queue""" + started = 0 + for parent_id, threadlist in list(self.started_threads.items()): + still_running = [] + for thread in threadlist: + if thread.isFinished(): + end_callback = self.end_callbacks.pop(id(thread)) + if thread.results is not None: + # The thread was executed successfully + end_callback(thread.results) + thread.setParent(None) + thread = None + else: + still_running.append(thread) + started += 1 + threadlist = None + if still_running: + self.started_threads[parent_id] = still_running + else: + self.started_threads.pop(parent_id) + if DEBUG_EDITOR: + print("Updating queue:", file=STDOUT) + print(" started:", started, file=STDOUT) + print(" pending:", len(self.pending_threads), file=STDOUT) + if self.pending_threads and started < self.max_simultaneous_threads: + thread, parent_id = self.pending_threads.pop(0) + thread.finished.connect(self.update_queue) + threadlist = self.started_threads.get(parent_id, []) + self.started_threads[parent_id] = threadlist+[thread] + if DEBUG_EDITOR: + print("===>starting:", thread, file=STDOUT) + thread.start() + + +class FileInfo(QObject): + """File properties""" + analysis_results_changed = Signal() + todo_results_changed = Signal() + save_breakpoints = Signal(str, str) + text_changed_at = Signal(str, int) + edit_goto = Signal(str, int, str) + send_to_help = Signal(str, str, str, str, bool) + + def __init__(self, filename, encoding, editor, new, threadmanager, + introspection_plugin): + QObject.__init__(self) + self.threadmanager = threadmanager + self.filename = filename + self.newly_created = new + self.default = False # Default untitled file + self.encoding = encoding + self.editor = editor + self.path = [] + + self.classes = (filename, None, None) + self.analysis_results = [] + self.todo_results = [] + self.lastmodified = QFileInfo(filename).lastModified() + + self.editor.textChanged.connect(self.text_changed) + self.editor.breakpoints_changed.connect(self.breakpoints_changed) + + self.pyflakes_results = None + self.pep8_results = None + + def text_changed(self): + """Editor's text has changed""" + self.default = False + self.text_changed_at.emit(self.filename, + self.editor.get_position('cursor')) + + def get_source_code(self): + """Return associated editor source code""" + return to_text_string(self.editor.toPlainText()) + + def run_code_analysis(self, run_pyflakes, run_pep8): + """Run code analysis""" + run_pyflakes = run_pyflakes and codeanalysis.is_pyflakes_installed() + run_pep8 = run_pep8 and\ + codeanalysis.get_checker_executable('pep8') is not None + self.pyflakes_results = [] + self.pep8_results = [] + if self.editor.is_python(): + enc = self.encoding.replace('-guessed', '').replace('-bom', '') + source_code = self.get_source_code().encode(enc) + if run_pyflakes: + self.pyflakes_results = None + if run_pep8: + self.pep8_results = None + if run_pyflakes: + self.threadmanager.add_thread(codeanalysis.check_with_pyflakes, + self.pyflakes_analysis_finished, + source_code, self) + if run_pep8: + self.threadmanager.add_thread(codeanalysis.check_with_pep8, + self.pep8_analysis_finished, + source_code, self) + + def pyflakes_analysis_finished(self, results): + """Pyflakes code analysis thread has finished""" + self.pyflakes_results = results + if self.pep8_results is not None: + self.code_analysis_finished() + + def pep8_analysis_finished(self, results): + """Pep8 code analysis thread has finished""" + self.pep8_results = results + if self.pyflakes_results is not None: + self.code_analysis_finished() + + def code_analysis_finished(self): + """Code analysis thread has finished""" + self.set_analysis_results(self.pyflakes_results+self.pep8_results) + self.analysis_results_changed.emit() + + def set_analysis_results(self, results): + """Set analysis results and update warning markers in editor""" + self.analysis_results = results + self.editor.process_code_analysis(results) + + def cleanup_analysis_results(self): + """Clean-up analysis results""" + self.analysis_results = [] + self.editor.cleanup_code_analysis() + + def run_todo_finder(self): + """Run TODO finder""" + if self.editor.is_python(): + self.threadmanager.add_thread(codeanalysis.find_tasks, + self.todo_finished, + self.get_source_code(), self) + + def todo_finished(self, results): + """Code analysis thread has finished""" + self.set_todo_results(results) + self.todo_results_changed.emit() + + def set_todo_results(self, results): + """Set TODO results and update markers in editor""" + self.todo_results = results + self.editor.process_todo(results) + + def cleanup_todo_results(self): + """Clean-up TODO finder results""" + self.todo_results = [] + + def breakpoints_changed(self): + """Breakpoint list has changed""" + breakpoints = self.editor.get_breakpoints() + if self.editor.breakpoints != breakpoints: + self.editor.breakpoints = breakpoints + self.save_breakpoints.emit(self.filename, repr(breakpoints)) + + +class EditorStack(QWidget): + reset_statusbar = Signal() + readonly_changed = Signal(bool) + encoding_changed = Signal(str) + sig_editor_cursor_position_changed = Signal(int, int) + refresh_eol_chars = Signal(str) + starting_long_process = Signal(str) + ending_long_process = Signal(str) + redirect_stdio = Signal(bool) + exec_in_extconsole = Signal(str, bool) + update_plugin_title = Signal() + editor_focus_changed = Signal() + zoom_in = Signal() + zoom_out = Signal() + zoom_reset = Signal() + sig_close_file = Signal(str, int) + file_saved = Signal(str, int, str) + file_renamed_in_data = Signal(str, int, str) + create_new_window = Signal() + opened_files_list_changed = Signal() + analysis_results_changed = Signal() + todo_results_changed = Signal() + update_code_analysis_actions = Signal() + refresh_file_dependent_actions = Signal() + refresh_save_all_action = Signal() + save_breakpoints = Signal(str, str) + text_changed_at = Signal(str, int) + current_file_changed = Signal(str ,int) + plugin_load = Signal((str,), ()) + edit_goto = Signal(str, int, str) + split_vertically = Signal() + split_horizontally = Signal() + sig_new_file = Signal((str,), ()) + sig_save_as = Signal() + sig_prev_edit_pos = Signal() + sig_prev_cursor = Signal() + sig_next_cursor = Signal() + + def __init__(self, parent, actions): + QWidget.__init__(self, parent) + + self.setAttribute(Qt.WA_DeleteOnClose) + + self.threadmanager = ThreadManager(self) + + self.newwindow_action = None + self.horsplit_action = None + self.versplit_action = None + self.close_action = None + self.__get_split_actions() + + layout = QVBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(layout) + + self.menu = None + self.fileswitcher_dlg = None +# self.filelist_btn = None +# self.previous_btn = None +# self.next_btn = None + self.tabs = None + + self.stack_history = [] + + self.setup_editorstack(parent, layout) + + self.find_widget = None + + self.data = [] + fileswitcher_action = create_action(self, _("File switcher..."), + icon=ima.icon('filelist'), + triggered=self.open_fileswitcher_dlg) + copy_to_cb_action = create_action(self, _("Copy path to clipboard"), + icon=ima.icon('editcopy'), + triggered=lambda: + QApplication.clipboard().setText(self.get_current_filename())) + close_right = create_action(self, _("Close all to the right"), + triggered=self.close_all_right) + close_all_but_this = create_action(self, _("Close all but this"), + triggered=self.close_all_but_this) + + self.menu_actions = actions + [None, fileswitcher_action, + copy_to_cb_action, None, close_right, + close_all_but_this] + self.outlineexplorer = None + self.help = None + self.unregister_callback = None + self.is_closable = False + self.new_action = None + self.open_action = None + self.save_action = None + self.revert_action = None + self.tempfile_path = None + self.title = _("Editor") + self.pyflakes_enabled = True + self.pep8_enabled = False + self.todolist_enabled = True + self.realtime_analysis_enabled = False + self.is_analysis_done = False + self.linenumbers_enabled = True + self.blanks_enabled = False + self.edgeline_enabled = True + self.edgeline_column = 79 + self.codecompletion_auto_enabled = True + self.codecompletion_case_enabled = False + self.codecompletion_enter_enabled = False + self.calltips_enabled = True + self.go_to_definition_enabled = True + self.close_parentheses_enabled = True + self.close_quotes_enabled = True + self.add_colons_enabled = True + self.auto_unindent_enabled = True + self.indent_chars = " "*4 + self.tab_stop_width = 40 + self.help_enabled = False + self.default_font = None + self.wrap_enabled = False + self.tabmode_enabled = False + self.intelligent_backspace_enabled = True + self.highlight_current_line_enabled = False + self.highlight_current_cell_enabled = False + self.occurrence_highlighting_enabled = True + self.occurrence_highlighting_timeout=1500 + self.checkeolchars_enabled = True + self.always_remove_trailing_spaces = False + self.fullpath_sorting_enabled = None + self.focus_to_editor = True + self.set_fullpath_sorting_enabled(False) + self.create_new_file_if_empty = True + ccs = 'Spyder' + if ccs not in syntaxhighlighters.COLOR_SCHEME_NAMES: + ccs = syntaxhighlighters.COLOR_SCHEME_NAMES[0] + self.color_scheme = ccs + self.introspector = None + self.__file_status_flag = False + + # Real-time code analysis + self.analysis_timer = QTimer(self) + self.analysis_timer.setSingleShot(True) + self.analysis_timer.setInterval(2000) + self.analysis_timer.timeout.connect(self.analyze_script) + + # Accepting drops + self.setAcceptDrops(True) + + # Local shortcuts + self.shortcuts = self.create_shortcuts() + + def create_shortcuts(self): + """Create local shortcuts""" + # --- Configurable shortcuts + inspect = config_shortcut(self.inspect_current_object, context='Editor', + name='Inspect current object', parent=self) + set_breakpoint = config_shortcut(self.set_or_clear_breakpoint, + context='Editor', name='Breakpoint', + parent=self) + set_cond_breakpoint = config_shortcut( + self.set_or_edit_conditional_breakpoint, + context='Editor', + name='Conditional breakpoint', + parent=self) + gotoline = config_shortcut(self.go_to_line, context='Editor', + name='Go to line', parent=self) + tab = config_shortcut(self.go_to_previous_file, context='Editor', + name='Go to previous file', parent=self) + tabshift = config_shortcut(self.go_to_next_file, context='Editor', + name='Go to next file', parent=self) + run_selection = config_shortcut(self.run_selection, context='Editor', + name='Run selection', parent=self) + new_file = config_shortcut(lambda : self.sig_new_file[()].emit(), + context='Editor', name='New file', + parent=self) + open_file = config_shortcut(lambda : self.plugin_load[()].emit(), + context='Editor', name='Open file', + parent=self) + save_file = config_shortcut(self.save, context='Editor', + name='Save file', parent=self) + save_all = config_shortcut(self.save_all, context='Editor', + name='Save all', parent=self) + save_as = config_shortcut(lambda : self.sig_save_as.emit(), + context='Editor', name='Save As', + parent=self) + close_all = config_shortcut(self.close_all_files, context='Editor', + name='Close all', parent=self) + prev_edit_pos = config_shortcut(lambda : self.sig_prev_edit_pos.emit(), + context="Editor", + name="Last edit location", + parent=self) + prev_cursor = config_shortcut(lambda : self.sig_prev_cursor.emit(), + context="Editor", + name="Previous cursor position", + parent=self) + next_cursor = config_shortcut(lambda : self.sig_next_cursor.emit(), + context="Editor", + name="Next cursor position", + parent=self) + + # --- Fixed shortcuts + fixed_shortcut(QKeySequence.ZoomIn, self, lambda: self.zoom_in.emit()) + fixed_shortcut("Ctrl+=", self, lambda: self.zoom_in.emit()) + fixed_shortcut(QKeySequence.ZoomOut, self, lambda: self.zoom_out.emit()) + fixed_shortcut("Ctrl+0", self, lambda: self.zoom_reset.emit()) + fixed_shortcut("Ctrl+W", self, self.close_file) + fixed_shortcut("Ctrl+F4", self, self.close_file) + fixed_shortcut(QKeySequence(RUN_CELL_SHORTCUT), self, self.run_cell) + fixed_shortcut(QKeySequence(RUN_CELL_AND_ADVANCE_SHORTCUT), self, + self.run_cell_and_advance) + + # Return configurable ones + return [inspect, set_breakpoint, set_cond_breakpoint, gotoline, tab, + tabshift, run_selection, new_file, open_file, save_file, + save_all, save_as, close_all, prev_edit_pos, prev_cursor, + next_cursor] + + def get_shortcut_data(self): + """ + Returns shortcut data, a list of tuples (shortcut, text, default) + shortcut (QShortcut or QAction instance) + text (string): action/shortcut description + default (string): default key sequence + """ + return [sc.data for sc in self.shortcuts] + + def setup_editorstack(self, parent, layout): + """Setup editorstack's layout""" + menu_btn = create_toolbutton(self, icon=ima.icon('tooloptions'), + tip=_('Options')) + self.menu = QMenu(self) + menu_btn.setMenu(self.menu) + menu_btn.setPopupMode(menu_btn.InstantPopup) + self.menu.aboutToShow.connect(self.__setup_menu) + +# self.filelist_btn = create_toolbutton(self, +# icon=ima.icon('filelist'), +# tip=_("File list management"), +# triggered=self.open_fileswitcher_dlg) +# +# self.previous_btn = create_toolbutton(self, +# icon=ima.icon('previous'), +# tip=_("Previous file"), +# triggered=self.go_to_previous_file) +# +# self.next_btn = create_toolbutton(self, +# icon=ima.icon('next'), +# tip=_("Next file"), +# triggered=self.go_to_next_file) + + # Optional tabs +# corner_widgets = {Qt.TopRightCorner: [self.previous_btn, +# self.filelist_btn, self.next_btn, +# 5, menu_btn]} + corner_widgets = {Qt.TopRightCorner: [menu_btn]} + self.tabs = BaseTabs(self, menu=self.menu, menu_use_tooltips=True, + corner_widgets=corner_widgets) + self.tabs.tabBar().setObjectName('plugin-tab') + self.tabs.set_close_function(self.close_file) + + if hasattr(self.tabs, 'setDocumentMode') \ + and not sys.platform == 'darwin': + # Don't set document mode to true on OSX because it generates + # a crash when the editor is detached from the main window + # Fixes Issue 561 + self.tabs.setDocumentMode(True) + self.tabs.currentChanged.connect(self.current_changed) + + if sys.platform == 'darwin': + tab_container = QWidget() + tab_container.setObjectName('tab-container') + tab_layout = QHBoxLayout(tab_container) + tab_layout.setContentsMargins(0, 0, 0, 0) + tab_layout.addWidget(self.tabs) + layout.addWidget(tab_container) + else: + layout.addWidget(self.tabs) + + def add_corner_widgets_to_tabbar(self, widgets): + self.tabs.add_corner_widgets(widgets) + + def closeEvent(self, event): + self.threadmanager.close_all_threads() + self.analysis_timer.timeout.disconnect(self.analyze_script) + QWidget.closeEvent(self, event) + if is_pyqt46: + self.destroyed.emit() + + def clone_editor_from(self, other_finfo, set_current): + fname = other_finfo.filename + enc = other_finfo.encoding + new = other_finfo.newly_created + finfo = self.create_new_editor(fname, enc, "", + set_current=set_current, new=new, + cloned_from=other_finfo.editor) + finfo.set_analysis_results(other_finfo.analysis_results) + finfo.set_todo_results(other_finfo.todo_results) + return finfo.editor + + def clone_from(self, other): + """Clone EditorStack from other instance""" + for other_finfo in other.data: + self.clone_editor_from(other_finfo, set_current=True) + self.set_stack_index(other.get_stack_index()) + + @Slot() + def open_fileswitcher_dlg(self): + """Open file list management dialog box""" + if not self.tabs.count(): + return + if self.fileswitcher_dlg is not None and \ + self.fileswitcher_dlg.is_visible: + self.fileswitcher_dlg.hide() + self.fileswitcher_dlg.is_visible = False + return + self.fileswitcher_dlg = FileSwitcher(self, self.tabs, self.data) + self.fileswitcher_dlg.sig_goto_file.connect(self.set_stack_index) + self.fileswitcher_dlg.sig_close_file.connect(self.close_file) + self.fileswitcher_dlg.show() + self.fileswitcher_dlg.is_visible = True + + def update_fileswitcher_dlg(self): + """Synchronize file list dialog box with editor widget tabs""" + if self.fileswitcher_dlg: + self.fileswitcher_dlg.setup() + + def go_to_line(self): + """Go to line dialog""" + if self.data: + self.get_current_editor().exec_gotolinedialog() + + def set_or_clear_breakpoint(self): + """Set/clear breakpoint""" + if self.data: + editor = self.get_current_editor() + editor.add_remove_breakpoint() + + def set_or_edit_conditional_breakpoint(self): + """Set conditional breakpoint""" + if self.data: + editor = self.get_current_editor() + editor.add_remove_breakpoint(edit_condition=True) + + def inspect_current_object(self): + """Inspect current object in the Help plugin""" + if self.introspector: + editor = self.get_current_editor() + position = editor.get_position('cursor') + self.help.switch_to_editor_source() + self.introspector.show_object_info(position, auto=False) + else: + text = self.get_current_editor().get_current_object() + if text: + self.send_to_help(text, force=True) + + #------ Editor Widget Settings + def set_closable(self, state): + """Parent widget must handle the closable state""" + self.is_closable = state + + def set_io_actions(self, new_action, open_action, + save_action, revert_action): + self.new_action = new_action + self.open_action = open_action + self.save_action = save_action + self.revert_action = revert_action + + def set_find_widget(self, find_widget): + self.find_widget = find_widget + + def set_outlineexplorer(self, outlineexplorer): + self.outlineexplorer = outlineexplorer + self.outlineexplorer.outlineexplorer_is_visible.connect( + self._refresh_outlineexplorer) + + def initialize_outlineexplorer(self): + """This method is called separately from 'set_oulineexplorer' to avoid + doing unnecessary updates when there are multiple editor windows""" + for index in range(self.get_stack_count()): + if index != self.get_stack_index(): + self._refresh_outlineexplorer(index=index) + + def add_outlineexplorer_button(self, editor_plugin): + oe_btn = create_toolbutton(editor_plugin) + oe_btn.setDefaultAction(self.outlineexplorer.visibility_action) + self.add_corner_widgets_to_tabbar([5, oe_btn]) + + def set_help(self, help_plugin): + self.help = help_plugin + + def set_tempfile_path(self, path): + self.tempfile_path = path + + def set_title(self, text): + self.title = text + + def __update_editor_margins(self, editor): + editor.setup_margins(linenumbers=self.linenumbers_enabled, + markers=self.has_markers()) + + def __codeanalysis_settings_changed(self, current_finfo): + if self.data: + run_pyflakes, run_pep8 = self.pyflakes_enabled, self.pep8_enabled + for finfo in self.data: + self.__update_editor_margins(finfo.editor) + finfo.cleanup_analysis_results() + if (run_pyflakes or run_pep8) and current_finfo is not None: + if current_finfo is not finfo: + finfo.run_code_analysis(run_pyflakes, run_pep8) + + def set_pyflakes_enabled(self, state, current_finfo=None): + # CONF.get(self.CONF_SECTION, 'code_analysis/pyflakes') + self.pyflakes_enabled = state + self.__codeanalysis_settings_changed(current_finfo) + + def set_pep8_enabled(self, state, current_finfo=None): + # CONF.get(self.CONF_SECTION, 'code_analysis/pep8') + self.pep8_enabled = state + self.__codeanalysis_settings_changed(current_finfo) + + def has_markers(self): + """Return True if this editorstack has a marker margin for TODOs or + code analysis""" + return self.todolist_enabled or self.pyflakes_enabled\ + or self.pep8_enabled + + def set_todolist_enabled(self, state, current_finfo=None): + # CONF.get(self.CONF_SECTION, 'todo_list') + self.todolist_enabled = state + if self.data: + for finfo in self.data: + self.__update_editor_margins(finfo.editor) + finfo.cleanup_todo_results() + if state and current_finfo is not None: + if current_finfo is not finfo: + finfo.run_todo_finder() + + def set_realtime_analysis_enabled(self, state): + self.realtime_analysis_enabled = state + + def set_realtime_analysis_timeout(self, timeout): + self.analysis_timer.setInterval(timeout) + + def set_linenumbers_enabled(self, state, current_finfo=None): + # CONF.get(self.CONF_SECTION, 'line_numbers') + self.linenumbers_enabled = state + if self.data: + for finfo in self.data: + self.__update_editor_margins(finfo.editor) + + def set_blanks_enabled(self, state): + self.blanks_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_blanks_enabled(state) + + def set_edgeline_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'edge_line') + self.edgeline_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_edge_line_enabled(state) + + def set_edgeline_column(self, column): + # CONF.get(self.CONF_SECTION, 'edge_line_column') + self.edgeline_column = column + if self.data: + for finfo in self.data: + finfo.editor.set_edge_line_column(column) + + def set_codecompletion_auto_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'codecompletion_auto') + self.codecompletion_auto_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_codecompletion_auto(state) + + def set_codecompletion_case_enabled(self, state): + self.codecompletion_case_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_codecompletion_case(state) + + def set_codecompletion_enter_enabled(self, state): + self.codecompletion_enter_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_codecompletion_enter(state) + + def set_calltips_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'calltips') + self.calltips_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_calltips(state) + + def set_go_to_definition_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'go_to_definition') + self.go_to_definition_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_go_to_definition_enabled(state) + + def set_close_parentheses_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'close_parentheses') + self.close_parentheses_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_close_parentheses_enabled(state) + + def set_close_quotes_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'close_quotes') + self.close_quotes_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_close_quotes_enabled(state) + + def set_add_colons_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'add_colons') + self.add_colons_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_add_colons_enabled(state) + + def set_auto_unindent_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'auto_unindent') + self.auto_unindent_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_auto_unindent_enabled(state) + + def set_indent_chars(self, indent_chars): + # CONF.get(self.CONF_SECTION, 'indent_chars') + indent_chars = indent_chars[1:-1] # removing the leading/ending '*' + self.indent_chars = indent_chars + if self.data: + for finfo in self.data: + finfo.editor.set_indent_chars(indent_chars) + + def set_tab_stop_width(self, tab_stop_width): + # CONF.get(self.CONF_SECTION, 'tab_stop_width') + self.tab_stop_width = tab_stop_width + if self.data: + for finfo in self.data: + finfo.editor.setTabStopWidth(tab_stop_width) + + def set_help_enabled(self, state): + self.help_enabled = state + + def set_default_font(self, font, color_scheme=None): + self.default_font = font + if color_scheme is not None: + self.color_scheme = color_scheme + if self.data: + for finfo in self.data: + finfo.editor.set_font(font, color_scheme) + + def set_color_scheme(self, color_scheme): + self.color_scheme = color_scheme + if self.data: + for finfo in self.data: + finfo.editor.set_color_scheme(color_scheme) + + def set_wrap_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'wrap') + self.wrap_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.toggle_wrap_mode(state) + + def set_tabmode_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'tab_always_indent') + self.tabmode_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_tab_mode(state) + + def set_intelligent_backspace_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'intelligent_backspace') + self.intelligent_backspace_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.toggle_intelligent_backspace(state) + + def set_occurrence_highlighting_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'occurrence_highlighting') + self.occurrence_highlighting_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_occurrence_highlighting(state) + + def set_occurrence_highlighting_timeout(self, timeout): + # CONF.get(self.CONF_SECTION, 'occurrence_highlighting/timeout') + self.occurrence_highlighting_timeout = timeout + if self.data: + for finfo in self.data: + finfo.editor.set_occurrence_timeout(timeout) + + def set_highlight_current_line_enabled(self, state): + self.highlight_current_line_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_highlight_current_line(state) + + def set_highlight_current_cell_enabled(self, state): + self.highlight_current_cell_enabled = state + if self.data: + for finfo in self.data: + finfo.editor.set_highlight_current_cell(state) + + def set_checkeolchars_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'check_eol_chars') + self.checkeolchars_enabled = state + + def set_fullpath_sorting_enabled(self, state): + # CONF.get(self.CONF_SECTION, 'fullpath_sorting') + self.fullpath_sorting_enabled = state + if self.data: + finfo = self.data[self.get_stack_index()] + self.data.sort(key=self.__get_sorting_func()) + new_index = self.data.index(finfo) + self.__repopulate_stack() + self.set_stack_index(new_index) + + def set_always_remove_trailing_spaces(self, state): + # CONF.get(self.CONF_SECTION, 'always_remove_trailing_spaces') + self.always_remove_trailing_spaces = state + + def set_focus_to_editor(self, state): + self.focus_to_editor = state + + def set_introspector(self, introspector): + self.introspector = introspector + + #------ Stacked widget management + def get_stack_index(self): + return self.tabs.currentIndex() + + def get_current_finfo(self): + if self.data: + return self.data[self.get_stack_index()] + + def get_current_editor(self): + return self.tabs.currentWidget() + + def get_stack_count(self): + return self.tabs.count() + + def set_stack_index(self, index): + self.tabs.setCurrentIndex(index) + + def set_tabbar_visible(self, state): + self.tabs.tabBar().setVisible(state) + + def remove_from_data(self, index): + self.tabs.blockSignals(True) + self.tabs.removeTab(index) + self.data.pop(index) + self.tabs.blockSignals(False) + self.update_actions() + self.update_fileswitcher_dlg() + + def __modified_readonly_title(self, title, is_modified, is_readonly): + if is_modified is not None and is_modified: + title += "*" + if is_readonly is not None and is_readonly: + title = "(%s)" % title + return title + + def get_tab_text(self, filename, is_modified=None, is_readonly=None): + """Return tab title""" + return self.__modified_readonly_title(osp.basename(filename), + is_modified, is_readonly) + + def get_tab_tip(self, filename, is_modified=None, is_readonly=None): + """Return tab menu title""" + if self.fullpath_sorting_enabled: + text = filename + else: + text = u("%s — %s") + text = self.__modified_readonly_title(text, + is_modified, is_readonly) + if self.tempfile_path is not None\ + and filename == encoding.to_unicode_from_fs(self.tempfile_path): + temp_file_str = to_text_string(_("Temporary file")) + if self.fullpath_sorting_enabled: + return "%s (%s)" % (text, temp_file_str) + else: + return text % (temp_file_str, self.tempfile_path) + else: + if self.fullpath_sorting_enabled: + return text + else: + return text % (osp.basename(filename), osp.dirname(filename)) + + def __get_sorting_func(self): + if self.fullpath_sorting_enabled: + return lambda item: osp.join(osp.dirname(item.filename), + '_'+osp.basename(item.filename)) + else: + return lambda item: osp.basename(item.filename) + + def add_to_data(self, finfo, set_current): + self.data.append(finfo) + self.data.sort(key=self.__get_sorting_func()) + index = self.data.index(finfo) + fname, editor = finfo.filename, finfo.editor + self.tabs.insertTab(index, editor, self.get_tab_text(fname)) + self.set_stack_title(index, False) + if set_current: + self.set_stack_index(index) + self.current_changed(index) + self.update_actions() + self.update_fileswitcher_dlg() + + def __repopulate_stack(self): + self.tabs.blockSignals(True) + self.tabs.clear() + for finfo in self.data: + if finfo.newly_created: + is_modified = True + else: + is_modified = None + tab_text = self.get_tab_text(finfo.filename, is_modified) + tab_tip = self.get_tab_tip(finfo.filename) + index = self.tabs.addTab(finfo.editor, tab_text) + self.tabs.setTabToolTip(index, tab_tip) + self.tabs.blockSignals(False) + self.update_fileswitcher_dlg() + + def rename_in_data(self, index, new_filename): + finfo = self.data[index] + if osp.splitext(finfo.filename)[1] != osp.splitext(new_filename)[1]: + # File type has changed! + txt = to_text_string(finfo.editor.get_text_with_eol()) + language = get_file_language(new_filename, txt) + finfo.editor.set_language(language) + set_new_index = index == self.get_stack_index() + current_fname = self.get_current_filename() + finfo.filename = new_filename + self.data.sort(key=self.__get_sorting_func()) + new_index = self.data.index(finfo) + self.__repopulate_stack() + if set_new_index: + self.set_stack_index(new_index) + else: + # Fixes Issue 1287 + self.set_current_filename(current_fname) + if self.outlineexplorer is not None: + self.outlineexplorer.file_renamed(finfo.editor, finfo.filename) + return new_index + + def set_stack_title(self, index, is_modified): + finfo = self.data[index] + fname = finfo.filename + is_modified = (is_modified or finfo.newly_created) and not finfo.default + is_readonly = finfo.editor.isReadOnly() + tab_text = self.get_tab_text(fname, is_modified, is_readonly) + tab_tip = self.get_tab_tip(fname, is_modified, is_readonly) + self.tabs.setTabText(index, tab_text) + self.tabs.setTabToolTip(index, tab_tip) + + + #------ Context menu + def __setup_menu(self): + """Setup tab context menu before showing it""" + self.menu.clear() + if self.data: + actions = self.menu_actions + else: + actions = (self.new_action, self.open_action) + self.setFocus() # --> Editor.__get_focus_editortabwidget + add_actions(self.menu, list(actions)+self.__get_split_actions()) + self.close_action.setEnabled(self.is_closable) + + + #------ Hor/Ver splitting + def __get_split_actions(self): + # New window + self.newwindow_action = create_action(self, _("New window"), + icon=ima.icon('newwindow'), tip=_("Create a new editor window"), + triggered=lambda: self.create_new_window.emit()) + # Splitting + self.versplit_action = create_action(self, _("Split vertically"), + icon=ima.icon('versplit'), + tip=_("Split vertically this editor window"), + triggered=lambda: self.split_vertically.emit()) + self.horsplit_action = create_action(self, _("Split horizontally"), + icon=ima.icon('horsplit'), + tip=_("Split horizontally this editor window"), + triggered=lambda: self.split_horizontally.emit()) + self.close_action = create_action(self, _("Close this panel"), + icon=ima.icon('close_panel'), triggered=self.close) + return [None, self.newwindow_action, None, + self.versplit_action, self.horsplit_action, self.close_action] + + def reset_orientation(self): + self.horsplit_action.setEnabled(True) + self.versplit_action.setEnabled(True) + + def set_orientation(self, orientation): + self.horsplit_action.setEnabled(orientation == Qt.Horizontal) + self.versplit_action.setEnabled(orientation == Qt.Vertical) + + def update_actions(self): + state = self.get_stack_count() > 0 + self.horsplit_action.setEnabled(state) + self.versplit_action.setEnabled(state) + + + #------ Accessors + def get_current_filename(self): + if self.data: + return self.data[self.get_stack_index()].filename + + def has_filename(self, filename): + fixpath = lambda path: osp.normcase(osp.realpath(path)) + for index, finfo in enumerate(self.data): + if fixpath(filename) == fixpath(finfo.filename): + return index + + def set_current_filename(self, filename): + """Set current filename and return the associated editor instance""" + index = self.has_filename(filename) + if index is not None: + self.set_stack_index(index) + editor = self.data[index].editor + editor.setFocus() + return editor + + def is_file_opened(self, filename=None): + if filename is None: + # Is there any file opened? + return len(self.data) > 0 + else: + return self.has_filename(filename) + + + #------ Close file, tabwidget... + def close_file(self, index=None, force=False): + """Close file (index=None -> close current file) + Keep current file index unchanged (if current file + that is being closed)""" + current_index = self.get_stack_index() + count = self.get_stack_count() + + if index is None: + if count > 0: + index = current_index + else: + self.find_widget.set_editor(None) + return + + new_index = None + if count > 1: + if current_index == index: + new_index = self._get_previous_file_index() + else: + new_index = current_index + + is_ok = force or self.save_if_changed(cancelable=True, index=index) + if is_ok: + finfo = self.data[index] + self.threadmanager.close_threads(finfo) + # Removing editor reference from outline explorer settings: + if self.outlineexplorer is not None: + self.outlineexplorer.remove_editor(finfo.editor) + + self.remove_from_data(index) + + # We pass self object ID as a QString, because otherwise it would + # depend on the platform: long for 64bit, int for 32bit. Replacing + # by long all the time is not working on some 32bit platforms + # (see Issue 1094, Issue 1098) + self.sig_close_file.emit(str(id(self)), index) + + 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() + self.refresh_file_dependent_actions.emit() + self.update_plugin_title.emit() + + editor = self.get_current_editor() + if editor: + editor.setFocus() + + if new_index is not None: + if index < new_index: + new_index -= 1 + self.set_stack_index(new_index) + + if self.get_stack_count() == 0 and self.create_new_file_if_empty: + self.sig_new_file[()].emit() + return False + + return is_ok + + def close_all_files(self): + """Close all opened scripts""" + while self.close_file(): + pass + + def close_all_right(self): + """ Close all files opened to the right """ + num = self.get_stack_index() + n = self.get_stack_count() + for i in range(num, n-1): + self.close_file(num+1) + + def close_all_but_this(self): + """Close all files but the current one""" + self.close_all_right() + for i in range(0, self.get_stack_count()-1 ): + self.close_file(0) + + #------ Save + def save_if_changed(self, cancelable=False, index=None): + """Ask user to save file if modified""" + if index is None: + indexes = list(range(self.get_stack_count())) + else: + indexes = [index] + buttons = QMessageBox.Yes | QMessageBox.No + if cancelable: + buttons |= QMessageBox.Cancel + unsaved_nb = 0 + for index in indexes: + if self.data[index].editor.document().isModified(): + unsaved_nb += 1 + if not unsaved_nb: + # No file to save + return True + if unsaved_nb > 1: + buttons |= QMessageBox.YesAll | QMessageBox.NoAll + yes_all = False + for index in indexes: + self.set_stack_index(index) + finfo = self.data[index] + if finfo.filename == self.tempfile_path or yes_all: + if not self.save(): + return False + elif finfo.editor.document().isModified(): + answer = QMessageBox.question(self, self.title, + _("%s has been modified." + "
    Do you want to save changes?" + ) % osp.basename(finfo.filename), + buttons) + if answer == QMessageBox.Yes: + if not self.save(): + return False + elif answer == QMessageBox.YesAll: + if not self.save(): + return False + yes_all = True + elif answer == QMessageBox.NoAll: + return True + elif answer == QMessageBox.Cancel: + return False + return True + + def save(self, index=None, force=False): + """Save file""" + if index is None: + # Save the currently edited file + if not self.get_stack_count(): + return + index = self.get_stack_index() + + finfo = self.data[index] + if not finfo.editor.document().isModified() and not force: + return True + if not osp.isfile(finfo.filename) and not force: + # File has not been saved yet + return self.save_as(index=index) + if self.always_remove_trailing_spaces: + self.remove_trailing_spaces(index) + txt = to_text_string(finfo.editor.get_text_with_eol()) + try: + finfo.encoding = encoding.write(txt, finfo.filename, + finfo.encoding) + finfo.newly_created = False + self.encoding_changed.emit(finfo.encoding) + finfo.lastmodified = QFileInfo(finfo.filename).lastModified() + + # We pass self object ID as a QString, because otherwise it would + # depend on the platform: long for 64bit, int for 32bit. Replacing + # by long all the time is not working on some 32bit platforms + # (see Issue 1094, Issue 1098) + self.file_saved.emit(str(id(self)), index, finfo.filename) + + finfo.editor.document().setModified(False) + self.modification_changed(index=index) + self.analyze_script(index) + self.introspector.validate() + + #XXX CodeEditor-only: re-scan the whole text to rebuild outline + # explorer data from scratch (could be optimized because + # rehighlighting text means searching for all syntax coloring + # patterns instead of only searching for class/def patterns which + # would be sufficient for outline explorer data. + finfo.editor.rehighlight() + + self._refresh_outlineexplorer(index) + return True + except EnvironmentError as error: + QMessageBox.critical(self, _("Save"), + _("Unable to save script '%s'" + "

    Error message:
    %s" + ) % (osp.basename(finfo.filename), + str(error))) + return False + + def file_saved_in_other_editorstack(self, index, filename): + """ + File was just saved in another editorstack, let's synchronize! + This avoid file to be automatically reloaded + + Filename is passed in case file was just saved as another name + """ + finfo = self.data[index] + finfo.newly_created = False + finfo.filename = to_text_string(filename) + finfo.lastmodified = QFileInfo(finfo.filename).lastModified() + + def select_savename(self, original_filename): + self.redirect_stdio.emit(False) + filename, _selfilter = getsavefilename(self, _("Save file"), + original_filename, + get_edit_filters(), + get_filter(get_edit_filetypes(), + osp.splitext(original_filename)[1])) + self.redirect_stdio.emit(True) + if filename: + return osp.normpath(filename) + + def save_as(self, index=None): + """Save file as...""" + if index is None: + # Save the currently edited file + index = self.get_stack_index() + finfo = self.data[index] + filename = self.select_savename(finfo.filename) + if filename: + ao_index = self.has_filename(filename) + # Note: ao_index == index --> saving an untitled file + if ao_index and ao_index != index: + if not self.close_file(ao_index): + return + if ao_index < index: + index -= 1 + + new_index = self.rename_in_data(index, new_filename=filename) + + # We pass self object ID as a QString, because otherwise it would + # depend on the platform: long for 64bit, int for 32bit. Replacing + # by long all the time is not working on some 32bit platforms + # (see Issue 1094, Issue 1098) + self.file_renamed_in_data.emit(str(id(self)), index, filename) + + ok = self.save(index=new_index, force=True) + self.refresh(new_index) + self.set_stack_index(new_index) + return ok + else: + return False + + def save_all(self): + """Save all opened files""" + folders = set() + for index in range(self.get_stack_count()): + if self.data[index].editor.document().isModified(): + folders.add(osp.dirname(self.data[index].filename)) + self.save(index) + + #------ Update UI + def start_stop_analysis_timer(self): + self.is_analysis_done = False + if self.realtime_analysis_enabled: + self.analysis_timer.stop() + self.analysis_timer.start() + + def analyze_script(self, index=None): + """Analyze current script with pyflakes + find todos""" + if self.is_analysis_done: + return + if index is None: + index = self.get_stack_index() + if self.data: + finfo = self.data[index] + run_pyflakes, run_pep8 = self.pyflakes_enabled, self.pep8_enabled + if run_pyflakes or run_pep8: + finfo.run_code_analysis(run_pyflakes, run_pep8) + if self.todolist_enabled: + finfo.run_todo_finder() + self.is_analysis_done = True + + def set_analysis_results(self, index, analysis_results): + """Synchronize analysis results between editorstacks""" + self.data[index].set_analysis_results(analysis_results) + + def get_analysis_results(self): + if self.data: + return self.data[self.get_stack_index()].analysis_results + + def set_todo_results(self, index, todo_results): + """Synchronize todo results between editorstacks""" + self.data[index].set_todo_results(todo_results) + + def get_todo_results(self): + if self.data: + return self.data[self.get_stack_index()].todo_results + + def current_changed(self, index): + """Stack index has changed""" +# count = self.get_stack_count() +# for btn in (self.filelist_btn, self.previous_btn, self.next_btn): +# btn.setEnabled(count > 1) + + editor = self.get_current_editor() + if index != -1: + editor.setFocus() + if DEBUG_EDITOR: + print("setfocusto:", editor, file=STDOUT) + else: + self.reset_statusbar.emit() + self.opened_files_list_changed.emit() + + # Index history management + id_list = [id(self.tabs.widget(_i)) + for _i in range(self.tabs.count())] + for _id in self.stack_history[:]: + if _id not in id_list: + self.stack_history.pop(self.stack_history.index(_id)) + current_id = id(self.tabs.widget(index)) + while current_id in self.stack_history: + self.stack_history.pop(self.stack_history.index(current_id)) + self.stack_history.append(current_id) + if DEBUG_EDITOR: + print("current_changed:", index, self.data[index].editor, end=' ', file=STDOUT) + print(self.data[index].editor.get_document_id(), file=STDOUT) + + self.update_plugin_title.emit() + if editor is not None: + self.current_file_changed.emit(self.data[index].filename, + editor.get_position('cursor')) + + def _get_previous_file_index(self): + if len(self.stack_history) > 1: + last = len(self.stack_history)-1 + w_id = self.stack_history.pop(last) + self.stack_history.insert(0, w_id) + last_id = self.stack_history[last] + for _i in range(self.tabs.count()): + if id(self.tabs.widget(_i)) == last_id: + return _i + + def go_to_previous_file(self): + """Ctrl+Tab""" + prev_index = self._get_previous_file_index() + if prev_index is not None: + self.set_stack_index(prev_index) + elif len(self.stack_history) == 0 and self.get_stack_count(): + self.stack_history = [id(self.tabs.currentWidget())] + + def go_to_next_file(self): + """Ctrl+Shift+Tab""" + if len(self.stack_history) > 1: + last = len(self.stack_history)-1 + w_id = self.stack_history.pop(0) + self.stack_history.append(w_id) + last_id = self.stack_history[last] + for _i in range(self.tabs.count()): + if id(self.tabs.widget(_i)) == last_id: + self.set_stack_index(_i) + break + elif len(self.stack_history) == 0 and self.get_stack_count(): + self.stack_history = [id(self.tabs.currentWidget())] + + def focus_changed(self): + """Editor focus has changed""" + fwidget = QApplication.focusWidget() + for finfo in self.data: + if fwidget is finfo.editor: + self.refresh() + self.editor_focus_changed.emit() + + def _refresh_outlineexplorer(self, index=None, update=True, clear=False): + """Refresh outline explorer panel""" + oe = self.outlineexplorer + if oe is None: + return + if index is None: + index = self.get_stack_index() + enable = False + if self.data: + finfo = self.data[index] + if finfo.editor.is_python(): + enable = True + oe.setEnabled(True) + oe.set_current_editor(finfo.editor, finfo.filename, + update=update, clear=clear) + if not enable: + oe.setEnabled(False) + + def __refresh_statusbar(self, index): + """Refreshing statusbar widgets""" + finfo = self.data[index] + self.encoding_changed.emit(finfo.encoding) + # Refresh cursor position status: + line, index = finfo.editor.get_cursor_line_column() + self.sig_editor_cursor_position_changed.emit(line, index) + + def __refresh_readonly(self, index): + finfo = self.data[index] + read_only = not QFileInfo(finfo.filename).isWritable() + if not osp.isfile(finfo.filename): + # This is an 'untitledX.py' file (newly created) + read_only = False + finfo.editor.setReadOnly(read_only) + self.readonly_changed.emit(read_only) + + def __check_file_status(self, index): + """Check if file has been changed in any way outside Spyder: + 1. removed, moved or renamed outside Spyder + 2. modified outside Spyder""" + if self.__file_status_flag: + # Avoid infinite loop: when the QMessageBox.question pops, it + # gets focus and then give it back to the CodeEditor instance, + # triggering a refresh cycle which calls this method + return + self.__file_status_flag = True + + finfo = self.data[index] + name = osp.basename(finfo.filename) + + if finfo.newly_created: + # File was just created (not yet saved): do nothing + # (do not return because of the clean-up at the end of the method) + pass + + elif not osp.isfile(finfo.filename): + # File doesn't exist (removed, moved or offline): + answer = QMessageBox.warning(self, self.title, + _("%s is unavailable " + "(this file may have been removed, moved " + "or renamed outside Spyder)." + "
    Do you want to close it?") % name, + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + self.close_file(index) + else: + finfo.newly_created = True + finfo.editor.document().setModified(True) + self.modification_changed(index=index) + + else: + # Else, testing if it has been modified elsewhere: + lastm = QFileInfo(finfo.filename).lastModified() + if to_text_string(lastm.toString()) \ + != to_text_string(finfo.lastmodified.toString()): + if finfo.editor.document().isModified(): + answer = QMessageBox.question(self, + self.title, + _("%s has been modified outside Spyder." + "
    Do you want to reload it and lose all " + "your changes?") % name, + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + self.reload(index) + else: + finfo.lastmodified = lastm + else: + self.reload(index) + + # Finally, resetting temporary flag: + self.__file_status_flag = False + + def refresh(self, index=None): + """Refresh tabwidget""" + if index is None: + index = self.get_stack_index() + # Set current editor + if self.get_stack_count(): + index = self.get_stack_index() + finfo = self.data[index] + editor = finfo.editor + editor.setFocus() + self._refresh_outlineexplorer(index, update=False) + self.update_code_analysis_actions.emit() + self.__refresh_statusbar(index) + self.__refresh_readonly(index) + self.__check_file_status(index) + self.update_plugin_title.emit() + else: + editor = None + # Update the modification-state-dependent parameters + self.modification_changed() + # Update FindReplace binding + self.find_widget.set_editor(editor, refresh=False) + + def modification_changed(self, state=None, index=None, editor_id=None): + """ + Current editor's modification state has changed + --> change tab title depending on new modification state + --> enable/disable save/save all actions + """ + if editor_id is not None: + for index, _finfo in enumerate(self.data): + if id(_finfo.editor) == editor_id: + break + # This must be done before refreshing save/save all actions: + # (otherwise Save/Save all actions will always be enabled) + self.opened_files_list_changed.emit() + # -- + if index is None: + index = self.get_stack_index() + if index == -1: + return + finfo = self.data[index] + if state is None: + state = finfo.editor.document().isModified() + self.set_stack_title(index, state) + # Toggle save/save all actions state + self.save_action.setEnabled(state) + self.refresh_save_all_action.emit() + # Refreshing eol mode + eol_chars = finfo.editor.get_line_separator() + os_name = sourcecode.get_os_name_from_eol_chars(eol_chars) + self.refresh_eol_chars.emit(os_name) + + + #------ Load, reload + def reload(self, index): + """Reload file from disk""" + finfo = self.data[index] + txt, finfo.encoding = encoding.read(finfo.filename) + finfo.lastmodified = QFileInfo(finfo.filename).lastModified() + position = finfo.editor.get_position('cursor') + finfo.editor.set_text(txt) + finfo.editor.document().setModified(False) + finfo.editor.set_cursor_position(position) + self.introspector.validate() + + #XXX CodeEditor-only: re-scan the whole text to rebuild outline + # explorer data from scratch (could be optimized because + # rehighlighting text means searching for all syntax coloring + # patterns instead of only searching for class/def patterns which + # would be sufficient for outline explorer data. + finfo.editor.rehighlight() + + self._refresh_outlineexplorer(index) + + def revert(self): + """Revert file from disk""" + index = self.get_stack_index() + finfo = self.data[index] + filename = finfo.filename + if finfo.editor.document().isModified(): + answer = QMessageBox.warning(self, self.title, + _("All changes to %s will be lost." + "
    Do you want to revert file from disk?" + ) % osp.basename(filename), + QMessageBox.Yes|QMessageBox.No) + if answer != QMessageBox.Yes: + return + self.reload(index) + + def create_new_editor(self, fname, enc, txt, set_current, new=False, + cloned_from=None): + """ + Create a new editor instance + Returns finfo object (instead of editor as in previous releases) + """ + editor = codeeditor.CodeEditor(self) + introspector = self.introspector + editor.get_completions.connect(introspector.get_completions) + editor.sig_show_object_info.connect(introspector.show_object_info) + editor.go_to_definition.connect(introspector.go_to_definition) + + finfo = FileInfo(fname, enc, editor, new, self.threadmanager, + self.introspector) + + self.add_to_data(finfo, set_current) + finfo.send_to_help.connect(self.send_to_help) + finfo.analysis_results_changed.connect( + lambda: self.analysis_results_changed.emit()) + finfo.todo_results_changed.connect( + lambda: self.todo_results_changed.emit()) + finfo.edit_goto.connect(lambda fname, lineno, name: + self.edit_goto.emit(fname, lineno, name)) + finfo.save_breakpoints.connect(lambda s1, s2: + self.save_breakpoints.emit(s1, s2)) + editor.run_selection.connect(self.run_selection) + editor.run_cell.connect(self.run_cell) + editor.run_cell_and_advance.connect(self.run_cell_and_advance) + editor.sig_new_file.connect(self.sig_new_file.emit) + language = get_file_language(fname, txt) + editor.setup_editor( + linenumbers=self.linenumbers_enabled, + show_blanks=self.blanks_enabled, + edge_line=self.edgeline_enabled, + edge_line_column=self.edgeline_column, language=language, + markers=self.has_markers(), font=self.default_font, + color_scheme=self.color_scheme, + wrap=self.wrap_enabled, tab_mode=self.tabmode_enabled, + intelligent_backspace=self.intelligent_backspace_enabled, + highlight_current_line=self.highlight_current_line_enabled, + highlight_current_cell=self.highlight_current_cell_enabled, + occurrence_highlighting=self.occurrence_highlighting_enabled, + occurrence_timeout=self.occurrence_highlighting_timeout, + codecompletion_auto=self.codecompletion_auto_enabled, + codecompletion_case=self.codecompletion_case_enabled, + codecompletion_enter=self.codecompletion_enter_enabled, + calltips=self.calltips_enabled, + go_to_definition=self.go_to_definition_enabled, + close_parentheses=self.close_parentheses_enabled, + close_quotes=self.close_quotes_enabled, + add_colons=self.add_colons_enabled, + auto_unindent=self.auto_unindent_enabled, + indent_chars=self.indent_chars, + tab_stop_width=self.tab_stop_width, + cloned_from=cloned_from, + filename=fname) + if cloned_from is None: + editor.set_text(txt) + editor.document().setModified(False) + finfo.text_changed_at.connect( + lambda fname, position: + self.text_changed_at.emit(fname, position)) + editor.sig_cursor_position_changed.connect( + self.editor_cursor_position_changed) + editor.textChanged.connect(self.start_stop_analysis_timer) + editor.modificationChanged.connect( + lambda state: self.modification_changed(state, + editor_id=id(editor))) + editor.focus_in.connect(self.focus_changed) + editor.zoom_in.connect(lambda: self.zoom_in.emit()) + editor.zoom_out.connect(lambda: self.zoom_out.emit()) + editor.zoom_reset.connect(lambda: self.zoom_reset.emit()) + if self.outlineexplorer is not None: + # Removing editor reference from outline explorer settings: + editor.destroyed.connect(lambda obj=editor: + self.outlineexplorer.remove_editor(obj)) + + self.find_widget.set_editor(editor) + + self.refresh_file_dependent_actions.emit() + self.modification_changed(index=self.data.index(finfo)) + + return finfo + + def editor_cursor_position_changed(self, line, index): + """Cursor position of one of the editor in the stack has changed""" + self.sig_editor_cursor_position_changed.emit(line, index) + + def send_to_help(self, qstr1, qstr2=None, qstr3=None, qstr4=None, + force=False): + """qstr1: obj_text, qstr2: argpspec, qstr3: note, qstr4: doc_text""" + if not force and not self.help_enabled: + return + if self.help is not None \ + and (force or self.help.dockwidget.isVisible()): + # Help plugin exists and is visible + if qstr4 is None: + self.help.set_object_text(qstr1, ignore_unknown=True, + force_refresh=force) + else: + objtxt = to_text_string(qstr1) + name = objtxt.split('.')[-1] + argspec = to_text_string(qstr2) + note = to_text_string(qstr3) + docstring = to_text_string(qstr4) + doc = {'obj_text': objtxt, 'name': name, 'argspec': argspec, + 'note': note, 'docstring': docstring} + self.help.set_editor_doc(doc, force_refresh=force) + editor = self.get_current_editor() + editor.setFocus() + + def new(self, filename, encoding, text, default_content=False): + """ + Create new filename with *encoding* and *text* + """ + finfo = self.create_new_editor(filename, encoding, text, + set_current=False, new=True) + finfo.editor.set_cursor_position('eof') + finfo.editor.insert_text(os.linesep) + if default_content: + finfo.default = True + finfo.editor.document().setModified(False) + return finfo + + def load(self, filename, set_current=True): + """ + Load filename, create an editor instance and return it + *Warning* This is loading file, creating editor but not executing + the source code analysis -- the analysis must be done by the editor + plugin (in case multiple editorstack instances are handled) + """ + filename = osp.abspath(to_text_string(filename)) + self.starting_long_process.emit(_("Loading %s...") % filename) + text, enc = encoding.read(filename) + finfo = self.create_new_editor(filename, enc, text, set_current) + index = self.data.index(finfo) + self._refresh_outlineexplorer(index, update=True) + self.ending_long_process.emit("") + if self.isVisible() and self.checkeolchars_enabled \ + and sourcecode.has_mixed_eol_chars(text): + name = osp.basename(filename) + QMessageBox.warning(self, self.title, + _("%s contains mixed end-of-line " + "characters.
    Spyder will fix this " + "automatically.") % name, + QMessageBox.Ok) + self.set_os_eol_chars(index) + self.is_analysis_done = False + return finfo + + def set_os_eol_chars(self, index=None): + if index is None: + index = self.get_stack_index() + finfo = self.data[index] + eol_chars = sourcecode.get_eol_chars_from_os_name(os.name) + finfo.editor.set_eol_chars(eol_chars) + finfo.editor.document().setModified(True) + + def remove_trailing_spaces(self, index=None): + """Remove trailing spaces""" + if index is None: + index = self.get_stack_index() + finfo = self.data[index] + finfo.editor.remove_trailing_spaces() + + def fix_indentation(self, index=None): + """Replace tab characters by spaces""" + if index is None: + index = self.get_stack_index() + finfo = self.data[index] + finfo.editor.fix_indentation() + + #------ Run + def run_selection(self): + """ + Run selected text or current line in console. + + If some text is selected, then execute that text in console. + + If no text is selected, then execute current line, unless current line + is empty. Then, advance cursor to next line. If cursor is on last line + and that line is not empty, then add a new blank line and move the + cursor there. If cursor is on last line and that line is empty, then do + not move cursor. + """ + text = self.get_current_editor().get_selection_as_executable_code() + if text: + self.exec_in_extconsole.emit(text, self.focus_to_editor) + return + editor = self.get_current_editor() + line = editor.get_current_line() + text = line.lstrip() + if text: + self.exec_in_extconsole.emit(text, self.focus_to_editor) + if editor.is_cursor_on_last_line() and text: + editor.append(editor.get_line_separator()) + editor.move_cursor_to_next('line', 'down') + + def run_cell(self): + """Run current cell""" + text = self.get_current_editor().get_cell_as_executable_code() + finfo = self.get_current_finfo() + if finfo.editor.is_python() and text: + self.exec_in_extconsole.emit(text, self.focus_to_editor) + + def run_cell_and_advance(self): + """Run current cell and advance to the next one""" + self.run_cell() + if self.focus_to_editor: + self.get_current_editor().go_to_next_cell() + else: + term = QApplication.focusWidget() + self.get_current_editor().go_to_next_cell() + term.setFocus() + term = QApplication.focusWidget() + self.get_current_editor().go_to_next_cell() + term.setFocus() + + #------ Drag and drop + def dragEnterEvent(self, event): + """Reimplement Qt method + Inform Qt about the types of data that the widget accepts""" + source = event.mimeData() + # The second check is necessary on Windows, where source.hasUrls() + # can return True but source.urls() is [] + if source.hasUrls() and source.urls(): + all_urls = mimedata2url(source) + text = [encoding.is_text_file(url) for url in all_urls] + if any(text): + event.acceptProposedAction() + else: + event.ignore() + elif source.hasText(): + event.acceptProposedAction() + elif os.name == 'nt': + # This covers cases like dragging from compressed files, + # which can be opened by the Editor if they are plain + # text, but doesn't come with url info. + # Fixes Issue 2032 + event.acceptProposedAction() + else: + event.ignore() + + def dropEvent(self, event): + """Reimplement Qt method + Unpack dropped data and handle it""" + source = event.mimeData() + if source.hasUrls(): + files = mimedata2url(source) + files = [f for f in files if encoding.is_text_file(f)] + files = set(files or []) + for fname in files: + self.plugin_load.emit(fname) + elif source.hasText(): + editor = self.get_current_editor() + if editor is not None: + editor.insert_text( source.text() ) + event.acceptProposedAction() + + +class EditorSplitter(QSplitter): + def __init__(self, parent, plugin, menu_actions, first=False, + register_editorstack_cb=None, unregister_editorstack_cb=None): + QSplitter.__init__(self, parent) + self.setAttribute(Qt.WA_DeleteOnClose) + self.setChildrenCollapsible(False) + + self.toolbar_list = None + self.menu_list = None + + self.plugin = plugin + + if register_editorstack_cb is None: + register_editorstack_cb = self.plugin.register_editorstack + self.register_editorstack_cb = register_editorstack_cb + if unregister_editorstack_cb is None: + unregister_editorstack_cb = self.plugin.unregister_editorstack + self.unregister_editorstack_cb = unregister_editorstack_cb + + self.menu_actions = menu_actions + self.editorstack = EditorStack(self, menu_actions) + self.register_editorstack_cb(self.editorstack) + if not first: + self.plugin.clone_editorstack(editorstack=self.editorstack) + self.editorstack.destroyed.connect(lambda: self.editorstack_closed()) + self.editorstack.split_vertically.connect( + lambda: self.split(orientation=Qt.Vertical)) + self.editorstack.split_horizontally.connect( + lambda: self.split(orientation=Qt.Horizontal)) + self.addWidget(self.editorstack) + + def closeEvent(self, event): + QSplitter.closeEvent(self, event) + if is_pyqt46: + self.destroyed.emit() + + def __give_focus_to_remaining_editor(self): + focus_widget = self.plugin.get_focus_widget() + if focus_widget is not None: + focus_widget.setFocus() + + def editorstack_closed(self): + if DEBUG_EDITOR: + print("method 'editorstack_closed':", file=STDOUT) + print(" self :", self, file=STDOUT) +# print >>STDOUT, " sender:", self.sender() + self.unregister_editorstack_cb(self.editorstack) + self.editorstack = None + try: + close_splitter = self.count() == 1 + except RuntimeError: + # editorsplitter has been destroyed (happens when closing a + # EditorMainWindow instance) + return + if close_splitter: + # editorstack just closed was the last widget in this QSplitter + self.close() + return + self.__give_focus_to_remaining_editor() + + def editorsplitter_closed(self): + if DEBUG_EDITOR: + print("method 'editorsplitter_closed':", file=STDOUT) + print(" self :", self, file=STDOUT) +# print >>STDOUT, " sender:", self.sender() + try: + close_splitter = self.count() == 1 and self.editorstack is None + except RuntimeError: + # editorsplitter has been destroyed (happens when closing a + # EditorMainWindow instance) + return + if close_splitter: + # editorsplitter just closed was the last widget in this QSplitter + self.close() + return + elif self.count() == 2 and self.editorstack: + # back to the initial state: a single editorstack instance, + # as a single widget in this QSplitter: orientation may be changed + self.editorstack.reset_orientation() + self.__give_focus_to_remaining_editor() + + def split(self, orientation=Qt.Vertical): + self.setOrientation(orientation) + self.editorstack.set_orientation(orientation) + editorsplitter = EditorSplitter(self.parent(), self.plugin, + self.menu_actions, + register_editorstack_cb=self.register_editorstack_cb, + unregister_editorstack_cb=self.unregister_editorstack_cb) + self.addWidget(editorsplitter) + editorsplitter.destroyed.connect(lambda: self.editorsplitter_closed()) + current_editor = editorsplitter.editorstack.get_current_editor() + if current_editor is not None: + current_editor.setFocus() + + def iter_editorstacks(self): + editorstacks = [(self.widget(0), self.orientation())] + if self.count() > 1: + editorsplitter = self.widget(1) + editorstacks += editorsplitter.iter_editorstacks() + return editorstacks + + def get_layout_settings(self): + """Return layout state""" + splitsettings = [] + for editorstack, orientation in self.iter_editorstacks(): + clines = [finfo.editor.get_cursor_line_number() + for finfo in editorstack.data] + cfname = editorstack.get_current_filename() + splitsettings.append((orientation == Qt.Vertical, cfname, clines)) + return dict(hexstate=qbytearray_to_str(self.saveState()), + sizes=self.sizes(), splitsettings=splitsettings) + + def set_layout_settings(self, settings): + """Restore layout state""" + splitsettings = settings.get('splitsettings') + if splitsettings is None: + return + splitter = self + editor = None + for index, (is_vertical, cfname, clines) in enumerate(splitsettings): + if index > 0: + splitter.split(Qt.Vertical if is_vertical else Qt.Horizontal) + splitter = splitter.widget(1) + editorstack = splitter.widget(0) + for index, finfo in enumerate(editorstack.data): + editor = finfo.editor + # FIXME: Temporal fix + try: + editor.go_to_line(clines[index]) + except IndexError: + pass + editorstack.set_current_filename(cfname) + hexstate = settings.get('hexstate') + if hexstate is not None: + self.restoreState( QByteArray().fromHex( + str(hexstate).encode('utf-8')) ) + sizes = settings.get('sizes') + if sizes is not None: + self.setSizes(sizes) + if editor is not None: + editor.clearFocus() + editor.setFocus() + + +class EditorWidget(QSplitter): + def __init__(self, parent, plugin, menu_actions, show_fullpath, + fullpath_sorting, show_all_files, show_comments): + QSplitter.__init__(self, parent) + self.setAttribute(Qt.WA_DeleteOnClose) + + statusbar = parent.statusBar() # Create a status bar + self.readwrite_status = ReadWriteStatus(self, statusbar) + self.eol_status = EOLStatus(self, statusbar) + self.encoding_status = EncodingStatus(self, statusbar) + self.cursorpos_status = CursorPositionStatus(self, statusbar) + + self.editorstacks = [] + + self.plugin = plugin + + self.find_widget = FindReplace(self, enable_replace=True) + self.plugin.register_widget_shortcuts(self.find_widget) + self.find_widget.hide() + self.outlineexplorer = OutlineExplorerWidget(self, + show_fullpath=show_fullpath, + fullpath_sorting=fullpath_sorting, + show_all_files=show_all_files, + show_comments=show_comments) + self.outlineexplorer.edit_goto.connect( + lambda filenames, goto, word: + plugin.load(filenames=filenames, goto=goto, word=word, + editorwindow=self.parent())) + + editor_widgets = QWidget(self) + editor_layout = QVBoxLayout() + editor_layout.setContentsMargins(0, 0, 0, 0) + editor_widgets.setLayout(editor_layout) + editorsplitter = EditorSplitter(self, plugin, menu_actions, + register_editorstack_cb=self.register_editorstack, + unregister_editorstack_cb=self.unregister_editorstack) + self.editorsplitter = editorsplitter + editor_layout.addWidget(editorsplitter) + editor_layout.addWidget(self.find_widget) + + splitter = QSplitter(self) + splitter.setContentsMargins(0, 0, 0, 0) + splitter.addWidget(editor_widgets) + splitter.addWidget(self.outlineexplorer) + splitter.setStretchFactor(0, 5) + splitter.setStretchFactor(1, 1) + + # Refreshing outline explorer + editorsplitter.editorstack.initialize_outlineexplorer() + + def register_editorstack(self, editorstack): + self.editorstacks.append(editorstack) + if DEBUG_EDITOR: + print("EditorWidget.register_editorstack:", editorstack, file=STDOUT) + self.__print_editorstacks() + self.plugin.last_focus_editorstack[self.parent()] = editorstack + editorstack.set_closable( len(self.editorstacks) > 1 ) + editorstack.set_outlineexplorer(self.outlineexplorer) + editorstack.set_find_widget(self.find_widget) + editorstack.reset_statusbar.connect(self.readwrite_status.hide) + editorstack.reset_statusbar.connect(self.encoding_status.hide) + editorstack.reset_statusbar.connect(self.cursorpos_status.hide) + editorstack.readonly_changed.connect( + self.readwrite_status.readonly_changed) + editorstack.encoding_changed.connect( + self.encoding_status.encoding_changed) + editorstack.sig_editor_cursor_position_changed.connect( + self.cursorpos_status.cursor_position_changed) + editorstack.refresh_eol_chars.connect(self.eol_status.eol_changed) + self.plugin.register_editorstack(editorstack) + oe_btn = create_toolbutton(self) + oe_btn.setDefaultAction(self.outlineexplorer.visibility_action) + editorstack.add_corner_widgets_to_tabbar([5, oe_btn]) + + def __print_editorstacks(self): + print("%d editorstack(s) in editorwidget:" \ + % len(self.editorstacks), file=STDOUT) + for edst in self.editorstacks: + print(" ", edst, file=STDOUT) + + def unregister_editorstack(self, editorstack): + if DEBUG_EDITOR: + print("EditorWidget.unregister_editorstack:", editorstack, file=STDOUT) + self.plugin.unregister_editorstack(editorstack) + self.editorstacks.pop(self.editorstacks.index(editorstack)) + if DEBUG_EDITOR: + self.__print_editorstacks() + + +class EditorMainWindow(QMainWindow): + def __init__(self, plugin, menu_actions, toolbar_list, menu_list, + show_fullpath, fullpath_sorting, show_all_files, + show_comments): + QMainWindow.__init__(self) + self.setAttribute(Qt.WA_DeleteOnClose) + + self.window_size = None + + self.editorwidget = EditorWidget(self, plugin, menu_actions, + show_fullpath, fullpath_sorting, + show_all_files, show_comments) + self.setCentralWidget(self.editorwidget) + + # Give focus to current editor to update/show all status bar widgets + editorstack = self.editorwidget.editorsplitter.editorstack + editor = editorstack.get_current_editor() + if editor is not None: + editor.setFocus() + + self.setWindowTitle("Spyder - %s" % plugin.windowTitle()) + self.setWindowIcon(plugin.windowIcon()) + + if toolbar_list: + toolbars = [] + for title, actions in toolbar_list: + toolbar = self.addToolBar(title) + toolbar.setObjectName(str(id(toolbar))) + add_actions(toolbar, actions) + toolbars.append(toolbar) + if menu_list: + quit_action = create_action(self, _("Close window"), + icon="close_panel.png", + tip=_("Close this window"), + triggered=self.close) + menus = [] + for index, (title, actions) in enumerate(menu_list): + menu = self.menuBar().addMenu(title) + if index == 0: + # File menu + add_actions(menu, actions+[None, quit_action]) + else: + add_actions(menu, actions) + menus.append(menu) + + def resizeEvent(self, event): + """Reimplement Qt method""" + if not self.isMaximized() and not self.isFullScreen(): + self.window_size = self.size() + QMainWindow.resizeEvent(self, event) + + def closeEvent(self, event): + """Reimplement Qt method""" + QMainWindow.closeEvent(self, event) + if is_pyqt46: + self.destroyed.emit() + for editorstack in self.editorwidget.editorstacks[:]: + if DEBUG_EDITOR: + print("--> destroy_editorstack:", editorstack, file=STDOUT) + editorstack.destroyed.emit() + + def get_layout_settings(self): + """Return layout state""" + splitsettings = self.editorwidget.editorsplitter.get_layout_settings() + return dict(size=(self.window_size.width(), self.window_size.height()), + pos=(self.pos().x(), self.pos().y()), + is_maximized=self.isMaximized(), + is_fullscreen=self.isFullScreen(), + hexstate=qbytearray_to_str(self.saveState()), + splitsettings=splitsettings) + + def set_layout_settings(self, settings): + """Restore layout state""" + size = settings.get('size') + if size is not None: + self.resize( QSize(*size) ) + self.window_size = self.size() + pos = settings.get('pos') + if pos is not None: + self.move( QPoint(*pos) ) + hexstate = settings.get('hexstate') + if hexstate is not None: + self.restoreState( QByteArray().fromHex( + str(hexstate).encode('utf-8')) ) + if settings.get('is_maximized'): + self.setWindowState(Qt.WindowMaximized) + if settings.get('is_fullscreen'): + self.setWindowState(Qt.WindowFullScreen) + splitsettings = settings.get('splitsettings') + if splitsettings is not None: + self.editorwidget.editorsplitter.set_layout_settings(splitsettings) + + +class EditorPluginExample(QSplitter): + def __init__(self): + QSplitter.__init__(self) + + menu_actions = [] + + self.editorstacks = [] + self.editorwindows = [] + + self.last_focus_editorstack = {} # fake + + self.find_widget = FindReplace(self, enable_replace=True) + self.outlineexplorer = OutlineExplorerWidget(self, show_fullpath=False, + show_all_files=False) + self.outlineexplorer.edit_goto.connect(self.go_to_file) + self.editor_splitter = EditorSplitter(self, self, menu_actions, + first=True) + + editor_widgets = QWidget(self) + editor_layout = QVBoxLayout() + editor_layout.setContentsMargins(0, 0, 0, 0) + editor_widgets.setLayout(editor_layout) + editor_layout.addWidget(self.editor_splitter) + editor_layout.addWidget(self.find_widget) + + self.setContentsMargins(0, 0, 0, 0) + self.addWidget(editor_widgets) + self.addWidget(self.outlineexplorer) + + self.setStretchFactor(0, 5) + self.setStretchFactor(1, 1) + + self.menu_actions = menu_actions + self.toolbar_list = None + self.menu_list = None + self.setup_window([], []) + + def go_to_file(self, fname, lineno, text): + editorstack = self.editorstacks[0] + editorstack.set_current_filename(to_text_string(fname)) + editor = editorstack.get_current_editor() + editor.go_to_line(lineno, word=text) + + def closeEvent(self, event): + for win in self.editorwindows[:]: + win.close() + if DEBUG_EDITOR: + print(len(self.editorwindows), ":", self.editorwindows, file=STDOUT) + print(len(self.editorstacks), ":", self.editorstacks, file=STDOUT) + + event.accept() + + def load(self, fname): + QApplication.processEvents() + editorstack = self.editorstacks[0] + editorstack.load(fname) + editorstack.analyze_script() + + def register_editorstack(self, editorstack): + if DEBUG_EDITOR: + print("FakePlugin.register_editorstack:", editorstack, file=STDOUT) + self.editorstacks.append(editorstack) + if self.isAncestorOf(editorstack): + # editorstack is a child of the Editor plugin + editorstack.set_fullpath_sorting_enabled(True) + editorstack.set_closable( len(self.editorstacks) > 1 ) + editorstack.set_outlineexplorer(self.outlineexplorer) + editorstack.set_find_widget(self.find_widget) + oe_btn = create_toolbutton(self) + oe_btn.setDefaultAction(self.outlineexplorer.visibility_action) + editorstack.add_corner_widgets_to_tabbar([5, oe_btn]) + + action = QAction(self) + editorstack.set_io_actions(action, action, action, action) + font = QFont("Courier New") + font.setPointSize(10) + editorstack.set_default_font(font, color_scheme='Spyder') + + editorstack.sig_close_file.connect(self.close_file_in_all_editorstacks) + editorstack.file_saved.connect(self.file_saved_in_editorstack) + editorstack.file_renamed_in_data.connect( + self.file_renamed_in_data_in_editorstack) + editorstack.create_new_window.connect(self.create_new_window) + editorstack.plugin_load.connect(self.load) + + def unregister_editorstack(self, editorstack): + if DEBUG_EDITOR: + print("FakePlugin.unregister_editorstack:", editorstack, file=STDOUT) + self.editorstacks.pop(self.editorstacks.index(editorstack)) + + def clone_editorstack(self, editorstack): + editorstack.clone_from(self.editorstacks[0]) + + def setup_window(self, toolbar_list, menu_list): + self.toolbar_list = toolbar_list + self.menu_list = menu_list + + def create_new_window(self): + window = EditorMainWindow(self, self.menu_actions, + self.toolbar_list, self.menu_list, + show_fullpath=False, fullpath_sorting=True, + show_all_files=False, show_comments=True) + window.resize(self.size()) + window.show() + self.register_editorwindow(window) + window.destroyed.connect(lambda: self.unregister_editorwindow(window)) + + def register_editorwindow(self, window): + if DEBUG_EDITOR: + print("register_editorwindowQObject*:", window, file=STDOUT) + self.editorwindows.append(window) + + def unregister_editorwindow(self, window): + if DEBUG_EDITOR: + print("unregister_editorwindow:", window, file=STDOUT) + self.editorwindows.pop(self.editorwindows.index(window)) + + def get_focus_widget(self): + pass + + @Slot(str, int) + def close_file_in_all_editorstacks(self, editorstack_id_str, index): + for editorstack in self.editorstacks: + if str(id(editorstack)) != editorstack_id_str: + editorstack.blockSignals(True) + editorstack.close_file(index, force=True) + editorstack.blockSignals(False) + + # This method is never called in this plugin example. It's here only + # to show how to use the file_saved signal (see above). + @Slot(str, int, str) + def file_saved_in_editorstack(self, editorstack_id_str, index, filename): + """A file was saved in editorstack, this notifies others""" + for editorstack in self.editorstacks: + if str(id(editorstack)) != editorstack_id_str: + editorstack.file_saved_in_other_editorstack(index, filename) + + # This method is never called in this plugin example. It's here only + # to show how to use the file_saved signal (see above). + @Slot(str, int, str) + def file_renamed_in_data_in_editorstack(self, editorstack_id_str, + index, filename): + """A file was renamed in data in editorstack, this notifies others""" + for editorstack in self.editorstacks: + if str(id(editorstack)) != editorstack_id_str: + editorstack.rename_in_data(index, filename) + + def register_widget_shortcuts(self, widget): + """Fake!""" + pass + + +def test(): + from spyder.utils.qthelpers import qapplication + from spyder.config.base import get_module_path + from spyder.utils.introspection.manager import IntrospectionManager + + cur_dir = osp.join(get_module_path('spyder'), 'widgets') + app = qapplication(test_time=8) + introspector = IntrospectionManager() + + test = EditorPluginExample() + test.resize(900, 700) + test.show() + + editorstack = test.editor_splitter.editorstack + editorstack.set_introspector(introspector) + introspector.set_editor_widget(editorstack) + + import time + t0 = time.time() + test.load(osp.join(cur_dir, "editor.py")) + test.load(osp.join(cur_dir, "explorer.py")) + test.load(osp.join(cur_dir, "variableexplorer", "collectionseditor.py")) + test.load(osp.join(cur_dir, "sourcecode", "codeeditor.py")) + print("Elapsed time: %.3f s" % (time.time()-t0)) + + sys.exit(app.exec_()) + + +if __name__ == "__main__": + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/editortools.py spyder-3.0.2+dfsg1/spyder/widgets/editortools.py --- spyder-2.3.8+dfsg1/spyder/widgets/editortools.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/editortools.py 2016-11-16 02:30:06.000000000 +0000 @@ -0,0 +1,577 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Editor tools: outline explorer, etc.""" + +# Standard library imports +from __future__ import print_function +import os.path as osp +import re + +# Third party imports +from qtpy.compat import from_qvariant +from qtpy.QtCore import Qt, Signal, Slot +from qtpy.QtWidgets import QHBoxLayout, QTreeWidgetItem, QVBoxLayout, QWidget + +# Local imports +from spyder.config.base import _, STDOUT +from spyder.py3compat import to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import (create_action, create_toolbutton, + set_item_user_text) +from spyder.widgets.onecolumntree import OneColumnTree + + +#=============================================================================== +# Class browser +#=============================================================================== +class PythonCFM(object): + """ + Collection of helpers to match functions and classes + for Python language + This has to be reimplemented for other languages for the outline explorer + to be supported (not implemented yet: outline explorer won't be populated + unless the current script is a Python script) + """ + def __get_name(self, statmt, text): + match = re.match(r'[\ ]*%s ([a-zA-Z0-9_]*)[\ ]*[\(|\:]' % statmt, text) + if match is not None: + return match.group(1) + + def get_function_name(self, text): + return self.__get_name('def', text) + + def get_class_name(self, text): + return self.__get_name('class', text) + + +class FileRootItem(QTreeWidgetItem): + def __init__(self, path, treewidget): + QTreeWidgetItem.__init__(self, treewidget, QTreeWidgetItem.Type) + self.path = path + self.setIcon(0, ima.icon('python')) + self.setToolTip(0, path) + set_item_user_text(self, path) + + def set_path(self, path, fullpath): + self.path = path + self.set_text(fullpath) + + def set_text(self, fullpath): + self.setText(0, self.path if fullpath else osp.basename(self.path)) + +class TreeItem(QTreeWidgetItem): + """Class browser item base class""" + def __init__(self, name, line, parent, preceding): + if preceding is None: + QTreeWidgetItem.__init__(self, parent, QTreeWidgetItem.Type) + else: + if preceding is not parent: + # Preceding must be either the same as item's parent + # or have the same parent as item + while preceding.parent() is not parent: + preceding = preceding.parent() + if preceding is None: + break + if preceding is None: + QTreeWidgetItem.__init__(self, parent, QTreeWidgetItem.Type) + else: + QTreeWidgetItem.__init__(self, parent, preceding, + QTreeWidgetItem.Type) + self.setText(0, name) + parent_text = from_qvariant(parent.data(0, Qt.UserRole), + to_text_string) + set_item_user_text(self, parent_text+'/'+name) + self.line = line + + def set_icon(self, icon): + self.setIcon(0, icon) + + def setup(self): + self.setToolTip(0, _("Line %s") % str(self.line)) + +class ClassItem(TreeItem): + def setup(self): + self.set_icon(ima.icon('class')) + self.setToolTip(0, _("Class defined at line %s") % str(self.line)) + +class FunctionItem(TreeItem): + def is_method(self): + return isinstance(self.parent(), ClassItem) + + def setup(self): + if self.is_method(): + self.setToolTip(0, _("Method defined at line %s") % str(self.line)) + name = to_text_string(self.text(0)) + if name.startswith('__'): + self.set_icon(ima.icon('private2')) + elif name.startswith('_'): + self.set_icon(ima.icon('private1')) + else: + self.set_icon(ima.icon('method')) + else: + self.set_icon(ima.icon('function')) + self.setToolTip(0, _("Function defined at line %s" + ) % str(self.line)) + +class CommentItem(TreeItem): + def __init__(self, name, line, parent, preceding): + name = name.lstrip("# ") + TreeItem.__init__(self, name, line, parent, preceding) + + def setup(self): + self.set_icon(ima.icon('blockcomment')) + font = self.font(0) + font.setItalic(True) + self.setFont(0, font) + self.setToolTip(0, _("Line %s") % str(self.line)) + +class CellItem(TreeItem): + def __init__(self, name, line, parent, preceding): + name = name.lstrip("#% ") + if name.startswith(""): + name = name[10:].lstrip() + elif name.startswith("In["): + name = name[2:] + if name.endswith("]:"): + name = name[:-1] + name = name.strip() + TreeItem.__init__(self, name, line, parent, preceding) + + def setup(self): + self.set_icon(ima.icon('cell')) + font = self.font(0) + font.setItalic(True) + self.setFont(0, font) + self.setToolTip(0, _("Cell starts at line %s") % str(self.line)) + +def get_item_children(item): + children = [item.child(index) for index in range(item.childCount())] + for child in children[:]: + others = get_item_children(child) + if others is not None: + children += others + return sorted(children, key=lambda child: child.line) + +def item_at_line(root_item, line): + previous_item = root_item + for item in get_item_children(root_item): + if item.line > line: + return previous_item + previous_item = item + + +def remove_from_tree_cache(tree_cache, line=None, item=None): + if line is None: + for line, (_it, _level, _debug) in list(tree_cache.items()): + if _it is item: + break + item, _level, debug = tree_cache.pop(line) + try: + for child in [item.child(_i) for _i in range(item.childCount())]: + remove_from_tree_cache(tree_cache, item=child) + item.parent().removeChild(item) + except RuntimeError: + # Item has already been deleted + #XXX: remove this debug-related fragment of code + print("unable to remove tree item: ", debug, file=STDOUT) + +class OutlineExplorerTreeWidget(OneColumnTree): + def __init__(self, parent, show_fullpath=False, fullpath_sorting=True, + show_all_files=True, show_comments=True): + self.show_fullpath = show_fullpath + self.fullpath_sorting = fullpath_sorting + self.show_all_files = show_all_files + self.show_comments = show_comments + OneColumnTree.__init__(self, parent) + self.freeze = False # Freezing widget to avoid any unwanted update + self.editor_items = {} + self.editor_tree_cache = {} + self.editor_ids = {} + self.current_editor = None + title = _("Outline") + self.set_title(title) + self.setWindowTitle(title) + self.setUniformRowHeights(True) + + def get_actions_from_items(self, items): + """Reimplemented OneColumnTree method""" + fromcursor_act = create_action(self, text=_('Go to cursor position'), + icon=ima.icon('fromcursor'), + triggered=self.go_to_cursor_position) + fullpath_act = create_action(self, text=_( 'Show absolute path'), + toggled=self.toggle_fullpath_mode) + fullpath_act.setChecked(self.show_fullpath) + allfiles_act = create_action(self, text=_( 'Show all files'), + toggled=self.toggle_show_all_files) + allfiles_act.setChecked(self.show_all_files) + comment_act = create_action(self, text=_('Show special comments'), + toggled=self.toggle_show_comments) + comment_act.setChecked(self.show_comments) + actions = [fullpath_act, allfiles_act, comment_act, fromcursor_act] + return actions + + @Slot(bool) + def toggle_fullpath_mode(self, state): + self.show_fullpath = state + self.setTextElideMode(Qt.ElideMiddle if state else Qt.ElideRight) + for index in range(self.topLevelItemCount()): + self.topLevelItem(index).set_text(fullpath=self.show_fullpath) + + def __hide_or_show_root_items(self, item): + """ + show_all_files option is disabled: hide all root items except *item* + show_all_files option is enabled: do nothing + """ + for _it in self.get_top_level_items(): + _it.setHidden(_it is not item and not self.show_all_files) + + @Slot(bool) + def toggle_show_all_files(self, state): + self.show_all_files = state + if self.current_editor is not None: + editor_id = self.editor_ids[self.current_editor] + item = self.editor_items[editor_id] + self.__hide_or_show_root_items(item) + + @Slot(bool) + def toggle_show_comments(self, state): + self.show_comments = state + self.update_all() + + def set_fullpath_sorting(self, state): + self.fullpath_sorting = state + self.__sort_toplevel_items() + + @Slot() + def go_to_cursor_position(self): + if self.current_editor is not None: + line = self.current_editor.get_cursor_line_number() + editor_id = self.editor_ids[self.current_editor] + root_item = self.editor_items[editor_id] + item = item_at_line(root_item, line) + self.setCurrentItem(item) + self.scrollToItem(item) + + def clear(self): + """Reimplemented Qt method""" + self.set_title('') + OneColumnTree.clear(self) + + def set_current_editor(self, editor, fname, update): + """Bind editor instance""" + editor_id = editor.get_document_id() + if editor_id in list(self.editor_ids.values()): + item = self.editor_items[editor_id] + if not self.freeze: + self.scrollToItem(item) + self.root_item_selected(item) + self.__hide_or_show_root_items(item) + if update: + self.save_expanded_state() + tree_cache = self.editor_tree_cache[editor_id] + self.populate_branch(editor, item, tree_cache) + self.restore_expanded_state() + else: + # import time + # t0 = time.time() + root_item = FileRootItem(fname, self) + root_item.set_text(fullpath=self.show_fullpath) + tree_cache = self.populate_branch(editor, root_item) + self.__sort_toplevel_items() + self.__hide_or_show_root_items(root_item) + self.root_item_selected(root_item) + # print >>STDOUT, "Elapsed time: %d ms" % round((time.time()-t0)*1000) + self.editor_items[editor_id] = root_item + self.editor_tree_cache[editor_id] = tree_cache + self.resizeColumnToContents(0) + if editor not in self.editor_ids: + self.editor_ids[editor] = editor_id + self.current_editor = editor + + def file_renamed(self, editor, new_filename): + """File was renamed, updating outline explorer tree""" + editor_id = editor.get_document_id() + if editor_id in list(self.editor_ids.values()): + root_item = self.editor_items[editor_id] + root_item.set_path(new_filename, fullpath=self.show_fullpath) + self.__sort_toplevel_items() + + def update_all(self): + self.save_expanded_state() + for editor, editor_id in list(self.editor_ids.items()): + item = self.editor_items[editor_id] + tree_cache = self.editor_tree_cache[editor_id] + self.populate_branch(editor, item, tree_cache) + self.restore_expanded_state() + + def remove_editor(self, editor): + if editor in self.editor_ids: + if self.current_editor is editor: + self.current_editor = None + editor_id = self.editor_ids.pop(editor) + if editor_id not in list(self.editor_ids.values()): + root_item = self.editor_items.pop(editor_id) + self.editor_tree_cache.pop(editor_id) + try: + self.takeTopLevelItem(self.indexOfTopLevelItem(root_item)) + except RuntimeError: + # item has already been removed + pass + + def __sort_toplevel_items(self): + if self.fullpath_sorting: + sort_func = lambda item: osp.dirname(item.path.lower()) + else: + sort_func = lambda item: osp.basename(item.path.lower()) + self.sort_top_level_items(key=sort_func) + + def populate_branch(self, editor, root_item, tree_cache=None): + if tree_cache is None: + tree_cache = {} + + # Removing cached items for which line is > total line nb + for _l in list(tree_cache.keys()): + if _l >= editor.get_line_count(): + # Checking if key is still in tree cache in case one of its + # ancestors was deleted in the meantime (deleting all children): + if _l in tree_cache: + remove_from_tree_cache(tree_cache, line=_l) + + ancestors = [(root_item, 0)] + previous_item = None + previous_level = None + + oe_data = editor.highlighter.get_outlineexplorer_data() + editor.has_cell_separators = oe_data.get('found_cell_separators', False) + for block_nb in range(editor.get_line_count()): + line_nb = block_nb+1 + data = oe_data.get(block_nb) + if data is None: + level = None + else: + level = data.fold_level + citem, clevel, _d = tree_cache.get(line_nb, (None, None, "")) + + # Skip iteration if line is not the first line of a foldable block + if level is None: + if citem is not None: + remove_from_tree_cache(tree_cache, line=line_nb) + continue + + # Searching for class/function statements + not_class_nor_function = data.is_not_class_nor_function() + if not not_class_nor_function: + class_name = data.get_class_name() + if class_name is None: + func_name = data.get_function_name() + if func_name is None: + if citem is not None: + remove_from_tree_cache(tree_cache, line=line_nb) + continue + + if previous_level is not None: + if level == previous_level: + pass + elif level > previous_level+4: # Invalid indentation + continue + elif level > previous_level: + ancestors.append((previous_item, previous_level)) + else: + while len(ancestors) > 1 and level <= previous_level: + ancestors.pop(-1) + _item, previous_level = ancestors[-1] + parent, _level = ancestors[-1] + + if citem is not None: + cname = to_text_string(citem.text(0)) + + preceding = root_item if previous_item is None else previous_item + if not_class_nor_function: + if data.is_comment() and not self.show_comments: + if citem is not None: + remove_from_tree_cache(tree_cache, line=line_nb) + continue + if citem is not None: + if data.text == cname and level == clevel: + previous_level = clevel + previous_item = citem + continue + else: + remove_from_tree_cache(tree_cache, line=line_nb) + if data.is_comment(): + if data.def_type == data.CELL: + item = CellItem(data.text, line_nb, parent, preceding) + else: + item = CommentItem( + data.text, line_nb, parent, preceding) + else: + item = TreeItem(data.text, line_nb, parent, preceding) + elif class_name is not None: + if citem is not None: + if class_name == cname and level == clevel: + previous_level = clevel + previous_item = citem + continue + else: + remove_from_tree_cache(tree_cache, line=line_nb) + item = ClassItem(class_name, line_nb, parent, preceding) + else: + if citem is not None: + if func_name == cname and level == clevel: + previous_level = clevel + previous_item = citem + continue + else: + remove_from_tree_cache(tree_cache, line=line_nb) + item = FunctionItem(func_name, line_nb, parent, preceding) + + item.setup() + debug = "%s -- %s/%s" % (str(item.line).rjust(6), + to_text_string(item.parent().text(0)), + to_text_string(item.text(0))) + tree_cache[line_nb] = (item, level, debug) + previous_level = level + previous_item = item + + return tree_cache + + def root_item_selected(self, item): + """Root item has been selected: expanding it and collapsing others""" + for index in range(self.topLevelItemCount()): + root_item = self.topLevelItem(index) + if root_item is item: + self.expandItem(root_item) + else: + self.collapseItem(root_item) + + def restore(self): + """Reimplemented OneColumnTree method""" + if self.current_editor is not None: + self.collapseAll() + editor_id = self.editor_ids[self.current_editor] + self.root_item_selected(self.editor_items[editor_id]) + + def get_root_item(self, item): + root_item = item + while isinstance(root_item.parent(), QTreeWidgetItem): + root_item = root_item.parent() + return root_item + + def activated(self, item): + """Double-click event""" + line = 0 + if isinstance(item, TreeItem): + line = item.line + root_item = self.get_root_item(item) + self.freeze = True + if line: + self.parent().edit_goto.emit(root_item.path, line, item.text(0)) + else: + self.parent().edit.emit(root_item.path) + self.freeze = False + parent = self.current_editor.parent() + for editor_id, i_item in list(self.editor_items.items()): + if i_item is root_item: + #XXX: not working anymore!!! + for editor, _id in list(self.editor_ids.items()): + if _id == editor_id and editor.parent() is parent: + self.current_editor = editor + break + break + + def clicked(self, item): + """Click event""" + if isinstance(item, FileRootItem): + self.root_item_selected(item) + self.activated(item) + + +class OutlineExplorerWidget(QWidget): + """Class browser""" + edit_goto = Signal(str, int, str) + edit = Signal(str) + outlineexplorer_is_visible = Signal() + + def __init__(self, parent=None, show_fullpath=True, fullpath_sorting=True, + show_all_files=True, show_comments=True): + QWidget.__init__(self, parent) + + self.treewidget = OutlineExplorerTreeWidget(self, + show_fullpath=show_fullpath, + fullpath_sorting=fullpath_sorting, + show_all_files=show_all_files, + show_comments=show_comments) + + self.visibility_action = create_action(self, + _("Show/hide outline explorer"), + icon='outline_explorer_vis.png', + toggled=self.toggle_visibility) + self.visibility_action.setChecked(True) + + btn_layout = QHBoxLayout() + btn_layout.setAlignment(Qt.AlignLeft) + for btn in self.setup_buttons(): + btn_layout.addWidget(btn) + + layout = QVBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.addLayout(btn_layout) + layout.addWidget(self.treewidget) + self.setLayout(layout) + + @Slot(bool) + def toggle_visibility(self, state): + self.setVisible(state) + current_editor = self.treewidget.current_editor + if current_editor is not None: + current_editor.clearFocus() + current_editor.setFocus() + if state: + self.outlineexplorer_is_visible.emit() + + def setup_buttons(self): + fromcursor_btn = create_toolbutton(self, + icon=ima.icon('fromcursor'), + tip=_('Go to cursor position'), + triggered=self.treewidget.go_to_cursor_position) + collapse_btn = create_toolbutton(self) + collapse_btn.setDefaultAction(self.treewidget.collapse_selection_action) + expand_btn = create_toolbutton(self) + expand_btn.setDefaultAction(self.treewidget.expand_selection_action) + restore_btn = create_toolbutton(self) + restore_btn.setDefaultAction(self.treewidget.restore_action) + return (fromcursor_btn, collapse_btn, expand_btn, restore_btn) + + def set_current_editor(self, editor, fname, update, clear): + if clear: + self.remove_editor(editor) + if editor.highlighter is not None: + self.treewidget.set_current_editor(editor, fname, update) + + def remove_editor(self, editor): + self.treewidget.remove_editor(editor) + + def get_options(self): + """ + Return outline explorer options + except for fullpath sorting option which is more global + """ + return dict(show_fullpath=self.treewidget.show_fullpath, + show_all_files=self.treewidget.show_all_files, + show_comments=self.treewidget.show_comments, + expanded_state=self.treewidget.get_expanded_state(), + scrollbar_position=self.treewidget.get_scrollbar_position(), + visibility=self.isVisible()) + + def update(self): + self.treewidget.update_all() + + def set_fullpath_sorting(self, state): + self.treewidget.set_fullpath_sorting(state) + + def file_renamed(self, editor, new_filename): + self.treewidget.file_renamed(editor, new_filename) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/explorer.py spyder-3.0.2+dfsg1/spyder/widgets/explorer.py --- spyder-2.3.8+dfsg1/spyder/widgets/explorer.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/explorer.py 2016-11-17 03:39:40.000000000 +0000 @@ -0,0 +1,1232 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Files and Directories Explorer""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +from __future__ import with_statement +import os +import os.path as osp +import re +import shutil + +# Third party imports +from qtpy import API, is_pyqt46 +from qtpy.compat import getsavefilename, getexistingdirectory +from qtpy.QtCore import (QDir, QFileInfo, QMimeData, QSize, + QSortFilterProxyModel, Qt, QTimer, QUrl, + Signal, Slot) +from qtpy.QtGui import QDrag +from qtpy.QtWidgets import (QFileSystemModel, QHBoxLayout, QFileIconProvider, + QInputDialog, QLabel, QLineEdit, QMenu, + QMessageBox, QToolButton, QTreeView, QVBoxLayout, + QWidget) +# Local imports +from spyder.config.base import _ +from spyder.py3compat import (getcwd, str_lower, to_binary_string, + to_text_string, PY2) +from spyder.utils import icon_manager as ima +from spyder.utils import encoding, misc, programs, vcs +from spyder.utils.qthelpers import add_actions, create_action, file_uri + +try: + from nbconvert import PythonExporter as nbexporter +except: + nbexporter = None # analysis:ignore + + +def fixpath(path): + """Normalize path fixing case, making absolute and removing symlinks""" + norm = osp.normcase if os.name == 'nt' else osp.normpath + return norm(osp.abspath(osp.realpath(path))) + + +def create_script(fname): + """Create a new Python script""" + text = os.linesep.join(["# -*- coding: utf-8 -*-", "", ""]) + encoding.write(to_text_string(text), fname, 'utf-8') + + +def listdir(path, include='.', exclude=r'\.pyc$|^\.', show_all=False, + folders_only=False): + """List files and directories""" + namelist = [] + dirlist = [to_text_string(osp.pardir)] + for item in os.listdir(to_text_string(path)): + if re.search(exclude, item) and not show_all: + continue + if osp.isdir(osp.join(path, item)): + dirlist.append(item) + elif folders_only: + continue + elif re.search(include, item) or show_all: + namelist.append(item) + return sorted(dirlist, key=str_lower) + \ + sorted(namelist, key=str_lower) + + +def has_subdirectories(path, include, exclude, show_all): + """Return True if path has subdirectories""" + try: + # > 1 because of '..' + return len( listdir(path, include, exclude, + show_all, folders_only=True) ) > 1 + except (IOError, OSError): + return False + + +class IconProvider(QFileIconProvider): + """Project tree widget icon provider""" + def __init__(self, treeview): + super(IconProvider, self).__init__() + self.treeview = treeview + + @Slot(int) + @Slot(QFileInfo) + def icon(self, icontype_or_qfileinfo): + """Reimplement Qt method""" + if isinstance(icontype_or_qfileinfo, QFileIconProvider.IconType): + return super(IconProvider, self).icon(icontype_or_qfileinfo) + else: + qfileinfo = icontype_or_qfileinfo + fname = osp.normpath(to_text_string(qfileinfo.absoluteFilePath())) + if osp.isdir(fname): + return ima.icon('DirOpenIcon') + else: + return ima.icon('FileIcon') + + +class DirView(QTreeView): + """Base file/directory tree view""" + def __init__(self, parent=None): + super(DirView, self).__init__(parent) + self.name_filters = ['*.py'] + self.parent_widget = parent + self.show_all = None + self.menu = None + self.common_actions = None + self.__expanded_state = None + self._to_be_loaded = None + self.fsmodel = None + self.setup_fs_model() + self._scrollbar_positions = None + + #---- Model + def setup_fs_model(self): + """Setup filesystem model""" + filters = QDir.AllDirs | QDir.Files | QDir.Drives | QDir.NoDotAndDotDot + self.fsmodel = QFileSystemModel(self) + self.fsmodel.setFilter(filters) + self.fsmodel.setNameFilterDisables(False) + + def install_model(self): + """Install filesystem model""" + self.setModel(self.fsmodel) + + def setup_view(self): + """Setup view""" + self.install_model() + if not is_pyqt46: + self.fsmodel.directoryLoaded.connect( + lambda: self.resizeColumnToContents(0)) + self.setAnimated(False) + self.setSortingEnabled(True) + self.sortByColumn(0, Qt.AscendingOrder) + self.fsmodel.modelReset.connect(self.reset_icon_provider) + self.reset_icon_provider() + # Disable the view of .spyproject. + self.filter_directories() + + def set_name_filters(self, name_filters): + """Set name filters""" + self.name_filters = name_filters + self.fsmodel.setNameFilters(name_filters) + + def set_show_all(self, state): + """Toggle 'show all files' state""" + if state: + self.fsmodel.setNameFilters([]) + else: + self.fsmodel.setNameFilters(self.name_filters) + + def get_filename(self, index): + """Return filename associated with *index*""" + if index: + return osp.normpath(to_text_string(self.fsmodel.filePath(index))) + + def get_index(self, filename): + """Return index associated with filename""" + return self.fsmodel.index(filename) + + def get_selected_filenames(self): + """Return selected filenames""" + if self.selectionMode() == self.ExtendedSelection: + return [self.get_filename(idx) for idx in self.selectedIndexes()] + else: + return [self.get_filename(self.currentIndex())] + + def get_dirname(self, index): + """Return dirname associated with *index*""" + fname = self.get_filename(index) + if fname: + if osp.isdir(fname): + return fname + else: + return osp.dirname(fname) + + #---- Tree view widget + def setup(self, name_filters=['*.py', '*.pyw'], show_all=False): + """Setup tree widget""" + self.setup_view() + + self.set_name_filters(name_filters) + self.show_all = show_all + + # Setup context menu + self.menu = QMenu(self) + self.common_actions = self.setup_common_actions() + + def reset_icon_provider(self): + """Reset file system model icon provider + The purpose of this is to refresh files/directories icons""" + self.fsmodel.setIconProvider(IconProvider(self)) + + #---- Context menu + def setup_common_actions(self): + """Setup context menu common actions""" + # Filters + filters_action = create_action(self, _("Edit filename filters..."), + None, ima.icon('filter'), + triggered=self.edit_filter) + # Show all files + all_action = create_action(self, _("Show all files"), + toggled=self.toggle_all) + all_action.setChecked(self.show_all) + self.toggle_all(self.show_all) + + return [filters_action, all_action] + + @Slot() + def edit_filter(self): + """Edit name filters""" + filters, valid = QInputDialog.getText(self, _('Edit filename filters'), + _('Name filters:'), + QLineEdit.Normal, + ", ".join(self.name_filters)) + if valid: + filters = [f.strip() for f in to_text_string(filters).split(',')] + self.parent_widget.sig_option_changed.emit('name_filters', filters) + self.set_name_filters(filters) + + @Slot(bool) + def toggle_all(self, checked): + """Toggle all files mode""" + self.parent_widget.sig_option_changed.emit('show_all', checked) + self.show_all = checked + self.set_show_all(checked) + + def create_file_new_actions(self, fnames): + """Return actions for submenu 'New...'""" + if not fnames: + return [] + new_file_act = create_action(self, _("File..."), + icon=ima.icon('filenew'), + triggered=lambda: + self.new_file(fnames[-1])) + new_module_act = create_action(self, _("Module..."), + icon=ima.icon('spyder'), + triggered=lambda: + self.new_module(fnames[-1])) + new_folder_act = create_action(self, _("Folder..."), + icon=ima.icon('folder_new'), + triggered=lambda: + self.new_folder(fnames[-1])) + new_package_act = create_action(self, _("Package..."), + icon=ima.icon('package_new'), + triggered=lambda: + self.new_package(fnames[-1])) + return [new_file_act, new_folder_act, None, + new_module_act, new_package_act] + + def create_file_import_actions(self, fnames): + """Return actions for submenu 'Import...'""" + return [] + + def create_file_manage_actions(self, fnames): + """Return file management actions""" + only_files = all([osp.isfile(_fn) for _fn in fnames]) + only_modules = all([osp.splitext(_fn)[1] in ('.py', '.pyw', '.ipy') + for _fn in fnames]) + only_notebooks = all([osp.splitext(_fn)[1] == '.ipynb' + for _fn in fnames]) + only_valid = all([encoding.is_text_file(_fn) for _fn in fnames]) + run_action = create_action(self, _("Run"), icon=ima.icon('run'), + triggered=self.run) + edit_action = create_action(self, _("Edit"), icon=ima.icon('edit'), + triggered=self.clicked) + move_action = create_action(self, _("Move..."), + icon="move.png", + triggered=self.move) + delete_action = create_action(self, _("Delete..."), + icon=ima.icon('editdelete'), + triggered=self.delete) + rename_action = create_action(self, _("Rename..."), + icon=ima.icon('rename'), + triggered=self.rename) + open_action = create_action(self, _("Open"), triggered=self.open) + ipynb_convert_action = create_action(self, _("Convert to Python script"), + icon=ima.icon('python'), + triggered=self.convert_notebooks) + + actions = [] + if only_modules: + actions.append(run_action) + if only_valid and only_files: + actions.append(edit_action) + else: + actions.append(open_action) + actions += [delete_action, rename_action] + basedir = fixpath(osp.dirname(fnames[0])) + if all([fixpath(osp.dirname(_fn)) == basedir for _fn in fnames]): + actions.append(move_action) + actions += [None] + if only_notebooks and nbexporter is not None: + actions.append(ipynb_convert_action) + + # VCS support is quite limited for now, so we are enabling the VCS + # related actions only when a single file/folder is selected: + dirname = fnames[0] if osp.isdir(fnames[0]) else osp.dirname(fnames[0]) + if len(fnames) == 1 and vcs.is_vcs_repository(dirname): + # QAction.triggered works differently for PySide and PyQt + if not API == 'pyside': + commit_slot = lambda _checked, fnames=[dirname]:\ + self.vcs_command(fnames, 'commit') + browse_slot = lambda _checked, fnames=[dirname]:\ + self.vcs_command(fnames, 'browse') + else: + commit_slot = lambda fnames=[dirname]:\ + self.vcs_command(fnames, 'commit') + browse_slot = lambda fnames=[dirname]:\ + self.vcs_command(fnames, 'browse') + vcs_ci = create_action(self, _("Commit"), + icon=ima.icon('vcs_commit'), + triggered=commit_slot) + vcs_log = create_action(self, _("Browse repository"), + icon=ima.icon('vcs_browse'), + triggered=browse_slot) + actions += [None, vcs_ci, vcs_log] + + return actions + + def create_folder_manage_actions(self, fnames): + """Return folder management actions""" + actions = [] + if os.name == 'nt': + _title = _("Open command prompt here") + else: + _title = _("Open terminal here") + action = create_action(self, _title, icon=ima.icon('cmdprompt'), + triggered=lambda: + self.open_terminal(fnames)) + actions.append(action) + _title = _("Open Python console here") + action = create_action(self, _title, icon=ima.icon('python'), + triggered=lambda: + self.open_interpreter(fnames)) + actions.append(action) + return actions + + def create_context_menu_actions(self): + """Create context menu actions""" + actions = [] + fnames = self.get_selected_filenames() + new_actions = self.create_file_new_actions(fnames) + if len(new_actions) > 1: + # Creating a submenu only if there is more than one entry + new_act_menu = QMenu(_('New'), self) + add_actions(new_act_menu, new_actions) + actions.append(new_act_menu) + else: + actions += new_actions + import_actions = self.create_file_import_actions(fnames) + if len(import_actions) > 1: + # Creating a submenu only if there is more than one entry + import_act_menu = QMenu(_('Import'), self) + add_actions(import_act_menu, import_actions) + actions.append(import_act_menu) + else: + actions += import_actions + if actions: + actions.append(None) + if fnames: + actions += self.create_file_manage_actions(fnames) + if actions: + actions.append(None) + if fnames and all([osp.isdir(_fn) for _fn in fnames]): + actions += self.create_folder_manage_actions(fnames) + if actions: + actions.append(None) + actions += self.common_actions + return actions + + def update_menu(self): + """Update context menu""" + self.menu.clear() + add_actions(self.menu, self.create_context_menu_actions()) + + #---- Events + def viewportEvent(self, event): + """Reimplement Qt method""" + + # Prevent Qt from crashing or showing warnings like: + # "QSortFilterProxyModel: index from wrong model passed to + # mapFromSource", probably due to the fact that the file system model + # is being built. See Issue 1250. + # + # This workaround was inspired by the following KDE bug: + # https://bugs.kde.org/show_bug.cgi?id=172198 + # + # Apparently, this is a bug from Qt itself. + self.executeDelayedItemsLayout() + + return QTreeView.viewportEvent(self, event) + + def contextMenuEvent(self, event): + """Override Qt method""" + self.update_menu() + self.menu.popup(event.globalPos()) + + def keyPressEvent(self, event): + """Reimplement Qt method""" + if event.key() in (Qt.Key_Enter, Qt.Key_Return): + self.clicked() + elif event.key() == Qt.Key_F2: + self.rename() + elif event.key() == Qt.Key_Delete: + self.delete() + elif event.key() == Qt.Key_Backspace: + self.go_to_parent_directory() + else: + QTreeView.keyPressEvent(self, event) + + def mouseDoubleClickEvent(self, event): + """Reimplement Qt method""" + QTreeView.mouseDoubleClickEvent(self, event) + self.clicked() + + @Slot() + def clicked(self): + """Selected item was double-clicked or enter/return was pressed""" + fnames = self.get_selected_filenames() + for fname in fnames: + if osp.isdir(fname): + self.directory_clicked(fname) + else: + self.open([fname]) + + def directory_clicked(self, dirname): + """Directory was just clicked""" + pass + + #---- Drag + def dragEnterEvent(self, event): + """Drag and Drop - Enter event""" + event.setAccepted(event.mimeData().hasFormat("text/plain")) + + def dragMoveEvent(self, event): + """Drag and Drop - Move event""" + if (event.mimeData().hasFormat("text/plain")): + event.setDropAction(Qt.MoveAction) + event.accept() + else: + event.ignore() + + def startDrag(self, dropActions): + """Reimplement Qt Method - handle drag event""" + data = QMimeData() + data.setUrls([QUrl(fname) for fname in self.get_selected_filenames()]) + drag = QDrag(self) + drag.setMimeData(data) + drag.exec_() + + #---- File/Directory actions + @Slot() + def open(self, fnames=None): + """Open files with the appropriate application""" + if fnames is None: + fnames = self.get_selected_filenames() + for fname in fnames: + if osp.isfile(fname) and encoding.is_text_file(fname): + self.parent_widget.sig_open_file.emit(fname) + else: + self.open_outside_spyder([fname]) + + def open_outside_spyder(self, fnames): + """Open file outside Spyder with the appropriate application + If this does not work, opening unknown file in Spyder, as text file""" + for path in sorted(fnames): + path = file_uri(path) + ok = programs.start_file(path) + if not ok: + self.parent_widget.edit.emit(path) + + def open_terminal(self, fnames): + """Open terminal""" + for path in sorted(fnames): + self.parent_widget.open_terminal.emit(path) + + def open_interpreter(self, fnames): + """Open interpreter""" + for path in sorted(fnames): + self.parent_widget.open_interpreter.emit(path) + + @Slot() + def run(self, fnames=None): + """Run Python scripts""" + if fnames is None: + fnames = self.get_selected_filenames() + for fname in fnames: + self.parent_widget.run.emit(fname) + + def remove_tree(self, dirname): + """Remove whole directory tree + Reimplemented in project explorer widget""" + shutil.rmtree(dirname, onerror=misc.onerror) + + def delete_file(self, fname, multiple, yes_to_all): + """Delete file""" + if multiple: + buttons = QMessageBox.Yes|QMessageBox.YesAll| \ + QMessageBox.No|QMessageBox.Cancel + else: + buttons = QMessageBox.Yes|QMessageBox.No + if yes_to_all is None: + answer = QMessageBox.warning(self, _("Delete"), + _("Do you really want " + "to delete %s?" + ) % osp.basename(fname), buttons) + if answer == QMessageBox.No: + return yes_to_all + elif answer == QMessageBox.Cancel: + return False + elif answer == QMessageBox.YesAll: + yes_to_all = True + try: + if osp.isfile(fname): + misc.remove_file(fname) + self.parent_widget.removed.emit(fname) + else: + self.remove_tree(fname) + self.parent_widget.removed_tree.emit(fname) + return yes_to_all + except EnvironmentError as error: + action_str = _('delete') + QMessageBox.critical(self, _("Project Explorer"), + _("Unable to %s %s" + "

    Error message:
    %s" + ) % (action_str, fname, to_text_string(error))) + return False + + @Slot() + def delete(self, fnames=None): + """Delete files""" + if fnames is None: + fnames = self.get_selected_filenames() + multiple = len(fnames) > 1 + yes_to_all = None + for fname in fnames: + spyproject_path = osp.join(fname,'.spyproject') + if osp.isdir(fname) and osp.exists(spyproject_path): + QMessageBox.information(self, _('File Explorer'), + _("The current directory contains a " + "project.

    " + "If you want to delete" + " the project, please go to " + "Projects » Delete " + "Project")) + else: + yes_to_all = self.delete_file(fname, multiple, yes_to_all) + if yes_to_all is not None and not yes_to_all: + # Canceled + break + + def convert_notebook(self, fname): + """Convert an IPython notebook to a Python script in editor""" + try: + script = nbexporter().from_filename(fname)[0] + except Exception as e: + QMessageBox.critical(self, _('Conversion error'), + _("It was not possible to convert this " + "notebook. The error is:\n\n") + \ + to_text_string(e)) + return + self.parent_widget.sig_new_file.emit(script) + + @Slot() + def convert_notebooks(self): + """Convert IPython notebooks to Python scripts in editor""" + fnames = self.get_selected_filenames() + if not isinstance(fnames, (tuple, list)): + fnames = [fnames] + for fname in fnames: + self.convert_notebook(fname) + + def rename_file(self, fname): + """Rename file""" + path, valid = QInputDialog.getText(self, _('Rename'), + _('New name:'), QLineEdit.Normal, + osp.basename(fname)) + if valid: + path = osp.join(osp.dirname(fname), to_text_string(path)) + if path == fname: + return + if osp.exists(path): + if QMessageBox.warning(self, _("Rename"), + _("Do you really want to rename %s and " + "overwrite the existing file %s?" + ) % (osp.basename(fname), osp.basename(path)), + QMessageBox.Yes|QMessageBox.No) == QMessageBox.No: + return + try: + misc.rename_file(fname, path) + self.parent_widget.renamed.emit(fname, path) + return path + except EnvironmentError as error: + QMessageBox.critical(self, _("Rename"), + _("Unable to rename file %s" + "

    Error message:
    %s" + ) % (osp.basename(fname), to_text_string(error))) + + @Slot() + def rename(self, fnames=None): + """Rename files""" + if fnames is None: + fnames = self.get_selected_filenames() + if not isinstance(fnames, (tuple, list)): + fnames = [fnames] + for fname in fnames: + self.rename_file(fname) + + @Slot() + def move(self, fnames=None): + """Move files/directories""" + if fnames is None: + fnames = self.get_selected_filenames() + orig = fixpath(osp.dirname(fnames[0])) + while True: + self.parent_widget.redirect_stdio.emit(False) + folder = getexistingdirectory(self, _("Select directory"), orig) + self.parent_widget.redirect_stdio.emit(True) + if folder: + folder = fixpath(folder) + if folder != orig: + break + else: + return + for fname in fnames: + basename = osp.basename(fname) + try: + misc.move_file(fname, osp.join(folder, basename)) + except EnvironmentError as error: + QMessageBox.critical(self, _("Error"), + _("Unable to move %s" + "

    Error message:
    %s" + ) % (basename, to_text_string(error))) + + def create_new_folder(self, current_path, title, subtitle, is_package): + """Create new folder""" + if current_path is None: + current_path = '' + if osp.isfile(current_path): + current_path = osp.dirname(current_path) + name, valid = QInputDialog.getText(self, title, subtitle, + QLineEdit.Normal, "") + if valid: + dirname = osp.join(current_path, to_text_string(name)) + try: + os.mkdir(dirname) + except EnvironmentError as error: + QMessageBox.critical(self, title, + _("Unable " + "to create folder %s" + "

    Error message:
    %s" + ) % (dirname, to_text_string(error))) + finally: + if is_package: + fname = osp.join(dirname, '__init__.py') + try: + with open(fname, 'wb') as f: + f.write(to_binary_string('#')) + return dirname + except EnvironmentError as error: + QMessageBox.critical(self, title, + _("Unable " + "to create file %s" + "

    Error message:
    %s" + ) % (fname, + to_text_string(error))) + + def new_folder(self, basedir): + """New folder""" + title = _('New folder') + subtitle = _('Folder name:') + self.create_new_folder(basedir, title, subtitle, is_package=False) + + def new_package(self, basedir): + """New package""" + title = _('New package') + subtitle = _('Package name:') + self.create_new_folder(basedir, title, subtitle, is_package=True) + + def create_new_file(self, current_path, title, filters, create_func): + """Create new file + Returns True if successful""" + if current_path is None: + current_path = '' + if osp.isfile(current_path): + current_path = osp.dirname(current_path) + self.parent_widget.redirect_stdio.emit(False) + fname, _selfilter = getsavefilename(self, title, current_path, filters) + self.parent_widget.redirect_stdio.emit(True) + if fname: + try: + create_func(fname) + return fname + except EnvironmentError as error: + QMessageBox.critical(self, _("New file"), + _("Unable to create file %s" + "

    Error message:
    %s" + ) % (fname, to_text_string(error))) + + def new_file(self, basedir): + """New file""" + title = _("New file") + filters = _("All files")+" (*)" + def create_func(fname): + """File creation callback""" + if osp.splitext(fname)[1] in ('.py', '.pyw', '.ipy'): + create_script(fname) + else: + with open(fname, 'wb') as f: + f.write(to_binary_string('')) + fname = self.create_new_file(basedir, title, filters, create_func) + if fname is not None: + self.open([fname]) + + def new_module(self, basedir): + """New module""" + title = _("New module") + filters = _("Python scripts")+" (*.py *.pyw *.ipy)" + create_func = lambda fname: self.parent_widget.create_module.emit(fname) + self.create_new_file(basedir, title, filters, create_func) + + def go_to_parent_directory(self): + pass + + #----- VCS actions + def vcs_command(self, fnames, action): + """VCS action (commit, browse)""" + try: + for path in sorted(fnames): + vcs.run_vcs_tool(path, action) + except vcs.ActionToolNotFound as error: + msg = _("For %s support, please install one of the
    " + "following tools:

    %s")\ + % (error.vcsname, ', '.join(error.tools)) + QMessageBox.critical(self, _("Error"), + _("""Unable to find external program.

    %s""") + % to_text_string(msg)) + + #----- Settings + def get_scrollbar_position(self): + """Return scrollbar positions""" + return (self.horizontalScrollBar().value(), + self.verticalScrollBar().value()) + + def set_scrollbar_position(self, position): + """Set scrollbar positions""" + # Scrollbars will be restored after the expanded state + self._scrollbar_positions = position + if self._to_be_loaded is not None and len(self._to_be_loaded) == 0: + self.restore_scrollbar_positions() + + def restore_scrollbar_positions(self): + """Restore scrollbar positions once tree is loaded""" + hor, ver = self._scrollbar_positions + self.horizontalScrollBar().setValue(hor) + self.verticalScrollBar().setValue(ver) + + def get_expanded_state(self): + """Return expanded state""" + self.save_expanded_state() + return self.__expanded_state + + def set_expanded_state(self, state): + """Set expanded state""" + self.__expanded_state = state + self.restore_expanded_state() + + def save_expanded_state(self): + """Save all items expanded state""" + model = self.model() + # If model is not installed, 'model' will be None: this happens when + # using the Project Explorer without having selected a workspace yet + if model is not None: + self.__expanded_state = [] + for idx in model.persistentIndexList(): + if self.isExpanded(idx): + self.__expanded_state.append(self.get_filename(idx)) + + def restore_directory_state(self, fname): + """Restore directory expanded state""" + root = osp.normpath(to_text_string(fname)) + if not osp.exists(root): + # Directory has been (re)moved outside Spyder + return + for basename in os.listdir(root): + path = osp.normpath(osp.join(root, basename)) + if osp.isdir(path) and path in self.__expanded_state: + self.__expanded_state.pop(self.__expanded_state.index(path)) + if self._to_be_loaded is None: + self._to_be_loaded = [] + self._to_be_loaded.append(path) + self.setExpanded(self.get_index(path), True) + if not self.__expanded_state and not is_pyqt46: + self.fsmodel.directoryLoaded.disconnect(self.restore_directory_state) + + def follow_directories_loaded(self, fname): + """Follow directories loaded during startup""" + if self._to_be_loaded is None: + return + path = osp.normpath(to_text_string(fname)) + if path in self._to_be_loaded: + self._to_be_loaded.remove(path) + if self._to_be_loaded is not None and len(self._to_be_loaded) == 0 \ + and not is_pyqt46: + self.fsmodel.directoryLoaded.disconnect( + self.follow_directories_loaded) + if self._scrollbar_positions is not None: + # The tree view need some time to render branches: + QTimer.singleShot(50, self.restore_scrollbar_positions) + + def restore_expanded_state(self): + """Restore all items expanded state""" + if self.__expanded_state is not None: + # In the old project explorer, the expanded state was a dictionnary: + if isinstance(self.__expanded_state, list) and not is_pyqt46: + self.fsmodel.directoryLoaded.connect( + self.restore_directory_state) + self.fsmodel.directoryLoaded.connect( + self.follow_directories_loaded) + + def filter_directories(self): + """Filter the directories to show""" + index = self.get_index('.spyproject') + if index is not None: + self.setRowHidden(index.row(), index.parent(), True) + +class ProxyModel(QSortFilterProxyModel): + """Proxy model: filters tree view""" + def __init__(self, parent): + super(ProxyModel, self).__init__(parent) + self.root_path = None + self.path_list = [] + self.setDynamicSortFilter(True) + + def setup_filter(self, root_path, path_list): + """Setup proxy model filter parameters""" + self.root_path = osp.normpath(to_text_string(root_path)) + self.path_list = [osp.normpath(to_text_string(p)) for p in path_list] + self.invalidateFilter() + + def sort(self, column, order=Qt.AscendingOrder): + """Reimplement Qt method""" + self.sourceModel().sort(column, order) + + def filterAcceptsRow(self, row, parent_index): + """Reimplement Qt method""" + if self.root_path is None: + return True + index = self.sourceModel().index(row, 0, parent_index) + path = osp.normpath(to_text_string(self.sourceModel().filePath(index))) + if self.root_path.startswith(path): + # This is necessary because parent folders need to be scanned + return True + else: + for p in self.path_list: + if path == p or path.startswith(p+os.sep): + return True + else: + return False + + def data(self, index, role): + """Show tooltip with full path only for the root directory""" + if role == Qt.ToolTipRole: + root_dir = self.path_list[0].split(osp.sep)[-1] + if index.data() == root_dir: + return osp.join(self.root_path, root_dir) + return QSortFilterProxyModel.data(self, index, role) + +class FilteredDirView(DirView): + """Filtered file/directory tree view""" + def __init__(self, parent=None): + super(FilteredDirView, self).__init__(parent) + self.proxymodel = None + self.setup_proxy_model() + self.root_path = None + + #---- Model + def setup_proxy_model(self): + """Setup proxy model""" + self.proxymodel = ProxyModel(self) + self.proxymodel.setSourceModel(self.fsmodel) + + def install_model(self): + """Install proxy model""" + if self.root_path is not None: + self.setModel(self.proxymodel) + + def set_root_path(self, root_path): + """Set root path""" + self.root_path = root_path + self.install_model() + index = self.fsmodel.setRootPath(root_path) + self.setRootIndex(self.proxymodel.mapFromSource(index)) + + def get_index(self, filename): + """Return index associated with filename""" + index = self.fsmodel.index(filename) + if index.isValid() and index.model() is self.fsmodel: + return self.proxymodel.mapFromSource(index) + + def set_folder_names(self, folder_names): + """Set folder names""" + assert self.root_path is not None + path_list = [osp.join(self.root_path, dirname) + for dirname in folder_names] + self.proxymodel.setup_filter(self.root_path, path_list) + + def get_filename(self, index): + """Return filename from index""" + if index: + path = self.fsmodel.filePath(self.proxymodel.mapToSource(index)) + return osp.normpath(to_text_string(path)) + + def setup_project_view(self): + """Setup view for projects""" + for i in [1, 2, 3]: + self.hideColumn(i) + self.setHeaderHidden(True) + # Disable the view of .spyproject. + self.filter_directories() + +class ExplorerTreeWidget(DirView): + """File/directory explorer tree widget + show_cd_only: Show current directory only + (True/False: enable/disable the option + None: enable the option and do not allow the user to disable it)""" + set_previous_enabled = Signal(bool) + set_next_enabled = Signal(bool) + + def __init__(self, parent=None, show_cd_only=None): + DirView.__init__(self, parent) + + self.history = [] + self.histindex = None + + self.show_cd_only = show_cd_only + self.__original_root_index = None + self.__last_folder = None + + self.menu = None + self.common_actions = None + + # Enable drag events + self.setDragEnabled(True) + + #---- Context menu + def setup_common_actions(self): + """Setup context menu common actions""" + actions = super(ExplorerTreeWidget, self).setup_common_actions() + if self.show_cd_only is None: + # Enabling the 'show current directory only' option but do not + # allow the user to disable it + self.show_cd_only = True + else: + # Show current directory only + cd_only_action = create_action(self, + _("Show current directory only"), + toggled=self.toggle_show_cd_only) + cd_only_action.setChecked(self.show_cd_only) + self.toggle_show_cd_only(self.show_cd_only) + actions.append(cd_only_action) + return actions + + @Slot(bool) + def toggle_show_cd_only(self, checked): + """Toggle show current directory only mode""" + self.parent_widget.sig_option_changed.emit('show_cd_only', checked) + self.show_cd_only = checked + if checked: + if self.__last_folder is not None: + self.set_current_folder(self.__last_folder) + elif self.__original_root_index is not None: + self.setRootIndex(self.__original_root_index) + + #---- Refreshing widget + def set_current_folder(self, folder): + """Set current folder and return associated model index""" + index = self.fsmodel.setRootPath(folder) + self.__last_folder = folder + if self.show_cd_only: + if self.__original_root_index is None: + self.__original_root_index = self.rootIndex() + self.setRootIndex(index) + return index + + def refresh(self, new_path=None, force_current=False): + """Refresh widget + force=False: won't refresh widget if path has not changed""" + if new_path is None: + new_path = getcwd() + if force_current: + index = self.set_current_folder(new_path) + self.expand(index) + self.setCurrentIndex(index) + self.set_previous_enabled.emit( + self.histindex is not None and self.histindex > 0) + self.set_next_enabled.emit(self.histindex is not None and \ + self.histindex < len(self.history)-1) + # Disable the view of .spyproject. + self.filter_directories() + + #---- Events + def directory_clicked(self, dirname): + """Directory was just clicked""" + self.chdir(directory=dirname) + + #---- Files/Directories Actions + @Slot() + def go_to_parent_directory(self): + """Go to parent directory""" + self.chdir( osp.abspath(osp.join(getcwd(), os.pardir)) ) + + @Slot() + def go_to_previous_directory(self): + """Back to previous directory""" + self.histindex -= 1 + self.chdir(browsing_history=True) + + @Slot() + def go_to_next_directory(self): + """Return to next directory""" + self.histindex += 1 + self.chdir(browsing_history=True) + + def update_history(self, directory): + """Update browse history""" + directory = osp.abspath(to_text_string(directory)) + if directory in self.history: + self.histindex = self.history.index(directory) + + def chdir(self, directory=None, browsing_history=False): + """Set directory as working directory""" + if directory is not None: + directory = osp.abspath(to_text_string(directory)) + if browsing_history: + directory = self.history[self.histindex] + elif directory in self.history: + self.histindex = self.history.index(directory) + else: + if self.histindex is None: + self.history = [] + else: + self.history = self.history[:self.histindex+1] + if len(self.history) == 0 or \ + (self.history and self.history[-1] != directory): + self.history.append(directory) + self.histindex = len(self.history)-1 + directory = to_text_string(directory) + if PY2: + PermissionError = OSError + try: + os.chdir(directory) + self.parent_widget.open_dir.emit(directory) + self.refresh(new_path=directory, force_current=True) + except PermissionError: + QMessageBox.critical(self.parent_widget, "Error", + _("You don't have the right permissions to " + "open this directory")) + + +class ExplorerWidget(QWidget): + """Explorer widget""" + sig_option_changed = Signal(str, object) + sig_open_file = Signal(str) + sig_new_file = Signal(str) + redirect_stdio = Signal(bool) + open_dir = Signal(str) + + def __init__(self, parent=None, name_filters=['*.py', '*.pyw'], + show_all=False, show_cd_only=None, show_icontext=True): + QWidget.__init__(self, parent) + + # Widgets + self.treewidget = ExplorerTreeWidget(self, show_cd_only=show_cd_only) + button_previous = QToolButton(self) + button_next = QToolButton(self) + button_parent = QToolButton(self) + self.button_menu = QToolButton(self) + menu = QMenu(self) + + self.action_widgets = [button_previous, button_next, button_parent, + self.button_menu] + + # Actions + icontext_action = create_action(self, _("Show icons and text"), + toggled=self.toggle_icontext) + previous_action = create_action(self, text=_("Previous"), + icon=ima.icon('ArrowBack'), + triggered=self.treewidget.go_to_previous_directory) + next_action = create_action(self, text=_("Next"), + icon=ima.icon('ArrowForward'), + triggered=self.treewidget.go_to_next_directory) + parent_action = create_action(self, text=_("Parent"), + icon=ima.icon('ArrowUp'), + triggered=self.treewidget.go_to_parent_directory) + options_action = create_action(self, text='', tip=_('Options')) + + # Setup widgets + self.treewidget.setup(name_filters=name_filters, show_all=show_all) + self.treewidget.chdir(getcwd()) + self.treewidget.common_actions += [None, icontext_action] + + button_previous.setDefaultAction(previous_action) + previous_action.setEnabled(False) + + button_next.setDefaultAction(next_action) + next_action.setEnabled(False) + + button_parent.setDefaultAction(parent_action) + + self.button_menu.setIcon(ima.icon('tooloptions')) + self.button_menu.setPopupMode(QToolButton.InstantPopup) + self.button_menu.setMenu(menu) + add_actions(menu, self.treewidget.common_actions) + options_action.setMenu(menu) + + self.toggle_icontext(show_icontext) + icontext_action.setChecked(show_icontext) + + for widget in self.action_widgets: + widget.setAutoRaise(True) + widget.setIconSize(QSize(16, 16)) + + # Layouts + blayout = QHBoxLayout() + blayout.addWidget(button_previous) + blayout.addWidget(button_next) + blayout.addWidget(button_parent) + blayout.addStretch() + blayout.addWidget(self.button_menu) + + layout = QVBoxLayout() + layout.addLayout(blayout) + layout.addWidget(self.treewidget) + self.setLayout(layout) + + # Signals and slots + self.treewidget.set_previous_enabled.connect( + previous_action.setEnabled) + self.treewidget.set_next_enabled.connect(next_action.setEnabled) + + @Slot(bool) + def toggle_icontext(self, state): + """Toggle icon text""" + self.sig_option_changed.emit('show_icontext', state) + for widget in self.action_widgets: + if widget is not self.button_menu: + if state: + widget.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + else: + widget.setToolButtonStyle(Qt.ToolButtonIconOnly) + + +#============================================================================== +# Tests +#============================================================================== +class FileExplorerTest(QWidget): + def __init__(self): + QWidget.__init__(self) + vlayout = QVBoxLayout() + self.setLayout(vlayout) + self.explorer = ExplorerWidget(self, show_cd_only=None) + vlayout.addWidget(self.explorer) + + hlayout1 = QHBoxLayout() + vlayout.addLayout(hlayout1) + label = QLabel("Open file:") + label.setAlignment(Qt.AlignRight) + hlayout1.addWidget(label) + self.label1 = QLabel() + hlayout1.addWidget(self.label1) + self.explorer.sig_open_file.connect(self.label1.setText) + + hlayout2 = QHBoxLayout() + vlayout.addLayout(hlayout2) + label = QLabel("Open dir:") + label.setAlignment(Qt.AlignRight) + hlayout2.addWidget(label) + self.label2 = QLabel() + hlayout2.addWidget(self.label2) + self.explorer.open_dir.connect(self.label2.setText) + + hlayout3 = QHBoxLayout() + vlayout.addLayout(hlayout3) + label = QLabel("Option changed:") + label.setAlignment(Qt.AlignRight) + hlayout3.addWidget(label) + self.label3 = QLabel() + hlayout3.addWidget(self.label3) + self.explorer.sig_option_changed.connect( + lambda x, y: self.label3.setText('option_changed: %r, %r' % (x, y))) + self.explorer.open_dir.connect( + lambda: self.explorer.treewidget.refresh('..')) + + +class ProjectExplorerTest(QWidget): + def __init__(self, parent=None): + QWidget.__init__(self, parent) + vlayout = QVBoxLayout() + self.setLayout(vlayout) + self.treewidget = FilteredDirView(self) + self.treewidget.setup_view() + self.treewidget.set_root_path(osp.dirname(osp.abspath(__file__))) + self.treewidget.set_folder_names(['variableexplorer']) + self.treewidget.setup_project_view() + vlayout.addWidget(self.treewidget) + + +def test(file_explorer): + from spyder.utils.qthelpers import qapplication + app = qapplication() + if file_explorer: + test = FileExplorerTest() + else: + test = ProjectExplorerTest() + test.resize(640, 480) + test.show() + app.exec_() + + +if __name__ == "__main__": + test(file_explorer=True) + test(file_explorer=False) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/externalshell/baseshell.py spyder-3.0.2+dfsg1/spyder/widgets/externalshell/baseshell.py --- spyder-2.3.8+dfsg1/spyder/widgets/externalshell/baseshell.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/externalshell/baseshell.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,318 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +from time import time, strftime, gmtime +import os.path as osp + +# Third party imports +from qtpy.QtCore import (QByteArray, QProcess, Qt, QTextCodec, QTimer, + Signal, Slot, QMutex) +from qtpy.QtWidgets import (QHBoxLayout, QInputDialog, QLabel, QLineEdit, + QMenu, QToolButton, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import _, get_conf_path +from spyder.py3compat import to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import (add_actions, create_action, + create_toolbutton) + + +LOCALE_CODEC = QTextCodec.codecForLocale() + + +#TODO: code refactoring/cleaning (together with systemshell.py and pythonshell.py) +class ExternalShellBase(QWidget): + """External Shell widget: execute Python script in a separate process""" + SHELL_CLASS = None + redirect_stdio = Signal(bool) + sig_finished = Signal() + + def __init__(self, parent=None, fname=None, wdir=None, + history_filename=None, show_icontext=True, + light_background=True, menu_actions=None, + show_buttons_inside=True, show_elapsed_time=True): + QWidget.__init__(self, parent) + + self.menu_actions = menu_actions + self.write_lock = QMutex() + self.buffer_lock = QMutex() + self.buffer = [] + + self.run_button = None + self.kill_button = None + self.options_button = None + self.icontext_action = None + + self.show_elapsed_time = show_elapsed_time + + self.fname = fname + if wdir is None: + wdir = osp.dirname(osp.abspath(fname)) + self.wdir = wdir if osp.isdir(wdir) else None + self.arguments = "" + + self.shell = self.SHELL_CLASS(parent, get_conf_path(history_filename)) + self.shell.set_light_background(light_background) + self.shell.execute.connect(self.send_to_process) + self.shell.sig_keyboard_interrupt.connect(self.keyboard_interrupt) + # Redirecting some SIGNALs: + self.shell.redirect_stdio.connect( + lambda state: self.redirect_stdio.emit(state)) + + self.state_label = None + self.time_label = None + + vlayout = QVBoxLayout() + toolbar_buttons = self.get_toolbar_buttons() + if show_buttons_inside: + self.state_label = QLabel() + hlayout = QHBoxLayout() + hlayout.addWidget(self.state_label) + hlayout.addStretch(0) + hlayout.addWidget(self.create_time_label()) + hlayout.addStretch(0) + for button in toolbar_buttons: + hlayout.addWidget(button) + vlayout.addLayout(hlayout) + else: + vlayout.setContentsMargins(0, 0, 0, 0) + vlayout.addWidget(self.get_shell_widget()) + self.setLayout(vlayout) + self.resize(640, 480) + if parent is None: + self.setWindowIcon(self.get_icon()) + self.setWindowTitle(_("Console")) + + self.t0 = None + self.timer = QTimer(self) + + self.process = None + + self.is_closing = False + + if show_buttons_inside: + self.update_time_label_visibility() + + @Slot(bool) + def set_elapsed_time_visible(self, state): + self.show_elapsed_time = state + if self.time_label is not None: + self.time_label.setVisible(state) + + def create_time_label(self): + """Create elapsed time label widget (if necessary) and return it""" + if self.time_label is None: + self.time_label = QLabel() + return self.time_label + + def update_time_label_visibility(self): + self.time_label.setVisible(self.show_elapsed_time) + + def is_running(self): + if self.process is not None: + return self.process.state() == QProcess.Running + + def get_toolbar_buttons(self): + if self.run_button is None: + self.run_button = create_toolbutton(self, text=_("Run"), + icon=ima.icon('run'), + tip=_("Run again this program"), + triggered=self.start_shell) + if self.kill_button is None: + self.kill_button = create_toolbutton(self, text=_("Kill"), + icon=ima.icon('kill'), + tip=_("Kills the current process, " + "causing it to exit immediately")) + buttons = [self.run_button] + if self.options_button is None: + options = self.get_options_menu() + if options: + self.options_button = create_toolbutton(self, text=_('Options'), + icon=ima.icon('tooloptions')) + self.options_button.setPopupMode(QToolButton.InstantPopup) + menu = QMenu(self) + add_actions(menu, options) + self.options_button.setMenu(menu) + if self.options_button is not None: + buttons.append(self.options_button) + buttons.append(self.kill_button) + return buttons + + def set_icontext_visible(self, state): + """Set icon text visibility""" + for widget in self.get_toolbar_buttons(): + if state: + widget.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + else: + widget.setToolButtonStyle(Qt.ToolButtonIconOnly) + + def get_options_menu(self): + self.show_time_action = create_action(self, _("Show elapsed time"), + toggled=self.set_elapsed_time_visible) + self.show_time_action.setChecked(self.show_elapsed_time) + actions = [self.show_time_action] + if self.menu_actions is not None: + actions += [None]+self.menu_actions + return actions + + def get_shell_widget(self): + return self.shell + + def get_icon(self): + raise NotImplementedError + + def show_time(self, end=False): + if self.time_label is None: + return + elapsed_time = time()-self.t0 + if elapsed_time > 24*3600: # More than a day...! + format = "%d %H:%M:%S" + else: + format = "%H:%M:%S" + if end: + color = "#AAAAAA" + else: + color = "#AA6655" + text = "%s" \ + "" % (color, strftime(format, gmtime(elapsed_time))) + self.time_label.setText(text) + + def closeEvent(self, event): + if self.process is not None: + self.is_closing = True + self.process.kill() + self.process.waitForFinished(100) + + try: + self.timer.timeout.disconnect(self.show_time) + except (RuntimeError, TypeError): + pass + + def set_running_state(self, state=True): + self.set_buttons_runnning_state(state) + self.shell.setReadOnly(not state) + if state: + if self.state_label is not None: + self.state_label.setText(_( + "Running...")) + self.t0 = time() + self.timer.timeout.connect(self.show_time) + self.timer.start(1000) + else: + if self.state_label is not None: + self.state_label.setText(_('Terminated.')) + try: + self.timer.timeout.disconnect(self.show_time) + except (RuntimeError, TypeError): + pass + + def set_buttons_runnning_state(self, state): + self.run_button.setVisible(not state) + self.kill_button.setVisible(state) + + @Slot(bool) + def start_shell(self, ask_for_arguments=False): + """Start shell""" + if ask_for_arguments and not self.get_arguments(): + self.set_running_state(False) + return + try: + self.terminate_button.clicked.disconnect(self.process.terminate) + self.kill_button.clicked.disconnect(self.process.terminate) + except (AttributeError, RuntimeError, TypeError): + pass + self.create_process() + + @Slot() + def get_arguments(self): + arguments, valid = QInputDialog.getText(self, _('Arguments'), + _('Command line arguments:'), + QLineEdit.Normal, + self.arguments) + if valid: + self.arguments = to_text_string(arguments) + return valid + + def create_process(self): + raise NotImplementedError + + def finished(self, exit_code, exit_status): + self.shell.flush() + self.sig_finished.emit() + if self.is_closing: + return + self.set_running_state(False) + self.show_time(end=True) + +#=============================================================================== +# Input/Output +#=============================================================================== + def transcode(self, qba): + try: + return to_text_string(qba.data(), 'utf8') + except UnicodeDecodeError: + return qba.data() + + def get_stdout(self): + self.process.setReadChannel(QProcess.StandardOutput) + qba = QByteArray() + while self.process.bytesAvailable(): + qba += self.process.readAllStandardOutput() + return self.transcode(qba) + + def get_stderr(self): + self.process.setReadChannel(QProcess.StandardError) + qba = QByteArray() + while self.process.bytesAvailable(): + qba += self.process.readAllStandardError() + return self.transcode(qba) + + def write_output(self): + # if we are already writing something else, + # store the present message in a buffer + if not self.write_lock.tryLock(): + self.buffer_lock.lock() + self.buffer.append(self.get_stdout()) + self.buffer_lock.unlock() + + if not self.write_lock.tryLock(): + return + + self.shell.write(self.get_stdout(), flush=True) + + while True: + self.buffer_lock.lock() + messages = self.buffer + self.buffer = [] + + if not messages: + self.write_lock.unlock() + self.buffer_lock.unlock() + return + + self.buffer_lock.unlock() + self.shell.write("\n".join(messages), flush=True) + + def send_to_process(self, qstr): + raise NotImplementedError + + def send_ctrl_to_process(self, letter): + char = chr("abcdefghijklmnopqrstuvwxyz".index(letter) + 1) + byte_array = QByteArray() + byte_array.append(char) + self.process.write(byte_array) + self.process.waitForBytesWritten(-1) + self.shell.write(LOCALE_CODEC.toUnicode(byte_array), flush=True) + + def keyboard_interrupt(self): + raise NotImplementedError diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/externalshell/__init__.py spyder-3.0.2+dfsg1/spyder/widgets/externalshell/__init__.py --- spyder-2.3.8+dfsg1/spyder/widgets/externalshell/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/externalshell/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.widgets.externalshell +============================ + +External Shell widget: execute Python script/terminal in a separate process +""" diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/externalshell/introspection.py spyder-3.0.2+dfsg1/spyder/widgets/externalshell/introspection.py --- spyder-2.3.8+dfsg1/spyder/widgets/externalshell/introspection.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/externalshell/introspection.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""External shell's introspection and notification servers""" + +# Standard library imports +import errno +import os +import socket +import threading + +# Third party imports +from qtpy.QtCore import QThread, Signal + +# Local imports +from spyder.config.base import get_conf_path, DEBUG +from spyder.utils.debug import log_last_error +from spyder.utils.bsdsocket import read_packet, write_packet +from spyder.utils.misc import select_port + + +LOG_FILENAME = get_conf_path('introspection.log') + +DEBUG_INTROSPECTION = DEBUG >= 2 + +if DEBUG_INTROSPECTION: + import logging + logging.basicConfig(filename=get_conf_path('introspection_debug.log'), + level=logging.DEBUG) + +SPYDER_PORT = 20128 + + +class IntrospectionServer(threading.Thread): + """Introspection server""" + def __init__(self): + threading.Thread.__init__(self) + self.shells = {} + self.setDaemon(True) + global SPYDER_PORT + self.port = SPYDER_PORT = select_port(default_port=SPYDER_PORT) + SPYDER_PORT += 1 + + def register(self, shell): + """Register introspection server + See notification server below""" + shell_id = str(id(shell)) + self.shells[shell_id] = shell + + def send_socket(self, shell_id, sock): + """Send socket to the appropriate object for later communication""" + shell = self.shells[shell_id] + shell.set_introspection_socket(sock) + if DEBUG_INTROSPECTION: + logging.debug('Introspection server: shell [%r] port [%r]' + % (shell, self.port)) + + def run(self): + """Start server""" + sock = socket.socket(socket.AF_INET) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind( ("127.0.0.1", self.port) ) + + while True: + sock.listen(2) + try: + conn, _addr = sock.accept() + except socket.error as e: + # See Issue 1275 for details on why errno EINTR is + # silently ignored here. + eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR + if e.args[0] == eintr: + continue + raise + shell_id = read_packet(conn) + if shell_id is not None: + self.send_socket(shell_id, conn) + +class NotificationServer(IntrospectionServer): + """Notification server""" + def __init__(self): + IntrospectionServer.__init__(self) + self.notification_threads = {} + + def register(self, shell): + """Register notification server + See pythonshell.ExternalPythonShell.create_process""" + IntrospectionServer.register(self, shell) + shell_id = str(id(shell)) + n_thread = self.notification_threads[shell_id] = NotificationThread() + return n_thread + + def send_socket(self, shell_id, sock): + """Send socket to the appropriate object for later communication""" + n_thread = self.notification_threads[shell_id] + n_thread.set_notify_socket(sock) + n_thread.start() + if DEBUG_INTROSPECTION: + logging.debug('Notification server: shell [%r] port [%r]' + % (self.shells[shell_id], self.port)) + +INTROSPECTION_SERVER = None + +def start_introspection_server(): + """ + Start introspection server (only one time) + This server is dedicated to introspection features, i.e. Spyder is calling + it to retrieve informations on remote objects + """ + global INTROSPECTION_SERVER + if INTROSPECTION_SERVER is None: + if DEBUG_INTROSPECTION: + import time + time_str = "Logging time: %s" % time.ctime(time.time()) + logging.debug("="*len(time_str)) + logging.debug(time_str) + logging.debug("="*len(time_str)) + INTROSPECTION_SERVER = IntrospectionServer() + INTROSPECTION_SERVER.start() + return INTROSPECTION_SERVER + +NOTIFICATION_SERVER = None + +def start_notification_server(): + """ + Start notify server (only one time) + This server is dedicated to notification features, i.e. remote objects + are notifying Spyder about anything relevant like debugging data (pdb) + or "this is the right moment to refresh variable explorer" (syshook) + """ + global NOTIFICATION_SERVER + if NOTIFICATION_SERVER is None: + NOTIFICATION_SERVER = NotificationServer() + NOTIFICATION_SERVER.start() + return NOTIFICATION_SERVER + + +class NotificationThread(QThread): + """Notification thread""" + sig_process_remote_view = Signal(object) + sig_pdb = Signal(str, int) + open_file = Signal(str, int) + refresh_namespace_browser = Signal() + + def __init__(self): + QThread.__init__(self) + self.notify_socket = None + + def set_notify_socket(self, notify_socket): + """Set the notification socket""" + self.notify_socket = notify_socket + + def run(self): + """Start notification thread""" + while True: + if self.notify_socket is None: + continue + output = None + try: + try: + cdict = read_packet(self.notify_socket) + except: + # This except statement is intended to handle a struct.error + # (but when writing 'except struct.error', it doesn't work) + # Note: struct.error is raised when the communication has + # been interrupted and the received data is not a string + # of length 8 as required by struct.unpack (see read_packet) + break + if cdict is None: + # Another notification thread has just terminated and + # then wrote 'None' in the notification socket + # (see the 'finally' statement below) + continue + if not isinstance(cdict, dict): + raise TypeError("Invalid data type: %r" % cdict) + command = cdict['command'] + data = cdict.get('data') + if command == 'pdb_step': + fname, lineno = data + self.sig_pdb.emit(fname, lineno) + self.refresh_namespace_browser.emit() + elif command == 'refresh': + self.refresh_namespace_browser.emit() + elif command == 'remote_view': + self.sig_process_remote_view.emit(data) + elif command == 'open_file': + fname, lineno = data + self.open_file.emit(fname, lineno) + else: + raise RuntimeError('Unsupported command: %r' % command) + if DEBUG_INTROSPECTION: + logging.debug("received command: %r" % command) + except: + log_last_error(LOG_FILENAME, "notification thread") + finally: + try: + write_packet(self.notify_socket, output) + except: + # The only reason why it should fail is that Spyder is + # closing while this thread is still alive + break diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/externalshell/monitor.py spyder-3.0.2+dfsg1/spyder/widgets/externalshell/monitor.py --- spyder-2.3.8+dfsg1/spyder/widgets/externalshell/monitor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/externalshell/monitor.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,521 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""External shell's monitor""" + +#TODO: The "disable auto-refresh when variable explorer is hidden" feature +# broken since we removed the "shell" widget reference from notification +# thread. We must find another mechanism to avoid refreshing systematically +# remote views for all consoles...! + +import os +import socket +import struct +import threading + +# Local imports +from spyder.config.base import get_conf_path, DEBUG +from spyder.py3compat import getcwd, is_text_string, pickle, _thread +from spyder.utils.misc import fix_reference_name +from spyder.utils.debug import log_last_error +from spyder.utils.dochelpers import (getargtxt, getdoc, getsource, + getobjdir, isdefined) +from spyder.utils.bsdsocket import (communicate, read_packet, write_packet, + PACKET_NOT_RECEIVED, PICKLE_HIGHEST_PROTOCOL) +from spyder.utils.introspection.module_completion import module_completion +from spyder.widgets.variableexplorer.utils import (get_remote_data, + make_remote_view) + + +LOG_FILENAME = get_conf_path('monitor.log') +DEBUG_MONITOR = DEBUG >= 2 +if DEBUG_MONITOR: + import logging + logging.basicConfig(filename=get_conf_path('monitor_debug.log'), + level=logging.DEBUG) + + +def monitor_save_globals(sock, settings, filename): + """Save globals() to file""" + return communicate(sock, '__save_globals__()', + settings=[settings, filename]) + +def monitor_load_globals(sock, filename, ext): + """Load globals() from file""" + return communicate(sock, '__load_globals__()', settings=[filename, ext]) + +def monitor_get_global(sock, name): + """Get global variable *name* value""" + return communicate(sock, '__get_global__("%s")' % name) + +def monitor_set_global(sock, name, value): + """Set global variable *name* value to *value*""" + return communicate(sock, '__set_global__("%s")' % name, + settings=[value]) + +def monitor_del_global(sock, name): + """Del global variable *name*""" + return communicate(sock, '__del_global__("%s")' % name) + +def monitor_copy_global(sock, orig_name, new_name): + """Copy global variable *orig_name* to *new_name*""" + return communicate(sock, '__copy_global__("%s", "%s")' \ + % (orig_name, new_name)) + +def _getcdlistdir(): + """Return current directory list dir""" + return os.listdir(getcwd()) + + +class Monitor(threading.Thread): + """Monitor server""" + def __init__(self, host, introspection_port, notification_port, + shell_id, timeout, auto_refresh): + threading.Thread.__init__(self) + self.setDaemon(True) + + self.pdb_obj = None + + self.timeout = None + self.set_timeout(timeout) + self.auto_refresh = auto_refresh + self.refresh_after_eval = False + self.remote_view_settings = None + + self.inputhook_flag = False + self.first_inputhook_call = True + + # Connecting to introspection server + self.i_request = socket.socket( socket.AF_INET ) + self.i_request.connect( (host, introspection_port) ) + write_packet(self.i_request, shell_id) + + # Connecting to notification server + self.n_request = socket.socket( socket.AF_INET ) + self.n_request.connect( (host, notification_port) ) + write_packet(self.n_request, shell_id) + + self._mlocals = { + "refresh": self.enable_refresh_after_eval, + "setlocal": self.setlocal, + "is_array": self.is_array, + "is_image": self.is_image, + "get_globals_keys": self.get_globals_keys, + "getmodcomplist": self.getmodcomplist, + "getcdlistdir": _getcdlistdir, + "getcwd": self.getcwd, + "setcwd": self.setcwd, + "getsyspath": self.getsyspath, + "getenv": self.getenv, + "setenv": self.setenv, + "isdefined": self.isdefined, + "thread": _thread, + "toggle_inputhook_flag": self.toggle_inputhook_flag, + "set_monitor_timeout": self.set_timeout, + "set_monitor_auto_refresh": self.set_auto_refresh, + "set_remote_view_settings": + self.set_remote_view_settings, + "set_spyder_breakpoints": self.set_spyder_breakpoints, + "__get_dir__": self.get_dir, + "__iscallable__": self.iscallable, + "__get_arglist__": self.get_arglist, + "__get__doc____": self.get__doc__, + "__get_doc__": self.get_doc, + "__get_source__": self.get_source, + "__get_global__": self.getglobal, + "__set_global__": self.setglobal, + "__del_global__": self.delglobal, + "__copy_global__": self.copyglobal, + "__save_globals__": self.saveglobals, + "__load_globals__": self.loadglobals, + "_" : None} + self._mglobals = None + + @property + def pdb_frame(self): + """Return current Pdb frame if there is any""" + if self.pdb_obj is not None and self.pdb_obj.curframe is not None: + return self.pdb_obj.curframe + + @property + def pdb_locals(self): + """Return current Pdb frame locals if available + Otherwise return an empty dictionary""" + if self.pdb_frame: + return self.pdb_obj.curframe_locals + else: + return {} + + def mlocals(self): + """Return current locals -- handles Pdb frames""" + ns = {} + ns.update(self._mlocals) + ns.update(self.pdb_locals) + return ns + + def mglobals(self): + """Return current globals -- handles Pdb frames""" + if self.pdb_frame is not None: + return self.pdb_frame.f_globals + else: + if self._mglobals is None: + from __main__ import __dict__ as glbs + self._mglobals = glbs + else: + glbs = self._mglobals + self._mglobals = glbs + return glbs + + def get_current_namespace(self): + """Return current namespace, i.e. globals() if not debugging, + or a dictionary containing both locals() and globals() + for current frame when debugging""" + ns = {} + glbs = self.mglobals() + + if self.pdb_frame is None: + ns.update(glbs) + else: + ns.update(glbs) + ns.update(self.pdb_locals) + + return ns + + def get_reference_namespace(self, name): + """Return namespace where reference name is defined, + eventually returns the globals() if reference has not yet been defined""" + glbs = self.mglobals() + if self.pdb_frame is None: + return glbs + else: + lcls = self.pdb_locals + if name in lcls: + return lcls + else: + return glbs + + def get_globals_keys(self): + """Return globals() keys or globals() and locals() keys if debugging""" + ns = self.get_current_namespace() + return list(ns.keys()) + + def isdefined(self, obj, force_import=False): + """Return True if object is defined in current namespace""" + ns = self.get_current_namespace() + return isdefined(obj, force_import=force_import, namespace=ns) + + def toggle_inputhook_flag(self, state): + """Toggle the input hook flag + + The only purpose of this flag is to unblock the PyOS_InputHook + callback when text is available in stdin (see sitecustomize.py)""" + self.inputhook_flag = state + + def set_timeout(self, timeout): + """Set monitor timeout (in milliseconds!)""" + self.timeout = float(timeout)/1000. + + def set_auto_refresh(self, state): + """Enable/disable namespace browser auto refresh feature""" + self.auto_refresh = state + + def enable_refresh_after_eval(self): + self.refresh_after_eval = True + + #------ Notifications + def refresh(self): + """Refresh variable explorer in ExternalPythonShell""" + communicate(self.n_request, dict(command="refresh")) + + def refresh_from_inputhook(self): + """Refresh variable explorer from the PyOS_InputHook. + See sitecustomize.py""" + # Refreshing variable explorer, except on first input hook call + # (otherwise, on slow machines, this may freeze Spyder) + if self.first_inputhook_call: + self.first_inputhook_call = False + else: + self.refresh() + + def register_pdb_session(self, pdb_obj): + self.pdb_obj = pdb_obj + + def notify_pdb_step(self, fname, lineno): + """Notify the ExternalPythonShell regarding pdb current frame""" + communicate(self.n_request, + dict(command="pdb_step", data=(fname, lineno))) + + def set_spyder_breakpoints(self): + """Set all Spyder breakpoints in active pdb session""" + if not self.pdb_obj: + return + self.pdb_obj.set_spyder_breakpoints() + + def notify_open_file(self, fname, lineno=1): + """Open file in Spyder's editor""" + communicate(self.n_request, + dict(command="open_file", data=(fname, lineno))) + + #------ Code completion / Calltips + def _eval(self, text): + """ + Evaluate text and return (obj, valid) + where *obj* is the object represented by *text* + and *valid* is True if object evaluation did not raise any exception + """ + assert is_text_string(text) + ns = self.get_current_namespace() + try: + return eval(text, ns), True + except: + return None, False + + def get_dir(self, objtxt): + """Return dir(object)""" + obj, valid = self._eval(objtxt) + if valid: + return getobjdir(obj) + + def iscallable(self, objtxt): + """Is object callable?""" + obj, valid = self._eval(objtxt) + if valid: + return callable(obj) + + def get_arglist(self, objtxt): + """Get func/method argument list""" + obj, valid = self._eval(objtxt) + if valid: + return getargtxt(obj) + + def get__doc__(self, objtxt): + """Get object __doc__""" + obj, valid = self._eval(objtxt) + if valid: + return obj.__doc__ + + def get_doc(self, objtxt): + """Get object documentation dictionary""" + obj, valid = self._eval(objtxt) + if valid: + return getdoc(obj) + + def get_source(self, objtxt): + """Get object source""" + obj, valid = self._eval(objtxt) + if valid: + return getsource(obj) + + def getmodcomplist(self, name, path): + """Return module completion list for object named *name*""" + return module_completion(name, path) + + #------ Other + def is_array(self, name): + """Return True if object is an instance of class numpy.ndarray""" + ns = self.get_current_namespace() + try: + import numpy + return isinstance(ns[name], numpy.ndarray) + except ImportError: + return False + + def is_image(self, name): + """Return True if object is an instance of class PIL.Image.Image""" + ns = self.get_current_namespace() + try: + from spyder.pil_patch import Image + return isinstance(ns[name], Image.Image) + except ImportError: + return False + + def getcwd(self): + """Return current working directory""" + return getcwd() + + def setcwd(self, dirname): + """Set current working directory""" + return os.chdir(dirname) + + def getenv(self): + """Return os.environ""" + return os.environ.copy() + + def setenv(self): + """Set os.environ""" + env = read_packet(self.i_request) + os.environ = env + + def getsyspath(self): + """Return sys.path[:]""" + import sys + return sys.path[:] + + def setlocal(self, name, value): + """ + Set local reference value + Not used right now - could be useful in the future + """ + self._mlocals[name] = value + + def set_remote_view_settings(self): + """ + Set the namespace remote view settings + (see the namespace browser widget) + """ + self.remote_view_settings = read_packet(self.i_request) + self.enable_refresh_after_eval() + + def update_remote_view(self): + """ + Return remote view of globals() + """ + settings = self.remote_view_settings + if settings: + ns = self.get_current_namespace() + remote_view = make_remote_view(ns, settings) + communicate(self.n_request, + dict(command="remote_view", data=remote_view)) + + def saveglobals(self): + """Save globals() into filename""" + ns = self.get_current_namespace() + from spyder.utils.iofuncs import iofunctions + settings = read_packet(self.i_request) + filename = read_packet(self.i_request) + data = get_remote_data(ns, settings, mode='picklable').copy() + return iofunctions.save(data, filename) + + def loadglobals(self): + """Load globals() from filename""" + glbs = self.mglobals() + from spyder.utils.iofuncs import iofunctions + filename = read_packet(self.i_request) + ext = read_packet(self.i_request) + load_func = iofunctions.load_funcs[ext] + data, error_message = load_func(filename) + if error_message: + return error_message + for key in list(data.keys()): + new_key = fix_reference_name(key, blacklist=list(glbs.keys())) + if new_key != key: + data[new_key] = data.pop(key) + try: + glbs.update(data) + except Exception as error: + return str(error) + self.refresh_after_eval = True + + def getglobal(self, name): + """ + Get global reference value + """ + ns = self.get_current_namespace() + return ns[name] + + def setglobal(self, name): + """ + Set global reference value + """ + ns = self.get_reference_namespace(name) + ns[name] = read_packet(self.i_request) + self.refresh_after_eval = True + + def delglobal(self, name): + """ + Del global reference + """ + ns = self.get_reference_namespace(name) + ns.pop(name) + self.refresh_after_eval = True + + def copyglobal(self, orig_name, new_name): + """ + Copy global reference + """ + ns = self.get_reference_namespace(orig_name) + ns[new_name] = ns[orig_name] + self.refresh_after_eval = True + + def run(self): + while True: + output = pickle.dumps(None, PICKLE_HIGHEST_PROTOCOL) + glbs = self.mglobals() + try: + if DEBUG_MONITOR: + logging.debug("****** Introspection request /Begin ******") + command = PACKET_NOT_RECEIVED + try: + timeout = self.timeout if self.auto_refresh else None + command = read_packet(self.i_request, timeout=timeout) + if command is None: + continue + timed_out = False + except socket.timeout: + timed_out = True + except struct.error: + # This should mean that Spyder GUI has crashed + if DEBUG_MONITOR: + logging.debug("struct.error -> quitting monitor") + break + if timed_out: + if DEBUG_MONITOR: + logging.debug("connection timed out -> updating remote view") + self.update_remote_view() + if DEBUG_MONITOR: + logging.debug("****** Introspection request /End ******") + continue + if DEBUG_MONITOR: + logging.debug("command: %r" % command) + lcls = self.mlocals() + result = eval(command, glbs, lcls) + if DEBUG_MONITOR: + logging.debug(" result: %r" % result) + if self.pdb_obj is None: + lcls["_"] = result + # old com implementation: (see solution (1) in Issue 434) + output = pickle.dumps(result, PICKLE_HIGHEST_PROTOCOL) +# # new com implementation: (see solution (2) in Issue 434) +# output = pickle.dumps((command, result), +# PICKLE_HIGHEST_PROTOCOL) + except SystemExit: + break + except: + if DEBUG_MONITOR: + logging.debug("error!") + log_last_error(LOG_FILENAME, command) + finally: + try: + if DEBUG_MONITOR: + logging.debug("updating remote view") + if self.refresh_after_eval: + self.update_remote_view() + self.refresh_after_eval = False + if DEBUG_MONITOR: + logging.debug("sending result") + logging.debug("****** Introspection request /End ******") + if command is not PACKET_NOT_RECEIVED: + if write_packet is None: + # This may happen during interpreter shutdown + break + else: + write_packet(self.i_request, output, + already_pickled=True) + except AttributeError as error: + if "'NoneType' object has no attribute" in str(error): + # This may happen during interpreter shutdown + break + else: + raise + except TypeError as error: + if "'NoneType' object is not subscriptable" in str(error): + # This may happen during interpreter shutdown + break + else: + raise + + self.i_request.close() + self.n_request.close() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/externalshell/pythonshell.py spyder-3.0.2+dfsg1/spyder/widgets/externalshell/pythonshell.py --- spyder-2.3.8+dfsg1/spyder/widgets/externalshell/pythonshell.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/externalshell/pythonshell.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,663 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""External Python Shell widget: execute Python script in a separate process""" + +# Standard library imports +import os +import os.path as osp +import socket +import sys + +# Third party imports +from qtpy.compat import getexistingdirectory +from qtpy.QtCore import QProcess, QProcessEnvironment, Qt, Signal, Slot +from qtpy.QtWidgets import QApplication, QMenu, QMessageBox, QSplitter + +# Local imports +from spyder.config.base import (_, DEBUG, get_module_source_path, + MAC_APP_NAME, running_in_mac_app) +from spyder.py3compat import (is_text_string, to_binary_string, + to_text_string) +from spyder.utils import icon_manager as ima +from spyder.utils.bsdsocket import communicate, write_packet +from spyder.utils.environ import RemoteEnvDialog +from spyder.utils.misc import add_pathlist_to_PYTHONPATH, get_python_executable +from spyder.utils.programs import get_python_args +from spyder.utils.qthelpers import (add_actions, create_action, + create_toolbutton, DialogManager) +from spyder.widgets.externalshell.baseshell import ExternalShellBase +from spyder.widgets.shell import PythonShellWidget +from spyder.widgets.variableexplorer.namespacebrowser import NamespaceBrowser +from spyder.widgets.variableexplorer.collectionseditor import CollectionsEditor + + +class ExtPythonShellWidget(PythonShellWidget): + + wait_for_ready_read = Signal() + go_to_error = Signal(str) + focus_changed = Signal() + + def __init__(self, parent, history_filename, profile=False): + PythonShellWidget.__init__(self, parent, history_filename, profile) + self.path = [] + + def set_externalshell(self, externalshell): + # ExternalShellBase instance: + self.externalshell = externalshell + + def clear_terminal(self): + """Reimplement ShellBaseWidget method""" + self.clear() + self.execute.emit("\n") + + def execute_lines(self, lines): + """ + Execute a set of lines as multiple command + lines: multiple lines of text to be executed as single commands + """ + for line in lines.splitlines(): + stripped_line = line.strip() + if stripped_line.startswith('#'): + continue + self.write(line+os.linesep, flush=True) + self.execute_command(line) + # Workaround for Issue 502 + # Emmiting wait_for_ready_read was making the console hang + # in Mac OS X + if sys.platform.startswith("darwin"): + import time + time.sleep(0.025) + else: + self.wait_for_ready_read.emit() + self.flush() + + #------ Code completion / Calltips + def ask_monitor(self, command, settings=[]): + sock = self.externalshell.introspection_socket + if sock is None: + return + try: + return communicate(sock, command, settings=settings) + except socket.error: + # Process was just closed + pass + except MemoryError: + # Happens when monitor is not ready on slow machines + pass + + def get_dir(self, objtxt): + """Return dir(object)""" + return self.ask_monitor("__get_dir__('%s')" % objtxt) + + def get_globals_keys(self): + """Return shell globals() keys""" + return self.ask_monitor("get_globals_keys()") + + def get_cdlistdir(self): + """Return shell current directory list dir""" + return self.ask_monitor("getcdlistdir()") + + def iscallable(self, objtxt): + """Is object callable?""" + return self.ask_monitor("__iscallable__('%s')" % objtxt) + + def get_arglist(self, objtxt): + """Get func/method argument list""" + return self.ask_monitor("__get_arglist__('%s')" % objtxt) + + def get__doc__(self, objtxt): + """Get object __doc__""" + return self.ask_monitor("__get__doc____('%s')" % objtxt) + + def get_doc(self, objtxt): + """Get object documentation dictionary""" + return self.ask_monitor("__get_doc__('%s')" % objtxt) + + def get_source(self, objtxt): + """Get object source""" + return self.ask_monitor("__get_source__('%s')" % objtxt) + + def is_defined(self, objtxt, force_import=False): + """Return True if object is defined""" + return self.ask_monitor("isdefined('%s', force_import=%s)" + % (objtxt, force_import)) + + def get_module_completion(self, objtxt): + """Return module completion list associated to object name""" + return self.ask_monitor("getmodcomplist('%s', %s)" % \ + (objtxt, self.path)) + + def get_cwd(self): + """Return shell current working directory""" + return self.ask_monitor("getcwd()") + + def set_cwd(self, dirname): + """Set shell current working directory""" + return self.ask_monitor("setcwd(r'%s')" % dirname) + + def get_env(self): + """Return environment variables: os.environ""" + return self.ask_monitor("getenv()") + + def set_env(self, env): + """Set environment variables via os.environ""" + return self.ask_monitor('setenv()', settings=[env]) + + def get_syspath(self): + """Return sys.path[:]""" + return self.ask_monitor("getsyspath()") + + def set_spyder_breakpoints(self): + """Set Spyder breakpoints into debugging session""" + return self.ask_monitor("set_spyder_breakpoints()") + + def is_running(self): + """Check if parent is running""" + return self.parent().is_running() + + +class ExternalPythonShell(ExternalShellBase): + """External Shell widget: execute Python script in a separate process""" + SHELL_CLASS = ExtPythonShellWidget + sig_pdb = Signal(str, int) + open_file = Signal(str, int) + started = Signal() + sig_finished = Signal() + + def __init__(self, parent=None, fname=None, wdir=None, + interact=False, debug=False, post_mortem=False, + path=[], python_args='', + arguments='', stand_alone=None, + umr_enabled=True, umr_namelist=[], umr_verbose=True, + pythonstartup=None, pythonexecutable=None, + external_interpreter=False, + monitor_enabled=True, mpl_backend=None, ets_backend='qt4', + qt_api=None, merge_output_channels=False, + colorize_sys_stderr=False, autorefresh_timeout=3000, + autorefresh_state=True, light_background=True, + menu_actions=None, show_buttons_inside=True, + show_elapsed_time=True): + + assert qt_api in (None, 'pyqt', 'pyside', 'pyqt5') + + self.namespacebrowser = None # namespace browser widget! + self.dialog_manager = DialogManager() + + self.stand_alone = stand_alone # stand alone settings (None: plugin) + self.interact = interact + self.pythonstartup = pythonstartup + self.pythonexecutable = pythonexecutable + self.external_interpreter = external_interpreter + self.monitor_enabled = monitor_enabled + self.mpl_backend = mpl_backend + self.ets_backend = ets_backend + self.qt_api = qt_api + self.merge_output_channels = merge_output_channels + self.colorize_sys_stderr = colorize_sys_stderr + self.umr_enabled = umr_enabled + self.umr_namelist = umr_namelist + self.umr_verbose = umr_verbose + self.autorefresh_timeout = autorefresh_timeout + self.autorefresh_state = autorefresh_state + + self.namespacebrowser_button = None + self.cwd_button = None + self.env_button = None + self.syspath_button = None + self.terminate_button = None + + self.notification_thread = None + + ExternalShellBase.__init__(self, parent=parent, fname=fname, wdir=wdir, + history_filename='history.py', + light_background=light_background, + menu_actions=menu_actions, + show_buttons_inside=show_buttons_inside, + show_elapsed_time=show_elapsed_time) + + if self.pythonexecutable is None: + self.pythonexecutable = get_python_executable() + + self.python_args = None + if python_args: + assert is_text_string(python_args) + self.python_args = python_args + + assert is_text_string(arguments) + self.arguments = arguments + + self.connection_file = None + self.shell.set_externalshell(self) + + self.toggle_globals_explorer(False) + self.interact_action.setChecked(self.interact) + self.debug_action.setChecked(debug) + + + self.introspection_socket = None + self.is_interpreter = fname is None + + if self.is_interpreter: + self.terminate_button.hide() + + self.post_mortem_action.setChecked(post_mortem and not self.is_interpreter) + + # Additional python path list + self.path = path + self.shell.path = path + + def set_introspection_socket(self, introspection_socket): + self.introspection_socket = introspection_socket + if self.namespacebrowser is not None: + settings = self.namespacebrowser.get_view_settings() + communicate(introspection_socket, + 'set_remote_view_settings()', settings=[settings]) + + def set_autorefresh_timeout(self, interval): + if self.introspection_socket is not None: + try: + communicate(self.introspection_socket, + "set_monitor_timeout(%d)" % interval) + except socket.error: + pass + + def closeEvent(self, event): + self.quit_monitor() + ExternalShellBase.closeEvent(self, event) + + def get_toolbar_buttons(self): + ExternalShellBase.get_toolbar_buttons(self) + if self.namespacebrowser_button is None \ + and self.stand_alone is not None: + self.namespacebrowser_button = create_toolbutton(self, + text=_("Variables"), icon=ima.icon('dictedit'), + tip=_("Show/hide global variables explorer"), + toggled=self.toggle_globals_explorer, text_beside_icon=True) + if self.terminate_button is None: + self.terminate_button = create_toolbutton(self, + text=_("Terminate"), icon=ima.icon('stop'), + tip=_("Attempts to stop the process. The process\n" + "may not exit as a result of clicking this\n" + "button (it is given the chance to prompt\n" + "the user for any unsaved files, etc).")) + buttons = [] + if self.namespacebrowser_button is not None: + buttons.append(self.namespacebrowser_button) + buttons += [self.run_button, self.terminate_button, self.kill_button, + self.options_button] + return buttons + + def get_options_menu(self): + ExternalShellBase.get_options_menu(self) + self.interact_action = create_action(self, _("Interact")) + self.interact_action.setCheckable(True) + self.debug_action = create_action(self, _("Debug")) + self.debug_action.setCheckable(True) + self.args_action = create_action(self, _("Arguments..."), + triggered=self.get_arguments) + self.post_mortem_action = create_action(self, _("Post Mortem Debug")) + self.post_mortem_action.setCheckable(True) + run_settings_menu = QMenu(_("Run settings"), self) + add_actions(run_settings_menu, + (self.interact_action, self.debug_action, self.args_action, + self.post_mortem_action)) + self.cwd_button = create_action(self, _("Working directory"), + icon=ima.icon('DirOpenIcon'), + tip=_("Set current working directory"), + triggered=self.set_current_working_directory) + self.env_button = create_action(self, _("Environment variables"), + icon=ima.icon('environ'), + triggered=self.show_env) + self.syspath_button = create_action(self, + _("Show sys.path contents"), + icon=ima.icon('syspath'), + triggered=self.show_syspath) + actions = [run_settings_menu, self.show_time_action, None, + self.cwd_button, self.env_button, self.syspath_button] + if self.menu_actions is not None: + actions += [None]+self.menu_actions + return actions + + def is_interpreter(self): + """Return True if shellwidget is a Python interpreter""" + return self.is_interpreter + + def get_shell_widget(self): + if self.stand_alone is None: + return self.shell + else: + self.namespacebrowser = NamespaceBrowser(self) + settings = self.stand_alone + self.namespacebrowser.set_shellwidget(self) + self.namespacebrowser.setup(**settings) + self.namespacebrowser.sig_collapse.connect( + lambda: self.toggle_globals_explorer(False)) + # Shell splitter + self.splitter = splitter = QSplitter(Qt.Vertical, self) + self.splitter.splitterMoved.connect(self.splitter_moved) + splitter.addWidget(self.shell) + splitter.setCollapsible(0, False) + splitter.addWidget(self.namespacebrowser) + splitter.setStretchFactor(0, 1) + splitter.setStretchFactor(1, 0) + splitter.setHandleWidth(5) + splitter.setSizes([2, 1]) + return splitter + + def get_icon(self): + return ima.icon('python') + + def set_buttons_runnning_state(self, state): + ExternalShellBase.set_buttons_runnning_state(self, state) + self.interact_action.setEnabled(not state and not self.is_interpreter) + self.debug_action.setEnabled(not state and not self.is_interpreter) + self.args_action.setEnabled(not state and not self.is_interpreter) + self.post_mortem_action.setEnabled(not state and not self.is_interpreter) + if state: + if self.arguments: + argstr = _("Arguments: %s") % self.arguments + else: + argstr = _("No argument") + else: + argstr = _("Arguments...") + self.args_action.setText(argstr) + self.terminate_button.setVisible(not self.is_interpreter and state) + if not state: + self.toggle_globals_explorer(False) + for btn in (self.cwd_button, self.env_button, self.syspath_button): + btn.setEnabled(state and self.monitor_enabled) + if self.namespacebrowser_button is not None: + self.namespacebrowser_button.setEnabled(state) + + def set_namespacebrowser(self, namespacebrowser): + """ + Set namespace browser *widget* + Note: this method is not used in stand alone mode + """ + self.namespacebrowser = namespacebrowser + self.configure_namespacebrowser() + + def configure_namespacebrowser(self): + """Connect the namespace browser to the notification thread""" + if self.notification_thread is not None: + self.notification_thread.refresh_namespace_browser.connect( + self.namespacebrowser.refresh_table) + signal = self.notification_thread.sig_process_remote_view + signal.connect(lambda data: + self.namespacebrowser.process_remote_view(data)) + + def create_process(self): + self.shell.clear() + + self.process = QProcess(self) + if self.merge_output_channels: + self.process.setProcessChannelMode(QProcess.MergedChannels) + else: + self.process.setProcessChannelMode(QProcess.SeparateChannels) + self.shell.wait_for_ready_read.connect( + lambda: self.process.waitForReadyRead(250)) + + # Working directory + if self.wdir is not None: + self.process.setWorkingDirectory(self.wdir) + + #-------------------------Python specific------------------------------ + # Python arguments + p_args = ['-u'] + if DEBUG >= 3: + p_args += ['-v'] + p_args += get_python_args(self.fname, self.python_args, + self.interact_action.isChecked(), + self.debug_action.isChecked(), + self.arguments) + + env = [to_text_string(_path) + for _path in self.process.systemEnvironment()] + if self.pythonstartup: + env.append('PYTHONSTARTUP=%s' % self.pythonstartup) + + #-------------------------Python specific------------------------------- + # Post mortem debugging + if self.post_mortem_action.isChecked(): + env.append('SPYDER_EXCEPTHOOK=True') + + # Set standard input/output encoding for Python consoles + # See http://stackoverflow.com/q/26312400/438386, specifically + # the comments of Martijn Pieters + env.append('PYTHONIOENCODING=UTF-8') + + # Monitor + if self.monitor_enabled: + env.append('SPYDER_SHELL_ID=%s' % id(self)) + env.append('SPYDER_AR_TIMEOUT=%d' % self.autorefresh_timeout) + env.append('SPYDER_AR_STATE=%r' % self.autorefresh_state) + from spyder.widgets.externalshell import introspection + introspection_server = introspection.start_introspection_server() + introspection_server.register(self) + notification_server = introspection.start_notification_server() + self.notification_thread = notification_server.register(self) + self.notification_thread.sig_pdb.connect( + lambda fname, lineno: + self.sig_pdb.emit(fname, lineno)) + self.notification_thread.open_file.connect( + lambda fname, lineno: + self.open_file.emit(fname, lineno)) + if self.namespacebrowser is not None: + self.configure_namespacebrowser() + env.append('SPYDER_I_PORT=%d' % introspection_server.port) + env.append('SPYDER_N_PORT=%d' % notification_server.port) + + # External modules options + env.append('ETS_TOOLKIT=%s' % self.ets_backend) + if self.mpl_backend is not None: + backends = {0: 'Automatic', 1: 'None', 2: 'TkAgg'} + env.append('SPY_MPL_BACKEND=%s' % backends[self.mpl_backend]) + if self.qt_api: + env.append('QT_API=%s' % self.qt_api) + env.append('COLORIZE_SYS_STDERR=%s' % self.colorize_sys_stderr) +# # Socket-based alternative (see input hook in sitecustomize.py): +# if self.install_qt_inputhook: +# from PyQt4.QtNetwork import QLocalServer +# self.local_server = QLocalServer() +# self.local_server.listen(str(id(self))) + + # User Module Deleter + if self.is_interpreter: + env.append('UMR_ENABLED=%r' % self.umr_enabled) + env.append('UMR_NAMELIST=%s' % ','.join(self.umr_namelist)) + env.append('UMR_VERBOSE=%r' % self.umr_verbose) + env.append('MATPLOTLIB_ION=True') + else: + if self.interact: + env.append('MATPLOTLIB_ION=True') + else: + env.append('MATPLOTLIB_ION=False') + + # External interpreter + env.append('EXTERNAL_INTERPRETER=%r' % self.external_interpreter) + + # Add sitecustomize path to path list + pathlist = [] + spy_path = get_module_source_path('spyder') + sc_path = osp.join(spy_path, 'utils', 'site') + pathlist.append(sc_path) + + # Adding Spyder path + pathlist += self.path + + # Adding path list to PYTHONPATH environment variable + add_pathlist_to_PYTHONPATH(env, pathlist) + + #-------------------------Python specific------------------------------ + + self.process.readyReadStandardOutput.connect(self.write_output) + self.process.readyReadStandardError.connect(self.write_error) + self.process.finished.connect(lambda ec, es=QProcess.ExitStatus: + self.finished(ec, es)) + self.sig_finished.connect(self.dialog_manager.close_all) + self.terminate_button.clicked.connect(self.process.terminate) + self.kill_button.clicked.connect(self.process.kill) + + #-------------------------Python specific------------------------------ + # Fixes for our Mac app: + # 1. PYTHONPATH and PYTHONHOME are set while bootstrapping the app, + # but their values are messing sys.path for external interpreters + # (e.g. EPD) so we need to remove them from the environment. + # 2. Set PYTHONPATH again but without grabbing entries defined in the + # environment (Fixes Issue 1321) + # 3. Remove PYTHONOPTIMIZE from env so that we can have assert + # statements working with our interpreters (See Issue 1281) + if running_in_mac_app(): + if MAC_APP_NAME not in self.pythonexecutable: + env = [p for p in env if not (p.startswith('PYTHONPATH') or \ + p.startswith('PYTHONHOME'))] # 1. + + add_pathlist_to_PYTHONPATH(env, pathlist, drop_env=True) # 2. + env = [p for p in env if not p.startswith('PYTHONOPTIMIZE')] # 3. + + processEnvironment = QProcessEnvironment() + for envItem in env: + envName, separator, envValue = envItem.partition('=') + processEnvironment.insert(envName, envValue) + self.process.setProcessEnvironment(processEnvironment) + self.process.start(self.pythonexecutable, p_args) + #-------------------------Python specific------------------------------ + + running = self.process.waitForStarted(3000) + self.set_running_state(running) + if not running: + QMessageBox.critical(self, _("Error"), + _("A Python console failed to start!")) + else: + self.shell.setFocus() + self.started.emit() + return self.process + + def finished(self, exit_code, exit_status): + """Reimplement ExternalShellBase method""" + ExternalShellBase.finished(self, exit_code, exit_status) + self.introspection_socket = None + + +#============================================================================== +# Input/Output +#============================================================================== + def write_error(self): + if os.name == 'nt': + #---This is apparently necessary only on Windows (not sure though): + # emptying standard output buffer before writing error output + self.process.setReadChannel(QProcess.StandardOutput) + if self.process.waitForReadyRead(1): + self.write_output() + self.shell.write_error(self.get_stderr()) + QApplication.processEvents() + + def send_to_process(self, text): + if not self.is_running(): + return + if not is_text_string(text): + text = to_text_string(text) + if self.mpl_backend == 0 and os.name == 'nt' and \ + self.introspection_socket is not None: + communicate(self.introspection_socket, "toggle_inputhook_flag(True)") +# # Socket-based alternative (see input hook in sitecustomize.py): +# while self.local_server.hasPendingConnections(): +# self.local_server.nextPendingConnection().write('go!') + if any([text == cmd for cmd in ['%ls', '%pwd', '%scientific']]) or \ + any([text.startswith(cmd) for cmd in ['%cd ', '%clear ']]): + text = 'evalsc(r"%s")\n' % text + if not text.endswith('\n'): + text += '\n' + self.process.write(to_binary_string(text, 'utf8')) + self.process.waitForBytesWritten(-1) + + # Eventually write prompt faster (when hitting Enter continuously) + # -- necessary/working on Windows only: + if os.name == 'nt': + self.write_error() + + def keyboard_interrupt(self): + if self.introspection_socket is not None: + communicate(self.introspection_socket, "thread.interrupt_main()") + + def quit_monitor(self): + if self.introspection_socket is not None: + try: + write_packet(self.introspection_socket, "thread.exit()") + except socket.error: + pass + +#============================================================================== +# Globals explorer +#============================================================================== + @Slot(bool) + def toggle_globals_explorer(self, state): + if self.stand_alone is not None: + self.splitter.setSizes([1, 1 if state else 0]) + self.namespacebrowser_button.setChecked(state) + if state and self.namespacebrowser is not None: + self.namespacebrowser.refresh_table() + + def splitter_moved(self, pos, index): + self.namespacebrowser_button.setChecked( self.splitter.sizes()[1] ) + +#============================================================================== +# Misc. +#============================================================================== + @Slot() + def set_current_working_directory(self): + """Set current working directory""" + cwd = self.shell.get_cwd() + self.redirect_stdio.emit(False) + directory = getexistingdirectory(self, _("Select directory"), cwd) + if directory: + self.shell.set_cwd(directory) + self.redirect_stdio.emit(True) + + @Slot() + def show_env(self): + """Show environment variables""" + get_func = self.shell.get_env + set_func = self.shell.set_env + self.dialog_manager.show(RemoteEnvDialog(get_func, set_func)) + + @Slot() + def show_syspath(self): + """Show sys.path contents""" + editor = CollectionsEditor() + editor.setup(self.shell.get_syspath(), title="sys.path", readonly=True, + width=600, icon=ima.icon('syspath')) + self.dialog_manager.show(editor) + + +def test(): + from spyder.utils.qthelpers import qapplication + app = qapplication() + + from spyder.plugins.variableexplorer import VariableExplorer + settings = VariableExplorer.get_settings() + + shell = ExternalPythonShell(pythonexecutable=sys.executable, + interact=True, + stand_alone=settings, + wdir=osp.dirname(__file__), + mpl_backend=0, + light_background=False) + + from spyder.config.gui import get_font + + font = get_font() + shell.shell.set_font(font) + + shell.shell.toggle_wrap_mode(True) + shell.start_shell(False) + shell.show() + sys.exit(app.exec_()) + + +if __name__ == "__main__": + test() \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/externalshell/systemshell.py spyder-3.0.2+dfsg1/spyder/widgets/externalshell/systemshell.py --- spyder-2.3.8+dfsg1/spyder/widgets/externalshell/systemshell.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/externalshell/systemshell.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""External System Shell widget: execute terminal in a separate process""" + +# Standard library imports +import os +import sys + +# Third party imports +from qtpy.QtCore import QProcess, QProcessEnvironment, QTextCodec, Signal +from qtpy.QtWidgets import QMessageBox + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import is_text_string, to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.misc import add_pathlist_to_PYTHONPATH +from spyder.widgets.externalshell.baseshell import ExternalShellBase +from spyder.widgets.shell import TerminalWidget + + +LOCALE_CODEC = QTextCodec.codecForLocale() +CP850_CODEC = QTextCodec.codecForName('cp850') + + +class ExternalSystemShell(ExternalShellBase): + """External Shell widget: execute Python script in a separate process""" + SHELL_CLASS = TerminalWidget + started = Signal() + + def __init__(self, parent=None, wdir=None, path=[], light_background=True, + menu_actions=None, show_buttons_inside=True, + show_elapsed_time=True): + ExternalShellBase.__init__(self, parent=parent, fname=None, wdir=wdir, + history_filename='.history', + light_background=light_background, + menu_actions=menu_actions, + show_buttons_inside=show_buttons_inside, + show_elapsed_time=show_elapsed_time) + + # Additional python path list + self.path = path + + # For compatibility with the other shells that can live in the external + # console + self.connection_file = None + + def get_icon(self): + return ima.icon('cmdprompt') + + def finish_process(self): + while not self.process.waitForFinished(100): + self.process.kill(); + + def create_process(self): + self.shell.clear() + + self.process = QProcess(self) + self.process.setProcessChannelMode(QProcess.MergedChannels) + + # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe) + env = [to_text_string(_path) + for _path in self.process.systemEnvironment()] + + processEnvironment = QProcessEnvironment() + for envItem in env: + envName, separator, envValue = envItem.partition('=') + processEnvironment.insert(envName, envValue) + + add_pathlist_to_PYTHONPATH(env, self.path) + self.process.setProcessEnvironment(processEnvironment) + + + # Working directory + if self.wdir is not None: + self.process.setWorkingDirectory(self.wdir) + + # Shell arguments + if os.name == 'nt': + p_args = ['/Q'] + else: + p_args = ['-i'] + + if self.arguments: + p_args.extend( shell_split(self.arguments) ) + + self.process.readyReadStandardOutput.connect(self.write_output) + self.process.finished.connect(self.finished) + self.kill_button.clicked.connect(self.process.kill) + + if os.name == 'nt': + self.process.start('cmd.exe', p_args) + else: + # Using bash: + self.process.start('bash', p_args) + self.send_to_process('PS1="\\u@\\h:\\w> "\n') + + running = self.process.waitForStarted() + self.set_running_state(running) + if not running: + QMessageBox.critical(self, _("Error"), + _("Process failed to start")) + else: + self.shell.setFocus() + self.started.emit() + + return self.process + +#=============================================================================== +# Input/Output +#=============================================================================== + def transcode(self, qba): + if os.name == 'nt': + return to_text_string( CP850_CODEC.toUnicode(qba.data()) ) + else: + return ExternalShellBase.transcode(self, qba) + + def send_to_process(self, text): + if not is_text_string(text): + text = to_text_string(text) + if text[:-1] in ["clear", "cls", "CLS"]: + self.shell.clear() + self.send_to_process(os.linesep) + return + if not text.endswith('\n'): + text += '\n' + if os.name == 'nt': + self.process.write(text.encode('cp850')) + else: + self.process.write(LOCALE_CODEC.fromUnicode(text)) + self.process.waitForBytesWritten(-1) + + def keyboard_interrupt(self): + # This does not work on Windows: + # (unfortunately there is no easy way to send a Ctrl+C to cmd.exe) + self.send_ctrl_to_process('c') + +# # The following code will soon be removed: +# # (last attempt to send a Ctrl+C on Windows) +# if os.name == 'nt': +# pid = int(self.process.pid()) +# import ctypes, win32api, win32con +# class _PROCESS_INFORMATION(ctypes.Structure): +# _fields_ = [("hProcess", ctypes.c_int), +# ("hThread", ctypes.c_int), +# ("dwProcessID", ctypes.c_int), +# ("dwThreadID", ctypes.c_int)] +# x = ctypes.cast( ctypes.c_void_p(pid), +# ctypes.POINTER(_PROCESS_INFORMATION) ) +# win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, +# x.dwProcessID) +# else: +# self.send_ctrl_to_process('c') + + +#============================================================================== +# Tests +#============================================================================== +def test(): + import os.path as osp + from spyder.utils.qthelpers import qapplication + app = qapplication(test_time=5) + shell = ExternalSystemShell(wdir=osp.dirname(__file__), + light_background=False) + + app.aboutToQuit.connect(shell.finish_process) + + from qtpy.QtGui import QFont + font = QFont() + font.setPointSize(10) + shell.shell.set_font(font) + + shell.shell.toggle_wrap_mode(True) + shell.start_shell(False) + shell.show() + sys.exit(app.exec_()) + + +if __name__ == "__main__": + test() \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/fileswitcher.py spyder-3.0.2+dfsg1/spyder/widgets/fileswitcher.py --- spyder-2.3.8+dfsg1/spyder/widgets/fileswitcher.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/fileswitcher.py 2016-11-16 16:12:05.000000000 +0000 @@ -0,0 +1,609 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +# Standard library imports +from __future__ import print_function +import os +import os.path as osp + +# Third party imports +from qtpy.QtCore import Signal, QEvent, QObject, QRegExp, QSize, Qt +from qtpy.QtGui import (QIcon, QRegExpValidator, QTextCursor) +from qtpy.QtWidgets import (QDialog, QHBoxLayout, QLabel, QLineEdit, + QListWidget, QListWidgetItem, QVBoxLayout) + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import iteritems, to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.stringmatching import get_search_scores +from spyder.widgets.helperwidgets import HelperToolButton, HTMLDelegate + + +# --- Python Outline explorer helpers +def process_python_symbol_data(oedata): + """Returns a list with line number, definition name, fold and token.""" + symbol_list = [] + for key in oedata: + val = oedata[key] + if val and key != 'found_cell_separators': + if val.is_class_or_function(): + symbol_list.append((key, val.def_name, val.fold_level, + val.get_token())) + return sorted(symbol_list) + + +def get_python_symbol_icons(oedata): + """Return a list of icons for oedata of a python file.""" + class_icon = ima.icon('class') + method_icon = ima.icon('method') + function_icon = ima.icon('function') + private_icon = ima.icon('private1') + super_private_icon = ima.icon('private2') + + symbols = process_python_symbol_data(oedata) + + # line - 1, name, fold level + fold_levels = sorted(list(set([s[2] for s in symbols]))) + parents = [None]*len(symbols) + icons = [None]*len(symbols) + indexes = [] + + parent = None + for level in fold_levels: + for index, item in enumerate(symbols): + line, name, fold_level, token = item + if index in indexes: + continue + + if fold_level == level: + indexes.append(index) + parent = item + else: + parents[index] = parent + + for index, item in enumerate(symbols): + parent = parents[index] + + if item[-1] == 'def': + icons[index] = function_icon + elif item[-1] == 'class': + icons[index] = class_icon + else: + icons[index] = QIcon() + + if parent is not None: + if parent[-1] == 'class': + if item[-1] == 'def' and item[1].startswith('__'): + icons[index] = super_private_icon + elif item[-1] == 'def' and item[1].startswith('_'): + icons[index] = private_icon + else: + icons[index] = method_icon + + return icons + + +def shorten_paths(path_list, is_unsaved): + """ + Takes a list of paths and tries to "intelligently" shorten them all. The + aim is to make it clear to the user where the paths differ, as that is + likely what they care about. Note that this operates on a list of paths + not on individual paths. + + If the path ends in an actual file name, it will be trimmed off. + """ + # TODO: at the end, if the path is too long, should do a more dumb kind of + # shortening, but not completely dumb. + + # Convert the path strings to a list of tokens and start building the + # new_path using the drive + path_list = path_list[:] # Make a local copy + new_path_list = [] + + for ii, (path, is_unsav) in enumerate(zip(path_list, is_unsaved)): + if is_unsav: + new_path_list.append(_('unsaved file')) + path_list[ii] = None + else: + drive, path = osp.splitdrive(osp.dirname(path)) + new_path_list.append(drive + osp.sep) + path_list[ii] = [part for part in path.split(osp.sep) if part] + + def recurse_level(level_idx): + sep = os.sep + + # If toks are all empty we need not have recursed here + if not any(level_idx.values()): + return + + # Firstly, find the longest common prefix for all in the level + # s = len of longest common prefix + sample_toks = list(level_idx.values())[0] + if not sample_toks: + s = 0 + else: + for s, sample_val in enumerate(sample_toks): + if not all(len(toks) > s and toks[s] == sample_val + for toks in level_idx.values()): + break + + # Shorten longest common prefix + if s == 0: + short_form = '' + else: + if s == 1: + short_form = sample_toks[0] + elif s == 2: + short_form = sample_toks[0] + sep + sample_toks[1] + else: + short_form = "..." + sep + sample_toks[s-1] + for idx in level_idx: + new_path_list[idx] += short_form + sep + level_idx[idx] = level_idx[idx][s:] + + # Group the remaining bit after the common prefix, shorten, and recurse + while level_idx: + k, group = 0, level_idx # k is length of the group's common prefix + while True: + # Abort if we've gone beyond end of one or more in the group + prospective_group = {idx: toks for idx, toks + in group.items() if len(toks) == k} + if prospective_group: + if k == 0: # we spit out the group with no suffix + group = prospective_group + break + # Only keep going if all n still match on the kth token + _, sample_toks = next(iteritems(group)) + prospective_group = {idx: toks for idx, toks + in group.items() + if toks[k] == sample_toks[k]} + if len(prospective_group) == len(group) or k == 0: + group = prospective_group + k += 1 + else: + break + _, sample_toks = next(iteritems(group)) + if k == 0: + short_form = '' + elif k == 1: + short_form = sample_toks[0] + elif k == 2: + short_form = sample_toks[0] + sep + sample_toks[1] + else: # k > 2 + short_form = sample_toks[0] + "..." + sep + sample_toks[k-1] + for idx in group.keys(): + new_path_list[idx] += short_form + (sep if k > 0 else '') + del level_idx[idx] + recurse_level({idx: toks[k:] for idx, toks in group.items()}) + + recurse_level({i: pl for i, pl in enumerate(path_list) if pl}) + + return [path.rstrip(os.sep) for path in new_path_list] + + +class KeyPressFilter(QObject): + """ + Use with `installEventFilter` to get up/down arrow key press signal. + """ + UP, DOWN = [-1, 1] # Step constants + + sig_up_key_pressed = Signal() + sig_down_key_pressed = Signal() + + def eventFilter(self, src, e): + if e.type() == QEvent.KeyPress: + if e.key() == Qt.Key_Up: + self.sig_up_key_pressed.emit() + elif e.key() == Qt.Key_Down: + self.sig_down_key_pressed.emit() + + return super(KeyPressFilter, self).eventFilter(src, e) + + +class FileSwitcher(QDialog): + """A Sublime-like file switcher.""" + sig_goto_file = Signal(int) + sig_close_file = Signal(int) + + # Constants that define the mode in which the list widget is working + # FILE_MODE is for a list of files, SYMBOL_MODE if for a list of symbols + # in a given file when using the '@' symbol. + FILE_MODE, SYMBOL_MODE = [1, 2] + + def __init__(self, parent, tabs, data): + QDialog.__init__(self, parent) + + # Variables + self.tabs = tabs # Editor stack tabs + self.data = data # Editor data + self.mode = self.FILE_MODE # By default start in this mode + self.initial_cursors = None # {fullpath: QCursor} + self.initial_path = None # Fullpath of initial active editor + self.initial_editor = None # Initial active editor + self.line_number = None # Selected line number in filer + self.is_visible = False # Is the switcher visible? + + help_text = _("Press Enter to switch files or Esc to " + "cancel.

    Type to filter filenames.

    " + "Use :number to go to a line, e.g. " + "main:42
    " + "Use @symbol_text to go to a symbol, e.g. " + "@init" + "

    Press Ctrl+W to close current tab.
    ") + + # Either allow searching for a line number or a symbol but not both + regex = QRegExp("([A-Za-z0-9_]{0,100}@[A-Za-z0-9_]{0,100})|" + + "([A-Za-z0-9_]{0,100}:{0,1}[0-9]{0,100})") + + # Widgets + self.edit = QLineEdit(self) + self.help = HelperToolButton() + self.list = QListWidget(self) + self.filter = KeyPressFilter() + regex_validator = QRegExpValidator(regex, self.edit) + + # Widgets setup + self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint) + self.setWindowOpacity(0.95) + self.edit.installEventFilter(self.filter) + self.edit.setValidator(regex_validator) + self.help.setToolTip(help_text) + self.list.setItemDelegate(HTMLDelegate(self)) + + # Layout + edit_layout = QHBoxLayout() + edit_layout.addWidget(self.edit) + edit_layout.addWidget(self.help) + layout = QVBoxLayout() + layout.addLayout(edit_layout) + layout.addWidget(self.list) + self.setLayout(layout) + + # Signals + self.rejected.connect(self.restore_initial_state) + self.filter.sig_up_key_pressed.connect(self.previous_row) + self.filter.sig_down_key_pressed.connect(self.next_row) + self.edit.returnPressed.connect(self.accept) + self.edit.textChanged.connect(self.setup) + self.list.itemSelectionChanged.connect(self.item_selection_changed) + self.list.clicked.connect(self.edit.setFocus) + + # Setup + self.save_initial_state() + self.set_dialog_position() + self.setup() + + # --- Properties + @property + def editors(self): + return [self.tabs.widget(index) for index in range(self.tabs.count())] + + @property + def line_count(self): + return [editor.get_line_count() for editor in self.editors] + + @property + def save_status(self): + return [getattr(td, 'newly_created', False) for td in self.data] + + @property + def paths(self): + return [getattr(td, 'filename', None) for td in self.data] + + @property + def filenames(self): + return [self.tabs.tabText(index) for index in range(self.tabs.count())] + + @property + def current_path(self): + return self.paths_by_editor[self.get_editor()] + + @property + def paths_by_editor(self): + return dict(zip(self.editors, self.paths)) + + @property + def editors_by_path(self): + return dict(zip(self.paths, self.editors)) + + @property + def filter_text(self): + """Get the normalized (lowecase) content of the filter text.""" + return to_text_string(self.edit.text()).lower() + + def save_initial_state(self): + """Saves initial cursors and initial active editor.""" + paths = self.paths + self.initial_editor = self.get_editor() + self.initial_cursors = {} + + for i, editor in enumerate(self.editors): + if editor is self.initial_editor: + self.initial_path = paths[i] + self.initial_cursors[paths[i]] = editor.textCursor() + + def accept(self): + self.is_visible = False + QDialog.accept(self) + self.list.clear() + + def restore_initial_state(self): + """Restores initial cursors and initial active editor.""" + self.list.clear() + self.is_visible = False + editors = self.editors_by_path + + for path in self.initial_cursors: + cursor = self.initial_cursors[path] + if path in editors: + self.set_editor_cursor(editors[path], cursor) + + if self.initial_editor in self.paths_by_editor: + index = self.paths.index(self.initial_path) + self.sig_goto_file.emit(index) + + def set_dialog_position(self): + """Positions the file switcher dialog in the center of the editor.""" + parent = self.parent() + geo = parent.geometry() + width = self.list.width() # This has been set in setup + + left = parent.geometry().width()/2 - width/2 + top = 0 + while parent: + geo = parent.geometry() + top += geo.top() + left += geo.left() + parent = parent.parent() + + # Note: the +1 pixel on the top makes it look better + self.move(left, top + self.tabs.tabBar().geometry().height() + 1) + + def fix_size(self, content, extra=50): + """ + Adjusts the width and height of the file switcher, + based on its content. + """ + # Update size of dialog based on longest shortened path + strings = [] + if content: + for rich_text in content: + label = QLabel(rich_text) + label.setTextFormat(Qt.PlainText) + strings.append(label.text()) + fm = label.fontMetrics() + + # Max width + max_width = max([fm.width(s) * 1.3 for s in strings]) + self.list.setMinimumWidth(max_width + extra) + + # Max height + if len(strings) < 8: + max_entries = len(strings) + else: + max_entries = 8 + max_height = fm.height() * max_entries * 2.5 + self.list.setMinimumHeight(max_height) + + # Set position according to size + self.set_dialog_position() + + # --- Helper methods: List widget + def count(self): + """Gets the item count in the list widget.""" + return self.list.count() + + def current_row(self): + """Returns the current selected row in the list widget.""" + return self.list.currentRow() + + def set_current_row(self, row): + """Sets the current selected row in the list widget.""" + return self.list.setCurrentRow(row) + + def select_row(self, steps): + """Select row in list widget based on a number of steps with direction. + + Steps can be positive (next rows) or negative (previous rows). + """ + row = self.current_row() + steps + if 0 <= row < self.count(): + self.set_current_row(row) + + def previous_row(self): + """Select previous row in list widget.""" + self.select_row(-1) + + def next_row(self): + """Select next row in list widget.""" + self.select_row(+1) + + # --- Helper methods: Editor + def get_editor(self, index=None, path=None): + """Get editor by index or path. + + If no path or index specified the current active editor is returned + """ + if index: + return self.tabs.widget(index) + elif path: + return self.tabs.widget(index) + else: + return self.parent().get_current_editor() + + def set_editor_cursor(self, editor, cursor): + """Set the cursor of an editor.""" + pos = cursor.position() + anchor = cursor.anchor() + + new_cursor = QTextCursor() + if pos == anchor: + new_cursor.movePosition(pos) + else: + new_cursor.movePosition(anchor) + new_cursor.movePosition(pos, QTextCursor.KeepAnchor) + editor.setTextCursor(cursor) + + def goto_line(self, line_number): + """Go to specified line number in current active editor.""" + if line_number: + line_number = int(line_number) + editor = self.get_editor() + editor.go_to_line(min(line_number, editor.get_line_count())) + + # --- Helper methods: Outline explorer + def get_symbol_list(self): + """Get the list of symbols present in the file.""" + try: + oedata = self.get_editor().get_outlineexplorer_data() + except AttributeError: + oedata = {} + return oedata + + # --- Handlers + def item_selection_changed(self): + """List widget item selection change handler.""" + row = self.current_row() + if self.count() and row >= 0: + if self.mode == self.FILE_MODE: + try: + stack_index = self.paths.index(self.filtered_path[row]) + self.sig_goto_file.emit(stack_index) + self.goto_line(self.line_number) + self.edit.setFocus() + except ValueError: + pass + else: + line_number = self.filtered_symbol_lines[row] + self.goto_line(line_number) + + def setup_file_list(self, filter_text, current_path): + """Setup list widget content for file list display.""" + short_paths = shorten_paths(self.paths, self.save_status) + paths = self.paths + results = [] + trying_for_line_number = ':' in filter_text + + # Get optional line number + if trying_for_line_number: + filter_text, line_number = filter_text.split(':') + else: + line_number = None + + # Get all available filenames and get the scores for "fuzzy" matching + scores = get_search_scores(filter_text, self.filenames, + template="{0}") + + # Build the text that will appear on the list widget + for index, score in enumerate(scores): + text, rich_text, score_value = score + if score_value != -1: + text_item = '' + rich_text.replace('&', '') + '' + if trying_for_line_number: + text_item += " [{0:} {1:}]".format(self.line_count[index], + _("lines")) + text_item += "
    {0:}".format(short_paths[index]) + results.append((score_value, index, text_item)) + + # Sort the obtained scores and populate the list widget + self.filtered_path = [] + for result in sorted(results): + index = result[1] + text = result[-1] + path = paths[index] + item = QListWidgetItem(ima.icon('FileIcon'), text) + item.setToolTip(path) + item.setSizeHint(QSize(0, 25)) + self.list.addItem(item) + self.filtered_path.append(path) + + # To adjust the delegate layout for KDE themes + self.list.files_list = True + + # Move selected item in list accordingly and update list size + if current_path in self.filtered_path: + self.set_current_row(self.filtered_path.index(current_path)) + elif self.filtered_path: + self.set_current_row(0) + self.fix_size(short_paths, extra=200) + + # If a line number is searched look for it + self.line_number = line_number + self.goto_line(line_number) + + def setup_symbol_list(self, filter_text, current_path): + """Setup list widget content for symbol list display.""" + # Get optional symbol name + filter_text, symbol_text = filter_text.split('@') + + # Fetch the Outline explorer data, get the icons and values + oedata = self.get_symbol_list() + icons = get_python_symbol_icons(oedata) + + symbol_list = process_python_symbol_data(oedata) + line_fold_token = [(item[0], item[2], item[3]) for item in symbol_list] + choices = [item[1] for item in symbol_list] + scores = get_search_scores(symbol_text, choices, template="{0}") + + # Build the text that will appear on the list widget + results = [] + lines = [] + self.filtered_symbol_lines = [] + for index, score in enumerate(scores): + text, rich_text, score_value = score + line, fold_level, token = line_fold_token[index] + lines.append(text) + if score_value != -1: + results.append((score_value, line, text, rich_text, + fold_level, icons[index], token)) + + template = '{0}{1}' + + for (score, line, text, rich_text, fold_level, icon, + token) in sorted(results): + fold_space = ' '*(fold_level) + line_number = line + 1 + self.filtered_symbol_lines.append(line_number) + textline = template.format(fold_space, rich_text) + item = QListWidgetItem(icon, textline) + item.setSizeHint(QSize(0, 16)) + self.list.addItem(item) + + # To adjust the delegate layout for KDE themes + self.list.files_list = False + + # Move selected item in list accordingly + # NOTE: Doing this is causing two problems: + # 1. It makes the cursor to auto-jump to the last selected + # symbol after opening or closing a different file + # 2. It moves the cursor to the first symbol by default, + # which is very distracting. + # That's why this line is commented! + # self.set_current_row(0) + + # Update list size + self.fix_size(lines, extra=125) + + def setup(self): + """Setup list widget content.""" + if not self.tabs.count(): + self.close() + return + + self.list.clear() + current_path = self.current_path + filter_text = self.filter_text + + # Get optional line or symbol to define mode and method handler + trying_for_symbol = ('@' in self.filter_text) + + if trying_for_symbol: + self.mode = self.SYMBOL_MODE + self.setup_symbol_list(filter_text, current_path) + else: + self.mode = self.FILE_MODE + self.setup_file_list(filter_text, current_path) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/findinfiles.py spyder-3.0.2+dfsg1/spyder/widgets/findinfiles.py --- spyder-2.3.8+dfsg1/spyder/widgets/findinfiles.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/findinfiles.py 2016-11-16 02:30:06.000000000 +0000 @@ -0,0 +1,798 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Find in files widget""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +from __future__ import with_statement +import fnmatch +import os +import os.path as osp +import re +import sys +import traceback + +# Third party imports +from qtpy.compat import getexistingdirectory +from qtpy.QtCore import QMutex, QMutexLocker, Qt, QThread, Signal, Slot +from qtpy.QtWidgets import (QHBoxLayout, QLabel, QRadioButton, QSizePolicy, + QTreeWidgetItem, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import getcwd, to_text_string +from spyder.utils import programs +from spyder.utils import icon_manager as ima +from spyder.utils.misc import abspardir, get_common_path +from spyder.utils.qthelpers import create_toolbutton, get_filetype_icon +from spyder.utils.vcs import is_hg_installed, get_vcs_root +from spyder.widgets.comboboxes import PathComboBox, PatternComboBox +from spyder.widgets.onecolumntree import OneColumnTree + + +#def find_files_in_hg_manifest(rootpath, include, exclude): +# p = Popen("hg manifest", stdout=PIPE) +# found = [] +# hgroot = get_vcs_root(rootpath) +# for path in p.stdout.read().splitlines(): +# dirname = osp.join('.', osp.dirname(path)) +# if re.search(exclude, dirname+os.sep): +# continue +# filename = osp.join('.', osp.dirname(path)) +# if re.search(exclude, filename): +# continue +# if re.search(include, filename): +# found.append(osp.join(hgroot, path)) +# return found +# +#def find_files_in_path(rootpath, include, exclude): +# found = [] +# for path, dirs, files in os.walk(rootpath): +# for d in dirs[:]: +# dirname = os.path.join(path, d) +# if re.search(exclude, dirname+os.sep): +# dirs.remove(d) +# for f in files: +# filename = os.path.join(path, f) +# if re.search(exclude, filename): +# continue +# if re.search(include, filename): +# found.append(filename) +# return found + + +#def find_string_in_files(texts, filenames, regexp=False): +# results = {} +# nb = 0 +# for fname in filenames: +# for lineno, line in enumerate(file(fname)): +# for text, enc in texts: +# if regexp: +# found = re.search(text, line) +# if found is not None: +# break +# else: +# found = line.find(text) +# if found > -1: +# break +# try: +# line_dec = line.decode(enc) +# except UnicodeDecodeError: +# line_dec = line +# if regexp: +# for match in re.finditer(text, line): +# res = results.get(osp.abspath(fname), []) +# res.append((lineno+1, match.start(), line_dec)) +# results[osp.abspath(fname)] = res +# nb += 1 +# else: +# while found > -1: +# res = results.get(osp.abspath(fname), []) +# res.append((lineno+1, found, line_dec)) +# results[osp.abspath(fname)] = res +# for text in texts: +# found = line.find(text, found+1) +# if found>-1: +# break +# nb += 1 +# return results, nb + +class SearchThread(QThread): + """Find in files search thread""" + sig_finished = Signal(bool) + + def __init__(self, parent): + QThread.__init__(self, parent) + self.mutex = QMutex() + self.stopped = None + self.results = None + self.pathlist = None + self.nb = None + self.error_flag = None + self.rootpath = None + self.python_path = None + self.hg_manifest = None + self.include = None + self.exclude = None + self.texts = None + self.text_re = None + self.completed = None + self.get_pythonpath_callback = None + + def initialize(self, path, python_path, hg_manifest, + include, exclude, texts, text_re): + self.rootpath = path + self.python_path = python_path + self.hg_manifest = hg_manifest + self.include = include + self.exclude = exclude + self.texts = texts + self.text_re = text_re + self.stopped = False + self.completed = False + + def run(self): + try: + self.filenames = [] + if self.hg_manifest: + ok = self.find_files_in_hg_manifest() + elif self.python_path: + ok = self.find_files_in_python_path() + else: + ok = self.find_files_in_path(self.rootpath) + if ok: + self.find_string_in_files() + except Exception: + # Important note: we have to handle unexpected exceptions by + # ourselves because they won't be catched by the main thread + # (known QThread limitation/bug) + traceback.print_exc() + self.error_flag = _("Unexpected error: see internal console") + self.stop() + self.sig_finished.emit(self.completed) + + def stop(self): + with QMutexLocker(self.mutex): + self.stopped = True + + def find_files_in_python_path(self): + pathlist = os.environ.get('PYTHONPATH', '').split(os.pathsep) + if self.get_pythonpath_callback is not None: + pathlist += self.get_pythonpath_callback() + if os.name == "nt": + # The following avoid doublons on Windows platforms: + # (e.g. "d:\Python" in PYTHONPATH environment variable, + # and "D:\Python" in Spyder's python path would lead + # to two different search folders) + winpathlist = [] + lcpathlist = [] + for path in pathlist: + lcpath = osp.normcase(path) + if lcpath not in lcpathlist: + lcpathlist.append(lcpath) + winpathlist.append(path) + pathlist = winpathlist + ok = True + for path in set(pathlist): + if osp.isdir(path): + ok = self.find_files_in_path(path) + if not ok: + break + return ok + + def find_files_in_hg_manifest(self): + p = programs.run_shell_command('hg manifest', cwd=self.rootpath) + hgroot = get_vcs_root(self.rootpath) + self.pathlist = [hgroot] + for path in p.stdout.read().decode().splitlines(): + with QMutexLocker(self.mutex): + if self.stopped: + return False + dirname = osp.dirname(path) + try: + if re.search(self.exclude, dirname+os.sep): + continue + filename = osp.basename(path) + if re.search(self.exclude, filename): + continue + if re.search(self.include, filename): + self.filenames.append(osp.join(hgroot, path)) + except re.error: + self.error_flag = _("invalid regular expression") + return False + return True + + def find_files_in_path(self, path): + if self.pathlist is None: + self.pathlist = [] + self.pathlist.append(path) + for path, dirs, files in os.walk(path): + with QMutexLocker(self.mutex): + if self.stopped: + return False + try: + for d in dirs[:]: + dirname = os.path.join(path, d) + if re.search(self.exclude, dirname+os.sep): + dirs.remove(d) + for f in files: + filename = os.path.join(path, f) + if re.search(self.exclude, filename): + continue + if re.search(self.include, filename): + self.filenames.append(filename) + except re.error: + self.error_flag = _("invalid regular expression") + return False + return True + + def find_string_in_files(self): + self.results = {} + self.nb = 0 + self.error_flag = False + for fname in self.filenames: + with QMutexLocker(self.mutex): + if self.stopped: + return + try: + for lineno, line in enumerate(open(fname, 'rb')): + for text, enc in self.texts: + if self.text_re: + found = re.search(text, line) + if found is not None: + break + else: + found = line.find(text) + if found > -1: + break + try: + line_dec = line.decode(enc) + except UnicodeDecodeError: + line_dec = line + if self.text_re: + for match in re.finditer(text, line): + res = self.results.get(osp.abspath(fname), []) + res.append((lineno+1, match.start(), line_dec)) + self.results[osp.abspath(fname)] = res + self.nb += 1 + else: + while found > -1: + res = self.results.get(osp.abspath(fname), []) + res.append((lineno+1, found, line_dec)) + self.results[osp.abspath(fname)] = res + for text, enc in self.texts: + found = line.find(text, found+1) + if found > -1: + break + self.nb += 1 + except IOError as xxx_todo_changeme: + (_errno, _strerror) = xxx_todo_changeme.args + self.error_flag = _("permission denied errors were encountered") + except re.error: + self.error_flag = _("invalid regular expression") + self.completed = True + + def get_results(self): + return self.results, self.pathlist, self.nb, self.error_flag + + +class FindOptions(QWidget): + """Find widget with options""" + find = Signal() + stop = Signal() + + def __init__(self, parent, search_text, search_text_regexp, search_path, + include, include_idx, include_regexp, + exclude, exclude_idx, exclude_regexp, + supported_encodings, in_python_path, more_options): + QWidget.__init__(self, parent) + + if search_path is None: + search_path = getcwd() + + if not isinstance(search_text, (list, tuple)): + search_text = [search_text] + if not isinstance(search_path, (list, tuple)): + search_path = [search_path] + if not isinstance(include, (list, tuple)): + include = [include] + if not isinstance(exclude, (list, tuple)): + exclude = [exclude] + + self.supported_encodings = supported_encodings + + # Layout 1 + hlayout1 = QHBoxLayout() + self.search_text = PatternComboBox(self, search_text, + _("Search pattern")) + self.edit_regexp = create_toolbutton(self, + icon=ima.icon('advanced'), + tip=_('Regular expression')) + self.edit_regexp.setCheckable(True) + self.edit_regexp.setChecked(search_text_regexp) + self.more_widgets = () + self.more_options = create_toolbutton(self, + toggled=self.toggle_more_options) + self.more_options.setCheckable(True) + self.more_options.setChecked(more_options) + + self.ok_button = create_toolbutton(self, text=_("Search"), + icon=ima.icon('DialogApplyButton'), + triggered=lambda: self.find.emit(), + tip=_("Start search"), + text_beside_icon=True) + self.ok_button.clicked.connect(self.update_combos) + self.stop_button = create_toolbutton(self, text=_("Stop"), + icon=ima.icon('stop'), + triggered=lambda: self.stop.emit(), + tip=_("Stop search"), + text_beside_icon=True) + self.stop_button.setEnabled(False) + for widget in [self.search_text, self.edit_regexp, + self.ok_button, self.stop_button, self.more_options]: + hlayout1.addWidget(widget) + + # Layout 2 + hlayout2 = QHBoxLayout() + self.include_pattern = PatternComboBox(self, include, + _("Included filenames pattern")) + if include_idx is not None and include_idx >= 0 \ + and include_idx < self.include_pattern.count(): + self.include_pattern.setCurrentIndex(include_idx) + self.include_regexp = create_toolbutton(self, + icon=ima.icon('advanced'), + tip=_('Regular expression')) + self.include_regexp.setCheckable(True) + self.include_regexp.setChecked(include_regexp) + include_label = QLabel(_("Include:")) + include_label.setBuddy(self.include_pattern) + self.exclude_pattern = PatternComboBox(self, exclude, + _("Excluded filenames pattern")) + if exclude_idx is not None and exclude_idx >= 0 \ + and exclude_idx < self.exclude_pattern.count(): + self.exclude_pattern.setCurrentIndex(exclude_idx) + self.exclude_regexp = create_toolbutton(self, + icon=ima.icon('advanced'), + tip=_('Regular expression')) + self.exclude_regexp.setCheckable(True) + self.exclude_regexp.setChecked(exclude_regexp) + exclude_label = QLabel(_("Exclude:")) + exclude_label.setBuddy(self.exclude_pattern) + for widget in [include_label, self.include_pattern, + self.include_regexp, + exclude_label, self.exclude_pattern, + self.exclude_regexp]: + hlayout2.addWidget(widget) + + # Layout 3 + hlayout3 = QHBoxLayout() + self.python_path = QRadioButton(_("PYTHONPATH"), self) + self.python_path.setChecked(in_python_path) + self.python_path.setToolTip(_( + "Search in all directories listed in sys.path which" + " are outside the Python installation directory")) + self.hg_manifest = QRadioButton(_("Hg repository"), self) + self.detect_hg_repository() + self.hg_manifest.setToolTip( + _("Search in current directory hg repository")) + self.custom_dir = QRadioButton(_("Here:"), self) + self.custom_dir.setChecked(not in_python_path) + self.dir_combo = PathComboBox(self) + self.dir_combo.addItems(search_path) + self.dir_combo.setToolTip(_("Search recursively in this directory")) + self.dir_combo.open_dir.connect(self.set_directory) + self.python_path.toggled.connect(self.dir_combo.setDisabled) + self.hg_manifest.toggled.connect(self.dir_combo.setDisabled) + browse = create_toolbutton(self, icon=ima.icon('DirOpenIcon'), + tip=_('Browse a search directory'), + triggered=self.select_directory) + for widget in [self.python_path, self.hg_manifest, self.custom_dir, + self.dir_combo, browse]: + hlayout3.addWidget(widget) + + self.search_text.valid.connect(lambda valid: self.find.emit()) + self.include_pattern.valid.connect(lambda valid: self.find.emit()) + self.exclude_pattern.valid.connect(lambda valid: self.find.emit()) + self.dir_combo.valid.connect(lambda valid: self.find.emit()) + + vlayout = QVBoxLayout() + vlayout.setContentsMargins(0, 0, 0, 0) + vlayout.addLayout(hlayout1) + vlayout.addLayout(hlayout2) + vlayout.addLayout(hlayout3) + self.more_widgets = (hlayout2, hlayout3) + self.toggle_more_options(more_options) + self.setLayout(vlayout) + + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) + + @Slot(bool) + def toggle_more_options(self, state): + for layout in self.more_widgets: + for index in range(layout.count()): + if state and self.isVisible() or not state: + layout.itemAt(index).widget().setVisible(state) + if state: + icon = ima.icon('options_less') + tip = _('Hide advanced options') + else: + icon = ima.icon('options_more') + tip = _('Show advanced options') + self.more_options.setIcon(icon) + self.more_options.setToolTip(tip) + + def update_combos(self): + self.search_text.lineEdit().returnPressed.emit() + self.include_pattern.lineEdit().returnPressed.emit() + self.exclude_pattern.lineEdit().returnPressed.emit() + + def detect_hg_repository(self, path=None): + if path is None: + path = getcwd() + hg_repository = is_hg_installed() and get_vcs_root(path) is not None + self.hg_manifest.setEnabled(hg_repository) + if not hg_repository and self.hg_manifest.isChecked(): + self.custom_dir.setChecked(True) + + def set_search_text(self, text): + if text: + self.search_text.add_text(text) + self.search_text.lineEdit().selectAll() + self.search_text.setFocus() + + def get_options(self, all=False): + # Getting options + utext = to_text_string(self.search_text.currentText()) + if not utext: + return + try: + texts = [(utext.encode('ascii'), 'ascii')] + except UnicodeEncodeError: + texts = [] + for enc in self.supported_encodings: + try: + texts.append((utext.encode(enc), enc)) + except UnicodeDecodeError: + pass + text_re = self.edit_regexp.isChecked() + include = to_text_string(self.include_pattern.currentText()) + include_re = self.include_regexp.isChecked() + exclude = to_text_string(self.exclude_pattern.currentText()) + exclude_re = self.exclude_regexp.isChecked() + python_path = self.python_path.isChecked() + hg_manifest = self.hg_manifest.isChecked() + path = osp.abspath( to_text_string( self.dir_combo.currentText() ) ) + + # Finding text occurrences + if not include_re: + include = fnmatch.translate(include) + if not exclude_re: + exclude = fnmatch.translate(exclude) + + if all: + search_text = [to_text_string(self.search_text.itemText(index)) \ + for index in range(self.search_text.count())] + search_path = [to_text_string(self.dir_combo.itemText(index)) \ + for index in range(self.dir_combo.count())] + include = [to_text_string(self.include_pattern.itemText(index)) \ + for index in range(self.include_pattern.count())] + include_idx = self.include_pattern.currentIndex() + exclude = [to_text_string(self.exclude_pattern.itemText(index)) \ + for index in range(self.exclude_pattern.count())] + exclude_idx = self.exclude_pattern.currentIndex() + more_options = self.more_options.isChecked() + return (search_text, text_re, search_path, + include, include_idx, include_re, + exclude, exclude_idx, exclude_re, + python_path, more_options) + else: + return (path, python_path, hg_manifest, + include, exclude, texts, text_re) + + @Slot() + def select_directory(self): + """Select directory""" + self.parent().redirect_stdio.emit(False) + directory = getexistingdirectory(self, _("Select directory"), + self.dir_combo.currentText()) + if directory: + self.set_directory(directory) + self.parent().redirect_stdio.emit(True) + + def set_directory(self, directory): + path = to_text_string(osp.abspath(to_text_string(directory))) + self.dir_combo.setEditText(path) + self.detect_hg_repository(path) + + def keyPressEvent(self, event): + """Reimplemented to handle key events""" + ctrl = event.modifiers() & Qt.ControlModifier + shift = event.modifiers() & Qt.ShiftModifier + if event.key() in (Qt.Key_Enter, Qt.Key_Return): + self.find.emit() + elif event.key() == Qt.Key_F and ctrl and shift: + # Toggle find widgets + self.parent().toggle_visibility.emit(not self.isVisible()) + else: + QWidget.keyPressEvent(self, event) + + +class ResultsBrowser(OneColumnTree): + def __init__(self, parent): + OneColumnTree.__init__(self, parent) + self.search_text = None + self.results = None + self.nb = None + self.error_flag = None + self.completed = None + self.data = None + self.set_title('') + self.root_items = None + + def activated(self, item): + """Double-click event""" + itemdata = self.data.get(id(self.currentItem())) + if itemdata is not None: + filename, lineno = itemdata + self.parent().edit_goto.emit(filename, lineno, self.search_text) + + def clicked(self, item): + """Click event""" + self.activated(item) + + def set_results(self, search_text, results, pathlist, nb, + error_flag, completed): + self.search_text = search_text + self.results = results + self.pathlist = pathlist + self.nb = nb + self.error_flag = error_flag + self.completed = completed + self.refresh() + if not self.error_flag and self.nb: + self.restore() + + def refresh(self): + """ + Refreshing search results panel + """ + title = "'%s' - " % self.search_text + if self.results is None: + text = _('Search canceled') + else: + nb_files = len(self.results) + if nb_files == 0: + text = _('String not found') + else: + text_matches = _('matches in') + text_files = _('file') + if nb_files > 1: + text_files += 's' + text = "%d %s %d %s" % (self.nb, text_matches, + nb_files, text_files) + if self.error_flag: + text += ' (' + self.error_flag + ')' + elif self.results is not None and not self.completed: + text += ' (' + _('interrupted') + ')' + self.set_title(title+text) + self.clear() + self.data = {} + + if not self.results: # First search interrupted *or* No result + return + + # Directory set + dir_set = set() + for filename in sorted(self.results.keys()): + dirname = osp.abspath(osp.dirname(filename)) + dir_set.add(dirname) + + # Root path + root_path_list = None + _common = get_common_path(list(dir_set)) + if _common is not None: + root_path_list = [_common] + else: + _common = get_common_path(self.pathlist) + if _common is not None: + root_path_list = [_common] + else: + root_path_list = self.pathlist + if not root_path_list: + return + for _root_path in root_path_list: + dir_set.add(_root_path) + # Populating tree: directories + def create_dir_item(dirname, parent): + if dirname not in root_path_list: + displayed_name = osp.basename(dirname) + else: + displayed_name = dirname + item = QTreeWidgetItem(parent, [displayed_name], + QTreeWidgetItem.Type) + item.setIcon(0, ima.icon('DirClosedIcon')) + return item + dirs = {} + for dirname in sorted(list(dir_set)): + if dirname in root_path_list: + parent = self + else: + parent_dirname = abspardir(dirname) + parent = dirs.get(parent_dirname) + if parent is None: + # This is related to directories which contain found + # results only in some of their children directories + if osp.commonprefix([dirname]+root_path_list): + # create new root path + pass + items_to_create = [] + while dirs.get(parent_dirname) is None: + items_to_create.append(parent_dirname) + parent_dirname = abspardir(parent_dirname) + items_to_create.reverse() + for item_dir in items_to_create: + item_parent = dirs[abspardir(item_dir)] + dirs[item_dir] = create_dir_item(item_dir, item_parent) + parent_dirname = abspardir(dirname) + parent = dirs[parent_dirname] + dirs[dirname] = create_dir_item(dirname, parent) + self.root_items = [dirs[_root_path] for _root_path in root_path_list] + # Populating tree: files + for filename in sorted(self.results.keys()): + parent_item = dirs[osp.dirname(filename)] + file_item = QTreeWidgetItem(parent_item, [osp.basename(filename)], + QTreeWidgetItem.Type) + file_item.setIcon(0, get_filetype_icon(filename)) + colno_dict = {} + fname_res = [] + for lineno, colno, line in self.results[filename]: + if lineno not in colno_dict: + fname_res.append((lineno, colno, line)) + colno_dict[lineno] = colno_dict.get(lineno, [])+[str(colno)] + for lineno, colno, line in fname_res: + colno_str = ",".join(colno_dict[lineno]) + item = QTreeWidgetItem(file_item, + ["%d (%s): %s" % (lineno, colno_str, line.rstrip())], + QTreeWidgetItem.Type) + item.setIcon(0, ima.icon('arrow')) + self.data[id(item)] = (filename, lineno) + # Removing empty directories + top_level_items = [self.topLevelItem(index) + for index in range(self.topLevelItemCount())] + for item in top_level_items: + if not item.childCount(): + self.takeTopLevelItem(self.indexOfTopLevelItem(item)) + + +class FindInFilesWidget(QWidget): + """ + Find in files widget + """ + def __init__(self, parent, + search_text = r"# ?TODO|# ?FIXME|# ?XXX", + search_text_regexp=True, search_path=None, + include=[".", ".py"], include_idx=None, include_regexp=True, + exclude=r"\.pyc$|\.orig$|\.hg|\.svn", exclude_idx=None, + exclude_regexp=True, + supported_encodings=("utf-8", "iso-8859-1", "cp1252"), + in_python_path=False, more_options=False): + QWidget.__init__(self, parent) + + self.setWindowTitle(_('Find in files')) + + self.search_thread = None + self.get_pythonpath_callback = None + + self.find_options = FindOptions(self, search_text, search_text_regexp, + search_path, + include, include_idx, include_regexp, + exclude, exclude_idx, exclude_regexp, + supported_encodings, in_python_path, + more_options) + self.find_options.find.connect(self.find) + self.find_options.stop.connect(self.stop_and_reset_thread) + + self.result_browser = ResultsBrowser(self) + + collapse_btn = create_toolbutton(self) + collapse_btn.setDefaultAction(self.result_browser.collapse_all_action) + expand_btn = create_toolbutton(self) + expand_btn.setDefaultAction(self.result_browser.expand_all_action) + restore_btn = create_toolbutton(self) + restore_btn.setDefaultAction(self.result_browser.restore_action) +# collapse_sel_btn = create_toolbutton(self) +# collapse_sel_btn.setDefaultAction( +# self.result_browser.collapse_selection_action) +# expand_sel_btn = create_toolbutton(self) +# expand_sel_btn.setDefaultAction( +# self.result_browser.expand_selection_action) + + btn_layout = QVBoxLayout() + btn_layout.setAlignment(Qt.AlignTop) + for widget in [collapse_btn, expand_btn, restore_btn]: +# collapse_sel_btn, expand_sel_btn]: + btn_layout.addWidget(widget) + + hlayout = QHBoxLayout() + hlayout.addWidget(self.result_browser) + hlayout.addLayout(btn_layout) + + layout = QVBoxLayout() + left, _x, right, bottom = layout.getContentsMargins() + layout.setContentsMargins(left, 0, right, bottom) + layout.addWidget(self.find_options) + layout.addLayout(hlayout) + self.setLayout(layout) + + def set_search_text(self, text): + """Set search pattern""" + self.find_options.set_search_text(text) + + def find(self): + """Call the find function""" + options = self.find_options.get_options() + if options is None: + return + self.stop_and_reset_thread(ignore_results=True) + self.search_thread = SearchThread(self) + self.search_thread.get_pythonpath_callback = \ + self.get_pythonpath_callback + self.search_thread.sig_finished.connect(self.search_complete) + self.search_thread.initialize(*options) + self.search_thread.start() + self.find_options.ok_button.setEnabled(False) + self.find_options.stop_button.setEnabled(True) + + def stop_and_reset_thread(self, ignore_results=False): + """Stop current search thread and clean-up""" + if self.search_thread is not None: + if self.search_thread.isRunning(): + if ignore_results: + self.search_thread.sig_finished.disconnect( + self.search_complete) + self.search_thread.stop() + self.search_thread.wait() + self.search_thread.setParent(None) + self.search_thread = None + + def closing_widget(self): + """Perform actions before widget is closed""" + self.stop_and_reset_thread(ignore_results=True) + + def search_complete(self, completed): + """Current search thread has finished""" + self.find_options.ok_button.setEnabled(True) + self.find_options.stop_button.setEnabled(False) + if self.search_thread is None: + return + found = self.search_thread.get_results() + self.stop_and_reset_thread() + if found is not None: + results, pathlist, nb, error_flag = found + search_text = to_text_string( + self.find_options.search_text.currentText()) + self.result_browser.set_results(search_text, results, pathlist, + nb, error_flag, completed) + self.result_browser.show() + + +def test(): + """Run Find in Files widget test""" + from spyder.utils.qthelpers import qapplication + app = qapplication() + widget = FindInFilesWidget(None) + widget.resize(640, 480) + widget.show() + sys.exit(app.exec_()) + + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/findreplace.py spyder-3.0.2+dfsg1/spyder/widgets/findreplace.py --- spyder-2.3.8+dfsg1/spyder/widgets/findreplace.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/findreplace.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,411 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Find/Replace widget""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import re + +# Third party imports +from qtpy.QtCore import Qt, QTimer, Signal, Slot +from qtpy.QtGui import QTextCursor +from qtpy.QtWidgets import (QCheckBox, QGridLayout, QHBoxLayout, QLabel, + QSizePolicy, QWidget) + +# Local imports +from spyder.config.base import _ +from spyder.config.gui import config_shortcut, fixed_shortcut +from spyder.py3compat import to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import create_toolbutton, get_icon +from spyder.widgets.comboboxes import PatternComboBox + + +def is_position_sup(pos1, pos2): + """Return True is pos1 > pos2""" + return pos1 > pos2 + +def is_position_inf(pos1, pos2): + """Return True is pos1 < pos2""" + return pos1 < pos2 + + +class FindReplace(QWidget): + """Find widget""" + STYLE = {False: "background-color:rgb(255, 175, 90);", + True: "", + None: ""} + visibility_changed = Signal(bool) + + def __init__(self, parent, enable_replace=False): + QWidget.__init__(self, parent) + self.enable_replace = enable_replace + self.editor = None + self.is_code_editor = None + + glayout = QGridLayout() + glayout.setContentsMargins(0, 0, 0, 0) + self.setLayout(glayout) + + self.close_button = create_toolbutton(self, triggered=self.hide, + icon=ima.icon('DialogCloseButton')) + glayout.addWidget(self.close_button, 0, 0) + + # Find layout + self.search_text = PatternComboBox(self, tip=_("Search string"), + adjust_to_minimum=False) + self.search_text.valid.connect( + lambda state: + self.find(changed=False, forward=True, rehighlight=False)) + self.search_text.lineEdit().textEdited.connect( + self.text_has_been_edited) + + self.previous_button = create_toolbutton(self, + triggered=self.find_previous, + icon=ima.icon('ArrowUp')) + self.next_button = create_toolbutton(self, + triggered=self.find_next, + icon=ima.icon('ArrowDown')) + self.next_button.clicked.connect(self.update_search_combo) + self.previous_button.clicked.connect(self.update_search_combo) + + self.re_button = create_toolbutton(self, icon=ima.icon('advanced'), + tip=_("Regular expression")) + self.re_button.setCheckable(True) + self.re_button.toggled.connect(lambda state: self.find()) + + self.case_button = create_toolbutton(self, + icon=get_icon("upper_lower.png"), + tip=_("Case Sensitive")) + self.case_button.setCheckable(True) + self.case_button.toggled.connect(lambda state: self.find()) + + self.words_button = create_toolbutton(self, + icon=get_icon("whole_words.png"), + tip=_("Whole words")) + self.words_button.setCheckable(True) + self.words_button.toggled.connect(lambda state: self.find()) + + self.highlight_button = create_toolbutton(self, + icon=get_icon("highlight.png"), + tip=_("Highlight matches")) + self.highlight_button.setCheckable(True) + self.highlight_button.toggled.connect(self.toggle_highlighting) + + hlayout = QHBoxLayout() + self.widgets = [self.close_button, self.search_text, + self.previous_button, self.next_button, + self.re_button, self.case_button, self.words_button, + self.highlight_button] + for widget in self.widgets[1:]: + hlayout.addWidget(widget) + glayout.addLayout(hlayout, 0, 1) + + # Replace layout + replace_with = QLabel(_("Replace with:")) + self.replace_text = PatternComboBox(self, adjust_to_minimum=False, + tip=_('Replace string')) + + self.replace_button = create_toolbutton(self, + text=_('Replace/find'), + icon=ima.icon('DialogApplyButton'), + triggered=self.replace_find, + text_beside_icon=True) + self.replace_button.clicked.connect(self.update_replace_combo) + self.replace_button.clicked.connect(self.update_search_combo) + + self.all_check = QCheckBox(_("Replace all")) + + self.replace_layout = QHBoxLayout() + widgets = [replace_with, self.replace_text, self.replace_button, + self.all_check] + for widget in widgets: + self.replace_layout.addWidget(widget) + glayout.addLayout(self.replace_layout, 1, 1) + self.widgets.extend(widgets) + self.replace_widgets = widgets + self.hide_replace() + + self.search_text.setTabOrder(self.search_text, self.replace_text) + + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + + self.shortcuts = self.create_shortcuts(parent) + + self.highlight_timer = QTimer(self) + self.highlight_timer.setSingleShot(True) + self.highlight_timer.setInterval(1000) + self.highlight_timer.timeout.connect(self.highlight_matches) + + def create_shortcuts(self, parent): + """Create shortcuts for this widget""" + # Configurable + findnext = config_shortcut(self.find_next, context='_', + name='Find next', parent=parent) + findprev = config_shortcut(self.find_previous, context='_', + name='Find previous', parent=parent) + togglefind = config_shortcut(self.show, context='_', + name='Find text', parent=parent) + togglereplace = config_shortcut(self.toggle_replace_widgets, + context='_', name='Replace text', + parent=parent) + # Fixed + fixed_shortcut("Escape", self, self.hide) + + return [findnext, findprev, togglefind, togglereplace] + + def get_shortcut_data(self): + """ + Returns shortcut data, a list of tuples (shortcut, text, default) + shortcut (QShortcut or QAction instance) + text (string): action/shortcut description + default (string): default key sequence + """ + return [sc.data for sc in self.shortcuts] + + def update_search_combo(self): + self.search_text.lineEdit().returnPressed.emit() + + def update_replace_combo(self): + self.replace_text.lineEdit().returnPressed.emit() + + def toggle_replace_widgets(self): + if self.enable_replace: + # Toggle replace widgets + if self.replace_widgets[0].isVisible(): + self.hide_replace() + self.hide() + else: + self.show_replace() + self.replace_text.setFocus() + + @Slot(bool) + def toggle_highlighting(self, state): + """Toggle the 'highlight all results' feature""" + if self.editor is not None: + if state: + self.highlight_matches() + else: + self.clear_matches() + + def show(self): + """Overrides Qt Method""" + QWidget.show(self) + self.visibility_changed.emit(True) + if self.editor is not None: + text = self.editor.get_selected_text() + + # If no text is highlighted for search, use whatever word is under + # the cursor + if not text: + try: + cursor = self.editor.textCursor() + cursor.select(QTextCursor.WordUnderCursor) + text = to_text_string(cursor.selectedText()) + except AttributeError: + # We can't do this for all widgets, e.g. WebView's + pass + + # Now that text value is sorted out, use it for the search + if text: + self.search_text.setEditText(text) + self.search_text.lineEdit().selectAll() + self.refresh() + else: + self.search_text.lineEdit().selectAll() + self.search_text.setFocus() + + @Slot() + def hide(self): + """Overrides Qt Method""" + for widget in self.replace_widgets: + widget.hide() + QWidget.hide(self) + self.visibility_changed.emit(False) + if self.editor is not None: + self.editor.setFocus() + self.clear_matches() + + def show_replace(self): + """Show replace widgets""" + self.show() + for widget in self.replace_widgets: + widget.show() + + def hide_replace(self): + """Hide replace widgets""" + for widget in self.replace_widgets: + widget.hide() + + def refresh(self): + """Refresh widget""" + if self.isHidden(): + if self.editor is not None: + self.clear_matches() + return + state = self.editor is not None + for widget in self.widgets: + widget.setEnabled(state) + if state: + self.find() + + def set_editor(self, editor, refresh=True): + """ + Set associated editor/web page: + codeeditor.base.TextEditBaseWidget + browser.WebView + """ + self.editor = editor + # Note: This is necessary to test widgets/editor.py + # in Qt builds that don't have web widgets + try: + from qtpy.QtWebEngineWidgets import QWebEngineView + except ImportError: + QWebEngineView = type(None) + self.words_button.setVisible(not isinstance(editor, QWebEngineView)) + self.re_button.setVisible(not isinstance(editor, QWebEngineView)) + from spyder.widgets.sourcecode.codeeditor import CodeEditor + self.is_code_editor = isinstance(editor, CodeEditor) + self.highlight_button.setVisible(self.is_code_editor) + if refresh: + self.refresh() + if self.isHidden() and editor is not None: + self.clear_matches() + + @Slot() + def find_next(self): + """Find next occurrence""" + state = self.find(changed=False, forward=True, rehighlight=False) + self.editor.setFocus() + self.search_text.add_current_text() + return state + + @Slot() + def find_previous(self): + """Find previous occurrence""" + state = self.find(changed=False, forward=False, rehighlight=False) + self.editor.setFocus() + return state + + def text_has_been_edited(self, text): + """Find text has been edited (this slot won't be triggered when + setting the search pattern combo box text programmatically""" + self.find(changed=True, forward=True, start_highlight_timer=True) + + def highlight_matches(self): + """Highlight found results""" + if self.is_code_editor and self.highlight_button.isChecked(): + text = self.search_text.currentText() + words = self.words_button.isChecked() + regexp = self.re_button.isChecked() + self.editor.highlight_found_results(text, words=words, + regexp=regexp) + + def clear_matches(self): + """Clear all highlighted matches""" + if self.is_code_editor: + self.editor.clear_found_results() + + def find(self, changed=True, forward=True, + rehighlight=True, start_highlight_timer=False): + """Call the find function""" + text = self.search_text.currentText() + if len(text) == 0: + self.search_text.lineEdit().setStyleSheet("") + if not self.is_code_editor: + # Clears the selection for WebEngine + self.editor.find_text('') + return None + else: + case = self.case_button.isChecked() + words = self.words_button.isChecked() + regexp = self.re_button.isChecked() + found = self.editor.find_text(text, changed, forward, case=case, + words=words, regexp=regexp) + self.search_text.lineEdit().setStyleSheet(self.STYLE[found]) + if self.is_code_editor and found: + if rehighlight or not self.editor.found_results: + self.highlight_timer.stop() + if start_highlight_timer: + self.highlight_timer.start() + else: + self.highlight_matches() + else: + self.clear_matches() + return found + + @Slot() + def replace_find(self): + """Replace and find""" + if (self.editor is not None): + replace_text = to_text_string(self.replace_text.currentText()) + search_text = to_text_string(self.search_text.currentText()) + pattern = search_text if self.re_button.isChecked() else None + case = self.case_button.isChecked() + first = True + cursor = None + while True: + if first: + # First found + seltxt = to_text_string(self.editor.get_selected_text()) + cmptxt1 = search_text if case else search_text.lower() + cmptxt2 = seltxt if case else seltxt.lower() + if self.editor.has_selected_text() and cmptxt1 == cmptxt2: + # Text was already found, do nothing + pass + else: + if not self.find(changed=False, forward=True, + rehighlight=False): + break + first = False + wrapped = False + position = self.editor.get_position('cursor') + position0 = position + cursor = self.editor.textCursor() + cursor.beginEditBlock() + else: + position1 = self.editor.get_position('cursor') + if is_position_inf(position1, + position0 + len(replace_text) - + len(search_text) + 1): + # Identify wrapping even when the replace string + # includes part of the search string + wrapped = True + if wrapped: + if position1 == position or \ + is_position_sup(position1, position): + # Avoid infinite loop: replace string includes + # part of the search string + break + if position1 == position0: + # Avoid infinite loop: single found occurrence + break + position0 = position1 + if pattern is None: + cursor.removeSelectedText() + cursor.insertText(replace_text) + else: + seltxt = to_text_string(cursor.selectedText()) + cursor.removeSelectedText() + cursor.insertText(re.sub(pattern, replace_text, seltxt)) + if self.find_next(): + found_cursor = self.editor.textCursor() + cursor.setPosition(found_cursor.selectionStart(), + QTextCursor.MoveAnchor) + cursor.setPosition(found_cursor.selectionEnd(), + QTextCursor.KeepAnchor) + else: + break + if not self.all_check.isChecked(): + break + self.all_check.setCheckState(Qt.Unchecked) + if cursor is not None: + cursor.endEditBlock() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/formlayout.py spyder-3.0.2+dfsg1/spyder/widgets/formlayout.py --- spyder-2.3.8+dfsg1/spyder/widgets/formlayout.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/formlayout.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,595 @@ +# -*- coding: utf-8 -*- +""" +formlayout +========== + +Module creating Qt form dialogs/layouts to edit various type of parameters + + +formlayout License Agreement (MIT License) +------------------------------------------ + +Copyright (c) 2009 Pierre Raybaut + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +""" + +# History: +# 1.0.15: added support for multiline strings +# 1.0.14: fixed Python 3 support (regression in 1.0.13) +# 1.0.13: replaced obsolete QColorDialog.getRgba function and fixed other +# compatibility issues with PySide (see Issue 8 of formlayout website) +# 1.0.12: added support for Python 3 +# 1.0.11: added support for PySide +# 1.0.10: added float validator: disable "OK" and "Apply" button when not valid +# 1.0.7: added support for "Apply" button +# 1.0.6: code cleaning + +# Standard library imports +from __future__ import print_function +import datetime +import os + +# Third party imports +try: + from qtpy.QtWidgets import QFormLayout +except ImportError: + raise ImportError("Warning: formlayout requires PyQt4 >v4.3") + +from qtpy.QtCore import Property, QSize, Qt, Signal, Slot +from qtpy.QtGui import (QColor, QDoubleValidator, QFont, QFontDatabase, QIcon, + QPixmap) +from qtpy.QtWidgets import (QApplication, QCheckBox, QColorDialog, QComboBox, + QDateEdit, QDateTimeEdit, QDialog, + QDialogButtonBox, QFontComboBox, QGridLayout, + QHBoxLayout, QLabel, QLineEdit, QPushButton, + QSpinBox, QStackedWidget, QStyle, QTabWidget, + QTextEdit, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import _, DEBUG, STDERR +from spyder.py3compat import is_string, is_text_string, to_text_string + + +__version__ = '1.0.15' +__license__ = __doc__ +DEBUG_FORMLAYOUT = DEBUG >= 2 + + +class ColorButton(QPushButton): + """ + Color choosing push button + """ + __pyqtSignals__ = ("colorChanged(QColor)",) + + def __init__(self, parent=None): + QPushButton.__init__(self, parent) + self.setFixedSize(20, 20) + self.setIconSize(QSize(12, 12)) + self.clicked.connect(self.choose_color) + self._color = QColor() + + def choose_color(self): + color = QColorDialog.getColor(self._color, self.parentWidget()) + if color.isValid(): + self.set_color(color) + + def get_color(self): + return self._color + + @Slot(QColor) + def set_color(self, color): + if color != self._color: + self._color = color + self.colorChanged.emit(self._color) + pixmap = QPixmap(self.iconSize()) + pixmap.fill(color) + self.setIcon(QIcon(pixmap)) + + color = Property("QColor", get_color, set_color) + + +def text_to_qcolor(text): + """ + Create a QColor from specified string + Avoid warning from Qt when an invalid QColor is instantiated + """ + color = QColor() + if not is_string(text): # testing for QString (PyQt API#1) + text = str(text) + if not is_text_string(text): + return color + if text.startswith('#') and len(text)==7: + correct = '#0123456789abcdef' + for char in text: + if char.lower() not in correct: + return color + elif text not in list(QColor.colorNames()): + return color + color.setNamedColor(text) + return color + + +class ColorLayout(QHBoxLayout): + """Color-specialized QLineEdit layout""" + def __init__(self, color, parent=None): + QHBoxLayout.__init__(self) + assert isinstance(color, QColor) + self.lineedit = QLineEdit(color.name(), parent) + self.lineedit.textChanged.connect(self.update_color) + self.addWidget(self.lineedit) + self.colorbtn = ColorButton(parent) + self.colorbtn.color = color + self.colorbtn.colorChanged.connect(self.update_text) + self.addWidget(self.colorbtn) + + def update_color(self, text): + color = text_to_qcolor(text) + if color.isValid(): + self.colorbtn.color = color + + def update_text(self, color): + self.lineedit.setText(color.name()) + + def text(self): + return self.lineedit.text() + + +def font_is_installed(font): + """Check if font is installed""" + return [fam for fam in QFontDatabase().families() + if to_text_string(fam) == font] + +def tuple_to_qfont(tup): + """ + Create a QFont from tuple: + (family [string], size [int], italic [bool], bold [bool]) + """ + if not isinstance(tup, tuple) or len(tup) != 4 \ + or not font_is_installed(tup[0]) \ + or not isinstance(tup[1], int) \ + or not isinstance(tup[2], bool) \ + or not isinstance(tup[3], bool): + return None + font = QFont() + family, size, italic, bold = tup + font.setFamily(family) + font.setPointSize(size) + font.setItalic(italic) + font.setBold(bold) + return font + +def qfont_to_tuple(font): + return (to_text_string(font.family()), int(font.pointSize()), + font.italic(), font.bold()) + +class FontLayout(QGridLayout): + """Font selection""" + def __init__(self, value, parent=None): + QGridLayout.__init__(self) + font = tuple_to_qfont(value) + assert font is not None + + # Font family + self.family = QFontComboBox(parent) + self.family.setCurrentFont(font) + self.addWidget(self.family, 0, 0, 1, -1) + + # Font size + self.size = QComboBox(parent) + self.size.setEditable(True) + sizelist = list(range(6, 12)) + list(range(12, 30, 2)) + [36, 48, 72] + size = font.pointSize() + if size not in sizelist: + sizelist.append(size) + sizelist.sort() + self.size.addItems([str(s) for s in sizelist]) + self.size.setCurrentIndex(sizelist.index(size)) + self.addWidget(self.size, 1, 0) + + # Italic or not + self.italic = QCheckBox(_("Italic"), parent) + self.italic.setChecked(font.italic()) + self.addWidget(self.italic, 1, 1) + + # Bold or not + self.bold = QCheckBox(_("Bold"), parent) + self.bold.setChecked(font.bold()) + self.addWidget(self.bold, 1, 2) + + def get_font(self): + font = self.family.currentFont() + font.setItalic(self.italic.isChecked()) + font.setBold(self.bold.isChecked()) + font.setPointSize(int(self.size.currentText())) + return qfont_to_tuple(font) + + +def is_edit_valid(edit): + text = edit.text() + state, _t = edit.validator().validate(text, 0) + return state == QDoubleValidator.Acceptable + +class FormWidget(QWidget): + update_buttons = Signal() + + def __init__(self, data, comment="", parent=None): + QWidget.__init__(self, parent) + from copy import deepcopy + self.data = deepcopy(data) + self.widgets = [] + self.formlayout = QFormLayout(self) + if comment: + self.formlayout.addRow(QLabel(comment)) + self.formlayout.addRow(QLabel(" ")) + if DEBUG_FORMLAYOUT: + print("\n"+("*"*80)) + print("DATA:", self.data) + print("*"*80) + print("COMMENT:", comment) + print("*"*80) + + def get_dialog(self): + """Return FormDialog instance""" + dialog = self.parent() + while not isinstance(dialog, QDialog): + dialog = dialog.parent() + return dialog + + def setup(self): + for label, value in self.data: + if DEBUG_FORMLAYOUT: + print("value:", value) + if label is None and value is None: + # Separator: (None, None) + self.formlayout.addRow(QLabel(" "), QLabel(" ")) + self.widgets.append(None) + continue + elif label is None: + # Comment + self.formlayout.addRow(QLabel(value)) + self.widgets.append(None) + continue + elif tuple_to_qfont(value) is not None: + field = FontLayout(value, self) + elif text_to_qcolor(value).isValid(): + field = ColorLayout(QColor(value), self) + elif is_text_string(value): + if '\n' in value: + for linesep in (os.linesep, '\n'): + if linesep in value: + value = value.replace(linesep, u"\u2029") + field = QTextEdit(value, self) + else: + field = QLineEdit(value, self) + elif isinstance(value, (list, tuple)): + value = list(value) # in case this is a tuple + selindex = value.pop(0) + field = QComboBox(self) + if isinstance(value[0], (list, tuple)): + keys = [ key for key, _val in value ] + value = [ val for _key, val in value ] + else: + keys = value + field.addItems(value) + if selindex in value: + selindex = value.index(selindex) + elif selindex in keys: + selindex = keys.index(selindex) + elif not isinstance(selindex, int): + print("Warning: '%s' index is invalid (label: "\ + "%s, value: %s)" % (selindex, label, value), + file=STDERR) + selindex = 0 + field.setCurrentIndex(selindex) + elif isinstance(value, bool): + field = QCheckBox(self) + field.setCheckState(Qt.Checked if value else Qt.Unchecked) + elif isinstance(value, float): + field = QLineEdit(repr(value), self) + field.setValidator(QDoubleValidator(field)) + dialog = self.get_dialog() + dialog.register_float_field(field) + field.textChanged.connect(lambda text: dialog.update_buttons()) + elif isinstance(value, int): + field = QSpinBox(self) + field.setRange(-1e9, 1e9) + field.setValue(value) + elif isinstance(value, datetime.datetime): + field = QDateTimeEdit(self) + field.setDateTime(value) + elif isinstance(value, datetime.date): + field = QDateEdit(self) + field.setDate(value) + else: + field = QLineEdit(repr(value), self) + self.formlayout.addRow(label, field) + self.widgets.append(field) + + def get(self): + valuelist = [] + for index, (label, value) in enumerate(self.data): + field = self.widgets[index] + if label is None: + # Separator / Comment + continue + elif tuple_to_qfont(value) is not None: + value = field.get_font() + elif is_text_string(value): + if isinstance(field, QTextEdit): + value = to_text_string(field.toPlainText() + ).replace(u"\u2029", os.linesep) + else: + value = to_text_string(field.text()) + elif isinstance(value, (list, tuple)): + index = int(field.currentIndex()) + if isinstance(value[0], int): + # Return an int index, if initialization was an int + value = index + else: + value = value[index+1] + if isinstance(value, (list, tuple)): + value = value[0] + elif isinstance(value, bool): + value = field.checkState() == Qt.Checked + elif isinstance(value, float): + value = float(field.text()) + elif isinstance(value, int): + value = int(field.value()) + elif isinstance(value, datetime.datetime): + value = field.dateTime() + try: + value = value.toPyDateTime() # PyQt + except AttributeError: + value = value.toPython() # PySide + elif isinstance(value, datetime.date): + value = field.date() + try: + value = value.toPyDate() # PyQt + except AttributeError: + value = value.toPython() # PySide + else: + value = eval(str(field.text())) + valuelist.append(value) + return valuelist + + +class FormComboWidget(QWidget): + update_buttons = Signal() + + def __init__(self, datalist, comment="", parent=None): + QWidget.__init__(self, parent) + layout = QVBoxLayout() + self.setLayout(layout) + self.combobox = QComboBox() + layout.addWidget(self.combobox) + + self.stackwidget = QStackedWidget(self) + layout.addWidget(self.stackwidget) + self.combobox.currentIndexChanged.connect( + self.stackwidget.setCurrentIndex) + + self.widgetlist = [] + for data, title, comment in datalist: + self.combobox.addItem(title) + widget = FormWidget(data, comment=comment, parent=self) + self.stackwidget.addWidget(widget) + self.widgetlist.append(widget) + + def setup(self): + for widget in self.widgetlist: + widget.setup() + + def get(self): + return [ widget.get() for widget in self.widgetlist] + + +class FormTabWidget(QWidget): + update_buttons = Signal() + + def __init__(self, datalist, comment="", parent=None): + QWidget.__init__(self, parent) + layout = QVBoxLayout() + self.tabwidget = QTabWidget() + layout.addWidget(self.tabwidget) + self.setLayout(layout) + self.widgetlist = [] + for data, title, comment in datalist: + if len(data[0])==3: + widget = FormComboWidget(data, comment=comment, parent=self) + else: + widget = FormWidget(data, comment=comment, parent=self) + index = self.tabwidget.addTab(widget, title) + self.tabwidget.setTabToolTip(index, comment) + self.widgetlist.append(widget) + + def setup(self): + for widget in self.widgetlist: + widget.setup() + + def get(self): + return [ widget.get() for widget in self.widgetlist] + + +class FormDialog(QDialog): + """Form Dialog""" + def __init__(self, data, title="", comment="", + icon=None, parent=None, apply=None): + QDialog.__init__(self, parent) + + self.apply_callback = apply + + # Form + if isinstance(data[0][0], (list, tuple)): + self.formwidget = FormTabWidget(data, comment=comment, + parent=self) + elif len(data[0])==3: + self.formwidget = FormComboWidget(data, comment=comment, + parent=self) + else: + self.formwidget = FormWidget(data, comment=comment, + parent=self) + layout = QVBoxLayout() + layout.addWidget(self.formwidget) + + self.float_fields = [] + self.formwidget.setup() + + # Button box + self.bbox = bbox = QDialogButtonBox(QDialogButtonBox.Ok + |QDialogButtonBox.Cancel) + self.formwidget.update_buttons.connect(self.update_buttons) + if self.apply_callback is not None: + apply_btn = bbox.addButton(QDialogButtonBox.Apply) + apply_btn.clicked.connect(self.apply) + bbox.accepted.connect(self.accept) + bbox.rejected.connect(self.reject) + layout.addWidget(bbox) + + self.setLayout(layout) + + self.setWindowTitle(title) + if not isinstance(icon, QIcon): + icon = QWidget().style().standardIcon(QStyle.SP_MessageBoxQuestion) + self.setWindowIcon(icon) + + def register_float_field(self, field): + self.float_fields.append(field) + + def update_buttons(self): + valid = True + for field in self.float_fields: + if not is_edit_valid(field): + valid = False + for btn_type in (QDialogButtonBox.Ok, QDialogButtonBox.Apply): + btn = self.bbox.button(btn_type) + if btn is not None: + btn.setEnabled(valid) + + @Slot() + def accept(self): + self.data = self.formwidget.get() + QDialog.accept(self) + + @Slot() + def reject(self): + self.data = None + QDialog.reject(self) + + def apply(self): + self.apply_callback(self.formwidget.get()) + + def get(self): + """Return form result""" + # It is import to avoid accessing Qt C++ object as it has probably + # already been destroyed, due to the Qt.WA_DeleteOnClose attribute + return self.data + + +def fedit(data, title="", comment="", icon=None, parent=None, apply=None): + """ + Create form dialog and return result + (if Cancel button is pressed, return None) + + data: datalist, datagroup + title: string + comment: string + icon: QIcon instance + parent: parent QWidget + apply: apply callback (function) + + datalist: list/tuple of (field_name, field_value) + datagroup: list/tuple of (datalist *or* datagroup, title, comment) + + -> one field for each member of a datalist + -> one tab for each member of a top-level datagroup + -> one page (of a multipage widget, each page can be selected with a combo + box) for each member of a datagroup inside a datagroup + + Supported types for field_value: + - int, float, str, unicode, bool + - colors: in Qt-compatible text form, i.e. in hex format or name (red,...) + (automatically detected from a string) + - list/tuple: + * the first element will be the selected index (or value) + * the other elements can be couples (key, value) or only values + """ + # Create a QApplication instance if no instance currently exists + # (e.g. if the module is used directly from the interpreter) + test_travis = os.environ.get('TEST_CI_WIDGETS', None) + if test_travis is not None: + from spyder.utils.qthelpers import qapplication + _app = qapplication(test_time=1) + elif QApplication.startingUp(): + _app = QApplication([]) + + dialog = FormDialog(data, title, comment, icon, parent, apply) + if dialog.exec_(): + return dialog.get() + + +if __name__ == "__main__": + + def create_datalist_example(): + return [('str', 'this is a string'), + ('str', """this is a + MULTILINE + string"""), + ('list', [0, '1', '3', '4']), + ('list2', ['--', ('none', 'None'), ('--', 'Dashed'), + ('-.', 'DashDot'), ('-', 'Solid'), + ('steps', 'Steps'), (':', 'Dotted')]), + ('float', 1.2), + (None, 'Other:'), + ('int', 12), + ('font', ('Arial', 10, False, True)), + ('color', '#123409'), + ('bool', True), + ('date', datetime.date(2010, 10, 10)), + ('datetime', datetime.datetime(2010, 10, 10)), + ] + + def create_datagroup_example(): + datalist = create_datalist_example() + return ((datalist, "Category 1", "Category 1 comment"), + (datalist, "Category 2", "Category 2 comment"), + (datalist, "Category 3", "Category 3 comment")) + + #--------- datalist example + datalist = create_datalist_example() + def apply_test(data): + print("data:", data) + print("result:", fedit(datalist, title="Example", + comment="This is just an example.", + apply=apply_test)) + + #--------- datagroup example + datagroup = create_datagroup_example() + print("result:", fedit(datagroup, "Global title")) + + #--------- datagroup inside a datagroup example + datalist = create_datalist_example() + datagroup = create_datagroup_example() + print("result:", fedit(((datagroup, "Title 1", "Tab 1 comment"), + (datalist, "Title 2", "Tab 2 comment"), + (datalist, "Title 3", "Tab 3 comment")), + "Global title")) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/helperwidgets.py spyder-3.0.2+dfsg1/spyder/widgets/helperwidgets.py --- spyder-2.3.8+dfsg1/spyder/widgets/helperwidgets.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/helperwidgets.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,260 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Helper widgets. +""" + +# Third party imports +from qtpy.QtCore import QPoint, QSize, Qt +from qtpy.QtGui import QAbstractTextDocumentLayout, QPainter, QTextDocument +from qtpy.QtWidgets import (QApplication, QCheckBox, QLineEdit, QMessageBox, + QSpacerItem, QStyle, QStyledItemDelegate, + QStyleOptionViewItem, QToolButton, QToolTip, + QVBoxLayout) + +# Local imports +from spyder.config.base import _ +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import get_std_icon + + +class HelperToolButton(QToolButton): + """Subclasses QToolButton, to provide a simple tooltip on mousedown. + """ + def __init__(self): + QToolButton.__init__(self) + self.setIcon(get_std_icon('MessageBoxInformation')) + style = """ + QToolButton { + border: 1px solid grey; + padding:0px; + border-radius: 2px; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #f6f7fa, stop: 1 #dadbde); + } + """ + self.setStyleSheet(style) + + def setToolTip(self, text): + self._tip_text = text + + def toolTip(self): + return self._tip_text + + def mousePressEvent(self, event): + QToolTip.hideText() + + def mouseReleaseEvent(self, event): + QToolTip.showText(self.mapToGlobal(QPoint(0, self.height())), + self._tip_text) + + +class MessageCheckBox(QMessageBox): + """ + A QMessageBox derived widget that includes a QCheckBox aligned to the right + under the message and on top of the buttons. + """ + def __init__(self, *args, **kwargs): + super(MessageCheckBox, self).__init__(*args, **kwargs) + + self._checkbox = QCheckBox() + + # Set layout to include checkbox + size = 9 + check_layout = QVBoxLayout() + check_layout.addItem(QSpacerItem(size, size)) + check_layout.addWidget(self._checkbox, 0, Qt.AlignRight) + check_layout.addItem(QSpacerItem(size, size)) + + # Access the Layout of the MessageBox to add the Checkbox + layout = self.layout() + layout.addLayout(check_layout, 1, 1) + + # --- Public API + # Methods to access the checkbox + def is_checked(self): + return self._checkbox.isChecked() + + def set_checked(self, value): + return self._checkbox.setChecked(value) + + def set_check_visible(self, value): + self._checkbox.setVisible(value) + + def is_check_visible(self): + self._checkbox.isVisible() + + def checkbox_text(self): + self._checkbox.text() + + def set_checkbox_text(self, text): + self._checkbox.setText(text) + + +class HTMLDelegate(QStyledItemDelegate): + """With this delegate, a QListWidgetItem or a QTableItem can render HTML. + + Taken from http://stackoverflow.com/a/5443112/2399799 + """ + def __init__(self, parent, margin=0): + super(HTMLDelegate, self).__init__(parent) + self._margin = margin + + def paint(self, painter, option, index): + options = QStyleOptionViewItem(option) + self.initStyleOption(options, index) + + style = (QApplication.style() if options.widget is None + else options.widget.style()) + + doc = QTextDocument() + doc.setDocumentMargin(self._margin) + doc.setHtml(options.text) + + options.text = "" + style.drawControl(QStyle.CE_ItemViewItem, options, painter) + + ctx = QAbstractTextDocumentLayout.PaintContext() + + textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options) + painter.save() + + # Adjustments for the file switcher + if hasattr(options.widget, 'files_list'): + if style.objectName() in ['oxygen', 'qtcurve', 'breeze']: + if options.widget.files_list: + painter.translate(textRect.topLeft() + QPoint(4, -9)) + else: + painter.translate(textRect.topLeft()) + else: + if options.widget.files_list: + painter.translate(textRect.topLeft() + QPoint(4, 4)) + else: + painter.translate(textRect.topLeft() + QPoint(2, 4)) + else: + painter.translate(textRect.topLeft() + QPoint(0, -3)) + doc.documentLayout().draw(painter, ctx) + painter.restore() + + def sizeHint(self, option, index): + options = QStyleOptionViewItem(option) + self.initStyleOption(options, index) + + doc = QTextDocument() + doc.setHtml(options.text) + + return QSize(doc.idealWidth(), doc.size().height() - 2) + + +class IconLineEdit(QLineEdit): + """Custom QLineEdit that includes an icon representing the validation.""" + + def __init__(self, *args, **kwargs): + super(IconLineEdit, self).__init__(*args, **kwargs) + + self._status = True + self._status_set = True + self._valid_icon = ima.icon('todo') + self._invalid_icon = ima.icon('warning') + self._set_icon = ima.icon('todo_list') + self._application_style = QApplication.style().objectName() + self._refresh() + self._paint_count = 0 + self._icon_visible = False + + def _refresh(self): + """After an application style change, the paintEvent updates the + custom defined stylesheet. + """ + padding = self.height() + css_base = """QLineEdit {{ + border: none; + padding-right: {padding}px; + }} + """ + css_oxygen = """QLineEdit {{background: transparent; + border: none; + padding-right: {padding}px; + }} + """ + if self._application_style == 'oxygen': + css_template = css_oxygen + else: + css_template = css_base + + css = css_template.format(padding=padding) + self.setStyleSheet(css) + self.update() + + def hide_status_icon(self): + """Show the status icon.""" + self._icon_visible = False + self.repaint() + self.update() + + def show_status_icon(self): + """Hide the status icon.""" + self._icon_visible = True + self.repaint() + self.update() + + def update_status(self, value, value_set): + """Update the status and set_status to update the icons to display.""" + self._status = value + self._status_set = value_set + self.repaint() + self.update() + + def paintEvent(self, event): + """Qt Override. + + Include a validation icon to the left of the line edit. + """ + super(IconLineEdit, self).paintEvent(event) + painter = QPainter(self) + + rect = self.geometry() + space = int((rect.height())/6) + h = rect.height() - space + w = rect.width() - h + + if self._icon_visible: + if self._status and self._status_set: + pixmap = self._set_icon.pixmap(h, h) + elif self._status: + pixmap = self._valid_icon.pixmap(h, h) + else: + pixmap = self._invalid_icon.pixmap(h, h) + + painter.drawPixmap(w, space, pixmap) + + application_style = QApplication.style().objectName() + if self._application_style != application_style: + self._application_style = application_style + self._refresh() + + # Small hack to gurantee correct padding on Spyder start + if self._paint_count < 5: + self._paint_count += 1 + self._refresh() + + +def test_msgcheckbox(): + from spyder.utils.qthelpers import qapplication + app = qapplication() + box = MessageCheckBox() + box.setWindowTitle(_("Spyder updates")) + box.setText("Testing checkbox") + box.set_checkbox_text("Check for updates on startup?") + box.setStandardButtons(QMessageBox.Ok) + box.setDefaultButton(QMessageBox.Ok) + box.setIcon(QMessageBox.Information) + box.exec_() + + +if __name__ == '__main__': + test_msgcheckbox() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/__init__.py spyder-3.0.2+dfsg1/spyder/widgets/__init__.py --- spyder-2.3.8+dfsg1/spyder/widgets/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.widgets +============== + +Widgets defined in this module may be used in any other Qt-based application + +They are also used in Spyder through the Plugin interface +(see spyder.plugins) +""" diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/internalshell.py spyder-3.0.2+dfsg1/spyder/widgets/internalshell.py --- spyder-2.3.8+dfsg1/spyder/widgets/internalshell.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/internalshell.py 2016-11-08 00:45:11.000000000 +0000 @@ -0,0 +1,467 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Internal shell widget : PythonShellWidget + Interpreter""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +#FIXME: Internal shell MT: for i in range(100000): print i -> bug + +# Standard library imports +from time import time +import os +import threading + +# Third party imports +from qtpy.QtCore import QEventLoop, QObject, Signal, Slot +from qtpy.QtWidgets import QMessageBox + +# Local imports +from spyder import get_versions +from spyder.interpreter import Interpreter +from spyder.py3compat import (builtins, getcwd, to_binary_string, + to_text_string, u) +from spyder.utils import icon_manager as ima +from spyder.utils import programs +from spyder.utils.dochelpers import getargtxt, getdoc, getobjdir, getsource +from spyder.utils.misc import get_error_match +from spyder.utils.qthelpers import create_action +from spyder.widgets.shell import PythonShellWidget +from spyder.widgets.variableexplorer.objecteditor import oedit +# TODO: remove the CONF object and make it work anyway +# In fact, this 'CONF' object has nothing to do in package spyder/widgets +# which should not contain anything directly related to Spyder's main app +from spyder.config.base import _, DEBUG, get_conf_path +from spyder.config.main import CONF + + +builtins.oedit = oedit + + +def create_banner(message): + """Create internal shell banner""" + if message is None: + versions = get_versions() + return 'Python %s %dbits [%s]'\ + % (versions['python'], versions['bitness'], versions['system']) + else: + return message + + +class SysOutput(QObject): + """Handle standard I/O queue""" + data_avail = Signal() + + def __init__(self): + QObject.__init__(self) + self.queue = [] + self.lock = threading.Lock() + + def write(self, val): + self.lock.acquire() + self.queue.append(val) + self.lock.release() + self.data_avail.emit() + + def empty_queue(self): + self.lock.acquire() + s = "".join(self.queue) + self.queue = [] + self.lock.release() + return s + + # We need to add this method to fix Issue 1789 + def flush(self): + pass + + # This is needed to fix Issue 2984 + @property + def closed(self): + return False + +class WidgetProxyData(object): + pass + +class WidgetProxy(QObject): + """Handle Shell widget refresh signal""" + + sig_new_prompt = Signal(str) + sig_set_readonly = Signal(bool) + sig_edit = Signal(str, bool) + sig_wait_input = Signal(str) + + def __init__(self, input_condition): + QObject.__init__(self) + self.input_data = None + self.input_condition = input_condition + + def new_prompt(self, prompt): + self.sig_new_prompt.emit(prompt) + + def set_readonly(self, state): + self.sig_set_readonly.emit(state) + + def edit(self, filename, external_editor=False): + self.sig_edit.emit(filename, external_editor) + + def data_available(self): + """Return True if input data is available""" + return self.input_data is not WidgetProxyData + + def wait_input(self, prompt=''): + self.input_data = WidgetProxyData + self.sig_wait_input.emit(prompt) + + def end_input(self, cmd): + self.input_condition.acquire() + self.input_data = cmd + self.input_condition.notify() + self.input_condition.release() + + +class InternalShell(PythonShellWidget): + """Shell base widget: link between PythonShellWidget and Interpreter""" + + status = Signal(str) + refresh = Signal() + go_to_error = Signal(str) + focus_changed = Signal() + + def __init__(self, parent=None, namespace=None, commands=[], message=None, + max_line_count=300, font=None, exitfunc=None, profile=False, + multithreaded=True, light_background=True): + PythonShellWidget.__init__(self, parent, + get_conf_path('history_internal.py'), + profile) + + self.set_light_background(light_background) + self.multithreaded = multithreaded + self.setMaximumBlockCount(max_line_count) + + if font is not None: + self.set_font(font) + + # Allow raw_input support: + self.input_loop = None + self.input_mode = False + + # KeyboardInterrupt support + self.interrupted = False # used only for not-multithreaded mode + self.sig_keyboard_interrupt.connect(self.keyboard_interrupt) + + # Code completion / calltips + getcfg = lambda option: CONF.get('internal_console', option) + case_sensitive = getcfg('codecompletion/case_sensitive') + self.set_codecompletion_case(case_sensitive) + + # keyboard events management + self.eventqueue = [] + + # Init interpreter + self.exitfunc = exitfunc + self.commands = commands + self.message = message + self.interpreter = None + self.start_interpreter(namespace) + + # Clear status bar + self.status.emit('') + + # Embedded shell -- requires the monitor (which installs the + # 'open_in_spyder' function in builtins) + if hasattr(builtins, 'open_in_spyder'): + self.go_to_error.connect(self.open_with_external_spyder) + + #------ Interpreter + def start_interpreter(self, namespace): + """Start Python interpreter""" + self.clear() + + if self.interpreter is not None: + self.interpreter.closing() + self.interpreter = Interpreter(namespace, self.exitfunc, + SysOutput, WidgetProxy, DEBUG) + self.interpreter.stdout_write.data_avail.connect(self.stdout_avail) + self.interpreter.stderr_write.data_avail.connect(self.stderr_avail) + self.interpreter.widget_proxy.sig_set_readonly.connect(self.setReadOnly) + self.interpreter.widget_proxy.sig_new_prompt.connect(self.new_prompt) + self.interpreter.widget_proxy.sig_edit.connect(self.edit_script) + self.interpreter.widget_proxy.sig_wait_input.connect(self.wait_input) + if self.multithreaded: + self.interpreter.start() + + # Interpreter banner + banner = create_banner(self.message) + self.write(banner, prompt=True) + + # Initial commands + for cmd in self.commands: + self.run_command(cmd, history=False, new_prompt=False) + + # First prompt + self.new_prompt(self.interpreter.p1) + self.refresh.emit() + + return self.interpreter + + def exit_interpreter(self): + """Exit interpreter""" + self.interpreter.exit_flag = True + if self.multithreaded: + self.interpreter.stdin_write.write(to_binary_string('\n')) + self.interpreter.restore_stds() + + def edit_script(self, filename, external_editor): + filename = to_text_string(filename) + if external_editor: + self.external_editor(filename) + else: + self.parent().edit_script(filename) + + def stdout_avail(self): + """Data is available in stdout, let's empty the queue and write it!""" + data = self.interpreter.stdout_write.empty_queue() + if data: + self.write(data) + + def stderr_avail(self): + """Data is available in stderr, let's empty the queue and write it!""" + data = self.interpreter.stderr_write.empty_queue() + if data: + self.write(data, error=True) + self.flush(error=True) + + + #------Raw input support + def wait_input(self, prompt=''): + """Wait for input (raw_input support)""" + self.new_prompt(prompt) + self.setFocus() + self.input_mode = True + self.input_loop = QEventLoop() + self.input_loop.exec_() + self.input_loop = None + + def end_input(self, cmd): + """End of wait_input mode""" + self.input_mode = False + self.input_loop.exit() + self.interpreter.widget_proxy.end_input(cmd) + + + #----- Menus, actions, ... + def setup_context_menu(self): + """Reimplement PythonShellWidget method""" + PythonShellWidget.setup_context_menu(self) + self.help_action = create_action(self, _("Help..."), + icon=ima.icon('DialogHelpButton'), + triggered=self.help) + self.menu.addAction(self.help_action) + + @Slot() + def help(self): + """Help on Spyder console""" + QMessageBox.about(self, _("Help"), + """%s +

    %s
    edit foobar.py +

    %s
    xedit foobar.py +

    %s
    run foobar.py +

    %s
    clear x, y +

    %s
    !ls +

    %s
    object? +

    %s
    result = oedit(object) + """ % (_('Shell special commands:'), + _('Internal editor:'), + _('External editor:'), + _('Run script:'), + _('Remove references:'), + _('System commands:'), + _('Python help:'), + _('GUI-based editor:'))) + + + #------ External editing + def open_with_external_spyder(self, text): + """Load file in external Spyder's editor, if available + This method is used only for embedded consoles + (could also be useful if we ever implement the magic %edit command)""" + match = get_error_match(to_text_string(text)) + if match: + fname, lnb = match.groups() + builtins.open_in_spyder(fname, int(lnb)) + + def external_editor(self, filename, goto=-1): + """Edit in an external editor + Recommended: SciTE (e.g. to go to line where an error did occur)""" + editor_path = CONF.get('internal_console', 'external_editor/path') + goto_option = CONF.get('internal_console', 'external_editor/gotoline') + try: + args = [filename] + if goto > 0 and goto_option: + args.append('%s%d'.format(goto_option, goto)) + programs.run_program(editor_path, args) + except OSError: + self.write_error("External editor was not found:" + " %s\n" % editor_path) + + + #------ I/O + def flush(self, error=False, prompt=False): + """Reimplement ShellBaseWidget method""" + PythonShellWidget.flush(self, error=error, prompt=prompt) + if self.interrupted: + self.interrupted = False + raise KeyboardInterrupt + + + #------ Clear terminal + def clear_terminal(self): + """Reimplement ShellBaseWidget method""" + self.clear() + self.new_prompt(self.interpreter.p2 if self.interpreter.more else self.interpreter.p1) + + + #------ Keyboard events + def on_enter(self, command): + """on_enter""" + if self.profile: + # Simple profiling test + t0 = time() + for _ in range(10): + self.execute_command(command) + self.insert_text(u("\n<Δt>=%dms\n") % (1e2*(time()-t0))) + self.new_prompt(self.interpreter.p1) + else: + self.execute_command(command) + self.__flush_eventqueue() + + def keyPressEvent(self, event): + """ + Reimplement Qt Method + Enhanced keypress event handler + """ + if self.preprocess_keyevent(event): + # Event was accepted in self.preprocess_keyevent + return + self.postprocess_keyevent(event) + + def __flush_eventqueue(self): + """Flush keyboard event queue""" + while self.eventqueue: + past_event = self.eventqueue.pop(0) + self.postprocess_keyevent(past_event) + + #------ Command execution + def keyboard_interrupt(self): + """Simulate keyboard interrupt""" + if self.multithreaded: + self.interpreter.raise_keyboard_interrupt() + else: + if self.interpreter.more: + self.write_error("\nKeyboardInterrupt\n") + self.interpreter.more = False + self.new_prompt(self.interpreter.p1) + self.interpreter.resetbuffer() + else: + self.interrupted = True + + def execute_lines(self, lines): + """ + Execute a set of lines as multiple command + lines: multiple lines of text to be executed as single commands + """ + for line in lines.splitlines(): + stripped_line = line.strip() + if stripped_line.startswith('#'): + continue + self.write(line+os.linesep, flush=True) + self.execute_command(line+"\n") + self.flush() + + def execute_command(self, cmd): + """ + Execute a command + cmd: one-line command only, with '\n' at the end + """ + if self.input_mode: + self.end_input(cmd) + return + if cmd.endswith('\n'): + cmd = cmd[:-1] + # cls command + if cmd == 'cls': + self.clear_terminal() + return + self.run_command(cmd) + + def run_command(self, cmd, history=True, new_prompt=True): + """Run command in interpreter""" + if not cmd: + cmd = '' + else: + if history: + self.add_to_history(cmd) + self.interpreter.stdin_write.write(to_binary_string(cmd + '\n')) + if not self.multithreaded: + self.interpreter.run_line() + self.refresh.emit() + + + #------ Code completion / Calltips + def _eval(self, text): + """Is text a valid object?""" + return self.interpreter.eval(text) + + def get_dir(self, objtxt): + """Return dir(object)""" + obj, valid = self._eval(objtxt) + if valid: + return getobjdir(obj) + + def get_globals_keys(self): + """Return shell globals() keys""" + return list(self.interpreter.namespace.keys()) + + def get_cdlistdir(self): + """Return shell current directory list dir""" + return os.listdir(getcwd()) + + def iscallable(self, objtxt): + """Is object callable?""" + obj, valid = self._eval(objtxt) + if valid: + return callable(obj) + + def get_arglist(self, objtxt): + """Get func/method argument list""" + obj, valid = self._eval(objtxt) + if valid: + return getargtxt(obj) + + def get__doc__(self, objtxt): + """Get object __doc__""" + obj, valid = self._eval(objtxt) + if valid: + return obj.__doc__ + + def get_doc(self, objtxt): + """Get object documentation dictionary""" + obj, valid = self._eval(objtxt) + if valid: + return getdoc(obj) + + def get_source(self, objtxt): + """Get object source""" + obj, valid = self._eval(objtxt) + if valid: + return getsource(obj) + + def is_defined(self, objtxt, force_import=False): + """Return True if object is defined""" + return self.interpreter.is_defined(objtxt, force_import) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/client.py spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/client.py --- spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/client.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/client.py 2016-11-16 15:40:01.000000000 +0000 @@ -0,0 +1,400 @@ +# -*- coding:utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Client widget for the IPython Console + +This is the widget used on all its tabs +""" + +# Standard library imports +from __future__ import absolute_import # Fix for Issue 1356 + +import os +import os.path as osp +from string import Template +from threading import Thread +import time + +# Third party imports (qtpy) +from qtpy.QtCore import Qt, QUrl, Signal, Slot +from qtpy.QtGui import QKeySequence +from qtpy.QtWidgets import (QHBoxLayout, QMenu, QMessageBox, QToolButton, + QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import (_, get_conf_path, get_image_path, + get_module_source_path) +from spyder.config.gui import get_font, get_shortcut +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import (add_actions, create_action, + create_toolbutton) +from spyder.widgets.browser import WebView +from spyder.widgets.mixins import SaveHistoryMixin +from spyder.widgets.ipythonconsole import ShellWidget + + +#----------------------------------------------------------------------------- +# Templates +#----------------------------------------------------------------------------- +# Using the same css file from the Help plugin for now. Maybe +# later it'll be a good idea to create a new one. +UTILS_PATH = get_module_source_path('spyder', 'utils') +CSS_PATH = osp.join(UTILS_PATH, 'help', 'static', 'css') +TEMPLATES_PATH = osp.join(UTILS_PATH, 'ipython', 'templates') + +BLANK = open(osp.join(TEMPLATES_PATH, 'blank.html')).read() +LOADING = open(osp.join(TEMPLATES_PATH, 'loading.html')).read() +KERNEL_ERROR = open(osp.join(TEMPLATES_PATH, 'kernel_error.html')).read() + + +#----------------------------------------------------------------------------- +# Auxiliary functions +#----------------------------------------------------------------------------- +def background(f): + """ + Call a function in a simple thread, to prevent blocking + + Taken from the Jupyter Qtconsole project + """ + t = Thread(target=f) + t.start() + return t + + +#----------------------------------------------------------------------------- +# Client widget +#----------------------------------------------------------------------------- +class ClientWidget(QWidget, SaveHistoryMixin): + """ + Client widget for the IPython Console + + This is a widget composed of a shell widget and a WebView info widget + to print different messages there. + """ + + SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime()) + append_to_history = Signal(str, str) + + def __init__(self, plugin, name, history_filename, config_options, + additional_options, interpreter_versions, + connection_file=None, hostname=None, + menu_actions=None, slave=False, + external_kernel=False): + super(ClientWidget, self).__init__(plugin) + SaveHistoryMixin.__init__(self) + + # --- Init attrs + self.name = name + self.history_filename = get_conf_path(history_filename) + self.connection_file = connection_file + self.hostname = hostname + self.menu_actions = menu_actions + self.slave = slave + + # --- Other attrs + self.options_button = None + self.stop_button = None + self.stop_icon = ima.icon('stop') + self.history = [] + + # --- Widgets + self.shellwidget = ShellWidget(config=config_options, + additional_options=additional_options, + interpreter_versions=interpreter_versions, + external_kernel=external_kernel, + local_kernel=True) + self.shellwidget.hide() + self.infowidget = WebView(self) + self.set_infowidget_font() + self.loading_page = self._create_loading_page() + self.infowidget.setHtml(self.loading_page, + QUrl.fromLocalFile(CSS_PATH)) + + # --- Layout + vlayout = QVBoxLayout() + toolbar_buttons = self.get_toolbar_buttons() + hlayout = QHBoxLayout() + for button in toolbar_buttons: + hlayout.addWidget(button) + vlayout.addLayout(hlayout) + vlayout.setContentsMargins(0, 0, 0, 0) + vlayout.addWidget(self.shellwidget) + vlayout.addWidget(self.infowidget) + self.setLayout(vlayout) + + # --- Exit function + self.exit_callback = lambda: plugin.close_client(client=self) + + # --- Signals + # As soon as some content is printed in the console, stop + # our loading animation + document = self.get_control().document() + document.contentsChange.connect(self._stop_loading_animation) + + #------ Public API -------------------------------------------------------- + def configure_shellwidget(self, give_focus=True): + """Configure shellwidget after kernel is started""" + if give_focus: + self.get_control().setFocus() + + # Connect shellwidget to the client + self.shellwidget.set_ipyclient(self) + + # To save history + self.shellwidget.executing.connect(self.add_to_history) + + # For Mayavi to run correctly + self.shellwidget.executing.connect(self.set_backend_for_mayavi) + + # To update history after execution + self.shellwidget.executed.connect(self.update_history) + + # To update the Variable Explorer after execution + self.shellwidget.executed.connect( + self.shellwidget.refresh_namespacebrowser) + + # To enable the stop button when executing a process + self.shellwidget.executing.connect(self.enable_stop_button) + + # To disable the stop button after execution stopped + self.shellwidget.executed.connect(self.disable_stop_button) + + def enable_stop_button(self): + self.stop_button.setEnabled(True) + + def disable_stop_button(self): + self.stop_button.setDisabled(True) + + @Slot() + def stop_button_click_handler(self): + """Method to handle what to do when the stop button is pressed""" + self.stop_button.setDisabled(True) + # Interrupt computations or stop debugging + if not self.shellwidget._reading: + self.interrupt_kernel() + else: + self.shellwidget.write_to_stdin('exit') + + def show_kernel_error(self, error): + """Show kernel initialization errors in infowidget""" + # Don't break lines in hyphens + # From http://stackoverflow.com/q/7691569/438386 + error = error.replace('-', '‑') + + # Create error page + message = _("An error ocurred while starting the kernel") + kernel_error_template = Template(KERNEL_ERROR) + page = kernel_error_template.substitute(css_path=CSS_PATH, + message=message, + error=error) + + # Show error + self.infowidget.setHtml(page) + self.shellwidget.hide() + self.infowidget.show() + + def get_name(self): + """Return client name""" + return ((_("Console") if self.hostname is None else self.hostname) + + " " + self.name) + + def get_control(self): + """Return the text widget (or similar) to give focus to""" + # page_control is the widget used for paging + page_control = self.shellwidget._page_control + if page_control and page_control.isVisible(): + return page_control + else: + return self.shellwidget._control + + def get_kernel(self): + """Get kernel associated with this client""" + return self.shellwidget.kernel_manager + + def get_options_menu(self): + """Return options menu""" + restart_action = create_action(self, _("Restart kernel"), + shortcut=QKeySequence("Ctrl+."), + icon=ima.icon('restart'), + triggered=self.restart_kernel, + context=Qt.WidgetWithChildrenShortcut) + + # Main menu + if self.menu_actions is not None: + actions = [restart_action, None] + self.menu_actions + else: + actions = [restart_action] + return actions + + def get_toolbar_buttons(self): + """Return toolbar buttons list""" + buttons = [] + # Code to add the stop button + if self.stop_button is None: + self.stop_button = create_toolbutton(self, text=_("Stop"), + icon=self.stop_icon, + tip=_("Stop the current command")) + self.disable_stop_button() + # set click event handler + self.stop_button.clicked.connect(self.stop_button_click_handler) + if self.stop_button is not None: + buttons.append(self.stop_button) + + if self.options_button is None: + options = self.get_options_menu() + if options: + self.options_button = create_toolbutton(self, + text=_('Options'), icon=ima.icon('tooloptions')) + self.options_button.setPopupMode(QToolButton.InstantPopup) + menu = QMenu(self) + add_actions(menu, options) + self.options_button.setMenu(menu) + if self.options_button is not None: + buttons.append(self.options_button) + + return buttons + + def add_actions_to_context_menu(self, menu): + """Add actions to IPython widget context menu""" + inspect_action = create_action(self, _("Inspect current object"), + QKeySequence(get_shortcut('console', + 'inspect current object')), + icon=ima.icon('MessageBoxInformation'), + triggered=self.inspect_object) + clear_line_action = create_action(self, _("Clear line or block"), + QKeySequence("Shift+Escape"), + icon=ima.icon('editdelete'), + triggered=self.clear_line) + reset_namespace_action = create_action(self, _("Reset namespace"), + QKeySequence("Ctrl+Alt+R"), + triggered=self.reset_namespace) + clear_console_action = create_action(self, _("Clear console"), + QKeySequence(get_shortcut('console', + 'clear shell')), + icon=ima.icon('editclear'), + triggered=self.clear_console) + quit_action = create_action(self, _("&Quit"), icon=ima.icon('exit'), + triggered=self.exit_callback) + add_actions(menu, (None, inspect_action, clear_line_action, + clear_console_action, reset_namespace_action, + None, quit_action)) + return menu + + def set_font(self, font): + """Set IPython widget's font""" + self.shellwidget._control.setFont(font) + self.shellwidget.font = font + + def set_infowidget_font(self): + """Set font for infowidget""" + font = get_font(option='rich_font') + self.infowidget.set_font(font) + + def shutdown(self): + """Shutdown kernel""" + if self.get_kernel() is not None and not self.slave: + self.shellwidget.kernel_manager.shutdown_kernel() + if self.shellwidget.kernel_client is not None: + background(self.shellwidget.kernel_client.stop_channels) + + def interrupt_kernel(self): + """Interrupt the associanted Spyder kernel if it's running""" + self.shellwidget.request_interrupt_kernel() + + @Slot() + def restart_kernel(self): + """ + Restart the associanted kernel + + Took this code from the qtconsole project + Licensed under the BSD license + """ + message = _('Are you sure you want to restart the kernel?') + buttons = QMessageBox.Yes | QMessageBox.No + result = QMessageBox.question(self, _('Restart kernel?'), + message, buttons) + if result == QMessageBox.Yes: + sw = self.shellwidget + if sw.kernel_manager: + try: + sw.kernel_manager.restart_kernel() + except RuntimeError as e: + sw._append_plain_text( + _('Error restarting kernel: %s\n') % e, + before_prompt=True + ) + else: + sw._append_html(_("
    Restarting kernel...\n



    "), + before_prompt=True, + ) + else: + sw._append_plain_text( + _('Cannot restart a kernel not started by Spyder\n'), + before_prompt=True + ) + + @Slot() + def inspect_object(self): + """Show how to inspect an object with our Help plugin""" + self.shellwidget._control.inspect_current_object() + + @Slot() + def clear_line(self): + """Clear a console line""" + self.shellwidget._keyboard_quit() + + @Slot() + def clear_console(self): + """Clear the whole console""" + self.shellwidget.clear_console() + + @Slot() + def reset_namespace(self): + """Resets the namespace by removing all names defined by the user""" + self.shellwidget.reset_namespace() + + def update_history(self): + self.history = self.shellwidget._history + + def set_backend_for_mayavi(self, command): + """ + Mayavi plots require the Qt backend, so we try to detect if one is + generated to change backends + """ + calling_mayavi = False + lines = command.splitlines() + for l in lines: + if not l.startswith('#'): + if 'import mayavi' in l or 'from mayavi' in l: + calling_mayavi = True + break + if calling_mayavi: + message = _("Changing backend to Qt for Mayavi") + self.shellwidget._append_plain_text(message + '\n') + self.shellwidget.execute("%gui inline\n%gui qt") + + #------ Private API ------------------------------------------------------- + def _create_loading_page(self): + """Create html page to show while the kernel is starting""" + loading_template = Template(LOADING) + loading_img = get_image_path('loading_sprites.png') + if os.name == 'nt': + loading_img = loading_img.replace('\\', '/') + message = _("Connecting to kernel...") + page = loading_template.substitute(css_path=CSS_PATH, + loading_img=loading_img, + message=message) + return page + + def _stop_loading_animation(self): + """Stop animation shown while the kernel is starting""" + self.infowidget.hide() + self.shellwidget.show() + self.infowidget.setHtml(BLANK) + + document = self.get_control().document() + document.contentsChange.disconnect(self._stop_loading_animation) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/control.py spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/control.py --- spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/control.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/control.py 2016-11-16 15:40:01.000000000 +0000 @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Control widgets used by ShellWidget""" + +from qtpy.QtCore import Qt, Signal +from qtpy.QtWidgets import QTextEdit + +from spyder.utils.qthelpers import restore_keyevent +from spyder.widgets.calltip import CallTipWidget +from spyder.widgets.mixins import (BaseEditMixin, GetHelpMixin, + TracebackLinksMixin) + + +class ControlWidget(TracebackLinksMixin, GetHelpMixin, QTextEdit, + BaseEditMixin): + """ + Subclass of QTextEdit with features from Spyder's mixins to use as the + control widget for IPython widgets + """ + QT_CLASS = QTextEdit + visibility_changed = Signal(bool) + go_to_error = Signal(str) + focus_changed = Signal() + + def __init__(self, parent=None): + QTextEdit.__init__(self, parent) + BaseEditMixin.__init__(self) + TracebackLinksMixin.__init__(self) + GetHelpMixin.__init__(self) + + self.calltip_widget = CallTipWidget(self, hide_timer_on=True) + self.found_results = [] + + # To not use Spyder calltips obtained through the monitor + self.calltips = False + + def showEvent(self, event): + """Reimplement Qt Method""" + self.visibility_changed.emit(True) + + def _key_paren_left(self, text): + """ Action for '(' """ + self.current_prompt_pos = self.parentWidget()._prompt_pos + if self.get_current_line_to_cursor(): + last_obj = self.get_last_obj() + if last_obj and not last_obj.isdigit(): + self.show_object_info(last_obj) + self.insert_text(text) + + def keyPressEvent(self, event): + """Reimplement Qt Method - Basic keypress event handler""" + event, text, key, ctrl, shift = restore_keyevent(event) + if key == Qt.Key_ParenLeft and not self.has_selected_text() \ + and self.help_enabled and not self.parent()._reading: + self._key_paren_left(text) + else: + # Let the parent widget handle the key press event + QTextEdit.keyPressEvent(self, event) + + def focusInEvent(self, event): + """Reimplement Qt method to send focus change notification""" + self.focus_changed.emit() + return super(ControlWidget, self).focusInEvent(event) + + def focusOutEvent(self, event): + """Reimplement Qt method to send focus change notification""" + self.focus_changed.emit() + return super(ControlWidget, self).focusOutEvent(event) + + +class PageControlWidget(QTextEdit, BaseEditMixin): + """ + Subclass of QTextEdit with features from Spyder's mixins.BaseEditMixin to + use as the paging widget for IPython widgets + """ + QT_CLASS = QTextEdit + visibility_changed = Signal(bool) + show_find_widget = Signal() + focus_changed = Signal() + + def __init__(self, parent=None): + QTextEdit.__init__(self, parent) + BaseEditMixin.__init__(self) + self.found_results = [] + + def showEvent(self, event): + """Reimplement Qt Method""" + self.visibility_changed.emit(True) + + def keyPressEvent(self, event): + """Reimplement Qt Method - Basic keypress event handler""" + event, text, key, ctrl, shift = restore_keyevent(event) + + if key == Qt.Key_Slash and self.isVisible(): + self.show_find_widget.emit() + + def focusInEvent(self, event): + """Reimplement Qt method to send focus change notification""" + self.focus_changed.emit() + return super(PageControlWidget, self).focusInEvent(event) + + def focusOutEvent(self, event): + """Reimplement Qt method to send focus change notification""" + self.focus_changed.emit() + return super(PageControlWidget, self).focusOutEvent(event) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/debugging.py spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/debugging.py --- spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/debugging.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/debugging.py 2016-11-16 15:40:01.000000000 +0000 @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Widget that handles communications between a console in debugging +mode and Spyder +""" + +import ast + +from qtpy.QtCore import QEventLoop + +from qtconsole.rich_jupyter_widget import RichJupyterWidget + +from spyder.py3compat import to_text_string + + +class DebuggingWidget(RichJupyterWidget): + """ + Widget with the necessary attributes and methods to handle + communications between a console in debugging mode and + Spyder + """ + + _input_reply = None + + # --- Public API -------------------------------------------------- + def silent_exec_input(self, code): + """Silently execute code through stdin""" + self._hidden = True + + # Wait until the kernel returns an answer + wait_loop = QEventLoop() + self.sig_input_reply.connect(wait_loop.quit) + self.kernel_client.iopub_channel.flush() + self.kernel_client.input(code) + wait_loop.exec_() + + # Remove loop connection and loop + self.sig_input_reply.disconnect(wait_loop.quit) + wait_loop = None + + # Restore hidden state + self._hidden = False + + # Emit signal + if 'pdb_step' in code and self._input_reply is not None: + fname = self._input_reply['fname'] + lineno = self._input_reply['lineno'] + self.sig_pdb_step.emit(fname, lineno) + elif 'get_namespace_view' in code: + view = self._input_reply + self.sig_namespace_view.emit(view) + elif 'get_var_properties' in code: + properties = self._input_reply + self.sig_var_properties.emit(properties) + + def write_to_stdin(self, line): + """Send raw characters to the IPython kernel through stdin""" + wait_loop = QEventLoop() + self.sig_prompt_ready.connect(wait_loop.quit) + self.kernel_client.input(line) + wait_loop.exec_() + + # Remove loop connection and loop + self.sig_prompt_ready.disconnect(wait_loop.quit) + wait_loop = None + + # Run post exec commands + self._post_exec_input(line) + + # ---- Private API (defined by us) ------------------------------- + def _post_exec_input(self, line): + """Commands to be run after writing to stdin""" + if self._reading: + pdb_commands = ['next', 'continue', 'step', 'return'] + if any([x == line for x in pdb_commands]): + # To open the file where the current pdb frame points to + self.silent_exec_input("!get_ipython().kernel.get_pdb_step()") + + # To refresh the Variable Explorer + self.silent_exec_input( + "!get_ipython().kernel.get_namespace_view()") + self.silent_exec_input( + "!get_ipython().kernel.get_var_properties()") + + # ---- Private API (overrode by us) ------------------------------- + def _handle_input_request(self, msg): + """ + Reimplemented to be able to handle requests when we ask for + hidden inputs + """ + self.kernel_client.iopub_channel.flush() + if not self._hidden: + def callback(line): + self.kernel_client.input(line) + if self._reading: + self._reading = False + self._readline(msg['content']['prompt'], callback=callback, + password=msg['content']['password']) + else: + # This is what we added, i.e. not doing anything if + # Spyder asks for silent inputs + pass + + def _handle_stream(self, msg): + """ + Reimplemented to handle input replies in hidden mode + """ + if not self._hidden: + self.flush_clearoutput() + self.append_stream(msg['content']['text']) + # This signal is a clear indication that all stdout + # has been handled at this point. Then Spyder can + # proceed to request other inputs + self.sig_prompt_ready.emit() + else: + # This allows Spyder to receive, transform and save the + # contents of a silent execution + content = msg.get('content', '') + if content: + name = content.get('name', '') + if name == 'stdout': + text = content['text'] + text = to_text_string(text.replace('\n', '')) + try: + reply = ast.literal_eval(text) + except: + reply = None + self._input_reply = reply + self.sig_input_reply.emit() + else: + self._input_reply = None + self.sig_input_reply.emit() + else: + self._input_reply = None + self.sig_input_reply.emit() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/help.py spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/help.py --- spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/help.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/help.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Widget that handles communications between the IPython Console and +the Help plugin +""" + +from __future__ import absolute_import + +import re + +from qtpy.QtCore import QEventLoop + +from qtconsole.ansi_code_processor import ANSI_OR_SPECIAL_PATTERN +from qtconsole.rich_jupyter_widget import RichJupyterWidget + +from spyder.config.base import _ +from spyder.py3compat import PY3 +from spyder.utils.dochelpers import getargspecfromtext, getsignaturefromtext + + +class HelpWidget(RichJupyterWidget): + """ + Widget with the necessary attributes and methods to handle communications + between the IPython Console and the Help plugin + """ + + def clean_invalid_var_chars(self, var): + """ + Replace invalid variable chars in a string by underscores + + Taken from http://stackoverflow.com/a/3305731/438386 + """ + if PY3: + return re.sub('\W|^(?=\d)', '_', var, re.UNICODE) + else: + return re.sub('\W|^(?=\d)', '_', var) + + def get_signature(self, content): + """Get signature from inspect reply content""" + data = content.get('data', {}) + text = data.get('text/plain', '') + if text: + text = ANSI_OR_SPECIAL_PATTERN.sub('', text) + self._control.current_prompt_pos = self._prompt_pos + line = self._control.get_current_line_to_cursor() + name = line[:-1].split('(')[-1] # Take last token after a ( + name = name.split('.')[-1] # Then take last token after a . + # Clean name from invalid chars + try: + name = self.clean_invalid_var_chars(name).split('_')[-1] + except: + pass + argspec = getargspecfromtext(text) + if argspec: + # This covers cases like np.abs, whose docstring is + # the same as np.absolute and because of that a proper + # signature can't be obtained correctly + signature = name + argspec + else: + signature = getsignaturefromtext(text, name) + return signature + else: + return '' + + def is_defined(self, objtxt, force_import=False): + """Return True if object is defined""" + wait_loop = QEventLoop() + self.sig_got_reply.connect(wait_loop.quit) + self.silent_exec_method( + "get_ipython().kernel.is_defined('%s', force_import=%s)" + % (objtxt, force_import)) + wait_loop.exec_() + + # Remove loop connection and loop + self.sig_got_reply.disconnect(wait_loop.quit) + wait_loop = None + + return self._kernel_reply + + def get_doc(self, objtxt): + """Get object documentation dictionary""" + wait_loop = QEventLoop() + self.sig_got_reply.connect(wait_loop.quit) + self.silent_exec_method("get_ipython().kernel.get_doc('%s')" % objtxt) + wait_loop.exec_() + + # Remove loop connection and loop + self.sig_got_reply.disconnect(wait_loop.quit) + wait_loop = None + + return self._kernel_reply + + def get_source(self, objtxt): + """Get object source""" + wait_loop = QEventLoop() + self.sig_got_reply.connect(wait_loop.quit) + self.silent_exec_method("get_ipython().kernel.get_source('%s')" % objtxt) + wait_loop.exec_() + + # Remove loop connection and loop + self.sig_got_reply.disconnect(wait_loop.quit) + wait_loop = None + + return self._kernel_reply + + #---- Private methods (overrode by us) --------------------------------- + def _handle_inspect_reply(self, rep): + """ + Reimplement call tips to only show signatures, using the same + style from our Editor and External Console too + """ + cursor = self._get_cursor() + info = self._request_info.get('call_tip') + if info and info.id == rep['parent_header']['msg_id'] and \ + info.pos == cursor.position(): + content = rep['content'] + if content.get('status') == 'ok' and content.get('found', False): + signature = self.get_signature(content) + if signature: + self._control.show_calltip(_("Arguments"), signature, + signature=True, color='#2D62FF') diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/__init__.py spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/__init__.py --- spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,19 @@ +# -*- coding:utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Widgets for the IPython Console +""" + +from .control import ControlWidget, PageControlWidget +from .debugging import DebuggingWidget +from .help import HelpWidget +from .namespacebrowser import NamepaceBrowserWidget + +# ShellWidget contains the other widgets and ClientWidget +# contains it +from .shell import ShellWidget +from .client import ClientWidget diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/namespacebrowser.py spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/namespacebrowser.py --- spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/namespacebrowser.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/namespacebrowser.py 2016-11-17 03:39:40.000000000 +0000 @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Widget that handle communications between the IPython Console and +the Variable Explorer +""" + +from qtpy.QtCore import QEventLoop + +from ipykernel.pickleutil import CannedObject +from ipykernel.serialize import deserialize_object +from qtconsole.rich_jupyter_widget import RichJupyterWidget + +from spyder.config.base import _ +from spyder.py3compat import to_text_string + + +class NamepaceBrowserWidget(RichJupyterWidget): + """ + Widget with the necessary attributes and methods to handle communications + between the IPython Console and the Variable Explorer + """ + + # Reference to the nsb widget connected to this client + namespacebrowser = None + + # To save the replies of kernel method executions (except + # getting values of variables) + _kernel_methods = {} + + # To save values and messages returned by the kernel + _kernel_value = None + _kernel_is_starting = True + + # --- Public API -------------------------------------------------- + def set_namespacebrowser(self, namespacebrowser): + """Set namespace browser widget""" + self.namespacebrowser = namespacebrowser + self.configure_namespacebrowser() + + def configure_namespacebrowser(self): + """Configure associated namespace browser widget""" + # Tell it that we are connected to client + self.namespacebrowser.is_ipyclient = True + + # Update namespace view + self.sig_namespace_view.connect(lambda data: + self.namespacebrowser.process_remote_view(data)) + + # Update properties of variables + self.sig_var_properties.connect(lambda data: + self.namespacebrowser.set_var_properties(data)) + + def refresh_namespacebrowser(self): + """Refresh namespace browser""" + if self.namespacebrowser: + self.silent_exec_method( + 'get_ipython().kernel.get_namespace_view()') + self.silent_exec_method( + 'get_ipython().kernel.get_var_properties()') + + def set_namespace_view_settings(self): + """Set the namespace view settings""" + settings = to_text_string(self.namespacebrowser.get_view_settings()) + code = u"get_ipython().kernel.namespace_view_settings = %s" % settings + self.silent_execute(code) + + def get_value(self, name): + """Ask kernel for a value""" + # Don't ask for values while reading (ipdb) is active + if self._reading: + raise ValueError(_("Inspecting and setting values while debugging " + "in IPython consoles is not supported yet by " + "Spyder.")) + + # Wait until the kernel returns the value + wait_loop = QEventLoop() + self.sig_got_reply.connect(wait_loop.quit) + self.silent_execute("get_ipython().kernel.get_value('%s')" % name) + wait_loop.exec_() + + # Remove loop connection and loop + self.sig_got_reply.disconnect(wait_loop.quit) + wait_loop = None + + # Handle exceptions + if self._kernel_value is None: + if self._kernel_reply: + msg = self._kernel_reply[:] + self._kernel_reply = None + raise ValueError(msg) + + return self._kernel_value + + def set_value(self, name, value): + """Set value for a variable""" + value = to_text_string(value) + self.silent_execute("get_ipython().kernel.set_value('%s', %s)" % + (name, value)) + + def remove_value(self, name): + """Remove a variable""" + self.silent_execute("get_ipython().kernel.remove_value('%s')" % name) + + def copy_value(self, orig_name, new_name): + """Copy a variable""" + self.silent_execute("get_ipython().kernel.copy_value('%s', '%s')" % + (orig_name, new_name)) + + def load_data(self, filename, ext): + # Wait until the kernel tries to load the file + wait_loop = QEventLoop() + self.sig_got_reply.connect(wait_loop.quit) + self.silent_exec_method( + "get_ipython().kernel.load_data('%s', '%s')" % (filename, ext)) + wait_loop.exec_() + + # Remove loop connection and loop + self.sig_got_reply.disconnect(wait_loop.quit) + wait_loop = None + + return self._kernel_reply + + def save_namespace(self, filename): + # Wait until the kernel tries to save the file + wait_loop = QEventLoop() + self.sig_got_reply.connect(wait_loop.quit) + self.silent_exec_method("get_ipython().kernel.save_namespace('%s')" % + filename) + wait_loop.exec_() + + # Remove loop connection and loop + self.sig_got_reply.disconnect(wait_loop.quit) + wait_loop = None + + return self._kernel_reply + + # ---- Private API (defined by us) ------------------------------ + def _handle_data_message(self, msg): + """ + Handle raw (serialized) data sent by the kernel + + We only handle data asked by Spyder, in case people use + publish_data for other purposes. + """ + # Deserialize data + try: + data = deserialize_object(msg['buffers'])[0] + except Exception as msg: + self._kernel_value = None + self._kernel_reply = repr(msg) + self.sig_got_reply.emit() + return + + # We only handle data asked by Spyder + value = data.get('__spy_data__', None) + if value is not None: + if isinstance(value, CannedObject): + value = value.get_object() + self._kernel_value = value + self.sig_got_reply.emit() + + # ---- Private API (overrode by us) ---------------------------- + def _handle_execute_reply(self, msg): + """ + Reimplemented to handle communications between Spyder + and the kernel + """ + msg_id = msg['parent_header']['msg_id'] + info = self._request_info['execute'].get(msg_id) + # unset reading flag, because if execute finished, raw_input can't + # still be pending. + self._reading = False + + # Refresh namespacebrowser after the kernel starts running + exec_count = msg['content']['execution_count'] + if exec_count == 0 and self._kernel_is_starting: + if self.namespacebrowser is not None: + self.set_namespace_view_settings() + self.refresh_namespacebrowser() + self._kernel_is_starting = False + + # Handle silent execution of kernel methods + if info and info.kind == 'silent_exec_method' and not self._hidden: + self.handle_exec_method(msg) + self._request_info['execute'].pop(msg_id) + else: + super(NamepaceBrowserWidget, self)._handle_execute_reply(msg) + + def _handle_status(self, msg): + """ + Reimplemented to refresh the namespacebrowser after kernel + restarts + """ + state = msg['content'].get('execution_state', '') + msg_type = msg['parent_header'].get('msg_type', '') + if state == 'starting' and not self._kernel_is_starting: + # This handles restarts when the kernel dies + # unexpectedly + self._kernel_is_starting = True + elif state == 'idle' and msg_type == 'shutdown_request': + # This handles restarts asked by the user + if self.namespacebrowser is not None: + self.set_namespace_view_settings() + self.refresh_namespacebrowser() + else: + super(NamepaceBrowserWidget, self)._handle_status(msg) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/shell.py spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/shell.py --- spyder-2.3.8+dfsg1/spyder/widgets/ipythonconsole/shell.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/ipythonconsole/shell.py 2016-11-17 03:39:40.000000000 +0000 @@ -0,0 +1,283 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Shell Widget for the IPython Console +""" + +import ast +import uuid + +from qtpy.QtCore import Signal +from qtpy.QtWidgets import QMessageBox + +from spyder.config.base import _ +from spyder.config.gui import config_shortcut, fixed_shortcut +from spyder.py3compat import to_text_string +from spyder.utils import programs +from spyder.widgets.arraybuilder import SHORTCUT_INLINE, SHORTCUT_TABLE +from spyder.widgets.ipythonconsole import (ControlWidget, DebuggingWidget, + HelpWidget, NamepaceBrowserWidget, + PageControlWidget) + + +class ShellWidget(NamepaceBrowserWidget, HelpWidget, DebuggingWidget): + """ + Shell widget for the IPython Console + + This is the widget in charge of executing code + """ + # NOTE: Signals can't be assigned separately to each widget + # That's why we define all needed signals here. + + # For NamepaceBrowserWidget + sig_namespace_view = Signal(object) + sig_var_properties = Signal(object) + + # For DebuggingWidget + sig_input_reply = Signal() + sig_pdb_step = Signal(str, int) + sig_prompt_ready = Signal() + + # For ShellWidget + focus_changed = Signal() + new_client = Signal() + sig_got_reply = Signal() + + def __init__(self, additional_options, interpreter_versions, + external_kernel, *args, **kw): + # To override the Qt widget used by RichJupyterWidget + self.custom_control = ControlWidget + self.custom_page_control = PageControlWidget + super(ShellWidget, self).__init__(*args, **kw) + self.additional_options = additional_options + self.interpreter_versions = interpreter_versions + + self.set_background_color() + + # Additional variables + self.ipyclient = None + self.external_kernel = external_kernel + + # Keyboard shortcuts + self.shortcuts = self.create_shortcuts() + + # To save kernel replies in silent execution + self._kernel_reply = None + + #---- Public API ---------------------------------------------------------- + def set_ipyclient(self, ipyclient): + """Bind this shell widget to an IPython client one""" + self.ipyclient = ipyclient + self.exit_requested.connect(ipyclient.exit_callback) + + def is_running(self): + if self.kernel_client is not None and \ + self.kernel_client.channels_running: + return True + else: + return False + + def set_cwd(self, dirname): + """Set shell current working directory.""" + return self.silent_execute( + "get_ipython().kernel.set_cwd(r'{}')".format(dirname)) + + # --- To handle the banner + def long_banner(self): + """Banner for IPython widgets with pylab message""" + # Default banner + from IPython.core.usage import quick_guide + banner_parts = [ + 'Python %s\n' % self.interpreter_versions['python_version'], + 'Type "copyright", "credits" or "license" for more information.\n\n', + 'IPython %s -- An enhanced Interactive Python.\n' % \ + self.interpreter_versions['ipython_version'], + quick_guide + ] + banner = ''.join(banner_parts) + + # Pylab additions + pylab_o = self.additional_options['pylab'] + autoload_pylab_o = self.additional_options['autoload_pylab'] + mpl_installed = programs.is_module_installed('matplotlib') + if mpl_installed and (pylab_o and autoload_pylab_o): + pylab_message = ("\nPopulating the interactive namespace from " + "numpy and matplotlib\n") + banner = banner + pylab_message + + # Sympy additions + sympy_o = self.additional_options['sympy'] + if sympy_o: + lines = """ +These commands were executed: +>>> from __future__ import division +>>> from sympy import * +>>> x, y, z, t = symbols('x y z t') +>>> k, m, n = symbols('k m n', integer=True) +>>> f, g, h = symbols('f g h', cls=Function) +""" + banner = banner + lines + if (pylab_o and sympy_o): + lines = """ +Warning: pylab (numpy and matplotlib) and symbolic math (sympy) are both +enabled at the same time. Some pylab functions are going to be overrided by +the sympy module (e.g. plot) +""" + banner = banner + lines + return banner + + def short_banner(self): + """Short banner with Python and QtConsole versions""" + banner = 'Python %s -- IPython %s' % ( + self.interpreter_versions['python_version'], + self.interpreter_versions['ipython_version']) + return banner + + # --- To define additional shortcuts + def clear_console(self): + self.execute("%clear") + + def reset_namespace(self): + """Resets the namespace by removing all names defined by the user""" + + reply = QMessageBox.question( + self, + _("Reset IPython namespace"), + _("All user-defined variables will be removed." + "
    Are you sure you want to reset the namespace?"), + QMessageBox.Yes | QMessageBox.No, + ) + + if reply == QMessageBox.Yes: + self.execute("%reset -f") + + def set_background_color(self): + light_color_o = self.additional_options['light_color'] + if not light_color_o: + self.set_default_style(colors='linux') + + def create_shortcuts(self): + inspect = config_shortcut(self._control.inspect_current_object, + context='Console', name='Inspect current object', + parent=self) + clear_console = config_shortcut(self.clear_console, context='Console', + name='Clear shell', parent=self) + + # Fixed shortcuts + fixed_shortcut("Ctrl+T", self, lambda: self.new_client.emit()) + fixed_shortcut("Ctrl+Alt+R", self, lambda: self.reset_namespace()) + fixed_shortcut(SHORTCUT_INLINE, self, + lambda: self._control.enter_array_inline()) + fixed_shortcut(SHORTCUT_TABLE, self, + lambda: self._control.enter_array_table()) + + return [inspect, clear_console] + + # --- To communicate with the kernel + def silent_execute(self, code): + """Execute code in the kernel without increasing the prompt""" + self.kernel_client.execute(to_text_string(code), silent=True) + + def silent_exec_method(self, code): + """Silently execute a kernel method and save its reply + + The methods passed here **don't** involve getting the value + of a variable but instead replies that can be handled by + ast.literal_eval. + + To get a value see `get_value` + + Parameters + ---------- + code : string + Code that contains the kernel method as part of its + string + + See Also + -------- + handle_exec_method : Method that deals with the reply + + Note + ---- + This is based on the _silent_exec_callback method of + RichJupyterWidget. Therefore this is licensed BSD + """ + # Generate uuid, which would be used as an indication of whether or + # not the unique request originated from here + local_uuid = to_text_string(uuid.uuid1()) + code = to_text_string(code) + msg_id = self.kernel_client.execute('', silent=True, + user_expressions={ local_uuid:code }) + self._kernel_methods[local_uuid] = code + self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, + 'silent_exec_method') + + def handle_exec_method(self, msg): + """ + Handle data returned by silent executions of kernel methods + + This is based on the _handle_exec_callback of RichJupyterWidget. + Therefore this is licensed BSD. + """ + user_exp = msg['content'].get('user_expressions') + if not user_exp: + return + for expression in user_exp: + if expression in self._kernel_methods: + # Process kernel reply + method = self._kernel_methods[expression] + reply = user_exp[expression] + data = reply.get('data') + if 'get_namespace_view' in method: + if data is not None and 'text/plain' in data: + view = ast.literal_eval(data['text/plain']) + self.sig_namespace_view.emit(view) + else: + view = None + elif 'get_var_properties' in method: + properties = ast.literal_eval(data['text/plain']) + self.sig_var_properties.emit(properties) + else: + if data is not None: + self._kernel_reply = ast.literal_eval(data['text/plain']) + else: + self._kernel_reply = None + self.sig_got_reply.emit() + + # Remove method after being processed + self._kernel_methods.pop(expression) + + #---- Private methods (overrode by us) --------------------------------- + def _context_menu_make(self, pos): + """Reimplement the IPython context menu""" + menu = super(ShellWidget, self)._context_menu_make(pos) + return self.ipyclient.add_actions_to_context_menu(menu) + + def _banner_default(self): + """ + Reimplement banner creation to let the user decide if he wants a + banner or not + """ + # Don't change banner for external kernels + if self.external_kernel: + return '' + show_banner_o = self.additional_options['show_banner'] + if show_banner_o: + return self.long_banner() + else: + return self.short_banner() + + #---- Qt methods ---------------------------------------------------------- + def focusInEvent(self, event): + """Reimplement Qt method to send focus change notification""" + self.focus_changed.emit() + return super(ShellWidget, self).focusInEvent(event) + + def focusOutEvent(self, event): + """Reimplement Qt method to send focus change notification""" + self.focus_changed.emit() + return super(ShellWidget, self).focusOutEvent(event) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/mixins.py spyder-3.0.2+dfsg1/spyder/widgets/mixins.py --- spyder-2.3.8+dfsg1/spyder/widgets/mixins.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/mixins.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,701 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Mix-in classes + +These classes were created to be able to provide Spyder's regular text and +console widget features to an independant widget based on QTextEdit for the +IPython console plugin. +""" + +# Standard library imports +from xml.sax.saxutils import escape +import os +import re +import sre_constants +import textwrap + +# Third party imports +from qtpy.QtCore import QPoint, QRegExp, Qt +from qtpy.QtGui import QCursor, QTextCursor, QTextDocument +from qtpy.QtWidgets import QApplication, QToolTip + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import is_text_string, to_text_string, u +from spyder.utils import encoding, sourcecode +from spyder.utils.dochelpers import (getargspecfromtext, getobj, + getsignaturefromtext) +from spyder.utils.misc import get_error_match +from spyder.widgets.arraybuilder import NumpyArrayDialog + + +HISTORY_FILENAMES = [] + + +class BaseEditMixin(object): + + def __init__(self): + self.eol_chars = None + self.calltip_size = 600 + + #------Line number area + def get_linenumberarea_width(self): + """Return line number area width""" + # Implemented in CodeEditor, but needed for calltip/completion widgets + return 0 + + + #------Calltips + def _format_signature(self, text): + formatted_lines = [] + name = text.split('(')[0] + rows = textwrap.wrap(text, width=50, + subsequent_indent=' '*(len(name)+1)) + for r in rows: + r = escape(r) # Escape most common html chars + r = r.replace(' ', ' ') + for char in ['=', ',', '(', ')', '*', '**']: + r = r.replace(char, + '' + \ + char + '') + formatted_lines.append(r) + signature = '
    '.join(formatted_lines) + return signature, rows + + def show_calltip(self, title, text, signature=False, color='#2D62FF', + at_line=None, at_position=None): + """Show calltip""" + if text is None or len(text) == 0: + return + + # Saving cursor position: + if at_position is None: + at_position = self.get_position('cursor') + self.calltip_position = at_position + + # Preparing text: + if signature: + text, wrapped_textlines = self._format_signature(text) + else: + if isinstance(text, list): + text = "\n ".join(text) + text = text.replace('\n', '
    ') + if len(text) > self.calltip_size: + text = text[:self.calltip_size] + " ..." + + # Formatting text + font = self.font() + size = font.pointSize() + family = font.family() + format1 = '
    '\ + % (family, size, color) + format2 = '
    '\ + % (family, size-1 if size > 9 else size) + tiptext = format1 + ('%s
    ' % title) + '
    ' + \ + format2 + text + "
    " + + # Showing tooltip at cursor position: + cx, cy = self.get_coordinates('cursor') + if at_line is not None: + cx = 5 + cursor = QTextCursor(self.document().findBlockByNumber(at_line-1)) + cy = self.cursorRect(cursor).top() + point = self.mapToGlobal(QPoint(cx, cy)) + point.setX(point.x()+self.get_linenumberarea_width()) + point.setY(point.y()+font.pointSize()+5) + if signature: + self.calltip_widget.show_tip(point, tiptext, wrapped_textlines) + else: + QToolTip.showText(point, tiptext) + + + #------EOL characters + def set_eol_chars(self, text): + """Set widget end-of-line (EOL) characters from text (analyzes text)""" + if not is_text_string(text): # testing for QString (PyQt API#1) + text = to_text_string(text) + eol_chars = sourcecode.get_eol_chars(text) + if eol_chars is not None and self.eol_chars is not None: + self.document().setModified(True) + self.eol_chars = eol_chars + + def get_line_separator(self): + """Return line separator based on current EOL mode""" + if self.eol_chars is not None: + return self.eol_chars + else: + return os.linesep + + def get_text_with_eol(self): + """Same as 'toPlainText', replace '\n' + by correct end-of-line characters""" + utext = to_text_string(self.toPlainText()) + lines = utext.splitlines() + linesep = self.get_line_separator() + txt = linesep.join(lines) + if utext.endswith('\n'): + txt += linesep + return txt + + + #------Positions, coordinates (cursor, EOF, ...) + def get_position(self, subject): + """Get offset in character for the given subject from the start of + text edit area""" + cursor = self.textCursor() + if subject == 'cursor': + pass + elif subject == 'sol': + cursor.movePosition(QTextCursor.StartOfBlock) + elif subject == 'eol': + cursor.movePosition(QTextCursor.EndOfBlock) + elif subject == 'eof': + cursor.movePosition(QTextCursor.End) + elif subject == 'sof': + cursor.movePosition(QTextCursor.Start) + else: + # Assuming that input argument was already a position + return subject + return cursor.position() + + def get_coordinates(self, position): + position = self.get_position(position) + cursor = self.textCursor() + cursor.setPosition(position) + point = self.cursorRect(cursor).center() + return point.x(), point.y() + + def get_cursor_line_column(self): + """Return cursor (line, column) numbers""" + cursor = self.textCursor() + return cursor.blockNumber(), cursor.columnNumber() + + def get_cursor_line_number(self): + """Return cursor line number""" + return self.textCursor().blockNumber()+1 + + def set_cursor_position(self, position): + """Set cursor position""" + position = self.get_position(position) + cursor = self.textCursor() + cursor.setPosition(position) + self.setTextCursor(cursor) + self.ensureCursorVisible() + + def move_cursor(self, chars=0): + """Move cursor to left or right (unit: characters)""" + direction = QTextCursor.Right if chars > 0 else QTextCursor.Left + for _i in range(abs(chars)): + self.moveCursor(direction, QTextCursor.MoveAnchor) + + def is_cursor_on_first_line(self): + """Return True if cursor is on the first line""" + cursor = self.textCursor() + cursor.movePosition(QTextCursor.StartOfBlock) + return cursor.atStart() + + def is_cursor_on_last_line(self): + """Return True if cursor is on the last line""" + cursor = self.textCursor() + cursor.movePosition(QTextCursor.EndOfBlock) + return cursor.atEnd() + + def is_cursor_at_end(self): + """Return True if cursor is at the end of the text""" + return self.textCursor().atEnd() + + def is_cursor_before(self, position, char_offset=0): + """Return True if cursor is before *position*""" + position = self.get_position(position) + char_offset + cursor = self.textCursor() + cursor.movePosition(QTextCursor.End) + if position < cursor.position(): + cursor.setPosition(position) + return self.textCursor() < cursor + + def __move_cursor_anchor(self, what, direction, move_mode): + assert what in ('character', 'word', 'line') + if what == 'character': + if direction == 'left': + self.moveCursor(QTextCursor.PreviousCharacter, move_mode) + elif direction == 'right': + self.moveCursor(QTextCursor.NextCharacter, move_mode) + elif what == 'word': + if direction == 'left': + self.moveCursor(QTextCursor.PreviousWord, move_mode) + elif direction == 'right': + self.moveCursor(QTextCursor.NextWord, move_mode) + elif what == 'line': + if direction == 'down': + self.moveCursor(QTextCursor.NextBlock, move_mode) + elif direction == 'up': + self.moveCursor(QTextCursor.PreviousBlock, move_mode) + + def move_cursor_to_next(self, what='word', direction='left'): + """ + Move cursor to next *what* ('word' or 'character') + toward *direction* ('left' or 'right') + """ + self.__move_cursor_anchor(what, direction, QTextCursor.MoveAnchor) + + + #------Selection + def clear_selection(self): + """Clear current selection""" + cursor = self.textCursor() + cursor.clearSelection() + self.setTextCursor(cursor) + + def extend_selection_to_next(self, what='word', direction='left'): + """ + Extend selection to next *what* ('word' or 'character') + toward *direction* ('left' or 'right') + """ + self.__move_cursor_anchor(what, direction, QTextCursor.KeepAnchor) + + + #------Text: get, set, ... + def __select_text(self, position_from, position_to): + position_from = self.get_position(position_from) + position_to = self.get_position(position_to) + cursor = self.textCursor() + cursor.setPosition(position_from) + cursor.setPosition(position_to, QTextCursor.KeepAnchor) + return cursor + + def get_text_line(self, line_nb): + """Return text line at line number *line_nb*""" + # Taking into account the case when a file ends in an empty line, + # since splitlines doesn't return that line as the last element + # TODO: Make this function more efficient + try: + return to_text_string(self.toPlainText()).splitlines()[line_nb] + except IndexError: + return self.get_line_separator() + + def get_text(self, position_from, position_to): + """ + Return text between *position_from* and *position_to* + Positions may be positions or 'sol', 'eol', 'sof', 'eof' or 'cursor' + """ + cursor = self.__select_text(position_from, position_to) + text = to_text_string(cursor.selectedText()) + all_text = position_from == 'sof' and position_to == 'eof' + if text and not all_text: + while text.endswith("\n"): + text = text[:-1] + while text.endswith(u("\u2029")): + text = text[:-1] + return text + + def get_character(self, position): + """Return character at *position*""" + position = self.get_position(position) + cursor = self.textCursor() + cursor.movePosition(QTextCursor.End) + if position < cursor.position(): + cursor.setPosition(position) + cursor.movePosition(QTextCursor.Right, + QTextCursor.KeepAnchor) + return to_text_string(cursor.selectedText()) + else: + return '' + + def insert_text(self, text): + """Insert text at cursor position""" + if not self.isReadOnly(): + self.textCursor().insertText(text) + + def replace_text(self, position_from, position_to, text): + cursor = self.__select_text(position_from, position_to) + cursor.removeSelectedText() + cursor.insertText(text) + + def remove_text(self, position_from, position_to): + cursor = self.__select_text(position_from, position_to) + cursor.removeSelectedText() + + def get_current_word(self): + """Return current word, i.e. word at cursor position""" + cursor = self.textCursor() + + if cursor.hasSelection(): + # Removes the selection and moves the cursor to the left side + # of the selection: this is required to be able to properly + # select the whole word under cursor (otherwise, the same word is + # not selected when the cursor is at the right side of it): + cursor.setPosition(min([cursor.selectionStart(), + cursor.selectionEnd()])) + else: + # Checks if the first character to the right is a white space + # and if not, moves the cursor one word to the left (otherwise, + # if the character to the left do not match the "word regexp" + # (see below), the word to the left of the cursor won't be + # selected), but only if the first character to the left is not a + # white space too. + def is_space(move): + curs = self.textCursor() + curs.movePosition(move, QTextCursor.KeepAnchor) + return not to_text_string(curs.selectedText()).strip() + if is_space(QTextCursor.NextCharacter): + if is_space(QTextCursor.PreviousCharacter): + return + cursor.movePosition(QTextCursor.WordLeft) + + cursor.select(QTextCursor.WordUnderCursor) + text = to_text_string(cursor.selectedText()) + match = re.findall(r'([a-zA-Z\_]+[0-9a-zA-Z\_]*)', text) + if match: + return match[0] + + def get_current_line(self): + """Return current line's text""" + cursor = self.textCursor() + cursor.select(QTextCursor.BlockUnderCursor) + return to_text_string(cursor.selectedText()) + + def get_current_line_to_cursor(self): + """Return text from prompt to cursor""" + return self.get_text(self.current_prompt_pos, 'cursor') + + def get_line_number_at(self, coordinates): + """Return line number at *coordinates* (QPoint)""" + cursor = self.cursorForPosition(coordinates) + return cursor.blockNumber()-1 + + def get_line_at(self, coordinates): + """Return line at *coordinates* (QPoint)""" + cursor = self.cursorForPosition(coordinates) + cursor.select(QTextCursor.BlockUnderCursor) + return to_text_string(cursor.selectedText()).replace(u('\u2029'), '') + + def get_word_at(self, coordinates): + """Return word at *coordinates* (QPoint)""" + cursor = self.cursorForPosition(coordinates) + cursor.select(QTextCursor.WordUnderCursor) + return to_text_string(cursor.selectedText()) + + def get_block_indentation(self, block_nb): + """Return line indentation (character number)""" + text = to_text_string(self.document().findBlockByNumber(block_nb).text()) + return len(text)-len(text.lstrip()) + + def get_selection_bounds(self): + """Return selection bounds (block numbers)""" + cursor = self.textCursor() + start, end = cursor.selectionStart(), cursor.selectionEnd() + block_start = self.document().findBlock(start) + block_end = self.document().findBlock(end) + return sorted([block_start.blockNumber(), block_end.blockNumber()]) + + + #------Text selection + def has_selected_text(self): + """Returns True if some text is selected""" + return bool(to_text_string(self.textCursor().selectedText())) + + def get_selected_text(self): + """ + Return text selected by current text cursor, converted in unicode + + Replace the unicode line separator character \u2029 by + the line separator characters returned by get_line_separator + """ + return to_text_string(self.textCursor().selectedText()).replace(u("\u2029"), + self.get_line_separator()) + + def remove_selected_text(self): + """Delete selected text""" + self.textCursor().removeSelectedText() + + def replace(self, text, pattern=None): + """Replace selected text by *text* + If *pattern* is not None, replacing selected text using regular + expression text substitution""" + cursor = self.textCursor() + cursor.beginEditBlock() + if pattern is not None: + seltxt = to_text_string(cursor.selectedText()) + cursor.removeSelectedText() + if pattern is not None: + text = re.sub(to_text_string(pattern), + to_text_string(text), to_text_string(seltxt)) + cursor.insertText(text) + cursor.endEditBlock() + + + #------Find/replace + def find_multiline_pattern(self, regexp, cursor, findflag): + """Reimplement QTextDocument's find method + + Add support for *multiline* regular expressions""" + pattern = to_text_string(regexp.pattern()) + text = to_text_string(self.toPlainText()) + try: + regobj = re.compile(pattern) + except sre_constants.error: + return + if findflag & QTextDocument.FindBackward: + # Find backward + offset = min([cursor.selectionEnd(), cursor.selectionStart()]) + text = text[:offset] + matches = [_m for _m in regobj.finditer(text, 0, offset)] + if matches: + match = matches[-1] + else: + return + else: + # Find forward + offset = max([cursor.selectionEnd(), cursor.selectionStart()]) + match = regobj.search(text, offset) + if match: + pos1, pos2 = match.span() + fcursor = self.textCursor() + fcursor.setPosition(pos1) + fcursor.setPosition(pos2, QTextCursor.KeepAnchor) + return fcursor + + def find_text(self, text, changed=True, forward=True, case=False, + words=False, regexp=False): + """Find text""" + cursor = self.textCursor() + findflag = QTextDocument.FindFlag() + if not forward: + findflag = findflag | QTextDocument.FindBackward + moves = [QTextCursor.NoMove] + if forward: + moves += [QTextCursor.NextWord, QTextCursor.Start] + if changed: + if to_text_string(cursor.selectedText()): + new_position = min([cursor.selectionStart(), + cursor.selectionEnd()]) + cursor.setPosition(new_position) + else: + cursor.movePosition(QTextCursor.PreviousWord) + else: + moves += [QTextCursor.End] + if not regexp: + text = re.escape(to_text_string(text)) + pattern = QRegExp(r"\b%s\b" % text if words else text, + Qt.CaseSensitive if case else Qt.CaseInsensitive, + QRegExp.RegExp2) + for move in moves: + cursor.movePosition(move) + if regexp and '\\n' in text: + # Multiline regular expression + found_cursor = self.find_multiline_pattern(pattern, cursor, + findflag) + else: + # Single line find: using the QTextDocument's find function, + # probably much more efficient than ours + found_cursor = self.document().find(pattern, cursor, findflag) + if found_cursor is not None and not found_cursor.isNull(): + self.setTextCursor(found_cursor) + return True + return False + + def is_editor(self): + """Needs to be overloaded in the codeeditor where it will be True""" + return False + + # --- Numpy matrix/array helper / See 'spyder/widgets/arraybuilder.py' + def enter_array_inline(self): + """ """ + self._enter_array(True) + + def enter_array_table(self): + """ """ + self._enter_array(False) + + def _enter_array(self, inline): + """ """ + offset = self.get_position('cursor') - self.get_position('sol') + rect = self.cursorRect() + dlg = NumpyArrayDialog(self, inline, offset) + + # TODO: adapt to font size + x = rect.left() + x = x + self.get_linenumberarea_width() - 14 + y = rect.top() + (rect.bottom() - rect.top())/2 + y = y - dlg.height()/2 - 3 + + pos = QPoint(x, y) + dlg.move(self.mapToGlobal(pos)) + + # called from editor + if self.is_editor(): + python_like_check = self.is_python_like() + suffix = '\n' + # called from a console + else: + python_like_check = True + suffix = '' + + if python_like_check and dlg.exec_(): + text = dlg.text() + suffix + if text != '': + cursor = self.textCursor() + cursor.beginEditBlock() + cursor.insertText(text) + cursor.endEditBlock() + + +class TracebackLinksMixin(object): + """ """ + QT_CLASS = None + go_to_error = None + + def __init__(self): + self.__cursor_changed = False + self.setMouseTracking(True) + + #------Mouse events + def mouseReleaseEvent(self, event): + """Go to error""" + self.QT_CLASS.mouseReleaseEvent(self, event) + text = self.get_line_at(event.pos()) + if get_error_match(text) and not self.has_selected_text(): + if self.go_to_error is not None: + self.go_to_error.emit(text) + + def mouseMoveEvent(self, event): + """Show Pointing Hand Cursor on error messages""" + text = self.get_line_at(event.pos()) + if get_error_match(text): + if not self.__cursor_changed: + QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor)) + self.__cursor_changed = True + event.accept() + return + if self.__cursor_changed: + QApplication.restoreOverrideCursor() + self.__cursor_changed = False + self.QT_CLASS.mouseMoveEvent(self, event) + + def leaveEvent(self, event): + """If cursor has not been restored yet, do it now""" + if self.__cursor_changed: + QApplication.restoreOverrideCursor() + self.__cursor_changed = False + self.QT_CLASS.leaveEvent(self, event) + + +class GetHelpMixin(object): + def __init__(self): + self.help = None + self.help_enabled = False + + def set_help(self, help_plugin): + """Set Help DockWidget reference""" + self.help = help_plugin + + def set_help_enabled(self, state): + self.help_enabled = state + + def inspect_current_object(self): + text = '' + text1 = self.get_text('sol', 'cursor') + tl1 = re.findall(r'([a-zA-Z_]+[0-9a-zA-Z_\.]*)', text1) + if tl1 and text1.endswith(tl1[-1]): + text += tl1[-1] + text2 = self.get_text('cursor', 'eol') + tl2 = re.findall(r'([0-9a-zA-Z_\.]+[0-9a-zA-Z_\.]*)', text2) + if tl2 and text2.startswith(tl2[0]): + text += tl2[0] + if text: + self.show_object_info(text, force=True) + + def show_object_info(self, text, call=False, force=False): + """Show signature calltip and/or docstring in the Help plugin""" + text = to_text_string(text) + + # Show docstring + help_enabled = self.help_enabled or force + if force and self.help is not None: + self.help.dockwidget.setVisible(True) + self.help.dockwidget.raise_() + if help_enabled and (self.help is not None) and \ + (self.help.dockwidget.isVisible()): + # Help widget exists and is visible + if hasattr(self, 'get_doc'): + self.help.set_shell(self) + else: + self.help.set_shell(self.parent()) + self.help.set_object_text(text, ignore_unknown=False) + self.setFocus() # if help was not at top level, raising it to + # top will automatically give it focus because of + # the visibility_changed signal, so we must give + # focus back to shell + + # Show calltip + if call and self.calltips: + # Display argument list if this is a function call + iscallable = self.iscallable(text) + if iscallable is not None: + if iscallable: + arglist = self.get_arglist(text) + name = text.split('.')[-1] + argspec = signature = '' + if isinstance(arglist, bool): + arglist = [] + if arglist: + argspec = '(' + ''.join(arglist) + ')' + else: + doc = self.get__doc__(text) + if doc is not None: + # This covers cases like np.abs, whose docstring is + # the same as np.absolute and because of that a + # proper signature can't be obtained correctly + argspec = getargspecfromtext(doc) + if not argspec: + signature = getsignaturefromtext(doc, name) + if argspec or signature: + if argspec: + tiptext = name + argspec + else: + tiptext = signature + self.show_calltip(_("Arguments"), tiptext, + signature=True, color='#2D62FF') + + def get_last_obj(self, last=False): + """ + Return the last valid object on the current line + """ + return getobj(self.get_current_line_to_cursor(), last=last) + + +class SaveHistoryMixin(object): + + INITHISTORY = None + SEPARATOR = None + append_to_history = None + + def __init__(self): + pass + + def add_to_history(self, command): + """Add command to history""" + command = to_text_string(command) + if command in ['', '\n'] or command.startswith('Traceback'): + return + if command.endswith('\n'): + command = command[:-1] + self.histidx = None + if len(self.history)>0 and self.history[-1] == command: + return + self.history.append(command) + text = os.linesep + command + + # When the first entry will be written in history file, + # the separator will be append first: + if self.history_filename not in HISTORY_FILENAMES: + HISTORY_FILENAMES.append(self.history_filename) + text = self.SEPARATOR + text + + encoding.write(text, self.history_filename, mode='ab') + if self.append_to_history is not None: + self.append_to_history.emit(self.history_filename, text) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/onecolumntree.py spyder-3.0.2+dfsg1/spyder/widgets/onecolumntree.py --- spyder-2.3.8+dfsg1/spyder/widgets/onecolumntree.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/onecolumntree.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +# Third party imports +from qtpy.QtCore import Slot +from qtpy.QtWidgets import QTreeWidget, QMenu + +# Local imports +from spyder.config.base import _ +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import (add_actions, create_action, + get_item_user_text) + + +class OneColumnTree(QTreeWidget): + """One-column tree widget with context menu, ...""" + def __init__(self, parent): + QTreeWidget.__init__(self, parent) + self.setItemsExpandable(True) + self.setColumnCount(1) + self.itemActivated.connect(self.activated) + self.itemClicked.connect(self.clicked) + # Setup context menu + self.menu = QMenu(self) + self.collapse_all_action = None + self.collapse_selection_action = None + self.expand_all_action = None + self.expand_selection_action = None + self.common_actions = self.setup_common_actions() + + self.__expanded_state = None + + self.itemSelectionChanged.connect(self.item_selection_changed) + self.item_selection_changed() + + def activated(self, item): + """Double-click event""" + raise NotImplementedError + + def clicked(self, item): + pass + + def set_title(self, title): + self.setHeaderLabels([title]) + + def setup_common_actions(self): + """Setup context menu common actions""" + self.collapse_all_action = create_action(self, + text=_('Collapse all'), + icon=ima.icon('collapse'), + triggered=self.collapseAll) + self.expand_all_action = create_action(self, + text=_('Expand all'), + icon=ima.icon('expand'), + triggered=self.expandAll) + self.restore_action = create_action(self, + text=_('Restore'), + tip=_('Restore original tree layout'), + icon=ima.icon('restore'), + triggered=self.restore) + self.collapse_selection_action = create_action(self, + text=_('Collapse selection'), + icon=ima.icon('collapse_selection'), + triggered=self.collapse_selection) + self.expand_selection_action = create_action(self, + text=_('Expand selection'), + icon=ima.icon('expand_selection'), + triggered=self.expand_selection) + return [self.collapse_all_action, self.expand_all_action, + self.restore_action, None, + self.collapse_selection_action, self.expand_selection_action] + + def update_menu(self): + self.menu.clear() + items = self.selectedItems() + actions = self.get_actions_from_items(items) + if actions: + actions.append(None) + actions += self.common_actions + add_actions(self.menu, actions) + + def get_actions_from_items(self, items): + # Right here: add other actions if necessary + # (reimplement this method) + return [] + + @Slot() + def restore(self): + self.collapseAll() + for item in self.get_top_level_items(): + self.expandItem(item) + + def is_item_expandable(self, item): + """To be reimplemented in child class + See example in project explorer widget""" + return True + + def __expand_item(self, item): + if self.is_item_expandable(item): + self.expandItem(item) + for index in range(item.childCount()): + child = item.child(index) + self.__expand_item(child) + + @Slot() + def expand_selection(self): + items = self.selectedItems() + if not items: + items = self.get_top_level_items() + for item in items: + self.__expand_item(item) + if items: + self.scrollToItem(items[0]) + + def __collapse_item(self, item): + self.collapseItem(item) + for index in range(item.childCount()): + child = item.child(index) + self.__collapse_item(child) + + @Slot() + def collapse_selection(self): + items = self.selectedItems() + if not items: + items = self.get_top_level_items() + for item in items: + self.__collapse_item(item) + if items: + self.scrollToItem(items[0]) + + def item_selection_changed(self): + """Item selection has changed""" + is_selection = len(self.selectedItems()) > 0 + self.expand_selection_action.setEnabled(is_selection) + self.collapse_selection_action.setEnabled(is_selection) + + def get_top_level_items(self): + """Iterate over top level items""" + return [self.topLevelItem(_i) for _i in range(self.topLevelItemCount())] + + def get_items(self): + """Return items (excluding top level items)""" + itemlist = [] + def add_to_itemlist(item): + for index in range(item.childCount()): + citem = item.child(index) + itemlist.append(citem) + add_to_itemlist(citem) + for tlitem in self.get_top_level_items(): + add_to_itemlist(tlitem) + return itemlist + + def get_scrollbar_position(self): + return (self.horizontalScrollBar().value(), + self.verticalScrollBar().value()) + + def set_scrollbar_position(self, position): + hor, ver = position + self.horizontalScrollBar().setValue(hor) + self.verticalScrollBar().setValue(ver) + + def get_expanded_state(self): + self.save_expanded_state() + return self.__expanded_state + + def set_expanded_state(self, state): + self.__expanded_state = state + self.restore_expanded_state() + + def save_expanded_state(self): + """Save all items expanded state""" + self.__expanded_state = {} + def add_to_state(item): + user_text = get_item_user_text(item) + self.__expanded_state[hash(user_text)] = item.isExpanded() + def browse_children(item): + add_to_state(item) + for index in range(item.childCount()): + citem = item.child(index) + user_text = get_item_user_text(citem) + self.__expanded_state[hash(user_text)] = citem.isExpanded() + browse_children(citem) + for tlitem in self.get_top_level_items(): + browse_children(tlitem) + + def restore_expanded_state(self): + """Restore all items expanded state""" + if self.__expanded_state is None: + return + for item in self.get_items()+self.get_top_level_items(): + user_text = get_item_user_text(item) + is_expanded = self.__expanded_state.get(hash(user_text)) + if is_expanded is not None: + item.setExpanded(is_expanded) + + def sort_top_level_items(self, key): + """Sorting tree wrt top level items""" + self.save_expanded_state() + items = sorted([self.takeTopLevelItem(0) + for index in range(self.topLevelItemCount())], key=key) + for index, item in enumerate(items): + self.insertTopLevelItem(index, item) + self.restore_expanded_state() + + def contextMenuEvent(self, event): + """Override Qt method""" + self.update_menu() + self.menu.popup(event.globalPos()) + diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/pathmanager.py spyder-3.0.2+dfsg1/spyder/widgets/pathmanager.py --- spyder-2.3.8+dfsg1/spyder/widgets/pathmanager.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/pathmanager.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Spyder path manager""" + +# Standard library imports +from __future__ import print_function +import os +import os.path as osp +import sys + +# Third party imports +from qtpy.compat import getexistingdirectory +from qtpy.QtCore import Qt, Signal, Slot +from qtpy.QtWidgets import (QDialog, QDialogButtonBox, QHBoxLayout, + QListWidget, QListWidgetItem, QMessageBox, + QVBoxLayout) + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import getcwd +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import create_toolbutton + + +class PathManager(QDialog): + redirect_stdio = Signal(bool) + + def __init__(self, parent=None, pathlist=None, ro_pathlist=None, sync=True): + QDialog.__init__(self, parent) + + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + + assert isinstance(pathlist, list) + self.pathlist = pathlist + if ro_pathlist is None: + ro_pathlist = [] + self.ro_pathlist = ro_pathlist + + self.last_path = getcwd() + + self.setWindowTitle(_("PYTHONPATH manager")) + self.setWindowIcon(ima.icon('pythonpath')) + self.resize(500, 300) + + self.selection_widgets = [] + + layout = QVBoxLayout() + self.setLayout(layout) + + top_layout = QHBoxLayout() + layout.addLayout(top_layout) + self.toolbar_widgets1 = self.setup_top_toolbar(top_layout) + + self.listwidget = QListWidget(self) + self.listwidget.currentRowChanged.connect(self.refresh) + layout.addWidget(self.listwidget) + + bottom_layout = QHBoxLayout() + layout.addLayout(bottom_layout) + self.sync_button = None + self.toolbar_widgets2 = self.setup_bottom_toolbar(bottom_layout, sync) + + # Buttons configuration + bbox = QDialogButtonBox(QDialogButtonBox.Close) + bbox.rejected.connect(self.reject) + bottom_layout.addWidget(bbox) + + self.update_list() + self.refresh() + + def _add_widgets_to_layout(self, layout, widgets): + layout.setAlignment(Qt.AlignLeft) + for widget in widgets: + layout.addWidget(widget) + + def setup_top_toolbar(self, layout): + toolbar = [] + movetop_button = create_toolbutton(self, + text=_("Move to top"), + icon=ima.icon('2uparrow'), + triggered=lambda: self.move_to(absolute=0), + text_beside_icon=True) + toolbar.append(movetop_button) + moveup_button = create_toolbutton(self, + text=_("Move up"), + icon=ima.icon('1uparrow'), + triggered=lambda: self.move_to(relative=-1), + text_beside_icon=True) + toolbar.append(moveup_button) + movedown_button = create_toolbutton(self, + text=_("Move down"), + icon=ima.icon('1downarrow'), + triggered=lambda: self.move_to(relative=1), + text_beside_icon=True) + toolbar.append(movedown_button) + movebottom_button = create_toolbutton(self, + text=_("Move to bottom"), + icon=ima.icon('2downarrow'), + triggered=lambda: self.move_to(absolute=1), + text_beside_icon=True) + toolbar.append(movebottom_button) + self.selection_widgets.extend(toolbar) + self._add_widgets_to_layout(layout, toolbar) + return toolbar + + def setup_bottom_toolbar(self, layout, sync=True): + toolbar = [] + add_button = create_toolbutton(self, text=_('Add path'), + icon=ima.icon('edit_add'), + triggered=self.add_path, + text_beside_icon=True) + toolbar.append(add_button) + remove_button = create_toolbutton(self, text=_('Remove path'), + icon=ima.icon('edit_remove'), + triggered=self.remove_path, + text_beside_icon=True) + toolbar.append(remove_button) + self.selection_widgets.append(remove_button) + self._add_widgets_to_layout(layout, toolbar) + layout.addStretch(1) + if os.name == 'nt' and sync: + self.sync_button = create_toolbutton(self, + text=_("Synchronize..."), + icon=ima.icon('fileimport'), triggered=self.synchronize, + tip=_("Synchronize Spyder's path list with PYTHONPATH " + "environment variable"), + text_beside_icon=True) + layout.addWidget(self.sync_button) + return toolbar + + @Slot() + def synchronize(self): + """ + Synchronize Spyder's path list with PYTHONPATH environment variable + Only apply to: current user, on Windows platforms + """ + answer = QMessageBox.question(self, _("Synchronize"), + _("This will synchronize Spyder's path list with " + "PYTHONPATH environment variable for current user, " + "allowing you to run your Python modules outside Spyder " + "without having to configure sys.path. " + "
    Do you want to clear contents of PYTHONPATH before " + "adding Spyder's path list?"), + QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) + if answer == QMessageBox.Cancel: + return + elif answer == QMessageBox.Yes: + remove = True + else: + remove = False + from spyder.utils.environ import (get_user_env, set_user_env, + listdict2envdict) + env = get_user_env() + if remove: + ppath = self.pathlist+self.ro_pathlist + else: + ppath = env.get('PYTHONPATH', []) + if not isinstance(ppath, list): + ppath = [ppath] + ppath = [path for path in ppath + if path not in (self.pathlist+self.ro_pathlist)] + ppath.extend(self.pathlist+self.ro_pathlist) + env['PYTHONPATH'] = ppath + set_user_env( listdict2envdict(env), parent=self ) + + def get_path_list(self): + """Return path list (does not include the read-only path list)""" + return self.pathlist + + def update_list(self): + """Update path list""" + self.listwidget.clear() + for name in self.pathlist+self.ro_pathlist: + item = QListWidgetItem(name) + item.setIcon(ima.icon('DirClosedIcon')) + if name in self.ro_pathlist: + item.setFlags(Qt.NoItemFlags) + self.listwidget.addItem(item) + self.refresh() + + def refresh(self, row=None): + """Refresh widget""" + for widget in self.selection_widgets: + widget.setEnabled(self.listwidget.currentItem() is not None) + not_empty = self.listwidget.count() > 0 + if self.sync_button is not None: + self.sync_button.setEnabled(not_empty) + + def move_to(self, absolute=None, relative=None): + index = self.listwidget.currentRow() + if absolute is not None: + if absolute: + new_index = len(self.pathlist)-1 + else: + new_index = 0 + else: + new_index = index + relative + new_index = max(0, min(len(self.pathlist)-1, new_index)) + path = self.pathlist.pop(index) + self.pathlist.insert(new_index, path) + self.update_list() + self.listwidget.setCurrentRow(new_index) + + @Slot() + def remove_path(self): + answer = QMessageBox.warning(self, _("Remove path"), + _("Do you really want to remove selected path?"), + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + self.pathlist.pop(self.listwidget.currentRow()) + self.update_list() + + @Slot() + def add_path(self): + self.redirect_stdio.emit(False) + directory = getexistingdirectory(self, _("Select directory"), + self.last_path) + self.redirect_stdio.emit(True) + if directory: + directory = osp.abspath(directory) + self.last_path = directory + if directory in self.pathlist: + answer = QMessageBox.question(self, _("Add path"), + _("This directory is already included in Spyder path " + "list.
    Do you want to move it to the top of " + "the list?"), + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + self.pathlist.remove(directory) + else: + return + self.pathlist.insert(0, directory) + self.update_list() + + +def test(): + """Run path manager test""" + from spyder.utils.qthelpers import qapplication + _app = qapplication() # analysis:ignore + test = PathManager(None, pathlist=sys.path[:-10], + ro_pathlist=sys.path[-10:]) + test.exec_() + print(test.get_path_list()) + + +if __name__ == "__main__": + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/projects/configdialog.py spyder-3.0.2+dfsg1/spyder/widgets/projects/configdialog.py --- spyder-2.3.8+dfsg1/spyder/widgets/projects/configdialog.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/projects/configdialog.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © Spyder Project Contributors +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) +# ----------------------------------------------------------------------------- +"""Configuration dialog for projects""" + +from qtpy.QtWidgets import QGroupBox, QVBoxLayout + +from spyder.config.base import _ +from spyder.plugins.configdialog import ConfigDialog, GeneralConfigPage +from spyder.utils.qthelpers import get_icon +from spyder.config.user import NoDefault +from spyder.widgets.projects import EmptyProject +from spyder.widgets.projects.config import (WORKSPACE, VCS, ENCODING, + CODESTYLE) + + +class ProjectPreferences(ConfigDialog): + """ """ + def __init__(self, parent, project): + super(ProjectPreferences, self).__init__() + + self._main = parent + self._project = project + self._project_preferences = [WorkspaceConfigPage] #, VersionConfigPage] + + self.setWindowTitle(_("Project preferences")) + self.setWindowIcon(get_icon("configure.png")) + + self.setup_dialog() + + def setup_dialog(self): + """ """ + # Move to spyder.py +# dlg = ConfigDialog(self) +# dlg.size_change.connect(self.set_prefs_size) +# if self.prefs_dialog_size is not None: +# dlg.resize(self.prefs_dialog_size) + for PrefPageClass in self._project_preferences: + widget = PrefPageClass(self, self._main, self._project) + widget.initialize() + self.add_page(widget) + + +class ProjectConfigPage(GeneralConfigPage): + """General config page that redefines the configuration accessors.""" + CONF_SECTION = None + NAME = None + ICON = None + + def __init__(self, parent, main, project): + self._project = project + self._conf_files = project.get_conf_files() + self._conf = self._conf_files[self.CONF_SECTION] + + GeneralConfigPage.__init__(self, parent, main) + + def set_option(self, option, value): + """ """ + CONF = self._conf + CONF.set(self.CONF_SECTION, option, value) + + def get_option(self, option, default=NoDefault): + """" """ + CONF = self._conf + return CONF.get(self.CONF_SECTION, option, default) + + +class WorkspaceConfigPage(ProjectConfigPage): + CONF_SECTION = WORKSPACE + NAME = _("General") + ICON = "genprefs.png" + + def setup_page(self): + newcb = self.create_checkbox + + # --- Workspace + interface_group = QGroupBox(_("Interface")) + restore_data_box = newcb(_("Restore data on startup"), + 'restore_data_on_startup') + save_data_box = newcb(_("Save data on exit"), + 'save_data_on_exit') + save_history_box = newcb(_("Save history"), + 'save_history') + save_non_project_box = newcb(_("Save non project files opened"), + 'save_non_project_files') + + interface_layout = QVBoxLayout() + interface_layout.addWidget(restore_data_box) + interface_layout.addWidget(save_data_box) + interface_layout.addWidget(save_history_box) + interface_layout.addWidget(save_non_project_box) + interface_group.setLayout(interface_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(interface_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + def apply_settings(self, options): + """ """ + pass # TODO: + #self.main.apply_settings() + + +class CodeConfigPage(ProjectConfigPage): + CONF_SECTION = CODESTYLE + NAME = _("Code") + ICON = "genprefs.png" + + def setup_page(self): + newcb = self.create_checkbox + + # --- Workspace + interface_group = QGroupBox(_("Workspace")) + restore_data_box = newcb(_("Restore data on startup"), + 'restore_data_on_startup') + save_data_box = newcb(_("Save data on exit"), + 'save_data_on_exit') + save_history_box = newcb(_("Save history"), + 'save_history') + save_non_project_box = newcb(_("Save non project files opened"), + 'save_non_project_files') + + interface_layout = QVBoxLayout() + interface_layout.addWidget(restore_data_box) + interface_layout.addWidget(save_data_box) + interface_layout.addWidget(save_history_box) + interface_layout.addWidget(save_non_project_box) + interface_group.setLayout(interface_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(interface_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + def apply_settings(self, options): + """ """ + print('applied') + #self.main.apply_settings() + + +class VersionConfigPage(ProjectConfigPage): + CONF_SECTION = VCS + NAME = _("Version control") + ICON = "genprefs.png" + + def setup_page(self): + newcb = self.create_checkbox + + # --- Workspace + vcs_group = QGroupBox(_("Version control")) + use_version_control = newcb(_("Use version control"), + 'use_version_control') + + styles = ['git', 'hg'] + choices = list(zip(styles, [style.lower() for style in styles])) + vcs_combo = self.create_combobox(_('Version control system'), choices, + 'version_control_system', + default='git') + + vcs_layout = QVBoxLayout() + vcs_layout.addWidget(use_version_control) + vcs_layout.addWidget(vcs_combo) + vcs_group.setLayout(vcs_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(vcs_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + def apply_settings(self, options): + """ """ + print('applied') + #self.main.apply_settings() + + +if __name__ == "__main__": + import os.path as osp + import tempfile + from spyder.utils.qthelpers import qapplication + app = qapplication() + proj_dir = tempfile.mkdtemp() + osp.sep + '.spyproject' + proj = EmptyProject(proj_dir) + dlg = ProjectPreferences(None, proj) + dlg.show() + app.exec_() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/projects/config.py spyder-3.0.2+dfsg1/spyder/widgets/projects/config.py --- spyder-2.3.8+dfsg1/spyder/widgets/projects/config.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/projects/config.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © Spyder Project Contributors +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) +# ----------------------------------------------------------------------------- +"""Configuration options for projects""" + +# Standard library imports +import os + +# Local imports +from spyder.config.user import UserConfig + +PROJECT_FILENAME = '.spyproj' +PROJECT_FOLDER = '.spyproject' + + +# Project configuration defaults +WORKSPACE = 'workspace' +WORKSPACE_DEFAULTS = [ + (WORKSPACE, + {'restore_data_on_startup': True, + 'save_data_on_exit': True, + 'save_history': True, + 'save_non_project_files': False, + } + )] +WORKSPACE_VERSION = '0.1.0' + + +CODESTYLE = 'codestyle' +CODESTYLE_DEFAULTS = [ + (CODESTYLE, + {'indentation': True, + } + )] +CODESTYLE_VERSION = '0.1.0' + + +ENCODING = 'encoding' +ENCODING_DEFAULTS = [ + (ENCODING, + {'text_encoding': 'utf-8', + } + )] +ENCODING_VERSION = '0.1.0' + + +VCS = 'vcs' +VCS_DEFAULTS = [ + (VCS, + {'use_version_control': False, + 'version_control_system': '', + } + )] +VCS_VERSION = '0.1.0' + + +class ProjectConfig(UserConfig): + """ProjectConfig class, based on UserConfig. + + Parameters + ---------- + name: str + name of the config + defaults: tuple + dictionnary containing options *or* list of tuples + (section_name, options) + version: str + version of the configuration file (X.Y.Z format) + filename: str + configuration file will be saved in %home%/subfolder/%name%.ini + """ + DEFAULT_SECTION_NAME = 'main' + + def __init__(self, name, root_path, filename, defaults=None, load=True, + version=None): + self.project_root_path = root_path + + # Config rootpath + self._root_path = os.path.join(root_path, PROJECT_FOLDER) + self._filename = filename + + # Create folder if non existent + if not os.path.isdir(self._root_path): + os.makedirs(self._root_path) + + # Add file + # NOTE: We have to think better about the uses of this file + # with open(os.path.join(root_path, PROJECT_FILENAME), 'w') as f: + # f.write('spyder-ide project\n') + + UserConfig.__init__(self, name, defaults=defaults, load=load, + version=version, subfolder=None, backup=False, + raw_mode=True, remove_obsolete=True) + diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/projects/explorer.py spyder-3.0.2+dfsg1/spyder/widgets/projects/explorer.py --- spyder-2.3.8+dfsg1/spyder/widgets/projects/explorer.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/projects/explorer.py 2016-11-17 03:39:40.000000000 +0000 @@ -0,0 +1,304 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Project Explorer""" + +# pylint: disable=C0103 + +# Standard library imports +from __future__ import print_function + +import os.path as osp +import shutil + +# Third party imports +from qtpy import PYQT5 +from qtpy.QtCore import Qt, Signal, Slot +from qtpy.QtWidgets import (QAbstractItemView, QHBoxLayout, QHeaderView, + QLabel, QMessageBox, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import to_text_string +from spyder.utils import misc +from spyder.utils.qthelpers import create_action +from spyder.widgets.explorer import FilteredDirView + + +class ExplorerTreeWidget(FilteredDirView): + """Explorer tree widget""" + + sig_delete_project = Signal() + + def __init__(self, parent, show_hscrollbar=True): + FilteredDirView.__init__(self, parent) + self.last_folder = None + self.setSelectionMode(FilteredDirView.ExtendedSelection) + self.show_hscrollbar = show_hscrollbar + + # Enable drag & drop events + self.setDragEnabled(True) + self.setDragDropMode(FilteredDirView.DragDrop) + + #------DirView API--------------------------------------------------------- + def setup_common_actions(self): + """Setup context menu common actions""" + actions = FilteredDirView.setup_common_actions(self) + + # Toggle horizontal scrollbar + hscrollbar_action = create_action(self, _("Show horizontal scrollbar"), + toggled=self.toggle_hscrollbar) + hscrollbar_action.setChecked(self.show_hscrollbar) + self.toggle_hscrollbar(self.show_hscrollbar) + + return actions + [hscrollbar_action] + + #------Public API---------------------------------------------------------- + @Slot(bool) + def toggle_hscrollbar(self, checked): + """Toggle horizontal scrollbar""" + self.parent_widget.sig_option_changed.emit('show_hscrollbar', checked) + self.show_hscrollbar = checked + self.header().setStretchLastSection(not checked) + self.header().setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) + if PYQT5: + self.header().setSectionResizeMode(QHeaderView.ResizeToContents) + else: + self.header().setResizeMode(QHeaderView.ResizeToContents) + + #---- Internal drag & drop + def dragMoveEvent(self, event): + """Reimplement Qt method""" + index = self.indexAt(event.pos()) + if index: + dst = self.get_filename(index) + if osp.isdir(dst): + event.acceptProposedAction() + else: + event.ignore() + else: + event.ignore() + + def dropEvent(self, event): + """Reimplement Qt method""" + event.ignore() + action = event.dropAction() + if action not in (Qt.MoveAction, Qt.CopyAction): + return + + # QTreeView must not remove the source items even in MoveAction mode: + # event.setDropAction(Qt.CopyAction) + + dst = self.get_filename(self.indexAt(event.pos())) + yes_to_all, no_to_all = None, None + src_list = [to_text_string(url.toString()) + for url in event.mimeData().urls()] + if len(src_list) > 1: + buttons = QMessageBox.Yes|QMessageBox.YesAll| \ + QMessageBox.No|QMessageBox.NoAll|QMessageBox.Cancel + else: + buttons = QMessageBox.Yes|QMessageBox.No + for src in src_list: + if src == dst: + continue + dst_fname = osp.join(dst, osp.basename(src)) + if osp.exists(dst_fname): + if yes_to_all is not None or no_to_all is not None: + if no_to_all: + continue + elif osp.isfile(dst_fname): + answer = QMessageBox.warning(self, _('Project explorer'), + _('File %s already exists.
    ' + 'Do you want to overwrite it?') % dst_fname, + buttons) + if answer == QMessageBox.No: + continue + elif answer == QMessageBox.Cancel: + break + elif answer == QMessageBox.YesAll: + yes_to_all = True + elif answer == QMessageBox.NoAll: + no_to_all = True + continue + else: + QMessageBox.critical(self, _('Project explorer'), + _('Folder %s already exists.' + ) % dst_fname, QMessageBox.Ok) + event.setDropAction(Qt.CopyAction) + return + try: + if action == Qt.CopyAction: + if osp.isfile(src): + shutil.copy(src, dst) + else: + shutil.copytree(src, dst) + else: + if osp.isfile(src): + misc.move_file(src, dst) + else: + shutil.move(src, dst) + self.parent_widget.removed.emit(src) + except EnvironmentError as error: + if action == Qt.CopyAction: + action_str = _('copy') + else: + action_str = _('move') + QMessageBox.critical(self, _("Project Explorer"), + _("Unable to %s %s" + "

    Error message:
    %s" + ) % (action_str, src, + to_text_string(error))) + @Slot() + def delete(self, fnames=None): + """Delete files""" + if fnames is None: + fnames = self.get_selected_filenames() + multiple = len(fnames) > 1 + yes_to_all = None + for fname in fnames: + if fname == self.proxymodel.path_list[0]: + self.sig_delete_project.emit() + else: + yes_to_all = self.delete_file(fname, multiple, yes_to_all) + if yes_to_all is not None and not yes_to_all: + # Canceled + break + + +class ProjectExplorerWidget(QWidget): + """Project Explorer""" + sig_option_changed = Signal(str, object) + sig_open_file = Signal(str) + + def __init__(self, parent, name_filters=[], + show_all=True, show_hscrollbar=True): + QWidget.__init__(self, parent) + self.treewidget = None + self.emptywidget = None + self.name_filters = name_filters + self.show_all = show_all + self.show_hscrollbar = show_hscrollbar + self.setup_layout() + + def setup_layout(self): + """Setup project explorer widget layout""" + + self.emptywidget = ExplorerTreeWidget(self) + + layout = QVBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(self.emptywidget) + self.setLayout(layout) + + def closing_widget(self): + """Perform actions before widget is closed""" + pass + + def set_project_dir(self, directory): + """Set the project directory""" + if directory is not None: + project = directory.split(osp.sep)[-1] + self.treewidget.set_root_path(osp.dirname(directory)) + self.treewidget.set_folder_names([project]) + self.treewidget.setup_project_view() + try: + self.treewidget.setExpanded(self.treewidget.get_index(directory), + True) + except TypeError: + pass + + def clear(self): + """Show an empty view""" + self.treewidget.hide() + self.emptywidget.show() + + def setup_project(self, directory): + """Setup project""" + if self.treewidget is not None: + self.treewidget.hide() + + # Setup a new tree widget + self.treewidget = ExplorerTreeWidget(self, self.show_hscrollbar) + self.treewidget.setup(name_filters=self.name_filters, + show_all=self.show_all) + self.treewidget.setup_view() + self.emptywidget.hide() + self.treewidget.show() + self.layout().addWidget(self.treewidget) + + # Setup the directory shown by the tree + self.set_project_dir(directory) + + # Signal to delete the project + self.treewidget.sig_delete_project.connect(self.delete_project) + + def delete_project(self): + """Delete current project without deleting the files in the directory.""" + if self.current_active_project: + path = self.current_active_project.root_path + buttons = QMessageBox.Yes|QMessageBox.No + answer = QMessageBox.warning(self, _("Delete"), + _("Do you really want " + "to delete {filename}?

    " + "Note: This action will only delete " + "the project. Its files are going to be " + "preserved on disk." + ).format(filename=osp.basename(path)), + buttons) + if answer == QMessageBox.Yes: + try: + self.close_project() + shutil.rmtree(osp.join(path,'.spyproject')) + except EnvironmentError as error: + QMessageBox.critical(self, _("Project Explorer"), + _("Unable to delete {varpath}" + "

    The error message was:
    {error}" ) + .format(varpath=path,error=to_text_string(error))) + +#============================================================================== +# Tests +#============================================================================== +class Test(QWidget): + def __init__(self): + QWidget.__init__(self) + vlayout = QVBoxLayout() + self.setLayout(vlayout) + + self.explorer = ProjectExplorerWidget(None, show_all=True) + self.explorer.setup_project(osp.dirname(osp.abspath(__file__))) + vlayout.addWidget(self.explorer) + + hlayout1 = QHBoxLayout() + vlayout.addLayout(hlayout1) + label = QLabel("Open file:") + label.setAlignment(Qt.AlignRight) + hlayout1.addWidget(label) + self.label1 = QLabel() + hlayout1.addWidget(self.label1) + self.explorer.sig_open_file.connect(self.label1.setText) + + hlayout3 = QHBoxLayout() + vlayout.addLayout(hlayout3) + label = QLabel("Option changed:") + label.setAlignment(Qt.AlignRight) + hlayout3.addWidget(label) + self.label3 = QLabel() + hlayout3.addWidget(self.label3) + self.explorer.sig_option_changed.connect( + lambda x, y: self.label3.setText('option_changed: %r, %r' % (x, y))) + + +def test(): + from spyder.utils.qthelpers import qapplication + app = qapplication() + test = Test() + test.resize(250, 480) + test.show() + app.exec_() + + +if __name__ == "__main__": + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/projects/__init__.py spyder-3.0.2+dfsg1/spyder/widgets/projects/__init__.py --- spyder-2.3.8+dfsg1/spyder/widgets/projects/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/projects/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © Spyder Project Contributors +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) +# ----------------------------------------------------------------------------- +"""Projects""" + +# Local imports +from spyder.widgets.projects.type import EmptyProject +from spyder.widgets.projects.type.python import PythonProject + + +def get_available_project_types(): + """ """ +# return [EmptyProject, PythonProject, PythonPackageProject, DjangoProject, +# SpyderPluginProject] + get_available_project_types_plugins() + return ([EmptyProject] + + get_available_project_types_plugins()) + + +def get_available_project_types_plugins(): + """ """ + return [] + + +#def components(path): +# ''' +# Returns the individual components of the given file path +# string (for the local operating system). +# +# The returned components, when joined with os.path.join(), point to +# the same location as the original path. +# ''' +# components = [] +# # The loop guarantees that the returned components can be +# # os.path.joined with the path separator and point to the same +# # location: +# while True: +# (new_path, tail) = osp.split(path) # Works on any platform +# components.append(tail) +# if new_path == path: # Root (including drive, on Windows) reached +# break +# path = new_path +# components.append(new_path) +# +# components.reverse() # First component first +# return components +# +# +#def longest_prefix(iter0, iter1): +# ''' +# Returns the longest common prefix of the given two iterables. +# ''' +# longest_prefix = [] +# for (elmt0, elmt1) in itertools.izip(iter0, iter1): +# if elmt0 != elmt1: +# break +# longest_prefix.append(elmt0) +# return longest_prefix +# +# +#def common_prefix_path(path0, path1): +# return os.path.join(*longest_prefix(components(path0), components(path1))) +# + +# +#def has_children_files(path, include, exclude, show_all): +# """Return True if path has children files""" +# try: +# return len(listdir(path, include, exclude, show_all)) > 0 +# except (IOError, OSError): +# return False +# +# +#def is_drive_path(path): +# """Return True if path is a drive (Windows)""" +# path = osp.abspath(path) +# return osp.normpath(osp.join(path, osp.pardir)) == path +# +# +#def get_dir_icon(dirname, project): +# """Return appropriate directory icon""" +# if is_drive_path(dirname): +# return get_std_icon('DriveHDIcon') +# prefix = 'pp_' if dirname in project.get_pythonpath() else '' +# if dirname == project.root_path: +# if project.is_opened(): +# return get_icon(prefix+'project.png') +# else: +# return get_icon('project_closed.png') +# elif osp.isfile(osp.join(dirname, '__init__.py')): +# return get_icon(prefix+'package.png') +# else: +# return get_icon(prefix+'folder.png') diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/projects/projectdialog.py spyder-3.0.2+dfsg1/spyder/widgets/projects/projectdialog.py --- spyder-2.3.8+dfsg1/spyder/widgets/projects/projectdialog.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/projects/projectdialog.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,199 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © Spyder Project Contributors +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) +# ----------------------------------------------------------------------------- +"""Project creation dialog.""" + +from __future__ import print_function + +# Standard library imports +import errno +import os +import os.path as osp +import sys +import tempfile + +# Third party imports +from qtpy.compat import getexistingdirectory +from qtpy.QtCore import Qt, Signal +from qtpy.QtWidgets import (QVBoxLayout, QLabel, QLineEdit, QPushButton, + QDialog, QComboBox, QGridLayout, QToolButton, + QDialogButtonBox, QGroupBox, QRadioButton, + QHBoxLayout) + + +# Local imports +from spyder.config.base import _, get_home_dir +from spyder.utils.qthelpers import get_std_icon +from spyder.py3compat import to_text_string +from spyder.widgets.projects import get_available_project_types + + +def is_writable(path): + """Check if path has write access""" + try: + testfile = tempfile.TemporaryFile(dir=path) + testfile.close() + except OSError as e: + if e.errno == errno.EACCES: # 13 + return False + return True + + +class ProjectDialog(QDialog): + """Project creation dialog.""" + + # path, type, packages + sig_project_creation_requested = Signal(object, object, object) + + def __init__(self, parent): + """Project creation dialog.""" + super(ProjectDialog, self).__init__(parent=parent) + + # Variables + current_python_version = '.'.join([to_text_string(sys.version_info[0]), + to_text_string(sys.version_info[1])]) + python_versions = ['2.7', '3.4', '3.5'] + if current_python_version not in python_versions: + python_versions.append(current_python_version) + python_versions = sorted(python_versions) + + self.project_name = None + self.location = get_home_dir() + + # Widgets + self.groupbox = QGroupBox() + self.radio_new_dir = QRadioButton(_("New directory")) + self.radio_from_dir = QRadioButton(_("Existing directory")) + + self.label_project_name = QLabel(_('Project name')) + self.label_location = QLabel(_('Location')) + self.label_project_type = QLabel(_('Project type')) + self.label_python_version = QLabel(_('Python version')) + + self.text_project_name = QLineEdit() + self.text_location = QLineEdit(get_home_dir()) + self.combo_project_type = QComboBox() + self.combo_python_version = QComboBox() + + self.button_select_location = QToolButton() + self.button_cancel = QPushButton(_('Cancel')) + self.button_create = QPushButton(_('Create')) + + self.bbox = QDialogButtonBox(Qt.Horizontal) + self.bbox.addButton(self.button_cancel, QDialogButtonBox.ActionRole) + self.bbox.addButton(self.button_create, QDialogButtonBox.ActionRole) + + # Widget setup + self.combo_python_version.addItems(python_versions) + self.radio_new_dir.setChecked(True) + self.text_location.setEnabled(True) + self.text_location.setReadOnly(True) + self.button_select_location.setIcon(get_std_icon('DirOpenIcon')) + self.button_cancel.setDefault(True) + self.button_cancel.setAutoDefault(True) + self.button_create.setEnabled(False) + self.combo_project_type.addItems(self._get_project_types()) + self.combo_python_version.setCurrentIndex( + python_versions.index(current_python_version)) + self.setWindowTitle(_('Create new project')) + self.setFixedWidth(500) + self.label_python_version.setVisible(False) + self.combo_python_version.setVisible(False) + + # Layouts + layout_top = QHBoxLayout() + layout_top.addWidget(self.radio_new_dir) + layout_top.addWidget(self.radio_from_dir) + layout_top.addStretch(1) + self.groupbox.setLayout(layout_top) + + layout_grid = QGridLayout() + layout_grid.addWidget(self.label_project_name, 0, 0) + layout_grid.addWidget(self.text_project_name, 0, 1, 1, 2) + layout_grid.addWidget(self.label_location, 1, 0) + layout_grid.addWidget(self.text_location, 1, 1) + layout_grid.addWidget(self.button_select_location, 1, 2) + layout_grid.addWidget(self.label_project_type, 2, 0) + layout_grid.addWidget(self.combo_project_type, 2, 1, 1, 2) + layout_grid.addWidget(self.label_python_version, 3, 0) + layout_grid.addWidget(self.combo_python_version, 3, 1, 1, 2) + + layout = QVBoxLayout() + layout.addWidget(self.groupbox) + layout.addSpacing(10) + layout.addLayout(layout_grid) + layout.addStretch() + layout.addSpacing(20) + layout.addWidget(self.bbox) + + self.setLayout(layout) + + # Signals and slots + self.button_select_location.clicked.connect(self.select_location) + self.button_create.clicked.connect(self.create_project) + self.button_cancel.clicked.connect(self.close) + self.radio_from_dir.clicked.connect(self.update_location) + self.radio_new_dir.clicked.connect(self.update_location) + self.text_project_name.textChanged.connect(self.update_location) + + def _get_project_types(self): + """Get all available project types.""" + project_types = get_available_project_types() + projects = [] + + for project in project_types: + projects.append(project.PROJECT_TYPE_NAME) + + return projects + + def select_location(self): + """Select directory.""" + location = getexistingdirectory(self, _("Select directory"), + self.location) + if location: + if is_writable(location): + self.location = location + self.update_location() + + def update_location(self, text=''): + """Update text of location.""" + self.text_project_name.setEnabled(self.radio_new_dir.isChecked()) + name = self.text_project_name.text().strip() + + if name and self.radio_new_dir.isChecked(): + path = osp.join(self.location, name) + self.button_create.setDisabled(os.path.isdir(path)) + elif self.radio_from_dir.isChecked(): + self.button_create.setEnabled(True) + path = self.location + else: + self.button_create.setEnabled(False) + path = self.location + + self.text_location.setText(path) + + def create_project(self): + """Create project.""" + packages = ['python={0}'.format(self.combo_python_version.currentText())] + self.sig_project_creation_requested.emit( + self.text_location.text(), + self.combo_project_type.currentText(), + packages) + self.accept() + + +def test(): + """Local test.""" + from spyder.utils.qthelpers import qapplication + app = qapplication() + dlg = ProjectDialog(None) + dlg.show() + sys.exit(app.exec_()) + + +if __name__ == "__main__": + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/projects/type/__init__.py spyder-3.0.2+dfsg1/spyder/widgets/projects/type/__init__.py --- spyder-2.3.8+dfsg1/spyder/widgets/projects/type/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/projects/type/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © Spyder Project Contributors +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) +# ----------------------------------------------------------------------------- +"""Project types""" + +import os +import os.path as osp + +from spyder.config.base import _ +from spyder.py3compat import to_text_string +from spyder.widgets.projects.config import (ProjectConfig, CODESTYLE, + CODESTYLE_DEFAULTS, + CODESTYLE_VERSION, WORKSPACE, + WORKSPACE_DEFAULTS, + WORKSPACE_VERSION, + ENCODING, ENCODING_DEFAULTS, + ENCODING_VERSION, + VCS, VCS_DEFAULTS, VCS_VERSION) + + +class BaseProject(object): + """Spyder base project. + + This base class must not be used directly, but inherited from. It does not + assume that python is specific to this project. + """ + PROJECT_FOLDER = '.spyderproject' + PROJECT_TYPE_NAME = None + IGNORE_FILE = "" + CONFIG_SETUP = {WORKSPACE: {'filename': '{0}.ini'.format(WORKSPACE), + 'defaults': WORKSPACE_DEFAULTS, + 'version': WORKSPACE_VERSION}, + CODESTYLE: {'filename': '{0}.ini'.format(CODESTYLE), + 'defaults': CODESTYLE_DEFAULTS, + 'version': CODESTYLE_VERSION}, + ENCODING: {'filename': '{0}.ini'.format(ENCODING), + 'defaults': ENCODING_DEFAULTS, + 'version': ENCODING_VERSION}, + VCS: {'filename': '{0}.ini'.format(VCS), + 'defaults': VCS_DEFAULTS, + 'version': VCS_VERSION} + } + + def __init__(self, root_path): + self.name = None + self.root_path = root_path + self.open_project_files = [] + self.open_non_project_files = [] + self.config_files = [] + self.CONF = {} + + # Configuration files + + self.related_projects = [] # storing project path, not project objects +# self.pythonpath = [] + self.opened = True + + self.ioerror_flag = False + self.create_project_config_files() + + # --- Helpers + # ------------------------------------------------------------------------- + def set_recent_files(self, recent_files): + """Set a list of files opened by the project.""" + for recent_file in recent_files[:]: + if not os.path.isfile(recent_file): + recent_files.remove(recent_file) + self.CONF[WORKSPACE].set('main', 'recent_files', + list(set(recent_files))) + + def get_recent_files(self): + """Return a list of files opened by the project.""" + recent_files = self.CONF[WORKSPACE].get('main', 'recent_files', + default=[]) + for recent_file in recent_files[:]: + if not os.path.isfile(recent_file): + recent_files.remove(recent_file) + return list(set(recent_files)) + + def create_project_config_files(self): + """ """ + dic = self.CONFIG_SETUP + for key in dic: + name = key + filename = dic[key]['filename'] + defaults = dic[key]['defaults'] + version = dic[key]['version'] + self.CONF[key] = ProjectConfig(name, self.root_path, filename, + defaults=defaults, load=True, + version=version) + + def get_conf_files(self): + """ """ + return self.CONF + + def add_ignore_lines(self, lines): + """ """ + text = self.IGNORE_FILE + for line in lines: + text += line + self.IGNORE_FILE = text + + def set_root_path(self, root_path): + """Set project root path.""" + if self.name is None: + self.name = osp.basename(root_path) + self.root_path = to_text_string(root_path) + config_path = self.__get_project_config_path() + if osp.exists(config_path): + self.load() + else: + if not osp.isdir(self.root_path): + os.mkdir(self.root_path) + self.save() + + def rename(self, new_name): + """Rename project and rename its root path accordingly.""" + old_name = self.name + self.name = new_name + pypath = self.relative_pythonpath # ?? + self.root_path = self.root_path[:-len(old_name)]+new_name + self.relative_pythonpath = pypath # ?? + self.save() + + def __get_project_config_folder(self): + """Return project configuration folder.""" + return osp.join(self.root_path, self.PROJECT_FOLDER) + + def __get_project_config_path(self): + """Return project configuration path""" + return osp.join(self.root_path, self.CONFIG_NAME) + + def load(self): + """Load project data""" +# fname = self.__get_project_config_path() +# try: +# # Old format (Spyder 2.0-2.1 for Python 2) +# with open(fname, 'U') as fdesc: +# data = pickle.loads(fdesc.read()) +# except (pickle.PickleError, TypeError, UnicodeDecodeError, +# AttributeError): +# try: +# # New format (Spyder >=2.2 for Python 2 and Python 3) +# with open(fname, 'rb') as fdesc: +# data = pickle.loads(fdesc.read()) +# except (IOError, OSError, pickle.PickleError): +# self.ioerror_flag = True +# return + # Compatibilty with old project explorer file format: +# if 'relative_pythonpath' not in data: +# print("Warning: converting old configuration file " +# "for project '%s'" % data['name'], file=STDERR) +# self.pythonpath = data['pythonpath'] +# data['relative_pythonpath'] = self.relative_pythonpath +# for attr in self.CONFIG_ATTR: +# setattr(self, attr, data[attr]) +# self.save() + + def save(self): + """Save project data""" +# data = {} +# for attr in self.PROJECT_ATTR: +# data[attr] = getattr(self, attr) +# try: +# with open(self.__get_project_config_path(), 'wb') as fdesc: +# pickle.dump(data, fdesc, 2) +# except (IOError, OSError): +# self.ioerror_flag = True + +# def delete(self): +# """Delete project""" +# os.remove(self.__get_project_config_path()) +# +# # --- Misc. +# def get_related_projects(self): +# """Return related projects path list""" +# return self.related_projects +# +# def set_related_projects(self, related_projects): +# """Set related projects""" +# self.related_projects = related_projects +# self.save() +# +# def open(self): +# """Open project""" +# self.opened = True +# self.save() +# +# def close(self): +# """Close project""" +# self.opened = False +# self.save() +# +# def is_opened(self): +# """Return True if project is opened""" +# return self.opened +# +# def is_file_in_project(self, fname): +# """Return True if file *fname* is in one of the project subfolders""" +# fixed_root = fixpath(self.root_path) +# return fixpath(fname) == fixed_root or\ +# fixpath(osp.dirname(fname)).startswith(fixed_root) +# +# def is_root_path(self, dirname): +# """Return True if dirname is project's root path""" +# return fixpath(dirname) == fixpath(self.root_path) + + +class EmptyProject(BaseProject): + """Empty Project""" + PROJECT_TYPE_NAME = _('Empty project') diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/projects/type/python.py spyder-3.0.2+dfsg1/spyder/widgets/projects/type/python.py --- spyder-2.3.8+dfsg1/spyder/widgets/projects/type/python.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/projects/type/python.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © Spyder Project Contributors +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) +# ----------------------------------------------------------------------------- +"""Python project type""" + +import os +import os.path as osp + +from spyder.config.base import _ +from spyder.widgets.projects.type import EmptyProject + + +class PythonProject(EmptyProject): + """Python project.""" + + PROJECT_TYPE_NAME = _('Python project') + IGNORE_FILE = """""" + + def _get_relative_pythonpath(self): + """Return PYTHONPATH list as relative paths""" + # Workaround to replace os.path.relpath (new in Python v2.6): + offset = len(self.root_path)+len(os.pathsep) + return [path[offset:] for path in self.pythonpath] + + def _set_relative_pythonpath(self, value): + """Set PYTHONPATH list relative paths""" + self.pythonpath = [osp.abspath(osp.join(self.root_path, path)) + for path in value] + + relative_pythonpath = property(_get_relative_pythonpath, + _set_relative_pythonpath) + + # --- Python Path + def is_in_pythonpath(self, dirname): + """Return True if dirname is in project's PYTHONPATH""" + return fixpath(dirname) in [fixpath(_p) for _p in self.pythonpath] + + def get_pythonpath(self): + """Return a copy of pythonpath attribute""" + return self.pythonpath[:] + + def set_pythonpath(self, pythonpath): + """Set project's PYTHONPATH""" + self.pythonpath = pythonpath + self.save() + + def remove_from_pythonpath(self, path): + """Remove path from project's PYTHONPATH + Return True if path was removed, False if it was not found""" + pathlist = self.get_pythonpath() + if path in pathlist: + pathlist.pop(pathlist.index(path)) + self.set_pythonpath(pathlist) + return True + else: + return False + + def add_to_pythonpath(self, path): + """Add path to project's PYTHONPATH + Return True if path was added, False if it was already there""" + pathlist = self.get_pythonpath() + if path in pathlist: + return False + else: + pathlist.insert(0, path) + self.set_pythonpath(pathlist) + return True + + +class PythonPackageProject(PythonProject): + """ """ + PROJECT_TYPE_NAME = _('Python package') + IGNORE_FILE = """ + """ + STRUCTURE_TEMPATE = { + 'relative_path/test.py': + """ +test + """, + 'other/test.py': + """ +test + """, + } diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/pydocgui.py spyder-3.0.2+dfsg1/spyder/widgets/pydocgui.py --- spyder-2.3.8+dfsg1/spyder/widgets/pydocgui.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/pydocgui.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""pydoc widget""" + +# Standard library imports +import os.path as osp +import sys + +# Third party imports +from qtpy.QtCore import Qt, QThread, QUrl, Signal +from qtpy.QtGui import QCursor +from qtpy.QtWidgets import QApplication + +# Local imports +from spyder.config.base import _ +from spyder.py3compat import PY3, to_text_string +from spyder.utils.misc import select_port +from spyder.widgets.browser import WebBrowser + + +class PydocServer(QThread): + """Pydoc server""" + server_started = Signal() + + def __init__(self, port=7464): + QThread.__init__(self) + self.port = port + self.server = None + self.complete = False + + def run(self): + import pydoc + if PY3: + # Python 3 + self.callback(pydoc._start_server(pydoc._url_handler, self.port)) + else: + # Python 2 + pydoc.serve(self.port, self.callback, self.completer) + + def callback(self, server): + self.server = server + self.server_started.emit() + + def completer(self): + self.complete = True + + def quit_server(self): + if PY3: + # Python 3 + if self.server.serving: + self.server.stop() + else: + # Python 2 + self.server.quit = 1 + + +class PydocBrowser(WebBrowser): + """ + pydoc widget + """ + DEFAULT_PORT = 30128 + + def __init__(self, parent): + WebBrowser.__init__(self, parent) + self.server = None + self.port = None + + def initialize(self): + """Start pydoc server""" + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + QApplication.processEvents() + self.start_server() + # Initializing continues in `initialize_continued` method... + + def initialize_continued(self): + """Load home page""" + self.go_home() + QApplication.restoreOverrideCursor() + + def is_server_running(self): + """Return True if pydoc server is already running""" + return self.server is not None + + def closeEvent(self, event): + self.server.quit_server() +# while not self.server.complete: #XXX Is it really necessary? +# pass + event.accept() + + #------ Public API ----------------------------------------------------- + def start_server(self): + """Start pydoc server""" + if self.server is None: + self.port = select_port(default_port=self.DEFAULT_PORT) + self.set_home_url('http://localhost:%d/' % self.port) + elif self.server.isRunning(): + self.server.server_started.disconnect(self.initialize_continued) + self.server.quit() + self.server = PydocServer(port=self.port) + self.server.server_started.connect(self.initialize_continued) + self.server.start() + + #------ WebBrowser API ----------------------------------------------------- + def get_label(self): + """Return address label text""" + return _("Module or package:") + + def reload(self): + """Reload page""" + self.start_server() + WebBrowser.reload(self) + + def text_to_url(self, text): + """Convert text address into QUrl object""" + if text.startswith('/'): + text = text[1:] + return QUrl(self.home_url.toString()+text+'.html') + + def url_to_text(self, url): + """Convert QUrl object to displayed text in combo box""" + return osp.splitext(to_text_string(url.path()))[0][1:] + + +def test(): + """Run web browser""" + from spyder.utils.qthelpers import qapplication + app = qapplication(test_time=8) + widget = PydocBrowser(None) + widget.show() + widget.initialize() + sys.exit(app.exec_()) + + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/shell.py spyder-3.0.2+dfsg1/spyder/widgets/shell.py --- spyder-2.3.8+dfsg1/spyder/widgets/shell.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/shell.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,1046 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Shell widgets: base, python and terminal""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import keyword +import locale +import os +import os.path as osp +import re +import sys +import time + +# Third party imports +from qtpy.compat import getsavefilename +from qtpy.QtCore import Property, QCoreApplication, Qt, QTimer, Signal, Slot +from qtpy.QtGui import QKeySequence, QTextCharFormat, QTextCursor +from qtpy.QtWidgets import QApplication, QMenu, QMessageBox, QToolTip + +# Local import +from spyder.config.base import _, DEBUG, get_conf_path, STDERR +from spyder.config.gui import config_shortcut, get_shortcut, fixed_shortcut +from spyder.config.main import CONF +from spyder.py3compat import (builtins, is_string, is_text_string, + PY3, to_text_string) +from spyder.utils import encoding +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import (add_actions, create_action, keybinding, + restore_keyevent) +from spyder.widgets.arraybuilder import SHORTCUT_INLINE, SHORTCUT_TABLE +from spyder.widgets.mixins import (GetHelpMixin, SaveHistoryMixin, + TracebackLinksMixin) +from spyder.widgets.sourcecode.base import ConsoleBaseWidget + + +class ShellBaseWidget(ConsoleBaseWidget, SaveHistoryMixin): + """ + Shell base widget + """ + + redirect_stdio = Signal(bool) + sig_keyboard_interrupt = Signal() + execute = Signal(str) + append_to_history = Signal(str, str) + + def __init__(self, parent, history_filename, profile=False): + """ + parent : specifies the parent widget + """ + ConsoleBaseWidget.__init__(self, parent) + SaveHistoryMixin.__init__(self) + + # Prompt position: tuple (line, index) + self.current_prompt_pos = None + self.new_input_line = True + + # History + self.histidx = None + self.hist_wholeline = False + assert is_text_string(history_filename) + self.history_filename = history_filename + self.history = self.load_history() + + # Session + self.historylog_filename = CONF.get('main', 'historylog_filename', + get_conf_path('history.log')) + + # Context menu + self.menu = None + self.setup_context_menu() + + # Simple profiling test + self.profile = profile + + # Buffer to increase performance of write/flush operations + self.__buffer = [] + self.__timestamp = 0.0 + self.__flushtimer = QTimer(self) + self.__flushtimer.setSingleShot(True) + self.__flushtimer.timeout.connect(self.flush) + + # Give focus to widget + self.setFocus() + + # Cursor width + self.setCursorWidth( CONF.get('main', 'cursor/width') ) + + def toggle_wrap_mode(self, enable): + """Enable/disable wrap mode""" + self.set_wrap_mode('character' if enable else None) + + def set_font(self, font): + """Set shell styles font""" + self.setFont(font) + self.set_pythonshell_font(font) + cursor = self.textCursor() + cursor.select(QTextCursor.Document) + charformat = QTextCharFormat() + charformat.setFontFamily(font.family()) + charformat.setFontPointSize(font.pointSize()) + cursor.mergeCharFormat(charformat) + + + #------ Context menu + def setup_context_menu(self): + """Setup shell context menu""" + self.menu = QMenu(self) + self.cut_action = create_action(self, _("Cut"), + shortcut=keybinding('Cut'), + icon=ima.icon('editcut'), + triggered=self.cut) + self.copy_action = create_action(self, _("Copy"), + shortcut=keybinding('Copy'), + icon=ima.icon('editcopy'), + triggered=self.copy) + paste_action = create_action(self, _("Paste"), + shortcut=keybinding('Paste'), + icon=ima.icon('editpaste'), + triggered=self.paste) + save_action = create_action(self, _("Save history log..."), + icon=ima.icon('filesave'), + tip=_("Save current history log (i.e. all " + "inputs and outputs) in a text file"), + triggered=self.save_historylog) + self.delete_action = create_action(self, _("Delete"), + shortcut=keybinding('Delete'), + icon=ima.icon('editdelete'), + triggered=self.delete) + selectall_action = create_action(self, _("Select All"), + shortcut=keybinding('SelectAll'), + icon=ima.icon('selectall'), + triggered=self.selectAll) + add_actions(self.menu, (self.cut_action, self.copy_action, + paste_action, self.delete_action, None, + selectall_action, None, save_action) ) + + def contextMenuEvent(self, event): + """Reimplement Qt method""" + state = self.has_selected_text() + self.copy_action.setEnabled(state) + self.cut_action.setEnabled(state) + self.delete_action.setEnabled(state) + self.menu.popup(event.globalPos()) + event.accept() + + + #------ Input buffer + def get_current_line_from_cursor(self): + return self.get_text('cursor', 'eof') + + def _select_input(self): + """Select current line (without selecting console prompt)""" + line, index = self.get_position('eof') + if self.current_prompt_pos is None: + pline, pindex = line, index + else: + pline, pindex = self.current_prompt_pos + self.setSelection(pline, pindex, line, index) + + @Slot() + def clear_line(self): + """Clear current line (without clearing console prompt)""" + if self.current_prompt_pos is not None: + self.remove_text(self.current_prompt_pos, 'eof') + + @Slot() + def clear_terminal(self): + """ + Clear terminal window + Child classes reimplement this method to write prompt + """ + self.clear() + + # The buffer being edited + def _set_input_buffer(self, text): + """Set input buffer""" + if self.current_prompt_pos is not None: + self.replace_text(self.current_prompt_pos, 'eol', text) + else: + self.insert(text) + self.set_cursor_position('eof') + + def _get_input_buffer(self): + """Return input buffer""" + input_buffer = '' + if self.current_prompt_pos is not None: + input_buffer = self.get_text(self.current_prompt_pos, 'eol') + input_buffer = input_buffer.replace(os.linesep, '\n') + return input_buffer + + input_buffer = Property("QString", _get_input_buffer, _set_input_buffer) + + + #------ Prompt + def new_prompt(self, prompt): + """ + Print a new prompt and save its (line, index) position + """ + if self.get_cursor_line_column()[1] != 0: + self.write('\n') + self.write(prompt, prompt=True) + # now we update our cursor giving end of prompt + self.current_prompt_pos = self.get_position('cursor') + self.ensureCursorVisible() + self.new_input_line = False + + def check_selection(self): + """ + Check if selected text is r/w, + otherwise remove read-only parts of selection + """ + if self.current_prompt_pos is None: + self.set_cursor_position('eof') + else: + self.truncate_selection(self.current_prompt_pos) + + + #------ Copy / Keyboard interrupt + @Slot() + def copy(self): + """Copy text to clipboard... or keyboard interrupt""" + if self.has_selected_text(): + ConsoleBaseWidget.copy(self) + elif not sys.platform == 'darwin': + self.interrupt() + + def interrupt(self): + """Keyboard interrupt""" + self.sig_keyboard_interrupt.emit() + + @Slot() + def cut(self): + """Cut text""" + self.check_selection() + if self.has_selected_text(): + ConsoleBaseWidget.cut(self) + + @Slot() + def delete(self): + """Remove selected text""" + self.check_selection() + if self.has_selected_text(): + ConsoleBaseWidget.remove_selected_text(self) + + @Slot() + def save_historylog(self): + """Save current history log (all text in console)""" + title = _("Save history log") + self.redirect_stdio.emit(False) + filename, _selfilter = getsavefilename(self, title, + self.historylog_filename, "%s (*.log)" % _("History logs")) + self.redirect_stdio.emit(True) + if filename: + filename = osp.normpath(filename) + try: + encoding.write(to_text_string(self.get_text_with_eol()), + filename) + self.historylog_filename = filename + CONF.set('main', 'historylog_filename', filename) + except EnvironmentError as error: + QMessageBox.critical(self, title, + _("Unable to save file '%s'" + "

    Error message:
    %s" + ) % (osp.basename(filename), + to_text_string(error))) + + + #------ Basic keypress event handler + def on_enter(self, command): + """on_enter""" + self.execute_command(command) + + def execute_command(self, command): + self.execute.emit(command) + self.add_to_history(command) + self.new_input_line = True + + def on_new_line(self): + """On new input line""" + self.set_cursor_position('eof') + self.current_prompt_pos = self.get_position('cursor') + self.new_input_line = False + + @Slot() + def paste(self): + """Reimplemented slot to handle multiline paste action""" + if self.new_input_line: + self.on_new_line() + ConsoleBaseWidget.paste(self) + + def keyPressEvent(self, event): + """ + Reimplement Qt Method + Basic keypress event handler + (reimplemented in InternalShell to add more sophisticated features) + """ + if self.preprocess_keyevent(event): + # Event was accepted in self.preprocess_keyevent + return + self.postprocess_keyevent(event) + + def preprocess_keyevent(self, event): + """Pre-process keypress event: + return True if event is accepted, false otherwise""" + # Copy must be done first to be able to copy read-only text parts + # (otherwise, right below, we would remove selection + # if not on current line) + ctrl = event.modifiers() & Qt.ControlModifier + meta = event.modifiers() & Qt.MetaModifier # meta=ctrl in OSX + if event.key() == Qt.Key_C and \ + ((Qt.MetaModifier | Qt.ControlModifier) & event.modifiers()): + if meta and sys.platform == 'darwin': + self.interrupt() + elif ctrl: + self.copy() + event.accept() + return True + + if self.new_input_line and ( len(event.text()) or event.key() in \ + (Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right) ): + self.on_new_line() + + return False + + def postprocess_keyevent(self, event): + """Post-process keypress event: + in InternalShell, this is method is called when shell is ready""" + event, text, key, ctrl, shift = restore_keyevent(event) + + # Is cursor on the last line? and after prompt? + if len(text): + #XXX: Shouldn't it be: `if len(unicode(text).strip(os.linesep))` ? + if self.has_selected_text(): + self.check_selection() + self.restrict_cursor_position(self.current_prompt_pos, 'eof') + + cursor_position = self.get_position('cursor') + + if key in (Qt.Key_Return, Qt.Key_Enter): + if self.is_cursor_on_last_line(): + self._key_enter() + # add and run selection + else: + self.insert_text(self.get_selected_text(), at_end=True) + + elif key == Qt.Key_Insert and not shift and not ctrl: + self.setOverwriteMode(not self.overwriteMode()) + + elif key == Qt.Key_Delete: + if self.has_selected_text(): + self.check_selection() + self.remove_selected_text() + elif self.is_cursor_on_last_line(): + self.stdkey_clear() + + elif key == Qt.Key_Backspace: + self._key_backspace(cursor_position) + + elif key == Qt.Key_Tab: + self._key_tab() + + elif key == Qt.Key_Space and ctrl: + self._key_ctrl_space() + + elif key == Qt.Key_Left: + if self.current_prompt_pos == cursor_position: + # Avoid moving cursor on prompt + return + method = self.extend_selection_to_next if shift \ + else self.move_cursor_to_next + method('word' if ctrl else 'character', direction='left') + + elif key == Qt.Key_Right: + if self.is_cursor_at_end(): + return + method = self.extend_selection_to_next if shift \ + else self.move_cursor_to_next + method('word' if ctrl else 'character', direction='right') + + elif (key == Qt.Key_Home) or ((key == Qt.Key_Up) and ctrl): + self._key_home(shift, ctrl) + + elif (key == Qt.Key_End) or ((key == Qt.Key_Down) and ctrl): + self._key_end(shift, ctrl) + + elif key == Qt.Key_Up: + if not self.is_cursor_on_last_line(): + self.set_cursor_position('eof') + y_cursor = self.get_coordinates(cursor_position)[1] + y_prompt = self.get_coordinates(self.current_prompt_pos)[1] + if y_cursor > y_prompt: + self.stdkey_up(shift) + else: + self.browse_history(backward=True) + + elif key == Qt.Key_Down: + if not self.is_cursor_on_last_line(): + self.set_cursor_position('eof') + y_cursor = self.get_coordinates(cursor_position)[1] + y_end = self.get_coordinates('eol')[1] + if y_cursor < y_end: + self.stdkey_down(shift) + else: + self.browse_history(backward=False) + + elif key in (Qt.Key_PageUp, Qt.Key_PageDown): + #XXX: Find a way to do this programmatically instead of calling + # widget keyhandler (this won't work if the *event* is coming from + # the event queue - i.e. if the busy buffer is ever implemented) + ConsoleBaseWidget.keyPressEvent(self, event) + + elif key == Qt.Key_Escape and shift: + self.clear_line() + + elif key == Qt.Key_Escape: + self._key_escape() + + elif key == Qt.Key_L and ctrl: + self.clear_terminal() + + elif key == Qt.Key_V and ctrl: + self.paste() + + elif key == Qt.Key_X and ctrl: + self.cut() + + elif key == Qt.Key_Z and ctrl: + self.undo() + + elif key == Qt.Key_Y and ctrl: + self.redo() + + elif key == Qt.Key_A and ctrl: + self.selectAll() + + elif key == Qt.Key_Question and not self.has_selected_text(): + self._key_question(text) + + elif key == Qt.Key_ParenLeft and not self.has_selected_text(): + self._key_parenleft(text) + + elif key == Qt.Key_Period and not self.has_selected_text(): + self._key_period(text) + + elif len(text) and not self.isReadOnly(): + self.hist_wholeline = False + self.insert_text(text) + self._key_other(text) + + else: + # Let the parent widget handle the key press event + ConsoleBaseWidget.keyPressEvent(self, event) + + + #------ Key handlers + def _key_enter(self): + command = self.input_buffer + self.insert_text('\n', at_end=True) + self.on_enter(command) + self.flush() + def _key_other(self, text): + raise NotImplementedError + def _key_backspace(self, cursor_position): + raise NotImplementedError + def _key_tab(self): + raise NotImplementedError + def _key_ctrl_space(self): + raise NotImplementedError + def _key_home(self, shift, ctrl): + if self.is_cursor_on_last_line(): + self.stdkey_home(shift, ctrl, self.current_prompt_pos) + def _key_end(self, shift, ctrl): + if self.is_cursor_on_last_line(): + self.stdkey_end(shift, ctrl) + def _key_pageup(self): + raise NotImplementedError + def _key_pagedown(self): + raise NotImplementedError + def _key_escape(self): + raise NotImplementedError + def _key_question(self, text): + raise NotImplementedError + def _key_parenleft(self, text): + raise NotImplementedError + def _key_period(self, text): + raise NotImplementedError + + + #------ History Management + def load_history(self): + """Load history from a .py file in user home directory""" + if osp.isfile(self.history_filename): + rawhistory, _ = encoding.readlines(self.history_filename) + rawhistory = [line.replace('\n', '') for line in rawhistory] + if rawhistory[1] != self.INITHISTORY[1]: + rawhistory[1] = self.INITHISTORY[1] + else: + rawhistory = self.INITHISTORY + history = [line for line in rawhistory \ + if line and not line.startswith('#')] + + # Truncating history to X entries: + while len(history) >= CONF.get('historylog', 'max_entries'): + del history[0] + while rawhistory[0].startswith('#'): + del rawhistory[0] + del rawhistory[0] + # Saving truncated history: + encoding.writelines(rawhistory, self.history_filename) + return history + + def browse_history(self, backward): + """Browse history""" + if self.is_cursor_before('eol') and self.hist_wholeline: + self.hist_wholeline = False + tocursor = self.get_current_line_to_cursor() + text, self.histidx = self.__find_in_history(tocursor, + self.histidx, backward) + if text is not None: + if self.hist_wholeline: + self.clear_line() + self.insert_text(text) + else: + cursor_position = self.get_position('cursor') + # Removing text from cursor to the end of the line + self.remove_text('cursor', 'eol') + # Inserting history text + self.insert_text(text) + self.set_cursor_position(cursor_position) + + def __find_in_history(self, tocursor, start_idx, backward): + """Find text 'tocursor' in history, from index 'start_idx'""" + if start_idx is None: + start_idx = len(self.history) + # Finding text in history + step = -1 if backward else 1 + idx = start_idx + if len(tocursor) == 0 or self.hist_wholeline: + idx += step + if idx >= len(self.history) or len(self.history) == 0: + return "", len(self.history) + elif idx < 0: + idx = 0 + self.hist_wholeline = True + return self.history[idx], idx + else: + for index in range(len(self.history)): + idx = (start_idx+step*(index+1)) % len(self.history) + entry = self.history[idx] + if entry.startswith(tocursor): + return entry[len(tocursor):], idx + else: + return None, start_idx + + + #------ Simulation standards input/output + def write_error(self, text): + """Simulate stderr""" + self.flush() + self.write(text, flush=True, error=True) + if DEBUG: + STDERR.write(text) + + def write(self, text, flush=False, error=False, prompt=False): + """Simulate stdout and stderr""" + if prompt: + self.flush() + if not is_string(text): + # This test is useful to discriminate QStrings from decoded str + text = to_text_string(text) + self.__buffer.append(text) + ts = time.time() + if flush or prompt: + self.flush(error=error, prompt=prompt) + elif ts - self.__timestamp > 0.05: + self.flush(error=error) + self.__timestamp = ts + # Timer to flush strings cached by last write() operation in series + self.__flushtimer.start(50) + + def flush(self, error=False, prompt=False): + """Flush buffer, write text to console""" + # Fix for Issue 2452 + if PY3: + try: + text = "".join(self.__buffer) + except TypeError: + text = b"".join(self.__buffer) + try: + text = text.decode( locale.getdefaultlocale()[1] ) + except: + pass + else: + text = "".join(self.__buffer) + + self.__buffer = [] + self.insert_text(text, at_end=True, error=error, prompt=prompt) + QCoreApplication.processEvents() + self.repaint() + # Clear input buffer: + self.new_input_line = True + + + #------ Text Insertion + def insert_text(self, text, at_end=False, error=False, prompt=False): + """ + Insert text at the current cursor position + or at the end of the command line + """ + if at_end: + # Insert text at the end of the command line + self.append_text_to_shell(text, error, prompt) + else: + # Insert text at current cursor position + ConsoleBaseWidget.insert_text(self, text) + + + #------ Re-implemented Qt Methods + def focusNextPrevChild(self, next): + """ + Reimplemented to stop Tab moving to the next window + """ + if next: + return False + return ConsoleBaseWidget.focusNextPrevChild(self, next) + + + #------ Drag and drop + def dragEnterEvent(self, event): + """Drag and Drop - Enter event""" + event.setAccepted(event.mimeData().hasFormat("text/plain")) + + def dragMoveEvent(self, event): + """Drag and Drop - Move event""" + if (event.mimeData().hasFormat("text/plain")): + event.setDropAction(Qt.MoveAction) + event.accept() + else: + event.ignore() + + def dropEvent(self, event): + """Drag and Drop - Drop event""" + if (event.mimeData().hasFormat("text/plain")): + text = to_text_string(event.mimeData().text()) + if self.new_input_line: + self.on_new_line() + self.insert_text(text, at_end=True) + self.setFocus() + event.setDropAction(Qt.MoveAction) + event.accept() + else: + event.ignore() + + def drop_pathlist(self, pathlist): + """Drop path list""" + raise NotImplementedError + + +# Example how to debug complex interclass call chains: +# +# from spyder.utils.debug import log_methods_calls +# log_methods_calls('log.log', ShellBaseWidget) + +class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget, + GetHelpMixin): + """Python shell widget""" + QT_CLASS = ShellBaseWidget + INITHISTORY = ['# -*- coding: utf-8 -*-', + '# *** Spyder Python Console History Log ***',] + SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime()) + go_to_error = Signal(str) + + def __init__(self, parent, history_filename, profile=False): + ShellBaseWidget.__init__(self, parent, history_filename, profile) + TracebackLinksMixin.__init__(self) + GetHelpMixin.__init__(self) + + # Local shortcuts + self.shortcuts = self.create_shortcuts() + + def create_shortcuts(self): + fixed_shortcut(SHORTCUT_INLINE, self, lambda: self.enter_array_inline()) + fixed_shortcut(SHORTCUT_TABLE, self, lambda: self.enter_array_table()) + inspectsc = config_shortcut(self.inspect_current_object, + context='Console', + name='Inspect current object', + parent=self) + return [inspectsc] + + def get_shortcut_data(self): + """ + Returns shortcut data, a list of tuples (shortcut, text, default) + shortcut (QShortcut or QAction instance) + text (string): action/shortcut description + default (string): default key sequence + """ + return [sc.data for sc in self.shortcuts] + + #------ Context menu + def setup_context_menu(self): + """Reimplements ShellBaseWidget method""" + ShellBaseWidget.setup_context_menu(self) + self.copy_without_prompts_action = create_action(self, + _("Copy without prompts"), + icon=ima.icon('copywop'), + triggered=self.copy_without_prompts) + clear_line_action = create_action(self, _("Clear line"), + QKeySequence(get_shortcut('console', + 'Clear line')), + icon=ima.icon('editdelete'), + tip=_("Clear line"), + triggered=self.clear_line) + clear_action = create_action(self, _("Clear shell"), + QKeySequence(get_shortcut('console', + 'Clear shell')), + icon=ima.icon('editclear'), + tip=_("Clear shell contents " + "('cls' command)"), + triggered=self.clear_terminal) + add_actions(self.menu, (self.copy_without_prompts_action, + clear_line_action, clear_action)) + + def contextMenuEvent(self, event): + """Reimplements ShellBaseWidget method""" + state = self.has_selected_text() + self.copy_without_prompts_action.setEnabled(state) + ShellBaseWidget.contextMenuEvent(self, event) + + @Slot() + def copy_without_prompts(self): + """Copy text to clipboard without prompts""" + text = self.get_selected_text() + lines = text.split(os.linesep) + for index, line in enumerate(lines): + if line.startswith('>>> ') or line.startswith('... '): + lines[index] = line[4:] + text = os.linesep.join(lines) + QApplication.clipboard().setText(text) + + + #------ Key handlers + def postprocess_keyevent(self, event): + """Process keypress event""" + ShellBaseWidget.postprocess_keyevent(self, event) + if QToolTip.isVisible(): + _event, _text, key, _ctrl, _shift = restore_keyevent(event) + self.hide_tooltip_if_necessary(key) + + def _key_other(self, text): + """1 character key""" + if self.is_completion_widget_visible(): + self.completion_text += text + + def _key_backspace(self, cursor_position): + """Action for Backspace key""" + if self.has_selected_text(): + self.check_selection() + self.remove_selected_text() + elif self.current_prompt_pos == cursor_position: + # Avoid deleting prompt + return + elif self.is_cursor_on_last_line(): + self.stdkey_backspace() + if self.is_completion_widget_visible(): + # Removing only last character because if there was a selection + # the completion widget would have been canceled + self.completion_text = self.completion_text[:-1] + + def _key_tab(self): + """Action for TAB key""" + if self.is_cursor_on_last_line(): + empty_line = not self.get_current_line_to_cursor().strip() + if empty_line: + self.stdkey_tab() + else: + self.show_code_completion(automatic=False) + + def _key_ctrl_space(self): + """Action for Ctrl+Space""" + if not self.is_completion_widget_visible(): + self.show_code_completion(automatic=False) + + def _key_pageup(self): + """Action for PageUp key""" + pass + + def _key_pagedown(self): + """Action for PageDown key""" + pass + + def _key_escape(self): + """Action for ESCAPE key""" + if self.is_completion_widget_visible(): + self.hide_completion_widget() + + def _key_question(self, text): + """Action for '?'""" + if self.get_current_line_to_cursor(): + last_obj = self.get_last_obj() + if last_obj and not last_obj.isdigit(): + self.show_object_info(last_obj) + self.insert_text(text) + # In case calltip and completion are shown at the same time: + if self.is_completion_widget_visible(): + self.completion_text += '?' + + def _key_parenleft(self, text): + """Action for '('""" + self.hide_completion_widget() + if self.get_current_line_to_cursor(): + last_obj = self.get_last_obj() + if last_obj and not last_obj.isdigit(): + self.insert_text(text) + self.show_object_info(last_obj, call=True) + return + self.insert_text(text) + + def _key_period(self, text): + """Action for '.'""" + self.insert_text(text) + if self.codecompletion_auto: + # Enable auto-completion only if last token isn't a float + last_obj = self.get_last_obj() + if last_obj and not last_obj.isdigit(): + self.show_code_completion(automatic=True) + + + #------ Paste + def paste(self): + """Reimplemented slot to handle multiline paste action""" + text = to_text_string(QApplication.clipboard().text()) + if len(text.splitlines()) > 1: + # Multiline paste + if self.new_input_line: + self.on_new_line() + self.remove_selected_text() # Remove selection, eventually + end = self.get_current_line_from_cursor() + lines = self.get_current_line_to_cursor() + text + end + self.clear_line() + self.execute_lines(lines) + self.move_cursor(-len(end)) + else: + # Standard paste + ShellBaseWidget.paste(self) + + + #------ Code Completion / Calltips + # Methods implemented in child class: + # (e.g. InternalShell) + def get_dir(self, objtxt): + """Return dir(object)""" + raise NotImplementedError + def get_module_completion(self, objtxt): + """Return module completion list associated to object name""" + pass + def get_globals_keys(self): + """Return shell globals() keys""" + raise NotImplementedError + def get_cdlistdir(self): + """Return shell current directory list dir""" + raise NotImplementedError + def iscallable(self, objtxt): + """Is object callable?""" + raise NotImplementedError + def get_arglist(self, objtxt): + """Get func/method argument list""" + raise NotImplementedError + def get__doc__(self, objtxt): + """Get object __doc__""" + raise NotImplementedError + def get_doc(self, objtxt): + """Get object documentation dictionary""" + raise NotImplementedError + def get_source(self, objtxt): + """Get object source""" + raise NotImplementedError + def is_defined(self, objtxt, force_import=False): + """Return True if object is defined""" + raise NotImplementedError + + def show_code_completion(self, automatic): + """Display a completion list based on the current line""" + # Note: unicode conversion is needed only for ExternalShellBase + text = to_text_string(self.get_current_line_to_cursor()) + last_obj = self.get_last_obj() + + if not text: + return + + if text.startswith('import '): + obj_list = self.get_module_completion(text) + words = text.split(' ') + if ',' in words[-1]: + words = words[-1].split(',') + self.show_completion_list(obj_list, completion_text=words[-1], + automatic=automatic) + return + + elif text.startswith('from '): + obj_list = self.get_module_completion(text) + if obj_list is None: + return + words = text.split(' ') + if '(' in words[-1]: + words = words[:-2] + words[-1].split('(') + if ',' in words[-1]: + words = words[:-2] + words[-1].split(',') + self.show_completion_list(obj_list, completion_text=words[-1], + automatic=automatic) + return + + obj_dir = self.get_dir(last_obj) + if last_obj and obj_dir and text.endswith('.'): + self.show_completion_list(obj_dir, automatic=automatic) + return + + # Builtins and globals + if not text.endswith('.') and last_obj \ + and re.match(r'[a-zA-Z_0-9]*$', last_obj): + b_k_g = dir(builtins)+self.get_globals_keys()+keyword.kwlist + for objname in b_k_g: + if objname.startswith(last_obj) and objname != last_obj: + self.show_completion_list(b_k_g, completion_text=last_obj, + automatic=automatic) + return + else: + return + + # Looking for an incomplete completion + if last_obj is None: + last_obj = text + dot_pos = last_obj.rfind('.') + if dot_pos != -1: + if dot_pos == len(last_obj)-1: + completion_text = "" + else: + completion_text = last_obj[dot_pos+1:] + last_obj = last_obj[:dot_pos] + completions = self.get_dir(last_obj) + if completions is not None: + self.show_completion_list(completions, + completion_text=completion_text, + automatic=automatic) + return + + # Looking for ' or ": filename completion + q_pos = max([text.rfind("'"), text.rfind('"')]) + if q_pos != -1: + completions = self.get_cdlistdir() + if completions: + self.show_completion_list(completions, + completion_text=text[q_pos+1:], + automatic=automatic) + return + + #------ Drag'n Drop + def drop_pathlist(self, pathlist): + """Drop path list""" + if pathlist: + files = ["r'%s'" % path for path in pathlist] + if len(files) == 1: + text = files[0] + else: + text = "[" + ", ".join(files) + "]" + if self.new_input_line: + self.on_new_line() + self.insert_text(text) + self.setFocus() + + +class TerminalWidget(ShellBaseWidget): + """ + Terminal widget + """ + COM = 'rem' if os.name == 'nt' else '#' + INITHISTORY = ['%s *** Spyder Terminal History Log ***' % COM, COM,] + SEPARATOR = '%s%s ---(%s)---' % (os.linesep*2, COM, time.ctime()) + go_to_error = Signal(str) + + def __init__(self, parent, history_filename, profile=False): + ShellBaseWidget.__init__(self, parent, history_filename, profile) + + #------ Key handlers + def _key_other(self, text): + """1 character key""" + pass + + def _key_backspace(self, cursor_position): + """Action for Backspace key""" + if self.has_selected_text(): + self.check_selection() + self.remove_selected_text() + elif self.current_prompt_pos == cursor_position: + # Avoid deleting prompt + return + elif self.is_cursor_on_last_line(): + self.stdkey_backspace() + + def _key_tab(self): + """Action for TAB key""" + if self.is_cursor_on_last_line(): + self.stdkey_tab() + + def _key_ctrl_space(self): + """Action for Ctrl+Space""" + pass + + def _key_escape(self): + """Action for ESCAPE key""" + self.clear_line() + + def _key_question(self, text): + """Action for '?'""" + self.insert_text(text) + + def _key_parenleft(self, text): + """Action for '('""" + self.insert_text(text) + + def _key_period(self, text): + """Action for '.'""" + self.insert_text(text) + + + #------ Drag'n Drop + def drop_pathlist(self, pathlist): + """Drop path list""" + if pathlist: + files = ['"%s"' % path for path in pathlist] + if len(files) == 1: + text = files[0] + else: + text = " ".join(files) + if self.new_input_line: + self.on_new_line() + self.insert_text(text) + self.setFocus() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/base.py spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/base.py --- spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/base.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/base.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,1368 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""QPlainTextEdit base class""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os +import re +import sys + +# Third party imports +from qtpy.compat import to_qvariant +from qtpy.QtCore import QEvent, QEventLoop, QPoint, Qt, Signal, Slot +from qtpy.QtGui import (QClipboard, QColor, QFont, QMouseEvent, QPalette, + QTextCharFormat, QTextFormat, QTextOption, QTextCursor) +from qtpy.QtWidgets import (QAbstractItemView, QApplication, QListWidget, + QListWidgetItem, QMainWindow, QPlainTextEdit, + QTextEdit, QToolTip) + +# Local imports +from spyder.config.gui import get_font +from spyder.config.main import CONF +from spyder.py3compat import PY3, str_lower, to_text_string +from spyder.utils import icon_manager as ima +from spyder.widgets.calltip import CallTipWidget +from spyder.widgets.mixins import BaseEditMixin +from spyder.widgets.sourcecode.terminal import ANSIEscapeCodeHandler + + +def insert_text_to(cursor, text, fmt): + """Helper to print text, taking into account backspaces""" + while True: + index = text.find(chr(8)) # backspace + if index == -1: + break + cursor.insertText(text[:index], fmt) + if cursor.positionInBlock() > 0: + cursor.deletePreviousChar() + text = text[index+1:] + cursor.insertText(text, fmt) + + +class CompletionWidget(QListWidget): + """Completion list widget""" + def __init__(self, parent, ancestor): + QListWidget.__init__(self, ancestor) + self.setWindowFlags(Qt.SubWindow | Qt.FramelessWindowHint) + self.textedit = parent + self.completion_list = None + self.case_sensitive = False + self.enter_select = None + self.hide() + self.itemActivated.connect(self.item_selected) + + def setup_appearance(self, size, font): + self.resize(*size) + self.setFont(font) + + def show_list(self, completion_list, automatic=True): + types = [c[1] for c in completion_list] + completion_list = [c[0] for c in completion_list] + if len(completion_list) == 1 and not automatic: + self.textedit.insert_completion(completion_list[0]) + return + + self.completion_list = completion_list + self.clear() + + icons_map = {'instance': 'attribute', + 'statement': 'attribute', + 'method': 'method', + 'function': 'function', + 'class': 'class', + 'module': 'module'} + + self.type_list = types + if any(types): + for (c, t) in zip(completion_list, types): + icon = icons_map.get(t, 'no_match') + self.addItem(QListWidgetItem(ima.icon(icon), c)) + else: + self.addItems(completion_list) + + self.setCurrentRow(0) + + QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) + self.show() + self.setFocus() + self.raise_() + + # Retrieving current screen height + desktop = QApplication.desktop() + srect = desktop.availableGeometry(desktop.screenNumber(self)) + screen_right = srect.right() + screen_bottom = srect.bottom() + + point = self.textedit.cursorRect().bottomRight() + point.setX(point.x()+self.textedit.get_linenumberarea_width()) + point = self.textedit.mapToGlobal(point) + + # Computing completion widget and its parent right positions + comp_right = point.x()+self.width() + ancestor = self.parent() + if ancestor is None: + anc_right = screen_right + else: + anc_right = min([ancestor.x()+ancestor.width(), screen_right]) + + # Moving completion widget to the left + # if there is not enough space to the right + if comp_right > anc_right: + point.setX(point.x()-self.width()) + + # Computing completion widget and its parent bottom positions + comp_bottom = point.y()+self.height() + ancestor = self.parent() + if ancestor is None: + anc_bottom = screen_bottom + else: + anc_bottom = min([ancestor.y()+ancestor.height(), screen_bottom]) + + # Moving completion widget above if there is not enough space below + x_position = point.x() + if comp_bottom > anc_bottom: + point = self.textedit.cursorRect().topRight() + point = self.textedit.mapToGlobal(point) + point.setX(x_position) + point.setY(point.y()-self.height()) + + if ancestor is not None: + # Useful only if we set parent to 'ancestor' in __init__ + point = ancestor.mapFromGlobal(point) + self.move(point) + + if to_text_string(self.textedit.completion_text): + # When initialized, if completion text is not empty, we need + # to update the displayed list: + self.update_current() + + def hide(self): + QListWidget.hide(self) + self.textedit.setFocus() + + def keyPressEvent(self, event): + text, key = event.text(), event.key() + alt = event.modifiers() & Qt.AltModifier + shift = event.modifiers() & Qt.ShiftModifier + ctrl = event.modifiers() & Qt.ControlModifier + modifier = shift or ctrl or alt + if (key in (Qt.Key_Return, Qt.Key_Enter) and self.enter_select) \ + or key == Qt.Key_Tab: + self.item_selected() + elif key in (Qt.Key_Return, Qt.Key_Enter, + Qt.Key_Left, Qt.Key_Right) or text in ('.', ':'): + self.hide() + self.textedit.keyPressEvent(event) + elif key in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown, + Qt.Key_Home, Qt.Key_End, + Qt.Key_CapsLock) and not modifier: + QListWidget.keyPressEvent(self, event) + elif len(text) or key == Qt.Key_Backspace: + self.textedit.keyPressEvent(event) + self.update_current() + elif modifier: + self.textedit.keyPressEvent(event) + else: + self.hide() + QListWidget.keyPressEvent(self, event) + + def update_current(self): + completion_text = to_text_string(self.textedit.completion_text) + + if completion_text: + for row, completion in enumerate(self.completion_list): + if not self.case_sensitive: + print(completion_text) + completion = completion.lower() + completion_text = completion_text.lower() + if completion.startswith(completion_text): + self.setCurrentRow(row) + self.scrollTo(self.currentIndex(), + QAbstractItemView.PositionAtTop) + break + else: + self.hide() + else: + self.hide() + + + def focusOutEvent(self, event): + event.ignore() + # Don't hide it on Mac when main window loses focus because + # keyboard input is lost + # Fixes Issue 1318 + if sys.platform == "darwin": + if event.reason() != Qt.ActiveWindowFocusReason: + self.hide() + else: + self.hide() + + def item_selected(self, item=None): + if item is None: + item = self.currentItem() + self.textedit.insert_completion( to_text_string(item.text()) ) + self.hide() + + +class TextEditBaseWidget(QPlainTextEdit, BaseEditMixin): + """Text edit base widget""" + BRACE_MATCHING_SCOPE = ('sof', 'eof') + cell_separators = None + focus_in = Signal() + zoom_in = Signal() + zoom_out = Signal() + zoom_reset = Signal() + focus_changed = Signal() + + def __init__(self, parent=None): + QPlainTextEdit.__init__(self, parent) + BaseEditMixin.__init__(self) + self.setAttribute(Qt.WA_DeleteOnClose) + + self.extra_selections_dict = {} + + self.textChanged.connect(self.changed) + self.cursorPositionChanged.connect(self.cursor_position_changed) + + self.indent_chars = " "*4 + + # Code completion / calltips + if parent is not None: + mainwin = parent + while not isinstance(mainwin, QMainWindow): + mainwin = mainwin.parent() + if mainwin is None: + break + if mainwin is not None: + parent = mainwin + + self.completion_widget = CompletionWidget(self, parent) + self.codecompletion_auto = False + self.codecompletion_case = True + self.codecompletion_enter = False + self.completion_text = "" + self.setup_completion() + + self.calltip_widget = CallTipWidget(self, hide_timer_on=True) + self.calltips = True + self.calltip_position = None + + self.has_cell_separators = False + self.highlight_current_cell_enabled = False + + # The color values may be overridden by the syntax highlighter + # Highlight current line color + self.currentline_color = QColor(Qt.red).lighter(190) + self.currentcell_color = QColor(Qt.red).lighter(194) + + # Brace matching + self.bracepos = None + self.matched_p_color = QColor(Qt.green) + self.unmatched_p_color = QColor(Qt.red) + + def setup_completion(self): + size = CONF.get('main', 'completion/size') + font = get_font() + self.completion_widget.setup_appearance(size, font) + + def set_indent_chars(self, indent_chars): + self.indent_chars = indent_chars + + def set_palette(self, background, foreground): + """ + Set text editor palette colors: + background color and caret (text cursor) color + """ + palette = QPalette() + palette.setColor(QPalette.Base, background) + palette.setColor(QPalette.Text, foreground) + self.setPalette(palette) + + # Set the right background color when changing color schemes + # or creating new Editor windows. This seems to be a Qt bug. + # Fixes Issue 2028 + if sys.platform == 'darwin': + if self.objectName(): + style = "QPlainTextEdit#%s {background: %s; color: %s;}" % \ + (self.objectName(), background.name(), foreground.name()) + self.setStyleSheet(style) + + + #------Extra selections + def get_extra_selections(self, key): + return self.extra_selections_dict.get(key, []) + + def set_extra_selections(self, key, extra_selections): + self.extra_selections_dict[key] = extra_selections + + def update_extra_selections(self): + extra_selections = [] + for key, extra in list(self.extra_selections_dict.items()): + if key == 'current_line' or key == 'current_cell': + # Python 3 compatibility (weird): current line has to be + # highlighted first + extra_selections = extra + extra_selections + else: + extra_selections += extra + self.setExtraSelections(extra_selections) + + def clear_extra_selections(self, key): + self.extra_selections_dict[key] = [] + self.update_extra_selections() + + + def changed(self): + """Emit changed signal""" + self.modificationChanged.emit(self.document().isModified()) + + + #------Highlight current line + def highlight_current_line(self): + """Highlight current line""" + selection = QTextEdit.ExtraSelection() + selection.format.setProperty(QTextFormat.FullWidthSelection, + to_qvariant(True)) + selection.format.setBackground(self.currentline_color) + selection.cursor = self.textCursor() + selection.cursor.clearSelection() + self.set_extra_selections('current_line', [selection]) + self.update_extra_selections() + + def unhighlight_current_line(self): + """Unhighlight current line""" + self.clear_extra_selections('current_line') + + #------Highlight current cell + def highlight_current_cell(self): + """Highlight current cell""" + if self.cell_separators is None or \ + not self.highlight_current_cell_enabled: + return + selection = QTextEdit.ExtraSelection() + selection.format.setProperty(QTextFormat.FullWidthSelection, + to_qvariant(True)) + selection.format.setBackground(self.currentcell_color) + selection.cursor, whole_file_selected, whole_screen_selected =\ + self.select_current_cell_in_visible_portion() + if whole_file_selected: + self.clear_extra_selections('current_cell') + elif whole_screen_selected: + if self.has_cell_separators: + self.set_extra_selections('current_cell', [selection]) + self.update_extra_selections() + else: + self.clear_extra_selections('current_cell') + else: + self.set_extra_selections('current_cell', [selection]) + self.update_extra_selections() + + def unhighlight_current_cell(self): + """Unhighlight current cell""" + self.clear_extra_selections('current_cell') + + #------Brace matching + def find_brace_match(self, position, brace, forward): + start_pos, end_pos = self.BRACE_MATCHING_SCOPE + if forward: + bracemap = {'(': ')', '[': ']', '{': '}'} + text = self.get_text(position, end_pos) + i_start_open = 1 + i_start_close = 1 + else: + bracemap = {')': '(', ']': '[', '}': '{'} + text = self.get_text(start_pos, position) + i_start_open = len(text)-1 + i_start_close = len(text)-1 + + while True: + if forward: + i_close = text.find(bracemap[brace], i_start_close) + else: + i_close = text.rfind(bracemap[brace], 0, i_start_close+1) + if i_close > -1: + if forward: + i_start_close = i_close+1 + i_open = text.find(brace, i_start_open, i_close) + else: + i_start_close = i_close-1 + i_open = text.rfind(brace, i_close, i_start_open+1) + if i_open > -1: + if forward: + i_start_open = i_open+1 + else: + i_start_open = i_open-1 + else: + # found matching brace + if forward: + return position+i_close + else: + return position-(len(text)-i_close) + else: + # no matching brace + return + + def __highlight(self, positions, color=None, cancel=False): + if cancel: + self.clear_extra_selections('brace_matching') + return + extra_selections = [] + for position in positions: + if position > self.get_position('eof'): + return + selection = QTextEdit.ExtraSelection() + selection.format.setBackground(color) + selection.cursor = self.textCursor() + selection.cursor.clearSelection() + selection.cursor.setPosition(position) + selection.cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor) + extra_selections.append(selection) + self.set_extra_selections('brace_matching', extra_selections) + self.update_extra_selections() + + def cursor_position_changed(self): + """Brace matching""" + if self.bracepos is not None: + self.__highlight(self.bracepos, cancel=True) + self.bracepos = None + cursor = self.textCursor() + if cursor.position() == 0: + return + cursor.movePosition(QTextCursor.PreviousCharacter, + QTextCursor.KeepAnchor) + text = to_text_string(cursor.selectedText()) + pos1 = cursor.position() + if text in (')', ']', '}'): + pos2 = self.find_brace_match(pos1, text, forward=False) + elif text in ('(', '[', '{'): + pos2 = self.find_brace_match(pos1, text, forward=True) + else: + return + if pos2 is not None: + self.bracepos = (pos1, pos2) + self.__highlight(self.bracepos, color=self.matched_p_color) + else: + self.bracepos = (pos1,) + self.__highlight(self.bracepos, color=self.unmatched_p_color) + + + #-----Widget setup and options + def set_codecompletion_auto(self, state): + """Set code completion state""" + self.codecompletion_auto = state + + def set_codecompletion_case(self, state): + """Case sensitive completion""" + self.codecompletion_case = state + self.completion_widget.case_sensitive = state + + def set_codecompletion_enter(self, state): + """Enable Enter key to select completion""" + self.codecompletion_enter = state + self.completion_widget.enter_select = state + + def set_calltips(self, state): + """Set calltips state""" + self.calltips = state + + def set_wrap_mode(self, mode=None): + """ + Set wrap mode + Valid *mode* values: None, 'word', 'character' + """ + if mode == 'word': + wrap_mode = QTextOption.WrapAtWordBoundaryOrAnywhere + elif mode == 'character': + wrap_mode = QTextOption.WrapAnywhere + else: + wrap_mode = QTextOption.NoWrap + self.setWordWrapMode(wrap_mode) + + + #------Reimplementing Qt methods + @Slot() + def copy(self): + """ + Reimplement Qt method + Copy text to clipboard with correct EOL chars + """ + if self.get_selected_text(): + QApplication.clipboard().setText(self.get_selected_text()) + + def toPlainText(self): + """ + Reimplement Qt method + Fix PyQt4 bug on Windows and Python 3 + """ + # Fix what appears to be a PyQt4 bug when getting file + # contents under Windows and PY3. This bug leads to + # corruptions when saving files with certain combinations + # of unicode chars on them (like the one attached on + # Issue 1546) + if os.name == 'nt' and PY3: + text = self.get_text('sof', 'eof') + return text.replace('\u2028', '\n').replace('\u2029', '\n')\ + .replace('\u0085', '\n') + else: + return super(TextEditBaseWidget, self).toPlainText() + + def keyPressEvent(self, event): + text, key = event.text(), event.key() + ctrl = event.modifiers() & Qt.ControlModifier + meta = event.modifiers() & Qt.MetaModifier + # Use our own copy method for {Ctrl,Cmd}+C to avoid Qt + # copying text in HTML (See Issue 2285) + if (ctrl or meta) and key == Qt.Key_C: + self.copy() + else: + super(TextEditBaseWidget, self).keyPressEvent(event) + + #------Text: get, set, ... + def get_selection_as_executable_code(self): + """Return selected text as a processed text, + to be executable in a Python/IPython interpreter""" + ls = self.get_line_separator() + + _indent = lambda line: len(line)-len(line.lstrip()) + + line_from, line_to = self.get_selection_bounds() + text = self.get_selected_text() + if not text: + return + + lines = text.split(ls) + if len(lines) > 1: + # Multiline selection -> eventually fixing indentation + original_indent = _indent(self.get_text_line(line_from)) + text = (" "*(original_indent-_indent(lines[0])))+text + + # If there is a common indent to all lines, find it. + # Moving from bottom line to top line ensures that blank + # lines inherit the indent of the line *below* it, + # which is the desired behavior. + min_indent = 999 + current_indent = 0 + lines = text.split(ls) + for i in range(len(lines)-1, -1, -1): + line = lines[i] + if line.strip(): + current_indent = _indent(line) + min_indent = min(current_indent, min_indent) + else: + lines[i] = ' ' * current_indent + if min_indent: + lines = [line[min_indent:] for line in lines] + + # Remove any leading whitespace or comment lines + # since they confuse the reserved word detector that follows below + while lines: + first_line = lines[0].lstrip() + if first_line == '' or first_line[0] == '#': + lines.pop(0) + else: + break + + # Add an EOL character after indentation blocks that start with some + # Python reserved words, so that it gets evaluated automatically + # by the console + varname = re.compile('[a-zA-Z0-9_]*') # matches valid variable names + maybe = False + nextexcept = () + for n, line in enumerate(lines): + if not _indent(line): + word = varname.match(line).group() + if maybe and word not in nextexcept: + lines[n-1] += ls + maybe = False + if word: + if word in ('def', 'for', 'while', 'with', 'class'): + maybe = True + nextexcept = () + elif word == 'if': + maybe = True + nextexcept = ('elif', 'else') + elif word == 'try': + maybe = True + nextexcept = ('except', 'finally') + if maybe: + if lines[-1].strip() == '': + lines[-1] += ls + else: + lines.append(ls) + + return ls.join(lines) + + def get_cell_as_executable_code(self): + """Return cell contents as executable code""" + start_pos, end_pos = self.__save_selection() + cursor, whole_file_selected = self.select_current_cell() + if not whole_file_selected: + self.setTextCursor(cursor) + text = self.get_selection_as_executable_code() + self.__restore_selection(start_pos, end_pos) + return text + + def is_cell_separator(self, cursor=None, block=None): + """Return True if cursor (or text block) is on a block separator""" + assert cursor is not None or block is not None + if cursor is not None: + cursor0 = QTextCursor(cursor) + cursor0.select(QTextCursor.BlockUnderCursor) + text = to_text_string(cursor0.selectedText()) + else: + text = to_text_string(block.text()) + if self.cell_separators is None: + return False + else: + return text.lstrip().startswith(self.cell_separators) + + def select_current_cell(self): + """Select cell under cursor + cell = group of lines separated by CELL_SEPARATORS + returns the textCursor and a boolean indicating if the + entire file is selected""" + cursor = self.textCursor() + cursor.movePosition(QTextCursor.StartOfBlock) + cur_pos = prev_pos = cursor.position() + + # Moving to the next line that is not a separator, if we are + # exactly at one of them + while self.is_cell_separator(cursor): + cursor.movePosition(QTextCursor.NextBlock) + prev_pos = cur_pos + cur_pos = cursor.position() + if cur_pos == prev_pos: + return cursor, False + prev_pos = cur_pos + # If not, move backwards to find the previous separator + while not self.is_cell_separator(cursor): + cursor.movePosition(QTextCursor.PreviousBlock) + prev_pos = cur_pos + cur_pos = cursor.position() + if cur_pos == prev_pos: + if self.is_cell_separator(cursor): + return cursor, False + else: + break + cursor.setPosition(prev_pos) + cell_at_file_start = cursor.atStart() + # Once we find it (or reach the beginning of the file) + # move to the next separator (or the end of the file) + # so we can grab the cell contents + while not self.is_cell_separator(cursor): + cursor.movePosition(QTextCursor.NextBlock, + QTextCursor.KeepAnchor) + cur_pos = cursor.position() + if cur_pos == prev_pos: + cursor.movePosition(QTextCursor.EndOfBlock, + QTextCursor.KeepAnchor) + break + prev_pos = cur_pos + cell_at_file_end = cursor.atEnd() + return cursor, cell_at_file_start and cell_at_file_end + + def select_current_cell_in_visible_portion(self): + """Select cell under cursor in the visible portion of the file + cell = group of lines separated by CELL_SEPARATORS + returns + -the textCursor + -a boolean indicating if the entire file is selected + -a boolean indicating if the entire visible portion of the file is selected""" + cursor = self.textCursor() + cursor.movePosition(QTextCursor.StartOfBlock) + cur_pos = prev_pos = cursor.position() + + beg_pos = self.cursorForPosition(QPoint(0, 0)).position() + bottom_right = QPoint(self.viewport().width() - 1, + self.viewport().height() - 1) + end_pos = self.cursorForPosition(bottom_right).position() + + # Moving to the next line that is not a separator, if we are + # exactly at one of them + while self.is_cell_separator(cursor): + cursor.movePosition(QTextCursor.NextBlock) + prev_pos = cur_pos + cur_pos = cursor.position() + if cur_pos == prev_pos: + return cursor, False, False + prev_pos = cur_pos + # If not, move backwards to find the previous separator + while not self.is_cell_separator(cursor)\ + and cursor.position() >= beg_pos: + cursor.movePosition(QTextCursor.PreviousBlock) + prev_pos = cur_pos + cur_pos = cursor.position() + if cur_pos == prev_pos: + if self.is_cell_separator(cursor): + return cursor, False, False + else: + break + cell_at_screen_start = cursor.position() <= beg_pos + cursor.setPosition(prev_pos) + cell_at_file_start = cursor.atStart() + # Selecting cell header + if not cell_at_file_start: + cursor.movePosition(QTextCursor.PreviousBlock) + cursor.movePosition(QTextCursor.NextBlock, + QTextCursor.KeepAnchor) + # Once we find it (or reach the beginning of the file) + # move to the next separator (or the end of the file) + # so we can grab the cell contents + while not self.is_cell_separator(cursor)\ + and cursor.position() <= end_pos: + cursor.movePosition(QTextCursor.NextBlock, + QTextCursor.KeepAnchor) + cur_pos = cursor.position() + if cur_pos == prev_pos: + cursor.movePosition(QTextCursor.EndOfBlock, + QTextCursor.KeepAnchor) + break + prev_pos = cur_pos + cell_at_file_end = cursor.atEnd() + cell_at_screen_end = cursor.position() >= end_pos + return cursor,\ + cell_at_file_start and cell_at_file_end,\ + cell_at_screen_start and cell_at_screen_end + + def go_to_next_cell(self): + """Go to the next cell of lines""" + cursor = self.textCursor() + cursor.movePosition(QTextCursor.NextBlock) + cur_pos = prev_pos = cursor.position() + while not self.is_cell_separator(cursor): + # Moving to the next code cell + cursor.movePosition(QTextCursor.NextBlock) + prev_pos = cur_pos + cur_pos = cursor.position() + if cur_pos == prev_pos: + return + self.setTextCursor(cursor) + + def get_line_count(self): + """Return document total line number""" + return self.blockCount() + + def __save_selection(self): + """Save current cursor selection and return position bounds""" + cursor = self.textCursor() + return cursor.selectionStart(), cursor.selectionEnd() + + def __restore_selection(self, start_pos, end_pos): + """Restore cursor selection from position bounds""" + cursor = self.textCursor() + cursor.setPosition(start_pos) + cursor.setPosition(end_pos, QTextCursor.KeepAnchor) + self.setTextCursor(cursor) + + def __duplicate_line_or_selection(self, after_current_line=True): + """Duplicate current line or selected text""" + cursor = self.textCursor() + cursor.beginEditBlock() + start_pos, end_pos = self.__save_selection() + if to_text_string(cursor.selectedText()): + cursor.setPosition(end_pos) + # Check if end_pos is at the start of a block: if so, starting + # changes from the previous block + cursor.movePosition(QTextCursor.StartOfBlock, + QTextCursor.KeepAnchor) + if not to_text_string(cursor.selectedText()): + cursor.movePosition(QTextCursor.PreviousBlock) + end_pos = cursor.position() + + cursor.setPosition(start_pos) + cursor.movePosition(QTextCursor.StartOfBlock) + while cursor.position() <= end_pos: + cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) + if cursor.atEnd(): + cursor_temp = QTextCursor(cursor) + cursor_temp.clearSelection() + cursor_temp.insertText(self.get_line_separator()) + break + cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) + text = cursor.selectedText() + cursor.clearSelection() + + if not after_current_line: + # Moving cursor before current line/selected text + cursor.setPosition(start_pos) + cursor.movePosition(QTextCursor.StartOfBlock) + start_pos += len(text) + end_pos += len(text) + + cursor.insertText(text) + cursor.endEditBlock() + self.setTextCursor(cursor) + self.__restore_selection(start_pos, end_pos) + + def duplicate_line(self): + """ + Duplicate current line or selected text + Paste the duplicated text *after* the current line/selected text + """ + self.__duplicate_line_or_selection(after_current_line=True) + + def copy_line(self): + """ + Copy current line or selected text + Paste the duplicated text *before* the current line/selected text + """ + self.__duplicate_line_or_selection(after_current_line=False) + + def __move_line_or_selection(self, after_current_line=True): + """Move current line or selected text""" + cursor = self.textCursor() + cursor.beginEditBlock() + start_pos, end_pos = self.__save_selection() + add_linesep = False + if to_text_string(cursor.selectedText()): + # Check if start_pos is at the start of a block + cursor.setPosition(start_pos) + cursor.movePosition(QTextCursor.StartOfBlock) + start_pos = cursor.position() + + cursor.setPosition(end_pos) + # Check if end_pos is at the start of a block: if so, starting + # changes from the previous block + cursor.movePosition(QTextCursor.StartOfBlock, + QTextCursor.KeepAnchor) + if to_text_string(cursor.selectedText()): + cursor.movePosition(QTextCursor.NextBlock) + end_pos = cursor.position() + else: + cursor.movePosition(QTextCursor.StartOfBlock) + start_pos = cursor.position() + cursor.movePosition(QTextCursor.NextBlock) + end_pos = cursor.position() + # check if on last line + if end_pos == start_pos: + cursor.movePosition(QTextCursor.End) + end_pos = cursor.position() + if start_pos == end_pos: + cursor.endEditBlock() + return + add_linesep = True + cursor.setPosition(start_pos) + cursor.setPosition(end_pos, QTextCursor.KeepAnchor) + + sel_text = to_text_string(cursor.selectedText()) + if add_linesep: + sel_text += os.linesep + cursor.removeSelectedText() + + if after_current_line: + text = to_text_string(cursor.block().text()) + if not text: + cursor.insertText(sel_text) + cursor.endEditBlock() + return + start_pos += len(text)+1 + end_pos += len(text)+1 + cursor.movePosition(QTextCursor.NextBlock) + if cursor.position() < start_pos: + cursor.movePosition(QTextCursor.End) + sel_text = os.linesep + sel_text + end_pos -= 1 + else: + cursor.movePosition(QTextCursor.PreviousBlock) + text = to_text_string(cursor.block().text()) + start_pos -= len(text)+1 + end_pos -= len(text)+1 + + cursor.insertText(sel_text) + + cursor.endEditBlock() + self.setTextCursor(cursor) + self.__restore_selection(start_pos, end_pos) + + def move_line_up(self): + """Move up current line or selected text""" + self.__move_line_or_selection(after_current_line=False) + + def move_line_down(self): + """Move down current line or selected text""" + self.__move_line_or_selection(after_current_line=True) + + def extend_selection_to_complete_lines(self): + """Extend current selection to complete lines""" + cursor = self.textCursor() + start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() + cursor.setPosition(start_pos) + cursor.setPosition(end_pos, QTextCursor.KeepAnchor) + if cursor.atBlockStart(): + cursor.movePosition(QTextCursor.PreviousBlock, + QTextCursor.KeepAnchor) + cursor.movePosition(QTextCursor.EndOfBlock, + QTextCursor.KeepAnchor) + self.setTextCursor(cursor) + + def delete_line(self): + """Delete current line""" + cursor = self.textCursor() + if self.has_selected_text(): + self.extend_selection_to_complete_lines() + start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() + cursor.setPosition(start_pos) + else: + start_pos = end_pos = cursor.position() + cursor.beginEditBlock() + cursor.setPosition(start_pos) + cursor.movePosition(QTextCursor.StartOfBlock) + while cursor.position() <= end_pos: + cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) + if cursor.atEnd(): + break + cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) + cursor.removeSelectedText() + cursor.endEditBlock() + self.ensureCursorVisible() + + + #------Code completion / Calltips + def hide_tooltip_if_necessary(self, key): + """Hide calltip when necessary""" + try: + calltip_char = self.get_character(self.calltip_position) + before = self.is_cursor_before(self.calltip_position, + char_offset=1) + other = key in (Qt.Key_ParenRight, Qt.Key_Period, Qt.Key_Tab) + if calltip_char not in ('?', '(') or before or other: + QToolTip.hideText() + except (IndexError, TypeError): + QToolTip.hideText() + + def show_completion_widget(self, textlist, automatic=True): + """Show completion widget""" + self.completion_widget.show_list(textlist, automatic=automatic) + + def hide_completion_widget(self): + """Hide completion widget""" + self.completion_widget.hide() + + def show_completion_list(self, completions, completion_text="", + automatic=True): + """Display the possible completions""" + if not completions: + return + if not isinstance(completions[0], tuple): + completions = [(c, '') for c in completions] + if len(completions) == 1 and completions[0][0] == completion_text: + return + self.completion_text = completion_text + # Sorting completion list (entries starting with underscore are + # put at the end of the list): + underscore = set([(comp, t) for (comp, t) in completions + if comp.startswith('_')]) + + completions = sorted(set(completions) - underscore, + key=lambda x: str_lower(x[0])) + completions += sorted(underscore, key=lambda x: str_lower(x[0])) + self.show_completion_widget(completions, automatic=automatic) + + def select_completion_list(self): + """Completion list is active, Enter was just pressed""" + self.completion_widget.item_selected() + + def insert_completion(self, text): + if text: + cursor = self.textCursor() + cursor.movePosition(QTextCursor.PreviousCharacter, + QTextCursor.KeepAnchor, + len(self.completion_text)) + cursor.removeSelectedText() + self.insert_text(text) + + def is_completion_widget_visible(self): + """Return True is completion list widget is visible""" + return self.completion_widget.isVisible() + + + #------Standard keys + def stdkey_clear(self): + if not self.has_selected_text(): + self.moveCursor(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) + self.remove_selected_text() + + def stdkey_backspace(self): + if not self.has_selected_text(): + self.moveCursor(QTextCursor.PreviousCharacter, + QTextCursor.KeepAnchor) + self.remove_selected_text() + + def __get_move_mode(self, shift): + return QTextCursor.KeepAnchor if shift else QTextCursor.MoveAnchor + + def stdkey_up(self, shift): + self.moveCursor(QTextCursor.Up, self.__get_move_mode(shift)) + + def stdkey_down(self, shift): + self.moveCursor(QTextCursor.Down, self.__get_move_mode(shift)) + + def stdkey_tab(self): + self.insert_text(self.indent_chars) + + def stdkey_home(self, shift, ctrl, prompt_pos=None): + """Smart HOME feature: cursor is first moved at + indentation position, then at the start of the line""" + move_mode = self.__get_move_mode(shift) + if ctrl: + self.moveCursor(QTextCursor.Start, move_mode) + else: + cursor = self.textCursor() + if prompt_pos is None: + start_position = self.get_position('sol') + else: + start_position = self.get_position(prompt_pos) + text = self.get_text(start_position, 'eol') + indent_pos = start_position+len(text)-len(text.lstrip()) + if cursor.position() != indent_pos: + cursor.setPosition(indent_pos, move_mode) + else: + cursor.setPosition(start_position, move_mode) + self.setTextCursor(cursor) + + def stdkey_end(self, shift, ctrl): + move_mode = self.__get_move_mode(shift) + if ctrl: + self.moveCursor(QTextCursor.End, move_mode) + else: + self.moveCursor(QTextCursor.EndOfBlock, move_mode) + + def stdkey_pageup(self): + pass + + def stdkey_pagedown(self): + pass + + def stdkey_escape(self): + pass + + + #----Qt Events + def mousePressEvent(self, event): + """Reimplement Qt method""" + if sys.platform.startswith('linux') and event.button() == Qt.MidButton: + self.calltip_widget.hide() + self.setFocus() + event = QMouseEvent(QEvent.MouseButtonPress, event.pos(), + Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) + QPlainTextEdit.mousePressEvent(self, event) + QPlainTextEdit.mouseReleaseEvent(self, event) + # Send selection text to clipboard to be able to use + # the paste method and avoid the strange Issue 1445 + # NOTE: This issue seems a focusing problem but it + # seems really hard to track + mode_clip = QClipboard.Clipboard + mode_sel = QClipboard.Selection + text_clip = QApplication.clipboard().text(mode=mode_clip) + text_sel = QApplication.clipboard().text(mode=mode_sel) + QApplication.clipboard().setText(text_sel, mode=mode_clip) + self.paste() + QApplication.clipboard().setText(text_clip, mode=mode_clip) + else: + self.calltip_widget.hide() + QPlainTextEdit.mousePressEvent(self, event) + + def focusInEvent(self, event): + """Reimplemented to handle focus""" + self.focus_changed.emit() + self.focus_in.emit() + self.highlight_current_cell() + QPlainTextEdit.focusInEvent(self, event) + + def focusOutEvent(self, event): + """Reimplemented to handle focus""" + self.focus_changed.emit() + QPlainTextEdit.focusOutEvent(self, event) + + def wheelEvent(self, event): + """Reimplemented to emit zoom in/out signals when Ctrl is pressed""" + # This feature is disabled on MacOS, see Issue 1510 + if sys.platform != 'darwin': + if event.modifiers() & Qt.ControlModifier: + if hasattr(event, 'angleDelta'): + if event.angleDelta().y() < 0: + self.zoom_out.emit() + elif event.angleDelta().y() > 0: + self.zoom_in.emit() + elif hasattr(event, 'delta'): + if event.delta() < 0: + self.zoom_out.emit() + elif event.delta() > 0: + self.zoom_in.emit() + return + QPlainTextEdit.wheelEvent(self, event) + self.highlight_current_cell() + + +class QtANSIEscapeCodeHandler(ANSIEscapeCodeHandler): + def __init__(self): + ANSIEscapeCodeHandler.__init__(self) + self.base_format = None + self.current_format = None + + def set_light_background(self, state): + if state: + self.default_foreground_color = 30 + self.default_background_color = 47 + else: + self.default_foreground_color = 37 + self.default_background_color = 40 + + def set_base_format(self, base_format): + self.base_format = base_format + + def get_format(self): + return self.current_format + + def set_style(self): + """ + Set font style with the following attributes: + 'foreground_color', 'background_color', 'italic', + 'bold' and 'underline' + """ + if self.current_format is None: + assert self.base_format is not None + self.current_format = QTextCharFormat(self.base_format) + # Foreground color + if self.foreground_color is None: + qcolor = self.base_format.foreground() + else: + cstr = self.ANSI_COLORS[self.foreground_color-30][self.intensity] + qcolor = QColor(cstr) + self.current_format.setForeground(qcolor) + # Background color + if self.background_color is None: + qcolor = self.base_format.background() + else: + cstr = self.ANSI_COLORS[self.background_color-40][self.intensity] + qcolor = QColor(cstr) + self.current_format.setBackground(qcolor) + + font = self.current_format.font() + # Italic + if self.italic is None: + italic = self.base_format.fontItalic() + else: + italic = self.italic + font.setItalic(italic) + # Bold + if self.bold is None: + bold = self.base_format.font().bold() + else: + bold = self.bold + font.setBold(bold) + # Underline + if self.underline is None: + underline = self.base_format.font().underline() + else: + underline = self.underline + font.setUnderline(underline) + self.current_format.setFont(font) + + +def inverse_color(color): + color.setHsv(color.hue(), color.saturation(), 255-color.value()) + + +class ConsoleFontStyle(object): + def __init__(self, foregroundcolor, backgroundcolor, + bold, italic, underline): + self.foregroundcolor = foregroundcolor + self.backgroundcolor = backgroundcolor + self.bold = bold + self.italic = italic + self.underline = underline + self.format = None + + def apply_style(self, font, light_background, is_default): + self.format = QTextCharFormat() + self.format.setFont(font) + foreground = QColor(self.foregroundcolor) + if not light_background and is_default: + inverse_color(foreground) + self.format.setForeground(foreground) + background = QColor(self.backgroundcolor) + if not light_background: + inverse_color(background) + self.format.setBackground(background) + font = self.format.font() + font.setBold(self.bold) + font.setItalic(self.italic) + font.setUnderline(self.underline) + self.format.setFont(font) + + +class ConsoleBaseWidget(TextEditBaseWidget): + """Console base widget""" + BRACE_MATCHING_SCOPE = ('sol', 'eol') + COLOR_PATTERN = re.compile('\x01?\x1b\[(.*?)m\x02?') + traceback_available = Signal() + userListActivated = Signal(int, str) + completion_widget_activated = Signal(str) + + def __init__(self, parent=None): + TextEditBaseWidget.__init__(self, parent) + + self.light_background = True + + self.setMaximumBlockCount(300) + + # ANSI escape code handler + self.ansi_handler = QtANSIEscapeCodeHandler() + + # Disable undo/redo (nonsense for a console widget...): + self.setUndoRedoEnabled(False) + + self.userListActivated.connect(lambda user_id, text: + self.completion_widget_activated.emit(text)) + + self.default_style = ConsoleFontStyle( + foregroundcolor=0x000000, backgroundcolor=0xFFFFFF, + bold=False, italic=False, underline=False) + self.error_style = ConsoleFontStyle( + foregroundcolor=0xFF0000, backgroundcolor=0xFFFFFF, + bold=False, italic=False, underline=False) + self.traceback_link_style = ConsoleFontStyle( + foregroundcolor=0x0000FF, backgroundcolor=0xFFFFFF, + bold=True, italic=False, underline=True) + self.prompt_style = ConsoleFontStyle( + foregroundcolor=0x00AA00, backgroundcolor=0xFFFFFF, + bold=True, italic=False, underline=False) + self.font_styles = (self.default_style, self.error_style, + self.traceback_link_style, self.prompt_style) + self.set_pythonshell_font() + self.setMouseTracking(True) + + def set_light_background(self, state): + self.light_background = state + if state: + self.set_palette(background=QColor(Qt.white), + foreground=QColor(Qt.darkGray)) + else: + self.set_palette(background=QColor(Qt.black), + foreground=QColor(Qt.lightGray)) + self.ansi_handler.set_light_background(state) + self.set_pythonshell_font() + + def set_selection(self, start, end): + cursor = self.textCursor() + cursor.setPosition(start) + cursor.setPosition(end, QTextCursor.KeepAnchor) + self.setTextCursor(cursor) + + def truncate_selection(self, position_from): + """Unselect read-only parts in shell, like prompt""" + position_from = self.get_position(position_from) + cursor = self.textCursor() + start, end = cursor.selectionStart(), cursor.selectionEnd() + if start < end: + start = max([position_from, start]) + else: + end = max([position_from, end]) + self.set_selection(start, end) + + def restrict_cursor_position(self, position_from, position_to): + """In shell, avoid editing text except between prompt and EOF""" + position_from = self.get_position(position_from) + position_to = self.get_position(position_to) + cursor = self.textCursor() + cursor_position = cursor.position() + if cursor_position < position_from or cursor_position > position_to: + self.set_cursor_position(position_to) + + #------Python shell + def insert_text(self, text): + """Reimplement TextEditBaseWidget method""" + # Eventually this maybe should wrap to insert_text_to if + # backspace-handling is required + self.textCursor().insertText(text, self.default_style.format) + + def paste(self): + """Reimplement Qt method""" + if self.has_selected_text(): + self.remove_selected_text() + self.insert_text(QApplication.clipboard().text()) + + def append_text_to_shell(self, text, error, prompt): + """ + Append text to Python shell + In a way, this method overrides the method 'insert_text' when text is + inserted at the end of the text widget for a Python shell + + Handles error messages and show blue underlined links + Handles ANSI color sequences + Handles ANSI FF sequence + """ + cursor = self.textCursor() + cursor.movePosition(QTextCursor.End) + if '\r' in text: # replace \r\n with \n + text = text.replace('\r\n', '\n') + text = text.replace('\r', '\n') + while True: + index = text.find(chr(12)) + if index == -1: + break + text = text[index+1:] + self.clear() + if error: + is_traceback = False + for text in text.splitlines(True): + if text.startswith(' File') \ + and not text.startswith(' File "<'): + is_traceback = True + # Show error links in blue underlined text + cursor.insertText(' ', self.default_style.format) + cursor.insertText(text[2:], + self.traceback_link_style.format) + else: + # Show error/warning messages in red + cursor.insertText(text, self.error_style.format) + if is_traceback: + self.traceback_available.emit() + elif prompt: + # Show prompt in green + insert_text_to(cursor, text, self.prompt_style.format) + else: + # Show other outputs in black + last_end = 0 + for match in self.COLOR_PATTERN.finditer(text): + insert_text_to(cursor, text[last_end:match.start()], + self.default_style.format) + last_end = match.end() + try: + for code in [int(_c) for _c in match.group(1).split(';')]: + self.ansi_handler.set_code(code) + except ValueError: + pass + self.default_style.format = self.ansi_handler.get_format() + insert_text_to(cursor, text[last_end:], self.default_style.format) +# # Slower alternative: +# segments = self.COLOR_PATTERN.split(text) +# cursor.insertText(segments.pop(0), self.default_style.format) +# if segments: +# for ansi_tags, text in zip(segments[::2], segments[1::2]): +# for ansi_tag in ansi_tags.split(';'): +# self.ansi_handler.set_code(int(ansi_tag)) +# self.default_style.format = self.ansi_handler.get_format() +# cursor.insertText(text, self.default_style.format) + self.set_cursor_position('eof') + self.setCurrentCharFormat(self.default_style.format) + + def set_pythonshell_font(self, font=None): + """Python Shell only""" + if font is None: + font = QFont() + for style in self.font_styles: + style.apply_style(font=font, + light_background=self.light_background, + is_default=style is self.default_style) + self.ansi_handler.set_base_format(self.default_style.format) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/codeeditor.py spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/codeeditor.py --- spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/codeeditor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/codeeditor.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,3030 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Editor widget based on QtGui.QPlainTextEdit +""" + +# TODO: Try to separate this module from spyder to create a self +# consistent editor module (Qt source code and shell widgets library) + +# %% This line is for cell execution testing +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +from __future__ import division +from unicodedata import category +import os.path as osp +import re +import sre_constants +import sys +import time + +# Third party imports +from qtpy import is_pyqt46 +from qtpy.compat import to_qvariant +from qtpy.QtCore import QRect, QRegExp, QSize, Qt, QTimer, Signal, Slot +from qtpy.QtGui import (QBrush, QColor, QCursor, QFont, QIntValidator, + QKeySequence, QPaintEvent, QPainter, + QTextBlockUserData, QTextCharFormat, QTextCursor, + QTextDocument, QTextFormat, QTextOption) +from qtpy.QtPrintSupport import QPrinter +from qtpy.QtWidgets import (QApplication, QDialog, QDialogButtonBox, + QGridLayout, QHBoxLayout, QInputDialog, QLabel, + QLineEdit, QMenu, QMessageBox, QSplitter, + QTextEdit, QToolTip, QVBoxLayout, QWidget) + +# %% This line is for cell execution testing + +# Local imports +from spyder.config.base import get_conf_path, _, DEBUG +from spyder.config.gui import (config_shortcut, fixed_shortcut, get_shortcut, + RUN_CELL_SHORTCUT, + RUN_CELL_AND_ADVANCE_SHORTCUT) +from spyder.config.main import CONF +from spyder.py3compat import to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils import syntaxhighlighters as sh +from spyder.utils import encoding, sourcecode +from spyder.utils.dochelpers import getobj +from spyder.utils.qthelpers import add_actions, create_action, mimedata2url +from spyder.utils.sourcecode import ALL_LANGUAGES, CELL_LANGUAGES +from spyder.widgets.arraybuilder import SHORTCUT_INLINE, SHORTCUT_TABLE +from spyder.widgets.editortools import PythonCFM +from spyder.widgets.sourcecode.base import TextEditBaseWidget +from spyder.widgets.sourcecode.kill_ring import QtKillRing + +try: + import nbformat as nbformat + from nbconvert import PythonExporter as nbexporter +except: + nbformat = None # analysis:ignore + +# %% This line is for cell execution testing +# For debugging purpose: +LOG_FILENAME = get_conf_path('codeeditor.log') +DEBUG_EDITOR = DEBUG >= 3 + + +def is_letter_or_number(char): + """ Returns whether the specified unicode character is a letter or a number. + """ + cat = category(char) + return cat.startswith('L') or cat.startswith('N') + +# ============================================================================= +# Go to line dialog box +# ============================================================================= +class GoToLineDialog(QDialog): + def __init__(self, editor): + QDialog.__init__(self, editor) + + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + + self.lineno = None + self.editor = editor + + self.setWindowTitle(_("Editor")) + self.setModal(True) + + label = QLabel(_("Go to line:")) + self.lineedit = QLineEdit() + validator = QIntValidator(self.lineedit) + validator.setRange(1, editor.get_line_count()) + self.lineedit.setValidator(validator) + self.lineedit.textChanged.connect(self.text_has_changed) + cl_label = QLabel(_("Current line:")) + cl_label_v = QLabel("%d" % editor.get_cursor_line_number()) + last_label = QLabel(_("Line count:")) + last_label_v = QLabel("%d" % editor.get_line_count()) + + glayout = QGridLayout() + glayout.addWidget(label, 0, 0, Qt.AlignVCenter|Qt.AlignRight) + glayout.addWidget(self.lineedit, 0, 1, Qt.AlignVCenter) + glayout.addWidget(cl_label, 1, 0, Qt.AlignVCenter|Qt.AlignRight) + glayout.addWidget(cl_label_v, 1, 1, Qt.AlignVCenter) + glayout.addWidget(last_label, 2, 0, Qt.AlignVCenter|Qt.AlignRight) + glayout.addWidget(last_label_v, 2, 1, Qt.AlignVCenter) + + bbox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, + Qt.Vertical, self) + bbox.accepted.connect(self.accept) + bbox.rejected.connect(self.reject) + btnlayout = QVBoxLayout() + btnlayout.addWidget(bbox) + btnlayout.addStretch(1) + + ok_button = bbox.button(QDialogButtonBox.Ok) + ok_button.setEnabled(False) + self.lineedit.textChanged.connect( + lambda text: ok_button.setEnabled(len(text) > 0)) + + layout = QHBoxLayout() + layout.addLayout(glayout) + layout.addLayout(btnlayout) + self.setLayout(layout) + + self.lineedit.setFocus() + + def text_has_changed(self, text): + """Line edit's text has changed""" + text = to_text_string(text) + if text: + self.lineno = int(text) + else: + self.lineno = None + + def get_line_number(self): + """Return line number""" + # It is import to avoid accessing Qt C++ object as it has probably + # already been destroyed, due to the Qt.WA_DeleteOnClose attribute + return self.lineno + + +#=============================================================================== +# Viewport widgets +#=============================================================================== +class LineNumberArea(QWidget): + """Line number area (on the left side of the text editor widget)""" + def __init__(self, editor): + QWidget.__init__(self, editor) + self.code_editor = editor + self.setMouseTracking(True) + + def sizeHint(self): + """Override Qt method""" + return QSize(self.code_editor.compute_linenumberarea_width(), 0) + + def paintEvent(self, event): + """Override Qt method""" + self.code_editor.linenumberarea_paint_event(event) + + def mouseMoveEvent(self, event): + """Override Qt method""" + self.code_editor.linenumberarea_mousemove_event(event) + + def mouseDoubleClickEvent(self, event): + """Override Qt method""" + self.code_editor.linenumberarea_mousedoubleclick_event(event) + + def mousePressEvent(self, event): + """Override Qt method""" + self.code_editor.linenumberarea_mousepress_event(event) + + def mouseReleaseEvent(self, event): + """Override Qt method""" + self.code_editor.linenumberarea_mouserelease_event(event) + + def wheelEvent(self, event): + """Override Qt method""" + self.code_editor.wheelEvent(event) + + +class ScrollFlagArea(QWidget): + """Source code editor's scroll flag area""" + WIDTH = 12 + FLAGS_DX = 4 + FLAGS_DY = 2 + + def __init__(self, editor): + QWidget.__init__(self, editor) + self.setAttribute(Qt.WA_OpaquePaintEvent) + self.code_editor = editor + editor.verticalScrollBar().valueChanged.connect( + lambda value: self.repaint()) + + def sizeHint(self): + """Override Qt method""" + return QSize(self.WIDTH, 0) + + def paintEvent(self, event): + """Override Qt method""" + self.code_editor.scrollflagarea_paint_event(event) + + def mousePressEvent(self, event): + """Override Qt method""" + vsb = self.code_editor.verticalScrollBar() + value = self.position_to_value(event.pos().y()-1) + vsb.setValue(value-.5*vsb.pageStep()) + + def get_scale_factor(self, slider=False): + """Return scrollbar's scale factor: + ratio between pixel span height and value span height""" + delta = 0 if slider else 2 + vsb = self.code_editor.verticalScrollBar() + position_height = vsb.height()-delta-1 + value_height = vsb.maximum()-vsb.minimum()+vsb.pageStep() + return float(position_height)/value_height + + def value_to_position(self, y, slider=False): + """Convert value to position""" + offset = 0 if slider else 1 + vsb = self.code_editor.verticalScrollBar() + return (y-vsb.minimum())*self.get_scale_factor(slider)+offset + + def position_to_value(self, y, slider=False): + """Convert position to value""" + offset = 0 if slider else 1 + vsb = self.code_editor.verticalScrollBar() + return vsb.minimum()+max([0, (y-offset)/self.get_scale_factor(slider)]) + + def make_flag_qrect(self, position): + """Make flag QRect""" + return QRect(self.FLAGS_DX/2, position-self.FLAGS_DY/2, + self.WIDTH-self.FLAGS_DX, self.FLAGS_DY) + + def make_slider_range(self, value): + """Make slider range QRect""" + vsb = self.code_editor.verticalScrollBar() + pos1 = self.value_to_position(value, slider=True) + pos2 = self.value_to_position(value + vsb.pageStep(), slider=True) + return QRect(1, pos1, self.WIDTH-2, pos2-pos1+1) + + def wheelEvent(self, event): + """Override Qt method""" + self.code_editor.wheelEvent(event) + + +class EdgeLine(QWidget): + """Source code editor's edge line (default: 79 columns, PEP8)""" + def __init__(self, editor): + QWidget.__init__(self, editor) + self.code_editor = editor + self.column = 79 + self.setAttribute(Qt.WA_TransparentForMouseEvents) + + def paintEvent(self, event): + """Override Qt method""" + painter = QPainter(self) + color = QColor(Qt.darkGray) + color.setAlphaF(.5) + painter.fillRect(event.rect(), color) + + +#=============================================================================== +# CodeEditor widget +#=============================================================================== +class BlockUserData(QTextBlockUserData): + def __init__(self, editor): + QTextBlockUserData.__init__(self) + self.editor = editor + self.breakpoint = False + self.breakpoint_condition = None + self.code_analysis = [] + self.todo = '' + self.editor.blockuserdata_list.append(self) + + def is_empty(self): + return not self.breakpoint and not self.code_analysis and not self.todo + + def __del__(self): + bud_list = self.editor.blockuserdata_list + bud_list.pop(bud_list.index(self)) + + +def set_scrollflagarea_painter(painter, light_color): + """Set scroll flag area painter pen and brush colors""" + painter.setPen(QColor(light_color).darker(120)) + painter.setBrush(QBrush(QColor(light_color))) + + +def get_file_language(filename, text=None): + """Get file language from filename""" + ext = osp.splitext(filename)[1] + if ext.startswith('.'): + ext = ext[1:] # file extension with leading dot + language = ext + if not ext: + if text is None: + text, _enc = encoding.read(filename) + for line in text.splitlines(): + if not line.strip(): + continue + if line.startswith('#!'): + shebang = line[2:] + if 'python' in shebang: + language = 'python' + else: + break + return language + + +class CodeEditor(TextEditBaseWidget): + """Source Code Editor Widget based exclusively on Qt""" + + LANGUAGES = {'Python': (sh.PythonSH, '#', PythonCFM), + 'Cython': (sh.CythonSH, '#', PythonCFM), + 'Fortran77': (sh.Fortran77SH, 'c', None), + 'Fortran': (sh.FortranSH, '!', None), + 'Idl': (sh.IdlSH, ';', None), + 'Diff': (sh.DiffSH, '', None), + 'GetText': (sh.GetTextSH, '#', None), + 'Nsis': (sh.NsisSH, '#', None), + 'Html': (sh.HtmlSH, '', None), + 'Yaml': (sh.YamlSH, '#', None), + 'Cpp': (sh.CppSH, '//', None), + 'OpenCL': (sh.OpenCLSH, '//', None), + 'Enaml': (sh.EnamlSH, '#', PythonCFM), + } + + TAB_ALWAYS_INDENTS = ('py', 'pyw', 'python', 'c', 'cpp', 'cl', 'h') + + # Custom signal to be emitted upon completion of the editor's paintEvent + painted = Signal(QPaintEvent) + + # To have these attrs when early viewportEvent's are triggered + edge_line = None + linenumberarea = None + + breakpoints_changed = Signal() + get_completions = Signal(bool) + go_to_definition = Signal(int) + sig_show_object_info = Signal(int) + run_selection = Signal() + run_cell_and_advance = Signal() + run_cell = Signal() + go_to_definition_regex = Signal(int) + sig_cursor_position_changed = Signal(int, int) + focus_changed = Signal() + sig_new_file = Signal(str) + + def __init__(self, parent=None): + TextEditBaseWidget.__init__(self, parent) + + self.setFocusPolicy(Qt.StrongFocus) + + # We use these object names to set the right background + # color when changing color schemes or creating new + # Editor windows. This seems to be a Qt bug. + # Fixes Issue 2028 + if sys.platform == 'darwin': + plugin_name = repr(parent) + if 'editor' in plugin_name.lower(): + self.setObjectName('editor') + elif 'help' in plugin_name.lower(): + self.setObjectName('help') + elif 'historylog' in plugin_name.lower(): + self.setObjectName('historylog') + elif 'configdialog' in plugin_name.lower(): + self.setObjectName('configdialog') + + # Caret (text cursor) + self.setCursorWidth( CONF.get('main', 'cursor/width') ) + + # 79-col edge line + self.edge_line_enabled = True + self.edge_line = EdgeLine(self) + + # Blanks enabled + self.blanks_enabled = False + + # Markers + self.markers_margin = True + self.markers_margin_width = 15 + self.error_pixmap = ima.icon('error').pixmap(QSize(14, 14)) + self.warning_pixmap = ima.icon('warning').pixmap(QSize(14, 14)) + self.todo_pixmap = ima.icon('todo').pixmap(QSize(14, 14)) + self.bp_pixmap = ima.icon('breakpoint_big').pixmap(QSize(14, 14)) + self.bpc_pixmap = ima.icon('breakpoint_cond_big').pixmap(QSize(14, 14)) + + # Line number area management + self.linenumbers_margin = True + self.linenumberarea_enabled = None + self.linenumberarea = LineNumberArea(self) + self.blockCountChanged.connect(self.update_linenumberarea_width) + self.updateRequest.connect(self.update_linenumberarea) + self.linenumberarea_pressed = -1 + self.linenumberarea_released = -1 + + # Colors to be defined in _apply_highlighter_color_scheme() + # Currentcell color and current line color are defined in base.py + self.occurrence_color = None + self.ctrl_click_color = None + self.sideareas_color = None + self.matched_p_color = None + self.unmatched_p_color = None + self.normal_color = None + self.comment_color = None + + self.linenumbers_color = QColor(Qt.darkGray) + + # --- Syntax highlight entrypoint --- + # + # - if set, self.highlighter is responsible for + # - coloring raw text data inside editor on load + # - coloring text data when editor is cloned + # - updating document highlight on line edits + # - providing color palette (scheme) for the editor + # - providing data for Outliner + # - self.highlighter is not responsible for + # - background highlight for current line + # - background highlight for search / current line occurrences + + self.highlighter_class = sh.TextSH + self.highlighter = None + ccs = 'Spyder' + if ccs not in sh.COLOR_SCHEME_NAMES: + ccs = sh.COLOR_SCHEME_NAMES[0] + self.color_scheme = ccs + + self.highlight_current_line_enabled = False + + # Scrollbar flag area + self.scrollflagarea_enabled = None + self.scrollflagarea = ScrollFlagArea(self) + self.scrollflagarea.hide() + self.warning_color = "#FFAD07" + self.error_color = "#EA2B0E" + self.todo_color = "#B4D4F3" + self.breakpoint_color = "#30E62E" + + self.update_linenumberarea_width() + + self.document_id = id(self) + + # Indicate occurrences of the selected word + self.cursorPositionChanged.connect(self.__cursor_position_changed) + self.__find_first_pos = None + self.__find_flags = None + + self.supported_language = False + self.supported_cell_language = False + self.classfunc_match = None + self.comment_string = None + self._kill_ring = QtKillRing(self) + + # Block user data + self.blockuserdata_list = [] + + # Update breakpoints if the number of lines in the file changes + self.blockCountChanged.connect(self.update_breakpoints) + + # Mark occurrences timer + self.occurrence_highlighting = None + self.occurrence_timer = QTimer(self) + self.occurrence_timer.setSingleShot(True) + self.occurrence_timer.setInterval(1500) + self.occurrence_timer.timeout.connect(self.__mark_occurrences) + self.occurrences = [] + self.occurrence_color = QColor(Qt.yellow).lighter(160) + + # Mark found results + self.textChanged.connect(self.__text_has_changed) + self.found_results = [] + self.found_results_color = QColor(Qt.magenta).lighter(180) + + # Context menu + self.gotodef_action = None + self.setup_context_menu() + + # Tab key behavior + self.tab_indents = None + self.tab_mode = True # see CodeEditor.set_tab_mode + + # Intelligent backspace mode + self.intelligent_backspace = True + + self.go_to_definition_enabled = False + self.close_parentheses_enabled = True + self.close_quotes_enabled = False + self.add_colons_enabled = True + self.auto_unindent_enabled = True + + # Mouse tracking + self.setMouseTracking(True) + self.__cursor_changed = False + self.ctrl_click_color = QColor(Qt.blue) + + # Breakpoints + self.breakpoints = self.get_breakpoints() + + # Keyboard shortcuts + self.shortcuts = self.create_shortcuts() + + # Code editor + self.__visible_blocks = [] # Visible blocks, update with repaint + self.painted.connect(self._draw_editor_cell_divider) + + self.verticalScrollBar().valueChanged.connect( + lambda value: self.rehighlight_cells()) + + def create_shortcuts(self): + codecomp = config_shortcut(self.do_completion, context='Editor', + name='Code Completion', parent=self) + duplicate_line = config_shortcut(self.duplicate_line, context='Editor', + name='Duplicate line', parent=self) + copyline = config_shortcut(self.copy_line, context='Editor', + name='Copy line', parent=self) + deleteline = config_shortcut(self.delete_line, context='Editor', + name='Delete line', parent=self) + movelineup = config_shortcut(self.move_line_up, context='Editor', + name='Move line up', parent=self) + movelinedown = config_shortcut(self.move_line_down, context='Editor', + name='Move line down', parent=self) + gotodef = config_shortcut(self.do_go_to_definition, context='Editor', + name='Go to definition', parent=self) + toggle_comment = config_shortcut(self.toggle_comment, context='Editor', + name='Toggle comment', parent=self) + blockcomment = config_shortcut(self.blockcomment, context='Editor', + name='Blockcomment', parent=self) + unblockcomment = config_shortcut(self.unblockcomment, context='Editor', + name='Unblockcomment', parent=self) + transform_uppercase = config_shortcut(self.transform_to_uppercase, + context='Editor', + name='Transform to uppercase', + parent=self) + transform_lowercase = config_shortcut(self.transform_to_lowercase, + context='Editor', + name='Transform to lowercase', + parent=self) + + def cb_maker(attr): + """Make a callback for cursor move event type, (e.g. "Start") + """ + def cursor_move_event(): + cursor = self.textCursor() + move_type = getattr(QTextCursor, attr) + cursor.movePosition(move_type) + self.setTextCursor(cursor) + return cursor_move_event + + line_start = config_shortcut(cb_maker('StartOfLine'), context='Editor', + name='Start of line', parent=self) + line_end = config_shortcut(cb_maker('EndOfLine'), context='Editor', + name='End of line', parent=self) + + prev_line = config_shortcut(cb_maker('Up'), context='Editor', + name='Previous line', parent=self) + next_line = config_shortcut(cb_maker('Down'), context='Editor', + name='Next line', parent=self) + + prev_char = config_shortcut(cb_maker('Left'), context='Editor', + name='Previous char', parent=self) + next_char = config_shortcut(cb_maker('Right'), context='Editor', + name='Next char', parent=self) + + prev_word = config_shortcut(cb_maker('StartOfWord'), context='Editor', + name='Previous word', parent=self) + next_word = config_shortcut(cb_maker('EndOfWord'), context='Editor', + name='Next word', parent=self) + + kill_line_end = config_shortcut(self.kill_line_end, context='Editor', + name='Kill to line end', parent=self) + kill_line_start = config_shortcut(self.kill_line_start, + context='Editor', + name='Kill to line start', + parent=self) + yank = config_shortcut(self._kill_ring.yank, context='Editor', + name='Yank', parent=self) + kill_ring_rotate = config_shortcut(self._kill_ring.rotate, + context='Editor', + name='Rotate kill ring', + parent=self) + + kill_prev_word = config_shortcut(self.kill_prev_word, context='Editor', + name='Kill previous word', + parent=self) + kill_next_word = config_shortcut(self.kill_next_word, context='Editor', + name='Kill next word', parent=self) + + start_doc = config_shortcut(cb_maker('Start'), context='Editor', + name='Start of Document', parent=self) + + end_doc = config_shortcut(cb_maker('End'), context='Editor', + name='End of document', parent=self) + + undo = config_shortcut(self.undo, context='Editor', + name='undo', parent=self) + redo = config_shortcut(self.redo, context='Editor', + name='redo', parent=self) + cut = config_shortcut(self.cut, context='Editor', + name='cut', parent=self) + copy = config_shortcut(self.copy, context='Editor', + name='copy', parent=self) + paste = config_shortcut(self.paste, context='Editor', + name='paste', parent=self) + delete = config_shortcut(self.delete, context='Editor', + name='delete', parent=self) + select_all = config_shortcut(self.selectAll, context='Editor', + name='Select All', parent=self) + + # Fixed shortcuts + fixed_shortcut(SHORTCUT_INLINE, self, lambda: self.enter_array_inline()) + fixed_shortcut(SHORTCUT_TABLE, self, lambda: self.enter_array_table()) + + return [codecomp, duplicate_line, copyline, deleteline, movelineup, + movelinedown, gotodef, toggle_comment, blockcomment, + unblockcomment, transform_uppercase, transform_lowercase, + line_start, line_end, prev_line, next_line, + prev_char, next_char, prev_word, next_word, kill_line_end, + kill_line_start, yank, kill_ring_rotate, kill_prev_word, + kill_next_word, start_doc, end_doc, undo, redo, cut, copy, + paste, delete, select_all] + + def get_shortcut_data(self): + """ + Returns shortcut data, a list of tuples (shortcut, text, default) + shortcut (QShortcut or QAction instance) + text (string): action/shortcut description + default (string): default key sequence + """ + return [sc.data for sc in self.shortcuts] + + def closeEvent(self, event): + TextEditBaseWidget.closeEvent(self, event) + if is_pyqt46: + self.destroyed.emit() + + def get_document_id(self): + return self.document_id + + def set_as_clone(self, editor): + """Set as clone editor""" + self.setDocument(editor.document()) + self.document_id = editor.get_document_id() + self.highlighter = editor.highlighter + self._apply_highlighter_color_scheme() + + #-----Widget setup and options + def toggle_wrap_mode(self, enable): + """Enable/disable wrap mode""" + self.set_wrap_mode('word' if enable else None) + + def setup_editor(self, linenumbers=True, language=None, markers=False, + font=None, color_scheme=None, wrap=False, tab_mode=True, + intelligent_backspace=True, highlight_current_line=True, + highlight_current_cell=True, occurrence_highlighting=True, + scrollflagarea=True, edge_line=True, edge_line_column=79, + codecompletion_auto=False, codecompletion_case=True, + codecompletion_enter=False, show_blanks=False, + calltips=None, go_to_definition=False, + close_parentheses=True, close_quotes=False, + add_colons=True, auto_unindent=True, indent_chars=" "*4, + tab_stop_width=40, cloned_from=None, filename=None, + occurrence_timeout=1500): + + # Code completion and calltips + self.set_codecompletion_auto(codecompletion_auto) + self.set_codecompletion_case(codecompletion_case) + self.set_codecompletion_enter(codecompletion_enter) + self.set_calltips(calltips) + self.set_go_to_definition_enabled(go_to_definition) + self.set_close_parentheses_enabled(close_parentheses) + self.set_close_quotes_enabled(close_quotes) + self.set_add_colons_enabled(add_colons) + self.set_auto_unindent_enabled(auto_unindent) + self.set_indent_chars(indent_chars) + self.setTabStopWidth(tab_stop_width) + + # Scrollbar flag area + self.set_scrollflagarea_enabled(scrollflagarea) + + # Edge line + self.set_edge_line_enabled(edge_line) + self.set_edge_line_column(edge_line_column) + + # Blanks + self.set_blanks_enabled(show_blanks) + + # Line number area + if cloned_from: + self.setFont(font) # this is required for line numbers area + self.setup_margins(linenumbers, markers) + + # Lexer + self.set_language(language, filename) + + # Highlight current cell + self.set_highlight_current_cell(highlight_current_cell) + + # Highlight current line + self.set_highlight_current_line(highlight_current_line) + + # Occurrence highlighting + self.set_occurrence_highlighting(occurrence_highlighting) + self.set_occurrence_timeout(occurrence_timeout) + + # Tab always indents (even when cursor is not at the begin of line) + self.set_tab_mode(tab_mode) + + # Intelligent backspace + self.toggle_intelligent_backspace(intelligent_backspace) + + if cloned_from is not None: + self.set_as_clone(cloned_from) + self.update_linenumberarea_width() + elif font is not None: + self.set_font(font, color_scheme) + elif color_scheme is not None: + self.set_color_scheme(color_scheme) + + self.toggle_wrap_mode(wrap) + + def set_tab_mode(self, enable): + """ + enabled = tab always indent + (otherwise tab indents only when cursor is at the beginning of a line) + """ + self.tab_mode = enable + + def toggle_intelligent_backspace(self, state): + self.intelligent_backspace = state + + def set_go_to_definition_enabled(self, enable): + """Enable/Disable go-to-definition feature, which is implemented in + child class -> Editor widget""" + self.go_to_definition_enabled = enable + + def set_close_parentheses_enabled(self, enable): + """Enable/disable automatic parentheses insertion feature""" + self.close_parentheses_enabled = enable + + def set_close_quotes_enabled(self, enable): + """Enable/disable automatic quote insertion feature""" + self.close_quotes_enabled = enable + + def set_add_colons_enabled(self, enable): + """Enable/disable automatic colons insertion feature""" + self.add_colons_enabled = enable + + def set_auto_unindent_enabled(self, enable): + """Enable/disable automatic unindent after else/elif/finally/except""" + self.auto_unindent_enabled = enable + + def set_occurrence_highlighting(self, enable): + """Enable/disable occurrence highlighting""" + self.occurrence_highlighting = enable + if not enable: + self.__clear_occurrences() + + def set_occurrence_timeout(self, timeout): + """Set occurrence highlighting timeout (ms)""" + self.occurrence_timer.setInterval(timeout) + + def set_highlight_current_line(self, enable): + """Enable/disable current line highlighting""" + self.highlight_current_line_enabled = enable + if self.highlight_current_line_enabled: + self.highlight_current_line() + else: + self.unhighlight_current_line() + + def set_highlight_current_cell(self, enable): + """Enable/disable current line highlighting""" + hl_cell_enable = enable and self.supported_cell_language + self.highlight_current_cell_enabled = hl_cell_enable + if self.highlight_current_cell_enabled: + self.highlight_current_cell() + else: + self.unhighlight_current_cell() + + def set_language(self, language, filename=None): + self.tab_indents = language in self.TAB_ALWAYS_INDENTS + self.comment_string = '' + sh_class = sh.TextSH + if language is not None: + for (key, value) in ALL_LANGUAGES.items(): + if language.lower() in value: + self.supported_language = True + sh_class, comment_string, CFMatch = self.LANGUAGES[key] + self.comment_string = comment_string + if key in CELL_LANGUAGES: + self.supported_cell_language = True + self.cell_separators = CELL_LANGUAGES[key] + if CFMatch is None: + self.classfunc_match = None + else: + self.classfunc_match = CFMatch() + break + if filename is not None and not self.supported_language: + sh_class = sh.guess_pygments_highlighter(filename) + self.support_language = sh_class is not sh.TextSH + self._set_highlighter(sh_class) + + def _set_highlighter(self, sh_class): + self.highlighter_class = sh_class + if self.highlighter is not None: + # Removing old highlighter + # TODO: test if leaving parent/document as is eats memory + self.highlighter.setParent(None) + self.highlighter.setDocument(None) + self.highlighter = self.highlighter_class(self.document(), + self.font(), + self.color_scheme) + self._apply_highlighter_color_scheme() + + def is_json(self): + return (isinstance(self.highlighter, sh.PygmentsSH) and + self.highlighter._lexer.name == 'JSON') + + def is_python(self): + return self.highlighter_class is sh.PythonSH + + def is_cython(self): + return self.highlighter_class is sh.CythonSH + + def is_enaml(self): + return self.highlighter_class is sh.EnamlSH + + def is_python_like(self): + return self.is_python() or self.is_cython() or self.is_enaml() + + def intelligent_tab(self): + """Provide intelligent behavoir for Tab key press""" + leading_text = self.get_text('sol', 'cursor') + if not leading_text.strip() or leading_text.endswith('#'): + # blank line or start of comment + self.indent_or_replace() + elif self.in_comment_or_string() and not leading_text.endswith(' '): + # in a word in a comment + self.do_completion() + elif leading_text.endswith('import ') or leading_text[-1] == '.': + # blank import or dot completion + self.do_completion() + elif (leading_text.split()[0] in ['from', 'import'] and + not ';' in leading_text): + # import line with a single statement + # (prevents lines like: `import pdb; pdb.set_trace()`) + self.do_completion() + elif leading_text[-1] in '(,' or leading_text.endswith(', '): + self.indent_or_replace() + elif leading_text.endswith(' '): + # if the line ends with a space, indent + self.indent_or_replace() + elif re.search(r"[^\d\W]\w*\Z", leading_text, re.UNICODE): + # if the line ends with a non-whitespace character + self.do_completion() + else: + self.indent_or_replace() + + def intelligent_backtab(self): + """Provide intelligent behavoir for Shift+Tab key press""" + leading_text = self.get_text('sol', 'cursor') + if not leading_text.strip(): + # blank line + self.unindent() + elif self.in_comment_or_string(): + self.unindent() + elif leading_text[-1] in '(,' or leading_text.endswith(', '): + position = self.get_position('cursor') + self.show_object_info(position) + else: + # if the line ends with any other character but comma + self.unindent() + + def rehighlight(self): + """ + Rehighlight the whole document to rebuild outline explorer data + and import statements data from scratch + """ + if self.highlighter is not None: + self.highlighter.rehighlight() + if self.highlight_current_cell_enabled: + self.highlight_current_cell() + else: + self.unhighlight_current_cell() + if self.highlight_current_line_enabled: + self.highlight_current_line() + else: + self.unhighlight_current_line() + + def rehighlight_cells(self): + """Rehighlight cells when moving the scrollbar""" + if self.highlight_current_cell_enabled: + self.highlight_current_cell() + + def setup_margins(self, linenumbers=True, markers=True): + """ + Setup margin settings + (except font, now set in self.set_font) + """ + self.linenumbers_margin = linenumbers + self.markers_margin = markers + self.set_linenumberarea_enabled(linenumbers or markers) + + def remove_trailing_spaces(self): + """Remove trailing spaces""" + cursor = self.textCursor() + cursor.beginEditBlock() + cursor.movePosition(QTextCursor.Start) + while True: + cursor.movePosition(QTextCursor.EndOfBlock) + text = to_text_string(cursor.block().text()) + length = len(text)-len(text.rstrip()) + if length > 0: + cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, + length) + cursor.removeSelectedText() + if cursor.atEnd(): + break + cursor.movePosition(QTextCursor.NextBlock) + cursor.endEditBlock() + + def fix_indentation(self): + """Replace tabs by spaces""" + text_before = to_text_string(self.toPlainText()) + text_after = sourcecode.fix_indentation(text_before) + if text_before != text_after: + self.setPlainText(text_after) + self.document().setModified(True) + + def get_current_object(self): + """Return current object (string) """ + source_code = to_text_string(self.toPlainText()) + offset = self.get_position('cursor') + return sourcecode.get_primary_at(source_code, offset) + + @Slot() + def delete(self): + """Remove selected text""" + if self.has_selected_text(): + self.remove_selected_text() + + #------Find occurrences + def __find_first(self, text): + """Find first occurrence: scan whole document""" + flags = QTextDocument.FindCaseSensitively|QTextDocument.FindWholeWords + cursor = self.textCursor() + # Scanning whole document + cursor.movePosition(QTextCursor.Start) + regexp = QRegExp(r"\b%s\b" % QRegExp.escape(text), Qt.CaseSensitive) + cursor = self.document().find(regexp, cursor, flags) + self.__find_first_pos = cursor.position() + return cursor + + def __find_next(self, text, cursor): + """Find next occurrence""" + flags = QTextDocument.FindCaseSensitively|QTextDocument.FindWholeWords + regexp = QRegExp(r"\b%s\b" % QRegExp.escape(text), Qt.CaseSensitive) + cursor = self.document().find(regexp, cursor, flags) + if cursor.position() != self.__find_first_pos: + return cursor + + def __cursor_position_changed(self): + """Cursor position has changed""" + line, column = self.get_cursor_line_column() + self.sig_cursor_position_changed.emit(line, column) + if self.highlight_current_cell_enabled: + self.highlight_current_cell() + else: + self.unhighlight_current_cell() + if self.highlight_current_line_enabled: + self.highlight_current_line() + else: + self.unhighlight_current_line() + if self.occurrence_highlighting: + self.occurrence_timer.stop() + self.occurrence_timer.start() + + def __clear_occurrences(self): + """Clear occurrence markers""" + self.occurrences = [] + self.clear_extra_selections('occurrences') + self.scrollflagarea.update() + + def __highlight_selection(self, key, cursor, foreground_color=None, + background_color=None, underline_color=None, + underline_style=QTextCharFormat.SpellCheckUnderline, + update=False): + extra_selections = self.get_extra_selections(key) + selection = QTextEdit.ExtraSelection() + if foreground_color is not None: + selection.format.setForeground(foreground_color) + if background_color is not None: + selection.format.setBackground(background_color) + if underline_color is not None: + selection.format.setProperty(QTextFormat.TextUnderlineStyle, + to_qvariant(underline_style)) + selection.format.setProperty(QTextFormat.TextUnderlineColor, + to_qvariant(underline_color)) + selection.format.setProperty(QTextFormat.FullWidthSelection, + to_qvariant(True)) + selection.cursor = cursor + extra_selections.append(selection) + self.set_extra_selections(key, extra_selections) + if update: + self.update_extra_selections() + + def __mark_occurrences(self): + """Marking occurrences of the currently selected word""" + self.__clear_occurrences() + + if not self.supported_language: + return + + text = self.get_current_word() + if text is None: + return + if self.has_selected_text() and self.get_selected_text() != text: + return + + if (self.is_python_like()) and \ + (sourcecode.is_keyword(to_text_string(text)) or \ + to_text_string(text) == 'self'): + return + + # Highlighting all occurrences of word *text* + cursor = self.__find_first(text) + self.occurrences = [] + while cursor: + self.occurrences.append(cursor.blockNumber()) + self.__highlight_selection('occurrences', cursor, + background_color=self.occurrence_color) + cursor = self.__find_next(text, cursor) + self.update_extra_selections() + if len(self.occurrences) > 1 and self.occurrences[-1] == 0: + # XXX: this is never happening with PySide but it's necessary + # for PyQt4... this must be related to a different behavior for + # the QTextDocument.find function between those two libraries + self.occurrences.pop(-1) + self.scrollflagarea.update() + + #-----highlight found results (find/replace widget) + def highlight_found_results(self, pattern, words=False, regexp=False): + """Highlight all found patterns""" + pattern = to_text_string(pattern) + if not pattern: + return + if not regexp: + pattern = re.escape(to_text_string(pattern)) + pattern = r"\b%s\b" % pattern if words else pattern + text = to_text_string(self.toPlainText()) + try: + regobj = re.compile(pattern) + except sre_constants.error: + return + extra_selections = [] + self.found_results = [] + for match in regobj.finditer(text): + pos1, pos2 = match.span() + selection = QTextEdit.ExtraSelection() + selection.format.setBackground(self.found_results_color) + selection.cursor = self.textCursor() + selection.cursor.setPosition(pos1) + self.found_results.append(selection.cursor.blockNumber()) + selection.cursor.setPosition(pos2, QTextCursor.KeepAnchor) + extra_selections.append(selection) + self.set_extra_selections('find', extra_selections) + self.update_extra_selections() + + def clear_found_results(self): + """Clear found results highlighting""" + self.found_results = [] + self.clear_extra_selections('find') + self.scrollflagarea.update() + + def __text_has_changed(self): + """Text has changed, eventually clear found results highlighting""" + if self.found_results: + self.clear_found_results() + + #-----markers + def get_markers_margin(self): + if self.markers_margin: + return self.markers_margin_width + else: + return 0 + + #-----linenumberarea + def set_linenumberarea_enabled(self, state): + self.linenumberarea_enabled = state + self.linenumberarea.setVisible(state) + self.update_linenumberarea_width() + + def get_linenumberarea_width(self): + """Return current line number area width""" + return self.linenumberarea.contentsRect().width() + + def compute_linenumberarea_width(self): + """Compute and return line number area width""" + if not self.linenumberarea_enabled: + return 0 + digits = 1 + maxb = max(1, self.blockCount()) + while maxb >= 10: + maxb /= 10 + digits += 1 + if self.linenumbers_margin: + linenumbers_margin = 3+self.fontMetrics().width('9'*digits) + else: + linenumbers_margin = 0 + return linenumbers_margin+self.get_markers_margin() + + def update_linenumberarea_width(self, new_block_count=None): + """ + Update line number area width. + + new_block_count is needed to handle blockCountChanged(int) signal + """ + self.setViewportMargins(self.compute_linenumberarea_width(), 0, + self.get_scrollflagarea_width(), 0) + + def update_linenumberarea(self, qrect, dy): + """Update line number area""" + if dy: + self.linenumberarea.scroll(0, dy) + else: + self.linenumberarea.update(0, qrect.y(), + self.linenumberarea.width(), + qrect.height()) + if qrect.contains(self.viewport().rect()): + self.update_linenumberarea_width() + + def linenumberarea_paint_event(self, event): + """Painting line number area""" + painter = QPainter(self.linenumberarea) + painter.fillRect(event.rect(), self.sideareas_color) + # This is needed to make that the font size of line numbers + # be the same as the text one when zooming + # See Issue 2296 + if sys.platform == 'darwin': + font = self.font() + else: + font = painter.font() + font_height = self.fontMetrics().height() + + active_block = self.textCursor().block() + active_line_number = active_block.blockNumber() + 1 + + def draw_pixmap(ytop, pixmap): + painter.drawPixmap(0, ytop + (font_height-pixmap.height()) / 2, + pixmap) + + for top, line_number, block in self.visible_blocks: + if self.linenumbers_margin: + if line_number == active_line_number: + font.setWeight(font.Bold) + painter.setFont(font) + painter.setPen(self.normal_color) + else: + font.setWeight(font.Normal) + painter.setFont(font) + painter.setPen(self.linenumbers_color) + + painter.drawText(0, top, self.linenumberarea.width(), + font_height, + Qt.AlignRight | Qt.AlignBottom, + to_text_string(line_number)) + + data = block.userData() + if self.markers_margin and data: + if data.code_analysis: + for _message, error in data.code_analysis: + if error: + break + if error: + draw_pixmap(top, self.error_pixmap) + else: + draw_pixmap(top, self.warning_pixmap) + if data.todo: + draw_pixmap(top, self.todo_pixmap) + if data.breakpoint: + if data.breakpoint_condition is None: + draw_pixmap(top, self.bp_pixmap) + else: + draw_pixmap(top, self.bpc_pixmap) + + def __get_linenumber_from_mouse_event(self, event): + """Return line number from mouse event""" + block = self.firstVisibleBlock() + line_number = block.blockNumber() + top = self.blockBoundingGeometry(block).translated( + self.contentOffset()).top() + bottom = top + self.blockBoundingRect(block).height() + + while block.isValid() and top < event.pos().y(): + block = block.next() + top = bottom + bottom = top + self.blockBoundingRect(block).height() + line_number += 1 + + return line_number + + def linenumberarea_mousemove_event(self, event): + """Handling line number area mouse move event""" + line_number = self.__get_linenumber_from_mouse_event(event) + block = self.document().findBlockByNumber(line_number-1) + data = block.userData() + + # this disables pyflakes messages if there is an active drag/selection + # operation + check = self.linenumberarea_released == -1 + if data and data.code_analysis and check: + self.__show_code_analysis_results(line_number, data.code_analysis) + + if event.buttons() == Qt.LeftButton: + self.linenumberarea_released = line_number + self.linenumberarea_select_lines(self.linenumberarea_pressed, + self.linenumberarea_released) + + def linenumberarea_mousedoubleclick_event(self, event): + """Handling line number area mouse double-click event""" + line_number = self.__get_linenumber_from_mouse_event(event) + shift = event.modifiers() & Qt.ShiftModifier + self.add_remove_breakpoint(line_number, edit_condition=shift) + + def linenumberarea_mousepress_event(self, event): + """Handling line number area mouse double press event""" + line_number = self.__get_linenumber_from_mouse_event(event) + self.linenumberarea_pressed = line_number + self.linenumberarea_released = line_number + self.linenumberarea_select_lines(self.linenumberarea_pressed, + self.linenumberarea_released) + + def linenumberarea_mouserelease_event(self, event): + """Handling line number area mouse release event""" + self.linenumberarea_released = -1 + self.linenumberarea_pressed = -1 + + def linenumberarea_select_lines(self, linenumber_pressed, + linenumber_released): + """Select line(s) after a mouse press/mouse press drag event""" + find_block_by_line_number = self.document().findBlockByLineNumber + move_n_blocks = (linenumber_released - linenumber_pressed) + start_line = linenumber_pressed + start_block = find_block_by_line_number(start_line - 1) + + cursor = self.textCursor() + cursor.setPosition(start_block.position()) + + # Select/drag downwards + if move_n_blocks > 0: + for n in range(abs(move_n_blocks) + 1): + cursor.movePosition(cursor.NextBlock, cursor.KeepAnchor) + # Select/drag upwards or select single line + else: + cursor.movePosition(cursor.NextBlock) + for n in range(abs(move_n_blocks) + 1): + cursor.movePosition(cursor.PreviousBlock, cursor.KeepAnchor) + + # Account for last line case + if linenumber_released == self.blockCount(): + cursor.movePosition(cursor.EndOfBlock, cursor.KeepAnchor) + else: + cursor.movePosition(cursor.StartOfBlock, cursor.KeepAnchor) + + self.setTextCursor(cursor) + + #------Breakpoints + def add_remove_breakpoint(self, line_number=None, condition=None, + edit_condition=False): + """Add/remove breakpoint""" + if not self.is_python_like(): + return + if line_number is None: + block = self.textCursor().block() + else: + block = self.document().findBlockByNumber(line_number-1) + data = block.userData() + if data: + data.breakpoint = not data.breakpoint + old_breakpoint_condition = data.breakpoint_condition + data.breakpoint_condition = None + else: + data = BlockUserData(self) + data.breakpoint = True + old_breakpoint_condition = None + if condition is not None: + data.breakpoint_condition = condition + if edit_condition: + data.breakpoint = True + condition = data.breakpoint_condition + if old_breakpoint_condition is not None: + condition = old_breakpoint_condition + condition, valid = QInputDialog.getText(self, + _('Breakpoint'), + _("Condition:"), + QLineEdit.Normal, condition) + if valid: + condition = str(condition) + if not condition: + condition = None + data.breakpoint_condition = condition + else: + data.breakpoint_condition = old_breakpoint_condition + return + if data.breakpoint: + text = to_text_string(block.text()).strip() + if len(text) == 0 or text.startswith('#') or text.startswith('"') \ + or text.startswith("'"): + data.breakpoint = False + block.setUserData(data) + self.linenumberarea.update() + self.scrollflagarea.update() + self.breakpoints_changed.emit() + + def get_breakpoints(self): + """Get breakpoints""" + breakpoints = [] + block = self.document().firstBlock() + for line_number in range(1, self.document().blockCount()+1): + data = block.userData() + if data and data.breakpoint: + breakpoints.append((line_number, data.breakpoint_condition)) + block = block.next() + return breakpoints + + def clear_breakpoints(self): + """Clear breakpoints""" + self.breakpoints = [] + for data in self.blockuserdata_list[:]: + data.breakpoint = False + # data.breakpoint_condition = None # not necessary, but logical + if data.is_empty(): + del data + + def set_breakpoints(self, breakpoints): + """Set breakpoints""" + self.clear_breakpoints() + for line_number, condition in breakpoints: + self.add_remove_breakpoint(line_number, condition) + + def update_breakpoints(self): + """Update breakpoints""" + self.breakpoints_changed.emit() + + #-----Code introspection + def do_completion(self, automatic=False): + """Trigger completion""" + if not self.is_completion_widget_visible(): + self.get_completions.emit(automatic) + + def do_go_to_definition(self): + """Trigger go-to-definition""" + if not self.in_comment_or_string(): + self.go_to_definition.emit(self.textCursor().position()) + + def show_object_info(self, position): + """Trigger a calltip""" + self.sig_show_object_info.emit(position) + + #-----edge line + def set_edge_line_enabled(self, state): + """Toggle edge line visibility""" + self.edge_line_enabled = state + self.edge_line.setVisible(state) + + def set_edge_line_column(self, column): + """Set edge line column value""" + self.edge_line.column = column + self.edge_line.update() + + # -----blank spaces + def set_blanks_enabled(self, state): + """Toggle blanks visibility""" + self.blanks_enabled = state + option = self.document().defaultTextOption() + option.setFlags(option.flags() | \ + QTextOption.AddSpaceForLineAndParagraphSeparators) + if self.blanks_enabled: + option.setFlags(option.flags() | QTextOption.ShowTabsAndSpaces) + else: + option.setFlags(option.flags() & ~QTextOption.ShowTabsAndSpaces) + self.document().setDefaultTextOption(option) + # Rehighlight to make the spaces less apparent. + self.rehighlight() + + #-----scrollflagarea + def set_scrollflagarea_enabled(self, state): + """Toggle scroll flag area visibility""" + self.scrollflagarea_enabled = state + self.scrollflagarea.setVisible(state) + self.update_linenumberarea_width() + + def get_scrollflagarea_width(self): + """Return scroll flag area width""" + if self.scrollflagarea_enabled: + return ScrollFlagArea.WIDTH + else: + return 0 + + def scrollflagarea_paint_event(self, event): + """Painting the scroll flag area""" + make_flag = self.scrollflagarea.make_flag_qrect + make_slider = self.scrollflagarea.make_slider_range + + # Filling the whole painting area + painter = QPainter(self.scrollflagarea) + painter.fillRect(event.rect(), self.sideareas_color) + block = self.document().firstBlock() + + # Painting warnings and todos + for line_number in range(1, self.document().blockCount()+1): + data = block.userData() + if data: + position = self.scrollflagarea.value_to_position(line_number) + if data.code_analysis: + # Warnings + color = self.warning_color + for _message, error in data.code_analysis: + if error: + color = self.error_color + break + set_scrollflagarea_painter(painter, color) + painter.drawRect(make_flag(position)) + if data.todo: + # TODOs + set_scrollflagarea_painter(painter, self.todo_color) + painter.drawRect(make_flag(position)) + if data.breakpoint: + # Breakpoints + set_scrollflagarea_painter(painter, self.breakpoint_color) + painter.drawRect(make_flag(position)) + block = block.next() + + # Occurrences + if self.occurrences: + set_scrollflagarea_painter(painter, self.occurrence_color) + for line_number in self.occurrences: + position = self.scrollflagarea.value_to_position(line_number) + painter.drawRect(make_flag(position)) + + # Found results + if self.found_results: + set_scrollflagarea_painter(painter, self.found_results_color) + for line_number in self.found_results: + position = self.scrollflagarea.value_to_position(line_number) + painter.drawRect(make_flag(position)) + + # Painting the slider range + pen_color = QColor(Qt.white) + pen_color.setAlphaF(.8) + painter.setPen(pen_color) + brush_color = QColor(Qt.white) + brush_color.setAlphaF(.5) + painter.setBrush(QBrush(brush_color)) + painter.drawRect(make_slider(self.firstVisibleBlock().blockNumber())) + + def resizeEvent(self, event): + """Reimplemented Qt method to handle line number area resizing""" + TextEditBaseWidget.resizeEvent(self, event) + cr = self.contentsRect() + self.linenumberarea.setGeometry(\ + QRect(cr.left(), cr.top(), + self.compute_linenumberarea_width(), cr.height())) + self.__set_scrollflagarea_geometry(cr) + + def __set_scrollflagarea_geometry(self, contentrect): + """Set scroll flag area geometry""" + cr = contentrect + if self.verticalScrollBar().isVisible(): + vsbw = self.verticalScrollBar().contentsRect().width() + else: + vsbw = 0 + _left, _top, right, _bottom = self.getContentsMargins() + if right > vsbw: + # Depending on the platform (e.g. on Ubuntu), the scrollbar sizes + # may be taken into account in the contents margins whereas it is + # not on Windows for example + vsbw = 0 + self.scrollflagarea.setGeometry(\ + QRect(cr.right()-ScrollFlagArea.WIDTH-vsbw, cr.top(), + self.scrollflagarea.WIDTH, cr.height())) + + #-----edgeline + def viewportEvent(self, event): + """Override Qt method""" + # 79-column edge line + offset = self.contentOffset() + x = self.blockBoundingGeometry(self.firstVisibleBlock()) \ + .translated(offset.x(), offset.y()).left() \ + +self.get_linenumberarea_width() \ + +self.fontMetrics().width('9'*self.edge_line.column)+5 + cr = self.contentsRect() + self.edge_line.setGeometry(QRect(x, cr.top(), 1, cr.bottom())) + self.__set_scrollflagarea_geometry(cr) + return TextEditBaseWidget.viewportEvent(self, event) + + #-----Misc. + def _apply_highlighter_color_scheme(self): + """Apply color scheme from syntax highlighter to the editor""" + hl = self.highlighter + if hl is not None: + self.set_palette(background=hl.get_background_color(), + foreground=hl.get_foreground_color()) + self.currentline_color = hl.get_currentline_color() + self.currentcell_color = hl.get_currentcell_color() + self.occurrence_color = hl.get_occurrence_color() + self.ctrl_click_color = hl.get_ctrlclick_color() + self.sideareas_color = hl.get_sideareas_color() + self.comment_color = hl.get_comment_color() + self.normal_color = hl.get_foreground_color() + self.matched_p_color = hl.get_matched_p_color() + self.unmatched_p_color = hl.get_unmatched_p_color() + + def apply_highlighter_settings(self, color_scheme=None): + """Apply syntax highlighter settings""" + if self.highlighter is not None: + # Updating highlighter settings (font and color scheme) + self.highlighter.setup_formats(self.font()) + if color_scheme is not None: + self.set_color_scheme(color_scheme) + else: + self.highlighter.rehighlight() + + def get_outlineexplorer_data(self): + """Get data provided by the Outline Explorer""" + return self.highlighter.get_outlineexplorer_data() + + def set_font(self, font, color_scheme=None): + """Set font""" + # Note: why using this method to set color scheme instead of + # 'set_color_scheme'? To avoid rehighlighting the document twice + # at startup. + if color_scheme is not None: + self.color_scheme = color_scheme + self.setFont(font) + self.update_linenumberarea_width() + self.apply_highlighter_settings(color_scheme) + + def set_color_scheme(self, color_scheme): + """Set color scheme for syntax highlighting""" + self.color_scheme = color_scheme + if self.highlighter is not None: + # this calls self.highlighter.rehighlight() + self.highlighter.set_color_scheme(color_scheme) + self._apply_highlighter_color_scheme() + if self.highlight_current_cell_enabled: + self.highlight_current_cell() + else: + self.unhighlight_current_cell() + if self.highlight_current_line_enabled: + self.highlight_current_line() + else: + self.unhighlight_current_line() + + def set_text(self, text): + """Set the text of the editor""" + self.setPlainText(text) + self.set_eol_chars(text) + #if self.supported_language: + #self.highlighter.rehighlight() + + def set_text_from_file(self, filename, language=None): + """Set the text of the editor from file *fname*""" + text, _enc = encoding.read(filename) + if language is None: + language = get_file_language(filename, text) + self.set_language(language, filename) + self.set_text(text) + + def append(self, text): + """Append text to the end of the text widget""" + cursor = self.textCursor() + cursor.movePosition(QTextCursor.End) + cursor.insertText(text) + + @Slot() + def paste(self): + """ + Reimplement QPlainTextEdit's method to fix the following issue: + on Windows, pasted text has only 'LF' EOL chars even if the original + text has 'CRLF' EOL chars + """ + clipboard = QApplication.clipboard() + text = to_text_string(clipboard.text()) + if len(text.splitlines()) > 1: + eol_chars = self.get_line_separator() + clipboard.setText( eol_chars.join((text+eol_chars).splitlines()) ) + # Standard paste + TextEditBaseWidget.paste(self) + + def get_block_data(self, block): + """Return block data (from syntax highlighter)""" + return self.highlighter.block_data.get(block) + + def get_fold_level(self, block_nb): + """Is it a fold header line? + If so, return fold level + If not, return None""" + block = self.document().findBlockByNumber(block_nb) + return self.get_block_data(block).fold_level + + +#=============================================================================== +# High-level editor features +#=============================================================================== + @Slot() + def center_cursor_on_next_focus(self): + """QPlainTextEdit's "centerCursor" requires the widget to be visible""" + self.centerCursor() + self.focus_in.disconnect(self.center_cursor_on_next_focus) + + def go_to_line(self, line, word=''): + """Go to line number *line* and eventually highlight it""" + block = self.document().findBlockByNumber(line-1) + self.setTextCursor(QTextCursor(block)) + if self.isVisible(): + self.centerCursor() + else: + self.focus_in.connect(self.center_cursor_on_next_focus) + self.horizontalScrollBar().setValue(0) + if word and to_text_string(word) in to_text_string(block.text()): + self.find(word, QTextDocument.FindCaseSensitively) + + def exec_gotolinedialog(self): + """Execute the GoToLineDialog dialog box""" + dlg = GoToLineDialog(self) + if dlg.exec_(): + self.go_to_line(dlg.get_line_number()) + + def cleanup_code_analysis(self): + """Remove all code analysis markers""" + self.setUpdatesEnabled(False) + self.clear_extra_selections('code_analysis') + for data in self.blockuserdata_list[:]: + data.code_analysis = [] + if data.is_empty(): + del data + self.setUpdatesEnabled(True) + # When the new code analysis results are empty, it is necessary + # to update manually the scrollflag and linenumber areas (otherwise, + # the old flags will still be displayed): + self.scrollflagarea.update() + self.linenumberarea.update() + + def process_code_analysis(self, check_results): + """Analyze filename code with pyflakes""" + self.cleanup_code_analysis() + if check_results is None: + # Not able to compile module + return + self.setUpdatesEnabled(False) + cursor = self.textCursor() + document = self.document() + flags = QTextDocument.FindCaseSensitively|QTextDocument.FindWholeWords + for message, line_number in check_results: + error = 'syntax' in message + # Note: line_number start from 1 (not 0) + block = self.document().findBlockByNumber(line_number-1) + data = block.userData() + if not data: + data = BlockUserData(self) + data.code_analysis.append( (message, error) ) + block.setUserData(data) + refs = re.findall(r"\'[a-zA-Z0-9_]*\'", message) + for ref in refs: + # Highlighting found references + text = ref[1:-1] + # Scanning line number *line* and following lines if continued + def is_line_splitted(line_no): + text = to_text_string( + document.findBlockByNumber(line_no).text()) + stripped = text.strip() + return stripped.endswith('\\') or stripped.endswith(',') \ + or len(stripped) == 0 + line2 = line_number-1 + while line2 < self.blockCount()-1 and is_line_splitted(line2): + line2 += 1 + cursor.setPosition(block.position()) + cursor.movePosition(QTextCursor.StartOfBlock) + regexp = QRegExp(r"\b%s\b" % QRegExp.escape(text), + Qt.CaseSensitive) + color = self.error_color if error else self.warning_color + # Highlighting all occurrences (this is a compromise as pyflakes + # do not provide the column number -- see Issue 709 on Spyder's + # GoogleCode project website) + cursor = document.find(regexp, cursor, flags) + if cursor: + while cursor and cursor.blockNumber() <= line2 \ + and cursor.blockNumber() >= line_number-1 \ + and cursor.position() > 0: + self.__highlight_selection('code_analysis', cursor, + underline_color=QColor(color)) + cursor = document.find(text, cursor, flags) + self.update_extra_selections() + self.setUpdatesEnabled(True) + self.linenumberarea.update() + + def __show_code_analysis_results(self, line_number, code_analysis): + """Show warning/error messages""" + msglist = [ msg for msg, _error in code_analysis ] + self.show_calltip(_("Code analysis"), msglist, + color='#129625', at_line=line_number) + + def go_to_next_warning(self): + """Go to next code analysis warning message + and return new cursor position""" + block = self.textCursor().block() + line_count = self.document().blockCount() + while True: + if block.blockNumber()+1 < line_count: + block = block.next() + else: + block = self.document().firstBlock() + data = block.userData() + if data and data.code_analysis: + break + line_number = block.blockNumber()+1 + self.go_to_line(line_number) + self.__show_code_analysis_results(line_number, data.code_analysis) + return self.get_position('cursor') + + def go_to_previous_warning(self): + """Go to previous code analysis warning message + and return new cursor position""" + block = self.textCursor().block() + while True: + if block.blockNumber() > 0: + block = block.previous() + else: + block = self.document().lastBlock() + data = block.userData() + if data and data.code_analysis: + break + line_number = block.blockNumber()+1 + self.go_to_line(line_number) + self.__show_code_analysis_results(line_number, data.code_analysis) + return self.get_position('cursor') + + + #------Tasks management + def go_to_next_todo(self): + """Go to next todo and return new cursor position""" + block = self.textCursor().block() + line_count = self.document().blockCount() + while True: + if block.blockNumber()+1 < line_count: + block = block.next() + else: + block = self.document().firstBlock() + data = block.userData() + if data and data.todo: + break + line_number = block.blockNumber()+1 + self.go_to_line(line_number) + self.show_calltip(_("To do"), data.todo, + color='#3096FC', at_line=line_number) + return self.get_position('cursor') + + def process_todo(self, todo_results): + """Process todo finder results""" + for data in self.blockuserdata_list[:]: + data.todo = '' + if data.is_empty(): + del data + for message, line_number in todo_results: + block = self.document().findBlockByNumber(line_number-1) + data = block.userData() + if not data: + data = BlockUserData(self) + data.todo = message + block.setUserData(data) + self.scrollflagarea.update() + + + #------Comments/Indentation + def add_prefix(self, prefix): + """Add prefix to current line or selected line(s)""" + cursor = self.textCursor() + if self.has_selected_text(): + # Add prefix to selected line(s) + start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() + + # Let's see if selection begins at a block start + first_pos = min([start_pos, end_pos]) + first_cursor = self.textCursor() + first_cursor.setPosition(first_pos) + begins_at_block_start = first_cursor.atBlockStart() + + cursor.beginEditBlock() + cursor.setPosition(end_pos) + # Check if end_pos is at the start of a block: if so, starting + # changes from the previous block + if cursor.atBlockStart(): + cursor.movePosition(QTextCursor.PreviousBlock) + if cursor.position() < start_pos: + cursor.setPosition(start_pos) + + while cursor.position() >= start_pos: + cursor.movePosition(QTextCursor.StartOfBlock) + cursor.insertText(prefix) + if start_pos == 0 and cursor.blockNumber() == 0: + # Avoid infinite loop when indenting the very first line + break + cursor.movePosition(QTextCursor.PreviousBlock) + cursor.movePosition(QTextCursor.EndOfBlock) + cursor.endEditBlock() + if begins_at_block_start: + # Extending selection to prefix: + cursor = self.textCursor() + start_pos = cursor.selectionStart() + end_pos = cursor.selectionEnd() + if start_pos < end_pos: + start_pos -= len(prefix) + else: + end_pos -= len(prefix) + cursor.setPosition(start_pos, QTextCursor.MoveAnchor) + cursor.setPosition(end_pos, QTextCursor.KeepAnchor) + self.setTextCursor(cursor) + else: + # Add prefix to current line + cursor.beginEditBlock() + cursor.movePosition(QTextCursor.StartOfBlock) + cursor.insertText(prefix) + cursor.endEditBlock() + + def __is_cursor_at_start_of_block(self, cursor): + cursor.movePosition(QTextCursor.StartOfBlock) + + def remove_suffix(self, suffix): + """ + Remove suffix from current line (there should not be any selection) + """ + cursor = self.textCursor() + cursor.setPosition(cursor.position()-len(suffix), + QTextCursor.KeepAnchor) + if to_text_string(cursor.selectedText()) == suffix: + cursor.removeSelectedText() + + def remove_prefix(self, prefix): + """Remove prefix from current line or selected line(s)""" + cursor = self.textCursor() + if self.has_selected_text(): + # Remove prefix from selected line(s) + start_pos, end_pos = sorted([cursor.selectionStart(), + cursor.selectionEnd()]) + cursor.setPosition(start_pos) + if not cursor.atBlockStart(): + cursor.movePosition(QTextCursor.StartOfBlock) + start_pos = cursor.position() + cursor.beginEditBlock() + cursor.setPosition(end_pos) + # Check if end_pos is at the start of a block: if so, starting + # changes from the previous block + if cursor.atBlockStart(): + cursor.movePosition(QTextCursor.PreviousBlock) + if cursor.position() < start_pos: + cursor.setPosition(start_pos) + + cursor.movePosition(QTextCursor.StartOfBlock) + old_pos = None + while cursor.position() >= start_pos: + new_pos = cursor.position() + if old_pos == new_pos: + break + else: + old_pos = new_pos + line_text = to_text_string(cursor.block().text()) + if (prefix.strip() and line_text.lstrip().startswith(prefix) + or line_text.startswith(prefix)): + cursor.movePosition(QTextCursor.Right, + QTextCursor.MoveAnchor, + line_text.find(prefix)) + cursor.movePosition(QTextCursor.Right, + QTextCursor.KeepAnchor, len(prefix)) + cursor.removeSelectedText() + cursor.movePosition(QTextCursor.PreviousBlock) + cursor.endEditBlock() + else: + # Remove prefix from current line + cursor.movePosition(QTextCursor.StartOfBlock) + line_text = to_text_string(cursor.block().text()) + if (prefix.strip() and line_text.lstrip().startswith(prefix) + or line_text.startswith(prefix)): + cursor.movePosition(QTextCursor.Right, + QTextCursor.MoveAnchor, + line_text.find(prefix)) + cursor.movePosition(QTextCursor.Right, + QTextCursor.KeepAnchor, len(prefix)) + cursor.removeSelectedText() + + def fix_indent(self, forward=True, comment_or_string=False): + """ + Fix indentation (Python only, no text selection) + forward=True: fix indent only if text is not enough indented + (otherwise force indent) + forward=False: fix indent only if text is too much indented + (otherwise force unindent) + + Returns True if indent needed to be fixed + """ + cursor = self.textCursor() + block_nb = cursor.blockNumber() + # find the line that contains our scope + diff = 0 + add_indent = False + prevline = None + for prevline in range(block_nb-1, -1, -1): + cursor.movePosition(QTextCursor.PreviousBlock) + prevtext = to_text_string(cursor.block().text()).rstrip() + if (self.is_python_like() and not prevtext.strip().startswith('#') \ + and prevtext) or prevtext: + if prevtext.strip().endswith(')'): + comment_or_string = True # prevent further parsing + elif prevtext.strip().endswith(':') and self.is_python_like(): + add_indent = True + comment_or_string = True + if prevtext.count(')') > prevtext.count('('): + diff = prevtext.count(')') - prevtext.count('(') + continue + elif diff: + diff += prevtext.count(')') - prevtext.count('(') + if not diff: + break + else: + break + + if not prevline: + return False + + indent = self.get_block_indentation(block_nb) + correct_indent = self.get_block_indentation(prevline) + + if add_indent: + correct_indent += len(self.indent_chars) + + if not comment_or_string: + if prevtext.endswith(':') and self.is_python_like(): + # Indent + correct_indent += len(self.indent_chars) + elif (prevtext.endswith('continue') or prevtext.endswith('break') \ + or prevtext.endswith('pass')) and self.is_python_like(): + # Unindent + correct_indent -= len(self.indent_chars) + elif len(re.split(r'\(|\{|\[', prevtext)) > 1: + rlmap = {")":"(", "]":"[", "}":"{"} + for par in rlmap: + i_right = prevtext.rfind(par) + if i_right != -1: + prevtext = prevtext[:i_right] + for _i in range(len(prevtext.split(par))): + i_left = prevtext.rfind(rlmap[par]) + if i_left != -1: + prevtext = prevtext[:i_left] + else: + break + else: + if prevtext.strip(): + if len(re.split(r'\(|\{|\[', prevtext)) > 1: + #correct indent only if there are still opening brackets + prevexpr = re.split(r'\(|\{|\[', prevtext)[-1] + correct_indent = len(prevtext)-len(prevexpr) + else: + correct_indent = len(prevtext) + + if (forward and indent >= correct_indent) or \ + (not forward and indent <= correct_indent): + # No indentation fix is necessary + return False + + if correct_indent >= 0: + cursor = self.textCursor() + cursor.movePosition(QTextCursor.StartOfBlock) + cursor.setPosition(cursor.position()+indent, QTextCursor.KeepAnchor) + cursor.removeSelectedText() + cursor.insertText(self.indent_chars[0]*correct_indent) + return True + + @Slot() + def clear_all_output(self): + """Removes all ouput in the ipynb format (Json only)""" + try: + nb = nbformat.reads(self.toPlainText(), as_version=4) + if nb.cells: + for cell in nb.cells: + if 'outputs' in cell: + cell['outputs'] = [] + if 'prompt_number' in cell: + cell['prompt_number'] = None + # We do the following rather than using self.setPlainText + # to benefit from QTextEdit's undo/redo feature. + self.selectAll() + self.insertPlainText(nbformat.writes(nb)) + except Exception as e: + QMessageBox.critical(self, _('Removal error'), + _("It was not possible to remove outputs from " + "this notebook. The error is:\n\n") + \ + to_text_string(e)) + return + + @Slot() + def convert_notebook(self): + """Convert an IPython notebook to a Python script in editor""" + try: + nb = nbformat.reads(self.toPlainText(), as_version=4) + script = nbexporter().from_notebook_node(nb)[0] + except Exception as e: + QMessageBox.critical(self, _('Conversion error'), + _("It was not possible to convert this " + "notebook. The error is:\n\n") + \ + to_text_string(e)) + return + self.sig_new_file.emit(script) + + def indent(self, force=False): + """ + Indent current line or selection + + force=True: indent even if cursor is not a the beginning of the line + """ + leading_text = self.get_text('sol', 'cursor') + if self.has_selected_text(): + self.add_prefix(self.indent_chars) + elif force or not leading_text.strip() \ + or (self.tab_indents and self.tab_mode): + if self.is_python_like(): + if not self.fix_indent(forward=True): + self.add_prefix(self.indent_chars) + else: + self.add_prefix(self.indent_chars) + else: + if len(self.indent_chars) > 1: + length = len(self.indent_chars) + self.insert_text(" "*(length-(len(leading_text) % length))) + else: + self.insert_text(self.indent_chars) + + def indent_or_replace(self): + """Indent or replace by 4 spaces depending on selection and tab mode""" + if (self.tab_indents and self.tab_mode) or not self.has_selected_text(): + self.indent() + else: + cursor = self.textCursor() + if self.get_selected_text() == \ + to_text_string(cursor.block().text()): + self.indent() + else: + cursor1 = self.textCursor() + cursor1.setPosition(cursor.selectionStart()) + cursor2 = self.textCursor() + cursor2.setPosition(cursor.selectionEnd()) + if cursor1.blockNumber() != cursor2.blockNumber(): + self.indent() + else: + self.replace(self.indent_chars) + + def unindent(self, force=False): + """ + Unindent current line or selection + + force=True: unindent even if cursor is not a the beginning of the line + """ + if self.has_selected_text(): + self.remove_prefix(self.indent_chars) + else: + leading_text = self.get_text('sol', 'cursor') + if force or not leading_text.strip() \ + or (self.tab_indents and self.tab_mode): + if self.is_python_like(): + if not self.fix_indent(forward=False): + self.remove_prefix(self.indent_chars) + elif leading_text.endswith('\t'): + self.remove_prefix('\t') + else: + self.remove_prefix(self.indent_chars) + + @Slot() + def toggle_comment(self): + """Toggle comment on current line or selection""" + cursor = self.textCursor() + start_pos, end_pos = sorted([cursor.selectionStart(), + cursor.selectionEnd()]) + cursor.setPosition(end_pos) + last_line = cursor.block().blockNumber() + if cursor.atBlockStart() and start_pos != end_pos: + last_line -= 1 + cursor.setPosition(start_pos) + first_line = cursor.block().blockNumber() + # If the selection contains only commented lines and surrounding + # whitespace, uncomment. Otherwise, comment. + is_comment_or_whitespace = True + at_least_one_comment = False + for _line_nb in range(first_line, last_line+1): + text = to_text_string(cursor.block().text()).lstrip() + is_comment = text.startswith(self.comment_string) + is_whitespace = (text == '') + is_comment_or_whitespace *= (is_comment or is_whitespace) + if is_comment: + at_least_one_comment = True + cursor.movePosition(QTextCursor.NextBlock) + if is_comment_or_whitespace and at_least_one_comment: + self.uncomment() + else: + self.comment() + + def comment(self): + """Comment current line or selection.""" + self.add_prefix(self.comment_string) + + def uncomment(self): + """Uncomment current line or selection.""" + self.remove_prefix(self.comment_string) + + def __blockcomment_bar(self): + return self.comment_string + '='*(79-len(self.comment_string)) + + def transform_to_uppercase(self): + """Change to uppercase current line or selection.""" + cursor = self.textCursor() + prev_pos = cursor.position() + selected_text = to_text_string(cursor.selectedText()) + + if len(selected_text) == 0: + prev_pos = cursor.position() + cursor.select(QTextCursor.WordUnderCursor) + selected_text = to_text_string(cursor.selectedText()) + + s = selected_text.upper() + cursor.insertText(s) + self.set_cursor_position(prev_pos) + + def transform_to_lowercase(self): + """Change to lowercase current line or selection.""" + cursor = self.textCursor() + prev_pos = cursor.position() + selected_text = to_text_string(cursor.selectedText()) + + if len(selected_text) == 0: + prev_pos = cursor.position() + cursor.select(QTextCursor.WordUnderCursor) + selected_text = to_text_string(cursor.selectedText()) + + s = selected_text.lower() + cursor.insertText(s) + self.set_cursor_position(prev_pos) + + def blockcomment(self): + """Block comment current line or selection.""" + comline = self.__blockcomment_bar() + self.get_line_separator() + cursor = self.textCursor() + if self.has_selected_text(): + self.extend_selection_to_complete_lines() + start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() + else: + start_pos = end_pos = cursor.position() + cursor.beginEditBlock() + cursor.setPosition(start_pos) + cursor.movePosition(QTextCursor.StartOfBlock) + while cursor.position() <= end_pos: + cursor.insertText(self.comment_string + " ") + cursor.movePosition(QTextCursor.EndOfBlock) + if cursor.atEnd(): + break + cursor.movePosition(QTextCursor.NextBlock) + end_pos += len(self.comment_string + " ") + cursor.setPosition(end_pos) + cursor.movePosition(QTextCursor.EndOfBlock) + if cursor.atEnd(): + cursor.insertText(self.get_line_separator()) + else: + cursor.movePosition(QTextCursor.NextBlock) + cursor.insertText(comline) + cursor.setPosition(start_pos) + cursor.movePosition(QTextCursor.StartOfBlock) + cursor.insertText(comline) + cursor.endEditBlock() + + def unblockcomment(self): + """Un-block comment current line or selection""" + def __is_comment_bar(cursor): + return to_text_string(cursor.block().text() + ).startswith(self.__blockcomment_bar()) + # Finding first comment bar + cursor1 = self.textCursor() + if __is_comment_bar(cursor1): + return + while not __is_comment_bar(cursor1): + cursor1.movePosition(QTextCursor.PreviousBlock) + if cursor1.atStart(): + break + if not __is_comment_bar(cursor1): + return + def __in_block_comment(cursor): + cs = self.comment_string + return to_text_string(cursor.block().text()).startswith(cs) + # Finding second comment bar + cursor2 = QTextCursor(cursor1) + cursor2.movePosition(QTextCursor.NextBlock) + while not __is_comment_bar(cursor2) and __in_block_comment(cursor2): + cursor2.movePosition(QTextCursor.NextBlock) + if cursor2.block() == self.document().lastBlock(): + break + if not __is_comment_bar(cursor2): + return + # Removing block comment + cursor3 = self.textCursor() + cursor3.beginEditBlock() + cursor3.setPosition(cursor1.position()) + cursor3.movePosition(QTextCursor.NextBlock) + while cursor3.position() < cursor2.position(): + cursor3.setPosition(cursor3.position()+2, QTextCursor.KeepAnchor) + cursor3.removeSelectedText() + cursor3.movePosition(QTextCursor.NextBlock) + for cursor in (cursor2, cursor1): + cursor3.setPosition(cursor.position()) + cursor3.select(QTextCursor.BlockUnderCursor) + cursor3.removeSelectedText() + cursor3.endEditBlock() + + #------Kill ring handlers + # Taken from Jupyter's QtConsole + # Copyright (c) 2001-2015, IPython Development Team + # Copyright (c) 2015-, Jupyter Development Team + def kill_line_end(self): + """Kill the text on the current line from the cursor forward""" + cursor = self.textCursor() + cursor.clearSelection() + cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) + if not cursor.hasSelection(): + # Line deletion + cursor.movePosition(QTextCursor.NextBlock, + QTextCursor.KeepAnchor) + self._kill_ring.kill_cursor(cursor) + self.setTextCursor(cursor) + + def kill_line_start(self): + """Kill the text on the current line from the cursor backward""" + cursor = self.textCursor() + cursor.clearSelection() + cursor.movePosition(QTextCursor.StartOfBlock, + QTextCursor.KeepAnchor) + self._kill_ring.kill_cursor(cursor) + self.setTextCursor(cursor) + + def _get_word_start_cursor(self, position): + """Find the start of the word to the left of the given position. If a + sequence of non-word characters precedes the first word, skip over + them. (This emulates the behavior of bash, emacs, etc.) + """ + document = self.document() + position -= 1 + while (position and not + is_letter_or_number(document.characterAt(position))): + position -= 1 + while position and is_letter_or_number(document.characterAt(position)): + position -= 1 + cursor = self.textCursor() + cursor.setPosition(position + 1) + return cursor + + def _get_word_end_cursor(self, position): + """Find the end of the word to the right of the given position. If a + sequence of non-word characters precedes the first word, skip over + them. (This emulates the behavior of bash, emacs, etc.) + """ + document = self.document() + cursor = self.textCursor() + position = cursor.position() + cursor.movePosition(QTextCursor.End) + end = cursor.position() + while (position < end and + not is_letter_or_number(document.characterAt(position))): + position += 1 + while (position < end and + is_letter_or_number(document.characterAt(position))): + position += 1 + cursor.setPosition(position) + return cursor + + def kill_prev_word(self): + """Kill the previous word""" + position = self.textCursor().position() + cursor = self._get_word_start_cursor(position) + cursor.setPosition(position, QTextCursor.KeepAnchor) + self._kill_ring.kill_cursor(cursor) + self.setTextCursor(cursor) + + def kill_next_word(self): + """Kill the next word""" + position = self.textCursor().position() + cursor = self._get_word_end_cursor(position) + cursor.setPosition(position, QTextCursor.KeepAnchor) + self._kill_ring.kill_cursor(cursor) + self.setTextCursor(cursor) + + #------Autoinsertion of quotes/colons + def __get_current_color(self): + """Get the syntax highlighting color for the current cursor position""" + cursor = self.textCursor() + block = cursor.block() + pos = cursor.position() - block.position() # relative pos within block + layout = block.layout() + block_formats = layout.additionalFormats() + + if block_formats: + # To easily grab current format for autoinsert_colons + if cursor.atBlockEnd(): + current_format = block_formats[-1].format + else: + current_format = None + for fmt in block_formats: + if (pos >= fmt.start) and (pos < fmt.start + fmt.length): + current_format = fmt.format + color = current_format.foreground().color().name() + return color + else: + return None + + def in_comment_or_string(self): + """Is the cursor inside or next to a comment or string?""" + if self.highlighter: + current_color = self.__get_current_color() + comment_color = self.highlighter.get_color_name('comment') + string_color = self.highlighter.get_color_name('string') + if (current_color == comment_color) or (current_color == string_color): + return True + else: + return False + else: + return False + + def __colon_keyword(self, text): + stmt_kws = ['def', 'for', 'if', 'while', 'with', 'class', 'elif', + 'except'] + whole_kws = ['else', 'try', 'except', 'finally'] + text = text.lstrip() + words = text.split() + if any([text == wk for wk in whole_kws]): + return True + elif len(words) < 2: + return False + elif any([words[0] == sk for sk in stmt_kws]): + return True + else: + return False + + def __forbidden_colon_end_char(self, text): + end_chars = [':', '\\', '[', '{', '(', ','] + text = text.rstrip() + if any([text.endswith(c) for c in end_chars]): + return True + else: + return False + + def __unmatched_braces_in_line(self, text): + block = self.textCursor().block() + line_pos = block.position() + for pos, char in enumerate(text): + if char in ['(', '[', '{']: + match = self.find_brace_match(line_pos+pos, char, forward=True) + if (match is None) or (match > line_pos+len(text)): + return True + if char in [')', ']', '}']: + match = self.find_brace_match(line_pos+pos, char, forward=False) + if (match is None) or (match < line_pos): + return True + return False + + def __has_colon_not_in_brackets(self, text): + """ + Return whether a string has a colon which is not between brackets. + This function returns True if the given string has a colon which is + not between a pair of (round, square or curly) brackets. It assumes + that the brackets in the string are balanced. + """ + for pos, char in enumerate(text): + if char == ':' and not self.__unmatched_braces_in_line(text[:pos]): + return True + return False + + def autoinsert_colons(self): + """Decide if we want to autoinsert colons""" + line_text = self.get_text('sol', 'cursor') + if not self.textCursor().atBlockEnd(): + return False + elif self.in_comment_or_string(): + return False + elif not self.__colon_keyword(line_text): + return False + elif self.__forbidden_colon_end_char(line_text): + return False + elif self.__unmatched_braces_in_line(line_text): + return False + elif self.__has_colon_not_in_brackets(line_text): + return False + else: + return True + + def __unmatched_quotes_in_line(self, text): + """Return whether a string has open quotes. + This simply counts whether the number of quote characters of either + type in the string is odd. + + Take from the IPython project (in IPython/core/completer.py in v0.13) + Spyder team: Add some changes to deal with escaped quotes + + - Copyright (C) 2008-2011 IPython Development Team + - Copyright (C) 2001-2007 Fernando Perez. + - Copyright (C) 2001 Python Software Foundation, www.python.org + + Distributed under the terms of the BSD License. + """ + # We check " first, then ', so complex cases with nested quotes will + # get the " to take precedence. + text = text.replace("\\'", "") + text = text.replace('\\"', '') + if text.count('"') % 2: + return '"' + elif text.count("'") % 2: + return "'" + else: + return '' + + def __next_char(self): + cursor = self.textCursor() + cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor) + next_char = to_text_string(cursor.selectedText()) + return next_char + + def __in_comment(self): + if self.highlighter: + current_color = self.__get_current_color() + comment_color = self.highlighter.get_color_name('comment') + if current_color == comment_color: + return True + else: + return False + else: + return False + + def autoinsert_quotes(self, key): + """Control how to automatically insert quotes in various situations""" + char = {Qt.Key_QuoteDbl: '"', Qt.Key_Apostrophe: '\''}[key] + + line_text = self.get_text('sol', 'eol') + line_to_cursor = self.get_text('sol', 'cursor') + cursor = self.textCursor() + last_three = self.get_text('sol', 'cursor')[-3:] + last_two = self.get_text('sol', 'cursor')[-2:] + trailing_text = self.get_text('cursor', 'eol').strip() + + if self.has_selected_text(): + text = ''.join([char, self.get_selected_text(), char]) + self.insert_text(text) + elif self.__in_comment(): + self.insert_text(char) + elif len(trailing_text) > 0 and not \ + self.__unmatched_quotes_in_line(line_to_cursor) == char: + self.insert_text(char) + elif self.__unmatched_quotes_in_line(line_text) and \ + (not last_three == 3*char): + self.insert_text(char) + # Move to the right if we are before a quote + elif self.__next_char() == char: + cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor, 1) + cursor.clearSelection() + self.setTextCursor(cursor) + # Automatic insertion of triple double quotes (for docstrings) + elif last_three == 3*char: + self.insert_text(3*char) + cursor = self.textCursor() + cursor.movePosition(QTextCursor.PreviousCharacter, + QTextCursor.KeepAnchor, 3) + cursor.clearSelection() + self.setTextCursor(cursor) + # If last two chars are quotes, just insert one more because most + # probably the user wants to write a docstring + elif last_two == 2*char: + self.insert_text(char) + # Automatic insertion of quotes + else: + self.insert_text(2*char) + cursor = self.textCursor() + cursor.movePosition(QTextCursor.PreviousCharacter) + self.setTextCursor(cursor) + +#=============================================================================== +# Qt Event handlers +#=============================================================================== + def setup_context_menu(self): + """Setup context menu""" + self.undo_action = create_action( + self, _("Undo"), icon=ima.icon('undo'), + shortcut=get_shortcut('editor', 'undo'), triggered=self.undo) + self.redo_action = create_action( + self, _("Redo"), icon=ima.icon('redo'), + shortcut=get_shortcut('editor', 'redo'), triggered=self.redo) + self.cut_action = create_action( + self, _("Cut"), icon=ima.icon('editcut'), + shortcut=get_shortcut('editor', 'cut'), triggered=self.cut) + self.copy_action = create_action( + self, _("Copy"), icon=ima.icon('editcopy'), + shortcut=get_shortcut('editor', 'copy'), triggered=self.copy) + self.paste_action = create_action( + self, _("Paste"), icon=ima.icon('editpaste'), + shortcut=get_shortcut('editor', 'paste'), triggered=self.paste) + selectall_action = create_action( + self, _("Select All"), icon=ima.icon('selectall'), + shortcut=get_shortcut('editor', 'select all'), + triggered=self.selectAll) + toggle_comment_action = create_action( + self, _("Comment")+"/"+_("Uncomment"), icon=ima.icon('comment'), + shortcut=get_shortcut('editor', 'toggle comment'), + triggered=self.toggle_comment) + self.clear_all_output_action = create_action( + self, _("Clear all ouput"), icon=ima.icon('ipython_console'), + triggered=self.clear_all_output) + self.ipynb_convert_action = create_action( + self, _("Convert to Python script"), icon=ima.icon('python'), + triggered=self.convert_notebook) + self.gotodef_action = create_action( + self, _("Go to definition"), + shortcut=get_shortcut('editor', 'go to definition'), + triggered=self.go_to_definition_from_cursor) + + # Run actions + self.run_cell_action = create_action( + self, _("Run cell"), icon=ima.icon('run_cell'), + shortcut=QKeySequence(RUN_CELL_SHORTCUT), + triggered=self.run_cell.emit) + self.run_cell_and_advance_action = create_action( + self, _("Run cell and advance"), icon=ima.icon('run_cell'), + shortcut=QKeySequence(RUN_CELL_AND_ADVANCE_SHORTCUT), + triggered=self.run_cell_and_advance.emit) + self.run_selection_action = create_action( + self, _("Run &selection or current line"), + icon=ima.icon('run_selection'), + shortcut=get_shortcut('editor', 'run selection'), + triggered=self.run_selection.emit) + + # Zoom actions + zoom_in_action = create_action( + self, _("Zoom in"), icon=ima.icon('zoom_in'), + shortcut=QKeySequence(QKeySequence.ZoomIn), + triggered=self.zoom_in.emit) + zoom_out_action = create_action( + self, _("Zoom out"), icon=ima.icon('zoom_out'), + shortcut=QKeySequence(QKeySequence.ZoomOut), + triggered=self.zoom_out.emit) + zoom_reset_action = create_action( + self, _("Zoom reset"), shortcut=QKeySequence("Ctrl+0"), + triggered=self.zoom_reset.emit) + + # Build menu + self.menu = QMenu(self) + actions_1 = [self.run_cell_action, self.run_cell_and_advance_action, + self.run_selection_action, self.gotodef_action, None, + self.undo_action, self.redo_action, None, self.cut_action, + self.copy_action, self.paste_action, selectall_action] + actions_2 = [None, zoom_in_action, zoom_out_action, zoom_reset_action, + None, toggle_comment_action] + if nbformat is not None: + nb_actions = [self.clear_all_output_action, + self.ipynb_convert_action, None] + actions = actions_1 + nb_actions + actions_2 + add_actions(self.menu, actions) + else: + actions = actions_1 + actions_2 + add_actions(self.menu, actions) + + # Read-only context-menu + self.readonly_menu = QMenu(self) + add_actions(self.readonly_menu, + (self.copy_action, None, selectall_action, + self.gotodef_action)) + + def keyPressEvent(self, event): + """Reimplement Qt method""" + key = event.key() + ctrl = event.modifiers() & Qt.ControlModifier + shift = event.modifiers() & Qt.ShiftModifier + text = to_text_string(event.text()) + if text: + self.__clear_occurrences() + if QToolTip.isVisible(): + self.hide_tooltip_if_necessary(key) + + # Handle the Qt Builtin key sequences + checks = [('SelectAll', 'Select All'), + ('Copy', 'Copy'), + ('Cut', 'Cut'), + ('Paste', 'Paste')] + + for qname, name in checks: + seq = getattr(QKeySequence, qname) + sc = get_shortcut('editor', name) + default = QKeySequence(seq).toString() + if event == seq and sc != default: + # if we have overridden it, call our action + for shortcut in self.shortcuts: + qsc, name, keystr = shortcut.data + if keystr == default: + qsc.activated.emit() + event.ignore() + return + + # otherwise, pass it on to parent + event.ignore() + return + + if key in (Qt.Key_Enter, Qt.Key_Return): + if not shift and not ctrl: + if self.add_colons_enabled and self.is_python_like() and \ + self.autoinsert_colons(): + self.textCursor().beginEditBlock() + self.insert_text(':' + self.get_line_separator()) + self.fix_indent() + self.textCursor().endEditBlock() + elif self.is_completion_widget_visible() \ + and self.codecompletion_enter: + self.select_completion_list() + else: + cmt_or_str = self.in_comment_or_string() + self.textCursor().beginEditBlock() + TextEditBaseWidget.keyPressEvent(self, event) + self.fix_indent(comment_or_string=cmt_or_str) + self.textCursor().endEditBlock() + elif shift: + self.run_cell_and_advance.emit() + elif ctrl: + self.run_cell.emit() + elif key == Qt.Key_Insert and not shift and not ctrl: + self.setOverwriteMode(not self.overwriteMode()) + elif key == Qt.Key_Backspace and not shift and not ctrl: + leading_text = self.get_text('sol', 'cursor') + leading_length = len(leading_text) + trailing_spaces = leading_length-len(leading_text.rstrip()) + if self.has_selected_text() or not self.intelligent_backspace: + TextEditBaseWidget.keyPressEvent(self, event) + else: + trailing_text = self.get_text('cursor', 'eol') + if not leading_text.strip() \ + and leading_length > len(self.indent_chars): + if leading_length % len(self.indent_chars) == 0: + self.unindent() + else: + TextEditBaseWidget.keyPressEvent(self, event) + elif trailing_spaces and not trailing_text.strip(): + self.remove_suffix(leading_text[-trailing_spaces:]) + elif leading_text and trailing_text and \ + leading_text[-1]+trailing_text[0] in ('()', '[]', '{}', + '\'\'', '""'): + cursor = self.textCursor() + cursor.movePosition(QTextCursor.PreviousCharacter) + cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor, 2) + cursor.removeSelectedText() + else: + TextEditBaseWidget.keyPressEvent(self, event) + if self.is_completion_widget_visible(): + self.completion_text = self.completion_text[:-1] + elif key == Qt.Key_Period: + self.insert_text(text) + if (self.is_python_like()) and not \ + self.in_comment_or_string() and self.codecompletion_auto: + # Enable auto-completion only if last token isn't a float + last_obj = getobj(self.get_text('sol', 'cursor')) + if last_obj and not last_obj.isdigit(): + self.do_completion(automatic=True) + elif key == Qt.Key_Home: + self.stdkey_home(shift, ctrl) + elif key == Qt.Key_End: + # See Issue 495: on MacOS X, it is necessary to redefine this + # basic action which should have been implemented natively + self.stdkey_end(shift, ctrl) + elif text == '(' and not self.has_selected_text(): + self.hide_completion_widget() + if self.close_parentheses_enabled: + self.handle_close_parentheses(text) + else: + self.insert_text(text) + elif text in ('[', '{') and not self.has_selected_text() \ + and self.close_parentheses_enabled: + s_trailing_text = self.get_text('cursor', 'eol').strip() + if len(s_trailing_text) == 0 or \ + s_trailing_text[0] in (',', ')', ']', '}'): + self.insert_text({'{': '{}', '[': '[]'}[text]) + cursor = self.textCursor() + cursor.movePosition(QTextCursor.PreviousCharacter) + self.setTextCursor(cursor) + else: + TextEditBaseWidget.keyPressEvent(self, event) + elif key in (Qt.Key_QuoteDbl, Qt.Key_Apostrophe) and \ + self.close_quotes_enabled: + self.autoinsert_quotes(key) + elif key in (Qt.Key_ParenRight, Qt.Key_BraceRight, Qt.Key_BracketRight)\ + and not self.has_selected_text() and self.close_parentheses_enabled \ + and not self.textCursor().atBlockEnd(): + cursor = self.textCursor() + cursor.movePosition(QTextCursor.NextCharacter, + QTextCursor.KeepAnchor) + text = to_text_string(cursor.selectedText()) + if text == {Qt.Key_ParenRight: ')', Qt.Key_BraceRight: '}', + Qt.Key_BracketRight: ']'}[key]: + cursor.clearSelection() + self.setTextCursor(cursor) + else: + TextEditBaseWidget.keyPressEvent(self, event) + elif key == Qt.Key_Colon and not self.has_selected_text() \ + and self.auto_unindent_enabled: + leading_text = self.get_text('sol', 'cursor') + if leading_text.lstrip() in ('else', 'finally'): + ind = lambda txt: len(txt)-len(txt.lstrip()) + prevtxt = to_text_string(self.textCursor( + ).block().previous().text()) + if ind(leading_text) == ind(prevtxt): + self.unindent(force=True) + TextEditBaseWidget.keyPressEvent(self, event) + elif key == Qt.Key_Space and not shift and not ctrl \ + and not self.has_selected_text() and self.auto_unindent_enabled: + leading_text = self.get_text('sol', 'cursor') + if leading_text.lstrip() in ('elif', 'except'): + ind = lambda txt: len(txt)-len(txt.lstrip()) + prevtxt = to_text_string(self.textCursor( + ).block().previous().text()) + if ind(leading_text) == ind(prevtxt): + self.unindent(force=True) + TextEditBaseWidget.keyPressEvent(self, event) + elif key == Qt.Key_Tab: + # Important note: can't be called with a QShortcut because + # of its singular role with respect to widget focus management + if not self.has_selected_text() and not self.tab_mode: + self.intelligent_tab() + else: + # indent the selected text + self.indent_or_replace() + elif key == Qt.Key_Backtab: + # Backtab, i.e. Shift+, could be treated as a QShortcut but + # there is no point since can't (see above) + if not self.has_selected_text() and not self.tab_mode: + self.intelligent_backtab() + else: + # indent the selected text + self.unindent() + else: + TextEditBaseWidget.keyPressEvent(self, event) + if self.is_completion_widget_visible() and text: + self.completion_text += text + + def handle_close_parentheses(self, text): + if not self.close_parentheses_enabled: + return + position = self.get_position('cursor') + rest = self.get_text('cursor', 'eol').rstrip() + if not rest or rest[0] in (',', ')', ']', '}'): + self.insert_text('()') + cursor = self.textCursor() + cursor.movePosition(QTextCursor.PreviousCharacter) + self.setTextCursor(cursor) + else: + self.insert_text(text) + if self.is_python_like() and self.get_text('sol', 'cursor') and \ + self.calltips: + self.sig_show_object_info.emit(position) + + def mouseMoveEvent(self, event): + """Underline words when pressing """ + if self.has_selected_text(): + TextEditBaseWidget.mouseMoveEvent(self, event) + return + if self.go_to_definition_enabled and \ + event.modifiers() & Qt.ControlModifier: + text = self.get_word_at(event.pos()) + if text and (self.is_python_like())\ + and not sourcecode.is_keyword(to_text_string(text)): + if not self.__cursor_changed: + QApplication.setOverrideCursor( + QCursor(Qt.PointingHandCursor)) + self.__cursor_changed = True + cursor = self.cursorForPosition(event.pos()) + cursor.select(QTextCursor.WordUnderCursor) + self.clear_extra_selections('ctrl_click') + self.__highlight_selection('ctrl_click', cursor, update=True, + foreground_color=self.ctrl_click_color, + underline_color=self.ctrl_click_color, + underline_style=QTextCharFormat.SingleUnderline) + event.accept() + return + if self.__cursor_changed: + QApplication.restoreOverrideCursor() + self.__cursor_changed = False + self.clear_extra_selections('ctrl_click') + TextEditBaseWidget.mouseMoveEvent(self, event) + + def leaveEvent(self, event): + """If cursor has not been restored yet, do it now""" + if self.__cursor_changed: + QApplication.restoreOverrideCursor() + self.__cursor_changed = False + self.clear_extra_selections('ctrl_click') + TextEditBaseWidget.leaveEvent(self, event) + + @Slot() + def go_to_definition_from_cursor(self, cursor=None): + """Go to definition from cursor instance (QTextCursor)""" + if not self.go_to_definition_enabled: + return + if cursor is None: + cursor = self.textCursor() + if self.in_comment_or_string(): + return + position = cursor.position() + text = to_text_string(cursor.selectedText()) + if len(text) == 0: + cursor.select(QTextCursor.WordUnderCursor) + text = to_text_string(cursor.selectedText()) + if not text is None: + self.go_to_definition.emit(position) + + def mousePressEvent(self, event): + """Reimplement Qt method""" + if event.button() == Qt.LeftButton\ + and (event.modifiers() & Qt.ControlModifier): + TextEditBaseWidget.mousePressEvent(self, event) + cursor = self.cursorForPosition(event.pos()) + self.go_to_definition_from_cursor(cursor) + else: + TextEditBaseWidget.mousePressEvent(self, event) + + def contextMenuEvent(self, event): + """Reimplement Qt method""" + nonempty_selection = self.has_selected_text() + self.copy_action.setEnabled(nonempty_selection) + self.cut_action.setEnabled(nonempty_selection) + self.clear_all_output_action.setVisible(self.is_json() and \ + nbformat is not None) + self.ipynb_convert_action.setVisible(self.is_json() and \ + nbformat is not None) + self.run_cell_action.setVisible(self.is_python()) + self.run_cell_and_advance_action.setVisible(self.is_python()) + self.run_selection_action.setVisible(self.is_python()) + self.gotodef_action.setVisible(self.go_to_definition_enabled \ + and self.is_python_like()) + + # Code duplication go_to_definition_from_cursor and mouse_move_event + cursor = self.textCursor() + text = to_text_string(cursor.selectedText()) + if len(text) == 0: + cursor.select(QTextCursor.WordUnderCursor) + text = to_text_string(cursor.selectedText()) + + self.undo_action.setEnabled( self.document().isUndoAvailable()) + self.redo_action.setEnabled( self.document().isRedoAvailable()) + menu = self.menu + if self.isReadOnly(): + menu = self.readonly_menu + menu.popup(event.globalPos()) + event.accept() + + #------ Drag and drop + def dragEnterEvent(self, event): + """Reimplement Qt method + Inform Qt about the types of data that the widget accepts""" + if mimedata2url(event.mimeData()): + # Let the parent widget handle this + event.ignore() + else: + TextEditBaseWidget.dragEnterEvent(self, event) + + def dropEvent(self, event): + """Reimplement Qt method + Unpack dropped data and handle it""" + if mimedata2url(event.mimeData()): + # Let the parent widget handle this + event.ignore() + else: + TextEditBaseWidget.dropEvent(self, event) + + #------ Paint event + def paintEvent(self, event): + """Overrides paint event to update the list of visible blocks""" + self.update_visible_blocks(event) + TextEditBaseWidget.paintEvent(self, event) + self.painted.emit(event) + + def update_visible_blocks(self, event): + """Update the list of visible blocks/lines position""" + self.__visible_blocks[:] = [] + block = self.firstVisibleBlock() + blockNumber = block.blockNumber() + top = int(self.blockBoundingGeometry(block).translated( + self.contentOffset()).top()) + bottom = top + int(self.blockBoundingRect(block).height()) + ebottom_top = 0 + ebottom_bottom = self.height() + + while block.isValid(): + visible = (top >= ebottom_top and bottom <= ebottom_bottom) + if not visible: + break + if block.isVisible(): + self.__visible_blocks.append((top, blockNumber+1, block)) + block = block.next() + top = bottom + bottom = top + int(self.blockBoundingRect(block).height()) + blockNumber = block.blockNumber() + + def _draw_editor_cell_divider(self): + """Draw a line on top of a define cell""" + if self.supported_cell_language: + cell_line_color = self.comment_color + painter = QPainter(self.viewport()) + pen = painter.pen() + pen.setStyle(Qt.SolidLine) + pen.setBrush(cell_line_color) + painter.setPen(pen) + + for top, line_number, block in self.visible_blocks: + if self.is_cell_separator(block): + painter.drawLine(4, top, self.width(), top) + + @property + def visible_blocks(self): + """ + Returns the list of visible blocks. + + Each element in the list is a tuple made up of the line top position, + the line number (already 1 based), and the QTextBlock itself. + + :return: A list of tuple(top position, line number, block) + :rtype: List of tuple(int, int, QtGui.QTextBlock) + """ + return self.__visible_blocks + + def is_editor(self): + return True + + +#=============================================================================== +# CodeEditor's Printer +#=============================================================================== + +#TODO: Implement the header and footer support +class Printer(QPrinter): + def __init__(self, mode=QPrinter.ScreenResolution, header_font=None): + QPrinter.__init__(self, mode) + self.setColorMode(QPrinter.Color) + self.setPageOrder(QPrinter.FirstPageFirst) + self.date = time.ctime() + if header_font is not None: + self.header_font = header_font + + # The following method is simply ignored by QPlainTextEdit + # (this is a copy from QsciEditor's Printer) + def formatPage(self, painter, drawing, area, pagenr): + header = '%s - %s - Page %s' % (self.docName(), self.date, pagenr) + painter.save() + painter.setFont(self.header_font) + painter.setPen(QColor(Qt.black)) + if drawing: + painter.drawText(area.right()-painter.fontMetrics().width(header), + area.top()+painter.fontMetrics().ascent(), header) + area.setTop(area.top()+painter.fontMetrics().height()+5) + painter.restore() + + +#=============================================================================== +# Editor + Class browser test +#=============================================================================== +class TestWidget(QSplitter): + def __init__(self, parent): + QSplitter.__init__(self, parent) + self.editor = CodeEditor(self) + self.editor.setup_editor(linenumbers=True, markers=True, tab_mode=False, + font=QFont("Courier New", 10), + show_blanks=True, color_scheme='Zenburn') + self.addWidget(self.editor) + from spyder.widgets.editortools import OutlineExplorerWidget + self.classtree = OutlineExplorerWidget(self) + self.addWidget(self.classtree) + self.classtree.edit_goto.connect( + lambda _fn, line, word: self.editor.go_to_line(line, word)) + self.setStretchFactor(0, 4) + self.setStretchFactor(1, 1) + self.setWindowIcon(ima.icon('spyder')) + + def load(self, filename): + self.editor.set_text_from_file(filename) + self.setWindowTitle("%s - %s (%s)" % (_("Editor"), + osp.basename(filename), + osp.dirname(filename))) + self.classtree.set_current_editor(self.editor, filename, False, False) + + +def test(fname): + from spyder.utils.qthelpers import qapplication + app = qapplication(test_time=5) + win = TestWidget(None) + win.show() + win.load(fname) + win.resize(900, 700) + + from spyder.utils.codeanalysis import (check_with_pyflakes, + check_with_pep8) + source_code = to_text_string(win.editor.toPlainText()) + results = check_with_pyflakes(source_code, fname) + \ + check_with_pep8(source_code, fname) + win.editor.process_code_analysis(results) + + sys.exit(app.exec_()) + + +if __name__ == '__main__': + if len(sys.argv) > 1: + fname = sys.argv[1] + else: + fname = __file__ + test(fname) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/__init__.py spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/__init__.py --- spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.widgets.sourcecode +========================= + +Source code related widgets (code editor, console) based exclusively on Qt +""" diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/kill_ring.py spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/kill_ring.py --- spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/kill_ring.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/kill_ring.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,132 @@ +""" +A generic Emacs-style kill ring, as well as a Qt-specific version. +Copyright (c) 2001-2015, IPython Development Team +Copyright (c) 2015-, Jupyter Development Team +All rights reserved. +""" + +# Third party imports +from qtpy.QtCore import QObject +from qtpy.QtGui import QTextCursor +from qtpy.QtWidgets import QTextEdit, QPlainTextEdit + + +# ---------------------------------------------------------------------------- +# Classes +# ---------------------------------------------------------------------------- +class KillRing(object): + """ A generic Emacs-style kill ring. + """ + + def __init__(self): + self.clear() + + def clear(self): + """ Clears the kill ring. + """ + self._index = -1 + self._ring = [] + + def kill(self, text): + """ Adds some killed text to the ring. + """ + self._ring.append(text) + + def yank(self): + """ Yank back the most recently killed text. + + Returns + ------- + A text string or None. + """ + self._index = len(self._ring) + return self.rotate() + + def rotate(self): + """ Rotate the kill ring, then yank back the new top. + + Returns + ------- + A text string or None. + """ + self._index -= 1 + if self._index >= 0: + return self._ring[self._index] + return None + + +class QtKillRing(QObject): + """ A kill ring attached to Q[Plain]TextEdit. + """ + + # ------------------------------------------------------------------------- + # QtKillRing interface + # ------------------------------------------------------------------------- + + def __init__(self, text_edit): + """ Create a kill ring attached to the specified Qt text edit. + """ + assert isinstance(text_edit, (QTextEdit, QPlainTextEdit)) + super(QtKillRing, self).__init__() + + self._ring = KillRing() + self._prev_yank = None + self._skip_cursor = False + self._text_edit = text_edit + + text_edit.cursorPositionChanged.connect(self._cursor_position_changed) + + def clear(self): + """ Clears the kill ring. + """ + self._ring.clear() + self._prev_yank = None + + def kill(self, text): + """ Adds some killed text to the ring. + """ + self._ring.kill(text) + + def kill_cursor(self, cursor): + """ Kills the text selected by the give cursor. + """ + text = cursor.selectedText() + if text: + cursor.removeSelectedText() + self.kill(text) + + def yank(self): + """ Yank back the most recently killed text. + """ + text = self._ring.yank() + if text: + self._skip_cursor = True + cursor = self._text_edit.textCursor() + cursor.insertText(text) + self._prev_yank = text + + def rotate(self): + """ Rotate the kill ring, then yank back the new top. + """ + if self._prev_yank: + text = self._ring.rotate() + if text: + self._skip_cursor = True + cursor = self._text_edit.textCursor() + cursor.movePosition(QTextCursor.Left, + QTextCursor.KeepAnchor, + n=len(self._prev_yank)) + cursor.insertText(text) + self._prev_yank = text + + # ------------------------------------------------------------------------- + # Protected interface + # ------------------------------------------------------------------------- + + # ----- Signal handlers --------------------------------------------------- + + def _cursor_position_changed(self): + if self._skip_cursor: + self._skip_cursor = False + else: + self._prev_yank = None diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/terminal.py spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/terminal.py --- spyder-2.3.8+dfsg1/spyder/widgets/sourcecode/terminal.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/sourcecode/terminal.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Terminal emulation tools""" + +import os + +class ANSIEscapeCodeHandler(object): + """ANSI Escape sequences handler""" + if os.name == 'nt': + # Windows terminal colors: + ANSI_COLORS = ( # Normal, Bright/Light + ('#000000', '#808080'), # 0: black + ('#800000', '#ff0000'), # 1: red + ('#008000', '#00ff00'), # 2: green + ('#808000', '#ffff00'), # 3: yellow + ('#000080', '#0000ff'), # 4: blue + ('#800080', '#ff00ff'), # 5: magenta + ('#008080', '#00ffff'), # 6: cyan + ('#c0c0c0', '#ffffff'), # 7: white + ) + elif os.name == 'mac': + # Terminal.app colors: + ANSI_COLORS = ( # Normal, Bright/Light + ('#000000', '#818383'), # 0: black + ('#C23621', '#FC391F'), # 1: red + ('#25BC24', '#25BC24'), # 2: green + ('#ADAD27', '#EAEC23'), # 3: yellow + ('#492EE1', '#5833FF'), # 4: blue + ('#D338D3', '#F935F8'), # 5: magenta + ('#33BBC8', '#14F0F0'), # 6: cyan + ('#CBCCCD', '#E9EBEB'), # 7: white + ) + else: + # xterm colors: + ANSI_COLORS = ( # Normal, Bright/Light + ('#000000', '#7F7F7F'), # 0: black + ('#CD0000', '#ff0000'), # 1: red + ('#00CD00', '#00ff00'), # 2: green + ('#CDCD00', '#ffff00'), # 3: yellow + ('#0000EE', '#5C5CFF'), # 4: blue + ('#CD00CD', '#ff00ff'), # 5: magenta + ('#00CDCD', '#00ffff'), # 6: cyan + ('#E5E5E5', '#ffffff'), # 7: white + ) + def __init__(self): + self.intensity = 0 + self.italic = None + self.bold = None + self.underline = None + self.foreground_color = None + self.background_color = None + self.default_foreground_color = 30 + self.default_background_color = 47 + + def set_code(self, code): + assert isinstance(code, int) + if code == 0: + # Reset all settings + self.reset() + elif code == 1: + # Text color intensity + self.intensity = 1 + # The following line is commented because most terminals won't + # change the font weight, against ANSI standard recommendation: +# self.bold = True + elif code == 3: + # Italic on + self.italic = True + elif code == 4: + # Underline simple + self.underline = True + elif code == 22: + # Normal text color intensity + self.intensity = 0 + self.bold = False + elif code == 23: + # No italic + self.italic = False + elif code == 24: + # No underline + self.underline = False + elif code >= 30 and code <= 37: + # Text color + self.foreground_color = code + elif code == 39: + # Default text color + self.foreground_color = self.default_foreground_color + elif code >= 40 and code <= 47: + # Background color + self.background_color = code + elif code == 49: + # Default background color + self.background_color = self.default_background_color + self.set_style() + + def set_style(self): + """ + Set font style with the following attributes: + 'foreground_color', 'background_color', 'italic', + 'bold' and 'underline' + """ + raise NotImplementedError + + def reset(self): + self.current_format = None + self.intensity = 0 + self.italic = False + self.bold = False + self.underline = False + self.foreground_color = None + self.background_color = None diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/status.py spyder-3.0.2+dfsg1/spyder/widgets/status.py --- spyder-2.3.8+dfsg1/spyder/widgets/status.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/status.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Status bar widgets""" + +# Standard library imports +import os + +# Third party imports +from qtpy.QtCore import QTimer +from qtpy.QtWidgets import QHBoxLayout, QLabel, QWidget + +# Local imports +from spyder import dependencies +from spyder.config.base import _ +from spyder.config.gui import get_font +from spyder.py3compat import to_text_string + + +if not os.name == 'nt': + PSUTIL_REQVER = '>=0.3' + dependencies.add("psutil", _("CPU and memory usage info in the status bar"), + required_version=PSUTIL_REQVER) + + +class StatusBarWidget(QWidget): + def __init__(self, parent, statusbar): + QWidget.__init__(self, parent) + + self.label_font = font = get_font(option='rich_font') + font.setPointSize(self.font().pointSize()) + font.setBold(True) + + layout = QHBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(layout) + + statusbar.addPermanentWidget(self) + + +#============================================================================== +# Main window-related status bar widgets +#============================================================================== +class BaseTimerStatus(StatusBarWidget): + TITLE = None + TIP = None + def __init__(self, parent, statusbar): + StatusBarWidget.__init__(self, parent, statusbar) + self.setToolTip(self.TIP) + layout = self.layout() + layout.addWidget(QLabel(self.TITLE)) + self.label = QLabel() + self.label.setFont(self.label_font) + layout.addWidget(self.label) + layout.addSpacing(20) + if self.is_supported(): + self.timer = QTimer() + self.timer.timeout.connect(self.update_label) + self.timer.start(2000) + else: + self.timer = None + self.hide() + + def set_interval(self, interval): + """Set timer interval (ms)""" + if self.timer is not None: + self.timer.setInterval(interval) + + def import_test(self): + """Raise ImportError if feature is not supported""" + raise NotImplementedError + + def is_supported(self): + """Return True if feature is supported""" + try: + self.import_test() + return True + except ImportError: + return False + + def get_value(self): + """Return value (e.g. CPU or memory usage)""" + raise NotImplementedError + + def update_label(self): + """Update status label widget, if widget is visible""" + if self.isVisible(): + self.label.setText('%d %%' % self.get_value()) + +class MemoryStatus(BaseTimerStatus): + TITLE = _("Memory:") + TIP = _("Memory usage status: " + "requires the `psutil` (>=v0.3) library on non-Windows platforms") + def import_test(self): + """Raise ImportError if feature is not supported""" + from spyder.utils.system import memory_usage # analysis:ignore + + def get_value(self): + """Return memory usage""" + from spyder.utils.system import memory_usage + return memory_usage() + +class CPUStatus(BaseTimerStatus): + TITLE = _("CPU:") + TIP = _("CPU usage status: requires the `psutil` (>=v0.3) library") + def import_test(self): + """Raise ImportError if feature is not supported""" + from spyder.utils import programs + if not programs.is_module_installed('psutil', '>=0.2.0'): + # The `interval` argument in `psutil.cpu_percent` function + # was introduced in v0.2.0 + raise ImportError + + def get_value(self): + """Return CPU usage""" + import psutil + return psutil.cpu_percent(interval=0) + + +#============================================================================== +# Editor-related status bar widgets +#============================================================================== +class ReadWriteStatus(StatusBarWidget): + def __init__(self, parent, statusbar): + StatusBarWidget.__init__(self, parent, statusbar) + layout = self.layout() + layout.addWidget(QLabel(_("Permissions:"))) + self.readwrite = QLabel() + self.readwrite.setFont(self.label_font) + layout.addWidget(self.readwrite) + layout.addSpacing(20) + + def readonly_changed(self, readonly): + readwrite = "R" if readonly else "RW" + self.readwrite.setText(readwrite.ljust(3)) + +class EOLStatus(StatusBarWidget): + def __init__(self, parent, statusbar): + StatusBarWidget.__init__(self, parent, statusbar) + layout = self.layout() + layout.addWidget(QLabel(_("End-of-lines:"))) + self.eol = QLabel() + self.eol.setFont(self.label_font) + layout.addWidget(self.eol) + layout.addSpacing(20) + + def eol_changed(self, os_name): + os_name = to_text_string(os_name) + self.eol.setText({"nt": "CRLF", "posix": "LF"}.get(os_name, "CR")) + +class EncodingStatus(StatusBarWidget): + def __init__(self, parent, statusbar): + StatusBarWidget.__init__(self, parent, statusbar) + layout = self.layout() + layout.addWidget(QLabel(_("Encoding:"))) + self.encoding = QLabel() + self.encoding.setFont(self.label_font) + layout.addWidget(self.encoding) + layout.addSpacing(20) + + def encoding_changed(self, encoding): + self.encoding.setText(str(encoding).upper().ljust(15)) + +class CursorPositionStatus(StatusBarWidget): + def __init__(self, parent, statusbar): + StatusBarWidget.__init__(self, parent, statusbar) + layout = self.layout() + layout.addWidget(QLabel(_("Line:"))) + self.line = QLabel() + self.line.setFont(self.label_font) + layout.addWidget(self.line) + layout.addWidget(QLabel(_("Column:"))) + self.column = QLabel() + self.column.setFont(self.label_font) + layout.addWidget(self.column) + self.setLayout(layout) + + def cursor_position_changed(self, line, index): + self.line.setText("%-6d" % (line+1)) + self.column.setText("%-4d" % (index+1)) + + +def test(): + from qtpy.QtWidgets import QMainWindow + from spyder.utils.qthelpers import qapplication + + app = qapplication(test_time=5) + win = QMainWindow() + win.setWindowTitle("Status widgets test") + win.resize(900, 300) + statusbar = win.statusBar() + swidgets = [] + for klass in (ReadWriteStatus, EOLStatus, EncodingStatus, + CursorPositionStatus, MemoryStatus, CPUStatus): + swidget = klass(win, statusbar) + swidgets.append(swidget) + win.show() + app.exec_() + + +if __name__ == "__main__": + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/tabs.py spyder-3.0.2+dfsg1/spyder/widgets/tabs.py --- spyder-2.3.8+dfsg1/spyder/widgets/tabs.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/tabs.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,337 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Tabs widget""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os.path as osp +import sys + +# Third party imports +from qtpy.QtCore import QByteArray, QMimeData, QPoint, Qt, Signal +from qtpy.QtGui import QDrag +from qtpy.QtWidgets import (QApplication, QHBoxLayout, QMenu, QTabBar, + QTabWidget, QWidget) + +# Local imports +from spyder.config.base import _ +from spyder.config.gui import fixed_shortcut +from spyder.py3compat import PY2, to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.misc import get_common_path +from spyder.utils.qthelpers import (add_actions, create_action, + create_toolbutton) + + +class TabBar(QTabBar): + """Tabs base class with drag and drop support""" + sig_move_tab = Signal((int, int), (str, int, int)) + + def __init__(self, parent, ancestor): + QTabBar.__init__(self, parent) + self.ancestor = ancestor + + # To style tabs on Mac + if sys.platform == 'darwin': + self.setObjectName('plugin-tab') + + # Dragging tabs + self.__drag_start_pos = QPoint() + self.setAcceptDrops(True) + self.setUsesScrollButtons(True) + + def mousePressEvent(self, event): + """Reimplement Qt method""" + if event.button() == Qt.LeftButton: + self.__drag_start_pos = QPoint(event.pos()) + QTabBar.mousePressEvent(self, event) + + def mouseMoveEvent(self, event): + """Override Qt method""" + if event.buttons() == Qt.MouseButtons(Qt.LeftButton) and \ + (event.pos() - self.__drag_start_pos).manhattanLength() > \ + QApplication.startDragDistance(): + drag = QDrag(self) + mimeData = QMimeData() + # Converting id's to long to avoid an OverflowError with PySide + if PY2: + ancestor_id = long(id(self.ancestor)) + parent_widget_id = long(id(self.parentWidget())) + self_id = long(id(self)) + else: + ancestor_id = id(self.ancestor) + parent_widget_id = id(self.parentWidget()) + self_id = id(self) + mimeData.setData("parent-id", QByteArray.number(ancestor_id)) + mimeData.setData("tabwidget-id", + QByteArray.number(parent_widget_id)) + mimeData.setData("tabbar-id", QByteArray.number(self_id)) + mimeData.setData("source-index", + QByteArray.number(self.tabAt(self.__drag_start_pos))) + drag.setMimeData(mimeData) + drag.exec_() + QTabBar.mouseMoveEvent(self, event) + + def dragEnterEvent(self, event): + """Override Qt method""" + mimeData = event.mimeData() + formats = list( mimeData.formats() ) + if "parent-id" in formats and \ + mimeData.data("parent-id").toLong()[0] == id(self.ancestor): + event.acceptProposedAction() + QTabBar.dragEnterEvent(self, event) + + def dropEvent(self, event): + """Override Qt method""" + mimeData = event.mimeData() + index_from = mimeData.data("source-index").toInt()[0] + index_to = self.tabAt(event.pos()) + if index_to == -1: + index_to = self.count() + if mimeData.data("tabbar-id").toLong()[0] != id(self): + tabwidget_from = str(mimeData.data("tabwidget-id").toLong()[0]) + + # We pass self object ID as a QString, because otherwise it would + # depend on the platform: long for 64bit, int for 32bit. Replacing + # by long all the time is not working on some 32bit platforms + # (see Issue 1094, Issue 1098) + self.sig_move_tab[(str, int, int)].emit(tabwidget_from, index_from, + index_to) + + event.acceptProposedAction() + elif index_from != index_to: + self.sig_move_tab.emit(index_from, index_to) + event.acceptProposedAction() + QTabBar.dropEvent(self, event) + + +class BaseTabs(QTabWidget): + """TabWidget with context menu and corner widgets""" + sig_close_tab = Signal(int) + + def __init__(self, parent, actions=None, menu=None, + corner_widgets=None, menu_use_tooltips=False): + QTabWidget.__init__(self, parent) + self.setUsesScrollButtons(True) + + # To style tabs on Mac + if sys.platform == 'darwin': + self.setObjectName('plugin-tab') + + self.corner_widgets = {} + self.menu_use_tooltips = menu_use_tooltips + + if menu is None: + self.menu = QMenu(self) + if actions: + add_actions(self.menu, actions) + else: + self.menu = menu + + # Corner widgets + if corner_widgets is None: + corner_widgets = {} + corner_widgets.setdefault(Qt.TopLeftCorner, []) + corner_widgets.setdefault(Qt.TopRightCorner, []) + self.browse_button = create_toolbutton(self, + icon=ima.icon('browse_tab'), + tip=_("Browse tabs")) + self.browse_tabs_menu = QMenu(self) + self.browse_button.setMenu(self.browse_tabs_menu) + self.browse_button.setPopupMode(self.browse_button.InstantPopup) + self.browse_tabs_menu.aboutToShow.connect(self.update_browse_tabs_menu) + corner_widgets[Qt.TopLeftCorner] += [self.browse_button] + + self.set_corner_widgets(corner_widgets) + + def update_browse_tabs_menu(self): + """Update browse tabs menu""" + self.browse_tabs_menu.clear() + names = [] + dirnames = [] + for index in range(self.count()): + if self.menu_use_tooltips: + text = to_text_string(self.tabToolTip(index)) + else: + text = to_text_string(self.tabText(index)) + names.append(text) + if osp.isfile(text): + # Testing if tab names are filenames + dirnames.append(osp.dirname(text)) + offset = None + + # If tab names are all filenames, removing common path: + if len(names) == len(dirnames): + common = get_common_path(dirnames) + if common is None: + offset = None + else: + offset = len(common)+1 + if offset <= 3: + # Common path is not a path but a drive letter... + offset = None + + for index, text in enumerate(names): + tab_action = create_action(self, text[offset:], + icon=self.tabIcon(index), + toggled=lambda state, index=index: + self.setCurrentIndex(index), + tip=self.tabToolTip(index)) + tab_action.setChecked(index == self.currentIndex()) + self.browse_tabs_menu.addAction(tab_action) + + def set_corner_widgets(self, corner_widgets): + """ + Set tabs corner widgets + corner_widgets: dictionary of (corner, widgets) + corner: Qt.TopLeftCorner or Qt.TopRightCorner + widgets: list of widgets (may contains integers to add spacings) + """ + assert isinstance(corner_widgets, dict) + assert all(key in (Qt.TopLeftCorner, Qt.TopRightCorner) + for key in corner_widgets) + self.corner_widgets.update(corner_widgets) + for corner, widgets in list(self.corner_widgets.items()): + cwidget = QWidget() + cwidget.hide() + prev_widget = self.cornerWidget(corner) + if prev_widget: + prev_widget.close() + self.setCornerWidget(cwidget, corner) + clayout = QHBoxLayout() + clayout.setContentsMargins(0, 0, 0, 0) + for widget in widgets: + if isinstance(widget, int): + clayout.addSpacing(widget) + else: + clayout.addWidget(widget) + cwidget.setLayout(clayout) + cwidget.show() + + def add_corner_widgets(self, widgets, corner=Qt.TopRightCorner): + self.set_corner_widgets({corner: + self.corner_widgets.get(corner, [])+widgets}) + + def contextMenuEvent(self, event): + """Override Qt method""" + self.setCurrentIndex(self.tabBar().tabAt(event.pos())) + if self.menu: + self.menu.popup(event.globalPos()) + + def mousePressEvent(self, event): + """Override Qt method""" + if event.button() == Qt.MidButton: + index = self.tabBar().tabAt(event.pos()) + if index >= 0: + self.sig_close_tab.emit(index) + event.accept() + return + QTabWidget.mousePressEvent(self, event) + + def keyPressEvent(self, event): + """Override Qt method""" + ctrl = event.modifiers() & Qt.ControlModifier + key = event.key() + handled = False + if ctrl and self.count() > 0: + index = self.currentIndex() + if key == Qt.Key_PageUp: + if index > 0: + self.setCurrentIndex(index - 1) + else: + self.setCurrentIndex(self.count() - 1) + handled = True + elif key == Qt.Key_PageDown: + if index < self.count() - 1: + self.setCurrentIndex(index + 1) + else: + self.setCurrentIndex(0) + handled = True + if not handled: + QTabWidget.keyPressEvent(self, event) + + def set_close_function(self, func): + """Setting Tabs close function + None -> tabs are not closable""" + state = func is not None + if state: + self.sig_close_tab.connect(func) + try: + # Assuming Qt >= 4.5 + QTabWidget.setTabsClosable(self, state) + self.tabCloseRequested.connect(func) + except AttributeError: + # Workaround for Qt < 4.5 + close_button = create_toolbutton(self, triggered=func, + icon=ima.icon('fileclose'), + tip=_("Close current tab")) + self.setCornerWidget(close_button if state else None) + + +class Tabs(BaseTabs): + """BaseTabs widget with movable tabs and tab navigation shortcuts""" + # Signals + move_data = Signal(int, int) + move_tab_finished = Signal() + sig_move_tab = Signal(str, str, int, int) + + def __init__(self, parent, actions=None, menu=None, + corner_widgets=None, menu_use_tooltips=False): + BaseTabs.__init__(self, parent, actions, menu, + corner_widgets, menu_use_tooltips) + tab_bar = TabBar(self, parent) + tab_bar.sig_move_tab.connect(self.move_tab) + tab_bar.sig_move_tab[(str, int, int)].connect( + self.move_tab_from_another_tabwidget) + self.setTabBar(tab_bar) + + fixed_shortcut("Ctrl+Tab", parent, lambda: self.tab_navigate(1)) + fixed_shortcut("Shift+Ctrl+Tab", parent, lambda: self.tab_navigate(-1)) + fixed_shortcut("Ctrl+W", parent, + lambda: self.sig_close_tab.emit(self.currentIndex())) + fixed_shortcut("Ctrl+F4", parent, + lambda: self.sig_close_tab.emit(self.currentIndex())) + + def tab_navigate(self, delta=1): + """Ctrl+Tab""" + if delta > 0 and self.currentIndex() == self.count()-1: + index = delta-1 + elif delta < 0 and self.currentIndex() == 0: + index = self.count()+delta + else: + index = self.currentIndex()+delta + self.setCurrentIndex(index) + + def move_tab(self, index_from, index_to): + """Move tab inside a tabwidget""" + self.move_data.emit(index_from, index_to) + + tip, text = self.tabToolTip(index_from), self.tabText(index_from) + icon, widget = self.tabIcon(index_from), self.widget(index_from) + current_widget = self.currentWidget() + + self.removeTab(index_from) + self.insertTab(index_to, widget, icon, text) + self.setTabToolTip(index_to, tip) + + self.setCurrentWidget(current_widget) + self.move_tab_finished.emit() + + def move_tab_from_another_tabwidget(self, tabwidget_from, + index_from, index_to): + """Move tab from a tabwidget to another""" + + # We pass self object IDs as QString objs, because otherwise it would + # depend on the platform: long for 64bit, int for 32bit. Replacing + # by long all the time is not working on some 32bit platforms + # (see Issue 1094, Issue 1098) + self.sig_move_tab.emit(tabwidget_from, str(id(self)), index_from, + index_to) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/arrayeditor.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/arrayeditor.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/arrayeditor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/arrayeditor.py 2016-11-11 04:08:03.000000000 +0000 @@ -0,0 +1,800 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +NumPy Array Editor Dialog based on Qt +""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +from __future__ import print_function + +# Third party imports +from qtpy.compat import from_qvariant, to_qvariant +from qtpy.QtCore import (QAbstractTableModel, QItemSelection, + QItemSelectionRange, QModelIndex, Qt, Slot) +from qtpy.QtGui import QColor, QCursor, QDoubleValidator, QKeySequence +from qtpy.QtWidgets import (QAbstractItemDelegate, QApplication, QCheckBox, + QComboBox, QDialog, QDialogButtonBox, QGridLayout, + QHBoxLayout, QInputDialog, QItemDelegate, QLabel, + QLineEdit, QMenu, QMessageBox, QPushButton, + QSpinBox, QStackedWidget, QTableView, QVBoxLayout, + QWidget) +import numpy as np + +# Local imports +from spyder.config.base import _ +from spyder.config.fonts import DEFAULT_SMALL_DELTA +from spyder.config.gui import get_font, fixed_shortcut +from spyder.py3compat import (io, is_binary_string, is_string, + is_text_string, PY3, to_binary_string, + to_text_string) +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import add_actions, create_action, keybinding + + +# Note: string and unicode data types will be formatted with '%s' (see below) +SUPPORTED_FORMATS = { + 'single': '%.3f', + 'double': '%.3f', + 'float_': '%.3f', + 'longfloat': '%.3f', + 'float16': '%.3f', + 'float32': '%.3f', + 'float64': '%.3f', + 'float96': '%.3f', + 'float128': '%.3f', + 'csingle': '%r', + 'complex_': '%r', + 'clongfloat': '%r', + 'complex64': '%r', + 'complex128': '%r', + 'complex192': '%r', + 'complex256': '%r', + 'byte': '%d', + 'bytes8': '%s', + 'short': '%d', + 'intc': '%d', + 'int_': '%d', + 'longlong': '%d', + 'intp': '%d', + 'int8': '%d', + 'int16': '%d', + 'int32': '%d', + 'int64': '%d', + 'ubyte': '%d', + 'ushort': '%d', + 'uintc': '%d', + 'uint': '%d', + 'ulonglong': '%d', + 'uintp': '%d', + 'uint8': '%d', + 'uint16': '%d', + 'uint32': '%d', + 'uint64': '%d', + 'bool_': '%r', + 'bool8': '%r', + 'bool': '%r', + } + + +LARGE_SIZE = 5e5 +LARGE_NROWS = 1e5 +LARGE_COLS = 60 + + +#============================================================================== +# Utility functions +#============================================================================== +def is_float(dtype): + """Return True if datatype dtype is a float kind""" + return ('float' in dtype.name) or dtype.name in ['single', 'double'] + + +def is_number(dtype): + """Return True is datatype dtype is a number kind""" + return is_float(dtype) or ('int' in dtype.name) or ('long' in dtype.name) \ + or ('short' in dtype.name) + + +def get_idx_rect(index_list): + """Extract the boundaries from a list of indexes""" + rows, cols = list(zip(*[(i.row(), i.column()) for i in index_list])) + return ( min(rows), max(rows), min(cols), max(cols) ) + + +#============================================================================== +# Main classes +#============================================================================== +class ArrayModel(QAbstractTableModel): + """Array Editor Table Model""" + + ROWS_TO_LOAD = 500 + COLS_TO_LOAD = 40 + + def __init__(self, data, format="%.3f", xlabels=None, ylabels=None, + readonly=False, parent=None): + QAbstractTableModel.__init__(self) + + self.dialog = parent + self.changes = {} + self.xlabels = xlabels + self.ylabels = ylabels + self.readonly = readonly + self.test_array = np.array([0], dtype=data.dtype) + + # for complex numbers, shading will be based on absolute value + # but for all other types it will be the real part + if data.dtype in (np.complex64, np.complex128): + self.color_func = np.abs + else: + self.color_func = np.real + + # Backgroundcolor settings + huerange = [.66, .99] # Hue + self.sat = .7 # Saturation + self.val = 1. # Value + self.alp = .6 # Alpha-channel + + self._data = data + self._format = format + + self.total_rows = self._data.shape[0] + self.total_cols = self._data.shape[1] + size = self.total_rows * self.total_cols + + try: + self.vmin = np.nanmin(self.color_func(data)) + self.vmax = np.nanmax(self.color_func(data)) + if self.vmax == self.vmin: + self.vmin -= 1 + self.hue0 = huerange[0] + self.dhue = huerange[1]-huerange[0] + self.bgcolor_enabled = True + except TypeError: + self.vmin = None + self.vmax = None + self.hue0 = None + self.dhue = None + self.bgcolor_enabled = False + + # Use paging when the total size, number of rows or number of + # columns is too large + if size > LARGE_SIZE: + self.rows_loaded = self.ROWS_TO_LOAD + self.cols_loaded = self.COLS_TO_LOAD + else: + if self.total_rows > LARGE_NROWS: + self.rows_loaded = self.ROWS_TO_LOAD + else: + self.rows_loaded = self.total_rows + if self.total_cols > LARGE_COLS: + self.cols_loaded = self.COLS_TO_LOAD + else: + self.cols_loaded = self.total_cols + + def get_format(self): + """Return current format""" + # Avoid accessing the private attribute _format from outside + return self._format + + def get_data(self): + """Return data""" + return self._data + + def set_format(self, format): + """Change display format""" + self._format = format + self.reset() + + def columnCount(self, qindex=QModelIndex()): + """Array column number""" + if self.total_cols <= self.cols_loaded: + return self.total_cols + else: + return self.cols_loaded + + def rowCount(self, qindex=QModelIndex()): + """Array row number""" + if self.total_rows <= self.rows_loaded: + return self.total_rows + else: + return self.rows_loaded + + def can_fetch_more(self, rows=False, columns=False): + if rows: + if self.total_rows > self.rows_loaded: + return True + else: + return False + if columns: + if self.total_cols > self.cols_loaded: + return True + else: + return False + + def fetch_more(self, rows=False, columns=False): + if self.can_fetch_more(rows=rows): + reminder = self.total_rows - self.rows_loaded + items_to_fetch = min(reminder, self.ROWS_TO_LOAD) + self.beginInsertRows(QModelIndex(), self.rows_loaded, + self.rows_loaded + items_to_fetch - 1) + self.rows_loaded += items_to_fetch + self.endInsertRows() + if self.can_fetch_more(columns=columns): + reminder = self.total_cols - self.cols_loaded + items_to_fetch = min(reminder, self.COLS_TO_LOAD) + self.beginInsertColumns(QModelIndex(), self.cols_loaded, + self.cols_loaded + items_to_fetch - 1) + self.cols_loaded += items_to_fetch + self.endInsertColumns() + + def bgcolor(self, state): + """Toggle backgroundcolor""" + self.bgcolor_enabled = state > 0 + self.reset() + + def get_value(self, index): + i = index.row() + j = index.column() + return self.changes.get((i, j), self._data[i, j]) + + def data(self, index, role=Qt.DisplayRole): + """Cell content""" + if not index.isValid(): + return to_qvariant() + value = self.get_value(index) + if is_binary_string(value): + try: + value = to_text_string(value, 'utf8') + except: + pass + if role == Qt.DisplayRole: + if value is np.ma.masked: + return '' + else: + return to_qvariant(self._format % value) + elif role == Qt.TextAlignmentRole: + return to_qvariant(int(Qt.AlignCenter|Qt.AlignVCenter)) + elif role == Qt.BackgroundColorRole and self.bgcolor_enabled \ + and value is not np.ma.masked: + hue = self.hue0+\ + self.dhue*(self.vmax-self.color_func(value)) \ + /(self.vmax-self.vmin) + hue = float(np.abs(hue)) + color = QColor.fromHsvF(hue, self.sat, self.val, self.alp) + return to_qvariant(color) + elif role == Qt.FontRole: + return to_qvariant(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) + return to_qvariant() + + def setData(self, index, value, role=Qt.EditRole): + """Cell content change""" + if not index.isValid() or self.readonly: + return False + i = index.row() + j = index.column() + value = from_qvariant(value, str) + dtype = self._data.dtype.name + if dtype == "bool": + try: + val = bool(float(value)) + except ValueError: + val = value.lower() == "true" + elif dtype.startswith("string") or dtype.startswith("bytes"): + val = to_binary_string(value, 'utf8') + elif dtype.startswith("unicode") or dtype.startswith("str"): + val = to_text_string(value) + else: + if value.lower().startswith('e') or value.lower().endswith('e'): + return False + try: + val = complex(value) + if not val.imag: + val = val.real + except ValueError as e: + QMessageBox.critical(self.dialog, "Error", + "Value error: %s" % str(e)) + return False + try: + self.test_array[0] = val # will raise an Exception eventually + except OverflowError as e: + print(type(e.message)) + QMessageBox.critical(self.dialog, "Error", + "Overflow error: %s" % e.message) + return False + + # Add change to self.changes + self.changes[(i, j)] = val + self.dataChanged.emit(index, index) + if not is_string(val): + if val > self.vmax: + self.vmax = val + if val < self.vmin: + self.vmin = val + return True + + def flags(self, index): + """Set editable flag""" + if not index.isValid(): + return Qt.ItemIsEnabled + return Qt.ItemFlags(QAbstractTableModel.flags(self, index)| + Qt.ItemIsEditable) + + def headerData(self, section, orientation, role=Qt.DisplayRole): + """Set header data""" + if role != Qt.DisplayRole: + return to_qvariant() + labels = self.xlabels if orientation == Qt.Horizontal else self.ylabels + if labels is None: + return to_qvariant(int(section)) + else: + return to_qvariant(labels[section]) + + def reset(self): + self.beginResetModel() + self.endResetModel() + + +class ArrayDelegate(QItemDelegate): + """Array Editor Item Delegate""" + def __init__(self, dtype, parent=None): + QItemDelegate.__init__(self, parent) + self.dtype = dtype + + def createEditor(self, parent, option, index): + """Create editor widget""" + model = index.model() + value = model.get_value(index) + if model._data.dtype.name == "bool": + value = not value + model.setData(index, to_qvariant(value)) + return + elif value is not np.ma.masked: + editor = QLineEdit(parent) + editor.setFont(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) + editor.setAlignment(Qt.AlignCenter) + if is_number(self.dtype): + editor.setValidator(QDoubleValidator(editor)) + editor.returnPressed.connect(self.commitAndCloseEditor) + return editor + + def commitAndCloseEditor(self): + """Commit and close editor""" + editor = self.sender() + # Avoid a segfault with PyQt5. Variable value won't be changed + # but at least Spyder won't crash. It seems generated by a + # bug in sip. See + # http://comments.gmane.org/gmane.comp.python.pyqt-pykde/26544 + try: + self.commitData.emit(editor) + except AttributeError: + pass + self.closeEditor.emit(editor, QAbstractItemDelegate.NoHint) + + def setEditorData(self, editor, index): + """Set editor widget's data""" + text = from_qvariant(index.model().data(index, Qt.DisplayRole), str) + editor.setText(text) + + +#TODO: Implement "Paste" (from clipboard) feature +class ArrayView(QTableView): + """Array view class""" + def __init__(self, parent, model, dtype, shape): + QTableView.__init__(self, parent) + + self.setModel(model) + self.setItemDelegate(ArrayDelegate(dtype, self)) + total_width = 0 + for k in range(shape[1]): + total_width += self.columnWidth(k) + self.viewport().resize(min(total_width, 1024), self.height()) + self.shape = shape + self.menu = self.setup_menu() + fixed_shortcut(QKeySequence.Copy, self, self.copy) + self.horizontalScrollBar().valueChanged.connect( + lambda val: self.load_more_data(val, columns=True)) + self.verticalScrollBar().valueChanged.connect( + lambda val: self.load_more_data(val, rows=True)) + + def load_more_data(self, value, rows=False, columns=False): + old_selection = self.selectionModel().selection() + old_rows_loaded = old_cols_loaded = None + + if rows and value == self.verticalScrollBar().maximum(): + old_rows_loaded = self.model().rows_loaded + self.model().fetch_more(rows=rows) + + if columns and value == self.horizontalScrollBar().maximum(): + old_cols_loaded = self.model().cols_loaded + self.model().fetch_more(columns=columns) + + if old_rows_loaded is not None or old_cols_loaded is not None: + # if we've changed anything, update selection + new_selection = QItemSelection() + for part in old_selection: + top = part.top() + bottom = part.bottom() + if (old_rows_loaded is not None and + top == 0 and bottom == (old_rows_loaded-1)): + # complete column selected (so expand it to match updated range) + bottom = self.model().rows_loaded-1 + left = part.left() + right = part.right() + if (old_cols_loaded is not None + and left == 0 and right == (old_cols_loaded-1)): + # compete row selected (so expand it to match updated range) + right = self.model().cols_loaded-1 + top_left = self.model().index(top, left) + bottom_right = self.model().index(bottom, right) + part = QItemSelectionRange(top_left, bottom_right) + new_selection.append(part) + self.selectionModel().select(new_selection, self.selectionModel().ClearAndSelect) + + + def resize_to_contents(self): + """Resize cells to contents""" + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + self.resizeColumnsToContents() + self.model().fetch_more(columns=True) + self.resizeColumnsToContents() + QApplication.restoreOverrideCursor() + + def setup_menu(self): + """Setup context menu""" + self.copy_action = create_action(self, _('Copy'), + shortcut=keybinding('Copy'), + icon=ima.icon('editcopy'), + triggered=self.copy, + context=Qt.WidgetShortcut) + menu = QMenu(self) + add_actions(menu, [self.copy_action, ]) + return menu + + def contextMenuEvent(self, event): + """Reimplement Qt method""" + self.menu.popup(event.globalPos()) + event.accept() + + def keyPressEvent(self, event): + """Reimplement Qt method""" + if event == QKeySequence.Copy: + self.copy() + else: + QTableView.keyPressEvent(self, event) + + def _sel_to_text(self, cell_range): + """Copy an array portion to a unicode string""" + if not cell_range: + return + row_min, row_max, col_min, col_max = get_idx_rect(cell_range) + if col_min == 0 and col_max == (self.model().cols_loaded-1): + # we've selected a whole column. It isn't possible to + # select only the first part of a column without loading more, + # so we can treat it as intentional and copy the whole thing + col_max = self.model().total_cols-1 + if row_min == 0 and row_max == (self.model().rows_loaded-1): + row_max = self.model().total_rows-1 + + _data = self.model().get_data() + if PY3: + output = io.BytesIO() + else: + output = io.StringIO() + try: + np.savetxt(output, _data[row_min:row_max+1, col_min:col_max+1], + delimiter='\t') + except: + QMessageBox.warning(self, _("Warning"), + _("It was not possible to copy values for " + "this array")) + return + contents = output.getvalue().decode('utf-8') + output.close() + return contents + + @Slot() + def copy(self): + """Copy text to clipboard""" + cliptxt = self._sel_to_text( self.selectedIndexes() ) + clipboard = QApplication.clipboard() + clipboard.setText(cliptxt) + + +class ArrayEditorWidget(QWidget): + def __init__(self, parent, data, readonly=False, + xlabels=None, ylabels=None): + QWidget.__init__(self, parent) + self.data = data + self.old_data_shape = None + if len(self.data.shape) == 1: + self.old_data_shape = self.data.shape + self.data.shape = (self.data.shape[0], 1) + elif len(self.data.shape) == 0: + self.old_data_shape = self.data.shape + self.data.shape = (1, 1) + + format = SUPPORTED_FORMATS.get(data.dtype.name, '%s') + self.model = ArrayModel(self.data, format=format, xlabels=xlabels, + ylabels=ylabels, readonly=readonly, parent=self) + self.view = ArrayView(self, self.model, data.dtype, data.shape) + + btn_layout = QHBoxLayout() + btn_layout.setAlignment(Qt.AlignLeft) + btn = QPushButton(_( "Format")) + # disable format button for int type + btn.setEnabled(is_float(data.dtype)) + btn_layout.addWidget(btn) + btn.clicked.connect(self.change_format) + btn = QPushButton(_( "Resize")) + btn_layout.addWidget(btn) + btn.clicked.connect(self.view.resize_to_contents) + bgcolor = QCheckBox(_( 'Background color')) + bgcolor.setChecked(self.model.bgcolor_enabled) + bgcolor.setEnabled(self.model.bgcolor_enabled) + bgcolor.stateChanged.connect(self.model.bgcolor) + btn_layout.addWidget(bgcolor) + + layout = QVBoxLayout() + layout.addWidget(self.view) + layout.addLayout(btn_layout) + self.setLayout(layout) + + def accept_changes(self): + """Accept changes""" + for (i, j), value in list(self.model.changes.items()): + self.data[i, j] = value + if self.old_data_shape is not None: + self.data.shape = self.old_data_shape + + def reject_changes(self): + """Reject changes""" + if self.old_data_shape is not None: + self.data.shape = self.old_data_shape + + def change_format(self): + """Change display format""" + format, valid = QInputDialog.getText(self, _( 'Format'), + _( "Float formatting"), + QLineEdit.Normal, self.model.get_format()) + if valid: + format = str(format) + try: + format % 1.1 + except: + QMessageBox.critical(self, _("Error"), + _("Format (%s) is incorrect") % format) + return + self.model.set_format(format) + + +class ArrayEditor(QDialog): + """Array Editor Dialog""" + def __init__(self, parent=None): + QDialog.__init__(self, parent) + + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + + self.data = None + self.arraywidget = None + self.stack = None + self.layout = None + # Values for 3d array editor + self.dim_indexes = [{}, {}, {}] + self.last_dim = 0 # Adjust this for changing the startup dimension + + def setup_and_check(self, data, title='', readonly=False, + xlabels=None, ylabels=None): + """ + Setup ArrayEditor: + return False if data is not supported, True otherwise + """ + self.data = data + self.data.flags.writeable = True + is_record_array = data.dtype.names is not None + is_masked_array = isinstance(data, np.ma.MaskedArray) + if data.size == 0: + self.error(_("Array is empty")) + return False + if data.ndim > 3: + self.error(_("Arrays with more than 3 dimensions are not supported")) + return False + if xlabels is not None and len(xlabels) != self.data.shape[1]: + self.error(_("The 'xlabels' argument length do no match array " + "column number")) + return False + if ylabels is not None and len(ylabels) != self.data.shape[0]: + self.error(_("The 'ylabels' argument length do no match array row " + "number")) + return False + if not is_record_array: + dtn = data.dtype.name + if dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') \ + and not dtn.startswith('unicode'): + arr = _("%s arrays") % data.dtype.name + self.error(_("%s are currently not supported") % arr) + return False + + self.layout = QGridLayout() + self.setLayout(self.layout) + self.setWindowIcon(ima.icon('arredit')) + if title: + title = to_text_string(title) + " - " + _("NumPy array") + else: + title = _("Array editor") + if readonly: + title += ' (' + _('read only') + ')' + self.setWindowTitle(title) + self.resize(600, 500) + + # Stack widget + self.stack = QStackedWidget(self) + if is_record_array: + for name in data.dtype.names: + self.stack.addWidget(ArrayEditorWidget(self, data[name], + readonly, xlabels, ylabels)) + elif is_masked_array: + self.stack.addWidget(ArrayEditorWidget(self, data, readonly, + xlabels, ylabels)) + self.stack.addWidget(ArrayEditorWidget(self, data.data, readonly, + xlabels, ylabels)) + self.stack.addWidget(ArrayEditorWidget(self, data.mask, readonly, + xlabels, ylabels)) + elif data.ndim == 3: + pass + else: + self.stack.addWidget(ArrayEditorWidget(self, data, readonly, + xlabels, ylabels)) + self.arraywidget = self.stack.currentWidget() + self.stack.currentChanged.connect(self.current_widget_changed) + self.layout.addWidget(self.stack, 1, 0) + + # Buttons configuration + btn_layout = QHBoxLayout() + if is_record_array or is_masked_array or data.ndim == 3: + if is_record_array: + btn_layout.addWidget(QLabel(_("Record array fields:"))) + names = [] + for name in data.dtype.names: + field = data.dtype.fields[name] + text = name + if len(field) >= 3: + title = field[2] + if not is_text_string(title): + title = repr(title) + text += ' - '+title + names.append(text) + else: + names = [_('Masked data'), _('Data'), _('Mask')] + if data.ndim == 3: + # QSpinBox + self.index_spin = QSpinBox(self, keyboardTracking=False) + self.index_spin.valueChanged.connect(self.change_active_widget) + # QComboBox + names = [str(i) for i in range(3)] + ra_combo = QComboBox(self) + ra_combo.addItems(names) + ra_combo.currentIndexChanged.connect(self.current_dim_changed) + # Adding the widgets to layout + label = QLabel(_("Axis:")) + btn_layout.addWidget(label) + btn_layout.addWidget(ra_combo) + self.shape_label = QLabel() + btn_layout.addWidget(self.shape_label) + label = QLabel(_("Index:")) + btn_layout.addWidget(label) + btn_layout.addWidget(self.index_spin) + self.slicing_label = QLabel() + btn_layout.addWidget(self.slicing_label) + # set the widget to display when launched + self.current_dim_changed(self.last_dim) + else: + ra_combo = QComboBox(self) + ra_combo.currentIndexChanged.connect(self.stack.setCurrentIndex) + ra_combo.addItems(names) + btn_layout.addWidget(ra_combo) + if is_masked_array: + label = QLabel(_("Warning: changes are applied separately")) + label.setToolTip(_("For performance reasons, changes applied "\ + "to masked array won't be reflected in "\ + "array's data (and vice-versa).")) + btn_layout.addWidget(label) + btn_layout.addStretch() + bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + bbox.accepted.connect(self.accept) + bbox.rejected.connect(self.reject) + btn_layout.addWidget(bbox) + self.layout.addLayout(btn_layout, 2, 0) + + self.setMinimumSize(400, 300) + + # Make the dialog act as a window + self.setWindowFlags(Qt.Window) + + return True + + def current_widget_changed(self, index): + self.arraywidget = self.stack.widget(index) + + def change_active_widget(self, index): + """ + This is implemented for handling negative values in index for + 3d arrays, to give the same behavior as slicing + """ + string_index = [':']*3 + string_index[self.last_dim] = '%i' + self.slicing_label.setText((r"Slicing: [" + ", ".join(string_index) + + "]") % index) + if index < 0: + data_index = self.data.shape[self.last_dim] + index + else: + data_index = index + slice_index = [slice(None)]*3 + slice_index[self.last_dim] = data_index + + stack_index = self.dim_indexes[self.last_dim].get(data_index) + if stack_index == None: + stack_index = self.stack.count() + self.stack.addWidget(ArrayEditorWidget(self, + self.data[slice_index])) + self.dim_indexes[self.last_dim][data_index] = stack_index + self.stack.update() + self.stack.setCurrentIndex(stack_index) + + def current_dim_changed(self, index): + """ + This change the active axis the array editor is plotting over + in 3D + """ + self.last_dim = index + string_size = ['%i']*3 + string_size[index] = '%i' + self.shape_label.setText(('Shape: (' + ', '.join(string_size) + + ') ') % self.data.shape) + if self.index_spin.value() != 0: + self.index_spin.setValue(0) + else: + # this is done since if the value is currently 0 it does not emit + # currentIndexChanged(int) + self.change_active_widget(0) + self.index_spin.setRange(-self.data.shape[index], + self.data.shape[index]-1) + + @Slot() + def accept(self): + """Reimplement Qt method""" + for index in range(self.stack.count()): + self.stack.widget(index).accept_changes() + QDialog.accept(self) + + def get_value(self): + """Return modified array -- this is *not* a copy""" + # It is import to avoid accessing Qt C++ object as it has probably + # already been destroyed, due to the Qt.WA_DeleteOnClose attribute + return self.data + + def error(self, message): + """An error occured, closing the dialog box""" + QMessageBox.critical(self, _("Array editor"), message) + self.setAttribute(Qt.WA_DeleteOnClose) + self.reject() + + @Slot() + def reject(self): + """Reimplement Qt method""" + if self.arraywidget is not None: + for index in range(self.stack.count()): + self.stack.widget(index).reject_changes() + QDialog.reject(self) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/collectionseditor.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/collectionseditor.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/collectionseditor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/collectionseditor.py 2016-11-17 03:39:40.000000000 +0000 @@ -0,0 +1,1493 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Collections (i.e. dictionary, list and tuple) editor widget and dialog +""" + +#TODO: Multiple selection: open as many editors (array/dict/...) as necessary, +# at the same time + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +from __future__ import print_function +import datetime +import gc +import sys +import warnings + +# Third party imports +from qtpy.compat import getsavefilename, to_qvariant +from qtpy.QtCore import (QAbstractTableModel, QDateTime, QModelIndex, Qt, + Signal, Slot) +from qtpy.QtGui import QColor, QKeySequence +from qtpy.QtWidgets import (QAbstractItemDelegate, QApplication, QDateEdit, + QDateTimeEdit, QDialog, QDialogButtonBox, + QInputDialog, QItemDelegate, QLineEdit, QMenu, + QMessageBox, QTableView, QVBoxLayout, QWidget) + +# Local import +from spyder.config.base import _ +from spyder.config.fonts import DEFAULT_SMALL_DELTA +from spyder.config.gui import get_font +from spyder.py3compat import (io, is_binary_string, is_text_string, + getcwd, PY3, to_text_string) +from spyder.utils import icon_manager as ima +from spyder.utils.misc import fix_reference_name +from spyder.utils.qthelpers import (add_actions, create_action, + mimedata2url) +from spyder.widgets.variableexplorer.importwizard import ImportWizard +from spyder.widgets.variableexplorer.texteditor import TextEditor +from spyder.widgets.variableexplorer.utils import ( + array, DataFrame, display_to_value, FakeObject, get_color_name, + get_human_readable_type, get_size, Image, is_editable_type, is_known_type, + MaskedArray, ndarray, np_savetxt, Series, sort_against, try_to_eval, + unsorted_unique, value_to_display,) + +if ndarray is not FakeObject: + from spyder.widgets.variableexplorer.arrayeditor import ArrayEditor + +if DataFrame is not FakeObject: + from spyder.widgets.variableexplorer.dataframeeditor import DataFrameEditor + + +LARGE_NROWS = 100 + + +class ProxyObject(object): + """Dictionary proxy to an unknown object""" + def __init__(self, obj): + self.__obj__ = obj + + def __len__(self): + return len(dir(self.__obj__)) + + def __getitem__(self, key): + return getattr(self.__obj__, key) + + def __setitem__(self, key, value): + setattr(self.__obj__, key, value) + + +class ReadOnlyCollectionsModel(QAbstractTableModel): + """CollectionsEditor Read-Only Table Model""" + ROWS_TO_LOAD = 50 + + def __init__(self, parent, data, title="", names=False, + minmax=False, remote=False): + QAbstractTableModel.__init__(self, parent) + if data is None: + data = {} + self.names = names + self.minmax = minmax + self.remote = remote + self.header0 = None + self._data = None + self.total_rows = None + self.showndata = None + self.keys = None + self.title = to_text_string(title) # in case title is not a string + if self.title: + self.title = self.title + ' - ' + self.sizes = [] + self.types = [] + self.set_data(data) + + def get_data(self): + """Return model data""" + return self._data + + def set_data(self, data, coll_filter=None): + """Set model data""" + self._data = data + + if coll_filter is not None and not self.remote and \ + isinstance(data, (tuple, list, dict)): + data = coll_filter(data) + self.showndata = data + + self.header0 = _("Index") + if self.names: + self.header0 = _("Name") + if isinstance(data, tuple): + self.keys = list(range(len(data))) + self.title += _("Tuple") + elif isinstance(data, list): + self.keys = list(range(len(data))) + self.title += _("List") + elif isinstance(data, dict): + self.keys = list(data.keys()) + self.title += _("Dictionary") + if not self.names: + self.header0 = _("Key") + else: + self.keys = dir(data) + self._data = data = self.showndata = ProxyObject(data) + self.title += _("Object") + if not self.names: + self.header0 = _("Attribute") + + self.title += ' ('+str(len(self.keys))+' '+ _("elements")+')' + + self.total_rows = len(self.keys) + if self.total_rows > LARGE_NROWS: + self.rows_loaded = self.ROWS_TO_LOAD + else: + self.rows_loaded = self.total_rows + + self.set_size_and_type() + self.reset() + + def set_size_and_type(self, start=None, stop=None): + data = self._data + + if start is None and stop is None: + start = 0 + stop = self.rows_loaded + fetch_more = False + else: + fetch_more = True + + if self.remote: + sizes = [ data[self.keys[index]]['size'] + for index in range(start, stop) ] + types = [ data[self.keys[index]]['type'] + for index in range(start, stop) ] + else: + sizes = [ get_size(data[self.keys[index]]) + for index in range(start, stop) ] + types = [ get_human_readable_type(data[self.keys[index]]) + for index in range(start, stop) ] + + if fetch_more: + self.sizes = self.sizes + sizes + self.types = self.types + types + else: + self.sizes = sizes + self.types = types + + def sort(self, column, order=Qt.AscendingOrder): + """Overriding sort method""" + reverse = (order==Qt.DescendingOrder) + if column == 0: + self.sizes = sort_against(self.sizes, self.keys, reverse) + self.types = sort_against(self.types, self.keys, reverse) + try: + self.keys.sort(reverse=reverse) + except: + pass + elif column == 1: + self.keys = sort_against(self.keys, self.types, reverse) + self.sizes = sort_against(self.sizes, self.types, reverse) + try: + self.types.sort(reverse=reverse) + except: + pass + elif column == 2: + self.keys = sort_against(self.keys, self.sizes, reverse) + self.types = sort_against(self.types, self.sizes, reverse) + try: + self.sizes.sort(reverse=reverse) + except: + pass + elif column == 3: + self.keys = sort_against(self.keys, self.sizes, reverse) + self.types = sort_against(self.types, self.sizes, reverse) + try: + self.sizes.sort(reverse=reverse) + except: + pass + elif column == 4: + values = [self._data[key] for key in self.keys] + self.keys = sort_against(self.keys, values, reverse) + self.sizes = sort_against(self.sizes, values, reverse) + self.types = sort_against(self.types, values, reverse) + self.beginResetModel() + self.endResetModel() + + def columnCount(self, qindex=QModelIndex()): + """Array column number""" + return 4 + + def rowCount(self, index=QModelIndex()): + """Array row number""" + if self.total_rows <= self.rows_loaded: + return self.total_rows + else: + return self.rows_loaded + + def canFetchMore(self, index=QModelIndex()): + if self.total_rows > self.rows_loaded: + return True + else: + return False + + def fetchMore(self, index=QModelIndex()): + reminder = self.total_rows - self.rows_loaded + items_to_fetch = min(reminder, self.ROWS_TO_LOAD) + self.set_size_and_type(self.rows_loaded, + self.rows_loaded + items_to_fetch) + self.beginInsertRows(QModelIndex(), self.rows_loaded, + self.rows_loaded + items_to_fetch - 1) + self.rows_loaded += items_to_fetch + self.endInsertRows() + + def get_index_from_key(self, key): + try: + return self.createIndex(self.keys.index(key), 0) + except ValueError: + return QModelIndex() + + def get_key(self, index): + """Return current key""" + return self.keys[index.row()] + + def get_value(self, index): + """Return current value""" + if index.column() == 0: + return self.keys[ index.row() ] + elif index.column() == 1: + return self.types[ index.row() ] + elif index.column() == 2: + return self.sizes[ index.row() ] + else: + return self._data[ self.keys[index.row()] ] + + def get_bgcolor(self, index): + """Background color depending on value""" + if index.column() == 0: + color = QColor(Qt.lightGray) + color.setAlphaF(.05) + elif index.column() < 3: + color = QColor(Qt.lightGray) + color.setAlphaF(.2) + else: + color = QColor(Qt.lightGray) + color.setAlphaF(.3) + return color + + def data(self, index, role=Qt.DisplayRole): + """Cell content""" + if not index.isValid(): + return to_qvariant() + value = self.get_value(index) + if index.column() == 3 and self.remote: + value = value['view'] + if index.column() == 3: + display = value_to_display(value, minmax=self.minmax) + else: + display = to_text_string(value) + if role == Qt.DisplayRole: + return to_qvariant(display) + elif role == Qt.EditRole: + return to_qvariant(value_to_display(value)) + elif role == Qt.TextAlignmentRole: + if index.column() == 3: + if len(display.splitlines()) < 3: + return to_qvariant(int(Qt.AlignLeft|Qt.AlignVCenter)) + else: + return to_qvariant(int(Qt.AlignLeft|Qt.AlignTop)) + else: + return to_qvariant(int(Qt.AlignLeft|Qt.AlignVCenter)) + elif role == Qt.BackgroundColorRole: + return to_qvariant( self.get_bgcolor(index) ) + elif role == Qt.FontRole: + return to_qvariant(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) + return to_qvariant() + + def headerData(self, section, orientation, role=Qt.DisplayRole): + """Overriding method headerData""" + if role != Qt.DisplayRole: + return to_qvariant() + i_column = int(section) + if orientation == Qt.Horizontal: + headers = (self.header0, _("Type"), _("Size"), _("Value")) + return to_qvariant( headers[i_column] ) + else: + return to_qvariant() + + def flags(self, index): + """Overriding method flags""" + # This method was implemented in CollectionsModel only, but to enable + # tuple exploration (even without editing), this method was moved here + if not index.isValid(): + return Qt.ItemIsEnabled + return Qt.ItemFlags(QAbstractTableModel.flags(self, index)| + Qt.ItemIsEditable) + def reset(self): + self.beginResetModel() + self.endResetModel() + + +class CollectionsModel(ReadOnlyCollectionsModel): + """Collections Table Model""" + + def set_value(self, index, value): + """Set value""" + self._data[ self.keys[index.row()] ] = value + self.showndata[ self.keys[index.row()] ] = value + self.sizes[index.row()] = get_size(value) + self.types[index.row()] = get_human_readable_type(value) + + def get_bgcolor(self, index): + """Background color depending on value""" + value = self.get_value(index) + if index.column() < 3: + color = ReadOnlyCollectionsModel.get_bgcolor(self, index) + else: + if self.remote: + color_name = value['color'] + else: + color_name = get_color_name(value) + color = QColor(color_name) + color.setAlphaF(.2) + return color + + def setData(self, index, value, role=Qt.EditRole): + """Cell content change""" + if not index.isValid(): + return False + if index.column() < 3: + return False + value = display_to_value(value, self.get_value(index), + ignore_errors=True) + self.set_value(index, value) + self.dataChanged.emit(index, index) + return True + + +class CollectionsDelegate(QItemDelegate): + """CollectionsEditor Item Delegate""" + + def __init__(self, parent=None): + QItemDelegate.__init__(self, parent) + self._editors = {} # keep references on opened editors + + def get_value(self, index): + if index.isValid(): + return index.model().get_value(index) + + def set_value(self, index, value): + if index.isValid(): + index.model().set_value(index, value) + + def show_warning(self, index): + """ + Decide if showing a warning when the user is trying to view + a big variable associated to a Tablemodel index + + This avoids getting the variables' value to know its + size and type, using instead those already computed by + the TableModel. + + The problem is when a variable is too big, it can take a + lot of time just to get its value + """ + try: + val_size = index.model().sizes[index.row()] + val_type = index.model().types[index.row()] + except: + return False + if val_type in ['list', 'tuple', 'dict'] and int(val_size) > 1e5: + return True + else: + return False + + def createEditor(self, parent, option, index): + """Overriding method createEditor""" + if index.column() < 3: + return None + if self.show_warning(index): + answer = QMessageBox.warning(self.parent(), _("Warning"), + _("Opening this variable can be slow\n\n" + "Do you want to continue anyway?"), + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.No: + return None + try: + value = self.get_value(index) + except Exception as msg: + QMessageBox.critical(self.parent(), _("Error"), + _("Spyder was unable to retrieve the value of " + "this variable from the console.

    " + "The error mesage was:
    " + "%s" + ) % to_text_string(msg)) + return + key = index.model().get_key(index) + readonly = isinstance(value, tuple) or self.parent().readonly \ + or not is_known_type(value) + #---editor = CollectionsEditor + if isinstance(value, (list, tuple, dict)): + editor = CollectionsEditor() + editor.setup(value, key, icon=self.parent().windowIcon(), + readonly=readonly) + self.create_dialog(editor, dict(model=index.model(), editor=editor, + key=key, readonly=readonly)) + return None + #---editor = ArrayEditor + elif isinstance(value, (ndarray, MaskedArray)) \ + and ndarray is not FakeObject: + if value.size == 0: + return None + editor = ArrayEditor(parent) + if not editor.setup_and_check(value, title=key, readonly=readonly): + return + self.create_dialog(editor, dict(model=index.model(), editor=editor, + key=key, readonly=readonly)) + return None + #---showing image + elif isinstance(value, Image) and ndarray is not FakeObject \ + and Image is not FakeObject: + arr = array(value) + if arr.size == 0: + return None + editor = ArrayEditor(parent) + if not editor.setup_and_check(arr, title=key, readonly=readonly): + return + conv_func = lambda arr: Image.fromarray(arr, mode=value.mode) + self.create_dialog(editor, dict(model=index.model(), editor=editor, + key=key, readonly=readonly, + conv=conv_func)) + return None + #--editor = DataFrameEditor + elif isinstance(value, (DataFrame, Series)) \ + and DataFrame is not FakeObject: + editor = DataFrameEditor() + if not editor.setup_and_check(value, title=key): + return + self.create_dialog(editor, dict(model=index.model(), editor=editor, + key=key, readonly=readonly)) + return None + #---editor = QDateTimeEdit + elif isinstance(value, datetime.datetime): + editor = QDateTimeEdit(value, parent) + editor.setCalendarPopup(True) + editor.setFont(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) + return editor + #---editor = QDateEdit + elif isinstance(value, datetime.date): + editor = QDateEdit(value, parent) + editor.setCalendarPopup(True) + editor.setFont(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) + return editor + #---editor = TextEditor + elif is_text_string(value) and len(value)>40: + editor = TextEditor(value, key) + self.create_dialog(editor, dict(model=index.model(), editor=editor, + key=key, readonly=readonly)) + return None + #---editor = QLineEdit + elif is_editable_type(value): + editor = QLineEdit(parent) + editor.setFont(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) + editor.setAlignment(Qt.AlignLeft) + # This is making Spyder crash because the QLineEdit that it's + # been modified is removed and a new one is created after + # evaluation. So the object on which this method is trying to + # act doesn't exist anymore. + # editor.returnPressed.connect(self.commitAndCloseEditor) + return editor + #---editor = CollectionsEditor for an arbitrary object + else: + editor = CollectionsEditor() + editor.setup(value, key, icon=self.parent().windowIcon(), + readonly=readonly) + self.create_dialog(editor, dict(model=index.model(), editor=editor, + key=key, readonly=readonly)) + return None + + def create_dialog(self, editor, data): + self._editors[id(editor)] = data + editor.accepted.connect( + lambda eid=id(editor): self.editor_accepted(eid)) + editor.rejected.connect( + lambda eid=id(editor): self.editor_rejected(eid)) + editor.show() + + def editor_accepted(self, editor_id): + data = self._editors[editor_id] + if not data['readonly']: + index = data['model'].get_index_from_key(data['key']) + value = data['editor'].get_value() + conv_func = data.get('conv', lambda v: v) + self.set_value(index, conv_func(value)) + self._editors.pop(editor_id) + self.free_memory() + + def editor_rejected(self, editor_id): + self._editors.pop(editor_id) + self.free_memory() + + def free_memory(self): + """Free memory after closing an editor.""" + gc.collect() + + def commitAndCloseEditor(self): + """Overriding method commitAndCloseEditor""" + editor = self.sender() + # Avoid a segfault with PyQt5. Variable value won't be changed + # but at least Spyder won't crash. It seems generated by a + # bug in sip. See + # http://comments.gmane.org/gmane.comp.python.pyqt-pykde/26544 + try: + self.commitData.emit(editor) + except AttributeError: + pass + self.closeEditor.emit(editor, QAbstractItemDelegate.NoHint) + + def setEditorData(self, editor, index): + """ + Overriding method setEditorData + Model --> Editor + """ + value = self.get_value(index) + if isinstance(editor, QLineEdit): + if is_binary_string(value): + try: + value = to_text_string(value, 'utf8') + except: + pass + if not is_text_string(value): + value = repr(value) + editor.setText(value) + elif isinstance(editor, QDateEdit): + editor.setDate(value) + elif isinstance(editor, QDateTimeEdit): + editor.setDateTime(QDateTime(value.date(), value.time())) + + def setModelData(self, editor, model, index): + """ + Overriding method setModelData + Editor --> Model + """ + if not hasattr(model, "set_value"): + # Read-only mode + return + + if isinstance(editor, QLineEdit): + value = editor.text() + try: + value = display_to_value(to_qvariant(value), + self.get_value(index), + ignore_errors=False) + except Exception as msg: + raise + QMessageBox.critical(editor, _("Edit item"), + _("Unable to assign data to item." + "

    Error message:
    %s" + ) % str(msg)) + return + elif isinstance(editor, QDateEdit): + qdate = editor.date() + value = datetime.date( qdate.year(), qdate.month(), qdate.day() ) + elif isinstance(editor, QDateTimeEdit): + qdatetime = editor.dateTime() + qdate = qdatetime.date() + qtime = qdatetime.time() + value = datetime.datetime( qdate.year(), qdate.month(), + qdate.day(), qtime.hour(), + qtime.minute(), qtime.second() ) + else: + # Should not happen... + raise RuntimeError("Unsupported editor widget") + self.set_value(index, value) + + +class BaseTableView(QTableView): + """Base collection editor table view""" + sig_option_changed = Signal(str, object) + sig_files_dropped = Signal(list) + redirect_stdio = Signal(bool) + + def __init__(self, parent): + QTableView.__init__(self, parent) + self.array_filename = None + self.menu = None + self.empty_ws_menu = None + self.paste_action = None + self.copy_action = None + self.edit_action = None + self.plot_action = None + self.hist_action = None + self.imshow_action = None + self.save_array_action = None + self.insert_action = None + self.remove_action = None + self.minmax_action = None + self.rename_action = None + self.duplicate_action = None + self.delegate = None + self.setAcceptDrops(True) + + def setup_table(self): + """Setup table""" + self.horizontalHeader().setStretchLastSection(True) + self.adjust_columns() + # Sorting columns + self.setSortingEnabled(True) + self.sortByColumn(0, Qt.AscendingOrder) + + def setup_menu(self, minmax): + """Setup context menu""" + if self.minmax_action is not None: + self.minmax_action.setChecked(minmax) + return + + resize_action = create_action(self, _("Resize rows to contents"), + triggered=self.resizeRowsToContents) + self.paste_action = create_action(self, _("Paste"), + icon=ima.icon('editpaste'), + triggered=self.paste) + self.copy_action = create_action(self, _("Copy"), + icon=ima.icon('editcopy'), + triggered=self.copy) + self.edit_action = create_action(self, _("Edit"), + icon=ima.icon('edit'), + triggered=self.edit_item) + self.plot_action = create_action(self, _("Plot"), + icon=ima.icon('plot'), + triggered=lambda: self.plot_item('plot')) + self.plot_action.setVisible(False) + self.hist_action = create_action(self, _("Histogram"), + icon=ima.icon('hist'), + triggered=lambda: self.plot_item('hist')) + self.hist_action.setVisible(False) + self.imshow_action = create_action(self, _("Show image"), + icon=ima.icon('imshow'), + triggered=self.imshow_item) + self.imshow_action.setVisible(False) + self.save_array_action = create_action(self, _("Save array"), + icon=ima.icon('filesave'), + triggered=self.save_array) + self.save_array_action.setVisible(False) + self.insert_action = create_action(self, _("Insert"), + icon=ima.icon('insert'), + triggered=self.insert_item) + self.remove_action = create_action(self, _("Remove"), + icon=ima.icon('editdelete'), + triggered=self.remove_item) + self.minmax_action = create_action(self, _("Show arrays min/max"), + toggled=self.toggle_minmax) + self.minmax_action.setChecked(minmax) + self.toggle_minmax(minmax) + self.rename_action = create_action(self, _("Rename"), + icon=ima.icon('rename'), + triggered=self.rename_item) + self.duplicate_action = create_action(self, _("Duplicate"), + icon=ima.icon('edit_add'), + triggered=self.duplicate_item) + menu = QMenu(self) + menu_actions = [self.edit_action, self.plot_action, self.hist_action, + self.imshow_action, self.save_array_action, + self.insert_action, self.remove_action, + self.copy_action, self.paste_action, + None, self.rename_action, self.duplicate_action, + None, resize_action] + if ndarray is not FakeObject: + menu_actions.append(self.minmax_action) + add_actions(menu, menu_actions) + self.empty_ws_menu = QMenu(self) + add_actions(self.empty_ws_menu, + [self.insert_action, self.paste_action, + None, resize_action]) + return menu + + #------ Remote/local API --------------------------------------------------- + def remove_values(self, keys): + """Remove values from data""" + raise NotImplementedError + + def copy_value(self, orig_key, new_key): + """Copy value""" + raise NotImplementedError + + def new_value(self, key, value): + """Create new value in data""" + raise NotImplementedError + + def is_list(self, key): + """Return True if variable is a list or a tuple""" + raise NotImplementedError + + def get_len(self, key): + """Return sequence length""" + raise NotImplementedError + + def is_array(self, key): + """Return True if variable is a numpy array""" + raise NotImplementedError + + def is_image(self, key): + """Return True if variable is a PIL.Image image""" + raise NotImplementedError + + def is_dict(self, key): + """Return True if variable is a dictionary""" + raise NotImplementedError + + def get_array_shape(self, key): + """Return array's shape""" + raise NotImplementedError + + def get_array_ndim(self, key): + """Return array's ndim""" + raise NotImplementedError + + def oedit(self, key): + """Edit item""" + raise NotImplementedError + + def plot(self, key, funcname): + """Plot item""" + raise NotImplementedError + + def imshow(self, key): + """Show item's image""" + raise NotImplementedError + + def show_image(self, key): + """Show image (item is a PIL image)""" + raise NotImplementedError + #--------------------------------------------------------------------------- + + def refresh_menu(self): + """Refresh context menu""" + index = self.currentIndex() + condition = index.isValid() + self.edit_action.setEnabled( condition ) + self.remove_action.setEnabled( condition ) + self.refresh_plot_entries(index) + + def refresh_plot_entries(self, index): + if index.isValid(): + key = self.model.get_key(index) + is_list = self.is_list(key) + is_array = self.is_array(key) and self.get_len(key) != 0 + condition_plot = (is_array and len(self.get_array_shape(key)) <= 2) + condition_hist = (is_array and self.get_array_ndim(key) == 1) + condition_imshow = condition_plot and self.get_array_ndim(key) == 2 + condition_imshow = condition_imshow or self.is_image(key) + else: + is_array = condition_plot = condition_imshow = is_list \ + = condition_hist = False + self.plot_action.setVisible(condition_plot or is_list) + self.hist_action.setVisible(condition_hist or is_list) + self.imshow_action.setVisible(condition_imshow) + self.save_array_action.setVisible(is_array) + + def adjust_columns(self): + """Resize two first columns to contents""" + for col in range(3): + self.resizeColumnToContents(col) + + def set_data(self, data): + """Set table data""" + if data is not None: + self.model.set_data(data, self.dictfilter) + self.sortByColumn(0, Qt.AscendingOrder) + + def mousePressEvent(self, event): + """Reimplement Qt method""" + if event.button() != Qt.LeftButton: + QTableView.mousePressEvent(self, event) + return + index_clicked = self.indexAt(event.pos()) + if index_clicked.isValid(): + if index_clicked == self.currentIndex() \ + and index_clicked in self.selectedIndexes(): + self.clearSelection() + else: + QTableView.mousePressEvent(self, event) + else: + self.clearSelection() + event.accept() + + def mouseDoubleClickEvent(self, event): + """Reimplement Qt method""" + index_clicked = self.indexAt(event.pos()) + if index_clicked.isValid(): + row = index_clicked.row() + # TODO: Remove hard coded "Value" column number (3 here) + index_clicked = index_clicked.child(row, 3) + self.edit(index_clicked) + else: + event.accept() + + def keyPressEvent(self, event): + """Reimplement Qt methods""" + if event.key() == Qt.Key_Delete: + self.remove_item() + elif event.key() == Qt.Key_F2: + self.rename_item() + elif event == QKeySequence.Copy: + self.copy() + elif event == QKeySequence.Paste: + self.paste() + else: + QTableView.keyPressEvent(self, event) + + def contextMenuEvent(self, event): + """Reimplement Qt method""" + if self.model.showndata: + self.refresh_menu() + self.menu.popup(event.globalPos()) + event.accept() + else: + self.empty_ws_menu.popup(event.globalPos()) + event.accept() + + def dragEnterEvent(self, event): + """Allow user to drag files""" + if mimedata2url(event.mimeData()): + event.accept() + else: + event.ignore() + + def dragMoveEvent(self, event): + """Allow user to move files""" + if mimedata2url(event.mimeData()): + event.setDropAction(Qt.CopyAction) + event.accept() + else: + event.ignore() + + def dropEvent(self, event): + """Allow user to drop supported files""" + urls = mimedata2url(event.mimeData()) + if urls: + event.setDropAction(Qt.CopyAction) + event.accept() + self.sig_files_dropped.emit(urls) + else: + event.ignore() + + @Slot(bool) + def toggle_minmax(self, state): + """Toggle min/max display for numpy arrays""" + self.sig_option_changed.emit('minmax', state) + self.model.minmax = state + + @Slot() + def edit_item(self): + """Edit item""" + index = self.currentIndex() + if not index.isValid(): + return + # TODO: Remove hard coded "Value" column number (3 here) + self.edit(index.child(index.row(), 3)) + + @Slot() + def remove_item(self): + """Remove item""" + indexes = self.selectedIndexes() + if not indexes: + return + for index in indexes: + if not index.isValid(): + return + one = _("Do you want to remove the selected item?") + more = _("Do you want to remove all selected items?") + answer = QMessageBox.question(self, _( "Remove"), + one if len(indexes) == 1 else more, + QMessageBox.Yes | QMessageBox.No) + if answer == QMessageBox.Yes: + idx_rows = unsorted_unique([idx.row() for idx in indexes]) + keys = [ self.model.keys[idx_row] for idx_row in idx_rows ] + self.remove_values(keys) + + def copy_item(self, erase_original=False): + """Copy item""" + indexes = self.selectedIndexes() + if not indexes: + return + idx_rows = unsorted_unique([idx.row() for idx in indexes]) + if len(idx_rows) > 1 or not indexes[0].isValid(): + return + orig_key = self.model.keys[idx_rows[0]] + if erase_original: + title = _('Rename') + field_text = _('New variable name:') + else: + title = _('Duplicate') + field_text = _('Variable name:') + new_key, valid = QInputDialog.getText(self, title, field_text, + QLineEdit.Normal, orig_key) + if valid and to_text_string(new_key): + new_key = try_to_eval(to_text_string(new_key)) + if new_key == orig_key: + return + self.copy_value(orig_key, new_key) + if erase_original: + self.remove_values([orig_key]) + + @Slot() + def duplicate_item(self): + """Duplicate item""" + self.copy_item() + + @Slot() + def rename_item(self): + """Rename item""" + self.copy_item(True) + + @Slot() + def insert_item(self): + """Insert item""" + index = self.currentIndex() + if not index.isValid(): + row = self.model.rowCount() + else: + row = index.row() + data = self.model.get_data() + if isinstance(data, list): + key = row + data.insert(row, '') + elif isinstance(data, dict): + key, valid = QInputDialog.getText(self, _( 'Insert'), _( 'Key:'), + QLineEdit.Normal) + if valid and to_text_string(key): + key = try_to_eval(to_text_string(key)) + else: + return + else: + return + value, valid = QInputDialog.getText(self, _('Insert'), _('Value:'), + QLineEdit.Normal) + if valid and to_text_string(value): + self.new_value(key, try_to_eval(to_text_string(value))) + + def __prepare_plot(self): + try: + import guiqwt.pyplot #analysis:ignore + return True + except (ImportError, AssertionError): + try: + if 'matplotlib' not in sys.modules: + import matplotlib + matplotlib.use("Qt4Agg") + return True + except ImportError: + QMessageBox.warning(self, _("Import error"), + _("Please install matplotlib" + " or guiqwt.")) + + def plot_item(self, funcname): + """Plot item""" + index = self.currentIndex() + if self.__prepare_plot(): + key = self.model.get_key(index) + try: + self.plot(key, funcname) + except (ValueError, TypeError) as error: + QMessageBox.critical(self, _( "Plot"), + _("Unable to plot data." + "

    Error message:
    %s" + ) % str(error)) + + @Slot() + def imshow_item(self): + """Imshow item""" + index = self.currentIndex() + if self.__prepare_plot(): + key = self.model.get_key(index) + try: + if self.is_image(key): + self.show_image(key) + else: + self.imshow(key) + except (ValueError, TypeError) as error: + QMessageBox.critical(self, _( "Plot"), + _("Unable to show image." + "

    Error message:
    %s" + ) % str(error)) + + @Slot() + def save_array(self): + """Save array""" + title = _( "Save array") + if self.array_filename is None: + self.array_filename = getcwd() + self.redirect_stdio.emit(False) + filename, _selfilter = getsavefilename(self, title, + self.array_filename, + _("NumPy arrays")+" (*.npy)") + self.redirect_stdio.emit(True) + if filename: + self.array_filename = filename + data = self.delegate.get_value( self.currentIndex() ) + try: + import numpy as np + np.save(self.array_filename, data) + except Exception as error: + QMessageBox.critical(self, title, + _("Unable to save array" + "

    Error message:
    %s" + ) % str(error)) + + @Slot() + def copy(self): + """Copy text to clipboard""" + clipboard = QApplication.clipboard() + clipl = [] + for idx in self.selectedIndexes(): + if not idx.isValid(): + continue + obj = self.delegate.get_value(idx) + # Check if we are trying to copy a numpy array, and if so make sure + # to copy the whole thing in a tab separated format + if isinstance(obj, (ndarray, MaskedArray)) \ + and ndarray is not FakeObject: + if PY3: + output = io.BytesIO() + else: + output = io.StringIO() + try: + np_savetxt(output, obj, delimiter='\t') + except: + QMessageBox.warning(self, _("Warning"), + _("It was not possible to copy " + "this array")) + return + obj = output.getvalue().decode('utf-8') + output.close() + elif isinstance(obj, (DataFrame, Series)) \ + and DataFrame is not FakeObject: + output = io.StringIO() + obj.to_csv(output, sep='\t', index=True, header=True) + if PY3: + obj = output.getvalue() + else: + obj = output.getvalue().decode('utf-8') + output.close() + elif is_binary_string(obj): + obj = to_text_string(obj, 'utf8') + else: + obj = to_text_string(obj) + clipl.append(obj) + clipboard.setText('\n'.join(clipl)) + + def import_from_string(self, text, title=None): + """Import data from string""" + data = self.model.get_data() + editor = ImportWizard(self, text, title=title, + contents_title=_("Clipboard contents"), + varname=fix_reference_name("data", + blacklist=list(data.keys()))) + if editor.exec_(): + var_name, clip_data = editor.get_data() + self.new_value(var_name, clip_data) + + @Slot() + def paste(self): + """Import text/data/code from clipboard""" + clipboard = QApplication.clipboard() + cliptext = '' + if clipboard.mimeData().hasText(): + cliptext = to_text_string(clipboard.text()) + if cliptext.strip(): + self.import_from_string(cliptext, title=_("Import from clipboard")) + else: + QMessageBox.warning(self, _( "Empty clipboard"), + _("Nothing to be imported from clipboard.")) + + +class CollectionsEditorTableView(BaseTableView): + """CollectionsEditor table view""" + def __init__(self, parent, data, readonly=False, title="", + names=False, minmax=False): + BaseTableView.__init__(self, parent) + self.dictfilter = None + self.readonly = readonly or isinstance(data, tuple) + CollectionsModelClass = ReadOnlyCollectionsModel if self.readonly \ + else CollectionsModel + self.model = CollectionsModelClass(self, data, title, names=names, + minmax=minmax) + self.setModel(self.model) + self.delegate = CollectionsDelegate(self) + self.setItemDelegate(self.delegate) + + self.setup_table() + self.menu = self.setup_menu(minmax) + + #------ Remote/local API --------------------------------------------------- + def remove_values(self, keys): + """Remove values from data""" + data = self.model.get_data() + for key in sorted(keys, reverse=True): + data.pop(key) + self.set_data(data) + + def copy_value(self, orig_key, new_key): + """Copy value""" + data = self.model.get_data() + data[new_key] = data[orig_key] + self.set_data(data) + + def new_value(self, key, value): + """Create new value in data""" + data = self.model.get_data() + data[key] = value + self.set_data(data) + + def is_list(self, key): + """Return True if variable is a list or a tuple""" + data = self.model.get_data() + return isinstance(data[key], (tuple, list)) + + def get_len(self, key): + """Return sequence length""" + data = self.model.get_data() + return len(data[key]) + + def is_array(self, key): + """Return True if variable is a numpy array""" + data = self.model.get_data() + return isinstance(data[key], (ndarray, MaskedArray)) + + def is_image(self, key): + """Return True if variable is a PIL.Image image""" + data = self.model.get_data() + return isinstance(data[key], Image) + + def is_dict(self, key): + """Return True if variable is a dictionary""" + data = self.model.get_data() + return isinstance(data[key], dict) + + def get_array_shape(self, key): + """Return array's shape""" + data = self.model.get_data() + return data[key].shape + + def get_array_ndim(self, key): + """Return array's ndim""" + data = self.model.get_data() + return data[key].ndim + + def oedit(self, key): + """Edit item""" + data = self.model.get_data() + from spyder.widgets.variableexplorer.objecteditor import oedit + oedit(data[key]) + + def plot(self, key, funcname): + """Plot item""" + data = self.model.get_data() + import spyder.pyplot as plt + plt.figure() + getattr(plt, funcname)(data[key]) + plt.show() + + def imshow(self, key): + """Show item's image""" + data = self.model.get_data() + import spyder.pyplot as plt + plt.figure() + plt.imshow(data[key]) + plt.show() + + def show_image(self, key): + """Show image (item is a PIL image)""" + data = self.model.get_data() + data[key].show() + #--------------------------------------------------------------------------- + + def refresh_menu(self): + """Refresh context menu""" + data = self.model.get_data() + index = self.currentIndex() + condition = (not isinstance(data, tuple)) and index.isValid() \ + and not self.readonly + self.edit_action.setEnabled( condition ) + self.remove_action.setEnabled( condition ) + self.insert_action.setEnabled( not self.readonly ) + self.refresh_plot_entries(index) + + def set_filter(self, dictfilter=None): + """Set table dict filter""" + self.dictfilter = dictfilter + + +class CollectionsEditorWidget(QWidget): + """Dictionary Editor Widget""" + def __init__(self, parent, data, readonly=False, title="", remote=False): + QWidget.__init__(self, parent) + if remote: + self.editor = RemoteCollectionsEditorTableView(self, data, readonly) + else: + self.editor = CollectionsEditorTableView(self, data, readonly, + title) + layout = QVBoxLayout() + layout.addWidget(self.editor) + self.setLayout(layout) + + def set_data(self, data): + """Set DictEditor data""" + self.editor.set_data(data) + + def get_title(self): + """Get model title""" + return self.editor.model.title + + +class CollectionsEditor(QDialog): + """Collections Editor Dialog""" + def __init__(self, parent=None): + QDialog.__init__(self, parent) + + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + + self.data_copy = None + self.widget = None + + def setup(self, data, title='', readonly=False, width=500, remote=False, + icon=None, parent=None): + if isinstance(data, dict): + # dictionnary + self.data_copy = data.copy() + datalen = len(data) + elif isinstance(data, (tuple, list)): + # list, tuple + self.data_copy = data[:] + datalen = len(data) + else: + # unknown object + import copy + self.data_copy = copy.deepcopy(data) + datalen = len(dir(data)) + self.widget = CollectionsEditorWidget(self, self.data_copy, title=title, + readonly=readonly, remote=remote) + + layout = QVBoxLayout() + layout.addWidget(self.widget) + self.setLayout(layout) + + # Buttons configuration + buttons = QDialogButtonBox.Ok + if not readonly: + buttons = buttons | QDialogButtonBox.Cancel + bbox = QDialogButtonBox(buttons) + bbox.accepted.connect(self.accept) + if not readonly: + bbox.rejected.connect(self.reject) + layout.addWidget(bbox) + + constant = 121 + row_height = 30 + error_margin = 20 + height = constant + row_height*min([15, datalen]) + error_margin + self.resize(width, height) + + self.setWindowTitle(self.widget.get_title()) + if icon is None: + self.setWindowIcon(ima.icon('dictedit')) + # Make the dialog act as a window + self.setWindowFlags(Qt.Window) + + def get_value(self): + """Return modified copy of dictionary or list""" + # It is import to avoid accessing Qt C++ object as it has probably + # already been destroyed, due to the Qt.WA_DeleteOnClose attribute + return self.data_copy + + +class DictEditor(CollectionsEditor): + def __init__(self, parent=None): + warnings.warn("`DictEditor` has been renamed to `CollectionsEditor` in " + "Spyder 3. Please use `CollectionsEditor` instead", + RuntimeWarning) + CollectionsEditor.__init__(self, parent) + + +#----Remote versions of CollectionsDelegate and CollectionsEditorTableView +class RemoteCollectionsDelegate(CollectionsDelegate): + """CollectionsEditor Item Delegate""" + def __init__(self, parent=None, get_value_func=None, set_value_func=None): + CollectionsDelegate.__init__(self, parent) + self.get_value_func = get_value_func + self.set_value_func = set_value_func + + def get_value(self, index): + if index.isValid(): + name = index.model().keys[index.row()] + return self.get_value_func(name) + + def set_value(self, index, value): + if index.isValid(): + name = index.model().keys[index.row()] + self.set_value_func(name, value) + + +class RemoteCollectionsEditorTableView(BaseTableView): + """DictEditor table view""" + def __init__(self, parent, data, minmax=False, + get_value_func=None, set_value_func=None, + new_value_func=None, remove_values_func=None, + copy_value_func=None, is_list_func=None, get_len_func=None, + is_array_func=None, is_image_func=None, is_dict_func=None, + get_array_shape_func=None, get_array_ndim_func=None, + oedit_func=None, plot_func=None, imshow_func=None, + is_data_frame_func=None, is_series_func=None, + show_image_func=None, remote_editing=False): + BaseTableView.__init__(self, parent) + + self.remote_editing_enabled = None + + self.remove_values = remove_values_func + self.copy_value = copy_value_func + self.new_value = new_value_func + + self.is_data_frame = is_data_frame_func + self.is_series = is_series_func + self.is_list = is_list_func + self.get_len = get_len_func + self.is_array = is_array_func + self.is_image = is_image_func + self.is_dict = is_dict_func + self.get_array_shape = get_array_shape_func + self.get_array_ndim = get_array_ndim_func + self.oedit = oedit_func + self.plot = plot_func + self.imshow = imshow_func + self.show_image = show_image_func + + self.dictfilter = None + self.model = None + self.delegate = None + self.readonly = False + self.model = CollectionsModel(self, data, names=True, + minmax=minmax, + remote=True) + self.setModel(self.model) + self.delegate = RemoteCollectionsDelegate(self, get_value_func, + set_value_func) + self.setItemDelegate(self.delegate) + + self.setup_table() + self.menu = self.setup_menu(minmax) + + def setup_menu(self, minmax): + """Setup context menu""" + menu = BaseTableView.setup_menu(self, minmax) + return menu + + def oedit_possible(self, key): + if (self.is_list(key) or self.is_dict(key) + or self.is_array(key) or self.is_image(key) + or self.is_data_frame(key) or self.is_series(key)): + # If this is a remote dict editor, the following avoid + # transfering large amount of data through the socket + return True + + def edit_item(self): + """ + Reimplement BaseTableView's method to edit item + + Some supported data types are directly edited in the remote process, + thus avoiding to transfer large amount of data through the socket from + the remote process to Spyder + """ + if self.remote_editing_enabled: + index = self.currentIndex() + if not index.isValid(): + return + key = self.model.get_key(index) + if self.oedit_possible(key): + # If this is a remote dict editor, the following avoid + # transfering large amount of data through the socket + self.oedit(key) + else: + BaseTableView.edit_item(self) + else: + BaseTableView.edit_item(self) + + +#============================================================================== +# Tests +#============================================================================== +def get_test_data(): + """Create test data""" + import numpy as np + from spyder.pil_patch import Image + image = Image.fromarray(np.random.random_integers(255, size=(100, 100)), + mode='P') + testdict = {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]} + testdate = datetime.date(1945, 5, 8) + class Foobar(object): + def __init__(self): + self.text = "toto" + self.testdict = testdict + self.testdate = testdate + foobar = Foobar() + return {'object': foobar, + 'str': 'kjkj kj k j j kj k jkj', + 'unicode': to_text_string('éù', 'utf-8'), + 'list': [1, 3, [sorted, 5, 6], 'kjkj', None], + 'tuple': ([1, testdate, testdict], 'kjkj', None), + 'dict': testdict, + 'float': 1.2233, + 'int': 223, + 'bool': True, + 'array': np.random.rand(10, 10), + 'masked_array': np.ma.array([[1, 0], [1, 0]], + mask=[[True, False], [False, False]]), + '1D-array': np.linspace(-10, 10), + 'empty_array': np.array([]), + 'image': image, + 'date': testdate, + 'datetime': datetime.datetime(1945, 5, 8), + 'complex': 2+1j, + 'complex64': np.complex64(2+1j), + 'int8_scalar': np.int8(8), + 'int16_scalar': np.int16(16), + 'int32_scalar': np.int32(32), + 'bool_scalar': np.bool(8), + 'unsupported1': np.arccos, + 'unsupported2': np.cast, + # Test for Issue #3518 + 'big_struct_array': np.zeros(1000, dtype=[('ID', 'f8'), + ('param1', 'f8', 5000)]), + } + + +def editor_test(): + """Collections editor test""" + from spyder.utils.qthelpers import qapplication + + app = qapplication() #analysis:ignore + dialog = CollectionsEditor() + dialog.setup(get_test_data()) + dialog.show() + app.exec_() + + +def remote_editor_test(): + """Remote collections editor test""" + from spyder.utils.qthelpers import qapplication + app = qapplication() + + from spyder.plugins.variableexplorer import VariableExplorer + from spyder.widgets.variableexplorer.utils import make_remote_view + + remote = make_remote_view(get_test_data(), VariableExplorer.get_settings()) + dialog = CollectionsEditor() + dialog.setup(remote, remote=True) + dialog.show() + app.exec_() + + +if __name__ == "__main__": + editor_test() + remote_editor_test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/dataframeeditor.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/dataframeeditor.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/dataframeeditor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/dataframeeditor.py 2016-11-08 01:15:20.000000000 +0000 @@ -0,0 +1,669 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the New BSD License +# +# DataFrameModel is based on the class ArrayModel from array editor +# and the class DataFrameModel from the pandas project. +# Present in pandas.sandbox.qtpandas in v0.13.1 +# Copyright (c) 2011-2012, Lambda Foundry, Inc. +# and PyData Development Team All rights reserved + +""" +Pandas DataFrame Editor Dialog +""" + +# Third party imports +from pandas import DataFrame, Series +from qtpy import API +from qtpy.compat import from_qvariant, to_qvariant +from qtpy.QtCore import QAbstractTableModel, QModelIndex, Qt, Slot +from qtpy.QtGui import QColor, QCursor, QKeySequence +from qtpy.QtWidgets import (QApplication, QCheckBox, QDialogButtonBox, QDialog, + QGridLayout, QHBoxLayout, QInputDialog, QLineEdit, + QMenu, QMessageBox, QPushButton, QTableView) +import numpy as np + +# Local imports +from spyder.config.base import _ +from spyder.config.fonts import DEFAULT_SMALL_DELTA +from spyder.config.gui import get_font, fixed_shortcut +from spyder.py3compat import io, is_text_string, PY2, to_text_string +from spyder.utils import encoding +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import (add_actions, create_action, + keybinding, qapplication) +from spyder.widgets.variableexplorer.arrayeditor import get_idx_rect + +# Supported Numbers and complex numbers +REAL_NUMBER_TYPES = (float, int, np.int64, np.int32) +COMPLEX_NUMBER_TYPES = (complex, np.complex64, np.complex128) +# Used to convert bool intrance to false since bool('False') will return True +_bool_false = ['false', '0'] + +# Limit at which dataframe is considered so large that it is loaded on demand +LARGE_SIZE = 5e5 +LARGE_NROWS = 1e5 +LARGE_COLS = 60 + +# Background colours +BACKGROUND_NUMBER_MINHUE = 0.66 # hue for largest number +BACKGROUND_NUMBER_HUERANGE = 0.33 # (hue for smallest) minus (hue for largest) +BACKGROUND_NUMBER_SATURATION = 0.7 +BACKGROUND_NUMBER_VALUE = 1.0 +BACKGROUND_NUMBER_ALPHA = 0.6 +BACKGROUND_NONNUMBER_COLOR = Qt.lightGray +BACKGROUND_INDEX_ALPHA = 0.8 +BACKGROUND_STRING_ALPHA = 0.05 +BACKGROUND_MISC_ALPHA = 0.3 + + +def bool_false_check(value): + """ + Used to convert bool intrance to false since any string in bool('') + will return True + """ + if value.lower() in _bool_false: + value = '' + return value + + +def global_max(col_vals, index): + """Returns the global maximum and minimum""" + col_vals_without_None = [x for x in col_vals if x is not None] + max_col, min_col = zip(*col_vals_without_None) + return max(max_col), min(min_col) + + +class DataFrameModel(QAbstractTableModel): + """ DataFrame Table Model""" + + ROWS_TO_LOAD = 500 + COLS_TO_LOAD = 40 + + def __init__(self, dataFrame, format="%.3g", parent=None): + QAbstractTableModel.__init__(self) + self.dialog = parent + self.df = dataFrame + self.df_index = dataFrame.index.tolist() + self.df_header = dataFrame.columns.tolist() + self._format = format + self.complex_intran = None + + self.total_rows = self.df.shape[0] + self.total_cols = self.df.shape[1] + size = self.total_rows * self.total_cols + + self.max_min_col = None + if size < LARGE_SIZE: + self.max_min_col_update() + self.colum_avg_enabled = True + self.bgcolor_enabled = True + self.colum_avg(1) + else: + self.colum_avg_enabled = False + self.bgcolor_enabled = False + self.colum_avg(0) + + # Use paging when the total size, number of rows or number of + # columns is too large + if size > LARGE_SIZE: + self.rows_loaded = self.ROWS_TO_LOAD + self.cols_loaded = self.COLS_TO_LOAD + else: + if self.total_rows > LARGE_NROWS: + self.rows_loaded = self.ROWS_TO_LOAD + else: + self.rows_loaded = self.total_rows + if self.total_cols > LARGE_COLS: + self.cols_loaded = self.COLS_TO_LOAD + else: + self.cols_loaded = self.total_cols + + def max_min_col_update(self): + """ + Determines the maximum and minimum number in each column. + + The result is a list whose k-th entry is [vmax, vmin], where vmax and + vmin denote the maximum and minimum of the k-th column (ignoring NaN). + This list is stored in self.max_min_col. + + If the k-th column has a non-numerical dtype, then the k-th entry + is set to None. If the dtype is complex, then compute the maximum and + minimum of the absolute values. If vmax equals vmin, then vmin is + decreased by one. + """ + if self.df.shape[0] == 0: # If no rows to compute max/min then return + return + self.max_min_col = [] + for dummy, col in self.df.iteritems(): + if col.dtype in REAL_NUMBER_TYPES + COMPLEX_NUMBER_TYPES: + if col.dtype in REAL_NUMBER_TYPES: + vmax = col.max(skipna=True) + vmin = col.min(skipna=True) + else: + vmax = col.abs().max(skipna=True) + vmin = col.abs().min(skipna=True) + if vmax != vmin: + max_min = [vmax, vmin] + else: + max_min = [vmax, vmin - 1] + else: + max_min = None + self.max_min_col.append(max_min) + + def get_format(self): + """Return current format""" + # Avoid accessing the private attribute _format from outside + return self._format + + def set_format(self, format): + """Change display format""" + self._format = format + self.reset() + + def bgcolor(self, state): + """Toggle backgroundcolor""" + self.bgcolor_enabled = state > 0 + self.reset() + + def colum_avg(self, state): + """Toggle backgroundcolor""" + self.colum_avg_enabled = state > 0 + if self.colum_avg_enabled: + self.return_max = lambda col_vals, index: col_vals[index] + else: + self.return_max = global_max + self.reset() + + def headerData(self, section, orientation, role=Qt.DisplayRole): + """Set header data""" + if role != Qt.DisplayRole: + return to_qvariant() + + if orientation == Qt.Horizontal: + if section == 0: + return 'Index' + elif section == 1 and PY2: + # Get rid of possible BOM utf-8 data present at the + # beginning of a file, which gets attached to the first + # column header when headers are present in the first + # row. + # Fixes Issue 2514 + try: + header = to_text_string(self.df_header[0], + encoding='utf-8-sig') + except: + header = to_text_string(self.df_header[0]) + return to_qvariant(header) + else: + return to_qvariant(to_text_string(self.df_header[section-1])) + else: + return to_qvariant() + + def get_bgcolor(self, index): + """Background color depending on value""" + column = index.column() + if column == 0: + color = QColor(BACKGROUND_NONNUMBER_COLOR) + color.setAlphaF(BACKGROUND_INDEX_ALPHA) + return color + if not self.bgcolor_enabled: + return + value = self.get_value(index.row(), column-1) + if self.max_min_col[column - 1] is None: + color = QColor(BACKGROUND_NONNUMBER_COLOR) + if is_text_string(value): + color.setAlphaF(BACKGROUND_STRING_ALPHA) + else: + color.setAlphaF(BACKGROUND_MISC_ALPHA) + else: + if isinstance(value, COMPLEX_NUMBER_TYPES): + color_func = abs + else: + color_func = float + vmax, vmin = self.return_max(self.max_min_col, column-1) + hue = (BACKGROUND_NUMBER_MINHUE + BACKGROUND_NUMBER_HUERANGE * + (vmax - color_func(value)) / (vmax - vmin)) + hue = float(abs(hue)) + if hue > 1: + hue = 1 + color = QColor.fromHsvF(hue, BACKGROUND_NUMBER_SATURATION, + BACKGROUND_NUMBER_VALUE, BACKGROUND_NUMBER_ALPHA) + return color + + def get_value(self, row, column): + """Returns the value of the DataFrame""" + # To increase the performance iat is used but that requires error + # handling, so fallback uses iloc + try: + value = self.df.iat[row, column] + except: + value = self.df.iloc[row, column] + return value + + def update_df_index(self): + """"Update the DataFrame index""" + self.df_index = self.df.index.tolist() + + def data(self, index, role=Qt.DisplayRole): + """Cell content""" + if not index.isValid(): + return to_qvariant() + if role == Qt.DisplayRole or role == Qt.EditRole: + column = index.column() + row = index.row() + if column == 0: + return to_qvariant(to_text_string(self.df_index[row])) + else: + value = self.get_value(row, column-1) + if isinstance(value, float): + return to_qvariant(self._format % value) + else: + try: + return to_qvariant(to_text_string(value)) + except UnicodeDecodeError: + return to_qvariant(encoding.to_unicode(value)) + elif role == Qt.BackgroundColorRole: + return to_qvariant(self.get_bgcolor(index)) + elif role == Qt.FontRole: + return to_qvariant(get_font(font_size_delta=DEFAULT_SMALL_DELTA)) + return to_qvariant() + + def sort(self, column, order=Qt.AscendingOrder): + """Overriding sort method""" + if self.complex_intran is not None: + if self.complex_intran.any(axis=0).iloc[column-1]: + QMessageBox.critical(self.dialog, "Error", + "TypeError error: no ordering " + "relation is defined for complex numbers") + return False + try: + ascending = order == Qt.AscendingOrder + if column > 0: + try: + self.df.sort_values(by=self.df.columns[column-1], + ascending=ascending, inplace=True, + kind='mergesort') + except AttributeError: + # for pandas version < 0.17 + self.df.sort(columns=self.df.columns[column-1], + ascending=ascending, inplace=True, + kind='mergesort') + self.update_df_index() + else: + self.df.sort_index(inplace=True, ascending=ascending) + self.update_df_index() + except TypeError as e: + QMessageBox.critical(self.dialog, "Error", + "TypeError error: %s" % str(e)) + return False + + self.reset() + return True + + def flags(self, index): + """Set flags""" + if index.column() == 0: + return Qt.ItemIsEnabled | Qt.ItemIsSelectable + return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | + Qt.ItemIsEditable) + + def setData(self, index, value, role=Qt.EditRole, change_type=None): + """Cell content change""" + column = index.column() + row = index.row() + + if change_type is not None: + try: + value = self.data(index, role=Qt.DisplayRole) + val = from_qvariant(value, str) + if change_type is bool: + val = bool_false_check(val) + self.df.iloc[row, column - 1] = change_type(val) + except ValueError: + self.df.iloc[row, column - 1] = change_type('0') + else: + val = from_qvariant(value, str) + current_value = self.get_value(row, column-1) + if isinstance(current_value, bool): + val = bool_false_check(val) + supported_types = (bool,) + REAL_NUMBER_TYPES + COMPLEX_NUMBER_TYPES + if (isinstance(current_value, supported_types) or + is_text_string(current_value)): + try: + self.df.iloc[row, column-1] = current_value.__class__(val) + except ValueError as e: + QMessageBox.critical(self.dialog, "Error", + "Value error: %s" % str(e)) + return False + else: + QMessageBox.critical(self.dialog, "Error", + "The type of the cell is not a supported " + "type") + return False + self.max_min_col_update() + return True + + def get_data(self): + """Return data""" + return self.df + + def rowCount(self, index=QModelIndex()): + """DataFrame row number""" + if self.total_rows <= self.rows_loaded: + return self.total_rows + else: + return self.rows_loaded + + def can_fetch_more(self, rows=False, columns=False): + if rows: + if self.total_rows > self.rows_loaded: + return True + else: + return False + if columns: + if self.total_cols > self.cols_loaded: + return True + else: + return False + + def fetch_more(self, rows=False, columns=False): + if self.can_fetch_more(rows=rows): + reminder = self.total_rows - self.rows_loaded + items_to_fetch = min(reminder, self.ROWS_TO_LOAD) + self.beginInsertRows(QModelIndex(), self.rows_loaded, + self.rows_loaded + items_to_fetch - 1) + self.rows_loaded += items_to_fetch + self.endInsertRows() + if self.can_fetch_more(columns=columns): + reminder = self.total_cols - self.cols_loaded + items_to_fetch = min(reminder, self.COLS_TO_LOAD) + self.beginInsertColumns(QModelIndex(), self.cols_loaded, + self.cols_loaded + items_to_fetch - 1) + self.cols_loaded += items_to_fetch + self.endInsertColumns() + + def columnCount(self, index=QModelIndex()): + """DataFrame column number""" + # This is done to implement series + if len(self.df.shape) == 1: + return 2 + elif self.total_cols <= self.cols_loaded: + return self.total_cols + 1 + else: + return self.cols_loaded + 1 + + def reset(self): + self.beginResetModel() + self.endResetModel() + + +class DataFrameView(QTableView): + """Data Frame view class""" + def __init__(self, parent, model): + QTableView.__init__(self, parent) + self.setModel(model) + + self.sort_old = [None] + self.header_class = self.horizontalHeader() + self.header_class.sectionClicked.connect(self.sortByColumn) + self.menu = self.setup_menu() + fixed_shortcut(QKeySequence.Copy, self, self.copy) + self.horizontalScrollBar().valueChanged.connect( + lambda val: self.load_more_data(val, columns=True)) + self.verticalScrollBar().valueChanged.connect( + lambda val: self.load_more_data(val, rows=True)) + + def load_more_data(self, value, rows=False, columns=False): + if rows and value == self.verticalScrollBar().maximum(): + self.model().fetch_more(rows=rows) + if columns and value == self.horizontalScrollBar().maximum(): + self.model().fetch_more(columns=columns) + + def sortByColumn(self, index): + """ Implement a Column sort """ + if self.sort_old == [None]: + self.header_class.setSortIndicatorShown(True) + sort_order = self.header_class.sortIndicatorOrder() + if not self.model().sort(index, sort_order): + if len(self.sort_old) != 2: + self.header_class.setSortIndicatorShown(False) + else: + self.header_class.setSortIndicator(self.sort_old[0], + self.sort_old[1]) + return + self.sort_old = [index, self.header_class.sortIndicatorOrder()] + + def contextMenuEvent(self, event): + """Reimplement Qt method""" + self.menu.popup(event.globalPos()) + event.accept() + + def setup_menu(self): + """Setup context menu""" + copy_action = create_action(self, _('Copy'), + shortcut=keybinding('Copy'), + icon=ima.icon('editcopy'), + triggered=self.copy, + context=Qt.WidgetShortcut) + functions = ((_("To bool"), bool), (_("To complex"), complex), + (_("To int"), int), (_("To float"), float), + (_("To str"), to_text_string)) + types_in_menu = [copy_action] + for name, func in functions: + # QAction.triggered works differently for PySide and PyQt + if not API == 'pyside': + slot = lambda _checked, func=func: self.change_type(func) + else: + slot = lambda func=func: self.change_type(func) + types_in_menu += [create_action(self, name, + triggered=slot, + context=Qt.WidgetShortcut)] + menu = QMenu(self) + add_actions(menu, types_in_menu) + return menu + + def change_type(self, func): + """A function that changes types of cells""" + model = self.model() + index_list = self.selectedIndexes() + [model.setData(i, '', change_type=func) for i in index_list] + + @Slot() + def copy(self): + """Copy text to clipboard""" + if not self.selectedIndexes(): + return + (row_min, row_max, + col_min, col_max) = get_idx_rect(self.selectedIndexes()) + index = header = False + if col_min == 0: + col_min = 1 + index = True + df = self.model().df + if col_max == 0: # To copy indices + contents = '\n'.join(map(str, df.index.tolist()[slice(row_min, + row_max+1)])) + else: # To copy DataFrame + if (col_min == 0 or col_min == 1) and (df.shape[1] == col_max): + header = True + obj = df.iloc[slice(row_min, row_max+1), slice(col_min-1, col_max)] + output = io.StringIO() + obj.to_csv(output, sep='\t', index=index, header=header) + if not PY2: + contents = output.getvalue() + else: + contents = output.getvalue().decode('utf-8') + output.close() + clipboard = QApplication.clipboard() + clipboard.setText(contents) + + +class DataFrameEditor(QDialog): + """ Data Frame Editor Dialog """ + def __init__(self, parent=None): + QDialog.__init__(self, parent) + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + self.is_series = False + self.layout = None + + def setup_and_check(self, data, title=''): + """ + Setup DataFrameEditor: + return False if data is not supported, True otherwise + """ + self.layout = QGridLayout() + self.setLayout(self.layout) + self.setWindowIcon(ima.icon('arredit')) + if title: + title = to_text_string(title) + " - %s" % data.__class__.__name__ + else: + title = _("%s editor") % data.__class__.__name__ + if isinstance(data, Series): + self.is_series = True + data = data.to_frame() + + self.setWindowTitle(title) + self.resize(600, 500) + + self.dataModel = DataFrameModel(data, parent=self) + self.dataTable = DataFrameView(self, self.dataModel) + + self.layout.addWidget(self.dataTable) + self.setLayout(self.layout) + self.setMinimumSize(400, 300) + # Make the dialog act as a window + self.setWindowFlags(Qt.Window) + btn_layout = QHBoxLayout() + + btn = QPushButton(_("Format")) + # disable format button for int type + btn_layout.addWidget(btn) + btn.clicked.connect(self.change_format) + btn = QPushButton(_('Resize')) + btn_layout.addWidget(btn) + btn.clicked.connect(self.resize_to_contents) + + bgcolor = QCheckBox(_('Background color')) + bgcolor.setChecked(self.dataModel.bgcolor_enabled) + bgcolor.setEnabled(self.dataModel.bgcolor_enabled) + bgcolor.stateChanged.connect(self.change_bgcolor_enable) + btn_layout.addWidget(bgcolor) + + self.bgcolor_global = QCheckBox(_('Column min/max')) + self.bgcolor_global.setChecked(self.dataModel.colum_avg_enabled) + self.bgcolor_global.setEnabled(not self.is_series and + self.dataModel.bgcolor_enabled) + self.bgcolor_global.stateChanged.connect(self.dataModel.colum_avg) + btn_layout.addWidget(self.bgcolor_global) + + btn_layout.addStretch() + bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + bbox.accepted.connect(self.accept) + bbox.rejected.connect(self.reject) + btn_layout.addWidget(bbox) + + self.layout.addLayout(btn_layout, 2, 0) + + return True + + def change_bgcolor_enable(self, state): + """ + This is implementet so column min/max is only active when bgcolor is + """ + self.dataModel.bgcolor(state) + self.bgcolor_global.setEnabled(not self.is_series and state > 0) + + def change_format(self): + """Change display format""" + format, valid = QInputDialog.getText(self, _('Format'), + _("Float formatting"), + QLineEdit.Normal, + self.dataModel.get_format()) + if valid: + format = str(format) + try: + format % 1.1 + except: + QMessageBox.critical(self, _("Error"), + _("Format (%s) is incorrect") % format) + return + self.dataModel.set_format(format) + + def get_value(self): + """Return modified Dataframe -- this is *not* a copy""" + # It is import to avoid accessing Qt C++ object as it has probably + # already been destroyed, due to the Qt.WA_DeleteOnClose attribute + df = self.dataModel.get_data() + if self.is_series: + return df.iloc[:, 0] + else: + return df + + def resize_to_contents(self): + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + self.dataTable.resizeColumnsToContents() + self.dataModel.fetch_more(columns=True) + self.dataTable.resizeColumnsToContents() + QApplication.restoreOverrideCursor() + + +#============================================================================== +# Tests +#============================================================================== +def test_edit(data, title="", parent=None): + """Test subroutine""" + app = qapplication() # analysis:ignore + dlg = DataFrameEditor(parent=parent) + + if dlg.setup_and_check(data, title=title): + dlg.exec_() + return dlg.get_value() + else: + import sys + sys.exit(1) + + +def test(): + """DataFrame editor test""" + from numpy import nan + from pandas.util.testing import assert_frame_equal, assert_series_equal + + df1 = DataFrame([ + [True, "bool"], + [1+1j, "complex"], + ['test', "string"], + [1.11, "float"], + [1, "int"], + [np.random.rand(3, 3), "Unkown type"], + ["Large value", 100], + ["áéí", "unicode"] + ], + index=['a', 'b', nan, nan, nan, 'c', + "Test global max", 'd'], + columns=[nan, 'Type']) + out = test_edit(df1) + assert_frame_equal(df1, out) + + result = Series([True, "bool"], index=[nan, 'Type'], name='a') + out = test_edit(df1.iloc[0]) + assert_series_equal(result, out) + + # Sorting large DataFrame takes time + df1 = DataFrame(np.random.rand(100100, 10)) + df1.sort(columns=[0, 1], inplace=True) + out = test_edit(df1) + assert_frame_equal(out, df1) + + series = Series(np.arange(10), name=0) + out = test_edit(series) + assert_series_equal(series, out) + + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/importwizard.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/importwizard.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/importwizard.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/importwizard.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,641 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Text data Importing Wizard based on Qt +""" + +# Standard library imports +from __future__ import print_function +from functools import partial as ft_partial + +# Third party imports +from qtpy.compat import to_qvariant +from qtpy.QtCore import QAbstractTableModel, QModelIndex, Qt, Signal, Slot +from qtpy.QtGui import QColor, QIntValidator +from qtpy.QtWidgets import (QCheckBox, QDialog, QFrame, QGridLayout, QGroupBox, + QHBoxLayout, QLabel, QLineEdit, + QPushButton, QMenu, QMessageBox, QRadioButton, + QSizePolicy, QSpacerItem, QTableView, QTabWidget, + QTextEdit, QVBoxLayout, QWidget) + +try: + import pandas as pd +except ImportError: + pd = None + +# Local import +from spyder.config.base import _ +from spyder.py3compat import (INT_TYPES, io, TEXT_TYPES, to_text_string, + zip_longest) +from spyder.utils import programs +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import add_actions, create_action + + +def try_to_parse(value): + _types = ('int', 'float') + for _t in _types: + try: + _val = eval("%s('%s')" % (_t, value)) + return _val + except (ValueError, SyntaxError): + pass + return value + +def try_to_eval(value): + try: + return eval(value) + except (NameError, SyntaxError, ImportError): + return value + +#----Numpy arrays support +class FakeObject(object): + """Fake class used in replacement of missing modules""" + pass +try: + from numpy import ndarray, array +except ImportError: + class ndarray(FakeObject): # analysis:ignore + """Fake ndarray""" + pass + +#----date and datetime objects support +import datetime +try: + from dateutil.parser import parse as dateparse +except ImportError: + def dateparse(datestr, dayfirst=True): # analysis:ignore + """Just for 'day/month/year' strings""" + _a, _b, _c = list(map(int, datestr.split('/'))) + if dayfirst: + return datetime.datetime(_c, _b, _a) + return datetime.datetime(_c, _a, _b) + +def datestr_to_datetime(value, dayfirst=True): + return dateparse(value, dayfirst=dayfirst) + +#----Background colors for supported types +COLORS = { + bool: Qt.magenta, + tuple([float] + list(INT_TYPES)): Qt.blue, + list: Qt.yellow, + dict: Qt.cyan, + tuple: Qt.lightGray, + TEXT_TYPES: Qt.darkRed, + ndarray: Qt.green, + datetime.date: Qt.darkYellow, + } + +def get_color(value, alpha): + """Return color depending on value type""" + color = QColor() + for typ in COLORS: + if isinstance(value, typ): + color = QColor(COLORS[typ]) + color.setAlphaF(alpha) + return color + + +class ContentsWidget(QWidget): + """Import wizard contents widget""" + asDataChanged = Signal(bool) + + def __init__(self, parent, text): + QWidget.__init__(self, parent) + + self.text_editor = QTextEdit(self) + self.text_editor.setText(text) + self.text_editor.setReadOnly(True) + + # Type frame + type_layout = QHBoxLayout() + type_label = QLabel(_("Import as")) + type_layout.addWidget(type_label) + data_btn = QRadioButton(_("data")) + data_btn.setChecked(True) + self._as_data= True + type_layout.addWidget(data_btn) + code_btn = QRadioButton(_("code")) + self._as_code = False + type_layout.addWidget(code_btn) + txt_btn = QRadioButton(_("text")) + type_layout.addWidget(txt_btn) + + h_spacer = QSpacerItem(40, 20, + QSizePolicy.Expanding, QSizePolicy.Minimum) + type_layout.addItem(h_spacer) + type_frame = QFrame() + type_frame.setLayout(type_layout) + + # Opts frame + grid_layout = QGridLayout() + grid_layout.setSpacing(0) + + col_label = QLabel(_("Column separator:")) + grid_layout.addWidget(col_label, 0, 0) + col_w = QWidget() + col_btn_layout = QHBoxLayout() + self.tab_btn = QRadioButton(_("Tab")) + self.tab_btn.setChecked(False) + col_btn_layout.addWidget(self.tab_btn) + other_btn_col = QRadioButton(_("other")) + other_btn_col.setChecked(True) + col_btn_layout.addWidget(other_btn_col) + col_w.setLayout(col_btn_layout) + grid_layout.addWidget(col_w, 0, 1) + self.line_edt = QLineEdit(",") + self.line_edt.setMaximumWidth(30) + self.line_edt.setEnabled(True) + other_btn_col.toggled.connect(self.line_edt.setEnabled) + grid_layout.addWidget(self.line_edt, 0, 2) + + row_label = QLabel(_("Row separator:")) + grid_layout.addWidget(row_label, 1, 0) + row_w = QWidget() + row_btn_layout = QHBoxLayout() + self.eol_btn = QRadioButton(_("EOL")) + self.eol_btn.setChecked(True) + row_btn_layout.addWidget(self.eol_btn) + other_btn_row = QRadioButton(_("other")) + row_btn_layout.addWidget(other_btn_row) + row_w.setLayout(row_btn_layout) + grid_layout.addWidget(row_w, 1, 1) + self.line_edt_row = QLineEdit(";") + self.line_edt_row.setMaximumWidth(30) + self.line_edt_row.setEnabled(False) + other_btn_row.toggled.connect(self.line_edt_row.setEnabled) + grid_layout.addWidget(self.line_edt_row, 1, 2) + + grid_layout.setRowMinimumHeight(2, 15) + + other_group = QGroupBox(_("Additional options")) + other_layout = QGridLayout() + other_group.setLayout(other_layout) + + skiprows_label = QLabel(_("Skip rows:")) + other_layout.addWidget(skiprows_label, 0, 0) + self.skiprows_edt = QLineEdit('0') + self.skiprows_edt.setMaximumWidth(30) + intvalid = QIntValidator(0, len(to_text_string(text).splitlines()), + self.skiprows_edt) + self.skiprows_edt.setValidator(intvalid) + other_layout.addWidget(self.skiprows_edt, 0, 1) + + other_layout.setColumnMinimumWidth(2, 5) + + comments_label = QLabel(_("Comments:")) + other_layout.addWidget(comments_label, 0, 3) + self.comments_edt = QLineEdit('#') + self.comments_edt.setMaximumWidth(30) + other_layout.addWidget(self.comments_edt, 0, 4) + + self.trnsp_box = QCheckBox(_("Transpose")) + #self.trnsp_box.setEnabled(False) + other_layout.addWidget(self.trnsp_box, 1, 0, 2, 0) + + grid_layout.addWidget(other_group, 3, 0, 2, 0) + + opts_frame = QFrame() + opts_frame.setLayout(grid_layout) + + data_btn.toggled.connect(opts_frame.setEnabled) + data_btn.toggled.connect(self.set_as_data) + code_btn.toggled.connect(self.set_as_code) +# self.connect(txt_btn, SIGNAL("toggled(bool)"), +# self, SLOT("is_text(bool)")) + + # Final layout + layout = QVBoxLayout() + layout.addWidget(type_frame) + layout.addWidget(self.text_editor) + layout.addWidget(opts_frame) + self.setLayout(layout) + + def get_as_data(self): + """Return if data type conversion""" + return self._as_data + + def get_as_code(self): + """Return if code type conversion""" + return self._as_code + + def get_as_num(self): + """Return if numeric type conversion""" + return self._as_num + + def get_col_sep(self): + """Return the column separator""" + if self.tab_btn.isChecked(): + return u"\t" + return to_text_string(self.line_edt.text()) + + def get_row_sep(self): + """Return the row separator""" + if self.eol_btn.isChecked(): + return u"\n" + return to_text_string(self.line_edt_row.text()) + + def get_skiprows(self): + """Return number of lines to be skipped""" + return int(to_text_string(self.skiprows_edt.text())) + + def get_comments(self): + """Return comment string""" + return to_text_string(self.comments_edt.text()) + + @Slot(bool) + def set_as_data(self, as_data): + """Set if data type conversion""" + self._as_data = as_data + self.asDataChanged.emit(as_data) + + @Slot(bool) + def set_as_code(self, as_code): + """Set if code type conversion""" + self._as_code = as_code + + +class PreviewTableModel(QAbstractTableModel): + """Import wizard preview table model""" + def __init__(self, data=[], parent=None): + QAbstractTableModel.__init__(self, parent) + self._data = data + + def rowCount(self, parent=QModelIndex()): + """Return row count""" + return len(self._data) + + def columnCount(self, parent=QModelIndex()): + """Return column count""" + return len(self._data[0]) + + def _display_data(self, index): + """Return a data element""" + return to_qvariant(self._data[index.row()][index.column()]) + + def data(self, index, role=Qt.DisplayRole): + """Return a model data element""" + if not index.isValid(): + return to_qvariant() + if role == Qt.DisplayRole: + return self._display_data(index) + elif role == Qt.BackgroundColorRole: + return to_qvariant(get_color(self._data[index.row()][index.column()], .2)) + elif role == Qt.TextAlignmentRole: + return to_qvariant(int(Qt.AlignRight|Qt.AlignVCenter)) + return to_qvariant() + + def setData(self, index, value, role=Qt.EditRole): + """Set model data""" + return False + + def get_data(self): + """Return a copy of model data""" + return self._data[:][:] + + def parse_data_type(self, index, **kwargs): + """Parse a type to an other type""" + if not index.isValid(): + return False + try: + if kwargs['atype'] == "date": + self._data[index.row()][index.column()] = \ + datestr_to_datetime(self._data[index.row()][index.column()], + kwargs['dayfirst']).date() + elif kwargs['atype'] == "perc": + _tmp = self._data[index.row()][index.column()].replace("%", "") + self._data[index.row()][index.column()] = eval(_tmp)/100. + elif kwargs['atype'] == "account": + _tmp = self._data[index.row()][index.column()].replace(",", "") + self._data[index.row()][index.column()] = eval(_tmp) + elif kwargs['atype'] == "unicode": + self._data[index.row()][index.column()] = to_text_string( + self._data[index.row()][index.column()]) + elif kwargs['atype'] == "int": + self._data[index.row()][index.column()] = int( + self._data[index.row()][index.column()]) + elif kwargs['atype'] == "float": + self._data[index.row()][index.column()] = float( + self._data[index.row()][index.column()]) + self.dataChanged.emit(index, index) + except Exception as instance: + print(instance) + + def reset(self): + self.beginResetModel() + self.endResetModel() + +class PreviewTable(QTableView): + """Import wizard preview widget""" + def __init__(self, parent): + QTableView.__init__(self, parent) + self._model = None + + # Setting up actions + self.date_dayfirst_action = create_action(self, "dayfirst", + triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=True)) + self.date_monthfirst_action = create_action(self, "monthfirst", + triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=False)) + self.perc_action = create_action(self, "perc", + triggered=ft_partial(self.parse_to_type, atype="perc")) + self.acc_action = create_action(self, "account", + triggered=ft_partial(self.parse_to_type, atype="account")) + self.str_action = create_action(self, "unicode", + triggered=ft_partial(self.parse_to_type, atype="unicode")) + self.int_action = create_action(self, "int", + triggered=ft_partial(self.parse_to_type, atype="int")) + self.float_action = create_action(self, "float", + triggered=ft_partial(self.parse_to_type, atype="float")) + + # Setting up menus + self.date_menu = QMenu() + self.date_menu.setTitle("Date") + add_actions( self.date_menu, (self.date_dayfirst_action, + self.date_monthfirst_action)) + self.parse_menu = QMenu(self) + self.parse_menu.addMenu(self.date_menu) + add_actions( self.parse_menu, (self.perc_action, self.acc_action)) + self.parse_menu.setTitle("String to") + self.opt_menu = QMenu(self) + self.opt_menu.addMenu(self.parse_menu) + add_actions( self.opt_menu, (self.str_action, self.int_action, + self.float_action)) + + def _shape_text(self, text, colsep=u"\t", rowsep=u"\n", + transpose=False, skiprows=0, comments='#'): + """Decode the shape of the given text""" + assert colsep != rowsep + out = [] + text_rows = text.split(rowsep)[skiprows:] + for row in text_rows: + stripped = to_text_string(row).strip() + if len(stripped) == 0 or stripped.startswith(comments): + continue + line = to_text_string(row).split(colsep) + line = [try_to_parse(to_text_string(x)) for x in line] + out.append(line) + # Replace missing elements with np.nan's or None's + if programs.is_module_installed('numpy'): + from numpy import nan + out = list(zip_longest(*out, fillvalue=nan)) + else: + out = list(zip_longest(*out, fillvalue=None)) + # Tranpose the last result to get the expected one + out = [[r[col] for r in out] for col in range(len(out[0]))] + if transpose: + return [[r[col] for r in out] for col in range(len(out[0]))] + return out + + def get_data(self): + """Return model data""" + if self._model is None: + return None + return self._model.get_data() + + def process_data(self, text, colsep=u"\t", rowsep=u"\n", + transpose=False, skiprows=0, comments='#'): + """Put data into table model""" + data = self._shape_text(text, colsep, rowsep, transpose, skiprows, + comments) + self._model = PreviewTableModel(data) + self.setModel(self._model) + + @Slot() + def parse_to_type(self,**kwargs): + """Parse to a given type""" + indexes = self.selectedIndexes() + if not indexes: return + for index in indexes: + self.model().parse_data_type(index, **kwargs) + + def contextMenuEvent(self, event): + """Reimplement Qt method""" + self.opt_menu.popup(event.globalPos()) + event.accept() + + +class PreviewWidget(QWidget): + """Import wizard preview widget""" + + def __init__(self, parent): + QWidget.__init__(self, parent) + + vert_layout = QVBoxLayout() + + # Type frame + type_layout = QHBoxLayout() + type_label = QLabel(_("Import as")) + type_layout.addWidget(type_label) + + self.array_btn = array_btn = QRadioButton(_("array")) + array_btn.setEnabled(ndarray is not FakeObject) + array_btn.setChecked(ndarray is not FakeObject) + type_layout.addWidget(array_btn) + + list_btn = QRadioButton(_("list")) + list_btn.setChecked(not array_btn.isChecked()) + type_layout.addWidget(list_btn) + + if pd: + self.df_btn = df_btn = QRadioButton(_("DataFrame")) + df_btn.setChecked(False) + type_layout.addWidget(df_btn) + + h_spacer = QSpacerItem(40, 20, + QSizePolicy.Expanding, QSizePolicy.Minimum) + type_layout.addItem(h_spacer) + type_frame = QFrame() + type_frame.setLayout(type_layout) + + self._table_view = PreviewTable(self) + vert_layout.addWidget(type_frame) + vert_layout.addWidget(self._table_view) + self.setLayout(vert_layout) + + def open_data(self, text, colsep=u"\t", rowsep=u"\n", + transpose=False, skiprows=0, comments='#'): + """Open clipboard text as table""" + if pd: + self.pd_text = text + self.pd_info = dict(sep=colsep, lineterminator=rowsep, + skiprows=skiprows,comment=comments) + self._table_view.process_data(text, colsep, rowsep, transpose, + skiprows, comments) + + def get_data(self): + """Return table data""" + return self._table_view.get_data() + + +class ImportWizard(QDialog): + """Text data import wizard""" + def __init__(self, parent, text, + title=None, icon=None, contents_title=None, varname=None): + QDialog.__init__(self, parent) + + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + + if title is None: + title = _("Import wizard") + self.setWindowTitle(title) + if icon is None: + self.setWindowIcon(ima.icon('fileimport')) + if contents_title is None: + contents_title = _("Raw text") + + if varname is None: + varname = _("variable_name") + + self.var_name, self.clip_data = None, None + + # Setting GUI + self.tab_widget = QTabWidget(self) + self.text_widget = ContentsWidget(self, text) + self.table_widget = PreviewWidget(self) + + self.tab_widget.addTab(self.text_widget, _("text")) + self.tab_widget.setTabText(0, contents_title) + self.tab_widget.addTab(self.table_widget, _("table")) + self.tab_widget.setTabText(1, _("Preview")) + self.tab_widget.setTabEnabled(1, False) + + name_layout = QHBoxLayout() + name_label = QLabel(_("Variable Name")) + name_layout.addWidget(name_label) + + self.name_edt = QLineEdit() + self.name_edt.setText(varname) + name_layout.addWidget(self.name_edt) + + btns_layout = QHBoxLayout() + cancel_btn = QPushButton(_("Cancel")) + btns_layout.addWidget(cancel_btn) + cancel_btn.clicked.connect(self.reject) + h_spacer = QSpacerItem(40, 20, + QSizePolicy.Expanding, QSizePolicy.Minimum) + btns_layout.addItem(h_spacer) + self.back_btn = QPushButton(_("Previous")) + self.back_btn.setEnabled(False) + btns_layout.addWidget(self.back_btn) + self.back_btn.clicked.connect(ft_partial(self._set_step, step=-1)) + self.fwd_btn = QPushButton(_("Next")) + if not text: + self.fwd_btn.setEnabled(False) + btns_layout.addWidget(self.fwd_btn) + self.fwd_btn.clicked.connect(ft_partial(self._set_step, step=1)) + self.done_btn = QPushButton(_("Done")) + self.done_btn.setEnabled(False) + btns_layout.addWidget(self.done_btn) + self.done_btn.clicked.connect(self.process) + + self.text_widget.asDataChanged.connect(self.fwd_btn.setEnabled) + self.text_widget.asDataChanged.connect(self.done_btn.setDisabled) + layout = QVBoxLayout() + layout.addLayout(name_layout) + layout.addWidget(self.tab_widget) + layout.addLayout(btns_layout) + self.setLayout(layout) + + def _focus_tab(self, tab_idx): + """Change tab focus""" + for i in range(self.tab_widget.count()): + self.tab_widget.setTabEnabled(i, False) + self.tab_widget.setTabEnabled(tab_idx, True) + self.tab_widget.setCurrentIndex(tab_idx) + + def _set_step(self, step): + """Proceed to a given step""" + new_tab = self.tab_widget.currentIndex() + step + assert new_tab < self.tab_widget.count() and new_tab >= 0 + if new_tab == self.tab_widget.count()-1: + try: + self.table_widget.open_data(self._get_plain_text(), + self.text_widget.get_col_sep(), + self.text_widget.get_row_sep(), + self.text_widget.trnsp_box.isChecked(), + self.text_widget.get_skiprows(), + self.text_widget.get_comments()) + self.done_btn.setEnabled(True) + self.done_btn.setDefault(True) + self.fwd_btn.setEnabled(False) + self.back_btn.setEnabled(True) + except (SyntaxError, AssertionError) as error: + QMessageBox.critical(self, _("Import wizard"), + _("Unable to proceed to next step" + "

    Please check your entries." + "

    Error message:
    %s") % str(error)) + return + elif new_tab == 0: + self.done_btn.setEnabled(False) + self.fwd_btn.setEnabled(True) + self.back_btn.setEnabled(False) + self._focus_tab(new_tab) + + def get_data(self): + """Return processed data""" + # It is import to avoid accessing Qt C++ object as it has probably + # already been destroyed, due to the Qt.WA_DeleteOnClose attribute + return self.var_name, self.clip_data + + def _simplify_shape(self, alist, rec=0): + """Reduce the alist dimension if needed""" + if rec != 0: + if len(alist) == 1: + return alist[-1] + return alist + if len(alist) == 1: + return self._simplify_shape(alist[-1], 1) + return [self._simplify_shape(al, 1) for al in alist] + + def _get_table_data(self): + """Return clipboard processed as data""" + data = self._simplify_shape( + self.table_widget.get_data()) + if self.table_widget.array_btn.isChecked(): + return array(data) + elif pd and self.table_widget.df_btn.isChecked(): + info = self.table_widget.pd_info + buf = io.StringIO(self.table_widget.pd_text) + return pd.read_csv(buf, **info) + return data + + def _get_plain_text(self): + """Return clipboard as text""" + return self.text_widget.text_editor.toPlainText() + + @Slot() + def process(self): + """Process the data from clipboard""" + var_name = self.name_edt.text() + try: + self.var_name = str(var_name) + except UnicodeEncodeError: + self.var_name = to_text_string(var_name) + if self.text_widget.get_as_data(): + self.clip_data = self._get_table_data() + elif self.text_widget.get_as_code(): + self.clip_data = try_to_eval( + to_text_string(self._get_plain_text())) + else: + self.clip_data = to_text_string(self._get_plain_text()) + self.accept() + + +def test(text): + """Test""" + from spyder.utils.qthelpers import qapplication + _app = qapplication() # analysis:ignore + dialog = ImportWizard(None, text) + if dialog.exec_(): + print(dialog.get_data()) + +if __name__ == "__main__": + test(u"17/11/1976\t1.34\n14/05/09\t3.14") diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/__init__.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/__init__.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +spyder.widgets.variableexplorer +=============================== + +Variable Explorer related widgets +""" diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/namespacebrowser.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/namespacebrowser.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/namespacebrowser.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/namespacebrowser.py 2016-11-17 03:39:40.000000000 +0000 @@ -0,0 +1,587 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Namespace browser widget + +This is the main widget used in the Variable Explorer plugin +""" + +# Standard library imports +import os.path as osp +import socket + +# Third library imports (qtpy) +from qtpy.compat import getsavefilename, getopenfilenames +from qtpy.QtCore import Qt, Signal, Slot +from qtpy.QtGui import QCursor +from qtpy.QtWidgets import (QApplication, QHBoxLayout, QInputDialog, QMenu, + QMessageBox, QToolButton, QVBoxLayout, QWidget) + +# Third party imports (others) +try: + import ipykernel.pickleutil + from ipykernel.serialize import serialize_object +except ImportError: + serialize_object = None + +# Local imports +from spyder.config.base import _, get_supported_types +from spyder.py3compat import is_text_string, getcwd, to_text_string +from spyder.utils import encoding +from spyder.utils import icon_manager as ima +from spyder.utils.iofuncs import iofunctions +from spyder.utils.misc import fix_reference_name +from spyder.utils.programs import is_module_installed +from spyder.utils.qthelpers import (add_actions, create_action, + create_toolbutton) +from spyder.widgets.externalshell.monitor import ( + communicate, monitor_copy_global, monitor_del_global, monitor_get_global, + monitor_load_globals, monitor_save_globals, monitor_set_global) +from spyder.widgets.variableexplorer.collectionseditor import ( + RemoteCollectionsEditorTableView) +from spyder.widgets.variableexplorer.importwizard import ImportWizard +from spyder.widgets.variableexplorer.utils import REMOTE_SETTINGS + + +SUPPORTED_TYPES = get_supported_types() + +# XXX --- Disable canning for Numpy arrays for now --- +# This allows getting values between a Python 3 frontend +# and a Python 2 kernel, and viceversa, for several types of +# arrays. +# See this link for interesting ideas on how to solve this +# in the future: +# http://stackoverflow.com/q/30698004/438386 +if serialize_object is not None: + ipykernel.pickleutil.can_map.pop('numpy.ndarray') + + +class NamespaceBrowser(QWidget): + """Namespace browser (global variables explorer widget)""" + sig_option_changed = Signal(str, object) + sig_collapse = Signal() + + def __init__(self, parent): + QWidget.__init__(self, parent) + + self.shellwidget = None + self.is_visible = True + self.setup_in_progress = None + + # Remote dict editor settings + self.check_all = None + self.exclude_private = None + self.exclude_uppercase = None + self.exclude_capitalized = None + self.exclude_unsupported = None + self.excluded_names = None + self.minmax = None + self.remote_editing = None + self.autorefresh = None + + self.editor = None + self.exclude_private_action = None + self.exclude_uppercase_action = None + self.exclude_capitalized_action = None + self.exclude_unsupported_action = None + + self.filename = None + + # For IPython clients + self.is_ipyclient = False + self.var_properties = {} + + def setup(self, check_all=None, exclude_private=None, + exclude_uppercase=None, exclude_capitalized=None, + exclude_unsupported=None, excluded_names=None, + minmax=None, remote_editing=None, + autorefresh=None): + """Setup the namespace browser""" + assert self.shellwidget is not None + + self.check_all = check_all + self.exclude_private = exclude_private + self.exclude_uppercase = exclude_uppercase + self.exclude_capitalized = exclude_capitalized + self.exclude_unsupported = exclude_unsupported + self.excluded_names = excluded_names + self.minmax = minmax + self.remote_editing = remote_editing + self.autorefresh = autorefresh + + if self.editor is not None: + self.editor.setup_menu(minmax) + self.exclude_private_action.setChecked(exclude_private) + self.exclude_uppercase_action.setChecked(exclude_uppercase) + self.exclude_capitalized_action.setChecked(exclude_capitalized) + self.exclude_unsupported_action.setChecked(exclude_unsupported) + if self.auto_refresh_button is not None: + self.auto_refresh_button.setChecked(autorefresh) + self.refresh_table() + return + + self.editor = RemoteCollectionsEditorTableView(self, None, + minmax=minmax, + remote_editing=remote_editing, + get_value_func=self.get_value, + set_value_func=self.set_value, + new_value_func=self.set_value, + remove_values_func=self.remove_values, + copy_value_func=self.copy_value, + is_list_func=self.is_list, + get_len_func=self.get_len, + is_array_func=self.is_array, + is_image_func=self.is_image, + is_dict_func=self.is_dict, + is_data_frame_func=self.is_data_frame, + is_series_func=self.is_series, + get_array_shape_func=self.get_array_shape, + get_array_ndim_func=self.get_array_ndim, + oedit_func=self.oedit, + plot_func=self.plot, imshow_func=self.imshow, + show_image_func=self.show_image) + self.editor.sig_option_changed.connect(self.sig_option_changed.emit) + self.editor.sig_files_dropped.connect(self.import_data) + + # Setup layout + layout = QVBoxLayout() + blayout = QHBoxLayout() + toolbar = self.setup_toolbar(exclude_private, exclude_uppercase, + exclude_capitalized, exclude_unsupported, + autorefresh) + for widget in toolbar: + blayout.addWidget(widget) + + # Options menu + options_button = create_toolbutton(self, text=_('Options'), + icon=ima.icon('tooloptions')) + options_button.setPopupMode(QToolButton.InstantPopup) + menu = QMenu(self) + 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) + add_actions(menu, actions) + options_button.setMenu(menu) + + blayout.addStretch() + blayout.addWidget(options_button) + layout.addLayout(blayout) + layout.addWidget(self.editor) + self.setLayout(layout) + layout.setContentsMargins(0, 0, 0, 0) + + self.sig_option_changed.connect(self.option_changed) + + def set_shellwidget(self, shellwidget): + """Bind shellwidget instance to namespace browser""" + self.shellwidget = shellwidget + shellwidget.set_namespacebrowser(self) + + def setup_toolbar(self, exclude_private, exclude_uppercase, + exclude_capitalized, exclude_unsupported, autorefresh): + """Setup toolbar""" + self.setup_in_progress = True + + toolbar = [] + + # There is no need of refreshes for ipyclients + if not self.is_ipyclient: + refresh_button = create_toolbutton(self, text=_('Refresh'), + icon=ima.icon('reload'), + triggered=self.refresh_table) + self.auto_refresh_button = create_toolbutton(self, + text=_('Refresh periodically'), + icon=ima.icon('auto_reload'), + toggled=self.toggle_auto_refresh) + self.auto_refresh_button.setChecked(autorefresh) + else: + refresh_button = self.auto_refresh_button = None + + load_button = create_toolbutton(self, text=_('Import data'), + icon=ima.icon('fileimport'), + triggered=lambda: self.import_data()) + self.save_button = create_toolbutton(self, text=_("Save data"), + icon=ima.icon('filesave'), + triggered=lambda: self.save_data(self.filename)) + self.save_button.setEnabled(False) + save_as_button = create_toolbutton(self, + text=_("Save data as..."), + icon=ima.icon('filesaveas'), + triggered=self.save_data) + + if self.is_ipyclient: + toolbar += [load_button, self.save_button, save_as_button] + else: + toolbar += [refresh_button, self.auto_refresh_button, load_button, + self.save_button, save_as_button] + + self.exclude_private_action = create_action(self, + _("Exclude private references"), + tip=_("Exclude references which name starts" + " with an underscore"), + toggled=lambda state: + self.sig_option_changed.emit('exclude_private', state)) + self.exclude_private_action.setChecked(exclude_private) + + self.exclude_uppercase_action = create_action(self, + _("Exclude all-uppercase references"), + tip=_("Exclude references which name is uppercase"), + toggled=lambda state: + self.sig_option_changed.emit('exclude_uppercase', state)) + self.exclude_uppercase_action.setChecked(exclude_uppercase) + + self.exclude_capitalized_action = create_action(self, + _("Exclude capitalized references"), + tip=_("Exclude references which name starts with an " + "uppercase character"), + toggled=lambda state: + self.sig_option_changed.emit('exclude_capitalized', state)) + self.exclude_capitalized_action.setChecked(exclude_capitalized) + + self.exclude_unsupported_action = create_action(self, + _("Exclude unsupported data types"), + tip=_("Exclude references to unsupported data types" + " (i.e. which won't be handled/saved correctly)"), + toggled=lambda state: + self.sig_option_changed.emit('exclude_unsupported', state)) + self.exclude_unsupported_action.setChecked(exclude_unsupported) + + self.setup_in_progress = False + + return toolbar + + def option_changed(self, option, value): + """Option has changed""" + setattr(self, to_text_string(option), value) + if self.is_ipyclient: + self.shellwidget.set_namespace_view_settings() + self.refresh_table() + else: + settings = self.get_view_settings() + communicate(self._get_sock(), + 'set_remote_view_settings()', settings=[settings]) + + def visibility_changed(self, enable): + """Notify the widget whether its container (the namespace browser + plugin is visible or not""" + # This is slowing down Spyder a lot if too much data is present in + # the Variable Explorer, and users give focus to it after being hidden. + # This also happens when the Variable Explorer is visible and users + # give focus to Spyder after using another application (like Chrome + # or Firefox). + # That's why we've decided to remove this feature + # Fixes Issue 2593 + # + # self.is_visible = enable + # if enable: + # self.refresh_table() + pass + + @Slot(bool) + def toggle_auto_refresh(self, state): + """Toggle auto refresh state""" + self.autorefresh = state + if not self.setup_in_progress and not self.is_ipyclient: + communicate(self._get_sock(), + "set_monitor_auto_refresh(%r)" % state) + + def _get_sock(self): + """Return socket connection""" + return self.shellwidget.introspection_socket + + def get_view_settings(self): + """Return dict editor view settings""" + settings = {} + for name in REMOTE_SETTINGS: + settings[name] = getattr(self, name) + return settings + + @Slot() + def refresh_table(self): + """Refresh variable table""" + if self.is_visible and self.isVisible(): + if self.is_ipyclient: + self.shellwidget.refresh_namespacebrowser() + else: + if self.shellwidget.is_running(): + sock = self._get_sock() + if sock is None: + return + try: + communicate(sock, "refresh()") + except socket.error: + # Process was terminated before calling this method + pass + + def process_remote_view(self, remote_view): + """Process remote view""" + if remote_view is not None: + self.set_data(remote_view) + + def set_var_properties(self, properties): + """Set properties of variables""" + self.var_properties = properties + + #------ Remote commands ------------------------------------ + def get_value(self, name): + if self.is_ipyclient: + value = self.shellwidget.get_value(name) + + # Reset temporal variable where value is saved to + # save memory + self.shellwidget._kernel_value = None + else: + value = monitor_get_global(self._get_sock(), name) + if value is None: + if communicate(self._get_sock(), '%s is not None' % name): + import pickle + msg = to_text_string(_("Object %s is not picklable") + % name) + raise pickle.PicklingError(msg) + return value + + def set_value(self, name, value): + if self.is_ipyclient: + value = serialize_object(value) + self.shellwidget.set_value(name, value) + else: + monitor_set_global(self._get_sock(), name, value) + self.refresh_table() + + def remove_values(self, names): + for name in names: + if self.is_ipyclient: + self.shellwidget.remove_value(name) + else: + monitor_del_global(self._get_sock(), name) + self.refresh_table() + + def copy_value(self, orig_name, new_name): + if self.is_ipyclient: + self.shellwidget.copy_value(orig_name, new_name) + else: + monitor_copy_global(self._get_sock(), orig_name, new_name) + self.refresh_table() + + def is_list(self, name): + """Return True if variable is a list or a tuple""" + if self.is_ipyclient: + return self.var_properties[name]['is_list'] + else: + return communicate(self._get_sock(), + 'isinstance(%s, (tuple, list))' % name) + + def is_dict(self, name): + """Return True if variable is a dictionary""" + if self.is_ipyclient: + return self.var_properties[name]['is_dict'] + else: + return communicate(self._get_sock(), 'isinstance(%s, dict)' % name) + + def get_len(self, name): + """Return sequence length""" + if self.is_ipyclient: + return self.var_properties[name]['len'] + else: + return communicate(self._get_sock(), "len(%s)" % name) + + def is_array(self, name): + """Return True if variable is a NumPy array""" + if self.is_ipyclient: + return self.var_properties[name]['is_array'] + else: + return communicate(self._get_sock(), 'is_array("%s")' % name) + + def is_image(self, name): + """Return True if variable is a PIL.Image image""" + if self.is_ipyclient: + return self.var_properties[name]['is_image'] + else: + return communicate(self._get_sock(), 'is_image("%s")' % name) + + def is_data_frame(self, name): + """Return True if variable is a DataFrame""" + if self.is_ipyclient: + return self.var_properties[name]['is_data_frame'] + else: + return communicate(self._get_sock(), + "isinstance(globals()['%s'], DataFrame)" % name) + + def is_series(self, name): + """Return True if variable is a Series""" + if self.is_ipyclient: + return self.var_properties[name]['is_series'] + else: + return communicate(self._get_sock(), + "isinstance(globals()['%s'], Series)" % name) + + def get_array_shape(self, name): + """Return array's shape""" + if self.is_ipyclient: + return self.var_properties[name]['array_shape'] + else: + return communicate(self._get_sock(), "%s.shape" % name) + + def get_array_ndim(self, name): + """Return array's ndim""" + if self.is_ipyclient: + return self.var_properties[name]['array_ndim'] + else: + return communicate(self._get_sock(), "%s.ndim" % name) + + def plot(self, name, funcname): + if self.is_ipyclient: + self.shellwidget.execute("%%varexp --%s %s" % (funcname, name)) + else: + command = "import spyder.pyplot; "\ + "__fig__ = spyder.pyplot.figure(); "\ + "__items__ = getattr(spyder.pyplot, '%s')(%s); "\ + "spyder.pyplot.show(); "\ + "del __fig__, __items__;" % (funcname, name) + self.shellwidget.send_to_process(command) + + def imshow(self, name): + if self.is_ipyclient: + self.shellwidget.execute("%%varexp --imshow %s" % name) + else: + command = "import spyder.pyplot; " \ + "__fig__ = spyder.pyplot.figure(); " \ + "__items__ = spyder.pyplot.imshow(%s); " \ + "spyder.pyplot.show(); del __fig__, __items__;" % name + self.shellwidget.send_to_process(command) + + def show_image(self, name): + command = "%s.show()" % name + if self.is_ipyclient: + self.shellwidget.execute(command) + else: + self.shellwidget.send_to_process(command) + + def oedit(self, name): + command = "from spyder.widgets.variableexplorer.objecteditor import oedit; " \ + "oedit('%s', modal=False, namespace=locals());" % name + self.shellwidget.send_to_process(command) + + #------ Set, load and save data ------------------------------------------- + def set_data(self, data): + """Set data""" + if data != self.editor.model.get_data(): + self.editor.set_data(data) + self.editor.adjust_columns() + + def collapse(self): + """Collapse""" + self.sig_collapse.emit() + + @Slot(bool) + @Slot(list) + def import_data(self, filenames=None): + """Import data from text file""" + title = _("Import data") + if filenames is None: + if self.filename is None: + basedir = getcwd() + else: + basedir = osp.dirname(self.filename) + filenames, _selfilter = getopenfilenames(self, title, basedir, + iofunctions.load_filters) + if not filenames: + return + elif is_text_string(filenames): + filenames = [filenames] + + for filename in filenames: + self.filename = to_text_string(filename) + ext = osp.splitext(self.filename)[1].lower() + + if ext not in iofunctions.load_funcs: + buttons = QMessageBox.Yes | QMessageBox.Cancel + answer = QMessageBox.question(self, title, + _("Unsupported file extension '%s'

    " + "Would you like to import it anyway " + "(by selecting a known file format)?" + ) % ext, buttons) + if answer == QMessageBox.Cancel: + return + formats = list(iofunctions.load_extensions.keys()) + item, ok = QInputDialog.getItem(self, title, + _('Open file as:'), + formats, 0, False) + if ok: + ext = iofunctions.load_extensions[to_text_string(item)] + else: + return + + load_func = iofunctions.load_funcs[ext] + + # 'import_wizard' (self.setup_io) + if is_text_string(load_func): + # Import data with import wizard + error_message = None + try: + text, _encoding = encoding.read(self.filename) + base_name = osp.basename(self.filename) + editor = ImportWizard(self, text, title=base_name, + varname=fix_reference_name(base_name)) + if editor.exec_(): + var_name, clip_data = editor.get_data() + self.set_value(var_name, clip_data) + except Exception as error: + error_message = str(error) + else: + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + QApplication.processEvents() + if self.is_ipyclient: + error_message = self.shellwidget.load_data(self.filename, + ext) + self.shellwidget._kernel_reply = None + else: + error_message = monitor_load_globals(self._get_sock(), + self.filename, ext) + QApplication.restoreOverrideCursor() + QApplication.processEvents() + + if error_message is not None: + QMessageBox.critical(self, title, + _("Unable to load '%s'" + "

    Error message:
    %s" + ) % (self.filename, error_message)) + self.refresh_table() + + @Slot() + def save_data(self, filename=None): + """Save data""" + if filename is None: + filename = self.filename + if filename is None: + filename = getcwd() + filename, _selfilter = getsavefilename(self, _("Save data"), + filename, + iofunctions.save_filters) + if filename: + self.filename = filename + else: + return False + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + QApplication.processEvents() + if self.is_ipyclient: + error_message = self.shellwidget.save_namespace(self.filename) + self.shellwidget._kernel_reply = None + else: + settings = self.get_view_settings() + error_message = monitor_save_globals(self._get_sock(), settings, + filename) + QApplication.restoreOverrideCursor() + QApplication.processEvents() + if error_message is not None: + QMessageBox.critical(self, _("Save data"), + _("Unable to save current workspace" + "

    Error message:
    %s") % error_message) + self.save_button.setEnabled(self.filename is not None) diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/objecteditor.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/objecteditor.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/objecteditor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/objecteditor.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Generic object editor dialog +""" + +# Standard library imports +from __future__ import print_function + +# Third party imports +from qtpy.QtCore import QObject + +# Local imports +from spyder.py3compat import is_text_string + + +class DialogKeeper(QObject): + def __init__(self): + QObject.__init__(self) + self.dialogs = {} + self.namespace = None + + def set_namespace(self, namespace): + self.namespace = namespace + + def create_dialog(self, dialog, refname, func): + self.dialogs[id(dialog)] = dialog, refname, func + dialog.accepted.connect( + lambda eid=id(dialog): self.editor_accepted(eid)) + dialog.rejected.connect( + lambda eid=id(dialog): self.editor_rejected(eid)) + dialog.show() + dialog.activateWindow() + dialog.raise_() + + def editor_accepted(self, dialog_id): + dialog, refname, func = self.dialogs[dialog_id] + self.namespace[refname] = func(dialog) + self.dialogs.pop(dialog_id) + + def editor_rejected(self, dialog_id): + self.dialogs.pop(dialog_id) + +keeper = DialogKeeper() + + +def create_dialog(obj, obj_name): + """Creates the editor dialog and returns a tuple (dialog, func) where func + is the function to be called with the dialog instance as argument, after + quitting the dialog box + + The role of this intermediate function is to allow easy monkey-patching. + (uschmitt suggested this indirection here so that he can monkey patch + oedit to show eMZed related data) + """ + # Local import + from spyder.widgets.variableexplorer.texteditor import TextEditor + from spyder.widgets.variableexplorer.utils import (ndarray, FakeObject, + Image, is_known_type, DataFrame, + Series) + from spyder.widgets.variableexplorer.collectionseditor import CollectionsEditor + from spyder.widgets.variableexplorer.arrayeditor import ArrayEditor + if DataFrame is not FakeObject: + from spyder.widgets.variableexplorer.dataframeeditor import DataFrameEditor + + conv_func = lambda data: data + readonly = not is_known_type(obj) + if isinstance(obj, ndarray) and ndarray is not FakeObject: + dialog = ArrayEditor() + if not dialog.setup_and_check(obj, title=obj_name, + readonly=readonly): + return + elif isinstance(obj, Image) and Image is not FakeObject \ + and ndarray is not FakeObject: + dialog = ArrayEditor() + import numpy as np + data = np.array(obj) + if not dialog.setup_and_check(data, title=obj_name, + readonly=readonly): + return + from spyder.pil_patch import Image + conv_func = lambda data: Image.fromarray(data, mode=obj.mode) + elif isinstance(obj, (DataFrame, Series)) and DataFrame is not FakeObject: + dialog = DataFrameEditor() + if not dialog.setup_and_check(obj): + return + elif is_text_string(obj): + dialog = TextEditor(obj, title=obj_name, readonly=readonly) + else: + dialog = CollectionsEditor() + dialog.setup(obj, title=obj_name, readonly=readonly) + + def end_func(dialog): + return conv_func(dialog.get_value()) + + return dialog, end_func + + +def oedit(obj, modal=True, namespace=None): + """Edit the object 'obj' in a GUI-based editor and return the edited copy + (if Cancel is pressed, return None) + + The object 'obj' is a container + + Supported container types: + dict, list, tuple, str/unicode or numpy.array + + (instantiate a new QApplication if necessary, + so it can be called directly from the interpreter) + """ + # Local import + from spyder.utils.qthelpers import qapplication + app = qapplication() + + if modal: + obj_name = '' + else: + assert is_text_string(obj) + obj_name = obj + if namespace is None: + namespace = globals() + keeper.set_namespace(namespace) + obj = namespace[obj_name] + # keep QApplication reference alive in the Python interpreter: + namespace['__qapp__'] = app + + result = create_dialog(obj, obj_name) + if result is None: + return + dialog, end_func = result + + if modal: + if dialog.exec_(): + return end_func(dialog) + else: + keeper.create_dialog(dialog, obj_name, end_func) + import os + if os.name == 'nt': + app.exec_() + + +#============================================================================== +# Tests +#============================================================================== +def test(): + """Run object editor test""" + import datetime, numpy as np + from spyder.pil_patch import Image + data = np.random.random_integers(255, size=(100, 100)).astype('uint8') + image = Image.fromarray(data) + example = {'str': 'kjkj kj k j j kj k jkj', + 'list': [1, 3, 4, 'kjkj', None], + 'dict': {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]}, + 'float': 1.2233, + 'array': np.random.rand(10, 10), + 'image': image, + 'date': datetime.date(1945, 5, 8), + 'datetime': datetime.datetime(1945, 5, 8), + } + image = oedit(image) + class Foobar(object): + def __init__(self): + self.text = "toto" + foobar = Foobar() + + print(oedit(foobar)) + print(oedit(example)) + print(oedit(np.random.rand(10, 10))) + print(oedit(oedit.__doc__)) + print(example) + + +if __name__ == "__main__": + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/texteditor.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/texteditor.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/texteditor.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/texteditor.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Text editor dialog +""" + +# Standard library imports +from __future__ import print_function + +# Third party imports +from qtpy.QtCore import Qt +from qtpy.QtWidgets import QDialog, QDialogButtonBox, QTextEdit, QVBoxLayout + +# Local import +from spyder.config.base import _ +from spyder.config.gui import get_font +from spyder.py3compat import (is_binary_string, to_binary_string, + to_text_string) +from spyder.utils import icon_manager as ima + + +class TextEditor(QDialog): + """Array Editor Dialog""" + def __init__(self, text, title='', font=None, parent=None, + readonly=False, size=(400, 300)): + QDialog.__init__(self, parent) + + # Destroying the C++ object right after closing the dialog box, + # otherwise it may be garbage-collected in another QThread + # (e.g. the editor's analysis thread in Spyder), thus leading to + # a segmentation fault on UNIX or an application crash on Windows + self.setAttribute(Qt.WA_DeleteOnClose) + + self.text = None + + # Display text as unicode if it comes as bytes, so users see + # its right representation + if is_binary_string(text): + self.is_binary = True + text = to_text_string(text, 'utf8') + else: + self.is_binary = False + + self.layout = QVBoxLayout() + self.setLayout(self.layout) + + # Text edit + self.edit = QTextEdit(parent) + self.edit.textChanged.connect(self.text_changed) + self.edit.setReadOnly(readonly) + self.edit.setPlainText(text) + if font is None: + font = get_font() + self.edit.setFont(font) + self.layout.addWidget(self.edit) + + # Buttons configuration + buttons = QDialogButtonBox.Ok + if not readonly: + buttons = buttons | QDialogButtonBox.Cancel + bbox = QDialogButtonBox(buttons) + bbox.accepted.connect(self.accept) + bbox.rejected.connect(self.reject) + self.layout.addWidget(bbox) + + # Make the dialog act as a window + self.setWindowFlags(Qt.Window) + + self.setWindowIcon(ima.icon('edit')) + self.setWindowTitle(_("Text editor") + \ + "%s" % (" - "+str(title) if str(title) else "")) + self.resize(size[0], size[1]) + + def text_changed(self): + """Text has changed""" + # Save text as bytes, if it was initially bytes + if self.is_binary: + self.text = to_binary_string(self.edit.toPlainText(), 'utf8') + else: + self.text = to_text_string(self.edit.toPlainText()) + + def get_value(self): + """Return modified text""" + # It is import to avoid accessing Qt C++ object as it has probably + # already been destroyed, due to the Qt.WA_DeleteOnClose attribute + return self.text + + +#============================================================================== +# Tests +#============================================================================== +def test(): + """Text editor demo""" + from spyder.utils.qthelpers import qapplication + _app = qapplication() # analysis:ignore + + text = """01234567890123456789012345678901234567890123456789012345678901234567890123456789 +dedekdh elkd ezd ekjd lekdj elkdfjelfjk e""" + dialog = TextEditor(text) + dialog.exec_() + + dlg_text = dialog.get_value() + assert text == dlg_text + + +if __name__ == "__main__": + test() diff -Nru spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/utils.py spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/utils.py --- spyder-2.3.8+dfsg1/spyder/widgets/variableexplorer/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/widgets/variableexplorer/utils.py 2016-11-17 03:39:40.000000000 +0000 @@ -0,0 +1,488 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Utilities for the Collections editor widget and dialog +""" + +from __future__ import print_function + +import re + +# Local imports +from spyder.config.base import get_supported_types +from spyder.py3compat import (NUMERIC_TYPES, TEXT_TYPES, to_text_string, + is_text_string, is_binary_string, reprlib, + PY2, to_binary_string) +from spyder.utils import programs +from spyder import dependencies +from spyder.config.base import _ + + +#============================================================================== +# Dependencies +#============================================================================== +PANDAS_REQVER = '>=0.13.1' +dependencies.add('pandas', _("View and edit DataFrames and Series in the " + "Variable Explorer"), + required_version=PANDAS_REQVER, optional=True) + +NUMPY_REQVER = '>=1.7' +dependencies.add("numpy", _("View and edit two and three dimensional arrays " + "in the Variable Explorer"), + required_version=NUMPY_REQVER, optional=True) + +#============================================================================== +# FakeObject +#============================================================================== +class FakeObject(object): + """Fake class used in replacement of missing modules""" + pass + + +#============================================================================== +# Numpy arrays support +#============================================================================== +try: + from numpy import ndarray, array, matrix, recarray + from numpy.ma import MaskedArray + from numpy import savetxt as np_savetxt + from numpy import set_printoptions as np_set_printoptions +except ImportError: + ndarray = array = matrix = recarray = MaskedArray = np_savetxt = \ + np_set_printoptions = FakeObject + +def get_numpy_dtype(obj): + """Return NumPy data type associated to obj + Return None if NumPy is not available + or if obj is not a NumPy array or scalar""" + if ndarray is not FakeObject: + # NumPy is available + import numpy as np + if isinstance(obj, np.generic) or isinstance(obj, np.ndarray): + # Numpy scalars all inherit from np.generic. + # Numpy arrays all inherit from np.ndarray. + # If we check that we are certain we have one of these + # types then we are less likely to generate an exception below. + try: + return obj.dtype.type + except (AttributeError, RuntimeError): + # AttributeError: some NumPy objects have no dtype attribute + # RuntimeError: happens with NetCDF objects (Issue 998) + return + + +#============================================================================== +# Pandas support +#============================================================================== +if programs.is_module_installed('pandas', PANDAS_REQVER): + from pandas import DataFrame, Series +else: + DataFrame = Series = FakeObject # analysis:ignore + + +#============================================================================== +# PIL Images support +#============================================================================== +try: + from spyder import pil_patch + Image = pil_patch.Image.Image +except ImportError: + Image = FakeObject # analysis:ignore + + +#============================================================================== +# BeautifulSoup support (see Issue 2448) +#============================================================================== +try: + import bs4 + NavigableString = bs4.element.NavigableString +except (ImportError, AttributeError): + NavigableString = FakeObject # analysis:ignore + + +#============================================================================== +# Misc. +#============================================================================== +def address(obj): + """Return object address as a string: ''""" + return "<%s @ %s>" % (obj.__class__.__name__, + hex(id(obj)).upper().replace('X', 'x')) + + +def try_to_eval(value): + """Try to eval value""" + try: + return eval(value) + except (NameError, SyntaxError, ImportError): + return value + + +def get_size(item): + """Return size of an item of arbitrary type""" + if isinstance(item, (list, tuple, dict)): + return len(item) + elif isinstance(item, (ndarray, MaskedArray)): + return item.shape + elif isinstance(item, Image): + return item.size + if isinstance(item, (DataFrame, Series)): + return item.shape + else: + return 1 + + +#============================================================================== +# Set limits for the amount of elements in the repr of collections (lists, +# dicts, tuples and sets) and Numpy arrays +#============================================================================== +CollectionsRepr = reprlib.Repr() +CollectionsRepr.maxlist = 10 +CollectionsRepr.maxdict = 10 +CollectionsRepr.maxtuple = 10 +CollectionsRepr.maxset = 10 + +if np_set_printoptions is not FakeObject: + np_set_printoptions(threshold=10) + + +#============================================================================== +# Date and datetime objects support +#============================================================================== +import datetime + + +try: + from dateutil.parser import parse as dateparse +except ImportError: + def dateparse(datestr): # analysis:ignore + """Just for 'year, month, day' strings""" + return datetime.datetime( *list(map(int, datestr.split(','))) ) + + +def datestr_to_datetime(value): + rp = value.rfind('(')+1 + v = dateparse(value[rp:-1]) + print(value, "-->", v) + return v + + +#============================================================================== +# Background colors for supported types +#============================================================================== +ARRAY_COLOR = "#00ff00" +SCALAR_COLOR = "#0000ff" +COLORS = { + bool: "#ff00ff", + NUMERIC_TYPES: SCALAR_COLOR, + list: "#ffff00", + dict: "#00ffff", + tuple: "#c0c0c0", + TEXT_TYPES: "#800000", + (ndarray, + MaskedArray, + matrix, + DataFrame, + Series): ARRAY_COLOR, + Image: "#008000", + datetime.date: "#808000", + } +CUSTOM_TYPE_COLOR = "#7755aa" +UNSUPPORTED_COLOR = "#ffffff" + +def get_color_name(value): + """Return color name depending on value type""" + if not is_known_type(value): + return CUSTOM_TYPE_COLOR + for typ, name in list(COLORS.items()): + if isinstance(value, typ): + return name + else: + np_dtype = get_numpy_dtype(value) + if np_dtype is None or not hasattr(value, 'size'): + return UNSUPPORTED_COLOR + elif value.size == 1: + return SCALAR_COLOR + else: + return ARRAY_COLOR + + +def is_editable_type(value): + """Return True if data type is editable with a standard GUI-based editor, + like CollectionsEditor, ArrayEditor, QDateEdit or a simple QLineEdit""" + return get_color_name(value) not in (UNSUPPORTED_COLOR, CUSTOM_TYPE_COLOR) + + +#============================================================================== +# Sorting +#============================================================================== +def sort_against(list1, list2, reverse=False): + """ + Arrange items of list1 in the same order as sorted(list2). + + In other words, apply to list1 the permutation which takes list2 + to sorted(list2, reverse). + """ + try: + return [item for _, item in + sorted(zip(list2, list1), key=lambda x: x[0], reverse=reverse)] + except: + return list1 + + +def unsorted_unique(lista): + """Removes duplicates from lista neglecting its initial ordering""" + return list(set(lista)) + + +#============================================================================== +# Display <--> Value +#============================================================================== +def value_to_display(value, minmax=False): + """Convert value for display purpose""" + try: + if isinstance(value, recarray): + fields = value.names + display = 'Field names: ' + ', '.join(fields) + elif isinstance(value, MaskedArray): + display = 'Masked array' + elif isinstance(value, ndarray): + if minmax: + try: + display = 'Min: %r\nMax: %r' % (value.min(), value.max()) + except (TypeError, ValueError): + display = repr(value) + else: + display = repr(value) + elif isinstance(value, (list, tuple, dict, set)): + display = CollectionsRepr.repr(value) + elif isinstance(value, Image): + display = '%s Mode: %s' % (address(value), value.mode) + elif isinstance(value, DataFrame): + cols = value.columns + if PY2 and len(cols) > 0: + # Get rid of possible BOM utf-8 data present at the + # beginning of a file, which gets attached to the first + # column header when headers are present in the first + # row. + # Fixes Issue 2514 + try: + ini_col = to_text_string(cols[0], encoding='utf-8-sig') + except: + ini_col = to_text_string(cols[0]) + cols = [ini_col] + [to_text_string(c) for c in cols[1:]] + else: + cols = [to_text_string(c) for c in cols] + display = 'Column names: ' + ', '.join(list(cols)) + elif isinstance(value, NavigableString): + # Fixes Issue 2448 + display = to_text_string(value) + elif is_binary_string(value): + try: + display = to_text_string(value, 'utf8') + except: + display = value + elif is_text_string(value): + display = value + elif isinstance(value, NUMERIC_TYPES) or isinstance(value, bool) or \ + isinstance(value, datetime.date): + display = repr(value) + else: + # Note: Don't trust on repr's. They can be inefficient and + # so freeze Spyder quite easily + # display = repr(value) + type_str = to_text_string(type(value)) + display = type_str[1:-1] + except: + type_str = to_text_string(type(value)) + display = type_str[1:-1] + + # Truncate display at 80 chars to avoid freezing Spyder + # because of large displays + if len(display) > 80: + display = display[:80].rstrip() + ' ...' + + return display + + + +def display_to_value(value, default_value, ignore_errors=True): + """Convert back to value""" + from qtpy.compat import from_qvariant + value = from_qvariant(value, to_text_string) + try: + np_dtype = get_numpy_dtype(default_value) + if isinstance(default_value, bool): + # We must test for boolean before NumPy data types + # because `bool` class derives from `int` class + try: + value = bool(float(value)) + except ValueError: + value = value.lower() == "true" + elif np_dtype is not None: + if 'complex' in str(type(default_value)): + value = np_dtype(complex(value)) + else: + value = np_dtype(value) + elif is_binary_string(default_value): + value = to_binary_string(value, 'utf8') + elif is_text_string(default_value): + value = to_text_string(value) + elif isinstance(default_value, complex): + value = complex(value) + elif isinstance(default_value, float): + value = float(value) + elif isinstance(default_value, int): + try: + value = int(value) + except ValueError: + value = float(value) + elif isinstance(default_value, datetime.datetime): + value = datestr_to_datetime(value) + elif isinstance(default_value, datetime.date): + value = datestr_to_datetime(value).date() + elif ignore_errors: + value = try_to_eval(value) + else: + value = eval(value) + except (ValueError, SyntaxError): + if ignore_errors: + value = try_to_eval(value) + else: + return default_value + return value + + +#============================================================================== +# Types +#============================================================================== +def get_type_string(item): + """Return type string of an object""" + if isinstance(item, DataFrame): + return "DataFrame" + if isinstance(item, Series): + return "Series" + found = re.findall(r"<(?:type|class) '(\S*)'>", str(type(item))) + if found: + return found[0] + + +def is_known_type(item): + """Return True if object has a known type""" + # Unfortunately, the masked array case is specific + return isinstance(item, MaskedArray) or get_type_string(item) is not None + + +def get_human_readable_type(item): + """Return human-readable type string of an item""" + if isinstance(item, (ndarray, MaskedArray)): + return item.dtype.name + elif isinstance(item, Image): + return "Image" + else: + text = get_type_string(item) + if text is None: + text = to_text_string('unknown') + else: + return text[text.find('.')+1:] + + +#============================================================================== +# Globals filter: filter namespace dictionaries (to be edited in +# CollectionsEditor) +#============================================================================== +def is_supported(value, check_all=False, filters=None, iterate=True): + """Return True if the value is supported, False otherwise""" + assert filters is not None + if not is_editable_type(value): + return False + elif not isinstance(value, filters): + return False + elif iterate: + if isinstance(value, (list, tuple, set)): + for val in value: + if not is_supported(val, filters=filters, iterate=check_all): + return False + if not check_all: + break + elif isinstance(value, dict): + for key, val in list(value.items()): + if not is_supported(key, filters=filters, iterate=check_all) \ + or not is_supported(val, filters=filters, + iterate=check_all): + return False + if not check_all: + break + return True + + +def globalsfilter(input_dict, check_all=False, filters=None, + exclude_private=None, exclude_capitalized=None, + exclude_uppercase=None, exclude_unsupported=None, + excluded_names=None): + """Keep only objects that can be pickled""" + output_dict = {} + for key, value in list(input_dict.items()): + excluded = (exclude_private and key.startswith('_')) or \ + (exclude_capitalized and key[0].isupper()) or \ + (exclude_uppercase and key.isupper() + and len(key) > 1 and not key[1:].isdigit()) or \ + (key in excluded_names) or \ + (exclude_unsupported and \ + not is_supported(value, check_all=check_all, + filters=filters)) + if not excluded: + output_dict[key] = value + return output_dict + + +#============================================================================== +# Create view to be displayed by NamespaceBrowser +#============================================================================== +REMOTE_SETTINGS = ('check_all', 'exclude_private', 'exclude_uppercase', + 'exclude_capitalized', 'exclude_unsupported', + 'excluded_names', 'minmax', 'remote_editing', + 'autorefresh') + + +def get_remote_data(data, settings, mode, more_excluded_names=None): + """ + Return globals according to filter described in *settings*: + * data: data to be filtered (dictionary) + * settings: variable explorer settings (dictionary) + * mode (string): 'editable' or 'picklable' + * more_excluded_names: additional excluded names (list) + """ + supported_types = get_supported_types() + assert mode in list(supported_types.keys()) + excluded_names = settings['excluded_names'] + if more_excluded_names is not None: + excluded_names += more_excluded_names + return globalsfilter(data, check_all=settings['check_all'], + filters=tuple(supported_types[mode]), + exclude_private=settings['exclude_private'], + exclude_uppercase=settings['exclude_uppercase'], + exclude_capitalized=settings['exclude_capitalized'], + exclude_unsupported=settings['exclude_unsupported'], + excluded_names=excluded_names) + + +def make_remote_view(data, settings, more_excluded_names=None): + """ + Make a remote view of dictionary *data* + -> globals explorer + """ + assert all([name in REMOTE_SETTINGS for name in settings]) + data = get_remote_data(data, settings, mode='editable', + more_excluded_names=more_excluded_names) + remote = {} + for key, value in list(data.items()): + view = value_to_display(value, minmax=settings['minmax']) + remote[key] = {'type': get_human_readable_type(value), + 'size': get_size(value), + 'color': get_color_name(value), + 'view': view} + return remote diff -Nru spyder-2.3.8+dfsg1/spyder/workers/updates.py spyder-3.0.2+dfsg1/spyder/workers/updates.py --- spyder-2.3.8+dfsg1/spyder/workers/updates.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder/workers/updates.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +# Standard library imports +import json +import ssl + +# Third party imports +from qtpy.QtCore import QObject, Signal + +# Local imports +from spyder import __version__ +from spyder.config.base import _ +from spyder.py3compat import PY3 +from spyder.utils.programs import check_version, is_stable_version + + +if PY3: + from urllib.request import urlopen + from urllib.error import URLError, HTTPError +else: + from urllib2 import urlopen, URLError, HTTPError + + +class WorkerUpdates(QObject): + """ + Worker that checks for releases using the Github API without blocking the + Spyder user interface, in case of connections issues. + """ + sig_ready = Signal() + + def __init__(self, parent): + QObject.__init__(self) + self._parent = parent + self.error = None + self.latest_release = None + + def check_update_available(self, version, releases): + """Checks if there is an update available. + + It takes as parameters the current version of Spyder and a list of + valid cleaned releases in chronological order (what github api returns + by default). Example: ['2.3.4', '2.3.3' ...] + """ + if is_stable_version(version): + # Remove non stable versions from the list + releases = [r for r in releases if is_stable_version(r)] + + latest_release = releases[0] + + if version.endswith('dev'): + return (False, latest_release) + + return (check_version(version, latest_release, '<'), latest_release) + + def start(self): + """Main method of the WorkerUpdates worker""" + self.url = 'https://api.github.com/repos/spyder-ide/spyder/releases' + self.update_available = False + self.latest_release = __version__ + + error_msg = None + + try: + if hasattr(ssl, '_create_unverified_context'): + # Fix for issue # 2685 [Works only with Python >=2.7.9] + # More info: https://www.python.org/dev/peps/pep-0476/#opting-out + context = ssl._create_unverified_context() + page = urlopen(self.url, context=context) + else: + page = urlopen(self.url) + try: + data = page.read() + + # Needed step for python3 compatibility + if not isinstance(data, str): + data = data.decode() + + data = json.loads(data) + releases = [item['tag_name'].replace('v', '') for item in data] + version = __version__ + + result = self.check_update_available(version, releases) + self.update_available, self.latest_release = result + except Exception: + error_msg = _('Unable to retrieve information.') + except HTTPError: + error_msg = _('Unable to retrieve information.') + except URLError: + error_msg = _('Unable to connect to the internet.

    Make ' + 'sure the connection is working properly.') + except Exception: + error_msg = _('Unable to check for updates.') + + self.error = error_msg + self.sig_ready.emit() diff -Nru spyder-2.3.8+dfsg1/spyder_breakpoints/breakpoints.py spyder-3.0.2+dfsg1/spyder_breakpoints/breakpoints.py --- spyder-2.3.8+dfsg1/spyder_breakpoints/breakpoints.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_breakpoints/breakpoints.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,104 @@ +# -*- coding:utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Based loosely on p_pylint.py by Pierre Raybaut +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Breakpoint Plugin""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os.path as osp + +# Local imports +from spyder.config.base import get_translation +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import create_action +from spyder.plugins import SpyderPluginMixin +from spyder.py3compat import to_text_string, is_text_string +from .widgets.breakpointsgui import BreakpointWidget + +_ = get_translation("breakpoints", "spyder_breakpoints") + + +class Breakpoints(BreakpointWidget, SpyderPluginMixin): + """Breakpoint list""" + CONF_SECTION = 'breakpoints' + +# CONFIGWIDGET_CLASS = BreakpointConfigPage + def __init__(self, parent=None): + BreakpointWidget.__init__(self, parent=parent) + SpyderPluginMixin.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + self.set_data() + + #------ SpyderPluginWidget API -------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _("Breakpoints") + + def get_plugin_icon(self): + """Return widget icon""" + path = osp.join(self.PLUGIN_PATH, self.IMG_PATH) + return ima.icon('profiler', icon_path=path) + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.dictwidget + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + return [] + + def on_first_registration(self): + """Action to be performed on first plugin registration""" + self.main.tabify_plugins(self.main.help, self) + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.edit_goto.connect(self.main.editor.load) + #self.redirect_stdio.connect(self.main.redirect_internalshell_stdio) + self.clear_all_breakpoints.connect( + self.main.editor.clear_all_breakpoints) + self.clear_breakpoint.connect(self.main.editor.clear_breakpoint) + self.main.editor.breakpoints_saved.connect(self.set_data) + self.set_or_edit_conditional_breakpoint.connect( + self.main.editor.set_or_edit_conditional_breakpoint) + + self.main.add_dockwidget(self) + + list_action = create_action(self, _("List breakpoints"), + triggered=self.show) + list_action.setEnabled(True) + pos = self.main.debug_menu_actions.index('list_breakpoints') + self.main.debug_menu_actions.insert(pos, list_action) + self.main.editor.pythonfile_dependent_actions += [list_action] + + def refresh_plugin(self): + """Refresh widget""" + pass + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + return True + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + pass + + def show(self): + """Show the breakpoints dockwidget""" + if self.dockwidget and not self.ismaximized: + self.dockwidget.setVisible(True) + self.dockwidget.setFocus() + self.dockwidget.raise_() diff -Nru spyder-2.3.8+dfsg1/spyder_breakpoints/__init__.py spyder-3.0.2+dfsg1/spyder_breakpoints/__init__.py --- spyder-2.3.8+dfsg1/spyder_breakpoints/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_breakpoints/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- + +#============================================================================== +# The following statement is required to register this 3rd party plugin: +#============================================================================== +from .breakpoints import Breakpoints as PLUGIN_CLASS diff -Nru spyder-2.3.8+dfsg1/spyder_breakpoints/locale/breakpoints.pot spyder-3.0.2+dfsg1/spyder_breakpoints/locale/breakpoints.pot --- spyder-2.3.8+dfsg1/spyder_breakpoints/locale/breakpoints.pot 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_breakpoints/locale/breakpoints.pot 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,49 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: spyder_breakpoints/breakpoints.py:45 +msgid "Breakpoints" +msgstr "" + +#: spyder_breakpoints/breakpoints.py:80 +msgid "List breakpoints" +msgstr "" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Condition" +msgstr "" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "File" +msgstr "" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Line" +msgstr "" + +#: spyder_breakpoints/widgets/breakpointsgui.py:180 +msgid "Clear breakpoints in all files" +msgstr "" + +#: spyder_breakpoints/widgets/breakpointsgui.py:201 +msgid "Clear this breakpoint" +msgstr "" + +#: spyder_breakpoints/widgets/breakpointsgui.py:206 +msgid "Edit this breakpoint" +msgstr "" + Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_breakpoints/locale/es/LC_MESSAGES/breakpoints.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_breakpoints/locale/es/LC_MESSAGES/breakpoints.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_breakpoints/locale/es/LC_MESSAGES/breakpoints.po spyder-3.0.2+dfsg1/spyder_breakpoints/locale/es/LC_MESSAGES/breakpoints.po --- spyder-2.3.8+dfsg1/spyder_breakpoints/locale/es/LC_MESSAGES/breakpoints.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_breakpoints/locale/es/LC_MESSAGES/breakpoints.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: 2.2\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2013-05-07 17:37-0500\n" +"Last-Translator: Carlos Cordoba \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" + +#: spyder_breakpoints/breakpoints.py:45 +msgid "Breakpoints" +msgstr "Puntos de interrupción" + +#: spyder_breakpoints/breakpoints.py:80 +msgid "List breakpoints" +msgstr "Listar puntos de interrupción" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Condition" +msgstr "Condición" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "File" +msgstr "Archivo" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Line" +msgstr "Línea" + +#: spyder_breakpoints/widgets/breakpointsgui.py:180 +msgid "Clear breakpoints in all files" +msgstr "Borrar los puntos de todos los archivos" + +#: spyder_breakpoints/widgets/breakpointsgui.py:201 +msgid "Clear this breakpoint" +msgstr "Eliminar este punto" + +#: spyder_breakpoints/widgets/breakpointsgui.py:206 +#, fuzzy +msgid "Edit this breakpoint" +msgstr "Eliminar este punto" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_breakpoints/locale/fr/LC_MESSAGES/breakpoints.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_breakpoints/locale/fr/LC_MESSAGES/breakpoints.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_breakpoints/locale/fr/LC_MESSAGES/breakpoints.po spyder-3.0.2+dfsg1/spyder_breakpoints/locale/fr/LC_MESSAGES/breakpoints.po --- spyder-2.3.8+dfsg1/spyder_breakpoints/locale/fr/LC_MESSAGES/breakpoints.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_breakpoints/locale/fr/LC_MESSAGES/breakpoints.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: utf-8\n" +"Generated-By: pygettext.py 1.5\n" + +#: spyder_breakpoints/breakpoints.py:45 +msgid "Breakpoints" +msgstr "Points d'arrêt" + +#: spyder_breakpoints/breakpoints.py:80 +msgid "List breakpoints" +msgstr "Liste des points d'arrêt" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Condition" +msgstr "Condition" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "File" +msgstr "Fichier" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Line" +msgstr "Ligne" + +#: spyder_breakpoints/widgets/breakpointsgui.py:180 +msgid "Clear breakpoints in all files" +msgstr "Supprimer tous les points d'arrêts" + +#: spyder_breakpoints/widgets/breakpointsgui.py:201 +msgid "Clear this breakpoint" +msgstr "Supprimer ce point d'arrêt" + +#: spyder_breakpoints/widgets/breakpointsgui.py:206 +msgid "Edit this breakpoint" +msgstr "Supprimer ce point d'arrêt" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_breakpoints/locale/pt_BR/LC_MESSAGES/breakpoints.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_breakpoints/locale/pt_BR/LC_MESSAGES/breakpoints.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_breakpoints/locale/pt_BR/LC_MESSAGES/breakpoints.po spyder-3.0.2+dfsg1/spyder_breakpoints/locale/pt_BR/LC_MESSAGES/breakpoints.po --- spyder-2.3.8+dfsg1/spyder_breakpoints/locale/pt_BR/LC_MESSAGES/breakpoints.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_breakpoints/locale/pt_BR/LC_MESSAGES/breakpoints.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Spyder's brazilian portuguese translation file +# Valter Nazianzeno , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2015-02-25 22:08-0400\n" +"Last-Translator: Valter Nazianzeno \n" +"Language-Team: \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.7.4\n" + +#: spyder_breakpoints/breakpoints.py:45 +msgid "Breakpoints" +msgstr "Pontos de interrupção" + +#: spyder_breakpoints/breakpoints.py:80 +msgid "List breakpoints" +msgstr "Listar pontos de interrupção" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Condition" +msgstr "Condição" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "File" +msgstr "Arquivo" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Line" +msgstr "Linha" + +#: spyder_breakpoints/widgets/breakpointsgui.py:180 +msgid "Clear breakpoints in all files" +msgstr "Limpar pontos de interrupção em todos os arquivos" + +#: spyder_breakpoints/widgets/breakpointsgui.py:201 +msgid "Clear this breakpoint" +msgstr "Limpar este ponto" + +#: spyder_breakpoints/widgets/breakpointsgui.py:206 +msgid "Edit this breakpoint" +msgstr "Editar este ponto" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_breakpoints/locale/ru/LC_MESSAGES/breakpoints.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_breakpoints/locale/ru/LC_MESSAGES/breakpoints.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_breakpoints/locale/ru/LC_MESSAGES/breakpoints.po spyder-3.0.2+dfsg1/spyder_breakpoints/locale/ru/LC_MESSAGES/breakpoints.po --- spyder-2.3.8+dfsg1/spyder_breakpoints/locale/ru/LC_MESSAGES/breakpoints.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_breakpoints/locale/ru/LC_MESSAGES/breakpoints.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,51 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2016-04-18 19:58+0300\n" +"Last-Translator: Roman Kulagin \n" +"Language-Team: \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.5.4\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: spyder_breakpoints/breakpoints.py:45 +msgid "Breakpoints" +msgstr "Точки останова" + +#: spyder_breakpoints/breakpoints.py:80 +msgid "List breakpoints" +msgstr "Список точек останова" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Condition" +msgstr "Условие" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "File" +msgstr "Файл" + +#: spyder_breakpoints/widgets/breakpointsgui.py:97 +msgid "Line" +msgstr "Строка" + +#: spyder_breakpoints/widgets/breakpointsgui.py:180 +msgid "Clear breakpoints in all files" +msgstr "Очистить все точки останова" + +#: spyder_breakpoints/widgets/breakpointsgui.py:201 +msgid "Clear this breakpoint" +msgstr "Убрать эту точку останова" + +#: spyder_breakpoints/widgets/breakpointsgui.py:206 +msgid "Edit this breakpoint" +msgstr "Редактировать эту точку останова" diff -Nru spyder-2.3.8+dfsg1/spyder_breakpoints/widgets/breakpointsgui.py spyder-3.0.2+dfsg1/spyder_breakpoints/widgets/breakpointsgui.py --- spyder-2.3.8+dfsg1/spyder_breakpoints/widgets/breakpointsgui.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_breakpoints/widgets/breakpointsgui.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,272 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# based loosley on pylintgui.py by Pierre Raybaut +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Breakpoint widget""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os.path as osp +import sys + +# Third party imports +from qtpy import API +from qtpy.compat import to_qvariant +from qtpy.QtCore import (QAbstractTableModel, QModelIndex, QTextCodec, Qt, + Signal) +from qtpy.QtWidgets import (QItemDelegate, QMenu, QTableView, QVBoxLayout, + QWidget) + +# Local imports +from spyder.config.base import get_translation +from spyder.config.main import CONF +from spyder.utils.qthelpers import add_actions, create_action + +# This is needed for testing this module as a stand alone script +try: + _ = get_translation("breakpoints", "spyder_breakpoints") +except KeyError as error: + import gettext + _ = gettext.gettext + + +locale_codec = QTextCodec.codecForLocale() + + +class BreakpointTableModel(QAbstractTableModel): + """ + Table model for breakpoints dictionary + + """ + def __init__(self, parent, data): + QAbstractTableModel.__init__(self, parent) + if data is None: + data = {} + self._data = None + self.breakpoints = None + self.set_data(data) + + def set_data(self, data): + """Set model data""" + self._data = data + keys = list(data.keys()) + self.breakpoints = [] + for key in keys: + bp_list = data[key] + if bp_list: + for item in data[key]: + self.breakpoints.append((key, item[0], item[1], "")) + self.reset() + + def rowCount(self, qindex=QModelIndex()): + """Array row number""" + return len(self.breakpoints) + + def columnCount(self, qindex=QModelIndex()): + """Array column count""" + return 4 + + def sort(self, column, order=Qt.DescendingOrder): + """Overriding sort method""" + if column == 0: + self.breakpoints.sort( + key=lambda breakpoint: breakpoint[1]) + self.breakpoints.sort( + key=lambda breakpoint: osp.basename(breakpoint[0])) + elif column == 1: + pass + elif column == 2: + pass + elif column == 3: + pass + self.reset() + + def headerData(self, section, orientation, role=Qt.DisplayRole): + """Overriding method headerData""" + if role != Qt.DisplayRole: + return to_qvariant() + i_column = int(section) + if orientation == Qt.Horizontal: + headers = (_("File"), _("Line"), _("Condition"), "") + return to_qvariant( headers[i_column] ) + else: + return to_qvariant() + + def get_value(self, index): + """Return current value""" + return self.breakpoints[index.row()][index.column()] + + def data(self, index, role=Qt.DisplayRole): + """Return data at table index""" + if not index.isValid(): + return to_qvariant() + if role == Qt.DisplayRole: + if index.column() == 0: + value = osp.basename(self.get_value(index)) + return to_qvariant(value) + else: + value = self.get_value(index) + return to_qvariant(value) + elif role == Qt.TextAlignmentRole: + return to_qvariant(int(Qt.AlignLeft|Qt.AlignVCenter)) + elif role == Qt.ToolTipRole: + if index.column() == 0: + value = self.get_value(index) + return to_qvariant(value) + else: + return to_qvariant() + + def reset(self): + self.beginResetModel() + self.endResetModel() + + +class BreakpointDelegate(QItemDelegate): + def __init__(self, parent=None): + QItemDelegate.__init__(self, parent) + + +class BreakpointTableView(QTableView): + edit_goto = Signal(str, int, str) + clear_breakpoint = Signal(str, int) + clear_all_breakpoints = Signal() + set_or_edit_conditional_breakpoint = Signal() + + def __init__(self, parent, data): + QTableView.__init__(self, parent) + self.model = BreakpointTableModel(self, data) + self.setModel(self.model) + self.delegate = BreakpointDelegate(self) + self.setItemDelegate(self.delegate) + + self.setup_table() + + def setup_table(self): + """Setup table""" + self.horizontalHeader().setStretchLastSection(True) + self.adjust_columns() + self.columnAt(0) + # Sorting columns + self.setSortingEnabled(False) + self.sortByColumn(0, Qt.DescendingOrder) + + def adjust_columns(self): + """Resize three first columns to contents""" + for col in range(3): + self.resizeColumnToContents(col) + + def mouseDoubleClickEvent(self, event): + """Reimplement Qt method""" + index_clicked = self.indexAt(event.pos()) + if self.model.breakpoints: + filename = self.model.breakpoints[index_clicked.row()][0] + line_number_str = self.model.breakpoints[index_clicked.row()][1] + self.edit_goto.emit(filename, int(line_number_str), '') + if index_clicked.column()==2: + self.set_or_edit_conditional_breakpoint.emit() + + def contextMenuEvent(self, event): + index_clicked = self.indexAt(event.pos()) + actions = [] + self.popup_menu = QMenu(self) + clear_all_breakpoints_action = create_action(self, + _("Clear breakpoints in all files"), + triggered=lambda: self.clear_all_breakpoints.emit()) + actions.append(clear_all_breakpoints_action) + if self.model.breakpoints: + filename = self.model.breakpoints[index_clicked.row()][0] + lineno = int(self.model.breakpoints[index_clicked.row()][1]) + # QAction.triggered works differently for PySide and PyQt + if not API == 'pyside': + clear_slot = lambda _checked, filename=filename, lineno=lineno: \ + self.clear_breakpoint.emit(filename, lineno) + edit_slot = lambda _checked, filename=filename, lineno=lineno: \ + (self.edit_goto.emit(filename, lineno, ''), + self.set_or_edit_conditional_breakpoint.emit()) + else: + clear_slot = lambda filename=filename, lineno=lineno: \ + self.clear_breakpoint.emit(filename, lineno) + edit_slot = lambda filename=filename, lineno=lineno: \ + (self.edit_goto.emit(filename, lineno, ''), + self.set_or_edit_conditional_breakpoint.emit()) + + clear_breakpoint_action = create_action(self, + _("Clear this breakpoint"), + triggered=clear_slot) + actions.insert(0,clear_breakpoint_action) + + edit_breakpoint_action = create_action(self, + _("Edit this breakpoint"), + triggered=edit_slot) + actions.append(edit_breakpoint_action) + add_actions(self.popup_menu, actions) + self.popup_menu.popup(event.globalPos()) + event.accept() + + +class BreakpointWidget(QWidget): + """ + Breakpoint widget + """ + VERSION = '1.0.0' + clear_all_breakpoints = Signal() + set_or_edit_conditional_breakpoint = Signal() + clear_breakpoint = Signal(str, int) + edit_goto = Signal(str, int, str) + + def __init__(self, parent): + QWidget.__init__(self, parent) + + self.setWindowTitle("Breakpoints") + self.dictwidget = BreakpointTableView(self, + self._load_all_breakpoints()) + layout = QVBoxLayout() + layout.addWidget(self.dictwidget) + self.setLayout(layout) + self.dictwidget.clear_all_breakpoints.connect( + lambda: self.clear_all_breakpoints.emit()) + self.dictwidget.clear_breakpoint.connect( + lambda s1, lino: self.clear_breakpoint.emit(s1, lino)) + self.dictwidget.edit_goto.connect( + lambda s1, lino, s2: self.edit_goto.emit(s1, lino, s2)) + self.dictwidget.set_or_edit_conditional_breakpoint.connect( + lambda: self.set_or_edit_conditional_breakpoint.emit()) + + def _load_all_breakpoints(self): + bp_dict = CONF.get('run', 'breakpoints', {}) + for filename in list(bp_dict.keys()): + if not osp.isfile(filename): + bp_dict.pop(filename) + return bp_dict + + def get_data(self): + pass + + def set_data(self): + bp_dict = self._load_all_breakpoints() + self.dictwidget.model.set_data(bp_dict) + self.dictwidget.adjust_columns() + self.dictwidget.sortByColumn(0, Qt.DescendingOrder) + + +#============================================================================== +# Tests +#============================================================================== +def test(): + """Run breakpoint widget test""" + from spyder.utils.qthelpers import qapplication + app = qapplication() + widget = BreakpointWidget(None) + widget.show() + sys.exit(app.exec_()) + + +if __name__ == '__main__': + test() diff -Nru spyder-2.3.8+dfsg1/spyder.egg-info/dependency_links.txt spyder-3.0.2+dfsg1/spyder.egg-info/dependency_links.txt --- spyder-2.3.8+dfsg1/spyder.egg-info/dependency_links.txt 2015-11-27 13:35:50.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder.egg-info/dependency_links.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ - diff -Nru spyder-2.3.8+dfsg1/spyder.egg-info/PKG-INFO spyder-3.0.2+dfsg1/spyder.egg-info/PKG-INFO --- spyder-2.3.8+dfsg1/spyder.egg-info/PKG-INFO 2015-11-27 13:35:50.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder.egg-info/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -Metadata-Version: 1.1 -Name: spyder -Version: 2.3.8 -Summary: Scientific PYthon Development EnviRonment -Home-page: https://github.com/spyder-ide/spyder -Author: Pierre Raybaut -Author-email: UNKNOWN -License: MIT -Download-URL: https://github.com/spyder-ide/spyder/files/spyder-2.3.8.zip -Description: Spyder is an interactive Python development environment providing - MATLAB-like features in a simple and light-weighted software. - It also provides ready-to-use pure-Python widgets to your PyQt4 or - PySide application: source code editor with syntax highlighting and - code introspection/analysis features, NumPy array editor, dictionary - editor, Python console, etc. -Keywords: PyQt4 PySide editor shell console widgets IDE -Platform: any -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: MacOS -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: OS Independent -Classifier: Operating System :: POSIX -Classifier: Operating System :: Unix -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Development Status :: 5 - Production/Stable -Classifier: Topic :: Scientific/Engineering -Classifier: Topic :: Software Development :: Widget Sets -Requires: rope (>=0.9.2) -Requires: sphinx (>=0.6.0) -Requires: PyQt4 (>=4.4) diff -Nru spyder-2.3.8+dfsg1/spyder.egg-info/SOURCES.txt spyder-3.0.2+dfsg1/spyder.egg-info/SOURCES.txt --- spyder-2.3.8+dfsg1/spyder.egg-info/SOURCES.txt 2015-11-27 13:35:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder.egg-info/SOURCES.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,692 +0,0 @@ -LICENSE -MANIFEST.in -README.md -bootstrap.py -setup.py -app_example/create_exe.py -app_example/example.pyw -doc/conf.py -doc/console.rst -doc/debugging.rst -doc/editor.rst -doc/explorer.rst -doc/findinfiles.rst -doc/historylog.rst -doc/index.rst -doc/inspector.rst -doc/installation.rst -doc/internalconsole.rst -doc/ipythonconsole.rst -doc/lightmode.rst -doc/onlinehelp.rst -doc/options.rst -doc/overview.rst -doc/projectexplorer.rst -doc/pylint.rst -doc/spyder_bbg.png -doc/variableexplorer.rst -doc/_static/favicon.ico -doc/images/arrayeditor.png -doc/images/console.png -doc/images/dicteditor.png -doc/images/editor1.png -doc/images/editor2.png -doc/images/editor3.png -doc/images/explorer.png -doc/images/explorer_menu1.png -doc/images/explorer_menu2.png -doc/images/findinfiles.png -doc/images/git_install_dialog.png -doc/images/historylog.png -doc/images/inspector_plain.png -doc/images/inspector_rich.png -doc/images/inspector_source.png -doc/images/internalconsole.png -doc/images/ipythonconsole.png -doc/images/ipythonconsolemenu.png -doc/images/ipythonkernelconnect.png -doc/images/lightmode.png -doc/images/listeditor.png -doc/images/onlinehelp.png -doc/images/projectexplorer.png -doc/images/projectexplorer2.png -doc/images/pylint.png -doc/images/texteditor.png -doc/images/variableexplorer-imshow.png -doc/images/variableexplorer-plot.png -doc/images/variableexplorer1.png -doc/images/variableexplorer2.png -img_src/spyder.ico -img_src/spyder.png -img_src/spyder3.png -img_src/spyder_light.ico -scripts/spyder -scripts/spyder.bat -scripts/spyder.desktop -scripts/spyder3 -scripts/spyder3.desktop -scripts/spyder_win_post_install.py -spyder.egg-info/PKG-INFO -spyder.egg-info/SOURCES.txt -spyder.egg-info/dependency_links.txt -spyder.egg-info/top_level.txt -spyderlib/__init__.py -spyderlib/baseconfig.py -spyderlib/cli_options.py -spyderlib/config.py -spyderlib/dependencies.py -spyderlib/guiconfig.py -spyderlib/interpreter.py -spyderlib/ipythonconfig.py -spyderlib/mac_stylesheet.qss -spyderlib/otherplugins.py -spyderlib/pil_patch.py -spyderlib/py3compat.py -spyderlib/pygments_patch.py -spyderlib/pyplot.py -spyderlib/requirements.py -spyderlib/rope_patch.py -spyderlib/scientific_startup.py -spyderlib/spyder.py -spyderlib/start_app.py -spyderlib/userconfig.py -spyderlib/defaults/Readme.txt -spyderlib/defaults/defaults-2.4.0.ini -spyderlib/defaults/defaults-3.0.0.ini -spyderlib/images/advanced.png -spyderlib/images/arredit.png -spyderlib/images/arrow.png -spyderlib/images/bold.png -spyderlib/images/browser.png -spyderlib/images/chevron-left.png -spyderlib/images/chevron-right.png -spyderlib/images/dictedit.png -spyderlib/images/font.png -spyderlib/images/genprefs.png -spyderlib/images/inspector.png -spyderlib/images/italic.png -spyderlib/images/matplotlib.png -spyderlib/images/none.png -spyderlib/images/not_found.png -spyderlib/images/options.svg -spyderlib/images/pythonpath_mgr.png -spyderlib/images/pythonxy.png -spyderlib/images/qt.png -spyderlib/images/qtassistant.png -spyderlib/images/qtdesigner.png -spyderlib/images/qtlinguist.png -spyderlib/images/scipy.png -spyderlib/images/set_workdir.png -spyderlib/images/splash.png -spyderlib/images/spyder.svg -spyderlib/images/spyder_light.svg -spyderlib/images/upper_lower.png -spyderlib/images/vcs_browse.png -spyderlib/images/vcs_commit.png -spyderlib/images/vitables.png -spyderlib/images/whole_words.png -spyderlib/images/win_env.png -spyderlib/images/winpython.svg -spyderlib/images/actions/1downarrow.png -spyderlib/images/actions/1uparrow.png -spyderlib/images/actions/2downarrow.png -spyderlib/images/actions/2uparrow.png -spyderlib/images/actions/arrow-continue.png -spyderlib/images/actions/arrow-step-in.png -spyderlib/images/actions/arrow-step-out.png -spyderlib/images/actions/arrow-step-over.png -spyderlib/images/actions/auto_reload.png -spyderlib/images/actions/browse_tab.png -spyderlib/images/actions/check.png -spyderlib/images/actions/cmdprompt.png -spyderlib/images/actions/collapse.png -spyderlib/images/actions/collapse_selection.png -spyderlib/images/actions/configure.png -spyderlib/images/actions/copywop.png -spyderlib/images/actions/delete.png -spyderlib/images/actions/edit.png -spyderlib/images/actions/edit24.png -spyderlib/images/actions/edit_add.png -spyderlib/images/actions/edit_remove.png -spyderlib/images/actions/editcopy.png -spyderlib/images/actions/editcut.png -spyderlib/images/actions/editdelete.png -spyderlib/images/actions/editpaste.png -spyderlib/images/actions/eraser.png -spyderlib/images/actions/exit.png -spyderlib/images/actions/expand.png -spyderlib/images/actions/expand_selection.png -spyderlib/images/actions/filter.png -spyderlib/images/actions/find.png -spyderlib/images/actions/findf.png -spyderlib/images/actions/findnext.png -spyderlib/images/actions/findprevious.png -spyderlib/images/actions/folder_new.png -spyderlib/images/actions/hide.png -spyderlib/images/actions/hist.png -spyderlib/images/actions/home.png -spyderlib/images/actions/imshow.png -spyderlib/images/actions/insert.png -spyderlib/images/actions/lock.png -spyderlib/images/actions/lock_open.png -spyderlib/images/actions/magnifier.png -spyderlib/images/actions/maximize.png -spyderlib/images/actions/next.png -spyderlib/images/actions/options_less.png -spyderlib/images/actions/options_more.png -spyderlib/images/actions/plot.png -spyderlib/images/actions/previous.png -spyderlib/images/actions/redo.png -spyderlib/images/actions/reload.png -spyderlib/images/actions/rename.png -spyderlib/images/actions/replace.png -spyderlib/images/actions/restore.png -spyderlib/images/actions/show.png -spyderlib/images/actions/special_paste.png -spyderlib/images/actions/stop.png -spyderlib/images/actions/stop_debug.png -spyderlib/images/actions/synchronize.png -spyderlib/images/actions/tooloptions.png -spyderlib/images/actions/undo.png -spyderlib/images/actions/unmaximize.png -spyderlib/images/actions/up.png -spyderlib/images/actions/window_fullscreen.png -spyderlib/images/actions/window_nofullscreen.png -spyderlib/images/actions/zoom_in.png -spyderlib/images/actions/zoom_out.png -spyderlib/images/console/clear.png -spyderlib/images/console/cmdprompt_t.png -spyderlib/images/console/console.png -spyderlib/images/console/environ.png -spyderlib/images/console/history.png -spyderlib/images/console/history24.png -spyderlib/images/console/ipython_console.png -spyderlib/images/console/ipython_console_t.png -spyderlib/images/console/kill.png -spyderlib/images/console/loading_sprites.png -spyderlib/images/console/prompt.png -spyderlib/images/console/python.png -spyderlib/images/console/python_t.png -spyderlib/images/console/restart.png -spyderlib/images/console/run_small.png -spyderlib/images/console/syspath.png -spyderlib/images/console/terminated.png -spyderlib/images/editor/blockcomment.png -spyderlib/images/editor/breakpoint_big.png -spyderlib/images/editor/breakpoint_cond_big.png -spyderlib/images/editor/breakpoint_cond_small.png -spyderlib/images/editor/breakpoint_small.png -spyderlib/images/editor/bug.png -spyderlib/images/editor/cell.png -spyderlib/images/editor/class.png -spyderlib/images/editor/close_panel.png -spyderlib/images/editor/comment.png -spyderlib/images/editor/convention.png -spyderlib/images/editor/debug.png -spyderlib/images/editor/error.png -spyderlib/images/editor/file.png -spyderlib/images/editor/filelist.png -spyderlib/images/editor/fromcursor.png -spyderlib/images/editor/function.png -spyderlib/images/editor/gotoline.png -spyderlib/images/editor/highlight.png -spyderlib/images/editor/horsplit.png -spyderlib/images/editor/indent.png -spyderlib/images/editor/last_edit_location.png -spyderlib/images/editor/method.png -spyderlib/images/editor/newwindow.png -spyderlib/images/editor/next_cursor.png -spyderlib/images/editor/next_wng.png -spyderlib/images/editor/outline_explorer.png -spyderlib/images/editor/outline_explorer_vis.png -spyderlib/images/editor/prev_cursor.png -spyderlib/images/editor/prev_wng.png -spyderlib/images/editor/private1.png -spyderlib/images/editor/private2.png -spyderlib/images/editor/refactor.png -spyderlib/images/editor/run.png -spyderlib/images/editor/run_again.png -spyderlib/images/editor/run_cell.png -spyderlib/images/editor/run_cell_advance.png -spyderlib/images/editor/run_selection.png -spyderlib/images/editor/run_settings.png -spyderlib/images/editor/select.png -spyderlib/images/editor/selectall.png -spyderlib/images/editor/todo.png -spyderlib/images/editor/todo_list.png -spyderlib/images/editor/uncomment.png -spyderlib/images/editor/unindent.png -spyderlib/images/editor/versplit.png -spyderlib/images/editor/warning.png -spyderlib/images/editor/wng_list.png -spyderlib/images/file/fileclose.png -spyderlib/images/file/filecloseall.png -spyderlib/images/file/fileimport.png -spyderlib/images/file/filenew.png -spyderlib/images/file/fileopen.png -spyderlib/images/file/filesave.png -spyderlib/images/file/filesaveas.png -spyderlib/images/file/print.png -spyderlib/images/file/save_all.png -spyderlib/images/filetypes/bat.png -spyderlib/images/filetypes/bmp.png -spyderlib/images/filetypes/c.png -spyderlib/images/filetypes/cc.png -spyderlib/images/filetypes/cfg.png -spyderlib/images/filetypes/chm.png -spyderlib/images/filetypes/cl.png -spyderlib/images/filetypes/cmd.png -spyderlib/images/filetypes/cpp.png -spyderlib/images/filetypes/css.png -spyderlib/images/filetypes/cxx.png -spyderlib/images/filetypes/diff.png -spyderlib/images/filetypes/doc.png -spyderlib/images/filetypes/enaml.png -spyderlib/images/filetypes/exe.png -spyderlib/images/filetypes/f.png -spyderlib/images/filetypes/f77.png -spyderlib/images/filetypes/f90.png -spyderlib/images/filetypes/gif.png -spyderlib/images/filetypes/h.png -spyderlib/images/filetypes/hh.png -spyderlib/images/filetypes/hpp.png -spyderlib/images/filetypes/htm.png -spyderlib/images/filetypes/html.png -spyderlib/images/filetypes/hxx.png -spyderlib/images/filetypes/inf.png -spyderlib/images/filetypes/ini.png -spyderlib/images/filetypes/jl.png -spyderlib/images/filetypes/jpeg.png -spyderlib/images/filetypes/jpg.png -spyderlib/images/filetypes/js.png -spyderlib/images/filetypes/log.png -spyderlib/images/filetypes/nsh.png -spyderlib/images/filetypes/nsi.png -spyderlib/images/filetypes/nt.png -spyderlib/images/filetypes/patch.png -spyderlib/images/filetypes/pdf.png -spyderlib/images/filetypes/png.png -spyderlib/images/filetypes/po.png -spyderlib/images/filetypes/pot.png -spyderlib/images/filetypes/pps.png -spyderlib/images/filetypes/properties.png -spyderlib/images/filetypes/ps.png -spyderlib/images/filetypes/pxd.png -spyderlib/images/filetypes/pxi.png -spyderlib/images/filetypes/py.png -spyderlib/images/filetypes/pyc.png -spyderlib/images/filetypes/pyw.png -spyderlib/images/filetypes/pyx.png -spyderlib/images/filetypes/rar.png -spyderlib/images/filetypes/readme.png -spyderlib/images/filetypes/reg.png -spyderlib/images/filetypes/rej.png -spyderlib/images/filetypes/session.png -spyderlib/images/filetypes/tar.png -spyderlib/images/filetypes/tex.png -spyderlib/images/filetypes/tgz.png -spyderlib/images/filetypes/tif.png -spyderlib/images/filetypes/tiff.png -spyderlib/images/filetypes/ts.png -spyderlib/images/filetypes/txt.png -spyderlib/images/filetypes/ui.png -spyderlib/images/filetypes/xls.png -spyderlib/images/filetypes/xml.png -spyderlib/images/filetypes/zip.png -spyderlib/images/projects/add_to_path.png -spyderlib/images/projects/folder.png -spyderlib/images/projects/package.png -spyderlib/images/projects/pp_folder.png -spyderlib/images/projects/pp_package.png -spyderlib/images/projects/pp_project.png -spyderlib/images/projects/project.png -spyderlib/images/projects/project_closed.png -spyderlib/images/projects/pydev.png -spyderlib/images/projects/pythonpath.png -spyderlib/images/projects/remove_from_path.png -spyderlib/images/projects/show_all.png -spyderlib/locale/spyderlib.pot -spyderlib/locale/es/LC_MESSAGES/spyderlib.mo -spyderlib/locale/es/LC_MESSAGES/spyderlib.po -spyderlib/locale/fr/LC_MESSAGES/spyderlib.mo -spyderlib/locale/fr/LC_MESSAGES/spyderlib.po -spyderlib/plugins/__init__.py -spyderlib/plugins/configdialog.py -spyderlib/plugins/console.py -spyderlib/plugins/editor.py -spyderlib/plugins/explorer.py -spyderlib/plugins/externalconsole.py -spyderlib/plugins/findinfiles.py -spyderlib/plugins/history.py -spyderlib/plugins/inspector.py -spyderlib/plugins/ipythonconsole.py -spyderlib/plugins/onlinehelp.py -spyderlib/plugins/outlineexplorer.py -spyderlib/plugins/projectexplorer.py -spyderlib/plugins/runconfig.py -spyderlib/plugins/shortcuts.py -spyderlib/plugins/variableexplorer.py -spyderlib/plugins/workingdirectory.py -spyderlib/qt/QtCore.py -spyderlib/qt/QtGui.py -spyderlib/qt/QtSvg.py -spyderlib/qt/QtWebKit.py -spyderlib/qt/__init__.py -spyderlib/qt/compat.py -spyderlib/utils/__init__.py -spyderlib/utils/bsdsocket.py -spyderlib/utils/codeanalysis.py -spyderlib/utils/debug.py -spyderlib/utils/dochelpers.py -spyderlib/utils/encoding.py -spyderlib/utils/environ.py -spyderlib/utils/iofuncs.py -spyderlib/utils/misc.py -spyderlib/utils/programs.py -spyderlib/utils/qthelpers.py -spyderlib/utils/sourcecode.py -spyderlib/utils/system.py -spyderlib/utils/vcs.py -spyderlib/utils/windows.py -spyderlib/utils/external/__init__.py -spyderlib/utils/external/lockfile.py -spyderlib/utils/external/path.py -spyderlib/utils/external/pickleshare.py -spyderlib/utils/inspector/__init__.py -spyderlib/utils/inspector/conf.py -spyderlib/utils/inspector/sphinxify.py -spyderlib/utils/inspector/tutorial.rst -spyderlib/utils/inspector/js/collapse_sections.js -spyderlib/utils/inspector/js/copy_button.js -spyderlib/utils/inspector/js/fix_image_paths.js -spyderlib/utils/inspector/js/jquery.js -spyderlib/utils/inspector/js/math_config.js -spyderlib/utils/inspector/js/move_outline.js -spyderlib/utils/inspector/js/utils.js -spyderlib/utils/inspector/js/mathjax/MathJax.js -spyderlib/utils/inspector/js/mathjax/config/TeX-AMS-MML_HTMLorMML-full.js -spyderlib/utils/inspector/js/mathjax/config/TeX-AMS-MML_HTMLorMML.js -spyderlib/utils/inspector/js/mathjax/config/TeX-AMS-MML_SVG-full.js -spyderlib/utils/inspector/js/mathjax/config/TeX-AMS-MML_SVG.js -spyderlib/utils/inspector/js/mathjax/config/TeX-MML-AM_HTMLorMML-full.js -spyderlib/utils/inspector/js/mathjax/config/TeX-MML-AM_HTMLorMML.js -spyderlib/utils/inspector/js/mathjax/config/default.js -spyderlib/utils/inspector/js/mathjax/config/local/local.js -spyderlib/utils/inspector/js/mathjax/extensions/FontWarnings.js -spyderlib/utils/inspector/js/mathjax/extensions/MathEvents.js -spyderlib/utils/inspector/js/mathjax/extensions/MathMenu.js -spyderlib/utils/inspector/js/mathjax/extensions/MathZoom.js -spyderlib/utils/inspector/js/mathjax/extensions/asciimath2jax.js -spyderlib/utils/inspector/js/mathjax/extensions/jsMath2jax.js -spyderlib/utils/inspector/js/mathjax/extensions/mml2jax.js -spyderlib/utils/inspector/js/mathjax/extensions/tex2jax.js -spyderlib/utils/inspector/js/mathjax/extensions/toMathML.js -spyderlib/utils/inspector/js/mathjax/extensions/v1.0-warning.js -spyderlib/utils/inspector/js/mathjax/extensions/HTML-CSS/handle-floats.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/AMSmath.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/AMSsymbols.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/HTML.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/action.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/autobold.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/autoload-all.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/bbox.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/begingroup.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/boldsymbol.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/cancel.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/color.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/enclose.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/extpfeil.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/mathchoice.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/mhchem.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/newcommand.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/noErrors.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/noUndefined.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/unicode.js -spyderlib/utils/inspector/js/mathjax/extensions/TeX/verb.js -spyderlib/utils/inspector/js/mathjax/images/CloseX-31.png -spyderlib/utils/inspector/js/mathjax/images/MenuArrow-15.png -spyderlib/utils/inspector/js/mathjax/jax/element/mml/jax.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/Arrows.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/CombDiactForSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/Dingbats.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/GeneralPunctuation.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/GeometricShapes.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/GreekAndCoptic.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/Latin1Supplement.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/LetterlikeSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/MathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/MiscMathSymbolsA.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/MiscMathSymbolsB.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/MiscSymbolsAndArrows.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/MiscTechnical.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/SpacingModLetters.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/SuppMathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/SupplementalArrowsA.js -spyderlib/utils/inspector/js/mathjax/jax/element/mml/optable/SupplementalArrowsB.js -spyderlib/utils/inspector/js/mathjax/jax/input/AsciiMath/config.js -spyderlib/utils/inspector/js/mathjax/jax/input/AsciiMath/jax.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/config.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/jax.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/a.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/b.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/c.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/d.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/e.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/f.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/fr.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/g.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/h.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/i.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/j.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/k.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/l.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/m.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/n.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/o.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/opf.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/p.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/q.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/r.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/s.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/scr.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/t.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/u.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/v.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/w.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/x.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/y.js -spyderlib/utils/inspector/js/mathjax/jax/input/MathML/entities/z.js -spyderlib/utils/inspector/js/mathjax/jax/input/TeX/config.js -spyderlib/utils/inspector/js/mathjax/jax/input/TeX/jax.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/config.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/jax.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/autoload/annotation-xml.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/autoload/maction.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/autoload/menclose.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/autoload/mglyph.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/autoload/mmultiscripts.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/autoload/ms.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/autoload/mtable.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/autoload/multiline.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/fontdata-extra.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/fontdata.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/Arrows.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/BoxDrawing.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/Dingbats.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/EnclosedAlphanum.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/GeneralPunctuation.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/GeometricShapes.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/GreekAndCoptic.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/Latin1Supplement.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/LatinExtendedA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/LetterlikeSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/MathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/MiscMathSymbolsB.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/MiscSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/MiscTechnical.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/PUA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/SpacingModLetters.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/AMS/Regular/SuppMathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Caligraphic/Bold/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Caligraphic/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Fraktur/Bold/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Fraktur/Bold/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Fraktur/Bold/Other.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Fraktur/Bold/PUA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Fraktur/Regular/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Fraktur/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Fraktur/Regular/Other.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Fraktur/Regular/PUA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/Arrows.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/CombDiactForSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/GeneralPunctuation.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/GeometricShapes.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/GreekAndCoptic.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/Latin1Supplement.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/LatinExtendedA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/LatinExtendedB.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/LetterlikeSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/MathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/MiscMathSymbolsA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/MiscSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/MiscTechnical.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/SpacingModLetters.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/SuppMathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Bold/SupplementalArrowsA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/GeneralPunctuation.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/GreekAndCoptic.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/LatinExtendedA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/LatinExtendedB.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/LetterlikeSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Italic/MathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/GeometricShapes.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/GreekAndCoptic.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/LatinExtendedA.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/LatinExtendedB.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/LetterlikeSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/MathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/MiscSymbols.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/SpacingModLetters.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Main/Regular/SuppMathOperators.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Math/BoldItalic/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Math/Italic/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Bold/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Bold/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Bold/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Bold/Other.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Italic/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Italic/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Italic/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Italic/Other.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Regular/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Regular/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/SansSerif/Regular/Other.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Script/Regular/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Script/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Size1/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Size2/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Size3/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Size4/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Typewriter/Regular/BasicLatin.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Typewriter/Regular/CombDiacritMarks.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Typewriter/Regular/Main.js -spyderlib/utils/inspector/js/mathjax/jax/output/SVG/fonts/TeX/Typewriter/Regular/Other.js -spyderlib/utils/inspector/static/css/default.css -spyderlib/utils/inspector/static/css/pygments.css -spyderlib/utils/inspector/static/images/collapse_expand.png -spyderlib/utils/inspector/static/images/debug-continue.png -spyderlib/utils/inspector/static/images/debug-step-in.png -spyderlib/utils/inspector/static/images/debug-step-out.png -spyderlib/utils/inspector/static/images/debug-step-over.png -spyderlib/utils/inspector/static/images/spyder-hello-docstring.png -spyderlib/utils/inspector/static/images/spyder-nice-docstring-rendering.png -spyderlib/utils/inspector/static/images/spyder-sympy-example.png -spyderlib/utils/inspector/templates/layout.html -spyderlib/utils/inspector/templates/usage.html -spyderlib/utils/inspector/templates/warning.html -spyderlib/utils/introspection/__init__.py -spyderlib/utils/introspection/fallback_plugin.py -spyderlib/utils/introspection/jedi_plugin.py -spyderlib/utils/introspection/module_completion.py -spyderlib/utils/introspection/plugin_manager.py -spyderlib/utils/introspection/rope_plugin.py -spyderlib/utils/ipython/templates/blank.html -spyderlib/utils/ipython/templates/kernel_error.html -spyderlib/utils/ipython/templates/loading.html -spyderlib/widgets/__init__.py -spyderlib/widgets/arrayeditor.py -spyderlib/widgets/browser.py -spyderlib/widgets/calltip.py -spyderlib/widgets/colors.py -spyderlib/widgets/comboboxes.py -spyderlib/widgets/dataframeeditor.py -spyderlib/widgets/dependencies.py -spyderlib/widgets/dicteditor.py -spyderlib/widgets/dicteditorutils.py -spyderlib/widgets/editor.py -spyderlib/widgets/editortools.py -spyderlib/widgets/explorer.py -spyderlib/widgets/findinfiles.py -spyderlib/widgets/findreplace.py -spyderlib/widgets/formlayout.py -spyderlib/widgets/importwizard.py -spyderlib/widgets/internalshell.py -spyderlib/widgets/ipython.py -spyderlib/widgets/mixins.py -spyderlib/widgets/objecteditor.py -spyderlib/widgets/onecolumntree.py -spyderlib/widgets/pathmanager.py -spyderlib/widgets/projectexplorer.py -spyderlib/widgets/pydocgui.py -spyderlib/widgets/shell.py -spyderlib/widgets/status.py -spyderlib/widgets/tabs.py -spyderlib/widgets/texteditor.py -spyderlib/widgets/externalshell/__init__.py -spyderlib/widgets/externalshell/baseshell.py -spyderlib/widgets/externalshell/inputhooks.py -spyderlib/widgets/externalshell/introspection.py -spyderlib/widgets/externalshell/monitor.py -spyderlib/widgets/externalshell/namespacebrowser.py -spyderlib/widgets/externalshell/osx_app_site.py -spyderlib/widgets/externalshell/pythonshell.py -spyderlib/widgets/externalshell/sitecustomize.py -spyderlib/widgets/externalshell/start_ipython_kernel.py -spyderlib/widgets/externalshell/systemshell.py -spyderlib/widgets/sourcecode/__init__.py -spyderlib/widgets/sourcecode/base.py -spyderlib/widgets/sourcecode/codeeditor.py -spyderlib/widgets/sourcecode/syntaxhighlighters.py -spyderlib/widgets/sourcecode/terminal.py -spyderplugins/__init__.py -spyderplugins/io_dicom.py -spyderplugins/io_hdf5.py -spyderplugins/p_breakpoints.py -spyderplugins/p_profiler.py -spyderplugins/p_pylint.py -spyderplugins/images/profiler.png -spyderplugins/images/pylint.png -spyderplugins/locale/es/LC_MESSAGES/p_breakpoints.mo -spyderplugins/locale/es/LC_MESSAGES/p_profiler.mo -spyderplugins/locale/es/LC_MESSAGES/p_pylint.mo -spyderplugins/locale/fr/LC_MESSAGES/p_breakpoints.mo -spyderplugins/locale/fr/LC_MESSAGES/p_profiler.mo -spyderplugins/locale/fr/LC_MESSAGES/p_pylint.mo -spyderplugins/widgets/__init__.py -spyderplugins/widgets/breakpointsgui.py -spyderplugins/widgets/profilergui.py -spyderplugins/widgets/pylintgui.py \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyder.egg-info/top_level.txt spyder-3.0.2+dfsg1/spyder.egg-info/top_level.txt --- spyder-2.3.8+dfsg1/spyder.egg-info/top_level.txt 2015-11-27 13:35:50.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder.egg-info/top_level.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -spyderlib -spyderplugins diff -Nru spyder-2.3.8+dfsg1/spyder_io_dcm/dcm.py spyder-3.0.2+dfsg1/spyder_io_dcm/dcm.py --- spyder-2.3.8+dfsg1/spyder_io_dcm/dcm.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_io_dcm/dcm.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,26 @@ +# -*- coding:utf-8 -*- +"""Example of I/O plugin for loading DICOM files""" + +import os.path as osp + +try: + try: + # pydicom 0.9 + import dicom as dicomio + except ImportError: + # pydicom 1.0 + from pydicom import dicomio + def load_dicom(filename): + try: + name = osp.splitext(osp.basename(filename))[0] + try: + data = dicomio.read_file(filename, force=True) + except TypeError: + data = dicomio.read_file(filename) + arr = data.pixel_array + return {name: arr}, None + except Exception as error: + return None, str(error) +except ImportError: + load_dicom = None + diff -Nru spyder-2.3.8+dfsg1/spyder_io_dcm/__init__.py spyder-3.0.2+dfsg1/spyder_io_dcm/__init__.py --- spyder-2.3.8+dfsg1/spyder_io_dcm/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_io_dcm/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,9 @@ +# ============================================================================= +# The following statements are required to register this I/O plugin: +# ============================================================================= +from .dcm import load_dicom + +FORMAT_NAME = "DICOM images" +FORMAT_EXT = ".dcm" +FORMAT_LOAD = load_dicom +FORMAT_SAVE = None \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyder_io_hdf5/hdf5.py spyder-3.0.2+dfsg1/spyder_io_hdf5/hdf5.py --- spyder-2.3.8+dfsg1/spyder_io_hdf5/hdf5.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_io_hdf5/hdf5.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,78 @@ +# -*- coding:utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""I/O plugin for loading/saving HDF5 files + +Note that this is a fairly dumb implementation which reads the whole HDF5 file into +Spyder's variable explorer. Since HDF5 files are designed for storing very large +data-sets, it may be much better to work directly with the HDF5 objects, thus keeping +the data on disk. Nonetheless, this plugin gives quick and dirty but convenient +access to HDF5 files. + +There is no support for creating files with compression, chunking etc, although +these can be read without problem. + +All datatypes to be saved must be convertible to a numpy array, otherwise an exception +will be raised. + +Data attributes are currently ignored. + +When reading an HDF5 file with sub-groups, groups in the HDF5 file will +correspond to dictionaries with the same layout. However, when saving +data, dictionaries are not turned into HDF5 groups. + +TODO: Look for the pytables library if h5py is not found?? +TODO: Check issues with valid python names vs valid h5f5 names +""" + +from __future__ import print_function + +try: + # Do not import h5py here because it will try to import IPython, + # and this is freezing the Spyder GUI + import imp + imp.find_module('h5py') + import numpy as np + + def load_hdf5(filename): + import h5py + def get_group(group): + contents = {} + for name, obj in list(group.items()): + if isinstance(obj, h5py.Dataset): + contents[name] = np.array(obj) + elif isinstance(obj, h5py.Group): + # it is a group, so call self recursively + contents[name] = get_group(obj) + # other objects such as links are ignored + return contents + + try: + f = h5py.File(filename, 'r') + contents = get_group(f) + f.close() + return contents, None + except Exception as error: + return None, str(error) + + def save_hdf5(data, filename): + import h5py + try: + f = h5py.File(filename, 'w') + for key, value in list(data.items()): + f[key] = np.array(value) + f.close() + except Exception as error: + return str(error) +except ImportError: + load_hdf5 = None + save_hdf5 = None + + +if __name__ == "__main__": + data = {'a' : [1, 2, 3, 4], 'b' : 4.5} + print(save_hdf5(data, "test.h5")) + print(load_hdf5("test.h5")) diff -Nru spyder-2.3.8+dfsg1/spyder_io_hdf5/__init__.py spyder-3.0.2+dfsg1/spyder_io_hdf5/__init__.py --- spyder-2.3.8+dfsg1/spyder_io_hdf5/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_io_hdf5/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,9 @@ +# ============================================================================= +# The following statements are required to register this I/O plugin: +# ============================================================================= +from .hdf5 import load_hdf5, save_hdf5 + +FORMAT_NAME = "HDF5" +FORMAT_EXT = ".h5" +FORMAT_LOAD = load_hdf5 +FORMAT_SAVE = save_hdf5 diff -Nru spyder-2.3.8+dfsg1/spyderlib/baseconfig.py spyder-3.0.2+dfsg1/spyderlib/baseconfig.py --- spyder-2.3.8+dfsg1/spyderlib/baseconfig.py 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/baseconfig.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,303 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011-2013 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Spyder base configuration management - -As opposed to spyderlib/config.py, this configuration script deals -exclusively with non-GUI features configuration only -(in other words, we won't import any PyQt object here, avoiding any -sip API incompatibility issue in spyderlib's non-gui modules) -""" - -from __future__ import print_function - -import os.path as osp -import os -import sys - -# Local imports -from spyderlib import __version__ -from spyderlib.utils import encoding -from spyderlib.py3compat import (is_unicode, TEXT_TYPES, INT_TYPES, PY3, - to_text_string, is_text_string) - - -#============================================================================== -# Only for development -#============================================================================== -# To activate/deactivate certain things for development -# SPYDER_DEV is (and *only* has to be) set in bootstrap.py -DEV = os.environ.get('SPYDER_DEV') - -# For testing purposes -# SPYDER_TEST can be set using the --test option of bootstrap.py -TEST = os.environ.get('SPYDER_TEST') - - -#============================================================================== -# Debug helpers -#============================================================================== -STDOUT = sys.stdout -STDERR = sys.stderr -def _get_debug_env(): - debug_env = os.environ.get('SPYDER_DEBUG', '') - if not debug_env.isdigit(): - debug_env = bool(debug_env) - return int(debug_env) -DEBUG = _get_debug_env() - -def debug_print(message): - """Output debug messages to stdout""" - if DEBUG: - ss = STDOUT - print(message, file=ss) - -#============================================================================== -# Configuration paths -#============================================================================== -# Spyder settings dir -if TEST is None: - SUBFOLDER = '.spyder%s' % __version__.split('.')[0] -else: - SUBFOLDER = 'spyder_test' - - -# We can't have PY2 and PY3 settings in the same dir because: -# 1. This leads to ugly crashes and freezes (e.g. by trying to -# embed a PY2 interpreter in PY3) -# 2. We need to save the list of installed modules (for code -# completion) separately for each version -if PY3: - SUBFOLDER = SUBFOLDER + '-py3' - - -def get_home_dir(): - """ - Return user home directory - """ - try: - # expanduser() returns a raw byte string which needs to be - # decoded with the codec that the OS is using to represent file paths. - path = encoding.to_unicode_from_fs(osp.expanduser('~')) - except: - path = '' - for env_var in ('HOME', 'USERPROFILE', 'TMP'): - if osp.isdir(path): - break - # os.environ.get() returns a raw byte string which needs to be - # decoded with the codec that the OS is using to represent environment - # variables. - path = encoding.to_unicode_from_fs(os.environ.get(env_var, '')) - if path: - return path - else: - raise RuntimeError('Please define environment variable $HOME') - - -def get_conf_path(filename=None): - """Return absolute path for configuration file with specified filename""" - if TEST is None: - conf_dir = osp.join(get_home_dir(), SUBFOLDER) - else: - import tempfile - conf_dir = osp.join(tempfile.gettempdir(), SUBFOLDER) - if not osp.isdir(conf_dir): - os.mkdir(conf_dir) - if filename is None: - return conf_dir - else: - return osp.join(conf_dir, filename) - - -def get_module_path(modname): - """Return module *modname* base path""" - return osp.abspath(osp.dirname(sys.modules[modname].__file__)) - - -def get_module_data_path(modname, relpath=None, attr_name='DATAPATH'): - """Return module *modname* data path - Note: relpath is ignored if module has an attribute named *attr_name* - - Handles py2exe/cx_Freeze distributions""" - datapath = getattr(sys.modules[modname], attr_name, '') - if datapath: - return datapath - else: - datapath = get_module_path(modname) - parentdir = osp.join(datapath, osp.pardir) - if osp.isfile(parentdir): - # Parent directory is not a directory but the 'library.zip' file: - # this is either a py2exe or a cx_Freeze distribution - datapath = osp.abspath(osp.join(osp.join(parentdir, osp.pardir), - modname)) - if relpath is not None: - datapath = osp.abspath(osp.join(datapath, relpath)) - return datapath - - -def get_module_source_path(modname, basename=None): - """Return module *modname* source path - If *basename* is specified, return *modname.basename* path where - *modname* is a package containing the module *basename* - - *basename* is a filename (not a module name), so it must include the - file extension: .py or .pyw - - Handles py2exe/cx_Freeze distributions""" - srcpath = get_module_path(modname) - parentdir = osp.join(srcpath, osp.pardir) - if osp.isfile(parentdir): - # Parent directory is not a directory but the 'library.zip' file: - # this is either a py2exe or a cx_Freeze distribution - srcpath = osp.abspath(osp.join(osp.join(parentdir, osp.pardir), - modname)) - if basename is not None: - srcpath = osp.abspath(osp.join(srcpath, basename)) - return srcpath - - -def is_py2exe_or_cx_Freeze(): - """Return True if this is a py2exe/cx_Freeze distribution of Spyder""" - return osp.isfile(osp.join(get_module_path('spyderlib'), osp.pardir)) - - -SCIENTIFIC_STARTUP = get_module_source_path('spyderlib', - 'scientific_startup.py') - - -#============================================================================== -# Image path list -#============================================================================== - -IMG_PATH = [] -def add_image_path(path): - if not osp.isdir(path): - return - global IMG_PATH - IMG_PATH.append(path) - for _root, dirs, _files in os.walk(path): - for dir in dirs: - IMG_PATH.append(osp.join(path, dir)) - -add_image_path(get_module_data_path('spyderlib', relpath='images')) - -from spyderlib.otherplugins import PLUGIN_PATH -if PLUGIN_PATH is not None: - add_image_path(osp.join(PLUGIN_PATH, 'images')) - -def get_image_path(name, default="not_found.png"): - """Return image absolute path""" - for img_path in IMG_PATH: - full_path = osp.join(img_path, name) - if osp.isfile(full_path): - return osp.abspath(full_path) - if default is not None: - return osp.abspath(osp.join(img_path, default)) - - -#============================================================================== -# Translations -#============================================================================== -def get_translation(modname, dirname=None): - """Return translation callback for module *modname*""" - if dirname is None: - dirname = modname - locale_path = get_module_data_path(dirname, relpath="locale", - attr_name='LOCALEPATH') - # fixup environment var LANG in case it's unknown - if "LANG" not in os.environ: - import locale - lang = locale.getdefaultlocale()[0] - if lang is not None: - os.environ["LANG"] = lang - import gettext - try: - _trans = gettext.translation(modname, locale_path, codeset="utf-8") - lgettext = _trans.lgettext - def translate_gettext(x): - if not PY3 and is_unicode(x): - x = x.encode("utf-8") - y = lgettext(x) - if is_text_string(y) and PY3: - return y - else: - return to_text_string(y, "utf-8") - return translate_gettext - except IOError as _e: # analysis:ignore - #print "Not using translations (%s)" % _e - def translate_dumb(x): - if not is_unicode(x): - return to_text_string(x, "utf-8") - return x - return translate_dumb - -# Translation callback -_ = get_translation("spyderlib") - - -#============================================================================== -# Namespace Browser (Variable Explorer) configuration management -#============================================================================== - -def get_supported_types(): - """ - Return a dictionnary containing types lists supported by the - namespace browser: - dict(picklable=picklable_types, editable=editables_types) - - See: - get_remote_data function in spyderlib/widgets/externalshell/monitor.py - get_internal_shell_filter method in namespacebrowser.py - - Note: - If you update this list, don't forget to update doc/variablexplorer.rst - """ - from datetime import date - editable_types = [int, float, complex, list, dict, tuple, date - ] + list(TEXT_TYPES) + list(INT_TYPES) - try: - from numpy import ndarray, matrix, generic - editable_types += [ndarray, matrix, generic] - except ImportError: - pass - try: - from pandas import DataFrame, Series - editable_types += [DataFrame, Series] - except ImportError: - pass - picklable_types = editable_types[:] - try: - from spyderlib.pil_patch import Image - editable_types.append(Image.Image) - except ImportError: - pass - return dict(picklable=picklable_types, editable=editable_types) - -# Variable explorer display / check all elements data types for sequences: -# (when saving the variable explorer contents, check_all is True, -# see widgets/externalshell/namespacebrowser.py:NamespaceBrowser.save_data) -CHECK_ALL = False #XXX: If True, this should take too much to compute... - -EXCLUDED_NAMES = ['nan', 'inf', 'infty', 'little_endian', 'colorbar_doc', - 'typecodes', '__builtins__', '__main__', '__doc__', 'NaN', - 'Inf', 'Infinity', 'sctypes', 'rcParams', 'rcParamsDefault', - 'sctypeNA', 'typeNA', 'False_', 'True_',] - -#============================================================================== -# Mac application utilities -#============================================================================== - -if PY3: - MAC_APP_NAME = 'Spyder.app' -else: - MAC_APP_NAME = 'Spyder-Py2.app' - -def running_in_mac_app(): - if sys.platform == "darwin" and MAC_APP_NAME in __file__: - return True - else: - return False diff -Nru spyder-2.3.8+dfsg1/spyderlib/cli_options.py spyder-3.0.2+dfsg1/spyderlib/cli_options.py --- spyder-2.3.8+dfsg1/spyderlib/cli_options.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/cli_options.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 The Spyder development team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -import optparse - -def get_options(): - """ - Convert options into commands - return commands, message - """ - parser = optparse.OptionParser(usage="spyder [options] files") - parser.add_option('-l', '--light', action='store_true', default=False, - help="Light version (all add-ons are disabled)") - parser.add_option('--new-instance', action='store_true', default=False, - help="Run a new instance of Spyder, even if the single " - "instance mode has been turned on (default)") - parser.add_option('--session', dest="startup_session", default='', - help="Startup session") - parser.add_option('--defaults', dest="reset_to_defaults", - action='store_true', default=False, - help="Reset configuration settings to defaults") - parser.add_option('--reset', dest="reset_session", - action='store_true', default=False, - help="Remove all configuration files!") - parser.add_option('--optimize', action='store_true', default=False, - help="Optimize Spyder bytecode (this may require " - "administrative privileges)") - parser.add_option('-w', '--workdir', dest="working_directory", default=None, - help="Default working directory") - parser.add_option('--show-console', action='store_true', default=False, - help="Do not hide parent console window (Windows)") - parser.add_option('--multithread', dest="multithreaded", - action='store_true', default=False, - help="Internal console is executed in another thread " - "(separate from main application thread)") - parser.add_option('--profile', action='store_true', default=False, - help="Profile mode (internal test, " - "not related with Python profiling)") - options, args = parser.parse_args() - return options, args diff -Nru spyder-2.3.8+dfsg1/spyderlib/config.py spyder-3.0.2+dfsg1/spyderlib/config.py --- spyder-2.3.8+dfsg1/spyderlib/config.py 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/config.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,741 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Spyder configuration options - -Note: Leave this file free of Qt related imports, so that it can be used to -quickly load a user config file -""" - -import os -import sys -import os.path as osp - -# Local import -from spyderlib.userconfig import UserConfig -from spyderlib.baseconfig import (CHECK_ALL, EXCLUDED_NAMES, SUBFOLDER, - get_home_dir, _) -from spyderlib.utils import iofuncs, codeanalysis - - -#============================================================================== -# Extensions supported by Spyder's Editor -#============================================================================== -EDIT_FILETYPES = ( - (_("Python files"), ('.py', '.pyw', '.ipy')), - (_("Cython/Pyrex files"), ('.pyx', '.pxd', '.pxi')), - (_("C files"), ('.c', '.h')), - (_("C++ files"), ('.cc', '.cpp', '.cxx', '.h', '.hh', '.hpp', '.hxx')), - (_("OpenCL files"), ('.cl', )), - (_("Fortran files"), ('.f', '.for', '.f77', '.f90', '.f95', '.f2k')), - (_("IDL files"), ('.pro', )), - (_("MATLAB files"), ('.m', )), - (_("Julia files"), ('.jl',)), - (_("Yaml files"), ('.yaml','.yml',)), - (_("Patch and diff files"), ('.patch', '.diff', '.rej')), - (_("Batch files"), ('.bat', '.cmd')), - (_("Text files"), ('.txt',)), - (_("reStructured Text files"), ('.txt', '.rst')), - (_("gettext files"), ('.po', '.pot')), - (_("NSIS files"), ('.nsi', '.nsh')), - (_("Web page files"), ('.css', '.htm', '.html',)), - (_("XML files"), ('.xml',)), - (_("Javascript files"), ('.js',)), - (_("Json files"), ('.json',)), - (_("IPython notebooks"), ('.ipynb',)), - (_("Enaml files"), ('.enaml',)), - (_("Configuration files"), ('.properties', '.session', '.ini', '.inf', - '.reg', '.cfg', '.desktop')), - ) - -def _create_filter(title, ftypes): - return "%s (*%s)" % (title, " *".join(ftypes)) - -ALL_FILTER = "%s (*)" % _("All files") - -def _get_filters(filetypes): - filters = [] - for title, ftypes in filetypes: - filters.append(_create_filter(title, ftypes)) - filters.append(ALL_FILTER) - return ";;".join(filters) - -def _get_extensions(filetypes): - ftype_list = [] - for _title, ftypes in filetypes: - ftype_list += list(ftypes) - return ftype_list - -def get_filter(filetypes, ext): - """Return filter associated to file extension""" - if not ext: - return ALL_FILTER - for title, ftypes in filetypes: - if ext in ftypes: - return _create_filter(title, ftypes) - else: - return '' - -EDIT_FILTERS = _get_filters(EDIT_FILETYPES) -EDIT_EXT = _get_extensions(EDIT_FILETYPES)+[''] - -# Extensions supported by Spyder's Variable explorer -IMPORT_EXT = list(iofuncs.iofunctions.load_extensions.values()) - -# Extensions that should be visible in Spyder's file/project explorers -SHOW_EXT = ['.png', '.ico', '.svg'] - -# Extensions supported by Spyder (Editor or Variable explorer) -VALID_EXT = EDIT_EXT+IMPORT_EXT - - -# Find in files include/exclude patterns -INCLUDE_PATTERNS = [r'|'.join(['\\'+_ext+r'$' for _ext in EDIT_EXT if _ext])+\ - r'|README|INSTALL', - r'\.pyw?$|\.ipy$|\.txt$|\.rst$', - '.'] -EXCLUDE_PATTERNS = [r'\.pyc$|\.pyo$|\.orig$|\.hg|\.svn|\bbuild\b', - r'\.pyc$|\.pyo$|\.orig$|\.hg|\.svn'] - - -# Name filters for file/project explorers (excluding files without extension) -NAME_FILTERS = ['*' + _ext for _ext in VALID_EXT + SHOW_EXT if _ext]+\ - ['README', 'INSTALL', 'LICENSE', 'CHANGELOG'] - - -# Port used to detect if there is a running instance and to communicate with -# it to open external files -OPEN_FILES_PORT = 21128 - -# Ctrl key -CTRL = "Meta" if sys.platform == 'darwin' else "Ctrl" - - -#============================================================================== -# Fonts -#============================================================================== -def is_ubuntu(): - "Detect if we are running in an Ubuntu-based distribution" - if sys.platform.startswith('linux') and osp.isfile('/etc/lsb-release'): - release_info = open('/etc/lsb-release').read() - if 'Ubuntu' in release_info: - return True - else: - return False - else: - return False - - -def is_gtk_desktop(): - "Detect if we are running in a Gtk-based desktop" - if sys.platform.startswith('linux'): - xdg_desktop = os.environ.get('XDG_CURRENT_DESKTOP', '') - if xdg_desktop: - gtk_desktops = ['Unity', 'GNOME', 'XFCE'] - if any([xdg_desktop.startswith(d) for d in gtk_desktops]): - return True - else: - return False - else: - return False - else: - return False - - -SANS_SERIF = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', - 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', - 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', - 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] - -MONOSPACE = ['Monospace', 'DejaVu Sans Mono', 'Consolas', - 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', - 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] - - -if sys.platform == 'darwin': - MONOSPACE = ['Menlo'] + MONOSPACE - BIG = MEDIUM = SMALL = 12 -elif os.name == 'nt': - BIG = 12 - MEDIUM = 10 - SMALL = 9 -elif is_ubuntu(): - SANS_SERIF = ['Ubuntu'] + SANS_SERIF - MONOSPACE = ['Ubuntu Mono'] + MONOSPACE - BIG = 13 - MEDIUM = SMALL = 11 -else: - BIG = 12 - MEDIUM = SMALL = 9 - - -#============================================================================== -# Defaults -#============================================================================== -DEFAULTS = [ - ('main', - { - 'single_instance': True, - 'open_files_port': OPEN_FILES_PORT, - 'tear_off_menus': False, - 'vertical_dockwidget_titlebars': False, - 'vertical_tabs': False, - 'animated_docks': True, - 'window/size': (1260, 740), - 'window/position': (10, 10), - 'window/is_maximized': True, - 'window/is_fullscreen': False, - 'window/prefs_dialog_size': (745, 411), - 'lightwindow/size': (650, 400), - 'lightwindow/position': (30, 30), - 'lightwindow/is_maximized': False, - 'lightwindow/is_fullscreen': False, - - # The following setting is currently not used but necessary from - # a programmatical point of view (see spyder.py): - # (may become useful in the future if we add a button to change - # settings within the "light mode") - 'lightwindow/prefs_dialog_size': (745, 411), - - 'memory_usage/enable': True, - 'memory_usage/timeout': 2000, - 'cpu_usage/enable': False, - 'cpu_usage/timeout': 2000, - 'use_custom_margin': True, - 'custom_margin': 0, - 'show_internal_console_if_traceback': True - }), - ('quick_layouts', - { - 'place_holder': '', - }), - ('editor_appearance', - { - 'cursor/width': 2, - 'completion/size': (300, 180), - }), - ('shell_appearance', - { - 'cursor/width': 2, - 'completion/size': (300, 180), - }), - ('internal_console', - { - 'max_line_count': 300, - 'working_dir_history': 30, - 'working_dir_adjusttocontents': False, - 'font/family': MONOSPACE, - 'font/size': MEDIUM, - 'font/italic': False, - 'font/bold': False, - 'wrap': True, - 'calltips': True, - 'codecompletion/auto': False, - 'codecompletion/enter_key': True, - 'codecompletion/case_sensitive': True, - 'external_editor/path': 'SciTE', - 'external_editor/gotoline': '-goto:', - 'light_background': True, - }), - ('console', - { - 'max_line_count': 500, - 'font/family': MONOSPACE, - 'font/size': MEDIUM, - 'font/italic': False, - 'font/bold': False, - 'wrap': True, - 'single_tab': True, - 'calltips': True, - 'object_inspector': True, - 'codecompletion/auto': True, - 'codecompletion/enter_key': True, - 'codecompletion/case_sensitive': True, - 'show_elapsed_time': False, - 'show_icontext': False, - 'monitor/enabled': True, - 'qt/api': 'default', - 'pyqt/api_version': 2, - 'pyqt/ignore_sip_setapi_errors': False, - 'matplotlib/backend/enabled': True, - 'matplotlib/backend/value': 'MacOSX' if (sys.platform == 'darwin' \ - and os.environ.get('QT_API') == 'pyside')\ - else 'Qt4Agg', - 'umr/enabled': True, - 'umr/verbose': True, - 'umr/namelist': ['guidata', 'guiqwt'], - 'light_background': True, - 'merge_output_channels': os.name != 'nt', - 'colorize_sys_stderr': os.name != 'nt', - 'pythonstartup/default': True, - 'pythonstartup/custom': False, - 'pythonexecutable/default': True, - 'pythonexecutable/custom': False, - 'ets_backend': 'qt4' - }), - ('ipython_console', - { - 'font/family': MONOSPACE, - 'font/size': MEDIUM, - 'font/italic': False, - 'font/bold': False, - 'show_banner': True, - 'use_gui_completion': True, - 'use_pager': False, - 'show_calltips': True, - 'ask_before_closing': True, - 'object_inspector': True, - 'buffer_size': 500, - 'pylab': True, - 'pylab/autoload': False, - 'pylab/backend': 0, - 'pylab/inline/figure_format': 0, - 'pylab/inline/resolution': 72, - 'pylab/inline/width': 6, - 'pylab/inline/height': 4, - 'startup/run_lines': '', - 'startup/use_run_file': False, - 'startup/run_file': '', - 'greedy_completer': False, - 'autocall': 0, - 'symbolic_math': False, - 'in_prompt': '', - 'out_prompt': '', - 'light_color': True, - 'dark_color': False - }), - ('variable_explorer', - { - 'autorefresh': False, - 'autorefresh/timeout': 2000, - 'check_all': CHECK_ALL, - 'excluded_names': EXCLUDED_NAMES, - 'exclude_private': True, - 'exclude_uppercase': True, - 'exclude_capitalized': False, - 'exclude_unsupported': True, - 'truncate': True, - 'minmax': False, - 'remote_editing': False, - }), - ('editor', - { - 'printer_header/font/family': SANS_SERIF, - 'printer_header/font/size': MEDIUM, - 'printer_header/font/italic': False, - 'printer_header/font/bold': False, - 'font/family': MONOSPACE, - 'font/size': MEDIUM, - 'font/italic': False, - 'font/bold': False, - 'wrap': False, - 'wrapflag': True, - 'code_analysis/pyflakes': True, - 'code_analysis/pep8': False, - 'todo_list': True, - 'realtime_analysis': True, - 'realtime_analysis/timeout': 2500, - 'outline_explorer': True, - 'line_numbers': True, - 'blank_spaces': False, - 'edge_line': True, - 'edge_line_column': 79, - 'toolbox_panel': True, - 'calltips': True, - 'go_to_definition': True, - 'close_parentheses': True, - 'close_quotes': False, - 'add_colons': True, - 'auto_unindent': True, - 'indent_chars': '* *', - 'tab_stop_width': 40, - 'object_inspector': True, - 'codecompletion/auto': True, - 'codecompletion/enter_key': True, - 'codecompletion/case_sensitive': True, - 'check_eol_chars': True, - 'tab_always_indent': False, - 'intelligent_backspace': True, - 'highlight_current_line': True, - 'highlight_current_cell': True, - 'occurence_highlighting': True, - 'occurence_highlighting/timeout': 1500, - 'always_remove_trailing_spaces': False, - 'fullpath_sorting': True, - 'show_tab_bar': True, - 'max_recent_files': 20, - 'save_all_before_run': True, - 'focus_to_editor': True, - 'onsave_analysis': False - }), - ('historylog', - { - 'enable': True, - 'max_entries': 100, - 'font/family': MONOSPACE, - 'font/size': MEDIUM, - 'font/italic': False, - 'font/bold': False, - 'wrap': True, - 'go_to_eof': True, - }), - ('inspector', - { - 'enable': True, - 'max_history_entries': 20, - 'font/family': MONOSPACE, - 'font/size': SMALL, - 'font/italic': False, - 'font/bold': False, - 'rich_text/font/family': SANS_SERIF, - 'rich_text/font/size': BIG, - 'rich_text/font/italic': False, - 'rich_text/font/bold': False, - 'wrap': True, - 'connect/editor': False, - 'connect/python_console': False, - 'connect/ipython_console': False, - 'math': True, - 'automatic_import': True, - }), - ('onlinehelp', - { - 'enable': True, - 'zoom_factor': .8, - 'max_history_entries': 20, - }), - ('outline_explorer', - { - 'enable': True, - 'show_fullpath': False, - 'show_all_files': False, - 'show_comments': True, - }), - ('project_explorer', - { - 'enable': True, - 'name_filters': NAME_FILTERS, - 'show_all': False, - 'show_hscrollbar': True - }), - ('arrayeditor', - { - 'font/family': MONOSPACE, - 'font/size': SMALL, - 'font/italic': False, - 'font/bold': False, - }), - ('texteditor', - { - 'font/family': MONOSPACE, - 'font/size': MEDIUM, - 'font/italic': False, - 'font/bold': False, - }), - ('dicteditor', - { - 'font/family': MONOSPACE, - 'font/size': SMALL, - 'font/italic': False, - 'font/bold': False, - }), - ('explorer', - { - 'enable': True, - 'wrap': True, - 'name_filters': NAME_FILTERS, - 'show_hidden': True, - 'show_all': True, - 'show_icontext': False, - }), - ('find_in_files', - { - 'enable': True, - 'supported_encodings': ["utf-8", "iso-8859-1", "cp1252"], - 'include': INCLUDE_PATTERNS, - 'include_regexp': True, - 'exclude': EXCLUDE_PATTERNS, - 'exclude_regexp': True, - 'search_text_regexp': True, - 'search_text': [''], - 'search_text_samples': [codeanalysis.TASKS_PATTERN], - 'in_python_path': False, - 'more_options': True, - }), - ('workingdir', - { - 'editor/open/browse_scriptdir': True, - 'editor/open/browse_workdir': False, - 'editor/new/browse_scriptdir': False, - 'editor/new/browse_workdir': True, - 'editor/open/auto_set_to_basedir': False, - 'editor/save/auto_set_to_basedir': False, - 'working_dir_adjusttocontents': False, - 'working_dir_history': 20, - 'startup/use_last_directory': True, - }), - ('shortcuts', - { - # ---- Global ---- - # -- In spyder.py - '_/close pane': "Shift+Ctrl+F4", - '_/preferences': "Ctrl+Alt+Shift+P", - '_/maximize pane': "Ctrl+Alt+Shift+M", - '_/fullscreen mode': "F11", - '_/quit': "Ctrl+Q", - '_/switch to/from layout 1': "Shift+Alt+F1", - '_/set layout 1': "Ctrl+Shift+Alt+F1", - '_/switch to/from layout 2': "Shift+Alt+F2", - '_/set layout 2': "Ctrl+Shift+Alt+F2", - '_/switch to/from layout 3': "Shift+Alt+F3", - '_/set layout 3': "Ctrl+Shift+Alt+F3", - # -- In plugins/editor - '_/debug step over': "Ctrl+F10", - '_/debug continue': "Ctrl+F12", - '_/debug step into': "Ctrl+F11", - '_/debug step return': "Ctrl+Shift+F11", - '_/debug exit': "Ctrl+Shift+F12", - # -- In plugins/init - '_/switch to inspector': "Ctrl+Shift+H", - '_/switch to outline_explorer': "Ctrl+Shift+O", - '_/switch to editor': "Ctrl+Shift+E", - '_/switch to historylog': "Ctrl+Shift+L", - '_/switch to onlinehelp': "Ctrl+Shift+D", - '_/switch to project_explorer': "Ctrl+Shift+P", - '_/switch to console': "Ctrl+Shift+C", - '_/switch to ipython_console': "Ctrl+Shift+I", - '_/switch to variable_explorer': "Ctrl+Shift+V", - '_/switch to find_in_files': "Ctrl+Shift+F", - '_/switch to explorer': "Ctrl+Shift+X", - # ---- Editor ---- - # -- In codeeditor - 'editor/code completion': CTRL+'+Space', - 'editor/duplicate line': "Ctrl+Alt+Up" if os.name == 'nt' else \ - "Shift+Alt+Up", - 'editor/copy line': "Ctrl+Alt+Down" if os.name == 'nt' else \ - "Shift+Alt+Down", - 'editor/delete line': 'Ctrl+D', - 'editor/move line up': "Alt+Up", - 'editor/move line down': "Alt+Down", - 'editor/go to definition': "Ctrl+G", - 'editor/toggle comment': "Ctrl+1", - 'editor/blockcomment': "Ctrl+4", - 'editor/unblockcomment': "Ctrl+5", - # -- In widgets/editor - 'editor/inspect current object': 'Ctrl+I', - 'editor/go to line': 'Ctrl+L', - 'editor/file list management': 'Ctrl+E', - 'editor/go to previous file': 'Ctrl+Tab', - 'editor/go to next file': 'Ctrl+Shift+Tab', - # -- In spyder.py - 'editor/find text': "Ctrl+F", - 'editor/find next': "F3", - 'editor/find previous': "Shift+F3", - 'editor/replace text': "Ctrl+H", - # -- In plugins/editor - 'editor/show/hide outline': "Ctrl+Alt+O", - 'editor/show/hide project explorer': "Ctrl+Alt+P", - 'editor/new file': "Ctrl+N", - 'editor/open file': "Ctrl+O", - 'editor/save file': "Ctrl+S", - 'editor/save all': "Ctrl+Shift+S", - 'editor/print': "Ctrl+P", - 'editor/close all': "Ctrl+Shift+W", - 'editor/breakpoint': 'F12', - 'editor/conditional breakpoint': 'Shift+F12', - 'editor/debug with winpdb': "F7", - 'editor/debug': "Ctrl+F5", - 'editor/run': "F5", - 'editor/configure': "F6", - 'editor/re-run last script': "Ctrl+F6", - 'editor/run selection': "F9", - 'editor/last edit location': "Ctrl+Alt+Shift+Left", - 'editor/previous cursor position': "Ctrl+Alt+Left", - 'editor/next cursor position': "Ctrl+Alt+Right", - # -- In p_breakpoints - '_/switch to breakpoints': "Ctrl+Shift+B", - # ---- Console (in widgets/shell) ---- - 'console/inspect current object': "Ctrl+I", - 'console/clear shell': "Ctrl+L", - 'console/clear line': "Shift+Escape", - # ---- Pylint (in p_pylint) ---- - 'pylint/run analysis': "F8", - # ---- Profiler (in p_profiler) ---- - 'profiler/run profiler': "F10" - }), - ('color_schemes', - { - 'names': ['Emacs', 'IDLE', 'Monokai', 'Pydev', 'Scintilla', - 'Spyder', 'Spyder/Dark', 'Zenburn'], - # ---- Emacs ---- - # Name Color Bold Italic - 'emacs/background': "#000000", - 'emacs/currentline': "#2b2b43", - 'emacs/currentcell': "#1c1c2d", - 'emacs/occurence': "#abab67", - 'emacs/ctrlclick': "#0000ff", - 'emacs/sideareas': "#555555", - 'emacs/matched_p': "#009800", - 'emacs/unmatched_p': "#c80000", - 'emacs/normal': ('#ffffff', False, False), - 'emacs/keyword': ('#3c51e8', False, False), - 'emacs/builtin': ('#900090', False, False), - 'emacs/definition': ('#ff8040', True, False), - 'emacs/comment': ('#005100', False, False), - 'emacs/string': ('#00aa00', False, True), - 'emacs/number': ('#800000', False, False), - 'emacs/instance': ('#ffffff', False, True), - # ---- IDLE ---- - # Name Color Bold Italic - 'idle/background': "#ffffff", - 'idle/currentline': "#f2e6f3", - 'idle/currentcell': "#feefff", - 'idle/occurence': "#e8f2fe", - 'idle/ctrlclick': "#0000ff", - 'idle/sideareas': "#efefef", - 'idle/matched_p': "#99ff99", - 'idle/unmatched_p': "#ff9999", - 'idle/normal': ('#000000', False, False), - 'idle/keyword': ('#ff7700', True, False), - 'idle/builtin': ('#900090', False, False), - 'idle/definition': ('#0000ff', False, False), - 'idle/comment': ('#dd0000', False, True), - 'idle/string': ('#00aa00', False, False), - 'idle/number': ('#924900', False, False), - 'idle/instance': ('#777777', True, True), - # ---- Monokai ---- - # Name Color Bold Italic - 'monokai/background': "#2a2b24", - 'monokai/currentline': "#484848", - 'monokai/currentcell': "#3d3d3d", - 'monokai/occurence': "#666666", - 'monokai/ctrlclick': "#0000ff", - 'monokai/sideareas': "#2a2b24", - 'monokai/matched_p': "#688060", - 'monokai/unmatched_p': "#bd6e76", - 'monokai/normal': ("#ddddda", False, False), - 'monokai/keyword': ("#f92672", False, False), - 'monokai/builtin': ("#ae81ff", False, False), - 'monokai/definition': ("#a6e22e", False, False), - 'monokai/comment': ("#75715e", False, True), - 'monokai/string': ("#e6db74", False, False), - 'monokai/number': ("#ae81ff", False, False), - 'monokai/instance': ("#ddddda", False, True), - # ---- Pydev ---- - # Name Color Bold Italic - 'pydev/background': "#ffffff", - 'pydev/currentline': "#e8f2fe", - 'pydev/currentcell': "#eff8fe", - 'pydev/occurence': "#ffff99", - 'pydev/ctrlclick': "#0000ff", - 'pydev/sideareas': "#efefef", - 'pydev/matched_p': "#99ff99", - 'pydev/unmatched_p': "#ff99992", - 'pydev/normal': ('#000000', False, False), - 'pydev/keyword': ('#0000ff', False, False), - 'pydev/builtin': ('#900090', False, False), - 'pydev/definition': ('#000000', True, False), - 'pydev/comment': ('#c0c0c0', False, False), - 'pydev/string': ('#00aa00', False, True), - 'pydev/number': ('#800000', False, False), - 'pydev/instance': ('#000000', False, True), - # ---- Scintilla ---- - # Name Color Bold Italic - 'scintilla/background': "#ffffff", - 'scintilla/currentline': "#e1f0d1", - 'scintilla/currentcell': "#edfcdc", - 'scintilla/occurence': "#ffff99", - 'scintilla/ctrlclick': "#0000ff", - 'scintilla/sideareas': "#efefef", - 'scintilla/matched_p': "#99ff99", - 'scintilla/unmatched_p': "#ff9999", - 'scintilla/normal': ('#000000', False, False), - 'scintilla/keyword': ('#00007f', True, False), - 'scintilla/builtin': ('#000000', False, False), - 'scintilla/definition': ('#007f7f', True, False), - 'scintilla/comment': ('#007f00', False, False), - 'scintilla/string': ('#7f007f', False, False), - 'scintilla/number': ('#007f7f', False, False), - 'scintilla/instance': ('#000000', False, True), - # ---- Spyder ---- - # Name Color Bold Italic - 'spyder/background': "#ffffff", - 'spyder/currentline': "#f7ecf8", - 'spyder/currentcell': "#fdfdde", - 'spyder/occurence': "#ffff99", - 'spyder/ctrlclick': "#0000ff", - 'spyder/sideareas': "#efefef", - 'spyder/matched_p': "#99ff99", - 'spyder/unmatched_p': "#ff9999", - 'spyder/normal': ('#000000', False, False), - 'spyder/keyword': ('#0000ff', False, False), - 'spyder/builtin': ('#900090', False, False), - 'spyder/definition': ('#000000', True, False), - 'spyder/comment': ('#adadad', False, True), - 'spyder/string': ('#00aa00', False, False), - 'spyder/number': ('#800000', False, False), - 'spyder/instance': ('#924900', False, True), - # ---- Spyder/Dark ---- - # Name Color Bold Italic - 'spyder/dark/background': "#131926", - 'spyder/dark/currentline': "#2b2b43", - 'spyder/dark/currentcell': "#31314e", - 'spyder/dark/occurence': "#abab67", - 'spyder/dark/ctrlclick': "#0000ff", - 'spyder/dark/sideareas': "#282828", - 'spyder/dark/matched_p': "#009800", - 'spyder/dark/unmatched_p': "#c80000", - 'spyder/dark/normal': ('#ffffff', False, False), - 'spyder/dark/keyword': ('#558eff', False, False), - 'spyder/dark/builtin': ('#aa00aa', False, False), - 'spyder/dark/definition': ('#ffffff', True, False), - 'spyder/dark/comment': ('#7f7f7f', False, False), - 'spyder/dark/string': ('#11a642', False, True), - 'spyder/dark/number': ('#c80000', False, False), - 'spyder/dark/instance': ('#be5f00', False, True), - # ---- Zenburn ---- - # Name Color Bold Italic - 'zenburn/background': "#3f3f3f", - 'zenburn/currentline': "#333333", - 'zenburn/currentcell': "#2c2c2c", - 'zenburn/occurence': "#7a738f", - 'zenburn/ctrlclick': "#0000ff", - 'zenburn/sideareas': "#3f3f3f", - 'zenburn/matched_p': "#688060", - 'zenburn/unmatched_p': "#bd6e76", - 'zenburn/normal': ('#dcdccc', False, False), - 'zenburn/keyword': ('#dfaf8f', True, False), - 'zenburn/builtin': ('#efef8f', False, False), - 'zenburn/definition': ('#efef8f', False, False), - 'zenburn/comment': ('#7f9f7f', False, True), - 'zenburn/string': ('#cc9393', False, False), - 'zenburn/number': ('#8cd0d3', False, False), - 'zenburn/instance': ('#dcdccc', False, True) - }) - ] - - -#============================================================================== -# Config instance -#============================================================================== -# IMPORTANT NOTES: -# 1. If you want to *change* the default value of a current option, you need to -# do a MINOR update in config version, e.g. from 3.0.0 to 3.1.0 -# 2. If you want to *remove* options that are no longer needed in our codebase, -# you need to do a MAJOR update in version, e.g. from 3.0.0 to 4.0.0 -# 3. You don't need to touch this value if you're just adding a new option -CONF_VERSION = '15.2.0' - -# XXX: Previously we had load=(not DEV) here but DEV was set to *False*. -# Check if it *really* needs to be updated or not -CONF = UserConfig('spyder', defaults=DEFAULTS, load=True, version=CONF_VERSION, - subfolder=SUBFOLDER, backup=True, raw_mode=True) - -# Removing old .spyder.ini location: -old_location = osp.join(get_home_dir(), '.spyder.ini') -if osp.isfile(old_location): - os.remove(old_location) diff -Nru spyder-2.3.8+dfsg1/spyderlib/defaults/defaults-2.4.0.ini spyder-3.0.2+dfsg1/spyderlib/defaults/defaults-2.4.0.ini --- spyder-2.3.8+dfsg1/spyderlib/defaults/defaults-2.4.0.ini 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/defaults/defaults-2.4.0.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,505 +0,0 @@ -[main] -lightwindow/is_fullscreen = False -memory_usage/timeout = 2000 -custom_margin = 0 -vertical_dockwidget_titlebars = False -lightwindow/size = (650, 400) -show_internal_console_if_traceback = True -memory_usage/enable = True -single_instance = True -window/is_maximized = False -cpu_usage/enable = False -lightwindow/is_maximized = False -animated_docks = True -window/is_fullscreen = False -cpu_usage/timeout = 2000 -window/size = (1260, 740) -open_files_port = 21128 -lightwindow/prefs_dialog_size = (745, 411) -window/prefs_dialog_size = (745, 411) -window/position = (10, 10) -lightwindow/position = (30, 30) -tear_off_menus = False -vertical_tabs = False -use_custom_margin = True - -[quick_layouts] -place_holder = - -[editor_appearance] -completion/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -calltips/font/size = 9 -calltips/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -cursor/width = 2 -calltips/font/italic = False -completion/font/size = 9 -completion/size = (300, 180) -completion/font/bold = False -calltips/size = 600 -calltips/font/bold = False -completion/font/italic = False - -[shell_appearance] -completion/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -calltips/font/size = 9 -calltips/font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -cursor/width = 2 -calltips/font/italic = False -completion/font/size = 9 -completion/size = (300, 180) -completion/font/bold = False -calltips/size = 600 -calltips/font/bold = False -completion/font/italic = False - -[internal_console] -working_dir_adjusttocontents = False -external_editor/gotoline = -goto: -font/italic = False -calltips = True -working_dir_history = 30 -external_editor/path = SciTE -max_line_count = 300 -shortcut = None -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -codecompletion/enter_key = True -font/bold = False -font/size = 9 -codecompletion/auto = False -wrap = True -codecompletion/case_sensitive = True -light_background = True -codecompletion/show_single = False - -[console] -pythonexecutable/default = True -colorize_sys_stderr = True -umd/enabled = True -show_icontext = False -calltips = True -matplotlib/backend/value = Qt4Agg -single_tab = True -qt/install_inputhook = True -max_line_count = 10000 -pythonstartup/default = False -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -pyqt/ignore_sip_setapi_errors = False -qt/api = default -pythonexecutable/custom = False -font/size = 9 -codecompletion/auto = True -wrap = True -umd/verbose = True -matplotlib/patch = True -codecompletion/show_single = False -matplotlib/backend/enabled = True -monitor/enabled = True -pythonstartup/custom = True -light_background = True -font/italic = False -codecompletion/enter_key = True -ets_backend = qt4 -merge_output_channels = True -show_elapsed_time = True -pyqt/api_version = 0 -shortcut = Ctrl+Shift+C -open_python_at_startup = True -font/bold = False -umd/namelist = ['guidata', 'guiqwt'] -codecompletion/case_sensitive = True -object_inspector = True - -[ipython_console] -show_calltips = False -pylab = True -symbolic_math = False -pylab/inline/height = 4 -open_ipython_at_startup = False -out_prompt = -autocall = 0 -in_prompt = -shortcut = None -font/bold = False -startup/run_lines = -startup/run_file = -pylab/inline/figure_format = 0 -greedy_completer = False -pylab/inline/resolution = 72 -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -dark_color = False -ask_before_closing = True -pylab/backend = 0 -font/size = 9 -light_color = True -buffer_size = 10000 -show_banner = True -font/italic = False -pylab/inline/width = 6 -use_gui_completion = True -use_pager = True -startup/use_run_file = False -object_inspector = True -pylab/autoload = True - -[variable_explorer] -collvalue = False -truncate = True -exclude_unsupported = True -minmax = False -exclude_uppercase = True -check_all = False -exclude_private = True -autorefresh = True -inplace = False -shortcut = Ctrl+Shift+V -excluded_names = ['nan', 'inf', 'infty', 'little_endian', 'colorbar_doc', 'typecodes', '__builtins__', '__main__', '__doc__', 'NaN', 'Inf', 'Infinity', 'sctypes', 'rcParams', 'rcParamsDefault', 'sctypeNA', 'typeNA', 'False_', 'True_'] -autorefresh/timeout = 2000 -exclude_capitalized = False -remote_editing = False - -[editor] -wrapflag = True -edge_line = True -add_colons = True -always_remove_trailing_spaces = False -auto_unindent = True -max_recent_files = 20 -onsave_analysis = False -wrap = False -indent_chars = * * -outline_explorer = True -show_tab_bar = True -shortcut = Ctrl+Shift+E -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -codecompletion/auto = True -fullpath_sorting = True -font/italic = False -check_eol_chars = True -intelligent_backspace = True -realtime_analysis/timeout = 2500 -todo_list = True -close_quotes = False -occurence_highlighting = True -object_inspector = True -go_to_definition = True -tab_stop_width = 40 -tab_always_indent = False -printer_header/font/bold = False -codecompletion/show_single = False -printer_header/font/italic = False -realtime_analysis = True -font/bold = False -printer_header/font/family = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] -toolbox_panel = True -calltips = True -highlight_current_line = True -font/size = 9 -edge_line_column = 79 -close_parentheses = True -save_all_before_run = True -code_analysis/pyflakes = True -line_numbers = True -codecompletion/enter_key = True -code_analysis/pep8 = False -printer_header/font/size = 9 -codecompletion/case_sensitive = True -occurence_highlighting/timeout = 1500 - -[historylog] -max_entries = 100 -go_to_eof = True -font/bold = False -enable = True -font/size = 9 -font/italic = False -wrap = True -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -shortcut = Ctrl+Shift+H - -[inspector] -max_history_entries = 20 -enable = True -font/italic = False -rich_text/font/italic = False -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -shortcut = Ctrl+Shift+I -automatic_import = True -connect/ipython_console = False -font/bold = False -rich_text/font/size = 12 -font/size = 9 -connect/python_console = False -wrap = True -connect/editor = False -rich_text/font/family = ['Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] -rich_text/font/bold = False -math = True - -[onlinehelp] -max_history_entries = 20 -enable = True -zoom_factor = 0.8 -shortcut = Ctrl+Shift+D - -[outline_explorer] -show_comments = True -show_fullpath = False -enable = True -shortcut = Ctrl+Shift+O -show_all_files = False - -[project_explorer] -show_all = False -name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*.spydata', '*.tif', '*.jpg', '*.npy', '*.gif', '*.csv', '*.png', '*.ico', '*.svg', 'README', 'INSTALL', 'LICENSE', 'CHANGELOG'] -enable = True -shortcut = Ctrl+Shift+P -show_hscrollbar = True - -[arrayeditor] -font/bold = False -font/size = 9 -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -font/italic = False - -[texteditor] -font/bold = False -font/size = 9 -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -font/italic = False - -[dicteditor] -font/bold = False -font/size = 9 -font/family = ['Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -font/italic = False - -[explorer] -enable = True -show_hidden = True -show_icontext = False -wrap = True -name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*.spydata', '*.tif', '*.jpg', '*.npy', '*.gif', '*.csv', '*.png', '*.ico', '*.svg', 'README', 'INSTALL', 'LICENSE', 'CHANGELOG'] -show_all = False -shortcut = None -show_toolbar = True - -[find_in_files] -enable = True -exclude_regexp = True -in_python_path = False -exclude = ['\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn|\\bbuild\\b', '\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn'] -search_text_regexp = True -more_options = True -search_text = [''] -supported_encodings = ['utf-8', 'iso-8859-1', 'cp1252'] -search_text_samples = ['(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP)([^#]*)'] -shortcut = None -include_regexp = True -include = ['\\.py$|\\.pyw$|\\.ipy$|\\.pyx$|\\.pxd$|\\.pxi$|\\.c$|\\.h$|\\.cc$|\\.cpp$|\\.cxx$|\\.h$|\\.hh$|\\.hpp$|\\.hxx$|\\.cl$|\\.f$|\\.for$|\\.f77$|\\.f90$|\\.f95$|\\.f2k$|\\.pro$|\\.m$|\\.patch$|\\.diff$|\\.rej$|\\.bat$|\\.cmd$|\\.txt$|\\.txt$|\\.rst$|\\.po$|\\.pot$|\\.nsi$|\\.nsh$|\\.css$|\\.htm$|\\.html$|\\.xml$|\\.js$|\\.enaml$|\\.properties$|\\.session$|\\.ini$|\\.inf$|\\.reg$|\\.cfg$|\\.desktop$|README|INSTALL', '\\.pyw?$|\\.ipy$|\\.txt$|\\.rst$', '.'] - -[workingdir] -working_dir_adjusttocontents = False -editor/new/browse_scriptdir = False -editor/open/auto_set_to_basedir = False -working_dir_history = 20 -editor/open/browse_scriptdir = True -editor/new/browse_workdir = True -editor/open/browse_workdir = False -startup/use_last_directory = True -editor/save/auto_set_to_basedir = False - -[shortcuts] -editor/duplicate line = Shift+Alt+Up -editor/go to next file = Ctrl+Shift+Tab -console/clear line = Shift+Escape -_/switch to outline_explorer = Ctrl+Shift+O -editor/show/hide outline = Ctrl+Alt+O -_/fullscreen mode = F11 -_/maximize plugin = Ctrl+Alt+Shift+M -_/maximize dockwidget = Ctrl+Alt+Shift+M -_/close plugin = Shift+Ctrl+F4 -_/close dockwidget = Shift+Ctrl+F4 -_/switch to inspector = Ctrl+Shift+I -profiler/run profiler = F10 -editor/move line down = Alt+Down -console/clear shell = Ctrl+L -pylint/run analysis = F8 -_/switch to onlinehelp = Ctrl+Shift+D -_/switch to editor = Ctrl+Shift+E -editor/code completion = Ctrl+Space -_/switch to variable_explorer = Ctrl+Shift+V -_/switch to/from layout 3 = Shift+Alt+F3 -_/preferences = Ctrl+Alt+Shift+P -_/switch to/from layout 1 = Shift+Alt+F1 -editor/run selection = F9 -_/debug step into = Ctrl+F11 -editor/toggle comment = Ctrl+1 -editor/go to definition = Ctrl+G -editor/show/hide project explorer = Ctrl+Alt+P -_/debug step return = Ctrl+Shift+F11 -editor/new file = Ctrl+N -_/debug step over = Ctrl+F10 -editor/save all = Ctrl+Shift+S -editor/unblockcomment = Ctrl+5 -_/debug exit = Ctrl+Shift+F12 -editor/go to previous file = Ctrl+Tab -editor/next cursor position = Ctrl+Alt+Right -editor/debug = Ctrl+F5 -editor/copy line = Shift+Alt+Down -editor/file list management = Ctrl+E -editor/debug with winpdb = F7 -_/quit = Ctrl+Q -editor/find next = F3 -editor/move line up = Alt+Up -console/inspect current object = Ctrl+I -editor/find previous = Shift+F3 -_/set layout 2 = Ctrl+Shift+Alt+F2 -_/set layout 3 = Ctrl+Shift+Alt+F3 -_/set layout 1 = Ctrl+Shift+Alt+F1 -_/switch to console = Ctrl+Shift+C -editor/re-run last script = Ctrl+F6 -editor/previous cursor position = Ctrl+Alt+Left -_/switch to project_explorer = Ctrl+Shift+P -editor/open file = Ctrl+O -editor/inspect current object = Ctrl+I -editor/last edit location = Ctrl+Alt+Shift+Left -editor/print = Ctrl+P -editor/configure = F6 -editor/breakpoint = F12 -editor/find text = Ctrl+F -editor/list breakpoints = Ctrl+B -editor/run = F5 -editor/close all = Ctrl+Shift+W -_/debug continue = Ctrl+F12 -editor/blockcomment = Ctrl+4 -editor/close file = Ctrl+W -editor/conditional breakpoint = Shift+F12 -_/switch to/from layout 2 = Shift+Alt+F2 -editor/replace text = Ctrl+H -editor/save file = Ctrl+S -editor/go to line = Ctrl+L -_/switch to historylog = Ctrl+Shift+H -editor/delete line = Ctrl+D - -[color_schemes] -names = ['Emacs', 'IDLE', 'Monokai', 'Pydev', 'Scintilla', 'Spyder', 'Spyder/Dark', 'Zenburn'] -monokai/background = #2a2b24 -monokai/currentline = #484848 -monokai/occurence = #666666 -monokai/ctrlclick = #0000ff -monokai/sideareas = #2a2b24 -monokai/matched_p = #688060 -monokai/unmatched_p = #bd6e76 -monokai/normal = ('#ddddda', False, False) -monokai/keyword = ('#f92672', False, False) -monokai/builtin = ('#ae81ff', False, False) -monokai/definition = ('#a6e22e', False, False) -monokai/comment = ('#75715e', False, True) -monokai/string = ('#e6db74', False, False) -monokai/number = ('#ae81ff', False, False) -monokai/instance = ('#ddddda', False, True) -idle/background = #ffffff -idle/currentline = #eeffdd -idle/occurence = #e8f2fe -idle/ctrlclick = #0000ff -idle/sideareas = #efefef -idle/matched_p = #99ff99 -idle/unmatched_p = #ff9999 -idle/normal = ('#000000', False, False) -idle/keyword = ('#ff7700', True, False) -idle/builtin = ('#900090', False, False) -idle/definition = ('#0000ff', False, False) -idle/comment = ('#dd0000', False, True) -idle/string = ('#00aa00', False, False) -idle/number = ('#924900', False, False) -idle/instance = ('#777777', True, True) -emacs/background = #000000 -emacs/currentline = #2b2b43 -emacs/occurence = #abab67 -emacs/ctrlclick = #0000ff -emacs/sideareas = #555555 -emacs/matched_p = #009800 -emacs/unmatched_p = #c80000 -emacs/normal = ('#ffffff', False, False) -emacs/keyword = ('#3c51e8', False, False) -emacs/builtin = ('#900090', False, False) -emacs/definition = ('#ff8040', True, False) -emacs/comment = ('#005100', False, False) -emacs/string = ('#00aa00', False, True) -emacs/number = ('#800000', False, False) -emacs/instance = ('#ffffff', False, True) -zenburn/background = #3f3f3f -zenburn/currentline = #333333 -zenburn/occurence = #7a738f -zenburn/ctrlclick = #0000ff -zenburn/sideareas = #3f3f3f -zenburn/matched_p = #688060 -zenburn/unmatched_p = #bd6e76 -zenburn/normal = ('#dcdccc', False, False) -zenburn/keyword = ('#dfaf8f', True, False) -zenburn/builtin = ('#efef8f', False, False) -zenburn/definition = ('#efef8f', False, False) -zenburn/comment = ('#7f9f7f', False, True) -zenburn/string = ('#cc9393', False, False) -zenburn/number = ('#8cd0d3', False, False) -zenburn/instance = ('#dcdccc', False, True) -spyder/dark/background = #131926 -spyder/dark/currentline = #2b2b43 -spyder/dark/occurence = #abab67 -spyder/dark/ctrlclick = #0000ff -spyder/dark/sideareas = #282828 -spyder/dark/matched_p = #009800 -spyder/dark/unmatched_p = #c80000 -spyder/dark/normal = ('#ffffff', False, False) -spyder/dark/keyword = ('#558eff', False, False) -spyder/dark/builtin = ('#aa00aa', False, False) -spyder/dark/definition = ('#ffffff', True, False) -spyder/dark/comment = ('#7f7f7f', False, False) -spyder/dark/string = ('#11a642', False, True) -spyder/dark/number = ('#c80000', False, False) -spyder/dark/instance = ('#be5f00', False, True) -scintilla/background = #ffffff -scintilla/currentline = #eeffdd -scintilla/occurence = #ffff99 -scintilla/ctrlclick = #0000ff -scintilla/sideareas = #efefef -scintilla/matched_p = #99ff99 -scintilla/unmatched_p = #ff9999 -scintilla/normal = ('#000000', False, False) -scintilla/keyword = ('#00007f', True, False) -scintilla/builtin = ('#000000', False, False) -scintilla/definition = ('#007f7f', True, False) -scintilla/comment = ('#007f00', False, False) -scintilla/string = ('#7f007f', False, False) -scintilla/number = ('#007f7f', False, False) -scintilla/instance = ('#000000', False, True) -pydev/background = #ffffff -pydev/currentline = #e8f2fe -pydev/occurence = #ffff99 -pydev/ctrlclick = #0000ff -pydev/sideareas = #efefef -pydev/matched_p = #99ff99 -pydev/unmatched_p = #ff9999 -pydev/normal = ('#000000', False, False) -pydev/keyword = ('#0000ff', False, False) -pydev/builtin = ('#900090', False, False) -pydev/definition = ('#000000', True, False) -pydev/comment = ('#c0c0c0', False, False) -pydev/string = ('#00aa00', False, True) -pydev/number = ('#800000', False, False) -pydev/instance = ('#000000', False, True) -spyder/background = #ffffff -spyder/currentline = #feefff -spyder/occurence = #ffff99 -spyder/ctrlclick = #0000ff -spyder/sideareas = #efefef -spyder/matched_p = #99ff99 -spyder/unmatched_p = #ff9999 -spyder/normal = ('#000000', False, False) -spyder/keyword = ('#0000ff', False, False) -spyder/builtin = ('#900090', False, False) -spyder/definition = ('#000000', True, False) -spyder/comment = ('#adadad', False, True) -spyder/string = ('#00aa00', False, False) -spyder/number = ('#800000', False, False) -spyder/instance = ('#924900', False, True) diff -Nru spyder-2.3.8+dfsg1/spyderlib/defaults/defaults-3.0.0.ini spyder-3.0.2+dfsg1/spyderlib/defaults/defaults-3.0.0.ini --- spyder-2.3.8+dfsg1/spyderlib/defaults/defaults-3.0.0.ini 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/defaults/defaults-3.0.0.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,487 +0,0 @@ -[main] -lightwindow/is_fullscreen = False -memory_usage/timeout = 2000 -custom_margin = 0 -vertical_dockwidget_titlebars = False -lightwindow/size = (650, 400) -show_internal_console_if_traceback = True -memory_usage/enable = True -single_instance = True -window/is_maximized = True -cpu_usage/enable = False -lightwindow/is_maximized = False -animated_docks = True -window/is_fullscreen = False -cpu_usage/timeout = 2000 -window/size = (1260, 740) -open_files_port = 21128 -lightwindow/prefs_dialog_size = (745, 411) -window/prefs_dialog_size = (745, 411) -window/position = (10, 10) -lightwindow/position = (30, 30) -tear_off_menus = False -vertical_tabs = False -use_custom_margin = True - -[quick_layouts] -place_holder = - -[editor_appearance] -completion/size = (300, 180) -cursor/width = 2 - -[shell_appearance] -completion/size = (300, 180) -cursor/width = 2 - -[internal_console] -working_dir_adjusttocontents = False -external_editor/gotoline = -goto: -font/italic = False -calltips = True -working_dir_history = 30 -external_editor/path = SciTE -max_line_count = 300 -shortcut = None -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -codecompletion/enter_key = True -font/bold = False -font/size = 10 -codecompletion/auto = False -wrap = True -codecompletion/case_sensitive = True -light_background = True -codecompletion/show_single = False - -[console] -pythonexecutable/default = True -colorize_sys_stderr = True -umd/enabled = True -show_icontext = False -calltips = True -matplotlib/backend/value = Qt4Agg -single_tab = True -qt/install_inputhook = True -max_line_count = 500 -pythonstartup/default = True -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -pyqt/ignore_sip_setapi_errors = False -qt/api = default -pythonexecutable/custom = False -font/size = 10 -codecompletion/auto = True -wrap = True -umd/verbose = True -matplotlib/patch = True -codecompletion/show_single = False -matplotlib/backend/enabled = True -monitor/enabled = True -pythonstartup/custom = False -light_background = True -font/italic = False -codecompletion/enter_key = True -ets_backend = qt4 -merge_output_channels = True -show_elapsed_time = False -pyqt/api_version = 0 -shortcut = Ctrl+Shift+C -open_python_at_startup = True -font/bold = False -umd/namelist = ['guidata', 'guiqwt'] -codecompletion/case_sensitive = True -object_inspector = True - -[ipython_console] -show_calltips = True -pylab = True -symbolic_math = False -pylab/inline/height = 4 -open_ipython_at_startup = True -out_prompt = -autocall = 0 -in_prompt = -shortcut = None -font/bold = False -startup/run_lines = -startup/run_file = -pylab/inline/figure_format = 0 -greedy_completer = False -pylab/inline/resolution = 72 -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -dark_color = False -ask_before_closing = True -pylab/backend = 0 -font/size = 10 -light_color = True -buffer_size = 500 -show_banner = True -font/italic = False -pylab/inline/width = 6 -use_gui_completion = True -use_pager = True -startup/use_run_file = False -object_inspector = True -pylab/autoload = False - -[variable_explorer] -collvalue = False -truncate = True -exclude_unsupported = True -minmax = False -exclude_uppercase = True -check_all = False -exclude_private = True -autorefresh = True -inplace = False -shortcut = Ctrl+Shift+V -excluded_names = ['nan', 'inf', 'infty', 'little_endian', 'colorbar_doc', 'typecodes', '__builtins__', '__main__', '__doc__', 'NaN', 'Inf', 'Infinity', 'sctypes', 'rcParams', 'rcParamsDefault', 'sctypeNA', 'typeNA', 'False_', 'True_'] -autorefresh/timeout = 2000 -exclude_capitalized = False -remote_editing = False - -[editor] -wrapflag = True -edge_line = True -add_colons = True -always_remove_trailing_spaces = False -auto_unindent = True -max_recent_files = 20 -onsave_analysis = False -wrap = False -indent_chars = * * -outline_explorer = True -show_tab_bar = True -shortcut = Ctrl+Shift+E -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -codecompletion/auto = True -fullpath_sorting = True -font/italic = False -check_eol_chars = True -intelligent_backspace = True -realtime_analysis/timeout = 2500 -todo_list = True -close_quotes = False -occurence_highlighting = True -object_inspector = True -go_to_definition = True -tab_stop_width = 40 -tab_always_indent = False -printer_header/font/bold = False -codecompletion/show_single = False -printer_header/font/italic = False -realtime_analysis = True -font/bold = False -printer_header/font/family = ['Ubuntu', 'Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] -toolbox_panel = True -calltips = True -highlight_current_line = True -font/size = 10 -edge_line_column = 79 -close_parentheses = True -save_all_before_run = True -code_analysis/pyflakes = True -line_numbers = True -codecompletion/enter_key = True -code_analysis/pep8 = False -printer_header/font/size = 10 -codecompletion/case_sensitive = True -occurence_highlighting/timeout = 1500 - -[historylog] -max_entries = 100 -go_to_eof = True -font/bold = False -enable = True -font/size = 10 -font/italic = False -wrap = True -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -shortcut = Ctrl+Shift+H - -[inspector] -max_history_entries = 20 -enable = True -font/italic = False -rich_text/font/italic = False -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -shortcut = Ctrl+Shift+I -automatic_import = True -connect/ipython_console = False -font/bold = False -rich_text/font/size = 13 -font/size = 10 -connect/python_console = False -wrap = True -connect/editor = False -rich_text/font/family = ['Ubuntu', 'Sans Serif', 'DejaVu Sans', 'Bitstream Vera Sans', 'Bitstream Charter', 'Lucida Grande', 'MS Shell Dlg 2', 'Calibri', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'Times', 'sans-serif'] -rich_text/font/bold = False -math = True - -[onlinehelp] -max_history_entries = 20 -enable = True -zoom_factor = 0.8 -shortcut = Ctrl+Shift+D - -[outline_explorer] -show_comments = True -show_fullpath = False -enable = True -shortcut = Ctrl+Shift+O -show_all_files = False - -[project_explorer] -show_all = False -name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.jl', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*.spydata', '*.tif', '*.jpg', '*.npy', '*.gif', '*.csv', '*.png', '*.ico', '*.svg', 'README', 'INSTALL', 'LICENSE', 'CHANGELOG'] -enable = True -shortcut = Ctrl+Shift+P -show_hscrollbar = True - -[arrayeditor] -font/bold = False -font/size = 10 -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -font/italic = False - -[texteditor] -font/bold = False -font/size = 10 -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -font/italic = False - -[dicteditor] -font/bold = False -font/size = 10 -font/family = ['Ubuntu Mono', 'Monospace', 'DejaVu Sans Mono', 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono', 'Andale Mono', 'Liberation Mono', 'Courier New', 'Courier', 'monospace', 'Fixed', 'Terminal'] -font/italic = False - -[explorer] -enable = True -show_hidden = True -show_icontext = False -wrap = True -name_filters = ['*.py', '*.pyw', '*.ipy', '*.pyx', '*.pxd', '*.pxi', '*.c', '*.h', '*.cc', '*.cpp', '*.cxx', '*.h', '*.hh', '*.hpp', '*.hxx', '*.cl', '*.f', '*.for', '*.f77', '*.f90', '*.f95', '*.f2k', '*.pro', '*.m', '*.jl', '*.patch', '*.diff', '*.rej', '*.bat', '*.cmd', '*.txt', '*.txt', '*.rst', '*.po', '*.pot', '*.nsi', '*.nsh', '*.css', '*.htm', '*.html', '*.xml', '*.js', '*.enaml', '*.properties', '*.session', '*.ini', '*.inf', '*.reg', '*.cfg', '*.desktop', '*.txt', '*.png', '*.mat', '*.spydata', '*.tif', '*.jpg', '*.npy', '*.gif', '*.csv', '*.png', '*.ico', '*.svg', 'README', 'INSTALL', 'LICENSE', 'CHANGELOG'] -show_all = False -shortcut = None -show_toolbar = True - -[find_in_files] -enable = True -exclude_regexp = True -in_python_path = False -exclude = ['\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn|\\bbuild\\b', '\\.pyc$|\\.pyo$|\\.orig$|\\.hg|\\.svn'] -search_text_regexp = True -more_options = True -search_text = [''] -supported_encodings = ['utf-8', 'iso-8859-1', 'cp1252'] -search_text_samples = ['(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP|@todo)([^#]*)'] -shortcut = None -include_regexp = True -include = ['\\.py$|\\.pyw$|\\.ipy$|\\.pyx$|\\.pxd$|\\.pxi$|\\.c$|\\.h$|\\.cc$|\\.cpp$|\\.cxx$|\\.h$|\\.hh$|\\.hpp$|\\.hxx$|\\.cl$|\\.f$|\\.for$|\\.f77$|\\.f90$|\\.f95$|\\.f2k$|\\.pro$|\\.m$|\\.jl$|\\.patch$|\\.diff$|\\.rej$|\\.bat$|\\.cmd$|\\.txt$|\\.txt$|\\.rst$|\\.po$|\\.pot$|\\.nsi$|\\.nsh$|\\.css$|\\.htm$|\\.html$|\\.xml$|\\.js$|\\.enaml$|\\.properties$|\\.session$|\\.ini$|\\.inf$|\\.reg$|\\.cfg$|\\.desktop$|README|INSTALL', '\\.pyw?$|\\.ipy$|\\.txt$|\\.rst$', '.'] - -[workingdir] -working_dir_adjusttocontents = False -editor/new/browse_scriptdir = False -editor/open/auto_set_to_basedir = False -working_dir_history = 20 -editor/open/browse_scriptdir = True -editor/new/browse_workdir = True -editor/open/browse_workdir = False -startup/use_last_directory = True -editor/save/auto_set_to_basedir = False - -[shortcuts] -editor/duplicate line = Shift+Alt+Up -editor/go to next file = Ctrl+Shift+Tab -console/clear line = Shift+Escape -_/switch to outline_explorer = Ctrl+Shift+O -editor/show/hide outline = Ctrl+Alt+O -editor/blockcomment = Ctrl+4 -_/maximize plugin = Ctrl+Alt+Shift+M -_/maximize dockwidget = Ctrl+Alt+Shift+M -_/close plugin = Shift+Ctrl+F4 -_/close dockwidget = Shift+Ctrl+F4 -_/switch to inspector = Ctrl+Shift+I -profiler/run profiler = F10 -editor/move line down = Alt+Down -console/clear shell = Ctrl+L -pylint/run analysis = F8 -_/switch to onlinehelp = Ctrl+Shift+D -_/switch to editor = Ctrl+Shift+E -editor/code completion = Ctrl+Space -_/switch to variable_explorer = Ctrl+Shift+V -_/switch to/from layout 3 = Shift+Alt+F3 -_/preferences = Ctrl+Alt+Shift+P -_/switch to/from layout 1 = Shift+Alt+F1 -editor/run selection = F9 -_/debug step into = Ctrl+F11 -_/fullscreen mode = F11 -editor/toggle comment = Ctrl+1 -editor/go to definition = Ctrl+G -editor/show/hide project explorer = Ctrl+Alt+P -_/debug step return = Ctrl+Shift+F11 -editor/new file = Ctrl+N -_/debug step over = Ctrl+F10 -editor/save all = Ctrl+Shift+S -editor/unblockcomment = Ctrl+5 -_/debug exit = Ctrl+Shift+F12 -editor/go to previous file = Ctrl+Tab -editor/next cursor position = Ctrl+Alt+Right -editor/debug = Ctrl+F5 -editor/copy line = Shift+Alt+Down -editor/file list management = Ctrl+E -editor/debug with winpdb = F7 -_/quit = Ctrl+Q -editor/find next = F3 -editor/move line up = Alt+Up -console/inspect current object = Ctrl+I -editor/find previous = Shift+F3 -_/set layout 2 = Ctrl+Shift+Alt+F2 -_/set layout 3 = Ctrl+Shift+Alt+F3 -_/set layout 1 = Ctrl+Shift+Alt+F1 -_/switch to console = Ctrl+Shift+C -editor/re-run last script = Ctrl+F6 -editor/previous cursor position = Ctrl+Alt+Left -_/switch to project_explorer = Ctrl+Shift+P -editor/open file = Ctrl+O -editor/inspect current object = Ctrl+I -editor/last edit location = Ctrl+Alt+Shift+Left -editor/print = Ctrl+P -editor/breakpoint = F12 -editor/find text = Ctrl+F -editor/list breakpoints = Ctrl+B -editor/run = F5 -editor/close all = Ctrl+Shift+W -_/debug continue = Ctrl+F12 -editor/configure = F6 -editor/close file = Ctrl+W -editor/conditional breakpoint = Shift+F12 -_/switch to/from layout 2 = Shift+Alt+F2 -editor/replace text = Ctrl+H -editor/save file = Ctrl+S -editor/go to line = Ctrl+L -_/switch to historylog = Ctrl+Shift+H -editor/delete line = Ctrl+D - -[color_schemes] -names = ['Emacs', 'IDLE', 'Monokai', 'Pydev', 'Scintilla', 'Spyder', 'Spyder/Dark', 'Zenburn'] -monokai/background = #2a2b24 -monokai/currentline = #484848 -monokai/occurence = #666666 -monokai/ctrlclick = #0000ff -monokai/sideareas = #2a2b24 -monokai/matched_p = #688060 -monokai/unmatched_p = #bd6e76 -monokai/normal = ('#ddddda', False, False) -monokai/keyword = ('#f92672', False, False) -monokai/builtin = ('#ae81ff', False, False) -monokai/definition = ('#a6e22e', False, False) -monokai/comment = ('#75715e', False, True) -monokai/string = ('#e6db74', False, False) -monokai/number = ('#ae81ff', False, False) -monokai/instance = ('#ddddda', False, True) -idle/background = #ffffff -idle/currentline = #eeffdd -idle/occurence = #e8f2fe -idle/ctrlclick = #0000ff -idle/sideareas = #efefef -idle/matched_p = #99ff99 -idle/unmatched_p = #ff9999 -idle/normal = ('#000000', False, False) -idle/keyword = ('#ff7700', True, False) -idle/builtin = ('#900090', False, False) -idle/definition = ('#0000ff', False, False) -idle/comment = ('#dd0000', False, True) -idle/string = ('#00aa00', False, False) -idle/number = ('#924900', False, False) -idle/instance = ('#777777', True, True) -emacs/background = #000000 -emacs/currentline = #2b2b43 -emacs/occurence = #abab67 -emacs/ctrlclick = #0000ff -emacs/sideareas = #555555 -emacs/matched_p = #009800 -emacs/unmatched_p = #c80000 -emacs/normal = ('#ffffff', False, False) -emacs/keyword = ('#3c51e8', False, False) -emacs/builtin = ('#900090', False, False) -emacs/definition = ('#ff8040', True, False) -emacs/comment = ('#005100', False, False) -emacs/string = ('#00aa00', False, True) -emacs/number = ('#800000', False, False) -emacs/instance = ('#ffffff', False, True) -zenburn/background = #3f3f3f -zenburn/currentline = #333333 -zenburn/occurence = #7a738f -zenburn/ctrlclick = #0000ff -zenburn/sideareas = #3f3f3f -zenburn/matched_p = #688060 -zenburn/unmatched_p = #bd6e76 -zenburn/normal = ('#dcdccc', False, False) -zenburn/keyword = ('#dfaf8f', True, False) -zenburn/builtin = ('#efef8f', False, False) -zenburn/definition = ('#efef8f', False, False) -zenburn/comment = ('#7f9f7f', False, True) -zenburn/string = ('#cc9393', False, False) -zenburn/number = ('#8cd0d3', False, False) -zenburn/instance = ('#dcdccc', False, True) -spyder/dark/background = #131926 -spyder/dark/currentline = #2b2b43 -spyder/dark/occurence = #abab67 -spyder/dark/ctrlclick = #0000ff -spyder/dark/sideareas = #282828 -spyder/dark/matched_p = #009800 -spyder/dark/unmatched_p = #c80000 -spyder/dark/normal = ('#ffffff', False, False) -spyder/dark/keyword = ('#558eff', False, False) -spyder/dark/builtin = ('#aa00aa', False, False) -spyder/dark/definition = ('#ffffff', True, False) -spyder/dark/comment = ('#7f7f7f', False, False) -spyder/dark/string = ('#11a642', False, True) -spyder/dark/number = ('#c80000', False, False) -spyder/dark/instance = ('#be5f00', False, True) -scintilla/background = #ffffff -scintilla/currentline = #eeffdd -scintilla/occurence = #ffff99 -scintilla/ctrlclick = #0000ff -scintilla/sideareas = #efefef -scintilla/matched_p = #99ff99 -scintilla/unmatched_p = #ff9999 -scintilla/normal = ('#000000', False, False) -scintilla/keyword = ('#00007f', True, False) -scintilla/builtin = ('#000000', False, False) -scintilla/definition = ('#007f7f', True, False) -scintilla/comment = ('#007f00', False, False) -scintilla/string = ('#7f007f', False, False) -scintilla/number = ('#007f7f', False, False) -scintilla/instance = ('#000000', False, True) -pydev/background = #ffffff -pydev/currentline = #e8f2fe -pydev/occurence = #ffff99 -pydev/ctrlclick = #0000ff -pydev/sideareas = #efefef -pydev/matched_p = #99ff99 -pydev/unmatched_p = #ff9999 -pydev/normal = ('#000000', False, False) -pydev/keyword = ('#0000ff', False, False) -pydev/builtin = ('#900090', False, False) -pydev/definition = ('#000000', True, False) -pydev/comment = ('#c0c0c0', False, False) -pydev/string = ('#00aa00', False, True) -pydev/number = ('#800000', False, False) -pydev/instance = ('#000000', False, True) -spyder/background = #ffffff -spyder/currentline = #feefff -spyder/occurence = #ffff99 -spyder/ctrlclick = #0000ff -spyder/sideareas = #efefef -spyder/matched_p = #99ff99 -spyder/unmatched_p = #ff9999 -spyder/normal = ('#000000', False, False) -spyder/keyword = ('#0000ff', False, False) -spyder/builtin = ('#900090', False, False) -spyder/definition = ('#000000', True, False) -spyder/comment = ('#adadad', False, True) -spyder/string = ('#00aa00', False, False) -spyder/number = ('#800000', False, False) -spyder/instance = ('#924900', False, True) diff -Nru spyder-2.3.8+dfsg1/spyderlib/defaults/Readme.txt spyder-3.0.2+dfsg1/spyderlib/defaults/Readme.txt --- spyder-2.3.8+dfsg1/spyderlib/defaults/Readme.txt 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/defaults/Readme.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -Copyright (c) 2013 The Spyder Development Team -Licensed under the terms of the MIT License -(see spyderlib/__init__.py for details) - -What is the purpose of this directory? -====================================== - -The files present here (licensed also MIT) are used to cleanly update user -configuration options from Spyder versions previous to 2.3. They way they did -an update was by resetting *all* config options to new defaults, which was -quite bad from a usability point of view. Now we compare new defaults against -a copy of their previous values and only change those that are different in -the user config file. This way almost all his/her values remain intact. - -In particular: - -* defaults-2.4.0.ini is used to do the update when the previous used version - is between 2.1.9 and 2.3.0beta3 - -* defaults-3.0.0.ini is used when the previous version is 2.3.0beta4 - -Notes -===== - -1. Please don't add more files here, unless you know what you're doing. diff -Nru spyder-2.3.8+dfsg1/spyderlib/dependencies.py spyder-3.0.2+dfsg1/spyderlib/dependencies.py --- spyder-2.3.8+dfsg1/spyderlib/dependencies.py 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/dependencies.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2013 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Module checking Spyder optional runtime dependencies""" - - -import os - -# Local imports -from spyderlib.utils import programs - - -class Dependency(object): - """Spyder's optional dependency - - version may starts with =, >=, > or < to specify the exact requirement ; - multiple conditions may be separated by ';' (e.g. '>=0.13;<1.0')""" - - OK = 'OK' - NOK = 'NOK' - - def __init__(self, modname, features, required_version, - installed_version=None): - self.modname = modname - self.features = features - self.required_version = required_version - if installed_version is None: - try: - self.installed_version = programs.get_module_version(modname) - except ImportError: - # Module is not installed - self.installed_version = None - else: - self.installed_version = installed_version - - def check(self): - """Check if dependency is installed""" - return programs.is_module_installed(self.modname, - self.required_version, - self.installed_version) - - def get_installed_version(self): - """Return dependency status (string)""" - if self.check(): - return '%s (%s)' % (self.installed_version, self.OK) - else: - return '%s (%s)' % (self.installed_version, self.NOK) - - def get_status(self): - """Return dependency status (string)""" - if self.check(): - return self.OK - else: - return self.NOK - - -DEPENDENCIES = [] - -def add(modname, features, required_version, installed_version=None): - """Add Spyder optional dependency""" - global DEPENDENCIES - for dependency in DEPENDENCIES: - if dependency.modname == modname: - raise ValueError("Dependency has already been registered: %s"\ - % modname) - DEPENDENCIES += [Dependency(modname, features, required_version, - installed_version)] - -def check(modname): - """Check if required dependency is installed""" - global DEPENDENCIES - for dependency in DEPENDENCIES: - if dependency.modname == modname: - return dependency.check() - else: - raise RuntimeError("Unkwown dependency %s" % modname) - -def status(): - """Return a complete status of Optional Dependencies""" - global DEPENDENCIES - maxwidth = 0 - col1 = [] - col2 = [] - for dependency in DEPENDENCIES: - title1 = dependency.modname - title1 += ' ' + dependency.required_version - col1.append(title1) - maxwidth = max([maxwidth, len(title1)]) - col2.append(dependency.get_installed_version()) - text = "" - for index in range(len(DEPENDENCIES)): - text += col1[index].ljust(maxwidth) + ': ' + col2[index] + os.linesep - return text diff -Nru spyder-2.3.8+dfsg1/spyderlib/guiconfig.py spyder-3.0.2+dfsg1/spyderlib/guiconfig.py --- spyder-2.3.8+dfsg1/spyderlib/guiconfig.py 2015-11-27 13:29:54.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/guiconfig.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,167 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2012 The Spyder development team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Spyder GUI-related configuration management -(for non-GUI configuration, see spyderlib/baseconfig.py) - -Important note regarding shortcuts: - For compatibility with QWERTZ keyboards, one must avoid using the following - shortcuts: - Ctrl + Alt + Q, W, F, G, Y, X, C, V, B, N -""" - -from collections import namedtuple - -from spyderlib.qt.QtGui import QFont, QFontDatabase, QShortcut, QKeySequence -from spyderlib.qt.QtCore import Qt - -from spyderlib.config import CONF -from spyderlib.userconfig import NoDefault -from spyderlib.widgets.sourcecode import syntaxhighlighters as sh -from spyderlib.py3compat import to_text_string - - -# To save metadata about widget shortcuts (needed to build our -# preferences page) -Shortcut = namedtuple('Shortcut', 'data') - - -def font_is_installed(font): - """Check if font is installed""" - return [fam for fam in QFontDatabase().families() - if to_text_string(fam)==font] - - -def get_family(families): - """Return the first installed font family in family list""" - if not isinstance(families, list): - families = [ families ] - for family in families: - if font_is_installed(family): - return family - else: - print("Warning: None of the following fonts is installed: %r" % families) - return QFont().family() - - -FONT_CACHE = {} -def get_font(section, option=None): - """Get console font properties depending on OS and user options""" - font = FONT_CACHE.get((section, option)) - if font is None: - if option is None: - option = 'font' - else: - option += '/font' - families = CONF.get(section, option+"/family", None) - if families is None: - return QFont() - family = get_family(families) - weight = QFont.Normal - italic = CONF.get(section, option+'/italic', False) - if CONF.get(section, option+'/bold', False): - weight = QFont.Bold - size = CONF.get(section, option+'/size', 9) - font = QFont(family, size, weight) - font.setItalic(italic) - FONT_CACHE[(section, option)] = font - return font - - -def set_font(font, section, option=None): - """Set font""" - if option is None: - option = 'font' - else: - option += '/font' - CONF.set(section, option+'/family', to_text_string(font.family())) - CONF.set(section, option+'/size', float(font.pointSize())) - CONF.set(section, option+'/italic', int(font.italic())) - CONF.set(section, option+'/bold', int(font.bold())) - FONT_CACHE[(section, option)] = font - - -def get_shortcut(context, name, default=NoDefault): - """Get keyboard shortcut (key sequence string)""" - return CONF.get('shortcuts', '%s/%s' % (context, name), default=default) - - -def set_shortcut(context, name, keystr): - """Set keyboard shortcut (key sequence string)""" - CONF.set('shortcuts', '%s/%s' % (context, name), keystr) - - -def new_shortcut(keystr, parent, action): - """Define a new shortcut according to a keysequence string""" - sc = QShortcut(QKeySequence(keystr), parent, action) - sc.setContext(Qt.WidgetWithChildrenShortcut) - return sc - - -def create_shortcut(action, context, name, parent): - """Creates a Shortcut namedtuple for a widget""" - keystr = get_shortcut(context, name) - qsc = new_shortcut(keystr, parent, action) - sc = Shortcut(data=(qsc, name, keystr)) - return sc - - -def iter_shortcuts(): - """Iterate over keyboard shortcuts""" - for option in CONF.options('shortcuts'): - context, name = option.split("/", 1) - yield context, name, get_shortcut(context, name) - - -def remove_deprecated_shortcuts(data): - """Remove deprecated shortcuts (shortcuts in CONF but not registered)""" - section = 'shortcuts' - options = [('%s/%s' % (context, name)).lower() for (context, name) in data] - for option, _ in CONF.items(section, raw=CONF.raw): - if option not in options: - CONF.remove_option(section, option) - if len(CONF.items(section, raw=CONF.raw)) == 0: - CONF.remove_section(section) - - -def reset_shortcuts(): - """Reset keyboard shortcuts to default values""" - CONF.reset_to_defaults(section='shortcuts') - - -def get_color_scheme(name): - """Get syntax color scheme""" - color_scheme = {} - for key in sh.COLOR_SCHEME_KEYS: - color_scheme[key] = CONF.get("color_schemes", "%s/%s" % (name, key)) - return color_scheme - - -def set_color_scheme(name, color_scheme, replace=True): - """Set syntax color scheme""" - section = "color_schemes" - names = CONF.get("color_schemes", "names", []) - for key in sh.COLOR_SCHEME_KEYS: - option = "%s/%s" % (name, key) - value = CONF.get(section, option, default=None) - if value is None or replace or name not in names: - CONF.set(section, option, color_scheme[key]) - names.append(to_text_string(name)) - CONF.set(section, "names", sorted(list(set(names)))) - - -def set_default_color_scheme(name, replace=True): - """Reset color scheme to default values""" - assert name in sh.COLOR_SCHEME_NAMES - set_color_scheme(name, sh.get_color_scheme(name), replace=replace) - - -for _name in sh.COLOR_SCHEME_NAMES: - set_default_color_scheme(_name, replace=False) -CUSTOM_COLOR_SCHEME_NAME = "Custom" -set_color_scheme(CUSTOM_COLOR_SCHEME_NAME, sh.get_color_scheme("Spyder"), - replace=False) Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/1downarrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/1downarrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/1uparrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/1uparrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/2downarrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/2downarrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/2uparrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/2uparrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/arrow-continue.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/arrow-continue.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/arrow-step-in.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/arrow-step-in.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/arrow-step-out.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/arrow-step-out.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/arrow-step-over.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/arrow-step-over.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/auto_reload.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/auto_reload.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/browse_tab.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/browse_tab.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/check.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/check.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/cmdprompt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/cmdprompt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/collapse.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/collapse.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/collapse_selection.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/collapse_selection.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/configure.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/configure.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/copywop.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/copywop.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/delete.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/delete.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/edit24.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/edit24.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/edit_add.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/edit_add.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/editcopy.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/editcopy.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/editcut.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/editcut.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/editdelete.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/editdelete.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/editpaste.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/editpaste.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/edit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/edit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/edit_remove.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/edit_remove.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/eraser.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/eraser.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/exit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/exit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/expand.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/expand.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/expand_selection.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/expand_selection.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/filter.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/filter.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/findf.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/findf.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/findnext.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/findnext.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/find.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/find.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/findprevious.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/findprevious.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/folder_new.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/folder_new.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/hide.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/hide.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/hist.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/hist.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/home.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/home.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/imshow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/imshow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/insert.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/insert.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/lock_open.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/lock_open.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/lock.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/lock.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/magnifier.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/magnifier.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/maximize.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/maximize.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/next.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/next.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/options_less.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/options_less.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/options_more.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/options_more.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/plot.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/plot.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/previous.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/previous.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/redo.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/redo.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/reload.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/reload.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/rename.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/rename.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/replace.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/replace.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/restore.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/restore.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/show.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/show.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/special_paste.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/special_paste.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/stop_debug.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/stop_debug.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/stop.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/stop.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/synchronize.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/synchronize.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/tooloptions.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/tooloptions.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/undo.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/undo.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/unmaximize.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/unmaximize.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/up.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/up.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/window_fullscreen.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/window_fullscreen.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/window_nofullscreen.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/window_nofullscreen.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/zoom_in.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/zoom_in.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/actions/zoom_out.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/actions/zoom_out.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/advanced.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/advanced.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/arredit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/arredit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/arrow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/arrow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/bold.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/bold.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/browser.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/browser.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/chevron-left.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/chevron-left.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/chevron-right.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/chevron-right.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/clear.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/clear.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/cmdprompt_t.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/cmdprompt_t.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/console.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/console.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/environ.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/environ.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/history24.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/history24.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/history.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/history.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/ipython_console.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/ipython_console.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/ipython_console_t.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/ipython_console_t.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/kill.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/kill.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/loading_sprites.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/loading_sprites.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/prompt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/prompt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/python.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/python.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/python_t.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/python_t.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/restart.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/restart.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/run_small.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/run_small.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/syspath.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/syspath.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/console/terminated.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/console/terminated.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/dictedit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/dictedit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/blockcomment.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/blockcomment.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/breakpoint_big.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/breakpoint_big.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/breakpoint_cond_big.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/breakpoint_cond_big.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/breakpoint_cond_small.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/breakpoint_cond_small.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/breakpoint_small.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/breakpoint_small.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/bug.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/bug.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/cell.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/cell.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/class.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/class.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/close_panel.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/close_panel.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/comment.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/comment.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/convention.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/convention.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/debug.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/debug.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/error.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/error.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/filelist.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/filelist.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/file.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/file.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/fromcursor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/fromcursor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/function.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/function.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/gotoline.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/gotoline.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/highlight.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/highlight.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/horsplit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/horsplit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/indent.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/indent.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/last_edit_location.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/last_edit_location.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/method.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/method.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/newwindow.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/newwindow.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/next_cursor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/next_cursor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/next_wng.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/next_wng.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/outline_explorer.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/outline_explorer.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/outline_explorer_vis.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/outline_explorer_vis.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/prev_cursor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/prev_cursor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/prev_wng.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/prev_wng.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/private1.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/private1.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/private2.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/private2.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/refactor.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/refactor.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/run_again.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/run_again.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/run_cell_advance.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/run_cell_advance.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/run_cell.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/run_cell.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/run.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/run.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/run_selection.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/run_selection.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/run_settings.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/run_settings.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/selectall.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/selectall.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/select.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/select.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/todo_list.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/todo_list.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/todo.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/todo.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/uncomment.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/uncomment.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/unindent.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/unindent.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/versplit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/versplit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/warning.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/warning.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/editor/wng_list.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/editor/wng_list.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/filecloseall.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/filecloseall.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/fileclose.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/fileclose.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/fileimport.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/fileimport.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/filenew.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/filenew.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/fileopen.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/fileopen.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/filesaveas.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/filesaveas.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/filesave.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/filesave.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/print.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/print.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/file/save_all.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/file/save_all.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/bat.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/bat.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/bmp.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/bmp.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/cc.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/cc.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/cfg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/cfg.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/chm.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/chm.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/cl.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/cl.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/cmd.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/cmd.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/c.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/c.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/cpp.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/cpp.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/css.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/css.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/cxx.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/cxx.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/diff.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/diff.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/doc.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/doc.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/enaml.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/enaml.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/exe.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/exe.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/f77.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/f77.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/f90.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/f90.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/f.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/f.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/gif.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/gif.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/hh.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/hh.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/h.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/h.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/hpp.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/hpp.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/html.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/html.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/htm.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/htm.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/hxx.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/hxx.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/inf.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/inf.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/ini.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/ini.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/jl.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/jl.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/jpeg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/jpeg.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/jpg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/jpg.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/js.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/js.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/log.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/log.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/nsh.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/nsh.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/nsi.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/nsi.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/nt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/nt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/patch.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/patch.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/pdf.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/pdf.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/png.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/png.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/po.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/po.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/pot.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/pot.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/pps.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/pps.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/properties.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/properties.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/ps.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/ps.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/pxd.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/pxd.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/pxi.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/pxi.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/pyc.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/pyc.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/py.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/py.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/pyw.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/pyw.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/pyx.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/pyx.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/rar.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/rar.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/readme.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/readme.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/reg.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/reg.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/rej.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/rej.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/session.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/session.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/tar.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/tar.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/tex.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/tex.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/tgz.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/tgz.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/tiff.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/tiff.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/tif.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/tif.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/ts.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/ts.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/txt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/txt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/ui.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/ui.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/xls.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/xls.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/xml.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/xml.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/filetypes/zip.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/filetypes/zip.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/font.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/font.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/genprefs.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/genprefs.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/inspector.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/inspector.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/italic.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/italic.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/matplotlib.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/matplotlib.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/none.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/none.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/not_found.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/not_found.png differ diff -Nru spyder-2.3.8+dfsg1/spyderlib/images/options.svg spyder-3.0.2+dfsg1/spyderlib/images/options.svg --- spyder-2.3.8+dfsg1/spyderlib/images/options.svg 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/images/options.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/add_to_path.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/add_to_path.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/folder.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/folder.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/package.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/package.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/pp_folder.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/pp_folder.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/pp_package.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/pp_package.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/pp_project.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/pp_project.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/project_closed.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/project_closed.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/project.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/project.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/pydev.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/pydev.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/pythonpath.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/pythonpath.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/remove_from_path.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/remove_from_path.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/projects/show_all.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/projects/show_all.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/pythonpath_mgr.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/pythonpath_mgr.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/pythonxy.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/pythonxy.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/qtassistant.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/qtassistant.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/qtdesigner.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/qtdesigner.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/qtlinguist.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/qtlinguist.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/qt.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/qt.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/scipy.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/scipy.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/set_workdir.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/set_workdir.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/splash.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/splash.png differ diff -Nru spyder-2.3.8+dfsg1/spyderlib/images/spyder_light.svg spyder-3.0.2+dfsg1/spyderlib/images/spyder_light.svg --- spyder-2.3.8+dfsg1/spyderlib/images/spyder_light.svg 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/images/spyder_light.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,366 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru spyder-2.3.8+dfsg1/spyderlib/images/spyder.svg spyder-3.0.2+dfsg1/spyderlib/images/spyder.svg --- spyder-2.3.8+dfsg1/spyderlib/images/spyder.svg 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/images/spyder.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,384 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/upper_lower.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/upper_lower.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/vcs_browse.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/vcs_browse.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/vcs_commit.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/vcs_commit.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/vitables.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/vitables.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/whole_words.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/whole_words.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/images/win_env.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/images/win_env.png differ diff -Nru spyder-2.3.8+dfsg1/spyderlib/images/winpython.svg spyder-3.0.2+dfsg1/spyderlib/images/winpython.svg --- spyder-2.3.8+dfsg1/spyderlib/images/winpython.svg 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/images/winpython.svg 1970-01-01 00:00:00.000000000 +0000 @@ -1,444 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -Nru spyder-2.3.8+dfsg1/spyderlib/__init__.py spyder-3.0.2+dfsg1/spyderlib/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/__init__.py 2015-11-27 13:34:38.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Spyder License Agreement (MIT License) --------------------------------------- - -Copyright (c) 2009-2013 Pierre Raybaut -Copyright (c) 2013-2015 The Spyder Development Team - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. -""" - -__version__ = '2.3.8' -__license__ = __doc__ -__project_url__ = 'https://github.com/spyder-ide/spyder' -__forum_url__ = 'http://groups.google.com/group/spyderlib' - -# Dear (Debian, RPM, ...) package makers, please feel free to customize the -# following path to module's data (images) and translations: -DATAPATH = LOCALEPATH = DOCPATH = MATHJAXPATH = JQUERYPATH = '' - - -import os -# Directory of the current file -__dir__ = os.path.dirname(os.path.abspath(__file__)) - - -def add_to_distribution(dist): - """Add package to py2exe/cx_Freeze distribution object - Extension to guidata.disthelpers""" - try: - dist.add_qt_bindings() - except AttributeError: - raise ImportError("This script requires guidata 1.5+") - for _modname in ('spyderlib', 'spyderplugins'): - dist.add_module_data_files(_modname, ("", ), - ('.png', '.svg', '.html', '.png', '.txt', - '.js', '.inv', '.ico', '.css', '.doctree', - '.qm', '.py',), - copy_to_root=False) - - -def get_versions(reporev=True): - """Get version information for components used by Spyder""" - import sys - import platform - import spyderlib.qt - import spyderlib.qt.QtCore - - revision = None - if reporev: - from spyderlib.utils import vcs - revision = vcs.get_git_revision(os.path.dirname(__dir__)) - - if not sys.platform == 'darwin': # To avoid a crash with our Mac app - system = platform.system() - else: - system = 'Darwin' - - return { - 'spyder': __version__, - 'python': platform.python_version(), # "2.7.3" - 'bitness': 64 if sys.maxsize > 2**32 else 32, - 'qt': spyderlib.qt.QtCore.__version__, - 'qt_api': spyderlib.qt.API_NAME, # PySide or PyQt4 - 'qt_api_ver': spyderlib.qt.__version__, - 'system': system, # Linux, Windows, ... - 'revision': revision, # '9fdf926eccce' - } diff -Nru spyder-2.3.8+dfsg1/spyderlib/interpreter.py spyder-3.0.2+dfsg1/spyderlib/interpreter.py --- spyder-2.3.8+dfsg1/spyderlib/interpreter.py 2015-10-11 22:53:22.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/interpreter.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,337 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Shell Interpreter""" - -from __future__ import print_function - -import sys -import atexit -import threading -import ctypes -import os -import re -import os.path as osp -import pydoc -from subprocess import Popen, PIPE -from code import InteractiveConsole - -# Local imports: -from spyderlib.utils.dochelpers import isdefined -from spyderlib.utils import encoding -from spyderlib.py3compat import is_text_string, getcwd -from spyderlib.utils.misc import remove_backslashes - -# Force Python to search modules in the current directory first: -sys.path.insert(0, '') - - -def guess_filename(filename): - """Guess filename""" - if osp.isfile(filename): - return filename - if not filename.endswith('.py'): - filename += '.py' - for path in [getcwd()] + sys.path: - fname = osp.join(path, filename) - if osp.isfile(fname): - return fname - elif osp.isfile(fname+'.py'): - return fname+'.py' - elif osp.isfile(fname+'.pyw'): - return fname+'.pyw' - return filename - -class Interpreter(InteractiveConsole, threading.Thread): - """Interpreter, executed in a separate thread""" - p1 = ">>> " - p2 = "... " - def __init__(self, namespace=None, exitfunc=None, - Output=None, WidgetProxy=None, debug=False): - """ - namespace: locals send to InteractiveConsole object - commands: list of commands executed at startup - """ - InteractiveConsole.__init__(self, namespace) - threading.Thread.__init__(self) - - self._id = None - - self.exit_flag = False - self.debug = debug - - # Execution Status - self.more = False - - if exitfunc is not None: - atexit.register(exitfunc) - - self.namespace = self.locals - self.namespace['__name__'] = '__main__' - self.namespace['execfile'] = self.execfile - self.namespace['runfile'] = self.runfile - self.namespace['raw_input'] = self.raw_input_replacement - self.namespace['help'] = self.help_replacement - - # Capture all interactive input/output - self.initial_stdout = sys.stdout - self.initial_stderr = sys.stderr - self.initial_stdin = sys.stdin - - # Create communication pipes - pr, pw = os.pipe() - self.stdin_read = os.fdopen(pr, "r") - self.stdin_write = os.fdopen(pw, "wb", 0) - self.stdout_write = Output() - self.stderr_write = Output() - - self.input_condition = threading.Condition() - self.widget_proxy = WidgetProxy(self.input_condition) - - self.redirect_stds() - - - #------ Standard input/output - def redirect_stds(self): - """Redirects stds""" - if not self.debug: - sys.stdout = self.stdout_write - sys.stderr = self.stderr_write - sys.stdin = self.stdin_read - - def restore_stds(self): - """Restore stds""" - if not self.debug: - sys.stdout = self.initial_stdout - sys.stderr = self.initial_stderr - sys.stdin = self.initial_stdin - - def raw_input_replacement(self, prompt=''): - """For raw_input builtin function emulation""" - self.widget_proxy.wait_input(prompt) - self.input_condition.acquire() - while not self.widget_proxy.data_available(): - self.input_condition.wait() - inp = self.widget_proxy.input_data - self.input_condition.release() - return inp - - def help_replacement(self, text=None, interactive=False): - """For help builtin function emulation""" - if text is not None and not interactive: - return pydoc.help(text) - elif text is None: - pyver = "%d.%d" % (sys.version_info[0], sys.version_info[1]) - self.write(""" -Welcome to Python %s! This is the online help utility. - -If this is your first time using Python, you should definitely check out -the tutorial on the Internet at http://www.python.org/doc/tut/. - -Enter the name of any module, keyword, or topic to get help on writing -Python programs and using Python modules. To quit this help utility and -return to the interpreter, just type "quit". - -To get a list of available modules, keywords, or topics, type "modules", -"keywords", or "topics". Each module also comes with a one-line summary -of what it does; to list the modules whose summaries contain a given word -such as "spam", type "modules spam". -""" % pyver) - else: - text = text.strip() - try: - eval("pydoc.help(%s)" % text) - except (NameError, SyntaxError): - print("no Python documentation found for '%r'" % text) - self.write(os.linesep) - self.widget_proxy.new_prompt("help> ") - inp = self.raw_input_replacement() - if inp.strip(): - self.help_replacement(inp, interactive=True) - else: - self.write(""" -You are now leaving help and returning to the Python interpreter. -If you want to ask for help on a particular object directly from the -interpreter, you can type "help(object)". Executing "help('string')" -has the same effect as typing a particular string at the help> prompt. -""") - - def run_command(self, cmd, new_prompt=True): - """Run command in interpreter""" - if cmd == 'exit()': - self.exit_flag = True - self.write('\n') - return - # -- Special commands type I - # (transformed into commands executed in the interpreter) - # ? command - special_pattern = r"^%s (?:r\')?(?:u\')?\"?\'?([a-zA-Z0-9_\.]+)" - run_match = re.match(special_pattern % 'run', cmd) - help_match = re.match(r'^([a-zA-Z0-9_\.]+)\?$', cmd) - cd_match = re.match(r"^\!cd \"?\'?([a-zA-Z0-9_ \.]+)", cmd) - if help_match: - cmd = 'help(%s)' % help_match.group(1) - # run command - elif run_match: - filename = guess_filename(run_match.groups()[0]) - cmd = "runfile('%s', args=None)" % remove_backslashes(filename) - # !cd system command - elif cd_match: - cmd = 'import os; os.chdir(r"%s")' % cd_match.groups()[0].strip() - # -- End of Special commands type I - - # -- Special commands type II - # (don't need code execution in interpreter) - xedit_match = re.match(special_pattern % 'xedit', cmd) - edit_match = re.match(special_pattern % 'edit', cmd) - clear_match = re.match(r"^clear ([a-zA-Z0-9_, ]+)", cmd) - # (external) edit command - if xedit_match: - filename = guess_filename(xedit_match.groups()[0]) - self.widget_proxy.edit(filename, external_editor=True) - # local edit command - elif edit_match: - filename = guess_filename(edit_match.groups()[0]) - if osp.isfile(filename): - self.widget_proxy.edit(filename) - else: - self.stderr_write.write( - "No such file or directory: %s\n" % filename) - # remove reference (equivalent to MATLAB's clear command) - elif clear_match: - varnames = clear_match.groups()[0].replace(' ', '').split(',') - for varname in varnames: - try: - self.namespace.pop(varname) - except KeyError: - pass - # Execute command - elif cmd.startswith('!'): - # System ! command - pipe = Popen(cmd[1:], shell=True, - stdin=PIPE, stderr=PIPE, stdout=PIPE) - txt_out = encoding.transcode( pipe.stdout.read().decode() ) - txt_err = encoding.transcode( pipe.stderr.read().decode().rstrip() ) - if txt_err: - self.stderr_write.write(txt_err) - if txt_out: - self.stdout_write.write(txt_out) - self.stdout_write.write('\n') - self.more = False - # -- End of Special commands type II - else: - # Command executed in the interpreter -# self.widget_proxy.set_readonly(True) - self.more = self.push(cmd) -# self.widget_proxy.set_readonly(False) - - if new_prompt: - self.widget_proxy.new_prompt(self.p2 if self.more else self.p1) - if not self.more: - self.resetbuffer() - - def run(self): - """Wait for input and run it""" - while not self.exit_flag: - self.run_line() - - def run_line(self): - line = self.stdin_read.readline() - if self.exit_flag: - return - # Remove last character which is always '\n': - self.run_command(line[:-1]) - - def get_thread_id(self): - """Return thread id""" - if self._id is None: - for thread_id, obj in list(threading._active.items()): - if obj is self: - self._id = thread_id - return self._id - - def raise_keyboard_interrupt(self): - if self.isAlive(): - ctypes.pythonapi.PyThreadState_SetAsyncExc(self.get_thread_id(), - ctypes.py_object(KeyboardInterrupt)) - return True - else: - return False - - - def closing(self): - """Actions to be done before restarting this interpreter""" - pass - - def execfile(self, filename): - """Exec filename""" - source = open(filename, 'r').read() - try: - try: - name = filename.encode('ascii') - except UnicodeEncodeError: - name = '' - code = compile(source, name, "exec") - except (OverflowError, SyntaxError): - InteractiveConsole.showsyntaxerror(self, filename) - else: - self.runcode(code) - - def runfile(self, filename, args=None): - """ - Run filename - args: command line arguments (string) - """ - if args is not None and not is_text_string(args): - raise TypeError("expected a character buffer object") - self.namespace['__file__'] = filename - sys.argv = [filename] - if args is not None: - for arg in args.split(): - sys.argv.append(arg) - self.execfile(filename) - sys.argv = [''] - self.namespace.pop('__file__') - - def eval(self, text): - """ - Evaluate text and return (obj, valid) - where *obj* is the object represented by *text* - and *valid* is True if object evaluation did not raise any exception - """ - assert is_text_string(text) - try: - return eval(text, self.locals), True - except: - return None, False - - def is_defined(self, objtxt, force_import=False): - """Return True if object is defined""" - return isdefined(objtxt, force_import=force_import, - namespace=self.locals) - - #=========================================================================== - # InteractiveConsole API - #=========================================================================== - def push(self, line): - """ - Push a line of source text to the interpreter - - The line should not have a trailing newline; it may have internal - newlines. The line is appended to a buffer and the interpreter’s - runsource() method is called with the concatenated contents of the - buffer as source. If this indicates that the command was executed - or invalid, the buffer is reset; otherwise, the command is incomplete, - and the buffer is left as it was after the line was appended. - The return value is True if more input is required, False if the line - was dealt with in some way (this is the same as runsource()). - """ - return InteractiveConsole.push(self, "#coding=utf-8\n" + line) - - def resetbuffer(self): - """Remove any unhandled source text from the input buffer""" - InteractiveConsole.resetbuffer(self) - \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/ipythonconfig.py spyder-3.0.2+dfsg1/spyderlib/ipythonconfig.py --- spyder-2.3.8+dfsg1/spyderlib/ipythonconfig.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/ipythonconfig.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2013 The Spyder Development Team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -IPython configuration variables needed by Spyder -""" - -from spyderlib.utils import programs -from spyderlib import dependencies -from spyderlib.baseconfig import _ - - -# Constants -IPYTHON_REQVER = '>=1.0' -ZMQ_REQVER = '>=2.1.11' -QTCONSOLE_REQVER = '>=4.0' - - -# Dependencies -dependencies.add("IPython", _("IPython Console integration"), - required_version=IPYTHON_REQVER) -dependencies.add("zmq", _("IPython Console integration"), - required_version=ZMQ_REQVER) - - -# Jupyter 4.0 requirements -ipy4_installed = programs.is_module_installed('IPython', '>=4.0') -if ipy4_installed: - dependencies.add("qtconsole", _("IPython Console integration"), - required_version=QTCONSOLE_REQVER) - - -# Auxiliary functions -def is_qtconsole_installed(): - pyzmq_installed = programs.is_module_installed('zmq') - pygments_installed = programs.is_module_installed('pygments') - ipyqt_installed = programs.is_module_installed('IPython.qt') - - if ipyqt_installed and pyzmq_installed and pygments_installed: - if ipy4_installed: - if programs.is_module_installed('qtconsole'): - return True - else: - return False - else: - return True - else: - return False - - -# Main check for IPython presence -IPYTHON_QT_INSTALLED = is_qtconsole_installed() Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/locale/es/LC_MESSAGES/spyderlib.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/locale/es/LC_MESSAGES/spyderlib.mo differ diff -Nru spyder-2.3.8+dfsg1/spyderlib/locale/es/LC_MESSAGES/spyderlib.po spyder-3.0.2+dfsg1/spyderlib/locale/es/LC_MESSAGES/spyderlib.po --- spyder-2.3.8+dfsg1/spyderlib/locale/es/LC_MESSAGES/spyderlib.po 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/locale/es/LC_MESSAGES/spyderlib.po 1970-01-01 00:00:00.000000000 +0000 @@ -1,5214 +0,0 @@ -# -*- coding: utf-8 -*- -# Spyder's spanish translation file -# Copyright (C) 2011 Spyder Development team -# -msgid "" -msgstr "" -"Project-Id-Version: 2.1\n" -"POT-Creation-Date: 2015-11-24 20:56+COT\n" -"PO-Revision-Date: 2015-08-24 15:13-0500\n" -"Last-Translator: Carlos Cordoba \n" -"Language-Team: Python\n" -"Language: es\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" -"X-Poedit-SourceCharset: utf-8\n" -"X-Poedit-Basepath: ../../../../\n" -"X-Generator: Poedit 1.5.4\n" - -#: spyderlib/config.py:29 -msgid "Python files" -msgstr "Archivos Python" - -#: spyderlib/config.py:30 -msgid "Cython/Pyrex files" -msgstr "Archivos Cython/Pyrex" - -#: spyderlib/config.py:31 -msgid "C files" -msgstr "Archivos C" - -#: spyderlib/config.py:32 -msgid "C++ files" -msgstr "Archivos C++" - -#: spyderlib/config.py:33 -msgid "OpenCL files" -msgstr "Archivos OpenCL" - -#: spyderlib/config.py:34 -msgid "Fortran files" -msgstr "Archivos Fortran" - -#: spyderlib/config.py:35 -msgid "IDL files" -msgstr "Archivos IDL" - -#: spyderlib/config.py:36 -msgid "MATLAB files" -msgstr "Archivos MATLAB" - -#: spyderlib/config.py:37 -msgid "Julia files" -msgstr "Archivos Julia" - -#: spyderlib/config.py:38 -msgid "Yaml files" -msgstr "Archivos Yaml" - -#: spyderlib/config.py:39 -msgid "Patch and diff files" -msgstr "Archivos Patch y diff" - -#: spyderlib/config.py:40 -msgid "Batch files" -msgstr "Archivos Batch" - -#: spyderlib/config.py:41 spyderlib/utils/iofuncs.py:514 -msgid "Text files" -msgstr "Archivos de Texto" - -#: spyderlib/config.py:42 -msgid "reStructured Text files" -msgstr "Archivos de Texto reStructurado" - -#: spyderlib/config.py:43 -msgid "gettext files" -msgstr "Archivos gettext" - -#: spyderlib/config.py:44 -msgid "NSIS files" -msgstr "Archivos NSIS" - -#: spyderlib/config.py:45 -msgid "Web page files" -msgstr "Archivos de Páginas web" - -#: spyderlib/config.py:46 -msgid "XML files" -msgstr "Archivos XML" - -#: spyderlib/config.py:47 -msgid "Javascript files" -msgstr "Archivos Javascript" - -#: spyderlib/config.py:48 -msgid "Json files" -msgstr "Archivos Json" - -#: spyderlib/config.py:49 -msgid "IPython notebooks" -msgstr "Notebooks de IPython" - -#: spyderlib/config.py:50 -msgid "Enaml files" -msgstr "Archivos Enaml" - -#: spyderlib/config.py:51 -msgid "Configuration files" -msgstr "Archivos de Configuración" - -#: spyderlib/config.py:58 spyderlib/widgets/explorer.py:651 -msgid "All files" -msgstr "Todos los archivos" - -#: spyderlib/ipythonconfig.py:23 spyderlib/ipythonconfig.py:25 -#: spyderlib/ipythonconfig.py:32 -msgid "IPython Console integration" -msgstr "Integración con la terminal de IPython" - -#: spyderlib/plugins/__init__.py:318 spyderlib/plugins/editor.py:94 -#: spyderlib/plugins/editor.py:527 spyderlib/plugins/editor.py:1606 -#: spyderlib/plugins/inspector.py:134 spyderlib/plugins/inspector.py:403 -#: spyderlib/widgets/editor.py:434 -#: spyderlib/widgets/sourcecode/codeeditor.py:85 -#: spyderlib/widgets/sourcecode/codeeditor.py:2709 -msgid "Editor" -msgstr "Editor" - -#: spyderlib/plugins/configdialog.py:144 -msgid "Preferences" -msgstr "Preferencias" - -#: spyderlib/plugins/configdialog.py:429 -msgid "Invalid directory path" -msgstr "Ruta de directorio inválida" - -#: spyderlib/plugins/configdialog.py:432 spyderlib/plugins/configdialog.py:448 -#: spyderlib/plugins/runconfig.py:172 spyderlib/plugins/runconfig.py:236 -#: spyderlib/plugins/workingdirectory.py:286 spyderlib/widgets/explorer.py:565 -#: spyderlib/widgets/externalshell/pythonshell.py:623 -#: spyderlib/widgets/findinfiles.py:504 spyderlib/widgets/pathmanager.py:218 -#: spyderlib/widgets/projectexplorer.py:890 -msgid "Select directory" -msgstr "Seleccionar directorio" - -#: spyderlib/plugins/configdialog.py:460 -msgid "Invalid file path" -msgstr "Ruta de archivo inválida" - -#: spyderlib/plugins/configdialog.py:463 spyderlib/plugins/configdialog.py:481 -msgid "Select file" -msgstr "Seleccionar archivo" - -#: spyderlib/plugins/configdialog.py:480 -msgid "All files (*)" -msgstr "Todos los archivos (*)" - -#: spyderlib/plugins/configdialog.py:550 spyderlib/widgets/formlayout.py:216 -msgid "Bold" -msgstr "Negrita" - -#: spyderlib/plugins/configdialog.py:553 spyderlib/widgets/formlayout.py:211 -msgid "Italic" -msgstr "Cursiva" - -#: spyderlib/plugins/configdialog.py:591 -msgid "Font: " -msgstr "Tipo de letra" - -#: spyderlib/plugins/configdialog.py:595 -msgid "Size: " -msgstr "Tamaño:" - -#: spyderlib/plugins/configdialog.py:604 spyderlib/plugins/history.py:47 -msgid "Font style" -msgstr "Fuente" - -#: spyderlib/plugins/configdialog.py:657 -msgid "General" -msgstr "General" - -#: spyderlib/plugins/configdialog.py:664 spyderlib/plugins/editor.py:103 -#: spyderlib/plugins/externalconsole.py:65 -#: spyderlib/plugins/ipythonconsole.py:161 -msgid "Interface" -msgstr "Interfaz" - -#: spyderlib/plugins/configdialog.py:672 -msgid "Qt windows style" -msgstr "Estilo de Qt" - -#: spyderlib/plugins/configdialog.py:676 -msgid "Use a single instance" -msgstr "Utilizar una única instancia" - -#: spyderlib/plugins/configdialog.py:678 -msgid "" -"Set this to open external
    Python files in an already running instance " -"(Requires a restart)" -msgstr "" -"Seleccione esta opción
    para abrir archivos externos de Python en la " -"ventana actual (Requiere reiniciar)" - -#: spyderlib/plugins/configdialog.py:681 -msgid "Vertical dockwidget title bars" -msgstr "Barras de título verticales para los componentes" - -#: spyderlib/plugins/configdialog.py:683 -msgid "Vertical dockwidget tabs" -msgstr "Componentes en pestañas verticales" - -#: spyderlib/plugins/configdialog.py:685 -msgid "Animated toolbars and dockwidgets" -msgstr "Barras de herramientas y componentes animados" - -#: spyderlib/plugins/configdialog.py:687 -msgid "Tear off menus" -msgstr "Separar los menús" - -#: spyderlib/plugins/configdialog.py:688 -msgid "Set this to detach any
    menu from the main window" -msgstr "" -"Establezca esta opción
    si desea separar los menús de la ventana principal" - -#: spyderlib/plugins/configdialog.py:690 -msgid "Custom dockwidget margin:" -msgstr "Márgenes de componente personalizadas:" - -#: spyderlib/plugins/configdialog.py:717 -msgid "Status bar" -msgstr "Barra de estado" - -#: spyderlib/plugins/configdialog.py:718 -msgid "Show memory usage every" -msgstr "Mostrar la memoria usada cada" - -#: spyderlib/plugins/configdialog.py:729 -msgid "Show CPU usage every" -msgstr "Mostrar el uso de CPU cada" - -#: spyderlib/plugins/configdialog.py:746 -msgid "Debugging" -msgstr "Depurar" - -#: spyderlib/plugins/configdialog.py:747 -msgid "Pop up internal console when internal errors appear" -msgstr "Mostrar la terminal interna cuando se produzcan errores" - -#: spyderlib/plugins/configdialog.py:769 -msgid "Syntax coloring" -msgstr "Coloreado de sintaxis" - -#: spyderlib/plugins/configdialog.py:778 -msgid "Background:" -msgstr "Fondo:" - -#: spyderlib/plugins/configdialog.py:779 -#: spyderlib/widgets/sourcecode/codeeditor.py:95 -msgid "Current line:" -msgstr "Línea seleccionada:" - -#: spyderlib/plugins/configdialog.py:780 -msgid "Current cell:" -msgstr "Celda seleccionada:" - -#: spyderlib/plugins/configdialog.py:781 -msgid "Occurence:" -msgstr "Ocurrencia:" - -#: spyderlib/plugins/configdialog.py:782 -msgid "Link:" -msgstr "Enlace:" - -#: spyderlib/plugins/configdialog.py:783 -msgid "Side areas:" -msgstr "Áreas laterales:" - -#: spyderlib/plugins/configdialog.py:784 -msgid "Matched parentheses:" -msgstr "Paréntesis emparejados:" - -#: spyderlib/plugins/configdialog.py:785 -msgid "Unmatched parentheses:" -msgstr "Paréntesis desemparejados:" - -#: spyderlib/plugins/configdialog.py:786 -msgid "Normal text:" -msgstr "Texto normal:" - -#: spyderlib/plugins/configdialog.py:787 -msgid "Keyword:" -msgstr "Palabra clave:" - -#: spyderlib/plugins/configdialog.py:788 -msgid "Builtin:" -msgstr "Objeto integrado:" - -#: spyderlib/plugins/configdialog.py:789 -msgid "Definition:" -msgstr "Definición:" - -#: spyderlib/plugins/configdialog.py:790 -msgid "Comment:" -msgstr "Comentario:" - -#: spyderlib/plugins/configdialog.py:791 -msgid "String:" -msgstr "Cadena:" - -#: spyderlib/plugins/configdialog.py:792 -msgid "Number:" -msgstr "Número:" - -#: spyderlib/plugins/configdialog.py:793 -msgid "Instance:" -msgstr "Instancia:" - -#: spyderlib/plugins/configdialog.py:799 -msgid "Color scheme" -msgstr "Esquema de coloreado" - -#: spyderlib/plugins/configdialog.py:821 spyderlib/plugins/shortcuts.py:344 -msgid "Reset to default values" -msgstr "Restaurar los valores por defecto" - -#: spyderlib/plugins/console.py:105 -msgid "Internal console" -msgstr "Terminal interna" - -#: spyderlib/plugins/console.py:125 spyderlib/spyder.py:772 -#: spyderlib/widgets/ipython.py:583 -msgid "&Quit" -msgstr "&Salir" - -#: spyderlib/plugins/console.py:126 spyderlib/spyder.py:773 -msgid "Quit" -msgstr "Salir" - -#: spyderlib/plugins/console.py:129 spyderlib/plugins/externalconsole.py:1099 -msgid "&Run..." -msgstr "E&jecutar..." - -#: spyderlib/plugins/console.py:130 spyderlib/plugins/externalconsole.py:1100 -msgid "Run a Python script" -msgstr "Ejecutar un archivo de Python" - -#: spyderlib/plugins/console.py:133 -msgid "Environment variables..." -msgstr "Variables de entorno..." - -#: spyderlib/plugins/console.py:135 -msgid "Show and edit environment variables (for current session)" -msgstr "Muestra y edita las variables de entorno (para la sesión actual)" - -#: spyderlib/plugins/console.py:139 -msgid "Show sys.path contents..." -msgstr "Contenidos del sys.path" - -#: spyderlib/plugins/console.py:141 -msgid "Show (read-only) sys.path" -msgstr "Muestra los contenidos del sys.path en modo lectura" - -#: spyderlib/plugins/console.py:144 -msgid "Buffer..." -msgstr "Mostrar líneas..." - -#: spyderlib/plugins/console.py:145 spyderlib/plugins/externalconsole.py:85 -#: spyderlib/plugins/history.py:40 -msgid "Set maximum line count" -msgstr "Establece el máximo número de líneas a mostrar en la terminal" - -#: spyderlib/plugins/console.py:148 spyderlib/plugins/explorer.py:57 -#: spyderlib/plugins/history.py:164 spyderlib/plugins/inspector.py:372 -#: spyderlib/plugins/projectexplorer.py:56 -msgid "&Font..." -msgstr "&Tipo de letra..." - -#: spyderlib/plugins/console.py:149 spyderlib/plugins/history.py:165 -msgid "Set shell font style" -msgstr "Establece el tipo de fuente de la terminal" - -#: spyderlib/plugins/console.py:152 -msgid "External editor path..." -msgstr "Ruta del editor externo..." - -#: spyderlib/plugins/console.py:153 -msgid "Set external editor executable path" -msgstr "Establece la ruta del editor externo" - -#: spyderlib/plugins/console.py:156 spyderlib/plugins/editor.py:144 -#: spyderlib/plugins/externalconsole.py:86 spyderlib/plugins/history.py:43 -#: spyderlib/plugins/history.py:167 spyderlib/plugins/inspector.py:175 -#: spyderlib/plugins/inspector.py:375 -msgid "Wrap lines" -msgstr "Ajuste de línea automático" - -#: spyderlib/plugins/console.py:159 spyderlib/plugins/editor.py:178 -#: spyderlib/plugins/externalconsole.py:133 -#: spyderlib/plugins/ipythonconsole.py:175 -msgid "Display balloon tips" -msgstr "Mostrar globos de sugerencias" - -#: spyderlib/plugins/console.py:163 spyderlib/plugins/editor.py:172 -#: spyderlib/plugins/externalconsole.py:127 -msgid "Automatic code completion" -msgstr "Completar código automáticamente" - -#: spyderlib/plugins/console.py:167 spyderlib/plugins/editor.py:176 -#: spyderlib/plugins/externalconsole.py:131 -msgid "Enter key selects completion" -msgstr "La tecla Enter selecciona el resultado a completar" - -#: spyderlib/plugins/console.py:172 -msgid "Internal console settings" -msgstr "Opciones" - -#: spyderlib/plugins/console.py:223 spyderlib/plugins/externalconsole.py:1285 -msgid "Run Python script" -msgstr "Ejecutar archivo de Python" - -#: spyderlib/plugins/console.py:224 spyderlib/plugins/externalconsole.py:229 -#: spyderlib/plugins/externalconsole.py:1286 spyderlib/widgets/explorer.py:666 -msgid "Python scripts" -msgstr "Archivos de Python" - -#: spyderlib/plugins/console.py:269 spyderlib/plugins/explorer.py:109 -#: spyderlib/plugins/history.py:282 spyderlib/plugins/inspector.py:651 -#: spyderlib/plugins/projectexplorer.py:118 -msgid "Select a new font" -msgstr "Seleccionar una nueva fuente" - -#: spyderlib/plugins/console.py:276 -msgid "Buffer" -msgstr "Mostrar" - -#: spyderlib/plugins/console.py:277 -msgid "Maximum line count" -msgstr "Máximo número de líneas a mostrar" - -#: spyderlib/plugins/console.py:286 -msgid "External editor" -msgstr "Editor externo" - -#: spyderlib/plugins/console.py:287 -msgid "External editor executable path:" -msgstr "Ruta ejecutable del editor externo:" - -#: spyderlib/plugins/editor.py:100 -msgid "Edit template for new modules" -msgstr "Editar la plantilla para nuevos módulos" - -#: spyderlib/plugins/editor.py:105 -msgid "Text and margin font style" -msgstr "Tipo de letra para el texto y las márgenes" - -#: spyderlib/plugins/editor.py:108 -msgid "Sort files according to full path" -msgstr "Ordenar archivos según su ruta completa" - -#: spyderlib/plugins/editor.py:110 -msgid "Show tab bar" -msgstr "Mostrar barra de pestañas" - -#: spyderlib/plugins/editor.py:117 spyderlib/plugins/editor.py:192 -#: spyderlib/plugins/externalconsole.py:81 -#: spyderlib/plugins/externalconsole.py:126 spyderlib/plugins/history.py:42 -#: spyderlib/plugins/inspector.py:174 spyderlib/plugins/ipythonconsole.py:199 -msgid "Source code" -msgstr "Código fuente" - -#: spyderlib/plugins/editor.py:118 -msgid "Show line numbers" -msgstr "Mostrar números de líneas" - -#: spyderlib/plugins/editor.py:119 spyderlib/plugins/editor.py:892 -msgid "Show blank spaces" -msgstr "Mostrar espacios en blanco" - -#: spyderlib/plugins/editor.py:120 -msgid "Show vertical line after" -msgstr "Mostrar una línea vertical después de" - -#: spyderlib/plugins/editor.py:121 -msgid "characters" -msgstr "caracteres" - -#: spyderlib/plugins/editor.py:129 -msgid "Highlight current line" -msgstr "Resaltar la línea actual" - -#: spyderlib/plugins/editor.py:131 -msgid "Highlight current cell" -msgstr "Resaltar la celda actual" - -#: spyderlib/plugins/editor.py:133 -msgid "Highlight occurences after" -msgstr "Resaltar ocurrencias después de" - -#: spyderlib/plugins/editor.py:147 spyderlib/plugins/history.py:51 -#: spyderlib/plugins/inspector.py:178 -msgid "Syntax color scheme: " -msgstr "Esquema de coloreado:" - -#: spyderlib/plugins/editor.py:161 spyderlib/plugins/runconfig.py:313 -#: spyderlib/plugins/runconfig.py:435 spyderlib/plugins/runconfig.py:440 -#: spyderlib/spyder.py:1850 spyderlib/utils/programs.py:175 -#: spyderlib/widgets/explorer.py:234 -#: spyderlib/widgets/externalshell/baseshell.py:138 -msgid "Run" -msgstr "Ejecutar" - -#: spyderlib/plugins/editor.py:162 -msgid "Save all files before running script" -msgstr "Guardar todo antes de ejecutar un archivo" - -#: spyderlib/plugins/editor.py:165 -msgid "Run selection" -msgstr "Ejecutar selección" - -#: spyderlib/plugins/editor.py:166 -msgid "Maintain focus in the Editor after running cells or selections" -msgstr "Mantener el foco en el Editor después de ejecutar celdas o selecciones" - -#: spyderlib/plugins/editor.py:169 spyderlib/plugins/externalconsole.py:365 -msgid "Introspection" -msgstr "Introspección" - -#: spyderlib/plugins/editor.py:174 spyderlib/plugins/externalconsole.py:129 -msgid "Case sensitive code completion" -msgstr "Diferenciar entre mayúsculas y minúsculas al completar código" - -#: spyderlib/plugins/editor.py:179 -msgid "Link to object definition" -msgstr "Enlazar a la definición de un objeto" - -#: spyderlib/plugins/editor.py:181 -msgid "" -"If this option is enabled, clicking on an object\n" -"name (left-click + Ctrl key) will go this object\n" -"definition (if resolved)." -msgstr "" -"Si está opción está activada, al hacer click\n" -"sobre el nombre de un objeto (click-izquierdo +\n" -"la tecla Ctrl), el Editor se ubicará en la definición\n" -"del mismo (de poder resolverse el nombre)." - -#: spyderlib/plugins/editor.py:185 -msgid "" -"Warning:
    The Python module rope is not installed on this " -"computer: calltips, code completion and go-to-definition features won't be " -"available." -msgstr "" -"Advertencia:
    El módulo de Pythonrope no está instalado en " -"este computador. Por tanto el completado de código, el ir a la definición de " -"una función o método y los globos de sugerencias se encontrarán desactivados." - -#: spyderlib/plugins/editor.py:193 -msgid "Automatic insertion of parentheses, braces and brackets" -msgstr "Inserción automática de paréntesis, llaves y corchetes" - -#: spyderlib/plugins/editor.py:196 -msgid "Automatic insertion of closing quotes" -msgstr "Inserción automática de comillas" - -#: spyderlib/plugins/editor.py:198 -msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" -msgstr "Inserción automática de ':' después de 'for', 'if', 'def', etc" - -#: spyderlib/plugins/editor.py:201 -msgid "Automatic indentation after 'else', 'elif', etc." -msgstr "Indentación automática después de 'else', 'elif', etc." - -#: spyderlib/plugins/editor.py:203 -msgid "Indentation characters: " -msgstr "Caracteres de indentación:" - -#: spyderlib/plugins/editor.py:204 -msgid "4 spaces" -msgstr "4 espacios" - -#: spyderlib/plugins/editor.py:205 -msgid "2 spaces" -msgstr "2 espacios" - -#: spyderlib/plugins/editor.py:206 -msgid "tab" -msgstr "tabulador" - -#: spyderlib/plugins/editor.py:207 -msgid "Tab stop width:" -msgstr "Ancho de las tabulaciones:" - -#: spyderlib/plugins/editor.py:207 -msgid "pixels" -msgstr "pixels" - -#: spyderlib/plugins/editor.py:209 -msgid "Tab always indent" -msgstr "Siempre indentar con la tecla Tab" - -#: spyderlib/plugins/editor.py:211 -msgid "" -"If enabled, pressing Tab will always indent,\n" -"even when the cursor is not at the beginning\n" -"of a line (when this option is enabled, code\n" -"completion may be triggered using the alternate\n" -"shortcut: Ctrl+Space)" -msgstr "" -"Si esta opción está activada, el oprimir Tab\n" -"siempre indentará el código, aún cuando el\n" -"cursor no esté al principio de una línea\n" -"(de seleccionar esta opción, se puede usar\n" -"la combinación de teclas Ctrl+Espacio para\n" -"activar el completado de código)" - -#: spyderlib/plugins/editor.py:216 -msgid "Intelligent backspace" -msgstr "Tecla de retroceso (\"backspace\") inteligente" - -#: spyderlib/plugins/editor.py:218 -msgid "Automatically remove trailing spaces when saving files" -msgstr "Eliminar automáticamente espacios en blanco al guardar un archivo" - -#: spyderlib/plugins/editor.py:222 -msgid "Analysis" -msgstr "Análisis" - -#: spyderlib/plugins/editor.py:224 -msgid "" -"Note: add analysis:ignore in a comment to ignore code/style " -"analysis warnings. For more informations on style guide for Python code, " -"please refer to the %s page." -msgstr "" -"Nota: Añada analysis:ignore a un comentario para ignorar " -"advertencias de análisis de código o estilo. Para más información sobre una " -"guía de estilo para escribir código en Python, por favor refiérase a la " -"página de %s (en inglés).
    " - -#: spyderlib/plugins/editor.py:233 -#: spyderlib/widgets/sourcecode/codeeditor.py:1617 -msgid "Code analysis" -msgstr "Análisis del código" - -#: spyderlib/plugins/editor.py:235 -msgid "" -"If enabled, Python source code will be analyzed\n" -"using pyflakes, lines containing errors or \n" -"warnings will be highlighted" -msgstr "" -"Si esta opción está activada, los archivos de Python\n" -"serán analizados automáticamente y las líneas que\n" -"contengan errores o advertencias serán resaltadas" - -#: spyderlib/plugins/editor.py:240 -msgid "Code analysis requires pyflakes %s+" -msgstr "El análisis del código requiere pyflakes %s+" - -#: spyderlib/plugins/editor.py:242 -msgid "Style analysis" -msgstr "Análisis de estilo" - -#: spyderlib/plugins/editor.py:244 -msgid "" -"If enabled, Python source code will be analyzed\n" -"using pep8, lines that are not following PEP8\n" -"style guide will be highlighted" -msgstr "" -"Si esta opción está activada, los archivos de Python\n" -"serán analizados con PEP8 y las líneas que no sigan\n" -"esta guía de estilo serán resaltadas." - -#: spyderlib/plugins/editor.py:251 -msgid "Tasks (TODO, FIXME, XXX, HINT, TIP, @todo)" -msgstr "Tareas (TODO, FIXME, XXX, HINT, TIP, @todo)" - -#: spyderlib/plugins/editor.py:254 -msgid "Perform analysis when saving file and every" -msgstr "Realizar los análisis al guardar el archivo y cada" - -#: spyderlib/plugins/editor.py:258 -msgid "Perform analysis only when saving file" -msgstr "Realizar análisis sólo cuando se guarde el archivo" - -#: spyderlib/plugins/editor.py:306 -msgid "End-of-line characters" -msgstr "Caracteres de fin de línea" - -#: spyderlib/plugins/editor.py:307 -msgid "" -"When opening a text file containing mixed end-of-line characters (this may " -"raise syntax errors in the consoles on Windows platforms), Spyder may fix " -"the file automatically." -msgstr "" -"Cuando se abra un archivo de texto que contenga varios tipos de caracteres " -"de fin de línea (lo cual puede dar lugar a errores en Windows), Spyder puede " -"arreglar el archivo automáticamente." - -#: spyderlib/plugins/editor.py:313 -msgid "Fix automatically and show warning message box" -msgstr "Arreglar automáticamente y mostrar un mensaje de advertencia" - -#: spyderlib/plugins/editor.py:324 spyderlib/plugins/externalconsole.py:363 -#: spyderlib/plugins/ipythonconsole.py:444 -#: spyderlib/plugins/variableexplorer.py:41 -msgid "Display" -msgstr "Visualización" - -#: spyderlib/plugins/editor.py:326 -msgid "Code Introspection/Analysis" -msgstr "Análisis e introspección de código" - -#: spyderlib/plugins/editor.py:329 spyderlib/plugins/externalconsole.py:367 -msgid "Advanced settings" -msgstr "Opciones avanzadas" - -#: spyderlib/plugins/editor.py:583 spyderlib/widgets/editortools.py:508 -msgid "Show/hide outline explorer" -msgstr "" -"Mostrar u ocultar el\n" -"explorador de código" - -#: spyderlib/plugins/editor.py:589 -msgid "Show/hide project explorer" -msgstr "Mostrar/cerrar el explorador de proyectos" - -#: spyderlib/plugins/editor.py:597 -msgid "&New file..." -msgstr "&Nuevo" - -#: spyderlib/plugins/editor.py:598 spyderlib/plugins/workingdirectory.py:82 -#: spyderlib/widgets/explorer.py:643 spyderlib/widgets/explorer.py:650 -msgid "New file" -msgstr "Nuevo archivo" - -#: spyderlib/plugins/editor.py:605 -msgid "&Open..." -msgstr "&Abrir" - -#: spyderlib/plugins/editor.py:606 spyderlib/plugins/editor.py:1647 -#: spyderlib/plugins/workingdirectory.py:69 -msgid "Open file" -msgstr "Abrir archivo" - -#: spyderlib/plugins/editor.py:613 -msgid "&Revert" -msgstr "&Restaurar" - -#: spyderlib/plugins/editor.py:614 -msgid "Revert file from disk" -msgstr "Restaurar archivo desde el disco" - -#: spyderlib/plugins/editor.py:617 -msgid "&Save" -msgstr "&Guardar" - -#: spyderlib/plugins/editor.py:618 -msgid "Save file" -msgstr "Guardar archivo" - -#: spyderlib/plugins/editor.py:625 -msgid "Sav&e all" -msgstr "Guardar t&odo" - -#: spyderlib/plugins/editor.py:626 -msgid "Save all files" -msgstr "Guardar todos los archivos" - -#: spyderlib/plugins/editor.py:633 -msgid "Save &as..." -msgstr "Gu&ardar como..." - -#: spyderlib/plugins/editor.py:634 -msgid "Save current file as..." -msgstr "Guardar el archivo actual como..." - -#: spyderlib/plugins/editor.py:636 spyderlib/plugins/editor.py:637 -msgid "Print preview..." -msgstr "Presentación preliminar..." - -#: spyderlib/plugins/editor.py:638 -msgid "&Print..." -msgstr "Im&primir" - -#: spyderlib/plugins/editor.py:639 -msgid "Print current file..." -msgstr "Imprimir el archivo actual..." - -#: spyderlib/plugins/editor.py:644 -msgid "&Close" -msgstr "&Cerrar" - -#: spyderlib/plugins/editor.py:645 -msgid "Close current file" -msgstr "Cerrar el archivo actual" - -#: spyderlib/plugins/editor.py:647 -msgid "C&lose all" -msgstr "C&errar todo" - -#: spyderlib/plugins/editor.py:648 -msgid "Close all opened files" -msgstr "Cerrar todos los archivos abiertos" - -#: spyderlib/plugins/editor.py:655 -msgid "Set/Clear breakpoint" -msgstr "Añadir o eliminar un punto de interrupción" - -#: spyderlib/plugins/editor.py:662 -msgid "Set/Edit conditional breakpoint" -msgstr "Añadir o editar un punto de interrupción condicional" - -#: spyderlib/plugins/editor.py:669 -msgid "Clear breakpoints in all files" -msgstr "Eliminar los puntos de interrupción de todos los archivos" - -#: spyderlib/plugins/editor.py:671 -msgid "Breakpoints" -msgstr "Puntos de interrupción (Breakpoints)" - -#: spyderlib/plugins/editor.py:675 -msgid "Debug with winpdb" -msgstr "Depurar con winpdb" - -#: spyderlib/plugins/editor.py:682 spyderlib/spyder.py:575 -msgid "&Debug" -msgstr "&Depurar" - -#: spyderlib/plugins/editor.py:683 -msgid "Debug file" -msgstr "Depurar archivo" - -#: spyderlib/plugins/editor.py:688 -msgid "Step" -msgstr "Ejecutar línea" - -#: spyderlib/plugins/editor.py:689 -msgid "Run current line" -msgstr "Ejecutar la línea seleccionada" - -#: spyderlib/plugins/editor.py:695 -msgid "Continue" -msgstr "Continuar" - -#: spyderlib/plugins/editor.py:696 -msgid "Continue execution until next breakpoint" -msgstr "Continuar con la ejecución hasta el siguiente punto de interrupción" - -#: spyderlib/plugins/editor.py:703 -msgid "Step Into" -msgstr "Ingresar en la función/método" - -#: spyderlib/plugins/editor.py:704 -msgid "Step into function or method of current line" -msgstr "Ingresar en la función o método de la línea actual" - -#: spyderlib/plugins/editor.py:711 -msgid "Step Return" -msgstr "Salir de la función/método" - -#: spyderlib/plugins/editor.py:712 -msgid "Run until current function or method returns" -msgstr "Ejecutar hasta que la función o método actual termine" - -#: spyderlib/plugins/editor.py:719 -msgid "Exit" -msgstr "Terminar" - -#: spyderlib/plugins/editor.py:720 -msgid "Exit Debug" -msgstr "Terminar depuración" - -#: spyderlib/plugins/editor.py:731 -msgid "Debugging control" -msgstr "Control de depuración" - -#: spyderlib/plugins/editor.py:735 spyderlib/plugins/editor.py:1246 -#: spyderlib/spyder.py:570 -msgid "&Run" -msgstr "E&jecutar" - -#: spyderlib/plugins/editor.py:736 -msgid "Run file" -msgstr "Ejecutar archivo" - -#: spyderlib/plugins/editor.py:742 -msgid "&Configure..." -msgstr "&Configurar..." - -#: spyderlib/plugins/editor.py:743 -#: spyderlib/widgets/externalshell/pythonshell.py:294 -msgid "Run settings" -msgstr "Ajustes de ejecución" - -#: spyderlib/plugins/editor.py:752 -msgid "Re-run &last script" -msgstr "Ejecutar de &nuevo el último archivo" - -#: spyderlib/plugins/editor.py:753 -msgid "Run again last file" -msgstr "Ejecutar de nuevo el mismo archivo" - -#: spyderlib/plugins/editor.py:760 -#: spyderlib/widgets/sourcecode/codeeditor.py:2305 -msgid "Run &selection or current line" -msgstr "Ejecutar la &selección o la línea actual" - -#: spyderlib/plugins/editor.py:763 -msgid "Run selection or current line" -msgstr "Ejecutar la &selección o línea actual" - -#: spyderlib/plugins/editor.py:776 -msgid "Run cell" -msgstr "Ejecutar la celda" - -#: spyderlib/plugins/editor.py:778 -msgid "" -"Run current cell (Ctrl+Enter)\n" -"[Use #%% to create cells]" -msgstr "" -"Ejecutar la celda actual (Ctrl+Enter)\n" -"[Usar #%% para crear celdas]" - -#: spyderlib/plugins/editor.py:783 -msgid "Run cell and advance" -msgstr "Ejecutar la celda y avanzar" - -#: spyderlib/plugins/editor.py:786 -msgid "Run current cell and go to the next one (Shift+Enter)" -msgstr "Ejecutar la celda actual y avanzar a la siguiente (Shift+Enter)" - -#: spyderlib/plugins/editor.py:792 -msgid "Show todo list" -msgstr "Mostrar lista de TODO's" - -#: spyderlib/plugins/editor.py:793 -msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" -msgstr "" -"Mostrar la lista de comentarios de\n" -"los TODO/FIXME/XXX/HINT/TIP/@todo" - -#: spyderlib/plugins/editor.py:801 -msgid "Show warning/error list" -msgstr "" -"Mostrar la lista de errores\n" -"y advertencias" - -#: spyderlib/plugins/editor.py:802 -msgid "Show code analysis warnings/errors" -msgstr "" -"Mostrar errores o advertencias\n" -"del análisis del código" - -#: spyderlib/plugins/editor.py:809 -msgid "Previous warning/error" -msgstr "Anterior advertencia o error" - -#: spyderlib/plugins/editor.py:810 -msgid "Go to previous code analysis warning/error" -msgstr "" -"Ir a la línea anterior de\n" -"advertencia o error" - -#: spyderlib/plugins/editor.py:813 -msgid "Next warning/error" -msgstr "Siguiente advertencia o error" - -#: spyderlib/plugins/editor.py:814 -msgid "Go to next code analysis warning/error" -msgstr "" -"Ir a la próxima línea de\n" -"advertencia o error" - -#: spyderlib/plugins/editor.py:818 -msgid "Last edit location" -msgstr "Última posición de edición" - -#: spyderlib/plugins/editor.py:819 -msgid "Go to last edit location" -msgstr "" -"Ir a la anterior posición\n" -"de edición" - -#: spyderlib/plugins/editor.py:825 -msgid "Previous cursor position" -msgstr "Anterior posición del cursor" - -#: spyderlib/plugins/editor.py:826 -msgid "Go to previous cursor position" -msgstr "Ir a la anterior posición del cursor" - -#: spyderlib/plugins/editor.py:832 -msgid "Next cursor position" -msgstr "Siguiente posición del cursor" - -#: spyderlib/plugins/editor.py:833 -msgid "Go to next cursor position" -msgstr "Ir a la siguiente posición del cursor" - -#: spyderlib/plugins/editor.py:840 -#: spyderlib/widgets/sourcecode/codeeditor.py:2292 -msgid "Comment" -msgstr "Comentar" - -#: spyderlib/plugins/editor.py:840 -#: spyderlib/widgets/sourcecode/codeeditor.py:2292 -msgid "Uncomment" -msgstr "Descomentar" - -#: spyderlib/plugins/editor.py:841 -msgid "Comment current line or selection" -msgstr "Comentar la línea o selección actual" - -#: spyderlib/plugins/editor.py:845 -msgid "Add &block comment" -msgstr "Añadir comentario de &bloque" - -#: spyderlib/plugins/editor.py:846 -msgid "Add block comment around current line or selection" -msgstr "" -"Añadir un comentario de bloque alrededor de la línea o selección actual" - -#: spyderlib/plugins/editor.py:852 -msgid "R&emove block comment" -msgstr "&Eliminar comentario de bloque" - -#: spyderlib/plugins/editor.py:853 -msgid "Remove comment block around current line or selection" -msgstr "Eliminar comentario de bloque alrededor de la línea o selección actual" - -#: spyderlib/plugins/editor.py:864 -msgid "Indent" -msgstr "Indentar" - -#: spyderlib/plugins/editor.py:865 -msgid "Indent current line or selection" -msgstr "Indentar la línea o selección actual" - -#: spyderlib/plugins/editor.py:868 -msgid "Unindent" -msgstr "Quitar indentación" - -#: spyderlib/plugins/editor.py:869 -msgid "Unindent current line or selection" -msgstr "Quitar indentación de la línea o selección actual" - -#: spyderlib/plugins/editor.py:874 -msgid "Carriage return and line feed (Windows)" -msgstr "Retorno de carro y salto de línea (Windows)" - -#: spyderlib/plugins/editor.py:877 -msgid "Line feed (UNIX)" -msgstr "Salto de línea (UNIX)" - -#: spyderlib/plugins/editor.py:880 -msgid "Carriage return (Mac)" -msgstr "Retorno de carro (Mac)" - -#: spyderlib/plugins/editor.py:886 -msgid "Convert end-of-line characters" -msgstr "Convertir caracteres de fin de línea" - -#: spyderlib/plugins/editor.py:890 -msgid "Remove trailing spaces" -msgstr "Eliminar espacios en blanco" - -#: spyderlib/plugins/editor.py:894 -msgid "Fix indentation" -msgstr "Corregir la indentación" - -#: spyderlib/plugins/editor.py:895 -msgid "Replace tab characters by space characters" -msgstr "Reemplazar caracteres de tabulación por espacios" - -#: spyderlib/plugins/editor.py:898 -msgid "Go to line..." -msgstr "Ir a la línea..." - -#: spyderlib/plugins/editor.py:906 -msgid "Set console working directory" -msgstr "Establecer directorio de trabajo" - -#: spyderlib/plugins/editor.py:908 -msgid "" -"Set current console (and file explorer) working directory to current script " -"directory" -msgstr "" -"Fija el directorio de trabajo para la terminal actual como el directorio del " -"archivo actual" - -#: spyderlib/plugins/editor.py:913 -msgid "Maximum number of recent files..." -msgstr "Máximo número de archivos recientes..." - -#: spyderlib/plugins/editor.py:916 -msgid "Clear recent files list" -msgstr "Limpiar la lista de archivos recientes" - -#: spyderlib/plugins/editor.py:916 -msgid "Clear this list" -msgstr "Limpiar esta lista" - -#: spyderlib/plugins/editor.py:918 -msgid "Open &recent" -msgstr "Abrir &reciente" - -#: spyderlib/plugins/editor.py:1234 spyderlib/spyder.py:551 -msgid "File toolbar" -msgstr "Barra de archivo" - -#: spyderlib/plugins/editor.py:1235 spyderlib/spyder.py:561 -msgid "Search toolbar" -msgstr "Barra de búsqueda" - -#: spyderlib/plugins/editor.py:1236 spyderlib/spyder.py:566 -msgid "Source toolbar" -msgstr "Barra de código fuente" - -#: spyderlib/plugins/editor.py:1237 spyderlib/spyder.py:571 -msgid "Run toolbar" -msgstr "Barra de ejecución" - -#: spyderlib/plugins/editor.py:1238 spyderlib/spyder.py:576 -msgid "Debug toolbar" -msgstr "Barra de depuración" - -#: spyderlib/plugins/editor.py:1239 spyderlib/spyder.py:556 -msgid "Edit toolbar" -msgstr "Barra de edición" - -#: spyderlib/plugins/editor.py:1242 spyderlib/spyder.py:548 -msgid "&File" -msgstr "&Archivo" - -#: spyderlib/plugins/editor.py:1243 spyderlib/spyder.py:555 -msgid "&Edit" -msgstr "&Editar" - -#: spyderlib/plugins/editor.py:1244 spyderlib/spyder.py:560 -msgid "&Search" -msgstr "&Buscar" - -#: spyderlib/plugins/editor.py:1245 spyderlib/spyder.py:565 -msgid "Sour&ce" -msgstr "&Código fuente" - -#: spyderlib/plugins/editor.py:1247 spyderlib/spyder.py:583 -msgid "&Tools" -msgstr "&Herramientas" - -#: spyderlib/plugins/editor.py:1248 -msgid "?" -msgstr "?" - -#: spyderlib/plugins/editor.py:1469 -msgid "Spyder Editor" -msgstr "Editor de Spyder" - -#: spyderlib/plugins/editor.py:1470 -msgid "This is a temporary script file." -msgstr "Este es un archivo temporal" - -#: spyderlib/plugins/editor.py:1536 -msgid "untitled" -msgstr "Sin título " - -#: spyderlib/plugins/editor.py:1607 -msgid "Maximum number of recent files" -msgstr "Máximo número de archivos recientes" - -#: spyderlib/plugins/editor.py:1729 -msgid "Printing..." -msgstr "Imprimir..." - -#: spyderlib/plugins/explorer.py:45 -msgid "File explorer" -msgstr "Explorador de archivos" - -#: spyderlib/plugins/explorer.py:58 spyderlib/plugins/inspector.py:373 -#: spyderlib/plugins/projectexplorer.py:57 -msgid "Set font style" -msgstr "Establece el tipo de fuente" - -#: spyderlib/plugins/externalconsole.py:46 -msgid "Interactive data plotting in the consoles" -msgstr "Graficar datos interactivamente en la terminal" - -#: spyderlib/plugins/externalconsole.py:53 -#: spyderlib/plugins/externalconsole.py:1066 -#: spyderlib/plugins/inspector.py:403 spyderlib/plugins/runconfig.py:178 -#: spyderlib/plugins/runconfig.py:447 -#: spyderlib/widgets/externalshell/baseshell.py:106 -#: spyderlib/widgets/ipython.py:509 -msgid "Console" -msgstr "Terminal" - -#: spyderlib/plugins/externalconsole.py:69 -msgid "One tab per script" -msgstr "Una pestaña por archivo" - -#: spyderlib/plugins/externalconsole.py:70 -#: spyderlib/widgets/externalshell/baseshell.py:171 -msgid "Show elapsed time" -msgstr "Mostrar el tiempo transcurrido" - -#: spyderlib/plugins/externalconsole.py:71 spyderlib/widgets/explorer.py:988 -msgid "Show icons and text" -msgstr "Mostrar iconos y texto " - -#: spyderlib/plugins/externalconsole.py:83 -msgid "Buffer: " -msgstr "Mostrar:" - -#: spyderlib/plugins/externalconsole.py:83 -#: spyderlib/plugins/ipythonconsole.py:201 -msgid " lines" -msgstr "líneas" - -#: spyderlib/plugins/externalconsole.py:88 -msgid "Merge process standard output/error channels" -msgstr "Combinar los canales de salida y error estándar del proceso" - -#: spyderlib/plugins/externalconsole.py:90 -msgid "" -"Merging the output channels of the process means that\n" -"the standard error won't be written in red anymore,\n" -"but this has the effect of speeding up display." -msgstr "" -"Combinar los canales de salida del proceso quiere decir que\n" -"el error estándar no será escrito en rojo , pero esto ayuda a\n" -"mejorar la velocidad en que aparece el texto en la terminal." - -#: spyderlib/plugins/externalconsole.py:94 -msgid "Colorize standard error channel using ANSI escape codes" -msgstr "Colorear el canal de error estándar usando códigos de escape ANSI " - -#: spyderlib/plugins/externalconsole.py:96 -msgid "" -"This method is the only way to have colorized standard\n" -"error channel when the output channels have been merged." -msgstr "" -"Éste método es la única forma de darle color al canal de error\n" -"estándar cuando los canales de salida han sido combinados." - -#: spyderlib/plugins/externalconsole.py:114 -#: spyderlib/plugins/ipythonconsole.py:188 -#: spyderlib/widgets/arrayeditor.py:460 -#: spyderlib/widgets/dataframeeditor.py:515 -msgid "Background color" -msgstr "Color de fondo" - -#: spyderlib/plugins/externalconsole.py:115 -msgid "" -"This option will be applied the next time a Python console or a terminal is " -"opened." -msgstr "" -"Esta opción será aplicada la próxima vez que una terminal de Python o una " -"consola sea abierta." - -#: spyderlib/plugins/externalconsole.py:118 -msgid "Light background (white color)" -msgstr "Fondo claro (color blanco)" - -#: spyderlib/plugins/externalconsole.py:143 -msgid "User Module Reloader (UMR)" -msgstr "Recargador de Módulos del Usuario (RMU)" - -#: spyderlib/plugins/externalconsole.py:144 -msgid "" -"UMR forces Python to reload modules which were imported when executing a \n" -"script in the external console with the 'runfile' function." -msgstr "" -"El RMU obliga a Python a recargar los módulos que fueron importados\n" -"durante la ejecución de un archivo en la terminal con la función 'runfile'." - -#: spyderlib/plugins/externalconsole.py:147 -msgid "Enable UMR" -msgstr "Activar el RMU" - -#: spyderlib/plugins/externalconsole.py:148 -msgid "" -"This option will enable the User Module Reloader (UMR) in Python/IPython " -"consoles. UMR forces Python to reload deeply modules during import when " -"running a Python script using the Spyder's builtin function runfile." -"

    1. UMR may require to restart the console in which it will be " -"called (otherwise only newly imported modules will be reloaded when " -"executing scripts).

    2. If errors occur when re-running a PyQt-" -"based program, please check that the Qt objects are properly destroyed (e.g. " -"you may have to use the attribute Qt.WA_DeleteOnClose on your main " -"window, using the setAttribute method)" -msgstr "" -"Esta opción activará el Recargador de Módulos del Usuario (RMU) en las " -"terminales de Python y IPython. El RMU obliga a Python a recargar " -"profundamente los módulos que fueron importados al ejecutar un archivo de " -"Python, usando la función incorporada de Spyder llamada runfile." -"

    1. El RMU puede requerir que se reinicie la terminal en la " -"cual será utilizado (de otra forma, sólo los módulos que fueron importados " -"en último lugar serán recargados al ejecutar un archivo).

    2. " -"Si ocurre algún error al re-ejecutar programas basados en PyQt o PySide, por " -"favor verifique que los objetos de Qt sean destruidos apropiadamente (por " -"ejemplo, puede tener que usar el atributo Qt.WA_DeleteOnClose en su " -"ventana principal, utilizando para ello el método setAttribute)." - -#: spyderlib/plugins/externalconsole.py:164 -msgid "Show reloaded modules list" -msgstr "Mostrar la lista de módulos que fueron recargados" - -#: spyderlib/plugins/externalconsole.py:165 -msgid "Please note that these changes will be applied only to new consoles" -msgstr "" -"Por favor tenga en cuenta que estos cambios sólo se aplicarán a nuevas " -"terminales" - -#: spyderlib/plugins/externalconsole.py:169 -msgid "Set UMR excluded (not reloaded) modules" -msgstr "Establecer la lista de módulos excluidos por el RMU" - -#: spyderlib/plugins/externalconsole.py:181 -msgid "Python executable" -msgstr "Archivo ejecutable de Python" - -#: spyderlib/plugins/externalconsole.py:183 -msgid "" -"Select the Python interpreter executable binary in which Spyder will run " -"scripts:" -msgstr "Seleccionar la ruta al intérprete de Python que desee usar:" - -#: spyderlib/plugins/externalconsole.py:186 -msgid "Default (i.e. the same as Spyder's)" -msgstr "Por defecto (es decir, el mismo de Spyder)" - -#: spyderlib/plugins/externalconsole.py:190 -msgid "Use the following Python interpreter:" -msgstr "Usar el siguiente intérprete:" - -#: spyderlib/plugins/externalconsole.py:194 -msgid "Executables" -msgstr "Ejecutables" - -#: spyderlib/plugins/externalconsole.py:214 -msgid "PYTHONSTARTUP replacement" -msgstr "Reemplazo de PYTHONSTARTUP" - -#: spyderlib/plugins/externalconsole.py:216 -msgid "" -"This option will override the PYTHONSTARTUP environment variable which\n" -"defines the script to be executed during the Python console startup." -msgstr "" -"Esta opción modificará la variable de entorno PYTHONSTARTUP, la cual\n" -"define el archivo que es ejecutado durante el arranque de la terminal de\n" -"Python." - -#: spyderlib/plugins/externalconsole.py:221 -msgid "Default PYTHONSTARTUP script" -msgstr "Utilizar el archivo por defecto de PYTHONSTARTUP" - -#: spyderlib/plugins/externalconsole.py:225 -msgid "Use the following startup script:" -msgstr "Utilizar el siguiente archivo de arranque:" - -#: spyderlib/plugins/externalconsole.py:244 -msgid "Monitor" -msgstr "Monitor" - -#: spyderlib/plugins/externalconsole.py:245 -msgid "" -"The monitor provides introspection features to console: code completion, " -"calltips and variable explorer. Because it relies on several modules, " -"disabling the monitor may be useful to accelerate console startup." -msgstr "" -"El monitor es el que brinda características de introspección a la terminal: " -"completado del código, globos de sugerencias y el explorador de variables. " -"Dado que depende de varios módulos adicionales, desactivar el monitor puede " -"acelerar el arranque de la terminal." - -#: spyderlib/plugins/externalconsole.py:252 -msgid "Enable monitor" -msgstr "Activar el monitor" - -#: spyderlib/plugins/externalconsole.py:264 -msgid "Default library" -msgstr "Librería por defecto" - -#: spyderlib/plugins/externalconsole.py:266 -msgid "Qt (PyQt/PySide)" -msgstr "Qt (PyQt/PySide)" - -#: spyderlib/plugins/externalconsole.py:268 -msgid "Qt-Python bindings library selection:" -msgstr "Selección de la librería de enlace entre Qt y Python:" - -#: spyderlib/plugins/externalconsole.py:270 -msgid "" -"This option will act on
    libraries such as Matplotlib, guidata or ETS" -msgstr "" -"Esta opción tendrá efecto
    en librerías como Matplotlib, guidata o ETS" - -#: spyderlib/plugins/externalconsole.py:291 -msgid "PyQt" -msgstr "PyQt" - -#: spyderlib/plugins/externalconsole.py:293 -msgid "API selection for QString and QVariant objects:" -msgstr "Selección del API para objetos QString and QVariant:" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "API #1" -msgstr "API #1" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "API #2" -msgstr "API #2" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "Default API" -msgstr "API por defecto" - -#: spyderlib/plugins/externalconsole.py:296 -msgid "" -"PyQt API #1 is the default
    API for Python 2. PyQt API #2 is the default " -"API for Python 3 and is compatible with PySide." -msgstr "" -"El API #1 de PyQt es el API por
    defecto para Python 2. El API #2 de PyQt " -"es el API por defecto para Python 3 y es compatible con PySide." - -#: spyderlib/plugins/externalconsole.py:300 -msgid "Ignore API change errors (sip.setapi)" -msgstr "Ignorar los errores de cambio de API (sip.setapi)" - -#: spyderlib/plugins/externalconsole.py:302 -msgid "" -"Enabling this option will ignore
    errors when changing PyQt API. As PyQt " -"does not support dynamic API changes, it is strongly recommended to use this " -"feature wisely, e.g. for debugging purpose." -msgstr "" -"Al activar esta opción se ignorarán
    los errores generados al cambiar de " -"API de PyQt. Dado que PyQt no soporta los cambios dinámicos de API, se " -"recomienda usar esta característica con sumo cuidado, por ejemplo, para " -"propósitos de depuración." - -#: spyderlib/plugins/externalconsole.py:321 -msgid "Matplotlib" -msgstr "Matplotlib" - -#: spyderlib/plugins/externalconsole.py:323 -msgid "GUI backend:" -msgstr "Salida gráfica:" - -#: spyderlib/plugins/externalconsole.py:325 -msgid "" -"Set the GUI toolkit used by
    Matplotlib to show figures (default: Qt4Agg)" -msgstr "" -"Establecer la librería gráfica
    utilizada para generar las gráficas de " -"Matplotlib (por defecto: Qt4Agg)" - -#: spyderlib/plugins/externalconsole.py:344 -msgid "Enthought Tool Suite" -msgstr "Enthought Tool Suite" - -#: spyderlib/plugins/externalconsole.py:345 -msgid "" -"Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical " -"user interfaces." -msgstr "" -"Enthought Tool Suite (ETS) funciona con las librerías gráficas PyQt4 (qt4) " -"y \n" -"wxPython (wx). Esta opción establece cual desea utilizar el usuario." - -#: spyderlib/plugins/externalconsole.py:349 -msgid "ETS_TOOLKIT:" -msgstr "ETS_TOOLKIT:" - -#: spyderlib/plugins/externalconsole.py:369 -msgid "External modules" -msgstr "Módulos externos" - -#: spyderlib/plugins/externalconsole.py:426 -#: spyderlib/plugins/externalconsole.py:666 -#: spyderlib/plugins/ipythonconsole.py:113 -#: spyderlib/plugins/ipythonconsole.py:808 spyderlib/spyder.py:1331 -#: spyderlib/spyder.py:1349 spyderlib/utils/environ.py:94 -#: spyderlib/utils/environ.py:107 spyderlib/widgets/dicteditor.py:449 -msgid "Warning" -msgstr "Advertencia" - -#: spyderlib/plugins/externalconsole.py:427 -msgid "" -"You selected a Python %d interpreter for the console but Spyder is " -"running on Python %d!.

    Although this is possible, we recommend " -"you to install and run Spyder directly with your selected interpreter, to " -"avoid seeing false warnings and errors due to the incompatible syntax " -"between these two Python versions." -msgstr "" -"Usted seleccionó un intérprete de Python %d para la terminal, pero " -"Spyder está corriendo bajo Python %d!.

    Aunque esto es posible, " -"le recomendamos instalar y correr Spyder directamente con el intérprete " -"seleccionado, para evitar ver falsos errores y alarmas en el Editor debido a " -"la sintaxis incompatible entre estas dos versiones de Python." - -#: spyderlib/plugins/externalconsole.py:590 -msgid "Trying to kill a kernel?" -msgstr "Desea cerrar un núcleo?" - -#: spyderlib/plugins/externalconsole.py:591 -msgid "" -"You can't close this kernel because it has one or more consoles connected to " -"it.

    You need to close them instead or you can kill the kernel using " -"the second button from right to left." -msgstr "" -"No puede cerrar este núcleo porque tiene una o más terminales conectadas a " -"él.

    Debe cerrarlas previamente o puede terminar el proceso usando el " -"segundo botón ubicado de derecha a izquierda." - -#: spyderlib/plugins/externalconsole.py:667 -msgid "" -"No Python console is currently selected to run %s.

    Please " -"select or open a new Python console and try again." -msgstr "" -"No existe una terminal de Python para ejecutar %s.

    Por favor " -"abra una nueva y pruebe otra vez." - -#: spyderlib/plugins/externalconsole.py:748 -msgid "" -"%s is already running in a separate process.\n" -"Do you want to kill the process before starting a new one?" -msgstr "" -"%s ya se está ejecutando en proceso aparte.\n" -"¿Desea terminar este proceso antes de empezar uno nuevo?" - -#: spyderlib/plugins/externalconsole.py:917 -msgid "Kernel" -msgstr "Núcleo" - -#: spyderlib/plugins/externalconsole.py:929 -msgid "" -"Either:
    1. Your IPython frontend and kernel versions are " -"incompatible or
    2. You don't have IPython installed in " -"your external interpreter.
    In any case, we're sorry but we can't " -"create a console for you." -msgstr "" -"O bien:
    1. Sus versiones del núcleo y la interfaz gráfica de IPython son " -"incompatibles o
    2. Usted no tiene IPython instalado en su " -"intérprete externo.
    Lo lamentamos, pero en cualquier caso no " -"podemos crear una terminal de IPython para usted." - -#: spyderlib/plugins/externalconsole.py:953 -msgid "Command Window" -msgstr "Símbolo" - -#: spyderlib/plugins/externalconsole.py:955 -msgid "Terminal" -msgstr "Terminal" - -#: spyderlib/plugins/externalconsole.py:1008 -msgid "Kernel %s" -msgstr "Núcleo %s" - -#: spyderlib/plugins/externalconsole.py:1088 -msgid "Open a &Python console" -msgstr "Abrir una terminal de Python" - -#: spyderlib/plugins/externalconsole.py:1091 -msgid "Open &command prompt" -msgstr "Abrir &símbolo del sistema" - -#: spyderlib/plugins/externalconsole.py:1092 -msgid "Open a Windows command prompt" -msgstr "Abre el símbolo del sistema de Windows" - -#: spyderlib/plugins/externalconsole.py:1094 -msgid "Open a &terminal" -msgstr "Abrir &terminal de comandos" - -#: spyderlib/plugins/externalconsole.py:1095 -msgid "Open a terminal window" -msgstr "Abre una terminal del sistema" - -#: spyderlib/plugins/externalconsole.py:1263 -msgid "Open an IPython console" -msgstr "Abrir una terminal de IPython" - -#: spyderlib/plugins/externalconsole.py:1264 -msgid "" -"The console monitor was disabled: the IPython kernel will be started as " -"expected, but an IPython console will have to be connected manually to the " -"kernel." -msgstr "" -"El monitor está desactivado, por tanto se creará un kernel de IPython pero " -"usted deberá conectar manualmente un intérprete al mismo." - -#: spyderlib/plugins/externalconsole.py:1294 -#: spyderlib/plugins/externalconsole.py:1307 -#: spyderlib/plugins/externalconsole.py:1311 -msgid "UMR" -msgstr "RMU" - -#: spyderlib/plugins/externalconsole.py:1295 -msgid "" -"UMR excluded modules:\n" -"(example: guidata, guiqwt)" -msgstr "" -"Módulos excluidos del RMU:\n" -"(por ejemplo: guidata, guiqwt)" - -#: spyderlib/plugins/externalconsole.py:1308 -msgid "" -"The following modules are not installed on your machine:\n" -"%s" -msgstr "" -"Los siguientes módulos no están instalados en su computador:\n" -"%s" - -#: spyderlib/plugins/externalconsole.py:1312 -msgid "" -"Please note that these changes will be applied only to new Python/IPython " -"consoles" -msgstr "" -"Por favor tenga en cuenta que estos cambios sólo se aplicarán a nuevas " -"terminales de IPython y Python" - -#: spyderlib/plugins/findinfiles.py:90 spyderlib/widgets/findinfiles.py:691 -msgid "Find in files" -msgstr "Buscar en archivos" - -#: spyderlib/plugins/findinfiles.py:114 -msgid "&Find in files" -msgstr "Bus&car en archivos" - -#: spyderlib/plugins/findinfiles.py:117 -msgid "Search text in multiple files" -msgstr "Buscar en varios archivos a la vez" - -#: spyderlib/plugins/history.py:36 -msgid "Settings" -msgstr "Ajustes" - -#: spyderlib/plugins/history.py:38 -msgid " entries" -msgstr "entradas" - -#: spyderlib/plugins/history.py:38 -msgid "History depth: " -msgstr "Longitud del historial" - -#: spyderlib/plugins/history.py:45 -msgid "Scroll automatically to last entry" -msgstr "Desplazarse automáticamente a la última entrada" - -#: spyderlib/plugins/history.py:113 spyderlib/plugins/inspector.py:458 -#: spyderlib/widgets/editor.py:540 spyderlib/widgets/explorer.py:1018 -#: spyderlib/widgets/externalshell/baseshell.py:151 -#: spyderlib/widgets/externalshell/namespacebrowser.py:226 -#: spyderlib/widgets/ipython.py:556 -msgid "Options" -msgstr "Opciones" - -#: spyderlib/plugins/history.py:133 -msgid "History log" -msgstr "Historial de comandos" - -#: spyderlib/plugins/history.py:160 -msgid "History..." -msgstr "Historial..." - -#: spyderlib/plugins/history.py:162 -msgid "Set history maximum entries" -msgstr "Establece el máximo número de entradas a almacenar" - -#: spyderlib/plugins/history.py:272 -msgid "History" -msgstr "Historial" - -#: spyderlib/plugins/history.py:273 -msgid "Maximum entries" -msgstr "Máximo número de entradas" - -#: spyderlib/plugins/inspector.py:56 -msgid "Rich text help on the Object Inspector" -msgstr "Ayuda en texto enriquecido en el Inspector de Objetos" - -#: spyderlib/plugins/inspector.py:120 -msgid "Plain text font style" -msgstr "Fuente para el texto plano" - -#: spyderlib/plugins/inspector.py:123 -msgid "Rich text font style" -msgstr "Fuente para el texto enriquecido" - -#: spyderlib/plugins/inspector.py:126 -msgid "Automatic connections" -msgstr "Conexiones automáticas" - -#: spyderlib/plugins/inspector.py:127 -msgid "" -"The Object Inspector can automatically show an object's help information " -"after a left parenthesis is written next to it. Below you can decide to " -"which plugin you want to connect it to turn on this feature." -msgstr "" -"El Inspector de Objetos puede mostrar automáticamente la ayuda de un objeto " -"después de escribir un paréntesis junto al mismo. A continuación puede " -"decidir a que panel desea conectarlo para activar esta característica." - -#: spyderlib/plugins/inspector.py:139 -msgid "" -"This feature requires the Rope or Jedi libraries.\n" -"It seems you don't have either installed." -msgstr "" -"Esta característica requiere las librerías Rope o Jedi.\n" -"Al parecer no tiene ninguna instalada." - -#: spyderlib/plugins/inspector.py:142 -msgid "Python Console" -msgstr "Terminal de IPython" - -#: spyderlib/plugins/inspector.py:144 -msgid "IPython Console" -msgstr "Terminal de IPython" - -#: spyderlib/plugins/inspector.py:156 -msgid "Additional features" -msgstr "Características adicionales" - -#: spyderlib/plugins/inspector.py:157 -msgid "Render mathematical equations" -msgstr "Renderizar ecuaciones matemáticas" - -#: spyderlib/plugins/inspector.py:163 -msgid "This feature requires Sphinx 1.1 or superior." -msgstr "Esta característica requiere Sphinx 1.1 o superior." - -#: spyderlib/plugins/inspector.py:165 -msgid "Sphinx %s is currently installed." -msgstr "Sphinx %s está instalado actualmente." - -#: spyderlib/plugins/inspector.py:357 -msgid "No further documentation available" -msgstr "No existe más documentación disponible" - -#: spyderlib/plugins/inspector.py:396 -msgid "Source" -msgstr "Origen" - -#: spyderlib/plugins/inspector.py:412 spyderlib/widgets/dicteditor.py:173 -msgid "Object" -msgstr "Objeto" - -#: spyderlib/plugins/inspector.py:428 -msgid "Plain Text" -msgstr "Texto plano" - -#: spyderlib/plugins/inspector.py:432 -msgid "Show Source" -msgstr "Mostrar código fuente" - -#: spyderlib/plugins/inspector.py:436 -msgid "Rich Text" -msgstr "Texto enriquecido" - -#: spyderlib/plugins/inspector.py:446 -msgid "Automatic import" -msgstr "Importar automáticamente" - -#: spyderlib/plugins/inspector.py:506 spyderlib/plugins/inspector.py:954 -msgid "Object inspector" -msgstr "Inspector de objetos" - -#: spyderlib/plugins/inspector.py:725 -msgid "" -"Here you can get help of any object by pressing %s in front of it, either on " -"the Editor or the Console.%sHelp can also be shown automatically after " -"writing a left parenthesis next to an object. You can activate this behavior " -"in %s." -msgstr "" -"En este panel es posible obtener la ayuda de cualquier objeto al oprimir %s " -"estando al frente del mismo, bien sea en el Editor o en la Terminal.%sEsta " -"ayuda también se puede mostrar automáticamente después de escribir un " -"paréntesis junto a un objeto. Este comportamiento puede activarse en %s." - -#: spyderlib/plugins/inspector.py:731 -msgid "Preferences > Object Inspector" -msgstr "Preferencias > Inspector de objetos" - -#: spyderlib/plugins/inspector.py:733 -msgid "Usage" -msgstr "Uso" - -#: spyderlib/plugins/inspector.py:734 -msgid "New to Spyder? Read our" -msgstr "Nuevo en Spyder? Lee nuestro" - -#: spyderlib/plugins/inspector.py:735 -msgid "tutorial" -msgstr "tutorial" - -#: spyderlib/plugins/inspector.py:742 -msgid "" -"Please consider installing Sphinx to get documentation rendered in rich text." -msgstr "" -"Por favor considere instalar Sphinx para obtener la documentación en texto " -"enriquecido " - -#: spyderlib/plugins/inspector.py:913 -msgid "Lock" -msgstr "Bloquear" - -#: spyderlib/plugins/inspector.py:913 -msgid "Unlock" -msgstr "Desbloquear" - -#: spyderlib/plugins/inspector.py:955 -msgid "" -"The following error occured when calling Sphinx %s.
    Incompatible " -"Sphinx version or doc string decoding failed.

    Error message:
    %s" -msgstr "" -"Ocurrió el siguiente error cuando se trató de utilizar Sphinx %s." -"
    Ello se debe a una versión incompatible de Sphinx o bien a que no fue " -"posible leer la documentación solicitada.

    Mensaje de error:
    %s" - -#: spyderlib/plugins/inspector.py:999 -msgid "No source code available." -msgstr "No está disponible el código fuente" - -#: spyderlib/plugins/ipythonconsole.py:61 -msgid "Symbolic mathematics in the IPython Console" -msgstr "Matemática simbólica en la terminal de IPython" - -#: spyderlib/plugins/ipythonconsole.py:110 -msgid "" -"The authenticity of host %s can't be established. Are you sure you " -"want to continue connecting?" -msgstr "" -"La autenticidad del servidor %s no puede ser establecida. ¿Está " -"seguro de que desea continuar conectándose?" - -#: spyderlib/plugins/ipythonconsole.py:122 -msgid "The authenticity of the host can't be established" -msgstr "La autenticidad del servidor no puede ser establecida" - -#: spyderlib/plugins/ipythonconsole.py:129 -msgid "Tunnel '%s' failed to start" -msgstr "El túnel '%s' falló en ser iniciado" - -#: spyderlib/plugins/ipythonconsole.py:134 -msgid "Could not connect to remote host" -msgstr "No fue posible conectarse al servidor remoto" - -#: spyderlib/plugins/ipythonconsole.py:150 -#: spyderlib/plugins/ipythonconsole.py:665 -msgid "IPython console" -msgstr "Terminal de IPython" - -#: spyderlib/plugins/ipythonconsole.py:162 -msgid "Display initial banner" -msgstr "Mostrar el banner inicial" - -#: spyderlib/plugins/ipythonconsole.py:163 -msgid "" -"This option lets you hide the message shown at\n" -"the top of the console when it's opened." -msgstr "" -"Esta opción le permite ocultar el mensaje que \n" -"aparece al principio de la terminal cuando se abre\n" -"por primera vez." - -#: spyderlib/plugins/ipythonconsole.py:165 -msgid "Use a completion widget" -msgstr "Usar un widget para completar texto" - -#: spyderlib/plugins/ipythonconsole.py:167 -msgid "Use a widget instead of plain text output for tab completion" -msgstr "" -"Puede decidir si usar un widget en lugar de texto plano \n" -"para mostrar las posibilidades de completado usando la\n" -"tecla Tab." - -#: spyderlib/plugins/ipythonconsole.py:169 -msgid "Use a pager to display additional text inside the console" -msgstr "Usar un paginador para mostrar textos dentro de la terminal" - -#: spyderlib/plugins/ipythonconsole.py:171 -msgid "" -"Useful if you don't want to fill the console with long help or completion " -"texts.\n" -"Note: Use the Q key to get out of the pager." -msgstr "" -"Es útil si no desea llenar la terminal con largos textos de ayuda.\n" -"Nota: Debe usar la tecla Q para salir del paginador" - -#: spyderlib/plugins/ipythonconsole.py:176 -msgid "Ask for confirmation before closing" -msgstr "Mostrar un diálogo de confirmación antes de cerrar una terminal" - -#: spyderlib/plugins/ipythonconsole.py:189 -msgid "Light background" -msgstr "Fondo claro" - -#: spyderlib/plugins/ipythonconsole.py:191 -msgid "Dark background" -msgstr "Fondo oscuro" - -#: spyderlib/plugins/ipythonconsole.py:201 -msgid "Buffer: " -msgstr "Mostrar" - -#: spyderlib/plugins/ipythonconsole.py:203 -msgid "" -"Set the maximum number of lines of text shown in the\n" -"console before truncation. Specifying -1 disables it\n" -"(not recommended!)" -msgstr "" -"Establece el máximo número de líneas que se mostrarán\n" -"en la terminal en cualquier momento. Si se introduce -1 se\n" -"mostrarán todas las líneas (no se recomienda!)" - -#: spyderlib/plugins/ipythonconsole.py:212 -msgid "Support for graphics (Matplotlib)" -msgstr "Soporte para crear gráficas (Matplotlib)" - -#: spyderlib/plugins/ipythonconsole.py:213 -msgid "Activate support" -msgstr "Activar el soporte" - -#: spyderlib/plugins/ipythonconsole.py:214 -msgid "Automatically load Pylab and NumPy modules" -msgstr "Cargar automáticamente los módulos de Pylab y NumPy" - -#: spyderlib/plugins/ipythonconsole.py:217 -msgid "" -"This lets you load graphics support without importing \n" -"the commands to do plots. Useful to work with other\n" -"plotting libraries different to Matplotlib or to develop \n" -"GUIs with Spyder." -msgstr "" -"Esto le permite cargar el soporte gráfico sin importar\n" -"los comandos para crear figuras. Es útil para trabajar con\n" -"otras librerías gráficas diferentes a Matplotlib o para\n" -"desarrollar interfaces gráficas con Spyder." - -#: spyderlib/plugins/ipythonconsole.py:236 -msgid "" -"This feature requires the Matplotlib library.\n" -"It seems you don't have it installed." -msgstr "" -"Esta característica requiere la librería Matplotlib.\n" -"Al parecer no la tiene instalada." - -#: spyderlib/plugins/ipythonconsole.py:241 -msgid "Inline" -msgstr "En línea" - -#: spyderlib/plugins/ipythonconsole.py:242 -msgid "Automatic" -msgstr "Automático" - -#: spyderlib/plugins/ipythonconsole.py:243 -msgid "Graphics backend" -msgstr "Salida gráfica:" - -#: spyderlib/plugins/ipythonconsole.py:244 -msgid "" -"Decide how graphics are going to be displayed in the console. If unsure, " -"please select %s to put graphics inside the console or %s to " -"interact with them (through zooming and panning) in a separate window." -msgstr "" -"Decidir como se mostrarán las gráficas en la terminal. Si no está seguro, " -"por favor seleccione %s para colocar las gráficas en la consola o " -"%s para interactuar con ellas (a través de acercamientos y paneos) en " -"una ventana aparte." - -#: spyderlib/plugins/ipythonconsole.py:264 -msgid "Backend:" -msgstr "Salida:" - -#: spyderlib/plugins/ipythonconsole.py:266 -msgid "This option will be applied the next time a console is opened." -msgstr "Esta opción será aplicada la próxima vez que una terminal sea abierta." - -#: spyderlib/plugins/ipythonconsole.py:278 -msgid "Inline backend" -msgstr "Salida en línea:" - -#: spyderlib/plugins/ipythonconsole.py:279 -msgid "Decide how to render the figures created by this backend" -msgstr "" -"Decida como renderizar las figuras creadas por este tipo de salida gráfica" - -#: spyderlib/plugins/ipythonconsole.py:283 -msgid "Format:" -msgstr "Formato:" - -#: spyderlib/plugins/ipythonconsole.py:286 -msgid "Resolution:" -msgstr "Resolución:" - -#: spyderlib/plugins/ipythonconsole.py:286 -msgid "dpi" -msgstr "dpi" - -#: spyderlib/plugins/ipythonconsole.py:288 -msgid "Only used when the format is PNG. Default is 72" -msgstr "Sólo se usa cuando el formato es PNG. Por defecto es 72." - -#: spyderlib/plugins/ipythonconsole.py:291 -msgid "Width:" -msgstr "Ancho:" - -#: spyderlib/plugins/ipythonconsole.py:291 -#: spyderlib/plugins/ipythonconsole.py:295 -msgid "inches" -msgstr "pulgadas" - -#: spyderlib/plugins/ipythonconsole.py:293 -msgid "Default is 6" -msgstr "Por defecto es 6" - -#: spyderlib/plugins/ipythonconsole.py:295 -msgid "Height:" -msgstr "Alto:" - -#: spyderlib/plugins/ipythonconsole.py:297 -msgid "Default is 4" -msgstr "Por defecto es 4" - -#: spyderlib/plugins/ipythonconsole.py:312 -msgid "Run code" -msgstr "Ejecutar código" - -#: spyderlib/plugins/ipythonconsole.py:313 -msgid "" -"You can run several lines of code when a console is started. Please " -"introduce each one separated by commas, for example:
    import os, import " -"sys" -msgstr "" -"Se pueden ejecutar varias líneas de código al abrir una terminal. Por favor " -"introduzca cada una separada por comas, por ejemplo:
    import os, import " -"sys" - -#: spyderlib/plugins/ipythonconsole.py:319 -msgid "Lines:" -msgstr "Líneas:" - -#: spyderlib/plugins/ipythonconsole.py:328 -msgid "Run a file" -msgstr "Ejecutar un archivo" - -#: spyderlib/plugins/ipythonconsole.py:329 -msgid "" -"You can also run a whole file at startup instead of just some lines (This is " -"similar to have a PYTHONSTARTUP file)." -msgstr "" -"También se puede ejecutar un archivo completo al inicio, en lugar de unas " -"pocas líneas (Esto es similar a tener un archivo PYTHONSTARTUP)." - -#: spyderlib/plugins/ipythonconsole.py:333 -msgid "Use the following file:" -msgstr "Usar el siguiente archivo:" - -#: spyderlib/plugins/ipythonconsole.py:348 -msgid "Greedy completion" -msgstr "Completado ambicioso" - -#: spyderlib/plugins/ipythonconsole.py:349 -msgid "" -"Enable Tab completion on elements of lists, results of function " -"calls, etc, without assigning them to a variable.
    For example, you " -"can get completions on things like li[0].<Tab> or ins." -"meth().<Tab>" -msgstr "" -"Habilita el completado usando la tecla Tab en elementos de listas, " -"resultados de llamadas de funciones, etc, sin asignarlos a una " -"variable.
    De esta forma se pueden obtener sugerencias de completado en " -"cosas como li[0].<Tab> o ins.meth().<Tab>" - -#: spyderlib/plugins/ipythonconsole.py:357 -msgid "Use the greedy completer" -msgstr "Usar el completado ambicioso" - -#: spyderlib/plugins/ipythonconsole.py:368 -msgid "Autocall" -msgstr "Autollamar" - -#: spyderlib/plugins/ipythonconsole.py:369 -msgid "" -"Autocall makes IPython automatically call any callable object even if you " -"didn't type explicit parentheses.
    For example, if you type str 43 " -"it becomes str(43) automatically." -msgstr "" -"Esta opción hace que IPython llame automáticamente cualquier objeto " -"\"llamable\" (callable) aún si no se escriben paréntesis a su alrededor." -"
    Por ejemplo, al escribir str 43, se convertirá automáticamente en " -"str(43)." - -#: spyderlib/plugins/ipythonconsole.py:376 -msgid "Smart" -msgstr "Inteligente" - -#: spyderlib/plugins/ipythonconsole.py:377 -msgid "Full" -msgstr "Total" - -#: spyderlib/plugins/ipythonconsole.py:378 -msgid "Off" -msgstr "Desactivado" - -#: spyderlib/plugins/ipythonconsole.py:380 -msgid "Autocall: " -msgstr "Autollamar:" - -#: spyderlib/plugins/ipythonconsole.py:381 -msgid "" -"On %s mode, Autocall is not applied if there are no arguments after " -"the callable. On %s mode, all callable objects are automatically " -"called (even if no arguments are present)." -msgstr "" -"En modo %s, Autollamar no se usa si no hay argumentos después del " -"objeto llamable. En modo %s, todos los objetos llamables son llamados " -"automáticamente (aún si no hay argumentos presentes)." - -#: spyderlib/plugins/ipythonconsole.py:393 -msgid "Symbolic Mathematics" -msgstr "Matemática simbólica" - -#: spyderlib/plugins/ipythonconsole.py:394 -msgid "" -"Perfom symbolic operations in the console (e.g. integrals, derivatives, " -"vector calculus, etc) and get the outputs in a beautifully printed style." -msgstr "" -"Realice operaciones simbólicas en la terminal (integrales, derivadas o " -"cálculo vectorial) y obtenga los resultados en un bello estilo impreso." - -#: spyderlib/plugins/ipythonconsole.py:399 -msgid "Use symbolic math" -msgstr "Usar matemática simbólica" - -#: spyderlib/plugins/ipythonconsole.py:400 -msgid "" -"This option loads the Sympy library to work with.
    Please refer to its " -"documentation to learn how to use it." -msgstr "" -"Esta opción carga la librería Sympy para trabajar
    con ella. Por favor lea " -"su documentación para aprender como usarla." - -#: spyderlib/plugins/ipythonconsole.py:413 -msgid "" -"This feature requires the Sympy library.\n" -"It seems you don't have it installed." -msgstr "" -"Esta característica requiere la librería Sympy.\n" -"Al parecer no la tiene instalada." - -#: spyderlib/plugins/ipythonconsole.py:418 -msgid "Prompts" -msgstr "Prompts" - -#: spyderlib/plugins/ipythonconsole.py:419 -msgid "Modify how Input and Output prompts are shown in the console." -msgstr "" -"Modifique como se muestran los prompts de entrada y salida en la terminal." - -#: spyderlib/plugins/ipythonconsole.py:422 -msgid "Input prompt:" -msgstr "Prompt de entrada:" - -#: spyderlib/plugins/ipythonconsole.py:424 -msgid "" -"Default is
    In [<span class=\"in-prompt-number\">%i</span>]:" -msgstr "" -"Por defecto es
    In [<span class=\"in-prompt-number\">%i</" -"span>]:" - -#: spyderlib/plugins/ipythonconsole.py:428 -msgid "Output prompt:" -msgstr "Prompt de salida:" - -#: spyderlib/plugins/ipythonconsole.py:430 -msgid "" -"Default is
    Out[<span class=\"out-prompt-number\">%i</span>]:" -msgstr "" -"Por defecto es
    Out[<span class=\"out-prompt-number\">%i</" -"span>]:" - -#: spyderlib/plugins/ipythonconsole.py:446 -msgid "Graphics" -msgstr "Gráficas" - -#: spyderlib/plugins/ipythonconsole.py:448 -#: spyderlib/plugins/workingdirectory.py:42 -msgid "Startup" -msgstr "Inicialización" - -#: spyderlib/plugins/ipythonconsole.py:450 -msgid "Advanced Settings" -msgstr "Opciones avanzadas" - -#: spyderlib/plugins/ipythonconsole.py:462 -#: spyderlib/plugins/ipythonconsole.py:725 -msgid "Connect to an existing kernel" -msgstr "Conectarse a un núcleo existente" - -#: spyderlib/plugins/ipythonconsole.py:464 -msgid "" -"Please enter the connection info of the kernel you want to connect to. For " -"that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for " -"example kernel-3764.json or just 3764)." -msgstr "" -"Por favor introduzca la información de conexión del núcleo al cual desea " -"conectarse. Para ello puede seleccionar su archivo de conexión JSON, usando " -"el botón Seleccionar, o escribir directamente su id, en caso de que " -"sea un núcleo local (por ejemplo, kernel-3764.json o sólo 3764)" - -#: spyderlib/plugins/ipythonconsole.py:475 -msgid "Connection info:" -msgstr "Información de conexión:" - -#: spyderlib/plugins/ipythonconsole.py:477 -msgid "Path to connection file or kernel id" -msgstr "Ruta al archivo de conexión o id del núcleo" - -#: spyderlib/plugins/ipythonconsole.py:479 -#: spyderlib/plugins/ipythonconsole.py:497 -msgid "Browse" -msgstr "Seleccionar" - -#: spyderlib/plugins/ipythonconsole.py:489 -msgid "This is a remote kernel" -msgstr "Este es un núcleo remoto" - -#: spyderlib/plugins/ipythonconsole.py:493 -msgid "username@hostname:port" -msgstr "usuario@servidor:puerto" - -#: spyderlib/plugins/ipythonconsole.py:496 -msgid "Path to ssh key file" -msgstr "Ruta al archivo de clave ssh" - -#: spyderlib/plugins/ipythonconsole.py:505 -msgid "Password or ssh key passphrase" -msgstr "Contraseña o frase de contraseña de la clave ssh" - -#: spyderlib/plugins/ipythonconsole.py:509 -msgid "Host name" -msgstr "Servidor" - -#: spyderlib/plugins/ipythonconsole.py:510 -msgid "Ssh key" -msgstr "Clave ssh" - -#: spyderlib/plugins/ipythonconsole.py:511 -msgid "Password" -msgstr "Contraseña" - -#: spyderlib/plugins/ipythonconsole.py:540 -msgid "Open IPython connection file" -msgstr "Abrir un archivo de conexión de IPython" - -#: spyderlib/plugins/ipythonconsole.py:546 -msgid "Select ssh key" -msgstr "Seleccionar clave ssh" - -#: spyderlib/plugins/ipythonconsole.py:713 -msgid "Open an &IPython console" -msgstr "Abrir una terminal de IPython" - -#: spyderlib/plugins/ipythonconsole.py:716 -msgid "Use %s+T when the console is selected to open a new one" -msgstr "Usar %s+T para abrir una nueva terminal" - -#: spyderlib/plugins/ipythonconsole.py:719 -msgid "Open a new console" -msgstr "Abrir una nueva terminal" - -#: spyderlib/plugins/ipythonconsole.py:726 -msgid "Open a new IPython console connected to an existing kernel" -msgstr "Abrir una nueva terminal de IPython conectada a un núcleo existente" - -#: spyderlib/plugins/ipythonconsole.py:809 -msgid "" -"No IPython console is currently available to run %s.

    Please " -"open a new one and try again." -msgstr "" -"No existe un intérprete de IPython para ejecutar %s.

    Por favor " -"abra uno nuevo e intente otra vez." - -#: spyderlib/plugins/ipythonconsole.py:950 -msgid "" -"Do you want to close all other consoles connected to the same kernel as this " -"one?" -msgstr "" -"¿Desea cerrar todas las otras terminales conectadas al mismo núcleo que ésta?" - -#: spyderlib/plugins/ipythonconsole.py:1032 -msgid "Connection error" -msgstr "Error de conexión" - -#: spyderlib/plugins/ipythonconsole.py:1033 -msgid "" -"Could not open ssh tunnel. The error was:\n" -"\n" -msgstr "" -"No fue posible crear un túnel ssh. El error fue:\n" -"\n" - -#: spyderlib/plugins/ipythonconsole.py:1069 -msgid "IPython" -msgstr "IPython" - -#: spyderlib/plugins/ipythonconsole.py:1070 -msgid "Unable to connect to IPython %s" -msgstr "No se pudo establecer conexión con el núcleo `%s`" - -#: spyderlib/plugins/ipythonconsole.py:1121 -msgid "Are you sure you want to restart the kernel?" -msgstr "Está seguro de que desea reiniciar el núcleo?" - -#: spyderlib/plugins/ipythonconsole.py:1123 -msgid "Restart kernel?" -msgstr "Reiniciar el núcleo?" - -#: spyderlib/plugins/onlinehelp.py:67 -msgid "Online help" -msgstr "Ayuda en línea" - -#: spyderlib/plugins/outlineexplorer.py:47 -#: spyderlib/widgets/editortools.py:194 -msgid "Outline" -msgstr "Explorador de código" - -#: spyderlib/plugins/projectexplorer.py:41 -#: spyderlib/widgets/projectexplorer.py:1137 -#: spyderlib/widgets/projectexplorer.py:1151 -msgid "Project explorer" -msgstr "Explorador de proyectos" - -#: spyderlib/plugins/projectexplorer.py:52 -#: spyderlib/widgets/projectexplorer.py:545 -msgid "New project..." -msgstr "Nuevo proyecto..." - -#: spyderlib/plugins/runconfig.py:28 -msgid "Execute in current Python or IPython console" -msgstr "Ejecutar en la terminal actual de Python o IPython" - -#: spyderlib/plugins/runconfig.py:29 -msgid "Execute in a new dedicated Python console" -msgstr "Ejecutar en una terminal de Python dedicada" - -#: spyderlib/plugins/runconfig.py:30 -msgid "Execute in an external System terminal" -msgstr "Ejecutar en una terminal de comandos del sistema" - -#: spyderlib/plugins/runconfig.py:40 -msgid "Always show %s on a first file run" -msgstr "Siempre muestre %s en una primera ejecución" - -#: spyderlib/plugins/runconfig.py:153 -msgid "General settings" -msgstr "Ajustes generales" - -#: spyderlib/plugins/runconfig.py:156 spyderlib/plugins/runconfig.py:201 -msgid "Command line options:" -msgstr "Opciones de línea de comandos:" - -#: spyderlib/plugins/runconfig.py:163 -msgid "Working directory:" -msgstr "Directorio de trabajo:" - -#: spyderlib/plugins/runconfig.py:189 -msgid "Dedicated Python console" -msgstr "Terminal de Python dedicada" - -#: spyderlib/plugins/runconfig.py:194 -msgid "Interact with the Python console after execution" -msgstr "Interactuar con la terminal después de la ejecución" - -#: spyderlib/plugins/runconfig.py:198 -msgid "Show warning when killing running process" -msgstr "Mostrar una advertencia cuando se termine el proceso" - -#: spyderlib/plugins/runconfig.py:207 -msgid "-u is added to the other options you set here" -msgstr "La opción -u se añade a estas opciones" - -#: spyderlib/plugins/runconfig.py:218 -msgid "this dialog" -msgstr "este diálogo" - -#: spyderlib/plugins/runconfig.py:276 -msgid "Run configuration" -msgstr "Opciones de ejecución" - -#: spyderlib/plugins/runconfig.py:277 -msgid "The following working directory is not valid:
    %s" -msgstr "El siguiente directorio de trabajo no es válido:
    %s" - -#: spyderlib/plugins/runconfig.py:353 -msgid "Run settings for %s" -msgstr "Ajustes de ejecución para %s" - -#: spyderlib/plugins/runconfig.py:384 -msgid "Select a run configuration:" -msgstr "Seleccionar una configuración de ejecución:" - -#: spyderlib/plugins/runconfig.py:414 spyderlib/plugins/runconfig.py:439 -msgid "Run Settings" -msgstr "Ajustes de ejecución" - -#: spyderlib/plugins/runconfig.py:441 -msgid "" -"The following are the default %s. These options may be overriden " -"using the %s dialog box (see the %s menu)" -msgstr "" -"Las siguientes son los %s por defecto. Estos ajustes pueden " -"modificarse usando el diálogo %s (ver el menú %s)" - -#: spyderlib/plugins/runconfig.py:465 -#: spyderlib/widgets/externalshell/pythonshell.py:297 -msgid "Working directory" -msgstr "Directorio de trabajo" - -#: spyderlib/plugins/runconfig.py:467 -msgid "Default working directory is:" -msgstr "Directorio de trabajo:" - -#: spyderlib/plugins/runconfig.py:469 -msgid "the script directory" -msgstr "El directorio en el que se encuentra el archivo actual" - -#: spyderlib/plugins/runconfig.py:472 spyderlib/plugins/workingdirectory.py:54 -msgid "the following directory:" -msgstr "el siguiente directorio:" - -#: spyderlib/plugins/runconfig.py:491 -msgid "Run Settings dialog" -msgstr "Ajustes de ejecución" - -#: spyderlib/plugins/shortcuts.py:178 -msgid "Context" -msgstr "Contexto" - -#: spyderlib/plugins/shortcuts.py:180 spyderlib/widgets/dicteditor.py:158 -msgid "Name" -msgstr "Nombre" - -#: spyderlib/plugins/shortcuts.py:182 -msgid "Mod1" -msgstr "Mod1" - -#: spyderlib/plugins/shortcuts.py:184 -msgid "Mod2" -msgstr "Mod2" - -#: spyderlib/plugins/shortcuts.py:186 -msgid "Mod3" -msgstr "Mod3" - -#: spyderlib/plugins/shortcuts.py:188 spyderlib/widgets/dicteditor.py:169 -msgid "Key" -msgstr "Clave/Tecla" - -#: spyderlib/plugins/shortcuts.py:321 -msgid "Conflicts" -msgstr "Conflicto con" - -#: spyderlib/plugins/shortcuts.py:322 -msgid "The following conflicts have been detected:" -msgstr "Los siguientes conflictos han sido detectados:" - -#: spyderlib/plugins/shortcuts.py:334 -msgid "Keyboard shortcuts" -msgstr "Atajos de teclado" - -#: spyderlib/plugins/variableexplorer.py:24 -msgid "Autorefresh" -msgstr "Actualizar automáticamente" - -#: spyderlib/plugins/variableexplorer.py:25 -msgid "Enable autorefresh" -msgstr "Activar las actualizaciones automáticas" - -#: spyderlib/plugins/variableexplorer.py:27 -msgid "Refresh interval: " -msgstr "Intervalo de actualización" - -#: spyderlib/plugins/variableexplorer.py:28 -msgid " ms" -msgstr "ms" - -#: spyderlib/plugins/variableexplorer.py:31 -msgid "Filter" -msgstr "Filtrar" - -#: spyderlib/plugins/variableexplorer.py:33 -#: spyderlib/widgets/externalshell/namespacebrowser.py:196 -msgid "Exclude private references" -msgstr "Excluir variables privadas" - -#: spyderlib/plugins/variableexplorer.py:34 -#: spyderlib/widgets/externalshell/namespacebrowser.py:211 -msgid "Exclude capitalized references" -msgstr "Excluir variables que comienzan en mayúsculas" - -#: spyderlib/plugins/variableexplorer.py:35 -#: spyderlib/widgets/externalshell/namespacebrowser.py:204 -msgid "Exclude all-uppercase references" -msgstr "Excluir variables en mayúsculas" - -#: spyderlib/plugins/variableexplorer.py:36 -#: spyderlib/widgets/externalshell/namespacebrowser.py:219 -msgid "Exclude unsupported data types" -msgstr "Excluir tipos de datos no soportados" - -#: spyderlib/plugins/variableexplorer.py:42 -#: spyderlib/widgets/dicteditor.py:702 -msgid "Truncate values" -msgstr "Abreviar valores" - -#: spyderlib/plugins/variableexplorer.py:44 -#: spyderlib/widgets/dicteditor.py:706 -msgid "Show arrays min/max" -msgstr "Mostrar el máximo y mínimo de arreglos" - -#: spyderlib/plugins/variableexplorer.py:46 -msgid "Edit data in the remote process" -msgstr "Editar los datos en un proceso remoto" - -#: spyderlib/plugins/variableexplorer.py:47 -msgid "" -"Editors are opened in the remote process for NumPy arrays, PIL images, " -"lists, tuples and dictionaries.\n" -"This avoids transfering large amount of data between the remote process and " -"Spyder (through the socket)." -msgstr "" -"Esta opción permite modificar arreglos de NumPy, imágenes de\n" -"de PIL/Pillow, Dataframes, y listas, tuplas y diccionarios en el\n" -"proceso remoto. Esto impide transferir grandes cantidades de\n" -"datos entre el proceso remoto y Spyder." - -#: spyderlib/plugins/variableexplorer.py:158 -msgid "Variable explorer" -msgstr "Explorador de variables" - -#: spyderlib/plugins/workingdirectory.py:35 -msgid "" -"The global working directory is the working directory for newly " -"opened consoles (Python/IPython consoles and terminals), for the " -"file explorer, for the find in files plugin and for new files " -"created in the editor." -msgstr "" -"El directorio de trabajo global es el directorio de trabajo para las " -"terminales que se abran de aquí en adelante (de IPython y Python, y " -"terminales de comandos), para el Explorador de archivos y Buscar " -"en archivos y para los nuevos archivos creados en el Editor." - -#: spyderlib/plugins/workingdirectory.py:44 -msgid "At startup, the global working directory is:" -msgstr "Al inicio, el directorio de trabajo global es:" - -#: spyderlib/plugins/workingdirectory.py:48 -msgid "the same as in last session" -msgstr "El mismo de la última sesión" - -#: spyderlib/plugins/workingdirectory.py:50 -msgid "At startup, Spyder will restore the global directory from last session" -msgstr "Al inicio Spyder restaurará el directorio global de la última sesión" - -#: spyderlib/plugins/workingdirectory.py:56 -msgid "At startup, the global working directory will be the specified path" -msgstr "Al inicio el directorio de trabajo global será el siguiente" - -#: spyderlib/plugins/workingdirectory.py:70 -msgid "Files are opened from:" -msgstr "Los archivos deben abrirse desde:" - -#: spyderlib/plugins/workingdirectory.py:74 -#: spyderlib/plugins/workingdirectory.py:87 -msgid "the current file directory" -msgstr "El directorio en el que se encuentra el archivo actual" - -#: spyderlib/plugins/workingdirectory.py:78 -#: spyderlib/plugins/workingdirectory.py:91 -msgid "the global working directory" -msgstr "El directorio de trabajo global" - -#: spyderlib/plugins/workingdirectory.py:83 -msgid "Files are created in:" -msgstr "Los archivos son creados en:" - -#: spyderlib/plugins/workingdirectory.py:97 -msgid "Change to file base directory" -msgstr "Cambiarse al directorio base de un archivo" - -#: spyderlib/plugins/workingdirectory.py:99 -msgid "When opening a file" -msgstr "Cuando se abra un archivo" - -#: spyderlib/plugins/workingdirectory.py:101 -msgid "When saving a file" -msgstr "Cuando se guarde un archivo" - -#: spyderlib/plugins/workingdirectory.py:160 -msgid "Back" -msgstr "Anterior" - -#: spyderlib/plugins/workingdirectory.py:168 -#: spyderlib/widgets/explorer.py:1004 spyderlib/widgets/importwizard.py:523 -msgid "Next" -msgstr "Siguiente" - -#: spyderlib/plugins/workingdirectory.py:181 -msgid "" -"This is the working directory for newly\n" -"opened consoles (Python/IPython consoles and\n" -"terminals), for the file explorer, for the\n" -"find in files plugin and for new files\n" -"created in the editor" -msgstr "" -"Este es el directorio de trabajo para las\n" -"terminales que se abran de aquí en\n" -"adelante (de IPython y Python y terminales de\n" -"comandos), para el Explorador de archivos,\n" -"y Buscar en archivos y para los nuevos\n" -"archivos creados en el Editor" - -#: spyderlib/plugins/workingdirectory.py:207 -msgid "Browse a working directory" -msgstr "Seleccionar un directorio de trabajo" - -#: spyderlib/plugins/workingdirectory.py:213 -msgid "Set as current console's working directory" -msgstr "Establece el directorio de trabajo para la terminal actual" - -#: spyderlib/plugins/workingdirectory.py:221 -msgid "Change to parent directory" -msgstr "Moverse al directorio superior" - -#: spyderlib/plugins/workingdirectory.py:228 -msgid "Global working directory" -msgstr "Directorio de trabajo global" - -#: spyderlib/spyder.py:120 -msgid "Initializing..." -msgstr "Inicializando..." - -#: spyderlib/spyder.py:244 -msgid "Numpy and Scipy documentation" -msgstr "Documentación de Numpy y Scipy" - -#: spyderlib/spyder.py:246 spyderlib/spyder.py:949 -msgid "Matplotlib documentation" -msgstr "Documentación de Matplotlib" - -#: spyderlib/spyder.py:249 -msgid "PyQt4 Reference Guide" -msgstr "Manual de referencia de PyQt4" - -#: spyderlib/spyder.py:252 -msgid "PyQt4 API Reference" -msgstr "Referencia del API de PyQt4" - -#: spyderlib/spyder.py:254 -msgid "Python(x,y)" -msgstr "Python(x,y)" - -#: spyderlib/spyder.py:256 -msgid "WinPython" -msgstr "WinPython" - -#: spyderlib/spyder.py:293 -msgid "Reload last session" -msgstr "Recargar la última sesión" - -#: spyderlib/spyder.py:297 -msgid "Load session..." -msgstr "Cargar sesión..." - -#: spyderlib/spyder.py:300 -msgid "Load Spyder session" -msgstr "Cargar sesión de Spyder" - -#: spyderlib/spyder.py:302 -msgid "Save session and quit..." -msgstr "Guardar sesión y salir..." - -#: spyderlib/spyder.py:305 -msgid "Save current session and quit application" -msgstr "Guardar sesión actual y salir de la aplicación" - -#: spyderlib/spyder.py:483 -msgid "Close current pane" -msgstr "Cerrar panel actual" - -#: spyderlib/spyder.py:489 -msgid "&Find text" -msgstr "&Buscar texto" - -#: spyderlib/spyder.py:494 -msgid "Find &next" -msgstr "Buscar &siguiente" - -#: spyderlib/spyder.py:500 -msgid "Find &previous" -msgstr "Buscar &anterior" - -#: spyderlib/spyder.py:505 -msgid "&Replace text" -msgstr "&Reemplazar texto" - -#: spyderlib/spyder.py:520 spyderlib/widgets/sourcecode/codeeditor.py:2268 -msgid "Undo" -msgstr "Deshacer" - -#: spyderlib/spyder.py:522 spyderlib/widgets/sourcecode/codeeditor.py:2271 -msgid "Redo" -msgstr "Rehacer" - -#: spyderlib/spyder.py:523 spyderlib/widgets/arrayeditor.py:392 -#: spyderlib/widgets/dataframeeditor.py:418 -#: spyderlib/widgets/dicteditor.py:674 spyderlib/widgets/shell.py:118 -#: spyderlib/widgets/sourcecode/codeeditor.py:2277 -msgid "Copy" -msgstr "Copiar" - -#: spyderlib/spyder.py:525 spyderlib/widgets/shell.py:114 -#: spyderlib/widgets/sourcecode/codeeditor.py:2274 -msgid "Cut" -msgstr "Cortar" - -#: spyderlib/spyder.py:526 spyderlib/widgets/dicteditor.py:671 -#: spyderlib/widgets/shell.py:122 -#: spyderlib/widgets/sourcecode/codeeditor.py:2280 -msgid "Paste" -msgstr "Pegar" - -#: spyderlib/spyder.py:528 spyderlib/widgets/explorer.py:461 -#: spyderlib/widgets/projectexplorer.py:1003 spyderlib/widgets/shell.py:131 -#: spyderlib/widgets/sourcecode/codeeditor.py:2283 -msgid "Delete" -msgstr "Eliminar" - -#: spyderlib/spyder.py:531 spyderlib/widgets/shell.py:135 -#: spyderlib/widgets/sourcecode/codeeditor.py:2287 -msgid "Select All" -msgstr "Seleccionar todo" - -#: spyderlib/spyder.py:580 -msgid "C&onsoles" -msgstr "&Terminales" - -#: spyderlib/spyder.py:586 -msgid "&View" -msgstr "&Ver" - -#: spyderlib/spyder.py:589 -msgid "&Help" -msgstr "A&yuda" - -#: spyderlib/spyder.py:594 -msgid "Welcome to Spyder!" -msgstr "Bienvenido a Spyder!" - -#: spyderlib/spyder.py:599 -msgid "Pre&ferences" -msgstr "Pre&ferencias" - -#: spyderlib/spyder.py:606 spyderlib/widgets/pathmanager.py:45 -#: spyderlib/widgets/projectexplorer.py:594 -msgid "PYTHONPATH manager" -msgstr "Administrador del PYTHONPATH" - -#: spyderlib/spyder.py:609 -msgid "Python Path Manager" -msgstr "Manejador de rutas de Python" - -#: spyderlib/spyder.py:612 -msgid "Update module names list" -msgstr "Actualizar la lista de nombres de módulos" - -#: spyderlib/spyder.py:614 -msgid "Refresh list of module names available in PYTHONPATH" -msgstr "" -"Actualiza la lista de nombres de los módulos disponibles en su PYTHONPATH" - -#: spyderlib/spyder.py:619 -msgid "Current user environment variables..." -msgstr "Variables de entorno del usuario actual..." - -#: spyderlib/spyder.py:621 -msgid "" -"Show and edit current user environment variables in Windows registry (i.e. " -"for all sessions)" -msgstr "" -"Mostrar y editar las variables de\n" -"entorno del usuario actual en el\n" -"registro de Windows (es decir,\n" -"para todas las sesiones)" - -#: spyderlib/spyder.py:629 spyderlib/spyder.py:1043 -msgid "External Tools" -msgstr "Herramientas externas" - -#: spyderlib/spyder.py:633 -msgid "Python(x,y) launcher" -msgstr "Lanzador de Python(x,y)" - -#: spyderlib/spyder.py:640 -msgid "WinPython control panel" -msgstr "Panel de control de WinPython" - -#: spyderlib/spyder.py:649 -msgid "Qt Designer" -msgstr "Diseñador de interfaces de Qt" - -#: spyderlib/spyder.py:654 -msgid "Qt Linguist" -msgstr "Traductor de aplicaciones de Qt" - -#: spyderlib/spyder.py:660 -msgid "Qt examples" -msgstr "Ejemplos de Qt" - -#: spyderlib/spyder.py:678 -msgid "guidata examples" -msgstr "Ejemplos de guidata" - -#: spyderlib/spyder.py:686 -msgid "guiqwt examples" -msgstr "Ejemplos de guiqwt" - -#: spyderlib/spyder.py:691 -msgid "Sift" -msgstr "Sift" - -#: spyderlib/spyder.py:699 -msgid "ViTables" -msgstr "ViTables" - -#: spyderlib/spyder.py:713 -msgid "Fullscreen mode" -msgstr "Modo a pantalla completa" - -#: spyderlib/spyder.py:725 -msgid "Main toolbar" -msgstr "Barra principal" - -#: spyderlib/spyder.py:734 -msgid "" -"Spyder Internal Console\n" -"\n" -"This console is used to report application\n" -"internal errors and to inspect Spyder\n" -"internals with the following commands:\n" -" spy.app, spy.window, dir(spy)\n" -"\n" -"Please don't use it to run your code\n" -"\n" -msgstr "" -"Terminal interna de Spyder!\n" -"\n" -"Esta terminal se utiliza para reportar errores de la\n" -"aplicación y para inspeccionar las características\n" -"internas de Spyder con los siguientes comandos:\n" -" spy.app, spy.window, dir(spy)\n" -"\n" -"Por favor no ejecuta su código en esta terminal\n" - -#: spyderlib/spyder.py:751 -msgid "Loading object inspector..." -msgstr "Cargando el inspector de objetos..." - -#: spyderlib/spyder.py:758 -msgid "Loading outline explorer..." -msgstr "Cargando el explorador de código..." - -#: spyderlib/spyder.py:766 -msgid "Loading editor..." -msgstr "Cargando el editor..." - -#: spyderlib/spyder.py:791 -msgid "Loading file explorer..." -msgstr "Cargando el explorador de archivos..." - -#: spyderlib/spyder.py:798 -msgid "Loading history plugin..." -msgstr "Cargando el historial..." - -#: spyderlib/spyder.py:809 -msgid "Loading online help..." -msgstr "Cargando la ayuda en línea..." - -#: spyderlib/spyder.py:815 -msgid "Loading project explorer..." -msgstr "Cargando el explorador de proyectos..." - -#: spyderlib/spyder.py:826 -msgid "Loading external console..." -msgstr "Cargando la terminal externa..." - -#: spyderlib/spyder.py:835 -msgid "Loading namespace browser..." -msgstr "Cargando el explorador de variables..." - -#: spyderlib/spyder.py:842 -msgid "Loading IPython console..." -msgstr "Cargando la terminal de IPython..." - -#: spyderlib/spyder.py:853 -msgid "Setting up main window..." -msgstr "Construyendo la ventana principal..." - -#: spyderlib/spyder.py:856 -msgid "Optional dependencies..." -msgstr "Dependencias opcionales..." - -#: spyderlib/spyder.py:860 -msgid "Report issue..." -msgstr "Reportar un problema..." - -#: spyderlib/spyder.py:864 -msgid "Spyder support..." -msgstr "Obtener soporte para Spyder" - -#: spyderlib/spyder.py:887 -msgid "Spyder documentation" -msgstr "Documentación de Spyder" - -#: spyderlib/spyder.py:889 -msgid "Spyder tutorial" -msgstr "Tutorial de Spyder" - -#: spyderlib/spyder.py:896 -msgid "Python documentation" -msgstr "Documentación de Python" - -#: spyderlib/spyder.py:902 spyderlib/spyder.py:941 -msgid "IPython documentation" -msgstr "Documentación de IPython" - -#: spyderlib/spyder.py:903 -msgid "Intro to IPython" -msgstr "Ayuda básica" - -#: spyderlib/spyder.py:905 -msgid "Quick reference" -msgstr "Referencia rápida" - -#: spyderlib/spyder.py:907 -msgid "Console help" -msgstr "Ayuda de la terminal" - -#: spyderlib/spyder.py:939 -msgid "Python(x,y) documentation folder" -msgstr "Carpeta de documentación de Python(x,y)" - -#: spyderlib/spyder.py:943 -msgid "guidata documentation" -msgstr "Documentación de guidata" - -#: spyderlib/spyder.py:946 -msgid "guiqwt documentation" -msgstr "Documentación de guiqwt" - -#: spyderlib/spyder.py:952 -msgid "NumPy documentation" -msgstr "Documentación de NumPy" - -#: spyderlib/spyder.py:954 -msgid "NumPy reference guide" -msgstr "Manual de referencia de NumPy" - -#: spyderlib/spyder.py:956 -msgid "NumPy user guide" -msgstr "Guía del usuario de Numpy" - -#: spyderlib/spyder.py:958 -msgid "SciPy documentation" -msgstr "Documentación de SciPy" - -#: spyderlib/spyder.py:965 -msgid "Installed Python modules" -msgstr "Módulos instalados de Python" - -#: spyderlib/spyder.py:969 -msgid "Online documentation" -msgstr "Documentación en línea" - -#: spyderlib/spyder.py:979 -msgid "Qt documentation" -msgstr "Documentación de Qt" - -#: spyderlib/spyder.py:985 -msgid "About %s..." -msgstr "Acerca de %s..." - -#: spyderlib/spyder.py:1006 -msgid "Panes" -msgstr "Paneles" - -#: spyderlib/spyder.py:1007 -msgid "Toolbars" -msgstr "Barras de herramientas" - -#: spyderlib/spyder.py:1010 -msgid "Reset window layout" -msgstr "Restablecer la disposición de componentes" - -#: spyderlib/spyder.py:1012 -msgid "Custom window layouts" -msgstr "Disposiciones personalizadas de componentes" - -#: spyderlib/spyder.py:1018 -msgid "Switch to/from layout %d" -msgstr "Cambiarse a la disposición %d" - -#: spyderlib/spyder.py:1023 -msgid "Set layout %d" -msgstr "Establecer la disposición %d" - -#: spyderlib/spyder.py:1031 -msgid "Attached console window (debugging)" -msgstr "Ventana de terminal anexa (para depuración)" - -#: spyderlib/spyder.py:1332 -msgid "" -"Window layout will be reset to default settings: this affects window " -"position, size and dockwidgets.\n" -"Do you want to continue?" -msgstr "" -"La disposición de componentes será restablecida a los ajustes por defecto. " -"Esto afecta a la posición y tamaño de la ventana y los componentes.\n" -"¿Desea continuar?" - -#: spyderlib/spyder.py:1350 -msgid "Quick switch layout #%d has not yet been defined." -msgstr "Aún no se ha definido la disposición de componentes #%d" - -#: spyderlib/spyder.py:1602 spyderlib/spyder.py:1603 -msgid "Maximize current pane" -msgstr "Maximizar el panel actual" - -#: spyderlib/spyder.py:1606 -msgid "Restore current pane" -msgstr "Restaurar el panel actual" - -#: spyderlib/spyder.py:1607 -msgid "Restore pane to its original size" -msgstr "Restaurar el panel a su tamaño original" - -#: spyderlib/spyder.py:1686 -msgid "About %s" -msgstr "Acerca de %s" - -#: spyderlib/spyder.py:1851 -msgid "Running an external system terminal is not supported on platform %s." -msgstr "" -"Ejecutar en una terminal externa del sistema no está soportado en la " -"plataforma %s." - -#: spyderlib/spyder.py:2066 -msgid "Open session" -msgstr "Abrir sesión" - -#: spyderlib/spyder.py:2067 spyderlib/spyder.py:2078 -msgid "Spyder sessions" -msgstr "Sesiones de Spyder" - -#: spyderlib/spyder.py:2077 -msgid "Save session" -msgstr "Guardar sesión" - -#: spyderlib/utils/codeanalysis.py:92 -msgid "Real-time code analysis on the Editor" -msgstr "Análisis del código en tiempo real en el Editor" - -#: spyderlib/utils/codeanalysis.py:96 -msgid "Real-time code style analysis on the Editor" -msgstr "Análisis de estilo del código en el Editor" - -#: spyderlib/utils/environ.py:95 -msgid "" -"Module pywin32 was not found.
    Please restart this Windows " -"session (not the computer) for changes to take effect." -msgstr "" -"No se pudo encontrar el módulo pywin32.
    Por favor reinicie esta " -"sesión de Windows (no el computador) para que los cambios surtan " -"efecto." - -#: spyderlib/utils/environ.py:108 -msgid "" -"If you accept changes, this will modify the current user environment " -"variables directly in Windows registry. Use it with precautions, at " -"your own risks.

    Note that for changes to take effect, you will need " -"to restart the parent process of this application (simply restart Spyder if " -"you have executed it from a Windows shortcut, otherwise restart any " -"application from which you may have executed it, like Python(x,y) Home for example)" -msgstr "" -"Si acepta los cambios, se modificarán las variables de entorno del usuario " -"actual directamente en el registro de Windows. Hágalo con precaución " -"y bajo su propio riesgo.

    Tenga en cuenta que para que los cambios " -"tengan efecto, deberá reiniciar el proceso padre de esta aplicación " -"(simplemente reinicie Spyder si lo ejecutó desde un acceso directo, de otra " -"forma reinicie la aplicación desde la cual lo inició, como por ejemplo " -"Python(x,y) Home)" - -#: spyderlib/utils/inspector/sphinxify.py:209 -#: spyderlib/utils/inspector/sphinxify.py:219 -msgid "" -"It was not possible to generate rich text help for this object.
    Please " -"see it in plain text." -msgstr "" -"No fue posible generar ayuda en texto enriquecido para este objeto.
    Por " -"favor véala en texto plano." - -#: spyderlib/utils/introspection/jedi_plugin.py:32 -msgid "(Experimental) Editor's code completion, go-to-definition and help" -msgstr "(Experimental) Completado del código y ayuda en el Editor" - -#: spyderlib/utils/introspection/rope_plugin.py:37 -msgid "Editor's code completion, go-to-definition and help" -msgstr "Completado del código y ayuda en el Editor" - -#: spyderlib/utils/iofuncs.py:496 -msgid "Supported files" -msgstr "Archivos soportados" - -#: spyderlib/utils/iofuncs.py:498 -msgid "All files (*.*)" -msgstr "Todos los archivos (*.*)" - -#: spyderlib/utils/iofuncs.py:508 -msgid "Spyder data files" -msgstr "Archivos de datos de Spyder" - -#: spyderlib/utils/iofuncs.py:510 spyderlib/widgets/dicteditor.py:1041 -msgid "NumPy arrays" -msgstr "Arreglos de NumPy" - -#: spyderlib/utils/iofuncs.py:511 -msgid "NumPy zip arrays" -msgstr "Arreglos comprimidos de NumPy" - -#: spyderlib/utils/iofuncs.py:512 -msgid "Matlab files" -msgstr "Archivos de Matlab" - -#: spyderlib/utils/iofuncs.py:513 -msgid "CSV text files" -msgstr "Archivos de texto CSV" - -#: spyderlib/utils/iofuncs.py:515 -msgid "JPEG images" -msgstr "Imágenes JPEG" - -#: spyderlib/utils/iofuncs.py:516 -msgid "PNG images" -msgstr "Imágenes PNG" - -#: spyderlib/utils/iofuncs.py:517 -msgid "GIF images" -msgstr "Imágenes GIF" - -#: spyderlib/utils/iofuncs.py:518 -msgid "TIFF images" -msgstr "Imágenes TIFF" - -#: spyderlib/utils/iofuncs.py:519 spyderlib/utils/iofuncs.py:520 -msgid "Pickle files" -msgstr "Archivos pickle" - -#: spyderlib/utils/iofuncs.py:521 -msgid "JSON files" -msgstr "Archivos JSON" - -#: spyderlib/utils/iofuncs.py:540 spyderlib/utils/iofuncs.py:547 -msgid "Unsupported file type '%s'" -msgstr "Tipo de archivo no soportado '%s'" - -#: spyderlib/utils/programs.py:176 -msgid "It was not possible to run this file in an external terminal" -msgstr "No fue posible ejecutar este archivo en una terminal del sistema" - -#: spyderlib/widgets/arrayeditor.py:452 spyderlib/widgets/arrayeditor.py:485 -#: spyderlib/widgets/dataframeeditor.py:507 -#: spyderlib/widgets/dataframeeditor.py:549 -msgid "Format" -msgstr "Formato" - -#: spyderlib/widgets/arrayeditor.py:457 -#: spyderlib/widgets/dataframeeditor.py:511 -msgid "Resize" -msgstr "Redimensionar" - -#: spyderlib/widgets/arrayeditor.py:486 -#: spyderlib/widgets/dataframeeditor.py:550 -msgid "Float formatting" -msgstr "Formato de punto flotante" - -#: spyderlib/widgets/arrayeditor.py:493 -#: spyderlib/widgets/dataframeeditor.py:558 spyderlib/widgets/explorer.py:578 -#: spyderlib/widgets/explorer.py:681 -#: spyderlib/widgets/externalshell/pythonshell.py:537 -#: spyderlib/widgets/externalshell/systemshell.py:93 -msgid "Error" -msgstr "Error" - -#: spyderlib/widgets/arrayeditor.py:494 -#: spyderlib/widgets/dataframeeditor.py:559 -msgid "Format (%s) is incorrect" -msgstr "El formato (%s) es incorrecto" - -#: spyderlib/widgets/arrayeditor.py:528 -msgid "Array is empty" -msgstr "El arreglo está vacío" - -#: spyderlib/widgets/arrayeditor.py:531 -msgid "Arrays with more than 3 dimensions are not supported" -msgstr "Los arreglos de más de tres dimensiones no están soportados" - -#: spyderlib/widgets/arrayeditor.py:535 -msgid "The 'xlabels' argument length do no match array column number" -msgstr "" -"El argumento de longitud 'xlabels' no coincide con el número de columnas del " -"arreglo" - -#: spyderlib/widgets/arrayeditor.py:539 -msgid "The 'ylabels' argument length do no match array row number" -msgstr "" -"El argumento de longitud 'ylabels' no coincide con el número de filas del " -"arreglo" - -#: spyderlib/widgets/arrayeditor.py:546 -msgid "%s arrays" -msgstr "Los arreglos %s" - -#: spyderlib/widgets/arrayeditor.py:547 -msgid "%s are currently not supported" -msgstr "%s no están soportados por el momento" - -#: spyderlib/widgets/arrayeditor.py:554 -msgid "NumPy array" -msgstr "Arreglos de NumPy" - -#: spyderlib/widgets/arrayeditor.py:556 spyderlib/widgets/arrayeditor.py:713 -msgid "Array editor" -msgstr "Editor de arreglos" - -#: spyderlib/widgets/arrayeditor.py:558 -msgid "read only" -msgstr "sólo lectura" - -#: spyderlib/widgets/arrayeditor.py:589 -msgid "Record array fields:" -msgstr "Campos del arreglo de records:" - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Data" -msgstr "Datos" - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Mask" -msgstr "Máscara" - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Masked data" -msgstr "Datos enmascarados" - -#: spyderlib/widgets/arrayeditor.py:614 -msgid "Axis:" -msgstr "Eje:" - -#: spyderlib/widgets/arrayeditor.py:619 -msgid "Index:" -msgstr "Índice:" - -#: spyderlib/widgets/arrayeditor.py:633 -msgid "Warning: changes are applied separately" -msgstr "Advertencia: los cambios son aplicados de forma separada" - -#: spyderlib/widgets/arrayeditor.py:634 -msgid "" -"For performance reasons, changes applied to masked array won't be reflected " -"in array's data (and vice-versa)." -msgstr "" -"Por razones de rendimiento, los cambios\n" -"aplicados a arreglos enmascarados no se\n" -"verán reflejados en los datos del arreglo\n" -"(y viceversa)." - -#: spyderlib/widgets/browser.py:30 -#: spyderlib/widgets/sourcecode/codeeditor.py:2311 -msgid "Zoom out" -msgstr "Alejar" - -#: spyderlib/widgets/browser.py:33 -#: spyderlib/widgets/sourcecode/codeeditor.py:2308 -msgid "Zoom in" -msgstr "Acercar" - -#: spyderlib/widgets/browser.py:131 -msgid "Home" -msgstr "Página de inicio" - -#: spyderlib/widgets/browser.py:171 -msgid "Find text" -msgstr "Encontrar texto" - -#: spyderlib/widgets/browser.py:190 -msgid "Address:" -msgstr "Dirección:" - -#: spyderlib/widgets/browser.py:225 -msgid "Unable to load page" -msgstr "No fue posible cargar la página" - -#: spyderlib/widgets/comboboxes.py:117 -msgid "Press enter to validate this entry" -msgstr "Presione Enter para validar esta entrada" - -#: spyderlib/widgets/comboboxes.py:118 -msgid "This entry is incorrect" -msgstr "Esta entrada es incorrecta" - -#: spyderlib/widgets/comboboxes.py:171 -msgid "Press enter to validate this path" -msgstr "Presione Enter para validar esta ruta" - -#: spyderlib/widgets/comboboxes.py:172 -msgid "" -"This path is incorrect.\n" -"Enter a correct directory path,\n" -"then press enter to validate" -msgstr "" -"Esta ruta es incorrecta.\n" -"Introduzca una ruta de\n" -"directorio correcta y después\n" -"presione Enter para validarla" - -#: spyderlib/widgets/dataframeeditor.py:423 -msgid "To bool" -msgstr "A booleano" - -#: spyderlib/widgets/dataframeeditor.py:423 -msgid "To complex" -msgstr "A complejo" - -#: spyderlib/widgets/dataframeeditor.py:424 -msgid "To float" -msgstr "A flotante" - -#: spyderlib/widgets/dataframeeditor.py:424 -msgid "To int" -msgstr "A entero" - -#: spyderlib/widgets/dataframeeditor.py:425 -msgid "To str" -msgstr "A cadena" - -#: spyderlib/widgets/dataframeeditor.py:489 -msgid "%s editor" -msgstr "Editor de %s" - -#: spyderlib/widgets/dataframeeditor.py:522 -msgid "Column min/max" -msgstr "Min/max de columna" - -#: spyderlib/widgets/dependencies.py:60 -msgid " Required " -msgstr "Requerido" - -#: spyderlib/widgets/dependencies.py:60 -msgid "Module" -msgstr "Módulo" - -#: spyderlib/widgets/dependencies.py:61 -msgid " Installed " -msgstr "Instalado" - -#: spyderlib/widgets/dependencies.py:61 -msgid "Provided features" -msgstr "Características proporcionadas" - -#: spyderlib/widgets/dependencies.py:127 -msgid "Optional Dependencies" -msgstr "Dependencias opcionales" - -#: spyderlib/widgets/dependencies.py:134 -msgid "" -"Spyder depends on several Python modules to provide additional functionality " -"for its plugins. The table below shows the required and installed versions " -"(if any) of all of them.

    Although Spyder can work without any of " -"these modules, it's strongly recommended that at least you try to install " -"%s and %s to have a much better experience." -msgstr "" -"Spyder depende de varios módulos de Python para proveer funcionalidad " -"adicional para sus componentes. La tabla que aparece a continuación muestra " -"las versiones requeridas e instaladas (de existir) de todos ellos." -"

    Aunque Spyder puede funcionar sin ninguno de estos módulos, se " -"recomienda al menos instalar %s y %s para tener una mejor " -"experiencia." - -#: spyderlib/widgets/dependencies.py:149 -msgid "Copy to clipboard" -msgstr "Copiar al portapapeles" - -#: spyderlib/widgets/dicteditor.py:156 -msgid "Index" -msgstr "Índice" - -#: spyderlib/widgets/dicteditor.py:161 -msgid "Tuple" -msgstr "Tupla" - -#: spyderlib/widgets/dicteditor.py:164 -msgid "List" -msgstr "Lista" - -#: spyderlib/widgets/dicteditor.py:167 -msgid "Dictionary" -msgstr "Diccionario" - -#: spyderlib/widgets/dicteditor.py:175 -msgid "Attribute" -msgstr "Atributo" - -#: spyderlib/widgets/dicteditor.py:177 -msgid "elements" -msgstr "elementos" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Size" -msgstr "Tamaño" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Type" -msgstr "Tipo" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Value" -msgstr "Valor" - -#: spyderlib/widgets/dicteditor.py:450 -msgid "" -"Opening this variable can be slow\n" -"\n" -"Do you want to continue anyway?" -msgstr "" -"Abrir e inspeccionar esta variable puede tomar mucho tiempo\n" -"\n" -"¿Desea continuar de todas formas?" - -#: spyderlib/widgets/dicteditor.py:458 spyderlib/widgets/dicteditor.py:607 -msgid "Edit item" -msgstr "Editar ítem" - -#: spyderlib/widgets/dicteditor.py:459 -msgid "Unable to retrieve data.

    Error message:
    %s" -msgstr "" -"No fue posible obtener los datos.

    Mensaje de error:
    %s" - -#: spyderlib/widgets/dicteditor.py:608 -msgid "Unable to assign data to item.

    Error message:
    %s" -msgstr "" -"No fue posible asignarle los datos al ítem.

    Mensaje de error:" -"
    %s" - -#: spyderlib/widgets/dicteditor.py:669 -msgid "Resize rows to contents" -msgstr "Ajustar filas a los contenidos" - -#: spyderlib/widgets/dicteditor.py:677 spyderlib/widgets/explorer.py:236 -msgid "Edit" -msgstr "Editar" - -#: spyderlib/widgets/dicteditor.py:680 spyderlib/widgets/dicteditor.py:1012 -#: spyderlib/widgets/dicteditor.py:1028 -msgid "Plot" -msgstr "Graficar" - -#: spyderlib/widgets/dicteditor.py:684 -msgid "Histogram" -msgstr "Histograma" - -#: spyderlib/widgets/dicteditor.py:688 -msgid "Show image" -msgstr "Mostrar como imagen" - -#: spyderlib/widgets/dicteditor.py:692 spyderlib/widgets/dicteditor.py:1035 -msgid "Save array" -msgstr "Guardar arreglo" - -#: spyderlib/widgets/dicteditor.py:696 spyderlib/widgets/dicteditor.py:976 -#: spyderlib/widgets/dicteditor.py:984 -msgid "Insert" -msgstr "Insertar" - -#: spyderlib/widgets/dicteditor.py:699 spyderlib/widgets/dicteditor.py:929 -msgid "Remove" -msgstr "Eliminar" - -#: spyderlib/widgets/dicteditor.py:710 spyderlib/widgets/dicteditor.py:946 -#: spyderlib/widgets/explorer.py:524 spyderlib/widgets/explorer.py:532 -#: spyderlib/widgets/explorer.py:544 -msgid "Rename" -msgstr "Renombrar" - -#: spyderlib/widgets/dicteditor.py:713 -msgid "Duplicate" -msgstr "Duplicar" - -#: spyderlib/widgets/dicteditor.py:927 -msgid "Do you want to remove selected item?" -msgstr "¿Desea eliminar la variable seleccionada?" - -#: spyderlib/widgets/dicteditor.py:928 -msgid "Do you want to remove all selected items?" -msgstr "¿Desea eliminar todas las variables seleccionadas?" - -#: spyderlib/widgets/dicteditor.py:946 spyderlib/widgets/dicteditor.py:976 -msgid "Key:" -msgstr "Nombre:" - -#: spyderlib/widgets/dicteditor.py:984 -msgid "Value:" -msgstr "Valor:" - -#: spyderlib/widgets/dicteditor.py:1000 -msgid "Import error" -msgstr "Error de importación" - -#: spyderlib/widgets/dicteditor.py:1001 -msgid "Please install matplotlib or guiqwt." -msgstr "Por favor instale Matplotlib o guiqwt." - -#: spyderlib/widgets/dicteditor.py:1013 -msgid "Unable to plot data.

    Error message:
    %s" -msgstr "" -"No fue posible graficar los datos.

    Mensaje de error:
    %s" - -#: spyderlib/widgets/dicteditor.py:1029 -msgid "Unable to show image.

    Error message:
    %s" -msgstr "" -"No fue posible generar la gráfica.

    Mensaje de error:
    %s" - -#: spyderlib/widgets/dicteditor.py:1051 -msgid "Unable to save array

    Error message:
    %s" -msgstr "" -"No fue posible guardar el arreglo

    Mensaje de error:
    %s" - -#: spyderlib/widgets/dicteditor.py:1068 -msgid "Clipboard contents" -msgstr "Contenidos del portapapeles" - -#: spyderlib/widgets/dicteditor.py:1082 -msgid "Import from clipboard" -msgstr "Importar desde el portapapeles" - -#: spyderlib/widgets/dicteditor.py:1084 -msgid "Empty clipboard" -msgstr "Vaciar el portapapeles" - -#: spyderlib/widgets/dicteditor.py:1085 -msgid "Nothing to be imported from clipboard." -msgstr "No hay nada para importar desde el portapapeles." - -#: spyderlib/widgets/dicteditorutils.py:59 -msgid "View and edit DataFrames and Series in the Variable Explorer" -msgstr "Ver y editar DataFrames y Series en el Explorador de Variables" - -#: spyderlib/widgets/editor.py:67 spyderlib/widgets/editor.py:417 -msgid "File list management" -msgstr "Gestión de la lista de archivos" - -#: spyderlib/widgets/editor.py:71 -msgid "Filter:" -msgstr "Filtro:" - -#: spyderlib/widgets/editor.py:76 -msgid "(press Enter to edit file)" -msgstr "(Oprimir Enter para editar)" - -#: spyderlib/widgets/editor.py:91 -msgid "&Edit file" -msgstr "Editar" - -#: spyderlib/widgets/editor.py:100 -msgid "&Close file" -msgstr "Cerrar" - -#: spyderlib/widgets/editor.py:108 -msgid "Hint: press Alt to show accelerators" -msgstr "Sugerencia: oprimir Alt para mostrar aceleradores" - -#: spyderlib/widgets/editor.py:420 -msgid "Copy path to clipboard" -msgstr "Copiar la ruta al portapapeles" - -#: spyderlib/widgets/editor.py:990 -msgid "Temporary file" -msgstr "Archivo temporal" - -#: spyderlib/widgets/editor.py:1087 -msgid "New window" -msgstr "Nueva ventana" - -#: spyderlib/widgets/editor.py:1088 -msgid "Create a new editor window" -msgstr "Crear una nueva ventana de edición" - -#: spyderlib/widgets/editor.py:1091 -msgid "Split vertically" -msgstr "Dividir verticalmente" - -#: spyderlib/widgets/editor.py:1093 -msgid "Split vertically this editor window" -msgstr "Dividir verticalmente esta panel o ventana de edición" - -#: spyderlib/widgets/editor.py:1095 -msgid "Split horizontally" -msgstr "Dividir horizontalmente" - -#: spyderlib/widgets/editor.py:1097 -msgid "Split horizontally this editor window" -msgstr "Dividir horizontalmente esta ventana o panel de edición" - -#: spyderlib/widgets/editor.py:1099 -msgid "Close this panel" -msgstr "Cerrar este panel" - -#: spyderlib/widgets/editor.py:1237 -msgid "%s has been modified.
    Do you want to save changes?" -msgstr "%s ha sido modificado.
    ¿Desea guardar los cambios?" - -#: spyderlib/widgets/editor.py:1300 -msgid "Save" -msgstr "Guardar" - -#: spyderlib/widgets/editor.py:1301 -msgid "Unable to save script '%s'

    Error message:
    %s" -msgstr "" -"No fue posible guardar el archivo '%s'

    Mensaje de error:
    %s" - -#: spyderlib/widgets/editor.py:1323 -msgid "Save Python script" -msgstr "Guardar archivo de Python" - -#: spyderlib/widgets/editor.py:1539 -msgid "" -"%s is unavailable (this file may have been removed, moved or renamed " -"outside Spyder).
    Do you want to close it?" -msgstr "" -"%s no está disponible (el archivo puede haber sido eliminado, movido " -"o renombrado por fuera de Spyder).
    ¿Desea cerrarlo?" - -#: spyderlib/widgets/editor.py:1559 -msgid "" -"%s has been modified outside Spyder.
    Do you want to reload it and " -"lose all your changes?" -msgstr "" -"%s fue modificado por fuera de Spyder.
    ¿Desea recargarlo y perder " -"todos sus cambios?" - -#: spyderlib/widgets/editor.py:1655 -msgid "" -"All changes to %s will be lost.
    Do you want to revert file from " -"disk?" -msgstr "" -"Todos los cambios a %s se perderán.
    Desea revertir el archivo del " -"disco?" - -#: spyderlib/widgets/editor.py:1811 -msgid "Loading %s..." -msgstr "Cargando %s..." - -#: spyderlib/widgets/editor.py:1821 -msgid "" -"%s contains mixed end-of-line characters.
    Spyder will fix this " -"automatically." -msgstr "" -"%s contiene varios tipos de caracteres de fin de línea.
    Spyder lo " -"arreglará automáticamente." - -#: spyderlib/widgets/editor.py:2192 -msgid "Close window" -msgstr "Cerrar ventana" - -#: spyderlib/widgets/editor.py:2194 -msgid "Close this window" -msgstr "Cierra esta ventana" - -#: spyderlib/widgets/editortools.py:93 spyderlib/widgets/editortools.py:129 -msgid "Line %s" -msgstr "Línea %s" - -#: spyderlib/widgets/editortools.py:98 -msgid "Class defined at line %s" -msgstr "Clase definida en la línea %s" - -#: spyderlib/widgets/editortools.py:106 -msgid "Method defined at line %s" -msgstr "Método definido en la línea %s" - -#: spyderlib/widgets/editortools.py:116 -msgid "Function defined at line %s" -msgstr "Función definida en la línea %s" - -#: spyderlib/widgets/editortools.py:148 -msgid "Cell starts at line %s" -msgstr "La celda empieza en la línea %s" - -#: spyderlib/widgets/editortools.py:201 spyderlib/widgets/editortools.py:536 -msgid "Go to cursor position" -msgstr "Ir a la posición del cursor" - -#: spyderlib/widgets/editortools.py:204 -msgid "Show absolute path" -msgstr "Mostrar la ruta completa" - -#: spyderlib/widgets/editortools.py:207 spyderlib/widgets/explorer.py:177 -msgid "Show all files" -msgstr "Mostrar todos los archivos" - -#: spyderlib/widgets/editortools.py:210 -msgid "Show special comments" -msgstr "Mostrar comentarios especiales" - -#: spyderlib/widgets/explorer.py:173 -msgid "Edit filename filters..." -msgstr "Editar filtros..." - -#: spyderlib/widgets/explorer.py:186 -msgid "Edit filename filters" -msgstr "Editar los filtros para nombres de archivo" - -#: spyderlib/widgets/explorer.py:187 -msgid "Name filters:" -msgstr "Nombres de los filtros:" - -#: spyderlib/widgets/explorer.py:205 -msgid "File..." -msgstr "Archivo..." - -#: spyderlib/widgets/explorer.py:208 -msgid "Module..." -msgstr "Módulo" - -#: spyderlib/widgets/explorer.py:211 -msgid "Folder..." -msgstr "Carpeta..." - -#: spyderlib/widgets/explorer.py:215 -msgid "Package..." -msgstr "Paquete..." - -#: spyderlib/widgets/explorer.py:238 -msgid "Move..." -msgstr "Mover a..." - -#: spyderlib/widgets/explorer.py:241 -msgid "Delete..." -msgstr "Eliminar..." - -#: spyderlib/widgets/explorer.py:244 -msgid "Rename..." -msgstr "Renombrar..." - -#: spyderlib/widgets/explorer.py:247 -msgid "Open" -msgstr "Abrir" - -#: spyderlib/widgets/explorer.py:248 -#: spyderlib/widgets/sourcecode/codeeditor.py:2299 -msgid "Convert to Python script" -msgstr "Convertir a un archivo de Python" - -#: spyderlib/widgets/explorer.py:271 -msgid "Commit" -msgstr "Consignar" - -#: spyderlib/widgets/explorer.py:275 -msgid "Browse repository" -msgstr "Explorar repositorio " - -#: spyderlib/widgets/explorer.py:287 -msgid "Open command prompt here" -msgstr "Abrir símbolo del sistema aquí" - -#: spyderlib/widgets/explorer.py:289 -msgid "Open terminal here" -msgstr "Abrir terminal del sistema aquí" - -#: spyderlib/widgets/explorer.py:294 -msgid "Open Python console here" -msgstr "Abrir una terminal de Python aquí" - -#: spyderlib/widgets/explorer.py:308 -msgid "New" -msgstr "Crear nuevo" - -#: spyderlib/widgets/explorer.py:316 -msgid "Import" -msgstr "Importar" - -#: spyderlib/widgets/explorer.py:462 -msgid "Do you really want to delete %s?" -msgstr "¿Realmente desea eliminar %s?" - -#: spyderlib/widgets/explorer.py:482 -msgid "delete" -msgstr "eliminar" - -#: spyderlib/widgets/explorer.py:483 spyderlib/widgets/projectexplorer.py:815 -#: spyderlib/widgets/projectexplorer.py:822 -#: spyderlib/widgets/projectexplorer.py:1089 -#: spyderlib/widgets/projectexplorer.py:1173 -msgid "Project Explorer" -msgstr "Explorador de proyectos" - -#: spyderlib/widgets/explorer.py:484 spyderlib/widgets/projectexplorer.py:762 -#: spyderlib/widgets/projectexplorer.py:1174 -msgid "Unable to %s %s

    Error message:
    %s" -msgstr "No fue posible %s %s

    Mensaje de error:
    %s" - -#: spyderlib/widgets/explorer.py:506 -#: spyderlib/widgets/sourcecode/codeeditor.py:1906 -msgid "Conversion error" -msgstr "Error de conversión" - -#: spyderlib/widgets/explorer.py:507 -#: spyderlib/widgets/sourcecode/codeeditor.py:1907 -msgid "" -"It was not possible to convert this notebook. The error is:\n" -"\n" -msgstr "" -"No fue posible convertir este notebook. El error es:\n" -"\n" - -#: spyderlib/widgets/explorer.py:525 -msgid "New name:" -msgstr "Nuevo nombre:" - -#: spyderlib/widgets/explorer.py:533 -msgid "" -"Do you really want to rename %s and overwrite the existing file " -"%s?" -msgstr "" -"¿Realmente desea renombrar %s y sobrescribir el archivo existente " -"%s?" - -#: spyderlib/widgets/explorer.py:545 -msgid "Unable to rename file %s

    Error message:
    %s" -msgstr "" -"No fue posible renombrar el archivo %s

    Mensaje de error:" -"
    %s" - -#: spyderlib/widgets/explorer.py:579 -msgid "Unable to move %s

    Error message:
    %s" -msgstr "No fue posible mover %s

    Mensaje de error:
    %s" - -#: spyderlib/widgets/explorer.py:597 -msgid "Unable to create folder %s

    Error message:
    %s" -msgstr "" -"No fue posible crear la carpeta %s

    Mensaje de error:
    " -"%s" - -#: spyderlib/widgets/explorer.py:610 spyderlib/widgets/explorer.py:644 -msgid "Unable to create file %s

    Error message:
    %s" -msgstr "" -"No fue posible crear el archivo %s

    Mensaje de error:
    " -"%s" - -#: spyderlib/widgets/explorer.py:618 -msgid "New folder" -msgstr "Nueva carpeta" - -#: spyderlib/widgets/explorer.py:619 -msgid "Folder name:" -msgstr "Nombre de la carpeta:" - -#: spyderlib/widgets/explorer.py:624 -msgid "New package" -msgstr "Nuevo paquete" - -#: spyderlib/widgets/explorer.py:625 -msgid "Package name:" -msgstr "Nombre del paquete:" - -#: spyderlib/widgets/explorer.py:665 -msgid "New module" -msgstr "Nuevo módulo" - -#: spyderlib/widgets/explorer.py:678 -msgid "" -"For %s support, please install one of the
    following tools:

    %s" -msgstr "" -"Para contar con soporte de %s, por favor instale una de
    las siguientes " -"herramientas:

    %s" - -#: spyderlib/widgets/explorer.py:682 -msgid "Unable to find external program.

    %s" -msgstr "No fue posible encontrar el programa externo.

    %s" - -#: spyderlib/widgets/explorer.py:882 -msgid "Show current directory only" -msgstr "Mostrar sólo el directorio actual" - -#: spyderlib/widgets/explorer.py:996 spyderlib/widgets/importwizard.py:518 -msgid "Previous" -msgstr "Anterior" - -#: spyderlib/widgets/explorer.py:1012 -msgid "Parent" -msgstr "Directorio superior" - -#: spyderlib/widgets/externalshell/baseshell.py:140 -msgid "Run again this program" -msgstr "Ejecutar de nuevo este programa" - -#: spyderlib/widgets/externalshell/baseshell.py:143 -msgid "Kill" -msgstr "Terminar" - -#: spyderlib/widgets/externalshell/baseshell.py:145 -msgid "Kills the current process, causing it to exit immediately" -msgstr "" -"Termina el proceso actual, provocando\n" -"que culmine inmediatamente" - -#: spyderlib/widgets/externalshell/baseshell.py:213 -msgid "Running..." -msgstr "Corriendo..." - -#: spyderlib/widgets/externalshell/baseshell.py:220 -msgid "Terminated." -msgstr "Terminado." - -#: spyderlib/widgets/externalshell/baseshell.py:242 -#: spyderlib/widgets/ipython.py:342 spyderlib/widgets/ipython.py:359 -#: spyderlib/widgets/mixins.py:608 -msgid "Arguments" -msgstr "Argumentos" - -#: spyderlib/widgets/externalshell/baseshell.py:243 -msgid "Command line arguments:" -msgstr "Argumentos de la línea de comandos:" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:173 -msgid "Refresh" -msgstr "Actualizar" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:177 -msgid "Refresh periodically" -msgstr "" -"Actualizar\n" -"periódicamente" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:181 -#: spyderlib/widgets/externalshell/namespacebrowser.py:446 -msgid "Import data" -msgstr "Importar datos" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:184 -#: spyderlib/widgets/externalshell/namespacebrowser.py:536 -#: spyderlib/widgets/externalshell/namespacebrowser.py:557 -msgid "Save data" -msgstr "Guardar datos" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:189 -msgid "Save data as..." -msgstr "Guardar datos como..." - -#: spyderlib/widgets/externalshell/namespacebrowser.py:197 -msgid "Exclude references which name starts with an underscore" -msgstr "" -"Excluir variables cuyo nombre comienza\n" -"con un guión abajo" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:205 -msgid "Exclude references which name is uppercase" -msgstr "" -"Excluir variables cuyo nombre está\n" -"por completo en mayúsculas" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:212 -msgid "Exclude references which name starts with an uppercase character" -msgstr "Excluir variables cuyo nombre comienza en mayúsculas" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:220 -msgid "" -"Exclude references to unsupported data types (i.e. which won't be handled/" -"saved correctly)" -msgstr "" -"Excluir variables que referencian tipos de datos no soportados\n" -"(es decir aquellos que no pueden manejarse y guardarse\n" -"correctamente)" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:342 -msgid "Object %s is not picklable" -msgstr "El objeto %s no es picklable" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:468 -msgid "" -"Unsupported file extension '%s'

    Would you like to import it " -"anyway (by selecting a known file format)?" -msgstr "" -"Extensión de archivo no soportada: '%s'

    ¿Desea importar el " -"archivo de todas formas (seleccionando un formato conocido)?" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:476 -msgid "Open file as:" -msgstr "Abrir archivo como:" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:524 -msgid "Unable to load '%s'

    Error message:
    %s" -msgstr "No fue posible cargar '%s'

    Mensaje de error:
    %s" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:558 -msgid "Unable to save current workspace

    Error message:
    %s" -msgstr "" -"No fue posible guardar el espacio de trabajo actual

    Mensaje de " -"error:
    %s" - -#: spyderlib/widgets/externalshell/pythonshell.py:269 -msgid "Variables" -msgstr "Variables" - -#: spyderlib/widgets/externalshell/pythonshell.py:270 -msgid "Show/hide global variables explorer" -msgstr "" -"Mostrar u ocultar el\n" -"explorador de variables" - -#: spyderlib/widgets/externalshell/pythonshell.py:274 -msgid "Terminate" -msgstr "Interrumpir" - -#: spyderlib/widgets/externalshell/pythonshell.py:275 -msgid "" -"Attempts to stop the process. The process\n" -"may not exit as a result of clicking this\n" -"button (it is given the chance to prompt\n" -"the user for any unsaved files, etc)." -msgstr "" -"Intenta interrumpir este proceso, pero éste\n" -"puede no concluir tras oprimir este botón\n" -"(pues es posible que se le pida al usuario\n" -"guardar archivos sin salvar, etc)." - -#: spyderlib/widgets/externalshell/pythonshell.py:288 -msgid "Interact" -msgstr "Interactuar" - -#: spyderlib/widgets/externalshell/pythonshell.py:290 -msgid "Debug" -msgstr "Depurar" - -#: spyderlib/widgets/externalshell/pythonshell.py:292 -#: spyderlib/widgets/externalshell/pythonshell.py:355 -msgid "Arguments..." -msgstr "Argumentos..." - -#: spyderlib/widgets/externalshell/pythonshell.py:299 -msgid "Set current working directory" -msgstr "Establece el directorio de trabajo" - -#: spyderlib/widgets/externalshell/pythonshell.py:301 -msgid "Environment variables" -msgstr "Variables de entorno" - -#: spyderlib/widgets/externalshell/pythonshell.py:305 -msgid "Show sys.path contents" -msgstr "Contenidos del sys.path" - -#: spyderlib/widgets/externalshell/pythonshell.py:351 -msgid "Arguments: %s" -msgstr "Argumentos: %s" - -#: spyderlib/widgets/externalshell/pythonshell.py:353 -msgid "No argument" -msgstr "Sin argumentos" - -#: spyderlib/widgets/externalshell/pythonshell.py:534 -msgid "" -"The kernel failed to start!! That's all we know... Please close this console " -"and open a new one." -msgstr "" -"No fue posible crear un núcleo!! Es todo lo que sabemos... Por favor cierre " -"esta terminal y abra una nueva." - -#: spyderlib/widgets/externalshell/pythonshell.py:538 -msgid "A Python console failed to start!" -msgstr "No se pudo iniciar una terminal de Python!" - -#: spyderlib/widgets/externalshell/systemshell.py:94 -msgid "Process failed to start" -msgstr "El proceso falló al empezar" - -#: spyderlib/widgets/findinfiles.py:155 -msgid "Unexpected error: see internal console" -msgstr "Error inesperado. Por favor vea la terminal interna." - -#: spyderlib/widgets/findinfiles.py:207 spyderlib/widgets/findinfiles.py:231 -#: spyderlib/widgets/findinfiles.py:278 -msgid "invalid regular expression" -msgstr "expresión regular inválida" - -#: spyderlib/widgets/findinfiles.py:276 -msgid "permission denied errors were encountered" -msgstr "permiso denegado, se encontraron errores" - -#: spyderlib/widgets/findinfiles.py:310 -msgid "Search pattern" -msgstr "Patrón de búsqueda" - -#: spyderlib/widgets/findinfiles.py:313 spyderlib/widgets/findinfiles.py:347 -#: spyderlib/widgets/findinfiles.py:359 spyderlib/widgets/findreplace.py:82 -msgid "Regular expression" -msgstr "Expresión regular" - -#: spyderlib/widgets/findinfiles.py:322 -msgid "Search" -msgstr "Buscar" - -#: spyderlib/widgets/findinfiles.py:325 -msgid "Start search" -msgstr "Comenzar la búsqueda" - -#: spyderlib/widgets/findinfiles.py:328 spyderlib/widgets/ipython.py:543 -msgid "Stop" -msgstr "Detener" - -#: spyderlib/widgets/findinfiles.py:331 -msgid "Stop search" -msgstr "Detener la búsqueda" - -#: spyderlib/widgets/findinfiles.py:341 -msgid "Included filenames pattern" -msgstr "" -"Patrones de nombres de\n" -"archivo a incluir" - -#: spyderlib/widgets/findinfiles.py:350 -msgid "Include:" -msgstr "Incluir:" - -#: spyderlib/widgets/findinfiles.py:353 -msgid "Excluded filenames pattern" -msgstr "" -"Patrones de nombres de\n" -"archivo a excluir" - -#: spyderlib/widgets/findinfiles.py:362 -msgid "Exclude:" -msgstr "Excluir:" - -#: spyderlib/widgets/findinfiles.py:372 -msgid "PYTHONPATH" -msgstr "PYTHONPATH" - -#: spyderlib/widgets/findinfiles.py:374 -msgid "" -"Search in all directories listed in sys.path which are outside the Python " -"installation directory" -msgstr "" -"Buscar en todos los directorios del\n" -"sys.path que están por fuera del\n" -"directorio de instalación de Python" - -#: spyderlib/widgets/findinfiles.py:377 -msgid "Hg repository" -msgstr "Repositorio Hg" - -#: spyderlib/widgets/findinfiles.py:380 -msgid "Search in current directory hg repository" -msgstr "" -"Buscar en el repositorio\n" -"actual de Mercurial" - -#: spyderlib/widgets/findinfiles.py:381 -msgid "Here:" -msgstr "Aquí" - -#: spyderlib/widgets/findinfiles.py:385 -msgid "Search recursively in this directory" -msgstr "" -"Buscar recursivamente\n" -"en este directorio" - -#: spyderlib/widgets/findinfiles.py:393 -msgid "Browse a search directory" -msgstr "" -"Seleccionar el directorio\n" -"de búsqueda" - -#: spyderlib/widgets/findinfiles.py:426 -msgid "Hide advanced options" -msgstr "Ocultar opciones avanzadas" - -#: spyderlib/widgets/findinfiles.py:429 -msgid "Show advanced options" -msgstr "Mostrar opciones avanzadas" - -#: spyderlib/widgets/findinfiles.py:571 -msgid "Search canceled" -msgstr "Búsqueda cancelada" - -#: spyderlib/widgets/findinfiles.py:575 -msgid "String not found" -msgstr "Texto no encontrado" - -#: spyderlib/widgets/findinfiles.py:577 -msgid "matches in" -msgstr "coincidencias en" - -#: spyderlib/widgets/findinfiles.py:578 -msgid "file" -msgstr "archivo" - -#: spyderlib/widgets/findinfiles.py:586 -msgid "interrupted" -msgstr "interrumpido" - -#: spyderlib/widgets/findreplace.py:62 -msgid "Search string" -msgstr "Buscar texto" - -#: spyderlib/widgets/findreplace.py:89 -msgid "Case Sensitive" -msgstr "" -"Distinguir mayúsculas\n" -"de minúsculas" - -#: spyderlib/widgets/findreplace.py:96 -msgid "Whole words" -msgstr "" -"Solamente palabras\n" -"completas" - -#: spyderlib/widgets/findreplace.py:103 -msgid "Highlight matches" -msgstr "Resaltar coincidencias" - -#: spyderlib/widgets/findreplace.py:118 -msgid "Replace with:" -msgstr "Reemplazar con:" - -#: spyderlib/widgets/findreplace.py:120 -msgid "Replace string" -msgstr "Reemplazar texto" - -#: spyderlib/widgets/findreplace.py:123 -msgid "Replace/find" -msgstr "Buscar/reemplazar" - -#: spyderlib/widgets/findreplace.py:132 -msgid "Replace all" -msgstr "Reemplazar todo" - -#: spyderlib/widgets/importwizard.py:111 spyderlib/widgets/importwizard.py:425 -msgid "Import as" -msgstr "Importar como" - -#: spyderlib/widgets/importwizard.py:113 -msgid "data" -msgstr "datos" - -#: spyderlib/widgets/importwizard.py:117 -msgid "code" -msgstr "código" - -#: spyderlib/widgets/importwizard.py:120 spyderlib/widgets/importwizard.py:497 -msgid "text" -msgstr "texto" - -#: spyderlib/widgets/importwizard.py:133 -msgid "Column separator:" -msgstr "Separador de columnas:" - -#: spyderlib/widgets/importwizard.py:137 -msgid "Tab" -msgstr "Tabulación" - -#: spyderlib/widgets/importwizard.py:140 spyderlib/widgets/importwizard.py:159 -msgid "other" -msgstr "otro" - -#: spyderlib/widgets/importwizard.py:152 -msgid "Row separator:" -msgstr "Separador de filas:" - -#: spyderlib/widgets/importwizard.py:156 -msgid "EOL" -msgstr "Fin de línea" - -#: spyderlib/widgets/importwizard.py:172 -msgid "Additional options" -msgstr "Opciones adicionales" - -#: spyderlib/widgets/importwizard.py:176 -msgid "Skip rows:" -msgstr "Saltar filas:" - -#: spyderlib/widgets/importwizard.py:187 -msgid "Comments:" -msgstr "Comentarios:" - -#: spyderlib/widgets/importwizard.py:193 -msgid "Transpose" -msgstr "Trasponer" - -#: spyderlib/widgets/importwizard.py:428 -msgid "array" -msgstr "arreglo" - -#: spyderlib/widgets/importwizard.py:433 -msgid "list" -msgstr "lista" - -#: spyderlib/widgets/importwizard.py:438 -msgid "DataFrame" -msgstr "DataFrame" - -#: spyderlib/widgets/importwizard.py:480 spyderlib/widgets/importwizard.py:567 -msgid "Import wizard" -msgstr "Asistente de importación" - -#: spyderlib/widgets/importwizard.py:485 -msgid "Raw text" -msgstr "Texto sin formato" - -#: spyderlib/widgets/importwizard.py:488 -msgid "variable_name" -msgstr "nombre_de_variable" - -#: spyderlib/widgets/importwizard.py:499 -msgid "table" -msgstr "tabla" - -#: spyderlib/widgets/importwizard.py:500 -msgid "Preview" -msgstr "Vista previa" - -#: spyderlib/widgets/importwizard.py:504 -msgid "Variable Name" -msgstr "Nombre de variable" - -#: spyderlib/widgets/importwizard.py:512 -msgid "Cancel" -msgstr "Cancelar" - -#: spyderlib/widgets/importwizard.py:527 -msgid "Done" -msgstr "Hecho" - -#: spyderlib/widgets/importwizard.py:568 -msgid "" -"Unable to proceed to next step

    Please check your entries." -"

    Error message:
    %s" -msgstr "" -"No fue posible pasar al siguiente paso

    Por favor revise sus " -"daros.

    Mensaje de error:
    %s" - -#: spyderlib/widgets/internalshell.py:252 -msgid "Help..." -msgstr "Ayuda..." - -#: spyderlib/widgets/internalshell.py:259 -msgid "Help" -msgstr "Ayuda" - -#: spyderlib/widgets/internalshell.py:268 -msgid "Shell special commands:" -msgstr "Comandos especiales:" - -#: spyderlib/widgets/internalshell.py:269 -msgid "Internal editor:" -msgstr "Editor interno:" - -#: spyderlib/widgets/internalshell.py:270 -msgid "External editor:" -msgstr "Editor externo:" - -#: spyderlib/widgets/internalshell.py:271 -msgid "Run script:" -msgstr "Ejecutar un archivo:" - -#: spyderlib/widgets/internalshell.py:272 -msgid "Remove references:" -msgstr "Eliminar referencias:" - -#: spyderlib/widgets/internalshell.py:273 -msgid "System commands:" -msgstr "Comandos del sistema:" - -#: spyderlib/widgets/internalshell.py:274 -msgid "Python help:" -msgstr "Ayuda de Python:" - -#: spyderlib/widgets/internalshell.py:275 -msgid "GUI-based editor:" -msgstr "Editor gráfico:" - -#: spyderlib/widgets/ipython.py:495 -msgid "An error ocurred while starting the kernel" -msgstr "Ocurrió un error mientras iniciaba el núcleo" - -#: spyderlib/widgets/ipython.py:523 -msgid "Restart kernel" -msgstr "Reiniciar el núcleo" - -#: spyderlib/widgets/ipython.py:545 -msgid "Stop the current command" -msgstr "Detener el comando actual" - -#: spyderlib/widgets/ipython.py:569 -msgid "Inspect current object" -msgstr "Inspeccionar objeto" - -#: spyderlib/widgets/ipython.py:574 -msgid "Clear line or block" -msgstr "Limpiar línea o bloque" - -#: spyderlib/widgets/ipython.py:578 -msgid "Clear console" -msgstr "Limpiar la terminal" - -#: spyderlib/widgets/ipython.py:623 -msgid "" -"It seems the kernel died unexpectedly. Use 'Restart kernel' to continue " -"using this console." -msgstr "" -"Al parecer el núcleo murió de forma inesperada. Use 'Reiniciar el núcleo' " -"para continuar usando esta terminal." - -#: spyderlib/widgets/ipython.py:639 -msgid "Changing backend to Qt for Mayavi" -msgstr "Cambiando la salida gráfica a Qt por Mayavi" - -#: spyderlib/widgets/ipython.py:648 -msgid "Kernel process is either remote or unspecified. Cannot interrupt" -msgstr "" -"El núcleo es remoto o no está especificado. Por ello no se puede interrumpir." - -#: spyderlib/widgets/ipython.py:657 -msgid "Kernel process is either remote or unspecified. Cannot restart." -msgstr "" -"El núcleo es remoto o no está especificado. Por ello no se puede reiniciar." - -#: spyderlib/widgets/ipython.py:734 -msgid "Connecting to kernel..." -msgstr "Conectándose al núcleo..." - -#: spyderlib/widgets/onecolumntree.py:63 -msgid "Collapse all" -msgstr "Colapsar resultados" - -#: spyderlib/widgets/onecolumntree.py:67 -msgid "Expand all" -msgstr "Expandir resultados" - -#: spyderlib/widgets/onecolumntree.py:71 -msgid "Restore" -msgstr "Restaurar" - -#: spyderlib/widgets/onecolumntree.py:72 -msgid "Restore original tree layout" -msgstr "Restaurar la disposición original" - -#: spyderlib/widgets/onecolumntree.py:76 -msgid "Collapse selection" -msgstr "Colapsar selección" - -#: spyderlib/widgets/onecolumntree.py:80 -msgid "Expand selection" -msgstr "Expandir selección" - -#: spyderlib/widgets/pathmanager.py:84 -msgid "Move to top" -msgstr "Mover al principio" - -#: spyderlib/widgets/pathmanager.py:90 -msgid "Move up" -msgstr "Mover arriba" - -#: spyderlib/widgets/pathmanager.py:96 -msgid "Move down" -msgstr "Mover abajo" - -#: spyderlib/widgets/pathmanager.py:102 -msgid "Move to bottom" -msgstr "Mover al final" - -#: spyderlib/widgets/pathmanager.py:113 spyderlib/widgets/pathmanager.py:225 -msgid "Add path" -msgstr "Añadir ruta" - -#: spyderlib/widgets/pathmanager.py:118 spyderlib/widgets/pathmanager.py:209 -msgid "Remove path" -msgstr "Eliminar ruta" - -#: spyderlib/widgets/pathmanager.py:128 -msgid "Synchronize..." -msgstr "Sincronizar..." - -#: spyderlib/widgets/pathmanager.py:130 -msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" -msgstr "" -"Sincronizar la lista de rutas de Spyder con la variable\n" -"de entorno PYTHONPATH" - -#: spyderlib/widgets/pathmanager.py:141 -msgid "Synchronize" -msgstr "Sincronizar" - -#: spyderlib/widgets/pathmanager.py:142 -msgid "" -"This will synchronize Spyder's path list with PYTHONPATH environment " -"variable for current user, allowing you to run your Python modules outside " -"Spyder without having to configure sys.path.
    Do you want to clear " -"contents of PYTHONPATH before adding Spyder's path list?" -msgstr "" -"Esta acción sincronizará la lista de rutas de Spyder con la variable de " -"entorno PYTHONPATH para el usuario actual, permitiéndole ejecutar sus " -"módulos de Python por fuera de Spyder sin tener que configurar sys.path." -"
    ¿Desea borrar los contenidos del PYTHONPATH antes de añadir la lista de " -"rutas de Spyder?" - -#: spyderlib/widgets/pathmanager.py:210 -msgid "Do you really want to remove selected path?" -msgstr "¿Realmente desea eliminar la ruta seleccionada?" - -#: spyderlib/widgets/pathmanager.py:226 -msgid "" -"This directory is already included in Spyder path list.
    Do you want to " -"move it to the top of the list?" -msgstr "" -"Este directorio ya está incluido en la lista de rutas de Spyder.
    ¿Desea " -"moverlo al principio de la lista?" - -#: spyderlib/widgets/projectexplorer.py:333 -msgid "its own configuration file" -msgstr "su propio archivo de configuración" - -#: spyderlib/widgets/projectexplorer.py:335 -msgid " and " -msgstr " y" - -#: spyderlib/widgets/projectexplorer.py:339 -msgid "the following projects:
    %s" -msgstr "los siguientes proyectos:
    %s" - -#: spyderlib/widgets/projectexplorer.py:541 -msgid "Project..." -msgstr "Proyecto..." - -#: spyderlib/widgets/projectexplorer.py:554 -msgid "Existing directory" -msgstr "Un directorio existente" - -#: spyderlib/widgets/projectexplorer.py:558 -msgid "Existing Spyder project" -msgstr "Un proyecto de Spyder existente" - -#: spyderlib/widgets/projectexplorer.py:562 -msgid "Existing Pydev project" -msgstr "Un proyecto de Pydev existente" - -#: spyderlib/widgets/projectexplorer.py:579 -msgid "Open project" -msgstr "Abrir proyecto" - -#: spyderlib/widgets/projectexplorer.py:584 -msgid "Close project" -msgstr "Cerrar proyecto" - -#: spyderlib/widgets/projectexplorer.py:589 -msgid "Close unrelated projects" -msgstr "Cerrar proyectos no relacionados" - -#: spyderlib/widgets/projectexplorer.py:598 -msgid "Edit related projects" -msgstr "Editar proyectos relacionados" - -#: spyderlib/widgets/projectexplorer.py:606 -msgid "Add to PYTHONPATH" -msgstr "Añadir al PYTHONPATH" - -#: spyderlib/widgets/projectexplorer.py:611 -msgid "Remove from PYTHONPATH" -msgstr "Eliminar del PYTHONPATH" - -#: spyderlib/widgets/projectexplorer.py:616 -msgid "Properties" -msgstr "Propiedades" - -#: spyderlib/widgets/projectexplorer.py:651 -msgid "Show horizontal scrollbar" -msgstr "Mostrar una barra de desplazamiento horizontal" - -#: spyderlib/widgets/projectexplorer.py:684 -msgid "Workspace" -msgstr "Espacio de trabajo" - -#: spyderlib/widgets/projectexplorer.py:685 -msgid "" -"The workspace was unable to load or save %s

    Please check if you have " -"the permission to write the associated configuration files." -msgstr "" -"No fue posible cargar o guardar el espacio de trabajo%s

    Por favor " -"verifique si cuenta con los permisos necesarios para escribir los archivos " -"de configuración del proyecto al disco." - -#: spyderlib/widgets/projectexplorer.py:744 -msgid "Import directory" -msgstr "Importar directorio" - -#: spyderlib/widgets/projectexplorer.py:746 -msgid "" -"The following directory is not in workspace:
    %s

    Do you want " -"to continue (and copy the directory to workspace)?" -msgstr "" -"El siguiente directorio no está en el espacio de trabajo:
    %s

    ¿Desea continuar (y copiar el directorio al espacio de trabajo)?" - -#: spyderlib/widgets/projectexplorer.py:764 -#: spyderlib/widgets/projectexplorer.py:1170 -msgid "copy" -msgstr "copiar" - -#: spyderlib/widgets/projectexplorer.py:816 -msgid "The project %s is already opened!" -msgstr "El proyecto %s ya está abierto!" - -#: spyderlib/widgets/projectexplorer.py:823 -msgid "" -"The project root path directory is inside the workspace but not as the " -"expected tree level. It is not a directory of the workspace:
    %s" -msgstr "" -"La ruta del directorio del proyecto está dentro del espacio de trabajo pero " -"no al nivel esperado de profundidad, pues no es un directorio sino un " -"subdirectorio del espacio de trabajo:
    %s" - -#: spyderlib/widgets/projectexplorer.py:834 -msgid "Project name:" -msgstr "Nombre del proyecto:" - -#: spyderlib/widgets/projectexplorer.py:843 -msgid "A project named %s already exists" -msgstr "Un proyecto llamado %s ya existe" - -#: spyderlib/widgets/projectexplorer.py:848 -msgid "" -"Invalid project name.

    Name must match the following regular " -"expression:
    %s" -msgstr "" -"Nombre de proyecto inválido.

    El nombre debe ajustarse a una expresión " -"del tipo:
    %s" - -#: spyderlib/widgets/projectexplorer.py:855 -msgid "" -"The following directory is not empty:
    %s

    Do you want to " -"continue?" -msgstr "" -"El siguiente directorio no está vacío:
    %s

    ¿Desear continuar?" - -#: spyderlib/widgets/projectexplorer.py:867 -msgid "New project" -msgstr "Nuevo proyecto" - -#: spyderlib/widgets/projectexplorer.py:875 -msgid "" -"The current workspace has not been configured yet.\n" -"Do you want to do this now?" -msgstr "" -"El siguiente espacio de trabajo no ha sido configurado.\n" -"¿Desea hacerlo ahora?" - -#: spyderlib/widgets/projectexplorer.py:912 -msgid "Import existing project" -msgstr "Importar proyecto existente" - -#: spyderlib/widgets/projectexplorer.py:925 -msgid "Select projects to import" -msgstr "Seleccionar proyectos a importar" - -#: spyderlib/widgets/projectexplorer.py:937 -msgid "The folder %s does not contain a valid %s project" -msgstr "La carpeta %s no contiene un proyecto de %s válido" - -#: spyderlib/widgets/projectexplorer.py:965 -msgid "Import existing Pydev project" -msgstr "Importar un proyecto existente de Pydev" - -#: spyderlib/widgets/projectexplorer.py:966 -msgid "" -"Unable to read Pydev project %s

    Error message:
    %s" -msgstr "" -"No fue posible cargar el proyecto de Pydev %s

    Mensaje " -"de error:
    %s" - -#: spyderlib/widgets/projectexplorer.py:1004 -msgid "" -"Do you really want to delete project %s?

    Note: project files " -"won't be deleted from disk." -msgstr "" -"¿Realmente desea eliminar el proyecto %s?

    Nota: Los archivos " -"del proyecto no serán eliminados del disco." - -#: spyderlib/widgets/projectexplorer.py:1057 -msgid "Related projects" -msgstr "Proyectos relacionados" - -#: spyderlib/widgets/projectexplorer.py:1065 -msgid "Select projects which are related to %s" -msgstr "Seleccionar los proyectos que están relacionados a %s" - -#: spyderlib/widgets/projectexplorer.py:1090 -msgid "" -"Statistics on source files only:
    (Python, C/C++, Fortran)

    %s files.
    %s lines of code." -msgstr "" -"Estadísticas para los archivos de código únicamente:
    (Python, C/C++, " -"Fortran)

    %s archivos.
    %s líneas de código." - -#: spyderlib/widgets/projectexplorer.py:1138 -msgid "File %s already exists.
    Do you want to overwrite it?" -msgstr "El archivo %s ya existe.
    ¿Desea sobrescribirlo?" - -#: spyderlib/widgets/projectexplorer.py:1152 -msgid "Folder %s already exists." -msgstr "La carpeta %s ya existe." - -#: spyderlib/widgets/projectexplorer.py:1172 -msgid "move" -msgstr "mover" - -#: spyderlib/widgets/projectexplorer.py:1182 -msgid "Select an existing workspace directory, or create a new one" -msgstr "" -"Seleccionar un directorio existente para que\n" -"sea el espacio de trabajo, o crear uno nuevo" - -#: spyderlib/widgets/projectexplorer.py:1183 -msgid "" -"What is the workspace?

    A Spyder workspace is a " -"directory on your filesystem that contains Spyder projects and ." -"spyderworkspace configuration file.

    A Spyder project is a " -"directory with source code (and other related files) and a configuration " -"file (named .spyderproject) with project settings (PYTHONPATH, linked " -"projects, ...).
    " -msgstr "" -"¿Qué es un espacio de trabajo?

    Un espacio de trabajo " -"de Spyder es un directorio en su sistema de archivos que contiene un " -"proyecto de Spyder y un archivo de configuración .spyderworkspace." -"

    Un proyecto de Spyder es un directorio con código fuente (y " -"otros archivos relacionados) y un archivo de configuración (llamado ." -"spyderproject) con los ajustes del proyecto (PYTHONPATH, proyectos " -"referenciados, etc).
    " - -#: spyderlib/widgets/projectexplorer.py:1210 -msgid "This is the current workspace directory" -msgstr "Este es el directorio actual del espacio de trabajo" - -#: spyderlib/widgets/projectexplorer.py:1241 -msgid "" -"The following directory is not a Spyder workspace:
    %s

    Do you want " -"to create a new workspace in this directory?" -msgstr "" -"El siguiente directorio no es un espacio de trabajo de Spyder:
    " -"%s

    ¿Desea crear un nuevo espacio de trabajo en este directorio?" - -#: spyderlib/widgets/pydocgui.py:107 -msgid "Module or package:" -msgstr "Módulo o paquete:" - -#: spyderlib/widgets/shell.py:126 -msgid "Save history log..." -msgstr "Guardar el historial..." - -#: spyderlib/widgets/shell.py:128 -msgid "Save current history log (i.e. all inputs and outputs) in a text file" -msgstr "" -"Guardar el historial actual (es decir\n" -"todas las entradas y salidas) en un\n" -"archivo de texto" - -#: spyderlib/widgets/shell.py:248 -msgid "Save history log" -msgstr "Guardar el historial" - -#: spyderlib/widgets/shell.py:251 -msgid "History logs" -msgstr "Historiales" - -#: spyderlib/widgets/shell.py:262 -msgid "Unable to save file '%s'

    Error message:
    %s" -msgstr "" -"No fue posible guardar el archivo '%s'

    Mensaje de error:
    %s" - -#: spyderlib/widgets/shell.py:701 -msgid "Copy without prompts" -msgstr "Copiar sin los prompts" - -#: spyderlib/widgets/shell.py:704 spyderlib/widgets/shell.py:708 -msgid "Clear line" -msgstr "Limpiar línea" - -#: spyderlib/widgets/shell.py:710 -msgid "Clear shell" -msgstr "Limpiar la terminal" - -#: spyderlib/widgets/shell.py:714 -msgid "Clear shell contents ('cls' command)" -msgstr "Limpia los contenidos de la terminal (equivalente al comando 'cls')" - -#: spyderlib/widgets/sourcecode/codeeditor.py:88 -msgid "Go to line:" -msgstr "Ir a la línea" - -#: spyderlib/widgets/sourcecode/codeeditor.py:97 -msgid "Line count:" -msgstr "Número total de líneas:" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1210 -msgid "Breakpoint" -msgstr "Punto de interrupción" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1211 -msgid "Condition:" -msgstr "Condición:" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1671 -msgid "To do" -msgstr "To do" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1880 -msgid "Removal error" -msgstr "Error de remoción" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1881 -msgid "" -"It was not possible to remove outputs from this notebook. The error is:\n" -"\n" -msgstr "" -"No fue posible remover las outputs de este notebook. El error es:\n" -"\n" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2296 -msgid "Clear all ouput" -msgstr "Eliminar todas las salidas" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2302 -msgid "Go to definition" -msgstr "Ir a la definición" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2314 -msgid "Zoom reset" -msgstr "Restaurar" - -#: spyderlib/widgets/sourcecode/syntaxhighlighters.py:30 -msgid "Syntax highlighting for Matlab, Julia and other file types" -msgstr "" -"Coloreado del código para archivos tipo Matlab, Julia y varios otros tipos" - -#: spyderlib/widgets/status.py:23 -msgid "CPU and memory usage info in the status bar" -msgstr "Uso de memoria y CPU en la barra de estado" - -#: spyderlib/widgets/status.py:92 -msgid "Memory:" -msgstr "Memoria:" - -#: spyderlib/widgets/status.py:93 -msgid "" -"Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows " -"platforms" -msgstr "" -"Para reportar el uso de memoria se requiere de\n" -"la librería `psutil` (>=0.3) en plataformas distintas\n" -"a Windows" - -#: spyderlib/widgets/status.py:105 -msgid "CPU:" -msgstr "CPU:" - -#: spyderlib/widgets/status.py:106 -msgid "CPU usage status: requires the `psutil` (>=v0.3) library" -msgstr "" -"Para reportar el estado del CPU se requiere\n" -"de la librería `psutil` (>=v0.3)" - -#: spyderlib/widgets/status.py:128 -msgid "Permissions:" -msgstr "Permisos:" - -#: spyderlib/widgets/status.py:142 -msgid "End-of-lines:" -msgstr "Fin de línea:" - -#: spyderlib/widgets/status.py:156 -msgid "Encoding:" -msgstr "Codificación:" - -#: spyderlib/widgets/status.py:169 -msgid "Line:" -msgstr "Línea:" - -#: spyderlib/widgets/status.py:173 -msgid "Column:" -msgstr "Columna:" - -#: spyderlib/widgets/tabs.py:137 -msgid "Browse tabs" -msgstr "" -"Navegar por\n" -"las pestañas" - -#: spyderlib/widgets/tabs.py:260 -msgid "Close current tab" -msgstr "Cerrar pestaña actual" - -#: spyderlib/widgets/texteditor.py:72 -msgid "Text editor" -msgstr "Editor de texto" - -#~ msgid "" -#~ "%s will be closed.\n" -#~ "Do you want to kill the associated kernel and all of its clients?" -#~ msgstr "" -#~ "La %s será cerrada.\n" -#~ "Desea cerrar el núcleo asociado y todos sus clientes?" - -#~ msgid "Install Spyder's input hook for Qt" -#~ msgstr "Instalar el \"input hook\" de Spyder para Qt" - -#~ msgid "" -#~ "PyQt installs an input hook that allows
    creating and interacting with " -#~ "Qt widgets in an interactive console without blocking it. On Windows " -#~ "platforms, it is strongly recommended to replace it by Spyder's. " -#~ "Regarding PySide, note that it does not install an input hook, so it is " -#~ "required to enable this feature in order to be able to manipulate PySide/" -#~ "Qtobjects interactively." -#~ msgstr "" -#~ "PyQt instala un mecanismo de \"input hook\"
    que permite crear e " -#~ "interactuar con widgets de Qt en una terminal sin bloquearla. En Windows, " -#~ "es altamente recomendable reemplazar este mecanismo por el de Spyder. Con " -#~ "respecto a PySide, debe decirse que no instala un \"inputhook\", así que " -#~ "se requiere activar esta característica para poder manipular objectos de " -#~ "PySide/Qt interactivamente." - -#~ msgid "Could not open ssh tunnel\n" -#~ msgstr "No fue posible crear un túnel ssh\n" - -#~ msgid "Mismatch between kernel and frontend" -#~ msgstr "Incompatibilidad entre el núcleo y la interfaz gráfica" - -#~ msgid "" -#~ "Your IPython frontend and kernel versions are incompatible!!

    We're sorry but we can't create an IPython console for you." -#~ msgstr "" -#~ "Sus versiones del núcleo y la interfaz gráfica de IPython son " -#~ "incompatibles!!

    Lo lamentamos pero no podemos crear una " -#~ "terminal de IPython para usted." - -#~ msgid "Always edit in-place" -#~ msgstr "Siempre editar en línea" - -#~ msgid "Show collection contents" -#~ msgstr "Mostrar contenidos de listas" - -#~ msgid "Show toolbar" -#~ msgstr "Mostrar barra de herramientas" - -#~ msgid "" -#~ "Resizing cells of a table of such size could take a long time.\n" -#~ "Do you want to continue anyway?" -#~ msgstr "" -#~ "Redimensionar las celdas de una tabla de este tamaño puede\n" -#~ "tomar mucho tiempo.\n" -#~ "\n" -#~ "¿Desea continuar de todas formas?" - -#~ msgid "Interrupt kernel" -#~ msgstr "Interrumpir el núcleo" - -#~ msgid "Run &selection" -#~ msgstr "Ejecutar &selección" - -#~ msgid "Patch Matplotlib figures" -#~ msgstr "Parchear las gráficas de Matplotlib" - -#~ msgid "" -#~ "Patching Matplotlib library will add a button to customize figure options " -#~ "(Qt4Agg only) and fix some issues." -#~ msgstr "" -#~ "Al aplicar este parche a la librería Matplotlib se añadirá un botón (sólo " -#~ "con\n" -#~ "el backend Qt4Agg) para ajustar las opciones de curvas y gráficas y para\n" -#~ "corregir algunos problemas." - -#~ msgid "(for example: kernel-3764.json, or simply 3764)" -#~ msgstr "(por ejemplo: `kernel-3764.json`, o simplemente `3764`)" - -#~ msgid "Provide an IPython kernel connection file:" -#~ msgstr "Provea un archivo de conexión para un núcleo de IPython" - -#~ msgid "Interpreter" -#~ msgstr "Intérprete" - -#~ msgid "Dedicated Python interpreter" -#~ msgstr "Intérprete de Python dedicado" - -#~ msgid "Open Python interpreter here" -#~ msgstr "Abrir intérprete de Python aquí" - -#~ msgid "Import as array" -#~ msgstr "Importar como arreglo" - -#~ msgid "(not installed)" -#~ msgstr "(no está instalado)" - -#~ msgid "" -#~ "This feature requires the pywin32 module.\n" -#~ "It seems you don't have it installed." -#~ msgstr "" -#~ "Esta característica requiere la librería pywin32.\n" -#~ "Al parecer no la tiene instalada." - -#~ msgid "Balloon tips" -#~ msgstr "Mostrar globos de sugerencias" - -#~ msgid "Show single completion" -#~ msgstr "" -#~ "Mostrar la lista de resultados a completar aún con una sola elección" - -#~ msgid "Automatic notification to object inspector" -#~ msgstr "Notificación automática al Inspector de objetos" - -#~ msgid "" -#~ "If this option is enabled, object inspector\n" -#~ "will automatically show informations on functions\n" -#~ "entered in editor (this is triggered when entering\n" -#~ "a left parenthesis after a valid function name)" -#~ msgstr "" -#~ "Si esta opción está activada, el Inspector de\n" -#~ "objetos mostrará automáticamente información\n" -#~ "sobre las funciones introducidas en el Editor\n" -#~ "(esto se activa cuando se introduce un paréntesis\n" -#~ "izquierdo después de un nombre de función válido" - -#~ msgid "" -#~ "If this option is enabled, object inspector\n" -#~ "will automatically show informations on functions\n" -#~ "entered in console (this is triggered when entering\n" -#~ "a left parenthesis after a valid function name)" -#~ msgstr "" -#~ "Si esta opción está activada, el Inspector de\n" -#~ "objetos mostrará automáticamente información\n" -#~ "sobre funciones introducidas en la Terminal \n" -#~ "(esto se activa cuando se introduce un paréntesis\n" -#~ "izquierdo después de un nombre de función válido)" - -#~ msgid "Open a Python interpreter at startup" -#~ msgstr "Abrir un intérprete de Python al inicio" - -#~ msgid "Open a Python &interpreter" -#~ msgstr "Abrir un &intérprete de Python" - -#~ msgid "
    Installed version: %s" -#~ msgstr "
    Versión instalada: %s" - -#~ msgid "" -#~ "Unable to open IPython console because no supported IPython version was " -#~ "found.

    Supported IPython versions: %s" -#~ msgstr "" -#~ "No es posible abrir un intérprete de IPython porque no se encontró " -#~ "ninguna versión de IPython soportada por Spyder.

    Versiones " -#~ "soportadas son: %s" - -#~ msgid "Open an IPython console at startup" -#~ msgstr "Abrir una terminal de IPython al inicio" - -#~ msgid "IPython Help" -#~ msgstr "Ayuda de IPython" - -#~ msgid "Close current dockwidget" -#~ msgstr "Cerrar el componente actual" - -#~ msgid "&Interpreters" -#~ msgstr "&Intérpretes" - -#~ msgid "Qt Assistant" -#~ msgstr "Documentación de Qt" - -#~ msgid "Web Resources" -#~ msgstr "Recursos en la Web" - -#~ msgid "Windows and toolbars" -#~ msgstr "Componentes y barras de herramientas" - -#~ msgid "Documentation" -#~ msgstr "Documentación" - -#~ msgid "Create a new Python script" -#~ msgstr "Crear un nuevo archivo de Python" - -#~ msgid "Open text file" -#~ msgstr "Abrir archivo de texto" - -#~ msgid "Save current file" -#~ msgstr "Guardar el archivo actual" - -#~ msgid "" -#~ "Run selection or current \n" -#~ "block of lines" -#~ msgstr "" -#~ "Ejecutar la selección \n" -#~ "o el bloque actual" - -#~ msgid "Open Spyder path manager" -#~ msgstr "Abrir el administrador de rutas de Spyder" - -#~ msgid "Maximize current plugin to fit the whole application window" -#~ msgstr "" -#~ "Maximizar el componente actual\n" -#~ "para que ocupe toda la ventana" - -#~ msgid "" -#~ "Restore current plugin to its original size and position within the " -#~ "application window" -#~ msgstr "" -#~ "Restaurar el componente actual\n" -#~ "a su tamaño y posición originales\n" -#~ "dentro de la ventana principal" - -#~ msgid "Step Over" -#~ msgstr "Saltar paso" - -#~ msgid "Debug Step Over" -#~ msgstr "Depurar - Saltar paso" - -#~ msgid "Debug Continue" -#~ msgstr "Continuar depurando" - -#~ msgid "Debug Step Into" -#~ msgstr "Depurar - Ingresar en el paso" - -#~ msgid "Debug Step Return" -#~ msgstr "Depurar - Salir del paso" - -#~ msgid "Run active script in a new Python interpreter" -#~ msgstr "Ejecutar el archivo actual en un nuevo intérprete de Python" - -#~ msgid "" -#~ "Debug current script in external console\n" -#~ "(external console is executed in a separate process)" -#~ msgstr "" -#~ "Depurar el archivo actual en una terminal\n" -#~ "externa, la cual es ejecutada en proceso\n" -#~ "separado" - -#~ msgid "Edit run configurations" -#~ msgstr "Editar las opciones de ejecución" - -#~ msgid "Run again last script in external console with the same options" -#~ msgstr "" -#~ "Ejecutar de nuevo el último archivo en un\n" -#~ "intérprete externo con las mismas opciones" - -#~ msgid "" -#~ "Run selected text or current block of lines \n" -#~ "inside current external console's interpreter" -#~ msgstr "" -#~ "Ejecutar el texto seleccionado o el bloque\n" -#~ "de líneas actual dentro del intérprete en uso" - -#~ msgid "Run %s" -#~ msgstr "Ejecutar %s" - -#~ msgid "Run configurations" -#~ msgstr "Opciones de ejecución" - -#~ msgid "Type \"copyright\", \"credits\" or \"license\" for more information." -#~ msgstr "" -#~ "Escriba \"copyright\", \"credits\" o \"license\" para más información." - -#~ msgid "Start an IPython kernel at startup" -#~ msgstr "Abrir un núcleo de IPython al inicio" - -#~ msgid "This option is not available for IPython versions prior to v0.12." -#~ msgstr "" -#~ "Esta opción no está disponible para versiones de IPython\n" -#~ "anteriores a la 0.12" - -#~ msgid "Format: " -#~ msgstr "Formato:" - -#, fuzzy -#~ msgid " Source" -#~ msgstr "Origen" - -#~ msgid "Builtin Modules: spy.app, spy.window" -#~ msgstr "Módulos integrados: spy.app, spy.window" - -#~ msgid "Open &interpreter" -#~ msgstr "Abrir un &intérprete de Python" - -#~ msgid "Set the appropriate IPython color option" -#~ msgstr "Ajustar la opción apropiada de color para IPython" - -#~ msgid "IPython interpreter command line options" -#~ msgstr "Opciones de línea de comandos del intérprete de IPython" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/locale/fr/LC_MESSAGES/spyderlib.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/locale/fr/LC_MESSAGES/spyderlib.mo differ diff -Nru spyder-2.3.8+dfsg1/spyderlib/locale/fr/LC_MESSAGES/spyderlib.po spyder-3.0.2+dfsg1/spyderlib/locale/fr/LC_MESSAGES/spyderlib.po --- spyder-2.3.8+dfsg1/spyderlib/locale/fr/LC_MESSAGES/spyderlib.po 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/locale/fr/LC_MESSAGES/spyderlib.po 1970-01-01 00:00:00.000000000 +0000 @@ -1,5554 +0,0 @@ -# -*- coding: utf-8 -*- -# Spyder's french translation file -# Copyright (C) 2009-2011 Pierre Raybaut -# -msgid "" -msgstr "" -"Project-Id-Version: 2.1\n" -"POT-Creation-Date: 2015-11-24 20:56+COT\n" -"PO-Revision-Date: 2015-03-16 22:59-0500\n" -"Last-Translator: Sylvain Corlay \n" -"Language-Team: Python\n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: pygettext.py 1.5\n" -"X-Generator: Poedit 1.5.4\n" - -#: spyderlib/config.py:29 -msgid "Python files" -msgstr "Fichiers Python" - -#: spyderlib/config.py:30 -msgid "Cython/Pyrex files" -msgstr "Fichiers Cython/Pyrex" - -#: spyderlib/config.py:31 -msgid "C files" -msgstr "Fichiers C" - -#: spyderlib/config.py:32 -msgid "C++ files" -msgstr "Fichiers C++" - -#: spyderlib/config.py:33 -msgid "OpenCL files" -msgstr "Fichiers OpenCL" - -#: spyderlib/config.py:34 -msgid "Fortran files" -msgstr "Fichiers Fortran" - -#: spyderlib/config.py:35 -msgid "IDL files" -msgstr "Fichiers IDL" - -#: spyderlib/config.py:36 -msgid "MATLAB files" -msgstr "Fichiers MATLAB" - -#: spyderlib/config.py:37 -msgid "Julia files" -msgstr "Fichiers Julia" - -#: spyderlib/config.py:38 -msgid "Yaml files" -msgstr "Fichiers Yaml" - -#: spyderlib/config.py:39 -msgid "Patch and diff files" -msgstr "Fichiers patch et diff" - -#: spyderlib/config.py:40 -msgid "Batch files" -msgstr "Fichiers Batch" - -#: spyderlib/config.py:41 spyderlib/utils/iofuncs.py:514 -msgid "Text files" -msgstr "Fichiers texte" - -#: spyderlib/config.py:42 -msgid "reStructured Text files" -msgstr "Fichiers reST" - -#: spyderlib/config.py:43 -msgid "gettext files" -msgstr "Fichiers gettext" - -#: spyderlib/config.py:44 -msgid "NSIS files" -msgstr "Fichiers NSIS" - -#: spyderlib/config.py:45 -msgid "Web page files" -msgstr "Fichiers web" - -#: spyderlib/config.py:46 -msgid "XML files" -msgstr "Fichiers XML" - -#: spyderlib/config.py:47 -msgid "Javascript files" -msgstr "Fichiers Javascript" - -#: spyderlib/config.py:48 -msgid "Json files" -msgstr "Fichiers Json" - -#: spyderlib/config.py:49 -msgid "IPython notebooks" -msgstr "Documents IPython " - -#: spyderlib/config.py:50 -msgid "Enaml files" -msgstr "Fichiers Enaml" - -#: spyderlib/config.py:51 -msgid "Configuration files" -msgstr "Configurations" - -#: spyderlib/config.py:58 spyderlib/widgets/explorer.py:651 -msgid "All files" -msgstr "Tous les fichiers" - -#: spyderlib/ipythonconfig.py:23 spyderlib/ipythonconfig.py:25 -#: spyderlib/ipythonconfig.py:32 -msgid "IPython Console integration" -msgstr "Intégration de la console IPython" - -#: spyderlib/plugins/__init__.py:318 spyderlib/plugins/editor.py:94 -#: spyderlib/plugins/editor.py:527 spyderlib/plugins/editor.py:1606 -#: spyderlib/plugins/inspector.py:134 spyderlib/plugins/inspector.py:403 -#: spyderlib/widgets/editor.py:434 -#: spyderlib/widgets/sourcecode/codeeditor.py:85 -#: spyderlib/widgets/sourcecode/codeeditor.py:2709 -msgid "Editor" -msgstr "Éditeur" - -#: spyderlib/plugins/configdialog.py:144 -msgid "Preferences" -msgstr "Préférences" - -#: spyderlib/plugins/configdialog.py:429 -msgid "Invalid directory path" -msgstr "Chemin d'accès de répertoire incorrect" - -#: spyderlib/plugins/configdialog.py:432 spyderlib/plugins/configdialog.py:448 -#: spyderlib/plugins/runconfig.py:172 spyderlib/plugins/runconfig.py:236 -#: spyderlib/plugins/workingdirectory.py:286 spyderlib/widgets/explorer.py:565 -#: spyderlib/widgets/externalshell/pythonshell.py:623 -#: spyderlib/widgets/findinfiles.py:504 spyderlib/widgets/pathmanager.py:218 -#: spyderlib/widgets/projectexplorer.py:890 -msgid "Select directory" -msgstr "Sélectionner un répertoire" - -#: spyderlib/plugins/configdialog.py:460 -msgid "Invalid file path" -msgstr "Chemin d'accès de fichier incorrect" - -#: spyderlib/plugins/configdialog.py:463 spyderlib/plugins/configdialog.py:481 -msgid "Select file" -msgstr "Sélectionner un fichier" - -#: spyderlib/plugins/configdialog.py:480 -msgid "All files (*)" -msgstr "Tous les fichiers (*)" - -#: spyderlib/plugins/configdialog.py:550 spyderlib/widgets/formlayout.py:216 -msgid "Bold" -msgstr "Gras" - -#: spyderlib/plugins/configdialog.py:553 spyderlib/widgets/formlayout.py:211 -msgid "Italic" -msgstr "Italique" - -#: spyderlib/plugins/configdialog.py:591 -msgid "Font: " -msgstr "Police : " - -#: spyderlib/plugins/configdialog.py:595 -msgid "Size: " -msgstr "Taille : " - -#: spyderlib/plugins/configdialog.py:604 spyderlib/plugins/history.py:47 -msgid "Font style" -msgstr "Police d'écriture" - -#: spyderlib/plugins/configdialog.py:657 -msgid "General" -msgstr "Général" - -#: spyderlib/plugins/configdialog.py:664 spyderlib/plugins/editor.py:103 -#: spyderlib/plugins/externalconsole.py:65 -#: spyderlib/plugins/ipythonconsole.py:161 -msgid "Interface" -msgstr "Interface" - -#: spyderlib/plugins/configdialog.py:672 -msgid "Qt windows style" -msgstr "Style de fenêtres Qt" - -#: spyderlib/plugins/configdialog.py:676 -msgid "Use a single instance" -msgstr "Ouvrir une seule instance de Spyder" - -#: spyderlib/plugins/configdialog.py:678 -msgid "" -"Set this to open external
    Python files in an already running instance " -"(Requires a restart)" -msgstr "" -"Activer cette option afin que les fichiers ouverts
    depuis l'extérieur " -"soit affichés dans une instance unique de Spyder
    (redémarrage requis)" - -#: spyderlib/plugins/configdialog.py:681 -msgid "Vertical dockwidget title bars" -msgstr "Barres de titre orientées verticalement" - -#: spyderlib/plugins/configdialog.py:683 -msgid "Vertical dockwidget tabs" -msgstr "Onglets distribués verticalement" - -#: spyderlib/plugins/configdialog.py:685 -msgid "Animated toolbars and dockwidgets" -msgstr "Panneaux et barres d'outils animés" - -#: spyderlib/plugins/configdialog.py:687 -msgid "Tear off menus" -msgstr "Menus détachables" - -#: spyderlib/plugins/configdialog.py:688 -msgid "Set this to detach any
    menu from the main window" -msgstr "" -"Activer cette option rend détachables tous les menus de la fenêtre principale" - -#: spyderlib/plugins/configdialog.py:690 -msgid "Custom dockwidget margin:" -msgstr "Marges personnalisées :" - -#: spyderlib/plugins/configdialog.py:717 -msgid "Status bar" -msgstr "Barre d'état" - -#: spyderlib/plugins/configdialog.py:718 -msgid "Show memory usage every" -msgstr "Afficher l'occupation mémoire toutes les" - -#: spyderlib/plugins/configdialog.py:729 -msgid "Show CPU usage every" -msgstr "Afficher la charge du CPU toutes les" - -#: spyderlib/plugins/configdialog.py:746 -msgid "Debugging" -msgstr "Débogage" - -#: spyderlib/plugins/configdialog.py:747 -msgid "Pop up internal console when internal errors appear" -msgstr "Afficher la console interne en cas d'erreur inattendue" - -#: spyderlib/plugins/configdialog.py:769 -msgid "Syntax coloring" -msgstr "Coloration syntaxique" - -#: spyderlib/plugins/configdialog.py:778 -msgid "Background:" -msgstr "Fond :" - -#: spyderlib/plugins/configdialog.py:779 -#: spyderlib/widgets/sourcecode/codeeditor.py:95 -msgid "Current line:" -msgstr "Ligne actuelle :" - -#: spyderlib/plugins/configdialog.py:780 -msgid "Current cell:" -msgstr "Cellule actuelle :" - -#: spyderlib/plugins/configdialog.py:781 -msgid "Occurence:" -msgstr "Occurence :" - -#: spyderlib/plugins/configdialog.py:782 -msgid "Link:" -msgstr "Lien :" - -#: spyderlib/plugins/configdialog.py:783 -msgid "Side areas:" -msgstr "Zones latérales :" - -#: spyderlib/plugins/configdialog.py:784 -msgid "Matched parentheses:" -msgstr "Parenthèse fermée :" - -#: spyderlib/plugins/configdialog.py:785 -msgid "Unmatched parentheses:" -msgstr "Parenthèse non fermée :" - -#: spyderlib/plugins/configdialog.py:786 -msgid "Normal text:" -msgstr "Texte normal :" - -#: spyderlib/plugins/configdialog.py:787 -msgid "Keyword:" -msgstr "Mot-clé :" - -#: spyderlib/plugins/configdialog.py:788 -msgid "Builtin:" -msgstr "Objet intégré :" - -#: spyderlib/plugins/configdialog.py:789 -msgid "Definition:" -msgstr "Définition :" - -#: spyderlib/plugins/configdialog.py:790 -msgid "Comment:" -msgstr "Commentaire :" - -#: spyderlib/plugins/configdialog.py:791 -msgid "String:" -msgstr "Chaîne :" - -#: spyderlib/plugins/configdialog.py:792 -msgid "Number:" -msgstr "Nombre :" - -#: spyderlib/plugins/configdialog.py:793 -msgid "Instance:" -msgstr "Instance :" - -#: spyderlib/plugins/configdialog.py:799 -msgid "Color scheme" -msgstr "Paramètres de coloration syntaxique" - -#: spyderlib/plugins/configdialog.py:821 spyderlib/plugins/shortcuts.py:344 -msgid "Reset to default values" -msgstr "Rétablir les valeurs par défaut" - -#: spyderlib/plugins/console.py:105 -msgid "Internal console" -msgstr "Console interne" - -#: spyderlib/plugins/console.py:125 spyderlib/spyder.py:772 -#: spyderlib/widgets/ipython.py:583 -msgid "&Quit" -msgstr "&Quitter" - -#: spyderlib/plugins/console.py:126 spyderlib/spyder.py:773 -msgid "Quit" -msgstr "Quitter" - -#: spyderlib/plugins/console.py:129 spyderlib/plugins/externalconsole.py:1099 -msgid "&Run..." -msgstr "Exécute&r..." - -#: spyderlib/plugins/console.py:130 spyderlib/plugins/externalconsole.py:1100 -msgid "Run a Python script" -msgstr "Exécuter un script Python" - -#: spyderlib/plugins/console.py:133 -msgid "Environment variables..." -msgstr "Variables d'environnement..." - -#: spyderlib/plugins/console.py:135 -msgid "Show and edit environment variables (for current session)" -msgstr "" -"Afficher et modifier les variables d'environnement (pour la session en cours)" - -#: spyderlib/plugins/console.py:139 -msgid "Show sys.path contents..." -msgstr "Afficher le contenu de sys.path..." - -#: spyderlib/plugins/console.py:141 -msgid "Show (read-only) sys.path" -msgstr "Afficher le contenu de sys.path (lecture seule)" - -#: spyderlib/plugins/console.py:144 -msgid "Buffer..." -msgstr "Tampon..." - -#: spyderlib/plugins/console.py:145 spyderlib/plugins/externalconsole.py:85 -#: spyderlib/plugins/history.py:40 -msgid "Set maximum line count" -msgstr "Modifier le nombre maximum de lignes" - -#: spyderlib/plugins/console.py:148 spyderlib/plugins/explorer.py:57 -#: spyderlib/plugins/history.py:164 spyderlib/plugins/inspector.py:372 -#: spyderlib/plugins/projectexplorer.py:56 -msgid "&Font..." -msgstr "&Police..." - -#: spyderlib/plugins/console.py:149 spyderlib/plugins/history.py:165 -msgid "Set shell font style" -msgstr "Changer la police d'écriture de la console" - -#: spyderlib/plugins/console.py:152 -msgid "External editor path..." -msgstr "Éditeur externe..." - -#: spyderlib/plugins/console.py:153 -msgid "Set external editor executable path" -msgstr "Modifier le chemin d'accès de l'éditeur externe" - -#: spyderlib/plugins/console.py:156 spyderlib/plugins/editor.py:144 -#: spyderlib/plugins/externalconsole.py:86 spyderlib/plugins/history.py:43 -#: spyderlib/plugins/history.py:167 spyderlib/plugins/inspector.py:175 -#: spyderlib/plugins/inspector.py:375 -msgid "Wrap lines" -msgstr "Retour à la ligne automatique" - -#: spyderlib/plugins/console.py:159 spyderlib/plugins/editor.py:178 -#: spyderlib/plugins/externalconsole.py:133 -#: spyderlib/plugins/ipythonconsole.py:175 -msgid "Display balloon tips" -msgstr "Afficher des info-bulles" - -#: spyderlib/plugins/console.py:163 spyderlib/plugins/editor.py:172 -#: spyderlib/plugins/externalconsole.py:127 -msgid "Automatic code completion" -msgstr "Complétion de code automatique" - -#: spyderlib/plugins/console.py:167 spyderlib/plugins/editor.py:176 -#: spyderlib/plugins/externalconsole.py:131 -msgid "Enter key selects completion" -msgstr "Entrée valide la complétion de code" - -#: spyderlib/plugins/console.py:172 -msgid "Internal console settings" -msgstr "Options de la console interne" - -#: spyderlib/plugins/console.py:223 spyderlib/plugins/externalconsole.py:1285 -msgid "Run Python script" -msgstr "Exécuter un script Python" - -#: spyderlib/plugins/console.py:224 spyderlib/plugins/externalconsole.py:229 -#: spyderlib/plugins/externalconsole.py:1286 spyderlib/widgets/explorer.py:666 -msgid "Python scripts" -msgstr "Scripts Python" - -#: spyderlib/plugins/console.py:269 spyderlib/plugins/explorer.py:109 -#: spyderlib/plugins/history.py:282 spyderlib/plugins/inspector.py:651 -#: spyderlib/plugins/projectexplorer.py:118 -msgid "Select a new font" -msgstr "Sélectionner une police d'écriture" - -#: spyderlib/plugins/console.py:276 -msgid "Buffer" -msgstr "Tampon" - -#: spyderlib/plugins/console.py:277 -msgid "Maximum line count" -msgstr "Nombre maximum de lignes" - -#: spyderlib/plugins/console.py:286 -msgid "External editor" -msgstr "Éditeur externe" - -#: spyderlib/plugins/console.py:287 -msgid "External editor executable path:" -msgstr "Chemin d'accès de l'exécutable :" - -#: spyderlib/plugins/editor.py:100 -msgid "Edit template for new modules" -msgstr "Modifier le modèle (nouveaux modules)" - -#: spyderlib/plugins/editor.py:105 -msgid "Text and margin font style" -msgstr "Police d'écriture du texte et de la marge" - -#: spyderlib/plugins/editor.py:108 -msgid "Sort files according to full path" -msgstr "Classer les fichiers suivant leur chemin complet" - -#: spyderlib/plugins/editor.py:110 -msgid "Show tab bar" -msgstr "Afficher la barre d'onglets" - -#: spyderlib/plugins/editor.py:117 spyderlib/plugins/editor.py:192 -#: spyderlib/plugins/externalconsole.py:81 -#: spyderlib/plugins/externalconsole.py:126 spyderlib/plugins/history.py:42 -#: spyderlib/plugins/inspector.py:174 spyderlib/plugins/ipythonconsole.py:199 -msgid "Source code" -msgstr "Code source" - -#: spyderlib/plugins/editor.py:118 -msgid "Show line numbers" -msgstr "Afficher les numéros de ligne" - -#: spyderlib/plugins/editor.py:119 spyderlib/plugins/editor.py:892 -msgid "Show blank spaces" -msgstr "Afficher les espaces" - -#: spyderlib/plugins/editor.py:120 -msgid "Show vertical line after" -msgstr "Afficher une ligne verticale après" - -#: spyderlib/plugins/editor.py:121 -msgid "characters" -msgstr "caractères" - -#: spyderlib/plugins/editor.py:129 -msgid "Highlight current line" -msgstr "Surligner la ligne en cours d'édition" - -#: spyderlib/plugins/editor.py:131 -msgid "Highlight current cell" -msgstr "Surligner la cellule en cours d'édition" - -#: spyderlib/plugins/editor.py:133 -msgid "Highlight occurences after" -msgstr "Surligner les occurences après" - -#: spyderlib/plugins/editor.py:147 spyderlib/plugins/history.py:51 -#: spyderlib/plugins/inspector.py:178 -msgid "Syntax color scheme: " -msgstr "Thème de coloration syntaxique : " - -#: spyderlib/plugins/editor.py:161 spyderlib/plugins/runconfig.py:313 -#: spyderlib/plugins/runconfig.py:435 spyderlib/plugins/runconfig.py:440 -#: spyderlib/spyder.py:1850 spyderlib/utils/programs.py:175 -#: spyderlib/widgets/explorer.py:234 -#: spyderlib/widgets/externalshell/baseshell.py:138 -msgid "Run" -msgstr "Exécuter" - -#: spyderlib/plugins/editor.py:162 -msgid "Save all files before running script" -msgstr "Enregistrer tous les fichiers avant l'exécution d'un script" - -#: spyderlib/plugins/editor.py:165 -msgid "Run selection" -msgstr "Exécuter la sélection" - -#: spyderlib/plugins/editor.py:166 -msgid "Maintain focus in the Editor after running cells or selections" -msgstr "" -"Garder le focus dans l'éditeur après l'exécution d'une cellule ou d'une " -"sélection" - -#: spyderlib/plugins/editor.py:169 spyderlib/plugins/externalconsole.py:365 -msgid "Introspection" -msgstr "Introspection" - -#: spyderlib/plugins/editor.py:174 spyderlib/plugins/externalconsole.py:129 -msgid "Case sensitive code completion" -msgstr "Complétion de code sensible à la casse" - -#: spyderlib/plugins/editor.py:179 -msgid "Link to object definition" -msgstr "Lien vers la définition d'un objet" - -#: spyderlib/plugins/editor.py:181 -msgid "" -"If this option is enabled, clicking on an object\n" -"name (left-click + Ctrl key) will go this object\n" -"definition (if resolved)." -msgstr "" -"Si cette option est activée, cliquer sur le nom\n" -"d'un objet (click gauche + touche Ctrl) ira à la\n" -"définition de cet objet (si celle-ci est trouvée)." - -#: spyderlib/plugins/editor.py:185 -msgid "" -"Warning:
    The Python module rope is not installed on this " -"computer: calltips, code completion and go-to-definition features won't be " -"available." -msgstr "" -"Avertissement :
    Le module Python rope n'est pas installé " -"sur cet ordinateur : les fonctionnalités telles que la complétion de code ou " -"le lien vers la définition d'un objet ne sont donc pas accessibles." - -#: spyderlib/plugins/editor.py:193 -msgid "Automatic insertion of parentheses, braces and brackets" -msgstr "Insertion automatique des parenthèses, crochets et accolades" - -#: spyderlib/plugins/editor.py:196 -msgid "Automatic insertion of closing quotes" -msgstr "Insertion automatique de guillemets de clôture" - -#: spyderlib/plugins/editor.py:198 -msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" -msgstr "Insertion automatique de ':' après 'for', 'if', 'def', etc." - -#: spyderlib/plugins/editor.py:201 -msgid "Automatic indentation after 'else', 'elif', etc." -msgstr "Indentation automatique après 'else', 'elif', etc." - -#: spyderlib/plugins/editor.py:203 -msgid "Indentation characters: " -msgstr "Caractères d'indentation : " - -#: spyderlib/plugins/editor.py:204 -msgid "4 spaces" -msgstr "4 espaces" - -#: spyderlib/plugins/editor.py:205 -msgid "2 spaces" -msgstr "2 espaces" - -#: spyderlib/plugins/editor.py:206 -msgid "tab" -msgstr "tabulation" - -#: spyderlib/plugins/editor.py:207 -msgid "Tab stop width:" -msgstr "Largeur des tabulations :" - -#: spyderlib/plugins/editor.py:207 -msgid "pixels" -msgstr "pixels" - -#: spyderlib/plugins/editor.py:209 -msgid "Tab always indent" -msgstr "Toujours indenter avec la touche Tab" - -#: spyderlib/plugins/editor.py:211 -msgid "" -"If enabled, pressing Tab will always indent,\n" -"even when the cursor is not at the beginning\n" -"of a line (when this option is enabled, code\n" -"completion may be triggered using the alternate\n" -"shortcut: Ctrl+Space)" -msgstr "" -"Si cette option est activée, presser la touche Tab\n" -"provoquera toujours l'indentation de la ligne,\n" -"quelle que soit la position du curseur (lorsque cette\n" -"option est activée, la complétion de code reste \n" -"accessible via le raccourci Ctrl+Espace)" - -#: spyderlib/plugins/editor.py:216 -msgid "Intelligent backspace" -msgstr "Retour arrière (\"backspace\") intelligent" - -#: spyderlib/plugins/editor.py:218 -msgid "Automatically remove trailing spaces when saving files" -msgstr "" -"Supprimer automatiquement les espaces en fin de ligne lors de " -"l'enregistrement" - -#: spyderlib/plugins/editor.py:222 -msgid "Analysis" -msgstr "Analyse" - -#: spyderlib/plugins/editor.py:224 -msgid "" -"Note: add analysis:ignore in a comment to ignore code/style " -"analysis warnings. For more informations on style guide for Python code, " -"please refer to the %s page." -msgstr "" -"Note: ajouter analysis:ignore dans un commentaire pour ignorer " -"les résultats de l'analyse de code ou de style. Pour plus d'informations sur " -"les recommandations officielles de style d'écriture avec le langage Python, " -"veuillez visiter la page de la %s." - -#: spyderlib/plugins/editor.py:233 -#: spyderlib/widgets/sourcecode/codeeditor.py:1617 -msgid "Code analysis" -msgstr "Analyse de code" - -#: spyderlib/plugins/editor.py:235 -msgid "" -"If enabled, Python source code will be analyzed\n" -"using pyflakes, lines containing errors or \n" -"warnings will be highlighted" -msgstr "" -"Si cette option est activée, le code source Python sera analysé\n" -"avec des outils l'outil d'introspection de code pyflakes\n" -"et les lignes contenant des erreurs seront indiquées" - -#: spyderlib/plugins/editor.py:240 -msgid "Code analysis requires pyflakes %s+" -msgstr "L'analyse de code requiert pyflakes %s+" - -#: spyderlib/plugins/editor.py:242 -msgid "Style analysis" -msgstr "Analyse de style" - -#: spyderlib/plugins/editor.py:244 -msgid "" -"If enabled, Python source code will be analyzed\n" -"using pep8, lines that are not following PEP8\n" -"style guide will be highlighted" -msgstr "" -"Si cette option est activée, le style du code source Python sera analysé\n" -"avec l'outil d'introspection de code pep8 et les lignes ne suivant pas\n" -"les recommandations officielles seront indiquées" - -#: spyderlib/plugins/editor.py:251 -msgid "Tasks (TODO, FIXME, XXX, HINT, TIP, @todo)" -msgstr "Tâches (TODO, FIXME, XXX, HINT, TIP, @todo)" - -#: spyderlib/plugins/editor.py:254 -msgid "Perform analysis when saving file and every" -msgstr "Analyser lors de l'enregistrement et toutes les" - -#: spyderlib/plugins/editor.py:258 -msgid "Perform analysis only when saving file" -msgstr "Analyser uniquement lors de l'enregistrement" - -#: spyderlib/plugins/editor.py:306 -msgid "End-of-line characters" -msgstr "Caractères de fin de ligne" - -#: spyderlib/plugins/editor.py:307 -msgid "" -"When opening a text file containing mixed end-of-line characters (this may " -"raise syntax errors in the consoles on Windows platforms), Spyder may fix " -"the file automatically." -msgstr "" -"Lors de l'ouverture d'un fichier contenant différents types de caractères de " -"fin de ligne (ce qui peut se traduire par des erreurs de syntaxe dans les " -"consoles Python sous Windows), Spyder peut réparer le fichier " -"automatiquement." - -#: spyderlib/plugins/editor.py:313 -msgid "Fix automatically and show warning message box" -msgstr "Réparer automatiquement et afficher un message d'avertissement" - -#: spyderlib/plugins/editor.py:324 spyderlib/plugins/externalconsole.py:363 -#: spyderlib/plugins/ipythonconsole.py:444 -#: spyderlib/plugins/variableexplorer.py:41 -msgid "Display" -msgstr "Affichage" - -#: spyderlib/plugins/editor.py:326 -msgid "Code Introspection/Analysis" -msgstr "Introspection et analyse de code" - -#: spyderlib/plugins/editor.py:329 spyderlib/plugins/externalconsole.py:367 -msgid "Advanced settings" -msgstr "Options avancées" - -#: spyderlib/plugins/editor.py:583 spyderlib/widgets/editortools.py:508 -msgid "Show/hide outline explorer" -msgstr "Afficher/masquer l'explorateur de structure" - -#: spyderlib/plugins/editor.py:589 -msgid "Show/hide project explorer" -msgstr "Afficher/masquer l'explorateur de projets" - -#: spyderlib/plugins/editor.py:597 -msgid "&New file..." -msgstr "&Nouveau fichier..." - -#: spyderlib/plugins/editor.py:598 spyderlib/plugins/workingdirectory.py:82 -#: spyderlib/widgets/explorer.py:643 spyderlib/widgets/explorer.py:650 -msgid "New file" -msgstr "Nouveau fichier" - -#: spyderlib/plugins/editor.py:605 -msgid "&Open..." -msgstr "&Ouvrir..." - -#: spyderlib/plugins/editor.py:606 spyderlib/plugins/editor.py:1647 -#: spyderlib/plugins/workingdirectory.py:69 -msgid "Open file" -msgstr "Ouvrir un fichier" - -#: spyderlib/plugins/editor.py:613 -msgid "&Revert" -msgstr "&Réinitialiser" - -#: spyderlib/plugins/editor.py:614 -msgid "Revert file from disk" -msgstr "Revenir à la version du fichier enregistrée sur le disque" - -#: spyderlib/plugins/editor.py:617 -msgid "&Save" -msgstr "&Enregistrer" - -#: spyderlib/plugins/editor.py:618 -msgid "Save file" -msgstr "Enregitrer un fichier" - -#: spyderlib/plugins/editor.py:625 -msgid "Sav&e all" -msgstr "Enregistrer &tout" - -#: spyderlib/plugins/editor.py:626 -msgid "Save all files" -msgstr "Enregistrer tous les fichiers" - -#: spyderlib/plugins/editor.py:633 -msgid "Save &as..." -msgstr "Enregistrer &sous..." - -#: spyderlib/plugins/editor.py:634 -msgid "Save current file as..." -msgstr "Enregistrer le fichier en cours d'édition sous un autre nom..." - -#: spyderlib/plugins/editor.py:636 spyderlib/plugins/editor.py:637 -msgid "Print preview..." -msgstr "Aperçu avant impression..." - -#: spyderlib/plugins/editor.py:638 -msgid "&Print..." -msgstr "Im&primer..." - -#: spyderlib/plugins/editor.py:639 -msgid "Print current file..." -msgstr "Imprimer le fichier en cours d'édition..." - -#: spyderlib/plugins/editor.py:644 -msgid "&Close" -msgstr "&Fermer" - -#: spyderlib/plugins/editor.py:645 -msgid "Close current file" -msgstr "Fermer le fichier en cours d'édition" - -#: spyderlib/plugins/editor.py:647 -msgid "C&lose all" -msgstr "Fermer t&out" - -#: spyderlib/plugins/editor.py:648 -msgid "Close all opened files" -msgstr "Fermer tous les fichiers ouverts" - -#: spyderlib/plugins/editor.py:655 -msgid "Set/Clear breakpoint" -msgstr "Ajouter/supprimer un point d'arrêt" - -#: spyderlib/plugins/editor.py:662 -msgid "Set/Edit conditional breakpoint" -msgstr "Ajouter/modifier un point d'arrêt conditionnel" - -#: spyderlib/plugins/editor.py:669 -msgid "Clear breakpoints in all files" -msgstr "Supprimer les points d'arrêt dans tous les fichiers" - -#: spyderlib/plugins/editor.py:671 -msgid "Breakpoints" -msgstr "Points d'arrêt" - -#: spyderlib/plugins/editor.py:675 -msgid "Debug with winpdb" -msgstr "Déboguer avec winpdb" - -#: spyderlib/plugins/editor.py:682 spyderlib/spyder.py:575 -msgid "&Debug" -msgstr "&Déboguer" - -#: spyderlib/plugins/editor.py:683 -msgid "Debug file" -msgstr "Déboguer le script" - -#: spyderlib/plugins/editor.py:688 -msgid "Step" -msgstr "Pas" - -#: spyderlib/plugins/editor.py:689 -msgid "Run current line" -msgstr "Exécuter la ligne en cours" - -#: spyderlib/plugins/editor.py:695 -msgid "Continue" -msgstr "Continuer" - -#: spyderlib/plugins/editor.py:696 -msgid "Continue execution until next breakpoint" -msgstr "Continuer l'exécution jusqu'au prochain point d'arrêt" - -#: spyderlib/plugins/editor.py:703 -msgid "Step Into" -msgstr "Pas vers l'intérieur" - -#: spyderlib/plugins/editor.py:704 -msgid "Step into function or method of current line" -msgstr "" -"Avancer dans la fonction, méthode \n" -"ou classe de la ligne en cours" - -#: spyderlib/plugins/editor.py:711 -msgid "Step Return" -msgstr "Pas vers l'extérieur" - -#: spyderlib/plugins/editor.py:712 -msgid "Run until current function or method returns" -msgstr "Exécuter jusqu'au retour de la fonction ou méthode" - -#: spyderlib/plugins/editor.py:719 -msgid "Exit" -msgstr "Sortir" - -#: spyderlib/plugins/editor.py:720 -msgid "Exit Debug" -msgstr "Quitter le débogage" - -#: spyderlib/plugins/editor.py:731 -msgid "Debugging control" -msgstr "Contrôle du débogage" - -#: spyderlib/plugins/editor.py:735 spyderlib/plugins/editor.py:1246 -#: spyderlib/spyder.py:570 -msgid "&Run" -msgstr "E&xécution" - -#: spyderlib/plugins/editor.py:736 -msgid "Run file" -msgstr "Exécuter le fichier" - -#: spyderlib/plugins/editor.py:742 -msgid "&Configure..." -msgstr "&Configurer..." - -#: spyderlib/plugins/editor.py:743 -#: spyderlib/widgets/externalshell/pythonshell.py:294 -msgid "Run settings" -msgstr "Options d'exécution" - -#: spyderlib/plugins/editor.py:752 -msgid "Re-run &last script" -msgstr "Exécuter de nouveau le &dernier script" - -#: spyderlib/plugins/editor.py:753 -msgid "Run again last file" -msgstr "Exécuter de nouveau le dernier fichier" - -#: spyderlib/plugins/editor.py:760 -#: spyderlib/widgets/sourcecode/codeeditor.py:2305 -msgid "Run &selection or current line" -msgstr "Exécuter la &sélection ou la ligne courante" - -#: spyderlib/plugins/editor.py:763 -msgid "Run selection or current line" -msgstr "Exécuter la sélection ou le bloc de lignes" - -#: spyderlib/plugins/editor.py:776 -msgid "Run cell" -msgstr "Exécuter la cellule" - -#: spyderlib/plugins/editor.py:778 -msgid "" -"Run current cell (Ctrl+Enter)\n" -"[Use #%% to create cells]" -msgstr "" -"Exécuter la cellule courante \n" -"[Utiliser #%% pour délimiter les cellules]" - -#: spyderlib/plugins/editor.py:783 -msgid "Run cell and advance" -msgstr "Exécuter la cellule et avancer" - -#: spyderlib/plugins/editor.py:786 -msgid "Run current cell and go to the next one (Shift+Enter)" -msgstr "" -"Exécuter la cellule en cours d'édition et aller à la suivante\n" -"(voir la documentation de l'Editeur, pour plus de détails sur les cellules)" - -#: spyderlib/plugins/editor.py:792 -msgid "Show todo list" -msgstr "Afficher la liste des tâches" - -#: spyderlib/plugins/editor.py:793 -msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" -msgstr "" -"Afficher la liste des commentaires du type TODO/FIXME/XXX/HINT/TIP/@todo" - -#: spyderlib/plugins/editor.py:801 -msgid "Show warning/error list" -msgstr "Afficher la liste des avertissements/erreurs" - -#: spyderlib/plugins/editor.py:802 -msgid "Show code analysis warnings/errors" -msgstr "" -"Afficher la liste des avertissements/erreurs provenant de l'analyse de code" - -#: spyderlib/plugins/editor.py:809 -msgid "Previous warning/error" -msgstr "Avertissement suivant" - -#: spyderlib/plugins/editor.py:810 -msgid "Go to previous code analysis warning/error" -msgstr "Afficher le message d'avertissement ou d'erreur suivant" - -#: spyderlib/plugins/editor.py:813 -msgid "Next warning/error" -msgstr "Avertissement précédent" - -#: spyderlib/plugins/editor.py:814 -msgid "Go to next code analysis warning/error" -msgstr "Afficher le message d'avertissement ou d'erreur précédent" - -#: spyderlib/plugins/editor.py:818 -msgid "Last edit location" -msgstr "Dernière position d'édition" - -#: spyderlib/plugins/editor.py:819 -msgid "Go to last edit location" -msgstr "Aller à la dernière position d'édition" - -#: spyderlib/plugins/editor.py:825 -msgid "Previous cursor position" -msgstr "Position suivante du curseur" - -#: spyderlib/plugins/editor.py:826 -msgid "Go to previous cursor position" -msgstr "Aller à la position précédente du curseur" - -#: spyderlib/plugins/editor.py:832 -msgid "Next cursor position" -msgstr "Position suivante du curseur" - -#: spyderlib/plugins/editor.py:833 -msgid "Go to next cursor position" -msgstr "Aller à la position suivante du curseur" - -#: spyderlib/plugins/editor.py:840 -#: spyderlib/widgets/sourcecode/codeeditor.py:2292 -msgid "Comment" -msgstr "Commenter" - -#: spyderlib/plugins/editor.py:840 -#: spyderlib/widgets/sourcecode/codeeditor.py:2292 -msgid "Uncomment" -msgstr "Décommenter" - -#: spyderlib/plugins/editor.py:841 -msgid "Comment current line or selection" -msgstr "Commenter la sélection ou la ligne en cours d'édition" - -#: spyderlib/plugins/editor.py:845 -msgid "Add &block comment" -msgstr "Ajouter un &bloc de commentaires" - -#: spyderlib/plugins/editor.py:846 -msgid "Add block comment around current line or selection" -msgstr "" -"Ajouter un bloc de commentaires autour de la sélection ou de la ligne en " -"cours d'édition" - -#: spyderlib/plugins/editor.py:852 -msgid "R&emove block comment" -msgstr "&Supprimer un bloc de commentaires" - -#: spyderlib/plugins/editor.py:853 -msgid "Remove comment block around current line or selection" -msgstr "" -"Supprimer le bloc de commentaires autour de la ligne en cours d'édition" - -#: spyderlib/plugins/editor.py:864 -msgid "Indent" -msgstr "Indenter" - -#: spyderlib/plugins/editor.py:865 -msgid "Indent current line or selection" -msgstr "Indenter la sélection ou la ligne en cours d'édition" - -#: spyderlib/plugins/editor.py:868 -msgid "Unindent" -msgstr "Désindenter" - -#: spyderlib/plugins/editor.py:869 -msgid "Unindent current line or selection" -msgstr "Désindenter la sélection ou la ligne en cours d'édition" - -#: spyderlib/plugins/editor.py:874 -msgid "Carriage return and line feed (Windows)" -msgstr "Retour chariot et retour à la ligne (Windows)" - -#: spyderlib/plugins/editor.py:877 -msgid "Line feed (UNIX)" -msgstr "Retour à la ligne (UNIX)" - -#: spyderlib/plugins/editor.py:880 -msgid "Carriage return (Mac)" -msgstr "Retour chariot (Mac)" - -#: spyderlib/plugins/editor.py:886 -msgid "Convert end-of-line characters" -msgstr "Convertir les caractères de fin de ligne" - -#: spyderlib/plugins/editor.py:890 -msgid "Remove trailing spaces" -msgstr "Supprimer les espaces en fin de ligne" - -#: spyderlib/plugins/editor.py:894 -msgid "Fix indentation" -msgstr "Corriger l'indentation" - -#: spyderlib/plugins/editor.py:895 -msgid "Replace tab characters by space characters" -msgstr "Remplacer les caractères de tabulation par des espaces" - -#: spyderlib/plugins/editor.py:898 -msgid "Go to line..." -msgstr "Aller à la ligne..." - -#: spyderlib/plugins/editor.py:906 -msgid "Set console working directory" -msgstr "Répertoire de travail de la console" - -#: spyderlib/plugins/editor.py:908 -msgid "" -"Set current console (and file explorer) working directory to current script " -"directory" -msgstr "" -"Choisir le répertoire du script comme répertoire de travail de la console " -"courante (et de l'explorateur de fichier)" - -#: spyderlib/plugins/editor.py:913 -msgid "Maximum number of recent files..." -msgstr "Nombre maximum de fichiers récents..." - -#: spyderlib/plugins/editor.py:916 -msgid "Clear recent files list" -msgstr "Effacer la liste des fichiers récents" - -#: spyderlib/plugins/editor.py:916 -msgid "Clear this list" -msgstr "Effacer cette liste" - -#: spyderlib/plugins/editor.py:918 -msgid "Open &recent" -msgstr "Fichiers &récents" - -#: spyderlib/plugins/editor.py:1234 spyderlib/spyder.py:551 -msgid "File toolbar" -msgstr "Barre d'outil fichiers" - -#: spyderlib/plugins/editor.py:1235 spyderlib/spyder.py:561 -msgid "Search toolbar" -msgstr "Barre d'outil de recherche" - -#: spyderlib/plugins/editor.py:1236 spyderlib/spyder.py:566 -msgid "Source toolbar" -msgstr "Barre d'outils code source" - -#: spyderlib/plugins/editor.py:1237 spyderlib/spyder.py:571 -msgid "Run toolbar" -msgstr "Barre d'outil exécution" - -#: spyderlib/plugins/editor.py:1238 spyderlib/spyder.py:576 -msgid "Debug toolbar" -msgstr "Barre d'outil de débogage" - -#: spyderlib/plugins/editor.py:1239 spyderlib/spyder.py:556 -msgid "Edit toolbar" -msgstr "Barre d'outil édition" - -#: spyderlib/plugins/editor.py:1242 spyderlib/spyder.py:548 -msgid "&File" -msgstr "&Fichier" - -#: spyderlib/plugins/editor.py:1243 spyderlib/spyder.py:555 -msgid "&Edit" -msgstr "&Édition" - -#: spyderlib/plugins/editor.py:1244 spyderlib/spyder.py:560 -msgid "&Search" -msgstr "&Recherche" - -#: spyderlib/plugins/editor.py:1245 spyderlib/spyder.py:565 -msgid "Sour&ce" -msgstr "Sour&ce" - -#: spyderlib/plugins/editor.py:1247 spyderlib/spyder.py:583 -msgid "&Tools" -msgstr "Ou&tils" - -#: spyderlib/plugins/editor.py:1248 -msgid "?" -msgstr "?" - -#: spyderlib/plugins/editor.py:1469 -msgid "Spyder Editor" -msgstr "Éditeur de Spyder" - -#: spyderlib/plugins/editor.py:1470 -msgid "This is a temporary script file." -msgstr "Ceci est un script temporaire." - -#: spyderlib/plugins/editor.py:1536 -msgid "untitled" -msgstr "sanstitre" - -#: spyderlib/plugins/editor.py:1607 -msgid "Maximum number of recent files" -msgstr "Nombre maximum de fichiers récents" - -#: spyderlib/plugins/editor.py:1729 -msgid "Printing..." -msgstr "Impression en cours..." - -#: spyderlib/plugins/explorer.py:45 -msgid "File explorer" -msgstr "Explorateur de fichiers" - -#: spyderlib/plugins/explorer.py:58 spyderlib/plugins/inspector.py:373 -#: spyderlib/plugins/projectexplorer.py:57 -msgid "Set font style" -msgstr "Changer la police d'écriture" - -#: spyderlib/plugins/externalconsole.py:46 -msgid "Interactive data plotting in the consoles" -msgstr "Visualisation interactive de données" - -#: spyderlib/plugins/externalconsole.py:53 -#: spyderlib/plugins/externalconsole.py:1066 -#: spyderlib/plugins/inspector.py:403 spyderlib/plugins/runconfig.py:178 -#: spyderlib/plugins/runconfig.py:447 -#: spyderlib/widgets/externalshell/baseshell.py:106 -#: spyderlib/widgets/ipython.py:509 -msgid "Console" -msgstr "Console" - -#: spyderlib/plugins/externalconsole.py:69 -msgid "One tab per script" -msgstr "Un onglet par script" - -#: spyderlib/plugins/externalconsole.py:70 -#: spyderlib/widgets/externalshell/baseshell.py:171 -msgid "Show elapsed time" -msgstr "Afficher le temps écoulé" - -#: spyderlib/plugins/externalconsole.py:71 spyderlib/widgets/explorer.py:988 -msgid "Show icons and text" -msgstr "Afficher icônes et textes" - -#: spyderlib/plugins/externalconsole.py:83 -msgid "Buffer: " -msgstr "Tampon : " - -#: spyderlib/plugins/externalconsole.py:83 -#: spyderlib/plugins/ipythonconsole.py:201 -msgid " lines" -msgstr " lignes" - -#: spyderlib/plugins/externalconsole.py:88 -msgid "Merge process standard output/error channels" -msgstr "Fusionner les canaux de sortie et d'erreur du processus" - -#: spyderlib/plugins/externalconsole.py:90 -msgid "" -"Merging the output channels of the process means that\n" -"the standard error won't be written in red anymore,\n" -"but this has the effect of speeding up display." -msgstr "" -"Fusionner les canaux de sortie et d'erreur du processus\n" -"signifie que les erreurs ne seront plus affichées en rouge,\n" -"mais cela entraînera également une amélioration des performances\n" -"d'affichage et une meilleure réactivité de la console." - -#: spyderlib/plugins/externalconsole.py:94 -msgid "Colorize standard error channel using ANSI escape codes" -msgstr "Coloriser le canal d'erreur standard (codes d'échappement ANSI)" - -#: spyderlib/plugins/externalconsole.py:96 -msgid "" -"This method is the only way to have colorized standard\n" -"error channel when the output channels have been merged." -msgstr "" -"Cette méthode est le seul moyen de coloriser le canal\n" -"d'erreur standard lorsque les canaux de sorties ont été fusionnés." - -#: spyderlib/plugins/externalconsole.py:114 -#: spyderlib/plugins/ipythonconsole.py:188 -#: spyderlib/widgets/arrayeditor.py:460 -#: spyderlib/widgets/dataframeeditor.py:515 -msgid "Background color" -msgstr "Couleur de fond" - -#: spyderlib/plugins/externalconsole.py:115 -msgid "" -"This option will be applied the next time a Python console or a terminal is " -"opened." -msgstr "" -"Cette option sera prise en compte lors de la prochaine ouverture de console " -"(interpréteur Python ou terminal)." - -#: spyderlib/plugins/externalconsole.py:118 -msgid "Light background (white color)" -msgstr "Fond blanc" - -#: spyderlib/plugins/externalconsole.py:143 -msgid "User Module Reloader (UMR)" -msgstr "User Module Reloader (UMR)" - -#: spyderlib/plugins/externalconsole.py:144 -msgid "" -"UMR forces Python to reload modules which were imported when executing a \n" -"script in the external console with the 'runfile' function." -msgstr "" -"L'UMR force Python à recharger les modules importés lors de l'exécution " -"d'un\n" -"script dans la console interne avec la fonction 'runfile'." - -#: spyderlib/plugins/externalconsole.py:147 -msgid "Enable UMR" -msgstr "Activer l'UMR" - -#: spyderlib/plugins/externalconsole.py:148 -msgid "" -"This option will enable the User Module Reloader (UMR) in Python/IPython " -"consoles. UMR forces Python to reload deeply modules during import when " -"running a Python script using the Spyder's builtin function runfile." -"

    1. UMR may require to restart the console in which it will be " -"called (otherwise only newly imported modules will be reloaded when " -"executing scripts).

    2. If errors occur when re-running a PyQt-" -"based program, please check that the Qt objects are properly destroyed (e.g. " -"you may have to use the attribute Qt.WA_DeleteOnClose on your main " -"window, using the setAttribute method)" -msgstr "" -"Cette option active le User Module Deleter (UMR) dans les consoles Python. " -"L'UMR force Python à recharger complètement les modules lors de leur " -"importation, dans le cadre de l'exécution d'un script Python avec la " -"fonction Spyder runfile.

    1. UMR peut nécessiter le " -"redémarrage de la console Python dans lequel il va être utilisé (dans le cas " -"contraire, seuls les modules importés après activation de l'UMR seront " -"rechargés complètement lors de l'exécution de scripts).

    2. Si " -"des erreurs survenaient lors de la réexécution de programmes utilisant PyQt, " -"veuillez vérifier que les objets Qt sont correctement détruits à la sortie " -"du programme (par exemple, il sera probablement nécessaire d'utiliser " -"l'attribut Qt.WA_DeleteOnClose sur votre objet fenêtre principale " -"grâce à la méthode setAttribute)" - -#: spyderlib/plugins/externalconsole.py:164 -msgid "Show reloaded modules list" -msgstr "Afficher les modules rechargés" - -#: spyderlib/plugins/externalconsole.py:165 -msgid "Please note that these changes will be applied only to new consoles" -msgstr "" -"Veuillez noter que ces changements ne seront pris en compte que dans les " -"nouvelles consoles Python" - -#: spyderlib/plugins/externalconsole.py:169 -msgid "Set UMR excluded (not reloaded) modules" -msgstr "Définir la liste des modules non rechargés par l'UMR" - -#: spyderlib/plugins/externalconsole.py:181 -msgid "Python executable" -msgstr "Exécutable Python" - -#: spyderlib/plugins/externalconsole.py:183 -msgid "" -"Select the Python interpreter executable binary in which Spyder will run " -"scripts:" -msgstr "Sélectionner l'interpréteur Python utilisé pour exécuter des scripts:" - -#: spyderlib/plugins/externalconsole.py:186 -msgid "Default (i.e. the same as Spyder's)" -msgstr "" -"Par défaut (interpréteur identique à celui dans lequel Spyder est exécuté)" - -#: spyderlib/plugins/externalconsole.py:190 -msgid "Use the following Python interpreter:" -msgstr "Utiliser l'interpréteur Python suivant :" - -#: spyderlib/plugins/externalconsole.py:194 -msgid "Executables" -msgstr "Exécutables" - -#: spyderlib/plugins/externalconsole.py:214 -msgid "PYTHONSTARTUP replacement" -msgstr "Substitution de PYTHONSTARTUP" - -#: spyderlib/plugins/externalconsole.py:216 -msgid "" -"This option will override the PYTHONSTARTUP environment variable which\n" -"defines the script to be executed during the Python console startup." -msgstr "" -"Cette option permet de remplacer le script de démarrage des consoles Python " -"défini par la\n" -"variable d'environnement PYTHONSTARTUP." - -#: spyderlib/plugins/externalconsole.py:221 -msgid "Default PYTHONSTARTUP script" -msgstr "Script PYTHONSTARTUP par défaut" - -#: spyderlib/plugins/externalconsole.py:225 -msgid "Use the following startup script:" -msgstr "Utiliser le script de démarrage suivant :" - -#: spyderlib/plugins/externalconsole.py:244 -msgid "Monitor" -msgstr "Moniteur" - -#: spyderlib/plugins/externalconsole.py:245 -msgid "" -"The monitor provides introspection features to console: code completion, " -"calltips and variable explorer. Because it relies on several modules, " -"disabling the monitor may be useful to accelerate console startup." -msgstr "" -"Le moniteur fournit à la console des fonctionnalités d'introspection telles " -"que la complétion de code, les info-bulles et l'explorateur de variables. " -"Parce qu'il nécessite l'import de nombreux modules, désactiver le moniteur " -"permet d'accélérer le démarrage de la console." - -#: spyderlib/plugins/externalconsole.py:252 -msgid "Enable monitor" -msgstr "Activer le moniteur" - -#: spyderlib/plugins/externalconsole.py:264 -msgid "Default library" -msgstr "Bibliothèque par défaut" - -#: spyderlib/plugins/externalconsole.py:266 -msgid "Qt (PyQt/PySide)" -msgstr "Qt (PyQt/PySide)" - -#: spyderlib/plugins/externalconsole.py:268 -msgid "Qt-Python bindings library selection:" -msgstr "Sélection de la bibliothèque d'interfaçage Qt :" - -#: spyderlib/plugins/externalconsole.py:270 -msgid "" -"This option will act on
    libraries such as Matplotlib, guidata or ETS" -msgstr "" -"Cette option est prise en charge par les bibliothèques telles que " -"Matplotlib, guidata ou ETS" - -#: spyderlib/plugins/externalconsole.py:291 -msgid "PyQt" -msgstr "PyQt" - -#: spyderlib/plugins/externalconsole.py:293 -msgid "API selection for QString and QVariant objects:" -msgstr "Sélection de l'API des objets QString et QVariant :" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "API #1" -msgstr "API n°1" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "API #2" -msgstr "API n°2" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "Default API" -msgstr "API par défaut" - -#: spyderlib/plugins/externalconsole.py:296 -msgid "" -"PyQt API #1 is the default
    API for Python 2. PyQt API #2 is the default " -"API for Python 3 and is compatible with PySide." -msgstr "" -"L'API n°1 de PyQt est l'API par défaut pour Python 2. L'API n°2 est l'API " -"par défaut pour Python 3 : c'est l'API qui est compatible avec PySide." - -#: spyderlib/plugins/externalconsole.py:300 -msgid "Ignore API change errors (sip.setapi)" -msgstr "Ignorer les erreurs de changement d'API (sip.setapi)" - -#: spyderlib/plugins/externalconsole.py:302 -msgid "" -"Enabling this option will ignore
    errors when changing PyQt API. As PyQt " -"does not support dynamic API changes, it is strongly recommended to use this " -"feature wisely, e.g. for debugging purpose." -msgstr "" -"L'activation de cette option permet d'ignorer les erreurs liées aux " -"changements\n" -"d'API de PyQt. Vu que PyQt ne prend pas en charge le changement dynamique\n" -" d'API, il est fortement recommandé d'utiliser cette fonctionnalité " -"exceptionnellement,\n" -"par exemple pour du débogage." - -#: spyderlib/plugins/externalconsole.py:321 -msgid "Matplotlib" -msgstr "Matplotlib" - -#: spyderlib/plugins/externalconsole.py:323 -msgid "GUI backend:" -msgstr "Backend graphique :" - -#: spyderlib/plugins/externalconsole.py:325 -msgid "" -"Set the GUI toolkit used by
    Matplotlib to show figures (default: Qt4Agg)" -msgstr "" -"Spécifie la bibliothèque d'interfaces graphiques à utiliser pour l'affichage " -"des figures Matplotlib (par défaut : Qt4Agg)" - -#: spyderlib/plugins/externalconsole.py:344 -msgid "Enthought Tool Suite" -msgstr "Enthought Tool Suite" - -#: spyderlib/plugins/externalconsole.py:345 -msgid "" -"Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical " -"user interfaces." -msgstr "" -"Le logiciel Enthought Tool Suite (ETS) prend en charge les interfaces " -"graphiques PyQt4 (qt4) et wxPython (wx)." - -#: spyderlib/plugins/externalconsole.py:349 -msgid "ETS_TOOLKIT:" -msgstr "ETS_TOOLKIT:" - -#: spyderlib/plugins/externalconsole.py:369 -msgid "External modules" -msgstr "Modules externes" - -#: spyderlib/plugins/externalconsole.py:426 -#: spyderlib/plugins/externalconsole.py:666 -#: spyderlib/plugins/ipythonconsole.py:113 -#: spyderlib/plugins/ipythonconsole.py:808 spyderlib/spyder.py:1331 -#: spyderlib/spyder.py:1349 spyderlib/utils/environ.py:94 -#: spyderlib/utils/environ.py:107 spyderlib/widgets/dicteditor.py:449 -msgid "Warning" -msgstr "Attention" - -#: spyderlib/plugins/externalconsole.py:427 -msgid "" -"You selected a Python %d interpreter for the console but Spyder is " -"running on Python %d!.

    Although this is possible, we recommend " -"you to install and run Spyder directly with your selected interpreter, to " -"avoid seeing false warnings and errors due to the incompatible syntax " -"between these two Python versions." -msgstr "" -"Vous avez sélectionné un interpréteur Python %d pour la console. Or " -"cette version de Spyder est exécutée par Python %d!.

    Même si " -"c'est techniquement possible, il est recommandé d'installer et d'exécuter " -"Spyder directement dans la version de l'interpréteur sélectionnée, afin " -"d'éviter l'apparition d'avertissements ou d'erreurs liées à une syntaxe " -"incompatible entre ces deux versions de Python" - -#: spyderlib/plugins/externalconsole.py:590 -msgid "Trying to kill a kernel?" -msgstr "Tentative d'arrêt d'un noyau" - -#: spyderlib/plugins/externalconsole.py:591 -msgid "" -"You can't close this kernel because it has one or more consoles connected to " -"it.

    You need to close them instead or you can kill the kernel using " -"the second button from right to left." -msgstr "" -"Le noyau ne peut pas être fermé car au moins une console y est connectée. " -"

    Veuillez soit fermer toutes les consoles connectées ou appuyer sur " -"le second bouton en partant de la droite pour tuer le processus du noyau." - -#: spyderlib/plugins/externalconsole.py:667 -msgid "" -"No Python console is currently selected to run %s.

    Please " -"select or open a new Python console and try again." -msgstr "" -"Aucune console Python n'est actuellement sélectionnée pour exécuter %s.

    Merci de sélectionner ou d'ouvrir une nouvelle console Python et " -"de réessayer." - -#: spyderlib/plugins/externalconsole.py:748 -msgid "" -"%s is already running in a separate process.\n" -"Do you want to kill the process before starting a new one?" -msgstr "" -"%s est déjà en cours d'exécution dans un processus séparé.\n" -"Souhaitez-vous tuer ce processus avant d'en démarrer un autre ?" - -#: spyderlib/plugins/externalconsole.py:917 -msgid "Kernel" -msgstr "Noyau" - -#: spyderlib/plugins/externalconsole.py:929 -msgid "" -"Either:
    1. Your IPython frontend and kernel versions are " -"incompatible or
    2. You don't have IPython installed in " -"your external interpreter.
    In any case, we're sorry but we can't " -"create a console for you." -msgstr "" -"
    1. Soit les versions de votre interface IPython et du noyau sont " -"incompatibles,
    2. soit IPython n'est pas installé pour " -"votre interpréteur externe.
    Dans tous les cas nous sommes désolés " -"mais nous ne pouvons ouvrir une console pour vous." - -#: spyderlib/plugins/externalconsole.py:953 -msgid "Command Window" -msgstr "Invite de commandes" - -#: spyderlib/plugins/externalconsole.py:955 -msgid "Terminal" -msgstr "Terminal" - -#: spyderlib/plugins/externalconsole.py:1008 -msgid "Kernel %s" -msgstr "Noyau %s" - -#: spyderlib/plugins/externalconsole.py:1088 -msgid "Open a &Python console" -msgstr "Ouvrir une console &Python" - -#: spyderlib/plugins/externalconsole.py:1091 -msgid "Open &command prompt" -msgstr "Ouvrir un invite de &commandes" - -#: spyderlib/plugins/externalconsole.py:1092 -msgid "Open a Windows command prompt" -msgstr "Ouvrir un invite de commandes Windows" - -#: spyderlib/plugins/externalconsole.py:1094 -msgid "Open a &terminal" -msgstr "Ouvrir un &terminal" - -#: spyderlib/plugins/externalconsole.py:1095 -msgid "Open a terminal window" -msgstr "Ouvrir un terminal de commandes dans Spyder" - -#: spyderlib/plugins/externalconsole.py:1263 -msgid "Open an IPython console" -msgstr "Ouvrir une console IPython" - -#: spyderlib/plugins/externalconsole.py:1264 -msgid "" -"The console monitor was disabled: the IPython kernel will be started as " -"expected, but an IPython console will have to be connected manually to the " -"kernel." -msgstr "" -"Le moniteur (console) a été désactivé. Par conséquent, le noyau IPython sera " -"démarré mais la console IPython devra y être connectée manuellement." - -#: spyderlib/plugins/externalconsole.py:1294 -#: spyderlib/plugins/externalconsole.py:1307 -#: spyderlib/plugins/externalconsole.py:1311 -msgid "UMR" -msgstr "UMR" - -#: spyderlib/plugins/externalconsole.py:1295 -msgid "" -"UMR excluded modules:\n" -"(example: guidata, guiqwt)" -msgstr "" -"Modules non rechargés par l'UMR :\n" -"(exemple: guidata, guiqwt)" - -#: spyderlib/plugins/externalconsole.py:1308 -msgid "" -"The following modules are not installed on your machine:\n" -"%s" -msgstr "" -"Les modules suivants ne sont pas installés sur votre ordinateur :\n" -"%s" - -#: spyderlib/plugins/externalconsole.py:1312 -msgid "" -"Please note that these changes will be applied only to new Python/IPython " -"consoles" -msgstr "" -"Veuillez noter que ces changements ne seront pris en compte que dans les " -"nouvelles consoles Python/IPython" - -#: spyderlib/plugins/findinfiles.py:90 spyderlib/widgets/findinfiles.py:691 -msgid "Find in files" -msgstr "Recherche dans des fichiers" - -#: spyderlib/plugins/findinfiles.py:114 -msgid "&Find in files" -msgstr "Rechercher dans des &fichiers" - -#: spyderlib/plugins/findinfiles.py:117 -msgid "Search text in multiple files" -msgstr "Rechercher une chaîne de caractères dans plusieurs fichiers à la fois" - -#: spyderlib/plugins/history.py:36 -msgid "Settings" -msgstr "Options" - -#: spyderlib/plugins/history.py:38 -msgid " entries" -msgstr " lignes" - -#: spyderlib/plugins/history.py:38 -msgid "History depth: " -msgstr "Taille de l'historique : " - -#: spyderlib/plugins/history.py:45 -msgid "Scroll automatically to last entry" -msgstr "Défiler automatiquement jusqu'à la dernière ligne" - -#: spyderlib/plugins/history.py:113 spyderlib/plugins/inspector.py:458 -#: spyderlib/widgets/editor.py:540 spyderlib/widgets/explorer.py:1018 -#: spyderlib/widgets/externalshell/baseshell.py:151 -#: spyderlib/widgets/externalshell/namespacebrowser.py:226 -#: spyderlib/widgets/ipython.py:556 -msgid "Options" -msgstr "Options" - -#: spyderlib/plugins/history.py:133 -msgid "History log" -msgstr "Historique" - -#: spyderlib/plugins/history.py:160 -msgid "History..." -msgstr "Historique..." - -#: spyderlib/plugins/history.py:162 -msgid "Set history maximum entries" -msgstr "Modifier le nombre d'entrées maximum de l'historique" - -#: spyderlib/plugins/history.py:272 -msgid "History" -msgstr "Historique" - -#: spyderlib/plugins/history.py:273 -msgid "Maximum entries" -msgstr "Nombre maximum d'entrées" - -#: spyderlib/plugins/inspector.py:56 -msgid "Rich text help on the Object Inspector" -msgstr "Texte enrichi dans l'inspecteur d'objets" - -#: spyderlib/plugins/inspector.py:120 -msgid "Plain text font style" -msgstr "Police d'écriture du texte brut" - -#: spyderlib/plugins/inspector.py:123 -msgid "Rich text font style" -msgstr "Police d'écriture du texte enrichi" - -#: spyderlib/plugins/inspector.py:126 -msgid "Automatic connections" -msgstr "Connexions automatiques" - -#: spyderlib/plugins/inspector.py:127 -msgid "" -"The Object Inspector can automatically show an object's help information " -"after a left parenthesis is written next to it. Below you can decide to " -"which plugin you want to connect it to turn on this feature." -msgstr "" -"L'inspecteur d'objet peut automatiquement afficher l'aide sur un objet dès " -"l'ouverture d'une parenthèse." - -#: spyderlib/plugins/inspector.py:139 -msgid "" -"This feature requires the Rope or Jedi libraries.\n" -"It seems you don't have either installed." -msgstr "" -"Cette fonctionnalité nécessite Rope ou Jedi.\n" -"Il semble qu'aucun de ces modules ne soit présent." - -#: spyderlib/plugins/inspector.py:142 -msgid "Python Console" -msgstr "Console Python" - -#: spyderlib/plugins/inspector.py:144 -msgid "IPython Console" -msgstr "Console IPython" - -#: spyderlib/plugins/inspector.py:156 -msgid "Additional features" -msgstr "Options supplémentaires" - -#: spyderlib/plugins/inspector.py:157 -msgid "Render mathematical equations" -msgstr "Styliser les équations mathématiques" - -#: spyderlib/plugins/inspector.py:163 -msgid "This feature requires Sphinx 1.1 or superior." -msgstr "Cette fonctionnalité nécessite l'installation de Sphinx 1.1+." - -#: spyderlib/plugins/inspector.py:165 -msgid "Sphinx %s is currently installed." -msgstr "Sphinx %s n'est pas installé." - -#: spyderlib/plugins/inspector.py:357 -msgid "No further documentation available" -msgstr "Aucune documentation disponible" - -#: spyderlib/plugins/inspector.py:396 -msgid "Source" -msgstr "Source" - -#: spyderlib/plugins/inspector.py:412 spyderlib/widgets/dicteditor.py:173 -msgid "Object" -msgstr "Objet" - -#: spyderlib/plugins/inspector.py:428 -msgid "Plain Text" -msgstr "Texte brut" - -#: spyderlib/plugins/inspector.py:432 -msgid "Show Source" -msgstr "Afficher les sources" - -#: spyderlib/plugins/inspector.py:436 -msgid "Rich Text" -msgstr "Texte enrichi" - -#: spyderlib/plugins/inspector.py:446 -msgid "Automatic import" -msgstr "Import automatique" - -#: spyderlib/plugins/inspector.py:506 spyderlib/plugins/inspector.py:954 -msgid "Object inspector" -msgstr "Inspecteur d'objets" - -#: spyderlib/plugins/inspector.py:725 -msgid "" -"Here you can get help of any object by pressing %s in front of it, either on " -"the Editor or the Console.%sHelp can also be shown automatically after " -"writing a left parenthesis next to an object. You can activate this behavior " -"in %s." -msgstr "" -"Pour obtenir de l'aide ici, sélectionner (ou placer le curseur sur) un objet " -"dans l'éditeur ou la console, puis appuyer sur %s.%sL'aide apparaît " -"automatiquement après la saisie d'une parenthèse gauche après un objet si " -"l'option correspondante est activée dans %s." - -#: spyderlib/plugins/inspector.py:731 -msgid "Preferences > Object Inspector" -msgstr "Préférences > Inspecteur d'objets" - -#: spyderlib/plugins/inspector.py:733 -msgid "Usage" -msgstr "Utilisation" - -#: spyderlib/plugins/inspector.py:734 -msgid "New to Spyder? Read our" -msgstr "Vous découvrez Spyder ? Lisez notre" - -#: spyderlib/plugins/inspector.py:735 -msgid "tutorial" -msgstr "Tutoriel" - -#: spyderlib/plugins/inspector.py:742 -msgid "" -"Please consider installing Sphinx to get documentation rendered in rich text." -msgstr "" -"Merci d'installer Sphinx pour obtenir la documentation en texte enrichi." - -#: spyderlib/plugins/inspector.py:913 -msgid "Lock" -msgstr "Verrouiller" - -#: spyderlib/plugins/inspector.py:913 -msgid "Unlock" -msgstr "Déverrouiller" - -#: spyderlib/plugins/inspector.py:955 -msgid "" -"The following error occured when calling Sphinx %s.
    Incompatible " -"Sphinx version or doc string decoding failed.

    Error message:
    %s" -msgstr "" -"L'erreur suivante s'est produite lors de l'exécution de Sphinx %s. " -"
    Veuillez vérifier si cette version de Sphinx est bien prise en charge " -"par Spyder.

    Message d'erreur :
    %s" - -#: spyderlib/plugins/inspector.py:999 -msgid "No source code available." -msgstr "Aucun code source disponible." - -#: spyderlib/plugins/ipythonconsole.py:61 -msgid "Symbolic mathematics in the IPython Console" -msgstr "Mathématiques symboliques pour la console IPython" - -#: spyderlib/plugins/ipythonconsole.py:110 -msgid "" -"The authenticity of host %s can't be established. Are you sure you " -"want to continue connecting?" -msgstr "" -"L'identité du serveur %s ne peut pas être confirmée. Êtes-vous sûr de " -"vouloir poursuivre ?" - -#: spyderlib/plugins/ipythonconsole.py:122 -msgid "The authenticity of the host can't be established" -msgstr "L'identité du serveur ne peut pas être confirmée" - -#: spyderlib/plugins/ipythonconsole.py:129 -msgid "Tunnel '%s' failed to start" -msgstr "Impossible d'ouvrir le tunnel '%s'" - -#: spyderlib/plugins/ipythonconsole.py:134 -msgid "Could not connect to remote host" -msgstr "Impossible d'établir la connection au serveur distant" - -#: spyderlib/plugins/ipythonconsole.py:150 -#: spyderlib/plugins/ipythonconsole.py:665 -msgid "IPython console" -msgstr "Console IPython" - -#: spyderlib/plugins/ipythonconsole.py:162 -msgid "Display initial banner" -msgstr "Afficher le message d'accueil" - -#: spyderlib/plugins/ipythonconsole.py:163 -msgid "" -"This option lets you hide the message shown at\n" -"the top of the console when it's opened." -msgstr "" -"Cette option permet de masquer la message d'accueil\n" -"qui s'affiche à l'ouverture de la console." - -#: spyderlib/plugins/ipythonconsole.py:165 -msgid "Use a completion widget" -msgstr "Utiliser un widget de complétion de code" - -#: spyderlib/plugins/ipythonconsole.py:167 -msgid "Use a widget instead of plain text output for tab completion" -msgstr "Utiliser un widget de complétion au lieu d'une liste en texte brut" - -#: spyderlib/plugins/ipythonconsole.py:169 -msgid "Use a pager to display additional text inside the console" -msgstr "Utiliser un pager pour afficher l'aide" - -#: spyderlib/plugins/ipythonconsole.py:171 -msgid "" -"Useful if you don't want to fill the console with long help or completion " -"texts.\n" -"Note: Use the Q key to get out of the pager." -msgstr "" -"Le pager permet d'éviter de remplir la console de texte d'aide.\n" -"Note : utiliser la touche Q pour quitter le pager." - -#: spyderlib/plugins/ipythonconsole.py:176 -msgid "Ask for confirmation before closing" -msgstr "Demander confirmation avant de fermer une console" - -#: spyderlib/plugins/ipythonconsole.py:189 -msgid "Light background" -msgstr "Fond blanc" - -#: spyderlib/plugins/ipythonconsole.py:191 -msgid "Dark background" -msgstr "Fond noir" - -#: spyderlib/plugins/ipythonconsole.py:201 -msgid "Buffer: " -msgstr "Tampon : " - -#: spyderlib/plugins/ipythonconsole.py:203 -msgid "" -"Set the maximum number of lines of text shown in the\n" -"console before truncation. Specifying -1 disables it\n" -"(not recommended!)" -msgstr "" -"Nombre maximum de lignes de texte affichées dans la console avant troncature " -"(saisir -1 désactive cette dernière, ce qui est fortement déconseillé)." - -#: spyderlib/plugins/ipythonconsole.py:212 -msgid "Support for graphics (Matplotlib)" -msgstr "Prise en charge des graphes (Matplotlib)" - -#: spyderlib/plugins/ipythonconsole.py:213 -msgid "Activate support" -msgstr "Activer" - -#: spyderlib/plugins/ipythonconsole.py:214 -msgid "Automatically load Pylab and NumPy modules" -msgstr "Importer automatiquement Pylab et NumPy" - -#: spyderlib/plugins/ipythonconsole.py:217 -msgid "" -"This lets you load graphics support without importing \n" -"the commands to do plots. Useful to work with other\n" -"plotting libraries different to Matplotlib or to develop \n" -"GUIs with Spyder." -msgstr "" -"Import automatique de toutes les fonctions de représentation graphique et de " -"calcul numérique" - -#: spyderlib/plugins/ipythonconsole.py:236 -msgid "" -"This feature requires the Matplotlib library.\n" -"It seems you don't have it installed." -msgstr "" -"Cette fonctionnalité nécessite l'installation du module Matplotlib.\n" -"Ce dernier n'est apparemment pas installé." - -#: spyderlib/plugins/ipythonconsole.py:241 -msgid "Inline" -msgstr "En ligne" - -#: spyderlib/plugins/ipythonconsole.py:242 -msgid "Automatic" -msgstr "Automatique" - -#: spyderlib/plugins/ipythonconsole.py:243 -msgid "Graphics backend" -msgstr "Sortie graphique" - -#: spyderlib/plugins/ipythonconsole.py:244 -msgid "" -"Decide how graphics are going to be displayed in the console. If unsure, " -"please select %s to put graphics inside the console or %s to " -"interact with them (through zooming and panning) in a separate window." -msgstr "" -"Cette option permet de régler la manière dont les graphes seront affichés " -"dans la console. Par exemple, le mode %s permet l'affichage en ligne " -"des graphes tandis que le mode %s permet d'interagir avec (zoom/pan) " -"dans une fenêtre séparée." - -#: spyderlib/plugins/ipythonconsole.py:264 -msgid "Backend:" -msgstr "Sortie :" - -#: spyderlib/plugins/ipythonconsole.py:266 -msgid "This option will be applied the next time a console is opened." -msgstr "" -"Cette option sera prise en compte lors de la prochaine ouverture de console." - -#: spyderlib/plugins/ipythonconsole.py:278 -msgid "Inline backend" -msgstr "Backend intégré" - -#: spyderlib/plugins/ipythonconsole.py:279 -msgid "Decide how to render the figures created by this backend" -msgstr "Option relative au rendu des figures dans ce backend" - -#: spyderlib/plugins/ipythonconsole.py:283 -msgid "Format:" -msgstr "Format :" - -#: spyderlib/plugins/ipythonconsole.py:286 -msgid "Resolution:" -msgstr "Résolution :" - -#: spyderlib/plugins/ipythonconsole.py:286 -msgid "dpi" -msgstr "ppp" - -#: spyderlib/plugins/ipythonconsole.py:288 -msgid "Only used when the format is PNG. Default is 72" -msgstr "Utilisé uniquement dans le cas du format PNG. Par défaut: 72" - -#: spyderlib/plugins/ipythonconsole.py:291 -msgid "Width:" -msgstr "Largeur :" - -#: spyderlib/plugins/ipythonconsole.py:291 -#: spyderlib/plugins/ipythonconsole.py:295 -msgid "inches" -msgstr "pouces" - -#: spyderlib/plugins/ipythonconsole.py:293 -msgid "Default is 6" -msgstr "Par défaut : 6" - -#: spyderlib/plugins/ipythonconsole.py:295 -msgid "Height:" -msgstr "Hauteur :" - -#: spyderlib/plugins/ipythonconsole.py:297 -msgid "Default is 4" -msgstr "Par défaut : 4" - -#: spyderlib/plugins/ipythonconsole.py:312 -msgid "Run code" -msgstr "Exécuter du code" - -#: spyderlib/plugins/ipythonconsole.py:313 -msgid "" -"You can run several lines of code when a console is started. Please " -"introduce each one separated by commas, for example:
    import os, import " -"sys" -msgstr "" -"Plusieurs lignes de code peuvent être exécutées lors du démarrage de la " -"console. Veuillez séparer deux lignes consécutives par une virgule - par " -"exemple :
    import os, import sys" - -#: spyderlib/plugins/ipythonconsole.py:319 -msgid "Lines:" -msgstr "Lignes :" - -#: spyderlib/plugins/ipythonconsole.py:328 -msgid "Run a file" -msgstr "Exécuter un fichier" - -#: spyderlib/plugins/ipythonconsole.py:329 -msgid "" -"You can also run a whole file at startup instead of just some lines (This is " -"similar to have a PYTHONSTARTUP file)." -msgstr "Option similaire à PYTHONSTARTUP pour un interpréteur standard" - -#: spyderlib/plugins/ipythonconsole.py:333 -msgid "Use the following file:" -msgstr "Utiliser le fichier suivant :" - -#: spyderlib/plugins/ipythonconsole.py:348 -msgid "Greedy completion" -msgstr "Complétion avancée" - -#: spyderlib/plugins/ipythonconsole.py:349 -msgid "" -"Enable Tab completion on elements of lists, results of function " -"calls, etc, without assigning them to a variable.
    For example, you " -"can get completions on things like li[0].<Tab> or ins." -"meth().<Tab>" -msgstr "" -"Active la complétion par Tab sur les éléments de liste, les " -"résultats de fonctions, etc... sans les avoir assignés à une variable." -"
    Par exemple vous pourrez avoir la complétion pour des expressions du " -"type li[0].<Tab> ou ins.meth().<Tab>." - -#: spyderlib/plugins/ipythonconsole.py:357 -msgid "Use the greedy completer" -msgstr "Utiliser la complétion avancée" - -#: spyderlib/plugins/ipythonconsole.py:368 -msgid "Autocall" -msgstr "Appel automatique" - -#: spyderlib/plugins/ipythonconsole.py:369 -msgid "" -"Autocall makes IPython automatically call any callable object even if you " -"didn't type explicit parentheses.
    For example, if you type str 43 " -"it becomes str(43) automatically." -msgstr "" -"L'appel automatique dans IPython va automatiquement appeler les objets " -"appelables même sans parenthèses explicites.
    Par exemple str 43 " -"deviendra automatiquement str(43)." - -#: spyderlib/plugins/ipythonconsole.py:376 -msgid "Smart" -msgstr "Intelligent" - -#: spyderlib/plugins/ipythonconsole.py:377 -msgid "Full" -msgstr "Toujours" - -#: spyderlib/plugins/ipythonconsole.py:378 -msgid "Off" -msgstr "Désactivé" - -#: spyderlib/plugins/ipythonconsole.py:380 -msgid "Autocall: " -msgstr "Appel automatique :" - -#: spyderlib/plugins/ipythonconsole.py:381 -msgid "" -"On %s mode, Autocall is not applied if there are no arguments after " -"the callable. On %s mode, all callable objects are automatically " -"called (even if no arguments are present)." -msgstr "" -"En mode %s, l'appel automatique n'est pas activé si il n'y a pas " -"d'arguments. En mode %s, tous les objets appelables sont " -"automatiquement appelés (même s'il n'y a pas d'arguments)" - -#: spyderlib/plugins/ipythonconsole.py:393 -msgid "Symbolic Mathematics" -msgstr "Calcul formel" - -#: spyderlib/plugins/ipythonconsole.py:394 -msgid "" -"Perfom symbolic operations in the console (e.g. integrals, derivatives, " -"vector calculus, etc) and get the outputs in a beautifully printed style." -msgstr "" -"Utilise le calcul formel pour réaliser des opérations dans la console (par " -"exemple intégrales, dérivées, calcul vectoriel, etc...) et affiche les " -"résultats de manière élégante." - -#: spyderlib/plugins/ipythonconsole.py:399 -msgid "Use symbolic math" -msgstr "Utiliser le calcul formel" - -#: spyderlib/plugins/ipythonconsole.py:400 -msgid "" -"This option loads the Sympy library to work with.
    Please refer to its " -"documentation to learn how to use it." -msgstr "" -"Activer cette option permet de travailler avec la bibliothèque Sympy." -"
    Merci de consulter la documentation pour savoir comment l'utiliser." - -#: spyderlib/plugins/ipythonconsole.py:413 -msgid "" -"This feature requires the Sympy library.\n" -"It seems you don't have it installed." -msgstr "" -"Cette fonctionnalité nécessite l'installation du module Sympy.\n" -"Ce dernier n'est apparemment pas installé." - -#: spyderlib/plugins/ipythonconsole.py:418 -msgid "Prompts" -msgstr "Invites de commande" - -#: spyderlib/plugins/ipythonconsole.py:419 -msgid "Modify how Input and Output prompts are shown in the console." -msgstr "" -"Change l'affichage des invites de commande d'entrée et de sortie de la " -"console." - -#: spyderlib/plugins/ipythonconsole.py:422 -msgid "Input prompt:" -msgstr "En entrée :" - -#: spyderlib/plugins/ipythonconsole.py:424 -msgid "" -"Default is
    In [<span class=\"in-prompt-number\">%i</span>]:" -msgstr "" -"Par défaut :
    In [<span class=\"in-prompt-number\">%i</span>]:" - -#: spyderlib/plugins/ipythonconsole.py:428 -msgid "Output prompt:" -msgstr "En sortie :" - -#: spyderlib/plugins/ipythonconsole.py:430 -msgid "" -"Default is
    Out[<span class=\"out-prompt-number\">%i</span>]:" -msgstr "" -"Par défaut :
    Out[<span class=\"out-prompt-number\">%i</span>]:" - -#: spyderlib/plugins/ipythonconsole.py:446 -msgid "Graphics" -msgstr "Graphiques" - -#: spyderlib/plugins/ipythonconsole.py:448 -#: spyderlib/plugins/workingdirectory.py:42 -msgid "Startup" -msgstr "Démarrage" - -#: spyderlib/plugins/ipythonconsole.py:450 -msgid "Advanced Settings" -msgstr "Options avancées" - -#: spyderlib/plugins/ipythonconsole.py:462 -#: spyderlib/plugins/ipythonconsole.py:725 -msgid "Connect to an existing kernel" -msgstr "Connecter à un noyau existant" - -#: spyderlib/plugins/ipythonconsole.py:464 -msgid "" -"Please enter the connection info of the kernel you want to connect to. For " -"that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for " -"example kernel-3764.json or just 3764)." -msgstr "" -"Entrez les informations de connexion du noyau auquel vous voulez vous " -"connecter. Pour cela vous pouvez soit sélectionner son fichier de connexion " -"JSON en utilisant le bouton Parcourir, ou écrivez directement son " -"identifiant si c'est un noyau local (Exemple : kernel-3764.json ou " -"juste 3764)." - -#: spyderlib/plugins/ipythonconsole.py:475 -msgid "Connection info:" -msgstr "Information de connexion :" - -#: spyderlib/plugins/ipythonconsole.py:477 -msgid "Path to connection file or kernel id" -msgstr "Chemin vers un fichier de connexion ou identifiant de noyau" - -#: spyderlib/plugins/ipythonconsole.py:479 -#: spyderlib/plugins/ipythonconsole.py:497 -msgid "Browse" -msgstr "Parcourir" - -#: spyderlib/plugins/ipythonconsole.py:489 -msgid "This is a remote kernel" -msgstr "Noyau distant" - -#: spyderlib/plugins/ipythonconsole.py:493 -msgid "username@hostname:port" -msgstr "utilisateur@hôte:port" - -#: spyderlib/plugins/ipythonconsole.py:496 -msgid "Path to ssh key file" -msgstr "Chemin vers la clé ssh" - -#: spyderlib/plugins/ipythonconsole.py:505 -msgid "Password or ssh key passphrase" -msgstr "Mot de passe ou passphrase de clé ssh" - -#: spyderlib/plugins/ipythonconsole.py:509 -msgid "Host name" -msgstr "Nom d'hôte" - -#: spyderlib/plugins/ipythonconsole.py:510 -msgid "Ssh key" -msgstr "Clé ssh" - -#: spyderlib/plugins/ipythonconsole.py:511 -msgid "Password" -msgstr "Mot de passe" - -#: spyderlib/plugins/ipythonconsole.py:540 -msgid "Open IPython connection file" -msgstr "Ouvrir un fichier de connexion IPython" - -#: spyderlib/plugins/ipythonconsole.py:546 -msgid "Select ssh key" -msgstr "Sélectionner une clé ssh" - -#: spyderlib/plugins/ipythonconsole.py:713 -msgid "Open an &IPython console" -msgstr "Ouvrir une console &IPython" - -#: spyderlib/plugins/ipythonconsole.py:716 -msgid "Use %s+T when the console is selected to open a new one" -msgstr "" -"Quand la console est sélectionnée, utiliser %s+T pour ouvrir une nouvelle " -"console" - -#: spyderlib/plugins/ipythonconsole.py:719 -#, fuzzy -msgid "Open a new console" -msgstr "Ouvrir une nouvelle console" - -#: spyderlib/plugins/ipythonconsole.py:726 -msgid "Open a new IPython console connected to an existing kernel" -msgstr "Ouvrir une nouvelle console IPython connecté à un noyau existant" - -#: spyderlib/plugins/ipythonconsole.py:809 -msgid "" -"No IPython console is currently available to run %s.

    Please " -"open a new one and try again." -msgstr "" -"Aucun client IPython n'est actuellement sélectionné pour exécuter %s." -"

    Merci d'ouvrir un nouveau client IPython et de réessayer." - -#: spyderlib/plugins/ipythonconsole.py:950 -msgid "" -"Do you want to close all other consoles connected to the same kernel as this " -"one?" -msgstr "" -"Voulez-vous fermer les toutes les autres consoles connectées au même noyau " -"que celle-ci ?" - -#: spyderlib/plugins/ipythonconsole.py:1032 -msgid "Connection error" -msgstr "Erreur de connexion" - -#: spyderlib/plugins/ipythonconsole.py:1033 -msgid "" -"Could not open ssh tunnel. The error was:\n" -"\n" -msgstr "" -"Impossible d'ouvrir un tunnel ssh. L'erreur rencontrée est:\n" -"\n" - -#: spyderlib/plugins/ipythonconsole.py:1069 -msgid "IPython" -msgstr "IPython" - -#: spyderlib/plugins/ipythonconsole.py:1070 -msgid "Unable to connect to IPython %s" -msgstr "Impossible de se connecter au noyau IPython `%s`" - -#: spyderlib/plugins/ipythonconsole.py:1121 -msgid "Are you sure you want to restart the kernel?" -msgstr "Souhaitez-vous vraiment redémarrer le noyau ?" - -#: spyderlib/plugins/ipythonconsole.py:1123 -msgid "Restart kernel?" -msgstr "Redémarrer le noyau" - -#: spyderlib/plugins/onlinehelp.py:67 -msgid "Online help" -msgstr "Aide en ligne" - -#: spyderlib/plugins/outlineexplorer.py:47 -#: spyderlib/widgets/editortools.py:194 -msgid "Outline" -msgstr "Structure" - -#: spyderlib/plugins/projectexplorer.py:41 -#: spyderlib/widgets/projectexplorer.py:1137 -#: spyderlib/widgets/projectexplorer.py:1151 -msgid "Project explorer" -msgstr "Explorateur de projets" - -#: spyderlib/plugins/projectexplorer.py:52 -#: spyderlib/widgets/projectexplorer.py:545 -msgid "New project..." -msgstr "Nouveau projet..." - -#: spyderlib/plugins/runconfig.py:28 -msgid "Execute in current Python or IPython console" -msgstr "Exécuter dans la console Python ou IPython active" - -#: spyderlib/plugins/runconfig.py:29 -msgid "Execute in a new dedicated Python console" -msgstr "Exécuter dans une nouvelle console Python dédiée" - -#: spyderlib/plugins/runconfig.py:30 -msgid "Execute in an external System terminal" -msgstr "Exécuter dans un terminal système externe" - -#: spyderlib/plugins/runconfig.py:40 -msgid "Always show %s on a first file run" -msgstr "Toujours afficher %s lors de la première exécution d'un script" - -#: spyderlib/plugins/runconfig.py:153 -msgid "General settings" -msgstr "Options générales" - -#: spyderlib/plugins/runconfig.py:156 spyderlib/plugins/runconfig.py:201 -msgid "Command line options:" -msgstr "Options en ligne de commande :" - -#: spyderlib/plugins/runconfig.py:163 -msgid "Working directory:" -msgstr "Répertoire de travail :" - -#: spyderlib/plugins/runconfig.py:189 -msgid "Dedicated Python console" -msgstr "Console Python dédiée" - -#: spyderlib/plugins/runconfig.py:194 -msgid "Interact with the Python console after execution" -msgstr "Interagir avec la console Python après l'exécution" - -#: spyderlib/plugins/runconfig.py:198 -msgid "Show warning when killing running process" -msgstr "Afficher un avertissement à l'interruption d'un processus" - -#: spyderlib/plugins/runconfig.py:207 -msgid "-u is added to the other options you set here" -msgstr "L'option -u est ajoutée aux autres options spécifiées ici" - -#: spyderlib/plugins/runconfig.py:218 -msgid "this dialog" -msgstr "cette fenêtre" - -#: spyderlib/plugins/runconfig.py:276 -msgid "Run configuration" -msgstr "Configuration d'exécution" - -#: spyderlib/plugins/runconfig.py:277 -msgid "The following working directory is not valid:
    %s" -msgstr "Le répertoire de travail suivant n'est pas valide :
    %s" - -#: spyderlib/plugins/runconfig.py:353 -msgid "Run settings for %s" -msgstr "Options d'exécution pour %s" - -#: spyderlib/plugins/runconfig.py:384 -msgid "Select a run configuration:" -msgstr "Sélectionner une configuration d'exécution :" - -#: spyderlib/plugins/runconfig.py:414 spyderlib/plugins/runconfig.py:439 -msgid "Run Settings" -msgstr "Options d'exécution" - -#: spyderlib/plugins/runconfig.py:441 -msgid "" -"The following are the default %s. These options may be overriden " -"using the %s dialog box (see the %s menu)" -msgstr "" -"La page suivante présente les réglages par défaut pour les %s. Ces " -"réglages peuvent être remplacés à tout moment en utilisant la boîte de " -"dialogue %s (voir le menu %s)" - -#: spyderlib/plugins/runconfig.py:465 -#: spyderlib/widgets/externalshell/pythonshell.py:297 -msgid "Working directory" -msgstr "Répertoire de travail" - -#: spyderlib/plugins/runconfig.py:467 -msgid "Default working directory is:" -msgstr "Le répertoire de travail par défaut est :" - -#: spyderlib/plugins/runconfig.py:469 -msgid "the script directory" -msgstr "le répertoire du fichier à exécuter" - -#: spyderlib/plugins/runconfig.py:472 spyderlib/plugins/workingdirectory.py:54 -msgid "the following directory:" -msgstr "le répertoire suivant :" - -#: spyderlib/plugins/runconfig.py:491 -msgid "Run Settings dialog" -msgstr "la fenêtre Options d'exécution" - -#: spyderlib/plugins/shortcuts.py:178 -msgid "Context" -msgstr "Contexte" - -#: spyderlib/plugins/shortcuts.py:180 spyderlib/widgets/dicteditor.py:158 -msgid "Name" -msgstr "Nom" - -#: spyderlib/plugins/shortcuts.py:182 -msgid "Mod1" -msgstr "Mod1" - -#: spyderlib/plugins/shortcuts.py:184 -msgid "Mod2" -msgstr "Mod2" - -#: spyderlib/plugins/shortcuts.py:186 -msgid "Mod3" -msgstr "Mod3" - -#: spyderlib/plugins/shortcuts.py:188 spyderlib/widgets/dicteditor.py:169 -msgid "Key" -msgstr "Clé" - -#: spyderlib/plugins/shortcuts.py:321 -msgid "Conflicts" -msgstr "Conflits" - -#: spyderlib/plugins/shortcuts.py:322 -msgid "The following conflicts have been detected:" -msgstr "Les conflits suivants ont été détectés :" - -#: spyderlib/plugins/shortcuts.py:334 -msgid "Keyboard shortcuts" -msgstr "Raccourcis clavier" - -#: spyderlib/plugins/variableexplorer.py:24 -msgid "Autorefresh" -msgstr "Rafraîchissement automatique" - -#: spyderlib/plugins/variableexplorer.py:25 -msgid "Enable autorefresh" -msgstr "Activer le rafraîchissement automatique" - -#: spyderlib/plugins/variableexplorer.py:27 -msgid "Refresh interval: " -msgstr "Période de rafraîchissement : " - -#: spyderlib/plugins/variableexplorer.py:28 -msgid " ms" -msgstr " ms" - -#: spyderlib/plugins/variableexplorer.py:31 -msgid "Filter" -msgstr "Filtre" - -#: spyderlib/plugins/variableexplorer.py:33 -#: spyderlib/widgets/externalshell/namespacebrowser.py:196 -msgid "Exclude private references" -msgstr "Exclure les références privées" - -#: spyderlib/plugins/variableexplorer.py:34 -#: spyderlib/widgets/externalshell/namespacebrowser.py:211 -msgid "Exclude capitalized references" -msgstr "Exclure les références commençant par une majuscule" - -#: spyderlib/plugins/variableexplorer.py:35 -#: spyderlib/widgets/externalshell/namespacebrowser.py:204 -msgid "Exclude all-uppercase references" -msgstr "Exclure les références en lettres capitales" - -#: spyderlib/plugins/variableexplorer.py:36 -#: spyderlib/widgets/externalshell/namespacebrowser.py:219 -msgid "Exclude unsupported data types" -msgstr "Exclure les types non supportés" - -#: spyderlib/plugins/variableexplorer.py:42 -#: spyderlib/widgets/dicteditor.py:702 -msgid "Truncate values" -msgstr "Tronquer les valeurs" - -#: spyderlib/plugins/variableexplorer.py:44 -#: spyderlib/widgets/dicteditor.py:706 -msgid "Show arrays min/max" -msgstr "Afficher les min/max des tableaux" - -#: spyderlib/plugins/variableexplorer.py:46 -msgid "Edit data in the remote process" -msgstr "Éditeurs dans le processus distant" - -#: spyderlib/plugins/variableexplorer.py:47 -msgid "" -"Editors are opened in the remote process for NumPy arrays, PIL images, " -"lists, tuples and dictionaries.\n" -"This avoids transfering large amount of data between the remote process and " -"Spyder (through the socket)." -msgstr "" -"Les tableaux NumPy, images PIL, listes, tuples et dictionnaires seront " -"modifiés dans un éditeur exécuté dans le processus distant.\n" -"Cela permet d'éviter de transférer de gros volumes de données entre le " -"processus distant et Spyder (à travers le socket)." - -#: spyderlib/plugins/variableexplorer.py:158 -msgid "Variable explorer" -msgstr "Explorateur de variables" - -#: spyderlib/plugins/workingdirectory.py:35 -msgid "" -"The global working directory is the working directory for newly " -"opened consoles (Python/IPython consoles and terminals), for the " -"file explorer, for the find in files plugin and for new files " -"created in the editor." -msgstr "" -"Le répertoire de travail global est le répertoire de travail utilisé " -"pour les nouvelles consoles (consoles Python/IPython et terminaux), " -"pour l'explorateur de fichiers, pour la recherche dans les " -"fichiers et pour les fichiers créés dans l'éditeur." - -#: spyderlib/plugins/workingdirectory.py:44 -msgid "At startup, the global working directory is:" -msgstr "Au démarrage, le répertoire de travail global est :" - -#: spyderlib/plugins/workingdirectory.py:48 -msgid "the same as in last session" -msgstr "celui utilisé lors de la dernière session" - -#: spyderlib/plugins/workingdirectory.py:50 -msgid "At startup, Spyder will restore the global directory from last session" -msgstr "" -"Au démarrage, Spyder reprendra le répertoire de travail global de la " -"dernière session" - -#: spyderlib/plugins/workingdirectory.py:56 -msgid "At startup, the global working directory will be the specified path" -msgstr "" -"Au démarrage, le répertoire de travail global sera le chemin d'accès " -"spécifié ici" - -#: spyderlib/plugins/workingdirectory.py:70 -msgid "Files are opened from:" -msgstr "Les fichiers sont ouverts depuis :" - -#: spyderlib/plugins/workingdirectory.py:74 -#: spyderlib/plugins/workingdirectory.py:87 -msgid "the current file directory" -msgstr "le répertoire du fichier en cours d'édition" - -#: spyderlib/plugins/workingdirectory.py:78 -#: spyderlib/plugins/workingdirectory.py:91 -msgid "the global working directory" -msgstr "le répertoire de travail global" - -#: spyderlib/plugins/workingdirectory.py:83 -msgid "Files are created in:" -msgstr "Les fichiers sont créés dans :" - -#: spyderlib/plugins/workingdirectory.py:97 -msgid "Change to file base directory" -msgstr "Sélectionner le répertoire de base du fichier" - -#: spyderlib/plugins/workingdirectory.py:99 -msgid "When opening a file" -msgstr "Lors de l'ouverture d'un fichier" - -#: spyderlib/plugins/workingdirectory.py:101 -msgid "When saving a file" -msgstr "Lors de l'enregistrement d'un fichier" - -#: spyderlib/plugins/workingdirectory.py:160 -msgid "Back" -msgstr "Retour" - -#: spyderlib/plugins/workingdirectory.py:168 -#: spyderlib/widgets/explorer.py:1004 spyderlib/widgets/importwizard.py:523 -msgid "Next" -msgstr "Suivant" - -#: spyderlib/plugins/workingdirectory.py:181 -msgid "" -"This is the working directory for newly\n" -"opened consoles (Python/IPython consoles and\n" -"terminals), for the file explorer, for the\n" -"find in files plugin and for new files\n" -"created in the editor" -msgstr "" -"Ceci est le répertoire de travail utilisé pour\n" -"les nouvelles consoles (consoles Python/IPython\n" -"et fenêtres de commandes), pour l'explorateur\n" -"de fichiers, pour la recherche dans les fichiers\n" -"et pour les fichiers créés dans l'éditeur" - -#: spyderlib/plugins/workingdirectory.py:207 -msgid "Browse a working directory" -msgstr "Sélectionner un répertoire de travail" - -#: spyderlib/plugins/workingdirectory.py:213 -msgid "Set as current console's working directory" -msgstr "Changer le répertoire de travail de la console actuelle" - -#: spyderlib/plugins/workingdirectory.py:221 -msgid "Change to parent directory" -msgstr "Aller au répertoire parent" - -#: spyderlib/plugins/workingdirectory.py:228 -msgid "Global working directory" -msgstr "Répertoire de travail global" - -#: spyderlib/spyder.py:120 -msgid "Initializing..." -msgstr "Initialisation..." - -#: spyderlib/spyder.py:244 -msgid "Numpy and Scipy documentation" -msgstr "Documentation de Numpy et Scipy" - -#: spyderlib/spyder.py:246 spyderlib/spyder.py:949 -msgid "Matplotlib documentation" -msgstr "Documentation de Matplotlib" - -#: spyderlib/spyder.py:249 -msgid "PyQt4 Reference Guide" -msgstr "Guide de référence de PyQt4" - -#: spyderlib/spyder.py:252 -msgid "PyQt4 API Reference" -msgstr "Documentation de l'API de PyQt4" - -#: spyderlib/spyder.py:254 -msgid "Python(x,y)" -msgstr "Python(x,y)" - -#: spyderlib/spyder.py:256 -msgid "WinPython" -msgstr "WinPython" - -#: spyderlib/spyder.py:293 -msgid "Reload last session" -msgstr "Recharger la session précédente" - -#: spyderlib/spyder.py:297 -msgid "Load session..." -msgstr "Charger une session..." - -#: spyderlib/spyder.py:300 -msgid "Load Spyder session" -msgstr "Charger une session Spyder" - -#: spyderlib/spyder.py:302 -msgid "Save session and quit..." -msgstr "Enregistrer la session et quitter..." - -#: spyderlib/spyder.py:305 -msgid "Save current session and quit application" -msgstr "Enregistrer la session en cours et quitter l'application" - -#: spyderlib/spyder.py:483 -msgid "Close current pane" -msgstr "Fermer le volet courant" - -#: spyderlib/spyder.py:489 -msgid "&Find text" -msgstr "Rec&hercher" - -#: spyderlib/spyder.py:494 -msgid "Find &next" -msgstr "Rechercher le &suivant" - -#: spyderlib/spyder.py:500 -msgid "Find &previous" -msgstr "Rechercher le &précédent" - -#: spyderlib/spyder.py:505 -msgid "&Replace text" -msgstr "&Remplacer" - -#: spyderlib/spyder.py:520 spyderlib/widgets/sourcecode/codeeditor.py:2268 -msgid "Undo" -msgstr "Annuler" - -#: spyderlib/spyder.py:522 spyderlib/widgets/sourcecode/codeeditor.py:2271 -msgid "Redo" -msgstr "Répéter" - -#: spyderlib/spyder.py:523 spyderlib/widgets/arrayeditor.py:392 -#: spyderlib/widgets/dataframeeditor.py:418 -#: spyderlib/widgets/dicteditor.py:674 spyderlib/widgets/shell.py:118 -#: spyderlib/widgets/sourcecode/codeeditor.py:2277 -msgid "Copy" -msgstr "Copier" - -#: spyderlib/spyder.py:525 spyderlib/widgets/shell.py:114 -#: spyderlib/widgets/sourcecode/codeeditor.py:2274 -msgid "Cut" -msgstr "Couper" - -#: spyderlib/spyder.py:526 spyderlib/widgets/dicteditor.py:671 -#: spyderlib/widgets/shell.py:122 -#: spyderlib/widgets/sourcecode/codeeditor.py:2280 -msgid "Paste" -msgstr "Coller" - -#: spyderlib/spyder.py:528 spyderlib/widgets/explorer.py:461 -#: spyderlib/widgets/projectexplorer.py:1003 spyderlib/widgets/shell.py:131 -#: spyderlib/widgets/sourcecode/codeeditor.py:2283 -msgid "Delete" -msgstr "Supprimer" - -#: spyderlib/spyder.py:531 spyderlib/widgets/shell.py:135 -#: spyderlib/widgets/sourcecode/codeeditor.py:2287 -msgid "Select All" -msgstr "Sélectionner tout" - -#: spyderlib/spyder.py:580 -msgid "C&onsoles" -msgstr "C&onsoles" - -#: spyderlib/spyder.py:586 -msgid "&View" -msgstr "&Affichage" - -#: spyderlib/spyder.py:589 -msgid "&Help" -msgstr "A&ide" - -#: spyderlib/spyder.py:594 -msgid "Welcome to Spyder!" -msgstr "Bienvenue dans Spyder !" - -#: spyderlib/spyder.py:599 -msgid "Pre&ferences" -msgstr "Pré&férences" - -#: spyderlib/spyder.py:606 spyderlib/widgets/pathmanager.py:45 -#: spyderlib/widgets/projectexplorer.py:594 -msgid "PYTHONPATH manager" -msgstr "Gestionnaire de PYTHONPATH" - -#: spyderlib/spyder.py:609 -msgid "Python Path Manager" -msgstr "Gestionnaire de chemins d'accès Python" - -#: spyderlib/spyder.py:612 -msgid "Update module names list" -msgstr "Mise à jour de la liste des modules" - -#: spyderlib/spyder.py:614 -msgid "Refresh list of module names available in PYTHONPATH" -msgstr "" -"Mise à jour de la liste des modules disponibles notamment à travers " -"PYTHONPATH" - -#: spyderlib/spyder.py:619 -msgid "Current user environment variables..." -msgstr "Variables d'environnement de l'utilisateur..." - -#: spyderlib/spyder.py:621 -msgid "" -"Show and edit current user environment variables in Windows registry (i.e. " -"for all sessions)" -msgstr "" -"Afficher et modifier les variables d'environnement de l'utilisateur courant " -"dans Windows (c'est-à-dire directement dans la base de registre)" - -#: spyderlib/spyder.py:629 spyderlib/spyder.py:1043 -msgid "External Tools" -msgstr "Outils externes" - -#: spyderlib/spyder.py:633 -msgid "Python(x,y) launcher" -msgstr "Accueil de Python(x,y)" - -#: spyderlib/spyder.py:640 -msgid "WinPython control panel" -msgstr "Panneau de contrôle WinPython" - -#: spyderlib/spyder.py:649 -msgid "Qt Designer" -msgstr "Qt Designer" - -#: spyderlib/spyder.py:654 -msgid "Qt Linguist" -msgstr "Qt Linguist" - -#: spyderlib/spyder.py:660 -msgid "Qt examples" -msgstr "Exemples Qt" - -#: spyderlib/spyder.py:678 -msgid "guidata examples" -msgstr "Exemples guidata" - -#: spyderlib/spyder.py:686 -msgid "guiqwt examples" -msgstr "Exemples guiqwt" - -#: spyderlib/spyder.py:691 -msgid "Sift" -msgstr "Sift" - -#: spyderlib/spyder.py:699 -msgid "ViTables" -msgstr "ViTables" - -#: spyderlib/spyder.py:713 -msgid "Fullscreen mode" -msgstr "Mode plein écran" - -#: spyderlib/spyder.py:725 -msgid "Main toolbar" -msgstr "Barre d'outil principale" - -#: spyderlib/spyder.py:734 -msgid "" -"Spyder Internal Console\n" -"\n" -"This console is used to report application\n" -"internal errors and to inspect Spyder\n" -"internals with the following commands:\n" -" spy.app, spy.window, dir(spy)\n" -"\n" -"Please don't use it to run your code\n" -"\n" -msgstr "" -"Console interne de Spyder\n" -"\n" -"Il s'agit d'une console de débogage\n" -"utilisée par Spyder pour signaler des erreurs\n" -"internes ou pour inspecter les entrailles de\n" -"l'application avec les commandes ci-dessous :\n" -" spy.app, spy.window, dir(spy)\n" -"\n" -"Ne l'utilisez pas pour exécuter votre propre code.\n" -"\n" - -#: spyderlib/spyder.py:751 -msgid "Loading object inspector..." -msgstr "Chargement de l'inspecteur d'objet..." - -#: spyderlib/spyder.py:758 -msgid "Loading outline explorer..." -msgstr "Chargement de l'explorateur de structure..." - -#: spyderlib/spyder.py:766 -msgid "Loading editor..." -msgstr "Chargement de l'éditeur..." - -#: spyderlib/spyder.py:791 -msgid "Loading file explorer..." -msgstr "Chargement de l'explorateur de fichiers..." - -#: spyderlib/spyder.py:798 -msgid "Loading history plugin..." -msgstr "Chargement du journal d'historique..." - -#: spyderlib/spyder.py:809 -msgid "Loading online help..." -msgstr "Chargement de l'aide en ligne..." - -#: spyderlib/spyder.py:815 -msgid "Loading project explorer..." -msgstr "Chargement de l'explorateur de projet..." - -#: spyderlib/spyder.py:826 -msgid "Loading external console..." -msgstr "Chargement de la console externe..." - -#: spyderlib/spyder.py:835 -msgid "Loading namespace browser..." -msgstr "Chargement de l'explorateur d'espace de noms..." - -#: spyderlib/spyder.py:842 -msgid "Loading IPython console..." -msgstr "Chargement de la console IPython..." - -#: spyderlib/spyder.py:853 -msgid "Setting up main window..." -msgstr "Configuration de la fenêtre principale..." - -#: spyderlib/spyder.py:856 -msgid "Optional dependencies..." -msgstr "Dépendances optionnelles..." - -#: spyderlib/spyder.py:860 -msgid "Report issue..." -msgstr "Rapport d'erreur..." - -#: spyderlib/spyder.py:864 -msgid "Spyder support..." -msgstr "Support technique de Spyder..." - -#: spyderlib/spyder.py:887 -msgid "Spyder documentation" -msgstr "Documentation de Spyder" - -#: spyderlib/spyder.py:889 -msgid "Spyder tutorial" -msgstr "Tutoriel de Spyder" - -#: spyderlib/spyder.py:896 -msgid "Python documentation" -msgstr "Documentation de Python" - -#: spyderlib/spyder.py:902 spyderlib/spyder.py:941 -msgid "IPython documentation" -msgstr "Documentation de IPython" - -#: spyderlib/spyder.py:903 -msgid "Intro to IPython" -msgstr "Introduction à IPython" - -#: spyderlib/spyder.py:905 -msgid "Quick reference" -msgstr "Référence rapide" - -#: spyderlib/spyder.py:907 -msgid "Console help" -msgstr "Aide sur la console" - -#: spyderlib/spyder.py:939 -msgid "Python(x,y) documentation folder" -msgstr "Dossier de documentation Python(x,y)" - -#: spyderlib/spyder.py:943 -msgid "guidata documentation" -msgstr "Documentation de guidata" - -#: spyderlib/spyder.py:946 -msgid "guiqwt documentation" -msgstr "Documentation de guiqwt" - -#: spyderlib/spyder.py:952 -msgid "NumPy documentation" -msgstr "Documentation de NumPy" - -#: spyderlib/spyder.py:954 -msgid "NumPy reference guide" -msgstr "Manuel de référence de NumPy" - -#: spyderlib/spyder.py:956 -msgid "NumPy user guide" -msgstr "Manuel de l'utilisateur de NumPy" - -#: spyderlib/spyder.py:958 -msgid "SciPy documentation" -msgstr "Documentation de SciPy" - -#: spyderlib/spyder.py:965 -msgid "Installed Python modules" -msgstr "Modules Python installés" - -#: spyderlib/spyder.py:969 -msgid "Online documentation" -msgstr "Documentation en ligne" - -#: spyderlib/spyder.py:979 -msgid "Qt documentation" -msgstr "Documentation de Qt" - -#: spyderlib/spyder.py:985 -msgid "About %s..." -msgstr "À propos de %s..." - -#: spyderlib/spyder.py:1006 -msgid "Panes" -msgstr "Volets" - -#: spyderlib/spyder.py:1007 -msgid "Toolbars" -msgstr "Barres d'outils" - -#: spyderlib/spyder.py:1010 -msgid "Reset window layout" -msgstr "Réinitialiser la disposition des fenêtres" - -#: spyderlib/spyder.py:1012 -msgid "Custom window layouts" -msgstr "Dispositions de fenêtres personnalisées" - -#: spyderlib/spyder.py:1018 -msgid "Switch to/from layout %d" -msgstr "Basculer vers/depuis la disposition %d" - -#: spyderlib/spyder.py:1023 -msgid "Set layout %d" -msgstr "Définir la disposition %d" - -#: spyderlib/spyder.py:1031 -msgid "Attached console window (debugging)" -msgstr "Invite de commandes attaché (débogage)" - -#: spyderlib/spyder.py:1332 -msgid "" -"Window layout will be reset to default settings: this affects window " -"position, size and dockwidgets.\n" -"Do you want to continue?" -msgstr "" -"La disposition des fenêtres sera réinitialisée selon les réglages par " -"défaut.\n" -"Souhaitez-vous continuer ?" - -#: spyderlib/spyder.py:1350 -msgid "Quick switch layout #%d has not yet been defined." -msgstr "" -"La disposition de fenêtre personnalisée n°%d n'a pas encore été définie." - -#: spyderlib/spyder.py:1602 spyderlib/spyder.py:1603 -msgid "Maximize current pane" -msgstr "Agrandir le volet courant" - -#: spyderlib/spyder.py:1606 -msgid "Restore current pane" -msgstr "Restaurer le volet courant" - -#: spyderlib/spyder.py:1607 -msgid "Restore pane to its original size" -msgstr "Restaurer le volet à sa taille d'origine" - -#: spyderlib/spyder.py:1686 -msgid "About %s" -msgstr "À propos de %s" - -#: spyderlib/spyder.py:1851 -msgid "Running an external system terminal is not supported on platform %s." -msgstr "" -"L'exécution dans un terminal système externe n'est pas prise en charge sur " -"la plateforme %s." - -#: spyderlib/spyder.py:2066 -msgid "Open session" -msgstr "Ouvrir une session" - -#: spyderlib/spyder.py:2067 spyderlib/spyder.py:2078 -msgid "Spyder sessions" -msgstr "Sessions Spyder" - -#: spyderlib/spyder.py:2077 -msgid "Save session" -msgstr "Enregistrer la session" - -#: spyderlib/utils/codeanalysis.py:92 -msgid "Real-time code analysis on the Editor" -msgstr "Analyse de code temps réel dans l'éditeur" - -#: spyderlib/utils/codeanalysis.py:96 -msgid "Real-time code style analysis on the Editor" -msgstr "Analyse de code temps réel dans l'éditeur" - -#: spyderlib/utils/environ.py:95 -msgid "" -"Module pywin32 was not found.
    Please restart this Windows " -"session (not the computer) for changes to take effect." -msgstr "" -"Le module pywin32 n'est pas installé.
    Merci de redémarrer la " -"session en cours (et non l'ordinateur) pour que les changements " -"effectués prennent effet." - -#: spyderlib/utils/environ.py:108 -msgid "" -"If you accept changes, this will modify the current user environment " -"variables directly in Windows registry. Use it with precautions, at " -"your own risks.

    Note that for changes to take effect, you will need " -"to restart the parent process of this application (simply restart Spyder if " -"you have executed it from a Windows shortcut, otherwise restart any " -"application from which you may have executed it, like Python(x,y) Home for example)" -msgstr "" -"Si vous acceptez les changements effectués, cela modifiera les variables " -"d'environnement de l'utilisateur courant directement dans la base de " -"registre Windows. Utilisez cette fonctionnalité avec précautions et à " -"vos risques et périls.

    Notez que pour que les changements effectués " -"prennent effet, il sera nécessaire de redémarrer le processus parent de " -"cette application (redémarrez simplement Spyder si vous l'avez exécuté à " -"partir d'un raccourci Windows, sinon redémarrez toute application ayant " -"servie à exécuter Spyder : Python(x,y) Home ou un invite de commandes " -"par exemple)" - -#: spyderlib/utils/inspector/sphinxify.py:209 -#: spyderlib/utils/inspector/sphinxify.py:219 -msgid "" -"It was not possible to generate rich text help for this object.
    Please " -"see it in plain text." -msgstr "" -"Le processus de génération de l'aide sous la forme de texte enrichi a échoué." -"
    Merci d'activer le mode d'affichage en texte brut." - -#: spyderlib/utils/introspection/jedi_plugin.py:32 -msgid "(Experimental) Editor's code completion, go-to-definition and help" -msgstr "(Expérimental) : autocomplétion, aller à la définition, aide." - -#: spyderlib/utils/introspection/rope_plugin.py:37 -msgid "Editor's code completion, go-to-definition and help" -msgstr "Editeur : complétion de code, aller à la définition, etc." - -#: spyderlib/utils/iofuncs.py:496 -msgid "Supported files" -msgstr "Fichiers compatibles" - -#: spyderlib/utils/iofuncs.py:498 -msgid "All files (*.*)" -msgstr "Tous les fichiers (*.*)" - -#: spyderlib/utils/iofuncs.py:508 -msgid "Spyder data files" -msgstr "Fichiers Spyder" - -#: spyderlib/utils/iofuncs.py:510 spyderlib/widgets/dicteditor.py:1041 -msgid "NumPy arrays" -msgstr "Tableaux NumPy" - -#: spyderlib/utils/iofuncs.py:511 -msgid "NumPy zip arrays" -msgstr "Tableaux NumPy compressés" - -#: spyderlib/utils/iofuncs.py:512 -msgid "Matlab files" -msgstr "Fichiers Matlab" - -#: spyderlib/utils/iofuncs.py:513 -msgid "CSV text files" -msgstr "Fichiers texte CSV" - -#: spyderlib/utils/iofuncs.py:515 -msgid "JPEG images" -msgstr "Images JPEG" - -#: spyderlib/utils/iofuncs.py:516 -msgid "PNG images" -msgstr "Images PNG" - -#: spyderlib/utils/iofuncs.py:517 -msgid "GIF images" -msgstr "Images GIF" - -#: spyderlib/utils/iofuncs.py:518 -msgid "TIFF images" -msgstr "Images TIFF" - -#: spyderlib/utils/iofuncs.py:519 spyderlib/utils/iofuncs.py:520 -msgid "Pickle files" -msgstr "Fichiers pickle" - -#: spyderlib/utils/iofuncs.py:521 -msgid "JSON files" -msgstr "Fichiers JSON" - -#: spyderlib/utils/iofuncs.py:540 spyderlib/utils/iofuncs.py:547 -msgid "Unsupported file type '%s'" -msgstr "Type de fichier non pris en charge '%s'" - -#: spyderlib/utils/programs.py:176 -msgid "It was not possible to run this file in an external terminal" -msgstr "Impossible d'exécuter ce fichier dans un terminal externe" - -#: spyderlib/widgets/arrayeditor.py:452 spyderlib/widgets/arrayeditor.py:485 -#: spyderlib/widgets/dataframeeditor.py:507 -#: spyderlib/widgets/dataframeeditor.py:549 -msgid "Format" -msgstr "Format" - -#: spyderlib/widgets/arrayeditor.py:457 -#: spyderlib/widgets/dataframeeditor.py:511 -msgid "Resize" -msgstr "Ajuster" - -#: spyderlib/widgets/arrayeditor.py:486 -#: spyderlib/widgets/dataframeeditor.py:550 -msgid "Float formatting" -msgstr "Format de flottant" - -#: spyderlib/widgets/arrayeditor.py:493 -#: spyderlib/widgets/dataframeeditor.py:558 spyderlib/widgets/explorer.py:578 -#: spyderlib/widgets/explorer.py:681 -#: spyderlib/widgets/externalshell/pythonshell.py:537 -#: spyderlib/widgets/externalshell/systemshell.py:93 -msgid "Error" -msgstr "Erreur" - -#: spyderlib/widgets/arrayeditor.py:494 -#: spyderlib/widgets/dataframeeditor.py:559 -msgid "Format (%s) is incorrect" -msgstr "Le format (%s) n'est pas valide" - -#: spyderlib/widgets/arrayeditor.py:528 -msgid "Array is empty" -msgstr "Ce tableau est vide" - -#: spyderlib/widgets/arrayeditor.py:531 -msgid "Arrays with more than 3 dimensions are not supported" -msgstr "Les tableaux de plus de trois dimensions ne sont pas pris en charge" - -#: spyderlib/widgets/arrayeditor.py:535 -msgid "The 'xlabels' argument length do no match array column number" -msgstr "" -"La taille de 'xlabels' ne correspond pas au nombre de colonnes du tableau" - -#: spyderlib/widgets/arrayeditor.py:539 -msgid "The 'ylabels' argument length do no match array row number" -msgstr "" -"La taille de 'ylabels' ne correspond pas au nombre de lignes du tableau" - -#: spyderlib/widgets/arrayeditor.py:546 -msgid "%s arrays" -msgstr "Les tableaux %s" - -#: spyderlib/widgets/arrayeditor.py:547 -msgid "%s are currently not supported" -msgstr "%s ne sont actuellement pas pris en charge" - -#: spyderlib/widgets/arrayeditor.py:554 -#, fuzzy -msgid "NumPy array" -msgstr "Tableaux NumPy" - -#: spyderlib/widgets/arrayeditor.py:556 spyderlib/widgets/arrayeditor.py:713 -msgid "Array editor" -msgstr "Éditeur de tableaux" - -#: spyderlib/widgets/arrayeditor.py:558 -msgid "read only" -msgstr "lecture seule" - -#: spyderlib/widgets/arrayeditor.py:589 -msgid "Record array fields:" -msgstr "Champs du tableau : " - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Data" -msgstr "Données" - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Mask" -msgstr "Masque" - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Masked data" -msgstr "Données masquées" - -#: spyderlib/widgets/arrayeditor.py:614 -msgid "Axis:" -msgstr "Axe :" - -#: spyderlib/widgets/arrayeditor.py:619 -msgid "Index:" -msgstr "Indice :" - -#: spyderlib/widgets/arrayeditor.py:633 -msgid "Warning: changes are applied separately" -msgstr "Attention: les changements seront pris en compte séparément" - -#: spyderlib/widgets/arrayeditor.py:634 -msgid "" -"For performance reasons, changes applied to masked array won't be reflected " -"in array's data (and vice-versa)." -msgstr "" -"Pour des questions de performance, les changements effectués sur les données " -"masquées ne seront pas reflétées sur les données du tableau (et " -"réciproquement)." - -#: spyderlib/widgets/browser.py:30 -#: spyderlib/widgets/sourcecode/codeeditor.py:2311 -msgid "Zoom out" -msgstr "Réduire" - -#: spyderlib/widgets/browser.py:33 -#: spyderlib/widgets/sourcecode/codeeditor.py:2308 -msgid "Zoom in" -msgstr "Agrandir" - -#: spyderlib/widgets/browser.py:131 -msgid "Home" -msgstr "Accueil" - -#: spyderlib/widgets/browser.py:171 -msgid "Find text" -msgstr "Rechercher" - -#: spyderlib/widgets/browser.py:190 -msgid "Address:" -msgstr "Adresse :" - -#: spyderlib/widgets/browser.py:225 -msgid "Unable to load page" -msgstr "Impossible de charger la page" - -#: spyderlib/widgets/comboboxes.py:117 -msgid "Press enter to validate this entry" -msgstr "Appuyer sur Entrée pour valider cette saisie" - -#: spyderlib/widgets/comboboxes.py:118 -msgid "This entry is incorrect" -msgstr "Cette saisie n'est pas correcte" - -#: spyderlib/widgets/comboboxes.py:171 -msgid "Press enter to validate this path" -msgstr "Appuyez sur Entrée pour valider ce chemin d'accès" - -#: spyderlib/widgets/comboboxes.py:172 -msgid "" -"This path is incorrect.\n" -"Enter a correct directory path,\n" -"then press enter to validate" -msgstr "" -"Ce chemin d'accès n'est pas valide :\n" -"veuillez entrer un chemin d'accès correct,\n" -"puis appuyer sur Entrée pour le valider" - -#: spyderlib/widgets/dataframeeditor.py:423 -msgid "To bool" -msgstr "Booléen" - -#: spyderlib/widgets/dataframeeditor.py:423 -msgid "To complex" -msgstr "Complexe" - -#: spyderlib/widgets/dataframeeditor.py:424 -msgid "To float" -msgstr "Flottant" - -#: spyderlib/widgets/dataframeeditor.py:424 -msgid "To int" -msgstr "Entier" - -#: spyderlib/widgets/dataframeeditor.py:425 -msgid "To str" -msgstr "Caractères" - -#: spyderlib/widgets/dataframeeditor.py:489 -msgid "%s editor" -msgstr "Éditeur de %s" - -#: spyderlib/widgets/dataframeeditor.py:522 -msgid "Column min/max" -msgstr "Colonne min/max" - -#: spyderlib/widgets/dependencies.py:60 -msgid " Required " -msgstr " Requis " - -#: spyderlib/widgets/dependencies.py:60 -msgid "Module" -msgstr "Module" - -#: spyderlib/widgets/dependencies.py:61 -msgid " Installed " -msgstr " Installé " - -#: spyderlib/widgets/dependencies.py:61 -msgid "Provided features" -msgstr "Fonctionnalités associées" - -#: spyderlib/widgets/dependencies.py:127 -msgid "Optional Dependencies" -msgstr "Dépendances optionnelles" - -#: spyderlib/widgets/dependencies.py:134 -msgid "" -"Spyder depends on several Python modules to provide additional functionality " -"for its plugins. The table below shows the required and installed versions " -"(if any) of all of them.

    Although Spyder can work without any of " -"these modules, it's strongly recommended that at least you try to install " -"%s and %s to have a much better experience." -msgstr "" -"Spyder dépend de nombreux modules Python pour disposer de l'intégralité des " -"fonctionnalités potentiellement fournies par ses plugins. Le tableau suivant " -"montre les versions requises et installées (le cas échéant) de toutes ces " -"dépendances.

    Même si Spyder est parfaitement fonctionnel sans tout " -"ces modules, il est néanmoins fortement recommandé d'installer au minimum " -"%s et %s afin de bénéficier des fonctionnalités les plus avancées." - -#: spyderlib/widgets/dependencies.py:149 -msgid "Copy to clipboard" -msgstr "Copier dans le presse-papier" - -#: spyderlib/widgets/dicteditor.py:156 -msgid "Index" -msgstr "Indice" - -#: spyderlib/widgets/dicteditor.py:161 -msgid "Tuple" -msgstr "Tuple" - -#: spyderlib/widgets/dicteditor.py:164 -msgid "List" -msgstr "Liste" - -#: spyderlib/widgets/dicteditor.py:167 -msgid "Dictionary" -msgstr "Dictionnaire" - -#: spyderlib/widgets/dicteditor.py:175 -msgid "Attribute" -msgstr "Attribut" - -#: spyderlib/widgets/dicteditor.py:177 -msgid "elements" -msgstr "éléments" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Size" -msgstr "Taille" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Type" -msgstr "Type" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Value" -msgstr "Valeur" - -#: spyderlib/widgets/dicteditor.py:450 -msgid "" -"Opening this variable can be slow\n" -"\n" -"Do you want to continue anyway?" -msgstr "" -"Afficher cette variable peut prendre du temps.\n" -"\n" -"Souhaitez-vous néanmoins continuer ?" - -#: spyderlib/widgets/dicteditor.py:458 spyderlib/widgets/dicteditor.py:607 -msgid "Edit item" -msgstr "Modifier" - -#: spyderlib/widgets/dicteditor.py:459 -msgid "Unable to retrieve data.

    Error message:
    %s" -msgstr "" -"Impossible d'accéder aux données

    Message d'erreur :
    %s" - -#: spyderlib/widgets/dicteditor.py:608 -msgid "Unable to assign data to item.

    Error message:
    %s" -msgstr "" -"Impossible d'assigner la valeur de l'objet.

    Message d'erreur :" -"
    %s" - -#: spyderlib/widgets/dicteditor.py:669 -msgid "Resize rows to contents" -msgstr "Ajuster la hauteur des lignes" - -#: spyderlib/widgets/dicteditor.py:677 spyderlib/widgets/explorer.py:236 -msgid "Edit" -msgstr "Modifier" - -#: spyderlib/widgets/dicteditor.py:680 spyderlib/widgets/dicteditor.py:1012 -#: spyderlib/widgets/dicteditor.py:1028 -msgid "Plot" -msgstr "Tracer" - -#: spyderlib/widgets/dicteditor.py:684 -msgid "Histogram" -msgstr "Histogramme" - -#: spyderlib/widgets/dicteditor.py:688 -msgid "Show image" -msgstr "Afficher l'image" - -#: spyderlib/widgets/dicteditor.py:692 spyderlib/widgets/dicteditor.py:1035 -msgid "Save array" -msgstr "Enregistrer le tableau" - -#: spyderlib/widgets/dicteditor.py:696 spyderlib/widgets/dicteditor.py:976 -#: spyderlib/widgets/dicteditor.py:984 -msgid "Insert" -msgstr "Insérer" - -#: spyderlib/widgets/dicteditor.py:699 spyderlib/widgets/dicteditor.py:929 -msgid "Remove" -msgstr "Supprimer" - -#: spyderlib/widgets/dicteditor.py:710 spyderlib/widgets/dicteditor.py:946 -#: spyderlib/widgets/explorer.py:524 spyderlib/widgets/explorer.py:532 -#: spyderlib/widgets/explorer.py:544 -msgid "Rename" -msgstr "Renommer" - -#: spyderlib/widgets/dicteditor.py:713 -msgid "Duplicate" -msgstr "Dupliquer" - -#: spyderlib/widgets/dicteditor.py:927 -msgid "Do you want to remove selected item?" -msgstr "Souhaitez-vous supprimer l'élément sélectionné ?" - -#: spyderlib/widgets/dicteditor.py:928 -msgid "Do you want to remove all selected items?" -msgstr "Souhaitez-vous supprimer les éléments sélectionnés ?" - -#: spyderlib/widgets/dicteditor.py:946 spyderlib/widgets/dicteditor.py:976 -msgid "Key:" -msgstr "Clé :" - -#: spyderlib/widgets/dicteditor.py:984 -msgid "Value:" -msgstr "Valeur :" - -#: spyderlib/widgets/dicteditor.py:1000 -msgid "Import error" -msgstr "Erreur d'import" - -#: spyderlib/widgets/dicteditor.py:1001 -msgid "Please install matplotlib or guiqwt." -msgstr "Merci d'installer matplotlib ou guiqwt." - -#: spyderlib/widgets/dicteditor.py:1013 -msgid "Unable to plot data.

    Error message:
    %s" -msgstr "" -"Impossible d'afficher les données

    Message d'erreur :
    %s" - -#: spyderlib/widgets/dicteditor.py:1029 -msgid "Unable to show image.

    Error message:
    %s" -msgstr "Impossible d'afficher l'image

    Message d'erreur :
    %s" - -#: spyderlib/widgets/dicteditor.py:1051 -msgid "Unable to save array

    Error message:
    %s" -msgstr "" -"Impossible d'enregistrer le tableau

    Message d'erreur :
    %s" - -#: spyderlib/widgets/dicteditor.py:1068 -msgid "Clipboard contents" -msgstr "Contenu du presse-papiers" - -#: spyderlib/widgets/dicteditor.py:1082 -msgid "Import from clipboard" -msgstr "Importer depuis le presse-papiers" - -#: spyderlib/widgets/dicteditor.py:1084 -msgid "Empty clipboard" -msgstr "Presse-papiers vide" - -#: spyderlib/widgets/dicteditor.py:1085 -msgid "Nothing to be imported from clipboard." -msgstr "Aucune donnée ne peut être importée depuis le presse-papiers." - -#: spyderlib/widgets/dicteditorutils.py:59 -msgid "View and edit DataFrames and Series in the Variable Explorer" -msgstr "" -"Voir et éditer les Dataframes et les Series dans l'explorateur de variables" - -#: spyderlib/widgets/editor.py:67 spyderlib/widgets/editor.py:417 -msgid "File list management" -msgstr "Gestionnaire de fichiers" - -#: spyderlib/widgets/editor.py:71 -msgid "Filter:" -msgstr "Filtre :" - -#: spyderlib/widgets/editor.py:76 -msgid "(press Enter to edit file)" -msgstr "(appuyer sur Entrée pour modifier le fichier)" - -#: spyderlib/widgets/editor.py:91 -msgid "&Edit file" -msgstr "Modifi&er le fichier" - -#: spyderlib/widgets/editor.py:100 -msgid "&Close file" -msgstr "&Fermer le fichier" - -#: spyderlib/widgets/editor.py:108 -msgid "Hint: press Alt to show accelerators" -msgstr "Astuce : la touche Alt affiche les accélérateurs" - -#: spyderlib/widgets/editor.py:420 -msgid "Copy path to clipboard" -msgstr "Copier le chemin d'accès dans le presse-papier" - -#: spyderlib/widgets/editor.py:990 -msgid "Temporary file" -msgstr "Fichier temporaire" - -#: spyderlib/widgets/editor.py:1087 -msgid "New window" -msgstr "Nouvelle fenêtre" - -#: spyderlib/widgets/editor.py:1088 -msgid "Create a new editor window" -msgstr "Créer une nouvelle fenêtre d'édition" - -#: spyderlib/widgets/editor.py:1091 -msgid "Split vertically" -msgstr "Séparation verticale" - -#: spyderlib/widgets/editor.py:1093 -msgid "Split vertically this editor window" -msgstr "Séparer en deux verticalement cette fenêtre d'édition" - -#: spyderlib/widgets/editor.py:1095 -msgid "Split horizontally" -msgstr "Séparation horizontale" - -#: spyderlib/widgets/editor.py:1097 -msgid "Split horizontally this editor window" -msgstr "Séparer en deux horizontalement cette fenêtre d'édition" - -#: spyderlib/widgets/editor.py:1099 -msgid "Close this panel" -msgstr "Fermer ce panneau" - -#: spyderlib/widgets/editor.py:1237 -msgid "%s has been modified.
    Do you want to save changes?" -msgstr "" -"%s a été modifié.
    Souhaitez-vous enregistrer ces changements ?" - -#: spyderlib/widgets/editor.py:1300 -msgid "Save" -msgstr "Enregistrer" - -#: spyderlib/widgets/editor.py:1301 -msgid "Unable to save script '%s'

    Error message:
    %s" -msgstr "" -"Impossible d'enregistrer le script '%s'

    Message d'erreur :
    " -"%s" - -#: spyderlib/widgets/editor.py:1323 -msgid "Save Python script" -msgstr "Enregistrer le script Python" - -#: spyderlib/widgets/editor.py:1539 -msgid "" -"%s is unavailable (this file may have been removed, moved or renamed " -"outside Spyder).
    Do you want to close it?" -msgstr "" -"%s n'est pas accessible (ce fichier a peut-être été supprimé, déplacé " -"ou renommé en dehors de Spyder).
    Souhaitez-vous le fermer ?" - -#: spyderlib/widgets/editor.py:1559 -msgid "" -"%s has been modified outside Spyder.
    Do you want to reload it and " -"lose all your changes?" -msgstr "" -"%s a été modifié en dehors de Spyder.
    Souhaitez-vous le recharger " -"et perdre ainsi vos modifications ?" - -#: spyderlib/widgets/editor.py:1655 -msgid "" -"All changes to %s will be lost.
    Do you want to revert file from " -"disk?" -msgstr "" -"Toutes les modifications effectuées sur %s seront perdues." -"
    Souhaitez-vous revenir à la version du fichier enregistrée sur le " -"disque ?" - -#: spyderlib/widgets/editor.py:1811 -msgid "Loading %s..." -msgstr "Chargement de \"%s\" en cours..." - -#: spyderlib/widgets/editor.py:1821 -msgid "" -"%s contains mixed end-of-line characters.
    Spyder will fix this " -"automatically." -msgstr "" -"%s contient des caractères de fin de ligne mélangés.
    Spyder va " -"corriger ceci automatiquement." - -#: spyderlib/widgets/editor.py:2192 -msgid "Close window" -msgstr "Fermer la fenêtre" - -#: spyderlib/widgets/editor.py:2194 -msgid "Close this window" -msgstr "Fermer cette fenêtre d'édition" - -#: spyderlib/widgets/editortools.py:93 spyderlib/widgets/editortools.py:129 -msgid "Line %s" -msgstr "Ligne %s" - -#: spyderlib/widgets/editortools.py:98 -msgid "Class defined at line %s" -msgstr "Classe déclarée ligne %s" - -#: spyderlib/widgets/editortools.py:106 -msgid "Method defined at line %s" -msgstr "Méthode déclarée ligne %s" - -#: spyderlib/widgets/editortools.py:116 -msgid "Function defined at line %s" -msgstr "Fonction déclarée ligne %s" - -#: spyderlib/widgets/editortools.py:148 -msgid "Cell starts at line %s" -msgstr "Cellule commençant ligne %s" - -#: spyderlib/widgets/editortools.py:201 spyderlib/widgets/editortools.py:536 -msgid "Go to cursor position" -msgstr "Aller à la position du curseur" - -#: spyderlib/widgets/editortools.py:204 -msgid "Show absolute path" -msgstr "Afficher les chemins complets" - -#: spyderlib/widgets/editortools.py:207 spyderlib/widgets/explorer.py:177 -msgid "Show all files" -msgstr "Afficher tous les fichiers" - -#: spyderlib/widgets/editortools.py:210 -msgid "Show special comments" -msgstr "Afficher les commentaires spéciaux" - -#: spyderlib/widgets/explorer.py:173 -msgid "Edit filename filters..." -msgstr "Modifier les filtres..." - -#: spyderlib/widgets/explorer.py:186 -msgid "Edit filename filters" -msgstr "Modifier les filtres" - -#: spyderlib/widgets/explorer.py:187 -msgid "Name filters:" -msgstr "Filtres sur les noms de fichiers :" - -#: spyderlib/widgets/explorer.py:205 -msgid "File..." -msgstr "Fichier..." - -#: spyderlib/widgets/explorer.py:208 -msgid "Module..." -msgstr "Module..." - -#: spyderlib/widgets/explorer.py:211 -msgid "Folder..." -msgstr "Dossier..." - -#: spyderlib/widgets/explorer.py:215 -msgid "Package..." -msgstr "Paquet..." - -#: spyderlib/widgets/explorer.py:238 -msgid "Move..." -msgstr "Déplacer..." - -#: spyderlib/widgets/explorer.py:241 -msgid "Delete..." -msgstr "Supprimer..." - -#: spyderlib/widgets/explorer.py:244 -msgid "Rename..." -msgstr "Renommer..." - -#: spyderlib/widgets/explorer.py:247 -msgid "Open" -msgstr "Ouvrir" - -#: spyderlib/widgets/explorer.py:248 -#: spyderlib/widgets/sourcecode/codeeditor.py:2299 -msgid "Convert to Python script" -msgstr "Convertir en fichier Python" - -#: spyderlib/widgets/explorer.py:271 -msgid "Commit" -msgstr "Commiter" - -#: spyderlib/widgets/explorer.py:275 -msgid "Browse repository" -msgstr "Explorer le dépôt" - -#: spyderlib/widgets/explorer.py:287 -msgid "Open command prompt here" -msgstr "Ouvrir un invite de commandes ici" - -#: spyderlib/widgets/explorer.py:289 -msgid "Open terminal here" -msgstr "Ouvrir un terminal ici" - -#: spyderlib/widgets/explorer.py:294 -msgid "Open Python console here" -msgstr "Ouvrir une console &Python ici" - -#: spyderlib/widgets/explorer.py:308 -msgid "New" -msgstr "Nouveau" - -#: spyderlib/widgets/explorer.py:316 -msgid "Import" -msgstr "Import" - -#: spyderlib/widgets/explorer.py:462 -msgid "Do you really want to delete %s?" -msgstr "Souhaitez-vous réellement supprimer %s ?" - -#: spyderlib/widgets/explorer.py:482 -msgid "delete" -msgstr "supprimer" - -#: spyderlib/widgets/explorer.py:483 spyderlib/widgets/projectexplorer.py:815 -#: spyderlib/widgets/projectexplorer.py:822 -#: spyderlib/widgets/projectexplorer.py:1089 -#: spyderlib/widgets/projectexplorer.py:1173 -msgid "Project Explorer" -msgstr "Explorateur de projets" - -#: spyderlib/widgets/explorer.py:484 spyderlib/widgets/projectexplorer.py:762 -#: spyderlib/widgets/projectexplorer.py:1174 -msgid "Unable to %s %s

    Error message:
    %s" -msgstr "Impossible de %s %s

    Message d'erreur :
    %s" - -#: spyderlib/widgets/explorer.py:506 -#: spyderlib/widgets/sourcecode/codeeditor.py:1906 -msgid "Conversion error" -msgstr "Erreur de conversion" - -#: spyderlib/widgets/explorer.py:507 -#: spyderlib/widgets/sourcecode/codeeditor.py:1907 -msgid "" -"It was not possible to convert this notebook. The error is:\n" -"\n" -msgstr "" -"Impossible de convertir ce notebook. Message d'erreur:\n" -"\n" - -#: spyderlib/widgets/explorer.py:525 -msgid "New name:" -msgstr "Nouveau nom :" - -#: spyderlib/widgets/explorer.py:533 -msgid "" -"Do you really want to rename %s and overwrite the existing file " -"%s?" -msgstr "" -"Souhaitez-vous réellement renommer %s et remplacer le ficher existant " -"%s ?" - -#: spyderlib/widgets/explorer.py:545 -msgid "Unable to rename file %s

    Error message:
    %s" -msgstr "" -"Impossible de renommer l'élément %s

    Message d'erreur :" -"
    %s" - -#: spyderlib/widgets/explorer.py:579 -msgid "Unable to move %s

    Error message:
    %s" -msgstr "" -"Impossible de déplacer %s

    Message d'erreur :
    %s" - -#: spyderlib/widgets/explorer.py:597 -msgid "Unable to create folder %s

    Error message:
    %s" -msgstr "" -"Impossible de créer le répertoire %s

    Message d'erreur :" -"
    %s" - -#: spyderlib/widgets/explorer.py:610 spyderlib/widgets/explorer.py:644 -msgid "Unable to create file %s

    Error message:
    %s" -msgstr "" -"Impossible de créer le fichier %s

    Message d'erreur :
    " -"%s" - -#: spyderlib/widgets/explorer.py:618 -msgid "New folder" -msgstr "Nouveau répertoire" - -#: spyderlib/widgets/explorer.py:619 -msgid "Folder name:" -msgstr "Nom du dossier :" - -#: spyderlib/widgets/explorer.py:624 -msgid "New package" -msgstr "Nouveau paquet" - -#: spyderlib/widgets/explorer.py:625 -msgid "Package name:" -msgstr "Nom du paquet :" - -#: spyderlib/widgets/explorer.py:665 -msgid "New module" -msgstr "Nouveau module" - -#: spyderlib/widgets/explorer.py:678 -msgid "" -"For %s support, please install one of the
    following tools:

    %s" -msgstr "" -"Pour ajouter la prise en charge de %s, merci d'installer
    l'un des " -"outils suivants :

    %s" - -#: spyderlib/widgets/explorer.py:682 -msgid "Unable to find external program.

    %s" -msgstr "Impossible de trouver un programme externe.

    %s" - -#: spyderlib/widgets/explorer.py:882 -msgid "Show current directory only" -msgstr "Afficher uniquement le répertoire courant" - -#: spyderlib/widgets/explorer.py:996 spyderlib/widgets/importwizard.py:518 -msgid "Previous" -msgstr "Précédent" - -#: spyderlib/widgets/explorer.py:1012 -msgid "Parent" -msgstr "Parent" - -#: spyderlib/widgets/externalshell/baseshell.py:140 -msgid "Run again this program" -msgstr "Exécuter de nouveau ce programme" - -#: spyderlib/widgets/externalshell/baseshell.py:143 -msgid "Kill" -msgstr "Terminer" - -#: spyderlib/widgets/externalshell/baseshell.py:145 -msgid "Kills the current process, causing it to exit immediately" -msgstr "" -"Tue le processus, entraînant une sortie brutale et immédiate du programme" - -#: spyderlib/widgets/externalshell/baseshell.py:213 -msgid "Running..." -msgstr "En cours d'exécution..." - -#: spyderlib/widgets/externalshell/baseshell.py:220 -msgid "Terminated." -msgstr "Terminé." - -#: spyderlib/widgets/externalshell/baseshell.py:242 -#: spyderlib/widgets/ipython.py:342 spyderlib/widgets/ipython.py:359 -#: spyderlib/widgets/mixins.py:608 -msgid "Arguments" -msgstr "Arguments" - -#: spyderlib/widgets/externalshell/baseshell.py:243 -msgid "Command line arguments:" -msgstr "Arguments en ligne de commande :" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:173 -msgid "Refresh" -msgstr "Rafraîchir" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:177 -msgid "Refresh periodically" -msgstr "Rafraîchir périodiquement" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:181 -#: spyderlib/widgets/externalshell/namespacebrowser.py:446 -msgid "Import data" -msgstr "Importer des données" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:184 -#: spyderlib/widgets/externalshell/namespacebrowser.py:536 -#: spyderlib/widgets/externalshell/namespacebrowser.py:557 -msgid "Save data" -msgstr "Enregistrer les données" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:189 -msgid "Save data as..." -msgstr "Enregistrer les données sous..." - -#: spyderlib/widgets/externalshell/namespacebrowser.py:197 -msgid "Exclude references which name starts with an underscore" -msgstr "Exclure les références dont le nom commence par un tiret bas" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:205 -msgid "Exclude references which name is uppercase" -msgstr "Exclure les références dont le nom s'écrit en lettres capitales" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:212 -msgid "Exclude references which name starts with an uppercase character" -msgstr "Exclure les références dont le nom commence par une lettre majuscule" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:220 -msgid "" -"Exclude references to unsupported data types (i.e. which won't be handled/" -"saved correctly)" -msgstr "" -"Exclure les références dont le type n'est pas supporté par l'espace de " -"travail (en particulier, l'enregistrement ne fonctionnera pas)" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:342 -msgid "Object %s is not picklable" -msgstr "" -"L'objet %s n'est pas pris en charge par le protocole de sérialisation " -"de Pickle" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:468 -msgid "" -"Unsupported file extension '%s'

    Would you like to import it " -"anyway (by selecting a known file format)?" -msgstr "" -"Extension de fichier non pris en charge '%s'

    Souhaitez-vous " -"néanmoins ouvrir ce fichier (en choisissant un format de fichier connu) ?" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:476 -msgid "Open file as:" -msgstr "Ouvrir le fichier en tant que :" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:524 -msgid "Unable to load '%s'

    Error message:
    %s" -msgstr "Impossible d'ouvrir '%s'

    Message d'erreur :
    %s" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:558 -msgid "Unable to save current workspace

    Error message:
    %s" -msgstr "" -"Impossible d'enregistrer l'espace de travail

    Message d'erreur :" -"
    %s" - -#: spyderlib/widgets/externalshell/pythonshell.py:269 -msgid "Variables" -msgstr "Variables" - -#: spyderlib/widgets/externalshell/pythonshell.py:270 -msgid "Show/hide global variables explorer" -msgstr "Afficher/masquer l'explorateur de variables globales" - -#: spyderlib/widgets/externalshell/pythonshell.py:274 -msgid "Terminate" -msgstr "Quitter" - -#: spyderlib/widgets/externalshell/pythonshell.py:275 -msgid "" -"Attempts to stop the process. The process\n" -"may not exit as a result of clicking this\n" -"button (it is given the chance to prompt\n" -"the user for any unsaved files, etc)." -msgstr "" -"Tentative de fermeture du processus. Il est possible que\n" -"le processus ne s'arrête pas suite à cette tentative,\n" -"et propose à l'utilisateur de sauvegarder\n" -"les fichiers non-enregistrés, etc..." - -#: spyderlib/widgets/externalshell/pythonshell.py:288 -msgid "Interact" -msgstr "Interagir" - -#: spyderlib/widgets/externalshell/pythonshell.py:290 -msgid "Debug" -msgstr "Déboguer" - -#: spyderlib/widgets/externalshell/pythonshell.py:292 -#: spyderlib/widgets/externalshell/pythonshell.py:355 -msgid "Arguments..." -msgstr "Arguments..." - -#: spyderlib/widgets/externalshell/pythonshell.py:299 -msgid "Set current working directory" -msgstr "Changer le répertoire de travail" - -#: spyderlib/widgets/externalshell/pythonshell.py:301 -msgid "Environment variables" -msgstr "Variables d'environnement" - -#: spyderlib/widgets/externalshell/pythonshell.py:305 -msgid "Show sys.path contents" -msgstr "Afficher le contenu de sys.path" - -#: spyderlib/widgets/externalshell/pythonshell.py:351 -msgid "Arguments: %s" -msgstr "Arguments : %s" - -#: spyderlib/widgets/externalshell/pythonshell.py:353 -msgid "No argument" -msgstr "Aucun argument" - -#: spyderlib/widgets/externalshell/pythonshell.py:534 -msgid "" -"The kernel failed to start!! That's all we know... Please close this console " -"and open a new one." -msgstr "" -"Le démarrage du noyau a échoué ! C'est malheureusement tout ce que nous " -"savons... Merci de fermer cette console et d'en ouvrir une nouvelle." - -#: spyderlib/widgets/externalshell/pythonshell.py:538 -msgid "A Python console failed to start!" -msgstr "Le démarrage d'une console Python a échoué !" - -#: spyderlib/widgets/externalshell/systemshell.py:94 -msgid "Process failed to start" -msgstr "Le processus n'a pas pu démarrer" - -#: spyderlib/widgets/findinfiles.py:155 -msgid "Unexpected error: see internal console" -msgstr "Erreur inattendue : voir console interne" - -#: spyderlib/widgets/findinfiles.py:207 spyderlib/widgets/findinfiles.py:231 -#: spyderlib/widgets/findinfiles.py:278 -msgid "invalid regular expression" -msgstr "expression régulière incorrecte" - -#: spyderlib/widgets/findinfiles.py:276 -msgid "permission denied errors were encountered" -msgstr "des erreurs d'autorisation d'accès ont été rencontrées" - -#: spyderlib/widgets/findinfiles.py:310 -msgid "Search pattern" -msgstr "Expression recherchée" - -#: spyderlib/widgets/findinfiles.py:313 spyderlib/widgets/findinfiles.py:347 -#: spyderlib/widgets/findinfiles.py:359 spyderlib/widgets/findreplace.py:82 -msgid "Regular expression" -msgstr "Expression régulière" - -#: spyderlib/widgets/findinfiles.py:322 -msgid "Search" -msgstr "Rechercher" - -#: spyderlib/widgets/findinfiles.py:325 -msgid "Start search" -msgstr "Démarrer la recherche" - -#: spyderlib/widgets/findinfiles.py:328 spyderlib/widgets/ipython.py:543 -msgid "Stop" -msgstr "Arrêter" - -#: spyderlib/widgets/findinfiles.py:331 -msgid "Stop search" -msgstr "Arrêter la recherche" - -#: spyderlib/widgets/findinfiles.py:341 -msgid "Included filenames pattern" -msgstr "Expression des noms de fichier à inclure" - -#: spyderlib/widgets/findinfiles.py:350 -msgid "Include:" -msgstr "Inclure :" - -#: spyderlib/widgets/findinfiles.py:353 -msgid "Excluded filenames pattern" -msgstr "Expression des noms de fichier à exclure" - -#: spyderlib/widgets/findinfiles.py:362 -msgid "Exclude:" -msgstr "Exclure :" - -#: spyderlib/widgets/findinfiles.py:372 -msgid "PYTHONPATH" -msgstr "PYTHONPATH" - -#: spyderlib/widgets/findinfiles.py:374 -msgid "" -"Search in all directories listed in sys.path which are outside the Python " -"installation directory" -msgstr "" -"Rechercher dans tous les répertoires listés dans sys.path qui sont situés en " -"dehors du répertoire d'installation de Python" - -#: spyderlib/widgets/findinfiles.py:377 -msgid "Hg repository" -msgstr "Dépôt Mercurial" - -#: spyderlib/widgets/findinfiles.py:380 -msgid "Search in current directory hg repository" -msgstr "Rechercher dans le dépôt Mercurial du répertoire courant" - -#: spyderlib/widgets/findinfiles.py:381 -msgid "Here:" -msgstr "Ici :" - -#: spyderlib/widgets/findinfiles.py:385 -msgid "Search recursively in this directory" -msgstr "Rechercher de manière récursive dans ce répertoire" - -#: spyderlib/widgets/findinfiles.py:393 -msgid "Browse a search directory" -msgstr "Sélectionner un répertoire de recherche" - -#: spyderlib/widgets/findinfiles.py:426 -msgid "Hide advanced options" -msgstr "Masquer les options avancées" - -#: spyderlib/widgets/findinfiles.py:429 -msgid "Show advanced options" -msgstr "Afficher les options avancées" - -#: spyderlib/widgets/findinfiles.py:571 -msgid "Search canceled" -msgstr "Recherche annulée" - -#: spyderlib/widgets/findinfiles.py:575 -msgid "String not found" -msgstr "Chaîne de caractères non trouvée" - -#: spyderlib/widgets/findinfiles.py:577 -msgid "matches in" -msgstr "correspondances trouvées dans" - -#: spyderlib/widgets/findinfiles.py:578 -msgid "file" -msgstr "fichier" - -#: spyderlib/widgets/findinfiles.py:586 -msgid "interrupted" -msgstr "interrompu" - -#: spyderlib/widgets/findreplace.py:62 -msgid "Search string" -msgstr "Chaîne de caractères à rechercher" - -#: spyderlib/widgets/findreplace.py:89 -msgid "Case Sensitive" -msgstr "Respecter la casse" - -#: spyderlib/widgets/findreplace.py:96 -msgid "Whole words" -msgstr "Mots entiers" - -#: spyderlib/widgets/findreplace.py:103 -msgid "Highlight matches" -msgstr "Surligner les résultats" - -#: spyderlib/widgets/findreplace.py:118 -msgid "Replace with:" -msgstr "Remplacer par :" - -#: spyderlib/widgets/findreplace.py:120 -msgid "Replace string" -msgstr "Chaîne de caractères de remplacement" - -#: spyderlib/widgets/findreplace.py:123 -msgid "Replace/find" -msgstr "Remplacer/rechercher" - -#: spyderlib/widgets/findreplace.py:132 -msgid "Replace all" -msgstr "Remplacer tout" - -#: spyderlib/widgets/importwizard.py:111 spyderlib/widgets/importwizard.py:425 -msgid "Import as" -msgstr "Importer en tant que" - -#: spyderlib/widgets/importwizard.py:113 -msgid "data" -msgstr "données" - -#: spyderlib/widgets/importwizard.py:117 -msgid "code" -msgstr "code" - -#: spyderlib/widgets/importwizard.py:120 spyderlib/widgets/importwizard.py:497 -msgid "text" -msgstr "texte" - -#: spyderlib/widgets/importwizard.py:133 -msgid "Column separator:" -msgstr "Séparateur de colonne :" - -#: spyderlib/widgets/importwizard.py:137 -msgid "Tab" -msgstr "Tab" - -#: spyderlib/widgets/importwizard.py:140 spyderlib/widgets/importwizard.py:159 -msgid "other" -msgstr "autre" - -#: spyderlib/widgets/importwizard.py:152 -msgid "Row separator:" -msgstr "Séparateur de ligne :" - -#: spyderlib/widgets/importwizard.py:156 -msgid "EOL" -msgstr "EOL" - -#: spyderlib/widgets/importwizard.py:172 -msgid "Additional options" -msgstr "Options supplémentaires" - -#: spyderlib/widgets/importwizard.py:176 -msgid "Skip rows:" -msgstr "Sauter des lignes :" - -#: spyderlib/widgets/importwizard.py:187 -msgid "Comments:" -msgstr "Commentaires :" - -#: spyderlib/widgets/importwizard.py:193 -msgid "Transpose" -msgstr "Transposer" - -#: spyderlib/widgets/importwizard.py:428 -msgid "array" -msgstr "tableau" - -#: spyderlib/widgets/importwizard.py:433 -msgid "list" -msgstr "liste" - -#: spyderlib/widgets/importwizard.py:438 -msgid "DataFrame" -msgstr "DataFrame" - -#: spyderlib/widgets/importwizard.py:480 spyderlib/widgets/importwizard.py:567 -msgid "Import wizard" -msgstr "Assistant d'importation" - -#: spyderlib/widgets/importwizard.py:485 -msgid "Raw text" -msgstr "Text brut" - -#: spyderlib/widgets/importwizard.py:488 -msgid "variable_name" -msgstr "nom_de_variable" - -#: spyderlib/widgets/importwizard.py:499 -msgid "table" -msgstr "tableau" - -#: spyderlib/widgets/importwizard.py:500 -msgid "Preview" -msgstr "Aperçu" - -#: spyderlib/widgets/importwizard.py:504 -msgid "Variable Name" -msgstr "Nom de variable" - -#: spyderlib/widgets/importwizard.py:512 -msgid "Cancel" -msgstr "Annuler" - -#: spyderlib/widgets/importwizard.py:527 -msgid "Done" -msgstr "Terminer" - -#: spyderlib/widgets/importwizard.py:568 -msgid "" -"Unable to proceed to next step

    Please check your entries." -"

    Error message:
    %s" -msgstr "" -"Impossible de passer à l'étape suivante

    Merci de vérifier " -"votre saisie.

    Message d'erreur :
    %s" - -#: spyderlib/widgets/internalshell.py:252 -msgid "Help..." -msgstr "Aide..." - -#: spyderlib/widgets/internalshell.py:259 -msgid "Help" -msgstr "Aide" - -#: spyderlib/widgets/internalshell.py:268 -msgid "Shell special commands:" -msgstr "Commandes spéciales de la console :" - -#: spyderlib/widgets/internalshell.py:269 -msgid "Internal editor:" -msgstr "Éditeur interne :" - -#: spyderlib/widgets/internalshell.py:270 -msgid "External editor:" -msgstr "Éditeur externe :" - -#: spyderlib/widgets/internalshell.py:271 -msgid "Run script:" -msgstr "Exécuter un script :" - -#: spyderlib/widgets/internalshell.py:272 -msgid "Remove references:" -msgstr "Supprimer des références :" - -#: spyderlib/widgets/internalshell.py:273 -msgid "System commands:" -msgstr "Commandes systèmes :" - -#: spyderlib/widgets/internalshell.py:274 -msgid "Python help:" -msgstr "Aide Python :" - -#: spyderlib/widgets/internalshell.py:275 -msgid "GUI-based editor:" -msgstr "Éditeur graphique :" - -#: spyderlib/widgets/ipython.py:495 -msgid "An error ocurred while starting the kernel" -msgstr "Une erreur est survenue lors du démarrage du noyau." - -#: spyderlib/widgets/ipython.py:523 -msgid "Restart kernel" -msgstr "Redémarrer le noyau" - -#: spyderlib/widgets/ipython.py:545 -msgid "Stop the current command" -msgstr "Interrompre la commande en cours" - -#: spyderlib/widgets/ipython.py:569 -msgid "Inspect current object" -msgstr "Inspecter l'onglet courant" - -#: spyderlib/widgets/ipython.py:574 -msgid "Clear line or block" -msgstr "Effacer la ligne ou le bloc" - -#: spyderlib/widgets/ipython.py:578 -msgid "Clear console" -msgstr "Effacer la console" - -#: spyderlib/widgets/ipython.py:623 -msgid "" -"It seems the kernel died unexpectedly. Use 'Restart kernel' to continue " -"using this console." -msgstr "" -"Le noyau a été arrêté de façon inattendue. Redémarrez le noyau pour " -"continuer d'utiliser cette console." - -#: spyderlib/widgets/ipython.py:639 -msgid "Changing backend to Qt for Mayavi" -msgstr "Utilisation du backend Qt pour Mayavi" - -#: spyderlib/widgets/ipython.py:648 -msgid "Kernel process is either remote or unspecified. Cannot interrupt" -msgstr "" -"Le processus du noyau est soit distant, soit non spécifié : impossible " -"d'arrêter le noyau." - -#: spyderlib/widgets/ipython.py:657 -msgid "Kernel process is either remote or unspecified. Cannot restart." -msgstr "" -"Le processus du noyau est soit distant, soit non spécifié : impossible de " -"redémarrer le noyau." - -#: spyderlib/widgets/ipython.py:734 -msgid "Connecting to kernel..." -msgstr "Connexion au noyau..." - -#: spyderlib/widgets/onecolumntree.py:63 -msgid "Collapse all" -msgstr "Replier tout" - -#: spyderlib/widgets/onecolumntree.py:67 -msgid "Expand all" -msgstr "Déplier tout" - -#: spyderlib/widgets/onecolumntree.py:71 -msgid "Restore" -msgstr "Restaurer" - -#: spyderlib/widgets/onecolumntree.py:72 -msgid "Restore original tree layout" -msgstr "Restaurer l'organisation initiale de l'arbre" - -#: spyderlib/widgets/onecolumntree.py:76 -msgid "Collapse selection" -msgstr "Replier la sélection" - -#: spyderlib/widgets/onecolumntree.py:80 -msgid "Expand selection" -msgstr "Déplier la sélection" - -#: spyderlib/widgets/pathmanager.py:84 -msgid "Move to top" -msgstr "Placer en premier" - -#: spyderlib/widgets/pathmanager.py:90 -msgid "Move up" -msgstr "Monter" - -#: spyderlib/widgets/pathmanager.py:96 -msgid "Move down" -msgstr "Descendre" - -#: spyderlib/widgets/pathmanager.py:102 -msgid "Move to bottom" -msgstr "Placer en dernier" - -#: spyderlib/widgets/pathmanager.py:113 spyderlib/widgets/pathmanager.py:225 -msgid "Add path" -msgstr "Ajouter un chemin" - -#: spyderlib/widgets/pathmanager.py:118 spyderlib/widgets/pathmanager.py:209 -msgid "Remove path" -msgstr "Supprimer" - -#: spyderlib/widgets/pathmanager.py:128 -msgid "Synchronize..." -msgstr "Synchroniser..." - -#: spyderlib/widgets/pathmanager.py:130 -msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" -msgstr "" -"Synchronise la liste des chemins d'accès de Spyder avec celle de la variable " -"d'environnement PYTHONPATH" - -#: spyderlib/widgets/pathmanager.py:141 -msgid "Synchronize" -msgstr "Synchroniser" - -#: spyderlib/widgets/pathmanager.py:142 -msgid "" -"This will synchronize Spyder's path list with PYTHONPATH environment " -"variable for current user, allowing you to run your Python modules outside " -"Spyder without having to configure sys.path.
    Do you want to clear " -"contents of PYTHONPATH before adding Spyder's path list?" -msgstr "" -"Ceci synchronisera la liste des chemins d'accès de Spyder avec celle de la " -"variable d'environnement PYTHONPATH pour l'utilisateur courant, vous " -"permettant ainsi d'exécuter vos modules Python en dehors de Spyder sans " -"avoir besoin de configurer sys.path.
    Souhaitez-vous effacer le contenu " -"de PYTHONPATH avant d'y ajouter la liste des chemins d'accès de Spyder ?" - -#: spyderlib/widgets/pathmanager.py:210 -msgid "Do you really want to remove selected path?" -msgstr "Souhaitez-vous vraiment supprimer le chemin sélectionné ?" - -#: spyderlib/widgets/pathmanager.py:226 -msgid "" -"This directory is already included in Spyder path list.
    Do you want to " -"move it to the top of the list?" -msgstr "" -"Ce répertoire est déjà inclus dans la liste des chemins d'accès de Spyder." -"
    Souhaitez-vous le placer en début de liste ?" - -#: spyderlib/widgets/projectexplorer.py:333 -msgid "its own configuration file" -msgstr "son propre fichier de configuration" - -#: spyderlib/widgets/projectexplorer.py:335 -msgid " and " -msgstr " et " - -#: spyderlib/widgets/projectexplorer.py:339 -msgid "the following projects:
    %s" -msgstr "les projets suivants :
    %s" - -#: spyderlib/widgets/projectexplorer.py:541 -msgid "Project..." -msgstr "Projet..." - -#: spyderlib/widgets/projectexplorer.py:554 -msgid "Existing directory" -msgstr "Répertoire existant" - -#: spyderlib/widgets/projectexplorer.py:558 -msgid "Existing Spyder project" -msgstr "Projet Spyder existant" - -#: spyderlib/widgets/projectexplorer.py:562 -msgid "Existing Pydev project" -msgstr "Projet Pydev existant" - -#: spyderlib/widgets/projectexplorer.py:579 -msgid "Open project" -msgstr "Ouvrir le projet" - -#: spyderlib/widgets/projectexplorer.py:584 -msgid "Close project" -msgstr "Fermer le projet" - -#: spyderlib/widgets/projectexplorer.py:589 -msgid "Close unrelated projects" -msgstr "Fermer les projets non associés" - -#: spyderlib/widgets/projectexplorer.py:598 -msgid "Edit related projects" -msgstr "Modifier les projets associés" - -#: spyderlib/widgets/projectexplorer.py:606 -msgid "Add to PYTHONPATH" -msgstr "Ajouter à PYTHONPATH" - -#: spyderlib/widgets/projectexplorer.py:611 -msgid "Remove from PYTHONPATH" -msgstr "Retirer de PYTHONPATH" - -#: spyderlib/widgets/projectexplorer.py:616 -msgid "Properties" -msgstr "Propriétés" - -#: spyderlib/widgets/projectexplorer.py:651 -msgid "Show horizontal scrollbar" -msgstr "Barre de défilement horizontal" - -#: spyderlib/widgets/projectexplorer.py:684 -msgid "Workspace" -msgstr "Espace de travail" - -#: spyderlib/widgets/projectexplorer.py:685 -msgid "" -"The workspace was unable to load or save %s

    Please check if you have " -"the permission to write the associated configuration files." -msgstr "" -"L'espace de travail n'est pas parvenu à ouvrir ou enregistrer " -"%s

    Veuillez vérifier que vous disposez bien des droits d'écriture sur " -"les fichiers de configuration associés." - -#: spyderlib/widgets/projectexplorer.py:744 -msgid "Import directory" -msgstr "Importer un répertoire" - -#: spyderlib/widgets/projectexplorer.py:746 -msgid "" -"The following directory is not in workspace:
    %s

    Do you want " -"to continue (and copy the directory to workspace)?" -msgstr "" -"Le répertoire suivant n'est pas dans l'espace de travail :
    %s

    Souhaitez-vous continuer (et copier ce répertoire dans l'espace de " -"travail) ?" - -#: spyderlib/widgets/projectexplorer.py:764 -#: spyderlib/widgets/projectexplorer.py:1170 -msgid "copy" -msgstr "copier" - -#: spyderlib/widgets/projectexplorer.py:816 -msgid "The project %s is already opened!" -msgstr "Le projet %s est déjà ouvert !" - -#: spyderlib/widgets/projectexplorer.py:823 -msgid "" -"The project root path directory is inside the workspace but not as the " -"expected tree level. It is not a directory of the workspace:
    %s" -msgstr "" -"Le répertoire racine du projet est bien à l'intérieur de l'espace de travail " -"mais pas au niveau d'arborescence attendu. Ce n'est pas un répertoire de " -"l'espace de travail :
    %s" - -#: spyderlib/widgets/projectexplorer.py:834 -msgid "Project name:" -msgstr "Nom du projet :" - -#: spyderlib/widgets/projectexplorer.py:843 -msgid "A project named %s already exists" -msgstr "Un projet nommé %s existe déjà" - -#: spyderlib/widgets/projectexplorer.py:848 -msgid "" -"Invalid project name.

    Name must match the following regular " -"expression:
    %s" -msgstr "" -"Nom de projet incorrect.

    Le nom doit respecter l'expression régulière " -"suivante :
    %s" - -#: spyderlib/widgets/projectexplorer.py:855 -msgid "" -"The following directory is not empty:
    %s

    Do you want to " -"continue?" -msgstr "" -"Le répertoire suivant n'est pas vide :
    %s

    Souhaitez-vous " -"néanmoins continuer ?" - -#: spyderlib/widgets/projectexplorer.py:867 -msgid "New project" -msgstr "Nouveau projet" - -#: spyderlib/widgets/projectexplorer.py:875 -msgid "" -"The current workspace has not been configured yet.\n" -"Do you want to do this now?" -msgstr "" -"L'espace de travail n'a pas encore été défini.\n" -"Souhaitez-vous le faire maintenant ?" - -#: spyderlib/widgets/projectexplorer.py:912 -msgid "Import existing project" -msgstr "Importer un projet existant" - -#: spyderlib/widgets/projectexplorer.py:925 -msgid "Select projects to import" -msgstr "Sélectionner les projets à importer" - -#: spyderlib/widgets/projectexplorer.py:937 -msgid "The folder %s does not contain a valid %s project" -msgstr "Le dossier %s ne contient pas de projet %s valide" - -#: spyderlib/widgets/projectexplorer.py:965 -msgid "Import existing Pydev project" -msgstr "Importer un projet Pydev" - -#: spyderlib/widgets/projectexplorer.py:966 -msgid "" -"Unable to read Pydev project %s

    Error message:
    %s" -msgstr "" -"Impossible d'ouvrir le projet Pydev %s

    Message " -"d'erreur :
    %s" - -#: spyderlib/widgets/projectexplorer.py:1004 -msgid "" -"Do you really want to delete project %s?

    Note: project files " -"won't be deleted from disk." -msgstr "" -"Souhaitez-vous réellement supprimer le projet %s ?

    Notez que " -"les fichiers et répertoires du projet ne seront pas supprimés du disque." - -#: spyderlib/widgets/projectexplorer.py:1057 -msgid "Related projects" -msgstr "Projets associés" - -#: spyderlib/widgets/projectexplorer.py:1065 -msgid "Select projects which are related to %s" -msgstr "Sélectionner les projets à associer à %s" - -#: spyderlib/widgets/projectexplorer.py:1090 -msgid "" -"Statistics on source files only:
    (Python, C/C++, Fortran)

    %s files.
    %s lines of code." -msgstr "" -"Statistique sur les fichiers source uniquement:
    (Python, C/C++, " -"Fortran)

    %s fichiers.
    %s lignes de code." - -#: spyderlib/widgets/projectexplorer.py:1138 -msgid "File %s already exists.
    Do you want to overwrite it?" -msgstr "Le fichier %s existe déjà.
    Souhaitez-vous le remplacer ?" - -#: spyderlib/widgets/projectexplorer.py:1152 -msgid "Folder %s already exists." -msgstr "Le dossier %s existe déjà." - -#: spyderlib/widgets/projectexplorer.py:1172 -msgid "move" -msgstr "déplacer" - -#: spyderlib/widgets/projectexplorer.py:1182 -msgid "Select an existing workspace directory, or create a new one" -msgstr "Sélectionner un espace de travail existant, ou en créer un nouveau" - -#: spyderlib/widgets/projectexplorer.py:1183 -msgid "" -"What is the workspace?

    A Spyder workspace is a " -"directory on your filesystem that contains Spyder projects and ." -"spyderworkspace configuration file.

    A Spyder project is a " -"directory with source code (and other related files) and a configuration " -"file (named .spyderproject) with project settings (PYTHONPATH, linked " -"projects, ...).
    " -msgstr "" -"Qu'est-ce que l'espace de travail ?

    L'espace de " -"travail Spyder est un répertoire de votre système de fichiers qui " -"contient les projets Spyder et le fichier de configuration ." -"spyderworkspace.

    Un projet Spyder est un répertoire " -"contenant du code source (et d'autres fichiers) ainsi qu'un fichier de " -"configuration (nommé .spyderproject) dans lequel sont stockés les " -"réglages du projet (PYTHONPATH, projets liés, ...).
    " - -#: spyderlib/widgets/projectexplorer.py:1210 -msgid "This is the current workspace directory" -msgstr "Ceci est l'espace de travail actif" - -#: spyderlib/widgets/projectexplorer.py:1241 -msgid "" -"The following directory is not a Spyder workspace:
    %s

    Do you want " -"to create a new workspace in this directory?" -msgstr "" -"Le répertoire suivant n'est pas un espace de travail Spyder :
    %s

    Souhaitez-vous créer un nouvel espace de travail dans ce " -"répertoire ?" - -#: spyderlib/widgets/pydocgui.py:107 -msgid "Module or package:" -msgstr "Module ou paquet :" - -#: spyderlib/widgets/shell.py:126 -msgid "Save history log..." -msgstr "Enregistrer l'historique..." - -#: spyderlib/widgets/shell.py:128 -msgid "Save current history log (i.e. all inputs and outputs) in a text file" -msgstr "" -"Enregistrer l'historique complet (toutes les entrées et sorties) dans un " -"fichier texte" - -#: spyderlib/widgets/shell.py:248 -msgid "Save history log" -msgstr "Enregistrer l'historique" - -#: spyderlib/widgets/shell.py:251 -msgid "History logs" -msgstr "Fichiers d'historique" - -#: spyderlib/widgets/shell.py:262 -msgid "Unable to save file '%s'

    Error message:
    %s" -msgstr "" -"Impossible d'enregistrer le fichier '%s'

    Message d'erreur :
    " -"%s" - -#: spyderlib/widgets/shell.py:701 -msgid "Copy without prompts" -msgstr "Copier sans les préfixes" - -#: spyderlib/widgets/shell.py:704 spyderlib/widgets/shell.py:708 -msgid "Clear line" -msgstr "Effacer la ligne" - -#: spyderlib/widgets/shell.py:710 -msgid "Clear shell" -msgstr "Effacer la console" - -#: spyderlib/widgets/shell.py:714 -msgid "Clear shell contents ('cls' command)" -msgstr "Effacer le contenu de la console" - -#: spyderlib/widgets/sourcecode/codeeditor.py:88 -msgid "Go to line:" -msgstr "Aller à la ligne :" - -#: spyderlib/widgets/sourcecode/codeeditor.py:97 -msgid "Line count:" -msgstr "Nombre de lignes :" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1210 -msgid "Breakpoint" -msgstr "Point d'arrêt" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1211 -msgid "Condition:" -msgstr "Condition :" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1671 -msgid "To do" -msgstr "À faire" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1880 -msgid "Removal error" -msgstr "Erreur de suppression" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1881 -msgid "" -"It was not possible to remove outputs from this notebook. The error is:\n" -"\n" -msgstr "" -"Impossible d'effacer les résultats de ce notebook:\n" -"\n" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2296 -msgid "Clear all ouput" -msgstr "Effacer tous les résultats" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2302 -msgid "Go to definition" -msgstr "Aller à la définition de l'objet" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2314 -msgid "Zoom reset" -msgstr "Réinitialisation du zoom" - -#: spyderlib/widgets/sourcecode/syntaxhighlighters.py:30 -msgid "Syntax highlighting for Matlab, Julia and other file types" -msgstr "Coloration syntaxique pour Matlab, Julia et d'autres types de fichier" - -#: spyderlib/widgets/status.py:23 -msgid "CPU and memory usage info in the status bar" -msgstr "" -"Informations sur l'utilisation processeur et mémoire dans la barre d'état" - -#: spyderlib/widgets/status.py:92 -msgid "Memory:" -msgstr "Mémoire :" - -#: spyderlib/widgets/status.py:93 -msgid "" -"Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows " -"platforms" -msgstr "" -"Occupation mémoire : requiert la bibliothèque `psutil` (>=v0.3) sur les " -"plateformes autres que Windows" - -#: spyderlib/widgets/status.py:105 -msgid "CPU:" -msgstr "CPU :" - -#: spyderlib/widgets/status.py:106 -msgid "CPU usage status: requires the `psutil` (>=v0.3) library" -msgstr "Occupation CPU : requiert la bibliothèque `psutil` (>=v0.3)" - -#: spyderlib/widgets/status.py:128 -msgid "Permissions:" -msgstr "Droits d'accès :" - -#: spyderlib/widgets/status.py:142 -msgid "End-of-lines:" -msgstr "Fins de ligne :" - -#: spyderlib/widgets/status.py:156 -msgid "Encoding:" -msgstr "Encodage :" - -#: spyderlib/widgets/status.py:169 -msgid "Line:" -msgstr "Ligne :" - -#: spyderlib/widgets/status.py:173 -msgid "Column:" -msgstr "Colonne :" - -#: spyderlib/widgets/tabs.py:137 -msgid "Browse tabs" -msgstr "Naviguer dans les onglets" - -#: spyderlib/widgets/tabs.py:260 -msgid "Close current tab" -msgstr "Fermer l'onglet" - -#: spyderlib/widgets/texteditor.py:72 -msgid "Text editor" -msgstr "Éditeur de texte" - -#~ msgid "" -#~ "%s will be closed.\n" -#~ "Do you want to kill the associated kernel and all of its clients?" -#~ msgstr "" -#~ "%s va être fermé.\n" -#~ "Souhaitez-vous fermer également le noyau associé et tous ses autres " -#~ "clients ?" - -#~ msgid "Install Spyder's input hook for Qt" -#~ msgstr "Installer le \"input hook\" de Spyder pour Qt" - -#~ msgid "" -#~ "PyQt installs an input hook that allows
    creating and interacting with " -#~ "Qt widgets in an interactive console without blocking it. On Windows " -#~ "platforms, it is strongly recommended to replace it by Spyder's. " -#~ "Regarding PySide, note that it does not install an input hook, so it is " -#~ "required to enable this feature in order to be able to manipulate PySide/" -#~ "Qtobjects interactively." -#~ msgstr "" -#~ "PyQt installe un \"input hook\", mécanisme permettant d'interagir avec " -#~ "des widgets Qt dans une console Python sans bloquer cette dernière. Sous " -#~ "Windows, il est fortement conseillé de le remplacer par celui de Spyder. " -#~ "Concernant PySide, aucun \"input hook\" n'étant implémenté, il est " -#~ "également recommandé d'activer cette option pour pouvoir manipuler des " -#~ "objets Qt de manière interactive." - -#~ msgid "Could not open ssh tunnel\n" -#~ msgstr "Impossible d'ouvrir un tunnel ssh\n" - -#~ msgid "Mismatch between kernel and frontend" -#~ msgstr "Incompatibilité entre le noyau et l'interface" - -#~ msgid "" -#~ "Your IPython frontend and kernel versions are incompatible!!

    We're sorry but we can't create an IPython console for you." -#~ msgstr "" -#~ "Les versions d'IPython de votre noyau et de l'interface sont " -#~ "incompatibles !

    Nous sommes désolés, mais nous ne pouvons " -#~ "pas vous ouvrir une console IPython." - -#~ msgid "Always edit in-place" -#~ msgstr "Édition en ligne pour tous les types" - -#~ msgid "Show collection contents" -#~ msgstr "Afficher le contenu des séquences" - -#~ msgid "Show toolbar" -#~ msgstr "Afficher la barre d'outils" - -#~ msgid "" -#~ "Resizing cells of a table of such size could take a long time.\n" -#~ "Do you want to continue anyway?" -#~ msgstr "" -#~ "Redimensionner les cellules d'un tableau d'une telle taille peut prendre " -#~ "du temps.\n" -#~ "Souhaitez-vous néanmoins continuer ?" - -#~ msgid "Interrupt kernel" -#~ msgstr "Interrompre le noyau" - -#~ msgid "Run &selection" -#~ msgstr "Exécuter la &sélection" - -#~ msgid "Patch Matplotlib figures" -#~ msgstr "Patcher les figures Matplotlib" - -#~ msgid "" -#~ "Patching Matplotlib library will add a button to customize figure options " -#~ "(Qt4Agg only) and fix some issues." -#~ msgstr "" -#~ "Appliquer un patch à la bibliothèque Matplotlib permet d'ajouter un " -#~ "bouton (au backend Qt4Agg) pour modifier les options des courbes et " -#~ "images affichées et de corriger quelques bogues." - -#~ msgid "(for example: kernel-3764.json, or simply 3764)" -#~ msgstr "(exemple: `kernel-3764.json`, ou simplement `3764`)" - -#~ msgid "Provide an IPython kernel connection file:" -#~ msgstr "Fichier de connexion du noyau IPython :" - -#~ msgid "Interpreter" -#~ msgstr "Interpréteur" - -#~ msgid "Dedicated Python interpreter" -#~ msgstr "Interpréteur Python dédié" - -#~ msgid "Open Python interpreter here" -#~ msgstr "Ouvrir un interpréteur Python ici" - -#~ msgid "Import as array" -#~ msgstr "Importer en tant que tableau" - -#~ msgid "(not installed)" -#~ msgstr "(non installé)" - -#~ msgid "Show single completion" -#~ msgstr "Afficher les listes de complétion avec un choix unique" - -#~ msgid "Open a Python interpreter at startup" -#~ msgstr "Ouvrir un interpréteur Python au démarrage" - -#~ msgid "Spyder startup" -#~ msgstr "Démarrage de Spyder" - -#~ msgid "Open an IPython console at startup" -#~ msgstr "Ouvrir une console IPython au démarrage" - -#~ msgid "Close current plugin" -#~ msgstr "Fermer la fenêtre courante" - -#~ msgid "Windows" -#~ msgstr "Fenêtres" - -#, fuzzy -#~ msgid "Plugins" -#~ msgstr " lignes" - -#~ msgid "Web resources" -#~ msgstr "Ressources en ligne" - -#, fuzzy -#~ msgid "Qt help" -#~ msgstr "Aide Qt" - -#, fuzzy -#~ msgid "IPython help" -#~ msgstr "Aide Python :" - -#~ msgid "Balloon tips" -#~ msgstr "Info-bulles" - -#~ msgid "Close current dockwidget" -#~ msgstr "Fermer le panneau actif" - -#~ msgid "IPython Help" -#~ msgstr "Aide IPython" - -#~ msgid "Windows and toolbars" -#~ msgstr "Fenêtres et barres d'outils" - -#~ msgid "Documentation" -#~ msgstr "Documentation" - -#~ msgid "Automatic notification to object inspector" -#~ msgstr "Notification automatique à l'inspecteur d'objets" - -#~ msgid "" -#~ "If this option is enabled, object inspector\n" -#~ "will automatically show informations on functions\n" -#~ "entered in editor (this is triggered when entering\n" -#~ "a left parenthesis after a valid function name)" -#~ msgstr "" -#~ "Si cette option est activée, l'inspecteur d'objet\n" -#~ "affichera automatiquement des informations\n" -#~ "sur les fonctions saisies dans l'éditeur\n" -#~ "(le mécanisme est déclenché par la saisie d'une\n" -#~ "parenthèse gauche après un nom valide de fonction)" - -#~ msgid "Create a new Python script" -#~ msgstr "Créer un nouveau script Python" - -#~ msgid "Open text file" -#~ msgstr "Ouvrir un fichier texte" - -#~ msgid "Save current file" -#~ msgstr "Enregistrer le fichier en cours d'édition" - -#~ msgid "" -#~ "Run current cell \n" -#~ "(see Editor documentation \n" -#~ "for more details on cells)" -#~ msgstr "" -#~ "Exécuter la cellule en cours d'édition\n" -#~ "(voir la documentation de l'Editeur, pour plus de détails sur les " -#~ "cellules)" - -#~ msgid "" -#~ "If this option is enabled, object inspector\n" -#~ "will automatically show informations on functions\n" -#~ "entered in console (this is triggered when entering\n" -#~ "a left parenthesis after a valid function name)" -#~ msgstr "" -#~ "Si cette option est activée, l'inspecteur d'objet affichera " -#~ "automatiquement des informations sur les fonctions saisies dans la " -#~ "console (le mécanisme est déclenché par la saisie d'une parenthèse gauche " -#~ "après un nom valide de fonction)" - -#~ msgid "Open a Python &interpreter" -#~ msgstr "Ouvrir un &interpréteur Python" - -#~ msgid "
    Installed version: %s" -#~ msgstr "
    Version installée: %s" - -#~ msgid "" -#~ "Unable to open IPython console because no supported IPython version was " -#~ "found.

    Supported IPython versions: %s" -#~ msgstr "" -#~ "Impossible d'ouvrir une console IPython car aucune version prise en " -#~ "charge n'est installée.

    Versions IPython prises en charge: " -#~ "%s" - -#~ msgid "&Interpreters" -#~ msgstr "&Interpréteurs" - -#~ msgid "Open Spyder path manager" -#~ msgstr "Ouvre le gestionnaire de chemin d'accès de Spyder" - -#~ msgid "Qt Assistant" -#~ msgstr "Qt Assistant (documentation Qt)" - -#~ msgid "Maximize current plugin to fit the whole application window" -#~ msgstr "" -#~ "Agrandir la fenêtre courante sur toute la surface de la fenêtre principale" - -#~ msgid "" -#~ "Restore current plugin to its original size and position within the " -#~ "application window" -#~ msgstr "" -#~ "Réduire la fenêtre courante à sa taille et position d'origine au sein de " -#~ "la fenêtre principale" - -#~ msgid "" -#~ "Run current block \n" -#~ "(see Editor section in documentation \n" -#~ "for more details on blocks) \n" -#~ "and advance to the next block" -#~ msgstr "" -#~ "Exécuter le bloc (voir la section 'Editor' de la documentation) \n" -#~ "et avancer jusqu'au bloc suivant" - -#~ msgid "Version" -#~ msgstr "Version" - -#~ msgid "Status" -#~ msgstr "Etat" - -#~ msgid "" -#~ "This feature requires the pywin32 module.\n" -#~ "It seems you don't have it installed." -#~ msgstr "" -#~ "Cette fonctionnalité nécessite l'installation du module pywin32.\n" -#~ "Ce dernier n'est apparemment pas installé." - -#~ msgid "Step Over" -#~ msgstr "Pas en avant" - -#~ msgid "" -#~ "Run selection or current \n" -#~ "block of lines" -#~ msgstr "Exécuter la sélection ou le bloc de lignes" - -#~ msgid "Debug current script in selected console" -#~ msgstr "Déboguer le script courant dans la console sélectionnée" - -#~ msgid "Debug Step Over" -#~ msgstr "Pas en avant (débogage)" - -#~ msgid "Debug Continue" -#~ msgstr "Continuer (débogage)" - -#~ msgid "Debug Step Into" -#~ msgstr "Pas vers l'intérieur (débogage)" - -#~ msgid "Debug Step Return" -#~ msgstr "Pas vers l'extérieur (débogage)" - -#~ msgid "" -#~ "Run selected script in\n" -#~ "current console" -#~ msgstr "Exécuter le script sélectionné dans la console courante" - -#~ msgid "Edit Run settings" -#~ msgstr "Modifier les options d'exécution" - -#~ msgid "" -#~ "Run again last script in current\n" -#~ "console with the same options" -#~ msgstr "" -#~ "Exécuter de nouveau le dernier script dans la console courante avec les " -#~ "mêmes options" - -#~ msgid "" -#~ "Run selected text or current block\n" -#~ "of lines inside current console" -#~ msgstr "" -#~ "Exécuter le texte sélectionné ou le bloc de lignes \n" -#~ "dans l'interpréteur de la console courante" - -#~ msgid "Run active script in a new Python interpreter" -#~ msgstr "Exécuter le script actuel dans un nouvel interpréteur Python" - -#~ msgid "" -#~ "Debug current script in external console\n" -#~ "(external console is executed in a separate process)" -#~ msgstr "" -#~ "Déboguer le script en cours d'édition dans la console externe\n" -#~ "(la console externe est exécutée dans un processus séparé)" - -#~ msgid "Edit run configurations" -#~ msgstr "Modifier les configurations d'exécution des scripts récents" - -#~ msgid "Run %s" -#~ msgstr "Exécution de %s" - -#~ msgid "Run configurations" -#~ msgstr "Configurations d'exécution" - -#~ msgid "Type \"copyright\", \"credits\" or \"license\" for more information." -#~ msgstr "" -#~ "Type \"copyright\", \"credits\" or \"license\" for more information." - -#~ msgid "Start an IPython kernel at startup" -#~ msgstr "Démarrer un noyau IPython au démarrage" - -#~ msgid "This option is not available for IPython versions prior to v0.12." -#~ msgstr "" -#~ "Cette option est désactivée pour les versions de IPython antérieures à " -#~ "v0.12." - -#, fuzzy -#~ msgid "Format: " -#~ msgstr "Format" - -#, fuzzy -#~ msgid " Source" -#~ msgstr "Source" - -#~ msgid "Open &interpreter" -#~ msgstr "Ouvrir un &interpréteur" - -#~ msgid "Start a new IPython kernel" -#~ msgstr "Démarrer un nouveau noyau IPython" - -#~ msgid "Client" -#~ msgstr "Client" - -#~ msgid "New IPython client..." -#~ msgstr "Nouveau client IPython..." - -#~ msgid "Qt" -#~ msgstr "Qt" - -#~ msgid "OS X" -#~ msgstr "OS X" - -#~ msgid "Gtk" -#~ msgstr "Gtk" - -#~ msgid "Wx" -#~ msgstr "Wx" - -#~ msgid "Tkinter" -#~ msgstr "Tkinter" - -#~ msgid "IPython kernels" -#~ msgstr "Noyaux IPython" - -#~ msgid "" -#~ "Note:
    IPython >=v0.12 is not installed on this computer." -#~ msgstr "" -#~ "Note :
    IPython >=v0.12 n'est pas installé sur cet " -#~ "ordinateur." - -#~ msgid "Set the appropriate IPython color option" -#~ msgstr "Utiliser le réglage de couleur IPython approprié" - -#~ msgid "Open an IPython interpreter at startup" -#~ msgstr "Ouvrir un interpréteur IPython au démarrage" - -#~ msgid "" -#~ "This option is not available for IPython\n" -#~ "versions which are not fully supported\n" -#~ "through Spyder's console (i.e. IPython v0.11+)." -#~ msgstr "" -#~ "Cette option est désactivée pour les versions de IPython\n" -#~ "qui ne sont entièrement prises en charge par Spyder\n" -#~ "à travers la console (i.e. IPython v0.11+)." - -#~ msgid "IPython interpreter command line options" -#~ msgstr "IPython : options en ligne de commande" - -#~ msgid "Open IPython interpreter" -#~ msgstr "Ouvrir un interpréteur IPython" - -#~ msgid "Open an IPython interpreter" -#~ msgstr "Ouvrir un interpréteur IPython" - -#~ msgid "Open IPython here" -#~ msgstr "Ouvrir IPython ici" - -#~ msgid "Please install the %s tool named '%s'" -#~ msgstr "Merci d'installer l'outil %s appelé '%s'" - -#~ msgid "Replace PyQt input hook by Spyder's" -#~ msgstr "Remplacer le \"input hook\" de PyQt par celui de Spyder" - -#~ msgid "What is the workspace?" -#~ msgstr "Qu'est-ce que l'espace de travail ?" - -#~ msgid "" -#~ "A Spyder project is a folder with source code files (and any other kind " -#~ "of related files) and a configuration file (named .spyderproject) " -#~ "which stores the project settings (PYTHONPATH, related projects, ...)." -#~ "

    The workspace is a directory, which contains Spyder projects " -#~ "(top level subdirectories) and a configuration file (named ." -#~ "spyderworkspace). " -#~ msgstr "" -#~ "Un projet Spyder est un répertoire contenant des fichiers source (et tout " -#~ "autre type de fichier) et un fichier de configuration (nommé ." -#~ "spyderproject) qui stocke les paramètres du projet (PYTHONPATH, " -#~ "projets associés, etc.).

    L'espace de travail est un répertoire " -#~ "contenant des projets Spyder (sous-répertoires uniquement) et un " -#~ "fichier de configuration (nommé .spyderworkspace)." - -#~ msgid "Matplotlib backend (default: Qt4Agg):" -#~ msgstr "Backend Matplotlib (valeur par défaut: Qt4Agg) :" - -#~ msgid "ETS_TOOLKIT (default value: qt4):" -#~ msgstr "ETS_TOOLKIT (valeur par défaut: qt4) :" - -#~ msgid "&Interact" -#~ msgstr "&Interagir" - -#~ msgid "Interact toolbar" -#~ msgstr "Barre d'outil d'interaction" - -#~ msgid "" -#~ "The project explorer shows a tree view of projects: the root of this tree " -#~ "is called the workspace.

    Each project is associated to a simple " -#~ "source code folder containing a configuration file (named ." -#~ "spyderproject) which stores the project settings (PYTHONPATH, related " -#~ "projects, ...). The workspace is also associated to a folder containing a " -#~ "configuration file (named .spyderworkspace) and the folders " -#~ "associated to its projects.

    In other words, the workspace is " -#~ "nothing but a list of projects whose associated folder share the same " -#~ "parent directory." -#~ msgstr "" -#~ "L'explorateur de projet affiche une arborescence de projets dont la " -#~ "racine est appelée l'espace de travail.

    Chaque projet est associé " -#~ "à un simple dossier de code source contenant un fichier de configuration " -#~ "(nommé .spyderproject) qui stocke les paramètres du projet " -#~ "(PYTHONPATH, projets associés, etc.). L'espace de travail est aussi " -#~ "associé à un dossier qui contient un fichier de configuration (nommé ." -#~ "spyderworkspace) et tous les dossiers associés à ses projets." -#~ "

    En d'autres termes, l'espace de travail est simplement une liste " -#~ "de projets dont les dossiers associés ont le même répertoire parent." - -#~ msgid "Create a new workspace directory" -#~ msgstr "Créer un nouvel espace de travail" - -#~ msgid "The directory %s is not a Spyder workspace." -#~ msgstr "Le répertoire %s n'est pas un espace de travail Spyder." - -#~ msgid "New folder..." -#~ msgstr "Nouveau répertoire..." - -#~ msgid "New file..." -#~ msgstr "Nouveau fichier..." - -#~ msgid "Open outside Spyder" -#~ msgstr "Ouvrir en dehors de Spyder" - -#~ msgid "Unable to delete selected file

    Error message:
    %s" -#~ msgstr "" -#~ "Impossible de supprimer le fichier sélectionné

    Message " -#~ "d'erreur :
    %s" - -#~ msgid "Unable to rename selected file

    Error message:
    %s" -#~ msgstr "" -#~ "Impossible de renommer le fichier sélectionné

    Message " -#~ "d'erreur :
    %s" - -#~ msgid "Folder name" -#~ msgstr "Nom du répertoire" - -#~ msgid "project" -#~ msgstr "projet" - -#~ msgid "Select project root path" -#~ msgstr "Sélectionner la racine du projet" - -#~ msgid "Edit filename filter" -#~ msgstr "Modifier le filtre des types de fichier affichés" - -#~ msgid "regular expressions" -#~ msgstr "expressions régulières" - -#~ msgid "global patterns" -#~ msgstr "syntaxe globale" - -#~ msgid "Include" -#~ msgstr "Inclure" - -#~ msgid "Exclude" -#~ msgstr "Exclure" - -#~ msgid "Edit filter" -#~ msgstr "Modifier le filtre" - -#~ msgid "Warning:" -#~ msgstr "Attention :" - -#~ msgid "" -#~ "%s is not properly installed
    (opening a terminal and typing " -#~ "\"%s script.py\" do not work)" -#~ msgstr "" -#~ "%s n'est pas installé correctement
    (l'exécution dans un " -#~ "terminal de \"%s script.py\" ne fonctionne pas)" - -#~ msgid "More informations on style guide for Python code: %s." -#~ msgstr "" -#~ "Pour plus d'informations sur les recommandations de style d'écriture du " -#~ "langage Python : %s." - -#~ msgid "unknown" -#~ msgstr "inconnu" - -#~ msgid "Startup script:" -#~ msgstr "Script de démarrage :" - -#~ msgid "Print" -#~ msgstr "Impression" - -#~ msgid "Unable to print document '%s'" -#~ msgstr "Impossible d'imprimer le document '%s'" - -#~ msgid "Show outline explorer" -#~ msgstr "Afficher l'explorateur de structure" - -#~ msgid "Co&mment" -#~ msgstr "Co&mmenter" - -#~ msgid "&Uncomment" -#~ msgstr "&Décommenter" - -#~ msgid "Uncomment current line or selection" -#~ msgstr "Décommenter la sélection ou la ligne en cours d'édition" - -#~ msgid "Matched braces:" -#~ msgstr "Parenthèses correspondantes:" - -#~ msgid "Unmatched braces:" -#~ msgstr "Parenthèse isolée:" - -#~ msgid "Please install matplotlib." -#~ msgstr "Merci d'installer matplotlib." - -#~ msgid "Remote editing" -#~ msgstr "Éditeurs dans le processus distant" - -#~ msgid "" -#~ "Remote editing for NumPy arrays, PIL images, lists, tuples and " -#~ "dictionaries" -#~ msgstr "" -#~ "Les tableaux NumPy, images PIL, listes, tuples et dictionnaires seront " -#~ "modifiés dans un éditeur exécuté dans le processus distant" - -#~ msgid "" -#~ "Editors are opened in the remote process for NumPy arrays, PIL images, " -#~ "lists, tuples and dictionaries" -#~ msgstr "" -#~ "Les tableaux NumPy, images PIL, listes, tuples et dictionnaires seront " -#~ "modifiés dans un éditeur exécuté dans le processus distant" - -#~ msgid "Open..." -#~ msgstr "Ouvrir..." - -#~ msgid "Save as..." -#~ msgstr "Enregistrer sous..." - -#~ msgid "Close" -#~ msgstr "Fermer" - -#~ msgid "Close all" -#~ msgstr "Fermer tout" - -#~ msgid "Add block comment" -#~ msgstr "Ajouter un bloc de commentaires" - -#~ msgid "Remove block comment" -#~ msgstr "Supprimer un bloc de commentaires" - -#~ msgid "Save all" -#~ msgstr "Enregistrer tout" - -#~ msgid "Print..." -#~ msgstr "Imprimer..." - -#~ msgid "Re-run last script" -#~ msgstr "Exécuter de nouveau le dernier script" - -#~ msgid "Close file" -#~ msgstr "Fermer le fichier" - -#~ msgid "Show TODO/FIXME/XXX comments list" -#~ msgstr "Afficher la liste des commentaires du type TODO/FIXME/XXX" - -#~ msgid "Configure..." -#~ msgstr "Configurer..." - -#~ msgid "Previous file" -#~ msgstr "Fichier précédent" - -#~ msgid "Next file" -#~ msgstr "Fichier suivant" - -#~ msgid "Revert" -#~ msgstr "Réinitialiser" - -#~ msgid "Add &block comment around current line or selection" -#~ msgstr "" -#~ "Ajouter un &bloc de commentaires autour de la sélection ou de la ligne en " -#~ "cours d'édition" - -#~ msgid "Tasks (TODO, FIXME, XXX)" -#~ msgstr "Tâches (TODO, FIXME, XXX)" - -#~ msgid "" -#~ "IPython interpreter command line options:\n" -#~ "(Qt4 support: -q4thread)\n" -#~ "(Qt4 and matplotlib support: -q4thread -pylab)" -#~ msgstr "" -#~ "Options en ligne de commande de IPython :\n" -#~ "(support Qt4 : -q4thread)\n" -#~ "(support Qt4 et matplotlib : -q4thread -pylab)" - -#~ msgid "" -#~ "PyQt installs an input hook that processes events when an interactive " -#~ "interpreter is waiting for user input, thus allowing to interact with " -#~ "widgets without blocking the Python shell. Unfortunately, this is not " -#~ "working well on Windows platforms." -#~ msgstr "" -#~ "PyQt installe un mécanisme (\"input hook\") qui permet d'interagir avec " -#~ "des widgets dans un interpréteur Python sans bloquer ce dernier. " -#~ "Malheureusement, ce mécanisme ne fonctionne pas parfaitement sous Windows." - -#~ msgid "Replace text" -#~ msgstr "Remplacer" - -#~ msgid "Find next" -#~ msgstr "Rechercher le suivant" - -#~ msgid "Find previous" -#~ msgstr "Rechercher le précédent" - -#~ msgid "Edit filename filter..." -#~ msgstr "Modifier le filtre des types de fichier affichés" diff -Nru spyder-2.3.8+dfsg1/spyderlib/locale/spyderlib.pot spyder-3.0.2+dfsg1/spyderlib/locale/spyderlib.pot --- spyder-2.3.8+dfsg1/spyderlib/locale/spyderlib.pot 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/locale/spyderlib.pot 1970-01-01 00:00:00.000000000 +0000 @@ -1,4358 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2015-11-24 20:56+COT\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: ENCODING\n" -"Generated-By: pygettext.py 1.5\n" - - -#: spyderlib/config.py:29 -msgid "Python files" -msgstr "" - -#: spyderlib/config.py:30 -msgid "Cython/Pyrex files" -msgstr "" - -#: spyderlib/config.py:31 -msgid "C files" -msgstr "" - -#: spyderlib/config.py:32 -msgid "C++ files" -msgstr "" - -#: spyderlib/config.py:33 -msgid "OpenCL files" -msgstr "" - -#: spyderlib/config.py:34 -msgid "Fortran files" -msgstr "" - -#: spyderlib/config.py:35 -msgid "IDL files" -msgstr "" - -#: spyderlib/config.py:36 -msgid "MATLAB files" -msgstr "" - -#: spyderlib/config.py:37 -msgid "Julia files" -msgstr "" - -#: spyderlib/config.py:38 -msgid "Yaml files" -msgstr "" - -#: spyderlib/config.py:39 -msgid "Patch and diff files" -msgstr "" - -#: spyderlib/config.py:40 -msgid "Batch files" -msgstr "" - -#: spyderlib/config.py:41 spyderlib/utils/iofuncs.py:514 -msgid "Text files" -msgstr "" - -#: spyderlib/config.py:42 -msgid "reStructured Text files" -msgstr "" - -#: spyderlib/config.py:43 -msgid "gettext files" -msgstr "" - -#: spyderlib/config.py:44 -msgid "NSIS files" -msgstr "" - -#: spyderlib/config.py:45 -msgid "Web page files" -msgstr "" - -#: spyderlib/config.py:46 -msgid "XML files" -msgstr "" - -#: spyderlib/config.py:47 -msgid "Javascript files" -msgstr "" - -#: spyderlib/config.py:48 -msgid "Json files" -msgstr "" - -#: spyderlib/config.py:49 -msgid "IPython notebooks" -msgstr "" - -#: spyderlib/config.py:50 -msgid "Enaml files" -msgstr "" - -#: spyderlib/config.py:51 -msgid "Configuration files" -msgstr "" - -#: spyderlib/config.py:58 spyderlib/widgets/explorer.py:651 -msgid "All files" -msgstr "" - -#: spyderlib/ipythonconfig.py:23 spyderlib/ipythonconfig.py:25 -#: spyderlib/ipythonconfig.py:32 -msgid "IPython Console integration" -msgstr "" - -#: spyderlib/plugins/__init__.py:318 spyderlib/plugins/editor.py:94 -#: spyderlib/plugins/editor.py:527 spyderlib/plugins/editor.py:1606 -#: spyderlib/plugins/inspector.py:134 spyderlib/plugins/inspector.py:403 -#: spyderlib/widgets/editor.py:434 -#: spyderlib/widgets/sourcecode/codeeditor.py:85 -#: spyderlib/widgets/sourcecode/codeeditor.py:2709 -msgid "Editor" -msgstr "" - -#: spyderlib/plugins/configdialog.py:144 -msgid "Preferences" -msgstr "" - -#: spyderlib/plugins/configdialog.py:429 -msgid "Invalid directory path" -msgstr "" - -#: spyderlib/plugins/configdialog.py:432 spyderlib/plugins/configdialog.py:448 -#: spyderlib/plugins/runconfig.py:172 spyderlib/plugins/runconfig.py:236 -#: spyderlib/plugins/workingdirectory.py:286 spyderlib/widgets/explorer.py:565 -#: spyderlib/widgets/externalshell/pythonshell.py:623 -#: spyderlib/widgets/findinfiles.py:504 spyderlib/widgets/pathmanager.py:218 -#: spyderlib/widgets/projectexplorer.py:890 -msgid "Select directory" -msgstr "" - -#: spyderlib/plugins/configdialog.py:460 -msgid "Invalid file path" -msgstr "" - -#: spyderlib/plugins/configdialog.py:463 spyderlib/plugins/configdialog.py:481 -msgid "Select file" -msgstr "" - -#: spyderlib/plugins/configdialog.py:480 -msgid "All files (*)" -msgstr "" - -#: spyderlib/plugins/configdialog.py:550 spyderlib/widgets/formlayout.py:216 -msgid "Bold" -msgstr "" - -#: spyderlib/plugins/configdialog.py:553 spyderlib/widgets/formlayout.py:211 -msgid "Italic" -msgstr "" - -#: spyderlib/plugins/configdialog.py:591 -msgid "Font: " -msgstr "" - -#: spyderlib/plugins/configdialog.py:595 -msgid "Size: " -msgstr "" - -#: spyderlib/plugins/configdialog.py:604 spyderlib/plugins/history.py:47 -msgid "Font style" -msgstr "" - -#: spyderlib/plugins/configdialog.py:657 -msgid "General" -msgstr "" - -#: spyderlib/plugins/configdialog.py:664 spyderlib/plugins/editor.py:103 -#: spyderlib/plugins/externalconsole.py:65 -#: spyderlib/plugins/ipythonconsole.py:161 -msgid "Interface" -msgstr "" - -#: spyderlib/plugins/configdialog.py:672 -msgid "Qt windows style" -msgstr "" - -#: spyderlib/plugins/configdialog.py:676 -msgid "Use a single instance" -msgstr "" - -#: spyderlib/plugins/configdialog.py:678 -msgid "Set this to open external
    Python files in an already running instance (Requires a restart)" -msgstr "" - -#: spyderlib/plugins/configdialog.py:681 -msgid "Vertical dockwidget title bars" -msgstr "" - -#: spyderlib/plugins/configdialog.py:683 -msgid "Vertical dockwidget tabs" -msgstr "" - -#: spyderlib/plugins/configdialog.py:685 -msgid "Animated toolbars and dockwidgets" -msgstr "" - -#: spyderlib/plugins/configdialog.py:687 -msgid "Tear off menus" -msgstr "" - -#: spyderlib/plugins/configdialog.py:688 -msgid "Set this to detach any
    menu from the main window" -msgstr "" - -#: spyderlib/plugins/configdialog.py:690 -msgid "Custom dockwidget margin:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:717 -msgid "Status bar" -msgstr "" - -#: spyderlib/plugins/configdialog.py:718 -msgid "Show memory usage every" -msgstr "" - -#: spyderlib/plugins/configdialog.py:729 -msgid "Show CPU usage every" -msgstr "" - -#: spyderlib/plugins/configdialog.py:746 -msgid "Debugging" -msgstr "" - -#: spyderlib/plugins/configdialog.py:747 -msgid "Pop up internal console when internal errors appear" -msgstr "" - -#: spyderlib/plugins/configdialog.py:769 -msgid "Syntax coloring" -msgstr "" - -#: spyderlib/plugins/configdialog.py:778 -msgid "Background:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:779 -#: spyderlib/widgets/sourcecode/codeeditor.py:95 -msgid "Current line:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:780 -msgid "Current cell:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:781 -msgid "Occurence:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:782 -msgid "Link:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:783 -msgid "Side areas:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:784 -msgid "Matched parentheses:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:785 -msgid "Unmatched parentheses:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:786 -msgid "Normal text:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:787 -msgid "Keyword:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:788 -msgid "Builtin:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:789 -msgid "Definition:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:790 -msgid "Comment:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:791 -msgid "String:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:792 -msgid "Number:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:793 -msgid "Instance:" -msgstr "" - -#: spyderlib/plugins/configdialog.py:799 -msgid "Color scheme" -msgstr "" - -#: spyderlib/plugins/configdialog.py:821 spyderlib/plugins/shortcuts.py:344 -msgid "Reset to default values" -msgstr "" - -#: spyderlib/plugins/console.py:105 -msgid "Internal console" -msgstr "" - -#: spyderlib/plugins/console.py:125 spyderlib/spyder.py:772 -#: spyderlib/widgets/ipython.py:583 -msgid "&Quit" -msgstr "" - -#: spyderlib/plugins/console.py:126 spyderlib/spyder.py:773 -msgid "Quit" -msgstr "" - -#: spyderlib/plugins/console.py:129 spyderlib/plugins/externalconsole.py:1099 -msgid "&Run..." -msgstr "" - -#: spyderlib/plugins/console.py:130 spyderlib/plugins/externalconsole.py:1100 -msgid "Run a Python script" -msgstr "" - -#: spyderlib/plugins/console.py:133 -msgid "Environment variables..." -msgstr "" - -#: spyderlib/plugins/console.py:135 -msgid "Show and edit environment variables (for current session)" -msgstr "" - -#: spyderlib/plugins/console.py:139 -msgid "Show sys.path contents..." -msgstr "" - -#: spyderlib/plugins/console.py:141 -msgid "Show (read-only) sys.path" -msgstr "" - -#: spyderlib/plugins/console.py:144 -msgid "Buffer..." -msgstr "" - -#: spyderlib/plugins/console.py:145 spyderlib/plugins/externalconsole.py:85 -#: spyderlib/plugins/history.py:40 -msgid "Set maximum line count" -msgstr "" - -#: spyderlib/plugins/console.py:148 spyderlib/plugins/explorer.py:57 -#: spyderlib/plugins/history.py:164 spyderlib/plugins/inspector.py:372 -#: spyderlib/plugins/projectexplorer.py:56 -msgid "&Font..." -msgstr "" - -#: spyderlib/plugins/console.py:149 spyderlib/plugins/history.py:165 -msgid "Set shell font style" -msgstr "" - -#: spyderlib/plugins/console.py:152 -msgid "External editor path..." -msgstr "" - -#: spyderlib/plugins/console.py:153 -msgid "Set external editor executable path" -msgstr "" - -#: spyderlib/plugins/console.py:156 spyderlib/plugins/editor.py:144 -#: spyderlib/plugins/externalconsole.py:86 spyderlib/plugins/history.py:43 -#: spyderlib/plugins/history.py:167 spyderlib/plugins/inspector.py:175 -#: spyderlib/plugins/inspector.py:375 -msgid "Wrap lines" -msgstr "" - -#: spyderlib/plugins/console.py:159 spyderlib/plugins/editor.py:178 -#: spyderlib/plugins/externalconsole.py:133 -#: spyderlib/plugins/ipythonconsole.py:175 -msgid "Display balloon tips" -msgstr "" - -#: spyderlib/plugins/console.py:163 spyderlib/plugins/editor.py:172 -#: spyderlib/plugins/externalconsole.py:127 -msgid "Automatic code completion" -msgstr "" - -#: spyderlib/plugins/console.py:167 spyderlib/plugins/editor.py:176 -#: spyderlib/plugins/externalconsole.py:131 -msgid "Enter key selects completion" -msgstr "" - -#: spyderlib/plugins/console.py:172 -msgid "Internal console settings" -msgstr "" - -#: spyderlib/plugins/console.py:223 spyderlib/plugins/externalconsole.py:1285 -msgid "Run Python script" -msgstr "" - -#: spyderlib/plugins/console.py:224 spyderlib/plugins/externalconsole.py:229 -#: spyderlib/plugins/externalconsole.py:1286 spyderlib/widgets/explorer.py:666 -msgid "Python scripts" -msgstr "" - -#: spyderlib/plugins/console.py:269 spyderlib/plugins/explorer.py:109 -#: spyderlib/plugins/history.py:282 spyderlib/plugins/inspector.py:651 -#: spyderlib/plugins/projectexplorer.py:118 -msgid "Select a new font" -msgstr "" - -#: spyderlib/plugins/console.py:276 -msgid "Buffer" -msgstr "" - -#: spyderlib/plugins/console.py:277 -msgid "Maximum line count" -msgstr "" - -#: spyderlib/plugins/console.py:286 -msgid "External editor" -msgstr "" - -#: spyderlib/plugins/console.py:287 -msgid "External editor executable path:" -msgstr "" - -#: spyderlib/plugins/editor.py:100 -msgid "Edit template for new modules" -msgstr "" - -#: spyderlib/plugins/editor.py:105 -msgid "Text and margin font style" -msgstr "" - -#: spyderlib/plugins/editor.py:108 -msgid "Sort files according to full path" -msgstr "" - -#: spyderlib/plugins/editor.py:110 -msgid "Show tab bar" -msgstr "" - -#: spyderlib/plugins/editor.py:117 spyderlib/plugins/editor.py:192 -#: spyderlib/plugins/externalconsole.py:81 -#: spyderlib/plugins/externalconsole.py:126 spyderlib/plugins/history.py:42 -#: spyderlib/plugins/inspector.py:174 spyderlib/plugins/ipythonconsole.py:199 -msgid "Source code" -msgstr "" - -#: spyderlib/plugins/editor.py:118 -msgid "Show line numbers" -msgstr "" - -#: spyderlib/plugins/editor.py:119 spyderlib/plugins/editor.py:892 -msgid "Show blank spaces" -msgstr "" - -#: spyderlib/plugins/editor.py:120 -msgid "Show vertical line after" -msgstr "" - -#: spyderlib/plugins/editor.py:121 -msgid "characters" -msgstr "" - -#: spyderlib/plugins/editor.py:129 -msgid "Highlight current line" -msgstr "" - -#: spyderlib/plugins/editor.py:131 -msgid "Highlight current cell" -msgstr "" - -#: spyderlib/plugins/editor.py:133 -msgid "Highlight occurences after" -msgstr "" - -#: spyderlib/plugins/editor.py:147 spyderlib/plugins/history.py:51 -#: spyderlib/plugins/inspector.py:178 -msgid "Syntax color scheme: " -msgstr "" - -#: spyderlib/plugins/editor.py:161 spyderlib/plugins/runconfig.py:313 -#: spyderlib/plugins/runconfig.py:435 spyderlib/plugins/runconfig.py:440 -#: spyderlib/spyder.py:1850 spyderlib/utils/programs.py:175 -#: spyderlib/widgets/explorer.py:234 -#: spyderlib/widgets/externalshell/baseshell.py:138 -msgid "Run" -msgstr "" - -#: spyderlib/plugins/editor.py:162 -msgid "Save all files before running script" -msgstr "" - -#: spyderlib/plugins/editor.py:165 -msgid "Run selection" -msgstr "" - -#: spyderlib/plugins/editor.py:166 -msgid "Maintain focus in the Editor after running cells or selections" -msgstr "" - -#: spyderlib/plugins/editor.py:169 spyderlib/plugins/externalconsole.py:365 -msgid "Introspection" -msgstr "" - -#: spyderlib/plugins/editor.py:174 spyderlib/plugins/externalconsole.py:129 -msgid "Case sensitive code completion" -msgstr "" - -#: spyderlib/plugins/editor.py:179 -msgid "Link to object definition" -msgstr "" - -#: spyderlib/plugins/editor.py:181 -msgid "" -"If this option is enabled, clicking on an object\n" -"name (left-click + Ctrl key) will go this object\n" -"definition (if resolved)." -msgstr "" - -#: spyderlib/plugins/editor.py:185 -msgid "Warning:
    The Python module rope is not installed on this computer: calltips, code completion and go-to-definition features won't be available." -msgstr "" - -#: spyderlib/plugins/editor.py:193 -msgid "Automatic insertion of parentheses, braces and brackets" -msgstr "" - -#: spyderlib/plugins/editor.py:196 -msgid "Automatic insertion of closing quotes" -msgstr "" - -#: spyderlib/plugins/editor.py:198 -msgid "Automatic insertion of colons after 'for', 'if', 'def', etc" -msgstr "" - -#: spyderlib/plugins/editor.py:201 -msgid "Automatic indentation after 'else', 'elif', etc." -msgstr "" - -#: spyderlib/plugins/editor.py:203 -msgid "Indentation characters: " -msgstr "" - -#: spyderlib/plugins/editor.py:204 -msgid "4 spaces" -msgstr "" - -#: spyderlib/plugins/editor.py:205 -msgid "2 spaces" -msgstr "" - -#: spyderlib/plugins/editor.py:206 -msgid "tab" -msgstr "" - -#: spyderlib/plugins/editor.py:207 -msgid "Tab stop width:" -msgstr "" - -#: spyderlib/plugins/editor.py:207 -msgid "pixels" -msgstr "" - -#: spyderlib/plugins/editor.py:209 -msgid "Tab always indent" -msgstr "" - -#: spyderlib/plugins/editor.py:211 -msgid "" -"If enabled, pressing Tab will always indent,\n" -"even when the cursor is not at the beginning\n" -"of a line (when this option is enabled, code\n" -"completion may be triggered using the alternate\n" -"shortcut: Ctrl+Space)" -msgstr "" - -#: spyderlib/plugins/editor.py:216 -msgid "Intelligent backspace" -msgstr "" - -#: spyderlib/plugins/editor.py:218 -msgid "Automatically remove trailing spaces when saving files" -msgstr "" - -#: spyderlib/plugins/editor.py:222 -msgid "Analysis" -msgstr "" - -#: spyderlib/plugins/editor.py:224 -msgid "Note: add analysis:ignore in a comment to ignore code/style analysis warnings. For more informations on style guide for Python code, please refer to the %s page." -msgstr "" - -#: spyderlib/plugins/editor.py:233 -#: spyderlib/widgets/sourcecode/codeeditor.py:1617 -msgid "Code analysis" -msgstr "" - -#: spyderlib/plugins/editor.py:235 -msgid "" -"If enabled, Python source code will be analyzed\n" -"using pyflakes, lines containing errors or \n" -"warnings will be highlighted" -msgstr "" - -#: spyderlib/plugins/editor.py:240 -msgid "Code analysis requires pyflakes %s+" -msgstr "" - -#: spyderlib/plugins/editor.py:242 -msgid "Style analysis" -msgstr "" - -#: spyderlib/plugins/editor.py:244 -msgid "" -"If enabled, Python source code will be analyzed\n" -"using pep8, lines that are not following PEP8\n" -"style guide will be highlighted" -msgstr "" - -#: spyderlib/plugins/editor.py:251 -msgid "Tasks (TODO, FIXME, XXX, HINT, TIP, @todo)" -msgstr "" - -#: spyderlib/plugins/editor.py:254 -msgid "Perform analysis when saving file and every" -msgstr "" - -#: spyderlib/plugins/editor.py:258 -msgid "Perform analysis only when saving file" -msgstr "" - -#: spyderlib/plugins/editor.py:306 -msgid "End-of-line characters" -msgstr "" - -#: spyderlib/plugins/editor.py:307 -msgid "When opening a text file containing mixed end-of-line characters (this may raise syntax errors in the consoles on Windows platforms), Spyder may fix the file automatically." -msgstr "" - -#: spyderlib/plugins/editor.py:313 -msgid "Fix automatically and show warning message box" -msgstr "" - -#: spyderlib/plugins/editor.py:324 spyderlib/plugins/externalconsole.py:363 -#: spyderlib/plugins/ipythonconsole.py:444 -#: spyderlib/plugins/variableexplorer.py:41 -msgid "Display" -msgstr "" - -#: spyderlib/plugins/editor.py:326 -msgid "Code Introspection/Analysis" -msgstr "" - -#: spyderlib/plugins/editor.py:329 spyderlib/plugins/externalconsole.py:367 -msgid "Advanced settings" -msgstr "" - -#: spyderlib/plugins/editor.py:583 spyderlib/widgets/editortools.py:508 -msgid "Show/hide outline explorer" -msgstr "" - -#: spyderlib/plugins/editor.py:589 -msgid "Show/hide project explorer" -msgstr "" - -#: spyderlib/plugins/editor.py:597 -msgid "&New file..." -msgstr "" - -#: spyderlib/plugins/editor.py:598 spyderlib/plugins/workingdirectory.py:82 -#: spyderlib/widgets/explorer.py:643 spyderlib/widgets/explorer.py:650 -msgid "New file" -msgstr "" - -#: spyderlib/plugins/editor.py:605 -msgid "&Open..." -msgstr "" - -#: spyderlib/plugins/editor.py:606 spyderlib/plugins/editor.py:1647 -#: spyderlib/plugins/workingdirectory.py:69 -msgid "Open file" -msgstr "" - -#: spyderlib/plugins/editor.py:613 -msgid "&Revert" -msgstr "" - -#: spyderlib/plugins/editor.py:614 -msgid "Revert file from disk" -msgstr "" - -#: spyderlib/plugins/editor.py:617 -msgid "&Save" -msgstr "" - -#: spyderlib/plugins/editor.py:618 -msgid "Save file" -msgstr "" - -#: spyderlib/plugins/editor.py:625 -msgid "Sav&e all" -msgstr "" - -#: spyderlib/plugins/editor.py:626 -msgid "Save all files" -msgstr "" - -#: spyderlib/plugins/editor.py:633 -msgid "Save &as..." -msgstr "" - -#: spyderlib/plugins/editor.py:634 -msgid "Save current file as..." -msgstr "" - -#: spyderlib/plugins/editor.py:636 spyderlib/plugins/editor.py:637 -msgid "Print preview..." -msgstr "" - -#: spyderlib/plugins/editor.py:638 -msgid "&Print..." -msgstr "" - -#: spyderlib/plugins/editor.py:639 -msgid "Print current file..." -msgstr "" - -#: spyderlib/plugins/editor.py:644 -msgid "&Close" -msgstr "" - -#: spyderlib/plugins/editor.py:645 -msgid "Close current file" -msgstr "" - -#: spyderlib/plugins/editor.py:647 -msgid "C&lose all" -msgstr "" - -#: spyderlib/plugins/editor.py:648 -msgid "Close all opened files" -msgstr "" - -#: spyderlib/plugins/editor.py:655 -msgid "Set/Clear breakpoint" -msgstr "" - -#: spyderlib/plugins/editor.py:662 -msgid "Set/Edit conditional breakpoint" -msgstr "" - -#: spyderlib/plugins/editor.py:669 -msgid "Clear breakpoints in all files" -msgstr "" - -#: spyderlib/plugins/editor.py:671 -msgid "Breakpoints" -msgstr "" - -#: spyderlib/plugins/editor.py:675 -msgid "Debug with winpdb" -msgstr "" - -#: spyderlib/plugins/editor.py:682 spyderlib/spyder.py:575 -msgid "&Debug" -msgstr "" - -#: spyderlib/plugins/editor.py:683 -msgid "Debug file" -msgstr "" - -#: spyderlib/plugins/editor.py:688 -msgid "Step" -msgstr "" - -#: spyderlib/plugins/editor.py:689 -msgid "Run current line" -msgstr "" - -#: spyderlib/plugins/editor.py:695 -msgid "Continue" -msgstr "" - -#: spyderlib/plugins/editor.py:696 -msgid "Continue execution until next breakpoint" -msgstr "" - -#: spyderlib/plugins/editor.py:703 -msgid "Step Into" -msgstr "" - -#: spyderlib/plugins/editor.py:704 -msgid "Step into function or method of current line" -msgstr "" - -#: spyderlib/plugins/editor.py:711 -msgid "Step Return" -msgstr "" - -#: spyderlib/plugins/editor.py:712 -msgid "Run until current function or method returns" -msgstr "" - -#: spyderlib/plugins/editor.py:719 -msgid "Exit" -msgstr "" - -#: spyderlib/plugins/editor.py:720 -msgid "Exit Debug" -msgstr "" - -#: spyderlib/plugins/editor.py:731 -msgid "Debugging control" -msgstr "" - -#: spyderlib/plugins/editor.py:735 spyderlib/plugins/editor.py:1246 -#: spyderlib/spyder.py:570 -msgid "&Run" -msgstr "" - -#: spyderlib/plugins/editor.py:736 -msgid "Run file" -msgstr "" - -#: spyderlib/plugins/editor.py:742 -msgid "&Configure..." -msgstr "" - -#: spyderlib/plugins/editor.py:743 -#: spyderlib/widgets/externalshell/pythonshell.py:294 -msgid "Run settings" -msgstr "" - -#: spyderlib/plugins/editor.py:752 -msgid "Re-run &last script" -msgstr "" - -#: spyderlib/plugins/editor.py:753 -msgid "Run again last file" -msgstr "" - -#: spyderlib/plugins/editor.py:760 -#: spyderlib/widgets/sourcecode/codeeditor.py:2305 -msgid "Run &selection or current line" -msgstr "" - -#: spyderlib/plugins/editor.py:763 -msgid "Run selection or current line" -msgstr "" - -#: spyderlib/plugins/editor.py:776 -msgid "Run cell" -msgstr "" - -#: spyderlib/plugins/editor.py:778 -msgid "" -"Run current cell (Ctrl+Enter)\n" -"[Use #%% to create cells]" -msgstr "" - -#: spyderlib/plugins/editor.py:783 -msgid "Run cell and advance" -msgstr "" - -#: spyderlib/plugins/editor.py:786 -msgid "Run current cell and go to the next one (Shift+Enter)" -msgstr "" - -#: spyderlib/plugins/editor.py:792 -msgid "Show todo list" -msgstr "" - -#: spyderlib/plugins/editor.py:793 -msgid "Show TODO/FIXME/XXX/HINT/TIP/@todo comments list" -msgstr "" - -#: spyderlib/plugins/editor.py:801 -msgid "Show warning/error list" -msgstr "" - -#: spyderlib/plugins/editor.py:802 -msgid "Show code analysis warnings/errors" -msgstr "" - -#: spyderlib/plugins/editor.py:809 -msgid "Previous warning/error" -msgstr "" - -#: spyderlib/plugins/editor.py:810 -msgid "Go to previous code analysis warning/error" -msgstr "" - -#: spyderlib/plugins/editor.py:813 -msgid "Next warning/error" -msgstr "" - -#: spyderlib/plugins/editor.py:814 -msgid "Go to next code analysis warning/error" -msgstr "" - -#: spyderlib/plugins/editor.py:818 -msgid "Last edit location" -msgstr "" - -#: spyderlib/plugins/editor.py:819 -msgid "Go to last edit location" -msgstr "" - -#: spyderlib/plugins/editor.py:825 -msgid "Previous cursor position" -msgstr "" - -#: spyderlib/plugins/editor.py:826 -msgid "Go to previous cursor position" -msgstr "" - -#: spyderlib/plugins/editor.py:832 -msgid "Next cursor position" -msgstr "" - -#: spyderlib/plugins/editor.py:833 -msgid "Go to next cursor position" -msgstr "" - -#: spyderlib/plugins/editor.py:840 -#: spyderlib/widgets/sourcecode/codeeditor.py:2292 -msgid "Comment" -msgstr "" - -#: spyderlib/plugins/editor.py:840 -#: spyderlib/widgets/sourcecode/codeeditor.py:2292 -msgid "Uncomment" -msgstr "" - -#: spyderlib/plugins/editor.py:841 -msgid "Comment current line or selection" -msgstr "" - -#: spyderlib/plugins/editor.py:845 -msgid "Add &block comment" -msgstr "" - -#: spyderlib/plugins/editor.py:846 -msgid "Add block comment around current line or selection" -msgstr "" - -#: spyderlib/plugins/editor.py:852 -msgid "R&emove block comment" -msgstr "" - -#: spyderlib/plugins/editor.py:853 -msgid "Remove comment block around current line or selection" -msgstr "" - -#: spyderlib/plugins/editor.py:864 -msgid "Indent" -msgstr "" - -#: spyderlib/plugins/editor.py:865 -msgid "Indent current line or selection" -msgstr "" - -#: spyderlib/plugins/editor.py:868 -msgid "Unindent" -msgstr "" - -#: spyderlib/plugins/editor.py:869 -msgid "Unindent current line or selection" -msgstr "" - -#: spyderlib/plugins/editor.py:874 -msgid "Carriage return and line feed (Windows)" -msgstr "" - -#: spyderlib/plugins/editor.py:877 -msgid "Line feed (UNIX)" -msgstr "" - -#: spyderlib/plugins/editor.py:880 -msgid "Carriage return (Mac)" -msgstr "" - -#: spyderlib/plugins/editor.py:886 -msgid "Convert end-of-line characters" -msgstr "" - -#: spyderlib/plugins/editor.py:890 -msgid "Remove trailing spaces" -msgstr "" - -#: spyderlib/plugins/editor.py:894 -msgid "Fix indentation" -msgstr "" - -#: spyderlib/plugins/editor.py:895 -msgid "Replace tab characters by space characters" -msgstr "" - -#: spyderlib/plugins/editor.py:898 -msgid "Go to line..." -msgstr "" - -#: spyderlib/plugins/editor.py:906 -msgid "Set console working directory" -msgstr "" - -#: spyderlib/plugins/editor.py:908 -msgid "Set current console (and file explorer) working directory to current script directory" -msgstr "" - -#: spyderlib/plugins/editor.py:913 -msgid "Maximum number of recent files..." -msgstr "" - -#: spyderlib/plugins/editor.py:916 -msgid "Clear recent files list" -msgstr "" - -#: spyderlib/plugins/editor.py:916 -msgid "Clear this list" -msgstr "" - -#: spyderlib/plugins/editor.py:918 -msgid "Open &recent" -msgstr "" - -#: spyderlib/plugins/editor.py:1234 spyderlib/spyder.py:551 -msgid "File toolbar" -msgstr "" - -#: spyderlib/plugins/editor.py:1235 spyderlib/spyder.py:561 -msgid "Search toolbar" -msgstr "" - -#: spyderlib/plugins/editor.py:1236 spyderlib/spyder.py:566 -msgid "Source toolbar" -msgstr "" - -#: spyderlib/plugins/editor.py:1237 spyderlib/spyder.py:571 -msgid "Run toolbar" -msgstr "" - -#: spyderlib/plugins/editor.py:1238 spyderlib/spyder.py:576 -msgid "Debug toolbar" -msgstr "" - -#: spyderlib/plugins/editor.py:1239 spyderlib/spyder.py:556 -msgid "Edit toolbar" -msgstr "" - -#: spyderlib/plugins/editor.py:1242 spyderlib/spyder.py:548 -msgid "&File" -msgstr "" - -#: spyderlib/plugins/editor.py:1243 spyderlib/spyder.py:555 -msgid "&Edit" -msgstr "" - -#: spyderlib/plugins/editor.py:1244 spyderlib/spyder.py:560 -msgid "&Search" -msgstr "" - -#: spyderlib/plugins/editor.py:1245 spyderlib/spyder.py:565 -msgid "Sour&ce" -msgstr "" - -#: spyderlib/plugins/editor.py:1247 spyderlib/spyder.py:583 -msgid "&Tools" -msgstr "" - -#: spyderlib/plugins/editor.py:1248 -msgid "?" -msgstr "" - -#: spyderlib/plugins/editor.py:1469 -msgid "Spyder Editor" -msgstr "" - -#: spyderlib/plugins/editor.py:1470 -msgid "This is a temporary script file." -msgstr "" - -#: spyderlib/plugins/editor.py:1536 -msgid "untitled" -msgstr "" - -#: spyderlib/plugins/editor.py:1607 -msgid "Maximum number of recent files" -msgstr "" - -#: spyderlib/plugins/editor.py:1729 -msgid "Printing..." -msgstr "" - -#: spyderlib/plugins/explorer.py:45 -msgid "File explorer" -msgstr "" - -#: spyderlib/plugins/explorer.py:58 spyderlib/plugins/inspector.py:373 -#: spyderlib/plugins/projectexplorer.py:57 -msgid "Set font style" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:46 -msgid "Interactive data plotting in the consoles" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:53 -#: spyderlib/plugins/externalconsole.py:1066 -#: spyderlib/plugins/inspector.py:403 spyderlib/plugins/runconfig.py:178 -#: spyderlib/plugins/runconfig.py:447 -#: spyderlib/widgets/externalshell/baseshell.py:106 -#: spyderlib/widgets/ipython.py:509 -msgid "Console" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:69 -msgid "One tab per script" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:70 -#: spyderlib/widgets/externalshell/baseshell.py:171 -msgid "Show elapsed time" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:71 spyderlib/widgets/explorer.py:988 -msgid "Show icons and text" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:83 -msgid "Buffer: " -msgstr "" - -#: spyderlib/plugins/externalconsole.py:83 -#: spyderlib/plugins/ipythonconsole.py:201 -msgid " lines" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:88 -msgid "Merge process standard output/error channels" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:90 -msgid "" -"Merging the output channels of the process means that\n" -"the standard error won't be written in red anymore,\n" -"but this has the effect of speeding up display." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:94 -msgid "Colorize standard error channel using ANSI escape codes" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:96 -msgid "" -"This method is the only way to have colorized standard\n" -"error channel when the output channels have been merged." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:114 -#: spyderlib/plugins/ipythonconsole.py:188 -#: spyderlib/widgets/arrayeditor.py:460 -#: spyderlib/widgets/dataframeeditor.py:515 -msgid "Background color" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:115 -msgid "This option will be applied the next time a Python console or a terminal is opened." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:118 -msgid "Light background (white color)" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:143 -msgid "User Module Reloader (UMR)" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:144 -msgid "" -"UMR forces Python to reload modules which were imported when executing a \n" -"script in the external console with the 'runfile' function." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:147 -msgid "Enable UMR" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:148 -msgid "This option will enable the User Module Reloader (UMR) in Python/IPython consoles. UMR forces Python to reload deeply modules during import when running a Python script using the Spyder's builtin function runfile.

    1. UMR may require to restart the console in which it will be called (otherwise only newly imported modules will be reloaded when executing scripts).

    2. If errors occur when re-running a PyQt-based program, please check that the Qt objects are properly destroyed (e.g. you may have to use the attribute Qt.WA_DeleteOnClose on your main window, using the setAttribute method)" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:164 -msgid "Show reloaded modules list" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:165 -msgid "Please note that these changes will be applied only to new consoles" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:169 -msgid "Set UMR excluded (not reloaded) modules" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:181 -msgid "Python executable" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:183 -msgid "Select the Python interpreter executable binary in which Spyder will run scripts:" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:186 -msgid "Default (i.e. the same as Spyder's)" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:190 -msgid "Use the following Python interpreter:" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:194 -msgid "Executables" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:214 -msgid "PYTHONSTARTUP replacement" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:216 -msgid "" -"This option will override the PYTHONSTARTUP environment variable which\n" -"defines the script to be executed during the Python console startup." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:221 -msgid "Default PYTHONSTARTUP script" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:225 -msgid "Use the following startup script:" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:244 -msgid "Monitor" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:245 -msgid "The monitor provides introspection features to console: code completion, calltips and variable explorer. Because it relies on several modules, disabling the monitor may be useful to accelerate console startup." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:252 -msgid "Enable monitor" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:264 -msgid "Default library" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:266 -msgid "Qt (PyQt/PySide)" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:268 -msgid "Qt-Python bindings library selection:" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:270 -msgid "This option will act on
    libraries such as Matplotlib, guidata or ETS" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:291 -msgid "PyQt" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:293 -msgid "API selection for QString and QVariant objects:" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "API #1" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "API #2" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:294 -msgid "Default API" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:296 -msgid "PyQt API #1 is the default
    API for Python 2. PyQt API #2 is the default API for Python 3 and is compatible with PySide." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:300 -msgid "Ignore API change errors (sip.setapi)" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:302 -msgid "Enabling this option will ignore
    errors when changing PyQt API. As PyQt does not support dynamic API changes, it is strongly recommended to use this feature wisely, e.g. for debugging purpose." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:321 -msgid "Matplotlib" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:323 -msgid "GUI backend:" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:325 -msgid "Set the GUI toolkit used by
    Matplotlib to show figures (default: Qt4Agg)" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:344 -msgid "Enthought Tool Suite" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:345 -msgid "Enthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical user interfaces." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:349 -msgid "ETS_TOOLKIT:" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:369 -msgid "External modules" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:426 -#: spyderlib/plugins/externalconsole.py:666 -#: spyderlib/plugins/ipythonconsole.py:113 -#: spyderlib/plugins/ipythonconsole.py:808 spyderlib/spyder.py:1331 -#: spyderlib/spyder.py:1349 spyderlib/utils/environ.py:94 -#: spyderlib/utils/environ.py:107 spyderlib/widgets/dicteditor.py:449 -msgid "Warning" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:427 -msgid "You selected a Python %d interpreter for the console but Spyder is running on Python %d!.

    Although this is possible, we recommend you to install and run Spyder directly with your selected interpreter, to avoid seeing false warnings and errors due to the incompatible syntax between these two Python versions." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:590 -msgid "Trying to kill a kernel?" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:591 -msgid "You can't close this kernel because it has one or more consoles connected to it.

    You need to close them instead or you can kill the kernel using the second button from right to left." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:667 -msgid "No Python console is currently selected to run %s.

    Please select or open a new Python console and try again." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:748 -msgid "" -"%s is already running in a separate process.\n" -"Do you want to kill the process before starting a new one?" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:917 -msgid "Kernel" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:929 -msgid "Either:
    1. Your IPython frontend and kernel versions are incompatible or
    2. You don't have IPython installed in your external interpreter.
    In any case, we're sorry but we can't create a console for you." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:953 -msgid "Command Window" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:955 -msgid "Terminal" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1008 -msgid "Kernel %s" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1088 -msgid "Open a &Python console" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1091 -msgid "Open &command prompt" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1092 -msgid "Open a Windows command prompt" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1094 -msgid "Open a &terminal" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1095 -msgid "Open a terminal window" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1263 -msgid "Open an IPython console" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1264 -msgid "The console monitor was disabled: the IPython kernel will be started as expected, but an IPython console will have to be connected manually to the kernel." -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1294 -#: spyderlib/plugins/externalconsole.py:1307 -#: spyderlib/plugins/externalconsole.py:1311 -msgid "UMR" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1295 -msgid "" -"UMR excluded modules:\n" -"(example: guidata, guiqwt)" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1308 -msgid "" -"The following modules are not installed on your machine:\n" -"%s" -msgstr "" - -#: spyderlib/plugins/externalconsole.py:1312 -msgid "Please note that these changes will be applied only to new Python/IPython consoles" -msgstr "" - -#: spyderlib/plugins/findinfiles.py:90 spyderlib/widgets/findinfiles.py:691 -msgid "Find in files" -msgstr "" - -#: spyderlib/plugins/findinfiles.py:114 -msgid "&Find in files" -msgstr "" - -#: spyderlib/plugins/findinfiles.py:117 -msgid "Search text in multiple files" -msgstr "" - -#: spyderlib/plugins/history.py:36 -msgid "Settings" -msgstr "" - -#: spyderlib/plugins/history.py:38 -msgid " entries" -msgstr "" - -#: spyderlib/plugins/history.py:38 -msgid "History depth: " -msgstr "" - -#: spyderlib/plugins/history.py:45 -msgid "Scroll automatically to last entry" -msgstr "" - -#: spyderlib/plugins/history.py:113 spyderlib/plugins/inspector.py:458 -#: spyderlib/widgets/editor.py:540 spyderlib/widgets/explorer.py:1018 -#: spyderlib/widgets/externalshell/baseshell.py:151 -#: spyderlib/widgets/externalshell/namespacebrowser.py:226 -#: spyderlib/widgets/ipython.py:556 -msgid "Options" -msgstr "" - -#: spyderlib/plugins/history.py:133 -msgid "History log" -msgstr "" - -#: spyderlib/plugins/history.py:160 -msgid "History..." -msgstr "" - -#: spyderlib/plugins/history.py:162 -msgid "Set history maximum entries" -msgstr "" - -#: spyderlib/plugins/history.py:272 -msgid "History" -msgstr "" - -#: spyderlib/plugins/history.py:273 -msgid "Maximum entries" -msgstr "" - -#: spyderlib/plugins/inspector.py:56 -msgid "Rich text help on the Object Inspector" -msgstr "" - -#: spyderlib/plugins/inspector.py:120 -msgid "Plain text font style" -msgstr "" - -#: spyderlib/plugins/inspector.py:123 -msgid "Rich text font style" -msgstr "" - -#: spyderlib/plugins/inspector.py:126 -msgid "Automatic connections" -msgstr "" - -#: spyderlib/plugins/inspector.py:127 -msgid "The Object Inspector can automatically show an object's help information after a left parenthesis is written next to it. Below you can decide to which plugin you want to connect it to turn on this feature." -msgstr "" - -#: spyderlib/plugins/inspector.py:139 -msgid "" -"This feature requires the Rope or Jedi libraries.\n" -"It seems you don't have either installed." -msgstr "" - -#: spyderlib/plugins/inspector.py:142 -msgid "Python Console" -msgstr "" - -#: spyderlib/plugins/inspector.py:144 -msgid "IPython Console" -msgstr "" - -#: spyderlib/plugins/inspector.py:156 -msgid "Additional features" -msgstr "" - -#: spyderlib/plugins/inspector.py:157 -msgid "Render mathematical equations" -msgstr "" - -#: spyderlib/plugins/inspector.py:163 -msgid "This feature requires Sphinx 1.1 or superior." -msgstr "" - -#: spyderlib/plugins/inspector.py:165 -msgid "Sphinx %s is currently installed." -msgstr "" - -#: spyderlib/plugins/inspector.py:357 -msgid "No further documentation available" -msgstr "" - -#: spyderlib/plugins/inspector.py:396 -msgid "Source" -msgstr "" - -#: spyderlib/plugins/inspector.py:412 spyderlib/widgets/dicteditor.py:173 -msgid "Object" -msgstr "" - -#: spyderlib/plugins/inspector.py:428 -msgid "Plain Text" -msgstr "" - -#: spyderlib/plugins/inspector.py:432 -msgid "Show Source" -msgstr "" - -#: spyderlib/plugins/inspector.py:436 -msgid "Rich Text" -msgstr "" - -#: spyderlib/plugins/inspector.py:446 -msgid "Automatic import" -msgstr "" - -#: spyderlib/plugins/inspector.py:506 spyderlib/plugins/inspector.py:954 -msgid "Object inspector" -msgstr "" - -#: spyderlib/plugins/inspector.py:725 -msgid "Here you can get help of any object by pressing %s in front of it, either on the Editor or the Console.%sHelp can also be shown automatically after writing a left parenthesis next to an object. You can activate this behavior in %s." -msgstr "" - -#: spyderlib/plugins/inspector.py:731 -msgid "Preferences > Object Inspector" -msgstr "" - -#: spyderlib/plugins/inspector.py:733 -msgid "Usage" -msgstr "" - -#: spyderlib/plugins/inspector.py:734 -msgid "New to Spyder? Read our" -msgstr "" - -#: spyderlib/plugins/inspector.py:735 -msgid "tutorial" -msgstr "" - -#: spyderlib/plugins/inspector.py:742 -msgid "Please consider installing Sphinx to get documentation rendered in rich text." -msgstr "" - -#: spyderlib/plugins/inspector.py:913 -msgid "Lock" -msgstr "" - -#: spyderlib/plugins/inspector.py:913 -msgid "Unlock" -msgstr "" - -#: spyderlib/plugins/inspector.py:955 -msgid "The following error occured when calling Sphinx %s.
    Incompatible Sphinx version or doc string decoding failed.

    Error message:
    %s" -msgstr "" - -#: spyderlib/plugins/inspector.py:999 -msgid "No source code available." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:61 -msgid "Symbolic mathematics in the IPython Console" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:110 -msgid "The authenticity of host %s can't be established. Are you sure you want to continue connecting?" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:122 -msgid "The authenticity of the host can't be established" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:129 -msgid "Tunnel '%s' failed to start" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:134 -msgid "Could not connect to remote host" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:150 -#: spyderlib/plugins/ipythonconsole.py:665 -msgid "IPython console" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:162 -msgid "Display initial banner" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:163 -msgid "" -"This option lets you hide the message shown at\n" -"the top of the console when it's opened." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:165 -msgid "Use a completion widget" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:167 -msgid "Use a widget instead of plain text output for tab completion" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:169 -msgid "Use a pager to display additional text inside the console" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:171 -msgid "" -"Useful if you don't want to fill the console with long help or completion texts.\n" -"Note: Use the Q key to get out of the pager." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:176 -msgid "Ask for confirmation before closing" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:189 -msgid "Light background" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:191 -msgid "Dark background" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:201 -msgid "Buffer: " -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:203 -msgid "" -"Set the maximum number of lines of text shown in the\n" -"console before truncation. Specifying -1 disables it\n" -"(not recommended!)" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:212 -msgid "Support for graphics (Matplotlib)" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:213 -msgid "Activate support" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:214 -msgid "Automatically load Pylab and NumPy modules" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:217 -msgid "" -"This lets you load graphics support without importing \n" -"the commands to do plots. Useful to work with other\n" -"plotting libraries different to Matplotlib or to develop \n" -"GUIs with Spyder." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:236 -msgid "" -"This feature requires the Matplotlib library.\n" -"It seems you don't have it installed." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:241 -msgid "Inline" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:242 -msgid "Automatic" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:243 -msgid "Graphics backend" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:244 -msgid "Decide how graphics are going to be displayed in the console. If unsure, please select %s to put graphics inside the console or %s to interact with them (through zooming and panning) in a separate window." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:264 -msgid "Backend:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:266 -msgid "This option will be applied the next time a console is opened." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:278 -msgid "Inline backend" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:279 -msgid "Decide how to render the figures created by this backend" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:283 -msgid "Format:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:286 -msgid "Resolution:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:286 -msgid "dpi" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:288 -msgid "Only used when the format is PNG. Default is 72" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:291 -msgid "Width:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:291 -#: spyderlib/plugins/ipythonconsole.py:295 -msgid "inches" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:293 -msgid "Default is 6" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:295 -msgid "Height:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:297 -msgid "Default is 4" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:312 -msgid "Run code" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:313 -msgid "You can run several lines of code when a console is started. Please introduce each one separated by commas, for example:
    import os, import sys" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:319 -msgid "Lines:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:328 -msgid "Run a file" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:329 -msgid "You can also run a whole file at startup instead of just some lines (This is similar to have a PYTHONSTARTUP file)." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:333 -msgid "Use the following file:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:348 -msgid "Greedy completion" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:349 -msgid "Enable Tab completion on elements of lists, results of function calls, etc, without assigning them to a variable.
    For example, you can get completions on things like li[0].<Tab> or ins.meth().<Tab>" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:357 -msgid "Use the greedy completer" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:368 -msgid "Autocall" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:369 -msgid "Autocall makes IPython automatically call any callable object even if you didn't type explicit parentheses.
    For example, if you type str 43 it becomes str(43) automatically." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:376 -msgid "Smart" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:377 -msgid "Full" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:378 -msgid "Off" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:380 -msgid "Autocall: " -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:381 -msgid "On %s mode, Autocall is not applied if there are no arguments after the callable. On %s mode, all callable objects are automatically called (even if no arguments are present)." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:393 -msgid "Symbolic Mathematics" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:394 -msgid "Perfom symbolic operations in the console (e.g. integrals, derivatives, vector calculus, etc) and get the outputs in a beautifully printed style." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:399 -msgid "Use symbolic math" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:400 -msgid "This option loads the Sympy library to work with.
    Please refer to its documentation to learn how to use it." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:413 -msgid "" -"This feature requires the Sympy library.\n" -"It seems you don't have it installed." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:418 -msgid "Prompts" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:419 -msgid "Modify how Input and Output prompts are shown in the console." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:422 -msgid "Input prompt:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:424 -msgid "Default is
    In [<span class=\"in-prompt-number\">%i</span>]:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:428 -msgid "Output prompt:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:430 -msgid "Default is
    Out[<span class=\"out-prompt-number\">%i</span>]:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:446 -msgid "Graphics" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:448 -#: spyderlib/plugins/workingdirectory.py:42 -msgid "Startup" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:450 -msgid "Advanced Settings" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:462 -#: spyderlib/plugins/ipythonconsole.py:725 -msgid "Connect to an existing kernel" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:464 -msgid "Please enter the connection info of the kernel you want to connect to. For that you can either select its JSON connection file using the Browse button, or write directly its id, in case it's a local kernel (for example kernel-3764.json or just 3764)." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:475 -msgid "Connection info:" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:477 -msgid "Path to connection file or kernel id" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:479 -#: spyderlib/plugins/ipythonconsole.py:497 -msgid "Browse" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:489 -msgid "This is a remote kernel" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:493 -msgid "username@hostname:port" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:496 -msgid "Path to ssh key file" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:505 -msgid "Password or ssh key passphrase" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:509 -msgid "Host name" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:510 -msgid "Ssh key" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:511 -msgid "Password" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:540 -msgid "Open IPython connection file" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:546 -msgid "Select ssh key" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:713 -msgid "Open an &IPython console" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:716 -msgid "Use %s+T when the console is selected to open a new one" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:719 -msgid "Open a new console" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:726 -msgid "Open a new IPython console connected to an existing kernel" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:809 -msgid "No IPython console is currently available to run %s.

    Please open a new one and try again." -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:950 -msgid "Do you want to close all other consoles connected to the same kernel as this one?" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:1032 -msgid "Connection error" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:1033 -msgid "" -"Could not open ssh tunnel. The error was:\n" -"\n" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:1069 -msgid "IPython" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:1070 -msgid "Unable to connect to IPython %s" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:1121 -msgid "Are you sure you want to restart the kernel?" -msgstr "" - -#: spyderlib/plugins/ipythonconsole.py:1123 -msgid "Restart kernel?" -msgstr "" - -#: spyderlib/plugins/onlinehelp.py:67 -msgid "Online help" -msgstr "" - -#: spyderlib/plugins/outlineexplorer.py:47 -#: spyderlib/widgets/editortools.py:194 -msgid "Outline" -msgstr "" - -#: spyderlib/plugins/projectexplorer.py:41 -#: spyderlib/widgets/projectexplorer.py:1137 -#: spyderlib/widgets/projectexplorer.py:1151 -msgid "Project explorer" -msgstr "" - -#: spyderlib/plugins/projectexplorer.py:52 -#: spyderlib/widgets/projectexplorer.py:545 -msgid "New project..." -msgstr "" - -#: spyderlib/plugins/runconfig.py:28 -msgid "Execute in current Python or IPython console" -msgstr "" - -#: spyderlib/plugins/runconfig.py:29 -msgid "Execute in a new dedicated Python console" -msgstr "" - -#: spyderlib/plugins/runconfig.py:30 -msgid "Execute in an external System terminal" -msgstr "" - -#: spyderlib/plugins/runconfig.py:40 -msgid "Always show %s on a first file run" -msgstr "" - -#: spyderlib/plugins/runconfig.py:153 -msgid "General settings" -msgstr "" - -#: spyderlib/plugins/runconfig.py:156 spyderlib/plugins/runconfig.py:201 -msgid "Command line options:" -msgstr "" - -#: spyderlib/plugins/runconfig.py:163 -msgid "Working directory:" -msgstr "" - -#: spyderlib/plugins/runconfig.py:189 -msgid "Dedicated Python console" -msgstr "" - -#: spyderlib/plugins/runconfig.py:194 -msgid "Interact with the Python console after execution" -msgstr "" - -#: spyderlib/plugins/runconfig.py:198 -msgid "Show warning when killing running process" -msgstr "" - -#: spyderlib/plugins/runconfig.py:207 -msgid "-u is added to the other options you set here" -msgstr "" - -#: spyderlib/plugins/runconfig.py:218 -msgid "this dialog" -msgstr "" - -#: spyderlib/plugins/runconfig.py:276 -msgid "Run configuration" -msgstr "" - -#: spyderlib/plugins/runconfig.py:277 -msgid "The following working directory is not valid:
    %s" -msgstr "" - -#: spyderlib/plugins/runconfig.py:353 -msgid "Run settings for %s" -msgstr "" - -#: spyderlib/plugins/runconfig.py:384 -msgid "Select a run configuration:" -msgstr "" - -#: spyderlib/plugins/runconfig.py:414 spyderlib/plugins/runconfig.py:439 -msgid "Run Settings" -msgstr "" - -#: spyderlib/plugins/runconfig.py:441 -msgid "The following are the default %s. These options may be overriden using the %s dialog box (see the %s menu)" -msgstr "" - -#: spyderlib/plugins/runconfig.py:465 -#: spyderlib/widgets/externalshell/pythonshell.py:297 -msgid "Working directory" -msgstr "" - -#: spyderlib/plugins/runconfig.py:467 -msgid "Default working directory is:" -msgstr "" - -#: spyderlib/plugins/runconfig.py:469 -msgid "the script directory" -msgstr "" - -#: spyderlib/plugins/runconfig.py:472 spyderlib/plugins/workingdirectory.py:54 -msgid "the following directory:" -msgstr "" - -#: spyderlib/plugins/runconfig.py:491 -msgid "Run Settings dialog" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:178 -msgid "Context" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:180 spyderlib/widgets/dicteditor.py:158 -msgid "Name" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:182 -msgid "Mod1" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:184 -msgid "Mod2" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:186 -msgid "Mod3" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:188 spyderlib/widgets/dicteditor.py:169 -msgid "Key" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:321 -msgid "Conflicts" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:322 -msgid "The following conflicts have been detected:" -msgstr "" - -#: spyderlib/plugins/shortcuts.py:334 -msgid "Keyboard shortcuts" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:24 -msgid "Autorefresh" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:25 -msgid "Enable autorefresh" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:27 -msgid "Refresh interval: " -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:28 -msgid " ms" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:31 -msgid "Filter" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:33 -#: spyderlib/widgets/externalshell/namespacebrowser.py:196 -msgid "Exclude private references" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:34 -#: spyderlib/widgets/externalshell/namespacebrowser.py:211 -msgid "Exclude capitalized references" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:35 -#: spyderlib/widgets/externalshell/namespacebrowser.py:204 -msgid "Exclude all-uppercase references" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:36 -#: spyderlib/widgets/externalshell/namespacebrowser.py:219 -msgid "Exclude unsupported data types" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:42 -#: spyderlib/widgets/dicteditor.py:702 -msgid "Truncate values" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:44 -#: spyderlib/widgets/dicteditor.py:706 -msgid "Show arrays min/max" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:46 -msgid "Edit data in the remote process" -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:47 -msgid "" -"Editors are opened in the remote process for NumPy arrays, PIL images, lists, tuples and dictionaries.\n" -"This avoids transfering large amount of data between the remote process and Spyder (through the socket)." -msgstr "" - -#: spyderlib/plugins/variableexplorer.py:158 -msgid "Variable explorer" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:35 -msgid "The global working directory is the working directory for newly opened consoles (Python/IPython consoles and terminals), for the file explorer, for the find in files plugin and for new files created in the editor." -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:44 -msgid "At startup, the global working directory is:" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:48 -msgid "the same as in last session" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:50 -msgid "At startup, Spyder will restore the global directory from last session" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:56 -msgid "At startup, the global working directory will be the specified path" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:70 -msgid "Files are opened from:" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:74 -#: spyderlib/plugins/workingdirectory.py:87 -msgid "the current file directory" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:78 -#: spyderlib/plugins/workingdirectory.py:91 -msgid "the global working directory" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:83 -msgid "Files are created in:" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:97 -msgid "Change to file base directory" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:99 -msgid "When opening a file" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:101 -msgid "When saving a file" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:160 -msgid "Back" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:168 -#: spyderlib/widgets/explorer.py:1004 spyderlib/widgets/importwizard.py:523 -msgid "Next" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:181 -msgid "" -"This is the working directory for newly\n" -"opened consoles (Python/IPython consoles and\n" -"terminals), for the file explorer, for the\n" -"find in files plugin and for new files\n" -"created in the editor" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:207 -msgid "Browse a working directory" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:213 -msgid "Set as current console's working directory" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:221 -msgid "Change to parent directory" -msgstr "" - -#: spyderlib/plugins/workingdirectory.py:228 -msgid "Global working directory" -msgstr "" - -#: spyderlib/spyder.py:120 -msgid "Initializing..." -msgstr "" - -#: spyderlib/spyder.py:244 -msgid "Numpy and Scipy documentation" -msgstr "" - -#: spyderlib/spyder.py:246 spyderlib/spyder.py:949 -msgid "Matplotlib documentation" -msgstr "" - -#: spyderlib/spyder.py:249 -msgid "PyQt4 Reference Guide" -msgstr "" - -#: spyderlib/spyder.py:252 -msgid "PyQt4 API Reference" -msgstr "" - -#: spyderlib/spyder.py:254 -msgid "Python(x,y)" -msgstr "" - -#: spyderlib/spyder.py:256 -msgid "WinPython" -msgstr "" - -#: spyderlib/spyder.py:293 -msgid "Reload last session" -msgstr "" - -#: spyderlib/spyder.py:297 -msgid "Load session..." -msgstr "" - -#: spyderlib/spyder.py:300 -msgid "Load Spyder session" -msgstr "" - -#: spyderlib/spyder.py:302 -msgid "Save session and quit..." -msgstr "" - -#: spyderlib/spyder.py:305 -msgid "Save current session and quit application" -msgstr "" - -#: spyderlib/spyder.py:483 -msgid "Close current pane" -msgstr "" - -#: spyderlib/spyder.py:489 -msgid "&Find text" -msgstr "" - -#: spyderlib/spyder.py:494 -msgid "Find &next" -msgstr "" - -#: spyderlib/spyder.py:500 -msgid "Find &previous" -msgstr "" - -#: spyderlib/spyder.py:505 -msgid "&Replace text" -msgstr "" - -#: spyderlib/spyder.py:520 spyderlib/widgets/sourcecode/codeeditor.py:2268 -msgid "Undo" -msgstr "" - -#: spyderlib/spyder.py:522 spyderlib/widgets/sourcecode/codeeditor.py:2271 -msgid "Redo" -msgstr "" - -#: spyderlib/spyder.py:523 spyderlib/widgets/arrayeditor.py:392 -#: spyderlib/widgets/dataframeeditor.py:418 -#: spyderlib/widgets/dicteditor.py:674 spyderlib/widgets/shell.py:118 -#: spyderlib/widgets/sourcecode/codeeditor.py:2277 -msgid "Copy" -msgstr "" - -#: spyderlib/spyder.py:525 spyderlib/widgets/shell.py:114 -#: spyderlib/widgets/sourcecode/codeeditor.py:2274 -msgid "Cut" -msgstr "" - -#: spyderlib/spyder.py:526 spyderlib/widgets/dicteditor.py:671 -#: spyderlib/widgets/shell.py:122 -#: spyderlib/widgets/sourcecode/codeeditor.py:2280 -msgid "Paste" -msgstr "" - -#: spyderlib/spyder.py:528 spyderlib/widgets/explorer.py:461 -#: spyderlib/widgets/projectexplorer.py:1003 spyderlib/widgets/shell.py:131 -#: spyderlib/widgets/sourcecode/codeeditor.py:2283 -msgid "Delete" -msgstr "" - -#: spyderlib/spyder.py:531 spyderlib/widgets/shell.py:135 -#: spyderlib/widgets/sourcecode/codeeditor.py:2287 -msgid "Select All" -msgstr "" - -#: spyderlib/spyder.py:580 -msgid "C&onsoles" -msgstr "" - -#: spyderlib/spyder.py:586 -msgid "&View" -msgstr "" - -#: spyderlib/spyder.py:589 -msgid "&Help" -msgstr "" - -#: spyderlib/spyder.py:594 -msgid "Welcome to Spyder!" -msgstr "" - -#: spyderlib/spyder.py:599 -msgid "Pre&ferences" -msgstr "" - -#: spyderlib/spyder.py:606 spyderlib/widgets/pathmanager.py:45 -#: spyderlib/widgets/projectexplorer.py:594 -msgid "PYTHONPATH manager" -msgstr "" - -#: spyderlib/spyder.py:609 -msgid "Python Path Manager" -msgstr "" - -#: spyderlib/spyder.py:612 -msgid "Update module names list" -msgstr "" - -#: spyderlib/spyder.py:614 -msgid "Refresh list of module names available in PYTHONPATH" -msgstr "" - -#: spyderlib/spyder.py:619 -msgid "Current user environment variables..." -msgstr "" - -#: spyderlib/spyder.py:621 -msgid "Show and edit current user environment variables in Windows registry (i.e. for all sessions)" -msgstr "" - -#: spyderlib/spyder.py:629 spyderlib/spyder.py:1043 -msgid "External Tools" -msgstr "" - -#: spyderlib/spyder.py:633 -msgid "Python(x,y) launcher" -msgstr "" - -#: spyderlib/spyder.py:640 -msgid "WinPython control panel" -msgstr "" - -#: spyderlib/spyder.py:649 -msgid "Qt Designer" -msgstr "" - -#: spyderlib/spyder.py:654 -msgid "Qt Linguist" -msgstr "" - -#: spyderlib/spyder.py:660 -msgid "Qt examples" -msgstr "" - -#: spyderlib/spyder.py:678 -msgid "guidata examples" -msgstr "" - -#: spyderlib/spyder.py:686 -msgid "guiqwt examples" -msgstr "" - -#: spyderlib/spyder.py:691 -msgid "Sift" -msgstr "" - -#: spyderlib/spyder.py:699 -msgid "ViTables" -msgstr "" - -#: spyderlib/spyder.py:713 -msgid "Fullscreen mode" -msgstr "" - -#: spyderlib/spyder.py:725 -msgid "Main toolbar" -msgstr "" - -#: spyderlib/spyder.py:734 -msgid "" -"Spyder Internal Console\n" -"\n" -"This console is used to report application\n" -"internal errors and to inspect Spyder\n" -"internals with the following commands:\n" -" spy.app, spy.window, dir(spy)\n" -"\n" -"Please don't use it to run your code\n" -"\n" -msgstr "" - -#: spyderlib/spyder.py:751 -msgid "Loading object inspector..." -msgstr "" - -#: spyderlib/spyder.py:758 -msgid "Loading outline explorer..." -msgstr "" - -#: spyderlib/spyder.py:766 -msgid "Loading editor..." -msgstr "" - -#: spyderlib/spyder.py:791 -msgid "Loading file explorer..." -msgstr "" - -#: spyderlib/spyder.py:798 -msgid "Loading history plugin..." -msgstr "" - -#: spyderlib/spyder.py:809 -msgid "Loading online help..." -msgstr "" - -#: spyderlib/spyder.py:815 -msgid "Loading project explorer..." -msgstr "" - -#: spyderlib/spyder.py:826 -msgid "Loading external console..." -msgstr "" - -#: spyderlib/spyder.py:835 -msgid "Loading namespace browser..." -msgstr "" - -#: spyderlib/spyder.py:842 -msgid "Loading IPython console..." -msgstr "" - -#: spyderlib/spyder.py:853 -msgid "Setting up main window..." -msgstr "" - -#: spyderlib/spyder.py:856 -msgid "Optional dependencies..." -msgstr "" - -#: spyderlib/spyder.py:860 -msgid "Report issue..." -msgstr "" - -#: spyderlib/spyder.py:864 -msgid "Spyder support..." -msgstr "" - -#: spyderlib/spyder.py:887 -msgid "Spyder documentation" -msgstr "" - -#: spyderlib/spyder.py:889 -msgid "Spyder tutorial" -msgstr "" - -#: spyderlib/spyder.py:896 -msgid "Python documentation" -msgstr "" - -#: spyderlib/spyder.py:902 spyderlib/spyder.py:941 -msgid "IPython documentation" -msgstr "" - -#: spyderlib/spyder.py:903 -msgid "Intro to IPython" -msgstr "" - -#: spyderlib/spyder.py:905 -msgid "Quick reference" -msgstr "" - -#: spyderlib/spyder.py:907 -msgid "Console help" -msgstr "" - -#: spyderlib/spyder.py:939 -msgid "Python(x,y) documentation folder" -msgstr "" - -#: spyderlib/spyder.py:943 -msgid "guidata documentation" -msgstr "" - -#: spyderlib/spyder.py:946 -msgid "guiqwt documentation" -msgstr "" - -#: spyderlib/spyder.py:952 -msgid "NumPy documentation" -msgstr "" - -#: spyderlib/spyder.py:954 -msgid "NumPy reference guide" -msgstr "" - -#: spyderlib/spyder.py:956 -msgid "NumPy user guide" -msgstr "" - -#: spyderlib/spyder.py:958 -msgid "SciPy documentation" -msgstr "" - -#: spyderlib/spyder.py:965 -msgid "Installed Python modules" -msgstr "" - -#: spyderlib/spyder.py:969 -msgid "Online documentation" -msgstr "" - -#: spyderlib/spyder.py:979 -msgid "Qt documentation" -msgstr "" - -#: spyderlib/spyder.py:985 -msgid "About %s..." -msgstr "" - -#: spyderlib/spyder.py:1006 -msgid "Panes" -msgstr "" - -#: spyderlib/spyder.py:1007 -msgid "Toolbars" -msgstr "" - -#: spyderlib/spyder.py:1010 -msgid "Reset window layout" -msgstr "" - -#: spyderlib/spyder.py:1012 -msgid "Custom window layouts" -msgstr "" - -#: spyderlib/spyder.py:1018 -msgid "Switch to/from layout %d" -msgstr "" - -#: spyderlib/spyder.py:1023 -msgid "Set layout %d" -msgstr "" - -#: spyderlib/spyder.py:1031 -msgid "Attached console window (debugging)" -msgstr "" - -#: spyderlib/spyder.py:1332 -msgid "" -"Window layout will be reset to default settings: this affects window position, size and dockwidgets.\n" -"Do you want to continue?" -msgstr "" - -#: spyderlib/spyder.py:1350 -msgid "Quick switch layout #%d has not yet been defined." -msgstr "" - -#: spyderlib/spyder.py:1602 spyderlib/spyder.py:1603 -msgid "Maximize current pane" -msgstr "" - -#: spyderlib/spyder.py:1606 -msgid "Restore current pane" -msgstr "" - -#: spyderlib/spyder.py:1607 -msgid "Restore pane to its original size" -msgstr "" - -#: spyderlib/spyder.py:1686 -msgid "About %s" -msgstr "" - -#: spyderlib/spyder.py:1851 -msgid "Running an external system terminal is not supported on platform %s." -msgstr "" - -#: spyderlib/spyder.py:2066 -msgid "Open session" -msgstr "" - -#: spyderlib/spyder.py:2067 spyderlib/spyder.py:2078 -msgid "Spyder sessions" -msgstr "" - -#: spyderlib/spyder.py:2077 -msgid "Save session" -msgstr "" - -#: spyderlib/utils/codeanalysis.py:92 -msgid "Real-time code analysis on the Editor" -msgstr "" - -#: spyderlib/utils/codeanalysis.py:96 -msgid "Real-time code style analysis on the Editor" -msgstr "" - -#: spyderlib/utils/environ.py:95 -msgid "Module pywin32 was not found.
    Please restart this Windows session (not the computer) for changes to take effect." -msgstr "" - -#: spyderlib/utils/environ.py:108 -msgid "If you accept changes, this will modify the current user environment variables directly in Windows registry. Use it with precautions, at your own risks.

    Note that for changes to take effect, you will need to restart the parent process of this application (simply restart Spyder if you have executed it from a Windows shortcut, otherwise restart any application from which you may have executed it, like Python(x,y) Home for example)" -msgstr "" - -#: spyderlib/utils/inspector/sphinxify.py:209 -#: spyderlib/utils/inspector/sphinxify.py:219 -msgid "It was not possible to generate rich text help for this object.
    Please see it in plain text." -msgstr "" - -#: spyderlib/utils/introspection/jedi_plugin.py:32 -msgid "(Experimental) Editor's code completion, go-to-definition and help" -msgstr "" - -#: spyderlib/utils/introspection/rope_plugin.py:37 -msgid "Editor's code completion, go-to-definition and help" -msgstr "" - -#: spyderlib/utils/iofuncs.py:496 -msgid "Supported files" -msgstr "" - -#: spyderlib/utils/iofuncs.py:498 -msgid "All files (*.*)" -msgstr "" - -#: spyderlib/utils/iofuncs.py:508 -msgid "Spyder data files" -msgstr "" - -#: spyderlib/utils/iofuncs.py:510 spyderlib/widgets/dicteditor.py:1041 -msgid "NumPy arrays" -msgstr "" - -#: spyderlib/utils/iofuncs.py:511 -msgid "NumPy zip arrays" -msgstr "" - -#: spyderlib/utils/iofuncs.py:512 -msgid "Matlab files" -msgstr "" - -#: spyderlib/utils/iofuncs.py:513 -msgid "CSV text files" -msgstr "" - -#: spyderlib/utils/iofuncs.py:515 -msgid "JPEG images" -msgstr "" - -#: spyderlib/utils/iofuncs.py:516 -msgid "PNG images" -msgstr "" - -#: spyderlib/utils/iofuncs.py:517 -msgid "GIF images" -msgstr "" - -#: spyderlib/utils/iofuncs.py:518 -msgid "TIFF images" -msgstr "" - -#: spyderlib/utils/iofuncs.py:519 spyderlib/utils/iofuncs.py:520 -msgid "Pickle files" -msgstr "" - -#: spyderlib/utils/iofuncs.py:521 -msgid "JSON files" -msgstr "" - -#: spyderlib/utils/iofuncs.py:540 spyderlib/utils/iofuncs.py:547 -msgid "Unsupported file type '%s'" -msgstr "" - -#: spyderlib/utils/programs.py:176 -msgid "It was not possible to run this file in an external terminal" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:452 spyderlib/widgets/arrayeditor.py:485 -#: spyderlib/widgets/dataframeeditor.py:507 -#: spyderlib/widgets/dataframeeditor.py:549 -msgid "Format" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:457 -#: spyderlib/widgets/dataframeeditor.py:511 -msgid "Resize" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:486 -#: spyderlib/widgets/dataframeeditor.py:550 -msgid "Float formatting" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:493 -#: spyderlib/widgets/dataframeeditor.py:558 spyderlib/widgets/explorer.py:578 -#: spyderlib/widgets/explorer.py:681 -#: spyderlib/widgets/externalshell/pythonshell.py:537 -#: spyderlib/widgets/externalshell/systemshell.py:93 -msgid "Error" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:494 -#: spyderlib/widgets/dataframeeditor.py:559 -msgid "Format (%s) is incorrect" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:528 -msgid "Array is empty" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:531 -msgid "Arrays with more than 3 dimensions are not supported" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:535 -msgid "The 'xlabels' argument length do no match array column number" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:539 -msgid "The 'ylabels' argument length do no match array row number" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:546 -msgid "%s arrays" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:547 -msgid "%s are currently not supported" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:554 -msgid "NumPy array" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:556 spyderlib/widgets/arrayeditor.py:713 -msgid "Array editor" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:558 -msgid "read only" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:589 -msgid "Record array fields:" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Data" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Mask" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:601 -msgid "Masked data" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:614 -msgid "Axis:" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:619 -msgid "Index:" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:633 -msgid "Warning: changes are applied separately" -msgstr "" - -#: spyderlib/widgets/arrayeditor.py:634 -msgid "For performance reasons, changes applied to masked array won't be reflected in array's data (and vice-versa)." -msgstr "" - -#: spyderlib/widgets/browser.py:30 -#: spyderlib/widgets/sourcecode/codeeditor.py:2311 -msgid "Zoom out" -msgstr "" - -#: spyderlib/widgets/browser.py:33 -#: spyderlib/widgets/sourcecode/codeeditor.py:2308 -msgid "Zoom in" -msgstr "" - -#: spyderlib/widgets/browser.py:131 -msgid "Home" -msgstr "" - -#: spyderlib/widgets/browser.py:171 -msgid "Find text" -msgstr "" - -#: spyderlib/widgets/browser.py:190 -msgid "Address:" -msgstr "" - -#: spyderlib/widgets/browser.py:225 -msgid "Unable to load page" -msgstr "" - -#: spyderlib/widgets/comboboxes.py:117 -msgid "Press enter to validate this entry" -msgstr "" - -#: spyderlib/widgets/comboboxes.py:118 -msgid "This entry is incorrect" -msgstr "" - -#: spyderlib/widgets/comboboxes.py:171 -msgid "Press enter to validate this path" -msgstr "" - -#: spyderlib/widgets/comboboxes.py:172 -msgid "" -"This path is incorrect.\n" -"Enter a correct directory path,\n" -"then press enter to validate" -msgstr "" - -#: spyderlib/widgets/dataframeeditor.py:423 -msgid "To bool" -msgstr "" - -#: spyderlib/widgets/dataframeeditor.py:423 -msgid "To complex" -msgstr "" - -#: spyderlib/widgets/dataframeeditor.py:424 -msgid "To float" -msgstr "" - -#: spyderlib/widgets/dataframeeditor.py:424 -msgid "To int" -msgstr "" - -#: spyderlib/widgets/dataframeeditor.py:425 -msgid "To str" -msgstr "" - -#: spyderlib/widgets/dataframeeditor.py:489 -msgid "%s editor" -msgstr "" - -#: spyderlib/widgets/dataframeeditor.py:522 -msgid "Column min/max" -msgstr "" - -#: spyderlib/widgets/dependencies.py:60 -msgid " Required " -msgstr "" - -#: spyderlib/widgets/dependencies.py:60 -msgid "Module" -msgstr "" - -#: spyderlib/widgets/dependencies.py:61 -msgid " Installed " -msgstr "" - -#: spyderlib/widgets/dependencies.py:61 -msgid "Provided features" -msgstr "" - -#: spyderlib/widgets/dependencies.py:127 -msgid "Optional Dependencies" -msgstr "" - -#: spyderlib/widgets/dependencies.py:134 -msgid "Spyder depends on several Python modules to provide additional functionality for its plugins. The table below shows the required and installed versions (if any) of all of them.

    Although Spyder can work without any of these modules, it's strongly recommended that at least you try to install %s and %s to have a much better experience." -msgstr "" - -#: spyderlib/widgets/dependencies.py:149 -msgid "Copy to clipboard" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:156 -msgid "Index" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:161 -msgid "Tuple" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:164 -msgid "List" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:167 -msgid "Dictionary" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:175 -msgid "Attribute" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:177 -msgid "elements" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Size" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Type" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:355 -msgid "Value" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:450 -msgid "" -"Opening this variable can be slow\n" -"\n" -"Do you want to continue anyway?" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:458 spyderlib/widgets/dicteditor.py:607 -msgid "Edit item" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:459 -msgid "Unable to retrieve data.

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:608 -msgid "Unable to assign data to item.

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:669 -msgid "Resize rows to contents" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:677 spyderlib/widgets/explorer.py:236 -msgid "Edit" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:680 spyderlib/widgets/dicteditor.py:1012 -#: spyderlib/widgets/dicteditor.py:1028 -msgid "Plot" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:684 -msgid "Histogram" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:688 -msgid "Show image" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:692 spyderlib/widgets/dicteditor.py:1035 -msgid "Save array" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:696 spyderlib/widgets/dicteditor.py:976 -#: spyderlib/widgets/dicteditor.py:984 -msgid "Insert" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:699 spyderlib/widgets/dicteditor.py:929 -msgid "Remove" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:710 spyderlib/widgets/dicteditor.py:946 -#: spyderlib/widgets/explorer.py:524 spyderlib/widgets/explorer.py:532 -#: spyderlib/widgets/explorer.py:544 -msgid "Rename" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:713 -msgid "Duplicate" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:927 -msgid "Do you want to remove selected item?" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:928 -msgid "Do you want to remove all selected items?" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:946 spyderlib/widgets/dicteditor.py:976 -msgid "Key:" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:984 -msgid "Value:" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1000 -msgid "Import error" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1001 -msgid "Please install matplotlib or guiqwt." -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1013 -msgid "Unable to plot data.

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1029 -msgid "Unable to show image.

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1051 -msgid "Unable to save array

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1068 -msgid "Clipboard contents" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1082 -msgid "Import from clipboard" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1084 -msgid "Empty clipboard" -msgstr "" - -#: spyderlib/widgets/dicteditor.py:1085 -msgid "Nothing to be imported from clipboard." -msgstr "" - -#: spyderlib/widgets/dicteditorutils.py:59 -msgid "View and edit DataFrames and Series in the Variable Explorer" -msgstr "" - -#: spyderlib/widgets/editor.py:67 spyderlib/widgets/editor.py:417 -msgid "File list management" -msgstr "" - -#: spyderlib/widgets/editor.py:71 -msgid "Filter:" -msgstr "" - -#: spyderlib/widgets/editor.py:76 -msgid "(press Enter to edit file)" -msgstr "" - -#: spyderlib/widgets/editor.py:91 -msgid "&Edit file" -msgstr "" - -#: spyderlib/widgets/editor.py:100 -msgid "&Close file" -msgstr "" - -#: spyderlib/widgets/editor.py:108 -msgid "Hint: press Alt to show accelerators" -msgstr "" - -#: spyderlib/widgets/editor.py:420 -msgid "Copy path to clipboard" -msgstr "" - -#: spyderlib/widgets/editor.py:990 -msgid "Temporary file" -msgstr "" - -#: spyderlib/widgets/editor.py:1087 -msgid "New window" -msgstr "" - -#: spyderlib/widgets/editor.py:1088 -msgid "Create a new editor window" -msgstr "" - -#: spyderlib/widgets/editor.py:1091 -msgid "Split vertically" -msgstr "" - -#: spyderlib/widgets/editor.py:1093 -msgid "Split vertically this editor window" -msgstr "" - -#: spyderlib/widgets/editor.py:1095 -msgid "Split horizontally" -msgstr "" - -#: spyderlib/widgets/editor.py:1097 -msgid "Split horizontally this editor window" -msgstr "" - -#: spyderlib/widgets/editor.py:1099 -msgid "Close this panel" -msgstr "" - -#: spyderlib/widgets/editor.py:1237 -msgid "%s has been modified.
    Do you want to save changes?" -msgstr "" - -#: spyderlib/widgets/editor.py:1300 -msgid "Save" -msgstr "" - -#: spyderlib/widgets/editor.py:1301 -msgid "Unable to save script '%s'

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/editor.py:1323 -msgid "Save Python script" -msgstr "" - -#: spyderlib/widgets/editor.py:1539 -msgid "%s is unavailable (this file may have been removed, moved or renamed outside Spyder).
    Do you want to close it?" -msgstr "" - -#: spyderlib/widgets/editor.py:1559 -msgid "%s has been modified outside Spyder.
    Do you want to reload it and lose all your changes?" -msgstr "" - -#: spyderlib/widgets/editor.py:1655 -msgid "All changes to %s will be lost.
    Do you want to revert file from disk?" -msgstr "" - -#: spyderlib/widgets/editor.py:1811 -msgid "Loading %s..." -msgstr "" - -#: spyderlib/widgets/editor.py:1821 -msgid "%s contains mixed end-of-line characters.
    Spyder will fix this automatically." -msgstr "" - -#: spyderlib/widgets/editor.py:2192 -msgid "Close window" -msgstr "" - -#: spyderlib/widgets/editor.py:2194 -msgid "Close this window" -msgstr "" - -#: spyderlib/widgets/editortools.py:93 spyderlib/widgets/editortools.py:129 -msgid "Line %s" -msgstr "" - -#: spyderlib/widgets/editortools.py:98 -msgid "Class defined at line %s" -msgstr "" - -#: spyderlib/widgets/editortools.py:106 -msgid "Method defined at line %s" -msgstr "" - -#: spyderlib/widgets/editortools.py:116 -msgid "Function defined at line %s" -msgstr "" - -#: spyderlib/widgets/editortools.py:148 -msgid "Cell starts at line %s" -msgstr "" - -#: spyderlib/widgets/editortools.py:201 spyderlib/widgets/editortools.py:536 -msgid "Go to cursor position" -msgstr "" - -#: spyderlib/widgets/editortools.py:204 -msgid "Show absolute path" -msgstr "" - -#: spyderlib/widgets/editortools.py:207 spyderlib/widgets/explorer.py:177 -msgid "Show all files" -msgstr "" - -#: spyderlib/widgets/editortools.py:210 -msgid "Show special comments" -msgstr "" - -#: spyderlib/widgets/explorer.py:173 -msgid "Edit filename filters..." -msgstr "" - -#: spyderlib/widgets/explorer.py:186 -msgid "Edit filename filters" -msgstr "" - -#: spyderlib/widgets/explorer.py:187 -msgid "Name filters:" -msgstr "" - -#: spyderlib/widgets/explorer.py:205 -msgid "File..." -msgstr "" - -#: spyderlib/widgets/explorer.py:208 -msgid "Module..." -msgstr "" - -#: spyderlib/widgets/explorer.py:211 -msgid "Folder..." -msgstr "" - -#: spyderlib/widgets/explorer.py:215 -msgid "Package..." -msgstr "" - -#: spyderlib/widgets/explorer.py:238 -msgid "Move..." -msgstr "" - -#: spyderlib/widgets/explorer.py:241 -msgid "Delete..." -msgstr "" - -#: spyderlib/widgets/explorer.py:244 -msgid "Rename..." -msgstr "" - -#: spyderlib/widgets/explorer.py:247 -msgid "Open" -msgstr "" - -#: spyderlib/widgets/explorer.py:248 -#: spyderlib/widgets/sourcecode/codeeditor.py:2299 -msgid "Convert to Python script" -msgstr "" - -#: spyderlib/widgets/explorer.py:271 -msgid "Commit" -msgstr "" - -#: spyderlib/widgets/explorer.py:275 -msgid "Browse repository" -msgstr "" - -#: spyderlib/widgets/explorer.py:287 -msgid "Open command prompt here" -msgstr "" - -#: spyderlib/widgets/explorer.py:289 -msgid "Open terminal here" -msgstr "" - -#: spyderlib/widgets/explorer.py:294 -msgid "Open Python console here" -msgstr "" - -#: spyderlib/widgets/explorer.py:308 -msgid "New" -msgstr "" - -#: spyderlib/widgets/explorer.py:316 -msgid "Import" -msgstr "" - -#: spyderlib/widgets/explorer.py:462 -msgid "Do you really want to delete %s?" -msgstr "" - -#: spyderlib/widgets/explorer.py:482 -msgid "delete" -msgstr "" - -#: spyderlib/widgets/explorer.py:483 spyderlib/widgets/projectexplorer.py:815 -#: spyderlib/widgets/projectexplorer.py:822 -#: spyderlib/widgets/projectexplorer.py:1089 -#: spyderlib/widgets/projectexplorer.py:1173 -msgid "Project Explorer" -msgstr "" - -#: spyderlib/widgets/explorer.py:484 spyderlib/widgets/projectexplorer.py:762 -#: spyderlib/widgets/projectexplorer.py:1174 -msgid "Unable to %s %s

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/explorer.py:506 -#: spyderlib/widgets/sourcecode/codeeditor.py:1906 -msgid "Conversion error" -msgstr "" - -#: spyderlib/widgets/explorer.py:507 -#: spyderlib/widgets/sourcecode/codeeditor.py:1907 -msgid "" -"It was not possible to convert this notebook. The error is:\n" -"\n" -msgstr "" - -#: spyderlib/widgets/explorer.py:525 -msgid "New name:" -msgstr "" - -#: spyderlib/widgets/explorer.py:533 -msgid "Do you really want to rename %s and overwrite the existing file %s?" -msgstr "" - -#: spyderlib/widgets/explorer.py:545 -msgid "Unable to rename file %s

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/explorer.py:579 -msgid "Unable to move %s

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/explorer.py:597 -msgid "Unable to create folder %s

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/explorer.py:610 spyderlib/widgets/explorer.py:644 -msgid "Unable to create file %s

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/explorer.py:618 -msgid "New folder" -msgstr "" - -#: spyderlib/widgets/explorer.py:619 -msgid "Folder name:" -msgstr "" - -#: spyderlib/widgets/explorer.py:624 -msgid "New package" -msgstr "" - -#: spyderlib/widgets/explorer.py:625 -msgid "Package name:" -msgstr "" - -#: spyderlib/widgets/explorer.py:665 -msgid "New module" -msgstr "" - -#: spyderlib/widgets/explorer.py:678 -msgid "For %s support, please install one of the
    following tools:

    %s" -msgstr "" - -#: spyderlib/widgets/explorer.py:682 -msgid "Unable to find external program.

    %s" -msgstr "" - -#: spyderlib/widgets/explorer.py:882 -msgid "Show current directory only" -msgstr "" - -#: spyderlib/widgets/explorer.py:996 spyderlib/widgets/importwizard.py:518 -msgid "Previous" -msgstr "" - -#: spyderlib/widgets/explorer.py:1012 -msgid "Parent" -msgstr "" - -#: spyderlib/widgets/externalshell/baseshell.py:140 -msgid "Run again this program" -msgstr "" - -#: spyderlib/widgets/externalshell/baseshell.py:143 -msgid "Kill" -msgstr "" - -#: spyderlib/widgets/externalshell/baseshell.py:145 -msgid "Kills the current process, causing it to exit immediately" -msgstr "" - -#: spyderlib/widgets/externalshell/baseshell.py:213 -msgid "Running..." -msgstr "" - -#: spyderlib/widgets/externalshell/baseshell.py:220 -msgid "Terminated." -msgstr "" - -#: spyderlib/widgets/externalshell/baseshell.py:242 -#: spyderlib/widgets/ipython.py:342 spyderlib/widgets/ipython.py:359 -#: spyderlib/widgets/mixins.py:608 -msgid "Arguments" -msgstr "" - -#: spyderlib/widgets/externalshell/baseshell.py:243 -msgid "Command line arguments:" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:173 -msgid "Refresh" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:177 -msgid "Refresh periodically" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:181 -#: spyderlib/widgets/externalshell/namespacebrowser.py:446 -msgid "Import data" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:184 -#: spyderlib/widgets/externalshell/namespacebrowser.py:536 -#: spyderlib/widgets/externalshell/namespacebrowser.py:557 -msgid "Save data" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:189 -msgid "Save data as..." -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:197 -msgid "Exclude references which name starts with an underscore" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:205 -msgid "Exclude references which name is uppercase" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:212 -msgid "Exclude references which name starts with an uppercase character" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:220 -msgid "Exclude references to unsupported data types (i.e. which won't be handled/saved correctly)" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:342 -msgid "Object %s is not picklable" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:468 -msgid "Unsupported file extension '%s'

    Would you like to import it anyway (by selecting a known file format)?" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:476 -msgid "Open file as:" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:524 -msgid "Unable to load '%s'

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/externalshell/namespacebrowser.py:558 -msgid "Unable to save current workspace

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:269 -msgid "Variables" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:270 -msgid "Show/hide global variables explorer" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:274 -msgid "Terminate" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:275 -msgid "" -"Attempts to stop the process. The process\n" -"may not exit as a result of clicking this\n" -"button (it is given the chance to prompt\n" -"the user for any unsaved files, etc)." -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:288 -msgid "Interact" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:290 -msgid "Debug" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:292 -#: spyderlib/widgets/externalshell/pythonshell.py:355 -msgid "Arguments..." -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:299 -msgid "Set current working directory" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:301 -msgid "Environment variables" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:305 -msgid "Show sys.path contents" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:351 -msgid "Arguments: %s" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:353 -msgid "No argument" -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:534 -msgid "The kernel failed to start!! That's all we know... Please close this console and open a new one." -msgstr "" - -#: spyderlib/widgets/externalshell/pythonshell.py:538 -msgid "A Python console failed to start!" -msgstr "" - -#: spyderlib/widgets/externalshell/systemshell.py:94 -msgid "Process failed to start" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:155 -msgid "Unexpected error: see internal console" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:207 spyderlib/widgets/findinfiles.py:231 -#: spyderlib/widgets/findinfiles.py:278 -msgid "invalid regular expression" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:276 -msgid "permission denied errors were encountered" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:310 -msgid "Search pattern" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:313 spyderlib/widgets/findinfiles.py:347 -#: spyderlib/widgets/findinfiles.py:359 spyderlib/widgets/findreplace.py:82 -msgid "Regular expression" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:322 -msgid "Search" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:325 -msgid "Start search" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:328 spyderlib/widgets/ipython.py:543 -msgid "Stop" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:331 -msgid "Stop search" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:341 -msgid "Included filenames pattern" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:350 -msgid "Include:" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:353 -msgid "Excluded filenames pattern" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:362 -msgid "Exclude:" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:372 -msgid "PYTHONPATH" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:374 -msgid "Search in all directories listed in sys.path which are outside the Python installation directory" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:377 -msgid "Hg repository" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:380 -msgid "Search in current directory hg repository" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:381 -msgid "Here:" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:385 -msgid "Search recursively in this directory" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:393 -msgid "Browse a search directory" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:426 -msgid "Hide advanced options" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:429 -msgid "Show advanced options" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:571 -msgid "Search canceled" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:575 -msgid "String not found" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:577 -msgid "matches in" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:578 -msgid "file" -msgstr "" - -#: spyderlib/widgets/findinfiles.py:586 -msgid "interrupted" -msgstr "" - -#: spyderlib/widgets/findreplace.py:62 -msgid "Search string" -msgstr "" - -#: spyderlib/widgets/findreplace.py:89 -msgid "Case Sensitive" -msgstr "" - -#: spyderlib/widgets/findreplace.py:96 -msgid "Whole words" -msgstr "" - -#: spyderlib/widgets/findreplace.py:103 -msgid "Highlight matches" -msgstr "" - -#: spyderlib/widgets/findreplace.py:118 -msgid "Replace with:" -msgstr "" - -#: spyderlib/widgets/findreplace.py:120 -msgid "Replace string" -msgstr "" - -#: spyderlib/widgets/findreplace.py:123 -msgid "Replace/find" -msgstr "" - -#: spyderlib/widgets/findreplace.py:132 -msgid "Replace all" -msgstr "" - -#: spyderlib/widgets/importwizard.py:111 spyderlib/widgets/importwizard.py:425 -msgid "Import as" -msgstr "" - -#: spyderlib/widgets/importwizard.py:113 -msgid "data" -msgstr "" - -#: spyderlib/widgets/importwizard.py:117 -msgid "code" -msgstr "" - -#: spyderlib/widgets/importwizard.py:120 spyderlib/widgets/importwizard.py:497 -msgid "text" -msgstr "" - -#: spyderlib/widgets/importwizard.py:133 -msgid "Column separator:" -msgstr "" - -#: spyderlib/widgets/importwizard.py:137 -msgid "Tab" -msgstr "" - -#: spyderlib/widgets/importwizard.py:140 spyderlib/widgets/importwizard.py:159 -msgid "other" -msgstr "" - -#: spyderlib/widgets/importwizard.py:152 -msgid "Row separator:" -msgstr "" - -#: spyderlib/widgets/importwizard.py:156 -msgid "EOL" -msgstr "" - -#: spyderlib/widgets/importwizard.py:172 -msgid "Additional options" -msgstr "" - -#: spyderlib/widgets/importwizard.py:176 -msgid "Skip rows:" -msgstr "" - -#: spyderlib/widgets/importwizard.py:187 -msgid "Comments:" -msgstr "" - -#: spyderlib/widgets/importwizard.py:193 -msgid "Transpose" -msgstr "" - -#: spyderlib/widgets/importwizard.py:428 -msgid "array" -msgstr "" - -#: spyderlib/widgets/importwizard.py:433 -msgid "list" -msgstr "" - -#: spyderlib/widgets/importwizard.py:438 -msgid "DataFrame" -msgstr "" - -#: spyderlib/widgets/importwizard.py:480 spyderlib/widgets/importwizard.py:567 -msgid "Import wizard" -msgstr "" - -#: spyderlib/widgets/importwizard.py:485 -msgid "Raw text" -msgstr "" - -#: spyderlib/widgets/importwizard.py:488 -msgid "variable_name" -msgstr "" - -#: spyderlib/widgets/importwizard.py:499 -msgid "table" -msgstr "" - -#: spyderlib/widgets/importwizard.py:500 -msgid "Preview" -msgstr "" - -#: spyderlib/widgets/importwizard.py:504 -msgid "Variable Name" -msgstr "" - -#: spyderlib/widgets/importwizard.py:512 -msgid "Cancel" -msgstr "" - -#: spyderlib/widgets/importwizard.py:527 -msgid "Done" -msgstr "" - -#: spyderlib/widgets/importwizard.py:568 -msgid "Unable to proceed to next step

    Please check your entries.

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/internalshell.py:252 -msgid "Help..." -msgstr "" - -#: spyderlib/widgets/internalshell.py:259 -msgid "Help" -msgstr "" - -#: spyderlib/widgets/internalshell.py:268 -msgid "Shell special commands:" -msgstr "" - -#: spyderlib/widgets/internalshell.py:269 -msgid "Internal editor:" -msgstr "" - -#: spyderlib/widgets/internalshell.py:270 -msgid "External editor:" -msgstr "" - -#: spyderlib/widgets/internalshell.py:271 -msgid "Run script:" -msgstr "" - -#: spyderlib/widgets/internalshell.py:272 -msgid "Remove references:" -msgstr "" - -#: spyderlib/widgets/internalshell.py:273 -msgid "System commands:" -msgstr "" - -#: spyderlib/widgets/internalshell.py:274 -msgid "Python help:" -msgstr "" - -#: spyderlib/widgets/internalshell.py:275 -msgid "GUI-based editor:" -msgstr "" - -#: spyderlib/widgets/ipython.py:495 -msgid "An error ocurred while starting the kernel" -msgstr "" - -#: spyderlib/widgets/ipython.py:523 -msgid "Restart kernel" -msgstr "" - -#: spyderlib/widgets/ipython.py:545 -msgid "Stop the current command" -msgstr "" - -#: spyderlib/widgets/ipython.py:569 -msgid "Inspect current object" -msgstr "" - -#: spyderlib/widgets/ipython.py:574 -msgid "Clear line or block" -msgstr "" - -#: spyderlib/widgets/ipython.py:578 -msgid "Clear console" -msgstr "" - -#: spyderlib/widgets/ipython.py:623 -msgid "It seems the kernel died unexpectedly. Use 'Restart kernel' to continue using this console." -msgstr "" - -#: spyderlib/widgets/ipython.py:639 -msgid "Changing backend to Qt for Mayavi" -msgstr "" - -#: spyderlib/widgets/ipython.py:648 -msgid "Kernel process is either remote or unspecified. Cannot interrupt" -msgstr "" - -#: spyderlib/widgets/ipython.py:657 -msgid "Kernel process is either remote or unspecified. Cannot restart." -msgstr "" - -#: spyderlib/widgets/ipython.py:734 -msgid "Connecting to kernel..." -msgstr "" - -#: spyderlib/widgets/onecolumntree.py:63 -msgid "Collapse all" -msgstr "" - -#: spyderlib/widgets/onecolumntree.py:67 -msgid "Expand all" -msgstr "" - -#: spyderlib/widgets/onecolumntree.py:71 -msgid "Restore" -msgstr "" - -#: spyderlib/widgets/onecolumntree.py:72 -msgid "Restore original tree layout" -msgstr "" - -#: spyderlib/widgets/onecolumntree.py:76 -msgid "Collapse selection" -msgstr "" - -#: spyderlib/widgets/onecolumntree.py:80 -msgid "Expand selection" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:84 -msgid "Move to top" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:90 -msgid "Move up" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:96 -msgid "Move down" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:102 -msgid "Move to bottom" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:113 spyderlib/widgets/pathmanager.py:225 -msgid "Add path" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:118 spyderlib/widgets/pathmanager.py:209 -msgid "Remove path" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:128 -msgid "Synchronize..." -msgstr "" - -#: spyderlib/widgets/pathmanager.py:130 -msgid "Synchronize Spyder's path list with PYTHONPATH environment variable" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:141 -msgid "Synchronize" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:142 -msgid "This will synchronize Spyder's path list with PYTHONPATH environment variable for current user, allowing you to run your Python modules outside Spyder without having to configure sys.path.
    Do you want to clear contents of PYTHONPATH before adding Spyder's path list?" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:210 -msgid "Do you really want to remove selected path?" -msgstr "" - -#: spyderlib/widgets/pathmanager.py:226 -msgid "This directory is already included in Spyder path list.
    Do you want to move it to the top of the list?" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:333 -msgid "its own configuration file" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:335 -msgid " and " -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:339 -msgid "the following projects:
    %s" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:541 -msgid "Project..." -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:554 -msgid "Existing directory" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:558 -msgid "Existing Spyder project" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:562 -msgid "Existing Pydev project" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:579 -msgid "Open project" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:584 -msgid "Close project" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:589 -msgid "Close unrelated projects" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:598 -msgid "Edit related projects" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:606 -msgid "Add to PYTHONPATH" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:611 -msgid "Remove from PYTHONPATH" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:616 -msgid "Properties" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:651 -msgid "Show horizontal scrollbar" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:684 -msgid "Workspace" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:685 -msgid "The workspace was unable to load or save %s

    Please check if you have the permission to write the associated configuration files." -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:744 -msgid "Import directory" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:746 -msgid "The following directory is not in workspace:
    %s

    Do you want to continue (and copy the directory to workspace)?" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:764 -#: spyderlib/widgets/projectexplorer.py:1170 -msgid "copy" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:816 -msgid "The project %s is already opened!" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:823 -msgid "The project root path directory is inside the workspace but not as the expected tree level. It is not a directory of the workspace:
    %s" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:834 -msgid "Project name:" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:843 -msgid "A project named %s already exists" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:848 -msgid "Invalid project name.

    Name must match the following regular expression:
    %s" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:855 -msgid "The following directory is not empty:
    %s

    Do you want to continue?" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:867 -msgid "New project" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:875 -msgid "" -"The current workspace has not been configured yet.\n" -"Do you want to do this now?" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:912 -msgid "Import existing project" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:925 -msgid "Select projects to import" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:937 -msgid "The folder %s does not contain a valid %s project" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:965 -msgid "Import existing Pydev project" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:966 -msgid "Unable to read Pydev project %s

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1004 -msgid "Do you really want to delete project %s?

    Note: project files won't be deleted from disk." -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1057 -msgid "Related projects" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1065 -msgid "Select projects which are related to %s" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1090 -msgid "Statistics on source files only:
    (Python, C/C++, Fortran)

    %s files.
    %s lines of code." -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1138 -msgid "File %s already exists.
    Do you want to overwrite it?" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1152 -msgid "Folder %s already exists." -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1172 -msgid "move" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1182 -msgid "Select an existing workspace directory, or create a new one" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1183 -msgid "What is the workspace?

    A Spyder workspace is a directory on your filesystem that contains Spyder projects and .spyderworkspace configuration file.

    A Spyder project is a directory with source code (and other related files) and a configuration file (named .spyderproject) with project settings (PYTHONPATH, linked projects, ...).
    " -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1210 -msgid "This is the current workspace directory" -msgstr "" - -#: spyderlib/widgets/projectexplorer.py:1241 -msgid "The following directory is not a Spyder workspace:
    %s

    Do you want to create a new workspace in this directory?" -msgstr "" - -#: spyderlib/widgets/pydocgui.py:107 -msgid "Module or package:" -msgstr "" - -#: spyderlib/widgets/shell.py:126 -msgid "Save history log..." -msgstr "" - -#: spyderlib/widgets/shell.py:128 -msgid "Save current history log (i.e. all inputs and outputs) in a text file" -msgstr "" - -#: spyderlib/widgets/shell.py:248 -msgid "Save history log" -msgstr "" - -#: spyderlib/widgets/shell.py:251 -msgid "History logs" -msgstr "" - -#: spyderlib/widgets/shell.py:262 -msgid "Unable to save file '%s'

    Error message:
    %s" -msgstr "" - -#: spyderlib/widgets/shell.py:701 -msgid "Copy without prompts" -msgstr "" - -#: spyderlib/widgets/shell.py:704 spyderlib/widgets/shell.py:708 -msgid "Clear line" -msgstr "" - -#: spyderlib/widgets/shell.py:710 -msgid "Clear shell" -msgstr "" - -#: spyderlib/widgets/shell.py:714 -msgid "Clear shell contents ('cls' command)" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:88 -msgid "Go to line:" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:97 -msgid "Line count:" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1210 -msgid "Breakpoint" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1211 -msgid "Condition:" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1671 -msgid "To do" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1880 -msgid "Removal error" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:1881 -msgid "" -"It was not possible to remove outputs from this notebook. The error is:\n" -"\n" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2296 -msgid "Clear all ouput" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2302 -msgid "Go to definition" -msgstr "" - -#: spyderlib/widgets/sourcecode/codeeditor.py:2314 -msgid "Zoom reset" -msgstr "" - -#: spyderlib/widgets/sourcecode/syntaxhighlighters.py:30 -msgid "Syntax highlighting for Matlab, Julia and other file types" -msgstr "" - -#: spyderlib/widgets/status.py:23 -msgid "CPU and memory usage info in the status bar" -msgstr "" - -#: spyderlib/widgets/status.py:92 -msgid "Memory:" -msgstr "" - -#: spyderlib/widgets/status.py:93 -msgid "Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows platforms" -msgstr "" - -#: spyderlib/widgets/status.py:105 -msgid "CPU:" -msgstr "" - -#: spyderlib/widgets/status.py:106 -msgid "CPU usage status: requires the `psutil` (>=v0.3) library" -msgstr "" - -#: spyderlib/widgets/status.py:128 -msgid "Permissions:" -msgstr "" - -#: spyderlib/widgets/status.py:142 -msgid "End-of-lines:" -msgstr "" - -#: spyderlib/widgets/status.py:156 -msgid "Encoding:" -msgstr "" - -#: spyderlib/widgets/status.py:169 -msgid "Line:" -msgstr "" - -#: spyderlib/widgets/status.py:173 -msgid "Column:" -msgstr "" - -#: spyderlib/widgets/tabs.py:137 -msgid "Browse tabs" -msgstr "" - -#: spyderlib/widgets/tabs.py:260 -msgid "Close current tab" -msgstr "" - -#: spyderlib/widgets/texteditor.py:72 -msgid "Text editor" -msgstr "" - diff -Nru spyder-2.3.8+dfsg1/spyderlib/mac_stylesheet.qss spyder-3.0.2+dfsg1/spyderlib/mac_stylesheet.qss --- spyder-2.3.8+dfsg1/spyderlib/mac_stylesheet.qss 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/mac_stylesheet.qss 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -/* -* Qt Stylesheet for MacOS X -* Copyright (c) 2015- The Spyder Development Team -*/ - - -/* ---------------- Dock widget and QSplitter separators --------------- */ - -QMainWindow::separator { - width: 3px; - height: 3px; - border: 1px solid lightgrey; - border-radius: 1px; -} - -QMainWindow::separator:hover { - background: darkgrey; -} - -QToolButton { - border: none; -} - -QSplitter::handle:horizontal { - border: 1px solid darkgrey; - width: 2px; -} - -QSplitter::handle:vertical { - border: 1px solid darkgrey; - height: 2px; -} - -QSplitter::handle:pressed { - background: darkgrey; -} - - -/* ----------------- Tabs ------------------ */ - -QWidget#tab-container { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #b1b1b1, stop: 0.07 #b3b3b3, - stop: 0.33 #b3b3b3, stop: 0.4 #b0b0b0, - stop: 0.47 #b3b3b3, stop: 1.0 #b2b2b2); -} - -QTabWidget::pane#plugin-tab { - border-top: 1px solid qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #b1b1b1, stop: 0.07 #b3b3b3, - stop: 0.33 #b3b3b3, stop: 0.4 #b0b0b0, - stop: 0.47 #b3b3b3, stop: 1.0 #b2b2b2); - border-bottom: 0px; - border-left: 0px; - border-right: 0px; -} - -QTabWidget::tab-bar#plugin-tab { - left: 5px; -} - -QTabBar::tab#plugin-tab { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #b1b1b1, stop: 0.07 #b3b3b3, - stop: 0.33 #b3b3b3, stop: 0.4 #b0b0b0, - stop: 0.47 #b3b3b3, stop: 1.0 #b2b2b2); - border: 1px solid #787878; - border-top-color: transparent; - border-bottom-color: transparent; - margin-left: -1px; - margin-right: -1px; - min-width: 15ex; - padding: 3px; -} - -QTabBar::tab:selected#plugin-tab { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #dfdfdf, stop: 0.1 #dddddd, - stop: 0.12 #dfdfdf, stop: 0.22 #e0e0e0, - stop: 0.33 #dedede, stop: 0.47 #dedede, - stop: 0.49 #e0e0e0, stop: 0.59 #dddddd, - stop: 0.61 #dfdfdf, stop: 0.73 #dedede, - stop: 0.80 #e0e0e0, stop: 1.0 #dedede); - border: 1px solid #787878; - border-top: 0px; - border-top-color: transparent; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; -} - -QTabBar::tab:first#plugin-tab { - margin-left: 0; -} - -QTabBar::tab:last#plugin-tab { - margin-right: 0; -} - -QTabBar::tab:only-one#plugin-tab { - margin: 0; -} - -QTabBar::scroller#plugin-tab { - width: 22px; -} - -QTabBar#plugin-tab QToolButton::left-arrow { - background: lightgrey; - border-right: 1px solid darkgrey; - image: url(spyderlib/images/chevron-left.png); -} - -QTabBar#plugin-tab QToolButton::right-arrow { - background: lightgrey; - image: url(spyderlib/images/chevron-right.png); -} - - -/* ------------------ Dock widgets ------------------- */ - -QDockWidget::close-button, QDockWidget::float-button { - padding: 0px; - margin: 2px; -} diff -Nru spyder-2.3.8+dfsg1/spyderlib/otherplugins.py spyder-3.0.2+dfsg1/spyderlib/otherplugins.py --- spyder-2.3.8+dfsg1/spyderlib/otherplugins.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/otherplugins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Spyder third-party plugins configuration management -""" - -import os -import os.path as osp -import sys -import traceback - -# Local imports -from spyderlib.utils import programs - - -# Calculate path to `spyderplugins` package, where Spyder looks for all 3rd -# party plugin modules -PLUGIN_PATH = None -if programs.is_module_installed("spyderplugins"): - import spyderplugins - PLUGIN_PATH = osp.abspath(spyderplugins.__path__[0]) - if not osp.isdir(PLUGIN_PATH): - # py2exe/cx_Freeze distribution: ignoring extra plugins - PLUGIN_PATH = None - - -def get_spyderplugins(prefix, extension): - """Scan directory of `spyderplugins` package and - return the list of module names matching *prefix* and *extension*""" - plist = [] - if PLUGIN_PATH is not None: - for name in os.listdir(PLUGIN_PATH): - modname, ext = osp.splitext(name) - if prefix is not None and not name.startswith(prefix): - continue - if extension is not None and ext != extension: - continue - plist.append(modname) - return plist - - -def get_spyderplugins_mods(prefix, extension): - """Import modules that match *prefix* and *extension* from - `spyderplugins` package and return the list""" - modlist = [] - for modname in get_spyderplugins(prefix, extension): - name = 'spyderplugins.%s' % modname - try: - __import__(name) - modlist.append(sys.modules[name]) - except Exception: - sys.stderr.write( - "ERROR: 3rd party plugin import failed for `%s`\n" % modname) - traceback.print_exc(file=sys.stderr) - return modlist diff -Nru spyder-2.3.8+dfsg1/spyderlib/pil_patch.py spyder-3.0.2+dfsg1/spyderlib/pil_patch.py --- spyder-2.3.8+dfsg1/spyderlib/pil_patch.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/pil_patch.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Patching PIL (Python Imaging Library) to avoid triggering the error: -AccessInit: hash collision: 3 for both 1 and 1 - -This error is occuring because of a bug in the PIL import mechanism. - -How to reproduce this bug in a standard Python interpreter outside Spyder? -By importing PIL by two different mechanisms - -Example on Windows: -=============================================================================== -C:\Python27\Lib\site-packages>python -Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32 -Type "help", "copyright", "credits" or "license" for more information. ->>> import Image ->>> from PIL import Image -AccessInit: hash collision: 3 for both 1 and 1 -=============================================================================== - -Another example on Windows (actually that's the same, but this is the exact -case encountered with Spyder when the global working directory is the -site-packages directory): -=============================================================================== -C:\Python27\Lib\site-packages>python -Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32 -Type "help", "copyright", "credits" or "license" for more information. ->>> import scipy ->>> from pylab import * -AccessInit: hash collision: 3 for both 1 and 1 -=============================================================================== - -The solution to this fix is the following patch: -=============================================================================== -C:\Python27\Lib\site-packages>python -Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win -32 -Type "help", "copyright", "credits" or "license" for more information. ->>> import Image ->>> import PIL ->>> PIL.Image = Image ->>> from PIL import Image ->>> -=============================================================================== -""" - -try: - # For Pillow compatibility - from PIL import Image - import PIL - PIL.Image = Image -except ImportError: - # For PIL - import Image - import PIL - PIL.Image = Image diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/configdialog.py spyder-3.0.2+dfsg1/spyderlib/plugins/configdialog.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/configdialog.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/configdialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,841 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Configuration dialog / Preferences""" - -import os.path as osp - -from spyderlib.baseconfig import _, running_in_mac_app -from spyderlib.config import CONF, is_gtk_desktop -from spyderlib.guiconfig import (CUSTOM_COLOR_SCHEME_NAME, - set_default_color_scheme) -from spyderlib.utils.qthelpers import get_icon, get_std_icon -from spyderlib.userconfig import NoDefault -from spyderlib.widgets.colors import ColorLayout -from spyderlib.widgets.sourcecode import syntaxhighlighters as sh - -from spyderlib.qt.QtGui import (QWidget, QDialog, QListWidget, QListWidgetItem, - QVBoxLayout, QStackedWidget, QListView, - QHBoxLayout, QDialogButtonBox, QCheckBox, - QMessageBox, QLabel, QLineEdit, QSpinBox, - QPushButton, QFontComboBox, QGroupBox, - QComboBox, QColor, QGridLayout, QTabWidget, - QRadioButton, QButtonGroup, QSplitter, - QStyleFactory, QScrollArea, QDoubleSpinBox) -from spyderlib.qt.QtCore import Qt, QSize, SIGNAL, SLOT, Slot -from spyderlib.qt.compat import (to_qvariant, from_qvariant, - getexistingdirectory, getopenfilename) -from spyderlib.py3compat import to_text_string, is_text_string, getcwd - - -class ConfigAccessMixin(object): - """Namespace for methods that access config storage""" - CONF_SECTION = None - - def set_option(self, option, value): - CONF.set(self.CONF_SECTION, option, value) - - def get_option(self, option, default=NoDefault): - return CONF.get(self.CONF_SECTION, option, default) - - -class ConfigPage(QWidget): - """Base class for configuration page in Preferences""" - - def __init__(self, parent, apply_callback=None): - QWidget.__init__(self, parent) - self.apply_callback = apply_callback - self.is_modified = False - - def initialize(self): - """ - Initialize configuration page: - * setup GUI widgets - * load settings and change widgets accordingly - """ - self.setup_page() - self.load_from_conf() - - def get_name(self): - """Return configuration page name""" - raise NotImplementedError - - def get_icon(self): - """Return configuration page icon (24x24)""" - raise NotImplementedError - - def setup_page(self): - """Setup configuration page widget""" - raise NotImplementedError - - def set_modified(self, state): - self.is_modified = state - self.emit(SIGNAL("apply_button_enabled(bool)"), state) - - def is_valid(self): - """Return True if all widget contents are valid""" - raise NotImplementedError - - def apply_changes(self): - """Apply changes callback""" - if self.is_modified: - self.save_to_conf() - if self.apply_callback is not None: - self.apply_callback() - self.set_modified(False) - - def load_from_conf(self): - """Load settings from configuration file""" - raise NotImplementedError - - def save_to_conf(self): - """Save settings to configuration file""" - raise NotImplementedError - - -class ConfigDialog(QDialog): - """Spyder configuration ('Preferences') dialog box""" - def __init__(self, parent=None): - QDialog.__init__(self, parent) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - self.contents_widget = QListWidget() - self.contents_widget.setMovement(QListView.Static) - self.contents_widget.setSpacing(1) - - bbox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Apply - |QDialogButtonBox.Cancel) - self.apply_btn = bbox.button(QDialogButtonBox.Apply) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - self.connect(bbox, SIGNAL("clicked(QAbstractButton*)"), - self.button_clicked) - - self.pages_widget = QStackedWidget() - self.connect(self.pages_widget, SIGNAL("currentChanged(int)"), - self.current_page_changed) - - self.connect(self.contents_widget, SIGNAL("currentRowChanged(int)"), - self.pages_widget.setCurrentIndex) - self.contents_widget.setCurrentRow(0) - - hsplitter = QSplitter() - hsplitter.addWidget(self.contents_widget) - hsplitter.addWidget(self.pages_widget) - - btnlayout = QHBoxLayout() - btnlayout.addStretch(1) - btnlayout.addWidget(bbox) - - vlayout = QVBoxLayout() - vlayout.addWidget(hsplitter) - vlayout.addLayout(btnlayout) - - self.setLayout(vlayout) - - self.setWindowTitle(_("Preferences")) - self.setWindowIcon(get_icon("configure.png")) - - def get_current_index(self): - """Return current page index""" - return self.contents_widget.currentRow() - - def set_current_index(self, index): - """Set current page index""" - self.contents_widget.setCurrentRow(index) - - def get_page(self, index=None): - """Return page widget""" - if index is None: - widget = self.pages_widget.currentWidget() - else: - widget = self.pages_widget.widget(index) - return widget.widget() - - def accept(self): - """Reimplement Qt method""" - for index in range(self.pages_widget.count()): - configpage = self.get_page(index) - if not configpage.is_valid(): - return - configpage.apply_changes() - QDialog.accept(self) - - def button_clicked(self, button): - if button is self.apply_btn: - # Apply button was clicked - configpage = self.get_page() - if not configpage.is_valid(): - return - configpage.apply_changes() - - def current_page_changed(self, index): - widget = self.get_page(index) - self.apply_btn.setVisible(widget.apply_callback is not None) - self.apply_btn.setEnabled(widget.is_modified) - - def add_page(self, widget): - self.connect(self, SIGNAL('check_settings()'), widget.check_settings) - self.connect(widget, SIGNAL('show_this_page()'), - lambda row=self.contents_widget.count(): - self.contents_widget.setCurrentRow(row)) - self.connect(widget, SIGNAL("apply_button_enabled(bool)"), - self.apply_btn.setEnabled) - scrollarea = QScrollArea(self) - scrollarea.setWidgetResizable(True) - scrollarea.setWidget(widget) - self.pages_widget.addWidget(scrollarea) - item = QListWidgetItem(self.contents_widget) - item.setIcon(widget.get_icon()) - item.setText(widget.get_name()) - item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) - item.setSizeHint(QSize(0, 25)) - - def check_all_settings(self): - """This method is called to check all configuration page settings - after configuration dialog has been shown""" - self.emit(SIGNAL('check_settings()')) - - def resizeEvent(self, event): - """ - Reimplement Qt method to be able to save the widget's size from the - main application - """ - QDialog.resizeEvent(self, event) - self.emit(SIGNAL("size_change(QSize)"), self.size()) - - -class SpyderConfigPage(ConfigPage, ConfigAccessMixin): - """Plugin configuration dialog box page widget""" - CONF_SECTION = None - - def __init__(self, parent): - ConfigPage.__init__(self, parent, - apply_callback=lambda: - self.apply_settings(self.changed_options)) - self.checkboxes = {} - self.radiobuttons = {} - self.lineedits = {} - self.validate_data = {} - self.spinboxes = {} - self.comboboxes = {} - self.fontboxes = {} - self.coloredits = {} - self.scedits = {} - self.changed_options = set() - self.default_button_group = None - - def apply_settings(self, options): - raise NotImplementedError - - def check_settings(self): - """This method is called to check settings after configuration - dialog has been shown""" - pass - - def set_modified(self, state): - ConfigPage.set_modified(self, state) - if not state: - self.changed_options = set() - - def is_valid(self): - """Return True if all widget contents are valid""" - for lineedit in self.lineedits: - if lineedit in self.validate_data and lineedit.isEnabled(): - validator, invalid_msg = self.validate_data[lineedit] - text = to_text_string(lineedit.text()) - if not validator(text): - QMessageBox.critical(self, self.get_name(), - "%s:
    %s" % (invalid_msg, text), - QMessageBox.Ok) - return False - return True - - def load_from_conf(self): - """Load settings from configuration file""" - for checkbox, (option, default) in list(self.checkboxes.items()): - checkbox.setChecked(self.get_option(option, default)) - self.connect(checkbox, SIGNAL("clicked(bool)"), - lambda _foo, opt=option: self.has_been_modified(opt)) - for radiobutton, (option, default) in list(self.radiobuttons.items()): - radiobutton.setChecked(self.get_option(option, default)) - self.connect(radiobutton, SIGNAL("toggled(bool)"), - lambda _foo, opt=option: self.has_been_modified(opt)) - for lineedit, (option, default) in list(self.lineedits.items()): - lineedit.setText(self.get_option(option, default)) - self.connect(lineedit, SIGNAL("textChanged(QString)"), - lambda _foo, opt=option: self.has_been_modified(opt)) - for spinbox, (option, default) in list(self.spinboxes.items()): - spinbox.setValue(self.get_option(option, default)) - if type(spinbox) is QSpinBox: - self.connect(spinbox, SIGNAL('valueChanged(int)'), - lambda _foo, opt=option: self.has_been_modified(opt)) - else: - self.connect(spinbox, SIGNAL('valueChanged(double)'), - lambda _foo, opt=option: self.has_been_modified(opt)) - for combobox, (option, default) in list(self.comboboxes.items()): - value = self.get_option(option, default) - for index in range(combobox.count()): - data = from_qvariant(combobox.itemData(index), to_text_string) - # For PyQt API v2, it is necessary to convert `data` to - # unicode in case the original type was not a string, like an - # integer for example (see spyderlib.qt.compat.from_qvariant): - if to_text_string(data) == to_text_string(value): - break - combobox.setCurrentIndex(index) - self.connect(combobox, SIGNAL('currentIndexChanged(int)'), - lambda _foo, opt=option: self.has_been_modified(opt)) - for (fontbox, sizebox), option in list(self.fontboxes.items()): - font = self.get_font(option) - fontbox.setCurrentFont(font) - sizebox.setValue(font.pointSize()) - if option is None: - property = 'plugin_font' - else: - property = option - self.connect(fontbox, SIGNAL('currentIndexChanged(int)'), - lambda _foo, opt=property: self.has_been_modified(opt)) - self.connect(sizebox, SIGNAL('valueChanged(int)'), - lambda _foo, opt=property: self.has_been_modified(opt)) - for clayout, (option, default) in list(self.coloredits.items()): - property = to_qvariant(option) - edit = clayout.lineedit - btn = clayout.colorbtn - edit.setText(self.get_option(option, default)) - self.connect(btn, SIGNAL('clicked()'), - lambda opt=option: self.has_been_modified(opt)) - self.connect(edit, SIGNAL("textChanged(QString)"), - lambda _foo, opt=option: self.has_been_modified(opt)) - for (clayout, cb_bold, cb_italic - ), (option, default) in list(self.scedits.items()): - edit = clayout.lineedit - btn = clayout.colorbtn - color, bold, italic = self.get_option(option, default) - edit.setText(color) - cb_bold.setChecked(bold) - cb_italic.setChecked(italic) - self.connect(btn, SIGNAL('clicked()'), - lambda opt=option: self.has_been_modified(opt)) - self.connect(edit, SIGNAL("textChanged(QString)"), - lambda _foo, opt=option: self.has_been_modified(opt)) - self.connect(cb_bold, SIGNAL("clicked(bool)"), - lambda _foo, opt=option: self.has_been_modified(opt)) - self.connect(cb_italic, SIGNAL("clicked(bool)"), - lambda _foo, opt=option: self.has_been_modified(opt)) - - def save_to_conf(self): - """Save settings to configuration file""" - for checkbox, (option, _default) in list(self.checkboxes.items()): - self.set_option(option, checkbox.isChecked()) - for radiobutton, (option, _default) in list(self.radiobuttons.items()): - self.set_option(option, radiobutton.isChecked()) - for lineedit, (option, _default) in list(self.lineedits.items()): - self.set_option(option, to_text_string(lineedit.text())) - for spinbox, (option, _default) in list(self.spinboxes.items()): - self.set_option(option, spinbox.value()) - for combobox, (option, _default) in list(self.comboboxes.items()): - data = combobox.itemData(combobox.currentIndex()) - self.set_option(option, from_qvariant(data, to_text_string)) - for (fontbox, sizebox), option in list(self.fontboxes.items()): - font = fontbox.currentFont() - font.setPointSize(sizebox.value()) - self.set_font(font, option) - for clayout, (option, _default) in list(self.coloredits.items()): - self.set_option(option, to_text_string(clayout.lineedit.text())) - for (clayout, cb_bold, cb_italic), (option, _default) in list(self.scedits.items()): - color = to_text_string(clayout.lineedit.text()) - bold = cb_bold.isChecked() - italic = cb_italic.isChecked() - self.set_option(option, (color, bold, italic)) - - @Slot(str) - def has_been_modified(self, option): - self.set_modified(True) - self.changed_options.add(option) - - def create_checkbox(self, text, option, default=NoDefault, - tip=None, msg_warning=None, msg_info=None, - msg_if_enabled=False): - checkbox = QCheckBox(text) - if tip is not None: - checkbox.setToolTip(tip) - self.checkboxes[checkbox] = (option, default) - if msg_warning is not None or msg_info is not None: - def show_message(is_checked): - if is_checked or not msg_if_enabled: - if msg_warning is not None: - QMessageBox.warning(self, self.get_name(), - msg_warning, QMessageBox.Ok) - if msg_info is not None: - QMessageBox.information(self, self.get_name(), - msg_info, QMessageBox.Ok) - self.connect(checkbox, SIGNAL("clicked(bool)"), show_message) - return checkbox - - def create_radiobutton(self, text, option, default=NoDefault, - tip=None, msg_warning=None, msg_info=None, - msg_if_enabled=False, button_group=None): - radiobutton = QRadioButton(text) - if button_group is None: - if self.default_button_group is None: - self.default_button_group = QButtonGroup(self) - button_group = self.default_button_group - button_group.addButton(radiobutton) - if tip is not None: - radiobutton.setToolTip(tip) - self.radiobuttons[radiobutton] = (option, default) - if msg_warning is not None or msg_info is not None: - def show_message(is_checked): - if is_checked or not msg_if_enabled: - if msg_warning is not None: - QMessageBox.warning(self, self.get_name(), - msg_warning, QMessageBox.Ok) - if msg_info is not None: - QMessageBox.information(self, self.get_name(), - msg_info, QMessageBox.Ok) - self.connect(radiobutton, SIGNAL("toggled(bool)"), show_message) - return radiobutton - - def create_lineedit(self, text, option, default=NoDefault, - tip=None, alignment=Qt.Vertical): - label = QLabel(text) - label.setWordWrap(True) - edit = QLineEdit() - layout = QVBoxLayout() if alignment == Qt.Vertical else QHBoxLayout() - layout.addWidget(label) - layout.addWidget(edit) - layout.setContentsMargins(0, 0, 0, 0) - if tip: - edit.setToolTip(tip) - self.lineedits[edit] = (option, default) - widget = QWidget(self) - widget.setLayout(layout) - return widget - - def create_browsedir(self, text, option, default=NoDefault, tip=None): - widget = self.create_lineedit(text, option, default, - alignment=Qt.Horizontal) - for edit in self.lineedits: - if widget.isAncestorOf(edit): - break - msg = _("Invalid directory path") - self.validate_data[edit] = (osp.isdir, msg) - browse_btn = QPushButton(get_std_icon('DirOpenIcon'), "", self) - browse_btn.setToolTip(_("Select directory")) - self.connect(browse_btn, SIGNAL("clicked()"), - lambda: self.select_directory(edit)) - layout = QHBoxLayout() - layout.addWidget(widget) - layout.addWidget(browse_btn) - layout.setContentsMargins(0, 0, 0, 0) - browsedir = QWidget(self) - browsedir.setLayout(layout) - return browsedir - - def select_directory(self, edit): - """Select directory""" - basedir = to_text_string(edit.text()) - if not osp.isdir(basedir): - basedir = getcwd() - title = _("Select directory") - directory = getexistingdirectory(self, title, basedir) - if directory: - edit.setText(directory) - - def create_browsefile(self, text, option, default=NoDefault, tip=None, - filters=None): - widget = self.create_lineedit(text, option, default, - alignment=Qt.Horizontal) - for edit in self.lineedits: - if widget.isAncestorOf(edit): - break - msg = _("Invalid file path") - self.validate_data[edit] = (osp.isfile, msg) - browse_btn = QPushButton(get_std_icon('FileIcon'), "", self) - browse_btn.setToolTip(_("Select file")) - self.connect(browse_btn, SIGNAL("clicked()"), - lambda: self.select_file(edit, filters)) - layout = QHBoxLayout() - layout.addWidget(widget) - layout.addWidget(browse_btn) - layout.setContentsMargins(0, 0, 0, 0) - browsedir = QWidget(self) - browsedir.setLayout(layout) - return browsedir - - def select_file(self, edit, filters=None): - """Select File""" - basedir = osp.dirname(to_text_string(edit.text())) - if not osp.isdir(basedir): - basedir = getcwd() - if filters is None: - filters = _("All files (*)") - title = _("Select file") - filename, _selfilter = getopenfilename(self, title, basedir, filters) - if filename: - edit.setText(filename) - - def create_spinbox(self, prefix, suffix, option, default=NoDefault, - min_=None, max_=None, step=None, tip=None): - if prefix: - plabel = QLabel(prefix) - else: - plabel = None - if suffix: - slabel = QLabel(suffix) - else: - slabel = None - if step is not None: - if type(step) is int: - spinbox = QSpinBox() - else: - spinbox = QDoubleSpinBox() - spinbox.setDecimals(1) - spinbox.setSingleStep(step) - else: - spinbox = QSpinBox() - if min_ is not None: - spinbox.setMinimum(min_) - if max_ is not None: - spinbox.setMaximum(max_) - if tip is not None: - spinbox.setToolTip(tip) - self.spinboxes[spinbox] = (option, default) - layout = QHBoxLayout() - for subwidget in (plabel, spinbox, slabel): - if subwidget is not None: - layout.addWidget(subwidget) - layout.addStretch(1) - layout.setContentsMargins(0, 0, 0, 0) - widget = QWidget(self) - widget.setLayout(layout) - return widget - - def create_coloredit(self, text, option, default=NoDefault, tip=None, - without_layout=False): - label = QLabel(text) - clayout = ColorLayout(QColor(Qt.black), self) - clayout.lineedit.setMaximumWidth(80) - if tip is not None: - clayout.setToolTip(tip) - self.coloredits[clayout] = (option, default) - if without_layout: - return label, clayout - layout = QHBoxLayout() - layout.addWidget(label) - layout.addLayout(clayout) - layout.addStretch(1) - layout.setContentsMargins(0, 0, 0, 0) - widget = QWidget(self) - widget.setLayout(layout) - return widget - - def create_scedit(self, text, option, default=NoDefault, tip=None, - without_layout=False): - label = QLabel(text) - clayout = ColorLayout(QColor(Qt.black), self) - clayout.lineedit.setMaximumWidth(80) - if tip is not None: - clayout.setToolTip(tip) - cb_bold = QCheckBox() - cb_bold.setIcon(get_icon("bold.png")) - cb_bold.setToolTip(_("Bold")) - cb_italic = QCheckBox() - cb_italic.setIcon(get_icon("italic.png")) - cb_italic.setToolTip(_("Italic")) - self.scedits[(clayout, cb_bold, cb_italic)] = (option, default) - if without_layout: - return label, clayout, cb_bold, cb_italic - layout = QHBoxLayout() - layout.addWidget(label) - layout.addLayout(clayout) - layout.addSpacing(10) - layout.addWidget(cb_bold) - layout.addWidget(cb_italic) - layout.addStretch(1) - layout.setContentsMargins(0, 0, 0, 0) - widget = QWidget(self) - widget.setLayout(layout) - return widget - - def create_combobox(self, text, choices, option, default=NoDefault, - tip=None): - """choices: couples (name, key)""" - label = QLabel(text) - combobox = QComboBox() - if tip is not None: - combobox.setToolTip(tip) - for name, key in choices: - combobox.addItem(name, to_qvariant(key)) - self.comboboxes[combobox] = (option, default) - layout = QHBoxLayout() - for subwidget in (label, combobox): - layout.addWidget(subwidget) - layout.addStretch(1) - layout.setContentsMargins(0, 0, 0, 0) - widget = QWidget(self) - widget.setLayout(layout) - return widget - - def create_fontgroup(self, option=None, text=None, - tip=None, fontfilters=None): - """Option=None -> setting plugin font""" - fontlabel = QLabel(_("Font: ")) - fontbox = QFontComboBox() - if fontfilters is not None: - fontbox.setFontFilters(fontfilters) - sizelabel = QLabel(" "+_("Size: ")) - sizebox = QSpinBox() - sizebox.setRange(7, 100) - self.fontboxes[(fontbox, sizebox)] = option - layout = QHBoxLayout() - for subwidget in (fontlabel, fontbox, sizelabel, sizebox): - layout.addWidget(subwidget) - layout.addStretch(1) - if text is None: - text = _("Font style") - group = QGroupBox(text) - group.setLayout(layout) - if tip is not None: - group.setToolTip(tip) - return group - - def create_button(self, text, callback): - btn = QPushButton(text) - self.connect(btn, SIGNAL('clicked()'), callback) - self.connect(btn, SIGNAL('clicked()'), - lambda opt='': self.has_been_modified(opt)) - return btn - - def create_tab(self, *widgets): - """Create simple tab widget page: widgets added in a vertical layout""" - widget = QWidget() - layout = QVBoxLayout() - for widg in widgets: - layout.addWidget(widg) - layout.addStretch(1) - widget.setLayout(layout) - return widget - - -class GeneralConfigPage(SpyderConfigPage): - """Config page that maintains reference to main Spyder window - and allows to specify page name and icon declaratively - """ - CONF_SECTION = None - - NAME = None # configuration page name, e.g. _("General") - ICON = None # name of icon resource (24x24) - - def __init__(self, parent, main): - SpyderConfigPage.__init__(self, parent) - self.main = main - - def get_name(self): - """Configuration page name""" - return self.NAME - - def get_icon(self): - """Loads page icon named by self.ICON""" - return get_icon(self.ICON) - - def apply_settings(self, options): - raise NotImplementedError - - -class MainConfigPage(GeneralConfigPage): - CONF_SECTION = "main" - - NAME = _("General") - ICON = "genprefs.png" - - def setup_page(self): - newcb = self.create_checkbox - - # --- Interface - interface_group = QGroupBox(_("Interface")) - styles = [str(txt) for txt in list(QStyleFactory.keys())] - # Don't offer users the possibility to change to a different - # style in Gtk-based desktops - # Fixes Issue 2036 - if is_gtk_desktop() and ('GTK+' in styles): - styles = ['GTK+'] - choices = list(zip(styles, [style.lower() for style in styles])) - style_combo = self.create_combobox(_('Qt windows style'), choices, - 'windows_style', - default=self.main.default_style) - - single_instance_box = newcb(_("Use a single instance"), - 'single_instance', - tip=_("Set this to open external
    " - "Python files in an already running " - "instance (Requires a restart)")) - vertdock_box = newcb(_("Vertical dockwidget title bars"), - 'vertical_dockwidget_titlebars') - verttabs_box = newcb(_("Vertical dockwidget tabs"), - 'vertical_tabs') - animated_box = newcb(_("Animated toolbars and dockwidgets"), - 'animated_docks') - tear_off_box = newcb(_("Tear off menus"), 'tear_off_menus', - tip=_("Set this to detach any
    " - "menu from the main window")) - margin_box = newcb(_("Custom dockwidget margin:"), - 'use_custom_margin') - margin_spin = self.create_spinbox("", "pixels", 'custom_margin', - 0, 0, 30) - self.connect(margin_box, SIGNAL("toggled(bool)"), - margin_spin.setEnabled) - margin_spin.setEnabled(self.get_option('use_custom_margin')) - margins_layout = QHBoxLayout() - margins_layout.addWidget(margin_box) - margins_layout.addWidget(margin_spin) - - # Decide if it's possible to activate or not singie instance mode - if running_in_mac_app(): - self.set_option("single_instance", True) - single_instance_box.setEnabled(False) - - interface_layout = QVBoxLayout() - interface_layout.addWidget(style_combo) - interface_layout.addWidget(single_instance_box) - interface_layout.addWidget(vertdock_box) - interface_layout.addWidget(verttabs_box) - interface_layout.addWidget(animated_box) - interface_layout.addWidget(tear_off_box) - interface_layout.addLayout(margins_layout) - interface_group.setLayout(interface_layout) - - # --- Status bar - sbar_group = QGroupBox(_("Status bar")) - memory_box = newcb(_("Show memory usage every"), 'memory_usage/enable', - tip=self.main.mem_status.toolTip()) - memory_spin = self.create_spinbox("", " ms", 'memory_usage/timeout', - min_=100, max_=1000000, step=100) - self.connect(memory_box, SIGNAL("toggled(bool)"), - memory_spin.setEnabled) - memory_spin.setEnabled(self.get_option('memory_usage/enable')) - memory_layout = QHBoxLayout() - memory_layout.addWidget(memory_box) - memory_layout.addWidget(memory_spin) - memory_layout.setEnabled(self.main.mem_status.is_supported()) - cpu_box = newcb(_("Show CPU usage every"), 'cpu_usage/enable', - tip=self.main.cpu_status.toolTip()) - cpu_spin = self.create_spinbox("", " ms", 'cpu_usage/timeout', - min_=100, max_=1000000, step=100) - self.connect(cpu_box, SIGNAL("toggled(bool)"), cpu_spin.setEnabled) - cpu_spin.setEnabled(self.get_option('cpu_usage/enable')) - cpu_layout = QHBoxLayout() - cpu_layout.addWidget(cpu_box) - cpu_layout.addWidget(cpu_spin) - cpu_layout.setEnabled(self.main.cpu_status.is_supported()) - - sbar_layout = QVBoxLayout() - sbar_layout.addLayout(memory_layout) - sbar_layout.addLayout(cpu_layout) - sbar_group.setLayout(sbar_layout) - - # --- Debugging - debug_group = QGroupBox(_("Debugging")) - popup_console_box = newcb(_("Pop up internal console when internal " - "errors appear"), - 'show_internal_console_if_traceback') - - debug_layout = QVBoxLayout() - debug_layout.addWidget(popup_console_box) - debug_group.setLayout(debug_layout) - - vlayout = QVBoxLayout() - vlayout.addWidget(interface_group) - vlayout.addWidget(sbar_group) - vlayout.addWidget(debug_group) - vlayout.addStretch(1) - self.setLayout(vlayout) - - def apply_settings(self, options): - self.main.apply_settings() - - -class ColorSchemeConfigPage(GeneralConfigPage): - CONF_SECTION = "color_schemes" - - NAME = _("Syntax coloring") - ICON = "genprefs.png" - - def setup_page(self): - tabs = QTabWidget() - names = self.get_option("names") - names.pop(names.index(CUSTOM_COLOR_SCHEME_NAME)) - names.insert(0, CUSTOM_COLOR_SCHEME_NAME) - fieldnames = { - "background": _("Background:"), - "currentline": _("Current line:"), - "currentcell": _("Current cell:"), - "occurence": _("Occurence:"), - "ctrlclick": _("Link:"), - "sideareas": _("Side areas:"), - "matched_p": _("Matched parentheses:"), - "unmatched_p": _("Unmatched parentheses:"), - "normal": _("Normal text:"), - "keyword": _("Keyword:"), - "builtin": _("Builtin:"), - "definition": _("Definition:"), - "comment": _("Comment:"), - "string": _("String:"), - "number": _("Number:"), - "instance": _("Instance:"), - } - from spyderlib.widgets.sourcecode import syntaxhighlighters - assert all([key in fieldnames - for key in syntaxhighlighters.COLOR_SCHEME_KEYS]) - for tabname in names: - cs_group = QGroupBox(_("Color scheme")) - cs_layout = QGridLayout() - for row, key in enumerate(syntaxhighlighters.COLOR_SCHEME_KEYS): - option = "%s/%s" % (tabname, key) - value = self.get_option(option) - name = fieldnames[key] - if is_text_string(value): - label, clayout = self.create_coloredit(name, option, - without_layout=True) - label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) - cs_layout.addWidget(label, row+1, 0) - cs_layout.addLayout(clayout, row+1, 1) - else: - label, clayout, cb_bold, cb_italic = self.create_scedit( - name, option, without_layout=True) - label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) - cs_layout.addWidget(label, row+1, 0) - cs_layout.addLayout(clayout, row+1, 1) - cs_layout.addWidget(cb_bold, row+1, 2) - cs_layout.addWidget(cb_italic, row+1, 3) - cs_group.setLayout(cs_layout) - if tabname in sh.COLOR_SCHEME_NAMES: - def_btn = self.create_button(_("Reset to default values"), - lambda: self.reset_to_default(tabname)) - tabs.addTab(self.create_tab(cs_group, def_btn), tabname) - else: - tabs.addTab(self.create_tab(cs_group), tabname) - - vlayout = QVBoxLayout() - vlayout.addWidget(tabs) - self.setLayout(vlayout) - - @Slot(str) - def reset_to_default(self, name): - set_default_color_scheme(name, replace=True) - self.load_from_conf() - - def apply_settings(self, options): - self.main.editor.apply_plugin_settings(['color_scheme_name']) - if self.main.historylog is not None: - self.main.historylog.apply_plugin_settings(['color_scheme_name']) - if self.main.inspector is not None: - self.main.inspector.apply_plugin_settings(['color_scheme_name']) diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/console.py spyder-3.0.2+dfsg1/spyderlib/plugins/console.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/console.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/console.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,337 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Internal Console Plugin""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import (QVBoxLayout, QFontDialog, QInputDialog, - QLineEdit, QMenu) -from spyderlib.qt.QtCore import SIGNAL -from spyderlib.qt.compat import getopenfilename - -import os -import sys -import os.path as osp - - -# Local imports -from spyderlib.baseconfig import _, debug_print -from spyderlib.config import CONF -from spyderlib.utils.misc import get_error_match, remove_backslashes -from spyderlib.utils.qthelpers import (get_icon, create_action, add_actions, - mimedata2url, DialogManager) -from spyderlib.utils.environ import EnvDialog -from spyderlib.widgets.internalshell import InternalShell -from spyderlib.widgets.findreplace import FindReplace -from spyderlib.widgets.dicteditor import DictEditor -from spyderlib.plugins import SpyderPluginWidget -from spyderlib.py3compat import to_text_string, getcwd - - -class Console(SpyderPluginWidget): - """ - Console widget - """ - CONF_SECTION = 'internal_console' - def __init__(self, parent=None, namespace=None, commands=[], message=None, - exitfunc=None, profile=False, multithreaded=False): - SpyderPluginWidget.__init__(self, parent) - - debug_print(" ..internal console: initializing") - self.dialog_manager = DialogManager() - - # Shell - light_background = self.get_option('light_background') - self.shell = InternalShell(parent, namespace, commands, message, - self.get_option('max_line_count'), - self.get_plugin_font(), exitfunc, profile, - multithreaded, - light_background=light_background) - self.connect(self.shell, SIGNAL('status(QString)'), - lambda msg: - self.emit(SIGNAL('show_message(QString,int)'), msg, 0)) - self.connect(self.shell, SIGNAL("go_to_error(QString)"), - self.go_to_error) - self.connect(self.shell, SIGNAL("focus_changed()"), - lambda: self.emit(SIGNAL("focus_changed()"))) - # Redirecting some SIGNALs: - self.connect(self.shell, SIGNAL('redirect_stdio(bool)'), - lambda state: self.emit(SIGNAL('redirect_stdio(bool)'), - state)) - - # Initialize plugin - self.initialize_plugin() - - # Find/replace widget - self.find_widget = FindReplace(self) - self.find_widget.set_editor(self.shell) - self.find_widget.hide() - self.register_widget_shortcuts("Editor", self.find_widget) - - # Main layout - layout = QVBoxLayout() - layout.addWidget(self.shell) - layout.addWidget(self.find_widget) - self.setLayout(layout) - - # Parameters - self.shell.toggle_wrap_mode(self.get_option('wrap')) - - # Accepting drops - self.setAcceptDrops(True) - - #------ Private API -------------------------------------------------------- - def set_historylog(self, historylog): - """Bind historylog instance to this console - Not used anymore since v2.0""" - historylog.add_history(self.shell.history_filename) - self.connect(self.shell, SIGNAL('append_to_history(QString,QString)'), - historylog.append_to_history) - - def set_inspector(self, inspector): - """Bind inspector instance to this console""" - self.shell.inspector = inspector - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _('Internal console') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.shell - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - self.dialog_manager.close_all() - self.shell.exit_interpreter() - return True - - def refresh_plugin(self): - pass - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - quit_action = create_action(self, _("&Quit"), - icon='exit.png', tip=_("Quit"), - triggered=self.quit) - self.register_shortcut(quit_action, "_", "Quit", "Ctrl+Q") - run_action = create_action(self, _("&Run..."), None, - 'run_small.png', _("Run a Python script"), - triggered=self.run_script) - environ_action = create_action(self, - _("Environment variables..."), - icon = 'environ.png', - tip=_("Show and edit environment variables" - " (for current session)"), - triggered=self.show_env) - syspath_action = create_action(self, - _("Show sys.path contents..."), - icon = 'syspath.png', - tip=_("Show (read-only) sys.path"), - triggered=self.show_syspath) - buffer_action = create_action(self, - _("Buffer..."), None, - tip=_("Set maximum line count"), - triggered=self.change_max_line_count) - font_action = create_action(self, - _("&Font..."), None, - 'font.png', _("Set shell font style"), - triggered=self.change_font) - exteditor_action = create_action(self, - _("External editor path..."), None, None, - _("Set external editor executable path"), - triggered=self.change_exteditor) - wrap_action = create_action(self, - _("Wrap lines"), - toggled=self.toggle_wrap_mode) - wrap_action.setChecked(self.get_option('wrap')) - calltips_action = create_action(self, _("Display balloon tips"), - toggled=self.toggle_calltips) - calltips_action.setChecked(self.get_option('calltips')) - codecompletion_action = create_action(self, - _("Automatic code completion"), - toggled=self.toggle_codecompletion) - codecompletion_action.setChecked(self.get_option('codecompletion/auto')) - codecompenter_action = create_action(self, - _("Enter key selects completion"), - toggled=self.toggle_codecompletion_enter) - codecompenter_action.setChecked(self.get_option( - 'codecompletion/enter_key')) - - option_menu = QMenu(_("Internal console settings"), self) - option_menu.setIcon(get_icon('tooloptions.png')) - add_actions(option_menu, (buffer_action, font_action, wrap_action, - calltips_action, codecompletion_action, - codecompenter_action, exteditor_action)) - - plugin_actions = [None, run_action, environ_action, syspath_action, - option_menu, None, quit_action] - - # Add actions to context menu - add_actions(self.shell.menu, plugin_actions) - - return plugin_actions - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self, SIGNAL('focus_changed()'), - self.main.plugin_focus_changed) - self.main.add_dockwidget(self) - # Connecting the following signal once the dockwidget has been created: - self.connect(self.shell, SIGNAL('traceback_available()'), - self.traceback_available) - - def traceback_available(self): - """Traceback is available in the internal console: showing the - internal console automatically to warn the user""" - if CONF.get('main', 'show_internal_console_if_traceback', False): - self.dockwidget.show() - self.dockwidget.raise_() - - #------ Public API --------------------------------------------------------- - def quit(self): - """Quit mainwindow""" - self.main.close() - - def show_env(self): - """Show environment variables""" - self.dialog_manager.show(EnvDialog()) - - def show_syspath(self): - """Show sys.path""" - editor = DictEditor() - editor.setup(sys.path, title="sys.path", readonly=True, - width=600, icon='syspath.png') - self.dialog_manager.show(editor) - - def run_script(self, filename=None, silent=False, set_focus=False, - args=None): - """Run a Python script""" - if filename is None: - self.shell.interpreter.restore_stds() - filename, _selfilter = getopenfilename(self, _("Run Python script"), - getcwd(), _("Python scripts")+" (*.py ; *.pyw ; *.ipy)") - self.shell.interpreter.redirect_stds() - if filename: - os.chdir( osp.dirname(filename) ) - filename = osp.basename(filename) - else: - return - filename = osp.abspath(filename) - rbs = remove_backslashes - command = "runfile('%s', args='%s')" % (rbs(filename), rbs(args)) - if set_focus: - self.shell.setFocus() - if self.dockwidget and not self.ismaximized: - self.dockwidget.setVisible(True) - self.dockwidget.raise_() - self.shell.write(command+'\n') - self.shell.run_command(command) - - - def go_to_error(self, text): - """Go to error if relevant""" - match = get_error_match(to_text_string(text)) - if match: - fname, lnb = match.groups() - self.edit_script(fname, int(lnb)) - - def edit_script(self, filename=None, goto=-1): - """Edit script""" - # Called from InternalShell - if not hasattr(self, 'main') \ - or not hasattr(self.main, 'editor'): - self.shell.external_editor(filename, goto) - return - if filename is not None: - self.emit(SIGNAL("edit_goto(QString,int,QString)"), - osp.abspath(filename), goto, '') - - def execute_lines(self, lines): - """Execute lines and give focus to shell""" - self.shell.execute_lines(to_text_string(lines)) - self.shell.setFocus() - - def change_font(self): - """Change console font""" - font, valid = QFontDialog.getFont(self.get_plugin_font(), - self, _("Select a new font")) - if valid: - self.shell.set_font(font) - self.set_plugin_font(font) - - def change_max_line_count(self): - "Change maximum line count""" - mlc, valid = QInputDialog.getInteger(self, _('Buffer'), - _('Maximum line count'), - self.get_option('max_line_count'), - 0, 1000000) - if valid: - self.shell.setMaximumBlockCount(mlc) - self.set_option('max_line_count', mlc) - - def change_exteditor(self): - """Change external editor path""" - path, valid = QInputDialog.getText(self, _('External editor'), - _('External editor executable path:'), - QLineEdit.Normal, - self.get_option('external_editor/path')) - if valid: - self.set_option('external_editor/path', to_text_string(path)) - - def toggle_wrap_mode(self, checked): - """Toggle wrap mode""" - self.shell.toggle_wrap_mode(checked) - self.set_option('wrap', checked) - - def toggle_calltips(self, checked): - """Toggle calltips""" - self.shell.set_calltips(checked) - self.set_option('calltips', checked) - - def toggle_codecompletion(self, checked): - """Toggle automatic code completion""" - self.shell.set_codecompletion_auto(checked) - self.set_option('codecompletion/auto', checked) - - def toggle_codecompletion_enter(self, checked): - """Toggle Enter key for code completion""" - self.shell.set_codecompletion_enter(checked) - self.set_option('codecompletion/enter_key', checked) - - #----Drag and drop - def dragEnterEvent(self, event): - """Reimplement Qt method - Inform Qt about the types of data that the widget accepts""" - source = event.mimeData() - if source.hasUrls(): - if mimedata2url(source): - event.acceptProposedAction() - else: - event.ignore() - elif source.hasText(): - event.acceptProposedAction() - - def dropEvent(self, event): - """Reimplement Qt method - Unpack dropped data and handle it""" - source = event.mimeData() - if source.hasUrls(): - pathlist = mimedata2url(source) - self.shell.drop_pathlist(pathlist) - elif source.hasText(): - lines = to_text_string(source.text()) - self.shell.set_cursor_position('eof') - self.shell.execute_lines(lines) - event.acceptProposedAction() diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/editor.py spyder-3.0.2+dfsg1/spyderlib/plugins/editor.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/editor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/editor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2341 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Editor Plugin""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import (QVBoxLayout, QPrintDialog, QSplitter, QToolBar, - QAction, QApplication, QDialog, QWidget, - QPrinter, QActionGroup, QInputDialog, QMenu, - QAbstractPrintDialog, QGroupBox, QTabWidget, - QLabel, QFontComboBox, QHBoxLayout, - QKeySequence) -from spyderlib.qt.QtCore import SIGNAL, QByteArray, Qt, Slot -from spyderlib.qt.compat import to_qvariant, from_qvariant, getopenfilenames - -import os -import re -import sys -import time -import os.path as osp - -# Local imports -from spyderlib.utils import encoding, sourcecode, codeanalysis -from spyderlib.baseconfig import get_conf_path, _ -from spyderlib.config import CONF, EDIT_FILTERS, get_filter, EDIT_FILETYPES -from spyderlib.guiconfig import get_color_scheme -from spyderlib.utils import programs -from spyderlib.utils.qthelpers import (get_icon, create_action, add_actions, - get_std_icon, get_filetype_icon, - add_shortcut_to_tooltip) -from spyderlib.widgets.findreplace import FindReplace -from spyderlib.widgets.status import (ReadWriteStatus, EOLStatus, - EncodingStatus, CursorPositionStatus) -from spyderlib.widgets.editor import (EditorSplitter, EditorStack, Printer, - EditorMainWindow) -from spyderlib.widgets.sourcecode.codeeditor import CodeEditor -from spyderlib.plugins import SpyderPluginWidget, PluginConfigPage -from spyderlib.plugins.runconfig import (RunConfigDialog, RunConfigOneDialog, - get_run_configuration, - ALWAYS_OPEN_FIRST_RUN_OPTION) -from spyderlib.py3compat import PY2, to_text_string, getcwd, qbytearray_to_str - - - -def _load_all_breakpoints(): - bp_dict = CONF.get('run', 'breakpoints', {}) - for filename in list(bp_dict.keys()): - if not osp.isfile(filename): - bp_dict.pop(filename) - return bp_dict - - -def load_breakpoints(filename): - breakpoints = _load_all_breakpoints().get(filename, []) - if breakpoints and isinstance(breakpoints[0], int): - # Old breakpoints format - breakpoints = [(lineno, None) for lineno in breakpoints] - return breakpoints - - -def save_breakpoints(filename, breakpoints): - if not osp.isfile(filename): - return - bp_dict = _load_all_breakpoints() - bp_dict[filename] = breakpoints - CONF.set('run', 'breakpoints', bp_dict) - - -def clear_all_breakpoints(): - CONF.set('run', 'breakpoints', {}) - - -def clear_breakpoint(filename, lineno): - breakpoints = load_breakpoints(filename) - if breakpoints: - for breakpoint in breakpoints[:]: - if breakpoint[0] == lineno: - breakpoints.remove(breakpoint) - save_breakpoints(filename, breakpoints) - - -WINPDB_PATH = programs.find_program('winpdb') - - -class EditorConfigPage(PluginConfigPage): - def get_name(self): - return _("Editor") - - def get_icon(self): - return get_icon("edit24.png") - - def setup_page(self): - template_btn = self.create_button(_("Edit template for new modules"), - self.plugin.edit_template) - - interface_group = QGroupBox(_("Interface")) - font_group = self.create_fontgroup(option=None, - text=_("Text and margin font style"), - fontfilters=QFontComboBox.MonospacedFonts) - newcb = self.create_checkbox - fpsorting_box = newcb(_("Sort files according to full path"), - 'fullpath_sorting') - showtabbar_box = newcb(_("Show tab bar"), 'show_tab_bar') - - interface_layout = QVBoxLayout() - interface_layout.addWidget(fpsorting_box) - interface_layout.addWidget(showtabbar_box) - interface_group.setLayout(interface_layout) - - display_group = QGroupBox(_("Source code")) - linenumbers_box = newcb(_("Show line numbers"), 'line_numbers') - blanks_box = newcb(_("Show blank spaces"), 'blank_spaces') - edgeline_box = newcb(_("Show vertical line after"), 'edge_line') - edgeline_spin = self.create_spinbox("", _("characters"), - 'edge_line_column', 79, 1, 500) - self.connect(edgeline_box, SIGNAL("toggled(bool)"), - edgeline_spin.setEnabled) - edgeline_spin.setEnabled(self.get_option('edge_line')) - edgeline_layout = QHBoxLayout() - edgeline_layout.addWidget(edgeline_box) - edgeline_layout.addWidget(edgeline_spin) - currentline_box = newcb(_("Highlight current line"), - 'highlight_current_line') - currentcell_box = newcb(_("Highlight current cell"), - 'highlight_current_cell') - occurence_box = newcb(_("Highlight occurences after"), - 'occurence_highlighting') - occurence_spin = self.create_spinbox("", " ms", - 'occurence_highlighting/timeout', - min_=100, max_=1000000, step=100) - self.connect(occurence_box, SIGNAL("toggled(bool)"), - occurence_spin.setEnabled) - occurence_spin.setEnabled(self.get_option('occurence_highlighting')) - occurence_layout = QHBoxLayout() - occurence_layout.addWidget(occurence_box) - occurence_layout.addWidget(occurence_spin) - wrap_mode_box = newcb(_("Wrap lines"), 'wrap') - names = CONF.get('color_schemes', 'names') - choices = list(zip(names, names)) - cs_combo = self.create_combobox(_("Syntax color scheme: "), - choices, 'color_scheme_name') - - display_layout = QVBoxLayout() - display_layout.addWidget(linenumbers_box) - display_layout.addWidget(blanks_box) - display_layout.addLayout(edgeline_layout) - display_layout.addWidget(currentline_box) - display_layout.addWidget(currentcell_box) - display_layout.addLayout(occurence_layout) - display_layout.addWidget(wrap_mode_box) - display_layout.addWidget(cs_combo) - display_group.setLayout(display_layout) - - run_group = QGroupBox(_("Run")) - saveall_box = newcb(_("Save all files before running script"), - 'save_all_before_run') - - run_selection_group = QGroupBox(_("Run selection")) - focus_box = newcb(_("Maintain focus in the Editor after running cells " - "or selections"), 'focus_to_editor') - - introspection_group = QGroupBox(_("Introspection")) - rope_is_installed = programs.is_module_installed('rope') - if rope_is_installed: - completion_box = newcb(_("Automatic code completion"), - 'codecompletion/auto') - case_comp_box = newcb(_("Case sensitive code completion"), - 'codecompletion/case_sensitive') - comp_enter_box = newcb(_("Enter key selects completion"), - 'codecompletion/enter_key') - calltips_box = newcb(_("Display balloon tips"), 'calltips') - gotodef_box = newcb(_("Link to object definition"), - 'go_to_definition', - tip=_("If this option is enabled, clicking on an object\n" - "name (left-click + Ctrl key) will go this object\n" - "definition (if resolved).")) - else: - rope_label = QLabel(_("Warning:
    " - "The Python module rope is not " - "installed on this computer: calltips, " - "code completion and go-to-definition " - "features won't be available.")) - rope_label.setWordWrap(True) - - sourcecode_group = QGroupBox(_("Source code")) - closepar_box = newcb(_("Automatic insertion of parentheses, braces " - "and brackets"), - 'close_parentheses') - close_quotes_box = newcb(_("Automatic insertion of closing quotes"), - 'close_quotes') - add_colons_box = newcb(_("Automatic insertion of colons after 'for', " - "'if', 'def', etc"), - 'add_colons') - autounindent_box = newcb(_("Automatic indentation after 'else', " - "'elif', etc."), 'auto_unindent') - indent_chars_box = self.create_combobox(_("Indentation characters: "), - ((_("4 spaces"), '* *'), - (_("2 spaces"), '* *'), - (_("tab"), '*\t*')), 'indent_chars') - tabwidth_spin = self.create_spinbox(_("Tab stop width:"), _("pixels"), - 'tab_stop_width', 40, 10, 1000, 10) - tab_mode_box = newcb(_("Tab always indent"), - 'tab_always_indent', default=False, - tip=_("If enabled, pressing Tab will always indent,\n" - "even when the cursor is not at the beginning\n" - "of a line (when this option is enabled, code\n" - "completion may be triggered using the alternate\n" - "shortcut: Ctrl+Space)")) - ibackspace_box = newcb(_("Intelligent backspace"), - 'intelligent_backspace', default=True) - removetrail_box = newcb(_("Automatically remove trailing spaces " - "when saving files"), - 'always_remove_trailing_spaces', default=False) - - analysis_group = QGroupBox(_("Analysis")) - pep8_url = 'PEP8' - analysis_label = QLabel(_("Note: add analysis:ignore in " - "a comment to ignore code/style analysis " - "warnings. For more informations on style " - "guide for Python code, please refer to the " - "%s page.") % pep8_url) - analysis_label.setWordWrap(True) - is_pyflakes = codeanalysis.is_pyflakes_installed() - is_pep8 = codeanalysis.get_checker_executable('pep8') is not None - analysis_label.setEnabled(is_pyflakes or is_pep8) - pyflakes_box = newcb(_("Code analysis")+" (pyflakes)", - 'code_analysis/pyflakes', default=True, - tip=_("If enabled, Python source code will be analyzed\n" - "using pyflakes, lines containing errors or \n" - "warnings will be highlighted")) - pyflakes_box.setEnabled(is_pyflakes) - if not is_pyflakes: - pyflakes_box.setToolTip(_("Code analysis requires pyflakes %s+") % - codeanalysis.PYFLAKES_REQVER) - pep8_box = newcb(_("Style analysis")+' (pep8)', - 'code_analysis/pep8', default=False, - tip=_('If enabled, Python source code will be analyzed\n' - 'using pep8, lines that are not following PEP8\n' - 'style guide will be highlighted')) - pep8_box.setEnabled(is_pep8) - ancb_layout = QHBoxLayout() - ancb_layout.addWidget(pyflakes_box) - ancb_layout.addWidget(pep8_box) - todolist_box = newcb(_("Tasks (TODO, FIXME, XXX, HINT, TIP, @todo)"), - 'todo_list', default=True) - realtime_radio = self.create_radiobutton( - _("Perform analysis when " - "saving file and every"), - 'realtime_analysis', True) - saveonly_radio = self.create_radiobutton( - _("Perform analysis only " - "when saving file"), - 'onsave_analysis') - af_spin = self.create_spinbox("", " ms", 'realtime_analysis/timeout', - min_=100, max_=1000000, step=100) - af_layout = QHBoxLayout() - af_layout.addWidget(realtime_radio) - af_layout.addWidget(af_spin) - - run_layout = QVBoxLayout() - run_layout.addWidget(saveall_box) - run_group.setLayout(run_layout) - - run_selection_layout = QVBoxLayout() - run_selection_layout.addWidget(focus_box) - run_selection_group.setLayout(run_selection_layout) - - introspection_layout = QVBoxLayout() - if rope_is_installed: - introspection_layout.addWidget(calltips_box) - introspection_layout.addWidget(completion_box) - introspection_layout.addWidget(case_comp_box) - introspection_layout.addWidget(comp_enter_box) - introspection_layout.addWidget(gotodef_box) - else: - introspection_layout.addWidget(rope_label) - introspection_group.setLayout(introspection_layout) - - analysis_layout = QVBoxLayout() - analysis_layout.addWidget(analysis_label) - analysis_layout.addLayout(ancb_layout) - analysis_layout.addWidget(todolist_box) - analysis_layout.addLayout(af_layout) - analysis_layout.addWidget(saveonly_radio) - analysis_group.setLayout(analysis_layout) - - sourcecode_layout = QVBoxLayout() - sourcecode_layout.addWidget(closepar_box) - sourcecode_layout.addWidget(autounindent_box) - sourcecode_layout.addWidget(add_colons_box) - sourcecode_layout.addWidget(close_quotes_box) - sourcecode_layout.addWidget(indent_chars_box) - sourcecode_layout.addWidget(tabwidth_spin) - sourcecode_layout.addWidget(tab_mode_box) - sourcecode_layout.addWidget(ibackspace_box) - sourcecode_layout.addWidget(removetrail_box) - sourcecode_group.setLayout(sourcecode_layout) - - eol_group = QGroupBox(_("End-of-line characters")) - eol_label = QLabel(_("When opening a text file containing " - "mixed end-of-line characters (this may " - "raise syntax errors in the consoles " - "on Windows platforms), Spyder may fix the " - "file automatically.")) - eol_label.setWordWrap(True) - check_eol_box = newcb(_("Fix automatically and show warning " - "message box"), - 'check_eol_chars', default=True) - - eol_layout = QVBoxLayout() - eol_layout.addWidget(eol_label) - eol_layout.addWidget(check_eol_box) - eol_group.setLayout(eol_layout) - - tabs = QTabWidget() - tabs.addTab(self.create_tab(font_group, interface_group, display_group), - _("Display")) - tabs.addTab(self.create_tab(introspection_group, analysis_group), - _("Code Introspection/Analysis")) - tabs.addTab(self.create_tab(template_btn, run_group, run_selection_group, - sourcecode_group, eol_group), - _("Advanced settings")) - - vlayout = QVBoxLayout() - vlayout.addWidget(tabs) - self.setLayout(vlayout) - - -class Editor(SpyderPluginWidget): - """ - Multi-file Editor widget - """ - CONF_SECTION = 'editor' - CONFIGWIDGET_CLASS = EditorConfigPage - TEMPFILE_PATH = get_conf_path('temp.py') - TEMPLATE_PATH = get_conf_path('template.py') - DISABLE_ACTIONS_WHEN_HIDDEN = False # SpyderPluginWidget class attribute - def __init__(self, parent, ignore_last_opened_files=False): - SpyderPluginWidget.__init__(self, parent) - - self.__set_eol_chars = True - - self.set_default_color_scheme() - - # Creating template if it doesn't already exist - if not osp.isfile(self.TEMPLATE_PATH): - header = ['# -*- coding: utf-8 -*-', '"""', 'Created on %(date)s', - '', '@author: %(username)s', '"""', ''] - encoding.write(os.linesep.join(header), self.TEMPLATE_PATH, 'utf-8') - - self.projectexplorer = None - self.outlineexplorer = None - self.inspector = 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 - self.edit_menu_actions = None #XXX: find another way to notify Spyder - # (see spyder.py: 'update_edit_menu' method) - self.search_menu_actions = None #XXX: same thing ('update_search_menu') - self.stack_menu_actions = None - - # Initialize plugin - self.initialize_plugin() - - # Configuration dialog size - self.dialog_size = None - - statusbar = self.main.statusBar() - self.readwrite_status = ReadWriteStatus(self, statusbar) - self.eol_status = EOLStatus(self, statusbar) - self.encoding_status = EncodingStatus(self, statusbar) - self.cursorpos_status = CursorPositionStatus(self, statusbar) - - layout = QVBoxLayout() - self.dock_toolbar = QToolBar(self) - add_actions(self.dock_toolbar, self.dock_toolbar_actions) - layout.addWidget(self.dock_toolbar) - - self.last_edit_cursor_pos = None - self.cursor_pos_history = [] - self.cursor_pos_index = None - self.__ignore_cursor_position = True - - self.editorstacks = [] - self.last_focus_editorstack = {} - self.editorwindows = [] - self.editorwindows_to_be_created = [] - self.toolbar_list = None - self.menu_list = None - - # Setup new windows: - self.connect(self.main, SIGNAL('all_actions_defined()'), - self.setup_other_windows) - - # Change module completions when PYTHONPATH changes - self.connect(self.main, SIGNAL("pythonpath_changed()"), - self.set_path) - - # Find widget - self.find_widget = FindReplace(self, enable_replace=True) - self.find_widget.hide() - self.connect(self.find_widget, SIGNAL("visibility_changed(bool)"), - lambda vs: self.rehighlight_cells()) - self.register_widget_shortcuts("Editor", self.find_widget) - - # Tabbed editor widget + Find/Replace widget - editor_widgets = QWidget(self) - editor_layout = QVBoxLayout() - editor_layout.setContentsMargins(0, 0, 0, 0) - editor_widgets.setLayout(editor_layout) - self.editorsplitter = EditorSplitter(self, self, - self.stack_menu_actions, first=True) - editor_layout.addWidget(self.editorsplitter) - editor_layout.addWidget(self.find_widget) - - # Splitter: editor widgets (see above) + outline explorer - self.splitter = QSplitter(self) - self.splitter.setContentsMargins(0, 0, 0, 0) - self.splitter.addWidget(editor_widgets) - self.splitter.setStretchFactor(0, 5) - self.splitter.setStretchFactor(1, 1) - layout.addWidget(self.splitter) - self.setLayout(layout) - - # Editor's splitter state - state = self.get_option('splitter_state', None) - if state is not None: - self.splitter.restoreState( QByteArray().fromHex(str(state)) ) - - self.recent_files = self.get_option('recent_files', []) - - self.untitled_num = 0 - - filenames = self.get_option('filenames', []) - if filenames and not ignore_last_opened_files: - 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: - for layout_settings in win_layout: - self.editorwindows_to_be_created.append(layout_settings) - self.set_last_focus_editorstack(self, self.editorstacks[0]) - else: - self.__load_temp_file() - - # Parameters of last file execution: - self.__last_ic_exec = None # internal console - self.__last_ec_exec = None # external console - - self.__ignore_cursor_position = False - current_editor = self.get_current_editor() - if current_editor is not None: - filename = self.get_current_filename() - position = current_editor.get_position('cursor') - self.add_cursor_position_to_history(filename, position) - self.update_cursorpos_actions() - self.set_path() - - def set_projectexplorer(self, projectexplorer): - self.projectexplorer = projectexplorer - - def show_hide_project_explorer(self): - if self.projectexplorer is not None: - dw = self.projectexplorer.dockwidget - if dw.isVisible(): - dw.hide() - else: - dw.show() - dw.raise_() - self.switch_to_plugin() - - def set_outlineexplorer(self, outlineexplorer): - self.outlineexplorer = outlineexplorer - for editorstack in self.editorstacks: - editorstack.set_outlineexplorer(self.outlineexplorer) - self.editorstacks[0].initialize_outlineexplorer() - self.connect(self.outlineexplorer, - SIGNAL("edit_goto(QString,int,QString)"), - lambda filenames, goto, word: - self.load(filenames=filenames, goto=goto, word=word, - editorwindow=self)) - self.connect(self.outlineexplorer, SIGNAL("edit(QString)"), - lambda filenames: - self.load(filenames=filenames, editorwindow=self)) - - def show_hide_outline_explorer(self): - if self.outlineexplorer is not None: - dw = self.outlineexplorer.dockwidget - if dw.isVisible(): - dw.hide() - else: - dw.show() - dw.raise_() - self.switch_to_plugin() - - def set_inspector(self, inspector): - self.inspector = inspector - for editorstack in self.editorstacks: - editorstack.set_inspector(self.inspector) - - #------ Private API -------------------------------------------------------- - def restore_scrollbar_position(self): - """Restoring scrollbar position after main window is visible""" - # Widget is now visible, we may center cursor on top level editor: - try: - self.get_current_editor().centerCursor() - except AttributeError: - pass - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - title = _('Editor') - filename = self.get_current_filename() - if filename: - title += ' - '+to_text_string(filename) - return title - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('edit.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.get_current_editor() - - def visibility_changed(self, enable): - """DockWidget visibility has changed""" - SpyderPluginWidget.visibility_changed(self, enable) - if self.dockwidget.isWindow(): - self.dock_toolbar.show() - else: - self.dock_toolbar.hide() - if enable: - self.refresh_plugin() - - def refresh_plugin(self): - """Refresh editor plugin""" - editorstack = self.get_current_editorstack() - editorstack.refresh() - self.refresh_save_all_action() - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - state = self.splitter.saveState() - self.set_option('splitter_state', qbytearray_to_str(state)) - filenames = [] - editorstack = self.editorstacks[0] - filenames += [finfo.filename for finfo in editorstack.data] - self.set_option('layout_settings', - self.editorsplitter.get_layout_settings()) - self.set_option('windows_layout_settings', - [win.get_layout_settings() for win in self.editorwindows]) - self.set_option('filenames', filenames) - self.set_option('recent_files', self.recent_files) - if not editorstack.save_if_changed(cancelable) and cancelable: - return False - else: - for win in self.editorwindows[:]: - win.close() - return True - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - self.toggle_outline_action = create_action(self, - _("Show/hide outline explorer"), - triggered=self.show_hide_outline_explorer, - context=Qt.WidgetWithChildrenShortcut) - self.register_shortcut(self.toggle_outline_action, context="Editor", - name="Show/hide outline") - self.toggle_project_action = create_action(self, - _("Show/hide project explorer"), - triggered=self.show_hide_project_explorer, - context=Qt.WidgetWithChildrenShortcut) - self.register_shortcut(self.toggle_project_action, context="Editor", - name="Show/hide project explorer") - self.addActions([self.toggle_outline_action, self.toggle_project_action]) - - # ---- File menu and toolbar ---- - self.new_action = create_action(self, _("&New file..."), - icon='filenew.png', tip=_("New file"), - triggered=self.new) - self.register_shortcut(self.new_action, context="Editor", - name="New file") - add_shortcut_to_tooltip(self.new_action, context="Editor", - name="New file") - - self.open_action = create_action(self, _("&Open..."), - icon='fileopen.png', tip=_("Open file"), - triggered=self.load) - self.register_shortcut(self.open_action, context="Editor", - name="Open file") - add_shortcut_to_tooltip(self.open_action, context="Editor", - name="Open file") - - self.revert_action = create_action(self, _("&Revert"), - icon='revert.png', tip=_("Revert file from disk"), - triggered=self.revert) - - self.save_action = create_action(self, _("&Save"), - icon='filesave.png', tip=_("Save file"), - triggered=self.save) - self.register_shortcut(self.save_action, context="Editor", - name="Save file") - add_shortcut_to_tooltip(self.save_action, context="Editor", - name="Save file") - - self.save_all_action = create_action(self, _("Sav&e all"), - icon='save_all.png', tip=_("Save all files"), - triggered=self.save_all) - self.register_shortcut(self.save_all_action, context="Editor", - name="Save all") - add_shortcut_to_tooltip(self.save_all_action, context="Editor", - name="Save all") - - save_as_action = create_action(self, _("Save &as..."), None, - 'filesaveas.png', _("Save current file as..."), - triggered=self.save_as) - print_preview_action = create_action(self, _("Print preview..."), - tip=_("Print preview..."), triggered=self.print_preview) - self.print_action = create_action(self, _("&Print..."), - icon='print.png', tip=_("Print current file..."), - triggered=self.print_file) - self.register_shortcut(self.print_action, context="Editor", - name="Print") - # Shortcut for close_action is defined in widgets/editor.py - self.close_action = create_action(self, _("&Close"), - icon='fileclose.png', tip=_("Close current file"), - triggered=self.close_file) - self.close_all_action = create_action(self, _("C&lose all"), - icon='filecloseall.png', tip=_("Close all opened files"), - triggered=self.close_all_files) - self.register_shortcut(self.close_all_action, context="Editor", - name="Close all") - - # ---- Debug menu ---- - set_clear_breakpoint_action = create_action(self, - _("Set/Clear breakpoint"), - icon=get_icon("breakpoint_big.png"), - triggered=self.set_or_clear_breakpoint, - context=Qt.WidgetShortcut) - self.register_shortcut(set_clear_breakpoint_action, context="Editor", - name="Breakpoint") - set_cond_breakpoint_action = create_action(self, - _("Set/Edit conditional breakpoint"), - icon=get_icon("breakpoint_cond_big.png"), - triggered=self.set_or_edit_conditional_breakpoint, - context=Qt.WidgetShortcut) - self.register_shortcut(set_cond_breakpoint_action, context="Editor", - name="Conditional breakpoint") - clear_all_breakpoints_action = create_action(self, - _("Clear breakpoints in all files"), - triggered=self.clear_all_breakpoints) - breakpoints_menu = QMenu(_("Breakpoints"), self) - add_actions(breakpoints_menu, (set_clear_breakpoint_action, - set_cond_breakpoint_action, None, - clear_all_breakpoints_action)) - self.winpdb_action = create_action(self, _("Debug with winpdb"), - triggered=self.run_winpdb) - self.winpdb_action.setEnabled(WINPDB_PATH is not None and PY2) - self.register_shortcut(self.winpdb_action, context="Editor", - name="Debug with winpdb") - - # --- Debug toolbar --- - debug_action = create_action(self, _("&Debug"), icon='debug.png', - tip=_("Debug file"), - triggered=self.debug_file) - self.register_shortcut(debug_action, context="Editor", name="Debug") - add_shortcut_to_tooltip(debug_action, context="Editor", name="Debug") - - debug_next_action = create_action(self, _("Step"), - icon='arrow-step-over.png', tip=_("Run current line"), - triggered=lambda: self.debug_command("next")) - self.register_shortcut(debug_next_action, "_", "Debug Step Over") - add_shortcut_to_tooltip(debug_next_action, context="_", - name="Debug Step Over") - - debug_continue_action = create_action(self, _("Continue"), - icon='arrow-continue.png', tip=_("Continue execution until " - "next breakpoint"), - triggered=lambda: self.debug_command("continue")) - self.register_shortcut(debug_continue_action, "_", "Debug Continue") - add_shortcut_to_tooltip(debug_continue_action, context="_", - name="Debug Continue") - - debug_step_action = create_action(self, _("Step Into"), - icon='arrow-step-in.png', tip=_("Step into function or method " - "of current line"), - triggered=lambda: self.debug_command("step")) - self.register_shortcut(debug_step_action, "_", "Debug Step Into") - add_shortcut_to_tooltip(debug_step_action, context="_", - name="Debug Step Into") - - debug_return_action = create_action(self, _("Step Return"), - icon='arrow-step-out.png', tip=_("Run until current function " - "or method returns"), - triggered=lambda: self.debug_command("return")) - self.register_shortcut(debug_return_action, "_", "Debug Step Return") - add_shortcut_to_tooltip(debug_return_action, context="_", - name="Debug Step Return") - - debug_exit_action = create_action(self, _("Exit"), - icon='stop_debug.png', tip=_("Exit Debug"), - triggered=lambda: self.debug_command("exit")) - self.register_shortcut(debug_exit_action, "_", "Debug Exit") - add_shortcut_to_tooltip(debug_exit_action, context="_", - name="Debug Exit") - - debug_control_menu_actions = [debug_next_action, - debug_step_action, - debug_return_action, - debug_continue_action, - debug_exit_action] - debug_control_menu = QMenu(_("Debugging control")) - add_actions(debug_control_menu, debug_control_menu_actions) - - # --- Run toolbar --- - run_action = create_action(self, _("&Run"), icon='run.png', - tip=_("Run file"), - triggered=self.run_file) - self.register_shortcut(run_action, context="Editor", name="Run") - add_shortcut_to_tooltip(run_action, context="Editor", name="Run") - - configure_action = create_action(self, - _("&Configure..."), icon='run_settings.png', - tip=_("Run settings"), - menurole=QAction.NoRole, - triggered=self.edit_run_configurations) - self.register_shortcut(configure_action, context="Editor", - name="Configure") - add_shortcut_to_tooltip(configure_action, context="Editor", - name="Configure") - - re_run_action = create_action(self, - _("Re-run &last script"), icon='run_again.png', - tip=_("Run again last file"), - triggered=self.re_run_file) - self.register_shortcut(re_run_action, context="Editor", - name="Re-run last script") - add_shortcut_to_tooltip(re_run_action, context="Editor", - name="Re-run last script") - - run_selected_action = create_action(self, _("Run &selection or " - "current line"), - icon='run_selection.png', - tip=_("Run selection or " - "current line"), - triggered=self.run_selection) - self.register_shortcut(run_selected_action, context="Editor", - name="Run selection") - - if sys.platform == 'darwin': - run_cell_sc = Qt.META + Qt.Key_Enter - else: - run_cell_sc = Qt.CTRL + Qt.Key_Enter - run_cell_advance_sc = Qt.SHIFT + Qt.Key_Enter - - run_cell_action = create_action(self, - _("Run cell"), icon='run_cell.png', - shortcut=QKeySequence(run_cell_sc), - tip=_("Run current cell (Ctrl+Enter)\n" - "[Use #%% to create cells]"), - triggered=self.run_cell) - - run_cell_advance_action = create_action(self, - _("Run cell and advance"), - icon='run_cell_advance.png', - shortcut=QKeySequence(run_cell_advance_sc), - tip=_("Run current cell and go to " - "the next one (Shift+Enter)"), - triggered=self.run_cell_and_advance) - - # --- Source code Toolbar --- - self.todo_list_action = create_action(self, - _("Show todo list"), icon='todo_list.png', - tip=_("Show TODO/FIXME/XXX/HINT/TIP/@todo comments list"), - triggered=self.go_to_next_todo) - self.todo_menu = QMenu(self) - self.todo_list_action.setMenu(self.todo_menu) - self.connect(self.todo_menu, SIGNAL("aboutToShow()"), - self.update_todo_menu) - - self.warning_list_action = create_action(self, - _("Show warning/error list"), icon='wng_list.png', - tip=_("Show code analysis warnings/errors"), - triggered=self.go_to_next_warning) - self.warning_menu = QMenu(self) - self.warning_list_action.setMenu(self.warning_menu) - self.connect(self.warning_menu, SIGNAL("aboutToShow()"), - self.update_warning_menu) - self.previous_warning_action = create_action(self, - _("Previous warning/error"), icon='prev_wng.png', - tip=_("Go to previous code analysis warning/error"), - triggered=self.go_to_previous_warning) - self.next_warning_action = create_action(self, - _("Next warning/error"), icon='next_wng.png', - tip=_("Go to next code analysis warning/error"), - triggered=self.go_to_next_warning) - - self.previous_edit_cursor_action = create_action(self, - _("Last edit location"), icon='last_edit_location.png', - tip=_("Go to last edit location"), - triggered=self.go_to_last_edit_location) - self.register_shortcut(self.previous_edit_cursor_action, - context="Editor", - name="Last edit location") - self.previous_cursor_action = create_action(self, - _("Previous cursor position"), icon='prev_cursor.png', - tip=_("Go to previous cursor position"), - triggered=self.go_to_previous_cursor_position) - self.register_shortcut(self.previous_cursor_action, - context="Editor", - name="Previous cursor position") - self.next_cursor_action = create_action(self, - _("Next cursor position"), icon='next_cursor.png', - tip=_("Go to next cursor position"), - triggered=self.go_to_next_cursor_position) - self.register_shortcut(self.next_cursor_action, - context="Editor", name="Next cursor position") - - # --- Edit Toolbar --- - self.toggle_comment_action = create_action(self, - _("Comment")+"/"+_("Uncomment"), icon='comment.png', - tip=_("Comment current line or selection"), - triggered=self.toggle_comment, context=Qt.WidgetShortcut) - self.register_shortcut(self.toggle_comment_action, context="Editor", - name="Toggle comment") - blockcomment_action = create_action(self, _("Add &block comment"), - tip=_("Add block comment around " - "current line or selection"), - triggered=self.blockcomment, context=Qt.WidgetShortcut) - self.register_shortcut(blockcomment_action, context="Editor", - name="Blockcomment") - unblockcomment_action = create_action(self, - _("R&emove block comment"), - tip = _("Remove comment block around " - "current line or selection"), - triggered=self.unblockcomment, context=Qt.WidgetShortcut) - self.register_shortcut(unblockcomment_action, context="Editor", - name="Unblockcomment") - - # ---------------------------------------------------------------------- - # The following action shortcuts are hard-coded in CodeEditor - # keyPressEvent handler (the shortcut is here only to inform user): - # (context=Qt.WidgetShortcut -> disable shortcut for other widgets) - self.indent_action = create_action(self, - _("Indent"), "Tab", icon='indent.png', - tip=_("Indent current line or selection"), - triggered=self.indent, context=Qt.WidgetShortcut) - self.unindent_action = create_action(self, - _("Unindent"), "Shift+Tab", icon='unindent.png', - tip=_("Unindent current line or selection"), - triggered=self.unindent, context=Qt.WidgetShortcut) - # ---------------------------------------------------------------------- - - self.win_eol_action = create_action(self, - _("Carriage return and line feed (Windows)"), - toggled=lambda: self.toggle_eol_chars('nt')) - self.linux_eol_action = create_action(self, - _("Line feed (UNIX)"), - toggled=lambda: self.toggle_eol_chars('posix')) - self.mac_eol_action = create_action(self, - _("Carriage return (Mac)"), - toggled=lambda: self.toggle_eol_chars('mac')) - eol_action_group = QActionGroup(self) - eol_actions = (self.win_eol_action, self.linux_eol_action, - self.mac_eol_action) - add_actions(eol_action_group, eol_actions) - eol_menu = QMenu(_("Convert end-of-line characters"), self) - add_actions(eol_menu, eol_actions) - - trailingspaces_action = create_action(self, - _("Remove trailing spaces"), - triggered=self.remove_trailing_spaces) - self.showblanks_action = create_action(self, _("Show blank spaces"), - toggled=self.toggle_show_blanks) - fixindentation_action = create_action(self, _("Fix indentation"), - tip=_("Replace tab characters by space characters"), - triggered=self.fix_indentation) - - gotoline_action = create_action(self, _("Go to line..."), - icon=get_icon("gotoline.png"), - triggered=self.go_to_line, - context=Qt.WidgetShortcut) - self.register_shortcut(gotoline_action, context="Editor", - name="Go to line") - - workdir_action = create_action(self, - _("Set console working directory"), - icon=get_std_icon('DirOpenIcon'), - tip=_("Set current console (and file explorer) working " - "directory to current script directory"), - triggered=self.__set_workdir) - - self.max_recent_action = create_action(self, - _("Maximum number of recent files..."), - triggered=self.change_max_recent_files) - self.clear_recent_action = create_action(self, - _("Clear this list"), tip=_("Clear recent files list"), - triggered=self.clear_recent_files) - self.recent_file_menu = QMenu(_("Open &recent"), self) - self.connect(self.recent_file_menu, SIGNAL("aboutToShow()"), - self.update_recent_file_menu) - - file_menu_actions = [self.new_action, self.open_action, - self.recent_file_menu, self.save_action, - self.save_all_action, save_as_action, - self.revert_action, - None, print_preview_action, self.print_action, - None, self.close_action, - self.close_all_action, None] - self.main.file_menu_actions += file_menu_actions - file_toolbar_actions = [self.new_action, self.open_action, - self.save_action, self.save_all_action] - self.main.file_toolbar_actions += file_toolbar_actions - - self.edit_menu_actions = [self.toggle_comment_action, - blockcomment_action, unblockcomment_action, - self.indent_action, self.unindent_action] - self.main.edit_menu_actions += [None]+self.edit_menu_actions - edit_toolbar_actions = [self.toggle_comment_action, - self.unindent_action, self.indent_action] - self.main.edit_toolbar_actions += edit_toolbar_actions - - self.search_menu_actions = [gotoline_action] - self.main.search_menu_actions += self.search_menu_actions - self.main.search_toolbar_actions += [gotoline_action] - - # ---- Run menu/toolbar construction ---- - run_menu_actions = [run_action, run_cell_action, - run_cell_advance_action, None, run_selected_action, - re_run_action, configure_action, None] - self.main.run_menu_actions += run_menu_actions - run_toolbar_actions = [run_action, run_cell_action, - run_cell_advance_action, re_run_action, - configure_action] - self.main.run_toolbar_actions += run_toolbar_actions - - # ---- Debug menu/toolbar construction ---- - # The breakpoints plugin is expecting that - # breakpoints_menu will be the first QMenu in debug_menu_actions - # If breakpoints_menu must be moved below another QMenu in the list - # please update the breakpoints plugin accordingly. - debug_menu_actions = [debug_action, breakpoints_menu, - debug_control_menu, None, self.winpdb_action] - self.main.debug_menu_actions += debug_menu_actions - debug_toolbar_actions = [debug_action, debug_next_action, - debug_step_action, debug_return_action, - debug_continue_action, debug_exit_action] - self.main.debug_toolbar_actions += debug_toolbar_actions - - source_menu_actions = [eol_menu, self.showblanks_action, - trailingspaces_action, fixindentation_action] - self.main.source_menu_actions += source_menu_actions - - source_toolbar_actions = [self.todo_list_action, - self.warning_list_action, self.previous_warning_action, - self.next_warning_action, None, - self.previous_edit_cursor_action, - self.previous_cursor_action, self.next_cursor_action] - self.main.source_toolbar_actions += source_toolbar_actions - - self.dock_toolbar_actions = file_toolbar_actions + [None] + \ - source_toolbar_actions + [None] + \ - run_toolbar_actions + [None] + \ - debug_toolbar_actions + [None] + \ - edit_toolbar_actions - self.pythonfile_dependent_actions = [run_action, configure_action, - set_clear_breakpoint_action, set_cond_breakpoint_action, - debug_action, run_selected_action, run_cell_action, - run_cell_advance_action, blockcomment_action, - unblockcomment_action, self.winpdb_action] - self.file_dependent_actions = self.pythonfile_dependent_actions + \ - [self.save_action, save_as_action, print_preview_action, - self.print_action, self.save_all_action, gotoline_action, - workdir_action, self.close_action, self.close_all_action, - self.toggle_comment_action, self.revert_action, - self.indent_action, self.unindent_action] - self.stack_menu_actions = [gotoline_action, workdir_action] - - return self.file_dependent_actions - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self.main, SIGNAL('restore_scrollbar_position()'), - self.restore_scrollbar_position) - self.connect(self.main.console, - SIGNAL("edit_goto(QString,int,QString)"), self.load) - self.connect(self, SIGNAL('exec_in_extconsole(QString,bool)'), - self.main.execute_in_external_console) - self.connect(self, SIGNAL('redirect_stdio(bool)'), - self.main.redirect_internalshell_stdio) - self.connect(self, SIGNAL("open_dir(QString)"), - self.main.workingdirectory.chdir) - self.set_inspector(self.main.inspector) - if self.main.outlineexplorer is not None: - self.set_outlineexplorer(self.main.outlineexplorer) - editorstack = self.get_current_editorstack() - if not editorstack.data: - self.__load_temp_file() - self.main.add_dockwidget(self) - - - #------ Focus tabwidget - def __get_focus_editorstack(self): - fwidget = QApplication.focusWidget() - if isinstance(fwidget, EditorStack): - return fwidget - else: - for editorstack in self.editorstacks: - if editorstack.isAncestorOf(fwidget): - return editorstack - - def set_last_focus_editorstack(self, editorwindow, editorstack): - self.last_focus_editorstack[editorwindow] = editorstack - self.last_focus_editorstack[None] = editorstack # very last editorstack - - def get_last_focus_editorstack(self, editorwindow=None): - return self.last_focus_editorstack[editorwindow] - - def remove_last_focus_editorstack(self, editorstack): - for editorwindow, widget in list(self.last_focus_editorstack.items()): - if widget is editorstack: - self.last_focus_editorstack[editorwindow] = None - - def save_focus_editorstack(self): - editorstack = self.__get_focus_editorstack() - if editorstack is not None: - for win in [self]+self.editorwindows: - if win.isAncestorOf(editorstack): - self.set_last_focus_editorstack(win, editorstack) - - - #------ Handling editorstacks - def register_editorstack(self, editorstack): - self.editorstacks.append(editorstack) - self.register_widget_shortcuts("Editor", editorstack) - - if self.isAncestorOf(editorstack): - # editorstack is a child of the Editor plugin - self.set_last_focus_editorstack(self, editorstack) - editorstack.set_closable( len(self.editorstacks) > 1 ) - if self.outlineexplorer is not None: - editorstack.set_outlineexplorer(self.outlineexplorer) - editorstack.set_find_widget(self.find_widget) - self.connect(editorstack, SIGNAL('reset_statusbar()'), - self.readwrite_status.hide) - self.connect(editorstack, SIGNAL('reset_statusbar()'), - self.encoding_status.hide) - self.connect(editorstack, SIGNAL('reset_statusbar()'), - self.cursorpos_status.hide) - self.connect(editorstack, SIGNAL('readonly_changed(bool)'), - self.readwrite_status.readonly_changed) - self.connect(editorstack, SIGNAL('encoding_changed(QString)'), - self.encoding_status.encoding_changed) - self.connect(editorstack, - SIGNAL('editor_cursor_position_changed(int,int)'), - self.cursorpos_status.cursor_position_changed) - self.connect(editorstack, SIGNAL('refresh_eol_chars(QString)'), - self.eol_status.eol_changed) - - editorstack.set_inspector(self.inspector) - editorstack.set_io_actions(self.new_action, self.open_action, - self.save_action, self.revert_action) - editorstack.set_tempfile_path(self.TEMPFILE_PATH) - settings = ( - ('set_pyflakes_enabled', 'code_analysis/pyflakes'), - ('set_pep8_enabled', 'code_analysis/pep8'), - ('set_todolist_enabled', 'todo_list'), - ('set_realtime_analysis_enabled', 'realtime_analysis'), - ('set_realtime_analysis_timeout', 'realtime_analysis/timeout'), - ('set_blanks_enabled', 'blank_spaces'), - ('set_linenumbers_enabled', 'line_numbers'), - ('set_edgeline_enabled', 'edge_line'), - ('set_edgeline_column', 'edge_line_column'), - ('set_codecompletion_auto_enabled', 'codecompletion/auto'), - ('set_codecompletion_case_enabled', 'codecompletion/case_sensitive'), - ('set_codecompletion_enter_enabled', 'codecompletion/enter_key'), - ('set_calltips_enabled', 'calltips'), - ('set_go_to_definition_enabled', 'go_to_definition'), - ('set_focus_to_editor', 'focus_to_editor'), - ('set_close_parentheses_enabled', 'close_parentheses'), - ('set_close_quotes_enabled', 'close_quotes'), - ('set_add_colons_enabled', 'add_colons'), - ('set_auto_unindent_enabled', 'auto_unindent'), - ('set_indent_chars', 'indent_chars'), - ('set_tab_stop_width', 'tab_stop_width'), - ('set_wrap_enabled', 'wrap'), - ('set_tabmode_enabled', 'tab_always_indent'), - ('set_intelligent_backspace_enabled', 'intelligent_backspace'), - ('set_highlight_current_line_enabled', 'highlight_current_line'), - ('set_highlight_current_cell_enabled', 'highlight_current_cell'), - ('set_occurence_highlighting_enabled', 'occurence_highlighting'), - ('set_occurence_highlighting_timeout', 'occurence_highlighting/timeout'), - ('set_checkeolchars_enabled', 'check_eol_chars'), - ('set_fullpath_sorting_enabled', 'fullpath_sorting'), - ('set_tabbar_visible', 'show_tab_bar'), - ('set_always_remove_trailing_spaces', 'always_remove_trailing_spaces'), - ) - for method, setting in settings: - getattr(editorstack, method)(self.get_option(setting)) - editorstack.set_inspector_enabled(CONF.get('inspector', - 'connect/editor')) - color_scheme = get_color_scheme(self.get_option('color_scheme_name')) - editorstack.set_default_font(self.get_plugin_font(), color_scheme) - - self.connect(editorstack, SIGNAL('starting_long_process(QString)'), - self.starting_long_process) - self.connect(editorstack, SIGNAL('ending_long_process(QString)'), - self.ending_long_process) - - # Redirect signals - self.connect(editorstack, SIGNAL('redirect_stdio(bool)'), - lambda state: - self.emit(SIGNAL('redirect_stdio(bool)'), state)) - self.connect(editorstack, SIGNAL('exec_in_extconsole(QString,bool)'), - lambda text, option: self.emit( - SIGNAL('exec_in_extconsole(QString,bool)'), text, option)) - self.connect(editorstack, SIGNAL("update_plugin_title()"), - lambda: self.emit(SIGNAL("update_plugin_title()"))) - - self.connect(editorstack, SIGNAL("editor_focus_changed()"), - self.save_focus_editorstack) - self.connect(editorstack, SIGNAL('editor_focus_changed()'), - self.main.plugin_focus_changed) - - self.connect(editorstack, SIGNAL('zoom_in()'), lambda: self.zoom(1)) - self.connect(editorstack, SIGNAL('zoom_out()'), lambda: self.zoom(-1)) - self.connect(editorstack, SIGNAL('zoom_reset()'), lambda: self.zoom(0)) - self.connect(editorstack, SIGNAL('sig_new_file()'), self.new) - - self.connect(editorstack, SIGNAL('close_file(QString,int)'), - self.close_file_in_all_editorstacks) - self.connect(editorstack, SIGNAL('file_saved(QString,int,QString)'), - self.file_saved_in_editorstack) - self.connect(editorstack, - SIGNAL('file_renamed_in_data(QString,int,QString)'), - self.file_renamed_in_data_in_editorstack) - - self.connect(editorstack, SIGNAL("create_new_window()"), - self.create_new_window) - - self.connect(editorstack, SIGNAL('opened_files_list_changed()'), - self.opened_files_list_changed) - self.connect(editorstack, SIGNAL('analysis_results_changed()'), - self.analysis_results_changed) - self.connect(editorstack, SIGNAL('todo_results_changed()'), - self.todo_results_changed) - self.connect(editorstack, SIGNAL('update_code_analysis_actions()'), - self.update_code_analysis_actions) - self.connect(editorstack, SIGNAL('update_code_analysis_actions()'), - self.update_todo_actions) - self.connect(editorstack, - SIGNAL('refresh_file_dependent_actions()'), - self.refresh_file_dependent_actions) - self.connect(editorstack, SIGNAL('refresh_save_all_action()'), - self.refresh_save_all_action) - self.connect(editorstack, SIGNAL('refresh_eol_chars(QString)'), - self.refresh_eol_chars) - - self.connect(editorstack, SIGNAL("save_breakpoints(QString,QString)"), - self.save_breakpoints) - - self.connect(editorstack, SIGNAL('text_changed_at(QString,int)'), - self.text_changed_at) - self.connect(editorstack, SIGNAL('current_file_changed(QString,int)'), - self.current_file_changed) - - self.connect(editorstack, SIGNAL('plugin_load(QString)'), self.load) - self.connect(editorstack, SIGNAL("edit_goto(QString,int,QString)"), - self.load) - - def unregister_editorstack(self, editorstack): - """Removing editorstack only if it's not the last remaining""" - self.remove_last_focus_editorstack(editorstack) - if len(self.editorstacks) > 1: - index = self.editorstacks.index(editorstack) - self.editorstacks.pop(index) - return True - else: - # editorstack was not removed! - return False - - def clone_editorstack(self, editorstack): - editorstack.clone_from(self.editorstacks[0]) - for finfo in editorstack.data: - self.register_widget_shortcuts("Editor", finfo.editor) - - @Slot(int, int) - def close_file_in_all_editorstacks(self, editorstack_id_str, index): - for editorstack in self.editorstacks: - if str(id(editorstack)) != editorstack_id_str: - editorstack.blockSignals(True) - editorstack.close_file(index, force=True) - editorstack.blockSignals(False) - - @Slot(int, int) - def file_saved_in_editorstack(self, editorstack_id_str, index, filename): - """A file was saved in editorstack, this notifies others""" - for editorstack in self.editorstacks: - if str(id(editorstack)) != editorstack_id_str: - editorstack.file_saved_in_other_editorstack(index, filename) - - @Slot(int, int) - def file_renamed_in_data_in_editorstack(self, editorstack_id_str, - index, filename): - """A file was renamed in data in editorstack, this notifies others""" - for editorstack in self.editorstacks: - if str(id(editorstack)) != editorstack_id_str: - editorstack.rename_in_data(index, filename) - - - #------ Handling editor windows - def setup_other_windows(self): - """Setup toolbars and menus for 'New window' instances""" - self.toolbar_list = ( - (_("File toolbar"), self.main.file_toolbar_actions), - (_("Search toolbar"), self.main.search_menu_actions), - (_("Source toolbar"), self.main.source_toolbar_actions), - (_("Run toolbar"), self.main.run_toolbar_actions), - (_("Debug toolbar"), self.main.debug_toolbar_actions), - (_("Edit toolbar"), self.main.edit_toolbar_actions), - ) - self.menu_list = ( - (_("&File"), self.main.file_menu_actions), - (_("&Edit"), self.main.edit_menu_actions), - (_("&Search"), self.main.search_menu_actions), - (_("Sour&ce"), self.main.source_menu_actions), - (_("&Run"), self.main.run_menu_actions), - (_("&Tools"), self.main.tools_menu_actions), - (_("?"), self.main.help_menu_actions), - ) - # Create pending new windows: - for layout_settings in self.editorwindows_to_be_created: - win = self.create_new_window() - win.set_layout_settings(layout_settings) - - def create_new_window(self): - oe_options = self.outlineexplorer.get_options() - fullpath_sorting=self.get_option('fullpath_sorting', True), - window = EditorMainWindow(self, self.stack_menu_actions, - self.toolbar_list, self.menu_list, - show_fullpath=oe_options['show_fullpath'], - fullpath_sorting=fullpath_sorting, - show_all_files=oe_options['show_all_files'], - show_comments=oe_options['show_comments']) - window.resize(self.size()) - window.show() - self.register_editorwindow(window) - self.connect(window, SIGNAL("destroyed()"), - lambda win=window: self.unregister_editorwindow(win)) - return window - - def register_editorwindow(self, window): - self.editorwindows.append(window) - - def unregister_editorwindow(self, window): - self.editorwindows.pop(self.editorwindows.index(window)) - - - #------ Accessors - def get_filenames(self): - return [finfo.filename for finfo in self.editorstacks[0].data] - - def get_filename_index(self, filename): - return self.editorstacks[0].has_filename(filename) - - def get_current_editorstack(self, editorwindow=None): - if self.editorstacks is not None: - if len(self.editorstacks) == 1: - return self.editorstacks[0] - else: - editorstack = self.__get_focus_editorstack() - if editorstack is None or editorwindow is not None: - return self.get_last_focus_editorstack(editorwindow) - return editorstack - - def get_current_editor(self): - editorstack = self.get_current_editorstack() - if editorstack is not None: - return editorstack.get_current_editor() - - def get_current_finfo(self): - editorstack = self.get_current_editorstack() - if editorstack is not None: - return editorstack.get_current_finfo() - - def get_current_filename(self): - editorstack = self.get_current_editorstack() - if editorstack is not None: - return editorstack.get_current_filename() - - def is_file_opened(self, filename=None): - return self.editorstacks[0].is_file_opened(filename) - - def set_current_filename(self, filename, editorwindow=None): - """Set focus to *filename* if this file has been opened - Return the editor instance associated to *filename*""" - editorstack = self.get_current_editorstack(editorwindow) - return editorstack.set_current_filename(filename) - - def set_path(self): - for finfo in self.editorstacks[0].data: - finfo.path = self.main.get_spyder_pythonpath() - - #------ Refresh methods - def refresh_file_dependent_actions(self): - """Enable/disable file dependent actions - (only if dockwidget is visible)""" - if self.dockwidget and self.dockwidget.isVisible(): - enable = self.get_current_editor() is not None - for action in self.file_dependent_actions: - action.setEnabled(enable) - - def refresh_save_all_action(self): - state = False - editorstack = self.editorstacks[0] - if editorstack.get_stack_count() > 1: - state = state or any([finfo.editor.document().isModified() - for finfo in editorstack.data]) - self.save_all_action.setEnabled(state) - - def update_warning_menu(self): - """Update warning list menu""" - editorstack = self.get_current_editorstack() - check_results = editorstack.get_analysis_results() - self.warning_menu.clear() - filename = self.get_current_filename() - for message, line_number in check_results: - error = 'syntax' in message - text = message[:1].upper()+message[1:] - icon = get_icon('error.png' if error else 'warning.png') - slot = lambda _l=line_number: self.load(filename, goto=_l) - action = create_action(self, text=text, icon=icon, triggered=slot) - self.warning_menu.addAction(action) - - def analysis_results_changed(self): - """ - Synchronize analysis results between editorstacks - Refresh analysis navigation buttons - """ - editorstack = self.get_current_editorstack() - results = editorstack.get_analysis_results() - index = editorstack.get_stack_index() - if index != -1: - for other_editorstack in self.editorstacks: - if other_editorstack is not editorstack: - other_editorstack.set_analysis_results(index, results) - self.update_code_analysis_actions() - - def update_todo_menu(self): - """Update todo list menu""" - editorstack = self.get_current_editorstack() - results = editorstack.get_todo_results() - self.todo_menu.clear() - filename = self.get_current_filename() - for text, line0 in results: - icon = get_icon('todo.png') - slot = lambda _l=line0: self.load(filename, goto=_l) - action = create_action(self, text=text, icon=icon, triggered=slot) - self.todo_menu.addAction(action) - self.update_todo_actions() - - def todo_results_changed(self): - """ - Synchronize todo results between editorstacks - Refresh todo list navigation buttons - """ - editorstack = self.get_current_editorstack() - results = editorstack.get_todo_results() - index = editorstack.get_stack_index() - if index != -1: - for other_editorstack in self.editorstacks: - if other_editorstack is not editorstack: - other_editorstack.set_todo_results(index, results) - self.update_todo_actions() - - def refresh_eol_chars(self, os_name): - os_name = to_text_string(os_name) - self.__set_eol_chars = False - if os_name == 'nt': - self.win_eol_action.setChecked(True) - elif os_name == 'posix': - self.linux_eol_action.setChecked(True) - else: - self.mac_eol_action.setChecked(True) - self.__set_eol_chars = True - - - #------ Slots - def opened_files_list_changed(self): - """ - Opened files list has changed: - --> open/close file action - --> modification ('*' added to title) - --> current edited file has changed - """ - # Refresh Python file dependent actions: - editor = self.get_current_editor() - if editor: - enable = editor.is_python() - for action in self.pythonfile_dependent_actions: - if action is self.winpdb_action: - action.setEnabled(enable and WINPDB_PATH is not None) - else: - action.setEnabled(enable) - - def update_code_analysis_actions(self): - editorstack = self.get_current_editorstack() - results = editorstack.get_analysis_results() - - # Update code analysis buttons - state = (self.get_option('code_analysis/pyflakes') \ - or self.get_option('code_analysis/pep8')) \ - and results is not None and len(results) - for action in (self.warning_list_action, self.previous_warning_action, - self.next_warning_action): - action.setEnabled(state) - - def update_todo_actions(self): - editorstack = self.get_current_editorstack() - results = editorstack.get_todo_results() - state = self.get_option('todo_list') \ - and results is not None and len(results) - self.todo_list_action.setEnabled(state) - - def rehighlight_cells(self): - """Rehighlight cells of current editor""" - editor = self.get_current_editor() - editor.rehighlight_cells() - QApplication.processEvents() - - - #------ Breakpoints - def save_breakpoints(self, filename, breakpoints): - filename = to_text_string(filename) - breakpoints = to_text_string(breakpoints) - filename = osp.normpath(osp.abspath(filename)) - if breakpoints: - breakpoints = eval(breakpoints) - else: - breakpoints = [] - save_breakpoints(filename, breakpoints) - self.emit(SIGNAL("breakpoints_saved()")) - - #------ File I/O - def __load_temp_file(self): - """Load temporary file from a text file in user home directory""" - if not osp.isfile(self.TEMPFILE_PATH): - # Creating temporary file - default = ['# -*- coding: utf-8 -*-', - '"""', _("Spyder Editor"), '', - _("This is a temporary script file."), - '"""', '', ''] - text = os.linesep.join([encoding.to_unicode(qstr) - for qstr in default]) - encoding.write(to_text_string(text), self.TEMPFILE_PATH, 'utf-8') - self.load(self.TEMPFILE_PATH) - - def __set_workdir(self): - """Set current script directory as working directory""" - fname = self.get_current_filename() - if fname is not None: - directory = osp.dirname(osp.abspath(fname)) - self.emit(SIGNAL("open_dir(QString)"), directory) - - def __add_recent_file(self, fname): - """Add to recent file list""" - if fname is None: - return - if fname in self.recent_files: - self.recent_files.remove(fname) - self.recent_files.insert(0, fname) - if len(self.recent_files) > self.get_option('max_recent_files'): - self.recent_files.pop(-1) - - def _clone_file_everywhere(self, finfo): - """Clone file (*src_editor* widget) in all editorstacks - Cloning from the first editorstack in which every single new editor - is created (when loading or creating a new file)""" - for editorstack in self.editorstacks[1:]: - editor = editorstack.clone_editor_from(finfo, set_current=False) - self.register_widget_shortcuts("Editor", editor) - - def new(self, fname=None, editorstack=None, text=None): - """ - Create a new file - Untitled - - fname=None --> fname will be 'untitledXX.py' but do not create file - fname= --> create file - """ - # If no text is provided, create default content - if text is None: - default_content = True - text, enc = encoding.read(self.TEMPLATE_PATH) - enc_match = re.search('-*- coding: ?([a-z0-9A-Z\-]*) -*-', text) - if enc_match: - enc = enc_match.group(1) - # Initialize template variables - # Windows - username = encoding.to_unicode_from_fs(os.environ.get('USERNAME', - '')) - # Linux, Mac OS X - if not username: - username = encoding.to_unicode_from_fs(os.environ.get('USER', - '-')) - VARS = { - 'date': time.ctime(), - 'username': username, - } - try: - text = text % VARS - except: - pass - else: - default_content = False - enc = encoding.read(self.TEMPLATE_PATH)[1] - - create_fname = lambda n: to_text_string(_("untitled")) + ("%d.py" % n) - # Creating editor widget - if editorstack is None: - current_es = self.get_current_editorstack() - else: - current_es = editorstack - created_from_here = fname is None - if created_from_here: - while True: - fname = create_fname(self.untitled_num) - self.untitled_num += 1 - if not osp.isfile(fname): - break - basedir = getcwd() - if CONF.get('workingdir', 'editor/new/browse_scriptdir'): - c_fname = self.get_current_filename() - if c_fname is not None and c_fname != self.TEMPFILE_PATH: - basedir = osp.dirname(c_fname) - fname = osp.abspath(osp.join(basedir, fname)) - else: - # QString when triggered by a Qt signal - fname = osp.abspath(to_text_string(fname)) - index = current_es.has_filename(fname) - if index and not current_es.close_file(index): - return - - # Creating the editor widget in the first editorstack (the one that - # can't be destroyed), then cloning this editor widget in all other - # editorstacks: - finfo = self.editorstacks[0].new(fname, enc, text, default_content) - finfo.path = self.main.get_spyder_pythonpath() - self._clone_file_everywhere(finfo) - current_editor = current_es.set_current_filename(finfo.filename) - self.register_widget_shortcuts("Editor", current_editor) - if not created_from_here: - self.save(force=True) - - def edit_template(self): - """Edit new file template""" - self.load(self.TEMPLATE_PATH) - - def update_recent_file_menu(self): - """Update recent file menu""" - recent_files = [] - for fname in self.recent_files: - if not self.is_file_opened(fname) and osp.isfile(fname): - recent_files.append(fname) - self.recent_file_menu.clear() - if recent_files: - for i, fname in enumerate(recent_files): - if i < 10: - accel = "%d" % ((i+1) % 10) - else: - accel = chr(i-10+ord('a')) - action = create_action(self, "&%s %s" % (accel, fname), - icon=get_filetype_icon(fname), - triggered=self.load) - action.setData(to_qvariant(fname)) - self.recent_file_menu.addAction(action) - self.clear_recent_action.setEnabled(len(recent_files) > 0) - add_actions(self.recent_file_menu, (None, self.max_recent_action, - self.clear_recent_action)) - - def clear_recent_files(self): - """Clear recent files list""" - self.recent_files = [] - - def change_max_recent_files(self): - "Change max recent files entries""" - editorstack = self.get_current_editorstack() - mrf, valid = QInputDialog.getInteger(editorstack, _('Editor'), - _('Maximum number of recent files'), - self.get_option('max_recent_files'), 1, 35) - if valid: - self.set_option('max_recent_files', mrf) - - def load(self, filenames=None, goto=None, word='', editorwindow=None, - processevents=True): - """ - Load a text file - editorwindow: load in this editorwindow (useful when clicking on - outline explorer with multiple editor windows) - processevents: determines if processEvents() should be called at the - end of this method (set to False to prevent keyboard events from - creeping through to the editor during debugging) - """ - editor0 = self.get_current_editor() - if editor0 is not None: - position0 = editor0.get_position('cursor') - filename0 = self.get_current_filename() - else: - position0, filename0 = None, None - if not filenames: - # Recent files action - action = self.sender() - if isinstance(action, QAction): - filenames = from_qvariant(action.data(), to_text_string) - if not filenames: - basedir = getcwd() - if CONF.get('workingdir', 'editor/open/browse_scriptdir'): - c_fname = self.get_current_filename() - if c_fname is not None and c_fname != self.TEMPFILE_PATH: - basedir = osp.dirname(c_fname) - self.emit(SIGNAL('redirect_stdio(bool)'), False) - parent_widget = self.get_current_editorstack() - if filename0 is not None: - selectedfilter = get_filter(EDIT_FILETYPES, - osp.splitext(filename0)[1]) - else: - selectedfilter = '' - filenames, _selfilter = getopenfilenames(parent_widget, - _("Open file"), basedir, EDIT_FILTERS, - selectedfilter=selectedfilter) - self.emit(SIGNAL('redirect_stdio(bool)'), True) - if filenames: - filenames = [osp.normpath(fname) for fname in filenames] - if CONF.get('workingdir', 'editor/open/auto_set_to_basedir'): - directory = osp.dirname(filenames[0]) - self.emit(SIGNAL("open_dir(QString)"), directory) - else: - return - - focus_widget = QApplication.focusWidget() - if self.dockwidget and not self.ismaximized and\ - (not self.dockwidget.isAncestorOf(focus_widget)\ - and not isinstance(focus_widget, CodeEditor)): - self.dockwidget.setVisible(True) - self.dockwidget.setFocus() - self.dockwidget.raise_() - - def _convert(fname): - fname = osp.abspath(encoding.to_unicode_from_fs(fname)) - if os.name == 'nt' and len(fname) >= 2 and fname[1] == ':': - fname = fname[0].upper()+fname[1:] - return fname - - if hasattr(filenames, 'replaceInStrings'): - # This is a QStringList instance (PyQt API #1), converting to list: - filenames = list(filenames) - if not isinstance(filenames, list): - filenames = [_convert(filenames)] - else: - filenames = [_convert(fname) for fname in list(filenames)] - if isinstance(goto, int): - goto = [goto] - elif goto is not None and len(goto) != len(filenames): - goto = None - - for index, filename in enumerate(filenames): - # -- Do not open an already opened file - current_editor = self.set_current_filename(filename, editorwindow) - if current_editor is None: - # -- Not a valid filename: - if not osp.isfile(filename): - continue - # -- - current_es = self.get_current_editorstack(editorwindow) - - # Creating the editor widget in the first editorstack (the one - # that can't be destroyed), then cloning this editor widget in - # all other editorstacks: - finfo = self.editorstacks[0].load(filename, set_current=False) - finfo.path = self.main.get_spyder_pythonpath() - self._clone_file_everywhere(finfo) - current_editor = current_es.set_current_filename(filename) - current_editor.set_breakpoints(load_breakpoints(filename)) - self.register_widget_shortcuts("Editor", current_editor) - - current_es.analyze_script() - self.__add_recent_file(filename) - if goto is not None: # 'word' is assumed to be None as well - current_editor.go_to_line(goto[index], word=word) - position = current_editor.get_position('cursor') - self.cursor_moved(filename0, position0, filename, position) - current_editor.clearFocus() - current_editor.setFocus() - current_editor.window().raise_() - if processevents: - QApplication.processEvents() - - def print_file(self): - """Print current file""" - editor = self.get_current_editor() - filename = self.get_current_filename() - printer = Printer(mode=QPrinter.HighResolution, - header_font=self.get_plugin_font('printer_header')) - printDialog = QPrintDialog(printer, editor) - if editor.has_selected_text(): - printDialog.addEnabledOption(QAbstractPrintDialog.PrintSelection) - self.emit(SIGNAL('redirect_stdio(bool)'), False) - answer = printDialog.exec_() - self.emit(SIGNAL('redirect_stdio(bool)'), True) - if answer == QDialog.Accepted: - self.starting_long_process(_("Printing...")) - printer.setDocName(filename) - editor.print_(printer) - self.ending_long_process() - - def print_preview(self): - """Print preview for current file""" - from spyderlib.qt.QtGui import QPrintPreviewDialog - editor = self.get_current_editor() - printer = Printer(mode=QPrinter.HighResolution, - header_font=self.get_plugin_font('printer_header')) - preview = QPrintPreviewDialog(printer, self) - preview.setWindowFlags(Qt.Window) - self.connect(preview, SIGNAL("paintRequested(QPrinter*)"), - lambda printer: editor.print_(printer)) - self.emit(SIGNAL('redirect_stdio(bool)'), False) - preview.exec_() - self.emit(SIGNAL('redirect_stdio(bool)'), True) - - def close_file(self): - """Close current file""" - editorstack = self.get_current_editorstack() - editorstack.close_file() - - def close_all_files(self): - """Close all opened scripts""" - self.editorstacks[0].close_all_files() - - def save(self, index=None, force=False): - """Save file""" - editorstack = self.get_current_editorstack() - return editorstack.save(index=index, force=force) - - def save_as(self): - """Save *as* the currently edited file""" - editorstack = self.get_current_editorstack() - if editorstack.save_as(): - fname = editorstack.get_current_filename() - if CONF.get('workingdir', 'editor/save/auto_set_to_basedir'): - self.emit(SIGNAL("open_dir(QString)"), osp.dirname(fname)) - self.__add_recent_file(fname) - - def save_all(self): - """Save all opened files""" - self.get_current_editorstack().save_all() - - def revert(self): - """Revert the currently edited file from disk""" - editorstack = self.get_current_editorstack() - editorstack.revert() - - - #------ Explorer widget - def close_file_from_name(self, filename): - """Close file from its name""" - filename = osp.abspath(to_text_string(filename)) - index = self.editorstacks[0].has_filename(filename) - if index is not None: - self.editorstacks[0].close_file(index) - - def removed(self, filename): - """File was removed in file explorer widget or in project explorer""" - self.close_file_from_name(filename) - - def removed_tree(self, dirname): - """Directory was removed in project explorer widget""" - dirname = osp.abspath(to_text_string(dirname)) - for fname in self.get_filenames(): - if osp.abspath(fname).startswith(dirname): - self.__close(fname) - - def renamed(self, source, dest): - """File was renamed in file explorer widget or in project explorer""" - filename = osp.abspath(to_text_string(source)) - index = self.editorstacks[0].has_filename(filename) - if index is not None: - for editorstack in self.editorstacks: - editorstack.rename_in_data(index, - new_filename=to_text_string(dest)) - - - #------ Source code - def indent(self): - """Indent current line or selection""" - editor = self.get_current_editor() - if editor is not None: - editor.indent() - - def unindent(self): - """Unindent current line or selection""" - editor = self.get_current_editor() - if editor is not None: - editor.unindent() - - def toggle_comment(self): - """Comment current line or selection""" - editor = self.get_current_editor() - if editor is not None: - editor.toggle_comment() - - def blockcomment(self): - """Block comment current line or selection""" - editor = self.get_current_editor() - if editor is not None: - editor.blockcomment() - - def unblockcomment(self): - """Un-block comment current line or selection""" - editor = self.get_current_editor() - if editor is not None: - editor.unblockcomment() - - def go_to_next_todo(self): - editor = self.get_current_editor() - position = editor.go_to_next_todo() - filename = self.get_current_filename() - self.add_cursor_position_to_history(filename, position) - - def go_to_next_warning(self): - editor = self.get_current_editor() - position = editor.go_to_next_warning() - filename = self.get_current_filename() - self.add_cursor_position_to_history(filename, position) - - def go_to_previous_warning(self): - editor = self.get_current_editor() - position = editor.go_to_previous_warning() - filename = self.get_current_filename() - self.add_cursor_position_to_history(filename, position) - - def run_winpdb(self): - """Run winpdb to debug current file""" - if self.save(): - fname = self.get_current_filename() - runconf = get_run_configuration(fname) - if runconf is None: - args = [] - wdir = None - else: - args = runconf.get_arguments().split() - wdir = runconf.get_working_directory() - # Handle the case where wdir comes back as an empty string - # when the working directory dialog checkbox is unchecked. - if not wdir: - wdir = None - programs.run_program(WINPDB_PATH, [fname]+args, wdir) - - def toggle_eol_chars(self, os_name): - editor = self.get_current_editor() - if self.__set_eol_chars: - editor.set_eol_chars(sourcecode.get_eol_chars_from_os_name(os_name)) - - def toggle_show_blanks(self, checked): - editor = self.get_current_editor() - editor.set_blanks_enabled(checked) - - def remove_trailing_spaces(self): - editorstack = self.get_current_editorstack() - editorstack.remove_trailing_spaces() - - def fix_indentation(self): - editorstack = self.get_current_editorstack() - editorstack.fix_indentation() - - #------ Cursor position history management - def update_cursorpos_actions(self): - self.previous_edit_cursor_action.setEnabled( - self.last_edit_cursor_pos is not None) - self.previous_cursor_action.setEnabled( - self.cursor_pos_index is not None and self.cursor_pos_index > 0) - self.next_cursor_action.setEnabled(self.cursor_pos_index is not None \ - and self.cursor_pos_index < len(self.cursor_pos_history)-1) - - def add_cursor_position_to_history(self, filename, position, fc=False): - if self.__ignore_cursor_position: - return - for index, (fname, pos) in enumerate(self.cursor_pos_history[:]): - if fname == filename: - if pos == position or pos == 0: - if fc: - self.cursor_pos_history[index] = (filename, position) - self.cursor_pos_index = index - self.update_cursorpos_actions() - return - else: - if self.cursor_pos_index >= index: - self.cursor_pos_index -= 1 - self.cursor_pos_history.pop(index) - break - if self.cursor_pos_index is not None: - self.cursor_pos_history = \ - self.cursor_pos_history[:self.cursor_pos_index+1] - self.cursor_pos_history.append((filename, position)) - self.cursor_pos_index = len(self.cursor_pos_history)-1 - self.update_cursorpos_actions() - - def cursor_moved(self, filename0, position0, filename1, position1): - """Cursor was just moved: 'go to'""" - if position0 is not None: - self.add_cursor_position_to_history(filename0, position0) - self.add_cursor_position_to_history(filename1, position1) - - def text_changed_at(self, filename, position): - self.last_edit_cursor_pos = (to_text_string(filename), position) - - def current_file_changed(self, filename, position): - self.add_cursor_position_to_history(to_text_string(filename), position, - fc=True) - - def go_to_last_edit_location(self): - if self.last_edit_cursor_pos is not None: - filename, position = self.last_edit_cursor_pos - if not osp.isfile(filename): - self.last_edit_cursor_pos = None - return - else: - self.load(filename) - editor = self.get_current_editor() - if position < editor.document().characterCount(): - editor.set_cursor_position(position) - - def __move_cursor_position(self, index_move): - if self.cursor_pos_index is None: - return - filename, _position = self.cursor_pos_history[self.cursor_pos_index] - self.cursor_pos_history[self.cursor_pos_index] = ( filename, - self.get_current_editor().get_position('cursor') ) - self.__ignore_cursor_position = True - old_index = self.cursor_pos_index - self.cursor_pos_index = min([ - len(self.cursor_pos_history)-1, - max([0, self.cursor_pos_index+index_move]) - ]) - filename, position = self.cursor_pos_history[self.cursor_pos_index] - if not osp.isfile(filename): - self.cursor_pos_history.pop(self.cursor_pos_index) - if self.cursor_pos_index < old_index: - old_index -= 1 - self.cursor_pos_index = old_index - else: - self.load(filename) - editor = self.get_current_editor() - if position < editor.document().characterCount(): - editor.set_cursor_position(position) - self.__ignore_cursor_position = False - self.update_cursorpos_actions() - - def go_to_previous_cursor_position(self): - self.__move_cursor_position(-1) - - def go_to_next_cursor_position(self): - self.__move_cursor_position(1) - - def go_to_line(self): - """Open 'go to line' dialog""" - editorstack = self.get_current_editorstack() - if editorstack is not None: - editorstack.go_to_line() - - def set_or_clear_breakpoint(self): - """Set/Clear breakpoint""" - editorstack = self.get_current_editorstack() - if editorstack is not None: - editorstack.set_or_clear_breakpoint() - - def set_or_edit_conditional_breakpoint(self): - """Set/Edit conditional breakpoint""" - editorstack = self.get_current_editorstack() - if editorstack is not None: - editorstack.set_or_edit_conditional_breakpoint() - - def clear_all_breakpoints(self): - """Clear breakpoints in all files""" - clear_all_breakpoints() - self.emit(SIGNAL("breakpoints_saved()")) - editorstack = self.get_current_editorstack() - if editorstack is not None: - for data in editorstack.data: - data.editor.clear_breakpoints() - self.refresh_plugin() - - def clear_breakpoint(self, filename, lineno): - """Remove a single breakpoint""" - clear_breakpoint(filename, lineno) - self.emit(SIGNAL("breakpoints_saved()")) - editorstack = self.get_current_editorstack() - if editorstack is not None: - index = self.is_file_opened(filename) - if index is not None: - editorstack.data[index].editor.add_remove_breakpoint(lineno) - - def debug_command(self, command): - """Debug actions""" - if self.main.ipyconsole is not None: - if self.main.last_console_plugin_focus_was_python: - self.main.extconsole.execute_python_code(command) - else: - self.main.ipyconsole.write_to_stdin(command) - focus_widget = self.main.ipyconsole.get_focus_widget() - if focus_widget: - focus_widget.setFocus() - else: - self.main.extconsole.execute_python_code(command) - - #------ Run Python script - def edit_run_configurations(self): - dialog = RunConfigDialog(self) - self.connect(dialog, SIGNAL("size_change(QSize)"), - lambda s: self.set_dialog_size(s)) - if self.dialog_size is not None: - dialog.resize(self.dialog_size) - fname = osp.abspath(self.get_current_filename()) - dialog.setup(fname) - if dialog.exec_(): - fname = dialog.file_to_run - if fname is not None: - self.load(fname) - self.run_file() - - def run_file(self, debug=False): - """Run script inside current interpreter or in a new one""" - editorstack = self.get_current_editorstack() - if editorstack.save(): - editor = self.get_current_editor() - fname = osp.abspath(self.get_current_filename()) - - # Escape single and double quotes in fname (Fixes Issue 2158) - fname = fname.replace("'", r"\'") - fname = fname.replace('"', r'\"') - - runconf = get_run_configuration(fname) - if runconf is None: - dialog = RunConfigOneDialog(self) - self.connect(dialog, SIGNAL("size_change(QSize)"), - lambda s: self.set_dialog_size(s)) - if self.dialog_size is not None: - dialog.resize(self.dialog_size) - dialog.setup(fname) - if CONF.get('run', 'open_at_least_once', True): - # Open Run Config dialog at least once: the first time - # a script is ever run in Spyder, so that the user may - # see it at least once and be conscious that it exists - show_dlg = True - CONF.set('run', 'open_at_least_once', False) - else: - # Open Run Config dialog only - # if ALWAYS_OPEN_FIRST_RUN_OPTION option is enabled - show_dlg = CONF.get('run', ALWAYS_OPEN_FIRST_RUN_OPTION) - if show_dlg and not dialog.exec_(): - return - runconf = dialog.get_configuration() - - wdir = runconf.get_working_directory() - args = runconf.get_arguments() - python_args = runconf.get_python_arguments() - interact = runconf.interact - current = runconf.current - systerm = runconf.systerm - - python = True # Note: in the future, it may be useful to run - # something in a terminal instead of a Python interp. - self.__last_ec_exec = (fname, wdir, args, interact, debug, - python, python_args, current, systerm) - self.re_run_file() - if not interact and not debug: - # If external console dockwidget is hidden, it will be - # raised in top-level and so focus will be given to the - # current external shell automatically - # (see SpyderPluginWidget.visibility_changed method) - editor.setFocus() - - def set_dialog_size(self, size): - self.dialog_size = size - - def debug_file(self): - """Debug current script""" - self.run_file(debug=True) - editor = self.get_current_editor() - if editor.get_breakpoints(): - time.sleep(0.5) - self.debug_command('continue') - - def re_run_file(self): - """Re-run last script""" - if self.get_option('save_all_before_run'): - self.save_all() - if self.__last_ec_exec is None: - return - (fname, wdir, args, interact, debug, - python, python_args, current, systerm) = self.__last_ec_exec - if current: - if self.main.ipyconsole is not None: - if self.main.last_console_plugin_focus_was_python: - self.emit( - SIGNAL('run_in_current_extconsole(QString,QString,QString,bool)'), - fname, wdir, args, debug) - else: - self.emit( - SIGNAL('run_in_current_ipyclient(QString,QString,QString,bool)'), - fname, wdir, args, debug) - else: - self.emit( - SIGNAL('run_in_current_extconsole(QString,QString,QString,bool)'), - fname, wdir, args, debug) - else: - self.main.open_external_console(fname, wdir, args, interact, - debug, python, python_args, - systerm) - - def run_selection(self): - """Run selection or current line in external console""" - editorstack = self.get_current_editorstack() - editorstack.run_selection() - - def run_cell(self): - """Run current cell""" - editorstack = self.get_current_editorstack() - editorstack.run_cell() - - def run_cell_and_advance(self): - """Run current cell and advance to the next one""" - editorstack = self.get_current_editorstack() - editorstack.run_cell_and_advance() - - #------ Zoom in/out/reset - def zoom(self, factor): - """Zoom in/out/reset""" - editor = self.get_current_editorstack().get_current_editor() - if factor == 0: - font = self.get_plugin_font() - editor.set_font(font) - else: - font = editor.font() - size = font.pointSize() + factor - if size > 0: - font.setPointSize(size) - editor.set_font(font) - - #------ Options - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - # toggle_fullpath_sorting - if self.editorstacks is not None: - # --- syntax highlight and text rendering settings - color_scheme_n = 'color_scheme_name' - color_scheme_o = get_color_scheme(self.get_option(color_scheme_n)) - font_n = 'plugin_font' - font_o = self.get_plugin_font() - currentline_n = 'highlight_current_line' - currentline_o = self.get_option(currentline_n) - currentcell_n = 'highlight_current_cell' - currentcell_o = self.get_option(currentcell_n) - occurence_n = 'occurence_highlighting' - occurence_o = self.get_option(occurence_n) - occurence_timeout_n = 'occurence_highlighting/timeout' - occurence_timeout_o = self.get_option(occurence_timeout_n) - focus_to_editor_n = 'focus_to_editor' - focus_to_editor_o = self.get_option(focus_to_editor_n) - - for editorstack in self.editorstacks: - if font_n in options: - scs = color_scheme_o if color_scheme_n in options else None - editorstack.set_default_font(font_o, scs) - completion_size = CONF.get('editor_appearance', - 'completion/size') - for finfo in editorstack.data: - comp_widget = finfo.editor.completion_widget - comp_widget.setup_appearance(completion_size, font_o) - elif color_scheme_n in options: - editorstack.set_color_scheme(color_scheme_o) - if currentline_n in options: - editorstack.set_highlight_current_line_enabled( - currentline_o) - if currentcell_n in options: - editorstack.set_highlight_current_cell_enabled( - currentcell_o) - if occurence_n in options: - editorstack.set_occurence_highlighting_enabled(occurence_o) - if occurence_timeout_n in options: - editorstack.set_occurence_highlighting_timeout( - occurence_timeout_o) - if focus_to_editor_n in options: - editorstack.set_focus_to_editor(focus_to_editor_o) - - # --- everything else - fpsorting_n = 'fullpath_sorting' - fpsorting_o = self.get_option(fpsorting_n) - tabbar_n = 'show_tab_bar' - tabbar_o = self.get_option(tabbar_n) - linenb_n = 'line_numbers' - linenb_o = self.get_option(linenb_n) - blanks_n = 'blank_spaces' - blanks_o = self.get_option(blanks_n) - edgeline_n = 'edge_line' - edgeline_o = self.get_option(edgeline_n) - edgelinecol_n = 'edge_line_column' - edgelinecol_o = self.get_option(edgelinecol_n) - wrap_n = 'wrap' - wrap_o = self.get_option(wrap_n) - tabindent_n = 'tab_always_indent' - tabindent_o = self.get_option(tabindent_n) - ibackspace_n = 'intelligent_backspace' - ibackspace_o = self.get_option(ibackspace_n) - removetrail_n = 'always_remove_trailing_spaces' - removetrail_o = self.get_option(removetrail_n) - autocomp_n = 'codecompletion/auto' - autocomp_o = self.get_option(autocomp_n) - case_comp_n = 'codecompletion/case_sensitive' - case_comp_o = self.get_option(case_comp_n) - enter_key_n = 'codecompletion/enter_key' - enter_key_o = self.get_option(enter_key_n) - calltips_n = 'calltips' - calltips_o = self.get_option(calltips_n) - gotodef_n = 'go_to_definition' - gotodef_o = self.get_option(gotodef_n) - closepar_n = 'close_parentheses' - closepar_o = self.get_option(closepar_n) - close_quotes_n = 'close_quotes' - close_quotes_o = self.get_option(close_quotes_n) - add_colons_n = 'add_colons' - add_colons_o = self.get_option(add_colons_n) - autounindent_n = 'auto_unindent' - autounindent_o = self.get_option(autounindent_n) - indent_chars_n = 'indent_chars' - indent_chars_o = self.get_option(indent_chars_n) - tab_stop_width_n = 'tab_stop_width' - tab_stop_width_o = self.get_option(tab_stop_width_n) - inspector_n = 'connect_to_oi' - inspector_o = CONF.get('inspector', 'connect/editor') - todo_n = 'todo_list' - todo_o = self.get_option(todo_n) - pyflakes_n = 'code_analysis/pyflakes' - pyflakes_o = self.get_option(pyflakes_n) - pep8_n = 'code_analysis/pep8' - pep8_o = self.get_option(pep8_n) - rt_analysis_n = 'realtime_analysis' - rt_analysis_o = self.get_option(rt_analysis_n) - rta_timeout_n = 'realtime_analysis/timeout' - rta_timeout_o = self.get_option(rta_timeout_n) - finfo = self.get_current_finfo() - if fpsorting_n in options: - if self.outlineexplorer is not None: - self.outlineexplorer.set_fullpath_sorting(fpsorting_o) - for window in self.editorwindows: - window.editorwidget.outlineexplorer.set_fullpath_sorting( - fpsorting_o) - for editorstack in self.editorstacks: - if fpsorting_n in options: - editorstack.set_fullpath_sorting_enabled(fpsorting_o) - if tabbar_n in options: - editorstack.set_tabbar_visible(tabbar_o) - if linenb_n in options: - editorstack.set_linenumbers_enabled(linenb_o, - current_finfo=finfo) - if blanks_n in options: - editorstack.set_blanks_enabled(blanks_o) - self.showblanks_action.setChecked(blanks_o) - if edgeline_n in options: - editorstack.set_edgeline_enabled(edgeline_o) - if edgelinecol_n in options: - editorstack.set_edgeline_column(edgelinecol_o) - if wrap_n in options: - editorstack.set_wrap_enabled(wrap_o) - if tabindent_n in options: - editorstack.set_tabmode_enabled(tabindent_o) - if ibackspace_n in options: - editorstack.set_intelligent_backspace_enabled(ibackspace_o) - if removetrail_n in options: - editorstack.set_always_remove_trailing_spaces(removetrail_o) - if autocomp_n in options: - editorstack.set_codecompletion_auto_enabled(autocomp_o) - if case_comp_n in options: - editorstack.set_codecompletion_case_enabled(case_comp_o) - if enter_key_n in options: - editorstack.set_codecompletion_enter_enabled(enter_key_o) - if calltips_n in options: - editorstack.set_calltips_enabled(calltips_o) - if gotodef_n in options: - editorstack.set_go_to_definition_enabled(gotodef_o) - if closepar_n in options: - editorstack.set_close_parentheses_enabled(closepar_o) - if close_quotes_n in options: - editorstack.set_close_quotes_enabled(close_quotes_o) - if add_colons_n in options: - editorstack.set_add_colons_enabled(add_colons_o) - if autounindent_n in options: - editorstack.set_auto_unindent_enabled(autounindent_o) - if indent_chars_n in options: - editorstack.set_indent_chars(indent_chars_o) - if tab_stop_width_n in options: - editorstack.set_tab_stop_width(tab_stop_width_o) - if inspector_n in options: - editorstack.set_inspector_enabled(inspector_o) - if todo_n in options: - editorstack.set_todolist_enabled(todo_o, - current_finfo=finfo) - if pyflakes_n in options: - editorstack.set_pyflakes_enabled(pyflakes_o, - current_finfo=finfo) - if pep8_n in options: - editorstack.set_pep8_enabled(pep8_o, current_finfo=finfo) - if rt_analysis_n in options: - editorstack.set_realtime_analysis_enabled(rt_analysis_o) - if rta_timeout_n in options: - editorstack.set_realtime_analysis_timeout(rta_timeout_o) - # We must update the current editor after the others: - # (otherwise, code analysis buttons state would correspond to the - # last editor instead of showing the one of the current editor) - if finfo is not None: - if todo_n in options and todo_o: - finfo.run_todo_finder() - if pyflakes_n in options or pep8_n in options: - finfo.run_code_analysis(pyflakes_o, pep8_o) diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/explorer.py spyder-3.0.2+dfsg1/spyderlib/plugins/explorer.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/explorer.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/explorer.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,117 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Files and Directories Explorer Plugin""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import QFontDialog -from spyderlib.qt.QtCore import SIGNAL - -import os.path as osp - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.utils.qthelpers import create_action -from spyderlib.widgets.explorer import ExplorerWidget -from spyderlib.plugins import SpyderPluginMixin -from spyderlib.py3compat import to_text_string - - -class Explorer(ExplorerWidget, SpyderPluginMixin): - """File and Directories Explorer DockWidget""" - CONF_SECTION = 'explorer' - def __init__(self, parent=None): - ExplorerWidget.__init__(self, parent=parent, - name_filters=self.get_option('name_filters'), - show_all=self.get_option('show_all'), - show_icontext=self.get_option('show_icontext')) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - self.set_font(self.get_plugin_font()) - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _("File explorer") - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.treewidget - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - # Font - font_action = create_action(self, _("&Font..."), None, 'font.png', - _("Set font style"), - triggered=self.change_font) - self.treewidget.common_actions.append(font_action) - return [] - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.main.add_dockwidget(self) - self.connect(self, SIGNAL("edit(QString)"), self.main.editor.load) - self.connect(self, SIGNAL("removed(QString)"), self.main.editor.removed) - self.connect(self, SIGNAL("renamed(QString,QString)"), - self.main.editor.renamed) - self.connect(self.main.editor, SIGNAL("open_dir(QString)"), self.chdir) - self.connect(self, SIGNAL("create_module(QString)"), - self.main.editor.new) - self.connect(self, SIGNAL("run(QString)"), - lambda fname: - self.main.open_external_console(to_text_string(fname), - osp.dirname(to_text_string(fname)), - '', False, False, True, '', False)) - # Signal "set_explorer_cwd(QString)" will refresh only the - # contents of path passed by the signal in explorer: - self.connect(self.main.workingdirectory, - SIGNAL("set_explorer_cwd(QString)"), - lambda directory: self.refresh_plugin(new_path=directory, - force_current=True)) - self.connect(self, SIGNAL("open_dir(QString)"), - lambda dirname: - self.main.workingdirectory.chdir(dirname, - refresh_explorer=False)) - - self.sig_open_file.connect(self.main.open_file) - self.sig_new_file.connect(self.main.new_file) - - def refresh_plugin(self, new_path=None, force_current=True): - """Refresh explorer widget""" - self.treewidget.update_history(new_path) - self.treewidget.refresh(new_path, force_current=force_current) - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - return True - - #------ Public API --------------------------------------------------------- - def chdir(self, directory): - """Set working directory""" - self.treewidget.chdir(directory) - - def change_font(self): - """Change font""" - font, valid = QFontDialog.getFont(self.get_plugin_font(), self, - _("Select a new font")) - if valid: - self.set_font(font) - self.set_plugin_font(font) - - def set_font(self, font): - """Set explorer widget font""" - self.setFont(font) - self.treewidget.setFont(font) diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/externalconsole.py spyder-3.0.2+dfsg1/spyderlib/plugins/externalconsole.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/externalconsole.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/externalconsole.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1367 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""External Console plugin""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -# Qt imports -from spyderlib.qt.QtGui import (QVBoxLayout, QMessageBox, QInputDialog, - QLineEdit, QPushButton, QGroupBox, QLabel, - QTabWidget, QFontComboBox, QHBoxLayout, - QButtonGroup, QWidget) -from spyderlib.qt.QtCore import SIGNAL, Qt -from spyderlib.qt.compat import getopenfilename - -# Stdlib imports -import atexit -import os -import os.path as osp -import sys -import subprocess - -# Local imports -from spyderlib.baseconfig import SCIENTIFIC_STARTUP, running_in_mac_app, _ -from spyderlib.config import CONF -from spyderlib.utils import encoding, programs -from spyderlib.utils.misc import (get_error_match, get_python_executable, - remove_backslashes, is_python_script) -from spyderlib.utils.qthelpers import get_icon, create_action, mimedata2url -from spyderlib.widgets.tabs import Tabs -from spyderlib.widgets.externalshell.pythonshell import ExternalPythonShell -from spyderlib.widgets.externalshell.systemshell import ExternalSystemShell -from spyderlib.widgets.findreplace import FindReplace -from spyderlib.plugins import SpyderPluginWidget, PluginConfigPage -from spyderlib.plugins.runconfig import get_run_configuration -from spyderlib.py3compat import to_text_string, is_text_string, getcwd -from spyderlib import dependencies - -MPL_REQVER = '>=1.0' -dependencies.add("matplotlib", _("Interactive data plotting in the consoles"), - required_version=MPL_REQVER) - - -class ExternalConsoleConfigPage(PluginConfigPage): - def __init__(self, plugin, parent): - PluginConfigPage.__init__(self, plugin, parent) - self.get_name = lambda: _("Console") - self.cus_exec_radio = None - self.pyexec_edit = None - - def initialize(self): - PluginConfigPage.initialize(self) - self.connect(self.pyexec_edit, SIGNAL("textChanged(QString)"), - self.python_executable_changed) - self.connect(self.cus_exec_radio, SIGNAL("toggled(bool)"), - self.python_executable_switched) - - def setup_page(self): - interface_group = QGroupBox(_("Interface")) - font_group = self.create_fontgroup(option=None, text=None, - fontfilters=QFontComboBox.MonospacedFonts) - newcb = self.create_checkbox - singletab_box = newcb(_("One tab per script"), 'single_tab') - showtime_box = newcb(_("Show elapsed time"), 'show_elapsed_time') - icontext_box = newcb(_("Show icons and text"), 'show_icontext') - - # Interface Group - interface_layout = QVBoxLayout() - interface_layout.addWidget(singletab_box) - interface_layout.addWidget(showtime_box) - interface_layout.addWidget(icontext_box) - interface_group.setLayout(interface_layout) - - # Source Code Group - display_group = QGroupBox(_("Source code")) - buffer_spin = self.create_spinbox( - _("Buffer: "), _(" lines"), - 'max_line_count', min_=0, max_=1000000, step=100, - tip=_("Set maximum line count")) - wrap_mode_box = newcb(_("Wrap lines"), 'wrap') - merge_channels_box = newcb( - _("Merge process standard output/error channels"), - 'merge_output_channels', - tip=_("Merging the output channels of the process means that\n" - "the standard error won't be written in red anymore,\n" - "but this has the effect of speeding up display.")) - colorize_sys_stderr_box = newcb( - _("Colorize standard error channel using ANSI escape codes"), - 'colorize_sys_stderr', - tip=_("This method is the only way to have colorized standard\n" - "error channel when the output channels have been " - "merged.")) - self.connect(merge_channels_box, SIGNAL("toggled(bool)"), - colorize_sys_stderr_box.setEnabled) - self.connect(merge_channels_box, SIGNAL("toggled(bool)"), - colorize_sys_stderr_box.setChecked) - colorize_sys_stderr_box.setEnabled( - self.get_option('merge_output_channels')) - - display_layout = QVBoxLayout() - display_layout.addWidget(buffer_spin) - display_layout.addWidget(wrap_mode_box) - display_layout.addWidget(merge_channels_box) - display_layout.addWidget(colorize_sys_stderr_box) - display_group.setLayout(display_layout) - - # Background Color Group - bg_group = QGroupBox(_("Background color")) - bg_label = QLabel(_("This option will be applied the next time " - "a Python console or a terminal is opened.")) - bg_label.setWordWrap(True) - lightbg_box = newcb(_("Light background (white color)"), - 'light_background') - bg_layout = QVBoxLayout() - bg_layout.addWidget(bg_label) - bg_layout.addWidget(lightbg_box) - bg_group.setLayout(bg_layout) - - # Advanced settings - source_group = QGroupBox(_("Source code")) - completion_box = newcb(_("Automatic code completion"), - 'codecompletion/auto') - case_comp_box = newcb(_("Case sensitive code completion"), - 'codecompletion/case_sensitive') - comp_enter_box = newcb(_("Enter key selects completion"), - 'codecompletion/enter_key') - calltips_box = newcb(_("Display balloon tips"), 'calltips') - - source_layout = QVBoxLayout() - source_layout.addWidget(completion_box) - source_layout.addWidget(case_comp_box) - source_layout.addWidget(comp_enter_box) - source_layout.addWidget(calltips_box) - source_group.setLayout(source_layout) - - # UMR Group - umr_group = QGroupBox(_("User Module Reloader (UMR)")) - umr_label = QLabel(_("UMR forces Python to reload modules which were " - "imported when executing a \nscript in the " - "external console with the 'runfile' function.")) - umr_enabled_box = newcb(_("Enable UMR"), 'umr/enabled', - msg_if_enabled=True, msg_warning=_( - "This option will enable the User Module Reloader (UMR) " - "in Python/IPython consoles. UMR forces Python to " - "reload deeply modules during import when running a " - "Python script using the Spyder's builtin function " - "runfile." - "

    1. UMR may require to restart the " - "console in which it will be called " - "(otherwise only newly imported modules will be " - "reloaded when executing scripts)." - "

    2. If errors occur when re-running a " - "PyQt-based program, please check that the Qt objects " - "are properly destroyed (e.g. you may have to use the " - "attribute Qt.WA_DeleteOnClose on your main " - "window, using the setAttribute method)"), - ) - umr_verbose_box = newcb(_("Show reloaded modules list"), - 'umr/verbose', msg_info=_( - "Please note that these changes will " - "be applied only to new consoles")) - umr_namelist_btn = QPushButton( - _("Set UMR excluded (not reloaded) modules")) - self.connect(umr_namelist_btn, SIGNAL('clicked()'), - self.plugin.set_umr_namelist) - - umr_layout = QVBoxLayout() - umr_layout.addWidget(umr_label) - umr_layout.addWidget(umr_enabled_box) - umr_layout.addWidget(umr_verbose_box) - umr_layout.addWidget(umr_namelist_btn) - umr_group.setLayout(umr_layout) - - # Python executable Group - pyexec_group = QGroupBox(_("Python executable")) - pyexec_bg = QButtonGroup(pyexec_group) - pyexec_label = QLabel(_("Select the Python interpreter executable " - "binary in which Spyder will run scripts:")) - def_exec_radio = self.create_radiobutton( - _("Default (i.e. the same as Spyder's)"), - 'pythonexecutable/default', - button_group=pyexec_bg) - self.cus_exec_radio = self.create_radiobutton( - _("Use the following Python interpreter:"), - 'pythonexecutable/custom', - button_group=pyexec_bg) - if os.name == 'nt': - filters = _("Executables")+" (*.exe)" - else: - filters = None - pyexec_file = self.create_browsefile('', 'pythonexecutable', - filters=filters) - for le in self.lineedits: - if self.lineedits[le][0] == 'pythonexecutable': - self.pyexec_edit = le - self.connect(def_exec_radio, SIGNAL("toggled(bool)"), - pyexec_file.setDisabled) - self.connect(self.cus_exec_radio, SIGNAL("toggled(bool)"), - pyexec_file.setEnabled) - pyexec_layout = QVBoxLayout() - pyexec_layout.addWidget(pyexec_label) - pyexec_layout.addWidget(def_exec_radio) - pyexec_layout.addWidget(self.cus_exec_radio) - pyexec_layout.addWidget(pyexec_file) - pyexec_group.setLayout(pyexec_layout) - - # PYTHONSTARTUP replacement - pystartup_group = QGroupBox(_("PYTHONSTARTUP replacement")) - pystartup_bg = QButtonGroup(pystartup_group) - pystartup_label = QLabel(_("This option will override the " - "PYTHONSTARTUP environment variable which\n" - "defines the script to be executed during " - "the Python console startup.")) - def_startup_radio = self.create_radiobutton( - _("Default PYTHONSTARTUP script"), - 'pythonstartup/default', - button_group=pystartup_bg) - cus_startup_radio = self.create_radiobutton( - _("Use the following startup script:"), - 'pythonstartup/custom', - button_group=pystartup_bg) - pystartup_file = self.create_browsefile('', 'pythonstartup', '', - filters=_("Python scripts")+\ - " (*.py)") - self.connect(def_startup_radio, SIGNAL("toggled(bool)"), - pystartup_file.setDisabled) - self.connect(cus_startup_radio, SIGNAL("toggled(bool)"), - pystartup_file.setEnabled) - - pystartup_layout = QVBoxLayout() - pystartup_layout.addWidget(pystartup_label) - pystartup_layout.addWidget(def_startup_radio) - pystartup_layout.addWidget(cus_startup_radio) - pystartup_layout.addWidget(pystartup_file) - pystartup_group.setLayout(pystartup_layout) - - # Monitor Group - monitor_group = QGroupBox(_("Monitor")) - monitor_label = QLabel(_("The monitor provides introspection " - "features to console: code completion, " - "calltips and variable explorer. " - "Because it relies on several modules, " - "disabling the monitor may be useful " - "to accelerate console startup.")) - monitor_label.setWordWrap(True) - monitor_box = newcb(_("Enable monitor"), 'monitor/enabled') - for obj in (completion_box, case_comp_box, comp_enter_box, - calltips_box): - self.connect(monitor_box, SIGNAL("toggled(bool)"), obj.setEnabled) - obj.setEnabled(self.get_option('monitor/enabled')) - - monitor_layout = QVBoxLayout() - monitor_layout.addWidget(monitor_label) - monitor_layout.addWidget(monitor_box) - monitor_group.setLayout(monitor_layout) - - # Qt Group - opts = [(_("Default library"), 'default'), ('PyQt4', 'pyqt'), - ('PySide', 'pyside')] - qt_group = QGroupBox(_("Qt (PyQt/PySide)")) - qt_setapi_box = self.create_combobox( - _("Qt-Python bindings library selection:"), opts, - 'qt/api', default='default', - tip=_("This option will act on
    " - "libraries such as Matplotlib, guidata " - "or ETS")) - if self.get_option('pythonexecutable/default'): - interpreter = get_python_executable() - else: - interpreter = self.get_option('pythonexecutable') - has_pyqt4 = programs.is_module_installed('PyQt4', - interpreter=interpreter) - has_pyside = programs.is_module_installed('PySide', - interpreter=interpreter) - if has_pyside and not has_pyqt4: - self.set_option('qt/api', 'pyside') - - qt_layout = QVBoxLayout() - qt_layout.addWidget(qt_setapi_box) - qt_group.setLayout(qt_layout) - qt_group.setEnabled(has_pyqt4 or has_pyside) - - # PyQt Group - if has_pyqt4: - pyqt_group = QGroupBox(_("PyQt")) - setapi_box = self.create_combobox( - _("API selection for QString and QVariant objects:"), - ((_("Default API"), 0), (_("API #1"), 1), (_("API #2"), 2)), - 'pyqt/api_version', default=0, - tip=_("PyQt API #1 is the default
    " - "API for Python 2. PyQt API #2 is " - "the default API for Python 3 and " - "is compatible with PySide.")) - ignore_api_box = newcb(_("Ignore API change errors (sip.setapi)"), - 'pyqt/ignore_sip_setapi_errors', - tip=_("Enabling this option will ignore
    " - "errors when changing PyQt API. As " - "PyQt does not support dynamic API " - "changes, it is strongly recommended " - "to use this feature wisely, e.g. " - "for debugging purpose.")) - try: - from sip import setapi #analysis:ignore - except ImportError: - setapi_box.setDisabled(True) - ignore_api_box.setDisabled(True) - - pyqt_layout = QVBoxLayout() - pyqt_layout.addWidget(setapi_box) - pyqt_layout.addWidget(ignore_api_box) - pyqt_group.setLayout(pyqt_layout) - qt_layout.addWidget(pyqt_group) - - # Matplotlib Group - mpl_group = QGroupBox(_("Matplotlib")) - mpl_backend_box = newcb('', 'matplotlib/backend/enabled', True) - mpl_backend_edit = self.create_lineedit(_("GUI backend:"), - 'matplotlib/backend/value', "Qt4Agg", - tip=_("Set the GUI toolkit used by
    " - "Matplotlib to show figures " - "(default: Qt4Agg)"), - alignment=Qt.Horizontal) - self.connect(mpl_backend_box, SIGNAL("toggled(bool)"), - mpl_backend_edit.setEnabled) - mpl_backend_layout = QHBoxLayout() - mpl_backend_layout.addWidget(mpl_backend_box) - mpl_backend_layout.addWidget(mpl_backend_edit) - mpl_backend_edit.setEnabled( - self.get_option('matplotlib/backend/enabled')) - mpl_installed = programs.is_module_installed('matplotlib') - - mpl_layout = QVBoxLayout() - mpl_layout.addLayout(mpl_backend_layout) - mpl_group.setLayout(mpl_layout) - mpl_group.setEnabled(mpl_installed) - - # ETS Group - ets_group = QGroupBox(_("Enthought Tool Suite")) - ets_label = QLabel(_("Enthought Tool Suite (ETS) supports " - "PyQt4 (qt4) and wxPython (wx) graphical " - "user interfaces.")) - ets_label.setWordWrap(True) - ets_edit = self.create_lineedit(_("ETS_TOOLKIT:"), 'ets_backend', - alignment=Qt.Horizontal) - - ets_layout = QVBoxLayout() - ets_layout.addWidget(ets_label) - ets_layout.addWidget(ets_edit) - ets_group.setLayout(ets_layout) - ets_group.setEnabled(programs.is_module_installed( - "enthought.etsconfig.api", - interpreter=interpreter)) - - tabs = QTabWidget() - tabs.addTab(self.create_tab(font_group, interface_group, display_group, - bg_group), - _("Display")) - tabs.addTab(self.create_tab(monitor_group, source_group), - _("Introspection")) - tabs.addTab(self.create_tab(pyexec_group, pystartup_group, umr_group), - _("Advanced settings")) - tabs.addTab(self.create_tab(qt_group, mpl_group, ets_group), - _("External modules")) - - vlayout = QVBoxLayout() - vlayout.addWidget(tabs) - self.setLayout(vlayout) - - def _auto_change_qt_api(self, pyexec): - """Change automatically Qt API depending on - selected Python executable""" - has_pyqt4 = programs.is_module_installed('PyQt4', interpreter=pyexec) - has_pyside = programs.is_module_installed('PySide', interpreter=pyexec) - for cb in self.comboboxes: - if self.comboboxes[cb][0] == 'qt/api': - qt_setapi_cb = cb - if has_pyside and not has_pyqt4: - qt_setapi_cb.setCurrentIndex(2) - elif has_pyqt4 and not has_pyside: - qt_setapi_cb.setCurrentIndex(1) - else: - qt_setapi_cb.setCurrentIndex(0) - - def python_executable_changed(self, pyexec): - """Custom Python executable value has been changed""" - if not self.cus_exec_radio.isChecked(): - return - if not is_text_string(pyexec): - pyexec = to_text_string(pyexec.toUtf8(), 'utf-8') - old_pyexec = self.get_option("pythonexecutable", - get_python_executable()) - if pyexec != old_pyexec: - self._auto_change_qt_api(pyexec) - self.warn_python_compatibility(pyexec) - - def python_executable_switched(self, custom): - """Python executable default/custom radio button has been toggled""" - def_pyexec = get_python_executable() - cust_pyexec = self.pyexec_edit.text() - if not is_text_string(cust_pyexec): - cust_pyexec = to_text_string(cust_pyexec.toUtf8(), 'utf-8') - if def_pyexec != cust_pyexec: - pyexec = cust_pyexec if custom else def_pyexec - self._auto_change_qt_api(pyexec) - if custom: - self.warn_python_compatibility(cust_pyexec) - - def warn_python_compatibility(self, pyexec): - if not osp.isfile(pyexec): - return - spyder_version = sys.version_info[0] - try: - cmd = [pyexec, "-c", "import sys; print(sys.version_info[0])"] - # subprocess.check_output is not present in python2.6 and 3.0 - process = subprocess.Popen(cmd, stdout=subprocess.PIPE) - console_version = int(process.communicate()[0]) - except IOError: - console_version = spyder_version - if spyder_version != console_version: - QMessageBox.warning(self, _('Warning'), - _("You selected a Python %d interpreter for the console " - "but Spyder is running on Python %d!.

    " - "Although this is possible, we recommend you to install and " - "run Spyder directly with your selected interpreter, to avoid " - "seeing false warnings and errors due to the incompatible " - "syntax between these two Python versions." - ) % (console_version, spyder_version), QMessageBox.Ok) - - -class ExternalConsole(SpyderPluginWidget): - """ - Console widget - """ - CONF_SECTION = 'console' - CONFIGWIDGET_CLASS = ExternalConsoleConfigPage - DISABLE_ACTIONS_WHEN_HIDDEN = False - def __init__(self, parent, light_mode): - SpyderPluginWidget.__init__(self, parent) - self.light_mode = light_mode - self.tabwidget = None - self.menu_actions = None - - self.inspector = None # Object inspector plugin - self.historylog = None # History log plugin - self.variableexplorer = None # Variable explorer plugin - - self.python_count = 0 - self.terminal_count = 0 - - try: - from sip import setapi #analysis:ignore - except ImportError: - self.set_option('pyqt/ignore_sip_setapi_errors', False) - - # Python executable selection (initializing default values as well) - executable = self.get_option('pythonexecutable', - get_python_executable()) - if self.get_option('pythonexecutable/default'): - executable = get_python_executable() - - # Python startup file selection - if not osp.isfile(self.get_option('pythonstartup', '')): - self.set_option('pythonstartup', SCIENTIFIC_STARTUP) - # default/custom settings are mutually exclusive: - self.set_option('pythonstartup/custom', - not self.get_option('pythonstartup/default')) - - if not osp.isfile(executable): - # This is absolutely necessary, in case the Python interpreter - # executable has been moved since last Spyder execution (following - # a Python distribution upgrade for example) - self.set_option('pythonexecutable', get_python_executable()) - elif executable.endswith('pythonw.exe'): - # That should not be necessary because this case is already taken - # care of by the `get_python_executable` function but, this was - # implemented too late, so we have to fix it here too, in case - # the Python executable has already been set with pythonw.exe: - self.set_option('pythonexecutable', - executable.replace("pythonw.exe", "python.exe")) - - self.shellwidgets = [] - self.filenames = [] - self.icons = [] - self.runfile_args = "" - - # Initialize plugin - self.initialize_plugin() - - layout = QVBoxLayout() - self.tabwidget = Tabs(self, self.menu_actions) - if hasattr(self.tabwidget, 'setDocumentMode')\ - and not sys.platform == 'darwin': - # Don't set document mode to true on OSX because it generates - # a crash when the console is detached from the main window - # Fixes Issue 561 - self.tabwidget.setDocumentMode(True) - self.connect(self.tabwidget, SIGNAL('currentChanged(int)'), - self.refresh_plugin) - self.connect(self.tabwidget, SIGNAL('move_data(int,int)'), - self.move_tab) - self.connect(self.main, SIGNAL("pythonpath_changed()"), - self.set_path) - - self.tabwidget.set_close_function(self.close_console) - - if sys.platform == 'darwin': - tab_container = QWidget() - tab_container.setObjectName('tab-container') - tab_layout = QHBoxLayout(tab_container) - tab_layout.setContentsMargins(0, 0, 0, 0) - tab_layout.addWidget(self.tabwidget) - layout.addWidget(tab_container) - else: - layout.addWidget(self.tabwidget) - - # Find/replace widget - self.find_widget = FindReplace(self) - self.find_widget.hide() - self.register_widget_shortcuts("Editor", self.find_widget) - - layout.addWidget(self.find_widget) - - self.setLayout(layout) - - # Accepting drops - self.setAcceptDrops(True) - - def move_tab(self, index_from, index_to): - """ - Move tab (tabs themselves have already been moved by the tabwidget) - """ - filename = self.filenames.pop(index_from) - shell = self.shellwidgets.pop(index_from) - icons = self.icons.pop(index_from) - - self.filenames.insert(index_to, filename) - self.shellwidgets.insert(index_to, shell) - self.icons.insert(index_to, icons) - self.emit(SIGNAL('update_plugin_title()')) - - def get_shell_index_from_id(self, shell_id): - """Return shellwidget index from id""" - for index, shell in enumerate(self.shellwidgets): - if id(shell) == shell_id: - return index - - def close_console(self, index=None, from_ipyclient=False): - """Close console tab from index or widget (or close current tab)""" - # Get tab index - if not self.tabwidget.count(): - return - if index is None: - index = self.tabwidget.currentIndex() - - # Detect what widget we are trying to close - for i, s in enumerate(self.shellwidgets): - if index == i: - shellwidget = s - - # If the tab is an IPython kernel, try to detect if it has a client - # connected to it - if shellwidget.is_ipykernel: - ipyclients = self.main.ipyconsole.get_clients() - if ipyclients: - for ic in ipyclients: - if ic.kernel_widget_id == id(shellwidget): - connected_ipyclient = True - break - else: - connected_ipyclient = False - else: - connected_ipyclient = False - - # Closing logic - if not shellwidget.is_ipykernel or from_ipyclient or \ - not connected_ipyclient: - self.tabwidget.widget(index).close() - self.tabwidget.removeTab(index) - self.filenames.pop(index) - self.shellwidgets.pop(index) - self.icons.pop(index) - self.emit(SIGNAL('update_plugin_title()')) - else: - QMessageBox.question(self, _('Trying to kill a kernel?'), - _("You can't close this kernel because it has one or more " - "consoles connected to it.

    " - "You need to close them instead or you can kill the kernel " - "using the second button from right to left."), - QMessageBox.Ok) - - - def set_variableexplorer(self, variableexplorer): - """Set variable explorer plugin""" - self.variableexplorer = variableexplorer - - def set_path(self): - """Set consoles PYTHONPATH if changed by the user""" - from spyderlib.widgets.externalshell import pythonshell - for sw in self.shellwidgets: - if isinstance(sw, pythonshell.ExternalPythonShell): - if sw.is_interpreter and sw.is_running(): - sw.path = self.main.get_spyder_pythonpath() - sw.shell.path = sw.path - - def __find_python_shell(self, interpreter_only=False): - current_index = self.tabwidget.currentIndex() - if current_index == -1: - return - from spyderlib.widgets.externalshell import pythonshell - for index in [current_index]+list(range(self.tabwidget.count())): - shellwidget = self.tabwidget.widget(index) - if isinstance(shellwidget, pythonshell.ExternalPythonShell): - if interpreter_only and not shellwidget.is_interpreter: - continue - elif not shellwidget.is_running(): - continue - else: - self.tabwidget.setCurrentIndex(index) - return shellwidget - - def get_current_shell(self): - """ - Called by object inspector to retrieve the current shell instance - """ - shellwidget = self.__find_python_shell() - return shellwidget.shell - - def get_running_python_shell(self): - """ - Called by object inspector to retrieve a running Python shell instance - """ - current_index = self.tabwidget.currentIndex() - if current_index == -1: - return - from spyderlib.widgets.externalshell import pythonshell - shellwidgets = [self.tabwidget.widget(index) - for index in range(self.tabwidget.count())] - shellwidgets = [_w for _w in shellwidgets - if isinstance(_w, pythonshell.ExternalPythonShell) \ - and _w.is_running()] - if shellwidgets: - # First, iterate on interpreters only: - for shellwidget in shellwidgets: - if shellwidget.is_interpreter: - return shellwidget.shell - else: - return shellwidgets[0].shell - - def run_script_in_current_shell(self, filename, wdir, args, debug): - """Run script in current shell, if any""" - norm = lambda text: remove_backslashes(to_text_string(text)) - line = "%s('%s'" % ('debugfile' if debug else 'runfile', - norm(filename)) - if args: - line += ", args='%s'" % norm(args) - if wdir: - line += ", wdir='%s'" % norm(wdir) - line += ")" - if not self.execute_python_code(line, interpreter_only=True): - QMessageBox.warning(self, _('Warning'), - _("No Python console is currently selected to run %s." - "

    Please select or open a new Python console " - "and try again." - ) % osp.basename(norm(filename)), QMessageBox.Ok) - else: - self.visibility_changed(True) - self.raise_() - - def set_current_shell_working_directory(self, directory): - """Set current shell working directory""" - shellwidget = self.__find_python_shell() - if shellwidget is not None: - directory = encoding.to_unicode_from_fs(directory) - shellwidget.shell.set_cwd(directory) - - def execute_python_code(self, lines, interpreter_only=False): - """Execute Python code in an already opened Python interpreter""" - shellwidget = self.__find_python_shell( - interpreter_only=interpreter_only) - if (shellwidget is not None) and (not shellwidget.is_ipykernel): - shellwidget.shell.execute_lines(to_text_string(lines)) - self.activateWindow() - shellwidget.shell.setFocus() - return True - else: - return False - - def pdb_has_stopped(self, fname, lineno, shellwidget): - """Python debugger has just stopped at frame (fname, lineno)""" - # This is a unique form of the edit_goto signal that is intended to - # prevent keyboard input from accidentally entering the editor - # during repeated, rapid entry of debugging commands. - self.emit(SIGNAL("edit_goto(QString,int,QString,bool)"), - fname, lineno, '', False) - if shellwidget.is_ipykernel: - # Focus client widget, not kernel - ipw = self.main.ipyconsole.get_focus_widget() - self.main.ipyconsole.activateWindow() - ipw.setFocus() - else: - self.activateWindow() - shellwidget.shell.setFocus() - - def set_spyder_breakpoints(self): - """Set all Spyder breakpoints into all shells""" - for shellwidget in self.shellwidgets: - shellwidget.shell.set_spyder_breakpoints() - - def start(self, fname, wdir=None, args='', interact=False, debug=False, - python=True, ipykernel=False, ipyclient=None, - give_ipyclient_focus=True, python_args=''): - """ - Start new console - - fname: - string: filename of script to run - None: open an interpreter - wdir: working directory - args: command line options of the Python script - interact: inspect script interactively after its execution - debug: run pdb - python: True: Python interpreter, False: terminal - ipykernel: True: IPython kernel - ipyclient: True: Automatically create an IPython client - python_args: additionnal Python interpreter command line options - (option "-u" is mandatory, see widgets.externalshell package) - """ - # Note: fname is None <=> Python interpreter - if fname is not None and not is_text_string(fname): - fname = to_text_string(fname) - if wdir is not None and not is_text_string(wdir): - wdir = to_text_string(wdir) - - if fname is not None and fname in self.filenames: - index = self.filenames.index(fname) - if self.get_option('single_tab'): - old_shell = self.shellwidgets[index] - if old_shell.is_running(): - runconfig = get_run_configuration(fname) - if runconfig is None or runconfig.show_kill_warning: - answer = QMessageBox.question(self, self.get_plugin_title(), - _("%s is already running in a separate process.\n" - "Do you want to kill the process before starting " - "a new one?") % osp.basename(fname), - QMessageBox.Yes | QMessageBox.Cancel) - else: - answer = QMessageBox.Yes - - if answer == QMessageBox.Yes: - old_shell.process.kill() - old_shell.process.waitForFinished() - else: - return - self.close_console(index) - else: - index = self.tabwidget.count() - - # Creating a new external shell - pythonpath = self.main.get_spyder_pythonpath() - light_background = self.get_option('light_background') - show_elapsed_time = self.get_option('show_elapsed_time') - if python: - if self.get_option('pythonexecutable/default'): - pythonexecutable = get_python_executable() - else: - pythonexecutable = self.get_option('pythonexecutable') - if self.get_option('pythonstartup/default') or ipykernel: - pythonstartup = None - else: - pythonstartup = self.get_option('pythonstartup', None) - monitor_enabled = self.get_option('monitor/enabled') - if self.get_option('matplotlib/backend/enabled'): - mpl_backend = self.get_option('matplotlib/backend/value') - else: - mpl_backend = None - ets_backend = self.get_option('ets_backend') - qt_api = self.get_option('qt/api') - if qt_api not in ('pyqt', 'pyside'): - qt_api = None - pyqt_api = self.get_option('pyqt/api_version') - ignore_sip_setapi_errors = self.get_option( - 'pyqt/ignore_sip_setapi_errors') - merge_output_channels = self.get_option('merge_output_channels') - colorize_sys_stderr = self.get_option('colorize_sys_stderr') - umr_enabled = self.get_option('umr/enabled') - umr_namelist = self.get_option('umr/namelist') - umr_verbose = self.get_option('umr/verbose') - ar_timeout = CONF.get('variable_explorer', 'autorefresh/timeout') - ar_state = CONF.get('variable_explorer', 'autorefresh') - - # CRUCIAL NOTE FOR IPYTHON KERNELS: - # autorefresh needs to be on so that our monitor - # can find __ipythonkernel__ in the globals namespace - # *after* the kernel has been started. - # Without the ns refresh provided by autorefresh, a - # client is *never* started (although the kernel is) - # Fix Issue 1595 - if not ar_state and ipykernel: - ar_state = True - - if self.light_mode: - from spyderlib.plugins.variableexplorer import VariableExplorer - sa_settings = VariableExplorer.get_settings() - else: - sa_settings = None - shellwidget = ExternalPythonShell(self, fname, wdir, - interact, debug, path=pythonpath, - python_args=python_args, - ipykernel=ipykernel, - arguments=args, stand_alone=sa_settings, - pythonstartup=pythonstartup, - pythonexecutable=pythonexecutable, - umr_enabled=umr_enabled, umr_namelist=umr_namelist, - umr_verbose=umr_verbose, ets_backend=ets_backend, - monitor_enabled=monitor_enabled, - mpl_backend=mpl_backend, - qt_api=qt_api, pyqt_api=pyqt_api, - ignore_sip_setapi_errors=ignore_sip_setapi_errors, - merge_output_channels=merge_output_channels, - colorize_sys_stderr=colorize_sys_stderr, - autorefresh_timeout=ar_timeout, - autorefresh_state=ar_state, - light_background=light_background, - menu_actions=self.menu_actions, - show_buttons_inside=False, - show_elapsed_time=show_elapsed_time) - self.connect(shellwidget, SIGNAL('pdb(QString,int)'), - lambda fname, lineno, shellwidget=shellwidget: - self.pdb_has_stopped(fname, lineno, shellwidget)) - self.register_widget_shortcuts("Console", shellwidget.shell) - else: - if os.name == 'posix': - cmd = 'gnome-terminal' - args = [] - if programs.is_program_installed(cmd): - if wdir: - args.extend(['--working-directory=%s' % wdir]) - programs.run_program(cmd, args) - return - cmd = 'konsole' - if programs.is_program_installed(cmd): - if wdir: - args.extend(['--workdir', wdir]) - programs.run_program(cmd, args) - return - shellwidget = ExternalSystemShell(self, wdir, path=pythonpath, - light_background=light_background, - menu_actions=self.menu_actions, - show_buttons_inside=False, - show_elapsed_time=show_elapsed_time) - - # Code completion / calltips - shellwidget.shell.setMaximumBlockCount( - self.get_option('max_line_count') ) - shellwidget.shell.set_font( self.get_plugin_font() ) - shellwidget.shell.toggle_wrap_mode( self.get_option('wrap') ) - shellwidget.shell.set_calltips( self.get_option('calltips') ) - shellwidget.shell.set_codecompletion_auto( - self.get_option('codecompletion/auto') ) - shellwidget.shell.set_codecompletion_case( - self.get_option('codecompletion/case_sensitive') ) - shellwidget.shell.set_codecompletion_enter( - self.get_option('codecompletion/enter_key') ) - if python and self.inspector is not None: - shellwidget.shell.set_inspector(self.inspector) - shellwidget.shell.set_inspector_enabled( - CONF.get('inspector', 'connect/python_console')) - if self.historylog is not None: - self.historylog.add_history(shellwidget.shell.history_filename) - self.connect(shellwidget.shell, - SIGNAL('append_to_history(QString,QString)'), - self.historylog.append_to_history) - self.connect(shellwidget.shell, SIGNAL("go_to_error(QString)"), - self.go_to_error) - self.connect(shellwidget.shell, SIGNAL("focus_changed()"), - lambda: self.emit(SIGNAL("focus_changed()"))) - if python: - if self.main.editor is not None: - self.connect(shellwidget, SIGNAL('open_file(QString,int)'), - self.open_file_in_spyder) - if fname is None: - if ipykernel: - # Connect client to any possible error while starting the - # kernel - ipyclient.connect(shellwidget, - SIGNAL("ipython_kernel_start_error(QString)"), - lambda error: ipyclient.show_kernel_error(error)) - - # Detect if kernel and frontend match or not - # Don't apply this for our Mac app because it's - # failing, see Issue 2006 - if self.get_option('pythonexecutable/custom') and \ - not running_in_mac_app(): - frontend_ver = programs.get_module_version('IPython') - old_vers = ['1', '2'] - if any([frontend_ver.startswith(v) for v in old_vers]): - frontend_ver = '<3.0' - else: - frontend_ver = '>=3.0' - pyexec = self.get_option('pythonexecutable') - kernel_and_frontend_match = \ - programs.is_module_installed('IPython', - version=frontend_ver, - interpreter=pyexec) - else: - kernel_and_frontend_match = True - - # Create a a kernel tab only if frontend and kernel - # versions match - if kernel_and_frontend_match: - tab_name = _("Kernel") - tab_icon1 = get_icon('ipython_console.png') - tab_icon2 = get_icon('ipython_console_t.png') - self.connect(shellwidget, - SIGNAL('create_ipython_client(QString)'), - lambda cf: self.register_ipyclient(cf, - ipyclient, - shellwidget, - give_focus=give_ipyclient_focus)) - else: - shellwidget.emit( - SIGNAL("ipython_kernel_start_error(QString)"), - _("Either:" - "
      " - "
    1. Your IPython frontend and kernel versions " - "are incompatible or
    2. " - "
    3. You don't have IPython installed in " - "your external interpreter.
    4. " - "
    " - "In any case, we're sorry but we can't create a " - "console for you.")) - shellwidget.deleteLater() - shellwidget = None - return - else: - self.python_count += 1 - tab_name = "Python %d" % self.python_count - tab_icon1 = get_icon('python.png') - tab_icon2 = get_icon('python_t.png') - else: - tab_name = osp.basename(fname) - tab_icon1 = get_icon('run.png') - tab_icon2 = get_icon('terminated.png') - else: - fname = id(shellwidget) - if os.name == 'nt': - tab_name = _("Command Window") - else: - tab_name = _("Terminal") - self.terminal_count += 1 - tab_name += (" %d" % self.terminal_count) - tab_icon1 = get_icon('cmdprompt.png') - tab_icon2 = get_icon('cmdprompt_t.png') - self.shellwidgets.insert(index, shellwidget) - self.filenames.insert(index, fname) - self.icons.insert(index, (tab_icon1, tab_icon2)) - if index is None: - index = self.tabwidget.addTab(shellwidget, tab_name) - else: - self.tabwidget.insertTab(index, shellwidget, tab_name) - - self.connect(shellwidget, SIGNAL("started()"), - lambda sid=id(shellwidget): self.process_started(sid)) - self.connect(shellwidget, SIGNAL("finished()"), - lambda sid=id(shellwidget): self.process_finished(sid)) - self.find_widget.set_editor(shellwidget.shell) - self.tabwidget.setTabToolTip(index, fname if wdir is None else wdir) - self.tabwidget.setCurrentIndex(index) - if self.dockwidget and not self.ismaximized and not ipykernel: - self.dockwidget.setVisible(True) - self.dockwidget.raise_() - - shellwidget.set_icontext_visible(self.get_option('show_icontext')) - - # Start process and give focus to console - shellwidget.start_shell() - if not ipykernel: - self.activateWindow() - shellwidget.shell.setFocus() - - def set_ipykernel_attrs(self, connection_file, kernel_widget, name): - """Add the pid of the kernel process to an IPython kernel tab""" - # Set connection file - kernel_widget.connection_file = connection_file - - # If we've reached this point then it's safe to assume IPython - # is available, and this import should be valid. - from IPython.core.application import get_ipython_dir - # For each kernel we launch, setup to delete the associated - # connection file at the time Spyder exits. - def cleanup_connection_file(connection_file): - connection_file = osp.join(get_ipython_dir(), 'profile_default', - 'security', connection_file) - try: - os.remove(connection_file) - except OSError: - pass - atexit.register(cleanup_connection_file, connection_file) - - # Set tab name according to client master name - index = self.get_shell_index_from_id(id(kernel_widget)) - tab_name = _("Kernel %s") % name - self.tabwidget.setTabText(index, tab_name) - - def register_ipyclient(self, connection_file, ipyclient, kernel_widget, - give_focus=True): - """ - Register `ipyclient` to be connected to `kernel_widget` - """ - # Check if our client already has a connection_file and kernel_widget_id - # which means that we are asking for a kernel restart - if ipyclient.connection_file is not None \ - and ipyclient.kernel_widget_id is not None: - restart_kernel = True - else: - restart_kernel = False - - # Setting kernel widget attributes - name = ipyclient.name.split('/')[0] - self.set_ipykernel_attrs(connection_file, kernel_widget, name) - - # Creating the client - ipyconsole = self.main.ipyconsole - ipyclient.connection_file = connection_file - ipyclient.kernel_widget_id = id(kernel_widget) - ipyconsole.register_client(ipyclient, restart=restart_kernel, - give_focus=give_focus) - - def open_file_in_spyder(self, fname, lineno): - """Open file in Spyder's editor from remote process""" - self.main.editor.activateWindow() - self.main.editor.raise_() - self.main.editor.load(fname, lineno) - - #------ Private API ------------------------------------------------------- - def process_started(self, shell_id): - index = self.get_shell_index_from_id(shell_id) - shell = self.shellwidgets[index] - icon, _icon = self.icons[index] - self.tabwidget.setTabIcon(index, icon) - if self.inspector is not None: - self.inspector.set_shell(shell.shell) - if self.variableexplorer is not None: - self.variableexplorer.add_shellwidget(shell) - - def process_finished(self, shell_id): - index = self.get_shell_index_from_id(shell_id) - if index is not None: - # Not sure why it happens, but sometimes the shellwidget has - # already been removed, so that's not bad if we can't change - # the tab icon... - _icon, icon = self.icons[index] - self.tabwidget.setTabIcon(index, icon) - if self.variableexplorer is not None: - self.variableexplorer.remove_shellwidget(shell_id) - - #------ SpyderPluginWidget API -------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - title = _('Console') - if self.filenames: - index = self.tabwidget.currentIndex() - fname = self.filenames[index] - if fname: - title += ' - ' + to_text_string(fname) - return title - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('console.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.tabwidget.currentWidget() - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - interpreter_action = create_action(self, - _("Open a &Python console"), None, - 'python.png', triggered=self.open_interpreter) - if os.name == 'nt': - text = _("Open &command prompt") - tip = _("Open a Windows command prompt") - else: - text = _("Open a &terminal") - tip = _("Open a terminal window") - terminal_action = create_action(self, text, None, None, tip, - triggered=self.open_terminal) - run_action = create_action(self, - _("&Run..."), None, - 'run_small.png', _("Run a Python script"), - triggered=self.run_script) - - consoles_menu_actions = [interpreter_action] - tools_menu_actions = [terminal_action] - self.menu_actions = [interpreter_action, terminal_action, run_action] - - self.main.consoles_menu_actions += consoles_menu_actions - self.main.tools_menu_actions += tools_menu_actions - - return self.menu_actions+consoles_menu_actions+tools_menu_actions - - def register_plugin(self): - """Register plugin in Spyder's main window""" - if self.main.light: - self.main.setCentralWidget(self) - self.main.widgetlist.append(self) - else: - self.main.add_dockwidget(self) - self.inspector = self.main.inspector - if self.inspector is not None: - self.inspector.set_external_console(self) - self.historylog = self.main.historylog - self.connect(self, SIGNAL("edit_goto(QString,int,QString)"), - self.main.editor.load) - self.connect(self, SIGNAL("edit_goto(QString,int,QString,bool)"), - lambda fname, lineno, word, processevents: - self.main.editor.load(fname, lineno, word, - processevents=processevents)) - self.connect(self.main.editor, - SIGNAL('run_in_current_extconsole(QString,QString,QString,bool)'), - self.run_script_in_current_shell) - self.connect(self.main.editor, - SIGNAL("breakpoints_saved()"), - self.set_spyder_breakpoints) - self.connect(self.main.editor, SIGNAL("open_dir(QString)"), - self.set_current_shell_working_directory) - self.connect(self.main.workingdirectory, - SIGNAL("set_current_console_wd(QString)"), - self.set_current_shell_working_directory) - self.connect(self, SIGNAL('focus_changed()'), - self.main.plugin_focus_changed) - self.connect(self, SIGNAL('redirect_stdio(bool)'), - self.main.redirect_internalshell_stdio) - expl = self.main.explorer - if expl is not None: - self.connect(expl, SIGNAL("open_terminal(QString)"), - self.open_terminal) - self.connect(expl, SIGNAL("open_interpreter(QString)"), - self.open_interpreter) - pexpl = self.main.projectexplorer - if pexpl is not None: - self.connect(pexpl, SIGNAL("open_terminal(QString)"), - self.open_terminal) - self.connect(pexpl, SIGNAL("open_interpreter(QString)"), - self.open_interpreter) - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - for shellwidget in self.shellwidgets: - shellwidget.close() - return True - - def refresh_plugin(self): - """Refresh tabwidget""" - shellwidget = None - if self.tabwidget.count(): - shellwidget = self.tabwidget.currentWidget() - editor = shellwidget.shell - editor.setFocus() - widgets = [shellwidget.create_time_label(), 5 - ]+shellwidget.get_toolbar_buttons()+[5] - else: - editor = None - widgets = [] - self.find_widget.set_editor(editor) - self.tabwidget.set_corner_widgets({Qt.TopRightCorner: widgets}) - if shellwidget: - shellwidget.update_time_label_visibility() - self.main.last_console_plugin_focus_was_python = True - self.emit(SIGNAL('update_plugin_title()')) - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - font_n = 'plugin_font' - font_o = self.get_plugin_font() - showtime_n = 'show_elapsed_time' - showtime_o = self.get_option(showtime_n) - icontext_n = 'show_icontext' - icontext_o = self.get_option(icontext_n) - calltips_n = 'calltips' - calltips_o = self.get_option(calltips_n) - inspector_n = 'connect_to_oi' - inspector_o = CONF.get('inspector', 'connect/python_console') - wrap_n = 'wrap' - wrap_o = self.get_option(wrap_n) - compauto_n = 'codecompletion/auto' - compauto_o = self.get_option(compauto_n) - case_comp_n = 'codecompletion/case_sensitive' - case_comp_o = self.get_option(case_comp_n) - compenter_n = 'codecompletion/enter_key' - compenter_o = self.get_option(compenter_n) - mlc_n = 'max_line_count' - mlc_o = self.get_option(mlc_n) - for shellwidget in self.shellwidgets: - if font_n in options: - shellwidget.shell.set_font(font_o) - completion_size = CONF.get('shell_appearance', - 'completion/size') - comp_widget = shellwidget.shell.completion_widget - comp_widget.setup_appearance(completion_size, font_o) - if showtime_n in options: - shellwidget.set_elapsed_time_visible(showtime_o) - if icontext_n in options: - shellwidget.set_icontext_visible(icontext_o) - if calltips_n in options: - shellwidget.shell.set_calltips(calltips_o) - if inspector_n in options: - if isinstance(shellwidget, ExternalPythonShell): - shellwidget.shell.set_inspector_enabled(inspector_o) - if wrap_n in options: - shellwidget.shell.toggle_wrap_mode(wrap_o) - if compauto_n in options: - shellwidget.shell.set_codecompletion_auto(compauto_o) - if case_comp_n in options: - shellwidget.shell.set_codecompletion_case(case_comp_o) - if compenter_n in options: - shellwidget.shell.set_codecompletion_enter(compenter_o) - if mlc_n in options: - shellwidget.shell.setMaximumBlockCount(mlc_o) - - #------ SpyderPluginMixin API --------------------------------------------- - def toggle_view(self, checked): - """Toggle view""" - if checked: - self.dockwidget.show() - self.dockwidget.raise_() - # Start a console in case there are none shown - from spyderlib.widgets.externalshell import pythonshell - consoles = None - for sw in self.shellwidgets: - if isinstance(sw, pythonshell.ExternalPythonShell): - if not sw.is_ipykernel: - consoles = True - break - if not consoles: - self.open_interpreter() - else: - self.dockwidget.hide() - - #------ Public API --------------------------------------------------------- - def open_interpreter(self, wdir=None): - """Open interpreter""" - if wdir is None: - wdir = getcwd() - if not self.main.light: - self.visibility_changed(True) - self.start(fname=None, wdir=to_text_string(wdir), args='', - interact=True, debug=False, python=True) - - def start_ipykernel(self, client, wdir=None, give_focus=True): - """Start new IPython kernel""" - if not self.get_option('monitor/enabled'): - QMessageBox.warning(self, _('Open an IPython console'), - _("The console monitor was disabled: the IPython kernel will " - "be started as expected, but an IPython console will have " - "to be connected manually to the kernel."), QMessageBox.Ok) - - if wdir is None: - wdir = getcwd() - self.main.ipyconsole.visibility_changed(True) - self.start(fname=None, wdir=to_text_string(wdir), args='', - interact=True, debug=False, python=True, ipykernel=True, - ipyclient=client, give_ipyclient_focus=give_focus) - - def open_terminal(self, wdir=None): - """Open terminal""" - if wdir is None: - wdir = getcwd() - self.start(fname=None, wdir=to_text_string(wdir), args='', - interact=True, debug=False, python=False) - - def run_script(self): - """Run a Python script""" - self.emit(SIGNAL('redirect_stdio(bool)'), False) - filename, _selfilter = getopenfilename(self, _("Run Python script"), - getcwd(), _("Python scripts")+" (*.py ; *.pyw ; *.ipy)") - self.emit(SIGNAL('redirect_stdio(bool)'), True) - if filename: - self.start(fname=filename, wdir=None, args='', - interact=False, debug=False) - - def set_umr_namelist(self): - """Set UMR excluded modules name list""" - arguments, valid = QInputDialog.getText(self, _('UMR'), - _('UMR excluded modules:\n' - '(example: guidata, guiqwt)'), - QLineEdit.Normal, - ", ".join(self.get_option('umr/namelist'))) - if valid: - arguments = to_text_string(arguments) - if arguments: - namelist = arguments.replace(' ', '').split(',') - fixed_namelist = [module_name for module_name in namelist - if programs.is_module_installed(module_name)] - invalid = ", ".join(set(namelist)-set(fixed_namelist)) - if invalid: - QMessageBox.warning(self, _('UMR'), - _("The following modules are not " - "installed on your machine:\n%s" - ) % invalid, QMessageBox.Ok) - QMessageBox.information(self, _('UMR'), - _("Please note that these changes will " - "be applied only to new Python/IPython " - "consoles"), QMessageBox.Ok) - else: - fixed_namelist = [] - self.set_option('umr/namelist', fixed_namelist) - - def go_to_error(self, text): - """Go to error if relevant""" - match = get_error_match(to_text_string(text)) - if match: - fname, lnb = match.groups() - self.emit(SIGNAL("edit_goto(QString,int,QString)"), - osp.abspath(fname), int(lnb), '') - - #----Drag and drop - def dragEnterEvent(self, event): - """Reimplement Qt method - Inform Qt about the types of data that the widget accepts""" - source = event.mimeData() - if source.hasUrls(): - if mimedata2url(source): - pathlist = mimedata2url(source) - shellwidget = self.tabwidget.currentWidget() - if all([is_python_script(to_text_string(qstr)) - for qstr in pathlist]): - event.acceptProposedAction() - elif shellwidget is None or not shellwidget.is_running(): - event.ignore() - else: - event.acceptProposedAction() - else: - event.ignore() - elif source.hasText(): - event.acceptProposedAction() - - def dropEvent(self, event): - """Reimplement Qt method - Unpack dropped data and handle it""" - source = event.mimeData() - shellwidget = self.tabwidget.currentWidget() - if source.hasText(): - qstr = source.text() - if is_python_script(to_text_string(qstr)): - self.start(qstr) - elif shellwidget: - shellwidget.shell.insert_text(qstr) - elif source.hasUrls(): - pathlist = mimedata2url(source) - if all([is_python_script(to_text_string(qstr)) - for qstr in pathlist]): - for fname in pathlist: - self.start(fname) - elif shellwidget: - shellwidget.shell.drop_pathlist(pathlist) - event.acceptProposedAction() diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/findinfiles.py spyder-3.0.2+dfsg1/spyderlib/plugins/findinfiles.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/findinfiles.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/findinfiles.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,151 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Find in Files Plugin""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import QApplication -from spyderlib.qt.QtCore import SIGNAL, Signal - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.utils.qthelpers import create_action -from spyderlib.widgets.findinfiles import FindInFilesWidget -from spyderlib.plugins import SpyderPluginMixin -from spyderlib.py3compat import getcwd - - -class FindInFiles(FindInFilesWidget, SpyderPluginMixin): - """Find in files DockWidget""" - CONF_SECTION = 'find_in_files' - sig_option_changed = Signal(str, object) - def __init__(self, parent=None): - supported_encodings = self.get_option('supported_encodings') - - search_path = self.get_option('search_path', None) - self.search_text_samples = self.get_option('search_text_samples') - search_text = self.get_option('search_text') - search_text = [txt for txt in search_text \ - if txt not in self.search_text_samples] - search_text += self.search_text_samples - - search_text_regexp = self.get_option('search_text_regexp') - include = self.get_option('include') - include_idx = self.get_option('include_idx', None) - include_regexp = self.get_option('include_regexp') - exclude = self.get_option('exclude') - exclude_idx = self.get_option('exclude_idx', None) - exclude_regexp = self.get_option('exclude_regexp') - in_python_path = self.get_option('in_python_path') - more_options = self.get_option('more_options') - FindInFilesWidget.__init__(self, parent, - search_text, search_text_regexp, search_path, - include, include_idx, include_regexp, - exclude, exclude_idx, exclude_regexp, - supported_encodings, - in_python_path, more_options) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - self.connect(self, SIGNAL('toggle_visibility(bool)'), self.toggle) - - def toggle(self, state): - """Toggle widget visibility""" - if self.dockwidget: - self.dockwidget.setVisible(state) - - def refreshdir(self): - """Refresh search directory""" - self.find_options.set_directory(getcwd()) - - def findinfiles_callback(self): - """Find in files callback""" - widget = QApplication.focusWidget() - if not self.ismaximized: - self.dockwidget.setVisible(True) - self.dockwidget.raise_() - text = '' - try: - if widget.has_selected_text(): - text = widget.get_selected_text() - except AttributeError: - # This is not a text widget deriving from TextEditBaseWidget - pass - self.set_search_text(text) - if text: - self.find() - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _("Find in files") - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.find_options.search_text - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - return [] - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.get_pythonpath_callback = self.main.get_spyder_pythonpath - self.main.add_dockwidget(self) - self.connect(self, SIGNAL("edit_goto(QString,int,QString)"), - self.main.editor.load) - self.connect(self, SIGNAL('redirect_stdio(bool)'), - self.main.redirect_internalshell_stdio) - self.connect(self.main.workingdirectory, - SIGNAL("refresh_findinfiles()"), self.refreshdir) - - findinfiles_action = create_action(self, _("&Find in files"), - icon='findf.png', - triggered=self.findinfiles_callback, - tip=_("Search text in multiple files")) - - self.main.search_menu_actions += [None, findinfiles_action] - self.main.search_toolbar_actions += [None, findinfiles_action] - - def refresh_plugin(self): - """Refresh widget""" - pass - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - self.closing_widget() # stop search thread and clean-up - options = self.find_options.get_options(all=True) - if options is not None: - search_text, text_re, search_path, \ - include, include_idx, include_re, \ - exclude, exclude_idx, exclude_re, \ - in_python_path, more_options = options - hist_limit = 15 - search_text = search_text[:hist_limit] - search_path = search_path[:hist_limit] - include = include[:hist_limit] - exclude = exclude[:hist_limit] - self.set_option('search_text', search_text) - self.set_option('search_text_regexp', text_re) - self.set_option('search_path', search_path) - self.set_option('include', include) - self.set_option('include_idx', include_idx) - self.set_option('include_regexp', include_re) - self.set_option('exclude', exclude) - self.set_option('exclude_idx', exclude_idx) - self.set_option('exclude_regexp', exclude_re) - self.set_option('in_python_path', in_python_path) - self.set_option('more_options', more_options) - return True diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/history.py spyder-3.0.2+dfsg1/spyderlib/plugins/history.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/history.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/history.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,294 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Console History Plugin""" - -from spyderlib.qt.QtGui import (QVBoxLayout, QFontDialog, QInputDialog, - QToolButton, QMenu, QFontComboBox, QGroupBox, - QHBoxLayout, QWidget) -from spyderlib.qt.QtCore import SIGNAL - -import os.path as osp -import sys - -# Local imports -from spyderlib.utils import encoding -from spyderlib.baseconfig import _ -from spyderlib.config import CONF -from spyderlib.guiconfig import get_color_scheme -from spyderlib.utils.qthelpers import (get_icon, create_action, - create_toolbutton, add_actions) -from spyderlib.widgets.tabs import Tabs -from spyderlib.widgets.sourcecode import codeeditor -from spyderlib.widgets.findreplace import FindReplace -from spyderlib.plugins import SpyderPluginWidget, PluginConfigPage -from spyderlib.py3compat import to_text_string, is_text_string - - -class HistoryConfigPage(PluginConfigPage): - def get_icon(self): - return get_icon('history24.png') - - def setup_page(self): - settings_group = QGroupBox(_("Settings")) - hist_spin = self.create_spinbox( - _("History depth: "), _(" entries"), - 'max_entries', min_=10, max_=10000, step=10, - tip=_("Set maximum line count")) - - sourcecode_group = QGroupBox(_("Source code")) - wrap_mode_box = self.create_checkbox(_("Wrap lines"), 'wrap') - go_to_eof_box = self.create_checkbox( - _("Scroll automatically to last entry"), 'go_to_eof') - font_group = self.create_fontgroup(option=None, - text=_("Font style"), - fontfilters=QFontComboBox.MonospacedFonts) - names = CONF.get('color_schemes', 'names') - choices = list(zip(names, names)) - cs_combo = self.create_combobox(_("Syntax color scheme: "), - choices, 'color_scheme_name') - - settings_layout = QVBoxLayout() - settings_layout.addWidget(hist_spin) - settings_group.setLayout(settings_layout) - - sourcecode_layout = QVBoxLayout() - sourcecode_layout.addWidget(wrap_mode_box) - sourcecode_layout.addWidget(go_to_eof_box) - sourcecode_layout.addWidget(cs_combo) - sourcecode_group.setLayout(sourcecode_layout) - - vlayout = QVBoxLayout() - vlayout.addWidget(settings_group) - vlayout.addWidget(font_group) - vlayout.addWidget(sourcecode_group) - vlayout.addStretch(1) - self.setLayout(vlayout) - - -class HistoryLog(SpyderPluginWidget): - """ - History log widget - """ - CONF_SECTION = 'historylog' - CONFIGWIDGET_CLASS = HistoryConfigPage - def __init__(self, parent): - self.tabwidget = None - self.menu_actions = None - self.dockviewer = None - self.wrap_action = None - - self.editors = [] - self.filenames = [] - self.icons = [] - - SpyderPluginWidget.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - self.set_default_color_scheme() - - layout = QVBoxLayout() - self.tabwidget = Tabs(self, self.menu_actions) - self.connect(self.tabwidget, SIGNAL('currentChanged(int)'), - self.refresh_plugin) - self.connect(self.tabwidget, SIGNAL('move_data(int,int)'), - self.move_tab) - - if sys.platform == 'darwin': - tab_container = QWidget() - tab_container.setObjectName('tab-container') - tab_layout = QHBoxLayout(tab_container) - tab_layout.setContentsMargins(0, 0, 0, 0) - tab_layout.addWidget(self.tabwidget) - layout.addWidget(tab_container) - else: - layout.addWidget(self.tabwidget) - - # Menu as corner widget - options_button = create_toolbutton(self, text=_("Options"), - icon=get_icon('tooloptions.png')) - options_button.setPopupMode(QToolButton.InstantPopup) - menu = QMenu(self) - add_actions(menu, self.menu_actions) - options_button.setMenu(menu) - self.tabwidget.setCornerWidget(options_button) - - # Find/replace widget - self.find_widget = FindReplace(self) - self.find_widget.hide() - self.register_widget_shortcuts("Editor", self.find_widget) - - layout.addWidget(self.find_widget) - - self.setLayout(layout) - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _('History log') - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('history.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.tabwidget.currentWidget() - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - return True - - def refresh_plugin(self): - """Refresh tabwidget""" - if self.tabwidget.count(): - editor = self.tabwidget.currentWidget() - else: - editor = None - self.find_widget.set_editor(editor) - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - history_action = create_action(self, _("History..."), - None, 'history.png', - _("Set history maximum entries"), - triggered=self.change_history_depth) - font_action = create_action(self, _("&Font..."), None, - 'font.png', _("Set shell font style"), - triggered=self.change_font) - self.wrap_action = create_action(self, _("Wrap lines"), - toggled=self.toggle_wrap_mode) - self.wrap_action.setChecked( self.get_option('wrap') ) - self.menu_actions = [history_action, font_action, self.wrap_action] - return self.menu_actions - - def on_first_registration(self): - """Action to be performed on first plugin registration""" - self.main.tabify_plugins(self.main.extconsole, self) - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self, SIGNAL('focus_changed()'), - self.main.plugin_focus_changed) - self.main.add_dockwidget(self) -# self.main.console.set_historylog(self) - self.connect(self.main.console.shell, SIGNAL("refresh()"), - self.refresh_plugin) - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - color_scheme_n = 'color_scheme_name' - color_scheme_o = get_color_scheme(self.get_option(color_scheme_n)) - font_n = 'plugin_font' - font_o = self.get_plugin_font() - wrap_n = 'wrap' - wrap_o = self.get_option(wrap_n) - self.wrap_action.setChecked(wrap_o) - for editor in self.editors: - if font_n in options: - scs = color_scheme_o if color_scheme_n in options else None - editor.set_font(font_o, scs) - elif color_scheme_n in options: - editor.set_color_scheme(color_scheme_o) - if wrap_n in options: - editor.toggle_wrap_mode(wrap_o) - - #------ Private API -------------------------------------------------------- - def move_tab(self, index_from, index_to): - """ - Move tab (tabs themselves have already been moved by the tabwidget) - """ - filename = self.filenames.pop(index_from) - editor = self.editors.pop(index_from) - icon = self.icons.pop(index_from) - - self.filenames.insert(index_to, filename) - self.editors.insert(index_to, editor) - self.icons.insert(index_to, icon) - - #------ Public API --------------------------------------------------------- - def add_history(self, filename): - """ - Add new history tab - Slot for SIGNAL('add_history(QString)') emitted by shell instance - """ - filename = encoding.to_unicode_from_fs(filename) - if filename in self.filenames: - return - editor = codeeditor.CodeEditor(self) - if osp.splitext(filename)[1] == '.py': - language = 'py' - icon = get_icon('python.png') - else: - language = 'bat' - icon = get_icon('cmdprompt.png') - editor.setup_editor(linenumbers=False, language=language, - scrollflagarea=False) - self.connect(editor, SIGNAL("focus_changed()"), - lambda: self.emit(SIGNAL("focus_changed()"))) - editor.setReadOnly(True) - color_scheme = get_color_scheme(self.get_option('color_scheme_name')) - editor.set_font( self.get_plugin_font(), color_scheme ) - editor.toggle_wrap_mode( self.get_option('wrap') ) - - text, _ = encoding.read(filename) - editor.set_text(text) - editor.set_cursor_position('eof') - - self.editors.append(editor) - self.filenames.append(filename) - self.icons.append(icon) - index = self.tabwidget.addTab(editor, osp.basename(filename)) - self.find_widget.set_editor(editor) - self.tabwidget.setTabToolTip(index, filename) - self.tabwidget.setTabIcon(index, icon) - self.tabwidget.setCurrentIndex(index) - - def append_to_history(self, filename, command): - """ - Append an entry to history filename - Slot for SIGNAL('append_to_history(QString,QString)') - emitted by shell instance - """ - if not is_text_string(filename): # filename is a QString - filename = to_text_string(filename.toUtf8(), 'utf-8') - command = to_text_string(command) - index = self.filenames.index(filename) - self.editors[index].append(command) - if self.get_option('go_to_eof'): - self.editors[index].set_cursor_position('eof') - self.tabwidget.setCurrentIndex(index) - - def change_history_depth(self): - "Change history max entries""" - depth, valid = QInputDialog.getInteger(self, _('History'), - _('Maximum entries'), - self.get_option('max_entries'), - 10, 10000) - if valid: - self.set_option('max_entries', depth) - - def change_font(self): - """Change console font""" - font, valid = QFontDialog.getFont(self.get_plugin_font(), - self, _("Select a new font")) - if valid: - for editor in self.editors: - editor.set_font(font) - self.set_plugin_font(font) - - def toggle_wrap_mode(self, checked): - """Toggle wrap mode""" - if self.tabwidget is None: - return - for editor in self.editors: - editor.toggle_wrap_mode(checked) - self.set_option('wrap', checked) diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/__init__.py spyder-3.0.2+dfsg1/spyderlib/plugins/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/__init__.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,393 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.plugins -================= - -Here, 'plugins' are widgets designed specifically for Spyder -These plugins inherit the following classes -(SpyderPluginMixin & SpyderPluginWidget) -""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import (QDockWidget, QWidget, QShortcut, QCursor, - QKeySequence, QMainWindow, QApplication) -from spyderlib.qt.QtCore import SIGNAL, Qt, QObject, Signal - -import sys - -# Local imports -from spyderlib.utils.qthelpers import toggle_actions, get_icon, create_action -from spyderlib.baseconfig import _ -from spyderlib.config import CONF -from spyderlib.userconfig import NoDefault -from spyderlib.guiconfig import get_font, set_font -from spyderlib.plugins.configdialog import SpyderConfigPage -from spyderlib.py3compat import configparser, is_text_string - - -class PluginConfigPage(SpyderConfigPage): - """Plugin configuration dialog box page widget""" - def __init__(self, plugin, parent): - self.plugin = plugin - self.get_option = plugin.get_option - self.set_option = plugin.set_option - self.get_font = plugin.get_plugin_font - self.set_font = plugin.set_plugin_font - self.apply_settings = plugin.apply_plugin_settings - SpyderConfigPage.__init__(self, parent) - - def get_name(self): - return self.plugin.get_plugin_title() - - def get_icon(self): - return self.plugin.get_plugin_icon() - - -class SpyderDockWidget(QDockWidget): - """Subclass to override needed methods""" - - def closeEvent(self, event): - """ - Reimplement Qt method to send a signal on close so that "Panes" main - window menu can be updated correctly - """ - self.emit(SIGNAL('plugin_closed()')) - - -class SpyderPluginMixin(object): - """ - Useful methods to bind widgets to the main window - See SpyderPluginWidget class for required widget interface - - Signals: - sig_option_changed - Example: - plugin.sig_option_changed.emit('show_all', checked) - 'show_message(QString,int)' - """ - CONF_SECTION = None - CONFIGWIDGET_CLASS = None - ALLOWED_AREAS = Qt.AllDockWidgetAreas - LOCATION = Qt.LeftDockWidgetArea - FEATURES = QDockWidget.DockWidgetClosable | \ - QDockWidget.DockWidgetFloatable | \ - QDockWidget.DockWidgetMovable - DISABLE_ACTIONS_WHEN_HIDDEN = True - sig_option_changed = None - def __init__(self, main): - """Bind widget to a QMainWindow instance""" - super(SpyderPluginMixin, self).__init__() - assert self.CONF_SECTION is not None - self.main = main - self.default_margins = None - self.plugin_actions = None - self.dockwidget = None - self.mainwindow = None - self.ismaximized = False - self.isvisible = False - - # NOTE: Don't use the default option of CONF.get to assign a - # None shortcut to plugins that don't have one. That will mess - # the creation of our Keyboard Shortcuts prefs page - try: - self.shortcut = CONF.get('shortcuts', '_/switch to %s' % \ - self.CONF_SECTION) - except configparser.NoOptionError: - self.shortcut = None - - # We decided to create our own toggle action instead of using - # the one that comes with dockwidget because it's not possible - # to raise and focus the plugin with it. - self.toggle_view_action = None - - def initialize_plugin(self): - """Initialize plugin: connect signals, setup actions, ...""" - self.plugin_actions = self.get_plugin_actions() - QObject.connect(self, SIGNAL('show_message(QString,int)'), - self.show_message) - QObject.connect(self, SIGNAL('update_plugin_title()'), - self.__update_plugin_title) - if self.sig_option_changed is not None: - self.sig_option_changed.connect(self.set_option) - self.setWindowTitle(self.get_plugin_title()) - self.create_toggle_view_action() - - def on_first_registration(self): - """Action to be performed on first plugin registration""" - # Was written to handle the very first plugin position in Spyder's - # main window layout, but this could also be used for other things - # (see for example the IPython console plugin for which this method - # had to be written to handle the fact that this plugin was - # introduced between v2.1 and v2.2) - raise NotImplementedError - - def initialize_plugin_in_mainwindow_layout(self): - """If this is the first time the plugin is shown, perform actions to - initialize plugin position in Spyder's window layout""" - if self.get_option('first_time', True): - try: - self.on_first_registration() - except NotImplementedError: - return - self.set_option('first_time', False) - - def update_margins(self): - layout = self.layout() - if self.default_margins is None: - self.default_margins = layout.getContentsMargins() - if CONF.get('main', 'use_custom_margin'): - margin = CONF.get('main', 'custom_margin') - layout.setContentsMargins(*[margin]*4) - else: - layout.setContentsMargins(*self.default_margins) - - def __update_plugin_title(self): - """Update plugin title, i.e. dockwidget or mainwindow title""" - if self.dockwidget is not None: - win = self.dockwidget - elif self.mainwindow is not None: - win = self.mainwindow - else: - return - win.setWindowTitle(self.get_plugin_title()) - - def create_dockwidget(self): - """Add to parent QMainWindow as a dock widget""" - - # This is not clear yet why the following do not work... - # (see Issue #880) -## # Using Qt.Window window flags solves Issue #880 (detached dockwidgets -## # are not painted after restarting Spyder and restoring their hexstate) -## # but it does not work with PyQt <=v4.7 (dockwidgets can't be docked) -## # or non-Windows platforms (lot of warnings are printed out) -## # (so in those cases, we use the default window flags: Qt.Widget): -## flags = Qt.Widget if is_old_pyqt or os.name != 'nt' else Qt.Window - dock = SpyderDockWidget(self.get_plugin_title(), self.main)#, flags) - - dock.setObjectName(self.__class__.__name__+"_dw") - dock.setAllowedAreas(self.ALLOWED_AREAS) - dock.setFeatures(self.FEATURES) - dock.setWidget(self) - self.update_margins() - self.connect(dock, SIGNAL('visibilityChanged(bool)'), - self.visibility_changed) - self.connect(dock, SIGNAL('plugin_closed()'), - self.plugin_closed) - self.dockwidget = dock - if self.shortcut is not None: - sc = QShortcut(QKeySequence(self.shortcut), self.main, - self.switch_to_plugin) - self.register_shortcut(sc, "_", "Switch to %s" % self.CONF_SECTION) - return (dock, self.LOCATION) - - def create_mainwindow(self): - """ - Create a QMainWindow instance containing this plugin - Note: this method is currently not used - """ - self.mainwindow = mainwindow = QMainWindow() - mainwindow.setAttribute(Qt.WA_DeleteOnClose) - icon = self.get_widget_icon() - if is_text_string(icon): - icon = get_icon(icon) - mainwindow.setWindowIcon(icon) - mainwindow.setWindowTitle(self.get_plugin_title()) - mainwindow.setCentralWidget(self) - self.refresh_plugin() - return mainwindow - - def create_configwidget(self, parent): - """Create configuration dialog box page widget""" - if self.CONFIGWIDGET_CLASS is not None: - configwidget = self.CONFIGWIDGET_CLASS(self, parent) - configwidget.initialize() - return configwidget - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - raise NotImplementedError - - def register_shortcut(self, qaction_or_qshortcut, context, name, - default=NoDefault): - """ - Register QAction or QShortcut to Spyder main application, - with shortcut (context, name, default) - """ - self.main.register_shortcut(qaction_or_qshortcut, - context, name, default) - - def register_widget_shortcuts(self, context, widget): - """ - Register widget shortcuts - widget interface must have a method called 'get_shortcut_data' - """ - for qshortcut, name, default in widget.get_shortcut_data(): - self.register_shortcut(qshortcut, context, name, default) - - def switch_to_plugin(self): - """Switch to plugin - This method is called when pressing plugin's shortcut key""" - if not self.ismaximized: - self.dockwidget.show() - if not self.toggle_view_action.isChecked(): - self.toggle_view_action.setChecked(True) - self.visibility_changed(True) - - def visibility_changed(self, enable): - """DockWidget visibility has changed""" - if enable: - self.dockwidget.raise_() - widget = self.get_focus_widget() - if widget is not None: - widget.setFocus() - visible = self.dockwidget.isVisible() or self.ismaximized - if self.DISABLE_ACTIONS_WHEN_HIDDEN: - toggle_actions(self.plugin_actions, visible) - self.isvisible = enable and visible - if self.isvisible: - self.refresh_plugin() # To give focus to the plugin's widget - - def plugin_closed(self): - """DockWidget was closed""" - self.toggle_view_action.setChecked(False) - - def set_option(self, option, value): - """ - Set a plugin option in configuration file - Use a SIGNAL to call it, e.g.: - plugin.sig_option_changed.emit('show_all', checked) - """ - CONF.set(self.CONF_SECTION, str(option), value) - - def get_option(self, option, default=NoDefault): - """Get a plugin option from configuration file""" - return CONF.get(self.CONF_SECTION, option, default) - - def get_plugin_font(self, option=None): - """Return plugin font option""" - return get_font(self.CONF_SECTION, option) - - def set_plugin_font(self, font, option=None): - """Set plugin font option""" - set_font(font, self.CONF_SECTION, option) - - def show_message(self, message, timeout=0): - """Show message in main window's status bar""" - self.main.statusBar().showMessage(message, timeout) - - def starting_long_process(self, message): - """ - Showing message in main window's status bar - and changing mouse cursor to Qt.WaitCursor - """ - self.show_message(message) - QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - QApplication.processEvents() - - def ending_long_process(self, message=""): - """ - Clearing main window's status bar - and restoring mouse cursor - """ - QApplication.restoreOverrideCursor() - self.show_message(message, timeout=2000) - QApplication.processEvents() - - def set_default_color_scheme(self, name='Spyder'): - """Set default color scheme (only once)""" - color_scheme_name = self.get_option('color_scheme_name', None) - if color_scheme_name is None: - names = CONF.get("color_schemes", "names") - if name not in names: - name = names[0] - self.set_option('color_scheme_name', name) - - def create_toggle_view_action(self): - """Associate a toggle view action with each plugin""" - title = self.get_plugin_title() - if self.CONF_SECTION == 'editor': - title = _('Editor') - if self.shortcut is not None: - action = create_action(self, title, toggled=self.toggle_view, - shortcut=QKeySequence(self.shortcut)) - action.setShortcutContext(Qt.WidgetWithChildrenShortcut) - else: - action = create_action(self, title, toggled=self.toggle_view) - self.toggle_view_action = action - - def toggle_view(self, checked): - """Toggle view""" - if checked: - self.dockwidget.show() - self.dockwidget.raise_() - else: - self.dockwidget.hide() - - -class SpyderPluginWidget(QWidget, SpyderPluginMixin): - """ - Spyder base widget class - Spyder's widgets either inherit this class or reimplement its interface - """ - sig_option_changed = Signal(str, object) - - def __init__(self, parent): - QWidget.__init__(self, parent) - SpyderPluginMixin.__init__(self, parent) - - def get_plugin_title(self): - """ - Return plugin title - Note: after some thinking, it appears that using a method - is more flexible here than using a class attribute - """ - raise NotImplementedError - - def get_plugin_icon(self): - """ - Return plugin icon (QIcon instance) - Note: this is required for plugins creating a main window - (see SpyderPluginMixin.create_mainwindow) - and for configuration dialog widgets creation - """ - return get_icon('qt.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - pass - - def closing_plugin(self, cancelable=False): - """ - Perform actions before parent main window is closed - Return True or False whether the plugin may be closed immediately or not - Note: returned value is ignored if *cancelable* is False - """ - raise NotImplementedError - - def refresh_plugin(self): - """Refresh widget""" - raise NotImplementedError - - def get_plugin_actions(self): - """ - Return a list of actions related to plugin - Note: these actions will be enabled when plugin's dockwidget is visible - and they will be disabled when it's hidden - """ - raise NotImplementedError - - def register_plugin(self): - """Register plugin in Spyder's main window""" - raise NotImplementedError diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/inspector.py spyder-3.0.2+dfsg1/spyderlib/plugins/inspector.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/inspector.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/inspector.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1005 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Object Inspector Plugin""" - -from spyderlib.qt.QtGui import (QHBoxLayout, QVBoxLayout, QLabel, QSizePolicy, - QMenu, QToolButton, QGroupBox, QFontComboBox, - QActionGroup, QFontDialog, QWidget, QComboBox, - QLineEdit, QMessageBox) -from spyderlib.qt.QtCore import SIGNAL, QUrl, QThread -from spyderlib.qt.QtWebKit import QWebPage - -import re -import os.path as osp -import socket -import sys - -# Local imports -from spyderlib import dependencies -from spyderlib.baseconfig import get_conf_path, get_module_source_path, _ -from spyderlib.ipythonconfig import IPYTHON_QT_INSTALLED -from spyderlib.config import CONF -from spyderlib.guiconfig import get_color_scheme, get_font, set_font -from spyderlib.utils import programs -from spyderlib.utils.qthelpers import (get_icon, create_toolbutton, - add_actions, create_action) -from spyderlib.widgets.comboboxes import EditableComboBox -from spyderlib.widgets.sourcecode import codeeditor -from spyderlib.widgets.findreplace import FindReplace -from spyderlib.widgets.browser import WebView -from spyderlib.widgets.externalshell.pythonshell import ExtPythonShellWidget -from spyderlib.plugins import SpyderPluginWidget, PluginConfigPage -from spyderlib.py3compat import to_text_string, get_meth_class_inst - -#XXX: Hardcoded dependency on optional IPython plugin component -# that requires the hack to make this work without IPython -if IPYTHON_QT_INSTALLED: - from spyderlib.widgets.ipython import IPythonControlWidget -else: - IPythonControlWidget = None # analysis:ignore - -# Check if we can import Sphinx to activate rich text mode -try: - from spyderlib.utils.inspector.sphinxify import (CSS_PATH, sphinxify, - warning, generate_context, - usage) - sphinx_version = programs.get_module_version('sphinx') -except (ImportError, TypeError): - sphinxify = sphinx_version = None # analysis:ignore - -# To add sphinx dependency to the Dependencies dialog -SPHINX_REQVER = '>=0.6.6' -dependencies.add("sphinx", _("Rich text help on the Object Inspector"), - required_version=SPHINX_REQVER) - - -class ObjectComboBox(EditableComboBox): - """ - QComboBox handling object names - """ - def __init__(self, parent): - EditableComboBox.__init__(self, parent) - self.object_inspector = parent - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - self.tips = {True: '', False: ''} - - def is_valid(self, qstr=None): - """Return True if string is valid""" - if not self.object_inspector.source_is_console(): - return True - if qstr is None: - qstr = self.currentText() - if not re.search('^[a-zA-Z0-9_\.]*$', str(qstr), 0): - return False - objtxt = to_text_string(qstr) - if self.object_inspector.get_option('automatic_import'): - shell = self.object_inspector.internal_shell - if shell is not None: - return shell.is_defined(objtxt, force_import=True) - shell = self.object_inspector.get_shell() - if shell is not None: - try: - return shell.is_defined(objtxt) - except socket.error: - shell = self.object_inspector.get_shell() - try: - return shell.is_defined(objtxt) - except socket.error: - # Well... too bad! - pass - - def validate_current_text(self): - self.validate(self.currentText()) - - def validate(self, qstr, editing=True): - """Reimplemented to avoid formatting actions""" - valid = self.is_valid(qstr) - if self.hasFocus() and valid is not None: - if editing: - # Combo box text is being modified: invalidate the entry - self.show_tip(self.tips[valid]) - self.emit(SIGNAL('valid(bool)'), False) - else: - # A new item has just been selected - if valid: - self.selected() - else: - self.emit(SIGNAL('valid(bool)'), False) - else: - self.set_default_style() - - -class ObjectInspectorConfigPage(PluginConfigPage): - def setup_page(self): - # Fonts group - plain_text_font_group = self.create_fontgroup(option=None, - text=_("Plain text font style"), - fontfilters=QFontComboBox.MonospacedFonts) - rich_text_font_group = self.create_fontgroup(option='rich_text', - text=_("Rich text font style")) - - # Connections group - connections_group = QGroupBox(_("Automatic connections")) - connections_label = QLabel(_("The Object Inspector can automatically " - "show an object's help information after " - "a left parenthesis is written next to it. " - "Below you can decide to which plugin " - "you want to connect it to turn on this " - "feature.")) - connections_label.setWordWrap(True) - editor_box = self.create_checkbox(_("Editor"), 'connect/editor') - rope_installed = programs.is_module_installed('rope') - jedi_installed = programs.is_module_installed('jedi', '>=0.8.1') - editor_box.setEnabled(rope_installed or jedi_installed) - if not rope_installed and not jedi_installed: - editor_tip = _("This feature requires the Rope or Jedi libraries.\n" - "It seems you don't have either installed.") - editor_box.setToolTip(editor_tip) - python_box = self.create_checkbox(_("Python Console"), - 'connect/python_console') - ipython_box = self.create_checkbox(_("IPython Console"), - 'connect/ipython_console') - ipython_box.setEnabled(IPYTHON_QT_INSTALLED) - - connections_layout = QVBoxLayout() - connections_layout.addWidget(connections_label) - connections_layout.addWidget(editor_box) - connections_layout.addWidget(python_box) - connections_layout.addWidget(ipython_box) - connections_group.setLayout(connections_layout) - - # Features group - features_group = QGroupBox(_("Additional features")) - math_box = self.create_checkbox(_("Render mathematical equations"), - 'math') - req_sphinx = sphinx_version is not None and \ - programs.is_module_installed('sphinx', '>=1.1') - math_box.setEnabled(req_sphinx) - if not req_sphinx: - sphinx_tip = _("This feature requires Sphinx 1.1 or superior.") - if sphinx_version is not None: - sphinx_tip += "\n" + _("Sphinx %s is currently installed." - ) % sphinx_version - math_box.setToolTip(sphinx_tip) - - features_layout = QVBoxLayout() - features_layout.addWidget(math_box) - features_group.setLayout(features_layout) - - # Source code group - sourcecode_group = QGroupBox(_("Source code")) - wrap_mode_box = self.create_checkbox(_("Wrap lines"), 'wrap') - names = CONF.get('color_schemes', 'names') - choices = list(zip(names, names)) - cs_combo = self.create_combobox(_("Syntax color scheme: "), - choices, 'color_scheme_name') - - sourcecode_layout = QVBoxLayout() - sourcecode_layout.addWidget(wrap_mode_box) - sourcecode_layout.addWidget(cs_combo) - sourcecode_group.setLayout(sourcecode_layout) - - # Final layout - vlayout = QVBoxLayout() - vlayout.addWidget(rich_text_font_group) - vlayout.addWidget(plain_text_font_group) - vlayout.addWidget(connections_group) - vlayout.addWidget(features_group) - vlayout.addWidget(sourcecode_group) - vlayout.addStretch(1) - self.setLayout(vlayout) - - -class RichText(QWidget): - """ - WebView widget with find dialog - """ - def __init__(self, parent): - QWidget.__init__(self, parent) - - self.webview = WebView(self) - self.find_widget = FindReplace(self) - self.find_widget.set_editor(self.webview) - self.find_widget.hide() - - layout = QVBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(self.webview) - layout.addWidget(self.find_widget) - self.setLayout(layout) - - def set_font(self, font, fixed_font=None): - """Set font""" - self.webview.set_font(font, fixed_font=fixed_font) - - def set_html(self, html_text, base_url): - """Set html text""" - self.webview.setHtml(html_text, base_url) - - def clear(self): - self.set_html('', self.webview.url()) - - -class PlainText(QWidget): - """ - Read-only editor widget with find dialog - """ - def __init__(self, parent): - QWidget.__init__(self, parent) - self.editor = None - - # Read-only editor - self.editor = codeeditor.CodeEditor(self) - self.editor.setup_editor(linenumbers=False, language='py', - scrollflagarea=False, edge_line=False) - self.connect(self.editor, SIGNAL("focus_changed()"), - lambda: self.emit(SIGNAL("focus_changed()"))) - self.editor.setReadOnly(True) - - # Find/replace widget - self.find_widget = FindReplace(self) - self.find_widget.set_editor(self.editor) - self.find_widget.hide() - - layout = QVBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(self.editor) - layout.addWidget(self.find_widget) - self.setLayout(layout) - - def set_font(self, font, color_scheme=None): - """Set font""" - self.editor.set_font(font, color_scheme=color_scheme) - - def set_color_scheme(self, color_scheme): - """Set color scheme""" - self.editor.set_color_scheme(color_scheme) - - def set_text(self, text, is_code): - self.editor.set_highlight_current_line(is_code) - self.editor.set_occurence_highlighting(is_code) - if is_code: - self.editor.set_language('py') - else: - self.editor.set_language(None) - self.editor.set_text(text) - self.editor.set_cursor_position('sof') - - def clear(self): - self.editor.clear() - - -class SphinxThread(QThread): - """ - A worker thread for handling rich text rendering. - - Parameters - ---------- - doc : str or dict - A string containing a raw rst text or a dict containing - the doc string components to be rendered. - See spyderlib.utils.dochelpers.getdoc for description. - context : dict - A dict containing the substitution variables for the - layout template - html_text_no_doc : unicode - Text to be rendered if doc string cannot be extracted. - math_option : bool - Use LaTeX math rendering. - - """ - def __init__(self, html_text_no_doc=''): - super(SphinxThread, self).__init__() - self.doc = None - self.context = None - self.html_text_no_doc = html_text_no_doc - self.math_option = False - - def render(self, doc, context=None, math_option=False): - """Start thread to render a given documentation""" - # If the thread is already running wait for it to finish before - # starting it again. - if self.wait(): - self.doc = doc - self.context = context - self.math_option = math_option - # This causes run() to be executed in separate thread - self.start() - - def run(self): - html_text = self.html_text_no_doc - doc = self.doc - if doc is not None: - if type(doc) is dict and 'docstring' in doc.keys(): - try: - context = generate_context(name=doc['name'], - argspec=doc['argspec'], - note=doc['note'], - math=self.math_option) - html_text = sphinxify(doc['docstring'], context) - if doc['docstring'] == '': - html_text += '
    ' - html_text += self.html_text_no_doc - - except Exception as error: - self.emit(SIGNAL('error_msg(QString)'), - to_text_string(error)) - return - elif self.context is not None: - try: - html_text = sphinxify(doc, self.context) - except Exception as error: - self.emit(SIGNAL('error_msg(QString)'), - to_text_string(error)) - return - self.emit(SIGNAL('html_ready(QString)'), html_text) - - -class ObjectInspector(SpyderPluginWidget): - """ - Docstrings viewer widget - """ - CONF_SECTION = 'inspector' - CONFIGWIDGET_CLASS = ObjectInspectorConfigPage - LOG_PATH = get_conf_path(CONF_SECTION) - def __init__(self, parent): - SpyderPluginWidget.__init__(self, parent) - - self.internal_shell = None - - # Initialize plugin - self.initialize_plugin() - - self.no_doc_string = _("No further documentation available") - - self._last_console_cb = None - self._last_editor_cb = None - - self.set_default_color_scheme() - - self.plain_text = PlainText(self) - self.rich_text = RichText(self) - - color_scheme = get_color_scheme(self.get_option('color_scheme_name')) - self.set_plain_text_font(self.get_plugin_font(), color_scheme) - self.plain_text.editor.toggle_wrap_mode(self.get_option('wrap')) - - # Add entries to read-only editor context-menu - font_action = create_action(self, _("&Font..."), None, - 'font.png', _("Set font style"), - triggered=self.change_font) - self.wrap_action = create_action(self, _("Wrap lines"), - toggled=self.toggle_wrap_mode) - self.wrap_action.setChecked(self.get_option('wrap')) - self.plain_text.editor.readonly_menu.addSeparator() - add_actions(self.plain_text.editor.readonly_menu, - (font_action, self.wrap_action)) - - self.set_rich_text_font(self.get_plugin_font('rich_text')) - - self.shell = None - - self.external_console = None - - # locked = disable link with Console - self.locked = False - self._last_texts = [None, None] - self._last_editor_doc = None - - # Object name - layout_edit = QHBoxLayout() - layout_edit.setContentsMargins(0, 0, 0, 0) - txt = _("Source") - if sys.platform == 'darwin': - source_label = QLabel(" " + txt) - else: - source_label = QLabel(txt) - layout_edit.addWidget(source_label) - self.source_combo = QComboBox(self) - self.source_combo.addItems([_("Console"), _("Editor")]) - self.connect(self.source_combo, SIGNAL('currentIndexChanged(int)'), - self.source_changed) - if (not programs.is_module_installed('rope') and - not programs.is_module_installed('jedi', '>=0.8.1')): - self.source_combo.hide() - source_label.hide() - layout_edit.addWidget(self.source_combo) - layout_edit.addSpacing(10) - layout_edit.addWidget(QLabel(_("Object"))) - self.combo = ObjectComboBox(self) - layout_edit.addWidget(self.combo) - self.object_edit = QLineEdit(self) - self.object_edit.setReadOnly(True) - layout_edit.addWidget(self.object_edit) - self.combo.setMaxCount(self.get_option('max_history_entries')) - self.combo.addItems( self.load_history() ) - self.combo.setItemText(0, '') - self.connect(self.combo, SIGNAL("valid(bool)"), - lambda valid: self.force_refresh()) - - # Plain text docstring option - self.docstring = True - self.rich_help = sphinxify is not None \ - and self.get_option('rich_mode', True) - self.plain_text_action = create_action(self, _("Plain Text"), - toggled=self.toggle_plain_text) - - # Source code option - self.show_source_action = create_action(self, _("Show Source"), - toggled=self.toggle_show_source) - - # Rich text option - self.rich_text_action = create_action(self, _("Rich Text"), - toggled=self.toggle_rich_text) - - # Add the help actions to an exclusive QActionGroup - help_actions = QActionGroup(self) - help_actions.setExclusive(True) - help_actions.addAction(self.plain_text_action) - help_actions.addAction(self.rich_text_action) - - # Automatic import option - self.auto_import_action = create_action(self, _("Automatic import"), - toggled=self.toggle_auto_import) - auto_import_state = self.get_option('automatic_import') - self.auto_import_action.setChecked(auto_import_state) - - # Lock checkbox - self.locked_button = create_toolbutton(self, - triggered=self.toggle_locked) - layout_edit.addWidget(self.locked_button) - self._update_lock_icon() - - # Option menu - options_button = create_toolbutton(self, text=_("Options"), - icon=get_icon('tooloptions.png')) - options_button.setPopupMode(QToolButton.InstantPopup) - menu = QMenu(self) - add_actions(menu, [self.rich_text_action, self.plain_text_action, - self.show_source_action, None, - self.auto_import_action]) - options_button.setMenu(menu) - layout_edit.addWidget(options_button) - - if self.rich_help: - self.switch_to_rich_text() - else: - self.switch_to_plain_text() - self.plain_text_action.setChecked(not self.rich_help) - self.rich_text_action.setChecked(self.rich_help) - self.rich_text_action.setEnabled(sphinxify is not None) - self.source_changed() - - # Main layout - layout = QVBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - layout.addLayout(layout_edit) - layout.addWidget(self.plain_text) - layout.addWidget(self.rich_text) - self.setLayout(layout) - - # Add worker thread for handling rich text rendering - if sphinxify is None: - self._sphinx_thread = None - else: - self._sphinx_thread = SphinxThread( - html_text_no_doc=warning(self.no_doc_string)) - self.connect(self._sphinx_thread, SIGNAL('html_ready(QString)'), - self._on_sphinx_thread_html_ready) - self.connect(self._sphinx_thread, SIGNAL('error_msg(QString)'), - self._on_sphinx_thread_error_msg) - - # Render internal links - view = self.rich_text.webview - view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) - view.linkClicked.connect(self.handle_link_clicks) - - self._starting_up = True - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _('Object inspector') - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('inspector.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - self.combo.lineEdit().selectAll() - return self.combo - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - return [] - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self, SIGNAL('focus_changed()'), - self.main.plugin_focus_changed) - self.main.add_dockwidget(self) - self.main.console.set_inspector(self) - self.internal_shell = self.main.console.shell - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - return True - - def refresh_plugin(self): - """Refresh widget""" - if self._starting_up: - self._starting_up = False - if sphinxify is not None: - self.switch_to_rich_text() - self.show_intro_message() - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - color_scheme_n = 'color_scheme_name' - color_scheme_o = get_color_scheme(self.get_option(color_scheme_n)) - font_n = 'plugin_font' - font_o = self.get_plugin_font() - connect_n = 'connect_to_oi' - rich_font_n = 'rich_text' - rich_font_o = self.get_plugin_font('rich_text') - wrap_n = 'wrap' - wrap_o = self.get_option(wrap_n) - self.wrap_action.setChecked(wrap_o) - math_n = 'math' - math_o = self.get_option(math_n) - - if font_n in options: - scs = color_scheme_o if color_scheme_n in options else None - self.set_plain_text_font(font_o, color_scheme=scs) - if rich_font_n in options: - self.set_rich_text_font(rich_font_o) - elif color_scheme_n in options: - self.set_plain_text_color_scheme(color_scheme_o) - if wrap_n in options: - self.toggle_wrap_mode(wrap_o) - if math_n in options: - self.toggle_math_mode(math_o) - - # To make auto-connection changes take place instantly - self.main.editor.apply_plugin_settings(options=[connect_n]) - self.main.extconsole.apply_plugin_settings(options=[connect_n]) - if self.main.ipyconsole is not None: - self.main.ipyconsole.apply_plugin_settings(options=[connect_n]) - - #------ Public API (related to inspector's source) ------------------------- - def source_is_console(self): - """Return True if source is Console""" - return self.source_combo.currentIndex() == 0 - - def switch_to_editor_source(self): - self.source_combo.setCurrentIndex(1) - - def switch_to_console_source(self): - self.source_combo.setCurrentIndex(0) - - def source_changed(self, index=None): - if self.source_is_console(): - # Console - self.combo.show() - self.object_edit.hide() - self.show_source_action.setEnabled(True) - self.auto_import_action.setEnabled(True) - else: - # Editor - self.combo.hide() - self.object_edit.show() - self.show_source_action.setDisabled(True) - self.auto_import_action.setDisabled(True) - self.restore_text() - - def save_text(self, callback): - if self.source_is_console(): - self._last_console_cb = callback - else: - self._last_editor_cb = callback - - def restore_text(self): - if self.source_is_console(): - cb = self._last_console_cb - else: - cb = self._last_editor_cb - if cb is None: - if self.is_plain_text_mode(): - self.plain_text.clear() - else: - self.rich_text.clear() - else: - func = cb[0] - args = cb[1:] - func(*args) - if get_meth_class_inst(func) is self.rich_text: - self.switch_to_rich_text() - else: - self.switch_to_plain_text() - - #------ Public API (related to rich/plain text widgets) -------------------- - @property - def find_widget(self): - if self.plain_text.isVisible(): - return self.plain_text.find_widget - else: - return self.rich_text.find_widget - - def set_rich_text_font(self, font): - """Set rich text mode font""" - self.rich_text.set_font(font, fixed_font=self.get_plugin_font()) - - def set_plain_text_font(self, font, color_scheme=None): - """Set plain text mode font""" - self.plain_text.set_font(font, color_scheme=color_scheme) - - def set_plain_text_color_scheme(self, color_scheme): - """Set plain text mode color scheme""" - self.plain_text.set_color_scheme(color_scheme) - - def change_font(self): - """Change console font""" - font, valid = QFontDialog.getFont(get_font(self.CONF_SECTION), self, - _("Select a new font")) - if valid: - self.set_plain_text_font(font) - set_font(font, self.CONF_SECTION) - - def toggle_wrap_mode(self, checked): - """Toggle wrap mode""" - self.plain_text.editor.toggle_wrap_mode(checked) - self.set_option('wrap', checked) - - def toggle_math_mode(self, checked): - """Toggle math mode""" - self.set_option('math', checked) - - def is_plain_text_mode(self): - """Return True if plain text mode is active""" - return self.plain_text.isVisible() - - def is_rich_text_mode(self): - """Return True if rich text mode is active""" - return self.rich_text.isVisible() - - def switch_to_plain_text(self): - """Switch to plain text mode""" - self.rich_help = False - self.plain_text.show() - self.rich_text.hide() - self.plain_text_action.setChecked(True) - - def switch_to_rich_text(self): - """Switch to rich text mode""" - self.rich_help = True - self.plain_text.hide() - self.rich_text.show() - self.rich_text_action.setChecked(True) - self.show_source_action.setChecked(False) - - def set_plain_text(self, text, is_code): - """Set plain text docs""" - - # text is coming from utils.dochelpers.getdoc - if type(text) is dict: - name = text['name'] - if name: - rst_title = ''.join(['='*len(name), '\n', name, '\n', - '='*len(name), '\n\n']) - else: - rst_title = '' - - if text['argspec']: - definition = ''.join(['Definition: ', name, text['argspec'], - '\n']) - else: - definition = '' - - if text['note']: - note = ''.join(['Type: ', text['note'], '\n\n----\n\n']) - else: - note = '' - - full_text = ''.join([rst_title, definition, note, - text['docstring']]) - else: - full_text = text - - self.plain_text.set_text(full_text, is_code) - self.save_text([self.plain_text.set_text, full_text, is_code]) - - def set_rich_text_html(self, html_text, base_url): - """Set rich text""" - self.rich_text.set_html(html_text, base_url) - self.save_text([self.rich_text.set_html, html_text, base_url]) - - def show_intro_message(self): - intro_message = _("Here you can get help of any object by pressing " - "%s in front of it, either on the Editor or the " - "Console.%s" - "Help can also be shown automatically after writing " - "a left parenthesis next to an object. You can " - "activate this behavior in %s.") - prefs = _("Preferences > Object Inspector") - if self.is_rich_text_mode(): - title = _("Usage") - tutorial_message = _("New to Spyder? Read our") - tutorial = _("tutorial") - intro_message = intro_message % ("Ctrl+I", "

    ", - ""+prefs+"") - self.set_rich_text_html(usage(title, intro_message, - tutorial_message, tutorial), - QUrl.fromLocalFile(CSS_PATH)) - else: - install_sphinx = "\n\n%s" % _("Please consider installing Sphinx " - "to get documentation rendered in " - "rich text.") - intro_message = intro_message % ("Ctrl+I", "\n\n", prefs) - intro_message += install_sphinx - self.set_plain_text(intro_message, is_code=False) - - def show_rich_text(self, text, collapse=False, img_path=''): - """Show text in rich mode""" - self.visibility_changed(True) - self.raise_() - self.switch_to_rich_text() - context = generate_context(collapse=collapse, img_path=img_path) - self.render_sphinx_doc(text, context) - - def show_plain_text(self, text): - """Show text in plain mode""" - self.visibility_changed(True) - self.raise_() - self.switch_to_plain_text() - self.set_plain_text(text, is_code=False) - - def show_tutorial(self): - tutorial_path = get_module_source_path('spyderlib.utils.inspector') - img_path = osp.join(tutorial_path, 'static', 'images') - tutorial = osp.join(tutorial_path, 'tutorial.rst') - text = open(tutorial).read() - if sphinxify is not None: - self.show_rich_text(text, collapse=True, img_path=img_path) - else: - self.show_plain_text(text) - - def handle_link_clicks(self, url): - url = to_text_string(url.toString()) - if url == "spy://tutorial": - self.show_tutorial() - elif url.startswith('http'): - programs.start_file(url) - else: - self.rich_text.webview.load(QUrl(url)) - - #------ Public API --------------------------------------------------------- - def set_external_console(self, external_console): - self.external_console = external_console - - def force_refresh(self): - if self.source_is_console(): - self.set_object_text(None, force_refresh=True) - elif self._last_editor_doc is not None: - self.set_editor_doc(self._last_editor_doc, force_refresh=True) - - def set_object_text(self, text, force_refresh=False, ignore_unknown=False): - """Set object analyzed by Object Inspector""" - if (self.locked and not force_refresh): - return - self.switch_to_console_source() - - add_to_combo = True - if text is None: - text = to_text_string(self.combo.currentText()) - add_to_combo = False - - found = self.show_help(text, ignore_unknown=ignore_unknown) - if ignore_unknown and not found: - return - - if add_to_combo: - self.combo.add_text(text) - if found: - self.save_history() - - if self.dockwidget is not None: - self.dockwidget.blockSignals(True) - self.__eventually_raise_inspector(text, force=force_refresh) - if self.dockwidget is not None: - self.dockwidget.blockSignals(False) - - def set_editor_doc(self, doc, force_refresh=False): - """ - Use the object inspector to show docstring dictionary computed - with introspection plugin from the Editor plugin - """ - if (self.locked and not force_refresh): - return - self.switch_to_editor_source() - self._last_editor_doc = doc - self.object_edit.setText(doc['obj_text']) - - if self.rich_help: - self.render_sphinx_doc(doc) - else: - self.set_plain_text(doc, is_code=False) - - if self.dockwidget is not None: - self.dockwidget.blockSignals(True) - self.__eventually_raise_inspector(doc['docstring'], - force=force_refresh) - if self.dockwidget is not None: - self.dockwidget.blockSignals(False) - - def __eventually_raise_inspector(self, text, force=False): - index = self.source_combo.currentIndex() - if hasattr(self.main, 'tabifiedDockWidgets'): - # 'QMainWindow.tabifiedDockWidgets' was introduced in PyQt 4.5 - if self.dockwidget and (force or self.dockwidget.isVisible()) \ - and not self.ismaximized \ - and (force or text != self._last_texts[index]): - dockwidgets = self.main.tabifiedDockWidgets(self.dockwidget) - if self.main.console.dockwidget not in dockwidgets and \ - (hasattr(self.main, 'extconsole') and \ - self.main.extconsole.dockwidget not in dockwidgets): - self.dockwidget.show() - self.dockwidget.raise_() - self._last_texts[index] = text - - def load_history(self, obj=None): - """Load history from a text file in user home directory""" - if osp.isfile(self.LOG_PATH): - history = [line.replace('\n', '') - for line in open(self.LOG_PATH, 'r').readlines()] - else: - history = [] - return history - - def save_history(self): - """Save history to a text file in user home directory""" - open(self.LOG_PATH, 'w').write("\n".join( \ - [to_text_string(self.combo.itemText(index)) - for index in range(self.combo.count())] )) - - def toggle_plain_text(self, checked): - """Toggle plain text docstring""" - if checked: - self.docstring = checked - self.switch_to_plain_text() - self.force_refresh() - self.set_option('rich_mode', not checked) - - def toggle_show_source(self, checked): - """Toggle show source code""" - if checked: - self.switch_to_plain_text() - self.docstring = not checked - self.force_refresh() - self.set_option('rich_mode', not checked) - - def toggle_rich_text(self, checked): - """Toggle between sphinxified docstrings or plain ones""" - if checked: - self.docstring = not checked - self.switch_to_rich_text() - self.set_option('rich_mode', checked) - - def toggle_auto_import(self, checked): - """Toggle automatic import feature""" - self.combo.validate_current_text() - self.set_option('automatic_import', checked) - self.force_refresh() - - def toggle_locked(self): - """ - Toggle locked state - locked = disable link with Console - """ - self.locked = not self.locked - self._update_lock_icon() - - def _update_lock_icon(self): - """Update locked state icon""" - icon = get_icon("lock.png" if self.locked else "lock_open.png") - self.locked_button.setIcon(icon) - tip = _("Unlock") if self.locked else _("Lock") - self.locked_button.setToolTip(tip) - - def set_shell(self, shell): - """Bind to shell""" - if IPythonControlWidget is not None: - # XXX(anatoli): hack to make Spyder run on systems without IPython - # there should be a better way - if isinstance(shell, IPythonControlWidget): - # XXX: this ignores passed argument completely - self.shell = self.external_console.get_current_shell() - else: - self.shell = shell - - def get_shell(self): - """Return shell which is currently bound to object inspector, - or another running shell if it has been terminated""" - if not isinstance(self.shell, ExtPythonShellWidget) \ - or not self.shell.externalshell.is_running(): - self.shell = None - if self.external_console is not None: - self.shell = self.external_console.get_running_python_shell() - if self.shell is None: - self.shell = self.internal_shell - return self.shell - - def render_sphinx_doc(self, doc, context=None): - """Transform doc string dictionary to HTML and show it""" - # Math rendering option could have changed - self._sphinx_thread.render(doc, context, self.get_option('math')) - - def _on_sphinx_thread_html_ready(self, html_text): - """Set our sphinx documentation based on thread result""" - self._sphinx_thread.wait() - self.set_rich_text_html(html_text, QUrl.fromLocalFile(CSS_PATH)) - - def _on_sphinx_thread_error_msg(self, error_msg): - """ Display error message on Sphinx rich text failure""" - self._sphinx_thread.wait() - self.plain_text_action.setChecked(True) - QMessageBox.critical(self, - _('Object inspector'), - _("The following error occured when calling " - "Sphinx %s.
    Incompatible Sphinx " - "version or doc string decoding failed." - "

    Error message:
    %s" - ) % (sphinx_version, error_msg)) - - def show_help(self, obj_text, ignore_unknown=False): - """Show help""" - shell = self.get_shell() - if shell is None: - return - obj_text = to_text_string(obj_text) - - if not shell.is_defined(obj_text): - if self.get_option('automatic_import') and\ - self.internal_shell.is_defined(obj_text, force_import=True): - shell = self.internal_shell - else: - shell = None - doc = None - source_text = None - - if shell is not None: - doc = shell.get_doc(obj_text) - source_text = shell.get_source(obj_text) - - is_code = False - - if self.rich_help: - self.render_sphinx_doc(doc) - return doc is not None - elif self.docstring: - hlp_text = doc - if hlp_text is None: - hlp_text = source_text - if hlp_text is None: - hlp_text = self.no_doc_string - if ignore_unknown: - return False - else: - hlp_text = source_text - if hlp_text is None: - hlp_text = doc - if hlp_text is None: - hlp_text = _("No source code available.") - if ignore_unknown: - return False - else: - is_code = True - self.set_plain_text(hlp_text, is_code=is_code) - return True diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/ipythonconsole.py spyder-3.0.2+dfsg1/spyderlib/plugins/ipythonconsole.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/ipythonconsole.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/ipythonconsole.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1228 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""IPython Console plugin - -Handles IPython clients (and in the future, will handle IPython kernels too --- meanwhile, the external console plugin is handling them)""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -# Stdlib imports -import atexit -import os -import os.path as osp -import sys - -# Qt imports -from spyderlib.qt.QtGui import (QVBoxLayout, QHBoxLayout, QFormLayout, - QMessageBox, QGroupBox, QDialogButtonBox, - QDialog, QTabWidget, QFontComboBox, - QCheckBox, QApplication, QLabel,QLineEdit, - QPushButton, QKeySequence, QWidget) -from spyderlib.qt.compat import getopenfilename -from spyderlib.qt.QtCore import SIGNAL, Qt - -# IPython imports -from IPython.core.application import get_ipython_dir -from IPython.kernel.connect import find_connection_file -from IPython.qt.manager import QtKernelManager -try: # IPython = "<=2.0" - from IPython.external.ssh import tunnel as zmqtunnel - import IPython.external.pexpect as pexpect -except ImportError: - from zmq.ssh import tunnel as zmqtunnel # analysis:ignore - try: - import pexpect # analysis:ignore - except ImportError: - pexpect = None # analysis:ignore - -# Local imports -from spyderlib import dependencies -from spyderlib.baseconfig import _ -from spyderlib.config import CONF -from spyderlib.utils.misc import get_error_match, remove_backslashes -from spyderlib.utils import programs -from spyderlib.utils.qthelpers import get_icon, create_action -from spyderlib.widgets.tabs import Tabs -from spyderlib.widgets.ipython import IPythonClient -from spyderlib.widgets.findreplace import FindReplace -from spyderlib.plugins import SpyderPluginWidget, PluginConfigPage -from spyderlib.py3compat import to_text_string - - -SYMPY_REQVER = '>=0.7.3' -dependencies.add("sympy", _("Symbolic mathematics in the IPython Console"), - required_version=SYMPY_REQVER) - - -# Replacing pyzmq openssh_tunnel method to work around the issue -# https://github.com/zeromq/pyzmq/issues/589 which was solved in pyzmq -# https://github.com/zeromq/pyzmq/pull/615 -def _stop_tunnel(cmd): - pexpect.run(cmd) - -def openssh_tunnel(self, lport, rport, server, remoteip='127.0.0.1', - keyfile=None, password=None, timeout=0.4): - if pexpect is None: - raise ImportError("pexpect unavailable, use paramiko_tunnel") - ssh="ssh " - if keyfile: - ssh += "-i " + keyfile - - if ':' in server: - server, port = server.split(':') - ssh += " -p %s" % port - - cmd = "%s -O check %s" % (ssh, server) - (output, exitstatus) = pexpect.run(cmd, withexitstatus=True) - if not exitstatus: - pid = int(output[output.find("(pid=")+5:output.find(")")]) - cmd = "%s -O forward -L 127.0.0.1:%i:%s:%i %s" % ( - ssh, lport, remoteip, rport, server) - (output, exitstatus) = pexpect.run(cmd, withexitstatus=True) - if not exitstatus: - atexit.register(_stop_tunnel, cmd.replace("-O forward", - "-O cancel", - 1)) - return pid - cmd = "%s -f -S none -L 127.0.0.1:%i:%s:%i %s sleep %i" % ( - ssh, lport, remoteip, rport, server, timeout) - - # pop SSH_ASKPASS from env - env = os.environ.copy() - env.pop('SSH_ASKPASS', None) - - ssh_newkey = 'Are you sure you want to continue connecting' - tunnel = pexpect.spawn(cmd, env=env) - failed = False - while True: - try: - i = tunnel.expect([ssh_newkey, '[Pp]assword:'], timeout=.1) - if i==0: - host = server.split('@')[-1] - question = _("The authenticity of host %s can't be " - "established. Are you sure you want to continue " - "connecting?") % host - reply = QMessageBox.question(self, _('Warning'), question, - QMessageBox.Yes | QMessageBox.No, - QMessageBox.No) - if reply == QMessageBox.Yes: - tunnel.sendline('yes') - continue - else: - tunnel.sendline('no') - raise RuntimeError( - _("The authenticity of the host can't be established")) - if i==1 and password is not None: - tunnel.sendline(password) - except pexpect.TIMEOUT: - continue - except pexpect.EOF: - if tunnel.exitstatus: - raise RuntimeError(_("Tunnel '%s' failed to start") % cmd) - else: - return tunnel.pid - else: - if failed or password is None: - raise RuntimeError(_("Could not connect to remote host")) - # TODO: Use this block when pyzmq bug #620 is fixed - # # Prompt a passphrase dialog to the user for a second attempt - # password, ok = QInputDialog.getText(self, _('Password'), - # _('Enter password for: ') + server, - # echo=QLineEdit.Password) - # if ok is False: - # raise RuntimeError('Could not connect to remote host.') - tunnel.sendline(password) - failed = True - - -class IPythonConsoleConfigPage(PluginConfigPage): - - def __init__(self, plugin, parent): - PluginConfigPage.__init__(self, plugin, parent) - self.get_name = lambda: _("IPython console") - - def setup_page(self): - newcb = self.create_checkbox - mpl_present = programs.is_module_installed("matplotlib") - - # --- Display --- - font_group = self.create_fontgroup(option=None, text=None, - fontfilters=QFontComboBox.MonospacedFonts) - - # Interface Group - interface_group = QGroupBox(_("Interface")) - banner_box = newcb(_("Display initial banner"), 'show_banner', - tip=_("This option lets you hide the message shown at\n" - "the top of the console when it's opened.")) - gui_comp_box = newcb(_("Use a completion widget"), - 'use_gui_completion', - tip=_("Use a widget instead of plain text " - "output for tab completion")) - pager_box = newcb(_("Use a pager to display additional text inside " - "the console"), 'use_pager', - tip=_("Useful if you don't want to fill the " - "console with long help or completion texts.\n" - "Note: Use the Q key to get out of the " - "pager.")) - calltips_box = newcb(_("Display balloon tips"), 'show_calltips') - ask_box = newcb(_("Ask for confirmation before closing"), - 'ask_before_closing') - - interface_layout = QVBoxLayout() - interface_layout.addWidget(banner_box) - interface_layout.addWidget(gui_comp_box) - interface_layout.addWidget(pager_box) - interface_layout.addWidget(calltips_box) - interface_layout.addWidget(ask_box) - interface_group.setLayout(interface_layout) - - # Background Color Group - bg_group = QGroupBox(_("Background color")) - light_radio = self.create_radiobutton(_("Light background"), - 'light_color') - dark_radio = self.create_radiobutton(_("Dark background"), - 'dark_color') - bg_layout = QVBoxLayout() - bg_layout.addWidget(light_radio) - bg_layout.addWidget(dark_radio) - bg_group.setLayout(bg_layout) - - # Source Code Group - source_code_group = QGroupBox(_("Source code")) - buffer_spin = self.create_spinbox( - _("Buffer: "), _(" lines"), - 'buffer_size', min_=-1, max_=1000000, step=100, - tip=_("Set the maximum number of lines of text shown in the\n" - "console before truncation. Specifying -1 disables it\n" - "(not recommended!)")) - source_code_layout = QVBoxLayout() - source_code_layout.addWidget(buffer_spin) - source_code_group.setLayout(source_code_layout) - - # --- Graphics --- - # Pylab Group - pylab_group = QGroupBox(_("Support for graphics (Matplotlib)")) - pylab_box = newcb(_("Activate support"), 'pylab') - autoload_pylab_box = newcb(_("Automatically load Pylab and NumPy " - "modules"), - 'pylab/autoload', - tip=_("This lets you load graphics support " - "without importing \nthe commands to do " - "plots. Useful to work with other\n" - "plotting libraries different to " - "Matplotlib or to develop \nGUIs with " - "Spyder.")) - autoload_pylab_box.setEnabled(self.get_option('pylab') and mpl_present) - self.connect(pylab_box, SIGNAL("toggled(bool)"), - autoload_pylab_box.setEnabled) - - pylab_layout = QVBoxLayout() - pylab_layout.addWidget(pylab_box) - pylab_layout.addWidget(autoload_pylab_box) - pylab_group.setLayout(pylab_layout) - - if not mpl_present: - self.set_option('pylab', False) - self.set_option('pylab/autoload', False) - pylab_group.setEnabled(False) - pylab_tip = _("This feature requires the Matplotlib library.\n" - "It seems you don't have it installed.") - pylab_box.setToolTip(pylab_tip) - - # Pylab backend Group - inline = _("Inline") - automatic = _("Automatic") - backend_group = QGroupBox(_("Graphics backend")) - bend_label = QLabel(_("Decide how graphics are going to be displayed " - "in the console. If unsure, please select " - "%s to put graphics inside the " - "console or %s to interact with " - "them (through zooming and panning) in a " - "separate window.") % (inline, automatic)) - bend_label.setWordWrap(True) - - backends = [(inline, 0), (automatic, 1), ("Qt", 2)] - # TODO: Add gtk3 when 0.13 is released - if sys.platform == 'darwin': - backends.append( ("Mac OSX", 3) ) - if programs.is_module_installed('pygtk'): - backends.append( ("Gtk", 4) ) - if programs.is_module_installed('wxPython'): - backends.append( ("Wx", 5) ) - if programs.is_module_installed('_tkinter'): - backends.append( ("Tkinter", 6) ) - backends = tuple(backends) - - backend_box = self.create_combobox( _("Backend:")+" ", backends, - 'pylab/backend', default=0, - tip=_("This option will be applied the " - "next time a console is opened.")) - - backend_layout = QVBoxLayout() - backend_layout.addWidget(bend_label) - backend_layout.addWidget(backend_box) - backend_group.setLayout(backend_layout) - backend_group.setEnabled(self.get_option('pylab') and mpl_present) - self.connect(pylab_box, SIGNAL("toggled(bool)"), - backend_group.setEnabled) - - # Inline backend Group - inline_group = QGroupBox(_("Inline backend")) - inline_label = QLabel(_("Decide how to render the figures created by " - "this backend")) - inline_label.setWordWrap(True) - formats = (("PNG", 0), ("SVG", 1)) - format_box = self.create_combobox(_("Format:")+" ", formats, - 'pylab/inline/figure_format', default=0) - resolution_spin = self.create_spinbox( - _("Resolution:")+" ", " "+_("dpi"), - 'pylab/inline/resolution', min_=50, max_=150, step=0.1, - tip=_("Only used when the format is PNG. Default is " - "72")) - width_spin = self.create_spinbox( - _("Width:")+" ", " "+_("inches"), - 'pylab/inline/width', min_=2, max_=20, step=1, - tip=_("Default is 6")) - height_spin = self.create_spinbox( - _("Height:")+" ", " "+_("inches"), - 'pylab/inline/height', min_=1, max_=20, step=1, - tip=_("Default is 4")) - - inline_layout = QVBoxLayout() - inline_layout.addWidget(inline_label) - inline_layout.addWidget(format_box) - inline_layout.addWidget(resolution_spin) - inline_layout.addWidget(width_spin) - inline_layout.addWidget(height_spin) - inline_group.setLayout(inline_layout) - inline_group.setEnabled(self.get_option('pylab') and mpl_present) - self.connect(pylab_box, SIGNAL("toggled(bool)"), - inline_group.setEnabled) - - # --- Startup --- - # Run lines Group - run_lines_group = QGroupBox(_("Run code")) - run_lines_label = QLabel(_("You can run several lines of code when " - "a console is started. Please introduce " - "each one separated by commas, for " - "example:
    " - "import os, import sys")) - run_lines_label.setWordWrap(True) - run_lines_edit = self.create_lineedit(_("Lines:"), 'startup/run_lines', - '', alignment=Qt.Horizontal) - - run_lines_layout = QVBoxLayout() - run_lines_layout.addWidget(run_lines_label) - run_lines_layout.addWidget(run_lines_edit) - run_lines_group.setLayout(run_lines_layout) - - # Run file Group - run_file_group = QGroupBox(_("Run a file")) - run_file_label = QLabel(_("You can also run a whole file at startup " - "instead of just some lines (This is " - "similar to have a PYTHONSTARTUP file).")) - run_file_label.setWordWrap(True) - file_radio = newcb(_("Use the following file:"), - 'startup/use_run_file', False) - run_file_browser = self.create_browsefile('', 'startup/run_file', '') - run_file_browser.setEnabled(False) - self.connect(file_radio, SIGNAL("toggled(bool)"), - run_file_browser.setEnabled) - - run_file_layout = QVBoxLayout() - run_file_layout.addWidget(run_file_label) - run_file_layout.addWidget(file_radio) - run_file_layout.addWidget(run_file_browser) - run_file_group.setLayout(run_file_layout) - - # ---- Advanced settings ---- - # Greedy completer group - greedy_group = QGroupBox(_("Greedy completion")) - greedy_label = QLabel(_("Enable Tab completion on elements " - "of lists, results of function calls, etc, " - "without assigning them to a " - "variable.
    " - "For example, you can get completions on " - "things like li[0].<Tab> or " - "ins.meth().<Tab>")) - greedy_label.setWordWrap(True) - greedy_box = newcb(_("Use the greedy completer"), "greedy_completer", - tip="Warning: It can be unsafe because the " - "code is actually evaluated when you press " - "Tab.") - - greedy_layout = QVBoxLayout() - greedy_layout.addWidget(greedy_label) - greedy_layout.addWidget(greedy_box) - greedy_group.setLayout(greedy_layout) - - # Autocall group - autocall_group = QGroupBox(_("Autocall")) - autocall_label = QLabel(_("Autocall makes IPython automatically call " - "any callable object even if you didn't type " - "explicit parentheses.
    " - "For example, if you type str 43 it " - "becomes str(43) automatically.")) - autocall_label.setWordWrap(True) - - smart = _('Smart') - full = _('Full') - autocall_opts = ((_('Off'), 0), (smart, 1), (full, 2)) - autocall_box = self.create_combobox( - _("Autocall: "), autocall_opts, 'autocall', default=0, - tip=_("On %s mode, Autocall is not applied if " - "there are no arguments after the callable. On " - "%s mode, all callable objects are " - "automatically called (even if no arguments are " - "present).") % (smart, full)) - - autocall_layout = QVBoxLayout() - autocall_layout.addWidget(autocall_label) - autocall_layout.addWidget(autocall_box) - autocall_group.setLayout(autocall_layout) - - # Sympy group - sympy_group = QGroupBox(_("Symbolic Mathematics")) - sympy_label = QLabel(_("Perfom symbolic operations in the console " - "(e.g. integrals, derivatives, vector calculus, " - "etc) and get the outputs in a beautifully " - "printed style.")) - sympy_label.setWordWrap(True) - sympy_box = newcb(_("Use symbolic math"), "symbolic_math", - tip=_("This option loads the Sympy library to work " - "with.
    Please refer to its documentation to " - "learn how to use it.")) - - sympy_layout = QVBoxLayout() - sympy_layout.addWidget(sympy_label) - sympy_layout.addWidget(sympy_box) - sympy_group.setLayout(sympy_layout) - - sympy_present = programs.is_module_installed("sympy") - if not sympy_present: - self.set_option("symbolic_math", False) - sympy_box.setEnabled(False) - sympy_tip = _("This feature requires the Sympy library.\n" - "It seems you don't have it installed.") - sympy_box.setToolTip(sympy_tip) - - # Prompts group - prompts_group = QGroupBox(_("Prompts")) - prompts_label = QLabel(_("Modify how Input and Output prompts are " - "shown in the console.")) - prompts_label.setWordWrap(True) - in_prompt_edit = self.create_lineedit(_("Input prompt:"), - 'in_prompt', '', - _('Default is
    ' - 'In [<span class="in-prompt-number">' - '%i</span>]:'), - alignment=Qt.Horizontal) - out_prompt_edit = self.create_lineedit(_("Output prompt:"), - 'out_prompt', '', - _('Default is
    ' - 'Out[<span class="out-prompt-number">' - '%i</span>]:'), - alignment=Qt.Horizontal) - - prompts_layout = QVBoxLayout() - prompts_layout.addWidget(prompts_label) - prompts_layout.addWidget(in_prompt_edit) - prompts_layout.addWidget(out_prompt_edit) - prompts_group.setLayout(prompts_layout) - - # --- Tabs organization --- - tabs = QTabWidget() - tabs.addTab(self.create_tab(font_group, interface_group, bg_group, - source_code_group), _("Display")) - tabs.addTab(self.create_tab(pylab_group, backend_group, inline_group), - _("Graphics")) - tabs.addTab(self.create_tab(run_lines_group, run_file_group), - _("Startup")) - tabs.addTab(self.create_tab(greedy_group, autocall_group, sympy_group, - prompts_group), _("Advanced Settings")) - - vlayout = QVBoxLayout() - vlayout.addWidget(tabs) - self.setLayout(vlayout) - - -class KernelConnectionDialog(QDialog): - """Dialog to connect to existing kernels (either local or remote)""" - - def __init__(self, parent=None): - super(KernelConnectionDialog, self).__init__(parent) - self.setWindowTitle(_('Connect to an existing kernel')) - - main_label = QLabel(_("Please enter the connection info of the kernel " - "you want to connect to. For that you can " - "either select its JSON connection file using " - "the Browse button, or write directly " - "its id, in case it's a local kernel (for " - "example kernel-3764.json or just " - "3764).")) - main_label.setWordWrap(True) - main_label.setAlignment(Qt.AlignJustify) - - # connection file - cf_label = QLabel(_('Connection info:')) - self.cf = QLineEdit() - self.cf.setPlaceholderText(_('Path to connection file or kernel id')) - self.cf.setMinimumWidth(250) - cf_open_btn = QPushButton(_('Browse')) - self.connect(cf_open_btn, SIGNAL('clicked()'), - self.select_connection_file) - - cf_layout = QHBoxLayout() - cf_layout.addWidget(cf_label) - cf_layout.addWidget(self.cf) - cf_layout.addWidget(cf_open_btn) - - # remote kernel checkbox - self.rm_cb = QCheckBox(_('This is a remote kernel')) - - # ssh connection - self.hn = QLineEdit() - self.hn.setPlaceholderText(_('username@hostname:port')) - - self.kf = QLineEdit() - self.kf.setPlaceholderText(_('Path to ssh key file')) - kf_open_btn = QPushButton(_('Browse')) - self.connect(kf_open_btn, SIGNAL('clicked()'), self.select_ssh_key) - - kf_layout = QHBoxLayout() - kf_layout.addWidget(self.kf) - kf_layout.addWidget(kf_open_btn) - - self.pw = QLineEdit() - self.pw.setPlaceholderText(_('Password or ssh key passphrase')) - self.pw.setEchoMode(QLineEdit.Password) - - ssh_form = QFormLayout() - ssh_form.addRow(_('Host name'), self.hn) - ssh_form.addRow(_('Ssh key'), kf_layout) - ssh_form.addRow(_('Password'), self.pw) - - # Ok and Cancel buttons - accept_btns = QDialogButtonBox( - QDialogButtonBox.Ok | QDialogButtonBox.Cancel, - Qt.Horizontal, self) - - self.connect(accept_btns, SIGNAL('accepted()'), self.accept) - self.connect(accept_btns, SIGNAL('rejected()'), self.reject) - - # Dialog layout - layout = QVBoxLayout(self) - layout.addWidget(main_label) - layout.addLayout(cf_layout) - layout.addWidget(self.rm_cb) - layout.addLayout(ssh_form) - layout.addWidget(accept_btns) - - # remote kernel checkbox enables the ssh_connection_form - def ssh_set_enabled(state): - for wid in [self.hn, self.kf, kf_open_btn, self.pw]: - wid.setEnabled(state) - for i in range(ssh_form.rowCount()): - ssh_form.itemAt(2 * i).widget().setEnabled(state) - - ssh_set_enabled(self.rm_cb.checkState()) - self.connect(self.rm_cb, SIGNAL('stateChanged(int)'), ssh_set_enabled) - - def select_connection_file(self): - cf = getopenfilename(self, _('Open IPython connection file'), - osp.join(get_ipython_dir(), 'profile_default', 'security'), - '*.json;;*.*')[0] - self.cf.setText(cf) - - def select_ssh_key(self): - kf = getopenfilename(self, _('Select ssh key'), - get_ipython_dir(), '*.pem;;*.*')[0] - self.kf.setText(kf) - - @staticmethod - def get_connection_parameters(parent=None): - dialog = KernelConnectionDialog(parent) - result = dialog.exec_() - is_remote = bool(dialog.rm_cb.checkState()) - accepted = result == QDialog.Accepted - if is_remote: - falsy_to_none = lambda arg: arg if arg else None - return (dialog.cf.text(), # connection file - falsy_to_none(dialog.hn.text()), # host name - falsy_to_none(dialog.kf.text()), # ssh key file - falsy_to_none(dialog.pw.text()), # ssh password - accepted) # ok - else: - return (dialog.cf.text(), None, None, None, accepted) - - -class IPythonConsole(SpyderPluginWidget): - """ - IPython Console plugin - - This is a widget with tabs where each one is an IPythonClient - """ - CONF_SECTION = 'ipython_console' - CONFIGWIDGET_CLASS = IPythonConsoleConfigPage - DISABLE_ACTIONS_WHEN_HIDDEN = False - - def __init__(self, parent): - SpyderPluginWidget.__init__(self, parent) - - self.tabwidget = None - self.menu_actions = None - - self.extconsole = None # External console plugin - self.inspector = None # Object inspector plugin - self.historylog = None # History log plugin - self.variableexplorer = None # Variable explorer plugin - - self.master_clients = 0 - self.clients = [] - - # Initialize plugin - self.initialize_plugin() - - layout = QVBoxLayout() - self.tabwidget = Tabs(self, self.menu_actions) - if hasattr(self.tabwidget, 'setDocumentMode')\ - and not sys.platform == 'darwin': - # Don't set document mode to true on OSX because it generates - # a crash when the console is detached from the main window - # Fixes Issue 561 - self.tabwidget.setDocumentMode(True) - self.connect(self.tabwidget, SIGNAL('currentChanged(int)'), - self.refresh_plugin) - self.connect(self.tabwidget, SIGNAL('move_data(int,int)'), - self.move_tab) - - self.tabwidget.set_close_function(self.close_client) - - if sys.platform == 'darwin': - tab_container = QWidget() - tab_container.setObjectName('tab-container') - tab_layout = QHBoxLayout(tab_container) - tab_layout.setContentsMargins(0, 0, 0, 0) - tab_layout.addWidget(self.tabwidget) - layout.addWidget(tab_container) - else: - layout.addWidget(self.tabwidget) - - # Find/replace widget - self.find_widget = FindReplace(self) - self.find_widget.hide() - self.register_widget_shortcuts("Editor", self.find_widget) - layout.addWidget(self.find_widget) - - self.setLayout(layout) - - # Accepting drops - self.setAcceptDrops(True) - - #------ SpyderPluginMixin API --------------------------------------------- - def on_first_registration(self): - """Action to be performed on first plugin registration""" - self.main.tabify_plugins(self.main.extconsole, self) - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - font_n = 'plugin_font' - font_o = self.get_plugin_font() - inspector_n = 'connect_to_oi' - inspector_o = CONF.get('inspector', 'connect/ipython_console') - for client in self.clients: - control = client.get_control() - if font_n in options: - client.set_font(font_o) - if inspector_n in options and control is not None: - control.set_inspector_enabled(inspector_o) - - def toggle_view(self, checked): - """Toggle view""" - if checked: - self.dockwidget.show() - self.dockwidget.raise_() - # Start a client in case there are none shown - if not self.clients: - if self.main.is_setting_up: - self.create_new_client(give_focus=False) - else: - self.create_new_client(give_focus=True) - else: - self.dockwidget.hide() - - #------ SpyderPluginWidget API -------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _('IPython console') - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('ipython_console.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - client = self.tabwidget.currentWidget() - if client is not None: - return client.get_control() - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - for client in self.clients: - client.close() - return True - - def refresh_plugin(self): - """Refresh tabwidget""" - client = None - if self.tabwidget.count(): - # Give focus to the control widget of the selected tab - client = self.tabwidget.currentWidget() - control = client.get_control() - control.setFocus() - widgets = client.get_toolbar_buttons()+[5] - - # Change extconsole tab to the client's kernel widget - idx = self.extconsole.get_shell_index_from_id( - client.kernel_widget_id) - if idx is not None: - self.extconsole.tabwidget.setCurrentIndex(idx) - else: - control = None - widgets = [] - self.find_widget.set_editor(control) - self.tabwidget.set_corner_widgets({Qt.TopRightCorner: widgets}) - self.main.last_console_plugin_focus_was_python = False - self.emit(SIGNAL('update_plugin_title()')) - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - ctrl = "Cmd" if sys.platform == "darwin" else "Ctrl" - main_create_client_action = create_action(self, - _("Open an &IPython console"), - None, 'ipython_console.png', - triggered=self.create_new_client, - tip=_("Use %s+T when the console is selected " - "to open a new one") % ctrl) - create_client_action = create_action(self, - _("Open a new console"), - QKeySequence("Ctrl+T"), 'ipython_console.png', - triggered=self.create_new_client) - create_client_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) - - connect_to_kernel_action = create_action(self, - _("Connect to an existing kernel"), None, None, - _("Open a new IPython console connected to an existing kernel"), - triggered=self.create_client_for_kernel) - - # Add the action to the 'Consoles' menu on the main window - main_consoles_menu = self.main.consoles_menu_actions - main_consoles_menu.insert(0, main_create_client_action) - main_consoles_menu += [None, connect_to_kernel_action] - - # Plugin actions - self.menu_actions = [create_client_action, connect_to_kernel_action] - - return self.menu_actions - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.main.add_dockwidget(self) - - self.extconsole = self.main.extconsole - self.inspector = self.main.inspector - self.historylog = self.main.historylog - self.variableexplorer = self.main.variableexplorer - - self.connect(self, SIGNAL('focus_changed()'), - self.main.plugin_focus_changed) - - if self.main.editor is not None: - self.connect(self, SIGNAL("edit_goto(QString,int,QString)"), - self.main.editor.load) - self.connect(self.main.editor, - SIGNAL('run_in_current_ipyclient(QString,QString,QString,bool)'), - self.run_script_in_current_client) - - #------ Public API (for clients) ------------------------------------------ - def get_clients(self): - """Return clients list""" - return [cl for cl in self.clients if isinstance(cl, IPythonClient)] - -# def get_kernels(self): -# """Return IPython kernel widgets list""" -# return [sw for sw in self.shellwidgets -# if isinstance(sw, IPythonKernel)] -# - - def get_focus_client(self): - """Return current client with focus, if any""" - widget = QApplication.focusWidget() - for client in self.get_clients(): - if widget is client or widget is client.get_control(): - return client - - def get_current_client(self): - """Return the currently selected client""" - client = self.tabwidget.currentWidget() - if client is not None: - return client - - def run_script_in_current_client(self, filename, wdir, args, debug): - """Run script in current client, if any""" - norm = lambda text: remove_backslashes(to_text_string(text)) - client = self.get_current_client() - if client is not None: - # Internal kernels, use runfile - if client.kernel_widget_id is not None: - line = "%s('%s'" % ('debugfile' if debug else 'runfile', - norm(filename)) - if args: - line += ", args='%s'" % norm(args) - if wdir: - line += ", wdir='%s'" % norm(wdir) - line += ")" - else: # External kernels, use %run - line = "%run " - if debug: - line += "-d " - line += "\"%s\"" % to_text_string(filename) - if args: - line += " %s" % norm(args) - self.execute_python_code(line) - self.visibility_changed(True) - self.raise_() - else: - #XXX: not sure it can really happen - QMessageBox.warning(self, _('Warning'), - _("No IPython console is currently available to run %s." - "

    Please open a new one and try again." - ) % osp.basename(filename), QMessageBox.Ok) - - def execute_python_code(self, lines): - client = self.get_current_client() - if client is not None: - client.shellwidget.execute(to_text_string(lines)) - self.activateWindow() - client.get_control().setFocus() - - def write_to_stdin(self, line): - client = self.get_current_client() - if client is not None: - client.shellwidget.write_to_stdin(line) - - def create_new_client(self, give_focus=True): - """Create a new client""" - self.master_clients += 1 - name = "%d/A" % self.master_clients - client = IPythonClient(self, name=name, history_filename='history.py', - menu_actions=self.menu_actions) - self.add_tab(client, name=client.get_name()) - self.main.extconsole.start_ipykernel(client, give_focus=give_focus) - - def register_client(self, client, restart=False, give_focus=True): - """Register new client""" - self.connect_client_to_kernel(client) - client.show_shellwidget(give_focus=give_focus) - - # Local vars - shellwidget = client.shellwidget - control = shellwidget._control - page_control = shellwidget._page_control - - # Create new clients with Ctrl+T shortcut - self.connect(shellwidget, SIGNAL('new_ipyclient()'), - self.create_new_client) - - # Handle kernel interrupts - extconsoles = self.extconsole.shellwidgets - kernel_widget = None - if extconsoles: - if extconsoles[-1].connection_file == client.connection_file: - kernel_widget = extconsoles[-1] - if restart: - shellwidget.custom_interrupt_requested.disconnect() - shellwidget.custom_interrupt_requested.connect( - kernel_widget.keyboard_interrupt) - if kernel_widget is None: - shellwidget.custom_interrupt_requested.connect( - client.interrupt_message) - - # Connect to our variable explorer - if kernel_widget is not None and self.variableexplorer is not None: - nsb = self.variableexplorer.currentWidget() - # When the autorefresh button is active, our kernels - # start to consume more and more CPU during time - # Fix Issue 1450 - # ---------------- - # When autorefresh is off by default we need the next - # line so that kernels don't start to consume CPU - # Fix Issue 1595 - nsb.auto_refresh_button.setChecked(True) - nsb.auto_refresh_button.setChecked(False) - nsb.auto_refresh_button.setEnabled(False) - nsb.set_ipyclient(client) - client.set_namespacebrowser(nsb) - - # If we are restarting the kernel we need to rename - # the client tab and do no more from here on - if restart: - self.rename_client_tab(client) - return - - # For tracebacks - self.connect(control, SIGNAL("go_to_error(QString)"), self.go_to_error) - - # Handle kernel restarts asked by the user - if kernel_widget is not None: - shellwidget.custom_restart_requested.connect( - lambda cl=client: self.restart_kernel(client)) - else: - shellwidget.custom_restart_requested.connect(client.restart_message) - - # Print a message if kernel dies unexpectedly - shellwidget.custom_restart_kernel_died.connect( - lambda t: client.if_kernel_dies(t)) - - # Connect text widget to our inspector - if kernel_widget is not None and self.inspector is not None: - control.set_inspector(self.inspector) - control.set_inspector_enabled(CONF.get('inspector', - 'connect/ipython_console')) - - # Connect client to our history log - if self.historylog is not None: - self.historylog.add_history(client.history_filename) - self.connect(client, SIGNAL('append_to_history(QString,QString)'), - self.historylog.append_to_history) - - # Set font for client - client.set_font( self.get_plugin_font() ) - - # Connect focus signal to client's control widget - self.connect(control, SIGNAL('focus_changed()'), - lambda: self.emit(SIGNAL('focus_changed()'))) - - # Update the find widget if focus changes between control and - # page_control - self.find_widget.set_editor(control) - if page_control: - self.connect(page_control, SIGNAL('focus_changed()'), - lambda: self.emit(SIGNAL('focus_changed()'))) - self.connect(control, SIGNAL('visibility_changed(bool)'), - self.refresh_plugin) - self.connect(page_control, SIGNAL('visibility_changed(bool)'), - self.refresh_plugin) - self.connect(page_control, SIGNAL('show_find_widget()'), - self.find_widget.show) - - def close_client(self, index=None, client=None, force=False): - """Close client tab from index or widget (or close current tab)""" - if not self.tabwidget.count(): - return - if client is not None: - index = self.tabwidget.indexOf(client) - if index is None and client is None: - index = self.tabwidget.currentIndex() - if index is not None: - client = self.tabwidget.widget(index) - - # Check if related clients or kernels are opened - # and eventually ask before closing them - if not force and isinstance(client, IPythonClient): - kernel_index = self.extconsole.get_shell_index_from_id( - client.kernel_widget_id) - close_all = True - if len(self.get_related_clients(client)) > 0 and \ - self.get_option('ask_before_closing'): - ans = QMessageBox.question(self, self.get_plugin_title(), - _("Do you want to close all other consoles connected " - "to the same kernel as this one?"), - QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) - if ans == QMessageBox.Cancel: - return - close_all = ans == QMessageBox.Yes - if close_all: - if kernel_index is not None: - self.extconsole.close_console(index=kernel_index, - from_ipyclient=True) - self.close_related_clients(client) - client.close() - - # Note: client index may have changed after closing related widgets - self.tabwidget.removeTab(self.tabwidget.indexOf(client)) - self.clients.remove(client) - self.emit(SIGNAL('update_plugin_title()')) - - def get_client_index_from_id(self, client_id): - """Return client index from id""" - for index, client in enumerate(self.clients): - if id(client) == client_id: - return index - - def rename_client_tab(self, client): - """Add the pid of the kernel process to client tab""" - index = self.get_client_index_from_id(id(client)) - self.tabwidget.setTabText(index, client.get_name()) - - def get_related_clients(self, client): - """ - Get all other clients that are connected to the same kernel as `client` - """ - related_clients = [] - for cl in self.get_clients(): - if cl.connection_file == client.connection_file and \ - cl is not client: - related_clients.append(cl) - return related_clients - - def close_related_clients(self, client): - """Close all clients related to *client*, except itself""" - related_clients = self.get_related_clients(client) - for cl in related_clients: - self.close_client(client=cl, force=True) - - #------ Public API (for kernels) ------------------------------------------ - def ssh_tunnel(self, *args, **kwargs): - if sys.platform == 'win32': - return zmqtunnel.paramiko_tunnel(*args, **kwargs) - else: - return openssh_tunnel(self, *args, **kwargs) - - def tunnel_to_kernel(self, ci, hostname, sshkey=None, password=None, timeout=10): - """tunnel connections to a kernel via ssh. remote ports are specified in - the connection info ci.""" - lports = zmqtunnel.select_random_ports(4) - rports = ci['shell_port'], ci['iopub_port'], ci['stdin_port'], ci['hb_port'] - remote_ip = ci['ip'] - for lp, rp in zip(lports, rports): - self.ssh_tunnel(lp, rp, hostname, remote_ip, sshkey, password, timeout) - return tuple(lports) - - def create_kernel_manager_and_client(self, connection_file=None, - hostname=None, sshkey=None, - password=None): - """Create kernel manager and client""" - cf = find_connection_file(connection_file) - kernel_manager = QtKernelManager(connection_file=cf, config=None) - kernel_client = kernel_manager.client() - kernel_client.load_connection_file() - if hostname is not None: - try: - newports = self.tunnel_to_kernel(dict(ip=kernel_client.ip, - shell_port=kernel_client.shell_port, - iopub_port=kernel_client.iopub_port, - stdin_port=kernel_client.stdin_port, - hb_port=kernel_client.hb_port), - hostname, sshkey, password) - (kernel_client.shell_port, kernel_client.iopub_port, - kernel_client.stdin_port, kernel_client.hb_port) = newports - except Exception as e: - QMessageBox.critical(self, _('Connection error'), - _("Could not open ssh tunnel. The " - "error was:\n\n") + to_text_string(e)) - return None, None - kernel_client.start_channels() - # To rely on kernel's heartbeat to know when a kernel has died - kernel_client.hb_channel.unpause() - return kernel_manager, kernel_client - - def connect_client_to_kernel(self, client): - """ - Connect a client to its kernel - """ - km, kc = self.create_kernel_manager_and_client(client.connection_file, - client.hostname, - client.sshkey, - client.password) - if km is not None: - widget = client.shellwidget - widget.kernel_manager = km - widget.kernel_client = kc - - def create_client_for_kernel(self): - """Create a client connected to an existing kernel""" - (cf, hostname, - kf, pw, ok) = KernelConnectionDialog.get_connection_parameters(self) - if not ok: - return - else: - self._create_client_for_kernel(cf, hostname, kf, pw) - - def _create_client_for_kernel(self, cf, hostname, kf, pw): - # Verifying if the connection file exists - cf = osp.basename(cf) - try: - find_connection_file(cf) - except (IOError, UnboundLocalError): - QMessageBox.critical(self, _('IPython'), - _("Unable to connect to IPython %s") % cf) - return - - # Getting the master name that corresponds to the client - # (i.e. the i in i/A) - master_name = None - slave_ord = ord('A') - 1 - for cl in self.get_clients(): - if cf in cl.connection_file: - cf = cl.connection_file - if master_name is None: - master_name = cl.name.split('/')[0] - new_slave_ord = ord(cl.name.split('/')[1]) - if new_slave_ord > slave_ord: - slave_ord = new_slave_ord - - # If we couldn't find a client with the same connection file, - # it means this is a new master client - if master_name is None: - self.master_clients += 1 - master_name = to_text_string(self.master_clients) - - # Set full client name - name = master_name + '/' + chr(slave_ord + 1) - - # Getting kernel_widget_id from the currently open kernels. - kernel_widget_id = None - for sw in self.extconsole.shellwidgets: - if sw.connection_file == cf.split('/')[-1]: - kernel_widget_id = id(sw) - - # Creating the client - client = IPythonClient(self, name=name, history_filename='history.py', - connection_file=cf, - kernel_widget_id=kernel_widget_id, - menu_actions=self.menu_actions, - hostname=hostname, sshkey=kf, password=pw) - - # Adding the tab - self.add_tab(client, name=client.get_name()) - - # Connecting kernel and client - self.register_client(client) - - def restart_kernel(self, client): - """ - Create a new kernel and connect it to `client` if the user asks for it - """ - # Took this bit of code (until if result == ) from the IPython project - # (qt/frontend_widget.py - restart_kernel). - # Licensed under the BSD license - message = _('Are you sure you want to restart the kernel?') - buttons = QMessageBox.Yes | QMessageBox.No - result = QMessageBox.question(self, _('Restart kernel?'), - message, buttons) - if result == QMessageBox.Yes: - client.show_restart_animation() - # Close old kernel tab - idx = self.extconsole.get_shell_index_from_id(client.kernel_widget_id) - self.extconsole.close_console(index=idx, from_ipyclient=True) - - # Create a new one and connect it to the client - self.extconsole.start_ipykernel(client) - - def get_shellwidget_by_kernelwidget_id(self, kernel_id): - """Return the IPython widget associated to a kernel widget id""" - for cl in self.clients: - if cl.kernel_widget_id == kernel_id: - return cl.shellwidget - else: - raise ValueError("Unknown kernel widget ID %r" % kernel_id) - - #------ Public API (for tabs) --------------------------------------------- - def add_tab(self, widget, name): - """Add tab""" - self.clients.append(widget) - index = self.tabwidget.addTab(widget, get_icon('ipython_console.png'), - name) - self.tabwidget.setCurrentIndex(index) - if self.dockwidget and not self.ismaximized: - self.dockwidget.setVisible(True) - self.dockwidget.raise_() - self.activateWindow() - widget.get_control().setFocus() - - def move_tab(self, index_from, index_to): - """ - Move tab (tabs themselves have already been moved by the tabwidget) - """ - client = self.clients.pop(index_from) - self.clients.insert(index_to, client) - self.emit(SIGNAL('update_plugin_title()')) - - #------ Public API (for help) --------------------------------------------- - def go_to_error(self, text): - """Go to error if relevant""" - match = get_error_match(to_text_string(text)) - if match: - fname, lnb = match.groups() - self.emit(SIGNAL("edit_goto(QString,int,QString)"), - osp.abspath(fname), int(lnb), '') - - def show_intro(self): - """Show intro to IPython help""" - from IPython.core.usage import interactive_usage - self.inspector.show_rich_text(interactive_usage) - - def show_guiref(self): - """Show qtconsole help""" - from IPython.core.usage import gui_reference - self.inspector.show_rich_text(gui_reference, collapse=True) - - def show_quickref(self): - """Show IPython Cheat Sheet""" - from IPython.core.usage import quick_reference - self.inspector.show_plain_text(quick_reference) - - #----Drag and drop - #TODO: try and reimplement this block - # (this is still the original code block copied from externalconsole.py) -# def dragEnterEvent(self, event): -# """Reimplement Qt method -# Inform Qt about the types of data that the widget accepts""" -# source = event.mimeData() -# if source.hasUrls(): -# if mimedata2url(source): -# pathlist = mimedata2url(source) -# shellwidget = self.tabwidget.currentWidget() -# if all([is_python_script(unicode(qstr)) for qstr in pathlist]): -# event.acceptProposedAction() -# elif shellwidget is None or not shellwidget.is_running(): -# event.ignore() -# else: -# event.acceptProposedAction() -# else: -# event.ignore() -# elif source.hasText(): -# event.acceptProposedAction() -# -# def dropEvent(self, event): -# """Reimplement Qt method -# Unpack dropped data and handle it""" -# source = event.mimeData() -# shellwidget = self.tabwidget.currentWidget() -# if source.hasText(): -# qstr = source.text() -# if is_python_script(unicode(qstr)): -# self.start(qstr) -# elif shellwidget: -# shellwidget.shell.insert_text(qstr) -# elif source.hasUrls(): -# pathlist = mimedata2url(source) -# if all([is_python_script(unicode(qstr)) for qstr in pathlist]): -# for fname in pathlist: -# self.start(fname) -# elif shellwidget: -# shellwidget.shell.drop_pathlist(pathlist) -# event.acceptProposedAction() - diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/onlinehelp.py spyder-3.0.2+dfsg1/spyderlib/plugins/onlinehelp.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/onlinehelp.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/onlinehelp.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Online Help Plugin""" - -from spyderlib.qt.QtCore import Signal - -import os.path as osp - -# Local imports -from spyderlib.baseconfig import get_conf_path, _ -from spyderlib.widgets.pydocgui import PydocBrowser -from spyderlib.plugins import SpyderPluginMixin -from spyderlib.py3compat import to_text_string - - -class OnlineHelp(PydocBrowser, SpyderPluginMixin): - """ - Online Help Plugin - """ - sig_option_changed = Signal(str, object) - CONF_SECTION = 'onlinehelp' - LOG_PATH = get_conf_path(CONF_SECTION) - def __init__(self, parent): - self.main = parent - PydocBrowser.__init__(self, parent) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - self.register_widget_shortcuts("Editor", self.find_widget) - - self.webview.set_zoom_factor(self.get_option('zoom_factor')) - self.url_combo.setMaxCount(self.get_option('max_history_entries')) - self.url_combo.addItems( self.load_history() ) - - #------ Public API --------------------------------------------------------- - def load_history(self, obj=None): - """Load history from a text file in user home directory""" - if osp.isfile(self.LOG_PATH): - history = [line.replace('\n', '') - for line in open(self.LOG_PATH, 'r').readlines()] - else: - history = [] - return history - - def save_history(self): - """Save history to a text file in user home directory""" - open(self.LOG_PATH, 'w').write("\n".join( \ - [to_text_string(self.url_combo.itemText(index)) - for index in range(self.url_combo.count())] )) - - #------ SpyderPluginMixin API --------------------------------------------- - def visibility_changed(self, enable): - """DockWidget visibility has changed""" - SpyderPluginMixin.visibility_changed(self, enable) - if enable and not self.is_server_running(): - self.initialize() - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _('Online help') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - self.url_combo.lineEdit().selectAll() - return self.url_combo - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - self.save_history() - self.set_option('zoom_factor', self.webview.get_zoom_factor()) - return True - - def refresh_plugin(self): - """Refresh widget""" - pass - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - return [] - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.main.add_dockwidget(self) - \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/outlineexplorer.py spyder-3.0.2+dfsg1/spyderlib/plugins/outlineexplorer.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/outlineexplorer.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/outlineexplorer.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Outline Explorer Plugin - -Data for outline are provided by method .get_outlineexplorer_data() of -highlighter of assigned editor. For example, for Python files code editor uses -highlighter spyderlib.widgets.sourcecode.syntaxhighlighters.PythonSH -""" - -from spyderlib.qt.QtCore import SIGNAL, Signal - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.utils.qthelpers import get_icon -from spyderlib.widgets.editortools import OutlineExplorerWidget -from spyderlib.plugins import SpyderPluginMixin -from spyderlib.py3compat import is_text_string - - -class OutlineExplorer(OutlineExplorerWidget, SpyderPluginMixin): - CONF_SECTION = 'outline_explorer' - sig_option_changed = Signal(str, object) - def __init__(self, parent=None, fullpath_sorting=True): - show_fullpath = self.get_option('show_fullpath') - show_all_files = self.get_option('show_all_files') - show_comments = self.get_option('show_comments') - OutlineExplorerWidget.__init__(self, parent=parent, - show_fullpath=show_fullpath, - fullpath_sorting=fullpath_sorting, - show_all_files=show_all_files, - show_comments=show_comments) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - self.treewidget.header().hide() - self.load_config() - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _("Outline") - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('outline_explorer.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.treewidget - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - return [] - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self.main, SIGNAL('restore_scrollbar_position()'), - self.restore_scrollbar_position) - self.main.add_dockwidget(self) - - def refresh_plugin(self): - """Refresh project explorer widget""" - pass - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - self.save_config() - return True - - #------ SpyderPluginMixin API --------------------------------------------- - def visibility_changed(self, enable): - """DockWidget visibility has changed""" - SpyderPluginMixin.visibility_changed(self, enable) - if enable: - self.emit(SIGNAL("outlineexplorer_is_visible()")) - - #------ Public API --------------------------------------------------------- - def restore_scrollbar_position(self): - """Restoring scrollbar position after main window is visible""" - scrollbar_pos = self.get_option('scrollbar_position', None) - if scrollbar_pos is not None: - self.treewidget.set_scrollbar_position(scrollbar_pos) - - def save_config(self): - """Save configuration: tree widget state""" - for option, value in list(self.get_options().items()): - self.set_option(option, value) - self.set_option('expanded_state', self.treewidget.get_expanded_state()) - self.set_option('scrollbar_position', - self.treewidget.get_scrollbar_position()) - - def load_config(self): - """Load configuration: tree widget state""" - expanded_state = self.get_option('expanded_state', None) - # Sometimes the expanded state option may be truncated in .ini file - # (for an unknown reason), in this case it would be converted to a - # string by 'userconfig': - if is_text_string(expanded_state): - expanded_state = None - if expanded_state is not None: - self.treewidget.set_expanded_state(expanded_state) diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/projectexplorer.py spyder-3.0.2+dfsg1/spyderlib/plugins/projectexplorer.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/projectexplorer.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/projectexplorer.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,150 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Project Explorer Plugin""" - -from spyderlib.qt.QtGui import QFontDialog -from spyderlib.qt.QtCore import SIGNAL - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.utils.qthelpers import get_icon, create_action -from spyderlib.widgets.projectexplorer import ProjectExplorerWidget -from spyderlib.plugins import SpyderPluginMixin -from spyderlib.py3compat import is_text_string - - -class ProjectExplorer(ProjectExplorerWidget, SpyderPluginMixin): - """Project explorer plugin""" - CONF_SECTION = 'project_explorer' - - def __init__(self, parent=None): - ProjectExplorerWidget.__init__(self, parent=parent, - name_filters=self.get_option('name_filters'), - show_all=self.get_option('show_all', False), - show_hscrollbar=self.get_option('show_hscrollbar')) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - self.treewidget.header().hide() - self.set_font(self.get_plugin_font()) - self.load_config() - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _("Project explorer") - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.treewidget - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - new_project_act = create_action(self, text=_('New project...'), - icon=get_icon('project_expanded.png'), - triggered=self.create_new_project) - - font_action = create_action(self, _("&Font..."), - None, 'font.png', _("Set font style"), - triggered=self.change_font) - self.treewidget.common_actions += (None, font_action) - - self.main.file_menu_actions.insert(1, new_project_act) - - return [] - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.main.pythonpath_changed() - self.connect(self.main, SIGNAL('restore_scrollbar_position()'), - self.restore_scrollbar_position) - self.connect(self, SIGNAL("pythonpath_changed()"), - self.main.pythonpath_changed) - self.connect(self, SIGNAL("projects_were_closed()"), - self.projects_were_closed) - self.connect(self, SIGNAL("create_module(QString)"), - self.main.editor.new) - self.connect(self, SIGNAL("edit(QString)"), self.main.editor.load) - self.connect(self, SIGNAL("removed(QString)"), - self.main.editor.removed) - self.connect(self, SIGNAL("removed_tree(QString)"), - self.main.editor.removed_tree) - self.connect(self, SIGNAL("renamed(QString,QString)"), - self.main.editor.renamed) - self.main.editor.set_projectexplorer(self) - self.main.add_dockwidget(self) - - self.sig_open_file.connect(self.main.open_file) - - def refresh_plugin(self): - """Refresh project explorer widget""" - pass - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - self.save_config() - self.closing_widget() - return True - - #------ Public API --------------------------------------------------------- - def create_new_project(self): - """Create new project""" - if self.dockwidget.isHidden(): - self.dockwidget.show() - self.dockwidget.raise_() - if not self.treewidget.new_project(): - # Notify dockwidget to schedule a repaint - self.dockwidget.update() - - def projects_were_closed(self): - """Project were just closed: checking if related files are opened in - the editor and closing them""" - for fname in self.main.editor.get_filenames(): - if self.treewidget.workspace.is_file_in_closed_project(fname): - self.main.editor.close_file_from_name(fname) - - def change_font(self): - """Change font""" - font, valid = QFontDialog.getFont(self.get_plugin_font(), self, - _("Select a new font")) - if valid: - self.set_font(font) - self.set_plugin_font(font) - - def set_font(self, font): - """Set project explorer widget font""" - self.treewidget.setFont(font) - - def save_config(self): - """Save configuration: opened projects & tree widget state""" - self.set_option('workspace', self.get_workspace()) - self.set_option('expanded_state', self.treewidget.get_expanded_state()) - self.set_option('scrollbar_position', - self.treewidget.get_scrollbar_position()) - - def load_config(self): - """Load configuration: opened projects & tree widget state""" - self.set_workspace(self.get_option('workspace', None)) - expanded_state = self.get_option('expanded_state', None) - # Sometimes the expanded state option may be truncated in .ini file - # (for an unknown reason), in this case it would be converted to a - # string by 'userconfig': - if is_text_string(expanded_state): - expanded_state = None - if expanded_state is not None: - self.treewidget.set_expanded_state(expanded_state) - - def restore_scrollbar_position(self): - """Restoring scrollbar position after main window is visible""" - scrollbar_pos = self.get_option('scrollbar_position', None) - if scrollbar_pos is not None: - self.treewidget.set_scrollbar_position(scrollbar_pos) diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/runconfig.py spyder-3.0.2+dfsg1/spyderlib/plugins/runconfig.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/runconfig.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/runconfig.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,504 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Run configurations related dialogs and widgets and data models""" - -from spyderlib.qt.QtGui import (QVBoxLayout, QDialog, QWidget, QGroupBox, - QLabel, QPushButton, QCheckBox, QLineEdit, - QComboBox, QHBoxLayout, QDialogButtonBox, - QStackedWidget, QGridLayout, QSizePolicy, - QRadioButton, QMessageBox, QFrame, - QButtonGroup) -from spyderlib.qt.QtCore import SIGNAL, SLOT, Qt -from spyderlib.qt.compat import getexistingdirectory - -import os.path as osp - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.config import CONF -from spyderlib.utils.qthelpers import get_icon, get_std_icon -from spyderlib.plugins.configdialog import GeneralConfigPage -from spyderlib.py3compat import to_text_string, getcwd - - -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_OPTION = 'default/interpreter/current' -DEDICATED_INTERPRETER_OPTION = 'default/interpreter/dedicated' -SYSTERM_INTERPRETER_OPTION = 'default/interpreter/systerm' - -WDIR_USE_SCRIPT_DIR_OPTION = 'default/wdir/use_script_directory' -WDIR_USE_FIXED_DIR_OPTION = 'default/wdir/use_fixed_directory' -WDIR_FIXED_DIR_OPTION = 'default/wdir/fixed_directory' - -ALWAYS_OPEN_FIRST_RUN = _("Always show %s on a first file run") -ALWAYS_OPEN_FIRST_RUN_OPTION = 'open_on_firstrun' - - -class RunConfiguration(object): - """Run configuration""" - def __init__(self, fname=None): - self.args = None - self.args_enabled = None - self.wdir = None - self.wdir_enabled = None - self.current = None - self.systerm = None - self.interact = None - self.show_kill_warning = None - self.python_args = None - self.python_args_enabled = 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) - self.wdir_enabled = True - - def set(self, options): - self.args = options.get('args', '') - self.args_enabled = options.get('args/enabled', False) - if CONF.get('run', WDIR_USE_FIXED_DIR_OPTION, False): - default_wdir = CONF.get('run', WDIR_FIXED_DIR_OPTION, getcwd()) - self.wdir = options.get('workdir', default_wdir) - self.wdir_enabled = True - else: - self.wdir = options.get('workdir', getcwd()) - self.wdir_enabled = options.get('workdir/enabled', False) - self.current = options.get('current', - CONF.get('run', CURRENT_INTERPRETER_OPTION, True)) - self.systerm = options.get('systerm', - CONF.get('run', SYSTERM_INTERPRETER_OPTION, False)) - self.interact = options.get('interact', False) - self.show_kill_warning = options.get('show_kill_warning', True) - self.python_args = options.get('python_args', '') - self.python_args_enabled = options.get('python_args/enabled', False) - - def get(self): - return { - 'args/enabled': self.args_enabled, - 'args': self.args, - 'workdir/enabled': self.wdir_enabled, - 'workdir': self.wdir, - 'current': self.current, - 'systerm': self.systerm, - 'interact': self.interact, - 'show_kill_warning': self.show_kill_warning, - 'python_args/enabled': self.python_args_enabled, - 'python_args': self.python_args, - } - - def get_working_directory(self): - if self.wdir_enabled: - return self.wdir - else: - return '' - - def get_arguments(self): - if self.args_enabled: - return self.args - else: - return '' - - def get_python_arguments(self): - if self.python_args_enabled: - return self.python_args - else: - return '' - - -def _get_run_configurations(): - history_count = CONF.get('run', 'history', 20) - try: - return [(filename, options) - for filename, options in CONF.get('run', 'configurations', []) - if osp.isfile(filename)][:history_count] - except ValueError: - 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() - for filename, options in configurations: - if fname == filename: - runconf = RunConfiguration() - runconf.set(options) - return runconf - - -class RunConfigOptions(QWidget): - """Run configuration options""" - def __init__(self, parent=None): - QWidget.__init__(self, parent) - - self.current_radio = None - self.dedicated_radio = None - self.systerm_radio = None - - self.runconf = RunConfiguration() - - firstrun_o = CONF.get('run', ALWAYS_OPEN_FIRST_RUN_OPTION, False) - - # --- General settings ---- - common_group = QGroupBox(_("General settings")) - common_layout = QGridLayout() - common_group.setLayout(common_layout) - self.clo_cb = QCheckBox(_("Command line options:")) - common_layout.addWidget(self.clo_cb, 0, 0) - self.clo_edit = QLineEdit() - self.connect(self.clo_cb, SIGNAL("toggled(bool)"), - self.clo_edit.setEnabled) - self.clo_edit.setEnabled(False) - common_layout.addWidget(self.clo_edit, 0, 1) - self.wd_cb = QCheckBox(_("Working directory:")) - common_layout.addWidget(self.wd_cb, 1, 0) - wd_layout = QHBoxLayout() - self.wd_edit = QLineEdit() - self.connect(self.wd_cb, SIGNAL("toggled(bool)"), - self.wd_edit.setEnabled) - self.wd_edit.setEnabled(False) - wd_layout.addWidget(self.wd_edit) - browse_btn = QPushButton(get_std_icon('DirOpenIcon'), "", self) - browse_btn.setToolTip(_("Select directory")) - self.connect(browse_btn, SIGNAL("clicked()"), self.select_directory) - wd_layout.addWidget(browse_btn) - common_layout.addLayout(wd_layout, 1, 1) - - # --- Interpreter --- - interpreter_group = QGroupBox(_("Console")) - interpreter_layout = QVBoxLayout() - interpreter_group.setLayout(interpreter_layout) - self.current_radio = QRadioButton(CURRENT_INTERPRETER) - interpreter_layout.addWidget(self.current_radio) - self.dedicated_radio = QRadioButton(DEDICATED_INTERPRETER) - 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.connect(self.current_radio, SIGNAL("toggled(bool)"), - new_group.setDisabled) - new_layout = QGridLayout() - new_group.setLayout(new_layout) - self.interact_cb = QCheckBox(_("Interact with the Python " - "console after execution")) - new_layout.addWidget(self.interact_cb, 1, 0, 1, -1) - - self.show_kill_warning_cb = QCheckBox(_("Show warning when killing" - " running process")) - 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) - self.pclo_edit = QLineEdit() - self.connect(self.pclo_cb, SIGNAL("toggled(bool)"), - 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) - - #TODO: Add option for "Post-mortem debugging" - - # Checkbox to preserve the old behavior, i.e. always open the dialog - # on first run - hline = QFrame() - hline.setFrameShape(QFrame.HLine) - hline.setFrameShadow(QFrame.Sunken) - self.firstrun_cb = QCheckBox(ALWAYS_OPEN_FIRST_RUN % _("this dialog")) - self.connect(self.firstrun_cb, SIGNAL("clicked(bool)"), - self.set_firstrun_o) - self.firstrun_cb.setChecked(firstrun_o) - - layout = QVBoxLayout() - layout.addWidget(interpreter_group) - layout.addWidget(common_group) - layout.addWidget(new_group) - layout.addWidget(hline) - layout.addWidget(self.firstrun_cb) - self.setLayout(layout) - - def select_directory(self): - """Select directory""" - basedir = to_text_string(self.wd_edit.text()) - if not osp.isdir(basedir): - basedir = getcwd() - directory = getexistingdirectory(self, _("Select directory"), basedir) - if directory: - self.wd_edit.setText(directory) - self.wd_cb.setChecked(True) - - def set(self, options): - self.runconf.set(options) - self.clo_cb.setChecked(self.runconf.args_enabled) - self.clo_edit.setText(self.runconf.args) - self.wd_cb.setChecked(self.runconf.wdir_enabled) - self.wd_edit.setText(self.runconf.wdir) - if self.runconf.current: - self.current_radio.setChecked(True) - elif self.runconf.systerm: - self.systerm_radio.setChecked(True) - 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.pclo_cb.setChecked(self.runconf.python_args_enabled) - self.pclo_edit.setText(self.runconf.python_args) - - def get(self): - self.runconf.args_enabled = self.clo_cb.isChecked() - self.runconf.args = to_text_string(self.clo_edit.text()) - self.runconf.wdir_enabled = self.wd_cb.isChecked() - self.runconf.wdir = to_text_string(self.wd_edit.text()) - 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.python_args_enabled = self.pclo_cb.isChecked() - self.runconf.python_args = to_text_string(self.pclo_edit.text()) - return self.runconf.get() - - def is_valid(self): - wdir = to_text_string(self.wd_edit.text()) - if not self.wd_cb.isChecked() or osp.isdir(wdir): - return True - else: - QMessageBox.critical(self, _("Run configuration"), - _("The following working directory is " - "not valid:
    %s") % wdir) - return False - - def set_firstrun_o(self): - CONF.set('run', ALWAYS_OPEN_FIRST_RUN_OPTION, - self.firstrun_cb.isChecked()) - - -class BaseRunConfigDialog(QDialog): - """Run configuration dialog box, base widget""" - def __init__(self, parent=None): - QDialog.__init__(self, parent) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - self.setWindowIcon(get_icon("run_settings.png")) - layout = QVBoxLayout() - self.setLayout(layout) - - def add_widgets(self, *widgets_or_spacings): - """Add widgets/spacing to dialog vertical layout""" - layout = self.layout() - for widget_or_spacing in widgets_or_spacings: - if isinstance(widget_or_spacing, int): - layout.addSpacing(widget_or_spacing) - else: - layout.addWidget(widget_or_spacing) - - def add_button_box(self, stdbtns): - """Create dialog button box and add it to the dialog layout""" - bbox = QDialogButtonBox(stdbtns) - run_btn = bbox.addButton(_("Run"), QDialogButtonBox.AcceptRole) - self.connect(run_btn, SIGNAL('clicked()'), self.run_btn_clicked) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - btnlayout = QHBoxLayout() - btnlayout.addStretch(1) - btnlayout.addWidget(bbox) - self.layout().addLayout(btnlayout) - - def resizeEvent(self, event): - """ - Reimplement Qt method to be able to save the widget's size from the - main application - """ - QDialog.resizeEvent(self, event) - self.emit(SIGNAL("size_change(QSize)"), self.size()) - - def run_btn_clicked(self): - """Run button was just clicked""" - pass - - def setup(self, fname): - """Setup Run Configuration dialog with filename *fname*""" - raise NotImplementedError - - -class RunConfigOneDialog(BaseRunConfigDialog): - """Run configuration dialog box: single file version""" - def __init__(self, parent=None): - BaseRunConfigDialog.__init__(self, parent) - self.filename = None - self.runconfigoptions = None - - def setup(self, fname): - """Setup Run Configuration dialog with filename *fname*""" - self.filename = fname - self.runconfigoptions = RunConfigOptions(self) - self.runconfigoptions.set(RunConfiguration(fname).get()) - self.add_widgets(self.runconfigoptions) - self.add_button_box(QDialogButtonBox.Cancel) - self.setWindowTitle(_("Run settings for %s") % osp.basename(fname)) - - def accept(self): - """Reimplement Qt method""" - if not self.runconfigoptions.is_valid(): - return - configurations = _get_run_configurations() - configurations.insert(0, (self.filename, self.runconfigoptions.get())) - _set_run_configurations(configurations) - QDialog.accept(self) - - def get_configuration(self): - # It is import to avoid accessing Qt C++ object as it has probably - # already been destroyed, due to the Qt.WA_DeleteOnClose attribute - return self.runconfigoptions.runconf - - -class RunConfigDialog(BaseRunConfigDialog): - """Run configuration dialog box: multiple file version""" - def __init__(self, parent=None): - BaseRunConfigDialog.__init__(self, parent) - self.file_to_run = None - self.combo = None - self.stack = None - - def run_btn_clicked(self): - """Run button was just clicked""" - self.file_to_run = to_text_string(self.combo.currentText()) - - def setup(self, fname): - """Setup Run Configuration dialog with filename *fname*""" - combo_label = QLabel(_("Select a run configuration:")) - self.combo = QComboBox() - self.combo.setMaxVisibleItems(20) - self.combo.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) - self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - - self.stack = QStackedWidget() - - configurations = _get_run_configurations() - for index, (filename, options) in enumerate(configurations): - if fname == filename: - break - else: - # There is no run configuration for script *fname*: - # creating a temporary configuration that will be kept only if - # dialog changes are accepted by the user - configurations.insert(0, (fname, RunConfiguration(fname).get())) - index = 0 - for filename, options in configurations: - widget = RunConfigOptions(self) - widget.set(options) - self.combo.addItem(filename) - self.stack.addWidget(widget) - self.connect(self.combo, SIGNAL("currentIndexChanged(int)"), - self.stack.setCurrentIndex) - self.combo.setCurrentIndex(index) - - self.add_widgets(combo_label, self.combo, 10, self.stack) - self.add_button_box(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) - - self.setWindowTitle(_("Run Settings")) - - def accept(self): - """Reimplement Qt method""" - configurations = [] - for index in range(self.stack.count()): - filename = to_text_string(self.combo.itemText(index)) - runconfigoptions = self.stack.widget(index) - if index == self.stack.currentIndex() and\ - not runconfigoptions.is_valid(): - return - options = runconfigoptions.get() - configurations.append( (filename, options) ) - _set_run_configurations(configurations) - QDialog.accept(self) - - -class RunConfigPage(GeneralConfigPage): - """Default Run Settings configuration page""" - CONF_SECTION = "run" - - NAME = _("Run") - ICON = "run.png" - - def setup_page(self): - run_dlg = _("Run Settings") - run_menu = _("Run") - about_label = QLabel(_("The following are the default %s. "\ - "These options may be overriden using the "\ - "%s dialog box (see the %s menu)"\ - ) % (run_dlg, run_dlg, run_menu)) - about_label.setWordWrap(True) - - interpreter_group = QGroupBox(_("Console")) - interpreter_bg = QButtonGroup(interpreter_group) - self.current_radio = self.create_radiobutton(CURRENT_INTERPRETER, - CURRENT_INTERPRETER_OPTION, True, - button_group=interpreter_bg) - self.dedicated_radio = self.create_radiobutton(DEDICATED_INTERPRETER, - DEDICATED_INTERPRETER_OPTION, False, - button_group=interpreter_bg) - self.systerm_radio = self.create_radiobutton(SYSTERM_INTERPRETER, - SYSTERM_INTERPRETER_OPTION, False, - button_group=interpreter_bg) - - interpreter_layout = QVBoxLayout() - interpreter_group.setLayout(interpreter_layout) - interpreter_layout.addWidget(self.current_radio) - interpreter_layout.addWidget(self.dedicated_radio) - interpreter_layout.addWidget(self.systerm_radio) - - wdir_group = QGroupBox(_("Working directory")) - wdir_bg = QButtonGroup(wdir_group) - wdir_label = QLabel(_("Default working directory is:")) - wdir_label.setWordWrap(True) - dirname_radio = self.create_radiobutton(_("the script directory"), - WDIR_USE_SCRIPT_DIR_OPTION, True, - button_group=wdir_bg) - thisdir_radio = self.create_radiobutton(_("the following directory:"), - WDIR_USE_FIXED_DIR_OPTION, False, - button_group=wdir_bg) - thisdir_bd = self.create_browsedir("", WDIR_FIXED_DIR_OPTION, getcwd()) - self.connect(thisdir_radio, SIGNAL("toggled(bool)"), - thisdir_bd.setEnabled) - self.connect(dirname_radio, SIGNAL("toggled(bool)"), - thisdir_bd.setDisabled) - thisdir_layout = QHBoxLayout() - thisdir_layout.addWidget(thisdir_radio) - thisdir_layout.addWidget(thisdir_bd) - - wdir_layout = QVBoxLayout() - wdir_layout.addWidget(wdir_label) - wdir_layout.addWidget(dirname_radio) - wdir_layout.addLayout(thisdir_layout) - wdir_group.setLayout(wdir_layout) - - firstrun_cb = self.create_checkbox( - ALWAYS_OPEN_FIRST_RUN % _("Run Settings dialog"), - ALWAYS_OPEN_FIRST_RUN_OPTION, False) - - vlayout = QVBoxLayout() - vlayout.addWidget(about_label) - vlayout.addSpacing(10) - vlayout.addWidget(interpreter_group) - vlayout.addWidget(wdir_group) - vlayout.addWidget(firstrun_cb) - vlayout.addStretch(1) - self.setLayout(vlayout) - - def apply_settings(self, options): - pass diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/shortcuts.py spyder-3.0.2+dfsg1/spyderlib/plugins/shortcuts.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/shortcuts.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/shortcuts.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,374 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Shortcut management""" - -from __future__ import print_function - -from spyderlib.qt.QtGui import (QVBoxLayout, QComboBox, QItemDelegate, - QTableView, QMessageBox, QPushButton) -from spyderlib.qt.QtCore import (Qt, QSize, QAbstractTableModel, QModelIndex, - SIGNAL) -from spyderlib.qt.compat import to_qvariant, from_qvariant - -import sys - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.guiconfig import (get_shortcut, set_shortcut, - iter_shortcuts, reset_shortcuts) -from spyderlib.plugins.configdialog import GeneralConfigPage -from spyderlib.py3compat import to_text_string, is_text_string - - -KEYSTRINGS = ["Escape", "Tab", "Backtab", "Backspace", "Return", "Enter", - "Delete", "Pause", "Print", "Clear", "Home", "End", "Left", - "Up", "Right", "Down", "PageUp", "PageDown"] + \ - ["F%d" % _i for _i in range(1, 36)] + \ - ["Space", "Exclam", "QuoteDbl", "NumberSign", "Dollar", "Percent", - "Ampersand", "Apostrophe", "ParenLeft", "ParenRight", "Asterisk", - "Plus", "Comma", "Minus", "Period", "Slash"] + \ - [str(_i) for _i in range(10)] + \ - ["Colon", "Semicolon", "Less", "Equal", "Greater", "Question", - "At"] + [chr(_i) for _i in range(65, 91)] + \ - ["BracketLeft", "Backslash", "BracketRight", "Underscore"] - - -class Key(object): - MODIFIERS = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift", - Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt", - Qt.MetaModifier: "Meta"} - if sys.platform == 'darwin': - MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift", - Qt.ControlModifier: "Cmd", Qt.AltModifier: "Alt", - Qt.MetaModifier: "Ctrl"} - elif sys.platform == 'win32': - MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift", - Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt", - Qt.MetaModifier: "Win"} - else: - MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift", - Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt", - Qt.MetaModifier: "Meta"} - KEYS = {} - for attr in KEYSTRINGS: - KEYS[getattr(Qt, "Key_"+attr)] = attr - - def __init__(self, key, mod1=Qt.NoModifier, mod2=Qt.NoModifier, - mod3=Qt.NoModifier): - modifiers = [mod1, mod2, mod3] - assert all([mod in self.MODIFIERS for mod in modifiers]) - self.modifiers = sorted(modifiers) - assert key in self.KEYS - self.key = key - - def __str__(self): - tlist = [] - for mod in sorted(list(set(self.modifiers))): - if mod != Qt.NoModifier: - tlist.append(self.MODIFIERS[mod]) - tlist.append(self.KEYS[self.key]) - return "+".join(tlist) - - def __unicode__(self): - return to_text_string(self.__str__()) - - @staticmethod - def modifier_from_str(modstr): - for k, v in list(Key.MODIFIERS.items()): - if v.lower() == modstr.lower(): - return k - - @staticmethod - def key_from_str(keystr): - for k, v in list(Key.KEYS.items()): - if v.lower() == keystr.lower(): - return k - - @staticmethod - def modifier_from_name(modname): - for k, v in list(Key.MODIFIERNAMES.items()): - if v.lower() == modname.lower(): - return k - -def keystr2key(keystr): - keylist = keystr.split("+") - mods = [] - if len(keylist) > 1: - for modstr in keylist[:-1]: - mods.append(Key.modifier_from_str(modstr)) - return Key(Key.key_from_str(keylist[-1]), *mods) - -class Shortcut(object): - def __init__(self, context, name, key=None): - self.context = context - self.name = name - if is_text_string(key): - key = keystr2key(key) - self.key = key - - def __str__(self): - return "%s/%s: %s" % (self.context, self.name, self.key) - - def load(self): - self.key = keystr2key(get_shortcut(self.context, self.name)) - - def save(self): - set_shortcut(self.context, self.name, str(self.key)) - - -CONTEXT, NAME, MOD1, MOD2, MOD3, KEY = list(range(6)) - -class ShortcutsModel(QAbstractTableModel): - def __init__(self): - QAbstractTableModel.__init__(self) - self.shortcuts = [] - - def sortByName(self): - self.shortcuts = sorted(self.shortcuts, - key=lambda x: x.context+'/'+x.name) - self.reset() - - def flags(self, index): - if not index.isValid(): - return Qt.ItemIsEnabled - column = index.column() - if column in (MOD1, MOD2, MOD3, KEY): - return Qt.ItemFlags(QAbstractTableModel.flags(self, index)| - Qt.ItemIsEditable) - else: - return Qt.ItemFlags(QAbstractTableModel.flags(self, index)) - - def data(self, index, role=Qt.DisplayRole): - if not index.isValid() or \ - not (0 <= index.row() < len(self.shortcuts)): - return to_qvariant() - shortcut = self.shortcuts[index.row()] - key = shortcut.key - column = index.column() - if role == Qt.DisplayRole: - if column == CONTEXT: - return to_qvariant(shortcut.context) - elif column == NAME: - return to_qvariant(shortcut.name) - elif column == MOD1: - return to_qvariant(Key.MODIFIERNAMES[key.modifiers[0]]) - elif column == MOD2: - return to_qvariant(Key.MODIFIERNAMES[key.modifiers[1]]) - elif column == MOD3: - return to_qvariant(Key.MODIFIERNAMES[key.modifiers[2]]) - elif column == KEY: - return to_qvariant(Key.KEYS[key.key]) - elif role == Qt.TextAlignmentRole: - return to_qvariant(int(Qt.AlignHCenter|Qt.AlignVCenter)) - return to_qvariant() - - def headerData(self, section, orientation, role=Qt.DisplayRole): - if role == Qt.TextAlignmentRole: - if orientation == Qt.Horizontal: - return to_qvariant(int(Qt.AlignHCenter|Qt.AlignVCenter)) - return to_qvariant(int(Qt.AlignRight|Qt.AlignVCenter)) - if role != Qt.DisplayRole: - return to_qvariant() - if orientation == Qt.Horizontal: - if section == CONTEXT: - return to_qvariant(_("Context")) - elif section == NAME: - return to_qvariant(_("Name")) - elif section == MOD1: - return to_qvariant(_("Mod1")) - elif section == MOD2: - return to_qvariant(_("Mod2")) - elif section == MOD3: - return to_qvariant(_("Mod3")) - elif section == KEY: - return to_qvariant(_("Key")) - return to_qvariant() - - def rowCount(self, index=QModelIndex()): - return len(self.shortcuts) - - def columnCount(self, index=QModelIndex()): - return 6 - - def setData(self, index, value, role=Qt.EditRole): - if index.isValid() and 0 <= index.row() < len(self.shortcuts): - shortcut = self.shortcuts[index.row()] - key = shortcut.key - column = index.column() - text = from_qvariant(value, str) - if column == MOD1: - key.modifiers[0] = Key.modifier_from_name(text) - elif column == MOD2: - key.modifiers[1] = Key.modifier_from_name(text) - elif column == MOD3: - key.modifiers[2] = Key.modifier_from_name(text) - elif column == KEY: - key.key = Key.key_from_str(text) - self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), - index, index) - return True - return False - - -class ShortcutsDelegate(QItemDelegate): - def __init__(self, parent=None): - QItemDelegate.__init__(self, parent) - self.modifiers = sorted(Key.MODIFIERNAMES.values()) - self.mod = None - self.keys = sorted(Key.KEYS.values()) - self.key = None - - def sizeHint(self, option, index): - fm = option.fontMetrics - if index.column() in (MOD1, MOD2, MOD3): - if self.mod is None: - w = 0 - for mod in self.modifiers: - cw = fm.width(mod) - if cw > w: - w = cw - self.mod = mod - else: - w = fm.width(self.mod) - return QSize(w+20, fm.height()) - elif index.column() == KEY: - if self.key is None: - w = 0 - for key in self.keys: - cw = fm.width(key) - if cw > w: - w = cw - self.key = key - else: - w = fm.width(self.key) - return QSize(w+20, fm.height()) - return QItemDelegate.sizeHint(self, option, index) - - def createEditor(self, parent, option, index): - if index.column() in (MOD1, MOD2, MOD3): - combobox = QComboBox(parent) - combobox.addItems(self.modifiers) - return combobox - elif index.column() == KEY: - combobox = QComboBox(parent) - combobox.addItems(self.keys) - return combobox - else: - return QItemDelegate.createEditor(self, parent, option, - index) - - def setEditorData(self, editor, index): - text = from_qvariant(index.model().data(index, Qt.DisplayRole), str) - if index.column() in (MOD1, MOD2, MOD3, KEY): - i = editor.findText(text) - if i == -1: - i = 0 - editor.setCurrentIndex(i) - else: - QItemDelegate.setEditorData(self, editor, index) - - def setModelData(self, editor, model, index): - if index.column() in (MOD1, MOD2, MOD3, KEY): - model.setData(index, to_qvariant(editor.currentText())) - else: - QItemDelegate.setModelData(self, editor, model, index) - - -class ShortcutsTable(QTableView): - def __init__(self, parent=None): - QTableView.__init__(self, parent) - self.model = ShortcutsModel() - self.setModel(self.model) - self.setItemDelegate(ShortcutsDelegate(self)) - self.load_shortcuts() - - def adjust_cells(self): - self.resizeColumnsToContents() -# self.resizeRowsToContents() - self.horizontalHeader().setStretchLastSection(True) - - def load_shortcuts(self): - shortcuts = [] - for context, name, keystr in iter_shortcuts(): - shortcut = Shortcut(context, name, keystr) - shortcuts.append(shortcut) - shortcuts = sorted(shortcuts, key=lambda x: x.context+x.name) - self.model.shortcuts = shortcuts - self.model.reset() - self.adjust_cells() - - def check_shortcuts(self): - """Check shortcuts for conflicts""" - conflicts = [] - for index, sh1 in enumerate(self.model.shortcuts): - if index == len(self.model.shortcuts)-1: - break - for sh2 in self.model.shortcuts[index+1:]: - if sh2 is sh1: - continue - if str(sh2.key) == str(sh1.key) \ - and (sh1.context == sh2.context or sh1.context == '_' - or sh2.context == '_'): - conflicts.append((sh1, sh2)) - if conflicts: - self.parent().emit(SIGNAL('show_this_page()')) - cstr = "\n".join(['%s <---> %s' % (sh1, sh2) - for sh1, sh2 in conflicts]) - QMessageBox.warning(self, _( "Conflicts"), - _("The following conflicts have been " - "detected:")+"\n"+cstr, QMessageBox.Ok) - - def save_shortcuts(self): - self.check_shortcuts() - for shortcut in self.model.shortcuts: - shortcut.save() - - -class ShortcutsConfigPage(GeneralConfigPage): - CONF_SECTION = "shortcuts" - - NAME = _("Keyboard shortcuts") - ICON = "genprefs.png" - - def setup_page(self): - self.table = ShortcutsTable(self) - self.connect(self.table.model, - SIGNAL("dataChanged(QModelIndex,QModelIndex)"), - lambda i1, i2, opt='': self.has_been_modified(opt)) - vlayout = QVBoxLayout() - vlayout.addWidget(self.table) - reset_btn = QPushButton(_("Reset to default values")) - self.connect(reset_btn, SIGNAL('clicked()'), self.reset_to_default) - vlayout.addWidget(reset_btn) - self.setLayout(vlayout) - - def check_settings(self): - self.table.check_shortcuts() - - def reset_to_default(self): - reset_shortcuts() - self.main.apply_shortcuts() - self.table.load_shortcuts() - self.load_from_conf() - self.set_modified(False) - - def apply_settings(self, options): - self.table.save_shortcuts() - self.main.apply_shortcuts() - - -def test(): - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - table = ShortcutsTable() - table.show() - app.exec_() - print([str(s) for s in table.model.shortcuts]) - table.check_shortcuts() - -if __name__ == '__main__': - test() \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/variableexplorer.py spyder-3.0.2+dfsg1/spyderlib/plugins/variableexplorer.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/variableexplorer.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/variableexplorer.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,194 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Namespace Browser Plugin""" - -from spyderlib.qt.QtGui import QStackedWidget, QGroupBox, QVBoxLayout -from spyderlib.qt.QtCore import Signal - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.config import CONF -from spyderlib.utils.qthelpers import get_icon -from spyderlib.utils import programs -from spyderlib.plugins import SpyderPluginMixin, PluginConfigPage -from spyderlib.widgets.externalshell.monitor import REMOTE_SETTINGS -from spyderlib.widgets.externalshell.namespacebrowser import NamespaceBrowser - - -class VariableExplorerConfigPage(PluginConfigPage): - def setup_page(self): - ar_group = QGroupBox(_("Autorefresh")) - ar_box = self.create_checkbox(_("Enable autorefresh"), - 'autorefresh') - ar_spin = self.create_spinbox(_("Refresh interval: "), - _(" ms"), 'autorefresh/timeout', - min_=100, max_=1000000, step=100) - - filter_group = QGroupBox(_("Filter")) - filter_data = [ - ('exclude_private', _("Exclude private references")), - ('exclude_capitalized', _("Exclude capitalized references")), - ('exclude_uppercase', _("Exclude all-uppercase references")), - ('exclude_unsupported', _("Exclude unsupported data types")), - ] - filter_boxes = [self.create_checkbox(text, option) - for option, text in filter_data] - - display_group = QGroupBox(_("Display")) - display_data = [('truncate', _("Truncate values"), '')] - if programs.is_module_installed('numpy'): - display_data.append(('minmax', _("Show arrays min/max"), '')) - display_data.append( - ('remote_editing', _("Edit data in the remote process"), - _("Editors are opened in the remote process for NumPy " - "arrays, PIL images, lists, tuples and dictionaries.\n" - "This avoids transfering large amount of data between " - "the remote process and Spyder (through the socket).")) - ) - display_boxes = [self.create_checkbox(text, option, tip=tip) - for option, text, tip in display_data] - - ar_layout = QVBoxLayout() - ar_layout.addWidget(ar_box) - ar_layout.addWidget(ar_spin) - ar_group.setLayout(ar_layout) - - filter_layout = QVBoxLayout() - for box in filter_boxes: - filter_layout.addWidget(box) - filter_group.setLayout(filter_layout) - - display_layout = QVBoxLayout() - for box in display_boxes: - display_layout.addWidget(box) - display_group.setLayout(display_layout) - - vlayout = QVBoxLayout() - vlayout.addWidget(ar_group) - vlayout.addWidget(filter_group) - vlayout.addWidget(display_group) - vlayout.addStretch(1) - self.setLayout(vlayout) - - -class VariableExplorer(QStackedWidget, SpyderPluginMixin): - """ - Variable Explorer Plugin - """ - CONF_SECTION = 'variable_explorer' - CONFIGWIDGET_CLASS = VariableExplorerConfigPage - sig_option_changed = Signal(str, object) - def __init__(self, parent): - QStackedWidget.__init__(self, parent) - SpyderPluginMixin.__init__(self, parent) - self.shellwidgets = {} - - # Initialize plugin - self.initialize_plugin() - - @staticmethod - def get_settings(): - """ - Return Variable Explorer settings dictionary - (i.e. namespace browser settings according to Spyder's configuration file) - """ - settings = {} -# CONF.load_from_ini() # necessary only when called from another process - for name in REMOTE_SETTINGS: - settings[name] = CONF.get(VariableExplorer.CONF_SECTION, name) - return settings - - #------ Public API --------------------------------------------------------- - def add_shellwidget(self, shellwidget): - shellwidget_id = id(shellwidget) - # Add shell only once: this method may be called two times in a row - # by the External console plugin (dev. convenience) - from spyderlib.widgets.externalshell import systemshell - if isinstance(shellwidget, systemshell.ExternalSystemShell): - return - if shellwidget_id not in self.shellwidgets: - nsb = NamespaceBrowser(self) - nsb.set_shellwidget(shellwidget) - nsb.setup(**VariableExplorer.get_settings()) - nsb.sig_option_changed.connect(self.sig_option_changed.emit) - self.addWidget(nsb) - self.shellwidgets[shellwidget_id] = nsb - self.set_shellwidget_from_id(shellwidget_id) - return nsb - - def remove_shellwidget(self, shellwidget_id): - # If shellwidget_id is not in self.shellwidgets, it simply means - # that shell was not a Python-based console (it was a terminal) - if shellwidget_id in self.shellwidgets: - nsb = self.shellwidgets.pop(shellwidget_id) - self.removeWidget(nsb) - nsb.close() - - def set_shellwidget_from_id(self, shellwidget_id): - if shellwidget_id in self.shellwidgets: - nsb = self.shellwidgets[shellwidget_id] - self.setCurrentWidget(nsb) - if self.isvisible: - nsb.visibility_changed(True) - - def import_data(self, fname): - """Import data in current namespace""" - if self.count(): - nsb = self.currentWidget() - nsb.refresh_table() - nsb.import_data(fname) - if self.dockwidget and not self.ismaximized: - self.dockwidget.setVisible(True) - self.dockwidget.raise_() - - #------ SpyderPluginMixin API --------------------------------------------- - def visibility_changed(self, enable): - """DockWidget visibility has changed""" - SpyderPluginMixin.visibility_changed(self, enable) - for nsb in list(self.shellwidgets.values()): - nsb.visibility_changed(enable and nsb is self.currentWidget()) - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _('Variable explorer') - - def get_plugin_icon(self): - """Return plugin icon""" - return get_icon('dictedit.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.currentWidget() - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - return True - - def refresh_plugin(self): - """Refresh widget""" - pass - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - return [] - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.main.extconsole.set_variableexplorer(self) - self.main.add_dockwidget(self) - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - for nsb in list(self.shellwidgets.values()): - nsb.setup(**VariableExplorer.get_settings()) - ar_timeout = self.get_option('autorefresh/timeout') - for shellwidget in self.main.extconsole.shellwidgets: - shellwidget.set_autorefresh_timeout(ar_timeout) \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/plugins/workingdirectory.py spyder-3.0.2+dfsg1/spyderlib/plugins/workingdirectory.py --- spyder-2.3.8+dfsg1/spyderlib/plugins/workingdirectory.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/plugins/workingdirectory.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,333 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Working Directory Plugin""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import (QToolBar, QLabel, QGroupBox, QVBoxLayout, - QHBoxLayout, QButtonGroup) -from spyderlib.qt.QtCore import SIGNAL, Signal -from spyderlib.qt.compat import getexistingdirectory - -import os -import os.path as osp - -# Local imports -from spyderlib.utils import encoding -from spyderlib.baseconfig import get_conf_path, _ -from spyderlib.utils.qthelpers import get_icon, get_std_icon, create_action - -# Package local imports -from spyderlib.widgets.comboboxes import PathComboBox -from spyderlib.plugins import SpyderPluginMixin, PluginConfigPage -from spyderlib.py3compat import to_text_string, getcwd - - -class WorkingDirectoryConfigPage(PluginConfigPage): - def setup_page(self): - about_label = QLabel(_("The global working directory is " - "the working directory for newly opened consoles " - "(Python/IPython consoles and terminals), for the " - "file explorer, for the find in files " - "plugin and for new files created in the editor.")) - about_label.setWordWrap(True) - - startup_group = QGroupBox(_("Startup")) - startup_bg = QButtonGroup(startup_group) - startup_label = QLabel(_("At startup, the global working " - "directory is:")) - startup_label.setWordWrap(True) - lastdir_radio = self.create_radiobutton( - _("the same as in last session"), - 'startup/use_last_directory', True, - _("At startup, Spyder will restore the " - "global directory from last session"), - button_group=startup_bg) - thisdir_radio = self.create_radiobutton( - _("the following directory:"), - 'startup/use_fixed_directory', False, - _("At startup, the global working " - "directory will be the specified path"), - button_group=startup_bg) - thisdir_bd = self.create_browsedir("", 'startup/fixed_directory', - getcwd()) - self.connect(thisdir_radio, SIGNAL("toggled(bool)"), - thisdir_bd.setEnabled) - self.connect(lastdir_radio, SIGNAL("toggled(bool)"), - thisdir_bd.setDisabled) - thisdir_layout = QHBoxLayout() - thisdir_layout.addWidget(thisdir_radio) - thisdir_layout.addWidget(thisdir_bd) - - editor_o_group = QGroupBox(_("Open file")) - editor_o_label = QLabel(_("Files are opened from:")) - editor_o_label.setWordWrap(True) - editor_o_bg = QButtonGroup(editor_o_group) - editor_o_radio1 = self.create_radiobutton( - _("the current file directory"), - 'editor/open/browse_scriptdir', - button_group=editor_o_bg) - editor_o_radio2 = self.create_radiobutton( - _("the global working directory"), - 'editor/open/browse_workdir', - button_group=editor_o_bg) - - editor_n_group = QGroupBox(_("New file")) - editor_n_label = QLabel(_("Files are created in:")) - editor_n_label.setWordWrap(True) - editor_n_bg = QButtonGroup(editor_n_group) - editor_n_radio1 = self.create_radiobutton( - _("the current file directory"), - 'editor/new/browse_scriptdir', - button_group=editor_n_bg) - editor_n_radio2 = self.create_radiobutton( - _("the global working directory"), - 'editor/new/browse_workdir', - button_group=editor_n_bg) - # Note: default values for the options above are set in plugin's - # constructor (see below) - - other_group = QGroupBox(_("Change to file base directory")) - newcb = self.create_checkbox - open_box = newcb(_("When opening a file"), - 'editor/open/auto_set_to_basedir') - save_box = newcb(_("When saving a file"), - 'editor/save/auto_set_to_basedir') - - startup_layout = QVBoxLayout() - startup_layout.addWidget(startup_label) - startup_layout.addWidget(lastdir_radio) - startup_layout.addLayout(thisdir_layout) - startup_group.setLayout(startup_layout) - - editor_o_layout = QVBoxLayout() - editor_o_layout.addWidget(editor_o_label) - editor_o_layout.addWidget(editor_o_radio1) - editor_o_layout.addWidget(editor_o_radio2) - editor_o_group.setLayout(editor_o_layout) - - editor_n_layout = QVBoxLayout() - editor_n_layout.addWidget(editor_n_label) - editor_n_layout.addWidget(editor_n_radio1) - editor_n_layout.addWidget(editor_n_radio2) - editor_n_group.setLayout(editor_n_layout) - - other_layout = QVBoxLayout() - other_layout.addWidget(open_box) - other_layout.addWidget(save_box) - other_group.setLayout(other_layout) - - vlayout = QVBoxLayout() - vlayout.addWidget(about_label) - vlayout.addSpacing(10) - vlayout.addWidget(startup_group) - vlayout.addWidget(editor_o_group) - vlayout.addWidget(editor_n_group) - vlayout.addWidget(other_group) - vlayout.addStretch(1) - self.setLayout(vlayout) - - -class WorkingDirectory(QToolBar, SpyderPluginMixin): - """ - Working directory changer widget - """ - CONF_SECTION = 'workingdir' - CONFIGWIDGET_CLASS = WorkingDirectoryConfigPage - LOG_PATH = get_conf_path(CONF_SECTION) - sig_option_changed = Signal(str, object) - def __init__(self, parent, workdir=None): - QToolBar.__init__(self, parent) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - self.setWindowTitle(self.get_plugin_title()) # Toolbar title - self.setObjectName(self.get_plugin_title()) # Used to save Window state - - # Previous dir action - self.history = [] - self.histindex = None - self.previous_action = create_action(self, "previous", None, - get_icon('previous.png'), _('Back'), - triggered=self.previous_directory) - self.addAction(self.previous_action) - - # Next dir action - self.history = [] - self.histindex = None - self.next_action = create_action(self, "next", None, - get_icon('next.png'), _('Next'), - triggered=self.next_directory) - self.addAction(self.next_action) - - # Enable/disable previous/next actions - self.connect(self, SIGNAL("set_previous_enabled(bool)"), - self.previous_action.setEnabled) - self.connect(self, SIGNAL("set_next_enabled(bool)"), - self.next_action.setEnabled) - - # Path combo box - adjust = self.get_option('working_dir_adjusttocontents') - self.pathedit = PathComboBox(self, adjust_to_contents=adjust) - self.pathedit.setToolTip(_("This is the working directory for newly\n" - "opened consoles (Python/IPython consoles and\n" - "terminals), for the file explorer, for the\n" - "find in files plugin and for new files\n" - "created in the editor")) - self.connect(self.pathedit, SIGNAL("open_dir(QString)"), self.chdir) - self.pathedit.setMaxCount(self.get_option('working_dir_history')) - wdhistory = self.load_wdhistory( workdir ) - if workdir is None: - if self.get_option('startup/use_last_directory'): - if wdhistory: - workdir = wdhistory[0] - else: - workdir = "." - else: - workdir = self.get_option('startup/fixed_directory', ".") - if not osp.isdir(workdir): - workdir = "." - self.chdir(workdir) - self.pathedit.addItems( wdhistory ) - self.refresh_plugin() - self.addWidget(self.pathedit) - - # Browse action - browse_action = create_action(self, "browse", None, - get_std_icon('DirOpenIcon'), - _('Browse a working directory'), - triggered=self.select_directory) - self.addAction(browse_action) - - # Set current console working directory action - setwd_action = create_action(self, icon=get_icon('set_workdir.png'), - text=_("Set as current console's " - "working directory"), - triggered=self.set_as_current_console_wd) - self.addAction(setwd_action) - - # Parent dir action - parent_action = create_action(self, "parent", None, - get_icon('up.png'), - _('Change to parent directory'), - triggered=self.parent_directory) - self.addAction(parent_action) - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _('Global working directory') - - def get_plugin_icon(self): - """Return widget icon""" - return get_std_icon('DirOpenIcon') - - def get_plugin_actions(self): - """Setup actions""" - return (None, None) - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self, SIGNAL('redirect_stdio(bool)'), - self.main.redirect_internalshell_stdio) - self.connect(self.main.console.shell, SIGNAL("refresh()"), - self.refresh_plugin) - self.main.addToolBar(self) - - def refresh_plugin(self): - """Refresh widget""" - curdir = getcwd() - self.pathedit.add_text(curdir) - self.save_wdhistory() - self.emit(SIGNAL("set_previous_enabled(bool)"), - self.histindex is not None and self.histindex > 0) - self.emit(SIGNAL("set_next_enabled(bool)"), - self.histindex is not None and \ - self.histindex < len(self.history)-1) - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - pass - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - return True - - #------ Public API --------------------------------------------------------- - def load_wdhistory(self, workdir=None): - """Load history from a text file in user home directory""" - if osp.isfile(self.LOG_PATH): - wdhistory, _ = encoding.readlines(self.LOG_PATH) - wdhistory = [name for name in wdhistory if os.path.isdir(name)] - else: - if workdir is None: - workdir = getcwd() - wdhistory = [ workdir ] - return wdhistory - - def save_wdhistory(self): - """Save history to a text file in user home directory""" - text = [ to_text_string( self.pathedit.itemText(index) ) \ - for index in range(self.pathedit.count()) ] - encoding.writelines(text, self.LOG_PATH) - - def select_directory(self): - """Select directory""" - self.emit(SIGNAL('redirect_stdio(bool)'), False) - directory = getexistingdirectory(self.main, _("Select directory"), - getcwd()) - if directory: - self.chdir(directory) - self.emit(SIGNAL('redirect_stdio(bool)'), True) - - def previous_directory(self): - """Back to previous directory""" - self.histindex -= 1 - self.chdir(browsing_history=True) - - def next_directory(self): - """Return to next directory""" - self.histindex += 1 - self.chdir(browsing_history=True) - - def parent_directory(self): - """Change working directory to parent directory""" - self.chdir(os.path.join(getcwd(), os.path.pardir)) - - def chdir(self, directory=None, browsing_history=False, - refresh_explorer=True): - """Set directory as working directory""" - # Working directory history management - if directory is not None: - directory = osp.abspath(to_text_string(directory)) - if browsing_history: - directory = self.history[self.histindex] - elif directory in self.history: - self.histindex = self.history.index(directory) - else: - if self.histindex is None: - self.history = [] - else: - self.history = self.history[:self.histindex+1] - self.history.append(directory) - self.histindex = len(self.history)-1 - - # Changing working directory - os.chdir( to_text_string(directory) ) - self.refresh_plugin() - if refresh_explorer: - self.emit(SIGNAL("set_explorer_cwd(QString)"), directory) - self.emit(SIGNAL("refresh_findinfiles()")) - - def set_as_current_console_wd(self): - """Set as current console working directory""" - self.emit(SIGNAL("set_current_console_wd(QString)"), getcwd()) diff -Nru spyder-2.3.8+dfsg1/spyderlib/py3compat.py spyder-3.0.2+dfsg1/spyderlib/py3compat.py --- spyder-2.3.8+dfsg1/spyderlib/py3compat.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/py3compat.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,248 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012-2013 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.py3compat -------------------- - -Transitional module providing compatibility functions intended to help -migrating from Python 2 to Python 3. - -This module should be fully compatible with: - * Python >=v2.6 - * Python 3 -""" - -from __future__ import print_function - -import sys -import os - -PY2 = sys.version[0] == '2' -PY3 = sys.version[0] == '3' - - -#============================================================================== -# Data types -#============================================================================== -if PY2: - # Python 2 - TEXT_TYPES = (str, unicode) - INT_TYPES = (int, long) -else: - # Python 3 - TEXT_TYPES = (str,) - INT_TYPES = (int,) -NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex]) - - -#============================================================================== -# Renamed/Reorganized modules -#============================================================================== -if PY2: - # Python 2 - import __builtin__ as builtins - import ConfigParser as configparser - try: - import _winreg as winreg - except ImportError: - pass - from sys import maxint as maxsize - try: - import CStringIO as io - except ImportError: - import StringIO as io - try: - import cPickle as pickle - except ImportError: - import pickle - from UserDict import DictMixin as MutableMapping - import thread as _thread - import repr as reprlib -else: - # Python 3 - import builtins - import configparser - try: - import winreg - except ImportError: - pass - from sys import maxsize - import io - import pickle - from collections import MutableMapping - import _thread - import reprlib - - -#============================================================================== -# Strings -#============================================================================== -if PY2: - # Python 2 - import codecs - def u(obj): - """Make unicode object""" - return codecs.unicode_escape_decode(obj)[0] -else: - # Python 3 - def u(obj): - """Return string as it is""" - return obj - -def is_text_string(obj): - """Return True if `obj` is a text string, False if it is anything else, - like binary data (Python 3) or QString (Python 2, PyQt API #1)""" - if PY2: - # Python 2 - return isinstance(obj, basestring) - else: - # Python 3 - return isinstance(obj, str) - -def is_binary_string(obj): - """Return True if `obj` is a binary string, False if it is anything else""" - if PY2: - # Python 2 - return isinstance(obj, str) - else: - # Python 3 - return isinstance(obj, bytes) - -def is_string(obj): - """Return True if `obj` is a text or binary Python string object, - False if it is anything else, like a QString (Python 2, PyQt API #1)""" - return is_text_string(obj) or is_binary_string(obj) - -def is_unicode(obj): - """Return True if `obj` is unicode""" - if PY2: - # Python 2 - return isinstance(obj, unicode) - else: - # Python 3 - return isinstance(obj, str) - -def to_text_string(obj, encoding=None): - """Convert `obj` to (unicode) text string""" - if PY2: - # Python 2 - if encoding is None: - return unicode(obj) - else: - return unicode(obj, encoding) - else: - # Python 3 - if encoding is None: - return str(obj) - elif isinstance(obj, str): - # In case this function is not used properly, this could happen - return obj - else: - return str(obj, encoding) - -def to_binary_string(obj, encoding=None): - """Convert `obj` to binary string (bytes in Python 3, str in Python 2)""" - if PY2: - # Python 2 - if encoding is None: - return str(obj) - else: - return obj.encode(encoding) - else: - # Python 3 - return bytes(obj, 'utf-8' if encoding is None else encoding) - - -#============================================================================== -# Function attributes -#============================================================================== -def get_func_code(func): - """Return function code object""" - if PY2: - # Python 2 - return func.func_code - else: - # Python 3 - return func.__code__ - -def get_func_name(func): - """Return function name""" - if PY2: - # Python 2 - return func.func_name - else: - # Python 3 - return func.__name__ - -def get_func_defaults(func): - """Return function default argument values""" - if PY2: - # Python 2 - return func.func_defaults - else: - # Python 3 - return func.__defaults__ - - -#============================================================================== -# Special method attributes -#============================================================================== -def get_meth_func(obj): - """Return method function object""" - if PY2: - # Python 2 - return obj.im_func - else: - # Python 3 - return obj.__func__ - -def get_meth_class_inst(obj): - """Return method class instance""" - if PY2: - # Python 2 - return obj.im_self - else: - # Python 3 - return obj.__self__ - -def get_meth_class(obj): - """Return method class""" - if PY2: - # Python 2 - return obj.im_class - else: - # Python 3 - return obj.__self__.__class__ - - -#============================================================================== -# Misc. -#============================================================================== -if PY2: - # Python 2 - input = raw_input - getcwd = os.getcwdu - cmp = cmp - import string - str_lower = string.lower - from itertools import izip_longest as zip_longest -else: - # Python 3 - input = input - getcwd = os.getcwd - def cmp(a, b): - return (a > b) - (a < b) - str_lower = str.lower - from itertools import zip_longest - -def qbytearray_to_str(qba): - """Convert QByteArray object to str in a way compatible with Python 2/3""" - return str(bytes(qba.toHex().data()).decode()) - - -if __name__ == '__main__': - pass diff -Nru spyder-2.3.8+dfsg1/spyderlib/pygments_patch.py spyder-3.0.2+dfsg1/spyderlib/pygments_patch.py --- spyder-2.3.8+dfsg1/spyderlib/pygments_patch.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/pygments_patch.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2014 The Spyder development team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Patching pygments to avoid errors with IPython console -""" - -def apply(): - """ - Monkey patching pygments - See Issue 2042 and https://github.com/ipython/ipython/pull/6878 - """ - from spyderlib.utils.programs import is_module_installed - if is_module_installed('pygments', '<2.0') and \ - is_module_installed('IPython', '>3.0'): - return - - # Patching IPython's patch of RegLexer (Oh God!!) - from pygments.lexer import _TokenType, Text, Error - from spyderlib.py3compat import to_text_string - try: - from IPython.qt.console.pygments_highlighter import RegexLexer - except ImportError: - from IPython.frontend.qt.console.pygments_highlighter import RegexLexer - def get_tokens_unprocessed(self, text, stack=('root',)): - pos = 0 - tokendefs = self._tokens - if hasattr(self, '_saved_state_stack'): - statestack = list(self._saved_state_stack) - else: - statestack = list(stack) - statetokens = tokendefs[statestack[-1]] - while 1: - for rexmatch, action, new_state in statetokens: - m = rexmatch(text, pos) - if m: - if action is not None: - if type(action) is _TokenType: - yield pos, action, m.group() - else: - for item in action(self, m): - yield item - pos = m.end() - if new_state is not None: - # state transition - if isinstance(new_state, tuple): - for state in new_state: - if state == '#pop': - statestack.pop() - elif state == '#push': - statestack.append(statestack[-1]) - else: - statestack.append(state) - elif isinstance(new_state, int): - # pop - del statestack[new_state:] - elif new_state == '#push': - statestack.append(statestack[-1]) - else: - assert False, "wrong state def: %r" % new_state - statetokens = tokendefs[statestack[-1]] - break - else: - try: - if text[pos] == '\n': - # at EOL, reset state to "root" - pos += 1 - statestack = ['root'] - statetokens = tokendefs['root'] - yield pos, Text, to_text_string('\n') - continue - yield pos, Error, text[pos] - pos += 1 - except IndexError: - break - self._saved_state_stack = list(statestack) - - RegexLexer.get_tokens_unprocessed = get_tokens_unprocessed diff -Nru spyder-2.3.8+dfsg1/spyderlib/pyplot.py spyder-3.0.2+dfsg1/spyderlib/pyplot.py --- spyder-2.3.8+dfsg1/spyderlib/pyplot.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/pyplot.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -# -*- coding:utf-8 -*- -""" -Importing guiqwt's pyplot module or matplotlib's pyplot -""" - -try: - from guiqwt.pyplot import * -except ImportError: - from matplotlib.pyplot import * diff -Nru spyder-2.3.8+dfsg1/spyderlib/qt/compat.py spyder-3.0.2+dfsg1/spyderlib/qt/compat.py --- spyder-2.3.8+dfsg1/spyderlib/qt/compat.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/qt/compat.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,210 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011-2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.qt.compat -------------------- - -Transitional module providing compatibility functions intended to help -migrating from PyQt to PySide. - -This module should be fully compatible with: - * PyQt >=v4.4 - * both PyQt API #1 and API #2 - * PySide -""" - -from __future__ import print_function - -import os -import sys -import collections - -from spyderlib.qt.QtGui import QFileDialog - -from spyderlib.py3compat import is_text_string, to_text_string, TEXT_TYPES - -#============================================================================== -# QVariant conversion utilities -#============================================================================== - -PYQT_API_1 = False -if os.environ['QT_API'] == 'pyqt': - import sip - try: - PYQT_API_1 = sip.getapi('QVariant') == 1 # PyQt API #1 - except AttributeError: - # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" - # Calling QFileDialog static method - if sys.platform == "win32": - # On Windows platforms: redirect standard outputs - _temp1, _temp2 = sys.stdout, sys.stderr - sys.stdout, sys.stderr = None, None - try: - result = QFileDialog.getExistingDirectory(parent, caption, basedir, - options) - finally: - if sys.platform == "win32": - # On Windows platforms: restore standard outputs - sys.stdout, sys.stderr = _temp1, _temp2 - if not is_text_string(result): - # PyQt API #1 - result = to_text_string(result) - return result - -def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', - filters='', selectedfilter='', options=None): - if options is None: - options = QFileDialog.Options(0) - try: - # PyQt =v4.6 - QString = None # analysis:ignore - tuple_returned = True - try: - # PyQt >=v4.6 - func = getattr(QFileDialog, attr+'AndFilter') - except AttributeError: - # PySide or PyQt =v4.6 - output, selectedfilter = result - else: - # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getOpenFileName', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) - -def getopenfilenames(parent=None, caption='', basedir='', filters='', - selectedfilter='', options=None): - """Wrapper around QtGui.QFileDialog.getOpenFileNames static method - Returns a tuple (filenames, selectedfilter) -- when dialog box is canceled, - returns a tuple (empty list, empty string) - Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getOpenFileNames', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) - -def getsavefilename(parent=None, caption='', basedir='', filters='', - selectedfilter='', options=None): - """Wrapper around QtGui.QFileDialog.getSaveFileName static method - Returns a tuple (filename, selectedfilter) -- when dialog box is canceled, - returns a tuple of empty strings - Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getSaveFileName', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) - -if __name__ == '__main__': - from spyderlib.utils.qthelpers import qapplication - _app = qapplication() - print(repr(getexistingdirectory())) - print(repr(getopenfilename(filters='*.py;;*.txt'))) - print(repr(getopenfilenames(filters='*.py;;*.txt'))) - print(repr(getsavefilename(filters='*.py;;*.txt'))) - sys.exit() diff -Nru spyder-2.3.8+dfsg1/spyderlib/qt/__init__.py spyder-3.0.2+dfsg1/spyderlib/qt/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/qt/__init__.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/qt/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Transitional package (PyQt4 --> PySide)""" - -import os - -os.environ.setdefault('QT_API', 'pyqt') -assert os.environ['QT_API'] in ('pyqt', 'pyside') - -API = os.environ['QT_API'] -API_NAME = {'pyqt': 'PyQt4', 'pyside': 'PySide'}[API] - -if API == 'pyqt': - # Since Spyder 2.3.6 we only support API #2 - try: - import sip - try: - sip.setapi('QString', 2) - sip.setapi('QVariant', 2) - sip.setapi('QDate', 2) - sip.setapi('QDateTime', 2) - sip.setapi('QTextStream', 2) - sip.setapi('QTime', 2) - sip.setapi('QUrl', 2) - except AttributeError: - pass - - from PyQt4.QtCore import PYQT_VERSION_STR as __version__ - except ImportError: # May fail on sip or on PyQt4 import - # Switching to PySide - API = os.environ['QT_API'] = 'pyside' - API_NAME = 'PySide' - else: - is_old_pyqt = __version__.startswith(('4.4', '4.5', '4.6', '4.7')) - is_pyqt46 = __version__.startswith('4.6') - import sip - try: - API_NAME += (" (API v%d)" % sip.getapi('QString')) - except AttributeError: - pass - -if API == 'pyside': - try: - from PySide import __version__ # analysis:ignore - except ImportError: - raise ImportError("Spyder requires PySide or PyQt to be installed") - else: - is_old_pyqt = is_pyqt46 = False diff -Nru spyder-2.3.8+dfsg1/spyderlib/qt/QtCore.py spyder-3.0.2+dfsg1/spyderlib/qt/QtCore.py --- spyder-2.3.8+dfsg1/spyderlib/qt/QtCore.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/qt/QtCore.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -import os - -if os.environ['QT_API'] == 'pyqt': - from PyQt4.QtCore import * # analysis:ignore - from PyQt4.QtCore import QCoreApplication # analysis:ignore - from PyQt4.QtCore import Qt # analysis:ignore - from PyQt4.QtCore import pyqtSignal as Signal # analysis:ignore - from PyQt4.QtCore import pyqtSlot as Slot # analysis:ignore - from PyQt4.QtCore import pyqtProperty as Property # analysis:ignore - from PyQt4.QtCore import QT_VERSION_STR as __version__ -else: - import PySide.QtCore - __version__ = PySide.QtCore.__version__ # analysis:ignore - from PySide.QtCore import * # analysis:ignore diff -Nru spyder-2.3.8+dfsg1/spyderlib/qt/QtGui.py spyder-3.0.2+dfsg1/spyderlib/qt/QtGui.py --- spyder-2.3.8+dfsg1/spyderlib/qt/QtGui.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/qt/QtGui.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -import os - -if os.environ['QT_API'] == 'pyqt': - from PyQt4.Qt import QKeySequence, QTextCursor # analysis:ignore - from PyQt4.QtGui import * # analysis:ignore -else: - from PySide.QtGui import * # analysis:ignore diff -Nru spyder-2.3.8+dfsg1/spyderlib/qt/QtSvg.py spyder-3.0.2+dfsg1/spyderlib/qt/QtSvg.py --- spyder-2.3.8+dfsg1/spyderlib/qt/QtSvg.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/qt/QtSvg.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -import os - -if os.environ['QT_API'] == 'pyqt': - from PyQt4.QtSvg import * # analysis:ignore -else: - from PySide.QtSvg import * # analysis:ignore \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/qt/QtWebKit.py spyder-3.0.2+dfsg1/spyderlib/qt/QtWebKit.py --- spyder-2.3.8+dfsg1/spyderlib/qt/QtWebKit.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/qt/QtWebKit.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -import os - -if os.environ['QT_API'] == 'pyqt': - from PyQt4.QtWebKit import * # analysis:ignore -else: - from PySide.QtWebKit import * # analysis:ignore \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/requirements.py spyder-3.0.2+dfsg1/spyderlib/requirements.py --- spyder-2.3.8+dfsg1/spyderlib/requirements.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/requirements.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2013 Pierre Raybaut -# © 2012-2014 anatoly techtonik -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Module checking Spyder installation requirements""" - -import sys -import os.path as osp -from distutils.version import LooseVersion - - -def show_warning(message): - """Show warning using Tkinter if available""" - try: - # If Tkinter is installed (highly probable), showing an error pop-up - import Tkinter, tkMessageBox - root = Tkinter.Tk() - root.withdraw() - tkMessageBox.showerror("Spyder", message) - except ImportError: - pass - raise RuntimeError(message) - -def check_path(): - """Check sys.path: is Spyder properly installed?""" - dirname = osp.abspath(osp.join(osp.dirname(__file__), osp.pardir)) - if dirname not in sys.path: - show_warning("Spyder must be installed properly " - "(e.g. from source: 'python setup.py install'),\n" - "or directory '%s' must be in PYTHONPATH " - "environment variable." % dirname) - -def check_qt(): - """Check Qt binding requirements""" - qt_infos = dict(pyqt=("PyQt4", "4.6"), pyside=("PySide", "1.2.0")) - try: - from spyderlib import qt - package_name, required_ver = qt_infos[qt.API] - actual_ver = qt.__version__ - if LooseVersion(actual_ver) < LooseVersion(required_ver): - show_warning("Please check Spyder installation requirements:\n" - "%s %s+ is required (found v%s)." - % (package_name, required_ver, actual_ver)) - except ImportError: - show_warning("Please check Spyder installation requirements:\n" - "%s %s+ (or %s %s+) is required." - % (qt_infos['pyqt']+qt_infos['pyside'])) diff -Nru spyder-2.3.8+dfsg1/spyderlib/rope_patch.py spyder-3.0.2+dfsg1/spyderlib/rope_patch.py --- spyder-2.3.8+dfsg1/spyderlib/rope_patch.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/rope_patch.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,210 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Patching rope: - -[1] For compatibility with Spyder's standalone version, built with py2exe or - cx_Freeze - -[2] For better performance, see this thread: - http://groups.google.com/group/rope-dev/browse_thread/thread/57de5731f202537a - -[3] To avoid considering folders without __init__.py as Python packages, thus - avoiding side effects as non-working introspection features on a Python - module or package when a folder in current directory has the same name. - See this thread: - http://groups.google.com/group/rope-dev/browse_thread/thread/924c4b5a6268e618 - -[4] To avoid rope adding a 2 spaces indent to every docstring it gets, because - it breaks the work of Sphinx on the Object Inspector. Also, to better - control how to get calltips and docstrings of forced builtin objects. - -[5] To make matplotlib return its docstrings in proper rst, instead of a mix - of rst and plain text. -""" - -def apply(): - """Monkey patching rope - - See [1], [2], [3], [4] and [5] in module docstring.""" - import rope - if rope.VERSION not in ('0.10.2', '0.9.4-1', '0.9.4', '0.9.3', '0.9.2'): - raise ImportError("rope %s can't be patched" % rope.VERSION) - - # [1] Patching project.Project for compatibility with py2exe/cx_Freeze - # distributions - from spyderlib.baseconfig import is_py2exe_or_cx_Freeze - if is_py2exe_or_cx_Freeze(): - from rope.base import project - class PatchedProject(project.Project): - def _default_config(self): - # py2exe/cx_Freeze distribution - from spyderlib.baseconfig import get_module_source_path - fname = get_module_source_path('spyderlib', - 'default_config.py') - return open(fname, 'rb').read() - project.Project = PatchedProject - - # Patching pycore.PyCore... - from rope.base import pycore - class PatchedPyCore(pycore.PyCore): - # [2] ...so that forced builtin modules (i.e. modules that were - # declared as 'extension_modules' in rope preferences) will be indeed - # recognized as builtins by rope, as expected - # - # This patch is included in rope 0.9.4+ but applying it anyway is ok - def get_module(self, name, folder=None): - """Returns a `PyObject` if the module was found.""" - # check if this is a builtin module - pymod = self._builtin_module(name) - if pymod is not None: - return pymod - module = self.find_module(name, folder) - if module is None: - raise pycore.ModuleNotFoundError( - 'Module %s not found' % name) - return self.resource_to_pyobject(module) - # [3] ...to avoid considering folders without __init__.py as Python - # packages - def _find_module_in_folder(self, folder, modname): - module = folder - packages = modname.split('.') - for pkg in packages[:-1]: - if module.is_folder() and module.has_child(pkg): - module = module.get_child(pkg) - else: - return None - if module.is_folder(): - if module.has_child(packages[-1]) and \ - module.get_child(packages[-1]).is_folder() and \ - module.get_child(packages[-1]).has_child('__init__.py'): - return module.get_child(packages[-1]) - elif module.has_child(packages[-1] + '.py') and \ - not module.get_child(packages[-1] + '.py').is_folder(): - return module.get_child(packages[-1] + '.py') - pycore.PyCore = PatchedPyCore - - # [2] Patching BuiltinName for the go to definition feature to simply work - # with forced builtins - from rope.base import builtins, libutils, pyobjects - import inspect - import os.path as osp - class PatchedBuiltinName(builtins.BuiltinName): - def _pycore(self): - p = self.pyobject - while p.parent is not None: - p = p.parent - if isinstance(p, builtins.BuiltinModule) and p.pycore is not None: - return p.pycore - def get_definition_location(self): - if not inspect.isbuiltin(self.pyobject): - _lines, lineno = inspect.getsourcelines(self.pyobject.builtin) - path = inspect.getfile(self.pyobject.builtin) - if path.endswith('pyc') and osp.isfile(path[:-1]): - path = path[:-1] - pycore = self._pycore() - if pycore and pycore.project: - resource = libutils.path_to_resource(pycore.project, path) - module = pyobjects.PyModule(pycore, None, resource) - return (module, lineno) - return (None, None) - builtins.BuiltinName = PatchedBuiltinName - - # [4] Patching several PyDocExtractor methods: - # 1. get_doc: - # To force rope to return the docstring of any object which has one, even - # if it's not an instance of AbstractFunction, AbstractClass, or - # AbstractModule. - # Also, to use utils.dochelpers.getdoc to get docs from forced builtins. - # - # 2. _get_class_docstring and _get_single_function_docstring: - # To not let rope add a 2 spaces indentation to every docstring, which was - # breaking our rich text mode. The only value that we are modifying is the - # 'indents' keyword of those methods, from 2 to 0. - # - # 3. get_calltip - # To easily get calltips of forced builtins - from rope.contrib import codeassist - from spyderlib.utils.dochelpers import getdoc - from rope.base import exceptions - class PatchedPyDocExtractor(codeassist.PyDocExtractor): - def get_builtin_doc(self, pyobject): - buitin = pyobject.builtin - return getdoc(buitin) - - def get_doc(self, pyobject): - if hasattr(pyobject, 'builtin'): - doc = self.get_builtin_doc(pyobject) - return doc - elif isinstance(pyobject, builtins.BuiltinModule): - docstring = pyobject.get_doc() - if docstring is not None: - docstring = self._trim_docstring(docstring) - else: - docstring = '' - # TODO: Add a module_name key, so that the name could appear - # on the OI text filed but not be used by sphinx to render - # the page - doc = {'name': '', - 'argspec': '', - 'note': '', - 'docstring': docstring - } - return doc - elif isinstance(pyobject, pyobjects.AbstractFunction): - return self._get_function_docstring(pyobject) - elif isinstance(pyobject, pyobjects.AbstractClass): - return self._get_class_docstring(pyobject) - elif isinstance(pyobject, pyobjects.AbstractModule): - return self._trim_docstring(pyobject.get_doc()) - elif pyobject.get_doc() is not None: # Spyder patch - return self._trim_docstring(pyobject.get_doc()) - return None - - def get_calltip(self, pyobject, ignore_unknown=False, remove_self=False): - if hasattr(pyobject, 'builtin'): - doc = self.get_builtin_doc(pyobject) - return doc['name'] + doc['argspec'] - try: - if isinstance(pyobject, pyobjects.AbstractClass): - pyobject = pyobject['__init__'].get_object() - if not isinstance(pyobject, pyobjects.AbstractFunction): - pyobject = pyobject['__call__'].get_object() - except exceptions.AttributeNotFoundError: - return None - if ignore_unknown and not isinstance(pyobject, pyobjects.PyFunction): - return - if isinstance(pyobject, pyobjects.AbstractFunction): - result = self._get_function_signature(pyobject, add_module=True) - if remove_self and self._is_method(pyobject): - return result.replace('(self)', '()').replace('(self, ', '(') - return result - - def _get_class_docstring(self, pyclass): - contents = self._trim_docstring(pyclass.get_doc(), indents=0) - supers = [super.get_name() for super in pyclass.get_superclasses()] - doc = 'class %s(%s):\n\n' % (pyclass.get_name(), ', '.join(supers)) + contents - - if '__init__' in pyclass: - init = pyclass['__init__'].get_object() - if isinstance(init, pyobjects.AbstractFunction): - doc += '\n\n' + self._get_single_function_docstring(init) - return doc - - def _get_single_function_docstring(self, pyfunction): - docs = pyfunction.get_doc() - docs = self._trim_docstring(docs, indents=0) - return docs - codeassist.PyDocExtractor = PatchedPyDocExtractor - - - # [5] Get the right matplotlib docstrings for our Object Inspector - try: - import matplotlib as mpl - mpl.rcParams['docstring.hardcopy'] = True - except ImportError: - pass diff -Nru spyder-2.3.8+dfsg1/spyderlib/scientific_startup.py spyder-3.0.2+dfsg1/spyderlib/scientific_startup.py --- spyder-2.3.8+dfsg1/spyderlib/scientific_startup.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/scientific_startup.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Scientific Python startup script - -Requires NumPy, SciPy and Matplotlib -""" - -# Need a temporary print function that is Python version agnostic. -import sys - -def exec_print(string="", end_space=False): - if sys.version[0] == '2': - if end_space: - exec("print '" + string + "',") - else: - exec("print '" + string + "'") - else: - if end_space: - exec("print('" + string + "', end=' ')") - else: - exec("print('" + string + "')") - -__has_numpy = True -__has_scipy = True -__has_matplotlib = True - -#============================================================================== -# Pollute the namespace but also provide MATLAB-like experience -#============================================================================== -try: - from pylab import * #analysis:ignore - # Enable Matplotlib's interactive mode: - ion() -except ImportError: - pass - -# Import modules following official guidelines: -try: - import numpy as np -except ImportError: - __has_numpy = False - -try: - import scipy as sp -except ImportError: - __has_scipy = False - -try: - import matplotlib as mpl - import matplotlib.pyplot as plt #analysis:ignore -except ImportError: - __has_matplotlib = False - -#============================================================================== -# Print what modules have been imported -#============================================================================== -__imports = "" -if __has_numpy: - __imports += "Imported NumPy %s" % np.__version__ -if __has_scipy: - __imports += ", SciPy %s" % sp.__version__ -if __has_matplotlib: - __imports += ", Matplotlib %s" % mpl.__version__ - -exec_print("") -if __imports: - exec_print(__imports) - -import os -if os.environ.get('QT_API') != 'pyside': - try: - import guiqwt - import guiqwt.pyplot as plt_ - import guidata - plt_.ion() - exec_print("+ guidata %s, guiqwt %s" % (guidata.__version__, - guiqwt.__version__)) - except ImportError: - exec_print() - -#============================================================================== -# Add help about the "scientific" command -#============================================================================== -def setscientific(): - """Set 'scientific' in __builtin__""" - infos = "" - - if __has_numpy: - infos += """ -This is a standard Python interpreter with preloaded tools for scientific -computing and visualization. It tries to import the following modules: - ->>> import numpy as np # NumPy (multidimensional arrays, linear algebra, ...)""" - - if __has_scipy: - infos += """ ->>> import scipy as sp # SciPy (signal and image processing library)""" - - if __has_matplotlib: - infos += """ ->>> import matplotlib as mpl # Matplotlib (2D/3D plotting library) ->>> import matplotlib.pyplot as plt # Matplotlib's pyplot: MATLAB-like syntax ->>> from pylab import * # Matplotlib's pylab interface ->>> ion() # Turned on Matplotlib's interactive mode""" - - try: - import guiqwt #analysis:ignore - infos += """ ->>> import guidata # GUI generation for easy dataset editing and display - ->>> import guiqwt # Efficient 2D data-plotting features ->>> import guiqwt.pyplot as plt_ # guiqwt's pyplot: MATLAB-like syntax ->>> plt_.ion() # Turned on guiqwt's interactive mode""" - except ImportError: - pass - - if __has_numpy: - infos += "\n" - - infos += """ -Within Spyder, this interpreter also provides: - * special commands (e.g. %ls, %cd, %pwd, %clear) - - %ls: List files in the current directory - - %cd dir: Change to directory dir - - %pwd: Show current directory - - %clear x: Remove variable x from namespace -""" - try: - # Python 2 - import __builtin__ as builtins - except ImportError: - # Python 3 - import builtins - try: - from site import _Printer - except ImportError: - # Python 3.4 - from _sitebuiltins import _Printer - builtins.scientific = _Printer("scientific", infos) - - -setscientific() -exec_print('Type "scientific" for more details.') - -#============================================================================== -# Delete temp vars -#============================================================================== -del setscientific, __has_numpy, __has_scipy, __has_matplotlib, __imports, exec_print diff -Nru spyder-2.3.8+dfsg1/spyderlib/spyder.py spyder-3.0.2+dfsg1/spyderlib/spyder.py --- spyder-2.3.8+dfsg1/spyderlib/spyder.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/spyder.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2360 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2013 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Spyder, the Scientific PYthon Development EnviRonment -===================================================== - -Developped and maintained by the Spyder Development -Team - -Copyright © 2009 - 2015 Pierre Raybaut -Copyright © 2010 - 2015 The Spyder Development Team -Licensed under the terms of the MIT License -(see spyderlib/__init__.py for details) -""" - -from __future__ import print_function - - -#============================================================================== -# Stdlib imports -#============================================================================== -import atexit -import errno -import os -import os.path as osp -import re -import socket -import shutil -import sys -import threading - - -#============================================================================== -# Keeping a reference to the original sys.exit before patching it -#============================================================================== -ORIGINAL_SYS_EXIT = sys.exit - - -#============================================================================== -# Check requirements -#============================================================================== -from spyderlib import requirements -requirements.check_path() -requirements.check_qt() - - -#============================================================================== -# Windows platforms only: support for hiding the attached console window -#============================================================================== -set_attached_console_visible = None -is_attached_console_visible = None -set_windows_appusermodelid = None -if os.name == 'nt': - from spyderlib.utils.windows import (set_attached_console_visible, - is_attached_console_visible, - set_windows_appusermodelid) - - -#============================================================================== -# Workaround: importing rope.base.project here, otherwise this module can't -# be imported if Spyder was executed from another folder than spyderlib -#============================================================================== -try: - import rope.base.project # analysis:ignore -except ImportError: - pass - - -#============================================================================== -# Don't show IPython ShimWarning's to our users -# TODO: Move to Jupyter imports in 3.1 -#============================================================================== -try: - import warnings - from IPython.utils.shimmodule import ShimWarning - warnings.simplefilter('ignore', ShimWarning) -except: - pass - - -#============================================================================== -# Qt imports -#============================================================================== -from spyderlib.qt.QtGui import (QApplication, QMainWindow, QSplashScreen, - QPixmap, QMessageBox, QMenu, QColor, QShortcut, - QKeySequence, QDockWidget, QAction, - QDesktopServices, QStyleFactory) -from spyderlib.qt.QtCore import (SIGNAL, QPoint, Qt, QSize, QByteArray, QUrl, - QCoreApplication) -from spyderlib.qt.compat import (from_qvariant, getopenfilename, - getsavefilename) -# Avoid a "Cannot mix incompatible Qt library" error on Windows platforms -# when PySide is selected by the QT_API environment variable and when PyQt4 -# is also installed (or any other Qt-based application prepending a directory -# containing incompatible Qt DLLs versions in PATH): -from spyderlib.qt import QtSvg # analysis:ignore - - -#============================================================================== -# Create our QApplication instance here because it's needed to render the -# splash screen created below -#============================================================================== -from spyderlib.utils.qthelpers import qapplication -MAIN_APP = qapplication() - - -#============================================================================== -# Create splash screen out of MainWindow to reduce perceived startup time. -#============================================================================== -from spyderlib.baseconfig import _, get_image_path -SPLASH = QSplashScreen(QPixmap(get_image_path('splash.png'), 'png')) -SPLASH_FONT = SPLASH.font() -SPLASH_FONT.setPixelSize(10) -SPLASH.setFont(SPLASH_FONT) -SPLASH.show() -SPLASH.showMessage(_("Initializing..."), Qt.AlignBottom | Qt.AlignCenter | - Qt.AlignAbsolute, QColor(Qt.white)) -QApplication.processEvents() - - -#============================================================================== -# Local utility imports -#============================================================================== -from spyderlib import __version__, __project_url__, __forum_url__, get_versions -from spyderlib.baseconfig import (get_conf_path, get_module_data_path, - get_module_source_path, STDERR, DEBUG, DEV, - debug_print, TEST, SUBFOLDER, MAC_APP_NAME, - running_in_mac_app) -from spyderlib.config import (CONF, EDIT_EXT, IMPORT_EXT, OPEN_FILES_PORT, - is_gtk_desktop) -from spyderlib.cli_options import get_options -from spyderlib import dependencies -from spyderlib.ipythonconfig import IPYTHON_QT_INSTALLED -from spyderlib.userconfig import NoDefault -from spyderlib.utils import encoding, programs -from spyderlib.utils.iofuncs import load_session, save_session, reset_session -from spyderlib.utils.programs import is_module_installed -from spyderlib.utils.introspection import module_completion -from spyderlib.utils.misc import select_port -from spyderlib.py3compat import (PY3, to_text_string, is_text_string, getcwd, - u, qbytearray_to_str, configparser as cp) - - -#============================================================================== -# Local gui imports -#============================================================================== -# NOTE: Move (if possible) import's of widgets and plugins exactly where they -# are needed in MainWindow to speed up perceived startup time (i.e. the time -# from clicking the Spyder icon to showing the splash screen). -try: - from spyderlib.utils.environ import WinUserEnvDialog -except ImportError: - WinUserEnvDialog = None # analysis:ignore - -from spyderlib.utils.qthelpers import (create_action, add_actions, get_icon, - get_std_icon, add_shortcut_to_tooltip, - create_module_bookmark_actions, - create_bookmark_action, - create_program_action, DialogManager, - keybinding, create_python_script_action, - file_uri) -from spyderlib.guiconfig import get_shortcut, remove_deprecated_shortcuts -from spyderlib.otherplugins import get_spyderplugins_mods - - -#============================================================================== -# To save and load temp sessions -#============================================================================== -TEMP_SESSION_PATH = get_conf_path('temp.session.tar') - - -#============================================================================== -# Get the cwd before initializing WorkingDirectory, which sets it to the one -# used in the last session -#============================================================================== -CWD = getcwd() - - -#============================================================================== -# Spyder's main window widgets utilities -#============================================================================== -def get_python_doc_path(): - """ - Return Python documentation path - (Windows: return the PythonXX.chm path if available) - """ - if os.name == 'nt': - doc_path = osp.join(sys.prefix, "Doc") - if not osp.isdir(doc_path): - return - python_chm = [path for path in os.listdir(doc_path) - if re.match(r"(?i)Python[0-9]{3}.chm", path)] - if python_chm: - return file_uri(osp.join(doc_path, python_chm[0])) - else: - vinf = sys.version_info - doc_path = '/usr/share/doc/python%d.%d/html' % (vinf[0], vinf[1]) - python_doc = osp.join(doc_path, "index.html") - if osp.isfile(python_doc): - return file_uri(python_doc) - - -def get_focus_python_shell(): - """Extract and return Python shell from widget - Return None if *widget* is not a Python shell (e.g. IPython kernel)""" - widget = QApplication.focusWidget() - from spyderlib.widgets.shell import PythonShellWidget - from spyderlib.widgets.externalshell.pythonshell import ExternalPythonShell - if isinstance(widget, PythonShellWidget): - return widget - elif isinstance(widget, ExternalPythonShell): - return widget.shell - - -def get_focus_widget_properties(): - """Get properties of focus widget - Returns tuple (widget, properties) where properties is a tuple of - booleans: (is_console, not_readonly, readwrite_editor)""" - widget = QApplication.focusWidget() - from spyderlib.widgets.shell import ShellBaseWidget - from spyderlib.widgets.editor import TextEditBaseWidget - textedit_properties = None - if isinstance(widget, (ShellBaseWidget, TextEditBaseWidget)): - console = isinstance(widget, ShellBaseWidget) - not_readonly = not widget.isReadOnly() - readwrite_editor = not_readonly and not console - textedit_properties = (console, not_readonly, readwrite_editor) - return widget, textedit_properties - - -#============================================================================== -# Main Window -#============================================================================== -class MainWindow(QMainWindow): - """Spyder main window""" - DOCKOPTIONS = QMainWindow.AllowTabbedDocks|QMainWindow.AllowNestedDocks - SPYDER_PATH = get_conf_path('path') - BOOKMARKS = ( - ('numpy', "http://docs.scipy.org/doc/", - _("Numpy and Scipy documentation")), - ('matplotlib', "http://matplotlib.sourceforge.net/contents.html", - _("Matplotlib documentation")), - ('PyQt4', - "http://pyqt.sourceforge.net/Docs/PyQt4/", - _("PyQt4 Reference Guide")), - ('PyQt4', - "http://pyqt.sourceforge.net/Docs/PyQt4/classes.html", - _("PyQt4 API Reference")), - ('xy', "http://code.google.com/p/pythonxy/", - _("Python(x,y)")), - ('winpython', "https://winpython.github.io/", - _("WinPython")) - ) - - def __init__(self, options=None): - QMainWindow.__init__(self) - - qapp = QApplication.instance() - self.default_style = str(qapp.style().objectName()) - - self.dialog_manager = DialogManager() - - self.init_workdir = options.working_directory - self.profile = options.profile - self.multithreaded = options.multithreaded - self.light = options.light - self.new_instance = options.new_instance - - self.debug_print("Start of MainWindow constructor") - - # Use a custom Qt stylesheet - if sys.platform == 'darwin': - spy_path = get_module_source_path('spyderlib') - mac_style = open(osp.join(spy_path, 'mac_stylesheet.qss')).read() - self.setStyleSheet(mac_style) - - # Shortcut management data - self.shortcut_data = [] - - # Loading Spyder path - self.path = [] - self.project_path = [] - if osp.isfile(self.SPYDER_PATH): - self.path, _x = encoding.readlines(self.SPYDER_PATH) - self.path = [name for name in self.path if osp.isdir(name)] - self.remove_path_from_sys_path() - self.add_path_to_sys_path() - self.load_temp_session_action = create_action(self, - _("Reload last session"), - triggered=lambda: - self.load_session(TEMP_SESSION_PATH)) - self.load_session_action = create_action(self, - _("Load session..."), - None, 'fileopen.png', - triggered=self.load_session, - tip=_("Load Spyder session")) - self.save_session_action = create_action(self, - _("Save session and quit..."), - None, 'filesaveas.png', - triggered=self.save_session, - tip=_("Save current session " - "and quit application")) - - # Plugins - self.console = None - self.workingdirectory = None - self.editor = None - self.explorer = None - self.inspector = None - self.onlinehelp = None - self.projectexplorer = None - self.outlineexplorer = None - self.historylog = None - self.extconsole = None - self.ipyconsole = None - self.variableexplorer = None - self.findinfiles = None - self.thirdparty_plugins = [] - - # Preferences - from spyderlib.plugins.configdialog import (MainConfigPage, - ColorSchemeConfigPage) - from spyderlib.plugins.shortcuts import ShortcutsConfigPage - from spyderlib.plugins.runconfig import RunConfigPage - self.general_prefs = [MainConfigPage, ShortcutsConfigPage, - ColorSchemeConfigPage, RunConfigPage] - self.prefs_index = None - self.prefs_dialog_size = None - - # Actions - self.close_dockwidget_action = None - self.find_action = None - self.find_next_action = None - self.find_previous_action = None - self.replace_action = None - self.undo_action = None - self.redo_action = None - self.copy_action = None - self.cut_action = None - self.paste_action = None - self.delete_action = None - self.selectall_action = None - self.maximize_action = None - self.fullscreen_action = None - - # Menu bars - self.file_menu = None - self.file_menu_actions = [] - self.edit_menu = None - self.edit_menu_actions = [] - self.search_menu = None - self.search_menu_actions = [] - self.source_menu = None - self.source_menu_actions = [] - self.run_menu = None - self.run_menu_actions = [] - self.debug_menu = None - self.debug_menu_actions = [] - self.consoles_menu = None - self.consoles_menu_actions = [] - self.tools_menu = None - self.tools_menu_actions = [] - self.external_tools_menu = None # We must keep a reference to this, - # otherwise the external tools menu is lost after leaving setup method - self.external_tools_menu_actions = [] - self.view_menu = None - self.plugins_menu = None - self.toolbars_menu = None - self.help_menu = None - self.help_menu_actions = [] - - # Status bar widgets - self.mem_status = None - self.cpu_status = None - - # Toolbars - self.toolbarslist = [] - self.main_toolbar = None - self.main_toolbar_actions = [] - self.file_toolbar = None - self.file_toolbar_actions = [] - self.edit_toolbar = None - self.edit_toolbar_actions = [] - self.search_toolbar = None - self.search_toolbar_actions = [] - self.source_toolbar = None - self.source_toolbar_actions = [] - self.run_toolbar = None - self.run_toolbar_actions = [] - self.debug_toolbar = None - self.debug_toolbar_actions = [] - - # Set Window title and icon - if DEV is not None: - title = "Spyder %s (Python %s.%s)" % (__version__, - sys.version_info[0], - sys.version_info[1]) - else: - title = "Spyder (Python %s.%s)" % (sys.version_info[0], - sys.version_info[1]) - if DEBUG: - title += " [DEBUG MODE %d]" % DEBUG - self.setWindowTitle(title) - icon_name = 'spyder_light.svg' if self.light else 'spyder.svg' - # Resampling SVG icon only on non-Windows platforms (see Issue 1314): - self.setWindowIcon(get_icon(icon_name, resample=os.name != 'nt')) - if set_windows_appusermodelid != None: - res = set_windows_appusermodelid() - debug_print("appusermodelid: " + str(res)) - - # Showing splash screen - self.splash = SPLASH - if not self.light: - if CONF.get('main', 'current_version', '') != __version__: - CONF.set('main', 'current_version', __version__) - # Execute here the actions to be performed only once after - # each update (there is nothing there for now, but it could - # be useful some day...) - - # List of satellite widgets (registered in add_dockwidget): - self.widgetlist = [] - - # Flags used if closing() is called by the exit() shell command - self.already_closed = False - self.is_starting_up = True - self.is_setting_up = True - - self.floating_dockwidgets = [] - self.window_size = None - self.window_position = None - self.state_before_maximizing = None - self.current_quick_layout = None - self.previous_layout_settings = None - self.last_plugin = None - self.fullscreen_flag = None # isFullscreen does not work as expected - # The following flag remember the maximized state even when - # the window is in fullscreen mode: - self.maximized_flag = None - - # Session manager - self.next_session_name = None - self.save_session_name = None - - # Track which console plugin type had last focus - # True: Console plugin - # False: IPython console plugin - self.last_console_plugin_focus_was_python = True - - # To keep track of the last focused widget - self.last_focused_widget = None - - # Server to open external files on a single instance - self.open_files_server = socket.socket(socket.AF_INET, - socket.SOCK_STREAM, - socket.IPPROTO_TCP) - - self.apply_settings() - self.debug_print("End of MainWindow constructor") - - def debug_print(self, message): - """Debug prints""" - debug_print(message) - - #---- Window setup - def create_toolbar(self, title, object_name, iconsize=24): - """Create and return toolbar with *title* and *object_name*""" - toolbar = self.addToolBar(title) - toolbar.setObjectName(object_name) - toolbar.setIconSize( QSize(iconsize, iconsize) ) - self.toolbarslist.append(toolbar) - return toolbar - - def setup(self): - """Setup main window""" - self.debug_print("*** Start of MainWindow setup ***") - if not self.light: - self.debug_print(" ..core actions") - self.close_dockwidget_action = create_action(self, - _("Close current pane"), - triggered=self.close_current_dockwidget, - context=Qt.ApplicationShortcut) - self.register_shortcut(self.close_dockwidget_action, "_", - "Close pane") - - _text = _("&Find text") - self.find_action = create_action(self, _text, icon='find.png', - tip=_text, triggered=self.find, - context=Qt.WidgetShortcut) - self.register_shortcut(self.find_action, "Editor", "Find text") - self.find_next_action = create_action(self, _("Find &next"), - icon='findnext.png', triggered=self.find_next, - context=Qt.WidgetShortcut) - self.register_shortcut(self.find_next_action, "Editor", - "Find next") - self.find_previous_action = create_action(self, - _("Find &previous"), - icon='findprevious.png', triggered=self.find_previous, - context=Qt.WidgetShortcut) - self.register_shortcut(self.find_previous_action, "Editor", - "Find previous") - _text = _("&Replace text") - self.replace_action = create_action(self, _text, icon='replace.png', - tip=_text, triggered=self.replace, - context=Qt.WidgetShortcut) - self.register_shortcut(self.replace_action, "Editor", - "Replace text") - def create_edit_action(text, tr_text, icon_name): - textseq = text.split(' ') - method_name = textseq[0].lower()+"".join(textseq[1:]) - return create_action(self, tr_text, - shortcut=keybinding(text.replace(' ', '')), - icon=get_icon(icon_name), - triggered=self.global_callback, - data=method_name, - context=Qt.WidgetShortcut) - self.undo_action = create_edit_action("Undo", _("Undo"), - 'undo.png') - self.redo_action = create_edit_action("Redo", _("Redo"), 'redo.png') - self.copy_action = create_edit_action("Copy", _("Copy"), - 'editcopy.png') - self.cut_action = create_edit_action("Cut", _("Cut"), 'editcut.png') - self.paste_action = create_edit_action("Paste", _("Paste"), - 'editpaste.png') - self.delete_action = create_edit_action("Delete", _("Delete"), - 'editdelete.png') - self.selectall_action = create_edit_action("Select All", - _("Select All"), - 'selectall.png') - self.edit_menu_actions = [self.undo_action, self.redo_action, - None, self.cut_action, self.copy_action, - self.paste_action, self.delete_action, - None, self.selectall_action] - self.search_menu_actions = [self.find_action, self.find_next_action, - self.find_previous_action, - self.replace_action] - self.search_toolbar_actions = [self.find_action, - self.find_next_action, - self.replace_action] - - namespace = None - if not self.light: - self.debug_print(" ..toolbars") - # File menu/toolbar - self.file_menu = self.menuBar().addMenu(_("&File")) - self.connect(self.file_menu, SIGNAL("aboutToShow()"), - self.update_file_menu) - self.file_toolbar = self.create_toolbar(_("File toolbar"), - "file_toolbar") - - # Edit menu/toolbar - self.edit_menu = self.menuBar().addMenu(_("&Edit")) - self.edit_toolbar = self.create_toolbar(_("Edit toolbar"), - "edit_toolbar") - - # Search menu/toolbar - self.search_menu = self.menuBar().addMenu(_("&Search")) - self.search_toolbar = self.create_toolbar(_("Search toolbar"), - "search_toolbar") - - # Source menu/toolbar - self.source_menu = self.menuBar().addMenu(_("Sour&ce")) - self.source_toolbar = self.create_toolbar(_("Source toolbar"), - "source_toolbar") - - # Run menu/toolbar - self.run_menu = self.menuBar().addMenu(_("&Run")) - self.run_toolbar = self.create_toolbar(_("Run toolbar"), - "run_toolbar") - - # Debug menu/toolbar - self.debug_menu = self.menuBar().addMenu(_("&Debug")) - self.debug_toolbar = self.create_toolbar(_("Debug toolbar"), - "debug_toolbar") - - # Consoles menu/toolbar - self.consoles_menu = self.menuBar().addMenu(_("C&onsoles")) - - # Tools menu - self.tools_menu = self.menuBar().addMenu(_("&Tools")) - - # View menu - self.view_menu = self.menuBar().addMenu(_("&View")) - - # Help menu - self.help_menu = self.menuBar().addMenu(_("&Help")) - - # Status bar - status = self.statusBar() - status.setObjectName("StatusBar") - status.showMessage(_("Welcome to Spyder!"), 5000) - - - self.debug_print(" ..tools") - # Tools + External Tools - prefs_action = create_action(self, _("Pre&ferences"), - icon='configure.png', - triggered=self.edit_preferences) - self.register_shortcut(prefs_action, "_", "Preferences") - add_shortcut_to_tooltip(prefs_action, context="_", - name="Preferences") - spyder_path_action = create_action(self, - _("PYTHONPATH manager"), - None, 'pythonpath_mgr.png', - triggered=self.path_manager_callback, - tip=_("Python Path Manager"), - menurole=QAction.ApplicationSpecificRole) - update_modules_action = create_action(self, - _("Update module names list"), - triggered=module_completion.reset, - tip=_("Refresh list of module names " - "available in PYTHONPATH")) - self.tools_menu_actions = [prefs_action, spyder_path_action] - if WinUserEnvDialog is not None: - winenv_action = create_action(self, - _("Current user environment variables..."), - icon='win_env.png', - tip=_("Show and edit current user environment " - "variables in Windows registry " - "(i.e. for all sessions)"), - triggered=self.win_env) - self.tools_menu_actions.append(winenv_action) - self.tools_menu_actions += [None, update_modules_action] - - # External Tools submenu - self.external_tools_menu = QMenu(_("External Tools")) - self.external_tools_menu_actions = [] - # Python(x,y) launcher - self.xy_action = create_action(self, - _("Python(x,y) launcher"), - icon=get_icon('pythonxy.png'), - triggered=lambda: - programs.run_python_script('xy', 'xyhome')) - if os.name == 'nt' and is_module_installed('xy'): - self.external_tools_menu_actions.append(self.xy_action) - # WinPython control panel - self.wp_action = create_action(self, _("WinPython control panel"), - icon=get_icon('winpython.svg'), - triggered=lambda: - programs.run_python_script('winpython', 'controlpanel')) - if os.name == 'nt' and is_module_installed('winpython'): - self.external_tools_menu_actions.append(self.wp_action) - # Qt-related tools - additact = [] - for name in ("designer-qt4", "designer"): - qtdact = create_program_action(self, _("Qt Designer"), - name, 'qtdesigner.png') - if qtdact: - break - for name in ("linguist-qt4", "linguist"): - qtlact = create_program_action(self, _("Qt Linguist"), - "linguist", 'qtlinguist.png') - if qtlact: - break - args = ['-no-opengl'] if os.name == 'nt' else [] - qteact = create_python_script_action(self, - _("Qt examples"), 'qt.png', "PyQt4", - osp.join("examples", "demos", - "qtdemo", "qtdemo"), args) - for act in (qtdact, qtlact, qteact): - if act: - additact.append(act) - if additact and (is_module_installed('winpython') or \ - is_module_installed('xy')): - self.external_tools_menu_actions += [None] + additact - - # Guidata and Sift - self.debug_print(" ..sift?") - gdgq_act = [] - if is_module_installed('guidata'): - from guidata import configtools - from guidata import config # (loading icons) analysis:ignore - guidata_icon = configtools.get_icon('guidata.svg') - guidata_act = create_python_script_action(self, - _("guidata examples"), guidata_icon, "guidata", - osp.join("tests", "__init__")) - if guidata_act: - gdgq_act += [guidata_act] - if is_module_installed('guiqwt'): - from guiqwt import config # analysis:ignore - guiqwt_icon = configtools.get_icon('guiqwt.svg') - guiqwt_act = create_python_script_action(self, - _("guiqwt examples"), guiqwt_icon, "guiqwt", - osp.join("tests", "__init__")) - if guiqwt_act: - gdgq_act += [guiqwt_act] - sift_icon = configtools.get_icon('sift.svg') - sift_act = create_python_script_action(self, _("Sift"), - sift_icon, "guiqwt", osp.join("tests", "sift")) - if sift_act: - gdgq_act += [sift_act] - if gdgq_act: - self.external_tools_menu_actions += [None] + gdgq_act - - # ViTables - vitables_act = create_program_action(self, _("ViTables"), - "vitables", 'vitables.png') - if vitables_act: - self.external_tools_menu_actions += [None, vitables_act] - - # Maximize current plugin - self.maximize_action = create_action(self, '', - triggered=self.maximize_dockwidget) - self.register_shortcut(self.maximize_action, "_", - "Maximize pane") - self.__update_maximize_action() - - # Fullscreen mode - self.fullscreen_action = create_action(self, - _("Fullscreen mode"), - triggered=self.toggle_fullscreen) - self.register_shortcut(self.fullscreen_action, "_", - "Fullscreen mode") - add_shortcut_to_tooltip(self.fullscreen_action, context="_", - name="Fullscreen mode") - - # Main toolbar - self.main_toolbar_actions = [self.maximize_action, - self.fullscreen_action, None, - prefs_action, spyder_path_action] - - self.main_toolbar = self.create_toolbar(_("Main toolbar"), - "main_toolbar") - - # Internal console plugin - self.debug_print(" ..plugin: internal console") - from spyderlib.plugins.console import Console - self.console = Console(self, namespace, exitfunc=self.closing, - profile=self.profile, - multithreaded=self.multithreaded, - message=_("Spyder Internal Console\n\n" - "This console is used to report application\n" - "internal errors and to inspect Spyder\n" - "internals with the following commands:\n" - " spy.app, spy.window, dir(spy)\n\n" - "Please don't use it to run your code\n\n")) - self.console.register_plugin() - - # Working directory plugin - self.debug_print(" ..plugin: working directory") - from spyderlib.plugins.workingdirectory import WorkingDirectory - self.workingdirectory = WorkingDirectory(self, self.init_workdir) - self.workingdirectory.register_plugin() - self.toolbarslist.append(self.workingdirectory) - - # Object inspector plugin - if CONF.get('inspector', 'enable'): - self.set_splash(_("Loading object inspector...")) - from spyderlib.plugins.inspector import ObjectInspector - self.inspector = ObjectInspector(self) - self.inspector.register_plugin() - - # Outline explorer widget - if CONF.get('outline_explorer', 'enable'): - self.set_splash(_("Loading outline explorer...")) - from spyderlib.plugins.outlineexplorer import OutlineExplorer - fullpath_sorting = CONF.get('editor', 'fullpath_sorting', True) - self.outlineexplorer = OutlineExplorer(self, - fullpath_sorting=fullpath_sorting) - self.outlineexplorer.register_plugin() - - # Editor plugin - self.set_splash(_("Loading editor...")) - from spyderlib.plugins.editor import Editor - self.editor = Editor(self) - self.editor.register_plugin() - - # Populating file menu entries - quit_action = create_action(self, _("&Quit"), - icon='exit.png', tip=_("Quit"), - triggered=self.console.quit) - self.register_shortcut(quit_action, "_", "Quit") - self.file_menu_actions += [self.load_temp_session_action, - self.load_session_action, - self.save_session_action, - None, quit_action] - self.set_splash("") - - self.debug_print(" ..widgets") - # Find in files - if CONF.get('find_in_files', 'enable'): - from spyderlib.plugins.findinfiles import FindInFiles - self.findinfiles = FindInFiles(self) - self.findinfiles.register_plugin() - - # Explorer - if CONF.get('explorer', 'enable'): - self.set_splash(_("Loading file explorer...")) - from spyderlib.plugins.explorer import Explorer - self.explorer = Explorer(self) - self.explorer.register_plugin() - - # History log widget - if CONF.get('historylog', 'enable'): - self.set_splash(_("Loading history plugin...")) - from spyderlib.plugins.history import HistoryLog - self.historylog = HistoryLog(self) - self.historylog.register_plugin() - - # Online help widget - try: # Qt >= v4.4 - from spyderlib.plugins.onlinehelp import OnlineHelp - except ImportError: # Qt < v4.4 - OnlineHelp = None # analysis:ignore - if CONF.get('onlinehelp', 'enable') and OnlineHelp is not None: - self.set_splash(_("Loading online help...")) - self.onlinehelp = OnlineHelp(self) - self.onlinehelp.register_plugin() - - # Project explorer widget - if CONF.get('project_explorer', 'enable'): - self.set_splash(_("Loading project explorer...")) - from spyderlib.plugins.projectexplorer import ProjectExplorer - self.projectexplorer = ProjectExplorer(self) - self.projectexplorer.register_plugin() - - # External console - if self.light: - # This is necessary to support the --working-directory option: - if self.init_workdir is not None: - os.chdir(self.init_workdir) - else: - self.set_splash(_("Loading external console...")) - from spyderlib.plugins.externalconsole import ExternalConsole - self.extconsole = ExternalConsole(self, light_mode=self.light) - self.extconsole.register_plugin() - - # Namespace browser - if not self.light: - # In light mode, namespace browser is opened inside external console - # Here, it is opened as an independent plugin, in its own dockwidget - self.set_splash(_("Loading namespace browser...")) - from spyderlib.plugins.variableexplorer import VariableExplorer - self.variableexplorer = VariableExplorer(self) - self.variableexplorer.register_plugin() - - # IPython console - if IPYTHON_QT_INSTALLED and not self.light: - self.set_splash(_("Loading IPython console...")) - from spyderlib.plugins.ipythonconsole import IPythonConsole - self.ipyconsole = IPythonConsole(self) - self.ipyconsole.register_plugin() - - if not self.light: - nsb = self.variableexplorer.add_shellwidget(self.console.shell) - self.connect(self.console.shell, SIGNAL('refresh()'), - nsb.refresh_table) - nsb.auto_refresh_button.setEnabled(False) - - self.set_splash(_("Setting up main window...")) - - # Help menu - dep_action = create_action(self, _("Optional dependencies..."), - triggered=self.show_dependencies, - icon='advanced.png') - report_action = create_action(self, - _("Report issue..."), - icon=get_icon('bug.png'), - triggered=self.report_issue) - support_action = create_action(self, - _("Spyder support..."), - triggered=self.google_group) - # Spyder documentation - doc_path = get_module_data_path('spyderlib', relpath="doc", - attr_name='DOCPATH') - # * Trying to find the chm doc - spyder_doc = osp.join(doc_path, "Spyderdoc.chm") - if not osp.isfile(spyder_doc): - spyder_doc = osp.join(doc_path, os.pardir, "Spyderdoc.chm") - # * Trying to find the html doc - if not osp.isfile(spyder_doc): - spyder_doc = osp.join(doc_path, "index.html") - # * Trying to find the development-version html doc - if not osp.isfile(spyder_doc): - spyder_doc = osp.join(get_module_source_path('spyderlib'), - os.pardir, 'build', 'lib', 'spyderlib', - 'doc', "index.html") - # * If we totally fail, point to our web build - if not osp.isfile(spyder_doc): - spyder_doc = 'http://pythonhosted.org/spyder' - else: - spyder_doc = file_uri(spyder_doc) - doc_action = create_bookmark_action(self, spyder_doc, - _("Spyder documentation"), shortcut="F1", - icon=get_std_icon('DialogHelpButton')) - tut_action = create_action(self, _("Spyder tutorial"), - triggered=self.inspector.show_tutorial) - self.help_menu_actions = [doc_action, tut_action, None, - report_action, dep_action, support_action, - None] - # Python documentation - if get_python_doc_path() is not None: - pydoc_act = create_action(self, _("Python documentation"), - triggered=lambda: - programs.start_file(get_python_doc_path())) - self.help_menu_actions.append(pydoc_act) - # IPython documentation - if self.ipyconsole is not None: - ipython_menu = QMenu(_("IPython documentation"), self) - intro_action = create_action(self, _("Intro to IPython"), - triggered=self.ipyconsole.show_intro) - quickref_action = create_action(self, _("Quick reference"), - triggered=self.ipyconsole.show_quickref) - guiref_action = create_action(self, _("Console help"), - triggered=self.ipyconsole.show_guiref) - add_actions(ipython_menu, (intro_action, guiref_action, - quickref_action)) - self.help_menu_actions.append(ipython_menu) - # Windows-only: documentation located in sys.prefix/Doc - ipm_actions = [] - def add_ipm_action(text, path): - """Add installed Python module doc action to help submenu""" - path = file_uri(path) - action = create_action(self, text, - icon='%s.png' % osp.splitext(path)[1][1:], - triggered=lambda path=path: programs.start_file(path)) - ipm_actions.append(action) - sysdocpth = osp.join(sys.prefix, 'Doc') - if osp.isdir(sysdocpth): # exists on Windows, except frozen dist. - for docfn in os.listdir(sysdocpth): - pt = r'([a-zA-Z\_]*)(doc)?(-dev)?(-ref)?(-user)?.(chm|pdf)' - match = re.match(pt, docfn) - if match is not None: - pname = match.groups()[0] - if pname not in ('Python', ): - add_ipm_action(pname, osp.join(sysdocpth, docfn)) - # Documentation provided by Python(x,y), if available - try: - from xy.config import DOC_PATH as xy_doc_path - xydoc = osp.join(xy_doc_path, "Libraries") - def add_xydoc(text, pathlist): - for path in pathlist: - if osp.exists(path): - add_ipm_action(text, path) - break - add_xydoc(_("Python(x,y) documentation folder"), - [xy_doc_path]) - add_xydoc(_("IPython documentation"), - [osp.join(xydoc, "IPython", "ipythondoc.chm")]) - add_xydoc(_("guidata documentation"), - [osp.join(xydoc, "guidata", "guidatadoc.chm"), - r"D:\Python\guidata\build\doc_chm\guidatadoc.chm"]) - add_xydoc(_("guiqwt documentation"), - [osp.join(xydoc, "guiqwt", "guiqwtdoc.chm"), - r"D:\Python\guiqwt\build\doc_chm\guiqwtdoc.chm"]) - add_xydoc(_("Matplotlib documentation"), - [osp.join(xydoc, "matplotlib", "Matplotlibdoc.chm"), - osp.join(xydoc, "matplotlib", "Matplotlib.pdf")]) - add_xydoc(_("NumPy documentation"), - [osp.join(xydoc, "NumPy", "numpy.chm")]) - add_xydoc(_("NumPy reference guide"), - [osp.join(xydoc, "NumPy", "numpy-ref.pdf")]) - add_xydoc(_("NumPy user guide"), - [osp.join(xydoc, "NumPy", "numpy-user.pdf")]) - add_xydoc(_("SciPy documentation"), - [osp.join(xydoc, "SciPy", "scipy.chm"), - osp.join(xydoc, "SciPy", "scipy-ref.pdf")]) - except (ImportError, KeyError, RuntimeError): - pass - # Installed Python modules submenu (Windows only) - if ipm_actions: - pymods_menu = QMenu(_("Installed Python modules"), self) - add_actions(pymods_menu, ipm_actions) - self.help_menu_actions.append(pymods_menu) - # Online documentation - web_resources = QMenu(_("Online documentation")) - webres_actions = create_module_bookmark_actions(self, - self.BOOKMARKS) - webres_actions.insert(2, None) - webres_actions.insert(5, None) - add_actions(web_resources, webres_actions) - self.help_menu_actions.append(web_resources) - # Qt assistant link - qta_exe = "assistant-qt4" if sys.platform.startswith('linux') else \ - "assistant" - qta_act = create_program_action(self, _("Qt documentation"), - qta_exe) - if qta_act: - self.help_menu_actions += [qta_act, None] - # About Spyder - about_action = create_action(self, - _("About %s...") % "Spyder", - icon=get_std_icon('MessageBoxInformation'), - triggered=self.about) - self.help_menu_actions += [None, about_action] - - # Status bar widgets - from spyderlib.widgets.status import MemoryStatus, CPUStatus - self.mem_status = MemoryStatus(self, status) - self.cpu_status = CPUStatus(self, status) - self.apply_statusbar_settings() - - # Third-party plugins - for mod in get_spyderplugins_mods(prefix='p_', extension='.py'): - try: - plugin = mod.PLUGIN_CLASS(self) - self.thirdparty_plugins.append(plugin) - plugin.register_plugin() - except AttributeError as error: - print("%s: %s" % (mod, str(error)), file=STDERR) - - # View menu - self.plugins_menu = QMenu(_("Panes"), self) - self.toolbars_menu = QMenu(_("Toolbars"), self) - self.view_menu.addMenu(self.plugins_menu) - self.view_menu.addMenu(self.toolbars_menu) - reset_layout_action = create_action(self, _("Reset window layout"), - triggered=self.reset_window_layout) - quick_layout_menu = QMenu(_("Custom window layouts"), self) - ql_actions = [] - for index in range(1, 4): - if index > 0: - ql_actions += [None] - qli_act = create_action(self, - _("Switch to/from layout %d") % index, - triggered=lambda i=index: - self.quick_layout_switch(i)) - self.register_shortcut(qli_act, "_", - "Switch to/from layout %d" % index) - qlsi_act = create_action(self, _("Set layout %d") % index, - triggered=lambda i=index: - self.quick_layout_set(i)) - self.register_shortcut(qlsi_act, "_", "Set layout %d" % index) - ql_actions += [qli_act, qlsi_act] - add_actions(quick_layout_menu, ql_actions) - if set_attached_console_visible is not None: - cmd_act = create_action(self, - _("Attached console window (debugging)"), - toggled=set_attached_console_visible) - cmd_act.setChecked(is_attached_console_visible()) - add_actions(self.view_menu, (None, cmd_act)) - add_actions(self.view_menu, (None, self.fullscreen_action, - self.maximize_action, - self.close_dockwidget_action, None, - reset_layout_action, - quick_layout_menu)) - - # Adding external tools action to "Tools" menu - if self.external_tools_menu_actions: - external_tools_act = create_action(self, _("External Tools")) - external_tools_act.setMenu(self.external_tools_menu) - self.tools_menu_actions += [None, external_tools_act] - - # Filling out menu/toolbar entries: - add_actions(self.file_menu, self.file_menu_actions) - add_actions(self.edit_menu, self.edit_menu_actions) - add_actions(self.search_menu, self.search_menu_actions) - add_actions(self.source_menu, self.source_menu_actions) - add_actions(self.run_menu, self.run_menu_actions) - add_actions(self.debug_menu, self.debug_menu_actions) - add_actions(self.consoles_menu, self.consoles_menu_actions) - add_actions(self.tools_menu, self.tools_menu_actions) - add_actions(self.external_tools_menu, - self.external_tools_menu_actions) - add_actions(self.help_menu, self.help_menu_actions) - - add_actions(self.main_toolbar, self.main_toolbar_actions) - add_actions(self.file_toolbar, self.file_toolbar_actions) - add_actions(self.edit_toolbar, self.edit_toolbar_actions) - add_actions(self.search_toolbar, self.search_toolbar_actions) - add_actions(self.source_toolbar, self.source_toolbar_actions) - add_actions(self.debug_toolbar, self.debug_toolbar_actions) - add_actions(self.run_toolbar, self.run_toolbar_actions) - - # Apply all defined shortcuts (plugins + 3rd-party plugins) - self.apply_shortcuts() - #self.remove_deprecated_shortcuts() - - # Emitting the signal notifying plugins that main window menu and - # toolbar actions are all defined: - self.emit(SIGNAL('all_actions_defined()')) - - # Window set-up - self.debug_print("Setting up window...") - self.setup_layout(default=False) - - self.splash.hide() - - # Enabling tear off for all menus except help menu - if CONF.get('main', 'tear_off_menus'): - for child in self.menuBar().children(): - if isinstance(child, QMenu) and child != self.help_menu: - child.setTearOffEnabled(True) - - # Menu about to show - for child in self.menuBar().children(): - if isinstance(child, QMenu): - self.connect(child, SIGNAL("aboutToShow()"), - self.update_edit_menu) - - self.debug_print("*** End of MainWindow setup ***") - self.is_starting_up = False - - def post_visible_setup(self): - """Actions to be performed only after the main window's `show` method - was triggered""" - self.emit(SIGNAL('restore_scrollbar_position()')) - - if self.projectexplorer is not None: - self.projectexplorer.check_for_io_errors() - - # Remove our temporary dir - atexit.register(self.remove_tmpdir) - - # Remove settings test directory - if TEST is not None: - import tempfile - conf_dir = osp.join(tempfile.gettempdir(), SUBFOLDER) - atexit.register(shutil.rmtree, conf_dir, ignore_errors=True) - - # [Workaround for Issue 880] - # QDockWidget objects are not painted if restored as floating - # windows, so we must dock them before showing the mainwindow, - # then set them again as floating windows here. - for widget in self.floating_dockwidgets: - widget.setFloating(True) - - # In MacOS X 10.7 our app is not displayed after initialized (I don't - # know why because this doesn't happen when started from the terminal), - # so we need to resort to this hack to make it appear. - if running_in_mac_app(): - import subprocess - idx = __file__.index(MAC_APP_NAME) - app_path = __file__[:idx] - subprocess.call(['open', app_path + MAC_APP_NAME]) - - # Server to maintain just one Spyder instance and open files in it if - # the user tries to start other instances with - # $ spyder foo.py - if CONF.get('main', 'single_instance') and not self.new_instance: - t = threading.Thread(target=self.start_open_files_server) - t.setDaemon(True) - t.start() - - # Connect the window to the signal emmited by the previous server - # when it gets a client connected to it - self.connect(self, SIGNAL('open_external_file(QString)'), - lambda fname: self.open_external_file(fname)) - - # Create Plugins and toolbars submenus - if not self.light: - self.create_plugins_menu() - self.create_toolbars_menu() - - # Open a Python console for light mode - if self.light: - self.extconsole.open_interpreter() - self.extconsole.setMinimumHeight(0) - - if not self.light: - # Hide Internal Console so that people don't use it instead of - # the External or IPython ones - if self.console.dockwidget.isVisible() and DEV is None: - self.console.toggle_view_action.setChecked(False) - self.console.dockwidget.hide() - - # Show the Object Inspector and Consoles by default - plugins_to_show = [self.inspector] - if self.ipyconsole is not None: - if self.ipyconsole.isvisible: - plugins_to_show += [self.extconsole, self.ipyconsole] - else: - plugins_to_show += [self.ipyconsole, self.extconsole] - else: - plugins_to_show += [self.extconsole] - for plugin in plugins_to_show: - if plugin.dockwidget.isVisible(): - plugin.dockwidget.raise_() - - # Show history file if no console is visible - ipy_visible = self.ipyconsole is not None and self.ipyconsole.isvisible - if not self.extconsole.isvisible and not ipy_visible: - self.historylog.add_history(get_conf_path('history.py')) - - # Give focus to the Editor - if self.editor.dockwidget.isVisible(): - try: - self.editor.get_focus_widget().setFocus() - except AttributeError: - pass - - self.is_setting_up = False - - def load_window_settings(self, prefix, default=False, section='main'): - """Load window layout settings from userconfig-based configuration - with *prefix*, under *section* - default: if True, do not restore inner layout""" - get_func = CONF.get_default if default else CONF.get - window_size = get_func(section, prefix+'size') - prefs_dialog_size = get_func(section, prefix+'prefs_dialog_size') - if default: - hexstate = None - else: - hexstate = get_func(section, prefix+'state', None) - pos = get_func(section, prefix+'position') - is_maximized = get_func(section, prefix+'is_maximized') - is_fullscreen = get_func(section, prefix+'is_fullscreen') - return hexstate, window_size, prefs_dialog_size, pos, is_maximized, \ - is_fullscreen - - def get_window_settings(self): - """Return current window settings - Symetric to the 'set_window_settings' setter""" - window_size = (self.window_size.width(), self.window_size.height()) - is_fullscreen = self.isFullScreen() - if is_fullscreen: - is_maximized = self.maximized_flag - else: - is_maximized = self.isMaximized() - pos = (self.window_position.x(), self.window_position.y()) - prefs_dialog_size = (self.prefs_dialog_size.width(), - self.prefs_dialog_size.height()) - hexstate = qbytearray_to_str(self.saveState()) - return (hexstate, window_size, prefs_dialog_size, pos, is_maximized, - is_fullscreen) - - def set_window_settings(self, hexstate, window_size, prefs_dialog_size, - pos, is_maximized, is_fullscreen): - """Set window settings - Symetric to the 'get_window_settings' accessor""" - self.setUpdatesEnabled(False) - self.window_size = QSize(window_size[0], window_size[1]) # width,height - self.prefs_dialog_size = QSize(prefs_dialog_size[0], - prefs_dialog_size[1]) # width,height - self.window_position = QPoint(pos[0], pos[1]) # x,y - self.setWindowState(Qt.WindowNoState) - self.resize(self.window_size) - self.move(self.window_position) - if not self.light: - # Window layout - if hexstate: - self.restoreState( QByteArray().fromHex(str(hexstate)) ) - # [Workaround for Issue 880] - # QDockWidget objects are not painted if restored as floating - # windows, so we must dock them before showing the mainwindow. - for widget in self.children(): - if isinstance(widget, QDockWidget) and widget.isFloating(): - self.floating_dockwidgets.append(widget) - widget.setFloating(False) - # Is fullscreen? - if is_fullscreen: - self.setWindowState(Qt.WindowFullScreen) - self.__update_fullscreen_action() - # Is maximized? - if is_fullscreen: - self.maximized_flag = is_maximized - elif is_maximized: - self.setWindowState(Qt.WindowMaximized) - self.setUpdatesEnabled(True) - - def save_current_window_settings(self, prefix, section='main'): - """Save current window settings with *prefix* in - the userconfig-based configuration, under *section*""" - win_size = self.window_size - prefs_size = self.prefs_dialog_size - - CONF.set(section, prefix+'size', (win_size.width(), win_size.height())) - CONF.set(section, prefix+'prefs_dialog_size', - (prefs_size.width(), prefs_size.height())) - CONF.set(section, prefix+'is_maximized', self.isMaximized()) - CONF.set(section, prefix+'is_fullscreen', self.isFullScreen()) - pos = self.window_position - CONF.set(section, prefix+'position', (pos.x(), pos.y())) - if not self.light: - self.maximize_dockwidget(restore=True)# Restore non-maximized layout - qba = self.saveState() - CONF.set(section, prefix+'state', qbytearray_to_str(qba)) - CONF.set(section, prefix+'statusbar', - not self.statusBar().isHidden()) - - def tabify_plugins(self, first, second): - """Tabify plugin dockwigdets""" - self.tabifyDockWidget(first.dockwidget, second.dockwidget) - - def setup_layout(self, default=False): - """Setup window layout""" - prefix = ('lightwindow' if self.light else 'window') + '/' - (hexstate, window_size, prefs_dialog_size, pos, is_maximized, - is_fullscreen) = self.load_window_settings(prefix, default) - - if hexstate is None and not self.light: - # First Spyder execution: - # trying to set-up the dockwidget/toolbar positions to the best - # appearance possible - splitting = ( - (self.projectexplorer, self.editor, Qt.Horizontal), - (self.editor, self.outlineexplorer, Qt.Horizontal), - (self.outlineexplorer, self.inspector, Qt.Horizontal), - (self.inspector, self.console, Qt.Vertical), - ) - for first, second, orientation in splitting: - if first is not None and second is not None: - self.splitDockWidget(first.dockwidget, second.dockwidget, - orientation) - for first, second in ((self.console, self.extconsole), - (self.extconsole, self.ipyconsole), - (self.ipyconsole, self.historylog), - (self.inspector, self.variableexplorer), - (self.variableexplorer, self.onlinehelp), - (self.onlinehelp, self.explorer), - (self.explorer, self.findinfiles), - ): - if first is not None and second is not None: - self.tabify_plugins(first, second) - for plugin in [self.findinfiles, self.onlinehelp, self.console, - ]+self.thirdparty_plugins: - if plugin is not None: - plugin.dockwidget.close() - for plugin in (self.inspector, self.extconsole): - if plugin is not None: - plugin.dockwidget.raise_() - self.extconsole.setMinimumHeight(250) - hidden_toolbars = [self.source_toolbar, self.edit_toolbar, - self.search_toolbar] - for toolbar in hidden_toolbars: - toolbar.close() - for plugin in (self.projectexplorer, self.outlineexplorer): - plugin.dockwidget.close() - - self.set_window_settings(hexstate, window_size, prefs_dialog_size, pos, - is_maximized, is_fullscreen) - - for plugin in self.widgetlist: - plugin.initialize_plugin_in_mainwindow_layout() - - def reset_window_layout(self): - """Reset window layout to default""" - answer = QMessageBox.warning(self, _("Warning"), - _("Window layout will be reset to default settings: " - "this affects window position, size and dockwidgets.\n" - "Do you want to continue?"), - QMessageBox.Yes | QMessageBox.No) - if answer == QMessageBox.Yes: - self.setup_layout(default=True) - - def quick_layout_switch(self, index): - """Switch to quick layout number *index*""" - if self.current_quick_layout == index: - self.set_window_settings(*self.previous_layout_settings) - self.current_quick_layout = None - else: - try: - settings = self.load_window_settings('layout_%d/' % index, - section='quick_layouts') - except cp.NoOptionError: - QMessageBox.critical(self, _("Warning"), - _("Quick switch layout #%d has not yet " - "been defined.") % index) - return - self.previous_layout_settings = self.get_window_settings() - self.set_window_settings(*settings) - self.current_quick_layout = index - - def quick_layout_set(self, index): - """Save current window settings as quick layout number *index*""" - self.save_current_window_settings('layout_%d/' % index, - section='quick_layouts') - - def plugin_focus_changed(self): - """Focus has changed from one plugin to another""" - if self.light: - # There is currently no point doing the following in light mode - return - self.update_edit_menu() - self.update_search_menu() - - # Now deal with Python shell and IPython plugins - shell = get_focus_python_shell() - if shell is not None: - # A Python shell widget has focus - self.last_console_plugin_focus_was_python = True - if self.inspector is not None: - # The object inspector may be disabled in .spyder.ini - self.inspector.set_shell(shell) - from spyderlib.widgets.externalshell import pythonshell - if isinstance(shell, pythonshell.ExtPythonShellWidget): - shell = shell.parent() - self.variableexplorer.set_shellwidget_from_id(id(shell)) - elif self.ipyconsole is not None: - focus_client = self.ipyconsole.get_focus_client() - if focus_client is not None: - self.last_console_plugin_focus_was_python = False - kwid = focus_client.kernel_widget_id - if kwid is not None: - idx = self.extconsole.get_shell_index_from_id(kwid) - if idx is not None: - kw = self.extconsole.shellwidgets[idx] - if self.inspector is not None: - self.inspector.set_shell(kw) - self.variableexplorer.set_shellwidget_from_id(kwid) - # Setting the kernel widget as current widget for the - # external console's tabwidget: this is necessary for - # the editor/console link to be working (otherwise, - # features like "Execute in current interpreter" will - # not work with IPython clients unless the associated - # IPython kernel has been selected in the external - # console... that's not brilliant, but it works for - # now: we shall take action on this later - self.extconsole.tabwidget.setCurrentWidget(kw) - focus_client.get_control().setFocus() - - def update_file_menu(self): - """Update file menu""" - self.load_temp_session_action.setEnabled(osp.isfile(TEMP_SESSION_PATH)) - - def update_edit_menu(self): - """Update edit menu""" - if self.menuBar().hasFocus(): - return - # Disabling all actions to begin with - for child in self.edit_menu.actions(): - child.setEnabled(False) - - widget, textedit_properties = get_focus_widget_properties() - if textedit_properties is None: # widget is not an editor/console - return - #!!! Below this line, widget is expected to be a QPlainTextEdit instance - console, not_readonly, readwrite_editor = textedit_properties - - # Editor has focus and there is no file opened in it - if not console and not_readonly and not self.editor.is_file_opened(): - return - - self.selectall_action.setEnabled(True) - - # Undo, redo - self.undo_action.setEnabled( readwrite_editor \ - and widget.document().isUndoAvailable() ) - self.redo_action.setEnabled( readwrite_editor \ - and widget.document().isRedoAvailable() ) - - # Copy, cut, paste, delete - has_selection = widget.has_selected_text() - self.copy_action.setEnabled(has_selection) - self.cut_action.setEnabled(has_selection and not_readonly) - self.paste_action.setEnabled(not_readonly) - self.delete_action.setEnabled(has_selection and not_readonly) - - # Comment, uncomment, indent, unindent... - if not console and not_readonly: - # This is the editor and current file is writable - for action in self.editor.edit_menu_actions: - action.setEnabled(True) - - def update_search_menu(self): - """Update search menu""" - if self.menuBar().hasFocus(): - return - # Disabling all actions to begin with - for child in [self.find_action, self.find_next_action, - self.find_previous_action, self.replace_action]: - child.setEnabled(False) - - widget, textedit_properties = get_focus_widget_properties() - for action in self.editor.search_menu_actions: - action.setEnabled(self.editor.isAncestorOf(widget)) - if textedit_properties is None: # widget is not an editor/console - return - #!!! Below this line, widget is expected to be a QPlainTextEdit instance - _x, _y, readwrite_editor = textedit_properties - for action in [self.find_action, self.find_next_action, - self.find_previous_action]: - action.setEnabled(True) - self.replace_action.setEnabled(readwrite_editor) - self.replace_action.setEnabled(readwrite_editor) - - def create_plugins_menu(self): - order = ['editor', 'console', 'ipython_console', 'variable_explorer', - 'inspector', None, 'explorer', 'outline_explorer', - 'project_explorer', 'find_in_files', None, 'historylog', - 'profiler', 'breakpoints', 'pylint', None, - 'onlinehelp', 'internal_console'] - for plugin in self.widgetlist: - action = plugin.toggle_view_action - action.setChecked(plugin.dockwidget.isVisible()) - try: - name = plugin.CONF_SECTION - pos = order.index(name) - except ValueError: - pos = None - if pos is not None: - order[pos] = action - else: - order.append(action) - actions = order[:] - for action in order: - if type(action) is str: - actions.remove(action) - add_actions(self.plugins_menu, actions) - - def create_toolbars_menu(self): - order = ['file_toolbar', 'run_toolbar', 'debug_toolbar', - 'main_toolbar', 'Global working directory', None, - 'search_toolbar', 'edit_toolbar', 'source_toolbar'] - for toolbar in self.toolbarslist: - action = toolbar.toggleViewAction() - name = toolbar.objectName() - try: - pos = order.index(name) - except ValueError: - pos = None - if pos is not None: - order[pos] = action - else: - order.append(action) - add_actions(self.toolbars_menu, order) - - def createPopupMenu(self): - if self.light: - menu = self.createPopupMenu() - else: - menu = QMenu('', self) - actions = self.help_menu_actions[:3] + \ - [None, self.help_menu_actions[-1]] - add_actions(menu, actions) - return menu - - def set_splash(self, message): - """Set splash message""" - if message: - self.debug_print(message) - self.splash.show() - self.splash.showMessage(message, Qt.AlignBottom | Qt.AlignCenter | - Qt.AlignAbsolute, QColor(Qt.white)) - QApplication.processEvents() - - def remove_tmpdir(self): - """Remove Spyder temporary directory""" - shutil.rmtree(programs.TEMPDIR, ignore_errors=True) - - def closeEvent(self, event): - """closeEvent reimplementation""" - if self.closing(True): - event.accept() - else: - event.ignore() - - def resizeEvent(self, event): - """Reimplement Qt method""" - if not self.isMaximized() and not self.fullscreen_flag: - self.window_size = self.size() - QMainWindow.resizeEvent(self, event) - - def moveEvent(self, event): - """Reimplement Qt method""" - if not self.isMaximized() and not self.fullscreen_flag: - self.window_position = self.pos() - QMainWindow.moveEvent(self, event) - - def hideEvent(self, event): - """Reimplement Qt method""" - if not self.light: - for plugin in self.widgetlist: - if plugin.isAncestorOf(self.last_focused_widget): - plugin.visibility_changed(True) - QMainWindow.hideEvent(self, event) - - def change_last_focused_widget(self, old, now): - """To keep track of to the last focused widget""" - if (now is None and QApplication.activeWindow() is not None): - QApplication.activeWindow().setFocus() - self.last_focused_widget = QApplication.focusWidget() - elif now is not None: - self.last_focused_widget = now - - def closing(self, cancelable=False): - """Exit tasks""" - if self.already_closed or self.is_starting_up: - return True - prefix = ('lightwindow' if self.light else 'window') + '/' - self.save_current_window_settings(prefix) - if CONF.get('main', 'single_instance'): - self.open_files_server.close() - for widget in self.widgetlist: - if not widget.closing_plugin(cancelable): - return False - self.dialog_manager.close_all() - self.already_closed = True - return True - - def add_dockwidget(self, child): - """Add QDockWidget and toggleViewAction""" - dockwidget, location = child.create_dockwidget() - if CONF.get('main', 'vertical_dockwidget_titlebars'): - dockwidget.setFeatures(dockwidget.features()| - QDockWidget.DockWidgetVerticalTitleBar) - self.addDockWidget(location, dockwidget) - self.widgetlist.append(child) - - def close_current_dockwidget(self): - widget = QApplication.focusWidget() - for plugin in self.widgetlist: - if plugin.isAncestorOf(widget): - plugin.dockwidget.hide() - break - - def __update_maximize_action(self): - if self.state_before_maximizing is None: - text = _("Maximize current pane") - tip = _("Maximize current pane") - icon = "maximize.png" - else: - text = _("Restore current pane") - tip = _("Restore pane to its original size") - icon = "unmaximize.png" - self.maximize_action.setText(text) - self.maximize_action.setIcon(get_icon(icon)) - self.maximize_action.setToolTip(tip) - - def maximize_dockwidget(self, restore=False): - """Shortcut: Ctrl+Alt+Shift+M - First call: maximize current dockwidget - Second call (or restore=True): restore original window layout""" - if self.state_before_maximizing is None: - if restore: - return - # No plugin is currently maximized: maximizing focus plugin - self.state_before_maximizing = self.saveState() - focus_widget = QApplication.focusWidget() - for plugin in self.widgetlist: - plugin.dockwidget.hide() - if plugin.isAncestorOf(focus_widget): - self.last_plugin = plugin - self.last_plugin.dockwidget.toggleViewAction().setDisabled(True) - self.setCentralWidget(self.last_plugin) - self.last_plugin.ismaximized = True - # Workaround to solve an issue with editor's outline explorer: - # (otherwise the whole plugin is hidden and so is the outline explorer - # and the latter won't be refreshed if not visible) - self.last_plugin.show() - self.last_plugin.visibility_changed(True) - if self.last_plugin is self.editor: - # Automatically show the outline if the editor was maximized: - self.addDockWidget(Qt.RightDockWidgetArea, - self.outlineexplorer.dockwidget) - self.outlineexplorer.dockwidget.show() - else: - # Restore original layout (before maximizing current dockwidget) - self.last_plugin.dockwidget.setWidget(self.last_plugin) - self.last_plugin.dockwidget.toggleViewAction().setEnabled(True) - self.setCentralWidget(None) - self.last_plugin.ismaximized = False - self.restoreState(self.state_before_maximizing) - self.state_before_maximizing = None - self.last_plugin.get_focus_widget().setFocus() - self.__update_maximize_action() - - def __update_fullscreen_action(self): - if self.isFullScreen(): - icon = "window_nofullscreen.png" - else: - icon = "window_fullscreen.png" - self.fullscreen_action.setIcon(get_icon(icon)) - - def toggle_fullscreen(self): - if self.isFullScreen(): - self.fullscreen_flag = False - self.showNormal() - if self.maximized_flag: - self.showMaximized() - else: - self.maximized_flag = self.isMaximized() - self.fullscreen_flag = True - self.showFullScreen() - self.__update_fullscreen_action() - - def add_to_toolbar(self, toolbar, widget): - """Add widget actions to toolbar""" - actions = widget.toolbar_actions - if actions is not None: - add_actions(toolbar, actions) - - def about(self): - """About Spyder""" - versions = get_versions() - # Show Mercurial revision for development version - revlink = '' - if versions['revision']: - rev = versions['revision'] - revlink = " (Commit: %s)" % (rev, rev) - QMessageBox.about(self, - _("About %s") % "Spyder", - """Spyder %s %s -
    The Scientific PYthon Development EnviRonment -

    Copyright © 2009 - 2015 Pierre Raybaut -
    Copyright © 2010 - 2015 The Spyder Development Team -
    Licensed under the terms of the MIT License -

    Created by Pierre Raybaut -
    Developed and maintained by the - Spyder Development Team -
    Many thanks to all the Spyder beta-testers and regular users. -

    Most of the icons come from the Crystal Project - (© 2006-2007 Everaldo Coelho). Other icons by - Yusuke Kamiyamane - (all rights reserved) and by - - The Oxygen icon theme. -

    For bug reports and feature requests, please go - to our Github website. For discussions around the - project, please go to our Google Group -

    This project is part of a larger effort to promote and - facilitate the use of Python for scientific and engineering - software development. The popular Python distributions - Anaconda, - WinPython and - Python(x,y) - also contribute to this plan. -

    Python %s %dbits, Qt %s, %s %s on %s""" - % (versions['spyder'], revlink, __project_url__, - __project_url__, __forum_url__, versions['python'], - versions['bitness'], versions['qt'], versions['qt_api'], - versions['qt_api_ver'], versions['system'])) - - def show_dependencies(self): - """Show Spyder's Optional Dependencies dialog box""" - from spyderlib.widgets.dependencies import DependenciesDialog - dlg = DependenciesDialog(None) - dlg.set_data(dependencies.DEPENDENCIES) - dlg.show() - dlg.exec_() - - def report_issue(self): - if PY3: - from urllib.parse import quote - else: - from urllib import quote # analysis:ignore - versions = get_versions() - # Get git revision for development version - revision = '' - if versions['revision']: - revision = versions['revision'] - issue_template = """\ -## 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** - - -## Version and main components - -* Spyder Version: %s %s -* Python Version: %s -* Qt Versions: %s, %s %s on %s - -## Optional dependencies -``` -%s -``` -""" % (versions['spyder'], - revision, - versions['python'], - versions['qt'], - versions['qt_api'], - versions['qt_api_ver'], - versions['system'], - dependencies.status()) - - url = QUrl("https://github.com/spyder-ide/spyder/issues/new") - url.addEncodedQueryItem("body", quote(issue_template)) - QDesktopServices.openUrl(url) - - def google_group(self): - url = QUrl("http://groups.google.com/group/spyderlib") - QDesktopServices.openUrl(url) - - #---- Global callbacks (called from plugins) - def get_current_editor_plugin(self): - """Return editor plugin which has focus: - console, extconsole, editor, inspector or historylog""" - if self.light: - return self.extconsole - widget = QApplication.focusWidget() - from spyderlib.widgets.editor import TextEditBaseWidget - from spyderlib.widgets.shell import ShellBaseWidget - if not isinstance(widget, (TextEditBaseWidget, ShellBaseWidget)): - return - for plugin in self.widgetlist: - if plugin.isAncestorOf(widget): - return plugin - else: - # External Editor window - plugin = widget - from spyderlib.widgets.editor import EditorWidget - while not isinstance(plugin, EditorWidget): - plugin = plugin.parent() - return plugin - - def find(self): - """Global find callback""" - plugin = self.get_current_editor_plugin() - if plugin is not None: - plugin.find_widget.show() - plugin.find_widget.search_text.setFocus() - return plugin - - def find_next(self): - """Global find next callback""" - plugin = self.get_current_editor_plugin() - if plugin is not None: - plugin.find_widget.find_next() - - def find_previous(self): - """Global find previous callback""" - plugin = self.get_current_editor_plugin() - if plugin is not None: - plugin.find_widget.find_previous() - - def replace(self): - """Global replace callback""" - plugin = self.find() - if plugin is not None: - plugin.find_widget.show_replace() - - def global_callback(self): - """Global callback""" - widget = QApplication.focusWidget() - action = self.sender() - callback = from_qvariant(action.data(), to_text_string) - from spyderlib.widgets.editor import TextEditBaseWidget - if isinstance(widget, TextEditBaseWidget): - getattr(widget, callback)() - - def redirect_internalshell_stdio(self, state): - if state: - self.console.shell.interpreter.redirect_stds() - else: - self.console.shell.interpreter.restore_stds() - - def open_external_console(self, fname, wdir, args, interact, debug, python, - python_args, systerm): - """Open external console""" - if systerm: - # Running script in an external system terminal - try: - programs.run_python_script_in_terminal(fname, wdir, args, - interact, debug, python_args) - except NotImplementedError: - QMessageBox.critical(self, _("Run"), - _("Running an external system terminal " - "is not supported on platform %s." - ) % os.name) - else: - self.extconsole.visibility_changed(True) - self.extconsole.raise_() - self.extconsole.start( - fname=to_text_string(fname), wdir=to_text_string(wdir), - args=to_text_string(args), interact=interact, - debug=debug, python=python, - python_args=to_text_string(python_args) ) - - def execute_in_external_console(self, lines, focus_to_editor): - """ - Execute lines in external or IPython console and eventually set focus - to the editor - """ - console = self.extconsole - if self.ipyconsole is None or self.last_console_plugin_focus_was_python: - console = self.extconsole - else: - console = self.ipyconsole - console.visibility_changed(True) - console.raise_() - console.execute_python_code(lines) - if focus_to_editor: - self.editor.visibility_changed(True) - - def new_file(self, text): - self.editor.new(text=text) - - def open_file(self, fname, external=False): - """ - Open filename with the appropriate application - Redirect to the right widget (txt -> editor, spydata -> workspace, ...) - or open file outside Spyder (if extension is not supported) - """ - fname = to_text_string(fname) - ext = osp.splitext(fname)[1] - if ext in EDIT_EXT: - self.editor.load(fname) - elif self.variableexplorer is not None and ext in IMPORT_EXT: - self.variableexplorer.import_data(fname) - elif encoding.is_text_file(fname): - self.editor.load(fname) - elif not external: - fname = file_uri(fname) - programs.start_file(fname) - - def open_external_file(self, fname): - """ - Open external files that can be handled either by the Editor or the - variable explorer inside Spyder. - """ - fname = encoding.to_unicode_from_fs(fname) - if osp.isfile(fname): - self.open_file(fname, external=True) - elif osp.isfile(osp.join(CWD, fname)): - self.open_file(osp.join(CWD, fname), external=True) - - #---- PYTHONPATH management, etc. - def get_spyder_pythonpath(self): - """Return Spyder PYTHONPATH""" - return self.path+self.project_path - - def add_path_to_sys_path(self): - """Add Spyder path to sys.path""" - for path in reversed(self.get_spyder_pythonpath()): - sys.path.insert(1, path) - - def remove_path_from_sys_path(self): - """Remove Spyder path from sys.path""" - sys_path = sys.path - while sys_path[1] in self.get_spyder_pythonpath(): - sys_path.pop(1) - - def path_manager_callback(self): - """Spyder path manager""" - from spyderlib.widgets.pathmanager import PathManager - self.remove_path_from_sys_path() - project_pathlist = self.projectexplorer.get_pythonpath() - dialog = PathManager(self, self.path, project_pathlist, sync=True) - self.connect(dialog, SIGNAL('redirect_stdio(bool)'), - self.redirect_internalshell_stdio) - dialog.exec_() - self.add_path_to_sys_path() - encoding.writelines(self.path, self.SPYDER_PATH) # Saving path - self.emit(SIGNAL("pythonpath_changed()")) - - def pythonpath_changed(self): - """Project Explorer PYTHONPATH contribution has changed""" - self.remove_path_from_sys_path() - self.project_path = self.projectexplorer.get_pythonpath() - self.add_path_to_sys_path() - self.emit(SIGNAL("pythonpath_changed()")) - - def win_env(self): - """Show Windows current user environment variables""" - self.dialog_manager.show(WinUserEnvDialog(self)) - - #---- Preferences - def apply_settings(self): - """Apply settings changed in 'Preferences' dialog box""" - qapp = QApplication.instance() - # Set 'gtk+' as the default theme in Gtk-based desktops - # Fixes Issue 2036 - if is_gtk_desktop() and ('GTK+' in QStyleFactory.keys()): - try: - qapp.setStyle('gtk+') - except: - pass - else: - qapp.setStyle(CONF.get('main', 'windows_style', - self.default_style)) - - default = self.DOCKOPTIONS - if CONF.get('main', 'vertical_tabs'): - default = default|QMainWindow.VerticalTabs - if CONF.get('main', 'animated_docks'): - default = default|QMainWindow.AnimatedDocks - self.setDockOptions(default) - - for child in self.widgetlist: - features = child.FEATURES - if CONF.get('main', 'vertical_dockwidget_titlebars'): - features = features|QDockWidget.DockWidgetVerticalTitleBar - child.dockwidget.setFeatures(features) - child.update_margins() - - self.apply_statusbar_settings() - - def apply_statusbar_settings(self): - """Update status bar widgets settings""" - for widget, name in ((self.mem_status, 'memory_usage'), - (self.cpu_status, 'cpu_usage')): - if widget is not None: - widget.setVisible(CONF.get('main', '%s/enable' % name)) - widget.set_interval(CONF.get('main', '%s/timeout' % name)) - - def edit_preferences(self): - """Edit Spyder preferences""" - from spyderlib.plugins.configdialog import ConfigDialog - dlg = ConfigDialog(self) - self.connect(dlg, SIGNAL("size_change(QSize)"), - lambda s: self.set_prefs_size(s)) - if self.prefs_dialog_size is not None: - dlg.resize(self.prefs_dialog_size) - for PrefPageClass in self.general_prefs: - widget = PrefPageClass(dlg, main=self) - widget.initialize() - dlg.add_page(widget) - for plugin in [self.workingdirectory, self.editor, - self.projectexplorer, self.extconsole, self.ipyconsole, - self.historylog, self.inspector, self.variableexplorer, - self.onlinehelp, self.explorer, self.findinfiles - ]+self.thirdparty_plugins: - if plugin is not None: - widget = plugin.create_configwidget(dlg) - if widget is not None: - dlg.add_page(widget) - if self.prefs_index is not None: - dlg.set_current_index(self.prefs_index) - dlg.show() - dlg.check_all_settings() - self.connect(dlg.pages_widget, SIGNAL("currentChanged(int)"), - self.__preference_page_changed) - dlg.exec_() - - def __preference_page_changed(self, index): - """Preference page index has changed""" - self.prefs_index = index - - def set_prefs_size(self, size): - """Save preferences dialog size""" - self.prefs_dialog_size = size - - #---- Shortcuts - def register_shortcut(self, qaction_or_qshortcut, context, name, - default=NoDefault): - """ - Register QAction or QShortcut to Spyder main application, - with shortcut (context, name, default) - """ - self.shortcut_data.append( (qaction_or_qshortcut, - context, name, default) ) - self.apply_shortcuts() - - def remove_deprecated_shortcuts(self): - """Remove deprecated shortcuts""" - data = [(context, name) for (qobject, context, name, - default) in self.shortcut_data] - remove_deprecated_shortcuts(data) - - def apply_shortcuts(self): - """Apply shortcuts settings to all widgets/plugins""" - toberemoved = [] - for index, (qobject, context, name, - default) in enumerate(self.shortcut_data): - keyseq = QKeySequence( get_shortcut(context, name, default) ) - try: - if isinstance(qobject, QAction): - qobject.setShortcut(keyseq) - elif isinstance(qobject, QShortcut): - qobject.setKey(keyseq) - except RuntimeError: - # Object has been deleted - toberemoved.append(index) - for index in sorted(toberemoved, reverse=True): - self.shortcut_data.pop(index) - - #---- Sessions - def load_session(self, filename=None): - """Load session""" - if filename is None: - self.redirect_internalshell_stdio(False) - filename, _selfilter = getopenfilename(self, _("Open session"), - getcwd(), _("Spyder sessions")+" (*.session.tar)") - self.redirect_internalshell_stdio(True) - if not filename: - return - if self.close(): - self.next_session_name = filename - - def save_session(self): - """Save session and quit application""" - self.redirect_internalshell_stdio(False) - filename, _selfilter = getsavefilename(self, _("Save session"), - getcwd(), _("Spyder sessions")+" (*.session.tar)") - self.redirect_internalshell_stdio(True) - if filename: - if self.close(): - self.save_session_name = filename - - def start_open_files_server(self): - self.open_files_server.setsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR, 1) - port = select_port(default_port=OPEN_FILES_PORT) - CONF.set('main', 'open_files_port', port) - self.open_files_server.bind(('127.0.0.1', port)) - self.open_files_server.listen(20) - while 1: # 1 is faster than True - try: - req, dummy = self.open_files_server.accept() - except socket.error as e: - # See Issue 1275 for details on why errno EINTR is - # silently ignored here. - eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR - # To avoid a traceback after closing on Windows - if e.args[0] == eintr: - continue - raise - fname = req.recv(1024) - if not self.light: - fname = fname.decode('utf-8') - self.emit(SIGNAL('open_external_file(QString)'), fname) - req.sendall(b' ') - - -#============================================================================== -# Utilities to create the 'main' function -#============================================================================== -def initialize(): - """Initialize Qt, patching sys.exit and eventually setting up ETS""" - # This doesn't create our QApplication, just holds a reference to - # MAIN_APP, created above to show our splash screen as early as - # possible - app = qapplication() - - #----Monkey patching PyQt4.QtGui.QApplication - class FakeQApplication(QApplication): - """Spyder's fake QApplication""" - def __init__(self, args): - self = app # analysis:ignore - @staticmethod - def exec_(): - """Do nothing because the Qt mainloop is already running""" - pass - from spyderlib.qt import QtGui - QtGui.QApplication = FakeQApplication - - #----Monkey patching rope - try: - from spyderlib import rope_patch - rope_patch.apply() - except ImportError: - # rope 0.9.2/0.9.3 is not installed - pass - - #----Monkey patching sys.exit - def fake_sys_exit(arg=[]): - pass - sys.exit = fake_sys_exit - - # Removing arguments from sys.argv as in standard Python interpreter - sys.argv = [''] - - # Selecting Qt4 backend for Enthought Tool Suite (if installed) - try: - from enthought.etsconfig.api import ETSConfig - ETSConfig.toolkit = 'qt4' - except ImportError: - pass - - #----Monkey patching rope (if installed) - # Compatibility with new Mercurial API (>= 1.3). - # New versions of rope (> 0.9.2) already handle this issue - try: - import rope - if rope.VERSION == '0.9.2': - import rope.base.fscommands - - class MercurialCommands(rope.base.fscommands.MercurialCommands): - def __init__(self, root): - self.hg = self._import_mercurial() - self.normal_actions = rope.base.fscommands.FileSystemCommands() - try: - self.ui = self.hg.ui.ui( - verbose=False, debug=False, quiet=True, - interactive=False, traceback=False, - report_untrusted=False) - except: - self.ui = self.hg.ui.ui() - self.ui.setconfig('ui', 'interactive', 'no') - self.ui.setconfig('ui', 'debug', 'no') - self.ui.setconfig('ui', 'traceback', 'no') - self.ui.setconfig('ui', 'verbose', 'no') - self.ui.setconfig('ui', 'report_untrusted', 'no') - self.ui.setconfig('ui', 'quiet', 'yes') - self.repo = self.hg.hg.repository(self.ui, root) - - rope.base.fscommands.MercurialCommands = MercurialCommands - except ImportError: - pass - - return app - - -class Spy(object): - """ - Inspect Spyder internals - - Attributes: - app Reference to main QApplication object - window Reference to spyder.MainWindow widget - """ - def __init__(self, app, window): - self.app = app - self.window = window - def __dir__(self): - return list(self.__dict__.keys()) +\ - [x for x in dir(self.__class__) if x[0] != '_'] - def versions(self): - return get_versions() - - -def run_spyder(app, options, args): - """ - Create and show Spyder's main window - Start QApplication event loop - """ - #TODO: insert here - # Main window - main = MainWindow(options) - try: - main.setup() - except BaseException: - if main.console is not None: - try: - main.console.shell.exit_interpreter() - except BaseException: - pass - raise - - main.show() - main.post_visible_setup() - - if main.console: - main.console.shell.interpreter.namespace['spy'] = \ - Spy(app=app, window=main) - - # Open external files passed as args - if args: - for a in args: - main.open_external_file(a) - - # Don't show icons in menus for Mac - if sys.platform == 'darwin': - QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, True) - - # Open external files with our Mac app - if running_in_mac_app(): - main.connect(app, SIGNAL('open_external_file(QString)'), - lambda fname: main.open_external_file(fname)) - - # To give focus again to the last focused widget after restoring - # the window - main.connect(app, SIGNAL('focusChanged(QWidget*, QWidget*)'), - main.change_last_focused_widget) - - app.exec_() - return main - - -def __remove_temp_session(): - if osp.isfile(TEMP_SESSION_PATH): - os.remove(TEMP_SESSION_PATH) - - -#============================================================================== -# Main -#============================================================================== -def main(): - """Session manager""" - __remove_temp_session() - - # **** Collect command line options **** - # Note regarding Options: - # It's important to collect options before monkey patching sys.exit, - # otherwise, optparse won't be able to exit if --help option is passed - options, args = get_options() - - if set_attached_console_visible is not None: - set_attached_console_visible(DEBUG or options.show_console\ - or options.reset_session\ - or options.reset_to_defaults\ - or options.optimize) - - app = initialize() - if options.reset_session: - # Remove all configuration files! - reset_session() -# CONF.reset_to_defaults(save=True) - return - elif options.reset_to_defaults: - # Reset Spyder settings to defaults - CONF.reset_to_defaults(save=True) - return - elif options.optimize: - # Optimize the whole Spyder's source code directory - import spyderlib - programs.run_python_script(module="compileall", - args=[spyderlib.__path__[0]], p_args=['-O']) - return - - if CONF.get('main', 'crash', False): - CONF.set('main', 'crash', False) - SPLASH.hide() - QMessageBox.information(None, "Spyder", - "Spyder crashed during last session.

    " - "If Spyder does not start at all and before submitting a " - "bug report, please try to reset settings to defaults by " - "running Spyder with the command line option '--reset':
    " - "python spyder --reset" - "

    " - "Warning: " - "this command will remove all your Spyder configuration files " - "located in '%s').

    " - "If restoring the default settings does not help, please take " - "the time to search for known bugs or " - "discussions matching your situation before " - "eventually creating a new issue here. " - "Your feedback will always be greatly appreciated." - "" % (get_conf_path(), __project_url__, - __forum_url__, __project_url__)) - - next_session_name = options.startup_session - while is_text_string(next_session_name): - if next_session_name: - error_message = load_session(next_session_name) - if next_session_name == TEMP_SESSION_PATH: - __remove_temp_session() - if error_message is None: - CONF.load_from_ini() - else: - print(error_message) - QMessageBox.critical(None, "Load session", - u("Unable to load '%s'

    Error message:
    %s") - % (osp.basename(next_session_name), error_message)) - mainwindow = None - try: - mainwindow = run_spyder(app, options, args) - except BaseException: - CONF.set('main', 'crash', True) - import traceback - traceback.print_exc(file=STDERR) - traceback.print_exc(file=open('spyder_crash.log', 'w')) - if mainwindow is None: - # An exception occured - SPLASH.hide() - return - next_session_name = mainwindow.next_session_name - save_session_name = mainwindow.save_session_name - if next_session_name is not None: - #-- Loading session - # Saving current session in a temporary file - # but only if we are not currently trying to reopen it! - if next_session_name != TEMP_SESSION_PATH: - save_session_name = TEMP_SESSION_PATH - if save_session_name: - #-- Saving session - error_message = save_session(save_session_name) - if error_message is not None: - QMessageBox.critical(None, "Save session", - u("Unable to save '%s'

    Error message:
    %s") - % (osp.basename(save_session_name), error_message)) - ORIGINAL_SYS_EXIT() - - -if __name__ == "__main__": - main() diff -Nru spyder-2.3.8+dfsg1/spyderlib/start_app.py spyder-3.0.2+dfsg1/spyderlib/start_app.py --- spyder-2.3.8+dfsg1/spyderlib/start_app.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/start_app.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import os.path as osp -import socket -import time -import atexit -import random - -# Local imports -from spyderlib.cli_options import get_options -from spyderlib.baseconfig import get_conf_path, running_in_mac_app -from spyderlib.config import CONF -from spyderlib.baseconfig import DEV, TEST -from spyderlib.utils.external import lockfile -from spyderlib.py3compat import is_unicode - - -def send_args_to_spyder(args): - """ - Simple socket client used to send the args passed to the Spyder - executable to an already running instance. - - Args can be Python scripts or files with these extensions: .spydata, .mat, - .npy, or .h5, which can be imported by the Variable Explorer. - """ - port = CONF.get('main', 'open_files_port') - - # Wait ~50 secs for the server to be up - # Taken from http://stackoverflow.com/a/4766598/438386 - for _x in range(200): - try: - for arg in args: - client = socket.socket(socket.AF_INET, socket.SOCK_STREAM, - socket.IPPROTO_TCP) - client.connect(("127.0.0.1", port)) - if is_unicode(arg): - arg = arg.encode('utf-8') - client.send(osp.abspath(arg)) - client.close() - except socket.error: - time.sleep(0.25) - continue - break - - -def main(): - """ - Start Spyder application. - - If single instance mode is turned on (default behavior) and an instance of - Spyder is already running, this will just parse and send command line - options to the application. - """ - # Renaming old configuration files (the '.' prefix has been removed) - # (except for .spyder.ini --> spyder.ini, which is done in userconfig.py) - if DEV is None: - cpath = get_conf_path() - for fname in os.listdir(cpath): - if fname.startswith('.'): - old, new = osp.join(cpath, fname), osp.join(cpath, fname[1:]) - try: - os.rename(old, new) - except OSError: - pass - - # Parse command line options - options, args = get_options() - - if CONF.get('main', 'single_instance') and not options.new_instance \ - and not running_in_mac_app(): - # Minimal delay (0.1-0.2 secs) to avoid that several - # instances started at the same time step in their - # own foots while trying to create the lock file - time.sleep(random.randrange(1000, 2000, 90)/10000.) - - # Lock file creation - lock_file = get_conf_path('spyder.lock') - lock = lockfile.FilesystemLock(lock_file) - - # Try to lock spyder.lock. If it's *possible* to do it, then - # there is no previous instance running and we can start a - # new one. If *not*, then there is an instance already - # running, which is locking that file - try: - lock_created = lock.lock() - except: - # If locking fails because of errors in the lockfile - # module, try to remove a possibly stale spyder.lock. - # This is reported to solve all problems with - # lockfile (See issue 2363) - try: - if os.name == 'nt': - if osp.isdir(lock_file): - import shutil - shutil.rmtree(lock_file, ignore_errors=True) - else: - if osp.islink(lock_file): - os.unlink(lock_file) - except: - pass - - # Then start Spyder as usual and *don't* continue - # executing this script because it doesn't make - # sense - from spyderlib import spyder - spyder.main() - return - - if lock_created: - # Start a new instance - if TEST is None: - atexit.register(lock.unlock) - from spyderlib import spyder - spyder.main() - else: - # Pass args to Spyder or print an informative - # message - if args: - send_args_to_spyder(args) - else: - print("Spyder is already running. If you want to open a new \n" - "instance, please pass to it the --new-instance option") - else: - from spyderlib import spyder - spyder.main() - - -if __name__ == "__main__": - main() diff -Nru spyder-2.3.8+dfsg1/spyderlib/userconfig.py spyder-3.0.2+dfsg1/spyderlib/userconfig.py --- spyder-2.3.8+dfsg1/spyderlib/userconfig.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/userconfig.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,454 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# userconfig License Agreement (MIT License) -# ------------------------------------------ -# -# Copyright © 2009-2012 Pierre Raybaut -# Copyright © 2014 The Spyder Development Team -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation -# files (the "Software"), to deal in the Software without -# restriction, including without limitation the rights to use, -# copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following -# conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - - -""" -This module provides user configuration file (.ini file) management features -based on ``ConfigParser`` (present in the standard library). -""" - -from __future__ import print_function - -import os -import re -import os.path as osp -import shutil -import time - -from spyderlib.baseconfig import (DEV, TEST, get_module_source_path, - get_home_dir) -from spyderlib.utils.programs import check_version -from spyderlib.py3compat import configparser as cp -from spyderlib.py3compat import PY2, is_text_string, to_text_string - -if PY2: - import codecs - -#============================================================================== -# Auxiliary classes -#============================================================================== -class NoDefault: - pass - - -#============================================================================== -# Defaults class -#============================================================================== -class DefaultsConfig(cp.ConfigParser): - """ - Class used to save defaults to a file and as base class for - UserConfig - """ - def __init__(self, name, subfolder): - cp.ConfigParser.__init__(self) - self.name = name - self.subfolder = subfolder - - def _write(self, fp): - """ - Private write method for Python 2 - The one from configparser fails for non-ascii Windows accounts - """ - if self._defaults: - fp.write("[%s]\n" % DEFAULTSECT) - for (key, value) in self._defaults.items(): - fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) - fp.write("\n") - for section in self._sections: - fp.write("[%s]\n" % section) - for (key, value) in self._sections[section].items(): - if key == "__name__": - continue - if (value is not None) or (self._optcre == self.OPTCRE): - value = to_text_string(value) - key = " = ".join((key, value.replace('\n', '\n\t'))) - fp.write("%s\n" % (key)) - fp.write("\n") - - def _set(self, section, option, value, verbose): - """ - Private set method - """ - if not self.has_section(section): - self.add_section( section ) - if not is_text_string(value): - value = repr( value ) - if verbose: - print('%s[ %s ] = %s' % (section, option, value)) - cp.ConfigParser.set(self, section, option, value) - - def _save(self): - """ - Save config into the associated .ini file - """ - # See Issue 1086 and 1242 for background on why this - # method contains all the exception handling. - fname = self.filename() - - def _write_file(fname): - if PY2: - # Python 2 - with codecs.open(fname, 'w', encoding='utf-8') as configfile: - self._write(configfile) - else: - # Python 3 - with open(fname, 'w', encoding='utf-8') as configfile: - self.write(configfile) - - try: # the "easy" way - _write_file(fname) - except IOError: - try: # the "delete and sleep" way - if osp.isfile(fname): - os.remove(fname) - time.sleep(0.05) - _write_file(fname) - except Exception as e: - print("Failed to write user configuration file.") - print("Please submit a bug report.") - raise(e) - - def filename(self): - """ - Create a .ini filename located in user home directory - """ - if TEST is None: - folder = get_home_dir() - else: - import tempfile - folder = tempfile.gettempdir() - w_dot = osp.join(folder, '.%s.ini' % self.name) - if self.subfolder is None: - return w_dot - else: - folder = osp.join(folder, self.subfolder) - w_dot = osp.join(folder, '.%s.ini' % self.name) - # Save defaults in a "defaults" dir of .spyder2 to not pollute it - if 'defaults' in self.name: - folder = osp.join(folder, 'defaults') - try: - os.makedirs(folder) - except os.error: - # Folder (or one of its parents) already exists - pass - old, new = w_dot, osp.join(folder, '%s.ini' % self.name) - if osp.isfile(old) and DEV is None: - try: - if osp.isfile(new): - os.remove(old) - else: - os.rename(old, new) - except OSError: - pass - return new - - def set_defaults(self, defaults): - for section, options in defaults: - for option in options: - new_value = options[ option ] - self._set(section, option, new_value, False) - - -#============================================================================== -# User config class -#============================================================================== -class UserConfig(DefaultsConfig): - """ - UserConfig class, based on ConfigParser - name: name of the config - defaults: dictionnary containing options - *or* list of tuples (section_name, options) - version: version of the configuration file (X.Y.Z format) - subfolder: configuration file will be saved in %home%/subfolder/%name%.ini - - Note that 'get' and 'set' arguments number and type - differ from the overriden methods - """ - DEFAULT_SECTION_NAME = 'main' - def __init__(self, name, defaults=None, load=True, version=None, - subfolder=None, backup=False, raw_mode=False, - remove_obsolete=False): - DefaultsConfig.__init__(self, name, subfolder) - self.raw = 1 if raw_mode else 0 - if (version is not None) and (re.match('^(\d+).(\d+).(\d+)$', version) is None): - raise ValueError("Version number %r is incorrect - must be in X.Y.Z format" % version) - if isinstance(defaults, dict): - defaults = [ (self.DEFAULT_SECTION_NAME, defaults) ] - self.defaults = defaults - if defaults is not None: - self.reset_to_defaults(save=False) - fname = self.filename() - if backup: - try: - shutil.copyfile(fname, "%s.bak" % fname) - except IOError: - pass - if load: - # If config file already exists, it overrides Default options: - self.load_from_ini() - old_ver = self.get_version(version) - _major = lambda _t: _t[:_t.find('.')] - _minor = lambda _t: _t[:_t.rfind('.')] - # Save new defaults - self.__save_new_defaults(defaults, version, subfolder) - # Updating defaults only if major/minor version is different - if _minor(version) != _minor(old_ver): - if backup: - try: - shutil.copyfile(fname, "%s-%s.bak" % (fname, old_ver)) - except IOError: - pass - if check_version(old_ver, '2.4.0', '<'): - self.reset_to_defaults(save=False) - else: - self.__update_defaults(defaults, old_ver) - # Remove deprecated options if major version has changed - if remove_obsolete or _major(version) != _major(old_ver): - self.__remove_deprecated_options(old_ver) - # Set new version number - self.set_version(version, save=False) - if defaults is None: - # If no defaults are defined, set .ini file settings as default - self.set_as_defaults() - - def get_version(self, version='0.0.0'): - """Return configuration (not application!) version""" - return self.get(self.DEFAULT_SECTION_NAME, 'version', version) - - def set_version(self, version='0.0.0', save=True): - """Set configuration (not application!) version""" - self.set(self.DEFAULT_SECTION_NAME, 'version', version, save=save) - - def load_from_ini(self): - """ - Load config from the associated .ini file - """ - try: - if PY2: - # Python 2 - fname = self.filename() - if osp.isfile(fname): - try: - with codecs.open(fname, encoding='utf-8') as configfile: - self.readfp(configfile) - except IOError: - print("Failed reading file", fname) - else: - # Python 3 - self.read(self.filename(), encoding='utf-8') - except cp.MissingSectionHeaderError: - print("Warning: File contains no section headers.") - - def __load_old_defaults(self, old_version): - """Read old defaults""" - old_defaults = cp.ConfigParser() - if check_version(old_version, '3.0.0', '<='): - path = get_module_source_path('spyderlib') - else: - path = osp.dirname(self.filename()) - path = osp.join(path, 'defaults') - old_defaults.read(osp.join(path, 'defaults-'+old_version+'.ini')) - return old_defaults - - def __save_new_defaults(self, defaults, new_version, subfolder): - """Save new defaults""" - new_defaults = DefaultsConfig(name='defaults-'+new_version, - subfolder=subfolder) - if not osp.isfile(new_defaults.filename()): - new_defaults.set_defaults(defaults) - new_defaults._save() - - def __update_defaults(self, defaults, old_version, verbose=False): - """Update defaults after a change in version""" - old_defaults = self.__load_old_defaults(old_version) - for section, options in defaults: - for option in options: - new_value = options[ option ] - try: - old_value = old_defaults.get(section, option) - except (cp.NoSectionError, cp.NoOptionError): - old_value = None - if old_value is None or \ - to_text_string(new_value) != old_value: - self._set(section, option, new_value, verbose) - - def __remove_deprecated_options(self, old_version): - """ - Remove options which are present in the .ini file but not in defaults - """ - old_defaults = self.__load_old_defaults(old_version) - for section in old_defaults.sections(): - for option, _ in old_defaults.items(section, raw=self.raw): - if self.get_default(section, option) is NoDefault: - self.remove_option(section, option) - if len(self.items(section, raw=self.raw)) == 0: - self.remove_section(section) - - def cleanup(self): - """ - Remove .ini file associated to config - """ - os.remove(self.filename()) - - def set_as_defaults(self): - """ - Set defaults from the current config - """ - self.defaults = [] - for section in self.sections(): - secdict = {} - for option, value in self.items(section, raw=self.raw): - secdict[option] = value - self.defaults.append( (section, secdict) ) - - def reset_to_defaults(self, save=True, verbose=False, section=None): - """ - Reset config to Default values - """ - for sec, options in self.defaults: - if section == None or section == sec: - for option in options: - value = options[ option ] - self._set(sec, option, value, verbose) - if save: - self._save() - - def __check_section_option(self, section, option): - """ - Private method to check section and option types - """ - if section is None: - section = self.DEFAULT_SECTION_NAME - elif not is_text_string(section): - raise RuntimeError("Argument 'section' must be a string") - if not is_text_string(option): - raise RuntimeError("Argument 'option' must be a string") - return section - - def get_default(self, section, option): - """ - Get Default value for a given (section, option) - -> useful for type checking in 'get' method - """ - section = self.__check_section_option(section, option) - for sec, options in self.defaults: - if sec == section: - if option in options: - return options[ option ] - else: - return NoDefault - - def get(self, section, option, default=NoDefault): - """ - Get an option - section=None: attribute a default section name - default: default value (if not specified, an exception - will be raised if option doesn't exist) - """ - section = self.__check_section_option(section, option) - - if not self.has_section(section): - if default is NoDefault: - raise cp.NoSectionError(section) - else: - self.add_section(section) - - if not self.has_option(section, option): - if default is NoDefault: - raise cp.NoOptionError(option, section) - else: - self.set(section, option, default) - return default - - value = cp.ConfigParser.get(self, section, option, raw=self.raw) - default_value = self.get_default(section, option) - if isinstance(default_value, bool): - value = eval(value) - elif isinstance(default_value, float): - value = float(value) - elif isinstance(default_value, int): - value = int(value) - else: - if PY2 and is_text_string(default_value): - try: - value = value.decode('utf-8') - except (UnicodeEncodeError, UnicodeDecodeError): - pass - try: - # lists, tuples, ... - value = eval(value) - except: - pass - return value - - def set_default(self, section, option, default_value): - """ - Set Default value for a given (section, option) - -> called when a new (section, option) is set and no default exists - """ - section = self.__check_section_option(section, option) - for sec, options in self.defaults: - if sec == section: - options[ option ] = default_value - - def set(self, section, option, value, verbose=False, save=True): - """ - Set an option - section=None: attribute a default section name - """ - section = self.__check_section_option(section, option) - default_value = self.get_default(section, option) - if default_value is NoDefault: - # This let us save correctly string value options with - # no config default that contain non-ascii chars in - # Python 2 - if PY2 and is_text_string(value): - value = repr(value) - default_value = value - self.set_default(section, option, default_value) - if isinstance(default_value, bool): - value = bool(value) - elif isinstance(default_value, float): - value = float(value) - elif isinstance(default_value, int): - value = int(value) - elif not is_text_string(default_value): - value = repr(value) - self._set(section, option, value, verbose) - if save: - self._save() - - def remove_section(self, section): - cp.ConfigParser.remove_section(self, section) - self._save() - - def remove_option(self, section, option): - cp.ConfigParser.remove_option(self, section, option) - self._save() diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/bsdsocket.py spyder-3.0.2+dfsg1/spyderlib/utils/bsdsocket.py --- spyder-2.3.8+dfsg1/spyderlib/utils/bsdsocket.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/bsdsocket.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,183 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""BSD socket interface communication utilities""" - -# Be extra careful here. The interface is used to communicate with subprocesses -# by redirecting output streams through a socket. Any exception in this module -# and failure to read out buffers will most likely lock up Spyder. - -import os -import socket -import struct -import threading -import errno -import traceback - -# Local imports -from spyderlib.baseconfig import DEBUG, STDERR -DEBUG_EDITOR = DEBUG >= 3 -from spyderlib.py3compat import pickle -PICKLE_HIGHEST_PROTOCOL = 2 - - -def temp_fail_retry(error, fun, *args): - """Retry to execute function, ignoring EINTR error (interruptions)""" - while 1: - try: - return fun(*args) - except error as e: - eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR - if e.args[0] == eintr: - continue - raise - - -SZ = struct.calcsize("l") - - -def write_packet(sock, data, already_pickled=False): - """Write *data* to socket *sock*""" - if already_pickled: - sent_data = data - else: - sent_data = pickle.dumps(data, PICKLE_HIGHEST_PROTOCOL) - sent_data = struct.pack("l", len(sent_data)) + sent_data - nsend = len(sent_data) - while nsend > 0: - nsend -= temp_fail_retry(socket.error, sock.send, sent_data) - - -def read_packet(sock, timeout=None): - """ - Read data from socket *sock* - Returns None if something went wrong - """ - sock.settimeout(timeout) - dlen, data = None, None - try: - if os.name == 'nt': - # Windows implementation - datalen = sock.recv(SZ) - dlen, = struct.unpack("l", datalen) - data = b'' - while len(data) < dlen: - data += sock.recv(dlen) - else: - # Linux/MacOSX implementation - # Thanks to eborisch: - # See issue 1106 - datalen = temp_fail_retry(socket.error, sock.recv, - SZ, socket.MSG_WAITALL) - if len(datalen) == SZ: - dlen, = struct.unpack("l", datalen) - data = temp_fail_retry(socket.error, sock.recv, - dlen, socket.MSG_WAITALL) - except socket.timeout: - raise - except socket.error: - data = None - finally: - sock.settimeout(None) - if data is not None: - try: - return pickle.loads(data) - except Exception: - # Catch all exceptions to avoid locking spyder - if DEBUG_EDITOR: - traceback.print_exc(file=STDERR) - return - - -# Using a lock object to avoid communication issues described in Issue 857 -COMMUNICATE_LOCK = threading.Lock() - -# * Old com implementation * -# See solution (1) in Issue 434, comment 13: -def communicate(sock, command, settings=[]): - """Communicate with monitor""" - try: - COMMUNICATE_LOCK.acquire() - write_packet(sock, command) - for option in settings: - write_packet(sock, option) - return read_packet(sock) - finally: - COMMUNICATE_LOCK.release() - -## new com implementation: -## See solution (2) in Issue 434, comment 13: -#def communicate(sock, command, settings=[], timeout=None): -# """Communicate with monitor""" -# write_packet(sock, command) -# for option in settings: -# write_packet(sock, option) -# if timeout == 0.: -# # non blocking socket is not really supported: -# # setting timeout to 0. here is equivalent (in current monitor's -# # implementation) to say 'I don't need to receive anything in return' -# return -# while True: -# output = read_packet(sock, timeout=timeout) -# if output is None: -# return -# output_command, output_data = output -# if command == output_command: -# return output_data -# elif DEBUG: -# logging.debug("###### communicate/warning /Begin ######") -# logging.debug("was expecting '%s', received '%s'" \ -# % (command, output_command)) -# logging.debug("###### communicate/warning /End ######") - - -class PacketNotReceived(object): - pass - -PACKET_NOT_RECEIVED = PacketNotReceived() - - -if __name__ == '__main__': - # socket read/write testing - client and server in one thread - - # (techtonik): the stuff below is placed into public domain - print("-- Testing standard Python socket interface --") - - address = ("127.0.0.1", 9999) - - server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server.setblocking(0) - server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server.bind( address ) - server.listen(2) - - client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - client.connect( address ) - - client.send("data to be catched") - # accepted server socket is the one we can read from - # note that it is different from server socket - accsock, addr = server.accept() - print('..got "%s" from %s' % (accsock.recv(4096), addr)) - client.send("more data for recv") - print('..got "%s" from %s' % (accsock.recv(4096), addr)) - - # accsock.close() - # client.send("more data for recv") - #socket.error: [Errno 9] Bad file descriptor - # accsock, addr = server.accept() - #socket.error: [Errno 11] Resource temporarily unavailable - - - print("-- Testing BSD socket write_packet/read_packet --") - - write_packet(client, "a tiny piece of data") - print('..got "%s" from read_packet()' % (read_packet(accsock))) - - client.close() - server.close() - - print("-- Done.") diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/codeanalysis.py spyder-3.0.2+dfsg1/spyderlib/utils/codeanalysis.py --- spyder-2.3.8+dfsg1/spyderlib/utils/codeanalysis.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/codeanalysis.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,185 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Source code analysis utilities -""" - -import sys -import re -import os -from subprocess import Popen, PIPE -import tempfile -import traceback - -# Local import -from spyderlib.baseconfig import _, DEBUG -from spyderlib.utils import programs, encoding -from spyderlib.py3compat import to_text_string, to_binary_string, PY3 -from spyderlib import dependencies -DEBUG_EDITOR = DEBUG >= 3 - -#============================================================================== -# Pyflakes/pep8 code analysis -#============================================================================== -TASKS_PATTERN = r"(^|#)[ ]*(TODO|FIXME|XXX|HINT|TIP|@todo)([^#]*)" - -#TODO: this is a test for the following function -def find_tasks(source_code): - """Find tasks in source code (TODO, FIXME, XXX, ...)""" - results = [] - for line, text in enumerate(source_code.splitlines()): - for todo in re.findall(TASKS_PATTERN, text): - results.append((todo[-1].strip().capitalize(), line+1)) - return results - - -def check_with_pyflakes(source_code, filename=None): - """Check source code with pyflakes - Returns an empty list if pyflakes is not installed""" - try: - if filename is None: - filename = '' - try: - source_code += '\n' - except TypeError: - # Python 3 - source_code += to_binary_string('\n') - - import _ast - from pyflakes.checker import Checker - # First, compile into an AST and handle syntax errors. - try: - tree = compile(source_code, filename, "exec", _ast.PyCF_ONLY_AST) - except SyntaxError as value: - # If there's an encoding problem with the file, the text is None. - if value.text is None: - results = [] - else: - results = [(value.args[0], value.lineno)] - except (ValueError, TypeError): - # Example of ValueError: file contains invalid \x escape character - # (see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=674797) - # Example of TypeError: file contains null character - # (see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=674796) - results = [] - else: - # Okay, it's syntactically valid. Now check it. - w = Checker(tree, filename) - w.messages.sort(key=lambda x: x.lineno) - results = [] - coding = encoding.get_coding(source_code) - lines = source_code.splitlines() - for warning in w.messages: - if 'analysis:ignore' not in \ - to_text_string(lines[warning.lineno-1], coding): - results.append((warning.message % warning.message_args, - warning.lineno)) - except Exception: - # Never return None to avoid lock in spyderlib/widgets/editor.py - # See Issue 1547 - results = [] - if DEBUG_EDITOR: - traceback.print_exc() # Print exception in internal console - return results - -# Required version: -# Why 0.5 (Python2)? Because it's based on _ast (thread-safe) -PYFLAKES_REQVER = '>=0.6.0' if PY3 else '>=0.5.0' -dependencies.add("pyflakes", _("Real-time code analysis on the Editor"), - required_version=PYFLAKES_REQVER) - -PEP8_REQVER = '>=0.6' -dependencies.add("pep8", _("Real-time code style analysis on the Editor"), - required_version=PEP8_REQVER) - - -def is_pyflakes_installed(): - """Return True if pyflakes required version is installed""" - return programs.is_module_installed('pyflakes', PYFLAKES_REQVER) - - -def get_checker_executable(name): - """Return checker executable in the form of a list of arguments - for subprocess.Popen""" - if programs.is_program_installed(name): - # Checker is properly installed - return [name] - else: - path1 = programs.python_script_exists(package=None, - module=name+'_script') - path2 = programs.python_script_exists(package=None, module=name) - if path1 is not None: # checker_script.py is available - # Checker script is available but has not been installed - # (this may work with pyflakes) - return [sys.executable, path1] - elif path2 is not None: # checker.py is available - # Checker package is available but its script has not been - # installed (this works with pep8 but not with pyflakes) - return [sys.executable, path2] - - -def check(args, source_code, filename=None, options=None): - """Check source code with checker defined with *args* (list) - Returns an empty list if checker is not installed""" - if args is None: - return [] - if options is not None: - args += options - if any(['pyflakes' in arg for arg in args]): - # Pyflakes requires an ending new line (pep8 don't! -- see Issue 1123) - # Note: this code is not used right now as it is faster to invoke - # pyflakes in current Python interpreter (see `check_with_pyflakes` - # function above) than calling it through a subprocess - source_code += '\n' - if filename is None: - # Creating a temporary file because file does not exist yet - # or is not up-to-date - tempfd = tempfile.NamedTemporaryFile(suffix=".py", delete=False) - tempfd.write(source_code) - tempfd.close() - args.append(tempfd.name) - else: - args.append(filename) - output = Popen(args, stdout=PIPE, stderr=PIPE - ).communicate()[0].strip().decode().splitlines() - if filename is None: - os.unlink(tempfd.name) - results = [] - coding = encoding.get_coding(source_code) - lines = source_code.splitlines() - for line in output: - lineno = int(re.search(r'(\:[\d]+\:)', line).group()[1:-1]) - if 'analysis:ignore' not in to_text_string(lines[lineno-1], coding): - message = line[line.find(': ')+2:] - results.append((message, lineno)) - return results - - -def check_with_pep8(source_code, filename=None): - """Check source code with pep8""" - try: - args = get_checker_executable('pep8') - results = check(args, source_code, filename=filename, options=['-r']) - except Exception: - # Never return None to avoid lock in spyderlib/widgets/editor.py - # See Issue 1547 - results = [] - if DEBUG_EDITOR: - traceback.print_exc() # Print exception in internal console - return results - - -if __name__ == '__main__': -# fname = __file__ - fname = os.path.join(os.path.dirname(__file__), - os.pardir, os.pardir, 'bootstrap.py') - code = open(fname).read() - check_results = check_with_pyflakes(code, fname)+\ - check_with_pep8(code, fname)+find_tasks(code) -# check_results = check_with_pep8(code, fname) - for message, line in check_results: - sys.stdout.write("Message: %s -- Line: %s\n" % (message, line)) diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/debug.py spyder-3.0.2+dfsg1/spyderlib/utils/debug.py --- spyder-2.3.8+dfsg1/spyderlib/utils/debug.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/debug.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2013 Pierre Raybaut -# Copyright © 2012-2013 anatoly techtonik -# -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Debug utilities that are independent of Spyder code. - -See spyderlib.baseconfig for other helpers. -""" - -from __future__ import print_function - -import inspect -import traceback -import time - - -def log_time(fd): - timestr = "Logging time: %s" % time.ctime(time.time()) - print("="*len(timestr), file=fd) - print(timestr, file=fd) - print("="*len(timestr), file=fd) - print("", file=fd) - -def log_last_error(fname, context=None): - """Log last error in filename *fname* -- *context*: string (optional)""" - fd = open(fname, 'a') - log_time(fd) - if context: - print("Context", file=fd) - print("-------", file=fd) - print("", file=fd) - print(context, file=fd) - print("", file=fd) - print("Traceback", file=fd) - print("---------", file=fd) - print("", file=fd) - traceback.print_exc(file=fd) - print("", file=fd) - print("", file=fd) - -def log_dt(fname, context, t0): - fd = open(fname, 'a') - log_time(fd) - print("%s: %d ms" % (context, 10*round(1e2*(time.time()-t0))), file=fd) - print("", file=fd) - print("", file=fd) - -def caller_name(skip=2): - """ - Get name of a caller in the format module.class.method - - `skip` specifies how many levels of call stack to skip for caller's name. - skip=1 means "who calls me", skip=2 "who calls my caller" etc. - - An empty string is returned if skipped levels exceed stack height - """ - stack = inspect.stack() - start = 0 + skip - if len(stack) < start + 1: - return '' - parentframe = stack[start][0] - - name = [] - module = inspect.getmodule(parentframe) - # `modname` can be None when frame is executed directly in console - # TODO(techtonik): consider using __main__ - if module: - name.append(module.__name__) - # detect classname - if 'self' in parentframe.f_locals: - # I don't know any way to detect call from the object method - # XXX: there seems to be no way to detect static method call - it will - # be just a function call - name.append(parentframe.f_locals['self'].__class__.__name__) - codename = parentframe.f_code.co_name - if codename != '': # top level usually - name.append( codename ) # function or a method - del parentframe - return ".".join(name) - -def get_class_that_defined(method): - for cls in inspect.getmro(method.im_class): - if method.__name__ in cls.__dict__: - return cls.__name__ - -def log_methods_calls(fname, some_class, prefix=None): - """ - Hack `some_class` to log all method calls into `fname` file. - If `prefix` format is not set, each log entry is prefixed with: - --[ asked / called / defined ] -- - asked - name of `some_class` - called - name of class for which a method is called - defined - name of class where method is defined - - Must be used carefully, because it monkeypatches __getattribute__ call. - - Example: log_methods_calls('log.log', ShellBaseWidget) - """ - # test if file is writable - open(fname, 'a').close() - FILENAME = fname - CLASS = some_class - - PREFIX = "--[ %(asked)s / %(called)s / %(defined)s ]--" - if prefix != None: - PREFIX = prefix - MAXWIDTH = {'o_O': 10} # hack with editable closure dict, to align names - - def format_prefix(method, methodobj): - """ - --[ ShellBase / Internal / BaseEdit ]------- get_position - """ - classnames = { - 'asked': CLASS.__name__, - 'called': methodobj.__class__.__name__, - 'defined': get_class_that_defined(method) - } - line = PREFIX % classnames - MAXWIDTH['o_O'] = max(len(line), MAXWIDTH['o_O']) - return line.ljust(MAXWIDTH['o_O'], '-') - - import types - def __getattribute__(self, name): - attr = object.__getattribute__(self, name) - if type(attr) is not types.MethodType: - return attr - else: - def newfunc(*args, **kwargs): - log = open(FILENAME, 'a') - prefix = format_prefix(attr, self) - log.write('%s %s\n' % (prefix, name)) - log.close() - result = attr(*args, **kwargs) - return result - return newfunc - - some_class.__getattribute__ = __getattribute__ - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/dochelpers.py spyder-3.0.2+dfsg1/spyderlib/utils/dochelpers.py --- spyder-2.3.8+dfsg1/spyderlib/utils/dochelpers.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/dochelpers.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,337 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Utilities and wrappers around inspect module""" - -from __future__ import print_function - -import inspect -import re - -# Local imports: -from spyderlib.utils import encoding -from spyderlib.py3compat import (is_text_string, builtins, get_meth_func, - get_meth_class_inst, get_meth_class, - get_func_defaults, to_text_string) - - -SYMBOLS = r"[^\'\"a-zA-Z0-9_.]" - -def getobj(txt, last=False): - """Return the last valid object name in string""" - txt_end = "" - for startchar, endchar in ["[]", "()"]: - if txt.endswith(endchar): - pos = txt.rfind(startchar) - if pos: - txt_end = txt[pos:] - txt = txt[:pos] - tokens = re.split(SYMBOLS, txt) - token = None - try: - while token is None or re.match(SYMBOLS, token): - token = tokens.pop() - if token.endswith('.'): - token = token[:-1] - if token.startswith('.'): - # Invalid object name - return None - if last: - #XXX: remove this statement as well as the "last" argument - token += txt[ txt.rfind(token) + len(token) ] - token += txt_end - if token: - return token - except IndexError: - return None - - -def getobjdir(obj): - """ - For standard objects, will simply return dir(obj) - In special cases (e.g. WrapITK package), will return only string elements - of result returned by dir(obj) - """ - return [item for item in dir(obj) if is_text_string(item)] - - -def getdoc(obj): - """ - Return text documentation from an object. This comes in a form of - dictionary with four keys: - - name: - The name of the inspected object - argspec: - It's argspec - note: - A phrase describing the type of object (function or method) we are - inspecting, and the module it belongs to. - docstring: - It's docstring - """ - - docstring = inspect.getdoc(obj) or inspect.getcomments(obj) or '' - - # Most of the time doc will only contain ascii characters, but there are - # some docstrings that contain non-ascii characters. Not all source files - # declare their encoding in the first line, so querying for that might not - # yield anything, either. So assume the most commonly used - # multi-byte file encoding (which also covers ascii). - try: - docstring = to_text_string(docstring) - except: - pass - - # Doc dict keys - doc = {'name': '', - 'argspec': '', - 'note': '', - 'docstring': docstring} - - if callable(obj): - try: - name = obj.__name__ - except AttributeError: - doc['docstring'] = docstring - return doc - if inspect.ismethod(obj): - imclass = get_meth_class(obj) - if get_meth_class_inst(obj) is not None: - doc['note'] = 'Method of %s instance' \ - % get_meth_class_inst(obj).__class__.__name__ - else: - doc['note'] = 'Unbound %s method' % imclass.__name__ - obj = get_meth_func(obj) - elif hasattr(obj, '__module__'): - doc['note'] = 'Function of %s module' % obj.__module__ - else: - doc['note'] = 'Function' - doc['name'] = obj.__name__ - if inspect.isfunction(obj): - args, varargs, varkw, defaults = inspect.getargspec(obj) - doc['argspec'] = inspect.formatargspec(args, varargs, varkw, - defaults, - formatvalue=lambda o:'='+repr(o)) - if name == '': - doc['name'] = name + ' lambda ' - doc['argspec'] = doc['argspec'][1:-1] # remove parentheses - else: - argspec = getargspecfromtext(doc['docstring']) - if argspec: - doc['argspec'] = argspec - # Many scipy and numpy docstrings begin with a function - # signature on the first line. This ends up begin redundant - # when we are using title and argspec to create the - # rich text "Definition:" field. We'll carefully remove this - # redundancy but only under a strict set of conditions: - # Remove the starting charaters of the 'doc' portion *iff* - # the non-whitespace characters on the first line - # match *exactly* the combined function title - # and argspec we determined above. - signature = doc['name'] + doc['argspec'] - docstring_blocks = doc['docstring'].split("\n\n") - first_block = docstring_blocks[0].strip() - if first_block == signature: - doc['docstring'] = doc['docstring'].replace( - signature, '', 1).lstrip() - else: - doc['argspec'] = '(...)' - - # Remove self from argspec - argspec = doc['argspec'] - doc['argspec'] = argspec.replace('(self)', '()').replace('(self, ', '(') - - return doc - - -def getsource(obj): - """Wrapper around inspect.getsource""" - try: - try: - src = encoding.to_unicode( inspect.getsource(obj) ) - except TypeError: - if hasattr(obj, '__class__'): - src = encoding.to_unicode( inspect.getsource(obj.__class__) ) - else: - # Bindings like VTK or ITK require this case - src = getdoc(obj) - return src - except (TypeError, IOError): - return - - -def getsignaturefromtext(text, objname): - """Get object signatures from text (object documentation) - Return a list containing a single string in most cases - Example of multiple signatures: PyQt4 objects""" - if isinstance(text, dict): - text = text.get('docstring', '') - # Regexps - oneline_re = objname + r'\([^\)].+?(?<=[\w\]\}\'"])\)(?!,)' - multiline_re = objname + r'\([^\)]+(?<=[\w\]\}\'"])\)(?!,)' - multiline_end_parenleft_re = r'(%s\([^\)]+(\),\n.+)+(?<=[\w\]\}\'"])\))' - # Grabbing signatures - if not text: - text = '' - sigs_1 = re.findall(oneline_re + '|' + multiline_re, text) - sigs_2 = [g[0] for g in re.findall(multiline_end_parenleft_re % objname, text)] - all_sigs = sigs_1 + sigs_2 - # The most relevant signature is usually the first one. There could be - # others in doctests but those are not so important - if all_sigs: - return all_sigs[0] - else: - return '' - -# Fix for Issue 1953 -# TODO: Add more signatures and remove this hack in 2.4 -getsignaturesfromtext = getsignaturefromtext - - -def getargspecfromtext(text): - """ - Try to get the formatted argspec of a callable from the first block of its - docstring - - This will return something like - '(foo, bar, k=1)' - """ - blocks = text.split("\n\n") - first_block = blocks[0].strip() - return getsignaturefromtext(first_block, '') - - -def getargsfromtext(text, objname): - """Get arguments from text (object documentation)""" - signature = getsignaturefromtext(text, objname) - if signature: - argtxt = signature[signature.find('(')+1:-1] - return argtxt.split(',') - - -def getargsfromdoc(obj): - """Get arguments from object doc""" - if obj.__doc__ is not None: - return getargsfromtext(obj.__doc__, obj.__name__) - - -def getargs(obj): - """Get the names and default values of a function's arguments""" - if inspect.isfunction(obj) or inspect.isbuiltin(obj): - func_obj = obj - elif inspect.ismethod(obj): - func_obj = get_meth_func(obj) - elif inspect.isclass(obj) and hasattr(obj, '__init__'): - func_obj = getattr(obj, '__init__') - else: - return [] - if not hasattr(func_obj, 'func_code'): - # Builtin: try to extract info from doc - args = getargsfromdoc(func_obj) - if args is not None: - return args - else: - # Example: PyQt4 - return getargsfromdoc(obj) - args, _, _ = inspect.getargs(func_obj.func_code) - if not args: - return getargsfromdoc(obj) - - # Supporting tuple arguments in def statement: - for i_arg, arg in enumerate(args): - if isinstance(arg, list): - args[i_arg] = "(%s)" % ", ".join(arg) - - defaults = get_func_defaults(func_obj) - if defaults is not None: - for index, default in enumerate(defaults): - args[index+len(args)-len(defaults)] += '='+repr(default) - if inspect.isclass(obj) or inspect.ismethod(obj): - if len(args) == 1: - return None - if 'self' in args: - args.remove('self') - return args - - -def getargtxt(obj, one_arg_per_line=True): - """ - Get the names and default values of a function's arguments - Return list with separators (', ') formatted for calltips - """ - args = getargs(obj) - if args: - sep = ', ' - textlist = None - for i_arg, arg in enumerate(args): - if textlist is None: - textlist = [''] - textlist[-1] += arg - if i_arg < len(args)-1: - textlist[-1] += sep - if len(textlist[-1]) >= 32 or one_arg_per_line: - textlist.append('') - if inspect.isclass(obj) or inspect.ismethod(obj): - if len(textlist) == 1: - return None - if 'self'+sep in textlist: - textlist.remove('self'+sep) - return textlist - - -def isdefined(obj, force_import=False, namespace=None): - """Return True if object is defined in namespace - If namespace is None --> namespace = locals()""" - if namespace is None: - namespace = locals() - attr_list = obj.split('.') - base = attr_list.pop(0) - if len(base) == 0: - return False - if base not in builtins.__dict__ and base not in namespace: - if force_import: - try: - module = __import__(base, globals(), namespace) - if base not in globals(): - globals()[base] = module - namespace[base] = module - except (ImportError, SyntaxError): - return False - else: - return False - for attr in attr_list: - try: - attr_not_found = not hasattr(eval(base, namespace), attr) - except SyntaxError: - return False - if attr_not_found: - if force_import: - try: - __import__(base+'.'+attr, globals(), namespace) - except (ImportError, SyntaxError): - return False - else: - return False - base += '.'+attr - return True - - -if __name__ == "__main__": - class Test(object): - def method(self, x, y=2): - pass - print(getargtxt(Test.__init__)) - print(getargtxt(Test.method)) - print(isdefined('numpy.take', force_import=True)) - print(isdefined('__import__')) - print(isdefined('.keys', force_import=True)) - print(getobj('globals')) - print(getobj('globals().keys')) - print(getobj('+scipy.signal.')) - print(getobj('4.')) - print(getdoc(sorted)) - print(getargtxt(sorted)) diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/encoding.py spyder-3.0.2+dfsg1/spyderlib/utils/encoding.py --- spyder-2.3.8+dfsg1/spyderlib/utils/encoding.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/encoding.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,257 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Text encoding utilities, text file I/O - -Functions 'get_coding', 'decode', 'encode' and 'to_unicode' come from Eric4 -source code (Utilities/__init___.py) Copyright © 2003-2009 Detlev Offenbach -""" - -import re -import os -import locale -import sys -from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF32 - -# Local imports -from spyderlib.py3compat import (is_string, to_text_string, is_binary_string, - is_unicode) - - -PREFERRED_ENCODING = locale.getpreferredencoding() - -def transcode(text, input=PREFERRED_ENCODING, output=PREFERRED_ENCODING): - """Transcode a text string""" - try: - return text.decode("cp437").encode("cp1252") - except UnicodeError: - try: - return text.decode("cp437").encode(output) - except UnicodeError: - return text - -#------------------------------------------------------------------------------ -# Functions for encoding and decoding bytes that come from -# the *file system*. -#------------------------------------------------------------------------------ - -# The default encoding for file paths and environment variables should be set -# to match the default encoding that the OS is using. -def getfilesystemencoding(): - """ - Query the filesystem for the encoding used to encode filenames - and environment variables. - """ - encoding = sys.getfilesystemencoding() - if encoding is None: - # Must be Linux or Unix and nl_langinfo(CODESET) failed. - encoding = PREFERRED_ENCODING - return encoding - -FS_ENCODING = getfilesystemencoding() - -def to_unicode_from_fs(string): - """ - Return a unicode version of string decoded using the file system encoding. - """ - if not is_string(string): # string is a QString - string = to_text_string(string.toUtf8(), 'utf-8') - else: - if is_binary_string(string): - try: - unic = string.decode(FS_ENCODING) - except (UnicodeError, TypeError): - pass - else: - return unic - return string - -def to_fs_from_unicode(unic): - """ - Return a byte string version of unic encoded using the file - system encoding. - """ - if is_unicode(unic): - try: - string = unic.encode(FS_ENCODING) - except (UnicodeError, TypeError): - pass - else: - return string - return unic - -#------------------------------------------------------------------------------ -# Functions for encoding and decoding *text data* itself, usually originating -# from or destined for the *contents* of a file. -#------------------------------------------------------------------------------ - -# Codecs for working with files and text. -CODING_RE = re.compile(r"coding[:=]\s*([-\w_.]+)") -CODECS = ['utf-8', 'iso8859-1', 'iso8859-15', 'koi8-r', - 'koi8-u', 'iso8859-2', 'iso8859-3', 'iso8859-4', 'iso8859-5', - 'iso8859-6', 'iso8859-7', 'iso8859-8', 'iso8859-9', - 'iso8859-10', 'iso8859-13', 'iso8859-14', 'latin-1', - 'utf-16'] - -def get_coding(text): - """ - Function to get the coding of a text. - @param text text to inspect (string) - @return coding string - """ - for line in text.splitlines()[:2]: - result = CODING_RE.search(to_text_string(line)) - if result: - return result.group(1) - return None - -def decode(text): - """ - Function to decode a text. - @param text text to decode (string) - @return decoded text and encoding - """ - try: - if text.startswith(BOM_UTF8): - # UTF-8 with BOM - return to_text_string(text[len(BOM_UTF8):], 'utf-8'), 'utf-8-bom' - elif text.startswith(BOM_UTF16): - # UTF-16 with BOM - return to_text_string(text[len(BOM_UTF16):], 'utf-16'), 'utf-16' - elif text.startswith(BOM_UTF32): - # UTF-32 with BOM - return to_text_string(text[len(BOM_UTF32):], 'utf-32'), 'utf-32' - coding = get_coding(text) - if coding: - return to_text_string(text, coding), coding - except (UnicodeError, LookupError): - pass - # Assume UTF-8 - try: - return to_text_string(text, 'utf-8'), 'utf-8-guessed' - except (UnicodeError, LookupError): - pass - # Assume Latin-1 (behaviour before 3.7.1) - return to_text_string(text, "latin-1"), 'latin-1-guessed' - -def encode(text, orig_coding): - """ - Function to encode a text. - @param text text to encode (string) - @param orig_coding type of the original coding (string) - @return encoded text and encoding - """ - if orig_coding == 'utf-8-bom': - return BOM_UTF8 + text.encode("utf-8"), 'utf-8-bom' - - # Try declared coding spec - coding = get_coding(text) - if coding: - try: - return text.encode(coding), coding - except (UnicodeError, LookupError): - raise RuntimeError("Incorrect encoding (%s)" % coding) - if orig_coding and orig_coding.endswith('-default') or \ - orig_coding.endswith('-guessed'): - coding = orig_coding.replace("-default", "") - coding = orig_coding.replace("-guessed", "") - try: - return text.encode(coding), coding - except (UnicodeError, LookupError): - pass - - # Try saving as ASCII - try: - return text.encode('ascii'), 'ascii' - except UnicodeError: - pass - - # Save as UTF-8 without BOM - return text.encode('utf-8'), 'utf-8' - -def to_unicode(string): - """Convert a string to unicode""" - if not is_unicode(string): - for codec in CODECS: - try: - unic = to_text_string(string, codec) - except UnicodeError: - pass - except TypeError: - break - else: - return unic - return string - - -def write(text, filename, encoding='utf-8', mode='wb'): - """ - Write 'text' to file ('filename') assuming 'encoding' - Return (eventually new) encoding - """ - text, encoding = encode(text, encoding) - with open(filename, mode) as textfile: - textfile.write(text) - return encoding - -def writelines(lines, filename, encoding='utf-8', mode='wb'): - """ - Write 'lines' to file ('filename') assuming 'encoding' - Return (eventually new) encoding - """ - return write(os.linesep.join(lines), filename, encoding, mode) - -def read(filename, encoding='utf-8'): - """ - Read text from file ('filename') - Return text and encoding - """ - text, encoding = decode( open(filename, 'rb').read() ) - return text, encoding - -def readlines(filename, encoding='utf-8'): - """ - Read lines from file ('filename') - Return lines and encoding - """ - text, encoding = read(filename, encoding) - return text.split(os.linesep), encoding - - -def is_text_file(filename): - """ - Test if the given path is a text-like file. - - Adapted from: http://stackoverflow.com/a/3002505 - - Original Authors: Trent Mick - Jorge Orpinel - """ - try: - open(filename) - except Exception: - return False - with open(filename, 'rb') as fid: - try: - CHUNKSIZE = 1024 - chunk = fid.read(CHUNKSIZE) - # check for a UTF BOM - for bom in [BOM_UTF8, BOM_UTF16, BOM_UTF32]: - if chunk.startswith(bom): - return True - chunk = chunk.decode('utf-8') - while 1: - if '\0' in chunk: # found null byte - return False - if len(chunk) < CHUNKSIZE: - break # done - chunk = fid.read(CHUNKSIZE).decode('utf-8') - except UnicodeDecodeError: - return False - except Exception: - pass - return True diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/environ.py spyder-3.0.2+dfsg1/spyderlib/utils/environ.py --- spyder-2.3.8+dfsg1/spyderlib/utils/environ.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/environ.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Environment variable utilities -""" - -from spyderlib.qt.QtGui import QDialog, QMessageBox - -import os - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.widgets.dicteditor import DictEditor - - -def envdict2listdict(envdict): - """Dict --> Dict of lists""" - sep = os.path.pathsep - for key in envdict: - if sep in envdict[key]: - envdict[key] = [path.strip() for path in envdict[key].split(sep)] - return envdict - -def listdict2envdict(listdict): - """Dict of lists --> Dict""" - for key in listdict: - if isinstance(listdict[key], list): - listdict[key] = os.path.pathsep.join(listdict[key]) - return listdict - -class RemoteEnvDialog(DictEditor): - """Remote process environment variables Dialog""" - def __init__(self, get_environ_func, set_environ_func, parent=None): - super(RemoteEnvDialog, self).__init__(parent) - self.setup(envdict2listdict(get_environ_func()), - title="os.environ", width=600, icon='environ.png') - self.set_environ = set_environ_func - def accept(self): - """Reimplement Qt method""" - self.set_environ(listdict2envdict(self.get_value())) - QDialog.accept(self) - -class EnvDialog(RemoteEnvDialog): - """Environment variables Dialog""" - def __init__(self): - def get_environ_func(): - return dict(os.environ) - def set_environ_func(env): - os.environ = env - RemoteEnvDialog.__init__(self, get_environ_func, set_environ_func) - - -# For Windows only -try: - from spyderlib.py3compat import winreg - - def get_user_env(): - """Return HKCU (current user) environment variables""" - reg = dict() - key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment") - for index in range(0, winreg.QueryInfoKey(key)[1]): - try: - value = winreg.EnumValue(key, index) - reg[value[0]] = value[1] - except: - break - return envdict2listdict(reg) - - def set_user_env(reg, parent=None): - """Set HKCU (current user) environment variables""" - reg = listdict2envdict(reg) - types = dict() - key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment") - for name in reg: - try: - _x, types[name] = winreg.QueryValueEx(key, name) - except WindowsError: - types[name] = winreg.REG_EXPAND_SZ - key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0, - winreg.KEY_SET_VALUE) - for name in reg: - winreg.SetValueEx(key, name, 0, types[name], reg[name]) - try: - from win32gui import SendMessageTimeout - from win32con import (HWND_BROADCAST, WM_SETTINGCHANGE, - SMTO_ABORTIFHUNG) - SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, - "Environment", SMTO_ABORTIFHUNG, 5000) - except ImportError: - QMessageBox.warning(parent, _("Warning"), - _("Module pywin32 was not found.
    " - "Please restart this Windows session " - "(not the computer) for changes to take effect.")) - - class WinUserEnvDialog(DictEditor): - """Windows User Environment Variables Editor""" - def __init__(self, parent=None): - super(WinUserEnvDialog, self).__init__(parent) - self.setup(get_user_env(), - title="HKEY_CURRENT_USER\Environment", width=600) - if parent is None: - parent = self - QMessageBox.warning(parent, _("Warning"), - _("If you accept changes, " - "this will modify the current user environment " - "variables directly in Windows registry. " - "Use it with precautions, at your own risks.
    " - "
    Note that for changes to take effect, you will " - "need to restart the parent process of this applica" - "tion (simply restart Spyder if you have executed it " - "from a Windows shortcut, otherwise restart any " - "application from which you may have executed it, " - "like Python(x,y) Home for example)")) - - def accept(self): - """Reimplement Qt method""" - set_user_env( listdict2envdict(self.get_value()), parent=self ) - QDialog.accept(self) - -except ImportError: - pass - -def main(): - """Run Windows environment variable editor""" - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - dialog = WinUserEnvDialog() - dialog.show() - app.exec_() - -if __name__ == "__main__": - main() diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/external/__init__.py spyder-3.0.2+dfsg1/spyderlib/utils/external/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/utils/external/__init__.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/external/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.utils.external -======================== - -External libraries needed for Spyder to work. -Put here only untouched libraries, else put them in utils. -""" - -import os - -# Hack to be able to use our own versions of rope and pyflakes, -# included in our Windows installers -if os.name == 'nt': - import os.path as osp - import sys - from spyderlib.baseconfig import get_module_source_path - - dirname = get_module_source_path(__name__) - if osp.isdir(osp.join(dirname, 'rope')): - sys.path.insert(0, dirname) diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/external/lockfile.py spyder-3.0.2+dfsg1/spyderlib/utils/external/lockfile.py --- spyder-2.3.8+dfsg1/spyderlib/utils/external/lockfile.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/external/lockfile.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,231 +0,0 @@ -# Copyright (c) 2005 Divmod, Inc. -# Copyright (c) Twisted Matrix Laboratories. -# Copyright (c) 2013 The Spyder Development Team -# Twisted is distributed under the MIT license. - -""" -Filesystem-based interprocess mutex. - -Changes by the Spyder Team to the original Twisted file: --. Rewrite kill Windows function to make it more reliable -""" - -__metaclass__ = type - -import errno, os - -from time import time as _uniquefloat - -from spyderlib.py3compat import PY2, to_binary_string - -def unique(): - if PY2: - return str(long(_uniquefloat() * 1000)) - else: - return str(int(_uniquefloat() * 1000)) - -from os import rename -if not os.name == 'nt': - from os import kill - from os import symlink - from os import readlink - from os import remove as rmlink - _windows = False -else: - _windows = True - - import ctypes - from ctypes import wintypes - - # http://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx - PROCESS_QUERY_INFORMATION = 0x400 - - # GetExitCodeProcess uses a special exit code to indicate that the - # process is still running. - STILL_ACTIVE = 259 - - def _is_pid_running(pid): - """Taken from http://www.madebuild.org/blog/?p=30""" - kernel32 = ctypes.windll.kernel32 - handle = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid) - if handle == 0: - return False - - # If the process exited recently, a pid may still exist for the - # handle. So, check if we can get the exit code. - exit_code = wintypes.DWORD() - retval = kernel32.GetExitCodeProcess(handle, - ctypes.byref(exit_code)) - is_running = (retval == 0) - kernel32.CloseHandle(handle) - - # See if we couldn't get the exit code or the exit code indicates - # that the process is still running. - return is_running or exit_code.value == STILL_ACTIVE - - def kill(pid, signal): # analysis:ignore - if not _is_pid_running(pid): - raise OSError(errno.ESRCH, None) - else: - return - - _open = open - - # XXX Implement an atomic thingamajig for win32 - def symlink(value, filename): #analysis:ignore - newlinkname = filename+"."+unique()+'.newlink' - newvalname = os.path.join(newlinkname, "symlink") - os.mkdir(newlinkname) - f = _open(newvalname, 'wb') - f.write(to_binary_string(value)) - f.flush() - f.close() - try: - rename(newlinkname, filename) - except: - os.remove(newvalname) - os.rmdir(newlinkname) - raise - - def readlink(filename): #analysis:ignore - try: - fObj = _open(os.path.join(filename, 'symlink'), 'rb') - except IOError as e: - if e.errno == errno.ENOENT or e.errno == errno.EIO: - raise OSError(e.errno, None) - raise - else: - result = fObj.read().decode() - fObj.close() - return result - - def rmlink(filename): #analysis:ignore - os.remove(os.path.join(filename, 'symlink')) - os.rmdir(filename) - - - -class FilesystemLock: - """ - A mutex. - - This relies on the filesystem property that creating - a symlink is an atomic operation and that it will - fail if the symlink already exists. Deleting the - symlink will release the lock. - - @ivar name: The name of the file associated with this lock. - - @ivar clean: Indicates whether this lock was released cleanly by its - last owner. Only meaningful after C{lock} has been called and - returns True. - - @ivar locked: Indicates whether the lock is currently held by this - object. - """ - - clean = None - locked = False - - def __init__(self, name): - self.name = name - - def lock(self): - """ - Acquire this lock. - - @rtype: C{bool} - @return: True if the lock is acquired, false otherwise. - - @raise: Any exception os.symlink() may raise, other than - EEXIST. - """ - clean = True - while True: - try: - symlink(str(os.getpid()), self.name) - except OSError as e: - if _windows and e.errno in (errno.EACCES, errno.EIO): - # The lock is in the middle of being deleted because we're - # on Windows where lock removal isn't atomic. Give up, we - # don't know how long this is going to take. - return False - if e.errno == errno.EEXIST: - try: - pid = readlink(self.name) - except OSError as e: - if e.errno == errno.ENOENT: - # The lock has vanished, try to claim it in the - # next iteration through the loop. - continue - raise - except IOError as e: - if _windows and e.errno == errno.EACCES: - # The lock is in the middle of being - # deleted because we're on Windows where - # lock removal isn't atomic. Give up, we - # don't know how long this is going to - # take. - return False - raise - try: - if kill is not None: - kill(int(pid), 0) - except OSError as e: - if e.errno == errno.ESRCH: - # The owner has vanished, try to claim it in the next - # iteration through the loop. - try: - rmlink(self.name) - except OSError as e: - if e.errno == errno.ENOENT: - # Another process cleaned up the lock. - # Race them to acquire it in the next - # iteration through the loop. - continue - raise - clean = False - continue - raise - return False - raise - self.locked = True - self.clean = clean - return True - - def unlock(self): - """ - Release this lock. - - This deletes the directory with the given name. - - @raise: Any exception os.readlink() may raise, or - ValueError if the lock is not owned by this process. - """ - pid = readlink(self.name) - if int(pid) != os.getpid(): - raise ValueError("Lock %r not owned by this process" % (self.name,)) - rmlink(self.name) - self.locked = False - - -def isLocked(name): - """Determine if the lock of the given name is held or not. - - @type name: C{str} - @param name: The filesystem path to the lock to test - - @rtype: C{bool} - @return: True if the lock is held, False otherwise. - """ - l = FilesystemLock(name) - result = None - try: - result = l.lock() - finally: - if result: - l.unlock() - return not result - - -__all__ = ['FilesystemLock', 'isLocked'] diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/external/path.py spyder-3.0.2+dfsg1/spyderlib/utils/external/path.py --- spyder-2.3.8+dfsg1/spyderlib/utils/external/path.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/external/path.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1047 +0,0 @@ -# -# Copyright (c) 2010 Mikhail Gusarov -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# - -""" path.py - An object representing a path to a file or directory. - -Original author: - Jason Orendorff - -Contributors: - Mikhail Gusarov - Marc Abramowitz - -Example: - -from path import path -d = path('/home/guido/bin') -for f in d.files('*.py'): - f.chmod(0755) - -This module requires Python 2.3 or later. -""" - -from __future__ import generators - -import sys -import warnings -import os -import fnmatch -import glob -import shutil -import codecs -import hashlib -import errno - -from spyderlib.py3compat import (TEXT_TYPES, getcwd, u, - is_text_string, is_unicode) - -__version__ = '2.4.1' -__all__ = ['path'] - -MODE_0777 = 511 -MODE_0666 = 438 - -# Platform-specific support for path.owner -if os.name == 'nt': - try: - import win32security - except ImportError: - win32security = None -else: - try: - import pwd - except ImportError: - pwd = None - -_base = TEXT_TYPES[-1] -_getcwd = getcwd - -# Universal newline support -_textmode = 'U' -if hasattr(__builtins__, 'open') and not hasattr(open, 'newlines'): - _textmode = 'r' - -class TreeWalkWarning(Warning): - pass - -class path(_base): - """ Represents a filesystem path. - - For documentation on individual methods, consult their - counterparts in os.path. - """ - - # --- Special Python methods. - - def __repr__(self): - return 'path(%s)' % _base.__repr__(self) - - # Adding a path and a string yields a path. - def __add__(self, more): - try: - resultStr = _base.__add__(self, more) - except TypeError: # Python bug - resultStr = NotImplemented - if resultStr is NotImplemented: - return resultStr - return self.__class__(resultStr) - - def __radd__(self, other): - if is_text_string(other): - return self.__class__(other.__add__(self)) - else: - return NotImplemented - - # The / operator joins paths. - def __div__(self, rel): - """ fp.__div__(rel) == fp / rel == fp.joinpath(rel) - - Join two path components, adding a separator character if - needed. - """ - return self.__class__(os.path.join(self, rel)) - - # Make the / operator work even when true division is enabled. - __truediv__ = __div__ - - def __enter__(self): - self._old_dir = self.getcwd() - os.chdir(self) - - def __exit__(self, *_): - os.chdir(self._old_dir) - - def getcwd(cls): - """ Return the current working directory as a path object. """ - return cls(_getcwd()) - getcwd = classmethod(getcwd) - - # - # --- Operations on path strings. - - def abspath(self): return self.__class__(os.path.abspath(self)) - def normcase(self): return self.__class__(os.path.normcase(self)) - def normpath(self): return self.__class__(os.path.normpath(self)) - def realpath(self): return self.__class__(os.path.realpath(self)) - def expanduser(self): return self.__class__(os.path.expanduser(self)) - def expandvars(self): return self.__class__(os.path.expandvars(self)) - def dirname(self): return self.__class__(os.path.dirname(self)) - def basename(self): return self.__class__(os.path.basename(self)) - - def expand(self): - """ Clean up a filename by calling expandvars(), - expanduser(), and normpath() on it. - - This is commonly everything needed to clean up a filename - read from a configuration file, for example. - """ - return self.expandvars().expanduser().normpath() - - def _get_namebase(self): - base, ext = os.path.splitext(self.name) - return base - - def _get_ext(self): - f, ext = os.path.splitext(_base(self)) - return ext - - def _get_drive(self): - drive, r = os.path.splitdrive(self) - return self.__class__(drive) - - parent = property( - dirname, None, None, - """ This path's parent directory, as a new path object. - - For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib') - """) - - name = property( - basename, None, None, - """ The name of this file or directory without the full path. - - For example, path('/usr/local/lib/libpython.so').name == 'libpython.so' - """) - - namebase = property( - _get_namebase, None, None, - """ The same as path.name, but with one file extension stripped off. - - For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz', - but path('/home/guido/python.tar.gz').namebase == 'python.tar' - """) - - ext = property( - _get_ext, None, None, - """ The file extension, for example '.py'. """) - - drive = property( - _get_drive, None, None, - """ The drive specifier, for example 'C:'. - This is always empty on systems that don't use drive specifiers. - """) - - def splitpath(self): - """ p.splitpath() -> Return (p.parent, p.name). """ - parent, child = os.path.split(self) - return self.__class__(parent), child - - def splitdrive(self): - """ p.splitdrive() -> Return (p.drive, ). - - Split the drive specifier from this path. If there is - no drive specifier, p.drive is empty, so the return value - is simply (path(''), p). This is always the case on Unix. - """ - drive, rel = os.path.splitdrive(self) - return self.__class__(drive), rel - - def splitext(self): - """ p.splitext() -> Return (p.stripext(), p.ext). - - Split the filename extension from this path and return - the two parts. Either part may be empty. - - The extension is everything from '.' to the end of the - last path segment. This has the property that if - (a, b) == p.splitext(), then a + b == p. - """ - filename, ext = os.path.splitext(self) - return self.__class__(filename), ext - - def stripext(self): - """ p.stripext() -> Remove one file extension from the path. - - For example, path('/home/guido/python.tar.gz').stripext() - returns path('/home/guido/python.tar'). - """ - return self.splitext()[0] - - if hasattr(os.path, 'splitunc'): - def splitunc(self): - unc, rest = os.path.splitunc(self) - return self.__class__(unc), rest - - def _get_uncshare(self): - unc, r = os.path.splitunc(self) - return self.__class__(unc) - - uncshare = property( - _get_uncshare, None, None, - """ The UNC mount point for this path. - This is empty for paths on local drives. """) - - def joinpath(self, *args): - """ Join two or more path components, adding a separator - character (os.sep) if needed. Returns a new path - object. - """ - return self.__class__(os.path.join(self, *args)) - - def splitall(self): - r""" Return a list of the path components in this path. - - The first item in the list will be a path. Its value will be - either os.curdir, os.pardir, empty, or the root directory of - this path (for example, '/' or 'C:\\'). The other items in - the list will be strings. - - path.path.joinpath(*result) will yield the original path. - """ - parts = [] - loc = self - while loc != os.curdir and loc != os.pardir: - prev = loc - loc, child = prev.splitpath() - if loc == prev: - break - parts.append(child) - parts.append(loc) - parts.reverse() - return parts - - def relpath(self): - """ Return this path as a relative path, - based from the current working directory. - """ - cwd = self.__class__(os.getcwd()) - return cwd.relpathto(self) - - def relpathto(self, dest): - """ Return a relative path from self to dest. - - If there is no relative path from self to dest, for example if - they reside on different drives in Windows, then this returns - dest.abspath(). - """ - origin = self.abspath() - dest = self.__class__(dest).abspath() - - orig_list = origin.normcase().splitall() - # Don't normcase dest! We want to preserve the case. - dest_list = dest.splitall() - - if orig_list[0] != os.path.normcase(dest_list[0]): - # Can't get here from there. - return dest - - # Find the location where the two paths start to differ. - i = 0 - for start_seg, dest_seg in zip(orig_list, dest_list): - if start_seg != os.path.normcase(dest_seg): - break - i += 1 - - # Now i is the point where the two paths diverge. - # Need a certain number of "os.pardir"s to work up - # from the origin to the point of divergence. - segments = [os.pardir] * (len(orig_list) - i) - # Need to add the diverging part of dest_list. - segments += dest_list[i:] - if len(segments) == 0: - # If they happen to be identical, use os.curdir. - relpath = os.curdir - else: - relpath = os.path.join(*segments) - return self.__class__(relpath) - - # --- Listing, searching, walking, and matching - - def listdir(self, pattern=None): - """ D.listdir() -> List of items in this directory. - - Use D.files() or D.dirs() instead if you want a listing - of just files or just subdirectories. - - The elements of the list are path objects. - - With the optional 'pattern' argument, this only lists - items whose names match the given pattern. - """ - names = os.listdir(self) - if pattern is not None: - names = fnmatch.filter(names, pattern) - return [self / child for child in names] - - def dirs(self, pattern=None): - """ D.dirs() -> List of this directory's subdirectories. - - The elements of the list are path objects. - This does not walk recursively into subdirectories - (but see path.walkdirs). - - With the optional 'pattern' argument, this only lists - directories whose names match the given pattern. For - example, d.dirs('build-*'). - """ - return [p for p in self.listdir(pattern) if p.isdir()] - - def files(self, pattern=None): - """ D.files() -> List of the files in this directory. - - The elements of the list are path objects. - This does not walk into subdirectories (see path.walkfiles). - - With the optional 'pattern' argument, this only lists files - whose names match the given pattern. For example, - d.files('*.pyc'). - """ - - return [p for p in self.listdir(pattern) if p.isfile()] - - def walk(self, pattern=None, errors='strict'): - """ D.walk() -> iterator over files and subdirs, recursively. - - The iterator yields path objects naming each child item of - this directory and its descendants. This requires that - D.isdir(). - - This performs a depth-first traversal of the directory tree. - Each directory is returned just before all its children. - - The errors= keyword argument controls behavior when an - error occurs. The default is 'strict', which causes an - exception. The other allowed values are 'warn', which - reports the error via warnings.warn(), and 'ignore'. - """ - if errors not in ('strict', 'warn', 'ignore'): - raise ValueError("invalid errors parameter") - - try: - childList = self.listdir() - except Exception: - if errors == 'ignore': - return - elif errors == 'warn': - warnings.warn( - "Unable to list directory '%s': %s" - % (self, sys.exc_info()[1]), - TreeWalkWarning) - return - else: - raise - - for child in childList: - if pattern is None or child.fnmatch(pattern): - yield child - try: - isdir = child.isdir() - except Exception: - if errors == 'ignore': - isdir = False - elif errors == 'warn': - warnings.warn( - "Unable to access '%s': %s" - % (child, sys.exc_info()[1]), - TreeWalkWarning) - isdir = False - else: - raise - - if isdir: - for item in child.walk(pattern, errors): - yield item - - def walkdirs(self, pattern=None, errors='strict'): - """ D.walkdirs() -> iterator over subdirs, recursively. - - With the optional 'pattern' argument, this yields only - directories whose names match the given pattern. For - example, mydir.walkdirs('*test') yields only directories - with names ending in 'test'. - - The errors= keyword argument controls behavior when an - error occurs. The default is 'strict', which causes an - exception. The other allowed values are 'warn', which - reports the error via warnings.warn(), and 'ignore'. - """ - if errors not in ('strict', 'warn', 'ignore'): - raise ValueError("invalid errors parameter") - - try: - dirs = self.dirs() - except Exception: - if errors == 'ignore': - return - elif errors == 'warn': - warnings.warn( - "Unable to list directory '%s': %s" - % (self, sys.exc_info()[1]), - TreeWalkWarning) - return - else: - raise - - for child in dirs: - if pattern is None or child.fnmatch(pattern): - yield child - for subsubdir in child.walkdirs(pattern, errors): - yield subsubdir - - def walkfiles(self, pattern=None, errors='strict'): - """ D.walkfiles() -> iterator over files in D, recursively. - - The optional argument, pattern, limits the results to files - with names that match the pattern. For example, - mydir.walkfiles('*.tmp') yields only files with the .tmp - extension. - """ - if errors not in ('strict', 'warn', 'ignore'): - raise ValueError("invalid errors parameter") - - try: - childList = self.listdir() - except Exception: - if errors == 'ignore': - return - elif errors == 'warn': - warnings.warn( - "Unable to list directory '%s': %s" - % (self, sys.exc_info()[1]), - TreeWalkWarning) - return - else: - raise - - for child in childList: - try: - isfile = child.isfile() - isdir = not isfile and child.isdir() - except: - if errors == 'ignore': - continue - elif errors == 'warn': - warnings.warn( - "Unable to access '%s': %s" - % (self, sys.exc_info()[1]), - TreeWalkWarning) - continue - else: - raise - - if isfile: - if pattern is None or child.fnmatch(pattern): - yield child - elif isdir: - for f in child.walkfiles(pattern, errors): - yield f - - def fnmatch(self, pattern): - """ Return True if self.name matches the given pattern. - - pattern - A filename pattern with wildcards, - for example '*.py'. - """ - return fnmatch.fnmatch(self.name, pattern) - - def glob(self, pattern): - """ Return a list of path objects that match the pattern. - - pattern - a path relative to this directory, with wildcards. - - For example, path('/users').glob('*/bin/*') returns a list - of all the files users have in their bin directories. - """ - cls = self.__class__ - return [cls(s) for s in glob.glob(_base(self / pattern))] - - # - # --- Reading or writing an entire file at once. - - def open(self, mode='r'): - """ Open this file. Return a file object. """ - return open(self, mode) - - def bytes(self): - """ Open this file, read all bytes, return them as a string. """ - f = self.open('rb') - try: - return f.read() - finally: - f.close() - - def write_bytes(self, bytes, append=False): - """ Open this file and write the given bytes to it. - - Default behavior is to overwrite any existing file. - Call p.write_bytes(bytes, append=True) to append instead. - """ - if append: - mode = 'ab' - else: - mode = 'wb' - f = self.open(mode) - try: - f.write(bytes) - finally: - f.close() - - def text(self, encoding=None, errors='strict'): - r""" Open this file, read it in, return the content as a string. - - This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r' - are automatically translated to '\n'. - - Optional arguments: - - encoding - The Unicode encoding (or character set) of - the file. If present, the content of the file is - decoded and returned as a unicode object; otherwise - it is returned as an 8-bit str. - errors - How to handle Unicode errors; see help(str.decode) - for the options. Default is 'strict'. - """ - if encoding is None: - # 8-bit - f = self.open(_textmode) - try: - return f.read() - finally: - f.close() - else: - # Unicode - f = codecs.open(self, 'r', encoding, errors) - # (Note - Can't use 'U' mode here, since codecs.open - # doesn't support 'U' mode, even in Python 2.3.) - try: - t = f.read() - finally: - f.close() - return (t.replace(u('\r\n'), u('\n')) - .replace(u('\r\x85'), u('\n')) - .replace(u('\r'), u('\n')) - .replace(u('\x85'), u('\n')) - .replace(u('\u2028'), u('\n'))) - - def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False): - r""" Write the given text to this file. - - The default behavior is to overwrite any existing file; - to append instead, use the 'append=True' keyword argument. - - There are two differences between path.write_text() and - path.write_bytes(): newline handling and Unicode handling. - See below. - - Parameters: - - - text - str/unicode - The text to be written. - - - encoding - str - The Unicode encoding that will be used. - This is ignored if 'text' isn't a Unicode string. - - - errors - str - How to handle Unicode encoding errors. - Default is 'strict'. See help(unicode.encode) for the - options. This is ignored if 'text' isn't a Unicode - string. - - - linesep - keyword argument - str/unicode - The sequence of - characters to be used to mark end-of-line. The default is - os.linesep. You can also specify None; this means to - leave all newlines as they are in 'text'. - - - append - keyword argument - bool - Specifies what to do if - the file already exists (True: append to the end of it; - False: overwrite it.) The default is False. - - - --- Newline handling. - - write_text() converts all standard end-of-line sequences - ('\n', '\r', and '\r\n') to your platform's default end-of-line - sequence (see os.linesep; on Windows, for example, the - end-of-line marker is '\r\n'). - - If you don't like your platform's default, you can override it - using the 'linesep=' keyword argument. If you specifically want - write_text() to preserve the newlines as-is, use 'linesep=None'. - - This applies to Unicode text the same as to 8-bit text, except - there are three additional standard Unicode end-of-line sequences: - u'\x85', u'\r\x85', and u'\u2028'. - - (This is slightly different from when you open a file for - writing with fopen(filename, "w") in C or open(filename, 'w') - in Python.) - - - --- Unicode - - If 'text' isn't Unicode, then apart from newline handling, the - bytes are written verbatim to the file. The 'encoding' and - 'errors' arguments are not used and must be omitted. - - If 'text' is Unicode, it is first converted to bytes using the - specified 'encoding' (or the default encoding if 'encoding' - isn't specified). The 'errors' argument applies only to this - conversion. - - """ - if is_unicode(text): - if linesep is not None: - # Convert all standard end-of-line sequences to - # ordinary newline characters. - text = (text.replace(u('\r\n'), u('\n')) - .replace(u('\r\x85'), u('\n')) - .replace(u('\r'), u('\n')) - .replace(u('\x85'), u('\n')) - .replace(u('\u2028'), u('\n'))) - text = text.replace(u('\n'), linesep) - if encoding is None: - encoding = sys.getdefaultencoding() - bytes = text.encode(encoding, errors) - else: - # It is an error to specify an encoding if 'text' is - # an 8-bit string. - assert encoding is None - - if linesep is not None: - text = (text.replace('\r\n', '\n') - .replace('\r', '\n')) - bytes = text.replace('\n', linesep) - - self.write_bytes(bytes, append) - - def lines(self, encoding=None, errors='strict', retain=True): - r""" Open this file, read all lines, return them in a list. - - Optional arguments: - encoding - The Unicode encoding (or character set) of - the file. The default is None, meaning the content - of the file is read as 8-bit characters and returned - as a list of (non-Unicode) str objects. - errors - How to handle Unicode errors; see help(str.decode) - for the options. Default is 'strict' - retain - If true, retain newline characters; but all newline - character combinations ('\r', '\n', '\r\n') are - translated to '\n'. If false, newline characters are - stripped off. Default is True. - - This uses 'U' mode in Python 2.3 and later. - """ - if encoding is None and retain: - f = self.open(_textmode) - try: - return f.readlines() - finally: - f.close() - else: - return self.text(encoding, errors).splitlines(retain) - - def write_lines(self, lines, encoding=None, errors='strict', - linesep=os.linesep, append=False): - r""" Write the given lines of text to this file. - - By default this overwrites any existing file at this path. - - This puts a platform-specific newline sequence on every line. - See 'linesep' below. - - lines - A list of strings. - - encoding - A Unicode encoding to use. This applies only if - 'lines' contains any Unicode strings. - - errors - How to handle errors in Unicode encoding. This - also applies only to Unicode strings. - - linesep - The desired line-ending. This line-ending is - applied to every line. If a line already has any - standard line ending ('\r', '\n', '\r\n', u'\x85', - u'\r\x85', u'\u2028'), that will be stripped off and - this will be used instead. The default is os.linesep, - which is platform-dependent ('\r\n' on Windows, '\n' on - Unix, etc.) Specify None to write the lines as-is, - like file.writelines(). - - Use the keyword argument append=True to append lines to the - file. The default is to overwrite the file. Warning: - When you use this with Unicode data, if the encoding of the - existing data in the file is different from the encoding - you specify with the encoding= parameter, the result is - mixed-encoding data, which can really confuse someone trying - to read the file later. - """ - if append: - mode = 'ab' - else: - mode = 'wb' - f = self.open(mode) - try: - for line in lines: - isUnicode = is_unicode(line) - if linesep is not None: - # Strip off any existing line-end and add the - # specified linesep string. - if isUnicode: - if line[-2:] in (u('\r\n'), u('\x0d\x85')): - line = line[:-2] - elif line[-1:] in (u('\r'), u('\n'), - u('\x85'), u('\u2028')): - line = line[:-1] - else: - if line[-2:] == '\r\n': - line = line[:-2] - elif line[-1:] in ('\r', '\n'): - line = line[:-1] - line += linesep - if isUnicode: - if encoding is None: - encoding = sys.getdefaultencoding() - line = line.encode(encoding, errors) - f.write(line) - finally: - f.close() - - def read_md5(self): - """ Calculate the md5 hash for this file. - - This reads through the entire file. - """ - return self.read_hash('md5') - - def _hash(self, hash_name): - f = self.open('rb') - try: - m = hashlib.new(hash_name) - while True: - d = f.read(8192) - if not d: - break - m.update(d) - return m - finally: - f.close() - - def read_hash(self, hash_name): - """ Calculate given hash for this file. - - List of supported hashes can be obtained from hashlib package. This - reads the entire file. - """ - return self._hash(hash_name).digest() - - def read_hexhash(self, hash_name): - """ Calculate given hash for this file, returning hexdigest. - - List of supported hashes can be obtained from hashlib package. This - reads the entire file. - """ - return self._hash(hash_name).hexdigest() - - # --- Methods for querying the filesystem. - # N.B. On some platforms, the os.path functions may be implemented in C - # (e.g. isdir on Windows, Python 3.2.2), and compiled functions don't get - # bound. Playing it safe and wrapping them all in method calls. - - def isabs(self): return os.path.isabs(self) - def exists(self): return os.path.exists(self) - def isdir(self): return os.path.isdir(self) - def isfile(self): return os.path.isfile(self) - def islink(self): return os.path.islink(self) - def ismount(self): return os.path.ismount(self) - - if hasattr(os.path, 'samefile'): - def samefile(self): return os.path.samefile(self) - - def getatime(self): return os.path.getatime(self) - atime = property( - getatime, None, None, - """ Last access time of the file. """) - - def getmtime(self): return os.path.getmtime(self) - mtime = property( - getmtime, None, None, - """ Last-modified time of the file. """) - - if hasattr(os.path, 'getctime'): - def getctime(self): return os.path.getctime(self) - ctime = property( - getctime, None, None, - """ Creation time of the file. """) - - def getsize(self): return os.path.getsize(self) - size = property( - getsize, None, None, - """ Size of the file, in bytes. """) - - if hasattr(os, 'access'): - def access(self, mode): - """ Return true if current user has access to this path. - - mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK - """ - return os.access(self, mode) - - def stat(self): - """ Perform a stat() system call on this path. """ - return os.stat(self) - - def lstat(self): - """ Like path.stat(), but do not follow symbolic links. """ - return os.lstat(self) - - def get_owner(self): - r""" Return the name of the owner of this file or directory. - - This follows symbolic links. - - On Windows, this returns a name of the form ur'DOMAIN\User Name'. - On Windows, a group can own a file or directory. - """ - if os.name == 'nt': - if win32security is None: - raise Exception("path.owner requires win32all to be installed") - desc = win32security.GetFileSecurity( - self, win32security.OWNER_SECURITY_INFORMATION) - sid = desc.GetSecurityDescriptorOwner() - account, domain, typecode = win32security.LookupAccountSid(None, sid) - return domain + u('\\') + account - else: - if pwd is None: - raise NotImplementedError("path.owner is not implemented on this platform.") - st = self.stat() - return pwd.getpwuid(st.st_uid).pw_name - - owner = property( - get_owner, None, None, - """ Name of the owner of this file or directory. """) - - if hasattr(os, 'statvfs'): - def statvfs(self): - """ Perform a statvfs() system call on this path. """ - return os.statvfs(self) - - if hasattr(os, 'pathconf'): - def pathconf(self, name): - return os.pathconf(self, name) - - # - # --- Modifying operations on files and directories - - def utime(self, times): - """ Set the access and modified times of this file. """ - os.utime(self, times) - - def chmod(self, mode): - os.chmod(self, mode) - - if hasattr(os, 'chown'): - def chown(self, uid, gid): - os.chown(self, uid, gid) - - def rename(self, new): - os.rename(self, new) - - def renames(self, new): - os.renames(self, new) - - # - # --- Create/delete operations on directories - - def mkdir(self, mode=MODE_0777): - os.mkdir(self, mode) - - def mkdir_p(self, mode=MODE_0777): - try: - self.mkdir(mode) - except OSError as e: - if e.errno != errno.EEXIST: - raise - - def makedirs(self, mode=MODE_0777): - os.makedirs(self, mode) - - def makedirs_p(self, mode=MODE_0777): - try: - self.makedirs(mode) - except OSError as e: - if e.errno != errno.EEXIST: - raise - - def rmdir(self): - os.rmdir(self) - - def rmdir_p(self): - try: - self.rmdir() - except OSError as e: - if e.errno != errno.ENOTEMPTY and e.errno != errno.EEXIST: - raise - - def removedirs(self): - os.removedirs(self) - - def removedirs_p(self): - try: - self.removedirs() - except OSError as e: - if e.errno != errno.ENOTEMPTY and e.errno != errno.EEXIST: - raise - - # --- Modifying operations on files - - def touch(self): - """ Set the access/modified times of this file to the current time. - Create the file if it does not exist. - """ - fd = os.open(self, os.O_WRONLY | os.O_CREAT, MODE_0666) - os.close(fd) - os.utime(self, None) - - def remove(self): - os.remove(self) - - def remove_p(self): - try: - self.unlink() - except OSError as e: - if e.errno != errno.ENOENT: - raise - - def unlink(self): - os.unlink(self) - - def unlink_p(self): - self.remove_p() - - # --- Links - - if hasattr(os, 'link'): - def link(self, newpath): - """ Create a hard link at 'newpath', pointing to this file. """ - os.link(self, newpath) - - if hasattr(os, 'symlink'): - def symlink(self, newlink): - """ Create a symbolic link at 'newlink', pointing here. """ - os.symlink(self, newlink) - - if hasattr(os, 'readlink'): - def readlink(self): - """ Return the path to which this symbolic link points. - - The result may be an absolute or a relative path. - """ - return self.__class__(os.readlink(self)) - - def readlinkabs(self): - """ Return the path to which this symbolic link points. - - The result is always an absolute path. - """ - p = self.readlink() - if p.isabs(): - return p - else: - return (self.parent / p).abspath() - - # - # --- High-level functions from shutil - - copyfile = shutil.copyfile - copymode = shutil.copymode - copystat = shutil.copystat - copy = shutil.copy - copy2 = shutil.copy2 - copytree = shutil.copytree - if hasattr(shutil, 'move'): - move = shutil.move - rmtree = shutil.rmtree - - def rmtree_p(self): - try: - self.rmtree() - except OSError as e: - if e.errno != errno.ENOENT: - raise - - # - # --- Special stuff from os - - if hasattr(os, 'chroot'): - def chroot(self): - os.chroot(self) - - if hasattr(os, 'startfile'): - def startfile(self): - os.startfile(self) diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/external/pickleshare.py spyder-3.0.2+dfsg1/spyderlib/utils/external/pickleshare.py --- spyder-2.3.8+dfsg1/spyderlib/utils/external/pickleshare.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/external/pickleshare.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,370 +0,0 @@ -#!/usr/bin/env python - -""" PickleShare - a small 'shelve' like datastore with concurrency support - -Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike -shelve, many processes can access the database simultaneously. Changing a -value in database is immediately visible to other processes accessing the -same database. - -Concurrency is possible because the values are stored in separate files. Hence -the "database" is a directory where *all* files are governed by PickleShare. - -Example usage:: - - from pickleshare import * - db = PickleShareDB('~/testpickleshare') - db.clear() - print "Should be empty:",db.items() - db['hello'] = 15 - db['aku ankka'] = [1,2,313] - db['paths/are/ok/key'] = [1,(5,46)] - print db.keys() - del db['aku ankka'] - -This module is certainly not ZODB, but can be used for low-load -(non-mission-critical) situations where tiny code size trumps the -advanced features of a "real" object database. - -Installation guide: easy_install pickleshare - -Author: Ville Vainio -License: MIT open source license. - -""" - -from __future__ import print_function - -from spyderlib.utils.external.path import path as Path -from spyderlib.py3compat import pickle, MutableMapping - -import os -import stat -import time -import glob -import errno - -def gethashfile(key): - return ("%02x" % abs(hash(key) % 256))[-2:] - -_sentinel = object() - -class PickleShareDB(MutableMapping): - """ The main 'connection' object for PickleShare database """ - def __init__(self, root): - """ Return a db object that will manage the specied directory""" - self.root = Path(root).expanduser().abspath() - if not self.root.isdir(): - self.root.makedirs() - # cache has { 'key' : (obj, orig_mod_time) } - self.cache = {} - - #========================================================================== - # Only affects Python 3 - def __iter__(self): - return iter(self.cache) - def __len__(self): - return len(self.cache) - #========================================================================== - - def __getitem__(self, key): - """ db['key'] reading """ - fil = self.root / key - try: - mtime = (fil.stat()[stat.ST_MTIME]) - except OSError: - raise KeyError(key) - - if fil in self.cache and mtime == self.cache[fil][1]: - return self.cache[fil][0] - try: - # The cached item has expired, need to read - obj = pickle.load(fil.open('rb')) - except: - raise KeyError(key) - - self.cache[fil] = (obj, mtime) - return obj - - def __setitem__(self, key, value): - """ db['key'] = 5 """ - fil = self.root / key - parent = fil.parent - if parent and not parent.isdir(): - parent.makedirs() - # We specify protocol 2, so that we can mostly go between Python 2 - # and Python 3. We can upgrade to protocol 3 when Python 2 is obsolete. - pickled = pickle.dump(value, fil.open('wb'), protocol=2) - try: - self.cache[fil] = (value, fil.mtime) - except OSError as e: - if e.errno != errno.ENOENT: - raise - - def hset(self, hashroot, key, value): - """ hashed set """ - hroot = self.root / hashroot - if not hroot.isdir(): - hroot.makedirs() - hfile = hroot / gethashfile(key) - d = self.get(hfile, {}) - d.update( {key : value}) - self[hfile] = d - - - - def hget(self, hashroot, key, default = _sentinel, fast_only = True): - """ hashed get """ - hroot = self.root / hashroot - hfile = hroot / gethashfile(key) - - d = self.get(hfile, _sentinel ) - #print "got dict",d,"from",hfile - if d is _sentinel: - if fast_only: - if default is _sentinel: - raise KeyError(key) - - return default - - # slow mode ok, works even after hcompress() - d = self.hdict(hashroot) - - return d.get(key, default) - - def hdict(self, hashroot): - """ Get all data contained in hashed category 'hashroot' as dict """ - hfiles = self.keys(hashroot + "/*") - hfiles.sort() - last = len(hfiles) and hfiles[-1] or '' - if last.endswith('xx'): - # print "using xx" - hfiles = [last] + hfiles[:-1] - - all = {} - - for f in hfiles: - # print "using",f - try: - all.update(self[f]) - except KeyError: - print("Corrupt", f, "deleted - hset is not threadsafe!") - del self[f] - - self.uncache(f) - - return all - - def hcompress(self, hashroot): - """ Compress category 'hashroot', so hset is fast again - - hget will fail if fast_only is True for compressed items (that were - hset before hcompress). - - """ - hfiles = self.keys(hashroot + "/*") - all = {} - for f in hfiles: - # print "using",f - all.update(self[f]) - self.uncache(f) - - self[hashroot + '/xx'] = all - for f in hfiles: - p = self.root / f - if p.basename() == 'xx': - continue - p.remove() - - - - def __delitem__(self, key): - """ del db["key"] """ - fil = self.root / key - self.cache.pop(fil, None) - try: - fil.remove() - except OSError: - # notfound and permission denied are ok - we - # lost, the other process wins the conflict - pass - - def _normalized(self, p): - """ Make a key suitable for user's eyes """ - return str(self.root.relpathto(p)).replace('\\', '/') - - def keys(self, globpat = None): - """ All keys in DB, or all keys matching a glob""" - - if globpat is None: - files = self.root.walkfiles() - else: - files = [Path(p) for p in glob.glob(self.root/globpat)] - return [self._normalized(p) for p in files if p.isfile()] - - def uncache(self,*items): - """ Removes all, or specified items from cache - - Use this after reading a large amount of large objects - to free up memory, when you won't be needing the objects - for a while. - - """ - if not items: - self.cache = {} - for it in items: - self.cache.pop(it, None) - - def waitget(self,key, maxwaittime = 60 ): - """ Wait (poll) for a key to get a value - - Will wait for `maxwaittime` seconds before raising a KeyError. - The call exits normally if the `key` field in db gets a value - within the timeout period. - - Use this for synchronizing different processes or for ensuring - that an unfortunately timed "db['key'] = newvalue" operation - in another process (which causes all 'get' operation to cause a - KeyError for the duration of pickling) won't screw up your program - logic. - """ - - wtimes = [0.2] * 3 + [0.5] * 2 + [1] - tries = 0 - waited = 0 - while 1: - try: - val = self[key] - return val - except KeyError: - pass - - if waited > maxwaittime: - raise KeyError(key) - - time.sleep(wtimes[tries]) - waited+=wtimes[tries] - if tries < len(wtimes) -1: - tries+=1 - - def getlink(self, folder): - """ Get a convenient link for accessing items """ - return PickleShareLink(self, folder) - - def __repr__(self): - return "PickleShareDB('%s')" % self.root - - - -class PickleShareLink: - """ A shortdand for accessing nested PickleShare data conveniently. - - Created through PickleShareDB.getlink(), example:: - - lnk = db.getlink('myobjects/test') - lnk.foo = 2 - lnk.bar = lnk.foo + 5 - - """ - def __init__(self, db, keydir ): - self.__dict__.update(locals()) - - def __getattr__(self, key): - return self.__dict__['db'][self.__dict__['keydir']+'/' + key] - def __setattr__(self, key, val): - self.db[self.keydir+'/' + key] = val - def __repr__(self): - db = self.__dict__['db'] - keys = db.keys( self.__dict__['keydir'] +"/*") - return "" % ( - self.__dict__['keydir'], - ";".join([Path(k).basename() for k in keys])) - - -def test(): - db = PickleShareDB('~/testpickleshare') - db.clear() - print("Should be empty:", list(db.items())) - db['hello'] = 15 - db['aku ankka'] = [1, 2, 313] - db['paths/nest/ok/keyname'] = [1, (5, 46)] - db.hset('hash', 'aku', 12) - db.hset('hash', 'ankka', 313) - print("12 =", db.hget('hash', 'aku')) - print("313 =", db.hget('hash', 'ankka')) - print("all hashed", db.hdict('hash')) - print(list(db.keys())) - print(db.keys('paths/nest/ok/k*')) - print(dict(db)) # snapsot of whole db - db.uncache() # frees memory, causes re-reads later - - # shorthand for accessing deeply nested files - lnk = db.getlink('myobjects/test') - lnk.foo = 2 - lnk.bar = lnk.foo + 5 - print(lnk.bar) # 7 - -def stress(): - db = PickleShareDB('~/fsdbtest') - import time, sys - for i in range(1000): - for j in range(1000): - if i % 15 == 0 and i < 200: - if str(j) in db: - del db[str(j)] - continue - - if j%33 == 0: - time.sleep(0.02) - - db[str(j)] = db.get(str(j), []) + [(i, j, "proc %d" % os.getpid())] - db.hset('hash', j, db.hget('hash', j, 15) + 1 ) - - print(i, end=' ') - sys.stdout.flush() - if i % 10 == 0: - db.uncache() - -def main(): - import textwrap - usage = textwrap.dedent("""\ - pickleshare - manage PickleShare databases - - Usage: - - pickleshare dump /path/to/db > dump.txt - pickleshare load /path/to/db < dump.txt - pickleshare test /path/to/db - """) - DB = PickleShareDB - import sys - if len(sys.argv) < 2: - print(usage) - return - - cmd = sys.argv[1] - args = sys.argv[2:] - if cmd == 'dump': - if not args: args= ['.'] - db = DB(args[0]) - import pprint - pprint.pprint(list(db.items())) - elif cmd == 'load': - cont = sys.stdin.read() - db = DB(args[0]) - data = eval(cont) - db.clear() - for k, v in list(db.items()): - db[k] = v - elif cmd == 'testwait': - db = DB(args[0]) - db.clear() - print(db.waitget('250')) - elif cmd == 'test': - test() - stress() - -if __name__== "__main__": - main() - - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/__init__.py spyder-3.0.2+dfsg1/spyderlib/utils/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/utils/__init__.py 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.utils -=============== - -Spyder utilities -""" diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/conf.py spyder-3.0.2+dfsg1/spyderlib/utils/inspector/conf.py --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/conf.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/conf.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,116 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2009 Tim Dumol -# Copyright (C) 2013 The Spyder Development Team -# Distributed under the terms of the BSD License - -"""Sphinx conf file for the object inspector rich text mode""" - -# 3rd party imports -from sphinx import __version__ as sphinx_version - -# Local imports -from spyderlib.config import CONF -from spyderlib.py3compat import u - -#============================================================================== -# General configuration -#============================================================================== - -# If your extensions are in another directory, add it here. If the directory -# is relative to the documentation root, use os.path.abspath to make it -# absolute, like shown here. -#sys.path.append(os.path.abspath('.')) - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. - -# We need jsmath to get pretty plain-text latex in docstrings -math = CONF.get('inspector', 'math', '') - -if sphinx_version < "1.1" or not math: - extensions = ['sphinx.ext.jsmath'] -else: - extensions = ['sphinx.ext.mathjax'] - -# For scipy and matplotlib docstrings, which need this extension to -# be rendered correctly (see Issue 1138) -extensions.append('sphinx.ext.autosummary') - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['templates'] - -# MathJax load path (doesn't have effect for sphinx 1.0-) -mathjax_path = 'MathJax/MathJax.js' - -# JsMath load path (doesn't have effect for sphinx 1.1+) -jsmath_path = 'easy/load.js' - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'docstring' - -# General information about the project. -project = u("Object Inspector") -copyright = u('2009--2013, The Spyder Development Team') - -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = ['.build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -# -# TODO: This role has to be set on a per project basis, i.e. numpy, sympy, -# mpmath, etc, use different default_role's which give different rendered -# docstrings. Setting this to None until it's solved. -default_role = 'None' - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -#============================================================================== -# Options for HTML output -#============================================================================== - -# The style sheet to use for HTML and HTML Help pages. A file of that name -# must exist either in Sphinx' static/ path, or in one of the custom paths -# given in html_static_path. -html_style = 'default.css' - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -html_short_title = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['static'] - -# A dictionary of values to pass into the template engine’s context for all -# pages -html_context = {} - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# If false, no module index is generated. -html_use_modindex = False - -# If false, no index is generated. -html_use_index = False - -# If true, the index is split into individual pages for each letter. -html_split_index = False - -# If true, the reST sources are included in the HTML build as _sources/. -html_copy_source = False - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/__init__.py spyder-3.0.2+dfsg1/spyderlib/utils/inspector/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/__init__.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 Spyder Development team -# Licensed under the terms of the MIT or BSD Licenses -# (See every file for its license) - -""" -spyderlib.utils.inspector -======================== - -Configuration files for the object inspector rich text mode -""" - -import sys -from spyderlib.baseconfig import get_module_source_path -sys.path.insert(0, get_module_source_path(__name__)) \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/collapse_sections.js spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/collapse_sections.js --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/collapse_sections.js 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/collapse_sections.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -//---------------------------------------------------------------------------- -// Toggleable sections -// -// Added expand/collapse functionality to RST sections. -// Code from the Cloud Sphinx theme -// -// Copyright 2011-2012 by Assurance Technologies -// -// Distributed under the terms of the BSD License -//---------------------------------------------------------------------------- - -//============================================================================ -// On document ready -//============================================================================ - -$(document).ready(function (){ - function init(){ - // get header & section, and add static classes - var header = $(this); - var section = header.parent(); - header.addClass("html-toggle-button"); - - // helper to test if url hash is within this section - function contains_hash(){ - var hash = document.location.hash; - return hash && (section[0].id == hash.substr(1) || - section.find(hash.replace(/\./g,"\\.")).length>0); - } - - // helper to control toggle state - function set_state(expanded){ - if(expanded){ - section.addClass("expanded").removeClass("collapsed"); - section.children().show(); - }else{ - section.addClass("collapsed").removeClass("expanded"); - section.children().hide(); - section.children("span:first-child:empty").show(); /* for :ref: span tag */ - header.show(); - } - } - - // initialize state - set_state(section.hasClass("expanded") || contains_hash()); - - // bind toggle callback - header.click(function (){ - section.children().next().slideToggle(300); - set_state(!section.hasClass("expanded")); - $(window).trigger('cloud-section-toggled', section[0]); - }); - - // open section if user jumps to it from w/in page - $(window).bind("hashchange", function () { - if(contains_hash()) { - var link = document.location.hash; - $(link).parents().each(set_state, [true]); - set_state(true); - $('html, body').animate({ scrollTop: $(link).offset().top }, 'fast'); - } - }); - } - - $(".section > h2, .section > h3, .section > h4").each(init); -}); diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/copy_button.js spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/copy_button.js --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/copy_button.js 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/copy_button.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -//---------------------------------------------------------------------------- -// Copy Button -// -// Add a [>>>] button on the top-right corner of code samples to hide -// the >>> and ... prompts and the output and thus make the code -// copyable. -// -// Taken from http://docs.python.org/_static/copybutton.js -//---------------------------------------------------------------------------- - -//============================================================================ -// On document ready -//============================================================================ - -$(document).ready(function() { - var div = $('.highlight-python .highlight,' + - '.highlight-python3 .highlight') - var pre = div.find('pre'); - - // get the styles from the current theme - pre.parent().parent().css('position', 'relative'); - var hide_text = 'Hide the prompts and output'; - var show_text = 'Show the prompts and output'; - var border_width = pre.css('border-top-width'); - var border_style = pre.css('border-top-style'); - var border_color = pre.css('border-top-color'); - var button_styles = { - 'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0', - 'border-color': border_color, 'border-style': border_style, - 'border-width': border_width, 'color': border_color, 'text-size': '75%', - 'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em', - 'margin-right': '10px', 'border-top-right-radius': '4px' - } - - // create and add the button to all the code blocks that contain >>> - div.each(function(index) { - var jthis = $(this); - if (jthis.find('.gp').length > 0) { - var button = $('>>>'); - button.css(button_styles) - button.attr('title', hide_text); - jthis.prepend(button); - } - // tracebacks (.gt) contain bare text elements that need to be - // wrapped in a span to work with .nextUntil() (see later) - jthis.find('pre:has(.gt)').contents().filter(function() { - return ((this.nodeType == 3) && (this.data.trim().length > 0)); - }).wrap(''); - }); - - // define the behavior of the button when it's clicked - $('.copybutton').toggle( - function() { - var button = $(this); - button.parent().find('.go, .gp, .gt').hide(); - button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden'); - button.css('text-decoration', 'line-through'); - button.attr('title', show_text); - }, - function() { - var button = $(this); - button.parent().find('.go, .gp, .gt').show(); - button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible'); - button.css('text-decoration', 'none'); - button.attr('title', hide_text); - }); -}); - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/fix_image_paths.js spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/fix_image_paths.js --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/fix_image_paths.js 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/fix_image_paths.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -//---------------------------------------------------------------------------- -// Set absolute path for images -// -// Copyright 2014 by The Spyder development team -// -// Distributed under the terms of the MIT License -//---------------------------------------------------------------------------- - -//============================================================================ -// On document ready -//============================================================================ - -$(document).ready(function () { - $('img').attr('src', function(index, attr){ - var path = attr.split('/') - var img_name = path.reverse()[0] - return '{{img_path}}' + '/' + img_name - }); -}); diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/math_config.js spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/math_config.js --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/math_config.js 2015-08-24 19:09:44.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/math_config.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -//---------------------------------------------------------------------------- -// Math configuration options and hacks -// -// Copyright (C) 2012 - The Spyder Team -// -// Distributed under the terms of the MIT License. -//---------------------------------------------------------------------------- - -//============================================================================ -// On document ready -//============================================================================ - -{% if right_sphinx_version and math_on %} - -$(document).ready(function () { - - // MathJax config - // -------------- - MathJax.Hub.Config({ - // We are using SVG instead of HTML-CSS because the last one gives - // troubles on QtWebkit. See this thread: - // https://groups.google.com/forum/?fromgroups#!topic/mathjax-users/HKA2lNqv-OQ - jax: ["input/TeX", "output/SVG"], - - // Menu options are not working. It would be useful to have 'Show TeX - // commands', but it opens an external browser pointing to css_path. - // I don't know why that's happening - showMathMenu: false, - messageStyle: "none", - "SVG": { - blacker: 1 - }, - - {% if platform == 'win32' %} - // Change math preview size so that it doesn't look too big while - // redendered - styles: { - ".MathJax_Preview": { - color: "#888", - "font-size": "55%" - } - } - {% endif %} - }); - - // MathJax Hooks - // ------------- - // Put here any code that needs to be evaluated after MathJax has been - // fully loaded - MathJax.Hub.Register.StartupHook("End", function () { - // Eliminate unnecessary margin-bottom for inline math - $('span.math svg').css('margin-bottom', '0px'); - }); - - {% if platform == 'win32' %} - // Windows fix - // ----------- - // Increase font size of math elements because they appear too small - // compared to the surrounding text. - // Use this hack because MathJax 'scale' option seems to not be working - // for SVG. - $('.math').css("color", "transparent"); - $('.math').css("fontSize", "213%"); - {% endif %} -}); - -{% else %} - -$(document).ready(function () { - // Show math in monospace - $('.math').css('font-family', 'monospace'); -}); - -{% endif %} diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/move_outline.js spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/move_outline.js --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/move_outline.js 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/move_outline.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -//---------------------------------------------------------------------------- -// Move Outline section to be the first one -// -// Copyright 2014 by The Spyder development team -// -// Distributed under the terms of the MIT License -//---------------------------------------------------------------------------- - -//============================================================================ -// On document ready -//============================================================================ - -$(document).ready(function (){ - var first_section_id = $(".section")[0].id; - $("#outline").insertBefore("#" + first_section_id); -}); diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/utils.js spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/utils.js --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/js/utils.js 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/js/utils.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -//---------------------------------------------------------------------------- -// Several utility functions to modify docstring webpages while they are -// rendered -// -// Copyright (C) 2012 - The Spyder Team -// -// Distributed under the terms of the MIT License. -//---------------------------------------------------------------------------- - -//============================================================================ -// On document ready -//============================================================================ - -$(document).ready(function () { - // Remove anchor header links. - // They're used by Sphinx to create crossrefs, so we don't need them - $('a.headerlink').remove(); - - // If the first child in the docstring div is a section, change its class - // to title. This means that the docstring has a real title and we need - // to use it. - // This is really useful to show module docstrings. - var first_doc_child = $('div.docstring').children(':first-child'); - if( first_doc_child.is('div.section') && $('div.title').length == 0 ) { - first_doc_child.removeClass('section').addClass('title'); - }; - - // Change docstring headers from h1 to h2 - // It can only be an h1 and that's the page title - // Taken from http://forum.jquery.com/topic/how-to-replace-h1-h2 - $('div.docstring').find('div.section h1').replaceWith(function () { - return '

    ' + $(this).text() + '

    '; - }); -}); diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/sphinxify.py spyder-3.0.2+dfsg1/spyderlib/utils/inspector/sphinxify.py --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/sphinxify.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/sphinxify.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,253 +0,0 @@ -# -*- coding: utf-8 -* - -""" -Process docstrings with Sphinx - -AUTHORS: -- Tim Joseph Dumol (2009-09-29): initial version -- The Spyder Development Team: Several changes to make it work with Spyder - -Copyright (C) 2009 Tim Dumol -Copyright (C) 2013 The Spyder Development Team -Distributed under the terms of the BSD License - -Taken from the Sage project (www.sagemath.org). -See here for the original version: -www.sagemath.org/doc/reference/sagenb/misc/sphinxify.html -""" - -# Stdlib imports -import codecs -import os -import os.path as osp -import shutil -import sys -from tempfile import mkdtemp - -# 3rd party imports -from docutils.utils import SystemMessage as SystemMessage -from jinja2 import Environment, FileSystemLoader -import sphinx -from sphinx.application import Sphinx - -# Local imports -from spyderlib.baseconfig import (_, get_module_data_path, - get_module_source_path) -from spyderlib.utils import encoding - - -#----------------------------------------------------------------------------- -# Globals and constants -#----------------------------------------------------------------------------- - -# Note: we do not use __file__ because it won't be working in the stand-alone -# version of Spyder (i.e. the py2exe or cx_Freeze build) -CONFDIR_PATH = get_module_source_path('spyderlib.utils.inspector') -CSS_PATH = osp.join(CONFDIR_PATH, 'static', 'css') -JS_PATH = osp.join(CONFDIR_PATH, 'js') - -# To let Debian packagers redefine the MathJax and JQuery locations so they can -# use their own packages for them. See Issue 1230, comment #7. -MATHJAX_PATH = get_module_data_path('spyderlib', - relpath=osp.join('utils', 'inspector', - JS_PATH, 'mathjax'), - attr_name='MATHJAXPATH') - -JQUERY_PATH = get_module_data_path('spyderlib', - relpath=osp.join('utils', 'inspector', - JS_PATH), - attr_name='JQUERYPATH') - -#----------------------------------------------------------------------------- -# Utility functions -#----------------------------------------------------------------------------- - -def is_sphinx_markup(docstring): - """Returns whether a string contains Sphinx-style ReST markup.""" - # this could be made much more clever - return ("`" in docstring or "::" in docstring) - - -def warning(message): - """Print a warning message on the rich text view""" - env = Environment() - env.loader = FileSystemLoader(osp.join(CONFDIR_PATH, 'templates')) - warning = env.get_template("warning.html") - return warning.render(css_path=CSS_PATH, text=message) - - -def usage(title, message, tutorial_message, tutorial): - """Print a usage message on the rich text view""" - env = Environment() - env.loader = FileSystemLoader(osp.join(CONFDIR_PATH, 'templates')) - usage = env.get_template("usage.html") - return usage.render(css_path=CSS_PATH, title=title, intro_message=message, - tutorial_message=tutorial_message, tutorial=tutorial) - - -def generate_context(name='', argspec='', note='', math=False, collapse=False, - img_path=''): - """ - Generate the html_context dictionary for our Sphinx conf file. - - This is a set of variables to be passed to the Jinja template engine and - that are used to control how the webpage is rendered in connection with - Sphinx - - Parameters - ---------- - name : str - Object's name. - note : str - A note describing what type has the function or method being - introspected - argspec : str - Argspec of the the function or method being introspected - math : bool - Turn on/off Latex rendering on the OI. If False, Latex will be shown in - plain text. - collapse : bool - Collapse sections - - Returns - ------- - A dict of strings to be used by Jinja to generate the webpage - """ - - context = \ - { - # Arg dependent variables - 'math_on': 'true' if math else '', - 'name': name, - 'argspec': argspec, - 'note': note, - 'collapse': collapse, - 'img_path': img_path, - - # Static variables - 'css_path': CSS_PATH, - 'js_path': JS_PATH, - 'jquery_path': JQUERY_PATH, - 'mathjax_path': MATHJAX_PATH, - 'right_sphinx_version': '' if sphinx.__version__ < "1.1" else 'true', - 'platform': sys.platform - } - - return context - - -def sphinxify(docstring, context, buildername='html'): - """ - Runs Sphinx on a docstring and outputs the processed documentation. - - Parameters - ---------- - docstring : str - a ReST-formatted docstring - - context : dict - Variables to be passed to the layout template to control how its - rendered (through the Sphinx variable *html_context*). - - buildername: str - It can be either `html` or `text`. - - Returns - ------- - An Sphinx-processed string, in either HTML or plain text format, depending - on the value of `buildername` - """ - - srcdir = mkdtemp() - srcdir = encoding.to_unicode_from_fs(srcdir) - - base_name = osp.join(srcdir, 'docstring') - rst_name = base_name + '.rst' - - if buildername == 'html': - suffix = '.html' - else: - suffix = '.txt' - output_name = base_name + suffix - - # This is needed so users can type \\ on latex eqnarray envs inside raw - # docstrings - if context['right_sphinx_version'] and context['math_on']: - docstring = docstring.replace('\\\\', '\\\\\\\\') - - # Add a class to several characters on the argspec. This way we can - # highlight them using css, in a similar way to what IPython does. - argspec = context['argspec'] - for char in ['=', ',', '(', ')', '*', '**']: - argspec = argspec.replace(char, - '' + char + '') - context['argspec'] = argspec - - doc_file = codecs.open(rst_name, 'w', encoding='utf-8') - doc_file.write(docstring) - doc_file.close() - - temp_confdir = False - if temp_confdir: - # TODO: This may be inefficient. Find a faster way to do it. - confdir = mkdtemp() - confdir = encoding.to_unicode_from_fs(confdir) - generate_configuration(confdir) - else: - confdir = osp.join(get_module_source_path('spyderlib.utils.inspector')) - - confoverrides = {'html_context': context} - - doctreedir = osp.join(srcdir, 'doctrees') - - sphinx_app = Sphinx(srcdir, confdir, srcdir, doctreedir, buildername, - confoverrides, status=None, warning=None, - freshenv=True, warningiserror=False, tags=None) - try: - sphinx_app.build(None, [rst_name]) - except SystemMessage: - output = _("It was not possible to generate rich text help for this " - "object.
    " - "Please see it in plain text.") - return warning(output) - - # TODO: Investigate if this is necessary/important for us - if osp.exists(output_name): - output = codecs.open(output_name, 'r', encoding='utf-8').read() - output = output.replace('
    ', '
    ')
    -    else:
    -        output = _("It was not possible to generate rich text help for this "
    -                    "object.
    " - "Please see it in plain text.") - return warning(output) - - if temp_confdir: - shutil.rmtree(confdir, ignore_errors=True) - shutil.rmtree(srcdir, ignore_errors=True) - - return output - - -def generate_configuration(directory): - """ - Generates a Sphinx configuration in `directory`. - - Parameters - ---------- - directory : str - Base directory to use - """ - - # conf.py file for Sphinx - conf = osp.join(get_module_source_path('spyderlib.utils.inspector'), - 'conf.py') - - # Docstring layout page (in Jinja): - layout = osp.join(osp.join(CONFDIR_PATH, 'templates'), 'layout.html') - - os.makedirs(osp.join(directory, 'templates')) - os.makedirs(osp.join(directory, 'static')) - shutil.copy(conf, directory) - shutil.copy(layout, osp.join(directory, 'templates')) - open(osp.join(directory, '__init__.py'), 'w').write('') - open(osp.join(directory, 'static', 'empty'), 'w').write('') diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/css/default.css spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/css/default.css --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/css/default.css 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/css/default.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,415 +0,0 @@ -body { - background-color: white; - color: rgb(51, 51, 51); - margin: 0px 25px 15px 25px; -} - - -/* --- Title style --- */ -div.title h1 { - font-size: 180%; - font-family: 'Trebuchet MS', sans-serif; - background-color: #6487DC; - background-image: -webkit-gradient( - linear, - 0 0, - 0 100%, - from(#54b4eb), - color-stop(60%, #2fa4e7), - to(#1d9ce5) - ); - text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2); - font-weight: normal; - padding: 6px 0px 6px 20px; - margin: 0px -25px; - color: #FFFFFF; -} - -/* - * The next two styles are needed to - * modify the anchors present on the - * title of pages like scipy.stats or - * scipy.io - */ -div.title h1 a { - color: transparent; - cursor: default; -} - -div.title h1 tt { - font-size: 95%; - background-color: transparent; - color: #FFFFFF; -} - - -/* --- Metadata style --- */ -div.metadata { - margin-top: 10px; - margin-bottom: 15px; - margin-right: 1px; - padding: 1px; - background-color: #EEEEEE; - border: 1px solid #C9C9C9; - border-radius: 6px 6px 6px 6px; - box-shadow: 1px 1px 7px #CACACA; -} - -div.metadata p { - margin: 7px 0px 7px 10px; -} - -span.def { - font-family: monospace; - font-size: 90%; -} - -span.argspec-highlight { - color: red; - font-size: 110%; - font-weight: 900; -} - - -/* --- Docstring div style --- */ -div.docstring { - margin-top: -1px; -} - -div.docstring p { - padding: 0px 2px 0px; -} - - -/* --- Headers style --- */ -h2, h3, h4 { - font-family: 'Helvetica', sans-serif; - color: rgb(49, 126, 172); - margin-top: 20px; - margin-bottom: 10px; -} - -h2 { - font-size: 140%; - font-weight: normal; - border-bottom: 1px solid rgb(220, 220, 220); - padding: 4px 0px 4px 0px; -} - -h3 { - font-size: 115%; -} - -h4 { - font-size: 100%; - margin-top: 14px; - font-weight: normal; -} - -h2.html-toggle-button, h3.html-toggle-button, h4.html-toggle-button { - padding-left: 20px; -} - -.collapsed > h2, .collapsed > h3, .collapsed > h4, .expanded > h2, .expanded > h3, .expanded > h4 { - background-color: transparent; - background-image: url(../images/collapse_expand.png); - background-repeat: no-repeat; - background-attachment: scroll; - cursor: pointer; -} - -.collapsed > h2 { - background-position: 2px 7px; -} - -.collapsed > h3 { - background-position: 2px 2px; -} - -.collapsed > h4 { - background-position: 2px 0px; -} - -.expanded > h2 { - background-position: 0px -31px; -} - -.expanded > h3 { - background-position: 0px -38px; -} - -.expanded > h4 { - background-position: 0px -39px; -} - -dl.docutils { - padding: 0px 10px 0px; -} - -div.section p { - padding: 0px 2px 0px; -} - -#warning { - margin-top: 5px; - background-color: #FFE4E4; - border: 1px solid #F66; - padding: 4px 8px 4px 8px; - text-align: center; -} - -#doc-warning { - margin-top: 16px; - width: 45%; - margin-left: auto; - margin-right: auto; - color: rgb(185, 74, 72); - background-color: rgb(242, 222, 222); - border: 1px solid rgb(238, 211, 215); - border-radius: 4px 4px 4px 4px; - padding: 15px; - text-align: center; - font-weight: bold; - font-size: 105%; -} - - -/* --- Links --- */ -a { - text-decoration: none; - color: rgba(40, 130, 180, 1); -} - -a:hover { - text-decoration: underline; -} - - -/* --- Images --- */ -img { - box-shadow: 0px 2px 6px #cacaca; - border: 1px solid #c9c9c9; -} - -img.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - - -/* --- Lists style --- */ -ol.arabic { - margin-left: -10px; -} - -ul { - margin-left: -5px; -} - -/* --- Literal blocks style --- */ -pre.literal-block { - padding-left: 35px; - font-size: 95%; -} - - -/* --- Docutils table style --- */ -table.docutils { - border-collapse: collapse; - border-spacing: 0; - border: #DDDDDD; - margin-left: auto; - margin-right: auto; - margin-top: 17px; - margin-bottom: 17px; - width: 90%; -} - -table.docutils td { - padding: 5px; -} - -table.docutils tr.row-odd { - background-color: rgb(249, 249, 249); -} - - -/* --- Docutils table headers --- */ -table.docutils th { - background-color: #EEEEEE; - border-bottom-color: #DDDDDD; - border-bottom-style: solid; - border-bottom-width: 1px; - border-top-color: #DDDDDD; - border-top-style: solid; - border-top-width: 1px; - font-weight: bold; - text-align: center; - padding: 6px 0px 6px 8px; - color: rgb(65, 65, 65); -} - - -/* --- Field-list table style --- */ -table.docutils.field-list { - font-size: 80%; - border-collapse: collapse; - border-left: transparent; - border-right: transparent; - margin-top: 15px; - margin-left: 40px; - width: 83%; -} - - -/* --- Field-list table headers --- */ -table.docutils.field-list th { - background-color: transparent; - border-top: transparent; - border-bottom: transparent; - color: black; - font-weight: bold; - text-align: left; - padding: 4px 0px 4px 8px; -} - - -/* --- Spacing around example code --- */ -div.highlight pre { - padding: 9px 14px; - background-color: rgb(247, 247, 249); - border-radius: 4px 4px 4px 4px; - border: 1px solid rgb(225, 225, 232); -} - -div.highlight { - padding: 0px 10px 0px; -} - -dt { - font-weight: bold; - /*font-size: 16px;*/ -} - -.classifier { - /*font-size: 10pt;*/ - font-weight: normal; -} - -tt { - background-color: #ECF0F3; - /*font-size: 95%;*/ - padding: 0px 1px; -} - - - -div.admonition.note { - font-size: 0.95em; - margin: 1.3em; - border: 1px solid #BCE8F1; - background-color: #D9EDF7; - padding: 0px 5px 0 5px; - color: #3A87AD; -} - -div.admonition p.admonition-title { - font-size: 1em; - margin-top: 7px; - font-weight: bold; -} - - -/* ----------- Panels ----------- */ - -.panel { - margin-top: 15px; - background-color: #ffffff; - border: 1px solid transparent; - border-radius: 4px; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); -} - -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-right-radius: 3px; - border-top-left-radius: 3px; -} - -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 110%; - color: rgb(255,255,255); -} - -.panel-body { - padding: 15px; -} - -.panel-usage { - border-color: #2fa4e7; - margin-top: 15px; - width: 60%; - margin-left: auto; - margin-right: auto; -} - -.panel-usage > .panel-heading { - color: #ffffff; - background-color: #2fa4e7; - border-color: #2fa4e7; -} - -.panel-usage > .panel-body > br { - display: block; - margin: 12px 0; - content: ""; -} - -.hr { - background-color: rgb(200, 200, 200); - height: 1px; -} - - -/* ----------- IPython console styles ----------- */ - -/* --- Loading --- */ -.loading { - position: absolute; - margin: -20px 0 0 -95px; - width: 180px; - height: auto; - left: 50%; - top: 50%; - background-color: #EEEEEE; - border: 1px solid #C9C9C9; - border-radius: 6px; - box-shadow: 0px 0px 7px #CACACA; - color: #333333; - padding: 12px; - text-align: center; -} - -#loading-image { - float: left; -} - -#loading-message { - margin-left: 23px; -} - -/* --- Kernel error messages --- */ -.panel-danger { - border-color: #eed3d7; -} - -.panel-danger > .panel-heading { - color: #b94a48; - background-color: #f2dede; - border-color: #eed3d7; - background-color: rgb(199, 28, 34); -} diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/css/pygments.css spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/css/pygments.css --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/css/pygments.css 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/css/pygments.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -.hll { background-color: #ffffcc } -.c { color: #408090; font-style: italic } /* Comment */ -.err { border: 1px solid #FF0000 } /* Error */ -.k { color: #007020; font-weight: bold } /* Keyword */ -.o { color: #666666 } /* Operator */ -.cm { color: #408090; font-style: italic } /* Comment.Multiline */ -.cp { color: #007020 } /* Comment.Preproc */ -.c1 { color: #408090; font-style: italic } /* Comment.Single */ -.cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ -.gd { color: #A00000 } /* Generic.Deleted */ -.ge { font-style: italic } /* Generic.Emph */ -.gr { color: #FF0000 } /* Generic.Error */ -.gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.gi { color: #00A000 } /* Generic.Inserted */ -.go { color: #303030 } /* Generic.Output */ -.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ -.gs { font-weight: bold } /* Generic.Strong */ -.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.gt { color: #0040D0 } /* Generic.Traceback */ -.kc { color: #007020; font-weight: bold } /* Keyword.Constant */ -.kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ -.kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ -.kp { color: #007020 } /* Keyword.Pseudo */ -.kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ -.kt { color: #902000 } /* Keyword.Type */ -.m { color: #208050 } /* Literal.Number */ -.s { color: #4070a0 } /* Literal.String */ -.na { color: #4070a0 } /* Name.Attribute */ -.nb { color: #007020 } /* Name.Builtin */ -.nc { color: #0e84b5; font-weight: bold } /* Name.Class */ -.no { color: #60add5 } /* Name.Constant */ -.nd { color: #555555; font-weight: bold } /* Name.Decorator */ -.ni { color: #d55537; font-weight: bold } /* Name.Entity */ -.ne { color: #007020 } /* Name.Exception */ -.nf { color: #06287e } /* Name.Function */ -.nl { color: #002070; font-weight: bold } /* Name.Label */ -.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ -.nt { color: #062873; font-weight: bold } /* Name.Tag */ -.nv { color: #bb60d5 } /* Name.Variable */ -.ow { color: #007020; font-weight: bold } /* Operator.Word */ -.w { color: #bbbbbb } /* Text.Whitespace */ -.mf { color: #208050 } /* Literal.Number.Float */ -.mh { color: #208050 } /* Literal.Number.Hex */ -.mi { color: #208050 } /* Literal.Number.Integer */ -.mo { color: #208050 } /* Literal.Number.Oct */ -.sb { color: #4070a0 } /* Literal.String.Backtick */ -.sc { color: #4070a0 } /* Literal.String.Char */ -.sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ -.s2 { color: #4070a0 } /* Literal.String.Double */ -.se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ -.sh { color: #4070a0 } /* Literal.String.Heredoc */ -.si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ -.sx { color: #c65d09 } /* Literal.String.Other */ -.sr { color: #235388 } /* Literal.String.Regex */ -.s1 { color: #4070a0 } /* Literal.String.Single */ -.ss { color: #517918 } /* Literal.String.Symbol */ -.bp { color: #007020 } /* Name.Builtin.Pseudo */ -.vc { color: #bb60d5 } /* Name.Variable.Class */ -.vg { color: #bb60d5 } /* Name.Variable.Global */ -.vi { color: #bb60d5 } /* Name.Variable.Instance */ -.il { color: #208050 } /* Literal.Number.Integer.Long */ \ No newline at end of file Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/images/collapse_expand.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/images/collapse_expand.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/images/debug-continue.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/images/debug-continue.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/images/debug-step-in.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/images/debug-step-in.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/images/debug-step-out.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/images/debug-step-out.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/images/debug-step-over.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/images/debug-step-over.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/images/spyder-hello-docstring.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/images/spyder-hello-docstring.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/images/spyder-nice-docstring-rendering.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/images/spyder-nice-docstring-rendering.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderlib/utils/inspector/static/images/spyder-sympy-example.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderlib/utils/inspector/static/images/spyder-sympy-example.png differ diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/templates/layout.html spyder-3.0.2+dfsg1/spyderlib/utils/inspector/templates/layout.html --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/templates/layout.html 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/templates/layout.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -{# - layout.html - ~~~~~~~~~~~ - - Layout template for the object inspector - - :copyright: Copyright 2013 by the Spyder Development Team. - :copyright: Copyright 2009 by Tim Dumol - :license: BSD license -#} - - - - - - - - - - - - {% if right_sphinx_version and math_on %} - {# DON'T try to load MathJax from the net. It's slow and sometimes gives - errors. See this thread for more info: - http://tex.stackexchange.com/questions/2692/comparing-mathjax-and-mathml - #} - - {% endif %} - - - - - - - - -{% if collapse %} - - -{% endif %} - -{% if img_path %} - -{% endif %} - - - - {# Docstring header #} - {% if name %} -

    {{name}}

    - - {% if argspec or note %} - - {% endif %} - - {% endif %} - - {# Docstring text #} -
    - {% block body %}{% endblock %} - {% if collapse %} -
    -

    Outline

    - {{ toc }} -
    - {% endif %} -
    - - - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/templates/usage.html spyder-3.0.2+dfsg1/spyderlib/utils/inspector/templates/usage.html --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/templates/usage.html 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/templates/usage.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -{# - usage.html - ~~~~~~~~~~ - - A simple page to inform users how to get help on the Object Inspector - - :copyright: Copyright 2013 by the Spyder Development Team. - :license: MIT license -#} - - - - - - - - - - - -
    -
    -
    {{title}}
    -
    -
    - {{intro_message}} -

    -
    -
    -
    - {{tutorial_message}} - {{tutorial}} -
    -
    -
    - - - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/templates/warning.html spyder-3.0.2+dfsg1/spyderlib/utils/inspector/templates/warning.html --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/templates/warning.html 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/templates/warning.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -{# - warning.html - ~~~~~~~~~~~ - - A simple page to emit a warning when no docstring text was found for the - one a user was looking for - - :copyright: Copyright 2012 by the Spyder team. - :license: MIT license -#} - -
    {{text}}
    diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/inspector/tutorial.rst spyder-3.0.2+dfsg1/spyderlib/utils/inspector/tutorial.rst --- spyder-2.3.8+dfsg1/spyderlib/utils/inspector/tutorial.rst 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/inspector/tutorial.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,950 +0,0 @@ -====================================================== -Spyder - the Scientific PYthon Development EnviRonment -====================================================== - -*Spyder* is an Integrated Development Environment (IDE) for scientific -computing using the Python programming language. It comes with an -Editor to write code, a Console to evaluate it and see its results at -any time, a Variable Explorer to see what variables have been defined -during evaluation, and several other facilities to help you to -effectively develop the programs you need as a scientist. - - -This tutorial is authored by -`Hans Fangohr `__ from the -University of Southampton (UK) (see `historical note`_ for more -detail). - - -First steps with Spyder -####################### - -This section is aimed at Python and Spyder beginners. If you find it too -simple, please continue to the next section. - -Execute a given program ------------------------ - -* We are going to use this program as a first example: - - .. code-block:: python - - # Demo file for Spyder Tutorial - # Hans Fangohr, University of Southampton, UK - - def hello(): - """Print "Hello World" and return None""" - print("Hello World") - - # main program starts here - hello() - -* To use this program, please create a new file in the Spyder editor pane. Then copy - and paste the code inside the box above on the file, and the save it with the name - ``hello.py``. - -* To execute the program, select ``Run > Run`` from the menu (or press F5), and - confirm the ``Run settings`` if required. - - If this is your first time, you should see an output like this:: - - In [1]: runfile('/Users/fangohr/Desktop/hello.py', wdir=r'/Users/fangohr/Desktop') - Hello World - - In [2]: - - If so, then you have just run your first Python program - well done. - - .. note:: - - The particular path shown next to ``runfile`` will depend on where you have saved - the file, but this is inserted by Spyder automatically. - - -Use the IPython Console -~~~~~~~~~~~~~~~~~~~~~~~ - -Before we proceed, we recommend you to use the IPython console. This console can do a -little more than the standard Python console, and we suggest to use it as the default -console here. - - -What happens when you execute the program? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* Python reads the file line by line, ignoring comments (i.e. lines starting - with the ``#`` symbol). - -* When it comes across the ``def`` keyword, it knows that a function - is DEFined in this and the next (one or more) lines. All *indented* lines - following ``def hello():`` belong to the function body. - - Note that the function object is just created at this point in the - file, but the function is not yet called (i.e. not executed). - -* When Python comes across commands (other than ``def ...`` and a few - other keywords) that are written in the left-most column, it will - execute these immediately. In the ``hello.py`` file this is only the - line reading ``hello()`` which will actually call (i.e. *execute*) - the function with name ``hello``. - - If you comment or remove the line ``hello()`` from the program and run - the whole file again (by pressing F5, or selecting ``Run > Run``), nothing - will be printed (because the function ``hello`` is defined, but not called, - i.e. not executed). - - -Now you should know how to execute a Python program that you have in -the editor pane in Spyder using the IPython console. - -If you are just starting to learn Python, this is probably a good -point to return to your text book / course and look at more basic -examples. - - -The next section gives more detailed information how you can execute -*parts* of the code in the editor in the IPython console, and thus -update parts of your definitions in the editor. This is a more -advanced technique but can be very useful. (You may also be interested -in the option to execute chunks (so-called "cells") of code that are -separated by delimiters -- see `Shortcuts for useful functions`_.) - - - -Call existing functions in the console --------------------------------------- - -Once you have executed the ``hello.py`` program, the function object ``hello`` -is defined and known to the IPython console. We can thus call the function from -the console like this: - -* Type ``hello()`` in the console (next to ``In [?]`` prompt, where - the question mark can be any positive integer number), and press the - ``Enter`` key. - - You should find that the ``hello()`` function is executed again, - i.e. ``Hello World`` is printed again. Your function call at the - console together with the output should look like this:: - - In [ ]: hello() - Hello World - -* Can you see how this differs from executing the whole program again? - - When we execute the whole program (by pressing F5), Python goes - through the file, creates the ``hello`` function object (overriding - the previous object), reaches the ``hello()`` line and calls the - function. - - When we call ``hello()`` in the console, we only call the - function object ``hello`` that has been defined in the IPython - console when we executed the whole ``hello.py`` file earlier (by - pressing F5). - - This will become clearer over time and also when we work with - slightly larger examples. You may want to return to this tutorial at - a slightly later stage. - - -Inspecting objects defined in the console ------------------------------------------ - -* Python provides a function that displays all known objects in the - current name space of the console. It is called ``dir()``: when you - type ``dir()`` at the console, you get a list of known objects. Ignore - everything starting with an underscore for now. Can you see ``hello`` - in the list? - - .. note:: - - If you get a long list of defined objects, then Spyder may have - done some convenience imports for you already. To address this you - may want to: - - - `Reset the name space`_ - - - Execute ``hello.py`` again by pressing F5 - - Then run ``dir()`` as suggested above. - -* Once an object is visible in the current name space (as is ``hello`` - in this example), we can use the ``help`` function as follows to - learn about it: Typing ``help(hello)`` at the console prompt, you - should see an output like this:: - - In [ ]: help(hello) - Help on function hello in module __main__: - - hello() - Print "Hello World" and return None - - - Where does Python take the information from? Some of it (like the - number of input arguments and names of those variables; here we have - no input arguments) Python can find through inspecting its objects, - additional information comes from the documentation string provided - for the function object ``hello``. The documentation string is the - first string immediately below the line ``def hello():``. - - This strings are special, and they are called *docstrings* which is short for - *documentation strings*. As they usually extend over multiple lines, there - are enclosed by triple single quotes (``'''``) or triple double quotes - (``"""``). - -* The Spyder environment also provides the ``Object inspector`` which - by default is located in the top right corner. - - While the cursor is on the name of an object, - press ``CTRL+i`` (or ``CMD+i`` on Mac), and you should find that - the same information as we obtained from ``help(hello)`` is provided - automatically in the object inspector: - - .. image:: static/images/spyder-hello-docstring.png - :align: center - - This works in the console and in the editor. - -Updating objects ----------------- - -Simple strategy: re-execute whole program -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* In the Editor window, change the function ``hello`` so - that it prints ``Good Bye World`` rather than ``Hello World``. - -* Press F5 (to execute the whole program) and check that the output of the - program is now:: - - Good Bye World - -What has happened when you pressed F5 is this: Python has gone through -the ``hello.py`` file and created a new function object ``hello`` -(overriding the function object ``hello`` we had defined before) and -then executed the function. - - -Looking at the details -~~~~~~~~~~~~~~~~~~~~~~ - -We need to start with a clearly defined state. To do this, please change the -function ``hello()`` back so that it prints ``Hello World``, then press -F5 to run the whole program and check that it prints ``Hello World``. - -* Call the function ``hello()`` from the command prompt (as described - in `Call existing functions in the console`_). You - should see ``Hello World`` printed. - -* Now change the function definition so that it would print ``Laters - World``, and save the file (but do NOT execute the program, i.e. do - NOT press F5 yet). - -* Call the function ``hello()`` in the console again. You - should find that the text printed reads ``Hello World``, like here - :: - - In [ ]: hello() - Hello World - - Why is this so? Because the ``hello`` function object in the console - is the old one which prints ``Hello World``. So far, we have - changed the file ``hello.py`` (and replaced ``Hello World`` in there with - ``Laters World``) in the editor but this has not affected the objects that - have previously been created in the console. - -Here are two possibilities to use our modified version of the ``hello`` -function: - -* Option 1: execute the whole file ``hello.py`` again by pressing F5: - this creates a new function object ``hello`` (and overrides the old - one). You should find that if you press F5, and then call - ``hello()`` at the prompt, the new text ``Laters World`` is printed. - -* Option 2: select the region you have changed (in this case the whole - function ``hello``, starting from the line ``def hello():`` down to - ``print("Laters Wold")``, and then select ``Run > Run selection``. - - This will update the ``hello`` object in the console without - having to execute the whole ``hello.py`` file:: - - In [ ]: def hello(): - ...: """Print "Hello World" and return None""" - ...: print("Laters world") - ...: - - If we now type ``hello()``, we see the update response:: - - In [ ]: hello() - Laters world - -The ability to execute *parts of the code* to update some objects in -the console (in the example above, we updated the function object -``hello``), is of great use when developing and debugging more complex -codes, and when creating objects/data in the console session take -time. For example, by modifying only the functions (or -classes/objects, etc) that we are actually developing or debugging, we -can keep re-using the data and other objects that are defined in the -console session. - - - -Recommended first steps for Python beginners -############################################ - -To teach and learn Python programming, we recommend here to use IPython -instead of the normal Python console. This accepts IPython as the -de-facto standard in the scientific Python community. - -Switch to an IPython console ----------------------------- - -If you already have an IPython console active, you can ignore this section, -and make it visible by clicking on the "IPython console" rider. - -In the console window (lower right corner by default), you see by -default a prompt with three greater than signs, i.e. ``>>>``. This -shows that we are using the ``console`` -- basically a normal Python -console session (with some added functionality from Spyder). - -Instead, we would like to use an *Interactive Python* console, short *IPython* -from the `IPython project `__. To do this, select -``Consoles > Open an IPython Console``. - -You should see in the consolse window a new shell appearing, and the -IPython prompt ``In [1]:`` should be displayed. - -Reset the name space --------------------- - -The `name space `__ -(i.e. the collection of objects defined in the console at any given time) -can be cleared in IPython using the ``%reset`` command. Type ``%reset`` -and press return, then confirm with ``y``:: - - In [1]: %reset - - Once deleted, variables cannot be recovered. Proceed (y/[n])? y - - In [2]: - -That's all. - -We discuss this a little further, but you can skip the following if -you are not interested: After issuing the ``%reset`` command, we -should have only a few objects defined in the name space of that -session. We can list all of them using the ``dir()`` command:: - - In [2]: dir() - Out[2]: - ['In', - 'Out', - '__builtin__', - '__builtins__', - '__name__', - '_dh', - '_i', - '_i2', - '_ih', - '_ii', - '_iii', - '_oh', - '_sh', - 'exit', - 'get_ipython', - 'help', - 'quit'] - -Finally, if you like to skip the confirmation step of the ``reset`` -command, use can use ``%reset -f`` instead of ``%reset``. - -Strive for PEP8 Compliance --------------------------- - -In addition to the syntax that is enforced by the Python programming -language, there are additional conventions regarding the layout of -the source code, in particular the `Style Guide for Python source code -`__ known as "PEP8". By -following this guide and writing code in the same style as almost all -Python programmers do, it becomes easier to read, and thus easier to -debug and re-use -- both for the original author and others. - -You should change Spyders settings to -`Warn if PEP8 coding guidelines are violated`_. - - - -Selected Preferences -#################### - -Where are the preferences? --------------------------- - -A lot of Spyder's behaviour can be configured through it's -Preferences. Where this is located in the menu depends on your -operating system: - -* On Windows and Linux, go to ``Tools > Preferences`` - -* On Mac OS, go to ``Python/Spyder > Preferences`` - -Warn if PEP8 coding guidelines are violated -------------------------------------------- - -Go to ``Preferences > Editor > Code -Introspection/Analysis`` and -select the tickbox next to ``Style analysis (PEP8)`` - -Automatic Symbolic Python -------------------------- - -Through ``Preferences > IPython console > Advanced Settings > Use -symbolic math`` we can activate IPython's SYMbolic PYthon (sympy) mode that is -provided by the `sympy `__ module. This mode -in Spyder allows nicely rendered mathematical output (latex style) and also -imports some sympy objects automatically when the IPython console starts, and -reports what it has done. - -.. code-block:: python - - These commands were executed: - >>> from __future__ import division - >>> from sympy import * - >>> x, y, z, t = symbols('x y z t') - >>> k, m, n = symbols('k m n', integer=True) - >>> f, g, h = symbols('f g h', cls=Function) - -We can then use the variables ``x``, ``y``, for example like this: - -.. image:: static/images/spyder-sympy-example.png - :align: center - - - -Shortcuts for useful functions -############################## - -- ``F5`` executes the current file - -- ``F9`` executes the currently highlighted chunk of code: this is very useful - to update definitions of functions (say) in the console session without - having to run the whole file again. If nothing is selected ``F9`` executes - the current line. - -- ``Tab`` auto-completes commands, function names, variable - names, methods in the Console (both Python and IPython) and in the - Editor. This feature is very useful, and should be used - routinely. Do try it now if auto-completion is new to you. - Assume you have defined a variable:: - - mylongvariablename = 42 - - Suppose we need to write code that computes ``mylongvariablename + - 100``, we can simply type ``my`` and then press the ``Tab`` key. The - full variable name will be completed and inserted at the cursor - position if the name is unique, and then we can carry on and type - ``+ 100``. If the name is not uniquely identifiable given the - letters ``my``, a list field will be displayed from which the right - variable can be chosen. Choosing from the list can be done with the - ```` key and ```` key and the ``Enter`` - key to select, or by typing more letters of the name in question - (the selection will update automatically) and confirming by pressing - ``Enter`` when the right name is identified. - -- ``Ctrl+Enter`` executes the current cell (menu enty ``Run > Run - cell``). A cell is defined as the code between two lines which start with - the agreed tag ``#%%``. - -- ``Shift+Enter`` executes the current cell and advances the - cursor to the next cell (menu entry ``Run > Run cell and - advance``). - - Cells are useful to execute a large file/code segment in smaller - units. (It is a little bit like a cell in an IPython notebook, in - that chunks of code can be run independently.) - -- ``Alt+`` moves the current line up. If multiple lines are - highlighted, they are moved up together. ``Alt+`` - works correspondingly moving line(s) down. - -- ``Ctrl+Left Mouse Click`` on a function/method in the source, opens a new - editor windows showing the definition of that function. - -- ``Shift+Ctrl+Alt+M`` maximizes the current window (or changes the - size back to normal if pressed in a maximized window) - -- ``Ctrl+Shift+F`` activates the search pane across all files. - -- ``Cmd + +`` (On MacOS X) or ``Ctrl + +`` (otherwise) will increase the font - size in the Editor, whereas ``Cmd + -`` (``Ctrl + -``) will decrease it. - Also works in the IPython Console. - - The font size for the Object Inspector, the Python console etc can be set - individually via ``Preferences > Object inspector`` etc. - - I couldn't find a way of changing the font size in the variable explorer. - -- ``Cmd+S`` (on MacOS X) and ``Ctrl+S`` (otherwise) *in the Editor* - pane saves the file - currently being edited. This also forces various warning triangles - in the left column of the Editor to be updated (otherwise they - update every 2 to 3 seconds by default). - -- ``Cmd+S`` (on MacOS X) and ``Ctrl+S`` (otherwise) *in the IPython console* - pane saves the current IPython session as an HTML file, including - any figures that may be displayed inline. This is useful as a quick - way of recording what has been done in a session. - - (It is not - possible to load this saved record back into the session - if you - need functionality like this, look for the IPython Notebook.) - -- ``Cmd+I`` (on Mac OS X) and ``Ctrl+I`` (otherwise) when pressed - while the cursor is on an object, opens documentation for that - object in the object inspector. - - - -Run Settings -############ - -These are the settings that define how the code in the editor is -executed if we select ``Run > Run`` or press F5. - -By default, the settings box will appear the first time we try to execute a -file. If we want to change the settings at any other time, they can be -found under ``Run > Configure`` or by pressing F6. - -There are three choices for the console to use, of which I'll discuss the -first two. Let's assume we have a program ``hello.py`` in the editor which -reads - -.. code-block:: python - - def hello(name): - """Given an object 'name', print 'Hello ' and the object.""" - print("Hello {}".format(name)) - - - i = 42 - if __name__ == "__main__": - hello(i) - -Execute in current Python or IPython console --------------------------------------------- - -This is the default suggestion, and also generally a good choice. - -Persistence of objects I (after code execution) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Choosing ``Execute in current Python or IPython console`` -setting under ``Run > Configure`` means that - -1. When the execution of ``hello.py`` is completed, we can interact - with the console in which the program ran, and we can use the - convenient IPython console for this (rather than the default - Python console). - - In particular, - -2. we can inspect and interact with objects that the execution of - our program created, such as ``i`` and ``hello()``. - -This is generally very useful for incremental coding, testing and -debugging: we can call ``hello()`` directly from the console -prompt, and don't need to execute the whole ``hello.py`` for this -(although if we change the function ``hello()``, we need to execute -the file, or at least the function definition, to make the new -version of ``hello()`` visible at the console; either by -executing the whole buffer or via ``Run > Run Selection``.) - -Persistence of objects II (from before code execution) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -However, executing the code in the editor in the current console -also means that - -3. the code that executes can see other (global) objects that were - defined in the console session. - -*This* persistence of objects is easily forgotten and usually not -required when working on small programs (although it can be of great -value occasionally). These objects could come from previous execution -of code, from interactive work in the console, or from convenience -imports such as ``from pylab import *`` (Spyder may do some of those -convenience imports automatically). - -This visibility of objects in the console name space to the -code we execute may also result in coding mistakes if the code -inadvertently relies on these objects. - -Here is an example: imagine that - -* we run the code ``hello.py``. Subsequently, the variable ``i`` - is known in the console as a global variable. - -* we edit the ``hello.py`` source and accidentally delete the line - ``i = 42`` - -* we execute the buffer containing ``hello.py`` again. At this - point, the call of ``hello(i)`` will *not* fail because the - console has an object of name ``i`` defined, although this is - not defined in the source of ``hello.py``. - -At this point, we could save ``hello.py`` and (falsely) think it -would execute correctly. However, running it in a new (I)Python -console session (or via ``python hello.py`` in a terminal, say) -would result in an error, because ``i`` is not defined. - -The problem arises because the code makes use of an object (here -``i``) without creating it. This also affects importing of modules: if -we had imported ``pylab`` at the IPython prompt, then our program will -see that when executed in this IPython console session. - -To learn how we can double check that our code does not depend on such -existing objects, see `How to double check your code executes correctly "on -its own"`_ . - -Execute in new dedicated Python console ---------------------------------------- - -Choosing ``Execute in new dedicated Python console`` under ``Run -> Configure`` will start *a new Python console everytime* the -``hello.py`` program is executed. The major advantage of this mode -over `Execute in current Python or IPython console`_ is that we -can be certain that there are no global objects defined in this -console which originate from debugging and repeated execution of -our code: every time we run the code in the editor, the python -console in which the code runs is restarted. - -This is a safe option, but provides less flexibility and cannot use -the IPython console. - -How to double check your code executes correctly "on its own" -------------------------------------------------------------- - -Assuming you have chosen for your code to -`Execute in current Python or IPython console`_, -then you have two options to check that your code does work on its own -(i.e. it does not depend on undefined variables, unimported modules -and commands etc.) - -(i) Switch from `Execute in current Python or IPython console`_ - to `Execute in new dedicated Python console`_, - and execute the code in the editor in this dedicated Python console. - - Alternatively, if you want to stay with the current IPython - console, you can - -(ii) Use IPython's magic ``%reset`` command which will remove all - objects (such as ``i`` in the example above) from the current name - space, and then execute the code in the editor. - -Recommendation --------------- - -My recommendation for beginners would be to -`Execute in current Python or IPython console`_, *and* -to choose the IPython console for this. - -Once you have completed a piece of code, double check that it executes -independently using one of the options explained in -`How to double check your code executes correctly "on its own"`_\ . - - - -Other observations -################## - -Multiple files --------------- - -When multiple files are opened in the Editor, the -corresponding tabs at the top of the window area are arranged in -alphabetical order of the filename from left to right. - -On the left of the tabs, there is as icon that shows ``Browse tabs`` -if the mouse hovers over it. It is useful to jump to a particular -file directly, if many files are open. - -Environment variables ---------------------- - -Environment variables can be displayed from the Python Console window (bottom -right window in default layout). Click on the ``Options`` icon (the tooltip is -``Options``), then select ``Environment variables``. - -Reset all customization ------------------------ - -All customization saved on disk can be reset by calling spyder from -the command line with the switch ``--reset``, i.e. a command like -``spyder --reset``. - -Objects in the variable explorer --------------------------------- - -Right-clicking on arrays in the variable explorer gives options to -plot and analyze these further. - -Double clicking on a dictionary object opens a new window that -displays the dictionary nicely. - -You can also show and edit the contents of numpy arrays, lists, numbers -and strings. - - - -Documentation string formatting -############################### - -If you want to add documentation for the code you are developing, we recommend -you to write documentation strings (or *docstrings*) for it, using a special -format called restructured text (`quick reference -`__). This format -also needs to follow a set of conventions called the `Numpydoc standard -`__ - -If you follow those guidelines, you can obtain beautifully formated docstrings -in Spyder. - -For example, to get an ``average()`` function look like this in the -Spyder Object inspector: - -.. image:: static/images/spyder-nice-docstring-rendering.png - :align: center - -you need to format the documentation string as follows - -.. code-block:: python - - def average(a, b): - """ - Given two numbers a and b, return their average value. - - Parameters - ---------- - a : number - A number - b : number - Another number - - Returns - ------- - res : number - The average of a and b, computed using 0.5*(a + b) - - Example - ------- - >>> average(5, 10) - 7.5 - - """ - - return (a + b) * 0.5 - -What matters here, is that the word ``Parameters`` is used, and -underlined. The line ``a : number`` shows us that the type of the -parameter ``a`` is ``number``. In the next line, which is indented, we -can write a more extended explanation of what this variable represents, -what conditions the allowed types have to fulfill, etc. - -The same for all Parameters, and also for the returned value. - -Often it is a good idea to include an example too, as shown. - - - -Debugging -######### - -Line by line step execution of code ------------------------------------ - -Activating the debug mode (with the ``Debug > Debug`` menu option or Ctrl+F5) -starts the Python debugger (Pdb) if the Python console is active, or the IPython -debugger (ipdb) if the IPython console is active. After doing that, the -Editor pane will highlight the line that is about to be executed, and the -Variable Explorer will display variables in the current context of the point -of program execution. (It only displays 'numerical' and array type of variables, -i.e. not function or class objects) - -After entering debug mode, you can execute the code line by line using the -``Step`` button of the Debug toolbar: - -.. image:: static/images/debug-step-over.png - :align: center - -or the shortcut Ctrl+F10. You can also inspect how a particular function is -working by stepping into it with the ``Step into`` button - -.. image:: static/images/debug-step-in.png - :align: center - -or the shortcut Ctrl+F11. Finally, to get out of a function and continue with -the next line you need to use the ``Step return`` button - -.. image:: static/images/debug-step-out.png - :align: center - -or the shortcut Ctrl+F12. - -If you prefer to inspect your program at a specific point, you need to insert a -*breakpoint* by pressing F12 on the line on which you want to stop. After -that a red dot will be placed next to the line and you can press the ``Continue`` -button - -.. image:: static/images/debug-continue.png - :align: center - -(after entering debug mode) to stop the execution at that line. - -.. note:: - - You can also control the debugging process by issuing these commands in the - console prompt: - - * ``n`` to move to the Next statement. - - * ``s`` to Step into the current statement. If this is a function - call, step into that function. - - * ``r`` to complete all statements in the current function and Return - from that function before returning control. - - * ``p`` to print values of variables, for example ``p x`` will print the - value of the variable ``x``. - -At the debugger prompt, you can also *change* values of variables. For -example, to modify a variable ``x`` at the IPython debugger prompt, you can say -``ipdb > x = 42`` and the debugger will carry on with ``x`` being bound to ``42``. -You can also call functions, and do many others things. Try this example:: - - def demo(x): - for i in range(5): - print("i={}, x={}".format(i, x)) - x = x + 1 - - demo(0) - -If we execute this (``Run > Run``), we should see the output:: - - i=0, x=0 - i=1, x=1 - i=2, x=2 - i=3, x=3 - i=4, x=4 - -Now execute this using the debugger (``Debug > Debug``), press the -``Step button`` until the highlighted line reaches the ``demo(0)`` -function call, then press the ``Step into`` to inspect this function. -Keep pressing the ``Step button`` to execute the next lines. Then, -modify ``x`` by typing ``x=10`` in the debugger prompt. You see x -changing in the Variable Explorer. You should also see ``x`` changing -when its value is printed as part of the ``demo()`` function. (The -printed output appears between your debugger commands and responses.) - -This debugging ability to execute code line by line, to inspect variables as -they change, and to modify them manually is a powerful tool to -understand what a piece of code is doing (and to correct it if desired). - -To leave the debugging mode, you can type ``exit`` or select from the -menu ``Debug > Debugging Control > Exit`` - -Debugging once an exception has occurred with IPython ------------------------------------------------------ - -In the IPython console, we can call ``%debug`` -straight after an exception has been raised: this will start the -IPython debug mode, which allows inspection of local variables at the -point where the exception occurred as described above. This is a lot -more efficient than adding ``print`` statements to the code an -running it again. - -If you use this, you may also want to use the commands ``up`` -(i.e. press ``u`` at the debugger) and ``down`` (i.e. press ``d``) which -navigate the inspection point up and down the stack. (Up the stack means -to the functions that have called the current function; down is the -opposite direction.) - - - -Plotting -######## - -Plotting with the IPython console ---------------------------------- - -Assuming we use an IPython console with version >= 1.0.0, we can -decide whether figures created with matplotlib/pylab will show - -1. *inline*, i.e. inside the IPython console, or whether they should - -2. appear inside a new window. - -Option 1 is convenient to save a record of the interactive session -(section `Shortcuts for useful functions`_ lists a shortcut to save -the IPython console to an html file). - -Option 2 allows to interactively zoom into the figure, manipulate it a little, -and save the figure to different file formats via a menu the window it -contains has. - -The command to get the figures to appear *inline* in the IPython -console is:: - - In [3]: %matplotlib inline - -The command to get figures appear in their own window (which -technically is a Qt windown) is:: - - In [4]: %matplotlib qt - -The Spyder preferences can be used to customize the default behavior -(in particular ``Preferences > IPython Console > Graphics > -Activate Support`` to switch into inline plotting). - -Here are two lines you can use to quickly create a plot and test -this:: - - In [5]: import pylab - In [6]: pylab.plot(range(10), 'o') - - -Plotting with the Python console --------------------------------- - -If we use the Python console, all plots will appear in a new window -(there is no way of making it appear inline inside the Python -console - this only works for the IPython Console). - -Here is a brief example that you can use to create and display a -plot:: - - >>> import pylab - >>> pylab.plot(range(10), 'o') - -If you execute your code in a dedicated console, you need to use -matplotlib's or pylab's ``show()`` command in your code to make a plot -appear, like this: ``pylab.show()``. - -Note that the ``show()`` command will bind the focus to new window -that has appeared, and that you will need to close that window before -Spyder can accept any further commands or respond to interaction. If -you cannot see the new window, check whether it may have appeared behind -the Spyder window, or be partly hidden. - - - -Historical note -############### - -This tutorial is based on `notes -`__ -by `Hans Fangohr `__, that are -used at the `University of Southampton `__ to -`teach Python for computational modelling -`__ to -undegraduate engineers and postgraduate PhD students for the -`Next Generation Computational Modelling `__ -doctoral training centre. diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/introspection/fallback_plugin.py spyder-3.0.2+dfsg1/spyderlib/utils/introspection/fallback_plugin.py --- spyder-2.3.8+dfsg1/spyderlib/utils/introspection/fallback_plugin.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/introspection/fallback_plugin.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,368 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2013 The Spyder Development Team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Introspection utilities used by Spyder -""" - -from __future__ import print_function -import imp -import os -import os.path as osp -import re -import time - -from spyderlib.utils.debug import log_dt -from spyderlib.utils import sourcecode, encoding -from spyderlib.utils.introspection.module_completion import module_completion -from spyderlib.utils.introspection.plugin_manager import ( - DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin, memoize) - - -class FallbackPlugin(IntrospectionPlugin): - """Basic Introspection Plugin for Spyder""" - - # ---- IntrospectionPlugin API -------------------------------------------- - name = 'fallback' - - def get_completions(self, info): - """Return a list of completion strings - - Simple completion based on python-like identifiers and whitespace - """ - items = [] - if (info.line.strip().startswith(('import ', 'from ')) and - info.is_python_like): - items += module_completion(info.line, [info.filename]) - elif info.obj: - base = info.obj - tokens = set(info.split_words(-1)) - items = [item for item in tokens if - item.startswith(base) and len(item) > len(base)] - if '.' in base: - start = base.rfind('.') + 1 - else: - start = 0 - - items = [i[start:len(base)] + i[len(base):].split('.')[0] - for i in items] - # get path completions - # get last word back to a space or a quote character - match = re.search('''[ "\']([\w\.\\\\/]+)\Z''', info.line) - if match: - items += _complete_path(match.groups()[0]) - return list(sorted(items)) - - def get_definition(self, info): - """ - Find the definition for an object within a set of source code - - This is used to find the path of python-like modules - (e.g. cython and enaml) for a goto definition - """ - token = info.obj - lines = info.lines - source_code = info.source_code - filename = info.filename - - line_nr = None - if '.' in token: - token = token.split('.')[-1] - - line_nr = get_definition_with_regex(source_code, token, - len(lines)) - if line_nr is None: - return - line = info.line - exts = python_like_exts() - if not osp.splitext(filename)[-1] in exts: - return filename, line_nr - if line.startswith('import ') or line.startswith('from '): - alt_path = osp.dirname(filename) - source_file = python_like_mod_finder(line, alt_path=alt_path, - stop_token=token) - if (not source_file or - not osp.splitext(source_file)[-1] in exts): - line_nr = get_definition_with_regex(source_code, token, - line_nr) - return filename, line_nr - mod_name = osp.basename(source_file).split('.')[0] - if mod_name == token or mod_name == '__init__': - return source_file, 1 - else: - with open(filename, 'rb') as fid: - code = fid.read() - code = encoding.decode(code)[0] - line_nr = get_definition_with_regex(code, token) - - return filename, line_nr - - -@memoize -def python_like_mod_finder(import_line, alt_path=None, - stop_token=None): - """ - Locate a module path based on an import line in an python-like file - - import_line is the line of source code containing the import - alt_path specifies an alternate base path for the module - stop_token specifies the desired name to stop on - - This is used to a find the path to python-like modules - (e.g. cython and enaml) for a goto definition. - """ - if stop_token and '.' in stop_token: - stop_token = stop_token.split('.')[-1] - tokens = re.split(r'\W', import_line) - if tokens[0] in ['from', 'import']: - # find the base location - try: - _, path, _ = imp.find_module(tokens[1]) - except ImportError: - if alt_path: - path = osp.join(alt_path, tokens[1]) - else: - path = None - if path: - path = osp.realpath(path) - if not tokens[1] == stop_token: - for part in tokens[2:]: - if part in ['import', 'cimport', 'as']: - break - path = osp.join(path, part) - if part == stop_token: - break - # from package import module - if stop_token and not stop_token in path: - for ext in python_like_exts(): - fname = '%s%s' % (stop_token, ext) - if osp.exists(osp.join(path, fname)): - return osp.join(path, fname) - # from module import name - for ext in python_like_exts(): - fname = '%s%s' % (path, ext) - if osp.exists(fname): - return fname - # if it is a file, return it - if osp.exists(path) and not osp.isdir(path): - return path - # default to the package file - path = osp.join(path, '__init__.py') - if osp.exists(path): - return path - - -def get_definition_with_regex(source, token, start_line=-1): - """ - Find the definition of an object within a source closest to a given line - """ - if not token: - return None - if DEBUG_EDITOR: - t0 = time.time() - patterns = [ # python / cython keyword definitions - '^c?import.*\W{0}{1}', - 'from.*\W{0}\W.*c?import ', - 'from .* c?import.*\W{0}{1}', - 'class\s*{0}{1}', - 'c?p?def[^=]*\W{0}{1}', - 'cdef.*\[.*\].*\W{0}{1}', - # enaml keyword definitions - 'enamldef.*\W{0}{1}', - 'attr.*\W{0}{1}', - 'event.*\W{0}{1}', - 'id\s*:.*\W{0}{1}'] - - matches = get_matches(patterns, source, token, start_line) - - if not matches: - patterns = ['.*\Wself.{0}{1}[^=!<>]*=[^=]', - '.*\W{0}{1}[^=!<>]*=[^=]', - 'self.{0}{1}[^=!<>]*=[^=]', - '{0}{1}[^=!<>]*=[^=]'] - matches = get_matches(patterns, source, token, start_line) - - # find the one closest to the start line (prefer before the start line) - if matches: - min_dist = len(source.splitlines()) - best_ind = 0 - for match in matches: - dist = abs(start_line - match) - if match <= start_line or not best_ind: - if dist < min_dist: - min_dist = dist - best_ind = match - if matches: - if DEBUG_EDITOR: - log_dt(LOG_FILENAME, 'regex definition match', t0) - return best_ind - else: - if DEBUG_EDITOR: - log_dt(LOG_FILENAME, 'regex definition failed match', t0) - return None - - -def get_matches(patterns, source, token, start_line): - patterns = [pattern.format(token, r'[^0-9a-zA-Z.[]') - for pattern in patterns] - pattern = re.compile('|^'.join(patterns)) - # add the trailing space to allow some regexes to match - lines = [line.strip() + ' ' for line in source.splitlines()] - if start_line == -1: - start_line = len(lines) - matches = [] - for (index, line) in enumerate(lines): - if re.match(pattern, line): - matches.append(index + 1) - return matches - - -def python_like_exts(): - """Return a list of all python-like extensions""" - exts = [] - for lang in sourcecode.PYTHON_LIKE_LANGUAGES: - exts.extend(list(sourcecode.ALL_LANGUAGES[lang])) - return ['.' + ext for ext in exts] - - -def all_editable_exts(): - """Return a list of all editable extensions""" - exts = [] - for (language, extensions) in sourcecode.ALL_LANGUAGES.items(): - exts.extend(list(extensions)) - return ['.' + ext for ext in exts] - - -def _listdir(root): - "List directory 'root' appending the path separator to subdirs." - res = [] - root = os.path.expanduser(root) - try: - for name in os.listdir(root): - path = os.path.join(root, name) - if os.path.isdir(path): - name += os.sep - res.append(name) - except: - pass # no need to report invalid paths - return res - - -def _complete_path(path=None): - """Perform completion of filesystem path. - http://stackoverflow.com/questions/5637124/tab-completion-in-pythons-raw-input - """ - if not path: - return _listdir('.') - dirname, rest = os.path.split(path) - tmp = dirname if dirname else '.' - res = [p for p in _listdir(tmp) if p.startswith(rest)] - # more than one match, or single match which does not exist (typo) - if len(res) > 1 or not os.path.exists(path): - return res - # resolved to a single directory, so return list of files below it - if os.path.isdir(path): - return [p for p in _listdir(path)] - # exact file match terminates this completion - return [path + ' '] - - -if __name__ == '__main__': - from spyderlib.utils.introspection.plugin_manager import CodeInfo - - p = FallbackPlugin() - - with open(__file__, 'rb') as fid: - code = fid.read().decode('utf-8') - code += '\nlog_dt' - - path, line = p.get_definition(CodeInfo('definition', code, len(code), - __file__)) - assert path.endswith('fallback_plugin.py') - - code += '\np.get_completions' - path, line = p.get_definition(CodeInfo('definition', code, len(code), - 'dummy.txt')) - assert path == 'dummy.txt' - assert 'def get_completions(' in code.splitlines()[line - 1] - - code += '\npython_like_mod_finder' - path, line = p.get_definition(CodeInfo('definition', code, len(code), - 'dummy.txt')) - assert path == 'dummy.txt' - # FIXME: we need to prioritize def over = - assert 'def python_like_mod_finder' in code.splitlines()[line - 1] - - code += 'python_like_mod_finder' - resp = p.get_definition(CodeInfo('definition', code, len(code), - 'dummy.txt')) - assert resp is None - - code = """ - class Test(object): - def __init__(self): - self.foo = bar - - t = Test() - t.foo""" - path, line = p.get_definition(CodeInfo('definition', code, len(code), - 'dummy.txt')) - assert line == 4 - - ext = python_like_exts() - assert '.py' in ext and '.pyx' in ext - - ext = all_editable_exts() - assert '.cfg' in ext and '.iss' in ext - - path = p.get_parent_until(os.path.abspath(__file__)) - assert path == 'spyderlib.utils.introspection.fallback_plugin' - - line = 'from spyderlib.widgets.sourcecode.codeeditor import CodeEditor' - path = python_like_mod_finder(line) - assert path.endswith('codeeditor.py') - path = python_like_mod_finder(line, stop_token='sourcecode') - assert path.endswith('__init__.py') and 'sourcecode' in path - - path = p.get_parent_until(osp.expanduser(r'~/.spyder2/temp.py')) - assert path == '.spyder2.temp' - - code = 'import re\n\nre' - path, line = p.get_definition(CodeInfo('definition', code, len(code), - 'dummy.txt')) - assert path == 'dummy.txt' and line == 1 - - code = 'self.proxy.widget; self.p' - comp = p.get_completions(CodeInfo('completions', code, len(code))) - assert comp == ['proxy'] - - code = 'self.sigMessageReady.emit; self.s' - comp = p.get_completions(CodeInfo('completions', code, len(code))) - assert comp == ['sigMessageReady'] - - code = encoding.to_unicode('álfa;á') - comp = p.get_completions(CodeInfo('completions', code, len(code))) - assert comp == [encoding.to_unicode('álfa')] - - code = 'from numpy import one' - comp = p.get_completions(CodeInfo('completions', code, len(code))) - assert 'ones' in comp - - comp = p.get_completions(CodeInfo('completions', code, len(code), - is_python_like=False)) - assert not comp - - code = 'from numpy.testing import (asse' - comp = p.get_completions(CodeInfo('completions', code, len(code))) - assert 'assert_equal' in comp - - code = ''' -def test(a, b): - pass -test(1,''' - path, line = p.get_definition(CodeInfo('definition', code, len(code), - 'dummy.txt')) - assert line == 2 diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/introspection/__init__.py spyder-3.0.2+dfsg1/spyderlib/utils/introspection/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/utils/introspection/__init__.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/introspection/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2013 The Spyder Development Team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Introspection utilities used by Spyder -""" -from . import module_completion -from .plugin_manager import PluginManager diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/introspection/jedi_plugin.py spyder-3.0.2+dfsg1/spyderlib/utils/introspection/jedi_plugin.py --- spyder-2.3.8+dfsg1/spyderlib/utils/introspection/jedi_plugin.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/introspection/jedi_plugin.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,292 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2013 The Spyder Development Team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Jedi Introspection Plugin -""" -import re -import os.path as osp -import sys -import time -import threading - -from spyderlib import dependencies -from spyderlib.baseconfig import _, debug_print -from spyderlib.utils import programs -from spyderlib.utils.debug import log_last_error, log_dt -from spyderlib.utils.dochelpers import getsignaturefromtext -from spyderlib.utils.introspection.plugin_manager import ( - DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin) - -try: - import jedi -except ImportError: - jedi = None - - -JEDI_REQVER = '>=0.8.1;<0.9.0' -dependencies.add('jedi', - _("(Experimental) Editor's code completion," - " go-to-definition and help"), - required_version=JEDI_REQVER) - - -class JediPlugin(IntrospectionPlugin): - """ - Jedi based introspection plugin for jedi - - Experimental Editor's code completion, go-to-definition and help - """ - - # ---- IntrospectionPlugin API -------------------------------------------- - name = 'jedi' - - def load_plugin(self): - """Load the Jedi introspection plugin""" - if not programs.is_module_installed('jedi', JEDI_REQVER): - raise ImportError('Requires Jedi %s' % JEDI_REQVER) - jedi.settings.case_insensitive_completion = False - self.busy = True - self._warmup_thread = threading.Thread(target=self.preload) - self._warmup_thread.start() - - def get_completions(self, info): - """Return a list of completion strings""" - completions = self.get_jedi_object('completions', info) - debug_print(str(completions)[:100]) - return [c.name for c in completions] - - def get_info(self, info): - """ - Find the calltip and docs - - Returns a dict like the following: - {'note': 'Function of numpy.core.numeric...', - 'argspec': "(shape, dtype=None, order='C')' - 'docstring': 'Return an array of given...' - 'name': 'ones', - 'calltip': 'ones(shape, dtype=None, order='C')'} - """ - call_def = self.get_jedi_object('goto_definitions', info) - for cd in call_def: - if cd.doc and not cd.doc.rstrip().endswith(')'): - call_def = cd - break - else: - call_def = call_def[0] - name = call_def.name - if name is None: - return - if call_def.module_path: - mod_name = self.get_parent_until(call_def.module_path) - else: - mod_name = None - if not mod_name: - mod_name = call_def.module_name - if call_def.doc.startswith(name + '('): - calltip = getsignaturefromtext(call_def.doc, name) - argspec = calltip[calltip.find('('):] - docstring = call_def.doc[call_def.doc.find(')') + 3:] - elif '(' in call_def.doc.splitlines()[0]: - calltip = call_def.doc.splitlines()[0] - name = call_def.doc.split('(')[0] - docstring = call_def.doc[call_def.doc.find(')') + 3:] - argspec = calltip[calltip.find('('):] - else: - calltip = name + '(...)' - argspec = '()' - docstring = call_def.doc - if call_def.type == 'module': - note = 'Module %s' % mod_name - argspec = '' - calltip = name - elif call_def.type == 'class': - note = 'Class in %s module' % mod_name - elif call_def.doc.startswith('%s(self' % name): - class_name = call_def.full_name.split('.')[-2] - note = 'Method of %s class in %s module' % ( - class_name.capitalize(), mod_name) - else: - note = '%s in %s module' % (call_def.type.capitalize(), - mod_name) - argspec = argspec.replace(' = ', '=') - calltip = calltip.replace(' = ', '=') - debug_print(call_def.name) - - doc_info = dict(name=name, argspec=argspec, - note=note, docstring=docstring, calltip=calltip) - return doc_info - - def get_definition(self, info): - """ - Find a definition location using Jedi - - Follows gotos until a definition is found, or it reaches a builtin - module. Falls back on token lookup if it is in an enaml file or does - not find a match - """ - line, filename = info.line_num, info.filename - def_info, module_path, line_nr = None, None, None - gotos = self.get_jedi_object('goto_assignments', info) - if gotos: - def_info = self.get_definition_info(gotos[0]) - if def_info and def_info['goto_next']: - defns = self.get_jedi_object('goto_definitions', info) - if defns: - new_info = self.get_definition_info(defns[0]) - if not new_info['in_builtin']: - def_info = new_info - elif not def_info: - return - # handle builtins -> try and find the module - if def_info and def_info['in_builtin']: - module_path, line_nr = self.find_in_builtin(def_info) - elif def_info: - module_path = def_info['module_path'] - line_nr = def_info['line_nr'] - if module_path == filename and line_nr == line: - return - return module_path, line_nr - - def set_pref(self, name, value): - """Set a plugin preference to a value""" - pass - - # ---- Private API ------------------------------------------------------- - - def get_jedi_object(self, func_name, info, use_filename=True): - """Call a desired function on a Jedi Script and return the result""" - if not jedi: - return - if DEBUG_EDITOR: - t0 = time.time() - # override IPython qt_loaders ImportDenier behavior - metas = sys.meta_path - for meta in metas: - if (meta.__class__.__name__ == 'ImportDenier' - and hasattr(meta, 'forbid')): - sys.meta_path.remove(meta) - - if use_filename: - filename = info.filename - else: - filename = None - - try: - script = jedi.Script(info.source_code, info.line_num, - info.column, filename) - func = getattr(script, func_name) - val = func() - except Exception as e: - val = None - debug_print('Jedi error (%s)' % func_name) - debug_print(str(e)) - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, str(e)) - if DEBUG_EDITOR: - log_dt(LOG_FILENAME, func_name, t0) - if not val and filename: - return self.get_jedi_object(func_name, info, False) - else: - return val - - @staticmethod - def get_definition_info(defn): - """Extract definition information from the Jedi definition object""" - try: - module_path = defn.module_path - name = defn.name - line_nr = defn.line_nr - description = defn.description - in_builtin = defn.in_builtin_module() - except Exception as e: - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, 'Get Defintion: %s' % e) - return None - pattern = 'class\s+{0}|def\s+{0}|self.{0}\s*=|{0}\s*='.format(name) - if not re.match(pattern, description): - goto_next = True - else: - goto_next = False - return dict(module_path=module_path, line_nr=line_nr, - description=description, name=name, in_builtin=in_builtin, - goto_next=goto_next) - - def find_in_builtin(self, info): - """Find a definition in a builtin file""" - module_path = info['module_path'] - line_nr = info['line_nr'] - ext = osp.splitext(info['module_path'])[1] - desc = info['description'] - name = info['name'] - if ext in self.python_like_exts() and ( - desc.startswith('import ') or desc.startswith('from ')): - path = self.python_like_mod_finder(desc, - osp.dirname(module_path), name) - if path: - info['module_path'] = module_path = path - info['line_nr'] = line_nr = 1 - if ext in self.all_editable_exts(): - pattern = 'from.*\W{0}\W?.*c?import|import.*\W{0}' - if not re.match(pattern.format(info['name']), desc): - line_nr = self.get_definition_from_file(module_path, name, - line_nr) - if not line_nr: - module_path = None - if not ext in self.all_editable_exts(): - line_nr = None - return module_path, line_nr - - def preload(self): - """Preload a list of libraries""" - for lib in ['numpy']: - jedi.preload_module(lib) - self.busy = False - -if __name__ == '__main__': - - from spyderlib.utils.introspection.plugin_manager import CodeInfo - - p = JediPlugin() - p.load_plugin() - - print('Warming up Jedi') - t0 = time.time() - while p.busy: - time.sleep(0.1) - print('Warmed up in %0.1f s' % (time.time() - t0)) - - source_code = "import numpy; numpy.ones(" - docs = p.get_info(CodeInfo('info', source_code, len(source_code))) - - assert docs['calltip'].startswith('ones(') and docs['name'] == 'ones' - - source_code = "import n" - completions = p.get_completions(CodeInfo('completions', source_code, - len(source_code))) - assert 'numpy' in completions - - source_code = "import matplotlib.pyplot as plt; plt.imsave" - path, line_nr = p.get_definition(CodeInfo('definition', source_code, - len(source_code))) - assert 'pyplot.py' in path - - source_code = 'from .plugin_manager import memoize' - path, line_nr = p.get_definition(CodeInfo('definition', source_code, - len(source_code), __file__)) - assert 'plugin_manager.py' in path and 'introspection' in path - - code = ''' -def test(a, b): - """Test docstring""" - pass -test(1,''' - path, line = p.get_definition(CodeInfo('definition', code, len(code), - 'dummy.txt')) - assert line == 2 - - docs = p.get_info(CodeInfo('info', code, len(code), __file__)) - assert 'Test docstring' in docs['docstring'] diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/introspection/module_completion.py spyder-3.0.2+dfsg1/spyderlib/utils/introspection/module_completion.py --- spyder-2.3.8+dfsg1/spyderlib/utils/introspection/module_completion.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/introspection/module_completion.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,322 +0,0 @@ -# -*- coding: utf-8 -*- -"""Module completion auxiliary functions""" - -#------------------------------------------------------------------------------ -# -# Most functions on this file were taken from the file core/completerlib, -# which belongs to the IPython project (v0.13). They were added here because -# a) IPython is not an Spyder runtime dependency, and b) we want to perfom -# module completion not only on our Python console, but also on our source -# code editor. -# -# Several of these functions were modified to make it work according to our -# needs -# -# Distributed under the terms of the BSD License. -# Copyright (C) 2010-2011 The IPython Development Team. -# Copyright (C) 2013 The Spyder Development Team -# -#------------------------------------------------------------------------------ - -import imp -import inspect -import os.path -import pkgutil -import re -from time import time -import sys -from zipimport import zipimporter - -from spyderlib.baseconfig import get_conf_path, running_in_mac_app -from spyderlib.utils.external.pickleshare import PickleShareDB - -#----------------------------------------------------------------------------- -# Globals and constants -#----------------------------------------------------------------------------- - -# Path to the modules database -MODULES_PATH = get_conf_path('db') - -# Time in seconds after which we give up -TIMEOUT_GIVEUP = 20 - -# Py2app only uses .pyc files for the stdlib when optimize=0, -# so we need to add it as another suffix here -if running_in_mac_app(): - suffixes = imp.get_suffixes() + [('.pyc', 'rb', '2')] -else: - suffixes = imp.get_suffixes() - -# Regular expression for the python import statement -import_re = re.compile(r'(?P[a-zA-Z_][a-zA-Z0-9_]*?)' - r'(?P[/\\]__init__)?' - r'(?P%s)$' % - r'|'.join(re.escape(s[0]) for s in suffixes)) - -# Modules database -modules_db = PickleShareDB(MODULES_PATH) - -#----------------------------------------------------------------------------- -# Utility functions -#----------------------------------------------------------------------------- - -def module_list(path): - """ - Return the list containing the names of the modules available in the given - folder. - """ - # sys.path has the cwd as an empty string, but isdir/listdir need it as '.' - if path == '': - path = '.' - - # A few local constants to be used in loops below - pjoin = os.path.join - - if os.path.isdir(path): - # Build a list of all files in the directory and all files - # in its subdirectories. For performance reasons, do not - # recurse more than one level into subdirectories. - files = [] - for root, dirs, nondirs in os.walk(path): - subdir = root[len(path)+1:] - if subdir: - files.extend(pjoin(subdir, f) for f in nondirs) - dirs[:] = [] # Do not recurse into additional subdirectories. - else: - files.extend(nondirs) - else: - try: - files = list(zipimporter(path)._files.keys()) - except: - files = [] - - # Build a list of modules which match the import_re regex. - modules = [] - for f in files: - m = import_re.match(f) - if m: - modules.append(m.group('name')) - return list(set(modules)) - - -def get_root_modules(paths): - """ - Returns list of names of all modules from PYTHONPATH folders. - - paths : list - A list of additional paths that Spyder adds to PYTHONPATH. They are - comming from our PYTHONPATH manager and from the currently selected - project. - """ - modules = [] - spy_modules = [] - - for path in paths: - spy_modules += module_list(path) - spy_modules = set(spy_modules) - if '__init__' in spy_modules: - spy_modules.remove('__init__') - spy_modules = list(spy_modules) - - if 'rootmodules' in modules_db: - return spy_modules + modules_db['rootmodules'] - - t = time() - modules = list(sys.builtin_module_names) - # TODO: Change this sys.path for console's interpreter sys.path - for path in sys.path: - modules += module_list(path) - if time() - t > TIMEOUT_GIVEUP: - print("Module list generation is taking too long, we give up.\n") - modules_db['rootmodules'] = [] - return [] - - modules = set(modules) - excluded_modules = ['__init__'] + spy_modules - for mod in excluded_modules: - if mod in modules: - modules.remove(mod) - modules = list(modules) - - modules_db['rootmodules'] = modules - return spy_modules + modules - - -def get_submodules(mod): - """Get all submodules of a given module""" - def catch_exceptions(module): - pass - try: - m = __import__(mod) - submodules = [mod] - submods = pkgutil.walk_packages(m.__path__, m.__name__ + '.', - catch_exceptions) - for sm in submods: - sm_name = sm[1] - submodules.append(sm_name) - except ImportError: - return [] - except: - return [mod] - - return submodules - - -def is_importable(module, attr, only_modules): - if only_modules: - return inspect.ismodule(getattr(module, attr)) - else: - return not(attr[:2] == '__' and attr[-2:] == '__') - - -def try_import(mod, only_modules=False): - try: - m = __import__(mod) - except: - return [] - mods = mod.split('.') - for module in mods[1:]: - m = getattr(m, module) - - m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__ - - completions = [] - if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init: - completions.extend([attr for attr in dir(m) if - is_importable(m, attr, only_modules)]) - - completions.extend(getattr(m, '__all__', [])) - if m_is_init: - completions.extend(module_list(os.path.dirname(m.__file__))) - completions = set(completions) - if '__init__' in completions: - completions.remove('__init__') - return list(completions) - - -def dot_completion(mod, paths): - if len(mod) < 2: - return [x for x in get_root_modules(paths) if x.startswith(mod[0])] - completion_list = try_import('.'.join(mod[:-1]), True) - completion_list = [x for x in completion_list if x.startswith(mod[-1])] - completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list] - return completion_list - -#----------------------------------------------------------------------------- -# Main functions -#----------------------------------------------------------------------------- - -def module_completion(line, paths=[]): - """ - Returns a list containing the completion possibilities for an import line. - - The line looks like this : - 'import xml.d' - 'from xml.dom import' - """ - - words = line.split(' ') - nwords = len(words) - - # from whatever -> 'import ' - if nwords == 3 and words[0] == 'from': - if words[2].startswith('i') or words[2] == '': - return ['import '] - else: - return [] - - # 'import xy or import xy, ' - if words[0] == 'import': - if nwords == 2 and words[1] == '': - return get_root_modules(paths) - if ',' == words[-1][-1]: - return [' '] - mod = words[-1].split('.') - return dot_completion(mod, paths) - - # 'from xy' - if nwords < 3 and (words[0] == 'from'): - if nwords == 1: - return get_root_modules(paths) - mod = words[1].split('.') - return dot_completion(mod, paths) - - # 'from xyz import abc' - if nwords >= 3 and words[0] == 'from': - mod = words[1] - completion_list = try_import(mod) - if words[2] == 'import' and words[3] != '': - if '(' in words[-1]: - words = words[:-2] + words[-1].split('(') - if ',' in words[-1]: - words = words[:-2] + words[-1].split(',') - return [x for x in completion_list if x.startswith(words[-1])] - else: - return completion_list - - return [] - - -def reset(): - """Clear root modules database""" - if 'rootmodules' in modules_db: - del modules_db['rootmodules'] - - -def get_preferred_submodules(): - """ - Get all submodules of the main scientific modules and others of our - interest - """ - if 'submodules' in modules_db: - return modules_db['submodules'] - - mods = ['numpy', 'scipy', 'sympy', 'pandas', 'networkx', 'statsmodels', - 'matplotlib', 'sklearn', 'skimage', 'mpmath', 'os', 'PIL', - 'OpenGL', 'array', 'audioop', 'binascii', 'cPickle', 'cStringIO', - 'cmath', 'collections', 'datetime', 'errno', 'exceptions', 'gc', - 'imageop', 'imp', 'itertools', 'marshal', 'math', 'mmap', 'msvcrt', - 'nt', 'operator', 'parser', 'rgbimg', 'signal', 'strop', 'sys', - 'thread', 'time', 'wx', 'xxsubtype', 'zipimport', 'zlib', 'nose', - 'PyQt4', 'PySide', 'os.path'] - - submodules = [] - for m in mods: - submods = get_submodules(m) - submodules += submods - - modules_db['submodules'] = submodules - return submodules - -#----------------------------------------------------------------------------- -# Tests -#----------------------------------------------------------------------------- - -if __name__ == "__main__": - # Some simple tests. - # Sort operations are done by the completion widget, so we have to - # replicate them here. - # We've chosen to use xml on most tests because it's on the standard - # library. This way we can ensure they work on all plataforms. - - assert sorted(module_completion('import xml.')) == \ - ['xml.dom', 'xml.etree', 'xml.parsers', 'xml.sax'] - - assert sorted(module_completion('import xml.d')) == ['xml.dom'] - - assert module_completion('from xml.etree ') == ['import '] - - assert sorted(module_completion('from xml.etree import '), key=str.lower) ==\ - ['cElementTree', 'ElementInclude', 'ElementPath', 'ElementTree'] - - assert module_completion('import sys, zl') == ['zlib'] - - s = 'from xml.etree.ElementTree import ' - assert module_completion(s + 'V') == ['VERSION'] - - assert sorted(module_completion(s + 'VERSION, XM')) == \ - ['XML', 'XMLID', 'XMLParser', 'XMLTreeBuilder'] - - assert module_completion(s + '(dum') == ['dump'] - - assert module_completion(s + '(dump, Su') == ['SubElement'] diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/introspection/plugin_manager.py spyder-3.0.2+dfsg1/spyderlib/utils/introspection/plugin_manager.py --- spyder-2.3.8+dfsg1/spyderlib/utils/introspection/plugin_manager.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/introspection/plugin_manager.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,530 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2015 The Spyder development team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -from __future__ import print_function - -import re -from collections import OrderedDict -import functools -import os.path as osp -import os -import imp -import time - -from spyderlib.baseconfig import DEBUG, get_conf_path, debug_print -from spyderlib.utils.introspection.module_completion import ( - get_preferred_submodules) -from spyderlib.utils import sourcecode -from spyderlib.utils.debug import log_last_error - -from spyderlib.qt.QtGui import QApplication -from spyderlib.qt.QtCore import Signal, QThread, QObject, QTimer - - -PLUGINS = ['rope', 'jedi', 'fallback'] - -LOG_FILENAME = get_conf_path('introspection.log') -DEBUG_EDITOR = DEBUG >= 3 -LEAD_TIME_SEC = 0.25 - - -class RequestHandler(QObject): - - """Handle introspection request. - """ - - introspection_complete = Signal() - - def __init__(self, code_info, plugins): - super(RequestHandler, self).__init__() - self.info = code_info - self.timer = QTimer() - self.timer.singleShot(LEAD_TIME_SEC * 1000, self._handle_timeout) - self.waiting = True - self.pending = {} - self.result = None - self.plugins = plugins - self._start_time = time.time() - self._threads = {} - for plugin in plugins: - self._make_async_call(plugin, code_info) - - def _handle_timeout(self): - debug_print('got timeout') - if self.pending: - for plugin in self.plugins: - if plugin.name in self.pending: - self._finalize(plugin.name, self.pending[plugin.name]) - return - self.waiting = False - - def _handle_incoming(self, name): - # coerce to a str in case it is a QString - name = str(name) - self._threads[name].wait() - if self.result: - return - result = self._threads[name].result - if name == self.plugins[0].name or not self.waiting: - self._finalize(name, result) - else: - self.pending[name] = result - - def _make_async_call(self, plugin, info): - """Trigger an introspection job in a thread""" - self._threads[str(plugin.name)] = thread = IntrospectionThread(plugin, info) - thread.request_handled.connect(self._handle_incoming) - thread.start() - - def _finalize(self, name, result): - self.result = result - self.waiting = False - self.pending = None - delta = time.time() - self._start_time - debug_print('%s request from %s complete: "%s" in %.1f sec' - % (self.info.name, name, str(result)[:100], delta)) - self.introspection_complete.emit() - - -class GetSubmodulesThread(QThread): - - """ - A thread to generate a list of submodules to be passed to - introspection plugins - """ - - def __init__(self): - super(GetSubmodulesThread, self).__init__() - self.submods = [] - - def run(self): - self.submods = get_preferred_submodules() - - -class IntrospectionThread(QThread): - - """ - A thread to perform an introspection task - """ - - request_handled = Signal(str) - - def __init__(self, plugin, info): - super(IntrospectionThread, self).__init__() - self.plugin = plugin - self.info = info - self.result = None - - def run(self): - func = getattr(self.plugin, 'get_%s' % self.info.name) - self.plugin.busy = True - try: - self.result = func(self.info) - except Exception as e: - debug_print(e) - self.plugin.busy = False - self.request_handled.emit(self.plugin.name) - - -class CodeInfo(object): - - id_regex = re.compile(r'[^\d\W][\w\.]*', re.UNICODE) - func_call_regex = re.compile(r'([^\d\W][\w\.]*)\([^\)\()]*\Z', - re.UNICODE) - - def __init__(self, name, source_code, position, filename=None, - is_python_like=True, **kwargs): - self.__dict__.update(kwargs) - self.name = name - self.filename = filename - self.source_code = source_code - self.position = position - self.is_python_like = is_python_like - - if position == 0: - self.lines = [] - self.line_num = 0 - self.obj = None - self.full_obj = None - else: - self._get_info() - - def _get_info(self): - self.lines = self.source_code[:self.position].splitlines() - self.line_num = len(self.lines) - - self.line = self.lines[-1] - self.column = len(self.lines[-1]) - - tokens = re.findall(self.id_regex, self.line) - if tokens and self.line.endswith(tokens[-1]): - self.obj = tokens[-1] - else: - self.obj = None - - self.full_obj = self.obj - - if self.obj: - full_line = self.source_code.splitlines()[self.line_num - 1] - rest = full_line[self.column:] - match = re.match(self.id_regex, rest) - if match: - self.full_obj = self.obj + match.group() - - if (self.name in ['info', 'definition'] and (not self.obj) - and self.is_python_like): - func_call = re.findall(self.func_call_regex, self.line) - if func_call: - self.obj = func_call[-1] - self.column = self.line.index(self.obj) + len(self.obj) - self.position = self.position - len(self.line) + self.column - - def split_words(self, position=None): - """ - Split our source code into valid identifiers. - - P""" - if position is None: - position = self.offset - text = self.source_code[:position] - return re.findall(self.id_regex, text) - - def __eq__(self, other): - try: - return self.__dict__ == other.__dict__ - except Exception: - return False - - -class PluginManager(QObject): - - send_to_inspector = Signal(str, str, str, str, bool) - edit_goto = Signal(str, int, str) - - def __init__(self, editor_widget): - super(PluginManager, self).__init__() - self.editor_widget = editor_widget - self.pending = None - self.busy = False - self.load_plugins() - self._submods_thread = GetSubmodulesThread() - self._submods_thread.finished.connect(self._update_extension_modules) - self._submods_thread.start() - - def load_plugins(self): - """Get and load a plugin, checking in order of PLUGINS""" - plugins = OrderedDict() - for plugin_name in PLUGINS: - mod_name = plugin_name + '_plugin' - try: - mod = __import__('spyderlib.utils.introspection.' + mod_name, - fromlist=[mod_name]) - cls = getattr(mod, '%sPlugin' % plugin_name.capitalize()) - plugin = cls() - plugin.load_plugin() - except Exception as e: - debug_print(e) - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME) - else: - plugins[plugin_name] = plugin - debug_print('Instropection Plugin Loaded: %s' % plugin.name) - self.plugins = plugins - debug_print('Plugins loaded: %s' % self.plugins.keys()) - return plugins - - def _get_code_info(self, name, position=None, **kwargs): - - editor = self.editor_widget.get_current_editor() - finfo = self.editor_widget.get_current_finfo() - - if position is None: - position = editor.get_position('cursor') - - kwargs['editor'] = editor - kwargs['finfo'] = finfo - kwargs['editor_widget'] = self.editor_widget - - return CodeInfo(name, finfo.get_source_code(), position, - finfo.filename, editor.is_python_like, **kwargs) - - def get_completions(self, automatic): - """Get code completion""" - info = self._get_code_info('completions', automatic=automatic) - - if 'jedi' in self.plugins and not self.plugins['jedi'].busy: - self._handle_request(info) - - elif info.line.lstrip().startswith(('import ', 'from ')): - self._handle_request(info, 'fallback') - - else: - self._handle_request(info) - - def go_to_definition(self, position): - """Go to definition""" - info = self._get_code_info('definition', position) - - self._handle_request(info) - - def show_object_info(self, position, auto=True): - """Show signature calltip and/or docstring in the Object Inspector""" - # auto is True means that this method was called automatically, - # i.e. the user has just entered an opening parenthesis -- in that - # case, we don't want to force the object inspector to be visible, - # to avoid polluting the window layout - info = self._get_code_info('info', position, auto=auto) - self._handle_request(info) - - def validate(self): - """Validate the plugins""" - if not self.busy: - for plugin in self.plugins.values(): - plugin.validate() - - def is_editor_ready(self): - """Check if the main app is starting up""" - if self.editor_widget: - window = self.editor_widget.window() - if hasattr(window, 'is_starting_up') and not window.is_starting_up: - return True - - def _handle_request(self, info, desired=None): - """Handle an incoming request from the user.""" - debug_print('%s request:' % info.name) - - editor = info.editor - if ((not editor.is_python_like()) - or sourcecode.is_keyword(info.obj) - or editor.in_comment_or_string()): - desired = 'fallback' - - self.pending = (info, desired) - if not self.busy: - self._handle_pending() - - def _handle_pending(self): - """Handle any pending requests, sending them to the correct plugin.""" - if not self.pending: - self._post_message('') - return - info, desired = self.pending - if desired and self.plugins[desired].busy: - return - self.busy = True - - if desired: - plugins = [self.plugins[desired]] - elif info.name == 'definition' and not info.editor.is_python(): - plugins = [p for p in self.plugins.values() if not p.busy] - else: - # use all but the fallback - plugins = [p for p in list(self.plugins.values())[:-1] if not p.busy] - - self.request = RequestHandler(info, plugins) - self.request.introspection_complete.connect( - self._introspection_complete) - self.pending = None - - def _introspection_complete(self): - """ - Handle an introspection response from the thread. - - Route the response to the correct handler, and then handle - any pending requests. - """ - self.busy = False - result = self.request.result - info = self.request.info - current = self._get_code_info('current') - - if result and current.filename == info.filename: - func = getattr(self, '_handle_%s_response' % info.name) - try: - func(result, current, info) - except Exception as e: - debug_print(e) - elif current.filename == info.filename and info.name == 'definition': - result = self.plugins['fallback'].get_definition(info) - - if info == self.pending: - self.pending = None - - self._handle_pending() - - def _handle_completions_response(self, comp_list, info, prev_info): - """ - Handle a `completions` response. - - Only handle the response if we are on the same line of text and - on the same `obj` as the original request. - """ - if info.line_num != prev_info.line_num: - return - completion_text = info.obj - prev_text = prev_info.obj - - if prev_info.obj is None: - completion_text = '' - prev_text = '' - - if not completion_text.startswith(prev_text): - return - - if info.full_obj and len(info.full_obj) > len(info.obj): - new_list = [c for c in comp_list if c.startswith(info.full_obj)] - if new_list: - pos = info.editor.get_position('cursor') - new_pos = pos + len(info.full_obj) - len(info.obj) - info.editor.set_cursor_position(new_pos) - completion_text = info.full_obj - comp_list = new_list - - if '.' in completion_text: - completion_text = completion_text.split('.')[-1] - - comp_list = [c.split('.')[-1] for c in comp_list] - comp_list = [c for c in comp_list if c.startswith(completion_text)] - - info.editor.show_completion_list(comp_list, completion_text, - prev_info.automatic) - - def _handle_info_response(self, resp, info, prev_info): - """ - Handle an `info` response, triggering a calltip and/or docstring. - - Only handle the response if we are on the same line of text as - when the request was initiated. - """ - if info.line_num != prev_info.line_num: - return - - if resp['calltip']: - info.editor.show_calltip('Arguments', resp['calltip'], - signature=True, - at_position=prev_info.position) - - if resp['name']: - self.send_to_inspector.emit( - resp['name'], resp['argspec'], - resp['note'], resp['docstring'], - not prev_info.auto) - - def _handle_definition_response(self, resp, info, prev_info): - """Handle a `definition` response""" - fname, lineno = resp - self.edit_goto.emit(fname, lineno, "") - - def _update_extension_modules(self): - """Set the extension_modules after submods thread finishes""" - for plugin in self.plugins.values(): - plugin.set_pref('extension_modules', - self._submods_thread.submods) - - def _post_message(self, message, timeout=60000): - """ - Post a message to the main window status bar with a timeout in ms - """ - if self.editor_widget: - statusbar = self.editor_widget.window().statusBar() - statusbar.showMessage(message, timeout) - QApplication.processEvents() - - -def memoize(obj): - """ - Memoize objects to trade memory for execution speed - - Use a limited size cache to store the value, which takes into account - The calling args and kwargs - - See https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize - """ - cache = obj.cache = {} - - @functools.wraps(obj) - def memoizer(*args, **kwargs): - key = str(args) + str(kwargs) - if key not in cache: - cache[key] = obj(*args, **kwargs) - # only keep the most recent 100 entries - if len(cache) > 100: - cache.popitem(last=False) - return cache[key] - return memoizer - - -class IntrospectionPlugin(object): - - busy = False - - def load_plugin(self): - """Initialize the plugin""" - pass - - def get_completions(self, info): - """Get a list of completions""" - pass - - def get_info(self, info): - """ - Find the calltip and docs - - Returns a dict like the following: - {'note': 'Function of numpy.core.numeric...', - 'argspec': "(shape, dtype=None, order='C')' - 'docstring': 'Return an array of given...' - 'name': 'ones', - 'calltip': 'ones(shape, dtype=None, order='C')'} - """ - pass - - def get_definition(self, info): - """Get a (filename, line_num) location for a definition""" - pass - - def set_pref(self, name, value): - """Set a plugin preference to a value""" - pass - - def validate(self): - """Validate the plugin""" - pass - - @staticmethod - @memoize - def get_parent_until(path): - """ - Given a file path, determine the full module path - - e.g. '/usr/lib/python2.7/dist-packages/numpy/core/__init__.pyc' yields - 'numpy.core' - """ - dirname = osp.dirname(path) - try: - mod = osp.basename(path) - mod = osp.splitext(mod)[0] - imp.find_module(mod, [dirname]) - except ImportError: - return - items = [mod] - while 1: - items.append(osp.basename(dirname)) - try: - dirname = osp.dirname(dirname) - imp.find_module('__init__', [dirname + os.sep]) - except ImportError: - break - return '.'.join(reversed(items)) - - -if __name__ == '__main__': - code = 'import numpy' - test = CodeInfo('test', code, len(code) - 2) - assert test.obj == 'num' - assert test.full_obj == 'numpy' - test2 = CodeInfo('test', code, len(code) - 2) - assert test == test2 diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/introspection/rope_plugin.py spyder-3.0.2+dfsg1/spyderlib/utils/introspection/rope_plugin.py --- spyder-2.3.8+dfsg1/spyderlib/utils/introspection/rope_plugin.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/introspection/rope_plugin.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,301 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2013 The Spyder Development Team -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Rope introspection plugin -""" - -import time - -from spyderlib import dependencies -from spyderlib.baseconfig import get_conf_path, _, STDERR -from spyderlib.utils import encoding, programs -from spyderlib.py3compat import PY2 -from spyderlib.utils.dochelpers import getsignaturefromtext -from spyderlib.utils import sourcecode -from spyderlib.utils.debug import log_last_error, log_dt -from spyderlib.utils.introspection.plugin_manager import ( - DEBUG_EDITOR, LOG_FILENAME, IntrospectionPlugin) -try: - try: - from spyderlib import rope_patch - rope_patch.apply() - except ImportError: - # rope 0.9.2/0.9.3 is not installed - pass - import rope.base.libutils - import rope.contrib.codeassist -except ImportError: - pass - - -ROPE_REQVER = '>=0.9.2' -dependencies.add('rope', - _("Editor's code completion, go-to-definition and help"), - required_version=ROPE_REQVER) - -#TODO: The following preferences should be customizable in the future -ROPE_PREFS = {'ignore_syntax_errors': True, - 'ignore_bad_imports': True, - 'soa_followed_calls': 2, - 'extension_modules': [], - } - - -class RopePlugin(IntrospectionPlugin): - """ - Rope based introspection plugin for jedi - - Editor's code completion, go-to-definition and help - """ - - project = None - - # ---- IntrospectionPlugin API -------------------------------------------- - name = 'rope' - - def load_plugin(self): - """Load the Rope introspection plugin""" - if not programs.is_module_installed('rope', ROPE_REQVER): - raise ImportError('Requires Rope %s' % ROPE_REQVER) - self.project = None - self.create_rope_project(root_path=get_conf_path()) - - def get_completions(self, info): - """Get a list of completions using Rope""" - if self.project is None: - return - filename = info.filename - source_code = info.source_code - offset = info.position - - if PY2: - filename = filename.encode('utf-8') - else: - #TODO: test if this is working without any further change in - # Python 3 with a user account containing unicode characters - pass - try: - resource = rope.base.libutils.path_to_resource(self.project, - filename) - except Exception as _error: - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, "path_to_resource: %r" % filename) - resource = None - try: - if DEBUG_EDITOR: - t0 = time.time() - proposals = rope.contrib.codeassist.code_assist(self.project, - source_code, offset, resource, maxfixes=3) - proposals = rope.contrib.codeassist.sorted_proposals(proposals) - if DEBUG_EDITOR: - log_dt(LOG_FILENAME, "code_assist/sorted_proposals", t0) - return [proposal.name for proposal in proposals] - except Exception as _error: #analysis:ignore - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, "get_completion_list") - - def get_info(self, info): - """Get a formatted calltip and docstring from Rope""" - if self.project is None: - return - filename = info.filename - source_code = info.source_code - offset = info.position - - if PY2: - filename = filename.encode('utf-8') - else: - #TODO: test if this is working without any further change in - # Python 3 with a user account containing unicode characters - pass - try: - resource = rope.base.libutils.path_to_resource(self.project, - filename) - except Exception as _error: - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, "path_to_resource: %r" % filename) - resource = None - try: - if DEBUG_EDITOR: - t0 = time.time() - cts = rope.contrib.codeassist.get_calltip( - self.project, source_code, offset, resource, - ignore_unknown=False, remove_self=True, maxfixes=3) - if DEBUG_EDITOR: - log_dt(LOG_FILENAME, "get_calltip", t0) - if cts is not None: - while '..' in cts: - cts = cts.replace('..', '.') - if '(.)' in cts: - cts = cts.replace('(.)', '(...)') - try: - doc_text = rope.contrib.codeassist.get_doc(self.project, - source_code, offset, resource, maxfixes=3) - if DEBUG_EDITOR: - log_dt(LOG_FILENAME, "get_doc", t0) - except Exception as _error: - doc_text = '' - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, "get_doc") - return self.handle_info(cts, doc_text, source_code, offset) - except Exception as _error: #analysis:ignore - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, "get_calltip_text") - - def handle_info(self, cts, doc_text, source_code, offset): - - obj_fullname = '' - calltip = '' - argspec = '' - note = '' - - if cts: - cts = cts.replace('.__init__', '') - parpos = cts.find('(') - if parpos: - obj_fullname = cts[:parpos] - obj_name = obj_fullname.split('.')[-1] - cts = cts.replace(obj_fullname, obj_name) - calltip = cts - if ('()' in cts) or ('(...)' in cts): - # Either inspected object has no argument, or it's - # a builtin or an extension -- in this last case - # the following attempt may succeed: - calltip = getsignaturefromtext(doc_text, obj_name) - if not obj_fullname: - obj_fullname = sourcecode.get_primary_at(source_code, offset) - if obj_fullname and not obj_fullname.startswith('self.'): - # doc_text was generated by utils.dochelpers.getdoc - if type(doc_text) is dict: - obj_fullname = doc_text['name'] or obj_fullname - argspec = doc_text['argspec'] - note = doc_text['note'] - doc_text = doc_text['docstring'] - elif calltip: - argspec_st = calltip.find('(') - argspec = calltip[argspec_st:] - module_end = obj_fullname.rfind('.') - module = obj_fullname[:module_end] - note = 'Present in %s module' % module - - return dict(name=obj_fullname, argspec=argspec, note=note, - docstring=doc_text, calltip=calltip) - - def get_definition(self, info): - """Find a definition location using Rope""" - if self.project is None: - return - - filename = info.filename - source_code = info.source_code - offset = info.position - - if PY2: - filename = filename.encode('utf-8') - else: - #TODO: test if this is working without any further change in - # Python 3 with a user account containing unicode characters - pass - try: - resource = rope.base.libutils.path_to_resource(self.project, - filename) - except Exception as _error: - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, "path_to_resource: %r" % filename) - resource = None - try: - if DEBUG_EDITOR: - t0 = time.time() - resource, lineno = rope.contrib.codeassist.get_definition_location( - self.project, source_code, offset, resource, maxfixes=3) - if DEBUG_EDITOR: - log_dt(LOG_FILENAME, "get_definition_location", t0) - if resource is not None: - filename = resource.real_path - if filename and lineno: - return filename, lineno - except Exception as _error: #analysis:ignore - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, "get_definition_location") - - def validate(self): - """Validate the Rope project""" - if self.project is not None: - self.project.validate(self.project.root) - - def set_pref(self, key, value): - """Set a Rope preference""" - if self.project is not None: - self.project.prefs.set(key, value) - - # ---- Private API ------------------------------------------------------- - - def create_rope_project(self, root_path): - """Create a Rope project on a desired path""" - if PY2: - root_path = encoding.to_fs_from_unicode(root_path) - else: - #TODO: test if this is working without any further change in - # Python 3 with a user account containing unicode characters - pass - try: - import rope.base.project - self.project = rope.base.project.Project(root_path, **ROPE_PREFS) - except ImportError: - print >>STDERR, 'project error' - self.project = None - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, - "create_rope_project: %r" % root_path) - except TypeError: - # Compatibility with new Mercurial API (>= 1.3). - # New versions of rope (> 0.9.2) already handle this issue - self.project = None - if DEBUG_EDITOR: - log_last_error(LOG_FILENAME, - "create_rope_project: %r" % root_path) - self.validate() - - def close_rope_project(self): - """Close the Rope project""" - if self.project is not None: - self.project.close() - - -if __name__ == '__main__': - - from spyderlib.utils.introspection.plugin_manager import CodeInfo - - p = RopePlugin() - p.load_plugin() - - source_code = "import numpy; numpy.ones" - docs = p.get_info(CodeInfo('info', source_code, len(source_code), - __file__)) - assert 'ones(' in docs['calltip'] and 'ones(' in docs['docstring'] - - source_code = "import numpy; n" - completions = p.get_completions(CodeInfo('completions', source_code, - len(source_code), __file__)) - assert 'numpy' in completions - - source_code = "import matplotlib.pyplot as plt; plt.imsave" - path, line_nr = p.get_definition(CodeInfo('definition', source_code, - len(source_code), __file__)) - assert 'pyplot.py' in path - - code = ''' -def test(a, b): - """Test docstring""" - pass -test(1,''' - path, line = p.get_definition(CodeInfo('definition', code, len(code), - 'dummy.txt')) - assert line == 2 - - docs = p.get_info(CodeInfo('info', code, len(code), __file__)) - assert 'Test docstring' in docs['docstring'] diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/iofuncs.py spyder-3.0.2+dfsg1/spyderlib/utils/iofuncs.py --- spyder-2.3.8+dfsg1/spyderlib/utils/iofuncs.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/iofuncs.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,589 +0,0 @@ -# -*- coding:utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Input/Output Utilities - -Note: 'load' functions has to return a dictionary from which a globals() - namespace may be updated -""" - -from __future__ import print_function - -import sys -import os -import tarfile -import os.path as osp -import shutil -import warnings -import json -import inspect -import dis - -# - If pandas fails to import here (for any reason), Spyder -# will crash at startup (e.g. see Issue 2300) -# - This also prevents Spyder to start IPython kernels -# (see Issue 2456) -try: - import pandas as pd -except: - pd = None #analysis:ignore - -# Local imports -from spyderlib.py3compat import pickle, to_text_string, getcwd, PY2 - - -class MatlabStruct(dict): - """ - Matlab style struct, enhanced. - - Supports dictionary and attribute style access. Can be pickled, - and supports code completion in a REPL. - - Examples - ======== - >>> from spyderlib.utils.iofuncs import MatlabStruct - >>> a = MatlabStruct() - >>> a.b = 'spam' # a["b"] == 'spam' - >>> a.c["d"] = 'eggs' # a.c.d == 'eggs' - >>> print(a) - {'c': {'d': 'eggs'}, 'b': 'spam'} - - """ - def __getattr__(self, attr): - """Access the dictionary keys for unknown attributes.""" - try: - return self[attr] - except KeyError: - msg = "'MatlabStruct' object has no attribute %s" % attr - raise AttributeError(msg) - - def __getitem__(self, attr): - """ - Get a dict value; create a MatlabStruct if requesting a submember. - - Do not create a key if the attribute starts with an underscore. - """ - if attr in self.keys() or attr.startswith('_'): - return dict.__getitem__(self, attr) - frame = inspect.currentframe() - # step into the function that called us - if frame.f_back.f_back and self._is_allowed(frame.f_back.f_back): - dict.__setitem__(self, attr, MatlabStruct()) - elif self._is_allowed(frame.f_back): - dict.__setitem__(self, attr, MatlabStruct()) - return dict.__getitem__(self, attr) - - def _is_allowed(self, frame): - """Check for allowed op code in the calling frame""" - allowed = [dis.opmap['STORE_ATTR'], dis.opmap['LOAD_CONST'], - dis.opmap.get('STOP_CODE', 0)] - bytecode = frame.f_code.co_code - instruction = bytecode[frame.f_lasti + 3] - instruction = ord(instruction) if PY2 else instruction - return instruction in allowed - - __setattr__ = dict.__setitem__ - __delattr__ = dict.__delitem__ - - @property - def __dict__(self): - """Allow for code completion in a REPL""" - return self.copy() - - -def get_matlab_value(val): - """ - Extract a value from a Matlab file - - From the oct2py project, see - http://pythonhosted.org/oct2py/conversions.html - """ - import numpy as np - if not isinstance(val, np.ndarray): - return val - # check for objects - if "'|O" in str(val.dtype) or "O'" in str(val.dtype): - data = MatlabStruct() - for key in val.dtype.fields.keys(): - data[key] = get_matlab_value(val[key][0]) - return data - # handle cell arrays - if val.dtype == np.object: - if val.size == 1: - val = val[0] - if "'|O" in str(val.dtype) or "O'" in str(val.dtype): - val = get_matlab_value(val) - if isinstance(val, MatlabStruct): - return val - if val.size == 1: - val = val.flatten() - if val.dtype == np.object: - if len(val.shape) > 2: - val = val.T - val = np.array([get_matlab_value(val[i].T) - for i in range(val.shape[0])]) - if len(val.shape) > 1: - if len(val.shape) == 2: - val = val.T - try: - return val.astype(val[0][0].dtype) - except ValueError: - # dig into the cell type - for row in range(val.shape[0]): - for i in range(val[row].size): - if not np.isscalar(val[row][i]): - if val[row][i].size > 1: - val[row][i] = val[row][i].squeeze() - else: - val[row][i] = val[row][i][0] - else: - val = np.array([get_matlab_value(val[i]) - for i in range(val.size)]) - if len(val.shape) == 1 or val.shape[0] == 1 or val.shape[1] == 1: - val = val.flatten() - val = val.tolist() - elif val.size == 1: - if hasattr(val, 'flatten'): - val = val.flatten()[0] - if isinstance(val, MatlabStruct) and isinstance(val.size, MatlabStruct): - del val['size'] - del val['dtype'] - return val - - -try: - import numpy as np - try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - import scipy.io as spio - except AttributeError: - # Python 2.5: warnings.catch_warnings was introduced in Python 2.6 - import scipy.io as spio # analysis:ignore - except: - spio = None - - if spio is None: - load_matlab = None - save_matlab = None - else: - def load_matlab(filename): - try: - out = spio.loadmat(filename) - for key, value in list(out.items()): - out[key] = get_matlab_value(value) - return out, None - except Exception as error: - return None, str(error) - - def save_matlab(data, filename): - try: - spio.savemat(filename, data, oned_as='row') - except Exception as error: - return str(error) -except: - load_matlab = None - save_matlab = None - - -try: - import numpy as np # analysis:ignore - - def load_array(filename): - try: - name = osp.splitext(osp.basename(filename))[0] - data = np.load(filename) - if hasattr(data, 'keys'): - return data, None - else: - return {name: data}, None - except Exception as error: - return None, str(error) - - def __save_array(data, basename, index): - """Save numpy array""" - fname = basename + '_%04d.npy' % index - np.save(fname, data) - return fname -except: - load_array = None - - -try: - from spyderlib.pil_patch import Image - - if sys.byteorder == 'little': - _ENDIAN = '<' - else: - _ENDIAN = '>' - DTYPES = { - "1": ('|b1', None), - "L": ('|u1', None), - "I": ('%si4' % _ENDIAN, None), - "F": ('%sf4' % _ENDIAN, None), - "I;16": ('|u2', None), - "I;16S": ('%si2' % _ENDIAN, None), - "P": ('|u1', None), - "RGB": ('|u1', 3), - "RGBX": ('|u1', 4), - "RGBA": ('|u1', 4), - "CMYK": ('|u1', 4), - "YCbCr": ('|u1', 4), - } - def __image_to_array(filename): - img = Image.open(filename) - try: - dtype, extra = DTYPES[img.mode] - except KeyError: - raise RuntimeError("%s mode is not supported" % img.mode) - shape = (img.size[1], img.size[0]) - if extra is not None: - shape += (extra,) - return np.array(img.getdata(), dtype=np.dtype(dtype)).reshape(shape) - - def load_image(filename): - try: - name = osp.splitext(osp.basename(filename))[0] - return {name: __image_to_array(filename)}, None - except Exception as error: - return None, str(error) -except: - load_image = None - - -def load_pickle(filename): - """Load a pickle file as a dictionary""" - try: - if pd: - return pd.read_pickle(filename), None - else: - with open(filename, 'rb') as fid: - data = pickle.load(fid) - return data, None - except Exception as err: - return None, str(err) - - -def load_json(filename): - """Load a json file as a dictionary""" - try: - with open(filename, 'rb') as fid: - data = json.load(fid) - return data, None - except Exception as err: - return None, str(err) - - -def save_dictionary(data, filename): - """Save dictionary in a single file .spydata file""" - filename = osp.abspath(filename) - old_cwd = getcwd() - os.chdir(osp.dirname(filename)) - error_message = None - try: - saved_arrays = {} - if load_array is not None: - # Saving numpy arrays with np.save - arr_fname = osp.splitext(filename)[0] - for name in list(data.keys()): - if isinstance(data[name], np.ndarray) and data[name].size > 0: - # Saving arrays at data root - fname = __save_array(data[name], arr_fname, - len(saved_arrays)) - saved_arrays[(name, None)] = osp.basename(fname) - data.pop(name) - elif isinstance(data[name], (list, dict)): - # Saving arrays nested in lists or dictionaries - if isinstance(data[name], list): - iterator = enumerate(data[name]) - else: - iterator = iter(list(data[name].items())) - to_remove = [] - for index, value in iterator: - if isinstance(value, np.ndarray) and value.size > 0: - fname = __save_array(value, arr_fname, - len(saved_arrays)) - saved_arrays[(name, index)] = osp.basename(fname) - to_remove.append(index) - for index in sorted(to_remove, reverse=True): - data[name].pop(index) - if saved_arrays: - data['__saved_arrays__'] = saved_arrays - pickle_filename = osp.splitext(filename)[0]+'.pickle' - with open(pickle_filename, 'wb') as fdesc: - pickle.dump(data, fdesc, 2) - tar = tarfile.open(filename, "w") - for fname in [pickle_filename]+[fn for fn in list(saved_arrays.values())]: - tar.add(osp.basename(fname)) - os.remove(fname) - tar.close() - if saved_arrays: - data.pop('__saved_arrays__') - except (RuntimeError, pickle.PicklingError, TypeError) as error: - error_message = to_text_string(error) - os.chdir(old_cwd) - return error_message - - -def load_dictionary(filename): - """Load dictionary from .spydata file""" - filename = osp.abspath(filename) - old_cwd = getcwd() - os.chdir(osp.dirname(filename)) - data = None - error_message = None - try: - tar = tarfile.open(filename, "r") - tar.extractall() - pickle_filename = osp.splitext(filename)[0]+'.pickle' - try: - # Old format (Spyder 2.0-2.1 for Python 2) - with open(pickle_filename, 'U') as fdesc: - data = pickle.loads(fdesc.read()) - except (pickle.PickleError, TypeError, UnicodeDecodeError): - # New format (Spyder >=2.2 for Python 2 and Python 3) - with open(pickle_filename, 'rb') as fdesc: - data = pickle.loads(fdesc.read()) - saved_arrays = {} - if load_array is not None: - # Loading numpy arrays saved with np.save - try: - saved_arrays = data.pop('__saved_arrays__') - for (name, index), fname in list(saved_arrays.items()): - arr = np.load( osp.join(osp.dirname(filename), fname) ) - if index is None: - data[name] = arr - elif isinstance(data[name], dict): - data[name][index] = arr - else: - data[name].insert(index, arr) - except KeyError: - pass - for fname in [pickle_filename]+[fn for fn in list(saved_arrays.values())]: - os.remove(fname) - except (EOFError, ValueError) as error: - error_message = to_text_string(error) - os.chdir(old_cwd) - return data, error_message - - -from spyderlib.baseconfig import get_conf_path, STDERR - -SAVED_CONFIG_FILES = ('inspector', 'onlinehelp', 'path', 'pylint.results', - 'spyder.ini', 'temp.py', 'temp.spydata', 'template.py', - 'history.py', 'history_internal.py', 'workingdir', - '.projects', '.spyderproject', '.ropeproject', - 'monitor.log', 'monitor_debug.log', 'rope.log') - -def reset_session(): - """Remove all config files""" - print("*** Reset Spyder settings to defaults ***", file=STDERR) - for fname in SAVED_CONFIG_FILES: - cfg_fname = get_conf_path(fname) - if osp.isfile(cfg_fname): - os.remove(cfg_fname) - elif osp.isdir(cfg_fname): - shutil.rmtree(cfg_fname) - else: - continue - print("removing:", cfg_fname, file=STDERR) - - -def save_session(filename): - """Save Spyder session""" - local_fname = get_conf_path(osp.basename(filename)) - filename = osp.abspath(filename) - old_cwd = getcwd() - os.chdir(get_conf_path()) - error_message = None - try: - tar = tarfile.open(local_fname, "w") - for fname in SAVED_CONFIG_FILES: - if osp.isfile(fname): - tar.add(fname) - tar.close() - shutil.move(local_fname, filename) - except Exception as error: - error_message = to_text_string(error) - os.chdir(old_cwd) - return error_message - - -def load_session(filename): - """Load Spyder session""" - filename = osp.abspath(filename) - old_cwd = getcwd() - os.chdir(osp.dirname(filename)) - error_message = None - renamed = False - try: - tar = tarfile.open(filename, "r") - extracted_files = tar.getnames() - - # Rename original config files - for fname in extracted_files: - orig_name = get_conf_path(fname) - bak_name = get_conf_path(fname+'.bak') - if osp.isfile(bak_name): - os.remove(bak_name) - if osp.isfile(orig_name): - os.rename(orig_name, bak_name) - renamed = True - - tar.extractall() - - for fname in extracted_files: - shutil.move(fname, get_conf_path(fname)) - - except Exception as error: - error_message = to_text_string(error) - if renamed: - # Restore original config files - for fname in extracted_files: - orig_name = get_conf_path(fname) - bak_name = get_conf_path(fname+'.bak') - if osp.isfile(orig_name): - os.remove(orig_name) - if osp.isfile(bak_name): - os.rename(bak_name, orig_name) - - finally: - # Removing backup config files - for fname in extracted_files: - bak_name = get_conf_path(fname+'.bak') - if osp.isfile(bak_name): - os.remove(bak_name) - - os.chdir(old_cwd) - return error_message - - -from spyderlib.baseconfig import _ - -class IOFunctions(object): - def __init__(self): - self.load_extensions = None - self.save_extensions = None - self.load_filters = None - self.save_filters = None - self.load_funcs = None - self.save_funcs = None - - def setup(self): - iofuncs = self.get_internal_funcs()+self.get_3rd_party_funcs() - load_extensions = {} - save_extensions = {} - load_funcs = {} - save_funcs = {} - load_filters = [] - save_filters = [] - load_ext = [] - for ext, name, loadfunc, savefunc in iofuncs: - filter_str = to_text_string(name + " (*%s)" % ext) - if loadfunc is not None: - load_filters.append(filter_str) - load_extensions[filter_str] = ext - load_funcs[ext] = loadfunc - load_ext.append(ext) - if savefunc is not None: - save_extensions[filter_str] = ext - save_filters.append(filter_str) - save_funcs[ext] = savefunc - load_filters.insert(0, to_text_string(_("Supported files")+" (*"+\ - " *".join(load_ext)+")")) - load_filters.append(to_text_string(_("All files (*.*)"))) - self.load_filters = "\n".join(load_filters) - self.save_filters = "\n".join(save_filters) - self.load_funcs = load_funcs - self.save_funcs = save_funcs - self.load_extensions = load_extensions - self.save_extensions = save_extensions - - def get_internal_funcs(self): - return [ - ('.spydata', _("Spyder data files"), - load_dictionary, save_dictionary), - ('.npy', _("NumPy arrays"), load_array, None), - ('.npz', _("NumPy zip arrays"), load_array, None), - ('.mat', _("Matlab files"), load_matlab, save_matlab), - ('.csv', _("CSV text files"), 'import_wizard', None), - ('.txt', _("Text files"), 'import_wizard', None), - ('.jpg', _("JPEG images"), load_image, None), - ('.png', _("PNG images"), load_image, None), - ('.gif', _("GIF images"), load_image, None), - ('.tif', _("TIFF images"), load_image, None), - ('.pkl', _("Pickle files"), load_pickle, None), - ('.pickle', _("Pickle files"), load_pickle, None), - ('.json', _("JSON files"), load_json, None), - ] - - def get_3rd_party_funcs(self): - other_funcs = [] - from spyderlib.otherplugins import get_spyderplugins_mods - for mod in get_spyderplugins_mods(prefix='io_', extension='.py'): - try: - other_funcs.append((mod.FORMAT_EXT, mod.FORMAT_NAME, - mod.FORMAT_LOAD, mod.FORMAT_SAVE)) - except AttributeError as error: - print("%s: %s" % (mod, str(error)), file=STDERR) - return other_funcs - - def save(self, data, filename): - ext = osp.splitext(filename)[1].lower() - if ext in self.save_funcs: - return self.save_funcs[ext](data, filename) - else: - return _("Unsupported file type '%s'") % ext - - def load(self, filename): - ext = osp.splitext(filename)[1].lower() - if ext in self.load_funcs: - return self.load_funcs[ext](filename) - else: - return None, _("Unsupported file type '%s'") % ext - -iofunctions = IOFunctions() -iofunctions.setup() - - -def save_auto(data, filename): - """Save data into filename, depending on file extension""" - pass - - -if __name__ == "__main__": - from spyderlib.py3compat import u - import datetime - testdict = {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]} - testdate = datetime.date(1945, 5, 8) - example = {'str': 'kjkj kj k j j kj k jkj', - 'unicode': u('éù'), - 'list': [1, 3, [4, 5, 6], 'kjkj', None], - 'tuple': ([1, testdate, testdict], 'kjkj', None), - 'dict': testdict, - 'float': 1.2233, - 'array': np.random.rand(4000, 400), - 'empty_array': np.array([]), - 'date': testdate, - 'datetime': datetime.datetime(1945, 5, 8), - } - import time - t0 = time.time() - save_dictionary(example, "test.spydata") - print(" Data saved in %.3f seconds" % (time.time()-t0)) - t0 = time.time() - example2, ok = load_dictionary("test.spydata") - print("Data loaded in %.3f seconds" % (time.time()-t0)) -# for key in example: -# print key, ":", example[key] == example2[key] - - a = MatlabStruct() - a.b = 'spam' - assert a["b"] == 'spam' - a.c["d"] = 'eggs' - assert a.c.d == 'eggs' - assert a == {'c': {'d': 'eggs'}, 'b': 'spam'} diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/ipython/templates/blank.html spyder-3.0.2+dfsg1/spyderlib/utils/ipython/templates/blank.html --- spyder-2.3.8+dfsg1/spyderlib/utils/ipython/templates/blank.html 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/ipython/templates/blank.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - - - - - - - - - - - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/ipython/templates/kernel_error.html spyder-3.0.2+dfsg1/spyderlib/utils/ipython/templates/kernel_error.html --- spyder-2.3.8+dfsg1/spyderlib/utils/ipython/templates/kernel_error.html 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/ipython/templates/kernel_error.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ - - - - - - - - - - - - -
    -
    -
    ${message}
    -
    -
    - ${error} -
    -
    - - - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/ipython/templates/loading.html spyder-3.0.2+dfsg1/spyderlib/utils/ipython/templates/loading.html --- spyder-2.3.8+dfsg1/spyderlib/utils/ipython/templates/loading.html 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/ipython/templates/loading.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - -
    -
    -
    ${message}
    -
    - - - diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/misc.py spyder-3.0.2+dfsg1/spyderlib/utils/misc.py --- spyder-2.3.8+dfsg1/spyderlib/utils/misc.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/misc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,236 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Miscellaneous utilities""" - -import os -import os.path as osp -import sys -import stat - - -def __remove_pyc_pyo(fname): - """Eventually remove .pyc and .pyo files associated to a Python script""" - if osp.splitext(fname)[1] == '.py': - for ending in ('c', 'o'): - if osp.exists(fname+ending): - os.remove(fname+ending) - -def rename_file(source, dest): - """ - Rename file from *source* to *dest* - If file is a Python script, also rename .pyc and .pyo files if any - """ - os.rename(source, dest) - __remove_pyc_pyo(source) - -def remove_file(fname): - """ - Remove file *fname* - If file is a Python script, also rename .pyc and .pyo files if any - """ - os.remove(fname) - __remove_pyc_pyo(fname) - -def move_file(source, dest): - """ - Move file from *source* to *dest* - If file is a Python script, also rename .pyc and .pyo files if any - """ - import shutil - shutil.copy(source, dest) - remove_file(source) - - -def onerror(function, path, excinfo): - """Error handler for `shutil.rmtree`. - - If the error is due to an access error (read-only file), it - attempts to add write permission and then retries. - If the error is for another reason, it re-raises the error. - - Usage: `shutil.rmtree(path, onerror=onerror)""" - if not os.access(path, os.W_OK): - # Is the error an access error? - os.chmod(path, stat.S_IWUSR) - function(path) - else: - raise - - -def select_port(default_port=20128): - """Find and return a non used port""" - import socket - while True: - try: - sock = socket.socket(socket.AF_INET, - socket.SOCK_STREAM, - socket.IPPROTO_TCP) -# sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind( ("127.0.0.1", default_port) ) - except socket.error as _msg: # analysis:ignore - default_port += 1 - else: - break - finally: - sock.close() - sock = None - return default_port - - -def count_lines(path, extensions=None, excluded_dirnames=None): - """Return number of source code lines for all filenames in subdirectories - of *path* with names ending with *extensions* - Directory names *excluded_dirnames* will be ignored""" - if extensions is None: - extensions = ['.py', '.pyw', '.ipy', '.c', '.h', '.cpp', '.hpp', - '.inc', '.', '.hh', '.hxx', '.cc', '.cxx', '.cl', - '.f', '.for', '.f77', '.f90', '.f95', '.f2k'] - if excluded_dirnames is None: - excluded_dirnames = ['build', 'dist', '.hg', '.svn'] - def get_filelines(path): - dfiles, dlines = 0, 0 - if osp.splitext(path)[1] in extensions: - dfiles = 1 - with open(path, 'rb') as textfile: - dlines = len(textfile.read().strip().splitlines()) - return dfiles, dlines - lines = 0 - files = 0 - if osp.isdir(path): - for dirpath, dirnames, filenames in os.walk(path): - for d in dirnames[:]: - if d in excluded_dirnames: - dirnames.remove(d) - if excluded_dirnames is None or \ - osp.dirname(dirpath) not in excluded_dirnames: - for fname in filenames: - dfiles, dlines = get_filelines(osp.join(dirpath, fname)) - files += dfiles - lines += dlines - else: - dfiles, dlines = get_filelines(path) - files += dfiles - lines += dlines - return files, lines - - -def fix_reference_name(name, blacklist=None): - """Return a syntax-valid Python reference name from an arbitrary name""" - import re - name = "".join(re.split(r'[^0-9a-zA-Z_]', name)) - while name and not re.match(r'([a-zA-Z]+[0-9a-zA-Z_]*)$', name): - if not re.match(r'[a-zA-Z]', name[0]): - name = name[1:] - continue - name = str(name) - if not name: - name = "data" - if blacklist is not None and name in blacklist: - get_new_name = lambda index: name+('%03d' % index) - index = 0 - while get_new_name(index) in blacklist: - index += 1 - name = get_new_name(index) - return name - - -def remove_backslashes(path): - """Remove backslashes in *path* - - For Windows platforms only. - Returns the path unchanged on other platforms. - - This is especially useful when formatting path strings on - Windows platforms for which folder paths may contain backslashes - and provoke unicode decoding errors in Python 3 (or in Python 2 - when future 'unicode_literals' symbol has been imported).""" - if os.name == 'nt': - # Removing trailing single backslash - if path.endswith('\\') and not path.endswith('\\\\'): - path = path[:-1] - # Replacing backslashes by slashes - path = path.replace('\\', '/') - return path - - -def get_error_match(text): - """Return error match""" - import re - return re.match(r' File "(.*)", line (\d*)', text) - - -def get_python_executable(): - """Return path to Python executable""" - executable = sys.executable.replace("pythonw.exe", "python.exe") - if executable.endswith("spyder.exe"): - # py2exe distribution - executable = "python.exe" - return executable - - -def monkeypatch_method(cls, patch_name): - # This function's code was inspired from the following thread: - # "[Python-Dev] Monkeypatching idioms -- elegant or ugly?" - # by Robert Brewer - # (Tue Jan 15 19:13:25 CET 2008) - """ - Add the decorated method to the given class; replace as needed. - - If the named method already exists on the given class, it will - be replaced, and a reference to the old method is created as - cls._old. If the "_old__" attribute - already exists, KeyError is raised. - """ - def decorator(func): - fname = func.__name__ - old_func = getattr(cls, fname, None) - if old_func is not None: - # Add the old func to a list of old funcs. - old_ref = "_old_%s_%s" % (patch_name, fname) - #print old_ref, old_func - old_attr = getattr(cls, old_ref, None) - if old_attr is None: - setattr(cls, old_ref, old_func) - else: - raise KeyError("%s.%s already exists." - % (cls.__name__, old_ref)) - setattr(cls, fname, func) - return func - return decorator - - -def is_python_script(fname): - """Is it a valid Python script?""" - return osp.isfile(fname) and fname.endswith(('.py', '.pyw', '.ipy')) - - -def abspardir(path): - """Return absolute parent dir""" - return osp.abspath(osp.join(path, os.pardir)) - - -def get_common_path(pathlist): - """Return common path for all paths in pathlist""" - common = osp.normpath(osp.commonprefix(pathlist)) - if len(common) > 1: - if not osp.isdir(common): - return abspardir(common) - else: - for path in pathlist: - if not osp.isdir(osp.join(common, path[len(common)+1:])): - # `common` is not the real common prefix - return abspardir(common) - else: - return osp.abspath(common) - -if __name__ == '__main__': - assert get_common_path([ - 'D:\\Python\\spyder-v21\\spyderlib\\widgets', - 'D:\\Python\\spyder\\spyderlib\\utils', - 'D:\\Python\\spyder\\spyderlib\\widgets', - 'D:\\Python\\spyder-v21\\spyderlib\\utils', - ]) == 'D:\\Python' diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/programs.py spyder-3.0.2+dfsg1/spyderlib/utils/programs.py --- spyder-2.3.8+dfsg1/spyderlib/utils/programs.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/programs.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,326 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Running programs utilities""" - -from __future__ import print_function - -from distutils.version import LooseVersion -import imp -import inspect -import os -import os.path as osp -import re -import subprocess -import sys -import tempfile - -# Local imports -from spyderlib.utils import encoding -from spyderlib.py3compat import PY2, is_text_string - - -if os.name == 'nt': - TEMPDIR = tempfile.gettempdir() + osp.sep + 'spyder' -else: - username = encoding.to_unicode_from_fs(os.environ.get('USER')) - TEMPDIR = tempfile.gettempdir() + osp.sep + 'spyder-' + username - - -def is_program_installed(basename): - """Return program absolute path if installed in PATH - Otherwise, return None""" - for path in os.environ["PATH"].split(os.pathsep): - abspath = osp.join(path, basename) - if osp.isfile(abspath): - return abspath - - -def find_program(basename): - """Find program in PATH and return absolute path - Try adding .exe or .bat to basename on Windows platforms - (return None if not found)""" - names = [basename] - if os.name == 'nt': - # Windows platforms - extensions = ('.exe', '.bat', '.cmd') - if not basename.endswith(extensions): - names = [basename+ext for ext in extensions]+[basename] - for name in names: - path = is_program_installed(name) - if path: - return path - - -def run_program(name, args=[], cwd=None): - """Run program in a separate process""" - assert isinstance(args, (tuple, list)) - path = find_program(name) - if not path: - raise RuntimeError("Program %s was not found" % name) - subprocess.Popen([path]+args, cwd=cwd) - - -def start_file(filename): - """Generalized os.startfile for all platforms supported by Qt - (this function is simply wrapping QDesktopServices.openUrl) - Returns True if successfull, otherwise returns False.""" - from spyderlib.qt.QtGui import QDesktopServices - from spyderlib.qt.QtCore import QUrl - - # We need to use setUrl instead of setPath because this is the only - # cross-platform way to open external files. setPath fails completely on - # Mac and doesn't open non-ascii files on Linux. - # Fixes Issue 740 - url = QUrl() - url.setUrl(filename) - return QDesktopServices.openUrl(url) - - -def python_script_exists(package=None, module=None): - """Return absolute path if Python script exists (otherwise, return None) - package=None -> module is in sys.path (standard library modules)""" - assert module is not None - try: - if package is None: - path = imp.find_module(module)[1] - else: - path = osp.join(imp.find_module(package)[1], module)+'.py' - except ImportError: - return - if not osp.isfile(path): - path += 'w' - if osp.isfile(path): - return path - - -def run_python_script(package=None, module=None, args=[], p_args=[]): - """Run Python script in a separate process - package=None -> module is in sys.path (standard library modules)""" - assert module is not None - assert isinstance(args, (tuple, list)) and isinstance(p_args, (tuple, list)) - path = python_script_exists(package, module) - subprocess.Popen([sys.executable]+p_args+[path]+args) - - -def shell_split(text): - """Split the string `text` using shell-like syntax - - This avoids breaking single/double-quoted strings (e.g. containing - strings with spaces). This function is almost equivalent to the shlex.split - function (see standard library `shlex`) except that it is supporting - unicode strings (shlex does not support unicode until Python 2.7.3).""" - assert is_text_string(text) # in case a QString is passed... - pattern = r'(\s+|(?': - return LooseVersion(actver) > LooseVersion(version) - elif cmp_op == '>=': - return LooseVersion(actver) >= LooseVersion(version) - elif cmp_op == '=': - return LooseVersion(actver) == LooseVersion(version) - elif cmp_op == '<': - return LooseVersion(actver) < LooseVersion(version) - elif cmp_op == '<=': - return LooseVersion(actver) <= LooseVersion(version) - else: - return False - except TypeError: - return True - - -def get_module_version(module_name): - """Return module version or None if version can't be retrieved.""" - mod = __import__(module_name) - return getattr(mod, '__version__', getattr(mod, 'VERSION', None)) - - -def is_module_installed(module_name, version=None, installed_version=None, - interpreter=None): - """Return True if module *module_name* is installed - - If version is not None, checking module version - (module must have an attribute named '__version__') - - version may starts with =, >=, > or < to specify the exact requirement ; - multiple conditions may be separated by ';' (e.g. '>=0.13;<1.0') - - interpreter: check if a module is installed with a given version - in a determined interpreter""" - if interpreter: - if not osp.isdir(TEMPDIR): - os.mkdir(TEMPDIR) - - if osp.isfile(interpreter) and ('python' in interpreter): - checkver = inspect.getsource(check_version) - get_modver = inspect.getsource(get_module_version) - ismod_inst = inspect.getsource(is_module_installed) - fd, script = tempfile.mkstemp(suffix='.py', dir=TEMPDIR) - with os.fdopen(fd, 'w') as f: - f.write("# -*- coding: utf-8 -*-" + "\n\n") - f.write("from distutils.version import LooseVersion" + "\n") - f.write("import re" + "\n\n") - f.write(checkver + "\n") - f.write(get_modver + "\n") - f.write(ismod_inst + "\n") - if version: - f.write("print(is_module_installed('%s','%s'))"\ - % (module_name, version)) - else: - f.write("print(is_module_installed('%s'))" % module_name) - try: - output, _err = subprocess.Popen([interpreter, script], - stdout=subprocess.PIPE).communicate() - except subprocess.CalledProcessError: - return True - if output: # TODO: Check why output could be empty! - return eval(output.decode()) - else: - return False - else: - # Try to not take a wrong decision if there is no interpreter - # available (needed for the change_pystartup method of ExtConsole - # config page) - return True - else: - if installed_version is None: - try: - actver = get_module_version(module_name) - except ImportError: - # Module is not installed - return False - else: - actver = installed_version - if actver is None and version is not None: - return False - elif version is None: - return True - else: - if ';' in version: - output = True - for ver in version.split(';'): - output = output and is_module_installed(module_name, ver) - return output - match = re.search('[0-9]', version) - assert match is not None, "Invalid version number" - symb = version[:match.start()] - if not symb: - symb = '=' - assert symb in ('>=', '>', '=', '<', '<='),\ - "Invalid version condition '%s'" % symb - version = version[match.start():] - - return check_version(actver, version, symb) - - -if __name__ == '__main__': - print(find_program('hg')) - print(shell_split('-q -o -a')) - print(shell_split('-q "d:\\Python de xxxx\\t.txt" -o -a')) - print(is_module_installed('IPython', '>=0.12')) - print(is_module_installed('IPython', '>=0.13;<1.0')) - print(is_module_installed('jedi', '>=0.7.0')) diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/qthelpers.py spyder-3.0.2+dfsg1/spyderlib/utils/qthelpers.py --- spyder-2.3.8+dfsg1/spyderlib/utils/qthelpers.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/qthelpers.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,455 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Qt utilities""" - -from spyderlib.qt.QtGui import (QAction, QStyle, QWidget, QIcon, QApplication, - QLabel, QVBoxLayout, QHBoxLayout, QLineEdit, - QKeyEvent, QMenu, QKeySequence, QToolButton, - QPixmap) -from spyderlib.qt.QtCore import (SIGNAL, QObject, Qt, QLocale, QTranslator, - QLibraryInfo, QEvent) -from spyderlib.qt.compat import to_qvariant, from_qvariant - -import os -import re -import os.path as osp -import sys - -# Local import -from spyderlib.baseconfig import get_image_path, running_in_mac_app -from spyderlib.guiconfig import get_shortcut -from spyderlib.utils import programs -from spyderlib.py3compat import is_text_string, to_text_string - -# Note: How to redirect a signal from widget *a* to widget *b* ? -# ---- -# It has to be done manually: -# * typing 'SIGNAL("clicked()")' works -# * typing 'signalstr = "clicked()"; SIGNAL(signalstr)' won't work -# Here is an example of how to do it: -# (self.listwidget is widget *a* and self is widget *b*) -# self.connect(self.listwidget, SIGNAL('option_changed'), -# lambda *args: self.emit(SIGNAL('option_changed'), *args)) - - -def get_icon(name, default=None, resample=False): - """Return image inside a QIcon object - default: default image name or icon - resample: if True, manually resample icon pixmaps for usual sizes - (16, 24, 32, 48, 96, 128, 256). This is recommended for QMainWindow icons - created from SVG images on non-Windows platforms due to a Qt bug (see - Issue 1314).""" - if default is None: - icon = QIcon(get_image_path(name)) - elif isinstance(default, QIcon): - icon_path = get_image_path(name, default=None) - icon = default if icon_path is None else QIcon(icon_path) - else: - icon = QIcon(get_image_path(name, default)) - if resample: - icon0 = QIcon() - for size in (16, 24, 32, 48, 96, 128, 256, 512): - icon0.addPixmap(icon.pixmap(size, size)) - return icon0 - else: - return icon - - -def get_image_label(name, default="not_found.png"): - """Return image inside a QLabel object""" - label = QLabel() - label.setPixmap(QPixmap(get_image_path(name, default))) - return label - - -class MacApplication(QApplication): - """Subclass to be able to open external files with our Mac app""" - def __init__(self, *args): - QApplication.__init__(self, *args) - - def event(self, event): - if event.type() == QEvent.FileOpen: - fname = str(event.file()) - self.emit(SIGNAL('open_external_file(QString)'), fname) - return QApplication.event(self, event) - - -def qapplication(translate=True): - """Return QApplication instance - Creates it if it doesn't already exist""" - if running_in_mac_app(): - SpyderApplication = MacApplication - else: - SpyderApplication = QApplication - - app = SpyderApplication.instance() - if app is None: - # Set Application name for Gnome 3 - # https://groups.google.com/forum/#!topic/pyside/24qxvwfrRDs - app = SpyderApplication(['Spyder']) - - # Set application name for KDE (See issue 2207) - app.setApplicationName('Spyder') - if translate: - install_translator(app) - return app - - -def file_uri(fname): - """Select the right file uri scheme according to the operating system""" - if os.name == 'nt': - # Local file - if re.search(r'^[a-zA-Z]:', fname): - return 'file:///' + fname - # UNC based path - else: - return 'file://' + fname - else: - return 'file://' + fname - - -QT_TRANSLATOR = None -def install_translator(qapp): - """Install Qt translator to the QApplication instance""" - global QT_TRANSLATOR - if QT_TRANSLATOR is None: - qt_translator = QTranslator() - if qt_translator.load("qt_"+QLocale.system().name(), - QLibraryInfo.location(QLibraryInfo.TranslationsPath)): - QT_TRANSLATOR = qt_translator # Keep reference alive - if QT_TRANSLATOR is not None: - qapp.installTranslator(QT_TRANSLATOR) - - -def keybinding(attr): - """Return keybinding""" - ks = getattr(QKeySequence, attr) - return from_qvariant(QKeySequence.keyBindings(ks)[0], str) - - -def _process_mime_path(path, extlist): - if path.startswith(r"file://"): - if os.name == 'nt': - # On Windows platforms, a local path reads: file:///c:/... - # and a UNC based path reads like: file://server/share - if path.startswith(r"file:///"): # this is a local path - path=path[8:] - else: # this is a unc path - path = path[5:] - else: - path = path[7:] - if osp.exists(path): - if extlist is None or osp.splitext(path)[1] in extlist: - return path - - -def mimedata2url(source, extlist=None): - """ - Extract url list from MIME data - extlist: for example ('.py', '.pyw') - """ - pathlist = [] - if source.hasUrls(): - for url in source.urls(): - path = _process_mime_path(to_text_string(url.toString()), extlist) - if path is not None: - pathlist.append(path) - elif source.hasText(): - for rawpath in to_text_string(source.text()).splitlines(): - path = _process_mime_path(rawpath, extlist) - if path is not None: - pathlist.append(path) - if pathlist: - return pathlist - - -def keyevent2tuple(event): - """Convert QKeyEvent instance into a tuple""" - return (event.type(), event.key(), event.modifiers(), event.text(), - event.isAutoRepeat(), event.count()) - - -def tuple2keyevent(past_event): - """Convert tuple into a QKeyEvent instance""" - return QKeyEvent(*past_event) - - -def restore_keyevent(event): - if isinstance(event, tuple): - _, key, modifiers, text, _, _ = event - event = tuple2keyevent(event) - else: - text = event.text() - modifiers = event.modifiers() - key = event.key() - ctrl = modifiers & Qt.ControlModifier - shift = modifiers & Qt.ShiftModifier - return event, text, key, ctrl, shift - - -def create_toolbutton(parent, text=None, shortcut=None, icon=None, tip=None, - toggled=None, triggered=None, - autoraise=True, text_beside_icon=False): - """Create a QToolButton""" - button = QToolButton(parent) - if text is not None: - button.setText(text) - if icon is not None: - if is_text_string(icon): - icon = get_icon(icon) - button.setIcon(icon) - if text is not None or tip is not None: - button.setToolTip(text if tip is None else tip) - if text_beside_icon: - button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - button.setAutoRaise(autoraise) - if triggered is not None: - QObject.connect(button, SIGNAL('clicked()'), triggered) - if toggled is not None: - QObject.connect(button, SIGNAL("toggled(bool)"), toggled) - button.setCheckable(True) - if shortcut is not None: - button.setShortcut(shortcut) - return button - - -def action2button(action, autoraise=True, text_beside_icon=False, parent=None): - """Create a QToolButton directly from a QAction object""" - if parent is None: - parent = action.parent() - button = QToolButton(parent) - button.setDefaultAction(action) - button.setAutoRaise(autoraise) - if text_beside_icon: - button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - return button - - -def toggle_actions(actions, enable): - """Enable/disable actions""" - if actions is not None: - for action in actions: - if action is not None: - action.setEnabled(enable) - - -def create_action(parent, text, shortcut=None, icon=None, tip=None, - toggled=None, triggered=None, data=None, menurole=None, - context=Qt.WindowShortcut): - """Create a QAction""" - action = QAction(text, parent) - if triggered is not None: - parent.connect(action, SIGNAL("triggered()"), triggered) - if toggled is not None: - parent.connect(action, SIGNAL("toggled(bool)"), toggled) - action.setCheckable(True) - if icon is not None: - if is_text_string(icon): - icon = get_icon(icon) - action.setIcon(icon) - if shortcut is not None: - action.setShortcut(shortcut) - if tip is not None: - action.setToolTip(tip) - action.setStatusTip(tip) - if data is not None: - action.setData(to_qvariant(data)) - if menurole is not None: - action.setMenuRole(menurole) - #TODO: Hard-code all shortcuts and choose context=Qt.WidgetShortcut - # (this will avoid calling shortcuts from another dockwidget - # since the context thing doesn't work quite well with these widgets) - action.setShortcutContext(context) - return action - - -def add_shortcut_to_tooltip(action, context, name): - """Add the shortcut associated with a given action to its tooltip""" - action.setToolTip(action.toolTip() + ' (%s)' % - get_shortcut(context=context, name=name)) - - -def add_actions(target, actions, insert_before=None): - """Add actions to a menu""" - previous_action = None - target_actions = list(target.actions()) - if target_actions: - previous_action = target_actions[-1] - if previous_action.isSeparator(): - previous_action = None - for action in actions: - if (action is None) and (previous_action is not None): - if insert_before is None: - target.addSeparator() - else: - target.insertSeparator(insert_before) - elif isinstance(action, QMenu): - if insert_before is None: - target.addMenu(action) - else: - target.insertMenu(insert_before, action) - elif isinstance(action, QAction): - if insert_before is None: - target.addAction(action) - else: - target.insertAction(insert_before, action) - previous_action = action - - -def get_item_user_text(item): - """Get QTreeWidgetItem user role string""" - return from_qvariant(item.data(0, Qt.UserRole), to_text_string) - - -def set_item_user_text(item, text): - """Set QTreeWidgetItem user role string""" - item.setData(0, Qt.UserRole, to_qvariant(text)) - - -def create_bookmark_action(parent, url, title, icon=None, shortcut=None): - """Create bookmark action""" - return create_action( parent, title, shortcut=shortcut, icon=icon, - triggered=lambda u=url: programs.start_file(u) ) - - -def create_module_bookmark_actions(parent, bookmarks): - """ - Create bookmark actions depending on module installation: - bookmarks = ((module_name, url, title), ...) - """ - actions = [] - for key, url, title in bookmarks: - # Create actions for scientific distros only if Spyder is installed - # under them - create_act = True - if key == 'xy' or key == 'winpython': - if not programs.is_module_installed(key): - create_act = False - if create_act: - act = create_bookmark_action(parent, url, title) - actions.append(act) - return actions - - -def create_program_action(parent, text, name, icon=None, nt_name=None): - """Create action to run a program""" - if is_text_string(icon): - icon = get_icon(icon) - if os.name == 'nt' and nt_name is not None: - name = nt_name - path = programs.find_program(name) - if path is not None: - return create_action(parent, text, icon=icon, - triggered=lambda: programs.run_program(name)) - - -def create_python_script_action(parent, text, icon, package, module, args=[]): - """Create action to run a GUI based Python script""" - if is_text_string(icon): - icon = get_icon(icon) - if programs.python_script_exists(package, module): - return create_action(parent, text, icon=icon, - triggered=lambda: - programs.run_python_script(package, module, args)) - - -class DialogManager(QObject): - """ - Object that keep references to non-modal dialog boxes for another QObject, - typically a QMainWindow or any kind of QWidget - """ - def __init__(self): - QObject.__init__(self) - self.dialogs = {} - - def show(self, dialog): - """Generic method to show a non-modal dialog and keep reference - to the Qt C++ object""" - for dlg in list(self.dialogs.values()): - if to_text_string(dlg.windowTitle()) \ - == to_text_string(dialog.windowTitle()): - dlg.show() - dlg.raise_() - break - else: - dialog.show() - self.dialogs[id(dialog)] = dialog - self.connect(dialog, SIGNAL('accepted()'), - lambda eid=id(dialog): self.dialog_finished(eid)) - self.connect(dialog, SIGNAL('rejected()'), - lambda eid=id(dialog): self.dialog_finished(eid)) - - def dialog_finished(self, dialog_id): - """Manage non-modal dialog boxes""" - return self.dialogs.pop(dialog_id) - - def close_all(self): - """Close all opened dialog boxes""" - for dlg in list(self.dialogs.values()): - dlg.reject() - - -def get_std_icon(name, size=None): - """Get standard platform icon - Call 'show_std_icons()' for details""" - if not name.startswith('SP_'): - name = 'SP_'+name - icon = QWidget().style().standardIcon( getattr(QStyle, name) ) - if size is None: - return icon - else: - return QIcon( icon.pixmap(size, size) ) - - -def get_filetype_icon(fname): - """Return file type icon""" - ext = osp.splitext(fname)[1] - if ext.startswith('.'): - ext = ext[1:] - return get_icon( "%s.png" % ext, get_std_icon('FileIcon') ) - - -class ShowStdIcons(QWidget): - """ - Dialog showing standard icons - """ - def __init__(self, parent): - QWidget.__init__(self, parent) - layout = QHBoxLayout() - row_nb = 14 - cindex = 0 - for child in dir(QStyle): - if child.startswith('SP_'): - if cindex == 0: - col_layout = QVBoxLayout() - icon_layout = QHBoxLayout() - icon = get_std_icon(child) - label = QLabel() - label.setPixmap(icon.pixmap(32, 32)) - icon_layout.addWidget( label ) - icon_layout.addWidget( QLineEdit(child.replace('SP_', '')) ) - col_layout.addLayout(icon_layout) - cindex = (cindex+1) % row_nb - if cindex == 0: - layout.addLayout(col_layout) - self.setLayout(layout) - self.setWindowTitle('Standard Platform Icons') - self.setWindowIcon(get_std_icon('TitleBarMenuButton')) - - -def show_std_icons(): - """ - Show all standard Icons - """ - app = qapplication() - dialog = ShowStdIcons(None) - dialog.show() - sys.exit(app.exec_()) - - -if __name__ == "__main__": - show_std_icons() diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/sourcecode.py spyder-3.0.2+dfsg1/spyderlib/utils/sourcecode.py --- spyder-2.3.8+dfsg1/spyderlib/utils/sourcecode.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/sourcecode.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Source code text utilities -""" -import re - -# Order is important: -EOL_CHARS = (("\r\n", 'nt'), ("\n", 'posix'), ("\r", 'mac')) - -ALL_LANGUAGES = { - 'Python': ('py', 'pyw', 'python', 'ipy'), - 'Cython': ('pyx', 'pxi', 'pxd'), - 'Enaml': ('enaml',), - 'Fortran77': ('f', 'for', 'f77'), - 'Fortran': ('f90', 'f95', 'f2k'), - 'Idl': ('pro',), - 'Matlab': ('m',), - 'Julia': ('jl',), - 'Diff': ('diff', 'patch', 'rej'), - 'GetText': ('po', 'pot'), - 'Nsis': ('nsi', 'nsh'), - 'Html': ('htm', 'html'), - 'Css': ('css',), - 'Xml': ('xml',), - 'Js': ('js',), - 'Json': ('json', 'ipynb'), - 'Cpp': ('c', 'cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'hxx'), - 'OpenCL': ('cl',), - 'Batch': ('bat', 'cmd', 'nt'), - 'Ini': ('properties', 'session', 'ini', 'inf', 'reg', 'url', - 'cfg', 'cnf', 'aut', 'iss'), - 'Yaml':('yaml','yml'), - } - -PYTHON_LIKE_LANGUAGES = ('Python', 'Cython', 'Enaml') - -CELL_LANGUAGES = {'Python': ('#%%', '# %%', '# ', '# In[')} - -def get_eol_chars(text): - """Get text EOL characters""" - for eol_chars, _os_name in EOL_CHARS: - if text.find(eol_chars) > -1: - return eol_chars - -def get_os_name_from_eol_chars(eol_chars): - """Return OS name from EOL characters""" - for chars, os_name in EOL_CHARS: - if eol_chars == chars: - return os_name - -def get_eol_chars_from_os_name(os_name): - """Return EOL characters from OS name""" - for eol_chars, name in EOL_CHARS: - if name == os_name: - return eol_chars - -def has_mixed_eol_chars(text): - """Detect if text has mixed EOL characters""" - eol_chars = get_eol_chars(text) - if eol_chars is None: - return False - correct_text = eol_chars.join((text+eol_chars).splitlines()) - return repr(correct_text) != repr(text) - -def fix_indentation(text): - """Replace tabs by spaces""" - return text.replace('\t', ' '*4) - - -def is_builtin(text): - """Test if passed string is the name of a Python builtin object""" - from spyderlib.py3compat import builtins - return text in [str(name) for name in dir(builtins) - if not name.startswith('_')] - - -def is_keyword(text): - """Test if passed string is the name of a Python keyword""" - import keyword - return text in keyword.kwlist - - -def get_primary_at(source_code, offset, retry=True): - """Return Python object in *source_code* at *offset* - Periods to the left of the cursor are carried forward - e.g. 'functools.par^tial' would yield 'functools.partial' - Retry prevents infinite recursion: retry only once - """ - obj = '' - left = re.split(r"[^0-9a-zA-Z_.]", source_code[:offset]) - if left and left[-1]: - obj = left[-1] - right = re.split(r"\W", source_code[offset:]) - if right and right[0]: - obj += right[0] - if obj and obj[0].isdigit(): - obj = '' - # account for opening chars with no text to the right - if not obj and retry and offset and source_code[offset - 1] in '([.': - return get_primary_at(source_code, offset - 1, retry=False) - return obj - - -def split_source(source_code): - '''Split source code into lines - ''' - eol_chars = get_eol_chars(source_code) - if eol_chars: - return source_code.split(eol_chars) - else: - return [source_code] - - -def get_identifiers(source_code): - '''Split source code into python identifier-like tokens''' - tokens = set(re.split(r"[^0-9a-zA-Z_.]", source_code)) - valid = re.compile(r'[a-zA-Z_]') - return [token for token in tokens if re.match(valid, token)] - - -if __name__ == '__main__': - code = 'import functools\nfunctools.partial' - assert get_primary_at(code, len(code)) == 'functools.partial' - assert get_identifiers(code) == ['import', 'functools', - 'functools.partial'] - assert split_source(code) == ['import functools', 'functools.partial'] - code = code.replace('\n', '\r\n') - assert split_source(code) == ['import functools', 'functools.partial'] diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/system.py spyder-3.0.2+dfsg1/spyderlib/utils/system.py --- spyder-2.3.8+dfsg1/spyderlib/utils/system.py 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/system.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Operating system utilities""" - - -import os - -# Local imports -from spyderlib.utils import programs - - -def windows_memory_usage(): - """Return physical memory usage (float) - Works on Windows platforms only""" - from ctypes import windll, Structure, c_uint64, sizeof, byref - from ctypes.wintypes import DWORD - class MemoryStatus(Structure): - _fields_ = [('dwLength', DWORD), - ('dwMemoryLoad',DWORD), - ('ullTotalPhys', c_uint64), - ('ullAvailPhys', c_uint64), - ('ullTotalPageFile', c_uint64), - ('ullAvailPageFile', c_uint64), - ('ullTotalVirtual', c_uint64), - ('ullAvailVirtual', c_uint64), - ('ullAvailExtendedVirtual', c_uint64),] - memorystatus = MemoryStatus() - # MSDN documetation states that dwLength must be set to MemoryStatus - # size before calling GlobalMemoryStatusEx - # http://msdn.microsoft.com/en-us/library/aa366770(v=vs.85) - memorystatus.dwLength = sizeof(memorystatus) - windll.kernel32.GlobalMemoryStatusEx(byref(memorystatus)) - return float(memorystatus.dwMemoryLoad) - -def psutil_phymem_usage(): - """ - Return physical memory usage (float) - Requires the cross-platform psutil (>=v0.3) library - (http://code.google.com/p/psutil/) - """ - import psutil - # This is needed to avoid a deprecation warning error with - # newer psutil versions - try: - percent = psutil.virtual_memory().percent - except: - percent = psutil.phymem_usage().percent - return percent - -if programs.is_module_installed('psutil', '>=0.3.0'): - # Function `psutil.phymem_usage` was introduced in psutil v0.3.0 - memory_usage = psutil_phymem_usage -elif os.name == 'nt': - # Backup plan for Windows platforms - memory_usage = windows_memory_usage -else: - raise ImportError("Feature requires psutil 0.3+ on non Windows platforms") - - -if __name__ == '__main__': - print("*"*80) - print(memory_usage.__doc__) - print(memory_usage()) - if os.name == 'nt': - # windll can only be imported if os.name = 'nt' or 'ce' - print("*"*80) - print(windows_memory_usage.__doc__) - print(windows_memory_usage()) \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/vcs.py spyder-3.0.2+dfsg1/spyderlib/utils/vcs.py --- spyder-2.3.8+dfsg1/spyderlib/utils/vcs.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/vcs.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,139 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Utilities for version control systems""" - -from __future__ import print_function - -import os.path as osp -import subprocess - -# Local imports -from spyderlib.utils import programs -from spyderlib.utils.misc import abspardir -from spyderlib.py3compat import PY3 - - -SUPPORTED = [ -{ - 'name': 'Mercurial', - 'rootdir': '.hg', - 'actions': dict( - commit=( ('thg', ['commit']), - ('hgtk', ['commit']) ), - browse=( ('thg', ['log']), - ('hgtk', ['log']) )) -}, { - 'name': 'Git', - 'rootdir': '.git', - 'actions': dict( - commit=( ('git', ['gui']), ), - browse=( ('gitk', []), )) -}] - - -class ActionToolNotFound(RuntimeError): - """Exception to transmit information about supported tools for - failed attempt to execute given action""" - - def __init__(self, vcsname, action, tools): - RuntimeError.__init__(self) - self.vcsname = vcsname - self.action = action - self.tools = tools - - -def get_vcs_info(path): - """Return support status dict if path is under VCS root""" - for info in SUPPORTED: - vcs_path = osp.join(path, info['rootdir']) - if osp.isdir(vcs_path): - return info - - -def get_vcs_root(path): - """Return VCS root directory path - Return None if path is not within a supported VCS repository""" - previous_path = path - while get_vcs_info(path) is None: - path = abspardir(path) - if path == previous_path: - return - else: - previous_path = path - return osp.abspath(path) - - -def is_vcs_repository(path): - """Return True if path is a supported VCS repository""" - return get_vcs_root(path) is not None - - -def run_vcs_tool(path, action): - """If path is a valid VCS repository, run the corresponding VCS tool - Supported VCS actions: 'commit', 'browse' - Return False if the VCS tool is not installed""" - info = get_vcs_info(get_vcs_root(path)) - tools = info['actions'][action] - for tool, args in tools: - if programs.find_program(tool): - programs.run_program(tool, args, cwd=path) - return - else: - cmdnames = [name for name, args in tools] - raise ActionToolNotFound(info['name'], action, cmdnames) - -def is_hg_installed(): - """Return True if Mercurial is installed""" - return programs.find_program('hg') is not None - - -def get_hg_revision(repopath): - """Return Mercurial revision for the repository located at repopath - Result is a tuple (global, local, branch), with None values on error - For example: - >>> get_hg_revision(".") - ('eba7273c69df+', '2015+', 'default') - """ - try: - hg = programs.find_program('hg') - assert hg is not None and osp.isdir(osp.join(repopath, '.hg')) - output, _err = subprocess.Popen([hg, 'id', '-nib', repopath], - stdout=subprocess.PIPE).communicate() - # output is now: ('eba7273c69df+ 2015+ default\n', None) - # Split 2 times max to allow spaces in branch names. - return tuple(output.decode().strip().split(None, 2)) - except (subprocess.CalledProcessError, AssertionError, AttributeError): - # print("Error: Failed to get revision number from Mercurial - %s" % exc) - return (None, None, None) - - -def get_git_revision(repopath): - """Return Git revision for the repository located at repopath - Result is the latest commit hash, with None on error - """ - try: - git = programs.find_program('git') - assert git is not None and osp.isdir(osp.join(repopath, '.git')) - commit = subprocess.Popen([git, 'rev-parse', '--short', 'HEAD'], - stdout=subprocess.PIPE, - cwd=repopath).communicate() - if PY3: - commit = str(commit[0][:-1]) - commit = commit[2:-1] - else: - commit = commit[0][:-1] - return commit - except (subprocess.CalledProcessError, AssertionError, AttributeError): - return None - - -if __name__ == '__main__': - print(get_vcs_root(osp.dirname(__file__))) - print(get_vcs_root(r'D:\Python\ipython\IPython\kernel')) - #run_vcs_tool(r'D:\Python\userconfig\userconfig', 'commit') - print(get_git_revision(osp.dirname(__file__)+"/../..")) - print(get_git_revision('/')) diff -Nru spyder-2.3.8+dfsg1/spyderlib/utils/windows.py spyder-3.0.2+dfsg1/spyderlib/utils/windows.py --- spyder-2.3.8+dfsg1/spyderlib/utils/windows.py 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/utils/windows.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Windows-specific utilities""" - - -from ctypes import windll - - -# --- Window control --- - -SW_SHOW = 5 # activate and display -SW_SHOWNA = 8 # show without activation -SW_HIDE = 0 - -GetConsoleWindow = windll.kernel32.GetConsoleWindow -ShowWindow = windll.user32.ShowWindow -IsWindowVisible = windll.user32.IsWindowVisible - -# Handle to console window associated with current Python -# interpreter procss, 0 if there is no window -console_window_handle = GetConsoleWindow() - -def set_attached_console_visible(state): - """Show/hide system console window attached to current process. - Return it's previous state. - - Availability: Windows""" - flag = {True: SW_SHOW, False: SW_HIDE} - return bool(ShowWindow(console_window_handle, flag[state])) - -def is_attached_console_visible(): - """Return True if attached console window is visible""" - return IsWindowVisible(console_window_handle) - -def set_windows_appusermodelid(): - """Make sure correct icon is used on Windows 7 taskbar""" - try: - return windll.shell32.SetCurrentProcessExplicitAppUserModelID("spyderlib.Spyder") - except AttributeError: - return "SetCurrentProcessExplicitAppUserModelID not found" - - -# [ ] the console state asks for a storage container -# [ ] reopen console on exit - better die open than become a zombie diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/arrayeditor.py spyder-3.0.2+dfsg1/spyderlib/widgets/arrayeditor.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/arrayeditor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/arrayeditor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,778 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -NumPy Array Editor Dialog based on Qt -""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from __future__ import print_function - -from spyderlib.qt.QtGui import (QHBoxLayout, QColor, QTableView, QItemDelegate, - QLineEdit, QCheckBox, QGridLayout, QCursor, - QDoubleValidator, QDialog, QDialogButtonBox, - QMessageBox, QPushButton, QInputDialog, QMenu, - QApplication, QKeySequence, QLabel, QComboBox, - QSpinBox, QStackedWidget, QWidget, QVBoxLayout) -from spyderlib.qt.QtCore import (Qt, QModelIndex, QAbstractTableModel, SIGNAL, - SLOT) -from spyderlib.qt.compat import to_qvariant, from_qvariant - -import numpy as np - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.guiconfig import get_font, new_shortcut -from spyderlib.utils.qthelpers import (add_actions, create_action, keybinding, - qapplication, get_icon) -from spyderlib.py3compat import io, to_text_string, is_text_string - -# Note: string and unicode data types will be formatted with '%s' (see below) -SUPPORTED_FORMATS = { - 'single': '%.3f', - 'double': '%.3f', - 'float_': '%.3f', - 'longfloat': '%.3f', - 'float32': '%.3f', - 'float64': '%.3f', - 'float96': '%.3f', - 'float128': '%.3f', - 'csingle': '%r', - 'complex_': '%r', - 'clongfloat': '%r', - 'complex64': '%r', - 'complex128': '%r', - 'complex192': '%r', - 'complex256': '%r', - 'byte': '%d', - 'short': '%d', - 'intc': '%d', - 'int_': '%d', - 'longlong': '%d', - 'intp': '%d', - 'int8': '%d', - 'int16': '%d', - 'int32': '%d', - 'int64': '%d', - 'ubyte': '%d', - 'ushort': '%d', - 'uintc': '%d', - 'uint': '%d', - 'ulonglong': '%d', - 'uintp': '%d', - 'uint8': '%d', - 'uint16': '%d', - 'uint32': '%d', - 'uint64': '%d', - 'bool_': '%r', - 'bool8': '%r', - 'bool': '%r', - } - - -LARGE_SIZE = 5e5 -LARGE_NROWS = 1e5 -LARGE_COLS = 60 - - -def is_float(dtype): - """Return True if datatype dtype is a float kind""" - return ('float' in dtype.name) or dtype.name in ['single', 'double'] - -def is_number(dtype): - """Return True is datatype dtype is a number kind""" - return is_float(dtype) or ('int' in dtype.name) or ('long' in dtype.name) \ - or ('short' in dtype.name) - -def get_idx_rect(index_list): - """Extract the boundaries from a list of indexes""" - rows, cols = list(zip(*[(i.row(), i.column()) for i in index_list])) - return ( min(rows), max(rows), min(cols), max(cols) ) - - -class ArrayModel(QAbstractTableModel): - """Array Editor Table Model""" - - ROWS_TO_LOAD = 500 - COLS_TO_LOAD = 40 - - def __init__(self, data, format="%.3f", xlabels=None, ylabels=None, - readonly=False, parent=None): - QAbstractTableModel.__init__(self) - - self.dialog = parent - self.changes = {} - self.xlabels = xlabels - self.ylabels = ylabels - self.readonly = readonly - self.test_array = np.array([0], dtype=data.dtype) - - # for complex numbers, shading will be based on absolute value - # but for all other types it will be the real part - if data.dtype in (np.complex64, np.complex128): - self.color_func = np.abs - else: - self.color_func = np.real - - # Backgroundcolor settings - huerange = [.66, .99] # Hue - self.sat = .7 # Saturation - self.val = 1. # Value - self.alp = .6 # Alpha-channel - - self._data = data - self._format = format - - self.total_rows = self._data.shape[0] - self.total_cols = self._data.shape[1] - size = self.total_rows * self.total_cols - - try: - self.vmin = self.color_func(data).min() - self.vmax = self.color_func(data).max() - if self.vmax == self.vmin: - self.vmin -= 1 - self.hue0 = huerange[0] - self.dhue = huerange[1]-huerange[0] - self.bgcolor_enabled = True - except TypeError: - self.vmin = None - self.vmax = None - self.hue0 = None - self.dhue = None - self.bgcolor_enabled = False - - # Use paging when the total size, number of rows or number of - # columns is too large - if size > LARGE_SIZE: - self.rows_loaded = self.ROWS_TO_LOAD - self.cols_loaded = self.COLS_TO_LOAD - else: - if self.total_rows > LARGE_NROWS: - self.rows_loaded = self.ROWS_TO_LOAD - else: - self.rows_loaded = self.total_rows - if self.total_cols > LARGE_COLS: - self.cols_loaded = self.COLS_TO_LOAD - else: - self.cols_loaded = self.total_cols - - def get_format(self): - """Return current format""" - # Avoid accessing the private attribute _format from outside - return self._format - - def get_data(self): - """Return data""" - return self._data - - def set_format(self, format): - """Change display format""" - self._format = format - self.reset() - - def columnCount(self, qindex=QModelIndex()): - """Array column number""" - if self.total_cols <= self.cols_loaded: - return self.total_cols - else: - return self.cols_loaded - - def rowCount(self, qindex=QModelIndex()): - """Array row number""" - if self.total_rows <= self.rows_loaded: - return self.total_rows - else: - return self.rows_loaded - - def can_fetch_more(self, rows=False, columns=False): - if rows: - if self.total_rows > self.rows_loaded: - return True - else: - return False - if columns: - if self.total_cols > self.cols_loaded: - return True - else: - return False - - def fetch_more(self, rows=False, columns=False): - if self.can_fetch_more(rows=rows): - reminder = self.total_rows - self.rows_loaded - items_to_fetch = min(reminder, self.ROWS_TO_LOAD) - self.beginInsertRows(QModelIndex(), self.rows_loaded, - self.rows_loaded + items_to_fetch - 1) - self.rows_loaded += items_to_fetch - self.endInsertRows() - if self.can_fetch_more(columns=columns): - reminder = self.total_cols - self.cols_loaded - items_to_fetch = min(reminder, self.COLS_TO_LOAD) - self.beginInsertColumns(QModelIndex(), self.cols_loaded, - self.cols_loaded + items_to_fetch - 1) - self.cols_loaded += items_to_fetch - self.endInsertColumns() - - def bgcolor(self, state): - """Toggle backgroundcolor""" - self.bgcolor_enabled = state > 0 - self.reset() - - def get_value(self, index): - i = index.row() - j = index.column() - return self.changes.get((i, j), self._data[i, j]) - - def data(self, index, role=Qt.DisplayRole): - """Cell content""" - if not index.isValid(): - return to_qvariant() - value = self.get_value(index) - if role == Qt.DisplayRole: - if value is np.ma.masked: - return '' - else: - return to_qvariant(self._format % value) - elif role == Qt.TextAlignmentRole: - return to_qvariant(int(Qt.AlignCenter|Qt.AlignVCenter)) - elif role == Qt.BackgroundColorRole and self.bgcolor_enabled\ - and value is not np.ma.masked: - hue = self.hue0+\ - self.dhue*(self.vmax-self.color_func(value))\ - /(self.vmax-self.vmin) - hue = float(np.abs(hue)) - color = QColor.fromHsvF(hue, self.sat, self.val, self.alp) - return to_qvariant(color) - elif role == Qt.FontRole: - return to_qvariant(get_font('arrayeditor')) - return to_qvariant() - - def setData(self, index, value, role=Qt.EditRole): - """Cell content change""" - if not index.isValid() or self.readonly: - return False - i = index.row() - j = index.column() - value = from_qvariant(value, str) - if self._data.dtype.name == "bool": - try: - val = bool(float(value)) - except ValueError: - val = value.lower() == "true" - elif self._data.dtype.name.startswith("string"): - val = str(value) - elif self._data.dtype.name.startswith("unicode"): - val = to_text_string(value) - else: - if value.lower().startswith('e') or value.lower().endswith('e'): - return False - try: - val = complex(value) - if not val.imag: - val = val.real - except ValueError as e: - QMessageBox.critical(self.dialog, "Error", - "Value error: %s" % str(e)) - return False - try: - self.test_array[0] = val # will raise an Exception eventually - except OverflowError as e: - print(type(e.message)) - QMessageBox.critical(self.dialog, "Error", - "Overflow error: %s" % e.message) - return False - - # Add change to self.changes - self.changes[(i, j)] = val - self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), - index, index) - if val > self.vmax: - self.vmax = val - if val < self.vmin: - self.vmin = val - return True - - def flags(self, index): - """Set editable flag""" - if not index.isValid(): - return Qt.ItemIsEnabled - return Qt.ItemFlags(QAbstractTableModel.flags(self, index)| - Qt.ItemIsEditable) - - def headerData(self, section, orientation, role=Qt.DisplayRole): - """Set header data""" - if role != Qt.DisplayRole: - return to_qvariant() - labels = self.xlabels if orientation == Qt.Horizontal else self.ylabels - if labels is None: - return to_qvariant(int(section)) - else: - return to_qvariant(labels[section]) - - -class ArrayDelegate(QItemDelegate): - """Array Editor Item Delegate""" - def __init__(self, dtype, parent=None): - QItemDelegate.__init__(self, parent) - self.dtype = dtype - - def createEditor(self, parent, option, index): - """Create editor widget""" - model = index.model() - value = model.get_value(index) - if model._data.dtype.name == "bool": - value = not value - model.setData(index, to_qvariant(value)) - return - elif value is not np.ma.masked: - editor = QLineEdit(parent) - editor.setFont(get_font('arrayeditor')) - editor.setAlignment(Qt.AlignCenter) - if is_number(self.dtype): - editor.setValidator(QDoubleValidator(editor)) - self.connect(editor, SIGNAL("returnPressed()"), - self.commitAndCloseEditor) - return editor - - def commitAndCloseEditor(self): - """Commit and close editor""" - editor = self.sender() - self.emit(SIGNAL("commitData(QWidget*)"), editor) - self.emit(SIGNAL("closeEditor(QWidget*)"), editor) - - def setEditorData(self, editor, index): - """Set editor widget's data""" - text = from_qvariant(index.model().data(index, Qt.DisplayRole), str) - editor.setText(text) - - -#TODO: Implement "Paste" (from clipboard) feature -class ArrayView(QTableView): - """Array view class""" - def __init__(self, parent, model, dtype, shape): - QTableView.__init__(self, parent) - - self.setModel(model) - self.setItemDelegate(ArrayDelegate(dtype, self)) - total_width = 0 - for k in range(shape[1]): - total_width += self.columnWidth(k) - self.viewport().resize(min(total_width, 1024), self.height()) - self.shape = shape - self.menu = self.setup_menu() - new_shortcut(QKeySequence.Copy, self, self.copy) - self.connect(self.horizontalScrollBar(), SIGNAL("valueChanged(int)"), - lambda val: self.load_more_data(val, columns=True)) - self.connect(self.verticalScrollBar(), SIGNAL("valueChanged(int)"), - lambda val: self.load_more_data(val, rows=True)) - - def load_more_data(self, value, rows=False, columns=False): - if rows and value == self.verticalScrollBar().maximum(): - self.model().fetch_more(rows=rows) - if columns and value == self.horizontalScrollBar().maximum(): - self.model().fetch_more(columns=columns) - - def resize_to_contents(self): - """Resize cells to contents""" - QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - self.resizeColumnsToContents() - self.model().fetch_more(columns=True) - self.resizeColumnsToContents() - QApplication.restoreOverrideCursor() - - def setup_menu(self): - """Setup context menu""" - self.copy_action = create_action(self, _( "Copy"), - shortcut=keybinding("Copy"), - icon=get_icon('editcopy.png'), - triggered=self.copy, - context=Qt.WidgetShortcut) - menu = QMenu(self) - add_actions(menu, [self.copy_action, ]) - return menu - - def contextMenuEvent(self, event): - """Reimplement Qt method""" - self.menu.popup(event.globalPos()) - event.accept() - - def keyPressEvent(self, event): - """Reimplement Qt method""" - if event == QKeySequence.Copy: - self.copy() - else: - QTableView.keyPressEvent(self, event) - - def _sel_to_text(self, cell_range): - """Copy an array portion to a unicode string""" - row_min, row_max, col_min, col_max = get_idx_rect(cell_range) - _data = self.model().get_data() - output = io.StringIO() - np.savetxt(output, - _data[row_min:row_max+1, col_min:col_max+1], - delimiter='\t') - contents = output.getvalue() - output.close() - return contents - - def copy(self): - """Copy text to clipboard""" - cliptxt = self._sel_to_text( self.selectedIndexes() ) - clipboard = QApplication.clipboard() - clipboard.setText(cliptxt) - - -class ArrayEditorWidget(QWidget): - def __init__(self, parent, data, readonly=False, - xlabels=None, ylabels=None): - QWidget.__init__(self, parent) - self.data = data - self.old_data_shape = None - if len(self.data.shape) == 1: - self.old_data_shape = self.data.shape - self.data.shape = (self.data.shape[0], 1) - elif len(self.data.shape) == 0: - self.old_data_shape = self.data.shape - self.data.shape = (1, 1) - - format = SUPPORTED_FORMATS.get(data.dtype.name, '%s') - self.model = ArrayModel(self.data, format=format, xlabels=xlabels, - ylabels=ylabels, readonly=readonly, parent=self) - self.view = ArrayView(self, self.model, data.dtype, data.shape) - - btn_layout = QHBoxLayout() - btn_layout.setAlignment(Qt.AlignLeft) - btn = QPushButton(_( "Format")) - # disable format button for int type - btn.setEnabled(is_float(data.dtype)) - btn_layout.addWidget(btn) - self.connect(btn, SIGNAL("clicked()"), self.change_format) - btn = QPushButton(_( "Resize")) - btn_layout.addWidget(btn) - self.connect(btn, SIGNAL("clicked()"), self.view.resize_to_contents) - bgcolor = QCheckBox(_( 'Background color')) - bgcolor.setChecked(self.model.bgcolor_enabled) - bgcolor.setEnabled(self.model.bgcolor_enabled) - self.connect(bgcolor, SIGNAL("stateChanged(int)"), self.model.bgcolor) - btn_layout.addWidget(bgcolor) - - layout = QVBoxLayout() - layout.addWidget(self.view) - layout.addLayout(btn_layout) - self.setLayout(layout) - - def accept_changes(self): - """Accept changes""" - for (i, j), value in list(self.model.changes.items()): - self.data[i, j] = value - if self.old_data_shape is not None: - self.data.shape = self.old_data_shape - - def reject_changes(self): - """Reject changes""" - if self.old_data_shape is not None: - self.data.shape = self.old_data_shape - - def change_format(self): - """Change display format""" - format, valid = QInputDialog.getText(self, _( 'Format'), - _( "Float formatting"), - QLineEdit.Normal, self.model.get_format()) - if valid: - format = str(format) - try: - format % 1.1 - except: - QMessageBox.critical(self, _("Error"), - _("Format (%s) is incorrect") % format) - return - self.model.set_format(format) - - -class ArrayEditor(QDialog): - """Array Editor Dialog""" - def __init__(self, parent=None): - QDialog.__init__(self, parent) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - self.data = None - self.arraywidget = None - self.stack = None - self.layout = None - # Values for 3d array editor - self.dim_indexes = [{}, {}, {}] - self.last_dim = 0 # Adjust this for changing the startup dimension - - def setup_and_check(self, data, title='', readonly=False, - xlabels=None, ylabels=None): - """ - Setup ArrayEditor: - return False if data is not supported, True otherwise - """ - self.data = data - is_record_array = data.dtype.names is not None - is_masked_array = isinstance(data, np.ma.MaskedArray) - if data.size == 0: - self.error(_("Array is empty")) - return False - if data.ndim > 3: - self.error(_("Arrays with more than 3 dimensions " - "are not supported")) - return False - if xlabels is not None and len(xlabels) != self.data.shape[1]: - self.error(_("The 'xlabels' argument length " - "do no match array column number")) - return False - if ylabels is not None and len(ylabels) != self.data.shape[0]: - self.error(_("The 'ylabels' argument length " - "do no match array row number")) - return False - if not is_record_array: - dtn = data.dtype.name - if dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') \ - and not dtn.startswith('unicode'): - arr = _("%s arrays") % data.dtype.name - self.error(_("%s are currently not supported") % arr) - return False - - self.layout = QGridLayout() - self.setLayout(self.layout) - self.setWindowIcon(get_icon('arredit.png')) - if title: - title = to_text_string(title) + " - " + _("NumPy array") - else: - title = _("Array editor") - if readonly: - title += ' (' + _('read only') + ')' - self.setWindowTitle(title) - self.resize(600, 500) - - # Stack widget - self.stack = QStackedWidget(self) - if is_record_array: - for name in data.dtype.names: - self.stack.addWidget(ArrayEditorWidget(self, data[name], - readonly, xlabels, ylabels)) - elif is_masked_array: - self.stack.addWidget(ArrayEditorWidget(self, data, readonly, - xlabels, ylabels)) - self.stack.addWidget(ArrayEditorWidget(self, data.data, readonly, - xlabels, ylabels)) - self.stack.addWidget(ArrayEditorWidget(self, data.mask, readonly, - xlabels, ylabels)) - elif data.ndim == 3: - pass - else: - self.stack.addWidget(ArrayEditorWidget(self, data, readonly, - xlabels, ylabels)) - self.arraywidget = self.stack.currentWidget() - self.connect(self.stack, SIGNAL('currentChanged(int)'), - self.current_widget_changed) - self.layout.addWidget(self.stack, 1, 0) - - # Buttons configuration - btn_layout = QHBoxLayout() - if is_record_array or is_masked_array or data.ndim == 3: - if is_record_array: - btn_layout.addWidget(QLabel(_("Record array fields:"))) - names = [] - for name in data.dtype.names: - field = data.dtype.fields[name] - text = name - if len(field) >= 3: - title = field[2] - if not is_text_string(title): - title = repr(title) - text += ' - '+title - names.append(text) - else: - names = [_('Masked data'), _('Data'), _('Mask')] - if data.ndim == 3: - # QSpinBox - self.index_spin = QSpinBox(self, keyboardTracking=False) - self.connect(self.index_spin, SIGNAL('valueChanged(int)'), - self.change_active_widget) - # QComboBox - names = [str(i) for i in range(3)] - ra_combo = QComboBox(self) - ra_combo.addItems(names) - self.connect(ra_combo, SIGNAL('currentIndexChanged(int)'), - self.current_dim_changed) - # Adding the widgets to layout - label = QLabel(_("Axis:")) - btn_layout.addWidget(label) - btn_layout.addWidget(ra_combo) - self.shape_label = QLabel() - btn_layout.addWidget(self.shape_label) - label = QLabel(_("Index:")) - btn_layout.addWidget(label) - btn_layout.addWidget(self.index_spin) - self.slicing_label = QLabel() - btn_layout.addWidget(self.slicing_label) - # set the widget to display when launched - self.current_dim_changed(self.last_dim) - else: - ra_combo = QComboBox(self) - self.connect(ra_combo, SIGNAL('currentIndexChanged(int)'), - self.stack.setCurrentIndex) - ra_combo.addItems(names) - btn_layout.addWidget(ra_combo) - if is_masked_array: - label = QLabel(_("Warning: changes are applied separately")) - label.setToolTip(_("For performance reasons, changes applied "\ - "to masked array won't be reflected in "\ - "array's data (and vice-versa).")) - btn_layout.addWidget(label) - btn_layout.addStretch() - bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - btn_layout.addWidget(bbox) - self.layout.addLayout(btn_layout, 2, 0) - - self.setMinimumSize(400, 300) - - # Make the dialog act as a window - self.setWindowFlags(Qt.Window) - - return True - - def current_widget_changed(self, index): - self.arraywidget = self.stack.widget(index) - - def change_active_widget(self, index): - """ - This is implemented for handling negative values in index for - 3d arrays, to give the same behavior as slicing - """ - string_index = [':']*3 - string_index[self.last_dim] = '%i' - self.slicing_label.setText((r"Slicing: [" + ", ".join(string_index) + - "]") % index) - if index < 0: - data_index = self.data.shape[self.last_dim] + index - else: - data_index = index - slice_index = [slice(None)]*3 - slice_index[self.last_dim] = data_index - - stack_index = self.dim_indexes[self.last_dim].get(data_index) - if stack_index == None: - stack_index = self.stack.count() - self.stack.addWidget(ArrayEditorWidget(self, - self.data[slice_index])) - self.dim_indexes[self.last_dim][data_index] = stack_index - self.stack.update() - self.stack.setCurrentIndex(stack_index) - - def current_dim_changed(self, index): - """ - This change the active axis the array editor is plotting over - in 3D - """ - self.last_dim = index - string_size = ['%i']*3 - string_size[index] = '%i' - self.shape_label.setText(('Shape: (' + ', '.join(string_size) + - ') ') % self.data.shape) - if self.index_spin.value() != 0: - self.index_spin.setValue(0) - else: - # this is done since if the value is currently 0 it does not emit - # currentIndexChanged(int) - self.change_active_widget(0) - self.index_spin.setRange(-self.data.shape[index], - self.data.shape[index]-1) - - def accept(self): - """Reimplement Qt method""" - for index in range(self.stack.count()): - self.stack.widget(index).accept_changes() - QDialog.accept(self) - - def get_value(self): - """Return modified array -- this is *not* a copy""" - # It is import to avoid accessing Qt C++ object as it has probably - # already been destroyed, due to the Qt.WA_DeleteOnClose attribute - return self.data - - def error(self, message): - """An error occured, closing the dialog box""" - QMessageBox.critical(self, _("Array editor"), message) - self.setAttribute(Qt.WA_DeleteOnClose) - self.reject() - - def reject(self): - """Reimplement Qt method""" - if self.arraywidget is not None: - for index in range(self.stack.count()): - self.stack.widget(index).reject_changes() - QDialog.reject(self) - - -def test_edit(data, title="", xlabels=None, ylabels=None, - readonly=False, parent=None): - """Test subroutine""" - dlg = ArrayEditor(parent) - if dlg.setup_and_check(data, title, xlabels=xlabels, ylabels=ylabels, - readonly=readonly) and dlg.exec_(): - return dlg.get_value() - else: - import sys - sys.exit() - - -def test(): - """Array editor test""" - _app = qapplication() - - arr = np.array(["kjrekrjkejr"]) - print("out:", test_edit(arr, "string array")) - from spyderlib.py3compat import u - arr = np.array([u("kjrekrjkejr")]) - print("out:", test_edit(arr, "unicode array")) - arr = np.ma.array([[1, 0], [1, 0]], mask=[[True, False], [False, False]]) - print("out:", test_edit(arr, "masked array")) - arr = np.zeros((2, 2), {'names': ('red', 'green', 'blue'), - 'formats': (np.float32, np.float32, np.float32)}) - print("out:", test_edit(arr, "record array")) - arr = np.array([(0, 0.0), (0, 0.0), (0, 0.0)], - dtype=[(('title 1', 'x'), '|i1'), - (('title 2', 'y'), '>f4')]) - print("out:", test_edit(arr, "record array with titles")) - arr = np.random.rand(5, 5) - print("out:", test_edit(arr, "float array", - xlabels=['a', 'b', 'c', 'd', 'e'])) - arr = np.round(np.random.rand(5, 5)*10)+\ - np.round(np.random.rand(5, 5)*10)*1j - print("out:", test_edit(arr, "complex array", - xlabels=np.linspace(-12, 12, 5), - ylabels=np.linspace(-12, 12, 5))) - arr_in = np.array([True, False, True]) - print("in:", arr_in) - arr_out = test_edit(arr_in, "bool array") - print("out:", arr_out) - print(arr_in is arr_out) - arr = np.array([1, 2, 3], dtype="int8") - print("out:", test_edit(arr, "int array")) - arr = np.zeros((3,3,4)) - arr[0,0,0]=1 - arr[0,0,1]=2 - arr[0,0,2]=3 - print("out:", test_edit(arr)) - - -if __name__ == "__main__": - test() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/browser.py spyder-3.0.2+dfsg1/spyderlib/widgets/browser.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/browser.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/browser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,258 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Simple web browser widget""" - -from spyderlib.qt.QtGui import (QHBoxLayout, QWidget, QVBoxLayout, - QProgressBar, QLabel, QMenu) -from spyderlib.qt.QtWebKit import QWebView, QWebPage, QWebSettings -from spyderlib.qt.QtCore import SIGNAL, QUrl - -import sys - -# Local imports -from spyderlib.utils.qthelpers import (get_icon, create_action, add_actions, - create_toolbutton, action2button) -from spyderlib.baseconfig import DEV, _ -from spyderlib.widgets.comboboxes import UrlComboBox -from spyderlib.widgets.findreplace import FindReplace -from spyderlib.py3compat import to_text_string, is_text_string - - -class WebView(QWebView): - """Web page""" - def __init__(self, parent): - QWebView.__init__(self, parent) - self.zoom_factor = 1. - self.zoom_out_action = create_action(self, _("Zoom out"), - icon=get_icon('zoom_out.png'), - triggered=self.zoom_out) - self.zoom_in_action = create_action(self, _("Zoom in"), - icon=get_icon('zoom_in.png'), - triggered=self.zoom_in) - - def find_text(self, text, changed=True, - forward=True, case=False, words=False, - regexp=False): - """Find text""" - findflag = QWebPage.FindWrapsAroundDocument - if not forward: - findflag = findflag | QWebPage.FindBackward - if case: - findflag = findflag | QWebPage.FindCaseSensitively - return self.findText(text, findflag) - - def get_selected_text(self): - """Return text selected by current text cursor""" - return self.selectedText() - - def set_font(self, font, fixed_font=None): - settings = self.page().settings() - for fontfamily in (settings.StandardFont, settings.SerifFont, - settings.SansSerifFont, settings.CursiveFont, - settings.FantasyFont): - settings.setFontFamily(fontfamily, font.family()) - if fixed_font is not None: - settings.setFontFamily(settings.FixedFont, fixed_font.family()) - size = font.pointSize() - settings.setFontSize(settings.DefaultFontSize, size) - settings.setFontSize(settings.DefaultFixedFontSize, size) - - def apply_zoom_factor(self): - """Apply zoom factor""" - if hasattr(self, 'setZoomFactor'): - # Assuming Qt >=v4.5 - self.setZoomFactor(self.zoom_factor) - else: - # Qt v4.4 - self.setTextSizeMultiplier(self.zoom_factor) - - def set_zoom_factor(self, zoom_factor): - """Set zoom factor""" - self.zoom_factor = zoom_factor - self.apply_zoom_factor() - - def get_zoom_factor(self): - """Return zoom factor""" - return self.zoom_factor - - def zoom_out(self): - """Zoom out""" - self.zoom_factor = max(.1, self.zoom_factor-.1) - self.apply_zoom_factor() - - def zoom_in(self): - """Zoom in""" - self.zoom_factor += .1 - self.apply_zoom_factor() - - #------ QWebView API ------------------------------------------------------- - def createWindow(self, webwindowtype): - import webbrowser - webbrowser.open(to_text_string(self.url().toString())) - - def contextMenuEvent(self, event): - menu = QMenu(self) - actions = [self.pageAction(QWebPage.Back), - self.pageAction(QWebPage.Forward), None, - self.pageAction(QWebPage.SelectAll), - self.pageAction(QWebPage.Copy), None, - self.zoom_in_action, self.zoom_out_action] - if DEV: - settings = self.page().settings() - settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True) - actions += [None, self.pageAction(QWebPage.InspectElement)] - add_actions(menu, actions) - menu.popup(event.globalPos()) - event.accept() - - -class WebBrowser(QWidget): - """ - Web browser widget - """ - def __init__(self, parent=None): - QWidget.__init__(self, parent) - - self.home_url = None - - self.webview = WebView(self) - self.connect(self.webview, SIGNAL("loadFinished(bool)"), - self.load_finished) - self.connect(self.webview, SIGNAL("titleChanged(QString)"), - self.setWindowTitle) - self.connect(self.webview, SIGNAL("urlChanged(QUrl)"), - self.url_changed) - - home_button = create_toolbutton(self, icon=get_icon('home.png'), - tip=_("Home"), - triggered=self.go_home) - - zoom_out_button = action2button(self.webview.zoom_out_action) - zoom_in_button = action2button(self.webview.zoom_in_action) - - pageact2btn = lambda prop: action2button(self.webview.pageAction(prop), - parent=self.webview) - refresh_button = pageact2btn(QWebPage.Reload) - stop_button = pageact2btn(QWebPage.Stop) - previous_button = pageact2btn(QWebPage.Back) - next_button = pageact2btn(QWebPage.Forward) - - stop_button.setEnabled(False) - self.connect(self.webview, SIGNAL("loadStarted()"), - lambda: stop_button.setEnabled(True)) - self.connect(self.webview, SIGNAL("loadFinished(bool)"), - lambda: stop_button.setEnabled(False)) - - progressbar = QProgressBar(self) - progressbar.setTextVisible(False) - progressbar.hide() - self.connect(self.webview, SIGNAL("loadStarted()"), progressbar.show) - self.connect(self.webview, SIGNAL("loadProgress(int)"), - progressbar.setValue) - self.connect(self.webview, SIGNAL("loadFinished(bool)"), - lambda _state: progressbar.hide()) - - label = QLabel(self.get_label()) - - self.url_combo = UrlComboBox(self) - self.connect(self.url_combo, SIGNAL('valid(bool)'), - self.url_combo_activated) - self.connect(self.webview, SIGNAL("iconChanged()"), self.icon_changed) - - self.find_widget = FindReplace(self) - self.find_widget.set_editor(self.webview) - self.find_widget.hide() - - find_button = create_toolbutton(self, icon='find.png', - tip=_("Find text"), - toggled=self.toggle_find_widget) - self.connect(self.find_widget, SIGNAL("visibility_changed(bool)"), - find_button.setChecked) - - hlayout = QHBoxLayout() - for widget in (previous_button, next_button, home_button, find_button, - label, self.url_combo, zoom_out_button, zoom_in_button, - refresh_button, progressbar, stop_button): - hlayout.addWidget(widget) - - layout = QVBoxLayout() - layout.addLayout(hlayout) - layout.addWidget(self.webview) - layout.addWidget(self.find_widget) - self.setLayout(layout) - - def get_label(self): - """Return address label text""" - return _("Address:") - - def set_home_url(self, text): - """Set home URL""" - self.home_url = QUrl(text) - - def set_url(self, url): - """Set current URL""" - self.url_changed(url) - self.go_to(url) - - def go_to(self, url_or_text): - """Go to page *address*""" - if is_text_string(url_or_text): - url = QUrl(url_or_text) - else: - url = url_or_text - self.webview.load(url) - - def go_home(self): - """Go to home page""" - if self.home_url is not None: - self.set_url(self.home_url) - - def text_to_url(self, text): - """Convert text address into QUrl object""" - return QUrl(text) - - def url_combo_activated(self, valid): - """Load URL from combo box first item""" - text = to_text_string(self.url_combo.currentText()) - self.go_to(self.text_to_url(text)) - - def load_finished(self, ok): - if not ok: - self.webview.setHtml(_("Unable to load page")) - - def url_to_text(self, url): - """Convert QUrl object to displayed text in combo box""" - return url.toString() - - def url_changed(self, url): - """Displayed URL has changed -> updating URL combo box""" - self.url_combo.add_text(self.url_to_text(url)) - - def icon_changed(self): - self.url_combo.setItemIcon(self.url_combo.currentIndex(), - self.webview.icon()) - self.setWindowIcon(self.webview.icon()) - - def toggle_find_widget(self, state): - if state: - self.find_widget.show() - else: - self.find_widget.hide() - - -def main(): - """Run web browser""" - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - widget = WebBrowser() - widget.show() - widget.set_home_url('http://localhost:7464/') - widget.go_home() - sys.exit(app.exec_()) - -if __name__ == '__main__': - main() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/calltip.py spyder-3.0.2+dfsg1/spyderlib/widgets/calltip.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/calltip.py 2015-10-12 00:00:38.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/calltip.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,292 +0,0 @@ -# -*- coding: utf-8 -*- -#----------------------------------------------------------------------------- -# Copyright (c) IPython Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file COPYING.txt, distributed with IPython. -#----------------------------------------------------------------------------- - -""" -Calltip widget used only to show signatures -""" - -# Standard library imports -from unicodedata import category - -# System library imports -from spyderlib.qt import QtCore, QtGui - -# Local imports -from spyderlib.py3compat import to_text_string - - -class CallTipWidget(QtGui.QLabel): - """ Shows call tips by parsing the current text of Q[Plain]TextEdit. - """ - - #-------------------------------------------------------------------------- - # 'QObject' interface - #-------------------------------------------------------------------------- - - def __init__(self, text_edit, hide_timer_on=False): - """ Create a call tip manager that is attached to the specified Qt - text edit widget. - """ - assert isinstance(text_edit, (QtGui.QTextEdit, QtGui.QPlainTextEdit)) - super(CallTipWidget, self).__init__(None, QtCore.Qt.ToolTip) - self.app = QtCore.QCoreApplication.instance() - - self.hide_timer_on = hide_timer_on - self._hide_timer = QtCore.QBasicTimer() - self._text_edit = text_edit - - self.setFont(text_edit.document().defaultFont()) - self.setForegroundRole(QtGui.QPalette.ToolTipText) - self.setBackgroundRole(QtGui.QPalette.ToolTipBase) - self.setPalette(QtGui.QToolTip.palette()) - - self.setAlignment(QtCore.Qt.AlignLeft) - self.setIndent(1) - self.setFrameStyle(QtGui.QFrame.NoFrame) - self.setMargin(1 + self.style().pixelMetric( - QtGui.QStyle.PM_ToolTipLabelFrameWidth, None, self)) - - def eventFilter(self, obj, event): - """ Reimplemented to hide on certain key presses and on text edit focus - changes. - """ - if obj == self._text_edit: - etype = event.type() - - if etype == QtCore.QEvent.KeyPress: - key = event.key() - if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return, - QtCore.Qt.Key_Down): - self.hide() - elif key == QtCore.Qt.Key_Escape: - self.hide() - return True - - elif etype == QtCore.QEvent.FocusOut: - self.hide() - - elif etype == QtCore.QEvent.Enter: - if (self._hide_timer.isActive() and - self.app.topLevelAt(QtGui.QCursor.pos()) == self): - self._hide_timer.stop() - - elif etype == QtCore.QEvent.Leave: - self._leave_event_hide() - - return super(CallTipWidget, self).eventFilter(obj, event) - - def timerEvent(self, event): - """ Reimplemented to hide the widget when the hide timer fires. - """ - if event.timerId() == self._hide_timer.timerId(): - self._hide_timer.stop() - self.hide() - - #-------------------------------------------------------------------------- - # 'QWidget' interface - #-------------------------------------------------------------------------- - - def enterEvent(self, event): - """ Reimplemented to cancel the hide timer. - """ - super(CallTipWidget, self).enterEvent(event) - if (self._hide_timer.isActive() and - self.app.topLevelAt(QtGui.QCursor.pos()) == self): - self._hide_timer.stop() - - def hideEvent(self, event): - """ Reimplemented to disconnect signal handlers and event filter. - """ - super(CallTipWidget, self).hideEvent(event) - self._text_edit.cursorPositionChanged.disconnect( - self._cursor_position_changed) - self._text_edit.removeEventFilter(self) - - def leaveEvent(self, event): - """ Reimplemented to start the hide timer. - """ - super(CallTipWidget, self).leaveEvent(event) - self._leave_event_hide() - - def mousePressEvent(self, event): - super(CallTipWidget, self).mousePressEvent(event) - self.hide() - - def paintEvent(self, event): - """ Reimplemented to paint the background panel. - """ - painter = QtGui.QStylePainter(self) - option = QtGui.QStyleOptionFrame() - option.initFrom(self) - painter.drawPrimitive(QtGui.QStyle.PE_PanelTipLabel, option) - painter.end() - - super(CallTipWidget, self).paintEvent(event) - - def setFont(self, font): - """ Reimplemented to allow use of this method as a slot. - """ - super(CallTipWidget, self).setFont(font) - - def showEvent(self, event): - """ Reimplemented to connect signal handlers and event filter. - """ - super(CallTipWidget, self).showEvent(event) - self._text_edit.cursorPositionChanged.connect( - self._cursor_position_changed) - self._text_edit.installEventFilter(self) - - #-------------------------------------------------------------------------- - # 'CallTipWidget' interface - #-------------------------------------------------------------------------- - - def show_tip(self, point, tip, wrapped_tiplines): - """ Attempts to show the specified tip at the current cursor location. - """ - # Attempt to find the cursor position at which to show the call tip. - text_edit = self._text_edit - cursor = text_edit.textCursor() - search_pos = cursor.position() - 1 - self._start_position, _ = self._find_parenthesis(search_pos, - forward=False) - if self._start_position == -1: - return False - - if self.hide_timer_on: - self._hide_timer.stop() - # Logic to decide how much time to show the calltip depending - # on the amount of text present - if len(wrapped_tiplines) == 1: - args = wrapped_tiplines[0].split('(')[1] - nargs = len(args.split(',')) - if nargs == 1: - hide_time = 1400 - elif nargs == 2: - hide_time = 1600 - else: - hide_time = 1800 - elif len(wrapped_tiplines) == 2: - args1 = wrapped_tiplines[1].strip() - nargs1 = len(args1.split(',')) - if nargs1 == 1: - hide_time = 2500 - else: - hide_time = 2800 - else: - hide_time = 3500 - self._hide_timer.start(hide_time, self) - - # Set the text and resize the widget accordingly. - self.setText(tip) - self.resize(self.sizeHint()) - - # Locate and show the widget. Place the tip below the current line - # unless it would be off the screen. In that case, decide the best - # location based trying to minimize the area that goes off-screen. - padding = 3 # Distance in pixels between cursor bounds and tip box. - cursor_rect = text_edit.cursorRect(cursor) - screen_rect = self.app.desktop().screenGeometry(text_edit) - point.setY(point.y() + padding) - tip_height = self.size().height() - tip_width = self.size().width() - - vertical = 'bottom' - horizontal = 'Right' - if point.y() + tip_height > screen_rect.height() + screen_rect.y(): - point_ = text_edit.mapToGlobal(cursor_rect.topRight()) - # If tip is still off screen, check if point is in top or bottom - # half of screen. - if point_.y() - tip_height < padding: - # If point is in upper half of screen, show tip below it. - # otherwise above it. - if 2*point.y() < screen_rect.height(): - vertical = 'bottom' - else: - vertical = 'top' - else: - vertical = 'top' - if point.x() + tip_width > screen_rect.width() + screen_rect.x(): - point_ = text_edit.mapToGlobal(cursor_rect.topRight()) - # If tip is still off-screen, check if point is in the right or - # left half of the screen. - if point_.x() - tip_width < padding: - if 2*point.x() < screen_rect.width(): - horizontal = 'Right' - else: - horizontal = 'Left' - else: - horizontal = 'Left' - pos = getattr(cursor_rect, '%s%s' %(vertical, horizontal)) - adjusted_point = text_edit.mapToGlobal(pos()) - if vertical == 'top': - point.setY(adjusted_point.y() - tip_height - padding) - if horizontal == 'Left': - point.setX(adjusted_point.x() - tip_width - padding) - - self.move(point) - self.show() - return True - - #-------------------------------------------------------------------------- - # Protected interface - #-------------------------------------------------------------------------- - - def _find_parenthesis(self, position, forward=True): - """ If 'forward' is True (resp. False), proceed forwards - (resp. backwards) through the line that contains 'position' until an - unmatched closing (resp. opening) parenthesis is found. Returns a - tuple containing the position of this parenthesis (or -1 if it is - not found) and the number commas (at depth 0) found along the way. - """ - commas = depth = 0 - document = self._text_edit.document() - char = to_text_string(document.characterAt(position)) - # Search until a match is found or a non-printable character is - # encountered. - while category(char) != 'Cc' and position > 0: - if char == ',' and depth == 0: - commas += 1 - elif char == ')': - if forward and depth == 0: - break - depth += 1 - elif char == '(': - if not forward and depth == 0: - break - depth -= 1 - position += 1 if forward else -1 - char = to_text_string(document.characterAt(position)) - else: - position = -1 - return position, commas - - def _leave_event_hide(self): - """ Hides the tooltip after some time has passed (assuming the cursor is - not over the tooltip). - """ - if (not self._hide_timer.isActive() and - # If Enter events always came after Leave events, we wouldn't need - # this check. But on Mac OS, it sometimes happens the other way - # around when the tooltip is created. - self.app.topLevelAt(QtGui.QCursor.pos()) != self): - self._hide_timer.start(800, self) - - #------ Signal handlers ---------------------------------------------------- - - def _cursor_position_changed(self): - """ Updates the tip based on user cursor movement. - """ - cursor = self._text_edit.textCursor() - if cursor.position() <= self._start_position: - self.hide() - else: - if not self.hide_timer_on: - position, commas = self._find_parenthesis(self._start_position + 1) - if position != -1: - self.hide() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/colors.py spyder-3.0.2+dfsg1/spyderlib/widgets/colors.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/colors.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/colors.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- - -from spyderlib.qt.QtGui import (QLineEdit, QIcon, QHBoxLayout, QColor, - QPushButton, QColorDialog, QPixmap) -from spyderlib.qt.QtCore import SIGNAL, QSize, Slot, Property - -# Local imports -from spyderlib.py3compat import is_text_string - - -class ColorButton(QPushButton): - """ - Color choosing push button - """ - __pyqtSignals__ = ("colorChanged(QColor)",) - - def __init__(self, parent=None): - QPushButton.__init__(self, parent) - self.setFixedSize(20, 20) - self.setIconSize(QSize(12, 12)) - self.connect(self, SIGNAL("clicked()"), self.choose_color) - self._color = QColor() - - def choose_color(self): - color = QColorDialog.getColor(self._color, self.parentWidget(), - 'Select Color', - QColorDialog.ShowAlphaChannel) - if color.isValid(): - self.set_color(color) - - def get_color(self): - return self._color - - @Slot(QColor) - def set_color(self, color): - if color != self._color: - self._color = color - self.emit(SIGNAL("colorChanged(QColor)"), self._color) - pixmap = QPixmap(self.iconSize()) - pixmap.fill(color) - self.setIcon(QIcon(pixmap)) - - color = Property("QColor", get_color, set_color) - - -def text_to_qcolor(text): - """ - Create a QColor from specified string - Avoid warning from Qt when an invalid QColor is instantiated - """ - color = QColor() - text = str(text) - if not is_text_string(text): - return color - if text.startswith('#') and len(text)==7: - correct = '#0123456789abcdef' - for char in text: - if char.lower() not in correct: - return color - elif text not in list(QColor.colorNames()): - return color - color.setNamedColor(text) - return color - - -class ColorLayout(QHBoxLayout): - """Color-specialized QLineEdit layout""" - def __init__(self, color, parent=None): - QHBoxLayout.__init__(self) - assert isinstance(color, QColor) - self.lineedit = QLineEdit(color.name(), parent) - self.connect(self.lineedit, SIGNAL("textChanged(QString)"), - self.update_color) - self.addWidget(self.lineedit) - self.colorbtn = ColorButton(parent) - self.colorbtn.color = color - self.connect(self.colorbtn, SIGNAL("colorChanged(QColor)"), - self.update_text) - self.addWidget(self.colorbtn) - - def update_color(self, text): - color = text_to_qcolor(text) - if color.isValid(): - self.colorbtn.color = color - - def update_text(self, color): - self.lineedit.setText(color.name()) - - def text(self): - return self.lineedit.text() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/comboboxes.py spyder-3.0.2+dfsg1/spyderlib/widgets/comboboxes.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/comboboxes.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/comboboxes.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,226 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Customized combobox widgets""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import (QComboBox, QFont, QToolTip, QSizePolicy, - QCompleter) -from spyderlib.qt.QtCore import SIGNAL, Qt, QUrl, QTimer - -import os.path as osp - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.py3compat import to_text_string - - -class BaseComboBox(QComboBox): - """Editable combo box base class""" - def __init__(self, parent): - QComboBox.__init__(self, parent) - self.setEditable(True) - self.setCompleter(QCompleter(self)) - - # --- overrides - def keyPressEvent(self, event): - """Handle key press events""" - if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: - if self.add_current_text_if_valid(): - self.selected() - else: - QComboBox.keyPressEvent(self, event) - - def focusOutEvent(self, event): - """Handle focus out event""" - # Calling asynchronously the 'add_current_text' to avoid crash - # https://groups.google.com/group/spyderlib/browse_thread/thread/2257abf530e210bd - QTimer.singleShot(50, self.add_current_text_if_valid) - QComboBox.focusOutEvent(self, event) - - # --- own methods - def is_valid(self, qstr): - """ - Return True if string is valid - Return None if validation can't be done - """ - pass - - def selected(self): - """Action to be executed when a valid item has been selected""" - self.emit(SIGNAL('valid(bool)'), True) - - def add_text(self, text): - """Add text to combo box: add a new item if text is not found in - combo box items""" - index = self.findText(text) - while index != -1: - self.removeItem(index) - index = self.findText(text) - self.insertItem(0, text) - index = self.findText('') - if index != -1: - self.removeItem(index) - self.insertItem(0, '') - if text != '': - self.setCurrentIndex(1) - else: - self.setCurrentIndex(0) - else: - self.setCurrentIndex(0) - - def add_current_text(self): - """Add current text to combo box history (convenient method)""" - self.add_text(self.currentText()) - - def add_current_text_if_valid(self): - """Add current text to combo box history if valid""" - valid = self.is_valid(self.currentText()) - if valid or valid is None: - self.add_current_text() - return True - - -class PatternComboBox(BaseComboBox): - """Search pattern combo box""" - def __init__(self, parent, items=None, tip=None, - adjust_to_minimum=True): - BaseComboBox.__init__(self, parent) - if adjust_to_minimum: - self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - if items is not None: - self.addItems(items) - if tip is not None: - self.setToolTip(tip) - - -class EditableComboBox(BaseComboBox): - """ - Editable combo box + Validate - """ - def __init__(self, parent): - BaseComboBox.__init__(self, parent) - self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) - self.font = QFont() - self.connect(self, SIGNAL("editTextChanged(QString)"), self.validate) - self.connect(self, SIGNAL("activated(QString)"), - lambda qstr: self.validate(qstr, editing=False)) - self.set_default_style() - self.tips = {True: _("Press enter to validate this entry"), - False: _('This entry is incorrect')} - - def show_tip(self, tip=""): - """Show tip""" - QToolTip.showText(self.mapToGlobal(self.pos()), tip, self) - - def set_default_style(self): - """Set widget style to default""" - self.font.setBold(False) - self.setFont(self.font) - self.setStyleSheet("") - self.show_tip() - - def selected(self): - """Action to be executed when a valid item has been selected""" - BaseComboBox.selected(self) - self.set_default_style() - - def validate(self, qstr, editing=True): - """Validate entered path""" - valid = self.is_valid(qstr) - if self.hasFocus() and valid is not None: - self.font.setBold(True) - self.setFont(self.font) - if valid: - self.setStyleSheet("color:rgb(50, 155, 50);") - else: - self.setStyleSheet("color:rgb(200, 50, 50);") - if editing: - # Combo box text is being modified: invalidate the entry - self.show_tip(self.tips[valid]) - self.emit(SIGNAL('valid(bool)'), False) - else: - # A new item has just been selected - if valid: - self.selected() - else: - self.emit(SIGNAL('valid(bool)'), False) - else: - self.set_default_style() - - -class PathComboBox(EditableComboBox): - """ - QComboBox handling path locations - """ - def __init__(self, parent, adjust_to_contents=False): - EditableComboBox.__init__(self, parent) - if adjust_to_contents: - self.setSizeAdjustPolicy(QComboBox.AdjustToContents) - else: - self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - self.tips = {True: _("Press enter to validate this path"), - False: _('This path is incorrect.\n' - 'Enter a correct directory path,\n' - 'then press enter to validate')} - - def is_valid(self, qstr=None): - """Return True if string is valid""" - if qstr is None: - qstr = self.currentText() - return osp.isdir( to_text_string(qstr) ) - - def selected(self): - """Action to be executed when a valid item has been selected""" - EditableComboBox.selected(self) - self.emit(SIGNAL("open_dir(QString)"), self.currentText()) - - -class UrlComboBox(PathComboBox): - """ - QComboBox handling urls - """ - def __init__(self, parent, adjust_to_contents=False): - PathComboBox.__init__(self, parent, adjust_to_contents) - self.disconnect(self, SIGNAL("editTextChanged(QString)"), self.validate) - - def is_valid(self, qstr=None): - """Return True if string is valid""" - if qstr is None: - qstr = self.currentText() - return QUrl(qstr).isValid() - - -def is_module_or_package(path): - """Return True if path is a Python module/package""" - is_module = osp.isfile(path) and osp.splitext(path)[1] in ('.py', '.pyw') - is_package = osp.isdir(path) and osp.isfile(osp.join(path, '__init__.py')) - return is_module or is_package - -class PythonModulesComboBox(PathComboBox): - """ - QComboBox handling Python modules or packages path - (i.e. .py, .pyw files *and* directories containing __init__.py) - """ - def __init__(self, parent, adjust_to_contents=False): - PathComboBox.__init__(self, parent, adjust_to_contents) - - def is_valid(self, qstr=None): - """Return True if string is valid""" - if qstr is None: - qstr = self.currentText() - return is_module_or_package(to_text_string(qstr)) - - def selected(self): - """Action to be executed when a valid item has been selected""" - EditableComboBox.selected(self) - self.emit(SIGNAL("open(QString)"), self.currentText()) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/dataframeeditor.py spyder-3.0.2+dfsg1/spyderlib/widgets/dataframeeditor.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/dataframeeditor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/dataframeeditor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,624 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2014 Spyder development team -# Licensed under the terms of the New BSD License -# -# DataFrameModel is based on the class ArrayModel from array editor -# and the class DataFrameModel from the pandas project. -# Present in pandas.sandbox.qtpandas in v0.13.1 -# Copyright (c) 2011-2012, Lambda Foundry, Inc. -# and PyData Development Team All rights reserved - -""" -Pandas DataFrame Editor Dialog -""" - -from spyderlib.qt.QtCore import (QAbstractTableModel, Qt, QModelIndex, - SIGNAL, SLOT) -from spyderlib.qt.QtGui import (QDialog, QTableView, QColor, QGridLayout, - QDialogButtonBox, QHBoxLayout, QPushButton, - QCheckBox, QMessageBox, QInputDialog, QCursor, - QLineEdit, QApplication, QMenu, QKeySequence) -from spyderlib.qt.compat import to_qvariant, from_qvariant -from spyderlib.utils.qthelpers import (qapplication, get_icon, create_action, - add_actions, keybinding) - -from spyderlib.baseconfig import _ -from spyderlib.guiconfig import get_font, new_shortcut -from spyderlib.py3compat import io, is_text_string, to_text_string, PY2 -from spyderlib.utils import encoding -from spyderlib.widgets.arrayeditor import get_idx_rect - -from pandas import DataFrame, Series -import numpy as np - -# Supported Numbers and complex numbers -_sup_nr = (float, int, np.int64, np.int32) -_sup_com = (complex, np.complex64, np.complex128) -# Used to convert bool intrance to false since bool('False') will return True -_bool_false = ['false', '0'] - - -LARGE_SIZE = 5e5 -LARGE_NROWS = 1e5 -LARGE_COLS = 60 - - -def bool_false_check(value): - """ - Used to convert bool intrance to false since any string in bool('') - will return True - """ - if value.lower() in _bool_false: - value = '' - return value - - -def global_max(col_vals, index): - """Returns the global maximum and minimum""" - max_col, min_col = zip(*col_vals) - return max(max_col), min(min_col) - - -class DataFrameModel(QAbstractTableModel): - """ DataFrame Table Model""" - - ROWS_TO_LOAD = 500 - COLS_TO_LOAD = 40 - - def __init__(self, dataFrame, format="%.3g", parent=None): - QAbstractTableModel.__init__(self) - self.dialog = parent - self.df = dataFrame - self.df_index = dataFrame.index.tolist() - self.df_header = dataFrame.columns.tolist() - self._format = format - self.complex_intran = None - - self.total_rows = self.df.shape[0] - self.total_cols = self.df.shape[1] - size = self.total_rows * self.total_cols - - huerange = [.66, .99] # Hue - self.sat = .7 # Saturation - self.val = 1. # Value - self.alp = .6 # Alpha-channel - self.hue0 = huerange[0] - self.dhue = huerange[1]-huerange[0] - self.max_min_col = None - if size < LARGE_SIZE: - self.max_min_col_update() - self.colum_avg_enabled = True - self.bgcolor_enabled = True - self.colum_avg(1) - else: - self.colum_avg_enabled = False - self.bgcolor_enabled = False - self.colum_avg(0) - - # Use paging when the total size, number of rows or number of - # columns is too large - if size > LARGE_SIZE: - self.rows_loaded = self.ROWS_TO_LOAD - self.cols_loaded = self.COLS_TO_LOAD - else: - if self.total_rows > LARGE_NROWS: - self.rows_loaded = self.ROWS_TO_LOAD - else: - self.rows_loaded = self.total_rows - if self.total_cols > LARGE_COLS: - self.cols_loaded = self.COLS_TO_LOAD - else: - self.cols_loaded = self.total_cols - - def max_min_col_update(self): - """Determines the maximum and minimum number in each column""" - # If there are no rows to compute max/min then return - if self.df.shape[0] == 0: - return - max_r = self.df.max(numeric_only=True) - min_r = self.df.min(numeric_only=True) - self.max_min_col = list(zip(max_r, min_r)) - if len(self.max_min_col) != self.df.shape[1]: - # Then it contain complex numbers or other types - float_intran = self.df.applymap(lambda e: isinstance(e, _sup_nr)) - self.complex_intran = self.df.applymap(lambda e: - isinstance(e, _sup_com)) - mask = float_intran & (~ self.complex_intran) - try: - df_abs = self.df[self.complex_intran].abs() - except TypeError: - df_abs = self.df[self.complex_intran] - max_c = df_abs.max(skipna=True) - min_c = df_abs.min(skipna=True) - df_real = self.df[mask] - max_r = df_real.max(skipna=True) - min_r = df_real.min(skipna=True) - self.max_min_col = list(zip(DataFrame([max_c, - max_r]).max(skipna=True), - DataFrame([min_c, - min_r]).min(skipna=True))) - self.max_min_col = [[vmax, vmin-1] if vmax == vmin else [vmax, vmin] - for vmax, vmin in self.max_min_col] - - def get_format(self): - """Return current format""" - # Avoid accessing the private attribute _format from outside - return self._format - - def set_format(self, format): - """Change display format""" - self._format = format - self.reset() - - def bgcolor(self, state): - """Toggle backgroundcolor""" - self.bgcolor_enabled = state > 0 - self.reset() - - def colum_avg(self, state): - """Toggle backgroundcolor""" - self.colum_avg_enabled = state > 0 - if self.colum_avg_enabled: - self.return_max = lambda col_vals, index: col_vals[index] - else: - self.return_max = global_max - self.reset() - - def headerData(self, section, orientation, role=Qt.DisplayRole): - """Set header data""" - if role != Qt.DisplayRole: - return to_qvariant() - - if orientation == Qt.Horizontal: - if section == 0: - return 'Index' - elif section == 1 and PY2: - # Get rid of possible BOM utf-8 data present at the - # beginning of a file, which gets attached to the first - # column header when headers are present in the first - # row. - # Fixes Issue 2514 - try: - header = to_text_string(self.df_header[0], - encoding='utf-8-sig') - except: - header = to_text_string(self.df_header[0]) - return to_qvariant(header) - else: - return to_qvariant(to_text_string(self.df_header[section-1])) - else: - return to_qvariant() - - def get_bgcolor(self, index): - """Background color depending on value""" - column = index.column() - if column == 0: - color = QColor(Qt.lightGray) - color.setAlphaF(.8) - return color - if not self.bgcolor_enabled: - return - value = self.get_value(index.row(), column-1) - if isinstance(value, _sup_com): - color_func = abs - else: - color_func = float - if isinstance(value, _sup_nr+_sup_com) and self.bgcolor_enabled: - vmax, vmin = self.return_max(self.max_min_col, column-1) - hue = self.hue0 + self.dhue*(vmax-color_func(value)) / (vmax-vmin) - hue = float(abs(hue)) - color = QColor.fromHsvF(hue, self.sat, self.val, self.alp) - elif is_text_string(value): - color = QColor(Qt.lightGray) - color.setAlphaF(.05) - else: - color = QColor(Qt.lightGray) - color.setAlphaF(.3) - return color - - def get_value(self, row, column): - """Returns the value of the DataFrame""" - # To increase the performance iat is used but that requires error - # handling, so fallback uses iloc - try: - value = self.df.iat[row, column] - except: - value = self.df.iloc[row, column] - return value - - def update_df_index(self): - """"Update the DataFrame index""" - self.df_index = self.df.index.tolist() - - def data(self, index, role=Qt.DisplayRole): - """Cell content""" - if not index.isValid(): - return to_qvariant() - if role == Qt.DisplayRole or role == Qt.EditRole: - column = index.column() - row = index.row() - if column == 0: - return to_qvariant(to_text_string(self.df_index[row])) - else: - value = self.get_value(row, column-1) - if isinstance(value, float): - return to_qvariant(self._format % value) - else: - try: - return to_qvariant(to_text_string(value)) - except UnicodeDecodeError: - return to_qvariant(encoding.to_unicode(value)) - elif role == Qt.BackgroundColorRole: - return to_qvariant(self.get_bgcolor(index)) - elif role == Qt.FontRole: - return to_qvariant(get_font('arrayeditor')) - return to_qvariant() - - def sort(self, column, order=Qt.AscendingOrder): - """Overriding sort method""" - if self.complex_intran is not None: - if self.complex_intran.any(axis=0).iloc[column-1]: - QMessageBox.critical(self.dialog, "Error", - "TypeError error: no ordering " - "relation is defined for complex numbers") - return False - try: - if column > 0: - self.df.sort(columns=self.df.columns[column-1], - ascending=order, inplace=True) - self.update_df_index() - else: - self.df.sort_index(inplace=True, ascending=order) - self.update_df_index() - except TypeError as e: - QMessageBox.critical(self.dialog, "Error", - "TypeError error: %s" % str(e)) - return False - - self.reset() - return True - - def flags(self, index): - """Set flags""" - if index.column() == 0: - return Qt.ItemIsEnabled | Qt.ItemIsSelectable - return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | - Qt.ItemIsEditable) - - def setData(self, index, value, role=Qt.EditRole, change_type=None): - """Cell content change""" - column = index.column() - row = index.row() - - if change_type is not None: - try: - value = self.data(index, role=Qt.DisplayRole) - val = from_qvariant(value, str) - if change_type is bool: - val = bool_false_check(val) - self.df.iloc[row, column - 1] = change_type(val) - except ValueError: - self.df.iloc[row, column - 1] = change_type('0') - else: - val = from_qvariant(value, str) - current_value = self.get_value(row, column-1) - if isinstance(current_value, bool): - val = bool_false_check(val) - if isinstance(current_value, ((bool,) + _sup_nr + _sup_com)) or \ - is_text_string(current_value): - try: - self.df.iloc[row, column-1] = current_value.__class__(val) - except ValueError as e: - QMessageBox.critical(self.dialog, "Error", - "Value error: %s" % str(e)) - return False - else: - QMessageBox.critical(self.dialog, "Error", - "The type of the cell is not a supported " - "type") - return False - self.max_min_col_update() - return True - - def get_data(self): - """Return data""" - return self.df - - def rowCount(self, index=QModelIndex()): - """DataFrame row number""" - if self.total_rows <= self.rows_loaded: - return self.total_rows - else: - return self.rows_loaded - - def can_fetch_more(self, rows=False, columns=False): - if rows: - if self.total_rows > self.rows_loaded: - return True - else: - return False - if columns: - if self.total_cols > self.cols_loaded: - return True - else: - return False - - def fetch_more(self, rows=False, columns=False): - if self.can_fetch_more(rows=rows): - reminder = self.total_rows - self.rows_loaded - items_to_fetch = min(reminder, self.ROWS_TO_LOAD) - self.beginInsertRows(QModelIndex(), self.rows_loaded, - self.rows_loaded + items_to_fetch - 1) - self.rows_loaded += items_to_fetch - self.endInsertRows() - if self.can_fetch_more(columns=columns): - reminder = self.total_cols - self.cols_loaded - items_to_fetch = min(reminder, self.COLS_TO_LOAD) - self.beginInsertColumns(QModelIndex(), self.cols_loaded, - self.cols_loaded + items_to_fetch - 1) - self.cols_loaded += items_to_fetch - self.endInsertColumns() - - def columnCount(self, index=QModelIndex()): - """DataFrame column number""" - # This is done to implement series - if len(self.df.shape) == 1: - return 2 - elif self.total_cols <= self.cols_loaded: - return self.total_cols + 1 - else: - return self.cols_loaded + 1 - - -class DataFrameView(QTableView): - """Data Frame view class""" - def __init__(self, parent, model): - QTableView.__init__(self, parent) - self.setModel(model) - - self.sort_old = [None] - self.header_class = self.horizontalHeader() - self.connect(self.header_class, - SIGNAL("sectionClicked(int)"), self.sortByColumn) - self.menu = self.setup_menu() - new_shortcut(QKeySequence.Copy, self, self.copy) - self.connect(self.horizontalScrollBar(), SIGNAL("valueChanged(int)"), - lambda val: self.load_more_data(val, columns=True)) - self.connect(self.verticalScrollBar(), SIGNAL("valueChanged(int)"), - lambda val: self.load_more_data(val, rows=True)) - - def load_more_data(self, value, rows=False, columns=False): - if rows and value == self.verticalScrollBar().maximum(): - self.model().fetch_more(rows=rows) - if columns and value == self.horizontalScrollBar().maximum(): - self.model().fetch_more(columns=columns) - - def sortByColumn(self, index): - """ Implement a Column sort """ - if self.sort_old == [None]: - self.header_class.setSortIndicatorShown(True) - sort_order = self.header_class.sortIndicatorOrder() - if not self.model().sort(index, sort_order): - if len(self.sort_old) != 2: - self.header_class.setSortIndicatorShown(False) - else: - self.header_class.setSortIndicator(self.sort_old[0], - self.sort_old[1]) - return - self.sort_old = [index, self.header_class.sortIndicatorOrder()] - - def contextMenuEvent(self, event): - """Reimplement Qt method""" - self.menu.popup(event.globalPos()) - event.accept() - - def setup_menu(self): - """Setup context menu""" - copy_action = create_action(self, _( "Copy"), - shortcut=keybinding("Copy"), - icon=get_icon('editcopy.png'), - triggered=self.copy, - context=Qt.WidgetShortcut) - functions = ((_("To bool"), bool), (_("To complex"), complex), - (_("To int"), int), (_("To float"), float), - (_("To str"), to_text_string)) - types_in_menu = [copy_action] - for name, func in functions: - types_in_menu += [create_action(self, name, - triggered=lambda func=func: - self.change_type(func), - context=Qt.WidgetShortcut)] - menu = QMenu(self) - add_actions(menu, types_in_menu) - return menu - - def change_type(self, func): - """A function that changes types of cells""" - model = self.model() - index_list = self.selectedIndexes() - [model.setData(i, '', change_type=func) for i in index_list] - - def copy(self): - """Copy text to clipboard""" - (row_min, row_max, - col_min, col_max) = get_idx_rect(self.selectedIndexes()) - index = header = False - if col_min == 0: - col_min = 1 - index = True - df = self.model().df - if col_max == 0: # To copy indices - contents = '\n'.join(map(str, df.index.tolist()[slice(row_min, - row_max+1)])) - else: # To copy DataFrame - if (col_min == 0 or col_min == 1) and (df.shape[1] == col_max): - header = True - obj = df.iloc[slice(row_min, row_max+1), slice(col_min-1, col_max)] - output = io.StringIO() - obj.to_csv(output, sep='\t', index=index, header=header) - contents = output.getvalue() - output.close() - clipboard = QApplication.clipboard() - clipboard.setText(contents) - - -class DataFrameEditor(QDialog): - """ Data Frame Editor Dialog """ - def __init__(self, parent=None): - QDialog.__init__(self, parent) - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - self.is_series = False - self.layout = None - - def setup_and_check(self, data, title=''): - """ - Setup DataFrameEditor: - return False if data is not supported, True otherwise - """ - self.layout = QGridLayout() - self.setLayout(self.layout) - self.setWindowIcon(get_icon('arredit.png')) - if title: - title = to_text_string(title) + " - %s" % data.__class__.__name__ - else: - title = _("%s editor") % data.__class__.__name__ - if isinstance(data, Series): - self.is_series = True - data = data.to_frame() - - self.setWindowTitle(title) - self.resize(600, 500) - - self.dataModel = DataFrameModel(data, parent=self) - self.dataTable = DataFrameView(self, self.dataModel) - - self.layout.addWidget(self.dataTable) - self.setLayout(self.layout) - self.setMinimumSize(400, 300) - # Make the dialog act as a window - self.setWindowFlags(Qt.Window) - btn_layout = QHBoxLayout() - - btn = QPushButton(_("Format")) - # disable format button for int type - btn_layout.addWidget(btn) - self.connect(btn, SIGNAL("clicked()"), self.change_format) - btn = QPushButton(_('Resize')) - btn_layout.addWidget(btn) - self.connect(btn, SIGNAL("clicked()"), self.resize_to_contents) - - bgcolor = QCheckBox(_('Background color')) - bgcolor.setChecked(self.dataModel.bgcolor_enabled) - bgcolor.setEnabled(self.dataModel.bgcolor_enabled) - self.connect(bgcolor, SIGNAL("stateChanged(int)"), - self.change_bgcolor_enable) - btn_layout.addWidget(bgcolor) - - self.bgcolor_global = QCheckBox(_('Column min/max')) - self.bgcolor_global.setChecked(self.dataModel.colum_avg_enabled) - self.bgcolor_global.setEnabled(not self.is_series and - self.dataModel.bgcolor_enabled) - self.connect(self.bgcolor_global, SIGNAL("stateChanged(int)"), - self.dataModel.colum_avg) - btn_layout.addWidget(self.bgcolor_global) - - btn_layout.addStretch() - bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - btn_layout.addWidget(bbox) - - self.layout.addLayout(btn_layout, 2, 0) - - return True - - def change_bgcolor_enable(self, state): - """ - This is implementet so column min/max is only active when bgcolor is - """ - self.dataModel.bgcolor(state) - self.bgcolor_global.setEnabled(not self.is_series and state > 0) - - def change_format(self): - """Change display format""" - format, valid = QInputDialog.getText(self, _('Format'), - _("Float formatting"), - QLineEdit.Normal, - self.dataModel.get_format()) - if valid: - format = str(format) - try: - format % 1.1 - except: - QMessageBox.critical(self, _("Error"), - _("Format (%s) is incorrect") % format) - return - self.dataModel.set_format(format) - - def get_value(self): - """Return modified Dataframe -- this is *not* a copy""" - # It is import to avoid accessing Qt C++ object as it has probably - # already been destroyed, due to the Qt.WA_DeleteOnClose attribute - df = self.dataModel.get_data() - if self.is_series: - return df.iloc[:, 0] - else: - return df - - def resize_to_contents(self): - QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - self.dataTable.resizeColumnsToContents() - self.dataModel.fetch_more(columns=True) - self.dataTable.resizeColumnsToContents() - QApplication.restoreOverrideCursor() - - -def test_edit(data, title="", parent=None): - """Test subroutine""" - dlg = DataFrameEditor(parent=parent) - if dlg.setup_and_check(data, title=title) and dlg.exec_(): - return dlg.get_value() - else: - import sys - sys.exit() - - -def test(): - """DataFrame editor test""" - from numpy import nan - - df1 = DataFrame([ - [True, "bool"], - [1+1j, "complex"], - ['test', "string"], - [1.11, "float"], - [1, "int"], - [np.random.rand(3, 3), "Unkown type"], - ["Large value", 100], - ["áéí", "unicode"] - ], - index=['a', 'b', nan, nan, nan, 'c', - "Test global max", 'd'], - columns=[nan, 'Type']) - out = test_edit(df1) - print("out:", out) - out = test_edit(df1.iloc[0]) - print("out:", out) - df1 = DataFrame(np.random.rand(100001, 10)) - # Sorting large DataFrame takes time - df1.sort(columns=[0, 1], inplace=True) - out = test_edit(df1) - print("out:", out) - out = test_edit(Series(np.arange(10))) - print("out:", out) - return out - - -if __name__ == '__main__': - _app = qapplication() - df = test() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/dependencies.py spyder-3.0.2+dfsg1/spyderlib/widgets/dependencies.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/dependencies.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/dependencies.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2013 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Module checking Spyder optional runtime dependencies""" - -from spyderlib.qt.QtGui import (QDialog, QTableView, QItemDelegate, QColor, - QVBoxLayout, QHBoxLayout, QPushButton, - QApplication, QLabel, QDialogButtonBox) -from spyderlib.qt.QtCore import (Qt, QModelIndex, QAbstractTableModel, SIGNAL, - SLOT) -from spyderlib.qt.compat import to_qvariant -import sys - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.utils.qthelpers import get_icon -from spyderlib import __version__ - - -class DependenciesTableModel(QAbstractTableModel): - def __init__(self, parent, dependencies): - QAbstractTableModel.__init__(self, parent) - self.dependencies = None - self.set_data(dependencies) - - def set_data(self, dependencies): - """Set model data""" - self.dependencies = dependencies - self.reset() - - def rowCount(self, qindex=QModelIndex()): - """Array row number""" - return len(self.dependencies) - - def columnCount(self, qindex=QModelIndex()): - """Array column count""" - return 4 - - def sort(self, column, order=Qt.DescendingOrder): - """Overriding sort method""" - if column == 0: - self.dependencies.sort(key=lambda dep: getattr(dep, 'modname')) - elif column == 1: - pass - elif column == 2: - pass - elif column == 3: - pass - self.reset() - - def headerData(self, section, orientation, role=Qt.DisplayRole): - """Overriding method headerData""" - if role != Qt.DisplayRole: - return to_qvariant() - i_column = int(section) - if orientation == Qt.Horizontal: - headers = (_("Module"), _(" Required "), - _(" Installed "), _("Provided features")) - return to_qvariant( headers[i_column] ) - else: - return to_qvariant() - - def get_value(self, index): - """Return current value""" - dep = self.dependencies[index.row()] - return (dep.modname, dep.required_version, - dep.get_installed_version(), dep.features)[index.column()] - - def data(self, index, role=Qt.DisplayRole): - """Return data at table index""" - if not index.isValid(): - return to_qvariant() - dep = self.dependencies[index.row()] - if role == Qt.DisplayRole: - if index.column() == 0: - value = self.get_value(index) - return to_qvariant(value) - else: - value = self.get_value(index) - return to_qvariant(value) - elif role == Qt.TextAlignmentRole: - return to_qvariant(int(Qt.AlignLeft|Qt.AlignVCenter)) - elif role == Qt.BackgroundColorRole: - from spyderlib.dependencies import Dependency - status = dep.get_status() - if status == Dependency.NOK: - color = QColor(Qt.red) - color.setAlphaF(.25) - return to_qvariant(color) - - -class DependenciesDelegate(QItemDelegate): - def __init__(self, parent=None): - QItemDelegate.__init__(self, parent) - - -class DependenciesTableView(QTableView): - def __init__(self, parent, data): - QTableView.__init__(self, parent) - self.model = DependenciesTableModel(self, data) - self.setModel(self.model) - self.delegate = DependenciesDelegate(self) - self.setItemDelegate(self.delegate) - self.setup_table() - - def setup_table(self): - """Setup table""" - self.horizontalHeader().setStretchLastSection(True) - self.adjust_columns() - self.columnAt(0) - # Sorting columns - self.setSortingEnabled(False) - self.sortByColumn(0, Qt.DescendingOrder) - - def adjust_columns(self): - """Resize three first columns to contents""" - for col in range(3): - self.resizeColumnToContents(col) - -class DependenciesDialog(QDialog): - def __init__(self, parent): - QDialog.__init__(self, parent) - self.setWindowTitle("Spyder %s: %s" % (__version__, - _("Optional Dependencies"))) - self.setWindowIcon(get_icon('advanced.png')) - self.setModal(True) - - self.view = DependenciesTableView(self, []) - - important_mods = ['rope', 'pyflakes', 'IPython', 'matplotlib'] - self.label = QLabel(_("Spyder depends on several Python modules to " - "provide additional functionality for its " - "plugins. The table below shows the required " - "and installed versions (if any) of all of " - "them.

    " - "Although Spyder can work without any of these " - "modules, it's strongly recommended that at " - "least you try to install %s and " - "%s to have a much better experience.") - % (', '.join(important_mods[:-1]), - important_mods[-1])) - self.label.setWordWrap(True) - self.label.setAlignment(Qt.AlignJustify) - self.label.setContentsMargins(5, 8, 12, 10) - - btn = QPushButton(_("Copy to clipboard"), ) - self.connect(btn, SIGNAL('clicked()'), self.copy_to_clipboard) - bbox = QDialogButtonBox(QDialogButtonBox.Ok) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - hlayout = QHBoxLayout() - hlayout.addWidget(btn) - hlayout.addStretch() - hlayout.addWidget(bbox) - - vlayout = QVBoxLayout() - vlayout.addWidget(self.label) - vlayout.addWidget(self.view) - vlayout.addLayout(hlayout) - - self.setLayout(vlayout) - self.resize(630, 420) - - def set_data(self, dependencies): - self.view.model.set_data(dependencies) - self.view.adjust_columns() - self.view.sortByColumn(0, Qt.DescendingOrder) - - def copy_to_clipboard(self): - from spyderlib.dependencies import status - QApplication.clipboard().setText(status()) - - -def test(): - """Run dependency widget test""" - from spyderlib import dependencies - - # Test sample - dependencies.add("IPython", "Enhanced Python interpreter", ">=0.13") - dependencies.add("matplotlib", "Interactive data plotting", ">=1.0") - dependencies.add("sympy", "Symbolic Mathematics", ">=10.0") - dependencies.add("foo", "Non-existent module", ">=1.0") - - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - dlg = DependenciesDialog(None) - dlg.set_data(dependencies.DEPENDENCIES) - dlg.show() - sys.exit(dlg.exec_()) - - -if __name__ == '__main__': - test() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/dicteditor.py spyder-3.0.2+dfsg1/spyderlib/widgets/dicteditor.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/dicteditor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/dicteditor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1460 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Dictionary Editor Widget and Dialog based on Qt -""" - -#TODO: Multiple selection: open as many editors (array/dict/...) as necessary, -# at the same time - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from __future__ import print_function -from spyderlib.qt.QtGui import (QMessageBox, QTableView, QItemDelegate, - QLineEdit, QVBoxLayout, QWidget, QColor, - QDialog, QDateEdit, QDialogButtonBox, QMenu, - QInputDialog, QDateTimeEdit, QApplication, - QKeySequence) -from spyderlib.qt.QtCore import (Qt, QModelIndex, QAbstractTableModel, SIGNAL, - SLOT, QDateTime, Signal) -from spyderlib.qt.compat import to_qvariant, from_qvariant, getsavefilename -from spyderlib.utils.qthelpers import mimedata2url - -import sys -import datetime - -# Local import -from spyderlib.baseconfig import _ -from spyderlib.guiconfig import get_font -from spyderlib.utils.misc import fix_reference_name -from spyderlib.utils.qthelpers import (get_icon, add_actions, create_action, - qapplication) -from spyderlib.widgets.dicteditorutils import (sort_against, get_size, - get_human_readable_type, value_to_display, get_color_name, - is_known_type, FakeObject, Image, ndarray, array, MaskedArray, - unsorted_unique, try_to_eval, datestr_to_datetime, - get_numpy_dtype, is_editable_type, DataFrame, Series) -if ndarray is not FakeObject: - from spyderlib.widgets.arrayeditor import ArrayEditor -if DataFrame is not FakeObject: - from spyderlib.widgets.dataframeeditor import DataFrameEditor -from spyderlib.widgets.texteditor import TextEditor -from spyderlib.widgets.importwizard import ImportWizard -from spyderlib.py3compat import (to_text_string, to_binary_string, - is_text_string, is_binary_string, getcwd, u) - - -LARGE_NROWS = 100 - - -def display_to_value(value, default_value, ignore_errors=True): - """Convert back to value""" - value = from_qvariant(value, to_text_string) - try: - np_dtype = get_numpy_dtype(default_value) - if isinstance(default_value, bool): - # We must test for boolean before NumPy data types - # because `bool` class derives from `int` class - try: - value = bool(float(value)) - except ValueError: - value = value.lower() == "true" - elif np_dtype is not None: - if 'complex' in str(type(default_value)): - value = np_dtype(complex(value)) - else: - value = np_dtype(value) - elif is_binary_string(default_value): - value = to_binary_string(value, 'utf8') - elif is_text_string(default_value): - value = to_text_string(value) - elif isinstance(default_value, complex): - value = complex(value) - elif isinstance(default_value, float): - value = float(value) - elif isinstance(default_value, int): - try: - value = int(value) - except ValueError: - value = float(value) - elif isinstance(default_value, datetime.datetime): - value = datestr_to_datetime(value) - elif isinstance(default_value, datetime.date): - value = datestr_to_datetime(value).date() - elif ignore_errors: - value = try_to_eval(value) - else: - value = eval(value) - except (ValueError, SyntaxError): - if ignore_errors: - value = try_to_eval(value) - else: - return default_value - return value - - -class ProxyObject(object): - """Dictionary proxy to an unknown object""" - def __init__(self, obj): - self.__obj__ = obj - - def __len__(self): - return len(dir(self.__obj__)) - - def __getitem__(self, key): - return getattr(self.__obj__, key) - - def __setitem__(self, key, value): - setattr(self.__obj__, key, value) - - -class ReadOnlyDictModel(QAbstractTableModel): - """DictEditor Read-Only Table Model""" - ROWS_TO_LOAD = 50 - - def __init__(self, parent, data, title="", names=False, truncate=True, - minmax=False, remote=False): - QAbstractTableModel.__init__(self, parent) - if data is None: - data = {} - self.names = names - self.truncate = truncate - self.minmax = minmax - self.remote = remote - self.header0 = None - self._data = None - self.total_rows = None - self.showndata = None - self.keys = None - self.title = to_text_string(title) # in case title is not a string - if self.title: - self.title = self.title + ' - ' - self.sizes = [] - self.types = [] - self.set_data(data) - - def get_data(self): - """Return model data""" - return self._data - - def set_data(self, data, dictfilter=None): - """Set model data""" - self._data = data - - if dictfilter is not None and not self.remote and \ - isinstance(data, (tuple, list, dict)): - data = dictfilter(data) - self.showndata = data - - self.header0 = _("Index") - if self.names: - self.header0 = _("Name") - if isinstance(data, tuple): - self.keys = list(range(len(data))) - self.title += _("Tuple") - elif isinstance(data, list): - self.keys = list(range(len(data))) - self.title += _("List") - elif isinstance(data, dict): - self.keys = list(data.keys()) - self.title += _("Dictionary") - if not self.names: - self.header0 = _("Key") - else: - self.keys = dir(data) - self._data = data = self.showndata = ProxyObject(data) - self.title += _("Object") - if not self.names: - self.header0 = _("Attribute") - - self.title += ' ('+str(len(self.keys))+' '+ _("elements")+')' - - self.total_rows = len(self.keys) - if self.total_rows > LARGE_NROWS: - self.rows_loaded = self.ROWS_TO_LOAD - else: - self.rows_loaded = self.total_rows - - self.set_size_and_type() - self.reset() - - def set_size_and_type(self, start=None, stop=None): - data = self._data - - if start is None and stop is None: - start = 0 - stop = self.rows_loaded - fetch_more = False - else: - fetch_more = True - - if self.remote: - sizes = [ data[self.keys[index]]['size'] - for index in range(start, stop) ] - types = [ data[self.keys[index]]['type'] - for index in range(start, stop) ] - else: - sizes = [ get_size(data[self.keys[index]]) - for index in range(start, stop) ] - types = [ get_human_readable_type(data[self.keys[index]]) - for index in range(start, stop) ] - - if fetch_more: - self.sizes = self.sizes + sizes - self.types = self.types + types - else: - self.sizes = sizes - self.types = types - - def sort(self, column, order=Qt.AscendingOrder): - """Overriding sort method""" - reverse = (order==Qt.DescendingOrder) - if column == 0: - self.sizes = sort_against(self.sizes, self.keys, reverse) - self.types = sort_against(self.types, self.keys, reverse) - try: - self.keys.sort(reverse=reverse) - except: - pass - elif column == 1: - self.keys = sort_against(self.keys, self.types, reverse) - self.sizes = sort_against(self.sizes, self.types, reverse) - try: - self.types.sort(reverse=reverse) - except: - pass - elif column == 2: - self.keys = sort_against(self.keys, self.sizes, reverse) - self.types = sort_against(self.types, self.sizes, reverse) - try: - self.sizes.sort(reverse=reverse) - except: - pass - elif column == 3: - self.keys = sort_against(self.keys, self.sizes, reverse) - self.types = sort_against(self.types, self.sizes, reverse) - try: - self.sizes.sort(reverse=reverse) - except: - pass - elif column == 4: - values = [self._data[key] for key in self.keys] - self.keys = sort_against(self.keys, values, reverse) - self.sizes = sort_against(self.sizes, values, reverse) - self.types = sort_against(self.types, values, reverse) - self.reset() - - def columnCount(self, qindex=QModelIndex()): - """Array column number""" - return 4 - - def rowCount(self, index=QModelIndex()): - """Array row number""" - if self.total_rows <= self.rows_loaded: - return self.total_rows - else: - return self.rows_loaded - - def canFetchMore(self, index=QModelIndex()): - if self.total_rows > self.rows_loaded: - return True - else: - return False - - def fetchMore(self, index=QModelIndex()): - reminder = self.total_rows - self.rows_loaded - items_to_fetch = min(reminder, self.ROWS_TO_LOAD) - self.set_size_and_type(self.rows_loaded, - self.rows_loaded + items_to_fetch) - self.beginInsertRows(QModelIndex(), self.rows_loaded, - self.rows_loaded + items_to_fetch - 1) - self.rows_loaded += items_to_fetch - self.endInsertRows() - - def get_index_from_key(self, key): - try: - return self.createIndex(self.keys.index(key), 0) - except ValueError: - return QModelIndex() - - def get_key(self, index): - """Return current key""" - return self.keys[index.row()] - - def get_value(self, index): - """Return current value""" - if index.column() == 0: - return self.keys[ index.row() ] - elif index.column() == 1: - return self.types[ index.row() ] - elif index.column() == 2: - return self.sizes[ index.row() ] - else: - return self._data[ self.keys[index.row()] ] - - def get_bgcolor(self, index): - """Background color depending on value""" - if index.column() == 0: - color = QColor(Qt.lightGray) - color.setAlphaF(.05) - elif index.column() < 3: - color = QColor(Qt.lightGray) - color.setAlphaF(.2) - else: - color = QColor(Qt.lightGray) - color.setAlphaF(.3) - return color - - def data(self, index, role=Qt.DisplayRole): - """Cell content""" - if not index.isValid(): - return to_qvariant() - value = self.get_value(index) - if index.column() == 3 and self.remote: - value = value['view'] - display = value_to_display(value, - truncate=index.column() == 3 and self.truncate, - minmax=self.minmax) - if role == Qt.DisplayRole: - return to_qvariant(display) - elif role == Qt.EditRole: - return to_qvariant(value_to_display(value)) - elif role == Qt.TextAlignmentRole: - if index.column() == 3: - if len(display.splitlines()) < 3: - return to_qvariant(int(Qt.AlignLeft|Qt.AlignVCenter)) - else: - return to_qvariant(int(Qt.AlignLeft|Qt.AlignTop)) - else: - return to_qvariant(int(Qt.AlignLeft|Qt.AlignVCenter)) - elif role == Qt.BackgroundColorRole: - return to_qvariant( self.get_bgcolor(index) ) - elif role == Qt.FontRole: - if index.column() < 3: - return to_qvariant(get_font('dicteditor_header')) - else: - return to_qvariant(get_font('dicteditor')) - return to_qvariant() - - def headerData(self, section, orientation, role=Qt.DisplayRole): - """Overriding method headerData""" - if role != Qt.DisplayRole: - if role == Qt.FontRole: - return to_qvariant(get_font('dicteditor_header')) - else: - return to_qvariant() - i_column = int(section) - if orientation == Qt.Horizontal: - headers = (self.header0, _("Type"), _("Size"), _("Value")) - return to_qvariant( headers[i_column] ) - else: - return to_qvariant() - - def flags(self, index): - """Overriding method flags""" - # This method was implemented in DictModel only, but to enable tuple - # exploration (even without editing), this method was moved here - if not index.isValid(): - return Qt.ItemIsEnabled - return Qt.ItemFlags(QAbstractTableModel.flags(self, index)| - Qt.ItemIsEditable) - -class DictModel(ReadOnlyDictModel): - """DictEditor Table Model""" - - def set_value(self, index, value): - """Set value""" - self._data[ self.keys[index.row()] ] = value - self.showndata[ self.keys[index.row()] ] = value - self.sizes[index.row()] = get_size(value) - self.types[index.row()] = get_human_readable_type(value) - - def get_bgcolor(self, index): - """Background color depending on value""" - value = self.get_value(index) - if index.column() < 3: - color = ReadOnlyDictModel.get_bgcolor(self, index) - else: - if self.remote: - color_name = value['color'] - else: - color_name = get_color_name(value) - color = QColor(color_name) - color.setAlphaF(.2) - return color - - def setData(self, index, value, role=Qt.EditRole): - """Cell content change""" - if not index.isValid(): - return False - if index.column() < 3: - return False - value = display_to_value(value, self.get_value(index), - ignore_errors=True) - self.set_value(index, value) - self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), - index, index) - return True - - -class DictDelegate(QItemDelegate): - """DictEditor Item Delegate""" - - def __init__(self, parent=None): - QItemDelegate.__init__(self, parent) - self._editors = {} # keep references on opened editors - - def get_value(self, index): - if index.isValid(): - return index.model().get_value(index) - - def set_value(self, index, value): - if index.isValid(): - index.model().set_value(index, value) - - def show_warning(self, index): - """ - Decide if showing a warning when the user is trying to view - a big variable associated to a Tablemodel index - - This avoids getting the variables' value to know its - size and type, using instead those already computed by - the TableModel. - - The problem is when a variable is too big, it can take a - lot of time just to get its value - """ - try: - val_size = index.model().sizes[index.row()] - val_type = index.model().types[index.row()] - except: - return False - if val_type in ['list', 'tuple', 'dict'] and int(val_size) > 1e5: - return True - else: - return False - - def createEditor(self, parent, option, index): - """Overriding method createEditor""" - if index.column() < 3: - return None - if self.show_warning(index): - answer = QMessageBox.warning(self.parent(), _("Warning"), - _("Opening this variable can be slow\n\n" - "Do you want to continue anyway?"), - QMessageBox.Yes | QMessageBox.No) - if answer == QMessageBox.No: - return None - try: - value = self.get_value(index) - except Exception as msg: - QMessageBox.critical(self.parent(), _("Edit item"), - _("Unable to retrieve data." - "

    Error message:
    %s" - ) % to_text_string(msg)) - return - key = index.model().get_key(index) - readonly = isinstance(value, tuple) or self.parent().readonly \ - or not is_known_type(value) - #---editor = DictEditor - if isinstance(value, (list, tuple, dict)): - editor = DictEditor() - editor.setup(value, key, icon=self.parent().windowIcon(), - readonly=readonly) - self.create_dialog(editor, dict(model=index.model(), editor=editor, - key=key, readonly=readonly)) - return None - #---editor = ArrayEditor - elif isinstance(value, (ndarray, MaskedArray)) \ - and ndarray is not FakeObject: - if value.size == 0: - return None - editor = ArrayEditor(parent) - if not editor.setup_and_check(value, title=key, readonly=readonly): - return - self.create_dialog(editor, dict(model=index.model(), editor=editor, - key=key, readonly=readonly)) - return None - #---showing image - elif isinstance(value, Image) and ndarray is not FakeObject \ - and Image is not FakeObject: - arr = array(value) - if arr.size == 0: - return None - editor = ArrayEditor(parent) - if not editor.setup_and_check(arr, title=key, readonly=readonly): - return - conv_func = lambda arr: Image.fromarray(arr, mode=value.mode) - self.create_dialog(editor, dict(model=index.model(), editor=editor, - key=key, readonly=readonly, - conv=conv_func)) - return None - #--editor = DataFrameEditor - elif isinstance(value, (DataFrame, Series)) \ - and DataFrame is not FakeObject: - editor = DataFrameEditor() - if not editor.setup_and_check(value, title=key): - return - self.create_dialog(editor, dict(model=index.model(), editor=editor, - key=key, readonly=readonly)) - return None - - #---editor = QDateTimeEdit - elif isinstance(value, datetime.datetime): - editor = QDateTimeEdit(value, parent) - editor.setCalendarPopup(True) - editor.setFont(get_font('dicteditor')) - self.connect(editor, SIGNAL("returnPressed()"), - self.commitAndCloseEditor) - return editor - #---editor = QDateEdit - elif isinstance(value, datetime.date): - editor = QDateEdit(value, parent) - editor.setCalendarPopup(True) - editor.setFont(get_font('dicteditor')) - self.connect(editor, SIGNAL("returnPressed()"), - self.commitAndCloseEditor) - return editor - #---editor = QTextEdit - elif is_text_string(value) and len(value)>40: - editor = TextEditor(value, key) - self.create_dialog(editor, dict(model=index.model(), editor=editor, - key=key, readonly=readonly)) - return None - #---editor = QLineEdit - elif is_editable_type(value): - editor = QLineEdit(parent) - editor.setFont(get_font('dicteditor')) - editor.setAlignment(Qt.AlignLeft) - self.connect(editor, SIGNAL("returnPressed()"), - self.commitAndCloseEditor) - return editor - #---editor = DictEditor for an arbitrary object - else: - editor = DictEditor() - editor.setup(value, key, icon=self.parent().windowIcon(), - readonly=readonly) - self.create_dialog(editor, dict(model=index.model(), editor=editor, - key=key, readonly=readonly)) - return None - - def create_dialog(self, editor, data): - self._editors[id(editor)] = data - self.connect(editor, SIGNAL('accepted()'), - lambda eid=id(editor): self.editor_accepted(eid)) - self.connect(editor, SIGNAL('rejected()'), - lambda eid=id(editor): self.editor_rejected(eid)) - editor.show() - - def editor_accepted(self, editor_id): - data = self._editors[editor_id] - if not data['readonly']: - index = data['model'].get_index_from_key(data['key']) - value = data['editor'].get_value() - conv_func = data.get('conv', lambda v: v) - self.set_value(index, conv_func(value)) - self._editors.pop(editor_id) - - def editor_rejected(self, editor_id): - self._editors.pop(editor_id) - - def commitAndCloseEditor(self): - """Overriding method commitAndCloseEditor""" - editor = self.sender() - self.emit(SIGNAL("commitData(QWidget*)"), editor) - self.emit(SIGNAL("closeEditor(QWidget*)"), editor) - - def setEditorData(self, editor, index): - """Overriding method setEditorData - Model --> Editor""" - value = self.get_value(index) - if isinstance(editor, QLineEdit): - if is_binary_string(value): - try: - value = to_text_string(value, 'utf8') - except: - pass - if not is_text_string(value): - value = repr(value) - editor.setText(value) - elif isinstance(editor, QDateEdit): - editor.setDate(value) - elif isinstance(editor, QDateTimeEdit): - editor.setDateTime(QDateTime(value.date(), value.time())) - - def setModelData(self, editor, model, index): - """Overriding method setModelData - Editor --> Model""" - if not hasattr(model, "set_value"): - # Read-only mode - return - - if isinstance(editor, QLineEdit): - value = editor.text() - try: - value = display_to_value(to_qvariant(value), - self.get_value(index), - ignore_errors=False) - except Exception as msg: - raise - QMessageBox.critical(editor, _("Edit item"), - _("Unable to assign data to item." - "

    Error message:
    %s" - ) % str(msg)) - return - elif isinstance(editor, QDateEdit): - qdate = editor.date() - value = datetime.date( qdate.year(), qdate.month(), qdate.day() ) - elif isinstance(editor, QDateTimeEdit): - qdatetime = editor.dateTime() - qdate = qdatetime.date() - qtime = qdatetime.time() - value = datetime.datetime( qdate.year(), qdate.month(), - qdate.day(), qtime.hour(), - qtime.minute(), qtime.second() ) - else: - # Should not happen... - raise RuntimeError("Unsupported editor widget") - self.set_value(index, value) - - -class BaseTableView(QTableView): - """Base dictionary editor table view""" - sig_option_changed = Signal(str, object) - sig_files_dropped = Signal(list) - - def __init__(self, parent): - QTableView.__init__(self, parent) - self.array_filename = None - self.menu = None - self.empty_ws_menu = None - self.paste_action = None - self.copy_action = None - self.edit_action = None - self.plot_action = None - self.hist_action = None - self.imshow_action = None - self.save_array_action = None - self.insert_action = None - self.remove_action = None - self.truncate_action = None - self.minmax_action = None - self.rename_action = None - self.duplicate_action = None - self.delegate = None - self.setAcceptDrops(True) - - def setup_table(self): - """Setup table""" - self.horizontalHeader().setStretchLastSection(True) - self.adjust_columns() - # Sorting columns - self.setSortingEnabled(True) - self.sortByColumn(0, Qt.AscendingOrder) - - def setup_menu(self, truncate, minmax): - """Setup context menu""" - if self.truncate_action is not None: - self.truncate_action.setChecked(truncate) - self.minmax_action.setChecked(minmax) - return - - resize_action = create_action(self, _("Resize rows to contents"), - triggered=self.resizeRowsToContents) - self.paste_action = create_action(self, _("Paste"), - icon=get_icon('editpaste.png'), - triggered=self.paste) - self.copy_action = create_action(self, _("Copy"), - icon=get_icon('editcopy.png'), - triggered=self.copy) - self.edit_action = create_action(self, _("Edit"), - icon=get_icon('edit.png'), - triggered=self.edit_item) - self.plot_action = create_action(self, _("Plot"), - icon=get_icon('plot.png'), - triggered=lambda: self.plot_item('plot')) - self.plot_action.setVisible(False) - self.hist_action = create_action(self, _("Histogram"), - icon=get_icon('hist.png'), - triggered=lambda: self.plot_item('hist')) - self.hist_action.setVisible(False) - self.imshow_action = create_action(self, _("Show image"), - icon=get_icon('imshow.png'), - triggered=self.imshow_item) - self.imshow_action.setVisible(False) - self.save_array_action = create_action(self, _("Save array"), - icon=get_icon('filesave.png'), - triggered=self.save_array) - self.save_array_action.setVisible(False) - self.insert_action = create_action(self, _("Insert"), - icon=get_icon('insert.png'), - triggered=self.insert_item) - self.remove_action = create_action(self, _("Remove"), - icon=get_icon('editdelete.png'), - triggered=self.remove_item) - self.truncate_action = create_action(self, _("Truncate values"), - toggled=self.toggle_truncate) - self.truncate_action.setChecked(truncate) - self.toggle_truncate(truncate) - self.minmax_action = create_action(self, _("Show arrays min/max"), - toggled=self.toggle_minmax) - self.minmax_action.setChecked(minmax) - self.toggle_minmax(minmax) - self.rename_action = create_action(self, _( "Rename"), - icon=get_icon('rename.png'), - triggered=self.rename_item) - self.duplicate_action = create_action(self, _( "Duplicate"), - icon=get_icon('edit_add.png'), - triggered=self.duplicate_item) - menu = QMenu(self) - menu_actions = [self.edit_action, self.plot_action, self.hist_action, - self.imshow_action, self.save_array_action, - self.insert_action, self.remove_action, - self.copy_action, self.paste_action, - None, self.rename_action, self.duplicate_action, - None, resize_action, None, self.truncate_action] - if ndarray is not FakeObject: - menu_actions.append(self.minmax_action) - add_actions(menu, menu_actions) - self.empty_ws_menu = QMenu(self) - add_actions(self.empty_ws_menu, - [self.insert_action, self.paste_action, - None, resize_action]) - return menu - - #------ Remote/local API --------------------------------------------------- - def remove_values(self, keys): - """Remove values from data""" - raise NotImplementedError - - def copy_value(self, orig_key, new_key): - """Copy value""" - raise NotImplementedError - - def new_value(self, key, value): - """Create new value in data""" - raise NotImplementedError - - def is_list(self, key): - """Return True if variable is a list or a tuple""" - raise NotImplementedError - - def get_len(self, key): - """Return sequence length""" - raise NotImplementedError - - def is_array(self, key): - """Return True if variable is a numpy array""" - raise NotImplementedError - - def is_image(self, key): - """Return True if variable is a PIL.Image image""" - raise NotImplementedError - - def is_dict(self, key): - """Return True if variable is a dictionary""" - raise NotImplementedError - - def get_array_shape(self, key): - """Return array's shape""" - raise NotImplementedError - - def get_array_ndim(self, key): - """Return array's ndim""" - raise NotImplementedError - - def oedit(self, key): - """Edit item""" - raise NotImplementedError - - def plot(self, key, funcname): - """Plot item""" - raise NotImplementedError - - def imshow(self, key): - """Show item's image""" - raise NotImplementedError - - def show_image(self, key): - """Show image (item is a PIL image)""" - raise NotImplementedError - #--------------------------------------------------------------------------- - - def refresh_menu(self): - """Refresh context menu""" - index = self.currentIndex() - condition = index.isValid() - self.edit_action.setEnabled( condition ) - self.remove_action.setEnabled( condition ) - self.refresh_plot_entries(index) - - def refresh_plot_entries(self, index): - if index.isValid(): - key = self.model.get_key(index) - is_list = self.is_list(key) - is_array = self.is_array(key) and self.get_len(key) != 0 - condition_plot = (is_array and len(self.get_array_shape(key)) <= 2) - condition_hist = (is_array and self.get_array_ndim(key) == 1) - condition_imshow = condition_plot and self.get_array_ndim(key) == 2 - condition_imshow = condition_imshow or self.is_image(key) - else: - is_array = condition_plot = condition_imshow = is_list \ - = condition_hist = False - self.plot_action.setVisible(condition_plot or is_list) - self.hist_action.setVisible(condition_hist or is_list) - self.imshow_action.setVisible(condition_imshow) - self.save_array_action.setVisible(is_array) - - def adjust_columns(self): - """Resize two first columns to contents""" - for col in range(3): - self.resizeColumnToContents(col) - - def set_data(self, data): - """Set table data""" - if data is not None: - self.model.set_data(data, self.dictfilter) - self.sortByColumn(0, Qt.AscendingOrder) - - def mousePressEvent(self, event): - """Reimplement Qt method""" - if event.button() != Qt.LeftButton: - QTableView.mousePressEvent(self, event) - return - index_clicked = self.indexAt(event.pos()) - if index_clicked.isValid(): - if index_clicked == self.currentIndex() \ - and index_clicked in self.selectedIndexes(): - self.clearSelection() - else: - QTableView.mousePressEvent(self, event) - else: - self.clearSelection() - event.accept() - - def mouseDoubleClickEvent(self, event): - """Reimplement Qt method""" - index_clicked = self.indexAt(event.pos()) - if index_clicked.isValid(): - row = index_clicked.row() - # TODO: Remove hard coded "Value" column number (3 here) - index_clicked = index_clicked.child(row, 3) - self.edit(index_clicked) - else: - event.accept() - - def keyPressEvent(self, event): - """Reimplement Qt methods""" - if event.key() == Qt.Key_Delete: - self.remove_item() - elif event.key() == Qt.Key_F2: - self.rename_item() - elif event == QKeySequence.Copy: - self.copy() - elif event == QKeySequence.Paste: - self.paste() - else: - QTableView.keyPressEvent(self, event) - - def contextMenuEvent(self, event): - """Reimplement Qt method""" - if self.model.showndata: - self.refresh_menu() - self.menu.popup(event.globalPos()) - event.accept() - else: - self.empty_ws_menu.popup(event.globalPos()) - event.accept() - - def dragEnterEvent(self, event): - """Allow user to drag files""" - if mimedata2url(event.mimeData()): - event.accept() - else: - event.ignore() - - def dragMoveEvent(self, event): - """Allow user to move files""" - if mimedata2url(event.mimeData()): - event.setDropAction(Qt.CopyAction) - event.accept() - else: - event.ignore() - - def dropEvent(self, event): - """Allow user to drop supported files""" - urls = mimedata2url(event.mimeData()) - if urls: - event.setDropAction(Qt.CopyAction) - event.accept() - self.sig_files_dropped.emit(urls) - else: - event.ignore() - - def toggle_truncate(self, state): - """Toggle display truncating option""" - self.sig_option_changed.emit('truncate', state) - self.model.truncate = state - - def toggle_minmax(self, state): - """Toggle min/max display for numpy arrays""" - self.sig_option_changed.emit('minmax', state) - self.model.minmax = state - - def edit_item(self): - """Edit item""" - index = self.currentIndex() - if not index.isValid(): - return - # TODO: Remove hard coded "Value" column number (3 here) - self.edit(index.child(index.row(), 3)) - - def remove_item(self): - """Remove item""" - indexes = self.selectedIndexes() - if not indexes: - return - for index in indexes: - if not index.isValid(): - return - one = _("Do you want to remove selected item?") - more = _("Do you want to remove all selected items?") - answer = QMessageBox.question(self, _( "Remove"), - one if len(indexes) == 1 else more, - QMessageBox.Yes | QMessageBox.No) - if answer == QMessageBox.Yes: - idx_rows = unsorted_unique([idx.row() for idx in indexes]) - keys = [ self.model.keys[idx_row] for idx_row in idx_rows ] - self.remove_values(keys) - - def copy_item(self, erase_original=False): - """Copy item""" - indexes = self.selectedIndexes() - if not indexes: - return - idx_rows = unsorted_unique([idx.row() for idx in indexes]) - if len(idx_rows) > 1 or not indexes[0].isValid(): - return - orig_key = self.model.keys[idx_rows[0]] - new_key, valid = QInputDialog.getText(self, _( 'Rename'), _( 'Key:'), - QLineEdit.Normal, orig_key) - if valid and to_text_string(new_key): - new_key = try_to_eval(to_text_string(new_key)) - if new_key == orig_key: - return - self.copy_value(orig_key, new_key) - if erase_original: - self.remove_values([orig_key]) - - def duplicate_item(self): - """Duplicate item""" - self.copy_item() - - def rename_item(self): - """Rename item""" - self.copy_item(True) - - def insert_item(self): - """Insert item""" - index = self.currentIndex() - if not index.isValid(): - row = self.model.rowCount() - else: - row = index.row() - data = self.model.get_data() - if isinstance(data, list): - key = row - data.insert(row, '') - elif isinstance(data, dict): - key, valid = QInputDialog.getText(self, _( 'Insert'), _( 'Key:'), - QLineEdit.Normal) - if valid and to_text_string(key): - key = try_to_eval(to_text_string(key)) - else: - return - else: - return - value, valid = QInputDialog.getText(self, _('Insert'), _('Value:'), - QLineEdit.Normal) - if valid and to_text_string(value): - self.new_value(key, try_to_eval(to_text_string(value))) - - def __prepare_plot(self): - try: - import guiqwt.pyplot #analysis:ignore - return True - except ImportError: - try: - if 'matplotlib' not in sys.modules: - import matplotlib - matplotlib.use("Qt4Agg") - return True - except ImportError: - QMessageBox.warning(self, _("Import error"), - _("Please install matplotlib" - " or guiqwt.")) - - def plot_item(self, funcname): - """Plot item""" - index = self.currentIndex() - if self.__prepare_plot(): - key = self.model.get_key(index) - try: - self.plot(key, funcname) - except (ValueError, TypeError) as error: - QMessageBox.critical(self, _( "Plot"), - _("Unable to plot data." - "

    Error message:
    %s" - ) % str(error)) - - def imshow_item(self): - """Imshow item""" - index = self.currentIndex() - if self.__prepare_plot(): - key = self.model.get_key(index) - try: - if self.is_image(key): - self.show_image(key) - else: - self.imshow(key) - except (ValueError, TypeError) as error: - QMessageBox.critical(self, _( "Plot"), - _("Unable to show image." - "

    Error message:
    %s" - ) % str(error)) - - def save_array(self): - """Save array""" - title = _( "Save array") - if self.array_filename is None: - self.array_filename = getcwd() - self.emit(SIGNAL('redirect_stdio(bool)'), False) - filename, _selfilter = getsavefilename(self, title, - self.array_filename, - _("NumPy arrays")+" (*.npy)") - self.emit(SIGNAL('redirect_stdio(bool)'), True) - if filename: - self.array_filename = filename - data = self.delegate.get_value( self.currentIndex() ) - try: - import numpy as np - np.save(self.array_filename, data) - except Exception as error: - QMessageBox.critical(self, title, - _("Unable to save array" - "

    Error message:
    %s" - ) % str(error)) - def copy(self): - """Copy text to clipboard""" - clipboard = QApplication.clipboard() - clipl = [] - for idx in self.selectedIndexes(): - if not idx.isValid(): - continue - clipl.append(to_text_string(self.delegate.get_value(idx))) - clipboard.setText(u('\n').join(clipl)) - - def import_from_string(self, text, title=None): - """Import data from string""" - data = self.model.get_data() - editor = ImportWizard(self, text, title=title, - contents_title=_("Clipboard contents"), - varname=fix_reference_name("data", - blacklist=list(data.keys()))) - if editor.exec_(): - var_name, clip_data = editor.get_data() - self.new_value(var_name, clip_data) - - def paste(self): - """Import text/data/code from clipboard""" - clipboard = QApplication.clipboard() - cliptext = '' - if clipboard.mimeData().hasText(): - cliptext = to_text_string(clipboard.text()) - if cliptext.strip(): - self.import_from_string(cliptext, title=_("Import from clipboard")) - else: - QMessageBox.warning(self, _( "Empty clipboard"), - _("Nothing to be imported from clipboard.")) - - -class DictEditorTableView(BaseTableView): - """DictEditor table view""" - def __init__(self, parent, data, readonly=False, title="", - names=False, truncate=True, minmax=False): - BaseTableView.__init__(self, parent) - self.dictfilter = None - self.readonly = readonly or isinstance(data, tuple) - DictModelClass = ReadOnlyDictModel if self.readonly else DictModel - self.model = DictModelClass(self, data, title, names=names, - truncate=truncate, minmax=minmax) - self.setModel(self.model) - self.delegate = DictDelegate(self) - self.setItemDelegate(self.delegate) - - self.setup_table() - self.menu = self.setup_menu(truncate, minmax) - - #------ Remote/local API --------------------------------------------------- - def remove_values(self, keys): - """Remove values from data""" - data = self.model.get_data() - for key in sorted(keys, reverse=True): - data.pop(key) - self.set_data(data) - - def copy_value(self, orig_key, new_key): - """Copy value""" - data = self.model.get_data() - data[new_key] = data[orig_key] - self.set_data(data) - - def new_value(self, key, value): - """Create new value in data""" - data = self.model.get_data() - data[key] = value - self.set_data(data) - - def is_list(self, key): - """Return True if variable is a list or a tuple""" - data = self.model.get_data() - return isinstance(data[key], (tuple, list)) - - def get_len(self, key): - """Return sequence length""" - data = self.model.get_data() - return len(data[key]) - - def is_array(self, key): - """Return True if variable is a numpy array""" - data = self.model.get_data() - return isinstance(data[key], (ndarray, MaskedArray)) - - def is_image(self, key): - """Return True if variable is a PIL.Image image""" - data = self.model.get_data() - return isinstance(data[key], Image) - - def is_dict(self, key): - """Return True if variable is a dictionary""" - data = self.model.get_data() - return isinstance(data[key], dict) - - def get_array_shape(self, key): - """Return array's shape""" - data = self.model.get_data() - return data[key].shape - - def get_array_ndim(self, key): - """Return array's ndim""" - data = self.model.get_data() - return data[key].ndim - - def oedit(self, key): - """Edit item""" - data = self.model.get_data() - from spyderlib.widgets.objecteditor import oedit - oedit(data[key]) - - def plot(self, key, funcname): - """Plot item""" - data = self.model.get_data() - import spyderlib.pyplot as plt - plt.figure() - getattr(plt, funcname)(data[key]) - plt.show() - - def imshow(self, key): - """Show item's image""" - data = self.model.get_data() - import spyderlib.pyplot as plt - plt.figure() - plt.imshow(data[key]) - plt.show() - - def show_image(self, key): - """Show image (item is a PIL image)""" - data = self.model.get_data() - data[key].show() - #--------------------------------------------------------------------------- - - def refresh_menu(self): - """Refresh context menu""" - data = self.model.get_data() - index = self.currentIndex() - condition = (not isinstance(data, tuple)) and index.isValid() \ - and not self.readonly - self.edit_action.setEnabled( condition ) - self.remove_action.setEnabled( condition ) - self.insert_action.setEnabled( not self.readonly ) - self.refresh_plot_entries(index) - - def set_filter(self, dictfilter=None): - """Set table dict filter""" - self.dictfilter = dictfilter - - -class DictEditorWidget(QWidget): - """Dictionary Editor Dialog""" - def __init__(self, parent, data, readonly=False, title="", remote=False): - QWidget.__init__(self, parent) - if remote: - self.editor = RemoteDictEditorTableView(self, data, readonly) - else: - self.editor = DictEditorTableView(self, data, readonly, title) - layout = QVBoxLayout() - layout.addWidget(self.editor) - self.setLayout(layout) - - def set_data(self, data): - """Set DictEditor data""" - self.editor.set_data(data) - - def get_title(self): - """Get model title""" - return self.editor.model.title - - -class DictEditor(QDialog): - """Dictionary/List Editor Dialog""" - def __init__(self, parent=None): - QDialog.__init__(self, parent) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - self.data_copy = None - self.widget = None - - def setup(self, data, title='', readonly=False, width=500, - icon='dictedit.png', remote=False, parent=None): - if isinstance(data, dict): - # dictionnary - self.data_copy = data.copy() - datalen = len(data) - elif isinstance(data, (tuple, list)): - # list, tuple - self.data_copy = data[:] - datalen = len(data) - else: - # unknown object - import copy - self.data_copy = copy.deepcopy(data) - datalen = len(dir(data)) - self.widget = DictEditorWidget(self, self.data_copy, title=title, - readonly=readonly, remote=remote) - - layout = QVBoxLayout() - layout.addWidget(self.widget) - self.setLayout(layout) - - # Buttons configuration - buttons = QDialogButtonBox.Ok - if not readonly: - buttons = buttons | QDialogButtonBox.Cancel - bbox = QDialogButtonBox(buttons) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - if not readonly: - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - layout.addWidget(bbox) - - constant = 121 - row_height = 30 - error_margin = 20 - height = constant + row_height*min([15, datalen]) + error_margin - self.resize(width, height) - - self.setWindowTitle(self.widget.get_title()) - if is_text_string(icon): - icon = get_icon(icon) - self.setWindowIcon(icon) - # Make the dialog act as a window - self.setWindowFlags(Qt.Window) - - def get_value(self): - """Return modified copy of dictionary or list""" - # It is import to avoid accessing Qt C++ object as it has probably - # already been destroyed, due to the Qt.WA_DeleteOnClose attribute - return self.data_copy - - -#----Remote versions of DictDelegate and DictEditorTableView -class RemoteDictDelegate(DictDelegate): - """DictEditor Item Delegate""" - def __init__(self, parent=None, get_value_func=None, set_value_func=None): - DictDelegate.__init__(self, parent) - self.get_value_func = get_value_func - self.set_value_func = set_value_func - - def get_value(self, index): - if index.isValid(): - name = index.model().keys[index.row()] - return self.get_value_func(name) - - def set_value(self, index, value): - if index.isValid(): - name = index.model().keys[index.row()] - self.set_value_func(name, value) - - -class RemoteDictEditorTableView(BaseTableView): - """DictEditor table view""" - def __init__(self, parent, data, truncate=True, minmax=False, - get_value_func=None, set_value_func=None, - new_value_func=None, remove_values_func=None, - copy_value_func=None, is_list_func=None, get_len_func=None, - is_array_func=None, is_image_func=None, is_dict_func=None, - get_array_shape_func=None, get_array_ndim_func=None, - oedit_func=None, plot_func=None, imshow_func=None, - is_data_frame_func=None, is_series_func=None, - show_image_func=None, remote_editing=False): - BaseTableView.__init__(self, parent) - - self.remote_editing_enabled = None - - self.remove_values = remove_values_func - self.copy_value = copy_value_func - self.new_value = new_value_func - - self.is_data_frame = is_data_frame_func - self.is_series = is_series_func - self.is_list = is_list_func - self.get_len = get_len_func - self.is_array = is_array_func - self.is_image = is_image_func - self.is_dict = is_dict_func - self.get_array_shape = get_array_shape_func - self.get_array_ndim = get_array_ndim_func - self.oedit = oedit_func - self.plot = plot_func - self.imshow = imshow_func - self.show_image = show_image_func - - self.dictfilter = None - self.model = None - self.delegate = None - self.readonly = False - self.model = DictModel(self, data, names=True, - truncate=truncate, minmax=minmax, - remote=True) - self.setModel(self.model) - self.delegate = RemoteDictDelegate(self, get_value_func, set_value_func) - self.setItemDelegate(self.delegate) - - self.setup_table() - self.menu = self.setup_menu(truncate, minmax) - - def setup_menu(self, truncate, minmax): - """Setup context menu""" - menu = BaseTableView.setup_menu(self, truncate, minmax) - return menu - - def oedit_possible(self, key): - if (self.is_list(key) or self.is_dict(key) - or self.is_array(key) or self.is_image(key) - or self.is_data_frame(key) or self.is_series(key)): - # If this is a remote dict editor, the following avoid - # transfering large amount of data through the socket - return True - - def edit_item(self): - """ - Reimplement BaseTableView's method to edit item - - Some supported data types are directly edited in the remote process, - thus avoiding to transfer large amount of data through the socket from - the remote process to Spyder - """ - if self.remote_editing_enabled: - index = self.currentIndex() - if not index.isValid(): - return - key = self.model.get_key(index) - if self.oedit_possible(key): - # If this is a remote dict editor, the following avoid - # transfering large amount of data through the socket - self.oedit(key) - else: - BaseTableView.edit_item(self) - else: - BaseTableView.edit_item(self) - - -def get_test_data(): - """Create test data""" - import numpy as np - from spyderlib.pil_patch import Image - image = Image.fromarray(np.random.random_integers(255, size=(100, 100)), - mode='P') - testdict = {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]} - testdate = datetime.date(1945, 5, 8) - class Foobar(object): - def __init__(self): - self.text = "toto" - self.testdict = testdict - self.testdate = testdate - foobar = Foobar() - return {'object': foobar, - 'str': 'kjkj kj k j j kj k jkj', - 'unicode': to_text_string('éù', 'utf-8'), - 'list': [1, 3, [sorted, 5, 6], 'kjkj', None], - 'tuple': ([1, testdate, testdict], 'kjkj', None), - 'dict': testdict, - 'float': 1.2233, - 'int': 223, - 'bool': True, - 'array': np.random.rand(10, 10), - 'masked_array': np.ma.array([[1, 0], [1, 0]], - mask=[[True, False], [False, False]]), - '1D-array': np.linspace(-10, 10), - 'empty_array': np.array([]), - 'image': image, - 'date': testdate, - 'datetime': datetime.datetime(1945, 5, 8), - 'complex': 2+1j, - 'complex64': np.complex64(2+1j), - 'int8_scalar': np.int8(8), - 'int16_scalar': np.int16(16), - 'int32_scalar': np.int32(32), - 'bool_scalar': np.bool(8), - 'unsupported1': np.arccos, - 'unsupported2': np.cast, - #1: (1, 2, 3), -5: ("a", "b", "c"), 2.5: np.array((4.0, 6.0, 8.0)), - } - -def test(): - """Dictionary editor test""" - app = qapplication() #analysis:ignore - dialog = DictEditor() - dialog.setup(get_test_data()) - dialog.show() - app.exec_() - print("out:", dialog.get_value()) - -def remote_editor_test(): - """Remote dictionary editor test""" - from spyderlib.plugins.variableexplorer import VariableExplorer - from spyderlib.widgets.externalshell.monitor import make_remote_view - remote = make_remote_view(get_test_data(), VariableExplorer.get_settings()) - from pprint import pprint - pprint(remote) - app = qapplication() - dialog = DictEditor() - dialog.setup(remote, remote=True) - dialog.show() - app.exec_() - if dialog.result(): - print(dialog.get_value()) - -if __name__ == "__main__": - remote_editor_test() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/dicteditorutils.py spyder-3.0.2+dfsg1/spyderlib/widgets/dicteditorutils.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/dicteditorutils.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/dicteditorutils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,320 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Utilities for the Dictionary Editor Widget and Dialog based on Qt -""" - -from __future__ import print_function - -import re - -# Local imports -from spyderlib.py3compat import (NUMERIC_TYPES, TEXT_TYPES, to_text_string, - is_text_string, is_binary_string, reprlib, - PY2) -from spyderlib.utils import programs -from spyderlib import dependencies -from spyderlib.baseconfig import _ - - -class FakeObject(object): - """Fake class used in replacement of missing modules""" - pass - - -#----Numpy arrays support -try: - from numpy import ndarray, array, matrix, recarray - from numpy.ma import MaskedArray -except ImportError: - ndarray = array = matrix = recarray = MaskedArray = FakeObject # analysis:ignore - - -def get_numpy_dtype(obj): - """Return NumPy data type associated to obj - Return None if NumPy is not available - or if obj is not a NumPy array or scalar""" - if ndarray is not FakeObject: - # NumPy is available - import numpy as np - if isinstance(obj, np.generic) or isinstance(obj, np.ndarray): - # Numpy scalars all inherit from np.generic. - # Numpy arrays all inherit from np.ndarray. - # If we check that we are certain we have one of these - # types then we are less likely to generate an exception below. - try: - return obj.dtype.type - except (AttributeError, RuntimeError): - # AttributeError: some NumPy objects have no dtype attribute - # RuntimeError: happens with NetCDF objects (Issue 998) - return - - -#----Pandas support -PANDAS_REQVER = '>=0.13.1' -dependencies.add('pandas', _("View and edit DataFrames and Series in the " - "Variable Explorer"), - required_version=PANDAS_REQVER) -if programs.is_module_installed('pandas', PANDAS_REQVER): - from pandas import DataFrame, Series -else: - DataFrame = Series = FakeObject # analysis:ignore - - -#----PIL Images support -try: - from spyderlib import pil_patch - Image = pil_patch.Image.Image -except ImportError: - Image = FakeObject # analysis:ignore - - -#----BeautifulSoup support (see Issue 2448) -try: - import bs4 - NavigableString = bs4.element.NavigableString -except ImportError: - NavigableString = FakeObject # analysis:ignore - - -#----Misc. -def address(obj): - """Return object address as a string: ''""" - return "<%s @ %s>" % (obj.__class__.__name__, - hex(id(obj)).upper().replace('X', 'x')) - - -#----Set limits for the amount of elements in the repr of collections -# (lists, dicts, tuples and sets) -CollectionsRepr = reprlib.Repr() -CollectionsRepr.maxlist = 10 -CollectionsRepr.maxdict = 10 -CollectionsRepr.maxtuple = 10 -CollectionsRepr.maxset = 10 - - -#----date and datetime objects support -import datetime -try: - from dateutil.parser import parse as dateparse -except ImportError: - def dateparse(datestr): # analysis:ignore - """Just for 'year, month, day' strings""" - return datetime.datetime( *list(map(int, datestr.split(','))) ) -def datestr_to_datetime(value): - rp = value.rfind('(')+1 - v = dateparse(value[rp:-1]) - print(value, "-->", v) - return v - - -#----Background colors for supported types -ARRAY_COLOR = "#00ff00" -SCALAR_COLOR = "#0000ff" -COLORS = { - bool: "#ff00ff", - NUMERIC_TYPES: SCALAR_COLOR, - list: "#ffff00", - dict: "#00ffff", - tuple: "#c0c0c0", - TEXT_TYPES: "#800000", - (ndarray, - MaskedArray, - matrix, - DataFrame, - Series): ARRAY_COLOR, - Image: "#008000", - datetime.date: "#808000", - } -CUSTOM_TYPE_COLOR = "#7755aa" -UNSUPPORTED_COLOR = "#ffffff" - -def get_color_name(value): - """Return color name depending on value type""" - if not is_known_type(value): - return CUSTOM_TYPE_COLOR - for typ, name in list(COLORS.items()): - if isinstance(value, typ): - return name - else: - np_dtype = get_numpy_dtype(value) - if np_dtype is None or not hasattr(value, 'size'): - return UNSUPPORTED_COLOR - elif value.size == 1: - return SCALAR_COLOR - else: - return ARRAY_COLOR - -def is_editable_type(value): - """Return True if data type is editable with a standard GUI-based editor, - like DictEditor, ArrayEditor, QDateEdit or a simple QLineEdit""" - return get_color_name(value) not in (UNSUPPORTED_COLOR, CUSTOM_TYPE_COLOR) - - -#----Sorting -def sort_against(lista, listb, reverse=False): - """Arrange lista items in the same order as sorted(listb)""" - try: - return [item for _, item in sorted(zip(listb, lista), reverse=reverse)] - except: - return lista - -def unsorted_unique(lista): - """Removes duplicates from lista neglecting its initial ordering""" - return list(set(lista)) - - -#----Display <--> Value -def value_to_display(value, truncate=False, trunc_len=80, minmax=False): - """Convert value for display purpose""" - try: - if isinstance(value, recarray): - fields = value.names - display = 'Field names: ' + ', '.join(fields) - elif minmax and isinstance(value, (ndarray, MaskedArray)): - if value.size == 0: - display = repr(value) - try: - display = 'Min: %r\nMax: %r' % (value.min(), value.max()) - except TypeError: - pass - except ValueError: - # Happens when one of the array cell contains a sequence - pass - elif isinstance(value, (list, tuple, dict, set)): - display = CollectionsRepr.repr(value) - elif isinstance(value, Image): - display = '%s Mode: %s' % (address(value), value.mode) - elif isinstance(value, DataFrame): - cols = value.columns - if PY2 and len(cols) > 0: - # Get rid of possible BOM utf-8 data present at the - # beginning of a file, which gets attached to the first - # column header when headers are present in the first - # row. - # Fixes Issue 2514 - try: - ini_col = to_text_string(cols[0], encoding='utf-8-sig') - except: - ini_col = to_text_string(cols[0]) - cols = [ini_col] + [to_text_string(c) for c in cols[1:]] - else: - cols = [to_text_string(c) for c in cols] - display = 'Column names: ' + ', '.join(list(cols)) - elif isinstance(value, NavigableString): - # Fixes Issue 2448 - display = to_text_string(value) - elif is_binary_string(value): - try: - display = to_text_string(value, 'utf8') - except: - pass - elif is_text_string(value): - display = value - else: - display = repr(value) - if truncate and len(display) > trunc_len: - display = display[:trunc_len].rstrip() + ' ...' - except: - display = to_text_string(type(value)) - - return display - - -def try_to_eval(value): - """Try to eval value""" - try: - return eval(value) - except (NameError, SyntaxError, ImportError): - return value - -def get_size(item): - """Return size of an item of arbitrary type""" - if isinstance(item, (list, tuple, dict)): - return len(item) - elif isinstance(item, (ndarray, MaskedArray)): - return item.shape - elif isinstance(item, Image): - return item.size - if isinstance(item, (DataFrame, Series)): - return item.shape - else: - return 1 - -def get_type_string(item): - """Return type string of an object""" - if isinstance(item, DataFrame): - return "DataFrame" - if isinstance(item, Series): - return "Series" - found = re.findall(r"<(?:type|class) '(\S*)'>", str(type(item))) - if found: - return found[0] - - -def is_known_type(item): - """Return True if object has a known type""" - # Unfortunately, the masked array case is specific - return isinstance(item, MaskedArray) or get_type_string(item) is not None - -def get_human_readable_type(item): - """Return human-readable type string of an item""" - if isinstance(item, (ndarray, MaskedArray)): - return item.dtype.name - elif isinstance(item, Image): - return "Image" - else: - text = get_type_string(item) - if text is None: - text = to_text_string('unknown') - else: - return text[text.find('.')+1:] - - -#----Globals filter: filter namespace dictionaries (to be edited in DictEditor) -def is_supported(value, check_all=False, filters=None, iterate=True): - """Return True if the value is supported, False otherwise""" - assert filters is not None - if not is_editable_type(value): - return False - elif not isinstance(value, filters): - return False - elif iterate: - if isinstance(value, (list, tuple, set)): - for val in value: - if not is_supported(val, filters=filters, iterate=check_all): - return False - if not check_all: - break - elif isinstance(value, dict): - for key, val in list(value.items()): - if not is_supported(key, filters=filters, iterate=check_all) \ - or not is_supported(val, filters=filters, - iterate=check_all): - return False - if not check_all: - break - return True - -def globalsfilter(input_dict, check_all=False, filters=None, - exclude_private=None, exclude_capitalized=None, - exclude_uppercase=None, exclude_unsupported=None, - excluded_names=None): - """Keep only objects that can be pickled""" - output_dict = {} - for key, value in list(input_dict.items()): - excluded = (exclude_private and key.startswith('_')) or \ - (exclude_capitalized and key[0].isupper()) or \ - (exclude_uppercase and key.isupper() - and len(key) > 1 and not key[1:].isdigit()) or \ - (key in excluded_names) or \ - (exclude_unsupported and \ - not is_supported(value, check_all=check_all, - filters=filters)) - if not excluded: - output_dict[key] = value - return output_dict diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/editor.py spyder-3.0.2+dfsg1/spyderlib/widgets/editor.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/editor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/editor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2429 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Editor Widget""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from __future__ import print_function - -from spyderlib.qt import is_pyqt46 -from spyderlib.qt.QtGui import (QVBoxLayout, QMessageBox, QMenu, QFont, - QAction, QApplication, QWidget, QHBoxLayout, - QLabel, QKeySequence, QMainWindow, - QSplitter, QListWidget, QListWidgetItem, - QDialog, QLineEdit) -from spyderlib.qt.QtCore import (SIGNAL, Qt, QFileInfo, QThread, QObject, - QByteArray, QSize, QPoint, QTimer, Slot) -from spyderlib.qt.compat import getsavefilename - -import os -import sys -import os.path as osp - -# Local imports -from spyderlib.utils import encoding, sourcecode, codeanalysis -from spyderlib.utils import introspection -from spyderlib.baseconfig import _, DEBUG, STDOUT, STDERR -from spyderlib.config import EDIT_FILTERS, EDIT_EXT, get_filter, EDIT_FILETYPES -from spyderlib.guiconfig import create_shortcut, new_shortcut -from spyderlib.utils.qthelpers import (get_icon, create_action, add_actions, - mimedata2url, get_filetype_icon, - create_toolbutton) -from spyderlib.widgets.tabs import BaseTabs -from spyderlib.widgets.findreplace import FindReplace -from spyderlib.widgets.editortools import OutlineExplorerWidget -from spyderlib.widgets.status import (ReadWriteStatus, EOLStatus, - EncodingStatus, CursorPositionStatus) -from spyderlib.widgets.sourcecode import syntaxhighlighters, codeeditor -from spyderlib.widgets.sourcecode.base import TextEditBaseWidget #analysis:ignore -from spyderlib.widgets.sourcecode.codeeditor import Printer #analysis:ignore -from spyderlib.widgets.sourcecode.codeeditor import get_file_language -from spyderlib.py3compat import to_text_string, qbytearray_to_str, u - - -DEBUG_EDITOR = DEBUG >= 3 - - -class FileListDialog(QDialog): - def __init__(self, parent, tabs, fullpath_sorting): - QDialog.__init__(self, parent) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - self.indexes = None - - self.setWindowIcon(get_icon('filelist.png')) - self.setWindowTitle(_("File list management")) - - self.setModal(True) - - flabel = QLabel(_("Filter:")) - self.edit = QLineEdit(self) - self.connect(self.edit, SIGNAL("returnPressed()"), self.edit_file) - self.connect(self.edit, SIGNAL("textChanged(QString)"), - lambda text: self.synchronize(0)) - fhint = QLabel(_("(press Enter to edit file)")) - edit_layout = QHBoxLayout() - edit_layout.addWidget(flabel) - edit_layout.addWidget(self.edit) - edit_layout.addWidget(fhint) - - self.listwidget = QListWidget(self) - self.listwidget.setResizeMode(QListWidget.Adjust) - self.connect(self.listwidget, SIGNAL("itemSelectionChanged()"), - self.item_selection_changed) - self.connect(self.listwidget, SIGNAL("itemActivated(QListWidgetItem*)"), - self.edit_file) - - btn_layout = QHBoxLayout() - edit_btn = create_toolbutton(self, icon=get_icon('edit.png'), - text=_("&Edit file"), autoraise=False, - triggered=self.edit_file, text_beside_icon=True) - edit_btn.setMinimumHeight(28) - btn_layout.addWidget(edit_btn) - - btn_layout.addStretch() - btn_layout.addSpacing(150) - btn_layout.addStretch() - - close_btn = create_toolbutton(self, text=_("&Close file"), - icon=get_icon("fileclose.png"), - autoraise=False, text_beside_icon=True, - triggered=lambda: self.emit(SIGNAL("close_file(int)"), - self.indexes[self.listwidget.currentRow()])) - close_btn.setMinimumHeight(28) - btn_layout.addWidget(close_btn) - - hint = QLabel(_("Hint: press Alt to show accelerators")) - hint.setAlignment(Qt.AlignCenter) - - vlayout = QVBoxLayout() - vlayout.addLayout(edit_layout) - vlayout.addWidget(self.listwidget) - vlayout.addLayout(btn_layout) - vlayout.addWidget(hint) - self.setLayout(vlayout) - - self.tabs = tabs - self.fullpath_sorting = fullpath_sorting - self.buttons = (edit_btn, close_btn) - - def edit_file(self): - row = self.listwidget.currentRow() - if self.listwidget.count() > 0 and row >= 0: - self.emit(SIGNAL("edit_file(int)"), self.indexes[row]) - self.accept() - - def item_selection_changed(self): - for btn in self.buttons: - btn.setEnabled(self.listwidget.currentRow() >= 0) - - def synchronize(self, stack_index): - count = self.tabs.count() - if count == 0: - self.accept() - return - self.listwidget.setTextElideMode(Qt.ElideMiddle - if self.fullpath_sorting - else Qt.ElideRight) - current_row = self.listwidget.currentRow() - if current_row >= 0: - current_text = to_text_string(self.listwidget.currentItem().text()) - else: - current_text = "" - self.listwidget.clear() - self.indexes = [] - new_current_index = stack_index - filter_text = to_text_string(self.edit.text()) - lw_index = 0 - for index in range(count): - text = to_text_string(self.tabs.tabText(index)) - if len(filter_text) == 0 or filter_text in text: - if text == current_text: - new_current_index = lw_index - lw_index += 1 - item = QListWidgetItem(self.tabs.tabIcon(index), - text, self.listwidget) - item.setSizeHint(QSize(0, 25)) - self.listwidget.addItem(item) - self.indexes.append(index) - if new_current_index < self.listwidget.count(): - self.listwidget.setCurrentRow(new_current_index) - for btn in self.buttons: - btn.setEnabled(lw_index > 0) - - -class AnalysisThread(QThread): - """Analysis thread""" - def __init__(self, parent, checker, source_code): - super(AnalysisThread, self).__init__(parent) - self.checker = checker - self.results = None - self.source_code = source_code - - def run(self): - """Run analysis""" - try: - self.results = self.checker(self.source_code) - except Exception: - if DEBUG_EDITOR: - import traceback - traceback.print_exc(file=STDERR) - - -class ThreadManager(QObject): - """Analysis thread manager""" - def __init__(self, parent, max_simultaneous_threads=2): - super(ThreadManager, self).__init__(parent) - self.max_simultaneous_threads = max_simultaneous_threads - self.started_threads = {} - self.pending_threads = [] - self.end_callbacks = {} - - def close_threads(self, parent): - """Close threads associated to parent_id""" - if DEBUG_EDITOR: - print("Call to 'close_threads'", file=STDOUT) - if parent is None: - # Closing all threads - self.pending_threads = [] - threadlist = [] - for threads in list(self.started_threads.values()): - threadlist += threads - else: - parent_id = id(parent) - self.pending_threads = [(_th, _id) for (_th, _id) - in self.pending_threads - if _id != parent_id] - threadlist = self.started_threads.get(parent_id, []) - for thread in threadlist: - if DEBUG_EDITOR: - print("Waiting for thread %r to finish" % thread, file=STDOUT) - while thread.isRunning(): - # We can't terminate thread safely, so we simply wait... - QApplication.processEvents() - - def close_all_threads(self): - """Close all threads""" - if DEBUG_EDITOR: - print("Call to 'close_all_threads'", file=STDOUT) - self.close_threads(None) - - def add_thread(self, checker, end_callback, source_code, parent): - """Add thread to queue""" - parent_id = id(parent) - thread = AnalysisThread(self, checker, source_code) - self.end_callbacks[id(thread)] = end_callback - self.pending_threads.append((thread, parent_id)) - if DEBUG_EDITOR: - print("Added thread %r to queue" % thread, file=STDOUT) - QTimer.singleShot(50, self.update_queue) - - def update_queue(self): - """Update queue""" - started = 0 - for parent_id, threadlist in list(self.started_threads.items()): - still_running = [] - for thread in threadlist: - if thread.isFinished(): - end_callback = self.end_callbacks.pop(id(thread)) - if thread.results is not None: - # The thread was executed successfully - end_callback(thread.results) - thread.setParent(None) - thread = None - else: - still_running.append(thread) - started += 1 - threadlist = None - if still_running: - self.started_threads[parent_id] = still_running - else: - self.started_threads.pop(parent_id) - if DEBUG_EDITOR: - print("Updating queue:", file=STDOUT) - print(" started:", started, file=STDOUT) - print(" pending:", len(self.pending_threads), file=STDOUT) - if self.pending_threads and started < self.max_simultaneous_threads: - thread, parent_id = self.pending_threads.pop(0) - self.connect(thread, SIGNAL('finished()'), self.update_queue) - threadlist = self.started_threads.get(parent_id, []) - self.started_threads[parent_id] = threadlist+[thread] - if DEBUG_EDITOR: - print("===>starting:", thread, file=STDOUT) - thread.start() - - -class FileInfo(QObject): - """File properties""" - def __init__(self, filename, encoding, editor, new, threadmanager, - introspection_plugin): - QObject.__init__(self) - self.threadmanager = threadmanager - self.filename = filename - self.newly_created = new - self.default = False # Default untitled file - self.encoding = encoding - self.editor = editor - self.path = [] - - self.classes = (filename, None, None) - self.analysis_results = [] - self.todo_results = [] - self.lastmodified = QFileInfo(filename).lastModified() - - self.connect(self.editor, SIGNAL('textChanged()'), - self.text_changed) - self.connect(self.editor, SIGNAL('breakpoints_changed()'), - self.breakpoints_changed) - - self.pyflakes_results = None - self.pep8_results = None - - def text_changed(self): - """Editor's text has changed""" - self.default = False - self.emit(SIGNAL('text_changed_at(QString,int)'), - self.filename, self.editor.get_position('cursor')) - - def get_source_code(self): - """Return associated editor source code""" - return to_text_string(self.editor.toPlainText()) - - def run_code_analysis(self, run_pyflakes, run_pep8): - """Run code analysis""" - run_pyflakes = run_pyflakes and codeanalysis.is_pyflakes_installed() - run_pep8 = run_pep8 and\ - codeanalysis.get_checker_executable('pep8') is not None - self.pyflakes_results = [] - self.pep8_results = [] - if self.editor.is_python(): - enc = self.encoding.replace('-guessed', '').replace('-bom', '') - source_code = self.get_source_code().encode(enc) - if run_pyflakes: - self.pyflakes_results = None - if run_pep8: - self.pep8_results = None - if run_pyflakes: - self.threadmanager.add_thread(codeanalysis.check_with_pyflakes, - self.pyflakes_analysis_finished, - source_code, self) - if run_pep8: - self.threadmanager.add_thread(codeanalysis.check_with_pep8, - self.pep8_analysis_finished, - source_code, self) - - def pyflakes_analysis_finished(self, results): - """Pyflakes code analysis thread has finished""" - self.pyflakes_results = results - if self.pep8_results is not None: - self.code_analysis_finished() - - def pep8_analysis_finished(self, results): - """Pep8 code analysis thread has finished""" - self.pep8_results = results - if self.pyflakes_results is not None: - self.code_analysis_finished() - - def code_analysis_finished(self): - """Code analysis thread has finished""" - self.set_analysis_results(self.pyflakes_results+self.pep8_results) - self.emit(SIGNAL('analysis_results_changed()')) - - def set_analysis_results(self, results): - """Set analysis results and update warning markers in editor""" - self.analysis_results = results - self.editor.process_code_analysis(results) - - def cleanup_analysis_results(self): - """Clean-up analysis results""" - self.analysis_results = [] - self.editor.cleanup_code_analysis() - - def run_todo_finder(self): - """Run TODO finder""" - if self.editor.is_python(): - self.threadmanager.add_thread(codeanalysis.find_tasks, - self.todo_finished, - self.get_source_code(), self) - - def todo_finished(self, results): - """Code analysis thread has finished""" - self.set_todo_results(results) - self.emit(SIGNAL('todo_results_changed()')) - - def set_todo_results(self, results): - """Set TODO results and update markers in editor""" - self.todo_results = results - self.editor.process_todo(results) - - def cleanup_todo_results(self): - """Clean-up TODO finder results""" - self.todo_results = [] - - def breakpoints_changed(self): - """Breakpoint list has changed""" - breakpoints = self.editor.get_breakpoints() - if self.editor.breakpoints != breakpoints: - self.editor.breakpoints = breakpoints - self.emit(SIGNAL("save_breakpoints(QString,QString)"), - self.filename, repr(breakpoints)) - - -class EditorStack(QWidget): - def __init__(self, parent, actions): - QWidget.__init__(self, parent) - - self.setAttribute(Qt.WA_DeleteOnClose) - - self.threadmanager = ThreadManager(self) - - self.newwindow_action = None - self.horsplit_action = None - self.versplit_action = None - self.close_action = None - self.__get_split_actions() - - layout = QVBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - self.setLayout(layout) - - self.menu = None - self.filelist_dlg = None -# self.filelist_btn = None -# self.previous_btn = None -# self.next_btn = None - self.tabs = None - - self.stack_history = [] - - self.setup_editorstack(parent, layout) - - self.find_widget = None - - self.data = [] - - filelist_action = create_action(self, _("File list management"), - icon=get_icon('filelist.png'), - triggered=self.open_filelistdialog) - copy_to_cb_action = create_action(self, _("Copy path to clipboard"), - icon="editcopy.png", - triggered=lambda: - QApplication.clipboard().setText(self.get_current_filename())) - self.menu_actions = actions+[None, filelist_action, copy_to_cb_action] - self.outlineexplorer = None - self.inspector = None - self.unregister_callback = None - self.is_closable = False - self.new_action = None - self.open_action = None - self.save_action = None - self.revert_action = None - self.tempfile_path = None - self.title = _("Editor") - self.pyflakes_enabled = True - self.pep8_enabled = False - self.todolist_enabled = True - self.realtime_analysis_enabled = False - self.is_analysis_done = False - self.linenumbers_enabled = True - self.blanks_enabled = False - self.edgeline_enabled = True - self.edgeline_column = 79 - self.codecompletion_auto_enabled = True - self.codecompletion_case_enabled = False - self.codecompletion_enter_enabled = False - self.calltips_enabled = True - self.go_to_definition_enabled = True - self.close_parentheses_enabled = True - self.close_quotes_enabled = True - self.add_colons_enabled = True - self.auto_unindent_enabled = True - self.indent_chars = " "*4 - self.tab_stop_width = 40 - self.inspector_enabled = False - self.default_font = None - self.wrap_enabled = False - self.tabmode_enabled = False - self.intelligent_backspace_enabled = True - self.highlight_current_line_enabled = False - self.highlight_current_cell_enabled = False - self.occurence_highlighting_enabled = True - self.checkeolchars_enabled = True - self.always_remove_trailing_spaces = False - self.fullpath_sorting_enabled = None - self.focus_to_editor = True - self.set_fullpath_sorting_enabled(False) - ccs = 'Spyder' - if ccs not in syntaxhighlighters.COLOR_SCHEME_NAMES: - ccs = syntaxhighlighters.COLOR_SCHEME_NAMES[0] - self.color_scheme = ccs - self.introspector = introspection.PluginManager(self) - - self.introspector.send_to_inspector.connect(self.send_to_inspector) - self.introspector.edit_goto.connect( - lambda fname, lineno, name: - self.emit(SIGNAL("edit_goto(QString,int,QString)"), - fname, lineno, name)) - - self.__file_status_flag = False - - # Real-time code analysis - self.analysis_timer = QTimer(self) - self.analysis_timer.setSingleShot(True) - self.analysis_timer.setInterval(2000) - self.connect(self.analysis_timer, SIGNAL("timeout()"), - self.analyze_script) - - # Accepting drops - self.setAcceptDrops(True) - - # Local shortcuts - self.shortcuts = self.create_shortcuts() - - def create_shortcuts(self): - """Create local shortcuts""" - # Configurable shortcuts - inspect = create_shortcut(self.inspect_current_object, context='Editor', - name='Inspect current object', parent=self) - breakpoint = create_shortcut(self.set_or_clear_breakpoint, - context='Editor', name='Breakpoint', - parent=self) - cbreakpoint = create_shortcut(self.set_or_edit_conditional_breakpoint, - context='Editor', - name='Conditional breakpoint', - parent=self) - gotoline = create_shortcut(self.go_to_line, context='Editor', - name='Go to line', parent=self) - filelist = create_shortcut(self.open_filelistdialog, context='Editor', - name='File list management', parent=self) - tab = create_shortcut(self.go_to_previous_file, context='Editor', - name='Go to previous file', parent=self) - tabshift = create_shortcut(self.go_to_next_file, context='Editor', - name='Go to next file', parent=self) - # Fixed shortcuts - new_shortcut(QKeySequence.ZoomIn, self, - lambda: self.emit(SIGNAL('zoom_in()'))) - new_shortcut("Ctrl+=", self, lambda: self.emit(SIGNAL('zoom_in()'))) - new_shortcut(QKeySequence.ZoomOut, self, - lambda: self.emit(SIGNAL('zoom_out()'))) - new_shortcut("Ctrl+0", self, lambda: self.emit(SIGNAL('zoom_reset()'))) - new_shortcut("Ctrl+W", self, self.close_file) - new_shortcut("Ctrl+F4", self, self.close_file) - # Return configurable ones - return [inspect, breakpoint, cbreakpoint, gotoline, filelist, tab, - tabshift] - - def get_shortcut_data(self): - """ - Returns shortcut data, a list of tuples (shortcut, text, default) - shortcut (QShortcut or QAction instance) - text (string): action/shortcut description - default (string): default key sequence - """ - return [sc.data for sc in self.shortcuts] - - def setup_editorstack(self, parent, layout): - """Setup editorstack's layout""" - menu_btn = create_toolbutton(self, icon=get_icon("tooloptions.png"), - tip=_("Options")) - self.menu = QMenu(self) - menu_btn.setMenu(self.menu) - menu_btn.setPopupMode(menu_btn.InstantPopup) - self.connect(self.menu, SIGNAL("aboutToShow()"), self.__setup_menu) - -# self.filelist_btn = create_toolbutton(self, -# icon=get_icon('filelist.png'), -# tip=_("File list management"), -# triggered=self.open_filelistdialog) -# -# self.previous_btn = create_toolbutton(self, -# icon=get_icon('previous.png'), -# tip=_("Previous file"), -# triggered=self.go_to_previous_file) -# -# self.next_btn = create_toolbutton(self, -# icon=get_icon('next.png'), -# tip=_("Next file"), -# triggered=self.go_to_next_file) - - # Optional tabs -# corner_widgets = {Qt.TopRightCorner: [self.previous_btn, -# self.filelist_btn, self.next_btn, -# 5, menu_btn]} - corner_widgets = {Qt.TopRightCorner: [menu_btn]} - self.tabs = BaseTabs(self, menu=self.menu, menu_use_tooltips=True, - corner_widgets=corner_widgets) - self.tabs.tabBar().setObjectName('plugin-tab') - self.tabs.set_close_function(self.close_file) - - if hasattr(self.tabs, 'setDocumentMode') \ - and not sys.platform == 'darwin': - # Don't set document mode to true on OSX because it generates - # a crash when the editor is detached from the main window - # Fixes Issue 561 - self.tabs.setDocumentMode(True) - self.connect(self.tabs, SIGNAL('currentChanged(int)'), - self.current_changed) - - if sys.platform == 'darwin': - tab_container = QWidget() - tab_container.setObjectName('tab-container') - tab_layout = QHBoxLayout(tab_container) - tab_layout.setContentsMargins(0, 0, 0, 0) - tab_layout.addWidget(self.tabs) - layout.addWidget(tab_container) - else: - layout.addWidget(self.tabs) - - def add_corner_widgets_to_tabbar(self, widgets): - self.tabs.add_corner_widgets(widgets) - - def closeEvent(self, event): - self.threadmanager.close_all_threads() - self.disconnect(self.analysis_timer, SIGNAL("timeout()"), - self.analyze_script) - QWidget.closeEvent(self, event) - if is_pyqt46: - self.emit(SIGNAL('destroyed()')) - - def clone_editor_from(self, other_finfo, set_current): - fname = other_finfo.filename - enc = other_finfo.encoding - new = other_finfo.newly_created - finfo = self.create_new_editor(fname, enc, "", - set_current=set_current, new=new, - cloned_from=other_finfo.editor) - finfo.set_analysis_results(other_finfo.analysis_results) - finfo.set_todo_results(other_finfo.todo_results) - return finfo.editor - - def clone_from(self, other): - """Clone EditorStack from other instance""" - for other_finfo in other.data: - self.clone_editor_from(other_finfo, set_current=True) - self.set_stack_index(other.get_stack_index()) - - def open_filelistdialog(self): - """Open file list management dialog box""" - self.filelist_dlg = dlg = FileListDialog(self, self.tabs, - self.fullpath_sorting_enabled) - self.connect(dlg, SIGNAL("edit_file(int)"), self.set_stack_index) - self.connect(dlg, SIGNAL("close_file(int)"), self.close_file) - dlg.synchronize(self.get_stack_index()) - dlg.exec_() - self.filelist_dlg = None - - def update_filelistdialog(self): - """Synchronize file list dialog box with editor widget tabs""" - if self.filelist_dlg is not None: - self.filelist_dlg.synchronize(self.get_stack_index()) - - def go_to_line(self): - """Go to line dialog""" - if self.data: - self.get_current_editor().exec_gotolinedialog() - - def set_or_clear_breakpoint(self): - """Set/clear breakpoint""" - if self.data: - editor = self.get_current_editor() - editor.add_remove_breakpoint() - - def set_or_edit_conditional_breakpoint(self): - """Set conditional breakpoint""" - if self.data: - editor = self.get_current_editor() - editor.add_remove_breakpoint(edit_condition=True) - - def inspect_current_object(self): - """Inspect current object in Object Inspector""" - if self.introspector: - editor = self.get_current_editor() - position = editor.get_position('cursor') - self.inspector.switch_to_editor_source() - self.introspector.show_object_info(position, auto=False) - else: - text = self.get_current_editor().get_current_object() - if text: - self.send_to_inspector(text, force=True) - - #------ Editor Widget Settings - def set_closable(self, state): - """Parent widget must handle the closable state""" - self.is_closable = state - - def set_io_actions(self, new_action, open_action, - save_action, revert_action): - self.new_action = new_action - self.open_action = open_action - self.save_action = save_action - self.revert_action = revert_action - - def set_find_widget(self, find_widget): - self.find_widget = find_widget - - def set_outlineexplorer(self, outlineexplorer): - self.outlineexplorer = outlineexplorer - self.connect(self.outlineexplorer, - SIGNAL("outlineexplorer_is_visible()"), - self._refresh_outlineexplorer) - - def initialize_outlineexplorer(self): - """This method is called separately from 'set_oulineexplorer' to avoid - doing unnecessary updates when there are multiple editor windows""" - for index in range(self.get_stack_count()): - if index != self.get_stack_index(): - self._refresh_outlineexplorer(index=index) - - def add_outlineexplorer_button(self, editor_plugin): - oe_btn = create_toolbutton(editor_plugin) - oe_btn.setDefaultAction(self.outlineexplorer.visibility_action) - self.add_corner_widgets_to_tabbar([5, oe_btn]) - - def set_inspector(self, inspector): - self.inspector = inspector - - def set_tempfile_path(self, path): - self.tempfile_path = path - - def set_title(self, text): - self.title = text - - def __update_editor_margins(self, editor): - editor.setup_margins(linenumbers=self.linenumbers_enabled, - markers=self.has_markers()) - - def __codeanalysis_settings_changed(self, current_finfo): - if self.data: - run_pyflakes, run_pep8 = self.pyflakes_enabled, self.pep8_enabled - for finfo in self.data: - self.__update_editor_margins(finfo.editor) - finfo.cleanup_analysis_results() - if (run_pyflakes or run_pep8) and current_finfo is not None: - if current_finfo is not finfo: - finfo.run_code_analysis(run_pyflakes, run_pep8) - - def set_pyflakes_enabled(self, state, current_finfo=None): - # CONF.get(self.CONF_SECTION, 'code_analysis/pyflakes') - self.pyflakes_enabled = state - self.__codeanalysis_settings_changed(current_finfo) - - def set_pep8_enabled(self, state, current_finfo=None): - # CONF.get(self.CONF_SECTION, 'code_analysis/pep8') - self.pep8_enabled = state - self.__codeanalysis_settings_changed(current_finfo) - - def has_markers(self): - """Return True if this editorstack has a marker margin for TODOs or - code analysis""" - return self.todolist_enabled or self.pyflakes_enabled\ - or self.pep8_enabled - - def set_todolist_enabled(self, state, current_finfo=None): - # CONF.get(self.CONF_SECTION, 'todo_list') - self.todolist_enabled = state - if self.data: - for finfo in self.data: - self.__update_editor_margins(finfo.editor) - finfo.cleanup_todo_results() - if state and current_finfo is not None: - if current_finfo is not finfo: - finfo.run_todo_finder() - - def set_realtime_analysis_enabled(self, state): - self.realtime_analysis_enabled = state - - def set_realtime_analysis_timeout(self, timeout): - self.analysis_timer.setInterval(timeout) - - def set_linenumbers_enabled(self, state, current_finfo=None): - # CONF.get(self.CONF_SECTION, 'line_numbers') - self.linenumbers_enabled = state - if self.data: - for finfo in self.data: - self.__update_editor_margins(finfo.editor) - - def set_blanks_enabled(self, state): - self.blanks_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_blanks_enabled(state) - - def set_edgeline_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'edge_line') - self.edgeline_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_edge_line_enabled(state) - - def set_edgeline_column(self, column): - # CONF.get(self.CONF_SECTION, 'edge_line_column') - self.edgeline_column = column - if self.data: - for finfo in self.data: - finfo.editor.set_edge_line_column(column) - - def set_codecompletion_auto_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'codecompletion_auto') - self.codecompletion_auto_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_codecompletion_auto(state) - - def set_codecompletion_case_enabled(self, state): - self.codecompletion_case_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_codecompletion_case(state) - - def set_codecompletion_enter_enabled(self, state): - self.codecompletion_enter_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_codecompletion_enter(state) - - def set_calltips_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'calltips') - self.calltips_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_calltips(state) - - def set_go_to_definition_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'go_to_definition') - self.go_to_definition_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_go_to_definition_enabled(state) - - def set_close_parentheses_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'close_parentheses') - self.close_parentheses_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_close_parentheses_enabled(state) - - def set_close_quotes_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'close_quotes') - self.close_quotes_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_close_quotes_enabled(state) - - def set_add_colons_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'add_colons') - self.add_colons_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_add_colons_enabled(state) - - def set_auto_unindent_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'auto_unindent') - self.auto_unindent_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_auto_unindent_enabled(state) - - def set_indent_chars(self, indent_chars): - # CONF.get(self.CONF_SECTION, 'indent_chars') - indent_chars = indent_chars[1:-1] # removing the leading/ending '*' - self.indent_chars = indent_chars - if self.data: - for finfo in self.data: - finfo.editor.set_indent_chars(indent_chars) - - def set_tab_stop_width(self, tab_stop_width): - # CONF.get(self.CONF_SECTION, 'tab_stop_width') - self.tab_stop_width = tab_stop_width - if self.data: - for finfo in self.data: - finfo.editor.setTabStopWidth(tab_stop_width) - - def set_inspector_enabled(self, state): - self.inspector_enabled = state - - def set_default_font(self, font, color_scheme=None): - # get_font(self.CONF_SECTION) - self.default_font = font - if color_scheme is not None: - self.color_scheme = color_scheme - if self.data: - for finfo in self.data: - finfo.editor.set_font(font, color_scheme) - - def set_color_scheme(self, color_scheme): - self.color_scheme = color_scheme - if self.data: - for finfo in self.data: - finfo.editor.set_color_scheme(color_scheme) - - def set_wrap_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'wrap') - self.wrap_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.toggle_wrap_mode(state) - - def set_tabmode_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'tab_always_indent') - self.tabmode_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_tab_mode(state) - - def set_intelligent_backspace_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'intelligent_backspace') - self.intelligent_backspace_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.toggle_intelligent_backspace(state) - - def set_occurence_highlighting_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'occurence_highlighting') - self.occurence_highlighting_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_occurence_highlighting(state) - - def set_occurence_highlighting_timeout(self, timeout): - # CONF.get(self.CONF_SECTION, 'occurence_highlighting/timeout') - self.occurence_highlighting_timeout = timeout - if self.data: - for finfo in self.data: - finfo.editor.set_occurence_timeout(timeout) - - def set_highlight_current_line_enabled(self, state): - self.highlight_current_line_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_highlight_current_line(state) - - def set_highlight_current_cell_enabled(self, state): - self.highlight_current_cell_enabled = state - if self.data: - for finfo in self.data: - finfo.editor.set_highlight_current_cell(state) - - def set_checkeolchars_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'check_eol_chars') - self.checkeolchars_enabled = state - - def set_fullpath_sorting_enabled(self, state): - # CONF.get(self.CONF_SECTION, 'fullpath_sorting') - self.fullpath_sorting_enabled = state - if self.data: - finfo = self.data[self.get_stack_index()] - self.data.sort(key=self.__get_sorting_func()) - new_index = self.data.index(finfo) - self.__repopulate_stack() - self.set_stack_index(new_index) - - def set_always_remove_trailing_spaces(self, state): - # CONF.get(self.CONF_SECTION, 'always_remove_trailing_spaces') - self.always_remove_trailing_spaces = state - - def set_focus_to_editor(self, state): - self.focus_to_editor = state - - #------ Stacked widget management - def get_stack_index(self): - return self.tabs.currentIndex() - - def get_current_finfo(self): - if self.data: - return self.data[self.get_stack_index()] - - def get_current_editor(self): - return self.tabs.currentWidget() - - def get_stack_count(self): - return self.tabs.count() - - def set_stack_index(self, index): - self.tabs.setCurrentIndex(index) - - def set_tabbar_visible(self, state): - self.tabs.tabBar().setVisible(state) - - def remove_from_data(self, index): - self.tabs.blockSignals(True) - self.tabs.removeTab(index) - self.data.pop(index) - self.tabs.blockSignals(False) - self.update_actions() - self.update_filelistdialog() - - def __modified_readonly_title(self, title, is_modified, is_readonly): - if is_modified is not None and is_modified: - title += "*" - if is_readonly is not None and is_readonly: - title = "(%s)" % title - return title - - def get_tab_text(self, filename, is_modified=None, is_readonly=None): - """Return tab title""" - return self.__modified_readonly_title(osp.basename(filename), - is_modified, is_readonly) - - def get_tab_tip(self, filename, is_modified=None, is_readonly=None): - """Return tab menu title""" - if self.fullpath_sorting_enabled: - text = filename - else: - text = u("%s — %s") - text = self.__modified_readonly_title(text, - is_modified, is_readonly) - if self.tempfile_path is not None\ - and filename == encoding.to_unicode_from_fs(self.tempfile_path): - temp_file_str = to_text_string(_("Temporary file")) - if self.fullpath_sorting_enabled: - return "%s (%s)" % (text, temp_file_str) - else: - return text % (temp_file_str, self.tempfile_path) - else: - if self.fullpath_sorting_enabled: - return text - else: - return text % (osp.basename(filename), osp.dirname(filename)) - - def __get_sorting_func(self): - if self.fullpath_sorting_enabled: - return lambda item: osp.join(osp.dirname(item.filename), - '_'+osp.basename(item.filename)) - else: - return lambda item: osp.basename(item.filename) - - def add_to_data(self, finfo, set_current): - self.data.append(finfo) - self.data.sort(key=self.__get_sorting_func()) - index = self.data.index(finfo) - fname, editor = finfo.filename, finfo.editor - self.tabs.insertTab(index, editor, get_filetype_icon(fname), - self.get_tab_text(fname)) - self.set_stack_title(index, False) - if set_current: - self.set_stack_index(index) - self.current_changed(index) - self.update_actions() - self.update_filelistdialog() - - def __repopulate_stack(self): - self.tabs.blockSignals(True) - self.tabs.clear() - for finfo in self.data: - icon = get_filetype_icon(finfo.filename) - if finfo.newly_created: - is_modified = True - else: - is_modified = None - tab_text = self.get_tab_text(finfo.filename, is_modified) - tab_tip = self.get_tab_tip(finfo.filename) - index = self.tabs.addTab(finfo.editor, icon, tab_text) - self.tabs.setTabToolTip(index, tab_tip) - self.tabs.blockSignals(False) - self.update_filelistdialog() - - def rename_in_data(self, index, new_filename): - finfo = self.data[index] - if osp.splitext(finfo.filename)[1] != osp.splitext(new_filename)[1]: - # File type has changed! - txt = to_text_string(finfo.editor.get_text_with_eol()) - language = get_file_language(new_filename, txt) - finfo.editor.set_language(language) - set_new_index = index == self.get_stack_index() - current_fname = self.get_current_filename() - finfo.filename = new_filename - self.data.sort(key=self.__get_sorting_func()) - new_index = self.data.index(finfo) - self.__repopulate_stack() - if set_new_index: - self.set_stack_index(new_index) - else: - # Fixes Issue 1287 - self.set_current_filename(current_fname) - if self.outlineexplorer is not None: - self.outlineexplorer.file_renamed(finfo.editor, finfo.filename) - return new_index - - def set_stack_title(self, index, is_modified): - finfo = self.data[index] - fname = finfo.filename - is_modified = (is_modified or finfo.newly_created) and not finfo.default - is_readonly = finfo.editor.isReadOnly() - tab_text = self.get_tab_text(fname, is_modified, is_readonly) - tab_tip = self.get_tab_tip(fname, is_modified, is_readonly) - self.tabs.setTabText(index, tab_text) - self.tabs.setTabToolTip(index, tab_tip) - - - #------ Context menu - def __setup_menu(self): - """Setup tab context menu before showing it""" - self.menu.clear() - if self.data: - actions = self.menu_actions - else: - actions = (self.new_action, self.open_action) - self.setFocus() # --> Editor.__get_focus_editortabwidget - add_actions(self.menu, list(actions)+self.__get_split_actions()) - self.close_action.setEnabled(self.is_closable) - - - #------ Hor/Ver splitting - def __get_split_actions(self): - # New window - self.newwindow_action = create_action(self, _("New window"), - icon="newwindow.png", tip=_("Create a new editor window"), - triggered=lambda: self.emit(SIGNAL("create_new_window()"))) - # Splitting - self.versplit_action = create_action(self, _("Split vertically"), - icon="versplit.png", - tip=_("Split vertically this editor window"), - triggered=lambda: self.emit(SIGNAL("split_vertically()"))) - self.horsplit_action = create_action(self, _("Split horizontally"), - icon="horsplit.png", - tip=_("Split horizontally this editor window"), - triggered=lambda: self.emit(SIGNAL("split_horizontally()"))) - self.close_action = create_action(self, _("Close this panel"), - icon="close_panel.png", triggered=self.close) - return [None, self.newwindow_action, None, - self.versplit_action, self.horsplit_action, self.close_action] - - def reset_orientation(self): - self.horsplit_action.setEnabled(True) - self.versplit_action.setEnabled(True) - - def set_orientation(self, orientation): - self.horsplit_action.setEnabled(orientation == Qt.Horizontal) - self.versplit_action.setEnabled(orientation == Qt.Vertical) - - def update_actions(self): - state = self.get_stack_count() > 0 - self.horsplit_action.setEnabled(state) - self.versplit_action.setEnabled(state) - - - #------ Accessors - def get_current_filename(self): - if self.data: - return self.data[self.get_stack_index()].filename - - def has_filename(self, filename): - fixpath = lambda path: osp.normcase(osp.realpath(path)) - for index, finfo in enumerate(self.data): - if fixpath(filename) == fixpath(finfo.filename): - return index - - def set_current_filename(self, filename): - """Set current filename and return the associated editor instance""" - index = self.has_filename(filename) - if index is not None: - self.set_stack_index(index) - editor = self.data[index].editor - editor.setFocus() - return editor - - def is_file_opened(self, filename=None): - if filename is None: - # Is there any file opened? - return len(self.data) > 0 - else: - return self.has_filename(filename) - - - #------ Close file, tabwidget... - def close_file(self, index=None, force=False): - """Close file (index=None -> close current file) - Keep current file index unchanged (if current file - that is being closed)""" - current_index = self.get_stack_index() - count = self.get_stack_count() - if index is None: - if count > 0: - index = current_index - else: - self.find_widget.set_editor(None) - return - new_index = None - if count > 1: - if current_index == index: - new_index = self._get_previous_file_index() - else: - new_index = current_index - is_ok = force or self.save_if_changed(cancelable=True, index=index) - if is_ok: - finfo = self.data[index] - self.threadmanager.close_threads(finfo) - # Removing editor reference from outline explorer settings: - if self.outlineexplorer is not None: - self.outlineexplorer.remove_editor(finfo.editor) - - self.remove_from_data(index) - - # We pass self object ID as a QString, because otherwise it would - # depend on the platform: long for 64bit, int for 32bit. Replacing - # by long all the time is not working on some 32bit platforms - # (see Issue 1094, Issue 1098) - self.emit(SIGNAL('close_file(QString,int)'), str(id(self)), index) - - if not self.data and self.is_closable: - # editortabwidget is empty: removing it - # (if it's not the first editortabwidget) - self.close() - self.emit(SIGNAL('opened_files_list_changed()')) - self.emit(SIGNAL('update_code_analysis_actions()')) - self._refresh_outlineexplorer() - self.emit(SIGNAL('refresh_file_dependent_actions()')) - self.emit(SIGNAL('update_plugin_title()')) - editor = self.get_current_editor() - if editor: - editor.setFocus() - - if new_index is not None: - if index < new_index: - new_index -= 1 - self.set_stack_index(new_index) - if self.get_stack_count() == 0: - self.emit(SIGNAL('sig_new_file()')) - return False - return is_ok - - def close_all_files(self): - """Close all opened scripts""" - while self.close_file(): - pass - - - #------ Save - def save_if_changed(self, cancelable=False, index=None): - """Ask user to save file if modified""" - if index is None: - indexes = list(range(self.get_stack_count())) - else: - indexes = [index] - buttons = QMessageBox.Yes | QMessageBox.No - if cancelable: - buttons |= QMessageBox.Cancel - unsaved_nb = 0 - for index in indexes: - if self.data[index].editor.document().isModified(): - unsaved_nb += 1 - if not unsaved_nb: - # No file to save - return True - if unsaved_nb > 1: - buttons |= QMessageBox.YesAll | QMessageBox.NoAll - yes_all = False - for index in indexes: - self.set_stack_index(index) - finfo = self.data[index] - if finfo.filename == self.tempfile_path or yes_all: - if not self.save(): - return False - elif finfo.editor.document().isModified(): - answer = QMessageBox.question(self, self.title, - _("%s has been modified." - "
    Do you want to save changes?" - ) % osp.basename(finfo.filename), - buttons) - if answer == QMessageBox.Yes: - if not self.save(): - return False - elif answer == QMessageBox.YesAll: - if not self.save(): - return False - yes_all = True - elif answer == QMessageBox.NoAll: - return True - elif answer == QMessageBox.Cancel: - return False - return True - - def save(self, index=None, force=False): - """Save file""" - if index is None: - # Save the currently edited file - if not self.get_stack_count(): - return - index = self.get_stack_index() - - finfo = self.data[index] - if not finfo.editor.document().isModified() and not force: - return True - if not osp.isfile(finfo.filename) and not force: - # File has not been saved yet - return self.save_as(index=index) - if self.always_remove_trailing_spaces: - self.remove_trailing_spaces(index) - txt = to_text_string(finfo.editor.get_text_with_eol()) - try: - finfo.encoding = encoding.write(txt, finfo.filename, - finfo.encoding) - finfo.newly_created = False - self.emit(SIGNAL('encoding_changed(QString)'), finfo.encoding) - finfo.lastmodified = QFileInfo(finfo.filename).lastModified() - - # We pass self object ID as a QString, because otherwise it would - # depend on the platform: long for 64bit, int for 32bit. Replacing - # by long all the time is not working on some 32bit platforms - # (see Issue 1094, Issue 1098) - self.emit(SIGNAL('file_saved(QString,int,QString)'), - str(id(self)), index, finfo.filename) - - finfo.editor.document().setModified(False) - self.modification_changed(index=index) - self.analyze_script(index) - self.introspector.validate() - - #XXX CodeEditor-only: re-scan the whole text to rebuild outline - # explorer data from scratch (could be optimized because - # rehighlighting text means searching for all syntax coloring - # patterns instead of only searching for class/def patterns which - # would be sufficient for outline explorer data. - finfo.editor.rehighlight() - - self._refresh_outlineexplorer(index) - return True - except EnvironmentError as error: - QMessageBox.critical(self, _("Save"), - _("Unable to save script '%s'" - "

    Error message:
    %s" - ) % (osp.basename(finfo.filename), - str(error))) - return False - - def file_saved_in_other_editorstack(self, index, filename): - """ - File was just saved in another editorstack, let's synchronize! - This avoid file to be automatically reloaded - - Filename is passed in case file was just saved as another name - """ - finfo = self.data[index] - finfo.newly_created = False - finfo.filename = to_text_string(filename) - finfo.lastmodified = QFileInfo(finfo.filename).lastModified() - - def select_savename(self, original_filename): - selectedfilter = get_filter(EDIT_FILETYPES, - osp.splitext(original_filename)[1]) - self.emit(SIGNAL('redirect_stdio(bool)'), False) - filename, _selfilter = getsavefilename(self, _("Save Python script"), - original_filename, EDIT_FILTERS, selectedfilter) - self.emit(SIGNAL('redirect_stdio(bool)'), True) - if filename: - return osp.normpath(filename) - - def save_as(self, index=None): - """Save file as...""" - if index is None: - # Save the currently edited file - index = self.get_stack_index() - finfo = self.data[index] - filename = self.select_savename(finfo.filename) - if filename: - ao_index = self.has_filename(filename) - # Note: ao_index == index --> saving an untitled file - if ao_index and ao_index != index: - if not self.close_file(ao_index): - return - if ao_index < index: - index -= 1 - - new_index = self.rename_in_data(index, new_filename=filename) - - # We pass self object ID as a QString, because otherwise it would - # depend on the platform: long for 64bit, int for 32bit. Replacing - # by long all the time is not working on some 32bit platforms - # (see Issue 1094, Issue 1098) - self.emit(SIGNAL('file_renamed_in_data(QString,int,QString)'), - str(id(self)), index, filename) - - ok = self.save(index=new_index, force=True) - self.refresh(new_index) - self.set_stack_index(new_index) - return ok - else: - return False - - def save_all(self): - """Save all opened files""" - folders = set() - for index in range(self.get_stack_count()): - if self.data[index].editor.document().isModified(): - folders.add(osp.dirname(self.data[index].filename)) - self.save(index) - - #------ Update UI - def start_stop_analysis_timer(self): - self.is_analysis_done = False - if self.realtime_analysis_enabled: - self.analysis_timer.stop() - self.analysis_timer.start() - - def analyze_script(self, index=None): - """Analyze current script with pyflakes + find todos""" - if self.is_analysis_done: - return - if index is None: - index = self.get_stack_index() - if self.data: - finfo = self.data[index] - run_pyflakes, run_pep8 = self.pyflakes_enabled, self.pep8_enabled - if run_pyflakes or run_pep8: - finfo.run_code_analysis(run_pyflakes, run_pep8) - if self.todolist_enabled: - finfo.run_todo_finder() - self.is_analysis_done = True - - def set_analysis_results(self, index, analysis_results): - """Synchronize analysis results between editorstacks""" - self.data[index].set_analysis_results(analysis_results) - - def get_analysis_results(self): - if self.data: - return self.data[self.get_stack_index()].analysis_results - - def set_todo_results(self, index, todo_results): - """Synchronize todo results between editorstacks""" - self.data[index].set_todo_results(todo_results) - - def get_todo_results(self): - if self.data: - return self.data[self.get_stack_index()].todo_results - - def current_changed(self, index): - """Stack index has changed""" -# count = self.get_stack_count() -# for btn in (self.filelist_btn, self.previous_btn, self.next_btn): -# btn.setEnabled(count > 1) - - editor = self.get_current_editor() - if index != -1: - editor.setFocus() - if DEBUG_EDITOR: - print("setfocusto:", editor, file=STDOUT) - else: - self.emit(SIGNAL('reset_statusbar()')) - self.emit(SIGNAL('opened_files_list_changed()')) - - # Index history management - id_list = [id(self.tabs.widget(_i)) - for _i in range(self.tabs.count())] - for _id in self.stack_history[:]: - if _id not in id_list: - self.stack_history.pop(self.stack_history.index(_id)) - current_id = id(self.tabs.widget(index)) - while current_id in self.stack_history: - self.stack_history.pop(self.stack_history.index(current_id)) - self.stack_history.append(current_id) - if DEBUG_EDITOR: - print("current_changed:", index, self.data[index].editor, end=' ', file=STDOUT) - print(self.data[index].editor.get_document_id(), file=STDOUT) - - self.emit(SIGNAL('update_plugin_title()')) - if editor is not None: - self.emit(SIGNAL('current_file_changed(QString,int)'), - self.data[index].filename, editor.get_position('cursor')) - - def _get_previous_file_index(self): - if len(self.stack_history) > 1: - last = len(self.stack_history)-1 - w_id = self.stack_history.pop(last) - self.stack_history.insert(0, w_id) - last_id = self.stack_history[last] - for _i in range(self.tabs.count()): - if id(self.tabs.widget(_i)) == last_id: - return _i - - def go_to_previous_file(self): - """Ctrl+Tab""" - prev_index = self._get_previous_file_index() - if prev_index is not None: - self.set_stack_index(prev_index) - elif len(self.stack_history) == 0 and self.get_stack_count(): - self.stack_history = [id(self.tabs.currentWidget())] - - def go_to_next_file(self): - """Ctrl+Shift+Tab""" - if len(self.stack_history) > 1: - last = len(self.stack_history)-1 - w_id = self.stack_history.pop(0) - self.stack_history.append(w_id) - last_id = self.stack_history[last] - for _i in range(self.tabs.count()): - if id(self.tabs.widget(_i)) == last_id: - self.set_stack_index(_i) - break - elif len(self.stack_history) == 0 and self.get_stack_count(): - self.stack_history = [id(self.tabs.currentWidget())] - - def focus_changed(self): - """Editor focus has changed""" - fwidget = QApplication.focusWidget() - for finfo in self.data: - if fwidget is finfo.editor: - self.refresh() - self.emit(SIGNAL("editor_focus_changed()")) - - def _refresh_outlineexplorer(self, index=None, update=True, clear=False): - """Refresh outline explorer panel""" - oe = self.outlineexplorer - if oe is None: - return - if index is None: - index = self.get_stack_index() - enable = False - if self.data: - finfo = self.data[index] - if finfo.editor.is_python(): - enable = True - oe.setEnabled(True) - oe.set_current_editor(finfo.editor, finfo.filename, - update=update, clear=clear) - if not enable: - oe.setEnabled(False) - - def __refresh_statusbar(self, index): - """Refreshing statusbar widgets""" - finfo = self.data[index] - self.emit(SIGNAL('encoding_changed(QString)'), finfo.encoding) - # Refresh cursor position status: - line, index = finfo.editor.get_cursor_line_column() - self.emit(SIGNAL('editor_cursor_position_changed(int,int)'), - line, index) - - def __refresh_readonly(self, index): - finfo = self.data[index] - read_only = not QFileInfo(finfo.filename).isWritable() - if not osp.isfile(finfo.filename): - # This is an 'untitledX.py' file (newly created) - read_only = False - finfo.editor.setReadOnly(read_only) - self.emit(SIGNAL('readonly_changed(bool)'), read_only) - - def __check_file_status(self, index): - """Check if file has been changed in any way outside Spyder: - 1. removed, moved or renamed outside Spyder - 2. modified outside Spyder""" - if self.__file_status_flag: - # Avoid infinite loop: when the QMessageBox.question pops, it - # gets focus and then give it back to the CodeEditor instance, - # triggering a refresh cycle which calls this method - return - self.__file_status_flag = True - - finfo = self.data[index] - name = osp.basename(finfo.filename) - - if finfo.newly_created: - # File was just created (not yet saved): do nothing - # (do not return because of the clean-up at the end of the method) - pass - - elif not osp.isfile(finfo.filename): - # File doesn't exist (removed, moved or offline): - answer = QMessageBox.warning(self, self.title, - _("%s is unavailable " - "(this file may have been removed, moved " - "or renamed outside Spyder)." - "
    Do you want to close it?") % name, - QMessageBox.Yes | QMessageBox.No) - if answer == QMessageBox.Yes: - self.close_file(index) - else: - finfo.newly_created = True - finfo.editor.document().setModified(True) - self.modification_changed(index=index) - - else: - # Else, testing if it has been modified elsewhere: - lastm = QFileInfo(finfo.filename).lastModified() - if to_text_string(lastm.toString()) \ - != to_text_string(finfo.lastmodified.toString()): - if finfo.editor.document().isModified(): - answer = QMessageBox.question(self, - self.title, - _("%s has been modified outside Spyder." - "
    Do you want to reload it and lose all " - "your changes?") % name, - QMessageBox.Yes | QMessageBox.No) - if answer == QMessageBox.Yes: - self.reload(index) - else: - finfo.lastmodified = lastm - else: - self.reload(index) - - # Finally, resetting temporary flag: - self.__file_status_flag = False - - def refresh(self, index=None): - """Refresh tabwidget""" - if index is None: - index = self.get_stack_index() - # Set current editor - if self.get_stack_count(): - index = self.get_stack_index() - finfo = self.data[index] - editor = finfo.editor - editor.setFocus() - self._refresh_outlineexplorer(index, update=False) - self.emit(SIGNAL('update_code_analysis_actions()')) - self.__refresh_statusbar(index) - self.__refresh_readonly(index) - self.__check_file_status(index) - self.emit(SIGNAL('update_plugin_title()')) - else: - editor = None - # Update the modification-state-dependent parameters - self.modification_changed() - # Update FindReplace binding - self.find_widget.set_editor(editor, refresh=False) - - def modification_changed(self, state=None, index=None, editor_id=None): - """ - Current editor's modification state has changed - --> change tab title depending on new modification state - --> enable/disable save/save all actions - """ - if editor_id is not None: - for index, _finfo in enumerate(self.data): - if id(_finfo.editor) == editor_id: - break - # This must be done before refreshing save/save all actions: - # (otherwise Save/Save all actions will always be enabled) - self.emit(SIGNAL('opened_files_list_changed()')) - # -- - if index is None: - index = self.get_stack_index() - if index == -1: - return - finfo = self.data[index] - if state is None: - state = finfo.editor.document().isModified() - self.set_stack_title(index, state) - # Toggle save/save all actions state - self.save_action.setEnabled(state) - self.emit(SIGNAL('refresh_save_all_action()')) - # Refreshing eol mode - eol_chars = finfo.editor.get_line_separator() - os_name = sourcecode.get_os_name_from_eol_chars(eol_chars) - self.emit(SIGNAL('refresh_eol_chars(QString)'), os_name) - - - #------ Load, reload - def reload(self, index): - """Reload file from disk""" - finfo = self.data[index] - txt, finfo.encoding = encoding.read(finfo.filename) - finfo.lastmodified = QFileInfo(finfo.filename).lastModified() - position = finfo.editor.get_position('cursor') - finfo.editor.set_text(txt) - finfo.editor.document().setModified(False) - finfo.editor.set_cursor_position(position) - self.introspector.validate() - - #XXX CodeEditor-only: re-scan the whole text to rebuild outline - # explorer data from scratch (could be optimized because - # rehighlighting text means searching for all syntax coloring - # patterns instead of only searching for class/def patterns which - # would be sufficient for outline explorer data. - finfo.editor.rehighlight() - - self._refresh_outlineexplorer(index) - - def revert(self): - """Revert file from disk""" - index = self.get_stack_index() - finfo = self.data[index] - filename = finfo.filename - if finfo.editor.document().isModified(): - answer = QMessageBox.warning(self, self.title, - _("All changes to %s will be lost." - "
    Do you want to revert file from disk?" - ) % osp.basename(filename), - QMessageBox.Yes|QMessageBox.No) - if answer != QMessageBox.Yes: - return - self.reload(index) - - def create_new_editor(self, fname, enc, txt, set_current, new=False, - cloned_from=None): - """ - Create a new editor instance - Returns finfo object (instead of editor as in previous releases) - """ - editor = codeeditor.CodeEditor(self) - introspector = self.introspector - self.connect(editor, SIGNAL("get_completions(bool)"), - introspector.get_completions) - self.connect(editor, SIGNAL("show_object_info(int)"), - introspector.show_object_info) - self.connect(editor, SIGNAL("go_to_definition(int)"), - introspector.go_to_definition) - - finfo = FileInfo(fname, enc, editor, new, self.threadmanager, - self.introspector) - self.add_to_data(finfo, set_current) - self.connect(finfo, SIGNAL( - "send_to_inspector(QString,QString,QString,QString,bool)"), - self.send_to_inspector) - self.connect(finfo, SIGNAL('analysis_results_changed()'), - lambda: self.emit(SIGNAL('analysis_results_changed()'))) - self.connect(finfo, SIGNAL('todo_results_changed()'), - lambda: self.emit(SIGNAL('todo_results_changed()'))) - self.connect(finfo, SIGNAL("edit_goto(QString,int,QString)"), - lambda fname, lineno, name: - self.emit(SIGNAL("edit_goto(QString,int,QString)"), - fname, lineno, name)) - self.connect(finfo, SIGNAL("save_breakpoints(QString,QString)"), - lambda s1, s2: - self.emit(SIGNAL("save_breakpoints(QString,QString)"), - s1, s2)) - self.connect(editor, SIGNAL("run_selection()"), self.run_selection) - self.connect(editor, SIGNAL("run_cell()"), self.run_cell) - self.connect(editor, SIGNAL('run_cell_and_advance()'), - self.run_cell_and_advance) - editor.sig_new_file.connect(lambda s: self.parent().plugin.new(text=s)) - language = get_file_language(fname, txt) - editor.setup_editor( - linenumbers=self.linenumbers_enabled, - show_blanks=self.blanks_enabled, - edge_line=self.edgeline_enabled, - edge_line_column=self.edgeline_column, language=language, - markers=self.has_markers(), font=self.default_font, - color_scheme=self.color_scheme, - wrap=self.wrap_enabled, tab_mode=self.tabmode_enabled, - intelligent_backspace=self.intelligent_backspace_enabled, - highlight_current_line=self.highlight_current_line_enabled, - highlight_current_cell=self.highlight_current_cell_enabled, - occurence_highlighting=self.occurence_highlighting_enabled, - occurence_timeout=self.occurence_highlighting_timeout, - codecompletion_auto=self.codecompletion_auto_enabled, - codecompletion_case=self.codecompletion_case_enabled, - codecompletion_enter=self.codecompletion_enter_enabled, - calltips=self.calltips_enabled, - go_to_definition=self.go_to_definition_enabled, - close_parentheses=self.close_parentheses_enabled, - close_quotes=self.close_quotes_enabled, - add_colons=self.add_colons_enabled, - auto_unindent=self.auto_unindent_enabled, - indent_chars=self.indent_chars, - tab_stop_width=self.tab_stop_width, - cloned_from=cloned_from) - if cloned_from is None: - editor.set_text(txt) - editor.document().setModified(False) - self.connect(finfo, SIGNAL('text_changed_at(QString,int)'), - lambda fname, position: - self.emit(SIGNAL('text_changed_at(QString,int)'), - fname, position)) - self.connect(editor, SIGNAL('cursorPositionChanged(int,int)'), - self.editor_cursor_position_changed) - self.connect(editor, SIGNAL('textChanged()'), - self.start_stop_analysis_timer) - self.connect(editor, SIGNAL('modificationChanged(bool)'), - lambda state: self.modification_changed(state, - editor_id=id(editor))) - self.connect(editor, SIGNAL("focus_in()"), self.focus_changed) - self.connect(editor, SIGNAL('zoom_in()'), - lambda: self.emit(SIGNAL('zoom_in()'))) - self.connect(editor, SIGNAL('zoom_out()'), - lambda: self.emit(SIGNAL('zoom_out()'))) - self.connect(editor, SIGNAL('zoom_reset()'), - lambda: self.emit(SIGNAL('zoom_reset()'))) - if self.outlineexplorer is not None: - # Removing editor reference from outline explorer settings: - self.connect(editor, SIGNAL("destroyed()"), - lambda obj=editor: - self.outlineexplorer.remove_editor(obj)) - - self.find_widget.set_editor(editor) - - self.emit(SIGNAL('refresh_file_dependent_actions()')) - self.modification_changed(index=self.data.index(finfo)) - - return finfo - - def editor_cursor_position_changed(self, line, index): - """Cursor position of one of the editor in the stack has changed""" - self.emit(SIGNAL('editor_cursor_position_changed(int,int)'), - line, index) - - def send_to_inspector(self, qstr1, qstr2=None, qstr3=None, - qstr4=None, force=False): - """qstr1: obj_text, qstr2: argpspec, qstr3: note, qstr4: doc_text""" - if not force and not self.inspector_enabled: - return - if self.inspector is not None \ - and (force or self.inspector.dockwidget.isVisible()): - # ObjectInspector widget exists and is visible - if qstr4 is None: - self.inspector.set_object_text(qstr1, ignore_unknown=True, - force_refresh=force) - else: - objtxt = to_text_string(qstr1) - name = objtxt.split('.')[-1] - argspec = to_text_string(qstr2) - note = to_text_string(qstr3) - docstring = to_text_string(qstr4) - doc = {'obj_text': objtxt, 'name': name, 'argspec': argspec, - 'note': note, 'docstring': docstring} - self.inspector.set_editor_doc(doc, force_refresh=force) - editor = self.get_current_editor() - editor.setFocus() - - def new(self, filename, encoding, text, default_content=False): - """ - Create new filename with *encoding* and *text* - """ - finfo = self.create_new_editor(filename, encoding, text, - set_current=False, new=True) - finfo.editor.set_cursor_position('eof') - finfo.editor.insert_text(os.linesep) - if default_content: - finfo.default = True - finfo.editor.document().setModified(False) - return finfo - - def load(self, filename, set_current=True): - """ - Load filename, create an editor instance and return it - *Warning* This is loading file, creating editor but not executing - the source code analysis -- the analysis must be done by the editor - plugin (in case multiple editorstack instances are handled) - """ - filename = osp.abspath(to_text_string(filename)) - self.emit(SIGNAL('starting_long_process(QString)'), - _("Loading %s...") % filename) - text, enc = encoding.read(filename) - finfo = self.create_new_editor(filename, enc, text, set_current) - index = self.data.index(finfo) - self._refresh_outlineexplorer(index, update=True) - self.emit(SIGNAL('ending_long_process(QString)'), "") - if self.isVisible() and self.checkeolchars_enabled \ - and sourcecode.has_mixed_eol_chars(text): - name = osp.basename(filename) - QMessageBox.warning(self, self.title, - _("%s contains mixed end-of-line " - "characters.
    Spyder will fix this " - "automatically.") % name, - QMessageBox.Ok) - self.set_os_eol_chars(index) - self.is_analysis_done = False - return finfo - - def set_os_eol_chars(self, index=None): - if index is None: - index = self.get_stack_index() - finfo = self.data[index] - eol_chars = sourcecode.get_eol_chars_from_os_name(os.name) - finfo.editor.set_eol_chars(eol_chars) - finfo.editor.document().setModified(True) - - def remove_trailing_spaces(self, index=None): - """Remove trailing spaces""" - if index is None: - index = self.get_stack_index() - finfo = self.data[index] - finfo.editor.remove_trailing_spaces() - - def fix_indentation(self, index=None): - """Replace tab characters by spaces""" - if index is None: - index = self.get_stack_index() - finfo = self.data[index] - finfo.editor.fix_indentation() - - #------ Run - def run_selection(self): - """Run selected text or current line in console""" - text = self.get_current_editor().get_selection_as_executable_code() - if not text: - line = self.get_current_editor().get_current_line() - text = line.lstrip() - self.emit(SIGNAL('exec_in_extconsole(QString,bool)'), text, - self.focus_to_editor) - - def run_cell(self): - """Run current cell""" - text = self.get_current_editor().get_cell_as_executable_code() - finfo = self.get_current_finfo() - if finfo.editor.is_python() and text: - self.emit(SIGNAL('exec_in_extconsole(QString,bool)'), - text, self.focus_to_editor) - - def run_cell_and_advance(self): - """Run current cell and advance to the next one""" - self.run_cell() - if self.focus_to_editor: - self.get_current_editor().go_to_next_cell() - else: - term = QApplication.focusWidget() - self.get_current_editor().go_to_next_cell() - term.setFocus() - - #------ Drag and drop - def dragEnterEvent(self, event): - """Reimplement Qt method - Inform Qt about the types of data that the widget accepts""" - source = event.mimeData() - # The second check is necessary on Windows, where source.hasUrls() - # can return True but source.urls() is [] - if source.hasUrls() and source.urls(): - if mimedata2url(source, extlist=EDIT_EXT): - event.acceptProposedAction() - else: - all_urls = mimedata2url(source) - text = [encoding.is_text_file(url) for url in all_urls] - if any(text): - event.acceptProposedAction() - else: - event.ignore() - elif source.hasText(): - event.acceptProposedAction() - elif os.name == 'nt': - # This covers cases like dragging from compressed files, - # which can be opened by the Editor if they are plain - # text, but doesn't come with url info. - # Fixes Issue 2032 - event.acceptProposedAction() - else: - event.ignore() - - def dropEvent(self, event): - """Reimplement Qt method - Unpack dropped data and handle it""" - source = event.mimeData() - if source.hasUrls(): - files = mimedata2url(source) - files = [f for f in files if encoding.is_text_file(f)] - supported_files = mimedata2url(source, extlist=EDIT_EXT) - files = set(files or []) | set(supported_files or []) - for fname in files: - self.emit(SIGNAL('plugin_load(QString)'), fname) - elif source.hasText(): - editor = self.get_current_editor() - if editor is not None: - editor.insert_text( source.text() ) - event.acceptProposedAction() - - -class EditorSplitter(QSplitter): - def __init__(self, parent, plugin, menu_actions, first=False, - register_editorstack_cb=None, unregister_editorstack_cb=None): - QSplitter.__init__(self, parent) - self.setAttribute(Qt.WA_DeleteOnClose) - self.setChildrenCollapsible(False) - - self.toolbar_list = None - self.menu_list = None - - self.plugin = plugin - - if register_editorstack_cb is None: - register_editorstack_cb = self.plugin.register_editorstack - self.register_editorstack_cb = register_editorstack_cb - if unregister_editorstack_cb is None: - unregister_editorstack_cb = self.plugin.unregister_editorstack - self.unregister_editorstack_cb = unregister_editorstack_cb - - self.menu_actions = menu_actions - self.editorstack = EditorStack(self, menu_actions) - self.register_editorstack_cb(self.editorstack) - if not first: - self.plugin.clone_editorstack(editorstack=self.editorstack) - self.connect(self.editorstack, SIGNAL("destroyed()"), - lambda: self.editorstack_closed()) - self.connect(self.editorstack, SIGNAL("split_vertically()"), - lambda: self.split(orientation=Qt.Vertical)) - self.connect(self.editorstack, SIGNAL("split_horizontally()"), - lambda: self.split(orientation=Qt.Horizontal)) - self.addWidget(self.editorstack) - - def closeEvent(self, event): - QSplitter.closeEvent(self, event) - if is_pyqt46: - self.emit(SIGNAL('destroyed()')) - - def __give_focus_to_remaining_editor(self): - focus_widget = self.plugin.get_focus_widget() - if focus_widget is not None: - focus_widget.setFocus() - - def editorstack_closed(self): - if DEBUG_EDITOR: - print("method 'editorstack_closed':", file=STDOUT) - print(" self :", self, file=STDOUT) -# print >>STDOUT, " sender:", self.sender() - self.unregister_editorstack_cb(self.editorstack) - self.editorstack = None - try: - close_splitter = self.count() == 1 - except RuntimeError: - # editorsplitter has been destroyed (happens when closing a - # EditorMainWindow instance) - return - if close_splitter: - # editorstack just closed was the last widget in this QSplitter - self.close() - return - self.__give_focus_to_remaining_editor() - - def editorsplitter_closed(self): - if DEBUG_EDITOR: - print("method 'editorsplitter_closed':", file=STDOUT) - print(" self :", self, file=STDOUT) -# print >>STDOUT, " sender:", self.sender() - try: - close_splitter = self.count() == 1 and self.editorstack is None - except RuntimeError: - # editorsplitter has been destroyed (happens when closing a - # EditorMainWindow instance) - return - if close_splitter: - # editorsplitter just closed was the last widget in this QSplitter - self.close() - return - elif self.count() == 2 and self.editorstack: - # back to the initial state: a single editorstack instance, - # as a single widget in this QSplitter: orientation may be changed - self.editorstack.reset_orientation() - self.__give_focus_to_remaining_editor() - - def split(self, orientation=Qt.Vertical): - self.setOrientation(orientation) - self.editorstack.set_orientation(orientation) - editorsplitter = EditorSplitter(self.parent(), self.plugin, - self.menu_actions, - register_editorstack_cb=self.register_editorstack_cb, - unregister_editorstack_cb=self.unregister_editorstack_cb) - self.addWidget(editorsplitter) - self.connect(editorsplitter, SIGNAL("destroyed()"), - lambda: self.editorsplitter_closed()) - current_editor = editorsplitter.editorstack.get_current_editor() - if current_editor is not None: - current_editor.setFocus() - - def iter_editorstacks(self): - editorstacks = [(self.widget(0), self.orientation())] - if self.count() > 1: - editorsplitter = self.widget(1) - editorstacks += editorsplitter.iter_editorstacks() - return editorstacks - - def get_layout_settings(self): - """Return layout state""" - splitsettings = [] - for editorstack, orientation in self.iter_editorstacks(): - clines = [finfo.editor.get_cursor_line_number() - for finfo in editorstack.data] - cfname = editorstack.get_current_filename() - splitsettings.append((orientation == Qt.Vertical, cfname, clines)) - return dict(hexstate=qbytearray_to_str(self.saveState()), - sizes=self.sizes(), splitsettings=splitsettings) - - def set_layout_settings(self, settings): - """Restore layout state""" - splitsettings = settings.get('splitsettings') - if splitsettings is None: - return - splitter = self - editor = None - for index, (is_vertical, cfname, clines) in enumerate(splitsettings): - if index > 0: - splitter.split(Qt.Vertical if is_vertical else Qt.Horizontal) - splitter = splitter.widget(1) - editorstack = splitter.widget(0) - for index, finfo in enumerate(editorstack.data): - editor = finfo.editor - editor.go_to_line(clines[index]) - editorstack.set_current_filename(cfname) - hexstate = settings.get('hexstate') - if hexstate is not None: - self.restoreState( QByteArray().fromHex(str(hexstate)) ) - sizes = settings.get('sizes') - if sizes is not None: - self.setSizes(sizes) - if editor is not None: - editor.clearFocus() - editor.setFocus() - - -class EditorWidget(QSplitter): - def __init__(self, parent, plugin, menu_actions, show_fullpath, - fullpath_sorting, show_all_files, show_comments): - QSplitter.__init__(self, parent) - self.setAttribute(Qt.WA_DeleteOnClose) - - statusbar = parent.statusBar() # Create a status bar - self.readwrite_status = ReadWriteStatus(self, statusbar) - self.eol_status = EOLStatus(self, statusbar) - self.encoding_status = EncodingStatus(self, statusbar) - self.cursorpos_status = CursorPositionStatus(self, statusbar) - - self.editorstacks = [] - - self.plugin = plugin - - self.find_widget = FindReplace(self, enable_replace=True) - self.plugin.register_widget_shortcuts("Editor", self.find_widget) - self.find_widget.hide() - self.outlineexplorer = OutlineExplorerWidget(self, - show_fullpath=show_fullpath, - fullpath_sorting=fullpath_sorting, - show_all_files=show_all_files, - show_comments=show_comments) - self.connect(self.outlineexplorer, - SIGNAL("edit_goto(QString,int,QString)"), - lambda filenames, goto, word: - plugin.load(filenames=filenames, goto=goto, word=word, - editorwindow=self.parent())) - - editor_widgets = QWidget(self) - editor_layout = QVBoxLayout() - editor_layout.setContentsMargins(0, 0, 0, 0) - editor_widgets.setLayout(editor_layout) - editorsplitter = EditorSplitter(self, plugin, menu_actions, - register_editorstack_cb=self.register_editorstack, - unregister_editorstack_cb=self.unregister_editorstack) - self.editorsplitter = editorsplitter - editor_layout.addWidget(editorsplitter) - editor_layout.addWidget(self.find_widget) - - splitter = QSplitter(self) - splitter.setContentsMargins(0, 0, 0, 0) - splitter.addWidget(editor_widgets) - splitter.addWidget(self.outlineexplorer) - splitter.setStretchFactor(0, 5) - splitter.setStretchFactor(1, 1) - - # Refreshing outline explorer - editorsplitter.editorstack.initialize_outlineexplorer() - - def register_editorstack(self, editorstack): - self.editorstacks.append(editorstack) - if DEBUG_EDITOR: - print("EditorWidget.register_editorstack:", editorstack, file=STDOUT) - self.__print_editorstacks() - self.plugin.last_focus_editorstack[self.parent()] = editorstack - editorstack.set_closable( len(self.editorstacks) > 1 ) - editorstack.set_outlineexplorer(self.outlineexplorer) - editorstack.set_find_widget(self.find_widget) - self.connect(editorstack, SIGNAL('reset_statusbar()'), - self.readwrite_status.hide) - self.connect(editorstack, SIGNAL('reset_statusbar()'), - self.encoding_status.hide) - self.connect(editorstack, SIGNAL('reset_statusbar()'), - self.cursorpos_status.hide) - self.connect(editorstack, SIGNAL('readonly_changed(bool)'), - self.readwrite_status.readonly_changed) - self.connect(editorstack, SIGNAL('encoding_changed(QString)'), - self.encoding_status.encoding_changed) - self.connect(editorstack, - SIGNAL('editor_cursor_position_changed(int,int)'), - self.cursorpos_status.cursor_position_changed) - self.connect(editorstack, SIGNAL('refresh_eol_chars(QString)'), - self.eol_status.eol_changed) - self.plugin.register_editorstack(editorstack) - oe_btn = create_toolbutton(self) - oe_btn.setDefaultAction(self.outlineexplorer.visibility_action) - editorstack.add_corner_widgets_to_tabbar([5, oe_btn]) - - def __print_editorstacks(self): - print("%d editorstack(s) in editorwidget:" \ - % len(self.editorstacks), file=STDOUT) - for edst in self.editorstacks: - print(" ", edst, file=STDOUT) - - def unregister_editorstack(self, editorstack): - if DEBUG_EDITOR: - print("EditorWidget.unregister_editorstack:", editorstack, file=STDOUT) - self.plugin.unregister_editorstack(editorstack) - self.editorstacks.pop(self.editorstacks.index(editorstack)) - if DEBUG_EDITOR: - self.__print_editorstacks() - - -class EditorMainWindow(QMainWindow): - def __init__(self, plugin, menu_actions, toolbar_list, menu_list, - show_fullpath, fullpath_sorting, show_all_files, - show_comments): - QMainWindow.__init__(self) - self.setAttribute(Qt.WA_DeleteOnClose) - - self.window_size = None - - self.editorwidget = EditorWidget(self, plugin, menu_actions, - show_fullpath, fullpath_sorting, - show_all_files, show_comments) - self.setCentralWidget(self.editorwidget) - - # Give focus to current editor to update/show all status bar widgets - editorstack = self.editorwidget.editorsplitter.editorstack - editor = editorstack.get_current_editor() - if editor is not None: - editor.setFocus() - - self.setWindowTitle("Spyder - %s" % plugin.windowTitle()) - self.setWindowIcon(plugin.windowIcon()) - - if toolbar_list: - toolbars = [] - for title, actions in toolbar_list: - toolbar = self.addToolBar(title) - toolbar.setObjectName(str(id(toolbar))) - add_actions(toolbar, actions) - toolbars.append(toolbar) - if menu_list: - quit_action = create_action(self, _("Close window"), - icon="close_panel.png", - tip=_("Close this window"), - triggered=self.close) - menus = [] - for index, (title, actions) in enumerate(menu_list): - menu = self.menuBar().addMenu(title) - if index == 0: - # File menu - add_actions(menu, actions+[None, quit_action]) - else: - add_actions(menu, actions) - menus.append(menu) - - def resizeEvent(self, event): - """Reimplement Qt method""" - if not self.isMaximized() and not self.isFullScreen(): - self.window_size = self.size() - QMainWindow.resizeEvent(self, event) - - def closeEvent(self, event): - """Reimplement Qt method""" - QMainWindow.closeEvent(self, event) - if is_pyqt46: - self.emit(SIGNAL('destroyed()')) - for editorstack in self.editorwidget.editorstacks[:]: - if DEBUG_EDITOR: - print("--> destroy_editorstack:", editorstack, file=STDOUT) - editorstack.emit(SIGNAL('destroyed()')) - - def get_layout_settings(self): - """Return layout state""" - splitsettings = self.editorwidget.editorsplitter.get_layout_settings() - return dict(size=(self.window_size.width(), self.window_size.height()), - pos=(self.pos().x(), self.pos().y()), - is_maximized=self.isMaximized(), - is_fullscreen=self.isFullScreen(), - hexstate=qbytearray_to_str(self.saveState()), - splitsettings=splitsettings) - - def set_layout_settings(self, settings): - """Restore layout state""" - size = settings.get('size') - if size is not None: - self.resize( QSize(*size) ) - self.window_size = self.size() - pos = settings.get('pos') - if pos is not None: - self.move( QPoint(*pos) ) - hexstate = settings.get('hexstate') - if hexstate is not None: - self.restoreState( QByteArray().fromHex(str(hexstate)) ) - if settings.get('is_maximized'): - self.setWindowState(Qt.WindowMaximized) - if settings.get('is_fullscreen'): - self.setWindowState(Qt.WindowFullScreen) - splitsettings = settings.get('splitsettings') - if splitsettings is not None: - self.editorwidget.editorsplitter.set_layout_settings(splitsettings) - - -class EditorPluginExample(QSplitter): - def __init__(self): - QSplitter.__init__(self) - - menu_actions = [] - - self.editorstacks = [] - self.editorwindows = [] - - self.last_focus_editorstack = {} # fake - - self.find_widget = FindReplace(self, enable_replace=True) - self.outlineexplorer = OutlineExplorerWidget(self, show_fullpath=False, - show_all_files=False) - self.connect(self.outlineexplorer, - SIGNAL("edit_goto(QString,int,QString)"), - self.go_to_file) - - editor_widgets = QWidget(self) - editor_layout = QVBoxLayout() - editor_layout.setContentsMargins(0, 0, 0, 0) - editor_widgets.setLayout(editor_layout) - editor_layout.addWidget(EditorSplitter(self, self, menu_actions, - first=True)) - editor_layout.addWidget(self.find_widget) - - self.setContentsMargins(0, 0, 0, 0) - self.addWidget(editor_widgets) - self.addWidget(self.outlineexplorer) - - self.setStretchFactor(0, 5) - self.setStretchFactor(1, 1) - - self.menu_actions = menu_actions - self.toolbar_list = None - self.menu_list = None - self.setup_window([], []) - - def go_to_file(self, fname, lineno, text): - editorstack = self.editorstacks[0] - editorstack.set_current_filename(to_text_string(fname)) - editor = editorstack.get_current_editor() - editor.go_to_line(lineno, word=text) - - def closeEvent(self, event): - for win in self.editorwindows[:]: - win.close() - if DEBUG_EDITOR: - print(len(self.editorwindows), ":", self.editorwindows, file=STDOUT) - print(len(self.editorstacks), ":", self.editorstacks, file=STDOUT) - - event.accept() - - def load(self, fname): - QApplication.processEvents() - editorstack = self.editorstacks[0] - editorstack.load(fname) - editorstack.analyze_script() - - def register_editorstack(self, editorstack): - if DEBUG_EDITOR: - print("FakePlugin.register_editorstack:", editorstack, file=STDOUT) - self.editorstacks.append(editorstack) - if self.isAncestorOf(editorstack): - # editorstack is a child of the Editor plugin - editorstack.set_fullpath_sorting_enabled(True) - editorstack.set_closable( len(self.editorstacks) > 1 ) - editorstack.set_outlineexplorer(self.outlineexplorer) - editorstack.set_find_widget(self.find_widget) - oe_btn = create_toolbutton(self) - oe_btn.setDefaultAction(self.outlineexplorer.visibility_action) - editorstack.add_corner_widgets_to_tabbar([5, oe_btn]) - - action = QAction(self) - editorstack.set_io_actions(action, action, action, action) - font = QFont("Courier New") - font.setPointSize(10) - editorstack.set_default_font(font, color_scheme='Spyder') - - self.connect(editorstack, SIGNAL('close_file(QString,int)'), - self.close_file_in_all_editorstacks) - self.connect(editorstack, SIGNAL('file_saved(QString,int,QString)'), - self.file_saved_in_editorstack) - self.connect(editorstack, - SIGNAL('file_renamed_in_data(QString,int,QString)'), - self.file_renamed_in_data_in_editorstack) - - self.connect(editorstack, SIGNAL("create_new_window()"), - self.create_new_window) - self.connect(editorstack, SIGNAL('plugin_load(QString)'), - self.load) - - def unregister_editorstack(self, editorstack): - if DEBUG_EDITOR: - print("FakePlugin.unregister_editorstack:", editorstack, file=STDOUT) - self.editorstacks.pop(self.editorstacks.index(editorstack)) - - def clone_editorstack(self, editorstack): - editorstack.clone_from(self.editorstacks[0]) - - def setup_window(self, toolbar_list, menu_list): - self.toolbar_list = toolbar_list - self.menu_list = menu_list - - def create_new_window(self): - window = EditorMainWindow(self, self.menu_actions, - self.toolbar_list, self.menu_list, - show_fullpath=False, fullpath_sorting=True, - show_all_files=False, show_comments=True) - window.resize(self.size()) - window.show() - self.register_editorwindow(window) - self.connect(window, SIGNAL("destroyed()"), - lambda win=window: self.unregister_editorwindow(win)) - - def register_editorwindow(self, window): - if DEBUG_EDITOR: - print("register_editorwindowQObject*:", window, file=STDOUT) - self.editorwindows.append(window) - - def unregister_editorwindow(self, window): - if DEBUG_EDITOR: - print("unregister_editorwindow:", window, file=STDOUT) - self.editorwindows.pop(self.editorwindows.index(window)) - - def get_focus_widget(self): - pass - - @Slot(int, int) - def close_file_in_all_editorstacks(self, editorstack_id_str, index): - for editorstack in self.editorstacks: - if str(id(editorstack)) != editorstack_id_str: - editorstack.blockSignals(True) - editorstack.close_file(index, force=True) - editorstack.blockSignals(False) - - # This method is never called in this plugin example. It's here only - # to show how to use the file_saved signal (see above). - @Slot(int, int) - def file_saved_in_editorstack(self, editorstack_id_str, index, filename): - """A file was saved in editorstack, this notifies others""" - for editorstack in self.editorstacks: - if str(id(editorstack)) != editorstack_id_str: - editorstack.file_saved_in_other_editorstack(index, filename) - - # This method is never called in this plugin example. It's here only - # to show how to use the file_saved signal (see above). - @Slot(int, int) - def file_renamed_in_data_in_editorstack(self, editorstack_id_str, - index, filename): - """A file was renamed in data in editorstack, this notifies others""" - for editorstack in self.editorstacks: - if str(id(editorstack)) != editorstack_id_str: - editorstack.rename_in_data(index, filename) - - def register_widget_shortcuts(self, context, widget): - """Fake!""" - pass - -def test(): - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - test = EditorPluginExample() - test.resize(900, 700) - test.show() - import time - t0 = time.time() - test.load(__file__) - test.load("explorer.py") - test.load("dicteditor.py") - test.load("sourcecode/codeeditor.py") - test.load("../spyder.py") - print("Elapsed time: %.3f s" % (time.time()-t0)) - sys.exit(app.exec_()) - -if __name__ == "__main__": - test() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/editortools.py spyder-3.0.2+dfsg1/spyderlib/widgets/editortools.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/editortools.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/editortools.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,574 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Editor tools: outline explorer, etc.""" - -from __future__ import print_function - -import re -import os.path as osp - -from spyderlib.qt.QtGui import (QWidget, QTreeWidgetItem, QHBoxLayout, - QVBoxLayout) -from spyderlib.qt.QtCore import Qt, SIGNAL -from spyderlib.qt.compat import from_qvariant - -# Local import -from spyderlib.baseconfig import _, STDOUT -from spyderlib.utils.qthelpers import (get_icon, create_action, - create_toolbutton, set_item_user_text) -from spyderlib.widgets.onecolumntree import OneColumnTree -from spyderlib.py3compat import to_text_string - - -#=============================================================================== -# Class browser -#=============================================================================== -class PythonCFM(object): - """ - Collection of helpers to match functions and classes - for Python language - This has to be reimplemented for other languages for the outline explorer - to be supported (not implemented yet: outline explorer won't be populated - unless the current script is a Python script) - """ - def __get_name(self, statmt, text): - match = re.match(r'[\ ]*%s ([a-zA-Z0-9_]*)[\ ]*[\(|\:]' % statmt, text) - if match is not None: - return match.group(1) - - def get_function_name(self, text): - return self.__get_name('def', text) - - def get_class_name(self, text): - return self.__get_name('class', text) - - -class FileRootItem(QTreeWidgetItem): - def __init__(self, path, treewidget): - QTreeWidgetItem.__init__(self, treewidget, QTreeWidgetItem.Type) - self.path = path - self.setIcon(0, get_icon('python.png')) - self.setToolTip(0, path) - set_item_user_text(self, path) - - def set_path(self, path, fullpath): - self.path = path - self.set_text(fullpath) - - def set_text(self, fullpath): - self.setText(0, self.path if fullpath else osp.basename(self.path)) - -class TreeItem(QTreeWidgetItem): - """Class browser item base class""" - def __init__(self, name, line, parent, preceding): - if preceding is None: - QTreeWidgetItem.__init__(self, parent, QTreeWidgetItem.Type) - else: - if preceding is not parent: - # Preceding must be either the same as item's parent - # or have the same parent as item - while preceding.parent() is not parent: - preceding = preceding.parent() - if preceding is None: - break - if preceding is None: - QTreeWidgetItem.__init__(self, parent, QTreeWidgetItem.Type) - else: - QTreeWidgetItem.__init__(self, parent, preceding, - QTreeWidgetItem.Type) - self.setText(0, name) - parent_text = from_qvariant(parent.data(0, Qt.UserRole), - to_text_string) - set_item_user_text(self, parent_text+'/'+name) - self.line = line - - def set_icon(self, icon_name): - self.setIcon(0, get_icon(icon_name)) - - def setup(self): - self.setToolTip(0, _("Line %s") % str(self.line)) - -class ClassItem(TreeItem): - def setup(self): - self.set_icon('class.png') - self.setToolTip(0, _("Class defined at line %s") % str(self.line)) - -class FunctionItem(TreeItem): - def is_method(self): - return isinstance(self.parent(), ClassItem) - - def setup(self): - if self.is_method(): - self.setToolTip(0, _("Method defined at line %s") % str(self.line)) - name = to_text_string(self.text(0)) - if name.startswith('__'): - self.set_icon('private2.png') - elif name.startswith('_'): - self.set_icon('private1.png') - else: - self.set_icon('method.png') - else: - self.set_icon('function.png') - self.setToolTip(0, _("Function defined at line %s" - ) % str(self.line)) - -class CommentItem(TreeItem): - def __init__(self, name, line, parent, preceding): - name = name.lstrip("# ") - TreeItem.__init__(self, name, line, parent, preceding) - - def setup(self): - self.set_icon('blockcomment.png') - font = self.font(0) - font.setItalic(True) - self.setFont(0, font) - self.setToolTip(0, _("Line %s") % str(self.line)) - -class CellItem(TreeItem): - def __init__(self, name, line, parent, preceding): - name = name.lstrip("#% ") - if name.startswith(""): - name = name[10:].lstrip() - elif name.startswith("In["): - name = name[2:] - if name.endswith("]:"): - name = name[:-1] - name = name.strip() - TreeItem.__init__(self, name, line, parent, preceding) - - def setup(self): - self.set_icon('cell.png') - font = self.font(0) - font.setItalic(True) - self.setFont(0, font) - self.setToolTip(0, _("Cell starts at line %s") % str(self.line)) - -def get_item_children(item): - children = [item.child(index) for index in range(item.childCount())] - for child in children[:]: - others = get_item_children(child) - if others is not None: - children += others - return sorted(children, key=lambda child: child.line) - -def item_at_line(root_item, line): - previous_item = root_item - for item in get_item_children(root_item): - if item.line > line: - return previous_item - previous_item = item - - -def remove_from_tree_cache(tree_cache, line=None, item=None): - if line is None: - for line, (_it, _level, _debug) in list(tree_cache.items()): - if _it is item: - break - item, _level, debug = tree_cache.pop(line) - try: - for child in [item.child(_i) for _i in range(item.childCount())]: - remove_from_tree_cache(tree_cache, item=child) - item.parent().removeChild(item) - except RuntimeError: - # Item has already been deleted - #XXX: remove this debug-related fragment of code - print("unable to remove tree item: ", debug, file=STDOUT) - -class OutlineExplorerTreeWidget(OneColumnTree): - def __init__(self, parent, show_fullpath=False, fullpath_sorting=True, - show_all_files=True, show_comments=True): - self.show_fullpath = show_fullpath - self.fullpath_sorting = fullpath_sorting - self.show_all_files = show_all_files - self.show_comments = show_comments - OneColumnTree.__init__(self, parent) - self.freeze = False # Freezing widget to avoid any unwanted update - self.editor_items = {} - self.editor_tree_cache = {} - self.editor_ids = {} - self.current_editor = None - title = _("Outline") - self.set_title(title) - self.setWindowTitle(title) - self.setUniformRowHeights(True) - - def get_actions_from_items(self, items): - """Reimplemented OneColumnTree method""" - fromcursor_act = create_action(self, text=_('Go to cursor position'), - icon=get_icon('fromcursor.png'), - triggered=self.go_to_cursor_position) - fullpath_act = create_action(self, text=_( 'Show absolute path'), - toggled=self.toggle_fullpath_mode) - fullpath_act.setChecked(self.show_fullpath) - allfiles_act = create_action(self, text=_( 'Show all files'), - toggled=self.toggle_show_all_files) - allfiles_act.setChecked(self.show_all_files) - comment_act = create_action(self, text=_('Show special comments'), - toggled=self.toggle_show_comments) - comment_act.setChecked(self.show_comments) - actions = [fullpath_act, allfiles_act, comment_act, fromcursor_act] - return actions - - def toggle_fullpath_mode(self, state): - self.show_fullpath = state - self.setTextElideMode(Qt.ElideMiddle if state else Qt.ElideRight) - for index in range(self.topLevelItemCount()): - self.topLevelItem(index).set_text(fullpath=self.show_fullpath) - - def __hide_or_show_root_items(self, item): - """ - show_all_files option is disabled: hide all root items except *item* - show_all_files option is enabled: do nothing - """ - for _it in self.get_top_level_items(): - _it.setHidden(_it is not item and not self.show_all_files) - - def toggle_show_all_files(self, state): - self.show_all_files = state - if self.current_editor is not None: - editor_id = self.editor_ids[self.current_editor] - item = self.editor_items[editor_id] - self.__hide_or_show_root_items(item) - - def toggle_show_comments(self, state): - self.show_comments = state - self.update_all() - - def set_fullpath_sorting(self, state): - self.fullpath_sorting = state - self.__sort_toplevel_items() - - def go_to_cursor_position(self): - if self.current_editor is not None: - line = self.current_editor.get_cursor_line_number() - editor_id = self.editor_ids[self.current_editor] - root_item = self.editor_items[editor_id] - item = item_at_line(root_item, line) - self.setCurrentItem(item) - self.scrollToItem(item) - - def clear(self): - """Reimplemented Qt method""" - self.set_title('') - OneColumnTree.clear(self) - - def set_current_editor(self, editor, fname, update): - """Bind editor instance""" - editor_id = editor.get_document_id() - if editor_id in list(self.editor_ids.values()): - item = self.editor_items[editor_id] - if not self.freeze: - self.scrollToItem(item) - self.root_item_selected(item) - self.__hide_or_show_root_items(item) - if update: - self.save_expanded_state() - tree_cache = self.editor_tree_cache[editor_id] - self.populate_branch(editor, item, tree_cache) - self.restore_expanded_state() - else: - # import time - # t0 = time.time() - root_item = FileRootItem(fname, self) - root_item.set_text(fullpath=self.show_fullpath) - tree_cache = self.populate_branch(editor, root_item) - self.__sort_toplevel_items() - self.__hide_or_show_root_items(root_item) - self.root_item_selected(root_item) - # print >>STDOUT, "Elapsed time: %d ms" % round((time.time()-t0)*1000) - self.editor_items[editor_id] = root_item - self.editor_tree_cache[editor_id] = tree_cache - self.resizeColumnToContents(0) - if editor not in self.editor_ids: - self.editor_ids[editor] = editor_id - self.current_editor = editor - - def file_renamed(self, editor, new_filename): - """File was renamed, updating outline explorer tree""" - editor_id = editor.get_document_id() - if editor_id in list(self.editor_ids.values()): - root_item = self.editor_items[editor_id] - root_item.set_path(new_filename, fullpath=self.show_fullpath) - self.__sort_toplevel_items() - - def update_all(self): - self.save_expanded_state() - for editor, editor_id in list(self.editor_ids.items()): - item = self.editor_items[editor_id] - tree_cache = self.editor_tree_cache[editor_id] - self.populate_branch(editor, item, tree_cache) - self.restore_expanded_state() - - def remove_editor(self, editor): - if editor in self.editor_ids: - if self.current_editor is editor: - self.current_editor = None - editor_id = self.editor_ids.pop(editor) - if editor_id not in list(self.editor_ids.values()): - root_item = self.editor_items.pop(editor_id) - self.editor_tree_cache.pop(editor_id) - try: - self.takeTopLevelItem(self.indexOfTopLevelItem(root_item)) - except RuntimeError: - # item has already been removed - pass - - def __sort_toplevel_items(self): - if self.fullpath_sorting: - sort_func = lambda item: osp.dirname(item.path.lower()) - else: - sort_func = lambda item: osp.basename(item.path.lower()) - self.sort_top_level_items(key=sort_func) - - def populate_branch(self, editor, root_item, tree_cache=None): - if tree_cache is None: - tree_cache = {} - - # Removing cached items for which line is > total line nb - for _l in list(tree_cache.keys()): - if _l >= editor.get_line_count(): - # Checking if key is still in tree cache in case one of its - # ancestors was deleted in the meantime (deleting all children): - if _l in tree_cache: - remove_from_tree_cache(tree_cache, line=_l) - - ancestors = [(root_item, 0)] - previous_item = None - previous_level = None - - oe_data = editor.highlighter.get_outlineexplorer_data() - editor.has_cell_separators = oe_data.get('found_cell_separators', False) - for block_nb in range(editor.get_line_count()): - line_nb = block_nb+1 - data = oe_data.get(block_nb) - if data is None: - level = None - else: - level = data.fold_level - citem, clevel, _d = tree_cache.get(line_nb, (None, None, "")) - - # Skip iteration if line is not the first line of a foldable block - if level is None: - if citem is not None: - remove_from_tree_cache(tree_cache, line=line_nb) - continue - - # Searching for class/function statements - not_class_nor_function = data.is_not_class_nor_function() - if not not_class_nor_function: - class_name = data.get_class_name() - if class_name is None: - func_name = data.get_function_name() - if func_name is None: - if citem is not None: - remove_from_tree_cache(tree_cache, line=line_nb) - continue - - if previous_level is not None: - if level == previous_level: - pass - elif level > previous_level+4: # Invalid indentation - continue - elif level > previous_level: - ancestors.append((previous_item, previous_level)) - else: - while len(ancestors) > 1 and level <= previous_level: - ancestors.pop(-1) - _item, previous_level = ancestors[-1] - parent, _level = ancestors[-1] - - if citem is not None: - cname = to_text_string(citem.text(0)) - - preceding = root_item if previous_item is None else previous_item - if not_class_nor_function: - if data.is_comment() and not self.show_comments: - if citem is not None: - remove_from_tree_cache(tree_cache, line=line_nb) - continue - if citem is not None: - if data.text == cname and level == clevel: - previous_level = clevel - previous_item = citem - continue - else: - remove_from_tree_cache(tree_cache, line=line_nb) - if data.is_comment(): - if data.def_type == data.CELL: - item = CellItem(data.text, line_nb, parent, preceding) - else: - item = CommentItem( - data.text, line_nb, parent, preceding) - else: - item = TreeItem(data.text, line_nb, parent, preceding) - elif class_name is not None: - if citem is not None: - if class_name == cname and level == clevel: - previous_level = clevel - previous_item = citem - continue - else: - remove_from_tree_cache(tree_cache, line=line_nb) - item = ClassItem(class_name, line_nb, parent, preceding) - else: - if citem is not None: - if func_name == cname and level == clevel: - previous_level = clevel - previous_item = citem - continue - else: - remove_from_tree_cache(tree_cache, line=line_nb) - item = FunctionItem(func_name, line_nb, parent, preceding) - - item.setup() - debug = "%s -- %s/%s" % (str(item.line).rjust(6), - to_text_string(item.parent().text(0)), - to_text_string(item.text(0))) - tree_cache[line_nb] = (item, level, debug) - previous_level = level - previous_item = item - - return tree_cache - - def root_item_selected(self, item): - """Root item has been selected: expanding it and collapsing others""" - for index in range(self.topLevelItemCount()): - root_item = self.topLevelItem(index) - if root_item is item: - self.expandItem(root_item) - else: - self.collapseItem(root_item) - - def restore(self): - """Reimplemented OneColumnTree method""" - if self.current_editor is not None: - self.collapseAll() - editor_id = self.editor_ids[self.current_editor] - self.root_item_selected(self.editor_items[editor_id]) - - def get_root_item(self, item): - root_item = item - while isinstance(root_item.parent(), QTreeWidgetItem): - root_item = root_item.parent() - return root_item - - def activated(self, item): - """Double-click event""" - line = 0 - if isinstance(item, TreeItem): - line = item.line - root_item = self.get_root_item(item) - self.freeze = True - if line: - self.parent().emit(SIGNAL("edit_goto(QString,int,QString)"), - root_item.path, line, item.text(0)) - else: - self.parent().emit(SIGNAL("edit(QString)"), root_item.path) - self.freeze = False - parent = self.current_editor.parent() - for editor_id, i_item in list(self.editor_items.items()): - if i_item is root_item: - #XXX: not working anymore!!! - for editor, _id in list(self.editor_ids.items()): - if _id == editor_id and editor.parent() is parent: - self.current_editor = editor - break - break - - def clicked(self, item): - """Click event""" - if isinstance(item, FileRootItem): - self.root_item_selected(item) - self.activated(item) - - -class OutlineExplorerWidget(QWidget): - """ - Class browser - - Signals: - SIGNAL("edit_goto(QString,int,QString)") - SIGNAL("edit(QString)") - """ - def __init__(self, parent=None, show_fullpath=True, fullpath_sorting=True, - show_all_files=True, show_comments=True): - QWidget.__init__(self, parent) - - self.treewidget = OutlineExplorerTreeWidget(self, - show_fullpath=show_fullpath, - fullpath_sorting=fullpath_sorting, - show_all_files=show_all_files, - show_comments=show_comments) - - self.visibility_action = create_action(self, - _("Show/hide outline explorer"), - icon='outline_explorer_vis.png', - toggled=self.toggle_visibility) - self.visibility_action.setChecked(True) - - btn_layout = QHBoxLayout() - btn_layout.setAlignment(Qt.AlignLeft) - for btn in self.setup_buttons(): - btn_layout.addWidget(btn) - - layout = QVBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - layout.addLayout(btn_layout) - layout.addWidget(self.treewidget) - self.setLayout(layout) - - def toggle_visibility(self, state): - self.setVisible(state) - current_editor = self.treewidget.current_editor - if current_editor is not None: - current_editor.clearFocus() - current_editor.setFocus() - if state: - self.emit(SIGNAL("outlineexplorer_is_visible()")) - - def setup_buttons(self): - fromcursor_btn = create_toolbutton(self, - icon=get_icon("fromcursor.png"), - tip=_('Go to cursor position'), - triggered=self.treewidget.go_to_cursor_position) - collapse_btn = create_toolbutton(self) - collapse_btn.setDefaultAction(self.treewidget.collapse_selection_action) - expand_btn = create_toolbutton(self) - expand_btn.setDefaultAction(self.treewidget.expand_selection_action) - restore_btn = create_toolbutton(self) - restore_btn.setDefaultAction(self.treewidget.restore_action) - return (fromcursor_btn, collapse_btn, expand_btn, restore_btn) - - def set_current_editor(self, editor, fname, update, clear): - if clear: - self.remove_editor(editor) - if editor.highlighter is not None: - self.treewidget.set_current_editor(editor, fname, update) - - def remove_editor(self, editor): - self.treewidget.remove_editor(editor) - - def get_options(self): - """ - Return outline explorer options - except for fullpath sorting option which is more global - """ - return dict(show_fullpath=self.treewidget.show_fullpath, - show_all_files=self.treewidget.show_all_files, - show_comments=self.treewidget.show_comments, - expanded_state=self.treewidget.get_expanded_state(), - scrollbar_position=self.treewidget.get_scrollbar_position(), - visibility=self.isVisible()) - - def update(self): - self.treewidget.update_all() - - def set_fullpath_sorting(self, state): - self.treewidget.set_fullpath_sorting(state) - - def file_renamed(self, editor, new_filename): - self.treewidget.file_renamed(editor, new_filename) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/explorer.py spyder-3.0.2+dfsg1/spyderlib/widgets/explorer.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/explorer.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/explorer.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1107 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Files and Directories Explorer""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from __future__ import with_statement - -from spyderlib.qt.QtGui import (QVBoxLayout, QLabel, QHBoxLayout, QInputDialog, - QFileSystemModel, QMenu, QWidget, QToolButton, - QLineEdit, QMessageBox, QToolBar, QTreeView, - QDrag, QSortFilterProxyModel) -from spyderlib.qt.QtCore import (Qt, SIGNAL, QMimeData, QSize, QDir, QUrl, - Signal, QTimer) -from spyderlib.qt.compat import getsavefilename, getexistingdirectory - -import os -import sys -import re -import os.path as osp -import shutil - -# Local imports -from spyderlib.utils.qthelpers import (get_icon, create_action, add_actions, - file_uri, get_std_icon) -from spyderlib.utils import misc, encoding, programs, vcs -from spyderlib.baseconfig import _ -from spyderlib.py3compat import (to_text_string, to_binary_string, getcwd, - str_lower) - -try: - from IPython.nbconvert import PythonExporter as nbexporter -except: - nbexporter = None # analysis:ignore - - -def fixpath(path): - """Normalize path fixing case, making absolute and removing symlinks""" - norm = osp.normcase if os.name == 'nt' else osp.normpath - return norm(osp.abspath(osp.realpath(path))) - - -def create_script(fname): - """Create a new Python script""" - text = os.linesep.join(["# -*- coding: utf-8 -*-", "", ""]) - encoding.write(to_text_string(text), fname, 'utf-8') - - -def listdir(path, include='.', exclude=r'\.pyc$|^\.', show_all=False, - folders_only=False): - """List files and directories""" - namelist = [] - dirlist = [to_text_string(osp.pardir)] - for item in os.listdir(to_text_string(path)): - if re.search(exclude, item) and not show_all: - continue - if osp.isdir(osp.join(path, item)): - dirlist.append(item) - elif folders_only: - continue - elif re.search(include, item) or show_all: - namelist.append(item) - return sorted(dirlist, key=str_lower) + \ - sorted(namelist, key=str_lower) - - -def has_subdirectories(path, include, exclude, show_all): - """Return True if path has subdirectories""" - try: - # > 1 because of '..' - return len( listdir(path, include, exclude, - show_all, folders_only=True) ) > 1 - except (IOError, OSError): - return False - - -class DirView(QTreeView): - """Base file/directory tree view""" - def __init__(self, parent=None): - super(DirView, self).__init__(parent) - self.name_filters = None - self.parent_widget = parent - self.show_all = None - self.menu = None - self.common_actions = None - self.__expanded_state = None - self._to_be_loaded = None - self.fsmodel = None - self.setup_fs_model() - self._scrollbar_positions = None - - #---- Model - def setup_fs_model(self): - """Setup filesystem model""" - filters = QDir.AllDirs | QDir.Files | QDir.Drives | QDir.NoDotAndDotDot - self.fsmodel = QFileSystemModel(self) - self.fsmodel.setFilter(filters) - self.fsmodel.setNameFilterDisables(False) - - def install_model(self): - """Install filesystem model""" - self.setModel(self.fsmodel) - - def setup_view(self): - """Setup view""" - self.install_model() - self.connect(self.fsmodel, SIGNAL('directoryLoaded(QString)'), - lambda: self.resizeColumnToContents(0)) - self.setAnimated(False) - self.setSortingEnabled(True) - self.sortByColumn(0, Qt.AscendingOrder) - - def set_name_filters(self, name_filters): - """Set name filters""" - self.name_filters = name_filters - self.fsmodel.setNameFilters(name_filters) - - def set_show_all(self, state): - """Toggle 'show all files' state""" - if state: - self.fsmodel.setNameFilters([]) - else: - self.fsmodel.setNameFilters(self.name_filters) - - def get_filename(self, index): - """Return filename associated with *index*""" - if index: - return osp.normpath(to_text_string(self.fsmodel.filePath(index))) - - def get_index(self, filename): - """Return index associated with filename""" - return self.fsmodel.index(filename) - - def get_selected_filenames(self): - """Return selected filenames""" - if self.selectionMode() == self.ExtendedSelection: - return [self.get_filename(idx) for idx in self.selectedIndexes()] - else: - return [self.get_filename(self.currentIndex())] - - def get_dirname(self, index): - """Return dirname associated with *index*""" - fname = self.get_filename(index) - if fname: - if osp.isdir(fname): - return fname - else: - return osp.dirname(fname) - - #---- Tree view widget - def setup(self, name_filters=['*.py', '*.pyw'], show_all=False): - """Setup tree widget""" - self.setup_view() - - self.set_name_filters(name_filters) - self.show_all = show_all - - # Setup context menu - self.menu = QMenu(self) - self.common_actions = self.setup_common_actions() - - #---- Context menu - def setup_common_actions(self): - """Setup context menu common actions""" - # Filters - filters_action = create_action(self, _("Edit filename filters..."), - None, get_icon('filter.png'), - triggered=self.edit_filter) - # Show all files - all_action = create_action(self, _("Show all files"), - toggled=self.toggle_all) - all_action.setChecked(self.show_all) - self.toggle_all(self.show_all) - - return [filters_action, all_action] - - def edit_filter(self): - """Edit name filters""" - filters, valid = QInputDialog.getText(self, _('Edit filename filters'), - _('Name filters:'), - QLineEdit.Normal, - ", ".join(self.name_filters)) - if valid: - filters = [f.strip() for f in to_text_string(filters).split(',')] - self.parent_widget.sig_option_changed.emit('name_filters', filters) - self.set_name_filters(filters) - - def toggle_all(self, checked): - """Toggle all files mode""" - self.parent_widget.sig_option_changed.emit('show_all', checked) - self.show_all = checked - self.set_show_all(checked) - - def create_file_new_actions(self, fnames): - """Return actions for submenu 'New...'""" - if not fnames: - return [] - new_file_act = create_action(self, _("File..."), icon='filenew.png', - triggered=lambda: - self.new_file(fnames[-1])) - new_module_act = create_action(self, _("Module..."), icon='py.png', - triggered=lambda: - self.new_module(fnames[-1])) - new_folder_act = create_action(self, _("Folder..."), - icon='folder_new.png', - triggered=lambda: - self.new_folder(fnames[-1])) - new_package_act = create_action(self, _("Package..."), - icon=get_icon('package_collapsed.png'), - triggered=lambda: - self.new_package(fnames[-1])) - return [new_file_act, new_folder_act, None, - new_module_act, new_package_act] - - def create_file_import_actions(self, fnames): - """Return actions for submenu 'Import...'""" - return [] - - def create_file_manage_actions(self, fnames): - """Return file management actions""" - only_files = all([osp.isfile(_fn) for _fn in fnames]) - only_modules = all([osp.splitext(_fn)[1] in ('.py', '.pyw', '.ipy') - for _fn in fnames]) - only_notebooks = all([osp.splitext(_fn)[1] == '.ipynb' - for _fn in fnames]) - only_valid = all([encoding.is_text_file(_fn) for _fn in fnames]) - run_action = create_action(self, _("Run"), icon="run_small.png", - triggered=self.run) - edit_action = create_action(self, _("Edit"), icon="edit.png", - triggered=self.clicked) - move_action = create_action(self, _("Move..."), - icon="move.png", - triggered=self.move) - delete_action = create_action(self, _("Delete..."), - icon="delete.png", - triggered=self.delete) - rename_action = create_action(self, _("Rename..."), - icon="rename.png", - triggered=self.rename) - open_action = create_action(self, _("Open"), triggered=self.open) - ipynb_convert_action = create_action(self, _("Convert to Python script"), - icon="python.png", - triggered=self.convert) - - actions = [] - if only_modules: - actions.append(run_action) - if only_valid and only_files: - actions.append(edit_action) - else: - actions.append(open_action) - actions += [delete_action, rename_action] - basedir = fixpath(osp.dirname(fnames[0])) - if all([fixpath(osp.dirname(_fn)) == basedir for _fn in fnames]): - actions.append(move_action) - actions += [None] - if only_notebooks and nbexporter is not None: - actions.append(ipynb_convert_action) - - # VCS support is quite limited for now, so we are enabling the VCS - # related actions only when a single file/folder is selected: - dirname = fnames[0] if osp.isdir(fnames[0]) else osp.dirname(fnames[0]) - if len(fnames) == 1 and vcs.is_vcs_repository(dirname): - vcs_ci = create_action(self, _("Commit"), - icon="vcs_commit.png", - triggered=lambda fnames=[dirname]: - self.vcs_command(fnames, 'commit')) - vcs_log = create_action(self, _("Browse repository"), - icon="vcs_browse.png", - triggered=lambda fnames=[dirname]: - self.vcs_command(fnames, 'browse')) - actions += [None, vcs_ci, vcs_log] - - return actions - - def create_folder_manage_actions(self, fnames): - """Return folder management actions""" - actions = [] - if os.name == 'nt': - _title = _("Open command prompt here") - else: - _title = _("Open terminal here") - action = create_action(self, _title, icon="cmdprompt.png", - triggered=lambda fnames=fnames: - self.open_terminal(fnames)) - actions.append(action) - _title = _("Open Python console here") - action = create_action(self, _title, icon="python.png", - triggered=lambda fnames=fnames: - self.open_interpreter(fnames)) - actions.append(action) - return actions - - def create_context_menu_actions(self): - """Create context menu actions""" - actions = [] - fnames = self.get_selected_filenames() - new_actions = self.create_file_new_actions(fnames) - if len(new_actions) > 1: - # Creating a submenu only if there is more than one entry - new_act_menu = QMenu(_('New'), self) - add_actions(new_act_menu, new_actions) - actions.append(new_act_menu) - else: - actions += new_actions - import_actions = self.create_file_import_actions(fnames) - if len(import_actions) > 1: - # Creating a submenu only if there is more than one entry - import_act_menu = QMenu(_('Import'), self) - add_actions(import_act_menu, import_actions) - actions.append(import_act_menu) - else: - actions += import_actions - if actions: - actions.append(None) - if fnames: - actions += self.create_file_manage_actions(fnames) - if actions: - actions.append(None) - if fnames and all([osp.isdir(_fn) for _fn in fnames]): - actions += self.create_folder_manage_actions(fnames) - if actions: - actions.append(None) - actions += self.common_actions - return actions - - def update_menu(self): - """Update context menu""" - self.menu.clear() - add_actions(self.menu, self.create_context_menu_actions()) - - #---- Events - def viewportEvent(self, event): - """Reimplement Qt method""" - - # Prevent Qt from crashing or showing warnings like: - # "QSortFilterProxyModel: index from wrong model passed to - # mapFromSource", probably due to the fact that the file system model - # is being built. See Issue 1250. - # - # This workaround was inspired by the following KDE bug: - # https://bugs.kde.org/show_bug.cgi?id=172198 - # - # Apparently, this is a bug from Qt itself. - self.executeDelayedItemsLayout() - - return QTreeView.viewportEvent(self, event) - - def contextMenuEvent(self, event): - """Override Qt method""" - self.update_menu() - self.menu.popup(event.globalPos()) - - def keyPressEvent(self, event): - """Reimplement Qt method""" - if event.key() in (Qt.Key_Enter, Qt.Key_Return): - self.clicked() - elif event.key() == Qt.Key_F2: - self.rename() - elif event.key() == Qt.Key_Delete: - self.delete() - else: - QTreeView.keyPressEvent(self, event) - - def mouseDoubleClickEvent(self, event): - """Reimplement Qt method""" - QTreeView.mouseDoubleClickEvent(self, event) - self.clicked() - - def clicked(self): - """Selected item was double-clicked or enter/return was pressed""" - fnames = self.get_selected_filenames() - for fname in fnames: - if osp.isdir(fname): - self.directory_clicked(fname) - else: - self.open([fname]) - - def directory_clicked(self, dirname): - """Directory was just clicked""" - pass - - #---- Drag - def dragEnterEvent(self, event): - """Drag and Drop - Enter event""" - event.setAccepted(event.mimeData().hasFormat("text/plain")) - - def dragMoveEvent(self, event): - """Drag and Drop - Move event""" - if (event.mimeData().hasFormat("text/plain")): - event.setDropAction(Qt.MoveAction) - event.accept() - else: - event.ignore() - - def startDrag(self, dropActions): - """Reimplement Qt Method - handle drag event""" - data = QMimeData() - data.setUrls([QUrl(fname) for fname in self.get_selected_filenames()]) - drag = QDrag(self) - drag.setMimeData(data) - drag.exec_() - - #---- File/Directory actions - def open(self, fnames=None): - """Open files with the appropriate application""" - if fnames is None: - fnames = self.get_selected_filenames() - for fname in fnames: - if osp.isfile(fname) and encoding.is_text_file(fname): - self.parent_widget.sig_open_file.emit(fname) - else: - self.open_outside_spyder([fname]) - - def open_outside_spyder(self, fnames): - """Open file outside Spyder with the appropriate application - If this does not work, opening unknown file in Spyder, as text file""" - for path in sorted(fnames): - path = file_uri(path) - ok = programs.start_file(path) - if not ok: - self.parent_widget.emit(SIGNAL("edit(QString)"), path) - - def open_terminal(self, fnames): - """Open terminal""" - for path in sorted(fnames): - self.parent_widget.emit(SIGNAL("open_terminal(QString)"), path) - - def open_interpreter(self, fnames): - """Open interpreter""" - for path in sorted(fnames): - self.parent_widget.emit(SIGNAL("open_interpreter(QString)"), path) - - def run(self, fnames=None): - """Run Python scripts""" - if fnames is None: - fnames = self.get_selected_filenames() - for fname in fnames: - self.parent_widget.emit(SIGNAL("run(QString)"), fname) - - def remove_tree(self, dirname): - """Remove whole directory tree - Reimplemented in project explorer widget""" - shutil.rmtree(dirname, onerror=misc.onerror) - - def delete_file(self, fname, multiple, yes_to_all): - """Delete file""" - if multiple: - buttons = QMessageBox.Yes|QMessageBox.YesAll| \ - QMessageBox.No|QMessageBox.Cancel - else: - buttons = QMessageBox.Yes|QMessageBox.No - if yes_to_all is None: - answer = QMessageBox.warning(self, _("Delete"), - _("Do you really want " - "to delete %s?" - ) % osp.basename(fname), buttons) - if answer == QMessageBox.No: - return yes_to_all - elif answer == QMessageBox.Cancel: - return False - elif answer == QMessageBox.YesAll: - yes_to_all = True - try: - if osp.isfile(fname): - misc.remove_file(fname) - self.parent_widget.emit(SIGNAL("removed(QString)"), - fname) - else: - self.remove_tree(fname) - self.parent_widget.emit(SIGNAL("removed_tree(QString)"), - fname) - return yes_to_all - except EnvironmentError as error: - action_str = _('delete') - QMessageBox.critical(self, _("Project Explorer"), - _("Unable to %s %s" - "

    Error message:
    %s" - ) % (action_str, fname, to_text_string(error))) - return False - - def delete(self, fnames=None): - """Delete files""" - if fnames is None: - fnames = self.get_selected_filenames() - multiple = len(fnames) > 1 - yes_to_all = None - for fname in fnames: - yes_to_all = self.delete_file(fname, multiple, yes_to_all) - if yes_to_all is not None and not yes_to_all: - # Canceled - return - - def convert_notebook(self, fname): - """Convert an IPython notebook to a Python script in editor""" - try: - script = nbexporter().from_filename(fname)[0] - except Exception as e: - QMessageBox.critical(self, _('Conversion error'), - _("It was not possible to convert this " - "notebook. The error is:\n\n") + \ - to_text_string(e)) - return - self.parent_widget.sig_new_file.emit(script) - - def convert(self, fnames=None): - """Convert IPython notebooks to Python scripts in editor""" - if fnames is None: - fnames = self.get_selected_filenames() - if not isinstance(fnames, (tuple, list)): - fnames = [fnames] - for fname in fnames: - self.convert_notebook(fname) - - def rename_file(self, fname): - """Rename file""" - path, valid = QInputDialog.getText(self, _('Rename'), - _('New name:'), QLineEdit.Normal, - osp.basename(fname)) - if valid: - path = osp.join(osp.dirname(fname), to_text_string(path)) - if path == fname: - return - if osp.exists(path): - if QMessageBox.warning(self, _("Rename"), - _("Do you really want to rename %s and " - "overwrite the existing file %s?" - ) % (osp.basename(fname), osp.basename(path)), - QMessageBox.Yes|QMessageBox.No) == QMessageBox.No: - return - try: - misc.rename_file(fname, path) - self.parent_widget.emit( \ - SIGNAL("renamed(QString,QString)"), fname, path) - return path - except EnvironmentError as error: - QMessageBox.critical(self, _("Rename"), - _("Unable to rename file %s" - "

    Error message:
    %s" - ) % (osp.basename(fname), to_text_string(error))) - - def rename(self, fnames=None): - """Rename files""" - if fnames is None: - fnames = self.get_selected_filenames() - if not isinstance(fnames, (tuple, list)): - fnames = [fnames] - for fname in fnames: - self.rename_file(fname) - - def move(self, fnames=None): - """Move files/directories""" - if fnames is None: - fnames = self.get_selected_filenames() - orig = fixpath(osp.dirname(fnames[0])) - while True: - self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), False) - folder = getexistingdirectory(self, _("Select directory"), orig) - self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), True) - if folder: - folder = fixpath(folder) - if folder != orig: - break - else: - return - for fname in fnames: - basename = osp.basename(fname) - try: - misc.move_file(fname, osp.join(folder, basename)) - except EnvironmentError as error: - QMessageBox.critical(self, _("Error"), - _("Unable to move %s" - "

    Error message:
    %s" - ) % (basename, to_text_string(error))) - - def create_new_folder(self, current_path, title, subtitle, is_package): - """Create new folder""" - if current_path is None: - current_path = '' - if osp.isfile(current_path): - current_path = osp.dirname(current_path) - name, valid = QInputDialog.getText(self, title, subtitle, - QLineEdit.Normal, "") - if valid: - dirname = osp.join(current_path, to_text_string(name)) - try: - os.mkdir(dirname) - except EnvironmentError as error: - QMessageBox.critical(self, title, - _("Unable " - "to create folder %s" - "

    Error message:
    %s" - ) % (dirname, to_text_string(error))) - finally: - if is_package: - fname = osp.join(dirname, '__init__.py') - try: - with open(fname, 'wb') as f: - f.write(to_binary_string('#')) - return dirname - except EnvironmentError as error: - QMessageBox.critical(self, title, - _("Unable " - "to create file %s" - "

    Error message:
    %s" - ) % (fname, - to_text_string(error))) - - def new_folder(self, basedir): - """New folder""" - title = _('New folder') - subtitle = _('Folder name:') - self.create_new_folder(basedir, title, subtitle, is_package=False) - - def new_package(self, basedir): - """New package""" - title = _('New package') - subtitle = _('Package name:') - self.create_new_folder(basedir, title, subtitle, is_package=True) - - def create_new_file(self, current_path, title, filters, create_func): - """Create new file - Returns True if successful""" - if current_path is None: - current_path = '' - if osp.isfile(current_path): - current_path = osp.dirname(current_path) - self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), False) - fname, _selfilter = getsavefilename(self, title, current_path, filters) - self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), True) - if fname: - try: - create_func(fname) - return fname - except EnvironmentError as error: - QMessageBox.critical(self, _("New file"), - _("Unable to create file %s" - "

    Error message:
    %s" - ) % (fname, to_text_string(error))) - - def new_file(self, basedir): - """New file""" - title = _("New file") - filters = _("All files")+" (*)" - def create_func(fname): - """File creation callback""" - if osp.splitext(fname)[1] in ('.py', '.pyw', '.ipy'): - create_script(fname) - else: - with open(fname, 'wb') as f: - f.write(to_binary_string('')) - fname = self.create_new_file(basedir, title, filters, create_func) - if fname is not None: - self.open([fname]) - - def new_module(self, basedir): - """New module""" - title = _("New module") - filters = _("Python scripts")+" (*.py *.pyw *.ipy)" - create_func = lambda fname: self.parent_widget.emit( \ - SIGNAL("create_module(QString)"), fname) - self.create_new_file(basedir, title, filters, create_func) - - #----- VCS actions - def vcs_command(self, fnames, action): - """VCS action (commit, browse)""" - try: - for path in sorted(fnames): - vcs.run_vcs_tool(path, action) - except vcs.ActionToolNotFound as error: - msg = _("For %s support, please install one of the
    " - "following tools:

    %s")\ - % (error.vcsname, ', '.join(error.tools)) - QMessageBox.critical(self, _("Error"), - _("""Unable to find external program.

    %s""") - % to_text_string(msg)) - - #----- Settings - def get_scrollbar_position(self): - """Return scrollbar positions""" - return (self.horizontalScrollBar().value(), - self.verticalScrollBar().value()) - - def set_scrollbar_position(self, position): - """Set scrollbar positions""" - # Scrollbars will be restored after the expanded state - self._scrollbar_positions = position - if self._to_be_loaded is not None and len(self._to_be_loaded) == 0: - self.restore_scrollbar_positions() - - def restore_scrollbar_positions(self): - """Restore scrollbar positions once tree is loaded""" - hor, ver = self._scrollbar_positions - self.horizontalScrollBar().setValue(hor) - self.verticalScrollBar().setValue(ver) - - def get_expanded_state(self): - """Return expanded state""" - self.save_expanded_state() - return self.__expanded_state - - def set_expanded_state(self, state): - """Set expanded state""" - self.__expanded_state = state - self.restore_expanded_state() - - def save_expanded_state(self): - """Save all items expanded state""" - model = self.model() - # If model is not installed, 'model' will be None: this happens when - # using the Project Explorer without having selected a workspace yet - if model is not None: - self.__expanded_state = [] - for idx in model.persistentIndexList(): - if self.isExpanded(idx): - self.__expanded_state.append(self.get_filename(idx)) - - def restore_directory_state(self, fname): - """Restore directory expanded state""" - root = osp.normpath(to_text_string(fname)) - if not osp.exists(root): - # Directory has been (re)moved outside Spyder - return - for basename in os.listdir(root): - path = osp.normpath(osp.join(root, basename)) - if osp.isdir(path) and path in self.__expanded_state: - self.__expanded_state.pop(self.__expanded_state.index(path)) - if self._to_be_loaded is None: - self._to_be_loaded = [] - self._to_be_loaded.append(path) - self.setExpanded(self.get_index(path), True) - if not self.__expanded_state: - self.disconnect(self.fsmodel, SIGNAL('directoryLoaded(QString)'), - self.restore_directory_state) - - def follow_directories_loaded(self, fname): - """Follow directories loaded during startup""" - if self._to_be_loaded is None: - return - path = osp.normpath(to_text_string(fname)) - if path in self._to_be_loaded: - self._to_be_loaded.remove(path) - if self._to_be_loaded is not None and len(self._to_be_loaded) == 0: - self.disconnect(self.fsmodel, SIGNAL('directoryLoaded(QString)'), - self.follow_directories_loaded) - if self._scrollbar_positions is not None: - # The tree view need some time to render branches: - QTimer.singleShot(50, self.restore_scrollbar_positions) - - def restore_expanded_state(self): - """Restore all items expanded state""" - if self.__expanded_state is not None: - # In the old project explorer, the expanded state was a dictionnary: - if isinstance(self.__expanded_state, list): - self.connect(self.fsmodel, SIGNAL('directoryLoaded(QString)'), - self.restore_directory_state) - self.connect(self.fsmodel, SIGNAL('directoryLoaded(QString)'), - self.follow_directories_loaded) - - -class ProxyModel(QSortFilterProxyModel): - """Proxy model: filters tree view""" - def __init__(self, parent): - super(ProxyModel, self).__init__(parent) - self.root_path = None - self.path_list = [] - self.setDynamicSortFilter(True) - - def setup_filter(self, root_path, path_list): - """Setup proxy model filter parameters""" - self.root_path = osp.normpath(to_text_string(root_path)) - self.path_list = [osp.normpath(to_text_string(p)) for p in path_list] - self.invalidateFilter() - - def sort(self, column, order=Qt.AscendingOrder): - """Reimplement Qt method""" - self.sourceModel().sort(column, order) - - def filterAcceptsRow(self, row, parent_index): - """Reimplement Qt method""" - if self.root_path is None: - return True - index = self.sourceModel().index(row, 0, parent_index) - path = osp.normpath(to_text_string(self.sourceModel().filePath(index))) - if self.root_path.startswith(path): - # This is necessary because parent folders need to be scanned - return True - else: - for p in self.path_list: - if path == p or path.startswith(p+os.sep): - return True - else: - return False - - -class FilteredDirView(DirView): - """Filtered file/directory tree view""" - def __init__(self, parent=None): - super(FilteredDirView, self).__init__(parent) - self.proxymodel = None - self.setup_proxy_model() - self.root_path = None - - #---- Model - def setup_proxy_model(self): - """Setup proxy model""" - self.proxymodel = ProxyModel(self) - self.proxymodel.setSourceModel(self.fsmodel) - - def install_model(self): - """Install proxy model""" - if self.root_path is not None: - self.fsmodel.setNameFilters(self.name_filters) - self.setModel(self.proxymodel) - - def set_root_path(self, root_path): - """Set root path""" - self.root_path = root_path - self.install_model() - index = self.fsmodel.setRootPath(root_path) - self.setRootIndex(self.proxymodel.mapFromSource(index)) - - def get_index(self, filename): - """Return index associated with filename""" - index = self.fsmodel.index(filename) - if index.isValid() and index.model() is self.fsmodel: - return self.proxymodel.mapFromSource(index) - - def set_folder_names(self, folder_names): - """Set folder names""" - assert self.root_path is not None - path_list = [osp.join(self.root_path, dirname) - for dirname in folder_names] - self.proxymodel.setup_filter(self.root_path, path_list) - - def get_filename(self, index): - """Return filename from index""" - if index: - path = self.fsmodel.filePath(self.proxymodel.mapToSource(index)) - return osp.normpath(to_text_string(path)) - - -class ExplorerTreeWidget(DirView): - """File/directory explorer tree widget - show_cd_only: Show current directory only - (True/False: enable/disable the option - None: enable the option and do not allow the user to disable it)""" - def __init__(self, parent=None, show_cd_only=None): - DirView.__init__(self, parent) - - self.history = [] - self.histindex = None - - self.show_cd_only = show_cd_only - self.__original_root_index = None - self.__last_folder = None - - self.menu = None - self.common_actions = None - - # Enable drag events - self.setDragEnabled(True) - - #---- Context menu - def setup_common_actions(self): - """Setup context menu common actions""" - actions = super(ExplorerTreeWidget, self).setup_common_actions() - if self.show_cd_only is None: - # Enabling the 'show current directory only' option but do not - # allow the user to disable it - self.show_cd_only = True - else: - # Show current directory only - cd_only_action = create_action(self, - _("Show current directory only"), - toggled=self.toggle_show_cd_only) - cd_only_action.setChecked(self.show_cd_only) - self.toggle_show_cd_only(self.show_cd_only) - actions.append(cd_only_action) - return actions - - def toggle_show_cd_only(self, checked): - """Toggle show current directory only mode""" - self.parent_widget.sig_option_changed.emit('show_cd_only', checked) - self.show_cd_only = checked - if checked: - if self.__last_folder is not None: - self.set_current_folder(self.__last_folder) - elif self.__original_root_index is not None: - self.setRootIndex(self.__original_root_index) - - #---- Refreshing widget - def set_current_folder(self, folder): - """Set current folder and return associated model index""" - index = self.fsmodel.setRootPath(folder) - self.__last_folder = folder - if self.show_cd_only: - if self.__original_root_index is None: - self.__original_root_index = self.rootIndex() - self.setRootIndex(index) - return index - - def refresh(self, new_path=None, force_current=False): - """Refresh widget - force=False: won't refresh widget if path has not changed""" - if new_path is None: - new_path = getcwd() - if force_current: - index = self.set_current_folder(new_path) - self.expand(index) - self.setCurrentIndex(index) - self.emit(SIGNAL("set_previous_enabled(bool)"), - self.histindex is not None and self.histindex > 0) - self.emit(SIGNAL("set_next_enabled(bool)"), - self.histindex is not None and \ - self.histindex < len(self.history)-1) - - #---- Events - def directory_clicked(self, dirname): - """Directory was just clicked""" - self.chdir(directory=dirname) - - #---- Files/Directories Actions - def go_to_parent_directory(self): - """Go to parent directory""" - self.chdir( osp.abspath(osp.join(getcwd(), os.pardir)) ) - - def go_to_previous_directory(self): - """Back to previous directory""" - self.histindex -= 1 - self.chdir(browsing_history=True) - - def go_to_next_directory(self): - """Return to next directory""" - self.histindex += 1 - self.chdir(browsing_history=True) - - def update_history(self, directory): - """Update browse history""" - directory = osp.abspath(to_text_string(directory)) - if directory in self.history: - self.histindex = self.history.index(directory) - - def chdir(self, directory=None, browsing_history=False): - """Set directory as working directory""" - if directory is not None: - directory = osp.abspath(to_text_string(directory)) - if browsing_history: - directory = self.history[self.histindex] - elif directory in self.history: - self.histindex = self.history.index(directory) - else: - if self.histindex is None: - self.history = [] - else: - self.history = self.history[:self.histindex+1] - if len(self.history) == 0 or \ - (self.history and self.history[-1] != directory): - self.history.append(directory) - self.histindex = len(self.history)-1 - directory = to_text_string(directory) - os.chdir(directory) - self.parent_widget.emit(SIGNAL("open_dir(QString)"), directory) - self.refresh(new_path=directory, force_current=True) - - -class ExplorerWidget(QWidget): - """Explorer widget""" - sig_option_changed = Signal(str, object) - sig_open_file = Signal(str) - sig_new_file = Signal(str) - - def __init__(self, parent=None, name_filters=['*.py', '*.pyw'], - show_all=False, show_cd_only=None, show_icontext=True): - QWidget.__init__(self, parent) - - self.treewidget = ExplorerTreeWidget(self, show_cd_only=show_cd_only) - self.treewidget.setup(name_filters=name_filters, show_all=show_all) - self.treewidget.chdir(getcwd()) - - icontext_action = create_action(self, _("Show icons and text"), - toggled=self.toggle_icontext) - self.treewidget.common_actions += [None, icontext_action] - - # Setup toolbar - self.toolbar = QToolBar(self) - self.toolbar.setIconSize(QSize(16, 16)) - - self.previous_action = create_action(self, text=_("Previous"), - icon=get_std_icon("ArrowBack"), - triggered=self.treewidget.go_to_previous_directory) - self.toolbar.addAction(self.previous_action) - self.previous_action.setEnabled(False) - self.connect(self.treewidget, SIGNAL("set_previous_enabled(bool)"), - self.previous_action.setEnabled) - - self.next_action = create_action(self, text=_("Next"), - icon=get_std_icon("ArrowForward"), - triggered=self.treewidget.go_to_next_directory) - self.toolbar.addAction(self.next_action) - self.next_action.setEnabled(False) - self.connect(self.treewidget, SIGNAL("set_next_enabled(bool)"), - self.next_action.setEnabled) - - parent_action = create_action(self, text=_("Parent"), - icon=get_std_icon("ArrowUp"), - triggered=self.treewidget.go_to_parent_directory) - self.toolbar.addAction(parent_action) - self.toolbar.addSeparator() - - options_action = create_action(self, text='', tip=_("Options"), - icon=get_icon('tooloptions.png')) - self.toolbar.addAction(options_action) - widget = self.toolbar.widgetForAction(options_action) - widget.setPopupMode(QToolButton.InstantPopup) - menu = QMenu(self) - add_actions(menu, self.treewidget.common_actions) - options_action.setMenu(menu) - - icontext_action.setChecked(show_icontext) - self.toggle_icontext(show_icontext) - - vlayout = QVBoxLayout() - vlayout.addWidget(self.toolbar) - vlayout.addWidget(self.treewidget) - self.setLayout(vlayout) - - def toggle_icontext(self, state): - """Toggle icon text""" - self.sig_option_changed.emit('show_icontext', state) - for action in self.toolbar.actions(): - if not action.isSeparator(): - widget = self.toolbar.widgetForAction(action) - if state: - widget.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - else: - widget.setToolButtonStyle(Qt.ToolButtonIconOnly) - - -class FileExplorerTest(QWidget): - def __init__(self): - QWidget.__init__(self) - vlayout = QVBoxLayout() - self.setLayout(vlayout) - self.explorer = ExplorerWidget(self, show_cd_only=None) - vlayout.addWidget(self.explorer) - - hlayout1 = QHBoxLayout() - vlayout.addLayout(hlayout1) - label = QLabel("Open file:") - label.setAlignment(Qt.AlignRight) - hlayout1.addWidget(label) - self.label1 = QLabel() - hlayout1.addWidget(self.label1) - self.explorer.sig_open_file.connect(self.label1.setText) - - hlayout2 = QHBoxLayout() - vlayout.addLayout(hlayout2) - label = QLabel("Open dir:") - label.setAlignment(Qt.AlignRight) - hlayout2.addWidget(label) - self.label2 = QLabel() - hlayout2.addWidget(self.label2) - self.connect(self.explorer, SIGNAL("open_dir(QString)"), - self.label2.setText) - - hlayout3 = QHBoxLayout() - vlayout.addLayout(hlayout3) - label = QLabel("Option changed:") - label.setAlignment(Qt.AlignRight) - hlayout3.addWidget(label) - self.label3 = QLabel() - hlayout3.addWidget(self.label3) - self.explorer.sig_option_changed.connect( - lambda x, y: self.label3.setText('option_changed: %r, %r' % (x, y))) - - self.connect(self.explorer, SIGNAL("open_parent_dir()"), - lambda: self.explorer.listwidget.refresh('..')) - -class ProjectExplorerTest(QWidget): - def __init__(self, parent=None): - QWidget.__init__(self, parent) - vlayout = QVBoxLayout() - self.setLayout(vlayout) - self.treewidget = FilteredDirView(self) - self.treewidget.setup_view() - self.treewidget.set_root_path(r'D:\Python') - self.treewidget.set_folder_names(['spyder', 'spyder-2.0']) - vlayout.addWidget(self.treewidget) - - -if __name__ == "__main__": - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - test = FileExplorerTest() -# test = ProjectExplorerTest() - test.resize(640, 480) - test.show() - sys.exit(app.exec_()) - diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/baseshell.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/baseshell.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/baseshell.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/baseshell.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,332 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -import sys -import os -import os.path as osp -from time import time, strftime, gmtime - -from spyderlib.qt.QtGui import (QApplication, QWidget, QVBoxLayout, - QHBoxLayout, QMenu, QLabel, QInputDialog, - QLineEdit, QToolButton) -from spyderlib.qt.QtCore import (QProcess, SIGNAL, QByteArray, QTimer, Qt, - QTextCodec) -LOCALE_CODEC = QTextCodec.codecForLocale() - -# Local imports -from spyderlib.utils.qthelpers import (get_icon, create_toolbutton, - create_action, add_actions) -from spyderlib.baseconfig import get_conf_path, _ -from spyderlib.py3compat import is_text_string, to_text_string - - -def add_pathlist_to_PYTHONPATH(env, pathlist, drop_env=False): - # PyQt API 1/2 compatibility-related tests: - assert isinstance(env, list) - assert all([is_text_string(path) for path in env]) - - pypath = "PYTHONPATH" - pathstr = os.pathsep.join(pathlist) - if os.environ.get(pypath) is not None and not drop_env: - for index, var in enumerate(env[:]): - if var.startswith(pypath+'='): - env[index] = var.replace(pypath+'=', - pypath+'='+pathstr+os.pathsep) - env.append('OLD_PYTHONPATH='+os.environ[pypath]) - else: - env.append(pypath+'='+pathstr) - - -#TODO: code refactoring/cleaning (together with systemshell.py and pythonshell.py) -class ExternalShellBase(QWidget): - """External Shell widget: execute Python script in a separate process""" - SHELL_CLASS = None - def __init__(self, parent=None, fname=None, wdir=None, - history_filename=None, show_icontext=True, - light_background=True, menu_actions=None, - show_buttons_inside=True, show_elapsed_time=True): - QWidget.__init__(self, parent) - - self.menu_actions = menu_actions - - self.run_button = None - self.kill_button = None - self.options_button = None - self.icontext_action = None - - self.show_elapsed_time = show_elapsed_time - - self.fname = fname - if wdir is None: - wdir = osp.dirname(osp.abspath(fname)) - self.wdir = wdir if osp.isdir(wdir) else None - self.arguments = "" - - self.shell = self.SHELL_CLASS(parent, get_conf_path(history_filename)) - self.shell.set_light_background(light_background) - self.connect(self.shell, SIGNAL("execute(QString)"), - self.send_to_process) - self.connect(self.shell, SIGNAL("keyboard_interrupt()"), - self.keyboard_interrupt) - # Redirecting some SIGNALs: - self.connect(self.shell, SIGNAL('redirect_stdio(bool)'), - lambda state: self.emit(SIGNAL('redirect_stdio(bool)'), - state)) - - self.state_label = None - self.time_label = None - - vlayout = QVBoxLayout() - toolbar_buttons = self.get_toolbar_buttons() - if show_buttons_inside: - self.state_label = QLabel() - hlayout = QHBoxLayout() - hlayout.addWidget(self.state_label) - hlayout.addStretch(0) - hlayout.addWidget(self.create_time_label()) - hlayout.addStretch(0) - for button in toolbar_buttons: - hlayout.addWidget(button) - vlayout.addLayout(hlayout) - else: - vlayout.setContentsMargins(0, 0, 0, 0) - vlayout.addWidget(self.get_shell_widget()) - self.setLayout(vlayout) - self.resize(640, 480) - if parent is None: - self.setWindowIcon(self.get_icon()) - self.setWindowTitle(_("Console")) - - self.t0 = None - self.timer = QTimer(self) - - self.process = None - - self.is_closing = False - - if show_buttons_inside: - self.update_time_label_visibility() - - def set_elapsed_time_visible(self, state): - self.show_elapsed_time = state - if self.time_label is not None: - self.time_label.setVisible(state) - - def create_time_label(self): - """Create elapsed time label widget (if necessary) and return it""" - if self.time_label is None: - self.time_label = QLabel() - return self.time_label - - def update_time_label_visibility(self): - self.time_label.setVisible(self.show_elapsed_time) - - def is_running(self): - if self.process is not None: - return self.process.state() == QProcess.Running - - def get_toolbar_buttons(self): - if self.run_button is None: - self.run_button = create_toolbutton(self, text=_("Run"), - icon=get_icon('run.png'), - tip=_("Run again this program"), - triggered=self.start_shell) - if self.kill_button is None: - self.kill_button = create_toolbutton(self, text=_("Kill"), - icon=get_icon('kill.png'), - tip=_("Kills the current process, " - "causing it to exit immediately")) - buttons = [self.run_button] - if self.options_button is None: - options = self.get_options_menu() - if options: - self.options_button = create_toolbutton(self, text=_("Options"), - icon=get_icon('tooloptions.png')) - self.options_button.setPopupMode(QToolButton.InstantPopup) - menu = QMenu(self) - add_actions(menu, options) - self.options_button.setMenu(menu) - if self.options_button is not None: - buttons.append(self.options_button) - buttons.append(self.kill_button) - return buttons - - def set_icontext_visible(self, state): - """Set icon text visibility""" - for widget in self.get_toolbar_buttons(): - if state: - widget.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - else: - widget.setToolButtonStyle(Qt.ToolButtonIconOnly) - - def get_options_menu(self): - self.show_time_action = create_action(self, _("Show elapsed time"), - toggled=self.set_elapsed_time_visible) - self.show_time_action.setChecked(self.show_elapsed_time) - actions = [self.show_time_action] - if self.menu_actions is not None: - actions += [None]+self.menu_actions - return actions - - def get_shell_widget(self): - return self.shell - - def get_icon(self): - raise NotImplementedError - - def show_time(self, end=False): - if self.time_label is None: - return - elapsed_time = time()-self.t0 - if elapsed_time > 24*3600: # More than a day...! - format = "%d %H:%M:%S" - else: - format = "%H:%M:%S" - if end: - color = "#AAAAAA" - else: - color = "#AA6655" - text = "%s" \ - "" % (color, strftime(format, gmtime(elapsed_time))) - self.time_label.setText(text) - - def closeEvent(self, event): - if self.process is not None: - self.is_closing = True - self.process.kill() - self.process.waitForFinished(100) - self.disconnect(self.timer, SIGNAL("timeout()"), self.show_time) - - def set_running_state(self, state=True): - self.set_buttons_runnning_state(state) - self.shell.setReadOnly(not state) - if state: - if self.state_label is not None: - self.state_label.setText(_( - "Running...")) - self.t0 = time() - self.connect(self.timer, SIGNAL("timeout()"), self.show_time) - self.timer.start(1000) - else: - if self.state_label is not None: - self.state_label.setText(_('Terminated.')) - self.disconnect(self.timer, SIGNAL("timeout()"), self.show_time) - - def set_buttons_runnning_state(self, state): - self.run_button.setVisible(not state and not self.is_ipykernel) - self.kill_button.setVisible(state) - - def start_shell(self, ask_for_arguments=False): - """Start shell""" - if ask_for_arguments and not self.get_arguments(): - self.set_running_state(False) - return - try: - self.disconnect(self.terminate_button, SIGNAL("clicked()"), - self.process.terminate) - self.disconnect(self.kill_button, SIGNAL("clicked()"), - self.process.terminate) - except: - pass - self.create_process() - - def get_arguments(self): - arguments, valid = QInputDialog.getText(self, _('Arguments'), - _('Command line arguments:'), - QLineEdit.Normal, - self.arguments) - if valid: - self.arguments = to_text_string(arguments) - return valid - - def create_process(self): - raise NotImplementedError - - def finished(self, exit_code, exit_status): - self.shell.flush() - self.emit(SIGNAL('finished()')) - if self.is_closing: - return - self.set_running_state(False) - self.show_time(end=True) - -#=============================================================================== -# Input/Output -#=============================================================================== - def transcode(self, qba): - try: - return to_text_string(qba.data(), 'utf8') - except UnicodeDecodeError: - return qba.data() - - def get_stdout(self): - self.process.setReadChannel(QProcess.StandardOutput) - qba = QByteArray() - while self.process.bytesAvailable(): - qba += self.process.readAllStandardOutput() - return self.transcode(qba) - - def get_stderr(self): - self.process.setReadChannel(QProcess.StandardError) - qba = QByteArray() - while self.process.bytesAvailable(): - qba += self.process.readAllStandardError() - return self.transcode(qba) - - def write_output(self): - self.shell.write(self.get_stdout(), flush=True) - QApplication.processEvents() - - def send_to_process(self, qstr): - raise NotImplementedError - - def send_ctrl_to_process(self, letter): - char = chr("abcdefghijklmnopqrstuvwxyz".index(letter) + 1) - byte_array = QByteArray() - byte_array.append(char) - self.process.write(byte_array) - self.process.waitForBytesWritten(-1) - self.shell.write(LOCALE_CODEC.toUnicode(byte_array), flush=True) - - def keyboard_interrupt(self): - raise NotImplementedError - - -def test(): - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - from spyderlib.widgets.externalshell.pythonshell import ExternalPythonShell - from spyderlib.widgets.externalshell.systemshell import ExternalSystemShell - import spyderlib - from spyderlib.plugins.variableexplorer import VariableExplorer - settings = VariableExplorer.get_settings() - shell = ExternalPythonShell(wdir=osp.dirname(spyderlib.__file__), - ipykernel=True, stand_alone=settings, - arguments="-q4thread -pylab -colors LightBG", - light_background=False) -# shell = ExternalPythonShell(wdir=osp.dirname(spyderlib.__file__), -# interact=True, umr_enabled=True, -# stand_alone=settings, -# umr_namelist=['guidata', 'guiqwt'], -# umr_verbose=True, light_background=False) -# shell = ExternalSystemShell(wdir=osp.dirname(spyderlib.__file__), -# light_background=False) - shell.shell.toggle_wrap_mode(True) - shell.start_shell(False) - from spyderlib.qt.QtGui import QFont - font = QFont("Lucida console") - font.setPointSize(10) - shell.shell.set_font(font) - shell.show() - sys.exit(app.exec_()) - -if __name__ == "__main__": - test() \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/__init__.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/__init__.py 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.widgets.externalshell -=============================== - -External Shell widget: execute Python script/terminal in a separate process -""" diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/inputhooks.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/inputhooks.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/inputhooks.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/inputhooks.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,150 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Inputhook management for GUI event loop integration - -Copyright (C) The IPython Development Team -Distributed under the terms of the modified BSD license -""" - -# Stdlib imports -import ctypes -import os -import sys - -# Qt imports -if os.environ["QT_API"] == 'pyqt': - from PyQt4 import QtCore, QtGui -elif os.environ["QT_API"] == 'pyside': - from PySide import QtCore, QtGui # analysis:ignore - -#----------------------------------------------------------------------------- -# Utilities -#----------------------------------------------------------------------------- -def _stdin_ready_posix(): - """Return True if there's something to read on stdin (posix version).""" - infds, outfds, erfds = select.select([sys.stdin],[],[],0) - return bool(infds) - -def _stdin_ready_nt(): - """Return True if there's something to read on stdin (nt version).""" - return msvcrt.kbhit() - -def _stdin_ready_other(): - """Return True, assuming there's something to read on stdin.""" - return True - - -def _ignore_CTRL_C_posix(): - """Ignore CTRL+C (SIGINT).""" - signal.signal(signal.SIGINT, signal.SIG_IGN) - -def _allow_CTRL_C_posix(): - """Take CTRL+C into account (SIGINT).""" - signal.signal(signal.SIGINT, signal.default_int_handler) - -def _ignore_CTRL_C_other(): - """Ignore CTRL+C (not implemented).""" - pass - -def _allow_CTRL_C_other(): - """Take CTRL+C into account (not implemented).""" - pass - - -if os.name == 'posix': - import select - import signal - stdin_ready = _stdin_ready_posix - ignore_CTRL_C = _ignore_CTRL_C_posix - allow_CTRL_C = _allow_CTRL_C_posix -elif os.name == 'nt': - import msvcrt - stdin_ready = _stdin_ready_nt - ignore_CTRL_C = _ignore_CTRL_C_other - allow_CTRL_C = _allow_CTRL_C_other -else: - stdin_ready = _stdin_ready_other - ignore_CTRL_C = _ignore_CTRL_C_other - allow_CTRL_C = _allow_CTRL_C_other - - -def clear_inputhook(): - """Set PyOS_InputHook to NULL and return the previous one""" - pyos_inputhook_ptr = ctypes.c_void_p.in_dll(ctypes.pythonapi, - "PyOS_InputHook") - pyos_inputhook_ptr.value = ctypes.c_void_p(None).value - allow_CTRL_C() - -def get_pyos_inputhook(): - """Return the current PyOS_InputHook as a ctypes.c_void_p.""" - return ctypes.c_void_p.in_dll(ctypes.pythonapi, "PyOS_InputHook") - -def set_pyft_callback(callback): - callback = ctypes.PYFUNCTYPE(ctypes.c_int)(callback) - return callback - -def remove_pyqt_inputhook(): - if os.environ["QT_API"] == 'pyqt': - QtCore.pyqtRemoveInputHook() - -#------------------------------------------------------------------------------ -# Input hooks -#------------------------------------------------------------------------------ -def qt4(): - """PyOS_InputHook python hook for Qt4. - - Process pending Qt events and if there's no pending keyboard - input, spend a short slice of time (50ms) running the Qt event - loop. - - As a Python ctypes callback can't raise an exception, we catch - the KeyboardInterrupt and temporarily deactivate the hook, - which will let a *second* CTRL+C be processed normally and go - back to a clean prompt line. - """ - try: - allow_CTRL_C() - app = QtCore.QCoreApplication.instance() - if not app: - app = QtGui.QApplication([" "]) - app.processEvents(QtCore.QEventLoop.AllEvents, 300) - if not stdin_ready(): - # Generally a program would run QCoreApplication::exec() - # from main() to enter and process the Qt event loop until - # quit() or exit() is called and the program terminates. - # - # For our input hook integration, we need to repeatedly - # enter and process the Qt event loop for only a short - # amount of time (say 50ms) to ensure that Python stays - # responsive to other user inputs. - # - # A naive approach would be to repeatedly call - # QCoreApplication::exec(), using a timer to quit after a - # short amount of time. Unfortunately, QCoreApplication - # emits an aboutToQuit signal before stopping, which has - # the undesirable effect of closing all modal windows. - # - # To work around this problem, we instead create a - # QEventLoop and call QEventLoop::exec(). Other than - # setting some state variables which do not seem to be - # used anywhere, the only thing QCoreApplication adds is - # the aboutToQuit signal which is precisely what we are - # trying to avoid. - timer = QtCore.QTimer() - event_loop = QtCore.QEventLoop() - timer.timeout.connect(event_loop.quit) - while not stdin_ready(): - timer.start(50) - event_loop.exec_() - timer.stop() - except KeyboardInterrupt: - print("\nKeyboardInterrupt - Press Enter for new prompt") - except: # NO exceptions are allowed to escape from a ctypes callback - ignore_CTRL_C() - from traceback import print_exc - print_exc() - print("Got exception from inputhook, unregistering.") - clear_inputhook() - finally: - allow_CTRL_C() - return 0 diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/introspection.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/introspection.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/introspection.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/introspection.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,199 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""External shell's introspection and notification servers""" - -from spyderlib.qt.QtCore import QThread, SIGNAL, Signal - -import threading -import socket -import errno -import os - -# Local imports -from spyderlib.baseconfig import get_conf_path, DEBUG -from spyderlib.utils.misc import select_port -from spyderlib.utils.debug import log_last_error -from spyderlib.utils.bsdsocket import read_packet, write_packet - - -LOG_FILENAME = get_conf_path('introspection.log') - -DEBUG_INTROSPECTION = DEBUG >= 2 - -if DEBUG_INTROSPECTION: - import logging - logging.basicConfig(filename=get_conf_path('introspection_debug.log'), - level=logging.DEBUG) - -SPYDER_PORT = 20128 - -class IntrospectionServer(threading.Thread): - """Introspection server""" - def __init__(self): - threading.Thread.__init__(self) - self.shells = {} - self.setDaemon(True) - global SPYDER_PORT - self.port = SPYDER_PORT = select_port(default_port=SPYDER_PORT) - SPYDER_PORT += 1 - - def register(self, shell): - """Register introspection server - See notification server below""" - shell_id = str(id(shell)) - self.shells[shell_id] = shell - - def send_socket(self, shell_id, sock): - """Send socket to the appropriate object for later communication""" - shell = self.shells[shell_id] - shell.set_introspection_socket(sock) - if DEBUG_INTROSPECTION: - logging.debug('Introspection server: shell [%r] port [%r]' - % (shell, self.port)) - - def run(self): - """Start server""" - sock = socket.socket(socket.AF_INET) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind( ("127.0.0.1", self.port) ) - - while True: - sock.listen(2) - try: - conn, _addr = sock.accept() - except socket.error as e: - # See Issue 1275 for details on why errno EINTR is - # silently ignored here. - eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR - if e.args[0] == eintr: - continue - raise - shell_id = read_packet(conn) - if shell_id is not None: - self.send_socket(shell_id, conn) - -class NotificationServer(IntrospectionServer): - """Notification server""" - def __init__(self): - IntrospectionServer.__init__(self) - self.notification_threads = {} - - def register(self, shell): - """Register notification server - See pythonshell.ExternalPythonShell.create_process""" - IntrospectionServer.register(self, shell) - shell_id = str(id(shell)) - n_thread = self.notification_threads[shell_id] = NotificationThread() - return n_thread - - def send_socket(self, shell_id, sock): - """Send socket to the appropriate object for later communication""" - n_thread = self.notification_threads[shell_id] - n_thread.set_notify_socket(sock) - n_thread.start() - if DEBUG_INTROSPECTION: - logging.debug('Notification server: shell [%r] port [%r]' - % (self.shells[shell_id], self.port)) - -INTROSPECTION_SERVER = None - -def start_introspection_server(): - """ - Start introspection server (only one time) - This server is dedicated to introspection features, i.e. Spyder is calling - it to retrieve informations on remote objects - """ - global INTROSPECTION_SERVER - if INTROSPECTION_SERVER is None: - if DEBUG_INTROSPECTION: - import time - time_str = "Logging time: %s" % time.ctime(time.time()) - logging.debug("="*len(time_str)) - logging.debug(time_str) - logging.debug("="*len(time_str)) - INTROSPECTION_SERVER = IntrospectionServer() - INTROSPECTION_SERVER.start() - return INTROSPECTION_SERVER - -NOTIFICATION_SERVER = None - -def start_notification_server(): - """ - Start notify server (only one time) - This server is dedicated to notification features, i.e. remote objects - are notifying Spyder about anything relevant like debugging data (pdb) - or "this is the right moment to refresh variable explorer" (syshook) - """ - global NOTIFICATION_SERVER - if NOTIFICATION_SERVER is None: - NOTIFICATION_SERVER = NotificationServer() - NOTIFICATION_SERVER.start() - return NOTIFICATION_SERVER - - -class NotificationThread(QThread): - """Notification thread""" - sig_process_remote_view = Signal(object) - def __init__(self): - QThread.__init__(self) - self.notify_socket = None - - def set_notify_socket(self, notify_socket): - """Set the notification socket""" - self.notify_socket = notify_socket - - def run(self): - """Start notification thread""" - while True: - if self.notify_socket is None: - continue - output = None - try: - try: - cdict = read_packet(self.notify_socket) - except: - # This except statement is intended to handle a struct.error - # (but when writing 'except struct.error', it doesn't work) - # Note: struct.error is raised when the communication has - # been interrupted and the received data is not a string - # of length 8 as required by struct.unpack (see read_packet) - break - if cdict is None: - # Another notification thread has just terminated and - # then wrote 'None' in the notification socket - # (see the 'finally' statement below) - continue - if not isinstance(cdict, dict): - raise TypeError("Invalid data type: %r" % cdict) - command = cdict['command'] - data = cdict.get('data') - if command == 'pdb_step': - fname, lineno = data - self.emit(SIGNAL('pdb(QString,int)'), fname, lineno) - self.emit(SIGNAL('refresh_namespace_browser()')) - elif command == 'refresh': - self.emit(SIGNAL('refresh_namespace_browser()')) - elif command == 'remote_view': - self.sig_process_remote_view.emit(data) - elif command == 'ipykernel': - self.emit(SIGNAL('new_ipython_kernel(QString)'), data) - elif command == 'open_file': - fname, lineno = data - self.emit(SIGNAL('open_file(QString,int)'), fname, lineno) - else: - raise RuntimeError('Unsupported command: %r' % command) - if DEBUG_INTROSPECTION: - logging.debug("received command: %r" % command) - except: - log_last_error(LOG_FILENAME, "notification thread") - finally: - try: - write_packet(self.notify_socket, output) - except: - # The only reason why it should fail is that Spyder is - # closing while this thread is still alive - break diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/monitor.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/monitor.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/monitor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/monitor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,595 +0,0 @@ -# -*- coding: utf-8 -*- -"""External shell's monitor""" - -#TODO: The "disable auto-refresh when variable explorer is hidden" feature -# broken since we removed the "shell" widget reference from notification -# thread. We must find another mechanism to avoid refreshing systematically -# remote views for all consoles...! - -import os -import socket -import struct -import threading - -# Local imports -from spyderlib.utils.misc import fix_reference_name -from spyderlib.utils.debug import log_last_error -from spyderlib.utils.dochelpers import (getargtxt, getdoc, getsource, - getobjdir, isdefined) -from spyderlib.utils.bsdsocket import (communicate, read_packet, write_packet, - PACKET_NOT_RECEIVED, PICKLE_HIGHEST_PROTOCOL) -from spyderlib.utils.introspection.module_completion import module_completion -from spyderlib.baseconfig import get_conf_path, get_supported_types, DEBUG -from spyderlib.py3compat import getcwd, is_text_string, pickle, _thread - - -SUPPORTED_TYPES = {} - -LOG_FILENAME = get_conf_path('monitor.log') - -DEBUG_MONITOR = DEBUG >= 2 - -if DEBUG_MONITOR: - import logging - logging.basicConfig(filename=get_conf_path('monitor_debug.log'), - level=logging.DEBUG) - -REMOTE_SETTINGS = ('check_all', 'exclude_private', 'exclude_uppercase', - 'exclude_capitalized', 'exclude_unsupported', - 'excluded_names', 'truncate', 'minmax', - 'remote_editing', 'autorefresh') - -def get_remote_data(data, settings, mode, more_excluded_names=None): - """ - Return globals according to filter described in *settings*: - * data: data to be filtered (dictionary) - * settings: variable explorer settings (dictionary) - * mode (string): 'editable' or 'picklable' - * more_excluded_names: additional excluded names (list) - """ - from spyderlib.widgets.dicteditorutils import globalsfilter - global SUPPORTED_TYPES - if not SUPPORTED_TYPES: - SUPPORTED_TYPES = get_supported_types() - assert mode in list(SUPPORTED_TYPES.keys()) - excluded_names = settings['excluded_names'] - if more_excluded_names is not None: - excluded_names += more_excluded_names - return globalsfilter(data, check_all=settings['check_all'], - filters=tuple(SUPPORTED_TYPES[mode]), - exclude_private=settings['exclude_private'], - exclude_uppercase=settings['exclude_uppercase'], - exclude_capitalized=settings['exclude_capitalized'], - exclude_unsupported=settings['exclude_unsupported'], - excluded_names=excluded_names) - -def make_remote_view(data, settings, more_excluded_names=None): - """ - Make a remote view of dictionary *data* - -> globals explorer - """ - from spyderlib.widgets.dicteditorutils import (get_human_readable_type, - get_size, get_color_name, value_to_display) - assert all([name in REMOTE_SETTINGS for name in settings]) - data = get_remote_data(data, settings, mode='editable', - more_excluded_names=more_excluded_names) - remote = {} - for key, value in list(data.items()): - view = value_to_display(value, truncate=settings['truncate'], - minmax=settings['minmax']) - remote[key] = {'type': get_human_readable_type(value), - 'size': get_size(value), - 'color': get_color_name(value), - 'view': view} - return remote - - -def monitor_save_globals(sock, settings, filename): - """Save globals() to file""" - return communicate(sock, '__save_globals__()', - settings=[settings, filename]) - -def monitor_load_globals(sock, filename, ext): - """Load globals() from file""" - return communicate(sock, '__load_globals__()', settings=[filename, ext]) - -def monitor_get_global(sock, name): - """Get global variable *name* value""" - return communicate(sock, '__get_global__("%s")' % name) - -def monitor_set_global(sock, name, value): - """Set global variable *name* value to *value*""" - return communicate(sock, '__set_global__("%s")' % name, - settings=[value]) - -def monitor_del_global(sock, name): - """Del global variable *name*""" - return communicate(sock, '__del_global__("%s")' % name) - -def monitor_copy_global(sock, orig_name, new_name): - """Copy global variable *orig_name* to *new_name*""" - return communicate(sock, '__copy_global__("%s", "%s")' \ - % (orig_name, new_name)) - - -def _getcdlistdir(): - """Return current directory list dir""" - return os.listdir(getcwd()) - -class Monitor(threading.Thread): - """Monitor server""" - def __init__(self, host, introspection_port, notification_port, - shell_id, timeout, auto_refresh): - threading.Thread.__init__(self) - self.setDaemon(True) - - self.ipykernel = None - self.ipython_shell = None - - self.pdb_obj = None - - self.timeout = None - self.set_timeout(timeout) - self.auto_refresh = auto_refresh - self.refresh_after_eval = False - self.remote_view_settings = None - - self.inputhook_flag = False - self.first_inputhook_call = True - - # To grab the IPython internal namespace - self.ip = None - - # Connecting to introspection server - self.i_request = socket.socket( socket.AF_INET ) - self.i_request.connect( (host, introspection_port) ) - write_packet(self.i_request, shell_id) - - # Connecting to notification server - self.n_request = socket.socket( socket.AF_INET ) - self.n_request.connect( (host, notification_port) ) - write_packet(self.n_request, shell_id) - - self._mlocals = { - "refresh": self.enable_refresh_after_eval, - "setlocal": self.setlocal, - "is_array": self.is_array, - "is_image": self.is_image, - "get_globals_keys": self.get_globals_keys, - "getmodcomplist": self.getmodcomplist, - "getcdlistdir": _getcdlistdir, - "getcwd": self.getcwd, - "setcwd": self.setcwd, - "getsyspath": self.getsyspath, - "getenv": self.getenv, - "setenv": self.setenv, - "isdefined": self.isdefined, - "thread": _thread, - "toggle_inputhook_flag": self.toggle_inputhook_flag, - "set_monitor_timeout": self.set_timeout, - "set_monitor_auto_refresh": self.set_auto_refresh, - "set_remote_view_settings": - self.set_remote_view_settings, - "set_spyder_breakpoints": self.set_spyder_breakpoints, - "__get_dir__": self.get_dir, - "__iscallable__": self.iscallable, - "__get_arglist__": self.get_arglist, - "__get__doc____": self.get__doc__, - "__get_doc__": self.get_doc, - "__get_source__": self.get_source, - "__get_global__": self.getglobal, - "__set_global__": self.setglobal, - "__del_global__": self.delglobal, - "__copy_global__": self.copyglobal, - "__save_globals__": self.saveglobals, - "__load_globals__": self.loadglobals, - "_" : None} - self._mglobals = None - - @property - def pdb_frame(self): - """Return current Pdb frame if there is any""" - if self.pdb_obj is not None and self.pdb_obj.curframe is not None: - return self.pdb_obj.curframe - - @property - def pdb_locals(self): - """Return current Pdb frame locals if available - Otherwise return an empty dictionary""" - if self.pdb_frame: - return self.pdb_obj.curframe_locals - else: - return {} - - def mlocals(self): - """Return current locals -- handles Pdb frames""" - ns = {} - ns.update(self._mlocals) - ns.update(self.pdb_locals) - return ns - - def mglobals(self): - """Return current globals -- handles Pdb frames""" - if self.pdb_frame is not None: - return self.pdb_frame.f_globals - else: - if self._mglobals is None: - from __main__ import __dict__ as glbs - self._mglobals = glbs - else: - glbs = self._mglobals - if self.ipykernel is None and '__ipythonkernel__' in glbs: - self.ipykernel = glbs['__ipythonkernel__'] - communicate(self.n_request, - dict(command="ipykernel", - data=self.ipykernel.connection_file)) - if self.ipython_shell is None and '__ipythonshell__' in glbs: - # IPython kernel - self.ipython_shell = glbs['__ipythonshell__'] - glbs = self.ipython_shell.user_ns - self.ip = self.ipython_shell.get_ipython() - self._mglobals = glbs - return glbs - - def get_current_namespace(self, with_magics=False): - """Return current namespace, i.e. globals() if not debugging, - or a dictionary containing both locals() and globals() - for current frame when debugging""" - ns = {} - glbs = self.mglobals() - - if self.pdb_frame is None: - ns.update(glbs) - else: - ns.update(glbs) - ns.update(self.pdb_locals) - - # Add magics to ns so we can show help about them on the Object - # Inspector - if self.ip and with_magics: - line_magics = self.ip.magics_manager.magics['line'] - cell_magics = self.ip.magics_manager.magics['cell'] - ns.update(line_magics) - ns.update(cell_magics) - - return ns - - def get_reference_namespace(self, name): - """Return namespace where reference name is defined, - eventually returns the globals() if reference has not yet been defined""" - glbs = self.mglobals() - if self.pdb_frame is None: - return glbs - else: - lcls = self.pdb_locals - if name in lcls: - return lcls - else: - return glbs - - def get_globals_keys(self): - """Return globals() keys or globals() and locals() keys if debugging""" - ns = self.get_current_namespace() - return list(ns.keys()) - - def isdefined(self, obj, force_import=False): - """Return True if object is defined in current namespace""" - ns = self.get_current_namespace(with_magics=True) - return isdefined(obj, force_import=force_import, namespace=ns) - - def toggle_inputhook_flag(self, state): - """Toggle the input hook flag - - The only purpose of this flag is to unblock the PyOS_InputHook - callback when text is available in stdin (see sitecustomize.py)""" - self.inputhook_flag = state - - def set_timeout(self, timeout): - """Set monitor timeout (in milliseconds!)""" - self.timeout = float(timeout)/1000. - - def set_auto_refresh(self, state): - """Enable/disable namespace browser auto refresh feature""" - self.auto_refresh = state - - def enable_refresh_after_eval(self): - self.refresh_after_eval = True - - #------ Notifications - def refresh(self): - """Refresh variable explorer in ExternalPythonShell""" - communicate(self.n_request, dict(command="refresh")) - - def refresh_from_inputhook(self): - """Refresh variable explorer from the PyOS_InputHook. - See sitecustomize.py""" - # Refreshing variable explorer, except on first input hook call - # (otherwise, on slow machines, this may freeze Spyder) - if self.first_inputhook_call: - self.first_inputhook_call = False - else: - self.refresh() - - def register_pdb_session(self, pdb_obj): - self.pdb_obj = pdb_obj - - def notify_pdb_step(self, fname, lineno): - """Notify the ExternalPythonShell regarding pdb current frame""" - communicate(self.n_request, - dict(command="pdb_step", data=(fname, lineno))) - - def set_spyder_breakpoints(self): - """Set all Spyder breakpoints in active pdb session""" - if not self.pdb_obj: - return - self.pdb_obj.set_spyder_breakpoints() - - def notify_open_file(self, fname, lineno=1): - """Open file in Spyder's editor""" - communicate(self.n_request, - dict(command="open_file", data=(fname, lineno))) - - #------ Code completion / Calltips - def _eval(self, text): - """ - Evaluate text and return (obj, valid) - where *obj* is the object represented by *text* - and *valid* is True if object evaluation did not raise any exception - """ - assert is_text_string(text) - ns = self.get_current_namespace(with_magics=True) - try: - return eval(text, ns), True - except: - return None, False - - def get_dir(self, objtxt): - """Return dir(object)""" - obj, valid = self._eval(objtxt) - if valid: - return getobjdir(obj) - - def iscallable(self, objtxt): - """Is object callable?""" - obj, valid = self._eval(objtxt) - if valid: - return callable(obj) - - def get_arglist(self, objtxt): - """Get func/method argument list""" - obj, valid = self._eval(objtxt) - if valid: - return getargtxt(obj) - - def get__doc__(self, objtxt): - """Get object __doc__""" - obj, valid = self._eval(objtxt) - if valid: - return obj.__doc__ - - def get_doc(self, objtxt): - """Get object documentation dictionary""" - obj, valid = self._eval(objtxt) - if valid: - return getdoc(obj) - - def get_source(self, objtxt): - """Get object source""" - obj, valid = self._eval(objtxt) - if valid: - return getsource(obj) - - def getmodcomplist(self, name, path): - """Return module completion list for object named *name*""" - return module_completion(name, path) - - #------ Other - def is_array(self, name): - """Return True if object is an instance of class numpy.ndarray""" - ns = self.get_current_namespace() - try: - import numpy - return isinstance(ns[name], numpy.ndarray) - except ImportError: - return False - - def is_image(self, name): - """Return True if object is an instance of class PIL.Image.Image""" - ns = self.get_current_namespace() - try: - from spyderlib.pil_patch import Image - return isinstance(ns[name], Image.Image) - except ImportError: - return False - - def getcwd(self): - """Return current working directory""" - return getcwd() - - def setcwd(self, dirname): - """Set current working directory""" - return os.chdir(dirname) - - def getenv(self): - """Return os.environ""" - return os.environ.copy() - - def setenv(self): - """Set os.environ""" - env = read_packet(self.i_request) - os.environ = env - - def getsyspath(self): - """Return sys.path[:]""" - import sys - return sys.path[:] - - def setlocal(self, name, value): - """ - Set local reference value - Not used right now - could be useful in the future - """ - self._mlocals[name] = value - - def set_remote_view_settings(self): - """ - Set the namespace remote view settings - (see the namespace browser widget) - """ - self.remote_view_settings = read_packet(self.i_request) - self.enable_refresh_after_eval() - - def update_remote_view(self): - """ - Return remote view of globals() - """ - settings = self.remote_view_settings - if settings: - ns = self.get_current_namespace() - more_excluded_names = ['In', 'Out'] if self.ipython_shell else None - remote_view = make_remote_view(ns, settings, more_excluded_names) - communicate(self.n_request, - dict(command="remote_view", data=remote_view)) - - def saveglobals(self): - """Save globals() into filename""" - ns = self.get_current_namespace() - from spyderlib.utils.iofuncs import iofunctions - settings = read_packet(self.i_request) - filename = read_packet(self.i_request) - more_excluded_names = ['In', 'Out'] if self.ipython_shell else None - data = get_remote_data(ns, settings, mode='picklable', - more_excluded_names=more_excluded_names).copy() - return iofunctions.save(data, filename) - - def loadglobals(self): - """Load globals() from filename""" - glbs = self.mglobals() - from spyderlib.utils.iofuncs import iofunctions - filename = read_packet(self.i_request) - ext = read_packet(self.i_request) - load_func = iofunctions.load_funcs[ext] - data, error_message = load_func(filename) - if error_message: - return error_message - for key in list(data.keys()): - new_key = fix_reference_name(key, blacklist=list(glbs.keys())) - if new_key != key: - data[new_key] = data.pop(key) - try: - glbs.update(data) - except Exception as error: - return str(error) - self.refresh_after_eval = True - - def getglobal(self, name): - """ - Get global reference value - """ - ns = self.get_current_namespace() - return ns[name] - - def setglobal(self, name): - """ - Set global reference value - """ - ns = self.get_reference_namespace(name) - ns[name] = read_packet(self.i_request) - self.refresh_after_eval = True - - def delglobal(self, name): - """ - Del global reference - """ - ns = self.get_reference_namespace(name) - ns.pop(name) - self.refresh_after_eval = True - - def copyglobal(self, orig_name, new_name): - """ - Copy global reference - """ - ns = self.get_reference_namespace(orig_name) - ns[new_name] = ns[orig_name] - self.refresh_after_eval = True - - def run(self): - self.ipython_shell = None - while True: - output = pickle.dumps(None, PICKLE_HIGHEST_PROTOCOL) - glbs = self.mglobals() - try: - if DEBUG_MONITOR: - logging.debug("****** Introspection request /Begin ******") - command = PACKET_NOT_RECEIVED - try: - timeout = self.timeout if self.auto_refresh else None - command = read_packet(self.i_request, timeout=timeout) - if command is None: - continue - timed_out = False - except socket.timeout: - timed_out = True - except struct.error: - # This should mean that Spyder GUI has crashed - if DEBUG_MONITOR: - logging.debug("struct.error -> quitting monitor") - break - if timed_out: - if DEBUG_MONITOR: - logging.debug("connection timed out -> updating remote view") - self.update_remote_view() - if DEBUG_MONITOR: - logging.debug("****** Introspection request /End ******") - continue - if DEBUG_MONITOR: - logging.debug("command: %r" % command) - lcls = self.mlocals() - result = eval(command, glbs, lcls) - if DEBUG_MONITOR: - logging.debug(" result: %r" % result) - if self.pdb_obj is None: - lcls["_"] = result - # old com implementation: (see solution (1) in Issue 434) - output = pickle.dumps(result, PICKLE_HIGHEST_PROTOCOL) -# # new com implementation: (see solution (2) in Issue 434) -# output = pickle.dumps((command, result), -# PICKLE_HIGHEST_PROTOCOL) - except SystemExit: - break - except: - if DEBUG_MONITOR: - logging.debug("error!") - log_last_error(LOG_FILENAME, command) - finally: - try: - if DEBUG_MONITOR: - logging.debug("updating remote view") - if self.refresh_after_eval: - self.update_remote_view() - self.refresh_after_eval = False - if DEBUG_MONITOR: - logging.debug("sending result") - logging.debug("****** Introspection request /End ******") - if command is not PACKET_NOT_RECEIVED: - if write_packet is None: - # This may happen during interpreter shutdown - break - else: - write_packet(self.i_request, output, - already_pickled=True) - except AttributeError as error: - if "'NoneType' object has no attribute" in str(error): - # This may happen during interpreter shutdown - break - else: - raise - except TypeError as error: - if "'NoneType' object is not subscriptable" in str(error): - # This may happen during interpreter shutdown - break - else: - raise - - self.i_request.close() - self.n_request.close() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/namespacebrowser.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/namespacebrowser.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/namespacebrowser.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/namespacebrowser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,561 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Namespace browser widget""" - -import os.path as osp -import socket - -from spyderlib.qt.QtGui import (QWidget, QVBoxLayout, QHBoxLayout, QMenu, - QToolButton, QMessageBox, QApplication, - QCursor, QInputDialog) -from spyderlib.qt.QtCore import SIGNAL, Qt, Signal -from spyderlib.qt.compat import getopenfilenames, getsavefilename - -# Local imports -from spyderlib.widgets.externalshell.monitor import ( - monitor_set_global, monitor_get_global, monitor_del_global, - monitor_copy_global, monitor_save_globals, monitor_load_globals, - communicate, REMOTE_SETTINGS) -from spyderlib.widgets.dicteditor import (RemoteDictEditorTableView, - DictEditorTableView) -from spyderlib.widgets.dicteditorutils import globalsfilter -from spyderlib.utils import encoding -from spyderlib.utils.misc import fix_reference_name -from spyderlib.utils.programs import is_module_installed -from spyderlib.utils.qthelpers import (get_icon, create_toolbutton, - add_actions, create_action) -from spyderlib.utils.iofuncs import iofunctions -from spyderlib.widgets.importwizard import ImportWizard -from spyderlib.baseconfig import _, get_supported_types -from spyderlib.py3compat import is_text_string, to_text_string, getcwd - - -SUPPORTED_TYPES = get_supported_types() - - -class NamespaceBrowser(QWidget): - """Namespace browser (global variables explorer widget)""" - sig_option_changed = Signal(str, object) - def __init__(self, parent): - QWidget.__init__(self, parent) - - self.shellwidget = None - self.is_internal_shell = None - self.ipyclient = None - self.is_ipykernel = None - - self.is_visible = True # Do not modify: light mode won't work! - - self.setup_in_progress = None - - # Remote dict editor settings - self.check_all = None - self.exclude_private = None - self.exclude_uppercase = None - self.exclude_capitalized = None - self.exclude_unsupported = None - self.excluded_names = None - self.truncate = None - self.minmax = None - self.remote_editing = None - self.autorefresh = None - - self.editor = None - self.exclude_private_action = None - self.exclude_uppercase_action = None - self.exclude_capitalized_action = None - self.exclude_unsupported_action = None - - self.filename = None - - def setup(self, check_all=None, exclude_private=None, - exclude_uppercase=None, exclude_capitalized=None, - exclude_unsupported=None, excluded_names=None, - truncate=None, minmax=None, remote_editing=None, - autorefresh=None): - """Setup the namespace browser""" - assert self.shellwidget is not None - - self.check_all = check_all - self.exclude_private = exclude_private - self.exclude_uppercase = exclude_uppercase - self.exclude_capitalized = exclude_capitalized - self.exclude_unsupported = exclude_unsupported - self.excluded_names = excluded_names - self.truncate = truncate - self.minmax = minmax - self.remote_editing = remote_editing - self.autorefresh = autorefresh - - if self.editor is not None: - self.editor.setup_menu(truncate, minmax) - self.exclude_private_action.setChecked(exclude_private) - self.exclude_uppercase_action.setChecked(exclude_uppercase) - self.exclude_capitalized_action.setChecked(exclude_capitalized) - self.exclude_unsupported_action.setChecked(exclude_unsupported) - # Don't turn autorefresh on for IPython kernels - # See Issue 1450 - if not self.is_ipykernel: - self.auto_refresh_button.setChecked(autorefresh) - self.refresh_table() - return - - # Dict editor: - if self.is_internal_shell: - self.editor = DictEditorTableView(self, None, truncate=truncate, - minmax=minmax) - else: - self.editor = RemoteDictEditorTableView(self, None, - truncate=truncate, minmax=minmax, - remote_editing=remote_editing, - get_value_func=self.get_value, - set_value_func=self.set_value, - new_value_func=self.set_value, - remove_values_func=self.remove_values, - copy_value_func=self.copy_value, - is_list_func=self.is_list, - get_len_func=self.get_len, - is_array_func=self.is_array, - is_image_func=self.is_image, - is_dict_func=self.is_dict, - is_data_frame_func=self.is_data_frame, - is_series_func=self.is_series, - get_array_shape_func=self.get_array_shape, - get_array_ndim_func=self.get_array_ndim, - oedit_func=self.oedit, - plot_func=self.plot, imshow_func=self.imshow, - show_image_func=self.show_image) - self.editor.sig_option_changed.connect(self.sig_option_changed.emit) - self.editor.sig_files_dropped.connect(self.import_data) - - - # Setup layout - hlayout = QHBoxLayout() - vlayout = QVBoxLayout() - toolbar = self.setup_toolbar(exclude_private, exclude_uppercase, - exclude_capitalized, exclude_unsupported, - autorefresh) - vlayout.setAlignment(Qt.AlignTop) - for widget in toolbar: - vlayout.addWidget(widget) - hlayout.addWidget(self.editor) - hlayout.addLayout(vlayout) - self.setLayout(hlayout) - hlayout.setContentsMargins(0, 0, 0, 0) - - self.sig_option_changed.connect(self.option_changed) - - def set_shellwidget(self, shellwidget): - """Bind shellwidget instance to namespace browser""" - self.shellwidget = shellwidget - from spyderlib.widgets import internalshell - self.is_internal_shell = isinstance(self.shellwidget, - internalshell.InternalShell) - self.is_ipykernel = self.shellwidget.is_ipykernel - if not self.is_internal_shell: - shellwidget.set_namespacebrowser(self) - - def set_ipyclient(self, ipyclient): - """Bind ipyclient instance to namespace browser""" - self.ipyclient = ipyclient - - def setup_toolbar(self, exclude_private, exclude_uppercase, - exclude_capitalized, exclude_unsupported, autorefresh): - """Setup toolbar""" - self.setup_in_progress = True - - toolbar = [] - - refresh_button = create_toolbutton(self, text=_("Refresh"), - icon=get_icon('reload.png'), - triggered=self.refresh_table) - self.auto_refresh_button = create_toolbutton(self, - text=_("Refresh periodically"), - icon=get_icon('auto_reload.png'), - toggled=self.toggle_auto_refresh) - self.auto_refresh_button.setChecked(autorefresh) - load_button = create_toolbutton(self, text=_("Import data"), - icon=get_icon('fileimport.png'), - triggered=self.import_data) - self.save_button = create_toolbutton(self, text=_("Save data"), - icon=get_icon('filesave.png'), - triggered=lambda: self.save_data(self.filename)) - self.save_button.setEnabled(False) - save_as_button = create_toolbutton(self, - text=_("Save data as..."), - icon=get_icon('filesaveas.png'), - triggered=self.save_data) - toolbar += [refresh_button, self.auto_refresh_button, load_button, - self.save_button, save_as_button] - - self.exclude_private_action = create_action(self, - _("Exclude private references"), - tip=_("Exclude references which name starts" - " with an underscore"), - toggled=lambda state: - self.sig_option_changed.emit('exclude_private', state)) - self.exclude_private_action.setChecked(exclude_private) - - self.exclude_uppercase_action = create_action(self, - _("Exclude all-uppercase references"), - tip=_("Exclude references which name is uppercase"), - toggled=lambda state: - self.sig_option_changed.emit('exclude_uppercase', state)) - self.exclude_uppercase_action.setChecked(exclude_uppercase) - - self.exclude_capitalized_action = create_action(self, - _("Exclude capitalized references"), - tip=_("Exclude references which name starts with an " - "uppercase character"), - toggled=lambda state: - self.sig_option_changed.emit('exclude_capitalized', state)) - self.exclude_capitalized_action.setChecked(exclude_capitalized) - - self.exclude_unsupported_action = create_action(self, - _("Exclude unsupported data types"), - tip=_("Exclude references to unsupported data types" - " (i.e. which won't be handled/saved correctly)"), - toggled=lambda state: - self.sig_option_changed.emit('exclude_unsupported', state)) - self.exclude_unsupported_action.setChecked(exclude_unsupported) - - options_button = create_toolbutton(self, text=_("Options"), - icon=get_icon('tooloptions.png')) - toolbar.append(options_button) - options_button.setPopupMode(QToolButton.InstantPopup) - menu = QMenu(self) - editor = self.editor - actions = [self.exclude_private_action, self.exclude_uppercase_action, - self.exclude_capitalized_action, - self.exclude_unsupported_action, None, - editor.truncate_action] - if is_module_installed('numpy'): - actions.append(editor.minmax_action) - add_actions(menu, actions) - options_button.setMenu(menu) - - self.setup_in_progress = False - - return toolbar - - def option_changed(self, option, value): - """Option has changed""" - setattr(self, to_text_string(option), value) - if not self.is_internal_shell: - settings = self.get_view_settings() - communicate(self._get_sock(), - 'set_remote_view_settings()', settings=[settings]) - - def visibility_changed(self, enable): - """Notify the widget whether its container (the namespace browser - plugin is visible or not""" - # This is slowing down Spyder a lot if too much data is present in - # the Variable Explorer, and users give focus to it after being hidden. - # This also happens when the Variable Explorer is visible and users - # give focus to Spyder after using another application (like Chrome - # or Firefox). - # That's why we've decided to remove this feature - # Fixes Issue 2593 - # - # self.is_visible = enable - # if enable: - # self.refresh_table() - pass - - def toggle_auto_refresh(self, state): - """Toggle auto refresh state""" - self.autorefresh = state - if not self.setup_in_progress and not self.is_internal_shell: - communicate(self._get_sock(), - "set_monitor_auto_refresh(%r)" % state) - - def _get_sock(self): - """Return socket connection""" - return self.shellwidget.introspection_socket - - def get_internal_shell_filter(self, mode, check_all=None): - """ - Return internal shell data types filter: - * check_all: check all elements data types for sequences - (dict, list, tuple) - * mode (string): 'editable' or 'picklable' - """ - assert mode in list(SUPPORTED_TYPES.keys()) - if check_all is None: - check_all = self.check_all - def wsfilter(input_dict, check_all=check_all, - filters=tuple(SUPPORTED_TYPES[mode])): - """Keep only objects that can be pickled""" - return globalsfilter( - input_dict, check_all=check_all, filters=filters, - exclude_private=self.exclude_private, - exclude_uppercase=self.exclude_uppercase, - exclude_capitalized=self.exclude_capitalized, - exclude_unsupported=self.exclude_unsupported, - excluded_names=self.excluded_names) - return wsfilter - - def get_view_settings(self): - """Return dict editor view settings""" - settings = {} - for name in REMOTE_SETTINGS: - settings[name] = getattr(self, name) - return settings - - def refresh_table(self): - """Refresh variable table""" - if self.is_visible and self.isVisible(): - if self.is_internal_shell: - # Internal shell - wsfilter = self.get_internal_shell_filter('editable') - self.editor.set_filter(wsfilter) - interpreter = self.shellwidget.interpreter - if interpreter is not None: - self.editor.set_data(interpreter.namespace) - self.editor.adjust_columns() - elif self.shellwidget.is_running(): - # import time; print >>STDOUT, time.ctime(time.time()), "Refreshing namespace browser" - sock = self._get_sock() - if sock is None: - return - try: - communicate(sock, "refresh()") - except socket.error: - # Process was terminated before calling this method - pass - - def process_remote_view(self, remote_view): - """Process remote view""" - if remote_view is not None: - self.set_data(remote_view) - - #------ Remote Python process commands ------------------------------------ - def get_value(self, name): - value = monitor_get_global(self._get_sock(), name) - if value is None: - if communicate(self._get_sock(), '%s is not None' % name): - import pickle - msg = to_text_string(_("Object %s is not picklable") - % name) - raise pickle.PicklingError(msg) - return value - - def set_value(self, name, value): - monitor_set_global(self._get_sock(), name, value) - self.refresh_table() - - def remove_values(self, names): - for name in names: - monitor_del_global(self._get_sock(), name) - self.refresh_table() - - def copy_value(self, orig_name, new_name): - monitor_copy_global(self._get_sock(), orig_name, new_name) - self.refresh_table() - - def is_list(self, name): - """Return True if variable is a list or a tuple""" - return communicate(self._get_sock(), - 'isinstance(%s, (tuple, list))' % name) - - def is_dict(self, name): - """Return True if variable is a dictionary""" - return communicate(self._get_sock(), 'isinstance(%s, dict)' % name) - - def get_len(self, name): - """Return sequence length""" - return communicate(self._get_sock(), "len(%s)" % name) - - def is_array(self, name): - """Return True if variable is a NumPy array""" - return communicate(self._get_sock(), 'is_array("%s")' % name) - - def is_image(self, name): - """Return True if variable is a PIL.Image image""" - return communicate(self._get_sock(), 'is_image("%s")' % name) - - def is_data_frame(self, name): - """Return True if variable is a DataFrame""" - return communicate(self._get_sock(), - "isinstance(globals()['%s'], DataFrame)" % name) - - def is_series(self, name): - """Return True if variable is a Series""" - return communicate(self._get_sock(), - "isinstance(globals()['%s'], Series)" % name) - - def get_array_shape(self, name): - """Return array's shape""" - return communicate(self._get_sock(), "%s.shape" % name) - - def get_array_ndim(self, name): - """Return array's ndim""" - return communicate(self._get_sock(), "%s.ndim" % name) - - def plot(self, name, funcname): - command = "import spyderlib.pyplot; "\ - "__fig__ = spyderlib.pyplot.figure(); "\ - "__items__ = getattr(spyderlib.pyplot, '%s')(%s); "\ - "spyderlib.pyplot.show(); "\ - "del __fig__, __items__;" % (funcname, name) - if self.is_ipykernel: - self.ipyclient.shellwidget.execute("%%varexp --%s %s" % (funcname, - name)) - else: - self.shellwidget.send_to_process(command) - - def imshow(self, name): - command = "import spyderlib.pyplot; " \ - "__fig__ = spyderlib.pyplot.figure(); " \ - "__items__ = spyderlib.pyplot.imshow(%s); " \ - "spyderlib.pyplot.show(); del __fig__, __items__;" % name - if self.is_ipykernel: - self.ipyclient.shellwidget.execute("%%varexp --imshow %s" % name) - else: - self.shellwidget.send_to_process(command) - - def show_image(self, name): - command = "%s.show()" % name - if self.is_ipykernel: - self.ipyclient.shellwidget.execute(command) - else: - self.shellwidget.send_to_process(command) - - def oedit(self, name): - command = "from spyderlib.widgets.objecteditor import oedit; " \ - "oedit('%s', modal=False, namespace=locals());" % name - self.shellwidget.send_to_process(command) - - #------ Set, load and save data ------------------------------------------- - def set_data(self, data): - """Set data""" - if data != self.editor.model.get_data(): - self.editor.set_data(data) - self.editor.adjust_columns() - - def collapse(self): - """Collapse""" - self.emit(SIGNAL('collapse()')) - - def import_data(self, filenames=None): - """Import data from text file""" - title = _("Import data") - if filenames is None: - if self.filename is None: - basedir = getcwd() - else: - basedir = osp.dirname(self.filename) - filenames, _selfilter = getopenfilenames(self, title, basedir, - iofunctions.load_filters) - if not filenames: - return - elif is_text_string(filenames): - filenames = [filenames] - - - for filename in filenames: - - self.filename = to_text_string(filename) - ext = osp.splitext(self.filename)[1].lower() - - if ext not in iofunctions.load_funcs: - buttons = QMessageBox.Yes | QMessageBox.Cancel - answer = QMessageBox.question(self, title, - _("Unsupported file extension '%s'

    " - "Would you like to import it anyway " - "(by selecting a known file format)?" - ) % ext, buttons) - if answer == QMessageBox.Cancel: - return - formats = list(iofunctions.load_extensions.keys()) - item, ok = QInputDialog.getItem(self, title, - _('Open file as:'), - formats, 0, False) - if ok: - ext = iofunctions.load_extensions[to_text_string(item)] - else: - return - - load_func = iofunctions.load_funcs[ext] - - # 'import_wizard' (self.setup_io) - if is_text_string(load_func): - # Import data with import wizard - error_message = None - try: - text, _encoding = encoding.read(self.filename) - if self.is_internal_shell: - self.editor.import_from_string(text) - else: - base_name = osp.basename(self.filename) - editor = ImportWizard(self, text, title=base_name, - varname=fix_reference_name(base_name)) - if editor.exec_(): - var_name, clip_data = editor.get_data() - monitor_set_global(self._get_sock(), - var_name, clip_data) - except Exception as error: - error_message = str(error) - else: - QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - QApplication.processEvents() - if self.is_internal_shell: - namespace, error_message = load_func(self.filename) - interpreter = self.shellwidget.interpreter - for key in list(namespace.keys()): - new_key = fix_reference_name(key, - blacklist=list(interpreter.namespace.keys())) - if new_key != key: - namespace[new_key] = namespace.pop(key) - if error_message is None: - interpreter.namespace.update(namespace) - else: - error_message = monitor_load_globals(self._get_sock(), - self.filename, ext) - QApplication.restoreOverrideCursor() - QApplication.processEvents() - - if error_message is not None: - QMessageBox.critical(self, title, - _("Unable to load '%s'" - "

    Error message:
    %s" - ) % (self.filename, error_message)) - self.refresh_table() - - - def save_data(self, filename=None): - """Save data""" - if filename is None: - filename = self.filename - if filename is None: - filename = getcwd() - filename, _selfilter = getsavefilename(self, _("Save data"), - filename, - iofunctions.save_filters) - if filename: - self.filename = filename - else: - return False - QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - QApplication.processEvents() - if self.is_internal_shell: - wsfilter = self.get_internal_shell_filter('picklable', - check_all=True) - namespace = wsfilter(self.shellwidget.interpreter.namespace).copy() - error_message = iofunctions.save(namespace, filename) - else: - settings = self.get_view_settings() - error_message = monitor_save_globals(self._get_sock(), - settings, filename) - QApplication.restoreOverrideCursor() - QApplication.processEvents() - if error_message is not None: - QMessageBox.critical(self, _("Save data"), - _("Unable to save current workspace" - "

    Error message:
    %s") % error_message) - self.save_button.setEnabled(self.filename is not None) - diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/osx_app_site.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/osx_app_site.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/osx_app_site.py 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/osx_app_site.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,163 +0,0 @@ -# -# IMPORTANT NOTE: Don't add a coding line here! It's not necessary for -# site files -# -# Spyder's MacOS X App site.py additions -# -# It includes missing variables and paths that are not added by -# py2app to its own site.py -# -# These functions were taken verbatim from Python 2.7.3 site.py -# - -import sys -import os -try: - import __builtin__ as builtins -except ImportError: - # Python 3 - import builtins - -# for distutils.commands.install -# These values are initialized by the getuserbase() and getusersitepackages() -# functions. -USER_SITE = None -USER_BASE = None - - -def getuserbase(): - """Returns the `user base` directory path. - - The `user base` directory can be used to store data. If the global - variable ``USER_BASE`` is not initialized yet, this function will also set - it. - """ - global USER_BASE - if USER_BASE is not None: - return USER_BASE - from sysconfig import get_config_var - USER_BASE = get_config_var('userbase') - return USER_BASE - -def getusersitepackages(): - """Returns the user-specific site-packages directory path. - - If the global variable ``USER_SITE`` is not initialized yet, this - function will also set it. - """ - global USER_SITE - user_base = getuserbase() # this will also set USER_BASE - - if USER_SITE is not None: - return USER_SITE - - from sysconfig import get_path - - if sys.platform == 'darwin': - from sysconfig import get_config_var - if get_config_var('PYTHONFRAMEWORK'): - USER_SITE = get_path('purelib', 'osx_framework_user') - return USER_SITE - - USER_SITE = get_path('purelib', '%s_user' % os.name) - return USER_SITE - - -class _Printer(object): - """interactive prompt objects for printing the license text, a list of - contributors and the copyright notice.""" - - MAXLINES = 23 - - def __init__(self, name, data, files=(), dirs=()): - self.__name = name - self.__data = data - self.__files = files - self.__dirs = dirs - self.__lines = None - - def __setup(self): - if self.__lines: - return - data = None - for dir in self.__dirs: - for filename in self.__files: - filename = os.path.join(dir, filename) - try: - fp = open(filename, "rU") - data = fp.read() - fp.close() - break - except IOError: - pass - if data: - break - if not data: - data = self.__data - self.__lines = data.split('\n') - self.__linecnt = len(self.__lines) - - def __repr__(self): - self.__setup() - if len(self.__lines) <= self.MAXLINES: - return "\n".join(self.__lines) - else: - return "Type %s() to see the full %s text" % ((self.__name,)*2) - - def __call__(self): - self.__setup() - prompt = 'Hit Return for more, or q (and Return) to quit: ' - lineno = 0 - while 1: - try: - for i in range(lineno, lineno + self.MAXLINES): - print(self.__lines[i]) - except IndexError: - break - else: - lineno += self.MAXLINES - key = None - while key is None: - try: - key = raw_input(prompt) - except NameError: - # Python 3 - key = input(prompt) - if key not in ('', 'q'): - key = None - if key == 'q': - break - -def setcopyright(): - """Set 'copyright' and 'credits' in builtins""" - builtins.copyright = _Printer("copyright", sys.copyright) - if sys.platform[:4] == 'java': - builtins.credits = _Printer( - "credits", - "Jython is maintained by the Jython developers (www.jython.org).") - else: - builtins.credits = _Printer("credits", """\ - Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands - for supporting Python development. See www.python.org for more information.""") - here = os.path.dirname(os.__file__) - builtins.license = _Printer( - "license", "See http://www.python.org/%.3s/license.html" % sys.version, - ["LICENSE.txt", "LICENSE"], - [os.path.join(here, os.pardir), here, os.curdir]) - - -class _Helper(object): - """Define the builtin 'help'. - This is a wrapper around pydoc.help (with a twist). - - """ - - def __repr__(self): - return "Type help() for interactive help, " \ - "or help(object) for help about object." - def __call__(self, *args, **kwds): - import pydoc - return pydoc.help(*args, **kwds) - -def sethelper(): - builtins.help = _Helper() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/pythonshell.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/pythonshell.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/pythonshell.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/pythonshell.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,639 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""External Python Shell widget: execute Python script in a separate process""" - -import sys -import os -import os.path as osp -import socket - -from spyderlib.qt.QtGui import QApplication, QMessageBox, QSplitter, QMenu -from spyderlib.qt.QtCore import QProcess, SIGNAL, Qt -from spyderlib.qt.compat import getexistingdirectory - -# Local imports -from spyderlib.utils.qthelpers import (get_icon, get_std_icon, add_actions, - create_toolbutton, create_action, - DialogManager) -from spyderlib.utils.environ import RemoteEnvDialog -from spyderlib.utils.programs import get_python_args -from spyderlib.utils.misc import get_python_executable -from spyderlib.baseconfig import (_, get_module_source_path, DEBUG, - MAC_APP_NAME, running_in_mac_app) -from spyderlib.widgets.shell import PythonShellWidget -from spyderlib.widgets.externalshell.namespacebrowser import NamespaceBrowser -from spyderlib.utils.bsdsocket import communicate, write_packet -from spyderlib.widgets.externalshell.baseshell import (ExternalShellBase, - add_pathlist_to_PYTHONPATH) -from spyderlib.widgets.dicteditor import DictEditor -from spyderlib.py3compat import (is_text_string, to_text_string, - to_binary_string) - - -class ExtPythonShellWidget(PythonShellWidget): - def __init__(self, parent, history_filename, profile=False): - PythonShellWidget.__init__(self, parent, history_filename, profile) - self.path = [] - - def set_externalshell(self, externalshell): - # ExternalShellBase instance: - self.externalshell = externalshell - - def clear_terminal(self): - """Reimplement ShellBaseWidget method""" - self.clear() - self.emit(SIGNAL("execute(QString)"), "\n") - - def execute_lines(self, lines): - """ - Execute a set of lines as multiple command - lines: multiple lines of text to be executed as single commands - """ - for line in lines.splitlines(): - stripped_line = line.strip() - if stripped_line.startswith('#'): - continue - self.write(line+os.linesep, flush=True) - self.execute_command(line) - # Workaround for Issue 502 - # Emmiting wait_for_ready_read was making the console hang - # in Mac OS X - if sys.platform.startswith("darwin"): - import time - time.sleep(0.025) - else: - self.emit(SIGNAL("wait_for_ready_read()")) - self.flush() - - #------ Code completion / Calltips - def ask_monitor(self, command, settings=[]): - sock = self.externalshell.introspection_socket - if sock is None: - return - try: - return communicate(sock, command, settings=settings) - except socket.error: - # Process was just closed - pass - except MemoryError: - # Happens when monitor is not ready on slow machines - pass - - def get_dir(self, objtxt): - """Return dir(object)""" - return self.ask_monitor("__get_dir__('%s')" % objtxt) - - def get_globals_keys(self): - """Return shell globals() keys""" - return self.ask_monitor("get_globals_keys()") - - def get_cdlistdir(self): - """Return shell current directory list dir""" - return self.ask_monitor("getcdlistdir()") - - def iscallable(self, objtxt): - """Is object callable?""" - return self.ask_monitor("__iscallable__('%s')" % objtxt) - - def get_arglist(self, objtxt): - """Get func/method argument list""" - return self.ask_monitor("__get_arglist__('%s')" % objtxt) - - def get__doc__(self, objtxt): - """Get object __doc__""" - return self.ask_monitor("__get__doc____('%s')" % objtxt) - - def get_doc(self, objtxt): - """Get object documentation dictionary""" - return self.ask_monitor("__get_doc__('%s')" % objtxt) - - def get_source(self, objtxt): - """Get object source""" - return self.ask_monitor("__get_source__('%s')" % objtxt) - - def is_defined(self, objtxt, force_import=False): - """Return True if object is defined""" - return self.ask_monitor("isdefined('%s', force_import=%s)" - % (objtxt, force_import)) - - def get_module_completion(self, objtxt): - """Return module completion list associated to object name""" - return self.ask_monitor("getmodcomplist('%s', %s)" % \ - (objtxt, self.path)) - - def get_cwd(self): - """Return shell current working directory""" - return self.ask_monitor("getcwd()") - - def set_cwd(self, dirname): - """Set shell current working directory""" - return self.ask_monitor("setcwd(r'%s')" % dirname) - - def get_env(self): - """Return environment variables: os.environ""" - return self.ask_monitor("getenv()") - - def set_env(self, env): - """Set environment variables via os.environ""" - return self.ask_monitor('setenv()', settings=[env]) - - def get_syspath(self): - """Return sys.path[:]""" - return self.ask_monitor("getsyspath()") - - def set_spyder_breakpoints(self): - """Set Spyder breakpoints into debugging session""" - return self.ask_monitor("set_spyder_breakpoints()") - - -class ExternalPythonShell(ExternalShellBase): - """External Shell widget: execute Python script in a separate process""" - SHELL_CLASS = ExtPythonShellWidget - def __init__(self, parent=None, fname=None, wdir=None, - interact=False, debug=False, path=[], python_args='', - ipykernel=False, arguments='', stand_alone=None, - umr_enabled=True, umr_namelist=[], umr_verbose=True, - pythonstartup=None, pythonexecutable=None, - monitor_enabled=True, mpl_backend=None, ets_backend='qt4', - qt_api=None, pyqt_api=0, - ignore_sip_setapi_errors=False, merge_output_channels=False, - colorize_sys_stderr=False, autorefresh_timeout=3000, - autorefresh_state=True, light_background=True, - menu_actions=None, show_buttons_inside=True, - show_elapsed_time=True): - - assert qt_api in (None, 'pyqt', 'pyside') - - self.namespacebrowser = None # namespace browser widget! - - self.dialog_manager = DialogManager() - - self.stand_alone = stand_alone # stand alone settings (None: plugin) - self.interact = interact - self.is_ipykernel = ipykernel - self.pythonstartup = pythonstartup - self.pythonexecutable = pythonexecutable - self.monitor_enabled = monitor_enabled - self.mpl_backend = mpl_backend - self.ets_backend = ets_backend - self.qt_api = qt_api - self.pyqt_api = pyqt_api - self.ignore_sip_setapi_errors = ignore_sip_setapi_errors - self.merge_output_channels = merge_output_channels - self.colorize_sys_stderr = colorize_sys_stderr - self.umr_enabled = umr_enabled - self.umr_namelist = umr_namelist - self.umr_verbose = umr_verbose - self.autorefresh_timeout = autorefresh_timeout - self.autorefresh_state = autorefresh_state - - self.namespacebrowser_button = None - self.cwd_button = None - self.env_button = None - self.syspath_button = None - self.terminate_button = None - - self.notification_thread = None - - ExternalShellBase.__init__(self, parent=parent, fname=fname, wdir=wdir, - history_filename='history.py', - light_background=light_background, - menu_actions=menu_actions, - show_buttons_inside=show_buttons_inside, - show_elapsed_time=show_elapsed_time) - - if self.pythonexecutable is None: - self.pythonexecutable = get_python_executable() - - self.python_args = None - if python_args: - assert is_text_string(python_args) - self.python_args = python_args - - assert is_text_string(arguments) - self.arguments = arguments - - self.connection_file = None - - if self.is_ipykernel: - self.interact = False - # Running our custom startup script for IPython kernels: - # (see spyderlib/widgets/externalshell/start_ipython_kernel.py) - self.fname = get_module_source_path( - 'spyderlib.widgets.externalshell', 'start_ipython_kernel.py') - - self.shell.set_externalshell(self) - - self.toggle_globals_explorer(False) - self.interact_action.setChecked(self.interact) - self.debug_action.setChecked(debug) - - self.introspection_socket = None - self.is_interpreter = fname is None - - if self.is_interpreter: - self.terminate_button.hide() - - # Additional python path list - self.path = path - self.shell.path = path - - def set_introspection_socket(self, introspection_socket): - self.introspection_socket = introspection_socket - if self.namespacebrowser is not None: - settings = self.namespacebrowser.get_view_settings() - communicate(introspection_socket, - 'set_remote_view_settings()', settings=[settings]) - - def set_autorefresh_timeout(self, interval): - if self.introspection_socket is not None: - try: - communicate(self.introspection_socket, - "set_monitor_timeout(%d)" % interval) - except socket.error: - pass - - def closeEvent(self, event): - self.quit_monitor() - ExternalShellBase.closeEvent(self, event) - - def get_toolbar_buttons(self): - ExternalShellBase.get_toolbar_buttons(self) - if self.namespacebrowser_button is None \ - and self.stand_alone is not None: - self.namespacebrowser_button = create_toolbutton(self, - text=_("Variables"), icon=get_icon('dictedit.png'), - tip=_("Show/hide global variables explorer"), - toggled=self.toggle_globals_explorer, text_beside_icon=True) - if self.terminate_button is None: - self.terminate_button = create_toolbutton(self, - text=_("Terminate"), icon=get_icon('stop.png'), - tip=_("Attempts to stop the process. The process\n" - "may not exit as a result of clicking this\n" - "button (it is given the chance to prompt\n" - "the user for any unsaved files, etc).")) - buttons = [] - if self.namespacebrowser_button is not None: - buttons.append(self.namespacebrowser_button) - buttons += [self.run_button, self.terminate_button, self.kill_button, - self.options_button] - return buttons - - def get_options_menu(self): - ExternalShellBase.get_options_menu(self) - self.interact_action = create_action(self, _("Interact")) - self.interact_action.setCheckable(True) - self.debug_action = create_action(self, _("Debug")) - self.debug_action.setCheckable(True) - self.args_action = create_action(self, _("Arguments..."), - triggered=self.get_arguments) - run_settings_menu = QMenu(_("Run settings"), self) - add_actions(run_settings_menu, - (self.interact_action, self.debug_action, self.args_action)) - self.cwd_button = create_action(self, _("Working directory"), - icon=get_std_icon('DirOpenIcon'), - tip=_("Set current working directory"), - triggered=self.set_current_working_directory) - self.env_button = create_action(self, _("Environment variables"), - icon=get_icon('environ.png'), - triggered=self.show_env) - self.syspath_button = create_action(self, - _("Show sys.path contents"), - icon=get_icon('syspath.png'), - triggered=self.show_syspath) - actions = [run_settings_menu, self.show_time_action, None, - self.cwd_button, self.env_button, self.syspath_button] - if self.menu_actions is not None: - actions += [None]+self.menu_actions - return actions - - def is_interpreter(self): - """Return True if shellwidget is a Python interpreter""" - return self.is_interpreter - - def get_shell_widget(self): - if self.stand_alone is None: - return self.shell - else: - self.namespacebrowser = NamespaceBrowser(self) - settings = self.stand_alone - self.namespacebrowser.set_shellwidget(self) - self.namespacebrowser.setup(**settings) - self.connect(self.namespacebrowser, SIGNAL('collapse()'), - lambda: self.toggle_globals_explorer(False)) - # Shell splitter - self.splitter = splitter = QSplitter(Qt.Vertical, self) - self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), - self.splitter_moved) - splitter.addWidget(self.shell) - splitter.setCollapsible(0, False) - splitter.addWidget(self.namespacebrowser) - splitter.setStretchFactor(0, 1) - splitter.setStretchFactor(1, 0) - splitter.setHandleWidth(5) - splitter.setSizes([2, 1]) - return splitter - - def get_icon(self): - return get_icon('python.png') - - def set_buttons_runnning_state(self, state): - ExternalShellBase.set_buttons_runnning_state(self, state) - self.interact_action.setEnabled(not state and not self.is_interpreter) - self.debug_action.setEnabled(not state and not self.is_interpreter) - self.args_action.setEnabled(not state and not self.is_interpreter) - if state: - if self.arguments: - argstr = _("Arguments: %s") % self.arguments - else: - argstr = _("No argument") - else: - argstr = _("Arguments...") - self.args_action.setText(argstr) - self.terminate_button.setVisible(not self.is_interpreter and state) - if not state: - self.toggle_globals_explorer(False) - for btn in (self.cwd_button, self.env_button, self.syspath_button): - btn.setEnabled(state and self.monitor_enabled) - if self.namespacebrowser_button is not None: - self.namespacebrowser_button.setEnabled(state) - - def set_namespacebrowser(self, namespacebrowser): - """ - Set namespace browser *widget* - Note: this method is not used in stand alone mode - """ - self.namespacebrowser = namespacebrowser - self.configure_namespacebrowser() - - def configure_namespacebrowser(self): - """Connect the namespace browser to the notification thread""" - if self.notification_thread is not None: - self.connect(self.notification_thread, - SIGNAL('refresh_namespace_browser()'), - self.namespacebrowser.refresh_table) - signal = self.notification_thread.sig_process_remote_view - signal.connect(lambda data: - self.namespacebrowser.process_remote_view(data)) - - def create_process(self): - self.shell.clear() - - self.process = QProcess(self) - if self.merge_output_channels: - self.process.setProcessChannelMode(QProcess.MergedChannels) - else: - self.process.setProcessChannelMode(QProcess.SeparateChannels) - self.connect(self.shell, SIGNAL("wait_for_ready_read()"), - lambda: self.process.waitForReadyRead(250)) - - # Working directory - if self.wdir is not None: - self.process.setWorkingDirectory(self.wdir) - - #-------------------------Python specific------------------------------- - # Python arguments - p_args = ['-u'] - if DEBUG >= 3: - p_args += ['-v'] - p_args += get_python_args(self.fname, self.python_args, - self.interact_action.isChecked(), - self.debug_action.isChecked(), - self.arguments) - - env = [to_text_string(_path) - for _path in self.process.systemEnvironment()] - if self.pythonstartup: - env.append('PYTHONSTARTUP=%s' % self.pythonstartup) - - # Set standard input/output encoding for Python consoles - # (IPython handles it on its own) - # See http://stackoverflow.com/q/26312400/438386, specifically - # the comments of Martijn Pieters - if not self.is_ipykernel: - env.append('PYTHONIOENCODING=UTF-8') - - # Monitor - if self.monitor_enabled: - env.append('SPYDER_SHELL_ID=%s' % id(self)) - env.append('SPYDER_AR_TIMEOUT=%d' % self.autorefresh_timeout) - env.append('SPYDER_AR_STATE=%r' % self.autorefresh_state) - from spyderlib.widgets.externalshell import introspection - introspection_server = introspection.start_introspection_server() - introspection_server.register(self) - notification_server = introspection.start_notification_server() - self.notification_thread = notification_server.register(self) - self.connect(self.notification_thread, SIGNAL('pdb(QString,int)'), - lambda fname, lineno: - self.emit(SIGNAL('pdb(QString,int)'), fname, lineno)) - self.connect(self.notification_thread, - SIGNAL('new_ipython_kernel(QString)'), - lambda args: - self.emit(SIGNAL('create_ipython_client(QString)'), - args)) - self.connect(self.notification_thread, - SIGNAL('open_file(QString,int)'), - lambda fname, lineno: - self.emit(SIGNAL('open_file(QString,int)'), - fname, lineno)) - if self.namespacebrowser is not None: - self.configure_namespacebrowser() - env.append('SPYDER_I_PORT=%d' % introspection_server.port) - env.append('SPYDER_N_PORT=%d' % notification_server.port) - - # External modules options - env.append('ETS_TOOLKIT=%s' % self.ets_backend) - if self.mpl_backend: - env.append('MATPLOTLIB_BACKEND=%s' % self.mpl_backend) - if self.qt_api: - env.append('QT_API=%s' % self.qt_api) - env.append('COLORIZE_SYS_STDERR=%s' % self.colorize_sys_stderr) -# # Socket-based alternative (see input hook in sitecustomize.py): -# if self.install_qt_inputhook: -# from PyQt4.QtNetwork import QLocalServer -# self.local_server = QLocalServer() -# self.local_server.listen(str(id(self))) - if self.pyqt_api: - env.append('PYQT_API=%d' % self.pyqt_api) - env.append('IGNORE_SIP_SETAPI_ERRORS=%s' - % self.ignore_sip_setapi_errors) - - # User Module Deleter - if self.is_interpreter: - env.append('UMR_ENABLED=%r' % self.umr_enabled) - env.append('UMR_NAMELIST=%s' % ','.join(self.umr_namelist)) - env.append('UMR_VERBOSE=%r' % self.umr_verbose) - env.append('MATPLOTLIB_ION=True') - else: - if self.interact: - env.append('MATPLOTLIB_ION=True') - else: - env.append('MATPLOTLIB_ION=False') - - # IPython kernel - env.append('IPYTHON_KERNEL=%r' % self.is_ipykernel) - - # Add sitecustomize path to path list - pathlist = [] - scpath = osp.dirname(osp.abspath(__file__)) - pathlist.append(scpath) - - # Adding Spyder path - pathlist += self.path - - # Adding path list to PYTHONPATH environment variable - add_pathlist_to_PYTHONPATH(env, pathlist) - - #-------------------------Python specific------------------------------- - - self.connect(self.process, SIGNAL("readyReadStandardOutput()"), - self.write_output) - self.connect(self.process, SIGNAL("readyReadStandardError()"), - self.write_error) - self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), - self.finished) - - self.connect(self, SIGNAL('finished()'), self.dialog_manager.close_all) - - self.connect(self.terminate_button, SIGNAL("clicked()"), - self.process.terminate) - self.connect(self.kill_button, SIGNAL("clicked()"), - self.process.kill) - - #-------------------------Python specific------------------------------- - # Fixes for our Mac app: - # 1. PYTHONPATH and PYTHONHOME are set while bootstrapping the app, - # but their values are messing sys.path for external interpreters - # (e.g. EPD) so we need to remove them from the environment. - # 2. Set PYTHONPATH again but without grabbing entries defined in the - # environment (Fixes Issue 1321) - # 3. Remove PYTHONOPTIMIZE from env so that we can have assert - # statements working with our interpreters (See Issue 1281) - if running_in_mac_app(): - env.append('SPYDER_INTERPRETER=%s' % self.pythonexecutable) - if MAC_APP_NAME not in self.pythonexecutable: - env = [p for p in env if not (p.startswith('PYTHONPATH') or \ - p.startswith('PYTHONHOME'))] # 1. - - add_pathlist_to_PYTHONPATH(env, pathlist, drop_env=True) # 2. - env = [p for p in env if not p.startswith('PYTHONOPTIMIZE')] # 3. - - self.process.setEnvironment(env) - self.process.start(self.pythonexecutable, p_args) - #-------------------------Python specific------------------------------- - - running = self.process.waitForStarted(3000) - self.set_running_state(running) - if not running: - if self.is_ipykernel: - self.emit(SIGNAL("ipython_kernel_start_error(QString)"), - _("The kernel failed to start!! That's all we know... " - "Please close this console and open a new one.")) - else: - QMessageBox.critical(self, _("Error"), - _("A Python console failed to start!")) - else: - self.shell.setFocus() - self.emit(SIGNAL('started()')) - return self.process - - def finished(self, exit_code, exit_status): - """Reimplement ExternalShellBase method""" - if self.is_ipykernel and exit_code == 1: - self.emit(SIGNAL("ipython_kernel_start_error(QString)"), - self.shell.get_text_with_eol()) - ExternalShellBase.finished(self, exit_code, exit_status) - self.introspection_socket = None - - -#=============================================================================== -# Input/Output -#=============================================================================== - def write_error(self): - if os.name == 'nt': - #---This is apparently necessary only on Windows (not sure though): - # emptying standard output buffer before writing error output - self.process.setReadChannel(QProcess.StandardOutput) - if self.process.waitForReadyRead(1): - self.write_output() - self.shell.write_error(self.get_stderr()) - QApplication.processEvents() - - def send_to_process(self, text): - if not self.is_running(): - return - - if not is_text_string(text): - text = to_text_string(text) - if self.mpl_backend == 'Qt4Agg' and os.name == 'nt' and \ - self.introspection_socket is not None: - communicate(self.introspection_socket, - "toggle_inputhook_flag(True)") -# # Socket-based alternative (see input hook in sitecustomize.py): -# while self.local_server.hasPendingConnections(): -# self.local_server.nextPendingConnection().write('go!') - if any([text == cmd for cmd in ['%ls', '%pwd', '%scientific']]) or \ - any([text.startswith(cmd) for cmd in ['%cd ', '%clear ']]): - text = 'evalsc(r"%s")\n' % text - if not text.endswith('\n'): - text += '\n' - self.process.write(to_binary_string(text, 'utf8')) - self.process.waitForBytesWritten(-1) - - # Eventually write prompt faster (when hitting Enter continuously) - # -- necessary/working on Windows only: - if os.name == 'nt': - self.write_error() - - def keyboard_interrupt(self): - if self.introspection_socket is not None: - communicate(self.introspection_socket, "thread.interrupt_main()") - - def quit_monitor(self): - if self.introspection_socket is not None: - try: - write_packet(self.introspection_socket, "thread.exit()") - except socket.error: - pass - -#=============================================================================== -# Globals explorer -#=============================================================================== - def toggle_globals_explorer(self, state): - if self.stand_alone is not None: - self.splitter.setSizes([1, 1 if state else 0]) - self.namespacebrowser_button.setChecked(state) - if state and self.namespacebrowser is not None: - self.namespacebrowser.refresh_table() - - def splitter_moved(self, pos, index): - self.namespacebrowser_button.setChecked( self.splitter.sizes()[1] ) - -#=============================================================================== -# Misc. -#=============================================================================== - def set_current_working_directory(self): - """Set current working directory""" - cwd = self.shell.get_cwd() - self.emit(SIGNAL('redirect_stdio(bool)'), False) - directory = getexistingdirectory(self, _("Select directory"), cwd) - if directory: - self.shell.set_cwd(directory) - self.emit(SIGNAL('redirect_stdio(bool)'), True) - - def show_env(self): - """Show environment variables""" - get_func = self.shell.get_env - set_func = self.shell.set_env - self.dialog_manager.show(RemoteEnvDialog(get_func, set_func)) - - def show_syspath(self): - """Show sys.path contents""" - editor = DictEditor() - editor.setup(self.shell.get_syspath(), title="sys.path", readonly=True, - width=600, icon='syspath.png') - self.dialog_manager.show(editor) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/sitecustomize.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/sitecustomize.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/sitecustomize.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/sitecustomize.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,778 +0,0 @@ -# -# IMPORTANT NOTE: Don't add a coding line here! It's not necessary for -# site files -# -# Spyder's ExternalPythonShell sitecustomize -# - -import sys -import os -import os.path as osp -import pdb -import bdb - - -PY2 = sys.version[0] == '2' - - -#============================================================================== -# sys.argv can be missing when Python is embedded, taking care of it. -# Fixes Issue 1473 and other crazy crashes with IPython 0.13 trying to -# access it. -#============================================================================== -if not hasattr(sys, 'argv'): - sys.argv = [''] - - -#============================================================================== -# Important Note: -# -# We avoid importing spyderlib here, so we are handling Python 3 compatiblity -# by hand. -#============================================================================== -def _print(*objects, **options): - end = options.get('end', '\n') - file = options.get('file', sys.stdout) - sep = options.get('sep', ' ') - string = sep.join([str(obj) for obj in objects]) - if sys.version[0] == '3': - # Python 3 - local_dict = {} - exec('printf = print', local_dict) # to avoid syntax error in Python 2 - local_dict['printf'](string, file=file, end=end, sep=sep) - else: - # Python 2 - if end: - print >>file, string - else: - print >>file, string, - - -#============================================================================== -# Execfile functions -# -# The definitions for Python 2 on Windows were taken from the IPython -# project (present in IPython.utils.py3compat) -# Copyright (C) The IPython Development Team -# Distributed under the terms of the modified BSD license -#============================================================================== -try: - # Python 2 - import __builtin__ as builtins - if os.name == 'nt': - def encode(u): - return u.encode('utf8', 'replace') - def execfile(fname, glob=None, loc=None): - loc = loc if (loc is not None) else glob - scripttext = builtins.open(fname).read()+ '\n' - # compile converts unicode filename to str assuming - # ascii. Let's do the conversion before calling compile - if isinstance(fname, unicode): - filename = encode(fname) - else: - filename = fname - exec(compile(scripttext, filename, 'exec'), glob, loc) - else: - def execfile(fname, *where): - if isinstance(fname, unicode): - filename = fname.encode(sys.getfilesystemencoding()) - else: - filename = fname - builtins.execfile(filename, *where) -except ImportError: - # Python 3 - import builtins - basestring = (str,) - def execfile(filename, namespace): - # Open a source file correctly, whatever its encoding is - exec(compile(open(filename, 'rb').read(), filename, 'exec'), namespace) - - -#============================================================================== -# Colorization of sys.stderr (standard Python interpreter) -#============================================================================== -if os.environ.get("COLORIZE_SYS_STDERR", "").lower() == "true": - class StderrProxy(object): - """Proxy to sys.stderr file object overriding only the `write` method - to provide red colorization for the whole stream, and blue-underlined - for traceback file links""" - def __init__(self): - self.old_stderr = sys.stderr - self.__buffer = '' - sys.stderr = self - - def __getattr__(self, name): - return getattr(self.old_stderr, name) - - def write(self, text): - if os.name == 'nt' and '\n' not in text: - self.__buffer += text - return - for text in (self.__buffer+text).splitlines(True): - if text.startswith(' File') \ - and not text.startswith(' File "<'): - # Show error links in blue underlined text - colored_text = ' '+'\x1b[4;34m'+text[2:]+'\x1b[0m' - else: - # Show error messages in red - colored_text = '\x1b[31m'+text+'\x1b[0m' - self.old_stderr.write(colored_text) - self.__buffer = '' - - stderrproxy = StderrProxy() - - -#============================================================================== -# Prepending this spyderlib package's path to sys.path to be sure -# that another version of spyderlib won't be imported instead: -#============================================================================== -spyderlib_path = osp.dirname(__file__) -while not osp.isdir(osp.join(spyderlib_path, 'spyderlib')): - spyderlib_path = osp.abspath(osp.join(spyderlib_path, os.pardir)) -if not spyderlib_path.startswith(sys.prefix): - # Spyder is not installed: moving its parent directory to the top of - # sys.path to be sure that this spyderlib package will be imported in - # the remote process (instead of another installed version of Spyder) - while spyderlib_path in sys.path: - sys.path.remove(spyderlib_path) - sys.path.insert(0, spyderlib_path) -os.environ['SPYDER_PARENT_DIR'] = spyderlib_path - - -#============================================================================== -# Set PyQt4 API to #1 or #2 -#============================================================================== -pyqt_api = int(os.environ.get("PYQT_API", "0")) -if pyqt_api: - try: - import sip - try: - for qtype in ('QString', 'QVariant'): - sip.setapi(qtype, pyqt_api) - except AttributeError: - # Old version of sip - pass - except ImportError: - pass - - -#============================================================================== -# Setting console encoding (otherwise Python does not recognize encoding) -# for Windows platforms -#============================================================================== -if os.name == 'nt': - try: - import locale, ctypes - _t, _cp = locale.getdefaultlocale('LANG') - try: - _cp = int(_cp[2:]) - ctypes.windll.kernel32.SetConsoleCP(_cp) - ctypes.windll.kernel32.SetConsoleOutputCP(_cp) - except (ValueError, TypeError): - # Code page number in locale is not valid - pass - except ImportError: - pass - - -#============================================================================== -# Settings for our MacOs X app -#============================================================================== -if sys.platform == 'darwin': - from spyderlib.baseconfig import MAC_APP_NAME - if MAC_APP_NAME in __file__: - interpreter = os.environ.get('SPYDER_INTERPRETER') - if MAC_APP_NAME not in interpreter: - # Add a minimal library (with spyderlib) at the end of sys.path to - # be able to connect our monitor to the external console - py_ver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) - app_pythonpath = '%s/Contents/Resources/lib/python%s' % (MAC_APP_NAME, - py_ver) - full_pythonpath = [p for p in sys.path if p.endswith(app_pythonpath)] - if full_pythonpath: - sys.path.remove(full_pythonpath[0]) - sys.path.append(full_pythonpath[0] + osp.sep + 'minimal-lib') - else: - # Add missing variables and methods to the app's site module - import site - import osx_app_site - osx_app_site.setcopyright() - osx_app_site.sethelper() - site._Printer = osx_app_site._Printer - site.USER_BASE = osx_app_site.getuserbase() - site.USER_SITE = osx_app_site.getusersitepackages() - - -#============================================================================== -# Importing user's sitecustomize -#============================================================================== -try: - import sitecustomize #analysis:ignore -except ImportError: - pass - - -#============================================================================== -# Add default filesystem encoding on Linux to avoid an error with -# Matplotlib 1.5 in Python 2 (Fixes Issue 2793) -#============================================================================== -if PY2 and sys.platform.startswith('linux'): - def _getfilesystemencoding_wrapper(): - return 'utf-8' - - sys.getfilesystemencoding = _getfilesystemencoding_wrapper - - -#============================================================================== -# Importing matplotlib before creating the monitor. -# This prevents a kernel crash with the inline backend in our IPython -# consoles on Linux and Python 3 (Fixes Issue 2257) -#============================================================================== -try: - import matplotlib -except ImportError: - matplotlib = None # analysis:ignore - -#============================================================================== -# Communication between Spyder and the remote process -#============================================================================== -if os.environ.get('SPYDER_SHELL_ID') is None: - monitor = None -else: - from spyderlib.widgets.externalshell.monitor import Monitor - monitor = Monitor("127.0.0.1", - int(os.environ['SPYDER_I_PORT']), - int(os.environ['SPYDER_N_PORT']), - os.environ['SPYDER_SHELL_ID'], - float(os.environ['SPYDER_AR_TIMEOUT']), - os.environ["SPYDER_AR_STATE"].lower() == "true") - monitor.start() - - def open_in_spyder(source, lineno=1): - """ - Open a source file in Spyder's editor (it could be a filename or a - Python module/package). - - If you want to use IPython's %edit use %ed instead - """ - try: - source = sys.modules[source] - except KeyError: - source = source - if not isinstance(source, basestring): - try: - source = source.__file__ - except AttributeError: - raise ValueError("source argument must be either " - "a string or a module object") - if source.endswith('.pyc'): - source = source[:-1] - source = osp.abspath(source) - if osp.exists(source): - monitor.notify_open_file(source, lineno=lineno) - else: - _print("Can't open file %s" % source, file=sys.stderr) - builtins.open_in_spyder = open_in_spyder - - if os.environ["QT_API"] == 'pyqt': - from PyQt4 import QtCore - elif os.environ["QT_API"] == 'pyside': - from PySide import QtCore #analysis:ignore - - def qt_nt_inputhook(): - """Qt input hook for Windows - - This input hook wait for available stdin data (notified by - ExternalPythonShell through the monitor's inputhook_flag - attribute), and in the meantime it processes Qt events. - """ - # Refreshing variable explorer, except on first input hook call: - # (otherwise, on slow machines, this may freeze Spyder) - monitor.refresh_from_inputhook() - if os.name == 'nt': - try: - # This call fails for Python without readline support - # (or on Windows platforms) when PyOS_InputHook is called - # for the second consecutive time, because the 100-bytes - # stdin buffer is full. - # For more details, see the `PyOS_StdioReadline` function - # in Python source code (Parser/myreadline.c) - sys.stdin.tell() - except IOError: - return 0 - app = QtCore.QCoreApplication.instance() - if app and app.thread() is QtCore.QThread.currentThread(): - timer = QtCore.QTimer() - QtCore.QObject.connect(timer, QtCore.SIGNAL('timeout()'), - app, QtCore.SLOT('quit()')) - monitor.toggle_inputhook_flag(False) - while not monitor.inputhook_flag: - timer.start(50) - QtCore.QCoreApplication.exec_() - timer.stop() -# # Socket-based alternative: -# socket = QtNetwork.QLocalSocket() -# socket.connectToServer(os.environ['SPYDER_SHELL_ID']) -# socket.waitForConnected(-1) -# while not socket.waitForReadyRead(10): -# timer.start(50) -# QtCore.QCoreApplication.exec_() -# timer.stop() -# socket.read(3) -# socket.disconnectFromServer() - return 0 - - -#============================================================================== -# Matplotlib settings -#============================================================================== -if matplotlib is not None: - mpl_backend = os.environ.get("MATPLOTLIB_BACKEND", "") - mpl_ion = os.environ.get("MATPLOTLIB_ION", "") - if not mpl_backend: - mpl_backend = 'Qt4Agg' - - # To have mpl docstrings as rst - matplotlib.rcParams['docstring.hardcopy'] = True - - # Activate interactive mode when needed - if mpl_ion.lower() == "true": - matplotlib.rcParams['interactive'] = True - - if os.environ.get("IPYTHON_KERNEL", "").lower() != "true": - import ctypes - from spyderlib.widgets.externalshell import inputhooks - - # Setting the user defined backend - matplotlib.use(mpl_backend) - - # Setting the right input hook according to mpl_backend, - # IMPORTANT NOTE: Don't try to abstract the steps to set a PyOS - # input hook callback in a function. It will *crash* the - # interpreter!! - if mpl_backend == "Qt4Agg" and os.name == 'nt' and \ - monitor is not None: - # Removing PyQt4 input hook which is not working well on - # Windows since opening a subprocess does not attach a real - # console to it (with keyboard events...) - if os.environ["QT_API"] == 'pyqt': - inputhooks.remove_pyqt_inputhook() - # Using our own input hook - # NOTE: it's not working correctly for some configurations - # (See issue 1831) - callback = inputhooks.set_pyft_callback(qt_nt_inputhook) - pyos_ih = inputhooks.get_pyos_inputhook() - pyos_ih.value = ctypes.cast(callback, ctypes.c_void_p).value - elif mpl_backend == "Qt4Agg" and os.environ["QT_API"] == 'pyside': - # PySide doesn't have an input hook, so we need to install one - # to be able to show plots. - # Note: This only works well for Posix systems - callback = inputhooks.set_pyft_callback(inputhooks.qt4) - pyos_ih = inputhooks.get_pyos_inputhook() - pyos_ih.value = ctypes.cast(callback, ctypes.c_void_p).value - elif mpl_backend != "Qt4Agg" and os.environ["QT_API"] == 'pyqt': - # Matplotlib backends install their own input hooks, so we - # need to remove the PyQt one to make them work - inputhooks.remove_pyqt_inputhook() - - -#============================================================================== -# IPython adjustments -#============================================================================== -if os.environ.get("IPYTHON_KERNEL", "").lower() == "true": - - # Use ipydb as the debugger to patch on IPython consoles - from IPython.core.debugger import Pdb as ipyPdb - pdb.Pdb = ipyPdb - - # Patch unittest.main so that errors are printed directly in the console. - # See http://comments.gmane.org/gmane.comp.python.ipython.devel/10557 - # Fixes Issue 1370 - import unittest - from unittest import TestProgram - class IPyTesProgram(TestProgram): - def __init__(self, *args, **kwargs): - test_runner = unittest.TextTestRunner(stream=sys.stderr) - kwargs['testRunner'] = kwargs.pop('testRunner', test_runner) - kwargs['exit'] = False - TestProgram.__init__(self, *args, **kwargs) - unittest.main = IPyTesProgram - - # Pandas monkey-patches - try: - # Make Pandas recognize our IPython consoles as proper qtconsoles - # Fixes Issue 2015 - def in_qtconsole(): - return True - import pandas as pd - pd.core.common.in_qtconsole = in_qtconsole - - # Set Pandas output encoding - pd.options.display.encoding = 'utf-8' - except (ImportError, AttributeError): - pass - - -#============================================================================== -# Pdb adjustments -#============================================================================== -class SpyderPdb(pdb.Pdb): - def set_spyder_breakpoints(self): - self.clear_all_breaks() - #------Really deleting all breakpoints: - for bp in bdb.Breakpoint.bpbynumber: - if bp: - bp.deleteMe() - bdb.Breakpoint.next = 1 - bdb.Breakpoint.bplist = {} - bdb.Breakpoint.bpbynumber = [None] - #------ - from spyderlib.config import CONF - CONF.load_from_ini() - if CONF.get('run', 'breakpoints/enabled', True): - breakpoints = CONF.get('run', 'breakpoints', {}) - i = 0 - for fname, data in list(breakpoints.items()): - for linenumber, condition in data: - i += 1 - self.set_break(self.canonic(fname), linenumber, - cond=condition) - - def notify_spyder(self, frame): - if not frame: - return - fname = self.canonic(frame.f_code.co_filename) - if sys.version[0] == '2': - try: - fname = unicode(fname, "utf-8") - except TypeError: - pass - lineno = frame.f_lineno - if isinstance(fname, basestring) and isinstance(lineno, int): - if osp.isfile(fname) and monitor is not None: - monitor.notify_pdb_step(fname, lineno) - -pdb.Pdb = SpyderPdb - -#XXX: I know, this function is now also implemented as is in utils/misc.py but -# I'm kind of reluctant to import spyderlib in sitecustomize, even if this -# import is very clean. -def monkeypatch_method(cls, patch_name): - # This function's code was inspired from the following thread: - # "[Python-Dev] Monkeypatching idioms -- elegant or ugly?" - # by Robert Brewer - # (Tue Jan 15 19:13:25 CET 2008) - """ - Add the decorated method to the given class; replace as needed. - - If the named method already exists on the given class, it will - be replaced, and a reference to the old method is created as - cls._old. If the "_old__" attribute - already exists, KeyError is raised. - """ - def decorator(func): - fname = func.__name__ - old_func = getattr(cls, fname, None) - if old_func is not None: - # Add the old func to a list of old funcs. - old_ref = "_old_%s_%s" % (patch_name, fname) - #print(old_ref, old_func) - old_attr = getattr(cls, old_ref, None) - if old_attr is None: - setattr(cls, old_ref, old_func) - else: - raise KeyError("%s.%s already exists." - % (cls.__name__, old_ref)) - setattr(cls, fname, func) - return func - return decorator - -@monkeypatch_method(pdb.Pdb, 'Pdb') -def user_return(self, frame, return_value): - """This function is called when a return trap is set here.""" - # This is useful when debugging in an active interpreter (otherwise, - # the debugger will stop before reaching the target file) - if self._wait_for_mainpyfile: - if (self.mainpyfile != self.canonic(frame.f_code.co_filename) - or frame.f_lineno<= 0): - return - self._wait_for_mainpyfile = 0 - self._old_Pdb_user_return(frame, return_value) - -@monkeypatch_method(pdb.Pdb, 'Pdb') -def interaction(self, frame, traceback): - self.setup(frame, traceback) - self.notify_spyder(frame) #-----Spyder-specific------------------------- - self.print_stack_entry(self.stack[self.curindex]) - self.cmdloop() - self.forget() - -@monkeypatch_method(pdb.Pdb, 'Pdb') -def reset(self): - self._old_Pdb_reset() - if monitor is not None: - monitor.register_pdb_session(self) - self.set_spyder_breakpoints() - -#XXX: notify spyder on any pdb command (is that good or too lazy? i.e. is more -# specific behaviour desired?) -@monkeypatch_method(pdb.Pdb, 'Pdb') -def postcmd(self, stop, line): - self.notify_spyder(self.curframe) - return self._old_Pdb_postcmd(stop, line) - -# Breakpoints don't work for files with non-ascii chars in Python 2 -# Fixes Issue 1484 -if sys.version[0] == '2': - @monkeypatch_method(pdb.Pdb, 'Pdb') - def break_here(self, frame): - from bdb import effective - filename = self.canonic(frame.f_code.co_filename) - try: - filename = unicode(filename, "utf-8") - except TypeError: - pass - if not filename in self.breaks: - return False - lineno = frame.f_lineno - if not lineno in self.breaks[filename]: - # The line itself has no breakpoint, but maybe the line is the - # first line of a function with breakpoint set by function name. - lineno = frame.f_code.co_firstlineno - if not lineno in self.breaks[filename]: - return False - - # flag says ok to delete temp. bp - (bp, flag) = effective(filename, lineno, frame) - if bp: - self.currentbp = bp.number - if (flag and bp.temporary): - self.do_clear(str(bp.number)) - return True - else: - return False - - -#============================================================================== -# # Restoring (almost) original sys.path: -# (Note: do not remove spyderlib_path from sys.path because if Spyder has been -# installed using python setup.py install, then this could remove the -# 'site-packages' directory from sys.path!) -#============================================================================== -try: - sys.path.remove(osp.join(spyderlib_path, - "spyderlib", "widgets", "externalshell")) -except ValueError: - pass - - -#============================================================================== -# Ignore PyQt4's sip API changes (this should be used wisely -e.g. for -# debugging- as dynamic API change is not supported by PyQt) -#============================================================================== -if os.environ.get("IGNORE_SIP_SETAPI_ERRORS", "").lower() == "true": - try: - import sip - from sip import setapi as original_setapi - def patched_setapi(name, no): - try: - original_setapi(name, no) - except ValueError as msg: - _print("Warning/PyQt4-Spyder (%s)" % str(msg), file=sys.stderr) - sip.setapi = patched_setapi - except ImportError: - pass - - -#============================================================================== -# User module reloader -#============================================================================== -class UserModuleReloader(object): - """ - User Module Reloader (UMR) aims at deleting user modules - to force Python to deeply reload them during import - - pathlist [list]: blacklist in terms of module path - namelist [list]: blacklist in terms of module name - """ - def __init__(self, namelist=None, pathlist=None): - if namelist is None: - namelist = [] - spy_modules = ['sitecustomize', 'spyderlib', 'spyderplugins'] - mpl_modules = ['matplotlib', 'tkinter', 'Tkinter', 'gtk'] - self.namelist = namelist + spy_modules + mpl_modules - - if pathlist is None: - pathlist = [] - self.pathlist = pathlist - self.previous_modules = list(sys.modules.keys()) - - def is_module_blacklisted(self, modname, modpath): - for path in [sys.prefix]+self.pathlist: - if modpath.startswith(path): - return True - else: - return set(modname.split('.')) & set(self.namelist) - - def run(self, verbose=False): - """ - Del user modules to force Python to deeply reload them - - Do not del modules which are considered as system modules, i.e. - modules installed in subdirectories of Python interpreter's binary - Do not del C modules - """ - log = [] - for modname, module in list(sys.modules.items()): - if modname not in self.previous_modules: - modpath = getattr(module, '__file__', None) - if modpath is None: - # *module* is a C module that is statically linked into the - # interpreter. There is no way to know its path, so we - # choose to ignore it. - continue - if not self.is_module_blacklisted(modname, modpath): - log.append(modname) - del sys.modules[modname] - if verbose and log: - _print("\x1b[4;33m%s\x1b[24m%s\x1b[0m"\ - % ("Reloaded modules", ": "+", ".join(log))) - -__umr__ = None - - -#============================================================================== -# runfile and debugfile commands -#============================================================================== -def _get_globals(): - """Return current Python interpreter globals namespace""" - from __main__ import __dict__ as namespace - shell = namespace.get('__ipythonshell__') - if shell is not None and hasattr(shell, 'user_ns'): - # IPython 0.13+ kernel - return shell.user_ns - else: - # Python interpreter - return namespace - return namespace - - -def runfile(filename, args=None, wdir=None, namespace=None): - """ - Run filename - args: command line arguments (string) - wdir: working directory - """ - try: - filename = filename.decode('utf-8') - except (UnicodeError, TypeError, AttributeError): - # UnicodeError, TypeError --> eventually raised in Python 2 - # AttributeError --> systematically raised in Python 3 - pass - global __umr__ - if os.environ.get("UMR_ENABLED", "").lower() == "true": - if __umr__ is None: - namelist = os.environ.get("UMR_NAMELIST", None) - if namelist is not None: - namelist = namelist.split(',') - __umr__ = UserModuleReloader(namelist=namelist) - else: - verbose = os.environ.get("UMR_VERBOSE", "").lower() == "true" - __umr__.run(verbose=verbose) - if args is not None and not isinstance(args, basestring): - raise TypeError("expected a character buffer object") - if namespace is None: - namespace = _get_globals() - namespace['__file__'] = filename - sys.argv = [filename] - if args is not None: - for arg in args.split(): - sys.argv.append(arg) - if wdir is not None: - try: - wdir = wdir.decode('utf-8') - except (UnicodeError, TypeError, AttributeError): - # UnicodeError, TypeError --> eventually raised in Python 2 - # AttributeError --> systematically raised in Python 3 - pass - os.chdir(wdir) - execfile(filename, namespace) - sys.argv = [''] - namespace.pop('__file__') - -builtins.runfile = runfile - - -def debugfile(filename, args=None, wdir=None): - """ - Debug filename - args: command line arguments (string) - wdir: working directory - """ - debugger = pdb.Pdb() - filename = debugger.canonic(filename) - debugger._wait_for_mainpyfile = 1 - debugger.mainpyfile = filename - debugger._user_requested_quit = 0 - if os.name == 'nt': - filename = filename.replace('\\', '/') - debugger.run("runfile(%r, args=%r, wdir=%r)" % (filename, args, wdir)) - -builtins.debugfile = debugfile - - -#============================================================================== -# Evaluate external commands -#============================================================================== -def evalsc(command): - """Evaluate special commands - (analog to IPython's magic commands but far less powerful/complete)""" - assert command.startswith('%') - - from subprocess import Popen, PIPE - namespace = _get_globals() - command = command[1:].strip() # Remove leading % - - import re - clear_match = re.match(r"^clear ([a-zA-Z0-9_, ]+)", command) - cd_match = re.match(r"^cd \"?\'?([a-zA-Z0-9_\ \:\\\/\.]+)", command) - - if cd_match: - os.chdir(eval('r"%s"' % cd_match.groups()[0].strip())) - elif clear_match: - varnames = clear_match.groups()[0].replace(' ', '').split(',') - for varname in varnames: - try: - namespace.pop(varname) - except KeyError: - pass - elif command in ('cd', 'pwd'): - try: - _print(os.getcwdu()) - except AttributeError: - _print(os.getcwd()) - elif command == 'ls': - if os.name == 'nt': - Popen('dir', shell=True, stdin=PIPE) - _print('\n') - else: - Popen('ls', shell=True, stdin=PIPE) - _print('\n') - elif command == 'scientific': - from spyderlib import baseconfig - execfile(baseconfig.SCIENTIFIC_STARTUP, namespace) - else: - raise NotImplementedError("Unsupported command: '%s'" % command) - -builtins.evalsc = evalsc - - -#============================================================================== -# Restoring original PYTHONPATH -#============================================================================== -try: - os.environ['PYTHONPATH'] = os.environ['OLD_PYTHONPATH'] - del os.environ['OLD_PYTHONPATH'] -except KeyError: - if os.environ.get('PYTHONPATH') is not None: - del os.environ['PYTHONPATH'] diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/start_ipython_kernel.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/start_ipython_kernel.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/start_ipython_kernel.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/start_ipython_kernel.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,205 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Startup file used by ExternalPythonShell exclusively for IPython kernels -(see spyderlib/widgets/externalshell/pythonshell.py)""" - -import sys -import os.path as osp - -# TODO: Move to Jupyter imports in 3.1 -try: - import warnings - from IPython.utils.shimmodule import ShimWarning - warnings.simplefilter('ignore', ShimWarning) -except: - pass - - -def sympy_config(mpl_backend): - """Sympy configuration""" - if mpl_backend is not None: - lines = """ -from sympy.interactive import init_session -init_session() -%matplotlib {0} -""".format(mpl_backend) - else: - lines = """ -from sympy.interactive import init_session -init_session() -""" - - return lines - - -def kernel_config(): - """Create a config object with IPython kernel options""" - from IPython.config.loader import Config, load_pyconfig_files - from IPython.core.application import get_ipython_dir - from spyderlib.config import CONF - from spyderlib.utils.programs import is_module_installed - - # ---- IPython config ---- - try: - profile_path = osp.join(get_ipython_dir(), 'profile_default') - ip_cfg = load_pyconfig_files(['ipython_config.py', - 'ipython_qtconsole_config.py'], - profile_path) - except: - ip_cfg = Config() - - # ---- Spyder config ---- - spy_cfg = Config() - - # Until we implement Issue 1052 - spy_cfg.InteractiveShell.xmode = 'Plain' - - # Run lines of code at startup - run_lines_o = CONF.get('ipython_console', 'startup/run_lines') - if run_lines_o: - spy_cfg.IPKernelApp.exec_lines = [x.strip() for x in run_lines_o.split(',')] - else: - spy_cfg.IPKernelApp.exec_lines = [] - - # Pylab configuration - mpl_backend = None - mpl_installed = is_module_installed('matplotlib') - pylab_o = CONF.get('ipython_console', 'pylab') - - if mpl_installed and pylab_o: - # Get matplotlib backend - backend_o = CONF.get('ipython_console', 'pylab/backend', 0) - backends = {0: 'inline', 1: 'auto', 2: 'qt', 3: 'osx', 4: 'gtk', - 5: 'wx', 6: 'tk'} - mpl_backend = backends[backend_o] - - # Automatically load Pylab and Numpy, or only set Matplotlib - # backend - autoload_pylab_o = CONF.get('ipython_console', 'pylab/autoload') - if autoload_pylab_o: - spy_cfg.IPKernelApp.exec_lines.append( - "%pylab {0}".format(mpl_backend)) - else: - spy_cfg.IPKernelApp.exec_lines.append( - "%matplotlib {0}".format(mpl_backend)) - - # Inline backend configuration - if backends[backend_o] == 'inline': - # Figure format - format_o = CONF.get('ipython_console', - 'pylab/inline/figure_format', 0) - formats = {0: 'png', 1: 'svg'} - spy_cfg.InlineBackend.figure_format = formats[format_o] - - # Resolution - spy_cfg.InlineBackend.rc = {'figure.figsize': (6.0, 4.0), - 'savefig.dpi': 72, - 'font.size': 10, - 'figure.subplot.bottom': .125, - 'figure.facecolor': 'white', - 'figure.edgecolor': 'white' - } - resolution_o = CONF.get('ipython_console', - 'pylab/inline/resolution') - spy_cfg.InlineBackend.rc['savefig.dpi'] = resolution_o - - # Figure size - width_o = float(CONF.get('ipython_console', 'pylab/inline/width')) - height_o = float(CONF.get('ipython_console', 'pylab/inline/height')) - spy_cfg.InlineBackend.rc['figure.figsize'] = (width_o, height_o) - - # Run a file at startup - use_file_o = CONF.get('ipython_console', 'startup/use_run_file') - run_file_o = CONF.get('ipython_console', 'startup/run_file') - if use_file_o and run_file_o: - spy_cfg.IPKernelApp.file_to_run = run_file_o - - # Autocall - autocall_o = CONF.get('ipython_console', 'autocall') - spy_cfg.ZMQInteractiveShell.autocall = autocall_o - - # To handle the banner by ourselves in IPython 3+ - spy_cfg.ZMQInteractiveShell.banner1 = '' - - # Greedy completer - greedy_o = CONF.get('ipython_console', 'greedy_completer') - spy_cfg.IPCompleter.greedy = greedy_o - - # Sympy loading - sympy_o = CONF.get('ipython_console', 'symbolic_math') - if sympy_o: - lines = sympy_config(mpl_backend) - spy_cfg.IPKernelApp.exec_lines.append(lines) - - # Merge IPython and Spyder configs. Spyder prefs will have prevalence - # over IPython ones - ip_cfg._merge(spy_cfg) - return ip_cfg - - -def change_edit_magic(shell): - """Use %edit to open files in Spyder""" - try: - shell.magics_manager.magics['line']['ed'] = \ - shell.magics_manager.magics['line']['edit'] - shell.magics_manager.magics['line']['edit'] = open_in_spyder #analysis:ignore - except: - pass - -def varexp(line): - """ - Spyder's variable explorer magic - - Used to generate plots, histograms and images of the variables displayed - on it. - """ - ip = get_ipython() #analysis:ignore - funcname, name = line.split() - import spyderlib.pyplot - __fig__ = spyderlib.pyplot.figure(); - __items__ = getattr(spyderlib.pyplot, funcname[2:])(ip.user_ns[name]) - spyderlib.pyplot.show() - del __fig__, __items__ - -# Remove this module's path from sys.path: -try: - sys.path.remove(osp.dirname(__file__)) -except ValueError: - pass - -locals().pop('__file__') -__doc__ = '' -__name__ = '__main__' - -# Add current directory to sys.path (like for any standard Python interpreter -# executed in interactive mode): -sys.path.insert(0, '') - -# Fire up the kernel instance. -from IPython.kernel.zmq.kernelapp import IPKernelApp -ipk_temp = IPKernelApp.instance() -ipk_temp.config = kernel_config() -ipk_temp.initialize() - -# Grabbing the kernel's shell to share its namespace with our -# Variable Explorer -__ipythonshell__ = ipk_temp.shell - -# Issue 977 : Since kernel.initialize() has completed execution, -# we can now allow the monitor to communicate the availablility of -# the kernel to accept front end connections. -__ipythonkernel__ = ipk_temp -del ipk_temp - -# Change %edit to open files inside Spyder -# NOTE: Leave this and other magic modifications *after* setting -# __ipythonkernel__ to not have problems while starting kernels -change_edit_magic(__ipythonshell__) -__ipythonshell__.register_magic_function(varexp) - -# Start the (infinite) kernel event loop. -__ipythonkernel__.start() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/systemshell.py spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/systemshell.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/externalshell/systemshell.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/externalshell/systemshell.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,146 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""External System Shell widget: execute terminal in a separate process""" - -import os - -from spyderlib.qt.QtGui import QMessageBox -from spyderlib.qt.QtCore import QProcess, SIGNAL, QTextCodec -LOCALE_CODEC = QTextCodec.codecForLocale() -CP850_CODEC = QTextCodec.codecForName('cp850') - -# Local imports -from spyderlib.utils.programs import shell_split -from spyderlib.baseconfig import _ -from spyderlib.utils.qthelpers import get_icon -from spyderlib.widgets.externalshell.baseshell import (ExternalShellBase, - add_pathlist_to_PYTHONPATH) -from spyderlib.widgets.shell import TerminalWidget -from spyderlib.py3compat import to_text_string, is_text_string - - -class ExternalSystemShell(ExternalShellBase): - """External Shell widget: execute Python script in a separate process""" - SHELL_CLASS = TerminalWidget - def __init__(self, parent=None, wdir=None, path=[], light_background=True, - menu_actions=None, show_buttons_inside=True, - show_elapsed_time=True): - ExternalShellBase.__init__(self, parent=parent, fname=None, wdir=wdir, - history_filename='.history', - light_background=light_background, - menu_actions=menu_actions, - show_buttons_inside=show_buttons_inside, - show_elapsed_time=show_elapsed_time) - - # Additional python path list - self.path = path - - # For compatibility with the other shells that can live in the external - # console - self.is_ipykernel = False - self.connection_file = None - - def get_icon(self): - return get_icon('cmdprompt.png') - - def create_process(self): - self.shell.clear() - - self.process = QProcess(self) - self.process.setProcessChannelMode(QProcess.MergedChannels) - - # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe) - env = [to_text_string(_path) - for _path in self.process.systemEnvironment()] - add_pathlist_to_PYTHONPATH(env, self.path) - self.process.setEnvironment(env) - - # Working directory - if self.wdir is not None: - self.process.setWorkingDirectory(self.wdir) - - # Shell arguments - if os.name == 'nt': - p_args = ['/Q'] - else: - p_args = ['-i'] - - if self.arguments: - p_args.extend( shell_split(self.arguments) ) - - self.connect(self.process, SIGNAL("readyReadStandardOutput()"), - self.write_output) - self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), - self.finished) - - self.connect(self.kill_button, SIGNAL("clicked()"), - self.process.kill) - - if os.name == 'nt': - self.process.start('cmd.exe', p_args) - else: - # Using bash: - self.process.start('bash', p_args) - self.send_to_process('PS1="\\u@\\h:\\w> "\n') - - running = self.process.waitForStarted() - self.set_running_state(running) - if not running: - QMessageBox.critical(self, _("Error"), - _("Process failed to start")) - else: - self.shell.setFocus() - self.emit(SIGNAL('started()')) - - return self.process - -#=============================================================================== -# Input/Output -#=============================================================================== - def transcode(self, qba): - if os.name == 'nt': - return to_text_string( CP850_CODEC.toUnicode(qba.data()) ) - else: - return ExternalShellBase.transcode(self, qba) - - def send_to_process(self, text): - if not is_text_string(text): - text = to_text_string(text) - if text[:-1] in ["clear", "cls", "CLS"]: - self.shell.clear() - self.send_to_process(os.linesep) - return - if not text.endswith('\n'): - text += '\n' - if os.name == 'nt': - self.process.write(text.encode('cp850')) - else: - self.process.write(LOCALE_CODEC.fromUnicode(text)) - self.process.waitForBytesWritten(-1) - - def keyboard_interrupt(self): - # This does not work on Windows: - # (unfortunately there is no easy way to send a Ctrl+C to cmd.exe) - self.send_ctrl_to_process('c') - -# # The following code will soon be removed: -# # (last attempt to send a Ctrl+C on Windows) -# if os.name == 'nt': -# pid = int(self.process.pid()) -# import ctypes, win32api, win32con -# class _PROCESS_INFORMATION(ctypes.Structure): -# _fields_ = [("hProcess", ctypes.c_int), -# ("hThread", ctypes.c_int), -# ("dwProcessID", ctypes.c_int), -# ("dwThreadID", ctypes.c_int)] -# x = ctypes.cast( ctypes.c_void_p(pid), -# ctypes.POINTER(_PROCESS_INFORMATION) ) -# win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, -# x.dwProcessID) -# else: -# self.send_ctrl_to_process('c') - \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/findinfiles.py spyder-3.0.2+dfsg1/spyderlib/widgets/findinfiles.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/findinfiles.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/findinfiles.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,802 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Find in files widget""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from __future__ import with_statement - -from spyderlib.qt.QtGui import (QHBoxLayout, QWidget, QTreeWidgetItem, - QSizePolicy, QRadioButton, QVBoxLayout, QLabel) -from spyderlib.qt.QtCore import SIGNAL, Qt, QThread, QMutexLocker, QMutex -from spyderlib.qt.compat import getexistingdirectory - -import sys -import os -import re -import fnmatch -import os.path as osp -from subprocess import Popen, PIPE -import traceback - -# Local imports -from spyderlib.utils.vcs import is_hg_installed, get_vcs_root -from spyderlib.utils.misc import abspardir, get_common_path -from spyderlib.utils.qthelpers import (get_icon, get_std_icon, - create_toolbutton, get_filetype_icon) -from spyderlib.baseconfig import _ -from spyderlib.widgets.comboboxes import PathComboBox, PatternComboBox -from spyderlib.widgets.onecolumntree import OneColumnTree -from spyderlib.py3compat import to_text_string, getcwd - - -#def find_files_in_hg_manifest(rootpath, include, exclude): -# p = Popen("hg manifest", stdout=PIPE) -# found = [] -# hgroot = get_vcs_root(rootpath) -# for path in p.stdout.read().splitlines(): -# dirname = osp.join('.', osp.dirname(path)) -# if re.search(exclude, dirname+os.sep): -# continue -# filename = osp.join('.', osp.dirname(path)) -# if re.search(exclude, filename): -# continue -# if re.search(include, filename): -# found.append(osp.join(hgroot, path)) -# return found -# -#def find_files_in_path(rootpath, include, exclude): -# found = [] -# for path, dirs, files in os.walk(rootpath): -# for d in dirs[:]: -# dirname = os.path.join(path, d) -# if re.search(exclude, dirname+os.sep): -# dirs.remove(d) -# for f in files: -# filename = os.path.join(path, f) -# if re.search(exclude, filename): -# continue -# if re.search(include, filename): -# found.append(filename) -# return found - - -#def find_string_in_files(texts, filenames, regexp=False): -# results = {} -# nb = 0 -# for fname in filenames: -# for lineno, line in enumerate(file(fname)): -# for text, enc in texts: -# if regexp: -# found = re.search(text, line) -# if found is not None: -# break -# else: -# found = line.find(text) -# if found > -1: -# break -# try: -# line_dec = line.decode(enc) -# except UnicodeDecodeError: -# line_dec = line -# if regexp: -# for match in re.finditer(text, line): -# res = results.get(osp.abspath(fname), []) -# res.append((lineno+1, match.start(), line_dec)) -# results[osp.abspath(fname)] = res -# nb += 1 -# else: -# while found > -1: -# res = results.get(osp.abspath(fname), []) -# res.append((lineno+1, found, line_dec)) -# results[osp.abspath(fname)] = res -# for text in texts: -# found = line.find(text, found+1) -# if found>-1: -# break -# nb += 1 -# return results, nb - -class SearchThread(QThread): - """Find in files search thread""" - def __init__(self, parent): - QThread.__init__(self, parent) - self.mutex = QMutex() - self.stopped = None - self.results = None - self.pathlist = None - self.nb = None - self.error_flag = None - self.rootpath = None - self.python_path = None - self.hg_manifest = None - self.include = None - self.exclude = None - self.texts = None - self.text_re = None - self.completed = None - self.get_pythonpath_callback = None - - def initialize(self, path, python_path, hg_manifest, - include, exclude, texts, text_re): - self.rootpath = path - self.python_path = python_path - self.hg_manifest = hg_manifest - self.include = include - self.exclude = exclude - self.texts = texts - self.text_re = text_re - self.stopped = False - self.completed = False - - def run(self): - try: - self.filenames = [] - if self.hg_manifest: - ok = self.find_files_in_hg_manifest() - elif self.python_path: - ok = self.find_files_in_python_path() - else: - ok = self.find_files_in_path(self.rootpath) - if ok: - self.find_string_in_files() - except Exception: - # Important note: we have to handle unexpected exceptions by - # ourselves because they won't be catched by the main thread - # (known QThread limitation/bug) - traceback.print_exc() - self.error_flag = _("Unexpected error: see internal console") - self.stop() - self.emit(SIGNAL("finished(bool)"), self.completed) - - def stop(self): - with QMutexLocker(self.mutex): - self.stopped = True - - def find_files_in_python_path(self): - pathlist = os.environ.get('PYTHONPATH', '').split(os.pathsep) - if self.get_pythonpath_callback is not None: - pathlist += self.get_pythonpath_callback() - if os.name == "nt": - # The following avoid doublons on Windows platforms: - # (e.g. "d:\Python" in PYTHONPATH environment variable, - # and "D:\Python" in Spyder's python path would lead - # to two different search folders) - winpathlist = [] - lcpathlist = [] - for path in pathlist: - lcpath = osp.normcase(path) - if lcpath not in lcpathlist: - lcpathlist.append(lcpath) - winpathlist.append(path) - pathlist = winpathlist - ok = True - for path in set(pathlist): - if osp.isdir(path): - ok = self.find_files_in_path(path) - if not ok: - break - return ok - - def find_files_in_hg_manifest(self): - p = Popen(['hg', 'manifest'], stdout=PIPE, - cwd=self.rootpath, shell=True) - hgroot = get_vcs_root(self.rootpath) - self.pathlist = [hgroot] - for path in p.stdout.read().decode().splitlines(): - with QMutexLocker(self.mutex): - if self.stopped: - return False - dirname = osp.dirname(path) - try: - if re.search(self.exclude, dirname+os.sep): - continue - filename = osp.basename(path) - if re.search(self.exclude, filename): - continue - if re.search(self.include, filename): - self.filenames.append(osp.join(hgroot, path)) - except re.error: - self.error_flag = _("invalid regular expression") - return False - return True - - def find_files_in_path(self, path): - if self.pathlist is None: - self.pathlist = [] - self.pathlist.append(path) - for path, dirs, files in os.walk(path): - with QMutexLocker(self.mutex): - if self.stopped: - return False - try: - for d in dirs[:]: - dirname = os.path.join(path, d) - if re.search(self.exclude, dirname+os.sep): - dirs.remove(d) - for f in files: - filename = os.path.join(path, f) - if re.search(self.exclude, filename): - continue - if re.search(self.include, filename): - self.filenames.append(filename) - except re.error: - self.error_flag = _("invalid regular expression") - return False - return True - - def find_string_in_files(self): - self.results = {} - self.nb = 0 - self.error_flag = False - for fname in self.filenames: - with QMutexLocker(self.mutex): - if self.stopped: - return - try: - for lineno, line in enumerate(open(fname, 'rb')): - for text, enc in self.texts: - if self.text_re: - found = re.search(text, line) - if found is not None: - break - else: - found = line.find(text) - if found > -1: - break - try: - line_dec = line.decode(enc) - except UnicodeDecodeError: - line_dec = line - if self.text_re: - for match in re.finditer(text, line): - res = self.results.get(osp.abspath(fname), []) - res.append((lineno+1, match.start(), line_dec)) - self.results[osp.abspath(fname)] = res - self.nb += 1 - else: - while found > -1: - res = self.results.get(osp.abspath(fname), []) - res.append((lineno+1, found, line_dec)) - self.results[osp.abspath(fname)] = res - for text, enc in self.texts: - found = line.find(text, found+1) - if found > -1: - break - self.nb += 1 - except IOError as xxx_todo_changeme: - (_errno, _strerror) = xxx_todo_changeme.args - self.error_flag = _("permission denied errors were encountered") - except re.error: - self.error_flag = _("invalid regular expression") - self.completed = True - - def get_results(self): - return self.results, self.pathlist, self.nb, self.error_flag - - -class FindOptions(QWidget): - """Find widget with options""" - def __init__(self, parent, search_text, search_text_regexp, search_path, - include, include_idx, include_regexp, - exclude, exclude_idx, exclude_regexp, - supported_encodings, in_python_path, more_options): - QWidget.__init__(self, parent) - - if search_path is None: - search_path = getcwd() - - if not isinstance(search_text, (list, tuple)): - search_text = [search_text] - if not isinstance(search_path, (list, tuple)): - search_path = [search_path] - if not isinstance(include, (list, tuple)): - include = [include] - if not isinstance(exclude, (list, tuple)): - exclude = [exclude] - - self.supported_encodings = supported_encodings - - # Layout 1 - hlayout1 = QHBoxLayout() - self.search_text = PatternComboBox(self, search_text, - _("Search pattern")) - self.edit_regexp = create_toolbutton(self, - icon=get_icon("advanced.png"), - tip=_("Regular expression")) - self.edit_regexp.setCheckable(True) - self.edit_regexp.setChecked(search_text_regexp) - self.more_widgets = () - self.more_options = create_toolbutton(self, - toggled=self.toggle_more_options) - self.more_options.setCheckable(True) - self.more_options.setChecked(more_options) - - self.ok_button = create_toolbutton(self, text=_("Search"), - icon=get_std_icon("DialogApplyButton"), - triggered=lambda: self.emit(SIGNAL('find()')), - tip=_("Start search"), - text_beside_icon=True) - self.connect(self.ok_button, SIGNAL('clicked()'), self.update_combos) - self.stop_button = create_toolbutton(self, text=_("Stop"), - icon=get_icon("stop.png"), - triggered=lambda: self.emit(SIGNAL('stop()')), - tip=_("Stop search"), - text_beside_icon=True) - self.stop_button.setEnabled(False) - for widget in [self.search_text, self.edit_regexp, - self.ok_button, self.stop_button, self.more_options]: - hlayout1.addWidget(widget) - - # Layout 2 - hlayout2 = QHBoxLayout() - self.include_pattern = PatternComboBox(self, include, - _("Included filenames pattern")) - if include_idx is not None and include_idx >= 0 \ - and include_idx < self.include_pattern.count(): - self.include_pattern.setCurrentIndex(include_idx) - self.include_regexp = create_toolbutton(self, - icon=get_icon("advanced.png"), - tip=_("Regular expression")) - self.include_regexp.setCheckable(True) - self.include_regexp.setChecked(include_regexp) - include_label = QLabel(_("Include:")) - include_label.setBuddy(self.include_pattern) - self.exclude_pattern = PatternComboBox(self, exclude, - _("Excluded filenames pattern")) - if exclude_idx is not None and exclude_idx >= 0 \ - and exclude_idx < self.exclude_pattern.count(): - self.exclude_pattern.setCurrentIndex(exclude_idx) - self.exclude_regexp = create_toolbutton(self, - icon=get_icon("advanced.png"), - tip=_("Regular expression")) - self.exclude_regexp.setCheckable(True) - self.exclude_regexp.setChecked(exclude_regexp) - exclude_label = QLabel(_("Exclude:")) - exclude_label.setBuddy(self.exclude_pattern) - for widget in [include_label, self.include_pattern, - self.include_regexp, - exclude_label, self.exclude_pattern, - self.exclude_regexp]: - hlayout2.addWidget(widget) - - # Layout 3 - hlayout3 = QHBoxLayout() - self.python_path = QRadioButton(_("PYTHONPATH"), self) - self.python_path.setChecked(in_python_path) - self.python_path.setToolTip(_( - "Search in all directories listed in sys.path which" - " are outside the Python installation directory")) - self.hg_manifest = QRadioButton(_("Hg repository"), self) - self.detect_hg_repository() - self.hg_manifest.setToolTip( - _("Search in current directory hg repository")) - self.custom_dir = QRadioButton(_("Here:"), self) - self.custom_dir.setChecked(not in_python_path) - self.dir_combo = PathComboBox(self) - self.dir_combo.addItems(search_path) - self.dir_combo.setToolTip(_("Search recursively in this directory")) - self.connect(self.dir_combo, SIGNAL("open_dir(QString)"), - self.set_directory) - self.connect(self.python_path, SIGNAL('toggled(bool)'), - self.dir_combo.setDisabled) - self.connect(self.hg_manifest, SIGNAL('toggled(bool)'), - self.dir_combo.setDisabled) - browse = create_toolbutton(self, icon=get_std_icon('DirOpenIcon'), - tip=_('Browse a search directory'), - triggered=self.select_directory) - for widget in [self.python_path, self.hg_manifest, self.custom_dir, - self.dir_combo, browse]: - hlayout3.addWidget(widget) - - self.connect(self.search_text, SIGNAL("valid(bool)"), - lambda valid: self.emit(SIGNAL('find()'))) - self.connect(self.include_pattern, SIGNAL("valid(bool)"), - lambda valid: self.emit(SIGNAL('find()'))) - self.connect(self.exclude_pattern, SIGNAL("valid(bool)"), - lambda valid: self.emit(SIGNAL('find()'))) - self.connect(self.dir_combo, SIGNAL("valid(bool)"), - lambda valid: self.emit(SIGNAL('find()'))) - - vlayout = QVBoxLayout() - vlayout.setContentsMargins(0, 0, 0, 0) - vlayout.addLayout(hlayout1) - vlayout.addLayout(hlayout2) - vlayout.addLayout(hlayout3) - self.more_widgets = (hlayout2, hlayout3) - self.toggle_more_options(more_options) - self.setLayout(vlayout) - - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) - - def toggle_more_options(self, state): - for layout in self.more_widgets: - for index in range(layout.count()): - if state and self.isVisible() or not state: - layout.itemAt(index).widget().setVisible(state) - if state: - icon_name = 'options_less.png' - tip = _('Hide advanced options') - else: - icon_name = 'options_more.png' - tip = _('Show advanced options') - self.more_options.setIcon(get_icon(icon_name)) - self.more_options.setToolTip(tip) - - def update_combos(self): - self.search_text.lineEdit().emit(SIGNAL('returnPressed()')) - self.include_pattern.lineEdit().emit(SIGNAL('returnPressed()')) - self.exclude_pattern.lineEdit().emit(SIGNAL('returnPressed()')) - - def detect_hg_repository(self, path=None): - if path is None: - path = getcwd() - hg_repository = is_hg_installed() and get_vcs_root(path) is not None - self.hg_manifest.setEnabled(hg_repository) - if not hg_repository and self.hg_manifest.isChecked(): - self.custom_dir.setChecked(True) - - def set_search_text(self, text): - if text: - self.search_text.add_text(text) - self.search_text.lineEdit().selectAll() - self.search_text.setFocus() - - def get_options(self, all=False): - # Getting options - utext = to_text_string(self.search_text.currentText()) - if not utext: - return - try: - texts = [(utext.encode('ascii'), 'ascii')] - except UnicodeEncodeError: - texts = [] - for enc in self.supported_encodings: - try: - texts.append((utext.encode(enc), enc)) - except UnicodeDecodeError: - pass - text_re = self.edit_regexp.isChecked() - include = to_text_string(self.include_pattern.currentText()) - include_re = self.include_regexp.isChecked() - exclude = to_text_string(self.exclude_pattern.currentText()) - exclude_re = self.exclude_regexp.isChecked() - python_path = self.python_path.isChecked() - hg_manifest = self.hg_manifest.isChecked() - path = osp.abspath( to_text_string( self.dir_combo.currentText() ) ) - - # Finding text occurences - if not include_re: - include = fnmatch.translate(include) - if not exclude_re: - exclude = fnmatch.translate(exclude) - - if all: - search_text = [to_text_string(self.search_text.itemText(index)) \ - for index in range(self.search_text.count())] - search_path = [to_text_string(self.dir_combo.itemText(index)) \ - for index in range(self.dir_combo.count())] - include = [to_text_string(self.include_pattern.itemText(index)) \ - for index in range(self.include_pattern.count())] - include_idx = self.include_pattern.currentIndex() - exclude = [to_text_string(self.exclude_pattern.itemText(index)) \ - for index in range(self.exclude_pattern.count())] - exclude_idx = self.exclude_pattern.currentIndex() - more_options = self.more_options.isChecked() - return (search_text, text_re, search_path, - include, include_idx, include_re, - exclude, exclude_idx, exclude_re, - python_path, more_options) - else: - return (path, python_path, hg_manifest, - include, exclude, texts, text_re) - - def select_directory(self): - """Select directory""" - self.parent().emit(SIGNAL('redirect_stdio(bool)'), False) - directory = getexistingdirectory(self, _("Select directory"), - self.dir_combo.currentText()) - if directory: - self.set_directory(directory) - self.parent().emit(SIGNAL('redirect_stdio(bool)'), True) - - def set_directory(self, directory): - path = to_text_string(osp.abspath(to_text_string(directory))) - self.dir_combo.setEditText(path) - self.detect_hg_repository(path) - - def keyPressEvent(self, event): - """Reimplemented to handle key events""" - ctrl = event.modifiers() & Qt.ControlModifier - shift = event.modifiers() & Qt.ShiftModifier - if event.key() in (Qt.Key_Enter, Qt.Key_Return): - self.emit(SIGNAL('find()')) - elif event.key() == Qt.Key_F and ctrl and shift: - # Toggle find widgets - self.parent().emit(SIGNAL('toggle_visibility(bool)'), - not self.isVisible()) - else: - QWidget.keyPressEvent(self, event) - - -class ResultsBrowser(OneColumnTree): - def __init__(self, parent): - OneColumnTree.__init__(self, parent) - self.search_text = None - self.results = None - self.nb = None - self.error_flag = None - self.completed = None - self.data = None - self.set_title('') - self.root_items = None - - def activated(self, item): - """Double-click event""" - itemdata = self.data.get(id(self.currentItem())) - if itemdata is not None: - filename, lineno = itemdata - self.parent().emit(SIGNAL("edit_goto(QString,int,QString)"), - filename, lineno, self.search_text) - - def clicked(self, item): - """Click event""" - self.activated(item) - - def set_results(self, search_text, results, pathlist, nb, - error_flag, completed): - self.search_text = search_text - self.results = results - self.pathlist = pathlist - self.nb = nb - self.error_flag = error_flag - self.completed = completed - self.refresh() - if not self.error_flag and self.nb: - self.restore() - - def refresh(self): - """ - Refreshing search results panel - """ - title = "'%s' - " % self.search_text - if self.results is None: - text = _('Search canceled') - else: - nb_files = len(self.results) - if nb_files == 0: - text = _('String not found') - else: - text_matches = _('matches in') - text_files = _('file') - if nb_files > 1: - text_files += 's' - text = "%d %s %d %s" % (self.nb, text_matches, - nb_files, text_files) - if self.error_flag: - text += ' (' + self.error_flag + ')' - elif self.results is not None and not self.completed: - text += ' (' + _('interrupted') + ')' - self.set_title(title+text) - self.clear() - self.data = {} - - if not self.results: # First search interrupted *or* No result - return - - # Directory set - dir_set = set() - for filename in sorted(self.results.keys()): - dirname = osp.abspath(osp.dirname(filename)) - dir_set.add(dirname) - - # Root path - root_path_list = None - _common = get_common_path(list(dir_set)) - if _common is not None: - root_path_list = [_common] - else: - _common = get_common_path(self.pathlist) - if _common is not None: - root_path_list = [_common] - else: - root_path_list = self.pathlist - if not root_path_list: - return - for _root_path in root_path_list: - dir_set.add(_root_path) - # Populating tree: directories - def create_dir_item(dirname, parent): - if dirname not in root_path_list: - displayed_name = osp.basename(dirname) - else: - displayed_name = dirname - item = QTreeWidgetItem(parent, [displayed_name], - QTreeWidgetItem.Type) - item.setIcon(0, get_std_icon('DirClosedIcon')) - return item - dirs = {} - for dirname in sorted(list(dir_set)): - if dirname in root_path_list: - parent = self - else: - parent_dirname = abspardir(dirname) - parent = dirs.get(parent_dirname) - if parent is None: - # This is related to directories which contain found - # results only in some of their children directories - if osp.commonprefix([dirname]+root_path_list): - # create new root path - pass - items_to_create = [] - while dirs.get(parent_dirname) is None: - items_to_create.append(parent_dirname) - parent_dirname = abspardir(parent_dirname) - items_to_create.reverse() - for item_dir in items_to_create: - item_parent = dirs[abspardir(item_dir)] - dirs[item_dir] = create_dir_item(item_dir, item_parent) - parent_dirname = abspardir(dirname) - parent = dirs[parent_dirname] - dirs[dirname] = create_dir_item(dirname, parent) - self.root_items = [dirs[_root_path] for _root_path in root_path_list] - # Populating tree: files - for filename in sorted(self.results.keys()): - parent_item = dirs[osp.dirname(filename)] - file_item = QTreeWidgetItem(parent_item, [osp.basename(filename)], - QTreeWidgetItem.Type) - file_item.setIcon(0, get_filetype_icon(filename)) - colno_dict = {} - fname_res = [] - for lineno, colno, line in self.results[filename]: - if lineno not in colno_dict: - fname_res.append((lineno, colno, line)) - colno_dict[lineno] = colno_dict.get(lineno, [])+[str(colno)] - for lineno, colno, line in fname_res: - colno_str = ",".join(colno_dict[lineno]) - item = QTreeWidgetItem(file_item, - ["%d (%s): %s" % (lineno, colno_str, line.rstrip())], - QTreeWidgetItem.Type) - item.setIcon(0, get_icon('arrow.png')) - self.data[id(item)] = (filename, lineno) - # Removing empty directories - top_level_items = [self.topLevelItem(index) - for index in range(self.topLevelItemCount())] - for item in top_level_items: - if not item.childCount(): - self.takeTopLevelItem(self.indexOfTopLevelItem(item)) - - -class FindInFilesWidget(QWidget): - """ - Find in files widget - """ - def __init__(self, parent, - search_text = r"# ?TODO|# ?FIXME|# ?XXX", - search_text_regexp=True, search_path=None, - include=[".", ".py"], include_idx=None, include_regexp=True, - exclude=r"\.pyc$|\.orig$|\.hg|\.svn", exclude_idx=None, - exclude_regexp=True, - supported_encodings=("utf-8", "iso-8859-1", "cp1252"), - in_python_path=False, more_options=False): - QWidget.__init__(self, parent) - - self.setWindowTitle(_('Find in files')) - - self.search_thread = None - self.get_pythonpath_callback = None - - self.find_options = FindOptions(self, search_text, search_text_regexp, - search_path, - include, include_idx, include_regexp, - exclude, exclude_idx, exclude_regexp, - supported_encodings, in_python_path, - more_options) - self.connect(self.find_options, SIGNAL('find()'), self.find) - self.connect(self.find_options, SIGNAL('stop()'), - self.stop_and_reset_thread) - - self.result_browser = ResultsBrowser(self) - - collapse_btn = create_toolbutton(self) - collapse_btn.setDefaultAction(self.result_browser.collapse_all_action) - expand_btn = create_toolbutton(self) - expand_btn.setDefaultAction(self.result_browser.expand_all_action) - restore_btn = create_toolbutton(self) - restore_btn.setDefaultAction(self.result_browser.restore_action) -# collapse_sel_btn = create_toolbutton(self) -# collapse_sel_btn.setDefaultAction( -# self.result_browser.collapse_selection_action) -# expand_sel_btn = create_toolbutton(self) -# expand_sel_btn.setDefaultAction( -# self.result_browser.expand_selection_action) - - btn_layout = QVBoxLayout() - btn_layout.setAlignment(Qt.AlignTop) - for widget in [collapse_btn, expand_btn, restore_btn]: -# collapse_sel_btn, expand_sel_btn]: - btn_layout.addWidget(widget) - - hlayout = QHBoxLayout() - hlayout.addWidget(self.result_browser) - hlayout.addLayout(btn_layout) - - layout = QVBoxLayout() - left, _x, right, bottom = layout.getContentsMargins() - layout.setContentsMargins(left, 0, right, bottom) - layout.addWidget(self.find_options) - layout.addLayout(hlayout) - self.setLayout(layout) - - def set_search_text(self, text): - """Set search pattern""" - self.find_options.set_search_text(text) - - def find(self): - """Call the find function""" - options = self.find_options.get_options() - if options is None: - return - self.stop_and_reset_thread(ignore_results=True) - self.search_thread = SearchThread(self) - self.search_thread.get_pythonpath_callback = \ - self.get_pythonpath_callback - self.connect(self.search_thread, SIGNAL("finished(bool)"), - self.search_complete) - self.search_thread.initialize(*options) - self.search_thread.start() - self.find_options.ok_button.setEnabled(False) - self.find_options.stop_button.setEnabled(True) - - def stop_and_reset_thread(self, ignore_results=False): - """Stop current search thread and clean-up""" - if self.search_thread is not None: - if self.search_thread.isRunning(): - if ignore_results: - self.disconnect(self.search_thread, - SIGNAL("finished(bool)"), - self.search_complete) - self.search_thread.stop() - self.search_thread.wait() - self.search_thread.setParent(None) - self.search_thread = None - - def closing_widget(self): - """Perform actions before widget is closed""" - self.stop_and_reset_thread(ignore_results=True) - - def search_complete(self, completed): - """Current search thread has finished""" - self.find_options.ok_button.setEnabled(True) - self.find_options.stop_button.setEnabled(False) - if self.search_thread is None: - return - found = self.search_thread.get_results() - self.stop_and_reset_thread() - if found is not None: - results, pathlist, nb, error_flag = found - search_text = to_text_string( - self.find_options.search_text.currentText()) - self.result_browser.set_results(search_text, results, pathlist, - nb, error_flag, completed) - self.result_browser.show() - - -def test(): - """Run Find in Files widget test""" - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - widget = FindInFilesWidget(None) - widget.show() - sys.exit(app.exec_()) - -if __name__ == '__main__': - test() - \ No newline at end of file diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/findreplace.py spyder-3.0.2+dfsg1/spyderlib/widgets/findreplace.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/findreplace.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/findreplace.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,393 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Find/Replace widget""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import (QHBoxLayout, QGridLayout, QCheckBox, QLabel, - QWidget, QSizePolicy, QTextCursor) -from spyderlib.qt.QtCore import SIGNAL, Qt, QTimer - -import re - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.guiconfig import create_shortcut, new_shortcut -from spyderlib.utils.qthelpers import (get_icon, get_std_icon, - create_toolbutton) -from spyderlib.widgets.comboboxes import PatternComboBox -from spyderlib.py3compat import to_text_string - - -def is_position_sup(pos1, pos2): - """Return True is pos1 > pos2""" - return pos1 > pos2 - -def is_position_inf(pos1, pos2): - """Return True is pos1 < pos2""" - return pos1 < pos2 - - -class FindReplace(QWidget): - """ - Find widget - - Signals: - visibility_changed(bool) - """ - STYLE = {False: "background-color:rgb(255, 175, 90);", - True: ""} - def __init__(self, parent, enable_replace=False): - QWidget.__init__(self, parent) - self.enable_replace = enable_replace - self.editor = None - self.is_code_editor = None - - glayout = QGridLayout() - glayout.setContentsMargins(0, 0, 0, 0) - self.setLayout(glayout) - - self.close_button = create_toolbutton(self, triggered=self.hide, - icon=get_std_icon("DialogCloseButton")) - glayout.addWidget(self.close_button, 0, 0) - - # Find layout - self.search_text = PatternComboBox(self, tip=_("Search string"), - adjust_to_minimum=False) - self.connect(self.search_text, SIGNAL('valid(bool)'), - lambda state: - self.find(changed=False, forward=True, rehighlight=False)) - self.connect(self.search_text.lineEdit(), - SIGNAL("textEdited(QString)"), self.text_has_been_edited) - - self.previous_button = create_toolbutton(self, - triggered=self.find_previous, - icon=get_std_icon("ArrowBack")) - self.next_button = create_toolbutton(self, - triggered=self.find_next, - icon=get_std_icon("ArrowForward")) - self.connect(self.next_button, SIGNAL('clicked()'), - self.update_search_combo) - self.connect(self.previous_button, SIGNAL('clicked()'), - self.update_search_combo) - - self.re_button = create_toolbutton(self, icon=get_icon("advanced.png"), - tip=_("Regular expression")) - self.re_button.setCheckable(True) - self.connect(self.re_button, SIGNAL("toggled(bool)"), - lambda state: self.find()) - - self.case_button = create_toolbutton(self, - icon=get_icon("upper_lower.png"), - tip=_("Case Sensitive")) - self.case_button.setCheckable(True) - self.connect(self.case_button, SIGNAL("toggled(bool)"), - lambda state: self.find()) - - self.words_button = create_toolbutton(self, - icon=get_icon("whole_words.png"), - tip=_("Whole words")) - self.words_button.setCheckable(True) - self.connect(self.words_button, SIGNAL("toggled(bool)"), - lambda state: self.find()) - - self.highlight_button = create_toolbutton(self, - icon=get_icon("highlight.png"), - tip=_("Highlight matches")) - self.highlight_button.setCheckable(True) - self.connect(self.highlight_button, SIGNAL("toggled(bool)"), - self.toggle_highlighting) - - hlayout = QHBoxLayout() - self.widgets = [self.close_button, self.search_text, - self.previous_button, self.next_button, - self.re_button, self.case_button, self.words_button, - self.highlight_button] - for widget in self.widgets[1:]: - hlayout.addWidget(widget) - glayout.addLayout(hlayout, 0, 1) - - # Replace layout - replace_with = QLabel(_("Replace with:")) - self.replace_text = PatternComboBox(self, adjust_to_minimum=False, - tip=_("Replace string")) - - self.replace_button = create_toolbutton(self, - text=_("Replace/find"), - icon=get_std_icon("DialogApplyButton"), - triggered=self.replace_find, - text_beside_icon=True) - self.connect(self.replace_button, SIGNAL('clicked()'), - self.update_replace_combo) - self.connect(self.replace_button, SIGNAL('clicked()'), - self.update_search_combo) - - self.all_check = QCheckBox(_("Replace all")) - - self.replace_layout = QHBoxLayout() - widgets = [replace_with, self.replace_text, self.replace_button, - self.all_check] - for widget in widgets: - self.replace_layout.addWidget(widget) - glayout.addLayout(self.replace_layout, 1, 1) - self.widgets.extend(widgets) - self.replace_widgets = widgets - self.hide_replace() - - self.search_text.setTabOrder(self.search_text, self.replace_text) - - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - - self.shortcuts = self.create_shortcuts(parent) - - self.highlight_timer = QTimer(self) - self.highlight_timer.setSingleShot(True) - self.highlight_timer.setInterval(1000) - self.connect(self.highlight_timer, SIGNAL("timeout()"), - self.highlight_matches) - - def create_shortcuts(self, parent): - """Create shortcuts for this widget""" - # Configurable - findnext = create_shortcut(self.find_next, context='Editor', - name='Find next', parent=parent) - findprev = create_shortcut(self.find_previous, context='Editor', - name='Find previous', parent=parent) - togglefind = create_shortcut(self.show, context='Editor', - name='Find text', parent=parent) - togglereplace = create_shortcut(self.toggle_replace_widgets, - context='Editor', name='Replace text', - parent=parent) - # Fixed - new_shortcut("Escape", self, self.hide) - - return [findnext, findprev, togglefind, togglereplace] - - def get_shortcut_data(self): - """ - Returns shortcut data, a list of tuples (shortcut, text, default) - shortcut (QShortcut or QAction instance) - text (string): action/shortcut description - default (string): default key sequence - """ - return [sc.data for sc in self.shortcuts] - - def update_search_combo(self): - self.search_text.lineEdit().emit(SIGNAL('returnPressed()')) - - def update_replace_combo(self): - self.replace_text.lineEdit().emit(SIGNAL('returnPressed()')) - - def toggle_replace_widgets(self): - if self.enable_replace: - # Toggle replace widgets - if self.replace_widgets[0].isVisible(): - self.hide_replace() - self.hide() - else: - self.show_replace() - self.replace_text.setFocus() - - def toggle_highlighting(self, state): - """Toggle the 'highlight all results' feature""" - if self.editor is not None: - if state: - self.highlight_matches() - else: - self.clear_matches() - - def show(self): - """Overrides Qt Method""" - QWidget.show(self) - self.emit(SIGNAL("visibility_changed(bool)"), True) - if self.editor is not None: - text = self.editor.get_selected_text() - if len(text) > 0: - self.search_text.setEditText(text) - self.search_text.lineEdit().selectAll() - self.refresh() - else: - self.search_text.lineEdit().selectAll() - self.search_text.setFocus() - - def hide(self): - """Overrides Qt Method""" - for widget in self.replace_widgets: - widget.hide() - QWidget.hide(self) - self.emit(SIGNAL("visibility_changed(bool)"), False) - if self.editor is not None: - self.editor.setFocus() - self.clear_matches() - - def show_replace(self): - """Show replace widgets""" - self.show() - for widget in self.replace_widgets: - widget.show() - - def hide_replace(self): - """Hide replace widgets""" - for widget in self.replace_widgets: - widget.hide() - - def refresh(self): - """Refresh widget""" - if self.isHidden(): - if self.editor is not None: - self.clear_matches() - return - state = self.editor is not None - for widget in self.widgets: - widget.setEnabled(state) - if state: - self.find() - - def set_editor(self, editor, refresh=True): - """ - Set associated editor/web page: - codeeditor.base.TextEditBaseWidget - browser.WebView - """ - self.editor = editor - from spyderlib.qt.QtWebKit import QWebView - self.words_button.setVisible(not isinstance(editor, QWebView)) - self.re_button.setVisible(not isinstance(editor, QWebView)) - from spyderlib.widgets.sourcecode.codeeditor import CodeEditor - self.is_code_editor = isinstance(editor, CodeEditor) - self.highlight_button.setVisible(self.is_code_editor) - if refresh: - self.refresh() - if self.isHidden() and editor is not None: - self.clear_matches() - - def find_next(self): - """Find next occurence""" - state = self.find(changed=False, forward=True, rehighlight=False) - self.editor.setFocus() - self.search_text.add_current_text() - return state - - def find_previous(self): - """Find previous occurence""" - state = self.find(changed=False, forward=False, rehighlight=False) - self.editor.setFocus() - return state - - def text_has_been_edited(self, text): - """Find text has been edited (this slot won't be triggered when - setting the search pattern combo box text programmatically""" - self.find(changed=True, forward=True, start_highlight_timer=True) - - def highlight_matches(self): - """Highlight found results""" - if self.is_code_editor and self.highlight_button.isChecked(): - text = self.search_text.currentText() - words = self.words_button.isChecked() - regexp = self.re_button.isChecked() - self.editor.highlight_found_results(text, words=words, - regexp=regexp) - - def clear_matches(self): - """Clear all highlighted matches""" - if self.is_code_editor: - self.editor.clear_found_results() - - def find(self, changed=True, forward=True, - rehighlight=True, start_highlight_timer=False): - """Call the find function""" - text = self.search_text.currentText() - if len(text) == 0: - self.search_text.lineEdit().setStyleSheet("") - return None - else: - case = self.case_button.isChecked() - words = self.words_button.isChecked() - regexp = self.re_button.isChecked() - found = self.editor.find_text(text, changed, forward, case=case, - words=words, regexp=regexp) - self.search_text.lineEdit().setStyleSheet(self.STYLE[found]) - if self.is_code_editor and found: - if rehighlight or not self.editor.found_results: - self.highlight_timer.stop() - if start_highlight_timer: - self.highlight_timer.start() - else: - self.highlight_matches() - else: - self.clear_matches() - return found - - def replace_find(self): - """Replace and find""" - if (self.editor is not None): - replace_text = to_text_string(self.replace_text.currentText()) - search_text = to_text_string(self.search_text.currentText()) - pattern = search_text if self.re_button.isChecked() else None - case = self.case_button.isChecked() - first = True - cursor = None - while True: - if first: - # First found - seltxt = to_text_string(self.editor.get_selected_text()) - cmptxt1 = search_text if case else search_text.lower() - cmptxt2 = seltxt if case else seltxt.lower() - if self.editor.has_selected_text() and cmptxt1 == cmptxt2: - # Text was already found, do nothing - pass - else: - if not self.find(changed=False, forward=True, - rehighlight=False): - break - first = False - wrapped = False - position = self.editor.get_position('cursor') - position0 = position - cursor = self.editor.textCursor() - cursor.beginEditBlock() - else: - position1 = self.editor.get_position('cursor') - if is_position_inf(position1, - position0 + len(replace_text) - - len(search_text) + 1): - # Identify wrapping even when the replace string - # includes part of the search string - wrapped = True - if wrapped: - if position1 == position or \ - is_position_sup(position1, position): - # Avoid infinite loop: replace string includes - # part of the search string - break - if position1 == position0: - # Avoid infinite loop: single found occurence - break - position0 = position1 - if pattern is None: - cursor.removeSelectedText() - cursor.insertText(replace_text) - else: - seltxt = to_text_string(cursor.selectedText()) - cursor.removeSelectedText() - cursor.insertText(re.sub(pattern, replace_text, seltxt)) - if self.find_next(): - found_cursor = self.editor.textCursor() - cursor.setPosition(found_cursor.selectionStart(), - QTextCursor.MoveAnchor) - cursor.setPosition(found_cursor.selectionEnd(), - QTextCursor.KeepAnchor) - else: - break - if not self.all_check.isChecked(): - break - self.all_check.setCheckState(Qt.Unchecked) - if cursor is not None: - cursor.endEditBlock() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/formlayout.py spyder-3.0.2+dfsg1/spyderlib/widgets/formlayout.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/formlayout.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/formlayout.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,586 +0,0 @@ -# -*- coding: utf-8 -*- -""" -formlayout -========== - -Module creating Qt form dialogs/layouts to edit various type of parameters - - -formlayout License Agreement (MIT License) ------------------------------------------- - -Copyright (c) 2009 Pierre Raybaut - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. -""" - -from __future__ import print_function - -# History: -# 1.0.15: added support for multiline strings -# 1.0.14: fixed Python 3 support (regression in 1.0.13) -# 1.0.13: replaced obsolete QColorDialog.getRgba function and fixed other -# compatibility issues with PySide (see Issue 8 of formlayout website) -# 1.0.12: added support for Python 3 -# 1.0.11: added support for PySide -# 1.0.10: added float validator: disable "OK" and "Apply" button when not valid -# 1.0.7: added support for "Apply" button -# 1.0.6: code cleaning - -__version__ = '1.0.15' -__license__ = __doc__ - -import os - -try: - from spyderlib.qt.QtGui import QFormLayout -except ImportError: - raise ImportError("Warning: formlayout requires PyQt4 >v4.3") - -from spyderlib.qt.QtGui import (QWidget, QLineEdit, QComboBox, QLabel, - QSpinBox, QIcon, QStyle, QDialogButtonBox, - QHBoxLayout, QVBoxLayout, QDialog, QColor, - QPushButton, QCheckBox, QColorDialog, QPixmap, - QTabWidget, QApplication, QStackedWidget, - QDateEdit, QDateTimeEdit, QFont, QFontComboBox, - QFontDatabase, QGridLayout, QDoubleValidator, - QTextEdit) -from spyderlib.qt.QtCore import Qt, SIGNAL, SLOT, QSize, Slot, Property -import datetime - -# Local imports -from spyderlib.baseconfig import _, DEBUG, STDERR -from spyderlib.py3compat import is_text_string, to_text_string, is_string, u - -DEBUG_FORMLAYOUT = DEBUG >= 2 - - -class ColorButton(QPushButton): - """ - Color choosing push button - """ - __pyqtSignals__ = ("colorChanged(QColor)",) - - def __init__(self, parent=None): - QPushButton.__init__(self, parent) - self.setFixedSize(20, 20) - self.setIconSize(QSize(12, 12)) - self.connect(self, SIGNAL("clicked()"), self.choose_color) - self._color = QColor() - - def choose_color(self): - color = QColorDialog.getColor(self._color, self.parentWidget()) - if color.isValid(): - self.set_color(color) - - def get_color(self): - return self._color - - @Slot(QColor) - def set_color(self, color): - if color != self._color: - self._color = color - self.emit(SIGNAL("colorChanged(QColor)"), self._color) - pixmap = QPixmap(self.iconSize()) - pixmap.fill(color) - self.setIcon(QIcon(pixmap)) - - color = Property("QColor", get_color, set_color) - - -def text_to_qcolor(text): - """ - Create a QColor from specified string - Avoid warning from Qt when an invalid QColor is instantiated - """ - color = QColor() - if not is_string(text): # testing for QString (PyQt API#1) - text = str(text) - if not is_text_string(text): - return color - if text.startswith('#') and len(text)==7: - correct = '#0123456789abcdef' - for char in text: - if char.lower() not in correct: - return color - elif text not in list(QColor.colorNames()): - return color - color.setNamedColor(text) - return color - - -class ColorLayout(QHBoxLayout): - """Color-specialized QLineEdit layout""" - def __init__(self, color, parent=None): - QHBoxLayout.__init__(self) - assert isinstance(color, QColor) - self.lineedit = QLineEdit(color.name(), parent) - self.connect(self.lineedit, SIGNAL("textChanged(QString)"), - self.update_color) - self.addWidget(self.lineedit) - self.colorbtn = ColorButton(parent) - self.colorbtn.color = color - self.connect(self.colorbtn, SIGNAL("colorChanged(QColor)"), - self.update_text) - self.addWidget(self.colorbtn) - - def update_color(self, text): - color = text_to_qcolor(text) - if color.isValid(): - self.colorbtn.color = color - - def update_text(self, color): - self.lineedit.setText(color.name()) - - def text(self): - return self.lineedit.text() - - -def font_is_installed(font): - """Check if font is installed""" - return [fam for fam in QFontDatabase().families() - if to_text_string(fam) == font] - -def tuple_to_qfont(tup): - """ - Create a QFont from tuple: - (family [string], size [int], italic [bool], bold [bool]) - """ - if not isinstance(tup, tuple) or len(tup) != 4 \ - or not font_is_installed(tup[0]) \ - or not isinstance(tup[1], int) \ - or not isinstance(tup[2], bool) \ - or not isinstance(tup[3], bool): - return None - font = QFont() - family, size, italic, bold = tup - font.setFamily(family) - font.setPointSize(size) - font.setItalic(italic) - font.setBold(bold) - return font - -def qfont_to_tuple(font): - return (to_text_string(font.family()), int(font.pointSize()), - font.italic(), font.bold()) - -class FontLayout(QGridLayout): - """Font selection""" - def __init__(self, value, parent=None): - QGridLayout.__init__(self) - font = tuple_to_qfont(value) - assert font is not None - - # Font family - self.family = QFontComboBox(parent) - self.family.setCurrentFont(font) - self.addWidget(self.family, 0, 0, 1, -1) - - # Font size - self.size = QComboBox(parent) - self.size.setEditable(True) - sizelist = list(range(6, 12)) + list(range(12, 30, 2)) + [36, 48, 72] - size = font.pointSize() - if size not in sizelist: - sizelist.append(size) - sizelist.sort() - self.size.addItems([str(s) for s in sizelist]) - self.size.setCurrentIndex(sizelist.index(size)) - self.addWidget(self.size, 1, 0) - - # Italic or not - self.italic = QCheckBox(_("Italic"), parent) - self.italic.setChecked(font.italic()) - self.addWidget(self.italic, 1, 1) - - # Bold or not - self.bold = QCheckBox(_("Bold"), parent) - self.bold.setChecked(font.bold()) - self.addWidget(self.bold, 1, 2) - - def get_font(self): - font = self.family.currentFont() - font.setItalic(self.italic.isChecked()) - font.setBold(self.bold.isChecked()) - font.setPointSize(int(self.size.currentText())) - return qfont_to_tuple(font) - - -def is_edit_valid(edit): - text = edit.text() - state, _t = edit.validator().validate(text, 0) - return state == QDoubleValidator.Acceptable - -class FormWidget(QWidget): - def __init__(self, data, comment="", parent=None): - QWidget.__init__(self, parent) - from copy import deepcopy - self.data = deepcopy(data) - self.widgets = [] - self.formlayout = QFormLayout(self) - if comment: - self.formlayout.addRow(QLabel(comment)) - self.formlayout.addRow(QLabel(" ")) - if DEBUG_FORMLAYOUT: - print("\n"+("*"*80)) - print("DATA:", self.data) - print("*"*80) - print("COMMENT:", comment) - print("*"*80) - - def get_dialog(self): - """Return FormDialog instance""" - dialog = self.parent() - while not isinstance(dialog, QDialog): - dialog = dialog.parent() - return dialog - - def setup(self): - for label, value in self.data: - if DEBUG_FORMLAYOUT: - print("value:", value) - if label is None and value is None: - # Separator: (None, None) - self.formlayout.addRow(QLabel(" "), QLabel(" ")) - self.widgets.append(None) - continue - elif label is None: - # Comment - self.formlayout.addRow(QLabel(value)) - self.widgets.append(None) - continue - elif tuple_to_qfont(value) is not None: - field = FontLayout(value, self) - elif text_to_qcolor(value).isValid(): - field = ColorLayout(QColor(value), self) - elif is_text_string(value): - if '\n' in value: - for linesep in (os.linesep, '\n'): - if linesep in value: - value = value.replace(linesep, u("\u2029")) - field = QTextEdit(value, self) - else: - field = QLineEdit(value, self) - elif isinstance(value, (list, tuple)): - value = list(value) # in case this is a tuple - selindex = value.pop(0) - field = QComboBox(self) - if isinstance(value[0], (list, tuple)): - keys = [ key for key, _val in value ] - value = [ val for _key, val in value ] - else: - keys = value - field.addItems(value) - if selindex in value: - selindex = value.index(selindex) - elif selindex in keys: - selindex = keys.index(selindex) - elif not isinstance(selindex, int): - print("Warning: '%s' index is invalid (label: "\ - "%s, value: %s)" % (selindex, label, value), - file=STDERR) - selindex = 0 - field.setCurrentIndex(selindex) - elif isinstance(value, bool): - field = QCheckBox(self) - field.setCheckState(Qt.Checked if value else Qt.Unchecked) - elif isinstance(value, float): - field = QLineEdit(repr(value), self) - field.setValidator(QDoubleValidator(field)) - dialog = self.get_dialog() - dialog.register_float_field(field) - self.connect(field, SIGNAL('textChanged(QString)'), - lambda text: dialog.update_buttons()) - elif isinstance(value, int): - field = QSpinBox(self) - field.setRange(-1e9, 1e9) - field.setValue(value) - elif isinstance(value, datetime.datetime): - field = QDateTimeEdit(self) - field.setDateTime(value) - elif isinstance(value, datetime.date): - field = QDateEdit(self) - field.setDate(value) - else: - field = QLineEdit(repr(value), self) - self.formlayout.addRow(label, field) - self.widgets.append(field) - - def get(self): - valuelist = [] - for index, (label, value) in enumerate(self.data): - field = self.widgets[index] - if label is None: - # Separator / Comment - continue - elif tuple_to_qfont(value) is not None: - value = field.get_font() - elif is_text_string(value): - if isinstance(field, QTextEdit): - value = to_text_string(field.toPlainText() - ).replace(u("\u2029"), os.linesep) - else: - value = to_text_string(field.text()) - elif isinstance(value, (list, tuple)): - index = int(field.currentIndex()) - if isinstance(value[0], int): - # Return an int index, if initialization was an int - value = index - else: - value = value[index+1] - if isinstance(value, (list, tuple)): - value = value[0] - elif isinstance(value, bool): - value = field.checkState() == Qt.Checked - elif isinstance(value, float): - value = float(field.text()) - elif isinstance(value, int): - value = int(field.value()) - elif isinstance(value, datetime.datetime): - value = field.dateTime() - try: - value = value.toPyDateTime() # PyQt - except AttributeError: - value = value.toPython() # PySide - elif isinstance(value, datetime.date): - value = field.date() - try: - value = value.toPyDate() # PyQt - except AttributeError: - value = value.toPython() # PySide - else: - value = eval(str(field.text())) - valuelist.append(value) - return valuelist - - -class FormComboWidget(QWidget): - def __init__(self, datalist, comment="", parent=None): - QWidget.__init__(self, parent) - layout = QVBoxLayout() - self.setLayout(layout) - self.combobox = QComboBox() - layout.addWidget(self.combobox) - - self.stackwidget = QStackedWidget(self) - layout.addWidget(self.stackwidget) - self.connect(self.combobox, SIGNAL("currentIndexChanged(int)"), - self.stackwidget, SLOT("setCurrentIndex(int)")) - - self.widgetlist = [] - for data, title, comment in datalist: - self.combobox.addItem(title) - widget = FormWidget(data, comment=comment, parent=self) - self.stackwidget.addWidget(widget) - self.widgetlist.append(widget) - - def setup(self): - for widget in self.widgetlist: - widget.setup() - - def get(self): - return [ widget.get() for widget in self.widgetlist] - - -class FormTabWidget(QWidget): - def __init__(self, datalist, comment="", parent=None): - QWidget.__init__(self, parent) - layout = QVBoxLayout() - self.tabwidget = QTabWidget() - layout.addWidget(self.tabwidget) - self.setLayout(layout) - self.widgetlist = [] - for data, title, comment in datalist: - if len(data[0])==3: - widget = FormComboWidget(data, comment=comment, parent=self) - else: - widget = FormWidget(data, comment=comment, parent=self) - index = self.tabwidget.addTab(widget, title) - self.tabwidget.setTabToolTip(index, comment) - self.widgetlist.append(widget) - - def setup(self): - for widget in self.widgetlist: - widget.setup() - - def get(self): - return [ widget.get() for widget in self.widgetlist] - - -class FormDialog(QDialog): - """Form Dialog""" - def __init__(self, data, title="", comment="", - icon=None, parent=None, apply=None): - QDialog.__init__(self, parent) - - self.apply_callback = apply - - # Form - if isinstance(data[0][0], (list, tuple)): - self.formwidget = FormTabWidget(data, comment=comment, - parent=self) - elif len(data[0])==3: - self.formwidget = FormComboWidget(data, comment=comment, - parent=self) - else: - self.formwidget = FormWidget(data, comment=comment, - parent=self) - layout = QVBoxLayout() - layout.addWidget(self.formwidget) - - self.float_fields = [] - self.formwidget.setup() - - # Button box - self.bbox = bbox = QDialogButtonBox(QDialogButtonBox.Ok - |QDialogButtonBox.Cancel) - self.connect(self.formwidget, SIGNAL('update_buttons()'), - self.update_buttons) - if self.apply_callback is not None: - apply_btn = bbox.addButton(QDialogButtonBox.Apply) - self.connect(apply_btn, SIGNAL("clicked()"), self.apply) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - layout.addWidget(bbox) - - self.setLayout(layout) - - self.setWindowTitle(title) - if not isinstance(icon, QIcon): - icon = QWidget().style().standardIcon(QStyle.SP_MessageBoxQuestion) - self.setWindowIcon(icon) - - def register_float_field(self, field): - self.float_fields.append(field) - - def update_buttons(self): - valid = True - for field in self.float_fields: - if not is_edit_valid(field): - valid = False - for btn_type in (QDialogButtonBox.Ok, QDialogButtonBox.Apply): - btn = self.bbox.button(btn_type) - if btn is not None: - btn.setEnabled(valid) - - def accept(self): - self.data = self.formwidget.get() - QDialog.accept(self) - - def reject(self): - self.data = None - QDialog.reject(self) - - def apply(self): - self.apply_callback(self.formwidget.get()) - - def get(self): - """Return form result""" - # It is import to avoid accessing Qt C++ object as it has probably - # already been destroyed, due to the Qt.WA_DeleteOnClose attribute - return self.data - - -def fedit(data, title="", comment="", icon=None, parent=None, apply=None): - """ - Create form dialog and return result - (if Cancel button is pressed, return None) - - data: datalist, datagroup - title: string - comment: string - icon: QIcon instance - parent: parent QWidget - apply: apply callback (function) - - datalist: list/tuple of (field_name, field_value) - datagroup: list/tuple of (datalist *or* datagroup, title, comment) - - -> one field for each member of a datalist - -> one tab for each member of a top-level datagroup - -> one page (of a multipage widget, each page can be selected with a combo - box) for each member of a datagroup inside a datagroup - - Supported types for field_value: - - int, float, str, unicode, bool - - colors: in Qt-compatible text form, i.e. in hex format or name (red,...) - (automatically detected from a string) - - list/tuple: - * the first element will be the selected index (or value) - * the other elements can be couples (key, value) or only values - """ - # Create a QApplication instance if no instance currently exists - # (e.g. if the module is used directly from the interpreter) - if QApplication.startingUp(): - _app = QApplication([]) - - dialog = FormDialog(data, title, comment, icon, parent, apply) - if dialog.exec_(): - return dialog.get() - - -if __name__ == "__main__": - - def create_datalist_example(): - return [('str', 'this is a string'), - ('str', """this is a - MULTILINE - string"""), - ('list', [0, '1', '3', '4']), - ('list2', ['--', ('none', 'None'), ('--', 'Dashed'), - ('-.', 'DashDot'), ('-', 'Solid'), - ('steps', 'Steps'), (':', 'Dotted')]), - ('float', 1.2), - (None, 'Other:'), - ('int', 12), - ('font', ('Arial', 10, False, True)), - ('color', '#123409'), - ('bool', True), - ('date', datetime.date(2010, 10, 10)), - ('datetime', datetime.datetime(2010, 10, 10)), - ] - - def create_datagroup_example(): - datalist = create_datalist_example() - return ((datalist, "Category 1", "Category 1 comment"), - (datalist, "Category 2", "Category 2 comment"), - (datalist, "Category 3", "Category 3 comment")) - - #--------- datalist example - datalist = create_datalist_example() - def apply_test(data): - print("data:", data) - print("result:", fedit(datalist, title="Example", - comment="This is just an example.", - apply=apply_test)) - - #--------- datagroup example - datagroup = create_datagroup_example() - print("result:", fedit(datagroup, "Global title")) - - #--------- datagroup inside a datagroup example - datalist = create_datalist_example() - datagroup = create_datagroup_example() - print("result:", fedit(((datagroup, "Title 1", "Tab 1 comment"), - (datalist, "Title 2", "Tab 2 comment"), - (datalist, "Title 3", "Tab 3 comment")), - "Global title")) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/importwizard.py spyder-3.0.2+dfsg1/spyderlib/widgets/importwizard.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/importwizard.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/importwizard.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,637 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Text data Importing Wizard based on Qt -""" - -from __future__ import print_function - -from spyderlib.qt.QtGui import (QTableView, QVBoxLayout, QHBoxLayout, - QGridLayout, QWidget, QDialog, QTextEdit, - QTabWidget, QPushButton, QLabel, QSpacerItem, - QSizePolicy, QCheckBox, QColor, QRadioButton, - QLineEdit, QFrame, QMenu, QIntValidator, - QGroupBox, QMessageBox) -from spyderlib.qt.QtCore import (Qt, QModelIndex, QAbstractTableModel, - SIGNAL, SLOT, Slot) -from spyderlib.qt.compat import to_qvariant - -from functools import partial as ft_partial - -try: - import pandas as pd -except ImportError: - pd = None - -# Local import -from spyderlib.baseconfig import _ -from spyderlib.utils import programs -from spyderlib.utils.qthelpers import get_icon, add_actions, create_action -from spyderlib.py3compat import (TEXT_TYPES, INT_TYPES, to_text_string, u, - zip_longest, io) - -def try_to_parse(value): - _types = ('int', 'float') - for _t in _types: - try: - _val = eval("%s('%s')" % (_t, value)) - return _val - except (ValueError, SyntaxError): - pass - return value - -def try_to_eval(value): - try: - return eval(value) - except (NameError, SyntaxError, ImportError): - return value - -#----Numpy arrays support -class FakeObject(object): - """Fake class used in replacement of missing modules""" - pass -try: - from numpy import ndarray, array -except ImportError: - class ndarray(FakeObject): # analysis:ignore - """Fake ndarray""" - pass - -#----date and datetime objects support -import datetime -try: - from dateutil.parser import parse as dateparse -except ImportError: - def dateparse(datestr, dayfirst=True): # analysis:ignore - """Just for 'day/month/year' strings""" - _a, _b, _c = list(map(int, datestr.split('/'))) - if dayfirst: - return datetime.datetime(_c, _b, _a) - return datetime.datetime(_c, _a, _b) - -def datestr_to_datetime(value, dayfirst=True): - return dateparse(value, dayfirst=dayfirst) - -#----Background colors for supported types -COLORS = { - bool: Qt.magenta, - tuple([float] + list(INT_TYPES)): Qt.blue, - list: Qt.yellow, - dict: Qt.cyan, - tuple: Qt.lightGray, - TEXT_TYPES: Qt.darkRed, - ndarray: Qt.green, - datetime.date: Qt.darkYellow, - } - -def get_color(value, alpha): - """Return color depending on value type""" - color = QColor() - for typ in COLORS: - if isinstance(value, typ): - color = QColor(COLORS[typ]) - color.setAlphaF(alpha) - return color - -class ContentsWidget(QWidget): - """Import wizard contents widget""" - def __init__(self, parent, text): - QWidget.__init__(self, parent) - - self.text_editor = QTextEdit(self) - self.text_editor.setText(text) - self.text_editor.setReadOnly(True) - - # Type frame - type_layout = QHBoxLayout() - type_label = QLabel(_("Import as")) - type_layout.addWidget(type_label) - data_btn = QRadioButton(_("data")) - data_btn.setChecked(True) - self._as_data= True - type_layout.addWidget(data_btn) - code_btn = QRadioButton(_("code")) - self._as_code = False - type_layout.addWidget(code_btn) - txt_btn = QRadioButton(_("text")) - type_layout.addWidget(txt_btn) - - h_spacer = QSpacerItem(40, 20, - QSizePolicy.Expanding, QSizePolicy.Minimum) - type_layout.addItem(h_spacer) - type_frame = QFrame() - type_frame.setLayout(type_layout) - - # Opts frame - grid_layout = QGridLayout() - grid_layout.setSpacing(0) - - col_label = QLabel(_("Column separator:")) - grid_layout.addWidget(col_label, 0, 0) - col_w = QWidget() - col_btn_layout = QHBoxLayout() - self.tab_btn = QRadioButton(_("Tab")) - self.tab_btn.setChecked(False) - col_btn_layout.addWidget(self.tab_btn) - other_btn_col = QRadioButton(_("other")) - other_btn_col.setChecked(True) - col_btn_layout.addWidget(other_btn_col) - col_w.setLayout(col_btn_layout) - grid_layout.addWidget(col_w, 0, 1) - self.line_edt = QLineEdit(",") - self.line_edt.setMaximumWidth(30) - self.line_edt.setEnabled(True) - self.connect(other_btn_col, SIGNAL("toggled(bool)"), - self.line_edt, SLOT("setEnabled(bool)")) - grid_layout.addWidget(self.line_edt, 0, 2) - - row_label = QLabel(_("Row separator:")) - grid_layout.addWidget(row_label, 1, 0) - row_w = QWidget() - row_btn_layout = QHBoxLayout() - self.eol_btn = QRadioButton(_("EOL")) - self.eol_btn.setChecked(True) - row_btn_layout.addWidget(self.eol_btn) - other_btn_row = QRadioButton(_("other")) - row_btn_layout.addWidget(other_btn_row) - row_w.setLayout(row_btn_layout) - grid_layout.addWidget(row_w, 1, 1) - self.line_edt_row = QLineEdit(";") - self.line_edt_row.setMaximumWidth(30) - self.line_edt_row.setEnabled(False) - self.connect(other_btn_row, SIGNAL("toggled(bool)"), - self.line_edt_row, SLOT("setEnabled(bool)")) - grid_layout.addWidget(self.line_edt_row, 1, 2) - - grid_layout.setRowMinimumHeight(2, 15) - - other_group = QGroupBox(_("Additional options")) - other_layout = QGridLayout() - other_group.setLayout(other_layout) - - skiprows_label = QLabel(_("Skip rows:")) - other_layout.addWidget(skiprows_label, 0, 0) - self.skiprows_edt = QLineEdit('0') - self.skiprows_edt.setMaximumWidth(30) - intvalid = QIntValidator(0, len(to_text_string(text).splitlines()), - self.skiprows_edt) - self.skiprows_edt.setValidator(intvalid) - other_layout.addWidget(self.skiprows_edt, 0, 1) - - other_layout.setColumnMinimumWidth(2, 5) - - comments_label = QLabel(_("Comments:")) - other_layout.addWidget(comments_label, 0, 3) - self.comments_edt = QLineEdit('#') - self.comments_edt.setMaximumWidth(30) - other_layout.addWidget(self.comments_edt, 0, 4) - - self.trnsp_box = QCheckBox(_("Transpose")) - #self.trnsp_box.setEnabled(False) - other_layout.addWidget(self.trnsp_box, 1, 0, 2, 0) - - grid_layout.addWidget(other_group, 3, 0, 2, 0) - - opts_frame = QFrame() - opts_frame.setLayout(grid_layout) - - self.connect(data_btn, SIGNAL("toggled(bool)"), - opts_frame, SLOT("setEnabled(bool)")) - self.connect(data_btn, SIGNAL("toggled(bool)"), - self, SLOT("set_as_data(bool)")) - self.connect(code_btn, SIGNAL("toggled(bool)"), - self, SLOT("set_as_code(bool)")) -# self.connect(txt_btn, SIGNAL("toggled(bool)"), -# self, SLOT("is_text(bool)")) - - # Final layout - layout = QVBoxLayout() - layout.addWidget(type_frame) - layout.addWidget(self.text_editor) - layout.addWidget(opts_frame) - self.setLayout(layout) - - def get_as_data(self): - """Return if data type conversion""" - return self._as_data - - def get_as_code(self): - """Return if code type conversion""" - return self._as_code - - def get_as_num(self): - """Return if numeric type conversion""" - return self._as_num - - def get_col_sep(self): - """Return the column separator""" - if self.tab_btn.isChecked(): - return u("\t") - return to_text_string(self.line_edt.text()) - - def get_row_sep(self): - """Return the row separator""" - if self.eol_btn.isChecked(): - return u("\n") - return to_text_string(self.line_edt_row.text()) - - def get_skiprows(self): - """Return number of lines to be skipped""" - return int(to_text_string(self.skiprows_edt.text())) - - def get_comments(self): - """Return comment string""" - return to_text_string(self.comments_edt.text()) - - @Slot(bool) - def set_as_data(self, as_data): - """Set if data type conversion""" - self._as_data = as_data - self.emit(SIGNAL("asDataChanged(bool)"), as_data) - - @Slot(bool) - def set_as_code(self, as_code): - """Set if code type conversion""" - self._as_code = as_code - - -class PreviewTableModel(QAbstractTableModel): - """Import wizard preview table model""" - def __init__(self, data=[], parent=None): - QAbstractTableModel.__init__(self, parent) - self._data = data - - def rowCount(self, parent=QModelIndex()): - """Return row count""" - return len(self._data) - - def columnCount(self, parent=QModelIndex()): - """Return column count""" - return len(self._data[0]) - - def _display_data(self, index): - """Return a data element""" - return to_qvariant(self._data[index.row()][index.column()]) - - def data(self, index, role=Qt.DisplayRole): - """Return a model data element""" - if not index.isValid(): - return to_qvariant() - if role == Qt.DisplayRole: - return self._display_data(index) - elif role == Qt.BackgroundColorRole: - return to_qvariant(get_color(self._data[index.row()][index.column()], .2)) - elif role == Qt.TextAlignmentRole: - return to_qvariant(int(Qt.AlignRight|Qt.AlignVCenter)) - return to_qvariant() - - def setData(self, index, value, role=Qt.EditRole): - """Set model data""" - return False - - def get_data(self): - """Return a copy of model data""" - return self._data[:][:] - - def parse_data_type(self, index, **kwargs): - """Parse a type to an other type""" - if not index.isValid(): - return False - try: - if kwargs['atype'] == "date": - self._data[index.row()][index.column()] = \ - datestr_to_datetime(self._data[index.row()][index.column()], - kwargs['dayfirst']).date() - elif kwargs['atype'] == "perc": - _tmp = self._data[index.row()][index.column()].replace("%", "") - self._data[index.row()][index.column()] = eval(_tmp)/100. - elif kwargs['atype'] == "account": - _tmp = self._data[index.row()][index.column()].replace(",", "") - self._data[index.row()][index.column()] = eval(_tmp) - elif kwargs['atype'] == "unicode": - self._data[index.row()][index.column()] = to_text_string( - self._data[index.row()][index.column()]) - elif kwargs['atype'] == "int": - self._data[index.row()][index.column()] = int( - self._data[index.row()][index.column()]) - elif kwargs['atype'] == "float": - self._data[index.row()][index.column()] = float( - self._data[index.row()][index.column()]) - self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, index) - except Exception as instance: - print(instance) - -class PreviewTable(QTableView): - """Import wizard preview widget""" - def __init__(self, parent): - QTableView.__init__(self, parent) - self._model = None - - # Setting up actions - self.date_dayfirst_action = create_action(self, "dayfirst", - triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=True)) - self.date_monthfirst_action = create_action(self, "monthfirst", - triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=False)) - self.perc_action = create_action(self, "perc", - triggered=ft_partial(self.parse_to_type, atype="perc")) - self.acc_action = create_action(self, "account", - triggered=ft_partial(self.parse_to_type, atype="account")) - self.str_action = create_action(self, "unicode", - triggered=ft_partial(self.parse_to_type, atype="unicode")) - self.int_action = create_action(self, "int", - triggered=ft_partial(self.parse_to_type, atype="int")) - self.float_action = create_action(self, "float", - triggered=ft_partial(self.parse_to_type, atype="float")) - - # Setting up menus - self.date_menu = QMenu() - self.date_menu.setTitle("Date") - add_actions( self.date_menu, (self.date_dayfirst_action, - self.date_monthfirst_action)) - self.parse_menu = QMenu(self) - self.parse_menu.addMenu(self.date_menu) - add_actions( self.parse_menu, (self.perc_action, self.acc_action)) - self.parse_menu.setTitle("String to") - self.opt_menu = QMenu(self) - self.opt_menu.addMenu(self.parse_menu) - add_actions( self.opt_menu, (self.str_action, self.int_action, - self.float_action)) - - def _shape_text(self, text, colsep=u("\t"), rowsep=u("\n"), - transpose=False, skiprows=0, comments='#'): - """Decode the shape of the given text""" - assert colsep != rowsep - out = [] - text_rows = text.split(rowsep)[skiprows:] - for row in text_rows: - stripped = to_text_string(row).strip() - if len(stripped) == 0 or stripped.startswith(comments): - continue - line = to_text_string(row).split(colsep) - line = [try_to_parse(to_text_string(x)) for x in line] - out.append(line) - # Replace missing elements with np.nan's or None's - if programs.is_module_installed('numpy'): - from numpy import nan - out = list(zip_longest(*out, fillvalue=nan)) - else: - out = list(zip_longest(*out, fillvalue=None)) - # Tranpose the last result to get the expected one - out = [[r[col] for r in out] for col in range(len(out[0]))] - if transpose: - return [[r[col] for r in out] for col in range(len(out[0]))] - return out - - def get_data(self): - """Return model data""" - if self._model is None: - return None - return self._model.get_data() - - def process_data(self, text, colsep=u("\t"), rowsep=u("\n"), - transpose=False, skiprows=0, comments='#'): - """Put data into table model""" - data = self._shape_text(text, colsep, rowsep, transpose, skiprows, - comments) - self._model = PreviewTableModel(data) - self.setModel(self._model) - - def parse_to_type(self,**kwargs): - """Parse to a given type""" - indexes = self.selectedIndexes() - if not indexes: return - for index in indexes: - self.model().parse_data_type(index, **kwargs) - - def contextMenuEvent(self, event): - """Reimplement Qt method""" - self.opt_menu.popup(event.globalPos()) - event.accept() - -class PreviewWidget(QWidget): - """Import wizard preview widget""" - - def __init__(self, parent): - QWidget.__init__(self, parent) - - vert_layout = QVBoxLayout() - - # Type frame - type_layout = QHBoxLayout() - type_label = QLabel(_("Import as")) - type_layout.addWidget(type_label) - - self.array_btn = array_btn = QRadioButton(_("array")) - array_btn.setEnabled(ndarray is not FakeObject) - array_btn.setChecked(ndarray is not FakeObject) - type_layout.addWidget(array_btn) - - list_btn = QRadioButton(_("list")) - list_btn.setChecked(not array_btn.isChecked()) - type_layout.addWidget(list_btn) - - if pd: - self.df_btn = df_btn = QRadioButton(_("DataFrame")) - df_btn.setChecked(False) - type_layout.addWidget(df_btn) - - h_spacer = QSpacerItem(40, 20, - QSizePolicy.Expanding, QSizePolicy.Minimum) - type_layout.addItem(h_spacer) - type_frame = QFrame() - type_frame.setLayout(type_layout) - - self._table_view = PreviewTable(self) - vert_layout.addWidget(type_frame) - vert_layout.addWidget(self._table_view) - self.setLayout(vert_layout) - - def open_data(self, text, colsep=u("\t"), rowsep=u("\n"), - transpose=False, skiprows=0, comments='#'): - """Open clipboard text as table""" - if pd: - self.pd_text = text - self.pd_info = dict(sep=colsep, lineterminator=rowsep, - skiprows=skiprows,comment=comments) - self._table_view.process_data(text, colsep, rowsep, transpose, - skiprows, comments) - - def get_data(self): - """Return table data""" - return self._table_view.get_data() - -class ImportWizard(QDialog): - """Text data import wizard""" - def __init__(self, parent, text, - title=None, icon=None, contents_title=None, varname=None): - QDialog.__init__(self, parent) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - if title is None: - title = _("Import wizard") - self.setWindowTitle(title) - if icon is None: - self.setWindowIcon(get_icon("fileimport.png")) - if contents_title is None: - contents_title = _("Raw text") - - if varname is None: - varname = _("variable_name") - - self.var_name, self.clip_data = None, None - - # Setting GUI - self.tab_widget = QTabWidget(self) - self.text_widget = ContentsWidget(self, text) - self.table_widget = PreviewWidget(self) - - self.tab_widget.addTab(self.text_widget, _("text")) - self.tab_widget.setTabText(0, contents_title) - self.tab_widget.addTab(self.table_widget, _("table")) - self.tab_widget.setTabText(1, _("Preview")) - self.tab_widget.setTabEnabled(1, False) - - name_layout = QHBoxLayout() - name_label = QLabel(_("Variable Name")) - name_layout.addWidget(name_label) - - self.name_edt = QLineEdit() - self.name_edt.setText(varname) - name_layout.addWidget(self.name_edt) - - btns_layout = QHBoxLayout() - cancel_btn = QPushButton(_("Cancel")) - btns_layout.addWidget(cancel_btn) - self.connect(cancel_btn, SIGNAL("clicked()"), self, SLOT("reject()")) - h_spacer = QSpacerItem(40, 20, - QSizePolicy.Expanding, QSizePolicy.Minimum) - btns_layout.addItem(h_spacer) - self.back_btn = QPushButton(_("Previous")) - self.back_btn.setEnabled(False) - btns_layout.addWidget(self.back_btn) - self.connect(self.back_btn, SIGNAL("clicked()"), - ft_partial(self._set_step, step=-1)) - self.fwd_btn = QPushButton(_("Next")) - btns_layout.addWidget(self.fwd_btn) - self.connect(self.fwd_btn, SIGNAL("clicked()"), - ft_partial(self._set_step, step=1)) - self.done_btn = QPushButton(_("Done")) - self.done_btn.setEnabled(False) - btns_layout.addWidget(self.done_btn) - self.connect(self.done_btn, SIGNAL("clicked()"), - self, SLOT("process()")) - - self.connect(self.text_widget, SIGNAL("asDataChanged(bool)"), - self.fwd_btn, SLOT("setEnabled(bool)")) - self.connect(self.text_widget, SIGNAL("asDataChanged(bool)"), - self.done_btn, SLOT("setDisabled(bool)")) - layout = QVBoxLayout() - layout.addLayout(name_layout) - layout.addWidget(self.tab_widget) - layout.addLayout(btns_layout) - self.setLayout(layout) - - def _focus_tab(self, tab_idx): - """Change tab focus""" - for i in range(self.tab_widget.count()): - self.tab_widget.setTabEnabled(i, False) - self.tab_widget.setTabEnabled(tab_idx, True) - self.tab_widget.setCurrentIndex(tab_idx) - - def _set_step(self, step): - """Proceed to a given step""" - new_tab = self.tab_widget.currentIndex() + step - assert new_tab < self.tab_widget.count() and new_tab >= 0 - if new_tab == self.tab_widget.count()-1: - try: - self.table_widget.open_data(self._get_plain_text(), - self.text_widget.get_col_sep(), - self.text_widget.get_row_sep(), - self.text_widget.trnsp_box.isChecked(), - self.text_widget.get_skiprows(), - self.text_widget.get_comments()) - self.done_btn.setEnabled(True) - self.done_btn.setDefault(True) - self.fwd_btn.setEnabled(False) - self.back_btn.setEnabled(True) - except (SyntaxError, AssertionError) as error: - QMessageBox.critical(self, _("Import wizard"), - _("Unable to proceed to next step" - "

    Please check your entries." - "

    Error message:
    %s") % str(error)) - return - elif new_tab == 0: - self.done_btn.setEnabled(False) - self.fwd_btn.setEnabled(True) - self.back_btn.setEnabled(False) - self._focus_tab(new_tab) - - def get_data(self): - """Return processed data""" - # It is import to avoid accessing Qt C++ object as it has probably - # already been destroyed, due to the Qt.WA_DeleteOnClose attribute - return self.var_name, self.clip_data - - def _simplify_shape(self, alist, rec=0): - """Reduce the alist dimension if needed""" - if rec != 0: - if len(alist) == 1: - return alist[-1] - return alist - if len(alist) == 1: - return self._simplify_shape(alist[-1], 1) - return [self._simplify_shape(al, 1) for al in alist] - - def _get_table_data(self): - """Return clipboard processed as data""" - data = self._simplify_shape( - self.table_widget.get_data()) - if self.table_widget.array_btn.isChecked(): - return array(data) - elif pd and self.table_widget.df_btn.isChecked(): - info = self.table_widget.pd_info - buf = io.StringIO(self.table_widget.pd_text) - return pd.read_csv(buf, **info) - return data - - def _get_plain_text(self): - """Return clipboard as text""" - return self.text_widget.text_editor.toPlainText() - - @Slot() - def process(self): - """Process the data from clipboard""" - var_name = self.name_edt.text() - try: - self.var_name = str(var_name) - except UnicodeEncodeError: - self.var_name = to_text_string(var_name) - if self.text_widget.get_as_data(): - self.clip_data = self._get_table_data() - elif self.text_widget.get_as_code(): - self.clip_data = try_to_eval( - to_text_string(self._get_plain_text())) - else: - self.clip_data = to_text_string(self._get_plain_text()) - self.accept() - - -def test(text): - """Test""" - from spyderlib.utils.qthelpers import qapplication - _app = qapplication() # analysis:ignore - dialog = ImportWizard(None, text) - if dialog.exec_(): - print(dialog.get_data()) - -if __name__ == "__main__": - test(u("17/11/1976\t1.34\n14/05/09\t3.14")) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/__init__.py spyder-3.0.2+dfsg1/spyderlib/widgets/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/__init__.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.widgets -================= - -Widgets defined in this module may be used in any other Qt-based application - -They are also used in Spyder through the Plugin interface -(see spyderlib.plugins) -""" diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/internalshell.py spyder-3.0.2+dfsg1/spyderlib/widgets/internalshell.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/internalshell.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/internalshell.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,457 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Internal shell widget : PythonShellWidget + Interpreter""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -#FIXME: Internal shell MT: for i in range(100000): print i -> bug - -#----Builtins* -from spyderlib.py3compat import builtins -from spyderlib.widgets.objecteditor import oedit -builtins.oedit = oedit - -import os -import threading -from time import time -from subprocess import Popen - -from spyderlib.qt.QtGui import QMessageBox -from spyderlib.qt.QtCore import SIGNAL, QObject, QEventLoop - -# Local import -from spyderlib import get_versions -from spyderlib.utils.qthelpers import create_action, get_std_icon -from spyderlib.interpreter import Interpreter -from spyderlib.utils.dochelpers import getargtxt, getsource, getdoc, getobjdir -from spyderlib.utils.misc import get_error_match -#TODO: remove the CONF object and make it work anyway -# In fact, this 'CONF' object has nothing to do in package spyderlib.widgets -# which should not contain anything directly related to Spyder's main app -from spyderlib.baseconfig import get_conf_path, _, DEBUG -from spyderlib.config import CONF -from spyderlib.widgets.shell import PythonShellWidget -from spyderlib.py3compat import to_text_string, getcwd, to_binary_string, u - - -def create_banner(message): - """Create internal shell banner""" - if message is None: - versions = get_versions() - return 'Python %s %dbits [%s]'\ - % (versions['python'], versions['bitness'], versions['system']) - else: - return message - - -class SysOutput(QObject): - """Handle standard I/O queue""" - def __init__(self): - QObject.__init__(self) - self.queue = [] - self.lock = threading.Lock() - - def write(self, val): - self.lock.acquire() - self.queue.append(val) - self.lock.release() - self.emit(SIGNAL("void data_avail()")) - - def empty_queue(self): - self.lock.acquire() - s = "".join(self.queue) - self.queue = [] - self.lock.release() - return s - - # We need to add this method to fix Issue 1789 - def flush(self): - pass - -class WidgetProxyData(object): - pass - -class WidgetProxy(QObject): - """Handle Shell widget refresh signal""" - def __init__(self, input_condition): - QObject.__init__(self) - self.input_data = None - self.input_condition = input_condition - - def new_prompt(self, prompt): - self.emit(SIGNAL("new_prompt(QString)"), prompt) - - def set_readonly(self, state): - self.emit(SIGNAL("set_readonly(bool)"), state) - - def edit(self, filename, external_editor=False): - self.emit(SIGNAL("edit(QString,bool)"), filename, external_editor) - - def data_available(self): - """Return True if input data is available""" - return self.input_data is not WidgetProxyData - - def wait_input(self, prompt=''): - self.input_data = WidgetProxyData - self.emit(SIGNAL("wait_input(QString)"), prompt) - - def end_input(self, cmd): - self.input_condition.acquire() - self.input_data = cmd - self.input_condition.notify() - self.input_condition.release() - - -class InternalShell(PythonShellWidget): - """Shell base widget: link between PythonShellWidget and Interpreter""" - def __init__(self, parent=None, namespace=None, commands=[], message=None, - max_line_count=300, font=None, exitfunc=None, profile=False, - multithreaded=True, light_background=True): - PythonShellWidget.__init__(self, parent, - get_conf_path('history_internal.py'), - profile) - - self.set_light_background(light_background) - self.multithreaded = multithreaded - self.setMaximumBlockCount(max_line_count) - - # For compatibility with ExtPythonShellWidget - self.is_ipykernel = False - - if font is not None: - self.set_font(font) - - # Allow raw_input support: - self.input_loop = None - self.input_mode = False - - # KeyboardInterrupt support - self.interrupted = False # used only for not-multithreaded mode - self.connect(self, SIGNAL("keyboard_interrupt()"), - self.keyboard_interrupt) - - # Code completion / calltips - getcfg = lambda option: CONF.get('internal_console', option) - case_sensitive = getcfg('codecompletion/case_sensitive') - self.set_codecompletion_case(case_sensitive) - - # keyboard events management - self.eventqueue = [] - - # Init interpreter - self.exitfunc = exitfunc - self.commands = commands - self.message = message - self.interpreter = None - self.start_interpreter(namespace) - - # Clear status bar - self.emit(SIGNAL("status(QString)"), '') - - # Embedded shell -- requires the monitor (which installs the - # 'open_in_spyder' function in builtins) - if hasattr(builtins, 'open_in_spyder'): - self.connect(self, SIGNAL("go_to_error(QString)"), - self.open_with_external_spyder) - - - #------ Interpreter - def start_interpreter(self, namespace): - """Start Python interpreter""" - self.clear() - - if self.interpreter is not None: - self.interpreter.closing() - self.interpreter = Interpreter(namespace, self.exitfunc, - SysOutput, WidgetProxy, DEBUG) - self.connect(self.interpreter.stdout_write, - SIGNAL("void data_avail()"), self.stdout_avail) - self.connect(self.interpreter.stderr_write, - SIGNAL("void data_avail()"), self.stderr_avail) - self.connect(self.interpreter.widget_proxy, - SIGNAL("set_readonly(bool)"), self.setReadOnly) - self.connect(self.interpreter.widget_proxy, - SIGNAL("new_prompt(QString)"), self.new_prompt) - self.connect(self.interpreter.widget_proxy, - SIGNAL("edit(QString,bool)"), self.edit_script) - self.connect(self.interpreter.widget_proxy, - SIGNAL("wait_input(QString)"), self.wait_input) - if self.multithreaded: - self.interpreter.start() - - # Interpreter banner - banner = create_banner(self.message) - self.write(banner, prompt=True) - - # Initial commands - for cmd in self.commands: - self.run_command(cmd, history=False, new_prompt=False) - - # First prompt - self.new_prompt(self.interpreter.p1) - self.emit(SIGNAL("refresh()")) - - return self.interpreter - - def exit_interpreter(self): - """Exit interpreter""" - self.interpreter.exit_flag = True - if self.multithreaded: - self.interpreter.stdin_write.write(to_binary_string('\n')) - self.interpreter.restore_stds() - - def edit_script(self, filename, external_editor): - filename = to_text_string(filename) - if external_editor: - self.external_editor(filename) - else: - self.parent().edit_script(filename) - - def stdout_avail(self): - """Data is available in stdout, let's empty the queue and write it!""" - data = self.interpreter.stdout_write.empty_queue() - if data: - self.write(data) - - def stderr_avail(self): - """Data is available in stderr, let's empty the queue and write it!""" - data = self.interpreter.stderr_write.empty_queue() - if data: - self.write(data, error=True) - self.flush(error=True) - - - #------Raw input support - def wait_input(self, prompt=''): - """Wait for input (raw_input support)""" - self.new_prompt(prompt) - self.setFocus() - self.input_mode = True - self.input_loop = QEventLoop() - self.input_loop.exec_() - self.input_loop = None - - def end_input(self, cmd): - """End of wait_input mode""" - self.input_mode = False - self.input_loop.exit() - self.interpreter.widget_proxy.end_input(cmd) - - - #----- Menus, actions, ... - def setup_context_menu(self): - """Reimplement PythonShellWidget method""" - PythonShellWidget.setup_context_menu(self) - self.help_action = create_action(self, _("Help..."), - icon=get_std_icon('DialogHelpButton'), - triggered=self.help) - self.menu.addAction(self.help_action) - - def help(self): - """Help on Spyder console""" - QMessageBox.about(self, _("Help"), - """%s -

    %s
    edit foobar.py -

    %s
    xedit foobar.py -

    %s
    run foobar.py -

    %s
    clear x, y -

    %s
    !ls -

    %s
    object? -

    %s
    result = oedit(object) - """ % (_('Shell special commands:'), - _('Internal editor:'), - _('External editor:'), - _('Run script:'), - _('Remove references:'), - _('System commands:'), - _('Python help:'), - _('GUI-based editor:'))) - - - #------ External editing - def open_with_external_spyder(self, text): - """Load file in external Spyder's editor, if available - This method is used only for embedded consoles - (could also be useful if we ever implement the magic %edit command)""" - match = get_error_match(to_text_string(text)) - if match: - fname, lnb = match.groups() - builtins.open_in_spyder(fname, int(lnb)) - - def external_editor(self, filename, goto=-1): - """Edit in an external editor - Recommended: SciTE (e.g. to go to line where an error did occur)""" - editor_path = CONF.get('internal_console', 'external_editor/path') - goto_option = CONF.get('internal_console', 'external_editor/gotoline') - try: - if goto > 0 and goto_option: - Popen(r'%s "%s" %s%d' % (editor_path, filename, - goto_option, goto)) - else: - Popen(r'%s "%s"' % (editor_path, filename)) - except OSError: - self.write_error("External editor was not found:" - " %s\n" % editor_path) - - - #------ I/O - def flush(self, error=False, prompt=False): - """Reimplement ShellBaseWidget method""" - PythonShellWidget.flush(self, error=error, prompt=prompt) - if self.interrupted: - self.interrupted = False - raise KeyboardInterrupt - - - #------ Clear terminal - def clear_terminal(self): - """Reimplement ShellBaseWidget method""" - self.clear() - self.new_prompt(self.interpreter.p2 if self.interpreter.more else self.interpreter.p1) - - - #------ Keyboard events - def on_enter(self, command): - """on_enter""" - if self.profile: - # Simple profiling test - t0 = time() - for _ in range(10): - self.execute_command(command) - self.insert_text(u("\n<Δt>=%dms\n") % (1e2*(time()-t0))) - self.new_prompt(self.interpreter.p1) - else: - self.execute_command(command) - self.__flush_eventqueue() - - def keyPressEvent(self, event): - """ - Reimplement Qt Method - Enhanced keypress event handler - """ - if self.preprocess_keyevent(event): - # Event was accepted in self.preprocess_keyevent - return - self.postprocess_keyevent(event) - - def __flush_eventqueue(self): - """Flush keyboard event queue""" - while self.eventqueue: - past_event = self.eventqueue.pop(0) - self.postprocess_keyevent(past_event) - - #------ Command execution - def keyboard_interrupt(self): - """Simulate keyboard interrupt""" - if self.multithreaded: - self.interpreter.raise_keyboard_interrupt() - else: - if self.interpreter.more: - self.write_error("\nKeyboardInterrupt\n") - self.interpreter.more = False - self.new_prompt(self.interpreter.p1) - self.interpreter.resetbuffer() - else: - self.interrupted = True - - def execute_lines(self, lines): - """ - Execute a set of lines as multiple command - lines: multiple lines of text to be executed as single commands - """ - for line in lines.splitlines(): - stripped_line = line.strip() - if stripped_line.startswith('#'): - continue - self.write(line+os.linesep, flush=True) - self.execute_command(line+"\n") - self.flush() - - def execute_command(self, cmd): - """ - Execute a command - cmd: one-line command only, with '\n' at the end - """ - if self.input_mode: - self.end_input(cmd) - return - if cmd.endswith('\n'): - cmd = cmd[:-1] - # cls command - if cmd == 'cls': - self.clear_terminal() - return - self.run_command(cmd) - - def run_command(self, cmd, history=True, new_prompt=True): - """Run command in interpreter""" - if not cmd: - cmd = '' - else: - if history: - self.add_to_history(cmd) - self.interpreter.stdin_write.write(to_binary_string(cmd + '\n')) - if not self.multithreaded: - self.interpreter.run_line() - self.emit(SIGNAL("refresh()")) - - - #------ Code completion / Calltips - def _eval(self, text): - """Is text a valid object?""" - return self.interpreter.eval(text) - - def get_dir(self, objtxt): - """Return dir(object)""" - obj, valid = self._eval(objtxt) - if valid: - return getobjdir(obj) - - def get_globals_keys(self): - """Return shell globals() keys""" - return list(self.interpreter.namespace.keys()) - - def get_cdlistdir(self): - """Return shell current directory list dir""" - return os.listdir(getcwd()) - - def iscallable(self, objtxt): - """Is object callable?""" - obj, valid = self._eval(objtxt) - if valid: - return callable(obj) - - def get_arglist(self, objtxt): - """Get func/method argument list""" - obj, valid = self._eval(objtxt) - if valid: - return getargtxt(obj) - - def get__doc__(self, objtxt): - """Get object __doc__""" - obj, valid = self._eval(objtxt) - if valid: - return obj.__doc__ - - def get_doc(self, objtxt): - """Get object documentation dictionary""" - obj, valid = self._eval(objtxt) - if valid: - return getdoc(obj) - - def get_source(self, objtxt): - """Get object source""" - obj, valid = self._eval(objtxt) - if valid: - return getsource(obj) - - def is_defined(self, objtxt, force_import=False): - """Return True if object is defined""" - return self.interpreter.is_defined(objtxt, force_import) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/ipython.py spyder-3.0.2+dfsg1/spyderlib/widgets/ipython.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/ipython.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/ipython.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,748 +0,0 @@ -# -*- coding:utf-8 -*- -# -# Copyright © 2011-2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -IPython v0.13+ client's widget -""" -# Fix for Issue 1356 -from __future__ import absolute_import - -# Stdlib imports -import os -import os.path as osp -import re -from string import Template -import sys -import time - -# Qt imports -from spyderlib.qt.QtGui import (QTextEdit, QKeySequence, QWidget, QMenu, - QHBoxLayout, QToolButton, QVBoxLayout, - QMessageBox) -from spyderlib.qt.QtCore import SIGNAL, Qt - -from spyderlib import pygments_patch -pygments_patch.apply() - -# IPython imports -try: - from qtconsole.rich_jupyter_widget import RichJupyterWidget as RichIPythonWidget -except ImportError: - from IPython.qt.console.rich_ipython_widget import RichIPythonWidget -from IPython.qt.console.ansi_code_processor import ANSI_OR_SPECIAL_PATTERN -from IPython.core.application import get_ipython_dir -from IPython.core.oinspect import call_tip -from IPython.config.loader import Config, load_pyconfig_files - -# Local imports -from spyderlib.baseconfig import (get_conf_path, get_image_path, - get_module_source_path, _) -from spyderlib.config import CONF -from spyderlib.guiconfig import (create_shortcut, get_font, get_shortcut, - new_shortcut) -from spyderlib.utils.dochelpers import getargspecfromtext, getsignaturefromtext -from spyderlib.utils.qthelpers import (get_std_icon, create_toolbutton, - add_actions, create_action, get_icon, - restore_keyevent) -from spyderlib.utils import programs, sourcecode -from spyderlib.widgets.browser import WebView -from spyderlib.widgets.calltip import CallTipWidget -from spyderlib.widgets.mixins import (BaseEditMixin, InspectObjectMixin, - SaveHistoryMixin, TracebackLinksMixin) -from spyderlib.py3compat import PY3 - - -#----------------------------------------------------------------------------- -# Templates -#----------------------------------------------------------------------------- -# Using the same css file from the Object Inspector for now. Maybe -# later it'll be a good idea to create a new one. -UTILS_PATH = get_module_source_path('spyderlib', 'utils') -CSS_PATH = osp.join(UTILS_PATH, 'inspector', 'static', 'css') -TEMPLATES_PATH = osp.join(UTILS_PATH, 'ipython', 'templates') - -BLANK = open(osp.join(TEMPLATES_PATH, 'blank.html')).read() -LOADING = open(osp.join(TEMPLATES_PATH, 'loading.html')).read() -KERNEL_ERROR = open(osp.join(TEMPLATES_PATH, 'kernel_error.html')).read() - -#----------------------------------------------------------------------------- -# Control widgets -#----------------------------------------------------------------------------- -class IPythonControlWidget(TracebackLinksMixin, InspectObjectMixin, QTextEdit, - BaseEditMixin): - """ - Subclass of QTextEdit with features from Spyder's mixins to use as the - control widget for IPython widgets - """ - QT_CLASS = QTextEdit - def __init__(self, parent=None): - QTextEdit.__init__(self, parent) - BaseEditMixin.__init__(self) - TracebackLinksMixin.__init__(self) - InspectObjectMixin.__init__(self) - - self.calltip_widget = CallTipWidget(self, hide_timer_on=True) - self.found_results = [] - - # To not use Spyder calltips obtained through the monitor - self.calltips = False - - def showEvent(self, event): - """Reimplement Qt Method""" - self.emit(SIGNAL("visibility_changed(bool)"), True) - - def _key_question(self, text): - """ Action for '?' and '(' """ - self.current_prompt_pos = self.parentWidget()._prompt_pos - if self.get_current_line_to_cursor(): - last_obj = self.get_last_obj() - if last_obj and not last_obj.isdigit(): - self.show_object_info(last_obj) - self.insert_text(text) - - def keyPressEvent(self, event): - """Reimplement Qt Method - Basic keypress event handler""" - event, text, key, ctrl, shift = restore_keyevent(event) - if key == Qt.Key_Question and not self.has_selected_text(): - self._key_question(text) - elif key == Qt.Key_ParenLeft and not self.has_selected_text(): - self._key_question(text) - else: - # Let the parent widget handle the key press event - QTextEdit.keyPressEvent(self, event) - - def focusInEvent(self, event): - """Reimplement Qt method to send focus change notification""" - self.emit(SIGNAL('focus_changed()')) - return super(IPythonControlWidget, self).focusInEvent(event) - - def focusOutEvent(self, event): - """Reimplement Qt method to send focus change notification""" - self.emit(SIGNAL('focus_changed()')) - return super(IPythonControlWidget, self).focusOutEvent(event) - - -class IPythonPageControlWidget(QTextEdit, BaseEditMixin): - """ - Subclass of QTextEdit with features from Spyder's mixins.BaseEditMixin to - use as the paging widget for IPython widgets - """ - QT_CLASS = QTextEdit - def __init__(self, parent=None): - QTextEdit.__init__(self, parent) - BaseEditMixin.__init__(self) - self.found_results = [] - - def showEvent(self, event): - """Reimplement Qt Method""" - self.emit(SIGNAL("visibility_changed(bool)"), True) - - def keyPressEvent(self, event): - """Reimplement Qt Method - Basic keypress event handler""" - event, text, key, ctrl, shift = restore_keyevent(event) - - if key == Qt.Key_Slash and self.isVisible(): - self.emit(SIGNAL("show_find_widget()")) - - def focusInEvent(self, event): - """Reimplement Qt method to send focus change notification""" - self.emit(SIGNAL('focus_changed()')) - return super(IPythonPageControlWidget, self).focusInEvent(event) - - def focusOutEvent(self, event): - """Reimplement Qt method to send focus change notification""" - self.emit(SIGNAL('focus_changed()')) - return super(IPythonPageControlWidget, self).focusOutEvent(event) - - -#----------------------------------------------------------------------------- -# Shell widget -#----------------------------------------------------------------------------- -class IPythonShellWidget(RichIPythonWidget): - """ - Spyder's IPython shell widget - - This class has custom control and page_control widgets, additional methods - to provide missing functionality and a couple more keyboard shortcuts. - """ - def __init__(self, *args, **kw): - # To override the Qt widget used by RichIPythonWidget - self.custom_control = IPythonControlWidget - self.custom_page_control = IPythonPageControlWidget - super(IPythonShellWidget, self).__init__(*args, **kw) - self.set_background_color() - - # --- Spyder variables --- - self.ipyclient = None - - # --- Keyboard shortcuts --- - self.shortcuts = self.create_shortcuts() - - # --- IPython variables --- - # To send an interrupt signal to the Spyder kernel - self.custom_interrupt = True - - # To restart the Spyder kernel in case it dies - self.custom_restart = True - - #---- Public API ---------------------------------------------------------- - def set_ipyclient(self, ipyclient): - """Bind this shell widget to an IPython client one""" - self.ipyclient = ipyclient - self.exit_requested.connect(ipyclient.exit_callback) - - def long_banner(self): - """Banner for IPython widgets with pylab message""" - from IPython.core.usage import default_gui_banner - banner = default_gui_banner - - pylab_o = CONF.get('ipython_console', 'pylab', True) - autoload_pylab_o = CONF.get('ipython_console', 'pylab/autoload', True) - mpl_installed = programs.is_module_installed('matplotlib') - if mpl_installed and (pylab_o and autoload_pylab_o): - pylab_message = ("\nPopulating the interactive namespace from " - "numpy and matplotlib") - banner = banner + pylab_message - - sympy_o = CONF.get('ipython_console', 'symbolic_math', True) - if sympy_o: - lines = """ -These commands were executed: ->>> from __future__ import division ->>> from sympy import * ->>> x, y, z, t = symbols('x y z t') ->>> k, m, n = symbols('k m n', integer=True) ->>> f, g, h = symbols('f g h', cls=Function) -""" - banner = banner + lines - return banner - - def short_banner(self): - """Short banner with Python and IPython versions""" - from IPython.core.release import version - py_ver = '%d.%d.%d' % (sys.version_info[0], sys.version_info[1], - sys.version_info[2]) - banner = 'Python %s on %s -- IPython %s' % (py_ver, sys.platform, - version) - return banner - - def clear_console(self): - self.execute("%clear") - - def write_to_stdin(self, line): - """Send raw characters to the IPython kernel through stdin""" - try: - self.kernel_client.stdin_channel.input(line) - except AttributeError: - self.kernel_client.input(line) - - def set_background_color(self): - lightbg_o = CONF.get('ipython_console', 'light_color') - if not lightbg_o: - self.set_default_style(colors='linux') - - def create_shortcuts(self): - inspect = create_shortcut(self._control.inspect_current_object, - context='Console', name='Inspect current object', - parent=self) - clear_console = create_shortcut(self.clear_console, context='Console', - name='Clear shell', parent=self) - - # Fixed shortcuts - new_shortcut("Ctrl+T", self, - lambda: self.emit(SIGNAL("new_ipyclient()"))) - - return [inspect, clear_console] - - def clean_invalid_var_chars(self, var): - """ - Replace invalid variable chars in a string by underscores - - Taken from http://stackoverflow.com/a/3305731/438386 - """ - if PY3: - return re.sub('\W|^(?=\d)', '_', var, re.UNICODE) - else: - return re.sub('\W|^(?=\d)', '_', var) - - def get_signature(self, content): - """Get signature from inspect reply content""" - data = content.get('data', {}) - text = data.get('text/plain', '') - if text: - text = ANSI_OR_SPECIAL_PATTERN.sub('', text) - self._control.current_prompt_pos = self._prompt_pos - line = self._control.get_current_line_to_cursor() - name = line[:-1].split('(')[-1] # Take last token after a ( - name = name.split('.')[-1] # Then take last token after a . - # Clean name from invalid chars - try: - name = self.clean_invalid_var_chars(name).split('_')[-1] - except: - pass - argspec = getargspecfromtext(text) - if argspec: - # This covers cases like np.abs, whose docstring is - # the same as np.absolute and because of that a proper - # signature can't be obtained correctly - signature = name + argspec - else: - signature = getsignaturefromtext(text, name) - return signature - else: - return '' - - #---- IPython private methods --------------------------------------------- - def _context_menu_make(self, pos): - """Reimplement the IPython context menu""" - menu = super(IPythonShellWidget, self)._context_menu_make(pos) - return self.ipyclient.add_actions_to_context_menu(menu) - - def _banner_default(self): - """ - Reimplement banner creation to let the user decide if he wants a - banner or not - """ - banner_o = CONF.get('ipython_console', 'show_banner', True) - if banner_o: - return self.long_banner() - else: - return self.short_banner() - - def _handle_object_info_reply(self, rep): - """ - Reimplement call tips to only show signatures, using the same style - from our Editor and External Console too - Note: For IPython 2- - """ - self.log.debug("oinfo: %s", rep.get('content', '')) - cursor = self._get_cursor() - info = self._request_info.get('call_tip') - if info and info.id == rep['parent_header']['msg_id'] and \ - info.pos == cursor.position(): - content = rep['content'] - if content.get('ismagic', False): - call_info, doc = None, None - else: - call_info, doc = call_tip(content, format_call=True) - if call_info is None and doc is not None: - name = content['name'].split('.')[-1] - argspec = getargspecfromtext(doc) - if argspec: - # This covers cases like np.abs, whose docstring is - # the same as np.absolute and because of that a proper - # signature can't be obtained correctly - call_info = name + argspec - else: - call_info = getsignaturefromtext(doc, name) - if call_info: - self._control.show_calltip(_("Arguments"), call_info, - signature=True, color='#2D62FF') - - def _handle_inspect_reply(self, rep): - """ - Reimplement call tips to only show signatures, using the same style - from our Editor and External Console too - Note: For IPython 3+ - """ - cursor = self._get_cursor() - info = self._request_info.get('call_tip') - if info and info.id == rep['parent_header']['msg_id'] and \ - info.pos == cursor.position(): - content = rep['content'] - if content.get('status') == 'ok' and content.get('found', False): - signature = self.get_signature(content) - if signature: - self._control.show_calltip(_("Arguments"), signature, - signature=True, color='#2D62FF') - - #---- Qt methods ---------------------------------------------------------- - def focusInEvent(self, event): - """Reimplement Qt method to send focus change notification""" - self.emit(SIGNAL('focus_changed()')) - return super(IPythonShellWidget, self).focusInEvent(event) - - def focusOutEvent(self, event): - """Reimplement Qt method to send focus change notification""" - self.emit(SIGNAL('focus_changed()')) - return super(IPythonShellWidget, self).focusOutEvent(event) - - -#----------------------------------------------------------------------------- -# Client widget -#----------------------------------------------------------------------------- -class IPythonClient(QWidget, SaveHistoryMixin): - """ - IPython client or frontend for Spyder - - This is a widget composed of a shell widget (i.e. RichIPythonWidget - + our additions = IPythonShellWidget) and an WebView info widget to - print kernel error and other messages. - """ - - SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime()) - - def __init__(self, plugin, name, history_filename, connection_file=None, - hostname=None, sshkey=None, password=None, - kernel_widget_id=None, menu_actions=None): - super(IPythonClient, self).__init__(plugin) - SaveHistoryMixin.__init__(self) - self.options_button = None - - # stop button and icon - self.stop_button = None - self.stop_icon = get_icon("stop.png") - - self.connection_file = connection_file - self.kernel_widget_id = kernel_widget_id - self.hostname = hostname - self.sshkey = sshkey - self.password = password - self.name = name - self.get_option = plugin.get_option - self.shellwidget = IPythonShellWidget(config=self.shellwidget_config(), - local_kernel=False) - self.shellwidget.hide() - self.infowidget = WebView(self) - self.menu_actions = menu_actions - self.history_filename = get_conf_path(history_filename) - self.history = [] - self.namespacebrowser = None - - self.set_infowidget_font() - self.loading_page = self._create_loading_page() - self.infowidget.setHtml(self.loading_page) - - vlayout = QVBoxLayout() - toolbar_buttons = self.get_toolbar_buttons() - hlayout = QHBoxLayout() - for button in toolbar_buttons: - hlayout.addWidget(button) - vlayout.addLayout(hlayout) - vlayout.setContentsMargins(0, 0, 0, 0) - vlayout.addWidget(self.shellwidget) - vlayout.addWidget(self.infowidget) - self.setLayout(vlayout) - - self.exit_callback = lambda: plugin.close_client(client=self) - - #------ Public API -------------------------------------------------------- - def show_shellwidget(self, give_focus=True): - """Show shellwidget and configure it""" - self.infowidget.hide() - self.shellwidget.show() - self.infowidget.setHtml(BLANK) - if give_focus: - self.get_control().setFocus() - - # Connect shellwidget to the client - self.shellwidget.set_ipyclient(self) - - # To save history - self.shellwidget.executing.connect(self.add_to_history) - - # For Mayavi to run correctly - self.shellwidget.executing.connect(self.set_backend_for_mayavi) - - # To update history after execution - self.shellwidget.executed.connect(self.update_history) - - # To update the Variable Explorer after execution - self.shellwidget.executed.connect(self.auto_refresh_namespacebrowser) - - # To show a stop button, when executing a process - self.shellwidget.executing.connect(self.enable_stop_button) - - # To hide a stop button after execution stopped - self.shellwidget.executed.connect(self.disable_stop_button) - - def enable_stop_button(self): - self.stop_button.setEnabled(True) - - def disable_stop_button(self): - self.stop_button.setDisabled(True) - - def stop_button_click_handler(self): - self.stop_button.setDisabled(True) - # Interrupt computations or stop debugging - if not self.shellwidget._reading: - self.interrupt_kernel() - else: - self.shellwidget.write_to_stdin('exit') - - def show_kernel_error(self, error): - """Show kernel initialization errors in infowidget""" - # Remove explanation about how to kill the kernel (doesn't apply to us) - error = error.split('issues/2049')[-1] - # Remove unneeded blank lines at the beginning - eol = sourcecode.get_eol_chars(error) - if eol: - error = error.replace(eol, '
    ') - while error.startswith('
    '): - error = error[4:] - # Remove connection message - if error.startswith('To connect another client') or \ - error.startswith('[IPKernelApp] To connect another client'): - error = error.split('
    ') - error = '
    '.join(error[2:]) - # Don't break lines in hyphens - # From http://stackoverflow.com/q/7691569/438386 - error = error.replace('-', '‑') - - message = _("An error ocurred while starting the kernel") - kernel_error_template = Template(KERNEL_ERROR) - page = kernel_error_template.substitute(css_path=CSS_PATH, - message=message, - error=error) - self.infowidget.setHtml(page) - - def show_restart_animation(self): - self.shellwidget.hide() - self.infowidget.setHtml(self.loading_page) - self.infowidget.show() - - def get_name(self): - """Return client name""" - return ((_("Console") if self.hostname is None else self.hostname) - + " " + self.name) - - def get_control(self): - """Return the text widget (or similar) to give focus to""" - # page_control is the widget used for paging - page_control = self.shellwidget._page_control - if page_control and page_control.isVisible(): - return page_control - else: - return self.shellwidget._control - - def get_options_menu(self): - """Return options menu""" - restart_action = create_action(self, _("Restart kernel"), - shortcut=QKeySequence("Ctrl+."), - icon=get_icon('restart.png'), - triggered=self.restart_kernel) - restart_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) - - # Main menu - if self.menu_actions is not None: - actions = [restart_action, None] + self.menu_actions - else: - actions = [restart_action] - return actions - - def get_toolbar_buttons(self): - """Return toolbar buttons list""" - #TODO: Eventually add some buttons (Empty for now) - # (see for example: spyderlib/widgets/externalshell/baseshell.py) - buttons = [] - # Code to add the stop button - if self.stop_button is None: - self.stop_button = create_toolbutton(self, text=_("Stop"), - icon=self.stop_icon, - tip=_("Stop the current command")) - self.disable_stop_button() - # set click event handler - self.stop_button.clicked.connect(self.stop_button_click_handler) - if self.stop_button is not None: - buttons.append(self.stop_button) - - if self.options_button is None: - options = self.get_options_menu() - if options: - self.options_button = create_toolbutton(self, - text=_("Options"), icon=get_icon('tooloptions.png')) - self.options_button.setPopupMode(QToolButton.InstantPopup) - menu = QMenu(self) - add_actions(menu, options) - self.options_button.setMenu(menu) - if self.options_button is not None: - buttons.append(self.options_button) - - return buttons - - def add_actions_to_context_menu(self, menu): - """Add actions to IPython widget context menu""" - # See spyderlib/widgets/ipython.py for more details on this method - inspect_action = create_action(self, _("Inspect current object"), - QKeySequence(get_shortcut('console', - 'inspect current object')), - icon=get_std_icon('MessageBoxInformation'), - triggered=self.inspect_object) - clear_line_action = create_action(self, _("Clear line or block"), - QKeySequence("Shift+Escape"), - icon=get_icon('eraser.png'), - triggered=self.clear_line) - clear_console_action = create_action(self, _("Clear console"), - QKeySequence(get_shortcut('console', - 'clear shell')), - icon=get_icon('clear.png'), - triggered=self.clear_console) - quit_action = create_action(self, _("&Quit"), icon='exit.png', - triggered=self.exit_callback) - add_actions(menu, (None, inspect_action, clear_line_action, - clear_console_action, None, quit_action)) - return menu - - def set_font(self, font): - """Set IPython widget's font""" - self.shellwidget._control.setFont(font) - self.shellwidget.font = font - - def set_infowidget_font(self): - font = get_font('inspector', 'rich_text') - self.infowidget.set_font(font) - - def interrupt_kernel(self): - """Interrupt the associanted Spyder kernel if it's running""" - self.shellwidget.request_interrupt_kernel() - - def restart_kernel(self): - """Restart the associanted Spyder kernel""" - self.shellwidget.request_restart_kernel() - - def inspect_object(self): - """Show how to inspect an object with our object inspector""" - self.shellwidget._control.inspect_current_object() - - def clear_line(self): - """Clear a console line""" - self.shellwidget._keyboard_quit() - - def clear_console(self): - """Clear the whole console""" - self.shellwidget.execute("%clear") - - def if_kernel_dies(self, t): - """ - Show a message in the console if the kernel dies. - t is the time in seconds between the death and showing the message. - """ - message = _("It seems the kernel died unexpectedly. Use " - "'Restart kernel' to continue using this console.") - self.shellwidget._append_plain_text(message + '\n') - - def update_history(self): - self.history = self.shellwidget._history - - def set_backend_for_mayavi(self, command): - calling_mayavi = False - lines = command.splitlines() - for l in lines: - if not l.startswith('#'): - if 'import mayavi' in l or 'from mayavi' in l: - calling_mayavi = True - break - if calling_mayavi: - message = _("Changing backend to Qt for Mayavi") - self.shellwidget._append_plain_text(message + '\n') - self.shellwidget.execute("%gui inline\n%gui qt") - - def interrupt_message(self): - """ - Print an interrupt message when the client is connected to an external - kernel - """ - message = _("Kernel process is either remote or unspecified. " - "Cannot interrupt") - QMessageBox.information(self, "IPython", message) - - def restart_message(self): - """ - Print a restart message when the client is connected to an external - kernel - """ - message = _("Kernel process is either remote or unspecified. " - "Cannot restart.") - QMessageBox.information(self, "IPython", message) - - def set_namespacebrowser(self, namespacebrowser): - """Set namespace browser widget""" - self.namespacebrowser = namespacebrowser - - def auto_refresh_namespacebrowser(self): - """Refresh namespace browser""" - if self.namespacebrowser: - self.namespacebrowser.refresh_table() - - def shellwidget_config(self): - """Generate a Config instance for shell widgets using our config - system - - This lets us create each widget with its own config (as opposed to - IPythonQtConsoleApp, where all widgets have the same config) - """ - # ---- IPython config ---- - try: - profile_path = osp.join(get_ipython_dir(), 'profile_default') - full_ip_cfg = load_pyconfig_files(['ipython_qtconsole_config.py'], - profile_path) - - # From the full config we only select the IPythonWidget section - # because the others have no effect here. - ip_cfg = Config({'IPythonWidget': full_ip_cfg.IPythonWidget}) - except: - ip_cfg = Config() - - # ---- Spyder config ---- - spy_cfg = Config() - - # Make the pager widget a rich one (i.e a QTextEdit) - spy_cfg.IPythonWidget.kind = 'rich' - - # Gui completion widget - gui_comp_o = self.get_option('use_gui_completion') - completions = {True: 'droplist', False: 'ncurses'} - spy_cfg.IPythonWidget.gui_completion = completions[gui_comp_o] - - # Pager - pager_o = self.get_option('use_pager') - if pager_o: - spy_cfg.IPythonWidget.paging = 'inside' - else: - spy_cfg.IPythonWidget.paging = 'none' - - # Calltips - calltips_o = self.get_option('show_calltips') - spy_cfg.IPythonWidget.enable_calltips = calltips_o - - # Buffer size - buffer_size_o = self.get_option('buffer_size') - spy_cfg.IPythonWidget.buffer_size = buffer_size_o - - # Prompts - in_prompt_o = self.get_option('in_prompt') - out_prompt_o = self.get_option('out_prompt') - if in_prompt_o: - spy_cfg.IPythonWidget.in_prompt = in_prompt_o - if out_prompt_o: - spy_cfg.IPythonWidget.out_prompt = out_prompt_o - - # Merge IPython and Spyder configs. Spyder prefs will have prevalence - # over IPython ones - ip_cfg._merge(spy_cfg) - return ip_cfg - - #------ Private API ------------------------------------------------------- - def _create_loading_page(self): - loading_template = Template(LOADING) - loading_img = get_image_path('loading_sprites.png') - if os.name == 'nt': - loading_img = loading_img.replace('\\', '/') - message = _("Connecting to kernel...") - page = loading_template.substitute(css_path=CSS_PATH, - loading_img=loading_img, - message=message) - return page - - #---- Qt methods ---------------------------------------------------------- - def closeEvent(self, event): - """ - Reimplement Qt method to stop sending the custom_restart_kernel_died - signal - """ - kc = self.shellwidget.kernel_client - if kc is not None: - kc.hb_channel.pause() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/mixins.py spyder-3.0.2+dfsg1/spyderlib/widgets/mixins.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/mixins.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/mixins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,647 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Mix-in classes - -These classes were created to be able to provide Spyder's regular text and -console widget features to an independant widget based on QTextEdit for the -IPython console plugin. -""" - -import os -import re -import sre_constants -import textwrap -from xml.sax.saxutils import escape - -from spyderlib.qt.QtGui import (QTextCursor, QTextDocument, QApplication, - QCursor, QToolTip) -from spyderlib.qt.QtCore import Qt, QPoint, QRegExp, SIGNAL - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.utils import encoding, sourcecode -from spyderlib.utils.misc import get_error_match -from spyderlib.utils.dochelpers import (getobj, getargspecfromtext, - getsignaturefromtext) -from spyderlib.py3compat import is_text_string, to_text_string, u - - -HISTORY_FILENAMES = [] - - -class BaseEditMixin(object): - def __init__(self): - self.eol_chars = None - self.calltip_size = 600 - - - #------Line number area - def get_linenumberarea_width(self): - """Return line number area width""" - # Implemented in CodeEditor, but needed for calltip/completion widgets - return 0 - - - #------Calltips - def _format_signature(self, text): - formatted_lines = [] - name = text.split('(')[0] - rows = textwrap.wrap(text, width=50, - subsequent_indent=' '*(len(name)+1)) - for r in rows: - r = escape(r) # Escape most common html chars - r = r.replace(' ', ' ') - for char in ['=', ',', '(', ')', '*', '**']: - r = r.replace(char, - '' + \ - char + '') - formatted_lines.append(r) - signature = '
    '.join(formatted_lines) - return signature, rows - - def show_calltip(self, title, text, signature=False, color='#2D62FF', - at_line=None, at_position=None): - """Show calltip""" - if text is None or len(text) == 0: - return - - # Saving cursor position: - if at_position is None: - at_position = self.get_position('cursor') - self.calltip_position = at_position - - # Preparing text: - if signature: - text, wrapped_textlines = self._format_signature(text) - else: - if isinstance(text, list): - text = "\n ".join(text) - text = text.replace('\n', '
    ') - if len(text) > self.calltip_size: - text = text[:self.calltip_size] + " ..." - - # Formatting text - font = self.font() - size = font.pointSize() - family = font.family() - format1 = '

    '\ - % (family, size, color) - format2 = '
    '\ - % (family, size-1 if size > 9 else size) - tiptext = format1 + ('%s
    ' % title) + '
    ' + \ - format2 + text + "
    " - - # Showing tooltip at cursor position: - cx, cy = self.get_coordinates('cursor') - if at_line is not None: - cx = 5 - cursor = QTextCursor(self.document().findBlockByNumber(at_line-1)) - cy = self.cursorRect(cursor).top() - point = self.mapToGlobal(QPoint(cx, cy)) - point.setX(point.x()+self.get_linenumberarea_width()) - point.setY(point.y()+font.pointSize()+5) - if signature: - self.calltip_widget.show_tip(point, tiptext, wrapped_textlines) - else: - QToolTip.showText(point, tiptext) - - - #------EOL characters - def set_eol_chars(self, text): - """Set widget end-of-line (EOL) characters from text (analyzes text)""" - if not is_text_string(text): # testing for QString (PyQt API#1) - text = to_text_string(text) - eol_chars = sourcecode.get_eol_chars(text) - if eol_chars is not None and self.eol_chars is not None: - self.document().setModified(True) - self.eol_chars = eol_chars - - def get_line_separator(self): - """Return line separator based on current EOL mode""" - if self.eol_chars is not None: - return self.eol_chars - else: - return os.linesep - - def get_text_with_eol(self): - """Same as 'toPlainText', replace '\n' - by correct end-of-line characters""" - utext = to_text_string(self.toPlainText()) - lines = utext.splitlines() - linesep = self.get_line_separator() - txt = linesep.join(lines) - if utext.endswith('\n'): - txt += linesep - return txt - - - #------Positions, coordinates (cursor, EOF, ...) - def get_position(self, subject): - """Get offset in character for the given subject from the start of - text edit area""" - cursor = self.textCursor() - if subject == 'cursor': - pass - elif subject == 'sol': - cursor.movePosition(QTextCursor.StartOfBlock) - elif subject == 'eol': - cursor.movePosition(QTextCursor.EndOfBlock) - elif subject == 'eof': - cursor.movePosition(QTextCursor.End) - elif subject == 'sof': - cursor.movePosition(QTextCursor.Start) - else: - # Assuming that input argument was already a position - return subject - return cursor.position() - - def get_coordinates(self, position): - position = self.get_position(position) - cursor = self.textCursor() - cursor.setPosition(position) - point = self.cursorRect(cursor).center() - return point.x(), point.y() - - def get_cursor_line_column(self): - """Return cursor (line, column) numbers""" - cursor = self.textCursor() - return cursor.blockNumber(), cursor.columnNumber() - - def get_cursor_line_number(self): - """Return cursor line number""" - return self.textCursor().blockNumber()+1 - - def set_cursor_position(self, position): - """Set cursor position""" - position = self.get_position(position) - cursor = self.textCursor() - cursor.setPosition(position) - self.setTextCursor(cursor) - self.ensureCursorVisible() - - def move_cursor(self, chars=0): - """Move cursor to left or right (unit: characters)""" - direction = QTextCursor.Right if chars > 0 else QTextCursor.Left - for _i in range(abs(chars)): - self.moveCursor(direction, QTextCursor.MoveAnchor) - - def is_cursor_on_first_line(self): - """Return True if cursor is on the first line""" - cursor = self.textCursor() - cursor.movePosition(QTextCursor.StartOfBlock) - return cursor.atStart() - - def is_cursor_on_last_line(self): - """Return True if cursor is on the last line""" - cursor = self.textCursor() - cursor.movePosition(QTextCursor.EndOfBlock) - return cursor.atEnd() - - def is_cursor_at_end(self): - """Return True if cursor is at the end of the text""" - return self.textCursor().atEnd() - - def is_cursor_before(self, position, char_offset=0): - """Return True if cursor is before *position*""" - position = self.get_position(position) + char_offset - cursor = self.textCursor() - cursor.movePosition(QTextCursor.End) - if position < cursor.position(): - cursor.setPosition(position) - return self.textCursor() < cursor - - def __move_cursor_anchor(self, what, direction, move_mode): - assert what in ('character', 'word', 'line') - if what == 'character': - if direction == 'left': - self.moveCursor(QTextCursor.PreviousCharacter, move_mode) - elif direction == 'right': - self.moveCursor(QTextCursor.NextCharacter, move_mode) - elif what == 'word': - if direction == 'left': - self.moveCursor(QTextCursor.PreviousWord, move_mode) - elif direction == 'right': - self.moveCursor(QTextCursor.NextWord, move_mode) - elif what == 'line': - if direction == 'down': - self.moveCursor(QTextCursor.NextBlock, move_mode) - elif direction == 'up': - self.moveCursor(QTextCursor.PreviousBlock, move_mode) - - def move_cursor_to_next(self, what='word', direction='left'): - """ - Move cursor to next *what* ('word' or 'character') - toward *direction* ('left' or 'right') - """ - self.__move_cursor_anchor(what, direction, QTextCursor.MoveAnchor) - - - #------Selection - def clear_selection(self): - """Clear current selection""" - cursor = self.textCursor() - cursor.clearSelection() - self.setTextCursor(cursor) - - def extend_selection_to_next(self, what='word', direction='left'): - """ - Extend selection to next *what* ('word' or 'character') - toward *direction* ('left' or 'right') - """ - self.__move_cursor_anchor(what, direction, QTextCursor.KeepAnchor) - - - #------Text: get, set, ... - def __select_text(self, position_from, position_to): - position_from = self.get_position(position_from) - position_to = self.get_position(position_to) - cursor = self.textCursor() - cursor.setPosition(position_from) - cursor.setPosition(position_to, QTextCursor.KeepAnchor) - return cursor - - def get_text_line(self, line_nb): - """Return text line at line number *line_nb*""" - # Taking into account the case when a file ends in an empty line, - # since splitlines doesn't return that line as the last element - # TODO: Make this function more efficient - try: - return to_text_string(self.toPlainText()).splitlines()[line_nb] - except IndexError: - return self.get_line_separator() - - def get_text(self, position_from, position_to): - """ - Return text between *position_from* and *position_to* - Positions may be positions or 'sol', 'eol', 'sof', 'eof' or 'cursor' - """ - cursor = self.__select_text(position_from, position_to) - text = to_text_string(cursor.selectedText()) - all_text = position_from == 'sof' and position_to == 'eof' - if text and not all_text: - while text.endswith("\n"): - text = text[:-1] - while text.endswith(u("\u2029")): - text = text[:-1] - return text - - def get_character(self, position): - """Return character at *position*""" - position = self.get_position(position) - cursor = self.textCursor() - cursor.movePosition(QTextCursor.End) - if position < cursor.position(): - cursor.setPosition(position) - cursor.movePosition(QTextCursor.Right, - QTextCursor.KeepAnchor) - return to_text_string(cursor.selectedText()) - else: - return '' - - def insert_text(self, text): - """Insert text at cursor position""" - if not self.isReadOnly(): - self.textCursor().insertText(text) - - def replace_text(self, position_from, position_to, text): - cursor = self.__select_text(position_from, position_to) - cursor.removeSelectedText() - cursor.insertText(text) - - def remove_text(self, position_from, position_to): - cursor = self.__select_text(position_from, position_to) - cursor.removeSelectedText() - - def get_current_word(self): - """Return current word, i.e. word at cursor position""" - cursor = self.textCursor() - - if cursor.hasSelection(): - # Removes the selection and moves the cursor to the left side - # of the selection: this is required to be able to properly - # select the whole word under cursor (otherwise, the same word is - # not selected when the cursor is at the right side of it): - cursor.setPosition(min([cursor.selectionStart(), - cursor.selectionEnd()])) - else: - # Checks if the first character to the right is a white space - # and if not, moves the cursor one word to the left (otherwise, - # if the character to the left do not match the "word regexp" - # (see below), the word to the left of the cursor won't be - # selected), but only if the first character to the left is not a - # white space too. - def is_space(move): - curs = self.textCursor() - curs.movePosition(move, QTextCursor.KeepAnchor) - return not to_text_string(curs.selectedText()).strip() - if is_space(QTextCursor.NextCharacter): - if is_space(QTextCursor.PreviousCharacter): - return - cursor.movePosition(QTextCursor.WordLeft) - - cursor.select(QTextCursor.WordUnderCursor) - text = to_text_string(cursor.selectedText()) - match = re.findall(r'([a-zA-Z\_]+[0-9a-zA-Z\_]*)', text) - if match: - return match[0] - - def get_current_line(self): - """Return current line's text""" - cursor = self.textCursor() - cursor.select(QTextCursor.BlockUnderCursor) - return to_text_string(cursor.selectedText()) - - def get_current_line_to_cursor(self): - """Return text from prompt to cursor""" - return self.get_text(self.current_prompt_pos, 'cursor') - - def get_line_number_at(self, coordinates): - """Return line number at *coordinates* (QPoint)""" - cursor = self.cursorForPosition(coordinates) - return cursor.blockNumber()-1 - - def get_line_at(self, coordinates): - """Return line at *coordinates* (QPoint)""" - cursor = self.cursorForPosition(coordinates) - cursor.select(QTextCursor.BlockUnderCursor) - return to_text_string(cursor.selectedText()).replace(u('\u2029'), '') - - def get_word_at(self, coordinates): - """Return word at *coordinates* (QPoint)""" - cursor = self.cursorForPosition(coordinates) - cursor.select(QTextCursor.WordUnderCursor) - return to_text_string(cursor.selectedText()) - - def get_block_indentation(self, block_nb): - """Return line indentation (character number)""" - text = to_text_string(self.document().findBlockByNumber(block_nb).text()) - return len(text)-len(text.lstrip()) - - def get_selection_bounds(self): - """Return selection bounds (block numbers)""" - cursor = self.textCursor() - start, end = cursor.selectionStart(), cursor.selectionEnd() - block_start = self.document().findBlock(start) - block_end = self.document().findBlock(end) - return sorted([block_start.blockNumber(), block_end.blockNumber()]) - - - #------Text selection - def has_selected_text(self): - """Returns True if some text is selected""" - return bool(to_text_string(self.textCursor().selectedText())) - - def get_selected_text(self): - """ - Return text selected by current text cursor, converted in unicode - - Replace the unicode line separator character \u2029 by - the line separator characters returned by get_line_separator - """ - return to_text_string(self.textCursor().selectedText()).replace(u("\u2029"), - self.get_line_separator()) - - def remove_selected_text(self): - """Delete selected text""" - self.textCursor().removeSelectedText() - - def replace(self, text, pattern=None): - """Replace selected text by *text* - If *pattern* is not None, replacing selected text using regular - expression text substitution""" - cursor = self.textCursor() - cursor.beginEditBlock() - if pattern is not None: - seltxt = to_text_string(cursor.selectedText()) - cursor.removeSelectedText() - if pattern is not None: - text = re.sub(to_text_string(pattern), - to_text_string(text), to_text_string(seltxt)) - cursor.insertText(text) - cursor.endEditBlock() - - - #------Find/replace - def find_multiline_pattern(self, regexp, cursor, findflag): - """Reimplement QTextDocument's find method - - Add support for *multiline* regular expressions""" - pattern = to_text_string(regexp.pattern()) - text = to_text_string(self.toPlainText()) - try: - regobj = re.compile(pattern) - except sre_constants.error: - return - if findflag & QTextDocument.FindBackward: - # Find backward - offset = min([cursor.selectionEnd(), cursor.selectionStart()]) - text = text[:offset] - matches = [_m for _m in regobj.finditer(text, 0, offset)] - if matches: - match = matches[-1] - else: - return - else: - # Find forward - offset = max([cursor.selectionEnd(), cursor.selectionStart()]) - match = regobj.search(text, offset) - if match: - pos1, pos2 = match.span() - fcursor = self.textCursor() - fcursor.setPosition(pos1) - fcursor.setPosition(pos2, QTextCursor.KeepAnchor) - return fcursor - - def find_text(self, text, changed=True, forward=True, case=False, - words=False, regexp=False): - """Find text""" - cursor = self.textCursor() - findflag = QTextDocument.FindFlag() - if not forward: - findflag = findflag | QTextDocument.FindBackward - moves = [QTextCursor.NoMove] - if forward: - moves += [QTextCursor.NextWord, QTextCursor.Start] - if changed: - if to_text_string(cursor.selectedText()): - new_position = min([cursor.selectionStart(), - cursor.selectionEnd()]) - cursor.setPosition(new_position) - else: - cursor.movePosition(QTextCursor.PreviousWord) - else: - moves += [QTextCursor.End] - if not regexp: - text = re.escape(to_text_string(text)) - pattern = QRegExp(r"\b%s\b" % text if words else text, - Qt.CaseSensitive if case else Qt.CaseInsensitive, - QRegExp.RegExp2) - for move in moves: - cursor.movePosition(move) - if regexp and '\\n' in text: - # Multiline regular expression - found_cursor = self.find_multiline_pattern(pattern, cursor, - findflag) - else: - # Single line find: using the QTextDocument's find function, - # probably much more efficient than ours - found_cursor = self.document().find(pattern, cursor, findflag) - if found_cursor is not None and not found_cursor.isNull(): - self.setTextCursor(found_cursor) - return True - return False - - -class TracebackLinksMixin(object): - QT_CLASS = None - - def __init__(self): - self.__cursor_changed = False - self.setMouseTracking(True) - - #------Mouse events - def mouseReleaseEvent(self, event): - """Go to error""" - self.QT_CLASS.mouseReleaseEvent(self, event) - text = self.get_line_at(event.pos()) - if get_error_match(text) and not self.has_selected_text(): - self.emit(SIGNAL("go_to_error(QString)"), text) - - def mouseMoveEvent(self, event): - """Show Pointing Hand Cursor on error messages""" - text = self.get_line_at(event.pos()) - if get_error_match(text): - if not self.__cursor_changed: - QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor)) - self.__cursor_changed = True - event.accept() - return - if self.__cursor_changed: - QApplication.restoreOverrideCursor() - self.__cursor_changed = False - self.QT_CLASS.mouseMoveEvent(self, event) - - def leaveEvent(self, event): - """If cursor has not been restored yet, do it now""" - if self.__cursor_changed: - QApplication.restoreOverrideCursor() - self.__cursor_changed = False - self.QT_CLASS.leaveEvent(self, event) - - -class InspectObjectMixin(object): - def __init__(self): - self.inspector = None - self.inspector_enabled = False - - def set_inspector(self, inspector): - """Set ObjectInspector DockWidget reference""" - self.inspector = inspector - self.inspector.set_shell(self) - - def set_inspector_enabled(self, state): - self.inspector_enabled = state - - def inspect_current_object(self): - text = '' - text1 = self.get_text('sol', 'cursor') - tl1 = re.findall(r'([a-zA-Z_]+[0-9a-zA-Z_\.]*)', text1) - if tl1 and text1.endswith(tl1[-1]): - text += tl1[-1] - text2 = self.get_text('cursor', 'eol') - tl2 = re.findall(r'([0-9a-zA-Z_\.]+[0-9a-zA-Z_\.]*)', text2) - if tl2 and text2.startswith(tl2[0]): - text += tl2[0] - if text: - self.show_object_info(text, force=True) - - def show_object_info(self, text, call=False, force=False): - """Show signature calltip and/or docstring in the Object Inspector""" - text = to_text_string(text) # Useful only for ExternalShellBase - - # Show docstring - insp_enabled = self.inspector_enabled or force - if force and self.inspector is not None: - self.inspector.dockwidget.setVisible(True) - self.inspector.dockwidget.raise_() - if insp_enabled and (self.inspector is not None) and \ - (self.inspector.dockwidget.isVisible()): - # ObjectInspector widget exists and is visible - self.inspector.set_shell(self) - self.inspector.set_object_text(text, ignore_unknown=False) - self.setFocus() # if inspector was not at top level, raising it to - # top will automatically give it focus because of - # the visibility_changed signal, so we must give - # focus back to shell - - # Show calltip - if call and self.calltips: - # Display argument list if this is a function call - iscallable = self.iscallable(text) - if iscallable is not None: - if iscallable: - arglist = self.get_arglist(text) - name = text.split('.')[-1] - argspec = signature = '' - if isinstance(arglist, bool): - arglist = [] - if arglist: - argspec = '(' + ''.join(arglist) + ')' - else: - doc = self.get__doc__(text) - if doc is not None: - # This covers cases like np.abs, whose docstring is - # the same as np.absolute and because of that a - # proper signature can't be obtained correctly - argspec = getargspecfromtext(doc) - if not argspec: - signature = getsignaturefromtext(doc, name) - if argspec or signature: - if argspec: - tiptext = name + argspec - else: - tiptext = signature - self.show_calltip(_("Arguments"), tiptext, - signature=True, color='#2D62FF') - - def get_last_obj(self, last=False): - """ - Return the last valid object on the current line - """ - return getobj(self.get_current_line_to_cursor(), last=last) - - -class SaveHistoryMixin(object): - - INITHISTORY = None - SEPARATOR = None - - def __init__(self): - pass - - def add_to_history(self, command): - """Add command to history""" - command = to_text_string(command) - if command in ['', '\n'] or command.startswith('Traceback'): - return - if command.endswith('\n'): - command = command[:-1] - self.histidx = None - if len(self.history)>0 and self.history[-1] == command: - return - self.history.append(command) - text = os.linesep + command - - # When the first entry will be written in history file, - # the separator will be append first: - if self.history_filename not in HISTORY_FILENAMES: - HISTORY_FILENAMES.append(self.history_filename) - text = self.SEPARATOR + text - - encoding.write(text, self.history_filename, mode='ab') - self.emit(SIGNAL('append_to_history(QString,QString)'), - self.history_filename, text) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/objecteditor.py spyder-3.0.2+dfsg1/spyderlib/widgets/objecteditor.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/objecteditor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/objecteditor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Object Editor Dialog based on Qt -""" - -from __future__ import print_function - -from spyderlib.qt.QtCore import QObject, SIGNAL - -# Local imports -from spyderlib.py3compat import is_text_string - - -class DialogKeeper(QObject): - def __init__(self): - QObject.__init__(self) - self.dialogs = {} - self.namespace = None - - def set_namespace(self, namespace): - self.namespace = namespace - - def create_dialog(self, dialog, refname, func): - self.dialogs[id(dialog)] = dialog, refname, func - self.connect(dialog, SIGNAL('accepted()'), - lambda eid=id(dialog): self.editor_accepted(eid)) - self.connect(dialog, SIGNAL('rejected()'), - lambda eid=id(dialog): self.editor_rejected(eid)) - dialog.show() - dialog.activateWindow() - dialog.raise_() - - def editor_accepted(self, dialog_id): - dialog, refname, func = self.dialogs[dialog_id] - self.namespace[refname] = func(dialog) - self.dialogs.pop(dialog_id) - - def editor_rejected(self, dialog_id): - self.dialogs.pop(dialog_id) - -keeper = DialogKeeper() - - -def create_dialog(obj, obj_name): - """Creates the editor dialog and returns a tuple (dialog, func) where func - is the function to be called with the dialog instance as argument, after - quitting the dialog box - - The role of this intermediate function is to allow easy monkey-patching. - (uschmitt suggested this indirection here so that he can monkey patch - oedit to show eMZed related data) - """ - # Local import - from spyderlib.widgets.texteditor import TextEditor - from spyderlib.widgets.dicteditorutils import (ndarray, FakeObject, - Image, is_known_type, - DataFrame, Series) - from spyderlib.widgets.dicteditor import DictEditor - from spyderlib.widgets.arrayeditor import ArrayEditor - if DataFrame is not FakeObject: - from spyderlib.widgets.dataframeeditor import DataFrameEditor - - conv_func = lambda data: data - readonly = not is_known_type(obj) - if isinstance(obj, ndarray) and ndarray is not FakeObject: - dialog = ArrayEditor() - if not dialog.setup_and_check(obj, title=obj_name, - readonly=readonly): - return - elif isinstance(obj, Image) and Image is not FakeObject \ - and ndarray is not FakeObject: - dialog = ArrayEditor() - import numpy as np - data = np.array(obj) - if not dialog.setup_and_check(data, title=obj_name, - readonly=readonly): - return - from spyderlib.pil_patch import Image - conv_func = lambda data: Image.fromarray(data, mode=obj.mode) - elif isinstance(obj, (DataFrame, Series)) and DataFrame is not FakeObject: - dialog = DataFrameEditor() - if not dialog.setup_and_check(obj): - return - elif is_text_string(obj): - dialog = TextEditor(obj, title=obj_name, readonly=readonly) - else: - dialog = DictEditor() - dialog.setup(obj, title=obj_name, readonly=readonly) - - def end_func(dialog): - return conv_func(dialog.get_value()) - - return dialog, end_func - - -def oedit(obj, modal=True, namespace=None): - """Edit the object 'obj' in a GUI-based editor and return the edited copy - (if Cancel is pressed, return None) - - The object 'obj' is a container - - Supported container types: - dict, list, tuple, str/unicode or numpy.array - - (instantiate a new QApplication if necessary, - so it can be called directly from the interpreter) - """ - # Local import - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - - if modal: - obj_name = '' - else: - assert is_text_string(obj) - obj_name = obj - if namespace is None: - namespace = globals() - keeper.set_namespace(namespace) - obj = namespace[obj_name] - # keep QApplication reference alive in the Python interpreter: - namespace['__qapp__'] = app - - result = create_dialog(obj, obj_name) - if result is None: - return - dialog, end_func = result - - if modal: - if dialog.exec_(): - return end_func(dialog) - else: - keeper.create_dialog(dialog, obj_name, end_func) - import os - if os.name == 'nt': - app.exec_() - - -def test(): - """Run object editor test""" - import datetime, numpy as np - from spyderlib.pil_patch import Image - image = Image.fromarray(np.random.random_integers(255, size=(100, 100))) - example = {'str': 'kjkj kj k j j kj k jkj', - 'list': [1, 3, 4, 'kjkj', None], - 'dict': {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]}, - 'float': 1.2233, - 'array': np.random.rand(10, 10), - 'image': image, - 'date': datetime.date(1945, 5, 8), - 'datetime': datetime.datetime(1945, 5, 8), - } - image = oedit(image) - class Foobar(object): - def __init__(self): - self.text = "toto" - foobar = Foobar() - print(oedit(foobar)) - print(oedit(example)) - print(oedit(np.random.rand(10, 10))) - print(oedit(oedit.__doc__)) - print(example) - -if __name__ == "__main__": - test() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/onecolumntree.py spyder-3.0.2+dfsg1/spyderlib/widgets/onecolumntree.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/onecolumntree.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/onecolumntree.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,220 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.widgets -================= - -Widgets defined in this module may be used in any other Qt-based application - -They are also used in Spyder through the Plugin interface -(see spyderlib.plugins) -""" - -from spyderlib.qt.QtGui import QTreeWidget, QMenu -from spyderlib.qt.QtCore import SIGNAL - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.utils.qthelpers import (get_icon, create_action, add_actions, - get_item_user_text) - - -class OneColumnTree(QTreeWidget): - """One-column tree widget with context menu, ...""" - def __init__(self, parent): - QTreeWidget.__init__(self, parent) - self.setItemsExpandable(True) - self.setColumnCount(1) - self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*,int)'), - self.activated) - self.connect(self, SIGNAL('itemClicked(QTreeWidgetItem*,int)'), - self.clicked) - # Setup context menu - self.menu = QMenu(self) - self.collapse_all_action = None - self.collapse_selection_action = None - self.expand_all_action = None - self.expand_selection_action = None - self.common_actions = self.setup_common_actions() - - self.__expanded_state = None - - self.connect(self, SIGNAL('itemSelectionChanged()'), - self.item_selection_changed) - self.item_selection_changed() - - def activated(self, item): - """Double-click event""" - raise NotImplementedError - - def clicked(self, item): - pass - - def set_title(self, title): - self.setHeaderLabels([title]) - - def setup_common_actions(self): - """Setup context menu common actions""" - self.collapse_all_action = create_action(self, - text=_('Collapse all'), - icon=get_icon('collapse.png'), - triggered=self.collapseAll) - self.expand_all_action = create_action(self, - text=_('Expand all'), - icon=get_icon('expand.png'), - triggered=self.expandAll) - self.restore_action = create_action(self, - text=_('Restore'), - tip=_('Restore original tree layout'), - icon=get_icon('restore.png'), - triggered=self.restore) - self.collapse_selection_action = create_action(self, - text=_('Collapse selection'), - icon=get_icon('collapse_selection.png'), - triggered=self.collapse_selection) - self.expand_selection_action = create_action(self, - text=_('Expand selection'), - icon=get_icon('expand_selection.png'), - triggered=self.expand_selection) - return [self.collapse_all_action, self.expand_all_action, - self.restore_action, None, - self.collapse_selection_action, self.expand_selection_action] - - def update_menu(self): - self.menu.clear() - items = self.selectedItems() - actions = self.get_actions_from_items(items) - if actions: - actions.append(None) - actions += self.common_actions - add_actions(self.menu, actions) - - def get_actions_from_items(self, items): - # Right here: add other actions if necessary - # (reimplement this method) - return [] - - def restore(self): - self.collapseAll() - for item in self.get_top_level_items(): - self.expandItem(item) - - def is_item_expandable(self, item): - """To be reimplemented in child class - See example in project explorer widget""" - return True - - def __expand_item(self, item): - if self.is_item_expandable(item): - self.expandItem(item) - for index in range(item.childCount()): - child = item.child(index) - self.__expand_item(child) - - def expand_selection(self): - items = self.selectedItems() - if not items: - items = self.get_top_level_items() - for item in items: - self.__expand_item(item) - if items: - self.scrollToItem(items[0]) - - def __collapse_item(self, item): - self.collapseItem(item) - for index in range(item.childCount()): - child = item.child(index) - self.__collapse_item(child) - - def collapse_selection(self): - items = self.selectedItems() - if not items: - items = self.get_top_level_items() - for item in items: - self.__collapse_item(item) - if items: - self.scrollToItem(items[0]) - - def item_selection_changed(self): - """Item selection has changed""" - is_selection = len(self.selectedItems()) > 0 - self.expand_selection_action.setEnabled(is_selection) - self.collapse_selection_action.setEnabled(is_selection) - - def get_top_level_items(self): - """Iterate over top level items""" - return [self.topLevelItem(_i) for _i in range(self.topLevelItemCount())] - - def get_items(self): - """Return items (excluding top level items)""" - itemlist = [] - def add_to_itemlist(item): - for index in range(item.childCount()): - citem = item.child(index) - itemlist.append(citem) - add_to_itemlist(citem) - for tlitem in self.get_top_level_items(): - add_to_itemlist(tlitem) - return itemlist - - def get_scrollbar_position(self): - return (self.horizontalScrollBar().value(), - self.verticalScrollBar().value()) - - def set_scrollbar_position(self, position): - hor, ver = position - self.horizontalScrollBar().setValue(hor) - self.verticalScrollBar().setValue(ver) - - def get_expanded_state(self): - self.save_expanded_state() - return self.__expanded_state - - def set_expanded_state(self, state): - self.__expanded_state = state - self.restore_expanded_state() - - def save_expanded_state(self): - """Save all items expanded state""" - self.__expanded_state = {} - def add_to_state(item): - user_text = get_item_user_text(item) - self.__expanded_state[hash(user_text)] = item.isExpanded() - def browse_children(item): - add_to_state(item) - for index in range(item.childCount()): - citem = item.child(index) - user_text = get_item_user_text(citem) - self.__expanded_state[hash(user_text)] = citem.isExpanded() - browse_children(citem) - for tlitem in self.get_top_level_items(): - browse_children(tlitem) - - def restore_expanded_state(self): - """Restore all items expanded state""" - if self.__expanded_state is None: - return - for item in self.get_items()+self.get_top_level_items(): - user_text = get_item_user_text(item) - is_expanded = self.__expanded_state.get(hash(user_text)) - if is_expanded is not None: - item.setExpanded(is_expanded) - - def sort_top_level_items(self, key): - """Sorting tree wrt top level items""" - self.save_expanded_state() - items = sorted([self.takeTopLevelItem(0) - for index in range(self.topLevelItemCount())], key=key) - for index, item in enumerate(items): - self.insertTopLevelItem(index, item) - self.restore_expanded_state() - - def contextMenuEvent(self, event): - """Override Qt method""" - self.update_menu() - self.menu.popup(event.globalPos()) - diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/pathmanager.py spyder-3.0.2+dfsg1/spyderlib/widgets/pathmanager.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/pathmanager.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/pathmanager.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,247 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Spyder path manager""" - -from __future__ import print_function - -from spyderlib.qt.QtGui import (QDialog, QListWidget, QDialogButtonBox, - QVBoxLayout, QHBoxLayout, QMessageBox, - QListWidgetItem) -from spyderlib.qt.QtCore import Qt, SIGNAL, SLOT -from spyderlib.qt.compat import getexistingdirectory - -import os -import sys -import os.path as osp - -# Local imports -from spyderlib.utils.qthelpers import get_icon, get_std_icon, create_toolbutton -from spyderlib.baseconfig import _ -from spyderlib.py3compat import getcwd - - -class PathManager(QDialog): - def __init__(self, parent=None, pathlist=None, ro_pathlist=None, sync=True): - QDialog.__init__(self, parent) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - assert isinstance(pathlist, list) - self.pathlist = pathlist - if ro_pathlist is None: - ro_pathlist = [] - self.ro_pathlist = ro_pathlist - - self.last_path = getcwd() - - self.setWindowTitle(_("PYTHONPATH manager")) - self.setWindowIcon(get_icon('pythonpath.png')) - self.resize(500, 300) - - self.selection_widgets = [] - - layout = QVBoxLayout() - self.setLayout(layout) - - top_layout = QHBoxLayout() - layout.addLayout(top_layout) - self.toolbar_widgets1 = self.setup_top_toolbar(top_layout) - - self.listwidget = QListWidget(self) - self.connect(self.listwidget, SIGNAL("currentRowChanged(int)"), - self.refresh) - layout.addWidget(self.listwidget) - - bottom_layout = QHBoxLayout() - layout.addLayout(bottom_layout) - self.sync_button = None - self.toolbar_widgets2 = self.setup_bottom_toolbar(bottom_layout, sync) - - # Buttons configuration - bbox = QDialogButtonBox(QDialogButtonBox.Close) - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - bottom_layout.addWidget(bbox) - - self.update_list() - self.refresh() - - def _add_widgets_to_layout(self, layout, widgets): - layout.setAlignment(Qt.AlignLeft) - for widget in widgets: - layout.addWidget(widget) - - def setup_top_toolbar(self, layout): - toolbar = [] - movetop_button = create_toolbutton(self, - text=_("Move to top"), - icon=get_icon('2uparrow.png'), - triggered=lambda: self.move_to(absolute=0), - text_beside_icon=True) - toolbar.append(movetop_button) - moveup_button = create_toolbutton(self, - text=_("Move up"), - icon=get_icon('1uparrow.png'), - triggered=lambda: self.move_to(relative=-1), - text_beside_icon=True) - toolbar.append(moveup_button) - movedown_button = create_toolbutton(self, - text=_("Move down"), - icon=get_icon('1downarrow.png'), - triggered=lambda: self.move_to(relative=1), - text_beside_icon=True) - toolbar.append(movedown_button) - movebottom_button = create_toolbutton(self, - text=_("Move to bottom"), - icon=get_icon('2downarrow.png'), - triggered=lambda: self.move_to(absolute=1), - text_beside_icon=True) - toolbar.append(movebottom_button) - self.selection_widgets.extend(toolbar) - self._add_widgets_to_layout(layout, toolbar) - return toolbar - - def setup_bottom_toolbar(self, layout, sync=True): - toolbar = [] - add_button = create_toolbutton(self, text=_("Add path"), - icon=get_icon('edit_add.png'), - triggered=self.add_path, - text_beside_icon=True) - toolbar.append(add_button) - remove_button = create_toolbutton(self, text=_("Remove path"), - icon=get_icon('edit_remove.png'), - triggered=self.remove_path, - text_beside_icon=True) - toolbar.append(remove_button) - self.selection_widgets.append(remove_button) - self._add_widgets_to_layout(layout, toolbar) - layout.addStretch(1) - if os.name == 'nt' and sync: - self.sync_button = create_toolbutton(self, - text=_("Synchronize..."), - icon=get_icon('synchronize.png'), triggered=self.synchronize, - tip=_("Synchronize Spyder's path list with PYTHONPATH " - "environment variable"), - text_beside_icon=True) - layout.addWidget(self.sync_button) - return toolbar - - def synchronize(self): - """ - Synchronize Spyder's path list with PYTHONPATH environment variable - Only apply to: current user, on Windows platforms - """ - answer = QMessageBox.question(self, _("Synchronize"), - _("This will synchronize Spyder's path list with " - "PYTHONPATH environment variable for current user, " - "allowing you to run your Python modules outside Spyder " - "without having to configure sys.path. " - "
    Do you want to clear contents of PYTHONPATH before " - "adding Spyder's path list?"), - QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) - if answer == QMessageBox.Cancel: - return - elif answer == QMessageBox.Yes: - remove = True - else: - remove = False - from spyderlib.utils.environ import (get_user_env, set_user_env, - listdict2envdict) - env = get_user_env() - if remove: - ppath = self.pathlist+self.ro_pathlist - else: - ppath = env.get('PYTHONPATH', []) - if not isinstance(ppath, list): - ppath = [ppath] - ppath = [path for path in ppath - if path not in (self.pathlist+self.ro_pathlist)] - ppath.extend(self.pathlist+self.ro_pathlist) - env['PYTHONPATH'] = ppath - set_user_env( listdict2envdict(env), parent=self ) - - def get_path_list(self): - """Return path list (does not include the read-only path list)""" - return self.pathlist - - def update_list(self): - """Update path list""" - self.listwidget.clear() - for name in self.pathlist+self.ro_pathlist: - item = QListWidgetItem(name) - item.setIcon(get_std_icon('DirClosedIcon')) - if name in self.ro_pathlist: - item.setFlags(Qt.NoItemFlags) - self.listwidget.addItem(item) - self.refresh() - - def refresh(self, row=None): - """Refresh widget""" - for widget in self.selection_widgets: - widget.setEnabled(self.listwidget.currentItem() is not None) - not_empty = self.listwidget.count() > 0 - if self.sync_button is not None: - self.sync_button.setEnabled(not_empty) - - def move_to(self, absolute=None, relative=None): - index = self.listwidget.currentRow() - if absolute is not None: - if absolute: - new_index = len(self.pathlist)-1 - else: - new_index = 0 - else: - new_index = index + relative - new_index = max(0, min(len(self.pathlist)-1, new_index)) - path = self.pathlist.pop(index) - self.pathlist.insert(new_index, path) - self.update_list() - self.listwidget.setCurrentRow(new_index) - - def remove_path(self): - answer = QMessageBox.warning(self, _("Remove path"), - _("Do you really want to remove selected path?"), - QMessageBox.Yes | QMessageBox.No) - if answer == QMessageBox.Yes: - self.pathlist.pop(self.listwidget.currentRow()) - self.update_list() - - def add_path(self): - self.emit(SIGNAL('redirect_stdio(bool)'), False) - directory = getexistingdirectory(self, _("Select directory"), - self.last_path) - self.emit(SIGNAL('redirect_stdio(bool)'), True) - if directory: - directory = osp.abspath(directory) - self.last_path = directory - if directory in self.pathlist: - answer = QMessageBox.question(self, _("Add path"), - _("This directory is already included in Spyder path " - "list.
    Do you want to move it to the top of " - "the list?"), - QMessageBox.Yes | QMessageBox.No) - if answer == QMessageBox.Yes: - self.pathlist.remove(directory) - else: - return - self.pathlist.insert(0, directory) - self.update_list() - - -def test(): - """Run path manager test""" - from spyderlib.utils.qthelpers import qapplication - _app = qapplication() # analysis:ignore - test = PathManager(None, sys.path[:-10], sys.path[-10:]) - test.exec_() - print(test.get_path_list()) - -if __name__ == "__main__": - test() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/projectexplorer.py spyder-3.0.2+dfsg1/spyderlib/widgets/projectexplorer.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/projectexplorer.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/projectexplorer.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1368 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2010-2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Project Explorer""" - -# pylint: disable=C0103 - -from __future__ import print_function - -from spyderlib.qt.QtGui import (QVBoxLayout, QLabel, QHBoxLayout, QWidget, - QFileIconProvider, QMessageBox, QInputDialog, - QLineEdit, QPushButton, QHeaderView, - QAbstractItemView) -from spyderlib.qt.QtCore import Qt, SIGNAL, QFileInfo, Slot, Signal -from spyderlib.qt.compat import getexistingdirectory - -import os -import re -import shutil -import os.path as osp -import xml.etree.ElementTree as ElementTree - -# Local imports -from spyderlib.utils import misc -from spyderlib.utils.qthelpers import get_icon, get_std_icon, create_action -from spyderlib.baseconfig import _, STDERR, get_image_path -from spyderlib.widgets.explorer import FilteredDirView, listdir, fixpath -from spyderlib.widgets.formlayout import fedit -from spyderlib.widgets.pathmanager import PathManager -from spyderlib.py3compat import to_text_string, getcwd, pickle - - -def has_children_files(path, include, exclude, show_all): - """Return True if path has children files""" - try: - return len( listdir(path, include, exclude, show_all) ) > 0 - except (IOError, OSError): - return False - - -def is_drive_path(path): - """Return True if path is a drive (Windows)""" - path = osp.abspath(path) - return osp.normpath(osp.join(path, osp.pardir)) == path - - -def get_dir_icon(dirname, project): - """Return appropriate directory icon""" - if is_drive_path(dirname): - return get_std_icon('DriveHDIcon') - prefix = 'pp_' if dirname in project.get_pythonpath() else '' - if dirname == project.root_path: - if project.is_opened(): - return get_icon(prefix+'project.png') - else: - return get_icon('project_closed.png') - elif osp.isfile(osp.join(dirname, '__init__.py')): - return get_icon(prefix+'package.png') - else: - return get_icon(prefix+'folder.png') - - -class Project(object): - """Spyder project""" - CONFIG_NAME = '.spyderproject' - CONFIG_ATTR = ('name', 'related_projects', 'relative_pythonpath', 'opened') - - def __init__(self): - self.name = None - self.root_path = None - self.related_projects = [] # storing project path, not project objects - self.pythonpath = [] - self.opened = True - self.ioerror_flag = False - - def set_root_path(self, root_path): - """Set workspace root path""" - if self.name is None: - self.name = osp.basename(root_path) - self.root_path = to_text_string(root_path) - config_path = self.__get_project_config_path() - if osp.exists(config_path): - self.load() - else: - if not osp.isdir(self.root_path): - os.mkdir(self.root_path) - self.save() - - def rename(self, new_name): - """Rename project and rename its root path accordingly""" - old_name = self.name - self.name = new_name - pypath = self.relative_pythonpath - self.root_path = self.root_path[:-len(old_name)]+new_name - self.relative_pythonpath = pypath - self.save() - - def _get_relative_pythonpath(self): - """Return PYTHONPATH list as relative paths""" - # Workaround to replace os.path.relpath (new in Python v2.6): - offset = len(self.root_path)+len(os.pathsep) - return [path[offset:] for path in self.pythonpath] - - def _set_relative_pythonpath(self, value): - """Set PYTHONPATH list relative paths""" - self.pythonpath = [osp.abspath(osp.join(self.root_path, path)) - for path in value] - - relative_pythonpath = property(_get_relative_pythonpath, - _set_relative_pythonpath) - - def __get_project_config_path(self): - """Return project configuration path""" - return osp.join(self.root_path, self.CONFIG_NAME) - - def load(self): - """Load project data""" - fname = self.__get_project_config_path() - try: - # Old format (Spyder 2.0-2.1 for Python 2) - with open(fname, 'U') as fdesc: - data = pickle.loads(fdesc.read()) - except (pickle.PickleError, TypeError, UnicodeDecodeError, - AttributeError): - try: - # New format (Spyder >=2.2 for Python 2 and Python 3) - with open(fname, 'rb') as fdesc: - data = pickle.loads(fdesc.read()) - except (IOError, OSError, pickle.PickleError): - self.ioerror_flag = True - return - # Compatibilty with old project explorer file format: - if 'relative_pythonpath' not in data: - print("Warning: converting old configuration file " \ - "for project '%s'" % data['name'], file=STDERR) - self.pythonpath = data['pythonpath'] - data['relative_pythonpath'] = self.relative_pythonpath - for attr in self.CONFIG_ATTR: - setattr(self, attr, data[attr]) - self.save() - - def save(self): - """Save project data""" - data = {} - for attr in self.CONFIG_ATTR: - data[attr] = getattr(self, attr) - try: - with open(self.__get_project_config_path(), 'wb') as fdesc: - pickle.dump(data, fdesc, 2) - except (IOError, OSError): - self.ioerror_flag = True - - def delete(self): - """Delete project""" - os.remove(self.__get_project_config_path()) - - #------Misc. - def get_related_projects(self): - """Return related projects path list""" - return self.related_projects - - def set_related_projects(self, related_projects): - """Set related projects""" - self.related_projects = related_projects - self.save() - - def open(self): - """Open project""" - self.opened = True - self.save() - - def close(self): - """Close project""" - self.opened = False - self.save() - - def is_opened(self): - """Return True if project is opened""" - return self.opened - - def is_file_in_project(self, fname): - """Return True if file *fname* is in one of the project subfolders""" - fixed_root = fixpath(self.root_path) - return fixpath(fname) == fixed_root \ - or fixpath(osp.dirname(fname)).startswith(fixed_root) - - def is_root_path(self, dirname): - """Return True if dirname is project's root path""" - return fixpath(dirname) == fixpath(self.root_path) - - def is_in_pythonpath(self, dirname): - """Return True if dirname is in project's PYTHONPATH""" - return fixpath(dirname) in [fixpath(_p) for _p in self.pythonpath] - - #------Python Path - def get_pythonpath(self): - """Return a copy of pythonpath attribute""" - return self.pythonpath[:] - - def set_pythonpath(self, pythonpath): - """Set project's PYTHONPATH""" - self.pythonpath = pythonpath - self.save() - - def remove_from_pythonpath(self, path): - """Remove path from project's PYTHONPATH - Return True if path was removed, False if it was not found""" - pathlist = self.get_pythonpath() - if path in pathlist: - pathlist.pop(pathlist.index(path)) - self.set_pythonpath(pathlist) - return True - else: - return False - - def add_to_pythonpath(self, path): - """Add path to project's PYTHONPATH - Return True if path was added, False if it was already there""" - pathlist = self.get_pythonpath() - if path in pathlist: - return False - else: - pathlist.insert(0, path) - self.set_pythonpath(pathlist) - return True - - -class Workspace(object): - """Spyder workspace - Set of projects with common root path parent directory""" - CONFIG_NAME = '.spyderworkspace' - CONFIG_ATTR = ('name', 'project_paths', ) - - def __init__(self): - self.name = None - self.root_path = None - self.projects = [] - self.ioerror_flag = False - - def _get_project_paths(self): - """Return workspace projects root path list""" - # Convert project absolute paths to paths relative to Workspace root - offset = len(self.root_path)+len(os.pathsep) - return [proj.root_path[offset:] for proj in self.projects] - - def _set_project_paths(self, pathlist): - """Set workspace projects root path list""" - # Convert paths relative to Workspace root to project absolute paths - for path in pathlist: - if path.startswith(self.root_path): - # do nothing, this is the old Workspace format - root_path = path - else: - root_path = osp.join(self.root_path, path) - self.add_project(root_path) - - project_paths = property(_get_project_paths, _set_project_paths) - - def is_valid(self): - """Return True if workspace is valid (i.e. root path is defined)""" - return self.root_path is not None and osp.isdir(self.root_path) - - def is_empty(self): - """Return True if workspace is empty (i.e. no project)""" - if not self.is_valid(): - return - return len(self.projects) == 0 - - def set_root_path(self, root_path): - """Set workspace root path""" - if self.name is None: - self.name = osp.basename(root_path) - self.root_path = to_text_string(osp.abspath(root_path)) - config_path = self.__get_workspace_config_path() - if osp.exists(config_path): - self.load() - else: - self.save() - - def set_name(self, name): - """Set workspace name""" - self.name = name - self.save() - - def __get_workspace_config_path(self): - """Return project configuration path""" - return osp.join(self.root_path, self.CONFIG_NAME) - - def load(self): - """Load workspace data""" - fname = self.__get_workspace_config_path() - try: - # Old format (Spyder 2.0-2.1 for Python 2) - with open(fname, 'U') as fdesc: - data = pickle.loads(fdesc.read()) - except (pickle.PickleError, TypeError, UnicodeDecodeError): - try: - # New format (Spyder >=2.2 for Python 2 and Python 3) - with open(fname, 'rb') as fdesc: - data = pickle.loads(fdesc.read()) - except (IOError, OSError, pickle.PickleError): - self.ioerror_flag = True - return - for attr in self.CONFIG_ATTR: - setattr(self, attr, data[attr]) - self.save() - - def save(self): - """Save workspace data""" - data = {} - for attr in self.CONFIG_ATTR: - data[attr] = getattr(self, attr) - try: - with open(self.__get_workspace_config_path(), 'wb') as fdesc: - pickle.dump(data, fdesc, 2) - except (IOError, OSError): - self.ioerror_flag = True - - def delete(self): - """Delete workspace""" - os.remove(self.__get_workspace_config_path()) - - #------Misc. - def get_ioerror_warning_message(self): - """Return a warning message if IOError exception was raised when - loading/saving the workspace or one of its projects""" - txt = "" - projlist = [_p.name for _p in self.projects if _p.ioerror_flag] - if self.ioerror_flag: - txt += _("its own configuration file") - if projlist: - txt += _(" and ") - else: - txt += "." - if projlist: - txt += _("the following projects:
    %s") % ", ".join(projlist) - return txt - - def is_file_in_workspace(self, fname): - """Return True if file *fname* is in one of the projects""" - return any([proj.is_file_in_project(fname) for proj in self.projects]) - - def is_file_in_closed_project(self, fname): - """Return True if file *fname* is in one of the closed projects""" - return any([proj.is_file_in_project(fname) for proj in self.projects - if not proj.is_opened()]) - - def is_in_pythonpath(self, dirname): - """Return True if dirname is in workspace's PYTHONPATH""" - return any([proj.is_in_pythonpath(dirname) for proj in self.projects]) - - def has_project(self, root_path_or_name): - """Return True if workspace has a project - with given root path or name""" - checklist = [project.root_path for project in self.projects - ]+[project.name for project in self.projects] - return root_path_or_name in checklist - - def get_source_project(self, fname): - """Return project which contains source *fname*""" - for project in self.projects: - if project.is_file_in_project(fname): - return project - - def get_project_from_name(self, name): - """Return project's object from name""" - for project in self.projects: - if project.name == name: - return project - - def get_folder_names(self): - """Return all project folder names (root path basename)""" - return [osp.basename(proj.root_path) for proj in self.projects] - - def add_project(self, root_path): - """Create project from root path, add it to workspace - Return the created project instance""" - project = Project() - try: - project.set_root_path(root_path) - except OSError: - # This may happens when loading a Workspace with absolute paths - # which has just been moved to a different location - return - self.projects.append(project) - self.save() - - def open_projects(self, projects, open_related=True): - """Open projects""" - for project in projects: - project.open() - related_projects = project.get_related_projects() - if open_related: - for projname in related_projects: - for relproj in self.projects: - if relproj.name == projname: - self.open_projects(relproj, open_related=False) - self.save() - - def close_projects(self, projects): - """Close projects""" - for project in projects: - project.close() - self.save() - - def remove_projects(self, projects): - """Remove projects""" - for project in projects: - project.delete() - self.projects.pop(self.projects.index(project)) - self.save() - - def close_unrelated_projects(self, projects): - """Close unrelated projects""" - unrelated_projects = [] - for project in projects: - for proj in self.projects: - if proj is project: - continue - if proj.name in project.get_related_projects(): - continue - if project.name in proj.get_related_projects(): - continue - unrelated_projects.append(proj) - self.close_projects(unrelated_projects) - self.save() - return unrelated_projects - - def rename_project(self, project, new_name): - """Rename project, update the related projects if necessary""" - old_name = project.name - for proj in self.projects: - relproj = proj.get_related_projects() - if old_name in relproj: - relproj[relproj.index(old_name)] = new_name - proj.set_related_projects(relproj) - project.rename(new_name) - self.save() - - def get_other_projects(self, project): - """Return all projects, except given project""" - return [_p for _p in self.projects if _p is not project] - - #------Python Path - def get_pythonpath(self): - """Return global PYTHONPATH (for all opened projects""" - pythonpath = [] - for project in self.projects: - if project.is_opened(): - pythonpath += project.get_pythonpath() - return pythonpath - - -def get_pydev_project_infos(project_path): - """Return Pydev project infos: name, related projects and PYTHONPATH""" - root = ElementTree.parse(osp.join(project_path, ".pydevproject")) - path = [] - project_root = osp.dirname(project_path) - for element in root.getiterator(): - if element.tag == 'path': - path.append(osp.abspath(osp.join(project_root, element.text[1:]))) - - root = ElementTree.parse(osp.join(project_path, ".project")) - related_projects = [] - name = None - for element in root.getiterator(): - if element.tag == 'project': - related_projects.append(element.text) - elif element.tag == 'name' and name is None: - name = element.text - - return name, related_projects, path - - -class IconProvider(QFileIconProvider): - """Project tree widget icon provider""" - def __init__(self, treeview): - super(IconProvider, self).__init__() - self.treeview = treeview - - @Slot(int) - @Slot(QFileInfo) - def icon(self, icontype_or_qfileinfo): - """Reimplement Qt method""" - if isinstance(icontype_or_qfileinfo, QFileIconProvider.IconType): - return super(IconProvider, self).icon(icontype_or_qfileinfo) - else: - qfileinfo = icontype_or_qfileinfo - fname = osp.normpath(to_text_string(qfileinfo.absoluteFilePath())) - if osp.isdir(fname): - project = self.treeview.get_source_project(fname) - if project is None: - return super(IconProvider, self).icon(qfileinfo) - else: - return get_dir_icon(fname, project) - else: - ext = osp.splitext(fname)[1][1:] - icon_path = get_image_path(ext+'.png', default=None) - if icon_path is not None: - return get_icon(icon_path) - else: - return super(IconProvider, self).icon(qfileinfo) - - -class ExplorerTreeWidget(FilteredDirView): - """Explorer tree widget - - workspace: this is the explorer tree widget root path - (this attribute name is specific to project explorer)""" - def __init__(self, parent, show_hscrollbar=True): - FilteredDirView.__init__(self, parent) - - self.workspace = Workspace() - - self.connect(self.fsmodel, SIGNAL('modelReset()'), - self.reset_icon_provider) - self.reset_icon_provider() - - self.last_folder = None - - self.setSelectionMode(FilteredDirView.ExtendedSelection) - - self.setHeaderHidden(True) - - self.show_hscrollbar = show_hscrollbar - - # Enable drag & drop events - self.setDragEnabled(True) - self.setDragDropMode(FilteredDirView.DragDrop) - - #------DirView API--------------------------------------------------------- - def setup_view(self): - """Setup view""" - FilteredDirView.setup_view(self) - - def create_file_new_actions(self, fnames): - """Return actions for submenu 'New...'""" - new_project_act = create_action(self, text=_('Project...'), - icon=get_icon('project_expanded.png'), - triggered=self.new_project) - if self.workspace.is_empty(): - new_project_act.setText(_('New project...')) - return [new_project_act] - else: - new_actions = FilteredDirView.create_file_new_actions(self, fnames) - return [new_project_act, None]+new_actions - - def create_file_import_actions(self, fnames): - """Return actions for submenu 'Import...'""" - import_folder_act = create_action(self, - text=_('Existing directory'), - icon=get_std_icon('DirOpenIcon'), - triggered=self.import_existing_directory) - import_spyder_act = create_action(self, - text=_('Existing Spyder project'), - icon=get_icon('spyder.svg'), - triggered=self.import_existing_project) - import_pydev_act = create_action(self, - text=_('Existing Pydev project'), - icon=get_icon('pydev.png'), - triggered=self.import_existing_pydev_project) - return [import_folder_act, import_spyder_act, import_pydev_act] - - def create_file_manage_actions(self, fnames): - """Reimplement DirView method""" - only_folders = all([osp.isdir(_fn) for _fn in fnames]) - projects = [self.get_source_project(fname) for fname in fnames] - pjfnames = list(zip(projects, fnames)) - any_project = any([_pr.is_root_path(_fn) for _pr, _fn in pjfnames]) - any_folder_in_path = any([_proj.is_in_pythonpath(_fn) - for _proj, _fn in pjfnames]) - any_folder_not_in_path = only_folders and \ - any([not _proj.is_in_pythonpath(_fn) - for _proj, _fn in pjfnames]) - open_act = create_action(self, - text=_('Open project'), - icon=get_icon('project_expanded.png'), - triggered=lambda: - self.open_projects(projects)) - close_act = create_action(self, - text=_('Close project'), - icon=get_icon('project_closed.png'), - triggered=lambda: - self.close_projects(projects)) - close_unrelated_act = create_action(self, - text=_('Close unrelated projects'), - triggered=lambda: - self.close_unrelated_projects(projects)) - manage_path_act = create_action(self, - icon=get_icon('pythonpath.png'), - text=_('PYTHONPATH manager'), - triggered=lambda: - self.manage_path(projects)) - relproj_act = create_action(self, - text=_('Edit related projects'), - triggered=lambda: - self.edit_related_projects(projects)) - state = self.workspace is not None\ - and len(self.workspace.projects) > 1 - relproj_act.setEnabled(state) - - add_to_path_act = create_action(self, - text=_('Add to PYTHONPATH'), - icon=get_icon('add_to_path.png'), - triggered=lambda: - self.add_to_path(fnames)) - remove_from_path_act = create_action(self, - text=_('Remove from PYTHONPATH'), - icon=get_icon('remove_from_path.png'), - triggered=lambda: - self.remove_from_path(fnames)) - properties_act = create_action(self, - text=_('Properties'), - icon=get_icon('advanced.png'), - triggered=lambda: - self.show_properties(fnames)) - - actions = [] - if any_project: - if any([not _proj.is_opened() for _proj in projects]): - actions += [open_act] - if any([_proj.is_opened() for _proj in projects]): - actions += [close_act, close_unrelated_act] - actions += [manage_path_act, relproj_act, None] - - if only_folders: - if any_folder_not_in_path: - actions += [add_to_path_act] - if any_folder_in_path: - actions += [remove_from_path_act] - actions += [None, properties_act, None] - actions += FilteredDirView.create_file_manage_actions(self, fnames) - return actions - - def create_context_menu_actions(self): - """Reimplement DirView method""" - if self.workspace.is_valid(): - # Workspace's root path is already defined - return FilteredDirView.create_context_menu_actions(self) - else: - return [] - - def setup_common_actions(self): - """Setup context menu common actions""" - actions = FilteredDirView.setup_common_actions(self) - - # Toggle horizontal scrollbar - hscrollbar_action = create_action(self, _("Show horizontal scrollbar"), - toggled=self.toggle_hscrollbar) - hscrollbar_action.setChecked(self.show_hscrollbar) - self.toggle_hscrollbar(self.show_hscrollbar) - - return actions + [hscrollbar_action] - - #------Public API---------------------------------------------------------- - def toggle_hscrollbar(self, checked): - """Toggle horizontal scrollbar""" - self.parent_widget.sig_option_changed.emit('show_hscrollbar', checked) - self.show_hscrollbar = checked - self.header().setStretchLastSection(not checked) - self.header().setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) - self.header().setResizeMode(QHeaderView.ResizeToContents) - - def set_folder_names(self, folder_names): - """Set folder names""" - self.setUpdatesEnabled(False) - FilteredDirView.set_folder_names(self, folder_names) - self.reset_icon_provider() - self.setUpdatesEnabled(True) - - def reset_icon_provider(self): - """Reset file system model icon provider - The purpose of this is to refresh files/directories icons""" - self.fsmodel.setIconProvider(IconProvider(self)) - - def check_for_io_errors(self): - """Eventually show a warning message box if IOError exception was - raised when loading/saving the workspace or one of its projects""" - txt = self.workspace.get_ioerror_warning_message() - if txt: - QMessageBox.critical(self, _('Workspace'), - _("The workspace was unable to load or save %s

    " - "Please check if you have the permission to write the " - "associated configuration files.") % txt) - - def set_workspace(self, root_path): - """Set project explorer's workspace directory""" - self.workspace = Workspace() - self.setModel(None) - self.fsmodel = None - self.proxymodel = None - self.setup_fs_model() - self.setup_proxy_model() - self.workspace.set_root_path(root_path) - self.set_root_path(root_path) - for index in range(1, self.model().columnCount()): - self.hideColumn(index) - self.set_folder_names(self.workspace.get_folder_names()) - - # The following fixes Issue 952: if we don't reset the "show all" - # option here, we will lose the feature because we have just rebuilt - # the fsmodel object from scratch. This would happen in particular - # when setting the workspace option in the project explorer widget - # (see spyderlib/widgets/projectexplorer.py). - self.set_show_all(self.show_all) - - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) -# print "folders:", self.workspace.get_folder_names() -# print "is_valid:", self.workspace.is_valid() -# print "is_empty:", self.workspace.is_empty() - - def get_workspace(self): - """Return project explorer's workspace directory""" - return self.workspace.root_path - - def is_in_workspace(self, fname): - """Return True if file/directory is in workspace""" - return self.workspace.is_file_in_workspace(fname) - - def get_project_path_from_name(self, name): - """Return project root path from name, knowing the workspace path""" - return osp.join(self.get_workspace(), name) - - def get_source_project(self, fname): - """Return project which contains source *fname*""" - return self.workspace.get_source_project(fname) - - def get_project_from_name(self, name): - """Return project's object from name""" - return self.workspace.get_project_from_name(name) - - def get_pythonpath(self): - """Return global PYTHONPATH (for all opened projects""" - return self.workspace.get_pythonpath() - - def add_project(self, folder, silent=False): - """Add project to tree""" - if not self.is_valid_project_root_path(folder, silent=silent): - return - if not fixpath(folder).startswith(fixpath(self.root_path)): - title = _("Import directory") - answer = QMessageBox.warning(self, title, - _("The following directory is not in workspace:" - "
    %s

    " - "Do you want to continue (and copy the " - "directory to workspace)?") % folder, - QMessageBox.Yes|QMessageBox.No) - if answer == QMessageBox.No: - return - name = self._select_project_name(title, - default=osp.basename(folder)) - if name is None: - return - dst = self.get_project_path_from_name(name) - try: - shutil.copytree(folder, dst) - except EnvironmentError as error: - QMessageBox.critical(self, title, - _("Unable to %s %s" - "

    Error message:
    %s" - ) % (_('copy'), folder, - to_text_string(error))) - folder = dst - - project = self.workspace.add_project(folder) - self.set_folder_names(self.workspace.get_folder_names()) - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) - - self.check_for_io_errors() - - return project - - def open_projects(self, projects, open_related=True): - """Open projects""" - self.workspace.open_projects(projects, open_related) - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) - self.reset_icon_provider() - for project in projects: - self.update(self.get_index(project.root_path)) - - def close_projects(self, projects): - """Close projects""" - self.workspace.close_projects(projects) - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) - self.parent_widget.emit(SIGNAL("projects_were_closed()")) - self.reset_icon_provider() - for project in projects: - index = self.get_index(project.root_path) - self.update(index) - self.collapse(index) - - def remove_projects(self, projects): - """Remove projects""" - self.workspace.remove_projects(projects) - self.set_folder_names(self.workspace.get_folder_names()) - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) - - def close_unrelated_projects(self, projects): - """Close unrelated projects""" - unrelated_projects = self.workspace.close_unrelated_projects(projects) - if unrelated_projects: - self.reset_icon_provider() - for project in unrelated_projects: - self.update(self.get_index(project.root_path)) - - def is_valid_project_root_path(self, root_path, silent=False): - """Return True root_path is a valid project root path""" - fixed_wr = fixpath(self.root_path) # workspace root path - fixed_pr = fixpath(osp.dirname(root_path)) # project root path - if self.workspace.has_project(root_path): - if not silent: - QMessageBox.critical(self, _("Project Explorer"), - _("The project %s" - " is already opened!" - ) % osp.basename(root_path)) - return False - elif fixed_pr != fixed_wr and fixed_pr.startswith(fixed_wr): - if not silent: - QMessageBox.warning(self, _("Project Explorer"), - _("The project root path directory " - "is inside the workspace but not as the " - "expected tree level. It is not a " - "directory of the workspace:
    " - "%s") % self.get_workspace()) - return True - - def _select_project_name(self, title, default=None): - """Select project name""" - name = '' if default is None else default - while True: - name, valid = QInputDialog.getText(self, title, _('Project name:'), - QLineEdit.Normal, name) - if valid and name: - name = to_text_string(name) - pattern = r'[a-zA-Z][a-zA-Z0-9\_\-]*$' - match = re.match(pattern, name) - path = self.get_project_path_from_name(name) - if self.workspace.has_project(name): - QMessageBox.critical(self, title, - _("A project named " - "%s already exists") % name) - continue - elif match is None: - QMessageBox.critical(self, title, - _("Invalid project name.

    " - "Name must match the following " - "regular expression:" - "
    %s") % pattern) - continue - elif osp.isdir(path): - answer = QMessageBox.warning(self, title, - _("The following directory is not empty:" - "
    %s

    " - "Do you want to continue?") % path, - QMessageBox.Yes|QMessageBox.No) - if answer == QMessageBox.No: - continue - return name - else: - return - - def new_project(self): - """Return True if project was created""" - title = _('New project') - if self.workspace.is_valid(): - name = self._select_project_name(title) - if name is not None: - folder = self.get_project_path_from_name(name) - self.add_project(folder) - else: - answer = QMessageBox.critical(self, title, - _("The current workspace has " - "not been configured yet.\n" - "Do you want to do this now?"), - QMessageBox.Yes|QMessageBox.Cancel) - if answer == QMessageBox.Yes: - self.emit(SIGNAL('select_workspace()')) - - def _select_existing_directory(self): - """Select existing source code directory, - to be used as a new project root path - (copied into the current project's workspace directory if necessary)""" - if self.last_folder is None: - self.last_folder = self.workspace.root_path - while True: - self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), False) - folder = getexistingdirectory(self, _("Select directory"), - self.last_folder) - self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), True) - if folder: - folder = osp.abspath(folder) - self.last_folder = folder - if not self.is_valid_project_root_path(folder): - continue - return folder - else: - return - - def import_existing_directory(self): - """Create project from existing directory - Eventually copy the whole directory to current workspace""" - folder = self._select_existing_directory() - if folder is None: - return - self.add_project(folder) - - def __select_existing_project(self, typename, configname): - """Select existing project""" - title = _('Import existing project') - while True: - folder = self._select_existing_directory() - if folder is None: - return - if not osp.isfile(osp.join(folder, configname)): - subfolders = [osp.join(folder, _f) for _f in os.listdir(folder) - if osp.isdir(osp.join(folder, _f)) - and osp.isfile(osp.join(folder, _f, configname))] - if subfolders: - data = [] - for subfolder in subfolders: - data.append((subfolder, False)) - comment = _("Select projects to import") - result = fedit(data, title=title, comment=comment) - if result is None: - return - else: - selected_folders = [] - for index, is_selected in enumerate(result): - if is_selected: - selected_folders.append(subfolders[index]) - return selected_folders - else: - QMessageBox.critical(self, title, - _("The folder %s " - "does not contain a valid %s project" - ) % (osp.basename(folder), typename)) - continue - return folder - - def import_existing_project(self): - """Import existing project""" - folders = self.__select_existing_project("Spyder", Project.CONFIG_NAME) - if folders is None: - return - if not isinstance(folders, (tuple, list)): - folders = [folders] - for folder in folders: - self.add_project(folder, silent=True) - - def import_existing_pydev_project(self): - """Import existing Pydev project""" - folders = self.__select_existing_project("Pydev", ".pydevproject") - if folders is None: - return - if not isinstance(folders, (tuple, list)): - folders = [folders] - for folder in folders: - try: - name, related_projects, path = get_pydev_project_infos(folder) - except RuntimeError as error: - QMessageBox.critical(self, - _('Import existing Pydev project'), - _("Unable to read Pydev project %s" - "

    Error message:
    %s" - ) % (osp.basename(folder), - to_text_string(error))) - finally: - project = self.add_project(folder, silent=True) - if project is not None: - project.name = name - project.set_related_projects(related_projects) - project.set_pythonpath(path) - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) - - def rename_file(self, fname): - """Rename file""" - path = FilteredDirView.rename_file(self, fname) - if path: - project = self.get_source_project(fname) - if project.is_root_path(fname): - self.workspace.rename_project(project, osp.basename(path)) - self.set_folder_names(self.workspace.get_folder_names()) - else: - self.remove_path_from_project_pythonpath(project, fname) - - def remove_tree(self, dirname): - """Remove whole directory tree""" - FilteredDirView.remove_tree(self, dirname) - project = self.get_source_project(dirname) - self.remove_path_from_project_pythonpath(project, dirname) - - def delete_file(self, fname, multiple, yes_to_all): - """Delete file""" - if multiple: - pj_buttons = QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel - else: - pj_buttons = QMessageBox.Yes|QMessageBox.No - project = self.get_source_project(fname) - if project.is_root_path(fname): - answer = QMessageBox.warning(self, _("Delete"), - _("Do you really want " - "to delete project %s?

    " - "Note: project files won't be deleted from " - "disk.") % project.name, pj_buttons) - if answer == QMessageBox.Yes: - self.remove_projects([project]) - return yes_to_all - else: - return FilteredDirView.delete_file(self, fname, multiple, - yes_to_all) - - def add_to_path(self, fnames): - """Add fnames to path""" - indexes = [] - for path in fnames: - project = self.get_source_project(path) - if project.add_to_pythonpath(path): - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) - indexes.append(self.get_index(path)) - if indexes: - self.reset_icon_provider() - for index in indexes: - self.update(index) - - def remove_path_from_project_pythonpath(self, project, path): - """Remove path from project's PYTHONPATH""" - ok = project.remove_from_pythonpath(path) - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) - return ok - - def remove_from_path(self, fnames): - """Remove from path""" - indexes = [] - for path in fnames: - project = self.get_source_project(path) - if self.remove_path_from_project_pythonpath(project, path): - indexes.append(self.get_index(path)) - if indexes: - self.reset_icon_provider() - for index in indexes: - self.update(index) - - def manage_path(self, projects): - """Manage path""" - for project in projects: - pathlist = project.get_pythonpath() - dlg = PathManager(self, pathlist, sync=False) - dlg.exec_() - project.set_pythonpath(dlg.get_path_list()) - self.parent_widget.emit(SIGNAL("pythonpath_changed()")) - - def edit_related_projects(self, projects): - """Edit related projects""" - title = _('Related projects') - for project in projects: - related_projects = project.get_related_projects() - data = [] - other_projects = self.workspace.get_other_projects(project) - for proj in other_projects: - name = proj.name - data.append((name, name in related_projects)) - comment = _("Select projects which are related to " - "%s") % project.name - result = fedit(data, title=title, comment=comment) - if result is not None: - related_projects = [] - for index, is_related in enumerate(result): - if is_related: - name = other_projects[index].name - related_projects.append(name) - project.set_related_projects(related_projects) - - def show_properties(self, fnames): - """Show properties""" - pathlist = sorted(fnames) - dirlist = [path for path in pathlist if osp.isdir(path)] - for path in pathlist[:]: - for folder in dirlist: - if path != folder and path.startswith(folder): - pathlist.pop(pathlist.index(path)) - files, lines = 0, 0 - for path in pathlist: - f, l = misc.count_lines(path) - files += f - lines += l - QMessageBox.information(self, _("Project Explorer"), - _("Statistics on source files only:
    " - "(Python, C/C++, Fortran)

    " - "%s files.
    " - "%s lines of code." - ) % (str(files), str(lines))) - - #---- Internal drag & drop - def dragMoveEvent(self, event): - """Reimplement Qt method""" - index = self.indexAt(event.pos()) - if index: - dst = self.get_filename(index) - if osp.isdir(dst): - event.acceptProposedAction() - else: - event.ignore() - else: - event.ignore() - - def dropEvent(self, event): - """Reimplement Qt method""" - event.ignore() - action = event.dropAction() - if action not in (Qt.MoveAction, Qt.CopyAction): - return - -# # QTreeView must not remove the source items even in MoveAction mode: -# event.setDropAction(Qt.CopyAction) - - dst = self.get_filename(self.indexAt(event.pos())) - yes_to_all, no_to_all = None, None - src_list = [to_text_string(url.toString()) - for url in event.mimeData().urls()] - if len(src_list) > 1: - buttons = QMessageBox.Yes|QMessageBox.YesAll| \ - QMessageBox.No|QMessageBox.NoAll|QMessageBox.Cancel - else: - buttons = QMessageBox.Yes|QMessageBox.No - for src in src_list: - if src == dst: - continue - dst_fname = osp.join(dst, osp.basename(src)) - if osp.exists(dst_fname): - if yes_to_all is not None or no_to_all is not None: - if no_to_all: - continue - elif osp.isfile(dst_fname): - answer = QMessageBox.warning(self, _('Project explorer'), - _('File %s already exists.
    ' - 'Do you want to overwrite it?') % dst_fname, - buttons) - if answer == QMessageBox.No: - continue - elif answer == QMessageBox.Cancel: - break - elif answer == QMessageBox.YesAll: - yes_to_all = True - elif answer == QMessageBox.NoAll: - no_to_all = True - continue - else: - QMessageBox.critical(self, _('Project explorer'), - _('Folder %s already exists.' - ) % dst_fname, QMessageBox.Ok) - event.setDropAction(Qt.CopyAction) - return - try: - if action == Qt.CopyAction: - if osp.isfile(src): - shutil.copy(src, dst) - else: - shutil.copytree(src, dst) - else: - if osp.isfile(src): - misc.move_file(src, dst) - else: - shutil.move(src, dst) - self.parent_widget.emit(SIGNAL("removed(QString)"), src) - except EnvironmentError as error: - if action == Qt.CopyAction: - action_str = _('copy') - else: - action_str = _('move') - QMessageBox.critical(self, _("Project Explorer"), - _("Unable to %s %s" - "

    Error message:
    %s" - ) % (action_str, src, - to_text_string(error))) - - -class WorkspaceSelector(QWidget): - """Workspace selector widget""" - TITLE = _('Select an existing workspace directory, or create a new one') - TIP = _("What is the workspace?" - "

    " - "A Spyder workspace is a directory on your filesystem that " - "contains Spyder projects and .spyderworkspace configuration " - "file." - "

    " - "A Spyder project is a directory with source code (and other " - "related files) and a configuration file (named " - ".spyderproject) with project settings (PYTHONPATH, linked " - "projects, ...).
    " - ) - - def __init__(self, parent): - super(WorkspaceSelector, self).__init__(parent) - self.browse_btn = None - self.create_btn = None - self.line_edit = None - self.first_time = True - - def set_workspace(self, path): - """Set workspace directory""" - self.line_edit.setText(path) - - def setup_widget(self): - """Setup workspace selector widget""" - self.line_edit = QLineEdit() - self.line_edit.setAlignment(Qt.AlignRight) - self.line_edit.setToolTip(_("This is the current workspace directory")\ - +'

    '+self.TIP) - self.line_edit.setReadOnly(True) - self.line_edit.setDisabled(True) - self.browse_btn = QPushButton(get_std_icon('DirOpenIcon'), "", self) - self.browse_btn.setToolTip(self.TITLE) - self.connect(self.browse_btn, SIGNAL("clicked()"), - self.select_directory) - layout = QHBoxLayout() - layout.addWidget(self.line_edit) - layout.addWidget(self.browse_btn) - layout.setContentsMargins(0, 0, 0, 0) - self.setLayout(layout) - - def select_directory(self): - """Select directory""" - if self.first_time: - QMessageBox.information(self, self.TITLE, self.TIP) - self.first_time = False - basedir = to_text_string(self.line_edit.text()) - if not osp.isdir(basedir): - basedir = getcwd() - while True: - self.parent().emit(SIGNAL('redirect_stdio(bool)'), False) - directory = getexistingdirectory(self, self.TITLE, basedir) - self.parent().emit(SIGNAL('redirect_stdio(bool)'), True) - if not directory: - break - path = osp.join(directory, Workspace.CONFIG_NAME) - if not osp.isfile(path): - answer = QMessageBox.warning(self, self.TITLE, - _("The following directory is not a Spyder " - "workspace:
    %s

    Do you want to " - "create a new workspace in this directory?" - ) % directory, QMessageBox.Yes|QMessageBox.No) - if answer == QMessageBox.No: - continue - directory = osp.abspath(osp.normpath(directory)) - self.set_workspace(directory) - self.emit(SIGNAL('selected_workspace(QString)'), directory) - break - - -class ProjectExplorerWidget(QWidget): - """ - Project Explorer - - Signals: - sig_open_file - SIGNAL("create_module(QString)") - SIGNAL("pythonpath_changed()") - SIGNAL("renamed(QString,QString)") - SIGNAL("removed(QString)") - """ - sig_option_changed = Signal(str, object) - sig_open_file = Signal(str) - - def __init__(self, parent, name_filters=['*.py', '*.pyw'], - show_all=False, show_hscrollbar=True): - QWidget.__init__(self, parent) - self.treewidget = None - self.selector = None - self.setup_layout(name_filters, show_all, show_hscrollbar) - - def setup_layout(self, name_filters, show_all, show_hscrollbar): - """Setup project explorer widget layout""" - self.selector = WorkspaceSelector(self) - self.selector.setup_widget() - self.connect(self.selector, SIGNAL('selected_workspace(QString)'), - self.set_workspace) - - self.treewidget = ExplorerTreeWidget(self, - show_hscrollbar=show_hscrollbar) - self.treewidget.setup(name_filters=name_filters, show_all=show_all) - self.connect(self.treewidget, SIGNAL('select_workspace()'), - self.selector.select_directory) - - layout = QVBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(self.selector) - layout.addWidget(self.treewidget) - self.setLayout(layout) - - def set_workspace(self, path): - """Set current workspace""" - path = osp.normpath(to_text_string(path)) - if path is not None and osp.isdir(path): - self.treewidget.set_workspace(path) - self.selector.set_workspace(path) - - def check_for_io_errors(self): - """Check for I/O errors that may occured when loading/saving - projects or the workspace itself and warn the user""" - self.treewidget.check_for_io_errors() - - def get_workspace(self): - """Return current workspace path""" - return self.treewidget.get_workspace() - - def closing_widget(self): - """Perform actions before widget is closed""" - pass - - def add_project(self, project): - """Add project""" - return self.treewidget.add_project(project) - - def get_pythonpath(self): - """Return PYTHONPATH""" - return self.treewidget.get_pythonpath() - - def get_source_project(self, fname): - """Return project which contains source *fname*""" - return self.treewidget.get_source_project(fname) - - -class Test(QWidget): - def __init__(self): - QWidget.__init__(self) - vlayout = QVBoxLayout() - self.setLayout(vlayout) - - self.explorer = ProjectExplorerWidget(None, show_all=True) - self.explorer.set_workspace(r'D:/Python') -# p1 = self.explorer.add_project(r"D:/Python/spyder") -# p1.set_pythonpath([r"D:\Python\spyder\spyderlib"]) -# p1.save() -# self.treewidget.close_projects(p1) -# _p2 = self.explorer.add_project(r"D:\Python\test_project") - - vlayout.addWidget(self.explorer) - - hlayout1 = QHBoxLayout() - vlayout.addLayout(hlayout1) - label = QLabel("Open file:") - label.setAlignment(Qt.AlignRight) - hlayout1.addWidget(label) - self.label1 = QLabel() - hlayout1.addWidget(self.label1) - self.explorer.sig_open_file.connect(self.label1.setText) - - hlayout3 = QHBoxLayout() - vlayout.addLayout(hlayout3) - label = QLabel("Option changed:") - label.setAlignment(Qt.AlignRight) - hlayout3.addWidget(label) - self.label3 = QLabel() - hlayout3.addWidget(self.label3) - self.explorer.sig_option_changed.connect( - lambda x, y: self.label3.setText('option_changed: %r, %r' % (x, y))) - - -if __name__ == "__main__": - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - test = Test() - test.resize(640, 480) - test.show() - app.exec_() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/pydocgui.py spyder-3.0.2+dfsg1/spyderlib/widgets/pydocgui.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/pydocgui.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/pydocgui.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""pydoc widget""" - -from spyderlib.qt.QtGui import QApplication, QCursor -from spyderlib.qt.QtCore import QThread, QUrl, Qt, SIGNAL - -import sys -import os.path as osp - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.widgets.browser import WebBrowser -from spyderlib.utils.misc import select_port -from spyderlib.py3compat import to_text_string, PY3 - - -class PydocServer(QThread): - """Pydoc server""" - def __init__(self, port=7464): - QThread.__init__(self) - self.port = port - self.server = None - self.complete = False - - def run(self): - import pydoc - if PY3: - # Python 3 - self.callback(pydoc._start_server(pydoc._url_handler, self.port)) - else: - # Python 2 - pydoc.serve(self.port, self.callback, self.completer) - - def callback(self, server): - self.server = server - self.emit(SIGNAL('server_started()')) - - def completer(self): - self.complete = True - - def quit_server(self): - if PY3: - # Python 3 - if self.server.serving: - self.server.stop() - else: - # Python 2 - self.server.quit = 1 - - -class PydocBrowser(WebBrowser): - """ - pydoc widget - """ - DEFAULT_PORT = 30128 - - def __init__(self, parent): - WebBrowser.__init__(self, parent) - self.server = None - self.port = None - - def initialize(self): - """Start pydoc server""" - QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - QApplication.processEvents() - self.start_server() - # Initializing continues in `initialize_continued` method... - - def initialize_continued(self): - """Load home page""" - self.go_home() - QApplication.restoreOverrideCursor() - - def is_server_running(self): - """Return True if pydoc server is already running""" - return self.server is not None - - def closeEvent(self, event): - self.server.quit_server() -# while not self.server.complete: #XXX Is it really necessary? -# pass - event.accept() - - #------ Public API ----------------------------------------------------- - def start_server(self): - """Start pydoc server""" - if self.server is None: - self.port = select_port(default_port=self.DEFAULT_PORT) - self.set_home_url('http://localhost:%d/' % self.port) - elif self.server.isRunning(): - self.disconnect(self.server, SIGNAL('server_started()'), - self.initialize_continued) - self.server.quit() - self.server = PydocServer(port=self.port) - self.connect(self.server, SIGNAL('server_started()'), - self.initialize_continued) - self.server.start() - - #------ WebBrowser API ----------------------------------------------------- - def get_label(self): - """Return address label text""" - return _("Module or package:") - - def reload(self): - """Reload page""" - self.start_server() - WebBrowser.reload(self) - - def text_to_url(self, text): - """Convert text address into QUrl object""" - if text.startswith('/'): - text = text[1:] - return QUrl(self.home_url.toString()+text+'.html') - - def url_to_text(self, url): - """Convert QUrl object to displayed text in combo box""" - return osp.splitext(to_text_string(url.path()))[0][1:] - - -def main(): - """Run web browser""" - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - widget = PydocBrowser(None) - widget.show() - widget.initialize() - sys.exit(app.exec_()) - -if __name__ == '__main__': - main() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/shell.py spyder-3.0.2+dfsg1/spyderlib/widgets/shell.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/shell.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/shell.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1029 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Shell widgets: base, python and terminal""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -import keyword -import locale -import os -import os.path as osp -import re -import sys -import time - -from spyderlib.qt.QtGui import (QMenu, QApplication, QToolTip, QKeySequence, - QMessageBox, QTextCursor, QTextCharFormat) -from spyderlib.qt.QtCore import (Qt, QCoreApplication, QTimer, SIGNAL, - Property) -from spyderlib.qt.compat import getsavefilename - -# Local import -from spyderlib.baseconfig import get_conf_path, _, STDERR, DEBUG -from spyderlib.config import CONF -from spyderlib.guiconfig import get_font, create_shortcut, get_shortcut -from spyderlib.utils import encoding -from spyderlib.utils.qthelpers import (keybinding, create_action, add_actions, - restore_keyevent, get_icon) -from spyderlib.widgets.sourcecode.base import ConsoleBaseWidget -from spyderlib.widgets.mixins import (InspectObjectMixin, TracebackLinksMixin, - SaveHistoryMixin) -from spyderlib.py3compat import (is_text_string, to_text_string, builtins, - is_string, PY3) - - -class ShellBaseWidget(ConsoleBaseWidget, SaveHistoryMixin): - """ - Shell base widget - """ - - def __init__(self, parent, history_filename, profile=False): - """ - parent : specifies the parent widget - """ - ConsoleBaseWidget.__init__(self, parent) - SaveHistoryMixin.__init__(self) - - # Prompt position: tuple (line, index) - self.current_prompt_pos = None - self.new_input_line = True - - # History - self.histidx = None - self.hist_wholeline = False - assert is_text_string(history_filename) - self.history_filename = history_filename - self.history = self.load_history() - - # Session - self.historylog_filename = CONF.get('main', 'historylog_filename', - get_conf_path('history.log')) - - # Context menu - self.menu = None - self.setup_context_menu() - - # Simple profiling test - self.profile = profile - - # Buffer to increase performance of write/flush operations - self.__buffer = [] - self.__timestamp = 0.0 - self.__flushtimer = QTimer(self) - self.__flushtimer.setSingleShot(True) - self.connect(self.__flushtimer, SIGNAL('timeout()'), self.flush) - - # Give focus to widget - self.setFocus() - - # Completion - completion_size = CONF.get('shell_appearance', 'completion/size') - completion_font = get_font('console') - self.completion_widget.setup_appearance(completion_size, - completion_font) - # Cursor width - self.setCursorWidth( CONF.get('shell_appearance', 'cursor/width') ) - - def toggle_wrap_mode(self, enable): - """Enable/disable wrap mode""" - self.set_wrap_mode('character' if enable else None) - - def set_font(self, font): - """Set shell styles font""" - self.setFont(font) - self.set_pythonshell_font(font) - cursor = self.textCursor() - cursor.select(QTextCursor.Document) - charformat = QTextCharFormat() - charformat.setFontFamily(font.family()) - charformat.setFontPointSize(font.pointSize()) - cursor.mergeCharFormat(charformat) - - - #------ Context menu - def setup_context_menu(self): - """Setup shell context menu""" - self.menu = QMenu(self) - self.cut_action = create_action(self, _("Cut"), - shortcut=keybinding('Cut'), - icon=get_icon('editcut.png'), - triggered=self.cut) - self.copy_action = create_action(self, _("Copy"), - shortcut=keybinding('Copy'), - icon=get_icon('editcopy.png'), - triggered=self.copy) - paste_action = create_action(self, _("Paste"), - shortcut=keybinding('Paste'), - icon=get_icon('editpaste.png'), - triggered=self.paste) - save_action = create_action(self, _("Save history log..."), - icon=get_icon('filesave.png'), - tip=_("Save current history log (i.e. all " - "inputs and outputs) in a text file"), - triggered=self.save_historylog) - self.delete_action = create_action(self, _("Delete"), - shortcut=keybinding('Delete'), - icon=get_icon('editdelete.png'), - triggered=self.delete) - selectall_action = create_action(self, _("Select All"), - shortcut=keybinding('SelectAll'), - icon=get_icon('selectall.png'), - triggered=self.selectAll) - add_actions(self.menu, (self.cut_action, self.copy_action, - paste_action, self.delete_action, None, - selectall_action, None, save_action) ) - - def contextMenuEvent(self, event): - """Reimplement Qt method""" - state = self.has_selected_text() - self.copy_action.setEnabled(state) - self.cut_action.setEnabled(state) - self.delete_action.setEnabled(state) - self.menu.popup(event.globalPos()) - event.accept() - - - #------ Input buffer - def get_current_line_from_cursor(self): - return self.get_text('cursor', 'eof') - - def _select_input(self): - """Select current line (without selecting console prompt)""" - line, index = self.get_position('eof') - if self.current_prompt_pos is None: - pline, pindex = line, index - else: - pline, pindex = self.current_prompt_pos - self.setSelection(pline, pindex, line, index) - - def clear_line(self): - """Clear current line (without clearing console prompt)""" - if self.current_prompt_pos is not None: - self.remove_text(self.current_prompt_pos, 'eof') - - def clear_terminal(self): - """ - Clear terminal window - Child classes reimplement this method to write prompt - """ - self.clear() - - # The buffer being edited - def _set_input_buffer(self, text): - """Set input buffer""" - if self.current_prompt_pos is not None: - self.replace_text(self.current_prompt_pos, 'eol', text) - else: - self.insert(text) - self.set_cursor_position('eof') - - def _get_input_buffer(self): - """Return input buffer""" - input_buffer = '' - if self.current_prompt_pos is not None: - input_buffer = self.get_text(self.current_prompt_pos, 'eol') - input_buffer = input_buffer.replace(os.linesep, '\n') - return input_buffer - - input_buffer = Property("QString", _get_input_buffer, _set_input_buffer) - - - #------ Prompt - def new_prompt(self, prompt): - """ - Print a new prompt and save its (line, index) position - """ - if self.get_cursor_line_column()[1] != 0: - self.write('\n') - self.write(prompt, prompt=True) - # now we update our cursor giving end of prompt - self.current_prompt_pos = self.get_position('cursor') - self.ensureCursorVisible() - self.new_input_line = False - - def check_selection(self): - """ - Check if selected text is r/w, - otherwise remove read-only parts of selection - """ - if self.current_prompt_pos is None: - self.set_cursor_position('eof') - else: - self.truncate_selection(self.current_prompt_pos) - - - #------ Copy / Keyboard interrupt - def copy(self): - """Copy text to clipboard... or keyboard interrupt""" - if self.has_selected_text(): - ConsoleBaseWidget.copy(self) - elif not sys.platform == 'darwin': - self.interrupt() - - def interrupt(self): - """Keyboard interrupt""" - self.emit(SIGNAL("keyboard_interrupt()")) - - def cut(self): - """Cut text""" - self.check_selection() - if self.has_selected_text(): - ConsoleBaseWidget.cut(self) - - def delete(self): - """Remove selected text""" - self.check_selection() - if self.has_selected_text(): - ConsoleBaseWidget.remove_selected_text(self) - - def save_historylog(self): - """Save current history log (all text in console)""" - title = _("Save history log") - self.emit(SIGNAL('redirect_stdio(bool)'), False) - filename, _selfilter = getsavefilename(self, title, - self.historylog_filename, "%s (*.log)" % _("History logs")) - self.emit(SIGNAL('redirect_stdio(bool)'), True) - if filename: - filename = osp.normpath(filename) - try: - encoding.write(to_text_string(self.get_text_with_eol()), - filename) - self.historylog_filename = filename - CONF.set('main', 'historylog_filename', filename) - except EnvironmentError as error: - QMessageBox.critical(self, title, - _("Unable to save file '%s'" - "

    Error message:
    %s" - ) % (osp.basename(filename), - to_text_string(error))) - - - #------ Basic keypress event handler - def on_enter(self, command): - """on_enter""" - self.execute_command(command) - - def execute_command(self, command): - self.emit(SIGNAL("execute(QString)"), command) - self.add_to_history(command) - self.new_input_line = True - - def on_new_line(self): - """On new input line""" - self.set_cursor_position('eof') - self.current_prompt_pos = self.get_position('cursor') - self.new_input_line = False - - def paste(self): - """Reimplemented slot to handle multiline paste action""" - if self.new_input_line: - self.on_new_line() - ConsoleBaseWidget.paste(self) - - def keyPressEvent(self, event): - """ - Reimplement Qt Method - Basic keypress event handler - (reimplemented in InternalShell to add more sophisticated features) - """ - if self.preprocess_keyevent(event): - # Event was accepted in self.preprocess_keyevent - return - self.postprocess_keyevent(event) - - def preprocess_keyevent(self, event): - """Pre-process keypress event: - return True if event is accepted, false otherwise""" - # Copy must be done first to be able to copy read-only text parts - # (otherwise, right below, we would remove selection - # if not on current line) - ctrl = event.modifiers() & Qt.ControlModifier - meta = event.modifiers() & Qt.MetaModifier # meta=ctrl in OSX - if event.key() == Qt.Key_C and \ - ((Qt.MetaModifier | Qt.ControlModifier) & event.modifiers()): - if meta and sys.platform == 'darwin': - self.interrupt() - elif ctrl: - self.copy() - event.accept() - return True - - if self.new_input_line and ( len(event.text()) or event.key() in \ - (Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right) ): - self.on_new_line() - - return False - - def postprocess_keyevent(self, event): - """Post-process keypress event: - in InternalShell, this is method is called when shell is ready""" - event, text, key, ctrl, shift = restore_keyevent(event) - - # Is cursor on the last line? and after prompt? - if len(text): - #XXX: Shouldn't it be: `if len(unicode(text).strip(os.linesep))` ? - if self.has_selected_text(): - self.check_selection() - self.restrict_cursor_position(self.current_prompt_pos, 'eof') - - cursor_position = self.get_position('cursor') - - if key in (Qt.Key_Return, Qt.Key_Enter): - if self.is_cursor_on_last_line(): - self._key_enter() - # add and run selection - else: - self.insert_text(self.get_selected_text(), at_end=True) - - elif key == Qt.Key_Insert and not shift and not ctrl: - self.setOverwriteMode(not self.overwriteMode()) - - elif key == Qt.Key_Delete: - if self.has_selected_text(): - self.check_selection() - self.remove_selected_text() - elif self.is_cursor_on_last_line(): - self.stdkey_clear() - - elif key == Qt.Key_Backspace: - self._key_backspace(cursor_position) - - elif key == Qt.Key_Tab: - self._key_tab() - - elif key == Qt.Key_Space and ctrl: - self._key_ctrl_space() - - elif key == Qt.Key_Left: - if self.current_prompt_pos == cursor_position: - # Avoid moving cursor on prompt - return - method = self.extend_selection_to_next if shift \ - else self.move_cursor_to_next - method('word' if ctrl else 'character', direction='left') - - elif key == Qt.Key_Right: - if self.is_cursor_at_end(): - return - method = self.extend_selection_to_next if shift \ - else self.move_cursor_to_next - method('word' if ctrl else 'character', direction='right') - - elif (key == Qt.Key_Home) or ((key == Qt.Key_Up) and ctrl): - self._key_home(shift, ctrl) - - elif (key == Qt.Key_End) or ((key == Qt.Key_Down) and ctrl): - self._key_end(shift, ctrl) - - elif key == Qt.Key_Up: - if not self.is_cursor_on_last_line(): - self.set_cursor_position('eof') - y_cursor = self.get_coordinates(cursor_position)[1] - y_prompt = self.get_coordinates(self.current_prompt_pos)[1] - if y_cursor > y_prompt: - self.stdkey_up(shift) - else: - self.browse_history(backward=True) - - elif key == Qt.Key_Down: - if not self.is_cursor_on_last_line(): - self.set_cursor_position('eof') - y_cursor = self.get_coordinates(cursor_position)[1] - y_end = self.get_coordinates('eol')[1] - if y_cursor < y_end: - self.stdkey_down(shift) - else: - self.browse_history(backward=False) - - elif key in (Qt.Key_PageUp, Qt.Key_PageDown): - #XXX: Find a way to do this programmatically instead of calling - # widget keyhandler (this won't work if the *event* is coming from - # the event queue - i.e. if the busy buffer is ever implemented) - ConsoleBaseWidget.keyPressEvent(self, event) - - elif key == Qt.Key_Escape and shift: - self.clear_line() - - elif key == Qt.Key_Escape: - self._key_escape() - - elif key == Qt.Key_L and ctrl: - self.clear_terminal() - - elif key == Qt.Key_V and ctrl: - self.paste() - - elif key == Qt.Key_X and ctrl: - self.cut() - - elif key == Qt.Key_Z and ctrl: - self.undo() - - elif key == Qt.Key_Y and ctrl: - self.redo() - - elif key == Qt.Key_A and ctrl: - self.selectAll() - - elif key == Qt.Key_Question and not self.has_selected_text(): - self._key_question(text) - - elif key == Qt.Key_ParenLeft and not self.has_selected_text(): - self._key_parenleft(text) - - elif key == Qt.Key_Period and not self.has_selected_text(): - self._key_period(text) - - elif len(text) and not self.isReadOnly(): - self.hist_wholeline = False - self.insert_text(text) - self._key_other(text) - - else: - # Let the parent widget handle the key press event - ConsoleBaseWidget.keyPressEvent(self, event) - - - #------ Key handlers - def _key_enter(self): - command = self.input_buffer - self.insert_text('\n', at_end=True) - self.on_enter(command) - self.flush() - def _key_other(self, text): - raise NotImplementedError - def _key_backspace(self, cursor_position): - raise NotImplementedError - def _key_tab(self): - raise NotImplementedError - def _key_ctrl_space(self): - raise NotImplementedError - def _key_home(self, shift, ctrl): - if self.is_cursor_on_last_line(): - self.stdkey_home(shift, ctrl, self.current_prompt_pos) - def _key_end(self, shift, ctrl): - if self.is_cursor_on_last_line(): - self.stdkey_end(shift, ctrl) - def _key_pageup(self): - raise NotImplementedError - def _key_pagedown(self): - raise NotImplementedError - def _key_escape(self): - raise NotImplementedError - def _key_question(self, text): - raise NotImplementedError - def _key_parenleft(self, text): - raise NotImplementedError - def _key_period(self, text): - raise NotImplementedError - - - #------ History Management - def load_history(self): - """Load history from a .py file in user home directory""" - if osp.isfile(self.history_filename): - rawhistory, _ = encoding.readlines(self.history_filename) - rawhistory = [line.replace('\n', '') for line in rawhistory] - if rawhistory[1] != self.INITHISTORY[1]: - rawhistory[1] = self.INITHISTORY[1] - else: - rawhistory = self.INITHISTORY - history = [line for line in rawhistory \ - if line and not line.startswith('#')] - - # Truncating history to X entries: - while len(history) >= CONF.get('historylog', 'max_entries'): - del history[0] - while rawhistory[0].startswith('#'): - del rawhistory[0] - del rawhistory[0] - # Saving truncated history: - encoding.writelines(rawhistory, self.history_filename) - return history - - def browse_history(self, backward): - """Browse history""" - if self.is_cursor_before('eol') and self.hist_wholeline: - self.hist_wholeline = False - tocursor = self.get_current_line_to_cursor() - text, self.histidx = self.__find_in_history(tocursor, - self.histidx, backward) - if text is not None: - if self.hist_wholeline: - self.clear_line() - self.insert_text(text) - else: - cursor_position = self.get_position('cursor') - # Removing text from cursor to the end of the line - self.remove_text('cursor', 'eol') - # Inserting history text - self.insert_text(text) - self.set_cursor_position(cursor_position) - - def __find_in_history(self, tocursor, start_idx, backward): - """Find text 'tocursor' in history, from index 'start_idx'""" - if start_idx is None: - start_idx = len(self.history) - # Finding text in history - step = -1 if backward else 1 - idx = start_idx - if len(tocursor) == 0 or self.hist_wholeline: - idx += step - if idx >= len(self.history) or len(self.history) == 0: - return "", len(self.history) - elif idx < 0: - idx = 0 - self.hist_wholeline = True - return self.history[idx], idx - else: - for index in range(len(self.history)): - idx = (start_idx+step*(index+1)) % len(self.history) - entry = self.history[idx] - if entry.startswith(tocursor): - return entry[len(tocursor):], idx - else: - return None, start_idx - - - #------ Simulation standards input/output - def write_error(self, text): - """Simulate stderr""" - self.flush() - self.write(text, flush=True, error=True) - if DEBUG: - STDERR.write(text) - - def write(self, text, flush=False, error=False, prompt=False): - """Simulate stdout and stderr""" - if prompt: - self.flush() - if not is_string(text): - # This test is useful to discriminate QStrings from decoded str - text = to_text_string(text) - self.__buffer.append(text) - ts = time.time() - if flush or prompt: - self.flush(error=error, prompt=prompt) - elif ts - self.__timestamp > 0.05: - self.flush(error=error) - self.__timestamp = ts - # Timer to flush strings cached by last write() operation in series - self.__flushtimer.start(50) - - def flush(self, error=False, prompt=False): - """Flush buffer, write text to console""" - # Fix for Issue 2452 - if PY3: - try: - text = "".join(self.__buffer) - except TypeError: - text = b"".join(self.__buffer) - try: - text = text.decode( locale.getdefaultlocale()[1] ) - except: - pass - else: - text = "".join(self.__buffer) - - self.__buffer = [] - self.insert_text(text, at_end=True, error=error, prompt=prompt) - QCoreApplication.processEvents() - self.repaint() - # Clear input buffer: - self.new_input_line = True - - - #------ Text Insertion - def insert_text(self, text, at_end=False, error=False, prompt=False): - """ - Insert text at the current cursor position - or at the end of the command line - """ - if at_end: - # Insert text at the end of the command line - self.append_text_to_shell(text, error, prompt) - else: - # Insert text at current cursor position - ConsoleBaseWidget.insert_text(self, text) - - - #------ Re-implemented Qt Methods - def focusNextPrevChild(self, next): - """ - Reimplemented to stop Tab moving to the next window - """ - if next: - return False - return ConsoleBaseWidget.focusNextPrevChild(self, next) - - - #------ Drag and drop - def dragEnterEvent(self, event): - """Drag and Drop - Enter event""" - event.setAccepted(event.mimeData().hasFormat("text/plain")) - - def dragMoveEvent(self, event): - """Drag and Drop - Move event""" - if (event.mimeData().hasFormat("text/plain")): - event.setDropAction(Qt.MoveAction) - event.accept() - else: - event.ignore() - - def dropEvent(self, event): - """Drag and Drop - Drop event""" - if (event.mimeData().hasFormat("text/plain")): - text = to_text_string(event.mimeData().text()) - if self.new_input_line: - self.on_new_line() - self.insert_text(text, at_end=True) - self.setFocus() - event.setDropAction(Qt.MoveAction) - event.accept() - else: - event.ignore() - - def drop_pathlist(self, pathlist): - """Drop path list""" - raise NotImplementedError - - -# Example how to debug complex interclass call chains: -# -# from spyderlib.utils.debug import log_methods_calls -# log_methods_calls('log.log', ShellBaseWidget) - -class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget, - InspectObjectMixin): - """Python shell widget""" - QT_CLASS = ShellBaseWidget - - INITHISTORY = ['# -*- coding: utf-8 -*-', - '# *** Spyder Python Console History Log ***',] - SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime()) - - def __init__(self, parent, history_filename, profile=False): - ShellBaseWidget.__init__(self, parent, history_filename, profile) - TracebackLinksMixin.__init__(self) - InspectObjectMixin.__init__(self) - - # Local shortcuts - self.shortcuts = self.create_shortcuts() - - def create_shortcuts(self): - inspectsc = create_shortcut(self.inspect_current_object, - context='Console', - name='Inspect current object', - parent=self) - return [inspectsc] - - def get_shortcut_data(self): - """ - Returns shortcut data, a list of tuples (shortcut, text, default) - shortcut (QShortcut or QAction instance) - text (string): action/shortcut description - default (string): default key sequence - """ - return [sc.data for sc in self.shortcuts] - - #------ Context menu - def setup_context_menu(self): - """Reimplements ShellBaseWidget method""" - ShellBaseWidget.setup_context_menu(self) - self.copy_without_prompts_action = create_action(self, - _("Copy without prompts"), - icon=get_icon('copywop.png'), - triggered=self.copy_without_prompts) - clear_line_action = create_action(self, _("Clear line"), - QKeySequence(get_shortcut('console', - 'Clear line')), - icon=get_icon('eraser.png'), - tip=_("Clear line"), - triggered=self.clear_line) - clear_action = create_action(self, _("Clear shell"), - QKeySequence(get_shortcut('console', - 'Clear shell')), - icon=get_icon('clear.png'), - tip=_("Clear shell contents " - "('cls' command)"), - triggered=self.clear_terminal) - add_actions(self.menu, (self.copy_without_prompts_action, - clear_line_action, clear_action)) - - def contextMenuEvent(self, event): - """Reimplements ShellBaseWidget method""" - state = self.has_selected_text() - self.copy_without_prompts_action.setEnabled(state) - ShellBaseWidget.contextMenuEvent(self, event) - - def copy_without_prompts(self): - """Copy text to clipboard without prompts""" - text = self.get_selected_text() - lines = text.split(os.linesep) - for index, line in enumerate(lines): - if line.startswith('>>> ') or line.startswith('... '): - lines[index] = line[4:] - text = os.linesep.join(lines) - QApplication.clipboard().setText(text) - - - #------ Key handlers - def postprocess_keyevent(self, event): - """Process keypress event""" - ShellBaseWidget.postprocess_keyevent(self, event) - if QToolTip.isVisible(): - _event, _text, key, _ctrl, _shift = restore_keyevent(event) - self.hide_tooltip_if_necessary(key) - - def _key_other(self, text): - """1 character key""" - if self.is_completion_widget_visible(): - self.completion_text += text - - def _key_backspace(self, cursor_position): - """Action for Backspace key""" - if self.has_selected_text(): - self.check_selection() - self.remove_selected_text() - elif self.current_prompt_pos == cursor_position: - # Avoid deleting prompt - return - elif self.is_cursor_on_last_line(): - self.stdkey_backspace() - if self.is_completion_widget_visible(): - # Removing only last character because if there was a selection - # the completion widget would have been canceled - self.completion_text = self.completion_text[:-1] - - def _key_tab(self): - """Action for TAB key""" - if self.is_cursor_on_last_line(): - empty_line = not self.get_current_line_to_cursor().strip() - if empty_line: - self.stdkey_tab() - else: - self.show_code_completion(automatic=False) - - def _key_ctrl_space(self): - """Action for Ctrl+Space""" - if not self.is_completion_widget_visible(): - self.show_code_completion(automatic=False) - - def _key_pageup(self): - """Action for PageUp key""" - pass - - def _key_pagedown(self): - """Action for PageDown key""" - pass - - def _key_escape(self): - """Action for ESCAPE key""" - if self.is_completion_widget_visible(): - self.hide_completion_widget() - - def _key_question(self, text): - """Action for '?'""" - if self.get_current_line_to_cursor(): - last_obj = self.get_last_obj() - if last_obj and not last_obj.isdigit(): - self.show_object_info(last_obj) - self.insert_text(text) - # In case calltip and completion are shown at the same time: - if self.is_completion_widget_visible(): - self.completion_text += '?' - - def _key_parenleft(self, text): - """Action for '('""" - self.hide_completion_widget() - if self.get_current_line_to_cursor(): - last_obj = self.get_last_obj() - if last_obj and not last_obj.isdigit(): - self.insert_text(text) - self.show_object_info(last_obj, call=True) - return - self.insert_text(text) - - def _key_period(self, text): - """Action for '.'""" - self.insert_text(text) - if self.codecompletion_auto: - # Enable auto-completion only if last token isn't a float - last_obj = self.get_last_obj() - if last_obj and not last_obj.isdigit(): - self.show_code_completion(automatic=True) - - - #------ Paste - def paste(self): - """Reimplemented slot to handle multiline paste action""" - text = to_text_string(QApplication.clipboard().text()) - if len(text.splitlines()) > 1: - # Multiline paste - if self.new_input_line: - self.on_new_line() - self.remove_selected_text() # Remove selection, eventually - end = self.get_current_line_from_cursor() - lines = self.get_current_line_to_cursor() + text + end - self.clear_line() - self.execute_lines(lines) - self.move_cursor(-len(end)) - else: - # Standard paste - ShellBaseWidget.paste(self) - - - #------ Code Completion / Calltips - # Methods implemented in child class: - # (e.g. InternalShell) - def get_dir(self, objtxt): - """Return dir(object)""" - raise NotImplementedError - def get_module_completion(self, objtxt): - """Return module completion list associated to object name""" - pass - def get_globals_keys(self): - """Return shell globals() keys""" - raise NotImplementedError - def get_cdlistdir(self): - """Return shell current directory list dir""" - raise NotImplementedError - def iscallable(self, objtxt): - """Is object callable?""" - raise NotImplementedError - def get_arglist(self, objtxt): - """Get func/method argument list""" - raise NotImplementedError - def get__doc__(self, objtxt): - """Get object __doc__""" - raise NotImplementedError - def get_doc(self, objtxt): - """Get object documentation dictionary""" - raise NotImplementedError - def get_source(self, objtxt): - """Get object source""" - raise NotImplementedError - def is_defined(self, objtxt, force_import=False): - """Return True if object is defined""" - raise NotImplementedError - - def show_code_completion(self, automatic): - """Display a completion list based on the current line""" - # Note: unicode conversion is needed only for ExternalShellBase - text = to_text_string(self.get_current_line_to_cursor()) - last_obj = self.get_last_obj() - - if text.startswith('import '): - obj_list = self.get_module_completion(text) - words = text.split(' ') - if ',' in words[-1]: - words = words[-1].split(',') - self.show_completion_list(obj_list, completion_text=words[-1], - automatic=automatic) - return - - elif text.startswith('from '): - obj_list = self.get_module_completion(text) - if obj_list is None: - return - words = text.split(' ') - if '(' in words[-1]: - words = words[:-2] + words[-1].split('(') - if ',' in words[-1]: - words = words[:-2] + words[-1].split(',') - self.show_completion_list(obj_list, completion_text=words[-1], - automatic=automatic) - return - - obj_dir = self.get_dir(last_obj) - if last_obj and obj_dir and text.endswith('.'): - self.show_completion_list(obj_dir, automatic=automatic) - return - - # Builtins and globals - if not text.endswith('.') and last_obj \ - and re.match(r'[a-zA-Z_0-9]*$', last_obj): - b_k_g = dir(builtins)+self.get_globals_keys()+keyword.kwlist - for objname in b_k_g: - if objname.startswith(last_obj) and objname != last_obj: - self.show_completion_list(b_k_g, completion_text=last_obj, - automatic=automatic) - return - else: - return - - # Looking for an incomplete completion - if last_obj is None: - last_obj = text - dot_pos = last_obj.rfind('.') - if dot_pos != -1: - if dot_pos == len(last_obj)-1: - completion_text = "" - else: - completion_text = last_obj[dot_pos+1:] - last_obj = last_obj[:dot_pos] - completions = self.get_dir(last_obj) - if completions is not None: - self.show_completion_list(completions, - completion_text=completion_text, - automatic=automatic) - return - - # Looking for ' or ": filename completion - q_pos = max([text.rfind("'"), text.rfind('"')]) - if q_pos != -1: - completions = self.get_cdlistdir() - if completions: - self.show_completion_list(completions, - completion_text=text[q_pos+1:], - automatic=automatic) - return - - #------ Drag'n Drop - def drop_pathlist(self, pathlist): - """Drop path list""" - if pathlist: - files = ["r'%s'" % path for path in pathlist] - if len(files) == 1: - text = files[0] - else: - text = "[" + ", ".join(files) + "]" - if self.new_input_line: - self.on_new_line() - self.insert_text(text) - self.setFocus() - - -class TerminalWidget(ShellBaseWidget): - """ - Terminal widget - """ - COM = 'rem' if os.name == 'nt' else '#' - INITHISTORY = ['%s *** Spyder Terminal History Log ***' % COM, COM,] - SEPARATOR = '%s%s ---(%s)---' % (os.linesep*2, COM, time.ctime()) - - def __init__(self, parent, history_filename, profile=False): - ShellBaseWidget.__init__(self, parent, history_filename, profile) - - #------ Key handlers - def _key_other(self, text): - """1 character key""" - pass - - def _key_backspace(self, cursor_position): - """Action for Backspace key""" - if self.has_selected_text(): - self.check_selection() - self.remove_selected_text() - elif self.current_prompt_pos == cursor_position: - # Avoid deleting prompt - return - elif self.is_cursor_on_last_line(): - self.stdkey_backspace() - - def _key_tab(self): - """Action for TAB key""" - if self.is_cursor_on_last_line(): - self.stdkey_tab() - - def _key_ctrl_space(self): - """Action for Ctrl+Space""" - pass - - def _key_escape(self): - """Action for ESCAPE key""" - self.clear_line() - - def _key_question(self, text): - """Action for '?'""" - self.insert_text(text) - - def _key_parenleft(self, text): - """Action for '('""" - self.insert_text(text) - - def _key_period(self, text): - """Action for '.'""" - self.insert_text(text) - - - #------ Drag'n Drop - def drop_pathlist(self, pathlist): - """Drop path list""" - if pathlist: - files = ['"%s"' % path for path in pathlist] - if len(files) == 1: - text = files[0] - else: - text = " ".join(files) - if self.new_input_line: - self.on_new_line() - self.insert_text(text) - self.setFocus() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/base.py spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/base.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/base.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/base.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1281 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""QPlainTextEdit base class""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -import os -import re -import sys - -from spyderlib.qt.QtGui import (QTextCursor, QColor, QFont, QApplication, - QTextEdit, QTextCharFormat, QToolTip, - QListWidget, QPlainTextEdit, QPalette, - QMainWindow, QTextOption, QMouseEvent, - QTextFormat, QClipboard) -from spyderlib.qt.QtCore import SIGNAL, Qt, QEventLoop, QEvent, QPoint -from spyderlib.qt.compat import to_qvariant - - -# Local imports -from spyderlib.widgets.sourcecode.terminal import ANSIEscapeCodeHandler -from spyderlib.widgets.mixins import BaseEditMixin -from spyderlib.widgets.calltip import CallTipWidget -from spyderlib.py3compat import to_text_string, str_lower, PY3 - - -class CompletionWidget(QListWidget): - """Completion list widget""" - def __init__(self, parent, ancestor): - QListWidget.__init__(self, ancestor) - self.setWindowFlags(Qt.SubWindow | Qt.FramelessWindowHint) - self.textedit = parent - self.completion_list = None - self.case_sensitive = False - self.enter_select = None - self.hide() - self.connect(self, SIGNAL("itemActivated(QListWidgetItem*)"), - self.item_selected) - - def setup_appearance(self, size, font): - self.resize(*size) - self.setFont(font) - - def show_list(self, completion_list, automatic=True): - if len(completion_list) == 1 and not automatic: - self.textedit.insert_completion(completion_list[0]) - return - - self.completion_list = completion_list - self.clear() - self.addItems(completion_list) - self.setCurrentRow(0) - - QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) - self.show() - self.setFocus() - self.raise_() - - # Retrieving current screen height - desktop = QApplication.desktop() - srect = desktop.availableGeometry(desktop.screenNumber(self)) - screen_right = srect.right() - screen_bottom = srect.bottom() - - point = self.textedit.cursorRect().bottomRight() - point.setX(point.x()+self.textedit.get_linenumberarea_width()) - point = self.textedit.mapToGlobal(point) - - # Computing completion widget and its parent right positions - comp_right = point.x()+self.width() - ancestor = self.parent() - if ancestor is None: - anc_right = screen_right - else: - anc_right = min([ancestor.x()+ancestor.width(), screen_right]) - - # Moving completion widget to the left - # if there is not enough space to the right - if comp_right > anc_right: - point.setX(point.x()-self.width()) - - # Computing completion widget and its parent bottom positions - comp_bottom = point.y()+self.height() - ancestor = self.parent() - if ancestor is None: - anc_bottom = screen_bottom - else: - anc_bottom = min([ancestor.y()+ancestor.height(), screen_bottom]) - - # Moving completion widget above if there is not enough space below - x_position = point.x() - if comp_bottom > anc_bottom: - point = self.textedit.cursorRect().topRight() - point = self.textedit.mapToGlobal(point) - point.setX(x_position) - point.setY(point.y()-self.height()) - - if ancestor is not None: - # Useful only if we set parent to 'ancestor' in __init__ - point = ancestor.mapFromGlobal(point) - self.move(point) - - if to_text_string(self.textedit.completion_text): - # When initialized, if completion text is not empty, we need - # to update the displayed list: - self.update_current() - - def hide(self): - QListWidget.hide(self) - self.textedit.setFocus() - - def keyPressEvent(self, event): - text, key = event.text(), event.key() - alt = event.modifiers() & Qt.AltModifier - shift = event.modifiers() & Qt.ShiftModifier - ctrl = event.modifiers() & Qt.ControlModifier - modifier = shift or ctrl or alt - if (key in (Qt.Key_Return, Qt.Key_Enter) and self.enter_select) \ - or key == Qt.Key_Tab: - self.item_selected() - elif key in (Qt.Key_Return, Qt.Key_Enter, - Qt.Key_Left, Qt.Key_Right) or text in ('.', ':'): - self.hide() - self.textedit.keyPressEvent(event) - elif key in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown, - Qt.Key_Home, Qt.Key_End, - Qt.Key_CapsLock) and not modifier: - QListWidget.keyPressEvent(self, event) - elif len(text) or key == Qt.Key_Backspace: - self.textedit.keyPressEvent(event) - self.update_current() - elif modifier: - self.textedit.keyPressEvent(event) - else: - self.hide() - QListWidget.keyPressEvent(self, event) - - def update_current(self): - completion_text = to_text_string(self.textedit.completion_text) - if completion_text: - for row, completion in enumerate(self.completion_list): - if not self.case_sensitive: - completion = completion.lower() - completion_text = completion_text.lower() - if completion.startswith(completion_text): - self.setCurrentRow(row) - break - else: - self.hide() - else: - self.hide() - - def focusOutEvent(self, event): - event.ignore() - # Don't hide it on Mac when main window loses focus because - # keyboard input is lost - # Fixes Issue 1318 - if sys.platform == "darwin": - if event.reason() != Qt.ActiveWindowFocusReason: - self.hide() - else: - self.hide() - - def item_selected(self, item=None): - if item is None: - item = self.currentItem() - self.textedit.insert_completion( to_text_string(item.text()) ) - self.hide() - - -class TextEditBaseWidget(QPlainTextEdit, BaseEditMixin): - """Text edit base widget""" - BRACE_MATCHING_SCOPE = ('sof', 'eof') - cell_separators = None - - def __init__(self, parent=None): - QPlainTextEdit.__init__(self, parent) - BaseEditMixin.__init__(self) - self.setAttribute(Qt.WA_DeleteOnClose) - - self.extra_selections_dict = {} - - self.connect(self, SIGNAL('textChanged()'), self.changed) - self.connect(self, SIGNAL('cursorPositionChanged()'), - self.cursor_position_changed) - - self.indent_chars = " "*4 - - # Code completion / calltips - if parent is not None: - mainwin = parent - while not isinstance(mainwin, QMainWindow): - mainwin = mainwin.parent() - if mainwin is None: - break - if mainwin is not None: - parent = mainwin - - self.completion_widget = CompletionWidget(self, parent) - self.codecompletion_auto = False - self.codecompletion_case = True - self.codecompletion_enter = False - self.completion_text = "" - - self.calltip_widget = CallTipWidget(self, hide_timer_on=True) - self.calltips = True - self.calltip_position = None - - self.has_cell_separators = False - self.highlight_current_cell_enabled = False - - # The color values may be overridden by the syntax highlighter - # Highlight current line color - self.currentline_color = QColor(Qt.red).lighter(190) - self.currentcell_color = QColor(Qt.red).lighter(194) - - # Brace matching - self.bracepos = None - self.matched_p_color = QColor(Qt.green) - self.unmatched_p_color = QColor(Qt.red) - - def setup_completion(self, size=None, font=None): - self.completion_widget.setup_appearance(size, font) - - def set_indent_chars(self, indent_chars): - self.indent_chars = indent_chars - - def set_palette(self, background, foreground): - """ - Set text editor palette colors: - background color and caret (text cursor) color - """ - palette = QPalette() - palette.setColor(QPalette.Base, background) - palette.setColor(QPalette.Text, foreground) - self.setPalette(palette) - - # Set the right background color when changing color schemes - # or creating new Editor windows. This seems to be a Qt bug. - # Fixes Issue 2028 - if sys.platform == 'darwin': - if self.objectName(): - style = "QPlainTextEdit#%s {background: %s; color: %s;}" % \ - (self.objectName(), background.name(), foreground.name()) - self.setStyleSheet(style) - - - #------Extra selections - def get_extra_selections(self, key): - return self.extra_selections_dict.get(key, []) - - def set_extra_selections(self, key, extra_selections): - self.extra_selections_dict[key] = extra_selections - - def update_extra_selections(self): - extra_selections = [] - for key, extra in list(self.extra_selections_dict.items()): - if key == 'current_line' or key == 'current_cell': - # Python 3 compatibility (weird): current line has to be - # highlighted first - extra_selections = extra + extra_selections - else: - extra_selections += extra - self.setExtraSelections(extra_selections) - - def clear_extra_selections(self, key): - self.extra_selections_dict[key] = [] - self.update_extra_selections() - - - def changed(self): - """Emit changed signal""" - self.emit(SIGNAL('modificationChanged(bool)'), - self.document().isModified()) - - - #------Highlight current line - def highlight_current_line(self): - """Highlight current line""" - selection = QTextEdit.ExtraSelection() - selection.format.setProperty(QTextFormat.FullWidthSelection, - to_qvariant(True)) - selection.format.setBackground(self.currentline_color) - selection.cursor = self.textCursor() - selection.cursor.clearSelection() - self.set_extra_selections('current_line', [selection]) - self.update_extra_selections() - - def unhighlight_current_line(self): - """Unhighlight current line""" - self.clear_extra_selections('current_line') - - #------Highlight current cell - def highlight_current_cell(self): - """Highlight current cell""" - if self.cell_separators is None or \ - not self.highlight_current_cell_enabled: - return - selection = QTextEdit.ExtraSelection() - selection.format.setProperty(QTextFormat.FullWidthSelection, - to_qvariant(True)) - selection.format.setBackground(self.currentcell_color) - selection.cursor, whole_file_selected, whole_screen_selected =\ - self.select_current_cell_in_visible_portion() - if whole_file_selected: - self.clear_extra_selections('current_cell') - elif whole_screen_selected: - if self.has_cell_separators: - self.set_extra_selections('current_cell', [selection]) - self.update_extra_selections() - else: - self.clear_extra_selections('current_cell') - else: - self.set_extra_selections('current_cell', [selection]) - self.update_extra_selections() - - def unhighlight_current_cell(self): - """Unhighlight current cell""" - self.clear_extra_selections('current_cell') - - #------Brace matching - def find_brace_match(self, position, brace, forward): - start_pos, end_pos = self.BRACE_MATCHING_SCOPE - if forward: - bracemap = {'(': ')', '[': ']', '{': '}'} - text = self.get_text(position, end_pos) - i_start_open = 1 - i_start_close = 1 - else: - bracemap = {')': '(', ']': '[', '}': '{'} - text = self.get_text(start_pos, position) - i_start_open = len(text)-1 - i_start_close = len(text)-1 - - while True: - if forward: - i_close = text.find(bracemap[brace], i_start_close) - else: - i_close = text.rfind(bracemap[brace], 0, i_start_close+1) - if i_close > -1: - if forward: - i_start_close = i_close+1 - i_open = text.find(brace, i_start_open, i_close) - else: - i_start_close = i_close-1 - i_open = text.rfind(brace, i_close, i_start_open+1) - if i_open > -1: - if forward: - i_start_open = i_open+1 - else: - i_start_open = i_open-1 - else: - # found matching brace - if forward: - return position+i_close - else: - return position-(len(text)-i_close) - else: - # no matching brace - return - - def __highlight(self, positions, color=None, cancel=False): - if cancel: - self.clear_extra_selections('brace_matching') - return - extra_selections = [] - for position in positions: - if position > self.get_position('eof'): - return - selection = QTextEdit.ExtraSelection() - selection.format.setBackground(color) - selection.cursor = self.textCursor() - selection.cursor.clearSelection() - selection.cursor.setPosition(position) - selection.cursor.movePosition(QTextCursor.NextCharacter, - QTextCursor.KeepAnchor) - extra_selections.append(selection) - self.set_extra_selections('brace_matching', extra_selections) - self.update_extra_selections() - - def cursor_position_changed(self): - """Brace matching""" - if self.bracepos is not None: - self.__highlight(self.bracepos, cancel=True) - self.bracepos = None - cursor = self.textCursor() - if cursor.position() == 0: - return - cursor.movePosition(QTextCursor.PreviousCharacter, - QTextCursor.KeepAnchor) - text = to_text_string(cursor.selectedText()) - pos1 = cursor.position() - if text in (')', ']', '}'): - pos2 = self.find_brace_match(pos1, text, forward=False) - elif text in ('(', '[', '{'): - pos2 = self.find_brace_match(pos1, text, forward=True) - else: - return - if pos2 is not None: - self.bracepos = (pos1, pos2) - self.__highlight(self.bracepos, color=self.matched_p_color) - else: - self.bracepos = (pos1,) - self.__highlight(self.bracepos, color=self.unmatched_p_color) - - - #-----Widget setup and options - def set_codecompletion_auto(self, state): - """Set code completion state""" - self.codecompletion_auto = state - - def set_codecompletion_case(self, state): - """Case sensitive completion""" - self.codecompletion_case = state - self.completion_widget.case_sensitive = state - - def set_codecompletion_enter(self, state): - """Enable Enter key to select completion""" - self.codecompletion_enter = state - self.completion_widget.enter_select = state - - def set_calltips(self, state): - """Set calltips state""" - self.calltips = state - - def set_wrap_mode(self, mode=None): - """ - Set wrap mode - Valid *mode* values: None, 'word', 'character' - """ - if mode == 'word': - wrap_mode = QTextOption.WrapAtWordBoundaryOrAnywhere - elif mode == 'character': - wrap_mode = QTextOption.WrapAnywhere - else: - wrap_mode = QTextOption.NoWrap - self.setWordWrapMode(wrap_mode) - - - #------Reimplementing Qt methods - def copy(self): - """ - Reimplement Qt method - Copy text to clipboard with correct EOL chars - """ - QApplication.clipboard().setText(self.get_selected_text()) - - def toPlainText(self): - """ - Reimplement Qt method - Fix PyQt4 bug on Windows and Python 3 - """ - # Fix what appears to be a PyQt4 bug when getting file - # contents under Windows and PY3. This bug leads to - # corruptions when saving files with certain combinations - # of unicode chars on them (like the one attached on - # Issue 1546) - if os.name == 'nt' and PY3: - text = self.get_text('sof', 'eof') - return text.replace('\u2028', '\n').replace('\u2029', '\n')\ - .replace('\u0085', '\n') - else: - return super(TextEditBaseWidget, self).toPlainText() - - def keyPressEvent(self, event): - text, key = event.text(), event.key() - ctrl = event.modifiers() & Qt.ControlModifier - meta = event.modifiers() & Qt.MetaModifier - # Use our own copy method for {Ctrl,Cmd}+C to avoid Qt - # copying text in HTML (See Issue 2285) - if (ctrl or meta) and key == Qt.Key_C: - self.copy() - else: - super(TextEditBaseWidget, self).keyPressEvent(event) - - #------Text: get, set, ... - def get_selection_as_executable_code(self): - """Return selected text as a processed text, - to be executable in a Python/IPython interpreter""" - ls = self.get_line_separator() - - _indent = lambda line: len(line)-len(line.lstrip()) - - line_from, line_to = self.get_selection_bounds() - text = self.get_selected_text() - if not text: - return - - lines = text.split(ls) - if len(lines) > 1: - # Multiline selection -> eventually fixing indentation - original_indent = _indent(self.get_text_line(line_from)) - text = (" "*(original_indent-_indent(lines[0])))+text - - # If there is a common indent to all lines, find it. - # Moving from bottom line to top line ensures that blank - # lines inherit the indent of the line *below* it, - # which is the desired behavior. - min_indent = 999 - current_indent = 0 - lines = text.split(ls) - for i in range(len(lines)-1, -1, -1): - line = lines[i] - if line.strip(): - current_indent = _indent(line) - min_indent = min(current_indent, min_indent) - else: - lines[i] = ' ' * current_indent - if min_indent: - lines = [line[min_indent:] for line in lines] - - # Remove any leading whitespace or comment lines - # since they confuse the reserved word detector that follows below - while lines: - first_line = lines[0].lstrip() - if first_line == '' or first_line[0] == '#': - lines.pop(0) - else: - break - - # Add an EOL character after indentation blocks that start with some - # Python reserved words, so that it gets evaluated automatically - # by the console - varname = re.compile('[a-zA-Z0-9_]*') # matches valid variable names - maybe = False - nextexcept = () - for n, line in enumerate(lines): - if not _indent(line): - word = varname.match(line).group() - if maybe and word not in nextexcept: - lines[n-1] += ls - maybe = False - if word: - if word in ('def', 'for', 'while', 'with', 'class'): - maybe = True - nextexcept = () - elif word == 'if': - maybe = True - nextexcept = ('elif', 'else') - elif word == 'try': - maybe = True - nextexcept = ('except', 'finally') - if maybe: - if lines[-1].strip() == '': - lines[-1] += ls - else: - lines.append(ls) - - return ls.join(lines) - - def get_cell_as_executable_code(self): - """Return cell contents as executable code""" - start_pos, end_pos = self.__save_selection() - cursor, whole_file_selected = self.select_current_cell() - if not whole_file_selected: - self.setTextCursor(cursor) - text = self.get_selection_as_executable_code() - self.__restore_selection(start_pos, end_pos) - return text - - def is_cell_separator(self, cursor=None, block=None): - """Return True if cursor (or text block) is on a block separator""" - assert cursor is not None or block is not None - if cursor is not None: - cursor0 = QTextCursor(cursor) - cursor0.select(QTextCursor.BlockUnderCursor) - text = to_text_string(cursor0.selectedText()) - else: - text = to_text_string(block.text()) - if self.cell_separators is None: - return False - else: - return text.lstrip().startswith(self.cell_separators) - - def select_current_cell(self): - """Select cell under cursor - cell = group of lines separated by CELL_SEPARATORS - returns the textCursor and a boolean indicating if the - entire file is selected""" - cursor = self.textCursor() - cursor.movePosition(QTextCursor.StartOfBlock) - cur_pos = prev_pos = cursor.position() - - # Moving to the next line that is not a separator, if we are - # exactly at one of them - while self.is_cell_separator(cursor): - cursor.movePosition(QTextCursor.NextBlock) - prev_pos = cur_pos - cur_pos = cursor.position() - if cur_pos == prev_pos: - return cursor, False - prev_pos = cur_pos - # If not, move backwards to find the previous separator - while not self.is_cell_separator(cursor): - cursor.movePosition(QTextCursor.PreviousBlock) - prev_pos = cur_pos - cur_pos = cursor.position() - if cur_pos == prev_pos: - if self.is_cell_separator(cursor): - return cursor, False - else: - break - cursor.setPosition(prev_pos) - cell_at_file_start = cursor.atStart() - # Once we find it (or reach the beginning of the file) - # move to the next separator (or the end of the file) - # so we can grab the cell contents - while not self.is_cell_separator(cursor): - cursor.movePosition(QTextCursor.NextBlock, - QTextCursor.KeepAnchor) - cur_pos = cursor.position() - if cur_pos == prev_pos: - cursor.movePosition(QTextCursor.EndOfBlock, - QTextCursor.KeepAnchor) - break - prev_pos = cur_pos - cell_at_file_end = cursor.atEnd() - return cursor, cell_at_file_start and cell_at_file_end - - def select_current_cell_in_visible_portion(self): - """Select cell under cursor in the visible portion of the file - cell = group of lines separated by CELL_SEPARATORS - returns - -the textCursor - -a boolean indicating if the entire file is selected - -a boolean indicating if the entire visible portion of the file is selected""" - cursor = self.textCursor() - cursor.movePosition(QTextCursor.StartOfBlock) - cur_pos = prev_pos = cursor.position() - - beg_pos = self.cursorForPosition(QPoint(0, 0)).position() - bottom_right = QPoint(self.viewport().width() - 1, - self.viewport().height() - 1) - end_pos = self.cursorForPosition(bottom_right).position() - - # Moving to the next line that is not a separator, if we are - # exactly at one of them - while self.is_cell_separator(cursor): - cursor.movePosition(QTextCursor.NextBlock) - prev_pos = cur_pos - cur_pos = cursor.position() - if cur_pos == prev_pos: - return cursor, False, False - prev_pos = cur_pos - # If not, move backwards to find the previous separator - while not self.is_cell_separator(cursor)\ - and cursor.position() >= beg_pos: - cursor.movePosition(QTextCursor.PreviousBlock) - prev_pos = cur_pos - cur_pos = cursor.position() - if cur_pos == prev_pos: - if self.is_cell_separator(cursor): - return cursor, False, False - else: - break - cell_at_screen_start = cursor.position() <= beg_pos - cursor.setPosition(prev_pos) - cell_at_file_start = cursor.atStart() - # Selecting cell header - if not cell_at_file_start: - cursor.movePosition(QTextCursor.PreviousBlock) - cursor.movePosition(QTextCursor.NextBlock, - QTextCursor.KeepAnchor) - # Once we find it (or reach the beginning of the file) - # move to the next separator (or the end of the file) - # so we can grab the cell contents - while not self.is_cell_separator(cursor)\ - and cursor.position() <= end_pos: - cursor.movePosition(QTextCursor.NextBlock, - QTextCursor.KeepAnchor) - cur_pos = cursor.position() - if cur_pos == prev_pos: - cursor.movePosition(QTextCursor.EndOfBlock, - QTextCursor.KeepAnchor) - break - prev_pos = cur_pos - cell_at_file_end = cursor.atEnd() - cell_at_screen_end = cursor.position() >= end_pos - return cursor,\ - cell_at_file_start and cell_at_file_end,\ - cell_at_screen_start and cell_at_screen_end - - def go_to_next_cell(self): - """Go to the next cell of lines""" - cursor = self.textCursor() - cursor.movePosition(QTextCursor.NextBlock) - cur_pos = prev_pos = cursor.position() - while not self.is_cell_separator(cursor): - # Moving to the next code cell - cursor.movePosition(QTextCursor.NextBlock) - prev_pos = cur_pos - cur_pos = cursor.position() - if cur_pos == prev_pos: - return - self.setTextCursor(cursor) - - def get_line_count(self): - """Return document total line number""" - return self.blockCount() - - def __save_selection(self): - """Save current cursor selection and return position bounds""" - cursor = self.textCursor() - return cursor.selectionStart(), cursor.selectionEnd() - - def __restore_selection(self, start_pos, end_pos): - """Restore cursor selection from position bounds""" - cursor = self.textCursor() - cursor.setPosition(start_pos) - cursor.setPosition(end_pos, QTextCursor.KeepAnchor) - self.setTextCursor(cursor) - - def __duplicate_line_or_selection(self, after_current_line=True): - """Duplicate current line or selected text""" - cursor = self.textCursor() - cursor.beginEditBlock() - start_pos, end_pos = self.__save_selection() - if to_text_string(cursor.selectedText()): - cursor.setPosition(end_pos) - # Check if end_pos is at the start of a block: if so, starting - # changes from the previous block - cursor.movePosition(QTextCursor.StartOfBlock, - QTextCursor.KeepAnchor) - if not to_text_string(cursor.selectedText()): - cursor.movePosition(QTextCursor.PreviousBlock) - end_pos = cursor.position() - - cursor.setPosition(start_pos) - cursor.movePosition(QTextCursor.StartOfBlock) - while cursor.position() <= end_pos: - cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) - if cursor.atEnd(): - cursor_temp = QTextCursor(cursor) - cursor_temp.clearSelection() - cursor_temp.insertText(self.get_line_separator()) - break - cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) - text = cursor.selectedText() - cursor.clearSelection() - - if not after_current_line: - # Moving cursor before current line/selected text - cursor.setPosition(start_pos) - cursor.movePosition(QTextCursor.StartOfBlock) - start_pos += len(text) - end_pos += len(text) - - cursor.insertText(text) - cursor.endEditBlock() - self.setTextCursor(cursor) - self.__restore_selection(start_pos, end_pos) - - def duplicate_line(self): - """ - Duplicate current line or selected text - Paste the duplicated text *after* the current line/selected text - """ - self.__duplicate_line_or_selection(after_current_line=True) - - def copy_line(self): - """ - Copy current line or selected text - Paste the duplicated text *before* the current line/selected text - """ - self.__duplicate_line_or_selection(after_current_line=False) - - def __move_line_or_selection(self, after_current_line=True): - """Move current line or selected text""" - cursor = self.textCursor() - cursor.beginEditBlock() - start_pos, end_pos = self.__save_selection() - if to_text_string(cursor.selectedText()): - # Check if start_pos is at the start of a block - cursor.setPosition(start_pos) - cursor.movePosition(QTextCursor.StartOfBlock) - start_pos = cursor.position() - - cursor.setPosition(end_pos) - # Check if end_pos is at the start of a block: if so, starting - # changes from the previous block - cursor.movePosition(QTextCursor.StartOfBlock, - QTextCursor.KeepAnchor) - if to_text_string(cursor.selectedText()): - cursor.movePosition(QTextCursor.NextBlock) - end_pos = cursor.position() - else: - cursor.movePosition(QTextCursor.StartOfBlock) - start_pos = cursor.position() - cursor.movePosition(QTextCursor.NextBlock) - end_pos = cursor.position() - cursor.setPosition(start_pos) - cursor.setPosition(end_pos, QTextCursor.KeepAnchor) - - sel_text = to_text_string(cursor.selectedText()) - cursor.removeSelectedText() - - if after_current_line: - text = to_text_string(cursor.block().text()) - start_pos += len(text)+1 - end_pos += len(text)+1 - cursor.movePosition(QTextCursor.NextBlock) - else: - cursor.movePosition(QTextCursor.PreviousBlock) - text = to_text_string(cursor.block().text()) - start_pos -= len(text)+1 - end_pos -= len(text)+1 - cursor.insertText(sel_text) - - cursor.endEditBlock() - self.setTextCursor(cursor) - self.__restore_selection(start_pos, end_pos) - - def move_line_up(self): - """Move up current line or selected text""" - self.__move_line_or_selection(after_current_line=False) - - def move_line_down(self): - """Move down current line or selected text""" - self.__move_line_or_selection(after_current_line=True) - - def extend_selection_to_complete_lines(self): - """Extend current selection to complete lines""" - cursor = self.textCursor() - start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() - cursor.setPosition(start_pos) - cursor.setPosition(end_pos, QTextCursor.KeepAnchor) - if cursor.atBlockStart(): - cursor.movePosition(QTextCursor.PreviousBlock, - QTextCursor.KeepAnchor) - cursor.movePosition(QTextCursor.EndOfBlock, - QTextCursor.KeepAnchor) - self.setTextCursor(cursor) - - def delete_line(self): - """Delete current line""" - cursor = self.textCursor() - if self.has_selected_text(): - self.extend_selection_to_complete_lines() - start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() - cursor.setPosition(start_pos) - else: - start_pos = end_pos = cursor.position() - cursor.beginEditBlock() - cursor.setPosition(start_pos) - cursor.movePosition(QTextCursor.StartOfBlock) - while cursor.position() <= end_pos: - cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) - if cursor.atEnd(): - break - cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) - cursor.removeSelectedText() - cursor.endEditBlock() - self.ensureCursorVisible() - - - #------Code completion / Calltips - def hide_tooltip_if_necessary(self, key): - """Hide calltip when necessary""" - try: - calltip_char = self.get_character(self.calltip_position) - before = self.is_cursor_before(self.calltip_position, - char_offset=1) - other = key in (Qt.Key_ParenRight, Qt.Key_Period, Qt.Key_Tab) - if calltip_char not in ('?', '(') or before or other: - QToolTip.hideText() - except (IndexError, TypeError): - QToolTip.hideText() - - def show_completion_widget(self, textlist, automatic=True): - """Show completion widget""" - self.completion_widget.show_list(textlist, automatic=automatic) - - def hide_completion_widget(self): - """Hide completion widget""" - self.completion_widget.hide() - - def show_completion_list(self, completions, completion_text="", - automatic=True): - """Display the possible completions""" - if completions is None or len(completions) == 0 or \ - completions == [completion_text]: - return - self.completion_text = completion_text - # Sorting completion list (entries starting with underscore are - # put at the end of the list): - underscore = set([comp for comp in completions - if comp.startswith('_')]) - completions = sorted(set(completions)-underscore, key=str_lower)+\ - sorted(underscore, key=str_lower) - self.show_completion_widget(completions, automatic=automatic) - - def select_completion_list(self): - """Completion list is active, Enter was just pressed""" - self.completion_widget.item_selected() - - def insert_completion(self, text): - if text: - cursor = self.textCursor() - cursor.movePosition(QTextCursor.PreviousCharacter, - QTextCursor.KeepAnchor, - len(self.completion_text)) - cursor.removeSelectedText() - self.insert_text(text) - - def is_completion_widget_visible(self): - """Return True is completion list widget is visible""" - return self.completion_widget.isVisible() - - - #------Standard keys - def stdkey_clear(self): - if not self.has_selected_text(): - self.moveCursor(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) - self.remove_selected_text() - - def stdkey_backspace(self): - if not self.has_selected_text(): - self.moveCursor(QTextCursor.PreviousCharacter, - QTextCursor.KeepAnchor) - self.remove_selected_text() - - def __get_move_mode(self, shift): - return QTextCursor.KeepAnchor if shift else QTextCursor.MoveAnchor - - def stdkey_up(self, shift): - self.moveCursor(QTextCursor.Up, self.__get_move_mode(shift)) - - def stdkey_down(self, shift): - self.moveCursor(QTextCursor.Down, self.__get_move_mode(shift)) - - def stdkey_tab(self): - self.insert_text(self.indent_chars) - - def stdkey_home(self, shift, ctrl, prompt_pos=None): - """Smart HOME feature: cursor is first moved at - indentation position, then at the start of the line""" - move_mode = self.__get_move_mode(shift) - if ctrl: - self.moveCursor(QTextCursor.Start, move_mode) - else: - cursor = self.textCursor() - if prompt_pos is None: - start_position = self.get_position('sol') - else: - start_position = self.get_position(prompt_pos) - text = self.get_text(start_position, 'eol') - indent_pos = start_position+len(text)-len(text.lstrip()) - if cursor.position() != indent_pos: - cursor.setPosition(indent_pos, move_mode) - else: - cursor.setPosition(start_position, move_mode) - self.setTextCursor(cursor) - - def stdkey_end(self, shift, ctrl): - move_mode = self.__get_move_mode(shift) - if ctrl: - self.moveCursor(QTextCursor.End, move_mode) - else: - self.moveCursor(QTextCursor.EndOfBlock, move_mode) - - def stdkey_pageup(self): - pass - - def stdkey_pagedown(self): - pass - - def stdkey_escape(self): - pass - - - #----Qt Events - def mousePressEvent(self, event): - """Reimplement Qt method""" - if sys.platform.startswith('linux') and event.button() == Qt.MidButton: - self.calltip_widget.hide() - self.setFocus() - event = QMouseEvent(QEvent.MouseButtonPress, event.pos(), - Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) - QPlainTextEdit.mousePressEvent(self, event) - QPlainTextEdit.mouseReleaseEvent(self, event) - # Send selection text to clipboard to be able to use - # the paste method and avoid the strange Issue 1445 - # NOTE: This issue seems a focusing problem but it - # seems really hard to track - mode_clip = QClipboard.Clipboard - mode_sel = QClipboard.Selection - text_clip = QApplication.clipboard().text(mode=mode_clip) - text_sel = QApplication.clipboard().text(mode=mode_sel) - QApplication.clipboard().setText(text_sel, mode=mode_clip) - self.paste() - QApplication.clipboard().setText(text_clip, mode=mode_clip) - else: - self.calltip_widget.hide() - QPlainTextEdit.mousePressEvent(self, event) - - def focusInEvent(self, event): - """Reimplemented to handle focus""" - self.emit(SIGNAL("focus_changed()")) - self.emit(SIGNAL("focus_in()")) - self.highlight_current_cell() - QPlainTextEdit.focusInEvent(self, event) - - def focusOutEvent(self, event): - """Reimplemented to handle focus""" - self.emit(SIGNAL("focus_changed()")) - QPlainTextEdit.focusOutEvent(self, event) - - def wheelEvent(self, event): - """Reimplemented to emit zoom in/out signals when Ctrl is pressed""" - # This feature is disabled on MacOS, see Issue 1510 - if sys.platform != 'darwin': - if event.modifiers() & Qt.ControlModifier: - if event.delta() < 0: - self.emit(SIGNAL("zoom_out()")) - elif event.delta() > 0: - self.emit(SIGNAL("zoom_in()")) - return - QPlainTextEdit.wheelEvent(self, event) - self.highlight_current_cell() - - -class QtANSIEscapeCodeHandler(ANSIEscapeCodeHandler): - def __init__(self): - ANSIEscapeCodeHandler.__init__(self) - self.base_format = None - self.current_format = None - - def set_light_background(self, state): - if state: - self.default_foreground_color = 30 - self.default_background_color = 47 - else: - self.default_foreground_color = 37 - self.default_background_color = 40 - - def set_base_format(self, base_format): - self.base_format = base_format - - def get_format(self): - return self.current_format - - def set_style(self): - """ - Set font style with the following attributes: - 'foreground_color', 'background_color', 'italic', - 'bold' and 'underline' - """ - if self.current_format is None: - assert self.base_format is not None - self.current_format = QTextCharFormat(self.base_format) - # Foreground color - if self.foreground_color is None: - qcolor = self.base_format.foreground() - else: - cstr = self.ANSI_COLORS[self.foreground_color-30][self.intensity] - qcolor = QColor(cstr) - self.current_format.setForeground(qcolor) - # Background color - if self.background_color is None: - qcolor = self.base_format.background() - else: - cstr = self.ANSI_COLORS[self.background_color-40][self.intensity] - qcolor = QColor(cstr) - self.current_format.setBackground(qcolor) - - font = self.current_format.font() - # Italic - if self.italic is None: - italic = self.base_format.fontItalic() - else: - italic = self.italic - font.setItalic(italic) - # Bold - if self.bold is None: - bold = self.base_format.font().bold() - else: - bold = self.bold - font.setBold(bold) - # Underline - if self.underline is None: - underline = self.base_format.font().underline() - else: - underline = self.underline - font.setUnderline(underline) - self.current_format.setFont(font) - - -def inverse_color(color): - color.setHsv(color.hue(), color.saturation(), 255-color.value()) - - -class ConsoleFontStyle(object): - def __init__(self, foregroundcolor, backgroundcolor, - bold, italic, underline): - self.foregroundcolor = foregroundcolor - self.backgroundcolor = backgroundcolor - self.bold = bold - self.italic = italic - self.underline = underline - self.format = None - - def apply_style(self, font, light_background, is_default): - self.format = QTextCharFormat() - self.format.setFont(font) - foreground = QColor(self.foregroundcolor) - if not light_background and is_default: - inverse_color(foreground) - self.format.setForeground(foreground) - background = QColor(self.backgroundcolor) - if not light_background: - inverse_color(background) - self.format.setBackground(background) - font = self.format.font() - font.setBold(self.bold) - font.setItalic(self.italic) - font.setUnderline(self.underline) - self.format.setFont(font) - -class ConsoleBaseWidget(TextEditBaseWidget): - """Console base widget""" - BRACE_MATCHING_SCOPE = ('sol', 'eol') - COLOR_PATTERN = re.compile('\x01?\x1b\[(.*?)m\x02?') - - def __init__(self, parent=None): - TextEditBaseWidget.__init__(self, parent) - - self.light_background = True - - self.setMaximumBlockCount(300) - - # ANSI escape code handler - self.ansi_handler = QtANSIEscapeCodeHandler() - - # Disable undo/redo (nonsense for a console widget...): - self.setUndoRedoEnabled(False) - - self.connect(self, SIGNAL('userListActivated(int, const QString)'), - lambda user_id, text: - self.emit(SIGNAL('completion_widget_activated(QString)'), - text)) - - self.default_style = ConsoleFontStyle( - foregroundcolor=0x000000, backgroundcolor=0xFFFFFF, - bold=False, italic=False, underline=False) - self.error_style = ConsoleFontStyle( - foregroundcolor=0xFF0000, backgroundcolor=0xFFFFFF, - bold=False, italic=False, underline=False) - self.traceback_link_style = ConsoleFontStyle( - foregroundcolor=0x0000FF, backgroundcolor=0xFFFFFF, - bold=True, italic=False, underline=True) - self.prompt_style = ConsoleFontStyle( - foregroundcolor=0x00AA00, backgroundcolor=0xFFFFFF, - bold=True, italic=False, underline=False) - self.font_styles = (self.default_style, self.error_style, - self.traceback_link_style, self.prompt_style) - self.set_pythonshell_font() - self.setMouseTracking(True) - - def set_light_background(self, state): - self.light_background = state - if state: - self.set_palette(background=QColor(Qt.white), - foreground=QColor(Qt.darkGray)) - else: - self.set_palette(background=QColor(Qt.black), - foreground=QColor(Qt.lightGray)) - self.ansi_handler.set_light_background(state) - self.set_pythonshell_font() - - def set_selection(self, start, end): - cursor = self.textCursor() - cursor.setPosition(start) - cursor.setPosition(end, QTextCursor.KeepAnchor) - self.setTextCursor(cursor) - - def truncate_selection(self, position_from): - """Unselect read-only parts in shell, like prompt""" - position_from = self.get_position(position_from) - cursor = self.textCursor() - start, end = cursor.selectionStart(), cursor.selectionEnd() - if start < end: - start = max([position_from, start]) - else: - end = max([position_from, end]) - self.set_selection(start, end) - - def restrict_cursor_position(self, position_from, position_to): - """In shell, avoid editing text except between prompt and EOF""" - position_from = self.get_position(position_from) - position_to = self.get_position(position_to) - cursor = self.textCursor() - cursor_position = cursor.position() - if cursor_position < position_from or cursor_position > position_to: - self.set_cursor_position(position_to) - - #------Python shell - def insert_text(self, text): - """Reimplement TextEditBaseWidget method""" - self.textCursor().insertText(text, self.default_style.format) - - def paste(self): - """Reimplement Qt method""" - if self.has_selected_text(): - self.remove_selected_text() - self.insert_text(QApplication.clipboard().text()) - - def append_text_to_shell(self, text, error, prompt): - """ - Append text to Python shell - In a way, this method overrides the method 'insert_text' when text is - inserted at the end of the text widget for a Python shell - - Handles error messages and show blue underlined links - Handles ANSI color sequences - Handles ANSI FF sequence - """ - cursor = self.textCursor() - cursor.movePosition(QTextCursor.End) - while True: - index = text.find(chr(12)) - if index == -1: - break - text = text[index+1:] - self.clear() - if error: - is_traceback = False - for text in text.splitlines(True): - if text.startswith(' File') \ - and not text.startswith(' File "<'): - is_traceback = True - # Show error links in blue underlined text - cursor.insertText(' ', self.default_style.format) - cursor.insertText(text[2:], - self.traceback_link_style.format) - else: - # Show error/warning messages in red - cursor.insertText(text, self.error_style.format) - if is_traceback: - self.emit(SIGNAL('traceback_available()')) - elif prompt: - # Show prompt in green - cursor.insertText(text, self.prompt_style.format) - else: - # Show other outputs in black - last_end = 0 - for match in self.COLOR_PATTERN.finditer(text): - cursor.insertText(text[last_end:match.start()], - self.default_style.format) - last_end = match.end() - for code in [int(_c) for _c in match.group(1).split(';')]: - self.ansi_handler.set_code(code) - self.default_style.format = self.ansi_handler.get_format() - cursor.insertText(text[last_end:], self.default_style.format) -# # Slower alternative: -# segments = self.COLOR_PATTERN.split(text) -# cursor.insertText(segments.pop(0), self.default_style.format) -# if segments: -# for ansi_tags, text in zip(segments[::2], segments[1::2]): -# for ansi_tag in ansi_tags.split(';'): -# self.ansi_handler.set_code(int(ansi_tag)) -# self.default_style.format = self.ansi_handler.get_format() -# cursor.insertText(text, self.default_style.format) - self.set_cursor_position('eof') - self.setCurrentCharFormat(self.default_style.format) - - def set_pythonshell_font(self, font=None): - """Python Shell only""" - if font is None: - font = QFont() - for style in self.font_styles: - style.apply_style(font=font, - light_background=self.light_background, - is_default=style is self.default_style) - self.ansi_handler.set_base_format(self.default_style.format) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/codeeditor.py spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/codeeditor.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/codeeditor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/codeeditor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2742 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Editor widget based on QtGui.QPlainTextEdit -""" - -#%% This line is for cell execution testing -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from __future__ import division - -import sys -import re -import sre_constants -import os.path as osp -import time - -from spyderlib.qt import is_pyqt46 -from spyderlib.qt.QtGui import (QColor, QMenu, QApplication, QSplitter, QFont, - QTextEdit, QTextFormat, QPainter, QTextCursor, - QBrush, QTextDocument, QTextCharFormat, - QPixmap, QPrinter, QToolTip, QCursor, QLabel, - QInputDialog, QTextBlockUserData, QLineEdit, - QKeySequence, QWidget, QVBoxLayout, - QHBoxLayout, QDialog, QIntValidator, - QDialogButtonBox, QGridLayout, QPaintEvent, - QMessageBox, QTextOption) -from spyderlib.qt.QtCore import (Qt, SIGNAL, Signal, QTimer, QRect, QRegExp, - QSize, SLOT, Slot) -from spyderlib.qt.compat import to_qvariant - -#%% This line is for cell execution testing -# Local import -#TODO: Try to separate this module from spyderlib to create a self -# consistent editor module (Qt source code and shell widgets library) -from spyderlib.baseconfig import get_conf_path, _, DEBUG, get_image_path -from spyderlib.config import CONF -from spyderlib.guiconfig import get_font, create_shortcut -from spyderlib.utils.qthelpers import (add_actions, create_action, keybinding, - mimedata2url, get_icon) -from spyderlib.utils.dochelpers import getobj -from spyderlib.utils import encoding, sourcecode -from spyderlib.utils.sourcecode import ALL_LANGUAGES, CELL_LANGUAGES -from spyderlib.widgets.editortools import PythonCFM -from spyderlib.widgets.sourcecode.base import TextEditBaseWidget -from spyderlib.widgets.sourcecode import syntaxhighlighters as sh -from spyderlib.py3compat import to_text_string - -try: - import IPython.nbformat as nbformat - import IPython.nbformat.current # analysis:ignore - from IPython.nbconvert import PythonExporter as nbexporter -except: - nbformat = None # analysis:ignore - -#%% This line is for cell execution testing -# For debugging purpose: -LOG_FILENAME = get_conf_path('codeeditor.log') -DEBUG_EDITOR = DEBUG >= 3 - - -#=============================================================================== -# Go to line dialog box -#=============================================================================== -class GoToLineDialog(QDialog): - def __init__(self, editor): - QDialog.__init__(self, editor) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - self.lineno = None - self.editor = editor - - self.setWindowTitle(_("Editor")) - self.setModal(True) - - label = QLabel(_("Go to line:")) - self.lineedit = QLineEdit() - validator = QIntValidator(self.lineedit) - validator.setRange(1, editor.get_line_count()) - self.lineedit.setValidator(validator) - self.connect(self.lineedit, SIGNAL('textChanged(QString)'), - self.text_has_changed) - cl_label = QLabel(_("Current line:")) - cl_label_v = QLabel("%d" % editor.get_cursor_line_number()) - last_label = QLabel(_("Line count:")) - last_label_v = QLabel("%d" % editor.get_line_count()) - - glayout = QGridLayout() - glayout.addWidget(label, 0, 0, Qt.AlignVCenter|Qt.AlignRight) - glayout.addWidget(self.lineedit, 0, 1, Qt.AlignVCenter) - glayout.addWidget(cl_label, 1, 0, Qt.AlignVCenter|Qt.AlignRight) - glayout.addWidget(cl_label_v, 1, 1, Qt.AlignVCenter) - glayout.addWidget(last_label, 2, 0, Qt.AlignVCenter|Qt.AlignRight) - glayout.addWidget(last_label_v, 2, 1, Qt.AlignVCenter) - - bbox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, - Qt.Vertical, self) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - btnlayout = QVBoxLayout() - btnlayout.addWidget(bbox) - btnlayout.addStretch(1) - - ok_button = bbox.button(QDialogButtonBox.Ok) - ok_button.setEnabled(False) - self.connect(self.lineedit, SIGNAL("textChanged(QString)"), - lambda text: ok_button.setEnabled(len(text) > 0)) - - layout = QHBoxLayout() - layout.addLayout(glayout) - layout.addLayout(btnlayout) - self.setLayout(layout) - - self.lineedit.setFocus() - - def text_has_changed(self, text): - """Line edit's text has changed""" - text = to_text_string(text) - if text: - self.lineno = int(text) - else: - self.lineno = None - - def get_line_number(self): - """Return line number""" - # It is import to avoid accessing Qt C++ object as it has probably - # already been destroyed, due to the Qt.WA_DeleteOnClose attribute - return self.lineno - - -#=============================================================================== -# Viewport widgets -#=============================================================================== -class LineNumberArea(QWidget): - """Line number area (on the left side of the text editor widget)""" - def __init__(self, editor): - QWidget.__init__(self, editor) - self.code_editor = editor - self.setMouseTracking(True) - - def sizeHint(self): - """Override Qt method""" - return QSize(self.code_editor.compute_linenumberarea_width(), 0) - - def paintEvent(self, event): - """Override Qt method""" - self.code_editor.linenumberarea_paint_event(event) - - def mouseMoveEvent(self, event): - """Override Qt method""" - self.code_editor.linenumberarea_mousemove_event(event) - - def mouseDoubleClickEvent(self, event): - """Override Qt method""" - self.code_editor.linenumberarea_mousedoubleclick_event(event) - - def mousePressEvent(self, event): - """Override Qt method""" - self.code_editor.linenumberarea_mousepress_event(event) - - def mouseReleaseEvent(self, event): - """Override Qt method""" - self.code_editor.linenumberarea_mouserelease_event(event) - - def wheelEvent(self, event): - """Override Qt method""" - self.code_editor.wheelEvent(event) - - -class ScrollFlagArea(QWidget): - """Source code editor's scroll flag area""" - WIDTH = 12 - FLAGS_DX = 4 - FLAGS_DY = 2 - - def __init__(self, editor): - QWidget.__init__(self, editor) - self.setAttribute(Qt.WA_OpaquePaintEvent) - self.code_editor = editor - self.connect(editor.verticalScrollBar(), SIGNAL('valueChanged(int)'), - lambda value: self.repaint()) - - def sizeHint(self): - """Override Qt method""" - return QSize(self.WIDTH, 0) - - def paintEvent(self, event): - """Override Qt method""" - self.code_editor.scrollflagarea_paint_event(event) - - def mousePressEvent(self, event): - """Override Qt method""" - vsb = self.code_editor.verticalScrollBar() - value = self.position_to_value(event.pos().y()-1) - vsb.setValue(value-.5*vsb.pageStep()) - - def get_scale_factor(self, slider=False): - """Return scrollbar's scale factor: - ratio between pixel span height and value span height""" - delta = 0 if slider else 2 - vsb = self.code_editor.verticalScrollBar() - position_height = vsb.height()-delta-1 - value_height = vsb.maximum()-vsb.minimum()+vsb.pageStep() - return float(position_height)/value_height - - def value_to_position(self, y, slider=False): - """Convert value to position""" - offset = 0 if slider else 1 - vsb = self.code_editor.verticalScrollBar() - return (y-vsb.minimum())*self.get_scale_factor(slider)+offset - - def position_to_value(self, y, slider=False): - """Convert position to value""" - offset = 0 if slider else 1 - vsb = self.code_editor.verticalScrollBar() - return vsb.minimum()+max([0, (y-offset)/self.get_scale_factor(slider)]) - - def make_flag_qrect(self, position): - """Make flag QRect""" - return QRect(self.FLAGS_DX/2, position-self.FLAGS_DY/2, - self.WIDTH-self.FLAGS_DX, self.FLAGS_DY) - - def make_slider_range(self, value): - """Make slider range QRect""" - vsb = self.code_editor.verticalScrollBar() - pos1 = self.value_to_position(value, slider=True) - pos2 = self.value_to_position(value + vsb.pageStep(), slider=True) - return QRect(1, pos1, self.WIDTH-2, pos2-pos1+1) - - def wheelEvent(self, event): - """Override Qt method""" - self.code_editor.wheelEvent(event) - - -class EdgeLine(QWidget): - """Source code editor's edge line (default: 79 columns, PEP8)""" - def __init__(self, editor): - QWidget.__init__(self, editor) - self.code_editor = editor - self.column = 79 - self.setAttribute(Qt.WA_TransparentForMouseEvents) - - def paintEvent(self, event): - """Override Qt method""" - painter = QPainter(self) - color = QColor(Qt.darkGray) - color.setAlphaF(.5) - painter.fillRect(event.rect(), color) - - -#=============================================================================== -# CodeEditor widget -#=============================================================================== -class BlockUserData(QTextBlockUserData): - def __init__(self, editor): - QTextBlockUserData.__init__(self) - self.editor = editor - self.breakpoint = False - self.breakpoint_condition = None - self.code_analysis = [] - self.todo = '' - self.editor.blockuserdata_list.append(self) - - def is_empty(self): - return not self.breakpoint and not self.code_analysis and not self.todo - - def __del__(self): - bud_list = self.editor.blockuserdata_list - bud_list.pop(bud_list.index(self)) - - -def set_scrollflagarea_painter(painter, light_color): - """Set scroll flag area painter pen and brush colors""" - painter.setPen(QColor(light_color).darker(120)) - painter.setBrush(QBrush(QColor(light_color))) - - -def get_file_language(filename, text=None): - """Get file language from filename""" - ext = osp.splitext(filename)[1] - if ext.startswith('.'): - ext = ext[1:] # file extension with leading dot - language = ext - if not ext: - if text is None: - text, _enc = encoding.read(filename) - for line in text.splitlines(): - if not line.strip(): - continue - if line.startswith('#!'): - shebang = line[2:] - if 'python' in shebang: - language = 'python' - else: - break - return language - - -class CodeEditor(TextEditBaseWidget): - """Source Code Editor Widget based exclusively on Qt""" - - LANGUAGES = {'Python': (sh.PythonSH, '#', PythonCFM), - 'Cython': (sh.CythonSH, '#', PythonCFM), - 'Fortran77': (sh.Fortran77SH, 'c', None), - 'Fortran': (sh.FortranSH, '!', None), - 'Idl': (sh.IdlSH, ';', None), - 'Matlab': (sh.MatlabSH, '%', None), - 'Diff': (sh.DiffSH, '', None), - 'GetText': (sh.GetTextSH, '#', None), - 'Nsis': (sh.NsisSH, '#', None), - 'Html': (sh.HtmlSH, '', None), - 'Css': (sh.CssSH, '', None), - 'Xml': (sh.XmlSH, '', None), - 'Js': (sh.JsSH, '//', None), - 'Json': (sh.JsonSH, '', None), - 'Julia': (sh.JuliaSH, '#', None), - 'Yaml': (sh.YamlSH, '#', None), - 'Cpp': (sh.CppSH, '//', None), - 'OpenCL': (sh.OpenCLSH, '//', None), - 'Batch': (sh.BatchSH, 'rem ', None), - 'Ini': (sh.IniSH, '#', None), - 'Enaml': (sh.EnamlSH, '#', PythonCFM), - } - - try: - import pygments # analysis:ignore - except ImportError: - # Removing all syntax highlighters requiring pygments to be installed - for key, (sh_class, comment_string, CFMatch) in list(LANGUAGES.items()): - if issubclass(sh_class, sh.PygmentsSH): - LANGUAGES.pop(key) - - TAB_ALWAYS_INDENTS = ('py', 'pyw', 'python', 'c', 'cpp', 'cl', 'h') - - # Custom signal to be emitted upon completion of the editor's paintEvent - painted = Signal(QPaintEvent) - sig_new_file = Signal(str) - - # To have these attrs when early viewportEvent's are triggered - edge_line = None - linenumberarea = None - - def __init__(self, parent=None): - TextEditBaseWidget.__init__(self, parent) - self.setFocusPolicy(Qt.StrongFocus) - - # We use these object names to set the right background - # color when changing color schemes or creating new - # Editor windows. This seems to be a Qt bug. - # Fixes Issue 2028 - if sys.platform == 'darwin': - plugin_name = repr(parent) - if 'editor' in plugin_name.lower(): - self.setObjectName('editor') - elif 'inspector' in plugin_name.lower(): - self.setObjectName('inspector') - elif 'historylog' in plugin_name.lower(): - self.setObjectName('historylog') - - # Completion - completion_size = CONF.get('editor_appearance', 'completion/size') - completion_font = get_font('editor') - self.completion_widget.setup_appearance(completion_size, - completion_font) - - # Caret (text cursor) - self.setCursorWidth( CONF.get('editor_appearance', 'cursor/width') ) - - # 79-col edge line - self.edge_line_enabled = True - self.edge_line = EdgeLine(self) - - # Blanks enabled - self.blanks_enabled = False - - # Markers - self.markers_margin = True - self.markers_margin_width = 15 - self.error_pixmap = QPixmap(get_image_path('error.png'), 'png') - self.warning_pixmap = QPixmap(get_image_path('warning.png'), 'png') - self.todo_pixmap = QPixmap(get_image_path('todo.png'), 'png') - self.bp_pixmap = QPixmap(get_image_path('breakpoint_small.png'), 'png') - self.bpc_pixmap = QPixmap(get_image_path('breakpoint_cond_small.png'), - 'png') - - # Line number area management - self.linenumbers_margin = True - self.linenumberarea_enabled = None - self.linenumberarea = LineNumberArea(self) - self.connect(self, SIGNAL("blockCountChanged(int)"), - self.update_linenumberarea_width) - self.connect(self, SIGNAL("updateRequest(QRect,int)"), - self.update_linenumberarea) - self.linenumberarea_pressed = -1 - self.linenumberarea_released = -1 - - # Colors to be defined in _apply_highlighter_color_scheme() - # Currentcell color and current line color are defined in base.py - self.occurence_color = None - self.ctrl_click_color = None - self.sideareas_color = None - self.matched_p_color = None - self.unmatched_p_color = None - self.normal_color = None - self.comment_color = None - - self.linenumbers_color = QColor(Qt.darkGray) - - # --- Syntax highlight entrypoint --- - # - # - if set, self.highlighter is responsible for - # - coloring raw text data inside editor on load - # - coloring text data when editor is cloned - # - updating document highlight on line edits - # - providing color palette (scheme) for the editor - # - providing data for Outliner - # - self.highlighter is not responsible for - # - background highlight for current line - # - background highlight for search / current line occurences - - self.highlighter_class = sh.TextSH - self.highlighter = None - ccs = 'Spyder' - if ccs not in sh.COLOR_SCHEME_NAMES: - ccs = sh.COLOR_SCHEME_NAMES[0] - self.color_scheme = ccs - - self.highlight_current_line_enabled = False - - # Scrollbar flag area - self.scrollflagarea_enabled = None - self.scrollflagarea = ScrollFlagArea(self) - self.scrollflagarea.hide() - self.warning_color = "#FFAD07" - self.error_color = "#EA2B0E" - self.todo_color = "#B4D4F3" - self.breakpoint_color = "#30E62E" - - self.update_linenumberarea_width() - - self.document_id = id(self) - - # Indicate occurences of the selected word - self.connect(self, SIGNAL('cursorPositionChanged()'), - self.__cursor_position_changed) - self.__find_first_pos = None - self.__find_flags = None - - self.supported_language = False - self.supported_cell_language = False - self.classfunc_match = None - self.comment_string = None - - # Block user data - self.blockuserdata_list = [] - - # Update breakpoints if the number of lines in the file changes - self.connect(self, SIGNAL("blockCountChanged(int)"), - self.update_breakpoints) - - # Mark occurences timer - self.occurence_highlighting = None - self.occurence_timer = QTimer(self) - self.occurence_timer.setSingleShot(True) - self.occurence_timer.setInterval(1500) - self.connect(self.occurence_timer, SIGNAL("timeout()"), - self.__mark_occurences) - self.occurences = [] - self.occurence_color = QColor(Qt.yellow).lighter(160) - - # Mark found results - self.connect(self, SIGNAL('textChanged()'), self.__text_has_changed) - self.found_results = [] - self.found_results_color = QColor(Qt.magenta).lighter(180) - - # Context menu - self.gotodef_action = None - self.setup_context_menu() - - # Tab key behavior - self.tab_indents = None - self.tab_mode = True # see CodeEditor.set_tab_mode - - # Intelligent backspace mode - self.intelligent_backspace = True - - self.go_to_definition_enabled = False - self.close_parentheses_enabled = True - self.close_quotes_enabled = False - self.add_colons_enabled = True - self.auto_unindent_enabled = True - - # Mouse tracking - self.setMouseTracking(True) - self.__cursor_changed = False - self.ctrl_click_color = QColor(Qt.blue) - - # Breakpoints - self.breakpoints = self.get_breakpoints() - - # Keyboard shortcuts - self.shortcuts = self.create_shortcuts() - - # Code editor - self.__visible_blocks = [] # Visible blocks, update with repaint - self.painted.connect(self._draw_editor_cell_divider) - - self.connect(self.verticalScrollBar(), SIGNAL('valueChanged(int)'), - lambda value: self.rehighlight_cells()) - - def create_shortcuts(self): - codecomp = create_shortcut(self.do_completion, context='Editor', - name='Code completion', parent=self) - duplicate_line = create_shortcut(self.duplicate_line, context='Editor', - name='Duplicate line', parent=self) - copyline = create_shortcut(self.copy_line, context='Editor', - name='Copy line', parent=self) - deleteline = create_shortcut(self.delete_line, context='Editor', - name='Delete line', parent=self) - movelineup = create_shortcut(self.move_line_up, context='Editor', - name='Move line up', parent=self) - movelinedown = create_shortcut(self.move_line_down, context='Editor', - name='Move line down', parent=self) - gotodef = create_shortcut(self.do_go_to_definition, context='Editor', - name='Go to definition', parent=self) - toggle_comment = create_shortcut(self.toggle_comment, context='Editor', - name='Toggle comment', parent=self) - blockcomment = create_shortcut(self.blockcomment, context='Editor', - name='Blockcomment', parent=self) - unblockcomment = create_shortcut(self.unblockcomment, context='Editor', - name='Unblockcomment', parent=self) - return [codecomp, duplicate_line, copyline, deleteline, movelineup, - movelinedown, gotodef, toggle_comment, blockcomment, - unblockcomment] - - def get_shortcut_data(self): - """ - Returns shortcut data, a list of tuples (shortcut, text, default) - shortcut (QShortcut or QAction instance) - text (string): action/shortcut description - default (string): default key sequence - """ - return [sc.data for sc in self.shortcuts] - - def closeEvent(self, event): - TextEditBaseWidget.closeEvent(self, event) - if is_pyqt46: - self.emit(SIGNAL('destroyed()')) - - - def get_document_id(self): - return self.document_id - - def set_as_clone(self, editor): - """Set as clone editor""" - self.setDocument(editor.document()) - self.document_id = editor.get_document_id() - self.highlighter = editor.highlighter - self._apply_highlighter_color_scheme() - - #-----Widget setup and options - def toggle_wrap_mode(self, enable): - """Enable/disable wrap mode""" - self.set_wrap_mode('word' if enable else None) - - def setup_editor(self, linenumbers=True, language=None, markers=False, - font=None, color_scheme=None, wrap=False, tab_mode=True, - intelligent_backspace=True, highlight_current_line=True, - highlight_current_cell=True, occurence_highlighting=True, - scrollflagarea=True, edge_line=True, edge_line_column=79, - codecompletion_auto=False, codecompletion_case=True, - codecompletion_enter=False, show_blanks=False, - calltips=None, go_to_definition=False, - close_parentheses=True, close_quotes=False, - add_colons=True, auto_unindent=True, indent_chars=" "*4, - tab_stop_width=40, cloned_from=None, - occurence_timeout=1500): - - # Code completion and calltips - self.set_codecompletion_auto(codecompletion_auto) - self.set_codecompletion_case(codecompletion_case) - self.set_codecompletion_enter(codecompletion_enter) - self.set_calltips(calltips) - self.set_go_to_definition_enabled(go_to_definition) - self.set_close_parentheses_enabled(close_parentheses) - self.set_close_quotes_enabled(close_quotes) - self.set_add_colons_enabled(add_colons) - self.set_auto_unindent_enabled(auto_unindent) - self.set_indent_chars(indent_chars) - self.setTabStopWidth(tab_stop_width) - - # Scrollbar flag area - self.set_scrollflagarea_enabled(scrollflagarea) - - # Edge line - self.set_edge_line_enabled(edge_line) - self.set_edge_line_column(edge_line_column) - - # Blanks - self.set_blanks_enabled(show_blanks) - - # Line number area - if cloned_from: - self.setFont(font) # this is required for line numbers area - self.setup_margins(linenumbers, markers) - - # Lexer - self.set_language(language) - - # Highlight current cell - self.set_highlight_current_cell(highlight_current_cell) - - # Highlight current line - self.set_highlight_current_line(highlight_current_line) - - # Occurence highlighting - self.set_occurence_highlighting(occurence_highlighting) - self.set_occurence_timeout(occurence_timeout) - - # Tab always indents (even when cursor is not at the begin of line) - self.set_tab_mode(tab_mode) - - # Intelligent backspace - self.toggle_intelligent_backspace(intelligent_backspace) - - if cloned_from is not None: - self.set_as_clone(cloned_from) - self.update_linenumberarea_width() - elif font is not None: - self.set_font(font, color_scheme) - elif color_scheme is not None: - self.set_color_scheme(color_scheme) - - self.toggle_wrap_mode(wrap) - - def set_tab_mode(self, enable): - """ - enabled = tab always indent - (otherwise tab indents only when cursor is at the beginning of a line) - """ - self.tab_mode = enable - - def toggle_intelligent_backspace(self, state): - self.intelligent_backspace = state - - def set_go_to_definition_enabled(self, enable): - """Enable/Disable go-to-definition feature, which is implemented in - child class -> Editor widget""" - self.go_to_definition_enabled = enable - - def set_close_parentheses_enabled(self, enable): - """Enable/disable automatic parentheses insertion feature""" - self.close_parentheses_enabled = enable - - def set_close_quotes_enabled(self, enable): - """Enable/disable automatic quote insertion feature""" - self.close_quotes_enabled = enable - - def set_add_colons_enabled(self, enable): - """Enable/disable automatic colons insertion feature""" - self.add_colons_enabled = enable - - def set_auto_unindent_enabled(self, enable): - """Enable/disable automatic unindent after else/elif/finally/except""" - self.auto_unindent_enabled = enable - - def set_occurence_highlighting(self, enable): - """Enable/disable occurence highlighting""" - self.occurence_highlighting = enable - if not enable: - self.__clear_occurences() - - def set_occurence_timeout(self, timeout): - """Set occurence highlighting timeout (ms)""" - self.occurence_timer.setInterval(timeout) - - def set_highlight_current_line(self, enable): - """Enable/disable current line highlighting""" - self.highlight_current_line_enabled = enable - if self.highlight_current_line_enabled: - self.highlight_current_line() - else: - self.unhighlight_current_line() - - def set_highlight_current_cell(self, enable): - """Enable/disable current line highlighting""" - hl_cell_enable = enable and self.supported_cell_language - self.highlight_current_cell_enabled = hl_cell_enable - if self.highlight_current_cell_enabled: - self.highlight_current_cell() - else: - self.unhighlight_current_cell() - - def set_language(self, language): - self.tab_indents = language in self.TAB_ALWAYS_INDENTS - self.comment_string = '' - sh_class = sh.TextSH - if language is not None: - for (key, value) in ALL_LANGUAGES.items(): - if language.lower() in value: - self.supported_language = True - sh_class, comment_string, CFMatch = self.LANGUAGES[key] - self.comment_string = comment_string - if key in CELL_LANGUAGES: - self.supported_cell_language = True - self.cell_separators = CELL_LANGUAGES[key] - if CFMatch is None: - self.classfunc_match = None - else: - self.classfunc_match = CFMatch() - break - self._set_highlighter(sh_class) - - def _set_highlighter(self, sh_class): - self.highlighter_class = sh_class - if self.highlighter is not None: - # Removing old highlighter - # TODO: test if leaving parent/document as is eats memory - self.highlighter.setParent(None) - self.highlighter.setDocument(None) - self.highlighter = self.highlighter_class(self.document(), - self.font(), - self.color_scheme) - self._apply_highlighter_color_scheme() - - def is_json(self): - return self.highlighter_class is sh.JsonSH - - def is_python(self): - return self.highlighter_class is sh.PythonSH - - def is_cython(self): - return self.highlighter_class is sh.CythonSH - - def is_enaml(self): - return self.highlighter_class is sh.EnamlSH - - def is_python_like(self): - return self.is_python() or self.is_cython() or self.is_enaml() - - def intelligent_tab(self): - """Provide intelligent behavoir for Tab key press""" - leading_text = self.get_text('sol', 'cursor') - if not leading_text.strip() or leading_text.endswith('#'): - # blank line or start of comment - self.indent_or_replace() - elif self.in_comment_or_string() and not leading_text.endswith(' '): - # in a word in a comment - self.do_completion() - elif leading_text.endswith('import ') or leading_text[-1] == '.': - # blank import or dot completion - self.do_completion() - elif (leading_text.split()[0] in ['from', 'import'] and - not ';' in leading_text): - # import line with a single statement - # (prevents lines like: `import pdb; pdb.set_trace()`) - self.do_completion() - elif leading_text[-1] in '(,' or leading_text.endswith(', '): - self.indent_or_replace() - elif leading_text.endswith(' '): - # if the line ends with a space, indent - self.indent_or_replace() - elif re.search(r"[^\d\W]\w*\Z", leading_text, re.UNICODE): - # if the line ends with a non-whitespace character - self.do_completion() - else: - self.indent_or_replace() - - def intelligent_backtab(self): - """Provide intelligent behavoir for Shift+Tab key press""" - leading_text = self.get_text('sol', 'cursor') - if not leading_text.strip(): - # blank line - self.unindent() - elif self.in_comment_or_string(): - self.unindent() - elif leading_text[-1] in '(,' or leading_text.endswith(', '): - position = self.get_position('cursor') - self.show_object_info(position) - else: - # if the line ends with any other character but comma - self.unindent() - - def rehighlight(self): - """ - Rehighlight the whole document to rebuild outline explorer data - and import statements data from scratch - """ - if self.highlighter is not None: - self.highlighter.rehighlight() - if self.highlight_current_cell_enabled: - self.highlight_current_cell() - else: - self.unhighlight_current_cell() - if self.highlight_current_line_enabled: - self.highlight_current_line() - else: - self.unhighlight_current_line() - - def rehighlight_cells(self): - """Rehighlight cells when moving the scrollbar""" - if self.highlight_current_cell_enabled: - self.highlight_current_cell() - - def setup_margins(self, linenumbers=True, markers=True): - """ - Setup margin settings - (except font, now set in self.set_font) - """ - self.linenumbers_margin = linenumbers - self.markers_margin = markers - self.set_linenumberarea_enabled(linenumbers or markers) - - def remove_trailing_spaces(self): - """Remove trailing spaces""" - cursor = self.textCursor() - cursor.beginEditBlock() - cursor.movePosition(QTextCursor.Start) - while True: - cursor.movePosition(QTextCursor.EndOfBlock) - text = to_text_string(cursor.block().text()) - length = len(text)-len(text.rstrip()) - if length > 0: - cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, - length) - cursor.removeSelectedText() - if cursor.atEnd(): - break - cursor.movePosition(QTextCursor.NextBlock) - cursor.endEditBlock() - - def fix_indentation(self): - """Replace tabs by spaces""" - text_before = to_text_string(self.toPlainText()) - text_after = sourcecode.fix_indentation(text_before) - if text_before != text_after: - self.setPlainText(text_after) - self.document().setModified(True) - - def get_current_object(self): - """Return current object (string) """ - source_code = to_text_string(self.toPlainText()) - offset = self.get_position('cursor') - return sourcecode.get_primary_at(source_code, offset) - - #------Find occurences - def __find_first(self, text): - """Find first occurence: scan whole document""" - flags = QTextDocument.FindCaseSensitively|QTextDocument.FindWholeWords - cursor = self.textCursor() - # Scanning whole document - cursor.movePosition(QTextCursor.Start) - regexp = QRegExp(r"\b%s\b" % QRegExp.escape(text), Qt.CaseSensitive) - cursor = self.document().find(regexp, cursor, flags) - self.__find_first_pos = cursor.position() - return cursor - - def __find_next(self, text, cursor): - """Find next occurence""" - flags = QTextDocument.FindCaseSensitively|QTextDocument.FindWholeWords - regexp = QRegExp(r"\b%s\b" % QRegExp.escape(text), Qt.CaseSensitive) - cursor = self.document().find(regexp, cursor, flags) - if cursor.position() != self.__find_first_pos: - return cursor - - def __cursor_position_changed(self): - """Cursor position has changed""" - line, column = self.get_cursor_line_column() - self.emit(SIGNAL('cursorPositionChanged(int,int)'), line, column) - if self.highlight_current_cell_enabled: - self.highlight_current_cell() - else: - self.unhighlight_current_cell() - if self.highlight_current_line_enabled: - self.highlight_current_line() - else: - self.unhighlight_current_line() - if self.occurence_highlighting: - self.occurence_timer.stop() - self.occurence_timer.start() - - def __clear_occurences(self): - """Clear occurence markers""" - self.occurences = [] - self.clear_extra_selections('occurences') - self.scrollflagarea.update() - - def __highlight_selection(self, key, cursor, foreground_color=None, - background_color=None, underline_color=None, - underline_style=QTextCharFormat.SpellCheckUnderline, - update=False): - extra_selections = self.get_extra_selections(key) - selection = QTextEdit.ExtraSelection() - if foreground_color is not None: - selection.format.setForeground(foreground_color) - if background_color is not None: - selection.format.setBackground(background_color) - if underline_color is not None: - selection.format.setProperty(QTextFormat.TextUnderlineStyle, - to_qvariant(underline_style)) - selection.format.setProperty(QTextFormat.TextUnderlineColor, - to_qvariant(underline_color)) - selection.format.setProperty(QTextFormat.FullWidthSelection, - to_qvariant(True)) - selection.cursor = cursor - extra_selections.append(selection) - self.set_extra_selections(key, extra_selections) - if update: - self.update_extra_selections() - - def __mark_occurences(self): - """Marking occurences of the currently selected word""" - self.__clear_occurences() - - if not self.supported_language: - return - - text = self.get_current_word() - if text is None: - return - if self.has_selected_text() and self.get_selected_text() != text: - return - - if (self.is_python_like()) and \ - (sourcecode.is_keyword(to_text_string(text)) or \ - to_text_string(text) == 'self'): - return - - # Highlighting all occurences of word *text* - cursor = self.__find_first(text) - self.occurences = [] - while cursor: - self.occurences.append(cursor.blockNumber()) - self.__highlight_selection('occurences', cursor, - background_color=self.occurence_color) - cursor = self.__find_next(text, cursor) - self.update_extra_selections() - if len(self.occurences) > 1 and self.occurences[-1] == 0: - # XXX: this is never happening with PySide but it's necessary - # for PyQt4... this must be related to a different behavior for - # the QTextDocument.find function between those two libraries - self.occurences.pop(-1) - self.scrollflagarea.update() - - #-----highlight found results (find/replace widget) - def highlight_found_results(self, pattern, words=False, regexp=False): - """Highlight all found patterns""" - pattern = to_text_string(pattern) - if not pattern: - return - if not regexp: - pattern = re.escape(to_text_string(pattern)) - pattern = r"\b%s\b" % pattern if words else pattern - text = to_text_string(self.toPlainText()) - try: - regobj = re.compile(pattern) - except sre_constants.error: - return - extra_selections = [] - self.found_results = [] - for match in regobj.finditer(text): - pos1, pos2 = match.span() - selection = QTextEdit.ExtraSelection() - selection.format.setBackground(self.found_results_color) - selection.cursor = self.textCursor() - selection.cursor.setPosition(pos1) - self.found_results.append(selection.cursor.blockNumber()) - selection.cursor.setPosition(pos2, QTextCursor.KeepAnchor) - extra_selections.append(selection) - self.set_extra_selections('find', extra_selections) - self.update_extra_selections() - - def clear_found_results(self): - """Clear found results highlighting""" - self.found_results = [] - self.clear_extra_selections('find') - self.scrollflagarea.update() - - def __text_has_changed(self): - """Text has changed, eventually clear found results highlighting""" - if self.found_results: - self.clear_found_results() - - #-----markers - def get_markers_margin(self): - if self.markers_margin: - return self.markers_margin_width - else: - return 0 - - #-----linenumberarea - def set_linenumberarea_enabled(self, state): - self.linenumberarea_enabled = state - self.linenumberarea.setVisible(state) - self.update_linenumberarea_width() - - def get_linenumberarea_width(self): - """Return current line number area width""" - return self.linenumberarea.contentsRect().width() - - def compute_linenumberarea_width(self): - """Compute and return line number area width""" - if not self.linenumberarea_enabled: - return 0 - digits = 1 - maxb = max(1, self.blockCount()) - while maxb >= 10: - maxb /= 10 - digits += 1 - if self.linenumbers_margin: - linenumbers_margin = 3+self.fontMetrics().width('9'*digits) - else: - linenumbers_margin = 0 - return linenumbers_margin+self.get_markers_margin() - - def update_linenumberarea_width(self, new_block_count=None): - """ - Update line number area width. - - new_block_count is needed to handle blockCountChanged(int) signal - """ - self.setViewportMargins(self.compute_linenumberarea_width(), 0, - self.get_scrollflagarea_width(), 0) - - def update_linenumberarea(self, qrect, dy): - """Update line number area""" - if dy: - self.linenumberarea.scroll(0, dy) - else: - self.linenumberarea.update(0, qrect.y(), - self.linenumberarea.width(), - qrect.height()) - if qrect.contains(self.viewport().rect()): - self.update_linenumberarea_width() - - def linenumberarea_paint_event(self, event): - """Painting line number area""" - painter = QPainter(self.linenumberarea) - painter.fillRect(event.rect(), self.sideareas_color) - # This is needed to make that the font size of line numbers - # be the same as the text one when zooming - # See Issue 2296 - if sys.platform == 'darwin': - font = self.font() - else: - font = painter.font() - font_height = self.fontMetrics().height() - - active_block = self.textCursor().block() - active_line_number = active_block.blockNumber() + 1 - - def draw_pixmap(ytop, pixmap): - painter.drawPixmap(0, ytop + (font_height-pixmap.height()) / 2, - pixmap) - - for top, line_number, block in self.visible_blocks: - if self.linenumbers_margin: - if line_number == active_line_number: - font.setWeight(font.Bold) - painter.setFont(font) - painter.setPen(self.normal_color) - else: - font.setWeight(font.Normal) - painter.setFont(font) - painter.setPen(self.linenumbers_color) - - painter.drawText(0, top, self.linenumberarea.width(), - font_height, - Qt.AlignRight | Qt.AlignBottom, - to_text_string(line_number)) - - data = block.userData() - if self.markers_margin and data: - if data.code_analysis: - for _message, error in data.code_analysis: - if error: - break - if error: - draw_pixmap(top, self.error_pixmap) - else: - draw_pixmap(top, self.warning_pixmap) - if data.todo: - draw_pixmap(top, self.todo_pixmap) - if data.breakpoint: - if data.breakpoint_condition is None: - draw_pixmap(top, self.bp_pixmap) - else: - draw_pixmap(top, self.bpc_pixmap) - - def __get_linenumber_from_mouse_event(self, event): - """Return line number from mouse event""" - block = self.firstVisibleBlock() - line_number = block.blockNumber() - top = self.blockBoundingGeometry(block).translated( - self.contentOffset()).top() - bottom = top + self.blockBoundingRect(block).height() - - while block.isValid() and top < event.pos().y(): - block = block.next() - top = bottom - bottom = top + self.blockBoundingRect(block).height() - line_number += 1 - - return line_number - - def linenumberarea_mousemove_event(self, event): - """Handling line number area mouse move event""" - line_number = self.__get_linenumber_from_mouse_event(event) - block = self.document().findBlockByNumber(line_number-1) - data = block.userData() - - # this disables pyflakes messages if there is an active drag/selection - # operation - check = self.linenumberarea_released == -1 - if data and data.code_analysis and check: - self.__show_code_analysis_results(line_number, data.code_analysis) - - if event.buttons() == Qt.LeftButton: - self.linenumberarea_released = line_number - self.linenumberarea_select_lines(self.linenumberarea_pressed, - self.linenumberarea_released) - - def linenumberarea_mousedoubleclick_event(self, event): - """Handling line number area mouse double-click event""" - line_number = self.__get_linenumber_from_mouse_event(event) - shift = event.modifiers() & Qt.ShiftModifier - self.add_remove_breakpoint(line_number, edit_condition=shift) - - def linenumberarea_mousepress_event(self, event): - """Handling line number area mouse double press event""" - line_number = self.__get_linenumber_from_mouse_event(event) - self.linenumberarea_pressed = line_number - self.linenumberarea_released = line_number - self.linenumberarea_select_lines(self.linenumberarea_pressed, - self.linenumberarea_released) - - def linenumberarea_mouserelease_event(self, event): - """Handling line number area mouse release event""" - self.linenumberarea_released = -1 - self.linenumberarea_pressed = -1 - - def linenumberarea_select_lines(self, linenumber_pressed, - linenumber_released): - """Select line(s) after a mouse press/mouse press drag event""" - find_block_by_line_number = self.document().findBlockByLineNumber - move_n_blocks = (linenumber_released - linenumber_pressed) - start_line = linenumber_pressed - start_block = find_block_by_line_number(start_line - 1) - - cursor = self.textCursor() - cursor.setPosition(start_block.position()) - - # Select/drag downwards - if move_n_blocks > 0: - for n in range(abs(move_n_blocks) + 1): - cursor.movePosition(cursor.NextBlock, cursor.KeepAnchor) - # Select/drag upwards or select single line - else: - cursor.movePosition(cursor.NextBlock) - for n in range(abs(move_n_blocks) + 1): - cursor.movePosition(cursor.PreviousBlock, cursor.KeepAnchor) - - # Account for last line case - if linenumber_released == self.blockCount(): - cursor.movePosition(cursor.EndOfBlock, cursor.KeepAnchor) - else: - cursor.movePosition(cursor.StartOfBlock, cursor.KeepAnchor) - - self.setTextCursor(cursor) - - #------Breakpoints - def add_remove_breakpoint(self, line_number=None, condition=None, - edit_condition=False): - """Add/remove breakpoint""" - if not self.is_python_like(): - return - if line_number is None: - block = self.textCursor().block() - else: - block = self.document().findBlockByNumber(line_number-1) - data = block.userData() - if data: - data.breakpoint = not data.breakpoint - old_breakpoint_condition = data.breakpoint_condition - data.breakpoint_condition = None - else: - data = BlockUserData(self) - data.breakpoint = True - old_breakpoint_condition = None - if condition is not None: - data.breakpoint_condition = condition - if edit_condition: - data.breakpoint = True - condition = data.breakpoint_condition - if old_breakpoint_condition is not None: - condition = old_breakpoint_condition - condition, valid = QInputDialog.getText(self, - _('Breakpoint'), - _("Condition:"), - QLineEdit.Normal, condition) - if valid: - condition = str(condition) - if not condition: - condition = None - data.breakpoint_condition = condition - else: - data.breakpoint_condition = old_breakpoint_condition - return - if data.breakpoint: - text = to_text_string(block.text()).strip() - if len(text) == 0 or text.startswith('#') or text.startswith('"') \ - or text.startswith("'"): - data.breakpoint = False - block.setUserData(data) - self.linenumberarea.update() - self.scrollflagarea.update() - self.emit(SIGNAL('breakpoints_changed()')) - - def get_breakpoints(self): - """Get breakpoints""" - breakpoints = [] - block = self.document().firstBlock() - for line_number in range(1, self.document().blockCount()+1): - data = block.userData() - if data and data.breakpoint: - breakpoints.append((line_number, data.breakpoint_condition)) - block = block.next() - return breakpoints - - def clear_breakpoints(self): - """Clear breakpoints""" - self.breakpoints = [] - for data in self.blockuserdata_list[:]: - data.breakpoint = False - # data.breakpoint_condition = None # not necessary, but logical - if data.is_empty(): - del data - - def set_breakpoints(self, breakpoints): - """Set breakpoints""" - self.clear_breakpoints() - for line_number, condition in breakpoints: - self.add_remove_breakpoint(line_number, condition) - - def update_breakpoints(self): - """Update breakpoints""" - self.emit(SIGNAL('breakpoints_changed()')) - - #-----Code introspection - def do_completion(self, automatic=False): - """Trigger completion""" - if not self.is_completion_widget_visible(): - self.emit(SIGNAL('get_completions(bool)'), automatic) - - def do_go_to_definition(self): - """Trigger go-to-definition""" - if not self.in_comment_or_string(): - self.emit(SIGNAL("go_to_definition(int)"), self.textCursor().position()) - - def show_object_info(self, position): - """Trigger a calltip""" - self.emit(SIGNAL('show_object_info(int)'), position) - - #-----edge line - def set_edge_line_enabled(self, state): - """Toggle edge line visibility""" - self.edge_line_enabled = state - self.edge_line.setVisible(state) - - def set_edge_line_column(self, column): - """Set edge line column value""" - self.edge_line.column = column - self.edge_line.update() - - # -----blank spaces - def set_blanks_enabled(self, state): - """Toggle blanks visibility""" - self.blanks_enabled = state - option = self.document().defaultTextOption() - option.setFlags(option.flags() | \ - QTextOption.AddSpaceForLineAndParagraphSeparators) - if self.blanks_enabled: - option.setFlags(option.flags() | QTextOption.ShowTabsAndSpaces) - else: - option.setFlags(option.flags() & ~QTextOption.ShowTabsAndSpaces) - self.document().setDefaultTextOption(option) - - #-----scrollflagarea - def set_scrollflagarea_enabled(self, state): - """Toggle scroll flag area visibility""" - self.scrollflagarea_enabled = state - self.scrollflagarea.setVisible(state) - self.update_linenumberarea_width() - - def get_scrollflagarea_width(self): - """Return scroll flag area width""" - if self.scrollflagarea_enabled: - return ScrollFlagArea.WIDTH - else: - return 0 - - def scrollflagarea_paint_event(self, event): - """Painting the scroll flag area""" - make_flag = self.scrollflagarea.make_flag_qrect - make_slider = self.scrollflagarea.make_slider_range - - # Filling the whole painting area - painter = QPainter(self.scrollflagarea) - painter.fillRect(event.rect(), self.sideareas_color) - block = self.document().firstBlock() - - # Painting warnings and todos - for line_number in range(1, self.document().blockCount()+1): - data = block.userData() - if data: - position = self.scrollflagarea.value_to_position(line_number) - if data.code_analysis: - # Warnings - color = self.warning_color - for _message, error in data.code_analysis: - if error: - color = self.error_color - break - set_scrollflagarea_painter(painter, color) - painter.drawRect(make_flag(position)) - if data.todo: - # TODOs - set_scrollflagarea_painter(painter, self.todo_color) - painter.drawRect(make_flag(position)) - if data.breakpoint: - # Breakpoints - set_scrollflagarea_painter(painter, self.breakpoint_color) - painter.drawRect(make_flag(position)) - block = block.next() - - # Occurences - if self.occurences: - set_scrollflagarea_painter(painter, self.occurence_color) - for line_number in self.occurences: - position = self.scrollflagarea.value_to_position(line_number) - painter.drawRect(make_flag(position)) - - # Found results - if self.found_results: - set_scrollflagarea_painter(painter, self.found_results_color) - for line_number in self.found_results: - position = self.scrollflagarea.value_to_position(line_number) - painter.drawRect(make_flag(position)) - - # Painting the slider range - pen_color = QColor(Qt.white) - pen_color.setAlphaF(.8) - painter.setPen(pen_color) - brush_color = QColor(Qt.white) - brush_color.setAlphaF(.5) - painter.setBrush(QBrush(brush_color)) - painter.drawRect(make_slider(self.firstVisibleBlock().blockNumber())) - - def resizeEvent(self, event): - """Reimplemented Qt method to handle line number area resizing""" - TextEditBaseWidget.resizeEvent(self, event) - cr = self.contentsRect() - self.linenumberarea.setGeometry(\ - QRect(cr.left(), cr.top(), - self.compute_linenumberarea_width(), cr.height())) - self.__set_scrollflagarea_geometry(cr) - - def __set_scrollflagarea_geometry(self, contentrect): - """Set scroll flag area geometry""" - cr = contentrect - if self.verticalScrollBar().isVisible(): - vsbw = self.verticalScrollBar().contentsRect().width() - else: - vsbw = 0 - _left, _top, right, _bottom = self.getContentsMargins() - if right > vsbw: - # Depending on the platform (e.g. on Ubuntu), the scrollbar sizes - # may be taken into account in the contents margins whereas it is - # not on Windows for example - vsbw = 0 - self.scrollflagarea.setGeometry(\ - QRect(cr.right()-ScrollFlagArea.WIDTH-vsbw, cr.top(), - self.scrollflagarea.WIDTH, cr.height())) - - #-----edgeline - def viewportEvent(self, event): - """Override Qt method""" - # 79-column edge line - offset = self.contentOffset() - x = self.blockBoundingGeometry(self.firstVisibleBlock()) \ - .translated(offset.x(), offset.y()).left() \ - +self.get_linenumberarea_width() \ - +self.fontMetrics().width('9'*self.edge_line.column)+5 - cr = self.contentsRect() - self.edge_line.setGeometry(QRect(x, cr.top(), 1, cr.bottom())) - self.__set_scrollflagarea_geometry(cr) - return TextEditBaseWidget.viewportEvent(self, event) - - #-----Misc. - def delete(self): - """Remove selected text""" - # Used by global callbacks in Spyder -> delete_action - self.remove_selected_text() - - def _apply_highlighter_color_scheme(self): - """Apply color scheme from syntax highlighter to the editor""" - hl = self.highlighter - if hl is not None: - self.set_palette(background=hl.get_background_color(), - foreground=hl.get_foreground_color()) - self.currentline_color = hl.get_currentline_color() - self.currentcell_color = hl.get_currentcell_color() - self.occurence_color = hl.get_occurence_color() - self.ctrl_click_color = hl.get_ctrlclick_color() - self.sideareas_color = hl.get_sideareas_color() - self.comment_color = hl.get_comment_color() - self.normal_color = hl.get_foreground_color() - self.matched_p_color = hl.get_matched_p_color() - self.unmatched_p_color = hl.get_unmatched_p_color() - - def apply_highlighter_settings(self, color_scheme=None): - """Apply syntax highlighter settings""" - if self.highlighter is not None: - # Updating highlighter settings (font and color scheme) - self.highlighter.setup_formats(self.font()) - if color_scheme is not None: - self.set_color_scheme(color_scheme) - else: - self.highlighter.rehighlight() - - def set_font(self, font, color_scheme=None): - """Set font""" - # Note: why using this method to set color scheme instead of - # 'set_color_scheme'? To avoid rehighlighting the document twice - # at startup. - if color_scheme is not None: - self.color_scheme = color_scheme - self.setFont(font) - self.update_linenumberarea_width() - self.apply_highlighter_settings(color_scheme) - - def set_color_scheme(self, color_scheme): - """Set color scheme for syntax highlighting""" - self.color_scheme = color_scheme - if self.highlighter is not None: - # this calls self.highlighter.rehighlight() - self.highlighter.set_color_scheme(color_scheme) - self._apply_highlighter_color_scheme() - if self.highlight_current_cell_enabled: - self.highlight_current_cell() - else: - self.unhighlight_current_cell() - if self.highlight_current_line_enabled: - self.highlight_current_line() - else: - self.unhighlight_current_line() - - def set_text(self, text): - """Set the text of the editor""" - self.setPlainText(text) - self.set_eol_chars(text) - #if self.supported_language: - #self.highlighter.rehighlight() - - def set_text_from_file(self, filename, language=None): - """Set the text of the editor from file *fname*""" - text, _enc = encoding.read(filename) - if language is None: - language = get_file_language(filename, text) - self.set_language(language) - self.set_text(text) - - def append(self, text): - """Append text to the end of the text widget""" - cursor = self.textCursor() - cursor.movePosition(QTextCursor.End) - cursor.insertText(text) - - def paste(self): - """ - Reimplement QPlainTextEdit's method to fix the following issue: - on Windows, pasted text has only 'LF' EOL chars even if the original - text has 'CRLF' EOL chars - """ - clipboard = QApplication.clipboard() - text = to_text_string(clipboard.text()) - if len(text.splitlines()) > 1: - eol_chars = self.get_line_separator() - clipboard.setText( eol_chars.join((text+eol_chars).splitlines()) ) - # Standard paste - TextEditBaseWidget.paste(self) - - def get_block_data(self, block): - """Return block data (from syntax highlighter)""" - return self.highlighter.block_data.get(block) - - def get_fold_level(self, block_nb): - """Is it a fold header line? - If so, return fold level - If not, return None""" - block = self.document().findBlockByNumber(block_nb) - return self.get_block_data(block).fold_level - - -#=============================================================================== -# High-level editor features -#=============================================================================== - @Slot() - def center_cursor_on_next_focus(self): - """QPlainTextEdit's "centerCursor" requires the widget to be visible""" - self.centerCursor() - self.disconnect(self, SIGNAL("focus_in()"), - self.center_cursor_on_next_focus) - - def go_to_line(self, line, word=''): - """Go to line number *line* and eventually highlight it""" - block = self.document().findBlockByNumber(line-1) - self.setTextCursor(QTextCursor(block)) - if self.isVisible(): - self.centerCursor() - else: - self.connect(self, SIGNAL("focus_in()"), - self.center_cursor_on_next_focus) - self.horizontalScrollBar().setValue(0) - if word and to_text_string(word) in to_text_string(block.text()): - self.find(word, QTextDocument.FindCaseSensitively) - - def exec_gotolinedialog(self): - """Execute the GoToLineDialog dialog box""" - dlg = GoToLineDialog(self) - if dlg.exec_(): - self.go_to_line(dlg.get_line_number()) - - def cleanup_code_analysis(self): - """Remove all code analysis markers""" - self.setUpdatesEnabled(False) - self.clear_extra_selections('code_analysis') - for data in self.blockuserdata_list[:]: - data.code_analysis = [] - if data.is_empty(): - del data - self.setUpdatesEnabled(True) - # When the new code analysis results are empty, it is necessary - # to update manually the scrollflag and linenumber areas (otherwise, - # the old flags will still be displayed): - self.scrollflagarea.update() - self.linenumberarea.update() - - def process_code_analysis(self, check_results): - """Analyze filename code with pyflakes""" - self.cleanup_code_analysis() - if check_results is None: - # Not able to compile module - return - self.setUpdatesEnabled(False) - cursor = self.textCursor() - document = self.document() - flags = QTextDocument.FindCaseSensitively|QTextDocument.FindWholeWords - for message, line_number in check_results: - error = 'syntax' in message - # Note: line_number start from 1 (not 0) - block = self.document().findBlockByNumber(line_number-1) - data = block.userData() - if not data: - data = BlockUserData(self) - data.code_analysis.append( (message, error) ) - block.setUserData(data) - refs = re.findall(r"\'[a-zA-Z0-9_]*\'", message) - for ref in refs: - # Highlighting found references - text = ref[1:-1] - # Scanning line number *line* and following lines if continued - def is_line_splitted(line_no): - text = to_text_string( - document.findBlockByNumber(line_no).text()) - stripped = text.strip() - return stripped.endswith('\\') or stripped.endswith(',') \ - or len(stripped) == 0 - line2 = line_number-1 - while line2 < self.blockCount()-1 and is_line_splitted(line2): - line2 += 1 - cursor.setPosition(block.position()) - cursor.movePosition(QTextCursor.StartOfBlock) - regexp = QRegExp(r"\b%s\b" % QRegExp.escape(text), - Qt.CaseSensitive) - color = self.error_color if error else self.warning_color - # Highlighting all occurences (this is a compromise as pyflakes - # do not provide the column number -- see Issue 709 on Spyder's - # GoogleCode project website) - cursor = document.find(regexp, cursor, flags) - if cursor: - while cursor and cursor.blockNumber() <= line2 \ - and cursor.blockNumber() >= line_number-1 \ - and cursor.position() > 0: - self.__highlight_selection('code_analysis', cursor, - underline_color=QColor(color)) - cursor = document.find(text, cursor, flags) - self.update_extra_selections() - self.setUpdatesEnabled(True) - self.linenumberarea.update() - - def __show_code_analysis_results(self, line_number, code_analysis): - """Show warning/error messages""" - msglist = [ msg for msg, _error in code_analysis ] - self.show_calltip(_("Code analysis"), msglist, - color='#129625', at_line=line_number) - - def go_to_next_warning(self): - """Go to next code analysis warning message - and return new cursor position""" - block = self.textCursor().block() - line_count = self.document().blockCount() - while True: - if block.blockNumber()+1 < line_count: - block = block.next() - else: - block = self.document().firstBlock() - data = block.userData() - if data and data.code_analysis: - break - line_number = block.blockNumber()+1 - self.go_to_line(line_number) - self.__show_code_analysis_results(line_number, data.code_analysis) - return self.get_position('cursor') - - def go_to_previous_warning(self): - """Go to previous code analysis warning message - and return new cursor position""" - block = self.textCursor().block() - while True: - if block.blockNumber() > 0: - block = block.previous() - else: - block = self.document().lastBlock() - data = block.userData() - if data and data.code_analysis: - break - line_number = block.blockNumber()+1 - self.go_to_line(line_number) - self.__show_code_analysis_results(line_number, data.code_analysis) - return self.get_position('cursor') - - - #------Tasks management - def go_to_next_todo(self): - """Go to next todo and return new cursor position""" - block = self.textCursor().block() - line_count = self.document().blockCount() - while True: - if block.blockNumber()+1 < line_count: - block = block.next() - else: - block = self.document().firstBlock() - data = block.userData() - if data and data.todo: - break - line_number = block.blockNumber()+1 - self.go_to_line(line_number) - self.show_calltip(_("To do"), data.todo, - color='#3096FC', at_line=line_number) - return self.get_position('cursor') - - def process_todo(self, todo_results): - """Process todo finder results""" - for data in self.blockuserdata_list[:]: - data.todo = '' - if data.is_empty(): - del data - for message, line_number in todo_results: - block = self.document().findBlockByNumber(line_number-1) - data = block.userData() - if not data: - data = BlockUserData(self) - data.todo = message - block.setUserData(data) - self.scrollflagarea.update() - - - #------Comments/Indentation - def add_prefix(self, prefix): - """Add prefix to current line or selected line(s)""" - cursor = self.textCursor() - if self.has_selected_text(): - # Add prefix to selected line(s) - start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() - - # Let's see if selection begins at a block start - first_pos = min([start_pos, end_pos]) - first_cursor = self.textCursor() - first_cursor.setPosition(first_pos) - begins_at_block_start = first_cursor.atBlockStart() - - cursor.beginEditBlock() - cursor.setPosition(end_pos) - # Check if end_pos is at the start of a block: if so, starting - # changes from the previous block - if cursor.atBlockStart(): - cursor.movePosition(QTextCursor.PreviousBlock) - if cursor.position() < start_pos: - cursor.setPosition(start_pos) - - while cursor.position() >= start_pos: - cursor.movePosition(QTextCursor.StartOfBlock) - cursor.insertText(prefix) - if start_pos == 0 and cursor.blockNumber() == 0: - # Avoid infinite loop when indenting the very first line - break - cursor.movePosition(QTextCursor.PreviousBlock) - cursor.movePosition(QTextCursor.EndOfBlock) - cursor.endEditBlock() - if begins_at_block_start: - # Extending selection to prefix: - cursor = self.textCursor() - start_pos = cursor.selectionStart() - end_pos = cursor.selectionEnd() - if start_pos < end_pos: - start_pos -= len(prefix) - else: - end_pos -= len(prefix) - cursor.setPosition(start_pos, QTextCursor.MoveAnchor) - cursor.setPosition(end_pos, QTextCursor.KeepAnchor) - self.setTextCursor(cursor) - else: - # Add prefix to current line - cursor.beginEditBlock() - cursor.movePosition(QTextCursor.StartOfBlock) - cursor.insertText(prefix) - cursor.endEditBlock() - - def __is_cursor_at_start_of_block(self, cursor): - cursor.movePosition(QTextCursor.StartOfBlock) - - def remove_suffix(self, suffix): - """ - Remove suffix from current line (there should not be any selection) - """ - cursor = self.textCursor() - cursor.setPosition(cursor.position()-len(suffix), - QTextCursor.KeepAnchor) - if to_text_string(cursor.selectedText()) == suffix: - cursor.removeSelectedText() - - def remove_prefix(self, prefix): - """Remove prefix from current line or selected line(s)""" - cursor = self.textCursor() - if self.has_selected_text(): - # Remove prefix from selected line(s) - start_pos, end_pos = sorted([cursor.selectionStart(), - cursor.selectionEnd()]) - cursor.setPosition(start_pos) - if not cursor.atBlockStart(): - cursor.movePosition(QTextCursor.StartOfBlock) - start_pos = cursor.position() - cursor.beginEditBlock() - cursor.setPosition(end_pos) - # Check if end_pos is at the start of a block: if so, starting - # changes from the previous block - if cursor.atBlockStart(): - cursor.movePosition(QTextCursor.PreviousBlock) - if cursor.position() < start_pos: - cursor.setPosition(start_pos) - - cursor.movePosition(QTextCursor.StartOfBlock) - old_pos = None - while cursor.position() >= start_pos: - new_pos = cursor.position() - if old_pos == new_pos: - break - else: - old_pos = new_pos - line_text = to_text_string(cursor.block().text()) - if (prefix.strip() and line_text.lstrip().startswith(prefix) - or line_text.startswith(prefix)): - cursor.movePosition(QTextCursor.Right, - QTextCursor.MoveAnchor, - line_text.find(prefix)) - cursor.movePosition(QTextCursor.Right, - QTextCursor.KeepAnchor, len(prefix)) - cursor.removeSelectedText() - cursor.movePosition(QTextCursor.PreviousBlock) - cursor.endEditBlock() - else: - # Remove prefix from current line - cursor.movePosition(QTextCursor.StartOfBlock) - line_text = to_text_string(cursor.block().text()) - if (prefix.strip() and line_text.lstrip().startswith(prefix) - or line_text.startswith(prefix)): - cursor.movePosition(QTextCursor.Right, - QTextCursor.MoveAnchor, - line_text.find(prefix)) - cursor.movePosition(QTextCursor.Right, - QTextCursor.KeepAnchor, len(prefix)) - cursor.removeSelectedText() - - def fix_indent(self, forward=True, comment_or_string=False): - """ - Fix indentation (Python only, no text selection) - forward=True: fix indent only if text is not enough indented - (otherwise force indent) - forward=False: fix indent only if text is too much indented - (otherwise force unindent) - - Returns True if indent needed to be fixed - """ - if not self.is_python_like(): - return - cursor = self.textCursor() - block_nb = cursor.blockNumber() - prevline = None - for prevline in range(block_nb-1, -1, -1): - cursor.movePosition(QTextCursor.PreviousBlock) - prevtext = to_text_string(cursor.block().text()).rstrip() - if not prevtext.strip().startswith('#'): - break - - if not prevline: - return False - - indent = self.get_block_indentation(block_nb) - correct_indent = self.get_block_indentation(prevline) - - if not comment_or_string: - if prevtext.endswith(':'): - # Indent - correct_indent += len(self.indent_chars) - elif prevtext.endswith('continue') or prevtext.endswith('break') \ - or prevtext.endswith('pass'): - # Unindent - correct_indent -= len(self.indent_chars) - elif prevtext.endswith(',') \ - and len(re.split(r'\(|\{|\[', prevtext)) > 1: - rlmap = {")":"(", "]":"[", "}":"{"} - for par in rlmap: - i_right = prevtext.rfind(par) - if i_right != -1: - prevtext = prevtext[:i_right] - for _i in range(len(prevtext.split(par))): - i_left = prevtext.rfind(rlmap[par]) - if i_left != -1: - prevtext = prevtext[:i_left] - else: - break - else: - prevexpr = re.split(r'\(|\{|\[', prevtext)[-1] - correct_indent = len(prevtext)-len(prevexpr) - - if (forward and indent >= correct_indent) or \ - (not forward and indent <= correct_indent): - # No indentation fix is necessary - return False - - if correct_indent >= 0: - cursor = self.textCursor() - cursor.beginEditBlock() - cursor.movePosition(QTextCursor.StartOfBlock) - cursor.setPosition(cursor.position()+indent, QTextCursor.KeepAnchor) - cursor.removeSelectedText() - cursor.insertText(self.indent_chars[0]*correct_indent) - cursor.endEditBlock() - return True - - def clear_all_output(self): - """removes all ouput in the ipynb format (Json only)""" - if self.is_json() and nbformat is not None: - try: - nb = nbformat.current.reads(self.toPlainText(), 'json') - except Exception as e: - QMessageBox.critical(self, _('Removal error'), - _("It was not possible to remove outputs from " - "this notebook. The error is:\n\n") + \ - to_text_string(e)) - return - if nb.worksheets: - for cell in nb.worksheets[0].cells: - if 'outputs' in cell: - cell['outputs'] = [] - if 'prompt_number' in cell: - cell['prompt_number'] = None - # We do the following rather than using self.setPlainText - # to benefit from QTextEdit's undo/redo feature. - self.selectAll() - self.insertPlainText(nbformat.current.writes(nb, 'json')) - else: - return - - def convert_notebook(self): - """Convert an IPython notebook to a Python script in editor""" - try: - try: # >3.0 - nb = nbformat.reads(self.toPlainText(), as_version=4) - except AttributeError: - nb = nbformat.current.reads(self.toPlainText(), 'json') - except Exception as e: - QMessageBox.critical(self, _('Conversion error'), - _("It was not possible to convert this " - "notebook. The error is:\n\n") + \ - to_text_string(e)) - return - script = nbexporter().from_notebook_node(nb)[0] - self.sig_new_file.emit(script) - - def indent(self, force=False): - """ - Indent current line or selection - - force=True: indent even if cursor is not a the beginning of the line - """ - leading_text = self.get_text('sol', 'cursor') - if self.has_selected_text(): - self.add_prefix(self.indent_chars) - elif force or not leading_text.strip() \ - or (self.tab_indents and self.tab_mode): - if self.is_python_like(): - if not self.fix_indent(forward=True): - self.add_prefix(self.indent_chars) - else: - self.add_prefix(self.indent_chars) - else: - if len(self.indent_chars) > 1: - length = len(self.indent_chars) - self.insert_text(" "*(length-(len(leading_text) % length))) - else: - self.insert_text(self.indent_chars) - - def indent_or_replace(self): - """Indent or replace by 4 spaces depending on selection and tab mode""" - if (self.tab_indents and self.tab_mode) or not self.has_selected_text(): - self.indent() - else: - cursor = self.textCursor() - if self.get_selected_text() == \ - to_text_string(cursor.block().text()): - self.indent() - else: - cursor1 = self.textCursor() - cursor1.setPosition(cursor.selectionStart()) - cursor2 = self.textCursor() - cursor2.setPosition(cursor.selectionEnd()) - if cursor1.blockNumber() != cursor2.blockNumber(): - self.indent() - else: - self.replace(self.indent_chars) - - def unindent(self, force=False): - """ - Unindent current line or selection - - force=True: unindent even if cursor is not a the beginning of the line - """ - if self.has_selected_text(): - self.remove_prefix(self.indent_chars) - else: - leading_text = self.get_text('sol', 'cursor') - if force or not leading_text.strip() \ - or (self.tab_indents and self.tab_mode): - if self.is_python_like(): - if not self.fix_indent(forward=False): - self.remove_prefix(self.indent_chars) - elif leading_text.endswith('\t'): - self.remove_prefix('\t') - else: - self.remove_prefix(self.indent_chars) - - def toggle_comment(self): - """Toggle comment on current line or selection""" - cursor = self.textCursor() - start_pos, end_pos = sorted([cursor.selectionStart(), - cursor.selectionEnd()]) - cursor.setPosition(end_pos) - last_line = cursor.block().blockNumber() - if cursor.atBlockStart() and start_pos != end_pos: - last_line -= 1 - cursor.setPosition(start_pos) - first_line = cursor.block().blockNumber() - # If the selection contains only commented lines and surrounding - # whitespace, uncomment. Otherwise, comment. - is_comment_or_whitespace = True - at_least_one_comment = False - for _line_nb in range(first_line, last_line+1): - text = to_text_string(cursor.block().text()).lstrip() - is_comment = text.startswith(self.comment_string) - is_whitespace = (text == '') - is_comment_or_whitespace *= (is_comment or is_whitespace) - if is_comment: - at_least_one_comment = True - cursor.movePosition(QTextCursor.NextBlock) - if is_comment_or_whitespace and at_least_one_comment: - self.uncomment() - else: - self.comment() - - def comment(self): - """Comment current line or selection""" - self.add_prefix(self.comment_string) - - def uncomment(self): - """Uncomment current line or selection""" - self.remove_prefix(self.comment_string) - - def __blockcomment_bar(self): - return self.comment_string + '='*(79-len(self.comment_string)) - - def blockcomment(self): - """Block comment current line or selection""" - comline = self.__blockcomment_bar() + self.get_line_separator() - cursor = self.textCursor() - if self.has_selected_text(): - self.extend_selection_to_complete_lines() - start_pos, end_pos = cursor.selectionStart(), cursor.selectionEnd() - else: - start_pos = end_pos = cursor.position() - cursor.beginEditBlock() - cursor.setPosition(start_pos) - cursor.movePosition(QTextCursor.StartOfBlock) - while cursor.position() <= end_pos: - cursor.insertText(self.comment_string + " ") - cursor.movePosition(QTextCursor.EndOfBlock) - if cursor.atEnd(): - break - cursor.movePosition(QTextCursor.NextBlock) - end_pos += len(self.comment_string + " ") - cursor.setPosition(end_pos) - cursor.movePosition(QTextCursor.EndOfBlock) - if cursor.atEnd(): - cursor.insertText(self.get_line_separator()) - else: - cursor.movePosition(QTextCursor.NextBlock) - cursor.insertText(comline) - cursor.setPosition(start_pos) - cursor.movePosition(QTextCursor.StartOfBlock) - cursor.insertText(comline) - cursor.endEditBlock() - - def unblockcomment(self): - """Un-block comment current line or selection""" - def __is_comment_bar(cursor): - return to_text_string(cursor.block().text() - ).startswith(self.__blockcomment_bar()) - # Finding first comment bar - cursor1 = self.textCursor() - if __is_comment_bar(cursor1): - return - while not __is_comment_bar(cursor1): - cursor1.movePosition(QTextCursor.PreviousBlock) - if cursor1.atStart(): - break - if not __is_comment_bar(cursor1): - return - def __in_block_comment(cursor): - cs = self.comment_string - return to_text_string(cursor.block().text()).startswith(cs) - # Finding second comment bar - cursor2 = QTextCursor(cursor1) - cursor2.movePosition(QTextCursor.NextBlock) - while not __is_comment_bar(cursor2) and __in_block_comment(cursor2): - cursor2.movePosition(QTextCursor.NextBlock) - if cursor2.block() == self.document().lastBlock(): - break - if not __is_comment_bar(cursor2): - return - # Removing block comment - cursor3 = self.textCursor() - cursor3.beginEditBlock() - cursor3.setPosition(cursor1.position()) - cursor3.movePosition(QTextCursor.NextBlock) - while cursor3.position() < cursor2.position(): - cursor3.setPosition(cursor3.position()+2, QTextCursor.KeepAnchor) - cursor3.removeSelectedText() - cursor3.movePosition(QTextCursor.NextBlock) - for cursor in (cursor2, cursor1): - cursor3.setPosition(cursor.position()) - cursor3.select(QTextCursor.BlockUnderCursor) - cursor3.removeSelectedText() - cursor3.endEditBlock() - - #------Autoinsertion of quotes/colons - def __get_current_color(self): - """Get the syntax highlighting color for the current cursor position""" - cursor = self.textCursor() - block = cursor.block() - pos = cursor.position() - block.position() # relative pos within block - layout = block.layout() - block_formats = layout.additionalFormats() - - if block_formats: - # To easily grab current format for autoinsert_colons - if cursor.atBlockEnd(): - current_format = block_formats[-1].format - else: - current_format = None - for fmt in block_formats: - if (pos >= fmt.start) and (pos < fmt.start + fmt.length): - current_format = fmt.format - color = current_format.foreground().color().name() - return color - else: - return None - - def in_comment_or_string(self): - """Is the cursor inside or next to a comment or string?""" - if self.highlighter: - current_color = self.__get_current_color() - comment_color = self.highlighter.get_color_name('comment') - string_color = self.highlighter.get_color_name('string') - if (current_color == comment_color) or (current_color == string_color): - return True - else: - return False - else: - return False - - def __colon_keyword(self, text): - stmt_kws = ['def', 'for', 'if', 'while', 'with', 'class', 'elif', - 'except'] - whole_kws = ['else', 'try', 'except', 'finally'] - text = text.lstrip() - words = text.split() - if any([text == wk for wk in whole_kws]): - return True - elif len(words) < 2: - return False - elif any([words[0] == sk for sk in stmt_kws]): - return True - else: - return False - - def __forbidden_colon_end_char(self, text): - end_chars = [':', '\\', '[', '{', '(', ','] - text = text.rstrip() - if any([text.endswith(c) for c in end_chars]): - return True - else: - return False - - def __unmatched_braces_in_line(self, text): - block = self.textCursor().block() - line_pos = block.position() - for pos, char in enumerate(text): - if char in ['(', '[', '{']: - match = self.find_brace_match(line_pos+pos, char, forward=True) - if (match is None) or (match > line_pos+len(text)): - return True - return False - - def autoinsert_colons(self): - """Decide if we want to autoinsert colons""" - line_text = self.get_text('sol', 'cursor') - if not self.textCursor().atBlockEnd(): - return False - elif self.in_comment_or_string(): - return False - elif not self.__colon_keyword(line_text): - return False - elif self.__forbidden_colon_end_char(line_text): - return False - elif self.__unmatched_braces_in_line(line_text): - return False - else: - return True - - def __unmatched_quotes_in_line(self, text): - """Return whether a string has open quotes. - This simply counts whether the number of quote characters of either - type in the string is odd. - - Take from the IPython project (in IPython/core/completer.py in v0.13) - Spyder team: Add some changes to deal with escaped quotes - - - Copyright (C) 2008-2011 IPython Development Team - - Copyright (C) 2001-2007 Fernando Perez. - - Copyright (C) 2001 Python Software Foundation, www.python.org - - Distributed under the terms of the BSD License. - """ - # We check " first, then ', so complex cases with nested quotes will - # get the " to take precedence. - text = text.replace("\\'", "") - text = text.replace('\\"', '') - if text.count('"') % 2: - return '"' - elif text.count("'") % 2: - return "'" - else: - return '' - - def __next_char(self): - cursor = self.textCursor() - cursor.movePosition(QTextCursor.NextCharacter, - QTextCursor.KeepAnchor) - next_char = to_text_string(cursor.selectedText()) - return next_char - - def __in_comment(self): - if self.highlighter: - current_color = self.__get_current_color() - comment_color = self.highlighter.get_color_name('comment') - if current_color == comment_color: - return True - else: - return False - else: - return False - - def autoinsert_quotes(self, key): - """Control how to automatically insert quotes in various situations""" - char = {Qt.Key_QuoteDbl: '"', Qt.Key_Apostrophe: '\''}[key] - - line_text = self.get_text('sol', 'eol') - line_to_cursor = self.get_text('sol', 'cursor') - cursor = self.textCursor() - last_three = self.get_text('sol', 'cursor')[-3:] - last_two = self.get_text('sol', 'cursor')[-2:] - trailing_text = self.get_text('cursor', 'eol').strip() - - if self.has_selected_text(): - text = ''.join([char, self.get_selected_text(), char]) - self.insert_text(text) - elif self.__in_comment(): - self.insert_text(char) - elif len(trailing_text) > 0 and not \ - self.__unmatched_quotes_in_line(line_to_cursor) == char: - self.insert_text(char) - elif self.__unmatched_quotes_in_line(line_text) and \ - (not last_three == 3*char): - self.insert_text(char) - # Move to the right if we are before a quote - elif self.__next_char() == char: - cursor.movePosition(QTextCursor.NextCharacter, - QTextCursor.KeepAnchor, 1) - cursor.clearSelection() - self.setTextCursor(cursor) - # Automatic insertion of triple double quotes (for docstrings) - elif last_three == 3*char: - self.insert_text(3*char) - cursor = self.textCursor() - cursor.movePosition(QTextCursor.PreviousCharacter, - QTextCursor.KeepAnchor, 3) - cursor.clearSelection() - self.setTextCursor(cursor) - # If last two chars are quotes, just insert one more because most - # probably the user wants to write a docstring - elif last_two == 2*char: - self.insert_text(char) - # Automatic insertion of quotes - else: - self.insert_text(2*char) - cursor = self.textCursor() - cursor.movePosition(QTextCursor.PreviousCharacter) - self.setTextCursor(cursor) - -#=============================================================================== -# Qt Event handlers -#=============================================================================== - def setup_context_menu(self): - """Setup context menu""" - self.undo_action = create_action(self, _("Undo"), - shortcut=keybinding('Undo'), - icon=get_icon('undo.png'), triggered=self.undo) - self.redo_action = create_action(self, _("Redo"), - shortcut=keybinding('Redo'), - icon=get_icon('redo.png'), triggered=self.redo) - self.cut_action = create_action(self, _("Cut"), - shortcut=keybinding('Cut'), - icon=get_icon('editcut.png'), triggered=self.cut) - self.copy_action = create_action(self, _("Copy"), - shortcut=keybinding('Copy'), - icon=get_icon('editcopy.png'), triggered=self.copy) - paste_action = create_action(self, _("Paste"), - shortcut=keybinding('Paste'), - icon=get_icon('editpaste.png'), triggered=self.paste) - self.delete_action = create_action(self, _("Delete"), - shortcut=keybinding('Delete'), - icon=get_icon('editdelete.png'), - triggered=self.delete) - selectall_action = create_action(self, _("Select All"), - shortcut=keybinding('SelectAll'), - icon=get_icon('selectall.png'), - triggered=self.selectAll) - toggle_comment_action = create_action(self, - _("Comment")+"/"+_("Uncomment"), - icon=get_icon("comment.png"), - triggered=self.toggle_comment) - self.clear_all_output_action = create_action(self, - _("Clear all ouput"), icon='ipython_console.png', - triggered=self.clear_all_output) - self.ipynb_convert_action = create_action(self, - _("Convert to Python script"), - triggered=self.convert_notebook, - icon='python.png') - self.gotodef_action = create_action(self, _("Go to definition"), - triggered=self.go_to_definition_from_cursor) - self.run_selection_action = create_action(self, - _("Run &selection or current line"), - icon='run_selection.png', - triggered=lambda: self.emit(SIGNAL('run_selection()'))) - zoom_in_action = create_action(self, _("Zoom in"), - QKeySequence(QKeySequence.ZoomIn), icon='zoom_in.png', - triggered=lambda: self.emit(SIGNAL('zoom_in()'))) - zoom_out_action = create_action(self, _("Zoom out"), - QKeySequence(QKeySequence.ZoomOut), icon='zoom_out.png', - triggered=lambda: self.emit(SIGNAL('zoom_out()'))) - zoom_reset_action = create_action(self, _("Zoom reset"), - QKeySequence("Ctrl+0"), - triggered=lambda: self.emit(SIGNAL('zoom_reset()'))) - self.menu = QMenu(self) - actions_1 = [self.undo_action, self.redo_action, None, self.cut_action, - self.copy_action, paste_action, self.delete_action, None] - actions_2 = [selectall_action, None, zoom_in_action, zoom_out_action, - zoom_reset_action, None, toggle_comment_action, None, - self.run_selection_action, self.gotodef_action] - if nbformat is not None: - nb_actions = [self.clear_all_output_action, - self.ipynb_convert_action, None] - actions = actions_1 + nb_actions + actions_2 - add_actions(self.menu, actions) - else: - actions = actions_1 + actions_2 - add_actions(self.menu, actions) - - # Read-only context-menu - self.readonly_menu = QMenu(self) - add_actions(self.readonly_menu, - (self.copy_action, None, selectall_action, - self.gotodef_action)) - - def keyPressEvent(self, event): - """Reimplement Qt method""" - key = event.key() - ctrl = event.modifiers() & Qt.ControlModifier - shift = event.modifiers() & Qt.ShiftModifier - text = to_text_string(event.text()) - if text: - self.__clear_occurences() - if QToolTip.isVisible(): - self.hide_tooltip_if_necessary(key) - if key in (Qt.Key_Enter, Qt.Key_Return): - if not shift and not ctrl: - if self.add_colons_enabled and self.is_python_like() and \ - self.autoinsert_colons(): - self.insert_text(':' + self.get_line_separator()) - self.fix_indent() - elif self.is_completion_widget_visible() \ - and self.codecompletion_enter: - self.select_completion_list() - else: - cmt_or_str = self.in_comment_or_string() - TextEditBaseWidget.keyPressEvent(self, event) - self.fix_indent(comment_or_string=cmt_or_str) - elif shift: - self.emit(SIGNAL('run_cell_and_advance()')) - elif ctrl: - self.emit(SIGNAL('run_cell()')) - elif key == Qt.Key_Insert and not shift and not ctrl: - self.setOverwriteMode(not self.overwriteMode()) - elif key == Qt.Key_Backspace and not shift and not ctrl: - leading_text = self.get_text('sol', 'cursor') - leading_length = len(leading_text) - trailing_spaces = leading_length-len(leading_text.rstrip()) - if self.has_selected_text() or not self.intelligent_backspace: - TextEditBaseWidget.keyPressEvent(self, event) - else: - trailing_text = self.get_text('cursor', 'eol') - if not leading_text.strip() \ - and leading_length > len(self.indent_chars): - if leading_length % len(self.indent_chars) == 0: - self.unindent() - else: - TextEditBaseWidget.keyPressEvent(self, event) - elif trailing_spaces and not trailing_text.strip(): - self.remove_suffix(leading_text[-trailing_spaces:]) - elif leading_text and trailing_text and \ - leading_text[-1]+trailing_text[0] in ('()', '[]', '{}', - '\'\'', '""'): - cursor = self.textCursor() - cursor.movePosition(QTextCursor.PreviousCharacter) - cursor.movePosition(QTextCursor.NextCharacter, - QTextCursor.KeepAnchor, 2) - cursor.removeSelectedText() - else: - TextEditBaseWidget.keyPressEvent(self, event) - if self.is_completion_widget_visible(): - self.completion_text = self.completion_text[:-1] - elif key == Qt.Key_Period: - self.insert_text(text) - if (self.is_python_like()) and not \ - self.in_comment_or_string() and self.codecompletion_auto: - # Enable auto-completion only if last token isn't a float - last_obj = getobj(self.get_text('sol', 'cursor')) - if last_obj and not last_obj.isdigit(): - self.do_completion(automatic=True) - elif key == Qt.Key_Home: - self.stdkey_home(shift, ctrl) - elif key == Qt.Key_End: - # See Issue 495: on MacOS X, it is necessary to redefine this - # basic action which should have been implemented natively - self.stdkey_end(shift, ctrl) - elif text == '(' and not self.has_selected_text(): - self.hide_completion_widget() - position = self.get_position('cursor') - s_trailing_text = self.get_text('cursor', 'eol').strip() - if self.close_parentheses_enabled and \ - (len(s_trailing_text) == 0 or \ - s_trailing_text[0] in (',', ')', ']', '}')): - self.insert_text('()') - cursor = self.textCursor() - cursor.movePosition(QTextCursor.PreviousCharacter) - self.setTextCursor(cursor) - else: - self.insert_text(text) - if self.is_python_like() and self.get_text('sol', 'cursor') and \ - self.calltips: - self.emit(SIGNAL('show_object_info(int)'), position) - elif text in ('[', '{') and not self.has_selected_text() \ - and self.close_parentheses_enabled: - s_trailing_text = self.get_text('cursor', 'eol').strip() - if len(s_trailing_text) == 0 or \ - s_trailing_text[0] in (',', ')', ']', '}'): - self.insert_text({'{': '{}', '[': '[]'}[text]) - cursor = self.textCursor() - cursor.movePosition(QTextCursor.PreviousCharacter) - self.setTextCursor(cursor) - else: - TextEditBaseWidget.keyPressEvent(self, event) - elif key in (Qt.Key_QuoteDbl, Qt.Key_Apostrophe) and \ - self.close_quotes_enabled: - self.autoinsert_quotes(key) - elif key in (Qt.Key_ParenRight, Qt.Key_BraceRight, Qt.Key_BracketRight)\ - and not self.has_selected_text() and self.close_parentheses_enabled \ - and not self.textCursor().atBlockEnd(): - cursor = self.textCursor() - cursor.movePosition(QTextCursor.NextCharacter, - QTextCursor.KeepAnchor) - text = to_text_string(cursor.selectedText()) - if text == {Qt.Key_ParenRight: ')', Qt.Key_BraceRight: '}', - Qt.Key_BracketRight: ']'}[key]: - cursor.clearSelection() - self.setTextCursor(cursor) - else: - TextEditBaseWidget.keyPressEvent(self, event) - elif key == Qt.Key_Colon and not self.has_selected_text() \ - and self.auto_unindent_enabled: - leading_text = self.get_text('sol', 'cursor') - if leading_text.lstrip() in ('else', 'finally'): - ind = lambda txt: len(txt)-len(txt.lstrip()) - prevtxt = to_text_string(self.textCursor( - ).block().previous().text()) - if ind(leading_text) == ind(prevtxt): - self.unindent(force=True) - TextEditBaseWidget.keyPressEvent(self, event) - elif key == Qt.Key_Space and not shift and not ctrl \ - and not self.has_selected_text() and self.auto_unindent_enabled: - leading_text = self.get_text('sol', 'cursor') - if leading_text.lstrip() in ('elif', 'except'): - ind = lambda txt: len(txt)-len(txt.lstrip()) - prevtxt = to_text_string(self.textCursor( - ).block().previous().text()) - if ind(leading_text) == ind(prevtxt): - self.unindent(force=True) - TextEditBaseWidget.keyPressEvent(self, event) - elif key == Qt.Key_Tab: - # Important note: can't be called with a QShortcut because - # of its singular role with respect to widget focus management - if not self.has_selected_text() and not self.tab_mode: - self.intelligent_tab() - else: - # indent the selected text - self.indent_or_replace() - elif key == Qt.Key_Backtab: - # Backtab, i.e. Shift+, could be treated as a QShortcut but - # there is no point since can't (see above) - if not self.has_selected_text() and not self.tab_mode: - self.intelligent_backtab() - else: - # indent the selected text - self.unindent() - else: - TextEditBaseWidget.keyPressEvent(self, event) - if self.is_completion_widget_visible() and text: - self.completion_text += text - - def mouseMoveEvent(self, event): - """Underline words when pressing """ - if self.has_selected_text(): - TextEditBaseWidget.mouseMoveEvent(self, event) - return - if self.go_to_definition_enabled and \ - event.modifiers() & Qt.ControlModifier: - text = self.get_word_at(event.pos()) - if text and (self.is_python_like())\ - and not sourcecode.is_keyword(to_text_string(text)): - if not self.__cursor_changed: - QApplication.setOverrideCursor( - QCursor(Qt.PointingHandCursor)) - self.__cursor_changed = True - cursor = self.cursorForPosition(event.pos()) - cursor.select(QTextCursor.WordUnderCursor) - self.clear_extra_selections('ctrl_click') - self.__highlight_selection('ctrl_click', cursor, update=True, - foreground_color=self.ctrl_click_color, - underline_color=self.ctrl_click_color, - underline_style=QTextCharFormat.SingleUnderline) - event.accept() - return - if self.__cursor_changed: - QApplication.restoreOverrideCursor() - self.__cursor_changed = False - self.clear_extra_selections('ctrl_click') - TextEditBaseWidget.mouseMoveEvent(self, event) - - def leaveEvent(self, event): - """If cursor has not been restored yet, do it now""" - if self.__cursor_changed: - QApplication.restoreOverrideCursor() - self.__cursor_changed = False - self.clear_extra_selections('ctrl_click') - TextEditBaseWidget.leaveEvent(self, event) - - def go_to_definition_from_cursor(self, cursor=None): - """Go to definition from cursor instance (QTextCursor)""" - if not self.go_to_definition_enabled: - return - if cursor is None: - cursor = self.textCursor() - if self.in_comment_or_string(): - return - position = cursor.position() - text = to_text_string(cursor.selectedText()) - if len(text) == 0: - cursor.select(QTextCursor.WordUnderCursor) - text = to_text_string(cursor.selectedText()) - if not text is None: - self.emit(SIGNAL("go_to_definition(int)"), position) - - def mousePressEvent(self, event): - """Reimplement Qt method""" - if event.button() == Qt.LeftButton\ - and (event.modifiers() & Qt.ControlModifier): - TextEditBaseWidget.mousePressEvent(self, event) - cursor = self.cursorForPosition(event.pos()) - self.go_to_definition_from_cursor(cursor) - else: - TextEditBaseWidget.mousePressEvent(self, event) - - def contextMenuEvent(self, event): - """Reimplement Qt method""" - nonempty_selection = self.has_selected_text() - self.copy_action.setEnabled(nonempty_selection) - self.cut_action.setEnabled(nonempty_selection) - self.delete_action.setEnabled(nonempty_selection) - self.clear_all_output_action.setVisible(self.is_json()) - self.ipynb_convert_action.setVisible(self.is_json()) - self.run_selection_action.setEnabled(nonempty_selection) - self.run_selection_action.setVisible(self.is_python()) - self.gotodef_action.setVisible(self.go_to_definition_enabled \ - and self.is_python_like()) - - # Code duplication go_to_definition_from_cursor and mouse_move_event - cursor = self.textCursor() - text = to_text_string(cursor.selectedText()) - if len(text) == 0: - cursor.select(QTextCursor.WordUnderCursor) - text = to_text_string(cursor.selectedText()) - - self.undo_action.setEnabled( self.document().isUndoAvailable()) - self.redo_action.setEnabled( self.document().isRedoAvailable()) - menu = self.menu - if self.isReadOnly(): - menu = self.readonly_menu - menu.popup(event.globalPos()) - event.accept() - - #------ Drag and drop - def dragEnterEvent(self, event): - """Reimplement Qt method - Inform Qt about the types of data that the widget accepts""" - if mimedata2url(event.mimeData()): - # Let the parent widget handle this - event.ignore() - else: - TextEditBaseWidget.dragEnterEvent(self, event) - - def dropEvent(self, event): - """Reimplement Qt method - Unpack dropped data and handle it""" - if mimedata2url(event.mimeData()): - # Let the parent widget handle this - event.ignore() - else: - TextEditBaseWidget.dropEvent(self, event) - - #------ Paint event - def paintEvent(self, event): - """Overrides paint event to update the list of visible blocks""" - self.update_visible_blocks(event) - TextEditBaseWidget.paintEvent(self, event) - self.painted.emit(event) - - def update_visible_blocks(self, event): - """Update the list of visible blocks/lines position""" - self.__visible_blocks[:] = [] - block = self.firstVisibleBlock() - blockNumber = block.blockNumber() - top = int(self.blockBoundingGeometry(block).translated( - self.contentOffset()).top()) - bottom = top + int(self.blockBoundingRect(block).height()) - ebottom_top = 0 - ebottom_bottom = self.height() - - while block.isValid(): - visible = (top >= ebottom_top and bottom <= ebottom_bottom) - if not visible: - break - if block.isVisible(): - self.__visible_blocks.append((top, blockNumber+1, block)) - block = block.next() - top = bottom - bottom = top + int(self.blockBoundingRect(block).height()) - blockNumber = block.blockNumber() - - def _draw_editor_cell_divider(self): - """Draw a line on top of a define cell""" - if self.supported_cell_language: - cell_line_color = self.comment_color - painter = QPainter(self.viewport()) - pen = painter.pen() - pen.setStyle(Qt.SolidLine) - pen.setBrush(cell_line_color) - painter.setPen(pen) - - for top, line_number, block in self.visible_blocks: - if self.is_cell_separator(block): - painter.drawLine(4, top, self.width(), top) - - @property - def visible_blocks(self): - """ - Returns the list of visible blocks. - - Each element in the list is a tuple made up of the line top position, - the line number (already 1 based), and the QTextBlock itself. - - :return: A list of tuple(top position, line number, block) - :rtype: List of tuple(int, int, QtGui.QTextBlock) - """ - return self.__visible_blocks - -#=============================================================================== -# CodeEditor's Printer -#=============================================================================== - -#TODO: Implement the header and footer support -class Printer(QPrinter): - def __init__(self, mode=QPrinter.ScreenResolution, header_font=None): - QPrinter.__init__(self, mode) - self.setColorMode(QPrinter.Color) - self.setPageOrder(QPrinter.FirstPageFirst) - self.date = time.ctime() - if header_font is not None: - self.header_font = header_font - - # The following method is simply ignored by QPlainTextEdit - # (this is a copy from QsciEditor's Printer) - def formatPage(self, painter, drawing, area, pagenr): - header = '%s - %s - Page %s' % (self.docName(), self.date, pagenr) - painter.save() - painter.setFont(self.header_font) - painter.setPen(QColor(Qt.black)) - if drawing: - painter.drawText(area.right()-painter.fontMetrics().width(header), - area.top()+painter.fontMetrics().ascent(), header) - area.setTop(area.top()+painter.fontMetrics().height()+5) - painter.restore() - - -#=============================================================================== -# Editor + Class browser test -#=============================================================================== -class TestWidget(QSplitter): - def __init__(self, parent): - QSplitter.__init__(self, parent) - self.editor = CodeEditor(self) - self.editor.setup_editor(linenumbers=True, markers=True, tab_mode=False, - font=QFont("Courier New", 10), - show_blanks=True, color_scheme='Pydev') - self.addWidget(self.editor) - from spyderlib.widgets.editortools import OutlineExplorerWidget - self.classtree = OutlineExplorerWidget(self) - self.addWidget(self.classtree) - self.connect(self.classtree, SIGNAL("edit_goto(QString,int,QString)"), - lambda _fn, line, word: self.editor.go_to_line(line, word)) - self.setStretchFactor(0, 4) - self.setStretchFactor(1, 1) - self.setWindowIcon(get_icon('spyder.svg')) - - def load(self, filename): - self.editor.set_text_from_file(filename) - self.setWindowTitle("%s - %s (%s)" % (_("Editor"), - osp.basename(filename), - osp.dirname(filename))) - self.classtree.set_current_editor(self.editor, filename, False, False) - -def test(fname): - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - app.setStyle('Plastique') - win = TestWidget(None) - win.show() - win.load(fname) - win.resize(1000, 800) - - from spyderlib.utils.codeanalysis import (check_with_pyflakes, - check_with_pep8) - source_code = to_text_string(win.editor.toPlainText()) - res = check_with_pyflakes(source_code, fname)#+\ -# check_with_pep8(source_code, fname) - win.editor.process_code_analysis(res) - - sys.exit(app.exec_()) - -if __name__ == '__main__': - if len(sys.argv) > 1: - fname = sys.argv[1] - else: - fname = __file__ -# fname = r"d:\Python\scintilla\src\LexCPP.cxx" -# fname = r"C:\Python26\Lib\pdb.py" -# fname = r"C:\Python26\Lib\ssl.py" -# fname = r"D:\Python\testouille.py" -# fname = r"C:\Python26\Lib\pydoc.py" - test(fname) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/__init__.py spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/__init__.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/__init__.py 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.widgets.sourcecode -============================ - -Source code related widgets (code editor, console) based exclusively on Qt -""" diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/syntaxhighlighters.py spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/syntaxhighlighters.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/syntaxhighlighters.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/syntaxhighlighters.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,917 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Editor widget syntax highlighters based on QtGui.QSyntaxHighlighter -(Python syntax highlighting rules are inspired from idlelib) -""" - -from __future__ import print_function - -import re -import keyword - -from spyderlib.qt.QtGui import (QColor, QApplication, QFont, - QSyntaxHighlighter, QCursor, QTextCharFormat) -from spyderlib.qt.QtCore import Qt - -# Local imports -from spyderlib import dependencies -from spyderlib.baseconfig import _ -from spyderlib.config import CONF -from spyderlib.py3compat import builtins, is_text_string, to_text_string -from spyderlib.utils.sourcecode import CELL_LANGUAGES - - -PYGMENTS_REQVER = '>=1.6' -dependencies.add("pygments", _("Syntax highlighting for Matlab, Julia and other " - "file types"), - required_version=PYGMENTS_REQVER) - - -#============================================================================== -# Constants -#============================================================================== -COLOR_SCHEME_KEYS = ("background", "currentline", "currentcell", "occurence", - "ctrlclick", "sideareas", "matched_p", "unmatched_p", - "normal", "keyword", "builtin", "definition", - "comment", "string", "number", "instance") -COLOR_SCHEME_NAMES = CONF.get('color_schemes', 'names') - - -#============================================================================== -# Auxiliary functions -#============================================================================== -def get_color_scheme(name): - """Get a color scheme from config using its name""" - name = name.lower() - scheme = {} - for key in COLOR_SCHEME_KEYS: - try: - scheme[key] = CONF.get('color_schemes', name+'/'+key) - except: - scheme[key] = CONF.get('color_schemes', 'spyder/'+key) - return scheme - - -#============================================================================== -# Syntax highlighting color schemes -#============================================================================== -class BaseSH(QSyntaxHighlighter): - """Base Syntax Highlighter Class""" - # Syntax highlighting rules: - PROG = None - # Syntax highlighting states (from one text block to another): - NORMAL = 0 - def __init__(self, parent, font=None, color_scheme='Spyder'): - QSyntaxHighlighter.__init__(self, parent) - - self.outlineexplorer_data = {} - - self.font = font - self._check_color_scheme(color_scheme) - if is_text_string(color_scheme): - self.color_scheme = get_color_scheme(color_scheme) - else: - self.color_scheme = color_scheme - - self.background_color = None - self.currentline_color = None - self.currentcell_color = None - self.occurence_color = None - self.ctrlclick_color = None - self.sideareas_color = None - self.matched_p_color = None - self.unmatched_p_color = None - - self.formats = None - self.setup_formats(font) - - self.cell_separators = None - - def get_background_color(self): - return QColor(self.background_color) - - def get_foreground_color(self): - """Return foreground ('normal' text) color""" - return self.formats["normal"].foreground().color() - - def get_currentline_color(self): - return QColor(self.currentline_color) - - def get_currentcell_color(self): - return QColor(self.currentcell_color) - - def get_occurence_color(self): - return QColor(self.occurence_color) - - def get_ctrlclick_color(self): - return QColor(self.ctrlclick_color) - - def get_sideareas_color(self): - return QColor(self.sideareas_color) - - def get_matched_p_color(self): - return QColor(self.matched_p_color) - - def get_unmatched_p_color(self): - return QColor(self.unmatched_p_color) - - def get_comment_color(self): - """ Return color for the comments """ - return self.formats['comment'].foreground().color() - - def get_color_name(self, fmt): - """Return color name assigned to a given format""" - return self.formats[fmt].foreground().color().name() - - def setup_formats(self, font=None): - base_format = QTextCharFormat() - if font is not None: - self.font = font - if self.font is not None: - base_format.setFont(self.font) - self.formats = {} - colors = self.color_scheme.copy() - self.background_color = colors.pop("background") - self.currentline_color = colors.pop("currentline") - self.currentcell_color = colors.pop("currentcell") - self.occurence_color = colors.pop("occurence") - self.ctrlclick_color = colors.pop("ctrlclick") - self.sideareas_color = colors.pop("sideareas") - self.matched_p_color = colors.pop("matched_p") - self.unmatched_p_color = colors.pop("unmatched_p") - for name, (color, bold, italic) in list(colors.items()): - format = QTextCharFormat(base_format) - format.setForeground(QColor(color)) - format.setBackground(QColor(self.background_color)) - if bold: - format.setFontWeight(QFont.Bold) - format.setFontItalic(italic) - self.formats[name] = format - - def _check_color_scheme(self, color_scheme): - if is_text_string(color_scheme): - assert color_scheme in COLOR_SCHEME_NAMES - else: - assert all([key in color_scheme for key in COLOR_SCHEME_KEYS]) - - def set_color_scheme(self, color_scheme): - self._check_color_scheme(color_scheme) - if is_text_string(color_scheme): - self.color_scheme = get_color_scheme(color_scheme) - else: - self.color_scheme = color_scheme - self.setup_formats() - self.rehighlight() - - def highlightBlock(self, text): - raise NotImplementedError - - def get_outlineexplorer_data(self): - return self.outlineexplorer_data - - def rehighlight(self): - self.outlineexplorer_data = {} - QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - QSyntaxHighlighter.rehighlight(self) - QApplication.restoreOverrideCursor() - - -class TextSH(BaseSH): - """Simple Text Syntax Highlighter Class (do nothing)""" - def highlightBlock(self, text): - pass - - -class GenericSH(BaseSH): - """Generic Syntax Highlighter""" - # Syntax highlighting rules: - PROG = None # to be redefined in child classes - def highlightBlock(self, text): - text = to_text_string(text) - self.setFormat(0, len(text), self.formats["normal"]) - - match = self.PROG.search(text) - index = 0 - while match: - for key, value in list(match.groupdict().items()): - if value: - start, end = match.span(key) - index += end-start - self.setFormat(start, end-start, self.formats[key]) - - match = self.PROG.search(text, match.end()) - - -#============================================================================== -# Python syntax highlighter -#============================================================================== -def any(name, alternates): - "Return a named group pattern matching list of alternates." - return "(?P<%s>" % name + "|".join(alternates) + ")" - -def make_python_patterns(additional_keywords=[], additional_builtins=[]): - "Strongly inspired from idlelib.ColorDelegator.make_pat" - kw = r"\b" + any("keyword", keyword.kwlist+additional_keywords) + r"\b" - builtinlist = [str(name) for name in dir(builtins) - if not name.startswith('_')]+additional_builtins - builtin = r"([^.'\"\\#]\b|^)" + any("builtin", builtinlist) + r"\b" - comment = any("comment", [r"#[^\n]*"]) - instance = any("instance", [r"\bself\b"]) - number = any("number", - [r"\b[+-]?[0-9]+[lLjJ]?\b", - r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", - r"\b[+-]?0[oO][0-7]+[lL]?\b", - r"\b[+-]?0[bB][01]+[lL]?\b", - r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?[jJ]?\b"]) - sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' - uf_sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*(\\)$(?!')$" - uf_dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*(\\)$(?!")$' - sq3string = r"(\b[rRuU])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" - dq3string = r'(\b[rRuU])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' - uf_sq3string = r"(\b[rRuU])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(\\)?(?!''')$" - uf_dq3string = r'(\b[rRuU])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(\\)?(?!""")$' - string = any("string", [sq3string, dq3string, sqstring, dqstring]) - ufstring1 = any("uf_sqstring", [uf_sqstring]) - ufstring2 = any("uf_dqstring", [uf_dqstring]) - ufstring3 = any("uf_sq3string", [uf_sq3string]) - ufstring4 = any("uf_dq3string", [uf_dq3string]) - return "|".join([instance, kw, builtin, comment, - ufstring1, ufstring2, ufstring3, ufstring4, string, - number, any("SYNC", [r"\n"])]) - -class OutlineExplorerData(object): - CLASS, FUNCTION, STATEMENT, COMMENT, CELL = list(range(5)) - def __init__(self): - self.text = None - self.fold_level = None - self.def_type = None - self.def_name = None - - def is_not_class_nor_function(self): - return self.def_type not in (self.CLASS, self.FUNCTION) - - def is_comment(self): - return self.def_type in (self.COMMENT, self.CELL) - - def get_class_name(self): - if self.def_type == self.CLASS: - return self.def_name - - def get_function_name(self): - if self.def_type == self.FUNCTION: - return self.def_name - -class PythonSH(BaseSH): - """Python Syntax Highlighter""" - # Syntax highlighting rules: - PROG = re.compile(make_python_patterns(), re.S) - IDPROG = re.compile(r"\s+(\w+)", re.S) - ASPROG = re.compile(r".*?\b(as)\b") - # Syntax highlighting states (from one text block to another): - (NORMAL, INSIDE_SQ3STRING, INSIDE_DQ3STRING, - INSIDE_SQSTRING, INSIDE_DQSTRING) = list(range(5)) - DEF_TYPES = {"def": OutlineExplorerData.FUNCTION, - "class": OutlineExplorerData.CLASS} - # Comments suitable for Outline Explorer - OECOMMENT = re.compile('^(# ?--[-]+|##[#]+ )[ -]*[^- ]+') - - def __init__(self, parent, font=None, color_scheme='Spyder'): - BaseSH.__init__(self, parent, font, color_scheme) - self.import_statements = {} - self.found_cell_separators = False - self.cell_separators = CELL_LANGUAGES['Python'] - - def highlightBlock(self, text): - text = to_text_string(text) - prev_state = self.previousBlockState() - if prev_state == self.INSIDE_DQ3STRING: - offset = -4 - text = r'""" '+text - elif prev_state == self.INSIDE_SQ3STRING: - offset = -4 - text = r"''' "+text - elif prev_state == self.INSIDE_DQSTRING: - offset = -2 - text = r'" '+text - elif prev_state == self.INSIDE_SQSTRING: - offset = -2 - text = r"' "+text - else: - offset = 0 - prev_state = self.NORMAL - - oedata = None - import_stmt = None - - self.setFormat(0, len(text), self.formats["normal"]) - - state = self.NORMAL - match = self.PROG.search(text) - while match: - for key, value in list(match.groupdict().items()): - if value: - start, end = match.span(key) - start = max([0, start+offset]) - end = max([0, end+offset]) - if key == "uf_sq3string": - self.setFormat(start, end-start, - self.formats["string"]) - state = self.INSIDE_SQ3STRING - elif key == "uf_dq3string": - self.setFormat(start, end-start, - self.formats["string"]) - state = self.INSIDE_DQ3STRING - elif key == "uf_sqstring": - self.setFormat(start, end-start, - self.formats["string"]) - state = self.INSIDE_SQSTRING - elif key == "uf_dqstring": - self.setFormat(start, end-start, - self.formats["string"]) - state = self.INSIDE_DQSTRING - else: - self.setFormat(start, end-start, self.formats[key]) - if key == "comment": - if text.lstrip().startswith(self.cell_separators): - self.found_cell_separators = True - oedata = OutlineExplorerData() - oedata.text = to_text_string(text).strip() - oedata.fold_level = start - oedata.def_type = OutlineExplorerData.CELL - oedata.def_name = text.strip() - elif self.OECOMMENT.match(text.lstrip()): - oedata = OutlineExplorerData() - oedata.text = to_text_string(text).strip() - oedata.fold_level = start - oedata.def_type = OutlineExplorerData.COMMENT - oedata.def_name = text.strip() - elif key == "keyword": - if value in ("def", "class"): - match1 = self.IDPROG.match(text, end) - if match1: - start1, end1 = match1.span(1) - self.setFormat(start1, end1-start1, - self.formats["definition"]) - oedata = OutlineExplorerData() - oedata.text = to_text_string(text) - oedata.fold_level = start - oedata.def_type = self.DEF_TYPES[ - to_text_string(value)] - oedata.def_name = text[start1:end1] - elif value in ("elif", "else", "except", "finally", - "for", "if", "try", "while", - "with"): - if text.lstrip().startswith(value): - oedata = OutlineExplorerData() - oedata.text = to_text_string(text).strip() - oedata.fold_level = start - oedata.def_type = \ - OutlineExplorerData.STATEMENT - oedata.def_name = text.strip() - elif value == "import": - import_stmt = text.strip() - # color all the "as" words on same line, except - # if in a comment; cheap approximation to the - # truth - if '#' in text: - endpos = text.index('#') - else: - endpos = len(text) - while True: - match1 = self.ASPROG.match(text, end, - endpos) - if not match1: - break - start, end = match1.span(1) - self.setFormat(start, end-start, - self.formats["keyword"]) - - match = self.PROG.search(text, match.end()) - - self.setCurrentBlockState(state) - - if oedata is not None: - block_nb = self.currentBlock().blockNumber() - self.outlineexplorer_data[block_nb] = oedata - self.outlineexplorer_data['found_cell_separators'] = self.found_cell_separators - if import_stmt is not None: - block_nb = self.currentBlock().blockNumber() - self.import_statements[block_nb] = import_stmt - - def get_import_statements(self): - return list(self.import_statements.values()) - - def rehighlight(self): - self.import_statements = {} - self.found_cell_separators = False - BaseSH.rehighlight(self) - - -#============================================================================== -# Cython syntax highlighter -#============================================================================== -C_TYPES = 'bool char double enum float int long mutable short signed struct unsigned void' - -class CythonSH(PythonSH): - """Cython Syntax Highlighter""" - ADDITIONAL_KEYWORDS = ["cdef", "ctypedef", "cpdef", "inline", "cimport", - "DEF"] - ADDITIONAL_BUILTINS = C_TYPES.split() - PROG = re.compile(make_python_patterns(ADDITIONAL_KEYWORDS, - ADDITIONAL_BUILTINS), re.S) - IDPROG = re.compile(r"\s+([\w\.]+)", re.S) - - -#============================================================================== -# Enaml syntax highlighter -#============================================================================== -class EnamlSH(PythonSH): - """Enaml Syntax Highlighter""" - ADDITIONAL_KEYWORDS = ["enamldef", "template", "attr", "event", "const", "alias"] - ADDITIONAL_BUILTINS = [] - PROG = re.compile(make_python_patterns(ADDITIONAL_KEYWORDS, - ADDITIONAL_BUILTINS), re.S) - IDPROG = re.compile(r"\s+([\w\.]+)", re.S) - - -#============================================================================== -# C/C++ syntax highlighter -#============================================================================== -C_KEYWORDS1 = 'and and_eq bitand bitor break case catch const const_cast continue default delete do dynamic_cast else explicit export extern for friend goto if inline namespace new not not_eq operator or or_eq private protected public register reinterpret_cast return sizeof static static_cast switch template throw try typedef typeid typename union using virtual while xor xor_eq' -C_KEYWORDS2 = 'a addindex addtogroup anchor arg attention author b brief bug c class code date def defgroup deprecated dontinclude e em endcode endhtmlonly ifdef endif endlatexonly endlink endverbatim enum example exception f$ file fn hideinitializer htmlinclude htmlonly if image include ingroup internal invariant interface latexonly li line link mainpage name namespace nosubgrouping note overload p page par param post pre ref relates remarks return retval sa section see showinitializer since skip skipline subsection test throw todo typedef union until var verbatim verbinclude version warning weakgroup' -C_KEYWORDS3 = 'asm auto class compl false true volatile wchar_t' - -def make_generic_c_patterns(keywords, builtins, - instance=None, define=None, comment=None): - "Strongly inspired from idlelib.ColorDelegator.make_pat" - kw = r"\b" + any("keyword", keywords.split()) + r"\b" - builtin = r"\b" + any("builtin", builtins.split()+C_TYPES.split()) + r"\b" - if comment is None: - comment = any("comment", [r"//[^\n]*", r"\/\*(.*?)\*\/"]) - comment_start = any("comment_start", [r"\/\*"]) - comment_end = any("comment_end", [r"\*\/"]) - if instance is None: - instance = any("instance", [r"\bthis\b"]) - number = any("number", - [r"\b[+-]?[0-9]+[lL]?\b", - r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", - r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) - sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' - string = any("string", [sqstring, dqstring]) - if define is None: - define = any("define", [r"#[^\n]*"]) - return "|".join([instance, kw, comment, string, number, - comment_start, comment_end, builtin, - define, any("SYNC", [r"\n"])]) - -def make_cpp_patterns(): - return make_generic_c_patterns(C_KEYWORDS1+' '+C_KEYWORDS2, C_KEYWORDS3) - -class CppSH(BaseSH): - """C/C++ Syntax Highlighter""" - # Syntax highlighting rules: - PROG = re.compile(make_cpp_patterns(), re.S) - # Syntax highlighting states (from one text block to another): - NORMAL = 0 - INSIDE_COMMENT = 1 - def __init__(self, parent, font=None, color_scheme=None): - BaseSH.__init__(self, parent, font, color_scheme) - - def highlightBlock(self, text): - text = to_text_string(text) - inside_comment = self.previousBlockState() == self.INSIDE_COMMENT - self.setFormat(0, len(text), - self.formats["comment" if inside_comment else "normal"]) - - match = self.PROG.search(text) - index = 0 - while match: - for key, value in list(match.groupdict().items()): - if value: - start, end = match.span(key) - index += end-start - if key == "comment_start": - inside_comment = True - self.setFormat(start, len(text)-start, - self.formats["comment"]) - elif key == "comment_end": - inside_comment = False - self.setFormat(start, end-start, - self.formats["comment"]) - elif inside_comment: - self.setFormat(start, end-start, - self.formats["comment"]) - elif key == "define": - self.setFormat(start, end-start, - self.formats["number"]) - else: - self.setFormat(start, end-start, self.formats[key]) - - match = self.PROG.search(text, match.end()) - - last_state = self.INSIDE_COMMENT if inside_comment else self.NORMAL - self.setCurrentBlockState(last_state) - - -def make_opencl_patterns(): - # Keywords: - kwstr1 = 'cl_char cl_uchar cl_short cl_ushort cl_int cl_uint cl_long cl_ulong cl_half cl_float cl_double cl_platform_id cl_device_id cl_context cl_command_queue cl_mem cl_program cl_kernel cl_event cl_sampler cl_bool cl_bitfield cl_device_type cl_platform_info cl_device_info cl_device_address_info cl_device_fp_config cl_device_mem_cache_type cl_device_local_mem_type cl_device_exec_capabilities cl_command_queue_properties cl_context_properties cl_context_info cl_command_queue_info cl_channel_order cl_channel_type cl_mem_flags cl_mem_object_type cl_mem_info cl_image_info cl_addressing_mode cl_filter_mode cl_sampler_info cl_map_flags cl_program_info cl_program_build_info cl_build_status cl_kernel_info cl_kernel_work_group_info cl_event_info cl_command_type cl_profiling_info cl_image_format' - # Constants: - kwstr2 = 'CL_FALSE, CL_TRUE, CL_PLATFORM_PROFILE, CL_PLATFORM_VERSION, CL_PLATFORM_NAME, CL_PLATFORM_VENDOR, CL_PLATFORM_EXTENSIONS, CL_DEVICE_TYPE_DEFAULT , CL_DEVICE_TYPE_CPU, CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ACCELERATOR, CL_DEVICE_TYPE_ALL, CL_DEVICE_TYPE, CL_DEVICE_VENDOR_ID, CL_DEVICE_MAX_COMPUTE_UNITS, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, CL_DEVICE_MAX_WORK_GROUP_SIZE, CL_DEVICE_MAX_WORK_ITEM_SIZES, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, CL_DEVICE_MAX_CLOCK_FREQUENCY, CL_DEVICE_ADDRESS_BITS, CL_DEVICE_MAX_READ_IMAGE_ARGS, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, CL_DEVICE_MAX_MEM_ALLOC_SIZE, CL_DEVICE_IMAGE2D_MAX_WIDTH, CL_DEVICE_IMAGE2D_MAX_HEIGHT, CL_DEVICE_IMAGE3D_MAX_WIDTH, CL_DEVICE_IMAGE3D_MAX_HEIGHT, CL_DEVICE_IMAGE3D_MAX_DEPTH, CL_DEVICE_IMAGE_SUPPORT, CL_DEVICE_MAX_PARAMETER_SIZE, CL_DEVICE_MAX_SAMPLERS, CL_DEVICE_MEM_BASE_ADDR_ALIGN, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, CL_DEVICE_SINGLE_FP_CONFIG, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, CL_DEVICE_GLOBAL_MEM_SIZE, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, CL_DEVICE_MAX_CONSTANT_ARGS, CL_DEVICE_LOCAL_MEM_TYPE, CL_DEVICE_LOCAL_MEM_SIZE, CL_DEVICE_ERROR_CORRECTION_SUPPORT, CL_DEVICE_PROFILING_TIMER_RESOLUTION, CL_DEVICE_ENDIAN_LITTLE, CL_DEVICE_AVAILABLE, CL_DEVICE_COMPILER_AVAILABLE, CL_DEVICE_EXECUTION_CAPABILITIES, CL_DEVICE_QUEUE_PROPERTIES, CL_DEVICE_NAME, CL_DEVICE_VENDOR, CL_DRIVER_VERSION, CL_DEVICE_PROFILE, CL_DEVICE_VERSION, CL_DEVICE_EXTENSIONS, CL_DEVICE_PLATFORM, CL_FP_DENORM, CL_FP_INF_NAN, CL_FP_ROUND_TO_NEAREST, CL_FP_ROUND_TO_ZERO, CL_FP_ROUND_TO_INF, CL_FP_FMA, CL_NONE, CL_READ_ONLY_CACHE, CL_READ_WRITE_CACHE, CL_LOCAL, CL_GLOBAL, CL_EXEC_KERNEL, CL_EXEC_NATIVE_KERNEL, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, CL_QUEUE_PROFILING_ENABLE, CL_CONTEXT_REFERENCE_COUNT, CL_CONTEXT_DEVICES, CL_CONTEXT_PROPERTIES, CL_CONTEXT_PLATFORM, CL_QUEUE_CONTEXT, CL_QUEUE_DEVICE, CL_QUEUE_REFERENCE_COUNT, CL_QUEUE_PROPERTIES, CL_MEM_READ_WRITE, CL_MEM_WRITE_ONLY, CL_MEM_READ_ONLY, CL_MEM_USE_HOST_PTR, CL_MEM_ALLOC_HOST_PTR, CL_MEM_COPY_HOST_PTR, CL_R, CL_A, CL_RG, CL_RA, CL_RGB, CL_RGBA, CL_BGRA, CL_ARGB, CL_INTENSITY, CL_LUMINANCE, CL_SNORM_INT8, CL_SNORM_INT16, CL_UNORM_INT8, CL_UNORM_INT16, CL_UNORM_SHORT_565, CL_UNORM_SHORT_555, CL_UNORM_INT_101010, CL_SIGNED_INT8, CL_SIGNED_INT16, CL_SIGNED_INT32, CL_UNSIGNED_INT8, CL_UNSIGNED_INT16, CL_UNSIGNED_INT32, CL_HALF_FLOAT, CL_FLOAT, CL_MEM_OBJECT_BUFFER, CL_MEM_OBJECT_IMAGE2D, CL_MEM_OBJECT_IMAGE3D, CL_MEM_TYPE, CL_MEM_FLAGS, CL_MEM_SIZECL_MEM_HOST_PTR, CL_MEM_HOST_PTR, CL_MEM_MAP_COUNT, CL_MEM_REFERENCE_COUNT, CL_MEM_CONTEXT, CL_IMAGE_FORMAT, CL_IMAGE_ELEMENT_SIZE, CL_IMAGE_ROW_PITCH, CL_IMAGE_SLICE_PITCH, CL_IMAGE_WIDTH, CL_IMAGE_HEIGHT, CL_IMAGE_DEPTH, CL_ADDRESS_NONE, CL_ADDRESS_CLAMP_TO_EDGE, CL_ADDRESS_CLAMP, CL_ADDRESS_REPEAT, CL_FILTER_NEAREST, CL_FILTER_LINEAR, CL_SAMPLER_REFERENCE_COUNT, CL_SAMPLER_CONTEXT, CL_SAMPLER_NORMALIZED_COORDS, CL_SAMPLER_ADDRESSING_MODE, CL_SAMPLER_FILTER_MODE, CL_MAP_READ, CL_MAP_WRITE, CL_PROGRAM_REFERENCE_COUNT, CL_PROGRAM_CONTEXT, CL_PROGRAM_NUM_DEVICES, CL_PROGRAM_DEVICES, CL_PROGRAM_SOURCE, CL_PROGRAM_BINARY_SIZES, CL_PROGRAM_BINARIES, CL_PROGRAM_BUILD_STATUS, CL_PROGRAM_BUILD_OPTIONS, CL_PROGRAM_BUILD_LOG, CL_BUILD_SUCCESS, CL_BUILD_NONE, CL_BUILD_ERROR, CL_BUILD_IN_PROGRESS, CL_KERNEL_FUNCTION_NAME, CL_KERNEL_NUM_ARGS, CL_KERNEL_REFERENCE_COUNT, CL_KERNEL_CONTEXT, CL_KERNEL_PROGRAM, CL_KERNEL_WORK_GROUP_SIZE, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, CL_KERNEL_LOCAL_MEM_SIZE, CL_EVENT_COMMAND_QUEUE, CL_EVENT_COMMAND_TYPE, CL_EVENT_REFERENCE_COUNT, CL_EVENT_COMMAND_EXECUTION_STATUS, CL_COMMAND_NDRANGE_KERNEL, CL_COMMAND_TASK, CL_COMMAND_NATIVE_KERNEL, CL_COMMAND_READ_BUFFER, CL_COMMAND_WRITE_BUFFER, CL_COMMAND_COPY_BUFFER, CL_COMMAND_READ_IMAGE, CL_COMMAND_WRITE_IMAGE, CL_COMMAND_COPY_IMAGE, CL_COMMAND_COPY_IMAGE_TO_BUFFER, CL_COMMAND_COPY_BUFFER_TO_IMAGE, CL_COMMAND_MAP_BUFFER, CL_COMMAND_MAP_IMAGE, CL_COMMAND_UNMAP_MEM_OBJECT, CL_COMMAND_MARKER, CL_COMMAND_ACQUIRE_GL_OBJECTS, CL_COMMAND_RELEASE_GL_OBJECTS, command execution status, CL_COMPLETE, CL_RUNNING, CL_SUBMITTED, CL_QUEUED, CL_PROFILING_COMMAND_QUEUED, CL_PROFILING_COMMAND_SUBMIT, CL_PROFILING_COMMAND_START, CL_PROFILING_COMMAND_END, CL_CHAR_BIT, CL_SCHAR_MAX, CL_SCHAR_MIN, CL_CHAR_MAX, CL_CHAR_MIN, CL_UCHAR_MAX, CL_SHRT_MAX, CL_SHRT_MIN, CL_USHRT_MAX, CL_INT_MAX, CL_INT_MIN, CL_UINT_MAX, CL_LONG_MAX, CL_LONG_MIN, CL_ULONG_MAX, CL_FLT_DIG, CL_FLT_MANT_DIG, CL_FLT_MAX_10_EXP, CL_FLT_MAX_EXP, CL_FLT_MIN_10_EXP, CL_FLT_MIN_EXP, CL_FLT_RADIX, CL_FLT_MAX, CL_FLT_MIN, CL_FLT_EPSILON, CL_DBL_DIG, CL_DBL_MANT_DIG, CL_DBL_MAX_10_EXP, CL_DBL_MAX_EXP, CL_DBL_MIN_10_EXP, CL_DBL_MIN_EXP, CL_DBL_RADIX, CL_DBL_MAX, CL_DBL_MIN, CL_DBL_EPSILON, CL_SUCCESS, CL_DEVICE_NOT_FOUND, CL_DEVICE_NOT_AVAILABLE, CL_COMPILER_NOT_AVAILABLE, CL_MEM_OBJECT_ALLOCATION_FAILURE, CL_OUT_OF_RESOURCES, CL_OUT_OF_HOST_MEMORY, CL_PROFILING_INFO_NOT_AVAILABLE, CL_MEM_COPY_OVERLAP, CL_IMAGE_FORMAT_MISMATCH, CL_IMAGE_FORMAT_NOT_SUPPORTED, CL_BUILD_PROGRAM_FAILURE, CL_MAP_FAILURE, CL_INVALID_VALUE, CL_INVALID_DEVICE_TYPE, CL_INVALID_PLATFORM, CL_INVALID_DEVICE, CL_INVALID_CONTEXT, CL_INVALID_QUEUE_PROPERTIES, CL_INVALID_COMMAND_QUEUE, CL_INVALID_HOST_PTR, CL_INVALID_MEM_OBJECT, CL_INVALID_IMAGE_FORMAT_DESCRIPTOR, CL_INVALID_IMAGE_SIZE, CL_INVALID_SAMPLER, CL_INVALID_BINARY, CL_INVALID_BUILD_OPTIONS, CL_INVALID_PROGRAM, CL_INVALID_PROGRAM_EXECUTABLE, CL_INVALID_KERNEL_NAME, CL_INVALID_KERNEL_DEFINITION, CL_INVALID_KERNEL, CL_INVALID_ARG_INDEX, CL_INVALID_ARG_VALUE, CL_INVALID_ARG_SIZE, CL_INVALID_KERNEL_ARGS, CL_INVALID_WORK_DIMENSION, CL_INVALID_WORK_GROUP_SIZE, CL_INVALID_WORK_ITEM_SIZE, CL_INVALID_GLOBAL_OFFSET, CL_INVALID_EVENT_WAIT_LIST, CL_INVALID_EVENT, CL_INVALID_OPERATION, CL_INVALID_GL_OBJECT, CL_INVALID_BUFFER_SIZE, CL_INVALID_MIP_LEVEL, CL_INVALID_GLOBAL_WORK_SIZE' - # Functions: - builtins = 'clGetPlatformIDs, clGetPlatformInfo, clGetDeviceIDs, clGetDeviceInfo, clCreateContext, clCreateContextFromType, clReleaseContext, clGetContextInfo, clCreateCommandQueue, clRetainCommandQueue, clReleaseCommandQueue, clGetCommandQueueInfo, clSetCommandQueueProperty, clCreateBuffer, clCreateImage2D, clCreateImage3D, clRetainMemObject, clReleaseMemObject, clGetSupportedImageFormats, clGetMemObjectInfo, clGetImageInfo, clCreateSampler, clRetainSampler, clReleaseSampler, clGetSamplerInfo, clCreateProgramWithSource, clCreateProgramWithBinary, clRetainProgram, clReleaseProgram, clBuildProgram, clUnloadCompiler, clGetProgramInfo, clGetProgramBuildInfo, clCreateKernel, clCreateKernelsInProgram, clRetainKernel, clReleaseKernel, clSetKernelArg, clGetKernelInfo, clGetKernelWorkGroupInfo, clWaitForEvents, clGetEventInfo, clRetainEvent, clReleaseEvent, clGetEventProfilingInfo, clFlush, clFinish, clEnqueueReadBuffer, clEnqueueWriteBuffer, clEnqueueCopyBuffer, clEnqueueReadImage, clEnqueueWriteImage, clEnqueueCopyImage, clEnqueueCopyImageToBuffer, clEnqueueCopyBufferToImage, clEnqueueMapBuffer, clEnqueueMapImage, clEnqueueUnmapMemObject, clEnqueueNDRangeKernel, clEnqueueTask, clEnqueueNativeKernel, clEnqueueMarker, clEnqueueWaitForEvents, clEnqueueBarrier' - # Qualifiers: - qualifiers = '__global __local __constant __private __kernel' - keyword_list = C_KEYWORDS1+' '+C_KEYWORDS2+' '+kwstr1+' '+kwstr2 - builtin_list = C_KEYWORDS3+' '+builtins+' '+qualifiers - return make_generic_c_patterns(keyword_list, builtin_list) - -class OpenCLSH(CppSH): - """OpenCL Syntax Highlighter""" - PROG = re.compile(make_opencl_patterns(), re.S) - - -#============================================================================== -# Fortran Syntax Highlighter -#============================================================================== - -def make_fortran_patterns(): - "Strongly inspired from idlelib.ColorDelegator.make_pat" - kwstr = 'access action advance allocatable allocate apostrophe assign assignment associate asynchronous backspace bind blank blockdata call case character class close common complex contains continue cycle data deallocate decimal delim default dimension direct do dowhile double doubleprecision else elseif elsewhere encoding end endassociate endblockdata enddo endfile endforall endfunction endif endinterface endmodule endprogram endselect endsubroutine endtype endwhere entry eor equivalence err errmsg exist exit external file flush fmt forall form format formatted function go goto id if implicit in include inout integer inquire intent interface intrinsic iomsg iolength iostat kind len logical module name named namelist nextrec nml none nullify number only open opened operator optional out pad parameter pass pause pending pointer pos position precision print private program protected public quote read readwrite real rec recl recursive result return rewind save select selectcase selecttype sequential sign size stat status stop stream subroutine target then to type unformatted unit use value volatile wait where while write' - bistr1 = 'abs achar acos acosd adjustl adjustr aimag aimax0 aimin0 aint ajmax0 ajmin0 akmax0 akmin0 all allocated alog alog10 amax0 amax1 amin0 amin1 amod anint any asin asind associated atan atan2 atan2d atand bitest bitl bitlr bitrl bjtest bit_size bktest break btest cabs ccos cdabs cdcos cdexp cdlog cdsin cdsqrt ceiling cexp char clog cmplx conjg cos cosd cosh count cpu_time cshift csin csqrt dabs dacos dacosd dasin dasind datan datan2 datan2d datand date date_and_time dble dcmplx dconjg dcos dcosd dcosh dcotan ddim dexp dfloat dflotk dfloti dflotj digits dim dimag dint dlog dlog10 dmax1 dmin1 dmod dnint dot_product dprod dreal dsign dsin dsind dsinh dsqrt dtan dtand dtanh eoshift epsilon errsns exp exponent float floati floatj floatk floor fraction free huge iabs iachar iand ibclr ibits ibset ichar idate idim idint idnint ieor ifix iiabs iiand iibclr iibits iibset iidim iidint iidnnt iieor iifix iint iior iiqint iiqnnt iishft iishftc iisign ilen imax0 imax1 imin0 imin1 imod index inint inot int int1 int2 int4 int8 iqint iqnint ior ishft ishftc isign isnan izext jiand jibclr jibits jibset jidim jidint jidnnt jieor jifix jint jior jiqint jiqnnt jishft jishftc jisign jmax0 jmax1 jmin0 jmin1 jmod jnint jnot jzext kiabs kiand kibclr kibits kibset kidim kidint kidnnt kieor kifix kind kint kior kishft kishftc kisign kmax0 kmax1 kmin0 kmin1 kmod knint knot kzext lbound leadz len len_trim lenlge lge lgt lle llt log log10 logical lshift malloc matmul max max0 max1 maxexponent maxloc maxval merge min min0 min1 minexponent minloc minval mod modulo mvbits nearest nint not nworkers number_of_processors pack popcnt poppar precision present product radix random random_number random_seed range real repeat reshape rrspacing rshift scale scan secnds selected_int_kind selected_real_kind set_exponent shape sign sin sind sinh size sizeof sngl snglq spacing spread sqrt sum system_clock tan tand tanh tiny transfer transpose trim ubound unpack verify' - bistr2 = 'cdabs cdcos cdexp cdlog cdsin cdsqrt cotan cotand dcmplx dconjg dcotan dcotand decode dimag dll_export dll_import doublecomplex dreal dvchk encode find flen flush getarg getcharqq getcl getdat getenv gettim hfix ibchng identifier imag int1 int2 int4 intc intrup invalop iostat_msg isha ishc ishl jfix lacfar locking locnear map nargs nbreak ndperr ndpexc offset ovefl peekcharqq precfill prompt qabs qacos qacosd qasin qasind qatan qatand qatan2 qcmplx qconjg qcos qcosd qcosh qdim qexp qext qextd qfloat qimag qlog qlog10 qmax1 qmin1 qmod qreal qsign qsin qsind qsinh qsqrt qtan qtand qtanh ran rand randu rewrite segment setdat settim system timer undfl unlock union val virtual volatile zabs zcos zexp zlog zsin zsqrt' - kw = r"\b" + any("keyword", kwstr.split()) + r"\b" - builtin = r"\b" + any("builtin", bistr1.split()+bistr2.split()) + r"\b" - comment = any("comment", [r"\![^\n]*"]) - number = any("number", - [r"\b[+-]?[0-9]+[lL]?\b", - r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", - r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) - sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' - string = any("string", [sqstring, dqstring]) - return "|".join([kw, comment, string, number, builtin, - any("SYNC", [r"\n"])]) - -class FortranSH(BaseSH): - """Fortran Syntax Highlighter""" - # Syntax highlighting rules: - PROG = re.compile(make_fortran_patterns(), re.S|re.I) - IDPROG = re.compile(r"\s+(\w+)", re.S) - # Syntax highlighting states (from one text block to another): - NORMAL = 0 - def __init__(self, parent, font=None, color_scheme=None): - BaseSH.__init__(self, parent, font, color_scheme) - - def highlightBlock(self, text): - text = to_text_string(text) - self.setFormat(0, len(text), self.formats["normal"]) - - match = self.PROG.search(text) - index = 0 - while match: - for key, value in list(match.groupdict().items()): - if value: - start, end = match.span(key) - index += end-start - self.setFormat(start, end-start, self.formats[key]) - if value.lower() in ("subroutine", "module", "function"): - match1 = self.IDPROG.match(text, end) - if match1: - start1, end1 = match1.span(1) - self.setFormat(start1, end1-start1, - self.formats["definition"]) - - match = self.PROG.search(text, match.end()) - -class Fortran77SH(FortranSH): - """Fortran 77 Syntax Highlighter""" - def highlightBlock(self, text): - text = to_text_string(text) - if text.startswith(("c", "C")): - self.setFormat(0, len(text), self.formats["comment"]) - else: - FortranSH.highlightBlock(self, text) - self.setFormat(0, 5, self.formats["comment"]) - self.setFormat(73, max([73, len(text)]), - self.formats["comment"]) - - -#============================================================================== -# IDL highlighter -# -# Contribution from Stuart Mumford (Littlemumford) - 02/02/2012 -# See Issue #850 -#============================================================================== -def make_idl_patterns(): - "Strongly inspired from idlelib.ColorDelegator.make_pat" - kwstr = 'begin of pro function endfor endif endwhile endrep endcase endswitch end if then else for do while repeat until break case switch common continue exit return goto help message print read retall stop' - bistr1 = 'a_correlate abs acos adapt_hist_equal alog alog10 amoeba arg_present arra_equal array_indices ascii_template asin assoc atan beseli beselj besel k besely beta bilinear bin_date binary_template dinfgen dinomial blk_con broyden bytarr byte bytscl c_correlate call_external call_function ceil chebyshev check_math chisqr_cvf chisqr_pdf choldc cholsol cindgen clust_wts cluster color_quan colormap_applicable comfit complex complexarr complexround compute_mesh_normals cond congrid conj convert_coord convol coord2to3 correlate cos cosh cramer create_struct crossp crvlength ct_luminance cti_test curvefit cv_coord cvttobm cw_animate cw_arcball cw_bgroup cw_clr_index cw_colorsel cw_defroi cw_field cw_filesel cw_form cw_fslider cw_light_editor cw_orient cw_palette_editor cw_pdmenu cw_rgbslider cw_tmpl cw_zoom dblarr dcindgen dcomplexarr defroi deriv derivsig determ diag_matrix dialog_message dialog_pickfile pialog_printersetup dialog_printjob dialog_read_image dialog_write_image digital_filter dilate dindgen dist double eigenql eigenvec elmhes eof erode erf erfc erfcx execute exp expand_path expint extrac extract_slice f_cvf f_pdf factorial fft file_basename file_dirname file_expand_path file_info file_same file_search file_test file_which filepath findfile findgen finite fix float floor fltarr format_axis_values fstat fulstr fv_test fx_root fz_roots gamma gauss_cvf gauss_pdf gauss2dfit gaussfit gaussint get_drive_list get_kbrd get_screen_size getenv grid_tps grid3 griddata gs_iter hanning hdf_browser hdf_read hilbert hist_2d hist_equal histogram hough hqr ibeta identity idl_validname idlitsys_createtool igamma imaginary indgen int_2d int_3d int_tabulated intarr interpol interpolate invert ioctl ishft julday keword_set krig2d kurtosis kw_test l64indgen label_date label_region ladfit laguerre la_cholmprove la_cholsol la_Determ la_eigenproblem la_eigenql la_eigenvec la_elmhes la_gm_linear_model la_hqr la_invert la_least_square_equality la_least_squares la_linear_equation la_lumprove la_lusol la_trimprove la_trisol leefit legendre linbcg lindgen linfit ll_arc_distance lmfit lmgr lngamma lnp_test locale_get logical_and logical_or logical_true lon64arr lonarr long long64 lsode lu_complex lumprove lusol m_correlate machar make_array map_2points map_image map_patch map_proj_forward map_proj_init map_proj_inverse matrix_multiply matrix_power max md_test mean meanabsdev median memory mesh_clip mesh_decimate mesh_issolid mesh_merge mesh_numtriangles mesh_smooth mesh_surfacearea mesh_validate mesh_volume min min_curve_surf moment morph_close morph_distance morph_gradient morph_histormiss morph_open morph_thin morph_tophat mpeg_open msg_cat_open n_elements n_params n_tags newton norm obj_class obj_isa obj_new obj_valid objarr p_correlate path_sep pcomp pnt_line polar_surface poly poly_2d poly_area poly_fit polyfillv ployshade primes product profile profiles project_vol ptr_new ptr_valid ptrarr qgrid3 qromb qromo qsimp query_bmp query_dicom query_image query_jpeg query_mrsid query_pict query_png query_ppm query_srf query_tiff query_wav r_correlate r_test radon randomn randomu ranks read_ascii read_binary read_bmp read_dicom read_image read_mrsid read_png read_spr read_sylk read_tiff read_wav read_xwd real_part rebin recall_commands recon3 reform region_grow regress replicate reverse rk4 roberts rot rotate round routine_info rs_test s_test savgol search2d search3d sfit shift shmdebug shmvar simplex sin sindgen sinh size skewness smooth sobel sort sph_scat spher_harm spl_init spl_interp spline spline_p sprsab sprsax sprsin sprstp sqrt standardize stddev strarr strcmp strcompress stregex string strjoin strlen strlowcase strmatch strmessage strmid strpos strsplit strtrim strupcase svdfit svsol swap_endian systime t_cvf t_pdf tag_names tan tanh temporary tetra_clip tetra_surface tetra_volume thin timegen tm_test total trace transpose tri_surf trigrid trisol ts_coef ts_diff ts_fcast ts_smooth tvrd uindgen unit uintarr ul64indgen ulindgen ulon64arr ulonarr ulong ulong64 uniq value_locate variance vert_t3d voigt voxel_proj warp_tri watershed where widget_actevix widget_base widget_button widget_combobox widget_draw widget_droplist widget_event widget_info widget_label widget_list widget_propertsheet widget_slider widget_tab widget_table widget_text widget_tree write_sylk wtn xfont xregistered xsq_test' - bistr2 = 'annotate arrow axis bar_plot blas_axpy box_cursor breakpoint byteorder caldata calendar call_method call_procedure catch cd cir_3pnt close color_convert compile_opt constrained_min contour copy_lun cpu create_view cursor cw_animate_getp cw_animate_load cw_animate_run cw_light_editor_get cw_light_editor_set cw_palette_editor_get cw_palette_editor_set define_key define_msgblk define_msgblk_from_file defsysv delvar device dfpmin dissolve dlm_load doc_librar draw_roi efont empty enable_sysrtn erase errplot expand file_chmod file_copy file_delete file_lines file_link file_mkdir file_move file_readlink flick flow3 flush forward_function free_lun funct gamma_ct get_lun grid_input h_eq_ct h_eq_int heap_free heap_gc hls hsv icontour iimage image_cont image_statistics internal_volume iplot isocontour isosurface isurface itcurrent itdelete itgetcurrent itregister itreset ivolume journal la_choldc la_ludc la_svd la_tridc la_triql la_trired linkimage loadct ludc make_dll map_continents map_grid map_proj_info map_set mesh_obj mk_html_help modifyct mpeg_close mpeg_put mpeg_save msg_cat_close msg_cat_compile multi obj_destroy on_error on_ioerror online_help openr openw openu oplot oploterr particle_trace path_cache plot plot_3dbox plot_field ploterr plots point_lun polar_contour polyfill polywarp popd powell printf printd ps_show_fonts psafm pseudo ptr_free pushd qhull rdpix readf read_interfile read_jpeg read_pict read_ppm read_srf read_wave read_x11_bitmap reads readu reduce_colors register_cursor replicate_inplace resolve_all resolve_routine restore save scale3 scale3d set_plot set_shading setenv setup_keys shade_surf shade_surf_irr shade_volume shmmap show3 showfont skip_lun slicer3 slide_image socket spawn sph_4pnt streamline stretch strput struct_assign struct_hide surface surfr svdc swap_enian_inplace t3d tek_color threed time_test2 triangulate triql trired truncate_lun tv tvcrs tvlct tvscl usersym vector_field vel velovect voronoi wait wdelete wf_draw widget_control widget_displaycontextmenu window write_bmp write_image write_jpeg write_nrif write_pict write_png write_ppm write_spr write_srf write_tiff write_wav write_wave writeu wset wshow xbm_edit xdisplayfile xdxf xinteranimate xloadct xmanager xmng_tmpl xmtool xobjview xobjview_rotate xobjview_write_image xpalette xpcolo xplot3d xroi xsurface xvaredit xvolume xyouts zoom zoom_24' - kw = r"\b" + any("keyword", kwstr.split()) + r"\b" - builtin = r"\b" + any("builtin", bistr1.split()+bistr2.split()) + r"\b" - comment = any("comment", [r"\;[^\n]*"]) - number = any("number", - [r"\b[+-]?[0-9]+[lL]?\b", - r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", - r"\b\.[0-9]d0|\.d0+[lL]?\b", - r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) - sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' - string = any("string", [sqstring, dqstring]) - return "|".join([kw, comment, string, number, builtin, - any("SYNC", [r"\n"])]) - -class IdlSH(GenericSH): - """IDL Syntax Highlighter""" - PROG = re.compile(make_idl_patterns(), re.S|re.I) - - -#============================================================================== -# Diff/Patch highlighter -#============================================================================== - -class DiffSH(BaseSH): - """Simple Diff/Patch Syntax Highlighter Class""" - def highlightBlock(self, text): - text = to_text_string(text) - if text.startswith("+++"): - self.setFormat(0, len(text), self.formats["keyword"]) - elif text.startswith("---"): - self.setFormat(0, len(text), self.formats["keyword"]) - elif text.startswith("+"): - self.setFormat(0, len(text), self.formats["string"]) - elif text.startswith("-"): - self.setFormat(0, len(text), self.formats["number"]) - elif text.startswith("@"): - self.setFormat(0, len(text), self.formats["builtin"]) - - -#============================================================================== -# NSIS highlighter -#============================================================================== - -def make_nsis_patterns(): - "Strongly inspired from idlelib.ColorDelegator.make_pat" - kwstr1 = 'Abort AddBrandingImage AddSize AllowRootDirInstall AllowSkipFiles AutoCloseWindow BGFont BGGradient BrandingText BringToFront Call CallInstDLL Caption ClearErrors CompletedText ComponentText CopyFiles CRCCheck CreateDirectory CreateFont CreateShortCut Delete DeleteINISec DeleteINIStr DeleteRegKey DeleteRegValue DetailPrint DetailsButtonText DirText DirVar DirVerify EnableWindow EnumRegKey EnumRegValue Exec ExecShell ExecWait Exch ExpandEnvStrings File FileBufSize FileClose FileErrorText FileOpen FileRead FileReadByte FileSeek FileWrite FileWriteByte FindClose FindFirst FindNext FindWindow FlushINI Function FunctionEnd GetCurInstType GetCurrentAddress GetDlgItem GetDLLVersion GetDLLVersionLocal GetErrorLevel GetFileTime GetFileTimeLocal GetFullPathName GetFunctionAddress GetInstDirError GetLabelAddress GetTempFileName Goto HideWindow ChangeUI CheckBitmap Icon IfAbort IfErrors IfFileExists IfRebootFlag IfSilent InitPluginsDir InstallButtonText InstallColors InstallDir InstallDirRegKey InstProgressFlags InstType InstTypeGetText InstTypeSetText IntCmp IntCmpU IntFmt IntOp IsWindow LangString LicenseBkColor LicenseData LicenseForceSelection LicenseLangString LicenseText LoadLanguageFile LogSet LogText MessageBox MiscButtonText Name OutFile Page PageCallbacks PageEx PageExEnd Pop Push Quit ReadEnvStr ReadINIStr ReadRegDWORD ReadRegStr Reboot RegDLL Rename ReserveFile Return RMDir SearchPath Section SectionEnd SectionGetFlags SectionGetInstTypes SectionGetSize SectionGetText SectionIn SectionSetFlags SectionSetInstTypes SectionSetSize SectionSetText SendMessage SetAutoClose SetBrandingImage SetCompress SetCompressor SetCompressorDictSize SetCtlColors SetCurInstType SetDatablockOptimize SetDateSave SetDetailsPrint SetDetailsView SetErrorLevel SetErrors SetFileAttributes SetFont SetOutPath SetOverwrite SetPluginUnload SetRebootFlag SetShellVarContext SetSilent ShowInstDetails ShowUninstDetails ShowWindow SilentInstall SilentUnInstall Sleep SpaceTexts StrCmp StrCpy StrLen SubCaption SubSection SubSectionEnd UninstallButtonText UninstallCaption UninstallIcon UninstallSubCaption UninstallText UninstPage UnRegDLL Var VIAddVersionKey VIProductVersion WindowIcon WriteINIStr WriteRegBin WriteRegDWORD WriteRegExpandStr WriteRegStr WriteUninstaller XPStyle' - kwstr2 = 'all alwaysoff ARCHIVE auto both bzip2 components current custom details directory false FILE_ATTRIBUTE_ARCHIVE FILE_ATTRIBUTE_HIDDEN FILE_ATTRIBUTE_NORMAL FILE_ATTRIBUTE_OFFLINE FILE_ATTRIBUTE_READONLY FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_TEMPORARY force grey HIDDEN hide IDABORT IDCANCEL IDIGNORE IDNO IDOK IDRETRY IDYES ifdiff ifnewer instfiles instfiles lastused leave left level license listonly lzma manual MB_ABORTRETRYIGNORE MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3 MB_DEFBUTTON4 MB_ICONEXCLAMATION MB_ICONINFORMATION MB_ICONQUESTION MB_ICONSTOP MB_OK MB_OKCANCEL MB_RETRYCANCEL MB_RIGHT MB_SETFOREGROUND MB_TOPMOST MB_YESNO MB_YESNOCANCEL nevershow none NORMAL off OFFLINE on READONLY right RO show silent silentlog SYSTEM TEMPORARY text textonly true try uninstConfirm windows zlib' - kwstr3 = 'MUI_ABORTWARNING MUI_ABORTWARNING_CANCEL_DEFAULT MUI_ABORTWARNING_TEXT MUI_BGCOLOR MUI_COMPONENTSPAGE_CHECKBITMAP MUI_COMPONENTSPAGE_NODESC MUI_COMPONENTSPAGE_SMALLDESC MUI_COMPONENTSPAGE_TEXT_COMPLIST MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_INFO MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_TITLE MUI_COMPONENTSPAGE_TEXT_INSTTYPE MUI_COMPONENTSPAGE_TEXT_TOP MUI_CUSTOMFUNCTION_ABORT MUI_CUSTOMFUNCTION_GUIINIT MUI_CUSTOMFUNCTION_UNABORT MUI_CUSTOMFUNCTION_UNGUIINIT MUI_DESCRIPTION_TEXT MUI_DIRECTORYPAGE_BGCOLOR MUI_DIRECTORYPAGE_TEXT_DESTINATION MUI_DIRECTORYPAGE_TEXT_TOP MUI_DIRECTORYPAGE_VARIABLE MUI_DIRECTORYPAGE_VERIFYONLEAVE MUI_FINISHPAGE_BUTTON MUI_FINISHPAGE_CANCEL_ENABLED MUI_FINISHPAGE_LINK MUI_FINISHPAGE_LINK_COLOR MUI_FINISHPAGE_LINK_LOCATION MUI_FINISHPAGE_NOAUTOCLOSE MUI_FINISHPAGE_NOREBOOTSUPPORT MUI_FINISHPAGE_REBOOTLATER_DEFAULT MUI_FINISHPAGE_RUN MUI_FINISHPAGE_RUN_FUNCTION MUI_FINISHPAGE_RUN_NOTCHECKED MUI_FINISHPAGE_RUN_PARAMETERS MUI_FINISHPAGE_RUN_TEXT MUI_FINISHPAGE_SHOWREADME MUI_FINISHPAGE_SHOWREADME_FUNCTION MUI_FINISHPAGE_SHOWREADME_NOTCHECKED MUI_FINISHPAGE_SHOWREADME_TEXT MUI_FINISHPAGE_TEXT MUI_FINISHPAGE_TEXT_LARGE MUI_FINISHPAGE_TEXT_REBOOT MUI_FINISHPAGE_TEXT_REBOOTLATER MUI_FINISHPAGE_TEXT_REBOOTNOW MUI_FINISHPAGE_TITLE MUI_FINISHPAGE_TITLE_3LINES MUI_FUNCTION_DESCRIPTION_BEGIN MUI_FUNCTION_DESCRIPTION_END MUI_HEADER_TEXT MUI_HEADER_TRANSPARENT_TEXT MUI_HEADERIMAGE MUI_HEADERIMAGE_BITMAP MUI_HEADERIMAGE_BITMAP_NOSTRETCH MUI_HEADERIMAGE_BITMAP_RTL MUI_HEADERIMAGE_BITMAP_RTL_NOSTRETCH MUI_HEADERIMAGE_RIGHT MUI_HEADERIMAGE_UNBITMAP MUI_HEADERIMAGE_UNBITMAP_NOSTRETCH MUI_HEADERIMAGE_UNBITMAP_RTL MUI_HEADERIMAGE_UNBITMAP_RTL_NOSTRETCH MUI_HWND MUI_ICON MUI_INSTALLCOLORS MUI_INSTALLOPTIONS_DISPLAY MUI_INSTALLOPTIONS_DISPLAY_RETURN MUI_INSTALLOPTIONS_EXTRACT MUI_INSTALLOPTIONS_EXTRACT_AS MUI_INSTALLOPTIONS_INITDIALOG MUI_INSTALLOPTIONS_READ MUI_INSTALLOPTIONS_SHOW MUI_INSTALLOPTIONS_SHOW_RETURN MUI_INSTALLOPTIONS_WRITE MUI_INSTFILESPAGE_ABORTHEADER_SUBTEXT MUI_INSTFILESPAGE_ABORTHEADER_TEXT MUI_INSTFILESPAGE_COLORS MUI_INSTFILESPAGE_FINISHHEADER_SUBTEXT MUI_INSTFILESPAGE_FINISHHEADER_TEXT MUI_INSTFILESPAGE_PROGRESSBAR MUI_LANGDLL_ALLLANGUAGES MUI_LANGDLL_ALWAYSSHOW MUI_LANGDLL_DISPLAY MUI_LANGDLL_INFO MUI_LANGDLL_REGISTRY_KEY MUI_LANGDLL_REGISTRY_ROOT MUI_LANGDLL_REGISTRY_VALUENAME MUI_LANGDLL_WINDOWTITLE MUI_LANGUAGE MUI_LICENSEPAGE_BGCOLOR MUI_LICENSEPAGE_BUTTON MUI_LICENSEPAGE_CHECKBOX MUI_LICENSEPAGE_CHECKBOX_TEXT MUI_LICENSEPAGE_RADIOBUTTONS MUI_LICENSEPAGE_RADIOBUTTONS_TEXT_ACCEPT MUI_LICENSEPAGE_RADIOBUTTONS_TEXT_DECLINE MUI_LICENSEPAGE_TEXT_BOTTOM MUI_LICENSEPAGE_TEXT_TOP MUI_PAGE_COMPONENTS MUI_PAGE_CUSTOMFUNCTION_LEAVE MUI_PAGE_CUSTOMFUNCTION_PRE MUI_PAGE_CUSTOMFUNCTION_SHOW MUI_PAGE_DIRECTORY MUI_PAGE_FINISH MUI_PAGE_HEADER_SUBTEXT MUI_PAGE_HEADER_TEXT MUI_PAGE_INSTFILES MUI_PAGE_LICENSE MUI_PAGE_STARTMENU MUI_PAGE_WELCOME MUI_RESERVEFILE_INSTALLOPTIONS MUI_RESERVEFILE_LANGDLL MUI_SPECIALINI MUI_STARTMENU_GETFOLDER MUI_STARTMENU_WRITE_BEGIN MUI_STARTMENU_WRITE_END MUI_STARTMENUPAGE_BGCOLOR MUI_STARTMENUPAGE_DEFAULTFOLDER MUI_STARTMENUPAGE_NODISABLE MUI_STARTMENUPAGE_REGISTRY_KEY MUI_STARTMENUPAGE_REGISTRY_ROOT MUI_STARTMENUPAGE_REGISTRY_VALUENAME MUI_STARTMENUPAGE_TEXT_CHECKBOX MUI_STARTMENUPAGE_TEXT_TOP MUI_UI MUI_UI_COMPONENTSPAGE_NODESC MUI_UI_COMPONENTSPAGE_SMALLDESC MUI_UI_HEADERIMAGE MUI_UI_HEADERIMAGE_RIGHT MUI_UNABORTWARNING MUI_UNABORTWARNING_CANCEL_DEFAULT MUI_UNABORTWARNING_TEXT MUI_UNCONFIRMPAGE_TEXT_LOCATION MUI_UNCONFIRMPAGE_TEXT_TOP MUI_UNFINISHPAGE_NOAUTOCLOSE MUI_UNFUNCTION_DESCRIPTION_BEGIN MUI_UNFUNCTION_DESCRIPTION_END MUI_UNGETLANGUAGE MUI_UNICON MUI_UNPAGE_COMPONENTS MUI_UNPAGE_CONFIRM MUI_UNPAGE_DIRECTORY MUI_UNPAGE_FINISH MUI_UNPAGE_INSTFILES MUI_UNPAGE_LICENSE MUI_UNPAGE_WELCOME MUI_UNWELCOMEFINISHPAGE_BITMAP MUI_UNWELCOMEFINISHPAGE_BITMAP_NOSTRETCH MUI_UNWELCOMEFINISHPAGE_INI MUI_WELCOMEFINISHPAGE_BITMAP MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH MUI_WELCOMEFINISHPAGE_CUSTOMFUNCTION_INIT MUI_WELCOMEFINISHPAGE_INI MUI_WELCOMEPAGE_TEXT MUI_WELCOMEPAGE_TITLE MUI_WELCOMEPAGE_TITLE_3LINES' - bistr = 'addincludedir addplugindir AndIf cd define echo else endif error execute If ifdef ifmacrodef ifmacrondef ifndef include insertmacro macro macroend onGUIEnd onGUIInit onInit onInstFailed onInstSuccess onMouseOverSection onRebootFailed onSelChange onUserAbort onVerifyInstDir OrIf packhdr system undef verbose warning' - instance = any("instance", [r'\$\{.*?\}', r'\$[A-Za-z0-9\_]*']) - define = any("define", [r"\![^\n]*"]) - comment = any("comment", [r"\;[^\n]*", r"\#[^\n]*", r"\/\*(.*?)\*\/"]) - return make_generic_c_patterns(kwstr1+' '+kwstr2+' '+kwstr3, bistr, - instance=instance, define=define, - comment=comment) - -class NsisSH(CppSH): - """NSIS Syntax Highlighter""" - # Syntax highlighting rules: - PROG = re.compile(make_nsis_patterns(), re.S) - - -#============================================================================== -# gettext highlighter -#============================================================================== - -def make_gettext_patterns(): - "Strongly inspired from idlelib.ColorDelegator.make_pat" - kwstr = 'msgid msgstr' - kw = r"\b" + any("keyword", kwstr.split()) + r"\b" - fuzzy = any("builtin", [r"#,[^\n]*"]) - links = any("normal", [r"#:[^\n]*"]) - comment = any("comment", [r"#[^\n]*"]) - number = any("number", - [r"\b[+-]?[0-9]+[lL]?\b", - r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", - r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) - sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' - string = any("string", [sqstring, dqstring]) - return "|".join([kw, string, number, fuzzy, links, comment, - any("SYNC", [r"\n"])]) - -class GetTextSH(GenericSH): - """gettext Syntax Highlighter""" - # Syntax highlighting rules: - PROG = re.compile(make_gettext_patterns(), re.S) - -#============================================================================== -# yaml highlighter -#============================================================================== - -def make_yaml_patterns(): - "Strongly inspired from sublime highlighter " - kw = any("keyword", [r":|>|-|\||\[|\]|[A-Za-z][\w\s\-\_ ]+(?=:)"]) - links = any("normal", [r"#:[^\n]*"]) - comment = any("comment", [r"#[^\n]*"]) - number = any("number", - [r"\b[+-]?[0-9]+[lL]?\b", - r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", - r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"]) - sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' - string = any("string", [sqstring, dqstring]) - return "|".join([kw, string, number, links, comment, - any("SYNC", [r"\n"])]) - -class YamlSH(GenericSH): - """yaml Syntax Highlighter""" - # Syntax highlighting rules: - PROG = re.compile(make_yaml_patterns(), re.S) - - -#============================================================================== -# HTML highlighter -#============================================================================== - -class BaseWebSH(BaseSH): - """Base class for CSS and HTML syntax highlighters""" - NORMAL = 0 - COMMENT = 1 - - def __init__(self, parent, font=None, color_scheme=None): - BaseSH.__init__(self, parent, font, color_scheme) - - def highlightBlock(self, text): - text = to_text_string(text) - previous_state = self.previousBlockState() - - if previous_state == self.COMMENT: - self.setFormat(0, len(text), self.formats["comment"]) - else: - previous_state = self.NORMAL - self.setFormat(0, len(text), self.formats["normal"]) - - self.setCurrentBlockState(previous_state) - match = self.PROG.search(text) - - match_count = 0 - n_characters = len(text) - # There should never be more matches than characters in the text. - while match and match_count < n_characters: - match_dict = match.groupdict() - for key, value in list(match_dict.items()): - if value: - start, end = match.span(key) - if previous_state == self.COMMENT: - if key == "multiline_comment_end": - self.setCurrentBlockState(self.NORMAL) - self.setFormat(end, len(text), - self.formats["normal"]) - else: - self.setCurrentBlockState(self.COMMENT) - self.setFormat(0, len(text), - self.formats["comment"]) - else: - if key == "multiline_comment_start": - self.setCurrentBlockState(self.COMMENT) - self.setFormat(start, len(text), - self.formats["comment"]) - else: - self.setCurrentBlockState(self.NORMAL) - self.setFormat(start, end-start, - self.formats[key]) - - match = self.PROG.search(text, match.end()) - match_count += 1 - -def make_html_patterns(): - """Strongly inspired from idlelib.ColorDelegator.make_pat """ - tags = any("builtin", [r"<", r"[\?/]?>", r"(?<=<).*?(?=[ >])"]) - keywords = any("keyword", [r" [\w:-]*?(?==)"]) - string = any("string", [r'".*?"']) - comment = any("comment", [r""]) - multiline_comment_start = any("multiline_comment_start", [r""]) - return "|".join([comment, multiline_comment_start, - multiline_comment_end, tags, keywords, string]) - -class HtmlSH(BaseWebSH): - """HTML Syntax Highlighter""" - PROG = re.compile(make_html_patterns(), re.S) - - -#============================================================================== -# Pygments based omni-parser -#============================================================================== - -# IMPORTANT NOTE: -# -------------- -# Do not be tempted to generalize the use of PygmentsSH (that is tempting -# because it would lead to more generic and compact code, and not only in -# this very module) because this generic syntax highlighter is far slower -# than the native ones (all classes above). For example, a Python syntax -# highlighter based on PygmentsSH would be 2 to 3 times slower than the -# current native PythonSH syntax highlighter. - -class PygmentsSH(BaseSH): - """ Generic Pygments syntax highlighter """ - # Store the language name and a ref to the lexer - _lang_name = None - _lexer = None - # Syntax highlighting states (from one text block to another): - NORMAL = 0 - def __init__(self, parent, font=None, color_scheme=None): - # Warning: do not move out those import statements - # (pygments is an optional dependency) - from pygments.lexers import get_lexer_by_name - from pygments.token import (Text, Other, Keyword, Name, String, Number, - Comment, Generic, Token) - # Map Pygments tokens to Spyder tokens - self._tokmap = {Text: "normal", - Generic: "normal", - Other: "normal", - Keyword: "keyword", - Token.Operator: "normal", - Name.Builtin: "builtin", - Name: "normal", - Comment: "comment", - String: "string", - Number: "number"} - # Load Pygments' Lexer - if self._lang_name is not None: - self._lexer = get_lexer_by_name(self._lang_name) - BaseSH.__init__(self, parent, font, color_scheme) - - def get_fmt(self, typ): - """ Get the format code for this type """ - # Exact matches first - for key in self._tokmap: - if typ is key: - return self._tokmap[key] - # Partial (parent-> child) matches - for key in self._tokmap: - if typ in key.subtypes: - return self._tokmap[key] - return 'normal' - - def highlightBlock(self, text): - """ Actually highlight the block """ - text = to_text_string(text) - lextree = self._lexer.get_tokens(text) - ct = 0 - for item in lextree: - typ, val = item - key = self.get_fmt(typ) - start = ct - ct += len(val) - self.setFormat(start, ct-start, self.formats[key]) - -class BatchSH(PygmentsSH): - """Batch highlighter""" - _lang_name = 'bat' - -class IniSH(PygmentsSH): - """INI highlighter""" - _lang_name = 'ini' - -class XmlSH(PygmentsSH): - """XML highlighter""" - _lang_name = 'xml' - -class JsSH(PygmentsSH): - """Javascript highlighter""" - _lang_name = 'js' - -class JsonSH(PygmentsSH): - """Json highlighter""" - _lang_name = 'json' - -class JuliaSH(PygmentsSH): - """Julia highlighter""" - _lang_name = 'julia' - -class CssSH(PygmentsSH): - """CSS Syntax Highlighter""" - _lang_name = 'css' - -class MatlabSH(PygmentsSH): - """Matlab highlighter""" - _lang_name = 'matlab' - - -if __name__ == '__main__': - # Test Python Outline Explorer comment regexps - valid_comments = [ - '# --- First variant', - '#------ 2nd variant', - '### 3rd variant' - ] - invalid_comments = [ - '#---', '#--------', '#--- ', '# -------' - ] - for line in valid_comments: - if not PythonSH.OECOMMENT.match(line): - print("Error matching '%s' as outline comment" % line) - for line in invalid_comments: - if PythonSH.OECOMMENT.match(line): - print("Error: '%s' is matched as outline comment" % line) - diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/terminal.py spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/terminal.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/sourcecode/terminal.py 2015-08-24 19:09:46.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/sourcecode/terminal.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Terminal emulation tools""" - -import os - -class ANSIEscapeCodeHandler(object): - """ANSI Escape sequences handler""" - if os.name == 'nt': - # Windows terminal colors: - ANSI_COLORS = ( # Normal, Bright/Light - ('#000000', '#808080'), # 0: black - ('#800000', '#ff0000'), # 1: red - ('#008000', '#00ff00'), # 2: green - ('#808000', '#ffff00'), # 3: yellow - ('#000080', '#0000ff'), # 4: blue - ('#800080', '#ff00ff'), # 5: magenta - ('#008080', '#00ffff'), # 6: cyan - ('#c0c0c0', '#ffffff'), # 7: white - ) - elif os.name == 'mac': - # Terminal.app colors: - ANSI_COLORS = ( # Normal, Bright/Light - ('#000000', '#818383'), # 0: black - ('#C23621', '#FC391F'), # 1: red - ('#25BC24', '#25BC24'), # 2: green - ('#ADAD27', '#EAEC23'), # 3: yellow - ('#492EE1', '#5833FF'), # 4: blue - ('#D338D3', '#F935F8'), # 5: magenta - ('#33BBC8', '#14F0F0'), # 6: cyan - ('#CBCCCD', '#E9EBEB'), # 7: white - ) - else: - # xterm colors: - ANSI_COLORS = ( # Normal, Bright/Light - ('#000000', '#7F7F7F'), # 0: black - ('#CD0000', '#ff0000'), # 1: red - ('#00CD00', '#00ff00'), # 2: green - ('#CDCD00', '#ffff00'), # 3: yellow - ('#0000EE', '#5C5CFF'), # 4: blue - ('#CD00CD', '#ff00ff'), # 5: magenta - ('#00CDCD', '#00ffff'), # 6: cyan - ('#E5E5E5', '#ffffff'), # 7: white - ) - def __init__(self): - self.intensity = 0 - self.italic = None - self.bold = None - self.underline = None - self.foreground_color = None - self.background_color = None - self.default_foreground_color = 30 - self.default_background_color = 47 - - def set_code(self, code): - assert isinstance(code, int) - if code == 0: - # Reset all settings - self.reset() - elif code == 1: - # Text color intensity - self.intensity = 1 - # The following line is commented because most terminals won't - # change the font weight, against ANSI standard recommendation: -# self.bold = True - elif code == 3: - # Italic on - self.italic = True - elif code == 4: - # Underline simple - self.underline = True - elif code == 22: - # Normal text color intensity - self.intensity = 0 - self.bold = False - elif code == 23: - # No italic - self.italic = False - elif code == 24: - # No underline - self.underline = False - elif code >= 30 and code <= 37: - # Text color - self.foreground_color = code - elif code == 39: - # Default text color - self.foreground_color = self.default_foreground_color - elif code >= 40 and code <= 47: - # Background color - self.background_color = code - elif code == 49: - # Default background color - self.background_color = self.default_background_color - self.set_style() - - def set_style(self): - """ - Set font style with the following attributes: - 'foreground_color', 'background_color', 'italic', - 'bold' and 'underline' - """ - raise NotImplementedError - - def reset(self): - self.current_format = None - self.intensity = 0 - self.italic = False - self.bold = False - self.underline = False - self.foreground_color = None - self.background_color = None diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/status.py spyder-3.0.2+dfsg1/spyderlib/widgets/status.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/status.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/status.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,201 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Status bar widgets""" - -import os - -from spyderlib.qt.QtGui import QWidget, QHBoxLayout, QLabel -from spyderlib.qt.QtCore import QTimer, SIGNAL - -# Local import -from spyderlib.baseconfig import _ -from spyderlib.guiconfig import get_font -from spyderlib.py3compat import to_text_string -from spyderlib import dependencies - - -if not os.name == 'nt': - PSUTIL_REQVER = '>=0.3' - dependencies.add("psutil", _("CPU and memory usage info in the status bar"), - required_version=PSUTIL_REQVER) - - -class StatusBarWidget(QWidget): - def __init__(self, parent, statusbar): - QWidget.__init__(self, parent) - - self.label_font = font = get_font('editor') - font.setPointSize(self.font().pointSize()) - font.setBold(True) - - layout = QHBoxLayout() - layout.setContentsMargins(0, 0, 0, 0) - self.setLayout(layout) - - statusbar.addPermanentWidget(self) - - -#============================================================================== -# Main window-related status bar widgets -#============================================================================== -class BaseTimerStatus(StatusBarWidget): - TITLE = None - TIP = None - def __init__(self, parent, statusbar): - StatusBarWidget.__init__(self, parent, statusbar) - self.setToolTip(self.TIP) - layout = self.layout() - layout.addWidget(QLabel(self.TITLE)) - self.label = QLabel() - self.label.setFont(self.label_font) - layout.addWidget(self.label) - layout.addSpacing(20) - if self.is_supported(): - self.timer = QTimer() - self.connect(self.timer, SIGNAL('timeout()'), self.update_label) - self.timer.start(2000) - else: - self.timer = None - self.hide() - - def set_interval(self, interval): - """Set timer interval (ms)""" - if self.timer is not None: - self.timer.setInterval(interval) - - def import_test(self): - """Raise ImportError if feature is not supported""" - raise NotImplementedError - - def is_supported(self): - """Return True if feature is supported""" - try: - self.import_test() - return True - except ImportError: - return False - - def get_value(self): - """Return value (e.g. CPU or memory usage)""" - raise NotImplementedError - - def update_label(self): - """Update status label widget, if widget is visible""" - if self.isVisible(): - self.label.setText('%d %%' % self.get_value()) - -class MemoryStatus(BaseTimerStatus): - TITLE = _("Memory:") - TIP = _("Memory usage status: " - "requires the `psutil` (>=v0.3) library on non-Windows platforms") - def import_test(self): - """Raise ImportError if feature is not supported""" - from spyderlib.utils.system import memory_usage # analysis:ignore - - def get_value(self): - """Return memory usage""" - from spyderlib.utils.system import memory_usage - return memory_usage() - -class CPUStatus(BaseTimerStatus): - TITLE = _("CPU:") - TIP = _("CPU usage status: requires the `psutil` (>=v0.3) library") - def import_test(self): - """Raise ImportError if feature is not supported""" - from spyderlib.utils import programs - if not programs.is_module_installed('psutil', '>=0.2.0'): - # The `interval` argument in `psutil.cpu_percent` function - # was introduced in v0.2.0 - raise ImportError - - def get_value(self): - """Return CPU usage""" - import psutil - return psutil.cpu_percent(interval=0) - - -#============================================================================== -# Editor-related status bar widgets -#============================================================================== -class ReadWriteStatus(StatusBarWidget): - def __init__(self, parent, statusbar): - StatusBarWidget.__init__(self, parent, statusbar) - layout = self.layout() - layout.addWidget(QLabel(_("Permissions:"))) - self.readwrite = QLabel() - self.readwrite.setFont(self.label_font) - layout.addWidget(self.readwrite) - layout.addSpacing(20) - - def readonly_changed(self, readonly): - readwrite = "R" if readonly else "RW" - self.readwrite.setText(readwrite.ljust(3)) - -class EOLStatus(StatusBarWidget): - def __init__(self, parent, statusbar): - StatusBarWidget.__init__(self, parent, statusbar) - layout = self.layout() - layout.addWidget(QLabel(_("End-of-lines:"))) - self.eol = QLabel() - self.eol.setFont(self.label_font) - layout.addWidget(self.eol) - layout.addSpacing(20) - - def eol_changed(self, os_name): - os_name = to_text_string(os_name) - self.eol.setText({"nt": "CRLF", "posix": "LF"}.get(os_name, "CR")) - -class EncodingStatus(StatusBarWidget): - def __init__(self, parent, statusbar): - StatusBarWidget.__init__(self, parent, statusbar) - layout = self.layout() - layout.addWidget(QLabel(_("Encoding:"))) - self.encoding = QLabel() - self.encoding.setFont(self.label_font) - layout.addWidget(self.encoding) - layout.addSpacing(20) - - def encoding_changed(self, encoding): - self.encoding.setText(str(encoding).upper().ljust(15)) - -class CursorPositionStatus(StatusBarWidget): - def __init__(self, parent, statusbar): - StatusBarWidget.__init__(self, parent, statusbar) - layout = self.layout() - layout.addWidget(QLabel(_("Line:"))) - self.line = QLabel() - self.line.setFont(self.label_font) - layout.addWidget(self.line) - layout.addWidget(QLabel(_("Column:"))) - self.column = QLabel() - self.column.setFont(self.label_font) - layout.addWidget(self.column) - self.setLayout(layout) - - def cursor_position_changed(self, line, index): - self.line.setText("%-6d" % (line+1)) - self.column.setText("%-4d" % (index+1)) - - -def test(): - from spyderlib.qt.QtGui import QMainWindow - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - win = QMainWindow() - win.setWindowTitle("Status widgets test") - win.resize(900, 300) - statusbar = win.statusBar() - swidgets = [] - for klass in (ReadWriteStatus, EOLStatus, EncodingStatus, - CursorPositionStatus, MemoryStatus, CPUStatus): - swidget = klass(win, statusbar) - swidgets.append(swidget) - win.show() - app.exec_() - -if __name__ == "__main__": - test() diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/tabs.py spyder-3.0.2+dfsg1/spyderlib/widgets/tabs.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/tabs.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/tabs.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,318 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Tabs widget""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import (QTabWidget, QMenu, QDrag, QApplication, - QTabBar, QWidget, QHBoxLayout) -from spyderlib.qt.QtCore import SIGNAL, Qt, QPoint, QMimeData, QByteArray - -import os.path as osp -import sys - -# Local imports -from spyderlib.baseconfig import _ -from spyderlib.guiconfig import new_shortcut -from spyderlib.utils.misc import get_common_path -from spyderlib.utils.qthelpers import (add_actions, create_toolbutton, - create_action, get_icon) -from spyderlib.py3compat import PY2, to_text_string - - -class TabBar(QTabBar): - """Tabs base class with drag and drop support""" - def __init__(self, parent, ancestor): - QTabBar.__init__(self, parent) - self.ancestor = ancestor - - # To style tabs on Mac - if sys.platform == 'darwin': - self.setObjectName('plugin-tab') - - # Dragging tabs - self.__drag_start_pos = QPoint() - self.setAcceptDrops(True) - - def mousePressEvent(self, event): - """Reimplement Qt method""" - if event.button() == Qt.LeftButton: - self.__drag_start_pos = QPoint(event.pos()) - QTabBar.mousePressEvent(self, event) - - def mouseMoveEvent(self, event): - """Override Qt method""" - if event.buttons() == Qt.MouseButtons(Qt.LeftButton) and \ - (event.pos() - self.__drag_start_pos).manhattanLength() > \ - QApplication.startDragDistance(): - drag = QDrag(self) - mimeData = QMimeData() - # Converting id's to long to avoid an OverflowError with PySide - if PY2: - ancestor_id = long(id(self.ancestor)) - parent_widget_id = long(id(self.parentWidget())) - self_id = long(id(self)) - else: - ancestor_id = id(self.ancestor) - parent_widget_id = id(self.parentWidget()) - self_id = id(self) - mimeData.setData("parent-id", QByteArray.number(ancestor_id)) - mimeData.setData("tabwidget-id", - QByteArray.number(parent_widget_id)) - mimeData.setData("tabbar-id", QByteArray.number(self_id)) - mimeData.setData("source-index", - QByteArray.number(self.tabAt(self.__drag_start_pos))) - drag.setMimeData(mimeData) - drag.exec_() - QTabBar.mouseMoveEvent(self, event) - - def dragEnterEvent(self, event): - """Override Qt method""" - mimeData = event.mimeData() - formats = list( mimeData.formats() ) - if "parent-id" in formats and \ - mimeData.data("parent-id").toLong()[0] == id(self.ancestor): - event.acceptProposedAction() - QTabBar.dragEnterEvent(self, event) - - def dropEvent(self, event): - """Override Qt method""" - mimeData = event.mimeData() - index_from = mimeData.data("source-index").toInt()[0] - index_to = self.tabAt(event.pos()) - if index_to == -1: - index_to = self.count() - if mimeData.data("tabbar-id").toLong()[0] != id(self): - tabwidget_from = str(mimeData.data("tabwidget-id").toLong()[0]) - - # We pass self object ID as a QString, because otherwise it would - # depend on the platform: long for 64bit, int for 32bit. Replacing - # by long all the time is not working on some 32bit platforms - # (see Issue 1094, Issue 1098) - self.emit(SIGNAL("move_tab(QString,int,int)"), - tabwidget_from, index_from, index_to) - - event.acceptProposedAction() - elif index_from != index_to: - self.emit(SIGNAL("move_tab(int,int)"), index_from, index_to) - event.acceptProposedAction() - QTabBar.dropEvent(self, event) - - -class BaseTabs(QTabWidget): - """TabWidget with context menu and corner widgets""" - def __init__(self, parent, actions=None, menu=None, - corner_widgets=None, menu_use_tooltips=False): - QTabWidget.__init__(self, parent) - self.setUsesScrollButtons(True) - - # To style tabs on Mac - if sys.platform == 'darwin': - self.setObjectName('plugin-tab') - - self.corner_widgets = {} - self.menu_use_tooltips = menu_use_tooltips - - if menu is None: - self.menu = QMenu(self) - if actions: - add_actions(self.menu, actions) - else: - self.menu = menu - - # Corner widgets - if corner_widgets is None: - corner_widgets = {} - corner_widgets.setdefault(Qt.TopLeftCorner, []) - corner_widgets.setdefault(Qt.TopRightCorner, []) - self.browse_button = create_toolbutton(self, - icon=get_icon("browse_tab.png"), - tip=_("Browse tabs")) - self.browse_tabs_menu = QMenu(self) - self.browse_button.setMenu(self.browse_tabs_menu) - self.browse_button.setPopupMode(self.browse_button.InstantPopup) - self.connect(self.browse_tabs_menu, SIGNAL("aboutToShow()"), - self.update_browse_tabs_menu) - corner_widgets[Qt.TopLeftCorner] += [self.browse_button] - - self.set_corner_widgets(corner_widgets) - - def update_browse_tabs_menu(self): - """Update browse tabs menu""" - self.browse_tabs_menu.clear() - names = [] - dirnames = [] - for index in range(self.count()): - if self.menu_use_tooltips: - text = to_text_string(self.tabToolTip(index)) - else: - text = to_text_string(self.tabText(index)) - names.append(text) - if osp.isfile(text): - # Testing if tab names are filenames - dirnames.append(osp.dirname(text)) - offset = None - - # If tab names are all filenames, removing common path: - if len(names) == len(dirnames): - common = get_common_path(dirnames) - if common is None: - offset = None - else: - offset = len(common)+1 - if offset <= 3: - # Common path is not a path but a drive letter... - offset = None - - for index, text in enumerate(names): - tab_action = create_action(self, text[offset:], - icon=self.tabIcon(index), - toggled=lambda state, index=index: - self.setCurrentIndex(index), - tip=self.tabToolTip(index)) - tab_action.setChecked(index == self.currentIndex()) - self.browse_tabs_menu.addAction(tab_action) - - def set_corner_widgets(self, corner_widgets): - """ - Set tabs corner widgets - corner_widgets: dictionary of (corner, widgets) - corner: Qt.TopLeftCorner or Qt.TopRightCorner - widgets: list of widgets (may contains integers to add spacings) - """ - assert isinstance(corner_widgets, dict) - assert all(key in (Qt.TopLeftCorner, Qt.TopRightCorner) - for key in corner_widgets) - self.corner_widgets.update(corner_widgets) - for corner, widgets in list(self.corner_widgets.items()): - cwidget = QWidget() - cwidget.hide() - prev_widget = self.cornerWidget(corner) - if prev_widget: - prev_widget.close() - self.setCornerWidget(cwidget, corner) - clayout = QHBoxLayout() - clayout.setContentsMargins(0, 0, 0, 0) - for widget in widgets: - if isinstance(widget, int): - clayout.addSpacing(widget) - else: - clayout.addWidget(widget) - cwidget.setLayout(clayout) - cwidget.show() - - def add_corner_widgets(self, widgets, corner=Qt.TopRightCorner): - self.set_corner_widgets({corner: - self.corner_widgets.get(corner, [])+widgets}) - - def contextMenuEvent(self, event): - """Override Qt method""" - if self.menu: - self.menu.popup(event.globalPos()) - - def mousePressEvent(self, event): - """Override Qt method""" - if event.button() == Qt.MidButton: - index = self.tabBar().tabAt(event.pos()) - if index >= 0: - self.emit(SIGNAL("close_tab(int)"), index) - event.accept() - return - QTabWidget.mousePressEvent(self, event) - - def keyPressEvent(self, event): - """Override Qt method""" - ctrl = event.modifiers() & Qt.ControlModifier - key = event.key() - handled = False - if ctrl and self.count() > 0: - index = self.currentIndex() - if key == Qt.Key_PageUp and index > 0: - self.setCurrentIndex(index-1) - handled = True - elif key == Qt.Key_PageDown and index < self.count()-1: - self.setCurrentIndex(index+1) - handled = True - if not handled: - QTabWidget.keyPressEvent(self, event) - - def set_close_function(self, func): - """Setting Tabs close function - None -> tabs are not closable""" - state = func is not None - if state: - self.connect(self, SIGNAL("close_tab(int)"), func) - try: - # Assuming Qt >= 4.5 - QTabWidget.setTabsClosable(self, state) - self.connect(self, SIGNAL("tabCloseRequested(int)"), func) - except AttributeError: - # Workaround for Qt < 4.5 - close_button = create_toolbutton(self, triggered=func, - icon=get_icon("fileclose.png"), - tip=_("Close current tab")) - self.setCornerWidget(close_button if state else None) - - -class Tabs(BaseTabs): - """BaseTabs widget with movable tabs and tab navigation shortcuts""" - def __init__(self, parent, actions=None, menu=None, - corner_widgets=None, menu_use_tooltips=False): - BaseTabs.__init__(self, parent, actions, menu, - corner_widgets, menu_use_tooltips) - tab_bar = TabBar(self, parent) - self.connect(tab_bar, SIGNAL('move_tab(int,int)'), self.move_tab) - self.connect(tab_bar, SIGNAL('move_tab(QString,int,int)'), - self.move_tab_from_another_tabwidget) - self.setTabBar(tab_bar) - - new_shortcut("Ctrl+Tab", parent, lambda: self.tab_navigate(1)) - new_shortcut("Shift+Ctrl+Tab", parent, lambda: self.tab_navigate(-1)) - new_shortcut("Ctrl+W", parent, lambda: self.emit(SIGNAL("close_tab(int)"), - self.currentIndex())) - new_shortcut("Ctrl+F4", parent, lambda: self.emit(SIGNAL("close_tab(int)"), - self.currentIndex())) - - def tab_navigate(self, delta=1): - """Ctrl+Tab""" - if delta > 0 and self.currentIndex() == self.count()-1: - index = delta-1 - elif delta < 0 and self.currentIndex() == 0: - index = self.count()+delta - else: - index = self.currentIndex()+delta - self.setCurrentIndex(index) - - def move_tab(self, index_from, index_to): - """Move tab inside a tabwidget""" - self.emit(SIGNAL('move_data(int,int)'), index_from, index_to) - - tip, text = self.tabToolTip(index_from), self.tabText(index_from) - icon, widget = self.tabIcon(index_from), self.widget(index_from) - current_widget = self.currentWidget() - - self.removeTab(index_from) - self.insertTab(index_to, widget, icon, text) - self.setTabToolTip(index_to, tip) - - self.setCurrentWidget(current_widget) - - self.emit(SIGNAL('move_tab_finished()')) - - def move_tab_from_another_tabwidget(self, tabwidget_from, - index_from, index_to): - """Move tab from a tabwidget to another""" - - # We pass self object IDs as QString objs, because otherwise it would - # depend on the platform: long for 64bit, int for 32bit. Replacing - # by long all the time is not working on some 32bit platforms - # (see Issue 1094, Issue 1098) - self.emit(SIGNAL('move_tab(QString,QString,int,int)'), - tabwidget_from, str(id(self)), index_from, index_to) diff -Nru spyder-2.3.8+dfsg1/spyderlib/widgets/texteditor.py spyder-3.0.2+dfsg1/spyderlib/widgets/texteditor.py --- spyder-2.3.8+dfsg1/spyderlib/widgets/texteditor.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderlib/widgets/texteditor.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Text Editor Dialog based on Qt -""" - -from __future__ import print_function - -from spyderlib.qt.QtCore import Qt, SIGNAL, SLOT -from spyderlib.qt.QtGui import QVBoxLayout, QTextEdit, QDialog, QDialogButtonBox - -# Local import -from spyderlib.baseconfig import _ -from spyderlib.guiconfig import get_font -from spyderlib.utils.qthelpers import get_icon -from spyderlib.py3compat import (to_text_string, to_binary_string, - is_binary_string) - - -class TextEditor(QDialog): - """Array Editor Dialog""" - def __init__(self, text, title='', font=None, parent=None, - readonly=False, size=(400, 300)): - QDialog.__init__(self, parent) - - # Destroying the C++ object right after closing the dialog box, - # otherwise it may be garbage-collected in another QThread - # (e.g. the editor's analysis thread in Spyder), thus leading to - # a segmentation fault on UNIX or an application crash on Windows - self.setAttribute(Qt.WA_DeleteOnClose) - - self.text = None - - # Display text as unicode if it comes as bytes, so users see - # its right representation - if is_binary_string(text): - self.is_binary = True - text = to_text_string(text, 'utf8') - else: - self.is_binary = False - - self.layout = QVBoxLayout() - self.setLayout(self.layout) - - # Text edit - self.edit = QTextEdit(parent) - self.connect(self.edit, SIGNAL('textChanged()'), self.text_changed) - self.edit.setReadOnly(readonly) - self.edit.setPlainText(text) - if font is None: - font = get_font('texteditor') - self.edit.setFont(font) - self.layout.addWidget(self.edit) - - # Buttons configuration - buttons = QDialogButtonBox.Ok - if not readonly: - buttons = buttons | QDialogButtonBox.Cancel - bbox = QDialogButtonBox(buttons) - self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()")) - self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()")) - self.layout.addWidget(bbox) - - # Make the dialog act as a window - self.setWindowFlags(Qt.Window) - - self.setWindowIcon(get_icon('edit.png')) - self.setWindowTitle(_("Text editor") + \ - "%s" % (" - "+str(title) if str(title) else "")) - self.resize(size[0], size[1]) - - def text_changed(self): - """Text has changed""" - # Save text as bytes, if it was initially bytes - if self.is_binary: - self.text = to_binary_string(self.edit.toPlainText(), 'utf8') - else: - self.text = to_text_string(self.edit.toPlainText()) - - def get_value(self): - """Return modified text""" - # It is import to avoid accessing Qt C++ object as it has probably - # already been destroyed, due to the Qt.WA_DeleteOnClose attribute - return self.text - - -def test(): - """Text editor demo""" - from spyderlib.utils.qthelpers import qapplication - _app = qapplication() # analysis:ignore - dialog = TextEditor(""" - 01234567890123456789012345678901234567890123456789012345678901234567890123456789 - dedekdh elkd ezd ekjd lekdj elkdfjelfjk e - """) - dialog.show() - if dialog.exec_(): - text = dialog.get_value() - print("Accepted:", text) - dialog = TextEditor(text) - dialog.exec_() - else: - print("Canceled") - -if __name__ == "__main__": - test() \ No newline at end of file Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderplugins/images/profiler.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderplugins/images/profiler.png differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderplugins/images/pylint.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderplugins/images/pylint.png differ diff -Nru spyder-2.3.8+dfsg1/spyderplugins/io_dicom.py spyder-3.0.2+dfsg1/spyderplugins/io_dicom.py --- spyder-2.3.8+dfsg1/spyderplugins/io_dicom.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/io_dicom.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -# -*- coding:utf-8 -*- -"""Example of I/O plugin for loading DICOM files""" - -import os.path as osp - -try: - import dicom - def load_dicom(filename): - try: - name = osp.splitext(osp.basename(filename))[0] - return {name: dicom.ReadFile(filename).PixelArray}, None - except Exception as error: - return None, str(error) -except ImportError: - load_dicom = None - -#=============================================================================== -# The following statements are required to register this I/O plugin: -#=============================================================================== -FORMAT_NAME = "DICOM images" -FORMAT_EXT = ".dcm" -FORMAT_LOAD = load_dicom -FORMAT_SAVE = None diff -Nru spyder-2.3.8+dfsg1/spyderplugins/io_hdf5.py spyder-3.0.2+dfsg1/spyderplugins/io_hdf5.py --- spyder-2.3.8+dfsg1/spyderplugins/io_hdf5.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/io_hdf5.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -# -*- coding:utf-8 -*- -# -# Copyright © 2011 David Anthony Powell -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""I/O plugin for loading/saving HDF5 files - -Note that this is a fairly dumb implementation which reads the whole HDF5 file into -Spyder's variable explorer. Since HDF5 files are designed for storing very large -data-sets, it may be much better to work directly with the HDF5 objects, thus keeping -the data on disk. Nonetheless, this plugin gives quick and dirty but convenient -access to HDF5 files. - -There is no support for creating files with compression, chunking etc, although -these can be read without problem. - -All datatypes to be saved must be convertible to a numpy array, otherwise an exception -will be raised. - -Data attributes are currently ignored. - -When reading an HDF5 file with sub-groups, groups in the HDF5 file will -correspond to dictionaries with the same layout. However, when saving -data, dictionaries are not turned into HDF5 groups. - -TODO: Look for the pytables library if h5py is not found?? -TODO: Check issues with valid python names vs valid h5f5 names -""" - -from __future__ import print_function - -try: - # Do not import h5py here because it will try to import IPython, - # and this is freezing the Spyder GUI - import imp - imp.find_module('h5py') - import numpy as np - - def load_hdf5(filename): - import h5py - def get_group(group): - contents = {} - for name, obj in list(group.items()): - if isinstance(obj, h5py.Dataset): - contents[name] = np.array(obj) - elif isinstance(obj, h5py.Group): - # it is a group, so call self recursively - contents[name] = get_group(obj) - # other objects such as links are ignored - return contents - - try: - f = h5py.File(filename, 'r') - contents = get_group(f) - f.close() - return contents, None - except Exception as error: - return None, str(error) - - def save_hdf5(data, filename): - import h5py - try: - f = h5py.File(filename, 'w') - for key, value in list(data.items()): - f[key] = np.array(value) - f.close() - except Exception as error: - return str(error) -except ImportError: - load_hdf5 = None - save_hdf5 = None - -#=============================================================================== -# The following statements are required to register this I/O plugin: -#=============================================================================== -FORMAT_NAME = "HDF5" -FORMAT_EXT = ".h5" -FORMAT_LOAD = load_hdf5 -FORMAT_SAVE = save_hdf5 - -if __name__ == "__main__": - data = {'a' : [1, 2, 3, 4], 'b' : 4.5} - print(save_hdf5(data, "test.h5")) - print(load_hdf5("test.h5")) Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderplugins/locale/es/LC_MESSAGES/p_breakpoints.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderplugins/locale/es/LC_MESSAGES/p_breakpoints.mo differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderplugins/locale/es/LC_MESSAGES/p_profiler.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderplugins/locale/es/LC_MESSAGES/p_profiler.mo differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderplugins/locale/es/LC_MESSAGES/p_pylint.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderplugins/locale/es/LC_MESSAGES/p_pylint.mo differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderplugins/locale/fr/LC_MESSAGES/p_breakpoints.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderplugins/locale/fr/LC_MESSAGES/p_breakpoints.mo differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderplugins/locale/fr/LC_MESSAGES/p_profiler.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderplugins/locale/fr/LC_MESSAGES/p_profiler.mo differ Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyderplugins/locale/fr/LC_MESSAGES/p_pylint.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyderplugins/locale/fr/LC_MESSAGES/p_pylint.mo differ diff -Nru spyder-2.3.8+dfsg1/spyderplugins/p_breakpoints.py spyder-3.0.2+dfsg1/spyderplugins/p_breakpoints.py --- spyder-2.3.8+dfsg1/spyderplugins/p_breakpoints.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/p_breakpoints.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ -# -*- coding:utf-8 -*- -# -# Copyright © 2012 Jed Ludlow -# Based loosely on p_pylint.py by Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Breakpoint Plugin""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtCore import SIGNAL - -# Local imports -from spyderlib.baseconfig import get_translation -_ = get_translation("p_breakpoints", dirname="spyderplugins") -from spyderlib.utils.qthelpers import get_icon, create_action -from spyderlib.plugins import SpyderPluginMixin -from spyderplugins.widgets.breakpointsgui import BreakpointWidget -from spyderlib.py3compat import to_text_string, is_text_string - - -class Breakpoints(BreakpointWidget, SpyderPluginMixin): - """Breakpoint list""" - CONF_SECTION = 'breakpoints' -# CONFIGWIDGET_CLASS = BreakpointConfigPage - def __init__(self, parent=None): - BreakpointWidget.__init__(self, parent=parent) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - self.set_data() - - #------ SpyderPluginWidget API -------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _("Breakpoints") - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('bug.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.dictwidget - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - return [] - - def on_first_registration(self): - """Action to be performed on first plugin registration""" - self.main.tabify_plugins(self.main.inspector, self) - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self, SIGNAL("edit_goto(QString,int,QString)"), - self.main.editor.load) - self.connect(self, SIGNAL('redirect_stdio(bool)'), - self.main.redirect_internalshell_stdio) - self.connect(self, SIGNAL('clear_all_breakpoints()'), - self.main.editor.clear_all_breakpoints) - self.connect(self, SIGNAL('clear_breakpoint(QString,int)'), - self.main.editor.clear_breakpoint) - self.connect(self, SIGNAL('set_or_edit_conditional_breakpoint()'), - self.main.editor.set_or_edit_conditional_breakpoint) - self.connect(self.main.editor, - SIGNAL("breakpoints_saved()"), - self.set_data) - - self.main.add_dockwidget(self) - - list_action = create_action(self, _("List breakpoints"), - triggered=self.show) - list_action.setEnabled(True) - - # A fancy way to insert the action into the Breakpoints menu under - # the assumption that Breakpoints is the first QMenu in the list. - for item in self.main.debug_menu_actions: - try: - menu_title = item.title() - except AttributeError: - pass - else: - # Depending on Qt API version, could get a QString or - # unicode from title() - if not is_text_string(menu_title): # string is a QString - menu_title = to_text_string(menu_title.toUtf8) - item.addAction(list_action) - # If we've reached this point it means we've located the - # first QMenu in the run_menu. Since there might be other - # QMenu entries in run_menu, we'll break so that the - # breakpoint action is only inserted once into the run_menu. - break - self.main.editor.pythonfile_dependent_actions += [list_action] - - def refresh_plugin(self): - """Refresh widget""" - pass - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - return True - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - pass - - def show(self): - """Show the breakpoints dockwidget""" - if self.dockwidget and not self.ismaximized: - self.dockwidget.setVisible(True) - self.dockwidget.setFocus() - self.dockwidget.raise_() - - -#============================================================================== -# The following statements are required to register this 3rd party plugin: -#============================================================================== -PLUGIN_CLASS = Breakpoints - diff -Nru spyder-2.3.8+dfsg1/spyderplugins/p_profiler.py spyder-3.0.2+dfsg1/spyderplugins/p_profiler.py --- spyder-2.3.8+dfsg1/spyderplugins/p_profiler.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/p_profiler.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,146 +0,0 @@ -# -*- coding:utf-8 -*- -# -# Copyright © 2011 Santiago Jaramillo -# based on p_pylint.py by Pierre Raybaut -# -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Profiler Plugin""" - -from spyderlib.qt.QtGui import QVBoxLayout, QGroupBox, QLabel -from spyderlib.qt.QtCore import SIGNAL, Qt - -# Local imports -from spyderlib.baseconfig import get_translation -_ = get_translation("p_profiler", dirname="spyderplugins") -from spyderlib.utils.qthelpers import get_icon, create_action -from spyderlib.plugins import SpyderPluginMixin, PluginConfigPage, runconfig - -from spyderplugins.widgets.profilergui import (ProfilerWidget, - is_profiler_installed) - - -class ProfilerConfigPage(PluginConfigPage): - def setup_page(self): - results_group = QGroupBox(_("Results")) - results_label1 = QLabel(_("Profiler plugin results "\ - "(the output of python's profile/cProfile)\n" - "are stored here:")) - results_label1.setWordWrap(True) - - # Warning: do not try to regroup the following QLabel contents with - # widgets above -- this string was isolated here in a single QLabel - # on purpose: to fix Issue 863 - results_label2 = QLabel(ProfilerWidget.DATAPATH) - - results_label2.setTextInteractionFlags(Qt.TextSelectableByMouse) - results_label2.setWordWrap(True) - - results_layout = QVBoxLayout() - results_layout.addWidget(results_label1) - results_layout.addWidget(results_label2) - results_group.setLayout(results_layout) - - vlayout = QVBoxLayout() - vlayout.addWidget(results_group) - vlayout.addStretch(1) - self.setLayout(vlayout) - -class Profiler(ProfilerWidget, SpyderPluginMixin): - """Profiler (after python's profile and pstats)""" - CONF_SECTION = 'profiler' - CONFIGWIDGET_CLASS = ProfilerConfigPage - def __init__(self, parent=None): - ProfilerWidget.__init__(self, parent=parent, - max_entries=self.get_option('max_entries', 50)) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - #------ SpyderPluginWidget API --------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _("Profiler") - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('profiler.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.datatree - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - return [] - - def on_first_registration(self): - """Action to be performed on first plugin registration""" - self.main.tabify_plugins(self.main.inspector, self) - self.dockwidget.hide() - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self, SIGNAL("edit_goto(QString,int,QString)"), - self.main.editor.load) - self.connect(self, SIGNAL('redirect_stdio(bool)'), - self.main.redirect_internalshell_stdio) - self.main.add_dockwidget(self) - - profiler_act = create_action(self, _("Profile"), - icon=get_icon('profiler.png'), - triggered=self.run_profiler) - profiler_act.setEnabled(is_profiler_installed()) - self.register_shortcut(profiler_act, context="Profiler", - name="Run profiler") - - self.main.run_menu_actions += [profiler_act] - self.main.editor.pythonfile_dependent_actions += [profiler_act] - - def refresh_plugin(self): - """Refresh profiler widget""" - #self.remove_obsolete_items() # FIXME: not implemented yet - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - return True - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - # The history depth option will be applied at - # next Spyder startup, which is soon enough - pass - - #------ Public API --------------------------------------------------------- - def run_profiler(self): - """Run profiler""" - self.analyze(self.main.editor.get_current_filename()) - - def analyze(self, filename): - """Reimplement analyze method""" - if self.dockwidget and not self.ismaximized: - self.dockwidget.setVisible(True) - self.dockwidget.setFocus() - self.dockwidget.raise_() - pythonpath = self.main.get_spyder_pythonpath() - runconf = runconfig.get_run_configuration(filename) - wdir, args = None, None - if runconf is not None: - if runconf.wdir_enabled: - wdir = runconf.wdir - if runconf.args_enabled: - args = runconf.args - ProfilerWidget.analyze(self, filename, wdir=wdir, args=args, - pythonpath=pythonpath) - - -#=============================================================================== -# The following statements are required to register this 3rd party plugin: -#=============================================================================== -PLUGIN_CLASS = Profiler - diff -Nru spyder-2.3.8+dfsg1/spyderplugins/p_pylint.py spyder-3.0.2+dfsg1/spyderplugins/p_pylint.py --- spyder-2.3.8+dfsg1/spyderplugins/p_pylint.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/p_pylint.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,178 +0,0 @@ -# -*- coding:utf-8 -*- -# -# Copyright © 2009-2011 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Pylint Code Analysis Plugin""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import QInputDialog, QVBoxLayout, QGroupBox, QLabel -from spyderlib.qt.QtCore import SIGNAL, Qt - -# Local imports -from spyderlib.baseconfig import get_translation -_ = get_translation("p_pylint", dirname="spyderplugins") -from spyderlib.utils.qthelpers import get_icon, create_action -from spyderlib.plugins import SpyderPluginMixin, PluginConfigPage - -from spyderplugins.widgets.pylintgui import PylintWidget, PYLINT_PATH - - -class PylintConfigPage(PluginConfigPage): - def setup_page(self): - settings_group = QGroupBox(_("Settings")) - save_box = self.create_checkbox(_("Save file before analyzing it"), - 'save_before', default=True) - - hist_group = QGroupBox(_("History")) - hist_label1 = QLabel(_("The following option will be applied at next " - "startup.")) - hist_label1.setWordWrap(True) - hist_spin = self.create_spinbox(_("History: "), - _(" results"), 'max_entries', default=50, - min_=10, max_=1000000, step=10) - - results_group = QGroupBox(_("Results")) - results_label1 = QLabel(_("Results are stored here:")) - results_label1.setWordWrap(True) - - # Warning: do not try to regroup the following QLabel contents with - # widgets above -- this string was isolated here in a single QLabel - # on purpose: to fix Issue 863 - results_label2 = QLabel(PylintWidget.DATAPATH) - - results_label2.setTextInteractionFlags(Qt.TextSelectableByMouse) - results_label2.setWordWrap(True) - - settings_layout = QVBoxLayout() - settings_layout.addWidget(save_box) - settings_group.setLayout(settings_layout) - - hist_layout = QVBoxLayout() - hist_layout.addWidget(hist_label1) - hist_layout.addWidget(hist_spin) - hist_group.setLayout(hist_layout) - - results_layout = QVBoxLayout() - results_layout.addWidget(results_label1) - results_layout.addWidget(results_label2) - results_group.setLayout(results_layout) - - vlayout = QVBoxLayout() - vlayout.addWidget(settings_group) - vlayout.addWidget(hist_group) - vlayout.addWidget(results_group) - vlayout.addStretch(1) - self.setLayout(vlayout) - - -class Pylint(PylintWidget, SpyderPluginMixin): - """Python source code analysis based on pylint""" - CONF_SECTION = 'pylint' - CONFIGWIDGET_CLASS = PylintConfigPage - def __init__(self, parent=None): - PylintWidget.__init__(self, parent=parent, - max_entries=self.get_option('max_entries', 50)) - SpyderPluginMixin.__init__(self, parent) - - # Initialize plugin - self.initialize_plugin() - - #------ SpyderPluginWidget API -------------------------------------------- - def get_plugin_title(self): - """Return widget title""" - return _("Static code analysis") - - def get_plugin_icon(self): - """Return widget icon""" - return get_icon('pylint.png') - - def get_focus_widget(self): - """ - Return the widget to give focus to when - this plugin's dockwidget is raised on top-level - """ - return self.treewidget - - def get_plugin_actions(self): - """Return a list of actions related to plugin""" - # Font - history_action = create_action(self, _("History..."), - None, 'history.png', - _("Set history maximum entries"), - triggered=self.change_history_depth) - self.treewidget.common_actions += (None, history_action) - return [] - - def on_first_registration(self): - """Action to be performed on first plugin registration""" - self.main.tabify_plugins(self.main.inspector, self) - self.dockwidget.hide() - - def register_plugin(self): - """Register plugin in Spyder's main window""" - self.connect(self, SIGNAL("edit_goto(QString,int,QString)"), - self.main.editor.load) - self.connect(self, SIGNAL('redirect_stdio(bool)'), - self.main.redirect_internalshell_stdio) - self.main.add_dockwidget(self) - - pylint_act = create_action(self, _("Run static code analysis"), - triggered=self.run_pylint) - pylint_act.setEnabled(PYLINT_PATH is not None) - self.register_shortcut(pylint_act, context="Pylint", - name="Run analysis") - - self.main.source_menu_actions += [None, pylint_act] - self.main.editor.pythonfile_dependent_actions += [pylint_act] - - def refresh_plugin(self): - """Refresh pylint widget""" - self.remove_obsolete_items() - - def closing_plugin(self, cancelable=False): - """Perform actions before parent main window is closed""" - return True - - def apply_plugin_settings(self, options): - """Apply configuration file's plugin settings""" - # The history depth option will be applied at - # next Spyder startup, which is soon enough - pass - - #------ Public API -------------------------------------------------------- - def change_history_depth(self): - "Change history max entries""" - depth, valid = QInputDialog.getInteger(self, _('History'), - _('Maximum entries'), - self.get_option('max_entries'), - 10, 10000) - if valid: - self.set_option('max_entries', depth) - - def run_pylint(self): - """Run pylint code analysis""" - if self.get_option('save_before', True)\ - and not self.main.editor.save(): - return - self.analyze( self.main.editor.get_current_filename() ) - - def analyze(self, filename): - """Reimplement analyze method""" - if self.dockwidget and not self.ismaximized: - self.dockwidget.setVisible(True) - self.dockwidget.setFocus() - self.dockwidget.raise_() - PylintWidget.analyze(self, filename) - - -#============================================================================== -# The following statements are required to register this 3rd party plugin: -#============================================================================== -PLUGIN_CLASS = Pylint - diff -Nru spyder-2.3.8+dfsg1/spyderplugins/widgets/breakpointsgui.py spyder-3.0.2+dfsg1/spyderplugins/widgets/breakpointsgui.py --- spyder-2.3.8+dfsg1/spyderplugins/widgets/breakpointsgui.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/widgets/breakpointsgui.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,234 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012 Jed Ludlow -# based loosley on pylintgui.py by Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Breakpoint widget""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from spyderlib.qt.QtGui import (QWidget, QTableView, QItemDelegate, - QVBoxLayout, QMenu) -from spyderlib.qt.QtCore import (Qt, SIGNAL, QTextCodec, - QModelIndex, QAbstractTableModel) -locale_codec = QTextCodec.codecForLocale() -from spyderlib.qt.compat import to_qvariant -import sys -import os.path as osp - -# Local imports -from spyderlib.baseconfig import get_translation -from spyderlib.config import CONF -from spyderlib.utils.qthelpers import create_action, add_actions - -_ = get_translation("p_breakpoints", dirname="spyderplugins") - -class BreakpointTableModel(QAbstractTableModel): - """ - Table model for breakpoints dictionary - - """ - def __init__(self, parent, data): - QAbstractTableModel.__init__(self, parent) - if data is None: - data = {} - self._data = None - self.breakpoints = None - self.set_data(data) - - def set_data(self, data): - """Set model data""" - self._data = data - keys = list(data.keys()) - self.breakpoints = [] - for key in keys: - bp_list = data[key] - if bp_list: - for item in data[key]: - self.breakpoints.append((key, item[0], item[1], "")) - self.reset() - - def rowCount(self, qindex=QModelIndex()): - """Array row number""" - return len(self.breakpoints) - - def columnCount(self, qindex=QModelIndex()): - """Array column count""" - return 4 - - def sort(self, column, order=Qt.DescendingOrder): - """Overriding sort method""" - if column == 0: - self.breakpoints.sort( - key=lambda breakpoint: breakpoint[1]) - self.breakpoints.sort( - key=lambda breakpoint: osp.basename(breakpoint[0])) - elif column == 1: - pass - elif column == 2: - pass - elif column == 3: - pass - self.reset() - - def headerData(self, section, orientation, role=Qt.DisplayRole): - """Overriding method headerData""" - if role != Qt.DisplayRole: - return to_qvariant() - i_column = int(section) - if orientation == Qt.Horizontal: - headers = (_("File"), _("Line"), _("Condition"), "") - return to_qvariant( headers[i_column] ) - else: - return to_qvariant() - - def get_value(self, index): - """Return current value""" - return self.breakpoints[index.row()][index.column()] - - def data(self, index, role=Qt.DisplayRole): - """Return data at table index""" - if not index.isValid(): - return to_qvariant() - if role == Qt.DisplayRole: - if index.column() == 0: - value = osp.basename(self.get_value(index)) - return to_qvariant(value) - else: - value = self.get_value(index) - return to_qvariant(value) - elif role == Qt.TextAlignmentRole: - return to_qvariant(int(Qt.AlignLeft|Qt.AlignVCenter)) - elif role == Qt.ToolTipRole: - if index.column() == 0: - value = self.get_value(index) - return to_qvariant(value) - else: - return to_qvariant() - -class BreakpointDelegate(QItemDelegate): - def __init__(self, parent=None): - QItemDelegate.__init__(self, parent) - -class BreakpointTableView(QTableView): - def __init__(self, parent, data): - QTableView.__init__(self, parent) - self.model = BreakpointTableModel(self, data) - self.setModel(self.model) - self.delegate = BreakpointDelegate(self) - self.setItemDelegate(self.delegate) - - self.setup_table() - - def setup_table(self): - """Setup table""" - self.horizontalHeader().setStretchLastSection(True) - self.adjust_columns() - self.columnAt(0) - # Sorting columns - self.setSortingEnabled(False) - self.sortByColumn(0, Qt.DescendingOrder) - - def adjust_columns(self): - """Resize three first columns to contents""" - for col in range(3): - self.resizeColumnToContents(col) - - def mouseDoubleClickEvent(self, event): - """Reimplement Qt method""" - index_clicked = self.indexAt(event.pos()) - if self.model.breakpoints: - filename = self.model.breakpoints[index_clicked.row()][0] - line_number_str = self.model.breakpoints[index_clicked.row()][1] - self.emit(SIGNAL("edit_goto(QString,int,QString)"), - filename, int(line_number_str), '') - if index_clicked.column()==2: - self.emit(SIGNAL("set_or_edit_conditional_breakpoint()")) - - def contextMenuEvent(self, event): - index_clicked = self.indexAt(event.pos()) - actions = [] - self.popup_menu = QMenu(self) - clear_all_breakpoints_action = create_action(self, - _("Clear breakpoints in all files"), - triggered=lambda: self.emit(SIGNAL('clear_all_breakpoints()'))) - actions.append(clear_all_breakpoints_action) - if self.model.breakpoints: - filename = self.model.breakpoints[index_clicked.row()][0] - lineno = int(self.model.breakpoints[index_clicked.row()][1]) - clear_breakpoint_action = create_action(self, - _("Clear this breakpoint"), - triggered=lambda filename=filename, lineno=lineno: \ - self.emit(SIGNAL('clear_breakpoint(QString,int)'), - filename, lineno)) - actions.insert(0,clear_breakpoint_action) - - edit_breakpoint_action = create_action(self, - _("Edit this breakpoint"), - triggered=lambda filename=filename, lineno=lineno: \ - (self.emit(SIGNAL('edit_goto(QString,int,QString)'), - filename, lineno, ''), - self.emit(SIGNAL("set_or_edit_conditional_breakpoint()"))) - ) - actions.append(edit_breakpoint_action) - add_actions(self.popup_menu, actions) - self.popup_menu.popup(event.globalPos()) - event.accept() - -class BreakpointWidget(QWidget): - """ - Breakpoint widget - """ - VERSION = '1.0.0' - - def __init__(self, parent): - QWidget.__init__(self, parent) - - self.setWindowTitle("Breakpoints") - self.dictwidget = BreakpointTableView(self, - self._load_all_breakpoints()) - layout = QVBoxLayout() - layout.addWidget(self.dictwidget) - self.setLayout(layout) - self.connect(self.dictwidget, SIGNAL('clear_all_breakpoints()'), - lambda: self.emit(SIGNAL('clear_all_breakpoints()'))) - self.connect(self.dictwidget, SIGNAL('clear_breakpoint(QString,int)'), - lambda s1, lino: self.emit( - SIGNAL('clear_breakpoint(QString,int)'), s1, lino)) - self.connect(self.dictwidget, SIGNAL("edit_goto(QString,int,QString)"), - lambda s1, lino, s2: self.emit( - SIGNAL("edit_goto(QString,int,QString)"), s1, lino, s2)) - self.connect(self.dictwidget, SIGNAL('set_or_edit_conditional_breakpoint()'), - lambda: self.emit(SIGNAL('set_or_edit_conditional_breakpoint()'))) - - def _load_all_breakpoints(self): - bp_dict = CONF.get('run', 'breakpoints', {}) - for filename in list(bp_dict.keys()): - if not osp.isfile(filename): - bp_dict.pop(filename) - return bp_dict - - def get_data(self): - pass - - def set_data(self): - bp_dict = self._load_all_breakpoints() - self.dictwidget.model.set_data(bp_dict) - self.dictwidget.adjust_columns() - self.dictwidget.sortByColumn(0, Qt.DescendingOrder) - -def test(): - """Run breakpoint widget test""" - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - widget = BreakpointWidget(None) - widget.show() - sys.exit(app.exec_()) - -if __name__ == '__main__': - test() diff -Nru spyder-2.3.8+dfsg1/spyderplugins/widgets/__init__.py spyder-3.0.2+dfsg1/spyderplugins/widgets/__init__.py --- spyder-2.3.8+dfsg1/spyderplugins/widgets/__init__.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/widgets/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.widgets -================= - -Widgets defined in this module may be used in any other Qt-based application - -They are also used in Spyder through the Plugin interface -(see spyderlib.plugins) -""" diff -Nru spyder-2.3.8+dfsg1/spyderplugins/widgets/profilergui.py spyder-3.0.2+dfsg1/spyderplugins/widgets/profilergui.py --- spyder-2.3.8+dfsg1/spyderplugins/widgets/profilergui.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/widgets/profilergui.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,543 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2011 Santiago Jaramillo -# based on pylintgui.py by Pierre Raybaut -# -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -Profiler widget - -See the official documentation on python profiling: -http://docs.python.org/library/profile.html - -Questions for Pierre and others: - - Where in the menu should profiler go? Run > Profile code ? -""" - -from __future__ import with_statement - -from spyderlib.qt.QtGui import (QHBoxLayout, QWidget, QMessageBox, QVBoxLayout, - QLabel, QTreeWidget, QTreeWidgetItem, - QApplication) -from spyderlib.qt.QtCore import SIGNAL, QProcess, QByteArray, Qt, QTextCodec -locale_codec = QTextCodec.codecForLocale() -from spyderlib.qt.compat import getopenfilename - -import sys -import os -import os.path as osp -import time - -# Local imports -from spyderlib.utils.qthelpers import (create_toolbutton, get_item_user_text, - set_item_user_text, get_icon) -from spyderlib.utils.programs import shell_split -from spyderlib.baseconfig import get_conf_path, get_translation -from spyderlib.widgets.texteditor import TextEditor -from spyderlib.widgets.comboboxes import PythonModulesComboBox -from spyderlib.widgets.externalshell import baseshell -from spyderlib.py3compat import to_text_string, getcwd -_ = get_translation("p_profiler", dirname="spyderplugins") - - -def is_profiler_installed(): - from spyderlib.utils.programs import is_module_installed - return is_module_installed('cProfile') and is_module_installed('pstats') - - -class ProfilerWidget(QWidget): - """ - Profiler widget - """ - DATAPATH = get_conf_path('profiler.results') - VERSION = '0.0.1' - - def __init__(self, parent, max_entries=100): - QWidget.__init__(self, parent) - - self.setWindowTitle("Profiler") - - self.output = None - self.error_output = None - - self._last_wdir = None - self._last_args = None - self._last_pythonpath = None - - self.filecombo = PythonModulesComboBox(self) - - self.start_button = create_toolbutton(self, icon=get_icon('run.png'), - text=_("Profile"), - tip=_("Run profiler"), - triggered=self.start, text_beside_icon=True) - self.stop_button = create_toolbutton(self, - icon=get_icon('stop.png'), - text=_("Stop"), - tip=_("Stop current profiling"), - text_beside_icon=True) - self.connect(self.filecombo, SIGNAL('valid(bool)'), - self.start_button.setEnabled) - #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) - # FIXME: The combobox emits this signal on almost any event - # triggering show_data() too early, too often. - - browse_button = create_toolbutton(self, icon=get_icon('fileopen.png'), - tip=_('Select Python script'), - triggered=self.select_file) - - self.datelabel = QLabel() - - self.log_button = create_toolbutton(self, icon=get_icon('log.png'), - text=_("Output"), - text_beside_icon=True, - tip=_("Show program's output"), - triggered=self.show_log) - - self.datatree = ProfilerDataTree(self) - - self.collapse_button = create_toolbutton(self, - icon=get_icon('collapse.png'), - triggered=lambda dD=-1: - self.datatree.change_view(dD), - tip=_('Collapse one level up')) - self.expand_button = create_toolbutton(self, - icon=get_icon('expand.png'), - triggered=lambda dD=1: - self.datatree.change_view(dD), - tip=_('Expand one level down')) - - hlayout1 = QHBoxLayout() - hlayout1.addWidget(self.filecombo) - hlayout1.addWidget(browse_button) - hlayout1.addWidget(self.start_button) - hlayout1.addWidget(self.stop_button) - - hlayout2 = QHBoxLayout() - hlayout2.addWidget(self.collapse_button) - hlayout2.addWidget(self.expand_button) - hlayout2.addStretch() - hlayout2.addWidget(self.datelabel) - hlayout2.addStretch() - hlayout2.addWidget(self.log_button) - - layout = QVBoxLayout() - layout.addLayout(hlayout1) - layout.addLayout(hlayout2) - layout.addWidget(self.datatree) - self.setLayout(layout) - - self.process = None - self.set_running_state(False) - self.start_button.setEnabled(False) - - if not is_profiler_installed(): - # This should happen only on certain GNU/Linux distributions - # or when this a home-made Python build because the Python - # profilers are included in the Python standard library - for widget in (self.datatree, self.filecombo, - self.start_button, self.stop_button): - widget.setDisabled(True) - url = 'http://docs.python.org/library/profile.html' - text = '%s %s' % (_('Please install'), url, - _("the Python profiler modules")) - self.datelabel.setText(text) - else: - pass # self.show_data() - - def analyze(self, filename, wdir=None, args=None, pythonpath=None): - if not is_profiler_installed(): - return - self.kill_if_running() - #index, _data = self.get_data(filename) - index = None # FIXME: storing data is not implemented yet - if index is None: - self.filecombo.addItem(filename) - self.filecombo.setCurrentIndex(self.filecombo.count()-1) - else: - self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) - self.filecombo.selected() - if self.filecombo.is_valid(): - if wdir is None: - wdir = osp.dirname(filename) - self.start(wdir, args, pythonpath) - - def select_file(self): - self.emit(SIGNAL('redirect_stdio(bool)'), False) - filename, _selfilter = getopenfilename(self, _("Select Python script"), - getcwd(), _("Python scripts")+" (*.py ; *.pyw)") - self.emit(SIGNAL('redirect_stdio(bool)'), False) - if filename: - self.analyze(filename) - - def show_log(self): - if self.output: - TextEditor(self.output, title=_("Profiler output"), - readonly=True, size=(700, 500)).exec_() - - def show_errorlog(self): - if self.error_output: - TextEditor(self.error_output, title=_("Profiler output"), - readonly=True, size=(700, 500)).exec_() - - def start(self, wdir=None, args=None, pythonpath=None): - filename = to_text_string(self.filecombo.currentText()) - if wdir is None: - wdir = self._last_wdir - if wdir is None: - wdir = osp.basename(filename) - if args is None: - args = self._last_args - if args is None: - args = [] - if pythonpath is None: - pythonpath = self._last_pythonpath - self._last_wdir = wdir - self._last_args = args - self._last_pythonpath = pythonpath - - self.datelabel.setText(_('Profiling, please wait...')) - - self.process = QProcess(self) - self.process.setProcessChannelMode(QProcess.SeparateChannels) - self.process.setWorkingDirectory(wdir) - self.connect(self.process, SIGNAL("readyReadStandardOutput()"), - self.read_output) - self.connect(self.process, SIGNAL("readyReadStandardError()"), - lambda: self.read_output(error=True)) - self.connect(self.process, - SIGNAL("finished(int,QProcess::ExitStatus)"), - self.finished) - self.connect(self.stop_button, SIGNAL("clicked()"), self.process.kill) - - if pythonpath is not None: - env = [to_text_string(_pth) - for _pth in self.process.systemEnvironment()] - baseshell.add_pathlist_to_PYTHONPATH(env, pythonpath) - self.process.setEnvironment(env) - - self.output = '' - self.error_output = '' - - p_args = ['-m', 'cProfile', '-o', self.DATAPATH] - if os.name == 'nt': - # On Windows, one has to replace backslashes by slashes to avoid - # confusion with escape characters (otherwise, for example, '\t' - # will be interpreted as a tabulation): - p_args.append(osp.normpath(filename).replace(os.sep, '/')) - else: - p_args.append(filename) - if args: - p_args.extend(shell_split(args)) - executable = sys.executable - if executable.endswith("spyder.exe"): - # py2exe distribution - executable = "python.exe" - self.process.start(executable, p_args) - - running = self.process.waitForStarted() - self.set_running_state(running) - if not running: - QMessageBox.critical(self, _("Error"), - _("Process failed to start")) - - def set_running_state(self, state=True): - self.start_button.setEnabled(not state) - self.stop_button.setEnabled(state) - - def read_output(self, error=False): - if error: - self.process.setReadChannel(QProcess.StandardError) - else: - self.process.setReadChannel(QProcess.StandardOutput) - qba = QByteArray() - while self.process.bytesAvailable(): - if error: - qba += self.process.readAllStandardError() - else: - qba += self.process.readAllStandardOutput() - text = to_text_string( locale_codec.toUnicode(qba.data()) ) - if error: - self.error_output += text - else: - self.output += text - - def finished(self): - self.set_running_state(False) - self.show_errorlog() # If errors occurred, show them. - self.output = self.error_output + self.output - # FIXME: figure out if show_data should be called here or - # as a signal from the combobox - self.show_data(justanalyzed=True) - - def kill_if_running(self): - if self.process is not None: - if self.process.state() == QProcess.Running: - self.process.kill() - self.process.waitForFinished() - - def show_data(self, justanalyzed=False): - if not justanalyzed: - self.output = None - self.log_button.setEnabled(self.output is not None \ - and len(self.output) > 0) - self.kill_if_running() - filename = to_text_string(self.filecombo.currentText()) - if not filename: - return - - self.datatree.load_data(self.DATAPATH) - self.datelabel.setText(_('Sorting data, please wait...')) - QApplication.processEvents() - self.datatree.show_tree() - - text_style = "%s " - date_text = text_style % time.strftime("%d %b %Y %H:%M", - time.localtime()) - self.datelabel.setText(date_text) - - -class TreeWidgetItem( QTreeWidgetItem ): - def __init__(self, parent=None): - QTreeWidgetItem.__init__(self, parent) - - def __lt__(self, otherItem): - column = self.treeWidget().sortColumn() - try: - return float( self.text(column) ) > float( otherItem.text(column) ) - except ValueError: - return self.text(column) > otherItem.text(column) - - -class ProfilerDataTree(QTreeWidget): - """ - Convenience tree widget (with built-in model) - to store and view profiler data. - - The quantities calculated by the profiler are as follows - (from profile.Profile): - [0] = The number of times this function was called, not counting direct - or indirect recursion, - [1] = Number of times this function appears on the stack, minus one - [2] = Total time spent internal to this function - [3] = Cumulative time that this function was present on the stack. In - non-recursive functions, this is the total execution time from start - to finish of each invocation of a function, including time spent in - all subfunctions. - [4] = A dictionary indicating for each function name, the number of times - it was called by us. - """ - SEP = r"<[=]>" # separator between filename and linenumber - # (must be improbable as a filename to avoid splitting the filename itself) - def __init__(self, parent=None): - QTreeWidget.__init__(self, parent) - self.header_list = [_('Function/Module'), _('Total Time'), - _('Local Time'), _('Calls'), _('File:line')] - self.icon_list = {'module': 'python.png', - 'function': 'function.png', - 'builtin': 'python_t.png', - 'constructor': 'class.png'} - self.profdata = None # To be filled by self.load_data() - self.stats = None # To be filled by self.load_data() - self.item_depth = None - self.item_list = None - self.items_to_be_shown = None - self.current_view_depth = None - self.setColumnCount(len(self.header_list)) - self.setHeaderLabels(self.header_list) - self.initialize_view() - self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*,int)'), - self.item_activated) - self.connect(self, SIGNAL('itemExpanded(QTreeWidgetItem*)'), - self.item_expanded) - - def set_item_data(self, item, filename, line_number): - """Set tree item user data: filename (string) and line_number (int)""" - set_item_user_text(item, '%s%s%d' % (filename, self.SEP, line_number)) - - def get_item_data(self, item): - """Get tree item user data: (filename, line_number)""" - filename, line_number_str = get_item_user_text(item).split(self.SEP) - return filename, int(line_number_str) - - def initialize_view(self): - """Clean the tree and view parameters""" - self.clear() - self.item_depth = 0 # To be use for collapsing/expanding one level - self.item_list = [] # To be use for collapsing/expanding one level - self.items_to_be_shown = {} - self.current_view_depth = 0 - - def load_data(self, profdatafile): - """Load profiler data saved by profile/cProfile module""" - import pstats - self.profdata = pstats.Stats(profdatafile) - self.profdata.calc_callees() - self.stats = self.profdata.stats - - def find_root(self): - """Find a function without a caller""" - self.profdata.sort_stats("cumulative") - for func in self.profdata.fcn_list: - if ('~', 0, '') != func: - # This skips the profiler function at the top of the list - # it does only occur in Python 3 - return func - - def find_callees(self, parent): - """Find all functions called by (parent) function.""" - # FIXME: This implementation is very inneficient, because it - # traverses all the data to find children nodes (callees) - return self.profdata.all_callees[parent] - - def show_tree(self): - """Populate the tree with profiler data and display it.""" - self.initialize_view() # Clear before re-populating - self.setItemsExpandable(True) - self.setSortingEnabled(False) - rootkey = self.find_root() # This root contains profiler overhead - if rootkey: - self.populate_tree(self, self.find_callees(rootkey)) - self.resizeColumnToContents(0) - self.setSortingEnabled(True) - self.sortItems(1, Qt.DescendingOrder) # FIXME: hardcoded index - self.change_view(1) - - def function_info(self, functionKey): - """Returns processed information about the function's name and file.""" - node_type = 'function' - filename, line_number, function_name = functionKey - if function_name == '': - modulePath, moduleName = osp.split(filename) - node_type = 'module' - if moduleName == '__init__.py': - modulePath, moduleName = osp.split(modulePath) - function_name = '<' + moduleName + '>' - if not filename or filename == '~': - file_and_line = '(built-in)' - node_type = 'builtin' - else: - if function_name == '__init__': - node_type = 'constructor' - file_and_line = '%s : %d' % (filename, line_number) - return filename, line_number, function_name, file_and_line, node_type - - def populate_tree(self, parentItem, children_list): - """Recursive method to create each item (and associated data) in the tree.""" - for child_key in children_list: - self.item_depth += 1 - (filename, line_number, function_name, file_and_line, node_type - ) = self.function_info(child_key) - (primcalls, total_calls, loc_time, cum_time, callers - ) = self.stats[child_key] - child_item = TreeWidgetItem(parentItem) - self.item_list.append(child_item) - self.set_item_data(child_item, filename, line_number) - - # FIXME: indexes to data should be defined by a dictionary on init - child_item.setToolTip(0, 'Function or module name') - child_item.setData(0, Qt.DisplayRole, function_name) - child_item.setIcon(0, get_icon(self.icon_list[node_type])) - - child_item.setToolTip(1, _('Time in function '\ - '(including sub-functions)')) - #child_item.setData(1, Qt.DisplayRole, cum_time) - child_item.setData(1, Qt.DisplayRole, '%.3f' % cum_time) - child_item.setTextAlignment(1, Qt.AlignCenter) - - child_item.setToolTip(2, _('Local time in function '\ - '(not in sub-functions)')) - #child_item.setData(2, Qt.DisplayRole, loc_time) - child_item.setData(2, Qt.DisplayRole, '%.3f' % loc_time) - child_item.setTextAlignment(2, Qt.AlignCenter) - - child_item.setToolTip(3, _('Total number of calls '\ - '(including recursion)')) - child_item.setData(3, Qt.DisplayRole, total_calls) - child_item.setTextAlignment(3, Qt.AlignCenter) - - child_item.setToolTip(4, _('File:line '\ - 'where function is defined')) - child_item.setData(4, Qt.DisplayRole, file_and_line) - #child_item.setExpanded(True) - if self.is_recursive(child_item): - child_item.setData(4, Qt.DisplayRole, '(%s)' % _('recursion')) - child_item.setDisabled(True) - else: - callees = self.find_callees(child_key) - if self.item_depth < 3: - self.populate_tree(child_item, callees) - elif callees: - child_item.setChildIndicatorPolicy(child_item.ShowIndicator) - self.items_to_be_shown[id(child_item)] = callees - self.item_depth -= 1 - - def item_activated(self, item): - filename, line_number = self.get_item_data(item) - self.parent().emit(SIGNAL("edit_goto(QString,int,QString)"), - filename, line_number, '') - - def item_expanded(self, item): - if item.childCount() == 0 and id(item) in self.items_to_be_shown: - callees = self.items_to_be_shown[id(item)] - self.populate_tree(item, callees) - - def is_recursive(self, child_item): - """Returns True is a function is a descendant of itself.""" - ancestor = child_item.parent() - # FIXME: indexes to data should be defined by a dictionary on init - while ancestor: - if (child_item.data(0, Qt.DisplayRole - ) == ancestor.data(0, Qt.DisplayRole) and - child_item.data(4, Qt.DisplayRole - ) == ancestor.data(4, Qt.DisplayRole)): - return True - else: - ancestor = ancestor.parent() - return False - - def get_top_level_items(self): - """Iterate over top level items""" - return [self.topLevelItem(_i) for _i in range(self.topLevelItemCount())] - - def get_items(self, maxlevel): - """Return items (excluding top level items)""" - itemlist = [] - def add_to_itemlist(item, maxlevel, level=1): - level += 1 - for index in range(item.childCount()): - citem = item.child(index) - itemlist.append(citem) - if level <= maxlevel: - add_to_itemlist(citem, maxlevel, level) - for tlitem in self.get_top_level_items(): - itemlist.append(tlitem) - if maxlevel > 1: - add_to_itemlist(tlitem, maxlevel=maxlevel) - return itemlist - - def change_view(self, change_in_depth): - """Change the view depth by expand or collapsing all same-level nodes""" - self.current_view_depth += change_in_depth - if self.current_view_depth < 1: - self.current_view_depth = 1 - self.collapseAll() - for item in self.get_items(maxlevel=self.current_view_depth): - item.setExpanded(True) - - -def test(): - """Run widget test""" - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - widget = ProfilerWidget(None) - widget.resize(800, 600) - widget.show() - #widget.analyze(__file__) - widget.analyze(osp.join(osp.dirname(__file__), os.pardir, os.pardir, - 'spyderlib/widgets', 'texteditor.py')) - sys.exit(app.exec_()) - -if __name__ == '__main__': - test() diff -Nru spyder-2.3.8+dfsg1/spyderplugins/widgets/pylintgui.py spyder-3.0.2+dfsg1/spyderplugins/widgets/pylintgui.py --- spyder-2.3.8+dfsg1/spyderplugins/widgets/pylintgui.py 2015-11-27 13:29:56.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyderplugins/widgets/pylintgui.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,499 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009-2010 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -"""Pylint widget""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# pylint: disable=R0911 -# pylint: disable=R0201 - -from __future__ import with_statement, print_function - -from spyderlib.qt.QtGui import (QHBoxLayout, QWidget, QTreeWidgetItem, - QMessageBox, QVBoxLayout, QLabel) -from spyderlib.qt.QtCore import SIGNAL, QProcess, QByteArray, QTextCodec -locale_codec = QTextCodec.codecForLocale() -from spyderlib.qt.compat import getopenfilename - -import sys -import os -import os.path as osp -import time -import re -import subprocess - -# Local imports -from spyderlib import dependencies -from spyderlib.utils import programs -from spyderlib.utils.encoding import to_unicode_from_fs -from spyderlib.utils.qthelpers import get_icon, create_toolbutton -from spyderlib.baseconfig import get_conf_path, get_translation -from spyderlib.widgets.onecolumntree import OneColumnTree -from spyderlib.widgets.texteditor import TextEditor -from spyderlib.widgets.comboboxes import (PythonModulesComboBox, - is_module_or_package) -from spyderlib.py3compat import PY3, to_text_string, getcwd, pickle -_ = get_translation("p_pylint", dirname="spyderplugins") - - -PYLINT = 'pylint' -if PY3: - if programs.find_program('pylint3'): - PYLINT = 'pylint3' - elif programs.find_program('python3-pylint'): - PYLINT = 'python3-pylint' - -PYLINT_PATH = programs.find_program(PYLINT) - - -def get_pylint_version(): - """Return pylint version""" - global PYLINT_PATH - if PYLINT_PATH is None: - return - process = subprocess.Popen([PYLINT, '--version'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - cwd=osp.dirname(PYLINT_PATH), - shell=True if os.name == 'nt' else False) - lines = to_unicode_from_fs(process.stdout.read()).splitlines() - if lines: - regex = '({0}*|pylint-script.py) ([0-9\.]*)'.format(PYLINT) - match = re.match(regex, lines[0]) - if match is not None: - return match.groups()[1] - - -PYLINT_REQVER = '>=0.25' -PYLINT_VER = get_pylint_version() -dependencies.add("pylint", _("Static code analysis"), - required_version=PYLINT_REQVER, installed_version=PYLINT_VER) - - -#TODO: display results on 3 columns instead of 1: msg_id, lineno, message -class ResultsTree(OneColumnTree): - def __init__(self, parent): - OneColumnTree.__init__(self, parent) - self.filename = None - self.results = None - self.data = None - self.set_title('') - - def activated(self, item): - """Double-click event""" - data = self.data.get(id(item)) - if data is not None: - fname, lineno = data - self.parent().emit(SIGNAL("edit_goto(QString,int,QString)"), - fname, lineno, '') - - def clicked(self, item): - """Click event""" - self.activated(item) - - def clear_results(self): - self.clear() - self.set_title('') - - def set_results(self, filename, results): - self.filename = filename - self.results = results - self.refresh() - - def refresh(self): - title = _('Results for ')+self.filename - self.set_title(title) - self.clear() - self.data = {} - # Populating tree - results = ((_('Convention'), - get_icon('convention.png'), self.results['C:']), - (_('Refactor'), - get_icon('refactor.png'), self.results['R:']), - (_('Warning'), - get_icon('warning.png'), self.results['W:']), - (_('Error'), - get_icon('error.png'), self.results['E:'])) - for title, icon, messages in results: - title += ' (%d message%s)' % (len(messages), - 's' if len(messages)>1 else '') - title_item = QTreeWidgetItem(self, [title], QTreeWidgetItem.Type) - title_item.setIcon(0, icon) - if not messages: - title_item.setDisabled(True) - modules = {} - for module, lineno, message, msg_id in messages: - basename = osp.splitext(osp.basename(self.filename))[0] - if not module.startswith(basename): - # Pylint bug - i_base = module.find(basename) - module = module[i_base:] - dirname = osp.dirname(self.filename) - if module.startswith('.') or module == basename: - modname = osp.join(dirname, module) - else: - modname = osp.join(dirname, *module.split('.')) - if osp.isdir(modname): - modname = osp.join(modname, '__init__') - for ext in ('.py', '.pyw'): - if osp.isfile(modname+ext): - modname = modname + ext - break - if osp.isdir(self.filename): - parent = modules.get(modname) - if parent is None: - item = QTreeWidgetItem(title_item, [module], - QTreeWidgetItem.Type) - item.setIcon(0, get_icon('py.png')) - modules[modname] = item - parent = item - else: - parent = title_item - if len(msg_id) > 1: - text = "[%s] %d : %s" % (msg_id, lineno, message) - else: - text = "%d : %s" % (lineno, message) - msg_item = QTreeWidgetItem(parent, [text], QTreeWidgetItem.Type) - msg_item.setIcon(0, get_icon('arrow.png')) - self.data[id(msg_item)] = (modname, lineno) - - -class PylintWidget(QWidget): - """ - Pylint widget - """ - DATAPATH = get_conf_path('pylint.results') - VERSION = '1.1.0' - - def __init__(self, parent, max_entries=100): - QWidget.__init__(self, parent) - - self.setWindowTitle("Pylint") - - self.output = None - self.error_output = None - - self.max_entries = max_entries - self.rdata = [] - if osp.isfile(self.DATAPATH): - try: - data = pickle.loads(open(self.DATAPATH, 'rb').read()) - if data[0] == self.VERSION: - self.rdata = data[1:] - except (EOFError, ImportError): - pass - - self.filecombo = PythonModulesComboBox(self) - if self.rdata: - self.remove_obsolete_items() - self.filecombo.addItems(self.get_filenames()) - - self.start_button = create_toolbutton(self, icon=get_icon('run.png'), - text=_("Analyze"), - tip=_("Run analysis"), - triggered=self.start, text_beside_icon=True) - self.stop_button = create_toolbutton(self, - icon=get_icon('stop.png'), - text=_("Stop"), - tip=_("Stop current analysis"), - text_beside_icon=True) - self.connect(self.filecombo, SIGNAL('valid(bool)'), - self.start_button.setEnabled) - self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) - - browse_button = create_toolbutton(self, icon=get_icon('fileopen.png'), - tip=_('Select Python file'), - triggered=self.select_file) - - self.ratelabel = QLabel() - self.datelabel = QLabel() - self.log_button = create_toolbutton(self, icon=get_icon('log.png'), - text=_("Output"), - text_beside_icon=True, - tip=_("Complete output"), - triggered=self.show_log) - self.treewidget = ResultsTree(self) - - hlayout1 = QHBoxLayout() - hlayout1.addWidget(self.filecombo) - hlayout1.addWidget(browse_button) - hlayout1.addWidget(self.start_button) - hlayout1.addWidget(self.stop_button) - - hlayout2 = QHBoxLayout() - hlayout2.addWidget(self.ratelabel) - hlayout2.addStretch() - hlayout2.addWidget(self.datelabel) - hlayout2.addStretch() - hlayout2.addWidget(self.log_button) - - layout = QVBoxLayout() - layout.addLayout(hlayout1) - layout.addLayout(hlayout2) - layout.addWidget(self.treewidget) - self.setLayout(layout) - - self.process = None - self.set_running_state(False) - - if PYLINT_PATH is None: - for widget in (self.treewidget, self.filecombo, - self.start_button, self.stop_button): - widget.setDisabled(True) - if os.name == 'nt' \ - and programs.is_module_installed("pylint"): - # Pylint is installed but pylint script is not in PATH - # (AFAIK, could happen only on Windows) - text = _('Pylint script was not found. Please add "%s" to PATH.') - text = to_text_string(text) % osp.join(sys.prefix, "Scripts") - else: - text = _('Please install pylint:') - url = 'http://www.logilab.fr' - text += ' %s' % (url, url) - self.ratelabel.setText(text) - else: - self.show_data() - - def analyze(self, filename): - if PYLINT_PATH is None: - return - filename = to_text_string(filename) # filename is a QString instance - self.kill_if_running() - index, _data = self.get_data(filename) - if index is None: - self.filecombo.addItem(filename) - self.filecombo.setCurrentIndex(self.filecombo.count()-1) - else: - self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) - self.filecombo.selected() - if self.filecombo.is_valid(): - self.start() - - def select_file(self): - self.emit(SIGNAL('redirect_stdio(bool)'), False) - filename, _selfilter = getopenfilename(self, _("Select Python file"), - getcwd(), _("Python files")+" (*.py ; *.pyw)") - self.emit(SIGNAL('redirect_stdio(bool)'), False) - if filename: - self.analyze(filename) - - def remove_obsolete_items(self): - """Removing obsolete items""" - self.rdata = [(filename, data) for filename, data in self.rdata - if is_module_or_package(filename)] - - def get_filenames(self): - return [filename for filename, _data in self.rdata] - - def get_data(self, filename): - filename = osp.abspath(filename) - for index, (fname, data) in enumerate(self.rdata): - if fname == filename: - return index, data - else: - return None, None - - def set_data(self, filename, data): - filename = osp.abspath(filename) - index, _data = self.get_data(filename) - if index is not None: - self.rdata.pop(index) - self.rdata.insert(0, (filename, data)) - self.save() - - def save(self): - while len(self.rdata) > self.max_entries: - self.rdata.pop(-1) - pickle.dump([self.VERSION]+self.rdata, open(self.DATAPATH, 'wb'), 2) - - def show_log(self): - if self.output: - TextEditor(self.output, title=_("Pylint output"), - readonly=True, size=(700, 500)).exec_() - - def start(self): - filename = to_text_string(self.filecombo.currentText()) - - self.process = QProcess(self) - self.process.setProcessChannelMode(QProcess.SeparateChannels) - self.process.setWorkingDirectory(osp.dirname(filename)) - self.connect(self.process, SIGNAL("readyReadStandardOutput()"), - self.read_output) - self.connect(self.process, SIGNAL("readyReadStandardError()"), - lambda: self.read_output(error=True)) - self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), - self.finished) - self.connect(self.stop_button, SIGNAL("clicked()"), - self.process.kill) - - self.output = '' - self.error_output = '' - - plver = PYLINT_VER - if plver is not None: - if plver.split('.')[0] == '0': - p_args = ['-i', 'yes'] - else: - # Option '-i' (alias for '--include-ids') was removed in pylint - # 1.0 - p_args = ["--msg-template='{msg_id}:{line:3d},"\ - "{column}: {obj}: {msg}"] - p_args += [osp.basename(filename)] - else: - p_args = [osp.basename(filename)] - self.process.start(PYLINT_PATH, p_args) - - running = self.process.waitForStarted() - self.set_running_state(running) - if not running: - QMessageBox.critical(self, _("Error"), - _("Process failed to start")) - - def set_running_state(self, state=True): - self.start_button.setEnabled(not state) - self.stop_button.setEnabled(state) - - def read_output(self, error=False): - if error: - self.process.setReadChannel(QProcess.StandardError) - else: - self.process.setReadChannel(QProcess.StandardOutput) - qba = QByteArray() - while self.process.bytesAvailable(): - if error: - qba += self.process.readAllStandardError() - else: - qba += self.process.readAllStandardOutput() - text = to_text_string( locale_codec.toUnicode(qba.data()) ) - if error: - self.error_output += text - else: - self.output += text - - def finished(self): - self.set_running_state(False) - if not self.output: - if self.error_output: - QMessageBox.critical(self, _("Error"), self.error_output) - print("pylint error:\n\n" + self.error_output, file=sys.stderr) - return - - # Convention, Refactor, Warning, Error - results = {'C:': [], 'R:': [], 'W:': [], 'E:': []} - txt_module = '************* Module ' - - module = '' # Should not be needed - just in case something goes wrong - for line in self.output.splitlines(): - if line.startswith(txt_module): - # New module - module = line[len(txt_module):] - continue - # Supporting option include-ids: ('R3873:' instead of 'R:') - if not re.match('^[CRWE]+([0-9]{4})?:', line): - continue - i1 = line.find(':') - if i1 == -1: - continue - msg_id = line[:i1] - i2 = line.find(':', i1+1) - if i2 == -1: - continue - line_nb = line[i1+1:i2].strip() - if not line_nb: - continue - line_nb = int(line_nb.split(',')[0]) - message = line[i2+1:] - item = (module, line_nb, message, msg_id) - results[line[0]+':'].append(item) - - # Rate - rate = None - txt_rate = 'Your code has been rated at ' - i_rate = self.output.find(txt_rate) - if i_rate > 0: - i_rate_end = self.output.find('/10', i_rate) - if i_rate_end > 0: - rate = self.output[i_rate+len(txt_rate):i_rate_end] - - # Previous run - previous = '' - if rate is not None: - txt_prun = 'previous run: ' - i_prun = self.output.find(txt_prun, i_rate_end) - if i_prun > 0: - i_prun_end = self.output.find('/10', i_prun) - previous = self.output[i_prun+len(txt_prun):i_prun_end] - - - filename = to_text_string(self.filecombo.currentText()) - self.set_data(filename, (time.localtime(), rate, previous, results)) - self.output = self.error_output + self.output - self.show_data(justanalyzed=True) - - def kill_if_running(self): - if self.process is not None: - if self.process.state() == QProcess.Running: - self.process.kill() - self.process.waitForFinished() - - def show_data(self, justanalyzed=False): - if not justanalyzed: - self.output = None - self.log_button.setEnabled(self.output is not None \ - and len(self.output) > 0) - self.kill_if_running() - filename = to_text_string(self.filecombo.currentText()) - if not filename: - return - - _index, data = self.get_data(filename) - if data is None: - text = _('Source code has not been rated yet.') - self.treewidget.clear_results() - date_text = '' - else: - datetime, rate, previous_rate, results = data - if rate is None: - text = _('Analysis did not succeed ' - '(see output for more details).') - self.treewidget.clear_results() - date_text = '' - else: - text_style = "%s " - rate_style = "%s" - prevrate_style = "%s" - color = "#FF0000" - if float(rate) > 5.: - color = "#22AA22" - elif float(rate) > 3.: - color = "#EE5500" - text = _('Global evaluation:') - text = (text_style % text)+(rate_style % (color, - ('%s/10' % rate))) - if previous_rate: - text_prun = _('previous run:') - text_prun = ' (%s %s/10)' % (text_prun, previous_rate) - text += prevrate_style % text_prun - self.treewidget.set_results(filename, results) - date = to_text_string(time.strftime("%d %b %Y %H:%M", datetime), - encoding='utf8') - date_text = text_style % date - - self.ratelabel.setText(text) - self.datelabel.setText(date_text) - - -def test(): - """Run pylint widget test""" - from spyderlib.utils.qthelpers import qapplication - app = qapplication() - widget = PylintWidget(None) - widget.show() - widget.analyze(__file__) - sys.exit(app.exec_()) - -if __name__ == '__main__': - test() Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_profiler/images/profiler.png and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_profiler/images/profiler.png differ diff -Nru spyder-2.3.8+dfsg1/spyder_profiler/__init__.py spyder-3.0.2+dfsg1/spyder_profiler/__init__.py --- spyder-2.3.8+dfsg1/spyder_profiler/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_profiler/__init__.py 2016-10-25 00:05:23.000000000 +0000 @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- + +#============================================================================== +# The following statement is required to register this 3rd party plugin: +#============================================================================== +from .profiler import Profiler as PLUGIN_CLASS Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_profiler/locale/es/LC_MESSAGES/profiler.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_profiler/locale/es/LC_MESSAGES/profiler.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_profiler/locale/es/LC_MESSAGES/profiler.po spyder-3.0.2+dfsg1/spyder_profiler/locale/es/LC_MESSAGES/profiler.po --- spyder-2.3.8+dfsg1/spyder_profiler/locale/es/LC_MESSAGES/profiler.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_profiler/locale/es/LC_MESSAGES/profiler.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,188 @@ +# -*- coding: utf-8 -*- +# Spyder Profiler's spanish translation file +# Copyright (C) 2011 Spyder Development team +# +msgid "" +msgstr "" +"Project-Id-Version: 2.1\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2012-06-25 21:59-0500\n" +"Last-Translator: Carlos Cordoba \n" +"Language-Team: Python\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Poedit-Language: Spanish\n" +"X-Poedit-SourceCharset: utf-8\n" +"X-Poedit-Basepath: ../../../../\n" + +#: spyder_profiler/profiler.py:33 +msgid "Results" +msgstr "Resultados" + +#: spyder_profiler/profiler.py:34 +msgid "" +"Profiler plugin results (the output of python's profile/cProfile)\n" +"are stored here:" +msgstr "" +"Los resultados del perfilador (es decir la salida de profile o cProfile)\n" +"son guardados aquí:" + +#: spyder_profiler/profiler.py:75 +msgid "Profiler" +msgstr "Perfilador (Profiler)" + +#: spyder_profiler/profiler.py:104 spyder_profiler/widgets/profilergui.py:81 +msgid "Profile" +msgstr "Perfilar (Profile)" + +#: spyder_profiler/widgets/profilergui.py:82 +msgid "Run profiler" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:87 +msgid "Stop" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:88 +msgid "Stop current profiling" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:96 +#: spyder_profiler/widgets/profilergui.py:219 +msgid "Select Python script" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:102 +msgid "Output" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:104 +msgid "Show program's output" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:113 +msgid "Collapse one level up" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:118 +msgid "Expand one level down" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:121 +msgid "Save data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:124 +msgid "Save profiling data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:126 +msgid "Load data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:129 +msgid "Load profiling data for comparison" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:131 +msgid "Clear comparison" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:171 +msgid "Please install" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:172 +msgid "the Python profiler modules" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:179 +msgid "Save profiler result" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:182 +#: spyder_profiler/widgets/profilergui.py:188 +#, fuzzy +msgid "Profiler result" +msgstr "Perfilador (Profiler)" + +#: spyder_profiler/widgets/profilergui.py:187 +msgid "Select script to compare" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:220 +msgid "Python scripts" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:227 +#: spyder_profiler/widgets/profilergui.py:232 +msgid "Profiler output" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:251 +msgid "Profiling, please wait..." +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:295 +msgid "Error" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:296 +msgid "Process failed to start" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:343 +msgid "Sorting data, please wait..." +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Function/Module" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Total Time" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:389 +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Diff" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Calls" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Local Time" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:391 +msgid "File:line" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:537 +msgid "Function or module name" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:541 +msgid "Time in function (including sub-functions)" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:550 +msgid "Local time in function (not in sub-functions)" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:560 +msgid "Total number of calls (including recursion)" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:570 +msgid "File:line where function is defined" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:575 +msgid "recursion" +msgstr "" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_profiler/locale/fr/LC_MESSAGES/profiler.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_profiler/locale/fr/LC_MESSAGES/profiler.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_profiler/locale/fr/LC_MESSAGES/profiler.po spyder-3.0.2+dfsg1/spyder_profiler/locale/fr/LC_MESSAGES/profiler.po --- spyder-2.3.8+dfsg1/spyder_profiler/locale/fr/LC_MESSAGES/profiler.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_profiler/locale/fr/LC_MESSAGES/profiler.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# Spyder Profiler's french translation file +# Copyright (C) 2011 Pierre Raybaut +# +msgid "" +msgstr "" +"Project-Id-Version: 2.1\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2011-04-11 21:41+2\n" +"Last-Translator: Pierre Raybaut\n" +"Language-Team: Python\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: utf-8\n" +"Generated-By: pygettext.py 1.5\n" + +#: spyder_profiler/profiler.py:33 +msgid "Results" +msgstr "Résultats" + +#: spyder_profiler/profiler.py:34 +msgid "" +"Profiler plugin results (the output of python's profile/cProfile)\n" +"are stored here:" +msgstr "Les résultats du profileur Python sont stockés ici :" + +#: spyder_profiler/profiler.py:75 +msgid "Profiler" +msgstr "Profileur" + +#: spyder_profiler/profiler.py:104 spyder_profiler/widgets/profilergui.py:81 +msgid "Profile" +msgstr "Profiler" + +#: spyder_profiler/widgets/profilergui.py:82 +msgid "Run profiler" +msgstr "Exécuter le profileur" + +#: spyder_profiler/widgets/profilergui.py:87 +msgid "Stop" +msgstr "Arrêter" + +#: spyder_profiler/widgets/profilergui.py:88 +msgid "Stop current profiling" +msgstr "Arrêter le processus en cours" + +#: spyder_profiler/widgets/profilergui.py:96 +#: spyder_profiler/widgets/profilergui.py:219 +msgid "Select Python script" +msgstr "Sélectionner un script Python" + +#: spyder_profiler/widgets/profilergui.py:102 +msgid "Output" +msgstr "Sortie" + +#: spyder_profiler/widgets/profilergui.py:104 +msgid "Show program's output" +msgstr "Afficher la sortie du programme" + +#: spyder_profiler/widgets/profilergui.py:113 +msgid "Collapse one level up" +msgstr "Replier l'arbre d'un niveau" + +#: spyder_profiler/widgets/profilergui.py:118 +msgid "Expand one level down" +msgstr "Déplier l'arbre d'un niveau" + +#: spyder_profiler/widgets/profilergui.py:121 +msgid "Save data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:124 +msgid "Save profiling data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:126 +msgid "Load data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:129 +msgid "Load profiling data for comparison" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:131 +msgid "Clear comparison" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:171 +msgid "Please install" +msgstr "Veuillez installer" + +#: spyder_profiler/widgets/profilergui.py:172 +msgid "the Python profiler modules" +msgstr "les profileurs Python" + +#: spyder_profiler/widgets/profilergui.py:179 +msgid "Save profiler result" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:182 +#: spyder_profiler/widgets/profilergui.py:188 +#, fuzzy +msgid "Profiler result" +msgstr "Sortie du profileur" + +#: spyder_profiler/widgets/profilergui.py:187 +msgid "Select script to compare" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:220 +msgid "Python scripts" +msgstr "Scripts Python" + +#: spyder_profiler/widgets/profilergui.py:227 +#: spyder_profiler/widgets/profilergui.py:232 +msgid "Profiler output" +msgstr "Sortie du profileur" + +#: spyder_profiler/widgets/profilergui.py:251 +msgid "Profiling, please wait..." +msgstr "Profilage en cours..." + +#: spyder_profiler/widgets/profilergui.py:295 +msgid "Error" +msgstr "Erreur" + +#: spyder_profiler/widgets/profilergui.py:296 +msgid "Process failed to start" +msgstr "Le processus n'a pas pu démarrer" + +#: spyder_profiler/widgets/profilergui.py:343 +msgid "Sorting data, please wait..." +msgstr "Classement des résultats en cours..." + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Function/Module" +msgstr "Fonction/Module" + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Total Time" +msgstr "Durée totale" + +#: spyder_profiler/widgets/profilergui.py:389 +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Diff" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Calls" +msgstr "Appels" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Local Time" +msgstr "Durée locale" + +#: spyder_profiler/widgets/profilergui.py:391 +msgid "File:line" +msgstr "Fichier:ligne" + +#: spyder_profiler/widgets/profilergui.py:537 +#, fuzzy +msgid "Function or module name" +msgstr "Fonction/Module" + +#: spyder_profiler/widgets/profilergui.py:541 +msgid "Time in function (including sub-functions)" +msgstr "Temps écoulé dans la fonction (sous-fonctions comprises)" + +#: spyder_profiler/widgets/profilergui.py:550 +msgid "Local time in function (not in sub-functions)" +msgstr "Temps écoulé dans la fonction (hors sous-fonctions)" + +#: spyder_profiler/widgets/profilergui.py:560 +msgid "Total number of calls (including recursion)" +msgstr "Nombre total d'appels (récursion comprise)" + +#: spyder_profiler/widgets/profilergui.py:570 +msgid "File:line where function is defined" +msgstr "Fichier:ligne de définition de la fonction" + +#: spyder_profiler/widgets/profilergui.py:575 +msgid "recursion" +msgstr "récursion" diff -Nru spyder-2.3.8+dfsg1/spyder_profiler/locale/profiler.pot spyder-3.0.2+dfsg1/spyder_profiler/locale/profiler.pot --- spyder-2.3.8+dfsg1/spyder_profiler/locale/profiler.pot 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_profiler/locale/profiler.pot 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,183 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: spyder_profiler/profiler.py:33 +msgid "Results" +msgstr "" + +#: spyder_profiler/profiler.py:34 +msgid "" +"Profiler plugin results (the output of python's profile/cProfile)\n" +"are stored here:" +msgstr "" + +#: spyder_profiler/profiler.py:75 +msgid "Profiler" +msgstr "" + +#: spyder_profiler/profiler.py:104 spyder_profiler/widgets/profilergui.py:81 +msgid "Profile" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:82 +msgid "Run profiler" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:87 +msgid "Stop" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:88 +msgid "Stop current profiling" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:96 +#: spyder_profiler/widgets/profilergui.py:219 +msgid "Select Python script" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:102 +msgid "Output" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:104 +msgid "Show program's output" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:113 +msgid "Collapse one level up" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:118 +msgid "Expand one level down" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:121 +msgid "Save data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:124 +msgid "Save profiling data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:126 +msgid "Load data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:129 +msgid "Load profiling data for comparison" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:131 +msgid "Clear comparison" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:171 +msgid "Please install" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:172 +msgid "the Python profiler modules" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:179 +msgid "Save profiler result" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:182 +#: spyder_profiler/widgets/profilergui.py:188 +msgid "Profiler result" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:187 +msgid "Select script to compare" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:220 +msgid "Python scripts" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:227 +#: spyder_profiler/widgets/profilergui.py:232 +msgid "Profiler output" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:251 +msgid "Profiling, please wait..." +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:295 +msgid "Error" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:296 +msgid "Process failed to start" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:343 +msgid "Sorting data, please wait..." +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Function/Module" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Total Time" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:389 +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Diff" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Calls" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Local Time" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:391 +msgid "File:line" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:537 +msgid "Function or module name" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:541 +msgid "Time in function (including sub-functions)" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:550 +msgid "Local time in function (not in sub-functions)" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:560 +msgid "Total number of calls (including recursion)" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:570 +msgid "File:line where function is defined" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:575 +msgid "recursion" +msgstr "" + Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_profiler/locale/pt_BR/LC_MESSAGES/profiler.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_profiler/locale/pt_BR/LC_MESSAGES/profiler.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_profiler/locale/pt_BR/LC_MESSAGES/profiler.po spyder-3.0.2+dfsg1/spyder_profiler/locale/pt_BR/LC_MESSAGES/profiler.po --- spyder-2.3.8+dfsg1/spyder_profiler/locale/pt_BR/LC_MESSAGES/profiler.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_profiler/locale/pt_BR/LC_MESSAGES/profiler.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +# Spyder's brazilian portuguese translation file +# Valter Nazianzeno , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2015-02-25 22:08-0400\n" +"Last-Translator: Valter Nazianzeno \n" +"Language-Team: \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.7.4\n" + +#: spyder_profiler/profiler.py:33 +msgid "Results" +msgstr "Resultados" + +#: spyder_profiler/profiler.py:34 +msgid "" +"Profiler plugin results (the output of python's profile/cProfile)\n" +"are stored here:" +msgstr "Os resultados do plugin Profiler são armazenados aqui: " + +#: spyder_profiler/profiler.py:75 +msgid "Profiler" +msgstr "Profiler" + +#: spyder_profiler/profiler.py:104 spyder_profiler/widgets/profilergui.py:81 +msgid "Profile" +msgstr "Profiler" + +#: spyder_profiler/widgets/profilergui.py:82 +msgid "Run profiler" +msgstr "Executar Profiler" + +#: spyder_profiler/widgets/profilergui.py:87 +msgid "Stop" +msgstr "Parar" + +#: spyder_profiler/widgets/profilergui.py:88 +msgid "Stop current profiling" +msgstr "Parar processo atual" + +#: spyder_profiler/widgets/profilergui.py:96 +#: spyder_profiler/widgets/profilergui.py:219 +msgid "Select Python script" +msgstr "Selecionar script Python" + +#: spyder_profiler/widgets/profilergui.py:102 +msgid "Output" +msgstr "Saída" + +#: spyder_profiler/widgets/profilergui.py:104 +msgid "Show program's output" +msgstr "Mostrar saída do programa" + +#: spyder_profiler/widgets/profilergui.py:113 +msgid "Collapse one level up" +msgstr "Subir um nível" + +#: spyder_profiler/widgets/profilergui.py:118 +msgid "Expand one level down" +msgstr "Descer um nível" + +#: spyder_profiler/widgets/profilergui.py:121 +msgid "Save data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:124 +msgid "Save profiling data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:126 +msgid "Load data" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:129 +msgid "Load profiling data for comparison" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:131 +msgid "Clear comparison" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:171 +msgid "Please install" +msgstr "Por favor instale" + +#: spyder_profiler/widgets/profilergui.py:172 +msgid "the Python profiler modules" +msgstr "Módulos do Python Profiler" + +#: spyder_profiler/widgets/profilergui.py:179 +msgid "Save profiler result" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:182 +#: spyder_profiler/widgets/profilergui.py:188 +#, fuzzy +msgid "Profiler result" +msgstr "Saída do Profiler" + +#: spyder_profiler/widgets/profilergui.py:187 +msgid "Select script to compare" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:220 +msgid "Python scripts" +msgstr "Scripts Python" + +#: spyder_profiler/widgets/profilergui.py:227 +#: spyder_profiler/widgets/profilergui.py:232 +msgid "Profiler output" +msgstr "Saída do Profiler" + +#: spyder_profiler/widgets/profilergui.py:251 +msgid "Profiling, please wait..." +msgstr "Processando..." + +#: spyder_profiler/widgets/profilergui.py:295 +msgid "Error" +msgstr "Erro" + +#: spyder_profiler/widgets/profilergui.py:296 +msgid "Process failed to start" +msgstr "O processo falhou ao iniciar" + +#: spyder_profiler/widgets/profilergui.py:343 +msgid "Sorting data, please wait..." +msgstr "Ordenando dados..." + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Function/Module" +msgstr "Função/Módulo" + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Total Time" +msgstr "Tempo Total" + +#: spyder_profiler/widgets/profilergui.py:389 +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Diff" +msgstr "" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Calls" +msgstr "Chamadas" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Local Time" +msgstr "Hora Local" + +#: spyder_profiler/widgets/profilergui.py:391 +msgid "File:line" +msgstr "Arquivo/Linha" + +#: spyder_profiler/widgets/profilergui.py:537 +#, fuzzy +msgid "Function or module name" +msgstr "Função/Módulo" + +#: spyder_profiler/widgets/profilergui.py:541 +msgid "Time in function (including sub-functions)" +msgstr "Tempo decorrido na função (sub-funções incluídas)" + +#: spyder_profiler/widgets/profilergui.py:550 +msgid "Local time in function (not in sub-functions)" +msgstr "Tempo decorrido na função (excluindo sub-funções)" + +#: spyder_profiler/widgets/profilergui.py:560 +msgid "Total number of calls (including recursion)" +msgstr "Número total de chamadas (incluindo recursão)" + +#: spyder_profiler/widgets/profilergui.py:570 +msgid "File:line where function is defined" +msgstr "Arquivo/Linha onde a função está definida" + +#: spyder_profiler/widgets/profilergui.py:575 +msgid "recursion" +msgstr "recursão" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_profiler/locale/ru/LC_MESSAGES/profiler.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_profiler/locale/ru/LC_MESSAGES/profiler.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_profiler/locale/ru/LC_MESSAGES/profiler.po spyder-3.0.2+dfsg1/spyder_profiler/locale/ru/LC_MESSAGES/profiler.po --- spyder-2.3.8+dfsg1/spyder_profiler/locale/ru/LC_MESSAGES/profiler.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_profiler/locale/ru/LC_MESSAGES/profiler.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,187 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2016-09-21 14:46+0300\n" +"Last-Translator: Roman Kulagin \n" +"Language-Team: \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.5.4\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: spyder_profiler/profiler.py:33 +msgid "Results" +msgstr "Результаты" + +#: spyder_profiler/profiler.py:34 +msgid "" +"Profiler plugin results (the output of python's profile/cProfile)\n" +"are stored here:" +msgstr "" +"Результаты Профайлера (вывод profile/cProfile python'а)\n" +"хранятся здесь:" + +#: spyder_profiler/profiler.py:75 +msgid "Profiler" +msgstr "Профайлер" + +#: spyder_profiler/profiler.py:104 spyder_profiler/widgets/profilergui.py:81 +msgid "Profile" +msgstr "Профилировать" + +#: spyder_profiler/widgets/profilergui.py:82 +msgid "Run profiler" +msgstr "Запустить профайлер" + +#: spyder_profiler/widgets/profilergui.py:87 +msgid "Stop" +msgstr "Стоп" + +#: spyder_profiler/widgets/profilergui.py:88 +msgid "Stop current profiling" +msgstr "Остановить текущее профилирование" + +#: spyder_profiler/widgets/profilergui.py:96 +#: spyder_profiler/widgets/profilergui.py:219 +msgid "Select Python script" +msgstr "Выбрать скрипт Python" + +#: spyder_profiler/widgets/profilergui.py:102 +msgid "Output" +msgstr "Вывод" + +#: spyder_profiler/widgets/profilergui.py:104 +msgid "Show program's output" +msgstr "Показать вывод программы" + +#: spyder_profiler/widgets/profilergui.py:113 +msgid "Collapse one level up" +msgstr "Свернуть на один уровень вверх" + +#: spyder_profiler/widgets/profilergui.py:118 +msgid "Expand one level down" +msgstr "Раскрыть на один уровень вниз" + +#: spyder_profiler/widgets/profilergui.py:121 +msgid "Save data" +msgstr "Сохранить данные" + +#: spyder_profiler/widgets/profilergui.py:124 +msgid "Save profiling data" +msgstr "Сохранить данные профилирования" + +#: spyder_profiler/widgets/profilergui.py:126 +msgid "Load data" +msgstr "Загрузить данные" + +#: spyder_profiler/widgets/profilergui.py:129 +msgid "Load profiling data for comparison" +msgstr "Загрузить данные профилирования для сравнения" + +#: spyder_profiler/widgets/profilergui.py:131 +msgid "Clear comparison" +msgstr "Очистить сравнение" + +#: spyder_profiler/widgets/profilergui.py:171 +msgid "Please install" +msgstr "Пожалуйста, установите" + +#: spyder_profiler/widgets/profilergui.py:172 +msgid "the Python profiler modules" +msgstr "модули профайлера Python" + +#: spyder_profiler/widgets/profilergui.py:179 +msgid "Save profiler result" +msgstr "Сохранить результаты профилирования" + +#: spyder_profiler/widgets/profilergui.py:182 +#: spyder_profiler/widgets/profilergui.py:188 +msgid "Profiler result" +msgstr "Результаты профилирования" + +#: spyder_profiler/widgets/profilergui.py:187 +msgid "Select script to compare" +msgstr "Выберите скрипт для сравнения" + +#: spyder_profiler/widgets/profilergui.py:220 +msgid "Python scripts" +msgstr "Скрипты Python" + +#: spyder_profiler/widgets/profilergui.py:227 +#: spyder_profiler/widgets/profilergui.py:232 +msgid "Profiler output" +msgstr "Вывод профайлера" + +#: spyder_profiler/widgets/profilergui.py:251 +msgid "Profiling, please wait..." +msgstr "Профилирование, пожалуйста, подождите..." + +#: spyder_profiler/widgets/profilergui.py:295 +msgid "Error" +msgstr "Ошибка" + +#: spyder_profiler/widgets/profilergui.py:296 +msgid "Process failed to start" +msgstr "Процесс не удалось запустить" + +#: spyder_profiler/widgets/profilergui.py:343 +msgid "Sorting data, please wait..." +msgstr "Сортировка данных, пожалуйста, подождите..." + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Function/Module" +msgstr "Функция/Модуль" + +#: spyder_profiler/widgets/profilergui.py:389 +msgid "Total Time" +msgstr "Общее время" + +#: spyder_profiler/widgets/profilergui.py:389 +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Diff" +msgstr "Различие" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Calls" +msgstr "Вызовы" + +#: spyder_profiler/widgets/profilergui.py:390 +msgid "Local Time" +msgstr "Локальное время" + +#: spyder_profiler/widgets/profilergui.py:391 +msgid "File:line" +msgstr "Файл:строка" + +#: spyder_profiler/widgets/profilergui.py:537 +msgid "Function or module name" +msgstr "Функция/Модуль" + +#: spyder_profiler/widgets/profilergui.py:541 +msgid "Time in function (including sub-functions)" +msgstr "Время в функции (включая подфункции)" + +#: spyder_profiler/widgets/profilergui.py:550 +msgid "Local time in function (not in sub-functions)" +msgstr "Локальное время в функции (исключая подфункции)" + +#: spyder_profiler/widgets/profilergui.py:560 +msgid "Total number of calls (including recursion)" +msgstr "Общее число вызовов (включая рекурсию)" + +#: spyder_profiler/widgets/profilergui.py:570 +msgid "File:line where function is defined" +msgstr "Файл:строка, где функция определена" + +#: spyder_profiler/widgets/profilergui.py:575 +msgid "recursion" +msgstr "рекурсия" diff -Nru spyder-2.3.8+dfsg1/spyder_profiler/profiler.py spyder-3.0.2+dfsg1/spyder_profiler/profiler.py --- spyder-2.3.8+dfsg1/spyder_profiler/profiler.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_profiler/profiler.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,149 @@ +# -*- coding:utf-8 -*- +# +# Copyright © Spyder Project Contributors +# based on p_pylint.py by Pierre Raybaut +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Profiler Plugin.""" + +# Standard library imports +import os.path as osp + +# Third party imports +from qtpy.QtCore import Qt, Signal +from qtpy.QtWidgets import QGroupBox, QLabel, QVBoxLayout + +# Local imports +from spyder.config.base import get_translation +from spyder.plugins import SpyderPluginMixin +from spyder.plugins.configdialog import PluginConfigPage +from spyder.plugins.runconfig import get_run_configuration +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import create_action +from .widgets.profilergui import (ProfilerWidget, is_profiler_installed) + + +_ = get_translation("profiler", "spyder_profiler") + + +class ProfilerConfigPage(PluginConfigPage): + def setup_page(self): + results_group = QGroupBox(_("Results")) + results_label1 = QLabel(_("Profiler plugin results " + "(the output of python's profile/cProfile)\n" + "are stored here:")) + results_label1.setWordWrap(True) + + # Warning: do not try to regroup the following QLabel contents with + # widgets above -- this string was isolated here in a single QLabel + # on purpose: to fix Issue 863 + results_label2 = QLabel(ProfilerWidget.DATAPATH) + + results_label2.setTextInteractionFlags(Qt.TextSelectableByMouse) + results_label2.setWordWrap(True) + + results_layout = QVBoxLayout() + results_layout.addWidget(results_label1) + results_layout.addWidget(results_label2) + results_group.setLayout(results_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(results_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + +class Profiler(ProfilerWidget, SpyderPluginMixin): + """Profiler (after python's profile and pstats)""" + CONF_SECTION = 'profiler' + CONFIGWIDGET_CLASS = ProfilerConfigPage + edit_goto = Signal(str, int, str) + + def __init__(self, parent=None): + ProfilerWidget.__init__(self, parent=parent, + max_entries=self.get_option('max_entries', 50)) + SpyderPluginMixin.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + + #------ SpyderPluginWidget API --------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _("Profiler") + + def get_plugin_icon(self): + """Return widget icon""" + path = osp.join(self.PLUGIN_PATH, self.IMG_PATH) + return ima.icon('profiler', icon_path=path) + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.datatree + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + return [] + + def on_first_registration(self): + """Action to be performed on first plugin registration""" + self.main.tabify_plugins(self.main.help, self) + self.dockwidget.hide() + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.edit_goto.connect(self.main.editor.load) + self.redirect_stdio.connect(self.main.redirect_internalshell_stdio) + self.main.add_dockwidget(self) + + profiler_act = create_action(self, _("Profile"), + icon=self.get_plugin_icon(), + triggered=self.run_profiler) + profiler_act.setEnabled(is_profiler_installed()) + self.register_shortcut(profiler_act, context="Profiler", + name="Run profiler") + + self.main.run_menu_actions += [profiler_act] + self.main.editor.pythonfile_dependent_actions += [profiler_act] + + def refresh_plugin(self): + """Refresh profiler widget""" + #self.remove_obsolete_items() # FIXME: not implemented yet + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + return True + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + # The history depth option will be applied at + # next Spyder startup, which is soon enough + pass + + #------ Public API --------------------------------------------------------- + def run_profiler(self): + """Run profiler""" + if self.main.editor.save(): + self.analyze(self.main.editor.get_current_filename()) + + def analyze(self, filename): + """Reimplement analyze method""" + if self.dockwidget and not self.ismaximized: + self.dockwidget.setVisible(True) + self.dockwidget.setFocus() + self.dockwidget.raise_() + pythonpath = self.main.get_spyder_pythonpath() + runconf = get_run_configuration(filename) + wdir, args = None, [] + if runconf is not None: + if runconf.wdir_enabled: + wdir = runconf.wdir + if runconf.args_enabled: + args = runconf.args + ProfilerWidget.analyze(self, filename, wdir=wdir, args=args, + pythonpath=pythonpath) diff -Nru spyder-2.3.8+dfsg1/spyder_profiler/widgets/profilergui.py spyder-3.0.2+dfsg1/spyder_profiler/widgets/profilergui.py --- spyder-2.3.8+dfsg1/spyder_profiler/widgets/profilergui.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_profiler/widgets/profilergui.py 2016-11-16 02:30:06.000000000 +0000 @@ -0,0 +1,691 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# based on pylintgui.py by Pierre Raybaut +# +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Profiler widget + +See the official documentation on python profiling: +http://docs.python.org/library/profile.html +""" + +# Standard library imports +from __future__ import with_statement +import os +import os.path as osp +import sys +import time + +# Third party imports +from qtpy.compat import getopenfilename, getsavefilename +from qtpy.QtCore import (QByteArray, QProcess, QProcessEnvironment, QTextCodec, + Qt, Signal) +from qtpy.QtGui import QColor +from qtpy.QtWidgets import (QApplication, QHBoxLayout, QLabel, QMessageBox, + QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget) + +# Local imports +from spyder.config.base import get_conf_path, get_translation +from spyder.py3compat import getcwd, to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import (create_toolbutton, get_item_user_text, + set_item_user_text) +from spyder.utils.programs import shell_split +from spyder.widgets.comboboxes import PythonModulesComboBox +from spyder.utils.misc import add_pathlist_to_PYTHONPATH +from spyder.widgets.variableexplorer.texteditor import TextEditor + +# This is needed for testing this module as a stand alone script +try: + _ = get_translation("profiler", "spyder_profiler") +except KeyError as error: + import gettext + _ = gettext.gettext + + +locale_codec = QTextCodec.codecForLocale() + + +def is_profiler_installed(): + from spyder.utils.programs import is_module_installed + return is_module_installed('cProfile') and is_module_installed('pstats') + + +class ProfilerWidget(QWidget): + """ + Profiler widget + """ + DATAPATH = get_conf_path('profiler.results') + VERSION = '0.0.1' + redirect_stdio = Signal(bool) + + def __init__(self, parent, max_entries=100): + QWidget.__init__(self, parent) + + self.setWindowTitle("Profiler") + + self.output = None + self.error_output = None + + self._last_wdir = None + self._last_args = None + self._last_pythonpath = None + + self.filecombo = PythonModulesComboBox(self) + + self.start_button = create_toolbutton(self, icon=ima.icon('run'), + text=_("Profile"), + tip=_("Run profiler"), + triggered=lambda : self.start(), + text_beside_icon=True) + self.stop_button = create_toolbutton(self, + icon=ima.icon('stop'), + text=_("Stop"), + tip=_("Stop current profiling"), + text_beside_icon=True) + self.filecombo.valid.connect(self.start_button.setEnabled) + #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) + # FIXME: The combobox emits this signal on almost any event + # triggering show_data() too early, too often. + + browse_button = create_toolbutton(self, icon=ima.icon('fileopen'), + tip=_('Select Python script'), + triggered=self.select_file) + + self.datelabel = QLabel() + + self.log_button = create_toolbutton(self, icon=ima.icon('log'), + text=_("Output"), + text_beside_icon=True, + tip=_("Show program's output"), + triggered=self.show_log) + + self.datatree = ProfilerDataTree(self) + + self.collapse_button = create_toolbutton(self, + icon=ima.icon('collapse'), + triggered=lambda dD: + self.datatree.change_view(-1), + tip=_('Collapse one level up')) + self.expand_button = create_toolbutton(self, + icon=ima.icon('expand'), + triggered=lambda dD: + self.datatree.change_view(1), + tip=_('Expand one level down')) + + self.save_button = create_toolbutton(self, text_beside_icon=True, + text=_("Save data"), + icon=ima.icon('filesave'), + triggered=self.save_data, + tip=_('Save profiling data')) + self.load_button = create_toolbutton(self, text_beside_icon=True, + text=_("Load data"), + icon=ima.icon('fileimport'), + triggered=self.compare, + tip=_('Load profiling data for comparison')) + self.clear_button = create_toolbutton(self, text_beside_icon=True, + text=_("Clear comparison"), + icon=ima.icon('editdelete'), + triggered=self.clear) + + hlayout1 = QHBoxLayout() + hlayout1.addWidget(self.filecombo) + hlayout1.addWidget(browse_button) + hlayout1.addWidget(self.start_button) + hlayout1.addWidget(self.stop_button) + + hlayout2 = QHBoxLayout() + hlayout2.addWidget(self.collapse_button) + hlayout2.addWidget(self.expand_button) + hlayout2.addStretch() + hlayout2.addWidget(self.datelabel) + hlayout2.addStretch() + hlayout2.addWidget(self.log_button) + hlayout2.addWidget(self.save_button) + hlayout2.addWidget(self.load_button) + hlayout2.addWidget(self.clear_button) + + layout = QVBoxLayout() + layout.addLayout(hlayout1) + layout.addLayout(hlayout2) + layout.addWidget(self.datatree) + self.setLayout(layout) + + self.process = None + self.set_running_state(False) + self.start_button.setEnabled(False) + self.clear_button.setEnabled(False) + + if not is_profiler_installed(): + # This should happen only on certain GNU/Linux distributions + # or when this a home-made Python build because the Python + # profilers are included in the Python standard library + for widget in (self.datatree, self.filecombo, + self.start_button, self.stop_button): + widget.setDisabled(True) + url = 'http://docs.python.org/library/profile.html' + text = '%s %s' % (_('Please install'), url, + _("the Python profiler modules")) + self.datelabel.setText(text) + else: + pass # self.show_data() + + def save_data(self): + """Save data""" + title = _( "Save profiler result") + filename, _selfilter = getsavefilename(self, title, + getcwd(), + _("Profiler result")+" (*.Result)") + if filename: + self.datatree.save_data(filename) + + def compare(self): + filename, _selfilter = getopenfilename(self, _("Select script to compare"), + getcwd(), _("Profiler result")+" (*.Result)") + if filename: + self.datatree.compare(filename) + self.show_data() + self.clear_button.setEnabled(True) + + def clear(self): + self.datatree.compare(None) + self.datatree.hide_diff_cols(True) + self.show_data() + self.clear_button.setEnabled(False) + + def analyze(self, filename, wdir=None, args=None, pythonpath=None): + if not is_profiler_installed(): + return + self.kill_if_running() + #index, _data = self.get_data(filename) + index = None # FIXME: storing data is not implemented yet + if index is None: + self.filecombo.addItem(filename) + self.filecombo.setCurrentIndex(self.filecombo.count()-1) + else: + self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) + self.filecombo.selected() + if self.filecombo.is_valid(): + if wdir is None: + wdir = osp.dirname(filename) + self.start(wdir, args, pythonpath) + + def select_file(self): + self.redirect_stdio.emit(False) + filename, _selfilter = getopenfilename(self, _("Select Python script"), + getcwd(), _("Python scripts")+" (*.py ; *.pyw)") + self.redirect_stdio.emit(True) + if filename: + self.analyze(filename) + + def show_log(self): + if self.output: + TextEditor(self.output, title=_("Profiler output"), + readonly=True, size=(700, 500)).exec_() + + def show_errorlog(self): + if self.error_output: + TextEditor(self.error_output, title=_("Profiler output"), + readonly=True, size=(700, 500)).exec_() + + def start(self, wdir=None, args=None, pythonpath=None): + filename = to_text_string(self.filecombo.currentText()) + if wdir is None: + wdir = self._last_wdir + if wdir is None: + wdir = osp.basename(filename) + if args is None: + args = self._last_args + if args is None: + args = [] + if pythonpath is None: + pythonpath = self._last_pythonpath + self._last_wdir = wdir + self._last_args = args + self._last_pythonpath = pythonpath + + self.datelabel.setText(_('Profiling, please wait...')) + + self.process = QProcess(self) + self.process.setProcessChannelMode(QProcess.SeparateChannels) + self.process.setWorkingDirectory(wdir) + self.process.readyReadStandardOutput.connect(self.read_output) + self.process.readyReadStandardError.connect( + lambda: self.read_output(error=True)) + self.process.finished.connect(lambda ec, es=QProcess.ExitStatus: + self.finished(ec, es)) + self.stop_button.clicked.connect(self.process.kill) + + if pythonpath is not None: + env = [to_text_string(_pth) + for _pth in self.process.systemEnvironment()] + add_pathlist_to_PYTHONPATH(env, pythonpath) + processEnvironment = QProcessEnvironment() + for envItem in env: + envName, separator, envValue = envItem.partition('=') + processEnvironment.insert(envName, envValue) + self.process.setProcessEnvironment(processEnvironment) + + self.output = '' + self.error_output = '' + + p_args = ['-m', 'cProfile', '-o', self.DATAPATH] + if os.name == 'nt': + # On Windows, one has to replace backslashes by slashes to avoid + # confusion with escape characters (otherwise, for example, '\t' + # will be interpreted as a tabulation): + p_args.append(osp.normpath(filename).replace(os.sep, '/')) + else: + p_args.append(filename) + if args: + p_args.extend(shell_split(args)) + executable = sys.executable + if executable.endswith("spyder.exe"): + # py2exe distribution + executable = "python.exe" + self.process.start(executable, p_args) + + running = self.process.waitForStarted() + self.set_running_state(running) + if not running: + QMessageBox.critical(self, _("Error"), + _("Process failed to start")) + + def set_running_state(self, state=True): + self.start_button.setEnabled(not state) + self.stop_button.setEnabled(state) + + def read_output(self, error=False): + if error: + self.process.setReadChannel(QProcess.StandardError) + else: + self.process.setReadChannel(QProcess.StandardOutput) + qba = QByteArray() + while self.process.bytesAvailable(): + if error: + qba += self.process.readAllStandardError() + else: + qba += self.process.readAllStandardOutput() + text = to_text_string( locale_codec.toUnicode(qba.data()) ) + if error: + self.error_output += text + else: + self.output += text + + def finished(self, exit_code, exit_status): + self.set_running_state(False) + self.show_errorlog() # If errors occurred, show them. + self.output = self.error_output + self.output + # FIXME: figure out if show_data should be called here or + # as a signal from the combobox + self.show_data(justanalyzed=True) + + def kill_if_running(self): + if self.process is not None: + if self.process.state() == QProcess.Running: + self.process.kill() + self.process.waitForFinished() + + def show_data(self, justanalyzed=False): + if not justanalyzed: + self.output = None + self.log_button.setEnabled(self.output is not None \ + and len(self.output) > 0) + self.kill_if_running() + filename = to_text_string(self.filecombo.currentText()) + if not filename: + return + + self.datelabel.setText(_('Sorting data, please wait...')) + QApplication.processEvents() + + self.datatree.load_data(self.DATAPATH) + self.datatree.show_tree() + + text_style = "%s " + date_text = text_style % time.strftime("%d %b %Y %H:%M", + time.localtime()) + self.datelabel.setText(date_text) + + +class TreeWidgetItem( QTreeWidgetItem ): + def __init__(self, parent=None): + QTreeWidgetItem.__init__(self, parent) + + def __lt__(self, otherItem): + column = self.treeWidget().sortColumn() + try: + return float( self.text(column) ) > float( otherItem.text(column) ) + except ValueError: + return self.text(column) > otherItem.text(column) + + +class ProfilerDataTree(QTreeWidget): + """ + Convenience tree widget (with built-in model) + to store and view profiler data. + + The quantities calculated by the profiler are as follows + (from profile.Profile): + [0] = The number of times this function was called, not counting direct + or indirect recursion, + [1] = Number of times this function appears on the stack, minus one + [2] = Total time spent internal to this function + [3] = Cumulative time that this function was present on the stack. In + non-recursive functions, this is the total execution time from start + to finish of each invocation of a function, including time spent in + all subfunctions. + [4] = A dictionary indicating for each function name, the number of times + it was called by us. + """ + SEP = r"<[=]>" # separator between filename and linenumber + # (must be improbable as a filename to avoid splitting the filename itself) + def __init__(self, parent=None): + QTreeWidget.__init__(self, parent) + self.header_list = [_('Function/Module'), _('Total Time'), _('Diff'), + _('Local Time'), _('Diff'), _('Calls'), _('Diff'), + _('File:line')] + self.icon_list = {'module': ima.icon('python'), + 'function': ima.icon('function'), + 'builtin': ima.icon('python_t'), + 'constructor': ima.icon('class')} + self.profdata = None # To be filled by self.load_data() + self.stats = None # To be filled by self.load_data() + self.item_depth = None + self.item_list = None + self.items_to_be_shown = None + self.current_view_depth = None + self.compare_file = None + self.setColumnCount(len(self.header_list)) + self.setHeaderLabels(self.header_list) + self.initialize_view() + self.itemActivated.connect(self.item_activated) + self.itemExpanded.connect(self.item_expanded) + + def set_item_data(self, item, filename, line_number): + """Set tree item user data: filename (string) and line_number (int)""" + set_item_user_text(item, '%s%s%d' % (filename, self.SEP, line_number)) + + def get_item_data(self, item): + """Get tree item user data: (filename, line_number)""" + filename, line_number_str = get_item_user_text(item).split(self.SEP) + return filename, int(line_number_str) + + def initialize_view(self): + """Clean the tree and view parameters""" + self.clear() + self.item_depth = 0 # To be use for collapsing/expanding one level + self.item_list = [] # To be use for collapsing/expanding one level + self.items_to_be_shown = {} + self.current_view_depth = 0 + + def load_data(self, profdatafile): + """Load profiler data saved by profile/cProfile module""" + import pstats + stats_indi = [pstats.Stats(profdatafile),] + self.profdata = stats_indi[0] + + if self.compare_file is not None: + stats_indi.append(pstats.Stats(self.compare_file)) + map(lambda x: x.calc_callees(), stats_indi) + self.profdata.calc_callees() + self.stats1 = stats_indi + self.stats = stats_indi[0].stats + + def compare(self,filename): + self.hide_diff_cols(False) + self.compare_file = filename + + def hide_diff_cols(self, hide): + for i in (2,4,6): + self.setColumnHidden(i, hide) + + def save_data(self, filename): + """""" + self.stats1[0].dump_stats(filename) + + def find_root(self): + """Find a function without a caller""" + self.profdata.sort_stats("cumulative") + for func in self.profdata.fcn_list: + if ('~', 0) != func[0:2] and not func[2].startswith( + ''): + # This skips the profiler function at the top of the list + # it does only occur in Python 3 + return func + + def find_callees(self, parent): + """Find all functions called by (parent) function.""" + # FIXME: This implementation is very inneficient, because it + # traverses all the data to find children nodes (callees) + return self.profdata.all_callees[parent] + + def show_tree(self): + """Populate the tree with profiler data and display it.""" + self.initialize_view() # Clear before re-populating + self.setItemsExpandable(True) + self.setSortingEnabled(False) + rootkey = self.find_root() # This root contains profiler overhead + if rootkey: + self.populate_tree(self, self.find_callees(rootkey)) + self.resizeColumnToContents(0) + self.setSortingEnabled(True) + self.sortItems(1, Qt.AscendingOrder) # FIXME: hardcoded index + self.change_view(1) + + def function_info(self, functionKey): + """Returns processed information about the function's name and file.""" + node_type = 'function' + filename, line_number, function_name = functionKey + if function_name == '': + modulePath, moduleName = osp.split(filename) + node_type = 'module' + if moduleName == '__init__.py': + modulePath, moduleName = osp.split(modulePath) + function_name = '<' + moduleName + '>' + if not filename or filename == '~': + file_and_line = '(built-in)' + node_type = 'builtin' + else: + if function_name == '__init__': + node_type = 'constructor' + file_and_line = '%s : %d' % (filename, line_number) + return filename, line_number, function_name, file_and_line, node_type + + def color_string(self, args): + x, format = args + diff_str = "" + color = "black" + if len(x) == 2 and self.compare_file is not None: + difference = x[0] - x[1] + if difference < 0: + diff_str = "".join(['',format[1] % difference]) + color = "green" + elif difference > 0: + diff_str = "".join(['+',format[1] % difference]) + color = "red" + return [format[0] % x[0], [diff_str, color]] + + + def format_output(self,child_key): + """ Formats the data""" + if True: + data = [x.stats.get(child_key,[0,0,0,0,0]) for x in self.stats1] + return map(self.color_string,zip(list(zip(*data))[1:4], [["%i"]*2, ["%.3f","%.3f"], ["%.3f","%.3f"]])) + + def populate_tree(self, parentItem, children_list): + """Recursive method to create each item (and associated data) in the tree.""" + for child_key in children_list: + self.item_depth += 1 + (filename, line_number, function_name, file_and_line, node_type + ) = self.function_info(child_key) + + ((total_calls, total_calls_dif), (loc_time, loc_time_dif), (cum_time, + cum_time_dif)) = self.format_output(child_key) + + (primcalls, total_calls, loc_time, cum_time, callers + ) = self.stats[child_key] + child_item = TreeWidgetItem(parentItem) + self.item_list.append(child_item) + self.set_item_data(child_item, filename, line_number) + + # FIXME: indexes to data should be defined by a dictionary on init + child_item.setToolTip(0, _('Function or module name')) + child_item.setData(0, Qt.DisplayRole, function_name) + child_item.setIcon(0, self.icon_list[node_type]) + + child_item.setToolTip(1, _('Time in function '\ + '(including sub-functions)')) + child_item.setData(1, Qt.DisplayRole, cum_time) + child_item.setTextAlignment(1, Qt.AlignRight) + + child_item.setData(2, Qt.DisplayRole, cum_time_dif[0]) + child_item.setForeground(2, QColor(cum_time_dif[1])) + child_item.setTextAlignment(2, Qt.AlignLeft) + + child_item.setToolTip(3, _('Local time in function '\ + '(not in sub-functions)')) + + child_item.setData(3, Qt.DisplayRole, loc_time) + child_item.setTextAlignment(3, Qt.AlignRight) + + child_item.setData(4, Qt.DisplayRole, loc_time_dif[0]) + child_item.setForeground(4, QColor(loc_time_dif[1])) + child_item.setTextAlignment(4, Qt.AlignLeft) + + child_item.setToolTip(5, _('Total number of calls '\ + '(including recursion)')) + + child_item.setData(5, Qt.DisplayRole, total_calls) + child_item.setTextAlignment(5, Qt.AlignRight) + + child_item.setData(6, Qt.DisplayRole, total_calls_dif[0]) + child_item.setForeground(6, QColor(total_calls_dif[1])) + child_item.setTextAlignment(6, Qt.AlignLeft) + + child_item.setToolTip(7, _('File:line '\ + 'where function is defined')) + child_item.setData(7, Qt.DisplayRole, file_and_line) + #child_item.setExpanded(True) + if self.is_recursive(child_item): + child_item.setData(7, Qt.DisplayRole, '(%s)' % _('recursion')) + child_item.setDisabled(True) + else: + callees = self.find_callees(child_key) + if self.item_depth < 3: + self.populate_tree(child_item, callees) + elif callees: + child_item.setChildIndicatorPolicy(child_item.ShowIndicator) + self.items_to_be_shown[id(child_item)] = callees + self.item_depth -= 1 + + def item_activated(self, item): + filename, line_number = self.get_item_data(item) + self.parent().edit_goto.emit(filename, line_number, '') + + def item_expanded(self, item): + if item.childCount() == 0 and id(item) in self.items_to_be_shown: + callees = self.items_to_be_shown[id(item)] + self.populate_tree(item, callees) + + def is_recursive(self, child_item): + """Returns True is a function is a descendant of itself.""" + ancestor = child_item.parent() + # FIXME: indexes to data should be defined by a dictionary on init + while ancestor: + if (child_item.data(0, Qt.DisplayRole + ) == ancestor.data(0, Qt.DisplayRole) and + child_item.data(4, Qt.DisplayRole + ) == ancestor.data(4, Qt.DisplayRole)): + return True + else: + ancestor = ancestor.parent() + return False + + def get_top_level_items(self): + """Iterate over top level items""" + return [self.topLevelItem(_i) for _i in range(self.topLevelItemCount())] + + def get_items(self, maxlevel): + """Return all items with a level <= `maxlevel`""" + itemlist = [] + def add_to_itemlist(item, maxlevel, level=1): + level += 1 + for index in range(item.childCount()): + citem = item.child(index) + itemlist.append(citem) + if level <= maxlevel: + add_to_itemlist(citem, maxlevel, level) + for tlitem in self.get_top_level_items(): + itemlist.append(tlitem) + if maxlevel > 0: + add_to_itemlist(tlitem, maxlevel=maxlevel) + return itemlist + + def change_view(self, change_in_depth): + """Change the view depth by expand or collapsing all same-level nodes""" + self.current_view_depth += change_in_depth + if self.current_view_depth < 0: + self.current_view_depth = 0 + self.collapseAll() + if self.current_view_depth > 0: + for item in self.get_items(maxlevel=self.current_view_depth-1): + item.setExpanded(True) + + +#============================================================================== +# Tests +#============================================================================== +def primes(n): + """ + Simple test function + Taken from http://www.huyng.com/posts/python-performance-analysis/ + """ + if n==2: + return [2] + elif n<2: + return [] + s=list(range(3,n+1,2)) + mroot = n ** 0.5 + half=(n+1)//2-1 + i=0 + m=3 + while m <= mroot: + if s[i]: + j=(m*m-3)//2 + s[j]=0 + while j\n" +"Language-Team: Python\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Poedit-SourceCharset: utf-8\n" +"X-Poedit-Basepath: ../../../../\n" +"X-Generator: Poedit 1.5.4\n" + +#: spyder_pylint/pylint.py:35 +msgid "Settings" +msgstr "Ajustes" + +#: spyder_pylint/pylint.py:36 +msgid "Save file before analyzing it" +msgstr "Guardar archivo antes de analizarlo" + +#: spyder_pylint/pylint.py:39 spyder_pylint/pylint.py:160 +msgid "History" +msgstr "Historial" + +#: spyder_pylint/pylint.py:40 +msgid "The following option will be applied at next startup." +msgstr "La siguiente opción será aplicada la próxima vez que se inicie Spyder" + +#: spyder_pylint/pylint.py:43 +msgid "History: " +msgstr "Guardar" + +#: spyder_pylint/pylint.py:44 +msgid " results" +msgstr " resultados" + +#: spyder_pylint/pylint.py:47 +msgid "Results" +msgstr "Resultados" + +#: spyder_pylint/pylint.py:48 +msgid "Results are stored here:" +msgstr "Los resultados se guardan en:" + +#: spyder_pylint/pylint.py:98 spyder_pylint/widgets/pylintgui.py:83 +msgid "Static code analysis" +msgstr "Análisis estático del código" + +#: spyder_pylint/pylint.py:115 +msgid "History..." +msgstr "" + +#: spyder_pylint/pylint.py:117 +msgid "Set history maximum entries" +msgstr "" + +#: spyder_pylint/pylint.py:133 +msgid "Run static code analysis" +msgstr "Realizar análisis estático del código" + +#: spyder_pylint/pylint.py:161 +msgid "Maximum entries" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:117 +msgid "Results for " +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:122 +msgid "Convention" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:124 +msgid "Refactor" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:126 +msgid "Warning" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:128 +#: spyder_pylint/widgets/pylintgui.py:363 +#: spyder_pylint/widgets/pylintgui.py:391 +msgid "Error" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:206 +msgid "Analyze" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:207 +msgid "Run analysis" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:211 +msgid "Stop" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:212 +msgid "Stop current analysis" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:218 +#: spyder_pylint/widgets/pylintgui.py:288 +msgid "Select Python file" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:224 +msgid "Output" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:226 +msgid "Complete output" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:260 +msgid "Pylint script was not found. Please add \"%s\" to PATH." +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:263 +msgid "Please install pylint:" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:289 +msgid "Python files" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:326 +msgid "Pylint output" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:364 +msgid "Process failed to start" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:465 +msgid "Source code has not been rated yet." +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:471 +msgid "Analysis did not succeed (see output for more details)." +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:484 +msgid "Global evaluation:" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:488 +msgid "previous run:" +msgstr "" + +#~ msgid "Pylint" +#~ msgstr "Análisis del código" + +#~ msgid "Run pylint code analysis" +#~ msgstr "Realizar un análisis del código" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_pylint/locale/fr/LC_MESSAGES/pylint.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_pylint/locale/fr/LC_MESSAGES/pylint.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_pylint/locale/fr/LC_MESSAGES/pylint.po spyder-3.0.2+dfsg1/spyder_pylint/locale/fr/LC_MESSAGES/pylint.po --- spyder-2.3.8+dfsg1/spyder_pylint/locale/fr/LC_MESSAGES/pylint.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_pylint/locale/fr/LC_MESSAGES/pylint.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- +# Spyder Pylint plugin's french translation file +# Copyright (C) 2009-2011 Pierre Raybaut +# +msgid "" +msgstr "" +"Project-Id-Version: 2.1\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2011-04-11 21:41+2\n" +"Last-Translator: Pierre Raybaut\n" +"Language-Team: Python\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: utf-8\n" +"Generated-By: pygettext.py 1.5\n" + +#: spyder_pylint/pylint.py:35 +msgid "Settings" +msgstr "Options" + +#: spyder_pylint/pylint.py:36 +msgid "Save file before analyzing it" +msgstr "Enregistrer le fichier avant de l'analyser" + +#: spyder_pylint/pylint.py:39 spyder_pylint/pylint.py:160 +msgid "History" +msgstr "Historique" + +#: spyder_pylint/pylint.py:40 +msgid "The following option will be applied at next startup." +msgstr "L'option suivante ne sera appliquée qu'au prochain démarrage." + +#: spyder_pylint/pylint.py:43 +msgid "History: " +msgstr "Historique : " + +#: spyder_pylint/pylint.py:44 +msgid " results" +msgstr " résultats" + +#: spyder_pylint/pylint.py:47 +msgid "Results" +msgstr "Résultats" + +#: spyder_pylint/pylint.py:48 +msgid "Results are stored here:" +msgstr "Les résultats sont stockés ici :" + +#: spyder_pylint/pylint.py:98 spyder_pylint/widgets/pylintgui.py:83 +msgid "Static code analysis" +msgstr "Analyse de code statique" + +#: spyder_pylint/pylint.py:115 +msgid "History..." +msgstr "Historique..." + +#: spyder_pylint/pylint.py:117 +msgid "Set history maximum entries" +msgstr "Modifier le nombre d'entrées maximum de l'historique" + +#: spyder_pylint/pylint.py:133 +msgid "Run static code analysis" +msgstr "Démarrer l'analyse de code statique" + +#: spyder_pylint/pylint.py:161 +msgid "Maximum entries" +msgstr "Nombre maximum d'entrées" + +#: spyder_pylint/widgets/pylintgui.py:117 +msgid "Results for " +msgstr "Résultats pour " + +#: spyder_pylint/widgets/pylintgui.py:122 +msgid "Convention" +msgstr "Convention" + +#: spyder_pylint/widgets/pylintgui.py:124 +msgid "Refactor" +msgstr "Factorisation" + +#: spyder_pylint/widgets/pylintgui.py:126 +msgid "Warning" +msgstr "Avertissement" + +#: spyder_pylint/widgets/pylintgui.py:128 +#: spyder_pylint/widgets/pylintgui.py:363 +#: spyder_pylint/widgets/pylintgui.py:391 +msgid "Error" +msgstr "Erreur" + +#: spyder_pylint/widgets/pylintgui.py:206 +msgid "Analyze" +msgstr "Analyser" + +#: spyder_pylint/widgets/pylintgui.py:207 +msgid "Run analysis" +msgstr "Analyser le code source" + +#: spyder_pylint/widgets/pylintgui.py:211 +msgid "Stop" +msgstr "Arrêter" + +#: spyder_pylint/widgets/pylintgui.py:212 +msgid "Stop current analysis" +msgstr "Arrêter l'analyse en cours" + +#: spyder_pylint/widgets/pylintgui.py:218 +#: spyder_pylint/widgets/pylintgui.py:288 +msgid "Select Python file" +msgstr "Sélectionner un fichier Python" + +#: spyder_pylint/widgets/pylintgui.py:224 +msgid "Output" +msgstr "Sortie" + +#: spyder_pylint/widgets/pylintgui.py:226 +msgid "Complete output" +msgstr "Sortie complète" + +#: spyder_pylint/widgets/pylintgui.py:260 +msgid "Pylint script was not found. Please add \"%s\" to PATH." +msgstr "Le script pylint est introuvable. Merci d'ajouter \"%s\" au PATH." + +#: spyder_pylint/widgets/pylintgui.py:263 +msgid "Please install pylint:" +msgstr "Merci d'installer au préalable pylint :" + +#: spyder_pylint/widgets/pylintgui.py:289 +msgid "Python files" +msgstr "Fichiers Python" + +#: spyder_pylint/widgets/pylintgui.py:326 +msgid "Pylint output" +msgstr "Analyse Pylint" + +#: spyder_pylint/widgets/pylintgui.py:364 +msgid "Process failed to start" +msgstr "Le processus n'a pas pu démarrer" + +#: spyder_pylint/widgets/pylintgui.py:465 +msgid "Source code has not been rated yet." +msgstr "Le code source n'a pas encore été analysé." + +#: spyder_pylint/widgets/pylintgui.py:471 +msgid "Analysis did not succeed (see output for more details)." +msgstr "L'analyse n'a pas réussi (voir la sortie pour plus de détails)." + +#: spyder_pylint/widgets/pylintgui.py:484 +msgid "Global evaluation:" +msgstr "Évaluation globale :" + +#: spyder_pylint/widgets/pylintgui.py:488 +msgid "previous run:" +msgstr "analyse précédente :" + +#~ msgid "Pylint" +#~ msgstr "Pylint" + +#~ msgid "Run pylint code analysis" +#~ msgstr "Analyser le code source avec pylint" Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_pylint/locale/pt_BR/LC_MESSAGES/pylint.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_pylint/locale/pt_BR/LC_MESSAGES/pylint.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_pylint/locale/pt_BR/LC_MESSAGES/pylint.po spyder-3.0.2+dfsg1/spyder_pylint/locale/pt_BR/LC_MESSAGES/pylint.po --- spyder-2.3.8+dfsg1/spyder_pylint/locale/pt_BR/LC_MESSAGES/pylint.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_pylint/locale/pt_BR/LC_MESSAGES/pylint.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +# Spyder's brazilian portuguese translation file +# Valter Nazianzeno , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2015-02-25 22:08-0400\n" +"Last-Translator: Valter Nazianzeno \n" +"Language-Team: \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.7.4\n" + +#: spyder_pylint/pylint.py:35 +msgid "Settings" +msgstr "Configurações" + +#: spyder_pylint/pylint.py:36 +msgid "Save file before analyzing it" +msgstr "Salvar arquivo antes de analisá-lo" + +#: spyder_pylint/pylint.py:39 spyder_pylint/pylint.py:160 +msgid "History" +msgstr "Histórico" + +#: spyder_pylint/pylint.py:40 +msgid "The following option will be applied at next startup." +msgstr "" +"A seguinte opção será aplicada na próxima vez que o Spyder for iniciado." + +#: spyder_pylint/pylint.py:43 +msgid "History: " +msgstr "Histórico:" + +#: spyder_pylint/pylint.py:44 +msgid " results" +msgstr " resultados" + +#: spyder_pylint/pylint.py:47 +msgid "Results" +msgstr "Resultados" + +#: spyder_pylint/pylint.py:48 +msgid "Results are stored here:" +msgstr "Os resultados são armazenados aqui:" + +#: spyder_pylint/pylint.py:98 spyder_pylint/widgets/pylintgui.py:83 +msgid "Static code analysis" +msgstr "Análise de código" + +#: spyder_pylint/pylint.py:115 +msgid "History..." +msgstr "Histórico..." + +#: spyder_pylint/pylint.py:117 +msgid "Set history maximum entries" +msgstr "Definir número máximo de entradas para o histórico" + +#: spyder_pylint/pylint.py:133 +msgid "Run static code analysis" +msgstr "Executar análise de código" + +#: spyder_pylint/pylint.py:161 +msgid "Maximum entries" +msgstr "Número máximo de entradas" + +#: spyder_pylint/widgets/pylintgui.py:117 +msgid "Results for " +msgstr "Resultados para " + +#: spyder_pylint/widgets/pylintgui.py:122 +msgid "Convention" +msgstr "Convention" + +#: spyder_pylint/widgets/pylintgui.py:124 +msgid "Refactor" +msgstr "Refatoração" + +#: spyder_pylint/widgets/pylintgui.py:126 +msgid "Warning" +msgstr "Aviso" + +#: spyder_pylint/widgets/pylintgui.py:128 +#: spyder_pylint/widgets/pylintgui.py:363 +#: spyder_pylint/widgets/pylintgui.py:391 +msgid "Error" +msgstr "Erro" + +#: spyder_pylint/widgets/pylintgui.py:206 +msgid "Analyze" +msgstr "Analisar" + +#: spyder_pylint/widgets/pylintgui.py:207 +msgid "Run analysis" +msgstr "Executar análise" + +#: spyder_pylint/widgets/pylintgui.py:211 +msgid "Stop" +msgstr "Parar" + +#: spyder_pylint/widgets/pylintgui.py:212 +msgid "Stop current analysis" +msgstr "Parar análise atual" + +#: spyder_pylint/widgets/pylintgui.py:218 +#: spyder_pylint/widgets/pylintgui.py:288 +msgid "Select Python file" +msgstr "Selecionar arquivo Python" + +#: spyder_pylint/widgets/pylintgui.py:224 +msgid "Output" +msgstr "Saída" + +#: spyder_pylint/widgets/pylintgui.py:226 +msgid "Complete output" +msgstr "Saída completa" + +#: spyder_pylint/widgets/pylintgui.py:260 +msgid "Pylint script was not found. Please add \"%s\" to PATH." +msgstr "" +"O script do Pylint não foi encontrada. Por favor adicione \"%s\" ao PATH." + +#: spyder_pylint/widgets/pylintgui.py:263 +msgid "Please install pylint:" +msgstr "Por favor instale o pylint:" + +#: spyder_pylint/widgets/pylintgui.py:289 +msgid "Python files" +msgstr "Arquivos Python" + +#: spyder_pylint/widgets/pylintgui.py:326 +msgid "Pylint output" +msgstr "Saída do Pylint" + +#: spyder_pylint/widgets/pylintgui.py:364 +msgid "Process failed to start" +msgstr "O processo falhou ao iniciar" + +#: spyder_pylint/widgets/pylintgui.py:465 +msgid "Source code has not been rated yet." +msgstr "O código fonte não foi avaliado ainda." + +#: spyder_pylint/widgets/pylintgui.py:471 +msgid "Analysis did not succeed (see output for more details)." +msgstr "A análise não foi bem sucedida (veja a saída para mais detalhes)." + +#: spyder_pylint/widgets/pylintgui.py:484 +msgid "Global evaluation:" +msgstr "Avaliação global:" + +#: spyder_pylint/widgets/pylintgui.py:488 +msgid "previous run:" +msgstr "análise anterior:" diff -Nru spyder-2.3.8+dfsg1/spyder_pylint/locale/pylint.pot spyder-3.0.2+dfsg1/spyder_pylint/locale/pylint.pot --- spyder-2.3.8+dfsg1/spyder_pylint/locale/pylint.pot 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_pylint/locale/pylint.pot 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,156 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: spyder_pylint/pylint.py:35 +msgid "Settings" +msgstr "" + +#: spyder_pylint/pylint.py:36 +msgid "Save file before analyzing it" +msgstr "" + +#: spyder_pylint/pylint.py:39 spyder_pylint/pylint.py:160 +msgid "History" +msgstr "" + +#: spyder_pylint/pylint.py:40 +msgid "The following option will be applied at next startup." +msgstr "" + +#: spyder_pylint/pylint.py:43 +msgid "History: " +msgstr "" + +#: spyder_pylint/pylint.py:44 +msgid " results" +msgstr "" + +#: spyder_pylint/pylint.py:47 +msgid "Results" +msgstr "" + +#: spyder_pylint/pylint.py:48 +msgid "Results are stored here:" +msgstr "" + +#: spyder_pylint/pylint.py:98 spyder_pylint/widgets/pylintgui.py:83 +msgid "Static code analysis" +msgstr "" + +#: spyder_pylint/pylint.py:115 +msgid "History..." +msgstr "" + +#: spyder_pylint/pylint.py:117 +msgid "Set history maximum entries" +msgstr "" + +#: spyder_pylint/pylint.py:133 +msgid "Run static code analysis" +msgstr "" + +#: spyder_pylint/pylint.py:161 +msgid "Maximum entries" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:117 +msgid "Results for " +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:122 +msgid "Convention" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:124 +msgid "Refactor" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:126 +msgid "Warning" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:128 +#: spyder_pylint/widgets/pylintgui.py:363 +#: spyder_pylint/widgets/pylintgui.py:391 +msgid "Error" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:206 +msgid "Analyze" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:207 +msgid "Run analysis" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:211 +msgid "Stop" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:212 +msgid "Stop current analysis" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:218 +#: spyder_pylint/widgets/pylintgui.py:288 +msgid "Select Python file" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:224 +msgid "Output" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:226 +msgid "Complete output" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:260 +msgid "Pylint script was not found. Please add \"%s\" to PATH." +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:263 +msgid "Please install pylint:" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:289 +msgid "Python files" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:326 +msgid "Pylint output" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:364 +msgid "Process failed to start" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:465 +msgid "Source code has not been rated yet." +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:471 +msgid "Analysis did not succeed (see output for more details)." +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:484 +msgid "Global evaluation:" +msgstr "" + +#: spyder_pylint/widgets/pylintgui.py:488 +msgid "previous run:" +msgstr "" + Binary files /tmp/tmpEqyBqH/03wvk8IMs2/spyder-2.3.8+dfsg1/spyder_pylint/locale/ru/LC_MESSAGES/pylint.mo and /tmp/tmpEqyBqH/DFu_dPLa4g/spyder-3.0.2+dfsg1/spyder_pylint/locale/ru/LC_MESSAGES/pylint.mo differ diff -Nru spyder-2.3.8+dfsg1/spyder_pylint/locale/ru/LC_MESSAGES/pylint.po spyder-3.0.2+dfsg1/spyder_pylint/locale/ru/LC_MESSAGES/pylint.po --- spyder-2.3.8+dfsg1/spyder_pylint/locale/ru/LC_MESSAGES/pylint.po 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_pylint/locale/ru/LC_MESSAGES/pylint.po 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,159 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: 2016-11-11 10:24+COT\n" +"PO-Revision-Date: 2016-04-18 20:29+0300\n" +"Last-Translator: Roman Kulagin \n" +"Language-Team: \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"X-Generator: Poedit 1.5.4\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: spyder_pylint/pylint.py:35 +msgid "Settings" +msgstr "Настройки" + +#: spyder_pylint/pylint.py:36 +msgid "Save file before analyzing it" +msgstr "Сохранить файл перед его анализом" + +#: spyder_pylint/pylint.py:39 spyder_pylint/pylint.py:160 +msgid "History" +msgstr "История" + +#: spyder_pylint/pylint.py:40 +msgid "The following option will be applied at next startup." +msgstr "Следующая опция будет применена при следующем запуске." + +#: spyder_pylint/pylint.py:43 +msgid "History: " +msgstr "История:" + +#: spyder_pylint/pylint.py:44 +msgid " results" +msgstr " результаты" + +#: spyder_pylint/pylint.py:47 +msgid "Results" +msgstr "Результаты" + +#: spyder_pylint/pylint.py:48 +msgid "Results are stored here:" +msgstr "Результаты хранятся здесь:" + +#: spyder_pylint/pylint.py:98 spyder_pylint/widgets/pylintgui.py:83 +msgid "Static code analysis" +msgstr "Статический анализ кода" + +#: spyder_pylint/pylint.py:115 +msgid "History..." +msgstr "История..." + +#: spyder_pylint/pylint.py:117 +msgid "Set history maximum entries" +msgstr "Установить максимальное число записей истории" + +#: spyder_pylint/pylint.py:133 +msgid "Run static code analysis" +msgstr "Запустить статический анализ кода" + +#: spyder_pylint/pylint.py:161 +msgid "Maximum entries" +msgstr "Максимум записей" + +#: spyder_pylint/widgets/pylintgui.py:117 +msgid "Results for " +msgstr "Результаты для" + +# Соглашения, Конвенции +#: spyder_pylint/widgets/pylintgui.py:122 +msgid "Convention" +msgstr "Договорённости" + +#: spyder_pylint/widgets/pylintgui.py:124 +msgid "Refactor" +msgstr "Рефакторинг" + +#: spyder_pylint/widgets/pylintgui.py:126 +msgid "Warning" +msgstr "Предупреждения" + +#: spyder_pylint/widgets/pylintgui.py:128 +#: spyder_pylint/widgets/pylintgui.py:363 +#: spyder_pylint/widgets/pylintgui.py:391 +msgid "Error" +msgstr "Ошибки" + +#: spyder_pylint/widgets/pylintgui.py:206 +msgid "Analyze" +msgstr "Анализ" + +#: spyder_pylint/widgets/pylintgui.py:207 +msgid "Run analysis" +msgstr "Запустить анализ" + +#: spyder_pylint/widgets/pylintgui.py:211 +msgid "Stop" +msgstr "Остановить" + +#: spyder_pylint/widgets/pylintgui.py:212 +msgid "Stop current analysis" +msgstr "Остановить текущий анализ" + +#: spyder_pylint/widgets/pylintgui.py:218 +#: spyder_pylint/widgets/pylintgui.py:288 +msgid "Select Python file" +msgstr "Выбрать файл Python" + +#: spyder_pylint/widgets/pylintgui.py:224 +msgid "Output" +msgstr "Вывод" + +#: spyder_pylint/widgets/pylintgui.py:226 +msgid "Complete output" +msgstr "Детальный вывод:" + +#: spyder_pylint/widgets/pylintgui.py:260 +msgid "Pylint script was not found. Please add \"%s\" to PATH." +msgstr "Скрипт pylint не найден. Пожалуйста добавьте \"%s\" в PATH." + +#: spyder_pylint/widgets/pylintgui.py:263 +msgid "Please install pylint:" +msgstr "Пожалуйста, установите pylint:" + +#: spyder_pylint/widgets/pylintgui.py:289 +msgid "Python files" +msgstr "Файлы Python" + +#: spyder_pylint/widgets/pylintgui.py:326 +msgid "Pylint output" +msgstr "Вывод pylint" + +#: spyder_pylint/widgets/pylintgui.py:364 +msgid "Process failed to start" +msgstr "Процесс не удалось запустить" + +#: spyder_pylint/widgets/pylintgui.py:465 +msgid "Source code has not been rated yet." +msgstr "Исходный код еще не был оценен." + +#: spyder_pylint/widgets/pylintgui.py:471 +msgid "Analysis did not succeed (see output for more details)." +msgstr "Анализ не удался (смотрите подробности в выводе)." + +#: spyder_pylint/widgets/pylintgui.py:484 +msgid "Global evaluation:" +msgstr "Общая оценка:" + +#: spyder_pylint/widgets/pylintgui.py:488 +msgid "previous run:" +msgstr "предыдущий запуск:" diff -Nru spyder-2.3.8+dfsg1/spyder_pylint/pylint.py spyder-3.0.2+dfsg1/spyder_pylint/pylint.py --- spyder-2.3.8+dfsg1/spyder_pylint/pylint.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_pylint/pylint.py 2016-11-19 00:31:58.000000000 +0000 @@ -0,0 +1,181 @@ +# -*- coding:utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Pylint Code Analysis Plugin.""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +import os.path as osp + +# Third party imports +from qtpy.QtCore import Qt, Signal, Slot +from qtpy.QtWidgets import QGroupBox, QInputDialog, QLabel, QVBoxLayout + +# Local imports +from spyder.config.base import get_translation +from spyder.plugins import SpyderPluginMixin +from spyder.plugins.configdialog import PluginConfigPage +from spyder.utils import icon_manager as ima +from spyder.utils.qthelpers import create_action +from .widgets.pylintgui import (PYLINT_PATH, PylintWidget) + + +_ = get_translation("pylint", "spyder_pylint") + + +class PylintConfigPage(PluginConfigPage): + def setup_page(self): + settings_group = QGroupBox(_("Settings")) + save_box = self.create_checkbox(_("Save file before analyzing it"), + 'save_before', default=True) + + hist_group = QGroupBox(_("History")) + hist_label1 = QLabel(_("The following option will be applied at next " + "startup.")) + hist_label1.setWordWrap(True) + hist_spin = self.create_spinbox(_("History: "), + _(" results"), 'max_entries', default=50, + min_=10, max_=1000000, step=10) + + results_group = QGroupBox(_("Results")) + results_label1 = QLabel(_("Results are stored here:")) + results_label1.setWordWrap(True) + + # Warning: do not try to regroup the following QLabel contents with + # widgets above -- this string was isolated here in a single QLabel + # on purpose: to fix Issue 863 + results_label2 = QLabel(PylintWidget.DATAPATH) + + results_label2.setTextInteractionFlags(Qt.TextSelectableByMouse) + results_label2.setWordWrap(True) + + settings_layout = QVBoxLayout() + settings_layout.addWidget(save_box) + settings_group.setLayout(settings_layout) + + hist_layout = QVBoxLayout() + hist_layout.addWidget(hist_label1) + hist_layout.addWidget(hist_spin) + hist_group.setLayout(hist_layout) + + results_layout = QVBoxLayout() + results_layout.addWidget(results_label1) + results_layout.addWidget(results_label2) + results_group.setLayout(results_layout) + + vlayout = QVBoxLayout() + vlayout.addWidget(settings_group) + vlayout.addWidget(hist_group) + vlayout.addWidget(results_group) + vlayout.addStretch(1) + self.setLayout(vlayout) + + +class Pylint(PylintWidget, SpyderPluginMixin): + """Python source code analysis based on pylint""" + CONF_SECTION = 'pylint' + CONFIGWIDGET_CLASS = PylintConfigPage + edit_goto = Signal(str, int, str) + + def __init__(self, parent=None): + PylintWidget.__init__(self, parent=parent, + max_entries=self.get_option('max_entries', 50)) + SpyderPluginMixin.__init__(self, parent) + + # Initialize plugin + self.initialize_plugin() + + #------ SpyderPluginWidget API -------------------------------------------- + def get_plugin_title(self): + """Return widget title""" + return _("Static code analysis") + + def get_plugin_icon(self): + """Return widget icon""" + path = osp.join(self.PLUGIN_PATH, self.IMG_PATH) + return ima.icon('pylint', icon_path=path) + + def get_focus_widget(self): + """ + Return the widget to give focus to when + this plugin's dockwidget is raised on top-level + """ + return self.treewidget + + def get_plugin_actions(self): + """Return a list of actions related to plugin""" + # Font + history_action = create_action(self, _("History..."), + None, ima.icon('history'), + _("Set history maximum entries"), + triggered=self.change_history_depth) + self.treewidget.common_actions += (None, history_action) + return [] + + def on_first_registration(self): + """Action to be performed on first plugin registration""" + self.main.tabify_plugins(self.main.help, self) + self.dockwidget.hide() + + def register_plugin(self): + """Register plugin in Spyder's main window""" + self.edit_goto.connect(self.main.editor.load) + self.redirect_stdio.connect(self.main.redirect_internalshell_stdio) + self.main.add_dockwidget(self) + + pylint_act = create_action(self, _("Run static code analysis"), + triggered=self.run_pylint) + pylint_act.setEnabled(PYLINT_PATH is not None) + self.register_shortcut(pylint_act, context="Pylint", + name="Run analysis") + + self.main.source_menu_actions += [None, pylint_act] + self.main.editor.pythonfile_dependent_actions += [pylint_act] + + def refresh_plugin(self): + """Refresh pylint widget""" + self.remove_obsolete_items() + + def closing_plugin(self, cancelable=False): + """Perform actions before parent main window is closed""" + return True + + def apply_plugin_settings(self, options): + """Apply configuration file's plugin settings""" + # The history depth option will be applied at + # next Spyder startup, which is soon enough + pass + + #------ Public API -------------------------------------------------------- + @Slot() + def change_history_depth(self): + "Change history max entries""" + depth, valid = QInputDialog.getInt(self, _('History'), + _('Maximum entries'), + self.get_option('max_entries'), + 10, 10000) + if valid: + self.set_option('max_entries', depth) + + @Slot() + def run_pylint(self): + """Run pylint code analysis""" + if self.get_option('save_before', True)\ + and not self.main.editor.save(): + return + self.analyze( self.main.editor.get_current_filename() ) + + def analyze(self, filename): + """Reimplement analyze method""" + if self.dockwidget and not self.ismaximized: + self.dockwidget.setVisible(True) + self.dockwidget.setFocus() + self.dockwidget.raise_() + PylintWidget.analyze(self, filename) diff -Nru spyder-2.3.8+dfsg1/spyder_pylint/widgets/pylintgui.py spyder-3.0.2+dfsg1/spyder_pylint/widgets/pylintgui.py --- spyder-2.3.8+dfsg1/spyder_pylint/widgets/pylintgui.py 1970-01-01 00:00:00.000000000 +0000 +++ spyder-3.0.2+dfsg1/spyder_pylint/widgets/pylintgui.py 2016-11-16 02:30:06.000000000 +0000 @@ -0,0 +1,515 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +"""Pylint widget""" + +# pylint: disable=C0103 +# pylint: disable=R0903 +# pylint: disable=R0911 +# pylint: disable=R0201 + +# Standard library imports +from __future__ import print_function, with_statement +import os +import os.path as osp +import re +import sys +import time + +# Third party imports +from qtpy.compat import getopenfilename +from qtpy.QtCore import QByteArray, QProcess, QTextCodec, Signal, Slot +from qtpy.QtWidgets import (QHBoxLayout, QLabel, QMessageBox, QTreeWidgetItem, + QVBoxLayout, QWidget) + +# Local imports +from spyder import dependencies +from spyder.config.base import get_conf_path, get_translation +from spyder.py3compat import getcwd, pickle, PY3, to_text_string +from spyder.utils import icon_manager as ima +from spyder.utils import programs +from spyder.utils.encoding import to_unicode_from_fs +from spyder.utils.qthelpers import create_toolbutton +from spyder.widgets.comboboxes import (is_module_or_package, + PythonModulesComboBox) +from spyder.widgets.onecolumntree import OneColumnTree +from spyder.widgets.variableexplorer.texteditor import TextEditor + + +# This is needed for testing this module as a stand alone script +try: + _ = get_translation("pylint", "spyder_pylint") +except KeyError as error: + import gettext + _ = gettext.gettext + + +PYLINT = 'pylint' +if PY3: + if programs.find_program('pylint3'): + PYLINT = 'pylint3' + elif programs.find_program('python3-pylint'): + PYLINT = 'python3-pylint' + + +locale_codec = QTextCodec.codecForLocale() +PYLINT_PATH = programs.find_program(PYLINT) + + +def get_pylint_version(): + """Return pylint version""" + if PYLINT_PATH is None: + return + cwd = osp.dirname(PYLINT_PATH) + args = ['--version'] + if os.name == 'nt': + cmd = ' '.join([PYLINT] + args) + process = programs.run_shell_command(cmd, cwd=cwd) + else: + process = programs.run_program(PYLINT, args, cwd=cwd) + lines = to_unicode_from_fs(process.stdout.read()).splitlines() + if lines: + regex = '({0}*|pylint-script.py) ([0-9\.]*)'.format(PYLINT) + match = re.match(regex, lines[0]) + if match is not None: + return match.groups()[1] + + +PYLINT_REQVER = '>=0.25' +PYLINT_VER = get_pylint_version() +dependencies.add("pylint", _("Static code analysis"), + required_version=PYLINT_REQVER, installed_version=PYLINT_VER) + + +#TODO: display results on 3 columns instead of 1: msg_id, lineno, message +class ResultsTree(OneColumnTree): + def __init__(self, parent): + OneColumnTree.__init__(self, parent) + self.filename = None + self.results = None + self.data = None + self.set_title('') + + def activated(self, item): + """Double-click event""" + data = self.data.get(id(item)) + if data is not None: + fname, lineno = data + self.parent().edit_goto.emit(fname, lineno, '') + + def clicked(self, item): + """Click event""" + self.activated(item) + + def clear_results(self): + self.clear() + self.set_title('') + + def set_results(self, filename, results): + self.filename = filename + self.results = results + self.refresh() + + def refresh(self): + title = _('Results for ')+self.filename + self.set_title(title) + self.clear() + self.data = {} + # Populating tree + results = ((_('Convention'), + ima.icon('convention'), self.results['C:']), + (_('Refactor'), + ima.icon('refactor'), self.results['R:']), + (_('Warning'), + ima.icon('warning'), self.results['W:']), + (_('Error'), + ima.icon('error'), self.results['E:'])) + for title, icon, messages in results: + title += ' (%d message%s)' % (len(messages), + 's' if len(messages)>1 else '') + title_item = QTreeWidgetItem(self, [title], QTreeWidgetItem.Type) + title_item.setIcon(0, icon) + if not messages: + title_item.setDisabled(True) + modules = {} + for module, lineno, message, msg_id in messages: + basename = osp.splitext(osp.basename(self.filename))[0] + if not module.startswith(basename): + # Pylint bug + i_base = module.find(basename) + module = module[i_base:] + dirname = osp.dirname(self.filename) + if module.startswith('.') or module == basename: + modname = osp.join(dirname, module) + else: + modname = osp.join(dirname, *module.split('.')) + if osp.isdir(modname): + modname = osp.join(modname, '__init__') + for ext in ('.py', '.pyw'): + if osp.isfile(modname+ext): + modname = modname + ext + break + if osp.isdir(self.filename): + parent = modules.get(modname) + if parent is None: + item = QTreeWidgetItem(title_item, [module], + QTreeWidgetItem.Type) + item.setIcon(0, ima.icon('python')) + modules[modname] = item + parent = item + else: + parent = title_item + if len(msg_id) > 1: + text = "[%s] %d : %s" % (msg_id, lineno, message) + else: + text = "%d : %s" % (lineno, message) + msg_item = QTreeWidgetItem(parent, [text], QTreeWidgetItem.Type) + msg_item.setIcon(0, ima.icon('arrow')) + self.data[id(msg_item)] = (modname, lineno) + + +class PylintWidget(QWidget): + """ + Pylint widget + """ + DATAPATH = get_conf_path('pylint.results') + VERSION = '1.1.0' + redirect_stdio = Signal(bool) + + def __init__(self, parent, max_entries=100): + QWidget.__init__(self, parent) + + self.setWindowTitle("Pylint") + + self.output = None + self.error_output = None + + self.max_entries = max_entries + self.rdata = [] + if osp.isfile(self.DATAPATH): + try: + data = pickle.loads(open(self.DATAPATH, 'rb').read()) + if data[0] == self.VERSION: + self.rdata = data[1:] + except (EOFError, ImportError): + pass + + self.filecombo = PythonModulesComboBox(self) + if self.rdata: + self.remove_obsolete_items() + self.filecombo.addItems(self.get_filenames()) + + self.start_button = create_toolbutton(self, icon=ima.icon('run'), + text=_("Analyze"), + tip=_("Run analysis"), + triggered=self.start, text_beside_icon=True) + self.stop_button = create_toolbutton(self, + icon=ima.icon('stop'), + text=_("Stop"), + tip=_("Stop current analysis"), + text_beside_icon=True) + self.filecombo.valid.connect(self.start_button.setEnabled) + self.filecombo.valid.connect(self.show_data) + + browse_button = create_toolbutton(self, icon=ima.icon('fileopen'), + tip=_('Select Python file'), + triggered=self.select_file) + + self.ratelabel = QLabel() + self.datelabel = QLabel() + self.log_button = create_toolbutton(self, icon=ima.icon('log'), + text=_("Output"), + text_beside_icon=True, + tip=_("Complete output"), + triggered=self.show_log) + self.treewidget = ResultsTree(self) + + hlayout1 = QHBoxLayout() + hlayout1.addWidget(self.filecombo) + hlayout1.addWidget(browse_button) + hlayout1.addWidget(self.start_button) + hlayout1.addWidget(self.stop_button) + + hlayout2 = QHBoxLayout() + hlayout2.addWidget(self.ratelabel) + hlayout2.addStretch() + hlayout2.addWidget(self.datelabel) + hlayout2.addStretch() + hlayout2.addWidget(self.log_button) + + layout = QVBoxLayout() + layout.addLayout(hlayout1) + layout.addLayout(hlayout2) + layout.addWidget(self.treewidget) + self.setLayout(layout) + + self.process = None + self.set_running_state(False) + + if PYLINT_PATH is None: + for widget in (self.treewidget, self.filecombo, + self.start_button, self.stop_button): + widget.setDisabled(True) + if os.name == 'nt' \ + and programs.is_module_installed("pylint"): + # Pylint is installed but pylint script is not in PATH + # (AFAIK, could happen only on Windows) + text = _('Pylint script was not found. Please add "%s" to PATH.') + text = to_text_string(text) % osp.join(sys.prefix, "Scripts") + else: + text = _('Please install pylint:') + url = 'http://www.logilab.fr' + text += ' %s' % (url, url) + self.ratelabel.setText(text) + else: + self.show_data() + + def analyze(self, filename): + if PYLINT_PATH is None: + return + filename = to_text_string(filename) # filename is a QString instance + self.kill_if_running() + index, _data = self.get_data(filename) + if index is None: + self.filecombo.addItem(filename) + self.filecombo.setCurrentIndex(self.filecombo.count()-1) + else: + self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) + self.filecombo.selected() + if self.filecombo.is_valid(): + self.start() + + @Slot() + def select_file(self): + self.redirect_stdio.emit(False) + filename, _selfilter = getopenfilename(self, _("Select Python file"), + getcwd(), _("Python files")+" (*.py ; *.pyw)") + self.redirect_stdio.emit(True) + if filename: + self.analyze(filename) + + def remove_obsolete_items(self): + """Removing obsolete items""" + self.rdata = [(filename, data) for filename, data in self.rdata + if is_module_or_package(filename)] + + def get_filenames(self): + return [filename for filename, _data in self.rdata] + + def get_data(self, filename): + filename = osp.abspath(filename) + for index, (fname, data) in enumerate(self.rdata): + if fname == filename: + return index, data + else: + return None, None + + def set_data(self, filename, data): + filename = osp.abspath(filename) + index, _data = self.get_data(filename) + if index is not None: + self.rdata.pop(index) + self.rdata.insert(0, (filename, data)) + self.save() + + def save(self): + while len(self.rdata) > self.max_entries: + self.rdata.pop(-1) + pickle.dump([self.VERSION]+self.rdata, open(self.DATAPATH, 'wb'), 2) + + @Slot() + def show_log(self): + if self.output: + TextEditor(self.output, title=_("Pylint output"), + readonly=True, size=(700, 500)).exec_() + + @Slot() + def start(self): + filename = to_text_string(self.filecombo.currentText()) + + self.process = QProcess(self) + self.process.setProcessChannelMode(QProcess.SeparateChannels) + self.process.setWorkingDirectory(osp.dirname(filename)) + self.process.readyReadStandardOutput.connect(self.read_output) + self.process.readyReadStandardError.connect( + lambda: self.read_output(error=True)) + self.process.finished.connect(lambda ec, es=QProcess.ExitStatus: + self.finished(ec, es)) + self.stop_button.clicked.connect(self.process.kill) + + self.output = '' + self.error_output = '' + + plver = PYLINT_VER + if plver is not None: + if plver.split('.')[0] == '0': + p_args = ['-i', 'yes'] + else: + # Option '-i' (alias for '--include-ids') was removed in pylint + # 1.0 + p_args = ["--msg-template='{msg_id}:{line:3d},"\ + "{column}: {obj}: {msg}"] + p_args += [osp.basename(filename)] + else: + p_args = [osp.basename(filename)] + self.process.start(PYLINT_PATH, p_args) + + running = self.process.waitForStarted() + self.set_running_state(running) + if not running: + QMessageBox.critical(self, _("Error"), + _("Process failed to start")) + + def set_running_state(self, state=True): + self.start_button.setEnabled(not state) + self.stop_button.setEnabled(state) + + def read_output(self, error=False): + if error: + self.process.setReadChannel(QProcess.StandardError) + else: + self.process.setReadChannel(QProcess.StandardOutput) + qba = QByteArray() + while self.process.bytesAvailable(): + if error: + qba += self.process.readAllStandardError() + else: + qba += self.process.readAllStandardOutput() + text = to_text_string( locale_codec.toUnicode(qba.data()) ) + if error: + self.error_output += text + else: + self.output += text + + def finished(self, exit_code, exit_status): + self.set_running_state(False) + if not self.output: + if self.error_output: + QMessageBox.critical(self, _("Error"), self.error_output) + print("pylint error:\n\n" + self.error_output, file=sys.stderr) + return + + # Convention, Refactor, Warning, Error + results = {'C:': [], 'R:': [], 'W:': [], 'E:': []} + txt_module = '************* Module ' + + module = '' # Should not be needed - just in case something goes wrong + for line in self.output.splitlines(): + if line.startswith(txt_module): + # New module + module = line[len(txt_module):] + continue + # Supporting option include-ids: ('R3873:' instead of 'R:') + if not re.match('^[CRWE]+([0-9]{4})?:', line): + continue + i1 = line.find(':') + if i1 == -1: + continue + msg_id = line[:i1] + i2 = line.find(':', i1+1) + if i2 == -1: + continue + line_nb = line[i1+1:i2].strip() + if not line_nb: + continue + line_nb = int(line_nb.split(',')[0]) + message = line[i2+1:] + item = (module, line_nb, message, msg_id) + results[line[0]+':'].append(item) + + # Rate + rate = None + txt_rate = 'Your code has been rated at ' + i_rate = self.output.find(txt_rate) + if i_rate > 0: + i_rate_end = self.output.find('/10', i_rate) + if i_rate_end > 0: + rate = self.output[i_rate+len(txt_rate):i_rate_end] + + # Previous run + previous = '' + if rate is not None: + txt_prun = 'previous run: ' + i_prun = self.output.find(txt_prun, i_rate_end) + if i_prun > 0: + i_prun_end = self.output.find('/10', i_prun) + previous = self.output[i_prun+len(txt_prun):i_prun_end] + + + filename = to_text_string(self.filecombo.currentText()) + self.set_data(filename, (time.localtime(), rate, previous, results)) + self.output = self.error_output + self.output + self.show_data(justanalyzed=True) + + def kill_if_running(self): + if self.process is not None: + if self.process.state() == QProcess.Running: + self.process.kill() + self.process.waitForFinished() + + def show_data(self, justanalyzed=False): + if not justanalyzed: + self.output = None + self.log_button.setEnabled(self.output is not None \ + and len(self.output) > 0) + self.kill_if_running() + filename = to_text_string(self.filecombo.currentText()) + if not filename: + return + + _index, data = self.get_data(filename) + if data is None: + text = _('Source code has not been rated yet.') + self.treewidget.clear_results() + date_text = '' + else: + datetime, rate, previous_rate, results = data + if rate is None: + text = _('Analysis did not succeed ' + '(see output for more details).') + self.treewidget.clear_results() + date_text = '' + else: + text_style = "%s " + rate_style = "%s" + prevrate_style = "%s" + color = "#FF0000" + if float(rate) > 5.: + color = "#22AA22" + elif float(rate) > 3.: + color = "#EE5500" + text = _('Global evaluation:') + text = (text_style % text)+(rate_style % (color, + ('%s/10' % rate))) + if previous_rate: + text_prun = _('previous run:') + text_prun = ' (%s %s/10)' % (text_prun, previous_rate) + text += prevrate_style % text_prun + self.treewidget.set_results(filename, results) + date = to_text_string(time.strftime("%d %b %Y %H:%M", datetime), + encoding='utf8') + date_text = text_style % date + + self.ratelabel.setText(text) + self.datelabel.setText(date_text) + + +#============================================================================== +# Tests +#============================================================================== +def test(): + """Run pylint widget test""" + from spyder.utils.qthelpers import qapplication + app = qapplication(test_time=20) + widget = PylintWidget(None) + widget.resize(640, 480) + widget.show() + widget.analyze(__file__) + sys.exit(app.exec_()) + + +if __name__ == '__main__': + test()