Drawing is cool!

Drawing pictures with turtle graphics can illustrate important computer science concepts while being fun as well.

This series of lessons shows students how to create a drawing program that draws pictures with color, different line characteristics, and shapes. It also will show students how to save and reload a picture in their program.

 

Saving and Loading!

When you work on a nice picture, it would be nice if you could save the picture. An XML file is a text file with a specific format that allows us to save and load files very easily. For instance, an XML file for saving and loading pictures might look like this:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<Drawing>
<Point x="233.0" y="300.0" color="black" width="1">False</Point>
<Point x="261.0" y="423.0" color="black" width="1">True</Point>
<Point x="427.0" y="402.0" color="black" width="1">False</Point>
<Point x="335.0" y="305.0" color="black" width="1">False</Point>
<Point x="262.0" y="424.0" color="black" width="1">False</Point>
<Point x="240.0" y="276.0" color="black" width="1">False</Point>
<Point x="327.0" y="173.0" color="black" width="1">True</Point>
<Point x="545.0" y="200.0" color="black" width="1">False</Point>
<Point x="431.0" y="66.0" color="black" width="1">False</Point>
<Point x="326.0" y="172.0" color="black" width="1">False</Point>
</Drawing>

The first line of an XML file is usually written as it appears above. Following that first line are tags that usually appear in pairs: beginning and ending tags. For instance, <Drawing> is the beginning tag and </Drawing> is the ending tag. The same is true of the Point tag: <Point> and </Point> are the beginning and ending of a point. Each point in the drawing can be recorded this way in a file when the program quits and this file can be read when it the program is started.

To work with XML files, we need to import a new module that makes working with XML files pretty easy. You put this line of code at the top of the file, with the other import lines.

from xml.dom import minidom

To write the points we'll add a new message to the Point class as shown here.

 
   def __str__(self):
      return '<Point x="'+str(self.x)+'" y="'+str(self.y)+ \
             '" color="'+self.color+'" width="'+ \
           str(self.width)+'">'+str(self.penup)+'</Point>'

This message is sent when a point is converted to a string which we'll do soon.

There are two places that code has to be added to the program. First, we'll write the code that writes the XML file when the program quits. The quitHandler code is called when the quit button is pressed, so we'll add this code to the quitHandler.

      file = open("drawing.xml","w")
      
      file.write( \
    '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n')
      file.write('<Drawing>\n')
      for i in range(len(data["points"])):
         file.write(str(data["points"][i])+'\n')

      file.write('</Drawing>\n')
      file.close()

This code writes the entire XML file to the file called drawing.xml. If this is done when the quit button is pressed, the program will remember its drawing when the program quits.

When the program starts, we want to read this file. This code uses the code that we imported at the top. This code should appear just before you call the mainloop function at the end of the code.

   try:
      xmldoc = minidom.parse("Drawing.xml")
      docroot = xmldoc.getElementsByTagName("Drawing")[0]
      pointElements = docroot.getElementsByTagName("Point")
 
      for pointElement in pointElements:
         if pointElement.firstChild.data == "True":          
            penup = True
         else:
            penup = False
         color = pointElement.attributes["color"].value
         width = int(pointElement.attributes["width"].value)
         x = float(pointElement.attributes["x"].value)
         y = float(pointElement.attributes["y"].value)
         p = Point(x,y,color,width,penup)
         data["points"].append(p)
      for point in data["points"]:
         point.draw(t)
  
      screen.update()
   except:
      print("file not found")

There are three things that you do when reading an XML file. You getElementsByTagName and you get the value of an attribute. Tag names are names like Drawing and Point. Attributes are names like x, y, color, and width. Finally, the data between the beginning and ending tag in an XML file is the firstChild's data. In this file, the penup value is the firstChild's data. This code repeats the body of the for loop for each of the Point elements it finds in the file.

Try it Out!

Test the code you wrote above and make sure it works first. Then you can try the exercise below.

Can you add a button that draws a square with a particular side length that you get through an Entry field?

If you get stuck, ask for help!