"""MagicHelper - dockable widget showing magic commands for the MainWindow """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- # stdlib imports import json import re import sys # System library imports from IPython.external.qt import QtGui,QtCore from IPython.core.magic import magic_escapes class MagicHelper(QtGui.QDockWidget): """MagicHelper - dockable widget for convenient search and running of magic command for IPython QtConsole. """ #--------------------------------------------------------------------------- # signals #--------------------------------------------------------------------------- pasteRequested = QtCore.Signal(str, name = 'pasteRequested') """This signal is emitted when user wants to paste selected magic command into the command line. """ runRequested = QtCore.Signal(str, name = 'runRequested') """This signal is emitted when user wants to execute selected magic command """ readyForUpdate = QtCore.Signal(name = 'readyForUpdate') """This signal is emitted when MagicHelper is ready to be populated. Since kernel querying mechanisms are out of scope of this class, it expects its owner to invoke MagicHelper.populate_magic_helper() as a reaction on this event. """ #--------------------------------------------------------------------------- # constructor #--------------------------------------------------------------------------- def __init__(self, name, parent): super(MagicHelper, self).__init__(name, parent) self.data = None class MinListWidget(QtGui.QListWidget): """Temp class to overide the default QListWidget size hint in order to make MagicHelper narrow """ def sizeHint(self): s = QtCore.QSize() s.setHeight(super(MinListWidget,self).sizeHint().height()) s.setWidth(self.sizeHintForColumn(0)) return s # construct content self.frame = QtGui.QFrame() self.search_label = QtGui.QLabel("Search:") self.search_line = QtGui.QLineEdit() self.search_class = QtGui.QComboBox() self.search_list = MinListWidget() self.paste_button = QtGui.QPushButton("Paste") self.run_button = QtGui.QPushButton("Run") # layout all the widgets main_layout = QtGui.QVBoxLayout() search_layout = QtGui.QHBoxLayout() search_layout.addWidget(self.search_label) search_layout.addWidget(self.search_line, 10) main_layout.addLayout(search_layout) main_layout.addWidget(self.search_class) main_layout.addWidget(self.search_list, 10) action_layout = QtGui.QHBoxLayout() action_layout.addWidget(self.paste_button) action_layout.addWidget(self.run_button) main_layout.addLayout(action_layout) self.frame.setLayout(main_layout) self.setWidget(self.frame) # connect all the relevant signals to handlers self.visibilityChanged[bool].connect( self._update_magic_helper ) self.search_class.activated[int].connect( self.class_selected ) self.search_line.textChanged[str].connect( self.search_changed ) self.search_list.itemDoubleClicked[QtGui.QListWidgetItem].connect( self.paste_requested ) self.paste_button.clicked[bool].connect( self.paste_requested ) self.run_button.clicked[bool].connect( self.run_requested ) #--------------------------------------------------------------------------- # implementation #--------------------------------------------------------------------------- def _update_magic_helper(self, visible): """Start update sequence. This method is called when MagicHelper becomes visible. It clears the content and emits readyForUpdate signal. The owner of the instance is expected to invoke populate_magic_helper() when magic info is available. """ if not visible or self.data != None: return self.data = {} self.search_class.clear() self.search_class.addItem("Populating...") self.search_list.clear() self.readyForUpdate.emit() def populate_magic_helper(self, data): """Expects data returned by lsmagics query from kernel. Populates the search_class and search_list with relevant items. """ self.search_class.clear() self.search_list.clear() self.data = json.loads( data['data'].get('application/json', {}) ) self.search_class.addItem('All Magics', 'any') classes = set() for mtype in sorted(self.data): subdict = self.data[mtype] for name in sorted(subdict): classes.add(subdict[name]) for cls in sorted(classes): label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls) self.search_class.addItem(label, cls) self.filter_magic_helper('.', 'any') def class_selected(self, index): """Handle search_class selection changes """ item = self.search_class.itemData(index) regex = self.search_line.text() self.filter_magic_helper(regex = regex, cls = item) def search_changed(self, search_string): """Handle search_line text changes. The text is interpreted as a regular expression """ item = self.search_class.itemData( self.search_class.currentIndex() ) self.filter_magic_helper(regex = search_string, cls = item) def _get_current_search_item(self, item = None): """Retrieve magic command currently selected in the search_list """ text = None if not isinstance(item, QtGui.QListWidgetItem): item = self.search_list.currentItem() text = item.text() return text def paste_requested(self, item = None): """Emit pasteRequested signal with currently selected item text """ text = self._get_current_search_item(item) if text != None: self.pasteRequested.emit(text) def run_requested(self, item = None): """Emit runRequested signal with currently selected item text """ text = self._get_current_search_item(item) if text != None: self.runRequested.emit(text) def filter_magic_helper(self, regex, cls): """Update search_list with magic commands whose text match regex and class match cls. If cls equals 'any' - any class matches. """ if regex == "" or regex == None: regex = '.' if cls == None: cls = 'any' self.search_list.clear() for mtype in sorted(self.data): subdict = self.data[mtype] prefix = magic_escapes[mtype] for name in sorted(subdict): mclass = subdict[name] pmagic = prefix + name if (re.match(regex, name) or re.match(regex, pmagic)) and \ (cls == 'any' or cls == mclass): self.search_list.addItem(pmagic)