296 lines
9.8 KiB
Python
296 lines
9.8 KiB
Python
#
|
|
# Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
|
|
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
|
|
#
|
|
# Basic plugin template created by:
|
|
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
|
|
# 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
|
|
|
|
import gi # isort:skip (Required before Gtk import).
|
|
|
|
gi.require_version('Gtk', '3.0')
|
|
|
|
from gi.repository import Gtk
|
|
from gi.repository.Gdk import RGBA
|
|
|
|
import deluge
|
|
from deluge import component
|
|
from deluge.common import fspeed
|
|
from deluge.plugins.pluginbase import Gtk3PluginBase
|
|
from deluge.ui.client import client
|
|
from deluge.ui.gtk3.torrentdetails import Tab
|
|
|
|
from .common import get_resource
|
|
from .graph import Graph, size_formatter_scale
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
# Gdk.RGBA textual spec
|
|
RED = 'rgb(255,0,0)'
|
|
GREEN = 'rgb(0,128,0)'
|
|
BLUE = 'rgb(0,0,255)'
|
|
DARKRED = 'rgb(139,0,0)'
|
|
ORANGE = 'rgb(255,165,0)'
|
|
|
|
DEFAULT_CONF = {
|
|
'version': 2,
|
|
'colors': {
|
|
'bandwidth_graph': {'upload_rate': BLUE, 'download_rate': GREEN},
|
|
'connections_graph': {
|
|
'dht_nodes': ORANGE,
|
|
'dht_cache_nodes': BLUE,
|
|
'dht_torrents': GREEN,
|
|
'num_connections': DARKRED,
|
|
},
|
|
'seeds_graph': {'num_peers': BLUE},
|
|
},
|
|
}
|
|
|
|
|
|
def neat_time(column, cell, model, data):
|
|
"""Render seconds as seconds or minutes with label"""
|
|
seconds = model.get_value(data, 0)
|
|
if seconds > 60:
|
|
text = '%d %s' % (seconds // 60, _('minutes'))
|
|
elif seconds == 60:
|
|
text = _('1 minute')
|
|
elif seconds == 1:
|
|
text = _('1 second')
|
|
else:
|
|
text = '%d %s' % (seconds, _('seconds'))
|
|
cell.set_property('text', text)
|
|
return
|
|
|
|
|
|
def int_str(number):
|
|
return str(int(number))
|
|
|
|
|
|
def fspeed_shortform(value):
|
|
return fspeed(value, shortform=True)
|
|
|
|
|
|
def text_to_rgba(color):
|
|
"""Turns a Color into a tuple with range 0-1 as used by the graph"""
|
|
color_rgba = RGBA()
|
|
color_rgba.parse(color)
|
|
return color_rgba
|
|
|
|
|
|
class GraphsTab(Tab):
|
|
def __init__(self, colors):
|
|
super().__init__()
|
|
|
|
builder = Gtk.Builder()
|
|
builder.add_from_file(get_resource('tabs.ui'))
|
|
self.window = builder.get_object('graph_tab')
|
|
self.notebook = builder.get_object('graph_notebook')
|
|
self.label = builder.get_object('graph_label')
|
|
|
|
self._name = 'Stats'
|
|
self._child_widget = self.window
|
|
self._tab_label = self.label
|
|
|
|
self.colors = colors
|
|
|
|
self.bandwidth_graph = builder.get_object('bandwidth_graph')
|
|
self.bandwidth_graph.connect('draw', self.on_graph_draw)
|
|
|
|
self.connections_graph = builder.get_object('connections_graph')
|
|
self.connections_graph.connect('draw', self.on_graph_draw)
|
|
|
|
self.seeds_graph = builder.get_object('seeds_graph')
|
|
self.seeds_graph.connect('draw', self.on_graph_draw)
|
|
|
|
self.notebook.connect('switch-page', self._on_notebook_switch_page)
|
|
|
|
self.selected_interval = 1 # Should come from config or similar
|
|
self.select_bandwidth_graph()
|
|
|
|
self.window.unparent()
|
|
self.label.unparent()
|
|
|
|
self.intervals = None
|
|
self.intervals_combo = builder.get_object('combo_intervals')
|
|
cell = Gtk.CellRendererText()
|
|
self.intervals_combo.pack_start(cell, True)
|
|
self.intervals_combo.set_cell_data_func(cell, neat_time)
|
|
self.intervals_combo.connect('changed', self._on_selected_interval_changed)
|
|
self.update_intervals()
|
|
|
|
def on_graph_draw(self, widget, context):
|
|
self.graph.draw_to_context(
|
|
context,
|
|
self.graph_widget.get_allocated_width(),
|
|
self.graph_widget.get_allocated_height(),
|
|
)
|
|
# Do not propagate the event
|
|
return True
|
|
|
|
def update(self):
|
|
d1 = client.stats.get_stats(list(self.graph.stat_info), self.selected_interval)
|
|
d1.addCallback(self.graph.set_stats)
|
|
|
|
def _update_complete(result):
|
|
self.graph_widget.queue_draw()
|
|
return result
|
|
|
|
d1.addCallback(_update_complete)
|
|
return d1
|
|
|
|
def clear(self):
|
|
pass
|
|
|
|
def update_intervals(self):
|
|
client.stats.get_intervals().addCallback(self._on_intervals_changed)
|
|
|
|
def select_bandwidth_graph(self):
|
|
log.debug('Selecting bandwidth graph')
|
|
self.graph_widget = self.bandwidth_graph
|
|
self.graph = Graph()
|
|
colors = self.colors['bandwidth_graph']
|
|
self.graph.add_stat(
|
|
'download_rate',
|
|
label='Download Rate',
|
|
color=text_to_rgba(colors['download_rate']),
|
|
)
|
|
self.graph.add_stat(
|
|
'upload_rate',
|
|
label='Upload Rate',
|
|
color=text_to_rgba(colors['upload_rate']),
|
|
)
|
|
self.graph.set_left_axis(
|
|
formatter=fspeed_shortform, min=10240, formatter_scale=size_formatter_scale
|
|
)
|
|
|
|
def select_connections_graph(self):
|
|
log.debug('Selecting connections graph')
|
|
self.graph_widget = self.connections_graph
|
|
g = Graph()
|
|
self.graph = g
|
|
colors = self.colors['connections_graph']
|
|
g.add_stat('dht_nodes', color=text_to_rgba(colors['dht_nodes']))
|
|
g.add_stat('dht_cache_nodes', color=text_to_rgba(colors['dht_cache_nodes']))
|
|
g.add_stat('dht_torrents', color=text_to_rgba(colors['dht_torrents']))
|
|
g.add_stat('num_connections', color=text_to_rgba(colors['num_connections']))
|
|
g.set_left_axis(formatter=int_str, min=10)
|
|
|
|
def select_seeds_graph(self):
|
|
log.debug('Selecting connections graph')
|
|
self.graph_widget = self.seeds_graph
|
|
self.graph = Graph()
|
|
colors = self.colors['seeds_graph']
|
|
self.graph.add_stat('num_peers', color=text_to_rgba(colors['num_peers']))
|
|
self.graph.set_left_axis(formatter=int_str, min=10)
|
|
|
|
def set_colors(self, colors):
|
|
self.colors = colors
|
|
# Fake switch page to update the graph colors (HACKY)
|
|
self._on_notebook_switch_page(
|
|
self.notebook, None, self.notebook.get_current_page() # This is unused
|
|
)
|
|
|
|
def _on_intervals_changed(self, intervals):
|
|
liststore = Gtk.ListStore(int)
|
|
for inter in intervals:
|
|
liststore.append([inter])
|
|
self.intervals_combo.set_model(liststore)
|
|
try:
|
|
current = intervals.index(self.selected_interval)
|
|
except Exception:
|
|
current = 0
|
|
# should select the value saved in config
|
|
self.intervals_combo.set_active(current)
|
|
|
|
def _on_selected_interval_changed(self, combobox):
|
|
model = combobox.get_model()
|
|
tree_iter = combobox.get_active_iter()
|
|
self.selected_interval = model.get_value(tree_iter, 0)
|
|
self.update()
|
|
return True
|
|
|
|
def _on_notebook_switch_page(self, notebook, page, page_num):
|
|
p = notebook.get_nth_page(page_num)
|
|
if p is self.bandwidth_graph:
|
|
self.select_bandwidth_graph()
|
|
self.update()
|
|
elif p is self.connections_graph:
|
|
self.select_connections_graph()
|
|
self.update()
|
|
elif p is self.seeds_graph:
|
|
self.select_seeds_graph()
|
|
self.update()
|
|
return True
|
|
|
|
|
|
class GtkUI(Gtk3PluginBase):
|
|
def enable(self):
|
|
log.debug('Stats plugin enable called')
|
|
self.config = deluge.configmanager.ConfigManager(
|
|
'stats.gtk3ui.conf', DEFAULT_CONF
|
|
)
|
|
|
|
self.builder = Gtk.Builder()
|
|
self.builder.add_from_file(get_resource('config.ui'))
|
|
|
|
component.get('Preferences').add_page(
|
|
'Stats', self.builder.get_object('prefs_box')
|
|
)
|
|
component.get('PluginManager').register_hook(
|
|
'on_apply_prefs', self.on_apply_prefs
|
|
)
|
|
component.get('PluginManager').register_hook(
|
|
'on_show_prefs', self.on_show_prefs
|
|
)
|
|
self.on_show_prefs()
|
|
|
|
self.graphs_tab = GraphsTab(self.config['colors'])
|
|
self.torrent_details = component.get('TorrentDetails')
|
|
self.torrent_details.add_tab(self.graphs_tab)
|
|
|
|
def disable(self):
|
|
component.get('Preferences').remove_page('Stats')
|
|
component.get('PluginManager').deregister_hook(
|
|
'on_apply_prefs', self.on_apply_prefs
|
|
)
|
|
component.get('PluginManager').deregister_hook(
|
|
'on_show_prefs', self.on_show_prefs
|
|
)
|
|
self.torrent_details.remove_tab(self.graphs_tab.get_name())
|
|
|
|
def on_apply_prefs(self):
|
|
log.debug('applying prefs for Stats')
|
|
gtkconf = {}
|
|
for graph, colors in self.config['colors'].items():
|
|
gtkconf[graph] = {}
|
|
for value, color in colors.items():
|
|
color_btn = self.builder.get_object(f'{graph}_{value}_color')
|
|
try:
|
|
gtkconf[graph][value] = color_btn.get_color().to_string()
|
|
except Exception:
|
|
gtkconf[graph][value] = DEFAULT_CONF['colors'][graph][value]
|
|
self.config['colors'] = gtkconf
|
|
self.graphs_tab.set_colors(self.config['colors'])
|
|
|
|
config = {}
|
|
client.stats.set_config(config)
|
|
|
|
def on_show_prefs(self):
|
|
for graph, colors in self.config['colors'].items():
|
|
for value, color in colors.items():
|
|
try:
|
|
color_btn = self.builder.get_object(f'{graph}_{value}_color')
|
|
color_btn.set_rgba(text_to_rgba(color))
|
|
except Exception as ex:
|
|
log.debug('Unable to set %s %s %s: %s', graph, value, color, ex)
|
|
client.stats.get_config().addCallback(self.cb_get_config)
|
|
|
|
def cb_get_config(self, config):
|
|
"""Callback for on show_prefs."""
|
|
pass
|