| 1 | //---------------------------------------------------------------------- |
| 2 | // Name: embedded.cpp |
| 3 | // Purpose: To serve as an example of how to use wxPython from |
| 4 | // within a C++ wxWindows program. |
| 5 | // |
| 6 | // Author: Robin Dunn |
| 7 | // |
| 8 | // Created: 1-May-2002 |
| 9 | // RCS-ID: $Id$ |
| 10 | // Copyright: (c) 2002 by Total Control Software |
| 11 | // Licence: wxWindows license |
| 12 | //---------------------------------------------------------------------- |
| 13 | |
| 14 | #include <Python.h> |
| 15 | |
| 16 | |
| 17 | // For compilers that support precompilation, includes "wx/wx.h". |
| 18 | #include <wx/wxprec.h> |
| 19 | |
| 20 | #ifdef __BORLANDC__ |
| 21 | #pragma hdrstop |
| 22 | #endif |
| 23 | |
| 24 | #ifndef WX_PRECOMP |
| 25 | #include <wx/wx.h> |
| 26 | #endif |
| 27 | |
| 28 | #include <wx/splitter.h> |
| 29 | |
| 30 | #if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__) |
| 31 | #include "mondrian.xpm" |
| 32 | #endif |
| 33 | |
| 34 | // Import Python and wxPython headers |
| 35 | #include <wx/wxPython/wxPython.h> |
| 36 | |
| 37 | |
| 38 | //---------------------------------------------------------------------- |
| 39 | // Class definitions |
| 40 | |
| 41 | class MyApp : public wxApp |
| 42 | { |
| 43 | public: |
| 44 | virtual bool OnInit(); |
| 45 | virtual int OnExit(); |
| 46 | bool Init_wxPython(); |
| 47 | private: |
| 48 | PyThreadState* m_mainTState; |
| 49 | }; |
| 50 | |
| 51 | |
| 52 | class MyFrame : public wxFrame |
| 53 | { |
| 54 | public: |
| 55 | MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); |
| 56 | void RedirectStdio(); |
| 57 | wxWindow* DoPythonStuff(wxWindow* parent); |
| 58 | void OnExit(wxCommandEvent& event); |
| 59 | void OnPyFrame(wxCommandEvent& event); |
| 60 | |
| 61 | private: |
| 62 | DECLARE_EVENT_TABLE() |
| 63 | }; |
| 64 | |
| 65 | //---------------------------------------------------------------------- |
| 66 | // MyApp methods |
| 67 | |
| 68 | |
| 69 | bool MyApp::OnInit() |
| 70 | { |
| 71 | if ( !Init_wxPython() ) |
| 72 | // don't start the app if we can't initialize wxPython. |
| 73 | return false; |
| 74 | |
| 75 | MyFrame *frame = new MyFrame(_T("Embedded wxPython Test"), |
| 76 | wxDefaultPosition, wxSize(700, 600)); |
| 77 | frame->Show(true); |
| 78 | return true; |
| 79 | } |
| 80 | |
| 81 | |
| 82 | |
| 83 | bool MyApp::Init_wxPython() |
| 84 | { |
| 85 | // Initialize Python |
| 86 | Py_Initialize(); |
| 87 | PyEval_InitThreads(); |
| 88 | |
| 89 | // Load the wxPython core API. Imports the wx._core_ module and sets a |
| 90 | // local pointer to a function table located there. The pointer is used |
| 91 | // internally by the rest of the API functions. |
| 92 | if ( ! wxPyCoreAPI_IMPORT() ) { |
| 93 | wxLogError(wxT("***** Error importing the wxPython API! *****")); |
| 94 | PyErr_Print(); |
| 95 | Py_Finalize(); |
| 96 | return false; |
| 97 | } |
| 98 | |
| 99 | // Save the current Python thread state and release the |
| 100 | // Global Interpreter Lock. |
| 101 | m_mainTState = wxPyBeginAllowThreads(); |
| 102 | |
| 103 | return true; |
| 104 | } |
| 105 | |
| 106 | |
| 107 | int MyApp::OnExit() |
| 108 | { |
| 109 | // Restore the thread state and tell Python to cleanup after itself. |
| 110 | // wxPython will do its own cleanup as part of that process. This is done |
| 111 | // in OnExit instead of ~MyApp because OnExit is only called if OnInit is |
| 112 | // successful. |
| 113 | wxPyEndAllowThreads(m_mainTState); |
| 114 | Py_Finalize(); |
| 115 | return 0; |
| 116 | } |
| 117 | |
| 118 | |
| 119 | IMPLEMENT_APP(MyApp) |
| 120 | |
| 121 | //---------------------------------------------------------------------- |
| 122 | |
| 123 | enum |
| 124 | { |
| 125 | ID_EXIT=1001, |
| 126 | ID_PYFRAME |
| 127 | }; |
| 128 | |
| 129 | |
| 130 | BEGIN_EVENT_TABLE(MyFrame, wxFrame) |
| 131 | EVT_MENU(ID_EXIT, MyFrame::OnExit) |
| 132 | EVT_MENU(ID_PYFRAME, MyFrame::OnPyFrame) |
| 133 | END_EVENT_TABLE() |
| 134 | |
| 135 | |
| 136 | |
| 137 | MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) |
| 138 | : wxFrame(NULL, -1, title, pos, size, |
| 139 | wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE) |
| 140 | { |
| 141 | SetIcon(wxICON(mondrian)); |
| 142 | |
| 143 | wxMenuBar* mbar = new wxMenuBar; |
| 144 | wxMenu* menu = new wxMenu; |
| 145 | menu->Append(ID_PYFRAME, _T("Make wx&Python frame")); |
| 146 | menu->AppendSeparator(); |
| 147 | menu->Append(ID_EXIT, _T("&Close Frame\tAlt-X")); |
| 148 | mbar->Append(menu, _T("&File")); |
| 149 | SetMenuBar(mbar); |
| 150 | |
| 151 | CreateStatusBar(); |
| 152 | RedirectStdio(); |
| 153 | |
| 154 | // Make some child windows from C++ |
| 155 | wxSplitterWindow* sp = new wxSplitterWindow(this, -1); |
| 156 | wxPanel* p1 = new wxPanel(sp, -1, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER); |
| 157 | |
| 158 | new wxStaticText(p1, -1, |
| 159 | _T("The frame, menu, splitter, this panel and this text were created in C++..."), |
| 160 | wxPoint(10,10)); |
| 161 | |
| 162 | // And get a panel from Python |
| 163 | wxWindow* p2 = DoPythonStuff(sp); |
| 164 | |
| 165 | sp->SplitHorizontally(p1, p2, GetClientSize().y/4); |
| 166 | } |
| 167 | |
| 168 | void MyFrame::OnExit(wxCommandEvent& event) |
| 169 | { |
| 170 | Close(); |
| 171 | } |
| 172 | |
| 173 | |
| 174 | //---------------------------------------------------------------------- |
| 175 | // This is where the fun begins... |
| 176 | |
| 177 | |
| 178 | char* python_code1 = "\ |
| 179 | import wx\n\ |
| 180 | f = wx.Frame(None, -1, 'Hello from wxPython!', size=(250, 150))\n\ |
| 181 | f.Show()\n\ |
| 182 | "; |
| 183 | |
| 184 | void MyFrame::OnPyFrame(wxCommandEvent& event) |
| 185 | { |
| 186 | // For simple Python code that doesn't have to interact with the |
| 187 | // C++ code in any way, you can execute it with PyRun_SimpleString. |
| 188 | |
| 189 | |
| 190 | // First, whenever you do anything with Python objects or code, you |
| 191 | // *MUST* aquire the Global Interpreter Lock and block other |
| 192 | // Python threads from running. |
| 193 | wxPyBlock_t blocked = wxPyBeginBlockThreads(); |
| 194 | |
| 195 | // Execute the code in the __main__ module |
| 196 | PyRun_SimpleString(python_code1); |
| 197 | |
| 198 | // Finally, release the GIL and let other Python threads run. |
| 199 | wxPyEndBlockThreads(blocked); |
| 200 | } |
| 201 | |
| 202 | |
| 203 | void MyFrame::RedirectStdio() |
| 204 | { |
| 205 | // This is a helpful little tidbit to help debugging and such. It |
| 206 | // redirects Python's stdout and stderr to a window that will popup |
| 207 | // only on demand when something is printed, like a traceback. |
| 208 | char* python_redirect = "\ |
| 209 | import sys\n\ |
| 210 | import wx\n\ |
| 211 | output = wx.PyOnDemandOutputWindow()\n\ |
| 212 | sys.stdin = sys.stderr = output\n\ |
| 213 | "; |
| 214 | wxPyBlock_t blocked = wxPyBeginBlockThreads(); |
| 215 | PyRun_SimpleString(python_redirect); |
| 216 | wxPyEndBlockThreads(blocked); |
| 217 | } |
| 218 | |
| 219 | |
| 220 | |
| 221 | |
| 222 | char* python_code2 = "\ |
| 223 | import sys\n\ |
| 224 | sys.path.append('.')\n\ |
| 225 | import embedded_sample\n\ |
| 226 | \n\ |
| 227 | def makeWindow(parent):\n\ |
| 228 | win = embedded_sample.MyPanel(parent)\n\ |
| 229 | return win\n\ |
| 230 | "; |
| 231 | |
| 232 | wxWindow* MyFrame::DoPythonStuff(wxWindow* parent) |
| 233 | { |
| 234 | // More complex embedded situations will require passing C++ objects to |
| 235 | // Python and/or returning objects from Python to be used in C++. This |
| 236 | // sample shows one way to do it. NOTE: The above code could just have |
| 237 | // easily come from a file, or the whole thing could be in the Python |
| 238 | // module that is imported and manipulated directly in this C++ code. See |
| 239 | // the Python API for more details. |
| 240 | |
| 241 | wxWindow* window = NULL; |
| 242 | PyObject* result; |
| 243 | |
| 244 | // As always, first grab the GIL |
| 245 | wxPyBlock_t blocked = wxPyBeginBlockThreads(); |
| 246 | |
| 247 | // Now make a dictionary to serve as the global namespace when the code is |
| 248 | // executed. Put a reference to the builtins module in it. (Yes, the |
| 249 | // names are supposed to be different, I don't know why...) |
| 250 | PyObject* globals = PyDict_New(); |
| 251 | PyObject* builtins = PyImport_ImportModule("__builtin__"); |
| 252 | PyDict_SetItemString(globals, "__builtins__", builtins); |
| 253 | Py_DECREF(builtins); |
| 254 | |
| 255 | // Execute the code to make the makeWindow function |
| 256 | result = PyRun_String(python_code2, Py_file_input, globals, globals); |
| 257 | // Was there an exception? |
| 258 | if (! result) { |
| 259 | PyErr_Print(); |
| 260 | wxPyEndBlockThreads(blocked); |
| 261 | return NULL; |
| 262 | } |
| 263 | Py_DECREF(result); |
| 264 | |
| 265 | // Now there should be an object named 'makeWindow' in the dictionary that |
| 266 | // we can grab a pointer to: |
| 267 | PyObject* func = PyDict_GetItemString(globals, "makeWindow"); |
| 268 | wxASSERT(PyCallable_Check(func)); |
| 269 | |
| 270 | // Now build an argument tuple and call the Python function. Notice the |
| 271 | // use of another wxPython API to take a wxWindows object and build a |
| 272 | // wxPython object that wraps it. |
| 273 | PyObject* arg = wxPyMake_wxObject(parent, false); |
| 274 | wxASSERT(arg != NULL); |
| 275 | PyObject* tuple = PyTuple_New(1); |
| 276 | PyTuple_SET_ITEM(tuple, 0, arg); |
| 277 | result = PyEval_CallObject(func, tuple); |
| 278 | |
| 279 | // Was there an exception? |
| 280 | if (! result) |
| 281 | PyErr_Print(); |
| 282 | else { |
| 283 | // Otherwise, get the returned window out of Python-land and |
| 284 | // into C++-ville... |
| 285 | bool success = wxPyConvertSwigPtr(result, (void**)&window, _T("wxWindow")); |
| 286 | wxASSERT_MSG(success, _T("Returned object was not a wxWindow!")); |
| 287 | Py_DECREF(result); |
| 288 | } |
| 289 | |
| 290 | // Release the python objects we still have |
| 291 | Py_DECREF(globals); |
| 292 | Py_DECREF(tuple); |
| 293 | |
| 294 | // Finally, after all Python stuff is done, release the GIL |
| 295 | wxPyEndBlockThreads(blocked); |
| 296 | |
| 297 | return window; |
| 298 | } |
| 299 | |
| 300 | |
| 301 | //---------------------------------------------------------------------- |