Embedding Pythonwin¶
Existing document
\Pythonwin\doc\EmbeddingWin32ui.html
Todo
Replace code roles with link to apidocs.
Replace file roles with links to file on github?
Pythonwin is distributed as 2 key components - Pythonwin.exe and win32ui.pyd
. win32ui.pyd
contains the raw functionality of Pythonwin - Pythonwin.exe is a simple “wrapper” .exe that hosts win32ui.pyd
.
This document describes how to embed the win32ui extension module in your own application.
Limitations¶
The biggest limitations you will find are that a host application must:
Have some small changes made to the application source to fully support win32ui command handling and routing (although most things work without modification)
Be an MFC application
Below is some example code needed to embed win32ui in your application.
Changes to the application¶
Subclass a Win32uiHostGlue
class¶
You must instantiate a Win32uiHostGlue
class. This class is used as a glue between win32ui.pyd
and the host .exe, and is defined in Win32uiHostGlue.h
.
Currently, this consists of about 5 methods that must be called by your application. The principle is that the Application
must hook certain handlers, and delegate them to the HostGlue
class. This will ensure the appropriate Win32ui internals are called.
Embedding win32ui Architecture¶
win32uihostglue.h
¶
The win32uihostglue.h
module defines a class which makes interfacing fairly simple. This Win32uiHostGlue`
class is used as a glue between win32ui.pyd
and the host .exe. In the most simple case, you need to instantiate one of the classes, and at certain key points in your CWinApp`
derived class, call the appropriate methods. You may choose to provide your own glue class derived from Win32uiHostGlue
in certain cases.
Below is an example class, which overrides the SetStatusText`
method, so that status information displays in the applications status bar (this is only necessary if your application has a “non standard” status bar - normally you could omit this.).
GameApp NEAR theApp; // My existing CWinApp derived class.
// HostGlue class.
class GameHostGlue : public Win32uiHostGlue
{
virtual void SetStatusText(const char *text, BOOL bForce)
// Assuming our GameApp above defines a SetStatusText method
{GetApp()->SetStatusText(text, bForce);}
};
// The one and only Glue object.
GameHostGlue NEAR glue;
And now we are well on our way.
Delegating to win32uiHostGlue
¶
You need to either implement, or modify, certain key methods of your Application
object. Probably the most important is the call to initialise win32ui. You need to modify your CWinApp::InitInstance
method (it is almost certain you already have one). The following code needs to be executed in this method:
InitInstance¶
BOOL GameApp::InitInstance()
{
...
if (!glue.DynamicApplicationInit("import initscore", csScripts)) {
// Assuming you have a ReportError method - do whatever makes sense!
ReportError("Could not attach to the Python win32ui extensions");
return FALSE;
}
...
Note the following:
The first param,
"import initscore"
is the Python command that will be executed to kick in the Python side of the world. For Pythonwin, this is"import pywin.framework.startup"
- seeinitscore.py
below.The second param may be empty or
NULL
, and defines an additional PythonPath to be added. This is useful if you do not want your application .py/.pyd files on the main PythonPath. In the example above, code not shown loadscsScripts
from the application’s INI file.The example above assumes the Python code is not responsible for creating the main application window. In the case of Pythonwin (where the Python code does create the main window) this call actually appears in
InitApplication()
for theCWinApp
class. The example above assumes you have existing C++ code which creates the main frame window. Your Python code could create child windows, as theinitscore.py
file demonstrates.
And the Rest¶
Below is the rest of the code you need to implement. You may need to create these methods, as the AppWizard-generated MFC application does not have some.
BOOL
GameApp::OnCmdMsg (UINT nID, int nCode,
void* pExtra, AFX_CMDHANDLERINFO*pHandlerInfo)
{
// yield to Python first - send to the main frame, as there is no Python app object.
if (glue.OnCmdMsg (m_pMainWnd, nID, nCode, pExtra, pHandlerInfo))
return TRUE;
else
return CWinApp::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
}
BOOL GameApp::PreTranslateMessage(MSG *pMsg)
{
if (glue.PreTranslateMessage(pMsg))
return TRUE;
else
return CWinApp::PreTranslateMessage(pMsg);
}
BOOL GameApp::OnIdle( LONG lCount )
{
// call base class idle first
if (CWinApp::OnIdle(lCount))
return TRUE;
return glue.OnIdle(lCount);
}
initscore.py
¶
Below is the code for initscore.py
. Obviously your code will vary, depending on your requirements.
1import sys
2import win32ui
3# First step - redirect python output to the debugging device, until we
4# can create a window to capture it.
5
6# Note that this is only useful while debugging, and simply sends any
7# Python output (ie, exceptions while developing the startup code) is
8# printed to the MSVC debugger. Note that since this code was written,
9# the win32trace module has appeared, giving another alternative to
10# debugging this code.
11class DebugOutput:
12 softspace=1
13 def write(self,message):
14 win32ui.OutputDebug(message)
15 sys.stderr=sys.stdout=DebugOutput()
16
17# One of your first priorities should be to set sys.stdout to somewhere useful,
18# depending on what useful means to your application. This code simply creates
19# the |Pythonwin| Interactive Window, which handles this automatically.
20
21# Now here is the code that does the real work.
22import win32con
23from pywin.framework import intpyapp, app
24
25class ScoreApp(intpyapp.InteractivePythonApp):
26 def InitInstance(self):
27 # Call the base class (if you want)
28 intpyapp.InteractivePythonApp.InitInstance(self)
29 # Do domething useful, specific to your app.
30 # Here, we minimise the interactive window.
31 # (after painting the main frame)
32 win32ui.PumpWaitingMessages()
33 interact.edit.currentView.GetParent().ShowWindow(win32con.SW_MINIMIZE)
34
35# def OnExitInstance(self):
36# return 0
37
38app = ScoreApp()
And we are done.