Nikola: everything zen

Aktualisiertes zen-Thema für Nikola

Diese Seite wird mit dem statischen Webseitengenerator Nikola betrieben. Dabei wird das zen-Thema verwendet.

Die immer noch aktuelle Version des Themes verwendet Font Awesome 3.2.1, welches nicht mehr unterstützt wird. Die Icons lassen sich natürlich weiterhin verwenden, aber man muss auf aktualisierte und neue Icons verzichten.

Da ich dies sowie ein paar Kleinigkeiten am Theme verändert, aber an LESS vorbei einfach nur im CSS herumgepfuscht habe, werde ich dies nicht committen, sondern nur hier als gepacktes Archiv abladen (für den Fall, dass jemand Interesse daran hat).

Installation

Das Theme wird wie gehabt im entsprechenden Verzeichnis entpackt (nikola_instanz/themes/) und anschließend in der conf.py zu

THEME = "zen_fa4"

geändert. Die Änderungen werden natürlich erst nach dem nächsten

nikola build

wirksam.

Less LESS with CSS

Die zen-Themes sind mit dem LESS-Framework erstellt. Der Vorteil daran ist, dass die LESS-Dateien in CSS-Dateien kompiliert werden, die dann ebenso editiert werden können.

Die CSS-Dateien befinden sich in .../themes/zen_fa4/assets/css/, im Fall des zen-Themes benötigt man nur die main.css. Die aktualisierten Font Awesome-Fonts befinden sich (logischerweise) in .../themes/zen_fa4/assets/fonts/.

Möchte man bei LESS bleiben, steht dafür aber auch ein Nikola-Plugin bereit.

Mediaplayer mit VLC

Mediaplayer mit LibVLC realisieren

VLC ist nicht nur ein Multimediaplayer, sondern auch ein Framework, zu dem Python-Bindings verfügbar sind. In diesem Beispiel wird analog zum GStreamer-Artikel ein einfacher Mediaplayer mittels LibVLC umgesetzt.

/images/20_vlc_player.thumbnail.png

LibVLC

Voraussetzung für die Verwendung ist die Installation der Python-Bindings. Diese sind unter der Paketbezeichnung python-vlc zu finden.

Glade

  • Darstellungsbereich der Mediendatei: Widget Gtk.DrawingArea
  • Steuerungselemente: Vor-/Zurückspulen (Gtk.Button), Pause (Gtk.Togglebutton)
  • Medienauswahl: Buttons, um Video- oder Bilddatei anzuzeigen
  • Playback manipulieren: Buttons zum Stummschalten und Drehen des Videos

Python

Player einrichten

Der VLC-Player wird initiiert, sobald das dazugehörige Widget, in diesem Fall also Gtk.DrawingArea gezeichnet wird. Dazu wird das Signal realize genutzt, das grundsätzlich für die Klasse der Widgets verfügbar ist.

vlcOptions = "--no-xlib"
win_id = widget.get_window().get_xid()
setup_player(vlcOptions)
vlcInstance = vlc.Instance(options)
player = vlcInstance.media_player_new()
player.set_xwindow(win_id)

Als Optionen können Kommandozeilenoptionen von VLC übergeben werden. Im Beispiel wird beim Klick auf den "Rotate"-Button das Bild um 180° gedreht. Der Player wird erneut initiiert und die zusätzliche Option --video-filter=transform{type=180} übergeben.

Medium abspielen

Wie auch der GStreamer-Player kann der VLC-Player viele Video-/Audio- oder Bild-Formate anzeigen bzw. abspielen.

player.set_mrl(file_url)
#Datei abspielen
player.play()
#Pause/Play-Schalter
player.pause()

Positionsanzeige

Die Umsetzung des Fortschrittsbalkens und die Nutzung als Schiebereglers gestaltet sich ziemlich einfach.

#Position abfragen
player.get_position()
#Position bestimmen
player.set_position(val)

Der Wertebereich liegt dabei zwischen 0 und 1. Das Problem bei diesen Funktionen ist, dass sie relativ ressourcenintensiv arbeiten und das Playback mitunter verruckelt ist. Die Lösung im hiesigen Beispiel besteht darin, get_position-Abfragen zu umgehen, indem die Regler-Position herangezogen wird.

Möglichkeiten und Limitierungen

Die Nutzung der LibVLC-Python-Bindings erweist sich als einfach und angesichts der GStreamer-Umsetzung als geradezu intuitiv. Auch das "Headerbar-Problem" besteht nicht.

Auf der anderen Seite greift man hier auf großes Projekt zurück, man muss VLC und die Python-Bindings installiert haben anstatt einfach das GStreamer-Modul aus dem GObject Introspection-Repository zu verwenden. Auch ist im Test der Ressourcenverbrauch von VLC gegenüber GStreamer größer.

Mediaplayer mit GStreamer

Mediaplayer mit GStreamer 1.x realisieren

GStreamer ist ein Multimedia-Framework, das zum Anzeigen und (De-)Kodieren von Mediendateien verwendet werden kann.

/images/19_gst_player.thumbnail.png

Glade

  • Darstellungsbereich der Mediendatei: Widget Gtk.DrawingArea
  • Steuerungselemente: Vor-/Zurückspulen (Gtk.Button), Pause (Gtk.Togglebutton)
  • Medienauswahl: Buttons, um Video- oder Bilddatei anzuzeigen

Python

Player einrichten

Elemente und Pipelines

GStreamer handhabt alle möglichen Arten von Medienflüssen. Jeder Schritt in dieser Verarbeitungskette wird per Element definiert und in Pipelines verbunden. Eine solche Pipeline besteht typischerweise aus "source"-, "filter"-/"decode"- und "sink"-Elementen.

------------------------------------------------------
|  Pipeline                                          |
|                                                    |
|  -------------   ----------------   -------------- |
|  | source    |   | filter       |   | sink       | |
|  |           |->>| decoder      |->>|            | |
|  | Quelle    |   | Verarbeitung |   | Ausgabe    | |
|  -------------   ----------------   -------------- |
------------------------------------------------------

Nach diesem Prinzip wird dies mittels Gst-Modul umgesetzt:

#init Gst and create pipeline
Gst.init()
pipeline = Gst.Pipeline()

#create elements
src = Gst.ElementFactory.make("filesrc","source")
decode = Gst.ElementFactory.make("decodebin","decode")
sink = Gst.ElementFactory.make("xvimagesink")

#configure elements
src.set_property("location",file_location)

#add elements to pipeline
pipeline.add(src)
pipeline.add(decode)
pipeline.add(sink)

#link elements together
src.link(decode)
decode.link(sink)

Fertige Pipelines

Es besteht auch beispielsweise die Möglichkeit, Audio- und Videosignale voneinander getrennt werden, indem jeweils ein "videosink" und ein "audiosink" erstellt usw. Auf der anderen Seite gibt es vorgefertigte Pipelines für Standardaufgaben wie etwa das Abspielen von Medien. Ein solches Element ist "playbin", das den Code signifikant vereinfacht:

Gst.init(None)
player = Gst.ElementFactory.make("playbin","player")
sink = Gst.ElementFactory.make("xvimagesink")
player.set_property("uri",uri_of_file)
player.set_property("video-sink", sink)

Und los!

Eine Pipeline oder ein "playbin"-Element können nun über Gst.STATE gesteuert werden:

player.set_state(Gst.State.PLAYING)
player.set_state(Gst.State.PAUSED)

Fortschrittsanzeige

Die Fortschrittsanzeige ist an dieser Stelle keine Gtk.ProgressBar sondern eine horizontale Gtk.Scale. Mit diesem Widget lässt sich nicht nur eine Position anzeigen, sondern auch per Maus setzen. Für letzteres wird das Signal value-changed benötigt. Streng genommen ist das Signal change-value an dieser Stelle die sauberere Lösung, die im nachfolgenden Beitrag zur Umsetzung des Mediaplayers mit LibVLC verwendet wird.

Möglichkeiten und Limitierungen

Bei der Einarbeitung in GStreamer stolpert man (an dieser Stelle generalisiert die Autorin weitgehend und möglicherweise unbegründet) über diverse Hürden:

Es gibt eine Reihe von Tutorials. Die Umsetzung wird durch zwei Umstände erschwert:

  1. Die primäre Sprache von und mit GStreamer ist C. Mit Python steht man eher auf experimentellem Boden.
  2. Durch die Versionssprünge sowohl bei GStreamer (von 0.10 auf 1.x) als auch Python (2.x auf 3.x) funktionieren viele ältere Anleitungen nicht mehr ohne weiteres.

Es gibt weiterhin Effekte, die sich mir nicht erschließen. Das in diesem Artikel aufgeführte Beispiel funktioniert nicht, wenn das Gtk-Fenster eine Headerbar enthält. Es sollte mit der Verwendung von "gtksink" lösbar sein, aber dies wiederum verweigert das Abspielen in einem zugewiesenen Widget. Die Komplexität und Mächtigkeit von GStreamer ist massiv verwirrend.

GPT: v0.4 Release

JFTR: v0.4 des GoProTools veröffentlicht

Das GoProTool hat ein wenig Zuneigung bekommen. In diesem Zuge habe ich aus dem aktuellen Stand den Release v0.4 "scarlatina" erstellt.

NEU:

  • Import von jedem Verzeichnis aus (vorher nur von erkannten Speicherkarten) mit praktischen Buttons in der Toolbar

REPARIERT:

  • Ordnerauswahl beim Import
  • Fortschrittsanzeige bei Videoimport

"REPARIERT":

  • Headerbar in der Vorschau-Version entfernt, weil Bild bei Playback nur funktioniert, wenn das Fenster keine Headerbar enthält (fragt nicht - isso, weil isso)

Bemerkung

Dies wird voraussichtlich beim Wechsel von GStreamer zu LibVLC behoben, siehe auch LibVLC-Artikel.

/images/window_player_v0.4.thumbnail.png

Das Konfigurationssystem GSettings

Das GNOME-eigene Konfigurationssystem GSettings

GSettings ist GNOMEs zentrales Konfigurationssystem für Anwendungen. Es ist die Schnittstelle für verschiedenmögliche Backends, gemeinhin ist dies dconf.

Mittels grafischem (dconf-editor) oder Kommandozeilentool (gsettings) lassen sich Konfigurationen abfragen und manipulieren.

Das folgende Beispiel kann Hintergrundbilder laden, festlegen und bookmarken/favorisieren.

/images/17_gsettings.thumbnail.png

Schemas

Um eine Konfiguration für eine Anwendung zu erstellen, muss diese in einer Schema-Datei definiert werden. Diese Datei ist eine XML-formatierte Datei, die anschließend in sein maschinenlesbares Äquivalent überführt werden muss.

Ein Beispiel für eine Schema-Datei mit einer festzulegenden Eigenschaft (key) wäre etwa:

<schemalist>
  <schema id="org.gtk.Test" path="/org/gtk/Test/">

    <key name="string-key" type="s">
      <default>""</default>
      <summary>A string</summary>
      <description>
        Configuration key defined for a string. Default value is set to an empty string.
      </description>
    </key>

  </schema>
</schemalist>

Die Dateibenennung folgt der Vorgabe "schema.id.gschema.xml". Das Standardinstallationsverzeichnis für Schema-Dateien ist /usr/share/glib-2.0/schemas. Schema-Dateien können auch außerhalb dieses Verzeichnisses genutzt werden (z.B. lokal, zu Testzwecken), sie werden dann aber nicht vom dconf-editor angezeigt.

Die erforderliche Kompilierung erfolgt mit

glib-compile-schemas /path/to/schema/files/
#default directory
glib-compile-schemas /usr/share/glib-2.0/schemas/

Die kompilierte und nun von GSettings verwendete Datei ist gschemas.compiled.

Glade

Das Beispiel soll Bilder anzeigen, dafür wird das Widget GtkImage benötigt. Alle Steuerungselemente werden in der Headerbar untergebracht:

  • "Open File"-Button: öffnet einen FileChooserDialog
  • Switch: schaltet Desktop-Icons an oder ab
  • "Fav"-Togglebutton: bookmarkt angezeigte Dateien, zeigt an, ob angezeigte Datei als Favorit markiert ist
  • "Set as wallpaper"-Button: angezeigte Datei als Hintergrundbild verwenden
  • MenuButton: unterhalb des Buttons wird eine Liste der favorisierten Dateien angezeigt, die von dort aus aufgerufen werden können

Python

Globales Schema laden

Eine bestehende Konfiguration zu laden, geschieht einfach per

setting = Gio.Settings.new("full.schema.path")
#load desktop background configuration
setting = Gio.Settings.new("org.gnome.desktop.background")

Lokales Schema laden

Bei einem lokal gespeicherten Schema muss der Ort der schemas.compiled angegeben werden, bevor die Konfiguration geladen werden kann:

schema_source = Gio.SettingsSchemaSource.new_from_directory(os.getcwd(),
            Gio.SettingsSchemaSource.get_default(), False)
schema = Gio.SettingsSchemaSource.lookup(schema_source,"org.example.wallpaper-changer",False)
setting = Gio.Settings.new_full(schema, None, None)

Widget verknüpfen

Es ist möglich, GSettings-Eigenschaften direkt an Widgets zu binden. Diese können dann bidirektional Zustände anzeigen bzw. man kann Änderungen an ihnen vornehmen:

setting.bind("setting-key", widget, property, Gio.SettingsBindFlags...)

Im Beispiel wäre dies also

self.bg_setting.bind("show-desktop-icons", self.obj("switch"), "active", Gio.SettingsBindFlags.DEFAULT)

Der Schalter zeigt beim Start die aktuelle Einstellung an. Eine Änderung des Status ist sofort wirksam.

Werte abrufen und festlegen

Eigenschaften können mit get_"type" und set_"type" ermittelt und festgelegt werden. Die relevante Funktion ist vom festgelegten Schlüsseltyp abhängig, also get_string und set_string für Zeichenketten, get_int und set_int für Ganzzahlen usw. (siehe PyGObject API Reference).

Wird der Wert einer Eigenschaft per get_value(key) abgefragt, wird dies immer als Wert des Typs GLib.Variant zurückgegeben. Entsprechend erwartet die Funktion set_value(key) ebenfalls diesen Typ.

Die Inhalte dieser Werte lassen sich einfach in simple Datentypen konvertieren, z.B.

#return string
setting.get_value(key).get_string()
#return anything (list, string, bool etc.)
setting.get_value(key).unpack()

Umgekehrt lassen sich reguläre Datentypen nach folgendem Muster als GLib.Variant-Typ ausdrücken und an GSettings übergeben:

setting.set_value(key, GLib.Variant(string_type, value)

Eine Liste der verfügbaren Stringtypen finden sich in der GNOME Developer-Dokumentation.

Im Beispiel wird auf diese Art die Favoritenliste aktualisiert:

app_setting.set_value("favourites", GLib.Variant('as',fav_list))

Listings

Schema

org.example.wallpaper-changer.gschema.xml (Source)

<?xml version="1.0" encoding="utf-8"?>

<schemalist>

  <schema path="/org/example/wallpaper-changer/" id="org.example.wallpaper-changer">

    <key name="favourites" type="as">
      <default>[]</default>
      <summary>List of favourite wallpapers</summary>
      <description>
        Add or remove entry by pressing the 'fav' toggle button.
      </description>
    </key>

  </schema>

</schemalist>

Glade

17_gsettings.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkFileFilter" id="filefilter">
    <mime-types>
      <mime-type>image/*</mime-type>
    </mime-types>
  </object>
  <object class="GtkImage" id="image1">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="icon_name">emblem-favorite</property>
  </object>
  <object class="GtkMenu" id="menu">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
  </object>
  <object class="GtkApplicationWindow" id="window">
    <property name="can_focus">False</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <signal name="size-allocate" handler="on_window_size_allocate" swapped="no"/>
    <child>
      <object class="GtkImage" id="image_area">
        <property name="width_request">400</property>
        <property name="height_request">300</property>
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="stock">gtk-missing-image</property>
      </object>
    </child>
    <child type="titlebar">
      <object class="GtkHeaderBar">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="title">Wallpaper changer</property>
        <property name="has_subtitle">False</property>
        <property name="show_close_button">True</property>
        <child>
          <object class="GtkButton" id="open_button">
            <property name="label" translatable="yes">Open file...</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <signal name="clicked" handler="on_open_button_clicked" swapped="no"/>
          </object>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkLabel">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Desktop icons</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkSwitch" id="switch">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkMenuButton" id="fav_menu">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="popup">menu</property>
            <child>
              <placeholder/>
            </child>
          </object>
          <packing>
            <property name="pack_type">end</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="setwp_button">
            <property name="label" translatable="yes">Set as wallpaper</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <signal name="clicked" handler="on_setwp_button_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="pack_type">end</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <object class="GtkToggleButton" id="fav_button">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="image">image1</property>
            <property name="always_show_image">True</property>
            <signal name="toggled" handler="on_fav_button_toggled" swapped="no"/>
          </object>
          <packing>
            <property name="pack_type">end</property>
            <property name="position">3</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
  <object class="GtkImage" id="preview">
    <property name="width_request">200</property>
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="margin_right">5</property>
  </object>
  <object class="GtkFileChooserDialog" id="filechooser_dialog">
    <property name="width_request">800</property>
    <property name="height_request">600</property>
    <property name="can_focus">False</property>
    <property name="type_hint">dialog</property>
    <property name="transient_for">window</property>
    <property name="attached_to">window</property>
    <property name="filter">filefilter</property>
    <property name="preview_widget">preview</property>
    <property name="use_preview_label">False</property>
    <signal name="delete-event" handler="on_dialog_close" swapped="no"/>
    <signal name="file-activated" handler="on_filechooser_dialog_file_activated" swapped="no"/>
    <signal name="response" handler="on_filechooser_dialog_response" swapped="no"/>
    <signal name="update-preview" handler="on_filechooser_dialog_update_preview" swapped="no"/>
    <child internal-child="vbox">
      <object class="GtkBox" id="fcbox">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <property name="can_focus">False</property>
            <child>
              <placeholder/>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
      </object>
    </child>
    <child type="titlebar">
      <object class="GtkHeaderBar">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="title">Choose image file</property>
        <property name="show_close_button">True</property>
      </object>
    </child>
  </object>
</interface>

Python

17_gsettings.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import os
import gi
gi.require_version('Gtk','3.0')
from gi.repository import Gtk, Gio, GLib, GdkPixbuf

class Handler:

    def on_window_destroy(self,window):
        window.close()

    def on_dialog_close(self,widget,*event):
        widget.hide_on_delete()
        return True

    def on_filechooser_dialog_response(self,widget,response):
        if response == 1:
            self.on_dialog_close(widget)
        elif response == 0:
            app.uri = widget.get_filename()
            app.draw_pixbuf(app.uri)
            app.handle_fav(app.uri)
            self.on_dialog_close(widget)

    def on_filechooser_dialog_file_activated(self,widget):
        self.on_filechooser_dialog_response(widget,0)

    def on_open_button_clicked(self,widget):
        app.obj("filechooser_dialog").show_all()

    def on_setwp_button_clicked(self,widget):
        app.bg_setting.set_string("picture-uri","file://%s" % app.uri)

    def on_window_size_allocate(self,widget,size):
        app.draw_pixbuf(app.uri)

    def on_filechooser_dialog_update_preview(self,widget):
        if widget.get_filename() != None and os.path.isfile(widget.get_filename()):
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(widget.get_filename(),200,200,True)
            app.obj("preview").set_from_pixbuf(pixbuf)

    def on_fav_button_toggled(self,widget):
        if widget.get_active():
            #add file to fav_list if not in list
            if app.uri not in app.fav_list:
                app.fav_list.append(app.uri)
        else:
            #remove file from fav_list if in list
            if app.uri in app.fav_list:
                app.fav_list.remove(app.uri)
        #update GSettings entry for favourites
        app.app_setting.set_value("favourites", GLib.Variant('as', app.fav_list))
        #update fav list in popup menu
        popup = app.obj("menu")
        #remove all items
        for i in popup.get_children():
            popup.remove(i)
        #reload all items from fav_list
        for fav in app.fav_list:
            #only label menuitem with filename instead of path
            item=Gtk.MenuItem(os.path.split(fav)[1])
            item.connect("activate",self.on_choose_fav_from_menu,fav)
            popup.append(item)
        popup.show_all()

    def on_choose_fav_from_menu(self,widget,file):
        app.uri = file
        app.draw_pixbuf(file)
        app.handle_fav(file)

class ExampleApp:

    def __init__(self):

        self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0))
        self.app.connect("activate", self.on_app_activate)
        self.app.connect("shutdown", self.on_app_shutdown)

    def on_app_activate(self, app):
        builder = Gtk.Builder()
        builder.add_from_file("17_gsettings.glade")
        builder.connect_signals(Handler())
        self.obj = builder.get_object

        #load existing GSettings application config
        self.bg_setting = Gio.Settings.new("org.gnome.desktop.background")
        #get_value returns Gio formatted file path
        file = self.bg_setting.get_value("picture-uri")
        #convert path into string
        self.uri = file.get_string()[7:]
        #bind GSettings key to GTK+ object
        self.bg_setting.bind("show-desktop-icons", self.obj("switch"), "active", Gio.SettingsBindFlags.DEFAULT)

        #add GSettings schema from compiled XML file located in current directory (only recommended for test use, standard location: /usr/share/glib-2.0/schemas/)
        schema_source = Gio.SettingsSchemaSource.new_from_directory(os.getcwd(),
                Gio.SettingsSchemaSource.get_default(), False)
        schema = Gio.SettingsSchemaSource.lookup(schema_source,"org.example.wallpaper-changer",False)
        self.app_setting = Gio.Settings.new_full(schema, None, None)
        #convert value (GLib.Variant) into native list
        self.fav_list = self.app_setting.get_value("favourites").unpack()

        self.obj("window").set_application(app)
        self.obj("window").set_wmclass("Wallpaper changer","Wallpaper changer")
        self.obj("window").show_all()

        self.draw_pixbuf(self.uri)
        self.handle_fav(self.uri)

    def draw_pixbuf(self,file):
        size=self.obj("image_area").get_allocation()
        pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(file,size.width,size.height,True)
        self.obj("image_area").set_from_pixbuf(pixbuf)

    def handle_fav(self,uri):
        #set toggle button to correct state
        if uri in self.fav_list:
            self.obj("fav_button").set_active(True)
        else:
            self.obj("fav_button").set_active(False)

    def on_app_shutdown(self, app):
        self.app.quit()

    def run(self, argv):
        self.app.run(argv)

app = ExampleApp()
app.run(sys.argv)