PyGTK CD Ripper Tutorial - Part 5

Part 1 of this pygtk tutorial stopped with a working toolbar and two place holders. Part 2 of this tutorial extended this minimal UI with a list of songs which was created using GTK ListView. Part 3 showed how to gain better control on ListView. Part 4 added some final polish to the UI and added an empty Preferences Dialog. This part of the tutorial will create the following preferences dialog:

Asunder_Preferences

To begin with, the preferences Dialog has to contain a gtk.Notebook, which is created with:

  notebook = gtk.Notebook()

In order to keep a well organized and readable code, the creation of the notebook instance is defined in a class method of PrefDialog and called in the init method of the class:

def insert_notebook(self):
    """
    demonstrate a gtk notebook 
    """
    notebook = gtk.Notebook()

...

def __init__(self, window):
    """
    initialize the preferences dialog
    """
    # title, parent, flags (0, for defaults), no buttons
    self.dialog = gtk.Dialog("Preferences", window, 0,
    (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
    self.dialog.set_default_size(250, 300)

    # creat the notebook instance
    self.insert_notebook()
    response = self.dialog.show_all()
    response = self.dialog.run()

    if response == gtk.RESPONSE_OK:
        print "The OK button was clicked"
    elif response == gtk.RESPONSE_CANCEL:
        print "The Cancel button was clicked"

    self.dialog.destroy()

Right now, this code does not do much. It attaches the notebook instance to the vbox which was automatically created inside the Dialog window. A notebook can have multiple pages, and each page in turn can have widgets attached to it with the packing method demonstrated before:

def insert_notebook(self):
    """
    demonstrate a gtk notebook 
    """
    notebook = gtk.Notebook()
    lables = []
    for page in ["General", "File Names", "Encode", "Advanced"]:
        # later, we can replace widget with a vbox, and then
        # the road is wide open to start packing other widgets inside it!
        widget = gtk.Label(page + " Inside")
        widget1 = gtk.Label("Second Label")
        vbox = gtk.VBox(False)
        vbox.pack_start(widget)
        vbox.pack_start(widget1)
        label = gtk.Label(page)
        notebook.append_page(vbox,label)

    self.dialog.vbox.pack_start(notebook)

The above method will create the following Preferences Dialog:

Prefs<em>with</em>Widgets

Download the complete code for the preferences dialog.

However, this notebook is still quite primitive. The widgets added are just text widgets and the notebook tabs titles are not given. To access a notebook's tab the following method can be used:

 # index is an integer, with N-1 representing the last page
 # e.g. for a notebook with 4 page, idx can be 0,1,2 or 3
 tab = notebook.get_nth_page(idx)

The setting the tab's label is done with:

 notebook.set_tab_label_text(tab, "some title you would like to choose")

Once a notebook page is direct accessible, it could also be manipulated, and widgets can be directly attached to it. Below, a gtk.FileChooserButton is added to the first tab (General). The file choose button, is not directly added to the page. Rather, it is packed inside a VBox instance. Into the same VBox gtk.Alignment instance, is also attached. This gtk.Aligment contains a flushed left gtk.Label.

    general = notebook.get_nth_page(0)
    filechooserbox = gtk.VBox(False)
    alignment = gtk.Alignment(0,0)
    filechooserlabel = gtk.Label("Destination folder")
    alignment.add(filechooserlabel)
    filechooserbutton = gtk.FileChooserButton("Destination folder")
    filechooserbutton.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
    filechooserbox.pack_start(alignment)
    filechooserbox.pack_start(filechooserbutton)
    general.add(filechooserbox)

This addition to the method insert_notebook will create the following preferences Dialog:

prefs<em>and</em>filechooser

Notice, that the new VBox and the gtk.FileChooserButton where added to the existing labels! Download the complete code for the preferences dialog with a FileChooserButton.

The complete assembly of widgets, is then added with the following add_notebook method:

def insert_notebook(self):
    """
    populate the notebook with widgets.
    """
    notebook = gtk.Notebook()

    for idx, page in enumerate(["General", "File Names", "Encode",
        "Advanced"]):
        # later, we can replace widget with a vbox, and then
        # the road is wide open to start packing other widgets inside it!
        widget = gtk.Label(page + " Inside")
        widget1 = gtk.Label("Second Label")
        vbox = gtk.VBox(False)
        vbox.pack_start(widget)
        vbox.pack_start(widget1)
        label = gtk.Label(page)
        notebook.append_page(vbox)
        pg = notebook.get_nth_page(idx)
        notebook.set_tab_label_text(pg, page)

    # here is how one can access a single tab
    another_label = gtk.Label("Accessed")
    pg.add(another_label)

    # populate general tab with proper stuff
    generalpage = notebook.get_nth_page(0)
    generalvbox = gtk.VBox(False)
    alignment = gtk.Alignment(0,0)
    filechooserlabel = gtk.Label("Destination folder")
    #filechooserlabel.set_justify(gtk.JUSTIFY_LEFT)
    alignment.add(filechooserlabel)
    filechooserbutton = gtk.FileChooserButton("Destination folder")
    filechooserbutton.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
    generalvbox.pack_start(alignment, False, False, 0)
    generalvbox.pack_start(filechooserbutton, False, False, 0)
    generalpage.add(generalvbox)

    # add check button for making m3u playlist
    make_playlist = gtk.CheckButton("Create M3U playlist")
    # make default as true
    make_playlist.set_active(True)
    generalvbox.pack_start(make_playlist, False, False, 0)

    # add the cdrom drive entry inside an hbox
    hbox = gtk.HBox(False)
    cdrom_label = gtk.Label("CD-ROM device: ")
    cdrom = gtk.Entry(128)
    cdrom.set_tooltip_text("Default: /dev/cdrom\n"
                           "Other example: /dev/hdc\n"
                           "Other example: /dev/sr0")
    hbox.pack_start(cdrom_label, False, False, 0)
    hbox.pack_start(cdrom, False, False, 0)
    generalvbox.pack_start(hbox, False,False, 0)

    eject_cd = gtk.CheckButton("Eject disc when finished")
    generalvbox.pack_start(eject_cd, False,False, 0)
    self.dialog.vbox.pack_start(notebook)

This code will produces the following General notebook tab:

general_tab

Again, the new widgets are all added to the previous labels, and the method becomes quite long and messy. This calls for some code clean up and reorganising. First, since all the pages (tabs) in the notebooks are going to have at least one widget, it makes since to create them and attach a gtk.VBox to each. Further, since long methods are hard to follow, it makes since to separate all the code that adds widgets to the General page. This code should be migrated to it's own method, e.g. def General_Page(self). Customizing other pages would be similarly done in extra methods, e.g. def FileNames(self) and so on.And Finally, since, the notebook instance should be accessible to all the methods that customize the pages, it should become an instance variable, e.g., instead of:

notebook = gtk.Notebook()

the notebook is created as:

self.notebook = gtk.Notebook()

And the complete method to create the notebook is now:

def insert_notebook(self):
    """
    create a notebook with 4 pages, each containing a gtk.VBox.
    """

    self.notebook = gtk.Notebook()

    for idx, page in enumerate(["General", "File Names", "Encode", 
        "Advanced"]):
        # later, we can replace widget with a vbox, and then
        # the road is wide open to start packing other widgets inside it!
        vbox = gtk.VBox(False)
        self.notebook.append_page(vbox)
        pg = self.notebook.get_nth_page(idx)
        self.notebook.set_tab_label_text(pg, page)

    self.set_general_page()
    self.dialog.vbox.pack_start(self.notebook)

the method set_general_page():

def set_general_page(self):
    """
    add widgets to the page General
    """    
    # populate general tab with proper stuff
    generalpage = self.notebook.get_nth_page(0)
    generalvbox = gtk.VBox(False)
    alignment = gtk.Alignment(0,0)
    filechooserlabel = gtk.Label("Destination folder")
    #filechooserlabel.set_justify(gtk.JUSTIFY_LEFT)
    alignment.add(filechooserlabel)
    filechooserbutton = gtk.FileChooserButton("Destination folder")
    filechooserbutton.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
    generalvbox.pack_start(alignment, False, False, 0)
    generalvbox.pack_start(filechooserbutton,  False, False, 0)

    generalpage.add(generalvbox)

    # add check button for making m3u playlist
    make_playlist = gtk.CheckButton("Create M3U playlist")
    # make default as true
    make_playlist.set_active(True)
    generalvbox.pack_start(make_playlist,  False, False, 0)

    # add the cdrom drive entry inside an hbox
    hbox = gtk.HBox(False)
    cdrom_label = gtk.Label("CD-ROM device: ")
    cdrom = gtk.Entry(128)
    cdrom.set_tooltip_text("Default: /dev/cdrom\n"
                           "Other example: /dev/hdc\n"
                           "Other example: /dev/sr0")
    hbox.pack_start(cdrom_label, False, False, 0)
    hbox.pack_start(cdrom, False, False, 0)
    generalvbox.pack_start(hbox, False,False, 0)

    eject_cd = gtk.CheckButton("Eject disc when finished")
    generalvbox.pack_start(eject_cd, False,False, 0)

The total length of the code is only a bit shorter. However, the code regarding the page General is completely encapsulated inside it's own method, and is called via:

 self.set_general_page()

The notebook itself is created with four pages, each containing a gtk.VBox widget. This makes modifying other pages quite straight forward, but avoids a long and complicated method to create a fully functioning notebook rich with widgets. The complete code for this part, demonstrating a gtk.Notebook with pages and embedded widget can be found in the git tag NotebookwithGeneral_Tab. The next tree pages of the notebook are still empty. However, in the original application asunder they all contain gtk widget which I found a bit tricky to control (namely, gtk.Frame). Hence, these pages will be populated with these multiple gtk.Frame widgets in the next part of this tutorial, and for now, the Preferences Dialog contains one page with the title General and looks like that:

general<em>tab</em>finished

This entry was tagged: python, programming, pygtk

Share this post:

Discussions/Feedback.

comments powered by Disqus