summaryrefslogtreecommitdiffstats
path: root/deluge/ui/gtk3/statusbar.py
diff options
context:
space:
mode:
Diffstat (limited to 'deluge/ui/gtk3/statusbar.py')
-rw-r--r--deluge/ui/gtk3/statusbar.py578
1 files changed, 578 insertions, 0 deletions
diff --git a/deluge/ui/gtk3/statusbar.py b/deluge/ui/gtk3/statusbar.py
new file mode 100644
index 0000000..0a2e800
--- /dev/null
+++ b/deluge/ui/gtk3/statusbar.py
@@ -0,0 +1,578 @@
+#
+# Copyright (C) 2007-2008 Andrew Resch <andrewresch@gmail.com>
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+
+import logging
+
+from gi.repository import Gtk
+from gi.repository.GLib import timeout_add
+
+import deluge.component as component
+from deluge.common import fsize, fspeed, get_pixmap
+from deluge.configmanager import ConfigManager
+from deluge.ui.client import client
+
+from .common import build_menu_radio_list
+from .dialogs import OtherDialog
+
+log = logging.getLogger(__name__)
+
+
+class StatusBarItem:
+ def __init__(
+ self,
+ image=None,
+ stock=None,
+ icon=None,
+ text=None,
+ markup=False,
+ callback=None,
+ tooltip=None,
+ ):
+ self._widgets = []
+ self._ebox = Gtk.EventBox()
+ self._hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, spacing=3)
+ self._image = Gtk.Image()
+ self._label = Gtk.Label()
+ if image or icon or stock:
+ self._hbox.add(self._image)
+ self._hbox.add(self._label)
+ self._ebox.add(self._hbox)
+
+ # Add image from file or stock
+ if image:
+ self.set_image_from_file(image)
+ if stock:
+ self.set_image_from_stock(stock)
+ if icon:
+ self.set_image_from_icon(icon)
+
+ # Add text
+ if markup:
+ self.set_markup(text)
+ else:
+ self.set_text(text)
+
+ if callback is not None:
+ self.set_callback(callback)
+
+ if tooltip:
+ self.set_tooltip(tooltip)
+
+ 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()
+
+ 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.IconSize.MENU)
+
+ def set_image_from_icon(self, icon):
+ self._image.set_from_icon_name(icon, Gtk.IconSize.MENU)
+
+ def set_text(self, text):
+ if not text:
+ self._label.hide()
+ elif self._label.get_text() != text:
+ self._label.set_text(text)
+ self._label.show()
+
+ def set_markup(self, text):
+ if not text:
+ self._label.hide()
+ elif self._label.get_text() != text:
+ self._label.set_markup(text)
+ self._label.show()
+
+ def set_tooltip(self, tip):
+ if self._ebox.get_tooltip_text() != tip:
+ self._ebox.set_tooltip_text(tip)
+
+ def get_widgets(self):
+ return self._widgets
+
+ def get_eventbox(self):
+ return self._ebox
+
+ def get_text(self):
+ return self._label.get_text()
+
+
+class StatusBar(component.Component):
+ def __init__(self):
+ component.Component.__init__(self, 'StatusBar', interval=3)
+ main_builder = component.get('MainWindow').get_builder()
+ self.statusbar = main_builder.get_object('statusbar')
+ self.config = ConfigManager('gtk3ui.conf')
+
+ # Status variables that are updated via callback
+ self.max_connections_global = -1
+ self.num_connections = 0
+ self.max_download_speed = -1.0
+ self.download_rate = ''
+ self.max_upload_speed = -1.0
+ self.upload_rate = ''
+ self.dht_nodes = 0
+ self.dht_status = False
+ self.health = False
+ self.download_protocol_rate = 0.0
+ self.upload_protocol_rate = 0.0
+
+ self.config_value_changed_dict = {
+ 'max_connections_global': self._on_max_connections_global,
+ 'max_download_speed': self._on_max_download_speed,
+ 'max_upload_speed': self._on_max_upload_speed,
+ 'dht': self._on_dht,
+ }
+ self.current_warnings = []
+ # Add hbox to the statusbar after removing the initial label widget
+ self.hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, spacing=10)
+ self.hbox.set_margin_top(2)
+ self.hbox.set_margin_bottom(3)
+ frame = self.statusbar.get_children()[0]
+ frame.remove(frame.get_children()[0])
+ frame.add(self.hbox)
+ self.statusbar.show_all()
+ # Create the not connected item
+ self.not_connected_item = StatusBarItem(
+ icon='network-offline-symbolic',
+ text=_('Not Connected'),
+ callback=self._on_notconnected_item_clicked,
+ )
+ # Show the not connected status bar
+ self.show_not_connected()
+
+ # Hide if necessary
+ self.visible(self.config['show_statusbar'])
+
+ client.register_event_handler(
+ 'ConfigValueChangedEvent', self.on_configvaluechanged_event
+ )
+
+ def start(self):
+ # Add in images and labels
+ self.remove_item(self.not_connected_item)
+
+ self.connections_item = self.add_item(
+ icon='network-transmit-receive-symbolic',
+ callback=self._on_connection_item_clicked,
+ tooltip=_('Connections (Limit)'),
+ pack_start=True,
+ )
+
+ self.download_item = self.add_item(
+ image=get_pixmap('downloading16.png'),
+ callback=self._on_download_item_clicked,
+ tooltip=_('Download Speed (Limit)'),
+ pack_start=True,
+ )
+
+ self.upload_item = self.add_item(
+ image=get_pixmap('seeding16.png'),
+ callback=self._on_upload_item_clicked,
+ tooltip=_('Upload Speed (Limit)'),
+ pack_start=True,
+ )
+
+ self.traffic_item = self.add_item(
+ image=get_pixmap('traffic16.png'),
+ callback=self._on_traffic_item_clicked,
+ tooltip=_('Protocol Traffic (Down:Up)'),
+ pack_start=True,
+ )
+
+ self.dht_item = StatusBarItem(
+ image=get_pixmap('dht16.png'), tooltip=_('DHT Nodes')
+ )
+
+ self.diskspace_item = self.add_item(
+ icon='drive-harddisk-symbolic',
+ callback=self._on_diskspace_item_clicked,
+ tooltip=_('Free Disk Space'),
+ pack_start=True,
+ )
+
+ self.external_ip_item = self.add_item(
+ tooltip=_('External IP Address'),
+ text=_('<b>IP</b> <small>%s</small>') % _('n/a'),
+ markup=True,
+ pack_start=True,
+ )
+
+ self.health_item = self.add_item(
+ icon='network-error-symbolic',
+ text=_('<b><small>Port Issue</small></b>'),
+ markup=True,
+ tooltip=_('No incoming connections, check port forwarding'),
+ callback=self._on_health_icon_clicked,
+ )
+
+ self.health = False
+
+ def update_config_values(configs):
+ self._on_max_connections_global(configs['max_connections_global'])
+ self._on_max_download_speed(configs['max_download_speed'])
+ self._on_max_upload_speed(configs['max_upload_speed'])
+ self._on_dht(configs['dht'])
+
+ # Get some config values
+ client.core.get_config_values(
+ ['max_connections_global', 'max_download_speed', 'max_upload_speed', 'dht']
+ ).addCallback(update_config_values)
+
+ def stop(self):
+ # When stopped, we just show the not connected thingy
+ try:
+ self.remove_item(self.connections_item)
+ self.remove_item(self.dht_item)
+ self.remove_item(self.download_item)
+ self.remove_item(self.upload_item)
+ self.remove_item(self.not_connected_item)
+ self.remove_item(self.health_item)
+ self.remove_item(self.traffic_item)
+ self.remove_item(self.diskspace_item)
+ self.remove_item(self.external_ip_item)
+ except Exception as ex:
+ log.debug('Unable to remove StatusBar item: %s', ex)
+ self.show_not_connected()
+
+ def visible(self, visible):
+ if visible:
+ self.statusbar.show()
+ else:
+ self.statusbar.hide()
+
+ self.config['show_statusbar'] = visible
+
+ def show_not_connected(self):
+ self.hbox.pack_start(self.not_connected_item.get_eventbox(), False, False, 0)
+
+ def add_item(
+ self,
+ image=None,
+ stock=None,
+ icon=None,
+ text=None,
+ markup=False,
+ callback=None,
+ tooltip=None,
+ pack_start=False,
+ ):
+ """Adds an item to the status bar"""
+ # The return tuple.. we return whatever widgets we add
+ item = StatusBarItem(image, stock, icon, text, markup, callback, tooltip)
+ if pack_start:
+ self.hbox.pack_start(item.get_eventbox(), False, False, 0)
+ else:
+ self.hbox.pack_end(item.get_eventbox(), False, False, 0)
+ return item
+
+ def remove_item(self, item):
+ """Removes an item from the statusbar"""
+ if item.get_eventbox() in self.hbox.get_children():
+ try:
+ self.hbox.remove(item.get_eventbox())
+ except Exception as ex:
+ log.debug('Unable to remove widget: %s', ex)
+
+ def add_timeout_item(
+ self, seconds=3, image=None, stock=None, icon=None, text=None, callback=None
+ ):
+ """Adds an item to the StatusBar for seconds"""
+ item = self.add_item(image, stock, icon, text, callback)
+ # Start a timer to remove this item in seconds
+ timeout_add(seconds * 1000, self.remove_item, item)
+
+ def display_warning(self, text, callback=None):
+ """Displays a warning to the user in the status bar"""
+ if text not in self.current_warnings:
+ item = self.add_item(
+ icon='dialog-warning-symbolic', text=text, callback=callback
+ )
+ self.current_warnings.append(text)
+ timeout_add(3000, self.remove_warning, item)
+
+ def remove_warning(self, item):
+ self.current_warnings.remove(item.get_text())
+ self.remove_item(item)
+
+ def clear_statusbar(self):
+ def remove(child):
+ self.hbox.remove(child)
+
+ self.hbox.foreach(remove)
+
+ def send_status_request(self):
+ # Sends an async request for data from the core
+ keys = [
+ 'peer.num_peers_connected',
+ 'upload_rate',
+ 'download_rate',
+ 'payload_upload_rate',
+ 'payload_download_rate',
+ 'net.sent_bytes',
+ 'net.recv_bytes',
+ 'net.sent_payload_bytes',
+ 'net.recv_payload_bytes',
+ ]
+
+ if self.dht_status:
+ keys.append('dht.dht_nodes')
+
+ if not self.health:
+ keys.append('net.has_incoming_connections')
+
+ client.core.get_session_status(keys).addCallback(self._on_get_session_status)
+ client.core.get_free_space().addCallback(self._on_get_free_space)
+ client.core.get_external_ip().addCallback(self._on_get_external_ip)
+
+ def on_configvaluechanged_event(self, key, value):
+ """
+ This is called when we receive a ConfigValueChangedEvent from
+ the core.
+ """
+ if key in self.config_value_changed_dict:
+ self.config_value_changed_dict[key](value)
+
+ def _on_max_connections_global(self, max_connections):
+ self.max_connections_global = max_connections
+ self.update_connections_label()
+
+ def _on_dht(self, value):
+ self.dht_status = value
+ if value:
+ self.hbox.pack_start(self.dht_item.get_eventbox(), False, False, 0)
+ self.send_status_request()
+ else:
+ self.remove_item(self.dht_item)
+
+ def _on_get_session_status(self, status):
+ self.download_rate = fspeed(
+ status['payload_download_rate'], precision=0, shortform=True
+ )
+ self.upload_rate = fspeed(
+ status['payload_upload_rate'], precision=0, shortform=True
+ )
+ self.download_protocol_rate = (
+ status['download_rate'] - status['payload_download_rate']
+ ) // 1024
+ self.upload_protocol_rate = (
+ status['upload_rate'] - status['payload_upload_rate']
+ ) // 1024
+ self.num_connections = status['peer.num_peers_connected']
+ self.update_download_label()
+ self.update_upload_label()
+ self.update_traffic_label()
+ self.update_connections_label()
+
+ if 'dht.dht_nodes' in status:
+ self.dht_nodes = status['dht.dht_nodes']
+ self.update_dht_label()
+
+ if 'net.has_incoming_connections' in status:
+ self.health = status['net.has_incoming_connections']
+ if self.health:
+ self.remove_item(self.health_item)
+
+ def _on_get_free_space(self, space):
+ if space >= 0:
+ self.diskspace_item.set_markup(
+ '<small>%s</small>' % fsize(space, shortform=True)
+ )
+ else:
+ self.diskspace_item.set_markup(
+ '<span foreground="red">' + _('Error') + '</span>'
+ )
+
+ def _on_max_download_speed(self, max_download_speed):
+ self.max_download_speed = max_download_speed
+ self.update_download_label()
+
+ def _on_max_upload_speed(self, max_upload_speed):
+ self.max_upload_speed = max_upload_speed
+ self.update_upload_label()
+
+ def _on_get_external_ip(self, external_ip):
+ ip = external_ip if external_ip else _('n/a')
+ self.external_ip_item.set_markup(_('<b>IP</b> <small>%s</small>') % ip)
+
+ def update_connections_label(self):
+ # Set the max connections label
+ if self.max_connections_global < 0:
+ label_string = '%s' % self.num_connections
+ else:
+ label_string = '{} <small>({})</small>'.format(
+ self.num_connections,
+ self.max_connections_global,
+ )
+
+ self.connections_item.set_markup(label_string)
+
+ if self.num_connections:
+ self.connections_item.set_image_from_icon(
+ 'network-transmit-receive-symbolic'
+ )
+ else:
+ self.connections_item.set_image_from_icon('network-idle-symbolic')
+
+ def update_dht_label(self):
+ # Set the max connections label
+ self.dht_item.set_markup('<small>%s</small>' % (self.dht_nodes))
+
+ def update_download_label(self):
+ # Set the download speed label
+ if self.max_download_speed <= 0:
+ label_string = self.download_rate
+ else:
+ label_string = '%s <small>(%i %s)</small>' % (
+ self.download_rate,
+ self.max_download_speed,
+ _('K/s'),
+ )
+
+ self.download_item.set_markup(label_string)
+
+ def update_upload_label(self):
+ # Set the upload speed label
+ if self.max_upload_speed <= 0:
+ label_string = self.upload_rate
+ else:
+ label_string = '%s <small>(%i %s)</small>' % (
+ self.upload_rate,
+ self.max_upload_speed,
+ _('K/s'),
+ )
+
+ self.upload_item.set_markup(label_string)
+
+ def update_traffic_label(self):
+ label_string = '<small>%i:%i %s</small>' % (
+ self.download_protocol_rate,
+ self.upload_protocol_rate,
+ _('K/s'),
+ )
+ self.traffic_item.set_markup(label_string)
+
+ def update(self):
+ self.send_status_request()
+
+ def set_limit_value(self, widget, core_key):
+ log.debug('_on_set_unlimit_other %s', core_key)
+ other_dialog_info = {
+ 'max_download_speed': (
+ _('Download Speed Limit'),
+ _('Set the maximum download speed'),
+ _('K/s'),
+ 'downloading.svg',
+ self.max_download_speed,
+ ),
+ 'max_upload_speed': (
+ _('Upload Speed Limit'),
+ _('Set the maximum upload speed'),
+ _('K/s'),
+ 'seeding.svg',
+ self.max_upload_speed,
+ ),
+ 'max_connections_global': (
+ _('Incoming Connections'),
+ _('Set the maximum incoming connections'),
+ '',
+ 'network-transmit-receive-symbolic',
+ self.max_connections_global,
+ ),
+ }
+
+ def set_value(value):
+ log.debug('value: %s', value)
+ if value is None:
+ return
+ elif value == 0:
+ value = -1
+ # Set the config in the core
+ if value != getattr(self, core_key):
+ client.core.set_config({core_key: value})
+
+ if widget.get_name() == 'unlimited':
+ set_value(-1)
+ elif widget.get_name() == 'other':
+
+ def dialog_finished(response_id):
+ if response_id == Gtk.ResponseType.OK:
+ set_value(dialog.get_value())
+
+ dialog = OtherDialog(*other_dialog_info[core_key])
+ dialog.run().addCallback(set_value)
+ else:
+ value = widget.get_children()[0].get_text().split(' ')[0]
+ set_value(value)
+
+ def _on_download_item_clicked(self, widget, event):
+ self.menu = build_menu_radio_list(
+ self.config['tray_download_speed_list'],
+ self._on_set_download_speed,
+ self.max_download_speed,
+ _('K/s'),
+ show_notset=True,
+ show_other=True,
+ )
+ self.menu.show_all()
+ self.menu.popup(None, None, None, None, event.button, event.time)
+
+ def _on_set_download_speed(self, widget):
+ log.debug('_on_set_download_speed')
+ self.set_limit_value(widget, 'max_download_speed')
+
+ def _on_upload_item_clicked(self, widget, event):
+ self.menu = build_menu_radio_list(
+ self.config['tray_upload_speed_list'],
+ self._on_set_upload_speed,
+ self.max_upload_speed,
+ _('K/s'),
+ show_notset=True,
+ show_other=True,
+ )
+ self.menu.show_all()
+ self.menu.popup(None, None, None, None, event.button, event.time)
+
+ def _on_set_upload_speed(self, widget):
+ log.debug('_on_set_upload_speed')
+ self.set_limit_value(widget, 'max_upload_speed')
+
+ def _on_connection_item_clicked(self, widget, event):
+ self.menu = build_menu_radio_list(
+ self.config['connection_limit_list'],
+ self._on_set_connection_limit,
+ self.max_connections_global,
+ show_notset=True,
+ show_other=True,
+ )
+ self.menu.show_all()
+ self.menu.popup(None, None, None, None, event.button, event.time)
+
+ def _on_set_connection_limit(self, widget):
+ log.debug('_on_set_connection_limit')
+ self.set_limit_value(widget, 'max_connections_global')
+
+ def _on_health_icon_clicked(self, widget, event):
+ component.get('Preferences').show('network')
+
+ def _on_notconnected_item_clicked(self, widget, event):
+ component.get('ConnectionManager').show()
+
+ def _on_traffic_item_clicked(self, widget, event):
+ component.get('Preferences').show('network')
+
+ def _on_diskspace_item_clicked(self, widget, event):
+ component.get('Preferences').show('downloads')