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:
parent
351e50eddf
commit
0a3caebf12
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue