From a24268437fd8f5cf62e11ea3d386b14d38e6d981 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 2 May 2002 04:45:47 +0000 Subject: [PATCH] Fixed the bugs preventing wxPython from being embedded in a wxWindows app, and added a sample showing how to do it. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@15329 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/MANIFEST.in | 7 + wxPython/distrib/make_installer.py | 8 + wxPython/samples/embedded/.cvsignore | 1 + wxPython/samples/embedded/README.txt | 32 ++ wxPython/samples/embedded/embedded.cpp | 378 +++++++++++++++++++ wxPython/samples/embedded/embedded.rc | 3 + wxPython/samples/embedded/embedded_sample.py | 24 ++ wxPython/samples/embedded/makefile.unx | 29 ++ wxPython/samples/embedded/makefile.vc | 13 + wxPython/samples/embedded/mondrian.ico | Bin 0 -> 766 bytes wxPython/samples/embedded/mondrian.xpm | 44 +++ wxPython/src/helpers.cpp | 17 +- wxPython/src/helpers.h | 6 +- wxPython/src/wx.i | 2 +- 14 files changed, 553 insertions(+), 11 deletions(-) create mode 100644 wxPython/samples/embedded/.cvsignore create mode 100644 wxPython/samples/embedded/README.txt create mode 100644 wxPython/samples/embedded/embedded.cpp create mode 100644 wxPython/samples/embedded/embedded.rc create mode 100644 wxPython/samples/embedded/embedded_sample.py create mode 100644 wxPython/samples/embedded/makefile.unx create mode 100644 wxPython/samples/embedded/makefile.vc create mode 100644 wxPython/samples/embedded/mondrian.ico create mode 100644 wxPython/samples/embedded/mondrian.xpm diff --git a/wxPython/MANIFEST.in b/wxPython/MANIFEST.in index e6c53348f3..5bd7866968 100644 --- a/wxPython/MANIFEST.in +++ b/wxPython/MANIFEST.in @@ -57,6 +57,13 @@ include samples/StyleEditor/*.cfg include samples/pySketch/*.py include samples/pySketch/images/*.bmp include samples/frogedit/*.py +include samples/embedded/*.py +include samples/embedded/*.cpp +include samples/embedded/*.txt +include samples/embedded/*.vc +include samples/embedded/*.unx +include samples/embedded/*.ico +include samples/embedded/*.xpm include wxPython/lib/*.py diff --git a/wxPython/distrib/make_installer.py b/wxPython/distrib/make_installer.py index 71c7faaddf..d41edc1b79 100644 --- a/wxPython/distrib/make_installer.py +++ b/wxPython/distrib/make_installer.py @@ -167,6 +167,14 @@ Source: "samples\pySketch\images\*.bmp"; DestDir: "{app}\wxPython\samples\pySk Source: "samples\frogedit\*.py"; DestDir: "{app}\wxPython\samples\frogedit"; Components: samples +Source: "samples\embedded\*.py"; DestDir: "{app}\wxPython\samples\embedded"; Components: samples +Source: "samples\embedded\*.cpp"; DestDir: "{app}\wxPython\samples\embedded"; Components: samples +Source: "samples\embedded\*.txt"; DestDir: "{app}\wxPython\samples\embedded"; Components: samples +Source: "samples\embedded\*.vc"; DestDir: "{app}\wxPython\samples\embedded"; Components: samples +Source: "samples\embedded\*.unx"; DestDir: "{app}\wxPython\samples\embedded"; Components: samples +Source: "samples\embedded\*.ico"; DestDir: "{app}\wxPython\samples\embedded"; Components: samples +Source: "samples\embedded\*.xpm"; DestDir: "{app}\wxPython\samples\embedded"; Components: samples + ;;------------------------------------------------------------ [Icons] diff --git a/wxPython/samples/embedded/.cvsignore b/wxPython/samples/embedded/.cvsignore new file mode 100644 index 0000000000..c2ebffa091 --- /dev/null +++ b/wxPython/samples/embedded/.cvsignore @@ -0,0 +1 @@ +embedded.dsp diff --git a/wxPython/samples/embedded/README.txt b/wxPython/samples/embedded/README.txt new file mode 100644 index 0000000000..3c4f31ba49 --- /dev/null +++ b/wxPython/samples/embedded/README.txt @@ -0,0 +1,32 @@ +This sample shows how to embed wxPython into a wxWindows application. +There are a few little tricks needed to make it work, but once over +the hurdle it should work just fine for you. I'll try to describe the +build issues here, see the code and comments in embedded.cpp for +examples of how to use it. + +1. The most important thing is that your wx application and wxPython + must use the same version and the same instance of wxWindows. That + means that you can not statically link your app with wxWindows, but + must use a dynamic library for wxWindows. + +2. You must ensure that your app and wxPython are using the same + wxWindows DLL. By default on MSW wxPython installs the wxWindows + DLL to a directory not on the PATH, so you may have to do something + creative to make that happen. But because of #3 this may not be + that big of a problem. + +3. wxPython, your app and wxWindows must be built with the same flags + and settings. This probably means that you will need to rebuild + wxPython yourself. It may be possible for me to distribute the + setup.h and etc. that I use, but you'll need to rebuild everything + yourself anyway to get debugger versions so I'm not too wrried + about it just yet. BTW, on MSW if you do a FINAL=0 build (full + debug version) then you will need to have a debug version of Python + built too since it expects to have extension modules in files with + a _d in the name. If you do a FINAL=hybrid build then you will be + able to use the stock version of Python, but you won't be able to + trace through the PYTHON API functions. + +4. I expect that most of these issues will be much more minor on + Unix. ;-) + diff --git a/wxPython/samples/embedded/embedded.cpp b/wxPython/samples/embedded/embedded.cpp new file mode 100644 index 0000000000..73a79512c9 --- /dev/null +++ b/wxPython/samples/embedded/embedded.cpp @@ -0,0 +1,378 @@ +//---------------------------------------------------------------------- +// Name: embedded.cpp +// Purpose: To server as an example of how to use wxPython from +// within a C++ wxWindows program. +// +// Author: Robin Dunn +// +// Created: 1-May-2002 +// RCS-ID: $Id$ +// Copyright: (c) 2002 by Total Control Software +// Licence: wxWindows license +//---------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx/wx.h". +#include + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include +#endif + +#include + +#if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__) + #include "mondrian.xpm" +#endif + +// Import Python and wxPython headers +#include +#include + +//---------------------------------------------------------------------- +// Class definitions + +class MyApp : public wxApp +{ +public: + virtual bool OnInit(); + virtual ~MyApp(); + void Init_wxPython(); +private: + PyThreadState* main_tstate; +}; + + +class MyFrame : public wxFrame +{ +public: + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); + void RedirectStdio(); + wxWindow* DoPythonStuff(wxWindow* parent); + void OnExit(wxCommandEvent& event); + void OnPyFrame(wxCommandEvent& event); + +private: + DECLARE_EVENT_TABLE() +}; + +//---------------------------------------------------------------------- +// MyApp methods + + +bool MyApp::OnInit() +{ + Init_wxPython(); + MyFrame *frame = new MyFrame(_T("Embedded wxPython Test"), + wxPoint(50, 50), wxSize(700, 600)); + frame->Show(TRUE); + return TRUE; +} + + +void MyApp::Init_wxPython() +{ + // Initialize Python + Py_Initialize(); + PyEval_InitThreads(); + + // Load the wxPython core API. Imports the wxPython.wxc + // module and sets a pointer to a function table located there. + wxPyCoreAPI_IMPORT(); + + // Save the current Python thread state and release the + // Global Interpreter Lock. + main_tstate = wxPyBeginAllowThreads(); +} + + +MyApp::~MyApp() +{ + // Restore the thread state and tell Python to cleanup after itself. + wxPyEndAllowThreads(main_tstate); + Py_Finalize(); +} + +IMPLEMENT_APP(MyApp) + +//---------------------------------------------------------------------- + +enum +{ + ID_EXIT=1001, + ID_PYFRAME +}; + + +BEGIN_EVENT_TABLE(MyFrame, wxFrame) + EVT_MENU(ID_EXIT, OnExit) + EVT_MENU(ID_PYFRAME, OnPyFrame) +END_EVENT_TABLE() + + + +MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) + : wxFrame(NULL, -1, title, pos, size, + wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE) +{ + SetIcon(wxICON(mondrian)); + + wxMenuBar* mbar = new wxMenuBar; + wxMenu* menu = new wxMenu; + menu->Append(ID_PYFRAME, "Make wx&Python frame"); + menu->AppendSeparator(); + menu->Append(ID_EXIT, "&Close Frame\tAlt-X"); + mbar->Append(menu, "&File"); + SetMenuBar(mbar); + + CreateStatusBar(); + RedirectStdio(); + + // Make some child windows from C++ + wxSplitterWindow* sp = new wxSplitterWindow(this, -1); + wxPanel* p1 = new wxPanel(sp, -1); + p1->SetFont(wxFont(12, wxSWISS, wxNORMAL, wxBOLD)); + new wxStaticText(p1, -1, + wxT("The frame, menu, splitter, this panel and this text were created in C++..."), + wxPoint(10,10)); + + // And get a panel from Python + wxWindow* p2 = DoPythonStuff(sp); + + sp->SplitHorizontally(p1, p2, GetClientSize().y/4); +} + +void MyFrame::OnExit(wxCommandEvent& event) +{ + Close(); +} + + +//---------------------------------------------------------------------- +// This is were the fun begins... + + +char* python_code1 = "\ +from wxPython.wx import wxFrame\n\ +f = wxFrame(None, -1, 'Hello from wxPython!', size=(250, 150))\n\ +f.Show()\n\ +"; + +void MyFrame::OnPyFrame(wxCommandEvent& event) +{ + // For simple Python code that doesn't have to interact with the + // C++ code in any way, you can execute it with PyRun_SimpleString. + + + // First, whenever you do anyting with Python objects or code, you + // *MUST* aquire the Global Interpreter Lock and block other + // Python threads from running. + wxPyBeginBlockThreads(); + + // Execute the code in the __main__ module + PyRun_SimpleString(python_code1); + + // Finally, release the GIL and let other Python threads run. + wxPyEndBlockThreads(); +} + + +void MyFrame::RedirectStdio() +{ + // This is a helpful little tidbit to help debugging and such. It + // redirects Python's stdout and stderr to a window that will popup + // only on demand when something is printed, like a traceback. + char* python_redirect = "\ +import sys\n\ +from wxPython.wx import wxPyOnDemandOutputWindow\n\ +output = wxPyOnDemandOutputWindow()\n\ +sys.stdin = sys.stderr = output\n\ +"; + wxPyBeginBlockThreads(); + PyRun_SimpleString(python_redirect); + wxPyEndBlockThreads(); +} + + + + +char* python_code2 = "\ +import embedded_sample\n\ +\n\ +def makeWindow(parent):\n\ + win = embedded_sample.MyPanel(parent)\n\ + return win\n\ +"; + +wxWindow* MyFrame::DoPythonStuff(wxWindow* parent) +{ + // More complex embedded situations will require passing C++ objects to + // Python and/or returning objects from Python to be used in C++. This + // sample shows one way to do it. NOTE: The above code could just have + // easily come from a file, or the whole thing coupld be in the Python + // module that is imported and manipulated directly in this C++ code. See + // the Python API for more details. + + wxWindow* window = NULL; + PyObject* result; + + // As always, first grab the GIL + wxPyBeginBlockThreads(); + + // Now make a dictionary to serve as the global namespace when the code is + // executed. Put a reference to the builtins module in it. (Yes, the + // names are supposed to be different, I don't know why...) + PyObject* globals = PyDict_New(); + PyObject* builtins = PyImport_ImportModule("__builtin__"); + PyDict_SetItemString(globals, "__builtins__", builtins); + Py_DECREF(builtins); + + // Execute the code to make the makeWindow function + result = PyRun_String(python_code2, Py_file_input, globals, globals); + // Was there an exception? + if (! result) { + PyErr_Print(); + wxPyEndBlockThreads(); + return NULL; + } + Py_DECREF(result); + + // Now there should be an object named 'makeWindow' in the dictionary that + // we can grab a pointer to: + PyObject* func = PyDict_GetItemString(globals, "makeWindow"); + wxASSERT(PyCallable_Check(func)); + + // Now build an argument tuple and call the Python function. Notice the + // use of another wxPython API to take a wxWindows object and build a + // wxPython object that wraps it. + PyObject* arg = wxPyMake_wxObject(parent); + wxASSERT(arg != NULL); + + PyObject* tuple = PyTuple_New(1); + PyTuple_SET_ITEM(tuple, 0, arg); + result = PyEval_CallObject(func, tuple); + + // Was there an exception? + if (! result) + PyErr_Print(); + else { + // Otherwise, get the returned window out of Python-land and + // into C++-ville... + bool error = SWIG_GetPtrObj(result, (void**)&window, "_wxWindow_p"); + wxASSERT_MSG(!error, wxT("Returned object was not a wxWindow!")); + Py_DECREF(result); + } + + // Release the python objects we still have + Py_DECREF(globals); + Py_DECREF(tuple); + + // Finally, after all Python stuff is done, release the GIL + wxPyEndBlockThreads(); + + return window; +} + + +//---------------------------------------------------------------------- + + + + + + + + + +// void MyFrame::OnButton(wxCommandEvent& WXUNUSED(event)) +// { +// //Py_Initialize(); +// //PySys_SetArgv(argc, argv) +// // // initialize thread support +// // PyEval_InitThreads(); +// wxPyBeginBlockThreads(); +// PyRun_SimpleString( +// "from wxPython.wx import *\n" +// "f = wxFrame(None, -1, 'Hello from wxPython', size=(250,150))\n" +// "f.Show()" +// ); +// wxPyEndBlockThreads(); +// } + + + + + + +// //////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////// +// /// this, RedirectIOToConsole(), help came from +// /// http://www.halcyon.com/ast/dload/guicon.htm + +// // #include +// // #include +// #include +// #include +// // #include +// // #include +// // //#ifndef _USE_OLD_IOSTREAMS +// // using namespace std; +// // //#endif + +// // maximum mumber of lines the output console should have +// static const WORD MAX_CONSOLE_LINES = 500; + +// void RedirectIOToConsole() +// { +// int hConHandle; +// long lStdHandle; +// CONSOLE_SCREEN_BUFFER_INFO coninfo; +// FILE *fp; + +// // allocate a console for this app +// AllocConsole(); + +// // set the screen buffer to be big enough to let us scroll text +// GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); + +// coninfo.dwSize.Y = MAX_CONSOLE_LINES; +// SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); + +// // redirect unbuffered STDOUT to the console +// lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); +// hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + +// fp = _fdopen( hConHandle, "w" ); +// *stdout = *fp; + +// setvbuf( stdout, NULL, _IONBF, 0 ); + +// // redirect unbuffered STDIN to the console +// lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); +// hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + +// fp = _fdopen( hConHandle, "r" ); +// *stdin = *fp; + +// setvbuf( stdin, NULL, _IONBF, 0 ); + +// // redirect unbuffered STDERR to the console +// lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); +// hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + +// fp = _fdopen( hConHandle, "w" ); +// *stderr = *fp; +// setvbuf( stderr, NULL, _IONBF, 0 ); + +// // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog +// // point to console as well +// //std::ios::sync_with_stdio(); +// } + +// //////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////// diff --git a/wxPython/samples/embedded/embedded.rc b/wxPython/samples/embedded/embedded.rc new file mode 100644 index 0000000000..7655c62a4c --- /dev/null +++ b/wxPython/samples/embedded/embedded.rc @@ -0,0 +1,3 @@ +mondrian ICON "mondrian.ico" +#include "wx/msw/wx.rc" + diff --git a/wxPython/samples/embedded/embedded_sample.py b/wxPython/samples/embedded/embedded_sample.py new file mode 100644 index 0000000000..f98c3f3efb --- /dev/null +++ b/wxPython/samples/embedded/embedded_sample.py @@ -0,0 +1,24 @@ +from wxPython.wx import * +from wxPython.lib.PyCrust import shell, version + +class MyPanel(wxPanel): + def __init__(self, parent): + wxPanel.__init__(self, parent, -1) + print parent + + text = wxStaticText(self, -1, + "Everything on this side of the splitter comes from Python.") + text.SetFont(wxFont(12, wxSWISS, wxNORMAL, wxBOLD)) + + intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % version.VERSION + pycrust = shell.Shell(self, -1, introText=intro) + #pycrust = wxTextCtrl(self, -1, intro) + + sizer = wxBoxSizer(wxVERTICAL) + sizer.Add(text, 0, wxEXPAND|wxALL, 10) + sizer.Add(pycrust, 1, wxEXPAND|wxBOTTOM|wxLEFT|wxRIGHT, 10) + + self.SetSizer(sizer) + #self.SetAutoLayout(true) + #self.Layout() + diff --git a/wxPython/samples/embedded/makefile.unx b/wxPython/samples/embedded/makefile.unx new file mode 100644 index 0000000000..ab17be9ca0 --- /dev/null +++ b/wxPython/samples/embedded/makefile.unx @@ -0,0 +1,29 @@ +# +# Makefile for Unix +# +# This makefile requires a Unix version of wxWindows +# to be installed on your system. This is most often +# done typing "make install" when using the complete +# sources of wxWindows or by installing the two +# RPM packages wxGTK.XXX.rpm and wxGTK-devel.XXX.rpm +# under Linux. +# + +PROGRAM = embedded +OBJECTS = $(PROGRAM).o + +CXX = $(shell wx-config --cxx) + + +.SUFFIXES: .o .cpp + +.cpp.o : + $(CXX) -c `wx-config --cxxflags` -o $@ $< + +all: $(PROGRAM) + +$(PROGRAM): $(OBJECTS) + $(CXX) -o $(PROGRAM) $(OBJECTS) `wx-config --libs` + +clean: + rm -f *.o $(PROGRAM) diff --git a/wxPython/samples/embedded/makefile.vc b/wxPython/samples/embedded/makefile.vc new file mode 100644 index 0000000000..44490bee7d --- /dev/null +++ b/wxPython/samples/embedded/makefile.vc @@ -0,0 +1,13 @@ +# +# Makefile for Windows and MS VIsual C++ +# + +WXDIR = $(WXWIN) +PYTHONDIR = d:\tools\Python22 + +PROGRAM = embedded +OBJECTS = $(PROGRAM).obj +EXTRAINC = /I$(PYTHONDIR)\include /I$(WXDIR)/wxPython/src +EXTRALIBS = /LIBPATH:$(PYTHONDIR)\libs + +!include $(WXDIR)\src\makeprog.vc diff --git a/wxPython/samples/embedded/mondrian.ico b/wxPython/samples/embedded/mondrian.ico new file mode 100644 index 0000000000000000000000000000000000000000..2310c5d275a87af295d5ea8dc79ea417a5e74c53 GIT binary patch literal 766 zcmZQzU<5)11px*Sc)`TLAO@s0fLH;D9e|jTfdxnc0Z 0) + // a C++ wxWindows app, so we don't need to call wxEntryStart. + if (wxTheApp != NULL) { return; - + } wxPyDoCleanup = TRUE; int argc = 0; @@ -527,8 +530,6 @@ PyObject* wxPyConstructObject(void* ptr, PyObject* wxPyConstructObject(void* ptr, const wxString& className, int setThisOwn) { - PyObject* obj; - if (!ptr) { Py_INCREF(Py_None); return Py_None; diff --git a/wxPython/src/helpers.h b/wxPython/src/helpers.h index 4ec75ec534..cfe7134df1 100644 --- a/wxPython/src/helpers.h +++ b/wxPython/src/helpers.h @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////// // Name: helpers.h -// Purpose: Helper functions/classes for the wxPython extenaion module +// Purpose: Helper functions/classes for the wxPython extension module // // Author: Robin Dunn // @@ -278,7 +278,9 @@ struct wxPyCoreAPI { }; #ifdef wxPyUSE_EXPORT -static wxPyCoreAPI* wxPyCoreAPIPtr = NULL; // Each module needs one, but doesn't have to use it. +// Notice that this is static, not extern. This is by design, each module +// needs one, but doesn't have to use it. +static wxPyCoreAPI* wxPyCoreAPIPtr = NULL; #endif diff --git a/wxPython/src/wx.i b/wxPython/src/wx.i index 010621f4fa..432dc65aee 100644 --- a/wxPython/src/wx.i +++ b/wxPython/src/wx.i @@ -109,7 +109,7 @@ public: %inline %{ wxPyApp* wxGetApp() { //return wxPythonApp; - return (wxPyApp*)wxGetApp(); + return (wxPyApp*)wxTheApp; } %} -- 2.45.2