Hi James et al,
with my recent additions to the Dia Python Plug-In it is possible to write
export filters completely in Python.
--Example.py
import sys, dia
sys.argv = [ 'dia-python' ]
class Renderer :
def __init__ (self) :
pass
def begin_render (self, data, filename) :
self.f = open(filename, "w")
self.f.write("Extents: "+ str(data.extents) + "\n")
self.f.write("file: "+ filename + "\n")
def end_render (self) :
self.f.close()
dia.register_export ("PyDia Export Example", "diapye", Renderer())
-end example.py
There are two kinds of filter implementations possible:
- Implement a renderer : The Python class needs to implement each
required renderer function like draw_image(), draw_string() etc.
See export-render.py
- Implement an object filter : Only begin_render() and end_render()
need to be implemented. Iteration over the Diagram Objects is totally
done by the filter. This one may finally (e.g.) allow to write a
Source Code generator for UML diagrams in an appropriate language ...
See export-object.py
The main part of the work was adding more object wrappers to the
language binding. They are:
DiaExportFilter, DiaDiagramData, DiaPoint, DiaRectangle, DiaBezPoint,
DiaFont, DiaColor, DiaImage, DiaProperty, DiaProperties, DiaArrow
(see the attached export-*.py examples, to how they are used)
Changes required to the Dia core:
---------------------------------
The Dia<Ex/Im>port interface needed a provision to run different
filters via one callback function. I've added an additional user_data
field to the Dia<Ex/Im>port struct and let it be passed to the
export() / import() function as additional parameter.
By this the Python extension is able to distinguish which renderer
class needs to be called.
Additional changes I would like to add/make:
--------------------------------------------
- extend the property API to allow Properties, which are themselves
lists of properties. This would be required to make the UML module
really useable from Python. But I'm not sure about the appropriate
implementation
- add register_menu_function () to allow to implement even generic
plug-ins in Python or any other language callable by C code.
- move some code from the Dia executable to libdia, which may finally
lead to a Python extension, which does not require to be embedded in
Dia. At the moment it would clean up some nastyness required to
make these functions and variable callable from Python. The current list
of code to move (see app/dia.def) :
Additional the GSList* open_diagrams should be moved to libdia.
- implement a more generic Import API, which should allow simplier
diagram construction from files and may finally allow round-trip
engineering ...
If noone objects I'll commit the first chunk of PyDia changes
to cvs. Is there anyone out there who would do the required
makefile.am changes ?
Enough for now. Any objections, hints, flames, whatever ?
Have Fun,
Hans
import sys, dia
# sys.path.insert(0, 'd:/graph/dia/dia')
sys.argv = [ 'dia-python' ]
class ObjRenderer :
def __init__ (self) :
pass
def begin_render (self, data, filename) :
self.f = open(filename, "w")
for layer in data.layers :
self.f.write ("<layer name=\"" + layer.name + "\">\n")
for o in layer.objects :
self.f.write ("\t"*1 + str(o) + "\n")
r = o.bounding_box
self.f.write ("\t"*2 + "<bbox>" + str(r.left) + "," +
str(r.top) + ";" \
+ str(r.right) + "," + str(r.bottom) +
"</bbox>\n")
self.f.write ("\t"*2 + "<connections>\n")
for c in o.connections :
if (len(c.connected) < 1) :
continue
self.f.write ("\t"*3 + str(c) + "\n")
for co in c.connected :
self.f.write ("\t"*4 + str(co) + "\n")
self.f.write ("\t"*3 +
"</DiaConnectionPoint>\n")
self.f.write ("\t"*2 + "</connections>\n")
self.f.write ("\t"*2 + str(o.properties) + "\n")
keys = o.properties.keys()
for s in keys :
self.f.write ("\t"*3 + str(o.properties[s]) +
"\n")
self.f.write ("\t"*2 + "</DiaProperties>\n")
self.f.write ("\t"*1 + "</DiaObject>\n\n")
self.f.write ("\t"*0 + "</layer>\n")
def end_render (self) :
self.f.close()
# dia-python keeps a reference to the renderer class and uses it on demand
dia.register_export ("PyDia Object Export", "diapyo", ObjRenderer())
import sys, dia
# sys.path.insert(0, 'd:/graph/dia/dia')
sys.argv = [ 'dia-python' ]
class DumpRenderer :
def __init__ (self) :
pass
def begin_render (self, data, filename) :
# DiagramData
self.f = open(filename, "w")
self.f.write("Data: " + str(data) + "\n")
self.f.write("Extents: "+ str(data.extents) + "\n")
self.f.write("active_layer: " + str(data.active_layer) + " " \
+ data.active_layer.name + "\n")
#self.f.write("grid .width: " + str(data.grid.width) \
# + " .height" + str(data.grid.height) \
# + "visible: " + str(data.visible) + "\n")
def end_render (self) :
self.f.close()
def set_linewidth (self, width) :
self.line_width = width
def set_linecaps (self, mode) :
self.line_caps = mode
def set_linejoin (self, mode) :
self.line_join = mode
def set_linestyle (self, style) :
self.line_style = style
def set_dashlength (self, length) :
self.dash_length = length
def set_fillstyle (self, style) :
self.fill_style = style
def set_font (self, font, size) :
self.font = font
def draw_line (self, start, end, color) :
self.f.write("draw_line:" + str(start) + str(end) + str(color) + "\n")
def draw_polyline (self, points, color) :
self.f.write("draw_polyline: " + str(color) + "\n")
for pt in points :
self.f.write ("\t" + str(pt) + "\n")
def draw_polygon (self, points, color) :
self.f.write("draw_polygon: " + str(color) + "\n")
for pt in points :
self.f.write ("\t" + str(pt) + "\n")
def fill_polygon (self, points, color) :
self.f.write("fill_polygon: " + str(color) + "\n")
for pt in points :
self.f.write ("\t" + str(pt) + "\n")
def draw_rect (self, rect, color) :
self.f.write("draw_rect: " + str(rect) + str(color) + "\n")
def fill_rect (self, rect, color) :
self.f.write("fill_rect: " + str(rect) + str(color) + "\n")
def draw_arc (self, center, width, height, angle1, angle2, color) :
self.f.write("draw_arc: " + str(center) + ";" \
+ str(width) + "x" + str(height) + ";" \
+ str(angle1) + "," + str(angle2) + ";" + str(color) +
"\n")
def fill_arc (self, center, width, height, angle1, angle2, color) :
self.f.write("fill_arc: " + str(center) + ";" \
+ str(width) + "x" + str(height) + ";" \
+ str(angle1) + "," + str(angle2) + ";" + str(color) +
"\n")
def draw_ellipse (self, center, width, height, color) :
self.f.write("draw_ellipse: " + str(center) \
+ str(width) + "x" +str(height) + ";" + str(color) +
"\n")
def fill_ellipse (self, center, width, height, color) :
self.f.write("fill_ellipse: " + str(center) \
+ str(width) + "x" +str(height) + ";" + str(color) +
"\n")
def draw_bezier (self, bezpoints, color) :
self.f.write("draw_bezier: " + str(color) + "\n")
for pt in bezpoints :
self.f.write ("\t" + str(pt) + "\n")
def fill_bezier (self, bezpoints, color) :
self.f.write("fill_bezier: " + str(color) + "\n")
for pt in bezpoints :
self.f.write ("\t" + str(pt) + "\n")
def draw_string (self, text, pos, alignment, color) :
self.f.write("draw_string: [" + text + "]; " + str(pos) \
+ str(alignment) + "; " +str(color))
def draw_image (self, point, width, height, image) :
self.f.write("draw_image: " + str(point) + str(width) + "x"
+str(height) \
+ " " + image.filename + "\n")
self.f.write("<rgb_data>" + image.rgb_data + "</rgb_data>")
self.f.write("<mask_data>" + image.mask_data + "</mask_data>")
# dia-python keeps a reference to the renderer class and uses it on demand
dia.register_export ("PyDia Render Export", "diapyr", DumpRenderer())
-------- Hans "at" Breuer "dot" Org -----------
Tell me what you need, and I'll tell you how to
get along without it. -- Dilbert