Enabling Code Completion In An Embedded Python Interpreter
Solution 1:
I think you are referring to rlcompleter's Completer object.
You can used it like so:
from rlcompleter import Completer
line = str(...)
completer = Completer(self.interpreter.locals)
suggestion = completer.complete(line, 0)
self.insertPlainText(suggestion)
The numeric argument indicates the n-th suggestion, and you can iterate over it until it returns None
.
For example, say we have
>>>my_data = '012345'
then
>>> completer.complete('my_', 0)
'my_data'>>> completer.complete('my_data.s', 0)
'my_data.split('>>> completer.complete('my_data.s', 1)
'my_data.splitlines('
Note that while the code above uses interpreter.locals
, you can apply a wider search (but be sure to provide a dictionary).
Solution 2:
If you want to save yourself some time - take a look at spyderlib, it contains a widget that embeds an interactive Python interpreter with some interaction sugar such as code completion. The bits of specific interest are
- spyderlib/widgets/sourcecode/codeeditor.py
- spyderlib/shell.py
- spyderlib/editor.py
- spyderlib/widgets/externalshell/pythonshell.py
- spyderlib/utiils/module_completion.py
- spyderlib/plugins/externalconsole.py
- spyderlib/plugins/console.py
- spyderlib/plugins/editor.py
The only caveat I have with spyderlib is that you can't just use that doodad on it's own - somewhere I have a version I extracted that contains the bare minimum of support modules needed to run. If you run into the same problem I did regarding the bloat send me a msg and I'll check my stuff into github for you to grab.
I also seem to remember there's an Qt based interactive Python interpreter widget that is used in NumPy or SciPy - I think it originally came from the ipython project however. It's pretty nice because it actually splits the interpeter from the execution of code - so if your code crashes, your interpreter doesn't crash with it. But, in that case you can't modify the Pythonic contents of other threads.. The spyderlib version can work both ways.
Solution 3:
I have an open-source PyQt based Python interpreter that you can find here: http://docs.projexsoftware.com/api/projexui/
The specific class is the XConsoleEdit found in projexui.widgets.xconsoleedit. It has auto-completion built-in.
Hope that helps!
Solution 4:
I get auto complete from rlcompleter2, but there are two problems in the following code,
import xxx as yyy
auto complete on yyy doesn't work- the locals() are not copied into the interactiveinterpreter, I tried to use this code in Autodesk Maya, eg, run
x=3
in maya script editor, and then runx
in the pyqt interpreter, it saysNameError: name 'x' is not defined
. if you do not use maya, this error can be reproduced from external python interpreter as well, first define some variable, then launch this ui, the variable is not copied into the interpreter in the ui.
import os
import re
import sys
import code
from PyQt4.QtGui import *
from PyQt4.QtCore import *
classMyInterpreter(QWidget):
def__init__(self, parent):
super(MyInterpreter, self).__init__(parent)
hBox = QHBoxLayout()
self.setLayout(hBox)
self.textEdit = PyInterp(self)
# this is how you pass in locals to the interpreter
self.textEdit.initInterpreter(locals())
self.resize(850, 400)
# self.centerOnScreen()
hBox.addWidget(self.textEdit)
hBox.setMargin(0)
hBox.setSpacing(0)
defcenterOnScreen(self):
# center the widget on the screen
resolution = QDesktopWidget().screenGeometry()
self.move((resolution.width() / 2) - (self.frameSize().width() / 2),
(resolution.height() / 2) - (self.frameSize().height() / 2))
classPyInterp(QTextEdit):
classInteractiveInterpreter(code.InteractiveInterpreter):
def__init__(self, locals):
code.InteractiveInterpreter.__init__(self, locals)
defrunIt(self, command):
code.InteractiveInterpreter.runsource(self, command)
def__init__(self, parent):
super(PyInterp, self).__init__(parent)
sys.stdout = self
sys.stderr = self
self.refreshMarker = False# to change back to >>> from ...
self.multiLine = False# code spans more than one line
self.command = ''# command to be ran
self.printBanner() # print sys info
self.marker() # make the >>> or ... marker
self.history = [] # list of commands entered
self.historyIndex = -1
self.interpreterLocals = {}
# setting the color for bg and text# palette = QPalette()# palette.setColor(QPalette.Base, QColor(0, 0, 0))# palette.setColor(QPalette.Text, QColor(0, 255, 0))# self.setPalette(palette)
self.setFont(QFont('Courier', 10))
# initilize interpreter with self locals
self.initInterpreter(locals())
from rlcompleter2 import Completer
self.completer = Completer()
defprintBanner(self):
self.write(sys.version)
self.write(' on ' + sys.platform + '\n')
self.write('PyQt4 ' + PYQT_VERSION_STR + '\n')
# msg = 'Type !hist for a history view and !hist(n) history index recall'# self.write(msg + '\n')defmarker(self):
if self.multiLine:
self.insertPlainText('... ')
else:
self.insertPlainText('>>> ')
definitInterpreter(self, interpreterLocals=None):
if interpreterLocals:
# when we pass in locals, we don't want it to be named "self"# so we rename it with the name of the class that did the passing# and reinsert the locals back into the interpreter dictionary
selfName = interpreterLocals['self'].__class__.__name__
interpreterLocalVars = interpreterLocals.pop('self')
self.interpreterLocals[selfName] = interpreterLocalVars
else:
self.interpreterLocals = interpreterLocals
self.interpreter = self.InteractiveInterpreter(self.interpreterLocals)
defupdateInterpreterLocals(self, newLocals):
className = newLocals.__class__.__name__
self.interpreterLocals[className] = newLocals
defwrite(self, line):
self.insertPlainText(line)
self.ensureCursorVisible()
defclearCurrentBlock(self):
# block being current row
length = len(self.document().lastBlock().text()[4:])
if length == 0:
returnNoneelse:
# should have a better way of doing this but I can't find it
[self.textCursor().deletePreviousChar() for x in xrange(length)]
returnTruedefrecallHistory(self):
# used when using the arrow keys to scroll through history
self.clearCurrentBlock()
if self.historyIndex <> -1:
self.insertPlainText(self.history[self.historyIndex])
returnTruedefcustomCommands(self, command):
if command == '!hist': # display history
self.append('') # move down one line# vars that are in the command are prefixed with ____CC and deleted# once the command is done so they don't show up in dir()
backup = self.interpreterLocals.copy()
history = self.history[:]
history.reverse()
for i, x inenumerate(history):
iSize = len(str(i))
delta = len(str(len(history))) - iSize
line = line = ' ' * delta + '%i: %s' % (i, x) + '\n'
self.write(line)
self.updateInterpreterLocals(backup)
self.marker()
returnTrueif re.match('!hist\(\d+\)', command): # recall command from history
backup = self.interpreterLocals.copy()
history = self.history[:]
history.reverse()
index = int(command[6:-1])
self.clearCurrentBlock()
command = history[index]
if command[-1] == ':':
self.multiLine = True
self.write(command)
self.updateInterpreterLocals(backup)
returnTruereturnFalsedefkeyPressEvent(self, event):
if event.key() == Qt.Key_Tab:
line = str(self.document().lastBlock().text())[4:]
self.completer.construct(line)
iflen(self.completer.rl_matches) == 1:
self.clearCurrentBlock()
self.insertPlainText(self.completer.rl_matches[0])
else:
print'repeat:', self.completer.repeated
mod = self.completer.repeated % len(self.completer.completions)
if mod == 0:
# print '\n'.join(self.completer.rl_matches)
col_print(self.completer.rl_matches)
else:
print' 'print'\n'.join(self.completer.rl_matches)
# print self.completer.rl_matches
self.marker()
self.insertPlainText(line)
returnif event.key() == Qt.Key_Escape:
# proper exit
self.interpreter.runIt('exit()')
if event.key() == Qt.Key_Down:
if self.historyIndex == len(self.history):
self.historyIndex -= 1try:
if self.historyIndex > -1:
self.historyIndex -= 1
self.recallHistory()
else:
self.clearCurrentBlock()
except:
passreturnNoneif event.key() == Qt.Key_Up:
try:
iflen(self.history) - 1 > self.historyIndex:
self.historyIndex += 1
self.recallHistory()
else:
self.historyIndex = len(self.history)
except:
passreturnNoneif event.key() == Qt.Key_Home:
# set cursor to position 4 in current block. 4 because that's where# the marker stops
blockLength = len(self.document().lastBlock().text()[4:])
lineLength = len(self.document().toPlainText())
position = lineLength - blockLength
textCursor = self.textCursor()
textCursor.setPosition(position)
self.setTextCursor(textCursor)
returnNoneif event.key() in [Qt.Key_Left, Qt.Key_Backspace]:
# don't allow deletion of marker# if qt version < 4.7, have to use position() - block().position()if self.textCursor().positionInBlock() == 4:
returnNoneif event.key() in [Qt.Key_Return, Qt.Key_Enter]:
# set cursor to end of line to avoid line splitting
textCursor = self.textCursor()
position = len(self.document().toPlainText())
textCursor.setPosition(position)
self.setTextCursor(textCursor)
line = str(self.document().lastBlock().text())[4:] # remove marker
line.rstrip()
self.historyIndex = -1if self.customCommands(line):
returnNoneelse:
try:
line[-1]
self.haveLine = Trueif line[-1] == ':':
self.multiLine = True
self.history.insert(0, line)
except:
self.haveLine = Falseif self.haveLine and self.multiLine: # multi line command
self.command += line + '\n'# + command and line
self.append('') # move down one line
self.marker() # handle marker stylereturnNoneif self.haveLine andnot self.multiLine: # one line command
self.command = line # line is the command
self.append('') # move down one line
self.interpreter.runIt(self.command)
self.command = ''# clear command
self.marker() # handle marker stylereturnNoneif self.multiLine andnot self.haveLine: # multi line done
self.append('') # move down one line
self.interpreter.runIt(self.command)
self.command = ''# clear command
self.multiLine = False# back to single line
self.marker() # handle marker stylereturnNoneifnot self.haveLine andnot self.multiLine: # just enter
self.append('')
self.marker()
returnNonereturnNone# allow all other key eventssuper(PyInterp, self).keyPressEvent(event)
# http://stackoverflow.com/a/30861871/2052889defcol_print(lines, term_width=90, indent=0, pad=2):
n_lines = len(lines)
if n_lines == 0:
return
col_width = max(len(line) for line in lines)
n_cols = int((term_width + pad - indent)/(col_width + pad))
n_cols = min(n_lines, max(1, n_cols))
col_len = int(n_lines/n_cols) + (0if n_lines % n_cols == 0else1)
if (n_cols - 1) * col_len >= n_lines:
n_cols -= 1
cols = [lines[i*col_len: i*col_len + col_len] for i inrange(n_cols)]
rows = list(zip(*cols))
rows_missed = zip(*[col[len(rows):] for col in cols[:-1]])
rows.extend(rows_missed)
for row in rows:
print(" "*indent + (" "*pad).join(line.ljust(col_width)
for line in row))
defmain():
app = QApplication(sys.argv)
win = MyInterpreter(None)
win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
desired effect: https://gfycat.com/DistantScrawnyCivet
current effect: https://gfycat.com/DeafeningHeavyBoto
Post a Comment for "Enabling Code Completion In An Embedded Python Interpreter"