Lots of enhancments and additions for wxGraphics classes. Added unittests too.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@69402 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2011-10-13 06:36:10 +00:00
parent eae4ace88a
commit 4f675b544c
3 changed files with 350 additions and 9 deletions

View File

@@ -1,6 +1,7 @@
#---------------------------------------------------------------------------
# Name: etg/graphics.py
# Author: Kevin Ollivier
# Robin Dunn
#
# Created: 10-Sept-2011
# Copyright: (c) 2011 by Kevin Ollivier
@@ -18,26 +19,28 @@ DOCSTRING = ""
# The classes and/or the basename of the Doxygen XML files to be processed by
# this script.
ITEMS = [
'wxGraphicsObject',
'wxGraphicsBitmap',
'wxGraphicsBrush',
'wxGraphicsContext',
'wxGraphicsFont',
'wxGraphicsPen',
'wxGraphicsContext',
'wxGraphicsGradientStop',
'wxGraphicsGradientStops',
'wxGraphicsMatrix',
'wxGraphicsObject',
'wxGraphicsPath',
'wxGraphicsPen',
'wxGraphicsRenderer',
]
OTHERDEPS = [ 'src/Point2D_helpers.cpp']
#---------------------------------------------------------------------------
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.
@@ -46,14 +49,31 @@ def run():
def markFactories(klass):
for func in klass.allItems():
if isinstance(func, etgtools.FunctionDef) and func.name.startswith('Create'):
if isinstance(func, etgtools.FunctionDef) \
and func.name.startswith('Create') \
and '*' in func.type:
func.factory = True
#---------------------------------------------
c = module.find('wxGraphicsObject')
assert isinstance(c, etgtools.ClassDef)
c.addCppMethod('bool', 'IsOk', '()', 'return !self->IsNull();')
c.addCppMethod('int', '__nonzero__', '()', "return !self->IsNull();")
#---------------------------------------------
c = module.find('wxGraphicsContext')
assert isinstance(c, etgtools.ClassDef)
markFactories(c)
tools.removeVirtuals(c)
c.abstract = True
# ensure that the target DC lives as long as the GC does
for m in c.find('Create').all():
for p in m.items:
if 'DC' in p.name or p.name == 'image':
p.keepReference = True
# FIXME: Handle wxEnhMetaFileDC?
c.find('Create').findOverload('wxEnhMetaFileDC').ignore()
@@ -61,16 +81,98 @@ def run():
# SIP doesn't like default parameter values to use dereference syntax,
# (such as "col = *wxBLACK") so tweak the syntax a bit by using a macro.
c.addHeaderCode("#define BLACK *wxBLACK")
for m in [c.find('CreateFont')] + c.find('CreateFont').overloads:
for m in c.find('CreateFont').all():
m.find('col').default = 'BLACK'
m = c.find('GetPartialTextExtents')
m.find('widths').ignore()
m.type = 'wxArrayDouble*'
m.setCppCode("""\
wxArrayDouble rval;
self->GetPartialTextExtents(*text, rval);
return new wxArrayDouble(rval);
""")
m = c.find('GetTextExtent')
m.pyName = 'GetFullTextExtent'
m.find('width').out = True
m.find('height').out = True
m.find('descent').out = True
m.find('externalLeading').out = True
m2 = c.addCppMethod('PyObject*', 'GetTextExtent', '(const wxString& text)',
pyArgsString="(text) -> (width, height)",
doc="Gets the dimensions of the string using the currently selected font.",
body="""\
wxDouble width = 0.0, height = 0.0;
self->GetTextExtent(*text, &width, &height, NULL, NULL);
return sipBuildResult(0, "(dd)", width, height);
""")
c.items.remove(m2)
c.insertItemAfter(m, m2)
c.addPyCode("GraphicsContext.DrawRotatedText = wx.deprecated(GraphicsContext.DrawText)")
c.includeCppCode('src/Point2D_helpers.cpp')
# we'll reimplement this overload as StrokeLineSegments
c.find('StrokeLines').findOverload('beginPoints').ignore()
c.addCppMethod('void', 'StrokeLineSegments', '(PyObject* beginPoints, PyObject* endPoints)',
pyArgsString="(beginPoint2Ds, endPoint2Ds)",
doc="Stroke disconnected lines from begin to end points.",
body="""\
size_t c1, c2, count;
wxPoint2D* beginP = wxPoint2D_array_helper(beginPoints, &c1);
wxPoint2D* endP = wxPoint2D_array_helper(endPoints, &c2);
if ( beginP != NULL && endP != NULL ) {
count = wxMin(c1, c2);
self->StrokeLines(count, beginP, endP);
}
delete [] beginP;
delete [] endP;
""")
# Also reimplement the main StrokeLines method to reuse the same helper
# function as StrokLineSegments
m = c.find('StrokeLines').findOverload('points').ignore()
c.addCppMethod('void', 'StrokeLines', '(PyObject* points)',
pyArgsString="(point2Ds)",
doc="Stroke lines conencting all the points.",
body="""\
size_t count;
wxPoint2D* ptsArray = wxPoint2D_array_helper(points, &count);
if ( ptsArray != NULL ) {
self->StrokeLines(count, ptsArray);
delete [] ptsArray;
}
""")
# and once more for DrawLines
m = c.find('DrawLines').ignore()
c.addCppMethod('void', 'DrawLines', '(PyObject* points, wxPolygonFillMode fillStyle = wxODDEVEN_RULE)',
pyArgsString="(point2Ds, fillStyle=ODDEVEN_RULE)",
doc="Draws a polygon.",
body="""\
size_t count;
wxPoint2D* ptsArray = wxPoint2D_array_helper(points, &count);
if ( ptsArray != NULL ) {
self->DrawLines(count, ptsArray, fillStyle);
delete [] ptsArray;
}
""")
#---------------------------------------------
c = module.find('wxGraphicsPath')
tools.removeVirtuals(c)
c.find('GetBox').findOverload('wxDouble *x, wxDouble *y').ignore()
c.find('GetCurrentPoint').findOverload('wxDouble *x, wxDouble *y').ignore()
#---------------------------------------------
c = module.find('wxGraphicsRenderer')
tools.removeVirtuals(c)
markFactories(c)
@@ -80,12 +182,43 @@ def run():
c.find('CreateContext').findOverload('wxEnhMetaFileDC').ignore()
# See above
for m in [c.find('CreateFont')] + c.find('CreateFont').overloads:
for m in c.find('CreateFont').all():
m.find('col').default = 'BLACK'
#---------------------------------------------
c = module.find('wxGraphicsMatrix')
tools.removeVirtuals(c)
c.find('Concat').overloads = []
c.find('IsEqual').overloads = []
c.find('Get.a').out = True
c.find('Get.b').out = True
c.find('Get.c').out = True
c.find('Get.d').out = True
c.find('Get.tx').out = True
c.find('Get.ty').out = True
c.find('TransformDistance.dx').inOut = True
c.find('TransformDistance.dy').inOut = True
c.find('TransformPoint.x').inOut = True
c.find('TransformPoint.y').inOut = True
#---------------------------------------------
c = module.find('wxGraphicsGradientStops')
c.addCppMethod('SIP_SSIZE_T', '__len__', '()', body="return (SIP_SSIZE_T)self->GetCount();")
c.addCppMethod('wxGraphicsGradientStop*', '__getitem__', '(size_t n)',
pyArgsString='(n)',
body="return new wxGraphicsGradientStop(self->Item(n));")
#---------------------------------------------
# Use the pyNames we set for these classes in geometry.py so the old
# names do not show up in the docstrings, etc.
tools.changeTypeNames(module, 'wxPoint2DDouble', 'wxPoint2D')
tools.changeTypeNames(module, 'wxRect2DDouble', 'wxRect2D')
#-----------------------------------------------------------------

53
src/Point2D_helpers.cpp Normal file
View File

@@ -0,0 +1,53 @@
#define sipType_wxPoint2D sipType_wxPoint2DDouble
// Convert a Python sequence of 2-tuples of numbers or wx.Point2D objects into a
// C array of wxPoint2D instances
static
wxPoint2D* wxPoint2D_array_helper(PyObject* source, size_t *count)
{
wxPoint2D* array;
Py_ssize_t idx, len;
// ensure that it is a sequence
if (! PySequence_Check(source))
goto error0;
// ensure it is not a string or unicode object (they are sequences too)
else if (PyString_Check(source) || PyUnicode_Check(source))
goto error0;
// ensure each item can be converted to wxPoint2D
else {
len = PySequence_Length(source);
for (idx=0; idx<len; idx++) {
PyObject* item = PySequence_ITEM(source, idx);
if (!sipCanConvertToType(item, sipType_wxPoint2D, SIP_NOT_NONE)) {
Py_DECREF(item);
goto error0;
}
Py_DECREF(item);
}
}
// The length of the sequence is returned in count.
*count = len;
array = new wxPoint2D[*count];
if (!array) {
PyErr_SetString(PyExc_MemoryError, "Unable to allocate temporary array");
return NULL;
}
for (idx=0; idx<len; idx++) {
PyObject* obj = PySequence_ITEM(source, idx);
int state = 0;
int err = 0;
wxPoint2D* item = reinterpret_cast<wxPoint2D*>(
sipConvertToType(obj, sipType_wxPoint2D, NULL, 0, &state, &err));
array[idx] = *item;
sipReleaseType((void*)item, sipType_wxPoint2D, state); // delete temporary instances
Py_DECREF(obj);
}
return array;
error0:
PyErr_SetString(PyExc_TypeError, "Expected a sequence of length-2 sequences or wxPoint2D objects.");
return NULL;
}

155
unittests/test_graphics.py Normal file
View File

@@ -0,0 +1,155 @@
import imp_unittest, unittest
import wtc
import wx
import os
#---------------------------------------------------------------------------
class graphics_Tests(wtc.WidgetTestCase):
def test_gcCreate1(self):
gc = wx.GraphicsContext.Create(wx.ClientDC(self.frame))
self.assertTrue(gc.IsOk())
def test_gcCreate2(self):
bmp = wx.Bitmap(100,100)
mdc = wx.MemoryDC(bmp)
gc = wx.GraphicsContext.Create(mdc)
self.assertTrue(gc.IsOk())
def test_gcCreate3(self):
img = wx.Image(100,100)
gc = wx.GraphicsContext.Create(img)
self.assertTrue(gc.IsOk())
def test_gcCreateBitmap(self):
gc = wx.GraphicsContext.Create(self.frame)
self.assertTrue(gc.IsOk())
bmp = wx.Bitmap(100,100)
gb = gc.CreateBitmap(bmp)
self.assertTrue(gb.IsOk())
self.assertTrue(isinstance(gb, wx.GraphicsBitmap))
img = wx.Image(100,100)
gb = gc.CreateBitmapFromImage(img)
self.assertTrue(gb.IsOk())
gb = gc.CreateSubBitmap(gb, 5, 5, 25, 25)
self.assertTrue(gb.IsOk())
img = gb.ConvertToImage()
self.assertTrue(img.IsOk())
def test_gcCreateBrush(self):
gc = wx.GraphicsContext.Create(self.frame)
gb = gc.CreateBrush(wx.Brush('blue'))
self.assertTrue(gb.IsOk())
self.assertTrue(isinstance(gb, wx.GraphicsBrush))
def test_gcCreateFont(self):
gc = wx.GraphicsContext.Create(self.frame)
gf = gc.CreateFont(wx.NORMAL_FONT)
self.assertTrue(gf.IsOk())
self.assertTrue(isinstance(gf, wx.GraphicsFont))
gf = gc.CreateFont(15, 'Courier')
self.assertTrue(gf.IsOk())
def test_gcCreatePen(self):
gc = wx.GraphicsContext.Create(self.frame)
gp = gc.CreatePen(wx.RED_PEN)
self.assertTrue(gp.IsOk())
self.assertTrue(isinstance(gp, wx.GraphicsPen))
def test_gcCreatePath(self):
gc = wx.GraphicsContext.Create(self.frame)
p = gc.CreatePath()
self.assertTrue(p.IsOk())
self.assertTrue(isinstance(p, wx.GraphicsPath))
def test_gcCreateMatrix(self):
gc = wx.GraphicsContext.Create(self.frame)
m = gc.CreateMatrix()
self.assertTrue(m.IsOk())
self.assertTrue(isinstance(m, wx.GraphicsMatrix))
values = m.Get()
self.assertTrue(len(values) == 6)
dx, dy = m.TransformDistance(5,6)
x,y = m.TransformPoint(7,8)
def test_gcTextExtents(self):
gc = wx.GraphicsContext.Create(self.frame)
gf = gc.CreateFont(wx.NORMAL_FONT)
gc.SetFont(gf)
ext = gc.GetPartialTextExtents("Hello")
self.assertEqual(len(ext), 5)
w, h, d, e = gc.GetFullTextExtent("Hello")
w, h = gc.GetTextExtent("Hello")
def test_gcStrokeLines1(self):
gc = wx.GraphicsContext.Create(self.frame)
gc.SetPen(wx.Pen('blue', 2))
points = [ wx.Point2D(5,5),
wx.Point2D(50,5),
wx.Point2D(50,50),
wx.Point2D(5,5),
]
gc.StrokeLines(points)
def test_gcStrokeLines2(self):
gc = wx.GraphicsContext.Create(self.frame)
gc.SetPen(wx.Pen('blue', 2))
points = [ (5,5), (50,5), wx.Point2D(50,50), (5,5) ]
gc.StrokeLines(points)
def test_gcStrokeLines3(self):
gc = wx.GraphicsContext.Create(self.frame)
gc.SetPen(wx.Pen('blue', 2))
with self.assertRaises(TypeError):
points = [ (5,5), (50,5), 'not a point', (5,5) ]
gc.StrokeLines(points)
def test_gcDrawLines(self):
gc = wx.GraphicsContext.Create(self.frame)
gc.SetPen(wx.Pen('blue', 2))
points = [ (5,5), (50,5), wx.Point2D(50,50), (5,5) ]
gc.DrawLines(points)
def test_gcGradientStops(self):
gs1 = wx.GraphicsGradientStop('red', 0.25)
gs2 = wx.GraphicsGradientStop('green', 0.50)
gs3 = wx.GraphicsGradientStop('blue', 0.90)
gs1.Colour
gs1.Position
stops = wx.GraphicsGradientStops()
stops.Add(gs1)
stops.Add(gs2)
stops.Add('white', 0.75)
stops.Add(gs3)
self.assertEqual(len(stops), 6) # 2 existing, plus 4 added
gs = stops[2]
self.assertTrue(gs.Position == 0.5)
gc = wx.GraphicsContext.Create(self.frame)
b = gc.CreateLinearGradientBrush(0,0, 500, 100, stops)
#---------------------------------------------------------------------------
if __name__ == '__main__':
unittest.main()