wxWidgets 3.0, Python 3.3 기준이다.
http://docs.python.org/3.4/faq/extending.html
#pragma once
#ifndef PYTHONCONSOLE_H
#define PYTHONCONSOLE_H
#include <Python.h>
#include <string>
class PythonConsole
{
public:
PythonConsole();
virtual ~PythonConsole();
public :
void feed_line( const char *line );
const char *get_output();
// Public vars
public :
const char *prompt;
const char *error_msg;
private:
const char *get_error();
protected:
private:
const char *ps1;
const char *ps2;
PyObject *glb, *loc;
PyObject *exc, *val, *trb, *obj, *dum;
std::string code;
};
#endif // PYTHONCONSOLE_H
두둥!
#include "PythonConsole.h"
#include <Python.h>
//#include <iostream>
#include <cassert>
using namespace std;
PythonConsole::PythonConsole()
{
//ctor
ps1 = ">>> ";
ps2 = "... ";
prompt = ps1;
error_msg = NULL;
Py_Initialize();
loc = PyDict_New();
glb = PyDict_New();
PyDict_SetItemString( glb, "__builtins__", PyEval_GetBuiltins () );
// Redirection of stdout and stderr to stream
PyRun_String( "from io import StringIO", Py_single_input, glb, loc );
PyRun_String( "import sys", Py_single_input, glb, loc );
PyRun_String( "old_stdout = sys.stdout", Py_single_input, glb, loc );
PyRun_String( "old_stderr = sys.stderr", Py_single_input, glb, loc );
PyRun_String( "sys.stdout = StringIO()", Py_single_input, glb, loc );
PyRun_String( "sys.stderr = StringIO()", Py_single_input, glb, loc );
PyRun_String( "print( sys.version )", Py_single_input, glb, loc );
exc = val = trb = obj = dum = NULL;
code.clear();
}
PythonConsole::~PythonConsole()
{
PyRun_String( "sys.stdout = old_stdout", Py_single_input, glb, loc );
PyRun_String( "sys.stderr = old_stderr", Py_single_input, glb, loc );
Py_XDECREF(glb);
Py_XDECREF(loc);
Py_Finalize();
}
const char *PythonConsole::get_error()
{
PyRun_String( "ss = sys.stderr.getvalue()", Py_single_input, glb, loc );
PyObject *str = PyDict_GetItem( loc, PyUnicode_FromString( "ss" ) );
PyRun_String( "sys.stderr = StringIO()", Py_single_input, glb, loc ); // clear buffer
return PyUnicode_AsUTF8( str );
}
const char *PythonConsole::get_output()
{
PyRun_String( "ss = sys.stdout.getvalue()", Py_single_input, glb, loc );
PyObject *str = PyDict_GetItem( loc, PyUnicode_FromString( "ss" ) );
PyRun_String( "sys.stdout = StringIO()", Py_single_input, glb, loc );
return PyUnicode_AsUTF8( str );
}
void PythonConsole::feed_line( const char *line )
{
code += line;
code += '\n'; // For python interpreter
//cout << line << endl;
//cout << code;
PyObject *src = Py_CompileString( code.c_str(), "<stdin>", Py_single_input );
error_msg = NULL;
if (NULL != src) // compiled just fine
{
assert( code[ code.size()-1 ] == '\n' );
if (ps1 == prompt || // ">>>"
'\n' == code[ code.size()-2 ] ) // ... " and double '\n'
{ // so execute it
dum = PyEval_EvalCode( src, glb, loc );
Py_XDECREF (dum);
Py_XDECREF (src);
code.clear();
if (PyErr_Occurred ()) {
PyErr_Print();
error_msg = get_error();
}
prompt = ps1;
}
}
else if (PyErr_ExceptionMatches (PyExc_SyntaxError))
{
const char *msg = NULL;
PyErr_Fetch (&exc, &val, &trb); // clears exception!
if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&
!strcmp (msg, "unexpected EOF while parsing")) // E_EOF
{
Py_XDECREF (exc);
Py_XDECREF (val);
Py_XDECREF (trb);
prompt = ps2;
}
else // some other syntax error
{
PyErr_Restore (exc, val, trb);
PyErr_Print();
error_msg = get_error();
code.clear();
prompt = ps1;
}
}
else // some non-syntax error
{
PyErr_Print();
error_msg = get_error();
code.clear();
prompt = ps1;
}
}
cons = new PythonConsole()...cons->feed_line( "print( 'hello world!' )" );이 때if( cons->error_msg ) {cout << cons->error_msg}else {
cout << cons->get_output();
}
...
delete cons;
이런식으로 운영해주면 된다.
그러니까, GUI의 frame에서 사용하려면 frame 초기화 부분에는 new 로 콘솔을 잡는 부분.
그리고 text edit같은걸로 code를 적은 뒤에는 feed_line으로 먹여준다.
마지막 frame 종료시엔 delete cons.
wxWidgets로 만든 예제 콘솔 frame:
ConsoleFrame.h:
#ifndef CONSOLEFRAME_H
#define CONSOLEFRAME_H
//(*Headers(ConsoleFrame)
#include <wx/sizer.h>
#include <wx/button.h>
#include <wx/frame.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
//*)
#include "PythonConsole.h"
class ConsoleFrame: public wxFrame
{
public:
ConsoleFrame(wxWindow* parent,wxWindowID id=wxID_ANY,const wxPoint& pos=wxDefaultPosition,const wxSize& size=wxDefaultSize);
virtual ~ConsoleFrame();
//(*Declarations(ConsoleFrame)
wxTextCtrl* TextCmd;
wxButton* ButtonSend;
wxTextCtrl* TextConsoleOutput;
wxStaticText* StaticTextPrompt;
//*)
protected:
//(*Identifiers(ConsoleFrame)
static const long ID_TEXTCTRL_CONSOLE;
static const long ID_STATICTEXT1;
static const long ID_TEXTCTRL_CMD;
static const long ID_BUTTON_SEND;
//*)
static const long ID_CLOSE;
private:
//(*Handlers(ConsoleFrame)
void OnTextCmdTextEnter(wxCommandEvent& event);
void OnTextCmdText(wxCommandEvent& event);
//*)
PythonConsole *cons;
void OnEscPress( wxCommandEvent &event );
DECLARE_EVENT_TABLE()
};
#endif
#include "ConsoleFrame.h"#include <wx/msgdlg.h>#include <wx/accel.h>//#include <wx/textctrl.h>/*#ifdef _WIN32#include <windows.h>// ?? dunno what to load, yet// http://stackoverflow.com/questions/2746168/how-to-construct-a-c-fstream-from-a-posix-file-descriptor#else#include <ext/stdio_filebuf.h>#include <fstream>#endif*///(*InternalHeaders(ConsoleFrame)#include <wx/string.h>#include <wx/intl.h>//*)//(*IdInit(ConsoleFrame)const long ConsoleFrame::ID_TEXTCTRL_CONSOLE = wxNewId();const long ConsoleFrame::ID_STATICTEXT1 = wxNewId();const long ConsoleFrame::ID_TEXTCTRL_CMD = wxNewId();const long ConsoleFrame::ID_BUTTON_SEND = wxNewId();//*)const long ConsoleFrame::ID_CLOSE = wxNewId(); // close ESC key acceleratorBEGIN_EVENT_TABLE(ConsoleFrame,wxFrame)//(*EventTable(ConsoleFrame)//*)END_EVENT_TABLE()ConsoleFrame::ConsoleFrame(wxWindow* parent,wxWindowID id,const wxPoint& pos,const wxSize& size){//(*Initialize(ConsoleFrame)wxBoxSizer* BoxSizer2;wxBoxSizer* BoxSizer1;Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("wxID_ANY"));BoxSizer1 = new wxBoxSizer(wxVERTICAL);TextConsoleOutput = new wxTextCtrl(this, ID_TEXTCTRL_CONSOLE, wxEmptyString, wxDefaultPosition, wxSize(427,235), wxTE_AUTO_SCROLL|wxTE_MULTILINE|wxTE_READONLY, wxDefaultValidator, _T("ID_TEXTCTRL_CONSOLE"));BoxSizer1->Add(TextConsoleOutput, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3);BoxSizer2 = new wxBoxSizer(wxHORIZONTAL);StaticTextPrompt = new wxStaticText(this, ID_STATICTEXT1, _(">>>"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));StaticTextPrompt->SetMinSize(wxSize(30,-1));BoxSizer2->Add(StaticTextPrompt, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);TextCmd = new wxTextCtrl(this, ID_TEXTCTRL_CMD, wxEmptyString, wxDefaultPosition, wxSize(264,20), wxTE_PROCESS_ENTER, wxDefaultValidator, _T("ID_TEXTCTRL_CMD"));TextCmd->SetFocus();BoxSizer2->Add(TextCmd, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);ButtonSend = new wxButton(this, ID_BUTTON_SEND, _("Send"), wxDefaultPosition, wxSize(55,27), 0, wxDefaultValidator, _T("ID_BUTTON_SEND"));BoxSizer2->Add(ButtonSend, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);BoxSizer1->Add(BoxSizer2, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);SetSizer(BoxSizer1);BoxSizer1->Fit(this);BoxSizer1->SetSizeHints(this);Connect(ID_TEXTCTRL_CMD,wxEVT_COMMAND_TEXT_ENTER,(wxObjectEventFunction)&ConsoleFrame::OnTextCmdTextEnter);Connect(ID_BUTTON_SEND,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ConsoleFrame::OnTextCmdTextEnter);//*)//TextCmd->Connect( wxEVT_KEY_DOWN, (wxObjectEventFunction)&ConsoleFrame::OnTextCmdKeyDown );// OK, esc key interception failed. Trying acceleratorswxButton *btn = new wxButton( this, ID_CLOSE, _("Close") );btn->Connect( wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&ConsoleFrame::OnEscPress );btn->Hide(); // Make this button invisible to userswxAcceleratorEntry entries[ 1 ];entries[0].Set( wxACCEL_NORMAL, WXK_ESCAPE, ID_CLOSE );wxAcceleratorTable accel( 1, entries );this->SetAcceleratorTable( accel );Connect( ID_CLOSE, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&ConsoleFrame::OnEscPress );this->cons = new PythonConsole();TextConsoleOutput->AppendText( wxString::FromUTF8( cons->get_output() ) ); // Get the initial version message}ConsoleFrame::~ConsoleFrame(){//(*Destroy(ConsoleFrame)//*)delete this->cons;}void ConsoleFrame::OnTextCmdTextEnter(wxCommandEvent& event){// wxMessageBox( TextCmd->GetLineText( 0 ) );cons->feed_line( TextCmd->GetLineText( 0 ).mb_str() );TextConsoleOutput->AppendText( StaticTextPrompt->GetLabel() + ' ' + TextCmd->GetLineText( 0 ) + '\n' );TextCmd->Clear();if( cons->error_msg ) {TextConsoleOutput->AppendText( wxString::FromUTF8( cons->error_msg ) );}else {const char *out = cons->get_output();if( out )TextConsoleOutput->AppendText( wxString::FromUTF8( out ) );}StaticTextPrompt->SetLabel( wxString::FromUTF8( cons->prompt ) );}void ConsoleFrame::OnEscPress( wxCommandEvent &event ){//wxMessageBox( _("I'm on a boat") );event.Skip(); // need this!Close();}