8

I have a fractal image creator. It creates a random fractal tree like thing. When done, it prompts the user to save the tree. I have it saving as a .svg right now and that works BUT I want it to save to a more convenient file type, like jpeg. Any ideas? Code:

import turtle
import random
from sys import exit
from time import clock
import canvasvg
turtle.colormode(255)
red = 125
green = 70
blue = 38        
pen = 10
def saveImg():
    print("Done.")
    save = input("Would you like to save this tree? Y/N \n")
    if save.upper() == "Y":
        t.hideturtle()
        name = input("What would you like to name it? \n")
        nameSav = name + ".svg"
        ts = turtle.getscreen().getcanvas()
        canvasvg.saveall(nameSav, ts)
    elif save.upper() == "N":
        def runChk():
            runAgain = input("Would you like to run again? Y/N (N will exit)")
            if runAgain.upper() == "Y":
                print("Running")
                main()
            elif runAgain.upper() == "N":
                print("Exiting...")
                exit()
            else:
                print("Invalid response.")
                runChk()
        runChk()
    else:
        print("Invalid response.")
        saveImg()

def tree(branchLen, t, red, green, blue, pen):
    time = str(round(clock()))
    print("Drawing... " + time)
    if branchLen > 3:
        pen = pen*0.8
        t.pensize(pen)
        if (red > 10 and green < 140):
            red = red - 15
            green = green + 8
    if branchLen > 5:
        angle = random.randrange(18, 55)
        angleTwo = 0.5*angle
        sub = random.randrange(1,16)
        t.color(red, green, blue)
        t.forward(branchLen)
        t.right(angleTwo)
        tree(branchLen-sub,t, red, green, blue, pen)
        t.left(angle)
        tree(branchLen-sub, t, red, green, blue, pen)
        t.right(angleTwo)
        t.backward(branchLen)

def main():
    t = turtle.Turtle()
    myWin = turtle.Screen()
    t.speed(0)
    t.hideturtle()
    t.left(90)
    t.up()
    t.backward(100)
    t.down()
    print("Please wait while I draw...")
    tree(random.randrange(60,95),t,red,green,blue, pen)
    saveImg()
main()
Colonel Thirty Two
  • 23,953
  • 8
  • 45
  • 85
Lillz
  • 283
  • 2
  • 5
  • 19

1 Answers1

7

Does it have to be a JPEG? Would PNG suffice?

If so you can convert from SVG to PNG using cairosvg. Unfortunately canvasvg.saveall() only allows you to pass it a file name into which it will write the SVG, so you will need to use a temporary file for the SVG and then convert that temp file to PNG using cairosvg.svg2png(). So something like this should do the job:

import os
import shutil
import tempfile

import canvasvg

name = raw_input("What would you like to name it? \n")
nameSav = name + ".png"
tmpdir = tempfile.mkdtemp()  # create a temporary directory
tmpfile = os.path.join(tmpdir, 'tmp.svg')  # name of file to save SVG to
ts = turtle.getscreen().getcanvas()
canvasvg.saveall(tmpfile, ts)
with open(tmpfile) as svg_input, open(nameSav, 'wb') as png_output:
    cairosvg.svg2png(bytestring=svg_input.read(), write_to=png_output)
shutil.rmtree(tmpdir)  # clean up temp file(s)

If you liked, you could write your own saveall() function based on canvasvg.saveall() (it's quite small) that accepts a file-like object instead of a file name, and writes to that object. Then you can pass in a StringIO object and not have to bother with the temporary file. Or your saveall() could just return the SVG data as a byte string.

mhawke
  • 84,695
  • 9
  • 117
  • 138
  • Ahh yes png will be fine. I'll try this tomorrow... is `tmp.svg` supposed to be nameSav or is tmp.svg a file created by os.path? – Lillz Jul 31 '14 at 05:15
  • `tmp.svg` is just a temporary file name .. you could choose any name you like. Because the temp file is created in a temp directory (with a random name) your file will be unique and you needn't worry about name collisions. – mhawke Jul 31 '14 at 07:46
  • I haven't used os and tempfile before but that error makes it sound like theres something wrong with the tmpfile variable.... it's not creating that directory? Does tempfile make a name for the file or do we need to apply the nameSav to it somehow? update: Just went to that directory in the error and tmp.svg is there and it is indeed the last image created.... Huh – Lillz Aug 01 '14 at 02:50
  • The problem is that in `shutil.rmtree(tmpfile)` tmpfile is the full path to the file (`tmp.svg`), but we actually want to remove the temp _directory_ created by `tempfile.mkdtemp()`. I have corrected my answer accordingly. – mhawke Aug 01 '14 at 03:54
  • 1
    alright no error now, except the canvas2svg warning: Items of type 'image' are not supported. but there is still no png output anywhere I can see.... – Lillz Aug 01 '14 at 04:30
  • It should be in whatever name the user enters with a ".svg" extension. This is actually wrong... I have fixed the code to save it with a ".png" instead. – mhawke Aug 01 '14 at 05:23
  • Oh, duh! Should've thought of that. Will test tomorrow. Meanwhile, do you know anything about python turtle graphics? [If you do, see if you can help here](http://stackoverflow.com/questions/25071174/turtle-speed-in-python-3-4). Thanks – Lillz Aug 01 '14 at 05:31
  • No, I know nothing about turtle graphics. There are plenty of resources available. If you have a specific question you could ask it in a new question. – mhawke Aug 01 '14 at 05:36
  • Hey I don't know if you'll still see this since it's an answered question, but I'll make it quick. When I try to save an image with the code above with the python launcher for windows (NOT IDLE), I get permission error with errno13 + the img name the user chooses (with .png) on the `with open(tmpfile) as svg_input, open(nameSav, 'wb') as png_output:` line. This doesn't happen if I run the code in IDLE -- It saves fine. Only when I use python launcher for windows does it occur. I'd give you the full error but the console window closes before I can do anything – Lillz Aug 02 '14 at 04:59
  • 1
    Your application does not have permission to create the output .png file in whichever directory the application is being run in (the current working directory). Presumably the working directory is different when you run python directly via the launcher. To fix, have your script change directory via `os.chdir()`, include the path to the directory in the `open()` (e.g. `open(os.path.join(savedir, nameSav), 'wb')` ), or have the user enter the file name including path. – mhawke Aug 03 '14 at 11:35
  • Apparently, canvasvg doesn't have the attribute saveall anymore. – 355durch113 Oct 16 '15 at 05:37