Updates to the QueuedTorrents dialog stuff.

Updates to StatusBar.
This commit is contained in:
Andrew Resch 2007-11-17 11:36:29 +00:00
parent 3c78e41fc2
commit 35fe99eee3
7 changed files with 260 additions and 138 deletions

6
TODO
View File

@ -22,7 +22,5 @@
* Add a health indication to the statusbar * Add a health indication to the statusbar
* Add sidebar for labels and other things.. Plugins should be able to add their * Add sidebar for labels and other things.. Plugins should be able to add their
own section to this. own section to this.
* Have the dbus interface queue up torrents if we're not connected to a host. * Finish queuedtorrents dialog
Once connected it should prompt the user if they would like to add the * Update statusbar to use new statusbaritems
queued torrents. Maybe add an indicator to the status bar that their are
queued torrents.

View File

@ -341,7 +341,8 @@ class TorrentManager:
"""Force a tracker reannounce""" """Force a tracker reannounce"""
try: try:
self.torrents[torrent_id].handle.force_reannounce() self.torrents[torrent_id].handle.force_reannounce()
except: except Exception, e:
log.debug("Unable to force reannounce: %s", e)
return False return False
return True return True

View File

@ -52,9 +52,7 @@ from deluge.log import LOG as log
class DbusInterface(dbus.service.Object, component.Component): class DbusInterface(dbus.service.Object, component.Component):
def __init__(self, args, path="/org/deluge_torrent/Deluge"): def __init__(self, args, path="/org/deluge_torrent/Deluge"):
component.Component.__init__(self, "DbusInterface", ["StatusBar"]) component.Component.__init__(self, "DbusInterface")
self.queue = []
self.widgets = None
# Check to see if the daemon is already running and if not, start it # Check to see if the daemon is already running and if not, start it
bus = dbus.SessionBus() bus = dbus.SessionBus()
obj = bus.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus") obj = bus.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus")
@ -89,58 +87,6 @@ class DbusInterface(dbus.service.Object, component.Component):
bus=dbus.SessionBus()) bus=dbus.SessionBus())
dbus.service.Object.__init__(self, bus_name, path) dbus.service.Object.__init__(self, bus_name, path)
def start(self):
"""Called when we connect to a host"""
log.debug("DbusInterface start..")
if len(self.queue) == 0:
return
# Make sure status bar info is showing
self.widgets = None
self.update_status_bar()
def add_to_queue(self, torrents):
"""Adds the list of torrents to the queue"""
# Add to the queue while removing duplicates
self.queue = list(set(self.queue + torrents))
# Update the status bar
self.update_status_bar()
def update_status_bar(self):
"""Attempts to update status bar"""
# If there are no queued torrents.. remove statusbar widgets and return
if len(self.queue) == 0:
if self.widgets != None:
for widget in self.widgets:
component.get("StatusBar").remove(widget)
return False
try:
statusbar = component.get("StatusBar")
except Exception, e:
# The statusbar hasn't been loaded yet, so we'll add a timer to
# update it later.
gobject.timeout_add(100, self.update_status_bar)
return False
# Set the label text for statusbar
if len(self.queue) > 1:
label = str(len(self.queue)) + _(" Torrents Queued")
else:
label = str(len(self.queue)) + _(" Torrent Queued")
# Add the statusbar items if needed, or just modify the label if they
# have already been added.
if self.widgets == None:
self.widgets = component.get("StatusBar").add_item(
stock=gtk.STOCK_SORT_DESCENDING,
text=label)
else:
self.widgets[1].set_text(label)
# We return False so the timer stops
return False
@dbus.service.method("org.deluge_torrent.Deluge", in_signature="as") @dbus.service.method("org.deluge_torrent.Deluge", in_signature="as")
def process_args(self, args): def process_args(self, args):
"""Process arguments sent to already running Deluge""" """Process arguments sent to already running Deluge"""
@ -153,7 +99,7 @@ class DbusInterface(dbus.service.Object, component.Component):
if not client.connected(): if not client.connected():
# We're not connected so add these to the queue # We're not connected so add these to the queue
log.debug("Not connected to host.. Adding to queue.") log.debug("Not connected to host.. Adding to queue.")
self.add_to_queue(args) component.get("QueuedTorrents").add_to_queue(args)
return return
for arg in args: for arg in args:

View File

@ -1,14 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.0 on Wed Nov 14 03:28:00 2007 --> <!--Generated with glade3 3.4.0 on Sat Nov 17 02:37:49 2007 -->
<glade-interface> <glade-interface>
<widget class="GtkDialog" id="queued_torrents_dialog"> <widget class="GtkDialog" id="queued_torrents_dialog">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property> <property name="border_width">5</property>
<property name="title" translatable="yes">Queued Torrents</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="default_width">450</property>
<property name="default_height">300</property>
<property name="destroy_with_parent">True</property> <property name="destroy_with_parent">True</property>
<property name="icon">.</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="has_separator">False</property>
<child internal-child="vbox"> <child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1"> <widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property> <property name="visible">True</property>
@ -23,7 +26,7 @@
<widget class="GtkHBox" id="hbox1"> <widget class="GtkHBox" id="hbox1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">5</property> <property name="spacing">10</property>
<child> <child>
<widget class="GtkImage" id="image1"> <widget class="GtkImage" id="image1">
<property name="visible">True</property> <property name="visible">True</property>
@ -39,6 +42,8 @@
<widget class="GtkLabel" id="label1"> <widget class="GtkLabel" id="label1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0.05000000074505806</property>
<property name="ypad">10</property>
<property name="label" translatable="yes">&lt;big&gt;&lt;b&gt;Add Queued Torrents&lt;/b&gt;&lt;/big&gt;</property> <property name="label" translatable="yes">&lt;big&gt;&lt;b&gt;Add Queued Torrents&lt;/b&gt;&lt;/big&gt;</property>
<property name="use_markup">True</property> <property name="use_markup">True</property>
</widget> </widget>
@ -63,11 +68,11 @@
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property> <property name="shadow_type">GTK_SHADOW_IN</property>
<child> <child>
<widget class="GtkTreeView" id="treeview1"> <widget class="GtkTreeView" id="treeview">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="headers_clickable">True</property> <property name="headers_visible">False</property>
</widget> </widget>
</child> </child>
</widget> </widget>
@ -115,49 +120,17 @@
</packing> </packing>
</child> </child>
<child> <child>
<widget class="GtkExpander" id="expander1"> <widget class="GtkCheckButton" id="chk_autoadd">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child> <property name="label" translatable="yes">Automatically add torrents on connect</property>
<widget class="GtkAlignment" id="alignment1"> <property name="response_id">0</property>
<property name="visible">True</property> <property name="draw_indicator">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="left_padding">11</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkCheckButton" id="chk_autoadd">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Automatically add torrents on connect</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Options</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget> </widget>
<packing> <packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
@ -172,21 +145,26 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property> <property name="layout_style">GTK_BUTTONBOX_END</property>
<child> <child>
<widget class="GtkButton" id="button_cancel"> <widget class="GtkButton" id="button_close">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-cancel</property> <property name="label" translatable="yes">gtk-close</property>
<property name="use_stock">True</property> <property name="use_stock">True</property>
<property name="response_id">0</property> <property name="response_id">0</property>
<signal name="clicked" handler="on_button_cancel_clicked"/> <signal name="clicked" handler="on_button_close_clicked"/>
</widget> </widget>
</child> </child>
<child> <child>
<widget class="GtkButton" id="button_add"> <widget class="GtkButton" id="button_add">
<property name="visible">True</property> <property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="is_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-add</property> <property name="label" translatable="yes">gtk-add</property>

View File

@ -52,6 +52,7 @@ from connectionmanager import ConnectionManager
from signals import Signals from signals import Signals
from pluginmanager import PluginManager from pluginmanager import PluginManager
from dbusinterface import DbusInterface from dbusinterface import DbusInterface
from queuedtorrents import QueuedTorrents
from deluge.configmanager import ConfigManager from deluge.configmanager import ConfigManager
from deluge.log import LOG as log from deluge.log import LOG as log
import deluge.configmanager import deluge.configmanager
@ -87,10 +88,6 @@ DEFAULT_PREFS = {
class GtkUI: class GtkUI:
def __init__(self, args): def __init__(self, args):
# Start the Dbus Interface before anything else.. Just in case we are
# already running.
self.dbusinterface = DbusInterface(args)
# Initialize gettext # Initialize gettext
locale.setlocale(locale.LC_MESSAGES, '') locale.setlocale(locale.LC_MESSAGES, '')
locale.bindtextdomain("deluge", locale.bindtextdomain("deluge",
@ -107,7 +104,12 @@ class GtkUI:
# Make sure gtkui.conf has at least the defaults set # Make sure gtkui.conf has at least the defaults set
config = ConfigManager("gtkui.conf", DEFAULT_PREFS) config = ConfigManager("gtkui.conf", DEFAULT_PREFS)
# Start the Dbus Interface before anything else.. Just in case we are
# already running.
self.queuedtorrents = QueuedTorrents()
self.dbusinterface = DbusInterface(args)
# We make sure that the UI components start once we get a core URI # We make sure that the UI components start once we get a core URI
client.connect_on_new_core(component.start) client.connect_on_new_core(component.start)
client.connect_on_no_core(component.stop) client.connect_on_no_core(component.stop)

View File

@ -0,0 +1,166 @@
#
# queuedtorrents.py
#
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
#
# Deluge is free software.
#
# You may redistribute it and/or modify it under the terms of the
# GNU General Public License, as published by the Free Software
# Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# deluge is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with deluge. If not, write to:
# The Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the OpenSSL
# library.
# You must obey the GNU General Public License in all respects for all of
# the code used other than OpenSSL. If you modify file(s) with this
# exception, you may extend this exception to your version of the file(s),
# but you are not obligated to do so. If you do not wish to do so, delete
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.
import os.path
import gtk, gtk.glade
import pkg_resources
import deluge.ui.component as component
import deluge.ui.client as client
import deluge.common
from deluge.log import LOG as log
class QueuedTorrents(component.Component):
def __init__(self):
component.Component.__init__(self, "QueuedTorrents", ["StatusBar"])
self.queue = []
self.status_item = None
self.glade = gtk.glade.XML(
pkg_resources.resource_filename("deluge.ui.gtkui",
"glade/queuedtorrents.glade"))
self.dialog = self.glade.get_widget("queued_torrents_dialog")
self.dialog.set_icon(deluge.common.get_logo(32))
self.glade.signal_autoconnect({
"on_button_remove_clicked": self.on_button_remove_clicked,
"on_button_clear_clicked": self.on_button_clear_clicked,
"on_button_close_clicked": self.on_button_close_clicked,
"on_button_add_clicked": self.on_button_add_clicked
})
self.treeview = self.glade.get_widget("treeview")
self.treeview.append_column(
gtk.TreeViewColumn(_("Torrent"), gtk.CellRendererText(), text=0))
self.liststore = gtk.ListStore(str, str)
self.treeview.set_model(self.liststore)
def run(self):
self.dialog.set_transient_for(component.get("MainWindow").window)
self.dialog.show()
def start(self):
if len(self.queue) == 0:
return
# Make sure status bar info is showing
self.status_item = None
self.update_status_bar()
# We only want the add button sensitive if we're connected to a host
self.glade.get_widget("button_add").set_sensitive(True)
self.run()
def stop(self):
# We only want the add button sensitive if we're connected to a host
self.glade.get_widget("button_add").set_sensitive(False)
def add_to_queue(self, torrents):
"""Adds the list of torrents to the queue"""
# Add to the queue while removing duplicates
self.queue = list(set(self.queue + torrents))
# Update the liststore
self.liststore.clear()
for torrent in self.queue:
self.liststore.append([os.path.split(torrent)[1], torrent])
# Update the status bar
self.update_status_bar()
def update_status_bar(self):
"""Attempts to update status bar"""
# If there are no queued torrents.. remove statusbar widgets and return
if len(self.queue) == 0:
if self.status_item != None:
component.get("StatusBar").remove_item(self.status_item)
self.status_item = None
return False
try:
statusbar = component.get("StatusBar")
except Exception, e:
# The statusbar hasn't been loaded yet, so we'll add a timer to
# update it later.
gobject.timeout_add(100, self.update_status_bar)
return False
# Set the label text for statusbar
if len(self.queue) > 1:
label = str(len(self.queue)) + _(" Torrents Queued")
else:
label = str(len(self.queue)) + _(" Torrent Queued")
# Add the statusbar items if needed, or just modify the label if they
# have already been added.
if self.status_item == None:
self.status_item = component.get("StatusBar").add_item(
stock=gtk.STOCK_SORT_DESCENDING,
text=label,
callback=self.on_statusbar_click)
else:
self.status_item.set_text(label)
# We return False so the timer stops
return False
def on_statusbar_click(self, widget, event):
log.debug("on_statusbar_click")
self.run()
def on_button_remove_clicked(self, widget):
selected = self.treeview.get_selection().get_selected()[1]
if selected != None:
path = self.liststore.get_value(selected, 1)
self.liststore.remove(selected)
self.queue.remove(path)
self.update_status_bar()
def on_button_clear_clicked(self, widget):
self.liststore.clear()
self.update_status_bar()
def on_button_close_clicked(self, widget):
self.dialog.hide()
def on_button_add_clicked(self, widget):
# Add all the torrents in the liststore
def add_torrent(model, path, iter, data):
torrent_path = model.get_value(iter, 1)
client.add_torrent_file([torrent_path])
self.liststore.foreach(add_torrent, None)
del self.queue[:]
self.dialog.hide()
self.update_status_bar()

View File

@ -38,6 +38,57 @@ import deluge.common
import deluge.ui.client as client import deluge.ui.client as client
from deluge.log import LOG as log from deluge.log import LOG as log
class StatusBarItem:
def __init__(self, image=None, stock=None, text=None, callback=None):
self._widgets = []
self._ebox = gtk.EventBox()
self._hbox = gtk.HBox()
self._image = gtk.Image()
self._label = gtk.Label()
self._hbox.add(self._image)
self._hbox.add(self._label)
self._ebox.add(self._hbox)
# Add image from file or stock
if image != None or stock != None:
if image != None:
self.set_image_from_file(image)
if stock != None:
self.set_image_from_stock(stock)
# Add text
if text != None:
self.set_text(text)
if callback != None:
self.set_callback(callback)
self.show_all()
def set_callback(self, callback):
self._ebox.connect("button-press-event", callback)
def show_all(self):
self._ebox.show()
self._hbox.show()
self._image.show()
self._label.show()
def set_image_from_file(self, image):
self._image.set_from_file(image)
def set_image_from_stock(self, stock):
self._image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
def set_text(self, text):
self._label.set_text(text)
def get_widgets(self):
return self._widgets()
def get_eventbox(self):
return self._ebox
class StatusBar(component.Component): class StatusBar(component.Component):
def __init__(self): def __init__(self):
component.Component.__init__(self, "StatusBar") component.Component.__init__(self, "StatusBar")
@ -90,37 +141,17 @@ class StatusBar(component.Component):
self.hbox.pack_start(label, expand=False, fill=False) self.hbox.pack_start(label, expand=False, fill=False)
self.statusbar.show_all() self.statusbar.show_all()
def add_item(self, image=None, stock=None, text=None): def add_item(self, image=None, stock=None, text=None, callback=None):
"""Adds an item to the status bar""" """Adds an item to the status bar"""
# The return tuple.. we return whatever widgets we add # The return tuple.. we return whatever widgets we add
ret = [] item = StatusBarItem(image, stock, text, callback)
# Add image from file or stock self.hbox.pack_start(item.get_eventbox(), expand=False, fill=False)
if image != None or stock != None: return item
_image = gtk.Image()
if image != None:
_image.set_from_file(image)
if stock != None:
_image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
self.hbox.pack_start(_image, expand=False, fill=False)
ret.append(_image)
# Add text
if text != None:
label = gtk.Label(text)
self.hbox.pack_start(label, expand=False, fill=False)
ret.append(label)
# Show the widgets
for widget in ret:
widget.show()
# Return the widgets
return tuple(ret)
def remove_item(self, widget): def remove_item(self, item):
"""Removes an item from the statusbar""" """Removes an item from the statusbar"""
try: try:
self.hbox.remove(widget) self.hbox.remove(item.get_eventbox())
except Exception, e: except Exception, e:
log.debug("Unable to remove widget: %s", e) log.debug("Unable to remove widget: %s", e)