Skip to content
Permalink
Browse files

Converted Python console from C++ to Python.

This cleans python support a bit and makes a starting point for further improvements of the console.

It's possible to run the console also outside QGIS:

import qgis.console
qgis.console.show_console()



git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@12725 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
wonder
wonder committed Jan 10, 2010
1 parent 3f79cba commit 342940bab4ee695d53b371cc8b478827942ae7af
@@ -99,6 +99,12 @@ ELSE (BINDINGS_GLOBAL_INSTALL)
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 ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
INSTALL(FILES __init__.py utils.py console.py ${PYUI_FILE} ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)

@@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-

from PyQt4.QtCore import *
from PyQt4.QtGui import *

import traceback
import sys

from ui_qgspythondialog import Ui_QgsPythonDialog

_console = None


def show_console():
""" called from QGIS to open the console """
global _console
if _console is None:
_console = QgsPythonConsole()
_console.show()
_console.raise_()
_console.setWindowState( _console.windowState() & ~Qt.WindowMinimized )
_console.activateWindow()



_old_stdout = sys.stdout

class QgisOutputCatcher:
def __init__(self):
self.data = ''
def write(self, stuff):
self.data += stuff
def get_and_clean_data(self):
tmp = self.data
self.data = ''
return tmp
def flush(self):
pass

def installConsoleHook():
sys.stdout = QgisOutputCatcher()

def uninstallConsoleHook():
sys.stdout = _old_stdout



class ConsoleHistory(object):
def __init__(self):
self.cmds = []
self.pos = 0
self.active_cmd = u"" # active, not yet entered command

def add_cmd(self, command):
if len(command) != 0:
self.cmds.append(command)
self.pos = len(self.cmds)
self.active_cmd = u""

def get_previous_cmd(self, current):
if self.pos == 0:
return None

if self.pos == len(self.cmds):
self.active_cmd = current
else:
self.cmds[self.pos] = current

self.pos -= 1
return self.cmds[self.pos]

def get_next_cmd(self, current):

# 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

if self.pos == len(self.cmds):
return self.active_cmd
else:
return self.cmds[self.pos]



class QgsPythonConsole(QWidget, Ui_QgsPythonDialog):
def __init__(self, parent=None):
QWidget.__init__(self, parent)

self.setupUi(self)

# minimize button was not enabled on mac
self.setWindowFlags( self.windowFlags() | Qt.WindowMinimizeButtonHint )

self.history = ConsoleHistory()

self.console_globals = {}

def escapeHtml(self, text):
return text.replace("<", "&lt;").replace(">", "&gt;")

@pyqtSlot()
def on_pbnPrev_clicked(self):
cmd = self.history.get_previous_cmd( self.getCommand() )
if cmd is not None:
self.edtCmdLine.setText(cmd)

@pyqtSlot()
def on_pbnNext_clicked(self):
cmd = self.history.get_next_cmd( self.getCommand() )
if cmd is not None:
self.edtCmdLine.setText(cmd)

def getCommand(self):
return unicode(self.edtCmdLine.toPlainText())

def execute(self, single):
command = self.getCommand()

self.history.add_cmd(command)

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()

#_old_stdout.write("cmd: '"+command+"'\n")
#_old_stdout.write("res: '"+result+"'\n")
#_old_stdout.write("out: '"+output+"'\n")
#_old_stdout.flush()

# 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>"

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>"

s = "<b><font color=\"green\">&gt;&gt;&gt;</font> " + self.escapeHtml( command ) + "</b><br>" + output
self.edtCmdLine.setText("")

self.txtHistory.moveCursor(QTextCursor.End)
self.txtHistory.insertHtml(s)
self.txtHistory.moveCursor(QTextCursor.End)
self.txtHistory.ensureCursorVisible()

@pyqtSlot()
def on_pbnExecute_clicked(self):
self.execute(False)

@pyqtSlot()
def on_pbnEval_clicked(self):
self.execute(True)

def showEvent(self, event):
QWidget.showEvent(self, event)
installConsoleHook()

def closeEvent(self, event):
uninstallConsoleHook()
QWidget.closeEvent(self, event)


if __name__ == '__main__':
import sys
a = QApplication(sys.argv)
w = QgsPythonConsole()
w.show()
a.exec_()
@@ -55,40 +55,6 @@ def initInterface(pointer):
iface = wrapinstance(pointer, QgisInterface)


#######################
# CONSOLE OUTPUT

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 = ''
def write(self, stuff):
self.data += stuff
def get_and_clean_data(self):
tmp = self.data
self.data = ''
return tmp
def flush(self):
pass


def installConsoleHooks():
sys.displayhook = console_displayhook
sys.stdout = QgisOutputCatcher()

def uninstallConsoleHooks():
sys.displayhook = sys.__displayhook__
sys.stdout = old_stdout


#######################
# PLUGINS

@@ -60,7 +60,6 @@ SET(QGIS_APP_SRCS
qgspluginmanager.cpp
qgspluginmetadata.cpp
qgspluginregistry.cpp
qgspythondialog.cpp
qgsprojectproperties.cpp
qgsrasterlayerproperties.cpp
qgssearchquerybuilder.cpp
@@ -162,7 +161,6 @@ SET (QGIS_APP_MOC_HDRS
qgsoptions.h
qgspastetransformations.h
qgspluginmanager.h
qgspythondialog.h
qgsprojectproperties.h
qgsrasterlayerproperties.h
qgssearchquerybuilder.h
@@ -197,7 +197,6 @@
#include "qgsspatialitesourceselect.h"
#endif

#include "qgspythondialog.h"
#include "qgspythonutils.h"

#ifndef WIN32
@@ -329,7 +328,6 @@ QgisApp *QgisApp::smInstance = 0;
QgisApp::QgisApp( QSplashScreen *splash, QWidget * parent, Qt::WFlags fl )
: QMainWindow( parent, fl ),
mSplash( splash ),
mPythonConsole( NULL ),
mPythonUtils( NULL )
#ifdef HAVE_QWT
,
@@ -507,7 +505,6 @@ QgisApp::~QgisApp()
delete mMapTools.mAddIsland;
delete mMapTools.mNodeTool;

delete mPythonConsole;
delete mPythonUtils;

deletePrintComposers();
@@ -1110,12 +1107,16 @@ void QgisApp::showPythonDialog()
if ( !mPythonUtils || !mPythonUtils->isEnabled() )
return;

if ( mPythonConsole == NULL )
mPythonConsole = new QgsPythonDialog( mQgisInterface, mPythonUtils );
mPythonConsole->show();
mPythonConsole->raise();
mPythonConsole->setWindowState( mPythonConsole->windowState() & ~Qt::WindowMinimized );
mPythonConsole->activateWindow();
bool res = mPythonUtils->runStringUnsafe(
"import qgis.console\n"
"qgis.console.show_console()\n", false );

if ( !res )
{
QString className, text;
mPythonUtils->getError( className, text );
QMessageBox::critical( this, tr( "Error" ), tr( "Failed to open Python console:" ) + "\n" + className + ": " + text );
}
}

void QgisApp::createActionGroups()
@@ -53,7 +53,6 @@ class QgsMapTip;
class QgsMapTool;
class QgsPoint;
class QgsProviderRegistry;
class QgsPythonDialog;
class QgsPythonUtils;
class QgsRasterLayer;
class QgsRectangle;
@@ -1045,7 +1044,6 @@ class QgisApp : public QMainWindow
//!flag to indicate that the previous screen mode was 'maximised'
bool mPrevScreenModeMaximized;

QgsPythonDialog* mPythonConsole;
QgsPythonUtils* mPythonUtils;

static QgisApp *smInstance;

0 comments on commit 342940b

Please sign in to comment.
You can’t perform that action at this time.