[pitivi] Redesign the effect list



commit 2cb3981e20fac40bc576e27553d5990d0f70dfb3
Author: Thibault Saunier <tsaunier gnome org>
Date:   Sun Jun 20 21:49:48 2010 -0400

    Redesign the effect list

 pitivi/effects.py       |   65 +++++++++++++++-
 pitivi/ui/effectlist.py |  198 +++++++++++++++++++++++++++++++---------------
 pitivi/ui/mainwindow.py |   11 +--
 3 files changed, 201 insertions(+), 73 deletions(-)
---
diff --git a/pitivi/effects.py b/pitivi/effects.py
index fc83209..1ea1c3a 100644
--- a/pitivi/effects.py
+++ b/pitivi/effects.py
@@ -28,6 +28,8 @@ import gst
 from pitivi.factories.operation import EffectFactory
 from pitivi.stream import get_stream_for_pad
 
+# Note: Some effects are available through the frei0r library and the libavfilter0 library
+
 # There are different types of effects available:
 #  _ Simple Audio/Video Effects
 #     GStreamer elements that only apply to audio OR video
@@ -37,6 +39,65 @@ from pitivi.stream import get_stream_for_pad
 #     that are too cumbersome to use as such
 #  _ Complex Audio/Video Effects
 
+(VIDEO_EFFECT, AUDIO_EFFECT)  = range(2)
+video_categories = (
+    ("All video effects", ("")),
+    ("Colors", ("cogcolorspace", "alphacolor", "videobalance", "gamma", "alpha",\
+                "frei0r-filter-color-distance", "frei0r-filter-contrast0r", \
+                "frei0r-filter-invert0r", "frei0r-filter-saturat0r", "frei0r-filter-r",\
+                "frei0r-filter-white-balance", "frei0r-filter-brightness", "frei0r-filter-b",\
+                "frei0r-filter-gamma", "frei0r-filter-hueshift0r", "frei0r-filter-transparency",\
+                "frei0r-filter-equaliz0r", "frei0r-filter-glow ", "frei0r-filter-g", "frei0r-filter-bw0r"\
+                )
+    ),
+    ("Noise", ("videorate", "frei0r-filter-edgeglow" )),
+    ("Analysis", ("videoanalyse", "videodetect", "videomark", "revtv", "navigationtest",\
+                  "frei0r-filter-rgb-parade", "frei0r-filter-vectorscope", "frei0r-filter-luminance",\
+                  )),
+    ("Blur", ("frei0r-filter-squareblur", )),
+    ("Geometry", ("cogscale", "aspectratiocrop", "cogdownsample", "videocrop", "videoflip",\
+                  "videobox", "gdkpixbufscale", "frei0r-filter-letterb0xed" \
+                  "frei0r-filter-k-means-clustering", "videoscale", "frei0r-filter-lens-correction",
+                  "frei0r-filter-perspective", "frei0r-filter-scale0tilt", "frei0r-filter-pixeliz0r",\
+                  "frei0r-filter-flippo", "frei0r-filter-3dflippo"
+                 )
+    ),
+    ("Fancy",("rippletv", "streaktv", "radioactv", "optv", "quarktv", "vertigotv",\
+              "shagadelictv", "warptv", "dicetv", "agingtv", "edgetv", "frei0r-filter-cartoon",\
+              "frei0r-filter-water", "frei0r-filter-nosync0r"
+             )
+    ),
+    ("Time", ("frei0r-filter-delay0r")),
+    ("Uncategorized", (""))
+)
+
+audio_categories = (("All audio effects", ("")),)
+
+def get_categories(effect, effectType):
+
+    effectName = effect.get_name()
+    categories = []
+
+    if effectType is AUDIO_EFFECT:
+        categories.append(audio_categories[0][0])
+        for categorie in audio_categories:
+            for name in categorie[1]:
+                if name == effectName:
+                    categories.append(categorie[0])
+        return categories
+
+    for categorie in video_categories:
+        for name in categorie[1]:
+            if name == effectName:
+                categories.append(categorie[0])
+
+    if categories == []:
+        categories.append("Uncategorized")
+
+    categories.append(video_categories[0][0])
+
+    return categories
+
 class Magician:
     """
     Handles all the effects
@@ -56,7 +117,7 @@ class Magician:
         factlist = gst.registry_get_default().get_feature_list(gst.ElementFactory)
         for fact in factlist:
             klass = fact.get_klass()
-            if "Effect" in klass:
+            if "Effect" in klass and not self._filterUslessEffect(fact):
                 factory = EffectFactory(fact.get_name(), fact.get_name())
                 added = self.addStreams(fact, factory)
                 if added is True:
@@ -66,6 +127,8 @@ class Magician:
                         self.simple_video.append(fact)
                     self.addFactory(fact.get_name(), factory)
 
+    def _filterUslessEffect(self, effect):
+        return effect.get_name() in ["colorconvert", "coglogoinsert", "festival", ]
 
     def _getEffectPlugins(self):
         # find all the pitivi plugins that provide effects
diff --git a/pitivi/ui/effectlist.py b/pitivi/ui/effectlist.py
index 7abb17c..2d79594 100644
--- a/pitivi/ui/effectlist.py
+++ b/pitivi/ui/effectlist.py
@@ -25,6 +25,7 @@ import gtk
 import pango
 import os
 import time
+import re
 
 from urllib import unquote
 from gettext import gettext as _
@@ -39,13 +40,18 @@ from pitivi.utils import beautify_length
 from xml.sax.saxutils import escape
 
 from pitivi.log.loggable import Loggable
+from pitivi.effects import AUDIO_EFFECT, VIDEO_EFFECT,\
+      audio_categories, video_categories, get_categories
 
 (COL_ICON,
  COL_ICON_LARGE,
- COL_INFOTEXT,
+ COL_NAME_TEXT,
+ COL_DESC_TEXT,
+ COL_EFFECT_TYPE,
+ COL_EFFECT_CATEGORIE,
  COL_FACTORY,
  COL_SEARCH_TEXT,
- COL_SHORT_TEXT) = range(6)
+ COL_SHORT_TEXT) = range(9)
 
 INVISIBLE = gtk.gdk.pixbuf_new_from_file(os.path.join(get_pixmap_dir(),
     "invisible.png"))
@@ -69,16 +75,26 @@ class EffectList(gtk.VBox, Loggable):
         self._ignoreRelease = False
 
         #Searchbox and combobox
-        self.filters = gtk.HBox()
-        self.search_box = gtk.Entry()
-        self.combobox = gtk.combo_box_new_text()
-        self.filters.pack_start(self.combobox, expand=False, padding=0)
-        self.filters.pack_start(self.search_box, expand=True, padding=0)
+        filters = gtk.HBox()
+        self.effectType = gtk.combo_box_new_text()
+        self.effectType.append_text("Video effects")
+        self.effectType.append_text("Audio effects")
+        self.effectCategory = gtk.combo_box_new_text()
+        self.show_categories(VIDEO_EFFECT)
+        self.effectType.set_active(VIDEO_EFFECT)
+
+
+        filters.pack_start(self.effectType, expand=False, padding=0)
+        filters.pack_start(self.effectCategory, expand=False, padding=0)
+
+        self.searchEntry = gtk.Entry()
+        self.searchEntry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
+        filters.pack_start(self.searchEntry, expand=True, padding=0)
 
         # Store
         # icon, icon, infotext, objectfactory
         self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, gtk.gdk.Pixbuf,
-                                        str, object, str, str)
+                                        str, str, int, object, object, str, str)
 
         # Scrolled Windows
         self.treeview_scrollwin = gtk.ScrolledWindow()
@@ -86,35 +102,56 @@ class EffectList(gtk.VBox, Loggable):
         self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)
 
         # TreeView
-        # Displays icon, long_name
+        # Displays name, description
         self.treeview = gtk.TreeView(self.storemodel)
         self.treeview_scrollwin.add(self.treeview)
         self.treeview.set_property("rules_hint", True)
-        self.treeview.set_headers_visible(False)
         self.treeview.set_property("search_column", COL_SEARCH_TEXT)
         self.treeview.set_property("has_tooltip", True)
         tsel = self.treeview.get_selection()
         tsel.set_mode(gtk.SELECTION_SINGLE)
 
-        pixbufcol = gtk.TreeViewColumn(_("Icon"))
-        pixbufcol.set_expand(False)
-        pixbufcol.set_spacing(5)
-        self.treeview.append_column(pixbufcol)
-        pixcell = gtk.CellRendererPixbuf()
-        pixcell.props.xpad = 6
-        pixbufcol.pack_start(pixcell)
-        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)
-
-        namecol = gtk.TreeViewColumn(_("Information"))
+        #pixbufcol = gtk.TreeViewColumn(_("Icon"))
+        #pixbufcol.set_expand(False)
+        #pixbufcol.set_spacing(5)
+        #self.treeview.append_column(pixbufcol)
+        #pixcell = gtk.CellRendererPixbuf()
+        #pixcell.props.xpad = 6
+        #pixbufcol.pack_start(pixcell)
+        #pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)
+
+        namecol = gtk.TreeViewColumn(_("Name"))
+        namecol.set_sort_column_id(COL_NAME_TEXT)
         self.treeview.append_column(namecol)
-        namecol.set_expand(True)
+        #namecol.set_expand(True)
         namecol.set_spacing(5)
-        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
-        namecol.set_min_width(150)
-        txtcell = gtk.CellRendererText()
-        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
-        namecol.pack_start(txtcell)
-        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)
+        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        namecol.set_fixed_width(150)
+        namecell = gtk.CellRendererText()
+        namecell.props.xpad = 6
+        namecell.set_property("ellipsize", pango.ELLIPSIZE_END)
+        namecol.pack_start(namecell)
+        namecol.add_attribute(namecell, "text", COL_NAME_TEXT)
+
+        desccol = gtk.TreeViewColumn(_("Description"))
+        desccol.set_sort_column_id(COL_DESC_TEXT)
+        self.treeview.append_column(desccol)
+        desccol.set_expand(True)
+        desccol.set_spacing(5)
+        desccol.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
+        desccol.set_min_width(150)
+        desccell = gtk.CellRendererText()
+        desccell.props.xpad = 6
+        desccell.set_property("ellipsize", pango.ELLIPSIZE_END)
+        desccol.pack_start(desccell)
+        desccol.add_attribute(desccell, "text", COL_DESC_TEXT)
+
+        self.effectType.connect ("changed", self._effectTypeChangedCb)
+        
+        self.effectCategory.connect ("changed", self._effectCategoryChangedCb)
+
+        self.searchEntry.connect ("changed", self.searchEntryChangedCb)
+        self.searchEntry.connect ("icon-press", self.searchEntryIconClickedCb)
 
         self.treeview.connect("button-press-event",
                               self._treeViewButtonPressEventCb)
@@ -128,27 +165,53 @@ class EffectList(gtk.VBox, Loggable):
                               self._dndDragBeginCb)
         self.treeview.connect("drag_data_get", self._dndDataGetCb)
 
-        self.pack_start(self.filters, expand=False)
+        self.pack_start(filters, expand=False)
         self.pack_end(self.treeview_scrollwin, expand=True)
-        #Get all available effects
-        self._addFactories(self._getEffects())
-        self.show_all()
 
-    def _addFactories(self, effects):
-        #TODO find a way to associate an icon to each effect
+        #create the filterModel
+        self.modelFilter = self.storemodel.filter_new()
+        self.modelFilter.set_visible_func(self._setRowVisible, data=None)
+        self.treeview.set_model(self.modelFilter)
+
+        #Add factories
+        self._addFactories(self.app.effects.simple_video, VIDEO_EFFECT)
+        self._addFactories(self.app.effects.simple_audio, AUDIO_EFFECT)
+
+        self.treeview_scrollwin.show_all()
+        filters.show_all()
+
+    def _addFactories(self, effects, effectType):
+        #TODO find a way to associate an icon/thumbnail to each effect
         thumbnail_file = os.path.join (os.getcwd(), "icons", "24x24", "pitivi.png")
         pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail_file)
 
         for effect in effects:
-            #TODO Check how it would look the best
-            visualname = ("<b>Name: </b>" + escape(unquote(effect.get_longname())) + "\n" +
-                    "<b>Description: </b>"+ effect.get_description())
+            uselessWords = re.compile('(Video |effect |Audio )')
+            name = uselessWords.sub("", (escape(unquote(effect.get_longname())))).title()
+            description = (escape(unquote(effect.get_description())))
+            categories = get_categories(effect, effectType)
 
             factory = self.app.effects.getFactory(effect.get_name())
-            self.storemodel.append ([pixbuf, pixbuf, visualname,
+            #visible = self._setRowVisible(self.storemodel, None, None)
+            self.storemodel.append ([pixbuf, pixbuf, name, description, effectType, categories,
                                     factory, effect.get_description(),
                                     factory.name])
 
+            self.storemodel.set_sort_column_id(COL_NAME_TEXT, gtk.SORT_ASCENDING)
+
+    def show_categories(self, effectType):
+        self.effectCategory.get_model().clear()
+
+        if effectType is VIDEO_EFFECT:
+            for categorie in video_categories:
+                self.effectCategory.append_text(categorie[0])
+
+        if effectType is AUDIO_EFFECT:
+            for categorie in audio_categories:
+                self.effectCategory.append_text(categorie[0])
+
+        self.effectCategory.set_active(0)
+
     def _dndDragBeginCb(self, view, context):
         self.info("tree drag_begin")
         path = self.treeview.get_selection().get_selected_rows()[1]
@@ -242,9 +305,15 @@ class EffectList(gtk.VBox, Loggable):
         return False
 
     def _treeViewQueryTooltipCb(self, treeview, x, y, keyboard_mode, tooltip):
-        pos = treeview.get_path_at_pos(x,y)[0]
-        treeview.set_tooltip_row (tooltip, pos)
-        tooltip.set_text(treeview.get_model()[pos[0]][4])
+        context = treeview.get_tooltip_context(x, y, keyboard_mode)
+
+        if context is None:
+            return False
+
+        treeview.set_tooltip_row (tooltip, context[1][0])
+        tooltip.set_icon(self.modelFilter.get_value(context[2], COL_ICON))
+        tooltip.set_text(self.modelFilter.get_value(context[2], COL_DESC_TEXT))
+
         return True
 
     def getSelectedItems(self):
@@ -264,35 +333,34 @@ class EffectList(gtk.VBox, Loggable):
         selection.set(selection.target, 8, factory)
         context.set_icon_pixbuf(INVISIBLE, 0, 0)
 
-    def _nothingUnderMouse(self, view, event):
-        return not bool(view.get_path_at_pos(int(event.x), int(event.y)))
-
-    def _getEffects():
-        raise NotImplementedError()
-
-    def _getDndTuple(self):
-        raise NotImplementedError()
-
-class VideoEffectList (EffectList):
+    def _effectTypeChangedCb(self, combobox):
+        self.modelFilter.refilter()
+        self.show_categories(combobox.get_active())
 
-    def __init__(self, instance, uiman):
-        EffectList.__init__(self,instance, uiman)
+    def _effectCategoryChangedCb(self, combobox):
+        self.modelFilter.refilter()
 
-    def _getEffects(self):
-        return self.app.effects.simple_video
+    def searchEntryChangedCb (self, entry):
+        self.modelFilter.refilter()
 
-    def _getDndTuple(self):
-        return  [dnd.VIDEO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]
+    def searchEntryIconClickedCb (self, entry, unused, unsed1):
+        entry.set_text("")
 
-class AudioEffectList (EffectList):
-
-    def __init__(self, instance, uiman):
-        EffectList.__init__(self,instance, uiman)
+    def _setRowVisible(self, model, iter, data):
+        if self.effectType.get_active() == model.get_value(iter, COL_EFFECT_TYPE):
+            if model.get_value(iter, COL_EFFECT_CATEGORIE) is None:
+                return False
+            if self.effectCategory.get_active_text() in model.get_value(iter, COL_EFFECT_CATEGORIE):
+                text = self.searchEntry.get_text().lower()
+                return text in model.get_value(iter, COL_DESC_TEXT).lower() or\
+                       text in model.get_value(iter, COL_NAME_TEXT).lower()
+            else:
+                return False
+        else:
+            return False
 
-    def _getEffects(self):
-        return self.app.effects.simple_audio
+    def _nothingUnderMouse(self, view, event):
+        return not bool(view.get_path_at_pos(int(event.x), int(event.y)))
 
     def _getDndTuple(self):
-        return  [dnd.AUDIO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]
-
-gobject.type_register(EffectList)
+        return  [dnd.EFFECT_TUPLE, dnd.EFFECT_TUPLE]
diff --git a/pitivi/ui/mainwindow.py b/pitivi/ui/mainwindow.py
index aada996..3b60d4d 100644
--- a/pitivi/ui/mainwindow.py
+++ b/pitivi/ui/mainwindow.py
@@ -56,7 +56,7 @@ from pitivi.receiver import receiver, handler
 import pitivi.formatters.format as formatter
 from pitivi.sourcelist import SourceListError
 from pitivi.ui.sourcelist import SourceList
-from pitivi.ui.effectlist import VideoEffectList, AudioEffectList
+from pitivi.ui.effectlist import EffectList
 from pitivi.ui.common import beautify_factory
 from pitivi.utils import beautify_length
 from pitivi.ui.zoominterface import Zoomable
@@ -404,15 +404,12 @@ class PitiviMainWindow(gtk.Window, Loggable):
         self.projecttabs = ProjectTabs()
 
         self.sourcelist = SourceList(instance, self.uimanager)
-        self.videoeffectlist = VideoEffectList(instance, self.uimanager)
-        self.audioeffectlist = AudioEffectList(instance, self.uimanager)
+        self.effectlist = EffectList(instance, self.uimanager)
         self.projecttabs.append_page(self.sourcelist, gtk.Label(_("Media Library")))
-        self.projecttabs.append_page(self.videoeffectlist, gtk.Label(_("Video Effects")))
-        self.projecttabs.append_page(self.audioeffectlist, gtk.Label(_("Audio Effects")))
+        self.projecttabs.append_page(self.effectlist, gtk.Label(_("Effect Library")))
         self._connectToSourceList()
         self.sourcelist.show()
-        self.videoeffectlist.show()
-        self.audioeffectlist.show()
+        self.effectlist.show()
 
         hpaned.pack1(self.projecttabs, resize=True, shrink=False)
         self.projecttabs.show()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]