From 9a7f1269c1ef78ee1563c0b1065f526c5aaf4bb0 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 7 Dec 2010 02:58:21 +0000 Subject: [PATCH] Generate CppMethods as a separate function that is called from the wrapper, instead of embedding it in the wrapper itself. This helps us be less SIP-specific and also be able to do things like 'return' the value and use 'self->' instead of having to use variable names that make less sense. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@66348 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- etg/app.py | 3 +- etg/colour.py | 32 ++++---- etg/event.py | 12 +-- etg/font.py | 25 +++++-- etg/gdicmn.py | 24 +++--- etg/geometry.py | 11 ++- etg/object.py | 4 +- etg/region.py | 4 +- etg/window.py | 89 ++++++++++++++++++---- etgtools/extractors.py | 60 +++++++++++++-- etgtools/sip_generator.py | 153 +++++++++++++++++++++++++++++++------- etgtools/tweaker_tools.py | 17 ++++- 12 files changed, 333 insertions(+), 101 deletions(-) diff --git a/etg/app.py b/etg/app.py index e9cb89e5..2c0ad2ba 100644 --- a/etg/app.py +++ b/etg/app.py @@ -62,7 +62,6 @@ def run(): # platforms, and other goodies, then change the c.name so SIP will # generate code wrapping this class as if it was the wxApp class seen in # the DoxyXML. - ##c.addCppCode(wxPyApp) c.insertCppCode('src/app_ex.cpp') for item in c.allItems(): # change the class name, ctors and dtor names @@ -84,7 +83,7 @@ def run(): doc="Hide all application windows just as the user can do with the\nsystem Hide command. Mac only.", body="""\ #ifdef __WXMAC__ - sipCpp->MacHideApp(); + self->MacHideApp(); #endif """) diff --git a/etg/colour.py b/etg/colour.py index ec66967a..63a634e2 100644 --- a/etg/colour.py +++ b/etg/colour.py @@ -36,18 +36,18 @@ def run(): #ifdef __WXMAC__ #include #endif - """) - - module.addCppFunction('wxColour', 'MacThemeColour', '(int themeBrushID)', """\ + """) + module.addCppFunction('wxColour*', 'MacThemeColour', '(int themeBrushID)', """\ #ifdef __WXMAC__ - sipRes = new wxColour(wxMacCreateCGColorFromHITheme(themeBrushID)); + return new wxColour(wxMacCreateCGColorFromHITheme(themeBrushID)); #else wxPyRaiseNotImplemented(); sipIsErr = 1; - sipRes = NULL; + return NULL; #endif """, factory=True) + # Change this macro into a value so we wont have problems when SIP takes its # address module.addCppCode("""\ @@ -93,7 +93,7 @@ def run(): c.find('GetPixel').ignore() # We need to add a typcast c.addCppMethod('wxIntPtr', 'GetPixel', '()', """\ - sipRes = (wxIntPtr)sipCpp->GetPixel(); + return (wxIntPtr)self->GetPixel(); """) # Set a flag on the return value and parameter types that are 'unsigned char' @@ -123,23 +123,23 @@ def run(): c.find('MakeMono.b').out = True - c.addCppMethod('SIP_PYOBJECT', 'Get', '(bool includeAlpha=true)', """\ + c.addCppMethod('PyObject*', 'Get', '(bool includeAlpha=true)', """\ int red = -1; int green = -1; int blue = -1; int alpha = wxALPHA_OPAQUE; - if (sipCpp->IsOk()) { - red = sipCpp->Red(); - green = sipCpp->Green(); - blue = sipCpp->Blue(); - alpha = sipCpp->Alpha(); + if (self->IsOk()) { + red = self->Red(); + green = self->Green(); + blue = self->Blue(); + alpha = self->Alpha(); } if (includeAlpha) - sipRes = sipBuildResult(&sipIsErr, "(iiii)", red, green, blue, alpha); + return sipBuildResult(&_isErr, "(iiii)", red, green, blue, alpha); else - sipRes = sipBuildResult(&sipIsErr, "(iii)", red, green, blue); + return sipBuildResult(&_isErr, "(iii)", red, green, blue); """, briefDoc="""\ - Get(includeAlpha=Valse) -> (r,g,b) or (r,g,b,a)\n + Get(includeAlpha=False) -> (r,g,b) or (r,g,b,a)\n Returns the RGB intensity values as a tuple, optionally the alpha value as well.""") @@ -152,7 +152,7 @@ def run(): c.addPyMethod('__getitem__', '(self, idx)', 'return self.Get()[idx]') c.addPyMethod('__setitem__', '(self, idx, val)', """\ - if idx == 0: self.red = val + if idx == 0: self.red = val elif idx == 1: self.green = val elif idx == 2: self.blue = val elif idx == 3: self.alpha = val diff --git a/etg/event.py b/etg/event.py index 0d6054fe..cda76707 100644 --- a/etg/event.py +++ b/etg/event.py @@ -133,20 +133,20 @@ def run(): c.find('GetClientData').ignore() c.find('SetClientData').ignore() - c.addCppMethod('SIP_PYOBJECT', 'GetClientData', '()', """\ - wxPyClientData* data = (wxPyClientData*)sipCpp->GetClientObject(); + c.addCppMethod('PyObject*', 'GetClientData', '()', """\ + wxPyClientData* data = (wxPyClientData*)self->GetClientObject(); if (data) { Py_INCREF(data->m_obj); - sipRes = data->m_obj; + return data->m_obj; } else { Py_INCREF(Py_None); - sipRes = Py_None; + return Py_None; } """) - c.addCppMethod('void', 'SetClientData', '(SIP_PYOBJECT clientData)', """\ + c.addCppMethod('void', 'SetClientData', '(PyObject* clientData)', """\ wxPyClientData* data = new wxPyClientData(clientData); - sipCpp->SetClientObject(data); + self->SetClientObject(data); """) diff --git a/etg/font.py b/etg/font.py index 3b89b9d3..ad2b6a05 100644 --- a/etg/font.py +++ b/etg/font.py @@ -48,19 +48,33 @@ def run(): assert isinstance(c, etgtools.ClassDef) tools.removeVirtuals(c) - c.addCppCtor("""( + # TODO: Since the TypeCode is inserted before the derived class + # (sipwxFont) is defined, we can't use the new version of addCppCtor. Can + # this be fixed? + c.addCppCtor_sip("""( int pointSize, wxFontFamily family, int flags = wxFONTFLAG_DEFAULT, - const wxString & faceName = wxEmptyString, - wxFontEncoding encoding = wxFONTENCODING_DEFAULT - )""", + const wxString& faceName = wxEmptyString, + wxFontEncoding encoding = wxFONTENCODING_DEFAULT )""", body="""\ - // YUCK! wxFont* font = wxFont::New(pointSize, family, flags, *faceName, encoding); sipCpp = new sipwxFont(*font); delete font; """) + + # Same as the above, but as a factory function + module.addCppFunction('wxFont*', 'FFont', """( + int pointSize, + wxFontFamily family, + int flags = wxFONTFLAG_DEFAULT, + const wxString& faceName = wxEmptyString, + wxFontEncoding encoding = wxFONTENCODING_DEFAULT )""", + body="""\ + wxFont* font = wxFont::New(pointSize, family, flags, *faceName, encoding); + return font; + """, factory=True) + for item in c.findAll('New'): item.factory = True @@ -76,6 +90,7 @@ def run(): c.addProperty('Underlined GetUnderlined SetUnderlined') c.addProperty('Weight GetWeight SetWeight') + #----------------------------------------------------------------- tools.doCommonTweaks(module) diff --git a/etg/gdicmn.py b/etg/gdicmn.py index 8ecea81f..1d28874b 100644 --- a/etg/gdicmn.py +++ b/etg/gdicmn.py @@ -104,8 +104,8 @@ def run(): # wxPoint typemap c.convertFromPyObject = tools.convertTwoIntegersTemplate('wxPoint') - c.addCppMethod('SIP_PYOBJECT', 'Get', '()', """\ - sipRes = sipBuildResult(&sipIsErr, "(ii)", sipCpp->x, sipCpp->y); + c.addCppMethod('PyObject*', 'Get', '()', """\ + return sipBuildResult(&_isErr, "(ii)", self->x, self->y); """, briefDoc="""\ Get() -> (x,y)\n Return the x and y properties as a tuple.""") @@ -157,8 +157,8 @@ def run(): # wxSize typemap c.convertFromPyObject = tools.convertTwoIntegersTemplate('wxSize') - c.addCppMethod('SIP_PYOBJECT', 'Get', '()', """\ - sipRes = sipBuildResult(&sipIsErr, "(ii)", sipCpp->GetWidth(), sipCpp->GetHeight()); + c.addCppMethod('PyObject*', 'Get', '()', """\ + return sipBuildResult(&_isErr, "(ii)", self->GetWidth(), self->GetHeight()); """, briefDoc="""\ Get() -> (width, height)\n Return the width and height properties as a tuple.""") @@ -183,7 +183,8 @@ def run(): #--------------------------------------- # wxRect tweaks c = module.find('wxRect') - + assert isinstance(c, etgtools.ClassDef) + c.addProperty("left GetLeft") c.addProperty("top GetTop") c.addProperty("right GetRight") @@ -228,9 +229,9 @@ def run(): # wxRect typemap c.convertFromPyObject = tools.convertFourIntegersTemplate('wxRect') - c.addCppMethod('SIP_PYOBJECT', 'Get', '()', """\ - sipRes = sipBuildResult(&sipIsErr, "(iiii)", - sipCpp->x, sipCpp->y, sipCpp->width, sipCpp->height); + c.addCppMethod('PyObject*', 'Get', '()', """\ + return sipBuildResult(&_isErr, "(iiii)", + self->x, self->y, self->width, self->height); """, briefDoc="""\ Get() -> (x, y, width, height)\n Return the rectangle's properties as a tuple.""") @@ -277,10 +278,9 @@ def run(): # wxRealPoint typemap c.convertFromPyObject = tools.convertTwoDoublesTemplate('wxRealPoint') - - c.addCppMethod('SIP_PYOBJECT', 'Get', '()', """\ - sipRes = sipBuildResult(&sipIsErr, "(dd)", - sipCpp->x, sipCpp->y); + + c.addCppMethod('PyObject*', 'Get', '()', """\ + return sipBuildResult(&_isErr, "(dd)", self->x, self->y); """, briefDoc="""\ Get() -> (x, y, width, height)\n Return the rectangle's properties as a tuple.""") diff --git a/etg/geometry.py b/etg/geometry.py index 7c4452e0..bf7f764f 100644 --- a/etg/geometry.py +++ b/etg/geometry.py @@ -59,8 +59,8 @@ def run(): c.convertFromPyObject = tools.convertTwoDoublesTemplate('wxPoint2DDouble') - c.addCppMethod('SIP_PYOBJECT', 'Get', '()', """\ - sipRes = sipBuildResult(&sipIsErr, "(dd)", sipCpp->m_x, sipCpp->m_y); + c.addCppMethod('PyObject*', 'Get', '()', """\ + return sipBuildResult(&_isErr, "(dd)", self->m_x, self->m_y); """, briefDoc="""\ Get() -> (x,y)\n Return the x and y properties as a tuple.""") @@ -93,7 +93,6 @@ def run(): item.ignore() - c = module.find('wxRect2DDouble') c.pyName = 'Rect2D' c.find('m_x').pyName = 'x' @@ -103,9 +102,9 @@ def run(): c.convertFromPyObject = tools.convertFourDoublesTemplate('wxRect2DDouble') - c.addCppMethod('SIP_PYOBJECT', 'Get', '()', """\ - sipRes = sipBuildResult(&sipIsErr, "(dddd)", - sipCpp->m_x, sipCpp->m_y, sipCpp->m_width, sipCpp->m_height); + c.addCppMethod('PyObject*', 'Get', '()', """\ + return sipBuildResult(&_isErr, "(dddd)", + self->m_x, self->m_y, self->m_width, self->m_height); """, briefDoc="""\ Get() -> (x, y, width, height)\n Return the rectangle's properties as a tuple.""") diff --git a/etg/object.py b/etg/object.py index 689f72d7..a2e8d3ca 100644 --- a/etg/object.py +++ b/etg/object.py @@ -52,11 +52,11 @@ def run(): assert isinstance(c, etgtools.ClassDef) c.addCppMethod('const wxChar*', 'GetClassName', '()', - body='sipRes = sipCpp->GetClassInfo()->GetClassName();', + body='return self->GetClassInfo()->GetClassName();', doc='Returns the class name of the C++ class using wxRTTI.') c.addCppMethod('void', 'Destroy', '()', - body='delete sipCpp;', + body='delete self;', doc='Deletes the C++ object this Python object is a proxy for.', transferThis=True) # TODO: Check this diff --git a/etg/region.py b/etg/region.py index adf1f2f4..2d5898dc 100644 --- a/etg/region.py +++ b/etg/region.py @@ -42,8 +42,8 @@ def run(): c = module.find('wxRegionIterator') c.find('operator++').ignore() - c.addCppMethod('void', 'Next', '()', 'sipCpp->operator++();') - c.addCppMethod('int', '__nonzero__', '()', 'sipRes = (int)sipCpp->operator bool();') + c.addCppMethod('void', 'Next', '()', 'self->operator++();') + c.addCppMethod('int', '__nonzero__', '()', 'return (int)self->operator bool();') c.addProperty('H GetH') c.addProperty('Height GetHeight') diff --git a/etg/window.py b/etg/window.py index fe22ccd4..55d4f3bc 100644 --- a/etg/window.py +++ b/etg/window.py @@ -20,6 +20,9 @@ DOCSTRING = "" ITEMS = [ 'wxVisualAttributes', 'wxWindow' ] +OTHERDEPS = [ 'src/window_ex.cpp', # some helper C++ code + ] + #--------------------------------------------------------------------------- def run(): @@ -30,10 +33,12 @@ def run(): #----------------------------------------------------------------- # Tweak the parsed meta objects in the module object as needed for # customizing the generated code and docstrings. - + c = module.find('wxWindow') assert isinstance(c, etgtools.ClassDef) + c.insertCppCode('src/window_ex.cpp') + # ignore some overloads that will be ambiguous afer wrapping c.find('GetChildren').overloads = [] c.find('GetClientSize').findOverload('int *').ignore() @@ -44,6 +49,10 @@ def run(): c.find('ClientToScreen').findOverload('int *').ignore() c.find('ScreenToClient').findOverload('int *').ignore() + # Rename these overloads for symmetry with the getters of the same name + c.find('SetSize').findOverload('wxRect').pyName = 'SetRect' + c.find('SetClientSize').findOverload('wxRect').pyName = 'SetClientRect' + m = c.find('GetTextExtent').findOverload('int *') m.pyName = 'GetFullTextExtent' m.find('w').out = True @@ -52,32 +61,82 @@ def run(): m.find('externalLeading').out = True c.find('GetHandle').type = 'void*' + c.find('GetHandle').setCppCode("sipRes = wxPyGetWinHandle(sipCpp);") - # Rename these overloads for symmetry with the getters of the same name - c.find('SetSize').findOverload('wxRect').pyName = 'SetRect' - c.find('SetClientSize').findOverload('wxRect').pyName = 'SetClientRect' + c.addCppMethod('void*', 'GetGtkWidget', '()', """\ + #ifdef __WXGTK__ + return (void*)self->GetHandle(); + #else + return NULL; + #endif + """) - # Add this method + # Add some new methods c.addCppMethod('wxWindow*', 'GetTopLevelParent', '()', - 'sipRes = wxGetTopLevelParent(sipCpp);') + 'return wxGetTopLevelParent(self);') + #c.addCppMethod('wxWindow*', 'FindWindowByLabel', '(const wxString& label)', + # 'return wxWindow::FindWindowByLabel(label, self);') - # TODO: make these be available on Windows, and empty stubs otherwise - c.find('RegisterHotKey').ignore() - c.find('UnregisterHotKey').ignore() + c.addCppMethod('bool', 'MacIsWindowScrollbar', '(const wxWindow* sb)', """\ + #ifdef __WXMAC__ + return self->MacIsWindowScrollbar(sb); + #else + return false; + #endif + """) + + # Make these be available on Windows, and empty stubs otherwise + c.find('RegisterHotKey').setCppCode("""\ + #ifdef __WXMSW__ + sipRes = sipCpp->RegisterHotKey(hotkeyId, modifiers, virtualKeyCode); + #else + sipRes = false; + #endif + """) + c.find('UnregisterHotKey').setCppCode("""\ + #ifdef __WXMSW__ + sipRes = sipCpp->UnregisterHotKey(hotkeyId); + #else + sipRes = false; + #endif + """) + c.find('RegisterHotKey').isVirtual = False + c.find('UnregisterHotKey').isVirtual = False - # maybe these too + c.find('SetDoubleBuffered').setCppCode("""\ + #if defined(__WXGTK20__) || defined(__WXMSW__) + sipCpp->SetDoubleBuffered(on); + #endif + """) + + #%Rename(ConvertDialogPointToPixels, wxPoint, ConvertDialogToPixels(const wxPoint& pt)); + #%Rename(ConvertDialogSizeToPixels, wxSize, ConvertDialogToPixels(const wxSize& sz)); + #%Rename(ConvertPixelPointToDialog, wxPoint, ConvertPixelsToDialog(const wxPoint& pt)); + #%Rename(ConvertPixelSizeToDialog, wxSize, ConvertPixelsToDialog(const wxSize& sz)); + + # MSW only. Do we want them wrapped? c.find('GetAccessible').ignore() c.find('SetAccessible').ignore() - # Make some of the protected methods overridable from Python + + # Make some of the protected methods visible and overridable from Python c.find('DoCentre').ignore(False) c.find('DoGetBestSize').ignore(False) c.find('SetInitialBestSize').ignore(False) c.find('SendDestroyEvent').ignore(False) c.find('ProcessEvent').ignore(False) + c.addPyMethod('PostCreate', '()', 'pass') - # Define some properties using the getters and setters + # transfer ownership of these parameters to the C++ object + c.find('SetCaret.caret').transfer = True + c.find('SetToolTip.tip').transfer = True + c.find('SetDropTarget.target').transfer = True + c.find('SetConstraints.constraints').transfer = True + c.find('SetSizer.sizer').transfer = True + c.find('SetSizerAndFit.sizer').transfer = True + + # Define some properties using the getter and setter methods c.addProperty('AcceleratorTable GetAcceleratorTable SetAcceleratorTable') c.addProperty('AutoLayout GetAutoLayout SetAutoLayout') c.addProperty('BackgroundColour GetBackgroundColour SetBackgroundColour') @@ -143,9 +202,9 @@ def run(): c.addProperty('MaxClientSize GetMaxClientSize SetMaxClientSize') - - - + tools.fixWindowClass(c) + + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/etgtools/extractors.py b/etgtools/extractors.py index 7fbcc357..56bed619 100644 --- a/etgtools/extractors.py +++ b/etgtools/extractors.py @@ -87,8 +87,6 @@ class BaseDef(object): raise ExtractorError("Unable to find item named '%s' within %s named '%s'" % (head, self.__class__.__name__, self.name)) - - def addItem(self, item): self.items.append(item) @@ -155,6 +153,7 @@ class VariableDef(BaseDef): self.type = None self.definition = '' self.argsString = '' + self.pyInt = False self.__dict__.update(**kw) if element is not None: self.extract(element) @@ -230,7 +229,7 @@ class FunctionDef(BaseDef): self.extract(element) def releaseGIL(self, release=True): - self.pyReleaseGIL = hold + self.pyReleaseGIL = release def extract(self, element): super(FunctionDef, self).extract(element) @@ -450,6 +449,9 @@ class ClassDef(BaseDef): return p + + #------------------------------------------------------------------ + def addCppMethod(self, type, name, argsString, body, doc=None, **kw): """ Add a new C++ method to a class. This method doesn't have to actually @@ -457,20 +459,42 @@ class ClassDef(BaseDef): back-end wrapper generator such that it is visible in the class in the target language. """ - md = CppMethodDef(type, name, argsString, body, doc, **kw) + md = CppMethodDef(type, name, argsString, body, doc, klass=self, **kw) self.items.append(md) return md - def addCppCtor(self, argsString, body, doc=None, noDerivedCtor=True, **kw): + def addCppCtor(self, argsString, body, doc=None, noDerivedCtor=True, useDerivedName=False, **kw): """ Add a C++ method that is a constructor. """ md = CppMethodDef('', self.name, argsString, body, doc=doc, - isCtor=True, noDerivedCtor=noDerivedCtor, **kw) + isCtor=True, klass=self, noDerivedCtor=noDerivedCtor, + useDerivedName=useDerivedName, **kw) self.items.append(md) return md - + + + def addCppMethod_sip(self, type, name, argsString, body, doc=None, **kw): + """ + Just like the above but can do more things that are SIP specific in + the code body, instead of using the general purpose implementation. + """ + md = CppMethodDef_sip(type, name, argsString, body, doc, klass=self, **kw) + self.items.append(md) + return md + + def addCppCtor_sip(self, argsString, body, doc=None, noDerivedCtor=True, **kw): + """ + Add a C++ method that is a constructor. + """ + md = CppMethodDef_sip('', self.name, argsString, body, doc=doc, + isCtor=True, klass=self, noDerivedCtor=noDerivedCtor, **kw) + self.items.append(md) + return md + + #------------------------------------------------------------------ + def addPyMethod(self, name, argsString, body, doc=None, **kw): """ @@ -627,9 +651,20 @@ class CppMethodDef(MethodDef): self.body = body self.briefDoc = doc self.protection = 'public' + self.klass = None + self.noDerivedCtor = False self.__dict__.update(kw) +class CppMethodDef_sip(CppMethodDef): + """ + Just like the above, but instead of generating a new function from the + privided code, the code is used inline inside SIP's %MethodCode directive. + This makes it possible to use additional SIP magic for things that are + beyond the general scope of the other C++ Method implementation. + """ + pass + #--------------------------------------------------------------------------- @@ -784,7 +819,6 @@ class ModuleDef(BaseDef): return item - def addCppFunction(self, type, name, argsString, body, doc=None, **kw): """ @@ -795,6 +829,16 @@ class ModuleDef(BaseDef): self.items.append(md) return md + + def addCppFunction_sip(self, type, name, argsString, body, doc=None, **kw): + """ + Add a new C++ function into the module that is written by hand, not + wrapped. + """ + md = CppMethodDef_sip(type, name, argsString, body, doc, **kw) + self.items.append(md) + return md + def addPyCode(self, code, order=None): """ diff --git a/etgtools/sip_generator.py b/etgtools/sip_generator.py index ec3674a0..a698dba5 100644 --- a/etgtools/sip_generator.py +++ b/etgtools/sip_generator.py @@ -12,7 +12,7 @@ The generator class for creating SIP definition files from the data objects produced by the ETG scripts. """ -import sys, os +import sys, os, re import extractors import generators from cStringIO import StringIO @@ -53,7 +53,7 @@ class SipWrapperGenerator(generators.WrapperGeneratorBase): %%Module(name=%s.%s, use_argument_names=True, language="C++") { %%AutoPyName(remove_leading="wx") -} +}; %%Copying Copyright: (c) 2010 by Total Control Software @@ -92,12 +92,15 @@ from %s import * stream.write("%End\n\n") # %Imports and %Includes - for i in module.imports: - stream.write("%%Import %s.sip\n" % i) - stream.write("\n") - for i in module.includes: - stream.write("%%Include %s.sip\n" % i) - + if module.imports: + for i in module.imports: + stream.write("%%Import %s.sip\n" % i) + stream.write("\n") + if module.includes: + for i in module.includes: + stream.write("%%Include %s.sip\n" % i) + stream.write("\n") + # C++ code to be written out to the generated module if module.cppCode: stream.write("%ModuleCode\n") @@ -133,14 +136,15 @@ from %s import * def generateModuleItems(self, module, stream): methodMap = { - extractors.ClassDef : self.generateClass, - extractors.FunctionDef : self.generateFunction, - extractors.EnumDef : self.generateEnum, - extractors.GlobalVarDef : self.generateGlobalVar, - extractors.TypedefDef : self.generateTypedef, - extractors.WigCode : self.generateWigCode, - extractors.PyCodeDef : self.generatePyCode, - extractors.CppMethodDef : self.generateCppMethod, + extractors.ClassDef : self.generateClass, + extractors.FunctionDef : self.generateFunction, + extractors.EnumDef : self.generateEnum, + extractors.GlobalVarDef : self.generateGlobalVar, + extractors.TypedefDef : self.generateTypedef, + extractors.WigCode : self.generateWigCode, + extractors.PyCodeDef : self.generatePyCode, + extractors.CppMethodDef : self.generateCppMethod, + extractors.CppMethodDef_sip : self.generateCppMethod_sip, } for item in module: @@ -287,14 +291,15 @@ from %s import * protected = [i for i in klass if i.protection == 'protected'] dispatch = { - extractors.MemberVarDef : self.generateMemberVar, - extractors.PropertyDef : self.generateProperty, - extractors.MethodDef : self.generateMethod, - extractors.EnumDef : self.generateEnum, - extractors.CppMethodDef : self.generateCppMethod, - extractors.PyMethodDef : self.generatePyMethod, - extractors.PyCodeDef : self.generatePyCode, - extractors.WigCode : self.generateWigCode, + extractors.MemberVarDef : self.generateMemberVar, + extractors.PropertyDef : self.generateProperty, + extractors.MethodDef : self.generateMethod, + extractors.EnumDef : self.generateEnum, + extractors.CppMethodDef : self.generateCppMethod, + extractors.CppMethodDef_sip : self.generateCppMethod_sip, + extractors.PyMethodDef : self.generatePyMethod, + extractors.PyCodeDef : self.generatePyCode, + extractors.WigCode : self.generateWigCode, # TODO: nested classes too? } for item in ctors: @@ -388,7 +393,94 @@ from %s import * def generateCppMethod(self, method, stream, indent=''): + # Add a new C++ method to a class. This one adds the code as a + # separate function and then adds a call to that function in the + # MethodCode directive. assert isinstance(method, extractors.CppMethodDef) + if method.ignored: + return + klass = method.klass + if klass: + assert isinstance(klass, extractors.ClassDef) + + # create the new function + fargs = method.argsString.strip('()').split(',') + for idx, arg in enumerate(fargs): + # take only the part before the =, if there is one + arg = arg.split('=')[0].strip() + arg = arg.replace('&', '*') # SIP will always want to use pointers for parameters + fargs[idx] = arg + fargs = ', '.join(fargs) + if fargs: + fargs = ', ' + fargs + if method.isCtor: + fname = '_%s_newCtor' % klass.name + fargs = '(int& _isErr%s)' % fargs + stream.write('%s%%TypeCode\n' % indent) + typ = klass.name + if method.useDerivedName: + typ = 'sip'+klass.name + stream.write('%sclass %s;\n' % (indent, typ)) # forward decalre the derived class + stream.write('%s%s* %s%s\n%s{\n' % (indent, typ, fname, fargs, indent)) + stream.write(nci(method.body, len(indent)+4)) + stream.write('%s}\n' % indent) + stream.write('%s%%End\n' % indent) + + else: + if klass: + fname = '_%s_%s' % (klass.name, method.name) + fargs = '(%s* self, int& _isErr%s)' % (klass.name, fargs) + stream.write('%s%%TypeCode\n' % indent) + else: + fname = '_%s_function' % method.name + fargs = '(int& _isErr%s)' % fargs + stream.write('%s%%ModuleCode\n' % indent) + stream.write('%s%s %s%s\n%s{\n' % (indent, method.type, fname, fargs, indent)) + stream.write(nci(method.body, len(indent)+4)) + stream.write('%s}\n' % indent) + stream.write('%s%%End\n' % indent) + + # now insert the method declaration and the code to call the new function + # find the parameter names + pnames = method.argsString.strip('()').split(',') + for idx, pn in enumerate(pnames): + # take only the part before the =, if there is one + name = pn.split('=')[0].strip() + # now get just the part after and space, * or &, which should be + # the parameter name + name = re.split(r'[ \*\&]+', name)[-1] + pnames[idx] = name + pnames = ', '.join(pnames) + if pnames: + pnames = ', ' + pnames + # convert PyObject* to SIP_PYOBJECT in the return type and param types + typ = method.type.replace('PyObject*', 'SIP_PYOBJECT') + argsString = method.argsString.replace('PyObject*', 'SIP_PYOBJECT') + # spit it all out + if method.isCtor: + stream.write('%s%s%s%s;\n' % + (indent, method.name, argsString, self.annotate(method))) + else: + stream.write('%s%s %s%s%s;\n' % + (indent, typ, method.name, argsString, self.annotate(method))) + stream.write('%s%%MethodCode\n' % indent) + stream.write(indent+' '*4) + if method.isCtor: + stream.write('sipCpp = %s(sipIsErr%s);\n' % (fname, pnames)) + else: + if method.type != 'void': + stream.write('sipRes = ') + if klass: + stream.write('%s(sipCpp, sipIsErr%s);\n' % (fname, pnames)) + else: + stream.write('%s(sipIsErr%s);\n' % (fname, pnames)) + stream.write('%s%%End\n\n' % indent) + + + def generateCppMethod_sip(self, method, stream, indent=''): + # Add a new C++ method to a class without the extra generated + # function, so SIP specific stuff can be done in the function body. + assert isinstance(method, extractors.CppMethodDef_sip) if method.ignored: return if method.isCtor: @@ -401,7 +493,8 @@ from %s import * stream.write('%s%%MethodCode\n' % indent) stream.write(nci(method.body, len(indent)+4)) stream.write('%s%%End\n\n' % indent) - + + def generatePyMethod(self, pm, stream, indent): assert isinstance(pm, extractors.PyMethodDef) @@ -409,7 +502,7 @@ from %s import * pm.klass.generateAfterClass.append(pm) else: klassName = pm.klass.pyName or pm.klass.name - stream.write("%Extract pycode\n") + stream.write("%Extract(id=pycode)\n") stream.write("def _%s_%s%s:\n" % (klassName, pm.name, pm.argsString)) if pm.briefDoc: stream.write(nci('"""\n%s\n"""\n' % pm.briefDoc, 4)) @@ -445,6 +538,10 @@ from %s import * annotations.append('TranserThis') if item.pyInt: annotations.append('PyInt') + + if isinstance(item, extractors.VariableDef): + if item.pyInt: + annotations.append('PyInt') if isinstance(item, extractors.FunctionDef): if item.deprecated: @@ -524,4 +621,8 @@ def nci(text, numSpaces=0, stripLeading=True): return newText +class SipGeneratorError(RuntimeError): + pass + + #--------------------------------------------------------------------------- diff --git a/etgtools/tweaker_tools.py b/etgtools/tweaker_tools.py index 1dffbfdc..bd6bcc61 100644 --- a/etgtools/tweaker_tools.py +++ b/etgtools/tweaker_tools.py @@ -81,7 +81,22 @@ def fixEventClass(klass): klass.addPrivateAssignOp() - +def fixWindowClass(klass): + """ + Do common tweaks for a window class. + """ + # The ctor and Create method transfer ownership of the this pointer + klass.find('%s.parent' % klass.name).transferThis = True + klass.find('Create.parent').transferThis = True + # give the id param a default value + klass.find('%s.id' % klass.name).default = 'wxID_ANY' + klass.find('Create.id').default = 'wxID_ANY' + # look for wxByte parameters + #for item in klass.allItems(): + # if hasattr(item, 'type') and item.type == 'wxByte': + # item.pyInt = True + + def removeVirtuals(klass): """ Sometimes methods are marked as virtual but probably don't ever need to be