def save_session(filename): """Save Spyder session""" local_fname = get_conf_path(osp.basename(filename)) filename = osp.abspath(filename) old_cwd = os.getcwdu() 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, error: error_message = unicode(error)
def save_session(filename): """Save Spyder session""" local_fname = get_conf_path(osp.basename(filename)) filename = osp.abspath(filename) old_cwd = os.getcwdu() 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, error: error_message = unicode(error)
def __init__(self, parent=None, namespace=None, commands=[], message="", max_line_count=300, font=None, exitfunc=None, profile=False, multithreaded=True, light_background=True): PythonShellWidget.__init__(self, parent, get_conf_path('.history_internal.py'), profile) self.set_light_background(light_background) self.multithreaded = multithreaded self.setMaximumBlockCount(max_line_count) if font is not None: self.set_font(font) # Allow raw_input support: self.input_loop = None self.input_mode = False # KeyboardInterrupt support self.interrupted = False # used only for not-multithreaded mode self.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') show_single = getcfg('codecompletion/show_single') self.set_codecompletion_case(case_sensitive) self.set_codecompletion_single(show_single) # 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(__builtin__, 'open_in_spyder'): self.connect(self, SIGNAL("go_to_error(QString)"), self.open_with_external_spyder)
def reset_session(): """Remove all config files""" print >>STDERR, "*** Reset Spyder settings to defaults ***" 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 >>STDERR, "removing:", cfg_fname
def reset_session(): """Remove all config files""" print >> STDERR, "*** Reset Spyder settings to defaults ***" 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 >> STDERR, "removing:", cfg_fname
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 isinstance(history_filename, (str, unicode)) 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() # Calltips calltip_size = CONF.get('shell_appearance', 'calltips/size') calltip_font = get_font('shell_appearance', 'calltips') self.setup_calltips(calltip_size, calltip_font) # Completion completion_size = CONF.get('shell_appearance', 'completion/size') completion_font = get_font('shell_appearance', 'completion') self.completion_widget.setup_appearance(completion_size, completion_font) # Cursor width self.setCursorWidth(CONF.get('shell_appearance', 'cursor/width'))
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 isinstance(history_filename, (str, unicode)) 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() # Calltips calltip_size = CONF.get('shell_appearance', 'calltips/size') calltip_font = get_font('shell_appearance', 'calltips') self.setup_calltips(calltip_size, calltip_font) # Completion completion_size = CONF.get('shell_appearance', 'completion/size') completion_font = get_font('shell_appearance', 'completion') self.completion_widget.setup_appearance(completion_size, completion_font) # Cursor width self.setCursorWidth( CONF.get('shell_appearance', 'cursor/width') )
def __init__(self, parent=None, namespace=None, commands=[], message="", max_line_count=300, font=None, exitfunc=None, profile=False, multithreaded=True, light_background=True): PythonShellWidget.__init__(self, parent, get_conf_path('.history_internal.py'), profile) self.set_light_background(light_background) self.multithreaded = multithreaded self.setMaximumBlockCount(max_line_count) if font is not None: self.set_font(font) # Allow raw_input support: self.input_loop = None self.input_mode = False # KeyboardInterrupt support self.interrupted = False # used only for not-multithreaded mode self.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') show_single = getcfg('codecompletion/show_single') self.set_codecompletion_case(case_sensitive) self.set_codecompletion_single(show_single) # 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(__builtin__, 'open_in_spyder'): self.connect(self, SIGNAL("go_to_error(QString)"), self.open_with_external_spyder)
def load_session(filename): """Load Spyder session""" filename = osp.abspath(filename) old_cwd = os.getcwdu() 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, error: error_message = unicode(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)
def load_session(filename): """Load Spyder session""" filename = osp.abspath(filename) old_cwd = os.getcwdu() 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, error: error_message = unicode(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)
import struct import cPickle as pickle # Local imports from SMlib.utils.misc import fix_reference_name from SMlib.utils.debug import log_last_error from SMlib.utils.dochelpers import (getargtxt, getdoc, getsource, getobjdir, isdefined) from SMlib.utils.bsdsocket import (communicate, read_packet, write_packet, PACKET_NOT_RECEIVED) from SMlib.utils.module_completion import module_completion from SMlib.configs.baseconfig import get_conf_path, get_supported_types, DEBUG SUPPORTED_TYPES = get_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', 'collvalue', 'inplace', 'remote_editing', 'autorefresh') def get_remote_data(data, settings, mode, more_excluded_names=None): """
import struct import cPickle as pickle # Local imports from SMlib.utils.misc import fix_reference_name from SMlib.utils.debug import log_last_error from SMlib.utils.dochelpers import (getargtxt, getdoc, getsource, getobjdir, isdefined) from SMlib.utils.bsdsocket import (communicate, read_packet, write_packet, PACKET_NOT_RECEIVED) from SMlib.utils.module_completion import module_completion from SMlib.configs.baseconfig import get_conf_path, get_supported_types, DEBUG SUPPORTED_TYPES = get_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', 'collvalue', 'inplace', 'remote_editing', 'autorefresh') def get_remote_data(data, settings, mode, more_excluded_names=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()
except Exception, error: error_message = unicode(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 SMlib.configs.baseconfig import _ class IOFunctions(object): def __init__(self): self.load_extensions = None self.save_extensions = None self.load_filters = None self.save_filters = 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()
from PyQt4.QtCore import QThread, SIGNAL, pyqtSignal import threading import socket import errno import os # Local imports from SMlib.configs.baseconfig import get_conf_path, DEBUG from SMlib.utils.misc import select_port from SMlib.utils.debug import log_last_error from SMlib.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 = {}
"""External shell's introspection and notification servers""" from PyQt4.QtCore import QThread, SIGNAL, pyqtSignal import threading import socket import errno import os # Local imports from SMlib.configs.baseconfig import get_conf_path, DEBUG from SMlib.utils.misc import select_port from SMlib.utils.debug import log_last_error from SMlib.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)
except Exception, error: error_message = unicode(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 SMlib.configs.baseconfig import _ class IOFunctions(object): def __init__(self): self.load_extensions = None self.save_extensions = None self.load_filters = None
class WorkingDirectory(QToolBar, SMPluginMixin): """ Working directory changer widget """ CONF_SECTION = 'workingdir' CONFIGWIDGET_CLASS = WorkingDirectoryConfigPage LOG_PATH = get_conf_path('.workingdir') sig_option_changed = pyqtSignal(str, object) def __init__(self, parent, workdir=None): QToolBar.__init__(self, parent) SMPluginMixin.__init__(self, parent) # Initialize plugin self.initialize_plugin() # Setting default values for editor-related options self.get_option('editor/open/browse_scriptdir', True) self.get_option('editor/open/browse_workdir', False) self.get_option('editor/new/browse_scriptdir', False) self.get_option('editor/new/browse_workdir', True) self.get_option('editor/open/auto_set_to_basedir', False) self.get_option('editor/save/auto_set_to_basedir', False) 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', False) self.pathedit = PathComboBox(self, adjust_to_contents=adjust) self.pathedit.setToolTip( _("This is the working directory for newly\n" "opened consoles (Python interpreters 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', 20)) wdhistory = self.load_wdhistory(workdir) if workdir is None: if self.get_option('startup/use_last_directory', True): 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 = os.getcwdu() 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 = os.getcwdu() wdhistory = [workdir] return wdhistory def save_wdhistory(self): """Save history to a text file in user home directory""" text = [ unicode( 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"), os.getcwdu()) 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(os.getcwdu(), 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(unicode(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(unicode(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)"), os.getcwdu())
import os.path import pkgutil import re from time import time import sys from zipimport import zipimporter from SMlib.configs.baseconfig import get_conf_path from SMlib.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 sys.platform == 'darwin' and 'Spyder.app' in __file__: 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<name>[a-zA-Z_][a-zA-Z0-9_]*?)' r'(?P<package>[/\\]__init__)?' r'(?P<suffix>%s)$' %
class ObjectInspector(SMPluginWidget): """ Docstrings viewer widget """ CONF_SECTION = 'inspector' CONFIGWIDGET_CLASS = ObjectInspectorConfigPage LOG_PATH = get_conf_path('.inspector') def __init__(self, parent): SMPluginWidget.__init__(self, parent) self.internal_shell = None # Initialize plugin self.initialize_plugin() self.no_doc_string = _("No documentation available") self._last_console_cb = None self._last_editor_cb = None self.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_rope_data = 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'): 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.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(text={}, html_text_no_doc=warning(self.no_doc_string), math_option=self.get_option('math')) 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) self._starting_up = True #------ SMPluginWidget 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 QTimer.singleShot(5000, self.refresh_plugin) self.set_object_text(None, force_refresh=False) 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() 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) #------ 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 func.im_self 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]) #------ 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_rope_data is not None: text = self._last_rope_data self.set_rope_doc(text, 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 = unicode(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) 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_rope_doc(self, text, force_refresh=False): """ Use the object inspector to show text computed with rope from the Editor plugin """ if (self.locked and not force_refresh): return self.switch_to_editor_source() self._last_rope_data = text self.object_edit.setText(text['obj_text']) if self.rich_help: self.set_sphinx_text(text) else: self.set_plain_text(text, is_code=False) if self.dockwidget is not None: self.dockwidget.blockSignals(True) self.__eventually_raise_inspector(text['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 file(self.LOG_PATH, 'r').readlines()] else: history = [] return history def save_history(self): """Save history to a text file in user home directory""" file(self.LOG_PATH, 'w').write("\n".join( \ [ unicode( 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.force_refresh() 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 set_sphinx_text(self, text): """Sphinxify text and display it""" # If the thread is already running wait for it to finish before # starting it again. if self._sphinx_thread.wait(): # Math rendering option could have changed self._sphinx_thread.math_option = self.get_option('math') self._sphinx_thread.text = text self._sphinx_thread.start() def _on_sphinx_thread_html_ready(self, html_text): """Set our sphinx documentation based on thread result""" 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.plain_text_action.setChecked(True) QMessageBox.critical(self, _('Object inspector'), _("The following error occured when calling " "<b>Sphinx %s</b>. <br>Incompatible Sphinx " "version or doc string decoding failed." "<br><br>Error message:<br>%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 = unicode(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_text = None source_text = None if shell is not None: doc_text = shell.get_doc(obj_text) if isinstance(doc_text, bool): doc_text = None source_text = shell.get_source(obj_text) is_code = False if self.rich_help: self.set_sphinx_text(doc_text) if ignore_unknown: return doc_text is not None else: return True elif self.docstring: hlp_text = doc_text 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_text 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