Skip to content

Commit 3102201

Browse files
author
wonder
committed
- improved error handling when loading python plugins
- redirection of output to python console, i.e. print command works git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@6767 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 73f4e75 commit 3102201

File tree

5 files changed

+85
-35
lines changed

5 files changed

+85
-35
lines changed

src/app/qgisapp.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,6 +1531,7 @@ void QgisApp::restoreSessionPlugins(QString thePluginDirString)
15311531
}
15321532

15331533
#ifdef HAVE_PYTHON
1534+
QString pluginName, description, version;
15341535

15351536
if (QgsPythonUtils::isEnabled())
15361537
{
@@ -1544,12 +1545,18 @@ void QgisApp::restoreSessionPlugins(QString thePluginDirString)
15441545
QString packageName = pluginDir[i];
15451546

15461547
// import plugin's package
1547-
QgsPythonUtils::loadPlugin(packageName);
1548+
if (!QgsPythonUtils::loadPlugin(packageName))
1549+
continue;
15481550

15491551
// get information from the plugin
1550-
QString pluginName = QgsPythonUtils::getPluginMetadata(packageName, "name");
1551-
QString description = QgsPythonUtils::getPluginMetadata(packageName, "description");
1552-
QString version = QgsPythonUtils::getPluginMetadata(packageName, "version");
1552+
// if there are some problems, don't continue with metadata retreival
1553+
pluginName = QgsPythonUtils::getPluginMetadata(packageName, "name");
1554+
if (pluginName != "__error__")
1555+
{
1556+
description = QgsPythonUtils::getPluginMetadata(packageName, "description");
1557+
if (description != "__error__")
1558+
version = QgsPythonUtils::getPluginMetadata(packageName, "version");
1559+
}
15531560

15541561
if (pluginName == "__error__" || description == "__error__" || version == "__error__")
15551562
{

src/app/qgspythondialog.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
#include "qgspythondialog.h"
1818
#include "qgspythonutils.h"
1919

20-
#include <iostream> // %%%
21-
2220

2321
QgsPythonDialog::QgsPythonDialog(QgisInterface* pIface, QWidget *parent)
2422
: QDialog(parent)
@@ -43,7 +41,10 @@ void QgsPythonDialog::on_edtCmdLine_returnPressed()
4341
// we're using custom hooks for output and exceptions to show output in console
4442
if (QgsPythonUtils::runString(command))
4543
{
46-
output = QgsPythonUtils::getResult();
44+
QgsPythonUtils::evalString("sys.stdout.data", output);
45+
QgsPythonUtils::runString("sys.stdout.data = ''");
46+
47+
output += QgsPythonUtils::getResult();
4748

4849
if (!output.isEmpty())
4950
output += "<br>";

src/app/qgspythonutils.cpp

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
#include "qgsapplication.h"
2424

25-
2625
#include <QMessageBox>
2726

2827
QString QgsPythonUtils::mPluginsPath;
@@ -84,9 +83,10 @@ void QgsPythonUtils::initPython(QgisInterface* interface)
8483

8584
// hook that will show information and traceback in message box
8685
runString(
87-
"def qgis_except_hook(type, value, tb):\n"
86+
"def qgis_except_hook_msg(type, value, tb, msg):\n"
8887
" lst = traceback.format_exception(type, value, tb)\n"
89-
" str = '<font color=\"red\">An error has occured while executing Python code:</font><br><br>'\n"
88+
" if msg == None: msg = 'An error has occured while executing Python code:'\n"
89+
" str = '<font color=\"red\">'+msg+'</font><br><br>'\n"
9090
" for s in lst:\n"
9191
" str += s\n"
9292
" str = str.replace('\\n', '<br>')\n"
@@ -96,6 +96,9 @@ void QgsPythonUtils::initPython(QgisInterface* interface)
9696
" msg.setTitle('Error')\n"
9797
" msg.setMessage(str, QgsMessageOutput.MessageHtml)\n"
9898
" msg.showMessage()\n");
99+
runString(
100+
"def qgis_except_hook(type, value, tb):\n"
101+
" qgis_except_hook_msg(type, value, tb, None)\n");
99102

100103
// hook for python console so all output will be redirected
101104
// and then shown in console
@@ -132,17 +135,31 @@ void QgsPythonUtils::installErrorHook()
132135

133136
void QgsPythonUtils::installConsoleHooks()
134137
{
135-
runString("sys.displayhook = console_display_hook");
138+
runString("sys.displayhook = console_display_hook\n");
139+
140+
runString(
141+
"class MyOutputCatcher:\n"
142+
" def __init__(self):\n"
143+
" self.data = ''\n"
144+
" def write(self, stuff):\n"
145+
" self.data += stuff\n");
146+
runString("_old_stdout = sys.stdout\n");
147+
runString("sys.stdout = MyOutputCatcher()\n");
148+
136149
}
137150

138151
void QgsPythonUtils::uninstallConsoleHooks()
139152
{
140153
runString("sys.displayhook = sys.__displayhook__");
154+
runString("sys.stdout = _old_stdout");
155+
156+
// TODO: uninstalling stdout redirection doesn't work
157+
141158
//installErrorHook();
142159
}
143160

144161

145-
bool QgsPythonUtils::runString(QString command)
162+
bool QgsPythonUtils::runString(const QString& command)
146163
{
147164
PyRun_String(command.toLocal8Bit().data(), Py_single_input, mMainDict, mMainDict);
148165

@@ -183,15 +200,20 @@ bool QgsPythonUtils::getError(QString& errorClassName, QString& errorText)
183200
}
184201

185202
QString QgsPythonUtils::getResult()
203+
{
204+
return getVariableFromMain("__result");
205+
}
206+
207+
QString QgsPythonUtils::getVariableFromMain(QString name)
186208
{
187209
PyObject* obj;
188210
PyObject* obj_str;
189211

190212
QString output;
191213

192214
// get the result
193-
obj = PyDict_GetItemString(mMainDict, "__result"); // obj is borrowed reference
194-
215+
obj = PyDict_GetItemString(mMainDict, name); // obj is borrowed reference
216+
195217
if (obj != NULL && obj != Py_None)
196218
{
197219
obj_str = PyObject_Str(obj); // obj_str is new reference
@@ -203,11 +225,25 @@ QString QgsPythonUtils::getResult()
203225
}
204226

205227
// erase result
206-
PyDict_SetItemString(mMainDict, "__result", Py_None);
207-
228+
PyDict_SetItemString(mMainDict, name, Py_None);
229+
208230
return output;
209231
}
210232

233+
bool QgsPythonUtils::evalString(const QString& command, QString& result)
234+
{
235+
PyObject* res = PyRun_String(command.toLocal8Bit().data(), Py_eval_input, mMainDict, mMainDict);
236+
237+
if (res != NULL && PyString_Check(res))
238+
{
239+
result = PyString_AsString(res);
240+
Py_XDECREF(res);
241+
return true;
242+
}
243+
Py_XDECREF(res);
244+
return false;
245+
}
246+
211247

212248
QString QgsPythonUtils::pythonPath()
213249
{
@@ -248,19 +284,17 @@ QString QgsPythonUtils::getPluginMetadata(QString pluginName, QString function)
248284
bool QgsPythonUtils::loadPlugin(QString packageName)
249285
{
250286
// load plugin's package and ensure that plugin is reloaded when changed
251-
if (!runString("import " + packageName) ||
252-
!runString("reload(" + packageName + ")"))
253-
{
254-
QString className, errorText;
255-
getError(className, errorText);
256-
QString str = className + ": " + errorText;
257-
258-
QMessageBox::warning(0, QObject::tr("Python error"),
259-
QObject::tr("Couldn't load plugin due this error:\n") + str);
260-
return false;
261-
}
262-
263-
return true;
287+
runString(
288+
"try:\n"
289+
" import " + packageName + "\n"
290+
" reload(" + packageName + ")\n"
291+
" __main__.__plugin_result = 'OK'\n"
292+
"except:\n"
293+
" qgis_except_hook_msg(sys.exc_type, sys.exc_value, sys.exc_traceback, "
294+
" 'Couldn\\'t load plugin \"" + packageName + "\"')\n"
295+
" __main__.__plugin_result = 'ERROR'\n");
296+
297+
return (getVariableFromMain("__plugin_result") == "OK");
264298
}
265299

266300

src/app/qgspythonutils.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,17 @@ class QgsPythonUtils
5353
//! run a statement (wrapper for PyRun_String)
5454
//! this command is more advanced as enables error checking etc.
5555
//! @return true if no error occured
56-
static bool runString(QString command);
56+
static bool runString(const QString& command);
57+
58+
static bool evalString(const QString& command, QString& result);
5759

5860
//! get information about error to the supplied arguments
5961
//! @return false if there was no python error
6062
static bool getError(QString& errorClassName, QString& errorText);
6163

64+
//! get variable from main dictionary
65+
static QString getVariableFromMain(QString name);
66+
6267
/* python console related functions */
6368

6469
//! change displayhook and excepthook

src/ui/qgspythondialog.ui

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
<ui version="4.0" >
2-
<author></author>
3-
<comment></comment>
4-
<exportmacro></exportmacro>
52
<class>QgsPythonDialog</class>
63
<widget class="QDialog" name="QgsPythonDialog" >
74
<property name="geometry" >
@@ -47,7 +44,10 @@ p, li { white-space: pre-wrap; }
4744
<item>
4845
<widget class="QTextBrowser" name="txtHistory" >
4946
<property name="html" >
50-
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;/head>&lt;body style=" white-space: pre-wrap; font-family:DejaVu Sans Condensed; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;">&lt;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;">&lt;/p>&lt;/body>&lt;/html></string>
47+
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
48+
p, li { white-space: pre-wrap; }
49+
&lt;/style>&lt;/head>&lt;body style=" font-family:'DejaVu Sans Condensed'; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
50+
&lt;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-size:10pt;">&lt;/p>&lt;/body>&lt;/html></string>
5151
</property>
5252
</widget>
5353
</item>
@@ -73,7 +73,10 @@ p, li { white-space: pre-wrap; }
7373
</item>
7474
</layout>
7575
</widget>
76-
<pixmapfunction></pixmapfunction>
76+
<tabstops>
77+
<tabstop>edtCmdLine</tabstop>
78+
<tabstop>txtHistory</tabstop>
79+
</tabstops>
7780
<resources/>
7881
<connections/>
7982
</ui>

0 commit comments

Comments
 (0)