From be09c0e1f88b41541d6e5fa3e92ca96beda571da Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Fri, 2 Mar 2012 21:53:10 +0000 Subject: [PATCH] Add longlong and datetime git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@70776 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- TODO.txt | 2 - etg/_core.py | 4 +- etg/defs.py | 3 +- etg/wxdatetime.py | 363 +++++++++++++++++++++++++++++++++++ src/longlong.sip | 58 ++++++ unittests/test_longlong.py | 28 +++ unittests/test_wxdatetime.py | 135 +++++++++++++ 7 files changed, 588 insertions(+), 5 deletions(-) create mode 100644 etg/wxdatetime.py create mode 100644 src/longlong.sip create mode 100644 unittests/test_longlong.py create mode 100644 unittests/test_wxdatetime.py diff --git a/TODO.txt b/TODO.txt index ec7824a7..01286c00 100644 --- a/TODO.txt +++ b/TODO.txt @@ -168,7 +168,6 @@ other dev stuff * For full coverage of what was in the Classic core modules we'll need ETG files for the following: - * filesys, fs_mem, fs_inet, fs_arch (with wxZipFSHandler typedef) * taskbar * all missing common dialogs * print (as in print framework classes) @@ -194,7 +193,6 @@ other dev stuff * sound * mimetype * artprov - * datetime, or make it be a MappedType for Python date objects? * clipbrd, dnd, finish dataobj * power * about diff --git a/etg/_core.py b/etg/_core.py index f693ca91..e0b2996f 100644 --- a/etg/_core.py +++ b/etg/_core.py @@ -37,6 +37,8 @@ INCLUDES = [ 'defs', 'clntdata', 'userdata', 'stockgdi', + 'longlong', + 'wxdatetime', 'windowid', 'platinfo', @@ -355,8 +357,8 @@ def run(): """) # Here is the function it calls module.includeCppCode('src/core_ex.cpp') - + #----------------------------------------------------------------- tools.doCommonTweaks(module) tools.runGenerators(module) diff --git a/etg/defs.py b/etg/defs.py index 0d2f41d1..61c7aef1 100644 --- a/etg/defs.py +++ b/etg/defs.py @@ -73,8 +73,7 @@ def run(): class wxImageHandler; class wxToolBar; class wxExecuteEnv; - class wxDateTime; - """)) + """)) # TBD: I've always disliked the WXK_* names. Should I rename all the items diff --git a/etg/wxdatetime.py b/etg/wxdatetime.py new file mode 100644 index 00000000..b7662cb0 --- /dev/null +++ b/etg/wxdatetime.py @@ -0,0 +1,363 @@ +#--------------------------------------------------------------------------- +# Name: etg/datetime.py +# Author: Robin Dunn +# +# Created: 27-Feb-2012 +# Copyright: (c) 2012 by Total Control Software +# License: wxWindows License +#--------------------------------------------------------------------------- + +import etgtools +import etgtools.tweaker_tools as tools + +PACKAGE = "wx" +MODULE = "_core" +NAME = "wxdatetime" # 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 = [ "wxDateTime", + "wxDateSpan", + "wxTimeSpan", + #"wxDateTimeHolidayAuthority", + #"wxDateTimeWorkDays", + ] + +#--------------------------------------------------------------------------- + +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. + + + # Lots of the uses of wxDateTime_t have been changed to "unsigned short" + # in the interface file, so lets go ahead and translate the rest of them + # too so there will not be any "wxDateTime_t" in Phoenix confusingly + # mixed with "unsigned short"s. + # + # Also add the class scope specifier to nested enum types for parameters + # and return values. + for item in module.allItems(): + if isinstance(item, (etgtools.FunctionDef, etgtools.ParamDef, etgtools.VariableDef)): + typesMap = { 'wxDateTime_t': 'unsigned short', + 'Month' : 'wxDateTime::Month', + 'WeekDay' : 'wxDateTime::WeekDay', + 'TZ' : 'wxDateTime::TZ', + 'TimeZone' : 'wxDateTime::TimeZone', + 'Tm' : 'wxDateTime::Tm', + } + if item.type in typesMap: + item.type = typesMap[item.type] + + + # ignore the #define and add it as a Python alias instead + module.find('wxInvalidDateTime').ignore() + module.addPyCode('InvalidDateTime = DefaultDateTime') + gs = module.addGlobalStr('wxDefaultDateTimeFormat', module.find('wxInvalidDateTime')) + module.addGlobalStr('wxDefaultTimeSpanFormat', gs) + + #--------------------------------------------- + # Tweaks for the wxDateTime class + c = module.find('wxDateTime') + assert isinstance(c, etgtools.ClassDef) + c.allowAutoProperties = False + tools.ignoreAllOperators(c) + + # Ignore ctors with unknown types or that have overload conflicts that + # can't be distingished in Python + ctor = c.find('wxDateTime') + ctor.findOverload('time_t').ignore() + ctor.findOverload('struct tm').ignore() + ctor.findOverload('double jdn').ignore() + ctor.findOverload('_SYSTEMTIME').ignore() + ctor.findOverload('hour').ignore() # careful, the one we want to keep has an 'hour' param too + + # Add static factories for some of the ctors we ignored + c.addCppMethod('wxDateTime*', 'FromTimeT', '(time_t timet)', + factory=True, isStatic=True, + doc="Construct a DateTime from a C time_t value, the number of seconds since the epoch.", + body="return new wxDateTime(timet);") + + c.addCppMethod('wxDateTime*', 'FromJDN', '(double jdn)', + factory=True, isStatic=True, + doc="Construct a DateTime from a Julian Day Number.\n\n" + "By definition, the Julian Day Number, usually abbreviated as JDN, of a particular instant is the fractional number of days since 12 hours Universal Coordinated Time (Greenwich mean noon) on January 1 of the year -4712 in the Julian proleptic calendar.", + body="return new wxDateTime(jdn);") + + c.addCppMethod('wxDateTime*', 'FromHMS', + """(unsigned short hour, + unsigned short minute=0, + unsigned short second=0, + unsigned short millisecond=0)""", + factory=True, isStatic=True, + doc="Construct a DateTime equal to Today() with the time set to the supplied parameters.", + body="return new wxDateTime(hour, minute, second, millisecond);") + + c.addCppMethod('wxDateTime*', 'FromDMY', + """(unsigned short day, + wxDateTime::Month month, + int year = Inv_Year, + unsigned short hour=0, + unsigned short minute=0, + unsigned short second=0, + unsigned short millisecond=0)""", + factory=True, isStatic=True, + doc="Construct a DateTime using the supplied parameters.", + body="return new wxDateTime(day, month, year, hour, minute, second, millisecond);") + + # and give them some simple wrappers for Classic compatibility + module.addPyFunction('DateTimeFromTimeT', '(timet)', + doc="Compatibility wrapper for DateTime.FromTimeT", + body="return DateTime.FromTimeT(timet)", + deprecated=True) + module.addPyFunction('DateTimeFromJDN', '(jdn)', + doc="Compatibility wrapper for DateTime.FromJDN", + body="return DateTime.FromJDN(jdn)", + deprecated=True) + module.addPyFunction('DateTimeFromHMS', '(hour, minute=0, second=0, millisecond=0)', + doc="Compatibility wrapper for DateTime.FromHMS", + body="return DateTime.FromHMS(hour, minute, second, millisecond)", + deprecated=True) + module.addPyFunction('DateTimeFromDMY', '(day, month, year=DateTime.Inv_Year, hour=0, minute=0, second=0, millisecond=0)', + doc="Compatibility wrapper for DateTime.FromDMY", + body="return DateTime.FromDMY(day, month, year, hour, minute, second, millisecond)", + deprecated=True) + + + # Fixup similar conflicts in the Set method overloads + c.find('Set').findOverload('struct tm').ignore() + c.find('Set').renameOverload('Tm', 'SetTm') + c.find('Set').renameOverload('time_t', 'SetTimeT') + c.find('Set').renameOverload('double jdn', 'SetJDN') + c.find('Set').renameOverload('hour', 'SetHMS') + + # Unknown parameter and return types + c.find('SetFromMSWSysTime').ignore() + c.find('GetAsMSWSysTime').ignore() + + # this overload is static, the other isn't. Rename it? + c.find('GetCentury').findOverload('year').ignore() + + c.find('GetNumberOfDays').ignore() + c.find('GetTmNow').ignore() + c.find('GetTmNow').ignore() + + # Link error?? + c.find('IsGregorianDate').ignore() + + # output the am/pm parameter values + c.find('GetAmPmStrings.am').out = True + c.find('GetAmPmStrings.pm').out = True + + + # remove the const version of the overloaded Add's and Subtract's + c.find('Add').findOverload('wxDateSpan', isConst=True).ignore() + c.find('Add').findOverload('wxTimeSpan', isConst=True).ignore() + c.find('Subtract').findOverload('wxDateSpan', isConst=True).ignore() + c.find('Subtract').findOverload('wxTimeSpan', isConst=True).ignore() + + # Ignore the end parameter for the parse methods, and provide replacement + # implementations that don't need them. + c.find('ParseDate.end').ignore() + c.find('ParseTime.end').ignore() + c.find('ParseDateTime.end').ignore() + c.find('ParseRfc822Date.end').ignore() + for m in c.find('ParseFormat').all(): + m.find('end').ignore() + + c.find('ParseDate').setCppCode("""\ + wxString::const_iterator end; + return self->ParseDate(*date, &end); + """) + c.find('ParseTime').setCppCode("""\ + wxString::const_iterator end; + return self->ParseTime(*time, &end); + """) + c.find('ParseDateTime').setCppCode("""\ + wxString::const_iterator end; + return self->ParseDateTime(*datetime, &end); + """) + c.find('ParseRfc822Date').setCppCode("""\ + wxString::const_iterator end; + return self->ParseRfc822Date(*date, &end); + """) + + pf = c.find('ParseFormat') + pf.findOverload('const wxString &date, const wxString &format, const wxDateTime &dateDef, wxString::').setCppCode( + """\ + wxString::const_iterator end; + return self->ParseFormat(*date, *format, *dateDef, &end); + """) + pf.findOverload('const wxString &date, const wxString &format, wxString::').setCppCode( + """\ + wxString::const_iterator end; + return self->ParseFormat(*date, *format, &end); + """) + pf.findOverload('const wxString &date, wxString::').setCppCode( + """\ + wxString::const_iterator end; + return self->ParseFormat(*date, &end); + """) + + + c.addPyMethod('__repr__', '(self)', """\ + if self.IsValid(): + f = self.Format().encode('utf-8') + return '' % f + else: + return '' + """) + + c.addPyMethod('__str__', '(self)', """\ + if self.IsValid(): + return self.Format().encode('utf-8') + else: + return "INVALID DateTime" + """) + + + # use lowercase to avoid conflicts + c.addProperty("day GetDay SetDay") + c.addProperty("month GetMonth SetMonth") + c.addProperty("year GetYear SetYear") + c.addProperty("hour GetHour SetHour") + c.addProperty("minute GetMinute SetMinute") + c.addProperty("second GetSecond SetSecond") + c.addProperty("millisecond GetMillisecond SetMillisecond") + c.addProperty("JDN GetJDN SetJDN") + + c.addProperty("DayOfYear GetDayOfYear") + c.addProperty("JulianDayNumber GetJulianDayNumber") + c.addProperty("LastMonthDay GetLastMonthDay") + c.addProperty("MJD GetMJD") + c.addProperty("ModifiedJulianDayNumber GetModifiedJulianDayNumber") + c.addProperty("RataDie GetRataDie") + c.addProperty("Ticks GetTicks") + c.addProperty("WeekOfMonth GetWeekOfMonth") + c.addProperty("WeekOfYear GetWeekOfYear") + + + c.addItem(etgtools.WigCode("""\ + bool operator<(const wxDateTime& dt) const; + bool operator<=(const wxDateTime& dt) const; + bool operator>(const wxDateTime& dt) const; + bool operator>=(const wxDateTime& dt) const; + bool operator==(const wxDateTime& dt) const; + bool operator!=(const wxDateTime& dt) const; + wxDateTime& operator+=(const wxTimeSpan& diff); + wxDateTime operator+(const wxTimeSpan& ts) const; + wxDateTime& operator-=(const wxTimeSpan& diff); + wxDateTime operator-(const wxTimeSpan& ts) const; + wxDateTime& operator+=(const wxDateSpan& diff); + wxDateTime operator+(const wxDateSpan& ds) const; + wxDateTime& operator-=(const wxDateSpan& diff); + wxDateTime operator-(const wxDateSpan& ds) const; + wxTimeSpan operator-(const wxDateTime& dt2) const; + """)) + + # Add some code to automatically convert from a Python datetime.date or a + # datetime.datetime object + c.addHeaderCode("#include ") + c.convertFromPyObject = """\ + PyDateTime_IMPORT; + + // Code to test a PyObject for compatibility with wxDateTime + if (!sipIsErr) { + if (sipCanConvertToType(sipPy, sipType_wxDateTime, SIP_NO_CONVERTORS)) + return TRUE; + if (PyDateTime_Check(sipPy) || PyDate_Check(sipPy)) + return TRUE; + return FALSE; + } + + // Code to convert a compatible PyObject to a wxDateTime + if (PyDateTime_Check(sipPy)) { + *sipCppPtr = new wxDateTime(PyDateTime_GET_DAY(sipPy), + (wxDateTime::Month)(PyDateTime_GET_MONTH(sipPy)-1), + PyDateTime_GET_YEAR(sipPy), + PyDateTime_DATE_GET_HOUR(sipPy), + PyDateTime_DATE_GET_MINUTE(sipPy), + PyDateTime_DATE_GET_SECOND(sipPy), + PyDateTime_DATE_GET_MICROSECOND(sipPy)/1000); // micro to milli + return sipGetState(sipTransferObj); + } + if (PyDate_Check(sipPy)) { + *sipCppPtr = new wxDateTime(PyDateTime_GET_DAY(sipPy), + (wxDateTime::Month)(PyDateTime_GET_MONTH(sipPy)-1), + PyDateTime_GET_YEAR(sipPy)); + return sipGetState(sipTransferObj); + } + // if we get this far then it must already be a wxDateTime instance + *sipCppPtr = reinterpret_cast(sipConvertToType( + sipPy, sipType_wxDateTime, sipTransferObj, SIP_NO_CONVERTORS, 0, sipIsErr)); + + return 0; // Not a new isntance + """ + + + #--------------------------------------------- + # Tweaks for the wxDateSpan class + c = module.find('wxDateSpan') + c.allowAutoProperties = False + tools.ignoreAllOperators(c) + + c.find('Add').findOverload('', isConst=True).ignore() + c.find('Multiply').findOverload('', isConst=True).ignore() + c.find('Subtract').findOverload('', isConst=True).ignore() + + c.addItem(etgtools.WigCode("""\ + wxDateSpan& operator+=(const wxDateSpan& other); + wxDateSpan operator+(const wxDateSpan& ds) const; + wxDateSpan& operator-=(const wxDateSpan& other); + wxDateSpan operator-(const wxDateSpan& ds) const; + wxDateSpan& operator-(); + wxDateSpan& operator*=(int factor); + wxDateSpan operator*(int n) const; + bool operator==(const wxDateSpan& ds) const; + bool operator!=(const wxDateSpan& ds) const; + """)) + + + + #--------------------------------------------- + # Tweaks for the wxTimeSpan class + c = module.find('wxTimeSpan') + c.allowAutoProperties = False + tools.ignoreAllOperators(c) + + c.find('Add').findOverload('', isConst=True).ignore() + c.find('Multiply').findOverload('', isConst=True).ignore() + c.find('Subtract').findOverload('', isConst=True).ignore() + + c.addItem(etgtools.WigCode("""\ + wxTimeSpan& operator+=(const wxTimeSpan& diff); + wxTimeSpan operator+(const wxTimeSpan& ts) const; + wxTimeSpan& operator-=(const wxTimeSpan& diff); + wxTimeSpan operator-(const wxTimeSpan& ts); + wxTimeSpan& operator*=(int n); + wxTimeSpan operator*(int n) const; + wxTimeSpan& operator-(); + bool operator<(const wxTimeSpan &ts) const; + bool operator<=(const wxTimeSpan &ts) const; + bool operator>(const wxTimeSpan &ts) const; + bool operator>=(const wxTimeSpan &ts) const; + bool operator==(const wxTimeSpan &ts) const; + bool operator!=(const wxTimeSpan &ts) const; + """)) + + + #----------------------------------------------------------------- + tools.doCommonTweaks(module) + tools.runGenerators(module) + + +#--------------------------------------------------------------------------- +if __name__ == '__main__': + run() + diff --git a/src/longlong.sip b/src/longlong.sip new file mode 100644 index 00000000..b5a10d29 --- /dev/null +++ b/src/longlong.sip @@ -0,0 +1,58 @@ +//-------------------------------------------------------------------------- +// Name: datetime.sip +// Purpose: Implements a %MappedType for wxLongLong +// +// Author: Robin Dunn +// +// Created: 29-Feb-2012 +// Copyright: (c) 2012 by Total Control Software +// Licence: wxWindows license +//-------------------------------------------------------------------------- + + +%MappedType wxLongLong { + + %TypeHeaderCode + #include + %End + + %ConvertToTypeCode + // Code to test a PyObject for compatibility + if (!sipIsErr) { + if (PyNumber_Check(sipPy)) + return TRUE; + return FALSE; + } + + // Code to convert from a compatible PyObject + #if wxUSE_LONGLONG_NATIVE + *sipCppPtr = new wxLongLong(PyLong_AsLongLong(sipPy)); + #else + #error TODO. Get the hi/lo 32-bit dwords to construct the wxLongLong + #endif + return sipGetState(sipTransferObj); + + %End + + %ConvertFromTypeCode + #if wxUSE_LONGLONG_NATIVE + return PyLong_FromLongLong(sipCpp->GetValue()); + #else + // Convert a wxLongLong to a Python Long by getting the hi/lo + // dwords, then shifting and or-ing them together + PyObject *hi, *lo, *shifter, *shifted, *result; + hi = PyLong_FromLong( sipCpp->GetHi() ); + lo = PyLong_FromLong( sipCpp->GetLo() ); + shifter = PyLong_FromLong(32); + shifted = PyNumber_Lshift(hi, shifter); + result = PyNumber_Or(shifted, lo); + Py_DECREF(hi); + Py_DECREF(lo); + Py_DECREF(shifter); + Py_DECREF(shifted); + return result; + #endif + %End +}; + + diff --git a/unittests/test_longlong.py b/unittests/test_longlong.py new file mode 100644 index 00000000..41ca5c24 --- /dev/null +++ b/unittests/test_longlong.py @@ -0,0 +1,28 @@ + +import imp_unittest, unittest +import wx + +#--------------------------------------------------------------------------- + +class longlong_Tests(unittest.TestCase): + + def test_longlong(self): + val1 = 2**50 # make a big value + + # setting the timespan's seconds property will use the mapped type + # code to convert to a wxLongLong... + ts = wx.TimeSpan(hours=0, min=0, sec=val1) + + # ...and fetching it from the timespan will use the mapped type to + # convert back from a wxLongLong to a Python object... + val2 = ts.GetSeconds() + + # ...which we can compare with the original. + self.assertTrue(val1 == val2) + + +#--------------------------------------------------------------------------- + + +if __name__ == '__main__': + unittest.main() diff --git a/unittests/test_wxdatetime.py b/unittests/test_wxdatetime.py new file mode 100644 index 00000000..544197a5 --- /dev/null +++ b/unittests/test_wxdatetime.py @@ -0,0 +1,135 @@ +import imp_unittest, unittest +import wx +import wtc +import datetime +import time + +##import os; print 'PID:', os.getpid(); raw_input('Ready to start, press enter...') + +#--------------------------------------------------------------------------- + +class datetime_Tests(wtc.WidgetTestCase): + + def test_datetime1(self): + d1 = wx.DateTime() + self.assertTrue(not d1.IsValid()) + d2 = wx.DateTime(1, wx.DateTime.Mar, 2012, 8, 15, 45, 123) + self.assertEqual(d2.year, 2012) + self.assertEqual(d2.month, wx.DateTime.Mar) + self.assertEqual(d2.day, 1) + self.assertEqual(d2.hour, 8) + self.assertEqual(d2.minute, 15) + self.assertEqual(d2.second, 45) + + def test_datetime2(self): + d1 = wx.DateTime.FromHMS(8, 15, 45, 123) + d2 = wx.DateTime.FromJDN(12345.67) + d3 = wx.DateTime.FromTimeT(int(time.time())) + d4 = wx.DateTime.FromDMY(1, wx.DateTime.Mar, 2012, 8, 15, 45, 123) + + def test_datetime3(self): + d1 = wx.DateTime.Today() + d2 = wx.DateTime.Now() + self.assertTrue( d1 != d2) + self.assertTrue( d2 > d1) + d3 = wx.DateTime(d1) + self.assertTrue(d1 is not d3) + self.assertTrue(d1 == d3) + + def test_datetime4(self): + d1 = wx.DateTime.Today() + d2 = wx.DateTime.Now() + span = d2 - d1 + self.assertTrue(isinstance(span, wx.TimeSpan)) + + def test_datetimeGetAmPm(self): + am, pm = wx.DateTime.GetAmPmStrings() + self.assertTrue(isinstance(am, basestring) and am != "") + self.assertTrue(isinstance(pm, basestring) and pm != "") + + + def test_datetimeProperties(self): + d1 = wx.DateTime.Today() + d1.day + d1.month + d1.year + d1.hour + d1.minute + d1.second + d1.millisecond + d1.JDN + d1.DayOfYear + d1.JulianDayNumber + d1.LastMonthDay + d1.MJD + d1.ModifiedJulianDayNumber + d1.RataDie + d1.Ticks + d1.WeekOfMonth + d1.WeekOfYear + + + def test_datetimeConvertFromPyDatetime(self): + dt = datetime.datetime(2012, 3, 1, 8, 15, 45) + d = wx.DateTime(dt) + self.assertEqual(d.year, 2012) + self.assertEqual(d.month, wx.DateTime.Mar) + self.assertEqual(d.day, 1) + self.assertEqual(d.hour, 8) + self.assertEqual(d.minute, 15) + self.assertEqual(d.second, 45) + + def test_datetimeConvertFromPyDate(self): + dt = datetime.date(2012, 3, 1) + d = wx.DateTime(dt) + self.assertEqual(d.year, 2012) + self.assertEqual(d.month, wx.DateTime.Mar) + self.assertEqual(d.day, 1) + self.assertEqual(d.hour, 0) + self.assertEqual(d.minute, 0) + self.assertEqual(d.second, 0) + + + def test_datetimeTm(self): + d = wx.DateTime.Now() + tm = d.GetTm() + self.assertTrue(isinstance(tm, wx.DateTime.Tm)) + self.assertTrue(tm.IsValid()) + tm.msec + tm.sec + tm.min + tm.hour + tm.mday + tm.yday + tm.mon + tm.year + + + def test_datetimeSet(self): + d1 = wx.DateTime.Now() + tm = d1.GetTm() + + d2 = wx.DateTime() + d2.SetTm(tm) + self.assertTrue(d1 == d2) + + d3 = wx.DateTime() + d3.Set(tm.mday, tm.mon, tm.year, tm.hour, tm.min, tm.sec, tm.msec) + self.assertTrue(d1 == d3) + + + def test_datetimeFormatParse(self): + d = wx.DateTime(1, wx.DateTime.Mar, 2012, 8, 15, 45) + fmt = '%Y-%m-%d %H:%M:%S' + st = d.Format(fmt) + self.assertEqual(st, '2012-03-01 08:15:45') + + d2 = wx.DateTime() + d2.ParseFormat(st, fmt) + self.assertEqual(d, d2) + +#--------------------------------------------------------------------------- + + +if __name__ == '__main__': + unittest.main()