[QGIS Commit] r12727 - in trunk/qgis: python src/ui
svn_qgis at osgeo.org
svn_qgis at osgeo.org
Sun Jan 10 06:14:47 EST 2010
Author: wonder
Date: 2010-01-10 06:14:46 -0500 (Sun, 10 Jan 2010)
New Revision: 12727
Removed:
trunk/qgis/src/ui/qgspythondialog.ui
Modified:
trunk/qgis/python/CMakeLists.txt
trunk/qgis/python/console.py
Log:
[FEATURE] new python console implementation.
Simpler, easier and better :-)
Modified: trunk/qgis/python/CMakeLists.txt
===================================================================
--- trunk/qgis/python/CMakeLists.txt 2010-01-10 00:10:48 UTC (rev 12726)
+++ trunk/qgis/python/CMakeLists.txt 2010-01-10 11:14:46 UTC (rev 12727)
@@ -99,12 +99,7 @@
ENDIF (BINDINGS_GLOBAL_INSTALL)
-# python console
-PYQT4_WRAP_UI(PYUI_FILE ${CMAKE_SOURCE_DIR}/src/ui/qgspythondialog.ui)
-ADD_CUSTOM_TARGET(pythonconsole ALL DEPENDS ${PYUI_FILE})
-
-
# Step 4: install built libs to python's site packages
-INSTALL(FILES __init__.py utils.py console.py ${PYUI_FILE} ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
+INSTALL(FILES __init__.py utils.py console.py ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
Modified: trunk/qgis/python/console.py
===================================================================
--- trunk/qgis/python/console.py 2010-01-10 00:10:48 UTC (rev 12726)
+++ trunk/qgis/python/console.py 2010-01-10 11:14:46 UTC (rev 12727)
@@ -1,30 +1,60 @@
# -*- coding: utf-8 -*-
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# Some portions of code were taken from managerR plugin.
+
+"""
+Implementation of interactive Python console widget for QGIS.
+
+Has +- the same behaviour as command-line interactive console:
+- runs commands one by one
+- supports expressions that span through more lines
+- has command history, accessible using up/down keys
+- supports pasting of commands
+
+TODO:
+- configuration - init commands, font, ...
+- syntax highlighting
+
+"""
+
from PyQt4.QtCore import *
from PyQt4.QtGui import *
-
+import sys
import traceback
-import sys
+import code
-from ui_qgspythondialog import Ui_QgsPythonDialog
+_init_commands = ["from qgis.core import *", "import qgis.utils"]
+
+
_console = None
-
def show_console():
""" called from QGIS to open the console """
global _console
if _console is None:
- _console = QgsPythonConsole()
+ _console = PythonConsole()
_console.show()
_console.raise_()
_console.setWindowState( _console.windowState() & ~Qt.WindowMinimized )
_console.activateWindow()
+
-
_old_stdout = sys.stdout
+_console_output = None
+# hook for python console so all output will be redirected
+# and then shown in console
+def console_displayhook(obj):
+ global _console_output
+ _console_output = obj
+
class QgisOutputCatcher:
def __init__(self):
self.data = ''
@@ -36,145 +66,218 @@
return tmp
def flush(self):
pass
-
-def installConsoleHook():
- sys.stdout = QgisOutputCatcher()
-def uninstallConsoleHook():
- sys.stdout = _old_stdout
+sys.stdout = QgisOutputCatcher()
+class PythonConsole(QWidget):
+ def __init__(self, parent=None):
+ QWidget.__init__(self, parent)
-class ConsoleHistory(object):
- def __init__(self):
- self.cmds = []
- self.pos = 0
- self.active_cmd = u"" # active, not yet entered command
+ self.edit = PythonEdit()
+ self.l = QVBoxLayout()
+ self.l.addWidget(self.edit)
+ self.setLayout(self.l)
+ self.setWindowTitle("Python console")
- def add_cmd(self, command):
- if len(command) != 0:
- self.cmds.append(command)
- self.pos = len(self.cmds)
- self.active_cmd = u""
+ s = QSettings()
+ self.restoreGeometry(s.value("/python/console/geometry").toByteArray())
- def get_previous_cmd(self, current):
- if self.pos == 0:
- return None
+ def sizeHint(self):
+ return QSize(500,300)
- if self.pos == len(self.cmds):
- self.active_cmd = current
- else:
- self.cmds[self.pos] = current
+ def closeEvent(self, event):
+ s = QSettings()
+ s.setValue("/python/console/geometry", QVariant(self.saveGeometry()))
+ QWidget.closeEvent(self, event)
- self.pos -= 1
- return self.cmds[self.pos]
- def get_next_cmd(self, current):
+class PythonEdit(QTextEdit, code.InteractiveInterpreter):
- # if we're at active (last) command, don't move
- if self.pos == len(self.cmds):
- return None
-
- self.cmds[self.pos] = current
- self.pos += 1
+ def __init__(self,parent=None):
+ QTextEdit.__init__(self, parent)
+ code.InteractiveInterpreter.__init__(self, locals=None)
- if self.pos == len(self.cmds):
- return self.active_cmd
- else:
- return self.cmds[self.pos]
+ self.setTextInteractionFlags(Qt.TextEditorInteraction)
+ self.setAcceptDrops(False)
+ self.setMinimumSize(30, 30)
+ self.setUndoRedoEnabled(False)
+ self.setAcceptRichText(False)
+ #monofont = QFont("Bitstream Vera Sans Mono", 10)
+ #self.setFont(monofont)
+ self.buffer = []
+ self.insertPlainText("To access Quantum GIS environment from this console\n"
+ "use qgis.utils.iface object (instance of QgisInterface class).\n"
+ "\n")
-class QgsPythonConsole(QWidget, Ui_QgsPythonDialog):
- def __init__(self, parent=None):
- QWidget.__init__(self, parent)
+ for line in _init_commands:
+ self.runsource(line)
- self.setupUi(self)
+ self.displayPrompt(False)
- # minimize button was not enabled on mac
- self.setWindowFlags( self.windowFlags() | Qt.WindowMinimizeButtonHint )
+ self.history = QStringList()
+ self.historyIndex = 0
- self.history = ConsoleHistory()
+ #from pythonhigh import PythonHighlighter
+ #self.high = PythonHighlighter(self)
- self.console_globals = {}
-
- def escapeHtml(self, text):
- return text.replace("<", "<").replace(">", ">")
+ def displayPrompt(self, more=False):
+ self.currentPrompt = "... " if more else ">>> "
+ self.currentPromptLength = len(self.currentPrompt)
+ self.insertPlainText(self.currentPrompt)
+ self.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
- @pyqtSlot()
- def on_pbnPrev_clicked(self):
- cmd = self.history.get_previous_cmd( self.getCommand() )
- if cmd is not None:
- self.edtCmdLine.setText(cmd)
+ def isCursorInEditionZone(self):
+ cursor = self.textCursor()
+ pos = cursor.position()
+ block = self.document().lastBlock()
+ last = block.position() + self.currentPromptLength
+ return pos >= last
- @pyqtSlot()
- def on_pbnNext_clicked(self):
- cmd = self.history.get_next_cmd( self.getCommand() )
- if cmd is not None:
- self.edtCmdLine.setText(cmd)
+ def currentCommand(self):
+ block = self.cursor.block()
+ text = block.text()
+ return text.right(text.length()-self.currentPromptLength)
- def getCommand(self):
- return unicode(self.edtCmdLine.toPlainText())
+ def showPrevious(self):
+ if self.historyIndex < len(self.history) and not self.history.isEmpty():
+ self.cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.MoveAnchor)
+ self.cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor)
+ self.cursor.removeSelectedText()
+ self.cursor.insertText(self.currentPrompt)
+ self.historyIndex += 1
+ if self.historyIndex == len(self.history):
+ self.insertPlainText("")
+ else:
+ self.insertPlainText(self.history[self.historyIndex])
- def execute(self, single):
- command = self.getCommand()
+ def showNext(self):
+ if self.historyIndex > 0 and not self.history.isEmpty():
+ self.cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.MoveAnchor)
+ self.cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor)
+ self.cursor.removeSelectedText()
+ self.cursor.insertText(self.currentPrompt)
+ self.historyIndex -= 1
+ if self.historyIndex == len(self.history):
+ self.insertPlainText("")
+ else:
+ self.insertPlainText(self.history[self.historyIndex])
- self.history.add_cmd(command)
+ def updateHistory(self, command):
+ if isinstance(command, QStringList):
+ for line in command:
+ self.history.append(line)
+ elif not command == "":
+ if len(self.history) <= 0 or \
+ not command == self.history[-1]:
+ self.history.append(command)
+ self.historyIndex = len(self.history)
- try:
- # run command
- code = compile(command, '<console>', 'single' if single else 'exec')
- res = eval(code, self.console_globals)
- result = unicode( res ) if res is not None else u''
-
- # get standard output
- output = sys.stdout.get_and_clean_data()
+ def keyPressEvent(self, e):
+ self.cursor = self.textCursor()
+ # if the cursor isn't in the edition zone, don't do anything except Ctrl+C
+ if not self.isCursorInEditionZone():
+ if e.modifiers() == Qt.ControlModifier or e.modifiers() == Qt.MetaModifier:
+ if e.key() == Qt.Key_C or e.key() == Qt.Key_A:
+ QTextEdit.keyPressEvent(self, e)
+ else:
+ # all other keystrokes get sent to the input line
+ self.cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
+ else:
+ # if Return is pressed, then perform the commands
+ if e.key() == Qt.Key_Return:
+ self.entered()
+ # if Up or Down is pressed
+ elif e.key() == Qt.Key_Down:
+ self.showPrevious()
+ elif e.key() == Qt.Key_Up:
+ self.showNext()
+ # if backspace is pressed, delete until we get to the prompt
+ elif e.key() == Qt.Key_Backspace:
+ if not self.cursor.hasSelection() and self.cursor.columnNumber() == self.currentPromptLength:
+ return
+ QTextEdit.keyPressEvent(self, e)
+ # if the left key is pressed, move left until we get to the prompt
+ elif e.key() == Qt.Key_Left and self.cursor.position() > self.document().lastBlock().position() + self.currentPromptLength:
+ if e.modifiers() == Qt.ShiftModifier:
+ anchor = QTextCursor.KeepAnchor
+ else:
+ anchor = QTextCursor.MoveAnchor
+ if (e.modifiers() == Qt.ControlModifier or e.modifiers() == Qt.MetaModifier):
+ self.cursor.movePosition(QTextCursor.WordLeft, anchor)
+ else:
+ self.cursor.movePosition(QTextCursor.Left, anchor)
+ # use normal operation for right key
+ elif e.key() == Qt.Key_Right:
+ if e.modifiers() == Qt.ShiftModifier:
+ anchor = QTextCursor.KeepAnchor
+ else:
+ anchor = QTextCursor.MoveAnchor
+ if (e.modifiers() == Qt.ControlModifier or e.modifiers() == Qt.MetaModifier):
+ self.cursor.movePosition(QTextCursor.WordRight, anchor)
+ else:
+ self.cursor.movePosition(QTextCursor.Right, anchor)
+ # if home is pressed, move cursor to right of prompt
+ elif e.key() == Qt.Key_Home:
+ if e.modifiers() == Qt.ShiftModifier:
+ anchor = QTextCursor.KeepAnchor
+ else:
+ anchor = QTextCursor.MoveAnchor
+ self.cursor.movePosition(QTextCursor.StartOfBlock, anchor, 1)
+ self.cursor.movePosition(QTextCursor.Right, anchor, self.currentPromptLength)
+ # use normal operation for end key
+ elif e.key() == Qt.Key_End:
+ if e.modifiers() == Qt.ShiftModifier:
+ anchor = QTextCursor.KeepAnchor
+ else:
+ anchor = QTextCursor.MoveAnchor
+ self.cursor.movePosition(QTextCursor.EndOfBlock, anchor, 1)
+ # use normal operation for all remaining keys
+ else:
+ QTextEdit.keyPressEvent(self, e)
+ self.setTextCursor(self.cursor)
+ self.ensureCursorVisible()
- #_old_stdout.write("cmd: '"+command+"'\n")
- #_old_stdout.write("res: '"+result+"'\n")
- #_old_stdout.write("out: '"+output+"'\n")
- #_old_stdout.flush()
+ def insertFromMimeData(self, source):
+ self.cursor = self.textCursor()
+ self.cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor, 1)
+ self.setTextCursor(self.cursor)
+ if source.hasText():
+ pasteList = QStringList()
+ pasteList = source.text().split("\n")
+ for line in pasteList:
+ self.insertPlainText(line)
+ self.runCommand(unicode(line))
- # escape the result so python objects display properly and
- # we can still use html output to get nicely formatted display
- output = self.escapeHtml( output ) + self.escapeHtml( result )
- if len(output) != 0:
- output += "<br>"
+ def entered(self):
+ self.cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
+ self.runCommand( unicode(self.currentCommand()) )
- except Exception, e:
- #lst = traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)
- lst = traceback.format_exception_only(sys.exc_type, sys.exc_value)
- errorText = "<br>".join(map(self.escapeHtml, lst))
- output = "<font color=\"red\">" + errorText + "</font><br>"
+ def runCommand(self, cmd):
- s = "<b><font color=\"green\">>>></font> " + self.escapeHtml( command ) + "</b><br>" + output
- self.edtCmdLine.setText("")
+ self.updateHistory(cmd)
- self.txtHistory.moveCursor(QTextCursor.End)
- self.txtHistory.insertHtml(s)
- self.txtHistory.moveCursor(QTextCursor.End)
- self.txtHistory.ensureCursorVisible()
+ self.insertPlainText("\n")
- @pyqtSlot()
- def on_pbnExecute_clicked(self):
- self.execute(False)
+ self.buffer.append(cmd)
+ src = "\n".join(self.buffer)
+ more = self.runsource(src, "<input>")
+ if not more:
+ self.buffer = []
- @pyqtSlot()
- def on_pbnEval_clicked(self):
- self.execute(True)
+ output = sys.stdout.get_and_clean_data()
+ if output:
+ self.insertPlainText(output)
+ self.displayPrompt(more)
- def showEvent(self, event):
- QWidget.showEvent(self, event)
- installConsoleHook()
+ def write(self, txt):
+ """ reimplementation from code.InteractiveInterpreter """
+ self.insertPlainText(txt)
- def closeEvent(self, event):
- uninstallConsoleHook()
- QWidget.closeEvent(self, event)
-
if __name__ == '__main__':
- import sys
a = QApplication(sys.argv)
- w = QgsPythonConsole()
- w.show()
+ show_console()
a.exec_()
Deleted: trunk/qgis/src/ui/qgspythondialog.ui
===================================================================
--- trunk/qgis/src/ui/qgspythondialog.ui 2010-01-10 00:10:48 UTC (rev 12726)
+++ trunk/qgis/src/ui/qgspythondialog.ui 2010-01-10 11:14:46 UTC (rev 12727)
@@ -1,118 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>QgsPythonDialog</class>
- <widget class="QDialog" name="QgsPythonDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>625</width>
- <height>641</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Python console</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0" colspan="5">
- <widget class="QLabel" name="lblInfo">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
-<html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'DejaVu Sans'; font-size:8pt; font-weight:400; font-style:normal;">
-<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">To access Quantum GIS environment from this python console use qgis.utils.iface object which is an instance of QgisInterface class.<br />Usage e.g.: qgis.utils.iface.zoomFull()</p></body></html></string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="2" column="4">
- <widget class="QPushButton" name="pbnPrev">
- <property name="text">
- <string>&Previous</string>
- </property>
- <property name="shortcut">
- <string comment="Ctrl+Up"/>
- </property>
- </widget>
- </item>
- <item row="3" column="4">
- <widget class="QPushButton" name="pbnExecute">
- <property name="text">
- <string>E&xecute</string>
- </property>
- </widget>
- </item>
- <item row="4" column="4">
- <widget class="QPushButton" name="pbnEval">
- <property name="text">
- <string>&Eval</string>
- </property>
- </widget>
- </item>
- <item row="5" column="4">
- <widget class="QPushButton" name="pbnNext">
- <property name="text">
- <string>&Next</string>
- </property>
- <property name="shortcut">
- <string/>
- </property>
- </widget>
- </item>
- <item row="2" column="1" rowspan="4" colspan="2">
- <widget class="QLabel" name="lblPrompt">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>>>></string>
- </property>
- </widget>
- </item>
- <item row="1" column="0" colspan="5">
- <widget class="QTextBrowser" name="txtHistory">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>3</verstretch>
- </sizepolicy>
- </property>
- <property name="html">
- <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
-<html><head><meta name="qrichtext" content="1" /><style type="text/css">
-p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
-<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"></p></body></html></string>
- </property>
- </widget>
- </item>
- <item row="2" column="3" rowspan="4">
- <widget class="QTextEdit" name="edtCmdLine">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>1</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <tabstops>
- <tabstop>txtHistory</tabstop>
- </tabstops>
- <resources/>
- <connections/>
-</ui>
More information about the QGIS-commit
mailing list