[pitivi] render: Make clear which muxers and encoders are unsupported



commit 82966026ef5a980ea5f183424de859f77b5ba25a
Author: Alexandru Băluț <alexandru balut gmail com>
Date:   Sat Jul 23 22:58:36 2016 +0200

    render: Make clear which muxers and encoders are unsupported
    
    The unsupported muxers and encoders appear under an "Unsupported"
    element at the bottom of the comboboxes's model.
    
    Fixes https://phabricator.freedesktop.org/T7496
    
    Reviewed-by: Thibault Saunier <tsaunier gnome org>
    Differential Revision: https://phabricator.freedesktop.org/D1217

 data/ui/projectsettings.ui |   24 ----
 data/ui/renderingdialog.ui |   70 +------------
 pitivi/preset.py           |   10 +-
 pitivi/render.py           |  254 +++++++++++++++++++++++++++-----------------
 pitivi/utils/ui.py         |   21 ++--
 tests/test_render.py       |    2 +-
 6 files changed, 180 insertions(+), 201 deletions(-)
---
diff --git a/data/ui/projectsettings.ui b/data/ui/projectsettings.ui
index 9f4851e..99b9976 100644
--- a/data/ui/projectsettings.ui
+++ b/data/ui/projectsettings.ui
@@ -23,28 +23,6 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkListStore" id="model1">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">Standard (4:3)</col>
-      </row>
-    </data>
-  </object>
-  <object class="GtkListStore" id="model2">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">Standard PAL</col>
-      </row>
-    </data>
-  </object>
   <object class="GtkDialog" id="project-settings-dialog">
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
@@ -353,7 +331,6 @@
                                       <object class="GtkComboBox" id="dar_combo">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
-                                        <property name="model">model1</property>
                                         <child>
                                           <object class="GtkCellRendererText" id="renderer1"/>
                                           <attributes>
@@ -370,7 +347,6 @@
                                       <object class="GtkComboBox" id="par_combo">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
-                                        <property name="model">model2</property>
                                         <child>
                                           <object class="GtkCellRendererText" id="renderer2"/>
                                           <attributes>
diff --git a/data/ui/renderingdialog.ui b/data/ui/renderingdialog.ui
index d3338d5..9a9dece 100644
--- a/data/ui/renderingdialog.ui
+++ b/data/ui/renderingdialog.ui
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.19.0 -->
+<!-- Generated with glade 3.20.0 -->
 <interface>
   <requires lib="gtk+" version="3.10"/>
   <object class="GtkAdjustment" id="adjustment1">
@@ -14,64 +14,6 @@
     <property name="can_focus">False</property>
     <property name="icon_name">help-contents</property>
   </object>
-  <object class="GtkListStore" id="model1">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">MP4</col>
-      </row>
-    </data>
-  </object>
-  <object class="GtkListStore" id="model2">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">H.264</col>
-      </row>
-    </data>
-  </object>
-  <object class="GtkListStore" id="model3">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">29.97 fps</col>
-      </row>
-    </data>
-  </object>
-  <object class="GtkListStore" id="model4">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">AC-3</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">AAC</col>
-      </row>
-    </data>
-  </object>
-  <object class="GtkListStore" id="model5">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">6 channels (5.1)</col>
-      </row>
-    </data>
-  </object>
   <object class="GtkDialog" id="render-dialog">
     <property name="can_focus">False</property>
     <property name="border_width">12</property>
@@ -230,8 +172,6 @@
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="halign">start</property>
-                        <property name="model">model1</property>
-                        <property name="active">0</property>
                         <signal name="changed" handler="_muxerComboChangedCb" swapped="no"/>
                         <child>
                           <object class="GtkCellRendererText" id="renderer1"/>
@@ -476,7 +416,6 @@
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="halign">start</property>
-                            <property name="model">model2</property>
                             <signal name="changed" handler="_videoEncoderComboChangedCb" swapped="no"/>
                             <child>
                               <object class="GtkCellRendererText" id="renderer2"/>
@@ -514,7 +453,6 @@
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="halign">start</property>
-                            <property name="model">model3</property>
                             <signal name="changed" handler="_frameRateComboChangedCb" swapped="no"/>
                             <child>
                               <object class="GtkCellRendererText" id="renderer3"/>
@@ -613,7 +551,6 @@
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="halign">start</property>
-                            <property name="model">model5</property>
                             <signal name="changed" handler="_channelsComboChangedCb" swapped="no"/>
                             <child>
                               <object class="GtkCellRendererText" id="renderer5"/>
@@ -650,7 +587,6 @@
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="halign">start</property>
-                            <property name="model">model4</property>
                             <signal name="changed" handler="_audioEncoderChangedComboCb" swapped="no"/>
                             <child>
                               <object class="GtkCellRendererText" id="renderer4"/>
@@ -688,7 +624,6 @@
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="halign">start</property>
-                            <property name="model">model5</property>
                             <signal name="changed" handler="_sampleRateComboChangedCb" swapped="no"/>
                             <child>
                               <object class="GtkCellRendererText" id="renderer6"/>
@@ -823,5 +758,8 @@ This option is a good trade-off between quality of the rendered video and stabil
       <action-widget response="0">closebutton</action-widget>
       <action-widget response="0">render_button</action-widget>
     </action-widgets>
+    <child>
+      <placeholder/>
+    </child>
   </object>
 </interface>
diff --git a/pitivi/preset.py b/pitivi/preset.py
index 71a2aff..8c1ecc7 100644
--- a/pitivi/preset.py
+++ b/pitivi/preset.py
@@ -482,22 +482,22 @@ class AudioPresetManager(PresetManager):
 
 class RenderPresetManager(PresetManager):
 
-    def __init__(self, system):
+    def __init__(self, system, encoders):
         default_path = get_renderpresets_dir()
         user_path = os.path.join(xdg_data_home(), 'render_presets')
         PresetManager.__init__(self, default_path, user_path, system)
+        self.encoders = encoders
 
     def _deserializePreset(self, parser):
         container = parser["container"]
         acodec = parser["acodec"]
         vcodec = parser["vcodec"]
 
-        from pitivi.render import Encoders
-        if acodec not in [fact.get_name() for fact in Encoders().aencoders]:
+        if acodec not in [fact.get_name() for fact in self.encoders.aencoders]:
             raise DeserializeException("Audio codec not available: %s" % acodec)
-        if vcodec not in [fact.get_name() for fact in Encoders().vencoders]:
+        if vcodec not in [fact.get_name() for fact in self.encoders.vencoders]:
             raise DeserializeException("Video codec not available: %s" % vcodec)
-        if container not in [fact.get_name() for fact in Encoders().muxers]:
+        if container not in [fact.get_name() for fact in self.encoders.muxers]:
             raise DeserializeException("Container not available: %s" % vcodec)
 
         try:
diff --git a/pitivi/render.py b/pitivi/render.py
index 8cb99cb..8aee2f9 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -30,6 +30,7 @@ from gi.repository import Gtk
 
 from pitivi import configure
 from pitivi.check import missing_soft_deps
+from pitivi.preset import RenderPresetManager
 from pitivi.utils.loggable import Loggable
 from pitivi.utils.misc import path_from_uri
 from pitivi.utils.misc import show_user_manual
@@ -39,7 +40,6 @@ from pitivi.utils.ui import audio_rates
 from pitivi.utils.ui import beautify_ETA
 from pitivi.utils.ui import frame_rates
 from pitivi.utils.ui import get_combo_value
-from pitivi.utils.ui import model
 from pitivi.utils.ui import set_combo_value
 from pitivi.utils.widgets import GstElementSettingsDialog
 
@@ -52,9 +52,15 @@ class Encoders(Loggable):
     It is a singleton. Use `Encoders()` to access the instance.
 
     Attributes:
-        muxers (List[Gst.ElementFactory]): The avalaible muxers.
-        aencoders (List[Gst.ElementFactory]): The avalaible audio encoders.
-        vencoders (List[Gst.ElementFactory]): The avalaible video encoders.
+        supported_muxers (List[Gst.ElementFactory]): The supported available
+            muxers.
+        supported_aencoders (List[Gst.ElementFactory]): The supported available
+            audio encoders.
+        supported_vencoders (List[Gst.ElementFactory]): The supported available
+            video encoders.
+        muxers (List[Gst.ElementFactory]): The available muxers.
+        aencoders (List[Gst.ElementFactory]): The available audio encoders.
+        vencoders (List[Gst.ElementFactory]): The available video encoders.
         compatible_audio_encoders (dict): Maps each muxer name to a list of
             compatible audio encoders ordered by rank.
         compatible_video_encoders (dict): Maps each muxer name to a list of
@@ -105,7 +111,7 @@ class Encoders(Loggable):
     def __new__(cls, *args, **kwargs):
         """Returns the singleton instance."""
         if not cls._instance:
-            cls._instance = super(Encoders, cls).__new__(cls, *args, **kwargs)
+            cls._instance = super(Encoders, cls).__new__(cls)
             # We have to initialize the instance here, otherwise
             # __init__ is called every time we use Encoders().
             Loggable.__init__(cls._instance)
@@ -150,6 +156,20 @@ class Encoders(Loggable):
         for muxer in useless_muxers:
             self.muxers.remove(muxer)
 
+        self.factories_by_name = dict([(fact.get_name(), fact)
+                                       for fact in self.muxers + self.aencoders + self.vencoders])
+
+        good_muxers, good_aencoders, good_vencoders = zip(*self.SUPPORTED_ENCODERS_COMBINATIONS)
+        self.supported_muxers = set([muxer
+                                     for muxer in self.muxers
+                                     if muxer.get_name() in good_muxers])
+        self.supported_aencoders = set([encoder
+                                        for encoder in self.aencoders
+                                        if encoder.get_name() in good_aencoders])
+        self.supported_vencoders = set([encoder
+                                        for encoder in self.vencoders
+                                        if encoder.get_name() in good_vencoders])
+
         self.default_muxer, \
             self.default_audio_encoder, \
             self.default_video_encoder = self._pick_defaults()
@@ -182,28 +202,39 @@ class Encoders(Loggable):
         Returns:
             (str, str, str): The muxer, audio encoder, video encoder.
         """
-        muxer_names = [fact.get_name() for fact in self.muxers]
-        aencoder_names = [fact.get_name() for fact in self.aencoders]
-        vencoder_names = [fact.get_name() for fact in self.vencoders]
         for muxer, audio, video in self.SUPPORTED_ENCODERS_COMBINATIONS:
-            if muxer not in muxer_names or \
-                    audio not in aencoder_names or \
-                    video not in vencoder_names:
+            if muxer not in self.factories_by_name or \
+                    audio not in self.factories_by_name or \
+                    video not in self.factories_by_name:
                 continue
             self.info("Default encoders: %s, %s, %s", muxer, audio, video)
             return muxer, audio, video
         self.warning("No good combination of container and encoders available.")
         return Encoders.OGG, Encoders.VORBIS, Encoders.THEORA
 
+    def is_supported(self, factory):
+        """Returns whether the specified factory is supported."""
+        if type(factory) is str:
+            factory = self.factories_by_name[factory]
+        return factory in self.supported_muxers or\
+            factory in self.supported_aencoders or\
+            factory in self.supported_vencoders
+
     def _registry_feature_added_cb(self, registry, feature):
         # TODO Check what feature has been added and update our lists
         pass
 
 
-def beautify_factoryname(factory):
+def beautify_factory_name(factory):
     """Returns a nice name for the specified Gst.ElementFactory instance.
 
     Intended for removing redundant words and shorten the codec names.
+
+    Args:
+        factory (Gst.ElementFactory): The factory which needs to be displayed.
+
+    Returns:
+        str: Cleaned up name.
     """
     # Only replace lowercase versions of "format", "video", "audio"
     # otherwise they might be part of a trademark name.
@@ -257,23 +288,6 @@ def extension_for_muxer(muxer_name):
     return exts.get(muxer_name)
 
 
-def factorylist(factories):
-    """Creates a Gtk.ListStore() of sorted, beautified factory names.
-
-    Args:
-        factories (List[Gst.ElementFactory]): The factories for display.
-
-    Returns:
-        Gtk.ListStore: The model with to columns for name and factory.
-    """
-    columns = (str, object)
-    data = [(beautify_factoryname(factory), factory)
-            for factory in factories
-            if factory.get_rank() > 0]
-    data.sort(key=lambda x: x[0])
-    return model(columns, data)
-
-
 # --------------------------------- Public classes -----------------------------#
 
 class RenderingProgressDialog(GObject.Object):
@@ -377,8 +391,6 @@ class RenderDialog(Loggable):
     _factory_formats = {}
 
     def __init__(self, app, project):
-        from pitivi.preset import RenderPresetManager
-
         Loggable.__init__(self)
 
         self.app = app
@@ -401,9 +413,11 @@ class RenderDialog(Loggable):
         # {object: sigId}
         self._gstSigId = {}
 
-        self.render_presets = RenderPresetManager(self.app.system)
+        self.render_presets = RenderPresetManager(self.app.system, Encoders())
         self.render_presets.loadAll()
 
+        # Whether encoders changing are a result of changing the muxer.
+        self.muxer_combo_changing = False
         self._createUi()
 
         # Directory and Filename
@@ -420,7 +434,10 @@ class RenderDialog(Loggable):
         self.preferred_aencoder = self.project.aencoder
         self.__unproxiedClips = {}
 
-        self._initializeComboboxModels()
+        self.frame_rate_combo.set_model(frame_rates)
+        self.channels_combo.set_model(audio_channels)
+        self.sample_rate_combo.set_model(audio_rates)
+        self.__initialize_muxers_model()
         self._displaySettings()
         self._displayRenderSettings()
 
@@ -434,7 +451,7 @@ class RenderDialog(Loggable):
         self.wg.addVertex(self.frame_rate_combo, signal="changed")
         self.wg.addVertex(self.channels_combo, signal="changed")
         self.wg.addVertex(self.sample_rate_combo, signal="changed")
-        self.wg.addVertex(self.muxercombobox, signal="changed")
+        self.wg.addVertex(self.muxer_combo, signal="changed")
         self.wg.addVertex(self.audio_encoder_combo, signal="changed")
         self.wg.addVertex(self.video_encoder_combo, signal="changed")
         self.wg.addVertex(self.preset_menubutton,
@@ -443,15 +460,15 @@ class RenderDialog(Loggable):
         self.wg.addEdge(self.frame_rate_combo, self.preset_menubutton)
         self.wg.addEdge(self.audio_encoder_combo, self.preset_menubutton)
         self.wg.addEdge(self.video_encoder_combo, self.preset_menubutton)
-        self.wg.addEdge(self.muxercombobox, self.preset_menubutton)
+        self.wg.addEdge(self.muxer_combo, self.preset_menubutton)
         self.wg.addEdge(self.channels_combo, self.preset_menubutton)
         self.wg.addEdge(self.sample_rate_combo, self.preset_menubutton)
 
         # Bind widgets to RenderPresetsManager
         self.render_presets.bindWidget(
             "container",
-            lambda x: self.muxer_setter(self.muxercombobox, x),
-            lambda: get_combo_value(self.muxercombobox).get_name())
+            lambda x: self.muxer_setter(self.muxer_combo, x),
+            lambda: get_combo_value(self.muxer_combo).get_name())
         self.render_presets.bindWidget(
             "acodec",
             lambda x: self.acodec_setter(self.audio_encoder_combo, x),
@@ -484,34 +501,30 @@ class RenderDialog(Loggable):
     def _updatePresetMenuButton(self, unused_source, unused_target):
         self.render_presets.updateMenuActions()
 
-    def muxer_setter(self, widget, value):
-        set_combo_value(widget, Gst.ElementFactory.find(value))
-        self.project.setEncoders(muxer=value)
+    def muxer_setter(self, widget, muxer_name):
+        set_combo_value(widget, Encoders().factories_by_name.get(muxer_name))
+        self.project.setEncoders(muxer=muxer_name)
 
         # Update the extension of the filename.
         basename = os.path.splitext(self.fileentry.get_text())[0]
         self.updateFilename(basename)
 
         # Update muxer-dependent widgets.
-        self.muxer_combo_changing = True
-        try:
-            self.updateAvailableEncoders()
-        finally:
-            self.muxer_combo_changing = False
+        self.updateAvailableEncoders()
 
-    def acodec_setter(self, widget, value):
-        set_combo_value(widget, Gst.ElementFactory.find(value))
-        self.project.aencoder = value
+    def acodec_setter(self, widget, aencoder_name):
+        set_combo_value(widget, Encoders().factories_by_name.get(aencoder_name))
+        self.project.aencoder = aencoder_name
         if not self.muxer_combo_changing:
             # The user directly changed the audio encoder combo.
-            self.preferred_aencoder = value
+            self.preferred_aencoder = aencoder_name
 
-    def vcodec_setter(self, widget, value):
-        set_combo_value(widget, Gst.ElementFactory.find(value))
-        self.project.setEncoders(vencoder=value)
+    def vcodec_setter(self, widget, vencoder_name):
+        set_combo_value(widget, Encoders().factories_by_name.get(vencoder_name))
+        self.project.setEncoders(vencoder=vencoder_name)
         if not self.muxer_combo_changing:
             # The user directly changed the video encoder combo.
-            self.preferred_vencoder = value
+            self.preferred_vencoder = vencoder_name
 
     def sample_rate_setter(self, widget, value):
         set_combo_value(widget, value)
@@ -542,10 +555,12 @@ class RenderDialog(Loggable):
         self.audio_settings_button = builder.get_object(
             "audio_settings_button")
         self.frame_rate_combo = builder.get_object("frame_rate_combo")
+        self.frame_rate_combo.set_model(frame_rates)
         self.scale_spinbutton = builder.get_object("scale_spinbutton")
         self.channels_combo = builder.get_object("channels_combo")
+        self.channels_combo.set_model(audio_channels)
         self.sample_rate_combo = builder.get_object("sample_rate_combo")
-        self.muxercombobox = builder.get_object("muxercombobox")
+        self.muxer_combo = builder.get_object("muxercombobox")
         self.audio_encoder_combo = builder.get_object("audio_encoder_combo")
         self.video_encoder_combo = builder.get_object("video_encoder_combo")
         self.filebutton = builder.get_object("filebutton")
@@ -575,12 +590,45 @@ class RenderDialog(Loggable):
     def _settingsChanged(self, unused_project, unused_key, unused_value):
         self.updateResolution()
 
-    def _initializeComboboxModels(self):
-        # Avoid loop import
-        self.frame_rate_combo.set_model(frame_rates)
-        self.channels_combo.set_model(audio_channels)
-        self.sample_rate_combo.set_model(audio_rates)
-        self.muxercombobox.set_model(factorylist(Encoders().muxers))
+    def __initialize_muxers_model(self):
+        # By default show only supported muxers and encoders.
+        model = self.create_combobox_model(Encoders().muxers)
+        self.muxer_combo.set_model(model)
+
+    def create_combobox_model(self, factories):
+        """Creates a model for a combobox showing factories.
+
+        Args:
+            combobox (Gtk.ComboBox): The combobox to setup.
+            factories (List[Gst.ElementFactory]): The factories to display.
+
+        Returns:
+            Gtk.ListStore: The model with (display name, factory, unsupported).
+        """
+        model = Gtk.TreeStore(str, object)
+        data_supported = []
+        data_unsupported = []
+        for factory in factories:
+            supported = Encoders().is_supported(factory)
+            row = (beautify_factory_name(factory), factory)
+            if supported:
+                data_supported.append(row)
+            else:
+                data_unsupported.append(row)
+
+        data_supported.sort()
+        for row in data_supported:
+            model.append(None, row)
+
+        # Translators: This item appears in a combobox's popup and
+        # contains as children the unsupported (but still available)
+        # muxers and encoders.
+        unsupported_iter = model.append(None, (_("Unsupported"), None))
+        data_unsupported.sort()
+        for row in data_unsupported:
+            model.append(unsupported_iter, row)
+
+        return model
 
     def _displaySettings(self):
         """Displays the settings also in the ProjectSettingsDialog."""
@@ -597,8 +645,8 @@ class RenderDialog(Loggable):
         self.scale_spinbutton.set_value(self.project.render_scale)
         # Muxer settings
         # This will trigger an update of the codec comboboxes.
-        set_combo_value(self.muxercombobox,
-                        Gst.ElementFactory.find(self.project.muxer))
+        set_combo_value(self.muxer_combo,
+                        Encoders().factories_by_name.get(self.project.muxer))
 
     def _checkForExistingFile(self, *unused_args):
         """Displays a warning if the file path already exists."""
@@ -659,30 +707,44 @@ class RenderDialog(Loggable):
 
     def updateAvailableEncoders(self):
         """Updates the encoder comboboxes to show the available encoders."""
-        vencoder_model = factorylist(
-            Encoders().compatible_video_encoders[self.project.muxer])
-        self.video_encoder_combo.set_model(vencoder_model)
-
-        aencoder_model = factorylist(
-            Encoders().compatible_audio_encoders[self.project.muxer])
-        self.audio_encoder_combo.set_model(aencoder_model)
-
-        self._updateEncoderCombo(
-            self.video_encoder_combo, self.preferred_vencoder)
-        self._updateEncoderCombo(
-            self.audio_encoder_combo, self.preferred_aencoder)
+        self.muxer_combo_changing = True
+        try:
+            model = self.create_combobox_model(
+                Encoders().compatible_video_encoders[self.project.muxer])
+            self.video_encoder_combo.set_model(model)
+            self._update_encoder_combo(self.video_encoder_combo,
+                                       self.preferred_vencoder)
+
+            model = self.create_combobox_model(
+                Encoders().compatible_audio_encoders[self.project.muxer])
+            self.audio_encoder_combo.set_model(model)
+            self._update_encoder_combo(self.audio_encoder_combo,
+                                       self.preferred_aencoder)
+        finally:
+            self.muxer_combo_changing = False
 
-    def _updateEncoderCombo(self, encoder_combo, preferred_encoder):
+    def _update_encoder_combo(self, encoder_combo, preferred_encoder):
         """Selects the specified encoder for the specified encoder combo."""
         if preferred_encoder:
             # A preference exists, pick it if it can be found in
             # the current model of the combobox.
-            vencoder = Gst.ElementFactory.find(preferred_encoder)
-            set_combo_value(encoder_combo, vencoder, default_index=0)
-        else:
-            # No preference exists, pick the first encoder from
-            # the current model of the combobox.
-            encoder_combo.set_active(0)
+            encoder = Encoders().factories_by_name.get(preferred_encoder)
+            set_combo_value(encoder_combo, encoder)
+        if not preferred_encoder or not get_combo_value(encoder_combo):
+            # No preference exists or it is not available,
+            # pick the first encoder from the combobox's model.
+            first = encoder_combo.props.model.get_iter_first()
+            if not first:
+                # Model is empty. Should not happen.
+                self.warning("Model is empty")
+                return
+            if not encoder_combo.props.model.iter_has_child(first):
+                # The first item is a supported factory.
+                encoder_combo.set_active_iter(first)
+            else:
+                # The first element is the Unsupported group.
+                second = encoder_combo.props.model.iter_nth_child(first, 0)
+                encoder_combo.set_active_iter(second)
 
     def _elementSettingsDialog(self, factory, settings_attr):
         """Opens a dialog to edit the properties for the specified factory.
@@ -846,7 +908,7 @@ class RenderDialog(Loggable):
         except KeyError:
             # Now find a format to set on the restriction caps.
             # The reason is we can't send different formats on the encoders.
-            factory = Gst.ElementFactory.find(self.project.vencoder)
+            factory = Encoders().factories_by_name.get(self.project.vencoder)
             for struct in factory.get_static_pad_templates():
                 if struct.direction == Gst.PadDirection.SINK:
                     caps = Gst.Caps.from_string(struct.get_caps().to_string())
@@ -1040,12 +1102,13 @@ class RenderDialog(Loggable):
         self.project.framerate = framerate
 
     def _videoEncoderComboChangedCb(self, combo):
-        vencoder = get_combo_value(combo).get_name()
-        self.project.vencoder = vencoder
-
+        factory = get_combo_value(combo)
+        name = factory.get_name()
+        self.project.vencoder = name
         if not self.muxer_combo_changing:
             # The user directly changed the video encoder combo.
-            self.preferred_vencoder = vencoder
+            self.debug("User chose a video encoder: %s", name)
+            self.preferred_vencoder = name
 
     def _videoSettingsButtonClickedCb(self, unused_button):
         factory = get_combo_value(self.video_encoder_combo)
@@ -1058,27 +1121,26 @@ class RenderDialog(Loggable):
         self.project.audiorate = get_combo_value(combo)
 
     def _audioEncoderChangedComboCb(self, combo):
-        aencoder = get_combo_value(combo).get_name()
-        self.project.aencoder = aencoder
+        factory = get_combo_value(combo)
+        name = factory.get_name()
+        self.project.aencoder = name
         if not self.muxer_combo_changing:
             # The user directly changed the audio encoder combo.
-            self.preferred_aencoder = aencoder
+            self.debug("User chose an audio encoder: %s", name)
+            self.preferred_aencoder = name
 
     def _audioSettingsButtonClickedCb(self, unused_button):
         factory = get_combo_value(self.audio_encoder_combo)
         self._elementSettingsDialog(factory, 'acodecsettings')
 
-    def _muxerComboChangedCb(self, muxer_combo):
+    def _muxerComboChangedCb(self, combo):
         """Handles the changing of the container format combobox."""
-        self.project.muxer = get_combo_value(muxer_combo).get_name()
+        factory = get_combo_value(combo)
+        self.project.muxer = factory.get_name()
 
         # Update the extension of the filename.
         basename = os.path.splitext(self.fileentry.get_text())[0]
         self.updateFilename(basename)
 
         # Update muxer-dependent widgets.
-        self.muxer_combo_changing = True
-        try:
-            self.updateAvailableEncoders()
-        finally:
-            self.muxer_combo_changing = False
+        self.updateAvailableEncoders()
diff --git a/pitivi/utils/ui.py b/pitivi/utils/ui.py
index 3cf94ac..ebad6f6 100644
--- a/pitivi/utils/ui.py
+++ b/pitivi/utils/ui.py
@@ -459,18 +459,21 @@ def model(columns, data):
     return ret
 
 
-def set_combo_value(combo, value, default_index=-1):
-    model = combo.props.model
-    for i, row in enumerate(model):
-        if row[1] == value:
-            combo.set_active(i)
-            return
-    combo.set_active(default_index)
+def set_combo_value(combo, value):
+    def select_specific_row(model, unused_path, iter_, unused_data):
+        if value == model.get_value(iter_, 1):
+            combo.set_active_iter(iter_)
+            return True
+        return False
+
+    combo.props.model.foreach(select_specific_row, None)
 
 
 def get_combo_value(combo):
-    active = combo.get_active()
-    return combo.props.model[active][1]
+    active_iter = combo.get_active_iter()
+    if not active_iter:
+        return None
+    return combo.props.model.get_value(active_iter, 1)
 
 
 def get_value_from_model(model, key):
diff --git a/tests/test_render.py b/tests/test_render.py
index da3d789..ca8ec3b 100644
--- a/tests/test_render.py
+++ b/tests/test_render.py
@@ -37,7 +37,7 @@ class TestRender(TestCase):
         """Checks we associate file extensions to the muxers of the presets."""
         with mock.patch("pitivi.preset.xdg_data_home") as xdg_data_home:
             xdg_data_home.return_value = "/pitivi-dir-which-does-not-exist"
-            preset_manager = RenderPresetManager(system=None)
+            preset_manager = RenderPresetManager(system=None, encoders=Encoders())
             preset_manager.loadAll()
             self.assertTrue(preset_manager.presets)
             for unused_name, preset in preset_manager.presets.items():


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