Skip to content

Commit fcee352

Browse files
jcfrjamesobutler
authored andcommitted
Add support for enabling stdout/stderr redirection
1 parent aacc05d commit fcee352

File tree

2 files changed

+85
-33
lines changed

2 files changed

+85
-33
lines changed

src/PythonQt.cpp

Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName)
135135
PythonQtRegisterToolClassesTemplateConverter(quint32);
136136
PythonQtRegisterToolClassesTemplateConverter(qint64);
137137
PythonQtRegisterToolClassesTemplateConverter(quint64);
138-
138+
139139
#ifdef PYTHONQT_SUPPORT_ML_TYPES
140140
PythonQtMethodInfo::addParameterTypeAlias("QList<MLfloat>", "QList<float>");
141141
PythonQtMethodInfo::addParameterTypeAlias("QVector<MLfloat>", "QVector<float>");
@@ -295,7 +295,7 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName)
295295
"QtFatalMsg",
296296
"QtSystemMsg"
297297
};
298-
298+
299299
for (auto i = 0u; i < sizeof(enumValues)/sizeof(int); i++) {
300300
PyObject* obj = PyInt_FromLong(enumValues[i]);
301301
if (PyModule_AddObject(pack, enumNames[i], obj) == 0) {
@@ -706,7 +706,7 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name, bool passO
706706
return (PyObject*)wrap;
707707
}
708708

709-
// not a known QObject, try to wrap via foreign wrapper factories
709+
// not a known QObject, try to wrap via foreign wrapper factories
710710
PyObject* foreignWrapper = nullptr;
711711
for (int i=0; i<_foreignWrapperFactories.size(); i++) {
712712
foreignWrapper = _foreignWrapperFactories.at(i)->wrap(name, ptr);
@@ -1097,7 +1097,7 @@ PythonQtObjectPtr PythonQt::parseFileWithPythonLoaders(const QString& file)
10971097
loader.setNewRef(callAndReturnPyObject(loaderClass, args));
10981098
QVariantList args2;
10991099
args2 << dummy;
1100-
PythonQtObjectPtr getCode;
1100+
PythonQtObjectPtr getCode;
11011101
getCode.setNewRef(PyObject_GetAttrString(loader, "get_code"));
11021102
result.setNewRef(callAndReturnPyObject(getCode, args2));
11031103
if (!result) {
@@ -1344,28 +1344,28 @@ PyObject* PythonQt::getObjectByType(const QString& typeName)
13441344
sys.setNewRef(PyImport_ImportModule("sys"));
13451345
PythonQtObjectPtr modules = lookupObject(sys, "modules");
13461346
Q_ASSERT(PyDict_Check(modules));
1347-
1347+
13481348
QStringList tmp = typeName.split(".");
13491349
QString simpleTypeName = tmp.takeLast();
13501350
QString moduleName = tmp.join(".");
1351-
1351+
13521352
PyObject* object = nullptr;
13531353
PyObject* moduleObject = PyDict_GetItemString(modules, QStringToPythonCharPointer(moduleName));
13541354
if (moduleObject) {
13551355
object = PyObject_GetAttrString(moduleObject, QStringToPythonCharPointer(simpleTypeName));
13561356
}
1357-
1357+
13581358
if (!object) {
13591359
moduleObject = PyDict_GetItemString(modules, "__builtin__");
13601360
if (moduleObject) {
13611361
object = PyObject_GetAttrString(moduleObject, QStringToPythonCharPointer(simpleTypeName));
13621362
}
13631363
}
1364-
1364+
13651365
PyErr_Clear();
13661366
return object;
13671367
}
1368-
1368+
13691369
QStringList PythonQt::introspectType(const QString& typeName, ObjectType type)
13701370
{
13711371
QStringList results;
@@ -1876,18 +1876,22 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ
18761876
PythonQtObjectPtr sys;
18771877
sys.setNewRef(PyImport_ImportModule("sys"));
18781878

1879-
if (redirectStdOut) {
1880-
PythonQtObjectPtr out;
1881-
PythonQtObjectPtr err;
1882-
// create a redirection object for stdout and stderr
1883-
out = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,nullptr, nullptr);
1884-
((PythonQtStdOutRedirect*)out.object())->_cb = stdOutRedirectCB;
1885-
err = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,nullptr, nullptr);
1886-
((PythonQtStdOutRedirect*)err.object())->_cb = stdErrRedirectCB;
1887-
// replace the built in file objects with our own objects
1888-
PyModule_AddObject(sys, "stdout", out);
1889-
PyModule_AddObject(sys, "stderr", err);
1890-
}
1879+
// Backup original 'sys.stdout' and 'sys.stderr'
1880+
PyModule_AddObject(sys, "pythonqt_original_stdout", PyObject_GetAttrString(sys, "stdout"));
1881+
PyModule_AddObject(sys, "pythonqt_original_stderr", PyObject_GetAttrString(sys, "stderr"));
1882+
1883+
// Create a redirection object for stdout and stderr
1884+
PythonQtObjectPtr out;
1885+
PythonQtObjectPtr err;
1886+
out = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,nullptr, nullptr);
1887+
((PythonQtStdOutRedirect*)out.object())->_cb = stdOutRedirectCB;
1888+
err = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,nullptr, nullptr);
1889+
((PythonQtStdOutRedirect*)err.object())->_cb = stdErrRedirectCB;
1890+
// replace the built in file objects with our own objects
1891+
PyModule_AddObject(sys, "pythonqt_stdout", out);
1892+
PyModule_AddObject(sys, "pythonqt_stderr", err);
1893+
1894+
setRedirectStdOutCallbackEnabled(redirectStdOut);
18911895

18921896
// add PythonQt to the list of builtin module names
18931897
PyObject *old_module_names = PyObject_GetAttrString(sys.object(),"builtin_module_names");
@@ -1909,12 +1913,48 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ
19091913
#endif
19101914
}
19111915

1916+
bool PythonQt::redirectStdOutCallbackEnabled() const
1917+
{
1918+
PythonQtObjectPtr sys;
1919+
sys.setNewRef(PyImport_ImportModule("sys"));
1920+
1921+
PythonQtObjectPtr pythonqt_stdout;
1922+
pythonqt_stdout.setNewRef(PyObject_GetAttrString(sys.object(), "pythonqt_stdout"));
1923+
1924+
PythonQtObjectPtr stdout;
1925+
stdout.setNewRef(PyObject_GetAttrString(sys.object(), "stdout"));
1926+
1927+
return PyObject_RichCompareBool(pythonqt_stdout.object(), stdout.object(), Py_EQ);
1928+
}
1929+
1930+
void PythonQt::setRedirectStdOutCallbackEnabled(bool enabled)
1931+
{
1932+
PythonQtObjectPtr sys;
1933+
sys.setNewRef(PyImport_ImportModule("sys"));
1934+
1935+
if (enabled) {
1936+
if( PyObject_HasAttrString(sys.object(), "pythonqt_stdout") ) {
1937+
PyModule_AddObject(sys.object(), "stdout", PyObject_GetAttrString(sys.object(), "pythonqt_stdout"));
1938+
}
1939+
if( PyObject_HasAttrString(sys.object(), "pythonqt_stderr") ) {
1940+
PyModule_AddObject(sys.object(), "stderr", PyObject_GetAttrString(sys.object(), "pythonqt_stderr"));
1941+
}
1942+
} else {
1943+
if( PyObject_HasAttrString(sys.object(), "pythonqt_original_stdout") ) {
1944+
PyModule_AddObject(sys.object(), "stdout", PyObject_GetAttrString(sys.object(), "pythonqt_original_stdout"));
1945+
}
1946+
if( PyObject_HasAttrString(sys.object(), "pythonqt_original_stderr") ) {
1947+
PyModule_AddObject(sys.object(), "stderr", PyObject_GetAttrString(sys.object(), "pythonqt_original_stderr"));
1948+
}
1949+
}
1950+
}
1951+
19121952
QString PythonQt::getReturnTypeOfWrappedMethod(PyObject* module, const QString& name)
19131953
{
19141954
QStringList tmp = name.split(".");
19151955
QString methodName = tmp.takeLast();
19161956
QString variableName = tmp.join(".");
1917-
PythonQtObjectPtr variableObject = lookupObject(module, variableName);
1957+
PythonQtObjectPtr variableObject = lookupObject(module, variableName);
19181958
if (variableObject.isNull()) {
19191959
// try lookup by interpreting the variableName as a type
19201960
QString type = getReturnTypeOfWrappedMethod(variableName, methodName);
@@ -1943,9 +1983,9 @@ QString PythonQt::getReturnTypeOfWrappedMethodHelper(const PythonQtObjectPtr& va
19431983
if (methodObject.isNull()) {
19441984
return "";
19451985
}
1946-
1986+
19471987
QString type;
1948-
1988+
19491989
if (PythonQtUtils::isPythonClassType(methodObject)) {
19501990
// the methodObject is not a method, but the name of a type/class. This means
19511991
// a constructor is called. Return the context.
@@ -2376,13 +2416,13 @@ void PythonQtPrivate::callMethodInPython(QMetaMethod &method, PythonQtInstanceWr
23762416
QString PythonQtPrivate::getSignature(PyObject* object)
23772417
{
23782418
QString signature;
2379-
2419+
23802420
if (object) {
23812421
PyMethodObject* method = nullptr;
23822422
PyFunctionObject* func = nullptr;
2383-
2423+
23842424
bool decrefMethod = false;
2385-
2425+
23862426
if (PythonQtUtils::isPythonClassType(object)) {
23872427
method = (PyMethodObject*)PyObject_GetAttrString(object, "__init__");
23882428
decrefMethod = true;
@@ -2418,7 +2458,7 @@ QString PythonQtPrivate::getSignature(PyObject* object)
24182458
}
24192459
}
24202460
}
2421-
2461+
24222462
if (func) {
24232463
QString funcName;
24242464
PyObject* s = PyObject_GetAttrString((PyObject*)func, "__name__");
@@ -2435,7 +2475,7 @@ QString PythonQtPrivate::getSignature(PyObject* object)
24352475
Py_DECREF(s);
24362476
}
24372477
}
2438-
2478+
24392479
QStringList arguments;
24402480
QStringList defaults;
24412481
QString varargs;
@@ -2465,7 +2505,7 @@ QString PythonQtPrivate::getSignature(PyObject* object)
24652505
}
24662506
Py_DECREF(co_varnames);
24672507
}
2468-
2508+
24692509
PyObject* defaultsTuple = func->func_defaults;
24702510
if (defaultsTuple) {
24712511
Q_ASSERT(PyTuple_Check(defaultsTuple));
@@ -2477,7 +2517,7 @@ QString PythonQtPrivate::getSignature(PyObject* object)
24772517
Py_DECREF(s);
24782518
}
24792519
}
2480-
2520+
24812521
int firstdefault = arguments.size() - defaults.size();
24822522
for (int i=0; i<arguments.size(); i++) {
24832523
if (!signature.isEmpty()) { signature += ", "; }
@@ -2498,12 +2538,12 @@ QString PythonQtPrivate::getSignature(PyObject* object)
24982538
}
24992539
signature = funcName + "(" + signature + ")";
25002540
}
2501-
2541+
25022542
if (method && decrefMethod) {
25032543
Py_DECREF(method);
25042544
}
25052545
}
2506-
2546+
25072547
return signature;
25082548
}
25092549

src/PythonQt.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,18 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
268268

269269
//@}
270270

271+
//---------------------------------------------------------------------------
272+
//! \name Standard output handling
273+
//@{
274+
275+
//! return \a true if std out/err redirection is enabled.
276+
bool redirectStdOutCallbackEnabled() const;
277+
278+
//! enable or disable std out/err redirection to pythonStdOut() and pythonStdErr() signals.
279+
void setRedirectStdOutCallbackEnabled(bool enabled);
280+
281+
//@}
282+
271283
//---------------------------------------------------------------------------
272284
//! \name Modules
273285
//@{

0 commit comments

Comments
 (0)