spyder-2.3.8/0000755000000000000000000000000012626531443011536 5ustar rootrootspyder-2.3.8/spyderplugins/0000755000000000000000000000000012626531443014446 5ustar rootrootspyder-2.3.8/spyderplugins/locale/0000755000000000000000000000000012626531443015705 5ustar rootrootspyder-2.3.8/spyderplugins/locale/es/0000755000000000000000000000000012626531443016314 5ustar rootrootspyder-2.3.8/spyderplugins/locale/es/LC_MESSAGES/0000755000000000000000000000000012626531443020101 5ustar rootrootspyder-2.3.8/spyderplugins/locale/es/LC_MESSAGES/p_profiler.mo0000664000000000000000000000143212626055324022600 0ustar rootrootDlR] ProfileProfilerProfiler plugin results (the output of python's profile/cProfile) are stored here:ResultsProject-Id-Version: 2.1 POT-Creation-Date: 2015-10-04 10:37+COT PO-Revision-Date: 2012-06-25 21:59-0500 Last-Translator: Carlos Cordoba Language-Team: Python Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated-By: pygettext.py 1.5 X-Poedit-Language: Spanish X-Poedit-SourceCharset: utf-8 X-Poedit-Basepath: ../../../../ Perfilar (Profile)Perfilador (Profiler)Los resultados del perfilador (es decir la salida de profile o cProfile) son guardados aquí:Resultadosspyder-2.3.8/spyderplugins/locale/es/LC_MESSAGES/p_pylint.mo0000664000000000000000000000216112626055324022275 0ustar rootroot t ",4Mf5 l x (#H(    resultsHistoryHistory: ResultsResults are stored here:Run static code analysisSave file before analyzing itSettingsStatic code analysisThe following option will be applied at next startup.Project-Id-Version: 2.1 POT-Creation-Date: 2015-10-04 10:37+COT PO-Revision-Date: 2014-05-18 16:44-0500 Last-Translator: Carlos Cordoba Language-Team: Python Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated-By: pygettext.py 1.5 X-Poedit-SourceCharset: utf-8 X-Poedit-Basepath: ../../../../ X-Generator: Poedit 1.5.4 resultadosHistorialGuardarResultadosLos resultados se guardan en:Realizar análisis estático del códigoGuardar archivo antes de analizarloAjustesAnálisis estático del códigoLa siguiente opción será aplicada la próxima vez que se inicie Spyderspyder-2.3.8/spyderplugins/locale/es/LC_MESSAGES/p_breakpoints.mo0000664000000000000000000000140412626055324023276 0ustar rootroot\  G/w' BreakpointsClear breakpoints in all filesClear this breakpointConditionFileLineList breakpointsProject-Id-Version: 2.2 POT-Creation-Date: 2015-10-04 10:37+COT PO-Revision-Date: 2013-05-07 17:37-0500 Last-Translator: Carlos Cordoba Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated-By: pygettext.py 1.5 Puntos de interrupciónBorrar los puntos de todos los archivosEliminar este puntoCondiciónArchivoLíneaListar puntos de interrupciónspyder-2.3.8/spyderplugins/locale/fr/0000755000000000000000000000000012626531443016314 5ustar rootrootspyder-2.3.8/spyderplugins/locale/fr/LC_MESSAGES/0000755000000000000000000000000012626531443020101 5ustar rootrootspyder-2.3.8/spyderplugins/locale/fr/LC_MESSAGES/p_profiler.mo0000664000000000000000000000444612626055324022610 0ustar rootroot)  # '-2`gvR+ 3@Uk* +  ,MTpw + 5")!<^ gq6 "%Bhq: +        CallsCollapse one level upErrorExpand one level downFile:lineFile:line where function is definedFunction/ModuleLocal TimeLocal time in function (not in sub-functions)OutputPlease installProcess failed to startProfileProfilerProfiler outputProfiler plugin results (the output of python's profile/cProfile) are stored here:Profiling, please wait...Python scriptsResultsRun profilerSelect Python scriptShow program's outputSorting data, please wait...StopStop current profilingTime in function (including sub-functions)Total TimeTotal number of calls (including recursion)recursionthe Python profiler modulesProject-Id-Version: 2.1 POT-Creation-Date: 2015-10-04 10:37+COT PO-Revision-Date: 2011-04-11 21:41+2 Last-Translator: Pierre Raybaut Language-Team: Python Language: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: utf-8 Generated-By: pygettext.py 1.5 AppelsReplier l'arbre d'un niveauErreurDéplier l'arbre d'un niveauFichier:ligneFichier:ligne de définition de la fonctionFonction/ModuleDurée localeTemps écoulé dans la fonction (hors sous-fonctions)SortieVeuillez installerLe processus n'a pas pu démarrerProfilerProfileurSortie du profileurLes résultats du profileur Python sont stockés ici :Profilage en cours...Scripts PythonRésultatsExécuter le profileurSélectionner un script PythonAfficher la sortie du programmeClassement des résultats en cours...ArrêterArrêter le processus en coursTemps écoulé dans la fonction (sous-fonctions comprises)Durée totaleNombre total d'appels (récursion comprise)récursionles profileurs Pythonspyder-2.3.8/spyderplugins/locale/fr/LC_MESSAGES/p_pylint.mo0000664000000000000000000000504212626055324022276 0ustar rootroot#4/L 7JR bms   5 4AJR k x#-25H~   A  (/ E P ^l/!?. > L"Wz$*5 I -Q    ?   #"   ! resultsAnalysis did not succeed (see output for more details).AnalyzeComplete outputConventionErrorGlobal evaluation:HistoryHistory...History: Maximum entriesOutputPlease install pylint:Process failed to startPylint outputPylint script was not found. Please add "%s" to PATH.Python filesRefactorResultsResults are stored here:Results for Run analysisRun static code analysisSave file before analyzing itSelect Python fileSet history maximum entriesSettingsSource code has not been rated yet.Static code analysisStopStop current analysisThe following option will be applied at next startup.Warningprevious run:Project-Id-Version: 2.1 POT-Creation-Date: 2015-10-04 10:37+COT PO-Revision-Date: 2011-04-11 21:41+2 Last-Translator: Pierre Raybaut Language-Team: Python Language: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: utf-8 Generated-By: pygettext.py 1.5 résultatsL'analyse n'a pas réussi (voir la sortie pour plus de détails).AnalyserSortie complèteConventionErreurÉvaluation globale :HistoriqueHistorique...Historique : Nombre maximum d'entréesSortieMerci d'installer au préalable pylint :Le processus n'a pas pu démarrerAnalyse PylintLe script pylint est introuvable. Merci d'ajouter "%s" au PATH.Fichiers PythonFactorisationRésultatsLes résultats sont stockés ici :Résultats pour Analyser le code sourceDémarrer l'analyse de code statiqueEnregistrer le fichier avant de l'analyserSélectionner un fichier PythonModifier le nombre d'entrées maximum de l'historiqueOptionsLe code source n'a pas encore été analysé.Analyse de code statiqueArrêterArrêter l'analyse en coursL'option suivante ne sera appliquée qu'au prochain démarrage.Avertissementanalyse précédente :spyder-2.3.8/spyderplugins/locale/fr/LC_MESSAGES/p_breakpoints.mo0000664000000000000000000000137212626055324023302 0ustar rootroot\  H/x# BreakpointsClear breakpoints in all filesClear this breakpointConditionFileLineList breakpointsProject-Id-Version: PACKAGE VERSION POT-Creation-Date: 2015-10-04 10:37+COT PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: utf-8 Generated-By: pygettext.py 1.5 Points d'arrêtSupprimer tous les points d'arrêtsSupprimer ce point d'arrêtConditionFichierLigneListe des points d'arrêtspyder-2.3.8/spyderplugins/io_hdf5.py0000664000000000000000000000571712626055324016350 0ustar rootroot# -*- 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")) spyder-2.3.8/spyderplugins/io_dicom.py0000664000000000000000000000136212626055324016605 0ustar rootroot# -*- 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 spyder-2.3.8/spyderplugins/widgets/0000755000000000000000000000000012626531443016114 5ustar rootrootspyder-2.3.8/spyderplugins/widgets/pylintgui.py0000664000000000000000000004634012626055324020522 0ustar rootroot# -*- 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() spyder-2.3.8/spyderplugins/widgets/profilergui.py0000664000000000000000000005557312626055324021035 0ustar rootroot# -*- 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() spyder-2.3.8/spyderplugins/widgets/__init__.py0000664000000000000000000000056712626055324020236 0ustar rootroot# -*- 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) """ spyder-2.3.8/spyderplugins/widgets/breakpointsgui.py0000664000000000000000000002120012626055324021510 0ustar rootroot# -*- 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() spyder-2.3.8/spyderplugins/p_breakpoints.py0000664000000000000000000001152412626055324017664 0ustar rootroot# -*- 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 spyder-2.3.8/spyderplugins/p_profiler.py0000664000000000000000000001272612626055324017172 0ustar rootroot# -*- 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 spyder-2.3.8/spyderplugins/p_pylint.py0000664000000000000000000001551412626055324016665 0ustar rootroot# -*- 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 spyder-2.3.8/spyderplugins/__init__.py0000664000000000000000000000000012626055324016546 0ustar rootrootspyder-2.3.8/spyderplugins/images/0000755000000000000000000000000012626531443015713 5ustar rootrootspyder-2.3.8/spyderplugins/images/profiler.png0000664000000000000000000000256012626055324020247 0ustar rootrootPNG  IHDRw=7IDATHSSWqNL:LiY%X@(*-/ {@ H]Bt[ز (۷f ;{s߲eK4[1HďKٿͱqNOfxadOCu)B:)q AWXxvr!fV܂ζv\4='1?7<ܸ 4,Q֢#rm;٬R7mƬӉۓ7QNHDPF )VY4=|bh9H˛ZNL GJcEJ)IP]^lBFCG$eˉ' O Jqk:r2Qq22D|3NuX:(bkbƆm(ܣQB>h% sa8JIEF>C&ب xboA%%XZqtBJمbm ǃil-\%*-Tj 6`>|EBU2 :U-_,pv2aa4$%U%ԇ_UU )&; VYI)跲(ř(S2hQt M{^ZPgd2KMGrF&ȣv:Z%C %O(Dz|*t\"X YϞjU! $(j)6ͱXuʁtﮞy58ϊq](/v諐4 ?O Z{HOjT- rXz},; z6{u=66-þ&fan8 /?~aS (َ]}t3:bf0yTӞڑUþp+YhȒq(eI|7wj2| S#P'n֙,Av'۶zS,vJqd,|qtO*qs4=.*Nikz+ð h UOmΥ'y%c4b0uhlL-EI{Qu8;F"z8G9i$oXerݓ2zУmr-WPŜA9s%]X&/DEFfЬXQQF9NZp(/z+*Wk8ҥu,9:w2BnVBfVC/?zN4lniTE,Q|fwoj# M :|4i62^qZ4nhZ垢YZZ665b@j@qAT 44\Dpp&|>[\祀Pj3Ceew;4_dkCVv="Rk븊(@Wi\țh/rX)4 341YM䔔 B/5bJZN؆QQa:%Rgz<xwUjMo46s&&&_V3,++E@vH)RIENDB`spyder-2.3.8/spyderlib/0000755000000000000000000000000012626531443013533 5ustar rootrootspyder-2.3.8/spyderlib/start_app.py0000664000000000000000000001070512626055324016106 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/mac_stylesheet.qss0000664000000000000000000000621712566665770017322 0ustar rootroot/* * 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; } spyder-2.3.8/spyderlib/locale/0000755000000000000000000000000012626531443014772 5ustar rootrootspyder-2.3.8/spyderlib/locale/es/0000755000000000000000000000000012626531443015401 5ustar rootrootspyder-2.3.8/spyderlib/locale/es/LC_MESSAGES/0000755000000000000000000000000012626531443017166 5ustar rootrootspyder-2.3.8/spyderlib/locale/es/LC_MESSAGES/spyderlib.mo0000664000000000000000000027637712626055324021547 0ustar rootroot d e@@V AV MVXV^VgVnVrV V VgV W W W.W5W ;WFWLW [WfWoW uWW WW WWWWWWWWBW!X>XGXWPXbX< YxHY4Y9YA0ZBrZDZ1Z6,[;c[7[c[I;\B\;\7]C<];]=]8]t3^!^^5j__$a.ab!b('bPbWb/^bb bbb2bbcc)cucu huru vuuuuu uuu v"v3)v]v-wx(x y*y=yLy z z'z >zLziz\~zzz { {1{P{Zk{*{7{@)|j||| |)|&|, }7}N}f}y} ~} }}}} }}}~>~ ^~l~ ~~~~~~ ~~ ~ ~.3C T u  MmZaz  ̀ـ3D ] k&w* /7<D, 2@Vm+ ݃ # ( 2<DTp}x&{l%Ԉ ۈ  -E [ir Ήԉۉ - FPf0o) ʊԊ !/F[X[=`NI<6 = IT e p| @?΍*398r Ɏڎ  0>Yk׏ &B G T>a  Ɛ Ӑސ 0!OTqƑ,Α=   Óϓד ߓ   * 6BQ itygz " ͕&ڕ  .DUfn! ƖіՖ /Ǘ ;Tk|:՘0 I S a n{ Bޙ  , 7BU o }Ú$ؚ&+˛  M22RɝC`3e "ў!'@Wm ~ ğ ҟݟ {Р  ,Mb s  %ߡ1!&<E%Y+Ţ͢4*=N bp5w ģУ  )5*D o }Ƥͤ -!Jl &ȥץۥ - AL`w75/ 8 DR p} ,Dʧ  *=$L q|E)ڨ  (9 MZs"`)"L$[  ƪت;0 AM.gQ'*JUh#ܬ +9PLe|4/^dîخ3M b0nȯ\ׯ94n"Ӱ 0Hcy Ʊ)߱ #!E` { !˲Ӳ ڲ!%*P#a lc~) 1>pF ¶ Ƕ Ѷ,ݶ 4EM!\~+ C8G]:m Ÿɸ۸*%4 = GS n z=:ùfŻ1,^N8H+x-Ry;:K`(sie-}S[N[ ' oWnGHS>S}T  )07 @JZs0"Ux &" %7+c9{<%@!Xz},2 9G Yc|<   } sUI  #(-29=F KYo  ) 3: R\`fk  ) @N  & 4 DfQ +29 @JQ c n x :"  i_=%/AROoO>@NDHoZSG<GYL&LsI (7?*''OV5] J ,9Ol S1,J.w -/  '6I=aAG.)<X,h q| i u"9"A39u5B(DIRZip% &$2LTgpx *M49OX,o%@ *$O,n:$3&KrC"5I`p %.1EYDn$4&= d p }  ! (1F OEY% !8,O3|#*'(+P|  KZ*v0LMQ   '%Axg0TPf3* % 3@%G*m (+C(=#Tx(2:O .9h? :H 5 $  (   + 0H 2y      ! 5 I _ #n    :   ! B  S ^ !{       = 1 J d     g "!  -;L]ew&. &<.c% )'-<Wp 8  ' = IVh q~'9;1 .B'X(&7S[d u , 5#.Y equ6hC@  #1 ESb jOuM '9H@Q    %# I Q W p  "   % !&)!#P!t!$!&!!!!G"P"Y"l"" """"$"%#(A#oj##<# $ $$$%J %T%\%% && &'&6& I& V& a&o&v& & & & & & &&&' ' ,'6'U'ps'l'Q((`(%( (0(())*)H)c))))#)) ) )***+9+M+S+o+(+"+++',D,,q,,,, , --'- 6- D-e-_u---..'. 9. G.R.o.. ... .2./ /- /N/k/4/310 e0o0 00X0:04:2go2S2+3743 l3 z3#3(3%3 344.4K4i4 44444 44 5535855556/6H6e6u66 6(6666767K7 Z77g7797778%#81I8,{888 88J8A9\9o9999H9: :-:C: _: i:"v:::1:::;!&;*H; s;; ;;; ;;";(< H<i< {<5<<<*<=5=K=a=="====>>B,>?o>>>>>' ?1?G?e?7y?T? @@@-@G@)b@@!@Z@0A JAXAnA~AAAAA1A B'Bj;B,BB(B C"CACTCeC-CYC D!D 5D=VDD<D4D:E WE[xE"E$EF48FmF@F*FmFeGO Hl[H,H6H$,IQIYI3nIIIDIJ2JMJzhJAJ(%KNK8iK"KK.KL+L?L[L2yLLLLLM'.M4VM*M,M*M)N8NJNONWN `N nN(zNNNNN&NO8 OYO6oOO OPrPPRiR|RR RRRRkS{SSS5SSTT5TITQT)eTTTT0T UMUdUsUUKUUV V!V>V+YVVVV V V+VVWUWRmW*WXuY5@ZvZNZ:N[[.\=\P\]]A^>^l _ v_'``pan-bb4bXb`EcSccd30dddPe}+fsf{gLgagIHh/hklZl?l n nn #n.n7n@n Wnanrn%nnnn:nn6o o oo4o0p4Dp ypp*pp'p#p;qXqzvqq r,r*Grrrwr(s0s6s=sPs hs#rs1ss>s tt5tJtdt#u?u\u cumuu1vGv^vqv v vv6wwmxzz z)z 1zk kvD%RK_G&b^E"OQ~\'K"#(<U_ =P v}}6FS)H tR:`Gt 3)TQ88 -([WsB4RZ/\%$V=r[B+zxGm} d9gUx*rcjI@B.w@ u"uhl.|Y@cn!bH&YAwq++Kl`<W~qJocvzD(>t#=H0N_oVkdw -.hC*u64}*'XnldAhlS jO E2u] T30:{{^? 6IWU%i|QYsOC &m|p,p4a NJ\Pt ;meVN?-[*Jy 0;!ZZ 6']1FO$< 19A3C+^o=#`Ls/qaX[4SR5Wpy<2w.,D~TLT yg5~q?3 jxM{c!oN9!biMz)7fe",L@^Md2m& pyZ:f?8YBik79Q$rP/-fasF'g1h0rf8,z|JeP5\]]eL>$;vU)2/V7 jA;({C` aMiE>nD5 Installed Required and entries lines ms%s are currently not supported%s arrays%s editor%s is already running in a separate process. Do you want to kill the process before starting a new one?&Close&Close file&Configure...&Debug&Edit&Edit file&File&Find in files&Find text&Font...&Help&New file...&Open...&Print...&Quit&Replace text&Revert&Run&Run...&Save&Search&Tools&View(Experimental) Editor's code completion, go-to-definition and help(press Enter to edit file)2 spaces4 spaces%s contains mixed end-of-line characters.
Spyder will fix this automatically.%s has been modified outside Spyder.
Do you want to reload it and lose all your changes?%s has been modified.
Do you want to save changes?%s is unavailable (this file may have been removed, moved or renamed outside Spyder).
Do you want to close it?-u is added to the other options you set hereUnable to %s %s

Error message:
%sUnable to assign data to item.

Error message:
%sUnable to create file %s

Error message:
%sUnable to create folder %s

Error message:
%sUnable to find external program.

%sUnable to load '%s'

Error message:
%sUnable to move %s

Error message:
%sUnable to plot data.

Error message:
%sUnable to proceed to next step

Please check your entries.

Error message:
%sUnable to read Pydev project %s

Error message:
%sUnable to rename file %s

Error message:
%sUnable to retrieve data.

Error message:
%sUnable to save array

Error message:
%sUnable to save current workspace

Error message:
%sUnable to save file '%s'

Error message:
%sUnable to save script '%s'

Error message:
%sUnable to show image.

Error message:
%sUnsupported file extension '%s'

Would you like to import it anyway (by selecting a known file format)?Unsupported file type '%s'Warning:
The Python module rope is not installed on this computer: calltips, code completion and go-to-definition features won't be available.Running...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, ...).
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.Warning: changes are applied separately?A Python console failed to start!A project named %s already existsAPI #1API #2API selection for QString and QVariant objects:About %sAbout %s...Activate supportAdd &block commentAdd block comment around current line or selectionAdd pathAdd to PYTHONPATHAdditional featuresAdditional optionsAddress:Advanced SettingsAdvanced settingsAll changes to %s will be lost.
Do you want to revert file from disk?All filesAll files (*)All files (*.*)Always show %s on a first file runAn error ocurred while starting the kernelAnalysisAnimated toolbars and dockwidgetsAre you sure you want to restart the kernel?ArgumentsArguments...Arguments: %sArray editorArray is emptyArrays with more than 3 dimensions are not supportedAsk for confirmation before closingAt startup, Spyder will restore the global directory from last sessionAt startup, the global working directory is:At startup, the global working directory will be the specified pathAttached console window (debugging)Attempts to stop the process. The process may not exit as a result of clicking this button (it is given the chance to prompt the user for any unsaved files, etc).AttributeAutocallAutocall 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: AutomaticAutomatic code completionAutomatic connectionsAutomatic importAutomatic indentation after 'else', 'elif', etc.Automatic insertion of closing quotesAutomatic insertion of colons after 'for', 'if', 'def', etcAutomatic insertion of parentheses, braces and bracketsAutomatically load Pylab and NumPy modulesAutomatically remove trailing spaces when saving filesAutorefreshAxis:BackBackend:Background colorBackground:Batch filesBoldBreakpointBreakpointsBrowseBrowse a search directoryBrowse a working directoryBrowse repositoryBrowse tabsBufferBuffer...Buffer: Buffer: Builtin:C filesC&lose allC&onsolesC++ filesCPU and memory usage info in the status barCPU usage status: requires the `psutil` (>=v0.3) libraryCPU:CSV text filesCancelCarriage return (Mac)Carriage return and line feed (Windows)Case SensitiveCase sensitive code completionCell starts at line %sChange to file base directoryChange to parent directoryChanging backend to Qt for MayaviClass defined at line %sClear all ouputClear breakpoints in all filesClear consoleClear lineClear line or blockClear recent files listClear shellClear shell contents ('cls' command)Clear this listClipboard contentsClose all opened filesClose current fileClose current paneClose current tabClose projectClose this panelClose this windowClose unrelated projectsClose windowCode Introspection/AnalysisCode analysisCode analysis requires pyflakes %s+Collapse allCollapse selectionColor schemeColorize standard error channel using ANSI escape codesColumn min/maxColumn separator:Column:Command WindowCommand line arguments:Command line options:CommentComment current line or selectionComment:Comments:CommitCondition:Configuration filesConflictsConnect to an existing kernelConnecting to kernel...Connection errorConnection info:ConsoleConsole helpContextContinueContinue execution until next breakpointConversion errorConvert end-of-line charactersConvert to Python scriptCopyCopy path to clipboardCopy to clipboardCopy without promptsCould not connect to remote hostCould not open ssh tunnel. The error was: Create a new editor windowCurrent cell:Current line:Current user environment variables...Custom dockwidget margin:Custom window layoutsCutCython/Pyrex filesDark backgroundDataDataFrameDebugDebug fileDebug toolbarDebug with winpdbDebuggingDebugging controlDecide 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.Decide how to render the figures created by this backendDedicated Python consoleDefault (i.e. the same as Spyder's)Default APIDefault PYTHONSTARTUP scriptDefault is 4Default is 6Default is
In [<span class="in-prompt-number">%i</span>]:Default is
Out[<span class="out-prompt-number">%i</span>]:Default libraryDefault working directory is:Definition:DeleteDelete...DictionaryDisplayDisplay balloon tipsDisplay initial bannerDo you really want to delete %s?Do you really want to delete project %s?

Note: project files won't be deleted from disk.Do you really want to remove selected path?Do you really want to rename %s and overwrite the existing file %s?Do you want to close all other consoles connected to the same kernel as this one?Do you want to remove all selected items?Do you want to remove selected item?DoneDuplicateEOLETS_TOOLKIT:EditEdit data in the remote processEdit filename filtersEdit filename filters...Edit itemEdit related projectsEdit template for new modulesEdit toolbarEditorEditor's code completion, go-to-definition and helpEditors are opened in the remote process for NumPy arrays, PIL images, lists, tuples and dictionaries. This avoids transfering large amount of data between the remote process and Spyder (through the socket).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.Empty clipboardEnable 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>Enable UMREnable autorefreshEnable monitorEnabling 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.Enaml filesEncoding:End-of-line charactersEnd-of-lines:Enter key selects completionEnthought Tool SuiteEnthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical user interfaces.Environment variablesEnvironment variables...ErrorExclude all-uppercase referencesExclude capitalized referencesExclude private referencesExclude references to unsupported data types (i.e. which won't be handled/saved correctly)Exclude references which name is uppercaseExclude references which name starts with an underscoreExclude references which name starts with an uppercase characterExclude unsupported data typesExclude:Excluded filenames patternExecutablesExecute in a new dedicated Python consoleExecute in an external System terminalExecute in current Python or IPython consoleExisting Pydev projectExisting Spyder projectExisting directoryExitExit DebugExpand allExpand selectionExternal ToolsExternal editorExternal editor executable path:External editor path...External editor:External modulesFile %s already exists.
Do you want to overwrite it?File explorerFile list managementFile toolbarFile...Files are created in:Files are opened from:FilterFilter:Find &nextFind &previousFind in filesFind textFix automatically and show warning message boxFix indentationFloat formattingFolder %s already exists.Folder name:Folder...Font styleFont: For %s support, please install one of the
following tools:

%sFor performance reasons, changes applied to masked array won't be reflected in array's data (and vice-versa).FormatFormat (%s) is incorrectFormat:Fortran filesFullFullscreen modeFunction defined at line %sGIF imagesGUI backend:GUI-based editor:GeneralGeneral settingsGlobal working directoryGo to cursor positionGo to definitionGo to last edit locationGo to line...Go to line:Go to next code analysis warning/errorGo to next cursor positionGo to previous code analysis warning/errorGo to previous cursor positionGraphicsGraphics backendGreedy completionHeight:HelpHelp...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.Here:Hg repositoryHide advanced optionsHighlight current cellHighlight current lineHighlight matchesHighlight occurences afterHint: press Alt to show acceleratorsHistogramHistoryHistory depth: History logHistory logsHistory...HomeHost nameIDL filesIPythonIPython ConsoleIPython Console integrationIPython consoleIPython documentationIPython notebooksIf enabled, Python source code will be analyzed using pep8, lines that are not following PEP8 style guide will be highlightedIf enabled, Python source code will be analyzed using pyflakes, lines containing errors or warnings will be highlightedIf enabled, pressing Tab will always indent, even when the cursor is not at the beginning of a line (when this option is enabled, code completion may be triggered using the alternate shortcut: Ctrl+Space)If this option is enabled, clicking on an object name (left-click + Ctrl key) will go this object definition (if resolved).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)Ignore API change errors (sip.setapi)ImportImport asImport dataImport directoryImport errorImport existing Pydev projectImport existing projectImport from clipboardImport wizardInclude:Included filenames patternIndentIndent current line or selectionIndentation characters: IndexIndex:Initializing...InlineInline backendInput prompt:InsertInspect current objectInstalled Python modulesInstance:Intelligent backspaceInteractInteract with the Python console after executionInteractive data plotting in the consolesInterfaceInternal consoleInternal console settingsInternal editor:Intro to IPythonIntrospectionInvalid directory pathInvalid file pathInvalid project name.

Name must match the following regular expression:
%sIt seems the kernel died unexpectedly. Use 'Restart kernel' to continue using this console.It was not possible to convert this notebook. The error is: It was not possible to generate rich text help for this object.
Please see it in plain text.It was not possible to remove outputs from this notebook. The error is: It was not possible to run this file in an external terminalItalicJPEG imagesJSON filesJavascript filesJson filesJulia filesKernelKernel %sKernel process is either remote or unspecified. Cannot interruptKernel process is either remote or unspecified. Cannot restart.KeyKey:Keyboard shortcutsKeyword:KillKills the current process, causing it to exit immediatelyLast edit locationLight backgroundLight background (white color)Line %sLine count:Line feed (UNIX)Line:Lines:Link to object definitionLink:ListLoad Spyder sessionLoad session...Loading %s...Loading IPython console...Loading editor...Loading external console...Loading file explorer...Loading history plugin...Loading namespace browser...Loading object inspector...Loading online help...Loading outline explorer...Loading project explorer...LockMATLAB filesMain toolbarMaintain focus in the Editor after running cells or selectionsMaskMasked dataMatched parentheses:Matlab filesMatplotlibMatplotlib documentationMaximize current paneMaximum entriesMaximum line countMaximum number of recent filesMaximum number of recent files...Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows platformsMemory:Merge process standard output/error channelsMerging the output channels of the process means that the standard error won't be written in red anymore, but this has the effect of speeding up display.Method defined at line %sMod1Mod2Mod3Modify how Input and Output prompts are shown in the console.ModuleModule pywin32 was not found.
Please restart this Windows session (not the computer) for changes to take effect.Module or package:Module...MonitorMove downMove to bottomMove to topMove upMove...NSIS filesNameName filters:NewNew fileNew folderNew moduleNew name:New packageNew projectNew project...New to Spyder? Read ourNew windowNextNext cursor positionNext warning/errorNo IPython console is currently available to run %s.

Please open a new one and try again.No Python console is currently selected to run %s.

Please select or open a new Python console and try again.No argumentNo further documentation availableNo source code available.Normal text:Nothing to be imported from clipboard.NumPy arrayNumPy arraysNumPy documentationNumPy reference guideNumPy user guideNumPy zip arraysNumber:Numpy and Scipy documentationObjectObject %s is not picklableObject inspectorOccurence:OffOn %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).One tab per scriptOnline documentationOnline helpOnly used when the format is PNG. Default is 72OpenOpen &command promptOpen &recentOpen IPython connection fileOpen Python console hereOpen a &Python consoleOpen a &terminalOpen a Windows command promptOpen a new IPython console connected to an existing kernelOpen a new consoleOpen a terminal windowOpen an &IPython consoleOpen an IPython consoleOpen command prompt hereOpen fileOpen file as:Open projectOpen sessionOpen terminal hereOpenCL filesOpening this variable can be slow Do you want to continue anyway?Optional DependenciesOptional dependencies...OptionsOutlineOutput prompt:PNG imagesPYTHONPATHPYTHONPATH managerPYTHONSTARTUP replacementPackage name:Package...PanesParentPasswordPassword or ssh key passphrasePastePatch and diff filesPath to connection file or kernel idPath to ssh key filePerfom symbolic operations in the console (e.g. integrals, derivatives, vector calculus, etc) and get the outputs in a beautifully printed style.Perform analysis only when saving filePerform analysis when saving file and everyPermissions:Pickle filesPlain TextPlain text font stylePlease consider installing Sphinx to get documentation rendered in rich text.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).Please install matplotlib or guiqwt.Please note that these changes will be applied only to new Python/IPython consolesPlease note that these changes will be applied only to new consolesPlotPop up internal console when internal errors appearPre&ferencesPreferencesPreferences > Object InspectorPress enter to validate this entryPress enter to validate this pathPreviewPreviousPrevious cursor positionPrevious warning/errorPrint current file...Print preview...Printing...Process failed to startProject ExplorerProject explorerProject name:Project...PromptsPropertiesProvided featuresPyQtPyQt API #1 is the default
API for Python 2. PyQt API #2 is the default API for Python 3 and is compatible with PySide.PyQt4 API ReferencePyQt4 Reference GuidePython ConsolePython Path ManagerPython documentationPython executablePython filesPython help:Python scriptsPython(x,y)Python(x,y) documentation folderPython(x,y) launcherQt (PyQt/PySide)Qt DesignerQt LinguistQt documentationQt examplesQt windows styleQt-Python bindings library selection:Quick referenceQuick switch layout #%d has not yet been defined.QuitR&emove block commentRaw textRe-run &last scriptReal-time code analysis on the EditorReal-time code style analysis on the EditorRecord array fields:RedoRefreshRefresh interval: Refresh list of module names available in PYTHONPATHRefresh periodicallyRegular expressionRelated projectsReload last sessionRemoval errorRemoveRemove comment block around current line or selectionRemove from PYTHONPATHRemove pathRemove references:Remove trailing spacesRenameRename...Render mathematical equationsReplace allReplace stringReplace tab characters by space charactersReplace with:Replace/findReport issue...Reset to default valuesReset window layoutResizeResize rows to contentsResolution:Restart kernelRestart kernel?RestoreRestore current paneRestore original tree layoutRestore pane to its original sizeRevert file from diskRich TextRich text font styleRich text help on the Object InspectorRow separator:RunRun &selection or current lineRun Python scriptRun SettingsRun Settings dialogRun a Python scriptRun a fileRun again last fileRun again this programRun cellRun cell and advanceRun codeRun configurationRun current cell (Ctrl+Enter) [Use #%% to create cells]Run current cell and go to the next one (Shift+Enter)Run current lineRun fileRun script:Run selectionRun selection or current lineRun settingsRun settings for %sRun toolbarRun until current function or method returnsRunning an external system terminal is not supported on platform %s.Sav&e allSaveSave &as...Save Python scriptSave all filesSave all files before running scriptSave arraySave current file as...Save current history log (i.e. all inputs and outputs) in a text fileSave current session and quit applicationSave dataSave data as...Save fileSave history logSave history log...Save sessionSave session and quit...SciPy documentationScroll automatically to last entrySearchSearch canceledSearch in all directories listed in sys.path which are outside the Python installation directorySearch in current directory hg repositorySearch patternSearch recursively in this directorySearch stringSearch text in multiple filesSearch toolbarSelect AllSelect a new fontSelect a run configuration:Select an existing workspace directory, or create a new oneSelect directorySelect fileSelect projects to importSelect projects which are related to %sSelect ssh keySelect the Python interpreter executable binary in which Spyder will run scripts:Set UMR excluded (not reloaded) modulesSet as current console's working directorySet console working directorySet current console (and file explorer) working directory to current script directorySet current working directorySet external editor executable pathSet font styleSet history maximum entriesSet layout %dSet maximum line countSet shell font styleSet the GUI toolkit used by
Matplotlib to show figures (default: Qt4Agg)Set the maximum number of lines of text shown in the console before truncation. Specifying -1 disables it (not recommended!)Set this to detach any
menu from the main windowSet this to open external
Python files in an already running instance (Requires a restart)Set/Clear breakpointSet/Edit conditional breakpointSetting up main window...SettingsShell special commands:Show (read-only) sys.pathShow CPU usage everyShow SourceShow TODO/FIXME/XXX/HINT/TIP/@todo comments listShow absolute pathShow advanced optionsShow all filesShow and edit current user environment variables in Windows registry (i.e. for all sessions)Show and edit environment variables (for current session)Show arrays min/maxShow blank spacesShow code analysis warnings/errorsShow current directory onlyShow elapsed timeShow horizontal scrollbarShow icons and textShow imageShow line numbersShow memory usage everyShow reloaded modules listShow special commentsShow sys.path contentsShow sys.path contents...Show tab barShow todo listShow vertical line afterShow warning when killing running processShow warning/error listShow/hide global variables explorerShow/hide outline explorerShow/hide project explorerSide areas:SiftSizeSize: Skip rows:SmartSort files according to full pathSour&ceSourceSource codeSource toolbarSphinx %s is currently installed.Split horizontallySplit horizontally this editor windowSplit verticallySplit vertically this editor windowSpyder EditorSpyder Internal Console This console is used to report application internal errors and to inspect Spyder internals with the following commands: spy.app, spy.window, dir(spy) Please don't use it to run your code Spyder data filesSpyder 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.Spyder documentationSpyder sessionsSpyder support...Spyder tutorialSsh keyStart searchStartupStatistics on source files only:
(Python, C/C++, Fortran)

%s files.
%s lines of code.Status barStepStep IntoStep ReturnStep into function or method of current lineStopStop searchStop the current commandString not foundString:Style analysisSupport for graphics (Matplotlib)Supported filesSwitch to/from layout %dSymbolic MathematicsSymbolic mathematics in the IPython ConsoleSynchronizeSynchronize Spyder's path list with PYTHONPATH environment variableSynchronize...Syntax color scheme: Syntax coloringSyntax highlighting for Matlab, Julia and other file typesSystem commands:TIFF imagesTabTab always indentTab stop width:Tasks (TODO, FIXME, XXX, HINT, TIP, @todo)Tear off menusTemporary fileTerminalTerminateTerminated.Text and margin font styleText editorText filesThe 'xlabels' argument length do no match array column numberThe 'ylabels' argument length do no match array row numberThe 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.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.The authenticity of host %s can't be established. Are you sure you want to continue connecting?The authenticity of the host can't be establishedThe 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.The current workspace has not been configured yet. Do you want to do this now?The folder %s does not contain a valid %s projectThe following are the default %s. These options may be overriden using the %s dialog box (see the %s menu)The following conflicts have been detected:The following directory is not a Spyder workspace:
%s

Do you want to create a new workspace in this directory?The following directory is not empty:
%s

Do you want to continue?The following directory is not in workspace:
%s

Do you want to continue (and copy the directory to workspace)?The following error occured when calling Sphinx %s.
Incompatible Sphinx version or doc string decoding failed.

Error message:
%sThe following modules are not installed on your machine: %sThe following working directory is not valid:
%sThe kernel failed to start!! That's all we know... Please close this console and open a new one.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.The project %s is already opened!The project root path directory is inside the workspace but not as the expected tree level. It is not a directory of the workspace:
%sThe workspace was unable to load or save %s

Please check if you have the permission to write the associated configuration files.This directory is already included in Spyder path list.
Do you want to move it to the top of the list?This entry is incorrectThis feature requires Sphinx 1.1 or superior.This feature requires the Matplotlib library. It seems you don't have it installed.This feature requires the Rope or Jedi libraries. It seems you don't have either installed.This feature requires the Sympy library. It seems you don't have it installed.This is a remote kernelThis is a temporary script file.This is the current workspace directoryThis 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 editorThis lets you load graphics support without importing the commands to do plots. Useful to work with other plotting libraries different to Matplotlib or to develop GUIs with Spyder.This method is the only way to have colorized standard error channel when the output channels have been merged.This option lets you hide the message shown at the top of the console when it's opened.This option loads the Sympy library to work with.
Please refer to its documentation to learn how to use it.This option will act on
libraries such as Matplotlib, guidata or ETSThis option will be applied the next time a Python console or a terminal is opened.This option will be applied the next time a console is opened.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)This option will override the PYTHONSTARTUP environment variable which defines the script to be executed during the Python console startup.This path is incorrect. Enter a correct directory path, then press enter to validateThis 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?To boolTo complexTo doTo floatTo intTo strToolbarsTransposeTruncate valuesTrying to kill a kernel?Tunnel '%s' failed to startTupleTypeUMRUMR excluded modules: (example: guidata, guiqwt)UMR forces Python to reload modules which were imported when executing a script in the external console with the 'runfile' function.Unable to connect to IPython %sUnable to load pageUncommentUndoUnexpected error: see internal consoleUnindentUnindent current line or selectionUnlockUnmatched parentheses:Update module names listUsageUse %s+T when the console is selected to open a new oneUse a completion widgetUse a pager to display additional text inside the consoleUse a single instanceUse a widget instead of plain text output for tab completionUse symbolic mathUse the following Python interpreter:Use the following file:Use the following startup script:Use the greedy completerUseful if you don't want to fill the console with long help or completion texts. Note: Use the Q key to get out of the pager.User Module Reloader (UMR)ValueValue:Variable NameVariable explorerVariablesVertical dockwidget tabsVertical dockwidget title barsViTablesView and edit DataFrames and Series in the Variable ExplorerWarningWeb page filesWelcome to Spyder!When opening a fileWhen 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.When saving a fileWhole wordsWidth:WinPythonWinPython control panelWindow layout will be reset to default settings: this affects window position, size and dockwidgets. Do you want to continue?Working directoryWorking directory:WorkspaceWrap linesXML filesYaml filesYou can also run a whole file at startup instead of just some lines (This is similar to have a PYTHONSTARTUP file).You can run several lines of code when a console is started. Please introduce each one separated by commas, for example:
import os, import sysYou 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.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.Zoom inZoom outZoom resetarraycharacterscodecopydatadeletedpielementsfilegettext filesguidata documentationguidata examplesguiqwt documentationguiqwt examplesinchesinterruptedinvalid regular expressionits own configuration filelistmatches inmoveotherpermission denied errors were encounteredpixelsreStructured Text filesread onlytabtabletextthe current file directorythe following directory:the following projects:
%sthe global working directorythe same as in last sessionthe script directorythis dialogtutorialuntitledusername@hostname:portvariable_nameProject-Id-Version: 2.1 POT-Creation-Date: 2015-11-24 20:56+COT PO-Revision-Date: 2015-08-24 15:13-0500 Last-Translator: Carlos Cordoba Language-Team: Python Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated-By: pygettext.py 1.5 X-Poedit-SourceCharset: utf-8 X-Poedit-Basepath: ../../../../ X-Generator: Poedit 1.5.4 InstaladoRequerido yentradaslíneasms%s no están soportados por el momentoLos arreglos %sEditor de %s%s ya se está ejecutando en proceso aparte. ¿Desea terminar este proceso antes de empezar uno nuevo?&CerrarCerrar&Configurar...&Depurar&EditarEditar&ArchivoBus&car en archivos&Buscar texto&Tipo de letra...A&yuda&Nuevo&AbrirIm&primir&Salir&Reemplazar texto&RestaurarE&jecutarE&jecutar...&Guardar&Buscar&Herramientas&Ver(Experimental) Completado del código y ayuda en el Editor(Oprimir Enter para editar)2 espacios4 espacios%s contiene varios tipos de caracteres de fin de línea.
Spyder lo arreglará automáticamente.%s fue modificado por fuera de Spyder.
¿Desea recargarlo y perder todos sus cambios?%s ha sido modificado.
¿Desea guardar los cambios?%s no está disponible (el archivo puede haber sido eliminado, movido o renombrado por fuera de Spyder).
¿Desea cerrarlo?La opción -u se añade a estas opcionesNo fue posible %s %s

Mensaje de error:
%sNo fue posible asignarle los datos al ítem.

Mensaje de error:
%sNo fue posible crear el archivo %s

Mensaje de error:
%sNo fue posible crear la carpeta %s

Mensaje de error:
%sNo fue posible encontrar el programa externo.

%sNo fue posible cargar '%s'

Mensaje de error:
%sNo fue posible mover %s

Mensaje de error:
%sNo fue posible graficar los datos.

Mensaje de error:
%sNo fue posible pasar al siguiente paso

Por favor revise sus daros.

Mensaje de error:
%sNo fue posible cargar el proyecto de Pydev %s

Mensaje de error:
%sNo fue posible renombrar el archivo %s

Mensaje de error:
%sNo fue posible obtener los datos.

Mensaje de error:
%sNo fue posible guardar el arreglo

Mensaje de error:
%sNo fue posible guardar el espacio de trabajo actual

Mensaje de error:
%sNo fue posible guardar el archivo '%s'

Mensaje de error:
%sNo fue posible guardar el archivo '%s'

Mensaje de error:
%sNo fue posible generar la gráfica.

Mensaje de error:
%sExtensión de archivo no soportada: '%s'

¿Desea importar el archivo de todas formas (seleccionando un formato conocido)?Tipo de archivo no soportado '%s'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.Corriendo...¿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).
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).
Advertencia: los cambios son aplicados de forma separada?No se pudo iniciar una terminal de Python!Un proyecto llamado %s ya existeAPI #1API #2Selección del API para objetos QString and QVariant:Acerca de %sAcerca de %s...Activar el soporteAñadir comentario de &bloqueAñadir un comentario de bloque alrededor de la línea o selección actualAñadir rutaAñadir al PYTHONPATHCaracterísticas adicionalesOpciones adicionalesDirección:Opciones avanzadasOpciones avanzadasTodos los cambios a %s se perderán.
Desea revertir el archivo del disco?Todos los archivosTodos los archivos (*)Todos los archivos (*.*)Siempre muestre %s en una primera ejecuciónOcurrió un error mientras iniciaba el núcleoAnálisisBarras de herramientas y componentes animadosEstá seguro de que desea reiniciar el núcleo?ArgumentosArgumentos...Argumentos: %sEditor de arreglosEl arreglo está vacíoLos arreglos de más de tres dimensiones no están soportadosMostrar un diálogo de confirmación antes de cerrar una terminalAl inicio Spyder restaurará el directorio global de la última sesiónAl inicio, el directorio de trabajo global es:Al inicio el directorio de trabajo global será el siguienteVentana de terminal anexa (para depuración)Intenta interrumpir este proceso, pero éste puede no concluir tras oprimir este botón (pues es posible que se le pida al usuario guardar archivos sin salvar, etc).AtributoAutollamarEsta 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).Autollamar:AutomáticoCompletar código automáticamenteConexiones automáticasImportar automáticamenteIndentación automática después de 'else', 'elif', etc.Inserción automática de comillasInserción automática de ':' después de 'for', 'if', 'def', etcInserción automática de paréntesis, llaves y corchetesCargar automáticamente los módulos de Pylab y NumPyEliminar automáticamente espacios en blanco al guardar un archivoActualizar automáticamenteEje:AnteriorSalida:Color de fondoFondo:Archivos BatchNegritaPunto de interrupciónPuntos de interrupción (Breakpoints)SeleccionarSeleccionar el directorio de búsquedaSeleccionar un directorio de trabajoExplorar repositorio Navegar por las pestañasMostrarMostrar líneas...Mostrar:MostrarObjeto integrado:Archivos CC&errar todo&TerminalesArchivos C++Uso de memoria y CPU en la barra de estadoPara reportar el estado del CPU se requiere de la librería `psutil` (>=v0.3)CPU:Archivos de texto CSVCancelarRetorno de carro (Mac)Retorno de carro y salto de línea (Windows)Distinguir mayúsculas de minúsculasDiferenciar entre mayúsculas y minúsculas al completar códigoLa celda empieza en la línea %sCambiarse al directorio base de un archivoMoverse al directorio superiorCambiando la salida gráfica a Qt por MayaviClase definida en la línea %sEliminar todas las salidasEliminar los puntos de interrupción de todos los archivosLimpiar la terminalLimpiar líneaLimpiar línea o bloqueLimpiar la lista de archivos recientesLimpiar la terminalLimpia los contenidos de la terminal (equivalente al comando 'cls')Limpiar esta listaContenidos del portapapelesCerrar todos los archivos abiertosCerrar el archivo actualCerrar panel actualCerrar pestaña actualCerrar proyectoCerrar este panelCierra esta ventanaCerrar proyectos no relacionadosCerrar ventanaAnálisis e introspección de códigoAnálisis del códigoEl análisis del código requiere pyflakes %s+Colapsar resultadosColapsar selecciónEsquema de coloreadoColorear el canal de error estándar usando códigos de escape ANSI Min/max de columnaSeparador de columnas:Columna:SímboloArgumentos de la línea de comandos:Opciones de línea de comandos:ComentarComentar la línea o selección actualComentario:Comentarios:ConsignarCondición:Archivos de ConfiguraciónConflicto conConectarse a un núcleo existenteConectándose al núcleo...Error de conexiónInformación de conexión:TerminalAyuda de la terminalContextoContinuarContinuar con la ejecución hasta el siguiente punto de interrupciónError de conversiónConvertir caracteres de fin de líneaConvertir a un archivo de PythonCopiarCopiar la ruta al portapapelesCopiar al portapapelesCopiar sin los promptsNo fue posible conectarse al servidor remotoNo fue posible crear un túnel ssh. El error fue: Crear una nueva ventana de ediciónCelda seleccionada:Línea seleccionada:Variables de entorno del usuario actual...Márgenes de componente personalizadas:Disposiciones personalizadas de componentesCortarArchivos Cython/PyrexFondo oscuroDatosDataFrameDepurarDepurar archivoBarra de depuraciónDepurar con winpdbDepurarControl de depuraciónDecidir 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.Decida como renderizar las figuras creadas por este tipo de salida gráficaTerminal de Python dedicadaPor defecto (es decir, el mismo de Spyder)API por defectoUtilizar el archivo por defecto de PYTHONSTARTUPPor defecto es 4Por defecto es 6Por defecto es
In [<span class="in-prompt-number">%i</span>]:Por defecto es
Out[<span class="out-prompt-number">%i</span>]:Librería por defectoDirectorio de trabajo:Definición:EliminarEliminar...DiccionarioVisualizaciónMostrar globos de sugerenciasMostrar el banner inicial¿Realmente desea eliminar %s?¿Realmente desea eliminar el proyecto %s?

Nota: Los archivos del proyecto no serán eliminados del disco.¿Realmente desea eliminar la ruta seleccionada?¿Realmente desea renombrar %s y sobrescribir el archivo existente %s?¿Desea cerrar todas las otras terminales conectadas al mismo núcleo que ésta?¿Desea eliminar todas las variables seleccionadas?¿Desea eliminar la variable seleccionada?HechoDuplicarFin de líneaETS_TOOLKIT:EditarEditar los datos en un proceso remotoEditar los filtros para nombres de archivoEditar filtros...Editar ítemEditar proyectos relacionadosEditar la plantilla para nuevos módulosBarra de ediciónEditorCompletado del código y ayuda en el EditorEsta opción permite modificar arreglos de NumPy, imágenes de de PIL/Pillow, Dataframes, y listas, tuplas y diccionarios en el proceso remoto. Esto impide transferir grandes cantidades de datos entre el proceso remoto y Spyder.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.Vaciar el portapapelesHabilita 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>Activar el RMUActivar las actualizaciones automáticasActivar el monitorAl 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.Archivos EnamlCodificación:Caracteres de fin de líneaFin de línea:La tecla Enter selecciona el resultado a completarEnthought Tool SuiteEnthought Tool Suite (ETS) funciona con las librerías gráficas PyQt4 (qt4) y wxPython (wx). Esta opción establece cual desea utilizar el usuario.Variables de entornoVariables de entorno...ErrorExcluir variables en mayúsculasExcluir variables que comienzan en mayúsculasExcluir variables privadasExcluir variables que referencian tipos de datos no soportados (es decir aquellos que no pueden manejarse y guardarse correctamente)Excluir variables cuyo nombre está por completo en mayúsculasExcluir variables cuyo nombre comienza con un guión abajoExcluir variables cuyo nombre comienza en mayúsculasExcluir tipos de datos no soportadosExcluir:Patrones de nombres de archivo a excluirEjecutablesEjecutar en una terminal de Python dedicadaEjecutar en una terminal de comandos del sistemaEjecutar en la terminal actual de Python o IPythonUn proyecto de Pydev existenteUn proyecto de Spyder existenteUn directorio existenteTerminarTerminar depuraciónExpandir resultadosExpandir selecciónHerramientas externasEditor externoRuta ejecutable del editor externo:Ruta del editor externo...Editor externo:Módulos externosEl archivo %s ya existe.
¿Desea sobrescribirlo?Explorador de archivosGestión de la lista de archivosBarra de archivoArchivo...Los archivos son creados en:Los archivos deben abrirse desde:FiltrarFiltro:Buscar &siguienteBuscar &anteriorBuscar en archivosEncontrar textoArreglar automáticamente y mostrar un mensaje de advertenciaCorregir la indentaciónFormato de punto flotanteLa carpeta %s ya existe.Nombre de la carpeta:Carpeta...FuenteTipo de letraPara contar con soporte de %s, por favor instale una de
las siguientes herramientas:

%sPor razones de rendimiento, los cambios aplicados a arreglos enmascarados no se verán reflejados en los datos del arreglo (y viceversa).FormatoEl formato (%s) es incorrectoFormato:Archivos FortranTotalModo a pantalla completaFunción definida en la línea %sImágenes GIFSalida gráfica:Editor gráfico:GeneralAjustes generalesDirectorio de trabajo globalIr a la posición del cursorIr a la definiciónIr a la anterior posición de ediciónIr a la línea...Ir a la líneaIr a la próxima línea de advertencia o errorIr a la siguiente posición del cursorIr a la línea anterior de advertencia o errorIr a la anterior posición del cursorGráficasSalida gráfica:Completado ambiciosoAlto:AyudaAyuda...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.AquíRepositorio HgOcultar opciones avanzadasResaltar la celda actualResaltar la línea actualResaltar coincidenciasResaltar ocurrencias después deSugerencia: oprimir Alt para mostrar aceleradoresHistogramaHistorialLongitud del historialHistorial de comandosHistorialesHistorial...Página de inicioServidorArchivos IDLIPythonTerminal de IPythonIntegración con la terminal de IPythonTerminal de IPythonDocumentación de IPythonNotebooks de IPythonSi 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.Si esta opción está activada, los archivos de Python serán analizados automáticamente y las líneas que contengan errores o advertencias serán resaltadasSi esta opción está activada, el oprimir Tab siempre indentará el código, aún cuando el cursor no esté al principio de una línea (de seleccionar esta opción, se puede usar la combinación de teclas Ctrl+Espacio para activar el completado de código)Si está opción está activada, al hacer click sobre el nombre de un objeto (click-izquierdo + la tecla Ctrl), el Editor se ubicará en la definición del mismo (de poder resolverse el nombre).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)Ignorar los errores de cambio de API (sip.setapi)ImportarImportar comoImportar datosImportar directorioError de importaciónImportar un proyecto existente de PydevImportar proyecto existenteImportar desde el portapapelesAsistente de importaciónIncluir:Patrones de nombres de archivo a incluirIndentarIndentar la línea o selección actualCaracteres de indentación:ÍndiceÍndice:Inicializando...En líneaSalida en línea:Prompt de entrada:InsertarInspeccionar objetoMódulos instalados de PythonInstancia:Tecla de retroceso ("backspace") inteligenteInteractuarInteractuar con la terminal después de la ejecuciónGraficar datos interactivamente en la terminalInterfazTerminal internaOpcionesEditor interno:Ayuda básicaIntrospecciónRuta de directorio inválidaRuta de archivo inválidaNombre de proyecto inválido.

El nombre debe ajustarse a una expresión del tipo:
%sAl parecer el núcleo murió de forma inesperada. Use 'Reiniciar el núcleo' para continuar usando esta terminal.No fue posible convertir este notebook. El error es: No fue posible generar ayuda en texto enriquecido para este objeto.
Por favor véala en texto plano.No fue posible remover las outputs de este notebook. El error es: No fue posible ejecutar este archivo en una terminal del sistemaCursivaImágenes JPEGArchivos JSONArchivos JavascriptArchivos JsonArchivos JuliaNúcleoNúcleo %sEl núcleo es remoto o no está especificado. Por ello no se puede interrumpir.El núcleo es remoto o no está especificado. Por ello no se puede reiniciar.Clave/TeclaNombre:Atajos de tecladoPalabra clave:TerminarTermina el proceso actual, provocando que culmine inmediatamenteÚltima posición de ediciónFondo claroFondo claro (color blanco)Línea %sNúmero total de líneas:Salto de línea (UNIX)Línea:Líneas:Enlazar a la definición de un objetoEnlace:ListaCargar sesión de SpyderCargar sesión...Cargando %s...Cargando la terminal de IPython...Cargando el editor...Cargando la terminal externa...Cargando el explorador de archivos...Cargando el historial...Cargando el explorador de variables...Cargando el inspector de objetos...Cargando la ayuda en línea...Cargando el explorador de código...Cargando el explorador de proyectos...BloquearArchivos MATLABBarra principalMantener el foco en el Editor después de ejecutar celdas o seleccionesMáscaraDatos enmascaradosParéntesis emparejados:Archivos de MatlabMatplotlibDocumentación de MatplotlibMaximizar el panel actualMáximo número de entradasMáximo número de líneas a mostrarMáximo número de archivos recientesMáximo número de archivos recientes...Para reportar el uso de memoria se requiere de la librería `psutil` (>=0.3) en plataformas distintas a WindowsMemoria:Combinar los canales de salida y error estándar del procesoCombinar los canales de salida del proceso quiere decir que el error estándar no será escrito en rojo , pero esto ayuda a mejorar la velocidad en que aparece el texto en la terminal.Método definido en la línea %sMod1Mod2Mod3Modifique como se muestran los prompts de entrada y salida en la terminal.MóduloNo se pudo encontrar el módulo pywin32.
Por favor reinicie esta sesión de Windows (no el computador) para que los cambios surtan efecto.Módulo o paquete:MóduloMonitorMover abajoMover al finalMover al principioMover arribaMover a...Archivos NSISNombreNombres de los filtros:Crear nuevoNuevo archivoNueva carpetaNuevo móduloNuevo nombre:Nuevo paqueteNuevo proyectoNuevo proyecto...Nuevo en Spyder? Lee nuestroNueva ventanaSiguienteSiguiente posición del cursorSiguiente advertencia o errorNo existe un intérprete de IPython para ejecutar %s.

Por favor abra uno nuevo e intente otra vez.No existe una terminal de Python para ejecutar %s.

Por favor abra una nueva y pruebe otra vez.Sin argumentosNo existe más documentación disponibleNo está disponible el código fuenteTexto normal:No hay nada para importar desde el portapapeles.Arreglos de NumPyArreglos de NumPyDocumentación de NumPyManual de referencia de NumPyGuía del usuario de NumpyArreglos comprimidos de NumPyNúmero:Documentación de Numpy y ScipyObjetoEl objeto %s no es picklableInspector de objetosOcurrencia:DesactivadoEn 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).Una pestaña por archivoDocumentación en líneaAyuda en líneaSólo se usa cuando el formato es PNG. Por defecto es 72.AbrirAbrir &símbolo del sistemaAbrir &recienteAbrir un archivo de conexión de IPythonAbrir una terminal de Python aquíAbrir una terminal de PythonAbrir &terminal de comandosAbre el símbolo del sistema de WindowsAbrir una nueva terminal de IPython conectada a un núcleo existenteAbrir una nueva terminalAbre una terminal del sistemaAbrir una terminal de IPythonAbrir una terminal de IPythonAbrir símbolo del sistema aquíAbrir archivoAbrir archivo como:Abrir proyectoAbrir sesiónAbrir terminal del sistema aquíArchivos OpenCLAbrir e inspeccionar esta variable puede tomar mucho tiempo ¿Desea continuar de todas formas?Dependencias opcionalesDependencias opcionales...OpcionesExplorador de códigoPrompt de salida:Imágenes PNGPYTHONPATHAdministrador del PYTHONPATHReemplazo de PYTHONSTARTUPNombre del paquete:Paquete...PanelesDirectorio superiorContraseñaContraseña o frase de contraseña de la clave sshPegarArchivos Patch y diffRuta al archivo de conexión o id del núcleoRuta al archivo de clave sshRealice operaciones simbólicas en la terminal (integrales, derivadas o cálculo vectorial) y obtenga los resultados en un bello estilo impreso.Realizar análisis sólo cuando se guarde el archivoRealizar los análisis al guardar el archivo y cadaPermisos:Archivos pickleTexto planoFuente para el texto planoPor favor considere instalar Sphinx para obtener la documentación en texto enriquecido 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)Por favor instale Matplotlib o guiqwt.Por favor tenga en cuenta que estos cambios sólo se aplicarán a nuevas terminales de IPython y PythonPor favor tenga en cuenta que estos cambios sólo se aplicarán a nuevas terminalesGraficarMostrar la terminal interna cuando se produzcan erroresPre&ferenciasPreferenciasPreferencias > Inspector de objetosPresione Enter para validar esta entradaPresione Enter para validar esta rutaVista previaAnteriorAnterior posición del cursorAnterior advertencia o errorImprimir el archivo actual...Presentación preliminar...Imprimir...El proceso falló al empezarExplorador de proyectosExplorador de proyectosNombre del proyecto:Proyecto...PromptsPropiedadesCaracterísticas proporcionadasPyQtEl 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.Referencia del API de PyQt4Manual de referencia de PyQt4Terminal de IPythonManejador de rutas de PythonDocumentación de PythonArchivo ejecutable de PythonArchivos PythonAyuda de Python:Archivos de PythonPython(x,y)Carpeta de documentación de Python(x,y)Lanzador de Python(x,y)Qt (PyQt/PySide)Diseñador de interfaces de QtTraductor de aplicaciones de QtDocumentación de QtEjemplos de QtEstilo de QtSelección de la librería de enlace entre Qt y Python:Referencia rápidaAún no se ha definido la disposición de componentes #%dSalir&Eliminar comentario de bloqueTexto sin formatoEjecutar de &nuevo el último archivoAnálisis del código en tiempo real en el EditorAnálisis de estilo del código en el EditorCampos del arreglo de records:RehacerActualizarIntervalo de actualizaciónActualiza la lista de nombres de los módulos disponibles en su PYTHONPATHActualizar periódicamenteExpresión regularProyectos relacionadosRecargar la última sesiónError de remociónEliminarEliminar comentario de bloque alrededor de la línea o selección actualEliminar del PYTHONPATHEliminar rutaEliminar referencias:Eliminar espacios en blancoRenombrarRenombrar...Renderizar ecuaciones matemáticasReemplazar todoReemplazar textoReemplazar caracteres de tabulación por espaciosReemplazar con:Buscar/reemplazarReportar un problema...Restaurar los valores por defectoRestablecer la disposición de componentesRedimensionarAjustar filas a los contenidosResolución:Reiniciar el núcleoReiniciar el núcleo?RestaurarRestaurar el panel actualRestaurar la disposición originalRestaurar el panel a su tamaño originalRestaurar archivo desde el discoTexto enriquecidoFuente para el texto enriquecidoAyuda en texto enriquecido en el Inspector de ObjetosSeparador de filas:EjecutarEjecutar la &selección o la línea actualEjecutar archivo de PythonAjustes de ejecuciónAjustes de ejecuciónEjecutar un archivo de PythonEjecutar un archivoEjecutar de nuevo el mismo archivoEjecutar de nuevo este programaEjecutar la celdaEjecutar la celda y avanzarEjecutar códigoOpciones de ejecuciónEjecutar la celda actual (Ctrl+Enter) [Usar #%% para crear celdas]Ejecutar la celda actual y avanzar a la siguiente (Shift+Enter)Ejecutar la línea seleccionadaEjecutar archivoEjecutar un archivo:Ejecutar selecciónEjecutar la &selección o línea actualAjustes de ejecuciónAjustes de ejecución para %sBarra de ejecuciónEjecutar hasta que la función o método actual termineEjecutar en una terminal externa del sistema no está soportado en la plataforma %s.Guardar t&odoGuardarGu&ardar como...Guardar archivo de PythonGuardar todos los archivosGuardar todo antes de ejecutar un archivoGuardar arregloGuardar el archivo actual como...Guardar el historial actual (es decir todas las entradas y salidas) en un archivo de textoGuardar sesión actual y salir de la aplicaciónGuardar datosGuardar datos como...Guardar archivoGuardar el historialGuardar el historial...Guardar sesiónGuardar sesión y salir...Documentación de SciPyDesplazarse automáticamente a la última entradaBuscarBúsqueda canceladaBuscar en todos los directorios del sys.path que están por fuera del directorio de instalación de PythonBuscar en el repositorio actual de MercurialPatrón de búsquedaBuscar recursivamente en este directorioBuscar textoBuscar en varios archivos a la vezBarra de búsquedaSeleccionar todoSeleccionar una nueva fuenteSeleccionar una configuración de ejecución:Seleccionar un directorio existente para que sea el espacio de trabajo, o crear uno nuevoSeleccionar directorioSeleccionar archivoSeleccionar proyectos a importarSeleccionar los proyectos que están relacionados a %sSeleccionar clave sshSeleccionar la ruta al intérprete de Python que desee usar:Establecer la lista de módulos excluidos por el RMUEstablece el directorio de trabajo para la terminal actualEstablecer directorio de trabajoFija el directorio de trabajo para la terminal actual como el directorio del archivo actualEstablece el directorio de trabajoEstablece la ruta del editor externoEstablece el tipo de fuenteEstablece el máximo número de entradas a almacenarEstablecer la disposición %dEstablece el máximo número de líneas a mostrar en la terminalEstablece el tipo de fuente de la terminalEstablecer la librería gráfica
utilizada para generar las gráficas de Matplotlib (por defecto: Qt4Agg)Establece el máximo número de líneas que se mostrarán en la terminal en cualquier momento. Si se introduce -1 se mostrarán todas las líneas (no se recomienda!)Establezca esta opción
si desea separar los menús de la ventana principalSeleccione esta opción
para abrir archivos externos de Python en la ventana actual (Requiere reiniciar)Añadir o eliminar un punto de interrupciónAñadir o editar un punto de interrupción condicionalConstruyendo la ventana principal...AjustesComandos especiales:Muestra los contenidos del sys.path en modo lecturaMostrar el uso de CPU cadaMostrar código fuenteMostrar la lista de comentarios de los TODO/FIXME/XXX/HINT/TIP/@todoMostrar la ruta completaMostrar opciones avanzadasMostrar todos los archivosMostrar y editar las variables de entorno del usuario actual en el registro de Windows (es decir, para todas las sesiones)Muestra y edita las variables de entorno (para la sesión actual)Mostrar el máximo y mínimo de arreglosMostrar espacios en blancoMostrar errores o advertencias del análisis del códigoMostrar sólo el directorio actualMostrar el tiempo transcurridoMostrar una barra de desplazamiento horizontalMostrar iconos y texto Mostrar como imagenMostrar números de líneasMostrar la memoria usada cadaMostrar la lista de módulos que fueron recargadosMostrar comentarios especialesContenidos del sys.pathContenidos del sys.pathMostrar barra de pestañasMostrar lista de TODO'sMostrar una línea vertical después deMostrar una advertencia cuando se termine el procesoMostrar la lista de errores y advertenciasMostrar u ocultar el explorador de variablesMostrar u ocultar el explorador de códigoMostrar/cerrar el explorador de proyectosÁreas laterales:SiftTamañoTamaño:Saltar filas:InteligenteOrdenar archivos según su ruta completa&Código fuenteOrigenCódigo fuenteBarra de código fuenteSphinx %s está instalado actualmente.Dividir horizontalmenteDividir horizontalmente esta ventana o panel de ediciónDividir verticalmenteDividir verticalmente esta panel o ventana de ediciónEditor de SpyderTerminal interna de Spyder! Esta terminal se utiliza para reportar errores de la aplicación y para inspeccionar las características internas de Spyder con los siguientes comandos: spy.app, spy.window, dir(spy) Por favor no ejecuta su código en esta terminal Archivos de datos de SpyderSpyder 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.Documentación de SpyderSesiones de SpyderObtener soporte para SpyderTutorial de SpyderClave sshComenzar la búsquedaInicializaciónEstadísticas para los archivos de código únicamente:
(Python, C/C++, Fortran)

%s archivos.
%s líneas de código.Barra de estadoEjecutar líneaIngresar en la función/métodoSalir de la función/métodoIngresar en la función o método de la línea actualDetenerDetener la búsquedaDetener el comando actualTexto no encontradoCadena:Análisis de estiloSoporte para crear gráficas (Matplotlib)Archivos soportadosCambiarse a la disposición %dMatemática simbólicaMatemática simbólica en la terminal de IPythonSincronizarSincronizar la lista de rutas de Spyder con la variable de entorno PYTHONPATHSincronizar...Esquema de coloreado:Coloreado de sintaxisColoreado del código para archivos tipo Matlab, Julia y varios otros tiposComandos del sistema:Imágenes TIFFTabulaciónSiempre indentar con la tecla TabAncho de las tabulaciones:Tareas (TODO, FIXME, XXX, HINT, TIP, @todo)Separar los menúsArchivo temporalTerminalInterrumpirTerminado.Tipo de letra para el texto y las márgenesEditor de textoArchivos de TextoEl argumento de longitud 'xlabels' no coincide con el número de columnas del arregloEl argumento de longitud 'ylabels' no coincide con el número de filas del arregloEl 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.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.La autenticidad del servidor %s no puede ser establecida. ¿Está seguro de que desea continuar conectándose?La autenticidad del servidor no puede ser establecidaEl monitor está desactivado, por tanto se creará un kernel de IPython pero usted deberá conectar manualmente un intérprete al mismo.El siguiente espacio de trabajo no ha sido configurado. ¿Desea hacerlo ahora?La carpeta %s no contiene un proyecto de %s válidoLas siguientes son los %s por defecto. Estos ajustes pueden modificarse usando el diálogo %s (ver el menú %s)Los siguientes conflictos han sido detectados:El siguiente directorio no es un espacio de trabajo de Spyder:
%s

¿Desea crear un nuevo espacio de trabajo en este directorio?El siguiente directorio no está vacío:
%s

¿Desear continuar?El siguiente directorio no está en el espacio de trabajo:
%s

¿Desea continuar (y copiar el directorio al espacio de trabajo)?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:
%sLos siguientes módulos no están instalados en su computador: %sEl siguiente directorio de trabajo no es válido:
%sNo fue posible crear un núcleo!! Es todo lo que sabemos... Por favor cierre esta terminal y abra una nueva.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.El proyecto %s ya está abierto!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:
%sNo 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.Este directorio ya está incluido en la lista de rutas de Spyder.
¿Desea moverlo al principio de la lista?Esta entrada es incorrectaEsta característica requiere Sphinx 1.1 o superior.Esta característica requiere la librería Matplotlib. Al parecer no la tiene instalada.Esta característica requiere las librerías Rope o Jedi. Al parecer no tiene ninguna instalada.Esta característica requiere la librería Sympy. Al parecer no la tiene instalada.Este es un núcleo remotoEste es un archivo temporalEste es el directorio actual del espacio de trabajoEste 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 EditorEsto le permite cargar el soporte gráfico sin importar los comandos para crear figuras. Es útil para trabajar con otras librerías gráficas diferentes a Matplotlib o para desarrollar interfaces gráficas con Spyder.Éste método es la única forma de darle color al canal de error estándar cuando los canales de salida han sido combinados.Esta opción le permite ocultar el mensaje que aparece al principio de la terminal cuando se abre por primera vez.Esta opción carga la librería Sympy para trabajar
con ella. Por favor lea su documentación para aprender como usarla.Esta opción tendrá efecto
en librerías como Matplotlib, guidata o ETSEsta opción será aplicada la próxima vez que una terminal de Python o una consola sea abierta.Esta opción será aplicada la próxima vez que una terminal sea abierta.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).Esta opción modificará la variable de entorno PYTHONSTARTUP, la cual define el archivo que es ejecutado durante el arranque de la terminal de Python.Esta ruta es incorrecta. Introduzca una ruta de directorio correcta y después presione Enter para validarlaEsta 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?A booleanoA complejoTo doA flotanteA enteroA cadenaBarras de herramientasTrasponerAbreviar valoresDesea cerrar un núcleo?El túnel '%s' falló en ser iniciadoTuplaTipoRMUMódulos excluidos del RMU: (por ejemplo: guidata, guiqwt)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'.No se pudo establecer conexión con el núcleo `%s`No fue posible cargar la páginaDescomentarDeshacerError inesperado. Por favor vea la terminal interna.Quitar indentaciónQuitar indentación de la línea o selección actualDesbloquearParéntesis desemparejados:Actualizar la lista de nombres de módulosUsoUsar %s+T para abrir una nueva terminalUsar un widget para completar textoUsar un paginador para mostrar textos dentro de la terminalUtilizar una única instanciaPuede decidir si usar un widget en lugar de texto plano para mostrar las posibilidades de completado usando la tecla Tab.Usar matemática simbólicaUsar el siguiente intérprete:Usar el siguiente archivo:Utilizar el siguiente archivo de arranque:Usar el completado ambiciosoEs útil si no desea llenar la terminal con largos textos de ayuda. Nota: Debe usar la tecla Q para salir del paginadorRecargador de Módulos del Usuario (RMU)ValorValor:Nombre de variableExplorador de variablesVariablesComponentes en pestañas verticalesBarras de título verticales para los componentesViTablesVer y editar DataFrames y Series en el Explorador de VariablesAdvertenciaArchivos de Páginas webBienvenido a Spyder!Cuando se abra un archivoCuando 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.Cuando se guarde un archivoSolamente palabras completasAncho:WinPythonPanel de control de WinPythonLa 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. ¿Desea continuar?Directorio de trabajoDirectorio de trabajo:Espacio de trabajoAjuste de línea automáticoArchivos XMLArchivos YamlTambién se puede ejecutar un archivo completo al inicio, en lugar de unas pocas líneas (Esto es similar a tener un archivo PYTHONSTARTUP).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 sysNo 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.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.AcercarAlejarRestaurararreglocaracterescódigocopiardatoseliminardpielementosarchivoArchivos gettextDocumentación de guidataEjemplos de guidataDocumentación de guiqwtEjemplos de guiqwtpulgadasinterrumpidoexpresión regular inválidasu propio archivo de configuraciónlistacoincidencias enmoverotropermiso denegado, se encontraron errorespixelsArchivos de Texto reStructuradosólo lecturatabuladortablatextoEl directorio en el que se encuentra el archivo actualel siguiente directorio:los siguientes proyectos:
%sEl directorio de trabajo globalEl mismo de la última sesiónEl directorio en el que se encuentra el archivo actualeste diálogotutorialSin título usuario@servidor:puertonombre_de_variablespyder-2.3.8/spyderlib/locale/es/LC_MESSAGES/spyderlib.po0000664000000000000000000045754712626055324021552 0ustar rootroot# -*- 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" spyder-2.3.8/spyderlib/locale/spyderlib.pot0000775000000000000000000031274712626055324017535 0ustar rootroot# 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 "" spyder-2.3.8/spyderlib/locale/fr/0000755000000000000000000000000012626531443015401 5ustar rootrootspyder-2.3.8/spyderlib/locale/fr/LC_MESSAGES/0000755000000000000000000000000012626531443017166 5ustar rootrootspyder-2.3.8/spyderlib/locale/fr/LC_MESSAGES/spyderlib.mo0000664000000000000000000030167312626055324021533 0ustar rootrootT e@ V !V -V8V>VGVNVRV qV {VgVV V WWW W&W,W ;WFWOW UWbW kWuW {WWWWWWWWBW!WX'XW0XbX<Xx(Y4Y9YAZBRZDZ1Z6 [;C[7[c[I\Be\;\7\C];`]=]8]t^!^^5J__a.aa!a(b0b7b/>bnb wbbb2bbbb cc%c7cOIc c cc"c*cd!d,:d gd qd ~d dd4d#dFe,HeCue#ee fff Rg ^ghggg0g%g;h7} >~L~ a~n~v~~~~ ~~ ~ ~.~# 4 U b lwM~m:AZ bpu ˀӀ$ = K&W~*ā$  6Mdv+ ǃσ ߃   $4P`v}x{LȆ% ňш  % ;IRm tˉ҉  &0F0O) Ŋߊ &[8[=`.I<ٌ  )4 E P\ c@m? 9Rev ǎ 9Kgӏ" ' 4>A  א!/TQ,ۑu=ܒe x   ʓ ϓݓ "1 ITYngz d"p & ):B`!g gz /˗З (?P:n٘  " /< OB\Ι֙ޙ  0 >IOV_~$Ӛ&e+ ś қݛMA2WRCݝ!3& Z gs"!מߞ. ?Kct  ß{ȟDXn} ŠҠ # 4 @L ]i%z1%+@l4֢ #158n  £̣ * 0 >K[s Ѥ٤! - CM&b ͥڥ  !8AV_7q5ߦ  1> R,^D Чڧ ߧ$ 2=EU) ŨϨ ߨ 4"Hkr`) $ AOm |; .(WQf'* U)#Ь L&|s4^%Ӯܮ #0/`s\9/C"Ux ԰߰ $:Q kx)ʱ#! <HMR Yd!j !ز%#" FT-c?ȵڵ p x ,˶ жܶ!?Oh+} C:.i z*׸  / ;=F:f1N8 B+½xRg:;п: `G(z4i&->Sl[Nk 'o@WnHwS>}ST]   4PV[0_"9 MW&\"7$9<v<%!;}T  $=\<e  }Se x sIm   0AVf my ) !',G`~  Y i w*fn    * 3> N Z h u A5 jrKICA RbPT<[?HI!xkTS9IJS"OvNDZ.CA #D.s/u(4%8 @Za  2D@4X#. #C71{X5V<(~Z nz3/<)=f'N9?FO_fuz )' # * 4 >I [ f s }K<,4.Ix(/%C^4z& )J^ x% +L _' $@AQ jt  7 ' 0<KTs 7)4^z/6>'^-)" 8B K U_s 4Od tKLN+h 08;Y [e84/ 8B FS#\&<Y^p% (  #?%Oun 0 -7 6e !  BG > F    1 );  e 3r + 2   2 G N  c q   "    D I a z   "    " >?I#  fw#CL]fx  "%)Hr;(7,8 ep (&B[9{      )6>"Nqq"g4LSh~! )I6R  .)9 c4m%  +(9$bfm8\5 :T         ] aP!!!! !!K!1" O" Z"e"n""""#"""""##.#R##n#*#%#/#%$ 9$+Z$($ $$$U$:%A%T%j% z%%%%%#%&&h9& &7&&''''P(T([() )#) ,)6)H)Z) a) n)|)"))))) ))) *$*A*S*[*x**$++++,@*,k,z,, ,,,,-^-z- - --k.. .>... /&/D/c/~/%/B/+/'0C0!^0000000U0S1m11 1 1 1 1111 222 2&,2S2Z2;q222,3/333 3 4H*4@s455j5bU66666 7#7-773e77 777*7 8'8!>8`8w88 88 8888999'99 :: /:=: L:$X:}:: : :: ::2:&;N9;;"; ;';+;+<=< R< \< h<P<<< =!=A= W=Ha== ==%= > >&">I>&X>7>>>>!>+?>?F? d?r?? ??,?)?;@ T@#b@(@@ @-@@A .AOAjA'A!AAAABJ+BvBC C5CKC,cCCCC4C^DoD DDDD<DE?1EWqE8EF FkkvD %PI]G$b\E"OO|Z%I"!&:U_ ;Nt{}6FS'F tR8`Et 2)RQ86 -(YWsB4PZ-\#$V;r[@+xvEm{ d7gSx*rcjG>B.u@ s!uhj,|W@cl!bH%YAw)*Kj^<W|qHmavzD(<r#=F0L]}mTibw,-fA(s43})'VnldAflQ jMC1u] R10:{y^? oGUS%gzQWqOA &kzn*p2a LJZP r 9meTN=-Y*Jw /;!XZ 6'[1DM#</9?3C+^o="^Ls/q_X[4QR5Uny:2u.+B~TLT ye5~o?3 hxMyaoN7 `iKx)5f~c ,J>}\Mb2k& pwX:d~=6Y@gi59O"pP.+d_qD&e0h.pf8,z|HcN3\][eJ<$9tU(0/V7 h?;'{C` aKiC>nB4 Installed Required and entries lines ms%s are currently not supported%s arrays%s editor%s is already running in a separate process. Do you want to kill the process before starting a new one?&Close&Close file&Configure...&Debug&Edit&Edit file&File&Find in files&Find text&Font...&Help&New file...&Open...&Print...&Quit&Replace text&Revert&Run&Run...&Save&Search&Tools&View(Experimental) Editor's code completion, go-to-definition and help(press Enter to edit file)2 spaces4 spaces%s contains mixed end-of-line characters.
Spyder will fix this automatically.%s has been modified outside Spyder.
Do you want to reload it and lose all your changes?%s has been modified.
Do you want to save changes?%s is unavailable (this file may have been removed, moved or renamed outside Spyder).
Do you want to close it?-u is added to the other options you set hereUnable to %s %s

Error message:
%sUnable to assign data to item.

Error message:
%sUnable to create file %s

Error message:
%sUnable to create folder %s

Error message:
%sUnable to find external program.

%sUnable to load '%s'

Error message:
%sUnable to move %s

Error message:
%sUnable to plot data.

Error message:
%sUnable to proceed to next step

Please check your entries.

Error message:
%sUnable to read Pydev project %s

Error message:
%sUnable to rename file %s

Error message:
%sUnable to retrieve data.

Error message:
%sUnable to save array

Error message:
%sUnable to save current workspace

Error message:
%sUnable to save file '%s'

Error message:
%sUnable to save script '%s'

Error message:
%sUnable to show image.

Error message:
%sUnsupported file extension '%s'

Would you like to import it anyway (by selecting a known file format)?Unsupported file type '%s'Warning:
The Python module rope is not installed on this computer: calltips, code completion and go-to-definition features won't be available.Running...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, ...).
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.Warning: changes are applied separately?A Python console failed to start!A project named %s already existsAPI #1API #2API selection for QString and QVariant objects:About %sAbout %s...Activate supportAdd &block commentAdd block comment around current line or selectionAdd pathAdd to PYTHONPATHAdditional featuresAdditional optionsAddress:Advanced SettingsAdvanced settingsAll changes to %s will be lost.
Do you want to revert file from disk?All filesAll files (*)All files (*.*)Always show %s on a first file runAn error ocurred while starting the kernelAnalysisAnimated toolbars and dockwidgetsAre you sure you want to restart the kernel?ArgumentsArguments...Arguments: %sArray editorArray is emptyArrays with more than 3 dimensions are not supportedAsk for confirmation before closingAt startup, Spyder will restore the global directory from last sessionAt startup, the global working directory is:At startup, the global working directory will be the specified pathAttached console window (debugging)Attempts to stop the process. The process may not exit as a result of clicking this button (it is given the chance to prompt the user for any unsaved files, etc).AttributeAutocallAutocall 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: AutomaticAutomatic code completionAutomatic connectionsAutomatic importAutomatic indentation after 'else', 'elif', etc.Automatic insertion of closing quotesAutomatic insertion of colons after 'for', 'if', 'def', etcAutomatic insertion of parentheses, braces and bracketsAutomatically load Pylab and NumPy modulesAutomatically remove trailing spaces when saving filesAutorefreshAxis:BackBackend:Background colorBackground:Batch filesBoldBreakpointBreakpointsBrowseBrowse a search directoryBrowse a working directoryBrowse repositoryBrowse tabsBufferBuffer...Buffer: Buffer: Builtin:C filesC&lose allC&onsolesC++ filesCPU and memory usage info in the status barCPU usage status: requires the `psutil` (>=v0.3) libraryCPU:CSV text filesCancelCarriage return (Mac)Carriage return and line feed (Windows)Case SensitiveCase sensitive code completionCell starts at line %sChange to file base directoryChange to parent directoryChanging backend to Qt for MayaviClass defined at line %sClear all ouputClear breakpoints in all filesClear consoleClear lineClear line or blockClear recent files listClear shellClear shell contents ('cls' command)Clear this listClipboard contentsClose all opened filesClose current fileClose current paneClose current tabClose projectClose this panelClose this windowClose unrelated projectsClose windowCode Introspection/AnalysisCode analysisCode analysis requires pyflakes %s+Collapse allCollapse selectionColor schemeColorize standard error channel using ANSI escape codesColumn min/maxColumn separator:Column:Command WindowCommand line arguments:Command line options:CommentComment current line or selectionComment:Comments:CommitCondition:Configuration filesConflictsConnect to an existing kernelConnecting to kernel...Connection errorConnection info:ConsoleConsole helpContextContinueContinue execution until next breakpointConversion errorConvert end-of-line charactersConvert to Python scriptCopyCopy path to clipboardCopy to clipboardCopy without promptsCould not connect to remote hostCould not open ssh tunnel. The error was: Create a new editor windowCurrent cell:Current line:Current user environment variables...Custom dockwidget margin:Custom window layoutsCutCython/Pyrex filesDark backgroundDataDataFrameDebugDebug fileDebug toolbarDebug with winpdbDebuggingDebugging controlDecide 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.Decide how to render the figures created by this backendDedicated Python consoleDefault (i.e. the same as Spyder's)Default APIDefault PYTHONSTARTUP scriptDefault is 4Default is 6Default is
In [<span class="in-prompt-number">%i</span>]:Default is
Out[<span class="out-prompt-number">%i</span>]:Default libraryDefault working directory is:Definition:DeleteDelete...DictionaryDisplayDisplay balloon tipsDisplay initial bannerDo you really want to delete %s?Do you really want to delete project %s?

Note: project files won't be deleted from disk.Do you really want to remove selected path?Do you really want to rename %s and overwrite the existing file %s?Do you want to close all other consoles connected to the same kernel as this one?Do you want to remove all selected items?Do you want to remove selected item?DoneDuplicateEOLETS_TOOLKIT:EditEdit data in the remote processEdit filename filtersEdit filename filters...Edit itemEdit related projectsEdit template for new modulesEdit toolbarEditorEditor's code completion, go-to-definition and helpEditors are opened in the remote process for NumPy arrays, PIL images, lists, tuples and dictionaries. This avoids transfering large amount of data between the remote process and Spyder (through the socket).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.Empty clipboardEnable 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>Enable UMREnable autorefreshEnable monitorEnabling 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.Enaml filesEncoding:End-of-line charactersEnd-of-lines:Enter key selects completionEnthought Tool SuiteEnthought Tool Suite (ETS) supports PyQt4 (qt4) and wxPython (wx) graphical user interfaces.Environment variablesEnvironment variables...ErrorExclude all-uppercase referencesExclude capitalized referencesExclude private referencesExclude references to unsupported data types (i.e. which won't be handled/saved correctly)Exclude references which name is uppercaseExclude references which name starts with an underscoreExclude references which name starts with an uppercase characterExclude unsupported data typesExclude:Excluded filenames patternExecutablesExecute in a new dedicated Python consoleExecute in an external System terminalExecute in current Python or IPython consoleExisting Pydev projectExisting Spyder projectExisting directoryExitExit DebugExpand allExpand selectionExternal ToolsExternal editorExternal editor executable path:External editor path...External editor:External modulesFile %s already exists.
Do you want to overwrite it?File explorerFile list managementFile toolbarFile...Files are created in:Files are opened from:FilterFilter:Find &nextFind &previousFind in filesFind textFix automatically and show warning message boxFix indentationFloat formattingFolder %s already exists.Folder name:Folder...Font styleFont: For %s support, please install one of the
following tools:

%sFor performance reasons, changes applied to masked array won't be reflected in array's data (and vice-versa).FormatFormat (%s) is incorrectFormat:Fortran filesFullFullscreen modeFunction defined at line %sGIF imagesGUI backend:GUI-based editor:GeneralGeneral settingsGlobal working directoryGo to cursor positionGo to definitionGo to last edit locationGo to line...Go to line:Go to next code analysis warning/errorGo to next cursor positionGo to previous code analysis warning/errorGo to previous cursor positionGraphicsGraphics backendGreedy completionHeight:HelpHelp...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.Here:Hg repositoryHide advanced optionsHighlight current cellHighlight current lineHighlight matchesHighlight occurences afterHint: press Alt to show acceleratorsHistogramHistoryHistory depth: History logHistory logsHistory...HomeHost nameIDL filesIPythonIPython ConsoleIPython Console integrationIPython consoleIPython documentationIPython notebooksIf enabled, Python source code will be analyzed using pep8, lines that are not following PEP8 style guide will be highlightedIf enabled, Python source code will be analyzed using pyflakes, lines containing errors or warnings will be highlightedIf enabled, pressing Tab will always indent, even when the cursor is not at the beginning of a line (when this option is enabled, code completion may be triggered using the alternate shortcut: Ctrl+Space)If this option is enabled, clicking on an object name (left-click + Ctrl key) will go this object definition (if resolved).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)Ignore API change errors (sip.setapi)ImportImport asImport dataImport directoryImport errorImport existing Pydev projectImport existing projectImport from clipboardImport wizardInclude:Included filenames patternIndentIndent current line or selectionIndentation characters: IndexIndex:Initializing...InlineInline backendInput prompt:InsertInspect current objectInstalled Python modulesInstance:Intelligent backspaceInteractInteract with the Python console after executionInteractive data plotting in the consolesInterfaceInternal consoleInternal console settingsInternal editor:Intro to IPythonIntrospectionInvalid directory pathInvalid file pathInvalid project name.

Name must match the following regular expression:
%sIt seems the kernel died unexpectedly. Use 'Restart kernel' to continue using this console.It was not possible to convert this notebook. The error is: It was not possible to generate rich text help for this object.
Please see it in plain text.It was not possible to remove outputs from this notebook. The error is: It was not possible to run this file in an external terminalItalicJPEG imagesJSON filesJavascript filesJson filesJulia filesKernelKernel %sKernel process is either remote or unspecified. Cannot interruptKernel process is either remote or unspecified. Cannot restart.KeyKey:Keyboard shortcutsKeyword:KillKills the current process, causing it to exit immediatelyLast edit locationLight backgroundLight background (white color)Line %sLine count:Line feed (UNIX)Line:Lines:Link to object definitionLink:ListLoad Spyder sessionLoad session...Loading %s...Loading IPython console...Loading editor...Loading external console...Loading file explorer...Loading history plugin...Loading namespace browser...Loading object inspector...Loading online help...Loading outline explorer...Loading project explorer...LockMATLAB filesMain toolbarMaintain focus in the Editor after running cells or selectionsMaskMasked dataMatched parentheses:Matlab filesMatplotlibMatplotlib documentationMaximize current paneMaximum entriesMaximum line countMaximum number of recent filesMaximum number of recent files...Memory usage status: requires the `psutil` (>=v0.3) library on non-Windows platformsMemory:Merge process standard output/error channelsMerging the output channels of the process means that the standard error won't be written in red anymore, but this has the effect of speeding up display.Method defined at line %sMod1Mod2Mod3Modify how Input and Output prompts are shown in the console.ModuleModule pywin32 was not found.
Please restart this Windows session (not the computer) for changes to take effect.Module or package:Module...MonitorMove downMove to bottomMove to topMove upMove...NSIS filesNameName filters:NewNew fileNew folderNew moduleNew name:New packageNew projectNew project...New to Spyder? Read ourNew windowNextNext cursor positionNext warning/errorNo IPython console is currently available to run %s.

Please open a new one and try again.No Python console is currently selected to run %s.

Please select or open a new Python console and try again.No argumentNo further documentation availableNo source code available.Normal text:Nothing to be imported from clipboard.NumPy arraysNumPy documentationNumPy reference guideNumPy user guideNumPy zip arraysNumber:Numpy and Scipy documentationObjectObject %s is not picklableObject inspectorOccurence:OffOn %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).One tab per scriptOnline documentationOnline helpOnly used when the format is PNG. Default is 72OpenOpen &command promptOpen &recentOpen IPython connection fileOpen Python console hereOpen a &Python consoleOpen a &terminalOpen a Windows command promptOpen a new IPython console connected to an existing kernelOpen a terminal windowOpen an &IPython consoleOpen an IPython consoleOpen command prompt hereOpen fileOpen file as:Open projectOpen sessionOpen terminal hereOpenCL filesOpening this variable can be slow Do you want to continue anyway?Optional DependenciesOptional dependencies...OptionsOutlineOutput prompt:PNG imagesPYTHONPATHPYTHONPATH managerPYTHONSTARTUP replacementPackage name:Package...PanesParentPasswordPassword or ssh key passphrasePastePatch and diff filesPath to connection file or kernel idPath to ssh key filePerfom symbolic operations in the console (e.g. integrals, derivatives, vector calculus, etc) and get the outputs in a beautifully printed style.Perform analysis only when saving filePerform analysis when saving file and everyPermissions:Pickle filesPlain TextPlain text font stylePlease consider installing Sphinx to get documentation rendered in rich text.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).Please install matplotlib or guiqwt.Please note that these changes will be applied only to new Python/IPython consolesPlease note that these changes will be applied only to new consolesPlotPop up internal console when internal errors appearPre&ferencesPreferencesPreferences > Object InspectorPress enter to validate this entryPress enter to validate this pathPreviewPreviousPrevious cursor positionPrevious warning/errorPrint current file...Print preview...Printing...Process failed to startProject ExplorerProject explorerProject name:Project...PromptsPropertiesProvided featuresPyQtPyQt API #1 is the default
API for Python 2. PyQt API #2 is the default API for Python 3 and is compatible with PySide.PyQt4 API ReferencePyQt4 Reference GuidePython ConsolePython Path ManagerPython documentationPython executablePython filesPython help:Python scriptsPython(x,y)Python(x,y) documentation folderPython(x,y) launcherQt (PyQt/PySide)Qt DesignerQt LinguistQt documentationQt examplesQt windows styleQt-Python bindings library selection:Quick referenceQuick switch layout #%d has not yet been defined.QuitR&emove block commentRaw textRe-run &last scriptReal-time code analysis on the EditorReal-time code style analysis on the EditorRecord array fields:RedoRefreshRefresh interval: Refresh list of module names available in PYTHONPATHRefresh periodicallyRegular expressionRelated projectsReload last sessionRemoval errorRemoveRemove comment block around current line or selectionRemove from PYTHONPATHRemove pathRemove references:Remove trailing spacesRenameRename...Render mathematical equationsReplace allReplace stringReplace tab characters by space charactersReplace with:Replace/findReport issue...Reset to default valuesReset window layoutResizeResize rows to contentsResolution:Restart kernelRestart kernel?RestoreRestore current paneRestore original tree layoutRestore pane to its original sizeRevert file from diskRich TextRich text font styleRich text help on the Object InspectorRow separator:RunRun &selection or current lineRun Python scriptRun SettingsRun Settings dialogRun a Python scriptRun a fileRun again last fileRun again this programRun cellRun cell and advanceRun codeRun configurationRun current cell (Ctrl+Enter) [Use #%% to create cells]Run current cell and go to the next one (Shift+Enter)Run current lineRun fileRun script:Run selectionRun selection or current lineRun settingsRun settings for %sRun toolbarRun until current function or method returnsRunning an external system terminal is not supported on platform %s.Sav&e allSaveSave &as...Save Python scriptSave all filesSave all files before running scriptSave arraySave current file as...Save current history log (i.e. all inputs and outputs) in a text fileSave current session and quit applicationSave dataSave data as...Save fileSave history logSave history log...Save sessionSave session and quit...SciPy documentationScroll automatically to last entrySearchSearch canceledSearch in all directories listed in sys.path which are outside the Python installation directorySearch in current directory hg repositorySearch patternSearch recursively in this directorySearch stringSearch text in multiple filesSearch toolbarSelect AllSelect a new fontSelect a run configuration:Select an existing workspace directory, or create a new oneSelect directorySelect fileSelect projects to importSelect projects which are related to %sSelect ssh keySelect the Python interpreter executable binary in which Spyder will run scripts:Set UMR excluded (not reloaded) modulesSet as current console's working directorySet console working directorySet current console (and file explorer) working directory to current script directorySet current working directorySet external editor executable pathSet font styleSet history maximum entriesSet layout %dSet maximum line countSet shell font styleSet the GUI toolkit used by
Matplotlib to show figures (default: Qt4Agg)Set the maximum number of lines of text shown in the console before truncation. Specifying -1 disables it (not recommended!)Set this to detach any
menu from the main windowSet this to open external
Python files in an already running instance (Requires a restart)Set/Clear breakpointSet/Edit conditional breakpointSetting up main window...SettingsShell special commands:Show (read-only) sys.pathShow CPU usage everyShow SourceShow TODO/FIXME/XXX/HINT/TIP/@todo comments listShow absolute pathShow advanced optionsShow all filesShow and edit current user environment variables in Windows registry (i.e. for all sessions)Show and edit environment variables (for current session)Show arrays min/maxShow blank spacesShow code analysis warnings/errorsShow current directory onlyShow elapsed timeShow horizontal scrollbarShow icons and textShow imageShow line numbersShow memory usage everyShow reloaded modules listShow special commentsShow sys.path contentsShow sys.path contents...Show tab barShow todo listShow vertical line afterShow warning when killing running processShow warning/error listShow/hide global variables explorerShow/hide outline explorerShow/hide project explorerSide areas:SiftSizeSize: Skip rows:SmartSort files according to full pathSour&ceSourceSource codeSource toolbarSphinx %s is currently installed.Split horizontallySplit horizontally this editor windowSplit verticallySplit vertically this editor windowSpyder EditorSpyder Internal Console This console is used to report application internal errors and to inspect Spyder internals with the following commands: spy.app, spy.window, dir(spy) Please don't use it to run your code Spyder data filesSpyder 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.Spyder documentationSpyder sessionsSpyder support...Spyder tutorialSsh keyStart searchStartupStatistics on source files only:
(Python, C/C++, Fortran)

%s files.
%s lines of code.Status barStepStep IntoStep ReturnStep into function or method of current lineStopStop searchStop the current commandString not foundString:Style analysisSupport for graphics (Matplotlib)Supported filesSwitch to/from layout %dSymbolic MathematicsSymbolic mathematics in the IPython ConsoleSynchronizeSynchronize Spyder's path list with PYTHONPATH environment variableSynchronize...Syntax color scheme: Syntax coloringSyntax highlighting for Matlab, Julia and other file typesSystem commands:TIFF imagesTabTab always indentTab stop width:Tasks (TODO, FIXME, XXX, HINT, TIP, @todo)Tear off menusTemporary fileTerminalTerminateTerminated.Text and margin font styleText editorText filesThe 'xlabels' argument length do no match array column numberThe 'ylabels' argument length do no match array row numberThe 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.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.The authenticity of host %s can't be established. Are you sure you want to continue connecting?The authenticity of the host can't be establishedThe 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.The current workspace has not been configured yet. Do you want to do this now?The folder %s does not contain a valid %s projectThe following are the default %s. These options may be overriden using the %s dialog box (see the %s menu)The following conflicts have been detected:The following directory is not a Spyder workspace:
%s

Do you want to create a new workspace in this directory?The following directory is not empty:
%s

Do you want to continue?The following directory is not in workspace:
%s

Do you want to continue (and copy the directory to workspace)?The following error occured when calling Sphinx %s.
Incompatible Sphinx version or doc string decoding failed.

Error message:
%sThe following modules are not installed on your machine: %sThe following working directory is not valid:
%sThe kernel failed to start!! That's all we know... Please close this console and open a new one.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.The project %s is already opened!The project root path directory is inside the workspace but not as the expected tree level. It is not a directory of the workspace:
%sThe workspace was unable to load or save %s

Please check if you have the permission to write the associated configuration files.This directory is already included in Spyder path list.
Do you want to move it to the top of the list?This entry is incorrectThis feature requires Sphinx 1.1 or superior.This feature requires the Matplotlib library. It seems you don't have it installed.This feature requires the Rope or Jedi libraries. It seems you don't have either installed.This feature requires the Sympy library. It seems you don't have it installed.This is a remote kernelThis is a temporary script file.This is the current workspace directoryThis 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 editorThis lets you load graphics support without importing the commands to do plots. Useful to work with other plotting libraries different to Matplotlib or to develop GUIs with Spyder.This method is the only way to have colorized standard error channel when the output channels have been merged.This option lets you hide the message shown at the top of the console when it's opened.This option loads the Sympy library to work with.
Please refer to its documentation to learn how to use it.This option will act on
libraries such as Matplotlib, guidata or ETSThis option will be applied the next time a Python console or a terminal is opened.This option will be applied the next time a console is opened.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)This option will override the PYTHONSTARTUP environment variable which defines the script to be executed during the Python console startup.This path is incorrect. Enter a correct directory path, then press enter to validateThis 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?To boolTo complexTo doTo floatTo intTo strToolbarsTransposeTruncate valuesTrying to kill a kernel?Tunnel '%s' failed to startTupleTypeUMRUMR excluded modules: (example: guidata, guiqwt)UMR forces Python to reload modules which were imported when executing a script in the external console with the 'runfile' function.Unable to connect to IPython %sUnable to load pageUncommentUndoUnexpected error: see internal consoleUnindentUnindent current line or selectionUnlockUnmatched parentheses:Update module names listUsageUse %s+T when the console is selected to open a new oneUse a completion widgetUse a pager to display additional text inside the consoleUse a single instanceUse a widget instead of plain text output for tab completionUse symbolic mathUse the following Python interpreter:Use the following file:Use the following startup script:Use the greedy completerUseful if you don't want to fill the console with long help or completion texts. Note: Use the Q key to get out of the pager.User Module Reloader (UMR)ValueValue:Variable NameVariable explorerVariablesVertical dockwidget tabsVertical dockwidget title barsViTablesView and edit DataFrames and Series in the Variable ExplorerWarningWeb page filesWelcome to Spyder!When opening a fileWhen 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.When saving a fileWhole wordsWidth:WinPythonWinPython control panelWindow layout will be reset to default settings: this affects window position, size and dockwidgets. Do you want to continue?Working directoryWorking directory:WorkspaceWrap linesXML filesYaml filesYou can also run a whole file at startup instead of just some lines (This is similar to have a PYTHONSTARTUP file).You can run several lines of code when a console is started. Please introduce each one separated by commas, for example:
import os, import sysYou 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.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.Zoom inZoom outZoom resetarraycharacterscodecopydatadeletedpielementsfilegettext filesguidata documentationguidata examplesguiqwt documentationguiqwt examplesinchesinterruptedinvalid regular expressionits own configuration filelistmatches inmoveotherpermission denied errors were encounteredpixelsreStructured Text filesread onlytabtabletextthe current file directorythe following directory:the following projects:
%sthe global working directorythe same as in last sessionthe script directorythis dialogtutorialuntitledusername@hostname:portvariable_nameProject-Id-Version: 2.1 POT-Creation-Date: 2015-11-24 20:56+COT PO-Revision-Date: 2015-03-16 22:59-0500 Last-Translator: Sylvain Corlay Language-Team: Python Language: fr MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: pygettext.py 1.5 X-Generator: Poedit 1.5.4 Installé Requis et lignes lignes ms%s ne sont actuellement pas pris en chargeLes tableaux %sÉditeur de %s%s est déjà en cours d'exécution dans un processus séparé. Souhaitez-vous tuer ce processus avant d'en démarrer un autre ?&Fermer&Fermer le fichier&Configurer...&Déboguer&ÉditionModifi&er le fichier&FichierRechercher dans des &fichiersRec&hercher&Police...A&ide&Nouveau fichier...&Ouvrir...Im&primer...&Quitter&Remplacer&RéinitialiserE&xécutionExécute&r...&Enregistrer&RechercheOu&tils&Affichage(Expérimental) : autocomplétion, aller à la définition, aide.(appuyer sur Entrée pour modifier le fichier)2 espaces4 espaces%s contient des caractères de fin de ligne mélangés.
Spyder va corriger ceci automatiquement.%s a été modifié en dehors de Spyder.
Souhaitez-vous le recharger et perdre ainsi vos modifications ?%s a été modifié.
Souhaitez-vous enregistrer ces changements ?%s n'est pas accessible (ce fichier a peut-être été supprimé, déplacé ou renommé en dehors de Spyder).
Souhaitez-vous le fermer ?L'option -u est ajoutée aux autres options spécifiées iciImpossible de %s %s

Message d'erreur :
%sImpossible d'assigner la valeur de l'objet.

Message d'erreur :
%sImpossible de créer le fichier %s

Message d'erreur :
%sImpossible de créer le répertoire %s

Message d'erreur :
%sImpossible de trouver un programme externe.

%sImpossible d'ouvrir '%s'

Message d'erreur :
%sImpossible de déplacer %s

Message d'erreur :
%sImpossible d'afficher les données

Message d'erreur :
%sImpossible de passer à l'étape suivante

Merci de vérifier votre saisie.

Message d'erreur :
%sImpossible d'ouvrir le projet Pydev %s

Message d'erreur :
%sImpossible de renommer l'élément %s

Message d'erreur :
%sImpossible d'accéder aux données

Message d'erreur :
%sImpossible d'enregistrer le tableau

Message d'erreur :
%sImpossible d'enregistrer l'espace de travail

Message d'erreur :
%sImpossible d'enregistrer le fichier '%s'

Message d'erreur :
%sImpossible d'enregistrer le script '%s'

Message d'erreur :
%sImpossible d'afficher l'image

Message d'erreur :
%sExtension de fichier non pris en charge '%s'

Souhaitez-vous néanmoins ouvrir ce fichier (en choisissant un format de fichier connu) ?Type de fichier non pris en charge '%s'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.En cours d'exécution...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, ...).
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.Attention: les changements seront pris en compte séparément?Le démarrage d'une console Python a échoué !Un projet nommé %s existe déjàAPI n°1API n°2Sélection de l'API des objets QString et QVariant :À propos de %sÀ propos de %s...ActiverAjouter un &bloc de commentairesAjouter un bloc de commentaires autour de la sélection ou de la ligne en cours d'éditionAjouter un cheminAjouter à PYTHONPATHOptions supplémentairesOptions supplémentairesAdresse :Options avancéesOptions avancéesToutes les modifications effectuées sur %s seront perdues.
Souhaitez-vous revenir à la version du fichier enregistrée sur le disque ?Tous les fichiersTous les fichiers (*)Tous les fichiers (*.*)Toujours afficher %s lors de la première exécution d'un scriptUne erreur est survenue lors du démarrage du noyau.AnalysePanneaux et barres d'outils animésSouhaitez-vous vraiment redémarrer le noyau ?ArgumentsArguments...Arguments : %sÉditeur de tableauxCe tableau est videLes tableaux de plus de trois dimensions ne sont pas pris en chargeDemander confirmation avant de fermer une consoleAu démarrage, Spyder reprendra le répertoire de travail global de la dernière sessionAu démarrage, le répertoire de travail global est :Au démarrage, le répertoire de travail global sera le chemin d'accès spécifié iciInvite de commandes attaché (débogage)Tentative de fermeture du processus. Il est possible que le processus ne s'arrête pas suite à cette tentative, et propose à l'utilisateur de sauvegarder les fichiers non-enregistrés, etc...AttributAppel automatiqueL'appel automatique dans IPython va automatiquement appeler les objets appelables même sans parenthèses explicites.
Par exemple str 43 deviendra automatiquement str(43).Appel automatique :AutomatiqueComplétion de code automatiqueConnexions automatiquesImport automatiqueIndentation automatique après 'else', 'elif', etc.Insertion automatique de guillemets de clôtureInsertion automatique de ':' après 'for', 'if', 'def', etc.Insertion automatique des parenthèses, crochets et accoladesImporter automatiquement Pylab et NumPySupprimer automatiquement les espaces en fin de ligne lors de l'enregistrementRafraîchissement automatiqueAxe :RetourSortie :Couleur de fondFond :Fichiers BatchGrasPoint d'arrêtPoints d'arrêtParcourirSélectionner un répertoire de rechercheSélectionner un répertoire de travailExplorer le dépôtNaviguer dans les ongletsTamponTampon...Tampon : Tampon : Objet intégré :Fichiers CFermer t&outC&onsolesFichiers C++Informations sur l'utilisation processeur et mémoire dans la barre d'étatOccupation CPU : requiert la bibliothèque `psutil` (>=v0.3)CPU :Fichiers texte CSVAnnulerRetour chariot (Mac)Retour chariot et retour à la ligne (Windows)Respecter la casseComplétion de code sensible à la casseCellule commençant ligne %sSélectionner le répertoire de base du fichierAller au répertoire parentUtilisation du backend Qt pour MayaviClasse déclarée ligne %sEffacer tous les résultatsSupprimer les points d'arrêt dans tous les fichiersEffacer la consoleEffacer la ligneEffacer la ligne ou le blocEffacer la liste des fichiers récentsEffacer la consoleEffacer le contenu de la consoleEffacer cette listeContenu du presse-papiersFermer tous les fichiers ouvertsFermer le fichier en cours d'éditionFermer le volet courantFermer l'ongletFermer le projetFermer ce panneauFermer cette fenêtre d'éditionFermer les projets non associésFermer la fenêtreIntrospection et analyse de codeAnalyse de codeL'analyse de code requiert pyflakes %s+Replier toutReplier la sélectionParamètres de coloration syntaxiqueColoriser le canal d'erreur standard (codes d'échappement ANSI)Colonne min/maxSéparateur de colonne :Colonne :Invite de commandesArguments en ligne de commande :Options en ligne de commande :CommenterCommenter la sélection ou la ligne en cours d'éditionCommentaire :Commentaires :CommiterCondition :ConfigurationsConflitsConnecter à un noyau existantConnexion au noyau...Erreur de connexionInformation de connexion :ConsoleAide sur la consoleContexteContinuerContinuer l'exécution jusqu'au prochain point d'arrêtErreur de conversionConvertir les caractères de fin de ligneConvertir en fichier PythonCopierCopier le chemin d'accès dans le presse-papierCopier dans le presse-papierCopier sans les préfixesImpossible d'établir la connection au serveur distantImpossible d'ouvrir un tunnel ssh. L'erreur rencontrée est: Créer une nouvelle fenêtre d'éditionCellule actuelle :Ligne actuelle :Variables d'environnement de l'utilisateur...Marges personnalisées :Dispositions de fenêtres personnaliséesCouperFichiers Cython/PyrexFond noirDonnéesDataFrameDéboguerDéboguer le scriptBarre d'outil de débogageDéboguer avec winpdbDébogageContrôle du débogageCette 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.Option relative au rendu des figures dans ce backendConsole Python dédiéePar défaut (interpréteur identique à celui dans lequel Spyder est exécuté)API par défautScript PYTHONSTARTUP par défautPar défaut : 4Par défaut : 6Par défaut :
In [<span class="in-prompt-number">%i</span>]:Par défaut :
Out[<span class="out-prompt-number">%i</span>]:Bibliothèque par défautLe répertoire de travail par défaut est :Définition :SupprimerSupprimer...DictionnaireAffichageAfficher des info-bullesAfficher le message d'accueilSouhaitez-vous réellement supprimer %s ?Souhaitez-vous réellement supprimer le projet %s ?

Notez que les fichiers et répertoires du projet ne seront pas supprimés du disque.Souhaitez-vous vraiment supprimer le chemin sélectionné ?Souhaitez-vous réellement renommer %s et remplacer le ficher existant %s ?Voulez-vous fermer les toutes les autres consoles connectées au même noyau que celle-ci ?Souhaitez-vous supprimer les éléments sélectionnés ?Souhaitez-vous supprimer l'élément sélectionné ?TerminerDupliquerEOLETS_TOOLKIT:ModifierÉditeurs dans le processus distantModifier les filtresModifier les filtres...ModifierModifier les projets associésModifier le modèle (nouveaux modules)Barre d'outil éditionÉditeurEditeur : complétion de code, aller à la définition, etc.Les tableaux NumPy, images PIL, listes, tuples et dictionnaires seront modifiés dans un éditeur exécuté dans le processus distant. Cela permet d'éviter de transférer de gros volumes de données entre le processus distant et Spyder (à travers le socket).
  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.Presse-papiers videActive 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>.Activer l'UMRActiver le rafraîchissement automatiqueActiver le moniteurL'activation de cette option permet d'ignorer les erreurs liées aux changements d'API de PyQt. Vu que PyQt ne prend pas en charge le changement dynamique d'API, il est fortement recommandé d'utiliser cette fonctionnalité exceptionnellement, par exemple pour du débogage.Fichiers EnamlEncodage :Caractères de fin de ligneFins de ligne :Entrée valide la complétion de codeEnthought Tool SuiteLe logiciel Enthought Tool Suite (ETS) prend en charge les interfaces graphiques PyQt4 (qt4) et wxPython (wx).Variables d'environnementVariables d'environnement...ErreurExclure les références en lettres capitalesExclure les références commençant par une majusculeExclure les références privéesExclure les références dont le type n'est pas supporté par l'espace de travail (en particulier, l'enregistrement ne fonctionnera pas)Exclure les références dont le nom s'écrit en lettres capitalesExclure les références dont le nom commence par un tiret basExclure les références dont le nom commence par une lettre majusculeExclure les types non supportésExclure :Expression des noms de fichier à exclureExécutablesExécuter dans une nouvelle console Python dédiéeExécuter dans un terminal système externeExécuter dans la console Python ou IPython activeProjet Pydev existantProjet Spyder existantRépertoire existantSortirQuitter le débogageDéplier toutDéplier la sélectionOutils externesÉditeur externeChemin d'accès de l'exécutable :Éditeur externe...Éditeur externe :Modules externesLe fichier %s existe déjà.
Souhaitez-vous le remplacer ?Explorateur de fichiersGestionnaire de fichiersBarre d'outil fichiersFichier...Les fichiers sont créés dans :Les fichiers sont ouverts depuis :FiltreFiltre :Rechercher le &suivantRechercher le &précédentRecherche dans des fichiersRechercherRéparer automatiquement et afficher un message d'avertissementCorriger l'indentationFormat de flottantLe dossier %s existe déjà.Nom du dossier :Dossier...Police d'écriturePolice : Pour ajouter la prise en charge de %s, merci d'installer
l'un des outils suivants :

%sPour 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).FormatLe format (%s) n'est pas valideFormat :Fichiers FortranToujoursMode plein écranFonction déclarée ligne %sImages GIFBackend graphique :Éditeur graphique :GénéralOptions généralesRépertoire de travail globalAller à la position du curseurAller à la définition de l'objetAller à la dernière position d'éditionAller à la ligne...Aller à la ligne :Afficher le message d'avertissement ou d'erreur précédentAller à la position suivante du curseurAfficher le message d'avertissement ou d'erreur suivantAller à la position précédente du curseurGraphiquesSortie graphiqueComplétion avancéeHauteur :AideAide...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.Ici :Dépôt MercurialMasquer les options avancéesSurligner la cellule en cours d'éditionSurligner la ligne en cours d'éditionSurligner les résultatsSurligner les occurences aprèsAstuce : la touche Alt affiche les accélérateursHistogrammeHistoriqueTaille de l'historique : HistoriqueFichiers d'historiqueHistorique...AccueilNom d'hôteFichiers IDLIPythonConsole IPythonIntégration de la console IPythonConsole IPythonDocumentation de IPythonDocuments IPython 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éesSi 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éesSi cette option est activée, presser la touche Tab provoquera toujours l'indentation de la ligne, quelle que soit la position du curseur (lorsque cette option est activée, la complétion de code reste accessible via le raccourci Ctrl+Espace)Si cette option est activée, cliquer sur le nom d'un objet (click gauche + touche Ctrl) ira à la définition de cet objet (si celle-ci est trouvée).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)Ignorer les erreurs de changement d'API (sip.setapi)ImportImporter en tant queImporter des donnéesImporter un répertoireErreur d'importImporter un projet PydevImporter un projet existantImporter depuis le presse-papiersAssistant d'importationInclure :Expression des noms de fichier à inclureIndenterIndenter la sélection ou la ligne en cours d'éditionCaractères d'indentation : IndiceIndice :Initialisation...En ligneBackend intégréEn entrée :InsérerInspecter l'onglet courantModules Python installésInstance :Retour arrière ("backspace") intelligentInteragirInteragir avec la console Python après l'exécutionVisualisation interactive de donnéesInterfaceConsole interneOptions de la console interneÉditeur interne :Introduction à IPythonIntrospectionChemin d'accès de répertoire incorrectChemin d'accès de fichier incorrectNom de projet incorrect.

Le nom doit respecter l'expression régulière suivante :
%sLe noyau a été arrêté de façon inattendue. Redémarrez le noyau pour continuer d'utiliser cette console.Impossible de convertir ce notebook. Message d'erreur: 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.Impossible d'effacer les résultats de ce notebook: Impossible d'exécuter ce fichier dans un terminal externeItaliqueImages JPEGFichiers JSONFichiers JavascriptFichiers JsonFichiers JuliaNoyauNoyau %sLe processus du noyau est soit distant, soit non spécifié : impossible d'arrêter le noyau.Le processus du noyau est soit distant, soit non spécifié : impossible de redémarrer le noyau.CléClé :Raccourcis clavierMot-clé :TerminerTue le processus, entraînant une sortie brutale et immédiate du programmeDernière position d'éditionFond blancFond blancLigne %sNombre de lignes :Retour à la ligne (UNIX)Ligne :Lignes :Lien vers la définition d'un objetLien :ListeCharger une session SpyderCharger une session...Chargement de "%s" en cours...Chargement de la console IPython...Chargement de l'éditeur...Chargement de la console externe...Chargement de l'explorateur de fichiers...Chargement du journal d'historique...Chargement de l'explorateur d'espace de noms...Chargement de l'inspecteur d'objet...Chargement de l'aide en ligne...Chargement de l'explorateur de structure...Chargement de l'explorateur de projet...VerrouillerFichiers MATLABBarre d'outil principaleGarder le focus dans l'éditeur après l'exécution d'une cellule ou d'une sélectionMasqueDonnées masquéesParenthèse fermée :Fichiers MatlabMatplotlibDocumentation de MatplotlibAgrandir le volet courantNombre maximum d'entréesNombre maximum de lignesNombre maximum de fichiers récentsNombre maximum de fichiers récents...Occupation mémoire : requiert la bibliothèque `psutil` (>=v0.3) sur les plateformes autres que WindowsMémoire :Fusionner les canaux de sortie et d'erreur du processusFusionner les canaux de sortie et d'erreur du processus signifie que les erreurs ne seront plus affichées en rouge, mais cela entraînera également une amélioration des performances d'affichage et une meilleure réactivité de la console.Méthode déclarée ligne %sMod1Mod2Mod3Change l'affichage des invites de commande d'entrée et de sortie de la console.ModuleLe 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.Module ou paquet :Module...MoniteurDescendrePlacer en dernierPlacer en premierMonterDéplacer...Fichiers NSISNomFiltres sur les noms de fichiers :NouveauNouveau fichierNouveau répertoireNouveau moduleNouveau nom :Nouveau paquetNouveau projetNouveau projet...Vous découvrez Spyder ? Lisez notreNouvelle fenêtreSuivantPosition suivante du curseurAvertissement précédentAucun client IPython n'est actuellement sélectionné pour exécuter %s.

Merci d'ouvrir un nouveau client IPython et de réessayer.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.Aucun argumentAucune documentation disponibleAucun code source disponible.Texte normal :Aucune donnée ne peut être importée depuis le presse-papiers.Tableaux NumPyDocumentation de NumPyManuel de référence de NumPyManuel de l'utilisateur de NumPyTableaux NumPy compressésNombre :Documentation de Numpy et ScipyObjetL'objet %s n'est pas pris en charge par le protocole de sérialisation de PickleInspecteur d'objetsOccurence :Désactivé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)Un onglet par scriptDocumentation en ligneAide en ligneUtilisé uniquement dans le cas du format PNG. Par défaut: 72OuvrirOuvrir un invite de &commandesFichiers &récentsOuvrir un fichier de connexion IPythonOuvrir une console &Python iciOuvrir une console &PythonOuvrir un &terminalOuvrir un invite de commandes WindowsOuvrir une nouvelle console IPython connecté à un noyau existantOuvrir un terminal de commandes dans SpyderOuvrir une console &IPythonOuvrir une console IPythonOuvrir un invite de commandes iciOuvrir un fichierOuvrir le fichier en tant que :Ouvrir le projetOuvrir une sessionOuvrir un terminal iciFichiers OpenCLAfficher cette variable peut prendre du temps. Souhaitez-vous néanmoins continuer ?Dépendances optionnellesDépendances optionnelles...OptionsStructureEn sortie :Images PNGPYTHONPATHGestionnaire de PYTHONPATHSubstitution de PYTHONSTARTUPNom du paquet :Paquet...VoletsParentMot de passeMot de passe ou passphrase de clé sshCollerFichiers patch et diffChemin vers un fichier de connexion ou identifiant de noyauChemin vers la clé sshUtilise 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.Analyser uniquement lors de l'enregistrementAnalyser lors de l'enregistrement et toutes lesDroits d'accès :Fichiers pickleTexte brutPolice d'écriture du texte brutMerci d'installer Sphinx pour obtenir la documentation en texte enrichi.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).Merci d'installer matplotlib ou guiqwt.Veuillez noter que ces changements ne seront pris en compte que dans les nouvelles consoles Python/IPythonVeuillez noter que ces changements ne seront pris en compte que dans les nouvelles consoles PythonTracerAfficher la console interne en cas d'erreur inattenduePré&férencesPréférencesPréférences > Inspecteur d'objetsAppuyer sur Entrée pour valider cette saisieAppuyez sur Entrée pour valider ce chemin d'accèsAperçuPrécédentPosition suivante du curseurAvertissement suivantImprimer le fichier en cours d'édition...Aperçu avant impression...Impression en cours...Le processus n'a pas pu démarrerExplorateur de projetsExplorateur de projetsNom du projet :Projet...Invites de commandePropriétésFonctionnalités associéesPyQtL'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.Documentation de l'API de PyQt4Guide de référence de PyQt4Console PythonGestionnaire de chemins d'accès PythonDocumentation de PythonExécutable PythonFichiers PythonAide Python :Scripts PythonPython(x,y)Dossier de documentation Python(x,y)Accueil de Python(x,y)Qt (PyQt/PySide)Qt DesignerQt LinguistDocumentation de QtExemples QtStyle de fenêtres QtSélection de la bibliothèque d'interfaçage Qt :Référence rapideLa disposition de fenêtre personnalisée n°%d n'a pas encore été définie.Quitter&Supprimer un bloc de commentairesText brutExécuter de nouveau le &dernier scriptAnalyse de code temps réel dans l'éditeurAnalyse de code temps réel dans l'éditeurChamps du tableau : RépéterRafraîchirPériode de rafraîchissement : Mise à jour de la liste des modules disponibles notamment à travers PYTHONPATHRafraîchir périodiquementExpression régulièreProjets associésRecharger la session précédenteErreur de suppressionSupprimerSupprimer le bloc de commentaires autour de la ligne en cours d'éditionRetirer de PYTHONPATHSupprimerSupprimer des références :Supprimer les espaces en fin de ligneRenommerRenommer...Styliser les équations mathématiquesRemplacer toutChaîne de caractères de remplacementRemplacer les caractères de tabulation par des espacesRemplacer par :Remplacer/rechercherRapport d'erreur...Rétablir les valeurs par défautRéinitialiser la disposition des fenêtresAjusterAjuster la hauteur des lignesRésolution :Redémarrer le noyauRedémarrer le noyauRestaurerRestaurer le volet courantRestaurer l'organisation initiale de l'arbreRestaurer le volet à sa taille d'origineRevenir à la version du fichier enregistrée sur le disqueTexte enrichiPolice d'écriture du texte enrichiTexte enrichi dans l'inspecteur d'objetsSéparateur de ligne :ExécuterExécuter la &sélection ou la ligne couranteExécuter un script PythonOptions d'exécutionla fenêtre Options d'exécutionExécuter un script PythonExécuter un fichierExécuter de nouveau le dernier fichierExécuter de nouveau ce programmeExécuter la celluleExécuter la cellule et avancerExécuter du codeConfiguration d'exécutionExécuter la cellule courante [Utiliser #%% pour délimiter les cellules]Exécuter la cellule en cours d'édition et aller à la suivante (voir la documentation de l'Editeur, pour plus de détails sur les cellules)Exécuter la ligne en coursExécuter le fichierExécuter un script :Exécuter la sélectionExécuter la sélection ou le bloc de lignesOptions d'exécutionOptions d'exécution pour %sBarre d'outil exécutionExécuter jusqu'au retour de la fonction ou méthodeL'exécution dans un terminal système externe n'est pas prise en charge sur la plateforme %s.Enregistrer &toutEnregistrerEnregistrer &sous...Enregistrer le script PythonEnregistrer tous les fichiersEnregistrer tous les fichiers avant l'exécution d'un scriptEnregistrer le tableauEnregistrer le fichier en cours d'édition sous un autre nom...Enregistrer l'historique complet (toutes les entrées et sorties) dans un fichier texteEnregistrer la session en cours et quitter l'applicationEnregistrer les donnéesEnregistrer les données sous...Enregitrer un fichierEnregistrer l'historiqueEnregistrer l'historique...Enregistrer la sessionEnregistrer la session et quitter...Documentation de SciPyDéfiler automatiquement jusqu'à la dernière ligneRechercherRecherche annuléeRechercher dans tous les répertoires listés dans sys.path qui sont situés en dehors du répertoire d'installation de PythonRechercher dans le dépôt Mercurial du répertoire courantExpression recherchéeRechercher de manière récursive dans ce répertoireChaîne de caractères à rechercherRechercher une chaîne de caractères dans plusieurs fichiers à la foisBarre d'outil de rechercheSélectionner toutSélectionner une police d'écritureSélectionner une configuration d'exécution :Sélectionner un espace de travail existant, ou en créer un nouveauSélectionner un répertoireSélectionner un fichierSélectionner les projets à importerSélectionner les projets à associer à %sSélectionner une clé sshSélectionner l'interpréteur Python utilisé pour exécuter des scripts:Définir la liste des modules non rechargés par l'UMRChanger le répertoire de travail de la console actuelleRépertoire de travail de la consoleChoisir le répertoire du script comme répertoire de travail de la console courante (et de l'explorateur de fichier)Changer le répertoire de travailModifier le chemin d'accès de l'éditeur externeChanger la police d'écritureModifier le nombre d'entrées maximum de l'historiqueDéfinir la disposition %dModifier le nombre maximum de lignesChanger la police d'écriture de la consoleSpécifie la bibliothèque d'interfaces graphiques à utiliser pour l'affichage des figures Matplotlib (par défaut : Qt4Agg)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é).Activer cette option rend détachables tous les menus de la fenêtre principaleActiver cette option afin que les fichiers ouverts
depuis l'extérieur soit affichés dans une instance unique de Spyder
(redémarrage requis)Ajouter/supprimer un point d'arrêtAjouter/modifier un point d'arrêt conditionnelConfiguration de la fenêtre principale...OptionsCommandes spéciales de la console :Afficher le contenu de sys.path (lecture seule)Afficher la charge du CPU toutes lesAfficher les sourcesAfficher la liste des commentaires du type TODO/FIXME/XXX/HINT/TIP/@todoAfficher les chemins completsAfficher les options avancéesAfficher tous les fichiersAfficher et modifier les variables d'environnement de l'utilisateur courant dans Windows (c'est-à-dire directement dans la base de registre)Afficher et modifier les variables d'environnement (pour la session en cours)Afficher les min/max des tableauxAfficher les espacesAfficher la liste des avertissements/erreurs provenant de l'analyse de codeAfficher uniquement le répertoire courantAfficher le temps écouléBarre de défilement horizontalAfficher icônes et textesAfficher l'imageAfficher les numéros de ligneAfficher l'occupation mémoire toutes lesAfficher les modules rechargésAfficher les commentaires spéciauxAfficher le contenu de sys.pathAfficher le contenu de sys.path...Afficher la barre d'ongletsAfficher la liste des tâchesAfficher une ligne verticale aprèsAfficher un avertissement à l'interruption d'un processusAfficher la liste des avertissements/erreursAfficher/masquer l'explorateur de variables globalesAfficher/masquer l'explorateur de structureAfficher/masquer l'explorateur de projetsZones latérales :SiftTailleTaille : Sauter des lignes :IntelligentClasser les fichiers suivant leur chemin completSour&ceSourceCode sourceBarre d'outils code sourceSphinx %s n'est pas installé.Séparation horizontaleSéparer en deux horizontalement cette fenêtre d'éditionSéparation verticaleSéparer en deux verticalement cette fenêtre d'éditionÉditeur de SpyderConsole interne de Spyder Il s'agit d'une console de débogage utilisée par Spyder pour signaler des erreurs internes ou pour inspecter les entrailles de l'application avec les commandes ci-dessous : spy.app, spy.window, dir(spy) Ne l'utilisez pas pour exécuter votre propre code. Fichiers SpyderSpyder 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.Documentation de SpyderSessions SpyderSupport technique de Spyder...Tutoriel de SpyderClé sshDémarrer la rechercheDémarrageStatistique sur les fichiers source uniquement:
(Python, C/C++, Fortran)

%s fichiers.
%s lignes de code.Barre d'étatPasPas vers l'intérieurPas vers l'extérieurAvancer dans la fonction, méthode ou classe de la ligne en coursArrêterArrêter la rechercheInterrompre la commande en coursChaîne de caractères non trouvéeChaîne :Analyse de stylePrise en charge des graphes (Matplotlib)Fichiers compatiblesBasculer vers/depuis la disposition %dCalcul formelMathématiques symboliques pour la console IPythonSynchroniserSynchronise la liste des chemins d'accès de Spyder avec celle de la variable d'environnement PYTHONPATHSynchroniser...Thème de coloration syntaxique : Coloration syntaxiqueColoration syntaxique pour Matlab, Julia et d'autres types de fichierCommandes systèmes :Images TIFFTabToujours indenter avec la touche TabLargeur des tabulations :Tâches (TODO, FIXME, XXX, HINT, TIP, @todo)Menus détachablesFichier temporaireTerminalQuitterTerminé.Police d'écriture du texte et de la margeÉditeur de texteFichiers texteLa taille de 'xlabels' ne correspond pas au nombre de colonnes du tableauLa taille de 'ylabels' ne correspond pas au nombre de lignes du tableauLe 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.L'inspecteur d'objet peut automatiquement afficher l'aide sur un objet dès l'ouverture d'une parenthèse.L'identité du serveur %s ne peut pas être confirmée. Êtes-vous sûr de vouloir poursuivre ?L'identité du serveur ne peut pas être confirméeLe 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.L'espace de travail n'a pas encore été défini. Souhaitez-vous le faire maintenant ?Le dossier %s ne contient pas de projet %s valideLa 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)Les conflits suivants ont été détectés :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 ?Le répertoire suivant n'est pas vide :
%s

Souhaitez-vous néanmoins continuer ?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) ?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 :
%sLes modules suivants ne sont pas installés sur votre ordinateur : %sLe répertoire de travail suivant n'est pas valide :
%sLe 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.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.Le projet %s est déjà ouvert !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 :
%sL'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.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 ?Cette saisie n'est pas correcteCette fonctionnalité nécessite l'installation de Sphinx 1.1+.Cette fonctionnalité nécessite l'installation du module Matplotlib. Ce dernier n'est apparemment pas installé.Cette fonctionnalité nécessite Rope ou Jedi. Il semble qu'aucun de ces modules ne soit présent.Cette fonctionnalité nécessite l'installation du module Sympy. Ce dernier n'est apparemment pas installé.Noyau distantCeci est un script temporaire.Ceci est l'espace de travail actifCeci est le répertoire de travail utilisé pour les nouvelles consoles (consoles Python/IPython et fenêtres de commandes), pour l'explorateur de fichiers, pour la recherche dans les fichiers et pour les fichiers créés dans l'éditeurImport automatique de toutes les fonctions de représentation graphique et de calcul numériqueCette méthode est le seul moyen de coloriser le canal d'erreur standard lorsque les canaux de sorties ont été fusionnés.Cette option permet de masquer la message d'accueil qui s'affiche à l'ouverture de la console.Activer cette option permet de travailler avec la bibliothèque Sympy.
Merci de consulter la documentation pour savoir comment l'utiliser.Cette option est prise en charge par les bibliothèques telles que Matplotlib, guidata ou ETSCette option sera prise en compte lors de la prochaine ouverture de console (interpréteur Python ou terminal).Cette option sera prise en compte lors de la prochaine ouverture de console.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)Cette option permet de remplacer le script de démarrage des consoles Python défini par la variable d'environnement PYTHONSTARTUP.Ce chemin d'accès n'est pas valide : veuillez entrer un chemin d'accès correct, puis appuyer sur Entrée pour le validerCeci 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 ?BooléenComplexeÀ faireFlottantEntierCaractèresBarres d'outilsTransposerTronquer les valeursTentative d'arrêt d'un noyauImpossible d'ouvrir le tunnel '%s'TupleTypeUMRModules non rechargés par l'UMR : (exemple: guidata, guiqwt)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'.Impossible de se connecter au noyau IPython `%s`Impossible de charger la pageDécommenterAnnulerErreur inattendue : voir console interneDésindenterDésindenter la sélection ou la ligne en cours d'éditionDéverrouillerParenthèse non fermée :Mise à jour de la liste des modulesUtilisationQuand la console est sélectionnée, utiliser %s+T pour ouvrir une nouvelle consoleUtiliser un widget de complétion de codeUtiliser un pager pour afficher l'aideOuvrir une seule instance de SpyderUtiliser un widget de complétion au lieu d'une liste en texte brutUtiliser le calcul formelUtiliser l'interpréteur Python suivant :Utiliser le fichier suivant :Utiliser le script de démarrage suivant :Utiliser la complétion avancéeLe pager permet d'éviter de remplir la console de texte d'aide. Note : utiliser la touche Q pour quitter le pager.User Module Reloader (UMR)ValeurValeur :Nom de variableExplorateur de variablesVariablesOnglets distribués verticalementBarres de titre orientées verticalementViTablesVoir et éditer les Dataframes et les Series dans l'explorateur de variablesAttentionFichiers webBienvenue dans Spyder !Lors de l'ouverture d'un fichierLors 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.Lors de l'enregistrement d'un fichierMots entiersLargeur :WinPythonPanneau de contrôle WinPythonLa disposition des fenêtres sera réinitialisée selon les réglages par défaut. Souhaitez-vous continuer ?Répertoire de travailRépertoire de travail :Espace de travailRetour à la ligne automatiqueFichiers XMLFichiers YamlOption similaire à PYTHONSTARTUP pour un interpréteur standardPlusieurs 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 sysLe 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.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 PythonAgrandirRéduireRéinitialisation du zoomtableaucaractèrescodecopierdonnéessupprimerpppélémentsfichierFichiers gettextDocumentation de guidataExemples guidataDocumentation de guiqwtExemples guiqwtpoucesinterrompuexpression régulière incorrecteson propre fichier de configurationlistecorrespondances trouvées dansdéplacerautredes erreurs d'autorisation d'accès ont été rencontréespixelsFichiers reSTlecture seuletabulationtableautextele répertoire du fichier en cours d'éditionle répertoire suivant :les projets suivants :
%sle répertoire de travail globalcelui utilisé lors de la dernière sessionle répertoire du fichier à exécutercette fenêtreTutorielsanstitreutilisateur@hôte:portnom_de_variablespyder-2.3.8/spyderlib/locale/fr/LC_MESSAGES/spyderlib.po0000664000000000000000000051017312626055324021533 0ustar rootroot# -*- 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" spyder-2.3.8/spyderlib/scientific_startup.py0000664000000000000000000001131212626055324020006 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/pil_patch.py0000664000000000000000000000434312566665770016074 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/interpreter.py0000664000000000000000000003063412606564142016460 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/defaults/0000755000000000000000000000000012626531443015342 5ustar rootrootspyder-2.3.8/spyderlib/defaults/defaults-2.4.0.ini0000664000000000000000000004347112566665770020342 0ustar rootroot[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) spyder-2.3.8/spyderlib/defaults/Readme.txt0000664000000000000000000000166512566665770017330 0ustar rootrootCopyright (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. spyder-2.3.8/spyderlib/defaults/defaults-3.0.0.ini0000664000000000000000000004153612566665770020337 0ustar rootroot[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) spyder-2.3.8/spyderlib/spyder.py0000664000000000000000000032346612626055324015432 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/qt/0000755000000000000000000000000012626531443014157 5ustar rootrootspyder-2.3.8/spyderlib/qt/QtCore.py0000664000000000000000000000140512626055324015727 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/qt/QtWebKit.py0000664000000000000000000000046212626055324016226 0ustar rootroot# -*- 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:ignorespyder-2.3.8/spyderlib/qt/QtGui.py0000664000000000000000000000056312626055324015567 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/qt/compat.py0000664000000000000000000002064312566665770016041 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/qt/QtSvg.py0000664000000000000000000000045412626055324015601 0ustar rootroot# -*- 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:ignorespyder-2.3.8/spyderlib/qt/__init__.py0000664000000000000000000000301612626055324016271 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/baseconfig.py0000664000000000000000000002517012626055322016211 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/widgets/0000755000000000000000000000000012626531443015201 5ustar rootrootspyder-2.3.8/spyderlib/widgets/internalshell.py0000664000000000000000000003776412626055324020441 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/pydocgui.py0000664000000000000000000001000012626055324017366 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/dataframeeditor.py0000664000000000000000000005767212626055324020730 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/editortools.py0000664000000000000000000005664312626055324020141 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/projectexplorer.py0000664000000000000000000016012512626055324021010 0ustar rootroot# -*- 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_() spyder-2.3.8/spyderlib/widgets/browser.py0000664000000000000000000002321012626055324017235 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/explorer.py0000664000000000000000000012762512626055324017431 0ustar rootroot# -*- 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_()) spyder-2.3.8/spyderlib/widgets/shell.py0000664000000000000000000011541712626055324016674 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/dicteditor.py0000664000000000000000000016012412626055324017712 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/dicteditorutils.py0000664000000000000000000002562312626055324020777 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/widgets/onecolumntree.py0000664000000000000000000002052312626055324020435 0ustar rootroot# -*- 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()) spyder-2.3.8/spyderlib/widgets/comboboxes.py0000664000000000000000000002001512626055324017712 0ustar rootroot# -*- 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()) spyder-2.3.8/spyderlib/widgets/colors.py0000664000000000000000000000556312626055324017066 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/findinfiles.py0000664000000000000000000010330112626055324020044 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/dependencies.py0000664000000000000000000001611512626055324020206 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/texteditor.py0000664000000000000000000000733412626055324017756 0ustar rootroot# -*- 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()spyder-2.3.8/spyderlib/widgets/tabs.py0000664000000000000000000003203412626055324016507 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/externalshell/0000755000000000000000000000000012626531443020053 5ustar rootrootspyder-2.3.8/spyderlib/widgets/externalshell/osx_app_site.py0000664000000000000000000001132712566665772023150 0ustar rootroot# # 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() spyder-2.3.8/spyderlib/widgets/externalshell/baseshell.py0000664000000000000000000003160012626055324022370 0ustar rootroot# -*- 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()spyder-2.3.8/spyderlib/widgets/externalshell/sitecustomize.py0000664000000000000000000007443012626055324023345 0ustar rootroot# # 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'] spyder-2.3.8/spyderlib/widgets/externalshell/namespacebrowser.py0000664000000000000000000006166212626055324024001 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/externalshell/monitor.py0000664000000000000000000005544312626055324022130 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/externalshell/introspection.py0000664000000000000000000001675612626055324023345 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/widgets/externalshell/inputhooks.py0000664000000000000000000001221712626055324022634 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/widgets/externalshell/__init__.py0000664000000000000000000000047412566665772022213 0ustar rootroot# -*- 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 """ spyder-2.3.8/spyderlib/widgets/externalshell/start_ipython_kernel.py0000664000000000000000000001566512626055324024712 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/externalshell/systemshell.py0000664000000000000000000001326612626055324023012 0ustar rootroot# -*- 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') spyder-2.3.8/spyderlib/widgets/externalshell/pythonshell.py0000664000000000000000000006762612626055324023020 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/objecteditor.py0000664000000000000000000001317312626055324020236 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/formlayout.py0000664000000000000000000005215612626055324017766 0ustar rootroot# -*- 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")) spyder-2.3.8/spyderlib/widgets/sourcecode/0000755000000000000000000000000012626531443017334 5ustar rootrootspyder-2.3.8/spyderlib/widgets/sourcecode/base.py0000664000000000000000000014544512626055324020636 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/sourcecode/codeeditor.py0000664000000000000000000034610112626055324022035 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/sourcecode/syntaxhighlighters.py0000664000000000000000000017775612626055324023666 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/sourcecode/__init__.py0000664000000000000000000000046512566665772021474 0ustar rootroot# -*- 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 """ spyder-2.3.8/spyderlib/widgets/sourcecode/terminal.py0000664000000000000000000001033212566665772021542 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/widgets/ipython.py0000664000000000000000000007466212626055324017265 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/pathmanager.py0000664000000000000000000002421012626055324020042 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/calltip.py0000664000000000000000000002625112606574046017217 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/__init__.py0000664000000000000000000000056712626055324017323 0ustar rootroot# -*- 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) """ spyder-2.3.8/spyderlib/widgets/findreplace.py0000664000000000000000000004062012626055324020032 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/importwizard.py0000664000000000000000000006013312626055324020312 0ustar rootroot# -*- 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")) spyder-2.3.8/spyderlib/widgets/editor.py0000664000000000000000000031105612626055324017050 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/status.py0000664000000000000000000001516312626055324017105 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/widgets/mixins.py0000664000000000000000000006276612626055324017104 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/widgets/arrayeditor.py0000664000000000000000000007420312626055324020107 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/dependencies.py0000664000000000000000000000612012626055322016531 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/rope_patch.py0000664000000000000000000002273212626055324016240 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/plugins/0000755000000000000000000000000012626531443015214 5ustar rootrootspyder-2.3.8/spyderlib/plugins/onlinehelp.py0000664000000000000000000000626712626055324017737 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/plugins/runconfig.py0000664000000000000000000005157312626055324017574 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/plugins/projectexplorer.py0000664000000000000000000001417312626055324021024 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/plugins/inspector.py0000664000000000000000000011731412626055324017604 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/plugins/explorer.py0000664000000000000000000001132112626055324017425 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/plugins/variableexplorer.py0000664000000000000000000001732312626055324021143 0ustar rootroot# -*- 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)spyder-2.3.8/spyderlib/plugins/outlineexplorer.py0000664000000000000000000001053712626055324021035 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/plugins/findinfiles.py0000664000000000000000000001417412626055324020070 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/plugins/workingdirectory.py0000664000000000000000000003435712626055324021210 0ustar rootroot# -*- 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()) spyder-2.3.8/spyderlib/plugins/console.py0000664000000000000000000003404512626055324017237 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/plugins/__init__.py0000664000000000000000000003550712626055324017340 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/plugins/shortcuts.py0000664000000000000000000003340112626055324017626 0ustar rootroot# -*- 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()spyder-2.3.8/spyderlib/plugins/configdialog.py0000664000000000000000000010706112626055324020221 0ustar rootroot# -*- 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']) spyder-2.3.8/spyderlib/plugins/history.py0000664000000000000000000002707012626055324017276 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/plugins/externalconsole.py0000664000000000000000000020034012626055324020773 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/plugins/editor.py0000664000000000000000000033636312626055324017073 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/plugins/ipythonconsole.py0000664000000000000000000015367712626055324020667 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/py3compat.py0000664000000000000000000001467112626055324016036 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/requirements.py0000664000000000000000000000360412626055324016634 0ustar rootroot# -*- 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'])) spyder-2.3.8/spyderlib/pyplot.py0000664000000000000000000000027612626055324015442 0ustar rootroot# -*- coding:utf-8 -*- """ Importing guiqwt's pyplot module or matplotlib's pyplot """ try: from guiqwt.pyplot import * except ImportError: from matplotlib.pyplot import * spyder-2.3.8/spyderlib/pygments_patch.py0000664000000000000000000000640412626055324017137 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/__init__.py0000664000000000000000000000622112626055756015657 0ustar rootroot# -*- 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' } spyder-2.3.8/spyderlib/cli_options.py0000664000000000000000000000425112566665770016451 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/config.py0000664000000000000000000007470212626055322015363 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/guiconfig.py0000664000000000000000000001262712626055322016066 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/userconfig.py0000664000000000000000000004304412626055324016257 0ustar rootroot#!/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() spyder-2.3.8/spyderlib/images/0000755000000000000000000000000012626531443015000 5ustar rootrootspyder-2.3.8/spyderlib/images/inspector.png0000664000000000000000000000222112626055324017512 0ustar rootrootPNG  IHDRw=XIDATHOh\UϽwd&ɤi+5Uj"nDAb®Ԃ.XPIAQ" VPhmkiCLf2d&e{u!5ծ끻{#RJJQbT!@(]Q46}Ht@֧HJ " jx 9p폲ٞb?yc(|`+tQ+@p}ߞ)+] -wSUro=gv+G&ށ0 @q&>>s__=nѕB{c2`UC_<+6ߨ!8 40;fhkejø{8:.ta1CG^;0}j#Ot=,hʲ_FYqtgSFn`? [Udz?UI4+ oõ 0ZN(^}9$bxæ- }΃]Jt.sr ?dj"XB2鬢;c1<4I" }Y. B*Я7YJHJEQ8ne4%4 `!ԠnDIDµ6C “^E+g}.TK0*Uj HP)3m!p=v9F΍1%[vcpz:mXE4 @C(A% BٰlX(_qܰ*bNe_|u~dT,C=X,F-4BhX AE?l|pzFN%9zR;ȎL} mZKKUk+8ǎmW "*$h! $9Pm;)I*9/{׋Ϙ'fOx K)K0- ֒bD3.$ D( $TFE#e+vDss%kGd}q5o Fv]Rmsܤ0#jL 4MsSD,ZEJje)%|RPD-SJ50 ׺Q䆺}2oy;EsIENDB`spyder-2.3.8/spyderlib/images/advanced.png0000664000000000000000000000145612626055322017260 0ustar rootrootPNG  IHDR(-SPLTESSS444jjjvvv{{{rrrLLLGGG䎏hhhڣܓ:::AABOOPeff==>555111```SSSIII}}}vwwkkkTTTPPPbbb```ٷѮ潾⳴IKtRNSJC ohߑJP\Cv~omf䕍angft7IDATJ&Ar 'q*\+@ AqEšAAQ[SUp_RC.ҨYټL=yͿ\gWlb OSK2|?mzbu8{ۓ5ԋU>y>rxf`GFh9|S[6f.SJ?R}ռ(q  OJh#dJ %Wǹ#"y_:GD2*ѪUl~v^J2ŖxIENDB`spyder-2.3.8/spyderlib/images/pythonxy.png0000664000000000000000000000251112626055324017410 0ustar rootrootPNG  IHDR szzIDATXWmLSW8˲qXtdHY"Y$Kf#f*GC Y[o(e㫽} 87lys}yyHiq՝*\SSsj?E yMV_ڿ B#*$3$P'-`0>C[4%mEzd7ozWiArA[[[D *h-hs _u/hfZ!AR9 ~߻† -¡@Qq8ol:Kd5n_Tla,\\|um+po$Ƣm 7*F gwwӝ'%)w3)ObE8׮t\߆/D .BSG!s_P+yn% Eg-C/6^p݀4-ܚWrխ6A|]L}02ŅbH c0j"07 տ51NYJ ?'X11P\0::J4Y}Y`м/c hX[!j^zC>:dѱ><ưz1;:$DL J(C`αIENDB`spyder-2.3.8/spyderlib/images/win_env.png0000664000000000000000000000177212626055324017163 0ustar rootrootPNG  IHDRש1PLTEW_w}&E53[s0ӈ왜B̐2=&g,|и|$ZM^|t`{.LJRt9^3qp+̐~؉latΒ}{yLuZWzOWs9up8zw} ã&ϱCвD۶ Ҵ4c+8ǝOFc5>*xavbiegem?MQUֲ軺1P=A|w^&&&&&R'T8NtNJAi CauU-_6iY|n^td:OaYw.?vvNhΦj?U櫌ڰ "ɡ&ytRNSUoO <92i-V=V_޷PQM˯ٷrŸD  'Q;]  c@ 5 IDAT(=J@矝͘ V#IJd46V X7+ckE>&43W#5#a]3xug1U ,%1@2WrmCd<#?I<)Hy 0471DM+k릭\(obU[M{&IR$A${I+jxeRl.VY3Aif,IENDB`spyder-2.3.8/spyderlib/images/chevron-left.png0000664000000000000000000000032112566665770020116 0ustar rootrootPNG  IHDR w&IDATx}1qoed{5q D@չ:&C8nfZTy7y3Q/IT G\ F C,18æ)N#Ŷ/(5|dbWk&2TaC b2p0ZpIENDB`spyder-2.3.8/spyderlib/images/editor/0000755000000000000000000000000012626531443016266 5ustar rootrootspyder-2.3.8/spyderlib/images/editor/next_cursor.png0000664000000000000000000000156412626055322021354 0ustar rootrootPNG  IHDRשPLTEfffadgcdf,w0%k4>E V Y Y Y T&o<wI V \'j$jb;f?|jDp4zmkmm]\\\]   }||W ] ] ^'k ?5#+tRNSqq:}rSwMRe ֲ.IDAT(uJ@KJ7,\UQ^ܸDbE%J i\$10ϜfN@©<hBUr)a_afА5ڝnݧ禟aVhvIq ,`@Vj`rUQAol9P:rh_*'y|3]B([.&a'#R}-ǫ}BqJ-P/@8WIENDB`spyder-2.3.8/spyderlib/images/editor/run_cell.png0000664000000000000000000000160012626055322020573 0ustar rootrootPNG  IHDRPLTESSSDDDBBB<UDDOfJJ [ NFFeQ::m ^ aaab$v&___n pi]]] }jZZZ  WWWs WWWvwVVVQQQLLL---VVVQQQRRRJJJcctttRNS5{ !i -Rk:GED=F IDATU1JA矬1?h?G}IENDB`spyder-2.3.8/spyderlib/images/editor/refactor.png0000664000000000000000000000047612626055322020607 0ustar rootrootPNG  IHDR aPLTECC::KK@@ cc%%PP\\uuGG## rr33!!Y. tRNSeͩ.`IDATA A/%xI\ѱTIdZ|Z[PY֘[}Wo 8^6l` t椐c\HgP2 $ %[_IENDB`spyder-2.3.8/spyderlib/images/editor/filelist.png0000664000000000000000000000075212626055322020612 0ustar rootrootPNG  IHDR(-S PLTE~zwtrp~n|p~rsy|~~ҶͯȨģ÷Ϳ蓸ڧ؆ӫ宴跓ڗ㭆ӎ肇jkڦ=,"tRNSŭ^lIDATMP.D &LЭIu#.Rv(@s1c P]VrBy]nBza 6f;+B(័)6hILy̒,/3X+ B, 5,ɏ-8%UҊnxi,IENDB`spyder-2.3.8/spyderlib/images/editor/breakpoint_cond_small.png0000664000000000000000000000134512626055322023327 0ustar rootrootPNG  IHDR(PLTEપڏ>>GGyyԍ㶶ԁAAZZڑuueejjeegghhccAAсcc\\\\֓XXNN>>yyDD((GG33 EEGG**EEyyɓ88ƛ‚ \\ڶtt$$͈ AAcc==PP@@SSRRDD@@II??VVՖ漼ݢ__GGPP>>OOদrr..<<8877׏RR22HH00""mmݪ ,,DD ެͯ%%((22''JJ %%ӓԔ&& $$ 44--"",,CC""((!!GGQBtRNS*~cqqccq*+UɫIDATM Aa=|[! Pdy~R,ghnduY+RfPMK.vwx-I喝>m<=:HѲ8lp)~7iy7߀ލ-@u%K%Яq!jIENDB`spyder-2.3.8/spyderlib/images/editor/function.png0000664000000000000000000000052712626055322020624 0ustar rootrootPNG  IHDR aPLTEqu*GMQQQB%S]Qc jW„'qvؚ5Z4m۶׶tDK5V==V7rG+-{d";Jp]_] <`8L w,EV$*L#0xu(W  RwL&pQbU{w,9s, LfќSN^̌QR;ƈx&C ޾Q, V1ZVhA6Mb8%ޝ%^ESKһEh` BŲ1`2k~:H?,-ĨulK~JTy[VYIa %W?:`, atURXQN}iEIu ZT@8# { D `}fKks7*Il|ADسi^l\ {We >9"r :ԁ;]y獕 +;*9χ4.DT!KkaTr*;RyGkCS\M|}BrqW2 ne4xEufs.;Nf!˹u MQ.'A~0د|i՟/z?ypb3s20^]ڲa>9-6ǯm:ο{w4Α[":ŕۡq͋LJu6} >|^pkHQ'KSw[nUړ/, < ܸVubj_UølK͂Xo}-~ϻ yߐG(|?xnL|w; cVtRNS5{ !i -Rk:GED=F IDATU=JCA;s&"7`;l-,=XYf܁(8s&yI XA,S9fd33 I'hG4w)&~eژ+Nul ƲNlm,ΝlBy, ԺR|5guSuHe33#2j EIaBa.K `@m< mD|(3>ۗĒ>;%7IENDB`spyder-2.3.8/spyderlib/images/editor/last_edit_location.png0000664000000000000000000000152112626055322022632 0ustar rootrootPNG  IHDRשPLTEfffcdfadg0,w4%k<&o T Y Y Y VE>IwV  \b$j'jf;j?|pDm4z2f]kmm ]\\\]  ||} 'k ^ ] ]W +tRNSqqrל}:wSMReֵ ֲOBIDAT(m.Cq߷AZV QcXhDbeIA.ArR ih3d.$ d9IB.C80fBRCJK^ &Y'E0]^MDHf͆)rԿdpDےּN 2}uavҊ!ޭ x A 4&Sgcf+ѰWkn*;׽J*UwZ97RNIENDB`spyder-2.3.8/spyderlib/images/editor/method.png0000664000000000000000000000056312626055322020257 0ustar rootrootPNG  IHDR aPLTEwdwdsao\hUR@D4C39*D2wdvq黝Š```趕ϋbP䎃{zspzt\I|mfaog}{}qswmtvyqbRz҇z€^MqG50DtRNSYYh n!/hIDATM; PEs{AV%)\" rUlៀL $ i $gXեKExt׶;sZ:DzJ2f6zdLd @"A IENDB`spyder-2.3.8/spyderlib/images/editor/wng_list.png0000664000000000000000000000134212626055322020621 0ustar rootrootPNG  IHDRשPLTE!ߙD,+'}gvuhSdbS ZKˬMŋבWʚÎEV׃mαNuT5ʛċ0Zpid !ҎܞӅӃԇTRPNLJHFC6UktRNSbx_ ދMqgIDAT(ScdI0"`GxYAt0~C;P(㩿Hϥ@:>=I>"=A W[@02f Ȩ;ȧ>%,yo]k?$;%Vq{sCecd|'Dq>[;;!A8JDl@ Z>vIENDB`spyder-2.3.8/spyderlib/images/editor/outline_explorer.png0000664000000000000000000000131612626055322022373 0ustar rootrootPNG  IHDRשPLTErm}m|n}fwctbr]o\n`qeuj{m|{t|u|uu~xudsapbo_o[l~[k}[k~\l~]m^n_o`petrڽѶ̳ʩæưȷͻbNdGJ'H&0*K::_Dz;sI}0ldz[[?d?D}Nc3Kٶ(tRNSy~Y@IDAT(ύjAD_%H[.~!$ET$zquRiL5 HN4Q5&Ω]= 5`:o`iS*ýFR6L<ʺimU#i ߙrfQ1l?edďWM޳u895/[=8W.MR%(n:&|\^\IENDB`spyder-2.3.8/spyderlib/images/editor/breakpoint_cond_big.png0000664000000000000000000000237212626055322022761 0ustar rootrootPNG  IHDRĴl;IDAT8˅UkLg ?\4QES%)!x5xMc b-w X8Lڹ! su]c7yy=L6Htюi4,87o:rr|R_{ǯɆ?|y}Yn%dLH Kr2;FVpn_ıg~^RHWww1kג=1U*zy?OoΝ ?T΍ q,ǩxHtnoȠoÇiD.NLsrK}~uz7ò/QJ X_mP*ǩ-@V \8AӋf2EEQٶ~ٻLl%@ڇA+Ϯ]]{ ;pi&rn@(93cc4g"̙T:gȒ8@0 Gv#n\,Wuj%K$2ԓ&Q_EEP'.X=y2d *=cN5U\I"LlCЁ|,lyٔ)t[>r)k {@hB9iͣJE6S0Hڌf_B k Q׾{75B'=tXG C7m}Z&Puu8e R<&nV*] P04Z`z&TBqCVYB*:6Y,Л\*7?k,hA4!P?,(j6υ4a# ↞Q|~ּ/7.iÂV 1<Ǿ6.\:&f9Rz&>c CherC2f#2u+)7BlOl| ?&t.2^%mcp-.##Bx;u8<*HD|lu+AZytyȧ\Tt"Iy y}74+W>`͵vDuVP`1R6cp6xIENDB`spyder-2.3.8/spyderlib/images/editor/close_panel.png0000664000000000000000000000201212626055322021252 0ustar rootrootPNG  IHDRשPLTEttuuOOOOONML3/0-;6A:=86/3,5140/+++qAAPP.+43.*-*-*-*-*,*,)/,#% #" &#:98,).,rm|j{iyfweucs`q]o\n_pbrctn}m|m}x{t{u||uux~retap`p`o_o^n^m]m\l~[k~[k}[l~_obodsuտһϷͳʰȬƩæůݶb33400-<7A;>9604,6251/,+,pABi,, >6RIUNNFKFMIROXT\YnMLx,,c  #,&0)5-933/YV.,#% "" &#DBctRNS~yTIDATu=NQ@{PgI k$P L4b1h`9⾠QBYG]m+wЍ8`@&r\بheȌcAG#Ȥ:ѪDVEs*>jZ#SʏŽL 3;l-Ė;ufcVzt9i w&`/IENDB`spyder-2.3.8/spyderlib/images/editor/outline_explorer_vis.png0000664000000000000000000000173512626055322023261 0ustar rootrootPNG  IHDRשCPLTErm|j{iyfweucs`q]o\n_pbrctn}m|m}retap`p`o_o^n^m]m\l~[k~[k}[l~_obodsuɷ;οꁊۗވS6R2D4;RO{׮Bx7qj;wUqP㢉sGc6̾˾IJ͚ðۭͪ*98tRNS~y_UIDATJB*)a9BZZ"h)(BQGG84<6Y^wVi\+wn -M̭1P"W%lI4bO-%$e c#Hv"g-I2}w',rA nշ}`E)[ t![ :$6j&cDyw_(Ɖ&;8OB=rcs.!!])7+:4/?6!)IENDB`spyder-2.3.8/spyderlib/images/editor/newwindow.png0000664000000000000000000000175612626055322021025 0ustar rootrootPNG  IHDRשPLTErm|j{iyfweucs`q]o\n_pbrctn}m|m}x{t{u||Kk\u,O/KZ!uGzSx~retap`p`o_o^n^m]m\l~[k~[k}[l~_o#O,\'bodsu2H?%U# S*q#NտһϷͳʰȬƩæůݶkpY|YRuR_|anr=iA܋ߕݒ=dA=kAmf~v~;e@=nAvn{rwSI`Vg]-Z/_S  K@]T^UbYwkGlJ6d86f89g:.c0)'>/&o2/j8.h69h<6b= #C3:pB#*L<,($PA=tRNS~ᴼyy4IDATJ`y&c+y9utڵRGAj 4ִDi+"^}pFg33~!1E+*D9k`tpLpEK 8i&jdYN:/4TXEAT s—mpΩLsmp'Ww\[BGj i,]uO@YOJn}"٩,IENDB`spyder-2.3.8/spyderlib/images/editor/horsplit.png0000664000000000000000000000213412626055322020637 0ustar rootrootPNG  IHDRשPLTErm|j{iyfweucs`q]o\n_pbrctn}m|m}XuhHeL:a?>mAXdretap`p`o_o^n^m]m\l~[k~[k}[l~_o*S7 ^/bodsu2H?%U# S*q#Nyɷ;ο𤫸ŵŷȹkpY|YRuR_|anr˽=iA܋ߕݒ=dA̻=kAmf~v~;e@˻=nAvn{rwSI`Vg]-Z/_S  K@]T^UbYwkGlJ6d86f89g:.c0)'>/&o2/j8.h69h<̪6b= #C3:pB#*L<,($PAqBtRNS~y梞(IDAT=JCA=s$d Bc%V p+H/{p+-D&ϣ@鼎iGTbÍی =s_Kf )) + `(!M%iDʭ51*-^F9Q-OPo [us{8\d\yYl3n~fqE0iĚcps6r RZOIENDB`spyder-2.3.8/spyderlib/images/editor/prev_cursor.png0000664000000000000000000000156112626055322021347 0ustar rootrootPNG  IHDRשPLTEfffcdfadg0,w4%k<&o T Y Y Y VE>IwV  \b$j'jf;j?|pDm4zkmm ]\\\]  ||} 'k ^ ] ]W w+tRNSqqrל}:wSMReֵ ֲOBIDAT(SuнJAYc2 66!y3AME Xm,|!cQP $u-ǝ<JyYM 3binaXsHiK0mMÎcg~vM4„ƚEWF w~<ȠSJ'E޼,K|W&IҭҎDb5|\o܀j$]<ӔP0e5 7$[w/4k\IENDB`spyder-2.3.8/spyderlib/images/editor/todo_list.png0000664000000000000000000000135712626055322021001 0ustar rootrootPNG  IHDRשPLTENMdTLcTTaP`@\>}6[ 6o;=X6Kָ>*}fi7*_xMe7,nIENDB`spyder-2.3.8/spyderlib/images/editor/breakpoint_small.png0000664000000000000000000000070312626055322022321 0ustar rootrootPNG  IHDR E5NPLTEiiee88C55QQiiee88eeiimUUQQt//t^^FF22;;BB22[[HHooff``eennHH__RR\\DDQQ11aaBBCCDDVV0099AA5577.. 00==33,,0055++,,//((--11..))**))44&&##$$~$$l$?tRNSgg&ggggg gIDATMA@{i3R{}[i2kzT}<W;_"m?El> [? :4qIENDB`spyder-2.3.8/spyderlib/images/editor/breakpoint_big.png0000664000000000000000000000233512626055322021755 0ustar rootrootPNG  IHDRĴl;IDAT8OiLTW_ۘ/%nYa@0-&.T\ *jc?T 18V:mma @ڲBiAIp?FCM3{wEܣ-%EC.ﱱY:-)itgm6-5%&xkgZ5Gf?kex_p;VCyAoffA˩Sd v<[}TP=lӧ7r(H@ΔM&0ޗKJ"GJ Mj}.sp.}C18-DGSsTof29|199~ccߔ3gX8joFMϜ1 j`BG_8H7&dރ5bX Yg\uA;w#yEqfx6|={\/nH-bſmUԶeGC-. f1S>EK>L۶Q,"^,7l~SH=B~~[jb VRɓ^ը1<9t6 = 5kTGܟ?rͣsޜ9tol "CHgTT"#/ #2bhժUߟ>H^Tajg 2gT?jeDs`^2\IFl[A3P!2.išv|e#jg@,6 hFrAA t 3. %K[3bq%KF\eSLiўkFT ,갾d Wv@gN[Bao &…Ԁpk7=H7H-_>YjA d|dB1`vL* rqV&,Y{P?6XKRSif Y[m^Ѹܳ` #;+CK~raU/dEt mJ5븮T:PNsص/q2CfJ&ŋmEyyN4mܽߧ8 ؙ*oݢŠ ocegoɿv{j6edx]߽+uZa^Uk2}#^Wz; Gpb<IENDB`spyder-2.3.8/spyderlib/images/editor/select.png0000664000000000000000000000046112626055322020253 0ustar rootrootPNG  IHDR(-SiPLTE?????????????????????~~~uuunnn??????/ax??????$.~}~~tuunnoAAA?????????@@@l-!tRNS\TPת[xVIDATcd@xABQ# F|1EC?B,dH2: ` /^IENDB`spyder-2.3.8/spyderlib/images/editor/uncomment.png0000664000000000000000000000136712626055322021007 0ustar rootrootPNG  IHDR DPLTE??LLL``챜ڴ·췪4a4YY__VVKKEEKKBB6g6>>;t;4]4-N--4?tRNS 3DHI264"19;<8) ,N!#.5& O-%0*/$E(CGW +?]VIDAT8V@q0P! 1RJ+2Vv?H .fq)(^A B JbIpRNAIB<+OLv-d#_5E=M$#%@2#[:veQr8h|.1vf) }h-!=vAiOz:fdyyRN8W#YIENDB`spyder-2.3.8/spyderlib/images/editor/private2.png0000664000000000000000000000056012626055322020530 0ustar rootrootPNG  IHDR aPLTE~}z{vvhhaaLLL`_LLLZZ]^LLLLLLΪ```ݞrq󣣹uu~~lmhissꗖΌ΃||vuߙΐ͑qq獍_^PZJtRNSYYh n!/hIDATM; PEs{Av6V))\@jD PJ2@x7XY]n*R.t*ɘE81y"зm/yx#@'ЇIENDB`spyder-2.3.8/spyderlib/images/editor/run_cell_advance.png0000664000000000000000000000155712626055322022267 0ustar rootrootPNG  IHDRPLTESSSDDDBBB<UDDOfJJ [ NFFeQ::m ^ b$v&n pi }j  s vw---VVVQQQRRRJJJcctt9P0&E"3s!@ƩddL ]M{kv8ѲI"%(ʏjlPUU\=U_Q٦ΨJ(/g1Fac 3 (Dm~rbG<6j|-\L!_<މymX&k64[j cLPcyRݗt/sGųˍkS77g;?amo̙nsiuG0fťlcЯq+5!yf*΂`GS3/Oe2x1bCs7KS G(3k~ 3(ًh7M#LX;B47?mٙ2BC9#o2FڮmZ|oW6skf483cSaAtWX\p}J)r|`= Ύ-OLX<13p$~OGR@i j"0jGnWРq=K÷fN0;:r ds5ra-04[Zi0\ rE!M(%UE#ײܯx.xz뫌޿4VtNoDs8&Hrl``PO(!anS'7?_W>9ڍaT Ϋj~)TTpl`TH^HD+ #Jf#'ߨ#лgF\0lp[Ux9kxƜ$\3m+փzBh҄G['gUw/,MJ@p 2F(S65NJ5KMx2ArSM=5lCr4ˮXbkkB E!vXÈ(t\3zvn" Ԉ;Wuo6đ J`[>x渎Q^7>m7t@-nh@ٝC-x:\Gdu Dk \t\4_ƭn!eEPQ^lN/0KJ$e ]b*G|;]M+UiO=IncZo/qY2٩ "p7c4_3'=>8t%o>ey64+MZ|TC!?ӓ+PN -aĘIENDB`spyder-2.3.8/spyderlib/images/editor/comment.png0000664000000000000000000000143012626055322020433 0ustar rootrootPNG  IHDR D&PLTE//``LLL眡衴窷EEKKVV__YY4a46g6BBKK>>4]4;t;-N- kFtRNS 2DHI3)8<;91"64, &5.#E iT0%-ma$/*l(e`WGCMJ!+|V[IDAT8k[0&CBB#Q`9;Ɋ"kz>b7 ezp<a# K1(I8Cp}? 7.$SKvҙl.O<ۻC(3+* ڪOE.%uX/$' _ޭQP#w ϡY򶨢 b}ݦ3A"4]a ["zA.x|t [+IٷMS1jȘ<;|$v11VSFWkuL)zݸ&ф c'u$^0]Ibesyi@_@ ;IENDB`spyder-2.3.8/spyderlib/images/editor/class.png0000664000000000000000000000050312626055322020076 0ustar rootrootPNG  IHDR a~PLTEffb~]yVq?Z3K1J)@0KfzuPl镨Ie⊟syRlޑMgu3O|tRNSYYh n!/eIDAT[M Cr'tSwҥ@xyC [#b1OfNPf (ř %cL#! KHUϜOhP~|o_vIENDB`spyder-2.3.8/spyderlib/images/editor/convention.png0000664000000000000000000000051412626055322021155 0ustar rootrootPNG  IHDR aPLTE=======? Dگ7g@ǓحNZāHQ: .sՉ\6C۔d痼ĩ 8{`rESjaٵT*G<wjSn?8yE@s| :7*0COR,X+:GcF }g5!Pv,<T5fmKYsh&>5}{7ҖHZYv٦XH PV3BYԳѼQ ͹G™U)frNSG+Yٱ0لq CQA,{.z'~+&Ҷ4LɺAAš)do{!dʈmT_B^zI~R:QvzeGD:,"lT/"j(4 @ثzp7_1R^IENDB`spyder-2.3.8/spyderlib/images/editor/unindent.png0000664000000000000000000000055612626055322020625 0ustar rootrootPNG  IHDRj PLTELDeLDeLDe'n8z'nK9zc ZT&nٱ-(tRNSa#IDATWm0Eь6(hE)*J\i~<>0F#FI}sj7Fșų yX =B,W5층ސNbIENDB`spyder-2.3.8/spyderlib/images/editor/run_settings.png0000664000000000000000000000233112626055322021516 0ustar rootrootPNG  IHDR]' PLTEꁃ~===ŜVVT͍(((bb]^^\NNNLLJWYWfgdggf||yDED>BBVWU !  "{"$m$LMKyf?@>./.ޗFGF]][~|3tRNS n*s #E4a F_ !EqmCf3,iIwV  \b$j'jf;j?|pDm4zŋבWʚÎEV׃mNuT5ʛċ0Zpid !ҎܞӅӃԇTRPNLJHFC ]\\\]  ||} 'k ^ ] ]W "V)EtRNS*˾_M ދ| Mvqqrל}:wSMReֵ ֲ{\_IDAT(caX،I0:*lSgt >F1t &ᷨ"l@wyF PsT VFAFq-: U uBL-R#V `j[ $t@`bD09 ($q9, g``8Ŝ85#ITgMgde`8CLL& \%x" 7IENDB`spyder-2.3.8/spyderlib/images/editor/private1.png0000664000000000000000000000056312626055322020532 0ustar rootrootPNG  IHDR aPLTExxvrn҃_xXBBByVBBBpQyTBBBBBBx˺ﻢʹ```ݯj򸝸{t{ފdߝ~qfo魐͘͞ߠyߡ}ܑmެٌ̡͡i椆}V;mftRNSYYh n!/hIDATM; PEs{AV%)\" rUlៀL $ i $gXեKExt׶;sZ:DzJ2f6zdLd @"A IENDB`spyder-2.3.8/spyderlib/images/editor/next_wng.png0000664000000000000000000000155412626055322020631 0ustar rootrootPNG  IHDRשPLTE8(::!*,+0'/}$gvއ%uhxSdbS ZK43,w0%k4>E V Y Y Y T&o<wI V \'j$jb;f?|jDp4zmŋבWʚÎEV׃mNuT5ʛċ0Zpid !ҎܞӅӃԇTRPNLJHFC]\\\]   }||W ] ] ^'k EtRNS*˾_M ދ| Mvqq:}rSwMRe ֲIDAT(c` 021#8L& 3v fG&&N,\,LLN̼psa >FF 8f0 {8#[TDXXYY=`vg($&*&τj;# y ya Q.A>@ ./##TvE3RL@Ap23r3Bs HpF)K2ؤ12<`23<bM N8  :~ IENDB`spyder-2.3.8/spyderlib/images/editor/versplit.png0000664000000000000000000000173412626055322020650 0ustar rootrootPNG  IHDRשPLTErm|j{iyfweucs`q]o\n_pbrctn}m|m}x{t{u||uHeM;a@>mBuXdx~retap`p`o_o^n^m]m\l~[k~[k}[l~_o,T:!^1bodsu2H?%U# S*q#NտһϷͳʰȬƩæůݶkpY|YRuR_|anr¾½=iA܋ߕݒ=dA=kAmf~v~;e@=nAvn{rwSI`Vg]-Z/_S  K@]T^UbYwkGlJ6d86f89g:.c0)'>/&o2/j8.h69h<̾Ȼŵ®ȷʻ6b= #C3:pB#*L<,($PA{>YP77 gRPTpRHI>6$!s`Y}@'cV  ;d 1^#0fMV1P aڬT6B%I&YI;%_10uT,dXT~3W%ހ'`VP_])zu͕Dֲ9u巒WQet ?Ḃ')^ X&/e0]Z$h 03~\eJ>{ѶB)N9 6w L}ק,rT䶬<N:T/ߊ@:<@q\~L!EIENDB`spyder-2.3.8/spyderlib/images/arredit.png0000664000000000000000000000147212626055322017143 0ustar rootrootPNG  IHDR DgAMA7nPLTE~{|z{zzyyywxxuwwtvvsuurttq}}|ssrookmmillhkkgjjfiieiifmmkxxxooliighhehhfhhdggcdd`eeaYYTXXSVVQXXT\\[YYmm4eOKtRNSk%&($i}j IDAT8O͓J1EM wAy+>Dҍ- FhΏIjKgֳ$/D;©KmlKPS %ͳ u!u 9|Z_7gOza 2v7k)XB;)tNB1`%$U8}]HB_o:N\0=YT -DmDE<[viiUhK9VQTXPI'?-/*s=C)(w#咊Oj#c52.G9oB MIENDB`spyder-2.3.8/spyderlib/images/whole_words.png0000664000000000000000000000021212626055324020036 0ustar rootrootPNG  IHDRRPLTEj#a{B#atRNSUT1'IDATc`  +P0Pp) ]0qqg+ IENDB`spyder-2.3.8/spyderlib/images/arrow.png0000664000000000000000000000034512626055322016641 0ustar rootrootPNG  IHDRa~eBPLTEUUEE jUUNNEE99yy--ddVV!!HH~FFwtRNS!tJIDAT@0v{SUY4 ~-#V"'B-jHF-J!4&Bk7;4ꜗIENDB`spyder-2.3.8/spyderlib/images/scipy.png0000664000000000000000000000155212626055324016641 0ustar rootrootPNG  IHDR(-S4PLTE!7334 646LBXkZnEZn4J %<7-Ha#>X68!Cb5R6 0Q#@5"? ?n7&G=b!@+Q%G1\(M5d8k <*OJ9m2_=LZNAz'KJC~8kHPE2[+&I#C [nj{j|Xs[v`zxf[v[uSmBhBivBjBi?e'Y(\2fDrV*_r(^([!P{ Dw=n(7q:;v0a'<pB^Z5 zIDATE1  lIQJR )LF Q"jYe`e(J(d0$! K"!y$%ߌpL!@#BeCt<]8D@{aA%銟{5RVBRRXJ1s}]#BtIENDB`spyder-2.3.8/spyderlib/images/pythonpath_mgr.png0000664000000000000000000000236112626055324020554 0ustar rootrootPNG  IHDRw=IDATHǵ]l33]{lCG`(7D ~BPTrEzEP+JAABHH.?U%_D%Xv:&z;3|_/VvT8W9yGSA6 rƐ^gǍg4 ?!)滻Fٰ/̀Bwx{Oxk>ӵ5{{Bd{1VS .G߭ql*pı@nğ+7Ք喐 CCdo3V3]'-.8XT@ UD+jbn)pNq "J uWy6џbadtcϽRJ +ڋumH^_ =}`nim1S JjթɅdr!'O1(VX86Oj^7F/iΛC9ik DA2~vW ~cH}UvD *-X Mmđ$%j9(oXMj}/5zCdԒ3<4@q@>CV[RsDX[n~68?䟾bm'B+D|C%Tj)zm:536WەI4uBbU8QRZlئ󽽥A*oTފ vFQ-pzfOrrݡlA~|Yo Mq(%L<e X箱(?>Y xё+ғ\)6\ xᥗ=t9)6s(>yoarcOd=z/6sK= ~=- *lp _5}IENDB`spyder-2.3.8/spyderlib/images/qtdesigner.png0000664000000000000000000000326012626055324017655 0ustar rootrootPNG  IHDR szzwIDATXýwPTWטLO*Q M+5Ib"!Jqe(. "% &A X kM3g޻w T_U3vf }2i)zW>_3M/k dSHJI2=}2 fQrI/I97C) 1T#%5=NZH*HrIvLz94΃5a /=zD^(1 5'f"8{<Ŧ< fY=j~ daL\;AR4kpJ_#ݜƋXLlvܑ6[(XFU%:KR wߕWbyg[B3oZOyLzA9"F~ؔ88Q Li8VϷW C75 `@,`'ؑ)4X 񰩑oK,EٜaP<B][5:VR,0n6~<6CmÓEkAH=e09nfb 4~RKLiYꅕ]vA2;~ (v xsS@YC f#氾+`l?:caP_ `!ST#Pfo 3YAXMd/~{rr 5)Mv .|;z@hjnDpbLie@{`p/*ԡ͎h. r|{&E91V9 ?3X_|A4C't n۱᝻H@6vS\_2&A_Wކ5\Q||xo] FCdV>pi[;ܮ(:\Tˇh4G\& B?p򊋘p0ĠpgLAb:l\l(9(51: hPqu-N\$UummpnPJ8˘⺖Ze8+.-j5v8%a\ŷ4h+ujds%nj>Td\[B'@\+-pnOC5B tZ8ŵ?~l({,aɈq*P\SCD4xqqU\\؟>. 1u_ÌLXxJFu\ܤ[MȘ'gwI{$r]H>1e]S|atK.O" +@kks+_Qb0 u V})hU==hrV/Y{y;.[=pT4gG0wxs9ty5Vt: @:qtn?r|,P ]>)p%4ma%7S 87 J<]$KIPM禷jUjb &Pnо| `P.@- ɘ>n*vS4ܺ IДŵwS|$z;G.WDvNQ!67QPꓒS yo~jz=_y] IENDB`spyder-2.3.8/spyderlib/images/vcs_commit.png0000664000000000000000000000104512626055324017652 0ustar rootrootPNG  IHDR(-SgAMA7PLTEWNRJVMIAUL?8TK:~4j_g\SK=79}3j_f\_URJ<68|2i^f[ZQVMQI;57{1eZ:46z1\S:~45y0SK9}35x/IB8|24w.@97{13v.6z12u-mci_lbтz`Wj`ρaX@9~ymh^}|_Vy΀g\|y]TcYvk҄zj`mcxx\SZQmc{vvZRQId[w~tYQG@\TWO=7e!S,tRNSy{CeA?O}ejt47kY=FWj mզg.׵f@`?}7h8IENDB`spyder-2.3.8/spyderlib/images/genprefs.png0000664000000000000000000000242512626055324017323 0ustar rootrootPNG  IHDRw=IDATHU}PuB)bj+."$_3S(-[bԁgGIj(™(' x`m c{}<߾I䀋s|_? t8~(|^yቌ{m$tYV/s4 Y }s9"Y\笹Vf=q#~tӨ`-cl"aVNTQ5 a: Șu z"QRnKDܞ)'5ct-Q a0!H|7x UnBy'˴РЂ ւT5WKFH0"IBp_6-NJ'^V0Mê9עGZܼOQ((QjR0a$QvB4o{،)ZjcT^*jL:.A[Eo͵ HR:mܘGKx.|hǜN((_oЬAPfUV8eۢ1V~A_ܦAS8}^fh{763K%SG[45*ݘ)6P)V 7ɔ5ci$^DߧJu]}B >3: Aw_k==^zߢRjw \oJNi>)&oQ)sʄfWK;zq4Brv)O;e.[v$tsd|[ƽjc-H+v59|5vAjnu8t憁ۤ$H%_̠'gT_/yJ Ay o^d T2YOH)|/;>r c٦$:ӒUR iذvaE {/P>z1sb_E)'R$ ai+Èn<7ZpwkCGWIÜ9^p !3dwy?wEО3ʭsTW՟ɽֿ#z?"nط_jKXXVU0oy BH䱳q *D2<|$&:-\F4knk#"DPj` Zu6 =+dFr*mư 'BS~kC#_C/42IENDB`spyder-2.3.8/spyderlib/images/matplotlib.png0000664000000000000000000000127712626055324017665 0ustar rootrootPNG  IHDR(-SPLTExxP\SYV<<gg&IIEEaakkSSRR GG^^FFIIMM?NN^NN~}}ii jjppphCCcc*qqaaoo RR\\FDD Ԝeܪ ccr$$ͦڂXPp<teEb{:8aaJJMՒʒ|Ec{줺WG}?pQtegtyP夁RArAjTAwwOĶy[xfiqgZcǡkkJUUa՚\\ccXMM|cU""FFiiޒBB{\xxRV||TppKYe |IDATMA7aDF@ t@NtUBbY5z*UtFh `xn1$%6=S_m}b;ZѴ]6U%;Zsnj\]_<{z+xjs`T (.Fќ1IENDB`spyder-2.3.8/spyderlib/images/qtassistant.png0000664000000000000000000000353012626055324020066 0ustar rootrootPNG  IHDR szzIDATX͗}P-:?dywne%bd0h"+oM2[z'DRGmwYR EB"vY`Y`Xoml)PLo8Z8{θ ҤAR@"#o97Y!0]|DSQ6wg6z.zQ,_1,ABWlKvPWwwP p{4iZ~ p\i,DdJȫiFIE6\GV|e ><g0@ @sOxKbq[K_ӄf+4RwAR݄*t|t˨ǁ췱S$g8L:lB&pyg:tk~XXR n\*WD˄U;GaU@$:c#[!{W 1N `5 Yڢ1Iޫ?CUWƮ u_`([#V-ĪQak~^-TSH [Sy䭑rUy*]ɗ+JqJhJ)'1n5 <KoqՐ53"/աAFҋy$fB)faX{r^GE0Hy "NE)RAΘٷ~BFg]Ìbw qW>p7tŰ$Tu4WSmp;z= R) k5\Υu2zs=3^Ѩi=<鉕?G bSJǒ-4ӋRP;sIȌ ݱ/kF”:UޜζT)$LYt.Wf:W8$:g*+>Gg+1`zwZ'=`Í1铫~\}A机6]J%NVRܞO2#w6|C6>5= Zo:M@mq#Vߛ}&Ho0e38X& |Jթ:y'AXR `<~*>K.}xIyxʇu%AH#SG%ɐ]ـRJ* P6CI\R""#>dЖT;ə |T:$*[ Q@, }z}C ?EԷtƓO# O0%w^u`4FPѿHq?ǂ*IENDB`spyder-2.3.8/spyderlib/images/italic.png0000664000000000000000000000020012626055324016744 0ustar rootrootPNG  IHDR7GIDAT(c`|@?80t#s0x3lů@!7f4 ovB> 3g@ i^IENDB`spyder-2.3.8/spyderlib/images/console/0000755000000000000000000000000012626531443016442 5ustar rootrootspyder-2.3.8/spyderlib/images/console/syspath.png0000664000000000000000000000135412626055322020645 0ustar rootrootPNG  IHDR(-SPLTEYXXրOPQ^^]eff~XYY{787|QQRxeabbױu5$t v111NNNwwwpj$4G ~~~ddd*4?(/9꽽󷷷Ϊமߚ᧧☼ދ㌺⩩ㆲކߊ쇳߂n(DtRNS#$m4O+]7GD BAES_iD)[{?/te4C}*>zvPơD8XXBwe,YH]#ǥ Bt6lM>-n[ B#&&{\qÄ aMj; x#\r3'j }81oŪ TЖs5CjO/$s\tthr f,; sn+sOAEyZrbVIENDB`spyder-2.3.8/spyderlib/images/console/ipython_console_t.png0000664000000000000000000000043312626055322022706 0ustar rootrootPNG  IHDR&TIDATӅ!Ka{yLAae5&h`D F@ "$84* 6w?'\.{8u:eC1ڍqA:%gސIH&Mh*.AB m,[nDɖO`ʕ1E砦!?ԝ\rQKͅfɋ{-9NT c+%mgvykFO/lZ>~Ȫ<mAIENDB`spyder-2.3.8/spyderlib/images/console/history24.png0000664000000000000000000000054612626055322021023 0ustar rootrootPNG  IHDRשPLTEbfffٔڲԭϪ̈{wrnkho|֋ܘ ntRNS!azIDAT(ϵ1@PD"$ gppB(0؎i_l2+Έ| w)@O TU ld8icUucF2Fo1-cF[p$8%zIENDB`spyder-2.3.8/spyderlib/images/console/cmdprompt_t.png0000664000000000000000000000077612626055322021511 0ustar rootrootPNG  IHDR(-SPLTExxx|||{{{zzz}}}mctRNSCRcnsuwwusncSDk8IDATJCA@{g筂 1E!--,(0U_⟹'r%vJ8XI^T)wXl5ITMzLvzd?,5={Jq_';7qPz l3(P}=IENDB`spyder-2.3.8/spyderlib/images/console/clear.png0000664000000000000000000000311312626055322020233 0ustar rootrootPNG  IHDR szzIDATX͕}l]u?{}]ºAY蘤[`K"104J@1?aLedoD ZiVtlk^:n]{ι]Dlk|o~y_9O`m2,ۮsۻcM(v~ m)#gç{1b.XLOO4utT|Y \ JX2ᒇ{{ `fծ@ҋGO`*hS}Ck;4,eIBH}kG=ObYYY21913zt%vsr"M|~lܲ` ΓN8cem0 wwiWҍ{|rs |'vpbdʖxcMϬ[L /au]s- uMF:ˁC7_=zS=p|6uxF*ez#osůrBwM-YpF4 f.?L']U^&n@_]!<>!6::Ym%$ 7~Ϻ+{ygN=0cxSw."8dN%APm c{!ysx &/e9l3@|2> (JaDW>u]#?GnycyȩFPXBDV$ג܃mW=}ݽb͉ C'ȒNP^U(rtuZЅuJ mV)5ssdrZd!a*>;g8PZ#hB]u=ph(M4Bc;aKH^&\&`S0U>ĪkJ{O #E8P|A28NE+?KY|%±ǩ6ع0ٵ`(b@vj6բU(Sci8ǵF)m%O|LjhG}Y ߋ@>⯃ A 9ݟ6V7|DKŧn= H]7>B6r.e؛с`1YNΘZ.xݾ"JŬ {&xPIENDB`spyder-2.3.8/spyderlib/images/console/terminated.png0000664000000000000000000000064712626055322021312 0ustar rootrootPNG  IHDRJ~snIDAT8˕AJ@IB EzkAϮ^.IENDB`spyder-2.3.8/spyderlib/images/console/python.png0000664000000000000000000000123312626055322020467 0ustar rootrootPNG  IHDR(-SnPLTEZZZ[[[I}@pMs@yAkDfIf?rAlCgEc}Ec|ɢ?|>t@nBiEd~Ge}a\G=u@oBjDeFd}[OP=v?qBktRG@FjEd}`VK@9aZODe\Q?]HIDA'tRNS% !)*#P; (#PIDATW5ΡJ+SہAs]\ | 0@apMWI0JIËe ><[0܀}D$x}9?YR; :;6·MP82YQT_TK 97#52Ƕ@@:9y|oy{umCU&-f&4t#u ݡS(+زUC'`8ih3GIcJ湉Ђ& !Iz޼INtN'xX@xxmX *䂇ݟ !q{/n>x~ yKHNr.*ڙ_#5_W<Exv'l:Ԋgv/|?|3; d?B&Ic?*I5&זj]f \?e|9;4RrzЁ OI2$"=sor˩}נ{X|@~!WO/g:aϹw {RF YBri#_W;r$ߑ_BY;;A_ :4A5r9hx@>6HhA3yz W c ThZI8{3yTGē༞^c~&Iy=?7ǰ~e*kǐwn]޻cKG=͹x9ZTGz^q2qQZmo´RJI  "dɅIQfFs $^}Z_,hrgAbNu_K(lI"rj4[ϋ'D,ײքeD^-{y 𛔓pyxf}Y/+D?_|5iQi^֛|rb4]ϊ'W'Tab /h9y)/ee|?\2))Yxb2Af$jYawӊJ#8g|3^Vy-OL$h7eŚ -6 V?/ O`Ç hYw**hɤ> -KU)Ztk*`م8hWfݨyy- `ڔ[[:FҪ {H_|Y ]eTup{kpdXm1ČV-{ vwVUwB_!/b|M(87/gy$oJeU0[3'?nJUI&zEYx=i1tP }8>{1-p3|6x= g0?3Y+ଶnX7B3*F>p:\hrX XYx0?M^y('ЍA^'>MyNV) =3p]w=N ŤNJڿsv^+8FW;B5wZZ`y%~݊:.DwID)I>*k|$/ |Iȣh?؟P޲-jḨUDMB}YWmdU^kqș3W(q;$毗q \к/'hI2}}mc~Ѝeȓ%emUC%|KKwrBmppxbP?i2ދ42o؈ hЀexPD{8qhqmNdS^ + ?&h =Vƭ@]p<W`-6/ྗe|1 D&*eJ羏peNϪ=[_.O+T["%[vZc>-T~Yf ;H$g4྿ď4{xKOPNwå%y,gBGZ>WL?&2yQv `]_ӏq oX_I0qSYn>A)S(K­ d|} \Iy4)3?o&J$[B@䒋g#V"=8T4262b!)QM."\$~g5t cɏ#"&WVwnMQc^;JE y#­}Wp+Gqgy~aÝEyp~P+q7.ɏ5m%[ Ey'E. '7ÓI!h_S|=a^ѐg8N1[ZbO+>swtpJ!>1gLo}AƔEғU2h+>-<>F ގZ+EFG8Q;~Ȳ[pAl[7JoVgy?|0i!_ڃv-t)aU~ȲXC a.9|V[pNR/Muc|\ \gr^L;r3&wᖛ';b|}k<8%?O] %ڦaiqrk>OÅMd-+KW>̥1Ciǭ `B1뇱lY㣘G܎a%bL^J>V]@{e+пR1DBJH E0 ulb'p )(ǧpQg$X-,\Wds:7G"SJrTG9.O(sz]-nY QRp֣2򄢎Z\AY Eo I8^(Ǭ-V{JnTBN޿ SR>Tt8kwMuŧ^n[%gq/?HGji~+?B!$F&*Vj9o%,|fJ Ÿ0A) ^( [>;bNXGk-p n(JQ1nEV|J@X'pW"V[QI8k&mD_gkX_y n;࠶zB7Se!l)1u+!bAТ ܁E{"(jP`sl,! dV9Rl7{d|kH;&(dFUg+ݬpyiՂ;]?Oc{#޵mel٢}WWqYuU)DE"Rʺ,xR.k7XIENDB`spyder-2.3.8/spyderlib/images/console/history.png0000664000000000000000000000053312626055322020651 0ustar rootrootPNG  IHDR(-SPLTEbfffٔڲԭϪ̈{wrnkho|֋ܘ ntRNS!aoIDATWe1 PD;ap +f )D,D q,wy Kx+y(E"Q`Q P{a)Uܛpl& ^\T8bibW+$$g0h. $](!IENDB`spyder-2.3.8/spyderlib/images/console/ipython_console.png0000664000000000000000000000075512626055322022372 0ustar rootrootPNG  IHDRlPLTE~ws}FV_AR[@QZ?PY?OY>NX=MWY~5Rr'kD>] |,`JFHc,IVR[c jru%v&p haa\jr<( .+ + * + - . + ( ISS$ ]Tӓ@OKV5-BғCAϓʁ*ֈ&J0,ܗB7#r{B,V{ <0&d9uz+  ~ !)טӌ-$ v!A_hj_B% @MtRNS4( d |)>!ϰT6lL b{q-)(()*+,+489buIDATWcb^>F`!,&` *mDT0= 1;V p`fbB q7v0a(`C& @*䘹9d102Z<̊L,żH(@I yy_IENDB`spyder-2.3.8/spyderlib/images/chevron-right.png0000664000000000000000000000031212566665770020301 0ustar rootrootPNG  IHDR Vu\IDATxҽ @'b%$DH!X \4{hg*G~=x>$2\=3gl@:B.P j4j~ cΕn'Q vd 0oN}eHZ_cr7_QIENDB`spyder-2.3.8/spyderlib/images/filetypes/0000755000000000000000000000000012626531443017004 5ustar rootrootspyder-2.3.8/spyderlib/images/filetypes/cmd.png0000664000000000000000000000100312626055322020246 0ustar rootrootPNG  IHDR(-SPLTEffg333``aߓ???SSS"""234UUU]]]Ƈ'''xyy]]]芊xyzJJK񻼾^_` ijkWWWvwy 9mp9tRNSp/pGg&EOg΀4somlf4'7&)&gIDATM@gv7vw 0ce& My}$AY(!Rꎢ`bqe4DLt>`2Yr`%|v%\S%NQi4z3v\xCƓ Vj fdEIENDB`spyder-2.3.8/spyderlib/images/filetypes/tex.png0000664000000000000000000000065312626055322020315 0ustar rootrootPNG  IHDR(-SPLTEQNN\YYںHEFEABSPQ<9:ZWX|zzܠ򝛜dabԾjgh~~YVWzxyRtRNS#IDATJBQ?Y-ZTPAQᢔx͔!Xj BH75J P޵cPHM'~~d2K:8iMֳ}^*IX=P?I/FKVE_}}~@zՂ7\ϱ26 Űh?PBqIENDB`spyder-2.3.8/spyderlib/images/filetypes/reg.png0000664000000000000000000000062212626055322020266 0ustar rootrootPNG  IHDR(-SPLTEYtRNS@fIDATAJP?P1.t/ŕ  5iByD@/$n*dUhIlG [(LPMŅs>+##-(緊:Z00tizR 庪o:˩|m䶪^zE{ö6i1Y,~qcWv{= B*Jpl(IENDB`spyder-2.3.8/spyderlib/images/filetypes/ps.png0000664000000000000000000000071412626055322020135 0ustar rootrootPNG  IHDR(-SPLTE籲nnn}}}VUVIFFzz{212=:;߹͛OdtRNS#IDAT.DAgDRlTD4F޻!b+)$T=QB׿q M֭h$9T8ME#Ӳ%yŕ!|y'(sÏEo3;<t|'~e*aLzP|%Y~򨪰ٜʴ𦨬@ ftRNS@fIDATW} Ag6_HH0P  ɹE9<3yFd|A.h[:@y `J}pPPUh*Գ5 8޹FWUKs2{ef&eS7KaYAIENDB`spyder-2.3.8/spyderlib/images/filetypes/hxx.png0000664000000000000000000000044512626055322020323 0ustar rootrootPNG  IHDR7IDAT(mѽJANF-g b">EZEا;^1Aekqa;?dW bt:v$Z>V"Rb&i+$?5ob7Vk:,Y3LdF[vo'Sλ//xtkTcϩ\XvOM^{ӵe[P [*)I aKdD2reiuX<*<0IENDB`spyder-2.3.8/spyderlib/images/filetypes/xls.png0000664000000000000000000000071412626055324020323 0ustar rootrootPNG  IHDR(-SPLTE𣣣yyEF__zz *-eI`7wc@@ vn/18~yu;;'+7zataff 1"=Kx_^La,uE(谰tRNS#IDAT=KJ@zN .D<;&VEBbߠ"HT1':o]KubWEEL/FO[A*H*oXr@rxGӣ婵SpwCvH  tYs5IENDB`spyder-2.3.8/spyderlib/images/filetypes/nsh.png0000664000000000000000000000170212626055322020301 0ustar rootrootPNG  IHDR(-SPLTE|ſ°몡ͻҤ͉ybuwWԮؓĹӣʣֵϥʽظɼനöܷ޿⾷ܱ٤ıµ[R]Tog[TͿ׼br^ͩ߼ݱԮȭqR}umzŅϢ׷ůңܢͿypPȆѨڼӾظ±̪Ǧ¦_bnh²ɴզޑךտϩŦjƧ۽ղ{Xly˭rXɳlg ȷu DT\ϰL]hس׻ŚHtRNS"=H. >K> *4t334H.?2"3`04&CIDATW]ʡAaw|gC%h6c3&$BRw!*`Gs,IENDB`spyder-2.3.8/spyderlib/images/filetypes/po.png0000664000000000000000000000165312626055322020134 0ustar rootrootPNG  IHDR(-SgAMA7PLTEnananananananam`nanai]fZbWnana]SYOnaSJnanaMFIBnaC=na>9na95m`5~1j^2z/fZ2z/aV]S2z/2z/WNRIMF2z/2z/GAC=?:2z/2z/:6734}12z/2z/2z/2z/꺾ŴŰ׽צ،ίϵ֢ԡΪđɇׇЦիܐۋ׎ɯսzﵬԜٕmmDŽ˵ߔ篵ޘ뵧ﱩэՋfgq֚sނqzБȈ쳢⛍ꝷnfugvMfMe]l~sۥ؁mw忧]l‹ڸ`ئ?wFjPYlc}slѐ^^SnzѯmĴ_W{gZ鰐ԃܿV\[KCCRbveآגޠpƄШ\WVQJGWeytiqnm[[XNXfxgZܦvיy~¼jie^j`hY{gp讠}xx̀ڠzfś³þҰ~&]9tRNS=H zIDATWcdF(b30p2622~ 1T0`| dΈ߲0 03Jx2",L|(L`TaOcd쎿w4Wed\|;- @6<@<֧%IENDB`spyder-2.3.8/spyderlib/images/filetypes/chm.png0000664000000000000000000000115412626055322020261 0ustar rootrootPNG  IHDR(-SPLTEѷ{pꡄۧƾXh)Z(\Aɿp3]ʦ}RL$ZQvlR%g'楈xxo`WuGмDZsNRe*I¸rBY!cQֶv֚f'S!`ITq;ǡ߄]]ތsp;xA݇dʲԯ٣ѫ 8tRNS#IDAT1.qӿVM&qLnfvG03JLQET HwA FN ̍P j oA= w{9ttpidddddd_iqx~oaddddddZm{`inpvnefeffezvcjibdddddddddddddddC{s?dddddddddA 6Nʧ{f o-蚍ñѿ|1;ӼWf?ۑI ^ ףKƃhsIDAT1(RR2t$) 2(Bd ,6b )%FJ$aQ)dxO$/I$)jO" XʦO-Ihnk^ S92級pc9#xӟ( >+gC]S.%r^O &`˭$1)'s}YPdYU~ښ$e+;o'4IENDB`spyder-2.3.8/spyderlib/images/filetypes/rej.png0000664000000000000000000000025112626055322020267 0ustar rootrootPNG  IHDRRPLTEF}KtRNS@fEIDATc`FA``TRRRRRd`TTRDQ6V26BQqRrQBQ  E R EQ ll jZIENDB`spyder-2.3.8/spyderlib/images/filetypes/css.png0000664000000000000000000000050512626055322020301 0ustar rootrootPNG  IHDR7 IDAT(mJA[WRGbai,,>/'D$yEFT rp`4e[ٟovISP @Ŧ1`itb 4`A( H  %(]" FGNYg@I& )ӭVk)՝R- C9gg~=(SK/Jm k }Fr=0ӻFP\>&`p cA MJl؂ߧ茉`opIENDB`spyder-2.3.8/spyderlib/images/filetypes/diff.png0000664000000000000000000000025112626055322020417 0ustar rootrootPNG  IHDRRPLTEF}KtRNS@fEIDATc`FA``TRRRRRd`TTRDQ6V26BQqRrQBQ  E R EQ ll jZIENDB`spyder-2.3.8/spyderlib/images/filetypes/ts.png0000664000000000000000000000150512626055322020140 0ustar rootrootPNG  IHDR(-S PLTET? S R @MPFbOc^$")!Q \&8>] n`-+JlBa  ]bQid Lm\r*$O]"2$4< ,?ty,*)6I5ITM`322_,#"#_)?Z$ +s 9676FK6f9:Qd5'^Sq`Vqu;;9GN5p7NW4??=Vo*#++:pbt.<:;[n0?E,s0j4321j6c'-8855=!8FDUp+($-2.`\}% !#'-+$|&&$l(#RtRNSHw? %QʲeT]8/[l i oZE. ny;p0U`ΠTgPJ>Hx\D-[JKz=9\Ӗ osŹIENDB`spyder-2.3.8/spyderlib/images/filetypes/log.png0000664000000000000000000000106712626055322020276 0ustar rootrootPNG  IHDR(-SSPLTE4(( 'WJ q0HKA?䫪$$*aQK+0e69ȱ("5n in%%իD,A"㳰6a=U ݿ$3,)߰UMg! ױ6HEA଩*ntRNS@fIDAT5J䷒@ } *ؔ42V\,b&&}g@Yq4g1QMfwU)-}VNau!+[mVN]xԥl)~޽}v NY5IENDB`spyder-2.3.8/spyderlib/images/filetypes/ui.png0000664000000000000000000000164512626055324020136 0ustar rootrootPNG  IHDR(-SRPLTE8R vRy3L Lq*!uW8R "13&|Kn!1x x*u'!1oh׶hMKFh FCB꭫Ud3B8Ђ,/"2]/&Gk>D)y[6Ca [=Y #3 |W?%6Cc JJLѝS=kb<#34OsKRMKG?PsS[^^VSURDR`g|lebccVI[|nYalwWNr6PsqsKFx{lXiLMr!4NMO0Yc=JB[9n}@z,&426t2u.3CE8FK4|Ns"^p)%l%%+,#$%Lkon96%Wl OeD[JT.tlJR"# YLX.ɀ v +KYtRNS?֮zM4ѳ}R)ץECS$kؔԫ6Q"b3@ڬ{ WIDATEMP@{?g&ЦaZ:*V0  ) 17`H"~JbsfS,OzVu֙>f u ޑsoPT5n'u]G0wVګ*fPܹ#z?]zj ut߭H/[~zo!4XIENDB`spyder-2.3.8/spyderlib/images/filetypes/pyx.png0000664000000000000000000000153612626055322020336 0ustar rootrootPNG  IHDR(-SPLTEdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddJFA{9ttpiddddddH\ddddddDO~aMdddddd@~v?ddddddA 6Nʧ{f !o-蚍SɺyM˼`͹|;ӼWf?ۑI ^ ףKƃTTIDAT=KB@{EGA>C(P$" --AE B R!@HZ1n-# 0N@xĹ!~/0pP%vhp?`޲ Eڪ:sC5/`6MͮjV0Q=[|jܝǻUmX5Nz] ؗPU Kՙ0M2Kd5AųGIENDB`spyder-2.3.8/spyderlib/images/filetypes/pyc.png0000664000000000000000000000151712626055322020310 0ustar rootrootPNG  IHDR(-S7PLTETTT:::NNNVVVMMM===::::::333222///111222333777UUUTTTQQQ[iX8=7QkMJ[GRiNPeLKKKbs_`s]OOOXXXfwcRaPUnQRfO_}ZUjQ\c[ZZZ___^^^???@@@AAAɢG=uKxWWWfpdrnnie~`R[PEEE>~?qAkGiDDDhvftpsqwtCgEc}888cccfffӡ=sAmChEd}Ge}S;;;P_NbgarnEt@nBiEd~Fd}GHIIIKZH[gYli;z=uqv>3CCCI~c`VK@58NNNSaQQiN^{ZѤEoaXMB70JJJNQMHRGMZKjwhaZFFFHHHGGGBBB_QF4JZGGMFPlLMaKRmNPhLS=?EWCFUCKeGI]GNlIJ`F<<<===999555㤇tRNSBCAIDAT++Y\1,J-6S&It閛r,/0`>pD_a$Jw|: ן <$l<-ZVq,`zfr~W8:kc.8M8?oG,6PV㭷ĸ"MW.{2$IENDB`spyder-2.3.8/spyderlib/images/filetypes/jpeg.png0000664000000000000000000000066012626055322020440 0ustar rootrootPNG  IHDR(-S&PLTE]dbdcefegfhbgkmlnomqrqksyxy{x}~~z}SGqqptcev:DLe`NPV@8+!^gosNSYXkE@V`8?U(2?7QvsMGP1"64#,tPg:1?bM]@C*chBxpx~TdqQQWAQXHjNU5OSBAtRNS@f8IDATWc` XPE! 3BIK (J@v  ېIENDB`spyder-2.3.8/spyderlib/images/filetypes/cfg.png0000664000000000000000000000062212626055322020250 0ustar rootrootPNG  IHDR(-SPLTEYtRNS@fIDATAJP?P1.t/ŕ  5iByD@/$n*dUhIlG [(LPMŅs>+##-(緊:Z00tizR+##-(緊:Z00tizR`2Yr`%|v%\S%NQi4z3v\xCƓ Vj fdEIENDB`spyder-2.3.8/spyderlib/images/filetypes/pxi.png0000664000000000000000000000153612626055322020316 0ustar rootrootPNG  IHDR(-SPLTEdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddJFA{9ttpiddddddH\ddddddDO~aMdddddd@~v?ddddddA 6Nʧ{f !o-蚍SɺyM˼`͹|;ӼWf?ۑI ^ ףKƃTTIDAT=KB@{EGA>C(P$" --AE B R!@HZ1n-# 0N@xĹ!~/0pP%vhp?`޲ Eڪ:sC5/`6MͮjV0Q=[|jܝǻUmX5Nz] ؗPU Kՙ0M2Kd5AųGIENDB`spyder-2.3.8/spyderlib/images/filetypes/tiff.png0000664000000000000000000000066012626055322020443 0ustar rootrootPNG  IHDR(-S&PLTE]dbdcefegfhbgkmlnomqrqksyxy{x}~~z}SGqqptcev:DLe`NPV@8+!^gosNSYXkE@V`8?U(2?7QvsMGP1"64#,tPg:1?bM]@C*chBxpx~TdqQQWAQXHjNU5OSBAtRNS@f8IDATWc` XPE! 3BIK (J@v  ېIENDB`spyder-2.3.8/spyderlib/images/filetypes/properties.png0000664000000000000000000000062212626055322021705 0ustar rootrootPNG  IHDR(-SPLTEYtRNS@fIDATAJP?P1.t/ŕ  5iByD@/$n*dUhIlG [(LPMŅs>+##-(緊:Z00tizREZEا;^1Aekqa;?dW bt:v$Z>V"Rb&i+$?5ob7Vk:,Y3LdF[vo'Sλ//xtkTcϩ\XvOM^{ӵe[P [*)I aKdD2reiuX<*<0IENDB`spyder-2.3.8/spyderlib/images/filetypes/pxd.png0000664000000000000000000000153612626055322020311 0ustar rootrootPNG  IHDR(-SPLTEdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddJFA{9ttpiddddddH\ddddddDO~aMdddddd@~v?ddddddA 6Nʧ{f !o-蚍SɺyM˼`͹|;ӼWf?ۑI ^ ףKƃTTIDAT=KB@{EGA>C(P$" --AE B R!@HZ1n-# 0N@xĹ!~/0pP%vhp?`޲ Eڪ:sC5/`6MͮjV0Q=[|jܝǻUmX5Nz] ؗPU Kՙ0M2Kd5AųGIENDB`spyder-2.3.8/spyderlib/images/filetypes/nsi.png0000664000000000000000000000170212626055322020302 0ustar rootrootPNG  IHDR(-SPLTE|ſ°몡ͻҤ͉ybuwWԮؓĹӣʣֵϥʽظɼനöܷ޿⾷ܱ٤ıµ[R]Tog[TͿ׼br^ͩ߼ݱԮȭqR}umzŅϢ׷ůңܢͿypPȆѨڼӾظ±̪Ǧ¦_bnh²ɴզޑךտϩŦjƧ۽ղ{Xly˭rXɳlg ȷu DT\ϰL]hس׻ŚHtRNS"=H. >K> *4t334H.?2"3`04&CIDATW]ʡAaw|gC%h6c3&$BRw!*`Gs,IENDB`spyder-2.3.8/spyderlib/images/filetypes/txt.png0000664000000000000000000000104012626055322020323 0ustar rootrootPNG  IHDR(-SGPLTEƧʴȸ˻þ¿¾Ƹɹͼ¿)tRNS#IDAT]@ _& !AJ $ϋ@(Ξ4' ֙-3W@ j̠[%D$I*;])ֶiwCQgq#ڈ#WIENDB`spyder-2.3.8/spyderlib/images/filetypes/py.png0000664000000000000000000000140112626055322020135 0ustar rootrootPNG  IHDR(-SPLTEĿ񫫫̾G=uKx>~?qAkGiݰCgEc}=sAmChEd}Ge}SEt@nBiEd~Fd}GH;z=uqv>3I~c`VK@58EoaXMB70aZ򷳳_QF4S=?⛭t tRNSoICH_'IDATM+ٖAAf"qrX:ڙ_ g@jIRJ’mx`2Yr`%|v%\S%NQi4z3v\xCƓ Vj fdEIENDB`spyder-2.3.8/spyderlib/images/filetypes/png.png0000664000000000000000000000066012626055322020277 0ustar rootrootPNG  IHDR(-S&PLTE]dbdcefegfhbgkmlnomqrqksyxy{x}~~z}SGqqptcev:DLe`NPV@8+!^gosNSYXkE@V`8?U(2?7QvsMGP1"64#,tPg:1?bM]@C*chBxpx~TdqQQWAQXHjNU5OSBAtRNS@f8IDATWc` XPE! 3BIK (J@v  ېIENDB`spyder-2.3.8/spyderlib/images/filetypes/bmp.png0000664000000000000000000000066012626055322020271 0ustar rootrootPNG  IHDR(-S&PLTE]dbdcefegfhbgkmlnomqrqksyxy{x}~~z}SGqqptcev:DLe`NPV@8+!^gosNSYXkE@V`8?U(2?7QvsMGP1"64#,tPg:1?bM]@C*chBxpx~TdqQQWAQXHjNU5OSBAtRNS@f8IDATWc` XPE! 3BIK (J@v  ېIENDB`spyder-2.3.8/spyderlib/images/filetypes/hh.png0000664000000000000000000000044512626055322020113 0ustar rootrootPNG  IHDR7IDAT(mѽJANF-g b">EZEا;^1Aekqa;?dW bt:v$Z>V"Rb&i+$?5ob7Vk:,Y3LdF[vo'Sλ//xtkTcϩ\XvOM^{ӵe[P [*)I aKdD2reiuX<*<0IENDB`spyder-2.3.8/spyderlib/images/filetypes/zip.png0000664000000000000000000000105112626055324020312 0ustar rootrootPNG  IHDR(-S}PLTE۴ؽ}ڹq׳lĉԮƉ}}φɃҸwҳrήnհpҊߺzܹ{XXSUƣbʭl˪i̫kO}BiV-KؿVY\^A~f3p[,}?жDJMOx8zb+m/}8ŗ}IvEZEا;^1Aekqa;?dW bt:v$Z>V"Rb&i+$?5ob7Vk:,Y3LdF[vo'Sλ//xtkTcϩ\XvOM^{ӵe[P [*)I aKdD2reiuX<*<0IENDB`spyder-2.3.8/spyderlib/images/filetypes/gif.png0000664000000000000000000000066012626055322020260 0ustar rootrootPNG  IHDR(-S&PLTE]dbdcefegfhbgkmlnomqrqksyxy{x}~~z}SGqqptcev:DLe`NPV@8+!^gosNSYXkE@V`8?U(2?7QvsMGP1"64#,tPg:1?bM]@C*chBxpx~TdqQQWAQXHjNU5OSBAtRNS@f8IDATWc` XPE! 3BIK (J@v  ېIENDB`spyder-2.3.8/spyderlib/images/filetypes/cxx.png0000664000000000000000000000044512626055322020316 0ustar rootrootPNG  IHDR7IDAT(mѽJANF-g b">EZEا;^1Aekqa;?dW bt:v$Z>V"Rb&i+$?5ob7Vk:,Y3LdF[vo'Sλ//xtkTcϩ\XvOM^{ӵe[P [*)I aKdD2reiuX<*<0IENDB`spyder-2.3.8/spyderlib/images/filetypes/exe.png0000664000000000000000000000113412626055322020271 0ustar rootrootPNG  IHDR(-SPLTErf 9у{DY?I]$ lr" %;y<6\Y*?Oz IENDB`spyder-2.3.8/spyderlib/images/filetypes/pps.png0000664000000000000000000000072312626055322020315 0ustar rootrootPNG  IHDR(-SPLTENy >do/TpQDgNsaqnfyh솽|||zzz}}}~~~FttRNS#IDAT1Na&Px K;m;X e:SĤB6SbD0A $qFIov,c$'%IFC.IEMס~?l{ynV5m'2xU(nL"?&4IENDB`spyder-2.3.8/spyderlib/images/filetypes/ini.png0000664000000000000000000000062212626055322020270 0ustar rootrootPNG  IHDR(-SPLTEYtRNS@fIDATAJP?P1.t/ŕ  5iByD@/$n*dUhIlG [(LPMŅs>+##-(緊:Z00tizR+##-(緊:Z00tizREZEا;^1Aekqa;?dW bt:v$Z>V"Rb&i+$?5ob7Vk:,Y3LdF[vo'Sλ//xtkTcϩ\XvOM^{ӵe[P [*)I aKdD2reiuX<*<0IENDB`spyder-2.3.8/spyderlib/images/filetypes/js.png0000664000000000000000000000074012626055322020126 0ustar rootrootPNG  IHDR(-SPLTE܆Ո֠RŪ甲T:qvԪ哴yՄֲ%bXZϕwQ@y&a,gXNҤh=y\[3oeً璻f5pdH`wrᗸ^յe|m챺ЙѲ여йF&tRNS@ftIDATWu10D;k NW=h t(."(t4ҊPř iNj Us\\m !VtRMb2`8.[? x| 5deIENDB`spyder-2.3.8/spyderlib/images/filetypes/pot.png0000664000000000000000000000157712626055322020325 0ustar rootrootPNG  IHDR(-SgAMA7:PLTEnananananananam`nanai]fZbWnana]SYOnaSJnanaMFIBnaC=na>9na95m`5~1j^2z/fZ2z/aV]S2z/2z/WNRIMF2z/2z/GAC=?:2z/2z/:6734}12z/2z/2z/2z/꺾ŴŰ׽צ،ίϵ֢ԡΪđɇׇɯսzﵬݍׄ˵ߔ篵ޘ뵧ﱩɍgsނqzБȈ쳢⛍ꝷngvMe]l~sۥ؁mw忧]`ئ?wFjPYlc}slѐ^^zѯmĴ_W{gZ鰐ԃܿV\CCRbveآגޠpƄШ\WJGWeytiqnm[NXfxgZܦvיy~¼j`hY{gp讠}xx̀ڠzfś³þҰ~ :9tRNS=H IDATW1JCQg^>w%E܀ ,d 6b*2,҈-(i"Oi sg ߭oAF^ e|?_WݩCPUg4}MŹʼn#D)ϙ ԕ堵^`^krf~Wt3zGuE IENDB`spyder-2.3.8/spyderlib/images/filetypes/jpg.png0000664000000000000000000000066012626055322020273 0ustar rootrootPNG  IHDR(-S&PLTE]dbdcefegfhbgkmlnomqrqksyxy{x}~~z}SGqqptcev:DLe`NPV@8+!^gosNSYXkE@V`8?U(2?7QvsMGP1"64#,tPg:1?bM]@C*chBxpx~TdqQQWAQXHjNU5OSBAtRNS@f8IDATWc` XPE! 3BIK (J@v  ېIENDB`spyder-2.3.8/spyderlib/images/filetypes/f.png0000664000000000000000000000042612626055322017740 0ustar rootrootPNG  IHDR7IDAT(mѡNC1]Cpfp`P84 ^G(EA2C"Z݀49M66",ѻ?oܬH5%kLHh:PWKAAvaک3d$O ]&Tʃ'#If].8w%l"{l. [Ň$QQ1E"vl{~!YuHpVN@rGxIENDB`spyder-2.3.8/spyderlib/images/filetypes/pyw.png0000664000000000000000000000140112626055322020324 0ustar rootrootPNG  IHDR(-SPLTEĿ񫫫̾G=uKx>~?qAkGiݰCgEc}=sAmChEd}Ge}SEt@nBiEd~Fd}GH;z=uqv>3I~c`VK@58EoaXMB70aZ򷳳_QF4S=?⛭t tRNSoICH_'IDATM+ٖAAf"qrX:ڙ_ g@jIRJ’mxWΠ3C[N[onxXdwKVjWbucn괸YexO[nݎx:tRNS<]uqqsm=MTLSQLeqLoLoooLooLopNq$Vw.IDATUM+Q~sμVj"OkvֶW, Ju9` oyğ+K D$?A:. hD|Zʴ6LצnD!lc A9AwB#igc{P>ܷۛ1AVm={\_g image/svg+xml spyder-2.3.8/spyderlib/images/qt.png0000664000000000000000000000331712626055324016137 0ustar rootrootPNG  IHDR szzIDATXý{XYdz>.6m$ 2ݦ biH5]uq)R&,6Ii ᑄ],Z )T3559eI"{}~sy?~{Р{Ҁ>ЎiTb@p~AO`=u xS>4ȹr ANVEv,o`WO9i#o9k #o:7[_A@cIM J?Z{M#xYLAvl]N?qɾpmƐdӤ[@4+_y$DHGcF#31'Ӏig9&mmJs󄘞<x"Wj8};o$/W&9ϰ@X#^3 C R#-ʰxC;Q hPɱp25%Շ~ؘ·)k0s#"RI5ߖZ#.c ~ZP5ηRUoH~FJЁ{|ft, *BD$`~,4<¼<IIKXë$5Ȏ"NZKA`J^kؠ2ЊL0y0X/ߌf]f̖+lu̵\v"ܙe ӰqW$+XulЇuw XFLf}{Bج̸@6hUʀ"yPDNc}'$ԦU0qP-qeklj!KLZ4~Eokii766>~.?+}ABҌԐׇpp9.g044C8 n݇M[M mP\.\56panlujiCeXS[Ϸ+m Zq~+`/&۰ sMrS\KOYն` j=\+btFJNos5@Mkyw͌el{0Wj,CNcWuXGUXD!a{̹OtyhievD'n(/! J")}URb\ y`;w|1㽣=\ AO;{`JqJ-W,Sa` lW#lM >~:@pB OG ^灒*p29nL[K%OhFiCj;L/ws"T*e(PC9{LhAt,+.?Ga8{[;:J)3S݁;w\ -@;|.}5]NTt(gy.huuu}QCms^_bOpsss!w{A d2BdKt3 :f5H?'vR=:;$e9@d]Lz"L m It' /3mם{Q҇NdtvP\.Zc>?d >艫^sss>JgK$$uh"&N5X,nS8Gm>k_]/ lIENDB`spyder-2.3.8/spyderlib/images/qtlinguist.png0000664000000000000000000000335112626055324017714 0ustar rootrootPNG  IHDR szzIDATX{Pyc;:,KJsڡXk~EThRڐ.8sBD˸˭Ūoyu#vߙ|~=UR>Lz\-vXS^? 4]bR@)5jN/JI9f!t 4<`Ҝe+GcE"j"U *؁}8øqeIo|J 1LqvOaU!r7EӮ՛ 2Wt2.1`7H'HR'&\4LLėCbYrJDŸʙ R(암LG<#8e >ghPif{`ww)%SPro,]; r:(ۇKWfm  8 U"u2B1}OX)eA(8y+ē$u,cb"w%]9uO&n?9>7jJ.oyeհgM)5Q[G19dc!q*l^@Dhd/p)ais4}n ڪ;8Y6.#DË%ςȞ {C)NjO8]8 :^zpNC“l ,ضeV9roBYO,MF2gM&34#HJS[h՛{ȱXQmdMU$G5zxsa'bYxv[>+މ8SGM1잺L=͇oՖÞ"U*0M؊Oʾ?m|<;=aE+; *X,½xwcS0y*^St\;ӷSZn%vU-8a -$٤Ol.Z-Jn@g*8#j.ҹע! pl4v (NJ^NCC]zl^v9:,mύ9!}0v3*߬nU8l B;k؀>:% & ŌYRHMsDG{Pr9&XDhhH8mZYў*H?׵<{aۏоk2W'>Ʉ"rhO5d=y!Z4c奏9w^ t7#۪xpDɂ}<=꘹ mm\-8V&ͪlE5Xx`<{il|4j]8GFnmg~.ׄPwL6|:8qtS,댇V6%Թ.tia.#L!bJt ~4#&/臩aWϊQM]Nb't$=#bQd99̽pyF!Bptu})Kl>E&^kk'=%hnb}T1D4xj830kޣNfEOHIA̷"CGKQuZ)$er@75@k?IENDB`spyder-2.3.8/spyderlib/images/projects/0000755000000000000000000000000012626531443016631 5ustar rootrootspyder-2.3.8/spyderlib/images/projects/show_all.png0000664000000000000000000000050712626055324021152 0ustar rootrootPNG  IHDR7IDAT(uѿJqM6XBBZ[ڊHPE| 6/ER +EQ 쌅 Ž͗sd~wxTh|O?U3s 7= Pm? Iu @!ATt7?Deϱ;\R٘b z~X%ƢRy6R*gcǩ{-I#_j[B.p\[2|cҝ$2dᛮS 1?eIb|r4IT=UئAjgIENDB`spyder-2.3.8/spyderlib/images/projects/pythonpath.png0000664000000000000000000000142212626055324021535 0ustar rootrootPNG  IHDR(-S PLTEѭ޵٬4c/XSvQԟі΄uol%""$!!mVˢd*qdhi_Z*:_)+!)3 &.rr8y6yr+y)xHzHۼ7`72]1:_:.}+,Z*qqee.d0RHeYwynt{p{p{rcbڧ)_,zo|qukxovm_\׈ڗ愶+Z-e 7'/+;695ieچy{H9g#styؐssxUUe==e>>uejuphW-(4,G=C;?7=510DCQ+*k *":2?7A=f))ef l k h e] a+tRNSAj޲#1%1QugIDATMa~M,vp [3p-TUSՂG:OUjjZj[]4ճlO-Tм@w~h~j@#ޫ#e -wlk'>-i|?GC|`IENDB`spyder-2.3.8/spyderlib/images/projects/project_closed.png0000664000000000000000000000055212626055324022341 0ustar rootrootPNG  IHDR71IDATajk8ML"&v1IFb4GDEOjgO{\o+z{"/\\4Ǧ~|#pp䳑"P䴘ٓ;1S"rr,XVBi rMH2sDRF6vhhn0vO* F&k,]j("y3\؎=ɡ_V_unx~c9}UuoR|xJ8 [b>#)| *7IENDB`spyder-2.3.8/spyderlib/images/projects/project.png0000664000000000000000000000125012626055324021004 0ustar rootrootPNG  IHDR(-SPLTEþ؜ޖهzrzflIT3=-4)% ($,-)% ($40)% ($($ͫ! #D@;ϋ !]I?muntltzkrrrilmHXUs7XHU*F.I(8&=.D+;$6)<- uAtRNSAj޲#'7F1Z%1w1HIDATW]A@Fi7Rٸ:HĂ8TYXLE-{Q 2LHm rH-{Y\$յRGjDuyP&y'p%7;@[FIENDB`spyder-2.3.8/spyderlib/images/projects/pydev.png0000664000000000000000000000152312626055324020470 0ustar rootrootPNG  IHDRaM=PLTEܺ֫ӧڳʱqppqtplxΝ޽pXjlXim[jp`qkoqrrih|Λvj}hz|gw{bsw]nrPbjYjjpqstn֪orkctw]npYjnUfkP`dBT`m~bofըȆvr{vohӥ{͗fkNjܶldƊժlΚǎuj۷…z޻ݾǎq~ׯذӦƍΜ{mj~͛̚ƌwp۶_v¬rҡkrmݸǾǸxtRNS@f]IDAT(SO(a28%(1)J91E9PvWJɟrruJ-~v^)hADJIǬq=~:+?tRNSAj޲s1Kb%!}Ճ&1yrIDATWʱ 0;?r i(h2CP D$DBndSwk8YM,(`-lf=JmJvYdbW"U푽^ԛO>T=VUC53`At6%4G9:ggc/mymeR(IENDB`spyder-2.3.8/spyderlib/images/projects/pp_package.png0000664000000000000000000000135112626055324021432 0ustar rootrootPNG  IHDR(-SPLTEѭ޵٬դџ͖ɄyvڹouĖ\oj`IlgYDmgYDVjX8d*qdhI;h7|?_Z*:A*O9jR(`I A00!)3 &.ۼڹpڸp۹ywXUwPrr͈ݻxwˁʀ׻v+y)xHzHڗ愶uщܶtԬ7`72]1.}+,Z*qq{ݵiȫk˧fʡ.d0RHwt{p{pcbyϪ`W\ř+Z-e7'/+;6ieh˨ZTRѻ>A{<. e$IǬq=~:(]*%f&6p>W-(4,G=C;?7=510DCQ+*ڗ愶k *":2?7A=y{f))ef l k h e] styuph4WW:tRNSAj޲'7F1Z%1;IDATWU1PޓH,@ -% 1cxQ=ŹŜwTRD5!P#{uK7ʪߨ~R *CF]nUQC{_/auSRDA VBa[IENDB`spyder-2.3.8/spyderlib/images/projects/folder.png0000664000000000000000000000130712626055324020614 0ustar rootrootPNG  IHDR(-SPLTEѭ޵٬դџ͖ɄuĆo%""$!!l%""$!!m%""$!!V$!!͢d*qdh@==ϋ_Z*:!)3 &.ۼڧꚷ׈ڗ愶چy{styuphѡ(GtRNSAj޲#'7F1Z%1%IDATWU1 @DCv hFW +=$b#f|02dU} @T tQy&~f\( j2'Q ]Ϫi;I/GJIENDB`spyder-2.3.8/spyderlib/images/none.png0000664000000000000000000000012212626055324016441 0ustar rootrootPNG  IHDR7tRNSv8 IDATc 0 HIENDB`spyder-2.3.8/spyderlib/images/file/0000755000000000000000000000000012626531443015717 5ustar rootrootspyder-2.3.8/spyderlib/images/file/fileimport.png0000664000000000000000000000400712626055322020577 0ustar rootrootPNG  IHDR szzIDATXÍk]Uq޹wm)ePl DPQ!Z#|1Z)-!UQ?b J0|DJ!ЖBȔ)әL>c{vng=^{})[{x'MdAnz}TI\C,w. 6 v9`GPyz){ptzsT-(.R +x4v-漼|N/G{}  ) PphYqgx3.SK5\nݜgcC>\GBYH%*J(%֭#"<Ä]) MSRJ5_{1aWs 'P^hJZFa5Sqr~jf0&(' 8 [@zBSRhM\P!VA$(&˒4Kꑓ`|~* (%wE))Σ Me{3x Cp L7j(m(T5FEK觟~>`ֵ@&%`%p4X,L0 Bl @7^ -l W JҘ7xhQA<B;S LGLk.7LBJ:UrYbV8^8I$ù̥41cҠNPnA[7VM\j,%=M&pYN%\HTx80iԿ挾c0Y;Ai^uky]9|xР4$Y.)wcWm{JyR"g\I9'S\7sDd:X= g jpR`K/DG$]q%iPֽLe$4PAА!v\u,MJJi2MK/?mS{E)}}ey]g/?ڋVapU) y1Dž) N Tg( 2`MZBJ<ۿ08:qWWPjӬhn~> g/?c#| y-Q* pI@fTkUzlE~ V~+R{A)R,MQQĞ <{O#cC;$$8(+j^'H3\ cL xd#^vh& 3Y6(mXs^|1WV02:* y&Q`UW% -yʀ./bOv}ހjlUF7Ə]=NwǛAL6RBxBMicFhǗvqێUZ!nZeq7]xl"ҨA'ci4ʊS䟿×?^9{Jݜ3kBSNʼ6ݣqq/.AOS|u5z "x;BD~f*nx7T&8x`x H\y ~|j>s+V_?L1/3?s7>㝿8qlyw#j@~^lxus m.̎s W-F.:.~ `Ra8soR ׻9=Gi821KMek64s9f1lݺu y9'X3I׽v_Ƿ| PVЦE,pkTROI(hsC.$rs[-hA)TjfD!jBmc}MIENDB`spyder-2.3.8/spyderlib/images/file/filecloseall.png0000664000000000000000000000045112626055322021062 0ustar rootrootPNG  IHDR(-SgAMA7oPLTE4BRz6Wv1Qq|6DVyomr*5ATpPm&1?Gb~OkNjD^z\wZya~^y[yWrSoj]zWrfUpTqSqEdDcrJ!tRNSdc?FI QA8IDATWc`  ~A)6 I@-# 0210)F6F83F kǪ(ъIENDB`spyder-2.3.8/spyderlib/images/file/filesave.png0000664000000000000000000000170712626055322020227 0ustar rootrootPNG  IHDRw=IDATHǝ͋U꩞qBfHFB1AĿ½KW]s#B\(.? !J4a4wWST+x9doi3gΝ}LzJ;k!Ds~:1|ȉ<ܼy'=.]zݳo*Q˗^[eפPopslanƒ;I3!V8b9/ @+=pNΙS>4cf Q+t}"k>d@Uq#kAMI1T3V0ǃ}Rr&g]l2;pH+[4TzmΟqA>; .߿]XJSH!äPea88"hD"aK1"c2V1bfErNojyYMH G0#ebcX9 l{Wx u渦{D JׯߡUxEEhzDKren>y|;4M;!{@I!U8mQUeڶ{fiD3[))R܎jb:4`@'D'ڶTIh,E>0)%#wM3X(4gef+Qyp;dʹf~AJ&|'/%quӇ"u93/Qmx襋7^qFՈѨZ,"ܻC)1̜d8xYu]{z2qٓbB) MYvwsηڶV"U3ی!l9 !LDd""`Kl:woDÜ01aq@6\ èʒlIENDB`spyder-2.3.8/spyderlib/images/file/print.png0000664000000000000000000000212412626055322017557 0ustar rootrootPNG  IHDRשPLTE```qqq ʡUUV;;;778_``ĵ[\\sttopp}~~eefVVVPPQEEEOOOCCC???HIHKLKKLKDEDMNMGHG<<>><==:;/,-BBB޾===GFGMLNNLONMOONPPNQPOQPORPPRKIK>>>̵GGGHHIؾ7QtRNS^c̿@;;4Isu<ּ#<&Y>mP~`}]+~ݽVkgmKIDAT(;JCQE׺~,Z,lM9hkc3r6 ,A/%ZaPEw;HXs?"@[<ՈlNӗRǪzG-Av:rbq)7{.e`HY).j~-Մ m[ZF5׽WL.[De%քNvsDnrv0v+l+;?>%C4IENDB`spyder-2.3.8/spyderlib/images/file/fileclose.png0000664000000000000000000000214312626055322020371 0ustar rootrootPNG  IHDR(-SPLTE7Pi.CZ2MkIhHgQp^}VuXw^~;Sl!-6?`<\{^}Qq0Op6Wx -7<]|]|Rr2Qs]| )%'5H$6#5 /E9]1Qs^{Xubb\{`ZyCbc\{]|[zGfKkYw;Wv ;Ic>]{=[zPj ?Rbv6Wv1Qq[k.9BU[c}6Tt-LkyV`jGWjh~YwPog}AUj%"! .'.)EVtLj.K *GfF]vH_x,Ii=]}NgOh;Z{4UuNgMe-KjE`{WpSkF`{XrdWxTtTllcE`|dSrQpZqJdaCdUoeZyYx\tUoJiUuYsngYtXwեԆЪВ֠twchwby`widwq_|[zHhKjca~JjXwXvNnB`?_?_~Cb8Wv;Zy7VuSq[x>]}=[z=\|=\{=]{\zEeGfLlb~gMmIhFe^{[xMlSrYxnmZyRr\yTnRrWwaxycWvSsUob}q}nb|tmBbOKbN\-R?͛{A$! a-ATԚ@! !xf$ ׹$"aɠ @@+sM9{vZc]eJQ((`ڕ0li;s淬!:mm*gŅim`2uB>91(kJzՎ; (kj4H\oC 4Ǿdn|+ilvT9G-.FIX,C{|i8~ًP(8kEC$M\.sv9hJ[ҋyak>Di1Y s2(J(*gR}{xrijSCΝgGJ*`Y T %<믽_8qff.R,QX^^&M14 #"{bs9}کSJeHxISo94ͦ?bc_Jō ֛LNLp1:;;iii{%O%$!MS4%I"fݵJ֮CLNNǘRhD $IB ϒ(99>>p}Tj59Z6''P|.O>[wcX|zYbT5}{}Z'|Zvt[R9[m:c5&kLj&gϞ+fWNTUUM<LMo_1 cLS@pMUmc_/X[c^ܾKQ S: wp]!i2)6߸@pIENDB`spyder-2.3.8/spyderlib/images/file/save_all.png0000664000000000000000000000261712626055322020220 0ustar rootrootPNG  IHDRw=VIDATHǝˏ\Guϙx왉 c,LHV% $X0 +6(A@"F,0!q&I4'v^qOO?{T"9#]UΧ}{ᯜ87NիFf$ vxyUNaVVxϳMk,[}h޽{ߝ;ǿMɜLu+8*+8@UՈ8Dxɧ1} hل0x!`"۵րKƵ~` UJ"I@DpsFS_旿5G~}.ʢ"C)E- ]~p4ִt+W6jpwq4C񱟮G ~xωS1o,|"#D C@=d׻#Q(JPj0FmΫ,"V $ʢbZ *IX4js#MS# Y`1;]\$Qe NnJ{O EhZ[fLk1BŤҥW"9q""wE ( ]?q=GUU8H"ڔI1!xL&%шxh\0{H$O0dyy7 ^cj&BN CUUX[#a?ZTTBLPَ\J)r nuԕxa:%UUcdZcP5"bDI3V={nXrR3g9t| \pA)E7v\>zcǏ- [[uuSBls$>|~/e] Sn-1}};Sm`X+N<`C&* KS*IENDB`spyder-2.3.8/spyderlib/images/file/fileopen.png0000664000000000000000000000230312626055322020223 0ustar rootrootPNG  IHDRשPLTEެXXXNNNKKKJJJHHHFFFDDDCCCBBB???<<<|wwwzdbbx(%%(%%WTTu(%%(%%(%%(%%!WUUr(%%(%%(%%!VTTq(%%(%%(%%!USSd(%%!USSƚ]I/}(y(%%!WUU֪mgd:Xspp v(%%%"":77̸;Uyc%6I(%%%""uss >Y_&,5)+1(')洿ᦽ菾呿ૻǯ쌻㎻㍻সƪ鋸ኸॷƨ臵އ݈ߤŨ牴܀$ɹutRNS5[r0@Ru0SRRRRRRRRQ[9O\&)$g 73q@B{о.Nߖ^q+dn5ap @+ dl  eIDAT(Se?/CaGjqcАHL,*&B /  Cgf'dS%Rý,9ȕYaŜQL?DF$ᵸb.I#Ks F.[ycY?Ւ.<-E)V5/xqVTׁlTK7T4lFKx\MVZZu_7`P@?T6'57^P 2*x`-H֮IENDB`spyder-2.3.8/spyderlib/images/file/filenew.png0000664000000000000000000000045512626055322020061 0ustar rootrootPNG  IHDRJ~sIDAT8˕R0E$(CWfh)=` V=w,o?xu! \ۘp3'l CT1ͪ@A40Ad 3ȐF3N߫2 <Z[H;2Y{=X@ gVX쐹Փ@:rK4 <7N!w,1Z#pίskvt7c;x~U 2)O<IENDB`spyder-2.3.8/spyderlib/images/actions/0000755000000000000000000000000012626531443016440 5ustar rootrootspyder-2.3.8/spyderlib/images/actions/edit24.png0000664000000000000000000000126312626055322020242 0ustar rootrootPNG  IHDRש\PLTEĠssrQPQ@>?~|}eee FFFܰأߜ󳱱ߡб񰰳ċew5tRNS; o[<@CCCCCCCCF8cIDATMJA~]-'AD q.©sADb[}F $Vi34ww`iP+dws?TL:+pQu. kW )h)|l㔽QRfo#eTD24]zIe^?,HAԃ^6W_`"zԽ`6;$ҍ$aK,Ž-IENDB`spyder-2.3.8/spyderlib/images/actions/zoom_out.png0000664000000000000000000000136712626055322021027 0ustar rootrootPNG  IHDR(-SPLTEhhmEGH::=BBCeejcejWWX+-0((-@??bcgPRWB@B23;WXZ778PPUJKOEHSHO_222rxbj557sx}000--.psz222!g`]][f^lzzZ[[fb... k !l!h#k&m'n(mQ)nmop4tfwtt0qUyQsMOP]]_Kf"}j@mooYUXzz{:&ktRNS%9 이5Ў ɐĵ +b@ˮ ͰiIDAT* JbPXH.,6彔*ɭd!e00UUx$U*+~h*scx*.4nΪ2Z58-Í[ec^jsa^o0ycL1. /"/"1#cdaJUI(:'1Y,71l*KLMLKJHK9.Y) $ Y I \^ t,!/"R> rm-}&SB+x$/y)bRK=!UiZPA"iXI< `O6A9;42*(!"u h'PA" =1 RtRNS!♟ԄvY,8:IDATJa[N@ zQmhlh8Q(Zzƣ78r_↶ nxw?LJ.F识vvjX+Q%Xl+':+`p?E)kO'P‚ ~3d) +0!*1IENDB`spyder-2.3.8/spyderlib/images/actions/replace.png0000664000000000000000000000206712626055322020565 0ustar rootrootPNG  IHDRשPLTE  xxyB@AtsuGDE%%%z{{RRTMLMQQQXXYHHHIIIHGIFFG\[]KKMKKLKLMfghSSVTTWgfiZY[534NOP>=>GFIVVYZZ\PPRBCDﹹ\]^867/-.\Z\EDESSTpop;:;523UUWXXZppqOLPYR\plq+()CACdeg111YY[423][]TOU^VaWTYooo[[[0#1?EMUFLlblTTUcdfCCDijl[Y[>%AFLW!`T&ZB;BRRRK"NVoP'VkBBBYYYǂVVXYNY38RNC7CVVVA*BS[W+]rmfmAE|GanGu>9=<;<:7;?6@TNTXXX퉉d`dB7C?7@<9Dn4%CgLkL"A_a252 N o F4V@ iCPB>8,`cIENDB`spyder-2.3.8/spyderlib/images/actions/editcut.png0000664000000000000000000000160112626055322020604 0ustar rootrootPNG  IHDR(-SPLTE"!! 444*((ggg! &''%&&&$$021455-*+000aaa(%'CCCAAABBBDCC---&&&///111333 #"!HGG  GGGHHH433  (((/./___IHIMMMLKK***9::AAA=?@?>>666popxwgii&$$ig.,,0..311!!:9: %#$311%#$%$%   ԐlmnzW4tRNS_YA A kwM>yF5*tzzO׹ ZvW>?g,_sh1a}IDAT1J" 5+CsECIݢC44DCGlh 3`%C=Ɏ`&υtx7}> 1&3kZt<(W@ @ŒBgi'pS14J3I$p2$I \I$I.9N[(VASxIENDB`spyder-2.3.8/spyderlib/images/actions/arrow-continue.png0000664000000000000000000000236212626055322022124 0ustar rootrootPNG  IHDRIDAT8ˍkLWKbm >? aJq 9E&*rQT Ee(!2(E(@)*E,R[K[hBۗ #B9O?^Z. rS!8[3%ʘ%4C[Lr~kH5ֈ Mpq2>nwvetٌ-Բ3Uq~)DcL㹞̱&{"7aU1hCu1)2oĆ7k-  mUZ\M- dɪ𼣽 7Ui/UH@f!e*[R-sb%RD2\Yb:k}}k E>.zZ:+ȣB~j]IN#cprC uZ ӎ9:Cӫ_!BN%R,@;Z Xcw_R $n<_@"'tWnK!Xr Ϭvʉ2$uT"kIH\}Y=$C-: &sU3n;2"v?. &+]VDq],NV#MLu%Pޙ݈U!3&n F5Ѡ~"rӑ;86!+qrySsO48< |r%ΗyKo^Xܟ.®k}_F'.4!-~ꐼ^ۍڍ[++EPX*KΤ<-ѯ; mNMoSۋ_#?H*=HKTT>uNz æ1C8kta`*^\l pp/-T.iy4>LjDp x>[¡NPco'aţl;Il\}~XQS= =}"կ26`V(I3"c9Nղ?`ʕ;] DZ*dHgुΨ:= ϸZfm=](Dir2{>blgB*&iA\vc{s=(q쥏2q:ρw<{2ӫyrazIJUՌޱtuւUTcXۣYՄGMYf#o`כ<~73( w[XS)ե|dWdIENDB`spyder-2.3.8/spyderlib/images/actions/restore.png0000664000000000000000000000061412626055322020631 0ustar rootrootPNG  IHDR(-SPLTEUUOOJJGGAA8899::11%n%11 i ++!|!oz{_wjp U UURRmmMMiiGG~~AAvv\\::UUoobb33gg,,__??YY%%77{wخtRNS@@@Y!YÐM@SYwt{IDATm@!iJ-ʬ5`؅:Hk>$ƨx:q=?yBFGGJ@yC8m;7n:4f83i75j84f76h:;q>;l?:e>5f93f7-Y1b_QP3b86k95j94j87m;:q=9z:h^YR@yA1{1C~C0v1@@7v8 <1>y?7h:6h:8m<>lA;g?>jA:f>:f?;q>9x:2254:{;MMeakhRR_\r|ed^]ympg\[d`xk_XUtaR95E=[Vmiif`NN?SP[W_\eb='bRp`VD=/;.G7**(A:*)"@733+<8-F?tRNS#,,***##",***LL***,"",***LL**,##***,,#h 7x{IDATWcdFd ZD0 F$f F^6G >3uH?(oˈ 0011V^< ²$ 4``8( .jY WqIENDB`spyder-2.3.8/spyderlib/images/actions/synchronize.png0000664000000000000000000000107712626055322021525 0ustar rootrootPNG  IHDR(-S)PLTEv_ !)sR k%a#G@jjr>ɂc6!܏BAԆK#]ZSzDb5e5Bk7J%{g1=+_ jpFNuf{lӂfo0p%^dւlQ>,se p3qrofT?@&nv Iuk.`9U {(>=A<6tRNS ` PϿ`@ ￯ᅬ`ppP00@އIDATWMν Aq`Af,*ܫK0WP(sM/ޘBN}A^}2AݍHkY/_4]KݞvL7F` Cz:yas!,]$vtU ~(fPIENDB`spyder-2.3.8/spyderlib/images/actions/check.png0000664000000000000000000000172212626055322020224 0ustar rootrootPNG  IHDRשRPLTE } .(=6h ,% ]T *# OH *"QC  ( UU ) )!EI  -%FH}93 /(JRvE@:.KET r7-KF]s&y g v}s 2" {F6~ZJ$&#}o5''#($F8zrƿּtoұ׻qkD; sںohC9 D:߸oeD8 ȼpgI> "ulSG%.)إߺآڣ{q[O1":+.$[Q~uߥܗݗwcV=.F76+6(aUߔFBĄzj]H9RB=36'RD‖掓Lj~rfRC\LF33B \b-uk@j3A  _xcP0w9#c8VQH`6aX1&#ħ0GcLq7r 1C{cHq3z1ԯnj Ƣ~"*q+(;E9ąIENDB`spyder-2.3.8/spyderlib/images/actions/configure.png0000664000000000000000000000252712626055322021134 0ustar rootrootPNG  IHDRw=IDATHK Pu"0`O@Q$ZzR(dzb*(E Bf<(ZO<T2Ħ4E}޷n}Dco-#KK{ɞH&ՊtIЎlz<a LzN`mnnnS^^.ry'YB=zk@3n lΟh~B3ؓ#7'W^JOގ‚/y_)HV^*R?Ix9[2PeEvww u5ؒ)^NՃͨCIq~?#C׬@_DNS\V\ƛHٰw۴hnE}P 'aS͖ ؔs凋x\|rOlfhj޽&OWԸ:8hj-V +a(cׇz= ww_$g֝?7UPHz"Ua5F|+~b9Z׬FlU(Ns% 31Spۀ"Am::ⲗ~+i8Sk_1(t N9;^11hCʽ/]0{4z(> 1i:63Jl.3c+ > 8NlqaR۷1Y]BT(/w@gFУ (>9ޙ,qdUWvNr{]eʰs }^ڂ|=,/jdkΒ8ʐg쳷9ooo܀h"do0U =ŷ _=z8-kЄK$VF$`[^$]./+srő#ׇ͛M%ኰSvQλ}~.D߃xf8WڜmdT+]\P;f :kjݞ+cq5/PLQU ;R6n(W܅H\K`%~[b4[>%OOt1*xD͸1:Sl00a` U,N+g) LLMH=/y 7%"*%zI*4̄&JLYHŸ$R?H|LEA"H`$? g5TH[0&b ǯ̈́gDdDE" nn~IENDB`spyder-2.3.8/spyderlib/images/actions/1downarrow.png0000664000000000000000000000112312626055322021245 0ustar rootrootPNG  IHDR(-S\PLTE g e g e c ` ^ \ Y V SQMKJCv wBvvdRwoX]O f Y dH{f e]# h g Z l br+~&l u n iLY!$$#!|&(*+)7T!}w o n.I{sr';?ut"(H1wv+N#'J!s<>#TJX12tRNSê8EBdI@HGxtIDATcd@0" 20##8 Pik0;#'`[dw1> (00ޕBݡSw@r'vƫH \DyIB{IENDB`spyder-2.3.8/spyderlib/images/actions/folder_new.png0000664000000000000000000000135612626055322021276 0ustar rootrootPNG  IHDR(-SPLTE4c/XKqI֓҄~{|xe(%%ױu5$t v؜pj$4G*4?(/9rr8y6yr+y)xHzH7`72]1:_:.}+,Z*qqeeߚ.d0RHeYwynt{p{p{rcb☼ދ㌺)_,zo|qukxovm_\ㆲކߊ+Z-e 7'/+;695ie쇳߂H9g#{<. e$(]*%f&6p<ъA4tRNS#T=VUC53`At6%4G9:ggc/mymeR(IENDB`spyder-2.3.8/spyderlib/images/actions/tooloptions.png0000664000000000000000000000130712626055322021537 0ustar rootrootPNG  IHDR(-SPLTEë›áƪ̡ƪʢĪɡéɤƪ̥ƮУĭΦŭͨĮ̨Į̖몪íí’de>>‘XXXed>>’?>‘de??mit.tRNSlIDAT]jaw;DRYyVigbΠPZ`g2XDgV3F9QO󱛱xwX@E!_vmn+_*SzR*<1 x$?R+^b_s8M&WIENDB`spyder-2.3.8/spyderlib/images/actions/options_more.png0000664000000000000000000000143612626055322021666 0ustar rootrootPNG  IHDR(-SPLTEë›áƪ̡ƪʢĪɡéɤƪ̥ƮУĭΦŭͨĮ̨Į̖몪íí’de>>{xVTUySrr‘XXX8y6yred>>+y)xHzH7`72]1:_:.}+,Z*qqee.d0RHeYwynt{p{p{rcb’)_,zo|qukxovm_\‘de+Z-e 7'/+;695ieH9g#{<. e$(]*%f&6ps N<y IBy!F2T@^ /K Sij= Up{iaJbanz ta$_g KZwK_%Muvv mF  !2 ,8hp"#(DddF)%z¼ÿnhlfz{vkd84>;kfwoMHHC:4F>E@MF&"$"805.   ~`d w s        2(E5 72NGPH:3qRtRNS^s N[e|CX dAgTm Ȑ fש Ȏ9sS}7ܭQSl>RC|Oz:IDATNBQwFr@1BФ& NÇp/Oq ހ`3ٜ`1`p#6` Ʈ>(-vD=CqؗO%bC[LL $Qr#z%(H^΂J *B_m|'W-ə㚅-6 H&M- O{T4ޒe&IENDB`spyder-2.3.8/spyderlib/images/actions/zoom_in.png0000664000000000000000000000136712626055322020626 0ustar rootrootPNG  IHDR(-SPLTEhhmEGH::=BBCeejcejWWX+-0((-@??bcgPRWB@B23;WXZ778PPUJKOEHSHO_222rxbj557sx}000--.psz222!g`]][f^lzzZ[[fb... k !l!h#k&m'n(mQ)nmop4tfwtt0qUyQsMOP]]_Kfj@mooYXzz{4:RktRNS%9 이5Ў ɐĵ +b@ˮ ͰiIDATM=KBQň*rkQ?A A 5($Pq)D:#`a̜lȢ&/C]-؛Q{]d0 x>֌Ls'_>rhX'xiyul /*8ձצʬ0yMGReݕoX.)' IENDB`spyder-2.3.8/spyderlib/images/actions/delete.png0000664000000000000000000000126212626055322020410 0ustar rootrootPNG  IHDR(-SPLTE{{{zz{~~||~{|||||||}}~~~~zzzdf=:坘ݗ6/cfܫ2)nKDᚗߛG>U= śYZG5t\H@E=U@LPZ]5"ZGR? UYĂ&  ~߿<;3-)&66ֲ763+PUQV! -/ַ̻9:PTOS<>ټϊʅtRNSﳓ`IDAT=@{/Ht0g0[͞΋=hѠ9b PAQ (7鄠$Ti$5geH\]\zP9E@u8ye8sy P1Aԩ;DIENDB`spyder-2.3.8/spyderlib/images/actions/editcopy.png0000664000000000000000000000140012626055322020760 0ustar rootrootPNG  IHDR(-SPLTE;;:wwwvvv000eegPPP:::LLLDDEMMLTTUMMM***xxykkk墣NNM444ݡNNNǟRRRAA@&&&bbbfffUUUlllqrtLMNTUU@@A%%%?>?))*GGG677ǵGPtRNS: pU3 Þ|V3 ͖"~θuV72tN1 ݝdhIDAT1+"JQJ E1JR)bp% 0H既TsWU *g@U/,;UX3;*^O UƈyUѦSUmK&>hh o5:Kw\u4r >IENDB`spyder-2.3.8/spyderlib/images/actions/options_less.png0000664000000000000000000000136312626055322021671 0ustar rootrootPNG  IHDR(-SPLTEë›áƪ̡ƪʢĪɡéɤƪ̥ƮУĭΦŭͨĮ̨Į̖몪íí’de>>‘XXXed>>ssxUUe==e>>vfkW-(4,G=C;?7=510DCQ+*’k *":2?7A=i‘def))ef l k h e] `+- K.tRNSlIDAT]ϱ P FxSAꢈ 99vq(TC)|@P';kAre-PޒftzeY3?I(^#,13 R.KVY]oN"!]+ `-\zɵ9-tIENDB`spyder-2.3.8/spyderlib/images/actions/stop.png0000664000000000000000000000061112626055322020130 0ustar rootrootPNG  IHDR exPLTEAAAA..,,RRTTVVVVUUSSPPRR\\aa``PPTTNNVVEEHHFFKKSS99<<;;BBOO115577337788//JJ --FF""'';;##"",,##++++##""""""14tRNS˲*aIDATmQ@9HPkqn x MS|>u;:P*2VUo2=2*c/se)/~ '? ǙIENDB`spyder-2.3.8/spyderlib/images/actions/window_fullscreen.png0000664000000000000000000000033212626055322022674 0ustar rootrootPNG  IHDRY 'PLTEnnnwtRNS@faIDATc`@HAH Q9p`qGP+44 q\"'́q% '$ .t;̬_m: xILM\&omf vٷϩ!"֭Ԯ",+-ۭݹ=5/ OapvO D =Dl P , ~)})+-/ / 0 0 2 6 1tRNS[q,*xIDATMP@w+p"#0 QP"H(}wKp:`'p|GrǺXUG_3<=tRNS4&]IDATW}G0 DQ@z v@ ^>>  TO# O1)o^v\N:/ Uwrv """A<9ZZGEG1*1969  @jjjjjlbbdxu7KKMCBDKJKDDEܴLUVY/-0bce524zz|qru_`a^^`ٰ yo8RSVa_b$ !<<>888$!"fefSRTKIL<;>988?YMI1 IDAT=JQ15I:`k.Ah)i$(jc.,J( 8w&@rcI䙼.5>m<4)!D87.!5Vniݵxb:9䶮/T%7ׁ̐_$,_XIENDB`spyder-2.3.8/spyderlib/images/actions/copywop.png0000664000000000000000000000157512626055322020655 0ustar rootrootPNG  IHDR(-SCPLTE;;:wwwvvv000eegPPP:::LLLDDEMMLTTUMMM***MRXMsxxyvFVekkk圝NNM444ݡNNNǟRRRAA@&&&bbbfffUUUlllqrtLMNTUU@@A%%%?>?))*GGG677|Ԣ`ƭgȱoʲW]ڣ݀dMqstߑBuⲤWY䚳O~9lzޝmcYm}ᆟKy~ㅠ}tj煞pgǵ];RtRNS: pU3 6|V3 ͖"~θuV72tN1 r^IDAT1+pEQ`B7ܠ(%I(f60ٌʯa{ ՐYP|dӖxbhGZ&_M=h;9Usnh9Cv\@Ϝ^^K!mcIENDB`spyder-2.3.8/spyderlib/images/actions/eraser.png0000664000000000000000000000121012626055322020420 0ustar rootrootPNG  IHDR(-SJPLTE˽SS!%% I@=A?>6!<@@9䑑E??{{@@^^㎎||^^^^65뙑88훑[[22~~}} MM R:tRNS~ۂ|IH|F~DGEIHFMǃIDATAK`O]C0QNJ0 GWLB{B02,"7_tRNS5 yy9̃&&5&&3HHHHHU{IDATjQ{g]fJB; Ey0iDa}6Ix?}qyf.Ay7ܒߠ?'$G;:W?Rzr d Ɉ"ji/2IG}^5-,+-`u0rj]F4~Y,qIENDB`spyder-2.3.8/spyderlib/images/actions/next.png0000664000000000000000000000123412626055322020123 0ustar rootrootPNG  IHDR(-SwPLTEb3mZ;P=) L$, V/+^'!!""" >) h%t}869===20000/1+z#|%zp4"% #%!||}}ᐎRtRNSGQBCAXr1!-.{/w #'%`v=~CI1wsnIDATW]? AqPJ)f&IEƛWaWJϏtz_rtB}b@#g^uCGeG({^pt0xCFag䏼L`,"'nHH9IENDB`spyder-2.3.8/spyderlib/images/actions/lock_open.png0000664000000000000000000000046312626055322021121 0ustar rootrootPNG  IHDR7IDAT-N\Q}c P]DkЃfH&.B sB=<Bw&DH<3JӛffBHs»Qͤ+1hhشAx55ԕ54  2K5SZ:,/FSQ'&[Ox]*RCMh4{֌ғ?0Mh IENDB`spyder-2.3.8/spyderlib/images/actions/filter.png0000664000000000000000000000172612626055322020440 0ustar rootrootPNG  IHDR(-S[PLTE~abr ,)>>̀*+ xdtRNS4&IDAT]0E*_&jvMN)m?SJy. 8%\wZ: rE;`uqƊ{$ 2Rahcwrhγ5^~'+߾^ SLIENDB`spyder-2.3.8/spyderlib/images/actions/show.png0000664000000000000000000000026612626055322020131 0ustar rootrootPNG  IHDR7}IDAT(1` ^V,xscªҴO&cǟC*zR/ոCnT)-;2" 30pXm"PpDo TRgW77D%&r,IENDB`spyder-2.3.8/spyderlib/images/actions/collapse.png0000664000000000000000000000113212626055322020744 0ustar rootrootPNG  IHDR(-SgAMA7>PLTEthsgrfbY`XthqeodbY\TsgZRpeYRWPaX_WGB_VD@\TVOHCFBB?XQVOEAC@B?΁φncf\yqwò֞̃silbdZbYxpǍzs~͎їk`aYnj}yrmcИi__WƌÇ^Wmblaj`h^g]]U[SYRWPUNcZ]U[TQKPJNHLGJEd\OJ~yytwq{tMHwshc^ZnhKFNJb_uq[WoimgRLIE`\ZV5tRNS쮳񸸮IDAT AQsRL6LՂ~98BmCRQTA} l$ṟ Igx# JWLҘ?fXӊJK캭3>>ı TO#   :/\Nvo^1)O wrvU 9691*1GEGZZA<9"""  bbdjjljjj@DDEKJKCBDKKMxu7^^`_`aqruzz|524bce/-0UVYLܴOPRMMP98<<;>KILSRTfef$!"888<<>$ !a_bRSVyo8ٰ __atuvMMM,*+*()VR8?"djtRNS3TA !:YIF 4j!رJ; !BZ>ϱI˔M1~IDAT(Sѽ+dpRv6)`+eJ[$%dAbʟbP0ù$ yy$x,&?{p!:)s8&t+q~:/ܦk*޹".\Jr#YSPjCXonl`c&^e`VoڭFRhv$Td(vgqZ[11`{^a'*81{E}IENDB`spyder-2.3.8/spyderlib/images/actions/collapse_selection.png0000664000000000000000000000132412626055322023014 0ustar rootrootPNG  IHDR(-SgAMA7PLTEthsgrfbY`XthqeodbY\TsgZRpeRFYRWPM?b\RaX&_W GB_VD@\TVOHCFBB?XQVOEAC@B?΁φncf\yqwò֞̃silbdZbYxpǍzs~͎їk`aYnj}yrmcИi__WƌÇ^Wmblao`]WfY0Rhc^ZnhKFNJb_uq[WoimgRLIE`\ZV+7-tRNSBBə쮳񸸮쳒HIDATm.DQ@ѽ=QШF#*LWDPJ$V(X:<ĺSF}D$eMeGu"K}/F3¾NfuOO@ W4}Ya|{osvQ8fA ԓbsB/[z+:ZIENDB`spyder-2.3.8/spyderlib/images/actions/cmdprompt.png0000664000000000000000000000120212626055322021145 0ustar rootrootPNG  IHDR(-StPLTE񱳳ſ [[[IIIKKKHHHFFFCCCDDD222 aaa---<<<@@@===;;;999888...555+++,,,666777)))///>>>$$$&&&### BBB???EEEJJJXXXVVVccciiimmmpppqqqrrrnnndddԿ$tRNSCRcnsuwwusncSD}IDATJQ3&A  E[,DQf7ÿ֓>?E* ]E8}AHw ]8J{])7CfٶvMNX0$$|ZڶDA>ƓY#qvIENDB`spyder-2.3.8/spyderlib/images/actions/stop_debug.png0000664000000000000000000000176112626055322021305 0ustar rootrootPNG  IHDRW?PLTEggferrb`|^zZ}uX{mWTwgSQtbNq[MIgRHF_HBU>A?L4?~<{B+;{9x9$7t3332/.,+*)'&%%#,6t5q5p4q4p4pdbbc]^[}~\YvvwYU}mnopligedcrVRxfhie_TNHEB@?RTNufbVJECDCRPLtWHCBBAONH\9;::9FKED.//9GC8!#"+E@})B>x Ay;u>v:q@{./Y>eZdWӭKՑy"c1^x l77u%pL1}1t e\(_qᴝNVn8Z>z>Z!x%jIENDB`spyder-2.3.8/spyderlib/images/actions/imshow.png0000664000000000000000000000066012626055322020455 0ustar rootrootPNG  IHDR(-S&PLTE]dbdcefegfhbgkmlnomqrqksyxy{x}~~z}SGqqptcev:DLe`NPV@8+!^gosNSYXkE@V`8?U(2?7QvsMGP1"64#,tPg:1?bM]@C*chBxpx~TdqQQWAQXHjNU5OSBAtRNS@f8IDATWc` XPE! 3BIK (J@v  ېIENDB`spyder-2.3.8/spyderlib/images/actions/unmaximize.png0000664000000000000000000000177212626055322021342 0ustar rootrootPNG  IHDRשPLTEȻ2WHhwsssrpqrqd*QacBd(NE5W[xB1U&K;,OUq0RD<\=%HNk*L!D8X7W8W*L!CIf'AHd6AHd)'"!.N!CJf걺 3A^8Ѣۈ#%GLh䭶-N<ݿ壯))LNj㰸ٷߵ$H!E߾桮/0RQmⲻݷ㷿{ڒ堭6Ws嫶;=_@c9]:^;^=_=btRNS/IDAT(ca`ddd@gbXgaL0YxvC 02`8 D1 &̀&  Fx(ΈM.Q u:or[ { `:X""l׆J#-5c u7-Ag ݹ $ƹX=Xχ.q ,P+gdʨ+hDN>X{,I(IENDB`spyder-2.3.8/spyderlib/images/actions/exit.png0000664000000000000000000000157712626055322020130 0ustar rootrootPNG  IHDR(-SCPLTEVP\p7gp7hQ ]VW,Mtryw0PW'R-2/3'QY#Xw4v1WVDFCDV :U 8Q 9K 9>K9Ig fq=m7*s(- %*s!0p48,1!/o9|B]tssrAZ:}KB]bqbqC^L_PfX0"IAibngNF/" ;32) 祡ᐌ70矛 "魪2, up@9 /'.' ?9rnxtA:$#@9vr{B<ꢞ&&衞>7쨥rlrl餡 "2),$.!91_W^W4,%H:xC5tf=tRNS'ab'##;uL m=]0+Gh1J$I1Ii$ d-IENDB`spyder-2.3.8/spyderlib/images/actions/maximize.png0000664000000000000000000000205412626055322020771 0ustar rootrootPNG  IHDRw=IDAT_hey5m!)va༬aM.MA%?.."(4Z$FCDjAI,fNw}>ϯSphHͦ իW_cl߾]FF^N8p`?;pcK\"!LӀBDlxdpWw D`0YHC4aɢ291Yٔ}\a5WGIB*:@i)w8, fW5T94з0{MUq8&M FFe}NZeZH^`b2hSU2qN$(J{xsOqsT98.WycJ-#n4I<{sϩX##,č\W%knI,-*>xgT-ti2=_`xiZm&MS,CU >B ܷ aL^i4R4Mq&P[,|4Λ\.oyN6JԪ`<1Q c|_8 %bTj%%IXX.qbbqyhY$3bn@ 8%I<8:EƯc2:6 H`anM9|"+^Vhs0Sg\!Qd\ѱ z~mN\bˮ3<}R3ز O' mιh9ͦ9"B`Kqb8y;FHP@˹c d1AD8B'7WYJTc\Pƺy8D "Kݴ*vA2"2sN w! Xkr)¹2m"B`7Cb :2Y>b,.ZQ~ K/ytEuTMRAQnPp.5];Ͼzrq$rC2~ut! J){W&%w#!VM:>{wr!(C3}r+L$w*Ku#7@z'F2%N7:|ң=RtRNS% UNW@{x)F|,Mm:IDAT?*[(2(A()J$}pO)QD) @@f{l_`фU&ο.=ni쟁 X{Z嫚:cgmKjsN=Z߈RzN+U pRպwUj*UՖU"_S$IENDB`spyder-2.3.8/spyderlib/images/actions/1uparrow.png0000664000000000000000000000112712626055322020726 0ustar rootrootPNG  IHDR(-S_PLTE i n u/z*~rrm h, et cf ]W ~*Q)w<W!y`nAujntz B}Wm.iU !,FS'x#N+vw1H("tu?;'rs{IN$ n ow}(sR+)'&!|#$$?Pf2tRNSH@wI=qE8~QuIDATWu1 ePJY,_k7LJJ R 8'o|ĿH[!)vwuyEl"D"cyVM57S4W%?Ә&IENDB`spyder-2.3.8/spyderlib/images/actions/findf.png0000664000000000000000000000160112626055322020231 0ustar rootrootPNG  IHDR(-S4PLTE./0[[Z...###ǽतˤ̬zzy\\];;<,,-;;??)(*867  5/4443LKKʚ777+&,.&/"$"797/'0+&-эGGHPOPnooLKM;;<]]]gfg??@9:;224a`dtsu424,,,778;:=>:<=GIH***=;=99:=>>ZZ[hghHEG$"#344/20EGE>=?mgoa``$$&234>?Anoq523LLKOLO2.4-/AGDLZ?]))(--.89:CEGbdc_M`>DNWR*WX"]nLL,Q+&+LNL?@@FBFH)MEƀMK0McM^=aI-Ls?|ggJmǎ TtRNSu tRNS񸳮쳸`XIDATJA@{gݙ ;>M!|W[HiEV@FE[ XD@>BW)ol@CB'0xt8z͚B[.Dk%k $jX[MY:IENDB`spyder-2.3.8/spyderlib/images/actions/previous.png0000664000000000000000000000122612626055322021022 0ustar rootrootPNG  IHDR(-StPLTE3mbP;Z L)= V,$^+/ h)> """!!'t%}9?<===968+z1/0002>#|%zp4%"# !%}|}|QtRNSGQBCA¼U!׌1-{χ.wȁ/v`%'$#~=CI1XIDATu;AQ@ѽϹI$YhT~Õh$3UZ? B'WCR`ޤ*0rIUSsb5uB/v`ڷSL̥zbm:<@4I)*hTo!C!IENDB`spyder-2.3.8/spyderlib/images/actions/up.png0000664000000000000000000000157512626055322017601 0ustar rootrootPNG  IHDR(-SPLTE*V _BW`m v u s l&BU[.1)#& ^?M0'"&*U Z5'")R P#(M&#c5, *.Rc6 #e7+(lI` #a/(%]cV1!Y '$' JY`s xfO> #D(J( ^O% Y^% Ya [_ _b3z< e-Cc1E e *L h %XOi%*y()("m2P\." `ȁ' Zm% Tb3jptRNSOIF?a9OٹrefwIDATmNBa|I:IGRQ.kBQ6وTIJd$sL>xrnm@SKk;һ3l#:Y{㦠ۋh\_uͺ-x-ZH)Kl YI%AijI.z%hρPa1yPfdW3[IENDB`spyder-2.3.8/spyderlib/images/actions/edit_remove.png0000664000000000000000000000066012626055322021451 0ustar rootrootPNG  IHDR(-S PLTEu w}|zx|wmjmqsrth k pqt x ~ x m t usrqn[ ;,WA[EP>H7@52,,(/*1+2-4/60/'h o 1#SA`QfT^OQIBA=;=;@ACBJFPH=0p h     ~ r y  )66n z %"*/%'$&$&|^ $:``gᒐbCR!,1CE -j*(P'RT 2czIENDB`spyder-2.3.8/spyderlib/images/actions/arrow-step-in.png0000664000000000000000000000120412626055322021651 0ustar rootrootPNG  IHDRשPLTEa|^Z߁o|VzgxRԉjWW]mNwEDfGdC_ޱ=|T5qnFjZNS:y=)c5)`2n.#tRNSP]G .C (Y=F7?  IDAT1NUQе;bbei8^- !LjuԤA`R#E(@`fbgv5Q7Fw.4ߢTN=1ś>PWI"gG(xcC{@{w]}l,ǏwWu>$I$قda}I[lIvhI:IA' $jn~$/__@&`IENDB`spyder-2.3.8/spyderlib/images/actions/editpaste.png0000664000000000000000000000165412626055322021135 0ustar rootrootPNG  IHDR(-SPLTEtLk8s[?=89:=hefgbmGjf\wPuHl;tIǒg2]+]/\`0\^-yQ[*^G,26?889:::Z(򚚛Y'X&W%~V$~U"}T!}T!vLxP|S |R |R uI|lPSNBz|z{{aaaٰ{aVخźŹŹ˿Ƚy]|Lѩe}LzGjCd@b;a=kHkCͣ{•g}{ysr͝tvxD]꛾șp_8Q@O[YlR{TE;UragQ}I`{h~\azFl[# !~j\vEm:b-s`ZtCsBf.xZvPh=eu[b!w 0'Y1;@>6~?xW,4m'IENDB`spyder-2.3.8/spyderlib/images/actions/2uparrow.png0000664000000000000000000000151512626055322020730 0ustar rootrootPNG  IHDR(-S PLTE do q,y ~ozo lo j g b a!} `qx` rW ifm9 TIxAmn|#{=)l `UW]euw%x \U [Av1x8kfhkmnporuy}#[:|7N%2F'z@7#uK*tw$L+ru}3C(!rw{>:MV!#w%&W{)J !~2Cr$qrz>*|TXVTQLLMOTuG|nϻRtRNSmM,|F)x{@WNU % 9IDAT=*&A2d0I % ,Š(h P򞪊RUw~RՠuTZGup^&lo&m3+-Kޅ1plX%غZ4qd}zx "-m-IENDB`spyder-2.3.8/spyderlib/images/actions/find.png0000664000000000000000000000212012626055322020060 0ustar rootrootPNG  IHDRw=IDATH_L[u[hӖRm]+n--P(MtrSʟ gCP'n,enŠLԄ`\osfYt1?x؃3SLLMN~=9s.#@J|'Jc3Emԧ rd9[x3++ㆰ`w|>Me68Cww_G9_ n0 s ELSfj,E˽h"?6 c8~Y+(7F r9~4щP_S#;^`aqjB`=hmiC$VMӰ&H6h!U8sţF~ċR~u%h z,-`ށ422Ue=#Yub[sG -`D>B+)FP# a$CtCbv}+vs\)/*tGp *zSPA$:E4R{X"%eTRp5~ xB  }iqxWt+p:0D2읮=O|QQ%w"\Èq~U5 22B8 O#$aQTle;sNTj0*n:Խq$ uI_Ƶ`} CȒJ u>=0^;Wa]aer)))!kX,N%]OMMՒ)ks-#7-ؾCf s/D* XJ?] O:eN30M[.:tf$KjFmUJ+U-Z}G$]$EBzzJo%M233sM(.;4L"IENDB`spyder-2.3.8/spyderlib/images/actions/redo.png0000664000000000000000000000167112626055322020103 0ustar rootrootPNG  IHDR(-S.PLTE:.SBN>C0B1B0B0D2RBM=K;UDTCTCUD>J:aQ_O_OaQhXH8 9-K;H8i]eVfXI9F6g\`RdX_SgZaSF6aTh\cU[ B/  nB    {aK9z8)]K O?n_ 9 K=kZc6)?2^L.(  }L:wg!t+%2-4/0+${@ U  E/< F L S Z ] ^ ] [ V 2/0;08/+/"n_4(o ]E(v9, L 0 A/L<QtRNS0PP7ē??KMqоMigIDATU.Ca|=o.vaNtXl6 nC&Qhi,&&#x^:d%gr_;o@).U4d h,lbgU}lyeEƓt|P=92v*mhwﶨTB>n_IENDB`spyder-2.3.8/spyderlib/images/actions/special_paste.png0000664000000000000000000000127312626055322021764 0ustar rootrootPNG  IHDR(-StPLTECe6 wJ"wI tFV0tDrCsDj;W&e6ƧW&e6s;e6+e5'((555444334d5333e5d5[+ j;k<j;h8 x`344e\@jjmgghhhi\\\ʳʸĦzwuywtھ–j_-׻hY]׷e1oӴв74ͯS{Iˬ|JɨƦsDq@ám@j=_/}nzC6tRNSeB¿įY4zzzz`zWĸ)0\IDATJE{gތ6m*Zm?#EEh-JF]i'L{/.> e?][\Y\?.R6Fvɼ$h;|P[{!AXhT#%x !@HB@8f@R\.o!H0`Op6Z-PIENDB`spyder-2.3.8/spyderlib/images/actions/rename.png0000664000000000000000000000067712626055322020426 0ustar rootrootPNG  IHDR(-SPLTEfff+f8tRNS4h}IDAT 07in>O"HqJ"5KZMF Ԙ1(1( ~\굺)%O)A+LE1A/q1vCw8) =_t)Tv}EQUxIENDB`spyder-2.3.8/spyderlib/images/actions/reload.png0000664000000000000000000000151412626055322020414 0ustar rootrootPNG  IHDR(-SPLTEf0+%>4nd!Hsvxzzz{y8$]:{ zRzmgabZLGJ;"pr6g3 b!?apgM(o5&fy|||{A?&l+2}&^[rj| '$!0` a!a!^ f" qeiVEhW{murhWRPA h_#byOc);76/4)1$-&} bI"g(f"^__]N9c) c!S!BBG82G``J( ؈'jr{wqw4OIWH 6(9):G:7626.8-7(4#+ oAE97@Bw~wtRNS6*e*/ި. ˾jVq꾄8]꺺5%"۠!-yRIDAT*d60PlvW,bSB)Pf)\Y}JUU )- }%cw<S+G&Z=-=50Mip=[f70؎WS.RuyU?ϵ"OIENDB`spyder-2.3.8/spyderlib/images/actions/browse_tab.png0000664000000000000000000000137212626055322021277 0ustar rootrootPNG  IHDR(-SPLTE666111001000..0...333 lllggi iiieeg YYZrssSSSOOOꜜ晙㘘ίܬctRNS.8ECppppDD &TDIDAT1@w%4j:ԛɑ0r>cL"By= ĎљH RSYCc[EZBdN`X,YV4$6.ӠY9ok@R ү7 XOIENDB`spyder-2.3.8/spyderlib/images/actions/edit.png0000664000000000000000000000122112626055322020066 0ustar rootrootPNG  IHDR(-S\PLTEĠssrQPQ@>?~|}eee FFFܰأߜ󳱱ߡб񰰳ċ5tRNS; o[<@CCCCCCCCF8cIDATMJAE{_W}-$! Sw!n؍8ԉADiATT 9F) 1-@BR8VۘsFFB] sRAߧUog@Vu^)zXrF֮z\~9nW>s]_KO>C맷?',+BIENDB`spyder-2.3.8/spyderlib/images/actions/window_nofullscreen.png0000664000000000000000000000116112626055322023232 0ustar rootrootPNG  IHDRשePLTEnnn͍ukˆ̋yɀukpeUNslunƉђ{ӛ̃ukrfWPYRsmnj}uleԔ~ђ֝sgZRŊ~~vjbqe֞ءth\TɎȍg_rf^V`XbYd[f\RLTNVOXQZSA>B?C@EAFB[T}\TB?snqm`Yslzs_VD@pld^^Zc\tnsl_WaXEALGc_yua]qjNjcZGCSOgbd_{„nj[UEtRNS@fIDAT(ϵ=N@FߋkkQ܀:"ŀLRػJU3zI M;]S+rnQ c .qY]-K+;$VWohӚ<`%0~\A?f Nu<04̡cl_$LI<6zJ|M] xIENDB`spyder-2.3.8/spyderlib/images/actions/auto_reload.png0000664000000000000000000000133712626055322021447 0ustar rootrootPNG  IHDR(-SPLTEf0+%>4nd!Hsvxzzz{y8$]:{ zRzmgabZLGJ;"pr6g3 bo5&f?&lxYkm ^[rj| qei h_#byOc)&} bI"g(f"^__]N9c) c!S!BBG82w r m ؈'jr{#awqw%Su&J o'B"h):WH*49):G:7%S+2oAE97'D(>)9*5*1@BELtRNS6*e*/. jVq8]5%"۠!-AmDIDATEJa=9Nt P465(5ynAS/<3 D vޥKrv"vVIݎpGv Zd8HʤZJR&XG7Hn&4v~}{AS=W!µG#1IENDB`spyder-2.3.8/spyderlib/images/actions/expand_selection.png0000664000000000000000000000134312626055322022472 0ustar rootrootPNG  IHDR(-SgAMA7PLTEd[ncdZbYk`RFaYodmci_M?_WYRlao`]R&B`Ob\R_WSv?NHLG&JEHD= ,WPUNIETNthsgrfqef\d[bY`X^Vء֞g_ȍɎ\T֝ђ~~v~ŊZRrfuk̃ӛ{}unjsmYRWPpeɀyunslUNfYjs݂Q2t݃TLރz,cZsl_M3KgbGCaX_W\Dyuc_LGEA_Vzssl^Zd^plD@\T}[TFBqmsnB?ZSXQVOTNRLEAC@B?A>'.tRNSBBɳƄʱIDATe1NA*ce,m%DCfVJ?E;wZ }:LGhd.ZH6-[.ԉ=ZDH |eB"?7T?ɳ?ՕzoxTGv<$Jhv%<^j a KD[IENDB`spyder-2.3.8/spyderlib/images/actions/arrow-step-out.png0000664000000000000000000000063212626055322022056 0ustar rootrootPNG  IHDRשPLTEzܹRVڻZ߼z^arfuq}hrpvNmW߃GSD=|OHd5qnFjZOS:y=)c5)`2nv "tRNSv8IDAT(ϭ]@[ Bv&G(чB {ל=0~cZD %3AWN&HH|lá% GB#5vH:Ik4;d)Kx:,)V(gɒ_u?yuyIENDB`spyder-2.3.8/spyderlib/images/options.svg0000664000000000000000000001113212566665770017232 0ustar rootroot image/svg+xml spyder-2.3.8/spyderlib/images/set_workdir.png0000664000000000000000000000130212626055324020037 0ustar rootrootPNG  IHDRa~ePLTEC^Lg3N!;(CZZZ[[[I}@pMs@yAkDfIf?rAlCgEc}Ec|ɢ?|>t_~PfEd~Ge}a\G=u@o[v`{Fd}[OPLg송QkRG@(C5POj,BY`VK@9:ACYeuUaZODe\Q?]HId*tRNS% !)rv8P; (#PBIDAT*$fN M'}};8*'G)iR{bk`/݇B9F;U#RAb< 4^atPjV:M[TUYeq%fろ^~Uu^<:]}"ϪߓGLka6!gK3%ZIENDB`spyder-2.3.8/spyderlib/images/bold.png0000664000000000000000000000017312626055322016426 0ustar rootrootPNG  IHDR7BIDAT(c`L@?&V i;L4)5``fϊ 000MA9IENDB`spyder-2.3.8/spyderlib/images/splash.png0000664000000000000000000050420212626055324017004 0ustar rootrootPNG  IHDRIIDATIg1\k}{WIvGHR$tH ТKDhPB!D\W9g[s}n\87s]wGuğOO?'߹]q]q>>}_~=_p\u:}  O "<% ""?Lan -nHk l n% P F$5 +((d($#Ȁ&KFD`0p LF!;DR,3w7 iC(]o[uE4L\Ӕ+ .)B0Db@PIThOѺi=mӗ]݋XFvl[u|W_oo7_VODSd1o_}8}dSb< ,@Lʀ뗝>q͹qฏwv㘗1ssF8'{뺺kkO:<|<<ήsb/Wvye$O`$CBʓ"*ӘHApAF'"T(Ѐx AB)LT>)HȈɓ yx܎v"\ 8|GHp@T@1ApEe Q''?̌#2*O#42b2 $@c*'4Rڮmwqn-ڽ%F 0a?10Ê2O"h&OF!?  H=ڽsGm{r]Ұqpze@Q,ƐJn-#"8 0h2>ʧa)J> IP@? O"" Ls;nv'l{2b A6 I'@GB|,PpqPm@gO 6Θ #2iR OʓXh"!+tXfq;ZFyRSdP̙ԀTt D'T"@(B4 0c}nyy]e)1:)  p\%I ] w]!^^߾y~.l|r uDP`0ED ۗ<y}lg(0BiafnDZ!3l˗#FxH0| Kx' (o߾}m=8ǹ{^mq zOǙ9pch<<ʟ_:o\{u׵׵ӹv^F{IKkl:bPH1 ?"ʤ"O3O@2*ObF  h"%?DE!@REA'/ DEAçIS'TT45uT3caA4˂@R0=Yf) J! e{=QX8 "׈3Eq=h@$0@A\yg__^ x||q=:/6(DaO*D*̀**1뷗qV*(E* ?ȓzn੖F1dtcRD\0 U'4( O/~zim$ŧPHce"D|+@qsZNEJe4pB 'x7RV%Hd 񈂙mPRqA@dg \HlF'%J(5v f-qq_/u-"b(NAA!l(%k ˛{\kbi@lလ9|{{9ϓk%PYXH BDxƿ?/~]B&< Έ!Ƕ3vm죏:٨% "$A+ڐˁٲsP/vaX adrh F6կo?Ώw7$PjRv>fq<0FGZ8v^m׶:0 q7n|,qWWDj A cG,f "ъ2T.((( ħ"Ph0zx @! !B@1Ljx=' BOӂ0J@ 凁(&)$ D@q,@E0a WAQ<+ D1'H!c&R$e; R6`kQZbun6v.S"%J4TpaTL|~>{=D 2chGD͗/j= HI`'B#|_?v¿_ e#i\(=TBqq۫d!#$MYFAJD8~;Z+ˈeڗiӞeuT@e=vox=n<|տo?>Nض@-#\mGgfP`peY~yu-nqZ3tő5sWw F\\n@5B6E d("Q(j0AJj((O XVH,0l$B+~'P 6cn[ҵȧx53@!T&'bF@|@A('(=t5Ob*ej0J"0<9 :."55 + I0#;J$\ntt,W$?kP J%ELc87ozAClXz,63.c1L^ P  D@(C@ " BL|a *2|~Wk݋$DX!(""2 #A˅CI> ! @$$e4YDFyT"חˣ:#2A%B@Bxݏ_zu^XB|2~'"3vP/0od;"P1Š0r\<_.XJ@FHZMFXwҩl"su>//+!51H 9Ombo}.{m "aPb!E$"O>v;^/s_3!l]pǵԵ\͆{qn`56pMډ2:5!@7䀒!a#2+CZR`?TDbO Ҍ18X^(O_5umIڈ0|de MH!`0ɧ5h %$Ihqtx  'CQb  fI B'ԋ+YSR,rRPdO+9 ,t^^1rnq~XQRFh*OF9HPĀ2'eCXRu|\{my.&nͬK@_}|]מdjHP58wϿ|ӿ3ò@JaGl$ 8v;u]{J(Dɓ #$_qg Oyn3 e|{{E6oxx׷/^_ں=ǞURT, 2+D#%0qtGz}wn쀏}xɃFr'~^v-{m$WDz\v\[P#M!pp̴8'ѹ$*A4+FO+Z|Je7(F H6sv;ccr_޿<zA@y'ĘRADL$C4It%SG5dt9OR:dj(d@N8ΘXfh@a,?aĎPBI!+\#[P^Xmܸq>$ITP$$"@E%e k16ؽ"2 __y!C%bTbƓFif6/ynw "Óed z;6G:9w+6" Dg(gkLE UO ,b͙:kC?$_?qn:|9??vnOEW dC0xLa-EJ=|/s8193CnwcȆfwn\{\lE'-Ey-D)m9 G ]*5ӱl$6\u۱m$O J*h(OC6HD!#csps{~_n/oOoo_^.>v/B_vx89{]\WuՊ4f@!L'pAp0q Rp*Fc(jPZD &)P+5axe" pb\.F@S,4Ԧ+!$[P"q^׃mv9l\L YxZ-$ ~?^_^q>vH!EZP޼uuu!RBI$BY1\ѽv!x<7xn{4< y^{m׶@0NO;ȓ+n Ds8>~y?u=ȅ $Ry \hʅ-`,"T 5Ӣ3\$[ "ºbq|~}88e<ʑh"XwPcx{}>ڏjx݈&D嵼ǵ۹|#@ P~"Աp(q///oM`!d cq]y†FHDcqd`Ix|\㱡Lk1 pц]lO:5Md ) BS-4J&{CV Ӵ^z s\Ί`.-]^e^^\]\f13qcղQK$Wfsw Yg8 D~ -5*> Hx\kr 313:$H <6R1S҈l  M,$-@O f nd0 j-S`XDP`@mvcP"꺨Xߏo/ "@)S# E?=a3_^_z']׵ O!ӄJJun=:- P(D  !z{{}|ڒĭ{oo_w~BTDpv zmtZZ%"%#w>R<p0hqOQUq pv)}Ne<9+Cdڱ œCWB(E|2 1 ko-,;-lN5Ԯ0-pbn.^ut|lir]cM%tZu;%0&jh$k8rsL,RF `)! %KA˜n7oǗ^|}e~i9[_}u^l- B8zt}<:|\jqcqVt hmϳڮ  J,Q*PbeļZ@-P@!DRʧA'fl$Cj L,!Ja`Yva@,ei$6PvE*Zj^4//uH$b:n1cNcy]{nH#,"x=ukkL?!5|br~u۶!Ktz?+9>>W׶.4B`M O0C6氼ܾ~MϏ8* dK ^˗ׯ?xZL!__\kwuSb(ɂd4vvx9^/o7om$Y>ՆXb*qKrחk"D3#=8r% AXוxRTߎvo>ow6׽kv#ܹf3 Ynp=rӽ39cҠ!? 䄌p -&(4M`b:5(VA]nDpBMF;o/y-ҶT*BD3<Ƕ;;n}y|\{" 1sK " ( uM(=9S.$uPv}@<"8t6pab%|H8n//oq?3a'rV$e.`M\?^uxuzIŵS;aqaT^h\8k{yRpp Xyd`6AcGGfE< ey_}˷߾}yvzv98d|B84)beۅv^zyǞ=%*\vi=! ljpj=ǵ^<:k-HkZmXM0i p@[Ć.An kÌ-C+j=n 䒲FDK't^׮@-`I)a78nqZK&#&Ź}y}{:by*2@!pDyz~쵏u^J0.`fd|*9޾|xxs* )$7/oI  D@)F&hc *u͠X/ߎn?G-Ab*`JH4MuugLDIKFy +Ƣ3p.t"vv9ko~y|T8q^f^NL 8haj9{=tOb22!r O{1P;OphS]tD&3-ǰѝy[p  tLLlƧ"Bnw^_W_~}{_o_^not~qc6 * b[]vݽ<>=k:.BA`.΢musewݫe%kYkq{um]׵$BT.]̚ .'-"%H(20d]: $BESkAB`ŧ}VTl< ,6p^-1kϮ%)FLq^//]x\=1!O<޾|q<gu:,R'!rv{yyk>"6%$>eǿ_g;sf'V y1E͐[+q^oo_GF`"fbn~y{{{<>),,-X\:k?n4#[@ 6-H 8혹ro??okh*ř!8r̜έјYS:2?(s23:u][{vz]^{nvuK` uFuu]{]AK mm^^]^׵vvˊڶZvvkbwݖ=kl6bH($*ĢGK&VVJT$VF4,-5Zq?丮@> (O6x#F6ɼE GJ>C۵^JHE IOB++_^^:~(kk 'J Xyy}_z\{uuBP+Ҡ/Og7ۏA$ OCQ/_^M8~q~]2Ih$& &!:ׯ|cQ'bu&zr>$a$!(V&D="cn\^xÿy]Vb V<`&W$KN7~N8umaݥG {кA B1sLS)"вco~<Ώ.3ڥAdSh%Z@"_cxu]EHEPD4o/~>:$"6\€1G 8+?w{?ff,@&8JKr`Mo?}S^~=?}%"SLOQ3x<2iq{\grm34rnsv?:c]N .j܎x$$(ңm]YYi .6?5fK,`y#v}iZvg'H2Ĝa6 XDhˈvɖ뼺2f`9_۟Sկ˯^|˷~;n^=c9y8P.P]ўͱ{]{-=,DTL]mF]u{EDlW]յuy½OJײ{uuOU]>u=w/\캻f%OFPʧ"IR"(l2OP*m F,O;sm]WWm%FK<92::!6ګ0mܝq?^[e9mthVOM[jq&Lz'gu9b6cL,mT`1I1_޾,x\{'k&#&,P;1`r_KWO (E23~}߯PH1A(IDnq}쵄TI%b umoo~˷o~t~|߿qu]y}\{"awqh!hš>y}\Hn-OB,8V%?viXکIzU "-5 ab [a.dq[ґ&؏mO>q6;>j27^^n~/?}߾|˷/_^^^o~ߞ61mfn1p03( GǵO^{miOlD۵];^n]tE-m=mweEIkZbq-yO,u^W{<;<p.'zm#V1`ړ@P@P|u^>|xyՇ.vpܮr\rr:K:*j&!!{ZЙ5kl\950u![=0,."{{:Z{޳ (M(!8\Qsf)8g:yv4uF`$ Q"` hp'p|aFIFY\a ZI iZ,rYQ=yJx 1 !A<޾;zl4Hjjӽc:JM@վ{?vtƧImcXDX4}CϙA2%! $ň8^^cVc~ͷ߾}?GW9{0D r{yp,/Oo~8>DBhxbR}?c D$D"HV0H j& Ϳw|_*55ub!NH5ʄɎ$DC $$ !!BB4afx*?v]ڴf5$y8 %P#Nxrg%Ms'+R!&u5R4IIzku=j){s?>1aiXbuYk]/^^_^_^^>.c]/r^oz\˱cGֱVc]Jە<$"$ VfgY5k*:c'FQ3k+39>Q={83 ;ltesǬ5k=x>z?H ^# &t$dI &&TJzYxK4`Mxr)\z^o??ӟTPX%P<i{=]iѨ'򮙁$d"d@G8|<{@bȄɀ!$(]/ڏ8 /F㧿O3$@vˑqONGXd0!.Aɓd'Mpr9_~y{>PH@A멳[>~=c 6 `EbR `11a21R!Dޥ:ʔbDDzrֈw3Gf9Ӥs~B@JVIM O36dX$Дv[/XװI y:9=FV:kZZ:.r\zZq\qbu|Үڮ6iS0CQg'̀#Ó(ΌʬA>ݎ[]uftlttH Maf|bg|܏yxc }x#y{:S!(L- JTЉlpE(DJiLap.q %|aZ $!8gO:qqt|<.ywz.3F`+R9t`PJ&h!3aG|10ΐav4T6 I j BK/rivc ! 4V dooyŅ^^^y>մ˵\gsσ)#%Yi2Y!`F //}y}mIA'L0Z;X1MSXv>=@K%~O~o~3 81&4T/B԰LX~9^>c7Vܑĵ*~13̞L%  O3u^ccq]v\z{g hf+ t "N9SOgӽ9Lf˺5csFBB* $LKzIHblhAZ$A٥$ԘEfi뚷󼟏1k  l0 JHP~yܛ-Ct~wۧ9θ3h&d1 Ak_: n [F&B@/JIB$HxP!("J|]Bk&H)+^.חq=ed15 h!]\MXkGePFqcȑc8c+у5XOXz:VZb5_6% i! 4!ITDA4]u"TG+3IXTfF֚;4>[10B ƽDqlu1ε׾gX=\gog5(`Lc[q2̸wؙq<}da"PZXIHi Z"m 0%mAHe-& ]QvrOe]ޜkEYTdA&Zg>x;كt.!D *[u^u=3$ bJ1"I11O?ӿ_}ݷL$!PCBwc߿oGSH ;bFIpgx2LInחx!Q(_0d5Xv}=rpΞ|tq10$00OWM`02@DPX0n4LHI3-Fyihֱu]/YGZ$Y')JIծGc\[)r\rc].hWUIt=ڮzt=uKڬ&I橋&yL T#qzݾ6tXdSA%$J“ZSƁ*[0~1 #NH:LQvfb405c Q 1!PV|q9[vCj^f9K֥z>Xc@܀舡# s1crq.>/zyǃ}Q 1F$cv0r<CbAqHm SHƄ HpZcj )uZ//_MpOB OlxyD@4  D!IVgB_?9=$DŽ.;k^~z=>~3k4Pd2ʓMBh$^>zDfLH0#$F'Nl{d,0"8dBĤ@5D@&3NLJmi&mt:zYs<\cǑIp`JVQƙdvp { yլ_8Lb! 0Il;[fGDCkHݝYr LihnhxjІ&$Y)ڤGZG#ikئ]khYZ]jڦMH&+IӴ+kIڴMJ!@!$@ O@ Fyʂ)1B|W :ȓ-K3tΓ*H$4 0A;sP{&VǬ;x9fv T&٢uw2>F=i8٣ugosfD7`FCv&5Fme]=v@))͐!I4C BW{\2XaH4v%Qyh_.7zHp(J`X/Wq|;}8 Ęi008 PM!6/׺>s븓 Р|Q@& ) duݾzi6{`&mn_}ljmz}x˹>l! $8 I.4 A溮B?} I i8$ ?iW{/\CL%!MA% MH `D2I4#)6!HL<~ݟ'|\/k] }>{{fN8B6$ )IF A '%cP"lgX& 'H!II I4]}4%yڬu`uk]O]kum4+m4Ml4)%E b <.B"Sb@`IBePŸCZQT:1B$B1' <ʓt"YPIIfL8G۵enfK љ 3!1>e:l9VM.qgf{gFw䱝dvb+N>\oo?> M $i»|1ڄm`]3cN#`xBD{z\9.>gIIJnn<Vq"IJ!dbr=ne~2b LĀQhQa]I&BI4ab4Q5!: dr}lK 5K kRNԿ_Ϳmm{#$m ĕNH6q4H١돺^ooyӌ5 "5&ޏaSsr3D;@I8!Bf0@I3Z0dP4xEV -@p0ʎ!^.GWw0Ck=j(66H,GLL06 d]/v;޷xɟ@M22w#H#ĊÓ&RSe,&Q6[OF,F;a)N wDdVڐw 4IIC_$!da A $H2@xN&Q "_ ThF(%QHJL`a08"If CXJpSA@.G4B!2Q&6vBt$[JT99ݞ=roL̒ct6'<ʞiOs~ǝGW\00B! %A"u= q &5=l J1'./ׯ9ϟ?7p YA 6I4@IP%N~Ζ=DG"RI0B$LIחkcI46iɰ&ov]G>1|䠉D2uoκ>T.oO>}w=6fE.ə6{&(2ʠ^}?LBp$@l?x' ' !])qTif&Y=zr\/㸬6~|/t 1A~kT _|q4΄@Iha4X,c& I*On96yp@$fP'(&B<!! SyDHb5'F8sP&0 bDA B#S)!h%@HN 6 Æ4# O OQIФ O!'=I<),W;.+%ъcB5.c  l=!8aT&N43`Éޝ=S8=luas:ev=|wo?qP4aM CDH dWXp&ԑd1  {0gh:p)9a]3;#X.f N"Ì$+I^/>qf64e6t`4 0JĮCs)Fɴ,Sk%A]io?{jӤ!1Xd5;L!Ǫ?v|,L@s@lƞibXOoy ! Kν_i'L`x HJF M8.8֥Hp<緷Ώs؎}}A$'D0s///YEc0զ]qh\C~y=L@|?yW (Z8;UʓMi ic$!w6'BdaX"@]QAQ!E3!6)ADt@x FOgMV O Ѩ )wtLCHNxʻBHA"Jp@03" (1I !BIt4Hq O.0iz NZ#Hp PB"5 D5 PfmzlMHt4ڝl6 ⊸}k $ L!C`T]nIg2D TA“HȩaI0d Ne=]_;~os@hDjJ.LJܽ[7&D'i34 HMxyyvz&:44 ֱN7d_TZ5dY"3!W񕛷Ͽ~~E 6"&À@)R@ )>?~63w (O5&qaGD˭3LBa c& V ^.y9$ӉM!&!6MC.׶3Єޏ~wn~G'O4 D@[be]霷 CAǀ )4" I|x^/o>}7aKmTH 0 "B?[Wҫ<y~v}a;Hj"O I(8cV8 OS$@%A #`Qy2& % 1`Rd$$&1`&i9LD3+d$doqQab l2aN'cM) 1̞4 @L[ $fN|D4 Ȼ D&bH@)`4|KgdoNq ĥN3&ơAR; |jUbD (:1&] */( 1ᩆ1 <)0ʡ{Tݮg{s̹cM.%Q 868N vg "USvZs9>*;p]K[[d"6maF&v d03MqBamU  Hax_/$TH$<$(D $H𠄇!w1VPA@!`w4+ '1„=C C"d,M/5 qoCcu`8%L O#mߒSf:,&afaŢox=fԊZ.34+]oE*! 'P %z5vu9}[P($u\.|;&@ȐSۙa6yHq}w/ {oۻё$<L1=7q}{2Ba?ˍs&d1t>)oy?w]ZjVf8.ԍNb@I!f]><}{zrd*Ɉ o=_o?`IdP!0ʄKC@n'! ,-|u b1e'f2!1$3<19%Z>{S l'4SQC$ӚA:ĀCpe 8EL0f8$,i2@By H<%D   bq8F U@p $I@C"AB0aU5d 3LA5fHXْ 9r>Ioe][GF;NLvf(hYӮ:Ƿޟ^93;&&4 qq1j(SX\vﵵL"1°n|RHՁ@EA AG {C޹g.EO0e>֊d+0( lޒHX!~9m5 aygƹ^y[`X CBaF}v|<ηz;p0id15w :Z14 ~;oLbøGm`B"! (PH !yhdML!$4d3ѠHfV5Y@ : .L@C2b6,R` 1&@(lm2iT akde%sd΍#GigԤ$)'`; t\0$鈎AfAJ$#.<$*!ADq<@@B ͈`   D $!"Ž !&DaVC@P)4I}sNaI@jIJJ ^CٸS2N63%43X3=u|㾜ycx3JÈAy8;rB*ٳV;D AC \}v $Ĉ"LkfTT䡵Oݷa]b̠$$Ø@D'7Oϗu{׳f2AC M^_^.v{q1,$@B _O?*1Xt,:ca)wFoo?{!=IH#&"|5&ou+ BM!1 t 4"]e1s9r'"d: @I$|e<8i(H G(2d@Ltf8N&B,O@+NA<҉,תC`fR\!! NW KȘNRfx'*$#N$Q*L&@P1%I ˆIA1&Py0<DD""6Ē@rI'!d0 xê[CHg-+dV6C&4<ɀ;w [ZFXRF0k]r?3LՀD * ^Y Dz]?}n+%6DagКx<_u~;m{6.Tɤ70 u$8ro}wם04*)D.뇧q]#dpdH]NʦNٟ}It@MJf\21 aWyd|G R ڄ@ĄQD1Pvdz?;mgq&@82] i2&8gvy߷s_9_m_aR[o_MD2W@&B&L|A A'2pV)~o{_>fMf2mպM;H|(Dޝ}9ڽA&`bs\|vr?owh]A`H&ؕq\>~ny!Id3$r㒳nnן~__?iDQB@$&( Y<NÏޏ</}#7H dA a; "d(,H& k?_D\LèIx0;fyWڟ~_v{#R,, $4[X`؀0*0X3 DS `,[ݷڃtf_R`$$4dw?޾޾!J%#>]_!C&fL=gYso3!s'˗<+άBd~oZZo}i a2$RP @3DÿoͿO?t'Cl DK8.O/_/߈˗OyC7a@!j=߶I8 _|h% La0Qg4&H"CCP$cC! mc uYy[O4v=\L\f53s8Yk N2]kZDU5ӎa&&&ɌUHf  $] I*4 Ҁ"`EB@D  Ypj0(B$0RL(ibnRnn[ U +!NirUFKNŽ@'NoL*#.yK{ Pє6ÃT]#O>ύ6cZԒ(CeozsߥMDEB;J<޳HJaP@ #$$S}i]//dǑH20Bg|;?x;_o}k*h>@1Ȑd堙Yuv7{0#k2Ò`d'hӕLrytO?>]._K33_÷^/yJ%3wn{ow` +i5jfR!ɺO//9/WK1ͱO~3G?y{{{4F%fbE w!CĀ!0Qc D:HR34Lb$#YP0 !CI!!BB X NFJZ|(>v/}g$昽1\jguٳ]Åf:ڽI\`V3C!11Y!A +q A WBP"ߐ aD`HcdHbcn@$ I5CŇ깷V{ZVmTJN.{a3 E( T|3gyS#BcjHi Ӗ4"yЉ *Sֻq<O?Jr}弟_n>o֒$Lq`<$a̚cryŘJ 8APD#-.&1I@A v( l  abX 84@qBjd$d# fٛ!zsrɞxˑýY9Ykή v,QlVdLg2qD$C4qű̑YS0!F CBL3jH !(3bD(0(R6@LEmmV[۞'mJ2NlC Q,!ًԛÚ`d X%UDŴS-w$nA( 8(i(4iZsgRS<h3CЗ|{WM#BȐjiRE:&mƎTQ&Ĕ`"C5v?=/V2Oˋ:oܷ};:!0s!RFI%sasy{mE$L22+081L$.d5$>t~/W5+ۗ?O{{"$yo,42bPęifO;!dW6/y{}/g)]E BduDh1# !:漇C$WƘI10F00u& u%" I  BhL2BJBe8L`]]3Щteut$k^)2Y[>>3$$Ί c(thFgĕ y". _EAA0A` 1F M& @ŠyP@(Rնn9P1b9←F9 +$L#,*!ߥgսNQƆVvkL.GU"5: b&T)b۪0 )!t3soC428;wѕWV_w\{ηۭ۞繩ID>@3ЄFYY$L5ǐe]n}wOdHIPdđ%HY8V)r8!D7׷/_>/O=]^ۗoƪD!B)Y3Hm2Crd$繷NS6b2^_$sۗ׭` 0]4"$cv! w$($Sq;k D W*&@CH@(t͈dAXU Lb""HLdF4EdL#e~ $]ù2+٫=2w<2\fgңUΙ961r9ɘD1 €)N; cJ Yax! CP IRD1K$aD B 41"(BPߡRjŲ[gF»v%瘙Hf̎qr2֢(jX{BuWZ6SDR`Yŝw[nWw BBԐVH{ %Fi;Ӣ)2hHa (!&:Xo_u|u$h,Bu S$Y!d`M}ۗ|TըW!"<@q6 Lk[H%fXݸ_o/}L&G&!CFB{Ow_?Ϙ- A62̀06YE&riǷ/_y/.H'J \t^kI̻^z!`!ZHb$SI&J, cÔf P$bJ3LZ $ " aLiJLJPQ2+ٖ#]4D`mܖ-}s5'ǰqepw9Vg]vQ{YΑk21+ hxd i % a+",(12˄q@BFJ@CȖe֖Ha2$SGst ̀InﺺM'V6foMKu;]k~P@$mx'!АC<ϞPQ,:ٞHAْ4`LE s=.fힲ3,N}S|?p{54BB|Ҵ) }ǧO??tl bdPh*5.ۗzo ' ۂj`V2}[w Dh00̹Yuv3|o׷/>l2 TM"˼?noPt@xlL2H1Ƙ; e(THF.O|\:X7:j]>]NI@+4Ef&3n0 LkBM1$HR2&(cvw'5+yq9:sxc?]=f={`-L׬0S|7 vEqft{X<$t@f! $H5DB7BH$@@af@BI*0&Q) 6HZi4:%ÐftbŽ8 ax')!ڣsw9ONwn'Oۜ;ݴt{Vytݽ[qZ("JHJ .St2|?w{oO| 18\/=P$ TH$BBzV227+f]Fq̺||ǟ~v~vzJFE$Zf؊$qXsy~|x_^Ϟ$L`#3l 03ݻyz?Og}ߐn(V]F7XiN9$z}J滏~޾|zv&L%$ Ckxr~gkBh0&v2&$BFwPHȻ@4$WW?{/_~߿>wW!H([Ӂ!0e#:Ne"A0Q+aP8$2 h%0$al3SZÈ 6^k6c8[efZk5=lxqe/ײk/=sI"eD]3K&EaF3@&LxH!B@D $J$ Z !f 4"HR !QDaFR 6N</p6$Xй]wd {!);$ CkP[zpzfon>9ܷ{goƮŇyZRZ'i S@j@`a̙~PPaCFvD|޹_O-`M`F`4BHeq^>w¨1%hfe=wCPI0߼?noP+2h-0d2&\^><}gzf$VStk2 3-aQZH>_n~޾>W`jHedV s?)ʺ^鲎}_^ooo:&Lʲ r//;'!( {$+JHD0L% $b EC0?'(T\ˌ$ђb!#D%<(n3M#2)N2(#A "[&Tc>w&&f ;\'dͬ|8/K=Ӆss8{0atraͰ``dB&< aD TT% jHbW&Ȋ aa2Һ2]cہ1D6pңTwB Yeq,N8 e=<$ (@%nVnt;x={۽{+瞽s?;{Od_~~캡l  HaFS:}mH0X#⼼|뼿gb̀P!D009%/ou;Ld !J۳<}s6epxoo @1d!R1 FIo_oob,3ǦXFq1{ 88.u6Lt_޿ѧ~!N dt}~,v}t4LVHV)|[Ykq϶圙 &F@Bzf6$@d|(P$HJX3$\]F%!$+_eu14M!Z\)&%8%12 ZL0LuhQ[֯:A"M{zuߏtc{I N;KTr8q].Lgɑ#5.\p !CW@B,0J ( %C!L" $ȃi)&j*ƘU1 $H!2A$cjJ5i#HHQE4YkNHD JJ@h dԽqmvq>>}ٽ=O'{>ϳ=Z-@$h@$TuV(6D4 >3{<ݟij*0.JV>o#۟na $KYL@>o_|6!;??__4` ci]ӱ+Wξ~V6 !arlkw ~O^__uC:`1 J@7\:*bD iW0" a$r^0_>vv7!taD W_]`D!MjAbk&H|Pf5ZaR*$Pȃ;RQFnu 8~x-zt'eVI 38قkX.\2d$+L Ʉ@C0DIi 1"%XE 9 &#L*]PbS5  H!!{H'L(f Дlu=gv$a)5T j7Kenڞͽo}3}Z^Woo<7L>ִ#1&A Ckq si=QaxCT!/;Խ` fB'Ay~zd:gL{{zwϽ|rg74 ":d3)Lm@MhJ24Q_?Lzj><uy~ 8W?g~ۿ629reb^_,u`%ZH HGto5y>??E!Z!o$~P-:D&Dz~mckϯ_>v}o-"d23<(I`&Ĝdpk)g*agNW!et2q 60$.e&QT0(6wJ3#n:$20y!9Μ3C&+"qNncRpI9y\#sd[EY!$LB 8dT(3XWI%H abDF$Jy6$Ɛf!8p#^cwRJ6\'e1,"2a s)[T,7-{s:{/Σ{z^._no~v<@Š4IDdDHq\&뾷T1!ʤVbq9zw=1 :VbeB!iX^׺kd$R &1g3[tHIJn_1?{{'ͭL/>??~{͹rcB)4%!L7k-^z;_fkj:vemvaQ!8" ɰ$!d2qG7_dS7BLIbLɤ! 1f\\>|Xu]cqsۗyc(EXEH8@3k"+ JMN 1݁I%0L !$P+ i,Ĥ<$R@ CęSֳ}3o+^dn9KK5q5:uC&C20 !B@$hQ@$`@IBV ]SY( 4.H0Ē`yw\Z]UV9”,m>s^ڶ"AJT_QDDJ6^98[{+w=~,$NwD||{O|8Y=]?Oty}eZ$؋& $>wG+%/&߈ańj7`B{UBw`Vhk !) aFC)mĂȍJb 1M Il 4MÀ `ipcn:8LruuU#TIz:8UݗwU1U1Nc̪Y*k5jT @KAĂ$@a A 7!(3nDBLHQBJZcV, L c2fzW:BZʪdm$b9< :Tq <h;NV@&ZtӋYnõ> nҍB:BC0k5Vm|&VHaBIIg $A5 iָO"ȳ"!@tZ_N7OׯOkagi5N\/>>=~x||3iCvIDԍcNm[kzҊTC:ML7Pɠ^ }Vg?(N*7):T瀚 KGJÏ>uoHY!j7 Z#oq\gM$'Qc ܠܤ*mI*17!x$!ӡMC4iI v0u,L@Y2 EI5jCH"$tB A E4)AJ`UZEauZp6(Eh]ڨ$l[UMkN6Ncm1clíjVƨQVr*J1R1FPAAR nBx 7DUvb!д`5,`Qf3>R3EWwNȄSeɒ$vc$M+ iJZ"Nw:@Xa,{e-b-&u^Gca4`ׯ~k& Ťu%B Xcw_/޳VF!RTDLDLvAP%IC0D 8^/O}MֱtLFibw b!Eb|wOOk-Hi:܀tě@FZUiY3Y~x*V#:pNol/>\?뚤Z.m#HB7-QP"nr\޼z{~inV $FA $]Rr#0x1|^?헧ϗ*~2TҒ դc4Ь5TJA"5V2 A ]eh.%NBHK !FC[1#+Va® Uc - 4I(*lnv649imԹT5kNk/J(B !P! 0!b% BDD@ M rSсGLEDgL9FhW[՚Сe!8su0+Ib D OG$+$4vkBjX݋c;ezT{-Gw6#$8ЄrCаmXka$!n$$Vɐc;=YIB4tG)*2M)>h A\ 6Nw}K&ĐEmiAD:k~=z_޿\~X$5v>=lޭ}?nhB+1,X؆>H'y>>=w2Hڃ hi\m; 4K jB|/^ye[;YMťC9o޼O>{z>bV;G:3HYJ'A;HxC(Qsn'c'x&D )5@#E%вM:I0Roίۿ]}O7^ IŃT섆 m::t" bkxS!e $T)EHHZI[4A SU)STp1=UeMǰF $ݱ'r 1NclclN58͹͛:Um51f! nQ(]L5!r#p$!`31B@ T7EaAP ):_D jZnҦjTzdrV=j-F,׊GqR&Ę5>@2hXEy<+{Xi1mCn:tNcձXYM/뻧cuvWN$-?>f|u5i1cs>轓( < Xۜ}&+J!ĐP% U@@w5Q!rcjۦV^UđVuj( ڲb) ֗O?ݟxڟ>}|Yo&t46Сi餡â n6|iA5`Ul@L%c3;g s#+MQ PJ)etD+6t ǰư*͜4oƶil1jl117Ƭ5UX7 H4HHC `B@! r 35T@@MfTmČmNլvTcVz9:kN^&Jի׊Tt;Į^E: b=NףI:&tLf-vǑ^wk?J" ƈȟ?ޗcOtǪwsu!洝61-M0$mn;)LLRhy&B kn9:9]#Q*PBH@RfN㼯kAID7עc\ As<\~ӗDZ)LB%Xe4@@k9I]u|],D,JIH;eHYt0sxڟR@J5F?|śPGv:{JHg㸒t @ bLq@4A4Vr<֪T &@24$PxC@WA "4`Dض1i;y4ow Gn0|3&tjt e"dًn:Ё CdͲdp+TW5jŒB1B*4*< R̀D(: $4GUj89ʪvڶ|8m绗=% MUZYmTn9O~NHMQFL"]ԲTHd]nÂH|}q]{Ra%H nCP4vtz#t  }?=>>) cz<{5D@ 7E5nsF17Ys^د_>tqU' i; !4.XJJw/"&BR[J 1]5t4V [ tHTnDaX7ʄᦌȍTe(p֨QYcm|4qv6mn99Ossf5j rhE%C|# Z@ $B &$cbТPPLH"FR1*b 2*`7`5ѣcT*jd:sY 4+Jc-E/̐vWLBwzN?}9:!&aăgûa|}*WǯKz% +7XɊR(dkuV AH"4::5ﷇ8ҋ4(-4i(:L$X*wpqYXII+M ДIcZN'"$+13iHx|x=?{O(D#Q&52s۶`*~}tp@7 bQ4aWݫii/$`SYQ cz7o^?o|y߭ꆩLpaĄn5%R ӫ!RٶӫׯqksHEbHV,j6Nƶ`TmlG;k?uF:7XB#+]I< 7($t eVet$$bIUnDLiq ϒJi ]*47RBJ: pQT9fU1NUQcqt8o4NK0Aa(ANaǁ.xAkm_/_UڦIصRi]Ë 5@3v7α{xuZ QC4 :17qau %& n,Ǐ_~zaj&Q:cWQ԰`sӝcD*i)/>}?! 7$H?W{k?.kO@@4BDFJ;~|+ (?|(Hh BZW:,עvBHiF. U :M@c;:ՉNpeaM")HTQ\5GssYtΧb\EQ%AKʚc)v$Dio3i h%+@$]h帴nF6+YIkˢ9XE"M$QJ 4UqEP3U0QbCaZtj  A(D@n*YbV6= mۨQc|w;8owNH}E/iM)H TIEPT(VR;j@/!ԀC1 U& 02`&[9krܪ6kmtwݶ|Nmnynsls9Y5kتF(Q ,@F(QJ (,|p!M*Y:Dh?Q#,bLI6)  $@H !m*7JReRmR&.…IYf[IC#!J tT<ыCtL4DfN:)(LVlFHWs>6tJW҉B%RYtiἽ9V_{;(լRMJ[Hc Iݝ{M6P4ԨW/^uѻƢJE j qzY?Z6x^:@(HVPSFIʹ^|9OnР)X" \.}޾~w&$Mf8Q4AhVGVwB0uwwU/>!tPP1&`b,|:ϟ:Cv($@@d%ݽ>?z՗>*7GVPTn`aeXx˻׃y/G_!G܈ q߿Ls͑tXEA0Wsմd.hH c\>:n_`eŖfX"}Di:8VW_>J tF1t3xCQ6/K̰/և_OЖjWc7{kg'Y/^NÇ~Pe]^qDzT}ec?#zX& $"?D "tPP8Fq;i|3f"n&^EHuU,tWpZPqlk :x biR,Xh!5tQYn6kccvmso|Χ|:O۶mNۘۜfY*jrX*ZC|S"$t;$yM:g@Fk$#MpĤbHa鄛M  I<㦓FT't;&ZT+ r:rE"E"]P<@ӝIkUZI$ 2IƘ:r̈X P!'iCjs{.>}~YDZzR)hEᦁ2Pi;=O뱮k-"*Ҧ♰XSIL_U#ЄE0WLc*:PE4]AŜ9N㩫kJ?~o?{5$n1iy6AHxw{mȧO؏NJBh!tJ:be;ͱc0lP1zӧ;B2cc=^F?~|?kaXELQ%$`vwק>LJBໟÏ 7*:)"Aª+t__?].B8 L۵NddVzZɲl; A3B-Nqē5СsR+0 %AV&);LBa%@MxScӺj1ǘ|mNmӶmmNSGU*GYeըK**@E B"$1!sC'6IhH: [G+NI<$ZIKDHh! Z1QB*Fhf_> jscMa'VbZ ȍ$4z뵯$ :E,!D{uU)+}8їi RܔAn^=V7?k% DH4QАX۟<>~^kqeJL "z4M-L* ק/I^xIb#Iߝ>?]d(4DgJ$Plfc߭74տJ)x{d$Wv/_>~xkcR?o-Z^barЋ<^:>W.Qv,]fgѵ5Z:(Q(ʛN ӄ$b:vڤCA/NGj/5f:$t %p_ϩ,fJ %!Y_>zw? Y~wk&$ U,Z9w;~e  q 4׏1J Vۋ^sU|ǯ:BnBrb $|3f=^EZűӇkwabJ*j#XV1&ubLǨ1u:f9(Jbв%7$дmi2RIf2Qhi*'֘519s961Fa*jQFըZ8XR:,),Bgp#$M'&}뺎MCFET˿g[7&5:`T0q\q-Ƭye!E"X $ Y5L`-xXLBWFRUS]1aqE6ci]n@t 0dE(B@DIs3Daq: tkTtԨm~R; !9}ݫ eMbI *J=TK?uNWMq8Xޝ^#I R;aD 1.5!Y MYz~}#!Rt0Uo;mw߿~3=ZGwkС*NJD1_|3ӇSFʪ%0_=}˗O9W~q ar*lG4X*WޜO}|H)TܨPu\O~9&%$Ͽ{ɲ|~:{.4Rp# J( 7ׯ9kG Kj.㸦`hPb鰆ccsNk֨,5,*KPJ )+$=R-YUbj  9Q۩N9NQsέs1ʲ,Q7kXS(8U(x"D  (b06 1|ӝ ;/t8[SVU#Ҫ#4h(L4z=.G1~N9j4 *Ш"n3$(PxSQ$5VA{G8H;!:>L򌤒H- V1##$Y!7N 45Xk4 h:b֥nVU] `$!hxzpz\u !0* 47Eضy%9rSHˆ9\?/N_,`u|}S&Hw xrT&ˇ/+*%IAJww/_'?n_u}UìX8s#)l߽>4Xބ!@\Pݧc1~?uH I (1@!bpVpr {o^p9pud]]Ws\]9B4)ABKn֬9jL6f՘s̚jQUzFdTZt 1HC4fIZUc5樭Ƙs6V(j8Ǩ1Z`Y0@)K-o(Fm@n:݁Nݯ~~g/lFehNwIgnR]5 ) Mq}Zׯ~O~_?=`BrU3ȍLHhMSZecRIki[HсXT&i"g&,ˊ -ZjPң4r3pp$QQ 7jd2QI"Yo?}}<+T&7]-H&m)Dbvt&+& `h.SWM7b CԦ yXA@1F'#I#.7~O]fm/f=}|t nz& :k`[㇏D7H%`KN &tQ~}wNU*GKIn߿ÏVJPTRb"_O},즍1H!$A `Bg틈47o's9Zκ.uj(*QTi,ƴQc6ƨc֜3cXc͐eK, v ="۪5ctWYZ)-CJ HiUcQfD˪1úA*(DaAX  $ F $$`H:tӽz軻蟪I~,R`1 j$()ec:~_7stQ E":MN`5Y0B$V٥ n:jd%e%Gf^faCV V(Ujn& J~)݁`D8AI{ZUs^.`uZZ Ih ABAs#$%16u{?uYP!7?e{{\.~:Vmγ&t,0tu!lWE>\ T*b$Ą:)MH$oOw_+u2%!JFCKmuuzw^)"F Bxq׿ΧׯIA1E*I)I*0pS "79r"7Džc}gqrt(1Ӭj51Qz5Fլ1G9P,,!nݔEjh FYR8j K-QVr*U5ưF VҡC KE(›|V-M)Y bB/:+n۶u_~/{XXLQg)J3ҡJzwߎ9fai!EHwXݳ4 Nz2zvF ݵ֪qauH-k,$EZj4~9> p.[FW:nт԰:=̑. %!!-@лlxR$E5Q !M5 r9O\1(kX6`J&&@@f}GO{! P>?{ IG - ]0Z( RZ A_H,s/o~?ˏk_}hBCW:cuzsc 7MWU#ׯ_?<<]<~zC#5L4% hӆHhp_^Gg8SpIwtz~R-@IT XB1޾7דrTZkᦍ Nc Me7ƄXa$A|s\8vמuYnd%Dh2 c89ݦtclVsqF5cQXj 7IMMhYYRuh,iQ *˪(Jòʲ,khC K R*ZQ,|BJjArӝ$d>P߾o/eu1?^.ק- J -v'} QӋǺ UQ4Hw߽~0X~In:Hg5H@Twc~ߧcPEiV(PE(-M Fb7/>Ǿ?k4iBQmn$z8?|_(orcI/)D$) 7/_:ow>}|]"sB@BT$b!t4fu hҫ1狪N|3zqua_2ֲXZUr 5TJVkבỿ?T_{?6$.LYe=53Vm]F ṯع"7֨EMU`ؽdhZIJCaVjxڹ!J⪪4qt+U HH.GBR1F1RfնֱwP%!|LU +!!{,p5iSz$ ZnԮnn hB34+DBYϮizu;V ]#&C2EA@ H> @>A6dG@$dnXXDm945ZUwz:yUv]|z`L#T€qdNRJ*MAF*ˉ!1Uұ^xv~rן~mۭ'MVZHTZI ܹ{on?y]1T CQ1@e؀QbP!μ|G/_:[#5R=<{ǟ\u&%!6 ʝ;w߿S&%N۞,QHv[PTTuYwq˫vnW^_enyւ+!G=~ыˋO>psnАIX`CU4Qhf tTo}xϞ5IWxnw?\Gp]B &-i g^{}sdz7sKbanRE(IL!(IT촔)d={ΨnϮ/_annv+қsӎ$T2**`-%nK-zu;,K-XǨXjZ2 JF`*@*$$T ).!1- $, +)+G@B yE Vh$Prd AW}{d]v Qc2Ԓu=QRcJ/#@n2C C t̅`f:С$ӸLz:4[(؈@1J$ PU"GeN(Pt(%[("XHeZm#(9" Õ]5xїZ?a,+fn1NHC`G%Ph8jmwO H0DNwgqvyzI*GV8?9lB@Q$K0}pz믿O01(G5GՇ}tIT@N36G0^%bIHu/?gOֳՓD[*PQaAF4,'>[!(3:`IA#h ,b߹{Jzqg{k۞!}мJTo  FjT1BUbYZ,˲c]Ǻ,2vX,cK%˒12uTjI#TBH "$z$(IUB()*tR"r$$Ph$ G O"EHP$ 2I$S./_›INNN+]fg3UR5jXcͮJWf,9o|s??=[9=;ٍ1$(ci[]U]VLjdTը1j̚'u?6̂pM05XHRct~Kr!tMTHcXI@HFtJۆDA22q6=ێ& #H0T(X8YOj3@$n=jot̥*7 VƨսB.XtHNv=] B("dwW79 * He-&9Іn,p!*}g'7ً/͑Jq޽tnmk+:SLRQuϴ`%6v$4Qr)cQA[7w'~nQN82hWB Eߔ~LGHI( s!,߹,U?{vi7۶44!=;)ZxeL+lB$X%JQbXZ˲[uunK1eZݲxTU5r i$Td(ԩX)*"`0"-ҡL+*?! "@ V(@nCZ $ 5 ζ/yeqsusvMU=*U9jVfў7tG};wNvc!GPmJMF JdFU5ӰTntFŔEYD iBz&[!(2Rmwiw:D!$MHGۢ1-e9`!$"$Hֳm, !)!!ufl=_ݶyGb@R hIQD Tw67۵$%[4(C@H<=={py7В#*ТȲ{ŋ) U4teζ9ge BH+\]^\^߿z'u?`v(`<0u%jv4i@SA VĚS(1Ɛtn~هDZl%ƤZS)IboZh_~G~˹m4D;bu @w 416 1jb8S=c v=y{Q!jT(!g9'ᦙISBERJ*A`{u\^\$@VPJ hGQѴƣDPye"ƣI%cdT(ƨedc7R:u1ZXuFƺԨ1FUXjFRTDh $BLE+NFڮ8sK RbZģhll"I & ģP-@ r$1l[I$"as;lpW_y7JU}I9gu7ƚJ `JET5:Q@2SMF&Qv Z 5H%]rݠtP+R c*44* q:7gۓa)%"fAa$ ]Y[J !@R)YFvйMmX2?p^\| 6QJ{w &RHkUf=Ujy`&H$XֆG(hHc+ ?@8JBHRɨ,U51b2euYNZFuYZe]j5ƨ%%cYR2R#uJU! !T@E`*II0 F!mwoT rdq*PA%Am٩LcJ `!!D!`B dʶmk_KoV~COIO}O|x:eTI8*J@ !GF IHA% "D%KmnӞ AyedpT5l;6! %@H2L7dBMtn=`D  P@$췭lmnD$N H;)1sm(bm?cwCew"!Q!P"9hȑG$8bBewrŜIHB@R#I9{0jwH0ՙBhNͶ=IDm~Ç<}cI#Q^gOR`B%˘Ju# &G9?9sg/>Mxq8L5H*㇯9{ys:Iن 5TC(Ywӳ/6I.G՜vAAɄV"%*Dnu+5 ,(IFeTQe2jd],c]keuuuYjԲ,RQ2Fj,#HU*#F0-B&923"&IU4bW: TC@lgo'QA4 =M "S6zzsgwvsn胒Vh' Q[2aɓuYsn䧒 BР xJ€4l.Zmᓧ͹ ?oݹwοLZS)d=Q}~`%eIĀdwk_<{Y+1fJ*j X&T ioDg+DVOj+ _6 9RGjYZF- 벬cYFN,˺XG2eZ*eԨQc,RITJJ # dbbh4 ڡ)c) ( -3D<>L{v{݀S DIGс - Gp$I((6xN9u⭦>3? c$)ݽm S.nNݨZnE%Gí@A Rɠ-M MAPLۘbLT;ȄRTAB 6MEI@IrRg쭧B "Gz: [B fD!2 aH*֛z@S7##І˞{m?xWK`$Դ&e@2#@eIf>D,+ (@ ɺ;=[^ܼ8l׉P4$FL᣷rC&J6EOhd "9=z7z04 FLBDAi-;Ĉ Nk>gm}#1"D`W仿b{Z'^\I$4r+LHvRXUv=~m|Gi(Jfn6;iAP :!`bN Z_+ ?G2*Fe7TQ%˨u21Nw,֥vKvcZQc72F%eTUjTFQUyń@0R4ZV0̄ "3czC'=L:dV#MMr+@tIGlmmimlUTppSgw_.IRUIgϞo{qqSU{SIyǻjTFQ0ɑDb#(@ ԂYrrg;[#PjZiCw[M(-%؈2L1Ⱥn&b*X:m8*$bI}49%IѦJ*crmYBcmƜ.^o_ç߿fuqs6aaHDĘ 2;gw{߮fo-zЄ$c9}pvh2D b8r4݃'*m XQaGdn{o޶? J% ڤ8R!mIJ ޹{O>d$Hi[(P B$)#Leyu~Lt%Hf&m @MXNNv˲^F^^^xj}OC5,RT8F22e2QحYڭnuӵ[ƨ,cYFƨRc,S/R)QA) vK+IBci L4bݛ6mlx NJ0樖"B0)`:][֩S[fSgյW7_xQ}} mFMoP?}{/+oϟ*ۭIiiۉ?i+!ha:[n=v+V ҢJa[3J! 6vuYúIW"X)դȺMi'IW``ewg#o./y@DPnRm_ЄgO//<_דܶM $-F Gź/pf]󍓦%5zܹ:@S2J[bnIgj[w{MϹu+f~*?lؿ;O>@|dzeDMBDh4s~wk>ĞPQ IaNZ^O|t4"b [=eV#ΝwNO9 œ'OnHW^1"a#YQUƭZF.˨%RRXeY21jTղT*ESGy CJ EMb%3!Ei( "%ӤA Iǣ.J:R(tll;& 5&#р(Az$1 rKm[ƟzޏoNU__~S̖eַo~?oq$~+_}ŋ12F1DPhmoѶAPmPnQSn4ͭ`$бTn@.lt[ 1e j #Ђ"ƤQQ4zfnN#4RIcko~e7_yxpy67 @!oi钂"l/_ܽC r-uWkY72`6"6/]\*pxq'777<ݶ!&(F[21*u:XZXڭYRc²Բd,:ZXT%Un%[*4)$PB# @.@ä "@0 3BXx-T5c:(Q5$4ivNpޮ99}/}t?w=z'|+_go??g?|wKT@I6=:F*Uw۶gwKvs$L;XUP '-҈|aᕁUɒ 2F%1,KuYe,cY[nRc1eIUcBRT'$ ""ˌ f р6SZZg҉!Z3]6G DlIAÑ(`7cf0mEAƐӛAK2ummzGOu/ſxyU=yWT6gggks?sU>{o駟O/?eIro~}rYbHmTDmoSf<9#fVnMۛSzz L)!hfd&<"N'Yk9imֱá01$遝@4IфݖltHBTV|dNUՂm(dC0Y:I*p/ebc *YF*ky{PvgQEB};5^?_דּk;,FGQP! M:.^_%ڈ-!!nN'}mBnQ wÇ}}Aj9;;g}>疤cHl:ZRBUa;Ct'u_9ˎ#5AI=zm4ҭ Lt!j]è:==wѭ[;Q[b<=2ifxe'* c8*cd]jc-c]jje]e˲21RFF0D 1h jШh#R#L(IltI#PbȀV0H  ݰ56GA&$h8iBz04ڭqNmvq/k?sp8<{* |+_v;@۶|߻7$UgeuJ;VD۶n{svtm9oѭfvt * $d]welW{nj @%*PFQk˴[馩rd KSb]NcІBCH'U!G@ tT6$4j ~I r hrRcw}L7QX֋vʬPJFaNsxb~|?wo-Sݠ q_^^8eʔ IfQGn9F4`R *M{xwy?ڶ)&$wڶmO GbZsroI(jw=x~L$A IIL-Cƺ,K'ucHE`GZ&aDl Y╅/D #5Fe,`,1QQKneX2jTUFR@82&HND Im Ab GUl#e*ĪaTF1jI$PxmMH Dl:IwI82؊{)IqvvS0vu}/?I_>}+꜓vWz$g~~TwG?ݻw*;_|xypZHDl;ͶQϞ=\gwJ:t mZ#2Q%׻w.173{.sz3bM;{^^\^__g; J3NObvϞ<T#14T<|?9?{a\v/F1P-%"\5)CZkoկًO>I#TIGm $ T믿yq}MWJ?bM;`Ԉ&"#DT:S ,|!QIFjI21FnUT(kfF!!A1jBJ;-q$ hkCũN؆ܢʨU#˒*<)VBfe$T֨&ԴFc% vkh!1hk;هm>\^y~gq|;~wRcmoGoo]\\kӯ}g߫qR={vos~nۜ6m;laym?}:6J@ArG?y/0 0I bo3UNZד{ݽsYa.?lgt:*򅅟PITUKj1d7JJc*U) IJPA:4 ح)BV""JdrdpB8HBQ_XF*۞STP$NR&Ln vjϩbR# ܶKo~?'>n]U o+W$2n}?_W_cv_oɶ>7ٶs[O[ǯ?m鴭,'u~|oܿpYp_^_~鳋}?K;||T}|~l=ƛ?7o}vm=~?~O߻/6iVewN:;ٓ?/(GrVwGor:{>?/fkHR]K7}kx筯%>yg㕶Y!$UjBX?>ΣoooWx,ų~;?ᳫ'7}5[Nvo}W7O|O~;7o}?_ng?Wͧf uo}sOvc~k\wpy~iM ;nnn./ݓX=+'c9yG~ͭ$n%H$NNN>h4Nw,Q7x%Tz/߽ǿgo!&!#D$9{돟<}z}uDD*79_<|Ϟ)'j0Pyd$B"H;E@o|k_ׇT|lNF!6^YB4UdJc GHF%JUd*1QR% ,!(4ض>;o;b"@3(%Z.kpvU~~R+)WHT9]ǎ#VKJd%Bpŝ3>ă>6ȶR2d&cܙL:6dT $ ƀm2K6mL(4PĔ9)m0Hl:b[RJ0PR<<x/d~_Ukϝ{VW$@`;3yWD>K/ΟfߜXY'?CKOq|qw׷;{Y]ZZ}Vo0zdva~{wd),dI8i!@?&su" .(M[db'1豣ٮ^RPdBA,zw{w}}L$1i- A`0>B8T}b*%(*EH!\ EUETk*$ # 9m$J1QvS8]HcN[YS$ %e,ATQGHQHdd)8H1 R*F8N0QR$A`{xKѣG?OL&ǎ]]]] !i8pu$"k~U̶;0L666ݻw>Wz{_jffoݺ_sg>꺒V:oX̯;CۥtN./gG).dPά\?=r,"lӶPLu]]QҖT3ZYYK)鬢ҡʨ4/fsg}i~yffVRf~u{{vVa"?[~̼z_1jne{O~>KgN؞Lm8R//-G$O1 pzzy^>>}ݶmWڶ$U5u0ܓYǿwۃѝ ~`8v}?C7* dPqx~>s7$>|{D eҐYcRiSB&v:IN%N{ǎ = #)%I,dQn߻ӵ[L%XZY;Ve޽GƚT! ml'R20q}2n,KDJۦuݴG[][6߹}%2 ,l\UWx8*(ːN`l2d:UPH5D4HҎɶA*,L[qb6d0& vJ*JHل"jt2v %d,1FN Mz* ]QyyOĻ:d[ұC#6S6n˿}MTǎs哟:y֝w޽4ӧO?䓒?яkd>[oU?iO3s-LI$ۙuݵW߾v6ۤ8zǎWos՝R^=̮.9u'ee<Z琤S'Onnv]G:}$%;܇XDؾ~_W0"g9+˫ɏߗ~ysd'#gJg>+kd2 `rquURf&8̕?vd\FNjonܫ~D-VIU*wJ0iI L!X[Yk'VfRIm9Cq߿? 8!^ݿlph|SGܸgBB 9 ?}bgk)' 2I g/ Oa) !00Qss h{k[NiKAW@v@02`LqpbP͡0 PH)^H:$DH@PIR%SR6$6%lƆ;Ŕ6p$v.jd# KH 08-Rtf:@ia4H iƉe[][ڶʨfff"?I$6/.Oz5Woorj_gK]Ǔ{^y[v&vw JA9CaݽMxa\3hKtѝq;4%6.̫77~fgҋ>3J>O֭Q;,]^WGf灵#g\~4J`yȱ#'$۾t~_D5[-_<~sۛ߾{Ҏ2ExK7 ?4U?~^/3߼Ư^]5Ş.|fgf?ݷqsatmv{kmK ]]yΝq'ŠsGvFO<r|}SKIUv-iQ:;L; 4jrck#]儨(B8FtDݝMdJc/,ݾ}L) &oWFF ) a1U^ٳm\r5N !Y`  oo4ڵ N,F0 4 B --//,ݽu` pA~뷷59"ԧz۷,$l[.{{cV0es DU~/_|7쾳2;JM4kvwyWDeUM7I75g|G$e7_ʥ{߿1*;`[q6jׇ_|<`0<41r?5d<s׾߼h]T0.Pj)%:=7w!f3]ߓA泮7/27^3v?;3?h8&Mڥ +rK)6 #e\[#NJ͛8%!H 6TU}M!c"= ܼucnnٳw]+SK wo JD")Oa h4hsckna 2I!¦̹Fѕ/CB [Ūe#9#a˘C6d2C! rTTBHR"HL"8m9lL҆xJ;il(Y CظJ۩LAB+8b,GXJ;t])ťXBvsʙVLL'*bg)7` RT:PW@^xŋYJ4]'x,tL)?^{aU!q믿⋟'?[n~O]o]M'_w6w{a7jm7hLg:L:i5]á>όW~Sll $PLxhCQUm7.tIr0e;msstnܹu>_} 4z26_}[?IsN>uH34X}Io޹ν7&Pիfά]8aIyҷׇwFedln<sHЫҍ{3kOu lmo}bWFA5_/85ROՠJ}lp/H5ɦK)/]|aU3T EFH'a;S>?fZ uVUYL]jAV-ٌGLo~ypπ %W KvILlR@ ۫+k3[:wa[BB` qͭf/<"裏>/ޞ`~r8K*ᰙo߼fw eԔs,.L.“nt޵p?CK|76n=%aqHLY.5wU Ha#m uϽ*I^i~?,wƗ_៩*":G6&u c9a^T,/rh{gڽvM!4++G=.\Y\ :KvB*UR:|eH۵꺧Vn<qƓNEFTD`AX#3MbxRXj+k"ZᴕIFvg7z\u! ,/ep 1A$0#JJ iimy0sfRʰ! gΜc1 ^>~[K!0tܽuciurڱS xŋr Q qUNLƷ_@&e1O! xΕwANdJ#'L#J Ҁ4S2Xi!C5"UR*TG%BRE@2I6!>i]SiW)!OvƉLC`i甓$mcA֒mvmvݢ1Ntfumiۮ.`S&sRNgKsLt٥̮83dR*nuO}o|ci?f0cgVoxii/VL@-S.vn]#7|?Ou=33S?Sn^;v3/:!zݍ{o{n2,*IiNTb_y=u]UՉc'9Ͻ/ݼxsvI5 clOډ ɀBƘĪQwJ ő :mM6^o~ؑBs?7;ܹk/7Kϟ|H2ưlYuכ4~@%31鬪^/ <ޏwd^?;3ޠ ffCBRBJdqvɮu9d%r fJpUrʦI'C0I4I8vooUGqq91OFv rb$0TEJ aKs nH$ri@N'-} F)ܾ)%6H H%po?7n޾"<B€H!a Z][ܻ{I83Tݽŕ=HK ;pm&0SJaJW̔ [`!Y"P!"@ ITH!!S)1ΠNiv&i̴-6i Ou6NmN);E2HQʤkfva RڮKQTp ,.K8J:6.duMWRtRTQ&x%I3O=79;?rlCm7]}Q]shww_>{qIGg?SO|bgk}N!L'KRH;i lZ5[o z?tkyi|}ʍ_ /n.m7]P@bcc!ɉE49m3;3 H9*Qhw|絣k$-.,}=[ze-V>G%e_{mXv:~=ƻ֛n, 3b5v2iƼJ,ԋ#ǎͿ$ڮ5m@fQj1JJJ; )J'I7ϏƓv˾(rR9#Ӟ8J&QU]eq]Mf?ȱM <Yr&ߧHba~ܾs3NDpЙӧ}@m,ۀŷv4) 2OI.yKo_*d"̔mĔV6776f)cM.]5 K {{2'O_|NIcX0SvB˪5怄S HrşPsH"PH QG%S1BvBDtRNalUlSFfY) ;m \p1 N#LT]B:Qr-:Eӵv<+^ (I]Mڦڈ 20&mg:Yd:-YJuMWڶwkJӥEHQES#I;`{~O  ~gz "A;[[~7gqco{= 3/_|3^OH"S[ɟG_y*e\3J6HvXvo\߿/nГ[YZI1?ģ?Wz/}[C$'݄?A(a 6l]f&lg hp{|+y./H3/-S-fo4e,.z=IJWř*8$3e'8z'WWP*Y,%]JI3ٴW6FwO xW g$Tƀ"ffGfI[e2BZ*+ґ2GWOnmweG`c;hg3 +f-a%FB"! DTZ]=juRI,%X"j;ƺ٤d#z'_~;`Q$A` 2hqqq{sswwgsskh d`)2e2uff"RF2Bf*mvkGd˙Yl-)Di(82vq`- lc UuO L]C5JB)BRDS!(bk* 3a2&mFJgXiiLDL;}LgD -UxR~߫EؒۅR(tm[W%S@,,YY2%d:+m[&m]4eܖISJ1mEǪǛClspsq>py%$"˷oo¤~…Q^y)̟z.W|p4o=sssv&i\bJSn̻uZZU/9d7_}{_ؿtԽ+)6lvo{w渌+C4).B %63\"3 MMQmHQM$tI#C:bwo1N9VnL t#!KA,/on@SF1B=7\W$ۑr,رo\#l#R^UOݻ}Ǚ`,bJTǍo\؀N̔,@AS2;3|wu]$F JR01ƸRܯWU0)#O&ݝk9Ts U1!,%T@N2.+GF)$.NN 4 ؙ\.8 i}iٔ,H@ArȖ'Mb\kga&4UR]WMUs@D %%%s餘Lt9eזteeetͤt]i&xRJD9o}k4cwVVO_^X85?xR~4:}.so1uāxqiɣnzWUxnn)({{|5AWƽ]CINL'H(ci HA`f+2mwuR +kk??* 6q˿_U\U|'z}{}'ee l'T%Ye䤌=jɨ59u+/}|N;UuD㏞~|㭛%6d J,=#K`{8oe^=kK\ƱqK֎ݾruv#Pn#aqiyww;T=C8da,˘m-(ԛdݴ6x s@` I`9$E( P $LRɔ'(@v)Nvb&I %t' 4;]g-;M̔ ؙ +1aL/gK.ʡTIڥ鴭t:;ۙvR+YJv%;,mmݸ4+]:&e{aL=oo' 3\EfVfGIU  ~7@"㫧jNˊɔ%PA.Ϭ>xшlgfvF`^4 [~g%;r‰Gl_|{ûfϙd3loݻйG9_>{{;A! 1 )^˶pvfX]>]߼4ٛl"@iڬ4SJ`wUC!BNJM9Ibb%%m S$JS ll Ϗmז`#e@j2&]Dt&,ڏ}/7߾V'MsD),9TdJ'Nj6)%twF 0S6ߟ_8GFK.$0¶`!,affgV7!%7^FR)d  lc!lA/eh8tZRT)6%0j)RH!q(DJU!v`,%vLl$")p%4 lRƘaLC8Li㴲tٯ´>P2ѣTEi[)2J<%3.&}%Kɶ,YJf\JfIO&ˤttOP]u2.}%;vlffCu]:uݻ|[׿z\?{b?R>ѣzQ2׿OWU]ǻsH~듟<_W'q{ Oqfʶ8`wV`pt4nJ&9UtG.gqaX]H]U(qT%++g4<8ڣ?׮tn&ǻ^V :z䘤ׯ3lLo߼ڇz>"$ fnn^;n+I1"u^㐤~_I,Moy⷟SQsؕo54n\J7R2U&$Sf=uI Id!d\Z\}!%D]U 0 cG{ps2mYlƼK`BcǏv]sPXQ3mtL XH +0AHFTs [7'㱝v`.]k-l@8!3%02 "I1lS6Si F)ve$g*m(Er2) IcldLbcc;̐&ӤxJi;Le4.AvR HE6N%΄әs%,%3-HR'2Nݍ_{3n-,z,VVVKΜ9?7ݻ4 SҸ~˃L@P)ݿɄRU[UWV7VWo>_S'Ov~ٟd,gY8)  )CR^EE{Е60\\6máN. UT fr:-,N]y>IW^]߿>v PٝΓ7n޽rDgG[WGN3L"lG۷^y< TU>y4wW=UU땕٣ WU*9KI`yi}/]gst{ҤA& *dUzL]$V(/Z٠bD,̯tҀ1HYq┧ v`*b~ai4XvB/](TOA4 7ڦzv~θkxb fvvͮL,XdƉgf'N˒X 8FB/%0BE];rFdO ̡L֎Ki @`,f ɨ-  2ؚGۿsIBNRΈ1%gWsH"B !+LHNEPKpP3)(vZ U+IM rāMI[Ta;;\&nTՙtItzr3HuLNYN.b;XaY:TQՑ7I"t;[7o[?Kkk/!qhaaħ>I?/;w& Ҩ~ԩϿL)mi[] M]ΩSw{=Kg>z<lݦ6 d(pO"*r&VGz]v`8ԯN,zlg&) C?r~8٩FդD^-_xG>gϜ|@Rfnn}{/ln7ݾP$U$G[o_G_mL8]BHfo|Ξ<#G||1.yustwRFFUL=2s䉳G*@RDeIiSͭK_䟯Zǿm֍mMQU"]͝~f[սř^琤L;۔:)=I7 iL:6Sa Z^:LF>!!0P Pzf0߶Ҡ9%W5+WQxT s];;ICEDITI~]]pwp B$X@0x=^2H`@L#vwv{T_=vY߻1$kiuinaascL $ 6Mfф) AUzHɼ}Vf1lv+6L a'|B!d) QE8 ˉ`[INt꜂ĐFvI'.:#\l M@)wF$`#a;)06YL]\dRݑ*}UIfv~vReR5@UwUԨ ɇb#@ MU WU]ˮTѯA.iJt{xvf֋C}Wzy$~C>777'vfC=>__z۷o7M_׿+|7L3p};߲[ (kk.\`J^\nNĥ i%i '7W-<}+lFfΙGϿ[U;o_֤LH$O~/rͽvɶR5͝^;>~a{4֗~{v[m5uEۂ^yE@UUq;VI!a0M7~~s{OUUqW~[{,sţN>H@<ܞW|ϳu]Gѵ?=znl5̌}?nMNGCQ=)DXbSҕzkڦҦqq:2(6Ң>vrwc;BƉd8@D/,mmoNR+BZX\ItCYx䑇6ַ6Q2ST )2B5߼qڐR*(%eɒDL 8w˿l˲1DUkkG3J6w ff}JB#D:@,-,,mnl޹~ dH#Nlwv033ABUը덻m;ƀ@`$ ]F d[Xj 4Ra*szq2*̔la9RҤccl;S`HF66-NR$ll')O)3m Y,tvg) ` .Vڽtɺ-ef/R2%]Jf?S׵TH]KN96)$3tRv^[Ikhw}͕Րm7?r&fff{_^^2s8^xܹszׅ ~~ᓟw}kwܙiWO~o_Oui}Hѣ /Q7N\P$H8,h7Ggg~3mזJTU뺖ýoݽt{3Jd%Ol횺{`Fh _o_$c #,kGٺydo]}}{>FRF(Qs{?_~xP6#bП y橓"2"jR{wί?~Oz?2v(ݼF ")vv9Ao0W])RbT)b;)N\Hc{ۻqrȀĔdl+BjnJTA@UlF(%JWjvi#W\[l*TӈL@gΜ{."eHPS6ӧ߻۵c$9IABgnܸn'xTE:}@[\;vtog;Lℐ[A$9SS`dZmk& )@R(Vjkcs2nXLYBF`6vDdI0dl ̡CQ}UU} ~_R]"A` Ȑ9mc`:&Ad)cl#2 `0i' lc8KdSp: b mC#[YagfdfteP.JȈBaHgY҇0Uu Frd*k\vm)g3o]yчȬNؽp|C~駟cǎI/^ Ǟ|պ%>={_͛9wg?_K|; mD3_~g^w酅5ms;nJ Ӆ+Aa;&ے"b.@L6w6ݫ_ߞoKl7MӶ* wnfkgWxwnQ,d T!z\2{_>wn?nsaҊ0ȶEx5w˿/Kf灈K)7.}ڍw~rgGPD˄{W~͏?ե~ 9d]Ji& ۜ 7æ*lBbGGM7rf]9!#RTpm VDouzI&$%DP7Bmɲ%Y 1?7oU*@r,S,ͰmOW꽝ph2%vd2 :vp1[ @X-d@T= a#DK6VVWWܾ_XX^]غ~2SFv"tWK["'u~o}4Aa,9d$,d,d0'9T}SW5WgAuM]WTQ6rIbdҐӁ)( \L *S"]*c0 p:-Kvrdeە4q)DϴAHm麦˶WNslH۽*cJU'X..Q%a:.7_T9"M" O;9 z__GJ.\lom-W>Po{sM![kk81~tcsn8̙3u]oݽ3n]3e;$Ғ !,)fon./.zkwo޽z7^s}?}66W=~ȩ#+G(޺wkηnl_N$ݖv:3ۄv{o6qQ/$ƀ-Dx}t~?g~CN>3s4߸έK/;7k933/rCUm!EUzh;~ׯ}㧎Y]>2u dd2]yon?Ը?xw,nlDU,e2DUpbdʅ,)SGʸ4ΎS+k;;[lـH$d,1%`~y&' BJ2`!½\7YqD.9hkwمޫWdE/d N$i*̹W.]0A [T>xo-d9` mnl7?yΥ~vk`!s c$ Ae0-̙ 7Mqd48 YL)AVtJB lH͡^/3AW~ѫPP)ƘV L,ɴq1aN;!1BeJBL ]@&a'`viC!-`L)Kev#K5nKK=.׹s)ؽٺBIp$tڥ)Ȅ=FԢ\*tdҖsp7;; u}٥_g_'ND0L~~_6^W~k_?|u]Kѣlc m;iv]o4 ?K$?![; )BEm@"]/7^YYY-+@BЕfҍ~3.{nqc2 CPDphҎ߾7.}1٥~GQ͝~5v96 .m IzؾkۓG!#ɲql&ew}x__;:;XU✴~(7b]ϨTM/@d6m3޼{;W8X-뙈ڐٵ]3v&eX6Kd\Gd3vRBHm훨UJ۲3)B'N'JI >N(fێ6JTU`TVWnmoBrV%*G@Xvƶ%'Pn;D]d'Y![2TQ=Y If ڑ{7R9&+p<2 #pJ2gI6uT3s3TqޞK'%?` $SNB̻j 1Lu_׽׫zU*@ 0`XHQlSVv6@t"`3J2mR8AřF i YY@`+Uh]uV[Tt&i P` TӶd,pe:* KֳdNǛ{ݵգ~ǯ]ɓdۿۿş{{{˗|G>rш^__aGǿf)_ַ>ȏOu]UO?tر7^Mґ9q2 '6 WlrҹkûGveRM$Œm؊ġtۣ7/~D]+efӔqK($c ‰i9~@uW\5I H$J 2,nqmO'EҹIZTuW6R QGUu"A]0n=ɾ:zA ]@[w\+TF;ʍfd. Lή))UZ)t,Җ,m8zlO8Lp"Q36HمՍ;8[Qd 3=K l@bl )_{TuX,JJA#vq[tqփ @? O!IUu'oݺUUI?zrr¿z~W{ッ~K˿˫ ~뷾]. Ng[̝;?s?WW>I<{f\2#a$ PBJ!DU-8/~n }7b$6]"T &JՀ_ܷk_\.B"BV %2/)3p}o(pv_LgLERb@$ Bxamo' 5&yHeiH 5&Yȥ;OS6YD 5R"aP w}Ipu褪 yZ T&% Vq3La'"jw0 ")$XD!"!" %j%/kípIJ^DȏI*DN$H$CD@!HqILtz ګg x㇯?{{/1A2I ,IZTaPq06P0*5H\kQD('w;AU4RvңĄt V؉ܝ,]n8:88=9iI澜o#3#TGUB)jsw_=- pXNOr}sw/MTbWyW_U I_l$A@H0y飓''/|폿]H,*$ 2?p!D {t'A!@ADA$( H$ IE&0 0 PU ԪJ Ž B Z$G2ҠZep:AĠCHN@JD Iet\%$F8LG DC xIC'XMܝf/n q2 *\Z);J;$A^Vs^y}Yw $4 Wlo6O?gѣG䤪2M ~l__{;x޼lI:C@`Z BT4/{o]/@f'pO%t aŏUQ@HNv6vƵ^_?#i;mml& r," EP RR$Zk%U+R֪ mg[~Q%"$zBJAT$8vBCd[dYő)463vӶ?T[ {iK{98Z[_;./WUBLU@J(%ZS) 'H%DKKܔ'QiKÿo^oK >{o@Nx7%OϿ!k'7o?,/@A5@T O'[ko8Og&*J#1" RHIH&9TiQC6Ack00uVMLI6H-QhܓEjIO\TBU TD8ĈXI.UԧyV{ZkqZZ7f뾠)iPn6ش (v*@jmq{ナ"AbXE,n?|$Z$v$IԨT.OOѣz+wt Ua["C aXݹ{WA " koP+*U LO~#/>pj2DHSInz"Hn02w$bIPNaG( a'I.HqjCkUjVC":\ޓ*Tjh5cTRu+M RSJT%AJb#D! (DCvp% AHO 'KoA[$ZklèF&<(B36 7m6jŏm6HW gYZxxpr}4o7'**@rbIN๻?|p/RB( D TE(BJvƭӓ1Uz&$]??z|!uS{G׶Otw H"K/}7^w(D؉>қo`:lް>W>y|zryq2Vexk֪5UX RQi=Zf'A"Q,ďDQ c[@/ \A hP C*MHHD (/(!Zѓiaըc[Db;j$MJ A1 ((AI#BbB @B$('b@A!AќD(Z a6&SM)@E!v"BK3Vf {7^_k88c¿a޽я~CPU%ѕO?׾'M^K %(Lhaɛ9 )DIp(%0;2K*0JJh$đıB(aPx|8\׫ H2oRGL (@"J@ & j(VC mT*J{̈DhQ EAn;1 c"ZY*RlH.8VHpE82O `)Ko~읧?;!=iHqoUcpT5xtiHOJ6yT2;R%NJ}襏wT%; R'Va!U)?|t_]zSEL(P?ygg vĎ"AHmoƶګѵh øg~{omnz}{L[PPʪz}Llaoly^I'"'C~$"\261mة6P% BuTb)p(tBD'$hqNtW4VCpbG@mPH'8&R D@Az۾j=PЪ%H"JuPLPKܕTiGo?VDWzꩿ7ݻwwɲ,9::*SO}c~vU}?!@ $JsK!XN*F$$@-}Ώ% *D1Uq-K_/'~7=m<BRB $*%*bBR"B%M*UkMjTc[)X('Q$Y pĐ1I(drn N ;K'!ZZkczGD??O}/_~ix e.^ny0v-};ϛ9Ď{,g'Z]&\t;Ϳ_w3|?{y_^QC a܇6-S}VY@IAO#)8Am~=($R 8J )adB(lo+o~k;m?}7=yxeJ@B$ b J)Dj%Xڛ3'P$HJԉ""O8q7!JRA({I(;n˱ܦ>N_l=!y~?k5琞ݛE0{G{e'Yl!gajlG׎i2'$ (ṻ/=:ٜUCQdy)RJ~' >wiypݞ 'DQR^xv3=u{7n>}|t렭0ո?6V1z~'%ɼ>tL L|1]nӋӓӲg=:ۜd,; ܻZk •+iUIB =qu%z  PR`G%UD@ +=Ȧ'qG鮈EvC!B(D$Q!2qް]k;(( ڡU:?xC/>Udݼݸ~XWZk7o>|_|;= IGGG IΝ;W~~IU+כ?|c?񽽽?Ϳ~@$섈1lMX~X@#Y(d"RQǠ`$Ѵ/y{{uH|>N~JDz 0)TcІƸw b6z5DB1)+ ЂIeQF(%LT(N*`z")H8ABHULX=;'21UTbX&J'V/ddjb!B%2W'7_N/ϝN*"0{ۋtֆaqooåOxzvPMCIêZN8qT`  CTj8\{ HIn=}U% /S_X^{CPv"DN=}n߸y7WZqghURO-\q<˼,;!1v|sy~q\o7;><|xYO'ӲIzPjM5 &;Ubď \iUMeUQU0A,ET(RL*"H"A%Bj{se)R [;ni]` JQJl Ugz_v5{8̭P "AJOQ@qP>ٿ}~q?oO.7O?g?OgZkU% ԧ7CI\l6w}ٟٟ?|`ooO )eY_o~8僃CIz_Wlz4{c%ScV,MNIIDL" ^J010?\.:ggBlH( ".a8[!ۓGK^X;h!لǶ$$Q饝#'RVR,!hHjv9#d'(h!]!{ӓ%;&I3;JzQz$FA]8!0+&!@vwş]~" WbJ$el7<gn=V,FUk%9JO{yYC%UJ!x}bEkׯ=|x_E*brxmmO~w9;9[! HPUޭg|n?n 㠪jmԚ~(!|/./l;66g}r}Lyٮ/8y|>m'_\n2m#epVZ״?z+wtH  \ IQ EX L%!!9"QH)#$A"E0K8 0D{Ye-]rT-wb%QIFB[&KH$%(A0As楯kja(BЪB LbX:j_g>+ׯߪ* _O.y?_}?˟x3{OG>W^yK_׿jHJr~~{$z+uR9OI,7˲ZCfug>^gӴ;Um}{:=yeHe(P؉Zݾ믾RŎTH(Uk{׮=>} dJXHb/믾vqv~S׮_O}oox<\ZJsH:88x~g~uX|7~7e={*tO>_}ÿ oLo*qUy{ t(ؑ Xbdt$IAHz @H!Q0WڿܜbrV$vH(옐 ђD`T{H$%D)=Xqc/a\;2!NdcL"`3˚d"zwS Ib(("IbGivl&]1664O.aݾ}S?~wKszNdQՆqo5~rvRB B?:<ppwizY2YH{޾?!8# $?o%$ @е[7{ H&%9%Q::>Z_?|PkZ A}Tn^szOݺ}qo5UUR!*R%SI `cb2'ƒe&q//ۭzYO騏n3βcڠVvo⣓{˓i{L|8(2dIC)*{ 1! !Ύ AQJIuR!n*;-KWܻn'!q`qtz:DԐPūyUV!(D"`uO駟Af?Wolz?GK/<<>|t^g>~gt/կ4Vq1O?8wO~gYVIIl?w>\.K_pW=wN?/1_\^jpl!"GL-R;:8>b\^_KRckjXo;Q}yk((v }Odd\DJ ոqv. H, ІՍ[Owe<,ZK>7n>uƵýqoF퍭B@J%EI!vHh䍲Ð ]}QKjFj;h۶fxs썭aXU  ͛e|vy\ܟgK߆+?8!Q%" I$'v":PDDؑPJ$(ʍ"He0IZc)F\b^nqs_zQbRd0q"(*T%u<.հ?@@&JEB ?۷*`Yo~?\;-M(5>wO_W^sw_8>VU@U]v/+k6 1W$]vsܭ[/\~]?^}_GM_n~6}~ez;_[/>oWW}>Vm/ŽgIB-m멻vd =Q4'IBJI&l, *P $*2{;>Lz_b㘮 L)"L)DeTF- WX2ra{e, ;%JAN݉ ,^dhP (!wK$ҴKUJ>[{/䕷^!We2j^\nV(J "碟_ ѵ^sϾg]'{aDŞ9Oyb}En+Rѽ A$ܝz',Çv7V瞾{֍LJG{0hh *eI*Y*)BIPDI.[/\nGΜtU]'0{,v``?z,ުakuMSO _f?y~+W8)Jb+MJ=N"'qW,O @H$EETTE҉*dZLٖ`/cd. JZ P!! 6N:z6}4zi`@$@o|}'yW|񅵠jbPkμ,~:_n6~|~O/~U$/|xgyF ?񣣣H/ou͓'ewt)T~=_zϼ{ܿxo3_C[umϴئKҳ$AB(б %8("[Pb ƀ{{lc'FqwTب$ H$) (p f ܃"+C޺/ AށRҝX!=$",Nwzex$ r/SRU?'!\$֛Mk+NrS5%vԣˋDoy~~³wiFPHQJ `A%`Ǐ~<]8;?p/}[[0,\7L Tx;]A@" E\~R!nѣO'?|'{a[!!  !(pP%$2 )p|M?)M$rϩ)VfX c6/+VVaٶx~:OI'~. tmHJl탇?rtϻ', *@R"֗7t>y3V{v0B\6w`'(e:(<$!B,$%VWHHP֗i\RW ݲ !DD aGD"dl"! %XHT406{7eZ!E IԻqN:1O}wp,2Ks +; XR Y-,KwO:_{w^O4 ܹu?ٟpU"¤"ɔ,=A)%HUO^evqK;׏ QqO)>7^-IH,!Iqo+pHRӜ/J  $ZTHt "+]QPDh\ď<<^NoM(PRY>,=T4 Ji?7n'-KlrY慼 \ ғJqG =@* !$=q6XI(*@`#RPH)QS"01&)JZzwk lw4RkT@ zvL(( Z UU 9}@ wjO}ŜJLIFܤetU苖n3y^?8ƻ{Oҧ^AUZkUII۽?w^LgObYjEDca!82mV bVcvq"ti;űH( $=t"( B/0 JqekvLe 8j;S&1&@B#@B\* D(!Pu25O?S{C@rT@l$rb;&Q۽ۋy>}0TuUM*T= *bSb@RI}vnerG7o޾e)BGѻ^PЊH)*QpQ")!$A%%lׯZ7b77>___\=:}WibWq5ЙEYaB{z{<"@RvJ7u ?"!Djzf\\lN{8qzXBB "Ut4'"J )/6"IR`ѻzzJ0(i$ 8@ R@(T*@jJE:(D!nIOǕnr݃ bֆj5I$:$&=!РhcU5R L*rȽ{!.RRAii.y>iʔE])fg_Zy^r9yU},Ԡ\ZY^t۲cwۉAVOJ!pJTJ H\q\UBh'RA @ D"u)qJ!MDѪ jUTUC=2;DIDURVZWK iCP$6Iԥ(*nn":qdGEk'"=˶svy;_}O]vC?d{?{ۏ.Ͳ< @EˎJ*uDI>o,#`}>M_@LoPN鄅(6I i@#St,II Rm[ye9}UkG@/zf,?_01{퇯HqE 4T x|]o֗v;nqaa!ih˭,lzAbE*%Hsd<ߙ\S U1A*L?mZaC}e kiEJ2P=~W}O߸~H;_O~t\^@aɊD@9IwX`)jռ=SġCH$M[`'. Ak z$BA(6OݸciZ_^^\]J̫p05%Mj6UmpլKݚ` ≊EVsgm֗YNοO9޼@i>I?w&p M-Z@CTje_,v4פծm\i/W2VejݭR iFI*c(%($!*#T@J(!JR\ݠ(X"!RTVjV! "%ND(T!ȠV%(&e+ztK,j&1PtETJEA\lo0?KJw_ћ.f#1lKCBvBU:I)PܕřxN!A yO3;〓XD$N$8AAA1Ď"(6x!Aq"d.ٱ-@!\O$A P M ∪6<=oK?:!=DU<'R!8(^(1NLe/ͥ-YH($,N8}a5D$$B/Yi=o,Βޙ/77n<Ήnܼa @8~Ƶ4yGTNT[Zp $D]*uBT7w>|,BD+ H4/3FݼO|˼<9ypp<\o 㚗꽦)IOZmFW6*]0w/jRkIN~ dr.zryqz}#w*dSÂemɠ%SHJê[Uwecrtx OӺ[U-^%4IsZZ"bnvc%B*Q:(R@)B%QJBRUjB$4! Tm9KZ' )RQ4HUդV!$  ]A hrIU5:)-݊!:H3г!ABDB,$b@2,yp" d374,V{CqO" ̎ efJKuwϼ\vO/ cA숝>mY/I rHzB ; !NDFJ0`*D;$U ށҖy=U`XVѼ;$#!@8*D ppvq'һ(UPI;Vlt,xμxͶOys, 0$i1AT$!'d2/}q:$q_6y;o7[LĎ@yo~;""2E'"%)v@ډ&PЫܶ K #@!HD )VW`IGЮ]5VvmGmN{Gg3H$ $%Vq5 bO/ONVczp4}e蜧.a> s} &xF֤9kX6f F/y}篾;)T–#XM ?Ժjim4gf \9ؓJZKK"(0)đ(H$ w/s>cW"'r,euD1\!BUmhCՠٙg,nFĒ Hڸn'0b-t" H!;2rDs^޼\&8#P'KB`p,hNa(؉hGG}٘9}r3HCCqp'ez6}ڤIԄg8sJrͽ56Pj(ӗ,sһmD֛6 q}ywE!B]!ܹy7aQJ$(Q 5"ܡ,4d=JtT"vv~DR$eQEB@vSC]NڗV7n\Vs߸q+v&11REQUw~J²b[UIY_8::ޗt6ѫﯖjo5ꚗL5~ysε!ؑNYJp.U !Bm>}:4hhqPQ*( vN"323Zkx1ڙVy7 VqՋ8leԪ9î?^_}կ߮힬* 4T5 sݗ~:v>6l3'l{u2{9k,mͶk_9؏^m+]CUIh 6JM*UD S[HjHxHFLZ{궧J BQ[U:RFjP X I(ت2FװcTFeXm[+Ѥ8]+1)(`""Jl tLPZ M]R5vKZ\ݝ]tj`[\Z,UFNhgQtٸ-VGZVPW;At1 RJbEM{/q]f R@hm!3j[jQb%P >=޿}UY-:rMQXU6 >^^}_=5z?"$@"ƢVf7[1EʪUEmLni/YVA\|髯> m<dck )+ ,B-XUI%Qhs ҫ3ZlD^tk7P[m cܟw(ڂv¾aE 6$7WkIlc<ϋ{ѨB NG ^%8_繠!դ`M.@DLe@4$Dm{q|bΗ٪K!tUD\v͚kvӫ{}>uYKi7vTL(PHшW7LP+VD !@+oί>i*m,wjt1!UFb,MTDehHBGMWtٟvi@P\Iw>WQy>r~7/ooPJ7}1IJ>߼HX1irnk?==}y{ﱍ}ǧۗ݋ܾww/sq_/aF됛![AԾ8秇q=~ׯ>Ou]52HLRԨd&t;kx l3}\e=NΓ.dȬl=+/V.~CŌH[n!MK˴yF   TRa$9dQIb˳J]ahzim6JHQdTmUqUUTURWP  ! 2b: If[wR*ɖZU>Ɩ^ZD]͕9BDVbӲt@9٫vPe=㚀:gp)ڽM秺k{ A@UKwwo}Wlpt9sv/{mneT"X<{V@MT@.S ,^SVT6@AG"BMpx׹{&dAZ엎zQ`QRұՖ휮u=+$!B XJFM J@!! &^/?/T[/JդI(l̪ @ B !] 5&PѰX ( y6n7}6:?ݟ_xt㏋իW_*7w}_QtP!X9כW~?Ga-٩Xa^wxx57᲍l9n9366߻?m8<5fk`m $t\+k;o?񲞦yi~Op]10q֨pz\.Y,F.RݐRtR",U$Z:T:TFjT\Umhj#ϒFM"!UIQUԨdQTmI*<Ry6BU9I̪E2&겺ƈil+E-(N9,6VIW^率B;Y3ᖮS^:9w}^ڽmS<ߞ^sm4m[ 燇s-pERVJ* IFSݭ.UݩBhH/QBV gD%nBXCXXs_J@w WDy9؟.5XhV:IJLu5˞]q!҄\T!*dH4Eh D ^Wnñ(I6/ \kS?Xt%p W]f`A0\p52w!$AұXB I/V+bD%IJ|:w~?j̹pk*u8>[H=/Փ|s"G#c8z̯_Oa:Vq{{;X{8Sqy8r<4ׇm!۶cy`8n;\*wxG[^ڵ|e}y_ז\XSwٌ=F<Ju9z_2^s4yĦ4Q N1iTRABJ2RV =MJhDNRP1 cڏO?OOkԯ}7_}r~9f9. mWl mkJ-]ֆ}:4Wzz:p{{ly༹@mV Cnap˗ok/OHUόAө ;_{qzk{mPlWa13Qu8mۛZΚ\|LqG7S֚Uݳ-m5i3ҤF8$?UU2RpK$*XT*#)F[R56*1T*UI*g\ATkQѾ & BI!P!M4ƫrtM"澯wW3m}eZbPWk%(;MMkwhٻm[[-ti\.֑UkGE{ izWz5h&ibB:&ݘ K:!a9I_}p<ꧧEEldw|] [CR|˞f5@CD[mze-[F0aTT TB U\WR*=VEbeg>ކy{s1"IGts|x;-BԀI 8P1+_O> PXĘD"nnnO :MD:$n(Dxqs'44b? o}룱mOOoBr޶⽗_}=ޑbMիkd\k\$$(.mO/o|e:ضUeq7/Zth)bk/wwsͺ{r| p8_r^-^kyز*Ii;b*C JRJyV*A"2 [PqU4tm>|ohCPP!B_.痧bJD!%`舒PxN?L 4F#P($FbO>O~*BZClBalyYeoho߾~ >>W_?yÀ6TWiDCP@xf y4jacAeB [7U"ƦTHRTBH%HRI$bPd!ZV[JlR2j#cRJ]`+T*m0 XP(N b"b:K{o |8O9YA;$|;auc/4WIt{mw۽BuDžY=]ml!݈(&(Wa]=i |#\U0Z5zN(E04ga~f:5ru LR5Rc#K%~_xP#}= FB2gdTa(Pc $CI%$! E jA $Qb"|6 ` ($ QD!AAޱCASQ"r%dO?ii`ER$HHpHJD [?xE\E!|/_|twG?}\ΫWreuT,LcID1" rj~'?Im^vmSVQG[7n|}^ n^nחtբRE^ce䝍w5$bH:4Ү>ⴝtݖʡl+M9&k5mCb*ulIGYk_WAd-I**"IH)P $\\hI3l5ΨJrU$DlvLQ@Jؚ-c^k4mZMw/ 0#z~8XqҴ at߿ &4AֲYv}:]=^KmnmpY;*66bJ8tkI+N$4+c $j\$ze:S$(-ts%sʋvJ b>XO۸kuQ_}5M1,:4I4QBU F"`a:tL-T $^`VW_wZɕB5+`@ B W<gBҢ?gB 1JkF?OW$A7ۧt>bD"ȕWS=5};ӟOeZOG0Ҧ@לCP! ud/Զݥú\6Fjp~r.sqnxq9ٵYm5YMdW;&*bC4["yxa1*P0vil}i}fW_zcctӤ׾fNq%0TH*ɨQJRTPh&B RTIjT¨ W"DګۖnuSm[iiӝ5mڨ- m F41cV\s>Mb/3@%g?\Vw-B^ւFeiڧ+,zڊv7+ =fzƂ(-@B7A9 j%ȳĶJsO+KIļ~1;b@St)+x!<62no?7?ɿR+OOu`5=ew4 0*TQU\e8\$XI-WP_? 5 42u{{- 4$IPDIp5V? 1`iIDB %=<>].DBb,R S-U5|#!DDB"1<^jl>==? uvk{mw+5zQ3r!mQpi[:, bR"TjTFrUH*IUxPd$\UHP$0@B EPձuiݲtMHv&hQ)( {>`.@Cuu|vp5ЬvuKrkynm]4^-frX! рbKb!ww 0: Āq/nE)6"&@BH'Q tO HJHJ@$x. $@BD1XZ;"y||C.WsS56FU :xyIiսV!D B TWU.0R@F0UIOK'?JIP*Hȳ; cO? 4r@@-wG?!!`:TJ&Q["|kN/_ƫ@@H|#D$t\K#B*TB1I@-B잞^}onoÜ3]GO<=Nj;nﳶU.F0tJ;˄](ݔJBy|}w|z cMC֪4)hw-{X`5]$cTs0e1L,\UrEURIFT%p%ڄPIPJp! 3!74YKnbUZYж=* ni+ѽwMmPK@!Yh1ga+L 0Hcm9޼}]XVQƐƐPI/mCH DT҄Iի?v/Fn5mvZ*XB 83cE8{%52/秪 |{_|ۇZ|\o~͛ogݝ^pCE%CT|_&+  GOO~o W! FCī$<3!>[Al *1I==>}r!$AI *Aߐwx8q==<^.C{nOގu8Ëq̇kBx;hVN0T#fA0hy|X]+##['n{XY39ܗg:etUCԱ56jm/OOYk[/U*$]UlU%"#C%PcTT`TZw`@ (rUA WH{v-{u^jŬڭtV- Ӷj T& u2[bvӵĶ1mDyMy7 ]՚-jGٟ`R h˕ژLfPSiD -uyoϿi;l7߿y Oӥ]5FhnO~^< 4! Pt4/>  1reB_>_$&J B>w~~y9+WʕFIx'`DH(qp:'?NM׼y$Ucn/t^+L!BWA<3y9uiOtqM'χ>T[k#;)"Q n⦉Hw8HsfGj3ʛɫŗ+okQ3*݋]&ݵּ8VvF٫%Gj%[jfԂ!00(Au5jT1\"I4 &\ A|>[mKwuծvW.Y}%W)tLV:!`݆RNDӘ+5B)ҙBnwtV^TԎi^8h\V/{E۶ūƮn0,$O│^J1`my&WJr`IJ0 !DD|{~ %"4&M! &Gc<׺1!hA2@q۶NRԐQ5R/O ۪H3XIR%lȀԘ=qk~z|O{սW;yk!ije4{O!Mw&FD C}_ϗǧO?yRh]B"0xsF%! d!JPJ:$~54rw0\%!V@H1*'o^?\\mU[~*IL$nbO>x^urM7O\3ۺ]}b-{FȒ?$$XPF8C$׎0/㾔\͜/_ob r2[$Ym*헱jmݥek[Gz$$U5ޤ ">#VPJRam"DP#B$$A! մA $(>`3ֶ^Zj^[m{ikimڂAVP(ТXՠrZM+ Vj{1q׶WIgqEDxXgϮ g 5a@CP%Q"=gS{PA *1 #1ڞn>qF!WJW~wejgƪB15*u7VcH$R)?koWNjis؎.J+0cRIA(@+mkЭv_K%bH:l~//^}q9qnc'y`әP)RIjԡjRՖ( 9{cZ PQDeCЀJ! W!QRپ[ɿ1J($Qc"2 *#v˹/smw/py\OW#DLBzN@C})oμjӱn7GzN t2Z骍I!k?LR#0ĠDe̱:T3gZC9,4*sejm"JS (ĢމIJQI0!iJZYT͕QEVֶVjg.]8g)ӬիY"W*Ew/@1tzf-$E9B'BTVuK6vall^86.44v,%ҘUr!H;A쵀TtF)( T@ r:d_ J춒 cZ`Lp~P:AIh B2\E)u\ݦR֑Q) R[⺼/*1W51T% H3F.wqEBU#Blgiz=]3 սYjNR Wb=\^n:=en bg<_η7_sҀF?>o[ۿ/>޷߾o|xfն}MgF iZL5?~A@gs_}F&\UPB$)꽗/ܓp (@D(i lۋsߓq8l/V7Ӗ bAjT@Śg|q<>񸽽I}Dm|Ö'XFԆ6Tv;8#!=XJ4Z#G˭;>,NMȐHW"DJj RR# AE"jZ6^te_vKs֜ӵZրZ^xUڳWs2QЄ2Ѣ!61yquwln#>Ce۶t ˩M7 4&-/"R!D%!2#5 6A t~XL03!-bTZ.yUl !I)I\E E!"1+ !y5')C5Ww* 0Rk.@WXmh?lY%cJ;MBR()"A2(n[z_k[z." ^G|ů>'&Ț__Q~~xo}~߿|և/Ϳ7nN7E~Ys-E&p9~sT)0F3@ʬt `"T4c۾HD LPI`cY\56 ޳Ww!Bqfޗ}=>ӹOw/N9pb睍wԘ2GjH*&$$eLXYa .J$$,WVnUuò8 x<dv8xj*؊T%J%jTUމgAtEHhw{w5Q+fݬծ^sf7k5u>皽k{i#НvfӫWZش ʄbF>VObɲEn]6,IcAAh]yqW*# ?cL.( 5Fmpn  ך)&0ڎi;)gE/O/o>d, &BHHYI"E7r&g鲒*!cEBX5jNQ˅!?{GQtC^أ"R`$@)"VU.t (_}ŋ{է@QвRض:<=]~O߾yg IQ6ʸ\j;}T"(si!  PQ?$I"D@ @@V\D x!͛ؐp8l/N~YY!@Ba2z uv}<|?}WqcKyNQjrUe:*$( vLDFIBkFAtyNnGVKV7 VwHe;x:ۨ*++ɰb[eRE AрZmڒb.^ ڍv˵\=Wluϵ>u׾}n\APzu7vk.ZmjE%@uT0CmkEP[w0v: N/bѐ0z, s l_mTU" J!bySBL\A,*CTIh Wi:IbꬹDLPc! *u<$EU%0 UIjdTD/m;**t,lHпwɏ~9bLaKZjIgQP)#C4I*uYJy7w& J@4jpΏOk$/"4I}GiERn"e( 1&%OOODDH" HBDp%W! $ʕk^t&9l<ャkg?ö?o}[>5?~yg:yN9$BH(cr 2HNR,-T "-A %`H*W]&[irSQ 2lcT GU# LJHR i(,&i{uYVl۵kZV5׾s5՗5\s˾׾֚ku/EۅM_16B7J4IuY+/B7 ,[,Ͱb$KM+Wۀ6y]1+ !&(`ϮCDYC`6?aJ U< gHbt6I,8Lf .HMo5tvJ 51mZ@-&m" HLʀbԠR4c*JP@ IQա0>׋5R\)b3.J*GUjԇx~]D4]ࠁFPUQ/;7_].gg:H)Hw MD(ImH`-ݝI_}O^4o_!I$!W틗w_|9C@0I#k%+ObP셟 ɟɿ޾OLٳmYz罿o̵sNvՠP@5$!8$I-v7t!aC؂P>3+֚c|> zN?rOݭf-<qɯ?4$H%BLJ-4 c 0DP$qz.}ۨϛu{{~=Y캬wpɼ~N%ЦJslESCBlZFD"*hJFl͖x{ag96jȀe mlUCʐ$nWVZm<@iV괕2Znt59:u}^:~dM<,'!"!Rc@ ZCl V::DusoIhFBD-)6 "Bk0o޾}/l25PB`mLF$!* w>KB1bHTЀӋdNC.bh5_kܤ*(IC4!\edSIUF1*:$Ԩ2 uQls W쬤cBsk?ٿ'[(N*# @h r0׬4h-H1B0FBjOB@TPCb", Ua^1tRݫzϟYIQU77clW_~5*l DMCA9HА$KPv h'AD9gϷov/a4ݞުntʚnV7yv3?'_dd IUVUFgȀ)K!@1M*H˽mv{Wt^X#`UCSSr-n;'jl, 6PC'"֥m۴Jwce趻gK;zd}u^u/~ZsL#)!!!` FuLj,;U#1{ :ib6Dm )"imTl"(p\߿W I "!t**݂P&AMuNwɆ{Hr@-" at:N* qu$Bzy3v =(!&e&#TXk}}q !# :"%UۻMnXB##5 EhzzWw;?/~" eY`iOm0D AK*!؝DOWs*!G}|MQ<`EPM(HiC-U&Mo۟|)Y* k<͛P+2ͶﳪRKLH! 2@HI@@(mRH*͸'ҫ/|=>s߯y:\ζ;TK$6eCF8Hv*݁ `E$e]BTӱ p`x"CAĖCZj#i!D!#+YC Q Hʨ# P%)Z*cT*2,eynZLlSR)$ Ir{:A"& +JHHJZlc#hjfA"]#cuC[Eo2_bT(4H TvIbCJF_/?e !17|{ U% BH&҈"D| $<HBt|ׯ  vzO~ Nk.}1vmEQkmsXiH rH@M"TD,%&BU$@9U`w_Ϯ9Ϗl/Znnv՝8l1nBae-\US3{\J11 x K!UT(,oUw׊UJwNF6ԨdUjMV~gnZC7Dս\k- ^鲻֜s߯rrs\a}]}s-ZdDT 0*Xv%=lM9$]K!@Lc`:"Zh4XE+Dр ҄ʬ^UUD+"DDe5-I  $@J@OpI%1$B0AlWHvl!DZ%DYXPdE7& 5@@Ԥ5"-$q'~5T!*KJJclK2$c8KeNӟܥР I$$ H:6!)څHtBBI[$`g_ /b懿7c3&6)*`l4м|1L@pի7VwZ}Nլnךs9׹zݯ_}{^g{}5kv9 kJ0]EwQ0B *ٜ[U0UA ;&BHA XeZH!P$6W`hZ1X h1* !@bKS ,շ7#4A&(`R*g}IDc@"Ĉb3:U- . =C*!Rd2%zVݤ@ C* U2C[ݞnz$beaIK.!#6ߺ/~kV+8Dh(RX&9HT+hh [ZJ$rO_W?߾WOcz$.$D!@$A!U'\I@& @%?|_}mI &ٳׯ߬9TH`@  kV8r:mcreBCB!46@Kh I sI :LXxy+w;V% BŚP0$@ʜ> gX`B'HHӍ@6/F ʔC|Xx-jl֚kkٽ]\B)9{Ξskw}]z\9hNe2ˑ!RDpmStm#vIIBTè U,%4 $“$ALۂ" I!@(Tƶ PEBbeL -KI"B $$QnO7c+/$Iw"!-E;^yd-ĢcA*6ƴI]ŦT^k@rXHQIQU$J:jussQVcRdehZ0cT%M{ÏKXB`cH(CU6CzN )0$!|O3<{vO>ZIHCQ iCB% B*xg D  *P DI$$$ `8\j I*77|nm$( vS,9 M%Y!d%k]k w#U4{\"UdH5U$[Me4խMwҲ- Wrk_gs9Zk6s}N{Jrky.=m$dVtU )!)h.Wյpu H i!* G ($4YT@R*5@C @2dTH8Om;"ƖB@!"BB!o..#搊"BxG (1 H C<,rl(!Te GR{nDR 9 2*`䆤*JaWMSHBdD]ACaU?@HǀP1FdCP%Eg1{k0J \E)"v?BIQA21)&T*`F\` A"g}߾y@~;l¡"F$)04! ߈!H* ҆o- !QP6ݗgAV[mۘ|~p:cwsۏN[>4ШL(@9} K6rB+$EUl&#K& $"NիyLf.v<(ls7>,PVO^Y!.Mw Bu]u}߯Z{/˾:Vӽ>˥/e׵Z\KQՖ'GDG,b4)i"P+evW%XR6ƓiH#2FFє6 K$bbX^%h!*iEg߾}אm@$H&:@E4Wj@ 1" $< h:AY|yޮBJKڎC,ג{o_ލ PT%$A&ĤJf2n斌mS4[SMqw{Țk/|_//ռN"*Vc$* I;kII)ژ@꾾zy7?~_NZ!*IbR$do??$ JʆV/U|>T %$ b$A "hR& JMRqYE#ڪ륿⫹7bBo엽v_kwV?ƽoί^;qA",@!bPزKCh@C LRD #1 ѳrҖ%0xGf5_O^nJtz,:MjZWs^.<˼}^k}5j۵˵e]k^:lgw +JJB$tlQ+*jVe nhRIH0CHj:eĦHGQB 8ƖTA񀶉J?T Ih6lhb$$0|Þ^bbx$р0"1r{zLtRPaMi0b4gvaKFu+U&@% nʒ(UaJKJ$UTi4 &n߾z:[)?|W9$vȤ!(% #i,YGTtJqsw/?d|5HHDIR5 *QcX )_~mz O吨PhEFh,$DJD-h$Jx ¶M@y<_Ljݫs=>O7f2׮Vv䫯BZ$|c((% DCCdAPVEPj(P XT2:` ݽr$*O"'"BD)CA<!Šg 9(!xDw^~PBi4!AIQ`b,Cr**e %6jc2GϾkQlzUQ ;D 4“„CD$x^M}I&m2MNu “¡"@sJCPsMnunnrkl*CU4KZ\k]ur9\ךk͞}}:}Y.츰]Hjuώ倍DJ &Ā X!Fm4 [RRE5&БHX2I@$"$J&6hT*IH6YսBB PJEPV "AR1E$ИIPɯ|Ѷ C &PWdсdDi&ڔ2 H/ M*B&45Fb&)W2FVWZ2FF qkKhѱ /?ܴ I$TLR!!QI+dVc oͿusws,@4&JHPڡH`d#IjL (F4n?nb R BIA!*(!@8E@0";AH͞ 0Yk> ڜN"Russw6iD ȡ!2 @#($;ShVqQTZ,!@P“!R"tt#Q&ӣ^p2Uu M8sTyyy;r^Kw9{_s_krv@A ]VI6"*CB : Px $,zB%+e4!fHl9M6* ;@xn(  I{QA' "  BdpH#!I/}1mRQ!ݒc &B,zYin޼z}wW5FIe"YE NOVAScA`,WjFe--2qe*)锪4N'_oŗJ!e, PI6jTҵ{Z6c|{~Ar}Pj aRBCZ !AAHjo>_clD t% $* ( " !B8!1%H H Jb@9d4@51jM+{ *2wxǀ-I8D- 0F`k|\w{0є C yCDt@XMZ=/rw'0 E^mwӽfϽ5}u^~^>^Ϗz/e׵_~2\=hZH'BD$)[ӎ4YFe&iR#P!04RAhQ`0tB'\dph~*ǔ*NWB`AWݺf7;!X cXIP@i"$mo ErP9JREH0@$$Zbhz ȒD D$@"@ ˤd$tY}><=HBAALL2ڪD!@ qmR%@ʌ*?嫯lcTnaNH?>/e9_ruHP\PyRUҺVORi34a$UU!J S$5$&(4־5Lb*d:0RiDRcka+9h% (1 FJT}ڶJɓThHmuj;APA@DD 2 fkecaN=/>o^yE*0ܱd2*!")FUTw/(ڤw QwQF6_!BQ)k%#dmI'ڶQ/?MJ DBgIKQǽ Q1v>x/jl'$Z:onnxsq>엿Odiw/_B!j$!&!+"@H4"OJǕ! "I:#MIXZ"! d OЎS䝍or$bgAh *bi.0 #bȪYA!`8?#BCACC @P$4 D4%\Uwg^}˭9-Rvy.u͛W_~YsZ@Ԉ9(Bna*bI&TUH P!T`k`N_'냑ё&*~'_(`b%T6H!,H i 4F t7cduԢH'&"|nƍ,4 D0bB+ Cts=_YMTB"(@H Iy $UW99 DA+ EUE7F `xb!Вn&)0d:"vi*V6JFe溟S**MW1z&hS L<])UIJ2* #I CXۗal#իGmxگ;rݪZNc_/_||=Fed_.~W_~ E)sk'S:_ܿ~oG(_?j<'pi@M! H&J B #Iu&Cx'N#YP@@ـ4* U4n^^* O D)[QEB2NLL1B>:  B@I8$>㛇뛷f/y5WO=@"B C0"t:ftWuյT,()RTJqHCUb 5LcHCn7XIst:TM*nMV1 A"!I0TI")P)HH"I+^ |1hHc8nLJp1rPI(FL UHX)R0nVC3H'U jcubG:` ԪX$4ě-:mB`c# /={%ӎv]M,(vT[5?}#Ncw{_ݳ߼|;vsڶկ^~}W~'?EHZ\No_BEܲ΄?ZzF~4k t)PQ*Bx^݄CI*i$! (B 94 94lU-A!AH"RMW8eH8I!66e]㾶Ahxgω$b T!!Pb&Eȡ) =Iv"Ra!<&E@" !‚V28f_V un؞|빯繖sڍ@7 BbJF I MKlƂI5)ʁJ QIa((+IUMpJSBnN7@L*vi 6+Zd7y{> jYe 1*b -A˘!#*!'!ͣҒ Q<#M$jIDH AP;~;_˅[ Ӌ$M?}XMVWf4*0tX5<("(]qkh}1 Nj뗭ݝv{9vO*ܻmSA~Tm6֜S5S{uOoZ˯~~ݯZ_?mϟI4/?믻  i%&e+F y P¡}l7eW[I6~{w{y|AJP1!cB|CCHVRzH C(Ԡ)e6oHΏk6d, T.Vk2;BH$Tf'' P M-iA$+Q[(Ĥ#MRYam:-UZ g=1Ǜon%Ww۫1!A,C(! 4UAka\kmU⠆ H*RZbԨ(!muHY (j|̹ۊ41r0TLhI$B *`OR1u`jXh*bų޾9F 0 FH@@HL5 AB81>_zUIU!MOVjTe]{^~ Kӻ,Zݠ*TP14Vz*_0I@w?znO/|{ٳ:zt5Zsůvb_=9_,iVWʂ?bY+%Hus{ޞ  V%s@@ H2aI ChQEN7$tcP_yhT o_yvy| I H+Th"Ƅ'BA1#<1" O$"!vx(*":kTiָ4l#Rt{w6h"hR`b!D3Ph!* &*T0t$b&7^vu%QBF$H F@IՄ "vipIᵸgq,Wux}li==XWtj41 QwT( !)0KZftO!={ ,Ԥ4)+*"6*j+8*PiRVv?ۯgkսчN;ngrOwudQ'G JAۯGm޼az74 vk.5'^MM%v~_}u VWZEۤvURTIblۻzt( $?_]T%@sFm8qH³e[4MϺǜuȈʔ@-``pxaF T Q@PRUEFDfv61WdI\W E*ёS?ߡ`+В^O4ʍ=kE@(-H[Z jTnlaZP3$S*KEuY>?Xs^>sUt=ϴ{OۧG9&@VNY2PV Fn* ,^3}h )(S=tPU(gqsp އt5O3Wovp>;m*7% E rP0jJPٵø76j)8t4atdFbx336EJ\e.) M ) (RK`JE70#`gz3" 0Ro{t}rFcoBQj(B]v_g~˗;ǦŵIyoǿ}~u̹?<q9M!z].C:}mnZp76I[hB P(2݌NI;PR)ӝ޿~{}xr &FG=smj!ō&EeЅ3rwwwԦ VbJi{yjA[)Pn-N3)7j9Ha0 B[BEAZ,/-5b8VkHEB=mu:`tD`X#>'{adoIWMZ:%c)X 2-LF[JRAR(Zsf } ELE(1 `H#,0!-sS=sq1w^uU9.!tWB L+rSBn)*%a&;:J\JBkE`2me3 fzCM&]5ga fM8>~p=ϴni)7ֶPO?>=>Aˍ"Q=xu1J;o_~-O|}I Wh ~iǟ/ۭ|fc\V_~}3Gk3UXfgDnvx@ HG6u_߽y+ #;E"mm k+8(@EFciKÇ_ftP%J>&"`Btyp7(<>>]vNXک(3kV[FJrSˀe/쬵γR-*,`XZ@tP7ujFhJwK7sYMNy^365Oe˼{7VtɚmQXHA*` u:J"/(P*[nWe=itJB]zcJEAn†R fÓYZeڻ>zzgN<}6Oj2`BT*JQ(/P@J`J!adfؓ:(GӮhBU@Z) A.s/PaJO,}sʄ JZAL)7sݿzLJ처9޾ӽO݁8.?{?~?qրokyg6Iw>iN\_璪"B`_O7/ ]2o }ϋ \@e\PG xr}Ff TѬd`i mwׯ~Z0BK ؇',%q4@AU۽8.*cƚ\6Yj E8gљQԊz} e=?~_?㏿uy\&\u>_>}oyC;@Gz}+!TRv*|}?t CV- /7~~*eȂJӗWYrbg 9/~" {50&bYhl7e24n =߼yOҦZRiZ`>G[(նܘQVDs:h 9fϏWEZz>:33k8Ȍ-o߼dIaϬX#UG޼~=#6Y#f=o?/yіZ:]?R@m) - [(ް9ϯoBi r3(8똤8X3:Nr9|c)vyֶ"XԂ0"T,8JA`PP Ë )7m6啼YaԚqtk 7 Rʋˆ|#S电 .r͹]3x6yvwNNbb VBodޝ9ڰlg3ӱLb*o*Kqf]e͐u^/\՗/yztfAR3 yutkg@I?ǥO畳vB+zxd1cuVq龂NU`Bl[ڣdfSy>7_>JEg-P|),p)b^k;g8-DN8dGL\Ѫ@ >?_umBZ47m)t&l@y"7*BŤ*- +6=%}}~~|>?sVn@.( v IֱqҖ6hGk.J}RZZ V@@兀mF(T/:kme #m_??oTGhfΰz}Cb㨳dR|qM(ETэ U< &DXfi ȋup}^UZnR̀P7"Ff`3J.ǵvc8'\nŪPF R*B-)fdo_zHgIeܿWwo<~8VyeQ*lϵz^>_CCN$mҖMZhϧ ؜Rk:isnnJi)8"UV g/yAhau\ybejIll:ԙIݫ߿~uǙQ^)(??rJRd*~~~x/gBg-)6?MvCڤ ѦZHJuVgEnFE;&Rq%:٤ň{ y}Ϗ}o⟝Tr\ –7hR;t#vE;k+FBywwH@ PPK,REY.}=NZ $T /;o޾}-Ek8btJJU3_ke]֫o4q |Boe, SZ"n\R X*7[@bqC[ eY>k|59\c[6"b@fdP(3csqp9|qzpk XҨ` "ҙVAF2,߀78##o_/~?=|:?'y'3޽^O~}x>s3LJ9mRB /gnSL P*T[c.:M3-#Ҝ|/_g6 e !3aa'8)Oc@VnB">|կUuX Ҫ(n]u!ݍ=-3MI;@}zz>}d0lB&>>\߿pIݳMwqR miҴJR]OGk)Ȉ-m:&YfZ٦u?&V cKfrqD*F+-cliT(2Z̺\-7mT(h[$U ;;|c #8>C(bV.-PzӇu>on0KPtZf.kZ\3y*kƛr|u7߿o;<_.ɟ|3UVʦ(@e(M+ )Byah"'3%n)Ё 3=Rb⼚Hb]LLa(7HF(YY5kLfYazbpָLVDD:ʍ\.nEX(X_ L`t}_?'~x>EP,֤u/'|=ϐZ"XҲ& BJ,"PݛIL l7 32et{^VSnPn˼y}8LK*Sh;س_T[-҂Uv_/˹3P(]q(ZgE,*:( ^x6t]IU;m7!4)&Ia'􅜽[_'v iunnRKIOvhgʐ0=`y~/sf';OgLJǽMiM_߽"j@qJ|ЊRHYJgtfnϦ|(Ǭ8z ßUErla۴H)R*Py围{Q):r8FQ\.Ke\Y_y7o~~> |3Ka+R*`nL+pc )E#RL%`鍌.*i7^bYRPEG7MHX*7"ܵs.`Ngg8wY[v5uzC n*yєE؍Z 5!]Gm@_/??[u :HYԴOwY)qc]6m7B>2:mX^H[*zNg,t-]v[2V;}^J9٫Fh`Hfz*4`[-XEE߼}F,=c_?r6%[Cb;(O߽e  B3;MBivS$w~PZ(ЄBݴ|͗D]MbIdLYo|y4)h.:dKU(P(TnfgcG7ԶrS@)Zqte`ʲ VgUyܽnˍH:NW4PV+=kh벙7f//6X ip[P`Eˇ?ZJ(Caf*8cm #_>~_?J& $t-@NK -Z7Rxz|]{uK дXv_OHIkN[0xx_>ϻ5sƐ积maPK ZۢPE2oʍIt#5$-L;mgrmsu*EnZĂZ򢊴U@i+M`9Tqͬ@׬e1`<7p?o1woIiZ (AlkRXƖ fXeAS2B3?+Õ?Za@2%b)2R@v6=sav0lJ_9wv6HAb VvLiblOfࠑ)P@3O߽a_ܴ 5q.)B)BlJl=>AƊ;(]Nɰp˔$T~(IfZP`9!myZd i+e ۴)vx)bEZ %ٜx\&J@Uf@+58|"a/&--$-i郢36|3Kzm"-fj@E+RT(MN)߈RJAIDiK=rܽ~W~s2|cφjԙ:0.ѭAʋRKqw֠ QԦ(]EN DhEƔ:J6˿a]'Ȁ"(L )@"KK:I\ cd;M-Fڀ*7-ZLAgF=7vv@h2]6PQ &<_û~x__v)Xyݡ2nZ(sL+eڊmi;`5gљY"~b*a/.|wc_l1Rَ9;} IXiB)7)#Јo.<=@vpBo_|n]t9' v_Ywo^O?6tSz)5LQkdM M6m)] -$ԖR;)RJ:%*)]-dak{i_7i mw5g䅚 ii |y=KhKZ }u8\cVϝ&;n^d;k3PZRZ-bRJ R(iBMˍrVGM fu*6z\:渻{/~9<;:MC].k(g ̴ZbAK(힙fFhiڊUqO^>|>#h02b @rS%0V<9w_e-/pYY"DRADK PJʍh"7-*+¦hI&qou8鶤NSϟ_y~ gerJ3՜CT^t>vEXԥ 33C9etha1菟~_=뾶b() il}~~|e qJEO_-E10 `y|x|Xg&FYPѴo͛Ђ4 U5nvH,&(I+-7$Τ-76ENoaOSϓE˚]%sxIltGъ̚a7n֦r@@qW~|z|xϟ>MZMˋey"B bQ@nPmA)ȍ2 -xDFrǝ_"k,aPJc9n^]7o^~Z[;x0to7L)LI -e`P"J-) Z Li fXݰQđ=0' ?}xG_Z)/oRnl-/vi8;3s{=BqXz=k)#Rp@(Xn "T SP?kVt/h,ҝ}ލ2Ŵ;3bmD"IS臏?{z^C1k7adH::ě5P,iqJ2:t`Yj:u` 3K4e73#`UL9޿~N;nВ!ii7Oׇ_?^L1fGR^HoL?}?#VƱ-B;ڈ e9g%{Dnpy7wrŔt\f~zzNmh Ԗ)wF(mH@JL`ڳY@u'sMNlq\.MAo߾siuX,Bϯ?ԀF-:4li*} bajQđ  %KeuRdXhq?>'g"t閲3YLhp\̤5.B ʍ4?>Phf|~zYz dNL՜"O?I8j;4"}{뙳Is!KwckJ*X R"T jݴVUn 5:-w[@\ _w7PiǹJa,PJGIwjPNwIv?~xWo_බ@W~Ŷb rSKA@Z^T+P3P-2@ ItkiSV+*(hu_/\4Ug! ZnJ)i6  0`J 1HhiIiNtLݜ JG ,%hE>}=v&RP3--)-v٦I-鹏眱搱5UShiET^ȍvXRBZQ $E {w;nM&{&mBs|\bwj=ƱtDn X6qYwwb.agQpd\tفqq5Y19`ˬ2ZV.<\7u:nsf羞9y6fonIBNsӦN00jmA2u`*T9j~ڝ6`/?d D ⫻WOOORBn7ݫkvnPڽ{'"}>=>&{y&6q?|3*"i+yJ%i ߘZZ( 8˻{mҝ&-M-vW!JK cfrׯ>43.3\uwu\d8XbM( P@KʴaDKJ (.P( Q)F-A-7~>\~E+Xp.%JcrSSLit+z N)B ED()V ڪB jqJo܉W0d3BVn}i Y] ɞ}u6%D*TPKk ApY38ubHe.pY{άЮRg)3뻧iqN#ᛊ->}4Ո)b5Ƿw=Vl) ֎vj5AlSRm>wp㶴?kΦi)mѴ܌7SvEB)mĖ'vm0RYw!P׏,zm!֊ %Nfz^w~~0f4huǟd b@iqr>__ XiÍȔ BAUq$}||(M0c-Ӆcw×/DZ̡3yٯ?4c-9BŔ(PZ 0J[`*3l`iH7Q@hZ:,Zy*[x{)"$d{II"V 0jG(Mۧ~x~S|l@FyQnA V B҂@+$m4嚞a5 ;i}ӤM;m!3/_>z}8\^޼w_zU׸Y:uZ|}z\cm-]88 `XN:{kXcXr][=,ewsYxYkZ.fqY2<=?}v9NF:2 BG|Sd MHv<{&;MHȌmmPLC79M^p>gݝض6;mIb\c6/JJ-0 3|Mw&|}:@E:i-Exw~,PZٜk(d_Ϗ|>6w^{@ 2`ѯ__{Z83*ހX( ^*(-7--Pn*7ji;:J𢐲[N FrzO ss̰5Zy>}8˒o,'ii TT Zڴ Tҥ Le,.T7Y6J3]P THz^ ӕ~Bӡg;@oZVe4.ni7X6$[bdLPoŽLD v4gj!)F^5",)tmBP]yPM7o߅m - 8sg:fДb>}| jKעv>~~X &sWo^ -ȋ7!2jEŰw+l" %ɹ;3֬cxuL2tC:ֱFmγc6|s=1 ı ȟնh%58QX+ ,R '-# %" $ +"iSH(–oZtj}K۴f :uʋBA*mPV((O@a*b) 2BEnX2{;9dκJjl׶u/~Oů_>_]=Z(QZ~x>&,@:ΒUXSF2)SN;Fq@~#8&9W)E)5rJ󺺥Rjb))(`eUR*Đ7o^Ar Rn2:L52 91bi6M)qNkwYy}ݝ8S0tBçGG֫W|85P"ӲI:Ph-x_V H_ן>|HdD+V@}rw3H)`-ʍrc20:Mn(BZyQjРsyu#ǥc͙-9v4,̤! TnHMmڝ5BXPSZ -7VB7B鐯WY~'K$:L@V-d̰f,يt Kj(* UˍF(RZ?S(lRuVݳ"TRYtv MRK*_qy? V̪sptR کәt3 ftY2 qAљ5/@A@AiL_&eM+"ɌTהYswvckV'D`>}4hq0",oo:e\DedQY: r13 PTUV$RltniP6w"fVa~6M-A,xA(n< XXλ}uYRg 8, }||?ÔBN_T 竻{-Xhq:m! I~?&"\߼WFj)-T3esB)@, !t4s=Ly~|Z 7ؚv1kͲ -Ck ^nňZAҫ&E JKE` Vb[[Bgdhah{r3MiUPJ T % `#RLgr7=#4TEnPnĈ!gfU[Aڊ R*Pya-HPH{*6Lcրj25T;I)2wq̙u̶9h+EڢW^}Ϛ oT_)(Q: ߼{Gfq/Zkt03(hZ`?|G8p-P6mf'uӤIZfVu 8㲎dYk] 0l8t0#o޾ `)PݫLAƺZtU@AYEIS`gt5k H~~gHf1(5bT_<ӧiӜlkbzcBhADL;_~_~4B}ؖ-Ӏ,H&Wb[_th!Ŗ j@njAEZ{~]OhB_/>4kN\ㅮdڱv ;V]ϰC]Rb/4Ю"PHځgF*@ږ[ӛ4imC9|;~`K) H۱.ga֬W_]2.\EP(ʍ  +ȴ(i-六`)MRқ6}fg';gr\˖` Rͱ]v q-Wǿ9^qK.]8t`<`0x;o{}//sq0s9g,Y8KaDQ+eEE\#dLq]\.άY̸py̬Ga5+\/w3R0)U"jlHiJt}ihiʚKgXZs88x̨̺kYz7H ŷ}Y+ m餤i8zjh18k_Zum4O;cnzc"2QTBAm==?];~vl( bG*YIYdDdDc1>c8GU^ݿxWPr}ϾYiz=o"7TZb __nyvOK‹`@y߿~ڪhSIHggU ;˽-E[hHLVZ>ݖ" 7lzBl)?5kkLJ~}ss{M_g?wq1)ҡ'$*J%Ԣ -v8 D\ܴN-AeiVoJˋB)Z퀴%i Ow9~.O?ԖA[A-XJDkwzWz;yzhRJ)j) D?Q R ЊP E)IiAaƤet"dClۊu<_=}Ǫcф=uMjLJ)ϯ=ܝ3ĀŒ`HbMMDkLy*+ mD M_*Q$4.4L5qlubӨ$+ʍ2!EˊRt}?>=nNPJ""rvyy},ŔMQmVhѶ3[=qq p=[4ʼnRۺϳk]rr9Fz*ZB7k%=rIgwV`Mo6 rӺS[EP1F\.?

49iEn4q+M)'7׮$`*eԘ&&F h-XuZ_ve).RkHϣ#j3JP@A*a,ӳś*B_3Z+T>|o Z}uB8XN<͛?XKiK1Ӂ\Edٺ[fS^ҊB V(J)Z1А)(P8@4Pf:^:~˟/_~V^4C#׳GVݫ/8#8(c8M,vUzI@;4R+Pufv4-YVn Eۊ#)4ЂE:: ?WEOlJC!bx-᦭k{f*{اg38ϏOkD*I(m+"( C-ܴj-7ci+7ve혌7-SullPl>>?~3uXZ 7􆊒@A>>O~`DS,j$1 m`4 1( .8,ׇ]XIq\;|x^Ok&I:ȍЇ}?|ʨAc?|/~.EZ:|}]VR入|}Z g>YtKš:uB4NFʋN"b,ԦJrShpf%yBѡBޣXϧdl:Л:.?_qsIthȖ`BI  4ˎJPCyQ @&:vlz&3Bq52騅0JL[L7= "Di+ª vL Y#0 qQEE!1OU'uRJԨ%(7IjBt-d$HPhSf*)iʹb]cMhM}A`dѪ1AM Ho?~tRXNESK踎 8N3DXjmb΀ؕc!xw\fF !s]ryJb[ݛ2lD( ܿ~kŖK4PϾ綛kgc;]+ BQBcY+h帜s-7 ~?NCjC߿{_*Ӓ,Do>{vkܴ `ARahb- 8~>_y{PEW޼yrڻtxQe@pRSA("[T{)Z(Z@>ig__{pp7!րs_o'cb q-mqKj3DV7D [V-[n lrS ttu]LPZZ(E03\ٞ]E"F R R1ke-=.VՖ* *(VRPkA%0" :J1.h*A`H!̕Ƿou :Uc:-WJww 6`Wo4$VIJYqY Bi" 2`P&8DP̦hT l60i( A N.w%zg30V]=~gZZ :-8}Z[(Q feIt(t~O!Tvi;ywb$HyuA,Ev`f秧/LJRk͋EBi Ĵ MA"TZi7 TEљXŊ|Rʟ,ro?~vt섶Xu÷/πO8"RiXAݔP([0I[+U@m-,Q:)% l&Ee1N;J3PUHy!(@c[erhvZ]'\9WcafkpNi jm|p# / `Кdɂ055*i@jH]=><?oN;hm 7Z+ i ~{uE h F J p3P"ЁhK+8.'Z=ZӦF6J˲P@l!ڸ6{.uvVN;nE.2hI 9kIg:ݳX.s3's; ˕cX|zxM-"7Pn:H EFP :O~R;%lr# ʚG׿hL5e*칖rS2N\ןp}Rٳo޾~0@0P Z 1J"RP$=#J,ĶHXl?_K{%gsJְb+qg%bX!|23W;4 0#2бZlP*1Ni.jm-Hkiˍr6b3-6BXeNQk٬sd'r${rJ}TJ€(TOJ!叄AXihJ4%"1bie +VJMؖT2h:?>af5c-EBȋVb{>}&O&FPV@ f X`)&@n"AEM_~g Fwt ݭf:Ӥ6!PZr, RJURpUڳ/fkMoZցW݃o_z.dDZ;þ9q}>g5%X;1Xlͫ7?ExÍb pw9|b& Oo+Ӧg]' ڄblPaxiZ@qjKN>ogsu8g<;:朶XʍPJ(%0 PM)Tb)U;bFuLJ~^ٞ\:Y+8/l>9$ X4/'-TA#78%e%zH=)ZA@#Zq,_Uc7J/-ܤPKpPzݾ<exD0k#sqww{sw$vi҄I / Hy!/ rJ(7"mFm$mtalP4ۉ6mVѴ#0Y9;T_ߝr CL#/BvR(/ y?}qrdWD6(FP=děbLhbT9V j XHMQ[z -uz.%a2 I=KmⴖԶX@9B* F܏j\)̴3ݓc}vyyk\]σNLM>okr$eϴͫ7'_k:yELP2~/(7&!~ͷ?׿INjze2H5-JOoiJu 0߿yٻwn m!"v6v挙ZV/OW=E7@!pׯ?Њ"@*`-m\mgv A@(r3ӊ:x?~^+OO]糈3^WH擃OK`=;ةKY0T(BQ։, B[,aD)bY(E^<.?/;Z=b2fG8x;2>sݽX0GBϢҖ 52Rĕhdij!l$ZbM:Iӣp-tqbBJBMt3.ǥ{c?JTvQQ$,DZqבY6*24jsgOB眧qIr\2nmf,6" C@ #m ( }Sir=3?~]Úsس7S:j]8fNqdΙt<3Ԫjyk]4:fd|`B)SN6#`) ؙ~Y5X Ei?~/) ZZ=K"Tn Orw̅~||}S mtB[J(JAD"-/ĚD#=( iJ[&+k={tzy}ff#:{zLg4'ԆN$3ejJIJٔ-/"F*BeCҢP昕Y2[MWK# MLFfP+J& /* Ea}O|;F.p\Z#7_{\;kdb-fwdiBU( Z* *TbY].9"ք_H@ъ.3dNuxw%4P`S)/4:bhUSJ~!QQBT0AQJ1h^w"L+lRg)-y}Cػ"5㲟NFŐI!A S2iA)4-X~CWԌg>9>|=?=߽u]]љ2rwz53us?|x3~Ï Lo7VRUbuBt顅k3ݏ?/8Ef:˴C:~ꕬ(BrqB fv>7ݝNByQnZp}W4j-7l) 0G"l\brӽ{BRZE"*جu\.-ק{ ^2z9ҕ잻{:uO>@dgwB[nM Q@ΰ(c[Kq\=^l+a*, c[4RZI2Jr3Bn[^bj?7 ܭu7<^jX8*yҙgsS* P* TA`T(E^H)e.fIp YA%B2"F+ Eמҙ&W_qOU1)(Զ̖  `T *Ki7 hpM鴢؀@0s{a().c3uSjӜ]Vp3 m5{"CW_wGZ`=K7 g._|x=qrqZ_?y^}N-3|뇏P 1-] ib0|dǚsAǕձGS]9iJi׾yûH[{#}xv5V%N-̜^ݭ )ԙ=۳E ?~~|"R(r+vrӢ)"݇B-Xg޽W?ʕ'Ei\^zA RE ,ɞq:"7E)M[@A}>8N]Lіٳȹ|qq8\l哃OBƶњŀ8 Z.NZ$@edr(Zאzqu`XE*(BİcMv] N;Bg*/ս!{*rYy}w=^>S{y|-ӝMFܘ(兘Fj1BQ1ZI]d2Ĭ, &YKQqe1` jb;=YLXƖ:3Cr훻qa#!XD+V8&uFאU϶elObJ+xs?^t(vf?2;̌R nŴôoÇIb- /Y:PTR?Ow?>?{!qw}%_߿kiVΩ^t8~ӧ-톡Sdh?_G" `ʪh|^_]ϝ38MJ]2X9gDj5ׯ_"T-*´ƻt-R "hz.}3;3|lZh h[r3=sUL;b jM5h!t67&RTJm${:)Xeіezps\\|w?ow{,|bKhw"=Ͱ(jIᏄbcJ"ꬍTm82JBZ=r= ,X ]"oJR%v9%GWYkue˓\X\V2wz=󼗫Nm+ h TA(7HZ"兦qq4ue$f-CBJ2qĔL TAes>^S ) YLjP.wouqP&YmIXmf:JA.ڢ@ B^P഍7J3mDd[(Sat 54:2o{,X܈7Wo^_ r R02quK DLJbY0"TQwwMś6PDQb;lWww.dOo(\a`fB"-BϾH8i2tlzP_~}w{~zUv.}zuHrw^(DUl(o?կ>6lRtԚR:OO_y~~Y($0 y}~7|"tF쳷O߳)N^z?ۇD`h@ ^k-AҎ@A@۷_';N-\Me]N(K|"̬c].YPh#ի3 [@^(EH[ 銅P@,T( MpsX_n㘬g|2}Z܅X # CXS-eƘY; ȟl(5%͚uKR %aS7,˰*, %#!a2urs]'mو'JA]ZI:9+c-:!e^tː\Gc=v*7`P T@ִ8`*7&әE#.\.pc=VVY!k$b"7%謲rBt FENn u+NL)zW0P"i31T,LdȴPD wǫ (DjSkLJ5; pB3nEp4UPee_?!=y~^P?o7Nmɴ\7sMV<~ͯ%]&1mmw=LB+RF~xOc" M(Ĉ\+)JHG+.(=׷ؑN쏏|o  2EVh兮Ҡ+usrΞ3 Z[JR@ rSntyW>=n?=0PR*(кA@˅=s=Vl;TZX^u|~wy;/+%??W\IENDB`spyder-2.3.8/spyderlib/images/vitables.png0000664000000000000000000000250612626055324017323 0ustar rootrootPNG  IHDR szz IDATXýW{lSU>+f!D#0J0 1!(`,,DA#3>pL1sfً==u}ѮVwv;"s{s=\L˻$GqI+poTBEM  Vl#sඪj͝ %mKth:fcZ 'lhȎNc9Y E线:򑑩ՙq1OfWozC'a4{^b:`yT!>"ٕ &KtoXt,x#yG2E3D/{c@RaAoNF;WM-hz2]/X\Lh$`R<3F'.bM8~`- 68x/⃃ĥl'RC\Í?#qS"_`@$xpsڑ/POMس'4̏@ /S9N*Rӡf; F$ԸT";ii7 g(^l( t#El8OC:V݊p ,\e?;mےpx1v+'z.$}Tp˪¢Wt蔢qjXi$-jk$ķ_pIvi:i.\sP)TQQpIFk> @ZP3IENDB`spyder-2.3.8/spyderlib/images/winpython.svg0000664000000000000000000003326712566665770017613 0ustar rootroot image/svg+xml spyder-2.3.8/spyderlib/images/spyder.svg0000664000000000000000000003414412566665770017055 0ustar rootroot image/svg+xml spyder-2.3.8/spyderlib/images/browser.png0000664000000000000000000000155512626055322017176 0ustar rootrootPNG  IHDR(-SOPLTE#xU,bV\cH|#BZ&[55cN8DDE HA=/\(|96YC"|s50f ~|#&*"yevX(iA "q'jxq Q:}dy4~?ara]{]6^7DRtU$l8l5CH5c}Ih|x:p ]]K,psd<yv}hu k7@\.4@jU*7yHPa+GE<&p ~d`9<*tRNSp: FKuGЖGIDATJBa$ h&4+43 BKKݥIΑ$_"#uXr~ T:{ۃfg$$iQ&@{/T]lEI*UIENDB`spyder-2.3.8/spyderlib/utils/0000755000000000000000000000000012626531443014673 5ustar rootrootspyder-2.3.8/spyderlib/utils/external/0000755000000000000000000000000012626531443016515 5ustar rootrootspyder-2.3.8/spyderlib/utils/external/pickleshare.py0000664000000000000000000002555112626055324021372 0ustar rootroot#!/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() spyder-2.3.8/spyderlib/utils/external/__init__.py0000664000000000000000000000127712626055324020636 0ustar rootroot# -*- 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) spyder-2.3.8/spyderlib/utils/external/path.py0000664000000000000000000010421012626055324020022 0ustar rootroot# # 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) spyder-2.3.8/spyderlib/utils/external/lockfile.py0000664000000000000000000001701612566665770020704 0ustar rootroot# 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'] spyder-2.3.8/spyderlib/utils/sourcecode.py0000664000000000000000000001100312626055324017374 0ustar rootroot# -*- 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'] spyder-2.3.8/spyderlib/utils/encoding.py0000664000000000000000000002005512566665770017055 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/utils/introspection/0000755000000000000000000000000012626531443017573 5ustar rootrootspyder-2.3.8/spyderlib/utils/introspection/module_completion.py0000664000000000000000000002515512626055324023674 0ustar rootroot# -*- 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'] spyder-2.3.8/spyderlib/utils/introspection/rope_plugin.py0000664000000000000000000002637412626055324022505 0ustar rootroot# -*- 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'] spyder-2.3.8/spyderlib/utils/introspection/jedi_plugin.py0000664000000000000000000002430512626055324022443 0ustar rootroot# -*- 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'] spyder-2.3.8/spyderlib/utils/introspection/__init__.py0000664000000000000000000000044712626055324021712 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/utils/introspection/plugin_manager.py0000664000000000000000000004220512626055324023141 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/utils/introspection/fallback_plugin.py0000664000000000000000000003131512626055324023266 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/utils/iofuncs.py0000664000000000000000000005041012626055324016714 0ustar rootroot# -*- 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'} spyder-2.3.8/spyderlib/utils/qthelpers.py0000664000000000000000000003675112626055324017271 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/utils/programs.py0000664000000000000000000003001312626055324017075 0ustar rootroot# -*- 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')) spyder-2.3.8/spyderlib/utils/debug.py0000664000000000000000000001112312626055324016332 0ustar rootroot# -*- 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__ spyder-2.3.8/spyderlib/utils/misc.py0000664000000000000000000001767312626055324016217 0ustar rootroot# -*- 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' spyder-2.3.8/spyderlib/utils/dochelpers.py0000664000000000000000000002700012566665770017414 0ustar rootroot# -*- 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)) spyder-2.3.8/spyderlib/utils/vcs.py0000664000000000000000000001103112626055324016035 0ustar rootroot# -*- 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('/')) spyder-2.3.8/spyderlib/utils/environ.py0000664000000000000000000001213512626055324016730 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/utils/system.py0000664000000000000000000000464312566665772016622 0ustar rootroot# -*- 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())spyder-2.3.8/spyderlib/utils/ipython/0000755000000000000000000000000012626531443016365 5ustar rootrootspyder-2.3.8/spyderlib/utils/ipython/templates/0000755000000000000000000000000012626531443020363 5ustar rootrootspyder-2.3.8/spyderlib/utils/ipython/templates/loading.html0000664000000000000000000000521412566665772022712 0ustar rootroot

${message}
spyder-2.3.8/spyderlib/utils/ipython/templates/blank.html0000664000000000000000000000061512566665772022364 0ustar rootroot spyder-2.3.8/spyderlib/utils/ipython/templates/kernel_error.html0000664000000000000000000000131212566665772023761 0ustar rootroot
${message}
${error}
spyder-2.3.8/spyderlib/utils/bsdsocket.py0000664000000000000000000001344012626055324017231 0ustar rootroot# -*- 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.") spyder-2.3.8/spyderlib/utils/__init__.py0000664000000000000000000000034112566665770017022 0ustar rootroot# -*- 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 """ spyder-2.3.8/spyderlib/utils/windows.py0000664000000000000000000000274512566665772016771 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/utils/codeanalysis.py0000664000000000000000000001625312626055324017733 0ustar rootroot# -*- 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)) spyder-2.3.8/spyderlib/utils/inspector/0000755000000000000000000000000012626531443016701 5ustar rootrootspyder-2.3.8/spyderlib/utils/inspector/tutorial.rst0000664000000000000000000010225012626055324021277 0ustar rootroot====================================================== 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. spyder-2.3.8/spyderlib/utils/inspector/templates/0000755000000000000000000000000012626531443020677 5ustar rootrootspyder-2.3.8/spyderlib/utils/inspector/templates/usage.html0000664000000000000000000000164512566665772022721 0ustar rootroot{# 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 #}
spyder-2.3.8/spyderlib/utils/inspector/templates/layout.html0000664000000000000000000000466212566665772023134 0ustar rootroot{# 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 %}
spyder-2.3.8/spyderlib/utils/inspector/templates/warning.html0000664000000000000000000000041012566665772023247 0ustar rootroot{# 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}}
spyder-2.3.8/spyderlib/utils/inspector/conf.py0000664000000000000000000000755012626055324020210 0ustar rootroot# -*- 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 spyder-2.3.8/spyderlib/utils/inspector/sphinxify.py0000664000000000000000000002027012626055324021276 0ustar rootroot# -*- 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('') spyder-2.3.8/spyderlib/utils/inspector/__init__.py0000664000000000000000000000064012626055324021013 0ustar rootroot# -*- 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__))spyder-2.3.8/spyderlib/utils/inspector/js/0000755000000000000000000000000012626531443017315 5ustar rootrootspyder-2.3.8/spyderlib/utils/inspector/js/fix_image_paths.js0000664000000000000000000000126612566665770023027 0ustar rootroot//---------------------------------------------------------------------------- // 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 }); }); spyder-2.3.8/spyderlib/utils/inspector/js/move_outline.js0000664000000000000000000000116612566665772022406 0ustar rootroot//---------------------------------------------------------------------------- // 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); }); spyder-2.3.8/spyderlib/utils/inspector/js/collapse_sections.js0000664000000000000000000000421112566665770023402 0ustar rootroot//---------------------------------------------------------------------------- // 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); }); spyder-2.3.8/spyderlib/utils/inspector/js/copy_button.js0000664000000000000000000000556412566665770022252 0ustar rootroot//---------------------------------------------------------------------------- // 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); }); }); spyder-2.3.8/spyderlib/utils/inspector/js/utils.js0000664000000000000000000000266212566665772021043 0ustar rootroot//---------------------------------------------------------------------------- // 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() + '

'; }); }); spyder-2.3.8/spyderlib/utils/inspector/js/math_config.js0000664000000000000000000000446412566665770022161 0ustar rootroot//---------------------------------------------------------------------------- // 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 %} spyder-2.3.8/spyderlib/utils/inspector/static/0000755000000000000000000000000012626531443020170 5ustar rootrootspyder-2.3.8/spyderlib/utils/inspector/static/css/0000755000000000000000000000000012626531443020760 5ustar rootrootspyder-2.3.8/spyderlib/utils/inspector/static/css/pygments.css0000664000000000000000000000623012566665772023363 0ustar rootroot.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 */spyder-2.3.8/spyderlib/utils/inspector/static/css/default.css0000664000000000000000000001614412566665772023146 0ustar rootrootbody { 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); } spyder-2.3.8/spyderlib/utils/inspector/static/images/0000755000000000000000000000000012626531443021435 5ustar rootrootspyder-2.3.8/spyderlib/utils/inspector/static/images/spyder-hello-docstring.png0000664000000000000000000001227712626055324026556 0ustar rootrootPNG  IHDRjIDATxw|uwvfPCQM@"H5("rT)x"Dr z<ܝHGHPF)b3DA.O|xm%kevK7y2Kx xb,p<₂:,rߙ¯ "fUL$00+wGuBKe\AvӶN_ޝ"Y4t;?- VyD,+vB7tY"bĸqn+ WuUb *2 ""bDBqb^`uV62!_9, 4X{'\&"!v]p,|NH;g;TWc׼ZXcHnEџ+/A1Vf6/=-b$FkH FUUjȝ}wd^'OeQ2;*Y'$m˙ (>qFnuJD"Y:7t]{ϯ` *(vCz %>ԆͥΨX=L^&գ{Fkaﳿ侊DQB"ӈfV" e{䃬svqO*K$z*'=#l(\;6Bl25U7V✑,\b `].^I.U(Nd2+[ bw960H͋tnrCD7yQ+W۞+9CT/BDDF Lab(!ݭ鼿~"" \Ι5gcÁnzʑ7g%V nIF>{_3ApeYxlm7Z(納)VITҳ8E;Ip+r;&L&mҪzU @'XDDL"ƈ|#>3z#&ɲ~r-4MSn L2 ]02t He?s7&a'Of|W.a0${B6/\b] A H4K_|(m&^n X$?J81` ,2`` X` X Xc +$x⢸B8zQ"Iz͞c#?y`_GQjsN{e"q#كC,edH@YjCh//Wo0i;IHb:-:سQ$-oт~[#gΞ'}i_׭k7V%F/IdT)Uo$N1HaD_$_пkIB%+(^9ׯK?<|pܬ{p`~~ď_ G);sZ6YZ+zsNC&/-Ԉ||{ER8}9b>Ʀm2wZY5ID)=zMգ=^j:t0/bü=NZvGv{[]{=v{=iTrЦ߷aag^/rC0);u[&1:/:xȌ5`A[SD>E=_E=.Q;Myjk~|N| QMRڥYR񒤔ФӤjDD$K͑#GM_3ٹǽ>ڥ ͹Ӿw4{l~gW6X֍C'_kmV952v|^V Iˀ;o9w O[q r,u׳C*YtS^L k\vSUکSfe3y~j$t:bAǟ2k"lz6d͜33qVfb],3(eΐ7Xix,Z,jx|IF vL2GRnM'[fڵEub\{0ŜWU-g:Np`&=&קvU%vka?S{ػlݖ(ssN[~۸30L#Re<ճW& XUg^ټ}7sZuzzPcOAaUmDDdX7s#\@O%ut;yxܵbçOS+e$-R_ZٲwWLbs97g T~TgLqɏMMMMڪ.|zuPWAu " @/YڴmQ?=wN.>QV|hаώٮזfX:^pnADQ# "yC'NT;=R_[^$׭f4h݀4 ߎ~!;o;倩K\&)[>-R C7d^?`J^9ʯ;ݛ?Vseim(j\לe瞺GlUz:{5w'<0'kqO;7~eÛZD%9!(#"~.J;瑈" cw|u٬o {NvboOJdpͫt*1}c>xgM{0ooW"Yn*n#"-䋐aK~ _l3Pvv>]*9 nQMD$DNDGCYYarǹf# h&]5".*EdK3+?Hp6l"AݥƮ"ܼ@4$[l#@P;g zuf6._Jq[m6Xq N{>KTO"B>M-2-vI"#̺xYW=⬺lZ$N{^<)6OS%tɕX瞿Pi\T⴩DD!f El(%wdDB!8,Eoca6* 慴b2F܈Ba%@+6*Q4q;J-,g @@,@,@, ,  @@,@, ,  @@,@, , ,  @@,@, ,  @@,@, ,   @@,@, ,  @@,@, ,_r;WIENDB`spyder-2.3.8/spyderlib/utils/inspector/static/images/spyder-nice-docstring-rendering.png0000664000000000000000000007537112626055324030350 0ustar rootrootPNG  IHDRtzIDATxutް ?ff|^l2YXlKfLhfAeeLE*f*I޶HzI\! X Ue"6*\*bm>⫈ "6X `K*bY⫈ &?LfM@Al0~g 3i8J V52^7SO%Rq+?2pXƫ<1zxTtJ NG6^A>F6KY#JE&*"g-N `;1|:_IlAz34Q>5I-2pĚG\_CA4`.s+0|QC!)I(PAf"@$zRJ!<f8b@f>+ǎp| G 0|TKRK[)zSL œK8 /Z_/*6 S!K$)^ .NoT/#@B'̒%, je..%BJ|ap2"2/ ## L\:nma<"Rb3U>Rf( qS%{5__Tl@ZUud!cf& 7s }.7Rmjąo_ܵIxE٣{S庣4Sp&DWf!jܡ t*M}=U$Nw/i'na! ,Ky΢B26hHn+AEE?=iLI2ėq 2pjO:t&^&(uQ*, E( _i$?GT:rEȅ%.nMŢ JCj&O+ EfQIx4fCjOU0h?Zlo҉ \fY2{&V~_V|r5ė4p)]'jMNM-n{ BY5'L5fUPh,1.ӄ_,mA<%D ZnqKY{ #SpwF&GROSȒ54HTY˒-ni(,)~ZC@@|!A-fjbgeյ8cL'{|jiQ)!oqY ϢdQaB겔WR\1=IDǩ*SMdI(g4<7D/2l !@>l\TXj_alPI}~\D}bjFg 3&ramJBmrdОnQRdvÊڗ6a1'e +*ɗݚ!tݑmqDy I8JH߇:4kF.K7E dz)TGV!ZF1 MT]fy{2N)unI^$%㷼;-qy˄Ua!BAQxg.{IvcIL<66O̩bY%tܒϰm|w.$j&B@@|QA! 릡x+BRH[K4KAXav#FI6ƗsWew8H$:J#^H&ppHey +]$b6V~ᯤ☉2]ˢ0ySKw;ZF85h&%fsŮ˗ܻU[B}B@@|ia{áJ޵$m 9m4i=92c2~CE-IX\%C 0[W۔0Fvq >_(뛒6mѻr+$}.?{$ d}! ؀X㻸ς>L0cd1vŖA_vkYliFaJlڵ|^a"mʧJm↖rc'4bÖ_MOO}qӪ [_mIRNXѼ?*ƣOm~11: Ë_|4 fSrZ䖸M&'b 0+[|> kr|y>V/~B@@|Qa++-Ls^jq +]) ^a k5_;EO]s~k9D߿\PӉm~TegZO}uĆ_b~.Mۗ_@@@|l@;^F<63UĆ@@@@|!qU)W   1?? SXt:@ P~ B$7aMLL AX AѺBlbaK*F} AX$2ay OBYzmm"]Z/~,)YM!ⱋK$׼$ʿD별0J9qN_"wE1^iv- f?q[@dcoD$082гNmU֯fоʻ,d{`7WM¢_]s:ޜ(8Fǂgg3تmvEv~Nw-*vT9l@(𹈔h,biY gTdst:tx[k[w/ S"rFinE*h49<Ǖ Jqy<&%>0'X V 9g)evO4s! mm uF_ėR KaxF*f8pNjJTK>7)FM44L-/`O>d1ُv:%庚odZ;0҂v&;ߦa tp:OG5;p~cJURXm~X\WdFgN;JjDҞ%fgffqHL^Z$v~ )u_7N8,f≈qٙydn*ڐ%--..,bTYe"tFaeMCsnsӉnHD\Q ~2@X#,S%$RWWYsAAyGbYM+%>=:.?̭z%ä5ƅh(\ RRs&IO=TM-uOJ}8̱goLDq6Ņz7M}hq6*,dϽa1X}v:p.t޲E&\׍&n,ǎS1J!/]s<^wwG:-5o1K72iBV.1 ݆,ga)T6!'M(h͉c޽cb5,b^;.O&Pe.&M7ߒd EgÏ" ~Qh~kCu ʹnT^V{Da7"H- "]m=D [F&/0\"Ҕc\5[k+%#5loy>?ϋ9l3~jncLf!y'^RpD*MɝV, Ϝrw4f$PB sؒ8a.Tku 5Z$R {o|5ω@X7GR?a?sT&,Қpt2i>",Y`$7`Nj 9ٝ?nƏ6:I>{WM}=aD+ Z ]s3S5(4-%߶=A"EUMmc$DXgݚ'PMl_ISؼHfmj|ncWؐnFFƦ&F_JRVBS]6;LMLMu4Th7G[ P}ϢO;֍ I2=UqAX3cs/!#)=Juw+kwUAKk[aңqj +|҇LZ`}@e}, v6X:;^:}sKC g;a!5S'NǧwPe=\|jb78\S[%$.xhZxE׳vr-tq7ْo7/77i6ӿxέ;C}vUuue2uzz"He96T V".x;hj>kDuv]^{^CEUM置ŝ6&zc5vںJkft&(厒eJ414IgШdƜo%'kkiik.J6l7aB!GօUxw=1T *W `(TIȤزt&$є<b|W~ ҙ,DSAc_H P\j F E,:i!K ݣIe E`,: ǰUD*gX佻;a1B\GS`,hުfs d1AI9( ,W'mE]8<|>KHQg-yHa7?6ʏg𯩘Љ O^8+w(ʠzOd_F>U(:VXØ\saϿx"SY&P~" ]V     , ,N>H'JK]@X4:d1b]d /y n*W ӈmEm#ڗa*1Ӓuxs"wq{Y[l@Usѓ8~SmeQq{N\!YJWM=wлsPo4"¿~tǞa3뀬.Q-uϥrtr󤽽&`1(ntkoa=W4< U#f؁=noZ߂he ;8PN"t.?£/ҨS]o;e.4.<)≲>,WȊEUIc,ðÎN{9\oNXbf]VB=g7vD*0eod^2QhglI=1ަaxmU;,V4=n뎼:{d=zvDsӡM3Z E\ .,zDSU'ݭSJDW Emjo5 F.gc V>Eұ' ӪGa]AX&{U SWޅ#J RBnvN:JWb#- ʇ^9mv%}();4Y;<䵽77 ՟5T,v܊:aukl!'=5w(~?oNM-Hke2!Y}딪u#NzNDlr[w;SXMCu" z?(mEtP|DCY;9TgyTꈫgFƺf־oZklv̼Q,/ UW7, Yqn'[車N~}[yL6MY =f{NnVu/3$̌ 6yON{p`wPfZʓUOc9GV5 x^Tp׉"G]ׇu(S.,#Y.*[)#JZ54\N Qr9ˑr{{|NJ;{c(L )ǥa;R~K%cf%-HKMbzb󸋇t u4ݽs֝;lց 7SbiD0(p2|,ڑx|y''v>`xrDXS GHH5k@USv\ő=(XN[~rjEXl2haƻDoݼuAi jX[Xb2}նk;F|DXXGVU;m}L2Dk鎜Y&5T&vmV0WzCԢY//q, ,7mv(˩z@MQ@+RV˨` 7wn>v*RÊְׂޒv־g"CycȱN9|z.#*a+XTDx+v@2]U~>?UMŸS*TXuPa7<IH%my'|@e{SCB/ {\Αpn {kI.q#⦮x[9?N ck}dZ{hmǙغuK8ݶ%dEieJ 9dk^Ge+%HKv'1ٲrф@̾e_t/hy}LPŀ3-C B&NOgF#Me]!~x8ڻvThޡV!zN-Ho23?_u;;Pd32dqry|C@XD98OOXJX\Fo$Zk{ E"i|x#+vafxra}F!NUO rCZƺڦ֎ s~l.??8:1<5>~ɕBYu yo S)9$\?+ 7;2>EWD; ?    GD(g:/DF\@@)_^ifIucE)qc8 rwa$x||Tz^'`a'v`ereloW!ffl}7P׾&S6)`<],A>fۙ-?6עo sg&8CX(}PtYo͎>7rۄ$/h{_~uᐭi[)c6c'[dnf㹯@=.K5}H3=-m}yfbGZhs="#uͿ;h*(mx~ 3?Q?1Q[6Pšcu4QC^ oyӹu[~ⓖhG~f-uUTvAZwCn4⭫}EI“f3zB6;_CΌO*Ť>[C{-$hkD{kZ h{cV+Z$wU2au{">7V1'w}`~UeΓ-FюJzo$Ym{VCr41c21=Nچ/2.-ՓYޯQ))k݊O$ #mdeKEoKu%N9 c$CQEi^}mM3xаƬ~ ?ޘcpr-v!MBD7'ÛFw= ך,mUvxMQn=^-S/UO NďWCE4.h`F-yKyKFL'iɺaQ??Tit.sreWX<kcqֈٗM zbEʏ/zv)VA#c֨措i7{뚡ɶ]f- P 'Y1~yG/ aꑻ-%!*]̴CRV:{= jy%WtSږ+si-wK;.۟9G+|13n);hu4ޠ;~~Qra0 ܢi106ҳ +#F<ɔ#1"F]Fjۏ ^z]$=m=><.ءn{0q;82(UlU9get&kV3U5"oki[~$4F֝v#O첱ܛ ̍T,w٩)gKKVh&Kن恲PG=?S+0neT Aﵮ_P.ך"b1K:[swN[kˍ[}f<|e2hc}8㔧:qDG߰1唆ړ7Xv}#gyH)[]4}K2O BppV3R w2Y*w͞rGL2q>R~+>"T66/)UIza4yDwj[ )?lSa eP ?%tW0>;- ?JDh-nw5766Ȅ`WO9뤦D;ufCW`V駝nO1g;] ΪgZN_PZ}y V`*4_}hF07^ME*?V؋ézwRY,jj_|Uք0<,ڹk^?`qΖں͕꽬ʬ;'n'ޜ[^'߿"VEz@NÈƺj 3xVǪaYC]YlճFsn[^پ[-KhhnQXL*zraɚ@G aY[ vV.k x܉PonwÒTV?ZԈPemO7k~]ke=u/NQ7byۮf$׾qt_giߓ}O] IoEu/ S'- Σ65B2e;V =8 ɓ{Zܫs~#aD)ֱ񆓷٦7u2=:YKGMԼnj`#=YΗ/oխ#g792O,dNTEa9~(,Wkbph6ftu¶m/%"'ab\='_zM^?:a@%a%U<gjw`\&'|MXf2ae^2]7J!ȿnƩ[#\C,dqk’,ΌNRD`ty<]y6OY\z权6'/jޓwK\KnN9hmE\XvB%zi O&1wMgòPY7-O!6qݹ?΄ T,Dk>/IċY:&䪔Zێ'M frs3pCNLXNZˤV\Xfb[S3)/a" md2$l2=peU :LTuF㍅R7@;@7UgDP.̣=OwUXx+p/9GPd7#,yu5yh]S"K{8o5hH{+M6PR(>␬IxBrwrP][OOO kӱlIer0}yVwƩ[Ͷ]8lN0R7 h+}\aGX`W?5Lם4jgַgu6]ȳ[Ogs}ucBQTkmXa} )YUaۘy,$ޑAnߵqG禓Odi<#DW|OGG@oE6nn'j Q?aQ#\ߪqrB s*l&LG6Qsm'Y⍴-vQ_mw$re,/DfF S\u0m*Z6٬e9-{E%VfNsh}j7Ԫ#/'AQaT4&~S$Bg/,B]g%K> #Ew# -`]?'W]MlvMH~fk?#'m ۯ"W Ǘn|"[Xf $gPGI'L~J9~|z1HeBLIh|w ^ć-/Xa1 Q , ,   aaa@X|a͏>(7[Cel edfd48@XrަV(DkYZZ%jGnH-Z^u_H~<{_ʕ'ߟXޫc|ORTawgUFHfpY*`ZqRaQ =Ċȼkţ,ezQWT^畒ޖ<%Jz34?+mZ?g7],讝['"6%;3;cA&QSgnt7.0+zd)^4Msed)qe=EIJ%c!Umt~畢 <8@$qgs]fpf`iW/x D'dn̮J|b@HRZݥ}~f>"S5t+XBCѤU[s#ΈdAY5JG~54n H5ĔJD^iZJ6_HyCD `u=~jcRY}tZSV\rƢ4=kU\RgpEP$y8>#a99fQ;ʕ3\q>7bqraم٧_kv&+UN eJ7ByoVV.-څ.Hdꑹao\-=J%(=!v\ϷWϔwHqX*f^+R^~)t6;g9cWZ=0sJg(Y63!V獱E5cs;0dv9W!$iؒMA9Ϻraن`r;_U oWNV|s.O8a͜ ?;3$Ʉ~#W5,[%$G/ sq")wEX+yي+)t!,<=kv Z _nR 1&ataB^tJBp^~D=NXk’SK7^̎~3=2C>_Xt;( oy=^"헛>N û1Ks*?LֻN5a]K-T&44+]zڇK;_$LvuieW}BXYEXT5"Fݢ`ֽtD1~}ho]}<D*fW> d;IlyZyxۺwqRVӞ*Q5=sZ狪{CBଳ~{š5 kn)4/vq4gbuuI`tłە-,m5! 5o. OO=-j6_''[W:͔ IM?C& @{FraY% PWQurO,@X7SYZͲT:CU؝u@=s .yl] Gܗ 렎fPJt? RU-Yh+ TXWF{2lgŒmNJOymMdRϳsFp*~0UmZJ[ ki0]l8 =t@|5E7J3>FK%"mc;-q& 5]D[<,_MyiFOO9 AD9gw$i8\ kNwIf`raYɄՇ԰v5dM k2ajEɅeɅG.f6Mx%XvOK=5Ε/EGƺV> ynLh~wsȪr4Dlݭ:ǎ8rbQ8ddpTX́x-{_V!Q-8,r1${5UDhIs֭m} ~%HaRڄL$^##2h$mH$ɡ ziM>L0PdBa(d"^FƒT<@XVa i"?oE5/пrx5*> _;Mj$Yϫ0wEVm@RM+C--j[ ;ifit1/Jżڤ@}S3SQDdç İ5כC󥏵5TGV.ŒJlE [Ȅpi6djjbHj2 vz.oF' "b5˾r9Jj?(qk#T @XP&2<]tm&/} ~L]."$'3?N+ókr[V=Q.,s:VJ&,=eDXeW/ " N$2*,*=z?;WXo]ܺɤ{|]?DX} @r2JR!V+ZÒ VÚʚ. \_ G^zLyzkCe{t`-vNXJ" oU{P9dRհE #{@ G$0rp q|XIiv,-w멩֍ɜ$},/L5X۟aG&Z:( F* qt|šl<=Z͍pt~+!+a> +~=Gd9taD˨a[X~{5% - #9+;1JŖiWb/$gOЖpRZI}@ʾdkc,#5->|ojRRny3_v͑NwE.Rv6a̬ URc,hjX1Hk(n>urOLI.heeQޕ\/20٥ul ӄ޴FMXO=vտ8nǐ56ƺG#KG =Oo6aF55-`KaV 2֌b|˥G|O%wĚ[랹({%##}CA~ƧJ/Z^^n?uznd. KaT,rR_ݪ\>[-;Xo ӑȘd+sڧn9"8Rfڙ))GCMixB,ѷ1a32_e =0D*]Et (,;vߍ6'} `; d`EZF6Q ɈTEY/m>W2+BfZZmSfJ&Z^J沘Ui:F8R\k%e8D+ꑒҶ=ce4E ^e"Rޭws6(I}P#J.֦o{Q>9Z*9aa{^LѰqSA^3=8c n-VeN踅|M_z3+rAN|GM>,W+Ob*^" {iĠ6MXvD@oa#Vٳ0pӪ#/L6W 5OEk?Sl$R+R A4!%l:z;"IV;a"U7}[pqNm&* 1 @U\.2aX|rn $tɦS:k?-@*dSUq ,%dTdpuT)H%If2&S'sn\zꍚN g)t_j~ ;֑vm{7^rFJYmDT0@3eȋ9\WZ\>{Mץ&G2g|,k15u_?LO@vɫիɶ7!Tx6OIWهaaa@X@X ,@X , ,  _(,B[/ó2gK9B>(Qaa[Z?i[BU CWσ RX֖-y7b]x=(#uv]%K,d8\Ҍbhޟz]LSYPNz.?Ű;SºՀiiRpƹ&\U5mS H|S|ynd+wv3ynWH#28T߹- `Z4$}e?\,}Qڬx>Wt8htq(*/lZ] VnD,s9C5}!swRgmwBquKrXήu/&ѺRE_WZnᄷ QoyZx-SmzӬG5ƦlTq*^#v)>)!ٚQy tr7 8 K&>3}ry#asЖwj.B[J*TÕJ ?4Tm l @X#EbR,S +HDbP6cF Y VS AD  Q~@԰@X ,@X , ,   $WDq a}Pf:,\!laNH}Gˡw2OwO!,6y&p-g!xOO]uSb`k8saX7{1qc1&zw(*]&Б=)oW.{WMrC/*;XVUQ׻t7G vT)))[xcWUzgP,Wiwb{a~%pXBXt̠Wsߗ\f]i{%w*\s;T8@_?l=U3λ\?_h,CF:>MǸ)?lA?qvU;)E Q>~v͂g*j> >}[{[VqY7Vٹwb򔳾ǃ^dLyy8,O!,vG럇JB MM]ޱ w3O3S</*) I\&zd'9`m ց5a9ȄpO}Sm>A Ua->&C(PIaʉ];.pP idBV' ԷzJ7BCeuCCsL܁M16ج]݇_?xY;'Hw m12ܽ,փvQa{RSvs㖸*2eªeBE_zpCi[9Y(YOľH#.6g31e! ip@HX@,T&s^.N0~SB&ܛ$bQN uRq:@XF֞Xߗ.BH,mƈUjo-2GOg~0S[PPPR-D%N|廀^Ϟ ^ު\X?iPȓm+Q:Q8Hs*ɜ_HhSb܋eY-W@YL]*] +f隴caǐs^Lh"##5w //Iaw$sF Mx.zg'N DIȡ 裺4aXlGֺf"H"NW效y|H$`1RHa#+y"xwr^]SڮeytǕO%9`jd/-󩇉ͪg">9cf:5϶s5Cz @HkasHB[ފ)T3:~.;5C7qc_'Q:x{{93y!ζ=.fzY050oF}G~N}ZE!ki⚓^J}rJK>onc~d'm<h֭ɚ}'ƺ**V*dOSM͒NJObYRPevڷ\ᗑ'h(Cߢ'g֭gӘF>yc$&z}ZOC*)eVsj*x$)+}vP0۞z YL.4t?O)s;^rW8"r~4: jDB/2@4u:Ob(]F ^]xd<#$O 2Z4k)hD{0' &6/@VsҰ+0 G&DZd#)xvQ6x&:f d\GL^}Tص^X5w]-t#1y:Ffcfz>/@vd[Xl1x"wbwVeM7^5ǣe4+`e0 S-)z6v,[)jP=xw_Dʺp.3ӳ o1 Y:R#)*U=饮mPWW!l谴JqerhZ+@onx?]ioy5Ȧ4SWCx5i cB,!L j9tu؝4Kw8Id \mסYjpፚA¡ֳ#ظ!g3_[ PڼGOEj}u8u{ʳ @{sG>e¢.T9'D,"ԈSfSV׳EG9j+HQTF|cB犸 7knfeZ˄uʲ=%7w]LG )ˉqnᑬuyAW3J׫KN ~PPx6PgraلGw*?eUtdkMp/wz]UV&8&,Ջ;2;԰fjoos050??!]+eaw~p4SY&=;̓DD )іdžgb 0fHYO8|:wMC q IAE=cun~0"_HjϘvڶs tbs!dJUuskncw>UZ+mr7hJjX 喩R>nFy禪9NG'L5{!’&zY37cy= \Xra]Ff K 7W;/߆L4>y@&FTsJ'st<ڌ7b4rgK#@d*,jׁ*Q|}F:ng}O )4rWKM<^gY5i{RK9CXX-I͓7({RuM}HD?sy¿zߓHђG^; &,Q/-/sJA<:~K^epq_;Ss["t_m]yaaaa@X@X ,@X , ,  Gؑ);^T>& dvk"!'Je$Ha)rb C?:/6H>b<;@X? ȂǝD @t !HN,Z?D/J{ Oّ~|)IU3GLfPl/:bvE7MYՓ O*Ln])x܁j2o%4*n]/qfIa1n˒\Xwd7maYٓnV7JETq=z [EJiyS͌o{Zn|9O!8s{XWl­AYQH$ %"C-3qAD֘7xbL|bLJJՃӵa ϧy*in 'fV)XydR>_Ʊr3W3-Y.&k5B2q=8.u@ FerqYViG+楢u’0oj_-n"#!FRZ%2V~&4κ XX m:lZԳr+z1:1rHpN~kb9Ĥ=$삒4?%bΙW#R1gMX<̢<\kEƲЋSFF ,+{{37_J?PG\Q }jbtgBsZUӍrZ芰D+º.K"]L1KDurUX9+91%jG=f^LXS܏'gU}å >*ZZlֽ%p'ThTR~v&V j6&OF$[O1Vs7 aca)Di^^Z\@0>OK=9Iy$k糞c1H A&uqa9-EGIQFR̍#d%gF2嶏qD%-K-1 S_'ӆwr6`P-^LpH~>U;*燠lۅB5ʺ{->K.>9+w"u52s[`Φ|\^W" TB3eХRbKA%%-KEia̕Sג w-R%RX>4Q=IQ(ESsh|Ec$S+1vPUԐީ扢ACF6fϐMA.g:u"cab?\K_=a닇4N1@X ,_X|>@5,aaa@X@X a 񋋿x.,x" Lr<ѿz^E +b+?@e|qSv -,*j@@@|pqKdT6LspX"]/,D ,t ADDQGҐ1ce 37_&31 ,GX2%%Vb1 %<]=,0aYL6CX8wcpK%<}- .0: @, +ŗH%8+ a@fVx42uaLa)ICw DZF?M$@/W3gȿ՟ȌB<1SqQ9G$K[$v޻@0%0YK *Gt\$J pb 4dP"KEB,X(XW(j D"X,4|h-=O(D|짘ZԶJ%鑮/. O+r$`3'bG6IB_ݙ~q{Aó<ж&ip[*rH?8ɛo[R牥 4^?BGL[hT麊ɻ`9Ϝ9,%V5L#dggHl>zȄT*v,,ĆKjIęgw\5+nwyg%&=|#fB/ĕ"Mŝqv HHMKcx<YO&O}8-gI)Preɥo x:9 q ~×|>gt|`x~Eʙ–RTa߾Ee2Mw8‰DT8*(ZYp,̉Kaё 3'\>bgzP$(I9{g_56fwiӉER1:GsRfy^93oz?!,!8pu'2^is%_ uq8ya}MB4|1o}/ D?~M(  䙠7:#"m &Z "+!HD^n)OC\d_ /WgG!3P] ᩍAJ:d$/M bV|+۷Cx8!@Xpg)RK._Q||tm-sb18"@kq5nȆa>-xNO;K(?}wojDpXC\a^ ^ A@~?a!":v!4,,4$D,>P"j|~dpxDsײ,VKi9ŕww_Xw8Rzu8v; ߵKcKy7LCr+C "^aHW}_+~-h*)CK`),w\=' WYⰹa]01wV"3+ OX#o^)9"yM7yL*<<\{~w3B)ad?>Wӳf"djjj~ ˆ@@~&i aR Dځ+P $RD$kBcwC@@@X , ‚aA@@@XFa 6X?wp8/3_"b0D?y *@E     aaa@X@X ,@X , ,  l@X ,@X , ,   aaa@X@X ,@X ,@X , ,?q7IENDB`spyder-2.3.8/spyderlib/utils/inspector/static/images/debug-step-out.png0000664000000000000000000000063212626055324025011 0ustar rootrootPNG  IHDRשPLTEzܹRVڻZ߼z^arfuq}hrpvNmW߃GSD=|OHd5qnFjZOS:y=)c5)`2nv "tRNSv8IDAT(ϭ]@[ Bv&G(чB {ל=0~cZD %3AWN&HH|lá% GB#5vH:Ik4;d)Kx:,)V(gɒ_u?yuyIENDB`spyder-2.3.8/spyderlib/utils/inspector/static/images/debug-step-in.png0000664000000000000000000000120412626055324024604 0ustar rootrootPNG  IHDRשPLTEa|^Z߁o|VzgxRԉjWW]mNwEDfGdC_ޱ=|T5qnFjZNS:y=)c5)`2n.#tRNSP]G .C (Y=F7?  IDAT1NUQе;bbei8^- !LjuԤA`R#E(@`fbgv5Q7Fw.4ߢTN=1ś>PWI"gG(xcC{@{w]}l,ǏwWu>$I$قda}I[lIvhI:IA' $jn~$/__@&`IENDB`spyder-2.3.8/spyderlib/utils/inspector/static/images/collapse_expand.png0000664000000000000000000000042212626055324025303 0ustar rootrootPNG  IHDR 2-ΙIDAT8Խn0v~_Յ܉/ A[~&ұEP#ɶQÜMZ:f|0҃+-%>ƍDEuw#@aqIF gh&hN JJ*WXPh *>X2e'דy8Vu3ίNKZFNbͰY$N`nvIENDB`spyder-2.3.8/spyderlib/utils/inspector/static/images/spyder-sympy-example.png0000664000000000000000000001753612626055324026276 0ustar rootrootPNG  IHDR3 :%IDATxo}/B}WZ RhZDM( L_ }@f08NiNIݕ9U6 'S66'7V7ֵVt>^ٖ l?3;;3HGH3]L`&f303 L`&f`&03)و'AmYV/銔_[%ѓq|Ej)/W5WW] eˉP~{i{g|Օ>X}o%d]. FGZ髺˺UO]6_o֙O2gU/LWTN"[+UU" lrjr ݻ_>V{/|~l߽'K7߈l4ҧtY,y^#IG[]D|wX73 ;Hf8R=jxp\w2V$ߪ#pC̔2愶+QT/,7SF?Z3L b&kd|PaE *$4rQ/!v)Xa(9jY)[j|MݛVTjJN).wy9Ws尜U%T!r {zyǴ!,j >%,n&KM,X'%l4Ohf:*=y{x jX@u˓O*T0%F.+O'@˚l/i,H391bR'=x>;iZsdKnrɎ`>``]7.y߾ye-0=V"T܋S_qݡ#:irBo I ,$K2X@4X j knI'㻋7n,vpƭX2>fX鶋HF}1_ 5U5 =5T[]Bo?tV??x~{~a 1ͤ9tf#A5l;TJ, 3#I)i1_Y`5K~8T쾙f>=8hD4P̢ k}fK!dE:k0e/̲[h6FGPhf>aźlGe_=5SԚ3GU*Leq`SKܜ.RQY`#lŃ94Jssu 9X mxD bxV/e]OӉ~\e. ?4=̚AҰJSVda{Vi44\ m|u5Y6[k,VZf̗=|{I mw #܁}ot3;lMy[HJ-^YsyV&*si 4]̿5{75hRGļc(WuP`f JG^IGqɰɤi$}ө b^uCTkLcqXc+[77뛛[ټ{T%8rUjriɞꆒ6̊S-%N Ύq8uu{2 ˊ=g5[q8+0 j^-nbvZ51OR4h:IHxM "~ ,QSbhj(^0f^3I=V~KIcT4-4{5r+LĿeD f2u>@oQprQtBXӓ>O㫧fZsJT= fg(^ ˎn23^#f]X닋/.^h6dlf'4|4ҔU.~r~=XjlPq=6X3,5S`LֻΟNgvipwwX\"S.,?qT-?my^d>G*Tjq6ʚff0C?{]D?<8dXdA,i$ Z8Cf6Cv~"ջt>PW9+F]336zOd笷f;κ75SM Μ L`f5U|G^ihr%IW:'6Wqo=ҽ,]m)l2IyAdf~+m(BnY7VٯZn#q3O>S.yղB&~D8$tUžs,Dbx;J㯯IݭϹxu @ʹyAjkjD7ih݇) aXN-ډQ4smﲰjϪ lsU3}褾Ȩ‚X1=^wHTbfl%,;tdμv՝N3c˒pfɳ |`Ρt Af97Ӡ{q펮oIt2:%͎3gδf#6w?]5s[zuZ4}w9JLk/3aβD<0yēNزĨY=GzkY3oigQaZ_Acf#[/\|_h\T~6dlQ \-6so׷ZŢEK겼=6 63q~]q3^drw'pdDjG53LIV3`t7nڕf.BlfV{09 !Kov`jfކ.9/8Nk6wq Lf`&f30 LL`&f30yjjrRD{)-|>ڵs9fhEY:goC ` Az88fΊRDK/+bB3I*5HfdA ̍8sss]rv-I3{r(f~_z}׉JD!mg&Eo9I4-*$fm&>XWJ %y8o2l#hoqPi8mf;ww<ߓOIPΐ7#w(3"d,{,) ngL"[o~HTi+$s#LB^_1zW;3)l:G7-R"tл~;8fvϐF'$5wMO<] eˉNɺ$\:^?RGUaAWu}Eחu^sisY#U4C; QJRN Rz곕L9S`p4iO5XҽF6RW"VgC}X߇.}~9&gl.CLh̬$Zu{*Se'C̔ߡmvK+D')?(Yp7V]fUV46GIYA*/f{EIV$ۥbhSFf1n5to%ꄦZEQ$KdXId|[ge'3s~͌ly6a/O EhL~Y$.I瘟w*r~3'77o zǧtTz..!+V,Ж'ALUhӕ^ht|RP v?ɗ4E$ՙF#INtF=Ȋ;#6Ddf%2O1\*uHJNXbǧ03Y3s/L} v"i7k#mԒLqx3V0XKHjY/Uh wKިoR _MZKR4kFi`f*AڙƚDu@6Sa&iOFܯYb#!@I/03s7p9z*5gж@3[[ۓh5g>f:f4HؘE%[Z)$>@&a\`x9baqmtRfo^g[ylf' Q.~rm~^#a>X*Z>ܾu)Ou{u^0+@3"U[gZFV 7C`W NɺuH4ٞpK;g轞X\"S. KÚ,WR3nf:dg'R+JrҜF?IB"Á0s/6zO=g5[qq uJhǹјt{=z3}cH 30 ^l[W Axq*]KO~/}q—?Iޥޗ9~kz$F ¼ z[3׶zPy! V-lg~s, lcN͌*Ub߹ eO{Fv\|__E[VVĿU]`rxk{fkrn^ښǍijZ0Z)ӝ8$ƊwjNFl+\ 26qJ6Y5WNꋌ*,eQxGuiu/>4X/xs I7MfΛ8NlLQ4;ߩY, Wɳ |`Ρt A@33g܋kwt}#3NRF`f;6ArWY,{Cfnh5sч⾙;pfFfO0~fpTbr{X ~^K.3#/af]%p$G{0yēNزĢY=Ȼ׵·(0ko13vMc?^xlQ ]-8N,[ۃ4b%{bsVuYfpkY{nt{=]i gLfHV{T;sȔd;HwY]l &?ϟ=vl6 kz!yr?v[νHdcxހOӚp_`zf`&03 Lf`&030 LfaϗJ%ӧOgz|ܹs/i j!0fL0M3QvZ=iR766{DxӭTf&i=90=_X!Emyʞrڲ1@k---&1r:$D0 XHgF*{Zi0qҿ'B JNPiOzeOb 9mT$+;߂l#=[ # Sjd)g[ڃ~C-'RQck!0s`ρ4D렒?Iv/-- y.ӫ23d0Hiw>F VMpP2.Sm!fPqZ2@&N&㎹.SdfQ5ߧ4wD8-ԘE%d=#㏹.cvujI@3No!wf"Z3?61'E"MC8uTKIvzG޷V0d8CʓZd>u9ƬeN.. 3c8YaHĊKxu˴KNHzFhA6)8v䢒\.6~ņY襝q`I*vyk]))[4ku+ o&9B)tuɟo&9]y94 s2Ni'kJS_3z;ԕCkLoRE9_qJ;9>f39jt5cn[f"E &m8l]W0|hL^rںLߍ> f@1;iOrc˘`]Zj̜)_/&_Ҟ3OTSkqSړ`&f303 L`&f`&03 L` ;JIENDB`spyder-2.3.8/spyderlib/utils/inspector/static/images/debug-continue.png0000664000000000000000000000236212626055324025057 0ustar rootrootPNG  IHDRIDAT8ˍkLWKbm >? aJq 9E&*rQT Ee(!2(E(@)*E,R[K[hBۗ #B9O?^Z. rS!8[3%ʘ%4C[Lr~kH5ֈ Mpq2>nwvetٌ-Բ3Uq~)DcL㹞̱&{"7aU1hCu1)2oĆ7k-  mUZ\M- dɪ𼣽 7Ui/UH@f!e*[R-sb%RD2\Yb:k}}k E>.zZ:+ȣB~j]IN#cprC uZ ӎ9:Cӫ_!BN%R,@;Z Xcw_R $n<_@"'tWnK!Xr Ϭvʉ2$uT"kIH\}Y=$C-: &sU3n;2"v?. &+]VDq],NV#MLu%Pޙ݈U!3&n F5Ѡ~"rӑ;86!+qrySsO48< |r%ΗyKo^Xܟ.®k}_F'.4!-~ꐼ^ۍڍ[++EPX*KΤ<-ѯ; mNMoSۋ_#?H*=HKTT>uNz æ1C8kta`*^\l pp/-T.iy4>LjDp x>[¡NPco'aţl;Il\}~XQS= =}"կ26`V(I3"c9Nղ?`ʕ;] DZ*dHgुΨ:= ϸZfm=](Dir2{>blgB*&iA\vc{s=(q쥏2q:ρw<{2ӫyrazIJUՌޱtuւUTcXۣYՄGMYf#o`כ<~73( w[XS)ե|dWdIENDB`spyder-2.3.8/spyderlib/utils/inspector/static/images/debug-step-over.png0000664000000000000000000000130612626055324025154 0ustar rootrootPNG  IHDR(-SqPLTE8:=54,0s2%z$z1ln u"w"xdj ZaS YN@@@@:GWLB{B02,"7_tRNS5 yy9̃&&5&&3HHHHHU{IDATjQ{g]fJB; Ey0iDa}6Ix?}qyf.Ay7ܒߠ?'$G;:W?Rzr d Ɉ"ji/2IG}^5-,+-`u0rj]F4~Y,qIENDB`spyder-2.3.8/spyderlib/ipythonconfig.py0000664000000000000000000000275112626055324016773 0ustar rootroot# -*- 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() spyder-2.3.8/spyderlib/otherplugins.py0000664000000000000000000000351412626055324016634 0ustar rootroot# -*- 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 spyder-2.3.8/setup.cfg0000664000000000000000000000007312626056072013361 0ustar rootroot[egg_info] tag_date = 0 tag_svn_revision = 0 tag_build = spyder-2.3.8/bootstrap.py0000775000000000000000000001200312626055322014123 0ustar rootroot#!/usr/bin/env python # -*- coding: utf-8 -*- # # Licensed under the terms of the MIT License # (see spyderlib/__init__.py for details) """ Bootstrapping Spyder Detect environment and execute Spyder from source checkout See Issue 741 """ # pylint: disable=C0103 import time time_start = time.time() import os import os.path as osp import sys import optparse # --- Parse command line parser = optparse.OptionParser( usage="python bootstrap.py [options] [-- spyder_options]", epilog="""\ Arguments for Spyder's main script are specified after the -- symbol (example: `python bootstrap.py -- --show-console`). 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)") 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, help="Test Spyder with a clean settings dir") parser.add_option('--no-apport', action='store_true', 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'), \ "Invalid GUI toolkit option '%s'" % options.gui # For testing purposes if options.test: os.environ['SPYDER_TEST'] = 'True' # Prepare arguments for Spyder's main script sys.argv = [sys.argv[0]] + args print("Executing Spyder from source checkout") DEVPATH = osp.dirname(osp.abspath(__file__)) # To activate/deactivate certain things for development os.environ['SPYDER_DEV'] = 'True' # --- Test environment for surprises # Warn if Spyder is located on non-ASCII path # See Issue 812 try: osp.join(DEVPATH, 'test') except UnicodeDecodeError: print("STOP: Spyder is located in the path with non-ASCII characters,") print(" which is known to cause problems (see issue #812).") try: input = raw_input except NameError: pass input("Press Enter to continue or Ctrl-C to abort...") # Warn if we're running under 3rd party exception hook, such as # apport_python_hook.py from Ubuntu if sys.excepthook != sys.__excepthook__: if sys.excepthook.__name__ != 'apport_excepthook': print("WARNING: 3rd party Python exception hook is active: '%s'" % sys.excepthook.__name__) else: if not options.no_apport: print("WARNING: Ubuntu Apport exception hook is detected") print(" Use --no-apport option to disable it") else: sys.excepthook = sys.__excepthook__ print("NOTICE: Ubuntu Apport exception hook is disabed") # --- Continue from spyderlib.utils.vcs import get_hg_revision print("Revision %s:%s, Branch: %s" % get_hg_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 # (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") else: print ("02. Skipping GUI toolkit detection") os.environ['QT_API'] = options.gui if options.debug: # safety check - Spyder config should not be imported at this point if "spyderlib.baseconfig" 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" # this way of interaction suxx, because there is no feedback # if operation is successful # 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 versions = get_versions(reporev=False) print("03. Imported Spyder %s" % versions['spyder']) print(" [Python %s %dbits, Qt %s, %s %s on %s]" % \ (versions['python'], versions['bitness'], versions['qt'], versions['qt_api'], versions['qt_api_ver'], versions['system'])) # --- 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 time_lapse = time.time()-time_start print("Bootstrap completed in " + time.strftime("%H:%M:%S.", time.gmtime(time_lapse)) + # gmtime() converts float into tuple, but loses milliseconds ("%.4f" % time_lapse).split('.')[1]) start_app.main() spyder-2.3.8/README.md0000664000000000000000000001250012626055322013012 0ustar rootroot# 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) ## Overview ![screenshot](./img_src/screenshot.png) Spyder is a Python development environment with a lot of features: * 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 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 Show documentation for any class or function call made either in the Editor or a Console. * 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 Supporting regular expressions and mercurial repositories * File/directories explorer * 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. ## Documentation You can read the Spyder documentation at: http://pythonhosted.org/spyder/ ## Installation This section explains how to install the latest stable release of Spyder. If you prefer testing the development version, please use 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 pip install --upgrade spyder For more details on supported platforms, please go to . ## Dependencies *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. ### Build dependencies 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. ### Runtime dependencies * Python 2.6+ * PyQt4 4.6+ or PySide 1.2.0+ (PyQt4 is recommended) ### Recommended modules * Rope v0.9.2+ (editor code completion, calltips and go-to-definition) * Pyflakes v0.5.0+ (real-time code analysis) * Sphinx v0.6+ (object inspector's rich text mode) * Numpy (N-dimensional arrays) * Scipy (signal/image processing) * Matplotlib (2D/3D plotting) * IPython 3.0- or qtconsole 4.0+ (enhanced Python interpreter) In Ubuntu you need to install `ipython-qtconsole`, on Fedora `ipython-gui` and on Gentoo `ipython` with the `qt4` USE flag. ### Optional modules * Pygments (syntax highlighting for several file types). * Pylint (static code analysis). * Pep8 (style analysis). ## Running from source 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: python bootstrap.py This is especially useful for beta-testing, troubleshooting and development of Spyder itself. ## More information * For code development please go to * For bug reports and feature requests * For discussions and troubleshooting: spyder-2.3.8/app_example/0000755000000000000000000000000012626531443014031 5ustar rootrootspyder-2.3.8/app_example/create_exe.py0000664000000000000000000000160312566665770016527 0ustar rootroot# -*- 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() spyder-2.3.8/app_example/example.pyw0000664000000000000000000000457712566665770016262 0ustar rootroot 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()spyder-2.3.8/LICENSE0000664000000000000000000002664612626055322012560 0ustar rootrootSpyder License Agreement (MIT License) -------------------------------------- Copyright (c) 2009-2013 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. Included software ----------------- License terms are included in any software used inside Spyder's source code. Included images (spyderlib/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 [2] Crystal Project Icons Author: Everaldo Coelho Site: http://www.everaldo.com 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 [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 [4] GNU General Public License version 3 spyderlib/images/pythonxy.png spyderlib/images/vitables.png [5] MIT License spyderlib/images/winpython.svg spyderlib/images/chevron-left.png spyderlib/images/chevron-right.png [6] BSD License spyderlib/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 [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 [9] zlib/libpng license spyderlib/images/filetypes/nsh.png spyderlib/images/filetypes/nsi.png [10] Eclipse Public License spyderlib/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 [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 [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-2.3.8/setup.py0000664000000000000000000002376112626055322013260 0ustar rootroot# -*- coding: utf-8 -*- # # Copyright © 2009-2010 Pierre Raybaut # Licensed under the terms of the MIT License # (see spyderlib/__init__.py for details) """ Spyder ====== The Scientific PYthon Development EnviRonment """ from __future__ import print_function import os 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_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 def get_package_data(name, extlist): """Return data files for package *name* with extensions in *extlist*""" flist = [] # Workaround to replace os.path.relpath (not available until Python 2.6): offset = len(name)+len(os.pathsep) for dirpath, _dirnames, filenames in os.walk(name): for fname in filenames: if not fname.startswith('.') and osp.splitext(fname)[1] in extlist: flist.append(osp.join(dirpath, fname)[offset:]) return flist def get_subpackages(name): """Return subpackages of package *name*""" splist = [] for dirpath, _dirnames, _filenames in os.walk(name): if osp.isfile(osp.join(dirpath, '__init__.py')): splist.append(".".join(dirpath.split(os.sep))) return splist def get_data_files(): """Return data_files in a platform dependent manner""" if sys.platform.startswith('linux'): if PY3: data_files = [('share/applications', ['scripts/spyder3.desktop']), ('share/pixmaps', ['img_src/spyder3.png'])] else: data_files = [('share/applications', ['scripts/spyder.desktop']), ('share/pixmaps', ['img_src/spyder.png'])] elif os.name == 'nt': data_files = [('scripts', ['img_src/spyder.ico', 'img_src/spyder_light.ico'])] else: data_files = [] return data_files class MyInstallData(install_data): def run(self): install_data.run(self) if sys.platform.startswith('linux'): try: subprocess.call(['update-desktop-database']) except: print("ERROR: unable to update desktop database", file=sys.stderr) CMDCLASS = {'install_data': MyInstallData} # Sphinx build (documentation) def get_html_help_exe(): """Return HTML Help Workshop executable path (Windows only)""" if os.name == 'nt': hhc_base = r'C:\Program Files%s\HTML Help Workshop\hhc.exe' for hhc_exe in (hhc_base % '', hhc_base % ' (x86)'): if osp.isfile(hhc_exe): return hhc_exe else: return try: from sphinx import setup_command class MyBuild(build): user_options = [('no-doc', None, "Don't build Spyder documentation")] \ + build.user_options def __init__(self, *args, **kwargs): build.__init__(self, *args, **kwargs) self.no_doc = False def with_doc(self): setup_dir = os.path.dirname(os.path.abspath(__file__)) is_doc_dir = os.path.isdir(os.path.join(setup_dir, 'doc')) install_obj = self.distribution.get_command_obj('install') return (is_doc_dir and not self.no_doc and not install_obj.no_doc) sub_commands = build.sub_commands + [('build_doc', with_doc)] CMDCLASS['build'] = MyBuild class MyInstall(install): user_options = [('no-doc', None, "Don't build Spyder documentation")] \ + install.user_options def __init__(self, *args, **kwargs): install.__init__(self, *args, **kwargs) self.no_doc = False CMDCLASS['install'] = MyInstall class MyBuildDoc(setup_command.BuildDoc): def run(self): 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') if not osp.exists(self.builder_target_dir): os.mkdir(self.builder_target_dir) hhc_exe = get_html_help_exe() self.builder = "html" if hhc_exe is None else "htmlhelp" try: setup_command.BuildDoc.run(self) except UnicodeDecodeError: print("ERROR: unable to build documentation because Sphinx "\ "do not handle source path with non-ASCII characters. "\ "Please try to move the source package to another "\ "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') try: shutil.move(fname, dest) except shutil.Error: print("Unable to replace %s" % dest) shutil.rmtree(self.builder_target_dir) CMDCLASS['build_doc'] = MyBuildDoc except ImportError: print('WARNING: unable to build documentation because Sphinx '\ '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 # 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] if PY3 and sys.platform.startswith('linux'): SCRIPTS.append('spyder3') else: SCRIPTS.append('spyder') EXTLIST = ['.mo', '.svg', '.png', '.css', '.html', '.js', '.chm', '.ini', '.txt', '.rst', '.qss'] 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, version=__version__, description='Scientific PYthon Development EnviRonment', long_description=WININST_MSG + \ """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.""", download_url='%s/files/%s-%s.zip' % (__project_url__, NAME, __version__), author="Pierre Raybaut", url=__project_url__, license='MIT', keywords='PyQt4 PySide 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)"], 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', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', 'Topic :: Scientific/Engineering', 'Topic :: Software Development :: Widget Sets'], cmdclass=CMDCLASS) spyder-2.3.8/img_src/0000755000000000000000000000000012626531443013161 5ustar rootrootspyder-2.3.8/img_src/spyder_light.ico0000664000000000000000000030224712566665770016402 0ustar rootroot 7f (?800 %g@  f v h?PNG  IHDR\rf7IDATx VSש:]_GRHDD0eÌ4ˈ! ]B.ɵ$D._w5=k|gkw^YϥDedw9wݟQQ"p~! 9"]+.dZd"rԪ_]/dyiO9plt}+v䷩#r[r(F}uI&>rs#GVʱu6tfokw8[~Lj@r٬O7[:l EN:o/ajńF6=z5 unlV }/AlgɱA{w:5"v^lvmh'yr4#I 9͖A\ Oa{SN4<")f7yDN&[\ OacyFu"G?9=j,ms} q 9]d AiC~*ʠ3B&>1 Ey6!';eq5ąL|"rz_Y/y&{g g1'?VWh9Rxo^o͠9fy$ T=|NXVlr72@I9vݏ m~-Zם#h p~Z#h 0LNwGo ;W2@R9]V]w"dH4u_2|ADֺH2i?rݗ -_hN2q%;w2RG\w"dP 2IJᅩ'\w$dPFh/AD`đL R9gq?$dFTr@f˩D&b 7ߨx|NuwjS% j6nI"ɨpB5~|$6QFSk׎ˋU.]|..dP ]U/tի=cvqG_o|X!#e7bfחKsOuQVK)-0uǁL߸qW_}RcGݺuiׯo]S`^Pgv}{jw,H{~ӧOWӦMs}ɱEO4UX|f`[1bE+Q 1w\SO?餓 >~zwu ~lt}O\Poҥt}P]vn!uԨQ.z%Eu"W'.(Vi&uwzbA/>_l!:0iRtя)=o xs-YR过-?taݔ۷jݺun,ԱRe//t4i3gVZkc#x.ՕW:[o{L-^>٢h ,w}=֭S{[o|M׷͛7n;c kU1&0 W1Y'S( aϧ_>f͚n۟Y@~?Liݾ?0zn]ES`US۳A.F4;)6? A@/9)Q"t:"+~h۶:ôBӄhO>D=VjXD[|$R\ A6֖:w-[z?ۘ VKA?IF02dVJA2{ pFLT%'x=q_!}6sL+DrlccٲejNã-  @<%q1B7mj„ Q*k0p0Alk[q)"#q2B7EZ*۟|gjtAu1$"O0~"!U@ S#j^d6x!)+_! Ё%Kү_?#Id91@!H2T/A?'j[u'ELkH%jIRz/"kc@b-q6@:U`pe(7JO:b+F?j7 9Q q{4[ԩ:ۅ#ju~c@$WpSa$IRPIeKJFd"2aW2q AK _5Pbc0_}8 ,몀\Ej;Hѯ" qOo}c |}D ;,>\_Oeb%I3U$MxC`'x;b?ͥu1 Dnw}=$W  o^al%:U@7bS"T_!t] @n@翅=3 B΀EXd$Wȸx w7S0A7I 9SN:A?jUv^%ֹ$Wb0i$5g_ks4B5)[7ʳrq*I7!Pj\U7ٜ%[~0xTEƸgORZ~ BN' ooFw=}Z@륂 9:|Z\|i59+_!8{F,裏ɓ]w#tԣ0L@>R߹W_!HA 0$y;{9CH/|V)9LW{n @ڌ~hժ?3q$>zG])3p<2ζ5~*G~::O*7*i4fp%<)!En j_}nDna*`KhctA/M]RYO<]j˚~9uh f|-ZQH/>_-~Un_ʳgr"q41h;]m[=#3ڳfٳU>PrԞ;fbpF3 H]4IvJ*tmM( &rMDyo|xfK6^v7OUKzKmjJ~#GLA703ipUX͚3P+nIoG [$5)$gMnʒ/SW6:O?f%2ߨi<$H~'RIpüM J-ZNTj뛔xy"ԡI׮NRYۯ a" @jEPa.9e?(+dUVyXItQ9]V?BlflTIh#Sݥ?6hb'!Yں$9! (*q2#'s"8T塬|q`M7^u蜳#nGa $;SIxPŋ޼yo\8j͑Ì.!մiqǹ]'z@;pɒ% zBy㇐!ǽ26fWνء (X2Edu+ooBt}/0< /I_f}ǎjA?A}y۳w43(r dg+--jժ'w 'Yfm11%[sъ65-vh,5=ۍ;V}׮_% t{:՟B~2tۑ ;aJ8tP6[N롱AKL : ֟)>S}2Zت($m۞YqIe oJHTibvvBmO1b8_0?0 8X岚hC%>{,k£d@LIsmڨ%fVE1c` |zbƏDB-Ps)2md:t:vLD#z欳a (h;>,M ɼFDe9i'O~Gk/_:}j1c6l mte;mL3ǜeRp{EOEHvn;d(R|?^m}y+Jϵ"0"h y{ | 7"C~*|͈#bUO!CheM׺vdl4D36K^ancmV{f3Fʁ[\D[۱lXujկݎ !@+rm:YaCϨr[n&?#am̕SvI"0uhԧ*2#Z"&!\xA6$r{.pqhz Yxސ_(`ʥ1" ~m1զ~Zz9`oL |sl3Щ[Be}4`]w ƎUwwݍ--4@0F<DQ*"0ON{]zף^hmVvcz՜-f< >nxBu(ooCFin  ʒk-o&:S{Z9k L(˓goMyuQ@˿KC5z4tn86Ec]wJm֨PLٷgjDkKiI$8]gOb2-ww蠖 3fB#L2DWMŖm:w<@_kմ];vƆm=O@4aՁiT_/>?leL ϗv$8ˋPVSF6쳏Z6aܴi?'@="z )$˗4p@/6/x@5bݶwWdaҤI^kHRBH"MHN0D`iiopժoھQfN;y7EÏ1pԨQ*C G6iS(c2A'[}+X`3!lNlt6z* Ҝp ZmJu׭tf;`@>WzLƿ5մ"$vnC({Ȁ֡ٮ..зtQ)Xu<,̝;W=SN7 \z[}-7I;e˼pqDwWoTS͜xI->3u3<[k,L.ޚCm`XsOl<*]*ƽzvln@-3Fm0K478w 6')ckb uA'tjѢ￧8XCA ´cjڴiZm΄(_"+U+QNm!luXv뭪V R]2}i^&+[mؒ MxvR}C;UD91i!ug$(l#U…N[H/oذgԉC v6(Z$P;W0iwXA(!XT/nkU+ VJ<,@jL:uzwC7؆6Q`[(80u2Y3!`^)mVe7yA,X׃=yêG$9pfWې .)hRI혾=VI4 ٰA;Vt =~sp 7{{ꩧ <= C-ܐCD$0p)V=8w}w[ ʔP=X%_ kC+ydp?^gFPɝ֔u{#Fx~It^zw_uق ZTZK8DNFspS =XmڴDGC!YǼU~ꫯdwTfm>O^NK ArݔM>V*3-[Fr [ ː-U*G{~0GYX 3+v^*A&,HoN58!r"~TecƀԑwoT3gӿ``aU؅ g0!Hap8VM"^hǎ!wYu… c2}up IjzB0}z2A wrT |+pIsVĵ`6}ĤmAa ~vǏNL!!j\U׫**߷4`Rss/la R$J umf3g0,\ iS?7iC 9qƅovLUnP- D1]fza^* 㧠C;EηS@;rA ~70LCIE K]Y11#^,AG6L+>$0h#ݥx1' gO-u%1f1.|U˛ AkAWv *Fv >2811f3.x#'Hq"H095`Z(O8D `bg?)SԬYݧ4iDtA^k!`FgAN&PmknK7@XcᰈHՌX5Jl1amBXAc!`4^!lwhD]&Pƍ;{{>l0dbba\!RRH!_ Q^;1.*l&%:8E vl5YxP8T ^۷֮];aPU6gחˡjg=6o r[\GIۚq-LR|y3V 7lf_Iw[Tc1ݙyQyf͢: ~A$$a7܁y%$'%L*02di]E~~@,D$iS<  5 kx:2q*=MH>W,`1=wÈP5xnBA(=A ~#w\ZOu+kuN&A9fhJSeI. *;1%c7{{=ꫯ-g![c:׽~zKMa2ۺ['vx& C֔+-0D_X^NFB^z6"񞊛y.l vߓ{u^pO,@v{ML=d{ !X#G!K=4Qr3>,0V$nN֨K"pXv1t˅"w}9Y={z\_u m_p|&0 7D9` P'J1 4TD5.ZQ.Q~A=+J%-#BL*qpGj& c_RYsrbS!NkL8G0NֱT)fx?_/5M)hI=EeHDȺ1#i%aT-v@;"S{IJo"kL\ 4 1jI$hё=H,X̰ ~ȣ@e#9mʐ2|A1h,ÖDk'"4JX]㠠vqʸC*opJjiF{T8৺fM<ēZuC7% 1GN!@BʉW)O,J}rHRmѭdd5jTlWt  YtwY"cY/~9Po?cMR:RD[əjb;k'BPAb߉ ~@L;s_(h KeyY耍Ҕ'p[C 0/DXpbUdg\W\O?XO;4_^ L"!2E>"N@?%Z* WWԷCjy@E>!@^"mOS5y4zνBIѣu#bGTd5l^T̛7 ]h뮄>l68S}箻n TEb#HU$K+Q"m}ad #VIu,4B@ӴiSuꩧj*֞ {d@RrR-B+\P#b\!rqc*[A ÿDyS"r |,'u `ͅM PP>o}Uڈ/˿TVv@H13$Sʚ:q[Ď+ԅ`+O!dqThVP}幏t &$ZSwfFB >a4|C< IcʄwߓGqY D9:=||Z{d\qㆆ٧N@.;?\k:T.FT~#֭ E">UWA:t:vgoxu .e˖?YXEywE \`~!hb^QiZVϞ*'naܹsmouq\w{XӳaoZ3M[эTK~ @y%;4 ͫSҷT. r^A?`1gF܃={T{챇GHuIʁqρn>KO$LUNn |jCqv!=oW@g9]r;V˿p5K2 _]YU(M#X"}|3y1 ΰGqq3*'L5m_ F_n!u0\ Uf|=yMfTy`Or *W/myY^wm|ߍe .W@$OQ!HX >uTZcp<+$*EHPb[~xiL<˟52':ϡ.h:2 3pXM?K)\{4).cN6~*&bSԆ osћM81򆟨ru_,߱rzX~ acn#cPP=U#x?L.*>J*j!`=4hV1cD .[yH8rFZy:˳AEX4Cj !Mm\u~- az3x6lև tC͡.;K#NO]u"t?^]om}([>'| *z-r--C!F1ׇ ޽{k;Cv{;te ~U!X9~/Z=,`jU \P vrO&aqIC)/[Ξ,NE;~?BvZ]tk#_bz?j *8Ԓ1~`9lPܒ uu6lhvw##"Bʙ*xdݬ`(1cʽ}*q@Un>Cw]pn @@âOWPm?:B'Uɻ]pN;cbD&!Q ޶'7x`:s ;dbD\uC&)FrQmuJg$LR rDLf##0JJe@/ΰG&)F`7& ňFkD~ ňT_OE wfD&{=2H)"}٬aLR!5u3 @J M(܉}ϰG&)Ei4ZKΰK&)EorP aLR*WMk@ΰK&EssE~庯v-g p4IENDB`( R{PnsY?% #=Vi%n. r8 wC|NYdo zmO"#%%,<^)6 GW -A130L Bq5W9bX9=m"G ] m sOV<x9r)essUpp mm Rii ee8cc q__?[[ zXX&&,,Tss33,99gAAII7 QQp**TLLllee SssA|$$6DD.odd##i $,4<--,U::~<vGGGGc[XX..?rrHHUUKO5 bb77{{>Yup0,HU %N,T2a,TAt+ S(R+S5e+RDxL% ?h~}{zxwuts:z}T10 +6x_:'W i#L+C[???????????`?????????????(0` |>bJgVlb pn  uz cLzjt(ng D%}`)@@b<<N77ss H ;AAvNN $U99IYY[[PqqII25&IhhV^7-g5 aF{7+dlq eO2 I -v-Nw???????( @ 'Vp0$4- 88v;D&L`!k/zb{:aa-s[[h ggW U22uuj ]]44 `A>Mu 6)bTnlkjhgedcatd_'"߀c?(0 G`NCRN91VZ syZf? =;;7vs||}%%ff<<5+`XI1  AAAAAAAAAAAAAAAAAAAAA?AAA(  2 ~ ?Yi E>憬Yݲ_䢺ӤnMG A?AAAAAAAAAAAAAAAspyder-2.3.8/img_src/spyder3.png0000664000000000000000000001264212626055322015264 0ustar rootrootPNG  IHDR>aiIDATx] /DnRmSEDdkӂBdle{ A*CØ[̼֭][zc=1vN! N:y~ -XB^^^N 搘Tl׌$a - FOH>a _RqPl 2 03/F'Yt,~C_yJGA8.V#BA4Oc{ }:-VAAA|G S:NIlI§1F/G~]͜EA(CJxh #h>_.^DXMXEXAXNXFx{E aa>a.aa6aa&!0C%t[\Ӧ&&FFp ' #I& &NH& 0+t#n   5l~ZkkGq҄H~$( ~(>'i¯zXpME_pHf=  99YA 2DAJJ7xC#ȑ#ٛoFFƌƎƍƏ&L&N&M&O̦L¦NʦMz-6}towy͘14% DjՊw}>Ⱦ;+;8,*}=z ;84hZY8l~~۶mٱctLjo۶h.$d~A֮];-Z}(ᒘ"w!ڦM.>}p7߰&M(ܹ3;~ 6d<ֽ{wֿ8kůW^Eޏ_}Tp-uЁ͛RŋeH& -_̢an^x_6mƮ]3ᦨ$g" TÆ ',ȑcD^UPFJӳfϚ5W߷~{FgϞ2"!}h;0}fVDN!2,H{׻woG~g.#J/,М9stRp,8;<4"|Moʕ=zW_M=dѲeK~#D_vJt=Jܺ ? n/N8aD8J4JFJ|ڵ0͚5 @{g̙3Ν;[;b%!jgaxwf,W^Q>a1"ԝ>cҥF@~!FH[A-ZGyD9:@w}mݺ9rVH70/8[9M>QVZȢD<"`>.%/$NB/veb,`g"3r;uT%·lm?KJJb:uR= &^ )%QsV.b2O?UDU[gG?Sʓ=tPE)C/]ŗBP&P̪҇.\ W/fh| ۽{7 ,H_pf1-[J"O5s`Ph%N@vj}j9P kp!ϞH<~ʂӦ (Ԩ+TЦueʰn`!F;ge1}M" ݫ\T,c64!Z-{vnB5jPV,X0=}}#+XmY|q,U-^5Î0^{5xq9}$Zv\99;މ_?9МY˳waK0?(A8!NkPljyi$geaV ^K~k[K/[x؍7<k?`G NA5SYHڍ/$A|JJ9V^q#@L׮]AsҴ7W\7dYH7nߪC1aW؟ %o \֓[(o` ] l RX ͂f AGVVsW:6 j)Xsw;AQ)3yJ mNK}/&]$. |In"ߔ`?LOXAp"&p&7"E'D|m[4H3cc1D^,n:T~8N 3j%ˎIH ip.ܡ^|g$[Ç7΀Q~ BUC1s-@-n*Dێ)ú֭ͬ{e WpZr\H1igN$@|UcWaUWI#Eczq9WP".$"P֭… Iva ɝOwnڸ t<lN lQ H/M:\y˕Z?s=&Pmˢ;I9-|f0e @o)gצMEK'xAIn3icyaH0Ls* JQE28OǼ ?t'y 6l sy2[l$" UeRRXWpl5jhB\ʪEl9,|a\Ne:WD䙅`nvʕY~lC1(|E#A>VLE#HkqɿjOs^`;^xoɣ7ϢƢQ_T35튰Q{)/xI #wC>EA?ٺ5Wmʂ3f.rLJ:'&^m/ P]mfdi¼DP8Hᆚ5c޽Y05a"p{ݺl߅:&ҥlƩp׹G̒QZ"Ϋ'>_\p˸%NPl"9D$0+|xѐI$sF j[!HbE֒r8p^6 51hfK2QgNwO8Ѐաe+yBѦ9f2A! džn4iHVsZ\([S$$R !xCnנ)C3Rc68;;[1(Iw)|=Uej(IV@/kx 2?Jٚ?0χ# ,Y"|C3&yCU 'Z23u r>k"GtJ4Q"Zc-[XAzιm+UbcMSN\a+E[:"pJfTmMF8("Əgq -"9<6fM6gO8`Z湶Y0l? طo_S'aZ5 (Mȫmgl2 rUj-' c,F 6cG[9-X@4t?`1oiU}[m54=*)42V-j:,; Ks :EOU;QDJ.N')8 dQkO N@Ů8ekGK4iD;zQ}D0އSBV@NҪ '>Fw _N!)X7SFޱ ߫{ؕe5 @@< G8V)L LII1 JKK ~U='x$X~"@] GP^-^P Qq'\i5{F!fL ITm~lsԬEvs2kd"P: 7wI? {kuE?\`3^7Pn-@)1"8'&p VHPjiY99Uh x 8`rUX>5p$D<ճ kyj H.ЃfIѪ8ʋŇS7ۻ蓴l2ry 3DJJnAhΘ5J`0=c+h*+ǁکҰ5OiD`H֭+@/x@fw[5Q[10m('`dt˴իuh`5~s ג~п >a76 ZhvEc -CrZEBQ*mg[^_z.׾|~]@_D_k0=[LrUEyVzGeL\xHA5(ؙtRڱ#V0T;0jie!d\FLLl&$"֐WB.o~|$+:ƞS6: XŶk~ aER, J`vhC߱%baqH?r& x$;&^EL(!ӧO7Hz>SCW AS Zj߭,L٘̒5(0Khojyaq@jK;Kp="m(谹'"ү[H6YԮƨ%aiIDATx] /DnRmSEDdkӂBdle{ A*CØ[̼֭][zc=1vN! N:y~ -XB^^^N 搘Tl׌$a - FOH>a _RqPl 2 03/F'Yt,~C_yJGA8.V#BA4Oc{ }:-VAAA|G S:NIlI§1F/G~]͜EA(CJxh #h>_.^DXMXEXAXNXFx{E aa>a.aa6aa&!0C%t[\Ӧ&&FFp ' #I& &NH& 0+t#n   5l~ZkkGq҄H~$( ~(>'i¯zXpME_pHf=  99YA 2DAJJ7xC#ȑ#ٛoFFƌƎƍƏ&L&N&M&O̦L¦NʦMz-6}towy͘14% DjՊw}>Ⱦ;+;8,*}=z ;84hZY8l~~۶mٱctLjo۶h.$d~A֮];-Z}(ᒘ"w!ڦM.>}p7߰&M(ܹ3;~ 6d<ֽ{wֿ8kůW^Eޏ_}Tp-uЁ͛RŋeH& -_̢an^x_6mƮ]3ᦨ$g" TÆ ',ȑcD^UPFJӳfϚ5W߷~{FgϞ2"!}h;0}fVDN!2,H{׻woG~g.#J/,М9stRp,8;<4"|Moʕ=zW_M=dѲeK~#D_vJt=Jܺ ? n/N8aD8J4JFJ|ڵ0͚5 @{g̙3Ν;[;b%!jgaxwf,W^Q>a1"ԝ>cҥF@~!FH[A-ZGyD9:@w}mݺ9rVH70/8[9M>QVZȢD<"`>.%/$NB/veb,`g"3r;uT%·lm?KJJb:uR= &^ )%QsV.b2O?UDU[gG?Sʓ=tPE)C/]ŗBP&P̪҇.\ W/fh| ۽{7 ,H_pf1-[J"O5s`Ph%N@vj}j9P kp!ϞH<~ʂӦ (Ԩ+TЦueʰn`!F;ge1}M" ݫ\T,c64!Z-{vnB5jPV,X0=}}#+XmY|q,U-^5Î0^{5xq9}$Zv\99;މ_?9МY˳waK0?(A8!NkPljyi$geaV ^K~k[K/[x؍7<k?`G NA5SYHڍ/$A|JJ9V^q#@L׮]AsҴ7W\7dYH7nߪC1aW؟ %o \֓[(o` ] l RX ͂f AGVVsW:6 j)Xsw;AQ)3yJ mNK}/&]$. |In"ߔ`?LOXAp"&p&7"E'D|m[4H3cc1D^,n:T~8N 3j%ˎIH ip.ܡ^|g$[Ç7΀Q~ BUC1s-@-n*Dێ)ú֭ͬ{e WpZr\H1igN$@|UcWaUWI#Eczq9WP".$"P֭… Iva ɝOwnڸ t<lN lQ H/M:\y˕Z?s=&Pmˢ;I9-|f0e @o)gצMEK'xAIn3icyaH0Ls* JQE28OǼ ?t'y 6l sy2[l$" UeRRXWpl5jhB\ʪEl9,|a\Ne:WD䙅`nvʕY~lC1(|E#A>VLE#HkqɿjOs^`;^xoɣ7ϢƢQ_T35튰Q{)/xI #wC>EA?ٺ5Wmʂ3f.rLJ:'&^m/ P]mfdi¼DP8Hᆚ5c޽Y05a"p{ݺl߅:&ҥlƩp׹G̒QZ"Ϋ'>_\p˸%NPl"9D$0+|xѐI$sF j[!HbE֒r8p^6 51hfK2QgNwO8Ѐաe+yBѦ9f2A! džn4iHVsZ\([S$$R !xCnנ)C3Rc68;;[1(Iw)|=Uej(IV@/kx 2?Jٚ?0χ# ,Y"|C3&yCU 'Z23u r>k"GtJ4Q"Zc-[XAzιm+UbcMSN\a+E[:"pJfTmMF8("Əgq -"9<6fM6gO8`Z湶Y0l? طo_S'aZ5 (Mȫmgl2 rUj-' c,F 6cG[9-X@4t?`1oiU}[m54=*)42V-j:,; Ks :EOU;QDJ.N')8 dQkO N@Ů8ekGK4iD;zQ}D0އSBV@NҪ '>Fw _N!)X7SFޱ ߫{ؕe5 @@< G8V)L LII1 JKK ~U='x$X~"@] GP^-^P Qq'\i5{F!fL ITm~lsԬEvs2kd"P: 7wI? {kuE?\`3^7Pn-@)1"8'&p VHPjiY99Uh x 8`rUX>5p$D<ճ kyj H.ЃfIѪ8ʋŇS7ۻ蓴l2ry 3DJJnAhΘ5J`0=c+h*+ǁکҰ5OiD`H֭+@/x@fw[5Q[10m('`dt˴իuh`5~s ג~п >a76 ZhvEc -CrZEBQ*mg[^_z.׾|~]@_D_k0=[LrUEyVzGeL\xHA5(ؙtRڱ#V0T;0jie!d\FLLl&$"֐WB.o~|$+:ƞS6: XŶk~ aER, J`vhC߱%baqH?r& x$;&^EL(!ӧO7Hz>SCW AS Zj߭,L٘̒5(0Khojyaq@jK;Kp="m(谹'"ү[H6YԮƨ%>N0wvBpNw܂/>;[J>kyk"|?`9yW)p5ݶDMVޭX?o^:  %]ѱ:!'yVPwo/fZZ  m>MBPDm[[6Wv~Jnaô@w_(4ϿEН=msP/1* I=0mOD~»^=|>`~v:CNw O%xu}`},䇩v'|0B[[% o,߳ӺC5xQ_a*݉ߣ{¡X߯AVr'5 Ѫ?E)d =]U'|R¡y+sЅ/&,/<1~>v9X<^G_>`~.!o?2|O7}Ic<ߟ'un|0 ?l!Sk^ 5p^J9t Iaw"onMx|o>}n'>AA"7W[8|ߓu[$5ݧm{ xCPhsp $xXm~ؾv/,ޜ8&|0 ?x)47bT޾Pi0uKIgo}:Mb57)QCx  ++{-'pGn?*[jۺO5?`*P-3gR~ˬ< !@/ݻwS.~644h@=&n;v=tq*W2 }Mtpə3gYft9ݗ@;W]u1ʒ%ÇKGy5j$YOL}mT zoc:uDK;RJۼya3o-k #j>Tb?G% %Ueg~z7n|%z$2JOj:FNBo v7|J :my#*FR~RK<>ˆSτ K.1}iӌ-Qۯ(3f̠?8ݿ-ZzI3[rsy{ Q&pN^[FuF{1V… >oAڸ{q3OOV=2?yH5БCSժUM+濉H9hK֭3ٖ-[=.uYP~ uI&]o 6G}w`$QqL27ha eER@ѯO>Z8˗7֡Exh׮]/Rzcʅ5Ųyʕ0 ^qmx"(. l߉G͛+V<@ի7ɓO?Xmx,O9~4IH5ӯ4E{B-^êa ßRziӦ2N-H箒><'NfYfz\z4dKCq,]v "[QC5oPt킐ь~ g?ՃDo_Eͥwo95=fj^k}/F<%縷##Fh3|5xn9Rҹsgߥ#,&g:<#oPxcFhv^z%O.ѫJ7|qs1<,eSbECX$03aUFhΝF 6kJ *T0;E9ru܉?8կ__W=" n~:|e˖g͚E'z3Vs"C}e%ALJ.mVE`cUD j_4"7x#F_m۶f.vK20rV ;L\ ,?`X>|nnYA+>"-뎘YI;o܆m2rNVt!uN>r);r5Ji[kժEO?zEMH\ N_4ʔ)c _EON_*e`^ݽ{w 0!ןjڴiCwuݯ`mEO@*R~o.t=z1X9ꫯF*>s-q H&br&@ wc{1f6s5}GO=qy!RgTQ0{v ;v2+_wH/HVYC2]u"EAӀ!nƍOncJ&!hVG7ѢE ]:tHuALG9(za z!jܸj"0F%. [~@*+H%V} >8΃stFl)@t3vYFhRQ[:cԩ'>4Zj"҉2ҀFH`D;v1X9X`cmr@#Z8Aּ 2l3VE E9eFh+4y,;>> 2=KЇH~NAfAH0B x1֭[W 6q3 8 Yf>ppGxL]khgSP;>"/q~(aVxq_GۮVp/V&\tƼy?|!0QPdZ(>% wy8mގaĈdo1 (G5OJÓ3_$V҄n1Dnl=XiN,boqģ/X/V㏖eVȎJݾXرðèc`SJ_s, W_4D]4Nt{kvWڿ~6KAyimʻ~j_4/!`ۀ+CFA+O:vo( 疒[-Gx"HU;wnT+989U q^jMtepBzt7C:DHh8E1M6ٳ]5|'?[*v.:~41GIn0ECR9OrJ#S@zFdH#:Cx:uPu7`Zx=XEnA 9,5LЎ% 7/b  [ΰx0@b %2Y.-]ϻM ~(7+W.(aa9EB'/ҪU+YݯAހsJ}6ޡ̣E+ ƌQ&0Z3@0Cǖi{t~@"$+\9#hxZ$8E!PP$[(ń9 +be~i9s4j!6?iosER}{gD)޽gFN;..R ϓM~2wҘ%_]kސXkI~c8JNbd~pAMH̑;nt*wrlXmz@ \AnNHp}0" a>j[Y+ߏBHMx$ڬ}{ bg-v7vztPhՁöcV`4J4HcKG}6VD48L;:ذa†ÇJ6ec-w s;JJ#;#;{,wk7֭3}^>FV-7x?_D fSK&~lDPlՊRb_?<س)d[l:/$ಳgf͜9sVHY3f͔)ONN6IIIU[x] "(ΌaO>؅rOCt҅nv0}ضm6 es.]|;!pO!3Kˈ!! 0Eso޼z衤-o{]Z})ߜmlQv-D oO)ѳ'fZh @N2Ki_$? @_S$H! ~#SIC L&X7aQߡn[N8( 9&ȝ? "(-H4?`Z?{1#@^ܲS;E` _ᥲ;_~( 2U(3i{3pñc]*PgE<-y_=Yk'._ D_2J5j-ZHZ;E8qB`ҥ܍@zq -hтK؟!*~O3 (ICߑ{+W(LR.CǯT ss8aUr;?^qb{2 pK*eիW XHD'EC~QѢEt挑4)ca1S(oQʜ ȣȔ Ko_ڀ|ð)]!Hgu-t:,=#yf={6}G7>Ey's'^ナ+"|jBO#,3K~$)ѥ3#S}_~i@⋢Ǚus^x33D=HAK`{A˽DGJVHU ʴ˖,%MtZnc[PXnnF{agϞaÆMj"PCtuV\M2H7_[E /rlFA:;OѪ`饗ļ2y&0~V@'ug)@(IA/yd~QKKC;S|fA E;qa(XaMb={(^! J&M"yw]0w!ȭ4oX2 ":t /դ!\II뫼<7X;*9 =.!͝tڕnV߻ϥo_k]inZZICb^ɓ)^;EAj֌|V8*~k9526d//Ǝkm^ёkᇆP<lЀRF'"QPq 8P2jA8qjx}vq! H,fϦ-^K7~zWr^+, qPXήꫯd$+9E&ȚkO /֟@8(B$yE;?R >6ǫ[nQE9 .=YjLuK4ǍgtST @C}j<7}?O<2>RݤيE8nMYC$wojcBJmo?Uw֭"YM6!^D3jf&8:}^>H)F%|]`wE2ο+?V:?js E@##0s2zTzV@1c2g1,-UIIP@EWd-`ԧmڒl f%X&}7 ˿s_~wS.QTV_ y\vҤI4s# ^V'N8;'X)G4/`FEk@IT1Jѷ9Rc!i m4G:=:?D@qs0x~.T T zom LCG?F^YD4Gk7j>LRRpp %Ӆ %Hf:zdh,ݿMZQfwdv~p]w [`%յUt8ΣK7;wB-(Qx|#[.١x`ZB'm" &MЃ>hHrd')(uD{aH lY(T2P,ƨgŊFCTf_5T z@*,۷oO:E Um[̙g$5kWʾj57 fY Q` e *u^/0;~Qb…N5E 9H؁ J0 XÇ(wӑy(7  /˛΅%67R[`w~$vP*E` E"dǏ. PW.8hFUyZGﲦYUɼk$ӝjpPnܹF~D1 #ɱqYܱmϼeINiz|\C ?$AP .QW'NyA"zu*u!doР=c:.U@+#伉})6~`I9l_ ? Xv|Tf})]Ti~%kPƭؒ%KRj$FUzV5T@)b,oi:~L#m޼z>g-PΏ/Pps޽/;eSHW,zE>6 U۽{7Fġ]nS`ՃS"E6a(yʯ]g'k-\B/^Lv17DYlA)+VB +3qj($tm\y%y NzEhE-?$/E:",q:W/GcF' VF)+Wmã%-[]f %5o](YR&L0pF|׌(M;ZʝkS A¯X ڸ ae޽mB(]!%t~kD ڵKX0OΜ̃GIO>IoG2!CHgPQ i!j>,zIMu*=]va-R@?$@Nj҉Aʔ)#\"A{֮]K|SbŌE:,S' Lxm>G xVR.X>rH ]'`Xp.(VQhQ%F0%`$Ĉয়~w\aF;sR 6&`ݺibxqӇA 5D˥ @#Mr,JqrEN$G$^؉ !Py(0 i !0B]4Uxee8ϣ1xQ;?Pw,TXf6-#Ώ"EjP|y~Blذ#GX+ܡY :G-R0\"f xl0p@*T1bxF@ W؉'mt~t\yH=BTF~!,( '-Z^PYwwfN~n9t,X2z֣ӯp of+91A-[b~h@Mt4t䝰 ۿZnk,DHA^JJrR̙!?sA/q(g{d:%ѹ?'(~Xe(YMLd#e\;A:RÆ U7eY@)ܐ&Pnݺݸqђi,®q2+4vX#kY0,DQ&o޼Fbյ;ciLHₑFOV'%:a3yVv,!ziLΏZqhL6rBP|-A *@V 3LGhi3߷[6!]v1?Fǜp.UxׯO5j0|N;c 2=( ˽`j)5' *V,r3 8<sehWc0¨W\9KkÝ:u;k&u C:? vx U z HSm'9 t{-Z,Os ԓ]"+-59H5i$ݗpWծ]X ^P %,VH6- L8 nX!J9] oC7*F"iF><S#Lpi#D@3(Jcp *pqp_!&!C2+(\B1n]:cﯿJ۶m}iIP(dc{~vy/w~ߋСC#qWnV@&ө,2& "a5v›_8-CZpcVJo۱Xq\#E9+pVH;Ya xm1B]3Y X!Bfs ?/zLQrnʝ`EiӦ?<**CV_ʝڜ!&PjxL#4 wEw> f),Mto8nUQ:<׋iƈ0 eFoJo,UXPBI 6##!:7|-`ҕr,>p(xD`O' %HvbpB@ΖčЙw 5K,SN5& (YȝR`'qTr'eF,GdӤ/Rw |A,=t[$nCԀvdAxaw7[FţDq*!$aX"qe}6Uȕ+QX$DգDSER2~"qAw!Y|&OX B $"`:`3Rppλx+o2 kOݧԅ)ZlIǎ"zU 6jq,tK$TG dw{ 7Y庼BժU}?eR,(D')w)FRÁ6g/B… +_.>}7 *hBw,B<򈌢( p͐# 2ŋi<4]A&MBfTG˹q$m[5WtWR_?:9;*>pLh[1[oL`&qM#^;ٮ7׎vHw^]Ν;SŊS.ob Lmqq] \hQ? C},gk,F;z0h!=KìW7L{ҫ V~TVB )]T_;CW c!"P'Qֹ~m:yndxD"'&X %mВoMpG&T XYBWW2} Z R~#%{u W" 3V~@PeBEAD$H{Z!xzP7…Umի wܼU ;Ï`ǎ" D󨢃 D "V>dGsx"p@:&ag-+2 !@!x9E-/*5S6<J wdo٣4apƆ񰞊  ނ+%K4"Dhݺu\yS(PXʫR1dB6LJR ՗EHZ̍T{oʻooC1'v+Շ_B'se7EzhVxҮwL{7Nќ%PHʥ~˖-p2~#,'OF5C&͇a;?W0 c3jmŢ`c|1+!ߺr!ؾ}1"XnQFFPYF-98ކsz("ahmwفÑ̖Ot[vmZFi{ۃyK2]VOyTNӟS4w`hi Hl aI}׌/??x9P7eX>Jr9Bڤ^:v= }sNCV^?gSʍgS^¿ǩcӼIb0S'^ben:,_KQ 8?Ν}pE")4bڇ/r78yv felE&|rvG+cE3ߪsN:歪cmi>N9`$wp5V>DYfʐ)`=Gd]vu$ 1 ktׅ¸яGm!5yڧ 2`Vk"# M #kDY={pAd()3"uS|ygqDep׈mۮ܅ 9oo,r4e(4=ȭQBb<"Dhڴ6*Ueʔ~LgrSJ4C*i[e iJ ټ=~swc[`]@/0jڥFFdY!v(WWX1ǯW:`i)8 Bt *pAǟ$49XB̧?N3r!8*s#O།)(WT">[-F!ɺ:Ij)Td>$?3C N͚53kfqN/2P͙30Ƣ[nT 8(= =xy]7&aOP(տ!@!Td_~FK@X&Lpѿ]nݺFϖ-rz;宛H \x!(D!SѡCG!Q7qDȕ+]W| u|u_$B׶)YJC 8ov>Șeȑ2uaثV.w]R~^HuFnf@QFm8cǎ5^I($T>ՎJK$1 C]xם74w]dM@,PHdOWt캾| jۢ]X G}w] HM !0?~.Nu݃/. ʪGb>|p1 p\wC|'Dw!TA஋8"*5&w .yw׍O|c4BnZv_t_R "Sl!T8젰 ]!, w]dz _q/DJg>jäV6$ڈ`ReJw} a@9ूaإ>jð4PYXv5aX:n!nuaX!_yC}aXP\QC`Ww} aXBbUƱ4nuaXFCz>ð e<0Bw} GΟwGk0Sw} Ga(;TYVn:|( VWmQ/ M8; _< )p^tG-xk/p:tG-x$}kxbpvDQ#C ۇ"tG--5{!>IENDB`( R{PnsY?% #=Vi%n. r8 wC|NYdo GGGzzzzFFFAAAAAAkkkAAA\\\DDDAAAAAAAAAAAAyyyAAAAAAAAAWWWmO"DDDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARRR#%%~~~CCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMM,<V)zzzBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJJ6 GW -xxxBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFA1-0tttAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDL Bi5qqqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|||AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBzzzW9nnnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoooAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArrrbX8=kkkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA```AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkkkm"hhhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASSSKKKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdddeeeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEEYYYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]]]GbbbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWWW___AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuuuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARRR\\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANNN ^nnnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJJ ngggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAppp swwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIIIOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArrrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdddAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtttAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVVVGGGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[[[<AAApppAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDhhhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJJxdddAAAAAAfffAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEERRRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBddd9DDDAAAAAAAAAgggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEEOOOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqrAAAAAAAAAAAAAAAgggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGGGMMMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApppAAAAAAKKK)AAAAAAAAAAAAAAAAAAgggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGGGJJJAAAAAAAAAAAA??F55c))##..u::SAAAAAAAAAAAAAAAAAAAAALLLDDDAAAAAAAAAewwwAAAAAAAAAAAAAAAAAAAAAgggxxxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIII55p"" ++z==KAAAAAANNNAAAAAAAAAAAARRRAAAAAAAAAAAAAAAAAAAAAAAAhhhjjjAAAAAAAAAAAAAAAAAAAAAJJJPPP44""pp{```AAAAAAAAAAAAAAA~~~UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiii___AAAAAAAAAAAAMMMJJJAAAAAAGGG44AAAAAAAAAAAAAAAAAAVVVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtttVVVAAANNNIIIAAAAAAAAAAAAAAAIII VVBBBAAAAAAAAAAAAAAAAAAAAAR88XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHAAAAAAAAAAAAAAAAAAAAAAAA&&--XXXAAAAAAAAAAAAAAAAAAAAAAAA**33fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGGGAAAAAAAAAAAAAAAAAAAAAAAAAAA))||AAAAAAAAAAAAAAAAAAAAAAAAAAA8__33fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA <>G33fAAAAAAAAAAAAxxxAAAAAAAAAAAAAAAAAAAAA))AAAAAAAAAAAAAAAAAAAAAAAAGGG||zzzAAA>>G33fAAAAAAAAAAAAAAAAAAAAAAAAAAA))AAAAAAAAAAAAAAAAAAAAAAAAAAAUUUAAAAAA>>G33fAAAAAAAAAAAAAAAAAAAAA))AAAAAAAAAAAAAAAAAAAAAAAAAAAKKK kk SAAAAAAAAAAAA>>G33fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAA>>G11jzzzAAAAAA@@C**AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPFFFRRR|AAAAAAAAAAAAAAAAAA>>G22EEE44e**AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEE{{{SShhhAAAAAAAAAAAAAAAAAAAAA>>G,,WW~~llBB **AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVVVDDDAAAAAAAAAAAAAAAAAA___VV556FFFAAAAAAAAAAAAAAAAAAAAAAAA>>G**AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA{{{AAAAAAAAAAAAAAAAAAAAAAAA>>I .oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>>G**AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\\BBBAAAAAAAAAAAAAAAAAAAAAAAAAAA22h00iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>>G**AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA((RR $,4}}}AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>>G**AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdddAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA{{'|||jjjXXXDDM**AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOGG,,xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlllAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA((www|| 66_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA___AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA44e dd5w22==MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvvvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA??F$$ZZ\\--44VVrFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBWWWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA??E))RR@ii==''PPyyuuuVVVBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>>GTTqMU8$iiiKKKAAAAAAAAADDDPPPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALLLqqqAAAAAAAAAAAAAAAAAAAAAJJJ]]]oooAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBB>YvXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCCSSSeeeKKKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq0-HHHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoooAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArrrHVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA___}}}GGGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXX AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALLLiiiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGGG&pppAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVVVttttttAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJJgggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEE```FFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-HHHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiiiGGGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhhhVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWWWWWWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDD4oooAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFrrrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcWWWAAAAAAAAAAAAAAAAAAAAAAAAAAA}}}AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsssEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|||.HHHAAAAAAAAAAAAAAAAAAAAAIIIbbbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA^^^VAAAAAAAAAAAAAAAAAAAAAdddGGGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWWWVVVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKKKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBDnnnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFqqqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxVVVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsssEEEAAAAAAAAAAAAAAAAAAAAAAAAppp.GGGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVVVWAAAAAAEEEgggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWWWVVVAAAAAAAAAAAAAAAFFF*AAA___MMMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVmmmyyyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFqqqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsssEEEAAAfff.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOOOWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWWW8nnnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRRRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFxxxzzzzzz||||||~~~~~~ .!XI~G, En}|{6z~U2,,=sf<#\ q)S.G"c????????????`?????????????(0` |EbPg[lf pPPPQQQcccYYYq  uNNNAAAAAADDDrrrAAAAAATTT| cLzMMMAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPjLLLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALLLu(JJJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHqJJJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFFm ^^^AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADcccAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%}]]]AAAAAAAAAAAAAAAAAAAAAAAAAAADDDAAAAAAAAAAAAAAAAAAAAAAAAeee`EEEdddXXXAAAAAAAAAAAAAAAAAAtttCCCAAAAAAAAAAAAAAAAAAPPP\\\AAAAAAkkkSSSAAAAAABBBvvv$$$$99UcccAAA(AAAAAAAAArrrPPPyyymmmBBBzzAAAAAAb3366`AAAAAAAAAzzzjjjAAAAAA??FHHBBEAAAAAANTT55`AAAAAAAAAhhhAAAAAAAAA??G;;QAAAZZZTT55aAAAAAAAAAAAAAAAAAAAAA??GXX$$||55aAAAAAAAAAAAAAAA>>HDDQ!!&&FII55bAAAAAAAAA>>H>>JAAAJJJ==;ddd""55bAAA>>H ==JAAAAAAppp--vDDDAAA""XX99Y::RAAAAAABBBvvvgg**AAAAAAAAA""$$==JAAAAAAAAAzzzrrrAAAAAATTg $TOOOHHHBBBAAA"">>JAAAAAAAAACCCAAAAAAAAAAAA,,y''Iii>>IAAAAAAAAAAAAiiiAAAAAAAAAAAAAAA//pPLLRRXXXBBBAAAAAAAAAFFFAAAAAAAAAAAAAAAAAAAAA>>J##@@38+Ixxx```AAAAAAAAAAAAAAAAAAAAAAAAAAA}}}V^PPPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA```7-DDDAAAAAAAAAAAASSShhhAAAHHHeeeAAAAAAAAAAAAAAAAAAAAAAAALLLgAAAAAAAAAAAApppLLLAAAAAAAAAAAAAAAAAAAAAdddAAAAAAAAAAAAAAAAAABBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGGGBBBAAAAAAAAAAAAAAA5eeeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArrrAAAAAAAAAAAAttt aPPPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPAAAAAAYYYFDDDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATTTAAAHHH{fffAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlllJJJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDD7+dlw%#"!iO3 I -v0Tw???????( @ 'Vp0$4- 8ttt{{{8v;qqqAAAAAAAAAAAAtttD&LnnnAAAAAAAAAAAAAAAAAAAAAAAAlll`!klllAAAAAAAAAAAAAAAAAAJJJAAAAAAAAAAAAAAAeee/ziiiAAAAAAAAAAAAAAAAAAAAAYYYAAAAAAAAAAAAAAAAAALLLbnnnAAAAAAAAAAAAAAAAAAvvvhhhAAAAAAAAAAAAAAACCC{VVVhhhAAAAAAAAAAAAsssAAAAAAAAAAAAAAAAAASSSbbbBBBvvvRR IIAAA:xx<>J 55a@@DAAAyyWAAA>>J??EAAAJJJAAABBB33[[T""??EAAAAAAjjjAAAAAA??Fj OO33KKoofffIIINNNAAAAAAAAAAAAAAA22h::`LLLRRR```nnnaaaAAAAAAAAAAAAAAAOOOA>MAAAAAAAAAUUUEEE```~~~```AAAAAAAAAAAACCCuAAAAAAAAAAAAAAAAAAAAAQQQBBBAAAAAAAAApppAAAAAAAAAAAAAAAAAAAAAAAAAAAxxx 6WWWAAAAAAAAAAAAAAAAAABBBNNN\\\)bAAAAAAAAAAAAAAAAAAAAAdddT}}}~~~nlkjhgedcati_'"߀c?(0 G`NCRN91V\\\bbbZ syZZZZAAAAAAAAAAAA\\\f?XXXAAAAAAAAAAAAAAAAAAAAAAAAXXXBBBAAAAAAAAAAAApppAAAAAAAAAAAAAAAӪBBBAAAAAAOOO]]]AAAAAAAAAZZZ yyyXXXRRR %%=>>IWWWlll@@B<>IAAA@@B__99 s貜kkv@@B33fMMM 파::U ee 22hAAAmmm??||}mm33fAAAQQQhhhAAA<hh++}TTUIIYݐSSAABlllFF^ bAAAAAADDD࢞AAAGGGVVVAAAAAAߘzzzAAAAAAAAAzzz{{{\\\^^^^^^gggnMG A?AAAAAAAAAAAAAAAspyder-2.3.8/doc/0000755000000000000000000000000012626531443012303 5ustar rootrootspyder-2.3.8/doc/_static/0000755000000000000000000000000012626531443013731 5ustar rootrootspyder-2.3.8/doc/_static/favicon.ico0000664000000000000000000001246612566665770016103 0ustar rootroot  & h( @ 'Vp0$4- 8ttt{{{8v;qqqAAAAAAAAAAAAtttD&LnnnAAAAAAAAAAAAAAAAAAAAAAAAlll`!klllAAAAAAAAAAAAAAAAAAJJJAAAAAAAAAAAAAAAeee/ziiiAAAAAAAAAAAAAAAAAAAAAYYYAAAAAAAAAAAAAAAAAALLLbnnnAAAAAAAAAAAAAAAAAAvvvhhhAAAAAAAAAAAAAAACCC{VVVhhhAAAAAAAAAAAAsssAAAAAAAAAAAAAAAAAASSSbbbBBBvvvRR IIAAA:xx<>J 55a@@DAAAyyWAAA>>J??EAAAJJJAAABBB33[[T""??EAAAAAAjjjAAAAAA??Fj OO33KKoofffIIINNNAAAAAAAAAAAAAAA22h::`LLLRRR```nnnaaaAAAAAAAAAAAAAAAOOOA>MAAAAAAAAAUUUEEE```~~~```AAAAAAAAAAAACCCuAAAAAAAAAAAAAAAAAAAAAQQQBBBAAAAAAAAApppAAAAAAAAAAAAAAAAAAAAAAAAAAAxxx 6WWWAAAAAAAAAAAAAAAAAABBBNNN\\\)bAAAAAAAAAAAAAAAAAAAAAdddT}}}~~~nlkjhgedcati_'"߀c?(  2 캺~ ?KKKtttNNNYi길JJJAAAAAAfffAAAAAAJJJ qqqAAAAAA|||AAAAAAKKKIIM}}-- ggpppE))IIMTTT--v,,HH >hh++}TTUIIYݐSSAABlllFF^ bAAAAAADDD࢞AAAGGGVVVAAAAAAߘzzzAAAAAAAAAzzz{{{\\\^^^^^^gggnMG A?AAAAAAAAAAAAAAAspyder-2.3.8/doc/ipythonconsole.rst0000664000000000000000000000305412606574046016122 0ustar rootrootIPython Console =============== Spyder's **IPython Console** implements a full two-process `IPython `_ session where a lightweight front-end interface connects to a full IPython kernel on the back end. Visit the IPython project website for full documentation of IPython's many features. .. image:: images/ipythonconsole.png 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 When "Connect to an existing kernel" is selected, Spyder prompts for the kernel connection file details: .. image:: images/ipythonkernelconnect.png **IPython Consoles** that are attached to kernels that were created by Spyder support the following features: * Code completion * Variable explorer with GUI-based editors for arrays, lists, dictionaries, strings, etc. * Debugging with standard Python debugger (`pdb`): at each breakpoint the corresponding script is opened in the :doc:`editor` at the breakpoint line number * User Module Deleter (see :doc:`console` for more details) **IPython Consoles** attached to external kernels support a smaller feature set: * Code completion * Debugging toolbar integration for launching the debugger and sending debugging step commands to the kernel. Breakpoints must be set manually from the console command line. Related plugins: * :doc:`inspector` * :doc:`editor` * :doc:`explorer` spyder-2.3.8/doc/onlinehelp.rst0000664000000000000000000000052712566665770015216 0ustar rootrootOnline help =========== The online help plugin provides an internal web browser to explore dynamically generated Python documentation on installed module, including your own modules (this documentation is provided by a pydoc server running in background). .. image:: images/onlinehelp.png Related plugins: * :doc:`inspector` spyder-2.3.8/doc/variableexplorer.rst0000664000000000000000000000320412566665770016422 0ustar rootrootVariable 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`. .. image:: images/variableexplorer1.png 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 .. image:: images/texteditor.png .. image:: images/dicteditor.png .. image:: images/arrayeditor.png .. image:: images/variableexplorer-plot.png .. image:: images/variableexplorer-imshow.png 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/variableexplorer2.png Supported types --------------- The variable explorer can't show all types of objects. The ones currently supported are: #. `Pandas` DataFrames and TimeSeries #. `NumPy` arrays and matrices #. `PIL/Pillow` images #. `datetime` dates #. Integers #. Floats #. Complex numbers #. Lists #. Dictionaries #. Tuples #. Strings Related plugins: * :doc:`console` * :doc:`internalconsole` spyder-2.3.8/doc/projectexplorer.rst0000664000000000000000000000305312566665770016305 0ustar rootrootProject 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/ spyder-2.3.8/doc/inspector.rst0000664000000000000000000000205512566665770015065 0ustar rootrootObject 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` spyder-2.3.8/doc/console.rst0000664000000000000000000000465112566665770014525 0ustar rootrootConsole ======= The **Console** is where you may enter, interact with and visualize data, inside a command interpreter. All the commands entered in the console are executed in a separate process, thus allowing the user to interrupt any process at any time. .. image:: images/console.png Many command windows may be created in the **Console**: * Python interpreter * Running Python script * System command window (this terminal emulation window has quite limited features compared to a real terminal: it may be useful on Windows platforms where the system terminal is not much more powerful - on the contrary, on GNU/Linux, a real system terminal is opened, outside Spyder) Python-based command windows support the following features: * Code completion and calltips * Variable explorer with GUI-based editors for arrays, lists, dictionaries, strings, etc. * Debugging with standard Python debugger (`pdb`): at each breakpoint the corresponding script is opened in the :doc:`editor` at the breakpoint line number * User Module Deleter (see below) Related plugins: * :doc:`inspector` * :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).spyder-2.3.8/doc/pylint.rst0000664000000000000000000000057012566665770014376 0ustar rootrootPylint extension ================ 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). .. image:: images/pylint.png Related plugins: * :doc:`editor` spyder-2.3.8/doc/findinfiles.rst0000664000000000000000000000045412566665770015352 0ustar rootrootFind in files ============= The *Find in Files* plugin provides text search in whole directories or `mercurial` repositories (or even in PYTHONPATH) with regular expression support for maximum search customization. .. image:: images/findinfiles.png Related plugins: * :doc:`editor` spyder-2.3.8/doc/editor.rst0000664000000000000000000000230712566665770014345 0ustar rootrootEditor ====== Spyder's text editor is a multi-language editor with features such as syntax coloring, code analysis (real-time code analysis powered by `pyflakes` and advanced code analysis using `pylint`), introspection capabilities such as code completion, calltips and go-to-definition features (powered by `rope`), function/class browser, horizontal/vertical splitting features, etc. Function/class/method browser: .. image:: images/editor1.png Code analysis with `pyflakes`: .. image:: images/editor2.png Horizontal/vertical splitting feature: .. image:: images/editor3.png How to define a code cell -------------------------- A "code cell" is a concept similar to MATLAB's "cell" (except that there is no "cell mode" in Spyder), i.e. a block of lines to be executed at once in the current interpreter (Python or IPython). Every script may be divided in as many cells as needed. Cells are separated by lines starting with: * `#%%` (standard cell separator) * `# %%` (standard cell separator, when file has been edited with Eclipse) * `# ` (IPython notebook cell separator) Related plugins: * :doc:`console` * :doc:`explorer` * :doc:`findinfiles` spyder-2.3.8/doc/spyder_bbg.png0000664000000000000000000001641712626055322015141 0ustar rootrootPNG  IHDRzIDATx] PG$٭MlUv7V6Tv+ (^(#^1wD(1H< "!x"/\G?q̼y2ofzz]}_{ǚD SFB |4UUUuرǏԜlz~UC[`bu/7o`$ 4ŋJ5%+,X]p!::ZΝ; A!rssN0A޽{C744\-`-9URRF|Ȑ!!A B"Y: t~Z'rs={t /T[[k+V{_R.]1QXAٳ0;w\|rǎuG700"ؽGk|$f: U(V#G}]]=zL:U|OHHI0j޼y",}ѝ8q"Xr=>؎b~zzLKKK%}: )PQQ!yLᠠ fOOϜ+{? 0G*..XcJs:HQQQtg֬Y#wd^Ӆ3(V555{fhZZ]6RZƣ3>>> &μz.rAFx˖- `;sLΞ=ۚMN8.9CI{+tAi+|<99قMXKIMMu]ӴҥKC QX9X><8rQIKy?X8qBN$l޽{mmӧOCCVԸq蔴RSFdM&y677Aap] Zdd2?T9Ղ _HH٢"GЅC{驹͌ l70Gڴi#~DG"708v|!9 Isol(cҫMFoppܹs.t9'ReP^sGamI)++@߮Kzԩrok.3pb5f "SÇ7c-/--)u^zMX3x\c u'*V0Gھ3gڵkN˙ʩXG;v옗v%.3`f@?c1]]]`-X1 Sk&z8ETXa{0͛j4H4A? nǼ0_:uDܹ3~1b'|2ؔl 4/_'FC~~~||}vb./۷!SȊhcbdHR}S~~#rN>·3KU偡ɫ}?qo6Z;`FbJD%( 5pYmUUѡQwuo O?Xfh_{yӟ0Բb,tUێrJݵ_k*i*U9;-w=쳊ۍ6%s/d|ܹ`٣G7>#蒿m;oI&[b|7޸*&bV| coeB>ߵVM-$TLX>֭M!^hL1c߿K.df/Mز_ /(ǸH:Q}6RY0{Բb` dtGW-ƣ<(JHHP?Y4 oB軙?tEIP||Uqvv6mFۗԈw޹~}R_RRDtUa}Z.9{/i>? ZvnGpjFwRחޛ~ٮBi_qqq]W;7Aᴆ,wր.3K2iøY޽\XC'Nt?/XhIFms}MNNCaa!.jʕ+l;/7!:4 `9wftrqJLJ}}Fm1 `ȬYeFDrYۮݡG1nVWC(/JMWkԭf0!(iwGӊ6e,@w2.."NAݕ=]!n͸R*;K #7gq6 p NcNϟG޸d qw(G<^^^[nvc[E2ر -Sk p挭m[ȠLhTMP/P/Wvw'׍N6`x0(|tASLZMaaI<)GkgS o*kL24@'1Sv`~mhhԯ_?C0&1-U 3$r̸CM;0VߩSRSSS6mƘlŋͯf,wttB'-[0 5M&*;332iL;6m۲GIchHȋ,1.Kv 03!/O+A3'gr|7y{O{ ]Am L쀀{f&GL}[R^=k37`ftjS۷oرcPP\11ر{HKL| Z5Mӄe3KqP9%={6s`))9 VZ(*aa65,kJHk9H3;4{Ao2 C ̻wIoߦ&_b_yeG1UqƉ{n3Ȅꕆ,vw0;$4w\XTUU+YR ̘(mݺW۴9'tM0_v{  cf֓9GӚQFIy2Kxx8SN)te`$$$-$޽à{қo.{__P%}(//㯾JmuWXa2KDDݺ:+ٳg\\yі*++)~qBi&ZRUwcM;$'ߓ2qq]2E{ ftw0 ~!F Un˹;h@]ǣgл}$WRGԿaW]]m6o,f>kLwJؑdAkܸe7=_<-We*1cƨRK::8bq7M'OņqAKjs16w-{8Xhfee"OOiӦхi2pK+Z[__t;m.y.ZJٳG8k0s.]p!))IY;JR{$ȎɆ*FpJj_Jӹ+WtY(^*v˔p嘼W\(\tBJJ^=t'\>ULLLzĿ9Ϡguqu`¼yf} tm7=(ԫB6\a7o q0}CG >Hl;w=0Fɖ#> 699Yv=q&e| '>TBr݈Ik8p nz?J`rʴcjk C[efZ&T\\,ig„ J6lXffܘ* Μ_=]ZU/e&Hޮj9Z0 ` kZ_;v( [30n]f^F7N#` f@kB`'0Ct2. [(,,LJJ53 :L3aaa1x޼y`Sۧŋ e [_~p>}?+xvvZQFؕVq)|ݭ[)Ei6qqq}:go׮]/\|رc g 3~#x':~8W.]={-))|լӧ9200p蹐~z`|srrT׮]Ii4j*|ٳEEE6lغu+F|| USSܳg+ǎf \]]ݩS'|ڳgϠ qme#XdɇyyyۗGSNtRrömFr $GLZ,∈0N&I|EJ"Iv|BIPll#F@Byy9)2`I@qI&1e6»)˜gΜ<^\\L>mʔ)yM⫐$S\ޔ".h.̏ iS~ \09'/SЦM?^#N˗CIVUU쁍C_%IL ?33S| jxCHIk'sNP?A`bE\ZZJ~ B$5=ᐜV X&%p`zh U CXF(o>oVS;:ΝK. :t(ْzp&I%(KdrZHLLnCWſe0KGx$Ln)!8<~y]'2r,Cq'N(IK_Z4)7 qiv Q! P-moٲER6\}}=p?ź*\ŗ apK&z]Lt 541cǎ- $~.0:=|r,o9Wܪ?RnɌ俚5zh.\ )p0! Pbbb+y$A9d/•>iٲeۮ_N}oT1/=:u~н(_ f$sD1bw0[ҫ"mcEGqYf1BCЀg޳~rh-#!C(.X) msnL=]ė~q}N#[n%wKB^D-!q sq^WWG H.M \M ^L2Ĝσ]ARi6*]zm7)==/YMͱРi:@*q@dt˕,ߜ΋: ɓ?~FF"̑ %t-ݻUX%'iJ.qm')$$Dp;c;<70s@Os $RմX`,}H{v$#a&Ǣ1vXsyBf>q# Z$=3^!5ŧFcC#KIIٷo_AAL( b jV߃/Pjhlڴ)..>7JKK  9ϧNb "'D^Hl6QʷO%X+ފ% 0&ܩl0LWMHH`3ѪF(<͛1117N!̭%KLď@RVQoaeee})xq=hP5!gOŇCqF 6dgg *0UUUhM[֎?nD%KhH.++ӥM0A\ICرC fؾBa ow7&x?iENKKsC|GJKt}hC999ģ^n8PvԩS%ֈv`D*4'h̘1C.,ZW(x,ҥK 0~X:M IENDB`spyder-2.3.8/doc/debugging.rst0000664000000000000000000000226712566665770015017 0ustar rootrootDebugging ========= Debugging in Spyder is supported thanks to the following Python modules: * `pdb`: the Python debugger, which is included in Python standard library. * `winpdb`: a graphical frontend to `pdb`, which is an external package (in the :doc:`editor`, press F7 to run `winpdb` on the currently edited script). Debugging with pdb ------------------ The Python debugger is partly integrated in Spyder: * Breakpoints may be defined in the :doc:`editor`. * Simple breakpoints can be set from the Run menu, by keyboard shortcut (F12 by default), or by double-click to the left of line numbers in the :doc:`editor`. * Conditional breakpoints can also be set from the Run menu, by keyboard shortcut (Shift+F12 by default), or by Shift+double-click to the left of line numbers in the :doc:`editor`. * The current frame (debugging step) is highlighted in the :doc:`editor`. * At each breakpoint, globals may be accessed through the :doc:`variableexplorer`. For a simple, yet quite complete introduction to `pdb`, you may read this: http://pythonconquerstheuniverse.wordpress.com/category/python-debugger/ Related plugins: * :doc:`editor` * :doc:`console` spyder-2.3.8/doc/options.rst0000664000000000000000000000177312626055322014537 0ustar rootrootCommand line options ==================== 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 --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) --multithread Internal console is executed in another thread (separate from main application thread) --profile Profile mode (internal test, not related with Python profiling)spyder-2.3.8/doc/explorer.rst0000664000000000000000000000073212566665770014717 0ustar rootrootExplorer ======== 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` spyder-2.3.8/doc/installation.rst0000664000000000000000000001712512606574046015552 0ustar rootrootInstallation ============ Spyder is quite easy to install on Windows, Linux and MacOS X. Just the read the following instructions with care. Installing on Windows Vista/7/8/10 ---------------------------------- The easy way ~~~~~~~~~~~~ Spyder is already included in these *Python Scientific Distributions*: #. `Anaconda `_ #. `WinPython `_ #. `Python(x,y) `_ You can start using it immediately after installing one of them (you only need to install one!). The hard way ~~~~~~~~~~~~ If you want to install Spyder directly, you need to follow these steps: #. Install the essential requirements: * `The Python language `_ * `PyQt4 `_ #. Install optional modules: 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 `_. Updating Spyder ~~~~~~~~~~~~~~~ You can update Spyder by: * Updating Anaconda, WinPython, Python(x,y). * 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). | Installing on MacOS X ---------------------- The easy way ~~~~~~~~~~~~ Thanks to the Spyder team and `Continuum `_, you have two alternatives: #. Use the `Anaconda `_ Python distribution. #. Use our DMG installers, which can be found `here `_. .. note:: The minimal version to run our DMG's is Mavericks (10.9) since Spyder 2.3.5. Previous versions work on Lion (10.7) or higher. The hard way ~~~~~~~~~~~~ Thanks to the *MacPorts* project, Spyder can be installed using its ``port`` package manager. There are `several versions`__ available from which you can choose from. __ http://www.macports.org/ports.php?by=name&substr=spyder .. warning:: It is known that the MacPorts version of Spyder is raising this error: ``ValueError: unknown locale: UTF-8``, which doesn't let it start correctly. To fix it you will have to set these environment variables in your ``~/.profile`` (or ``~/.bashrc``) manually:: export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 | Installing on Linux ------------------- Please refer to the `Recommended modules`_ 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`` #. **Debian Unstable**: Using the package manager: ``sudo apt-get install spyder`` The Spyder's official Debian package is available `here`__ __ http://packages.debian.org/fr/sid/spyder. #. **Other Distributions** Spyder is also available in other GNU/Linux distributions, like * `Archlinux `_ * `Fedora `_ * `Gentoo `_ * `openSUSE `_ * `Mageia `_ Please refer to your distribution's documentation to learn how to install it there. | Installing or running directly from source ------------------------------------------ Requirements ~~~~~~~~~~~~ The minimal requirements to run Spyder are * `Python `_ 2.6+ * `PyQt4 `_ >= v4.6 or `PySide `_ >=1.2.0 (PyQt4 is recommended). Recommended modules ~~~~~~~~~~~~~~~~~~~ We recommend you to install these modules to get the most out of Spyder: * `IPython `_ 3.0 or less, or `qtconsole `_ 4.0 or higher -- 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, go-to-definition and calltips on the Editor. * `pyflakes `_ 0.x (x>=5) -- for real-time code analysis. * `pylint `_ -- for static code analysis. * `pep8 `_ -- for style analysis. * `numpy `_ -- for N-dimensional arrays. * `scipy `_ -- for signal and image processing. * `matplotlib `_ -- for 2D and 3D plotting. * `psutil `_ -- for memory/CPU usage in the status bar. Installation procedure ~~~~~~~~~~~~~~~~~~~~~~ #. Download and unzip the source package (spyder-*version*.zip): #. Change your current directory to the unzipped directory #. Run: * ``sudo python setup.py install``, on Linux or MacOS X, or * ``python setup.py install``, on Windows. .. warning:: 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). Run without installing ~~~~~~~~~~~~~~~~~~~~~~ You can execute Spyder without installing it first by following these steps: #. Unzip the source package #. Change current directory to the unzipped directory #. Run Spyder with the command ``python bootstrap.py`` #. (*Optional*) Build the documentation with ``python setup.py build_doc``. This is especially useful for beta-testing, troubleshooting and development of Spyder itself. | Installing the development version ---------------------------------- If you want to try the next Spyder version, you have to: #. Install `Git `_, a powerful source control management tool. #. Clone the Spyder source code repository with the command: ``git clone https://github.com/spyder-ide/spyder.git`` #. To keep your repository up-to-date, run ``git pull`` inside the cloned directory. #. (*Optional*) If you want to read the documentation, you must build it first with the command ``python setup.py build_doc`` | Help and support ---------------- Spyder websites: * For bug reports and feature requests you can go to our `website `_. * For discussions and help requests, you can suscribe to our `Google Group `_. spyder-2.3.8/doc/internalconsole.rst0000664000000000000000000000260312566665770016255 0ustar rootrootInternal Console ================ The **Internal Console** is dedicated to Spyder internal debugging or may be used as an embedded Python console in your own application. All the commands entered in the internal console are executed in the same 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 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. spyder-2.3.8/doc/lightmode.rst0000664000000000000000000000060412566665770015031 0ustar rootrootLight 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` spyder-2.3.8/doc/conf.py0000664000000000000000000001466712566665770013640 0ustar rootroot# -*- coding: utf-8 -*- # # Spyder documentation build configuration file, created by # sphinx-quickstart on Fri Jul 10 16:32:25 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path 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('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Spyder' copyright = '2009, Pierre Raybaut' # 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' # The full version, including alpha/beta/rc tags. release = '2.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'sphinxdoc' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. ## html_theme_options = {'sidebarbgcolor': '#227A2B', ## 'sidebarlinkcolor': '#98ff99'} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # 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 # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'spyder_bbg.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = '_static/favicon.ico' # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'Spyderdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Spyder.tex', 'Spyder Documentation', 'Pierre Raybaut', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True spyder-2.3.8/doc/overview.rst0000664000000000000000000000652012566665770014726 0ustar rootrootOverview ======== Spyder is a Python development environment with the following key features: Key features: * general features: * MATLAB-like PYTHONPATH management dialog box (works with all consoles) * Windows only: current user environment variables editor * direct links to documentation (Python, Matplotlib, !NumPy, !Scipy, etc.) * direct link to Python(x,y) launcher * direct links to !QtDesigner, !QtLinguist and !QtAssistant (Qt documentation) * *preferences* dialog box: * keyboard shortcuts * syntax coloring schemes (source editor, history log, object inspector) * console: background color (black/white), automatic code completion, etc. * and a lot more... * :doc:`editor`: * syntax coloring (Python, C/C++, Fortran) * *breakpoints* and *conditional breakpoints* (debugger: `pdb`) * run or debug Python scripts (see console features) * *run configuration* dialog box: * working directory * command line options * run in a new Python interpreter or in an existing Python interpreter or IPython client * Python interpreter command line options * *code outline explorer*: functions, classes, if/else/try/... statements * *powerful code introspection features* (powered by `rope`): * *code completion* * *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* * typing helpers (optional): * automatically insert closing parentheses, braces and brackets * automatically unindent after 'else', 'elif', 'finally', etc. * *to-do* lists (TODO, FIXME, XXX) * errors/warnings (real-time *code analysis* provided by `pyflakes`) * integrated *`pylint` code analysis* * 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) * open Python interpreters or basic terminal command windows * run Python scripts (see source editor features) * *variable explorer*: * *GUI-based editors* for a lot of data types (numbers, strings, lists, arrays, dictionaries, ...) * *import/export data* from/to a lot of file types (text files, !NumPy files, MATLAB files) * multiple array/list/dict editor instances at once, thus allowing to compare variable contents * data visualization * :doc:`historylog` * :doc:`inspector`: * 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) 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-2.3.8/doc/images/0000755000000000000000000000000012626531443013550 5ustar rootrootspyder-2.3.8/doc/images/dicteditor.png0000664000000000000000000002773012626055322016420 0ustar rootrootPNG  IHDRf(Bc/IDATxtTսZkRz- M0QF^Q@D`_P"0 ՠE5>}Vʋz[̭E\.w=E/!IALEJՕ;{?{3L|?k/<3=3vE$1c,_|ժUd8$sR:]T)/[ Qn- !Iw#},c,up!}Zӥ@B''?aR [!ɓIWZEYiӦ]<&MuFLҁF(v:&՛ҧey6d=nܸ;F3?u䯗~ZcN].&Ig TIh={) GϘ1cd/R^hŋ竸͡TꦶѝU;r~=·WE)yKʧTJRye7‘?S?ohφžۇxǶۆ6ڢ& Y r…7'բm_9޽kށoykUrQ/w!KǔCtTn#I?g~vE=_cÕg.8EgOZߪ/국##p_w+uj9%g61yM.y9V=V{k/SY*JΤZv#?O65z0gUI˿ugM.εΝܩs{m/]q8|8Y) N%Zl=FJ TY~L婖 1Aɽ&8zG\ݫ+ϝ:矊/:u4.H7%բFJ_ȉudJvaN|*OF8L< EJκ񎩥}Ϲf,&[?{WݜW5?3tuonJuL}]jh %ƺ7c0fXCL~ 4-2=y\HB9j~Z+>S-$I?/<{ݸ?3xGOy /|L3'iy?w+b˞=޺ib^yy+Bo<ޚFm_'*&yrpg?ocw}H^Go;>@SKF+Lڛ? @oL6Bv(u5rj[iJ;9D婖G(A#|穂g>p$?-w֕7]V@ʡ5Jf伭µyEF7oط#G>j{{c+&^{H1mGEvo*PY`L!W(kf[F=`^rQЛ‚,ǧΟe~yq( zrXR޳kf[0Oyз&+Odtt/㯖渠Z\ri>jϻ%?8t?-uS-VG }j${^gkom॔cd98c)V;//>{؄OuY[#_i#R[Z?o4K|ѵneU)7]x+v8GN/EZIW"_l oj*^1<1XV}E}T88Ic/1ۜ`/-[ړD ,S+ .m8]Shio}rzoVsyyE~ȑQ3|pZqKEuϲOE񬫑ƾ]OKs%STjٍ(@IiğpQy{z3{ng>SÉu/Ijתy/6OVXk֭N n>ť拖'LUh47xƁX'ۛwW 7KLV VWrfX/CSJ[n,?\02!F?L]X gNKd/v yB 欲xuΖE w84hZV6B?x>ųb#tqŘwJ{)~]g<ղHҿDC ͼsR7yՂ.肑SzU_ѳn`a ?͍~FT8d]d`cy^^i ԁw^3=Zw&}9|vSz;z4<>fIͧ9Vx]э^p_7х@SXXaS]e#II_lѴx?0I>>!Tn#IT&҂uψq2~knOoVټM{iwD9{KaIƋ5\OJ0Jhs3OEqII@`E}i;D,!Q6~~{%%kvɅۑN=*>~곔;^Oc{ XUYtxu0YHzQޏZT7x#]|iǦ86J' ǮDt|9mղHbС'/BQAx| ɰ,zՃ 0XSf+ppG} \pW%բmoi[xmۢ mn[~2dl(X__QDQZ,YrmP-FW=~V2=3ryoܣ++_:;$Zv#IX.gϞlٲ ɚjQ6ȩo7kHwi{kWпtw#-FA婖G Ç'ߝ TjWt _>Ϟg?}1y]ʻ!$ Eҿ3EEENa#t@/}i#I ܓ~>}R> ҇@ }>H@ } G(qĉ3_|l۶!}Toذwi>8 :Ԓ}ue x?~\큁aΝWWgK@҇AH@ A韐6 }bs }GZ" tLOƊP(T1W*WҬ\ j/ tT@~0OYa\ש+SU_t).˝&C|O!J-z1Oe|gi\,ۈ'~0{h#<؀җMov*}0Jtqy'7V?d?&J~ ۷϶> W(y/Xdy‡58Eu7, nUקMO;J/WF́)}rjaޯd{#O[+&6p_ķo]>#:q>m{`ځN@iZlOh,g"Ğ4EJ'Z &2k\=r}Ku>V~eڟts3B -+Nk HpTe-=o#X]-pݙ`)Ol4%.[x'ֿ/Og7Yn#fLdEB+vgODN~pFHӅ_Y/Cb~YAA#}⡇| {߽i_nݺپ}xbTHDrF_|z3;vAe|}566F.d?`{&D_3l:vqW־#{ !D?ӌށ"!}H )핾-}#>; CHHɮ!}CH HGž> }H ҇#exd@>nf֗BeWq !4 TQ>cpkprرkA6ekƎiL)#x"4?p;3x֘HO$RԵѬo%[,\v'ݻJ?[w b;"c̆UUѰ-E~R]:]PQUxe%Wi>5o|EMYos_ [TtIF]iuOնI%*"[lł?j鷴@,vP>rYmE^ -lXxktOJ?uIqҞ~݋wRg%(H_hMAr֦M~fQQѶhw9qX0C[Qk ~7$o:>l#3w]+}I Z J- Ra/?nfS~*Sa%E džl3~vLJ?CВqfFQ+}vS7!žջSsokk裏O>OO<N/2%V֖P;p^Ŋbʊ>JTS }BF[J͗\4¢XR!aKX/\+,;sg)XQxD=N݄ŞBng#_閯ӑI/?cﳽwSlAq&!}M*`:MEuO-/OrIH+}Y 3J5\3{ƺxXEމLlodfr{+ },_ L{Y-}U7Bu_[:4$Ke$hM:&3{;!bh@.+ ?4 IIkȦAMxOE;beRVĒl5@ 7rN(J vpG5Ž g=jatUUFl\= ;$&{2ּv$lEFݫ` H3)ҍy;U%oB̷(zԲ*ԠQX5!9YYR&%Փ]Co"/A҇rHG!}CH> }H ҇#A>!}$H>H ҇{S`XGJ>C҇ }H@>HG!}#!A> }H@HH>CHH>CHH.eP&E!q&bN@HfؚJ>.<ҏWpuUdtU5^rg#ӕjiq4yxt8\]f}M.yb2KH_#}pY,Vfixi!f?95W åOHjSLLzjjIMNgTa$ŝV[mޘdYR"R]L>w2єkH#Uq摾HM "^Ed(FSOuE~\'*OК|D(Y o 8; ]WFLmZ8_>B+!]k7`H+*} '&"kNiť[Li$/9Eexy{mw[ضeJl-}UZ7^e;WH7W Jso2VRpl6wϺlǔq;4H-gfT%w]lS7!]ȍ^bJr@- [e5Wae~.J?jK^)/{0 h EXKt#G+! kA䩓$d^QxD=N݄bnT̎ ?ɭ엾fye9KEkޯ -{3V>;z'J:Ngݤ v]ӔI?QD]'}Ho5NH#A|&m S5wkRaM흨_OI_n+wgKVruUbg{jkylߦJ_ҡ!H_-#GkB4 5/SO CS\*+h.J_KS!uekMBH@ 7rN(J vpG5Ž GʎFWUUJ{o$6[̥ؓCB-bMNxeGKڎhg D|*7![l\W\2ª /grVrIIHԥ_L }H H@>HG!}CH> }H ҇ A> }H@HH>CHH.;J`>V>!}$H>C҇ }H@>H ҇CGB!}HGB!}HGBq7B6֗or+rC!1eplWmJ*aV\(~cر5~}4E]tBJs"v2\ˈĻqZ ԭW.鋭1O18WK.;TSwz9zF#Fڈv-Dz)Sԫph\k=%};Y Heļ$5"ɑ3i^p9~J?+zc)/]~*5{r[Ki-+X^B#BwVExO,}J?xaiwHeHT aIo^KH[Uu-=4[=m"ES~Z;uVi|OOzxC}/e&%|(Hgڒ{Q'."pn{@2Aymeb흨_OfJ_?z*:  (}ymMXҗ7@B]-}WT~NSȹ"F">D-+G6ݏV^k[ߵ^mևӲ#=Pó4= Rt {z kJ_NBuۉps%PU#nF"־C$ٔ\PfJ;9HH㹚:A 3(5#)GR>B#lŘ5BP/grV,9ObM4<1 KiudBo3_,#edB҇a#A>!}$H>C҇ }H@PH ҇C>) VX#aHG!}CH> }H ҇#A> }H@HH> }$$H҇!}$$H҇!}$~C,dkyM켲MNUGtXKqRS\ݦ2_߂{srر{G!5.~cر5~}4E]x40\]]UqW{wW4bb1DxV(\1XG gɷwobє Pj3)9kF(b2Ǎ$X%+hMw~StyTmgz.#6;OH Um7ctyr@7I85jRu2M8KAzO` j^n*Pz|vr j69>c滃[p8u|ClzM "^Ed(FSOuE~\* "sym%l/lq*RKv-:̗Z&lUrG~җ볨wyφ{NDa!L,Uh0wq&|;' InBZǒuv,#z kv?HaR\;;g]b~GĆMGفmdNkOЭW.c鋭1%18W fawµ᩾6sCF_ؒY2E NHbNW>WؒuF{hAƩOU3CRv@֦d_ٯl'UM٦IJ_ku;R^t(T;H_Nd4f;yDJڃo.K߳(}a,mΘ|% ػ ,n!8Ɣ wrm42)fo[' g}w{ֲ>4JTt\L8KJg~܏Fz_hoJFsA دNJ߸5$x{'j2q7!m?J_*A7;-җ/HL>]o4Լ!ߛ z7!}^MGIJ?P;U=bhSȦWGdBS<}5;5E QfJ!I*靨vZ-nxL0i^YajZ_ ,HG!}CH> }H ҇#ANT`#& }@҇H> }H@>!}C҇H_ }C@҇olb Ɇqld:b4i[pS]ʺr\+:Wb SF+4K7.楥楺tfB>AץK:ʒPe+uq1@b{jd7D);z%8E0'oZ*%D0Zڕ~+ޕ>y\Rt~[,PVSj9,}{55Wq[Wwmx4UL;eRIwG!yQv`S?gw9]^9 }B?[Y{ls]c}F[+zc)/]~*ܥl |Wl- \$irﶫ+MܱQ}O*sbO_P&Koy=%6of ?ac|O,[ěk~->(}{ܛnl }$rM#L3/)*7jCMXyL9Sw۠an=5!}߰a; >!}^ >A_> H@> }Hd1ͽHY"Cy:dʁ,[ c Hӂ(}y'ЭS` +>J?ٳ $z#e@҇ }H@>HGoii9*HGl=<HN'>@>@ }>H@ }!}@̢DC1"pH`@jضmҥKo)Eh"bD>tŋ׭[wdz/Q\N<Ç.("!}r]SNdSAQt:ө/̲YMQ\!}r]܏_g<(N'}:J%Oeť /wY)}_qNQ\>@+O>Gl"ޡ }VO[6#(.8pԩSY8EDqA?5"/ uƭ{)" h?>!7̣'_XL_H}oIT7l& C䡈(.H~۟ohڏ@cؚFU}4^Q.ϔOw>Sх9ǝN EDqA>Y^יoڌ%W] /6YV&jfYz+8u*> ]?Vˉ}|r>4fC{g=krK-{gdǣX| XY1AjrŒ)- (" hF>|㾶/_̂Ђg(?tڵ B":Z.:+b1~(7`Ӊ(.H7/Kdљ2I HI_) e4_mb(̔ϧ㯯i}b_~SхmjÛ ͊Mȱ78ش/*CQ\>@+W}QŜǭQ5 9RVP\饗;xxF0{Rzcg|o۶uOvAQ\>@+U՗]/gUy:β)" h&]PD.~]]Ç,pjO:pd؏^ o6$Y$Ƿzk{vAQ\Y+++WZE~̾Dqz:F|uo۶-°<(E~nӴv(F҇@ }>H KQd+d=5n 2IENDB`spyder-2.3.8/doc/images/lightmode.png0000664000000000000000000005052412626055322016237 0ustar rootrootPNG  IHDRFRQIDATx|睯ܽݽx߻w&&{8v\J{lcl)SL1SDT!@ $$ j EpW3sFs|ќw3sޙ9 6>3R2q¢/g]B!G*nU}c®nBg4SRrq@ PbÊgŶG;HSьy鳕' *_B!XU*{L4deڄ z O$'%$۹3B!G*n*sN  URR'6m=![ŰY+u7:&%55w˗EB!'NAӼLW"ۊs}iB!'ʚ|RR+ws׈J]K\ݵRK;.mXy6{2L-?#iC>sBwޕbtlxBH"KM)yAY)#%L%[ݺ}9vge-gѢE˗/α,O!0"¼ro#SɴD Z=gO$s?/_0j$zJHI)/Sɴjܾ OMMuŋo۶=%(EG˚u;Y ozavVxT߲n>AK)Կ|bٲ#cSRFJJyʣ}wt{hC?G\wAdRϓ/[kef$2c͝KIy-!QVSIaSmW^WVf9絡Ͷ5^f&d}DSGvvߑ#E%׆1wT2b^l"$lw}JG,ѭWJT2m[mŹ ݟ! y ܝ{+,,ܱcGdddvff˲e'/MKS'fg?r,lj{֍6 {gT'7&ٷɧL[зG{bwlzزQʊUDC c{?+>NlY[_});CN/^b܎8󥿾eymHd%, HٸO<.Ӛ x +HO,xpdZs%NkUA1h^{}XԯWhoo oooow2L~Mb[qn}fDE'EF'EE'Jۿŋg͚u04\AAΝ;}tr]1jG7I1Ib9֫j}V5]UXc'xT!OlTZݰdђԢ++7mW^~lvz<{=>쩏AކgL%*E8IIħ}+Ҏ;4?RNn~'֬yfy*++{o)'}O<ꛫۗyh`? )dx9{Mb /^ro\급Dž6aEojjٚ5eZ yg_)6g2q-uH^~*& $lg\"xZpv/_AiSg؍!20`0)P39~426,i8i}D䗜w@dZs%ʯ2<*S_ony￧+Ægo\|ÑMD)morjցC 2$cFBb7i$i]6~;ʢ/[VU}sr gJ2wE' 0LWm$W԰4?"ïdߒ;C&$}Ua&4j G2 )]#eTĎꔲfxJ-iѱSO?' h^"`ߗzVx|?kcԘIRL_'?>"v߁9 zLTJyV_IĠG|STnwzD^M))#%L%ӊXu8iǓR9^t8;+;ëVR̙L}'$&\KRqB|N#ߟ;M^S%ʯ2<*Iof{PѫW X+#}kj8GgȗGѼKKˊ/^;k֬;w ͫ$ߝR#G%Jԑ@|6,Gm5dDUzI2e3uVH} 5[f1R`R&--S1-*0Ⴎ GڧN#J8j3LXҿ^ڲ*֤eaFhXZ@]9N!DED~dL(55DrC%UA)h4ĩzʿ*z˿RRM"6G\ь] Js;v쫯Zhі-[,YWWW[}a|9gApTu#C6n׼Z*D{6##_H%R~BUlά%Rs/iW+ZkD#=@ j"Wd8fOU/:)y@н"0+Jym~ͼ\ɵaYp%l5&QkҲokC*BH`#s$4,Qbe!zJ$gUv+oF^zMG|[mR^i5Kʣ8E@zt)*./((.//o(_?rtȱr~OY,XәRLݷ 9{6^r43G^ WJz]M$2rO+";VլV j5~А H;|\{OBn^d6{b"xgxx/,3KTy3/ΨXL'Ϝ5_fռ<õ!T/h#֗l}RLOD~;6oNxzJߺ2Vb$iS)VўjaϬ8YyTVU*w߆ʲJUL}* KK+eȡ?%XnN =<.6UurJG?R*Q%jd^ V/-R#QQ3$Z Z6+,,WQ&1Dj,.7xIԿYT؇!QyImLL>-TG(#>d7tuȿ2LC߮}\R=+Z+o&Htާ?}Zr3gjԨxdSgV%O˳5*)_woEz~ O2On*޲)|~{Ri7mޖ*T2m*qN $JN"%$2m5%Ź ?_{\u{;/hejUk ik?rͷ-[6e'PZ3yBH-iGlQFiRI.A<ߢҽ/(/*e*XQ`߂uu/]RweID~Ul}I wϚsZ/-[NF#Y`_-.,T3p;H i7Y6W.VFQarՀT2m+,/W`ãJ/8 BHSG}wF"BHS[;7xȐBim+VGo~-$BHSD<+b_B!B!ؗB!ؗBBB%B%B:} sNo#S4SNe%uD'b\$M.!`_s|Cp{xYow !?ҏ,\6fJo3k喘L_WzCDtٳ5n"kw5!G.,.̇|˫ev,6c_Q茧Gz\#ԩӧb_BiH+2Ԁ}[ؾ%x~0w<'/_:V|sG7ZFؗҞ2fJH dYݙ?ڷs~#x{9|87Hq} LEz;q.`Ki[---ݷoߞ={ٳ/!fρ踄]G޻kWTTȈ;#233 {};kCc׸fG1%2~]-Q{}^>0ܜVd `~*}ׯ[|vWcS~Qo5}i['9}3蹜/3>jXFcw%Kz]z9ɈO:ľ˷.TΫ8_]xqcRnEJ^ՑªSB}o-Z{<;oȐ!uuu/]xVR[s\jN<~y~HY#z1½e*J:| 8Hľ*9z%3hsu<󮹣_Ȁ+#婢)%KJ{ס}[YYľKgIIˏMޓw8/> .希w0&= m)8<ۺ"MpϱZ} 0رcG22ӏdIMMOIIKLN-+x}l3?('l:ו*Wk.?;1k`kcz}uw|YKE_tȀ#ĖTؗ^{nׯC.ܼ%v['U{yW>2壖oDDԒ[RzL{D~$5-=%5-9%511СcYϟ>}fxN;SN]V~Rwj+/\IɈ4{ZєiV6i=݈}?R0Ken^:!qHyhK}vh߯Jܜ¢Riɴ'O:}ٳgk\pKRlMA!)6*uJzLڄѮ-lߵkWW_3*Ւ>]Uuj>١Kpi׹]UoS%WӮ]3܁о)Ծ,m_T^v,e<} !FEO3ycb' 㧏Y<5iNo~~Wxx'O<]ݠZQKLa6s՚gfYW{\Q}W^s|mnn^~~!y|Inn~VSWX}\"O|{U^M]]ue=J}uhQIIP} mn~1 &+#婢ؗngϞO\Sd~lGܙZiΈJceWؚus+_x_V^>[\&}`}ϝԜֽ<;'"#5YVVl2o+ZΞ=WW޾ꭍ}'?Q}z|HZXX,k*}Օuq;;;TֳCUm֛pp7r}1 &]0킩 ȿ.7& !S:nrʩSNᆭGY/\p%1ŕWdrь߹:25]_µfѹÓ]ow-kuwǵ\Z-`S:E-+;i}5`0+븤|^!^uf_w]wlAb9]3+MW{CtEYly4L} %{:juD\ԁF$!1N ="뿅w^3Wnqw'G}p}{_[`6B5B:}333G1`@J~Ǿj 6a/ !a[wް}-Wl ]1tن+6ʿkDInߵq[1Ý~HtxȨ];b#$}[M1[6&b_K!(S >[G'NtS*˴RC{]9BH$|⺰2WU˴R&-?Od+BH@^mf?dQ!Bx3`_ }/?\pݗ}33]={KJ]*rNXVݞ:U]cHgpa۴mY/[433lؖУ|4dm$'n_\Uk!j>PK v6y/+&yU'eyL/$b`:P_gSI:m9=`V.cdMЯRm^!i1ġ}}!e2f۴r5۞}Ɛ~ès3W޴j ɺ8D_>gO~Vj1LnZ-s ^-a>:r=P夀zZbd1Z|%"ÖM9f썞i[.]&C,Ht^ =CݲݦWmkUaYfACC}ʔyORR{HUF2,+\~8$./'{m@t( _y;?jeHO1Pj k^?{*N-J=Ak9K۽}-7/e)-_H?Nfi.ZQk%O%ex:(5~1̳.sb~!C'&C, ˝cMn~{6z6ҟl:}mͶU46m_=)=6Ax5/٨P[U[ Ӿ*l4j\@9@2c}Qo_,YnwWyw:Zcfs~34S[Kf9KfKUij=5owW 0hڲcn3xzw[ C-&>c>*ZGOM[Tfpq~nͶg_oҩɾڑB' d.iFpelfeq7a׮Y~R6oy-b_V%J6obYO/䛀-wF:Վٍϒ)9ثNdW6Cd6;DoCOM4K/hc4~Ղ˱Yɲ5gGF6jFw}[ө[Z?[WQr1m֪t_,Ynwd+ټէ5|X3;|broq~6~ӿi=`!fgpRǐ=iF`>05Սcz}:Yv?f{:ny\RFX-ns7:ohUw@`YR+PvaLSUL]Ri{OմYOGaYݽ=6-7vKۅ}!$W[=]܍=͒yr}I 5^[lyT4󣍟gd[Rw-+\m;] <-yQr,?w]sVbwJvl6f_XD6|[YvOľַ{WHk !Nv~ɧ W׼nj@MBZr1jp4ïĵ6)/Mr nïض/žrF:Ë/|63;jյbX!k iOTjޮl 8_-m=\4WR q^϶b_˾Z ֚mټP+n1ۊ}a1mvZ}}oH z 4}-_lX ߨk+ć~-[vټPAmaCS?b]q<{`AþԚԾ%Z? = }KXΧokɓl_dy²NO;n }}Y| 4Kp!ۼ]qYΒ|Lb_ }[}333{TOʠkD6vI=#FҥKuk-VYF Ҿò6j߲2y's,zd~'O\RΟoM#6jf@J+{zZ}կ|FJLsCٵ_}ڲeHiɀa]O/$U!PhdX?aLj-Es.VTa-R ԣھGվV ?|F^SJ?jQvQ'rMOUp_}?ګȞy)OU_ŲNi-4/zּj 6+Ću>ҙ}u.WR߼Wo>5,jBiiJЄӹ}-g޲NJmо zk_iq&oO*V&Py }+ξJc׷¥?:yz%uW\{9:NX֠jhm{=yE<vX_FݽU%unR5T[6ic_4,ŠUiۡʓD=}ίo طɭNn;? }/66]>Zse_m¾2d _vWu@Kط~m ۔#iԯ QTSddVoIGV?D<mOF#1wzhA7@MimJy!M:Gԣ ;dɟ:i4}]M˂.ƲWkmھfOV㪧dgߙAwTWJ]Yj[[yj}<m= Dlȡ}_ОRWCU;ӵvм,ZS\s)e&]iN~ }UߺZӰ۾]%uܥlپ#)&}nzU'h 6u_TkMɖYv;z4yV!oK̳L}Sn'wA;3-d@,>7Pw]Y=pWOˎ,tz7m. 3+O~.Y㪻۩]W-dLtM6E~jU}G \~|kط=/M`_뿥-b_/۾eAA/ži.AAv[о̙g:i233S[2oZ詋@K̽ZhQgn _ }[[/ 7ER_S`o}z<h Ѣkƾط0(jT,., PA=!Zy; }[oW{;}=h `6àeԀndo_sk7[vQ`Eymgu]F~ _}]=hQ'w]`6}_oSg&^fzi_؇}o+Kr/4 }I }/}/žb_ }Y`_ }/`_ `_ }//`_ }/`_ }/`_ }/žb_ }/}/žb_ }`_}/žb_ }/`_}/ }/`_ `_ }//`_ }//`_ }/`_ }/`_b_ }/`_}/žb_ }`_}/ž`_}/e`_ }/`_ }/`_ }/`_b_ }/}/žb_ }/`_}/ž`_}/ }/`_}/`_ }//@/!B`B!ğ`_B!B!ؗB!ؗBx66B!eߋB! ؗBؗB ]_ݶ=oYƫW2,,!]F.OA]b_B }M>'U8'?}YK{zWb4"-žؗ}mK7 /ؿ|[Sa7pyiMy;ە}N*طI7ڂߎؗtnL짪=pe]Ovuum{Ƶ)oO2&OGS벯vDoM ؟|зicA@dg} >}7G/oNOmS9r5NrwC; |(¾o[]s_3 yz/wϢghdVg_{@?hoS=F~͟Kl#6}n)]LK=fоbƝ!~ -ОZug[EGu6`_brɷ5lfL+lo)]LKw6xL]7r.C9}+O_?z|gb娻[}۷.Rb_oȍ}Ir' 7=[G̼k]*sFǼw}[[YDzo뿾ئ-}I.}?=7\+WC&9L|9Nn޴j_O? }ib_b_ߺGn?{~ykOϺG{ݥAOzamӤ*g4ýWvW6II~DNP}7CK~ߏ{o]ofz.k};ȤǮ_}ﴸ})a_} !$`ڿ龜AB!8(t17[RzBioJj!ҖH!%ϘuϴЬdatBϑžB/žؗ}[}mǾB/žؗB/žؗ}/žboG3{rKLb žؗBs]X\nq{ʫeZ{c_} !h:iD]"J b_B¾cftԀ}l_GǾ!!/S6OU{ʚ~>d{sv?I7:}tpWm6}-W]򴱯_kpX 2aeX KNB:}nè vTΡ~6O78Wmu1ط쫚hWR7>#-ϦLD/dYdDp†de#: hzGo[o߯Koxq8v[cvjÿիW+J Mh_ žؗ?q]X\nq{ʫeZ!-)=b_KigH?p}ؘ+ͬ[bSl4}SRs`_} !ϾM%Җu_ھ} !}/ž}/ž}/%}/%`_}/!b_}/!b_} !ھrdNJMJNNBH7>>h=ok쨘b?0lӮMjݻ/ž`^gӉ#? i{s}ܾAzt^i~ۺ5`)ٷdS2FϙiS2ܹKpp#!3f)"L?6eKdYR^Q%lf;_tֳĒy9_N*F |իWEGiEm޸l6۷~Dha"67) :1W\ײ^ݻVDS;SKdYR6m ȣTY2|pRIHGz)___=u^<+W222hb_fzGQk6U2͉}#rU-Km$6/'V8^x%%OBڷ}kqdJh55+zGUo:O+nIOgﶯ̳~sھMa_L4켵Wvb_TSnUQY5Z:-SIgY%6%ZogruVw M77t8۾zתrپ5W9>mTzlᅔdZC RHQ+ϊ飣)huu/hX:s괟%Kؔt> i=[SUoYYt_|Y4}[}gol" NkB@VCqiHòϒu\CI'!վaopf/9'}jf1&!f}`ߦjim5uMtTm_eV}!!Ү.Vu+S~.Ì4/vWҧ]k%5MIW'wSªQ eڬIƏ:6!/=#t/pŋ[/Mb_} !iG@%7Lnւ}/%@7B/žB}%b_Bm҂}/%`oJj}/!6}#DYKۗm_>BžB/ž6eE6;:s[G~ӏ2Rs"H~vGtsX4w7]:'3yr!Ͼ%Urɾ%:婐Uj/d&o]M.;þb߆CdZi1|ȓ1Jr@\D 3}䖛C-d@͛U&6l2)Yʬ7~eR/l#Ʋb_J}d޷2,ooZ>w#Hr`RG1R|꣺'kՌٷZ:f $O}Oܰ9dXd0oRZa!'oٵԲQ_>*5[QF诗.?t0SvFDM:}P};tWlt'r7Hɣe)Z̘͆jGD6Zi"NՒ|!|UVSF1 4ϕ~]K-6A-h߰N\lx(eInط#WꨡDk\p#szeR9-]êӶH}Uܰ9d[{꼨T$'iX}Iֲݩ5Yv)7Ϥ[о_Sg-s@׭ ط#Wډbk9Ygp}U{dǙFo8U4˦ar! ]H46xKY [[=bOǎ:]Yq2tUk/Uĵrr]$nguߙ.5ʦOy~|D[l=jЏo$&V~HY]W}ϳ]W2_i{+׫w,;Mی}tJxM[? <ᇽfo[W>St,ɾMی}tfgn޼KjW^jͪ5׮Z&8xKΙ/v0žm]ux`s6cS'!ǾZ J KN(,(-/)/_Amzr}2ifl$7w,imS`_}o9lٱM/ Mی:m[8Iž@,;9Mیeؗ`_}/BHdzb_KaߒSZ/žMIт}/%`odTh6K[_@`_ 4}33;=c?nW^}7c_7==N]zK,rʕ= /}bߌ;=|=^ϼ2}{]s'*O֧Tщi?K 0灼eZJ%mش9:.|/@`ۻd.RI*w05.no-yN}O:?}cqog򱔣Gswؼe۬Ș)Y:ƾ~}]ӳ3n⚵! ӟ_zn=n}ƾ~mwۧ㏇ >|؈aÆ>R" z[n]۷?׾+Vwx‚%D_6Q/`_ }/`_ }/`_}b_}b_wC ]N̚1Ǭ~uaKDžw]%Ւy++W<~LʭHɫ:RXUTSzjUh,׾KgIIˏMޓw8/> .希w0&= þw]}ٳ5k.\p%)& ۾nM}ug=\QQq ^tm };SS+ӣ]bߪ/RW'DǾ~}w2%b?YF`8F`Kafaaasf`"0]U]Uտߧ?PS|S/ ¥NAN͇ b8A@`AAAPxJ׮4 9Z-A4E6f= mA&.(& Am?U0Qm\fvEe=DE]_MVҳgW3$6g6ғ^3  ]ĦoԄwI\pSP(623ATrs?09,p kfBA6iĖi&O1Vfˈؗy92ܢ8@mRԹ٦jQ<$˚m]Bq  )ۜGlo͉ 5+7i$ k  m.T%z(&o׸FKm_՚6p͕/l˩$ (  6   (TޤAmAPH Pj$APAPZjՄ \#FXx1&A` .\dmذx6pPaMMM[Xm ֬YC -DZ WW|'G0m۶v'Iwn"lT]]M]}^N^S_})'|f=deٶ}v+]5'A`%rcϞ={ƍ>/aM]اƿW}oXKPjwol7G_ݱxDɟ/޸(Xq%,% LOX\VxZy_#gV.E~UjA`#rWTTTTVVBF,9d[;7}&Ww1{qVg{s;wz_XUc;o}OYqsf(#o9zc~1XEX=E޹%VzW>+[\RW_!跚wͱ#t۞u+%eM{m޽deM4߸Yn 2~d lx;P;w\Gq]?'Je4YŏYDNxt}v?(Ngo;7"_+Ec/t7V>;to+(\'oF. cADMYxUԌ2ۖU+Nճrؓ{*~u]?,WVm|Wpz My@=ˤeؤq:tɰ2K{{#GMG=w.ζUt{#P3ju&3{o*w =xg#5/s~ҳlv mPAm̙SL0a&TZZoN6;.SZfcHH8brQ*#;xjjƑ⎗,"BBnz': _)􍭱 gŋ~`&f~U?4 5~ܿvCɍ?}\_we |'ݓ4"<vE`qF㕛8E"aK>rsH5+ vw! F}̏d89a.ψOKΉ/$i  Ks *tՕ?~رƿDfo66Ia~'s hKmf,TTTl38RI Bv9ŮuO;_z-;$:|3Mv,R3W`x.}Wnw}n]$?utTljpٱmRRr۷ rxvĩS|tܹ::(IVVm|S4ø9)A`'M4ɀQO>xf4HWVVXB&G!BqN[/TmtHJqF}dfI}9@_r$<*Kh38Jߖ>jke6۸VJIŶ{m4l?o?8r͢>>=tM2]a?QOb۔x>|G?8AlgΜ=[4jnL[Òȭf'mI-]hRZr%7;wliiٽ{c-z_՗hDliw ;lKd9@`Ɂ×@ޒI.(qeug;j7wFuڳ'ٶX]Wӟ'4Nx}|;}_w|>|4ػT'RV)^'҉6l`r3fu'Nl?FtćN&q 6lv:ouk#NUH/2לI/1&sй>OΠ5 I9 -ŋZ~ʔ)'w 9@r`!s4V Ym\;囮xuS^u˯^+ui+_{ׯ'ͧzrmؒauûM|fmuMdJ`3+5/>z_kюJ#-#&[bg7 jmmP!e)9dr>_-Զ./lpoNlӺeW4hĈ(Ŷűz1O<5u!23{q}9s' =S7pǫŌbI 96XsĞM~ɶgyfׯߺu+uf©**++e5Rm F)).4|DeG&l[rm5V:dOo{.Ȍ[*Mcz.JgSO=E*r-(l;vΝ;m?^$iHJ Z[ۼdK'^|6o3gV}d|?SL)[V\Gbqӷn۷f'|yĈzs2f/qUurkKmIF~^P l NRO ۠`TCdɓٵcL^qUO=Ok9Yid? 4;9hJl(+  m555ӦߵiƏ/{≗,Lb MMAtC̄6lKfٙ `TM8^%>#(4AcہLl#Xg  ⍯Pf]mǓ|oh-O?ʄ u2Hψw`!d,ahCb7oWt[PQ(mTafZKRNr4IĔ-m&ҸiCvlr}9۠| Xnxc).]lS_VUUa?9b d9% WvIOp\6f)qslR3:sp/Ʌsl%!wf7m ,̃wމW3TIOccDllzk>}2cJph6 r6-hl 9Z|9v"iHJ3m6q[d@ c(m!n  m 7  m6l*kolB6!n A~gۄZK2r.-o`moocsBCܖmf R~{om`\oc  $n3E~qOa`ؖ]Di&c).l G"l8a.ֶZso{&ok-x$PxkY0ٖ _v'fy=7fJ˃ҿM&[9 m~ۼok vK*ȖGwoq[&qf#7;_OޏMrs̏Tm>ۂs -۲)n[zIm-O.zXcMQsm6z&>'A~`|π""备h-6nzMh  qZ}lMJlEG;V6 "^Hnz!xABm`[Wۙ#- 6wlB+M BAb[?$>{ dߚa.˱}sRmۜ Cm1nU}IDJ;q 鯹>ud0@#mP.%!nmlB6!nߖMA^M4xwsr&ƿMHّzm Aܖϼo6ȯlSnɾe4ۡr&x?6'q[|%۬wo]۠,& D'$ҿM:?6yzag*n˃H_fK@`ؖ ۨa$Cs\e[bk؄9RQ noܖ6*Z bD. =qV%_ӫIs6ٱe$4>_Zr+rȵ#W\ǎXpЌ+S6nmJ?6~ 6&Wq["YY~m6mJ[0KR$: 'WpJזKFt621P-wmR{9OX_Ue$6_m^evaۉlr$$ J䪑kG \qyoKmpn> FncPD$UmAc$ V &aps qoo BAA|mAB%A!n U^D l{R|Ō܁=1jԨJU հ"iHbm|8@rZ3Fv &~% = mҏMnӼ^cj`$c E(w6*=Q=0n3$ ٳ-m6l3y6U%N6ɳd]lsjlV q[Dey69ѾҴIr_jR%67ms@T\B@[?6mҕmo*[6O[*:c mn Gui_T 9< ܩ16Yb:v~lKk&&6yAP\HBfl pMo B¼o o܆~ p ! yW<@A6o(n[^'v٤̙3+++Q q66vJu{L8sʶ"ZԩSTqRĕN1NHq6K&׌e;*GGňˇ:沬 >Aܖa cQxl 6{Qsե[AFڈr׎Zj3>clc~ާOYf[n6<BY'l-cc ;&K̓3au+&W&"^ 3ˏ=T׎^ߙܱ lfi3l^o>|i]o;e뿨~ȏDoʛ*d*=ϑ/D9J S0;*Ɖ)z3)Λ,?/Hޢv$m\z--<'cJY٥vq[^6ӿ͐ʿ179Wr |0lذRL2rHq[߷U_ٶۮm4C }ۏ;ABǏO8qԩ3g|ŝ$Fܖ$3g4?О;n=g3_aƀw7^K"6{g;"6mٱ rĉZDҐ( B$ H6Nܰ[0 A @RB! mPzUF]b /onn&  q[AA-_\(+F˸ǵx \J D{Sο6Eh_Wz$;yd>f*hwѝg,-*bWu]T5?'Z]j9ETibHQ^ *dŀ[]?0zZ5!1?Vpv6zs%n 'p6:ڠGJ8Bg!YSG-α/*J525JH" tfۂ' #BrE^R=>H}Li *kN$ _m%Ҍ+<9BaK˥'0DŽY;Otx6BEO^[7u-\\sïq[6ܠ7S /AbxUmv0 Gk3{V%rdƨ%f[؆͖mL-|[m=ζn$Kpn>}|C!nȵY|9v"iHJ16mK.E/ A؆ 5/ Jo BtG7 m@)xπmߖį-/HD5l*# ׿m`AlV|l;(۴`n ki5-`V#( mae)co.V2)d8Vz'Yۂy&+FŚ{6͢4#Lta36Mm*bVd $A֏:@q|dqvq[<΍ْ'Vm,iw͞mXؿMڔGʭ] !ne[bsdRsmoi!mZC6jn?& ٲs6D7 kN5Bņ m w`{h8ccۓsaK͖mo nK"fɦvȍ}1l &k{6 Ȗmo &7 m@ⶰ mABmAB TUU-\nɒ%$D[bECCݻq )/ڵkڌd\'"rN>zꖖk6mQMMͮ]N5HX.Lk5qw&DU&Aԅ GCBGmBHHn__>}׸ [nmOV! $=n u A >ⶰ}býf칳GNKƳ-c:U\]]tUb5o* uq^?6E5pl@[A r]ߙm+?Rt="crȅĉ lCtD;,~򋏜^3?Z9zʔ)eeeW&  q[`6 ma 6 ma -oZϼϫQ}Meao< ! 6woTy/$(a>NYipfcomgݨ+O%XɈ\ *_! ݏE <873a>K6pDrJɞo㟐,urZ,%ͷ!o_8vSrxӽ>Cÿ-ięC{RcAпGmAQi6zZzb!UIZiw]8SJ۰<ضMmMco&=ozyƷqs4Me+wn@y&Oy<4UӲMۦ i++P)>f6mlp3hIHa[nOlD;e36;ގα=HTۂ7[6@6oTcyƎ-$j"oI1[ +>zH]`/)b1yQE?vc۸4ȿ[V2m74omR$e2B>RYn6EEEa蒹V.!,,Gh6)amಲevtcm¦}Eyߦ=3dffد#7K ߰YRJoCVʹZi ߌIÉZ>ۤcsjPܿ l gq[1Ŏ|%C5㏈̡--?6u H%T2˲yi,o^6V?qmޠMrq]36i@~ll2'YyoFKMIu^M8PUmޜ| 8ϛޏ;E.}N6m>b[sN{p^|}㐧mѱc95CVXy=o1 xzٖ4ùS=D}ܶM86yo8Рq[+6Ry)0wc& 8po yΛmbct~l,m\4mNP¿)l+ךmbtw#n A 0\07\$}a q[ݿ-O*nu\ B` q[¿ q[6 ma 6 ma -mn9۷Dǎ^@moz T,N }P cؘ+ "9mM6} {ɯ<&v HfnظQRjܽ\T!n \o8bO(aKU.k4qEc*r6{_͑o۲c #om8,-6mmRw7a0c90C6|ơMEN q[ߦzN[7296Im9^U+7 62q}eEAܖ3Kr&st;-wpc+oS> 0-2o<qtf6IM*ƌ߁96A.oKY%[:mg#Uj56'?lkmm5aMBܖ׿ ^6\\5\$}ad[cB&Ҍ_#%m_6xziXiTiȟlfV#G*d1&VK6>M6X$#Y ٜے6i*1wS~viO2}EʤQ9՚<{I\pSP(623ATrs?:53K6 یo89$#!VTps^FV$\jKMCM۪ES-lOlm"4Pf[ћrB,#c_pp6I-NmMFmKUh$Oher*>3ƘA#HVkyMjd;ioJGoNO&ls'38XMvţf6),"3ɼ)cژ'Vv~5yd *E,kIw l ߲V;X nLgLK9f&kf$L%לڗ6g-J,L&ۜ%'[Ny$78XYT'YzRډ (YpkNj;ͱn&c<$0F$x$ k<</TW (@˩( r6W7t7}nTLuWu{!@>+@}`@(1ZFQժjR)vyX5⅑\.G zL&jE[ GenַA xahnTۍ̈U4C F#oOOl2ULMZUS`@0Do ;(d2#NwP* 61ngH"Xv#DvbtClsWbι%d(,2yKrwmDR`H?3g?J\0B %*Z+Zub6}Ƶz8B༻x…v,Jў{v0Nt0aWׄ\3ߣ}DFA7t ō3NAtbEbcIs`lH9Y8.ۉphĚWFKXTb!0:{%AgN{}1rEX(5(~(rKG>8 Ac7xKk_{!fuh9q+-N>|L_В~Cw0OY M*) X>yL ^a7ʿY#X'nYݰ"n $7nkkG !D[F^¶D I:_0znH'FQ߶J_W1Ǽq떌-۲q*y`DzfyG{jbJr"h11g//%7:f+Kw-V):f 79[p jĕbK$'-P IINxa^lHf\P(cFbS5`C:;;$9cAoɋMC]#LjP[lgbqGGD"J2LJ#+= q5o@0#Ѐc3j=r(w_.=yl;'O0e|ѳ6]ȡ@0j4;yŹi1o}vԅY08=:oLP<#`ƶ于#W7-:2n^+5P^ (Rғ6MP25pڹhb1br{)FKEc DM&kzIyhG0gw8|WF#+FyfwĂ"˕͂nFjbQ)191rD3Fߕ225nl,؁]myc#eh}WF ͔M H`T4 6%AƨTd.QEZh1OO|5f)B6;p0ZFu蹉/'^܈9?'FS %BQc1e5Ll?oiO_{MG~0ʿ-E?xa~$ZI)ݑ4z$ف~7̈3$NVP[61"FbX@0B4wΛ9r\MX3lWƬÛ'Vܼ bDH\h񬣵5Z-#0F @}(55uǎ{IڽgOBkٽ{q T*k77u;S(4 0-b@Ɋd%FFdE!tr>01[ZEgŴFss['(1)> I7w to6 Ex4J̇0 2zB^h`kC6\1!q Tݨjb;03lxsjD򋐲Fv5I1ȵd4zY,pNW M2mIu?XƈI15P8xnH|TsfޛX9| Pm0kA(1R{|twןa-*µUX7FN5Ikk;ԄkV<*?V?Ъ<QeavOa@Hgt\2:!!|n8Zy_îz*[bx`dH++/yMbϊNUL=qjK#ߏr#"yֶu\ۼRcſ7@9"'971q9dϾ}Rv'&'$ٶm;E@\@0F @~QffV~B%A!>x5¨E.H:;; Zj44 6ׯoinikk;$2LT.MNˆ6#(1jhh 0jmm'. rJtF,͋}0H d2aN$ mH(DɯR11z #i^׸T+lnaL`D0R"BR#zhͼr#F ^l 6 x#ViH5 JlZ,ŚŦƧ0190Sf{4ST &btw[6jljDaՒL3ԥ! b_M4SC,.Αm5Fx#[J$G3hޠ7 2[q0āQfff?! V@ #`F 40y)F h@0#1*` O:UYY}mѢ%+W}|*$ rrr"4ŝ>?p * #ԖEEE߾PѴؼH$dxGF# -_ZV#Z* EW~N7r<2!|nRH_C 'N|6كX1OHOO[Aw1d@yGBCI/"28C,}kh+R)Hg0TWWwuumyn{}d7<9ucG/^zOza[؝9]["lNl+gY">d p; 5;^22;7ZdHoX,mjoEէѬ8+Vm6;B3G9uwwmq`:F8RMU8lk]c~v̙ ݮoZ,6nq  ½ 8pv""F"9"0~Ƈ0扠xv?t5Ǧݜ7nܸ=.**CTqi:"m[0±qS8Ll/W8j&@B7*@eG~k;w///MM~0PWcz ze5Pgbk\'Ro-9NG Fͽ2Yk?1 Gͯ|2g^Ls>cF09o=ޞrR^$b]=) ̄(b}bҫ~|S.-|+PZh^_TT F7_h\!Wx}@ 4@3#`D(7/s?ȼx1+''%[˗m*)++C$ aw#<_$ _z2>hpa43LL燄>}zܹ3g|WXXӧL2uԗ^z#~q ,.`QF7nҤIǏ;v1c|I/ר0+CnS ##^}^x\#F@mYX0͎Y>zLs'׾xA9cvvJ;tõQ0Qa!:ȑ#CCCGBAo~_L(=BYqug#( D^GB5lsG@줩(vȻ;1F#kc>/"j&LbBgyÇ Oϫ,v"<r2$k N®x٨ۍPO/1{fm>ܠ̀xP"5R&ɋ^l*?anfSf^Ք~N{qd AQCpb{lh>kUMEJKKB!l6|</6'O`qOLjl~fMy|o$_uÚQdn~uXGXMvo&&>VVRZ%F<ȇ*++!PsDP(·@#`  (^l̙Pw /0zCX/[7Ջ|٠ba1lyEa{/6+1#V/vv[Vfh|:&bŮoOɋ;’|4haLj?ňՋ9ێQ֗^x\ya^`y>enLjmPt`z{0ןluƤ/vŸ\_<%5O֗dz-1__v0cʓ׮u1j @0F P/1y)F h@0#`@AQС#r寮ތhsx<_W9kwyb]pʥw뮔߳vcYE)GN=Uިږ_]?F !XÍ `3FV5/^zdC?lذS41s7o4w|5OF=b%sMƲd[!ŒU11q<2>;V[~]ƍpq7L|q#ᩈ:_FC#"&"UN; i#>L#249|m![hKO<0"|M؝m Ӊ8i 0DKˆc#r#Qky!ύ˶{hrieUky!O)6m"Y@0#`@#`Ec E!o@s?[4z`=knӥюC936H$ ./7d' (#,WLʹL1CAҮ3X)h1_s )ńü6ûv r{(PbAz#9# #F36Hu`rcZJڃ- #S –b35j - ҹ(d/n!Ԥ)6#`@ Fn*,,ĩlTjČѕ+WF|v.))bTPPVzzz{z-VlF[JKKAIe_*K6)z=*PVV5i:\GX>ׄgyajͻRVoރVdB+L`40"8֛ڧ~قzIDI64xYi0bT^^ъ S[ɊO>1eܙzsE#j hHbd0F#ZS͆]2-Vg8zPpxQ^^ZQljJrK!ZFNK^aN!J#3FhEPҫ7Q(:r2c羴s0]P%srrxF<ǔ:ȟ1B|f~羣·.X|c-wFak;uAZ4ygE߼LhPɬ,AW4X䫅K+dRWϚq\֮]xjČQii)55 [^(lnjjk@7*5bƨaKM=z(zyZÇ8v}A5`@ĀG? [+־IENDB`spyder-2.3.8/doc/images/ipythonkernelconnect.png0000664000000000000000000002622612626055322020532 0ustar rootrootPNG  IHDR#s#P,]IDATx] |չ?3;!HPpAzm]Ā 5*U`+BEm HkZޢiyȣr[( m(CMx;s3eΞ9s|w9r !_>|x%ࣦio:܇NE}?2BQcrIʈ iRNdl?R&fi?-:2 ʋlo>^ ,# oV^=yayCIj;I9D~WG'uQGGaMsuʔCBIr>X}g J87hzW޶<:?+=}xl-WEh΁E<8W;pP>yp9BwIp"I\Qt>bp\ !3@8ꫯ?'! /0鼎}@ xE;wZxw9[=Oty*UHSÏ҉%K5!ZY(i!zR3Ƶ&͂N9i RqGh5Yyd #ﶲˁ lG "%5is |{ekGK%?𖤪j,]BE$ n=rEI:%얄9TS̜AByĉ~XqLzԨQ3!H1$/]9@Yo\/oEN|CfҮPHJnʃ#LZ}ikH6Ďp ICd㩘ՐK,[ LN>-?sǎE'׀ X`mCGfNyOB'ޮ%{>LBsWtr>}W<)إpТxaN ̟ҥKss3m#h:H~Ym9[/˧\M*vN0{qH$kalZ=)9pIS͛7+++GtҊ %oqAoL7FU+zJKNU+%#E]cRPofDϔ .T2gCȋ̖}l߾W^9p'y5Ɨ$o|}A7N;WHOu3",Mʎx,66$zD {*FfNiv c>h׮hÇwڕ6ǒٳO>e%oz+S}$dU>ع{|`L&P,[B?);fx=(+9`zcǎQ?qܹmE~ç`lNǣY2) (W8|ӦM5hm/ uB\kyϱ$æU;CڋVl>4;df^J{LSLܣ֑X *sԊ( |"3 Ïe.T)+'v8fZ{NSH1:pǎ];>#Fg1rϞϮ?{5k4Onn`wey~#)qGh~òKNwCBsAu.zj3|ʭ_|i|ٺXo~qxwz6ړX%ފ\bk5pI `xh7?`DKbC, hs>qvs&ZḞ.~'?yǎN:T_)-]{? 豁W% UaN^h=Perb|JqM8@ 2_ ?%-z.`Kjh `o׮ YY]%ʸ4A7Q|t޼{><~kWԵk婧Mg{N1)z\]߸;z)o貸}C/[v9k/ӞT`Y9XXxڅ݌^~.{fؕ $.θF%$ϵ2ZL?oۀ7Y?L999ӧO2- u 0Z__Or޽{iӟ]vm߾ѣG);[f>6^$B?yP#!ưCx}+'J(;}Ǥ*Yr /{I#v8i1ڤ-+́6#Xbl6^FɛO$?0X] /sΞKAfAO4;v@cϞ=&L4j7tʕ+-#Y^^ɓ'YU7aѨ{ر#mMrB_c]w h{&2Uj(^!ðOR@fJAHF<)Cy;%6Zo [$1I10W{\I].NjG< mUnnݻ8ϯ=WI0j4q%֣}g!7bGN:ə9s&]7khh5lyU:H?=ҍ#l.}rboGew>;6'lepЦ]a@iY61QtR,p ?t8C ̵D-;nhM5[f4ٙ^J!2fP۰m`^2ob HxÆ 1bDr!AŕtnvwOn3'O> oJ0 ]bJm4/]/ˋD2,^f"lt{;e%ߨɷsG "iXi`zF8] h2XqeG1Xؖn[\AB1 ^|F58i(AlL a8ix'v%UL]NZ,# sa4J?\%m 0HHりiBs%Fp:fS>9_HHC% itħRbA%EE~`0!;sc*`QHd £R;Ys@lPH$B`4PJ&D.jS7 c EMWG HQ/FMS"Pb{:NPRcYK{]Q-TXZN7ni\fiO@9iO] vWeI\Nj_L(Vr58)0!3ʄHtY j ~R6>"yQ[T>X57SoQ;*XSt'VX5!s)miBΑA<2ql1R)ObXI?))əi`3L9NR('kJk4. %%0[Õ÷Lme_vs-u1\AyIL (46551FMd3D[t5G L1 ƨlAtdcFLj-468L+2BrL:W4J4 [HBo(>]$pI3 3dQC)f|MxS<딟tT,Ũk]@Ւfblb5xUPyW'coi&CJx$L#%5Z(D_8ԜiabԞE)H">Akb Ɛb8˾"袜4]֐XY->RqUn #Mr.%zGBZQ K5:81Mb03sD!鈝Βkxr)(&fa;@Cj"Е)XNbr"YpĈ@wHv |c=Rcj*d7nP%5)r31u2cu,݅;)ҩ 5-$/ɤTmALPG46A؏ۘ3洮9su !‘&O@pR?NW̄`i%WF#1,P3.Bnuc0#dKJ1tWywajOPɆ{ Q<}Ҷ VG[fCIQʺʅUJ-W9 e ie! P8!0ycpbz Wcȿr\%闶SΪBCg&2 R@lk12 -7?~TvNmO p!QP)%M8j68rcBN+e+-@Pȭvٌ۶YKΕml>Nf, \-Y} O?䍰why|qW}.J?+L0FĘAeНz3:pn)용]|n=Aj <-,B@q(/5Ć )!ve,**ox{apciR{(\ZCEᥕ 38?u6FI SުܫFv[!gm9kJK -S\:Y>p )/o@`ڵ4+N 7 )Ae~+ ?kE4M`cZ%N4~>&>5~[u+fNHVEިSǸ%㟐B0Jӊ -2*o[옷>問y%QmVu*׻jT<H P6vvg[j@JVfdȜWI&\r޽$)[}tvioPhpe1@+j3g3<#G֭۴iӌh$bFɡ<=9!MlNk>qnm٭ *PiGj0ȶhcc#dQ M,lVuGOk,oQ[Kz)u?=-&@͕:r>nFhPwrvYᾤH~zvb۳:=zۛ҃t!;MLi3֞ae14~$xClNPh7`mM2'Ǿ{EaVaH`;\03&Վ]rTF=cg ?+}v{Ɠ,G#=K/:OOɍ]h]msf:XA _n%)c `6WkyJj.YsLfneUgm/ u5-? | e)}׽}U:Iq9+'Ҥ0b2_ue˗ݻW[?c/3≹̙$ڶ@ƅᄈ <2̚5kh"b>}Oכ15?$ֱϳf QDlge)Ae&kvMU&O?N*Gc0Bҧc6x(k a#ĒL3}g k -qF]GcRy+R,Hl1Xi(a) 3rاe"2U~#BD4|U(iN1ۣ "hY!Y35:a7ҨRg(Rj^15a":];V]^5@yy]5?9?Ν!ɧơP#E@?v_@ Hpx:9)l!`_n}Rh1х@$0⺿i):s, ) l'r"oH"O표% :?FWMHQ0 tQ;,oŎ ;ZV"Kt#-!,fPR'{LD}GR(0\ٮ;sȩN i4y;?.f#Uq) FO",hDZD7%)JG֢ċ.Ki>Y;) "ӈ550Q_(R(yf54D+ʵ"EI8C7X#5BN"GD4PFhNf=3!4E9]40썥{j2E4]+(FLa| 9)'L&ld/ՆkEm|r;bdAE=BL Z}Ӿѵ'>;\@Q=F"IG[Q{>]ٳOGvUȊ%TP1fC| X]VmHQyd9dаsR>I"E2Ґ-dƑv)&bG-\4Ś ; !NoEh$QHQz2ATƆ&9I5"E$43drdD!M"gˈEN|2MQF+?ÎN\\? Ҕd/-7|bG?gV*j]덣ް*6X |F3e9I!2GO=П}ُ^@oT^7I>GO8y}M5]SVC>_SV0{4{bCn?SHc1#EŒ 6fV]5n y+}zOfhرc)g黿>[Oyf yKLԮ55+Y:nmUMͿ;<0γC(:L:vL{76l#i1C-!hܓ\z;?雭h~ ,5Ѩ~w+pA%}?*6 +H~X~W;AV X￵6:drl-BMŞBH  wZJ߫G8_/7~Yk}(1hߺpPu^ 05r83 D EzW uNquր >6+_NҢ13pk4^dĆ+YXQчW}Gsn៷̝yXҙ{6rHF~W\m-߷UJϜQK5L坮iڟqsf Mw|q⫧E}]ox-,g@"?5 W>߼AC' y>摜{II{6鉵G4rڅ--=w>"1~$O8L2l޴n8E?)29sM~+ceyov@vHp(Cjq|E%=װOr퇣>3.L@9U5eTsEG-)<umMBMy{ M0))]FچVRϢzPx rrr>}߿uTo߾P#W:pn.9E!]=f&Q;:k5jȫ/AnV^]TT4pQz+%莬Jk׎BM @qt_P#[IPrqA @3f/tR['riֵkי3gp:\f2-T`2FEWrLj'k2M\GGE2iJ1h~;jHQɠɃ(,j"EYA8^H%.))Rmw ؈)J|LlHSŊe l"E-6]LC2(RUoFG3gjERK.3dF#ɓW_}ĉ}NrZ!T;$3 gU+ $_2 /6qB JizH'Ӝ$_I EE.Pɸ7 w-ͬܞ4gup(ǍkuTl3cc 2r杣B] FiFw9TUUu)"A`LrAL ƭZWbՄ69o;]v ڋ/gqiUٕ,` WX4VS4ve݀ &oZuuucc֭[7nhƝw9zjxHgHVv62^ufAC577=ztk֬QHQ4v؂f,u`IENDB`spyder-2.3.8/doc/images/texteditor.png0000664000000000000000000001373612626055322016462 0ustar rootrootPNG  IHDR~y3IDATx흏sսS!L"1 0@KXL@D"4b'}'CoU`qZЇM"?&{䆒gv={vsnMG^_8r7cOuΝ[ TTTTVVcDK+i 8e9ӹ6퉅jQR.{Ԕpɓ_}Վ]&_K7 ?ёV6AέYX,M?>9[m?)R"Wp؃v)rիWYߕd] JUUU10SJn6/)i~tzMK.-=\f}XSa)R"W5KDщx_~YT_^5Y2#p}͛7oft =HGg9jAS֪)\8{]^.UȦzDU.]LY55[hT gDGZI[ ,('5U.|@ڴWԗVvCd^,⬓'O~R<0q6ժ K+]Œ*+tЊn1B,[81rԷ{bllb;3H.(nC̞={Zt7H" rnΑߧK("a„ bXڱv8X^pϤIGdK/(Zjg掎s~v1+iGW}ǻ0Fڽ2B~nx9&ncfQs|nN^:{_,7dQYto6J %)*>hg*/ABve?+nCR|+Cdو#uFޞU)t?#G1v5oZ+F ͛ gdW݆d!7̌Ə?ą>|o>B{]Adt7>vfsR_ZA췿'm_l~i":K\d Y2tXGԽ)7IJb+3;Zy^ j>S.\hoot˗e;2oQWyckcd",cߚVBb\ ?@]t3q<~(nxY;Č/[-序0˙=[!B̜C贿̶JoQ4qg>DCV9#;>%۟FRgrǾ-s}Nq<*G^<,`u6$;ClrwtՐ!C|ș iՃY?.uJ}ieQ8D6܃hh[©SDa2yҼdDVQQm;b%䍐ҝޛȒ1Dv>hipШj\·yfsnrF,sRdXUMr%C.Yw c j1-ƈcViq +N抷r!r^rcֹ~$F:{xK㣂ӮOGN~+3x:y!YdeeuYd7H$AL/k("+𐓓W׬^矋Ν;wDly]C3'MrzˎbivaAps<gzfSw@AR"3#h]2]j-ױ1h:ԙCNZ5D掃#td?4x0mf=ZN%f}!NpO^6$ӧbi$A|U>?@HԗVvEe˖kk׮=qٳgO>-ϫy;qVZv8p@lyisGjQZPS2\q@E6{sY%O ۧ0hкdGC3~݆$!7{II( _AAFu,嘛)$ >"Fnnt7?n][[˗m7_-+++G~7Pٹhf_5q[Vܨ͜BsȊ+^S5k:z?ځsXl?8j4]Ȧ*1C"?Dc %a!8{]ueeex/쥕MtzE%_>%Y*HM/ Ȇ@# 9se cFpVԗV=W3ƫ~A>/˥]^6ɧBw"TGn{- oyvv}DVd3m"A  2H_?@BLV"z "@d 2D!2@dSd\zVd= "Dn&"DX1>tҢ#K&^Jrt*OYuuuI%Mn_WX#8ēMF]>"3:j%/CUnrl%qXAEK-x^幱 :V":z[ym\]Vyx}хi'qcuLFo0#;g+*WSqEr~?!EW)4Y:='N`uclu$ix&2ӁȂ!DߤΓ_]g8p瓉'lqx'DXsͩDgd/)^?]~bx-XJgg>Wv| ˌȢyq]>zp]&<Ƴk9빫"27ϣo&2qz'!EfuW#G p^ܝ_Bwѫ6B^ Z{ԏfq\KtUEǷs3MM|7 *xn:cQR_䉽鍿,2oEօ'Ld '44ɳ; ,c歷W+ uhȍ?HލW_|Ro/Mɢ(һ!W߿x+G-P@y@`joާ*W"Qޑ9:ʯ\\OpcEq_ʼnt~>Al@ęKw][bS7EC ~  O )  2DX"D!2@d "D 2@d "D!2@d "D!2@d "D 2@d "D!2@d "D!2@d "D 2@d "D!2@d "D!2@d "D"@d 2D!2@d "Cd"D 2D "@d "D 2D  2@d "@d 2D!2@d "CduEp 2D "v6ՑIA*#oQ]]=yB2$GGd"ܰaCͩH^]f RSdSLQW?JE%%o& 5EVTT.S d$8"%p.}.>8"ٹC?زJj!I^"냉#2HY M..46ᣞIAʊ~g841TJyW2:upYȒJCd' eEGZ1ic {A%й;HFmO[^|֍Q O"yG[1]ƴ[lj5k= VdnU`ě 2h";{ٿ=tQZEZkFi}E2:{NȺd!T0s5YHRFgܒ8"ϼD[yoQs횗6oZ~^{o2WqȺOvVL*Ժ^nLx YTv]弓[xޮ*2ov1+Y^mtAdj?=yxɻ~ֶ4sKJcIVn.n&n>l;mԬܴ+qcO9,#zd' eEOXzgwOM}lҴV8uF Y׳SUZj'kk͌FzWd' eE/8R gF& eEvt툓N)wz{"' eEv.E冋& eEvxyW'6dzEGd"2e-xa̹=%HFWGd"+//K?kܹA"뛉#2HMUUU^ZLݿF*KjT+k>aٶ6=펡yW'  NeE![oHsI]E[ <888eO|s.YO~XM dطSvp{{ +֥ehQxHUst|K@cXT۔+#=`5E{eNr)9yF:ls(O%)$hQ,.`_4n Ul9鴸,=Hcx|G}j5rs*ʢnoܻwNSg3<oLu7V"^~Mnjj=猎mGU{lllF9oƚ(=[,iI[(*]%%#{E;Lwm rJ%rVYt 9Cef;e8I\M!#LJݜ`nigeo`sg:.\UU駟>캮7<:4후`aѣ Tl0`#];mmms_όIt$"@is QD[lYbԩSxQdēiA+o0liiɢĵoTI٭ ! d\ ˎ۷O<跿wy Эվ䚀 )SnSnYׇۙ'^Ԅ`" XPWWm۶ 2+`AKKK¾ډWYY`  :{0@fxСҒ7Uڵb``'Io]`٣ SN>}i XN9vOzMlVlv{1=lwZQYA;'Y gd_$ 9D0D-tXRQV $1MꕇrVaGLNg,qD }K쏠M::: 䉀գَ'|%M^Tb$G3L9tM el(8z(F=JwU v!J(M6hpMK4 .,Vh)%3YBN+^hF)s^{#2VTIVU xddDuDy#4 @. glG3T=>lЁ-j#r嚪aGv|\J^n y\+/Pt1f P梳-%!Vn[zt\JDJ"$ȔFYtG&WTT_!`w[<88H KB77VeaGss-`;GGʵHUKE7\]>pK[v,==$Tc3^JW4I|{ʻIZ+Z]]A)`idlV;9pr4,:U)%+saұ2R]ktE)v+3/MGsZJCl z'%[Jj'=R{$;k7wſ»E}wʂAF6S|$<_7Bj)8SܿM_]դ~~ﯽ|eί/דݍ6oYж9SC|p%|8 x0 `oӶ>5[3=}mMm֦Ʈ^x1q,{3V$ ".l'Tl}gmp_=龞SّW_]M5~IҰR r!`)#5 c!ZFu(0Xg)bMsr,!Rz#) Rp8/oyvLV'EhJ< 8攑Q5]~K .}7_zڿ֟h9V}.lkm j3tԩcj=}jMǴݰ`s~䣇ƵS;פRnM<6. iin3,ٓ/Z3Kn-??<>#SM-ͱe^ /+g""֢1U̗ vp,ɀtW/q7nRQ4UMÕJ^&!`WC#G5²d8/)us8l?ُ=ܹϓO=5c JƧ 6O=qq,8RKftCt' XA0Bvaڱanq(o!; ,Z2#ǣ)5VԃK;[o||qsI)`n0zS'e;|xTp.٧4^a1/@[L i|FE0l>gR/0(64gNC_ꖿEFȮ}{,4mZ{C?>u { ӯ]Mc1DF0\W֭l_}7mƍM_h8-qQ"`@ \u44kV;>]oӻuڵkzwD \*C QЌC 6ɟLZn]5<1~iY9'Գd . ~pm/kpcm]f}C$)x.y;{]9XYSCS˕GFW@護Z{S;="k眳~z 0@ں5]www#A+ 5o:ry% RO;= 2,` 着| sC{V&5$e30Tl `x%f9$#d CzXR*6 0L_-6eg/+LsbqyPā zNkA) x  ̺JiUTas-&]IlkI<Ŕu Xzջi9M  .aV!ř6|Rrt x%i2v 0 `qG9'`Qi 0 1'rbeciQy E/h˔<iUPٰP8`&Oavj 0 1|X@0 `!`t<4Sjơ7捌P>FI/*zdƇ/X4qz?;ii2^@Y`W~SP$\X`#3;ww'o^4)Y?_E `٧Oc>??WeڗN?{3u=/1I r4Rb#`g8}M.}s ۦ=ןiFz]km"4ϤFGSl3O~䌀rT###7>_oucv|~O:SR0nz1ZZd]kf>- -]GXjˇٴ1K-v:4@1 +l#8}>3}9co~p ל7edC l 2TE]l@dK,#`f>  #[ ˿PcEvCo|xaGCw{) 4AF!l] q`wʒ3#[+궍ϖuuuQ0 >xΝ;6]}I|ek_pͶmۄEmdR`0X[[{-3vn{=pǛ. 0@1<<,jbBYT `,  `@0@0 `p- L x p`[l|?j|W_Pi0w(&ԣiLhkGV"58_ GΒUt0@ _lkϴXD=`hnl3!@y%VfZ!hW #`@{n5O驨صkז-[mVVVvȑ^H  ` Xhg˟zj;v=~H pdb@$)@@m\/Z>ڲ{{zzz{{ſ=~"HLȀO^>eJןn{ʔ'D7<<|R0VxI,)#Dg6OILT_}OZW_vyFvSjU) $Kj++ Xd sC xǎ555}}}B== 8100Wmle g# ,u/mΡ%TUg<#%nNq|E&,mF6 ]HQtIvz -|V#R(dO8~ HOOCGjpkkk6U碀5ᆱQ*"7 _tu/P%ఁb5]bMi. $v. %+Wt!oeeeǚ5V}lƌ~+7o[[ۗj4 Q\O(4iUByj}eCZpq25}ȦVT!zb xw_zieut^{ ---o{7:/\Cl*F4H8 h8khGU1}RHK2Ϙ>q,a*3"0uѩZR(:MRHUYϛ~&4A絀O~ Nt_qٚ3GE+fشiicb/ȑo4Ad~,ghP7xSk5i:_"(lh4nP :aMQ IMr+R|8pηlR&XBV7#*ʓ=9o&5X.g̫(0E$sO` oD}'M>Z=e 1MxV藻Pp[ZZĿ`PTC G @0 0 ` ɣ=` \Pz?*^p/`CeyE' ?6vݯ}tB7@2ZC|oVM N{<~wA ƹ 2*pGD9'?` _lߙ33C޵ 9L_'&Nrv#)SHK9ġjr@0.}z0Ĥv3np:45t2J>[D9&uWs9{Οfw}l}xdJ_wmoLz).K-/@x4++/vD|h14th/v=`Ixn $};)kvf9]:V^;ۮV|VCKϹs~\y/< Wr X Li96 kzrDaCٍhO{++F ̆Zoܦ_ Ft.҄M)' HyڕYv{Neʵ xyx"g͞{<KW-k4kb3$`oKrwy `7Yo #?< )t`=z/_Eۅ[?̦Y `X{ѝ+-]Gw `  ?(zKmr?N),zȜ7 !Hpf]V7Xq?NpyאL 8YqW1b{6_LE2\VwM[Q{ɕ^Ҥ+&"HN`\ ^k~^pHhن KO⫘)~bHNԼ|/9A< XϞԮφ&`LL$V0z;팉_]$%<}CXLkz,E28WT:V&F}@w`Hh6Db]EG}֖c_|᪫}_65e)fMY6[&aR/V` 7bB|~^X t gvςO/~1_?< ` XT+W,**Ҿ UuHNd@F@9)`Q vuuutt?|  ٳbS#gBokdE"ؕUj C(=@يſ###ߡӧW:y(x7T;ҵkqT(n0<p0׷bWCyÝ#Ûk` ,3};3f80 `'um6R2TTf'ϫ#0Fn<44vڗ_~Y{{{WU߷qhwC,k։ţ Y~$bʐO|hX&|c|tC"u ;( ֛(N!` y&ÇoڴIԌ '[7Ehllz;pwkuU!{qH[ԕ$q[ʑMX_p``v\6JW'L;0䙀;WĻ==G|]n*ۊu^qYk65A֑D B_sgg6gi&dX=F6޺Q;FO~gq}kYZ+V444/ >.ѝ#,Ti HoH1RyA;0䓀}N6  [NL/rSÉg6V\__/S(GJݪS]@vh,0e5Zl `7MJƛ˽;v*d?0;?>qMK.-** sXU@ajYQ5m1NNVאIŤVcYr1F)QǬ:aYlN~۝BI]]]<pyE{!`1yk]yGP;[%/@rY'zj$NX"=v؞7wnym˪g6w 0F޷o߂ ΝUWwg]paUU` _u|ڣ] `L X_^dEF0 `d`T@D@ #`@F0 `\VVy [NdF6vm(=@MȈ<ddLQ 9۷od8tF?]==}}t;vd8jab}Z$mxe|",pH0,##szb37n̰G0F0L 1]tɕ{Ban=#q$g0 ';wnذALuuWΞdw~l;g9E&/QLEDۦŏr0<00088(&J9`^c5'ϞI,`G9Ms0Fׯ_/&:;;::|?ZyG_oRKdFbԩ%ow˫f=~Rǩ-eQQ[+ֻL ,:44=n4GoҺu\ X ƒ>uBD0p/}`k{[~>?Sm=="ڵk)=@ϜڴyU~vw[[o]}Ê׋;;íЅ `x='jkNTԵT9VSSW[[w.`ժU `B=E-zׅ gxn/^ ` `@0@0䛀WW7o^𿁊? ~zwx 믿^*UO2)' 9|D0@3!H@Ȱ}gl]G+7:jہ˖m(?9cо 5ypéG$HN.F0/FQ:y? 5 Xhg8<[4U$ؿw>@ xڊ>X$-+[5X^J3u:*^0䭀,;r) XWٝ5_ |l#`Hk/=aemiΓo MF0|:C X}cpخV]#`?zX$#`ob[O>"#`gxesk=Ģ#&W1S$Pt2,jGD0@ 0 `  `@0@0 tQ04e[xbT%@&=O$qцuYP }IÝ#`DH,2 yTC@F', ""x&Rx^҈S0.,u 5p4.XQ,7YV X-:DԾO'a` X3{F4h^)6j}טu+#֥ 4z0q+dz3Zh)X%)`u;3Fơ} w_],jocߡݷ0q)f ɷl6 :R^[ak[Zy #`=V0L F0 ` @0% #`@)k>X~%FGR m3h0aL#F6`pXlIٍ`!cרpy&ˀ!͈ |#g´WqXdd`Yޑiyl_ J7A@*SXռf@Oۏ0 lFq/QET.beVҘR]̨q#`Q6&Zd1ɦvU,ۃ0@Xka!EKnByXn܃6Mrnm/5+0͏!IF4M{?YUSS'X[ƺj3,TaGJs0Ȧ0F@0 0 ` @*5 "` @0 0 ` -On\eUq Ek.ۧ^]ڽ%{k%-wY8Q ޻PɤC?"}կ9Z24}Rt2)֢>]/ *{"tkk_[(_PA) t[sG^}9PPSSx8l{KLu1lkr~uQ>x}[׺8"1p)Mk/xw};mR,_6<2s]v]E6A6n7̉RX8U6V> ls0@^6cx*|_\m7]_x^8pWϋXLR>BŸUCUW=R{tMnkkcwuXS}u۾yE/** ՟lmX' e> Xc{GLdCBµG+\VV  069Xo{{ Șb틀dK":=F[[ a766v^i ---LWpss3JTϟ3g,!1qAVV{Ȥ^uMMMzW_} ksLi}}}---*Z"STG uuu 8Xt8ę&XWW722f8BO s? j7>JKKKJJTk׮t < B;=QS0d  0 ` =?L(O2IENDB`spyder-2.3.8/doc/images/variableexplorer-imshow.png0000664000000000000000000016013012626055322021130 0ustar rootrootPNG  IHDR~*']IDATx`Uǿ3ztT("";P.`UJ-@2lfMI𢡊WegIsQ:tH$D"HG;woРA!ZgʕҥK<͛7st=?<3O>O~g}\ԩSK"H$efYH$D"U\d#qC?D"*/ը!D"HYjŏD"Hʋ,~~5zG"H$RD?w=d#H$"_H$T9ů$o5|k6;_G/hxm<ܓZ!D"H׹/n|hYj ~+LlЂ0|JePc @yk0x&7,?hC {Ǘ>vM`L^~7A=XκnS!hIL/237&~\H@<9%cѩ|GᭁDDž^BLxX:O[%c;8~D,v9L᯻<~VD=2(o,=+\uim,NtχϢ"s?}ۺ*+UgHgpW!v^s,>D,}8agrgT>8rO僳axPtIK<.Eϰ/tZ/|Cԗַ0%{X?@ZOJhuV8}m<ֆS1V1X)bh$> _͎Ije'^sڞAvM|۳U.>tWsv}Vg|@=ۺ*S[wHAqqqAXmS_j}%y,DXl,ۃ'#SM<K0 oV2^?]P UJ_DЊkUqc{TuVtešf?Җ/ OR#YUYN,z 矫]C#f- C_£9>eUmЮ]i%:E>ϲʕ,=X:Yu&;nFy~-RAnuo@|zQl5NU ^&dwpXB2&`)+cMJlTJ5 feE3E`z hm-hy!pT|sqC[vB Un}#E]Y Ӟ=B6*t ;A}.> ~[p!#U;/1v͖Àz߶6}7ESZxW =s[e_ɗe _uw=E_-i5[ s\yH #byË-~ wEtL]cv7jVAh7l+5-+'d|s˶]W\rlEr_ȡm-f 7 [>Au2(D^6\g>}厫>msu.\ b$ҵ;?eX#/_Bgy-0L6wUhz5c #-RӫH]ggVow[nذA{!46.QyT䎲EǗEE=,ZE> 4LJ|>?ˎTgnHyU3Z{VUCMԊpE>5`d ?nEaItMD2 ϸ>ڵp.7 r]eUc+Y΅ y^V47|s:84qP[aIӧO7sLz%Q]1!$q0\€>! `E k_jjyoFF_͸H~w뭷j"''ͻxu[^x Wփ… lGGWc,~$6? yCw^[> ԭcȑo#3h%CZczV&>@W,~~5zG"j:\.,]^{t:s7nb/z߿W_ dW/a<X~^?ռ!DA/==]_^n.2++DG"3H$R+رc~$?߅ ~uH->277Te{񗐝}u5m/zDw>I$R_?b1 ~9 s7 O0!~oqZ}\]ׇ:DwoFЪ~Qy-¿_gxeȥOD"#({(U~ǎM+5T=yI~~U?k<7et\]p‰"Yم.֭ѩSu_X3?W6-}9xQ`mf^΅@GG ~|E~ot~=P?Lo;F`h0'݅׬O>0WK͍xOaHd {=?O? /O~n|DwdxWHz9[NtZJNJ3Czp"̑f3&!FGG* t;NY ﮏLXW<y_ċCƔ6w0FpO njn?_}@Fqܝ(iw~>?dσD:JJJto_ ==q`fo#Sq++g`RynRPPPYY)$I,8 xxk lw^T)?ab @ Q( ,]NM~,X8D";S鬁ߔ)WwyuUrW-NGGWu r/Yd |2o$RSrr2իGG:+'3qE:%iyT^c##EӧO#??D  qˁTVT3OZߌRE4CU$f?RD/rqnїDڿ?bccѴi$-~4usǏ́!yD;wΧ:KMM̙3AHZGy999$ I8D"j=sH$SSixq֓Ԩ$??DG)G"H$?DGG"H-[F B]տ7oGEmD"#]׸qc?~TkqI?DGGG"##JQHR<&2s5Q^,TCϟtpTθ8jX_m݋U?9Wig `%4IJ@X 6RzSv/,lFxk9 ;vj t}+~'a+i7}$~~2-Ʃ-KQ}p࿟!qX?tc8$p0s`ܶ']vL($`DУeĚ%/y:;w"dCf^ڰ1wl+cV|t3?٣E#Rσ >[={l-9N>|XXm? 2!X,hժB' nA/S;7R'j6 45sl[V.Vpf&~$Rf1=!ט?Y$Y)祇=xY!oLlТ&~i]6;#$#+?y.mv_=꺛 Ӆ=ﻷB?8ZŜU=9@0p1`zNӁ R5Q/T&zrx}d>g7ף;v q_H1L|]RLɽBW"Al2 2™]R.۲e v2(n۶-N>-`e?bO=m 6~(aC7L>[+6@4n쀙(X:\@v>#1q,}%_$n݊}ZN:tH^?v:uꄄ~E˱Rߣ"q6, H Q엲}eM?+c?=nO.⭁ i ;,?F4؋aˍ3BԿt g9/&MQi#trX xrP/Y䨖c^'~ eУAOuJu 8s* ־OBSnCŹs=yޕ1M6p-Ja93f`Μ9Q֡I! /߇"n^/kwnW| qS.5jp`EfP^=,]TX.2ǡ0??_X$0S{cذ0RAt^߉E-~s~O/g^q'{XD؏h7`³[~ksXt cj&f~&c;,~ъ'aM e'r9> Ģ)=3hDPKX)m">m=PDWЗ~;Qן nÑM[&4g(:\7ç'r* 7Drdxs i87A\v<ׯ;iN?9k Z@f "Fo?ef0K^'wuv̺q:5M}wdᕟ6 ?ڷDj A߉B `lzN&ZGR:6N@iu{H&Kh{D_gΜa)a1/X޽K$f-ի m/wމ ~/}26i]nw.EAa!h'ϏN gEP\}I˨-^ -f#9tilؼ څt^~ݔlCÉnEoኁ-qz@lz}|evn}k \T g Z鰷- $US8`Cqdc6P+XVl 3Z7z9r8; jUamOoCq1?7R2_M?b8oȔev˔5e:/8c;CcŸ+a7W!38kuALv-ؽ~wf\.V9 ҕH׬{=lgϧad~SLplRĐJZ C%,{!F$V~5 K/+pb$uw /SEPq,:#"m?ĥ6CbD4ikRЫQ1oߝ׏"2Oz\||hd[\W  k0 aЈSq%eY TƮ3ho*79Yކ ||ykVP)t`ۑSqQSY- HwAӃ^ާxы)6dnu krN4&, -c ~7nw?}_sqXZq(8R]O/>-V᧯~ W+y!\Ҳ)^.vlM,?6jnbǍn >{;oXVa0ȻKރⷘ}9s r툋UXd8|_'3ѵm<ٕ~[߾)+4Vo8^^[0%{mS߁; 7j? ǎarcw?WpV1 5|-|]wZ ~F`#qa"H~5 ik#-ceA4y+w972\t0kcͫoپb-H} y0ߙ ^ sH`/]L^̼+"hh߉ӘVnĹ 0k赸uw^|x(,)u`Yпi jՂS@MYç-؛SDg>޸cl^\p{1c:5+Rկ:ջdA\Ӷ' uz1|XAIyi XgbNJvfI2EEC͂ӑWdl/=?ykWFXFt_c-v~V,0 &u(+WB py>nkٲ&!gDѣG1cl7Ol#cXُ'waĀzeGae'#Ub4̾r)J-^3w8t`+ۆB)7kڭqdo6L.w}T&E.Suze9iN,z ƆA}fÄF"'n,|hS`>yWĕ.E5kf߀˷>2!qw;oě{\K[4LLcgxvFnz 1aߛv0'& 7MZw-ھ@o [+N:~#ÙϾ43@+pÞc&ۮA&+ЋCnX&!ȉ&IIe>8׸%YVhwN={{t҅C9GSz7oV~/W%;0v* {:6v=z"S􅳦yXykSf>5O2U̞88vpR-?nAu? =XNHKE)M맠osE;جd#U %I%Z^*sMRpj /y᧟1iPt 폞Mw{6W7|;r ˆ,KhG{wdgcʪmp3p=hgaQ5q ׅYXua`<&$`#Ac>tG Z9 q>|(Ę?U_NL+`'wF D|ۇuk7/B&`U: v=⋣& { [X~$̎ߦM#Í|_bO\vhٵe|5靖-śvxɇ3&o//~Kx_p­ToXC_)?}^?~<:0oXi/ XѠn2rN!n=%mrug?RTz/>-1slir1l+[i_?cb] xN1w-z_֭GZZ7__<*MF)s;-K /&cnLwu(éHf $Y/~reAj; dŃ7\ҤTֻ< N.n"Q(krqd!NdfıBVg[Zm+?}q͛7ȑ#MLK"I7 î}$މ%~dWZpeDw_Nq1/oOrs6dr4oF <}9v33@3 @G;#&yzlgn0tEs/p|c]SELv55;#"l<'ŋ2Pc&XY[\Y$B~q N\Η\gkb1e[|[Aop#Ϡwcha=7M8-.d~- q3pλУ_v-^X|1{L,[/;'nкyC Ft{lCvfYR0|`W(~爵3hoJx H~~eC ^f7=U6Yh٨o1[8//^ X1g}X1 r-5/s6=Ә<,.i5D>XY2%[Xw-+1Ɗ뵥^{OzI gEk-}.B֭['f _~XޗEu-lгg 0qQvoً_-u2N`(>}TN<6e!W1">hbحNb/Х+, {A>}vŝun92urǙ;mD+ۉ\t9Y;.ߔ.D:媋OQgU>?__0v$_"?0oƍhܬ9Z5.<&{O-)c/ 9jFՌ/+ڣI <|]y>.jgķzo,?s߬k[ ws@@+l6L;\K9]ؙ+L?ٳE+(~GN`܄u ?s|GQϝ}t`^Cs5[ѲA$R;X[l߱-/]ZL.)^!ݹ?_w.DwafWH~~s}^k%E*ꆟ[CqťuӊT9N 9`a/͛czH W+|?8RF)H,G?ԗؕ}TY= H:(a -"I̦R GHbɓ'OŹn=y@t{ s@[j<ݙ=q_deO [Ua?|ǾGDZOZㆰ͕iHgBVMY'0JYΦ*~5Eh]eB5=z)fA|K OVREG"#UNWjYF>Ij\l̓ẑ>,DGGX qEhPHG"-RHt%#UO'q$`wdok8@G"D"~ok"W |R@*Y3\K7N&vc~$??DG8IIR"_Su樷_[߆7`m܇nyAR?5CZjקy]aG"##H~s|1=‡ƑORu X o?<Έj=5|_(#####HC>=G>ٛmjq~-~TKV}ͧŲYrO]~;5qi3bCz6q-vB<c܊iC3??6lH E"~5CJ*X%-Ifۛuq65]?'W;ܽ^+g+U u` h9nIZWpI!X(/{XG5QoЭHG*Z΅D"Q'Iʫ0L;aP  /XӬ a<3C}X~f;( y~~~~$_^Y׹ ̀/wy^?s,~:c/H~sU=COq7]Z=l32 o <7%1C\8 .hg*&H~*IӣHJRd`+E([qBUZǯr.%xzU'h>0ֽI2̤~~~$*׻wgWSJeU록;H~~$7, g7&D"J\iYҽ7*)Ti-5h"#H$ C <%Z^gE-~$D"=׷[cgz [+ISҪu5z H~~$>y2'IRL2) M֬_[DGG". kHIR"_ H~~$B|S>E*^^mm?D"Mf8l8U^?Yf L&3$I(w%Q_ZCN˛D"##H~U=1٨'ad)-_ڤ, FK{HHHDGG"Ώ=U$4KZ˓$Uc]( 9ݻ3ˉ ,k?{q GDGG"~gk"^ /f3˻(q݀6RknjI2'>>ѭ[W?D"OOq:Bhw^L& #c7ڶmreiKy4q)3D"U(ҋс7]Z|صku"ܮ][JDKLLDz:Q/kK3/ȁqD"+ I>en]1raժU8p ۷cbK/gϞQ/ s<YR!Dd < tLT30qi3Z#c |$yeFDGW'gG)Q=%|x$)/Ci4C[*+-RGJ ͨR0e ; A-dDXa=-T:OmSme= ?  .;ow@^{^..|^oiMՉ~h,2g? ۭILVKO舉Eݺumtr8(5kVꫮ!X~KwF;dL,}~~52220w\fLbr p L&U^kU?, W;O<$)&[}<߱cǐ}L,et EvBO_/!1]s5e. r.&w ŵE0B"#I9ӧOǾ}Æ?of |if|zڟ}3{׮]n{`ͪU^9-2d$.]:#))`q,$?~||-C?3f@ll,~sW1D,F Ok}JOs~܊xb\շnބ3grɲ{n_ HL`K,{_:M #\V'x?{1$y6J]Sg XcA3]\~Zz!, Q@S,qPeBrJ X(OaO+he }GhDI"Dz @áS 1_6 u\q3t+2#6>WZX? =;UZ=z{?o|ر۷mGF {ߡ#&&DGwv^ZOEW3V N~O7m++^]KEK1r08-sjq5#hRVH&mĞ:5\y,}Os>4=SOfs^MqE8ྈZ2 r4 K,:`-0s3q(LphȳY*i j=v>x\>g0]-˖5Yؾ7@#!K/F>IR^9f619v܏؃qaИVG:qkl0ZEbHnt ;{^)-|bt>??ϼ!_,:̙o6@ 4_1z9ϠNǩq5OyA`x8F1I^ $Scq_6'&{4`ќs/WW!PW qSXqǬZ<\Xp,#ݽfyFg)Ɲg bC{q!3Sô1JWU U=hoJti&# ;/`6@_D_ s3߭9yܷXcY1_23 2 4ItCF sA8AXXfa? ~nyXqstY4g_Vq?l\  M&aIy4nbN|Hi":ᎁO̳>HOOҞ5cׯ~JEtE,MJW}mXe/l3$3DGwWFh 05ά4&@?9[G<t6q5lD}j^|,̘gLW-/9_ˁ K]R!Q-`-+ /su[0maK5$kπIV-&Pe/LnrMNΠ1q٧^u1q YZ Y9܏f"~޽;+SJeѓ?"^.(*@ѵkwC˄Myp5NXE}~hѮ75$_GᘠBIŐO aPVK0-̷rhht ߢEyOKykE ϩ :i>5OUǏ MzbaGk㱩;M++},!9q+Ӹ́`>nr;nDeW3Ww]wX W_̞ff;b/5$_#(KсONA8da9&k;*Y}lI^-ݐX|xyn!"+Y҂a:J dr@w<:q=;T@dx|qps s{9Tߣ=xL hg><'|c:YM8qH7KI q>gk?.ZW'l#cFF>E*^ޜtm'k_#~joC3S.䋮Đ{$} ;V% |V 媆uT8n}[r{6S.Ϯ>os(kp(ʩ0s'\]㠧xR6|pgxT>`X+'|-A"{Y$C\9/yyj Ý>z<iO!m~с[XFA$)SU\s ~}DΉC(8u {q$Ǐ@+q- ;շv.tN5մ8w9LXÝ.ծ\ uf'|~fWg vbp(t t@~N{~k}5V3NZ~Oވ Yކ8@Xc* K HR)O p]l h̊di }!yҸOa gր֤͉<^VKgq~ny6yW!?5әjHi#ϧ Y^R y *q+@PByD5pK ;yHS}_Ms)WɏXYz"n&nXb ƁIҮQVGC8<=-$.9c9%h>4aE@Տ~i3DWHif|z\!uepZFV?c ڂ]2O`4xU,_oCcC36wZmvB<1~A@,IX8pX|,o7G `\ IߐϜgNRؙUpI1'Ϧ>[l"`g?>,~a |*/ X {8$ fZSKY^u>kz 6ue.EݙXFٻ&9dJ,ecGt }"|MHOicg }Sد"=ƾb)n) ?EWKе]{?GK#T.ABZ?]˫jBb<[rKO@ݼ-~pg@IZwpI 5vߋ E?y/N 3%C=bAdH7Hh.&KNЭ{F)ej @99_<'| |I;90C2sz9}/aSü ?&[%Z [۪}u-0}CլB}}ϔ,LV{^|0:^lx7.8_fZX|ݹs~\ ? vKt3< ]9,9w rUr zߜ"X )ϷՁoz9~6^m$+~0=bi\ةj8]Cn_Ib(:ZJA!!/\<=c^G~R"9E 1MuvfmH[뎥XPjlka{ !/ W@Wꍠf?|=d|#|Ccz}VpdE̠?)=83"g1{믿ƭZn_(p6lKYHϏn 0'Ns@h8$O)>F_@NRPN}I?dO6g ZAUb'HR`N1R”[I:i>=,XUW XHwRPn <sI7P+ ?s1`!-l> G_ۏ¾_&I.moJ˗)[Kouh߬9\GkA|~c G°2Z2ӕUuaiKbJkV!fvߴ KZ5 Ê2+r PßK4`hYq155Ǘ]Ӡx<]CVm:/ NJ/&Z@G/BvFY9]C^z!`XxnEpgLҒ,AV>) zw.DwY< <]6r̩~"qfM(Qc"h[;o =U$4KZW$)3_=]k7{ \ܨ1Nm/D^+18xr}|~?nӛ$c0>񅼐9Ywq;5*ߟN ~84ckrr_CXJ@Kj$OR9ja$N(rȼU{zYnӒ:ŰgH%$~" yWq˱@/ mD8M*q)iN(=Eo)2ċX\a.3zur-Kl^=,_\O(43ΔP/0'nW,#G͸u([W+O>[:G>ti4GCx N {״ {{B%YG SL奌( T[ՈxPR8eedD 5TTnyY.kaϞ=YgmqϪǏq{{ |3'~~ |m_dEɘ3#ON.δ9ߚ n!}/@ON^u X,r^.=?@}q <<<:oMtv#Ĝcט:#u1?aW*@;@0f\Ƙ</ 2k^~Y9F'8Nsq; ڞ?\)ӘϷ o gՎ}KMPRA[봾)3U(ayh  4 F: ēz" Ԭd-/2x Ys s]CsG/k|4ǽ`RW1pDVzux!]<0rDyzupc/ Y>WyW_}}8tW}Rw;¯Jɗi=}>C/:ƟWEG45zz3G׬u{zZa"e%scfPWÙ1V{ 2-q>SDdia1(92Oh05*uUyvep m"QGAȜ{{8\G0օV`=9ő !8,L~_zœ'O ;KSO|_O/wss;S{~BofiYp؛nJ(zgy٣]>sfsWpXf2L-\zf>\#OwRcr)Td9K|>t/5umd0K8:UsFC31l!ԑs_sG >Q%{M/^$=7u=Tw8boڤ\}9w'^ʻkwK?w>G<;1V,K{D{^ǃgQMuiQBT Hrc zL ̳{`^br3gKh(RO#8' M.9*Ri]պ5ϡ% =-s4vq4g5n.9b!ⲞFƬC =3ozj}7~T{TL7ʟ^g+E|ps}~ǝ]ЎDGc,y9)>Ʊ^K*z^ϯG;|Xbq4 J ,<kPM99i%.ܣk 7QzY4sSSR\FM\GpG˪✮_K$I5 us~`#1 շb97NrP%:#5'S_ 67 _[~WCw畯>t;w_'K WK7X̜yM DuM'`gGa ("Yx)4sSZsZg)i ˺ӹ<`BK# N{10sg$ң57ˢ䄺X,jϱ*avSmU}B!aA(5~neјm yF3݌rۍ׃$[ }܇կk2/eHw[h]ZJ|;.;[n(4_ ,xAO}׮;wX~;1Fr(3)Yu PG}zB~fuA7Hlsh:ڨ zn84 ~3zs }s9[װ8(0T榮c}3oo" Abq `u4'k)FOGM QsֺOr)k5'RSi&5&ڛin^w~ǫw">w|?9$~naZpq±<f_G=pPw89k_/M8a߾}| >k:w A7%gQ)ˎ_fׯ_ kaQ\G6^ԅ>~3LM3yL+5|CL!yFE㰠YCDboq Q` 6;Ịj(ZC~1"õInS2y#72y=칅aZ7;nOڻu+{ۮu~/W66ZpO㛆ߗ.<1 pǮOXvL}3ǖ%|Ϸ?n ng9?;{LSvbVE/aTmbW,E",Usuu~|ֲMX|=γifd#g`n J#gR|0@ J-]#Ǻ5U"PDZɵ/D hM#éPjUΟ^McĠ㼅y{=S&aFn<؍Ws=Hw[ 7{G#-˻~|tZCޚfZw]pvKC? 2-8%<54yW>&~ߟ>,S?+X^ { 3v?~~yͻOx*]g_y!DPRQOהگ~ ?}]^CUM^7dd:HەҧA7UMzU{lBDQMc>Pހ礦yB!8 : AuN:khϼ kz/vsCtXgNGAHo m"6tXsOhypFyOt_֮ ^KWh#vS/<眻^wc'/_޳lwbWﵳxg6?p9̊ >Ȼ)/_*O;}5hp~o+e>=7K.^??Y ~~8}c( 5.(2SZ  k.AVT;}ˇӝ5Fs \Ǽ?f8h~M9F%hy)[AsY[W.~#W|![)xΘOv:2ϻ~c+Ր7d#~Wr/k%{u5p^?~o9Q{+mww~^G΁C't߿N~hZ:,ƻ."nnx(<ڵ>\x~oaGPw>z8 . p;.o *k::9 /~g>|_.yJFl"627r5O}`&Pr%ǰ*uM25v{5:?:;v2;-Zz08)*R/{&r4FXy% svI׌ja^Ijq?sS}:dM՝0Z`k:^Sea&,"Oz7؊? UV6v?^vkwF?È??wܓ_<7?!Ns/9_ayo|3E7a߉pmHk/¯=gq4p""{aC~{<Fvxpg0<N?tػwoW0=n3oC0Ǒ`7 r~@. "`Yuۛ ]i]ܡ7uu~k௫ eM^Հ@g"CQN{Mz9uIԨ3[իs~.;(F{A7ΪԜz@7,-BSFa{~WA㳟G{~yً>3uE)_X?7č\ '3r*WϾ7+3wN}f6l:}\an8Kwyf?A{ްr__ƨKA(NB_u7x YW#qG}m=vs;Ƚ |~7wnp죿 Fq)Ww<߀l47o1qLh7{Č:1x3gkTkR&aVQk  ~!:~ho_Ź/ňFcoy¨#=ӟk_`3԰3}a ,U0< ngG|wWπӞq:㞄 Ns8E 7= /?x{z׾異Oyiմ#gi it>e{é^p'u'B?;{ W/ r\PgDzf ïQxWZ]7P:6'gV=]XfN6FXy9+`NC| ܳƜgrY?c_Tm@ZSo\U--=[ "!^ ƸBh=瞬w&fwo`r ˹4~_BG\~]ygN^?wG/ҟx&{ÍO?x__^/ʅnlƒ9ۿ x?1{0s&7r, wvBϹ|Nw߁{^w;~?權`Vվկ9fvLc]aLosg{zez=/9ߐb=|-CYG_C!"F]¸ "]ȋ8Ou s W|<#oF~ }u@{j .c'#35G/%MqX̏ڡ;g^vw4 OPW_ )Fp(!98CGd|ҍpp!jRxʏ{y/qBFr>ֻ&c)hWoQ>A@{ACb 4D{%\l8 VWWE =0ex&3cb 9no*0zUy^tr{9Bs>}3U9Ğ`nh ;>Yvww݇_.쮟;iw8 Q~z @7wW_? >psw2&VT 71N!>MN~c왝~ΐ2E&xV0% ?œ:g 0, ҧgf;~ȰsMy y9 r_rUN&:oG@<9/)?}]CKK|X#p+w݇_t <rl殟; q|w|C=''kx>-'9O;Yo\~9k0FvHp8z?^9΅ ?D:ANaWzqgG=FߺbE#ի@f?pȋhgQ071jG7 +~X z09J=zn!zw9f >:zTXr՜Ɖϣg30ass|W4|\P:N;~{lww﫿o;{qod>pUWP}(<D[v7 LvF㈭RW)n"z&]B# 9B=cDñ9cR)|zz7 J!+^9Gu3kM :~=rz1ިT)FG3k:X1d`VJDkڣ5XX#vtϺ;r˗c5L!iEK\5Fϗ׬]~~PfZ`_e+~1mJzcГ0zƯ9Av<(ztjUp' wz ؓGwd(p]ko&CZ|Mr:.{~#bNPg;~qs;ww9~mJD5166=jYCx-F vV5WU~yI'siqT=kt@LtMHICc`5Ԛ8A˙1G6B_#h^c5Okd7o9jC|>Bަ&$Rkc)0ҵX a i-fc{v>nW;}ov~ y?w8~~G~ gY=B彀aw~s4.Ï۰gDN5}"~o]"#9ef'M α78ɚ)͏Y]'Á]?wH~ElcأA5oqH=DK9]QԪ¯8'Q[T5T gs5@ ep=՜s>ֱ!ZhȮ[˺:zaw5'utά}L lukCVLw=Woޠȃ9߬A2i 9\O6sҳuj}?{;?#3v_A7C 0&bau~eVT:kq.%=%瓹^#l% /!1Z8My:z)t]`p$݈Ҡ4z]g\+,P MAJM! `G9́QπO}GE1b]u4ڑCzE ?8ĚAqDku.1yY,?_6utkDZ5EƳ?1?w8~~G~G=i5gSDФ E.soG͛=7:01oH>_ȻcBa!O pAjh]ͥ\yd8KrNTcfӪ7J_PQ苵Gs<(b}zNat(9Bon躘HL4n˫\ߡ\\^[&:YXB\6!CsF4gZo)g? ?w8~~G~}cW)("h̪<Қf[ χYgQoOq^\5]GW&:2R%UoayH";}띾~QwĹB@ȑj"iYCtG\} ~usӹME߆C]#G>; .}Ϭv<@ik}9e[ʛ~wO~~pߑ߷?|!9"h#X"9bIz]s4rY)pHW|n?1RWs|˺{`|=DiDz c,5c0y]":M\17|J5O\ZMȫSy ?^) uA 㯟Δ3<tT5/%=o g" 82(&ˑ:y1| ~BԵvs;w$[ ?~Z\v(9qE;lqM00zAA&ĩFZOk{|Zu&8UM2ik]U A,gB1H=߷r/JJ\#azg4s`5_dBޤ|"I=1=-kI?A.ݼ }v&9/Xt xGIOj-:> h ] { Q2s(07Wo9~w;?#{:D{ϫ}?k2/їCORDpDE0{:Dk竿9;:actI??{~ϻcz2\G3Lm1ԋ`T!tQٯ"Aos#9]D51n,GyIsLC p ą=_#MyM]ڎbM7یqè$J>Rr{ӎ_ ^9lf @fZoo%C/!35/]Z\)p%Yɸq^0(oֺg r GwsQ߅ew:s ox/|3>?Ey`(yA rs^ GCsӣݾ3K ?a?oCԂTx(U dWP@h Hz o-]Ψh׭]+c#cXyTȳ zX=W~F?w;p&qgg/} 8{ vu7cN;L8S;Lž3߻{믇ུoyܮߑ9w7__7M {Zt+!)~9l$댸:滤I>9i\us(#6C9rɻ:[.Yŵku|ȗs\!7ad0f-sv5=hylhH>hP[Y:kt~۷QG05/QEk?7hG"1O kSJ=#q-?0^u;Zk1<ߋ^~mt 9{ggzwv?>ջ?vKۦGB22#+ؗO#9nh\ Ǹl  D!o uwX3x]un VňB FK$vp sCkQ͹D+4U{ּ-'9MًA]8q5>kef8{|Oسj_`V~ >!w;^vm fgwqc;b}_g"yc>spv= of/_Í@J#r]_ڭ uOw ]_AznnQ (s=a=Guǟyxd`ì\<\ǦGQFۺfS#ܛ;w89/4o*-u-]?ˣ~9:_mj>͂v :0Z'C;03[+qb0瘟kL彴[j뽶7 ;=GA_(Ev$ qGy۱^'~%>bGYz+A#u~19^W}EAK`W!,hj&9BoB_'[@M =k!29bnCT5{ YCN;[ous;w/Wp9zyC ?#sƍ=kI/k#*M8_أ\k3'(r=^T-֜\aX GZ2:4xT{9ũ?4e/렰r 7 y]k8LV~I=/#8ps;"{aYˬz9_ꕙ&-A{f7G[]$=;~]ԭ{?5.8%uW" wT3 oL { @.SeDa>8繰_VA4$'qɯ# ({- y7^w7(ZÏ >?w8q7nWWъg9s|̃'~~0Ks<  ~A!);~ڧ5ysa-k?o.o=3(^Y1"֜1Қ@S yKssvBcu=4s~y,ӑ.Ei7l2drkhRr\ڻm\=8bΊ~.ϺIGpsn;s၏<ynk'^It߽< vmz=/ - zMFަ gюy7o[f<:_ϟ3s^;jz=Wy!HAwɼD牰GRxѠ]1U}Eߩv R:Ku5O4fz>~?<}4Rj'?˒~=_[@`X5H8q{W?w8qm7«߰C|k~+_{t$ށߟz%d){Y*V<]S{ cR rތN} ACo ⎰'kVݯBvt{-z%ԸӛXXVkh!b1mGvߚ7o{w#QK [SQd B#,u}3'^_a+xenaMGpzFY | Z?\ч4t|SG?]uwO{5~a| 7Zziͩ`p'=}g#~8 w7n ?%K_2+oz,Cp|u]lI+n簥<}~oyλvK:1Ϋkʅc2_5~$t]"wn&-/i_ڒq?kW4k>ƞQF5~/{TEǷB_b9Ee$!K}.ÑWVQM? Xr][3\ܡk[;}|mߨ>{'7_]4q'_l8ڄÃ~1sdpMxޜBՀЏ bW6߸.9w[vIp\^kpvw?.vϺa/91o>}.޻L7Oziֹ?# ?yǯCY,@3=s'u<\psu`Pùv]^G- xv/e ŶQA^ 9]5k }`%􆈸aa3(uTad&*˨ͼY}k.>fissQ g3ʵ!;[t"<4&_K^ևn7k@P? loN3|@f}'sן!}PЂ_}p ts/ؿzAiA.G~lBPYkgtx YG`ǵ炽O\!ڥBA'zGk+yMn-`Wm+y<nr ȅ9_V{uN} F][qУzPC?] z j_ AG^ ,.9 GUoyny~'=sqT=ޟ cX{0y 6c6;޼.=_n܃Cpǿ͠܅;OwW]|])ols!yp; q? Lvm1;G7#utRn6g:BFQS̫A_T&ꪸMm)(F ?Q.(悻A;8z2 v\w@!}Au ?m!*ي|,i[@߳vG%ڟOM7ka6o:xLqv jy>'p\qx۹zwNM1em/ ?^]neK\w3_AI$qM3}@λ| ,w4F fi% Ary/׹>:%.B3)2Gu<0H 3\Y5$$#vM}-z oJr/5z洯D;'vqsQ?cN7wx> 8_ς> O[.PQ]Csޞ|e;x^2tNz~Ǭvg:}~}P[99Z?Ob؜ZvbBӜk gRs_Gk@q5~p5ZZv \11Kv-պ>B]CA7Hknb8⺴n>îvbT5 ҫQWZ+,n׷-e6uS'~_psQ[~+p=bʻ !蝷Sd31|W G sszwOcc17ruh3WG_0Lw ;B]b (fҷz)}H(`Ua`V<  :%{醎y._ygTsv~l?{o,<1AW|+ v?^; >=G/^qgEIK;GE`ck/͉oCwyԧ]?Zk{Y#|E.2dzz |=Q~s^3ar7sTgI$qz5AoޯG:G` (}b ~@W5K>-B[?6M}'oU"?w8{p? ~ps\_^guEQb/At!8t/BЅNrN1F "z ~ |tw`&z[ٌJg \zT4ֽi qzیoiDK ,z_;~ \7jX ~Fۧ!^ev<3?PGubQuGϠ/XR0g@wb ?_c(úe/^B%F]ҙ<+O>A?w8~~1NK.ʲ߄׼5l6~Oɯ1>G+5ecMG7d!"n8p㝾.9se3G y%#hn0OG9Nkv.mMZ?/l?g?qL=iji^iۅ|j9 9K M J+km;~c鳢ݾn<׵vw6k>w89[ǕW^ |+_ {f~{ku9B]5T=k^$Ѡ˸؁ytd^eHɲ`Q,(_hhC.`.^+u%b^3ysq+f2G80o VO4OtG6`82N"'uob9x>rF)s3vޠ@/- Z#Թksv#أk{MOsIW~I=5rwwGמQ U~;/A^;V)BOI'v;u#}ޥ,`qpGI\kNg}Q)'3~shy$Vp9 $Qe;`!E9^שzTgewz@fO-y;q}]f>`^D_+1Rބ׳׭'(Nq~O5W:nײַ9~|ѿǘ|ĥZ4` nq2|ӊGq@Xփ35'Wji=Q{9]}=q\)b̛8 I1]#z3RoIt8ƉQ"1Iݑ5:oy!wZ8/=/{R ?_QG] ~=FX'34Y#c5N#W}~pߑϟ~;~o-J7<3lt`M70EM Ơ4xPM}2!k vЗiיQGeF7Ü-c|5 82fGMonZbίldAK1kq=bn氟c"d28jomf |~?S3,j:_Mii{? W_s;wDw>Î~i4|ÎFPk|XY2f!/1 gOڳi22}Gmu1bG2GĝT0w4G5:wfCC51#"ܡniUUѪ`I+ rgo 4-om+DDD~}~v}F K@,C]cc&j$U`H/( ~.$.6 !ȡ>Bj1NbMkcy; L:ݨ6A^Ā|T-?A G+Ʈ҉\w gc7vZrԩt|\9 !m]=;* " tGwk0((̵-A϶rSj|G`mI @sZo|lsSg,:s{hc*V&6yY#v& wC>VOʜJ!#CX[y:߂lBq7ۤt-6~S{j:@@}.|<0*[Rr 8?% be HA\!/D,¡*s~3~{۱}>#,Uz&k E+M觛Zm_UgECmwk~9[OD\8Q`W.˜tC0U~j|=e8)c[@cXV5q]2l#C858WjK2 X(@!ڱjuAOZN|nrm]q! iw\scwЎ?ߡ=P$ _T:㲫w]A`8,A4~[ůWODO ~!'wmo-:|Aނ?Am`or)L)F=u}u &lO:2A뚄 ԍy%]U&a 71v hSMk1\!*.084AN[oi(VP`.V~гrVހߢV5_-h~r)su~COD:=Er2TG?G ٌJ q *6~ eckWW|'] R@GWPw}W`I4*Ր7$TScLڠ˸疠 _Aߦpd7 E47cHONs} xUOtVնx~SODoZg@u?c3s+E8 ̝_18Ev} r*suV\X_k5~G`m1*<j`SO7Pe sdQ)OSy24/Ak"9y[Z:|crz> /(ihlaE:t ~̐iRΑ1`c~W5rA erC˟W? m=ܲw+oC Z-qTv [@BP6])7A?V֪hO<}뒀_wM7736w|SgjVmfi87˂s+KpaYwÜϼz%X ܪl\{xhۍm?h)'sۉ?SO&vm)}WW!6FB$ s yo':T=7Ѝ9>lqWvβrAY@ $޿.œcjPm(㺭sy}L }Qѧ\WcuMWcs'?} mYCD& c {N^my<"^#K`)Ǹ{L3m}kT}b.e!\ׇC>Ye]_Ao'Ίa uhhXŨ)v! 5>+]xSq[uhCoR[*1n{UL-HIn2 9&$:<;1yUo YeN1͜UvG9xmHvvFV+MIykLպE v_Re&0N8`-|[ד{w5gMyso:iqToc>J`3WgL\੣ YǰouTcV"4yW/; ݐgf@wiް>9rwvYZPY*΂A~/l@VO]SL E"MEU>`ӹVd }JEcbWx4e͵<9Ju[nCkAkPڊ2L֌c-P/PY8/[}PsO8~V}W9ϝ|~"~ &A%Ozci݅^-=Gc_/UzɣϬi1m2Qǝg_ k!jɱܯbX'sč:*f1s./s !?_8@ °kc*"-?EJbm#%T>1ߪ!:pq0%dTlʹHթ9?NmU9{DDD$s<ZoAi8ʃ>;ro\ i%Ӿ3ޝf Y.(kGG:y ~-MJR u)`Ֆ*-WJЧ5e[Χ,w+-~NK#$%?/"4aƚuZ]UpgorA#s\R c"@go^ۤX!Бi6רqB@#h1tP^SM> ~q`@]JQP ԵuC-!`6Z~ɏS񤀟~'n ~Ý+'&\!̩LJaNЄ.2Ba]v|(U>J. dYy^^Q -O5%D]B`1_=FŬF\R &Gk)+`.sYL\6ؑ֞B)WX3j2m#&h F'[wXd:gn[#_ ~ EO*ЫPi '0N~C6pǨ0Ƽtp_{1$"xӛ> ׾5{K}t/'~ AM-mоǫ*f:&n"=g;a 19,$j $ 'O?Zo9?Z8gq\a#M{Gw&VXf!M=0mM`ͺ.V#q1 eWOIJ'vGnem,V/j_p_ usoߵ -D;'?I>w~mn"m짗'alQF=VLhT/Vk1cUA?{`:bK=c*#h/PPÑ_ $9^!nl!"Ü[O1{:v0է vmum31SL] [ s0ɹ}/6ql6Fߎ?<$ڱU4jFafX7Rs=nI>}1?O4a8h/𳗎uZnv{o;|`co6A!+7:~jr٦l:N< :@z}*Gcoj۽…tТw0G@)F6G@/2ِw:~oN xJsq7Gӱ? *45s ;~ vm w[enрßqA8zI֡fEcc(tbo.,#k.g M}Tߋ~"~/ Dc][^{YOV-|qvǏz ȶ`Ndub\W1Tt'[T/P5n t# tG<فow599 rǂ>qk4l@ g4]`~Oцg`WӾDZ=3Ƶ>~z; z~ mk]1(DWzr8gcKKM/GX:x p9rj 0*s~"_+?g``aX=z։fu|sp !G/<.VSp:cmum:| ۖwe[}S'SO}p#Vݡ `5</|ݴd}}h~7/<絳/Cx"<(w ML~Q5bƹ[i?.cu]=m11%۝@Gvkx5Чyح_sLsN痗^:z/k|M| ]?d0(c|wjq?' qv1yIVVk4~JODc2 siգ Xsa)Wv*XnB9DS *LBUN+PS?|+V5ɶm?bn&^k[ Iu0#+CQ>4 T7/_J~"~ם)K0a35(Z;B9єPz5YV׫)x/h\g{&qp5s~~S7:HT5 _4b+wt`k;%J>5TǡlcRS{tvSW V1U̇@|:_4S]zpT{ ^; ~k&ޅcscFmhۮ/0GżlЎ DkҚ:|d'S}>{=@e5qW! AjMZ5k)Z1_|dz w |ʘǶAЋO2 QÎ,$:x/}%ثxu~O'"w}SgʴfQW{ wj/a5mꘟ~srU_mzVw%/3;` -~~/t??/Y*uBY6.A\Qcxa {f|Aq@RMy>nC?G@Wx省?SǀAfP8vbF $䔯V#e8 qRa ↮9%Qz~ -[`w&<_R } TЍG sA7Bwc#]3jI(3ӸEArß?S~*'"'""'/K@LDa:t`K.,md #@ A@H+d0CSdr:߉) og8^ 17?%C#:=m)Yl5GG 첡6߱j-P CK-b/mW9a/RNJכhsN56kkL?&YODODDOo5l##7p=囘QLe@x99mىIp/ٳBLc!*uˆO1-˫ie0Pc'hq qd1 꾫0 zsQ@Gvc3qU|/ȡ(\mʑa H]g5f[Sxs4.wG1sz_ ;_~ z>T>YiW~ jw`T7ik<‰$e^>‘֨& {T@¶ >l;vl)€ DA^AP7RZhd[1Z w^ \<@sT'[յo -Qe Hi?P渦s~W"}EODODDOo?_>;\ rwV79~ڼB(C3u,?d3$f w&!)jϒ u2V3pv2-ge:7P9~Ҷ>[ch(.My9݂a:XUjxaΎGl)dfbPuvʟ   ~> Cm˶V`VH3)U`v`dG? }`8F"EBqHmb8}1pVVN=T9O}66E{sjl}=: ~c# ;OU @H遶F)ʃe`o3m0ԑƖo:nGz7!@_zуs''"'""'_!u&6c8 б6씀gQY5Їg|Ώ1oh30VVc ZJwsKM>5NҰ: ~PWЉJv j_AQD7a1r Ǖ:Vj s W]v_@~>܎_aa.VW? v ]QXA zv3caZ\oc~X 7:!W g؆&GCic Pw 7ӷrn;x=X'~U^F虽~6?ί?=;v{pe-'Ku?`o2_ /vDns._FGV##y~K*'SOzٵZ4may XZ#qO+>4g6Վ7oCkma8[w> )k5_ùLyMpۯ2e]d? ~C}5z~po@~p'''b_gZK}L`t̀ϹpW"P5K,+ysk,b BX+6T)ڵ_y,oj w]S35gֱk W} ZM/B= ^/?ua m:@/-"~ kG`C֦iӔ͜E܏}\ CN45uN4u }\y^͹O.pk@Nu9𻪎ނ-/^,~;B&Qn (\]~W"]5]q ~~Ss/ )G9:uMgBNZ>-Wt 0{vfcQޢsi}U]8@Ktm5q }FEQgYԤPk/7(AطSl!}WODo #;rh4 `e!xsX;ptyU?Wzsnl}?V|qDDDD^gw/ 4_, J d x վ1^_y^^4N*_6]+ ܘ'"bUKE駟w]gg? I=8~q|AxGpmɱ!_&t]XYY;COODODOf .?i_5?G׽u{vt:‡?a>gǦd^߷-? i뺬{!7ð>я_zxի^%''"'"'w}x;<loo98Mu!1,--1%Q3 k_Znh3gwɰ= DDnv{&/2c{߼з+?{%?п/AE{/կ>G>~߆??)O}Sx_'><>s~;DDDn ˲ ~aGӼA @qh B{-={Ύ^f^#_EpOMS4M?z|U+Dn%''"""'r3_c r [˰v]J{.+]\Y  ܰK*Ӻu)8T>qhtOYyX}^z}!;|1`{(M0_g]g69cp]E8zN=5yƽ!q݇huZ='//H tG*N4 w [w(XCHdίvP$cKXPtuiKS>ffmG|ޟPJS+P.6'R vI_>B_Sxs׼9 A{POODDDO&6z/ջ_+ӝGXm[BMwsyv<^u.с\A'B}+ټf7Ιn󯺁w> ~i~;B]5ghuXwSXSYͩx]ubs߀ߋ#vG6kXY~:n 8Zl㡓WڮgV*@;tt,`#?~1T5X{b&4_`϶\!i^7kr wӧ뛝~`* ôiDDDDDy›v ܷ6S8~"~""~rGDD䆈  toss9"7DD&gy<('"'"w looɥy~"."w;,""bo㉌?>Us)t6d"HVdXNh'i9ci%Z %O9cI\yqR,d(yJr\(xx$'' .VtBV6y0&\\f`ȴÇk9iii;!32|k%W_OMˤRC5>BDd(y26YYYI ⻿&׋cN|jҤIh f(1=S+jNޙ1Bw.q'_MY!/f|*:TS+L%RRZ'*`wL@uL`uTPud?[ @X5kִCo;9)y)_/qtK Rpl,I۔CTJuXej%tkud ٮ9wP-̜;J~/obMdkdm'M] nޤϬY}rԓ/|(E'mSS)kfR+tڈс֮ 7αs31ʿ7}jP哷<=n8ЦY-ZewboO\\Qx88ҕ&ۖ%ʧRMRb_qE]ǻ{,2yO 0 4-ZQ[N5S'MN$Tf/$˧J!16UDJJʽ,]qw0/=~F=zvEd=#gޛ4mSTZ ո G:aHM1b䈠*]'ͼkT>3+qWqo뿒P`{ﶹw(/2 /(duv'˞JNnOQu[f$Ac}ڴi/j~ 0RY'Ksk\JD Cb  QQ`cK';활U=))n^W9gLAUK_LLL\3|+=p֝z:Rgv!VΊlwFYbvJ(Ҟ>r뷯tӡ%&z_Q4@K-񃀏߿?uIII#ȠV P.J'BڸWq8gY)էVB' F&Dݍl,8~an3wN?y`'7FZk|6͇'no=8.IEeGe~6b917ٜreu>bN`\攁Ϩߞm/*n&[%܌},m|؜7E/:`[91E*8@'B!O۷/u[0Pi(.uҔ;웸_=(KETZ (/>\' %خ 8i w.uAmaGL(1Tch[4/uPY&0s{};!}d 2mJ_-uި6Qeȑ( k|ADDi3PHN蚩4;9CO?xj$S+R^Ǵ+֔9 [B6sy>z't`U9.B0!OlhFM2{pɃa}=Us8 Q }Ö[;d9c:7ذ8HVowp ߄aV%u⷇Cآ2%QT jcb'-2VɵOѴy2Q훗>:aHdX@UxI擱A0co1(;|S>gq︸8I,Тjpոhnq4eoQpa~(t߂m6V1l۶-K|̓JW9.V0c'zM|$d 8A 4iX'wųZS'^+z{, C CX"M&ϕuJjAYb#7i :S1V¨}aPiYˊτJݰmM.}!gmCC{{ضM C/wnQ9ĉC2e ψKe2{̹%$읽[JD~n2Ik670`̠lkPaJڧvE%w3Ӎb/s?UbBC,'渋hT\U%yeŧ48*EwmyٿJ)ߵ VUHBISHG\c!w Q+j}':bFCFBämd۲DTx@Ncc>6$`gXnӮ]=OܵwxC۫OdhTTT6DcD)BBBfϞ=P+jN~Y⒩f_X2K/g~dʿf_2ܒIդJ!16Ac=++k-dd 517V|ϥy\Zp~"IsJ:ọJ!16cѷo_gqKԪ=:co['>cOfB/널k@<oXh6a'A/Uz Cbl8@Xthl==ZK.c:XX`,`,0c0  X`,ƮxFƞhjjw=TWWYUt6l8u UDm@ Ifk`lqAc#16t&coy0V+0TUUXvcdL䆟{`AZWp+K R a,]#cbV t KkOH+9r0thlAWjEe*V)׻AeNsoڞcEKدyQe o,ϑιiL&yMC9/ugl,wNx*('gփRM;\tyVІQ,V7g&Ul ڒgŲ1v8eI=Nk:ϲ7f>_)}jOi]ϱϵ{..» ܍-(L7C+v+2V[crwG﮸#l#Xc5c~]!5]]q̱xmcwz1߮hœvc '-ݩ8Hc ̱0X̱0"Y17O.E ca,Hf6 ca,0Xmca,0Y̆9s,KX ca,xca,0X caX;ca,b c5jf;T X)-//l͛7T_+׋4vXخ666^t+W\zڵk?$_~7nX ]׶+h0Ve7V܁~c!ֶ0֏Xca,0XmcaϊuQ׶+hoa,Aca,0X$ ca,0X c`,0X ca,E0X ca,v ̱c1b0X ca,0X ca,0X ca,0X ca,1VݧbmG(0Vlbw+֛^ێ^_bXŻb `-p!cmGtcERwK|+e%RWcj0ӳb]zӸkq+eYC?`xa;ǘkUm,m`,Aca,0X$ ca,0X c`,0X ca,E0X ca,v`m;̱c1X ca,H0X ca,0"X ca,0Xca,5v_=5gֶakXյR :\ێӕ}|8tWL&Ymcala{m;XƪͱFX?0ka<+mV4 c%+e;Uα:[nUxDLs,֤gźA׶,oX655Ѐ߻wU׬Y*X c; 6:uT*X c;XX c5a-/0jXcXcx2m"΁0jZZ' caVGPT̀0jcNQY:&Xct=*Ebm;(ˤ=nX!c}ud FXE`,0Xca,56 0%+`OhX@;c XX`,`,0c0 hlll2:#kuuuIIɄ RETPP@Qآk5^(:Og0VƦ?W)(:Og0VƦk|X(:Og0^hȁKq뇁X#{fճo4ťnc`}նcAQ\a0WTd,("KXMߴO0^85ڇ놙 [w‘غvmw~) _R7WaϏF0os/XA_;4lqǫ&LioU7so*yYϵֳֆ8*ҡƢtc5vRp|,;| m9|CˏJ>*0|jƶ)pbQWs֣.hYKVOtΛF0X8Y5dȪ#ojUI^K(sۦs lۖ5OΌz0ƪC.trc5^uΡ3OOʺ@|n:4ՃWsM+udJ}ߔnlޔ^fWhD9W3^>-na{ .<>[6H~י5h>G&"Kض.3(; S-j5"ka]TYg,(!a0֬Lhؓ/ڟ=SjVP7\F0tYiŹibԍa=Yj>Ƃ":جncuolzz>KO#$R ? ؼ2K̙3=럁X[]]]\\Loib„ ߥ臁X}+Xt`,c0c <4Xq *`KQIENDB`spyder-2.3.8/doc/images/projectexplorer.png0000664000000000000000000004600312626055322017507 0ustar rootrootPNG  IHDR1KŎKIDATx\{OrLrcDD1XQQXb+&"6DD w( E)"U@V" s2}gm0;3}! wBH4 @Cd'kkdolZh ʫjjf4ei4 ǯl00 ;_';(H [ʧ)fx.YSdHv:^*^$ŰTєgAcp7O ~˒cQ|[[%9EJ"C~eb/|eҳ /h5O;M&N&uD[ ,nސ_U&aJsh rY z3Z0r8'35O~e}s4j@qy[Jˎ&YDV`(gG8fh19 MAuah  04@4 @@4 @@4#jBs\Ç%%%FFFFGxx ~$@<A?8@褤$nhdgI~څ555 3_>볩hž]'-cne7Wz 7[ɌBhd֌`4&555""ʕ+xMnh}ϝEt>3zۃk7]xsN>ɡ1,!b@@~L+:v97OI/ H|Mz`MTXFsp|[|!wy7Ն0:0h//mt^pwh9a @C 9 E<=mz;d0uKI=V M2@i`9,.͸v+n:X1fh4W@t28-SuhA4ggg G]csFDB8ܗNGbu')=XX&++trqf!qT h ‹>b^'Nz[rCtFGn!zذzF Ә6ahq'OиE\V3IEDHw\un1&τQxqMQ˗ڐ<|KG)_3h4zFA?O/b夻;QpE,;rY5'VC4OzF /z{\CC톆nHnUyg,;%bτ3K3EŹ!iҔgdd>tiWW4b1F\S+Ndτi_VFq\\6b Q5H'vn^RW#u֞uu@Z~~۷Fow䝱%ewAK@iƭʪ{(? ( o]? (? (OЀ43i04 ixi62g/j 4iiyFDHn|!3 3 ~~ ^&s0`a6`ᆆiy23 ~~А'60B(~uS*4jOCڜ4f? @~F~P޺~P~P'? :Ɏ,Wi4Oß44Y*Z~f' aVq4fD+>,/5XҨavҐg) ~ /o La~ z,ciJo,Ti4OC_M*  ? S|FoJ? ӆb9[FSÅh4ܧo~Aa2ӀF? ( gK{9ѩ!{ P5J\̨M(jʺz,FZZ]H)jwbuk@JCk׮w+iBi̬w.-{OHIIC[Pm:]aHh xWWW !ęuOot:G1OhiPO~>}u)DS2XgR,"ɽ \%yҨ64 QCP~:4!9w#o79 CqיO)4 Qd%CF i>iiLu$Ρ0|ya;g744\ kmvqrݢD)adW!d&Ã`@E~i#6d8G+Z mk-kn_މn7_(dptR\r]"!?Mff&4jbWg) ~|FiQǧ]۱3%Bh$"[%CclkL5ӜvW|ea4O_vt`q?l@s8u5nʐńDԧ1n˚87'~_﮸NܱHML֠ E jh?#/̰c?yȩ:~q|S;6N '42h$E;M.%`.b=H44WQ{o~xWY cXfnKwAnP={n/dJcZQ+kE4&4qkӁQBFߵ5H2#i&&// 0t4[JcZQI+kEqky2 w(XC,diaT͓qnƈrۀ@nPi@ix i@ii@ijE4>OC ~ӷCXOЀw(RUx$aRiQ]1gbBFJc? Q|P4ViW.htO?Jh졊kSK4T}iT5O 5Y<4ƗfOC3ړ? ( i@i+ZӘYiF(4ƇiTg`i@iOOC.Řc[YɅ~=(4PМ4Pj&i4ڭ' ? ~YYG` ~})~ZQpCa-o ~})~ZQ4q!71Nv?^F뵢Ti(š7Z FE~#Ok N(~!rF~P`~P~P~P~P4[F[Ʊ6 aEF>7? fOЀOC aVz5yADQ EC$zO`UĆ C]4L UiJ1b#r"NK}&P1T!4H*@Ik,Ti !H[CHMHQ540TQ. "i OCOh3f4ꝫ ?@iQ\Ӏh:ӘN 1}? ?ihOʇl{qt$*΀Ҩ0[%0Yg@iT@cH?Z,:JCixOCCC: ( haC#rG`Yg@i?OfO#OЀ4444i@ii@ii@iӘZ74? A0Өاt? -%~y_^ePw}pO?ReiMl#{XKXԇ 4zQ`>QS'Z)O?5ɑmr%dO'tO?ۏV4&6COk'z*ѧ 4zӐҊ(6I9>1v7}p`4ƗfOcU`oFsOM4ˡ ,BCC###cccр u+%%Gbу \ȼ4HqޞNK44111%%%mmmQ]]]__>OboX Ѐ(GCRQQ3fό]qQ꾠}FHQ(fåe+\-fNr=Fu.fggV R\\~,<.g9r&+ƴ AM Z6xK7|:9#~=v|)4S09t'%W)PmYYYUUUcBz!.=4|ទl7q :,gFЈb g)Pܰ#.. p W{8T@X4@]eRPiJh jS4=7::ijjB|QWBL K싕 A7*G)PiFO~ŪgbMZqZ?R6J ***jnnF-xOnjfceQXKH_M:… ':> 1VXy,k0f=~{@Wv)P^qҥBԛLh6ᎎCJ |M DLdd_ZZ@J# Ӏ>44445F>N2o$ }i0#tJc~Q649Kd!Y϶ff.aOPMOC eOm4IOZC 4̀ҨF~ʃ85BkH֧ (j 45Q4Jih44445Xk? itƗ1n? @c? 5dT!faE1P6?峪^(ORVI7l4 Iς9i՚仕zC6$2h> ʕU2`! - '40Uk,L_9 %N4Я1Ai Q  [A <"V.N Ta>4P|1?Ng|.i@ii@i ~P~PB'Pc8]~4~ZNw 3mePI81nW +Ҩ͍>Ө?38xyeP? !yeP~nhvg ye54zp?S[81nLW>?1m@iL$? @cr~P炟FpF|.:p И5? @~%J2/4 BQ"P4z(D47 E@OQy*<֊@OQIhZ WhOQ\5*J_? L2#BQ,J+cI&EP=iXIWF#F݉)))m=h?j|}}===ѧsС޽@Z$9@Wbqmm-ЀHB]hG禡 dQp긠>oH'6{jlJJ PSs1"X$*rE(G-@gS׹hP{?_ s_$*Bs]$P4O& (Nu_.gN;mfޛEc狎~buK$bE"gICZBPa'Js;'bSt~?En}N/45Es"geaȭ' }(piX:>m1QOE;oݺhe/Hݻwoǎ8i0Bd%4Z?w}M8Ƴvlڱ)^&J81[l4 ]as$29hn68H?i/`k7'^_sJN7mޝ42~Td4/ VQH4ϷFeC';zNr[DݬP漴ssZ$:!qtJ!`t< b>!\;#b>ܿnoʿO\v7y&H!fFN]N:wtv?cj,}Gy>ӽ{H$EDѡ1xsg{ٛ^c_Y 8(R$"m|Duth+@ixE$Zbٶ}_YjeVjܖ~V$+~~D3PZ[|]mx`H%4dtk-5l}x=F{vp{wU6}? Zl쉆D>6G&e,y҃ r~qGkhie}v ^^ٽs5OL՗85d S]I@Y(!ƹ|ooz}uV=ڂ=wȭ;{^4{?]0igC냾ǡvi/-k㉞/9l X>zE䰄'PΞ3mo/-woE2@cJxxظSrr2sb&4ׯ߾}޽{?ZD HMMh@i A"}.h}.eeeugii)@JGHi  bp]|(v>Rг:KNNi) !P Q|+yv/~h@iw-9 ǁXA<ĥBgW @cJzHi> 闘g>oNvy΋H[%.xeK =/@k'25%%LnWC m6O(Fq|!.RyeFK1$vSUUf_Q3RHI)'N/NHnrR+ax BH+4Q4i***PofESyV\ .C\Z|s8}8e8q G7U~gBi"##G}( N[C.Mµ[7ĩs ?͓VJy­%v3ZBh]d6`QQ4| Eiï]VPPP* FO;bB԰hKjhC~kK+:4h\у҄OPC)...**pS8o8k2]4< |p+Ut !!!/_F !An*q8e,Ztʠ4Bᒑ.%''',, уx;X{AMu.^xUXs!? 8Qg!? -[,\pŎqqqMMM֭{Uh'4;~53噁L9'Θ0eʔB @J#Ǐ]9`:]7C}5}ЀW(û߽/ƛ= \zhOc1y_Ͽq8狃ٟG糷}οߙVosGgAi$Wſu8f 8^ v~P;hoYSV̙BW͓4 f˫sq<+ _.w0~փ-/^#oqi ( smSZCL~럖C}-r?<߫W/OOOFa^5'ߦZXTxesw5pGvbLC: DŽ~+CO ]zh=#mFnua6XhP-,^h(W.eOVEPP͸šAҘG_8xA !b`_PÆ+Cǔlp\F455w+!`V7 Ů% <7Lv*CC`:Q) P+*k2gXtxK싕rIe+ãyR q )J @o4eb3P^C9&jae ե̶J+Q 555pS3+Zr~&~43a .ǣo k<ݙe Ҭ'& it.]*,,DL̄aaah@:YFС_g#mJ b"##ps5J5uYJc 5Vc٤m @1L&]Gi4D3 e_R^Ab+F5(P̐/E= }F?JMlUzd dfc1R&Q@c<}Mk6 0SJV7OeYv4Sk6$aRq_$ȡ#lxf&5Z=JC ml ( {@&FyШ3# ~FҀQGi8? (:Oc4*1Fݩ~iDiFW~ 6Qk NhOUY|:ђS`X{z~<#uꀟFK~RX)${N 0:uO%?!+5zia`^S4Zhn1i,WƩ~ӀpiufO^{? @#Xi8? (:Oc+?їB4"+\i5PhH-k#6X0>+OC֤-{ :c Рm@C֤{ W#2XH"hh^-htl> Ri kZS5l9+ ۤ0_h(՚eꇑkJӃ3r^cKT-h4&G( IBxJT44h=i@iԁ44{iHm¤`a%I\d̰xdt~- V'? ry_%L1ٿ NiOC]MZmሕ?#@c^iOiJ(#q_$ a>*w<{94fhb1+YR!1Th˧yB R-䐒hC4i7u@crJClt W(b]4I"qk"RW>Xkԧ|W&.^"QP-Ti{pA n(>c]a>]"^Yt*R2ȦKS>~4Ğ GM Nq$8F4'OC4ӧ#K`b^lS[ii8gmH9j@#!8!wPuF eht4<z/3VCOn ,RiD(ҰƮsw(~=sF( 34]Ljһ4дwvw<݊Ü6:w5CF M{{;¥^AeG]i$-?dD~U㲳G9~3i <ʭ Om^~O|R:̞|Zyіik't{?ǟI(DdUwE7Ͷ[fX455oeo[_z+1+Ӛ*(4'44 }ڞɿB| +Ӎj~|qk:TN8 !tXenh h50KrY寱8Y;VI5N +olioN y]7|b깠@Y58HLDQW..34X1-'wΑ3NqO=|"бă\q`CxUFi7nwt Qh'z 8@iX"**@(@9<<~hQׯ_DO>@iX$114ZZ%$$>2w(5&4jDjjjFfhiiC?vu£;狷FlY҈⨨h$-uubVQ%%i4FLL .Pѓ-,l눤J&5FmaNtvvfgg=:\а2AC4CͲpjz?lJHLlhhP!thCXX.]4aaa_QKFRZv$! y!Orb:::olge$'p!=lhT:25F{UMfSRRwԄp)[zlRB1SJ^h^LNk!PU1g}|`+{NOHy z#g- J'=NQ.!@i i @iT%(Pa<w,A4rhܣGK-tkcev ͽr2mBmE,ZiNV/ݫvəe׳+|+}+,)44RU_~hlk4B㦠&.CCP+,f :8ʼ&\^jm,NivD<xu9u<+ ؾ302X  \a,ĸ\\Iq0Lw#Liph\3e#wϛx6Պw`yؚ! dA(+ 9?\Cl&mݛKDPIlb]̟3aⴡ{O+oϲ[j”=3/94t犆p6HP򨃎i1zꛯ?o|n=`5LDAY \a,D1!gjACc/COJm\&8l>NZ4aO&ZO / kƎ?4t[AViph7 Xpr0֎vѦV!@i -Fhhhxxxdddtttlll|||bbbRRRjjkRRR @iHpqFeeekkciBJ45I/|j-!߬mHM6ДN縧}ܘEK`4vSݰx -SfAJYYYUUU譢_GfSCpH[Q`uX 3_=}}}߅nF#P~~84hB#\q8Yy!D5X, -  sZZhɐâf,Iiy&ԛu~%Ĵ`Ӱ;c/ڄ6 ]0cI}pEJ$xEe\,y#n `}2W'&.-WƩnԐ:0REق&$$$##<'^[ ͯXL)X_G tcnԐ@˂&((Ph]SSޤX$,֒3 Z66?l EJWhLEiphkjj[mhhscc#64/jfWw)͍/LH ~%$QZ_NPLhx_D@@&-v=Mm Fm5” LkђCK?N4$hoEGFE] HLLIm`R2;͌P z+4jp+VnZZzzN֍oMIHLJƿĩ4dB"c2y'5 @;yiʵ+/^8>*..-+#+;YjP 5 @>MGGGҵkQQQIIIQqWbcC:t K톩%9:(y hpAC&ei#v`%OuØ~FE@iC~}^zJW|Fס;[@< z eHɸ~=ƍwJ Ah\{ ^s` JCwX#@i4:J4hO#8,pB'4Ө@c~zDfM)B 4- ƈ14Ӕb40 ȀP^JM?MBb䂣4t@4B/a34i3א[~6M?  i T@S#rs~]+ ( {҃Ҙ[=i4W!^}at0{#_bl)t0{o = 1zOjXx'E+^|O+IkE'PYhޓ2njb\yb,D=T,BiQ~eB?4O+`|<J# M=1@Clh&=Fh:aE'B䍋=ɡ<Q ?S<Wtv=cPQyJ# m{{E'PYhޓq@cG4P 4P h+ i~FҀXWXO# iБgё C͈34OC0-z3.h+W3iō1cJ/? =C5ǨԮ1J# 4dt/ځ`BS? BocbaJ 4 *1J# 4\:xR%%`q64ZcF ,~ӀҰiubg4 TW=4SN&TÇKJJ=E"...4Jĉh(HtS Ty{d<{ptkz聺 Oh {*rJ'R]PM0 ˻0h;6?ks.xO逡/5P\8R썽;#4h'UAcJ3i̤_;˃v۔|֣Y4₆!,j*SԘ/dhF={z 6nL>L'tIqsV54\hx" Br`LA eWQ?{gl<u{ftWc#^=3X$R \5Q@sS VMB|=F{vp{wU6}? ZlH~D>֌!EB#190yЂSӼEU[{Uk2p8`'\5klf˧.O:1;>&Ww{ms-C|&4aJOL|q_Za5 #580ŞCv?Ckۇ__(Jsfm?Wc hT2TBSQQQ*L "4(TVaA쬑’Ch h h Ԕk9~ /S^'{p:}'.6DcccɑBih;KDH(h /] =qq㜷w@JJ"1ijbWbщ6Ũw}S3>7~h״O =V%ۭLZ"q>!?49 ^~jTCOGǥ#b9i>WmukwڵoAMVV?v Q͏=%{m5|:jylԷǿ4oc~.N{ol=kK)n-Nk×p oibP>?A;=9lcߗG-4||49ٹ^^~g={G)**>ncYoJ*+ko޺SU%nolx'fɢ t hDGX5OLjhvzlk_]_ n2[{,O죋;4%eMWW++.XuĎΩNWm^*>Ɲ qtkH}@t挿c|% Rex>{ 0~֞=]AC oC(4A+|l }=ʲ?:xUƊƽ{PsCĜ:0|6g&۟{ߏBAjV/sm˶^݁euUW؂~9AW;gؚ5?'~ sh(++GqWo9v& c&zhK+)) <]z5Oyyyxۄ7oHC(4kz=Ǐw7}w'Ǎq;&/0~c{ݎ*,+N%ѿ.[,PY3ԟ|HkzV2@,ڗskFcЩAS#6ZsSν07,;L񼂢;e{۲@ccGM]cg|\K+t=?o4AXAZ"(3 c^rpjK>n:`z*_N{|ts*ڶ˰CKФgDŽG'%'t>BB=NܮJϸaE5q4M!w>a#Gjf>u׃c 0;wNdzf!4i:]QQ_\|Lfv]B&4O6333Q' |.;)A.;}}wXÐSo=>`o~ѿGO^B_𼠙ۋ:1LI>trD1\G;j3H?=۷EAܳ? @V[{[ψ MYYK1EnTXxGK!PÄA? 64z)| MnjG>zۣWVѴvo+===sW>UłA`ۋ?; ubߟ~}z qbV,Yhk&| >͝:֝[6#4l7w95r*WhPܸ}Jb굼?uphP^g`'?WN޺$?ӢnD/%g [1 mPW6:zmЪ|:=sj¯#B8tOMf;d3aZW7:}HF¬ϥĤM5֠M- $3i{>`؞_0+밯'~>2?{kol}Յ K"b$,q4Ȩ G⊟N朽}CUKfL'7ujY|w|6JJJJ5uQÄ7Ox[b#'֞ro$]o^[+V<${V|3O;A,h춇I &{Ⱥ 9Li˼? GniQ}Ykx6nN.B> ͅ ay+M&3ӓ\Ejq0~{({q+ sS4?QA??t^aNC4|+W"\^:\{q--+6!=8$uG4QyD`]zUGDXf+41Zh pE2IENDB`spyder-2.3.8/doc/images/variableexplorer-plot.png0000664000000000000000000006151212626055322020604 0ustar rootrootPNG  IHDR~?<4cIDATx\TWwM6ͦMHij0{ר`W,@+XEQʀ+VQD 3ÝFxars;wN%޽4M4Mt9`JObbX?kߧZp]џwKs[yvJV7/o;{o9u,ֿ뒽y/z3Gcįn7'kݴ͵e7k ׫ <,Y+S~@-[<"λ0N[2|>%0oʴu ;Okߵ^Jb\?)[ۿ-;39G֏w]{9~Vc38c[n0&Y[fL|R(eoz3.L#+}gʴ }&v*aLkߧv~v~ޭ.+r;Krn2tΈ?qzZ@)r#y79}neL;2Q:I(j(;@6-'˴} vy [Gϟsk^ko湵3ץrΈ?B;%0Z&7 QjܰM<V&ߙ>B|~}{yP2 xI=8yeZ>MPZN}yW^J;^k{;kZqn|ﺴ9~D m??e-]dǪ^ǗpU?s |[(FK΃U|v-+n{)5[k]A,Kz/s?#~ ~)GTU eWp6=Le2h:2ˉ5{[+跢[%.je,wkr;2{.eZ;9ߵ0 m/%ym,w{Y΃*[em1 X{<6OB;calogR0M 7 kˬiVaG %wBw Z[,_=ڟj>>Rc}ni;*F;mv,V3*:s(vE}m~/%|mDNfP#Kb.LwFJB1@= Q৴PYo/c}0g<B-_ CoVWtBNΈ_AOdYWggZ Sk@(׫'h.+mm۶tp%cI= пPzX\"S?kdďiE׈%zK+vrUf$7iXrqRI Fܡ@ꏓmh.CرġF[lAHHV^[bwk.DDD 22,e$YN`ѷ3/qD‚yVY5t^\Y4MW@gK!Sbʕ+~z ~GDRG%HN;rYYmǯl9#g{2G3&-ZvagF6"R;l‚x =z?ٳgu{L?O ~SN… _kǑ;{ȶ!Q𕡑;} h`th;~G0/_>ktaظq tbZx&ßu͚5jhC "ß ~c:X8U;og>1,3F[!X 6@#==)E|ÀC~H,׷o_xxx`ٲeruoxx8G SU+Wc1TV6#pǾT5,77MQO<ݻpwu$%%xjU܃_>}0~?F~1!ͣyk"[-j23n(Akq @̓2/xy7qT/Dh(O$kݻwm\xΘ[7o (GM"~Z󖹫?F~UX 3m/ QUXveHf6M3^V~"'m'/##gΜk&Q?}2U2iUV#'bgC^m-UEE3Uµ8,DY;{%t]Oٕү_GJjr,^+Aav'#bW*fYQvdzq/Yh7hʵ?]?F۷o#55~~0sLdggmuO$(F~?FhvBۺe )Z\SL͛7e'Ŵp޼^ݬX Lj??6Ǐǖ̘>2eeeɯƑ?Rw.U^J?'#~ _W EpVxzzڵkĭ[W\܏?s(GWfMo~w6XH4MJ'"x{1s,9WTÞ>pq#&O~aӦ@y[T U!cď*pΜ9r?%EQEUYuЫW/:uJNt.^hXde":HQ?^EQUGQ?("Q({M((M((G((*2eZ5vGQEQN~?~{0ml؎0CJHѦ]'k׎i];w>J̥g~J ~*@"#Fo~4MN-[P7h<-<~xô+ ~\\bp+x ~Cݻ4M; ~~w&֭{ky_EO?Oo}.37Vp ~?_oh۶-MӴӛWSx ++GvGW[xuJDDs#QPE ]U&%O\N]i7tC&p9ggOЦMGxo;sG4M ~Dj~QiJہUhUDďG~hdžѣGɝQC+4M ~vFj$AUKQ>TѬ]FvWעE {4MNo(_>br(=G?'o͛>@pp0MӴSo~xwѴiS>,[h"U=#vC ~Ϋ^{M1.\ivJO:>4i94kWn_GQ%iy+ubԖoB%Q?M4 %o.ZW=c1yCcfE#4M+`-?Gi&9_Fh)  c"t>|HNeMJJ"*UɳEKlVrOZWwM>Xhɓ'WZ]%uRuQE7=$^oz.K/w}' .vWk?,4& ]V:EKQ_eWwM:~ou_)O1h Aov*wFUE+ V(睫PdW>7>nh)J!,FG$؜[`ܶے`l ɱT_娜Z Ti"\_jwʯ*uMUb fQJ}+O B}|qҤB_xp'^Ž$>k|ҥBw>"b.`&H^,r]s$G>gc|g|ɻ`ZCmVVbE`n`VSxS)W;&_aF WL~bJ]Go94?=򍧕ba_f j86#dԒL{(6U r k4 ii;Qu2| Mŗ!y={;;:|:J`ZŹ Vt<b~܏pqehD ~111PX`<==e4_G67o;f8}ڏ;38.ULmMXM@NJJ Ο?SNaϞ=ؼy3=T$$$`߾}ٳgI#(_WKւ xs%?}]s&nAx,m^xcbۈi?$ߎƟ;["ѢIi6\ Ǽqw^n>|tP%HTnUQX}_[nE||<ߏ4hl9#PotMsRcMlami8>5 m8}4e˖voooBCC @P,;~8ϙ# g'5x< [jq$o ѣ36no>cҰfiu-@VonԬ'G4_nFs##"9m뾭m5[b?`G5r5,y:vWp AçyE@g^W,gc!2.:8^* /یb| o߇$\ OO|e;bɠ~o?ϧ!q8z;'bJH~i܁ !ӑ3Fpk킔vرcq!cʕh߹= f5·cE>]j*][lA5tu,Dldee!bڍHlى۰; j3mBLܺu >>>2ꣀ^rE`^*^  :|;ҮS YVcʭ#QNc%pۺMN%;x)akE4@﭅5뤥<$0, %h}/u@K$w(D{>a{L BNA?r뎜BH9O^`\RHKIZ1cUcVŜX cm!YLܞ}q"1JWKZvܔ@mAVLZp%Fx-Ed^v$0*6]va޽ȏ~'EIsBGEƸspED:%A;x}%]Ɔh,Q/0;.Z܎R=F[d㚣&h;4ixbW4lQ}oG//$!"YHPVρ^QruV;\O صj`鿺ɿ4[quddd )K*;{Tհi*] ֢/3 h^+V/szݗ[*_Eh-_È\Ҷ, &.\j˄sy{<f,3x<&beA:qq}IWDOMWlڊЄؕp!aZ=jF %߷7BӰrCZ=!Qpoz[z:&-^0}'G 7}a,N=oE=Ѧo͚58rNHHDEE`P@8pAAA={m ~A:åk|fUaކ~9yn? <[Wb8; IOYb1A~xCԅkLihr+^F8j7}& Ipj.\u4:'bsq>AbkVdąx (Cj {_EUiߺh:u;5k.WAޘ9s& )CQsxELOOzM@_1-zFY+N6Sb"47;Z?VYfm{"'yΘ1FaEXz|J+sX[ h;h:Z[}g8_40B ѣ0 ϕk+uK 8dB&28~ ~qhk:m0Q3 kgvDƈ3gUt?0;>038o\w ሐLD HH]ɗ\j- ğF-‹gbQ~MD- D˶;̐䋈O,L*~\/ [匿q&)&j(u=%3N䯆zk +]'-iI?H"N$"X9 1 0 m!{V Ǖ9x;~ ^DE;ѪU+̝;f-”Klc~',GO:tFCrf'',"z;vUQ/&kll(2Ϲo-h3d~⁚HFf`t|7_o.sl(W/IMaQޘ֭Öqe8܋;ͱo'8[#Wm^b|],;O 7⋥ODTdK]I:Hp=^/5FNKF] ޜ^ޭz;6M˓O9gR!8 ~)҃{2z<^94>21j$(DCmDZYO@X 68Y?S 5@m$ks#dp䉉TX ?OlO}R^یī-oVN?d'WWLcts|25kL%9SR\Avdn4#r ~%=."Qnw)OJO-XŝHU 6oAb΄$9xsz|r;%@k</ B- 8_2*MC5xuj1wވvp] 2-;Og} ޾3{z\GƢy! KG@XwbOn]><sv)vY!}<:cξ8,C5[mM["(n}\`2dF]1b>,\aaa ܅_'a5Rߚ&1{4B1g&zӐIA:hIp}lgBf 1 ___|puuEh 9'`}UsѠ'ڌF a7f0p>^_>~ExYIɘ ~k ݽ}Y?HD-[`'k`>u1ci\>ra/x? F,$,]O_)+ADyo" &NLkؼ^4M:9>d;8q6 q 2yIp[ m$^!AFW9r$ހ)frZƮmfRcTVqHr]M\R*5!Gvލ^ Vq%̐ ݆.Ckum!!2 {xx୷ޒ=؁s@ }us>  L>M_V.O_۱_G"K7w" k T,[~I[sGϕ-D*/OZ-GMxy"9B@${}tl>ĹHۊ]Q\? sVfǒ@$?QҝؖI,9x<;J ~mOu,ObMQ{n}LXqn>}gũscUxG gnrRco4Ǝg╦JeyU'O c,vmئ_>ωuݺuCʕGUIF3Xt %`[q"SX"^FT5Ĥ]ks}%_g 6aÆҥ N͛7kU^>;ߑg={ݿ91:p޴~/ 6l ,OgX >s#tFDu⽰عW^rfďc%_7Yh) kc()ދn&=~qUQsT#Vz8/Xn39X2`m91'<ԝ8O`6 {N!>]c ~rՙsxOٛvX%hKME6$MZ'>)[kHΊ?֭[˰Tnq"{Xt,)..NJFMEBCd,~eloiύD~Oq=};GN{^KVpQ2? {P(sW2W*@_w.Z.*](*U$':DRWh)uK?O~?&t]X_h)'Y<тiŕ tת^C%O*2SKv6'9ض&9{Q-']M_@i8 ޻+}E:~?iB[ֺha5gIޞYywf4=w'<0h<iJtJ_0x}Cjy"=ɕ;U%Q OWQi Tr`ďGi&shQsEyn&'﫠JJg%a4pE 3kh3j'y?O+W4M ?]8q?ৗZ9g97;;4MӥW_۱EKqdbnZ?J{~ҫ6Ot ~ř5L؝ MW_F-a1.ϼ;Uf ս6~ =? ~.Zܖ[_jշ+s.3;p~?2~^8n9G~?=.ZuKY3E~4Mӥ~6׭@]wď~?R?nE "~4MSEK9_G(M.Z+FG~?umuR%Q?Gt.Z8n)_VVHصkl1-cy8>"htZE.Z>}/6ޑРE#4]Ap>0Whժ};=۹s'+G=|w{%K~?+/:g֔aUvmr nUuKDNgϞ!!4rrr$h!&G~4MWT ou~AҺΞE֌`F瑜,WDZcp=q]EK|hOKUGiڙiS>xn<,&o|S5%~7n={QegEff>}&G mV. 4}O(MN/ ' ϸ*8*u|H8r$N;u붢=z"ocPCHp7DsaPsWA!EYirR6E#9SNaɒ%XbRRRj5kO$$$.]Dyh+HY^-œB-ֹ+&\R _3%;x%u;7n*}>:`g)'-Ƞx#octr5qnbZ4E#?Pԯ_ΝC@@F%ի'#]VuYVHW lZW8c|vKz=B, ʑ7ozzܗeڳL1ijT?G+ 7~xݻ\2!AN:A`СvFLL 4]}Oogɏڵꥊ$1gfdk}7a;wڨR[ _R?_1ȑ#囁#88 u7oѣb}䃑+VB-ޱzӧOKkč=bqN x\UV+U't ٸG+^7oĉMZ{wqa]߀^zCcN={]vNw}s55RVUϯZj$tXµUK,hH{.Iw%#__.\ۙؾ}]ݹO;KH~E֧~?O҃rGݺu_b_XGW_Ua1}м̗_~~׮],:,Xt<}tc[?رOhy~~"W4׹{/.^3nHr iZ)'~<8MtrrX:,YtB+{'F1<|#4t;; kVUd1G#P$z1olF"JWX+hB7 [DDFI`__?,Ɩ-[`\Q\(|E9D_II ~ }]Đm~u~;I2*`"~Uu+0r$tXr2~E#( 5`prуߝɓf?~?+GQT!t|X4~E#(F.2 ~?kz(=;+W"Y5[MpC;,1d E~?*oH.mقݐ3~,[h]dTh;u@ʚ50)GQEׁuE)OIIj?X Y w!7;)SxR?RaT,\"#q闺axԷ;} ?aѯbv>]kUӾ=pAG𣨢HѾ۷ ~T;wpES'\l}l \/Ȫ[ u] #Lkh..p1j-mG(Dy/@\\tB𣬁_ZT^;[~!=?.7qq+|o^7Z?w5& 8G(*OAA@qE#xڸO7,o3P ;^#ݠ%yà|*c3FmTP4Z J喗*G#QTYЕ+@*EA,_X>PwJXݭrrpS{Hзe8ii? . (ɐ gb}iy[]jp}QX_~ca?~?*mIZt"=Êw??CxxJY9޽Sl܅KC^y{*aϸ_j_;c0SV}l=1iATo7?J[ĉӮGY}K|.?Xݩ-IB%l^膜wuggc0âWAE̫b-E ~bZR֭#GbHII1bK~.=5k~  BZZNg𿿓n}_#6yIG{lO<1cxwW a5@igW^RА~(>wIIIVzRm2e d[8|0l#U+HNG~?sn[7n/`|aW¹- ܖUtּwDHoW_FF5 հ=kӖm2U9S?u7cƌA޽o>tU͛7Oĺ"Q m *~:s"'_#_~6-YD֨ط^ŹE+' Oy!{늈ߡC ۾ 2dF%EQ*Ӌ_-A777/"{9/G O|S=P +a ~N> &`ĉr̙3:tgϞr0lÆ lGQ%w,~E#($Am -c (G#QT1S]-Ǘq~E#(y30t(`4G~ʓD2ǡCn(!4]&(B-];]geA?E1G#QT1Hte~?"~UCK~>E~?ED} e~?"~Uefg~?"(\JTnQvG(GQE@qc =G#QGQR"AH%q>V~?EB6 @:l&~E#QT9-[81~ںu+MvE# 5kVW~GS?qQy~?U1${= ;OG~HJJFJJ RSSsYz\~:nܸ[n!++ wmV wG('Yt9OG~IIItϑ+WkȐ&23p'ݻg5A4}~?rI?' x9?~x"\u$mܹsw%?qOe ~&h`6jU^Pѯ?z(]jwC~?[f̀-[tC9~?%j|Y@5 }:{srCOef6m$拕saN3~?EYݷ'qr]eO}][u'K mi?~?Re ]w-}?%C~d C2Gvvܮx$Ap(g޾RG?F~?EKK+?GS?Ѓߍ ]offÇc<}`UUFU|=gi?G(cTDD!~TQH~-7f.{wq_}=1zV}Qb[24F *}?z3E#˗I ~?%eώۙ ^˹#AC}~%$[D}ňDvHQtFp:p*pRRg'#Gݻuޮ[(yl]lκݗ_sQjw||tոy\j\GwPtM`H"#,m톭\,~?_T ~3, "BHHh8AAN秜*ڼ맋6p"&Qt6Lgebѣ1c+[DD2ĉI}H#O`VM;tIGQ$`~lݺ5gPƻ DD-W'\9 tmmf݊V贪=z*'|tlYܙWAn%TNY4Qv70C-ڨC-^p 4#ڝhnZbnh-`M kg bfFVB>`d4G+:e4FtKt^`3F/l%X3oW_x}. H~Υ$ݽ ^ypuյm(G7L|oJ^7ڗJxiG%Ԏ AM< Znn^g-4=iG#D6 FmOE**z5UJ*8/?WBՀϰ>i=Ɏ&w33mNT*BUٶ2a?~?_qHTˊ3gtU}Kٱ~?%]θ|#*߷^@~ٱZ*U$ D wʨBRl]rK2 i)èJ"~iӀt,E#]Q~W^w^]%Q?g7k{nE~N~>K1PvWUE#%x"QCW]{첐" D'_ v>n"~z2DOʥ~?gK7.휶h}[v<"*>Mʫ`H#UuLQ?Ȟ!Q‘ PAb] &AhFe_? 4L6 ~239"E~D }"~Z~7Թ䏠Y\FQEa*SXG#._jv~?GW'ÕY?TET1LQaUW[3GL{y(6~E}ެV*Tk(#_:Ri9dwT dKPuw )ʣH3)7$Q? 0y2~েBF8]C:%0ZJT/>KsX6MPqHG#5?惞~TA~?'ur~") ;*^"Q?G+7|8 ~"-cxE(E#ȱ3f <<W^}eK 5o"QeL,I͠HGrx5*jLmug7Ag.v?_iCz^I?GQ?샟Bդ D⇾ݜV|#4+Ӷ)G9SVoREYV궅G+$(S7./EQ?৏9[5'G𳡌 +W(E~r ~>>uP(G(\_J ܾ ~"-|GDt4o(E~r ~(E~r~]Ǐ(E~r ~Z78xE(E#[׮(Ucgm \G+9ѾyaPrz3NLꬼ8e {,%Y~?8k/LQ?_n1vl ^^ Q,Gs{8b-E"?W,ԪHa5a5CZʗkVr_ ZM6dT0ݗg.ı/VZ}QL#ɺ|hӆR*m3<=9~te`0\1ҨrB=2poRSI[GDtb U~fjT^+R̅8vKD,|&{7mh~ŋ!P{gEֆad/p /GpA% "".^(vaq_GT{a (:=Iw+]vbuy.Sէ+UsN5 ~ KJKە%yJ-okc)}]xHͯ3Zig2mO(Vj{@F?/»wfM7e"~)>oW+|]yU//T:@į\?o>oE.%-Pvvј54|ח6o7.AWoqeVB[D|SkGvڗg#~_9O  ~V鋝(X/ c䢅,;Ŏ{VzbFx c;">ۋc&d?Ocvb Ag į _a_!~ ~⧱}۲C?@ O3y5O?@ oZ?@??h_~1/NC?}on٢E-~6}.~Ι-^́!~ ~*_۶fs ~_?o**$~ǎ׳ ~_?ׅEB_97U_!~yݽ{^|鞿y?~l<,{=|-c-{{Rof>/_4kOX:k<߯Yį|{1?įoʕִiS۽{{g[~.\ЦNZx"lnmŊ6| 5m:8߽{gϞ=KJtcK~˙3g?PWևJ*xuE{x>U!F4żf:~׈j_WdCDk=%_W+E hWo^"◛ks4ibgϞZj2I}>=1cݻ7f{?TӎO dAAݹs'.˗/O6/X -O!~~ժbb, SĈ{R"ZbC./rmq3Wz~cT=-XZ'qxOD폴U!$4iR$׮]+%~s̱ .:tp5jDz Wڸqs]U~_6ؐ!>}X=ҪJ˖-Ӯ͚5K6+ڵK67nC*D]Wb ww:v♛g\a5mǫyT.%錮JT5'zQ#W_Tm۶ƒ46lh.]ڵk;qTW.&^C>};v,PK! WfFw[O\//\+ y2,b,^׵D*tl/A!~/^z֥K;%6m'x޽{ۮ]2 ߘ1cl96BWE[+Svz~&]25}V϶6xycB%U 5W Vd񫠨.۬Y},nݺ[dU3ŋ/^ &X۶mSNvWmݺWu6[UƋΛ7QF1Alӹsg7]Yf Nh J4yֱcG7͛mڴ7~i_&WJ& ěh+;IqW(Q+$z^<3Iq/6.IH`tQϧ{CR.`%" GQFNtpj$Dz[P۬N#G: H>|UjZ}1K\9SJfϞmOdo˖-={۷mw0ydqF`oǎ3gtUzݢN:v=(V-hJf_.8-r |w?oժUyfLU'Okq6l]FZ{1hpU)wn{qJmڴ)p gP%XְXa}p Æ T'[h~A0[ ~_:&y̝;UGm߾֬Yڮ:jߔ)S\*}>r݊F]zA킌>~:^,Y}P*ת-]Fڬ[Jj*իWwUau4h!~)#8ҧY@֯_u6cǎu{6O8RԩSQyڵ:ԱflXnWIo֭nc?Ki5ܹsΗ/_\Ɉf,ܹUTQۃ:~K6JRt)RTSD[7Jy FIڬAKNm[l-cn??0#@?B!~GA"ď C!TÇn.ƚ.ʄTdtD!T}޾}&B WRѱc3# ~P'{?UՕMHEFǚ9LďGA?ď ~\X!G!G!@Hpa! ~.~ ~ ~ ~ ~Tխ[$tLNN&!{?2O( !$ <!|IENDB`spyder-2.3.8/doc/images/explorer_menu1.png0000664000000000000000000001323612626055322017227 0ustar rootrootPNG  IHDRN.eIDATx |SUǯ"GsoFp >3#||(* 2* & HYڲo@K ]"ݠP(PB4Iٚٗ66Mn[ڴ9Y89c $PBq'*pZVѨjjRH.` gww7&bOaSYl"lAC>ͭ s&3jgf)Ifhmھ1YD8T68?vX#E2̉SeԈ.JLUfeoEkY baGËUH$ypAwQb.{97(ٴ#ie/,~}geJ  v]MH}k`W$g+;!!ǝR +_8m 鰯!fƚڕP hg9-opʯؼM?NG Ae~ Ύ^|AO2!+hXy8Q8Qp_R(؂ @ x|>̙3؂ 2;D & "N4:D-*1i)qk o7\joM_Cܼ #nḦI1Xm<s[{IMoq *@A¸XN@xK?NS|wαl@cv3ceIc[n[IܵcDEow {2yPqnqp.slp4fNA[=) ݛx}D=utI0p]Oq{䶙}nS^15i):e| Ƿ/(z=wȒzێe8I{jZ3RROX8Q8QpLtS>EdϬlH؈ya.-Hp`#hLKK`P8I%J>&''3`pY L&kj\PP@rN钏Ζ6Svc̼<&HZ[ Q P}6 qu)J@& %9YYV~RRrpppww7Fs)= '|~糲o.J$5o*.(ɿ ' *JP»B WSSS^^sZu3[886oiGWTR*,<&'''%%Ġu:C>zD )/m ' q'j8R go_b,:Mk4 Er,'^p"i4i4K P}W}NhhtXgoN$spVEw^z#ΎfEee~x[gɹ[2ѝh8 67-⩇ WԼ>[ye/IvL8KBwtcSכ҅=c멹3OyI̖o7[iq'P6 jjةH}}^ʕK 8 yI(DSsy෕S[jᄑ`UM,a8QB1L76z3[~Y_hz'gxC11HaIs>۠9N">]{OO/&7-xkT.Or'pZ,=_v=|G!We} _|Jq%d h;";w.dV,lfM!>*>Xם 8 dQ#"bD%싈ڻ7<$$؁8Q8Qq&Μ̄!X81|+ف嵶 K"rBVG,pr\@aÆ6aH$K2LV=N ' (<)KewwJcGtqrAF1l9AAs !b8;:X*vˁR5&tB^r ?*<'I!SP{d{E9CW%nj}?$NGFNȰ%F=2-f&F`M̄7X8Q8Qq'*pCVh٢' q'DB0. gNz̼&[8jMIqE2']4tc\1q:K- i!aQ )ի6ou^ z?lF t% G8#Gڵ;"66uuWl ޳hL>N`vqq/0*8Fӱk r#H$!7Jd|9i*q+ ⒊N?##+FylBRFIiu;G WcLRR}#zIN}/{UV^Zm[Dr anHH^6P9TNg[xe+i7ywc!gdTrGLi^"i#"9qy3QiK ^#KeNa 7-yC%n2P,hps_ӶXnZv}W]㉣8uGҊmZb˗'qZ֢jWzPN\wt_ 3ϛd*hoj0g))(>UӸ~}Xmyf‘2ܴ֚ xLR8pp2WT]d2Vނ*7}f qh(g3WX^UWQuϱn(ĉB(ĉB8Ӫ,t `.cs}S&NRcvqoʄIB`NG5(={6M2_f).&:u\/#3 ꩕#ޗ} kBxd;5W42#C4$@s8HUq9Nz[8PjU;[O\'{KoM%;qLYՋ2%vw82,J7wщ q' q"NlĉB1ÉaD1* ;[D!NĉB(ĉB(ĉ8GUs%=gۦad" mj@488SJj%7D̤@r"s:5˦!+_>[>Mx$?,]= ׮WÒM KTka6K?mjRB *\j^#Saڥ65V U;Q8Qq' q' q"ĸ Vy#{k2JT5y#uxz# ]}<&tKJ9t & 7 c*oJNT du1bJ-{zNl rk@s{0H9h?C6Pg@x#\=Ngi o杭Pg1/<{`pT;^T( z6OS![rg%(xBΓ إ:S!B!ND!NĉBYWWWZZzܫ Ll@yI؂,YRR$}}Vl#؂N5H_*Gv)z=PUU-,wBgN'{X-k)Xt4xwpX-Q8dԅ~MzQL:S*]@&X2Y]] "P19X~N2k29Eft}u`0PдjnLk^7N/o^|?pAEmJ]qʣ/WTZj3g,,,Ri΁i&ycS %yfAA㱓 m: Nd6 '9/TG8⻝?WqN8zmRr֝1Fzn%^_Lk4=pf^^ ;b1V?>]cyeL*=VPp|"#P؂;Da;_J$+\>_([x0E' 9$x%* 111ҥK؂8Q8Qq&ͰIENDB`spyder-2.3.8/doc/images/inspector_rich.png0000664000000000000000000003245212626055322017276 0ustar rootrootPNG  IHDROk>v4IDATxٯ%GH3aXrry bs?O}^ZB`[U1P@bs\]6ۅAnm\\޷*9ED2>͓K|'"3#?_ׇVqYd d+lG@ V/XUIJݧVvXE,oΓŲ_5@ t<8&ӌhFi`?~j5@ luRmw;ڮxh!>#5eEU(n{J@ b*_f˓lg<]Lﶫ ]Q3o1ۄU½ղ^xNM[O<RLUvfvWM?gRUGc|at9X=ǫE Qi?8tvf٥Όb._醼T>٧앜-&m]K<ςvy) K%mgFlgJ7vUDlmlWP:,`yI a};vkk䟊vbX$>5ªy6Eb~Aܢ>tvlyn;GWQ_mV/a8_^m&6YdWmf VyJ~TD@WmX!IJ>?1ƿ|l[~ֿcQ,}!Eq_>p>vG> kC8fv`vfЇ?!@ V)dv+l| &<*dԖ9.dA֊n`C rݻw{oɓ)kfe+mwԩvm \vV.3<G뮻{~bOfn[n׾vdKY:.\ʥ^PݻDuK?֓9ۙ7ΚOmGiޘt{2mWR̛tYy#lDt6Q⋐T,I׿)vϟgϞ;vm|ͷ|E׼oU˕~yS**!:('ӱ݃;R@(%>CjX`>vF?F!Qv&"Ÿ :]fm;}߸;wt2۷[V_Zukw_v~q?o B1B&OH=wf1H]kØ3@lsqw{\rW^ydg~^λ{/_+N~q)IaZl'ݲv"۱cC=?裏?'zOx'yǏ=zt4}lQE98~&31[6 RO~r/| _l~6;t7<~y%,vgϞ=@ZvY.L#6k{g玃?[o[oo¿=÷sW<*:l4.]k׮[عs'Ԍ?iv`;lvl@lnjO:u7:t΁vޝO<]3['[@_m2~J l[?T.lv&T9lӧ+9svN8KvO>d&'m%}`0ji44Ԗ|cg",ϺI DW4 Lk0]Znʊ*-T ڝfM^j^odofaW]}b(jٮO_#ܲl<'L9?ɫ:ˌ\Z)fQlIԑT嶝`5\"rqBV 5ʫ ػJ)ӔMB SfuhLDJhm;튥ej1#[JJG d/uDope/No{!K"nûY()Fo,7ᳮA?'l4p"礏iΞ={:FNm'n-la-jBMk8EROYg;m@eF7T&7 d6鷰 up u!HMr!`1v%Ν;ϟ?I׉V xSk6vjmgjmn;–)(b;eCN?g)Vbzyh֓=b'RAZʕ+3紻&['[#1Apn-`weZWdc\E )7nYQd CD =+v=!DzeM,ZTϺIլm`)='ܷ=ι9_-q1OSV\&vTl$XTs/S$zYW򰆷) bˏogGSN ;vهwy'ռxb>03g?>S'Nc=1^ۗ[sH1MCnL.\҄]ݔpԲkP!d1Mӧy駟^3خdm;vkGqƐYm]7l`WI-ǜks@\su絪cMa.>? {76/q f;5AL- #)ПF?CyOYxex}َKSv/w2i*i¶:uŲ$dۭSOfYU9I\#Ū:KKe/Z1jo?7dOl;+8CDwih3K~z'tm5.m;mg&-x|5+DmT~- -k(sb$g9jqR;a5O83v+;l;!~U]5 K9^Q:]LE8 WRl.HJEGbYp~uwiܓ)x^((~)~zōyS*ұvVEN!l`~[|&(la;vl`]xѴv`;E_ ~vhж'&g+(D3ɛ^hR N|Y]piVIf'f0kz[Yjg}sͶ]=5ysbFkYV>}-u0y.)7_1t.eʫkAx٧l˲1hgf-񶲽0& KGE k .4 o ƞ$tDF>[_olG21m,TllmGʻ3V`ͩbm(% "8[Phiz;zHh .?ޞ,]|jY9,Q<ĪƏ{ݬlW_r31qC.V:I%uE9ͧߴj|C p*VlIk;A9y%I-vyZ[rj.̪xljG;|-!J h|ܓY]'vWtNʻ9^.=hOSh/~vlʆYmpvl =+d;juvy7vI窑(bdۉyʺ@vlON|ioOlʇ,(_jljbμ3o 3_=zvʆVPb>$qvabJj=* 'l .Gnxh{8{5x߮[YYLB%T;nU#Ea}r.oѓ=v`%U;%H;v^kT)bR}Hy|*En^u燼ߔϑ{Ѭ&)HvlO;vɷy^J,'E_D .D_]q4awCVjvsו7oy#p^f{!Z'r_ [ob;lzqsa\ݠc~0yVxN ۭJVƒYݳݴZvv]ŋMdıT##/c0l˯e /;k׶6Q}X6A?7b(J@~7ZRm! nB]<Gi7 r Oo ׯ2aތ=z9 Ml X㶝{:W8nl[ Q_n2a}="ѹ&TTM!>ZҸ$NCma|_n eHFJrLN'=PZNIM.b|jU_#UpֶKA?zAGoZ&I73LfPv]()SgN}\D83y IK&-QrO܇B0?هYv,`E[81_p '3}Pu4B xG$J!>ZҨti]s(-gh7 Ed39}VN9LHޛo[z2}؏ݒmޕG@&5uw랏o )#G;P&`jZ"vђ&NLɣ793b釞` KdC;gi5 xc{'I)m"lJb~>LخW! f{5 tvla;v`;vv˚`;lv`;l]ov`;u^vH Bzv@v؎@ alwn™3g6 #bvhavdalwgbd`;@ vڮ]n r뭷~{Vs^u#M Nk^7x7|}&K;e;,y»}8Ov?@ ]vFk@ `]v~WR=EIh0JmvڑmUVY6%nvR]Y^97(LcMw ޡQ<5-vۭ};ARHv 'ѶuXvUEHy.mP@vg;+8wØ( mW~eR]$mG7&@ IOJ 8z .GfShz8{@ `mVhH=Zgi'3}c@ `So%^kf;*_%| HL֥'Ӿ vOyJ% g2{3R{TL@ ]mG #v@v؎@ a;@ `;lG l6hJl0C`;lvvla;vkbp0Y67668 ڮ`;lnbh1fܸlen"#lc= Cl˷f;'n-/c]_1!8G}鶳"M@o݅&zmvd#DW 6tۍێvpкHz $ܸ~s*h;9Jٿ-b,ɡh23Ѡ'ӱU(6b}vfrV`mgvml4oWz2(Y}I4O V8]mvMvm;lݶ+LYN-B0Pv#ߢ<5.~`O$]l70c6'1 N՜,mPn& 4n;+b]v,c-NQ.wq(.DJv]=Nsۉ22S D[#Z7vnf* Ҽ'Si=iVv&hֶrMn;!OϷȏn4ݶuc26Uc,%$n \gbJODv`;ln-mXxJ3(̼kl5mG>}+c>Yvj̭ma;l0KAӬI!0N]?qisFJՍWA|] =TR'B i#volOa|`ůf.F7kr'M@lY5^`bn*[*5i_j-u2am sIm'ԿWoM_쁱ÅГd=ujMXv-J &:'o,)b]ltOvAehw\18T15-iSLX.lVv3os)k:qT̍ HY]dؤ;~\F[%5v)ȋݓuފ}3Ӟ]?l^ h;3vԧT)yOM[ԋ)6%}[CTdӦ1lla;v4*Rv[݊cMṼwѶvخc^mŻP#{IRdzfX*)vlnl׻="pwiRZcJRF;l; `i9 uF&0s=oxt#Zxz<lYM^Av&<5S7oْydQ/еߟq.dAj-Tc"iitgbΟ@Y*ƭYsI:fVyU+֮4\vs0c3\(zqcd eR=\Ƅz2 4}4O,ȧT]+(pm;쉙f;1MIJ_ET=s 1 .Ҵ~ilיLs\"#X'}Wخϭfxك޶ (YC ?=R9s -)i|gb..>ZhJ g憢24Nc9o**n"Uvr `nN,(g)3AgOd-U~;=c2R δ~7xwP49?4c_tuڦ$R}}/f83/m?`Bp>~{&ȭJg  4#=9UԋT/8x/'9͏fv+AתF`;l`;l׆ 9 3:`.2 W]da;l0Wn5s+m\aPUa(ty`ŌDΰ0An}6- N<8|G"7xCFkHm`6_ 68\F"[.g88[-n$f+Fޜvz2}yh N{#c4KC#ǣSҶlבT˯Gm|#]HrtcF/ !l :jvlۭ튇:xC..]yK7M7u]3,Q^鮋>RηQhrDM"Ư \;lLեk=Lb*IlϜ ݙjLz4Po,aZ?Ui To(<>Xml?NOf虜M }!3ֺo GiodW6ix궫R"F Fw O;,@iS ۭOhFv3r݀a;l"-F`;lʶ9v`u]#pPywG30vC D>ݠڈ6 |Sglo%< 5Gl04b}l`߷K=q$ j>KI#ַ1vC7|Aqߓ:#Mnʶ>=҆`OFc#x Sdc,~tI3Rc0vnl'Xa;vl7wgw_~o[-7-z'>.:$yn`5<+Ptjfw:fMv=S /2*NTE]lWXj|9WGF` o-CsUyus(<`Ok0r3!̩ЬR_B/_Iʕ0BWl=)'qziēoo`nRO5B19+KZ%ƚTmwMR<@f0uu/J̕?B12\b~aMAqr;CuV'vnnm;X ?܋*#vGW'Ѧ5=;ՄŦǝV\НPmu3˛~;鋶Go0`ζg)v++H'rM1n5UgnϔK:|fpO5z5jOBu6/zv\ oT{2`Yg5z2>`N}J}HEy}Tۅ5Y͓qf$7Mɕ7NzJ%CUu(zrY>v2\m_L6 v`9aޣ8ۛ^ɦ6fb;v`;lv@2.]:uԍ7xС:ؽ{w&^o>ydtllMl}]m+1vov{e>dc.v{ebùg ӕޜ9n)ӍnlnM3M&R$} ?Mm*K#qMz&s-j\@خ?2e'K*=iRvm0zd\Lg\xOz`ֵ^Uhh.7Dꕕ>@!3F"z#"q1ldT3D"-n̆ ]ӣ:F z2NNqM7#O}؎!XDb;~`;lv>Zm;òR`U`w]IENDB`spyder-2.3.8/doc/images/ipythonconsole.png0000664000000000000000000006147112626055322017343 0ustar rootrootPNG  IHDRp~cIDATxy c?Y$@~ԕd] WWEk[e`]Yk9ټHQ""A%HDI}US]]=3;:lOOuUw7www~wν?p>|Ї>6NX-KKUЊh}{yz|88Fk=AlϽɖ0yʝ'a~OE#Ŧ-[ɩLGO>~ 7nl޼-QxF&Z׬Yjժ+Xl%˖-]>,_rЪk֬]Kdm ߀[npbbD>P[nڵ%hܬtBre6K9Kl,,Yy|Sp1 9?CĿK|w|Gŷ5Kc8[Z$ԖNv>8?3Jb!E3/e*] ̉bJtC~@t ӏܤSz')NjJtD;%:ybP UU#ӿbiRJKxZJrEZF0QRB՚(!=ijVRWvZm֮u5#,QM./4'vv2hҧ/~zѢ.zjŸJO/M"fJQ!Q^BW&FvG{v؇5"W_ym m k;,{9!;SĂŶf*OdPǦKm<6 U)TRǦU*ܙC~;y9DYc;uЍRZ$JNLTC9(;1EDۑM?|˺&`"' Ʌ'&cs@;ηW&gQ-/=(lk˔wN!qLӐ\O9K-4}K4kP[ QMp2Ds2$w.㵏H kf5)iak,Bi䠛naPP͓4uB;F?S-PSI*6w*7Ν;>dI٬ٺy35BiĴ6Hn٬mЭ@LSllVѤU~FRǣM[Gu*ulRecT*JS6RuZH/Tj B%$Sj.Te\NS5{EO$O,sc\KrN둢c܌c_1j۠GE6F/)kr ~N|lcǦJrT*.=6*QỤRG-<SN}6 h RT&/܎ꔝ%1Zvr\uPϷO/p գR٣ݜ={hx7[8ɞyb Ys7|tq>Șl'-Yv\44o2߻~=B5HT|xU V%*9VTɩ: 5ةvjԎ_P=N͹Pn}y3P:{v^tF?c=<38??Wt8 T:|s^>ʔ44J]T#bq|*jlXf5T- ֛!@<:mڗ凿~_C_=~+}ၿ%Nu UtV)y[cBH,9Xpa;hݞFpm8СCaТhbVEk**ک©09*6 4L؄J=J [*3J л\TYSh ao$BΘ"P { u~]( BG\s<&:t9BN8UTEJJMSJUܠNP7l())ya^탻>wbD> >N<4痾pţRb 8ڼ/MCjZ]k\ou\ن>pQJiҶP8ԍߵN_~m*PߣQ;xG+*]f7fj_Û>?uB ۨn 5[*_v8%MSFhz*T*qj<M PW;7_Qﭧfw|=~M-CsR;U+TmʻW ~r瓳7\Xs g1|d#iBZݵy~nB}zVv4 {7B5NjVCt tBu 5Q[N!.)B*77N;n?}8I_¹J6Ѫ0k@Sհ7ԩ:V*ڳ}/}񮷟{w{5z() cu%y Q!鼆y1$+]ThUƪjU~=ZsTP-oAHN<4<0?P( 6)PMbzLŅj}{dc/jrrݻfIfOM?eߴQga**lӲy\>C#T/[[?ɼFu|e:P-1 A}:pTi_J 3ڃT×S ove#U~@Iu_&Q'A ثe>'ZY<=yYTTK2 ]PY/Teak5XMՀ`ݻ4nz~߼N_4LE ӷ4Ʉ*Q]?> nZ_SBXֹ{-Yn$~=4d $g%ge)~[֧WP]1?tz*?$hʏ*> x'nU?,[*U u^3*=5|=t:Eܦ4Y"͞5('YN d*?X*{[ebOk%>RH"qAf:#=tv:3h' ɦ.`fawU{<_cJΣΩyX)Vs\ N͟VPn[g\;N6)4yhNKx#ua._a%Yj*:U=TPwٳ{M}8\kuB9{u֬Hygb)ċ{o<$?4O5L oC/-<-v:B5qr(%M)[V)Hnp*VKfKjZDR+dc˸Z>Ԡ67p!=TڑlOwu胕:Xt[KקyTi\_7VCo*V2UJcWМ?J[t*1JX"tOڟ3s?5idB]{^XGB[Än3a֧&6QŦXwSق˟G-rpCЮN*oڌi: cjQVl$7JǓJ 5WFjV!TPuN<eS0?3Wz(iF\7xߤ~ݻ}_9C3ளހe/`گ_[Ma3oO3Yse2#j&MIwf仧Tc *U*ToSCzF8*[4|95P 6kԷhDy~Νl:{V}(S@@nOJ(tu5{ҽ,t;=:Cwg,unW[|)/)0](lOoTnkIҭZj*̚!y3SY4ORUjj8Zu9N%As{ӃMU>K[t uѢEB)o0x U@}>;>a3~3RnО>Yn뢔͐Dz;R=[)[Ց⩭˕2Oi#F lHۚYj61kErIlokkkI5Z>5զ+Jl75U;SH}pS7U+>jZ-[$3OO+?N_WNt9 =( =iگ!ΕOڵws0l _1ƃo$ag9s<SSOg|tDU*UD'ԍҢjrj>F{(|T9T'gdB=kάAjR@/NJtH6RZz;ۛӔ2R:3L1npRKT50ZLL7vP󴹆ٴ,]/l:3U;n&Or"q r&)lT=hշ~7 Tvm# Om/WT}qC)^+`Vj&]",,B~D}D>ƒب~00{ŋ޶U ,4#vcc>-,ցXY+۠h]S4LM8ᔊ89\3l G$R(#/_*/Ύ;Y9|bN?z(QJX:rNQ:,~(Q;=xcG0O}GZpP{?!;S:p#=r(<گگoooo;;e¯ʯ//////$|4 z.hB*t@HЇ_~BVʹPG@@.V U<B[BulP!TcR? BP4B]RDBmzS,vvO9CRSHm ??=eh !%KSEW桏_}Sٿ'S_?7ܦgʿq{J)]+sr/S!TagJ[U<5Oؿh5[RZm̄D85Po^UUUQQQ^NɢL2aÆBR_T,U$>G>}fP@払_pᳯ<}Wxuɫ/d=uukG]>=fi 95P]VYYO~~7)y7o~ۯz[4[<~2Z}~ͷf}Gs}~歋Եu$;pEPAj:;oZ$,94k?ǿways,{N5~}?i"}u[4 2?ܱ>Й-^~̶;|v4eschKW\)--p/?~ȱ㇏;tȁ?g߁W~Oz7z{Zm?c?|aH?=_G|EJrdT2SW>EZ.}%bDԒPV1.GsDOH_,d+PBݱcǏ|rѢ%/]&!>r`Ϟw;~byΝ&e:Mo?{߲>_?/>˶\%FɰAR`)kyWLv'ϝm;D߹vpП4P@!LgΜO}czꫯze7ɦweBPS]Bu" xSN9s֓NS:}ҩSgO'|2P?W:IM0}k<GQ? Ma%o% dtj\j}Mt;}*ߨN++C}_DCI+._PZ{ҥ+WGh_-Omqf{KPi&R/7AMf˗իRV#<]ʷYt%wmSN.zozWMT?}ʆ?$h{nOmH+R̬-6.ƛPM??|Fk jg<;.]ZsJh٠'_:T2w(mVIgq*+!?7_{aloMjM-))իE[̨(Gw ˒pH5XP= U[l&PM?|3d2TJ8U)e+V.[ɒKmjwr_[:}G+16g=9%%5ɗK֜թ!Cmr>vLjֳws~?h%ļyWYoj;?dM*]C;d}j,CUyc%B5+5iPvEJ5hS'PɝyywEZjӦMCCC4])+XJ l8{-V͙3M\o4EqmBA Uu6IHt C,׸a-5ڟ'ꀏ)8b(Kф۲T_S}]"]'s4BxBy#S;,'BPW#= lrnѵP% 6#FP]|Upaڧk-WSe枆Z0JNED|PU Ncsym9&,IW2~ܥpcJT"u5bń,)\==I-ԡo*&=H,_ҊUcUu,||[8F6Zi&Tb|lJS1Bu?Cs CefmеQA'mH%پHVy#]GQƈPP ui* {ClUv U{&zԵA?{! zGO{HUlun;jL/Tyh!*GKH5O^G(KF]{/roP=:6ԹtG(K W_Oha._ui#>'.$B]z5\BݸqclTq t(O4O}sF{)68;%M ^ g9;]LaVl߼S*K,KDPΩ=@BUwaTθ캤Tt@~ŷ̀T9 k3Zѿ" hn⡤$B]f OkB!˔c/w oymL:$ڡ,&P_4ޔ*NP2d_ @C j&ԡ(C8dUogxVq|Dݻرkm{Wus,f$ۊN̹:@Pgw%Խ6#j v B^%@ T>gڳ%6*D%C6*D{˓KG9́P@&B{8Qj"}Q;w!TjLJy:1j"ԃG%ǏC T>zT$Pm TP;vqJ.P@&B=v'v %j$,)j"/>ɒKm TP_|_:uEB=ÁP@B%tK\Y;w,BӧXP*DϜ%6PɤýS6c42K<(f=9P_ NEqZ&j2O7Lj1kFbjyo(Ӭ(B-3 *3Ή焔d'op^ӛIaRɚǼ-&LꝚ_m pF[;r0qzyJxnNj+tknFNFjgWWG&9b6j7\aV=;0`(*BE'TyޞSB-QS 3k4^bG!y6yfAt?7ӑ6>B(,3˼,Dwr B,9sI=6";08?0,Tg;*^>VP\EanƑڴdM- Ğfxi#|箅Z4"5{U$Hs"( j9©#rٳgℴPY fPD\8Psu0q`#FՄSZ^eԩu+"rN&Ԣ fSk-}&kR45v(#ԞYSGxsΥVGLqLB5Ԇ S,q ?oB-mBQF4os 54p; *9s\B7oc=FZ C5 "/,Y`ww h:.RAY푢,iۆ6B&Yj {BNPMDL]5*i g]dD"t3Ԧu}gMsMəg 5V&?`Ykl i_4+z%'TԈB , 'Rv)D ot=|s?/j8Mm$6EPr.T͐uc#g\PU麨 ,Lӝ YQp׮+PfltB (u/]-)y9/>I·)V#]Xs0RfEbUQ\ Uq8B׍\RqNCϚtDUgQ U&mT.* U}"w 3BGc ԦYX(hHvvq9>Sji b@ 5?0 I-eHMSuRX2yzbK#"wdX@-ygfR@S9[u(rz%< ͻ|ͲNRtMݜra(J]#ڀ_-AVLUR>+hӧ|M9 bx*㚅O [f8&o$vY ? TXP[g\)u)_oU{dn)rX@‚V/8s".Kc=^ 8ƫPM Ӊ8ƛP{r P!T* ZiW/c<4V qhQ512y\=&%8 4XPsjj ]3@H7#C0 4wNCLF̨bj-g(bVQt`+[ _ o2Bk4ݷ]n9Psk=K.}_?x{&,ΡxA$ńGY+K7BM._u5LdGEj""vɅV7&eA9/4B^!?.K џRBue/ &j\u߲9k#1\>ǙP ߸ fYj.)q5T2w(!$B ՛NU_Ltm/WII|E'f4bzyŏզ~qu@_(eNE%žks>0-T\Rϩ%i0hIBKYB2YM 8&mВJVv˻~?au+2>5۩-KB5,!z)[\]?c+K|MU#}dw}WNJ{D5Pyzwӧv0ڵ8dӈUա$f%%lE+{29syK7eWu+OtϬH-T_-`KasJ V5Ue,0%$0uS5gݖ\>w6jS PIHM]TU}vh~ͳP#mg;7B]rt U-<%(zծ@mB5j- 9ꔻЧƦƦ NƩmʋv`z ._Cݏ[^IfBWEӴWwQ=^Su.{UQ]&iR̈́ίQ)X vF9i|(Ԑ|YލB|4}e|ԡ'羚U_>*3B E8)S]}]}C=%PaV:k$ RRB&4COUԻ)Te#eFܛKM]~zr>;]}y8DUiiGG*(\=ܸ$)5(g~;gqߨ(%o/qԍז%C'\~*@YG"$V>TBsD))}H;1 øiaR^wiL5M©PdB-(+TYj{+b*긹6PGшs)7Z}!B!6XB;3f̈́PR{.5QՇ uBu,RPCZ$j &3M9m# PIM.N(%* B5jeCp:}t P@fBojhnlJ9s̙3KK=4L9fmqn' Ƨ{cM T[©.WT5襥q&y@Up!cdzlE<拧B-o ZB< PS]RMMP'HP(Ռn UBɛPS0sƕPS-N†$744D*:ڌIHxaaqVߤ4 c{4QTE7y3篐F Ph ȃ7یv+'P j)2#t -\hPSMO˼5=\.RΫ"a(@]p.< DjiM-9֖jc-R U7arˌmzG=kמܸiZӹSGP[l TJ<6|ŋ_~ɠ^سc[>a3:FjEK@I *#ԟ'ϟq}WzK'NxcǏ9vБ;w= rCCB&PwANgp"[#G+N//(PL;wŋ׮]{M_j’B-+w ՊZZF Bw0N>c3fB TSr:B>=:}s̙*5P]B6c洙3 B'T~uFi2Cx(OP U6SY]]USS][PU PWHI xBC6O"L+JQP@ږN*5P0⋏("FM@6*ȅzwjäI, 8P6iaꑒIyZ8Gsz5,9>e* `PI1TPmhjyE#8j42xD-] (PSs%TۅvWM/ Bi57)T T4! H"AdBe?~ B0~[IеP5@1 B#N =B́PSGj(TTX *D+*Er 5@ BR U8BjWW B͍P j*TBNP@&BQY5/nTj$Ԫ*TPS!TPBP@фʓB B5̪~ B T#V' P P@9*5R!TPkS]BNP@Be BjiuM_©*5PS!Tj" B T#Ԗ0:k,Tj"Բ2ZPS!Tj|Ξ=BF B5jm]Y-s* BM(TTP*5P̙P +TT j]}YF B5j9j= BP"j BM"zr*%_$zPr T* \*5PERP@fBmPB&j%H"A BmhPP!**w* BM &r*% *0 BM ƦjTT*5P!T B BjMSs $j3 @ B ԄBe BjmsK- @ B dB Bj]Kk-O*5P TB T` BB xP@ Z9H*B TB TP@ 8B,ٳ!Tj|ɩ*5P.G @ 8B2e }!B 8BP@*CMs/T-,yFJd TPk[ZSG|؃*H-~!TPkɩYf÷nB5juSS o:B01J͛j664F%{>̙3B01J?s>|-))d24])ԪFHUPLկ~'شiS@ B >S/jE}}eC#9uľʄ{&P/]ꫯ:Aɣ ,pljy}}5RGOB&P^Jmnz7_g ijY]hC% TJ< &+ںr[S쑒hI* %ׯHT+ԚZzKS&|;(ucp|K~w޺uK#Ժ:KdfR) TJufuM)oZB`Y$(ZU-PPPH*fkHHHHHHHN:J8%zN-h Q.WT2VW . VU|۩eZyh%)=hinnmnimnmmiM(}R)56466ѯS-VQ^^A,m/fə!~TVZvv2fd9h+ج'M=il#Ď+4Cu5RN啔<#e)u-|1jloO7WgͰTTݴTPVkZ\鑖)4+=b%iRu3Rem 馪tSM[S][S}F#~uXn :m<mҜT[tSEnemmYUie-+ *5Rɩ#fRLhtҷe:oXjKYgS);k1,Y?gK)4An&VJMʹ`3IC2]H5e7mfv|;;:MۙJdAM]MumMEuyK(*UՔ!ep=60e4 RYUfޭ:՚#.|V%V`ٌ$RM)uZE]i-6'+i6|'vUqT3R϶]k$TIх Trf ~ Ԕ +ZgjyZBͫSA۩9\f}6ReΚ`N:Ç+Ǯ={P?%sww}2d kn#+ \S5J0_y g₶)wd ?nqL4JwMir6M3,M克=ހ"{1X]=SS<;0>avIENDB`spyder-2.3.8/doc/images/variableexplorer2.png0000664000000000000000000005043312626055322017712 0ustar rootrootPNG  IHDRIGbPIDATx Tՙ}dͻ͚w3c]4mL2yNLBBPxiC4 Q[Fb/4- @$ZixEE|9>{s9뿺ꜳ/eyyW  &@AY iAAPN$! ^$AA  *VM#'Ҫ *;q*=$aM9&*x  *$x(*&L  *$ED&pVMI#'r}yZAT4&fF<珦#E-9shto~N\MFϱW5fjWwAACAn}$e=?3:9h3y 1@C[^?;&xÔ6y_$sʻ'ݵ AAPB̓1q:/ X"/ZxgBAؐtϤGҗPKI$vAP}@nG6AgzjߩM/'4gʹ M H$AA Ikic荞1΢~iیsiKٴh9>~!I=HѬQ89f^@,uי$1D $ZvQ&fBM_ś~EJInF@* Rv0qI !KWA; ͢Qs4fKF I )$u_v0s嚿w&MiXtɵoa>x~/;竴%h\!m=pJ K]>/i@T-5J" $i}|˗{.M;,vнٿ]gE=i٦^ziK, Aa #JK+wYy̨Y:OES1)(̓P/=$*5Zўǿ{`rKN{ /VOzoGSs}UHzMhorJCz ?ApǼ2+S4O./`?X]? ߥ5 Դ ih+赧ЩOCrL_Ӎ'Y+N??Vʨy=/*$0)xpC z="5$A$$"{CR0Tn\r3h7yHߥt ϥmj?W7rQ7UI .fN GGIE5BexMIPU)dx^))&$s$=tXa_:C? 4ni»WҤkG?`Y4,s(}n_\2D$5ɓ#SpyBrEA.̦Ik HKb._!&r9?<yJHV4jLjD:c脧6I'n=WA/s3c $>FE$tI N ]_:HPń<k_S@T!7#~Fd?cH2LA1T9IkWcNSo>M:u00֊ci7*GpaXCd$z$r[q tKI_dq%YǭIhm@ *$1_c1H. Z@ ^!it3i֭Rh˖m; *wyRH*T$ *Z$ J$  $U'O?{-[}ݪT5X׿tR:SJr@ T>hAPu H$ГAUI$I I$I I$I JIZr%qtUWI&ŋI$x $19s&b؎;x}^@ $ j=IcI$AAUIb!R7$ tRSS' ;$Scc;I*fǡ'QZ/IATt?YH*9$R`*w֏_lalJ/ (={Җ/ʠ/ O%g%$C{'vd VTcE^1^ںTVOR@ROR=J9_$6 t>\PH (iR@4YO:KI%L+C4䞤?L4E3=~F.jE#ߡ3ϣ-gӖ;O-O96!)$Rk˥=I.ޘ@G[kGe;=Ю+oBH@*'|:kI] $'IK>^Ȁ/񇫟I_C4܃wO?,$DΦ:IQIۜTį*+')dls-^ŃP@pr "Zv'|4-ok 'i ̸ݓT_x_I =V]_cAPឤw7wO%Z@z}5;І{}/IqG9á1֡) OAT랤^#So>M:nNCI?5_CH_O=}#I$x $1{kf:fItK_W)ꢎq$@ H>V/ OAT9s&b؎;x}^@ $ j=IL+W; դIhż^@ $ j=I T$V $Ib  *I' \ڵAUI$@ $I$ H$  @ H I$@ $I$ 镭ІDxXO$@ _0AIO?4 \=M˯־K5+V$IAP@G}u.*]c<;H**$qm~@ (Hڻw/Ow.{C}7nH*$PivjlLI-XpWRH: ]CڑіwKGHٖL y{bi#=ں)ڱ AuI~!״w7ow_s+u,~zIE$7NzޭNHoot$6ݎhP3 -jy/a冲=WmkN+R"1 mv_ GHl=t hx@@'55Q@=B" {;(Jrw1_Oc{zЪ!ęX@Eoer_|D $s1Y AI>oyu{V'1H^juZWNWOF OR=\&o9m$ߛ{0 ჳ0/rC՞؎Z0w"~0`j=@Hznw+0#He{PNڷo $ɋIz$:ԐxH*$:jNR@ +д'{a`>T]NKb2ʃB$A i 7Sthz_'n]w'^HSVn{fJϽh/sTTH HjHJH0Q($rmhдIzlP#%UhAI>@]Khjf*ݙ&t#jիWwv4OOmh<]JFQzӏ&ns+ܦ/0sICI&hCBlP?7%\y s<'~#|&iAJϹLF1pɅtE~F> :g[?7ҙ}I;q;+$k{RBv*%wl(D<$ l$@~BNR u{a 9>i ާ rn-s{GAI-"tҺCEЯyL1$AT?{n㿡ێo w%}aC?_?Ƨ$IAP]AҮ]n?os;?s~ ol,KH$AAuI{zwhʓҁ?;:&ο/Wm㏱, A$1Hk֬nyFsQ$@  H*I$@AH$IA H$  H$IA$@ H $I$@A ̐'u  ' $xJpC * H$AA$@ AI$@AH$IAP iҥ_fΜIbP~?c >ٳ#L<ӧsU HN&6Ei$A ImƵ}v;w_{5.vT eI bH$AA$@ AI$@` I$@ AH$I$ @ H$AI$@ $I$ * OAAP9OoƄTHق!){ wAjkp|ufjA͗mOSGrj_jwwQⱆ.Vґg/!ۊz j:F $~1t'ѡGO~]ԩ]#>;x:cǎò~t1I*8)y:!`40!V@b;l]ij V;k/#àA~ڞv  H:##1.衇-LKSs~'}?W~TKIcJMZ{ѓļH/lpx J~jo?!j$"`D&`` r\DPеb+'z~נyAN_=h4IttW~MsΥ~y6Ï(%$:r{[_[60.t)=(0O_o֒W[t_C%T( - BgM>MwN_:]k3f}GI#:# $0K I jHN> Tp[,P ''$eEpZޔ@h-˓or ^'}*w-Nkߗ(n' I/P/烈bK߻A1ciԩ;{2NL~@ҐBL^(@R~fHIjvIXc#Mi^<$cNM{ 5n?ITϐuVZt)=c˫xj鞮itH7͛G?8"} @o($p{p=ܦ/0sII 6ܓ${e%gaܞ^rh0+Okk.2NW]}]{ڸtrя~Տ.+$'IOzu 7ȐݓS) NcC-'> Ig |0TNK YRʩ}~>Ls^A,Ƶ[ss8fϦcŋiʹm6ھ};kcڹs$csnÌۘqIBATLI$ 0$ AI$@AH$IA H$AA$@ / @ H$AI$@ H$IA$3̬Y^0aBjoo˗I$ !ʹiӨ֯__}YZjM>+AR__$1 rIyڸq#imX$T۞$lذaV[$M6I 300Kb98.H$`53b† j^z)bH$$ğ7P` )זå#iǨښckϴPzm!T-{f!},ն"\ΐrk{OʩaOE}ȓ+zZN.XNsXNkS>IIAO @dx^ SOIʼloղKzI2t!6`Z(!8#x텄Luʅ]xy-W6!OI]2m{VM$@RAX(:$CNq7 !yf:"B\csۖ$I n  SXYO&G/V'Ua*N;H† 6@IlaZ//Wo$?x~$bԳX^RtHIg+ユz:7suj] p;@Mr9m;cj"HY {?.Pr!/kԓ2XܳdHk`?fڟ @6lذHZ~=&1H)=̃4o IyA2$yȊu IUcZU` vX%)'I#pea@ Ad MIQ 6lI$'WߦnQF>} tH3 !IgH8Oj;I*!92xVs )$6l#%Y-tSI(. Ł$S;IFztH~+'m$aÆ  ɖދb3%(\Ch{"}ѓbx 5 $ H$U<$ɉ oޠ-R]I~z${8Xk)Y~lO)g;&XA I"D Hb˓InÆ  Il $Iذa|H.,nLg9ŀ$6A% H† [i!K TL]UIYRhP:B7 H$aV8$1)) T'&8 IǏ!Od SHCy@j\ |<"1?4;WZ\Xik<.4d|ԑA5޲AOBO9/j}yjNyM2{+݇* {u]OD;j}QﯵOǩzC$@RA @*'{e^ʜ[@Rd@eV ),ܓg%(d}RwB:IjM^&%!YM\V2wrIvP*ǂۆ>=ҝmGS)Q$@ u81iWzzjt$,Yt I\/̠/vH 'a$@ II$Cv[*y}hkuH vD$I0@ TO@Rp&-mIa$`$@ !)DEIm (Fz X6@ H*zR~c۩ }O$_#$D"$rM8N)ƣa$` R SccBHҕ૱?P'==$7 ĮE=J q6@ HjHbpC1!HrԘJj x`T)<@6l$N!Z:4Q@Ay~! S!#ɅQObupRL( @R$eM_$lI0X]{8RTd$`}aC {u R $Fn=@6l$mM|0=J$ /jHN`7q Q0$Bq1 p# 6@R;Q}T.arr)$KNRo+ )$BMҁ#M$5HI6I† u.KRCtb:u` %pp[p|Ô-tޛb$eBm׳9IذQ>&- [}݅PP`y4ERnc,h<&J{y!]o/MfB=꽼zrdG\ + g[CCSw{l>鞕v1@θmII$8O_r vwQK/c 4 sQ&LZ萤8 I] 3I엑 ^-i$˓?jMncXn=p֧_ϤHeIϸ{tm 6jK@%qێ%:q-֌ۆل%Cp#ϖ6l1!H!y $3b$@>2@WFw7ʹvl8[qf`f3`>snќgzlu'gW*$$!bBR$IetـȲP~[?|uu:vA˒G"/T91--gGWNGv,z_,|Q:X Q'1`B ɺ[1G@ H$v[>!p= 'yI"lRE$7>%= :4}Ԗݿ6ic}ɽN0v:x?C‚E I݅LSa9ݯ`J+ȮuaW7Mf® }aYYt u4 ?Oj@)?r+ޗ|H$%$> XtH Cu\/Hd^3XnPaAs\ KXv,b_^ s9$`^k0,\!BnA8?%I.TI?&`Xg&"L:Z 'wnr/~t_j?'|^!<}[t H*$YfFoG&S?# C!x|; 2na+d{b%렄*< H,'@(@&c5VҸu^. |<&{wy 3*p=̤Eh_GkH$CRII'A30w9;1K1ZNͷx,/FORr:D3NWx@Rd@½%5B{$CpHE_m` T$/m~jo=Փn~_K$ZԟkZH=#9P:`BNjE?/ Y_sS7-BxrbR($ydn0nB;J.d jC:OnpE:Đn-ܦMuKs6b۶,Y@p_bTFHꡔؘtejlIehOI k8|)1Rc"fƆۑ:2'2~ⶒۜN՜G*?B(H[gh͑<`:t>Δ-\hy9!i]-+m;_D/OkH*$1$9@jLbgIAu6  KncޣV>mJ~^Ԑ@2xos`H҅b@R!F@6l$v Ƀ<,B hZ/DioE$9FH O6l$ @'595uwvp|Ô-tޛb$eBm׳9Iذ`0@] ɜ(N󎹠! I܅B6%i |M]s4R~%=wI;X0uޟ(ec:C= 담(mTN3WNϰ FHb M4vmO#N"镭y Ԑڨ%]ٍIF]jDZƣHǤlcK'Oiz,0"٭[4^[m^z $r0vu/K|mXsT3n-6fYvNiG?7r3n+3+{7[`P$Ke4ld ztlǢEnR·n I1Ճ a2IV :Z-+6 i)) $fo I/BQi0#m_D;&g:aY'J_N\M yg!a*"' HCH⃱0IѯU鐼CҚa3֬z ",h@0"S EK d.'{d~}`^z - $IyH҆lUu9--Ԓn0bmjPhsl v,/5̞W4Q0@ K$$Iv#Ϥ}h~v<yx@`^*y*E޹*\$$Cޕj&}YzL^) $ر#rrؚ[_]91LW A I;6G_b_n{r~r$$a#!TD:yraLZ%s\8R)dE${Ik.G I7|3T&JΩiQ$f]ivS=N>t ȹN<]C} <s/؎1g5m'X!iϞ=!Ocie)tSh;Ɯא;w9;;Q1frzS6ڜBSƈs6L9=s Tv +/{(*&LB!)k>Ka\n9c c$-$hBK <!9 Or*ɉͺ,fS)hZlE-9K57?'{lڦCKlmfcL*H rQ LMOw9`ebt(iCI8OR ͔]ɆYBo;N8:ciz=gŲ|:8hʆ)֧ɟ>6i;{ 隍* IʂӺ?^Қ 4M7$@ V͐wPm rθ6ҥ/3D{7Mg.t_P{}f?+\J j6>uAw/U]xiZ \ֿf_1G%YXH )*$G9ST"H*q>{"ΦG}mzoÎ* lҴ} mӆv{$`}E+Mzs^P>t߫w|?zvziP/댣}!}'\jb)rц0d:_ miB_f/amҴ[dHse};}>6-i]I$b!(\7-O?|xiJ7{ח__VXhV{/{V@H`0X I!Gٽ đ?BL:. Iؾt:;'?#WKgBʙKv+&+#>hx/tgWV$@ IEnS*x!'#lWr<(TS4RQ IDRI$ yTOO(m9<yQ!IWN2=<Xŀ8NM`0 VH 5[bGDm5q[K9S.Š& Rmrҵ(S6('I`_OףdI0  `! $`0=IHC ,`%뮻`0@ H`0 $`0 `0 HI0 $`$  H`0tڸq#-[ON7|3?$Tbq|+`0,$KOc=AiԩtwCPk;=\:s|}+R[{)ȍdIENDB`spyder-2.3.8/doc/images/inspector_source.png0000664000000000000000000003543012626055322017650 0ustar rootrootPNG  IHDROk>v:IDATxۯWH3a$4C^0B#AZ\:0~ACh,  oa> 8$ع$d1v{oUUo磒Oyպժj}{H㢭 )]vk.666664ˈvXF42Xx&OHzIO;ӽ>DLLH9DJ-[v{_)%e!H"~Y7Th-ڍV;vf%G뮻|A{={O(XZ^|jXjw#p@c \v=ۿK /Ν;pҥfw#?;ozF8|'~@oEzE GP;Rnxvwy'aɓ'z8|?OW:uO?AٗFs/̹Kgg/=¥^o]޾p;_+?9rj[oݏXǨLMG{ssȑ#GtG-t.Gy{']v^zO>{?zs/==O<ijhÏvI"id'VUHjSVr]t/^FQݱcǢ_yַCs#vo JLLzpT8geJnO2yvG +_/I/Uh>Q&ch/\/_x/˕+\v^{zw~' q)SlA Sn72 oU9vRcjLYߩ O;X C Ļw~*Xv`DeKI4:أ@]Lhu{E(HMHvϟsr3>{˷otҕ+W]ͤHyc{Wʡ<%]En5%rݷ^Qj5n*XfOo&4q仂vʓvRT,,$G]z+Ώ6jw.7w}K_:x`$iO>O?Flv?:諯Oyxpv/_7_m}^[~idqr+.+O5|i_?Eԫ1V'c"\\̧gPCquӼL=ֆSvhC2BVjqHȩ}WHFQ^ujן6[]]|Kt\{w)):G[;9Tjk 6-u=;꟯X!CdhSwN 4ӑQ,EV4bl6M':i[eӬŝrzfJc8dX#ڤ+I(욌v^K7.FzcΓ|=ueR6%ц+ѿ*uO.D"h@,?*<9[D,;DA%ȣnO,ꤶ$utDFG2'06u8Ĉ5j%f!NN3sݡrE׳uG/Y6ZvjsWǐZwfgwEká#$]`X2 -WFs[DYV3):G[ 83jgV Yx}l 4=*qaOUİvsVasSGi[6U]T vV61Xk53ohߵfɘ\?Z#WR=8D!QSEov[jg]1t'㑟*WDeEdž@aqe쉏.yV@$3}]2?>PUsjrPr0|L410;ʞub]9J1gK3)x2P~SLuK=F -vb1VWזj+ٛ63Qe`&L&j7v*T'3q)l[*uJA@yvn0nn(o)/4[-Qc>+R@ީ;!}[ASGE~T&lה5kR[D+Ķ#t2H MBfg|uC"Nn=''WMf=oW<Yywhخ%L [b)-vsR;#PTA2mX^o8BiBfb U |:|ʙ%;\.S>| )kI.+K3`o2%݊~ogv?R-zU9BZ%RlUfzVS^)*J|eռDte><*r.bbqeՕӛYjJRy݂o Gy(br7\bs 9d_mBlӝ!|#HIѪB;4լyfr>KN]Kbq]),z][BN L˓n)~okU"X<2hN8QG[M>khs4k%]|O uyy6=;֞*AtvF.̫WOޑFXP%B:vAv?[tUQ%j |JQbf#S/3nͮan9(6˧;Apgqh%G!'h FF+%h<׎NnTbrnHa?Zŝζ$XHT(6lqevi$y-fғj^'K79o9S'ũAt:%('9G'yɧCysj 7Z1F+ܘ:ٌotFZS9GR]"gϰa^%*+%תݪ:}}|75sbj缄u”TAO=W_/` 1)~q>%Dp% h%"n}#(!ˁ!OF+'hxVlpJwmRJ=MkK;J\U[zD s1SBZrG+ze5kPN(-Y6E5d6+~7Fl\Őb1@"!=ʧ7e*r!\gR/TY/H!],Lvvvvvvvjjj]7ծ\mRN[nFk[v v]wpkۣpk h^[9 5lg@bǹS:1} VA5w(`کSy֓eRR(LNLN2y^tF1vvrtrQRaD @c b` ,VJP:NSo;Yu.ک8y$c<-M@]4ɦFB8fR,> Cs#p>Ki<&4ʕ>K7;P;@|--1`|䦼K%vvvvv˨v'k|B-t*`Q.n4H.ou %d9wn k!e&?56@`hY}e ZյUU3,;@\3xJ2ӴGO"WWoȒ,?FgtGAh 9Ñ)]oҁ!s16@9wNyx1FT]{ְ,(y-/^G{^|D]T(wA.#2, J@CC[2Kj9sWj P xEkI\x[a >nvj',w SuFZ'ǥ#qu,kcZ9k) vp #&6'ZRS/*o <yұˡF8@P;@P;@P;@Pg"nQP?+_hKw&*^ \is?n=M#!ܚC\Xpaі\zch nv5fC ,##mi7q&*S7 BkvEeW$"h^Kb:9BIEUէӓBܙ@VS2Ez8 ecϮvUoYlWg[ 5oi/Z7mMg%>2ƌ P;N)W,-GDzjvV]t=W- Sod]u5CCm;^v};= k=PQ;QSP]=jvڡvڡv:3;.Bڡv3Hh1yEj-66q<j76:S.Ӝ+,pg+LbHr˙sk4Oq D0>v+LP=Z qL4fa@E2O 8}4+{).3 q& XQ#8b?R$޴ 3\ٔ8j@ڨ"mMFx~ĄDכqKCDb%.;uqV; ;]kl 4ɋy+~j'Yd8 %\8Ü2%OP;nv$c߫>$vvvvv`-bQ.l<5 Z*QX%ԶFSmE[!U<bbDӼ:PYk%m3 9G[%uq ya>A(f)K4ͫcE-% B!%5mkvDe5A' TfIȵRֲnB]\=Pc-ORSpn(u|m\JrUqXdN)xW}9jǖ=V.f]L+K.3/9C@%3]zS̜$ ^L Y\UQ%l %+TKٽX%{|y=RSfĐbަNuVX-_W>[ܘ[6ׅ +*D.k*ǖ5ŬkuTk b]mu%z,2g"A{/ZӏEn&df9R-%ĊkCewNlT j9qVz|mt2UO뉥p_GI+A*zzW[[\4D!daej7Ou7MՙH)) I} ~\66@dn5Sj5/Đ. J]VI1.WZ1lT핅Ķ5'.Lq4*ZއRhk2Lڙzq_Sw&-{lѝyﻶG E26JdNy*Ĕl FrHDA>GJbȺJN&||T9F!8첵Ã̧xlZ]!WBUyW*g>/-`fDeOz2}O|NPNjI(V(i׳vEӘvHlJF5!7MĚ?U*CMW>P/@Yw/HOy>\.|ǖpxqYy HigUʼ+uԎV)<˙W=U.͂4n7)ylujjj Pfjpk̬jC0@PN2+_jMY1kd|a|0ayшcbo{^g"ڭډ55FPޘR04K{"JGEZ eיvd Yc誐wOYC\H24dqe#QvO{jӡF!Ӭ#IKw";kz,JS,ubjP#ZcTuys5-Q*jφk,buŪi*gFjdl!>*_^v~5FB5 Ieש4AP;n=[F-CK0d@P;P;P;f @-E屭njjM'c[3P;Ǡcti3d6@cٶf>>!UgzNtՒP!v]޷+X):mj=wfTuXu7;NZP; }A\y`$,OչUu~1+j'g<ʤeϝFQa>U[fofO&umz[r>[pokSMs>@nשuZiR :cp [\Z.STs\+z;.s2 =V5K5dv L>C4zF(6GmKR4DӜ:%2h>\RU^'dbúuݵ$Txxr;A;8?ѿɺ\rf\ԝ>SVmL1KNO-+B bmS:kͷ-vˡvBMs!v検ꬅ!=o>>w [Ѥc31_bBvEZly :+x+M6Z5#fl`B.1ruujoMA4R&քK{E}ZAn RklTyxC \[lbԼ#!_"W :5\K¤YhΘ>&<tx+ M=0wzy%Ԯjl8W℻!Ѯ(b>2Ms'j6 G3|Yͬ} S'0Iy]ijޑ]VɳꜮLBuJ'l܊UR,&8gevӞL*Bj_{?iNco )ÕiXO;j7ў 赕7 \dl{@P;@P;@P;@P;/Xj7?JaPGufv~q볡oljM } VīBO&PeV,cY#OzO^G4:c^kݲ/p?iRKZjWN+G3\KMvˢv Y$g 9:[hҮ#Wx=ů&R>Y[K-j4jΐ2=5i{щL®pNWJ^#6D5/Y;X4iW"nyԮ!uϋҮpQ2ݮ"V Oj-Qj}{kDhѴ,jQſ݌&D5]hi x}:[X̖vE!kFI%o-yJ@-#v0^/FsرM7G뮻Ґ"1-dό%20 Ψ޽{Ϟ=[2 DiX7joaF% @Ft^ިΫ]ǫ::vcjC%B`ܹsSP:fxDsͪ9eRo}B*L(}r&oWN^Xfvg K3 ]lLKR-ih2?|N 9oV> (ڝ)ݰ9K njsGtT 1:Ik^x;e;Sc>]=ٲ\3X2-*94mAe$;Nߐwڥwbe.%7*3'%b9& #㸼lfapkzՀI3SH1LXl\JR*N%G~C| v"+v|N{i_bш>)S1:IĽftj}lffݲӏs,՘rKTwORAs I%w*8Cf3+ ܶ+JNn Ov֍sѼlfNAvwA#+wdoAJ)p0N7%njuٵ?kh4!Ibߑ7yW#5kfDvn(ΔÄ3݆W*β80p¹:0v`.vwfk*OIn R;|yv~1q$/RifD.ߙ)ZQ;JͧI P>Jz"7%]:3-;Ecj-P;@P;@P;vX.rCCӐ3IENDB`spyder-2.3.8/doc/images/editor3.png0000664000000000000000000013151012626055322015627 0ustar rootrootPNG  IHDRZZ)|IDATx읉wם?x̛,g̼$93;&21yI%1iccLc#1Vf3BHH-II*EbUWW{oޭԁRխ~w   ,  hB`1AA4dA@1AAЄp  8  :?MTAAP(8HgH*P:  JW2 ! R6JNpuƿި󧧵?! x/h?YlD,V@o: 'ah/H  (88 FAw="AS@#CA%t(IA lƧ3`hB7Y   JfC3 xiwP;#z8AA_mq  BAAAA AAp  p   h" 8AA  P 1AA%  @A:X\^^VeeeK,A;=f'|jҕx ov([ikX'RoYYYzkjjHqBAP#Q`Ikߛ )Xp@ehhhttjP׃'"v}>n] #: ,u^Vg=~΢ջT+>ߑ~[)^My6-Y+ǟmt뷯|{I8sbg;;g[{[z*u ;6t䅂ݵd}ɧoݺ%Y+7#biwiCOpb)qollȏvNŨ"Jjrl^x8ѸFur{cK, JJHl/z Nt ])إha3ӦZXfoK._r nt%O)l;Y';P:GANd]u_жa w8p m6|ȑ8`!;KloLR?BiQQ`}}#u4wGg/-| ?x0Ifrj_ק|XNSrZv AY#'^gC޽лyo(P}n^^-ww=]yٱGcA?:nwE5(O8qgϞ?/tҕ+uűWu0v|wS'mlIh |c-zb)JRt`SSS{{ɓ'u{{{_vb}{M=OϟĜ*>i0 !8/@@LB)+}Frx!vwk@ 쮸G?qe;x{3yu路.߿f;+_woܲ3A8w \W7: w+8/͝IK' ?ˑ ,o+8X?vT8qk6][|b>{ێN"Sȕu.xoFqR?BiQ.ƨGGG)Ww+㵍f{z=ϼ7eڜ ojl fI2XP 1By} q݉a4z$C%|)F/f8DC&/9wP[ԭ5C{9k[S֌qڤ%FFF+[ׯyV@X\26 n $~3w+{}坞B޿\?&t3rV-}aFqRjjr.Ny꾾Qyo5/p)Ă5r'N0l+5ıf&Ǐ1D].\HNrgY`S0:Jo-`@Li-eڑϛGSȷAPqAG_r5|eK<>x֥nk%:Tu֍c,$(ŧN:s̹sF.\t1Ă׮ݸdA!x<;!mw$;8wG7sfWtś7wCӑ틿wlֶvBRș[z_ZzŦ9`ZCRyT;xEqK8D`t#Lsy68Z\b1AE+ƅ} '*,fANm ;#ipPg4BX㌌> AH4JgA<uvetcs|uG߼7|G__OZ;bk"\=e[|O極 +pW yN|ho,Hmˠgo}_}+~/gJk dU^vB+Q3OPZ"K;zvwtttdd*ڦXD'g(@rh>gqn2N-P;),2 e1d |B+S.1&)9Xgj O'|%*xmf^b8"r--{w8ǜknAPbq"bAI\¾)OsS]WdxfO7=Guc}n'R }Q!BC:j=zt9uֱ3g*1;[[dؘAֶԵĆ딴lbM荘x i~i}ho;ܷ{=@/W]G*1Pl'LS*H:}Y"'(x0ɌTڧvC#;ꯒp@"79n, ^vؾ`'gyZۦ-/aSd^pE,$~/(«$pВkh!WIq8(FAAdpc'/G^Z05//S{ކpo„v᮱*6vD',p tkWoFyˌk[jcABSoGŢ]TMM]뛺[;8wo,+7Q"?wzzz.]444tӧO:uJ!ӿ <}orGb⩷DiIQ4'IN.bfrE*QN&`^5S8SFAqW =_ymy杕T7&^?嬥Sg f}&klv횾-[C:z^:ppbeTL8xfj$Gcpb)JGz]mVSSCo޼谹Y [Ǧ ȏ{6qhSPZ2q7 lʎ=8a2j eNχjjCdBUUUD{cccTs~V4I <}oq7:E" n(-0;:@V^<Ɂ3u+mEt|d BӼĉd!T뼦ᄋw_[a~i,ɡ3/zgz?뱾'BfBB %eeeuٝ;wr-L̚Lm[p~S &[Iswtϧ#%ʙfVF ~{>VߌW~7zpS`86 u eb7BI~༑E"꧂ڵ":H***(ZO̚Lm[p~~oo/>峽힧Oybg|A95(dJqoX2d`o}JПtN }$,=wyO2.Fgqݦ=n8t̎ߎ}PПtN:2qIK\bi 666i&LGGGWs^m4ssy)d-|Sg Qpn*8 }Jv}xhiI`͎ڇE=Y"ɴ"I6*.u ( B~пN1G:lʔeGNTpPϟ?_ПjW`kusסdrj5K[p·?~{ʻw4]/,L6isx6n q0*qәpxפ_ORa.|=as_4ˍ3g3_9/%q O+x)ڔCtN ΌIYd7˴"6*{mN&m5\:t,˚+y]?8cFЌй#(gk`3#Ijۏ :뵺90sM*% 7\^ZU+TWYKLbN #|j￾WּWt¥3 աOA=Љ 2Jx jZqr;887~3')Lb>~wQ.DtHpCMj`Phi;rlo5Gnw.ی1ך]=Ni>d3:8/_va̶eGmt9m=F feA]A:Ĺ&õIjƺbZ9mBG.^x֭qK˕8D`t۝\f:Ak#\dl@ipS}Lx,ҟtN } mvK2 ,0UI{iy_$Fqm}-vu=T?Zo=2u Mxכ<[wIm9k~Btq{Tw\OE]? b? TUUS=^__Ot<5i nj[WeƳkcN ɽd՟z*ڦXcj?Io sy2pA֫g:nJgdTAP8}ԩSJsM{;ໃUH8hxe<t*5NX_WIftxcwiT>@#*IoPdQ.UXZIQq-qQu+ gD?6e2ANӽc7pZښj6s:z% yL' qpX'Ewۦ9e,{QK&Tó;l0==Na6)ϐR۷; 뵺90sM굡ُ\ܺ2UZJmjjj͛7SӐ/M 8090UILd_)/+?1켣߻lV}\T_z\}TztC A!8 BAAJGBApB$残ŋ! 8e}t˗\266vڵAݸqc\p p8AAA(ppdd͛˖-d'J( Nw\.e}<9.SuTTbcR@7Q 4@+27AscA 81B WIBfV%݅WN)M L-Rja $ '|*7KSO0ۇi_U0{Z=\늮 ]C&K!!fQ l%q]nej6-bN8AA UsΝ ;vÝ $qU>"u<p3⋃aR/8S93p >PF^\ѳs2pwv爡LB8 .]9k6݃gryÚ1h"lyCj rj'" ؟6Yϟ6zlruȦcyB`6rKd:K-5y(~gcR Gr96f>m^;H/߫Bx8(7$A^ٝ U1ukBAJ<T&;P6}Ѐ7 -X !JNn:<}[`5c:X$aȼ^"AM4n &mv6RmhMn;35[ӡwGl<{,34:L:Q~?N9In\U<T!mY)%2ks.pv S &4&88hC aAF682fQ\<08X!'K>/A\LIA^uStlc`(!8€x᠈dp0q5&[LGܲ;ZL)! wm͢aj^!Ȝ⠀i I7p뭲,ֆwO|&2[]y؜ILWHAʓviYqRKB7r.ry?bM8AHJ"uAdSY]V~C{L`:fↃezъzCœr%0D @9(FFvd2AqKz>XaμtP l#)S9:2h_%2n6[BPqŋ[~}^^^r;\0Ϡ qF78A /GXl~>ge 'u6Ipr(/Zkq%ߘпbA"d;q* D46iHi#nËޖh5>(S翻%I,3oPHB,-g~P_a1-YЌ>4cnw^N!AJbUĉG(eƧbℋo+u( pYEI*Z%*۩{3C,!A84@͏'&tl%|%?VRi?p8AAA8  BAp! 8lI-+"1Cd\Fd%$H/O'E!`<$>@m:F+B wk4X.1dȼήXiINHG~,>c+f3ˣ8KȌÄl>qZϴHK9B1 otG˚۬qi\.71qLvs,k#F]og Bj ^X_N96҃V+S.sCwqZE3+vOCZ| qRɨG?n ܚd)Xv}Wc9,n[3U $8$%p\'hc>}up ƙ±\BP_!A8 O7 aVr`29` eqʙcf m]!' YqAeq}jNP4ּ, <~z dp2il*_|iNiR?"Լ]BA%d-fjy8k8sW\ܸHaH fpFVo )?˩y>ΫOlC\)AgaiIr-HtA_  bPb L,_& CCi$dы?Ā)w&1 qS8 Y羋dqseQtX|f6!kp0pĂA()ePRq莙I4#*{Qb佑aEaDmcK-q^%4J"Ya~$qл".u"Db8o0`+3hc( "O顂o@>az Byܖ!f /XGx&}uy}ZĚ%gRԚR^I\K,[ڦ,Όg$?4c;z-mxj)䨍8 JTO^ּ؁p8A*0RL%R>@A hb9p o] 6lRyKlaÆ 6 6 p6lذabN@̿'r)[Nn7UE*WUM>Ud=f0,UeSOj˝gGPě SۘH c;-#"ק.uAxUռtzM#,ۓ}^m#fӚbMTH3Vq$nM;!u]&.[GLEXAs`b61pP.KyY>VԢXо:VgƔ[7W{5 ;N="A}_ Pz&c͒,?dR֒(ukZQ߮+AKu0"{rAqtZKTsE)ٗK( ,"JsLZ!z46 6l;B$L)@?Cf'B?ã3ft׮g7[3ȗՒcd+I˖H=Zxn ZL+Ē'N;I)bɒ-_%i&]D7,6 6l C  #\X ؋2B0gc{0["3pWK AAV㚗(Qfȥ-NAǾZ{.0gbb;(* gM;Axi23ȉAӌRp`Cl q:bRW7u(ȯ: qZ`g8X=*,fK~Sښ b9Q 4ԫ;ؘynpk'?KeYq\ewž|A.6g2 b6*<5Ďm .w ;YM#܈ml؀؀d 8MelgAz؀؀4Au p0YaurjsĆ 88 6lذSAz(}  BApN(䄾I&)oN85+؃ʛn|y|b:RJ_KUc 8ʒ$9'㼻0 2Aهj)%S VD`xEU"ܪEY!8AV%+-R)(p3˃?Af 8CR-^b: !`YlD3 74ܞ*^^EZ@<\1,'qJL^zP ed}9'm0f_"و9tOƥeSLA iٕ64Mԁp;+ s<''SZ2x}IT^~2m3&#!8=0*M+#74kо:{@ÿJ Nn6< ɥ.d*tMt%8((l>rc%?ZL&m PavvWm!n`μOA\Z2M\y^gBp p&1Q`YbP)x褏z@%V0K 'K8)|rL?!$ yZxnv08R+8(h~q>Bpf,1(k?֒1ya56qv4=pv7f'!p8  `j8Ap〃< ar3^  8AA BA A! p p0CpPwSz? go8ˡ ^BXK iGxRk bIO€+F QDŻ23p0R 9Oeza^yp&6+3-RY7hW%@8ADKm8AA8kGA]b^.˸ss}Z^68k W[op̦vbۍYq6<1K\f Av ] MYG?m, N. pYMj4 V;/s%W*Wf{'?, pחؕNܮ1`jWpr8<8Sa/<.0+q8'y8y''8,tC |%ˉ>SYJ;ۜDz97pBp0&l.MGb8c;1@ 'M3Pvsdf̋}D A1`7%`8s9zXœłɫ%N˛日n@pޛg `hqPG`ė)Öi35B 8( ɋ3qxB$`5}vTE>V"0U!EE8P}\fcPa>8(n{сNdz~;Q)6T2m;XkI/$s&>۬]e?6;EMl,/qZM̢0Ō-vΰHdpkfmx9qj=u  QI'C5~Эѡ-r~[;?J έbܚ綑s4W'*cco4;@BZA!PNpdhkfmH /N4  c!`JqPs=4{?H))8ʼnVGh:/YʊLd-"N!bƘy'c ZCv35B$p'ppPy9 'cAu~#x/BԯY @x:1$4,`8*o{!``>c, cF{?sƘOIbZF|s*?%%jxJpL1}Tۣ03rĭקc:k (Wɡ$&tsp]1Yz!`,6HBA<;Ic7iq}{9KQXSL㶤f~ߒpb&8/ %~쩳>oEJ &,>ºewnodBx4#nC3Y}! >C e &ɑ=үBYіvg&3pL oX1*$MmOb}AK5>oAp8AAA8  BAp! 8PAewgK)11Ud2֖Ұ3dm\d+QM@Rw͛s/wLi1-XN^IqKyAz$+.RB TyqXpnͶ|y~}'SVLu^%Rx#݁ń?f1d;V -yo\JwRٺ8Db1\b^ ˻Z RWq3LJ#<fI(Bo1g\8e ף.I[" A~Wt0agHY pW"g8AAHS ƅG0h LE]98;(L]մ %yUQƭy+OvYnU&=Ngw,fM(;h( LBЄAqBgM"9SI]p9q>:AK>^P ^[fKY1C13^^_te8(,]qIC  YB#Jb4rhx2`78ɟ>7wkRo.+/6Uǫ5|!`YlP 0#>q>oz܆7,Sքc"e24-*dxOŜW3šgf~3_%Q֐?U=o`8֗>\ܒ^&v73KHUy;'=x8j(00:5R, k~3,S=BA >$Q)Xz^ ) R$vLiwBA %el̘B:؄ A 8AA A p Bp8A!8[-fU4Rfwv 7GƐ=嘾x_!6$ƶo6NJ%p1%GqJ̞9b8ľ A#K0Gf1a 4̢XBLW$23BbZs);nÙ/ 8*Q qߓcY3&/!(qд^]iGs^B:6cqE.}1BG2by4ޘ5!z[A_L>0K$]v_oúg4[:D^ΐy%m#a][OlRg^87ǭ1V0KIKA*˃p6"yT Cf pe<:(D?O-(=yQ\kWE`D&ݒ(` MlR*h&D% e<?(3a@m}?4˝|Mt=`U0Cr" ||ٹR7Wa(Jʼn+ J漻D8##>Kj$oٕHr=$} 8Ag8!af=n;bW[,ɂ Ip!HGFFUVVd5$p8a&=|>bL@2[UpJ`~~~OOO) BP&r1 q! 8e-ތ$ AlHBApfG1=  8Hn5!({D+7 4sjB+S~L/NGJƐ`gG~18"YL FNB}Y*s/)Z:8zA(q3(խQpY$"uՊPDRaVaVKfE/#̠b&2""!Ф`QLkB يR 2xtg=HkfVb. ǬBk@*GcƮh.|ՙVqUHeBف*vX4prn:0 $4Xx_yʏ3(!/um⳦ +$AAHNgHZc&4OzV$wl2^Dv\L/ ai HUTHx͌ Af py6~$ZNSH3S4M];vZ: IbrOBMNHM<8 aN=';8j +-+D󺢒ۭTK[ub*,Q8e.GI 2RcA1$n33r_}{,ɼ|)WLkH1e>Ci q )-%'СǛ[OvoSb&O\y$#VMΫcє8erssEŋ8AAMqp  BA!  AA  AAp  BI>W |'·סu=*++[dIEEpQ`,U IfB*5QppggOldWm^rOy6-_5w|u\~TVVzޚ}555566RxܷPSG%C~?T~**V^em][l55Kj Jiжgo!ThUUcGIu= ]QXe?/[YXvwCQ]iu-[WMPR,lVKKՠ5>>Nڵk|u7%$oOg/\zjWeEw-.]ۖݺL!MOKVlmJ>l붮X}K* |e boωmn>ثn?5xй vƒ%ud ܰ#KKzd(fRrUUU'O?SvΞ=[J[{:*!I{ZRR̬wO.Z=iMϿp֭1޹k mh۸oӶugYumC롎Σ_,#'**)+kv;2D[Cڶm#݃5m_W7._OY8mŭf:}o;o5v7d(fR\555'N_d߲kj{{睯쏢<99~G{e)FQRcG8M\"I|EO=?᰿؉y=S9=?swfY+?[˝[K/_rrG0v%O)l;Y';P: b:8ANƪ]u[%mAhoomi9pAlȑ#q\͇|l/e0Ab)JRr|>VR:i3_Z~s֮ uv;tHa 7_wbR,WRSð:͌*GlMV6EbJ#O8oϾ뽇]{qwSus>P\ܼBo[&u{ j>Yӳcs7U:ZG_?1vpڭ.8aTaTaTbTmb'be JM~'N>}/|嗗.]2zEEu`MmjLٶ 79Vܒ1)fR(EJשjjjjoo?y$noo/miSñ\*An&co8 $1H?dLp}pP|jOOkw{(=-]qߏ~pͫv>g[-o_]D;ʹwV;޸egGwͷ?a!) **jj88a/͝IK' ?ˑ zo+r+itځ(MW?nV0}X{tϽore]}/n=^GQ3OPZ"K;\lj{ttru缒;^8iʻ{S͹ɠM -X.a,Sf201%v,k7)m>0ϡ_~nzS?a'4yək;&i1R>14Enz;0F` *֞~WkL#ӇǏ,wvo}g{ռf-㞩1Z ==Plݷnkb9QQQMQԏ)3e̿3Wזxe~.T:wC`MqxVڊO&r5j+W+~3Lg˿Y Fp{/̪ܶ]CRg-=s I1 Q:Arյ?~իwi^X/2m,l/Б÷\oV^ @7!2 LmDS T!9$X90dO^r&˥&_؂Ija)'.e kNa6%tFtj'_x7/,oߚydRظ/vٞ wwu]=|u{}]tSjFFF5!Fz^?6̔)NXxK('r=qڤ%FFFW(/d]6>~c[Ǿ*kh0%ܝw_y_Uw+o97yedJm3(6bVPt)uʃ媮룟T_r+k^+:sRl O Kp61LJo:b|]|ꋲ0>6fexG]=‚V<cPZSN,(fȲERzjO0Qwox_fvۃL}@\f$ ?g]ֺ]H'z޺qɾJ *oNM:7_a\G8uԙ3gΝ;7ra⥋!uڍA=oQX\:?m9]_ysMeoȿ|p_L;6k[Oe!)-cw-=|bSME0-ڡt)uʃ媬T?uEqKu9?po+G:=l눊rZ^C3YX.槬k0>xdT - -`Q1eӶזNzlΝw>ƝK_IkGWH*Ť.-}MZ8aTaTaTbTp0bZ<+7\2vfLI\ ٢`[ec. zO{N ܳ?i5\pׅUCP*_/?hg(NT(-Jҥ=RVVK?dGFF_mʌA"p? Am/Y.eAz8 "ݬNص!Z!r tX.,+ٝ(eXO45bɉuܦi8df8?xTb8)fR(EJKp%%%J81こ \>8ƙbg>*{.rOKn[$>9c.7NY,5kKlpsW6EΛI Nkuqsԃ*՞vHHƞty̽זS_^XW>K W?~EtyI۸W"o;~pa՞6v [6q4?~3]O… kO+|rW]\-eR墻vhbSSkkk{{{Od"_| $F3ůO)J^ZK. HrrLCLWa/{ꝿx^^W>>/-ϼrsޒ&n~+9kԙoƂYέkl"J.c |@GWKgkS5`TaTaTΨ?W7Q^\Fdnެ~9ھ|AmȒz|mzމ[Կ1Qb)JGڶm[MM [\\yf2dV [Ǧ ȏf߯s>l2/jEmbN+lhy ,UJCOx=FOwaU&/AiSK׾_~yիWH **j`ƙ/V莸nH?Yw5%_ѱ/"*/NAgaTaT!L1_( Z.h{:88/! &.Q`* ,M###$x⊊  |傠)*  AAp  BAAA  8AA!(K444opx?Xl܆1<<<"G7Qo(Wp!ίke݁-ۍCVW^y衇N L£{~=3" sF QX̗k׮]sHѣGٳJ@^}{l˖-U`=u^͈%k6jj, ̪*,yfk\tjhٳX&%B[dDX&O۷noooBL?|̙7n D(S\͈%D`tuܹ(֭[C.BGXJm/-2D5MKK ՠEw,ѵk<ׇ(F/ILSQx.7#nC/ف\23_ʳt_m,)jÆ #kJ;%E7hxkllڂvNq~ɚȊ\sDQ/`YYYuuu@tvΝܛV w,@᭾~@S$ê[Iswtϧ#GOXIl޷O7#}:8#g(=;vƭF\.uNQzVl881o _ZDV-[&(̗ؓn]vZDTEEa w Q6Eˮ^g.l7hxkjjjooִKOnliS硒$9I*oX2-?;󓁅a*mCA:5 FcIuNQl0]lxNpMN\W̛HA .ɿYD8,(FaB8>ApZПtN:BwlMKEȀ~ ֦LaD!DdR_l96N<ޔis{2@qu`GO^WO |46ڡ? ZrD<|0M.Mt87 SzSd8MdA .ɹY8L0_ Rj;|mPC7m]]HG6Nرutٔ)ˎ%@Yn tXOEĥDokk;~85”wi^X/2m,l/Б#5Y4%DtJmtg̙@iʏԨ}2z]?w6eSC3# 5r1R) ^͘07Ef/ә*Aخn|sIǜf7\Xqp#Fa8XQQޥjtj~^HGqwe{uy]fJ5T=Λ?Tٟ1̵YKLbtPm*&M67S8a`kn8|cxLqPH4яq\} " @(Ri6M-"EEFiI&69n6jjޤu4nH\;ڰy>XȊ'ymɒ_E,"(QI=(Qvfwg̙csggϞ3s'6q~o__.\4\d%U +Ko!B,B=0ԈTUj*]]6N)Lj xNlqvk'U?O|j)8XsZ<W+\gNf&_{ܹsgeU~? u}_?ꆡ^Ycj_qˍlgA\ ~|!A`IjAg.R^̹һO}mrg~=;AmxЫZ}g~.ioYlnU&XlN]% ;u蝌T8E_qo3NO|o: }=ܓD,?D>E7 j88R"wC׉|ͥ2̗#G?fA|ڟ =m4V[wp<_)NZL˺>;xA\(j'+:wҭrL:Acǎ]xQgy̙3>?jKlYA$*Pu*Z iB8{ C`QY-8K7eyg0_f(X>X~(G;凲Ǟ)enf=?`/-)>r%(ԽƯozԵ-[5ކFhYw`r>pU^KO蔻e n喯~[o8_Š|}??, VhH3mbv4+Poz4wqӧIT8_?KJ2V#_z8kLo:Qts-+ K]wU3R3_JiOArc?Q^|t7:ѫ/%mֻ?D٥ك㏋FGRDM7Osjap !p'kSɝGuc>^ԇ$u{@'Q`|$D%e6|$u1̎R+DDS488|'grqzOu?|OͬŹPGF퍽uSyСlnnWY.u?6VGadEZ@'Q`|$p7o. +I,㢏=I#S裏koظ;o> yb'(t{:Pi}ꩧO!D7DIͥ2`,#IAV- ;a{CDQp=pWfg3_ɥKtViSh¹p{[-Ї>tsDkp=pWfg3_XٽJ#ȽЪssZ v ՙJ>A$/A̗ QAhE|qaunAOe^P;vAQGB4 6|#$igf29Ӷ0~=Cޢ~o:4 pq^wI32I"&Ӟ!B_eEdcl浪B WvO6(YQWG8mwJm<f=RraD K;Tm i y7]i[>Oy;5ew/{os')8$a)ɣkvN?o;># lJ {;.sCZg{:8z=^9$1ȾS= 03;C8(f{Exwy;VεAU`[ak80R8\_#jaq*9CEHͦ.niMwzv !~njͺ8iZSwqG$Mk;#gtX+*EazT\*ܴInMnfO,dOGl9n;C_u֞)iw(_UXUJ&/ Q`beM8f$EaE YPod|p\AJ\DgI=USQxj, {8HP2=Uzu,¿҃ZlkN!3Tkp8h^2VGċΤ?7' ,+:=azgvqn&{l٩S>tMGݼ&mϪBfE2)~)q&YN{A+i3e~;-şM Go'z'Nh3|.*$ HżLQ`JhO|&$;LAS)"2OItНDQ;4sN[g%YɌ$-Y# YĻZ7=pADX2vJ]Apև/رE'V24ԩfh)HZR'V, )|!`pX+hZG6M4Tem =I6']/ h,8(_|ܹszꩧxGyȑ#?piMw;0^`(a#IfŴ=dF7  6}j7&)ݡ(LZ݄㠊jL@J V9Iwbdal!A|[B8xym=ggs)ԟ)Ϭ.֍g? dTw2M@, V޻-Z&w<(X|,P`IӉbxUJ:L!Ac3zxVS=\em&iRX栛2la 2AUq$&QkYr^ۭ^vu6eU8j7!_(`~ӷӳn~.]S݇%-ygB ̛>nK~򳵛j׳S}MwbKѝ`$j\DvPC;(~]!,ȼ=xNeUTaty8E`Zp4s5K6Mk4%VNA#͒x_Z31'tӖC%L /N'xO:hB9PkI; pZ\R7r{(F+użN* bj=RqR!4?OMC¥Aطo_bh3KCвqpydfl`>ǫAT881rC{  hl8j, AAƆC!Zkl5Op S AyB{ V1?_u2L'I7LQD,ҪN4bV6du<ԦW?&0Az⠙q)^%fy2W8( ~qYya.gE QbaV4H%8ApгN::> t9ېWt]RV, kU8XZ2z؜IX:p:eaV$ M(!t bt ;):}C5()T`"CSU\O+|kXZ؍xj9+nS,¿ӴTZl|е&ppbhKZ n}G $:qpYC4:1Yw}ܡH|޺ 7]*ƵYcB$%@޻wU*-+%ˁlokk (,=*Ef`O`z*"ŗX[ks>Js~c[cEd-:ujWW`pv/ʤ͌)X `철.@qzHdu.ڜ҂ȴhW J>6pkUAIS`Q>M{:pYsCb8QޤweW`1=py#X !̘AT3j %a/z5AcAc)m 3k@Ibe2 [em `dɧyqv4ݨ2\,և7R1kPv+JR(fbq``BAa 7 xp !ţh/ Ap`AAOBa?! 8A /ҳgyJ1e`^d )pplTg&bQ)0^ptf}3j)d®pOQ˙;Mf/:XDhv69Q 8HOaKy苛@BAaD$~'*'͂}k񂀃s+ka XYS /#5I,>I>Mx֣e:U$*MSfH$lOz&d:B޾G?u[>MPENBgV=onwSd;듕=*E]OQ &&5#ܾG)f7#N[s\S/]lQ m-tx'XһƒV˶3Ft o "XC?)YO 6N~خ ?Va_)L)qڿUwxbK1AƠ\ Djp0t30ͤ-1 ` zC:GiБLC AQf&8jX5f4#;Hmp;s4plw:" h/Y; /w'$81s4\t!h.%p|p .;[p8A)p / AAA`O!!=v-Nk޼Rz濱+SDfXippө2I7UҺHnO>qw%n&ޤcigk Ӿ Wrұn?*ۄ 3i66LVa̪:RqPCa(A@Db@;Y)zsmqhY-M80:,@-wE$E*O)ML,5eiUX.3.5&Drp}k8{Ws@;0=ʽNBT˖ϫ߁v8/*\1qIK_8XҠQ >-HGSUݎ4Vdϱ"l`eI[fuӉݬ? ^ᠲ]<R(ЙrSMiY|4dA/c&L՚X7w@7UƑtx +Q(࠮SIrY,,lv=Pe-KKmk.{iTUhu8f*یqm./wҭG'Ut0WqмA}7ÇWLgI!,V] y*8;HSϓ^q7M3a8ьtX 3Iq8,N`eh;yp- dܼt87];if(л뀃|0AU(q݋C?tu:?jzs>{k=rxҞYOܻ.g# ~e66iU+pp58uo11٢'C[:)wPJJٕ`2}}mp0@>sT[8~G˿O-4צeu]AΎAZM.;(a C4un/JDr`xYWY|Ur3@Y+QM3.M38RY,Y{X9 -)JL3ĪԔguTu[]}kڬ iI>Fƾ9d)lG)j7ܓ}Y1|*fcp; $--a8x܅?_SoOG?t?~pkb * -˴c8)έX]Z ݅H8Y&${qkEL߷l)md}@Qԕ?8 B p"ˇ@k*Ww04&\k!덀gk;p Y걕sE˪1.0m[Z^( kY#p; d-v^pR.`apnʿ}mI~]9.<{jXwu#jՈ\=CslG}ι]"s<|1ݫ7xQ1Lot ͤDZz鱤kClJLep/!9dk8y8=,ؼ-.561K'Iyqo6x8{[OkCeN8׿7}?wtp>V^`j?Y`t$F? 0W~ X`8yBk:2r]GCAvn˜ІW؈i:ͦ4W7Npޟêݦu}٬#owo[-}?W_lS$o&XQoPظ"p'}W"7ۡ@k+ U!arl"i7fbнֳtŭqPrڰ]{8 c4{K8;ge!ļR䅘št2u1ܥ]6?cU8+>[ \=i{>ֿ3?N==oɏF~䯇^{S~}JݓLK~xyp[ٿ S ppQ&_ie pp_ʷ'o?xOP~ W7%=o_#1wP|?_:)K-#N?O-Qm#?$ܽ9U?U}|)~=sGW`~5@A`Oqp_'ϷAuW_P(fn+Е2+|C_߼"?Bկڛϯ(?luW6%$^AA`!!=BB{  8Y;G'fc3i[toy!jAZP~5QpBk i;Yx]2];gI{ ByI\MVu ;t4~d&?5t 88a){=Zg=l8(+ml8ICI)h C4bA:Z#$ʔa] ٢^{46nv݀ON:ô˰4-T9ԬM-pJ{"mZk/aQW5BfqsWD{!bK,_O&cjRύ]MS,-öI v߸l,WĦ< )،⚸YҷJnY3pM+qЦuҟKҿ:Y?]@W_{JK0 7^! Xψ#rf99݃U^f:{BTϜߌl2>VQbn0tcwmWHxp\8(L kTW12_Cɒ x 2"~=߅\}!owP;ْqN}WwfBb!BVX0jPGQ0++_1yhnswiߛn\ ɶxϥp+ כ ;blñ ISߏG{k)MQA s<\jl˚{q$J)5X9̯oՋPMS)k53xkO mgCb>Pg v.oX>=ZgsWՈ'+֫XJ#x9O-ap#Պՙk3~fob!{/"Lؽ&X nE9zL RȎKkeXj$Hi1OiZݥS[Ȟ%^!Ji% BAkiYM n{cq=c C\~`*]xf+k#>Pev@6a?19";a !œA{  8sA)p AA0g3h'$t'Lߤ*mlUg;oq=%^Xo`eIDSP<;AKAnjpl8$_ ;Qx]͈7az2>  %8램 #;k;n- 8&@ώ6NL,&7%kduazm)ռTiԃVMHW+¿Tnc9&4..񺋃vb RBe&cBnr >GIIwPb!N ]*A8cB$%@9ev&d^ \J]r`hc*dnK07jt[=K U%YPU8«?6]:\8"FA* I!Hl9_ƄvheBZBࠋQYjpAp0H\ iz}l= pph{_68)G oxfcP!AfD [Dg6Ef{8hG뼃,vk8w_R}X=^hBmP-1QPVZUv} MgsЍ ],Ys %by1 u Ku8LX!R !8\W 4A p`O5'/ Ap`AAOB!B!B^Ly\ri++KuC\PF'z6 Q%LU& Ne*-A'jj3ms\W?pLI17q!%}w*N4qT/UcJJ*yݥ"YMQs:5H@Q'ߐfS9)2UwLaB?b}[%C oTq)&|Gc\fC^q;A`6X IfQ '>JUa:a40_Aa ҏڸU ' `-38іwa+V8{ؚmpP}QA|.G Wq!IWX4x1oF'3s3$~>qA!O4C_}q<Bz58u8XCɂqP~(OǒypœAl9 pZb)$%!ib+.ƍ=DB/LC AAOBa?! 8A / Ap`AAOBa?! 8A  8Adb?6ל?_9_t9S`Rc:zy㧘ً+R{f.M3"qvLgËq⿛KӇo2R uؒ;{m7-s뇣%F)rPy :秏q0_8Ȧ_Suvds S6$+qPWؠYIKTU8a3㠢I,tUv8/ကs=QW@hgxitA$@\6+S_܀Ђyks-6MuudY$>vؑ7H4mbRrPv#SAaESuiM$n%I 琅~YgYύÒBEq7&M&GѱQ`EzEvyf]p0 $twgz(I6_u8XTݙ3:.pP%^gqdeg%;H–,H|4e#m/m ᠂;. 3|G]g 4XQJ.UYk~awP!th& !Ri;IJ{KL06D  3HB<5 S~E~L\ڢqP~H4ża= z=w)Sr8BCY8t`$ UrnpuQiTQA|rkܐmG'UtnxL8h̼mx}t$X_ ^⩵fObB,6AQ  u)\8ȵ9g$Ru5w0K7qs2+>vy;FF7{]E|A;b^ehاvbƈNݴ[`k8h}f\q^, ;HCW.ip5_4C; nч>R7V@ BtǎklP5AnuQC==4^o&.͋9F箉,ƍ`p8 , o&vVmks/LCK~Νj!>F=lCX]Lw7"qh԰kZ&YbAZ=}IA pM_ ƗĜ'LC@짬nU &⹶ 77[Rp S˞|_EK|!=0%8Ap S˞8qBPs) A#3_A`O-{z6" A#3_A`O-{6B3L߷Q4.>V& )ie Y!:I}S8Cm0$+WwwO6UqPW0YIwMU% Ayi"'/p8\G'45bvj mB+1_` |4If+StjeeM]ɔt$PBHp2LbhBCmˤ"YMQs1sڦ'79vN^ AA(L羭mB -˄pmJaC-PP + jT)9qq  %b@;X|)L]lXC|2O{5hSNh$VF0 /۬wйM uAl;5+pu9reǬXi OaQztRM' |uH$lk͌+miS 4dOtuם={v#IĊ'nDfSTY)?8X70ef M'>O{48BK]t:T[vAU8xq֡ZOv}>u(X$ƨԮʺ^K LWyAe[6¨BPo80E F ¨BPqԩS'"$K|~%. pc{ʲ-aT!XZh@K /p뮻ٳ~e7BP.qֲٜ-U0,-4,%q A! JNBAAk, AAp  ZW&O8IENDB`spyder-2.3.8/doc/images/ipythonconsolemenu.png0000664000000000000000000001040512626055322020217 0ustar rootrootPNG  IHDRV<)IDATxipC?C'LɗS>tƶ,0Wl`;`.4NZ2IpB8 !drSfiHS da ѱ:lJ=]-i3һ}vc₹;͘5cfYٳfϟ?gAmj=lX֕7V ~F>AY92uY,`nNx=dfOkz 2DʜA8},d_DD[i:_쎨E8%30̘$G>[lt@0dj"L.x]nA쬜 >L(nt;_wƾ F6wm`0lش`jYYp (Nkذ;rl$;isr`lZ!d nXvkg0<<~lnƆml&$, ^hVkgMSEZF c6m…rH߼yc###n-F&Xדd]Ƒy/|P{G}9NL0$߿`ĉDWWJ)*\VɚlcK2.]2 ^رcDwww8].2@?_SWvk7V----c"֔C[@S>;:I -**x< DGw?_Xls?P p6rMZkط?9(N^!nvּ`ʼf$!/"Q|jlj|OR0yR ),,˰G}}}NAem*xb'_ٻ􅷗ZnQ)ijkI@Xp@OIpOMas 1!9U j(.| J07ftttٻm_ٷdu@K_~5(O?dZklv /0Mi5 ut\P3Ph+3dLu_=IX/ 4Lz%jˑ| |}zWP2BFcx(sw |.#&Į3p#"%ŽܗٍjfĪǙ< ޫW~wB q.,}eO ZCWS TB9p8]] ,E81ڊ ^`&}0|\>ol?^,EǙ< h'O 3uGO_ D,#(AtNk/G S"kzN&6]w!\u3 `OBAEj%/P%qZY*ju5Hpiiٳg(.[J0녰jZK <:JSǩrͭ>G]ȁk\C䌽Kxsin56\>F EECLG'E6l?7@J4`%6%l6=PH \L<}g<+h>(Kn33 T 1qG> -K`R&abv+bo{d!&E0#=P`Yxz}vo2l Neee@p{{; !e~8xXp:c Z@.$ ̽!LH{t M(q!j wttYF`vm㌱!&_,0&c"X@8'&'tbM8big v8`l#@`l/?|J9e:y1`~p NA~p}ii|#Ў}чo׽ "nIM,#?''_4Lfա64ӳG*yx킇ր4sՅ+(\DU_Y=os?Xg$s qLdz&L 7m-%U… 'u:f?^챕k׭X'+Z5-tͬEf.\6ti^Ie%:Ãҵ*e RMy:4d ~^d2lݎ$xպW~GF^01,@$4d+hT xE_2%xU:˺7V]]T^&Rdy@d=d&'Q=Pfp?wʲlj~M;6}]L;ZWNM`B&"k峂h"NU&4lQ=|rSKs+[ZZl V*| ?,+иq?fvڌ VDDrw%NXMx =%c~ݿ@$%8lp13 G,y;xVQ fC~1Tf^<ypL$8L ̽MAlswK`iT\|+$NpR2F'~ǔj|3Ub#*-q$7~&S/LaI$ d2k.GN, <9v|nJL2$Eٳgjۻw$%Xsw tL lGp| Ɩ755aa1RD3g00`l)"Ә`l$ciSN͘`l%Nm &1yc6%2B([Fj獀\'&[z|* Ɩi&ILL0%MX,`P9NL06M 2YF-# $ Ʀ U62 zrI />SmcK*[uD\n)[vٶ{߶]zoNZ[vmz]Vukw`%``uW7v{/yx>X5jNe+˖s+T6^lJ{NPay{f@IvU^~9$jHxmd#X-+ck[˗/Ck^^ɫb20Z"8_X|pr7*SQW߄,$iDlKhxAA,ŒkfbQC R1u>j uEKEuEȔ}0rʺ,(mFTPEcC H>]ʖ_NŁM* eEH[}ѮXQhF6 R])CVe(iަzgj*LV|MOrmTeMz=~x@#pD%r'SnNC\rr%WJv?F>e9y"(HuIWGngIENDB`spyder-2.3.8/doc/images/console.png0000664000000000000000000004553112626055322015727 0ustar rootrootPNG  IHDRK͝zK IDATx Eܛ žKBXH$Q?3pD!3~3ögfM|8q DvC6FH Y@{=^WU/g7rsVW׷UU+_ʞ={(PWcqB wP!LS%*aUVrm?0_ݿyT~:[.qwgrL1߸}ܴD-mO=.}?2Re*8?& YU2xz 7|2dwRd(I[a㹏|z̈́SMJ}f>x^n2}:&_+HB=S`dYd9R)`*NRjjl#Ԗ)3Pc%$!of{֭%Adh.ɚo߰Wb7a1̗O0. .S'Y&2ǃiLMԴ_;<ԡ3r**2VÌ΋e+6K10l5 r;GҠrd4lђ%cKĤ>nnahwoI5DW>Glk5 Vz9jR)W_*eftb᪬},{ tww_6邙s8joocvL. ]+;|L^,GrES;5J^ ~NUID 懬&6oa}R88>H `\ڗI ꪣah:N\ʸ `/NN- @ئƏQ$T~!&< fM&Ӱ` k8NOlWx|| ;⦮5uMn>\ ٷH lq=hcΛ8qȑ7<3gq ߱cG\v8rpLKIqd}SۿMSMlx/ĩw! 7t8e,WK_+yK'5"HD,7c9ryZ n$4dVNAzDnܙNCHpwhN(' F_FMp4(`ٍa {Ep2j8G-O?u1&۴q쳟8n7t4s΄*澰gVp]箬ΪV]dum35Gl^'.do {HְSZRä~Y>"ka\%Uw'˦DhrdDz+UY[ib={/G[?URM'G4]~rm\%gݔ$c9O!^kl P'j t9VV z݉K"`pMNXa_D7cb_-;~o6e)w_w L7Y~rL^kmr9?rrl؁e1q&]UMo!VM'G 4Y5lX9au[pr1zi k-ז(yET?K%*eXwl׉ݨkJf^w*kzq;"%[]>Tfhߧ$;Wb=HKNLmgZ+Vej>?HJ{x`٢͝Zrg 2tU5,ۆE{@i,K&1r.ݒR|Ca'ͧ ea޲mao6ka)`` _< ]ܦb_C&#;jb۳Ϩޑhfzj%?Kczk$Vv ֤%===gbo1;>ktm8zo=u4 `л?5Onw/>ɻ75l$&Z+.7RérCӰl{޳gdo[ÇzC9dDvl@'Y̲JؓNjƢ~\U7 wW}IEco gS^OC^$bF7f+ў< axwK;YxOcP{3 Oߎvn/k8W;O}eNt}ThyuG|q;"+O_^Wmi[dQGMVX1.~EEWJV[ckE+ao6LM(9fI=;Nțġ:1sפ žzۆw-5Fvl ְ-AOx\(4ݚO;*`,Ys-RNP[^K*S>o#gWmJS1qŠ^id<b-i; 4)U*{D@ňקZ8 F+0Z,b鏄춅;䫽n#$aѰ?Bl'iGò^Z$>Hws:+RYq/QbUM'. 0%PLv" аaS_rM"_]RRoGy++v.Po}cSdLr6+uz'sC5Ӹ|Ikbm41W1cz$,p$’%G #`[Q Ga;f(﷋x:5n4Le7ۏHhOW .M+긋xe'eRZ+]Ey^7f^/&-y.EopѲKܯv_PT@ѡC=É3=9rLL,E(z6.#<5,U^7pX*|8*װD}}qLװANjk8έIN&q7)5QԐZ a>t|۩uzI k$WR<vъ6lkV>likkSۆ-KW4V+t|.I%vG}Fr_zjWRtI̫(x8- چ_O^ЬQur.N-Uf{iQ9gČ'`~:/E4E\cCd,zc+qIvհ%|j;Ǿ:DSVRRuGQƖK ޓp Pi,0ofRf2v,F&6p2\÷~ݍ&e0Sc}E5ps|sp&/ 7]p:&(Zhɞ8mq %)VU .+oB˯^ diw gDu>' o n./vՈYow;vZ+SHDQE?#JcFu`^n /+b o-m\,b_*h؝6Dvgԭ3.i?%҄ݤyxPR&gb?AP`SRFbC7FfjJujk\1=k~&pӤF#R;t%IaMZeQwTw_rKUD؟r:,u Z u@VFWVR4T)ʗRgh;f0ˎNbOV ;. D":2#QS+o>f9|{0"_쮲*$OnMk=ѥۥSFMR6ӬjhXxLÙM[d%5 7x5}OIߜ0rLX.r5mƹ*ǜqsC-YVMKBh(Z[VU.Z4:V,82f~OhƹTU~vz*'}"!hU27cc~ȑ(%:;Ѱxy:CAK#2,Xl-VǡğKR T%:{TaCx;qU7̈xxl[( vچEò4-4,Tl2ǯ>N>Do/~O_sh MH b/;^ot^u0ϘI\%섊I|Oi*)o`LiLD[U'YodMU'jze]9d5SB̨S yBoN{ "Ȕ8%38|)ج*i|QS?aQ^X9A3 ̓y6w*4r\_%Q/3@Q18|]TMW|}[i84{L1n-ogߟX0h.ѣ޾}Ӓ Iͪd: lUq2&uh$60y^o\,£,Tnf.ʝ'0i!ޫe oϜPE /rBժWň#%Urh9{YKPHL;/T(ϗ2"q@ i|JZ<%npcs2R_V--ݗ^2LWU+;tKUau;6ӌGșFHi>?5,[͊k"E.e)-`e.I ĽP@4Z=lATR^Ղ&FaV 2y6Ul(3X*+o;)z]rp4PrГPĨDwj4"/ MyX<)So4h֕>FgH^A%igƌ&6m5iMW  o۶Mï mՊVm7lGV8"IIsh8Wbu,%Pg|&):o?9xUj*CDzZZZ[YϕhREɣa̅Mi d'\:yY ی1bԩK,+Sjje15r}4̢̈́XiAFH)MY9rM0'L)*t WcZZ/8?dV1cz{(0wuH_܎FkkH||߾ 4 C2 ap#4~ohƵr!0h{ԿvRy{7o޼Os&L2~ˮ͝m>?5ѿճ*zz􇷯_3f'*6@\wkWe޶=&,Hʌ4Y;ҙ6`_L+"_&֍ǬǏ_\ÒZ}OwaRvMwMܷ'tƑ_*2z޶OgZ @ YIk1{| *[LNAi|5/bYvS߮zGϛiVwokDSrO-|[fTʡ|={v`ճǎKru n#Hzdӏډm˜+[3 /;eMlmC']++vߗ  wZlv/zr2OSî7o%|Cjfx8k5R#swy5T}Sww< ZߦmM[?o9K_^iXj[u9O:&sv7(KUFK ,I$G?jgv}ӰCL{AW{~GY_ ?SشJE,2$ ?UW}'>x6a+>.`[er6lk):2]t Pmyzhx̼qҬN|ڝt:Y,=Z&='kk=)WQoFߗwlWvSa@"QL9dA~ҰUl~hmU8]qyZ{UWv~ኋ|O[_ް㏿%1e2ێ-Tk.ZYw-#g>I*ȏϵF|xӔKj1c@h6bBSsѿ+2my?-~}wg9VeMF*M/Cmد%vK\ +g;TV=qejxIJ~^R!N}4 v\Ih] twfjEZ҄V%o^sn_,Xpow+_|3[/=;־烝#jYa>O}ӧرcDR EmmzmzG>أW]uU& 4hm;y22uwygg}u(-q6ZwS}ݎW:ξos˯g=b .<`^v)[~Z9@J_%;>Lml/4lGgiw1bΝ; &ۆ8mqyvڕ[j;l~뭷:,=npCOVHl:ģK*V Y@mYk?~|r׺7<2uT/8𤪪v~)U=f(92`9﫤w鼏'v2S}- ᙮m&1a[mmUsKVJV9r~֮0Ķm۟|Gdt놙S;@yt+|~2x^v 37С5 ;}ziL)%76[H{__0/R4%5T0Q4Xaq#|4 YydͺaGԉg}ycM6g~KloA d)F*ϪǜxG CZS 1bڴi04 A߱dɒ{ݶs/4\D#9K. SJ= v }&_E_0QhA@5 0*cc951'S4Lk u԰)8LhhF]Lؽ3Rr5#7Et&2e yV|d"aid`lUrNMJ0~+2eTfestf9xz zJ:u?Ѱ oVYl\N,e,k6=B:o&M4lc h:GZڈL`R ~b;2߻y/;L[[P"KI<ޯ'پZ{'d7H<+{ϐ/46 !I,C2Hp\`$ M8 3Fa7S]xѰ;{|Lc~MfkM4&jxr5Z݁2#׫G% OH%%֮Qb̷8qf{N 3vD|\sk.\GGf,"w[Z m6yI{9sH,m?lp\ijeOi# {szk6{VYQ>՗) 'nG,9ð?;|&KOdcG$=M{Tr oGcUx9)JIs9'^xsae]&Ղ\GNj.qɔC>rHdV){"XR:Uá\mEKHw?}*{o^,y)٤^2݆*cJEMIS4Rڬ$ s${H53_V)95cүiIJ-K"./Խ6ټ~K5]Bm:d^_ z_P=᥎9 5W6F\m?]s +Ěu;C$n9$|#hIy)O-u5|;~ړJ|-8_JV KˑP$doGoC2DR|ݑ?NRV[l\|f•Ҷ5lhyaw$<#4NV>\/, /,jR)}gi.yg5\(AZw i; ?jxBR:qK,_V>QHUJ0Dm>Ӱ\4UÄO MpQPD_XܺW K 3Ǝ=k~ߣ=S 'פQKi85K]Aa0ItC`dca7Ivof +c):qO֨/{gQI55+ami|ZqltԡR:*MM=4l~gai +c> \)}c-40~>d8)FfKϟ V=g9-)}bf.S"a -9D3qr%p5=3%&8 ݆:?L*ܴm$zN3sfZeU0:+h9 X4STZp2ԀioWsݖH8X*ǝU G >͂wHp/i8wSZ?a?tHE' VgAЪ&h,J3avahZ]]]ۑ#GB@4?mۘ0P ՗.G vѲa@=aw- 0o^VJC@5o]hX~@#5Sa0F\ë׽L{rgXU O6 js^7Z{gr4ekGK,jxΜٶ}ÔN9fA@C4<{m,"4<;aѣaNS5,Gtה17{ծW[ͥsrgkd/%dZߛaY)o-װwKeK>y_=5-8\g.fz{s8q˥npJ"Xd8xmgTgN5S|epb iY06YEmMz#e)Ca㤫K^j`*>oY5G:hu5+OY]X>r}~7o/xiQuj ӉOR6|GD~f]s\n}l UڃdMq7nkL0H3'9eNy3 ImWU 㫩Ypzf}'G"c,xlE fd694y ӋkaQuqbQyg?rj#~1QeVi3f-n=$19#wIC%QS/)k*Lɲ" >٫ڏ;xH~ӆK<^9mZn5{ QSDJ?&I*.Rf֤2հ-=`jtٌS6W|+-G4-L>2kaX2%,-5Tҕ17_RxBG 5lɫѰ'In}56iۥơp+&ӈSpG=5eG>F)/1CcIE~h& pᕖS>z&記KW3k5魱MҰ.;ܱn˕ԚVYh.ZЇ4eְWM]1)sQi4+5@QF/wgpc@tyt~'1Aa\9*z.eYfkډj"'X{KNhi7iP km ]| 젆cw\-5b+7_D!;iOdgݬZ_=l;n^ՆI|`Վr[ϗ%ˊ%H\7px;2аچ.FnR:"su)[:]ʒd eR`~#Mqt%N%XƲ/hR]Ú2U*Tcohu]3ݜKb?dΗȪa;2tヶ'ZtH]^OQ(|)H\1o D]t3G*9tU2⤕8VkSw趩]=n"3>eHDΣwqLW3~r)3v]jtCX&Z!'00I-4sk~뭷p^#fKhij*A\}#JNaA5§{^X\<.X&, O$ 4\s8 5'%aaarI4  qچa稘o aA5{B<䒉5,RI^a@0i@Û7ofpp=﷍f pΘM̧ݻf9B(K&]w;đq3s̲M󫩝x^?rI@5m@4ls aahha4 @h0 ahaahh0 a@@_]woB@s4lGÔRhhaI' 0 O4 @4 0 C4 @h5믿nE)h ۜ|0 0a4 @h0 04 @4 0 C4 @0 0a4 q kRh^H8&kX~{)^deYH5 _vWgFܥԕY p0@lQ@}i(96UU}")qՌ'xHM9>emTpSG[r52۔єUY,ifY;kװpDM`BcJ=44I {6.Jy>rE?=TAB babȦ0Opb4򤞢Џ PÒ~]57ژ V/ )C(x#e& 3dto4T 5z!%˘6='hv)\`qMK46p~V,EzH2%W*rR){+= Gfx8\g@C H?%kbX /ZA('ch55|]SmEɗiMX7~shQ8*㰉ci\mմOM7}/Lu5lW8'Aī6,_&*&nE }Qm+ LWI+5pRLPnV}`W2DK{Έrf2TSGM@p,C{%_÷~avЖ_ IFb\\ǃ6ًwͷztdu#o+ K)͘>K4!kruί?ү|+{A@RN\Oi<ЙT׊b~-!C?3IENDB`spyder-2.3.8/doc/images/editor2.png0000664000000000000000000004032512626055322015631 0ustar rootrootPNG  IHDR= t@IDATx{W}TTEUڐTvɓ$"MB`L0` e%^  0!ˑX)k b=eYde_+^IWsyN9==s3}%=}uL;ӿ>(0%U3119G+S'o}oMB9Znx4TU@x~[Ʒ2urdMBP:!/˗~7xY'd5Y9jUk+V4ҥKm6BA|"6ly|.Z69|U\`޽{8dY͢jy^{g=*N<)Mڐw{Odې/A.UYFٳ/䋃NNNHZ /ׯ޷O~wן:YB]Uh!ͩZe HΒ֘3裏>`]0C|`[\R~ӟ8p@eSco`&{?_۷=&dgv?m넪\L-ݜeSg U3|k׮s >cra0[2:ȥMvt4G~[c_% +nwd%Catvy~:5}ΛײpvyߗQV˯j>|Kgz.UlRt!%+8U}1$gb!4O~D Y(gZJVK/!Q>-I_d%OWs;~wF[rg}dT'_+_O읷|ڛ)Fذe]Ja]~FZy}ѱ/ 'B) 嬉o}BVV5]^R]dgM,Y4$]=*&[ʯW+= ,zꩆhj7yQ{U:PusR[9VU/qzǎ~Ri 9KVpڗنؽk>[8dISuje#=DmޖLvz/-i~A.U?/{Mvµ_[;|_c߸v?{?}xDщЈW߶:fWZա-3(dhܟ\Tw)=_8ԋCPΚw[]Us眴hT-W([ĉ%!rTF?3`w;?5{W4`'$\lXuժU6YuUzH:;NofFM>7,.UettTdT!;-yvw}FpG|"\ek%:=s'78mMl.U/eΛ.>්|!rVUe; ҮsNbs^`Rs='|q9KVF?3N5(d#흕fU^LY[F6V\-9Q=%V )ziUb>7l5Z_}nvm_Y?ܡ:A.U^ՐKU0Vh̕/f G *ɚ5kׯFzH%ӭam1WZj97l5f_XT h7v-RT-φt,ZkgߣpwxbT{󝶨:CW/UOgku:;~[ߨe]K-ĵgyرcG>G4,Y4jW@~'喝u&̩ꌷr]U'ͿiZ/gZ3(Ol7΂uKՏ?-[8WD ?g # mVb_*OU_wEGsۇ$_ʆ9;貪;np>|XWL,Yfs9jlG!tszs($]m6=Goӗ/䟲0ɯd붪o[;g Z_iqQim* t<]:A.U/]tddɓ쏵F?sݑ{M W~}@2fɥnwյZMw%RYSQ3?7[ zV}ǷCOU_բD=%m{ywUzHdai뷪X6 Z_53հ7vԸz%Vv,RT#<~XTԏ7Ml~ط'V2r[G֧cOՋ񟟿~#P~r9K-VZuYSB̏z9w܁ΰ[\M}cR>xꩧ;GwJ=֒[?}QD{,@T-eݲg NZdrȐN'PurÞ;n͛}}sf?I~E9ͩZMßs g NUn۴iӞ={k9KJ^b3399916}5wohp-[؈|ʯ|խxGe=֒m[?}BLHC-: A.U[5'I[xА>_f.zӜek?@ΒiڵR -Z^zǵ #=$% :A.UCǒ`h[9U˯{9dCDz) Uw]ռ [H馛_|,YAV,h A]W5o҄?u]?DV[~%MA_@6MBM:!Z[J";gϞ-MBk$}W"DxaVYszMȣҶ+W\f{_UtHRR .!YGD]Pumj}]V2_6rcbSWmDoZdɪO[l hΤ(JTZUKUUZnIj:!?%~U$U~juN&]+UߏcItqx^cp￸D~2V ;iԴ4:33mIU>肪%[T{ȑ#:qSN>}ٳϟqK!Uj@ZEJjԊчcrTeiنYVZ6͋KsZz-?<Tݪg3lO+ˠZn=IƸ֐M$CEr̡zKT3ꬰM\,(_V}?z5U8!'nv:먺yڂzXOWuX4pY<SͭjZ\>&aaYFY/kFv+kѷPjRF](y7T][.NFi][E%?Y]-M7e~0 wUk]|y5Vx6ud"嫖ҞrKpJQgPw:&xvdrW"֊I/=DnSV ;2` [j*OWFrG6Wj#U/\pd`S|^PAJITTu-{YIHRtq*.jE.Yu UŦʲo.UE*՜*TpگZRkr켪f zYת;wʱc^ػw/cǎm۶m߾]._UjRM:EՆń,TT]#Ͷ0kyYRIvWյes˛'S Q5 w9הS;w,+es {zWvP6l Cai`M:3?Z]NbO׻mW_+ezUbϧlU(2Vζ*91D7E*N׌N¼+UZӬtm;غ9%SYu#řizyתmYc} <{4Ǖ ;{TZS\?&ccd1sQu :k̻ O3{T=>>$h:9ʕ7,G炙+=U8qnŋϟ?F0&FݪξVjUҐLRyXS]QNz離޺K^v۹^{Դdb:zj(z J}wRÜP "ll k͓{S*4T]]7~9PuON?nA㳚~,uXlp!s_k~޻nz>s^Wm)Q}wBm r-V\X~uFCgrY8u*>zY~831F_3r-dϜcCWX)*K]S l[wvѝ&i牢zuw];9߭dbTN嫶e(~ŗϳwJOx?q'ɢ(b,lߺ5hID;.n;"}#ZU9Q'8GMWܥuXLueCx_#+vɨaʓP 4vGR޴7r]g}'[X?7mqvtTh) :gy0q&:8tLoz&6a-k6ҋ[ȵKy6n nd}I9o{ڎº`S Sfqaf4/gպ.k코7Ci=vN{([<׮TcD{`ʕec7U[M1c͝dm[^R݃Ms3z/4y 6i%aoK9Y罔7Ci=o(m&Q F՚1v+wgu8i܋[v>U׷iW475l7JҔqqn8ZK[&rk#kQx:w{ںòвy_3soQ_G2~ZRެOuwr6/ U3U׮]kx+}7{,Ĥ 鑊mCdNբێH[<@BxP?{-^wٞ=eFƵ7;NѠQ-~Rތ!լ1~Zn{'7T#P180O'2@ը`|`Ϯ;H Uqt;@[U#E|HR'?=[^th6s`͟΁E~?mWjSg+JUǣdT5LrZ*y5,%%lWCcLᶜ?JZMkab`@Uz9VuJanEՖ؞o7īE\UI3xlY*&_ ǻWi=v;v5Hx# 2X m7tjiHw*mU'Nމ:~;SI+;U\ZlJ8aUmy=m_z(%S 0t-coj:Ru|Z^-}཮pPj{[ՎԶv[Yh0.Uu|ݹS wM;nSu-Nwe 7[cq藒yUXѦ@V.U㛨"iUap<5,CՖjBgTN&R+D5uM0ieyO4nx7*U6 GՍ5)!5jTNU- Amj:@ըUjTUjTM3۞oNs;'$=ZݞSU@J:=yٖ U뻡=YBmM䬯2^ʝ^ h.G#bŲNk{刬{Lt֝>T#v;Oyhj3h;_zw|ESu`_Ǔh,Vm%[tc@/U[Mۺrk }8\njD1Kb˞3GXݾt֝Q iEJ~Nڞp=UYGΨگ8VzO:Ϟ33mNwmyT5\4j@IY`UO;_k._jk"gG‰:DHurT*q{,+;Shd%r[*ۘڶ7=FU3Oy>x7PuOz˃f- j1|uoښXkXb2}R ;:dv(WJ?y8sp]vvVLMXP<嶻-Gdww, u1FKG}>UwVJ8^<~W5Uz`R)u}GjFUFUFkGbO{Ȳܹl{45gd3֌L-n7#ᴰm0nRՓŋU]޼ًz=:xqu2<͋ð8p5*Wk  śǜN/*̹AI Wx2x4T"lgqKE՞Vjl\*3d0T^ 6z!jXy27I/n)ll`T:bDPرx:_ T, 4UGu5p挕,lp0@W͋m(ŘuTvr=U;GըUWuܞsk4^-Ӌ[U%nBյe5`U<=mU7u^թ`ےZC65l,+'kU),v).0rZQ5@@TB P5FUj@ըUTWT@Z>՞YUj&&&&TQ5B&&&^T ?<?028^3+건OF(5~c YFqZ-J-wV8011O|ZvgUUw3spU=6\UӵiCqi[Um+uPjtz^Sreqx7x|v6W;j%EC#VT+/ZOOԳ~mt7CGlMGw _5 fƭjZ\5~hy^faZc^XeM7tê8-Tc<yg_m K1 Cpn,E26jvoZgjC֠c⹙2N ?:s/M؎|Z8p5yUoWaHZO6U c<:m7ͻMsVB7xھ^Lo;}p&&WYo4U< }Ul+]\{omPuZLKȢT߭yUUVFڍUm_ڧK &7Q31CgF# I\]sUtz1kjIJG׫>/6}T]O㿡/+oQY/e],CӼG011jT꥿"VY?*UqY/wR'~KixjxRTT-_<.qzOaT,=1rzʼn]jS=(\V(#]_Sxƃ?U./ZUOO@x U:>wX\:i%l(řϾF%OAl5*W.ą׼\Y%oxB|A0;V%Vsϩ!]@f3@ըUvKCރJcNuE:7debU QF}6JkbZ־%/@mWmZU-S_N^UW*BLTJ+/@-u*_uUPu1TmW.DըUwC|ն¶zC{`#~1T~1[뺪޳:e`/T]v|9sFS/_|c-kQU)ͳo^UTջwobfE{\վHz@Cwġ9˵YzyZR߲..}ǐd͈_C"7jZ[HV9Z:xPRV)ʴp sQ]kF[4m.wj@WEգ:6|@*ꛫcBKͱI!R3])5vEu[T2_gl(GS[kL<wj@=SDթ F `j,js:< er3ͨ:_aP@{AF6tR#BEͩZh@(U{P;.jQ֑gf<ϧj :XĻtM݁UA0.Vj۠b[duZ؁0tԔgr@>WuujڮT 2TP5jTP5FLUj&T U3jTݚ|Qj)(v8UUU3oaR>䪁@JGv:Fj@WmyܨPjɒ%%PSTfR O.C:=3}ċpC=-)#mYQiM(L-nGZx׋'s}]f+3^j%3tu|G+Զ%-րU;_-Gj{SkծR[k ]`pJXiۺ-]z/E~{ȓӚ9H:ًVP5^W}HR?$Sqrm0qѶT(U.pDTf-jYT&ǹ{ Ν;w8eo>={e :NWT3g7l ?C~)TqQ5嫎bzUEdL1!ix-[iwT˿ttGKGDM,{ʕ+U._uu/Uk.= yR۳SJSnU/N4y ϒ BqϜ9#ߩgQ54]5.knrjyb|Icէ/~b=xp:T]|FVMVIx\JIx[nAL=f@QPW^R%Y|zVe UhZIBFR1pT]DUWJPuU]%mT]pTUjTPuU]-f~Ν:앐mFaTW&Nl(v~UkT _>+9"kU6y꛿!v#6|ۦjˬ5z:Mz\{hpka>U+T**0 gx,"3.֢Օ6U*ùuk2Nmٌڽ-z qDZ{^xydDXOUO;#O^;uױO)_uTml]'L\ ٮpXyb CmX-1mw?US_'^]'>X⦅?;_oGnڒ.ӥꚑ: ,] Z*-GUcZBh3Nk.U-Vbxg AGM~.=}Xf/ur',x]̮ULUW^ffss-ګYYbgS՚Ql5Gvâj@6UO>~E͔!xuj׉]lsoir6- 2yUoWa֢kC:Z[gb:D Vg-n̲Pb<{ĝBfAՇgV1U`uqpGq ׷!_puә&趲r5N׌L`-t2lV Zo߂mej]L ER}IܹNT帘`Tͯ?xq3`1q݌ O+k w3bd8*<{@\,N$v8wD\:-^GLnRs'~$.h՗ S⥏{.Z#xXU|N4W\<&@wo U`N0>$^#x}9k7'oLPC5ٿ;ȹG7eoR.R?Uo?-~oXbU׋mW͊kKG@UkJۮ\r˗{~MT=XfqXbWűgŖ[c,v%_g>/^#tY{ܜx}U` ,z.x{ҵ !&*hJ3tϊiQvӋ (G!>Q֐[MDa 07$yp}Ehn⵰?N^Mw7/ʽԂUYy9Uzk QRz`Um*lQKfdJ_7SAv#0um:l:"ga#UU[)̠:}U89R=*W6KCfSfavY 0nzt=U/o09xJw"G=V,9Y`O3\"j\W], %gEQfnzCٶ{2Ba똶yܸgڶxLY#7T8"Rz:< Kq<@Zx)̛7oQ=gj(IUP540CgKrIENDB`spyder-2.3.8/doc/images/pylint.png0000664000000000000000000004167512626055322015611 0ustar rootrootPNG  IHDRK(/}CIDATx |U՝iޙ8NNgj!j_q|JDPU[- yB IL$ 9~_ncK?߹sg֨o2i1>ś}䃠]e//z)~0~}:Ҙ_巯/y+ ;k֬]vm۶m֭e1]d07g}19.%Sb:J礖;I^~*ds9@A 2Y>|̱AcN~rOS7G773o%G,۟tۿۆO~q'+Q*}@:mn'O22ݞ9sƚnݺW^yeӧO:omڭmlJmibM%d'@柟'yZŠܩ?75% ȱ]~:%Oy%t]u+Ǿ=94bj~ng?Ѩ>evb_c95wτ~vw^:|Zq?g }zR_jWI?~Ϳz(|;:072ٚAk܌ֲ΄Du=uԧM{KM, fyžrX8`/s&پ|":7݄\mF 4Nշ4X" kFDgŸN;%-H% q,ǰj,ݳ>|rGAnKlѥ*g~Q}ʾ-1lJ[OӸO }ПzJ]kjA+;'oLX[/NuMaٱ'w1?5pyʔ)'NdYm63fW:(WFȧbqeO D;I:*wtsN[.H. ))E >vh \R/*=IGVreXة\cžQ ӹ 0uU䶇a_7q==}W=IBkCa3]GO//9tg(P_|c,CפiY*WAc~ɓ}QM^yÇoپh¾?~iӈUXz.hKB4kgR%\8KDRB]I-ŏ}Eh`(!6Uw~zIeU+a_ۦӻ|fQxח^zi޽MMMl?{'xrl_335ʹXdϳf\\ԣmiCJW/^獰~nK}6r"OA0ԝcUJ,鋧6Gs-Tl}A_N^iny7|lpI7LO?? }lO>vW#CC_Q. H[}9p7u5&}sٳ]0mٲ}.Y{Nfn4 zAZ7'&Mp uZ,~IuX,4"* tr+⾍cg 9IQW|OvMzu첆}AVWſ}Y7c~_O5>7B=!tÔ/.޾ ?+w>xQ3to>-,ag/*3}w{1>ӿma1̙3M6i$yqBTueTJG !u}M҉ e-?7C;)B~^5g~{^%udV`~ꩧ} itYꫯ>sO>$aTiҺ'{%%UכORS4ۗQo|kW|пoooT|O]J_KY\)F;ALlϰv |Wf͚z3߾2Ǿ Om҃{|m~iϚ, 'O3g… q?gϞ"d}:6[vU{seO6`v1eʔgyYZNQ} S8}?L*7oj_З cƲӾ}/`_ MaL]G[3?};&$b}qW/_^?yٛq;fr~lgg>H}-:DԖU_ww*+nzџ?w;js>YQ&[_stqu{^J5ZThlKq586[VnRō͒X.4D艼H-e x,}+락M^5)Ϋoe:ζmPE*[gZ֑9%g78ij6.jWmߊG沬]$>Әͺw()PfT5,"DNtI:&QɨōHjEtN)Z7csY߲+,qe:MM%RӎVDƣ<r .koٚ,7ٳ's1iٱO ݘ>-[2h/Q]:#+-;_%+}MWY.߃\:o6Y u+!DNK&jctUrtu +ԭ*>jiz/yE>k /"}ngƂT1W)q+i_A|pgOZL>E[ s QgQpVӫֳ{BE/ʗ/Y~m_0I)چZ*ݾ|I_!,W @~ ԑ&W," m~eқ\vN{/Vv-D+hrfcyL|"Tn)*N%K/B^fu~YE `_"6P E[meU}׾QX=*nBԺcԑI}Q}r7t]_$rWSd_B-l*sOh'ٻ}ʂ^=ψ}K&*\\o:k#zWZ FšQR߃AG]^=Ҍ򵋺5ünT'?xϳ^\_qTe/RͦL4V׾1TF] =fINEx}ԃR"RQW^cY!ΩܸeY]$:Qvu}(_o2?ȃQW܄ȸ.y)t7%:Lec}G 'XY'A:5+G2&2b|\8ب~O^d< @ʣ{SGQ5Jc'O}/u@#$ }Yi w/մ}m6}7:}[Ͽ(7l)1sRKS띊$l?湜Ud}YI_soJjVi}!̬{ RRuP .3$:־|w,HfA +I.k],}cS_WOeߠEþg_s"N碂XT>b~uga38[}_wWj9_}HB EAՆnɥLsZ;c.e)$yθ,;UK0v?wٟh}=b_UD+.Djg~k}nu]I-ŏ}?\yv1Ean˷Lvٹ&Y\ZdgYTn)N״zf"4:cJ\ԣdbJZJWg׹C~nK}6z3+ uXKџ>;0SZ=g ߨ+%oRR/5jokY)ʸce 3 K玴"H%Š_ 3X""X1Ji(E9lN'z j;m8gs9܂?ʣ4CN0uJg.kط?JJgyR:%xmʗNh}S-܃/),1=ķoZ8}./ IԾZ[UzyZ+W|O6 웒H -`|t Ⱦʖ<D8WW_ߌ5y}QKkKUGv\yMc-k#bjLzq׬)0U /d}u%*1of+pⅆY~;|SkTp3,K~㕮Q%gKfߋ/B&Νs%?g `޵PYs7f+C'#& `QsRyy#a2u"SXy ELm]>^g.BȾ&̸̾ɓ']{o3Ŏ7 bHO55= Səu ,y.Ҿv:ۅ[Jn5'r?Xd};::B" (hi,e_zmKDsѻmXr)ϲ\^@бtֱu\tz/d}O>cǎ1ﶶ655ڵ˳ۥۧaM3Փ׈쓩(ؗ؀@qCkNľeݳgO]];=G]}z-cW `ʈ㋹>Xwrw}E˖9o_y]K?m]Krٌ2ؾׯ_gv 6ylJk_C 6^$}1SWb~Sy-_402׾A5m|dk`h񸏿qSm#s3@f7n&3]W\Nx z3"oo1oqƕWF~g/R}S1x ?l"*Qbw8Svɋ\VƓ7bٯ8z4b#GFLDF2N7Y|4`JX/iJUb޼ekk!FwwIGy=}qIgy29Y< }ܾv#G~N2>6&K&"Jq$4qOq0V1HaEDn~ǺQWR,yɋ9D^=m$/p7}MH^ cG@FǾqƂ⒒Ҳʪ VOf}}sY;v>|IIOO jjjq M[lijjbeN ~dM?3f1ܸ09\Y&)fg 6ۣEy`_}S yoݺ6V [蜵Nu8e=8ިhT^|Sx}6K6sd$]Z@`žvvB]? }Ϝ9;bRGF{e7iilk}utg4H%!(3- ob_;;!,e^.'"_0mh}QsQv2Ԭ6dWLէKbi_)`l.QKZ@`ߔľAo}]V&.\y>1h8ѻT*s ({&3 =:}vYþ&6Aez޿GGÇۏ=pFwgw_ƑˮZf/_ɘƇ+}3Ճ/H@4V^wn|#G6mڴb zŋYd# ob_A# ޢŋ/[r379 /7ݱ/} }"/7 oZM$E~¾Пc_;/}}Lak `NPKMRdGk@~_9%( u)Y"oA9-`"oKގDA/I߷}UCvR2E~_}K]P, +c_"/ձo /7ݱ/}oҗo}zZϵ{8eNQ>߃vyMKJ@(:̬Y}O{'Hޮ!_oxåKS($H(h."~yHʽBX5EZ웊)/#EXa M4m8poٽ8ڽ=WșʥJr6c_}@S4Du9i}Sh_AQÊDm)߳YmQ9G g=ӎN{vyR93gʚYL ZԕH 6I ,gP }R;,Z:k;˜G'n \e15)áY>^pjD|F3F b`>;NQ&4eCr 8Rnt ;Ǻ鈄ϔi2(5^-ݾnvT%gYW*5Qpg2Os"VBkU=6&ɗ?c_5QV?x`?}ѣG ]4twnw~cd@5]J|4)tx*~2U}%k44dq􊡯!煤R~vN*ľ^œ/}r%wIy#!/f5Wy5gM׷ei٭g_5pK\sǾJm-G?ctW?Qb_5 #ozzzN8ѮPX_xr,\vy7͐7BS<;KϳEH Ծc5qX<+`i"s[\yJ,0dސ{tj ݭosotuuaP@yWU*#0YL4[{J%z&5I+݂s3a9Ɏ}/YIx.CԽ*fw%`>yo8uT;w~D?\ѯ}@,z{{{zzp~c_\w]}wƍEEE%%%eee啕UUU8&3eݱcÇ;;;ϛt0_pGbߤe˖.&Zɓta:~84ľI}KKKkkk[[[Y{!^+]iش5^+K ɠ]#|0)}_ԣ[Ÿ,CށVj96ѹC~̰D}˾q'} c_ ] okkcmlldo*y&¾/ ŋ=1.;wΥ [Lp?C3sLkڷ{$qp^A沥ʹlc6VC%s)ԳkSúoH7bNs1N9qasMU.aEVtJAڼbYd*_԰t$U`L};::B" l.G֭[,a?1aŀY{T^ރFk\b7:U;U >m=>԰T`L}O>cǎ2ڵ˥by3g?ѹ}8CwF;|(a_ vr?C. N粵&sl_˻>԰T`L}nSWWW[[˴ZUUU^^RPAAAcc#}mɬLGG2o3vd`]k%5BޒweuVʴZ▮6Q1i԰Ԇ$ZaVVV_~ڵk7lRwe9ar…3Kr>ʣa5pT>˹l\n>I ²<'Pa}ޗynkkkoo?zhD-1jiT|9K9}sUVl˗3Pp{Fzq~h_իmt>rȦMVX>X/x"q MZk=h$[TTxe˖UWW[noߌ},}GzGeZ/Nj5D$ľ@ǁ3Ui]/pk94-ioJ)`_y#}Nû3Nc~>ѕ}VrɻC]:49}S6j\^._5|J!ols6Z+5-o оܡ-,E%y׳Ij_'8/M3)~Dy{3}lG޲^ MeiC] /tJe2͗.eU7SXŖghh^CbU՝C6:3n{2c1K+I> M+W!А|j}Mx(}_vw1u3 -&x7lT[FWu.l0T-p zEz+Ey|$u (;;,qI&WV*5?!%?m]MIǰdhCު+/-_YW'`o0WFWU~rK^sl1IWOU$ܤZ37aY^(kC bm9ђ$w休ϐ\3+缚feioDڞg#dM}'{m_u&]R{n#ȳ/}㦧ĉ WM*g~βeϟ %O6}_-KMɲRzonn/pR{d&t ;VsLZD =tNet;%nŠŢ )yɹ)գľ\bwF]]]2{] ܂h~JNQWԨx'wIJԳ[4g-yyKQWuGVt;cJ2jB>̡ bp쩚}5tO!<~g1p/2o*bޞ>])e}TRvןT/fI `_ľb_۱oooFlYvJ+ i1+5,tMVS3Op lO:*B'ޚ+4./CE~_A )n|ͥEKԾMI(^? %=5` +5I Wx6~)G,ꪦ }(_e YDCN!kQ/ZBJ>iXϮ'4îL)dVD `_odn]Qcr[}ӥ(0TRlұP27D<MD2hOw(k?ȓ3ͰnpX kh&ޛ+(0PFMb}\X#nfIfgb/'=1tM_ZԻQ?MR '3G#)n0א7 aNH+۬ƎoF+ɼ̭ڜ@4T -J(UKqIU=dꁓ9Yphy&qGH R.[gӟoauJ^jE/vb߬},{zֳ|vVìNj_^}pdJӾj'f%)a2YhmɊS)c_y͑ wշ\ztm)eTJA}@LQf_b&PI8Wcd\756SW#nLľ}7/K7.}3/~1|-ߔqVQ Xkq-o٪-"iHi-KMTy&@s[pK6=ϞaB_{ѱAHʿ^kož6`ܘc?}=~_TTDľB4qVWYru*NYl_aRv|~sɳ ydľs߷xk^~o1y~z%Ir=}Fw=W97]fF#BKz>gZ*-:3c@x{$'&)$W-y~;OFbUԿrA7 /c[zcVaa!?qo[m>ʣ|1wج1%㷉R@}㩰/'cߠg0D{]| ]Ay87J{EԌKw(B_ط_<ﻡ`âUHf3x2euQGpZľ'-6qqڶgpGp:7][F nS/սu+y^G/py鏱/b_ttk<ĈY*X$LƠk5?)+I~5J,; fά>VE2!M}CiIi"yGmx<ط?ľg[gTxvϴ{;-"H CFR37iB*F\a +p͟Jr%kVmN JYa{j4uI5!8K ǎ"bљEȞ<vDUZ_a%b5uPA<}Y99@Y>;NQ&4eCr}L@5WvFyeRZ.fYm.mɊu*eM]\sdÅ*=wF^EԄ"eG;SEꑵ '^V9[ xqS u~ũY7շ^4}o7bQC ݾ[^;*-);߈je.->q45N3Hdѳjjb_H=hn<{ք?0\vA_ȦF/ܷWWau%xy\3)2U/aI4ӱZN.9o3!̹f9欩YN^`Aھ.]-G[oAU|)nG)#h_s% NjpRA@/}}ߞ'N+^59*?ޥۙ5}_q=vѵͥz;[hBJgtϙVfR%3aG;VQbEw=t췠K3e$Ow;QW}b_[w67^\SP}߰5&cG]=$yi~Gn(e}ںo ~dM?3f1ܸ09\Y&O2TH|+WL*Ug*l+ ož!6]w֭MMMٟߘb@G=vY/SF@z}KDq)̰+qK(NJCgs:I0>}ŻǎcmmmefݵkKAgΜ1)Уs#=q44v6Q5¾%î$$$Hf9k^YDhz$?>.)?7bӼ֙+)o,|+%uYg1ZG9sya]TMKKHGI[N'%jm)#e 4R~.ru H,FW" =o/:phʰ,}%סJ辍D bߠYWч4zaO,yۗ[HOe_Rqjm}/d}f 4ġΝb2َ79Vmq׆ l$2IIt9bZg3B_(}uᯠ;k}DoXdF9䙐@F7h#&Cnb]97۫ީ]vSYl;d_'d}8:3̯WP7lW/ͯZd~[/LZ-g}oG]0ιegzc+?[϶f䉣)",(=qT/=JSsy\V}ޗ #'u7sn<-/_DV}a_ha׮]---Frl$о˖-d>-۷}yu2˪0=7۝H g¾H^?psgߘee|;c2OD{6'N$qǎ/ 4>6$55u`֝w:{ݾy9YKԛ~gshe˖27$_xyTl˜1 ga_7aa+W?~`zzfQ[[́8q"ȑK.Ѿ|rnb9KUל ;|WէN:ℴR!33?}4@ %  fWZ&[)XXH<1QA2**/=OAf )Qb)*P?Rstd!3b,756 }I|c=FGgW}ܹ{fgg˾{}vzIWq#l޼{?~ÇI ͛7+**҈I6V2.v6+i4rzttzdW)_䰎e,#]9T월fm郞)-;S[Wvkh žYߵqY҃J K\ /۷r{={ѧ;˦-޹8D˵rj9|٦ Oih WQ8fBG>5$o_Ѹ}e}XFfn&ɾS_9flо&J]"ľ}NuN1eiim\=u*H$&om}mNI:eu;ĭ5oZN 2F-`о ;wzZKk۹s==̣GIF›$6;T]]mܾ$o[霥oJ)k}*im3|:3$t`իW\ 8>>춛R䫵k~嗝j"Ub_Ǘr<+_+MIl::Q Ji)m+Zw2o=Ϻݩh<Ǿ%p%h=K0@DطY5rA~敫.44)?v`}[ZVvtvZ;:n8Ww ?:ykni&)+X6[lW[W:0ϪU-ZDݻwwwwۜoItBO׬Y#$aꔦ{uϳLF(u0 ׄsSlJz8Q=ϮtѾJ]"ľ jd҉훞}rSegoȐ:vz .wtZۏ9bܾ.⛅K7Q_Zeg޸9w [CE^ž:!~o]&]~{۷ľ^::=ՠo_w@Ƞz(yyԝz5|ו*y&lE71}M4["ƾ9zڴiY]un efffd!32z_?W]jm?AI7soUu\O#7>?c /\;&ZVJIII.]"':DtC]Vu2˝Ǵt]Q発I[z\J/fOv:-J}ו7^u)z+uR.W:ſDlJBҿ8Rz-^vl_ADۗn殫;w޽7c+(,qbCcYٍX;:I:b>_qة㎝4Ӡ,DDk׮%r nbbƍKKKɟ555$&_fMLLFUQ]Wrvo³Fle[}j:0EuEeD9vW^xlKKW{kni-?[i90Hvr=~D#QБ.%K8~Axiײt7WOy߭"33{H$ܠFޙDszzzWwM.)qs ##C'N6\imo?rYJ=~VNbJqF+xPFK -#3_ҞL8+ ˢ:\=W(wKun/ 8E33)硡M6?]WPfffF~8#sխC$t{J=7r{]ԗ\T <tJNkQ3.o)؜etbQ}vo5#O7hDGQ}As^xUOYAi[}ɡ|BYQ)%\]g=kO%A(EI?eݪ}$lPԐZKlx.:t%y<US-5+ZY}wyMY#dsrr/qIGFDUl*s ~j$( 3Pzqk$ۏz7_veĹ8>+lʪ̒6&)dGĺ]$V:VhlUR%rSP ʥhcIvUkUj]d5\͊Weeoo+X~hkAto,yn_/[?u! )/UAZbW̱e3x:L~mJss֡Ν;$ѓ.Q2E^fJ5;p}rB 5ɲH$%n۷?r#uE黮n_111/_˴ʨduCgժUgΜk}?t{KػY3.Z7%b﷟(DqwKzm_:(suj)^/TZ~.a(I%.qV]{P {w'2u?Kw/k]dMY܁_ygߊq|WQsrr*ڸq#i;w͛,'}*++c>?%7;c~˯t~}M*?K N)퓏?\}طKv&G}.D<8Cf.=˾\ ImA۳9z:6+aVwm|ϾMMMĬ3u! @Ā<zgT'M`_ H)`_@`  }/`_`_ }b_  }fhh(/???Om۶Ia_'ڵnd}iok7 Diil0nuVT~~EmbI\lA `w.!~vf~RH]܏cb`_ھCjޞdà}۔p~u 5_.))}V[y'+6.[2+]O-{>jvj(5qš"Z[)bRgG9qThn4аoQScs1ߞ ?8شmV.]i[/U7wl0RyS_ZKSY oJLA٩#<5-5GG2#v>rxS+\#)\:۝oZÙVYzyIC9Ӥ9þm+)LuQL=˝.LϜ속eʫWLB_yCe<΃48u_*}vٳ@8er 0+E7HgHxO/Ҹ0/@۾MNd2%0r0{xMK<5wS]Wj2q-{tJQ˔) UIGQgokŗk8g7rWPfv8uវ;P Pm ߟ>*צF}/:!Navf_U2rԦF)wy6P\KVCh&&d\|3VGiNA%=l0:Uh_>cܾ3ޚql̩fLyRS&.WIzLՎf; ,lJz8 CkM>F˾wCW\ayWQk>[^i+rY`I&Y֒}-&)z+A*+'JtSK$F$~q:zŽm5bƼ܅ni7K\w3T53 Hɾh\ױ`_; E_.<M<ͯ>Hn~)S?=4um} @ol~Y>:6dz$'RW[o|}`ڗuTlٲƍFƜZWDE?( F}?~7+ H~0x$!+DOe{lUd.L\?(%\ < }CɾNZ:ĵzKo[M}$Qa.eW.*@Ԕ\s)GucQ('ɓ%*tOF)A)ywn]}GھwyJMSSIn¢%m\|:yџ-X}Om'DlGHoVpTDTၝ0))E/Z?m$Jl6R'طt?k8"|:6eΟ?߃}5M:((žXUg].`_wiE4y-9;wqEe|gV7; ݒ\Ǿ,zٲ^%&OUwFqAqÜ1ʛZ"H廮|@u%Do8QGKԻCJ}S@Ka*wݻ.l?zyY|8N}Fþ =AMຯ #pN8gR]W̱Pejî؊[*'f,lذG&O>iM~F抟Z?џyB^I&Y7M]S+ QJs_I&%%Z̲3RڂՖKN,[M7{***V{!9)kJ8\8:z1t>}@$$~/060/?:eűϦK-abl`adߙYoI]rrY)~t鬴s~09 [^ cc3xa߿9=s*QoBq ?H[J_]e:'<6}/< 2M0{#o$Ԓ% K{r%G>{o[ɑw TT>Ǿf}eʷNW-:ђ̅6$aq xz#3h^}]u7}}Lï4c%86 wfմ/78.cE}#ƾ4ufKp/h_`l`oc_;\:pԩʪ*(ef(}x{xx'DxǏI͛EiKJJl6 vHSJҶ k?o7iykuכ [VҚ$%sEIco0/Χji7 7Lc_/pBoo/nkk+yqg Z}`wY)SWžf /z>|8<<ݙW|hKg0i_k+?K> ̏LlNM?@HwruG }r}͎ \VVV__C9~mstϘ[?~V˳VOΰ?ao`ON= ׻mjl,yΫ gAMMMžf...nkkMG]#eD/`U}u!_}N82!;Fz?/5;6pAA{۷o?ydbL꽻>0~}{Ic^`_ɻ/}ǦwJ!ELRrr,5;6pnnnkk+}IS>|0t8 9U{8{/eۓe_S#+p?My{Ǎ|p 9|{y R;>xu {[ۧ:ɨ/iF \]lzwϓ~7kjj!瞟K^f6om$ޗ蹥Ν;ݽ}}})nodq3}˥۬RrRi?M/>9g{߈g]=zD$1y{]3ĸ MMþ b_2Aˋ_V4>|رk׮=||sOOONNΑ#GW="1<}ՁO==% yW8/Qo~~:T]]-طÉw]OW9~H}_b_پ~}3+3'H}>-Qy_ׇ)"b;.޿xcG}xv&$Cl8~XF}o߾uTO߿ǎ$mp"@ǝj#2TE=gCy^ FU^TګІºs&[Y\V~ʧ6}0RCA3񥛩k@Xsƍ?񏷝ܹsݻݻ?NAHQ4oľM-{wU}Նmc/eJ~aIf`-X:5櫒/=qN7Y>9:}C޾mmڅ ל9z>t8QP"!jFOt<\SI$~efះqbr7腇  ;n?[/b_ߦmo;em5Y޾|eӛn6i1fp*/uBTW 9%hd )ut5USI gT7u~l~s>EY8[nhJW(`w Ƥ!a.z{3*fL-rˍ}GKY[O3ݥrSt! t}nbN:0^k^V;+ȝyLa RWҠ}=v>Jg(p [@i>>;mدˎy%)?-KuhGsEGm1V˷Frk!Ekr7 ~n Z}ib y{z|CȺѱ҃ {u_V(F0v[$(BU"c \;oRݓ1EHuJcghYcQfd^#o$ƾZ4_dki2ڑ-'wHQbj# ٜ:#<<'2"=mCA+S3OS z #_ouVMszCq up:}}GCC>*1τB_}o8uTnnn~~~AAAQQQIIIYYYyyyeeeUUUEEYFlv"Ļ?80iӚՊ-`_ľ~ i>򡷷wppɓ'?;y&<}mڒ:FW MXsy˾IN;e%0)~$V4qI;! 1_\4v |H BUJVUU]pH~_Rܸ3W'}ؗ^GNHTľI w'<%)c?RtY+'vAb<ޏ֕bF;䞛A"WJHc!_ܲ/,3fq֏3n؛&/̀ٔT=ڞdf9\oľQRDҵ-..nkkMG]#eD/`v Uc_aT.g"]u!_}C("n__f#fmhhЩdAA{_ҠO<$z̴xyǣ}Ab!_}C&%tR}}}]]jUUUyyN%sss[[[IKM.t8 9U{8{/eۓ#r g<댬ǎOKR[YYwĉl'YYY'Oԩ$dAFt<0)ϥ#2 gu7J8@ 7KBM~e݋ђ,nv7dueGwRVFF1 ̴7f^sW| <;vڵkG:9r|zEZ44<} F D?tPuuf/b;x;.}~R/poľľaed%M"*jf6yԨ_XF^Xp|Դ ] |\҈\}9=Vp, `Ap yr[N9X2rc;se#S7S pU y,[5<|4~]lЬ?G m׋ E+Qtybeb4ؗ؂Ֆ"Su3fA}4(_cFʟ9HEQ3lp}<3?Tmhmk8E(?}ZaS*}ݮb߈X~SgM\l~:7}nzNnIq Àg;ƾ;`ZG.Aןc?LBVKـkF* :YqZ-Dvv]{ i =;+wTžGs.޳xV,ւзVf"} ŧgS0ְLŌ}\nѾ|<3_qSm*ޟn| mR1'Nuxofm=QWyZuF1]WƮ^c_;|/l}T(srÜ')9K޾Z͓]oȊcZUSFh<^5rW7WUa+<촙_r`K^^pE=Qn∽YjW Ҩ YR㸵EY}Ïأ!8W WcҴzr 29KuzNE=#6[q|m 䡖GxBynW/~Kq2dTze28z%"5g':+ҿų`rnTTHf}g}Gx;pyG ľ#}/b_mlA5ybTM.DEMLb pۓ6#uFjr`_ľ^q#.ڀ9),E|$ROnR2G ϚlqWdCv" 2k㾩ib_yr?/ҙ7 `_ľ(Vӻe﮺$+])i䉨)j|5 )s:ɋ3k-y.e@)t}mmڅ ל9:1H=GCXA;FfH_ͣIEΪt98v09ԟ_RgL]*Z_Aiavz5+R))/XƦEk)SVVWv^8fxƱjmTX:d[b~l0Z-93YxsR4*E +V~Zf hu"dӽbV| ZA#KJFe.^:3ͨ1`˩/75 Mh5)'w}u1#> ݗ/Lk׸ +#9s(vyKQ$Ǫc_P/b_sN=i$5_^|e|5Z!cYxӑgHqmXm%>j'RCVD} J˾tLl٪;qD/b_ M<00Ǒw!鸧'?Ty__+tu2Vpj;uV&t>QYgJўgj0r*=-ڳ}zΊHXUg # aWwzY7vOQlGCF3Ր*ά(=ۗ&̘j1uW_tוba>XQQQ ׋Yւ*rQBp pĀ}#﫷w`|_9UJa{|ZNjt>k{`}9zb|_IY"->c_mV¾CpUF3kyX:y“lQD.E laUh@ aw RM^RgmL] ۚZRv \}&  jSAN vI'r囙zʴlU/_R=C.ɯUd_ب~M1zyWUcM gyI5#AgT4GT>jԄ}1ѫ$ZM$ձdJ9gOyԩ֚ҿar /;q:N}1zl@A=5f} ʏչ}߉fM4\}À 﫧+vZT3X0 `@ľX8;YќJ:(TWhda`žXKfN9׷{PC6*\ `_ǾAX4W= +ߣ}yPȪ1hwfG|dﺢ{5M,]XL`5 cp;:u9׎aĩSrss JJJ+++HL,}9-nmmmwwN@ڝՊ`_ľ~>iz򡷷wppɓ'?&ȇ7obߦ-))lzwoiۄ5ǟsjE+Ƌ [V$%@y_9⢤7MSJoľ^ \UUu…^"VN}IKs\-7c_cWTTؗ>iδ?s$eG[_?kNZ_<\{]e69ge"=65l`C} 5;6pYYY}}}OOi}-;=cn7oY1/Z>9膽i+>ٗgz$y05;6pqqq[[$l.?eY(%~{}}_Pe]h!???Om۶Iٱ S'OLIqQCwwfooz<~qܿ=+y־vjii1d#ٱ Nkk+}Zyaoq|A{sM{q_˞'ʾL)I_ur/5?ϓFp=3ooCx/=rYʣ@Ӿ  rkD---w~C}_y~_Ct$@۾@X=ZRRBIYD$v3ͯ럷Wh(Dyĸ MMþ :2brرk׮=||sOOONNΑ#GѣG$2V c>'oZҧNAX߿СCՒD}ۉhW&/O69u|@;T`7tWb_H!ľa-UMW:~c_,o!;ckl9#Fׇ}/~mG9}%_@t鈯6(z3#(Fd#_RzZ{& \yG{x`qk%—EvZea7.5?ge 8>?@˾BetݲwW]^UW15Ѧ'URm葓NY]QT!r4XYd;u e7k۾mmڅ ל9;>2"jW>( !h z;O̚uPᘉ8Zkced桛@O|ŖVOԊ.HkfzGwd)ZcBJuPS[,`At =aA93zhye+&F1tCe_̎0ybȉ}NYm[Ϳbc/_ٵrzMgs\ OXh[*"DNZ[sl0y<"B]/@uSM2rgJfVtAZC5 j]E'x%5*wP_nVbF%(BxF45A|=]OQW:5%FtVW]% & ғm! (]d @`AAAPm=ƬwNӎ `J?1`A߲p6 Ql OIՏ U#gׯu{6(Yߚ$c|X{nr2SI{ɖw!ԋ؜$;6gNhPi*Һ--yc862 a0e+y  ۸#L@YB<l6 RMN8$T#9D%c$ (Eq:9KA*D#cm"ޒCٖAOlsfe]̞V4F ֳLR$ %mAe[t$iS!䑘%KK8żE1ɸftK^T2AblζlKIIAAmAAAPʲ jmR JmAPM Jζ  (  -ٶ{^T (;XyKf۪QTXba[wzz*   ۖ}yf':;s3wRNv:7.h*϶ŋm?A¶ya3 lw-wt}iwߺv?^qEfpP*_B;@mއu8ܥiv楽owV~k{6rJy'l?GW^qQ;Q)rR4 M O,O-g[Pm/zb 7N9kmpzqsKGvm,x E;&} y#9 *~8>ؼ`drjqv%[۾PZrb1Wأ?>7nGّ{ylS^}8wفx%W[۔36 JCi^ N/P&sنX mʂ+\_vixh{cC:|߽Z?㾲M "sW;ElӇRmDPpe e{A_$*> A˨gkI CǶB7e+k\涿nU]\ߪ7iW_Nm}f9}ODN6jl~em)l{~+05Cmu[o|ck |˷t]{,zÚݶ\l}忽dꅿyx斋o}Wfl ()l%fu=rе !׷Ͱ2?Q m%mDl^wcZ k[?Ai6UmX (;M A6 lζj J OTefaM: l`6 lal6 a l`6 laĶuM8qw;l6SmNӧO'$jmI `0Bl#ATs"ے6 la86|DJpͶ✜ڠ(;lK$J$)d}fk-`6F>2d/lV'žnpHoPeD*-s(0\.ܑq2͹zoYE=alcT|We6͑^a[Y~~* tt)e5.;mio಍ $&n#tє_ff81$69`:_30#v2xQh̄ܒRQN-'*?H)F=m^&-Mlӊ@.,Si=3m^ܰ .o,G_zyw~[wq7v[mgۊI*XM*1c2*q[fgjQI1rKf x۸MU(M tp!FGzwAE 9TDD8Plb]\0l+Co:46N~kſp6gemv$m{ FS+BaR!f'eT%=| Q!`L{`g/mu88ٶ$t!~gjZ$_C~_>g8%mA۸ sǶPIxΉFd$0{By\,1IAVdgSDm+~dMa/>m9SooњgOxԀ&_w yyLM< 6I1IWDmڅܬ\牅Tt8K&:^QerDX`3mw4|C7Xvl- DC zM\2 $)#LF1Qf\z^KPnp3OuU((*껍pa{EX`qyIX&`$ ڻ3UZ\ca۷77\qӔ'^MW=x. Nb әmt{tl;h.:{v?z1[ ϶Cu[m{KM2N7CǍs?l]+[7'l}:!QtnmI `0Bl#Znĉ{رc+**H `0Zl]`0lTAAq6mɎ670 h l`6 lal6 a l`6 la6 aM[׿j?u6 @mƍm]R?F~ћ^XPM_f m0 m-zγ>9;jnw׼Kz^SSl+˗PTm0 m'zjxcp#'O>wmۖl#d˖VSm2{f[Y~v(JK_ Y%n8$23**v2bCav/2fDer3m'Mss~W7 /555}6B))Ta\ZΓ \ZM6'}qSve;L٥R^(T&g2I^r^ g(ێ+eKftsƲ0l-~Omp$Q>Thx!RG'H3$I*t}[zm#_yI!ejL!TDž򎬜T&`8CWVR޸lG7?qäeHW]ܶ@*Xa@.Ptm2T3~i1ɴ` }\L/UpG,;L4 J:L۔ pjr]&mێ)h\Ycǧ=ƥgΜ!wA*sAgzREn`mr|ͩX <ێrY&1<-࢔Wa$%҈Uz*l6_\4däޜZ]<۷:C~Ŀ3a+m|94pEh&'Y<6`cFlcH1oYw$k~哞LNaC(Lgۢ*T9鍚1_S# @#'\As}C;|}c ֢Hcb3^ 9Н{g;8ҿk9;[w\Gá26c{쀩+ܿ?gV{]&Wu>ce.If83ĘpoPGM}. =<6y %ӭ[۰YG(S4R8Yߺy8sQΠm<[麼ݕu\wy-{n39KmƶÇNW]_O/~5lǏm`pvHk^o_F;v~gnm` d[GNX6ţ'O:uә- la8l#裏6mW~iȏXm`p؆I6 a l66 la6 al66 l`lTAAq6mIsfa m` `m0 m`0 m` pٶjժ#FL>cVX1nܸ%Km`pVQQ /̘1X[lyΝ m` ÁaH/(͛G=g l`FEvȑCmܸ5km0 m?o}@u֑<m5EE5`[VW+IJ|a̶#GtS]]M̜93S&"\BǰQҔlȶҒ\*d[8dVʐނpD**sSyk掌CYWŦʬ̣u[O8;wbĈd,?LF~Z5d!sKJ@r(pzG$%UVQIئPݜ_pkS;vO?ogzfFM_L&%'A0g)Z tx!1NSK9rҐCх!.eIȿfe"MmJ2sLa8Ç?ߧu Dg0,Ӣ&-|J|<&{فysBy0-,1BL 0Cnr"Iҕ d&!Fq '$p- T$[Daƒ\牅TtJ˨rL6VY&6uΡ,0Al[lHx#G>|u;vI&aL:yDMJ\bd8ĉ]"&ڧ;%]xcx>*1f00ۄW&}y0ضgOee%OaE[n"򂰍O0%~. W0l۵ko=mڴw}ޛ}]SNlۂ-\tfۧ>gA}_Im`' p؆I6 a l66 la6 al66 l`6 lal6RAY%770 h l`6 la8l#<^˹.Km`pض}p]ngΞ̙丩7qWm0 m;w~]돝8}qkl6Nt]oCG>H}}Pql6wڽj?`ᢪ4t]V;6 la8l}gu vSW{7lڵ/(u]l6x57~g?^uUk>ZTZVfIG}umzsF,_RԢCQ 0eɻ#=>^=gСzG_Ѫ]N=c`[mqNNqmFȖ-3(;5HW->`IȶoրiqllՓA0O '4$AWn5zues_f/~m2f`l_jռ<9 /j9bsK<3cѢE .$x_\t_\զ$EsJ1P&rD:hq1]OW&FfNme2Ur$%'6l8~橧-|vР=?Py }|{fHJr6MƙKbL87&> e~qH0^8JJv='#f =r;2'Ӄ2GI *J`*Ͽ'qNa8{ 0b׮]{ݷo8@I[$Ctcx. ݆aNM m` 6"ѱO̮$ɇ'fc_~{?ӧϜQvN<&Y9U+Y^\ S]͒Y7M&|}7f{#<7L*`Ω|[ lKOS3a4H@$"ޭXVۊp'45=kdc&C DL:U8{QKM۴_InvvYmԟAxw{Z-MP\cbwٯxfk)). RBKvJZL&9 G͠洖F%8 'F"ItS~fc&Ho:/C%>F=%TL<Ҩ]=yj~`+۸$ͪ?s?RbdB75Y<.kpنmWEmz3'嫿YZt(I>j%)CrPuǹ$Y%6‰yg7A߼ bm%>o% L,9$SۓJ)pvh[Iq%m S’Y7бM&6P`#ņ> Ѭ6m5ma|Vbf&gɸ6Q5S7?ӋmX|-_\a~YF+"۸; Sˊ1$ 읻'{Յ9`D4e( ڸ"wGKPp>w`[BFȠ0= ,u:Z p di` 5 6Rl"]ߖɷrSa4HlmLfLN%&??hFLxLL/i?u-0s}vYm(nfBA2m9R )6lbn}XQL9e|isKmLea'XOh$aq! 7E@6X 3 Bt褑9mlvd%F5m0 m^$5 mlBWJdNɯܼpnf"-Ip]oS8ydd ,%͒aLq `OsInŘ$stFs҂XS䗼$Lp a͗ynC38sI _G5Q{@0m4ܨM{i;Oa-l <P.oalK6˜AIDATx흋T՝rf&D|Ʊ`* -P8GФ0v@3Qh2!7kGDsg(@ 7qγy֩Ωwۻޜڟާ֗B!rUM /馛VXыBaV+Ul!N[lō7XD!аIP+ |5$ ZB+HY@!B- X@#Y,ҥKB :WX!E!PK5s̿k\ӧOw`׬Ff̘1F] -$3BtҤIj\_~YC螥ޣ}ptdMj2 ]KZR X@\Cwީ_=9!P$0i\RK6w$ |go~}ۯt^'?%/%R.i,FL}LBU-\pvZR7Hk_%16_:S.RiĔ 3 {o?.}=Z[|so{˄ M6#@^nJ.~KM#u1!]<B$0[`AqI-HkkݥN3S%b/FL<]Q>]7=?]Wu҄ N:u_kM>[NM)W-Ǎ/:/[?l_˲ȏ6/##5\sUZb#¬껞Fֳkj v$RiĔ R|Ð/;=u'Lyb_ypP/KK{iں'z~KzڠRiЖcV[oƋ+='z.ib&ľ'VZmIC&xvՂ5Fj#謳o.▿#0ӈ̺8zឿ~}S+RCP$ Ub"yܸ3޿yku#>g{/}[u]/4v„cQW{6XKcu3O}Ǎ:祩GfM0{|S-u8XyF/eT䡽WhV(G̬><\ﯷ/=ޏVauygL8g^ǽĸ B^7NIKjIFd⍨iHOwg]g'9<%Ri$_Q$Hs{ォ?W֒/y_q[6Gpͭc;{7\rɣq/~{/9׿KN;AA׌qF}:Qԏ77\zZ6 r2ҲY}w?kјAS|}Ot:7;JS'2A`[^g3t B!N=TyKjviQQ}WmD2w_s_<ە<+RiĔ g+ҽd5{7U'p^{9wQz14;Yš=.|Sz_x)'|J -BDӌG~@YeOƥhW=^3O9Ϫuę?eN1K ׋SƝB(HG2n`# !Ym_w9#K-S.$Wt%Ao~B|رcպ >zI+z|n1w㠁0/W4QF\# cĵnql#'^/=̬xf0!PN>o|*/ $YnX#1iӅ: o?\@DK-SH1&Ǎ"Yއgo!G硫 Y _6@mܸW# -+N>zOz8qI'}qBs{g|7-SR$WǮCѣ ֎3N;d'g_8c841 }N@!@… ָmn#GZpizp񔃋/:k.N,^KB)uӤӈ)OW$(>SbHخQxɧ#BFu 7Lk\RK6wޡۯwn)?妁Xr1Bi!P$0;we˺TjI6ȵ>G~w*zA=ݴ-Sԓ&6?K-S.$B%Dƍ@.oDb/gA>TM(?%o:6biDBhB( /k!yWWMlD22nFL| B!FH !j'$p &O@!Pe"Y25$"B!HF!HF!@2B!@2B!B!B!B!BdB ?"B(;Bӑ#Gv%|g͛>$#BGwY!16B݃2VcX1ViBr5 [le+ƊbRc$s3VbaGG1+X1Vհ#[3/9HgcX1VÎf:H Sc~*Z*n-f!k,ɻm9<6nE 9 S(ALЀ7}7-^c%ck֚=$ǝ[%i!'o $?k٠]CoA30vsh-'Cmo)Ϙ@rG W[a(}kzo⑃W }_uK- $ v^2dZB}5D$G $F2uq{Om{1їTW;X yXwf3 /$7srfaArӡ!F@ӖUpͯ]8u(_[q_NcX1Vc2<88; $s3VbBh͚5bhb+jX矯[9~|3%'RcX1VU#y;wǒŬ!Pʐ,O aCcHl^m۶km !J1ڿ!y B!y۷\/|uW^&I2V8jhUIq@_SDDdMސH $d<;mA2H $ddd A2Hmdp A2H //$ $d A2H //$@2H //$d A2^2^2H&d Azɕ\+u,Ђ̂[K;;R4̜[.  fl J7Y6ER;#pAa(,(Um3qH>Ҫu*U]c- ɏcx ,%Auj3AH~c1YRxJUc8$oܽaz|r6 -S%=狳i˦e&{[vu+c:"$6㷙,$XY< $/c:зn;m&CW ڢ\;B'2${guL,tkAvT>sڑʏT$ :niKv/?|ʐW{SH+c ΢b'"X6 * 2 iPq/oE~,g+w([%'5p=^rrnHwapd \$;ī]mvdZ9ۻ\# \;еPhovv\<(|\2EK߷R 9z*,kt6MY6jع`qֵ=_Ja|DlҜE VЫNYt fwX)M9D ͂k@Y*~+WT>(/9o,=R_-5ws;h{W+%SgXp ArY9T=2璭`&/kfSCL!Lf"V=}.H&bjk}Z }A h3sM!H aqry i3Alc?FeJ\]<*GGyzO $dӻA2 $d A2H $%%dA2HnG$o߾//$@2H //$d A2^2^2H&d Ar}=nA2H $o0H+=_Ժ іq xlW +eVܕJ$N9$dG2)RyMZT6䫳[+L rkRKqP$+(el؍dۇ.'O긭%ExKަҊY9\-Hqy;ӎֲiⓋi \ ɮ|@qW./.oͤ"9!^/-Z9+n ɮ|zN]䮺R oɮ|@*'3 N1R_]ᨚ=d8܁S֩ivAAS궙CPBM_Gb0ky8w+P CP£BxT Q!<*f^2O $d A2H$ A2H $d A2H A2H $d A2H $d A2H $d AÆT$K޽{ւd A2H#d $d A2H $dj$oiD!P*x/w'v Rd)NW%I2V8S& I $d܎H;a-H $zԾH޲e  $d A2H $dd A2H /$dL $%d A2Hx $dH /$d A2H4$q xTHː\e -ĵ\2K6iEM)A=W{Xc NwiB==KNcp ,$2tBSvVa6[~ZDz24̖ze l_V]xj46މwibo ypE|:<-kͮ JQi@Oŋd-{eP6 5$n1 erbOmK+7PfU6d&_$gVT>KAV6UF|QS&Uű勚Ne Ic.ݭ)[t7qbj{OוL.TXkFX:o[qީ~M߰6KsSXRTג@䊶LY;K4e+EKF[ެ9Kf}qգMH qsJi$Y8͡ ?e-{O1dY+qy i3v4Uam}^o`6*d׏dcؒSYwW= 4VH_$+gA£BxT Q!yzH $d A2H&d A2H $d A2H$d A2H $d A2 $d A2H $dHN.w $d A#YkA2H $ɇ 8pDdqF A2H $[Q?B! KE?*)NJ oCR$c`_'Xs_H$ A2Hq nA2H $8 nA2H $d A2H $d A2H $d I $d A2H $d A2 $d2$;ϸ $@2H ͐\e TZW.9?ּu[ Ifb=BWeP=|7UH~\ ]۲Ԭ--`l+1 9S7.W.tKji=`˻mi@٥T!dUeeus d@Da_A@<Ş|:<-ЖFr.+_+"$f3*z~5mi,Ѭ*!ܒUUN u*zM. Գ,͖X+8_Zq? Ut YGl)=_\/Q)-!~hVdxlieR仌J@ePԬ*VLΖggkHylL6cZ4>ӊVZ)Bn'*(N }xy/N ]w[E8: Tw9EHV.=}.dK&ZN 긭odBRtU,\tR08Ǖi’/0)ޡHv^7H%k-X"9!^,TXrڐ\].3);aSimހnIkq`x@/ىH]Ƚu<^0F C{DUff*?5Si$WQcybVe|iV$k}ճ XcLfiRa z &ɫή2\垯'<0aB$od&up*z^&ԢUű ,P-_Mj/jrF!9[Y.$oݔu!YxZ"V0Ï?O>O?3t?Crbp+jW+,<CPrkfN+ 9ГG)oYqJ!(k{`ɏP$7]U,BICr= Pfy!ٕiZ4;/L$2UIBb_8%. ɮ|?Yaj vBT ٕ6+?}ui Opn[$+Q^r"y}AI\uAБr/W4ŀe1YxK\6DlT,<{ mUwG7zH?Bz\Q.׹%GZtZX#92~a A"QrW;룎O\k0٪X @r_V٪d}lz;!m:-wy£BxT Q!<*wdL $d A2H $d I $d A2H $d A2 $d A2H $d A2H&d Ar==kA2H $Gւd A2H $d A2HN54~B(J*)N}URdxRd#8Iqv` A2H ;a-H $da-H $d A2H $d A2H $d A2 $d A2H $d A2H&d A2H $d Ar'!'f/[_- 8dm) {W2K*d<% $W~]QcJ%rT5˥ *C(Yt$#pC%@aҮ>\S$uQfY[]Pw=-/M$U|QSAh75vNa +=_/ FJ)Vlym&JW #,ZQ~լ|I+H֒d[||aՐ,oղY} dzxCzrdvu+R$˜[|Q;0lʗH*O/aӠr{XT+0HqG?lVS\ɍ)v+9ka/9.ui2+ B މ>([: C(L}Z7jtT+B;R-Hv^Vruk{Ž \G`]4ݫ1=t~$;6riޥ Yg7ݪ]ږ*Kf PpޱJbicJ.͂k|T+WT>Ɯ]}+Fu̒)Xշ}86+xLZ)2V-#fA >3Hvy3}Q>UomCr48ā ?e-z, yCPLi${=/fPfF/ȑ#v>3OGjwiCP:C@£BxT Q!IܹCb&< $dH y\- A2 $dҸE5CBMXAUaG]3qPļБ,yfMӮ믗I8Beb^yxuٳgw$=~In8BH6(}yux|W;GMNG/]حәG4ߙdvyu|۷?4]IwaNgv!$Ф)E[@n+.'&V+/E]Rg~E:q&wyСCR#ko=Ez$b^^$رC%+H|$WвяkC{o|)^G/Eo'|.I_ѷNvAr 'L <裏,ԴqPԼu)^GүDΫ[7iҺ ۼL$AoeT-~Iϭ}cGү[g=[&x,d`1aQ#Ћq0}ǚQԹ͞-IaWo2U(MF򫯾*Ck}\kq+z='LB7lXHx駟A L7YlOz]E/3r-F+^w]痀{t]r1T^*䃿^ij'3#nG+Kq^5/_N,6ijN75d aW4^b1_8FJLR_5o}.U`Rkv1$`?=Z/p.-HF59t@8j޵j"ٱs]_+V~P94Fdmܳ98?3'DNB'!_|wzuͅ|XoJΰ1Kk^}װ3ƿ5ISV#Iowsy^8WoX`w/=*j;tEwD?: u ɬw_`01$c̴2Z9iGotΫoXTO !فSM"DqmT;y'?jd7l:k,V^ ZoɨmO&$uBJS@!9Mm^ܦA[:[dzsdP$=~E:C︧NrYOgU9_cPtM$;<6W{xd(g Cٵ]v@w-@nv A.xxà$iڌ~:w{d0Ϋջ&ǞGSy0(uf"R8p!;&NY52?gՙ8oco?!Gү[g}ʪU+q<FrseF(H7Ga Iam" ֡"y;{^@aETiռyH~gzHGү[;wDq8ɻW`应M/ңk.`^^$o޼y߾}/K#WӁGɻVsMEZ0x]+f^^$˿䷦K#WӁGɿ_1vlzyw˼:Hկ~gϞJ$=~E:qPkobjʦю壙WW^y|қot$=~In8BH/|[O{j{O2~!I_̫È XgifحәG"J_b^^$o޼yҥӥ+X,Jn8BHf^yuH>xKHB-jϫCG2B!(BdBdB$#B$#B !B !!!HF!б!B\^2B!Z/B!4B\RS?KIENDB`spyder-2.3.8/doc/images/projectexplorer2.png0000664000000000000000000004351412626055322017575 0ustar rootrootPNG  IHDRQ <GIDATx|Wǐm&[Md7fCH;!!`R EM4n nmp6ƽʒ^d6Fe4ƶ,K=!WHsw{p1GK$.hExH/ǻFoooԚ&JKKsss`Q0b D&Wl/,hlU'6 kam8۷}5<³r,//o _0܆7< ֥7j ;::.ǖTd5C@./!CjZE恁1u)ba ;bW:ya/n*;;`=4#R̭o6fq$YI/h0/#vEi$㆘7I 8. c_222h8Z NcJwrdy=<{CwvvbVTTDA~Vأ<JDn-[., _Vt-qg҈%c#ƪPM2466^PG5תw[3OGnN?Tf]A[~=\th8[Șeo#Ƨ9KX;yjJt"yYjҝ>u?'<ϸr[޶^ UVT LJ &V.徵WVyE5&r%ho  ,x"68f1R{nbbbRrjX u9s挳qͼ2Ӷ]QZE5RH4a,9<7' 7PΟ׿Cmn"Yum}Vy}amSimSqL÷-o, Uvrc<@BU!/䗔d y9P)[p4hT7&*^Uiˀt>;;P \_[WZR#hC=pO kItK+s+$2P%J`Lq$)ħSf=ΫOIҜQ.'L>33S|UUo\^RQm)19"‡5y3Os2OT0jhFZ"O' l3O+W^c?TӁFyw jKIqȌߴ1 10ftGTth懡,LII! ͍ͭ+W{Fjd,ˊ,ϓGCAfƂyeu%*tw'J9Y%jg8Dڸy@uB˼y&擒ype{faY,cpΟ.켴h1byad̓Tcr~~gm6R9-ZΊ|p*t=%ӐzPYϓFq}eGy}ᝓ2|*r#KoGLHgg9 0lsrF/~|ioz_^ʋ9[{l_ls&-hV Kaŗ8F祌6e7~摏Ûe՟g?OζӕιQ6Bv\;[GB΅=4n9]y8Mo?1G̏γ'_Kv6yG&*L~u{玈; r8be}Vqq{ زkMyKW9 >kӅcz$xs>!qþ?c%1gh2rYt /Կ2!=zD]goK}:o5n?'~56AF.d~tЙg<;rw oÿfWUUa^Cn]p{=m}3((B;;0 "l:iQmb^nf>==U<|Bz<_tKNN컻A H [|||BBBRRRJJJjj*@ 1*̓yV-B̓lxC 6Isb|eՕP{püڡ'V]] ^Im``ՆtEV 6 yB9$v/qD"u+\<7 ϝ;7LgeeA''^;s'`:D]AEvb~gPNynK(`-,zWZƑ˾)KHBI̘ =gO _7E~ZpJNq,uf+}YۜlwveYʽ| Ķ2=2!|/Rca^΁ 3:.=\rM[sja1lL8$;p?y?슿EP}LeF ѷ66?6?=7k.Wy.\WYixһySf ΖՁ[ Bw߫ӃEEE%.y[.H^~k˿>3Σ 3^XZZfu|p7F{><<<33S 4KM,Tu)m{-L3 -++^} r^ OG#':xx WoK3YUF骷Rޢ늎ȌI烃KJJ'KODFF1!$H瑍'nQ qFzz:"G6t^y}#C#^?HGc,;QMy6#g~cۓOp,T< +˕XieX,YkŞAUf---q?OYH?cx*K&I$|CR^YCDY`KkŞҹ|AlWVV~xY0A[AG6<5+FieY$p?̸7V5ݟҟWOD9f&dzΣydCe{ jO`?QGMYa1l.۱2a%ZO#*&&"7d'ߔuR3/#\J!~nUu b~1_VVV[[[__( M)3Aۗśy\Qk%-:rW ļ?u^dWc8yd|2oooqg&YqJ"?2V^(4wtvtwwkbR=Tgdd[Pڀy-m'XL fs2GICt4~\eyPH3Vp{zDTzPIN];7va>-M61 ̻Ee6]Km4hRNZyxShnghp:?yWYE硡!Db76667m໺zD"qӬp)ݝ:̇NwyD .qW{4-~T7~>b.{u:BcK^u^կ6+|yxƷwk'CU^0S/UJ \u4I'2X\vw?2AikoxPހ_K8űkɋ-ѭG]E~T7|6۹؟MЙq;!1nG,#q0߯])XPժƘe><ϯqti[[[^w^-:zeڀ'All󚙗C+C0$ çˁ=`ˮ+Н ƼwF(|Eyyff c55^#gkKɶ^}uyjy?yyKM|mmmCtY |GgWwW4)1<ɤCs]ԟv˵ԑfbՎ->+4a]Boςy= %xa#T6@di$Q Gsr u I(hkVXXX\\LUVT LCi>b3;Cso y&+(5_ ϏBfD#C (~p(~p(<2tLKQydZ/2ɗ33yY_|ٟ>}vC!G: 6&]\\%p![Z:a7T?c], ?OGU:j?x쌘G#]~ HZ;D{Vk3ޯq˝NjMAf&ߞB,Mr=-"Q_kk7̧pK}kkkssP(mllt¼ 6lv78Zjӎ9 ?7 :MY%WVKl)KSKGk7q{b$Iԁa70&`:9E,իNNwwttrb|񢽍iKkPu/]44-ϊ!dwS-@:Okp~|քa| ǰd 4I#3F:M[S\azƤ^^^ }≆ f|=UJO0/o7e:Mh&^DJg m_r\l?KY:hAlj < 0.4FyӴ52O7Gt0L1үEVB^(ޘ2L@oyi/]tξWN׎`_:1M1v?!uuu44Ft>&&FC+{5ooop-b^ɼtVA?]O_wxKaIj5vgꃤ{ sUwI4F b~|0o22dM4_rqro_.:Ş=w L,4)21929C&85i Oɹc uM_t7y 7{vb^ϕ) ~ۇ'f`X`I̓VA=bΝWGrJ=xrΧW\3<ǭf_8a&u}0lGycTԏdcOfb6}no °0l%}ab1W3H$ |~yyYqq{MuSEo/qdDk4ƅ(Sj:o ҥK<@]]]DoCcSNQ)`TK$.hExH/D|{y :?5WfuuIl7llU# RILL\eA tvFH1fm4$]sWW׋ {b>`/,,T0q9<' (gxˊy ]I&' ŕ]xU`BpgZq<#1S_7R->~]ȳ6Mwó`T sVE0Ϧ2u~(ɓh| yqᲳ Av))눭s+.EfXvM")|suG|K|9=*?/]8D,]īuu'] .H E g:rW۝ 'wuuVVuUBʴHLi*U+.Bd>ϯJj[*:1ϣ%[K ˢBe6]Km㙤oR ǢJOʹle&l矞{s?-hqml׉ڙM>e7'&{kg^6{;;;{{k`]RB˳r703-,rfΤU,>W߿ݚyB{urX aڠK1F8 ^^qcT*w=ϔOߘb(amw9~gǻp_h:H*# 4+tcͼE]z6j*' &<} g~3l5eqo7[`_ʷR}աb*Nߩ:sGp>8K %I Ќѥۏy cx* گo}\̺2eY}N0EG7^#gkK(C@ W}bO왝񿮙8Inl3Jymſkݶ{**ܴwUC*l"xg2} iHAhTj)h =dʙt呗m8'W>n^1}ŧRsz&]uF~ssG)ǟxĿ6.G$+__ƓLѧIxo};4m궖÷J\đi &9Ss~+?mZ`1nE1#'㯬j>M|vׄMx3{7dn444]3( ]=}k;BUԚ/fW\?oS['ߦpR:<˟> VCF'BmK[u<2X.BTu^_XOxL (D1t~T.N$DR.`/DJGf:WQQA>?88H́&TuƉ΃3U[[ WUURypȌqi`dC5#y躃+}Yۜl~9~F`,::*LJJ}Šq1Pd4 OHHήw<ý39WgohyV>-EC :?̸t޽{ $kGF2p%Q\f=)) jKCCdzX8# JW%CgYۜ%wͫpJ/?MÓe8p' >ðGEEηCr=&{ޖ /w)xo?ydaE'cᙙYjbX4-;"`RCCC:;;Z}}x"!yy|,C`~uMsK <BW܇FHGQ5:Jᰕ ^%?==^Bֶ⃬6xtD/_f<0RhWG#7:Xǧ_PX uݻ v q)<2U>77ť"--6HzM-- н/-+suu2G˧^=MZb#^S$xVKo)r/_-/ MZb# ȌKϯ:(;aBb9 x)8$vXxn~A%_ ҟל‰ S='A-]1Sƪyd:a~\okk;w\HH(tೲsZڂ? >-=Wy# vYuPbiN$Grywukjc㸍Bܼ>_zv^A!/0S3 ߟCz21bt~-%iSRth5666en OvLYK1%WW1hyb~tesyh༻7+%v-<_3}i>+'|ṛFͷס/^Ǡg}1-x\῵_K+N6k^~uy=1xk;_p~6sw\2c4j%VZnC=b1?Nt|;B2$Lz˦Κ5r˿^z߭?ɹsa0Nu9wIlŌK?vY~ʆcS~?NN_e x<==G C/Zhޗf?{! wsuJ6mƋ~K_zuWAG#7*ONN^S.Yox1y침\HzaTM+XG#x,;;{WK6 _*~ɫ@{›K,3?G:o\:Oק[U[YƃkSx1xtN++ϗVV&;::vuu (ci݋mk=}ڹ3IQ@#-ǥ9lmm}͂|ӽw-x!۬{oo(_mM8xC#ݦUp;wPJ9dZ\>qdǽ?D[.)=[cMЊ}euj,HuބgggC8H;p)ܕ1+K>somit\ QyH,"""***:::66III)))`4]_;>J+]\n, <%}(++ .!!*ymw;iǼ:ZLiș3.yp4<2y躃+}Yۜlw2a;543OVu>Gh 1tu>555''\z@{ū=6o~3fu;C({r44ѷMYO&LM;I)}Q9b|rr2<(<"#ru{f3tj+F(#a(1̀t><<<33S 4KM,l|d,0.gp+b1':\RR=y\:&222006 y q-:^^^7nHOO'ZYX=WT!J'3BLe\1R謇ګ,tYUْxʸ_L>qX 7tEӦNvOf-+4+C*Q['RVMR՗Ұ$:bJk!L?=nZ?ڄٮߵ3|ʬosOL2T0P@\*CuHr&(o=,[E-iKF4.y[1o:ɉ7M1sω{L$p64/f`WWEW.'le7#~Y\ln10w(IՖӴ$[Q})7{,<'Xx>OܟTVv13;dy hieݣVǣН|9z. ejE(HwSSY1o:{EEW-^¾ Oo8j<Wuw%bE#"vJ4UG­?|vFyE/X$~us7@{߀/7{f焿xk&N}{vFؼ;NqWy>h[A?:3 1ot-8EڎXbz#48 CjA~p@y'^:0o'>l7{y!0wދ,} 睛3NϚc3fL,f&31Lc:yWۿ¾Զ |f U399$0'a3D(?I^ˠ7{.GYnEnĆ8C DG|' 9-+VXvx橭oXb)!摍Cf޴{Jgy!>KS^.H^1TW_䘒E ˑN|͓瑍K F<1 H8 =t<#3O,Ymn#wK`҃ id1<|AļJ3懤^5ydU޻9aʒuu- ;zy߮L?*:ϳG̛ìWW3ꤱ1eb ۜ LPhYIJ^ M|կ?J ~~(Ffl6t;(P͘#7=Szm ;~)(W ' 1G{yW6PW!Ȇc?Sa]rz<@#XG7~x9ĚP9d1G+w ;%ԃs8y<294 K.+=,b1L:ymAZ$Ѣ"""`OT9HiLq2 ̳ycBBżt3agTfY:M+~ť僃600@dNLLDxW6.bD NU3yηm]Z*`$ě0:;(_>S.cbl|CZ\E )H5 57i@JrJbSUUqZ֐A7:UBY`Hbv`NIlt^N%,\H(] G"]KѤRZEHHSIU@z;2^EU:]*J'@V0xӔVGr=dzb2KCdr+#oՙ埧o&::6R{wxCY1IMZ H yMИNM!SIrпSxc~FFNCBa4e<~3RBO1?Z̫-{#-qNA#lDʰy#V00 >ltww7K ௑+**zavvvNN{Flp}|0r[/R5IMZT 'ա53FfƐt~穁4'Ufؓw#pH!Y[[taMjPO=Ĥ`Q0bO Rz?OSg\aNdɑvD#uѮ'itٷ穏 ȴ<iyQSVΡm yHt~UQ݋KPWSv̧0t17ey6v2IDATxŝ/99J" _?)*!~8iJdXI䄈I` &`,c,`syOLwUׯ=`fKUuu=]5=}__"W/yw3@wIL7ŋ/^l]IJetv(47lWu/^xWd>#Dڿk;Zʚ/^x-vk7=ԦkCKW}j6ӝOvi%K/^xjRWOI?&U3M`Q߮ vxvezKg/]̥Vo0.;22._-Yy-r̝M6r>H',ogJ.S]ַ37lw͘9<\sM7[߸|k?^.Lާ,٢H"ZOLDַ30!v&ҕnI[eɛӿRo)M:Bqq$驖d_N:`(v&LXu6 .6 !Ͱ]v^ 7?9Zɛ qHM;o5œskgdwa4"1= ?4vdyj;[Oݨ'&Qt'{wMqgEZO\8fcq_OzhIhBj %Rձ1+&=wW`PΥbnbˑ|-]¡&ڗeXdX] _Oxŋ.Ml}.y_Ƽ %jKYAZ*{dϞ=[n}Gh~5k&%+mwĉՁeZBjZ9lDc/K/1_y1'OLV8=ٳՄsm>47y%Cv zɿxGwC=Ï?#G?<>o~ M_ѓgzgϼ2OvKVvCvt KF<]ۖisͳN[ vPvGm2ՙ 5[olw]wm۶?Aݹw4o&9w܅w.{}񧟎Ugvdq$ӟrME5vtE^ذpnQl  K4>ٳgm߾}ӦMlw׾'߻x魳oMv?\p+/gKVOʄXG#=KGϐ*.X|%v8FaҴ]*o>SR<[7iM9,Uktv6mzꩧ93Ӈ>xp8\޾wLsy0O&;o}[ٿ._UlcO|ݳ%N檎?˴ӧO*HV+]BҫK5Ukw}G|xw߿{wow;;x,yC{w[a;q…-[lSyfv*@Z]vlvvhv[vİɅ N8g߾}{ٳg֭gok&$kb;h+c;h7Zo Sե 6+h`}%mbkE:fاd3c2ե3NG+L]WzyY>48ZצՖ(5 :G5 ]ɓ'3ާq_b7S6=K]]krlK8:;pm’&.zm.7V'0uͫ½H}F޸JL_ٺKˌ}VhLS4cs KE9g>ЮZ׸ kxwb=zI?6Ɨ$ށc^x! 6vfb;0TlbYy|Z+\m H3yK[m1KW ;Q w6gG<40G11wtIG8!9Xi+;lUnny.]f|o;ҵV Nk(]\xJZ=z S< qlv\z&EYFj\=_x:9q aEle˖mel޼ٳ̥"t+[zA:9qp`ʕ+/5Hc=j<JVvɛ>(<|:1S;vѣ/13;wܛc^z%l7OTe.LL}&C4[d+oȢm~;k9kp.yhǦ@[X(`.7#vQq8b;خs#W6[j{io0zȓ#_]}|ģN_trԝRajv|&S)|;ĈчQ Iy7s )r )?5Tv#l/:9>k?)ms!HI0`޶~]'0uv{a;vla; v`;l`;vla;vT`}`nY w5ڌ h7ui: tvEvky7w$mnͽш.j͡JlY[T]v ҃~`ʮ]g;aZoXĒ7zɛOߗηyۙ*un|{)k턑aJH{itHA> v=˴9kd(*MyۙOVNbys.d:ٌ K'fOEq5)qoüp$i?>"=a0} \mP Alg0ɔJ>b8NhmW 9EӍ\ΑvHuɕ4wY(6`R iifϟ-.wmgh kMBi;蓑-@6C/f:}&iK=r)Hw!!y3i*OϛWVZ@Jk_'EgUElNޖv#ۖm.ąJ֐Z5)G2K9Ay};ix# d33iS4o0>= 5<+F{D]*;zzu SiT9]+lq x(v-ɑTč=I5~Mvumgճ"iQWJjEV|mv֠qoꧺҊJ[Cvu99햙#֩m竴R&cN4ƀy}>I7k$Sg;{TʣX^Jnu^K^]܁W׺׊u)~.ҎHdέ%_mlxliZWvJ!\O!Hթ{omdV>ޖOenʿܼC+UK?.bT:Ԓ-K^@R.[O.j~xJreDɕ=/or[vH #R AMv n}]шGw~PYC\^7w/FFꝣ~5@ v`;"ge>,;`UN?_~S gsj@yξz7\H`U!$[cNRv_ڶ[Lu+aaQ2mW>O9;V7e;ULMO ۢK1wsK ٳQ~TV [SH.%e =~6ʨ~Q$VO=sِEHKȑ24R5**SqFUNKo;e@+qah6pu)){D<,_wײIu:i@tN˨]4|bslL7^stB&fڪ ֌ N&^uC96*^qQkjjG L*ݥZ3^7mgtkY'eTGFC_M\d5m;)Chl.%-֌ zuK~Ca;⚓0v*{D<%l7{EIb Oh\=</Qȭ=/Դ]gE)ILc}!q;5$אCJ|l:s..o&||H<%_agy]pp]s'2c2< ?m~㍤\Z3|P|==8YW '3ΕjTȒ'Jxr,j៊;6b̸I⵩I 0@>u>ÙiW 9}TJ@3[`FZz=KohcN a;X]1 `;l[y50,U۵v2vd; x."t8ib}H4E1rnqzto'̶U(Ά7?FPӞLH$̘SNbɎnğzOMg&Zyzۥk~r_HʶL7AjAs&,ә5{c%d߮#nC]2ݕͩb;+|e_y2`;}e$4)OT1(J j`P,,OqTAs"F2tZYE۲e˶ wyde"4fٳ{c>ŋ.]?駟^Lğla;vl`;lv`;lvl-v~/?\|& Xa`l7`6Xvaۍu~0{|ٟ0YeTšzxAC{kY\wq{e};e,)Ȑ\qlzɛF sَJG{;y򘣻d&F2:az~$\(HZUZg;YJ5Mخ=75EM77Bf{bB:cPE.?h֔mgQf[C*؎_vla;vla;cN|V/lGla;vla;vf< s`8MKh`: ʰ_ދv+cD =ʣQ!}k5݇gٞ:ۉv?gʏϰ'@vl'sgRqmľѱ<{t]+lgfոK'骣&Y}J9vm]"so&/I{}A'WVvLF2}V]!ITBt*×]]>j8{;qLo4,à7 ~Us0~@7l"ev]ݤevv`ٮdvjn Q18"fߋ<&]lˣsw)GSl7$Fخ#1uu5#< [ze.?k"tk Y&6vͷ]:̀ HfD!)$-6* kuQ?!ɕxCnC2]F]=G"vltv#?QsRl6 ߇юe!>XY,%~hخ#_>l'E>?Y%](nA-lLԒdjb Sv",hRMma;l] 2M{"yp{<0ՋHTvRx_@HOґv.o4q~b4?2q2}Z*EJبމd_t޳ثvU; D!b'h(4PQKG]@lk֏d_tb/?XN>ғs_y6M{"Zk;chEn4'\J79]`f {G.Dzjxl؎_v`;lv #ЖFPgW3H`1w vr!Ye;@bԛbCfITN\*1w]}y"HS8&K1EAt A?N?Qu+9]O@.O8y>lsiDP'9RU LM1t7t VRp: LU#Ɩy͖IoB|ajI|y +k I|yLKiXYA~vuh,_GTlJycKUeH]Hw_re&^>q؎_5~Y6clv+>v`;lnY[5DlҞ4vlJh).v F ]8P#$b Bg,5 O"ǜj).i,6$)cOm "&5*ސ2LgnŰ ʀ,' Ӊyuv3aAto(:]mO*#d8i 3ګdP3{ydTm@lpLo1&0GL_X]@b`;vˣMm-]xla;vk/l<-LȌm癓ae9۹3(Nl3m鯱ň?rؚ)JEWȑc+lN1dYN  [8>scΝM4O_i601&8mrlDi!N#lخͶkB E@+oqe;9lbh"x΍a(fA9 ۹Obc"bԲ'lBSP.vώ8E'S(%)G}`G~`;l0yhjuvYi1/پj+Ro.TkP0H]J,9NFS3Gخ'v #eN"e $~-%fTVO]QŻ# F5-ӧOJU㣿Gx`\GETSqTR[SS F#l]–-[yyŷh3@vv`;>-@ǰlU&6/ÒIENDB`spyder-2.3.8/doc/images/historylog.png0000664000000000000000000003140212626055322016460 0ustar rootrootPNG  IHDRE%2IDATx{pսEAKRܭTT*!Ԓ c޳ r6qrMv-1!,/cK,# ؒl$[eے߶d{tt9ݧ{j0ӧO>*({PAr62`_ }?DZ rdľ0_If@M7d1H~Nj},ŠakXg >\'ٝgs%C@E-!!ξu*}%}USsrrlb_R 1Ԣ}QoaDYJleӫ̜u=Fz{ρe_˳Gf[Wa-c={ ϳ􂲒z]iMOrF+zMs jrP)Ž}Ԥ}b`_ }`_/0Dݿ3 _?ogs%x=1]l)v4XEՉ "ׯ6mڤgʔ)˗//ʾ{foʮͧ:Q---oo,\p۶mmvIS\a!k-YdS-yիWO>=\y+*r(W\ɚ/gΜ ;l7RHRNS\a!BFٹ]ذa8rWwēo7:u unPF82w9ĿBQjFo-^1,h+mvW} m['k73?mim'>qke)O~\ssUWd$ž0e ۺOǶl9;ў̆=ƿҙ-o={\bw \b Z}ۯČ3w{zҸ6{ {}>7%t/*pPeɒQF["ɽ{wqwy7xW2Kj`׿*]ӧ#j&nraiߎ+e2ekϪΏWuܽ}۾o#R]"__Ծ:\b=O-59k'Pu}{4^>=t6pt`oҥBQ3+M O}Yq%8 kUYC\"ݾ?3IGO̞=>.{Asg?~vg"eLvK.Eez4g5Kj%-}ULk].\Zp+ T_-Vg3V}淾-]~+_g?Okԧ>uUWYmnm=u+L%L\Kn?j,-[HEc*X4̮߯TY.Tc_Oz 6 1 8ѻ05;kaߞ}/<~Ccd2'N8ٟStɰ :itҭ"w8;$7ZabfguҤqr%Ӳf{ ٭cޜڡC=bE圜MDotNTvۉtϾKڮn[n3fk/}_?M׹[k1e|DW$mXrBւŋ"W0Bnƍáf5_=l-[X~-f/j?uz'xiYv'~K"ۼ9n-QF1n ^g "EΜ0or)J'J})}/^(/7}ywÇׯ_"Oi^f_K,оٷ_B߈o_D|M7$o믿^斖l|睓s)M0m{MD9@lklvu2qIwx8ԌJi-ۅ]\a$w֢zpAa߇'`Dg \x˗d>sۢMD곯Dz'6c@@^v"aŊ{9x 58{ړU)d9kyw ~6=*!oxUd_n&cKo{ o޻,_ѣáfͷ #ɾsZgΉǏ:}*ދ5eTת(:%h\.π=י|z$;?C u7zi9S;|]tuwR!W!L{ߛ$[XyכrCcMKs8ⱾҴΨ [ @u_Y񷧽[x֭[/t&#*ͻ پ7oZufb)cΜ=s\N/]r+5oh}jtZsK3D(ҬF[_#l>`.A^z[F>0߲;nKd8tPOOOUWr̷b+*0#Ѳdƍ{G{챉'g?.L0A$?zhLp{O߱GNKyJ2-|d2@J1 7仫[zu& _3wxralM6[pGc7wǏO^1yYvm/Q%W~ԩbƲd,]Kymanq9Ep+?~ᇑVDRѾp:}c<{Pb#G;9vу?qoz_O=]vعk˶ԲbO27$zW'~7nx(aͬ;ڢ.^%.}}}/xinrۅw._yDv8p ϳ9sݻEE%~2mj!2{xak ,ٱox6q+ğXlnn޻wooPʚYX]vuuȕk˳bn9im(=n_kߐ0i+qűox6ze&k׮"{@C0{]9mmopGďoPtr[}!};@±/UQdrNj/qFgdEK[郶 [X2ůx;+[&>o}z.ZN!\A׊ğ8;}"+5 ?Z[[Of񽷖ٺu̙3`ߡjpKUY\aX~iӦMqL|3ž=r@Uf8xd!@do{"2,/`_ #Ͼ wfgnξƿmYDR9*`߆?w8NP9˖-}Ӎ k>K {Nەi<ضiuxmӼ b5h"XPTN}}}b_SDEkFpSD*34i~Ⱦh=qv 7bꫜ7elŴPoͤM%aMkB_}@ CjIU"CaNj,-Z071[fŢʅmo^/Ho'eC>"C}u}%b9r市pO+coH뱮YN0YVcWsDZ˾j9z60DbT/,oe׫D*]o2vJ%=7/Zp7 y{Ά|D+B}nEߕhs#Zt?U"C!M7&skNcV2eþW}0"1(mT}'φ|DbߔV+)U:Z/`_Nے }*a=/TξOhωlGdVa%w[[s?^,xVA_ĢHDR9*d߃ EdJi_}/`_}b_Unڴ5E@ fa(\b_(}.]*\rENrɥKDJ{{;žP??ygΚ;wȰj*} o/hv ,uGLWn= h6W\O8OO_. ^xQ|YdI%}`ꠍ޳Н:w|6{lI:Du6&=I/^U|hbv3^#Nolk5@5䚤y}`.\_6lb&so/_o_DK #ܾ---K빯/&+>_s_K۪gN[9l@ ,8q̙wƛ oq7NY9/^վږg}Qc_6Z,zO3O7-Zپ^Wi_Tu}`DWhUܹ33_o^869zرwN>%r~#r/T(X~߃q~}]~/w6>žP[}?xt{=Gٹ;ٷg='O,X}o+ОQѾ{>}Yff͚9sX1C|f/ӧM:uٳ;::oiko_MziopP%"6 嚠#㜃trrsqˑN9d-|;sLdozM\R /T־2lPzi1^r޼@+7og&FF݈Y'=o9~cO´ctơWt}P}ϾR0GAe|J2Q;mF]_Dȱ)+ EP9jF._4Xxe}ǣ+'ȾEĚrA[H=`Z}N 5 nth}57F=J\{RXej8B¯h|`9R :kJioiUWmjj{ -}޾|ސXt}5J) ׽l~}{({k߈M!(xvy iڗ:\уN0c7|Uط83&ؾT}'m o:E]rcp3c\oi5[X=h~Va+.U]etmy.}i)w!ңh}1^WCh_FB*YVupe쫉ZV)vPGQeq-k2簌}*ZpUzD5þLfK%sL%)i!7yQ1ȝ ɤ2.u evKP ];&rnLM%jrK;)ξ:-aE0R`7RiηY ~뼏us^ }^ʈ}Y50!o<:r!'Kө*}E97vJo! ~q"תc_o;_ 7U#1”skm@V<5be ٷr kEWz "#poUN ǾJDaQ]:ľW?7(ܠt--{c{^>oc_ }oc_陮OrBfM)̜:ujΝ:SN}~g?Y'!oxgd_w4"Ecn~ƆzEAPVΝ;ۻf!U#L6mٳ_7o\7o#wgΜΝ;or…6"Ϙ:uUzzz۷he\ӧ-:0tl- ] i uYF!zžb_b_#ľU&}`%EqoW(6\WP-9=(> 5fbhlhJIyr{<} =e1sn aжvքu 1kqoaTSZK<ǂBWۢkDÑ[zAx iǾBuJ!mb٦L:nط( )5{354fb~#J%g 5#xn_ِp{3S{`i;*ߦfCo$KJ*`Y~Gj y.߻Jی\D~_hb_b_`_ }oF,WUFcB99c z< xXW0ick~ocߴGPÉTR>P&gIgyJo3 :"VJ2%ndm:N46&ݭrtALN}97Ko_|t[\\[`xLH N]zBRwk2FܫLe{qfVc}G}"+%e~&K `_}b_}b_ }G}a"󦻳9`ߑiTkβƤ;t@z+]a}kǾ@әi0$T7à4nvYƾP㱯:+_@Kpaչ5YlWg4?n7cngAzb_m:ڔ>uʯ=#m(]I_mZ4&1olH]463=φK:-Rl?/ Vjyo_rN}i/`_}b_j_T]VIk yo+Loa3 J)i A>˃r9[;ycS3 =ToMeiyr=g<'Atܧ8W\]ڷ#xT" 7}=+)eN,0r^l\t[R&̾\ ^PKϭm}ж$7̂ƲֳQ|JiNѯZKmUc__볹)\,=]{Շ̾|h}/`_ }/}qTS;4 33/Do,5n_id*}c0hޓH[IYdW3˽86iA>a["j?`l8Meiy^e(J,HB[2o9\A_ }KK3kMѯvvz+_HZKyZ͵fO{o¶V{Շ̾žؗ6* -}mb_`_ }/7}2Qx^;|dUQU(طRo}K`"3TL #_}kȾ8T;ƞa0Y 5㟆A;a]~"(Lkn5ZZ9_+Y$Ǽ~ Gꄻ%|)?אIq>4~bSwKiCytRM* ioѠ\A__m`2o7_@}=+LꔊP r-0`ڵoo+F}TKϭm}W/Zb7|m˳GSkd'Ղ]+ލ4,̂ƲֳQ|yK3`u4&KW_~,9集ݍ4BjRm%yKc_ߡkŒkoٗ_z]eJGqVmžžb_ }//`_}/ž}/ž}/`_ طiohv55g H%Z}5饦{{Cjv 249=h(cnT\9h>NYAozM\o-W\ƴ J/-^ԫ\ݛr捀ȷ1G<᯦v&c#1]rozB>J!^+-Kxe?"Ŏr9Qt.**:q7ݑ"[(7AE|Ѡ&(+rWEĚr9A[H=`Z}N 5 ih}57F=J\{RXej8B¯h|`9R :58w77J9CSy :B끾 #~a*---K}l"URӕ/ctľ+P^6޾AoĦrh<_a|*$WlS#̘ =~oUo;u(E6yo`΄ оT}'m o:F\r4ahުsr(m-'| o w/.\74wȑݻwwvvn۶Cd״.2wҶ<Ͼ˴ dL{Gsn;Om1΄krn.. :끖gm&jZRuy֧O"-F[3ζbP*iMM;X=g_T㗓VK[zwi_M^Jշ 8N˳*}*{Q-}/@E@3 PUľ}/@c_gR:A1-u>GQ靖8VW "7Eqy裏9rꕒH$!R CSHٮ->[;nA{5׼۟ė 6U֭eㄭtZ%;X~Y9̙#|3 m-ʅͭ9_UB˴>i/kAi˾[qgV&YS&Sդ[Ag"CtvqώFiDc<*"ǾٳB- YZDcVDz!ѣG[Mbe_wub%ҹ^k뻣TJ'$^9ͥ&ZQ>Emre7֧=?Kj˭ԠH&UNX7u喒tůiv̭Mrݤ\GV8Ju #76k8Dc jվa0ŵ*ͪ`ٗqf :t@>9rDŽrc_b_ ž<Ѿ;" a~^0{ԩɓ'9s+"?j}y@ݻw0`kkkK(/r5ǾEwԩQ2ljޞ!f#TS_=?TJ2}=:nePI4:Lloj߯t_g0@rXTN`r(ɌsXZ8EnoӛR}ݩ^ڑ| JDQK_ϣ_ط?Ub_e\k"ve!3'0=McVɞoPKگs~"Yז\-ْkJΐ3O(G։S\ʩKSc_}19O ٻw g<#P21?QSþT},O}Si;S:(wN>z/G\k&gC><%2 P 񻒎ՏD@S*6T`_(}'Dk}xRƾ!#Sf/}-}XSQKL)jumז\6/ }Lz[doL4R{5z:<{Yn.Is~<DZܬZpZFOS76Z˳-H#vOT{o֏4\yY=P e6GgDj<{Zu{gewOUwW]k*Hߒ#Дl7 *'r0ۿSV}~ݯRo }s?` c߭zdeѐQt5iy.x*d_K[}`R7hCz9>މ*m*g_-طj /jb_Ծ" a_W0y#K/e2RJ@Df͚U@Dzϟ_}@LnPJ/ }m#>2IENDB`spyder-2.3.8/doc/images/git_install_dialog.png0000664000000000000000000007352412626055322020120 0ustar rootrootPNG  IHDRwIDATxip$uY<Lx>ċ8sYnQ$dMJT1c=?[\L)eKH;(Ծ¾6 4VkTH973++YYpq͛m l`jjY|AX{;=z{@ҁ0V;wܹ'tLR . BKX֟\-R1d%[*Ih=n;`MM563,4vsJ4n2, <Sy4g,f pوrCb@$l_+aqKL>F`1zJ/^.?6ŗ!%!8XU > _*[;w74f <$1 ~I %`pKvq@FƋCo:`Nvݔ nƑąXe (ϸI@wk;I.D<߅gw ρ>ۘ|Bk`ja=G̱~#nIX/=Kv*eSzeoXqʳC9%W<]Bܩw?;k{N  pݼ 82wl8k`bBŗdccF =ݳg=zYA!jwTG^x y3/(dw׋q/7VOدWº*hO rk?}b>zrRhM=blr!ed6^ד&tς '.2~ >WUz4pM!j wyzI78b1xD3&^/ $ HhyRӃx=K}|Ub$ti7R{bmq'Q12sBb]Lm0bIz_Ȇ~~n?J ۍMM`[q۹y`$k55SS`0@|u`0 6ݹgjw,kS&f6ϷmB@pr=Y@L&P pS)բ8'9.o3?M'#uw_-!S~Y^=bK=; 񤞌/͓06mw@|GIoh[[9??g'{CP@F^z#bQcqЈwz8jxxvʔqkMi{Ą&n(\ܢ( g=r\<&Ŋ @271#Rkh56NLTݺildz&t,F(ȶ'{~? $L7= [,D9j@{##rk ł10>rH)gl׼Uwݨ{<WF,ڶw;O@a _~pSO,c11{իۢb$7yj`*vuNW;o# Vz}pﳟ {^4> ~`8ؽ pu)݇}a1쟐=ůg~󰳐 s.… + Arpszemn͝XOO; mkkX-ڶ>60}]FZZc-|q#?wHi+K|Uwrgc2L&Ԯ7D=Qt:u; kt\nxdg7mp10k'g}yb^č}^O:gY\Ť 9eJyn S^z֯OrG(Чw/|`Q"/*5ʏG|*OJ|a .Iq_|b7 `ʋ}>c?}s>wi`CsW}BA? 썎X~}7<3:K.]dgzpc\ eoc:ȅa^Ţg Yw_Z{t;60y3ӳݽѱ ֶںںkVnj45G}bosFM pXv=$05'{a |Wd0pDʳXg шƤ3/g)ܬ5S5;E|E>wsfs?_!GnL_NwbC{# ܭ0pCͱ;Q;wbwwFvGwuY- կM#;-7^pa[o,<[8>A)X;z0:wkSmltv57WVV kކuoFƭznب_xܞ5g^%F] O=C rN$aFc0x@j׺͹fK\;7N*h 4BF˒n[#4wl3-+f명 .N}A|CP~N= 8-}etZ\-}v9"`nGsNCge9}nް֭u67-f$+0zG70&.w%)|@b>R=ʛ+z 8EK8Cڟ<q/ Ənqoz`tó>O-*Yf#0Yk`wG%m~Sw߻p޻nk:;;67{7\^~YskPt& wFFz?xJ1:41]Y=!vg Ơ?> 3 LnAsvc 866wcaddo_UUfwzGj[rsRSS1m{n/1r/1؊Ulw.a-d6b&kl[aݹs9].۽r.xpp7mn≃{Nɶl./ ZBe|EBUB~.Xg ใ Fo 0Z5I&X~@ga0 {;-kôb7،v3\wX6Mk'ىm1?6@0#v63 ŠY̦n qRb]2UMKj̵fnp 3\ks^gʞ4)/g=5s^z)_u'ڪYO@'H{F(?\Pk(3 EcgrX !CuPd]6.jdLUy`2 adPPs`UKƪrQ_:[[䯾1Yu11U.)--'ICeOXo ~ @x 'JOҒQ~$a)ٹyf~]Uy08<-۷QG*@>&b|d١!-;8"ʶwRww\_lܺ#u9g>~ .}_?O9`ހ}PyɂŹhs-vל9p͹<`Sksw۬mo_ﻹ۷ӻݻճZ/ծ>@f߭-vۛ[;;8#Kuu\b`N7u߼:s{0v͛]km-p/̶9e`ћfj 3Ն:_^+jULUTOWOUTESeSSJE¦**3VV/.WVOW p֘Ge62)Tk$FqgK%SeU@smqzG q[,w,0keG~ \xs0}PM j!6US6Y]0U]..))-[)[-[-1530f*gmx #gjgke0<@IYE*3]S<w;{)廒^Hs5<"Ѷ6`EW'O>UgI?M`)evWn_.,3p.o_+~Aߜ ́kmsf>mMY6笓~/vum p uxol{kk۽_I\}=77`2R[R;H{JGZwFYdG[}]km@pcC]ty x# W)ҚI2{~na݂ JNJJNjʈ3VR6Albb%Ċ`|DidY4Xo 6纃P~XWMee,:C ̔MMsVΖ>(O\~}_"w)6ݶU1lY4U˦c5c7V_zhbWF*.+2R~nձS@󢙪ʢiY?YqcXѲˀirSӰH5kU7+ ~:T`G?Uk0,x䓡O~6\KPhٕruu\omQ_Wg}J~}r>u:;GF,)TɆ:jSSSt vQ8kEWn_f^-V2tt|Ϸ댍~Balzto0OL&j %_ZG7w<~׷ ^4w.wt,u,vmKK͝K-}otv[;ZڗZژ0 }CzWF7Yggtg. ܊olvv,׆C(o[đ՛kӬ#_K`Մ%UU% KF 8Xa >nrbcĊƭx WčBy׺ձF@o[#O@|&bSR> "3#OŪEג`N]bʻv|rX>]/ďV]rPC 6T`٧K+t;+eWʮ_(1IceW^)4\Pɧ?UOKh pUς'^ /~ҝOn܋~_˛W7WEU@. 2^7u!SŸa 1QIОIt>>~>#wUzF5Ls?ʳ7)(Al$lXHahQݒʙKgv+3Y=DMZ8Ygܛm3Vûn 7vntlmnltwotvvt/..5ZB…a׾ֵܱѳsΞ֎eZa)(,+Zm{oFzoFwnEȣ-+MMްt:v;`-6o2VԱ6]#2,5cEe KOMc^\2xo0_>YZ9]7W/<@ILx:uuRQtqLi,\**I/k|wByZGz_  =("$A=WLaZSŬl@:CKOorc{PwJ/YЗ]փ0e_L?P@B,|X Kj+<\ 8E@cE. $%WU@yEGSYǽvʧzKivze%$ޘ^Q@ć,@LKKVZ3dydETeTUtula XN@Zid:힀!ؼhw7::Z[W_7B Mz0_< hh߷:ٳѹֹҾk]ijYjl!376Oo]in_+JGvgO7۷uwmwoVްrgf_Ee`YЛj 5yj01\Ul~_Q/$!RJ+e;KaxIpKa2 ͗W \i׆ӳpo9k{ ѯ,q#؄j깲r*+ M}h#u}#&ۚձtG')Fyc^۱$eTzNk' kˏ^|p?d}Ϙ8UXd ؔ.~ԒkγA%*&;17LsGb}JyVuS$d*XOS/}Z?C蓱^7Sm2& 0S7=\ih4σ2k4OL30b_;.Oowlwmut#WХp}R}SKl[kCSFKf{vGw jhi[6V8k^a_knh%+E]=ݞ]6A{vkˆϷظV_xfcIͿJ^gX= ۊչlq,[Kf[l ,!)h0sVgc7GC9 q9sM}=VjS17D]n`6\LWcKuua}RahVLr*\ 60.41lMbs^b^Ũ by+jɦYk͕YcL] x~}!"m1-їK爕3Fjԗc> x_> X4y&d^_H 1OGM5!cbU<[ 0 OU^cLi:>336U&\sBL^B|],)^5RZF[ >SO떹>=˟ˌ-9"se G c0 <$ezcFSVsvk[= ma0 _x֜p}l6 m d m`і-2g$M$눵wtv3 {w;:ȇjbLkBM[ĸ$l˓Mw=1w[w5cΚ}Ֆ0ª3ɀc&p1gŎw?شwמ$ 9v8y}ӕFr0_u끓DvIICI;L籉dq_d܃w4W̕ʀjV0UMA2yG&aFbnւƪ75M5K&ݨ{U~ TAP,PW*0"=}bL \fx@։=/~=jm~R4W`}T5#Is6O,z&_'.X#c \Fjz5AHbw|a\&ߖmmʓOPFZaeL=$+3c->;Lj;vH;vYdwZۣ033w2n{k?> I[[c`--x0mjZ/ihe xI*w\-w<$c|$q}M ṛLh^ܘ/=1M5|;7\Zzh9n++PlWsps|Al/W_VX#SgfruW++}ܖ8{DlX,BI~*X0X3dҸeӰ!_E |[00`lXl!n-ͳp<;P`=L,8ق6"D;%9F~(EIa8Lfܸ9ga8=#4;~㑨:ۃ,B_mnc+,2뼽7dM6?kk3y c(4&YcMVoHk뙨vۇoNMfՄ:u#a\Qm5Z.4'GP(T@N=RB/ۗg޿ocχGPFC& $kw\\ PA8Ԥ67?ZZBʣP(cA<ՙA(> BĨ/\ 74 Q(qzbn( 7_'Eȣo2FyˣP( 8_ZQ) : ?_R:cĚ( uu⋖X: ?V-go;'a"5>Q2~Q̲FË>*ϛģwG/gS7ˑ]U_^,;#)8)(k_jvPwPOQYPĠr$(?Y^ϼuc+)P^ZG||/IO,Gt mcԹ+ejG=^P Y3/]uBK |B?ygʂ)SJi^1u(,ik 9$6JVrJ):˿] Rw449zAĦ&Q_^JϤ722h*}W&-LD+'*7KZ(S.w=ϿWjh"6P9x˻rgl5u;3˧?[~<@P=ۋEns5TW#|knhޛs/|%Z%* $ ޘ>Ili_ %^:%VR|(9P(#k'_9o ʯW{BNR_(`]{^WF(Ңlh pP( PgEx]JXki"rY361*?%?L?AGKPT*c:,!5^LuP^7'} _)} )Ɋ(ʧ |W!߿?z/&(~E)!NbU @\|D*ޓ\葝z_3;~{g45ǿ峍7DI fP*J2wf˓F'K/+-Rq4ǜ+8٪D|ki FzNjTscFҌV`ǛO>❫yCO~WJ{mIhw{zd)I(~{xO|#W 2'4Q uYQ| D$7X'S]B ʔ{ %o]_vHP:aQl*"iͫR%v(ݪuC$A=?W^ſvp_; /\ʫG9gOcOEiC (${1LV2(rQ+eLe*H,Irg*)ZJ?]nOZ 3'_3 7ρ%H,CIy,ɹAyx lw.;*6 B.++پ?(( k_~Noޝ煗QP(*)?]eؙ{(( o>6 B;:te5ѩ< B(KkˣP(TNR]7UMϾQ(*)?Scؙ{s(( KR~hٟ_x(( c'ニοBP9E坶6݊{/<6( grR?( 47?\Z( KJ|Ew6}pooophbP(*ed巶uu`>#0&N@yl)B!Oǚ;zH$ŀ; 尣.V,͋)BE!pۣ;cwܼ `5Ch oL&Ȓ!V`0Q(02ފ:q< u7ʃt8,* p08hZ]f6'f8HA,=ݚ~a}IBP9I%pnˣP(TR-Vg;*6 BH~`0 LqcP(TR~FIt|^FAP|Ս\*ؾ;QP(*)[#o>6 B='׶ǐ( {_~ˣP(TQPP~ۣo=qy Eʏ]-M9l )ҝ ^9J>:ƌ Tn51헓?+OQ|=οK'*ԙT\)`f-B-CGL"jy$+A<{D[JMWmFն]0Bta]㞾(Pod{%;N1).vq-mqn_^Xr$/ V? *߉GO}̣;9aq%( CUSwJ-DբbeU.r(,Q:MɖRn&SQSTm9%J]]2׏]\zZˎKE4j.nJ˧v$=#+4b#=rA?#6YE؈\է*ǂM'R.q4VjTq>_eU?}y|$Kii$IQhns*ې(Mk+MIvk.Jj$=wV]|ytz)^HM.LNy٪3CyqI$KN^4OBymN g*;N1y:V' V} FyRޟ'hT!eHFtՔC 7+*mCsLTDQndmNN^UyLzk.ZS#$)mWxFh%.(n z.TS!zFSpTԥR)HVoʭ.H 77pUSQ=YqWQj._MByZe״] P6sGH^a*˵gn$ۭ\< E=*GB@ r鶝NB p/N積OѣrHN'RBrPHy :]?qyLV]*@;0$)/~kdnԫD!EC7M᱇#Q^8RPG<͖pDl-YUNxG*R·)7vhJ+<x^~jkyl??j*IF* (edRHjJJT*+(Oy y9VŗkH[Fk$VOWJ UG{\Ns+'ż%_/TJr-Q3OQmΜ&/Ls+䯶 / HgKdQ|ҴeJ<%֑s{ƜV^0(7^~yKJVX<0rLR3q}+^q~# ц\ 7_U_>IBC \>ђǩGlJyP㦼ǣ[,ϾwSIԓU"Ӓ)ZRBS^6RB-qlD+ѓǥDyyP72_~lg1bsz*>R򚉟LQ>yF5Ѷ"I {iW3‡TȨQJ h<=bCWՒBWcRNm,#ɪ"6H+)dS<&݊䏬036(OۧdXIj©1T Be>5ؠP(RvaIBPHyWDr-< Bn%rRG)" BJx|w & A|^Q_|yS8_2#; hkT +Yo BUt[ j$;1RyO/yD (h<:eϓrB!p#%c8^^^ܫPzjhZkjŸ<#CS'׶GƳ&9oWC7CK0ɤL#%.'m)(f]꼼y!rlD xv%.Be#Fyjd):&r={(?88ٹWnto?~uO[/0 fP${1;qqkF(|`A ]B6T$PEfzlj%NG߄$* ^*ܝx빬ORޛԷ}]=I)4؋:o%WByOWTzI >ZJ(Zҧػ-ZA:U}O')}SQ6AV1BCeIͶBYV |+ SwϽ=#-Vf3 (< ӳ?8 6 B W, B2IMٔBP</BP$.'ۭz( ߭"BʣP(RBPO] )BP9(@`ĸ< B(c._glP(*(P>j#Q( )˟L7Bz%sAf,ycٛ|F˓YuxbsL+S8)SqyדIL|!ivGCyc1 쪕*pt4}DPLH짼4.{EyX$_8fB}la<*#gs4b#|~ މ'&7HF(K+b*dQQ>qS#O(UH^$'B[%M.Hm7j$Y~Ia9¢ͯTUC̢d9KLyJVާ HӞiع؆@#c#cI*4}yo}'N "u)z%SP(Yq %J)RGqň()Y!4Bo7j(,Ha;ZeP,yyy%DrI\GuTSF ZCT`i*bʳn^NJDU2 C=kKQf-HZjQ*L*MFTy}Bɒơ#5ŽVPX+eeOLK:/O}͢tGY}* $5@y&iJP~h(L h(ʫʦ:UFaG{w|' %P㢼~t(rOOO[Eu&iU|`fFXR oER w Xm(_%ny'}(\$dI ikI )nAb55Gl4R^lL:F RwAKxP"6 qjۤo>p0p[UoU`Z+_~z?8b!(,,꽯"6Iz$: *Ez*)F]%Ⱥ1%uQ[+]nJP<zRnX6IQGby_~D)`%=e*O&꯽UVۧy 𛟤UO;e;w^xBPOǯE&>"6 RB G?ϏN (( zdt)BP9v'>ٖ:Q|̤C^.ܝxsd钥vJJ#gsYHߖb&L.SGjsEO궜ѕs6(?y86>r s{V.x<8)BLqc|"dc2$:TZE, 2>kvj"|zL[~,K2!Ke7i-%8ݏ&cge%`ILVT\cJk)<`kOuG)f~-] iIRM|ڔg}%9)VNJj+gR<%y>4ir(f4KڷN=[Gd/yMʧL[*R+El~B`)`)ǑF-Ŭ^O.}*.rK Ny~΄^FAPբg_FAP݉7}Br#]#Q(*G}nDFїGPo=qy EO^/O9l AWNM{el Ajgfpl Aʯ9B@yIADP(Hr^&dLy ^uʞ';'yp*JC픗_m%54N4j&;}Dy|("N@1pxbjzvnEÇ=zD`I)b?d;V#%>m) Nys;ϿS^zz+HU۬K4s2?d!#RϮD\sУ3G 5{NPPhIx?;rRvtvO{vvvLJI04nĽ)OeLTdɻw8qʀQm; T^%!g*叾 Iȑ_5Z'<$ήu>hl͗Vg=w>{*G2 ZkׯfP(R>v&V %(?~(:6QP(*(ԤG4w/֊܉}PTk+gReEe[zE#w6J ǝ/!J;W1M@A?U|u1cF^Jê7uFpۤzm?T*eЗ?6ہC(BnR=\Ӧ (Ր(iبD[ǽA9Q#Qm Q^ znPz=?ڏ{(OHc v |S^%ElB rLrJ)<9Jsc~Gi+]VWZOFɉl)[EcS^9BFk/O1,`?$.OW(l5eWʏyJUQ!lf~`P)ܔɸ<*}U'lS;q]o>cz,m+F^36( (qy B .@GŜ( ;esRn-YōR8hgcfqO "ڙ+ ʥeGq#Jtg\L 5Mѕ>Zچ[ 7At,2춒9oe hd zi+k} YZc>:ԇqy`K/ݮy<_ /`ZUX+ѿAlzѕu8]}z]Y[ +]<@6IbZ#}Ʈ:Wh64ס@X 汼x3 S me.Ǿmb^N:bd޺#ǕG ԱyʐP툳W1.pAy/vҶ{cذ~'-fzfeB@MMLl%,SL7 -C/P ?tg kvSKNT>`qh@ʻĮFpVBj1mQy ; &M9 ~TW8K|:UlBrɍ%g p8 =2ij 7C9R3@@w~kD}Osw?\< `W^O_ֈ"Z3$њlM"=/wghf^,hk*O_ .B{,HY;@9 dڑ8܈uD[Nll3ؘ@9˓ěFy%Ry{Bóx}R*r7UyL&2#֭~@CMŤ^sjwdEkԽly„>': $C\*xd}örw<#ZOXns%6oevH0ec12+%]/[#D(UvE*8Sl8~jJB03 UV-I+YPy娖齆˗HٝV ~@CM?CNK?Р;ɤצ_ $T3kho_;?+RhP/2@ql&whe ʫ1)kkjL=< /Qz/ULBPyL#`upv|ZQ*퍀a<Hdi/TTU( CӝPyLc [h#/tuA@2T*U?'**/EEEIܓHr6`ITRjciBזZݒR7 {VTy)Kգgl,*cYZf eMke{ ]. X3fWI55zGXP+z@*8Q5eݨk,ƽhZs,kiXNMê0@jKb pVy۲,"*,K|M~YS.9 K,'9 )ݘc3TRȒcaWQꯢᶈ$YrE:t\sxSy{=HkkF\-7Xw_Bc8ccuKeΙ/Dn~:W(VĜĭWLqIJlpdgcQv˨wڲs '/'?` */_M?):Obz? Py@B}Ƥ k| `jU~ZSyTyCDQ@Gje,A@BRTOKC<* *+JU~`-SPP `Wi_N#ď~rx$1ʃʇ}}}@|!4^?>k~,_G/{(hމ{9KH嵻')\^È/#ݕ4%ꯢ`ܸ\*? Uyc2*;)EGe2qh*'<r !xN}cW=\^~^GFVO&%~},k\gggƂ`WX@A=*GzE\Xۙ݉M(N>ƶK\lKD= Ih^Q\M_]38S;0j_*N ʮ6ob5w1x$3폹*;sPE)Jk˽ms5m!I*ōxʇ=R%7aY& 54"6-ߵ9v듟F_*F~M\J>$Ӛc{nB<WLGgk:'%%+ʘL[d4W^*Y"Pyܪ|%[5mwTurrrW_GF?|HW H9BFҼB<q+]13IENDB`spyder-2.3.8/doc/images/onlinehelp.png0000664000000000000000000003453412626055322016423 0ustar rootrootPNG  IHDRdD9#IDATx r;l&"/QQh:jDAMB@DIYE.3 02efɼKw|~N߾Vuշ  <# 5AaĒ  K 0[ 8&8AAHP"mb)NԨY=KBK "R}˵-! բR^ҳ$ A,=4.)94*˥=K]GT`cɖvsIA)a=:Nt|. YT"zK׻Ois,Y듡..rIAdP,}ޮ)7hu|CI쬹!ty Sڙ0lbbi6?;#AD2=KDcjh%A1Lx6lё5óܰ$ ˴Ų^o7YSSSWWʘd׾Ҋ5kpD:bcOYa[H,GIWw}]:w=ȑGO7oAKXLhg?bY]S3g_kăcƎ5fԨQoH&f(Y]s?qM)]^pD^3fȸXn_4d(D"}qqSky… eN3O7~s-oٿWe bXDqS,?1|@8-Cɾ47=JϽ'nDSNzW׮]ׯ[6mRA|ú~\+ڞ{wGGH_EK1׻g=7zv>GRY>ÇsƍC Y0[ oBdЈ%QbyGKmzI>9r}Ȇ${8JVGo칑;Ų}K/v~;}g\T?IN]ʄ2e[obŊxWCXls=j*Ԍ5=Ozx7SHY/"%(Rva^x֭[z`/낳~?97,V /_~<o~%K[O:@"E@\A, @ŲGKѦÇêvС a1ri%W3lss/ܾgߵ{?}J˄dݭ''O=E-X@dv-Zi[X~KbiK}ߧPy϶}/k=nxNO_P7xaݰO߫ ]Yeu?Sw:HS,GGGO :sǮ{}Wּ"|={3]8m[nM4tͷ򇞗4ho ۧo \}u׉b٭֙tX"DыeΝ]io#j~ڬBZñ-BIY~}=3ɊxΟ R|ƱxDZ#Vk[|1޹ѻ?ͱcF2JHp455 (>EsΝ;qƥS,_u{f7~_OT'4|qKuYw]w<ջ}y 7hHM)R$+.dkkk5ss_bƍo__:li|a鹝~:[f?qU/wsճW]ݣG^zr@^}?}zW*&ИEЈne!?HXؖpS֧ gs%ȄRg6QH4C>ӥAkghQuKrW8:[ yϞ>v؁C>=p@ّc+:?'ݳD"*0"pxɂ]6o}z/}zٿ{kci=zb8LoGP8"EwSdcy8,gl9ug8a춯mS:ͨ6?{/S96_w?zV&)G988>}d<}G9~H]{Zڎ?vtKhiڵC ; ٣N;]t+rU{:w: xM~ci+W,7UrCBSЫ4Swdr%餢Xaci6XĬ%QbYo"|޳6lguϏ=x-=@N~[D=oW\~ŀ>YE=Nտ_bE5HB[J}܁ ]Fs1Uxkc}DMߓaXXraoo7v_'bM#}}T]kÏxy PU}~'ʻ?Qr}#9~Z:|>{c^#۾pDɍ%O6\0ς+6\7L\~Y=ǯ[7 ^pPj"MH_EKQW=bg[ZOzvOvnO>q+:קVuF0~K_0j n>#~ZZ7KZ,I>Q.;-5=KlorF?b)$ G[։mɓ]z饃߱cGrJIV|4&+b/ԭyun/>v`/z˥'HXزe瞈gU'Oc_[箬{qCuƚF5}XSXG.X- oo RiE(X⸉P;6[>\?\Ex榽66|X^|}O|?tι7#"߷nݺnݺAW>2*K,BU"i3Y%tðb9A0-5Kj=%[,7)!ҵŏX^s5,+׿{,2Lڽ{.zsPɊdE|Ɗ O7r͖K|_crȖ!S4߾rёٳgG:Cʕ+׬Y#^DǺ~~NϺ4]?<m2|RˇzH4nMMɉ'N4Ipğoq?3"lwf|z))Qı%&P5_ٹi׾]Ͽ?<غݻj?LH,7oSO2O;,[lj8͛ʃG~Dã# xM/[qnvAnewѴ9:h'+C ݳ|ʺt䒮kt={];ReľϾpE??9&_x4vCPDc)q~8 ܹsE&!lLn!{*>Y 5"e~m .]8k__K,|Oy{W8_lBqYLG,"K)ap?=4;;ӦmۖF{o?ֳW)M<0cƌ <-s*ec]k>ܱi[ֆ)0|z(gɅ]{m-|7"eb{(.FK@ܱcG򎺺Fzj*ɾfe;z̬6FcMMF^=:0&Khnn.//G Ē$Ē  K @, $ Ē  Ē  K ȖX67[kkkgΜrʗQYY9mڴ%b E'sٶm[ܥ>bOT,ӋX@xX@)0%X6[!2B3,#f7 b+RF%b .F^GbK[pmA,rӦMB79CނXb'K@,KȔXر>^}K(RQ^^t:u={K(F<~YVK\ "?K nϡC!٦b [n裏>C&b˽{~K_bY]]XbKV%=K@,=J Xҳg @%=Kz%Xڋc@,b) >X6,@=Ka;j"? .1c={hiiimmmkk;xÇѣǬ;#X"X"%b P4bZݮVgq]{>:걵Ke K8~{4?Cqx!}{aaǞzw`3{K@,sE,V2Ɨ2tʤC;&bhcx b eQHhkne n厬C|Co֥@XػEaCyƠ3\pՇ-.4s]8]|P"|aen,IdV(P%Gw4g}!Y_[1u=~? _DRËW;3}:biׯ0@qݳVci ðQ%hsliݓva=ӱ~O/ui_%w<.7z V>E:NK|pQXXJ C# qY:SMmKnT\!}ai=S,-m,{:bðDv;UIޛc/b1LGRan7zetu5KCοXz]$KDXXP=ؓ7,SY1:+߳t9^x2 N,3>=S \fT,}ݳB?b~ct_ܳRUow\ XzKDf^iRhIv67K"43}_l8}P"KyWC{X{2b9K";G\3cMOMV9Kǯs> ̋ 9KJ@<d'(aXD,L\,@,K$D,D,D,K˦r@,K@,K@,K@,K@,K@,K ^>G춳~uK|ZDXV*ԛbI.ԉdۃ eN1MHI%K+yxO-wFD,˜Yԛ_G^hKzl y76^]6poFK_?9"Ӂ{ GR!LX4H7䝥^!N: `zюTH2'V0Rj}{>~x@iˋcIgC#e({1p %(>%yc-l+QQeze2:Ɋekw|RCxv`T6Q,s >@ᴅXf ^adS{:rV[%& ,Js+pBB,ۢ"B)\KzX"X"X"X"Xڰi`WGqg~|>(B%ZeV IJŒ(E&!)?cK ̼]{݆\ Y֑ISXQ9E r,Ղya`[q[*yIkWőLnB,.(iѵ%{qF,e{ΌŲ"[Hu$CɴTN+.OKAFWq4&kXp,C.i lq5"n'JdZgEW i=KEFr/ڰI^Qnh˔^ʬ .'Jz)e݂XX*-UbX"5Ck6:$iah4E&(&'y<0aX *:5XHlhDo&P >0XC:')&8bcgyñ]TZz*ZX򙯙4YB,ˬZt}FAYS()@,K@,K@,K@,K@, u$ sӐBdŸʒXk!&TKʎ|̄EcuТ_-.a/,u乵 M^ `xMǀR4VOD9! b}T׆ѱ~/SKK$!5i.ݚ۵pZtR(JĔXvY#.]jZN$nhyCsuK[)gqz3elj*~LUA,{3ƕeje%bX0UKl4~:y1 aXKj0laUtyi*"A,G[[zѠ$9Dg]UͼtQLIXbXfU,d<@ev-Bȯ!K@,y$߈DE='3NlC<`X""HR^YУE,LL95QݵݗidHGgT>KZ>.iBXWRpc3TX@"%cL6xة&_ĥ!ykQ:^0S;QoEHՂmi. TKYF3@J,]WdLMK@b^!蠟ޫKUOqtR)ցex38@ggiCPV29!TSń'~E&-bt_{Lv`94HE%bf+pWIdE,%L D,n .uzuh8R*0\db9iO]eԇaM[Lw7aKnIv6:K]A؏foZL=*tM͝u >vfjA\K2-pNCu(KӐiM 2'*S"0{d]6A3fSX"L*mϨ)!RֱIlD/Ģ+C=ˊdk- m?vig](B~h͉ES"sIvtKA=}`cRc Y)rio/27ב]G/3dѥKbtRźHq讏L)WHtZ:VsZAid;Ͷ2-ÌxV Tj KwMG,0 _,ݺeH_ES"@Jw.,bg̽f單)SLEbX*q2ZUNߨfES"5I],MN,=,tOevEbKpaX+KJNs"cQȫVI9hQ,㱎3' VIhrKf/@,S .4 R=&5qNQB(75D~&vJ8t?viKY)\\C4폧 n+i`˂` *!KבLg8χ!JSP4%bX"҈s2BeZtIfR55z/sfɣbjm>9b]\6O"){sU22ሎ<ELA”oh_h 3ї" ܛNu9=̨̼XۢaXat))ˇ( 2c;3/@,7uE͸WvчHj ") X L0rڦx(׎:3/@,0']FG uk<|bMG<'3P) ob >WAd'Q3/@,p0"* 9ER"d,bIbXbXbXbXbHedg*yPuݧ? % bY]nʓB .g %b %b %b %b %#?2ISC<e%O,u~dJy97`)@,j;֢ s19.e@vr}\kh\p,]GeZtUᡣ:9\̬"xh!rn0u wŜϙr9XEYE:`"cC#V1վ3\ZYLho%8VB,`.ҳuHG)29{׏ѝe.T rweZtU1leEG&ڑ%x"u+Xr>gRX9\R"Mb]xx<ۢKGfo3')L'@jsE \n<̆XbхN \L.!K@,3;QFҚQ!WyXd2QƓ'%bYb-'Xtu D,;L,;ܢ+~tjO,B--I,+{5C+oomUZyʕoκl7ԦIݢ6hXpp&kѥra7gGd腝{HjuSJa~Lp-)iq>,KS͹\\KPR-\6 Le_ǖlR2Ӄūh<Դ+TghAY,+}sbiJ}sU.ŧSJI :(/o;,PmbW,s%!sp|!ګKe/bXhP[ X5K6\.N,M2 kr 33 [$B-P`Z0}Һacune6Z[G;3F6F5{B >EWb|tL :z+@캸I?:\Tf&{:yȼǖ6m3;sQ%& Bü\hXEW܆!?s4OK g %b %b %b %b XBMeR;KUe0B,hegc]͍ke$tK~^QZMf0SWBQ}t^un(0Gck@O{synvLKt$; V|ZlYo ‘QNK/ݚFE}縨ԵMe&rgXz 2ܲ*eY1+-urNCF>l4ku Lpj_D3Jjd\[8_KG{F$M٩{W \^<|>Q;D2pJY,uZy7Ί/Kwf"i=,܊X"eUwj+ ca$VhqawML-XF2S-T/'/'a㾞v˸X"N ܠT;!g=w%8G6,|38ðd`<{Uv=MўbivR<arXs!C7|)9be.ΌtUM+̤D(z%b %b %b %b e^6 Y5b˓jWV,I7z.i1BkQO(FU֔B6OXo4+JԺ5KH㓐іϓ44ue#eҔ5Es9bbٱb>L%-KF,4.eܞYt!eêC fkVh.)q^V{FSY֔B6OX1Zt1 XvXu&\[{FJk,e嘊E ,JrSe2cnP%4n %,KKKK ڙ3g\esTVVN6M]I>7ib=9w<!3gζmG]mi)3X|KŢ+״C X%}9tCzzDy\(  (Dr*C@' FIвZ'ˍ|x8  N{)szOX'5(K0AA^"<(z BFȈC(e $=B$  (0bD#,ҘqGzϰDAA>"osԓ'!AAPb0!+F"B  (?"  ! @ B  ! |AAy.!AA Dwޙ͛i&gAP"8ᡄ J^Sz-]t5::JiPH:~='U[[Kh1AHa Hm޼9D8o</R B(Z"\x@P̌ •7Ҋ?}Vd;%+,.﾿U]|&pƍRߖ-[mZZ[[wA6@Wɚ:vu9p@o3fyɔF4"aŦ55ʱarl[q[yj_]۸@\;zvTԵU5-r>]q풕 lܸ#g{޷'~aw$LCĂ:}תΩpAٳgۃ Z61oɺFgІ-/ц B([`H@Ut9yw w*/jwoXۮ=ywI"BʙRb&3gáPhxx-?G APEHտx#zuYe=jC`Uziڵk'Ɲ뷬dekjѪoÕ^uul._y{n!'f)ϟv(.ݰ{6nh3{eAٱwd[ ҍM`SѢT-Qk,eDس7~ۉ$!LS)TVlDm۶]v^paddpP3.m4kӖ}+vJ d\5e%ƔQJcR]@C~?(y!5* w{p|f ZPIYo/3|s`'xt|QDXZi{kӎM;ڸcڭ win@Uf!'7D8@%{??S)+Fݽ:::Hvoxj]Hr*J Q7|s4x+8zyI|UР,wj"g0N*c F?be. ab`(Èy!~3e>Y2jk3[QmEe+]EJ>\'gI -_8qSY~ ظe{OOOͿ{LEK"n}^ zu"P/ĉNRUg)Ut>a5|E |3Uɚ#B9ST E%RakkkwwÇ a_'_#Ġg4ld5 hPh^+"cL0ڢ3FiBI![oA Vl\zsx?^||}K^,^;)_>N"l١cTs[o__߮ӆsovNx?ng:8pAέ)(D8<|`ACєzP(qϨ}/\PpУ'ž}iY{VPvڵZqh]珂4AGMyMKoV4HQ3OPYT"KGE;v#m'ju;{9Oyy*'?/y,1ħ7Țh5H NKvnQ&Gh~OUn˴.9 Փ8Fh/V2r+|-(Ĉ?.ں{a2O&s{ 'PMv\*q@ Bͼq!xGHoڂۣ{/-,kjc^YD|jΏ^, qdJ $¦{/.=PA J@!G\'B{ q>pƦN94R԰`kx+c9trKk~I%GʾoW%t~_~9aٗו_+{Y}#y̖b>O6aձ*B*r<)g *J:x'Ʈ>믿_tnjUw?w疬O H 9i&M`O,,&Бc/ Zvׂ XE0v# !GYb?I+ۢajzÂi^+Sq8 !Ub:<>#kp O "4-&rnCl,y?{w?J+|&A~5cGKB L9mlٻw7/ ;ʲoHvsteIk4+=hJ)gu9f1ҳMcFb|(Sz.Z:{=w….^&`'t!+F$u=(:sAxM9bAǫW^_K^RNY3oFyRZZqT.NuN[n:}4y:9mŕG8aM+wP7Ǚdaf,^ՀǙarmL*0d:c u'GȚ,.Q OVX\5? hT_aΌx^@YZY*O &"gt]w"$o^+ng񝇧M~vO!OFMW(s. )#{v{5X PkŸ:^9`aN":xllѣǎ;N|ٳϫ8h h[5{ߤc'I__}wo^{?o2m >x [GJ1_3僅rS|u/_ԲARTDem=¯n^)E \>Xx?#?31ok,DFqڹb B~Т,sJ/3Uy!BΥ8-uɚ\f`⊉0%`gڕ:.!Du@ ^Ld=aWqᏧ{#/㗔ϟΟG~}?K<S>صK~~7E:$!Dd%2vC(Mۿ:uQF]'OHҌ pTˍEђM2B+ςCdjVqu7E:4_`2u u:jbv_`U^'%g JT?_vE"R=J'$8~ۻɮ•"51~a֔!6A1WK;}z<=v7;'R;_'>Oef O^(3cSǩO_͛6?2vחz- Ɍ?77#N"~ .ҡwtûvݽ{׾}O Bxfˌ 4APt'B5A?i'/Xа`~~}^<7W+Y(%彙 @n'=utq@)"7?Z/s w6s5dumSV"~Kw'?_=3K$斶sЫ ^;;;sr\#ή^Dȭ>cyה)&N,X=ge.$Fu\cgW\Dx֧ӏW/%6B@ӫxu"BʙRϻDXQQm6Zz5bGGk' >8fVܲ%K d̾byyHKHl|?g^OzUY-Oh?e?{?8eٳgѱ{G[O`GT;BGHZ`75շxď*=gT>}FwH' ׆"ׯ-YQΔ?2M ,~'~)8Ɵ}YMM͚5kȡu7@8q-A ؈7yꭶv:OSg/+0e*[;Z{r_@PDH_~ꄲMIs~jr#ŠU*6+V[t]Ͳ閕t۰pM `Ӫ|ҊEK.p{~Ut,` ^biYЁ3>u(IDH9S#:P[tM/_rʶ6!ɺēK:{klZv%dǫl޶q꺭jU~Ś5k65nnܼe{ݖ՟lBi&B(b /LۼyٳgB!#G 0UGqQJBi)!]0G JV߰aҥKGGG)-" 2NΌ5o<~ BDAAMzBAP^aRk~7D ]W2Y B be[r,|w66?~Ht"  (lg|7Ĭ={! <{~}Ozc/ȧt"  (ke`VA8%R8"j:thx oHqh &I D$B!҆.{W4J? $$)&g!/.\p :U[8Dx>lm;p$8_;^x :ɏ5h':uʝ'Z紥yާ}>0%8ax?*0Hh#ȧIPx hųq\Hfp]Dx⁁1)sGiڷoQ (UVV~4lmwHAn2E&!!X|v6ф4>Wo죡/'=>s8 ^p .f8Nsq>B"tO%b6-: ]رcc º:jnc(>9v||\Ft;;;=޾V ̳g϶ <|jS'?.z5!ȝ5SѣG92::J .6Bq-k4qp?#?31v{K35FApŊj9z`=.Ɉ~4rO~ЯQ ;o~:AA!"Oi" w"eϞ=]]]v#WLO?6ፌmeiÐ:wnvcs_|Ы|4:F` =;rDOi@຺ 6TvGHoN͚h‡=}wάcz]/gj}s@ßEzI?ӵ?t)tNɓ-x.*"LS "̘a bjcc"Bʂ>3Qi_LƉ!p ؠtX͙VF>Kw61cFqe姞0((B'X*yKA+KʖD28,)yRС#?*OpH|d33e,_ȳuf52}*udbZb BJ0PqCr[t}Eö*6u[PrL2x\N5fL+#BjZ4CHYNzLG 7p߆,"X;ZD<CBeLG"dyR)>s|>'Yy4en&:PxUMUc鬛KABlPڈ0Ze OPkVVsel 2<@t{:{>A[髍܄PBV$͇4/{b^UY9i"1NOhIhnM5&^Z\=2qK\͢&? ٥+++N8AMÇ+SnG>>aDsDD|f OF7cٜY2ˋg!9TY')!ln6Bѓ<ŽZ##qu>jz|+#}fM4ۤn? !(l*0mHhTntDVaNXv0D8n#hzR[0*V{T 9*j|F31x9읚N͡ACkieDXQQm6V^M7 /׆Own;|pqY"??2D]zPqVZ/E\ ~Jهpkl>=.'*>fRsn<|LH_nE0Tr6>3̀2FhA' 9  A)$QC&|'Vq 7?qц%C544h#ܵId.-gZ ܷo(r_p6j͚5}i2w F1.tSrNxldSc]{qOip+?1!e0fmwBs'Bmi1Jv#s","1DΝ;8@n F*c$e1oy; ]ɕQBTA!iliKhݺu͙VFBڣ歮n+Wb΋huƒqP#BgSJO DAd#tnd,D=^c}v Tᠡ{:N-niiiYY͛?!WQaBB dSo?1!FA3F"4J" *ajOZ DAYi#k,Fad%wA D"ēBP#H@3Kt9e?̖ ka4Dxر1Uv(VSCxP B1$3le23oaR [ !4Jg#r߃  (6B(tAi #{lavӦMur־;Z GAA0"Vk"fHq4l r=B B!Ae:FB'F"L3m*9sfYQ6SS!Ljo +"yUNr/;\|bAAyR ,Jw\\%/ 򬘚=k3;BMcu 3ƶbBw "žXpmڌ@B B`нzK!bA:%O T?h )ld; i͎r ^ CK/ET5yBF(9 sL4/w |#BwFx ]%iji%0{Մr:%O  Lñ2,^eiLibKe/EX5KJiDhhBA Da 6B[(:z({AkOlyMKuMWnntJ$69IKމ3F$61^ryFX f(ٽ/mqۈѹ&$O #k0Y=dL+#BYA^sXM;f! (lj0aKKK]]݆ TUVVVWWtĉکKJ}/θ tJ$%+wU%Y6CLΙ%R5W殘>lP! D822r (NVG 8G9<=4̷! (TTTTI ,ϊ=KN8A1Rn8\tFBAP!Aޗ_~*"'O:u̙3gϞ=\By.!ASj#@ B DDA %aO9 J҉yp|~9kJ?ɲA Bl&Bmi}2ng;vcc$@WN B,%E\RKAP!"L+;/6TS8AÔk%a?yPZ-G (06#z 4_E941#Z%B93ɐ1( }px [ql1%zsgz8 "VY455UQ jnyhp3XPsA$!-_70[[6U&)͢Xo뵳g(.ڱ=r9`݄7NEG B{\(%V"yfAAJ*? {ABCĂtJ$i9- 4i֘YĴv,;K^8j#Fb+/H(HRYğ9VmD KW@khߐՕx"tD(@h`3Ei״GW֩21 tM;!!Fxd|]Oߣ fM/DgP d<Ȏk]%d] j*ilO9Rha,4i gκN3lbvg<aT<(`?0k{ C7EQՍ9n]%f]vtD7AbK~Bl0*Mck_{sG+飐Nɓ2݆"L |' B$ꤗ}yr { dhԬ*I/y}o# G"4yUT7[Ň t'EY1_W_z<ȺzW1-|)Ke@@+Gc#t-vJ!wI !BջDA dO[>CP0t/}RhXNɓ\T ojƎ-aq}hhY NYuAe±`In,IFǜ?8MŤK5=Ur̸KdLL5 !Շ[5d?M--u5fϹp>V]NIAWj U[ Oo1nɻ9MBF!& E^ O[, L.~ \lz`LK23JVrVq'B!F BѣG2:::44D^v^y]}ӕD '9F VD݀?hPg5^%!Vr7-!6BDHHED^P[e嗶8mmD}}'^cݘ'$B7Fבט-U "muo.-qJsHh]sdvOk9ϰmcfӎSA!X۶}iGxfZq=M>_Eqt!lPv!!`qYYh:5L?A U?|sYI iNذ2ay 3־G[qa4_V00K %wV2H^)w4(]. fGWA:Mk)Q<055vl8͛)@aqlMim5Arq,1śIm.zT p"\5Z)$@"Ý@[ut+zJAfz7ںo M3™Sz"rjL'6@!B1&fs[- /Hoc]lc=| !A۝zFsܠ)Wo3#}yNٺmb'¬:܉IzKSa~&3!D$&uϒ5~sυ!B:%O %1]Y75Nˊs_䶱]x7WMV=KA$~"d 4{JTtC)!Bti#4*{A- qt j:ȓdUb 3BK&ݿN٥N}p_gQ3a4I2mhA>Y{B[L]&2ɣmUH g,wSx{)6i$#ίFw|TD؛ a3FxZՇ{ B`нzK!bA:%O GU rhWS3DH!} B(AD&Li\_&FRWWaÆ*U%TN]R#|q]}7OSHD(v0-\{G#u;g`0Qܵ33K@P0Scq$;EN6038G9_K eIʂ/u>GTuL>?$/ۊc)|"F2i$X!aZX۵ n/ JT 8X*gXNd{csCbk ^OR>cc}"LGWA:ÓD1xcgoVYr"4te_b*a}D bc9,$1̇JgnjX]%dbffTka*}qAP~c^p F[ cIs>XXX8sֽtJX߸tag 4 \71݊\Ĵ^>[db˜6bτ65m{̈L:E0p[y0 !DO{Ack_{sG+飐NɓГD茐4"$1P@bNJ@v#7,W}?x"mJI-"ӥQ!9={=ڦ9OFނAH6C1cE *{A- qt j:ȓ\߶ξŦ0߯L34Ʀ%~{?ڦkoKpC Bm>]M-(7,f; [:mɿl_{Lz&۔6 !3FxZՇނ {ABCĂtJ$p=cVB4w86Dnolő<ͷ8 ߗ Э9__yFaIv` s6 kO1ީQux[IwGK= ^h"FHƿ짩e~9Wڪ<)HFNq&ӇsD;#șOIaD(0:R"Lp}F۳xģ̨,QL3q#Eŏ#(mLL8\ﱷMj!f@0q6BmѣG2:::44D^v^y]}ӕD 'e4r 37lX:}I ~^p".cP@D ]]]Du,\V~i{ύӦFDH7'yRw8Z"d'nh/fs "LD&̰Q_"BP A 8m---uuu6lRUYYY]]^]Bu%>gwsM:%O r{/, 'VC{fߌ|C3Krn lyjKʔc;EHΗ@qc[0 54.(]1q~r)y(" Sg ]ؐ*rHPZرccR1 b_cD" @yd#@ B DDAAD!0DgPͬEݻo F *ҕ w3v7AF7\qYA:M\]zPc>~_cv|Lw  a6+\| ߴ A:%O)EYfAmtTVqt$6@! Θx@A ¬n0m5߰tJd9HDy: %A":᫋\,9[ە$HXZ4 +$}:o+xu%9hlKbEP#ce+7gV"/]D˴ *gLw  aWGGßɓ܉P"D,uG~!*E3M`# @i#ԠPi{|šM+юɓD贍q$Nc󓡡lH(G(VVESU@" @c#|x+rQn? w!VaN*( 8m{l6H7"a}-RrYB5AF~WC JϲtJ~dQգtڰy>ϥؙĊ,klo9:] B f.a½>ڼТa:AIAafzZ%Q?}^ikg)l=:2 "BA l]0 -[?ezr)yI@A"L%t!C!B B!A"ľDAsFAA0,/,~0;<|ʑ=!'BAi&Bm:}jeTFvW~X0Tm챼 @ DHX\VV,ܵN S7/᷹"(6"44PA"+6#z 4_E94N$^v(I.&%Bg!Ba_A8a@.(1|>)so2OkJږC- ֭kl9 fL7ںo M3™SL"f%m7abа 4rƺgV):iF`IO5GX€ jqƮĥYcg5]zgUCVA.7(3YU3F(7*EB=HeŬ|UU_ݞɣ=f  1Aym#ԠPiji%0{Մr:%O TgC(8!pllh[}>I(jċm}Qլ'B1(!YsGA ־a8 ZK,/i, AAzv^y]}ӕD 'eڹ5Y EmFfΘY[sCUٳ+TCAl P[e嗶8mmD}}'EAV"㺦gъ́7Θa/Hט-Ф B f.vR3n껹~'EC%b"4Lg%A[~JϵN}&yXGYȊ8% B P JWL_䣃tJ3d @)ݳ.dlllH9,c(! b_cD" @9n#@ B DDAyK,؞jD(+7 9"Df"ԖWV$Z =K2BP6%f*GASX !lP!q^qYYp:%LۢĹ Da*Ҋy߯K fۘvB@i#<2@UtN3͌-JKOK/jkBCϒLܕȓߧ]l@t l(/0A A"D@B}p{:A䉶0BA0{Ѕg ":LcHPnţA B D}!! 晍! a&!"BPK_ikov D!"dWV+Iyg "Lȍ`F ,.+bdž"A wjD~bo, A |_h*:AJD.ڴOw;{cG =0qf(v-T:LK>{+orEF!Do6m7&͙`aaY)y&=J'Ar Bu\")!(,.\\El9&کh:ܓܛ-HA |~sυ!B:%O bt D( (!T$vmM5[nn,v+/4'&X'#BY-q A <6<48rǙA,9 O 0߯o}~t%~K'zi*[ڹ?R?}.]+[$y"jI2mJ@h{GL<>F d0#hlkTyD(f6!AF}RhXNɓ$4{e_lrX3ȽҎ5a@P+6w bQ/2g$z1q3[8r]$u/3C =B !fPB٧e~9Wڪ<)Hb#S)#$BT.y:.jV*/z0e&5!]2$&1]U:_?pL gRXy{^[?*@FHZ~bkZ:opsS򤠌#BKd.rBХTa__VΩKܝFb洍У:.+io#"$G<)( "c ;9Ͻ#A5@CX@t յS_q{W5<)(",n̒`Clfm i RI!0 FD B>MrGVB"CPC g.C2"  aJ, !REu0#l"ǣX"9AAC B!A"!"BA L$BD"`tS*3pV)a88{wBD!0DgPLb_7PrgƘ4\$ibEFe!;w}Ja2ӆ_9J^y!gB !D6#z 4_E94_YO$55vmgي%- 8 NY\|[b_SrE~Uc:q6an'=G= w'qa& aou04g3gKDJ*KE9Ѕ}ysG{ a8m\LPpY6u"tUbo%wɈª.+HA L~sυ!B:%O JeDDhu Ҡz'Dl <~%#2OWvEA tZ%8pvubKyJ.TXkAtC0Cm- qt j:ȓ$oAP] s#}ص4|ɓ Baǫ :DmP20 9b17:#+TC0mt յS_q{W5<)(",a, F|24ĥD9TVdWD;fJ!DTPqA銉|tN DAA ”YBB1b`F }O~ B7" 8s, /r!A/6<*8pƀhyn#@ B B! B!!D6BD"LѣbyNSC"|s~"T {(7M3SX@8pF!0D>P[{c<*JB:MyF9WaD(ڦ{X79eE@8pFFzee]0evODۡ]-0}<8DI1C0(|74"ā6Ĥ=2@UtNdz_&0aډP;~_c~o;eH"vJ )eQu3(4rVPi-s' /b,\Hn34XmJ*V=3"/{o2kKVMA8pFou04g3gKDKNG"#P2.fadK~A~=.}"ddfLZ <8s#AAJ[A%uRkSf#4"\2o#bFq?=XMA8pFsѱ5ۯ=yn#QHIA63-˟9/mL7F(2<3H'.YDhb`qkЌдzeڅ)-(Dm: 6(q0f5<48rǙA,9 O _-,ڏxeœb _ΞqiӯQ[^RMcd@Dܗ "tBrZzɴAǂK΂%B%cBsC^D(Hq &Bٿ*)Zq06zK!bA:%O @B{p(]UұN#5vM%W ciu`@M+0 l/ B7cl< ir"4zlxFܑ>9WXO.YKqL.勋41AaP(4]O=jA\[u9'y 0 8!r5s'bMáоGD(0ZE3f M`[}۴ 7)D%k&js!BIA֕]%Z"Y"2Eْ9=#~jJ3Kp06µ'6+77\A:%O "rm1!k*`L.Q5<ɓ Ba߫ q]Sݰ΀4/8œ #3>jD=K`嚩 ՙ:\L|7+Ta%TN]R#|q]}7OShP-gd8/x!3zq3 J{ fPB٧e~9Wڪ<)Hb#S l|Dʥt iIP F( )/ڍB>AFvĦ״t^Wt%+AIAB J֙-Bg,'BP Aba[e嗶8mmD}}'EAV"qG56s3fF5f" |@x@A ¬%TN]R#|q]}7OShPnl,q#.^'*{U do{gyNf|rϱTX㰈qLAVL6mKY$LK_R%mwL=@0/mck_{sG+飐Nɓ`@sF?'xBJF~_XUDl51XG)%B5,LSm2zGf2Ӆ$fhP\y):bk\8<,4F-+Ŧ6o^%U] UMV$`̅ a[Ň t'I`_h?þolٿJJͮ^{}_U."4퀌 df`C@$.41A.Dt`#[kyN^"d/j"=/Dޓ!!l+.wkO-<)HZpLX$em!F #mhB6ۈ-&l`v ?Ѝ <h<C1&<m;Ilzxflta#HlsAh#HXJߣXDkLjqeؕXW`\J.z!an5(}ZZvIk'̞s5 }Nɓr+`;7 #$BTmrYYLޕ2CfM/$?/1瑘3K J{3e=]жtWTKj_yP2O /–}j=pʅJIxJn- W'@_Kw!˟J|i(L/ߑl E3K !l֮شꛮ$"p9<)(S}zq]zyi]Ye2k_*;ۂ.cD6BjXMoO(PBNA*Y$h-!#BaS" ¤DD!]Bu%>gwsM:%O 3KDv⹤.~k13vcM!N@ l%”) 54.(]1q~r)y2]) B t!U[e>zQG˲AA"] B FAA0#}jC%zM3*8A>؜楟`Y@B BmIjk:v9j.&G `U^sEiE D!DHW\VVnOk ]au4(n"o?Ij7/ D(%Ǐ_xq…[թbšYa#<2@UtNTrkFD8"Ԙδ3D%n@4=пD{~r/4&g~[n4IwA B"\x@ )|@B}p{aI1ETK E4|H^ eJ$" DQX"L}RhXNɓ'̉NLX}7 BG@[VF )@^ "BD?Ɯ2Bυ_4&!A D1<1VGꤔ%cRh֍#\~bkZ:opsS򤠨'&MN BYGoh2D1A D8227(N#lXMoO򤠨Mao`Fkt';zԮ A B0a/X6@@.DH-tQQQ/_^\\jTN]R#|q]}7OShZJQ#y/5A<] Lvt^KOkzJHAKgI(jh\Pb"S򌱁A#"  a6kL2666AE`\;"  !5@A"!"BA L$BD"S,מkO]}\Ay@Lֆ$~%Y?},}W%IG\$[%<>8."LWW9A@0DHW\VV̲_V1t/Oq2p|7V'Tw>?1K9AP|.vŐX]rZbXʜ mK٢z;lޙEq{p3I&wɽz8%5qܐn ( *(" (YUn߷SUNUs{a^)-HqwQ<4J'O^JFȫ%{%o}w|'Yt<|p#*2Sa(cy#/";LG"%*x^g^2=1@Ѹ8L(er;1=SyKvC (*Y8_%avte*I].q>O{,)DfRi=-MyE>N}&"%*F1-Y`=òa(>UbY.%)r1+4mO*&{Qe UR:L8SEWݽsP0;/+Rh81B3T7%e'!(V)\:b ̰e-ItɡZrAcf7GgO49NGJ9g 6h'\8vg! IBcK!̹+C`дi+?Rjl\aTQǣEͼ]nJŽM+?+~T4f>M***(]K_J'TGJ"/d.PZ F؃PJ-O4CP"`uo$8]3D1r :\;v&< HH,4ke-c`0B!D7TFhG… iii'Ofhi-FY7BckR!F#θ~YS>׎/abZބKgM4CFFFcЁ455i ?:`0B0Bk `0B0B!'!{*lKWxy5vN{/]!;10BAo4BQKưctK!`.\'V!{t'f7B*`eQIqQ[gc]"9]6+aW pe#%C -77BQLq@ɨ-{—V9&Q4 c ˔ ؾ?3\6-GUҹWSP:1c22~ٸm hZDsTz wa'1(>S#X:,0B>rEr?3BiEJU;òa<1J(Ej\w*Fi=pr|W[NoI>_f@:t.e"^Ŧd\w*Fxs{S׭)pO֭_JUiPapxwgAiP/4\w]#F(H_5l Zͬ+?gqM'Iv[noryÆˁ5קXV]j0BB25kzD3H7By:ber:3Mhbt0> 97h!F!psF0vk#F:Un #i>bbFh_4=Z7Cgyp~ө߮|NJϽrw}Ӑ@]Y,vl")@2B?aj6"I!FaE`ͣ3Zc1,p]WhZ#dGy0 7O= N˧kjjL (!FaE6B򼨸8%̎k1D|@f1::lSao賳PgĀIA1¢;̠fh_<}hPp>v;SĐ@vD8eлhʲh$JS3*trokNd+얆,e㍛s R19sk*ڝ'kBśeA*E:t1ݞ FҒzQ\Îlh)i==&r0EO41;qG"-G_2& n|lЗ>g$DMР_Z _0ht|aN;Zy[J|uS Ҙyx s^Ij`d!g 2\>Fx闼5&ZDZՙ"2B}X#4,44^MF(ze<ϐY0~7 QA*Y0QLJWe!=|0*hGMľ^^^MW* UJ&#(6y0#׉7$-%оhFh25}а`j&=F}#T)k V2B")!ׇx_cGX/TJ[%RZ9!F؋c.xt?>mDZe+e/L7Bv,c涐kTIs8'tFhS4)oabӃy~b (|keܔ,#,Iؑ2ւ*iB\&}ldΆg: #RĔ jq )V)ٻUK#+oP<4?\Ȥ ]pjL]FKY 7B7}O%ʻȞT+4J+*B9l2o$JB%:@bDeOkA۸v XsvW,NwEK*F(7%7qP ctEŗn2(R=h2#DDDD5ܹ&vv⵫]rv%*+Fȹ0zq(b35B\5F5jE 4 -R"f$I;X%${:@5Gj/zwiZD' SAFa@0B:/ #pw`0B#0B#0B#t&xx%iIMM<}4`0B\۷WUUْ6i{ƞN29daaa!CٳF 7=~| n6޺ٳ'Nɱj_t~Ԫ?ɲY~lI_!%oll}vKKKkk+ kA[ڔsdap啛 ,&otpiũslB{\)A\sBæa2vviS2gygΜqFbaa[oA0BU~8!fVK;wjo555߾}Ç<=zf{SOg]>TE֪ʼn`~zٮϞi@5)ܿӹ+/x<Ƿq.(= ճ|[cfΜ96弥0, R >ggΜ&H<ؤ%6Kq6eW"yq2ؔ<9ngcTҨA4*5;evAAFHY|G4hƏa`ٲe0B[U>FaÆ*łr.h}ӦM^ sPLyeԙE3vmrT1_HCJ(/ε#[Aq,̓砷 N0cfvi3N5BO[ a zҗ.!P͆[n577߽{:jVKm.^Mi_y~.tFMy.ș0;ؒA病_2R>s&;a;ưa DRH!߿'N+tx׳`W"F{MMkN7feg75#U?b+lYC~s̱m~8u:z[à}?c_'N=9J?~ھ4nْXvͬTBr#Ɠ̬e0?S ``h,eŊuu ܹն'z)9YIwm:~WMM!Z 1>g]bUҐ !5+|srٸi+t`ТESN4RJjIFX^^>lذW_;JFlf#l#ܶm۵?_yûwӧOj/^,x~GGG''',?؞=XqFzԜ3e'ƭyo\ KF}w1YY/^v-C>[~cA3v8ٙx66- J6tir37X8]TjG)ze:+;.zZ> LFr I֚OcZ{"!}U ; RhN^!>3qœR>\1bmIY~^r}‘9qi<=ifn\6ȴ+)!z>}k/,,F aJ 5Fu붦;w˯]RRWUcնmU7r.UmRn*/8W00mR6i`}IuLvGJy6jk.lI_,ӧO͠ۉb63)eG4vl̆%$Vcs^sje.$/؛PŽJȏp2+Ɏtj3bB)̧ټ9G4jGIӺ:;W}ք<{wּ3o⾉SwӼ%oF%Z!#,..[~s+) MfJ O8L{7<_9BJu8tva;BbhؔRVk5655)aB5W\9]B os@Brl 0}ӱ1:Jj N#lhhZ0mmm>|#=K5Wg|&`ϣQi;փsv&{.MO2I%}*mȡxM=tX'XƸq#}ٯ~~;>>^=o޼׍Qo$|<޻w2? Sŋu 7׮]k뵯vO~?>WPI#a<23ɖ2Yg*+G˖ŲXPQfGŘi"[!ISl4l Y}Q&yD?'oҳD^m&pp=йŠ<|]f aBj3z.N,77הWޤgC mWG'NM@:7cʑOlO 4iة#7qS„^w?4|FH;97$)<|pfffT:NF`!0BG0lFڸExVIҫ5uյuuM֭w9go~}F=?f MB'ْ.94eKbo,(TaSS;p{ya'Fgtp}VǾac˧~6-v£9aQMNMwl%,=y~y t$^H|u٫486#›7oKwҥׯ W7n^Z0B`&\jmzZߏF|r d,2h pyٿ2SAfzm5B.`QM5w`it쮑BCK+KGokϔ1ζ)) L__g$~?xό_,=:C#TC} +Wtjޤݾs"c7ͮz&?)GRȑ͵5q{Ş&4аA2`PV~[m-))Q `IjT@a8/[52:#X5"""VO: .#0B#W4Ĵ IENDB`spyder-2.3.8/doc/index.rst0000664000000000000000000000253112566665770014165 0ustar rootrootSpyder - Documentation ====================== Spyder is the Scientific PYthon Development EnviRonment: * a powerful interactive development environment for the Python language with advanced editing, interactive testing, debugging and introspection features * and a numerical computing environment thanks to the support of `IPython` (enhanced interactive Python interpreter) and popular Python libraries such as `NumPy` (linear algebra), `SciPy` (signal and image processing) or `matplotlib` (interactive 2D/3D plotting). Spyder may also be used as a library providing powerful console-related widgets for your PyQt-based applications -- for example, it may be used to integrate a debugging console directly in the layout of your graphical user interface. Spyder websites: * Downloads, bug reports and feature requests: https://github.com/spyder-ide/spyder * Discussions: http://groups.google.com/group/spyderlib Contents: .. toctree:: :maxdepth: 2 :glob: overview installation options lightmode console ipythonconsole debugging variableexplorer inspector onlinehelp historylog explorer projectexplorer editor findinfiles pylint internalconsole Indices and tables: * :ref:`genindex` * :ref:`search` spyder-2.3.8/doc/historylog.rst0000664000000000000000000000031712566665770015261 0ustar rootrootHistory log =========== The history log plugin collects command histories of Python/IPython interpreters or command windows. .. image:: images/historylog.png Related plugins: * :doc:`console` spyder-2.3.8/PKG-INFO0000664000000000000000000000252412626056072012640 0ustar rootrootMetadata-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) spyder-2.3.8/MANIFEST.in0000664000000000000000000000066212626055322013277 0ustar rootrootrecursive-include spyderlib *.pot *.po *.svg *.png *.css *.qss recursive-include spyderlibplugins *.pot *.po *.svg *.png recursive-include doc *.py *.rst *.png *.ico *. recursive-include app_example *.py *.pyw *.bat *.qm *.svg *.png include scripts/* include img_src/*.ico include img_src/spyder.png include img_src/spyder3.png include MANIFEST.in include README.md include LICENSE include CHANGELOG include bootstrap.py spyder-2.3.8/spyder.egg-info/0000755000000000000000000000000012626531443014536 5ustar rootrootspyder-2.3.8/spyder.egg-info/SOURCES.txt0000664000000000000000000007777112626056072016447 0ustar rootrootLICENSE 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.pyspyder-2.3.8/spyder.egg-info/PKG-INFO0000664000000000000000000000252412626056066015643 0ustar rootrootMetadata-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) spyder-2.3.8/spyder.egg-info/dependency_links.txt0000664000000000000000000000000112626056066020611 0ustar rootroot spyder-2.3.8/spyder.egg-info/top_level.txt0000664000000000000000000000003012626056066017266 0ustar rootrootspyderlib spyderplugins spyder-2.3.8/scripts/0000755000000000000000000000000012626531443013225 5ustar rootrootspyder-2.3.8/scripts/spyder.bat0000664000000000000000000000003512566665770015241 0ustar rootroot@echo off python "%~dpn0" %*spyder-2.3.8/scripts/spyder.desktop0000664000000000000000000000041212566665770016143 0ustar rootroot[Desktop Entry] Version=1.0 Type=Application Name=Spyder GenericName=Spyder Comment=Scientific PYthon Development EnviRonment TryExec=spyder Exec=spyder %F Categories=Development;Science;IDE;Qt; Icon=spyder Terminal=false StartupNotify=true MimeType=text/x-python; spyder-2.3.8/scripts/spyder_win_post_install.py0000664000000000000000000002473412626055322020566 0ustar rootroot# postinstall script for Spyder """Create Spyder start menu and desktop entries""" from __future__ import print_function import os import sys import os.path as osp import struct try: # Python 2 import _winreg as winreg except ImportError: # Python 3 import winreg # analysis:ignore EWS = "Edit with Spyder" KEY_C = r"Software\Classes\%s" KEY_C0 = KEY_C % r"Python.%sFile\shell\%s" KEY_C1 = KEY_C0 + r"\command" # ability to run spyder-win-post-install outside of bdist_wininst installer # copied from pywin32-win-post-install.py # http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/file/default/pywin32_postinstall.py ver_string = "%d.%d" % (sys.version_info[0], sys.version_info[1]) root_key_name = "Software\\Python\\PythonCore\\" + ver_string try: # When this script is run from inside the bdist_wininst installer, # file_created() and directory_created() are additional builtin # functions which write lines to Python23\pywin32-install.log. This is # a list of actions for the uninstaller, the format is inspired by what # the Wise installer also creates. # https://docs.python.org/2/distutils/builtdist.html#the-postinstallation-script file_created # analysis:ignore is_bdist_wininst = True except NameError: is_bdist_wininst = False # we know what it is not - but not what it is :) # file_created() and directory_created() functions do nothing if post # install script isn't run from bdist_wininst installer, instead if # shortcuts and start menu directory exist, they are removed when the # post install script is called with the -remote option def file_created(file): pass def directory_created(directory): pass def get_root_hkey(): try: winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, root_key_name, 0, winreg.KEY_CREATE_SUB_KEY) return winreg.HKEY_LOCAL_MACHINE except OSError: # Either not exist, or no permissions to create subkey means # must be HKCU return winreg.HKEY_CURRENT_USER try: create_shortcut # analysis:ignore except NameError: # Create a function with the same signature as create_shortcut # provided by bdist_wininst def create_shortcut(path, description, filename, arguments="", workdir="", iconpath="", iconindex=0): try: import pythoncom except ImportError: print("pywin32 is required to run this script manually", file=sys.stderr) sys.exit(1) from win32com.shell import shell, shellcon # analysis:ignore ilink = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink) ilink.SetPath(path) ilink.SetDescription(description) if arguments: ilink.SetArguments(arguments) if workdir: ilink.SetWorkingDirectory(workdir) if iconpath or iconindex: ilink.SetIconLocation(iconpath, iconindex) # now save it. ipf = ilink.QueryInterface(pythoncom.IID_IPersistFile) ipf.Save(filename, 0) # Support the same list of "path names" as bdist_wininst. def get_special_folder_path(path_name): try: import pythoncom except ImportError: print("pywin32 is required to run this script manually", 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', 'CSIDL_DESKTOPDIRECTORY', 'CSIDL_COMMON_STARTUP', 'CSIDL_STARTUP', 'CSIDL_COMMON_PROGRAMS', 'CSIDL_PROGRAMS', 'CSIDL_PROGRAM_FILES_COMMON', 'CSIDL_PROGRAM_FILES', 'CSIDL_FONTS'] for maybe in path_names: if maybe == path_name: csidl = getattr(shellcon, maybe) return shell.SHGetSpecialFolderPath(0, csidl, False) raise ValueError("%s is an unknown path ID" % (path_name,)) def install(): """Function executed when running the script with the -install switch""" # Create Spyder start menu folder # Don't use CSIDL_COMMON_PROGRAMS because it requres admin rights # This is consistent with use of CSIDL_DESKTOPDIRECTORY below # CSIDL_COMMON_PROGRAMS = # C:\ProgramData\Microsoft\Windows\Start Menu\Programs # CSIDL_PROGRAMS = # C:\Users\\AppData\Roaming\Microsoft\Windows\Start Menu\Programs start_menu = osp.join(get_special_folder_path('CSIDL_PROGRAMS'), 'Spyder (Py%i.%i %i bit)' % (sys.version_info[0], sys.version_info[1], struct.calcsize('P')*8)) if not osp.isdir(start_menu): os.mkdir(start_menu) directory_created(start_menu) # Create Spyder start menu entries 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')) workdir = "%HOMEDRIVE%%HOMEPATH%" import distutils.sysconfig lib_dir = distutils.sysconfig.get_python_lib(plat_specific=1) ico_dir = osp.join(lib_dir, 'spyderlib', '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__)) desc = 'Scientific Python Development EnvironmEnt, an alternative to IDLE' fname = osp.join(start_menu, 'Spyder (full).lnk') create_shortcut(python, desc, fname, '"%s"' % script, workdir, 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) file_created(fname) current = True # only affects current user root = winreg.HKEY_CURRENT_USER if current else winreg.HKEY_LOCAL_MACHINE winreg.SetValueEx(winreg.CreateKey(root, KEY_C1 % ("", EWS)), "", 0, winreg.REG_SZ, '"%s" "%s\Scripts\spyder" "%%1"' % (pythonw, sys.prefix)) winreg.SetValueEx(winreg.CreateKey(root, KEY_C1 % ("NoCon", EWS)), "", 0, winreg.REG_SZ, '"%s" "%s\Scripts\spyder" "%%1"' % (pythonw, sys.prefix)) # Create desktop shortcut file desktop_folder = get_special_folder_path("CSIDL_DESKTOPDIRECTORY") fname = osp.join(desktop_folder, 'Spyder.lnk') desc = 'Scientific Python Development EnvironmEnt, an alternative to IDLE' create_shortcut(pythonw, desc, fname, '"%s"' % script, workdir, osp.join(ico_dir, 'spyder.ico')) file_created(fname) def remove(): """Function executed when running the script with the -remove switch""" current = True # only affects current user root = winreg.HKEY_CURRENT_USER if current else winreg.HKEY_LOCAL_MACHINE for key in (KEY_C1 % ("", EWS), KEY_C1 % ("NoCon", EWS), KEY_C0 % ("", EWS), KEY_C0 % ("NoCon", EWS)): try: winreg.DeleteKey(root, key) except WindowsError: pass else: if not is_bdist_wininst: print("Successfully removed Spyder shortcuts from Windows "\ "Explorer context menu.", file=sys.stdout) if not is_bdist_wininst: # clean up desktop desktop_folder = get_special_folder_path("CSIDL_DESKTOPDIRECTORY") fname = osp.join(desktop_folder, 'Spyder.lnk') if osp.isfile(fname): try: os.remove(fname) except OSError: print("Failed to remove %s; you may be able to remove it "\ "manually." % fname, file=sys.stderr) else: print("Successfully removed Spyder shortcuts from your desktop.", file=sys.stdout) # clean up startmenu start_menu = osp.join(get_special_folder_path('CSIDL_PROGRAMS'), 'Spyder (Py%i.%i %i bit)' % (sys.version_info[0], sys.version_info[1], struct.calcsize('P')*8)) if osp.isdir(start_menu): for fname in os.listdir(start_menu): try: os.remove(osp.join(start_menu,fname)) except OSError: print("Failed to remove %s; you may be able to remove it "\ "manually." % fname, file=sys.stderr) else: print("Successfully removed Spyder shortcuts from your "\ " start menu.", file=sys.stdout) try: os.rmdir(start_menu) except OSError: print("Failed to remove %s; you may be able to remove it "\ "manually." % fname, file=sys.stderr) else: print("Successfully removed Spyder shortcut folder from your "\ " start menu.", file=sys.stdout) if __name__=='__main__': if len(sys.argv) > 1: if sys.argv[1] == '-install': try: install() except OSError: print("Failed to create Start Menu items.", file=sys.stderr) elif sys.argv[1] == '-remove': remove() else: print("Unknown command line option %s" % sys.argv[1], file=sys.stderr) else: print("You need to pass either -install or -remove as options to "\ "this script", file=sys.stderr) spyder-2.3.8/scripts/spyder30000775000000000000000000000010412566665770014557 0ustar rootroot#! /usr/bin/python3 from spyderlib import start_app start_app.main()spyder-2.3.8/scripts/spyder3.desktop0000664000000000000000000000043112566665770016227 0ustar rootroot[Desktop Entry] Version=1.0 Type=Application Name=Spyder3 GenericName=Spyder3 Comment=Scientific PYthon Development EnviRonment - Python3 TryExec=spyder3 Exec=spyder3 %F Categories=Development;Science;IDE;Qt; Icon=spyder3 Terminal=false StartupNotify=true MimeType=text/x-python; spyder-2.3.8/scripts/spyder0000775000000000000000000000010612566665770014476 0ustar rootroot#!/usr/bin/env python from spyderlib import start_app start_app.main()