Skip to content

Commit e4c860e

Browse files
author
wonder
committed
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@12725 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent f905146 commit e4c860e

11 files changed

+197
-307
lines changed

python/CMakeLists.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ ELSE (BINDINGS_GLOBAL_INSTALL)
9999
ENDIF (BINDINGS_GLOBAL_INSTALL)
100100

101101

102+
# python console
103+
PYQT4_WRAP_UI(PYUI_FILE ${CMAKE_SOURCE_DIR}/src/ui/qgspythondialog.ui)
104+
105+
ADD_CUSTOM_TARGET(pythonconsole ALL DEPENDS ${PYUI_FILE})
106+
107+
102108
# Step 4: install built libs to python's site packages
103-
INSTALL(FILES __init__.py utils.py ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
109+
INSTALL(FILES __init__.py utils.py console.py ${PYUI_FILE} ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
104110

python/console.py

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from PyQt4.QtCore import *
4+
from PyQt4.QtGui import *
5+
6+
import traceback
7+
import sys
8+
9+
from ui_qgspythondialog import Ui_QgsPythonDialog
10+
11+
_console = None
12+
13+
14+
def show_console():
15+
""" called from QGIS to open the console """
16+
global _console
17+
if _console is None:
18+
_console = QgsPythonConsole()
19+
_console.show()
20+
_console.raise_()
21+
_console.setWindowState( _console.windowState() & ~Qt.WindowMinimized )
22+
_console.activateWindow()
23+
24+
25+
26+
_old_stdout = sys.stdout
27+
28+
class QgisOutputCatcher:
29+
def __init__(self):
30+
self.data = ''
31+
def write(self, stuff):
32+
self.data += stuff
33+
def get_and_clean_data(self):
34+
tmp = self.data
35+
self.data = ''
36+
return tmp
37+
def flush(self):
38+
pass
39+
40+
def installConsoleHook():
41+
sys.stdout = QgisOutputCatcher()
42+
43+
def uninstallConsoleHook():
44+
sys.stdout = _old_stdout
45+
46+
47+
48+
class ConsoleHistory(object):
49+
def __init__(self):
50+
self.cmds = []
51+
self.pos = 0
52+
self.active_cmd = u"" # active, not yet entered command
53+
54+
def add_cmd(self, command):
55+
if len(command) != 0:
56+
self.cmds.append(command)
57+
self.pos = len(self.cmds)
58+
self.active_cmd = u""
59+
60+
def get_previous_cmd(self, current):
61+
if self.pos == 0:
62+
return None
63+
64+
if self.pos == len(self.cmds):
65+
self.active_cmd = current
66+
else:
67+
self.cmds[self.pos] = current
68+
69+
self.pos -= 1
70+
return self.cmds[self.pos]
71+
72+
def get_next_cmd(self, current):
73+
74+
# if we're at active (last) command, don't move
75+
if self.pos == len(self.cmds):
76+
return None
77+
78+
self.cmds[self.pos] = current
79+
self.pos += 1
80+
81+
if self.pos == len(self.cmds):
82+
return self.active_cmd
83+
else:
84+
return self.cmds[self.pos]
85+
86+
87+
88+
class QgsPythonConsole(QWidget, Ui_QgsPythonDialog):
89+
def __init__(self, parent=None):
90+
QWidget.__init__(self, parent)
91+
92+
self.setupUi(self)
93+
94+
# minimize button was not enabled on mac
95+
self.setWindowFlags( self.windowFlags() | Qt.WindowMinimizeButtonHint )
96+
97+
self.history = ConsoleHistory()
98+
99+
self.console_globals = {}
100+
101+
def escapeHtml(self, text):
102+
return text.replace("<", "&lt;").replace(">", "&gt;")
103+
104+
@pyqtSlot()
105+
def on_pbnPrev_clicked(self):
106+
cmd = self.history.get_previous_cmd( self.getCommand() )
107+
if cmd is not None:
108+
self.edtCmdLine.setText(cmd)
109+
110+
@pyqtSlot()
111+
def on_pbnNext_clicked(self):
112+
cmd = self.history.get_next_cmd( self.getCommand() )
113+
if cmd is not None:
114+
self.edtCmdLine.setText(cmd)
115+
116+
def getCommand(self):
117+
return unicode(self.edtCmdLine.toPlainText())
118+
119+
def execute(self, single):
120+
command = self.getCommand()
121+
122+
self.history.add_cmd(command)
123+
124+
try:
125+
# run command
126+
code = compile(command, '<console>', 'single' if single else 'exec')
127+
res = eval(code, self.console_globals)
128+
result = unicode( res ) if res is not None else u''
129+
130+
# get standard output
131+
output = sys.stdout.get_and_clean_data()
132+
133+
#_old_stdout.write("cmd: '"+command+"'\n")
134+
#_old_stdout.write("res: '"+result+"'\n")
135+
#_old_stdout.write("out: '"+output+"'\n")
136+
#_old_stdout.flush()
137+
138+
# escape the result so python objects display properly and
139+
# we can still use html output to get nicely formatted display
140+
output = self.escapeHtml( output ) + self.escapeHtml( result )
141+
if len(output) != 0:
142+
output += "<br>"
143+
144+
except Exception, e:
145+
#lst = traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)
146+
lst = traceback.format_exception_only(sys.exc_type, sys.exc_value)
147+
errorText = "<br>".join(map(self.escapeHtml, lst))
148+
output = "<font color=\"red\">" + errorText + "</font><br>"
149+
150+
s = "<b><font color=\"green\">&gt;&gt;&gt;</font> " + self.escapeHtml( command ) + "</b><br>" + output
151+
self.edtCmdLine.setText("")
152+
153+
self.txtHistory.moveCursor(QTextCursor.End)
154+
self.txtHistory.insertHtml(s)
155+
self.txtHistory.moveCursor(QTextCursor.End)
156+
self.txtHistory.ensureCursorVisible()
157+
158+
@pyqtSlot()
159+
def on_pbnExecute_clicked(self):
160+
self.execute(False)
161+
162+
@pyqtSlot()
163+
def on_pbnEval_clicked(self):
164+
self.execute(True)
165+
166+
def showEvent(self, event):
167+
QWidget.showEvent(self, event)
168+
installConsoleHook()
169+
170+
def closeEvent(self, event):
171+
uninstallConsoleHook()
172+
QWidget.closeEvent(self, event)
173+
174+
175+
if __name__ == '__main__':
176+
import sys
177+
a = QApplication(sys.argv)
178+
w = QgsPythonConsole()
179+
w.show()
180+
a.exec_()

python/utils.py

-34
Original file line numberDiff line numberDiff line change
@@ -55,40 +55,6 @@ def initInterface(pointer):
5555
iface = wrapinstance(pointer, QgisInterface)
5656

5757

58-
#######################
59-
# CONSOLE OUTPUT
60-
61-
old_stdout = sys.stdout
62-
console_output = None
63-
64-
# hook for python console so all output will be redirected
65-
# and then shown in console
66-
def console_displayhook(obj):
67-
global console_output
68-
console_output = obj
69-
70-
class QgisOutputCatcher:
71-
def __init__(self):
72-
self.data = ''
73-
def write(self, stuff):
74-
self.data += stuff
75-
def get_and_clean_data(self):
76-
tmp = self.data
77-
self.data = ''
78-
return tmp
79-
def flush(self):
80-
pass
81-
82-
83-
def installConsoleHooks():
84-
sys.displayhook = console_displayhook
85-
sys.stdout = QgisOutputCatcher()
86-
87-
def uninstallConsoleHooks():
88-
sys.displayhook = sys.__displayhook__
89-
sys.stdout = old_stdout
90-
91-
9258
#######################
9359
# PLUGINS
9460

src/app/CMakeLists.txt

-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ SET(QGIS_APP_SRCS
6060
qgspluginmanager.cpp
6161
qgspluginmetadata.cpp
6262
qgspluginregistry.cpp
63-
qgspythondialog.cpp
6463
qgsprojectproperties.cpp
6564
qgsrasterlayerproperties.cpp
6665
qgssearchquerybuilder.cpp
@@ -162,7 +161,6 @@ SET (QGIS_APP_MOC_HDRS
162161
qgsoptions.h
163162
qgspastetransformations.h
164163
qgspluginmanager.h
165-
qgspythondialog.h
166164
qgsprojectproperties.h
167165
qgsrasterlayerproperties.h
168166
qgssearchquerybuilder.h

src/app/qgisapp.cpp

+10-9
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@
197197
#include "qgsspatialitesourceselect.h"
198198
#endif
199199

200-
#include "qgspythondialog.h"
201200
#include "qgspythonutils.h"
202201

203202
#ifndef WIN32
@@ -329,7 +328,6 @@ QgisApp *QgisApp::smInstance = 0;
329328
QgisApp::QgisApp( QSplashScreen *splash, QWidget * parent, Qt::WFlags fl )
330329
: QMainWindow( parent, fl ),
331330
mSplash( splash ),
332-
mPythonConsole( NULL ),
333331
mPythonUtils( NULL )
334332
#ifdef HAVE_QWT
335333
,
@@ -507,7 +505,6 @@ QgisApp::~QgisApp()
507505
delete mMapTools.mAddIsland;
508506
delete mMapTools.mNodeTool;
509507

510-
delete mPythonConsole;
511508
delete mPythonUtils;
512509

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

1113-
if ( mPythonConsole == NULL )
1114-
mPythonConsole = new QgsPythonDialog( mQgisInterface, mPythonUtils );
1115-
mPythonConsole->show();
1116-
mPythonConsole->raise();
1117-
mPythonConsole->setWindowState( mPythonConsole->windowState() & ~Qt::WindowMinimized );
1118-
mPythonConsole->activateWindow();
1110+
bool res = mPythonUtils->runStringUnsafe(
1111+
"import qgis.console\n"
1112+
"qgis.console.show_console()\n", false );
1113+
1114+
if ( !res )
1115+
{
1116+
QString className, text;
1117+
mPythonUtils->getError( className, text );
1118+
QMessageBox::critical( this, tr( "Error" ), tr( "Failed to open Python console:" ) + "\n" + className + ": " + text );
1119+
}
11191120
}
11201121

11211122
void QgisApp::createActionGroups()

src/app/qgisapp.h

-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ class QgsMapTip;
5353
class QgsMapTool;
5454
class QgsPoint;
5555
class QgsProviderRegistry;
56-
class QgsPythonDialog;
5756
class QgsPythonUtils;
5857
class QgsRasterLayer;
5958
class QgsRectangle;
@@ -1045,7 +1044,6 @@ class QgisApp : public QMainWindow
10451044
//!flag to indicate that the previous screen mode was 'maximised'
10461045
bool mPrevScreenModeMaximized;
10471046

1048-
QgsPythonDialog* mPythonConsole;
10491047
QgsPythonUtils* mPythonUtils;
10501048

10511049
static QgisApp *smInstance;

0 commit comments

Comments
 (0)