Callouts with Inkscape update

767 views
Skip to first unread message

Terry Brown

unread,
Aug 31, 2010, 5:50:24 PM8/31/10
to leo-e...@googlegroups.com
Just pushed rev 2 to

https://code.launchpad.net/~leo-editor-team/+junk/inkcall

now includes inkscape-edit.doc.png which gives some minimal guidance on the minimal inkscape interaction needed. inkscape-edit.doc.png (attached) was generated with the script itself, which amuses me greatly.

Still needs better docs, maybe a special case for one line callouts.

Cheers -Terry

inkscape-edit.doc.png

Edward K. Ream

unread,
Aug 31, 2010, 8:21:15 PM8/31/10
to leo-e...@googlegroups.com
On Tue, Aug 31, 2010 at 4:50 PM, Terry Brown <terry_...@yahoo.com> wrote:
> Just pushed rev 2 to
>
> https://code.launchpad.net/~leo-editor-team/+junk/inkcall
>
> now includes inkscape-edit.doc.png which gives some minimal guidance on the minimal inkscape interaction needed.  inkscape-edit.doc.png (attached) was generated with the script itself, which amuses me greatly.

Thanks for this. I'm having trouble installing lxml on Windows 7,
which is a problem because I'd like Windows-centric screen shots.

easy_install lxml says "make sure to install the development packages
of libxml2 and libxslt". I've downloaded the windows binary packages
and put their paths in sitecustomize.py, but no joy importing or using
them.

Edward

Edward K. Ream

unread,
Aug 31, 2010, 8:54:01 PM8/31/10
to leo-e...@googlegroups.com
On Tue, Aug 31, 2010 at 7:21 PM, Edward K. Ream <edre...@gmail.com> wrote:

> Thanks for this.  I'm having trouble installing lxml on Windows 7,
> which is a problem because I'd like Windows-centric screen shots.

Switched to ubuntu, but still having problems. lxml exists with
python 2.6.5, but now I crash in resize_curve_box at the statement:

th = self.get_dim(filename, text_id, 'height') # text height

The filename is /tmp/<temp name>

And the crash is "No such file or directory"

Edward

Edward K. Ream

unread,
Aug 31, 2010, 9:04:46 PM8/31/10
to leo-e...@googlegroups.com
On Tue, Aug 31, 2010 at 7:54 PM, Edward K. Ream <edre...@gmail.com> wrote:

> The filename is /tmp/<temp name>
>
> And the crash is "No such file or directory"

Actually, the crash was due to not having inkscape installed.

Oh cool. It worked.

Edward
------------------------------------------------------------------------------
Edward K. Ream email: edre...@gmail.com
Leo: http://webpages.charter.net/edreamleo/front.html
------------------------------------------------------------------------------

Edward K. Ream

unread,
Aug 31, 2010, 10:02:43 PM8/31/10
to leo-editor


On Aug 31, 7:21 pm, "Edward K. Ream" <edream...@gmail.com> wrote:

> I'm having trouble installing lxml on Windows 7.

Sorry for the noise. I didn't read the installation instructions.
easy_install lxml==2.2.4 will install for Python 2.6.

Haha. I crashed in the same place. Apparently inkscape isn't on the
path...

Oh, I see. Inkscape has its own Python...

So either I tell Python about Inkscape or I tell Inkscape about
inkcall... Hmm. The internal inkscape python is 2.5...

Here is the code that isn't working:

cmd = [
"inkscape",
"--with-gui",
svgfile,
]

proc = subprocess.Popen(cmd)

I now have inkscape.bat that brings up inkscape, so we should be
close.

The following test script does bring up inkscape:

import subprocess

cmd = [
r"c:\scripts\inkscape.bat",
"--with-gui",
# svgfile,]

proc = subprocess.Popen(cmd)

However, making a similar change in three places in inkcall.py crashes
at these lines at get_dim:

for line in stdout.strip().split('\n'):
id_,x,y,w,h = line.split(',')

Ah. The first line is the basically the cmd above. Maybe I can
ignore it. Let's change the second line to:

data = line.split(',')
if len(data) != 5: continue
id_,x,y,w,h = data

Ok. That worked. We're almost home.

Inkscape didn't load "some_screen_shot.svg", presumably because the
path is not fully qualified.

Success! setting svgfile = os.path.join(os.getcwd(),svgfile) brings
up the file in inkscape!

Edward

Edward K. Ream

unread,
Aug 31, 2010, 10:05:32 PM8/31/10
to leo-editor


On Aug 31, 9:02 pm, "Edward K. Ream" <edream...@gmail.com> wrote:

> Success!  setting svgfile = os.path.join(os.getcwd(),svgfile) brings
> up the file in inkscape!

Now I have to figure out how to use all this working code ;-)

EKR

Terry Brown

unread,
Aug 31, 2010, 10:27:32 PM8/31/10
to leo-e...@googlegroups.com
On Tue, 31 Aug 2010 19:02:43 -0700 (PDT)
"Edward K. Ream" <edre...@gmail.com> wrote:

> > I'm having trouble installing lxml on Windows 7.
>
> Sorry for the noise. I didn't read the installation instructions.
> easy_install lxml==2.2.4 will install for Python 2.6.

Hmm, I tried (just now) installing lxml for Python 2.7, which is what I have on my Win 7 here (virtualbox :-). But didn't succeed with lxml==2.2.2. Will try other random numbers, but not impressed with lxml Windows install.

If I can get that working I'll try and clean up some of the other stuff you pointed out.

Cheers -Terry

Terry Brown

unread,
Aug 31, 2010, 11:21:58 PM8/31/10
to leo-e...@googlegroups.com
On Tue, 31 Aug 2010 19:02:43 -0700 (PDT)
"Edward K. Ream" <edre...@gmail.com> wrote:

> cmd = [
> r"c:\scripts\inkscape.bat",
> "--with-gui",
> # svgfile,]
>
> proc = subprocess.Popen(cmd)
>
> However, making a similar change in three places in inkcall.py crashes
> at these lines at get_dim:
>
> for line in stdout.strip().split('\n'):
> id_,x,y,w,h = line.split(',')
>
> Ah. The first line is the basically the cmd above. Maybe I can
> ignore it. Let's change the second line to:
>
> data = line.split(',')
> if len(data) != 5: continue
> id_,x,y,w,h = data

I think the .bat echoed the command to stdout, tripping up the read of the inkscape output.

I've added a --inkscape parameter, so you can do:

C:\Users\tbrown\inkcall>c:\Python26\python.exe inkcall.py --inkscape "c:\Program
Files (x86)\Inkscape\inkscape.exe"

And like the docs would say, if they were written, parameters can be passed in by setting attributes on an object and using it as the opt parameter of the constructor of the InkCall class, as main() sort of indicates.

Cheers -Terry

Edward K. Ream

unread,
Sep 1, 2010, 8:22:20 AM9/1/10
to leo-e...@googlegroups.com
On Tue, Aug 31, 2010 at 10:21 PM, Terry Brown <terry_...@yahoo.com> wrote:

> I've added a --inkscape parameter

This works. Thanks.

I pushed this change to edit_svg at rev 4.

svgfile = os.path.abspath(svgfile)

This is a good collaboration we have going here. Many thanks for your work.

Edward

P.S. I eagerly await your docs :-) Sometimes its easier to fix
crashers than to understand how to use a program!

EKR

Terry Brown

unread,
Sep 1, 2010, 12:05:07 PM9/1/10
to leo-e...@googlegroups.com
On Wed, 1 Sep 2010 07:22:20 -0500

"Edward K. Ream" <edre...@gmail.com> wrote:

> I pushed this change to edit_svg at rev 4.
>
> svgfile = os.path.abspath(svgfile)

I don't really see what this does, but if it's fixing something in Windows, great.

> This is a good collaboration we have going here. Many thanks for your work.
>
> Edward
>
> P.S. I eagerly await your docs :-) Sometimes its easier to fix
> crashers than to understand how to use a program!

Docs are in the module docstring as of rev 5 (just pushed).

I'll guess I'll add a scriptlet to extract them to .html (they're Rst)

Cheers -Terry

Edward K. Ream

unread,
Sep 1, 2010, 12:35:10 PM9/1/10
to leo-e...@googlegroups.com
On Wed, Sep 1, 2010 at 11:05 AM, Terry Brown <terry_...@yahoo.com> wrote:
> On Wed, 1 Sep 2010 07:22:20 -0500
> "Edward K. Ream" <edre...@gmail.com> wrote:
>
>> I pushed this change to edit_svg at rev 4.
>>
>>     svgfile = os.path.abspath(svgfile)
>
> I don't really see what this does, but if it's fixing something in Windows, great.

It's the same as os.path.join(os.getcwd(),svgfile), which is essential
the way I'm using inkcall.py.

> Docs are in the module docstring as of rev 5 (just pushed).

Thanks. I'll look at them immediately.

Edward

Edward K. Ream

unread,
Sep 1, 2010, 12:41:05 PM9/1/10
to leo-e...@googlegroups.com
On Wed, Sep 1, 2010 at 11:35 AM, Edward K. Ream <edre...@gmail.com> wrote:

>> Docs are in the module docstring as of rev 5 (just pushed).
>
> Thanks.  I'll look at them immediately.

Excellent docs! Thanks very much. I added a note about easy_install at rev 7.

EKR

Terry Brown

unread,
Sep 1, 2010, 1:32:39 PM9/1/10
to leo-e...@googlegroups.com
On Wed, 1 Sep 2010 11:41:05 -0500
"Edward K. Ream" <edre...@gmail.com> wrote:

> Excellent docs! Thanks very much. I added a note about easy_install at rev 7.

rev 9 includes using PIL (*if installed*) to get pixel perfect rendering of the screenshot. If it's not installed, either the screenshot may be rescaled (and less sharp) in the rendered png, or you need to edit the template to have the right screenshot size, and align it on a pixel boundary.

Cheers -Terry

Edward K. Ream

unread,
Sep 1, 2010, 2:21:00 PM9/1/10
to leo-e...@googlegroups.com
On Wed, Sep 1, 2010 at 12:32 PM, Terry Brown <terry_...@yahoo.com> wrote:

> rev 9 includes using PIL (*if installed*) to get pixel perfect rendering of the screenshot.  If it's not installed, either the screenshot may be rescaled (and less sharp) in the rendered png, or you need to edit the template to have the right screenshot size, and align it on a pixel boundary.

Just pulled rev 9 and installed PIL. This makes a huge difference.

Edward

Terry Brown

unread,
Sep 1, 2010, 2:23:40 PM9/1/10
to leo-e...@googlegroups.com
On Wed, 1 Sep 2010 12:32:39 -0500
Terry Brown <terry_...@yahoo.com> wrote:

> rev 9 includes using PIL (*if installed*) to get pixel perfect rendering of the screenshot. If it's not installed, either the screenshot may be rescaled (and less sharp) in the rendered png, or you need to edit the template to have the right screenshot size, and align it on a pixel boundary.

Gah :-) rev 11 finally gets guaranteed no rescaling of screenshot pixels if PIL is available. Inkscape allocates large bounding boxes for elements with drop shadows, which may have non-integer boundaries, leading to potential blurring of screenshot pixels. This is fixed now (an existing inkscape flag) and PIL is used to trim the transparent boundaries these oversized bboxes may introduce if balloons are near the edge.

Cheers -Terry

Edward K. Ream

unread,
Sep 1, 2010, 2:28:32 PM9/1/10
to leo-editor


On Sep 1, 1:23 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:

> Gah :-)  rev 11 finally gets guaranteed no rescaling of screenshot pixels if PIL is available.  Inkscape allocates large bounding boxes for elements with drop shadows, which may have non-integer boundaries, leading to potential blurring of screenshot pixels.  This is fixed now (an existing inkscape flag) and PIL is used to trim the transparent boundaries these oversized bboxes may introduce if balloons are near the edge.

I pushed rev 12. It warns if PIL is not available.

I am getting this traceback when I close inkscape:


c:\leo.repo\inkcall>ink

c:\leo.repo\inkcall>cd c:\leo.repo\inkcall

c:\leo.repo\inkcall>python26 inkcall.py --inkscape "c:\Program Files
(x86)\Inkscape\inkscape.exe"

c:\leo.repo\inkcall>c:\python26\python.exe inkcall.py --inkscape "c:
\Program Files (x86)\Inkscape\inkscape.exe"
Traceback (most recent call last):
File "inkcall.py", line 538, in <module>
main()
File "inkcall.py", line 189, in main
inkcall.make_png(filename)
File "inkcall.py", line 450, in make_png
img = Image.open(outfilename)
File "c:\python26\lib\site-packages\PIL\Image.py", line 1952, in
open
fp = __builtin__.open(fp, "rb")
IOError: [Errno 2] No such file or directory:
'some_screen_shot.co.png'

Edward

c:\leo.repo\inkcall>
>
> Cheers -Terry

Terry Brown

unread,
Sep 1, 2010, 2:56:06 PM9/1/10
to leo-e...@googlegroups.com
On Wed, 1 Sep 2010 11:28:32 -0700 (PDT)

"Edward K. Ream" <edre...@gmail.com> wrote:

> c:\leo.repo\inkcall>c:\python26\python.exe inkcall.py --inkscape "c:
> \Program Files (x86)\Inkscape\inkscape.exe"
> Traceback (most recent call last):
> File "inkcall.py", line 538, in <module>
> main()
> File "inkcall.py", line 189, in main
> inkcall.make_png(filename)
> File "inkcall.py", line 450, in make_png
> img = Image.open(outfilename)
> File "c:\python26\lib\site-packages\PIL\Image.py", line 1952, in
> open
> fp = __builtin__.open(fp, "rb")
> IOError: [Errno 2] No such file or directory:
> 'some_screen_shot.co.png'

Don't see why, when you're running from the directory containing the file, but try

c:\python26\python.exe inkcall.py \
--inkscape "c:\Program Files (x86)\Inkscape\inkscape.exe" \
--png c:\output.png

?

Out to run some errands now.

Cheers -Terry

Edward K. Ream

unread,
Sep 1, 2010, 3:46:17 PM9/1/10
to leo-e...@googlegroups.com
On Wed, Sep 1, 2010 at 1:56 PM, Terry Brown <terry_...@yahoo.com> wrote:


> c:\python26\python.exe inkcall.py \
>  --inkscape "c:\Program Files (x86)\Inkscape\inkscape.exe" \
>  --png c:\output.png

Now the message is: No such file or directory: c:\\output.png

EKR

Terry Brown

unread,
Sep 1, 2010, 5:18:38 PM9/1/10
to leo-e...@googlegroups.com
On Wed, 1 Sep 2010 14:46:17 -0500
"Edward K. Ream" <edre...@gmail.com> wrote:

> > c:\python26\python.exe inkcall.py \
> >  --inkscape "c:\Program Files (x86)\Inkscape\inkscape.exe" \
> >  --png c:\output.png
>
> Now the message is: No such file or directory: c:\\output.png

Oh, didn't see it properly the first time. It needs a screenshot filename as an argument. Try c:\python26\python.exe inkcall.py --help

Really I wasn't expecting you'd use it from the command line (although I want to fix any problems there). This script is working for me from Leo (in Ubuntu, admittedly).

import PyQt4.QtGui
app = g.app.gui.qtApp
pix = PyQt4.QtGui.QPixmap
w = pix.grabWindow(app.activeWindow().winId())
path = 'test.png'
w.save(path,'png')

import inkcall
reload(inkcall) # for dev. env.
ik = inkcall.InkCall()
ik.make_svg(path, ['test this'], [33])
ik.edit_svg(path)
ik.make_png(path)

There does still seem to be something wrong with the --png flag - I'll install leo in win 7 py 2.6 and see what happens.

I never could get lxml installed in win 7 py 2.7, fine in 2.6 with your lxml==2.2.4.

Cheers -Terry

Terry Brown

unread,
Sep 1, 2010, 6:07:40 PM9/1/10
to leo-e...@googlegroups.com
On Wed, 1 Sep 2010 16:18:38 -0500
Terry Brown <terry_...@yahoo.com> wrote:

> There does still seem to be something wrong with the --png flag - I'll install leo in win 7 py 2.6 and see what happens.

This script is working for me in windows 7, py 2.6, qt 4.7. Obviously it takes some shortcuts, it's up to the user to work out how to get inkcall.py on PYTHONPATH etc.
path should be absolute, as it could end up anywhere.

import PyQt4.QtGui
app = g.app.gui.qtApp
pix = PyQt4.QtGui.QPixmap
w = pix.grabWindow(app.activeWindow().winId())
path = 'test.png'
w.save(path,'png')

import sys
sys.path.append("c:\\users\\tbrown\\inkcall")


import inkcall
reload(inkcall) # for dev. env.

ik = inkcall.InkCall({
'template': "c:\\users\\tbrown\\inkcall\\template.svg",
'inkscape': r"c:\Program Files (x86)\Inkscape\inkscape.exe" ,
})
g.es('generate SVG')


ik.make_svg(path, ['test this'], [33])

g.es('edit SVG')
ik.edit_svg(path)
g.es('generate PNG')
ik.make_png(path)

I think I'll try and make the fail output more useful, seeing there seems to be lots of ways for this to fail to connect to various files it needs.

Cheers -Terry

Edward K. Ream

unread,
Sep 3, 2010, 5:24:39 AM9/3/10
to leo-e...@googlegroups.com
On Wed, Sep 1, 2010 at 5:07 PM, Terry Brown <terry_...@yahoo.com> wrote:

> I think I'll try and make the fail output more useful, seeing there seems to be lots of ways for this to fail to connect to various files it needs.

That would be good. Obscure tracebacks aren't exactly user-friendly.
I'll start using this code just as soon as I finish DnD. Maybe today.

Edward

Edward K. Ream

unread,
Sep 3, 2010, 6:39:42 PM9/3/10
to leo-editor


On Aug 31, 4:50 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:
> Just pushed rev 2 to
>
> https://code.launchpad.net/~leo-editor-team/+junk/inkcall

I've just begun to study the code in detail. My first impression is
"wow", this is very cool. For example, template.svg looks like a lot
of work. Perhaps inkscape created it? Or its precursor?

Terry, before I go on, would you mind if I converted the @auto node to
an @thin node? There are good reasons for doing so: it would allow
helpers of a method to be children of that method. It's remarkable how
effective this is in clarifying the context of helpers. I've already
done this in my @@auto study tree, but I think it would be useful to
have it done all the time.

The following are notes to myself about the code:

The string constants like "co_bc_1" looked like gobbledygook until I
saw their definitions at the start of InkCall class.

There are three steps (after initialization) to this code:

Step 1: make_svg.
Step 2: edit_svg.
Step 3: make_png.

Steps 2 and 3 are just calls to Inkscape via::

proc = subprocess.Popen(cmd, stderr=subprocess.PIPE)

where cmd contains the args to Inkscape.

Step 1 is the hard part. It consists of make_dom and its four
helpers. At present, I have no idea what is going on, but I look
forward to finding out :-) Clearly, at some point I am going to have
to learn a little about Inkscape itself and svg files.

It's frustrating that installing lxml is not at all easy. To my
knowledge, there is no binary installer for Python 3.1.1 available.
The binary installer for Python 3.1 doesn't work(!). It's too bad
that lxml is not part of Python distros.

Terry, is lxml significantly better than Python's standard
xml.etree.ElementTree module?

Edward

Edward K. Ream

unread,
Sep 3, 2010, 7:47:10 PM9/3/10
to leo-editor
On Fri, Sep 3, 2010 at 5:39 PM, Edward K. Ream <edre...@gmail.com> wrote:
>
>
> On Aug 31, 4:50 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:
>> Just pushed rev 2 to
>>
>> https://code.launchpad.net/~leo-editor-team/+junk/inkcall
>
> I've just begun to study the code in detail.  My first impression is
> "wow", this is very cool.  For example, template.svg looks like a lot
> of work.

To answer several of my own questions, the overall situation is clear:
svg images are xml files. Thus template.svg, as "complex" as it
looks, is just one or more pictures, with supporting info. Depending
on your point of view, Terry's scripts manipulate either the xml or
the pictures they represent.

The big advantage that lxml claims over ElementTree is that lxml
supports xpath queries of the xml. That is, using lxml it is possible
to isolate sub-parts of the svg images.

So xpath is a nice tool to have. Otoh, can it really be all that hard
to parse xml into whatever parts one wants? And why does lxml need
python-specific DLL's?

Edward

P.S. The Inkscape man page describes the command-line args:
http://inkscape.modevia.com/inkscape-man.html
I've copied the description of the relevant args to my study outline.

EKR

Edward K. Ream

unread,
Sep 3, 2010, 8:12:20 PM9/3/10
to leo-editor
On Sep 3, 6:47 pm, "Edward K. Ream" <edream...@gmail.com> wrote:

> The overall situation is clear:

Indeed it is. I can now understand the docstring :-)

Indeed, the template provides the "raw materials" for the callouts.
The user supplies the desired combination and location of callouts,
and the original screenshot, and the result is a new screenshots with
callouts applied.

At the code level, the fundamental operation is the xpath call. For
example::

object = template.xpath("//*[@id='co_text_1]")

This searches the xml file for an xml element whose id is 'co_text_1.
The //*[ syntax is a bit boggling at first, but once one sees the
pattern everything becomes clear. As I said in an earlier post, the
start of the InkCall class defines the objects that should be defined
in the template. Here is the list the actual list.

ids = [
"co_bc_1", # 1 digit black circle
"co_bc_2", # 2 digit black circle
"co_bc_text_1", # text holder for 1 digit black circle
"co_bc_text_2", # text holder for 2 digit black circle
"co_frame", # frame for speech balloon callout
"co_g_bc_1", # group for 1 digit black circle
"co_g_bc_2", # group for 2 digit black circle
"co_g_co", # group for speech balloon callout
"co_shot", # image for screen shot
"co_text_holder", # text holder for speech balloon callout
]

We are going to be seeing xpath statements involving these, so it's
good to notice the patterns in the names: co_<type>_id.

Naturally, these names can be "computed" using Python string
operations. For example:

part = dict([(g, template.xpath("//*[@id='%s']"%g)[0])
for g in 'co_g_co', 'co_g_bc_1', 'co_g_bc_2'])

This is much less horrifying now that we know the patterns for a)
xpath and b) object names.

So that's about it for the code ;-) It's not too difficult once one
sees the patterns. Of course, only Terry knows where the bodies are
hidden....

Now that I understand the code in detail, it's time to put it to
work...

EKR

Edward K. Ream

unread,
Sep 4, 2010, 7:53:35 AM9/4/10
to leo-editor


On Sep 3, 6:47 pm, "Edward K. Ream" <edream...@gmail.com> wrote:

> The big advantage that lxml claims over ElementTree is that lxml
> supports xpath queries of the xml.  That is, using lxml it is possible
> to isolate sub-parts of the svg images.

I would like remove the dependency of lxml. This would make Terry's
work much more easily accessible. Installing lxml is not nearly easy
enough.

Looking again at the code, I see only three uses of xpath. The first
is to find an element. The second ('..') is to find the parent of an
element. The third, in enable_filters, is a bit more complex.

I naively assume that transliterating the present script to
xml.ElementTree would be fairly straightforward. Imo, this work, even
if a bit more difficult than I imagine, would be well worthwhile.

Any comments, Terry, or others?

Edward

Edward K. Ream

unread,
Sep 4, 2010, 7:57:54 AM9/4/10
to leo-editor


On Sep 4, 6:53 am, "Edward K. Ream" <edream...@gmail.com> wrote:

> I would like remove the dependency of lxml.  This would make Terry's
> work much more easily accessible.

Let me emphasize that Terry's script is not just for me. Many people
will want to use it and @button slideshow to create slides. For this
reason, it is important to reduce installation problems as much as
possible. In other words, lxml must go.

EKR

Edward K. Ream

unread,
Sep 4, 2010, 11:03:47 AM9/4/10
to leo-editor


On Sep 3, 5:39 pm, "Edward K. Ream" <edream...@gmail.com> wrote:
> On Aug 31, 4:50 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:

> Terry, before I go on, would you mind if I converted the @auto node to
> an @thin node?

I just did this at rev 14. I probably won't do anything more for
today, so if this causes you problems feel free to revert. Otoh, I
think the @thin view of the code is much superior.

Edward

Terry Brown

unread,
Sep 4, 2010, 11:22:55 AM9/4/10
to leo-e...@googlegroups.com

--- On Sat, 9/4/10, Edward K. Ream <edre...@gmail.com> wrote:

Thinking this was a simple enough program, I started it specifically trying to avoid lxml, which I usually use without hesitation.

I could accept making a dictionary of @id to node mappings, because I don't think xml.etree.ElementTree can search on @id content (am I missing something?). But then, given an element 'e', I wanted to access its parent. How do you do that in xml.etree.ElementTree? I could find no way. Googling just suggests lxml :-) So yes, lxml could be eliminated, and given the small size of the file involved dumb searches are practical, it just adds a lot of cruft to compensate for the inadequacies of xml.etree.ElementTree.

Note that for this kind of thing I find it much more natural to code from a DOM perspective. Of course it could be done from a SAX perspective, for this kind of app. that seems an unnecessary chore to me.

But if you want to get rid of lxml you can, there's only dozens of nodes in the file, so even the simplest full recursion searches should work.

Cheers -Terry

Terry Brown

unread,
Sep 4, 2010, 11:48:11 AM9/4/10
to leo-e...@googlegroups.com

--- On Sat, 9/4/10, Terry Brown <terry_...@yahoo.com> wrote:

> But if you want to get rid of lxml you can, there's only
> dozens of nodes in the file, so even the simplest full
> recursion searches should work.

I mean there's only a small number of elements in the SVG (XML), so even the simplest...

Cheers -Terry

Edward K. Ream

unread,
Sep 4, 2010, 12:55:11 PM9/4/10
to leo-e...@googlegroups.com
On Sat, Sep 4, 2010 at 10:22 AM, Terry Brown <terry_...@yahoo.com> wrote:


> I could accept making a dictionary of @id to node mappings, because I don't think xml.etree.ElementTree can search on @id content (am I missing something?).  But then, given an element 'e', I wanted to access its parent.  How do you do that in xml.etree.ElementTree?  I could find no way.

You have got to be kidding me :-) How about finding children? If we
can do that, we can create a tree by hand.

Edward

Terry Brown

unread,
Sep 4, 2010, 12:54:38 PM9/4/10
to leo-e...@googlegroups.com
On Fri, 3 Sep 2010 15:39:42 -0700 (PDT)

"Edward K. Ream" <edre...@gmail.com> wrote:

> For example, template.svg looks like a lot
> of work. Perhaps inkscape created it?

It's from Inkscape, I just drew it. Inkscape lets you edit XML, so I could set the IDs I needed.

Cheers -Terry

Edward K. Ream

unread,
Sep 4, 2010, 12:58:54 PM9/4/10
to leo-e...@googlegroups.com

I see. Thanks.

EKR

Terry Brown

unread,
Sep 4, 2010, 4:09:10 PM9/4/10
to leo-e...@googlegroups.com
On Sat, 4 Sep 2010 11:55:11 -0500

"Edward K. Ream" <edre...@gmail.com> wrote:

Sure, the first attempt recursed the tree and created a dict of @id->element mapping. That same process could create an @id -> parent mapping. That would probably be most of what you need to remove lxml. It's too bad there's no decent xpath in standard python yet. For this SVG

<flowRoot id="flowRoot18">
<flowRegion id="rect3911">
<rect id="rect3911" width="165." height="93." x="123." y="114."/>
</flowRegion>
<flowPara id="co_text_holder">Some text</flowPara>
</flowRoot>

you need to find the parent of the flowPara to read the size for adjusting the frame. In XPath that's

doc.xpath("//*[@id='co_text_holder']/..")[0] # take first item of one item list

w/o xpath, it's somehow search for the element with @id='co_text_holder', and then somehow find its parent.

But sadly it would be easier to write inkcall without lxml than it would be to fix lxml installation on windows problems. Assuming that just asking on the lxml list wouldn't solve the problem, I assume it wouldn't, seeing we followed the official docs.

Of course, I only use windows occasionally for games :-} so it didn't occur to me that inkcall would be run on it.

And to be fair to lxml it does install relatively easily in Windows for python 2.6

I'll drop a note on their list.

Cheers -Terry

Edward K. Ream

unread,
Sep 4, 2010, 4:18:36 PM9/4/10
to leo-editor


On Sep 4, 3:09 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:


> w/o xpath, it's somehow search for the element with @id='co_text_holder', and then somehow find its parent.

How about this? ::

import xml.etree.ElementTree as etree
path = r'C:\leo.repo\inkcall\template.svg'
root = etree.parse(path).getroot()
print('='*30)
d = {}
def computeParents(d,e):
for child in e.getchildren():
d[child] = e
computeParents(d,child)

computeParents(d,root)
for key in d.keys():
print('child: %9s parent: %9s' % (id(key),id(d.get(key))))

Edward

Edward K. Ream

unread,
Sep 4, 2010, 4:34:12 PM9/4/10
to leo-editor


On Sep 4, 3:18 pm, "Edward K. Ream" <edream...@gmail.com> wrote:
> On Sep 4, 3:09 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:
>
> > w/o xpath, it's somehow search for the element with @id='co_text_holder', and then somehow find its parent.
>
> How about this? ::

That computes parents. The first step in associating ids with
elements is::

def getId(e):
return id(e)

ids = {}
def computeIds(d,e):
i = getId(e)
if i: d[i] = e
for child in e.getchildren():
computeIds(d,child)

for key in ids.keys():
val = ids.get(key)
print('id: %9s element: %9s' % (key,val))

Of course, getId is wrong. But its fine for prototyping. So we're
close.

So now, given an element e, we need only find it's id attribute. But
that is easy:

def getId(e):
return e.attrib.get('id')

To test, we do:

names = (
"co_bc_1", # 1 digit black circle
"co_bc_2", # 2 digit black circle
"co_bc_text_1", # text holder for 1 digit black circle
"co_bc_text_2", # text holder for 2 digit black circle
"co_frame", # frame for speech balloon callout
"co_g_bc_1", # group for 1 digit black circle
"co_g_bc_2", # group for 2 digit black circle
"co_g_co", # group for speech balloon callout
"co_shot", # image for screen shot
"co_text_holder", # text holder for speech balloon callout
)

for name in names:
print(name,ids.get(name))

Now how hard was that?

Edward

Edward K. Ream

unread,
Sep 4, 2010, 4:37:48 PM9/4/10
to leo-editor


On Sep 4, 3:34 pm, "Edward K. Ream" <edream...@gmail.com> wrote:

> names = (
>     "co_bc_1",        # 1 digit black circle
>     "co_bc_2",        # 2 digit black circle
>     "co_bc_text_1",   # text holder for 1 digit black circle
>     "co_bc_text_2",   # text holder for 2 digit black circle
>     "co_frame",       # frame for speech balloon callout
>     "co_g_bc_1",      # group for 1 digit black circle
>     "co_g_bc_2",      # group for 2 digit black circle
>     "co_g_co",        # group for speech balloon callout
>     "co_shot",        # image for screen shot
>     "co_text_holder", # text holder for speech balloon callout
> )
>
> for name in names:
>     print(name,ids.get(name))

The output is something like this:

co_bc_1 <Element {http://www.w3.org/2000/svg}path at 8824f10>
co_bc_2 <Element {http://www.w3.org/2000/svg}path at 881bbd0>
co_bc_text_1 <Element {http://www.w3.org/2000/svg}tspan at 3bbb930>
co_bc_text_2 <Element {http://www.w3.org/2000/svg}tspan at 8824350>
co_frame <Element {http://www.w3.org/2000/svg}path at 8bf9ed0>
co_g_bc_1 <Element {http://www.w3.org/2000/svg}g at 8824870>
co_g_bc_2 <Element {http://www.w3.org/2000/svg}g at 881be30>
co_g_co <Element {http://www.w3.org/2000/svg}g at 8bf92b0>
co_shot <Element {http://www.w3.org/2000/svg}image at 8bf9670>
co_text_holder <Element {http://www.w3.org/2000/svg}flowPara at
8bff6b0>

EKR

Terry Brown

unread,
Sep 4, 2010, 7:26:21 PM9/4/10
to leo-e...@googlegroups.com
On Sat, 4 Sep 2010 13:34:12 -0700 (PDT)

"Edward K. Ream" <edre...@gmail.com> wrote:

> return e.attrib.get('id')

or just e.get('id') I think.

> for name in names:
> print(name,ids.get(name))
>
> Now how hard was that?

Infinitely (1./0.) harder than not having to write anything (the lxml in linux case), but not bad in the lxml isn't available for windows case. I did email the lxml list, I'll see if they respond. Python's lack of full XML support is unfortunate (schemas as well as xpath), Java's had it all forever :-/ I know XML's not the be all and end all (JSON etc), but it's fairly important.

Cheers -Terry

Edward K. Ream

unread,
Sep 5, 2010, 8:37:11 AM9/5/10
to leo-e...@googlegroups.com
On Sat, Sep 4, 2010 at 6:26 PM, Terry Brown <terry_...@yahoo.com> wrote:

>> Now how hard was that?
>
> Infinitely (1./0.) harder than not having to write anything (the lxml in linux case), but not bad in the lxml isn't available for windows case.

I'm starting to believe you :-) I spent an hour last night slogging
through the inkcall code.

I'm hoping there is a principle that will simplify the transliteration
from lxml to ElementTree, namely that all methods in common between
lxml and ElementTree work exactly the same way. This is a theorem,
not a given.

It's important, as I found out. inkcall is doing quite a bit. For
example, in make_dom there are these lines:

for i in part.values():
i.xpath('..')[0].remove(i)

The hope is to replace this with something like:

for i in part.values():
# Recompute all parents, as in these posts.
parents_d = self.getParents()
parent = parents_d.get(i)
parent.remove(i)

In other words, remove had better work the same way in both environments!

Edward

Edward K. Ream

unread,
Sep 6, 2010, 1:15:25 PM9/6/10
to leo-editor


On Sep 5, 7:37 am, "Edward K. Ream" <edream...@gmail.com> wrote:

> I'm hoping there is a principle that will simplify the transliteration
> from lxml to ElementTree, namely that all methods in common between
> lxml and ElementTree work exactly the same way.  This is a theorem,
> not a given.

It principle appears to be true.

I've carefully transliterated all the xpath code, at each stage
verifying that the newly-computed objects are exactly what xpath
returns.

The only code to be transliterated is enable_filters. The code there
uses different xpath calls than typical. I don't expect major
problems.

I contend that the new code is actually at least as easy to understand
than the xpath code. I think it would have been just as easy to write
the new code as it would have been to write the old code.

In other words, for *this* application at least, lxml appears to add
exactly nothing of significance to ElementTree.

Edward

Edward K. Ream

unread,
Sep 6, 2010, 1:16:06 PM9/6/10
to leo-editor


On Sep 6, 12:15 pm, "Edward K. Ream" <edream...@gmail.com> wrote:

> I've carefully transliterated all the xpath code, at each stage
> verifying that the newly-computed objects are exactly what xpath
> returns.

The latest code is at rev 16.

EKR

Terry Brown

unread,
Sep 6, 2010, 1:54:58 PM9/6/10
to leo-e...@googlegroups.com
On Mon, 6 Sep 2010 10:15:25 -0700 (PDT)

"Edward K. Ream" <edre...@gmail.com> wrote:

> I contend that the new code is actually at least as easy to understand
> than the xpath code. I think it would have been just as easy to write
> the new code as it would have been to write the old code.
>
> In other words, for *this* application at least, lxml appears to add
> exactly nothing of significance to ElementTree.

Of course I disagree. I think a platform which does not provide at least moderate support for XPath does not really support XML, at least from a DOM perspective, which I think is the natural way to handle XML in this context (Inkscape extensions are written in python, using the DOM model, and requiring lxml(*)).

To put it another way, if you're writing DOM based XML code then you're familiar with XPath and expect to be able to use it. Say, for an HTML example, I want the captions of all the tables within the content element with headers which mention "precipitation":

doc.xpath("//*[@id='content']//caption[..//th[contains(., 'precipitation')]/text()")

Could you write code to find them without XPath? Of course. Should you need to? I don't think so. You'd certainly never have to write such code in Java :-/

(*) which makes you wonder how they distribute their extensions with the Windows version of Inkscape. I suspect they distribute python with inkscape in a subdirectory of the inkscape directory... :-)

Cheers -Terry

Edward K. Ream

unread,
Sep 6, 2010, 3:59:15 PM9/6/10
to leo-editor


On Sep 6, 12:54 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:
> On Mon, 6 Sep 2010 10:15:25 -0700 (PDT)
> "Edward K. Ream" <edream...@gmail.com> wrote:
>
> > I contend that the new code is actually at least as easy to understand
> > than the xpath code.  I think it would have been just as easy to write
> > the new code as it would have been to write the old code.
>
> > In other words, for *this* application at least, lxml appears to add
> > exactly nothing of significance to ElementTree.
>
> Of course I disagree.

We can disagree about xpath. Happily, distribution problems with lxml
will not trouble us now.

The present code at rev 19 is completely independent of lxml and will
work on Py3k. Alas, PIL has not yet been ported to Py3k, so we are
still better off using Py2k for this project.

Rev 19 will likely be close to the final code, although I'll tweak it
to make using it as easy as possible.

In any event, inkcall is a *great* tool. Many thanks for your vision
in creating it. Now it's time for me to start using it :-)

Edward

P.S. The error message about output.png was due to faulty command-line
arguments. Missing files won't show a crash either.

EKR

Terry Brown

unread,
Sep 6, 2010, 5:02:58 PM9/6/10
to leo-e...@googlegroups.com
On Mon, 6 Sep 2010 12:59:15 -0700 (PDT)
"Edward K. Ream" <edre...@gmail.com> wrote:

> The present code at rev 19 is completely independent of lxml and will
> work on Py3k. Alas, PIL has not yet been ported to Py3k, so we are
> still better off using Py2k for this project.

Doh. rev 20 uses Qt instead of PIL for image dimension determination, although PIL is still preferred as with PIL we also remove the excessively large transparent borders Inkscape puts on the image when the callout is near / over the edge of the screenshot. So:

PIL present -> no rescaling and borders trimmed

PIL absent and Qt present -> no rescaling, borders NOT trimmed

PIL absent and Qt absent -> image rescaling may occur unless screen shot size matches template placeholder. Borders not trimmed.

Cheers -Terry

Terry Brown

unread,
Sep 6, 2010, 8:33:30 PM9/6/10
to leo-e...@googlegroups.com
On Mon, 6 Sep 2010 16:02:58 -0500
Terry Brown <terry_...@yahoo.com> wrote:

> PIL present -> no rescaling and borders trimmed

rescaling be bad, of course, we want 1:1 pixel mapping on the annotated image from the screenshot.

Cheers -Terry

Matt Wilkie

unread,
Sep 7, 2010, 12:34:11 AM9/7/10
to leo-e...@googlegroups.com
> (*) which makes you wonder how they distribute their extensions with the Windows version of Inkscape.  I suspect they distribute python with inkscape in a subdirectory of the inkscape directory... :-)

Appears to be so, my windows PortableInkscape 0.48 folder has a python
sub-tree occupying some 22mb. Running its python.exe, with Inkscape
dir in PATH, sez "Python 2.6.5 (r265:79096, Mar 19 2010, 21:48:26)
[MSC v.1500 32 bit (Intel)] on win32"

-matt

Edward K. Ream

unread,
Sep 7, 2010, 9:35:26 AM9/7/10
to leo-e...@googlegroups.com
On Mon, Sep 6, 2010 at 4:02 PM, Terry Brown

> Doh.  rev 20 uses Qt instead of PIL for image dimension determination, although PIL is still preferred...

Thanks for this. As I'll explain in another thread, today I shall
move development of this code into Leo's core.

Edward

Reply all
Reply to author
Forward
0 new messages