Skip to content

Commit 9c466f2

Browse files
committed
ENH: Stdin can optionally be redirected to a custom callback
Since the original 'stdin' is backed up, it's also possible to enable/disable the custom callback using setRedirectStdInCallBackEnabled
1 parent 5f4745d commit 9c466f2

File tree

5 files changed

+218
-0
lines changed

5 files changed

+218
-0
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ set(sources
110110
src/PythonQtSignalReceiver.cpp
111111
src/PythonQtSlot.cpp
112112
src/PythonQtStdDecorators.cpp
113+
src/PythonQtStdIn.cpp
113114
src/PythonQtStdOut.cpp
114115
src/gui/PythonQtScriptingConsole.cpp
115116

@@ -141,6 +142,7 @@ set(headers
141142
src/PythonQtSignalReceiver.h
142143
src/PythonQtSlot.h
143144
src/PythonQtStdDecorators.h
145+
src/PythonQtStdIn.h
144146
src/PythonQtStdOut.h
145147
src/PythonQtSystem.h
146148
src/PythonQtVariants.h

src/PythonQt.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "PythonQtMethodInfo.h"
4646
#include "PythonQtSignalReceiver.h"
4747
#include "PythonQtConversion.h"
48+
#include "PythonQtStdIn.h"
4849
#include "PythonQtStdOut.h"
4950
#include "PythonQtCppWrapperFactory.h"
5051
#include "PythonQtVariants.h"
@@ -192,6 +193,12 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName)
192193
}
193194
Py_INCREF(&PythonQtStdOutRedirectType);
194195

196+
// add our own python object types for redirection of stdin
197+
if (PyType_Ready(&PythonQtStdInRedirectType) < 0) {
198+
std::cerr << "could not initialize PythonQtStdInRedirectType" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
199+
}
200+
Py_INCREF(&PythonQtStdInRedirectType);
201+
195202
initPythonQtModule(flags & RedirectStdOut, pythonQtModuleName);
196203

197204
_p->setupSharedLibrarySuffixes();
@@ -220,6 +227,46 @@ PythonQtPrivate::~PythonQtPrivate() {
220227
PythonQtMethodInfo::cleanupCachedMethodInfos();
221228
}
222229

230+
void PythonQt::setRedirectStdInCallBack(PythonQtInputChangedCB* callback, void * callbackData)
231+
{
232+
if (!callback)
233+
{
234+
std::cerr << "PythonQt::setRedirectStdInCallBack - callback parameter is NULL !" << std::endl;
235+
return;
236+
}
237+
238+
PythonQtObjectPtr sys;
239+
PythonQtObjectPtr in;
240+
sys.setNewRef(PyImport_ImportModule("sys"));
241+
242+
// Backup original 'sys.stdin' if not yet done
243+
PyRun_SimpleString("if not hasattr(sys, 'pythonqt_original_stdin'):"
244+
"sys.pythonqt_original_stdin = sys.stdin");
245+
246+
in = PythonQtStdInRedirectType.tp_new(&PythonQtStdInRedirectType, NULL, NULL);
247+
((PythonQtStdInRedirect*)in.object())->_cb = callback;
248+
((PythonQtStdInRedirect*)in.object())->_callData = callbackData;
249+
// replace the built in file objects with our own objects
250+
PyModule_AddObject(sys, "stdin", in);
251+
252+
// Backup custom 'stdin' into 'pythonqt_stdin'
253+
PyRun_SimpleString("sys.pythonqt_stdin = sys.stdin");
254+
}
255+
256+
void PythonQt::setRedirectStdInCallBackEnabled(bool enabled)
257+
{
258+
if (enabled)
259+
{
260+
PyRun_SimpleString("if hasattr(sys, 'pythonqt_stdin'):"
261+
"sys.stdin = sys.pythonqt_stdin");
262+
}
263+
else
264+
{
265+
PyRun_SimpleString("if hasattr(sys,'pythonqt_original_stdin'):"
266+
"sys.stdin = sys.pythonqt_original_stdin");
267+
}
268+
}
269+
223270
PythonQtImportFileInterface* PythonQt::importInterface()
224271
{
225272
return _self->_p->_importInterface?_self->_p->_importInterface:_self->_p->_defaultImporter;

src/PythonQt.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "PythonQtClassWrapper.h"
4848
#include "PythonQtSlot.h"
4949
#include "PythonQtObjectPtr.h"
50+
#include "PythonQtStdIn.h"
5051
#include <QObject>
5152
#include <QVariant>
5253
#include <QList>
@@ -173,6 +174,21 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
173174
CallOverloads
174175
};
175176

177+
178+
//---------------------------------------------------------------------------
179+
//! \name Standard input handling
180+
//@{
181+
182+
//! Overwrite default handling of stdin using a custom callback. It internally backup
183+
//! the original 'sys.stdin' into 'sys.pythonqt_original_stdin'
184+
void setRedirectStdInCallBack(PythonQtInputChangedCB* callback, void * callbackData = 0);
185+
186+
//! Enable or disable stdin custom callback. It resets 'sys.stdin' using either 'sys.pythonqt_stdin'
187+
//! or 'sys.pythonqt_original_stdin'
188+
void setRedirectStdInCallBackEnabled(bool enabled);
189+
190+
//@}
191+
176192
//---------------------------------------------------------------------------
177193
//! \name Modules
178194
//@{

src/PythonQtStdIn.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*=========================================================================
2+
3+
Library: CTK
4+
5+
Copyright (c) Kitware Inc.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.commontk.org/LICENSE
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
=========================================================================*/
20+
21+
//----------------------------------------------------------------------------------
22+
/*!
23+
// \file PythonQtStdIn.cpp
24+
// \author Jean-Christophe Fillion-Robin
25+
// \author Last changed by $Author: jcfr $
26+
// \date 2011
27+
*/
28+
//----------------------------------------------------------------------------------
29+
30+
#include "PythonQtStdIn.h"
31+
32+
static PyObject *PythonQtStdInRedirect_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/)
33+
{
34+
PythonQtStdInRedirect *self;
35+
self = (PythonQtStdInRedirect *)type->tp_alloc(type, 0);
36+
self->_cb = NULL;
37+
self->_callData = NULL;
38+
39+
return (PyObject *)self;
40+
}
41+
42+
static PyObject *PythonQtStdInRedirect_readline(PyObject * self, PyObject * args)
43+
{
44+
PythonQtStdInRedirect* s = (PythonQtStdInRedirect*)self;
45+
QString string;
46+
if (s->_cb) {
47+
string = (*s->_cb)(s->_callData);
48+
}
49+
return Py_BuildValue(const_cast<char*>("s"), const_cast<char*>(string.toAscii().data()));
50+
}
51+
52+
static PyMethodDef PythonQtStdInRedirect_methods[] = {
53+
{"readline", (PyCFunction)PythonQtStdInRedirect_readline, METH_VARARGS,
54+
"read input line"},
55+
{NULL, NULL, 0 , NULL} /* sentinel */
56+
};
57+
58+
static PyMemberDef PythonQtStdInRedirect_members[] = {
59+
{NULL} /* Sentinel */
60+
};
61+
62+
PyTypeObject PythonQtStdInRedirectType = {
63+
PyObject_HEAD_INIT(NULL)
64+
0, /*ob_size*/
65+
"PythonQtStdInRedirect", /*tp_name*/
66+
sizeof(PythonQtStdInRedirect), /*tp_basicsize*/
67+
0, /*tp_itemsize*/
68+
0, /*tp_dealloc*/
69+
0, /*tp_print*/
70+
0, /*tp_getattr*/
71+
0, /*tp_setattr*/
72+
0, /*tp_compare*/
73+
0, /*tp_repr*/
74+
0, /*tp_as_number*/
75+
0, /*tp_as_sequence*/
76+
0, /*tp_as_mapping*/
77+
0, /*tp_hash */
78+
0, /*tp_call*/
79+
0, /*tp_str*/
80+
0, /*tp_getattro*/
81+
0, /*tp_setattro*/
82+
0, /*tp_as_buffer*/
83+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
84+
"PythonQtStdInRedirect", /* tp_doc */
85+
0, /* tp_traverse */
86+
0, /* tp_clear */
87+
0, /* tp_richcompare */
88+
0, /* tp_weaklistoffset */
89+
0, /* tp_iter */
90+
0, /* tp_iternext */
91+
PythonQtStdInRedirect_methods, /* tp_methods */
92+
PythonQtStdInRedirect_members, /* tp_members */
93+
0, /* tp_getset */
94+
0, /* tp_base */
95+
0, /* tp_dict */
96+
0, /* tp_descr_get */
97+
0, /* tp_descr_set */
98+
0, /* tp_dictoffset */
99+
0, /* tp_init */
100+
0, /* tp_alloc */
101+
PythonQtStdInRedirect_new, /* tp_new */
102+
};

src/PythonQtStdIn.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#ifndef _PYTHONQTSTDIN_H
2+
#define _PYTHONQTSTDIN_H
3+
4+
/*=========================================================================
5+
6+
Library: CTK
7+
8+
Copyright (c) Kitware Inc.
9+
10+
Licensed under the Apache License, Version 2.0 (the "License");
11+
you may not use this file except in compliance with the License.
12+
You may obtain a copy of the License at
13+
14+
http://www.commontk.org/LICENSE
15+
16+
Unless required by applicable law or agreed to in writing, software
17+
distributed under the License is distributed on an "AS IS" BASIS,
18+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
See the License for the specific language governing permissions and
20+
limitations under the License.
21+
22+
=========================================================================*/
23+
24+
//----------------------------------------------------------------------------------
25+
/*!
26+
// \file PythonQtStdIn.h
27+
// \author Jean-Christophe Fillion-Robin
28+
// \author Last changed by $Author: jcfr $
29+
// \date 2011
30+
*/
31+
//----------------------------------------------------------------------------------
32+
33+
34+
#include <dPython.h>
35+
#include "structmember.h"
36+
#include <QString>
37+
38+
//! declares the type of the stdout redirection class
39+
extern PyTypeObject PythonQtStdInRedirectType;
40+
41+
//! declares the callback that is called from the write() function
42+
typedef QString PythonQtInputChangedCB(void* callData);
43+
44+
//! declares the stdin redirection class
45+
typedef struct {
46+
PyObject_HEAD
47+
PythonQtInputChangedCB* _cb;
48+
void * _callData;
49+
} PythonQtStdInRedirect;
50+
51+
#endif

0 commit comments

Comments
 (0)