Comment intégrer un interpréteur Python dans un widget PyQT
Je veux être en mesure d'apporter un interactif python terminal à partir de mon application en python. Certains, mais pas tous, des variables dans mon programme doit être exposée à l'interprète.
Actuellement, je utiliser un sous-classés et modifiés QPlainTextEdit
et de la route de tous les "commandes" de là à eval
ou exec
, et de garder trace d'un autre espace de noms dans un dict. Cependant il faut être un plus élégant et robuste! Comment?
Voici un exemple de faire juste ce que je veux, mais c'est avec IPython et pyGTK...
http://ipython.scipy.org/moin/Cookbook/EmbeddingInGTK
Ci-dessous est ce que j'ai actuellement. Mais il ya tellement de nombreux cas de coin que j'ai probablement manqué quelques-uns. Il est très lent, essayez une grande impression de boucle... Il a obtenu d'être plus simple et moins de bug sujettes à façon, ...je l'espère!!
C'est le def runCommand(self)
fonction qui est la clé de la compréhension de mon problème. J'idéalement ne pas vouloir l'améliorer, j'ai plutôt envie de remplacer son contenu par quelque chose de plus simple et plus intelligente.
La fonctionnalité de la console.updateNamespace({'myVar1' : app, 'myVar2' : 1234})
déclaration dans "principal" est également important.
import sys, os
import traceback
from PyQt4 import QtCore
from PyQt4 import QtGui
class Console(QtGui.QPlainTextEdit):
def __init__(self, prompt='$> ', startup_message='', parent=None):
QtGui.QPlainTextEdit.__init__(self, parent)
self.prompt = prompt
self.history = []
self.namespace = {}
self.construct = []
self.setGeometry(50, 75, 600, 400)
self.setWordWrapMode(QtGui.QTextOption.WrapAnywhere)
self.setUndoRedoEnabled(False)
self.document().setDefaultFont(QtGui.QFont("monospace", 10, QtGui.QFont.Normal))
self.showMessage(startup_message)
def updateNamespace(self, namespace):
self.namespace.update(namespace)
def showMessage(self, message):
self.appendPlainText(message)
self.newPrompt()
def newPrompt(self):
if self.construct:
prompt = '.' * len(self.prompt)
else:
prompt = self.prompt
self.appendPlainText(prompt)
self.moveCursor(QtGui.QTextCursor.End)
def getCommand(self):
doc = self.document()
curr_line = unicode(doc.findBlockByLineNumber(doc.lineCount() - 1).text())
curr_line = curr_line.rstrip()
curr_line = curr_line[len(self.prompt):]
return curr_line
def setCommand(self, command):
if self.getCommand() == command:
return
self.moveCursor(QtGui.QTextCursor.End)
self.moveCursor(QtGui.QTextCursor.StartOfLine, QtGui.QTextCursor.KeepAnchor)
for i in range(len(self.prompt)):
self.moveCursor(QtGui.QTextCursor.Right, QtGui.QTextCursor.KeepAnchor)
self.textCursor().removeSelectedText()
self.textCursor().insertText(command)
self.moveCursor(QtGui.QTextCursor.End)
def getConstruct(self, command):
if self.construct:
prev_command = self.construct[-1]
self.construct.append(command)
if not prev_command and not command:
ret_val = '\n'.join(self.construct)
self.construct = []
return ret_val
else:
return ''
else:
if command and command[-1] == (':'):
self.construct.append(command)
return ''
else:
return command
def getHistory(self):
return self.history
def setHisory(self, history):
self.history = history
def addToHistory(self, command):
if command and (not self.history or self.history[-1] != command):
self.history.append(command)
self.history_index = len(self.history)
def getPrevHistoryEntry(self):
if self.history:
self.history_index = max(0, self.history_index - 1)
return self.history[self.history_index]
return ''
def getNextHistoryEntry(self):
if self.history:
hist_len = len(self.history)
self.history_index = min(hist_len, self.history_index + 1)
if self.history_index < hist_len:
return self.history[self.history_index]
return ''
def getCursorPosition(self):
return self.textCursor().columnNumber() - len(self.prompt)
def setCursorPosition(self, position):
self.moveCursor(QtGui.QTextCursor.StartOfLine)
for i in range(len(self.prompt) + position):
self.moveCursor(QtGui.QTextCursor.Right)
def runCommand(self):
command = self.getCommand()
self.addToHistory(command)
command = self.getConstruct(command)
if command:
tmp_stdout = sys.stdout
class stdoutProxy():
def __init__(self, write_func):
self.write_func = write_func
self.skip = False
def write(self, text):
if not self.skip:
stripped_text = text.rstrip('\n')
self.write_func(stripped_text)
QtCore.QCoreApplication.processEvents()
self.skip = not self.skip
sys.stdout = stdoutProxy(self.appendPlainText)
try:
try:
result = eval(command, self.namespace, self.namespace)
if result != None:
self.appendPlainText(repr(result))
except SyntaxError:
exec command in self.namespace
except SystemExit:
self.close()
except:
traceback_lines = traceback.format_exc().split('\n')
# Remove traceback mentioning this file, and a linebreak
for i in (3,2,1,-1):
traceback_lines.pop(i)
self.appendPlainText('\n'.join(traceback_lines))
sys.stdout = tmp_stdout
self.newPrompt()
def keyPressEvent(self, event):
if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
self.runCommand()
return
if event.key() == QtCore.Qt.Key_Home:
self.setCursorPosition(0)
return
if event.key() == QtCore.Qt.Key_PageUp:
return
elif event.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Backspace):
if self.getCursorPosition() == 0:
return
elif event.key() == QtCore.Qt.Key_Up:
self.setCommand(self.getPrevHistoryEntry())
return
elif event.key() == QtCore.Qt.Key_Down:
self.setCommand(self.getNextHistoryEntry())
return
elif event.key() == QtCore.Qt.Key_D and event.modifiers() == QtCore.Qt.ControlModifier:
self.close()
super(Console, self).keyPressEvent(event)
welcome_message = '''
---------------------------------------------------------------
Welcome to a primitive Python interpreter.
---------------------------------------------------------------
'''
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
console = Console(startup_message=welcome_message)
console.updateNamespace({'myVar1' : app, 'myVar2' : 1234})
console.show();
sys.exit(app.exec_())
- Premier résultat de google: doc.trolltech.com/qq/qq23-pythonqt.html, et à partir de la seconde: wiki.python.org/moin/EmbedingPyQtTutorial. Ceux adaptée à vos besoins? Nouveau: stackoverflow.com/questions/2742636/...
- Non, mon application est écrite en Python. Ces pages concernent emballage C(++) dans des applications en python et l'incorporation de python en C(++) des applications.
- S'il vous plaît, j'ai oublié mon commentaire précédent
Vous devez vous connecter pour publier un commentaire.
Peu en retard je sais, mais je vous recommande l' code.InteractiveConsole classe: http://docs.python.org/py3k/library/code.html#code.InteractiveConsole
Vous devriez regarder dans l'utilisation de threads pour conserver l'INTERFACE utilisateur réactive lors de l'impression des grandes boucles. Ce serait également aider à garder votre retraçage propre.
Maintenant les variables dans un dict est la voie à suivre – c'est ce que Python lui-même n'en interne.
Aussi loin que l'exposition “de certains, mais pas tous”, pensez juste à les exposer tous. Beaucoup plus facile. Si vous êtes préoccupé par la sécurité, méfiez-vous que vous ne pouvez pas fiable cacher quoi que ce soit en Python.
Comme pour le hideux curseur/manipulation de texte: profitez du fait que vous disposez d'une interface graphique.
Avec un terminal, vous avez juste une “zone de texte”, mais dans Qt, il peut être plus approprié d'avoir un journal/affichage du résultat de la et un séparé boîte de commande.
L'affichage de l'historique d'affichage, la saisie des commandes et des résultats en lecture seule zone de texte.
La commande zone de texte vous permet de saisir une commande proprement.
Cette approche est utilisée dans certains frameworks web – par exemple via des WebError:
Première ébauche de la version mise à jour de mon code à l'appui de IPython 0.13
Pas sûr de ce que vous voulez exactement, mais ont essayé de sauver le contenu du widget dans un fichier temporaire et de le passer à un standard de l'interpréteur python avec Popen ?
Doc est ici : http://docs.python.org/release/2.6.5/library/subprocess.html#subprocess.Popen
Exemple :
Cela sonne comme vous l'avez fait quelque chose de similaire à mon Veusz application, https://veusz.github.io/. J'ai pensé que vous pourriez trouver utile de voir une implémentation plus complète. Je ne peux pas poster de liens hypertextes, mais jetez un oeil à windows/consolewindow.py pour le widget de la classe. Les commandes sont exécutées par le document/commandinterpreter.py classe. L'interface est définie dans document/commandinterface.py. C'est surtout le fait de la manipulation d'un dict cependant.