Merge pull request #81 from RobinD42/pr35-msw-module

Finish PR 35
This commit is contained in:
Robin Dunn
2016-05-14 23:51:46 -07:00
14 changed files with 284 additions and 30 deletions

1
.gitignore vendored
View File

@@ -17,6 +17,7 @@ mydbstub.py*
/license
/*.egg-info
/REV.txt
/.idea
/bin/sip-*
/bin/waf-*

View File

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

View File

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

68
etg/_msw.py Normal file
View File

@@ -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 <wxpy_api.h>')
module.addImport('_core')
module.addPyCode('import wx', order=10)
module.addInclude(INCLUDES)
# -----------------------------------------------------------------
# -----------------------------------------------------------------
tools.doCommonTweaks(module)
tools.runGenerators(module)
# ---------------------------------------------------------------------------
if __name__ == '__main__':
run()

View File

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

52
etg/metafile.py Normal file
View File

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

79
src/axbase.sip Normal file
View File

@@ -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);
};
//--------------------------------------------------------------------------

View File

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

View File

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

View File

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

27
wscript
View File

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

View File

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

View File

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