Object persistence

In the preceding iteration, we added the capability to define multiple beat patterns. However, the beat patterns can be played only on a single script run. When the program is closed and restarted, all previous pattern data is lost.

We need a way to persist or store the beat patterns beyond a single program run. We need the ability to store values in some form of file storage and reload, play, and even edit the patterns. We need some form of object persistence.

Prepare for Lift Off

Python provides several modules for object persistence. The module that we will use for persistence is called the pickle module. This is a standard library of Python.

An object represented as a string of bytes is called pickle in Python. Pickling , also known as object serialization , lets us convert our object into a string of bytes. The process reconstructing of the object back from the string of bytes is called unpickling or deserialization.

More information about the pickle module is available at http://docs.python.org/2/library/pickle.html.

Let us illustrate it with a simple example:

import pickle
party_menu= ['Bread', 'Salad', 'Bordelaise','Wine', 'Truffles']
pickle.dump(party_menu, open( "mymenu.p", "wb" ) )

First, we serialize or pickle our list PartyMenu using pickle.dump and save it in an external file mymenu.p.

We later retrieve the object using pickle.load:

import pickle
menu= pickle.load( open( "mymenu.p", "rb" ) )
print menu # ['Bread', 'Salad', 'Bordelaise', 'Wine', 'Truffles']

Remember that in our previous iteration, we created a list, called self.pattern_list, where each item of the list is a dictionary that stores information about one single beat pattern.

If we need to reuse this information, we only need to pickle this self.pattern_list. Having saved the object, we can later easily unpickle the file to reconstruct our beat patterns.

Engage Thrusters

  1. We first need to add three top menu items to our program, as shown in the following screenshot:
    Engage Thrusters
    • File | Load Project
    • File | Save Project
    • File | Exit

    While we are creating our menu items let us also add an About menu item:

    • About | About

    Here, we are particularly interested in saving the project (pickling), and loading the project back (unpickling). The code for menu items is defined in a separate method called create_top_menu, as shown in the following code (also refer to the code in 3.09.py):

    def create_top_menu(self):
      self.menubar = Menu(self.root)
      self.filemenu = Menu(self.menubar, tearoff=0 ) 
      self.filemenu.add_command(label="Load Project",command=self.load_project )
      self.filemenu.add_command(label="Save Project",command=self.save_project)
      self.filemenu.add_separator()
      self.filemenu.add_command(label="Exit",command=self.exit_app) 
      self.menubar.add_cascade(label="File",menu=self.filemenu)
      self.aboutmenu = Menu(self.menubar, tearoff=0 ) 
      self.aboutmenu.add_command(label="About",command=self.about)
      self.menubar.add_cascade(label="About",menu=self.aboutmenu)
      self.root.config(menu=self.menubar)   

    The code is self-explanatory. We have created similar menu items in our last two projects. Finally, to display this menu, we call this method from our Main method.

  2. To pickle our object, we first import the pickle module into the current namespace as follows (see the code in 3.09.py):
    import pickle

    The Save Project menu has a command callback attached to self.save_project, which is where we define the pickling process:

    def save_project(self):
      self.record_pattern() #make sure last pattern is recorded
      file_name = tkFileDialog.asksaveasfilename(filetypes=[('Drum Beat File','*.bt')] , title="Save project as...")
      pickle.dump(self.pattern_list,open( file_name, "wb" ) "wb" ) )
      self.root.title(os.path.basename(filenamefile_name) + " - DrumBeast")

    The description of the code is listed as follows:

    • Recall that a pattern is added to self.pattern_list only when the pattern number is changed by the user. In situations where a user might have defined a beat pattern but may not have clicked on the pattern number's Spinbox widget, the pattern is not included in self.pattern_list. To make sure it is added, we first call self.record_pattern to capture this beat pattern.
    • The save_project method is called when the user clicks on the Save Project menu, hence, we need to give the user an option to save the project in a file. We have chosen to define a new file extension (.bt) to keep track of our beat patterns.
    • When the user specifies the filename with .bt extension, the data in the self.pattern_list object is dumped into the file using pickle.dump.
    • Lastly, the title of the Toplevel window is changed to reflect the filename.
  3. We are done pickling the object. Let us now code the unpickling process.

    The unpickling process is handled by a method load_project, which is called from the Load Project menu as follows:

    def load_project(self):
      file_name = tkFileDialog.askopenfilename(filetypes=[('Drum Beat File','*.bt')], title='Load Project') 
      if file_name == '':return
      self.root.title(os.path.basename(file_name) + " - DrumBeast")
      fh = open(file_name,"rb") # open the file in reading mode
      try:
        while True: # load from the file until EOF is reached
          self.pattern_list = pickle.load(fh)
      exceptEOFError:
        pass
      fh.close()
      try:
        self.Reconstruct_pattern(0,pattern_listself.pattern_list[0]['bpu'],pattern_listself.pattern_list[0]['units'])
      except:
        tkMessageBox.showerror("Error","An unexpected erroroccurred trying to reconstruct patterns")

    The description of the code is listed as follows:

    • When a user clicks on the Load Project menu, the first line of the method prompts him/her with an Open File window. When the user specifies a previously pickled file with a .bt extension, the filename is stored in a variable called file_name.
    • If the filename returned is none because the user cancels the Open File dialog, nothing is done.
    • If filename is supplied, the title of the Toplevel window is changed to add the filename. The file is then opened in read mode, and the contents of the file are read into self.pattern_list using pickle.load.
    • The self.pattern_list now contains the list of beat patterns defined in the previous pickle. The file is closed and the first pattern of self.pattern_list is reconstructed in the drum machine. If there are more than one patterns defined in the serialized file, you can view each of the patterns simply by changing the pattern number Spinbox widget.
    • Try playing any of the patterns, and you should be able to replay the pattern exactly as it was defined at the time of pickling.

      Tip

      Pickling, though great for serialization, is vulnerable to malicious or erroneous data. You may want to use pickle only if the data is from a trusted source, or if proper validation mechanisms are in place.

      You may also find the json module useful for serializing objects in JSON and ElementTree , or xml.minidom libraries relevant for parsing XML data.

  4. Now, let us complete coding our exit and about commands:
    def about(self):
      tkMessageBox.showinfo("About", "About Info")
    
    def exit_app(self):
      if tkMessageBox.askokcancel("Quit", "Really Quit?"):
        self.root.destroy()

    And add this line to our app method to override the Close button of the Toplevel window:

    self.root.protocol('WM_DELETE_WINDOW', self.exit_app)

    This is self-explanatory. We have done similar coding in our previous project.

Objective Complete – Mini Debriefing

In this iteration, we used Python's built-in pickle module to pickle and unpickle the beat patterns defined by the user.

This now lets us save patterns defined by the user. We have also provided the ability to load, replay, and edit the project later.

Now, if you define one or more beat patterns in your program you can save the project with a .bt file extension. You can later load the project and start working on it from the place where you had last left it.

While we were dealing with the top menu we also completed the code for the About and Exit menu items.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.200.109