Hello wxPython MSW users,
Goal
My ultimate goal is to embed a MS Word editor in a wxPython application (I want Word to appear as one of the controls inside my wx.Frame
).
Background
Searching on the web it seems that this is indeed possible using something called Active X and/or COM components. (Pardon my naivete here, and in the rest of this long post: I typically do all my work on Linux and I have very little experience with MSW. So while am fairly experienced with wxWidgets and wxPython I know virtually nothing of MSW API’s, or the portions of wxPython that integrate with MSW API’s)
What I have learned and tried:
1. Launch Word in its own window from Python (this works)
As a first step, I am able to open a MS Word window on its own and send commands via API. For example using:
from time import sleep
import win32com.client as win32
RANGE = range(3, 8)
def word():
word = win32.gencache.EnsureDispatch('Word.Application')
doc = word.Documents.Add()
word.Visible = True
sleep(1)
rng = doc.Range(0,0)
rng.InsertAfter('Hacking Word with Python\r\n\r\n')
sleep(1)
for i in RANGE:
rng.InsertAfter('Line %d\r\n' % i)
sleep(1)
rng.InsertAfter("\r\nPython rules!\r\n")
doc.Close(False)
word.Application.Quit()
if __name__ == '__main__':
word()
(I did not write this: I found it on the web).
The program is self evident to me minus the magic win32.gencache.EnsureDispatch('Word.Application')
which I assume calls some Windows API to launch the word executable and get a handle to it to which we can make API calls. By the way, where can I find the documentation to that API?, or at least, what is it called? (If I knew the right terminology I could probably find the documentation).
2. Launch iewin
example (mostly works minus a recurring MessageTranslation exception)
My next step is to make that window appear inside a wxPython UI instead of on its own. From I have read, the wx.lib.activex
module is the preferred way to go about doing this. Is this correct? (There is not much documentation on the wxPython site about doing this and this module in particular. And all the posts I can find are ancient. I am hoping this means this module is really easy to use, not that it doesn’t work )
To begin with I am trying to run the iewin
sample which I found mentioned on the web. It sort of works but it raises lots of errors of this type.
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\site-packages\wx\lib\activex.py", line 155, in MSWTranslateMessage
res = self.ipao.TranslateAccelerator(msg)
ctypes.ArgumentError: argument 1: TypeError: wrong type
Am I missing something? (In case it helps I am running Windows 10 on a VM inside VirtualBox. The VM has Edge, not Internet Explorer, but I am assuming the API is compatible? (MSW is famous for backwards compatibility). I also do have MS Word 2010 installed in it.
(By the way, I have been looking at the code of activex.py
and I notice that, coincidentally, its last modification by Robin, from a couple of years ago, actually involved catching an OSError
in this same method MSWTranslateMessage
)
I have also found the flashwin
and pdfwin
examples. They both raise an error and don’t even open a window, but I am not surprised because I don’t think I have flash (is that still a thing?) and I definitely don’t have Adobe Reader installed. Anyhow the code in both looks analogous to that of iewin
.
3. Create a control that embeds MS Word (I am stuck)
Next. I have tried to create my own MSWord
control that I can use in my UI.
_progID = 'Word.Application'
import wx
import wx.lib.activex
class MSWord(wx.lib.activex.ActiveXCtrl):
def __init__(self, parent, ID=wx.ID_ANY,
pos=wx.DefaultPosition, size=wx.DefaultSize,
style=0):
super().__init__(parent, _progID, ID, pos, size, style)
This fails with
2023-02-10 18:44:34 INFO [comtypes.client._code_cache]: Could not import comtypes.gen, trying to create it.
2023-02-10 18:44:34 INFO [comtypes.client._code_cache]: Creating comtypes.gen package failed: [WinError 5] Access is denied: 'C:\\Program Files\\Python310\\lib\\site-packages\\comtypes\\gen'
2023-02-10 18:44:34 INFO [comtypes.client._code_cache]: Created a memory-only package.
2023-02-10 18:44:34 INFO [comtypes.client._code_cache]: Using writeable comtypes cache directory: 'C:\Users\labqa\AppData\Roaming\Python\Python310\comtypes_cache'
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\Program Files\Python310\lib\runpy.py", line 86, in _run_code
exec(code, run_globals)
File "Z:\meticy-client\src\meticy\devtools\widgets\msword.py", line 57, in <module>
TEST_FRAME = MSWordTestFrame(None, title="Meticy MS Word Editor", size=wx.Size(800, 600))
File "Z:\meticy-client\src\meticy\devtools\widgets\msword.py", line 28, in __init__
self.viewer = MSWord(self, wx.ID_ANY, wx.DefaultPosition)
File "Z:\meticy-client\src\meticy\ui\widgets\msword.py", line 30, in __init__
super().__init__(parent, _progID, ID, pos, size, style)
File "C:\Program Files\Python310\lib\site-packages\wx\lib\activex.py", line 110, in __init__
self.ipao = self._ax.QueryInterface(myole4ax.IOleInPlaceActiveObject)
File "C:\Program Files\Python310\lib\site-packages\comtypes\__init__.py", line 1197, in QueryInterface
self.__com_QueryInterface(byref(iid), byref(p))
_ctypes.COMError: (-2147467262, 'No such interface supported', (None, None, None, 0, None))
I notice that the iewin
example and the others invoke com.components.GetModule
passing some magic
registry key. Is it necessary? What does it do? Why does the program that opens Word in its own window not need it?
If a key is necessary to embed Word in wxPython, how does one know which key to use? From the pdfwin
sample I gather the key may depend on the version of MS Word installed. Is this so? Can the relevant key for the installed version be retrieved programmatically from python?
Thank you in advance for answering my beginner questions and pointing me in the right direction!