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
This commit is contained in:
Robin Dunn
2002-05-02 04:45:47 +00:00
parent f44602beb7
commit a24268437f
14 changed files with 553 additions and 11 deletions

View File

@@ -57,6 +57,13 @@ include samples/StyleEditor/*.cfg
include samples/pySketch/*.py include samples/pySketch/*.py
include samples/pySketch/images/*.bmp include samples/pySketch/images/*.bmp
include samples/frogedit/*.py 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 include wxPython/lib/*.py

View File

@@ -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\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] [Icons]

View File

@@ -0,0 +1 @@
embedded.dsp

View File

@@ -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. ;-)

View File

@@ -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 <wx/wxprec.h>
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/splitter.h>
#if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__)
#include "mondrian.xpm"
#endif
// Import Python and wxPython headers
#include <Python.h>
#include <wxPython.h>
//----------------------------------------------------------------------
// 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 <windows.h>
// // #include <stdio.h>
// #include <fcntl.h>
// #include <io.h>
// // #include <iostream>
// // #include <fstream>
// // //#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();
// }
// ////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,3 @@
mondrian ICON "mondrian.ico"
#include "wx/msw/wx.rc"

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View File

@@ -0,0 +1,44 @@
/* XPM */
static char *mondrian_xpm[] = {
/* columns rows colors chars-per-pixel */
"32 32 6 1",
" c Black",
". c Blue",
"X c #00bf00",
"o c Red",
"O c Yellow",
"+ c Gray100",
/* pixels */
" ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" "
};

View File

@@ -80,8 +80,11 @@ BOOL WINAPI DllMain(
LPVOID lpvReserved // reserved LPVOID lpvReserved // reserved
) )
{ {
wxSetInstance(hinstDLL); // If wxPython is embedded in another wxWindows app then
return 1; // the inatance has already been set.
if (! wxGetInstance())
wxSetInstance(hinstDLL);
return TRUE;
} }
#endif #endif
@@ -179,12 +182,12 @@ void __wxPreStart()
wxPyTMutex = new wxMutex; wxPyTMutex = new wxMutex;
#endif #endif
// Bail out if there is already windows created. This means that the // Bail out if there is already a wxApp created. This means that the
// toolkit has already been initialized, as in embedding wxPython in // toolkit has already been initialized, as in embedding wxPython in
// a C++ wxWindows app. // a C++ wxWindows app, so we don't need to call wxEntryStart.
if (wxTopLevelWindows.Number() > 0) if (wxTheApp != NULL) {
return; return;
}
wxPyDoCleanup = TRUE; wxPyDoCleanup = TRUE;
int argc = 0; int argc = 0;
@@ -527,8 +530,6 @@ PyObject* wxPyConstructObject(void* ptr,
PyObject* wxPyConstructObject(void* ptr, PyObject* wxPyConstructObject(void* ptr,
const wxString& className, const wxString& className,
int setThisOwn) { int setThisOwn) {
PyObject* obj;
if (!ptr) { if (!ptr) {
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;

View File

@@ -1,6 +1,6 @@
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Name: helpers.h // Name: helpers.h
// Purpose: Helper functions/classes for the wxPython extenaion module // Purpose: Helper functions/classes for the wxPython extension module
// //
// Author: Robin Dunn // Author: Robin Dunn
// //
@@ -278,7 +278,9 @@ struct wxPyCoreAPI {
}; };
#ifdef wxPyUSE_EXPORT #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 #endif

View File

@@ -109,7 +109,7 @@ public:
%inline %{ %inline %{
wxPyApp* wxGetApp() { wxPyApp* wxGetApp() {
//return wxPythonApp; //return wxPythonApp;
return (wxPyApp*)wxGetApp(); return (wxPyApp*)wxTheApp;
} }
%} %}