def test_droplist_completer_mousepick(self): leftButton = QtCore.Qt.LeftButton w = CompletionWidget(self.console) w.show_items(self.text_edit.textCursor(), ["item1", "item2", "item3"]) QTest.mouseClick(w.viewport(), leftButton, pos=QtCore.QPoint(19, 8)) QTest.mouseRelease(w.viewport(), leftButton, pos=QtCore.QPoint(19, 8)) QTest.mouseDClick(w.viewport(), leftButton, pos=QtCore.QPoint(19, 8)) self.assertEqual(self.text_edit.toPlainText(), "item1") self.assertFalse(w.isVisible())
def __init__(self, is_complete=None, text='', use_ansi=True, comment_prefix='#', parent=None, **kwargs): """ Initialize. :param is_complete: function str->(bool, str) that checks whether the input is complete code :param code: True if object should initially expect code to be executed; otherwise arbitrary text. :param text: initial text. :param parent: parent widget. :param kwargs: arguments for LoggingConfigurable :return: """ edit_class.__init__(self, text, parent) DocumentConfig.__init__(self, **kwargs) self.use_ansi = use_ansi self.highlighter.enable() # Call tips # forcefully disable calltips if PySide is < 1.0.7, because they crash if QT_API == QT_API_PYSIDE: import PySide if PySide.__version_info__ < (1,0,7): self.log.warn("PySide %s < 1.0.7 detected, disabling calltips" % PySide.__version__) self.enable_call_tips = False self.call_tip_widget = CallTipWidget(self) self.call_tip_widget.setFont(self.font) self.font_changed.connect(self.call_tip_widget.setFont) self._bracket_matcher = BracketMatcher(self) self.document().contentsChange.connect(self._document_contents_change) self.setFrameStyle(QtGui.QFrame.NoFrame) self.setAcceptDrops(True) self._control = self # required for completer self._clear_temporary_buffer = lambda: None self.completer = CompletionWidget(self) self.completer.setFont(self.font) self.is_complete = is_complete self.viewport_filter = ViewportFilter(self) self.viewport().installEventFilter(self.viewport_filter) self.entry_filter = CodeAreaFilter(self) self.installEventFilter(self.entry_filter) self.text_area_filter = TextAreaFilter(self) self.installEventFilter(self.text_area_filter) # Text interaction self.setTextInteractionFlags(QtCore.Qt.TextEditable | QtCore.Qt.TextEditorInteraction) self.setUndoRedoEnabled(True) self.kill_ring = QtKillRing(self) self.history = History(self) self._comment_prefix = comment_prefix
def test_common_path_complete(self): with TemporaryDirectory() as tmpdir: items = [ os.path.join(tmpdir, "common/common1/item1"), os.path.join(tmpdir, "common/common1/item2"), os.path.join(tmpdir, "common/common1/item3") ] for item in items: os.makedirs(item) w = CompletionWidget(self.console) w.show_items(self.text_edit.textCursor(), items) self.assertEqual(w.currentItem().text(), '/item1') QTest.keyClick(w, QtCore.Qt.Key_Down) self.assertEqual(w.currentItem().text(), '/item2') QTest.keyClick(w, QtCore.Qt.Key_Down) self.assertEqual(w.currentItem().text(), '/item3')
def test_droplist_completer_keyboard(self): w = CompletionWidget(self.console) w.show_items(self.text_edit.textCursor(), ["item1", "item2", "item3"]) QTest.keyClick(w, QtCore.Qt.Key_PageDown) QTest.keyClick(w, QtCore.Qt.Key_Enter) self.assertEqual(self.text_edit.toPlainText(), "item3")
def test_droplist_completer_shows(self): w = CompletionWidget(self.console) w.show_items(self.text_edit.textCursor(), ["item1", "item2", "item3"]) self.assertTrue(w.isVisible())
class CodeArea(MetaQObjectHasTraits('NewBase', (DocumentConfig, edit_class), {})): """ Text edit that has two modes, code and chat mode, accepting code to be executed or arbitrary text (chat messages). """ name = 'Code' # name of the widget font_changed = QtCore.Signal(QtGui.QFont) # Signal emitted when the font is changed. execute_on_complete_input = Bool(True, config=True, help="""Whether to automatically execute on syntactically complete input. If False, Shift-Enter is required to submit each execution. Disabling this is mainly useful for non-Python kernels, where the completion check would be wrong. """ ) # Whether to automatically show calltips on open-parentheses. enable_call_tips = Bool(True, config=True, help="Whether to draw information calltips on open-parentheses.") call_tip_widget = None # CallTipWidget _bracket_matcher = None # BracketMatcher call_tip_position = 0 # cursor position where a call tip should be shown clear_on_kernel_restart = Bool(True, config=True, help="Whether to clear the console when the kernel is restarted") please_export = QtCore.Signal(ExportItem) # tasks for the kernel is_complete = None # function str->(bool, str) that checks whether the input is complete code viewport_filter = None entry_filter = None text_area_filter = None release_focus = QtCore.Signal() to_next = QtCore.Signal() completer = None # completion object kill_ring = None # QKillRing history = None # History object _comment_prefix = '#' # prefix for line comments def __init__(self, is_complete=None, text='', use_ansi=True, comment_prefix='#', parent=None, **kwargs): """ Initialize. :param is_complete: function str->(bool, str) that checks whether the input is complete code :param code: True if object should initially expect code to be executed; otherwise arbitrary text. :param text: initial text. :param parent: parent widget. :param kwargs: arguments for LoggingConfigurable :return: """ edit_class.__init__(self, text, parent) DocumentConfig.__init__(self, **kwargs) self.use_ansi = use_ansi self.highlighter.enable() # Call tips # forcefully disable calltips if PySide is < 1.0.7, because they crash if QT_API == QT_API_PYSIDE: import PySide if PySide.__version_info__ < (1,0,7): self.log.warn("PySide %s < 1.0.7 detected, disabling calltips" % PySide.__version__) self.enable_call_tips = False self.call_tip_widget = CallTipWidget(self) self.call_tip_widget.setFont(self.font) self.font_changed.connect(self.call_tip_widget.setFont) self._bracket_matcher = BracketMatcher(self) self.document().contentsChange.connect(self._document_contents_change) self.setFrameStyle(QtGui.QFrame.NoFrame) self.setAcceptDrops(True) self._control = self # required for completer self._clear_temporary_buffer = lambda: None self.completer = CompletionWidget(self) self.completer.setFont(self.font) self.is_complete = is_complete self.viewport_filter = ViewportFilter(self) self.viewport().installEventFilter(self.viewport_filter) self.entry_filter = CodeAreaFilter(self) self.installEventFilter(self.entry_filter) self.text_area_filter = TextAreaFilter(self) self.installEventFilter(self.text_area_filter) # Text interaction self.setTextInteractionFlags(QtCore.Qt.TextEditable | QtCore.Qt.TextEditorInteraction) self.setUndoRedoEnabled(True) self.kill_ring = QtKillRing(self) self.history = History(self) self._comment_prefix = comment_prefix def _set_font(self, font): DocumentConfig.set_font(self, font) if hasattr(self, 'completer') and self.completer: self.completer.setFont(font) self.font_changed.emit(font) @property def source(self): """ Get the source from the document edited. :return: Source object. """ return Source(self.toPlainText()) def post(self, item): """ Process the item received. :param item: ImportItem for the input area. :return: """ _post(item, self) @QtCore.Slot() def set_focus(self): """ Set the focus to this widget. :return: """ self.setFocus() # JupyterWidget def process_complete(self, items): cursor = self.textCursor() matches = items.matches start = items.start end = items.end start = max(start, 0) end = max(end, start) # Move the control's cursor to the desired end point cursor_pos = self.textCursor().position() if end < cursor_pos: cursor.movePosition(QtGui.QTextCursor.Left, n=(cursor_pos - end)) elif end > cursor_pos: cursor.movePosition(QtGui.QTextCursor.Right, n=(end - cursor_pos)) # This line actually applies the move to control's cursor self.setTextCursor(cursor) offset = end - start # Move the local cursor object to the start of the match and # complete. cursor.movePosition(QtGui.QTextCursor.Left, n=offset) self._complete_with_items(cursor, matches) # ConsoleWidget def _complete_with_items(self, cursor, items): """ Complete code at a given location. :param cursor: cursor where completion is performed. :param items: list of items that can serve for completion. :return: """ self.completer.cancel_completion() if len(items) == 1: cursor.setPosition(self.textCursor().position(), QtGui.QTextCursor.KeepAnchor) cursor.insertText(items[0]) elif len(items) > 1: current_pos = self.textCursor().position() prefix = os.path.commonprefix(items) if prefix: cursor.setPosition(current_pos, QtGui.QTextCursor.KeepAnchor) cursor.insertText(prefix) cursor.movePosition(QtGui.QTextCursor.Left, n=len(prefix)) self.completer.show_items(cursor, items) # FrontendWidget def _auto_call_tip(self): """Trigger call tip automatically on open parenthesis Call tips can be requested explcitly with `_call_tip`. """ cursor = self.textCursor() cursor.movePosition(QtGui.QTextCursor.Left) if cursor.document().characterAt(cursor.position()) == '(': # trigger auto call tip on open paren self._call_tip() # FrontendWidget def _call_tip(self): """Shows a call tip, if appropriate, at the current cursor location.""" # Decide if it makes sense to show a call tip # if not self.enable_calltips or not self.kernel_client.shell_channel.is_alive(): # return False # cursor_pos = self._get_input_buffer_cursor_pos() # code = self.toPlainText() # # Send the metadata request to the kernel # msg_id = self.kernel_client.inspect(code, cursor_pos) if not self.enable_call_tips: return False cursor_pos = self.textCursor().position() self.please_export.emit(Inspect(self.source, cursor_pos)) return True # FrontendWidget def _document_contents_change(self, position, removed, added): """ Called whenever the document's content changes. Display a call tip if appropriate. """ # Calculate where the cursor should be *after* the change: position += added if position == self.textCursor().position(): self.call_tip_position = position self._auto_call_tip()