diff --git a/.gitignore b/.gitignore index dabbde1e..ff53a6fa 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ mydbstub.py* /license /*.egg-info /REV.txt +/.idea /bin/sip-* /bin/waf-* diff --git a/TODO.rst b/TODO.rst index 849948fd..577c6bdd 100644 --- a/TODO.rst +++ b/TODO.rst @@ -128,10 +128,11 @@ Other Dev Stuff * Add a _msw module that will contain classes and such that are only available in the Windows port: - * axbase (ActiveX.) - * wxCHMHelpController - * metafile - * access + * [done] axbase + * [done] metafile + * [] wxCHMHelpController + * [] access + * [] New activex classes in wx/msw/ole/activex.h ? * Any others? * Add _propdlg module diff --git a/demo/ActiveX_IEHtmlWindow.py b/demo/ActiveX_IEHtmlWindow.py index 86a40955..ef1c20db 100644 --- a/demo/ActiveX_IEHtmlWindow.py +++ b/demo/ActiveX_IEHtmlWindow.py @@ -66,7 +66,7 @@ class TestPanel(wx.Panel): btnSizer.Add(txt, 0, wx.CENTER|wx.ALL, 2) self.location = wx.ComboBox( - self, -1, "", style=wx.CB_DROPDOWN|wx.PROCESS_ENTER + self, -1, "", style=wx.CB_DROPDOWN|wx.TE_PROCESS_ENTER ) self.Bind(wx.EVT_COMBOBOX, self.OnLocationSelect, self.location) diff --git a/etg/_msw.py b/etg/_msw.py new file mode 100644 index 00000000..6fee7bd6 --- /dev/null +++ b/etg/_msw.py @@ -0,0 +1,68 @@ +# --------------------------------------------------------------------------- +# Name: etg/_msw.py +# Author: Dietmar Schwertberger +# +# Created: 13-Nov-2015 +# Copyright: (c) 2015-2016 by Total Control Software +# License: wxWindows License +# --------------------------------------------------------------------------- + +import etgtools +import etgtools.tweaker_tools as tools + +PACKAGE = "wx" +MODULE = "_msw" +NAME = "_msw" # Base name of the file to generate to for this script +DOCSTRING = "" + +# The classes and/or the basename of the Doxygen XML files to be processed by +# this script. +ITEMS = [] + +# The list of other ETG scripts and back-end generator modules that are +# included as part of this module. These items are in their own etg scripts +# for easier maintainability, but their class and function definitions are +# intended to be part of this module, not their own module. This also makes it +# easier to promote one of these to module status later if desired, simply +# remove it from this list of Includes, and change the MODULE value in the +# promoted script to be the same as its NAME. + +INCLUDES = ['metafile', + 'axbase', + ] + +# Separate the list into those that are generated from ETG scripts and the +# rest. These lists can be used from the build scripts to get a list of +# sources and/or additional dependencies when building this extension module. +ETGFILES = ['etg/%s.py' % NAME] + tools.getEtgFiles(INCLUDES) +DEPENDS = tools.getNonEtgFiles(INCLUDES) +OTHERDEPS = [] + + +# --------------------------------------------------------------------------- + +def run(): + # Parse the XML file(s) building a collection of Extractor objects + module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING) + etgtools.parseDoxyXML(module, ITEMS) + module.check4unittest = False + + # ----------------------------------------------------------------- + # Tweak the parsed meta objects in the module object as needed for + # customizing the generated code and docstrings. + + module.addHeaderCode('#include ') + module.addImport('_core') + module.addPyCode('import wx', order=10) + module.addInclude(INCLUDES) + + # ----------------------------------------------------------------- + # ----------------------------------------------------------------- + tools.doCommonTweaks(module) + tools.runGenerators(module) + + +# --------------------------------------------------------------------------- + +if __name__ == '__main__': + run() \ No newline at end of file diff --git a/etg/mediactrl.py b/etg/mediactrl.py index 7a23f762..6faa559a 100644 --- a/etg/mediactrl.py +++ b/etg/mediactrl.py @@ -49,6 +49,14 @@ def run(): # keep e.g. '(const wxString &fileName)' item.ignore() + # Transplant the docstrings from the ignored Load methods into the + # appropriate compatibility method + if 'proxy' in item.argsString: + m = c.find('LoadURIWithProxy') + else: + m = c.find('LoadURI') + m.briefDoc = item.briefDoc + m.detailedDoc = item.detailedDoc c = module.find('wxMediaEvent') diff --git a/etg/metafile.py b/etg/metafile.py new file mode 100644 index 00000000..3cfd7570 --- /dev/null +++ b/etg/metafile.py @@ -0,0 +1,52 @@ +#--------------------------------------------------------------------------- +# Name: etg/metafile.py +# Author: Robin Dunn +# Dietmar Schwertberger +# +# Created: 01-Nov-2015 +# Copyright: (c) 2015 by Wide Open Technologies +# License: wxWindows License +#--------------------------------------------------------------------------- + +import etgtools +import etgtools.tweaker_tools as tools + +PACKAGE = "wx" +MODULE = "_core" +NAME = "metafile" # Base name of the file to generate to for this script +DOCSTRING = "" + +# The classes and/or the basename of the Doxygen XML files to be processed by +# this script. +ITEMS = [ 'wxMetafile', + 'wxMetafileDC', + ] + + +#--------------------------------------------------------------------------- + +def run(): + # Parse the XML file(s) building a collection of Extractor objects + module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING) + etgtools.parseDoxyXML(module, ITEMS) + + #----------------------------------------------------------------- + # Tweak the parsed meta objects in the module object as needed for + # customizing the generated code and docstrings. + + c = module.find('wxMetafile') + c.addPrivateCopyCtor() + + c = module.find('wxMetafileDC') + c.addPrivateCopyCtor() + + module.find("wxMakeMetafilePlaceable").ignore() + + #----------------------------------------------------------------- + tools.doCommonTweaks(module) + tools.runGenerators(module) + + +#--------------------------------------------------------------------------- +if __name__ == '__main__': + run() diff --git a/ext/wxWidgets b/ext/wxWidgets index e44418ba..a36e071c 160000 --- a/ext/wxWidgets +++ b/ext/wxWidgets @@ -1 +1 @@ -Subproject commit e44418ba2da041805536a842ba165e9c61ac8625 +Subproject commit a36e071cd4d63d8d7d03a1d300b619f1db61a409 diff --git a/src/axbase.sip b/src/axbase.sip new file mode 100644 index 00000000..d0b58ec0 --- /dev/null +++ b/src/axbase.sip @@ -0,0 +1,79 @@ +//-------------------------------------------------------------------------- +// Name: src/axbase.sip +// Purpose: A wxWindow class and wrapper interface that virtualizes +// MSWTranslateMessage for use in ActiveX Controls. See _axbase in Classic +// +// Author: Robin Dunn +// +// Created: 13-May-2016 +// Copyright: (c) 2016 by Total Control Software +// Licence: wxWindows license +//-------------------------------------------------------------------------- + + +%ModuleHeaderCode +class wxPyAxBaseWindow : public wxWindow +{ + DECLARE_DYNAMIC_CLASS(wxPyAxBaseWindow) + +public: + wxPyAxBaseWindow(wxWindow* parent, const wxWindowID id=-1, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0, + const wxString& name = wxPanelNameStr) + : wxWindow(parent, id, pos, size, style, name) {} + + wxPyAxBaseWindow() : wxWindow() {} + + virtual bool MSWTranslateMessage(WXMSG* msg) + { + return wxWindow::MSWTranslateMessage(msg); + } +}; + +wxPyAxBaseWindow* wxPyAxBaseWindow_FromHWND(wxWindow* parent, unsigned long _hWnd); +%End + +//-------------------------------------------------------------------------- + + +%ModuleCode +IMPLEMENT_DYNAMIC_CLASS(wxPyAxBaseWindow, wxWindow); + +wxPyAxBaseWindow* wxPyAxBaseWindow_FromHWND(wxWindow* parent, unsigned long _hWnd) +{ + WXHWND hWnd = (WXHWND)_hWnd; + wxPyAxBaseWindow* win = new wxPyAxBaseWindow; + if (parent) + parent->AddChild(win); + win->SetEventHandler(win); + win->SetHWND(hWnd); + win->SubclassWin(hWnd); + win->AdoptAttributesFromHWND(); + win->SetupColours(); + return win; +} +%End + + +//-------------------------------------------------------------------------- + +typedef void WXMSG; + + +class wxPyAxBaseWindow : wxWindow +{ +public: + wxPyAxBaseWindow(wxWindow* parent, const wxWindowID id=-1, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0, + const wxString& name = wxPanelNameStr); + wxPyAxBaseWindow(); + virtual bool MSWTranslateMessage(WXMSG* msg); +}; + +//-------------------------------------------------------------------------- + + diff --git a/src/stockgdi.sip b/src/stockgdi.sip index b6892a7c..cf159ec1 100644 --- a/src/stockgdi.sip +++ b/src/stockgdi.sip @@ -1,6 +1,6 @@ //-------------------------------------------------------------------------- // Name: src/stockobj.sip -// Purpose: Wraper definition and some Python code for wxStockGDI and +// Purpose: Wrapper definition and some Python code for wxStockGDI and // using it to initialize the stock objects like wx.BLUE_BRUSH, // etc. We're putting it here because wxStockGDI is not // documented, and probably should not be. diff --git a/unittests/test_mediactrl.py b/unittests/test_mediactrl.py index a20a17fa..99187e47 100644 --- a/unittests/test_mediactrl.py +++ b/unittests/test_mediactrl.py @@ -33,7 +33,8 @@ class mediactrl_Tests(wtc.WidgetTestCase): def test_mediactrl4(self): evt = wx.media.MediaEvent() - + + def test_mediactrl5(self): wx.media.wxEVT_MEDIA_LOADED wx.media.wxEVT_MEDIA_STOP diff --git a/unittests/test_metafile.py b/unittests/test_metafile.py new file mode 100644 index 00000000..e1729b83 --- /dev/null +++ b/unittests/test_metafile.py @@ -0,0 +1,31 @@ +import unittest +import wtc +import wx +import wx.msw + + +#--------------------------------------------------------------------------- + +class MetafileDCTests(wtc.WidgetTestCase): + + @unittest.skipIf('wxMSW' not in wx.PlatformInfo, "Metafile classes only imsplemented on Windows") + def test_MetafileDC1(self): + # Not testing with output file because it is not released soon enough + # to be able to delete the file in this test, resulting in permission + # errors. + dc = wx.msw.MetafileDC() + dc.DrawLine(0,0, 50,50) + metafile = dc.Close() + del dc + + self.assertTrue(isinstance(metafile, wx.msw.Metafile)) + metafile.SetClipboard(50,50) + metafile.Play(wx.ClientDC(self.frame)) + del metafile + + +#--------------------------------------------------------------------------- + + +if __name__ == '__main__': + unittest.main() diff --git a/wscript b/wscript index 26f129a6..c278308e 100644 --- a/wscript +++ b/wscript @@ -225,10 +225,10 @@ def configure(conf): # ** Add code for new modules here - # NOTE: This assumes that if the platform is not win32 (from - # the test above) and not darwin then we must be using the - # GTK2 port of wxWidgets. If we ever support other ports then - # this code will need to be adjusted. + # NOTE: This assumes that if the platform is not win32 (from the test + # above) and not darwin then we must be using the GTK2 or GTK3 port of + # wxWidgets. If we ever support other ports then this code will need + # to be adjusted. if not isDarwin: if conf.options.gtk3: gtkflags = os.popen('pkg-config gtk+-3.0 --cflags', 'r').read()[:-1] @@ -288,8 +288,9 @@ from waflib.Configure import conf @conf def my_check_python_headers(conf): """ - Check for headers and libraries necessary to extend or embed python by using the module *distutils*. - On success the environment variables xxx_PYEXT and xxx_PYEMBED are added: + Check for headers and libraries necessary to extend or embed python by + using the module *distutils*. On success the environment variables + xxx_PYEXT and xxx_PYEMBED are added: * PYEXT: for compiling python extensions * PYEMBED: for embedding a python interpreter @@ -403,7 +404,7 @@ def my_check_python_headers(conf): def build(bld): # Ensure that the directory containing this script is on the python - # path for spawned commands so the builder and phoenix packages can be + # sys.path for spawned commands so the builder and phoenix packages can be # found. thisdir = os.path.abspath(".") sys.path.insert(0, thisdir) @@ -578,6 +579,15 @@ def build(bld): ) makeExtCopyRule(bld, '_media') + if isWindows: + etg = loadETG('etg/_msw.py') + bld(features = 'c cxx cxxshlib pyext', + target = makeTargetName(bld, '_msw'), + source = getEtgSipCppFiles(etg) + rc, + uselib = 'WX WXPY', + ) + makeExtCopyRule(bld, '_msw') + # ** Add code for new modules here @@ -621,8 +631,7 @@ def copyFileToPkg(task): from buildtools.config import opj src = task.inputs[0].abspath() tgt = task.outputs[0].abspath() - #task.exec_command('touch %s' % tgt) - open(tgt, "wb").close() # 'touch' + open(tgt, "wb").close() # essentially just a unix 'touch' command tgt = opj(cfg.PKGDIR, os.path.basename(src)) copy_file(src, tgt, verbose=1) return 0 diff --git a/wx/lib/activex.py b/wx/lib/activex.py index 5ff72c98..c83d6680 100644 --- a/wx/lib/activex.py +++ b/wx/lib/activex.py @@ -29,6 +29,7 @@ this: """ import wx +import wx.msw import ctypes as ct import ctypes.wintypes as wt @@ -57,13 +58,13 @@ WM_DESTROY = 2 #------------------------------------------------------------------------------ -class ActiveXCtrl(wx.PyAxBaseWindow): +class ActiveXCtrl(wx.msw.PyAxBaseWindow): """ A wx.Window for hosting ActiveX controls. The COM interface of the ActiveX control is accessible through the ctrl property of this class, and this class is also set as the event sink for COM events originating from the ActiveX control. In other words, to - catch the COM events you mearly have to derive from this class and + catch the COM events you merely have to derive from this class and provide a method with the correct name. See the comtypes package documentation for more details. """ @@ -90,7 +91,7 @@ class ActiveXCtrl(wx.PyAxBaseWindow): # create the control atl.AtlAxWinInit() hInstance = kernel32.GetModuleHandleA(None) - hwnd = user32.CreateWindowExA(0, "AtlAxWin", axID, + hwnd = user32.CreateWindowExA(0, b"AtlAxWin", axID.encode("ASCII"), WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, x,y, w,h, parent.GetHandle(), None, @@ -112,10 +113,12 @@ class ActiveXCtrl(wx.PyAxBaseWindow): self._evt_connections = [] self.AddEventSink(self) - # Turn the window handle into a wx.Window and set this object to be that window - win = wx.PyAxBaseWindow_FromHWND(parent, hwnd) - self.PostCreate(win) + wx.Window.__init__(self, parent, wxid, pos, size, style, name) + # Turn the window handle into a wx.Window and set this object to be that window + #win = wx.PyAxBaseWindow_FromHWND(parent, hwnd) + self.AssociateHandle(hwnd) + # Set some wx.Window properties if wxid == wx.ID_ANY: wxid = wx.Window.NewControlId() @@ -126,14 +129,16 @@ class ActiveXCtrl(wx.PyAxBaseWindow): self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroyWindow) - + + def AddEventSink(self, sink, interface=None): """ Add a new target to search for method names that match the COM Event names. """ self._evt_connections.append(cc.GetEvents(self._ax, sink, interface)) - + + def GetCtrl(self): """Easy access to the COM interface for the ActiveX Control""" return self._ax @@ -151,7 +156,7 @@ class ActiveXCtrl(wx.PyAxBaseWindow): if res == hr.S_OK: return True else: - return wx.PyAxBaseWindow.MSWTranslateMessage(self, msg) + return super(ActiveXCtrl, self).MSWTranslateMessage(msg) # TBD: Are the focus handlers needed? diff --git a/wx/lib/activexwrapper.py b/wx/lib/activexwrapper.py index ec7d626d..29325557 100644 --- a/wx/lib/activexwrapper.py +++ b/wx/lib/activexwrapper.py @@ -90,8 +90,7 @@ def MakeActiveXClass(CoClass, eventClass=None, eventObj=None): } # make a new class object - import new - classObj = new.classobj(className, baseClasses, classDict) + classObj = type(className, baseClasses, classDict) return classObj @@ -103,7 +102,7 @@ def axw__init__(self, parent, ID=-1, pos=wx.DefaultPosition, size=wx.DefaultSize # init base classes pywin.mfc.activex.Control.__init__(self) wx.Window.__init__( self, parent, -1, pos, size, style|wx.NO_FULL_REPAINT_ON_RESIZE) - self.this.own(False) # this should be set in wx.Window.__init__ when it calls _setOORInfo, but... + #self.this.own(False) # this should be set in wx.Window.__init__ when it calls _setOORInfo, but... win32ui.EnableControlContainer() self._eventObj = self._eventObj # move from class to instance @@ -126,7 +125,7 @@ def axw__init__(self, parent, ID=-1, pos=wx.DefaultPosition, size=wx.DefaultSize def axw__getattr__(self, attr): try: return pywin.mfc.activex.Control.__getattr__(self, attr) - except AttributeError: + except (AttributeError, win32ui.error): try: eo = self.__dict__['_eventObj'] return getattr(eo, attr)