justvanrossum / drawbot-skia

A cross-platform subset of the DrawBot drawing API, using Skia
Apache License 2.0
77 stars 8 forks source link

drawbot-skia and Django #113

Open meyouwe opened 1 year ago

meyouwe commented 1 year ago

Hey Just, this kind of left field. I have ported some drawbot code to drawbot-skia for use on a website. I use Django as my webserver. When I run the drawbot-skia code through a Django view and get it to output a skia SVG, it will output the file. Reload the page and it will output another file with a number variation. Reload again and it just keeps going. If I manually delete those files and reload the page. All the files that I have deleted are but back. Do this too quickly and you get the following warning:

drawbot_skia.errors.DrawbotError: size() can't be called if there's already a canvas active

It is odd that it is saying the canvas is still active even thought there is del canvas in the _def _saveImagesvg()

I feel that this file behaviour has something to do with the FILEWStream part. Like it leaves the stream open or something. Because the underlying python-skia library is in C it is impossible for me to figure out what is going on. Outside of Django this works fine.

So I know this is a vague question but have you seen this behaviour before? Any idea why it might be doing this? Ultimately I would just like to grab the SVG and put it in the HTML page.

Talk soon, Maarten

justvanrossum commented 1 year ago

Hi Maarten, it sounds like perhaps two things:

The solution for both could be the same.

Can you show me how you are calling drawbot-skia from your server app? Replace the actual drawing with a dummy rectangle to keep the example simple.

meyouwe commented 1 year ago

Hi Just. Thanks very much for getting back to me. The "not getting a fresh DB context" point you made is all I needed to fix the issue. Days of bouncing around heaps of different ideas and it was as simple as putting importlib.reload(db) in the top of function to reload the library each time.

import drawbot_skia.drawbot as db

def rect(request):
     importlib.reload(db)
     db.size(100, 100)
     db.fill(0, 1, 0)
     db.rect(0, 10, 50, 50)
     db.saveImage(f"media/rect.svg")
     return HttpResponse("<html><body>Done</body></html>")

Thank you again. Very grateful for your guidance.

justvanrossum commented 1 year ago

Reload the module may be effective but is not a great solution. I'll try to post a better fix later.

meyouwe commented 1 year ago

Actually adding db.newDrawing() also works. Would that be a better solution?

justvanrossum commented 1 year ago

Ah yes, newDrawing is a pretty essential thing indeed. Perhaps it's good enough, but try some stress-testing: if this code is indeed run from multiple threads, there will be hard-to-reproduce problems.

justvanrossum commented 1 year ago

The following example should be thread-safe:

from drawbot_skia.drawing import Drawing
# from drawbot_skia.path import BezierPath
# from math import *
# from random import randint, random

db = Drawing()
db.rect(10, 10, 200, 300)
db.saveImage("maarten_test.png")

I added some comments to show where you'd find names that are normally part of the "drawbot" namespace, but aren't part of the Drawing class.

meyouwe commented 1 year ago

This is great Just. Thank you very much for the hand up.