Dependency Manager for optional components

Add a Dependency Manager window and class to handle optional
dependencies and make it possible to register special features
with their description and optional dependencies.
This commit is contained in:
Thomas Perl 2008-10-14 19:27:10 +02:00
parent 351e50eddf
commit 0a3caebf12
4 changed files with 335 additions and 0 deletions

View File

@ -135,6 +135,15 @@
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="itemDependencies">
<property name="visible">True</property>
<property name="label" translatable="yes">Additional components</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_itemDependencies_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="trennlinie3">
<property name="visible">True</property>
@ -7276,4 +7285,208 @@ MTP-based player</property>
</child>
</widget>
<widget class="GtkDialog" id="gPodderDependencyManager">
<property name="visible">True</property>
<property name="title" translatable="yes">Additional components</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="modal">True</property>
<property name="default_width">500</property>
<property name="default_height">300</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<property name="has_separator">True</property>
<signal name="response" handler="on_gPodderDependencyManager_response"/>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="closebutton1">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-7</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkScrolledWindow" id="sw_components">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="treeview_components">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal name="cursor_changed" handler="on_treeview_components_cursor_changed"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="spacing">6</property>
<child>
<widget class="GtkButton" id="btn_install">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_btn_install_clicked" last_modification_time="Tue, 09 Sep 2008 14:13:06 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-save</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">Install package</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="btn_about">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-about</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_btn_about_clicked" last_modification_time="Tue, 09 Sep 2008 14:14:04 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -1924,6 +1924,9 @@ class gPodder(GladeWidget):
else:
gPodderMaemoPreferences()
def on_itemDependencies_activate(self, widget):
gPodderDependencyManager()
def on_add_new_google_search(self, widget, *args):
def add_google_video_search(query):
self.add_new_channel('http://video.google.com/videofeed?type=search&q='+urllib.quote(query)+'&so=1&num=250&output=rss')
@ -3589,6 +3592,38 @@ class gPodderConfigEditor(GladeWidget):
def on_btnClose_clicked(self, widget):
self.gPodderConfigEditor.destroy()
class gPodderDependencyManager(GladeWidget):
def new(self):
col_name = gtk.TreeViewColumn(_('Feature'), gtk.CellRendererText(), text=0)
self.treeview_components.append_column(col_name)
col_installed = gtk.TreeViewColumn(_('Status'), gtk.CellRendererText(), text=2)
self.treeview_components.append_column(col_installed)
self.treeview_components.set_model(services.dependency_manager.get_model())
def on_btn_about_clicked(self, widget):
selection = self.treeview_components.get_selection()
model, iter = selection.get_selected()
if iter is not None:
title = model.get_value(iter, 0)
description = model.get_value(iter, 1)
available = model.get_value(iter, 3)
missing = model.get_value(iter, 4)
if not available:
description += '\n\n'+_('Missing components:')+'\n\n'+missing
self.show_message(description, title)
def on_btn_install_clicked(self, widget):
# TODO: Implement package manager integration
pass
def on_treeview_components_cursor_changed(self, treeview):
# TODO: If installing is possible, enable btn_install
pass
def on_gPodderDependencyManager_response(self, dialog, response_id):
self.gPodderDependencyManager.destroy()
def main():
gobject.threads_init()

View File

@ -72,6 +72,86 @@ class ObservableService(object):
log('Signal "%s" is not available for notification.', signal_name, sender=self)
class DependencyManager(object):
def __init__(self):
self.dependencies = []
def depend_on(self, feature_name, description, modules, tools):
self.dependencies.append([feature_name, description, modules, tools])
def modules_available(self, modules):
"""
Receives a list of modules and checks if each
of them is available. Returns a tuple with the
first item being a boolean variable that is True
when all required modules are available and False
otherwise. The second item is a dictionary that
lists every module as key with the available as
boolean value.
"""
result = {}
all_available = True
for module in modules:
try:
__import__(module)
result[module] = True
except:
result[module] = False
all_available = False
return (all_available, result)
def tools_available(self, tools):
"""
See modules_available.
"""
result = {}
all_available = True
for tool in tools:
if util.find_command(tool):
result[tool] = True
else:
result[tool] = False
all_available = False
return (all_available, result)
def get_model(self):
# Name, Description, Available (str), Available (bool), Missing (str)
model = gtk.ListStore(str, str, str, bool, str)
for feature_name, description, modules, tools in self.dependencies:
modules_available, module_info = self.modules_available(modules)
tools_available, tool_info = self.tools_available(tools)
available = modules_available and tools_available
if available:
available_str = _('Available')
else:
available_str = _('Missing dependencies')
missing_str = []
for module in modules:
if not module_info[module]:
missing_str.append(_('Python module "%s" not installed') % module)
for tool in tools:
if not tool_info[tool]:
missing_str.append(_('Command "%s" not installed') % tool)
missing_str = '\n'.join(missing_str)
model.append([feature_name, description, available_str, available, missing_str])
return model
dependency_manager = DependencyManager()
# Register non-module-specific dependencies here
dependency_manager.depend_on(_('Bluetooth file transfer'), _('Send podcast episodes to Bluetooth devices. Needs Python Bluez bindings.'), ['bluetooth'], ['bluetooth-sendto'])
dependency_manager.depend_on(_('Update tags on MP3 files'), _('Support the "Update tags after download" option for MP3 files.'), ['eyeD3'], [])
dependency_manager.depend_on(_('Update tags on OGG files'), _('Support the "Update tags after download" option for OGG files.'), [], ['vorbiscomment'])
dependency_manager.depend_on(_('Gnome BitTorrent integration'), _('Download .torrent files using Gnome BitTorrent.'), [], ['gnome-btdownload'])
class CoverDownloader(ObservableService):
"""
This class manages downloading cover art and notification

View File

@ -64,6 +64,13 @@ try:
except:
log('(gpodder.sync) Could not find Python Imaging Library (PIL)')
# Register our dependencies for the synchronization module
services.dependency_manager.depend_on(_('iPod synchronization'), _('Support synchronization of podcasts to Apple iPod devices via libgpod.'), ['gpod', 'mad', 'eyeD3'], [])
services.dependency_manager.depend_on(_('MTP device synchronization'), _('Support synchronization of podcasts to devices using the Media Transfer Protocol via pymtp.'), ['pymtp'], [])
services.dependency_manager.depend_on(_('iPod OGG converter'), _('Convert OGG podcasts to MP3 files on synchronization to iPods using oggdec and LAME.'), [], ['oggdec', 'lame'])
services.dependency_manager.depend_on(_('iPod video podcasts'), _('Detect video lengths via MPlayer, to synchronize video podcasts to iPods.'), [], ['mplayer'])
services.dependency_manager.depend_on(_('Rockbox cover art support'), _('Copy podcast cover art to filesystem-based MP3 players running Rockbox.org firmware. Needs Python Imaging.'), ['Image'], [])
import os
import os.path
import glob