graph plugin; initial commit

This commit is contained in:
Martijn Voncken 2008-09-30 18:05:16 +00:00
parent 92608b436b
commit 1cb4559362
10 changed files with 676 additions and 0 deletions

View File

@ -0,0 +1,6 @@
#!/bin/bash
mkdir temp
export PYTHONPATH=./temp
python setup.py build develop --install-dir ./temp
cp ./temp/Graph.egg-link ~/.config/deluge/plugins
rm -fr ./temp

View File

@ -0,0 +1,65 @@
#
# __init__.py
#
# 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 ('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 3 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
from deluge.log import LOG as log
from deluge.plugins.init import PluginBase
class CorePlugin(PluginBase):
def __init__(self, plugin_api, plugin_name):
# Load the Core portion of the plugin
try:
from core import Core
self.plugin = Core(plugin_api, plugin_name)
except Exception, e:
log.debug("Did not load a Core plugin: %s", e)
class WebUIPlugin(PluginBase):
def __init__(self, plugin_api, plugin_name):
try:
from webui import WebUI
self.plugin = WebUI(plugin_api, plugin_name)
except Exception, e:
log.debug("Did not load a WebUI plugin: %s", e)
class GtkUIPlugin(PluginBase):
def __init__(self, plugin_api, plugin_name):
# Load the GtkUI portion of the plugin
try:
from gtkui import GtkUI
self.plugin = GtkUI(plugin_api, plugin_name)
except Exception, e:
log.debug("Did not load a GtkUI plugin: %s", e)

View File

@ -0,0 +1,93 @@
#
# core.py
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) Marcos Pinto 2007 <markybob@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 3 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
import deluge
from deluge.log import LOG as log
from deluge.plugins.corepluginbase import CorePluginBase
from deluge import component
import gobject
#from deluge.plugins.coreclient import client #1.1 and later only
#client: see http://dev.deluge-torrent.org/wiki/Development/UiClient#Remoteapi
DEFAULT_PREFS = {
"test":"NiNiNi",
"update_interval":2000, #2 seconds.
"length":150, # 2 seconds * 150 --> 5 minutes.
}
"""
port of the old NetworkGraph Plugin.
"""
class Core(CorePluginBase):
def enable(self):
self.core = component.get("Core")
self.savedUpSpeeds = []
self.savedDownSpeeds = []
self.savedConnections = []
self.config = deluge.configmanager.ConfigManager("graph.conf", DEFAULT_PREFS)
self.update_timer = gobject.timeout_add(self.config.get("update_interval"), self.update_stats)
self.length = self.config.get("length")
def disable(self):
gobject.source_remove(self.update_timer)
def update_stats(self):
try:
status = self.core.session.status()
self.savedUpSpeeds.insert(0, int(status.payload_upload_rate))
if len(self.savedUpSpeeds) > self.length:
self.savedUpSpeeds.pop()
self.savedDownSpeeds.insert(0, int(status.payload_download_rate))
if len(self.savedDownSpeeds) > self.length:
self.savedDownSpeeds.pop()
except Exception,e:
log.error(e.message)
return True
def export_get_upload(self,length=None):
return self.savedUpSpeeds
def export_get_download(self,length=None):
return self.savedDownSpeeds
def export_set_config(self, config):
"sets the config dictionary"
for key in config.keys():
self.config[key] = config[key]
self.config.save()
def export_get_config(self):
"returns the config dictionary"
return self.config.get_config()

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.5 on Fri Aug 8 23:34:44 2008 -->
<glade-interface>
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkHBox" id="prefs_box">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Test config value:</property>
</widget>
</child>
<child>
<widget class="GtkEntry" id="txt_test">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -0,0 +1,183 @@
#graph.py
"""
port of old plugin by markybob.
"""
import cairo
import math
from deluge.log import LOG as log
import deluge.common
import time
from deluge.ui.client import aclient
class NetworkGraph:
def __init__(self):
self.width = 100
self.height = 100
self.length = 150
self.savedUpSpeeds = []
self.savedDownSpeeds = []
self.download_line = True
self.download_fill = True
self.upload_line = True
self.upload_fill = True
self.download_line_color = (0, 0.75,0, 1.0)
self.download_fill_color = (0.6 ,1.1 , 0.6, 1.0)
self.upload_line_color = (0, 0, 1.0, 0.75)
self.upload_fill_color = (0.43,0.43,1.1, 0.5)
self.mean_selected = True
self.legend_selected = True
self.max_selected = True
self.line_size = 4
self.black = (0, 0 , 0,)
self.interval = 2000 #2secs
self.text_bg = (255, 255 , 255, 128) #prototyping.
def async_request(self):
"""
convenience method, see test.py
"""
aclient.graph_get_upload(self.set_upload)
aclient.graph_get_download(self.set_download)
aclient.graph_get_config(self.set_config)
#async callbacks:
def set_config(self, config):
self.length = config["stats_length"]
self.interval = config["update_interval"]
def set_upload(self , upload):
self.savedUpSpeeds = upload
def set_download(self , download):
self.savedDownSpeeds = download
def draw(self, width, height):
self.width = width
self.height = height
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width,self.height)
self.ctx = cairo.Context(self.surface)
if (self.download_fill or self.download_line) and (self.upload_fill or self.upload_line):
maxSpeed = max(max(self.savedDownSpeeds),max(self.savedUpSpeeds))
meanSpeed = max(sum(self.savedUpSpeeds) /len(self.savedUpSpeeds), sum(self.savedDownSpeeds)/len(self.savedDownSpeeds))
elif self.download_fill or self.download_line:
maxSpeed = max(self.savedDownSpeeds)
meanSpeed = sum(self.savedDownSpeeds)/len(self.savedDownSpeeds)
elif self.upload_fill or self.upload_line:
maxSpeed = max(self.savedUpSpeeds)
meanSpeed = sum(self.savedUpSpeeds) /len(self.savedUpSpeeds)
else:
maxSpeed = 0
if maxSpeed > 0:
if self.max_selected:
self.drawText(deluge.common.fspeed(maxSpeed),4,2)
if self.download_fill:
self.drawSpeedPoly(self.savedDownSpeeds,self.download_fill_color, maxSpeed, True)
if self.download_line:
self.drawSpeedPoly(self.savedDownSpeeds,self.download_line_color,maxSpeed, False)
if self.upload_fill:
self.drawSpeedPoly(self.savedUpSpeeds,self.upload_fill_color,maxSpeed, True)
if self.upload_line:
self.drawSpeedPoly(self.savedUpSpeeds,self.upload_line_color,maxSpeed, False)
if self.mean_selected:
mean = int(self.height - 1 - ((self.height-28)*meanSpeed/maxSpeed))
self.drawLine(self.black, 0,mean, self.width, mean)
self.drawText(deluge.common.fspeed(meanSpeed), 4, mean - 12 - 2)
if self.legend_selected:
self.drawLegend()
return self.surface
def tracePath(self, speeds, maxSpeed):
lineWidth = self.line_size
self.ctx.set_line_width(lineWidth)
self.ctx.move_to(self.width + lineWidth,self.height + lineWidth)
self.ctx.line_to(self.width + lineWidth,int(self.height-((self.height-28)*speeds[0]/maxSpeed)))
for i in range(len(speeds)):
self.ctx.line_to(int(self.width-1-((i*self.width)/(self.length-1))),int(self.height-1-((self.height-28)*speeds[i]/maxSpeed)))
self.ctx.line_to(int(self.width-1-(((len(speeds)-1)*self.width)/(self.length-1))),int(self.height)-1 + lineWidth)
self.ctx.close_path()
def drawSpeedPoly(self, speeds, color, maxSpeed, fill):
self.tracePath(speeds, maxSpeed)
self.ctx.set_source_rgba(color[0],color[1],color[2], color[3])
if fill:
self.ctx.fill()
else:
self.ctx.stroke()
def drawLegend(self):
showDown = self.download_fill or self.download_line
showUp = self.upload_fill or self.upload_line
downBox_X = self.width-113
self.drawText("Download:", self.width-180,3)
self.drawText("Upload:", self.width-80,3)
if self.download_fill and self.download_line:
self.drawRect(self.download_line_color,downBox_X,5,12,12)
self.drawRect(self.download_fill_color,downBox_X+12,5,12,12)
elif self.download_fill:
self.drawRect(self.download_fill_color,downBox_X,5,24,12)
elif self.download_line:
self.drawRect(self.download_line_color,downBox_X,5,24,12)
if self.upload_fill and self.upload_line:
self.drawRect(self.upload_line_color,self.width-30,5,12,12)
self.drawRect(self.upload_fill_color,self.width-18,5,12,12)
elif self.upload_fill:
self.drawRect(self.upload_fill_color,self.width-30,5,24,12)
elif self.upload_line:
self.drawRect(self.upload_line_color,self.width-30,5,24,12)
if True:
txt_end = time.strftime("%H:%M:%S")
txt_start = time.strftime("%H:%M:%S",time.localtime(time.time() - self.length * (self.interval / 1000.0) ))
self.length * self.interval
self.drawText(txt_end, self.width-60,self.height - 20)
self.drawText(txt_start, 4 ,self.height - 20)
def drawText(self,text,x,y):
self.ctx.set_font_size(12)
self.ctx.move_to(x, y +12)
self.ctx.set_source_rgba(*self.black)
self.ctx.show_text(text)
def drawRect(self,color,x,y,height,width):
self.ctx.set_source_rgba(color[0],color[1],color[2],color[3],)
self.ctx.rectangle(x,y,height,width)
self.ctx.fill()
def drawLine(self,color,x1,y1,x2,y2):
self.ctx.set_source_rgba(*color)
self.ctx.set_line_width(1)
self.ctx.move_to(x1, y1)
self.ctx.line_to(x2, y2)
self.ctx.stroke()
if __name__ == "__main__":
import test

View File

@ -0,0 +1,75 @@
#
# gtkui.py
#
# 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 ('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 3 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
from deluge.log import LOG as log
from deluge.ui.client import aclient
import gtk
class GtkUI(object):
def __init__(self, plugin_api, plugin_name):
log.debug("Calling Graph UI init")
self.plugin = plugin_api
def enable(self):
self.glade = gtk.glade.XML(self.get_resource("config.glade"))
self.plugin.add_preferences_page("Graph", self.glade.get_widget("prefs_box"))
self.plugin.register_hook("on_apply_prefs", self.on_apply_prefs)
self.plugin.register_hook("on_show_prefs", self.on_show_prefs)
self.on_show_prefs()
def disable(self):
self.plugin.remove_preferences_page("Graph")
self.plugin.deregister_hook("on_apply_prefs", self.on_apply_prefs)
self.plugin.deregister_hook("on_show_prefs", self.on_show_prefs)
def on_apply_prefs(self):
log.debug("applying prefs for Graph")
config = {
"test":self.glade.get_widget("txt_test").get_text()
}
aclient.graph_set_config(None, config)
def on_show_prefs(self):
aclient.graph_get_config(self.cb_get_config)
def cb_get_config(self, config):
"callback for on show_prefs"
self.glade.get_widget("txt_test").set_text(config["test"])
def get_resource(self, filename):
import pkg_resources, os
return pkg_resources.resource_filename("graph", os.path.join("data", filename))

View File

@ -0,0 +1,10 @@
$:render.header(_("Network Graph"), 'graph')
$:render.admin_toolbar('graph')
<div style="padding-left:20px">
<img src="$base/graph/network.png?height=400&width=800">
</div>
$:render.footer()

View File

@ -0,0 +1,60 @@
from deluge.ui.client import sclient, aclient
sclient.set_core_uri()
from graph import NetworkGraph
def test_sync():
if 1:
upload = sclient.graph_get_upload()
download = sclient.graph_get_download()
print upload
print download
else:
upload = [66804, 66915, 66974, 67447, 67540, 67318, 67320, 67249, 66659, 66489, 67027, 66914, 66802, 67303, 67654, 67643, 67763, 67528, 67523, 67431, 67214, 66939, 67316, 67020, 66881, 67103, 67377, 67141, 67366, 67492, 67375, 67203, 67056, 67010, 67029, 66741, 66695, 66868, 66805, 66264, 66249, 66317, 66459, 66306, 66681, 66954, 66662, 66278, 65921, 65695, 65681, 65942, 66000, 66140, 66424, 66480, 66257, 66271, 66145, 65854, 65568, 65268, 65112, 65050, 65027, 64676, 64655, 64178, 64386, 63979, 63271, 62746, 62337, 62297, 62496, 62902, 63801, 64121, 62957, 62921, 63051, 62644, 63240, 64107, 63968, 63987, 63644, 63263, 63153, 62999, 62843, 62777, 63101, 63078, 63178, 63126, 63401, 62630, 62451, 62505, 62254, 61485, 61264, 60937, 60568, 61011, 61109, 60325, 60196, 59640, 59619, 59514, 60813, 60572, 61632, 61689, 63365, 64583, 66396, 67179, 68209, 68295, 67674, 67559, 67195, 66178, 65632, 66124, 66456, 66676, 67183, 67620, 66960, 66347, 65925, 65907, 65896, 66738, 66703, 67060, 67004, 67007, 66329, 65304, 52002, 38969, 25433, 12426, 0, 0]
download = [42926, 43853, 43157, 45470, 44254, 46272, 45083, 47344, 46716, 51963, 50112, 52334, 55525, 57545, 53691, 51637, 49574, 49836, 48295, 49843, 52878, 56014, 56966, 56938, 60065, 60461, 56542, 59526, 58678, 54424, 51862, 55109, 52132, 53783, 51687, 56567, 52182, 50758, 46714, 50511, 48161, 50920, 48694, 50528, 55074, 55420, 55882, 59268, 59958, 57938, 57115, 51424, 51180, 53184, 52879, 51177, 54417, 51097, 47901, 49870, 55865, 61118, 61476, 63498, 58878, 49630, 45975, 45632, 45892, 44855, 49495, 48304, 45829, 42152, 39403, 37574, 32384, 34933, 34901, 33492, 31953, 36271, 33826, 34515, 36408, 41106, 43054, 44110, 40810, 41383, 37267, 35881, 38660, 37525, 34857, 36718, 36842, 34281, 39528, 41854, 42952, 40021, 41722, 41045, 42917, 39287, 38672, 32824, 28765, 22686, 18490, 15714, 15268, 14793, 15305, 16354, 16720, 17502, 17857, 16622, 18447, 19929, 31138, 36965, 36158, 32795, 30445, 21997, 18100, 22491, 27227, 29317, 32436, 35700, 39140, 36258, 33697, 24751, 20354, 8211, 3836, 1560, 834, 2034, 1744, 1637, 1637, 1637, 0, 0]
from graph import NetworkGraph
n = NetworkGraph()
n.savedUpSpeeds = upload
n.savedDownSpeeds = download
n.draw(800,200)
n.surface.write_to_png('output_sync.png')
def test_async():
n = NetworkGraph()
n.async_request()
aclient.force_call(True)
surface = n.draw(600,300)
surface.write_to_png('output_async.png')
def test_write():
"""
writing to a file-like object; need this for webui.
"""
class fake_file:
def __init__(self):
self.data = []
def write(self, str):
self.data.append(str)
n = NetworkGraph()
n.async_request()
aclient.force_call(True)
surface = n.draw(900,150)
file_like = fake_file()
surface.write_to_png(file_like)
data = "".join(file_like.data)
f = open("file_like.png","wb")
f.write(data)
f.close()
test_sync()
test_async()
test_write()

View File

@ -0,0 +1,101 @@
#
# webui.py
#
# 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 ('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 3 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
import os
from deluge.log import LOG as log
from deluge.ui.client import sclient, aclient
from deluge.plugins.webuipluginbase import WebUIPluginBase
from deluge import component
from graph import NetworkGraph
api = component.get("WebPluginApi")
forms = api.forms
#pages:
class graph_page:
@api.deco.deluge_page
def GET(self, args):
return api.render.graph.graph()
class network_png:
@api.deco.check_session
def GET(self, args):
vars = api.web.input(width = 600, height = 150)
log.debug("1")
api.web.header("Content-Type", "image/png")
n = NetworkGraph()
n.async_request()
aclient.force_call(True)
self.data = ""
surface = n.draw(int(vars.width), int(vars.height))
surface.write_to_png(self)
print self.data
def write(self, str): #file like object for pango; write_to_png
self.data += str
class WebUI(WebUIPluginBase):
#map url's to classes: [(url,class), ..]
urls = [
('/graph', graph_page),
('/graph/network.png', network_png)
]
def enable(self):
api.config_page_manager.register('plugins', 'graph' ,ConfigForm)
api.menu_manager.register_admin_page("graph", _("Graph"), "/graph") #<--top menu
def disable(self):
api.config_page_manager.deregister('graph')
api.menu_manager.deregister_admin_page("graph") #<--top menu
class ConfigForm(forms.Form):
#meta:
title = _("Graph")
#load/save:
def initial_data(self):
return sclient.graph_get_config()
def save(self, data):
cfg = dict(data)
sclient.graph_set_config(cfg)
#django newforms magic: define config fields:
test = forms.CharField(label=_("Test config value"))

View File

@ -0,0 +1,56 @@
#
# setup.py
#
# 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 ('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 3 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
from setuptools import setup
__author__ = "Martijn Voncken <mvoncken@gmail.com>"
setup(
name="Graph",
version="0.1",
description=__doc__,
author=__author__,
packages=["graph"],
package_data = {"graph": ["template/*","data/*"]},
entry_points="""
[deluge.plugin.core]
Graph = graph:CorePlugin
[deluge.plugin.webui]
Graph = graph:WebUIPlugin
[deluge.plugin.gtkui]
Graph = graph:GtkUIPlugin
"""
)