Advertisement
Guest User

Untitled

a guest
May 30th, 2018
586
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.18 KB | None | 0 0
  1. #
  2. # graph.py
  3. #
  4. # Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
  5. # Copyright (C) 2008 Damien Churchill <damoxc@gmail.com>
  6. # Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
  7. # Copyright (C) Marcos Pinto 2007 <markybob@gmail.com>
  8. #
  9. # Deluge is free software.
  10. #
  11. # You may redistribute it and/or modify it under the terms of the
  12. # GNU General Public License, as published by the Free Software
  13. # Foundation; either version 3 of the License, or (at your option)
  14. # any later version.
  15. #
  16. # deluge is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  19. # See the GNU General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU General Public License
  22. # along with deluge. If not, write to:
  23. # The Free Software Foundation, Inc.,
  24. # 51 Franklin Street, Fifth Floor
  25. # Boston, MA 02110-1301, USA.
  26. #
  27. # In addition, as a special exception, the copyright holders give
  28. # permission to link the code of portions of this program with the OpenSSL
  29. # library.
  30. # You must obey the GNU General Public License in all respects for all of
  31. # the code used other than OpenSSL. If you modify file(s) with this
  32. # exception, you may extend this exception to your version of the file(s),
  33. # but you are not obligated to do so. If you do not wish to do so, delete
  34. # this exception statement from your version. If you delete this exception
  35. # statement from all source files in the program, then also delete it here.
  36.  
  37. """
  38. port of old plugin by markybob.
  39. """
  40. import time
  41. import math
  42. import cairo
  43. from deluge.log import LOG as log
  44. from deluge.ui.client import client
  45.  
  46. white = (0, 0, 0)
  47. gray = (0.75, 0.75, 0.75)
  48. black = (1.0, 1.0, 1.0)
  49. darkred = (0.65, 0, 0)
  50. red = (1.0, 0, 0)
  51. green = (0, 1.0, 0)
  52. blue = (0, 0, 1.0)
  53. orange = (1.0, 0.74, 0)
  54.  
  55. def default_formatter(value):
  56. return str(value)
  57.  
  58. def size_formatter_scale(value):
  59. scale = 1.0
  60. for i in range(0,3):
  61. scale = scale * 1024.0
  62. if value / scale < 1024:
  63. return scale
  64.  
  65. def change_opacity(color, opactiy):
  66. """A method to assist in changing the opactiy of a color inorder to draw the
  67. fills.
  68. """
  69. color = list(color)
  70. if len(color) == 4:
  71. color[3] = opactiy
  72. else:
  73. color.append(opactiy)
  74. return tuple(color)
  75.  
  76. class Graph:
  77. def __init__(self):
  78. self.width = 100
  79. self.height = 100
  80. self.length = 150
  81. self.stat_info = {}
  82. self.line_size = 2
  83. self.dash_length = [10]
  84. self.mean_selected = True
  85. self.legend_selected = True
  86. self.max_selected = True
  87. self.black = (0, 0 , 0,)
  88. self.interval = 2 # 2 secs
  89. self.text_bg = (255, 255 , 255, 128) # prototyping
  90. self.set_left_axis()
  91.  
  92. def set_left_axis(self, **kargs):
  93. self.left_axis = kargs
  94.  
  95. def add_stat(self, stat, label='', axis='left', line=True, fill=True, color=None):
  96. self.stat_info[stat] = {
  97. 'axis': axis,
  98. 'label': label,
  99. 'line': line,
  100. 'fill': fill,
  101. 'color': color
  102. }
  103.  
  104. def set_stats(self, stats):
  105. self.last_update = stats["_last_update"]
  106. del stats["_last_update"]
  107. self.length = stats["_length"]
  108. del stats["_length"]
  109. self.interval = stats["_update_interval"]
  110. del stats["_update_interval"]
  111. self.stats = stats
  112. return
  113.  
  114. # def set_config(self, config):
  115. # self.length = config["length"]
  116. # self.interval = config["update_interval"]
  117.  
  118. def set_interval(self, interval):
  119. self.interval = interval
  120.  
  121. def draw_to_context(self, context, width, height):
  122. self.ctx = context
  123. self.width, self.height = width, height
  124. self.draw_rect(white, 0, 0, self.width, self.height)
  125. self.draw_graph()
  126. return self.ctx
  127.  
  128. def draw(self, width, height):
  129. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
  130. ctx = cairo.Context(surface)
  131. self.draw_to_context(ctx, width, height)
  132. return surface
  133.  
  134.  
  135. def draw_x_axis(self, bounds):
  136. (left, top, right, bottom) = bounds
  137. duration = self.length * self.interval
  138. start = self.last_update - duration
  139. ratio = (right - left) / float(duration)
  140.  
  141. if duration < 1800 * 10:
  142. #try rounding to nearest 1min, 5mins, 10mins, 30mins
  143. for step in [60, 300, 600, 1800]:
  144. if duration / step < 10:
  145. x_step = step
  146. break
  147. else:
  148. # if there wasnt anything useful find a nice fitting hourly divisor
  149. x_step = ((duration / 5) /3600 )* 3600
  150.  
  151. #this doesnt allow for dst and timezones...
  152. seconds_to_step = math.ceil(start/float(x_step)) * x_step - start
  153.  
  154. for i in xrange(0, duration/x_step + 1):
  155. text = time.strftime('%H:%M', time.localtime(start + seconds_to_step + i*x_step))
  156. # + 0.5 to allign x to nearest pixel
  157. x = int(ratio * (seconds_to_step + i*x_step) + left) + 0.5
  158. self.draw_x_text(text, x, bottom)
  159. self.draw_dotted_line(gray, x, top-0.5, x, bottom+0.5)
  160.  
  161. self.draw_line(gray, left, bottom+0.5, right, bottom+0.5)
  162.  
  163. def draw_graph(self):
  164. font_extents = self.ctx.font_extents()
  165. x_axis_space = font_extents[2] + 2 + self.line_size / 2.0
  166. plot_height = self.height - x_axis_space
  167. #lets say we need 2n-1*font height pixels to plot the y ticks
  168. tick_limit = (plot_height / font_extents[3] )# / 2.0
  169.  
  170. max_value = 0
  171. for stat in self.stat_info:
  172. if self.stat_info[stat]['axis'] == 'left':
  173. try:
  174. l_max = max(self.stats[stat])
  175. except ValueError:
  176. l_max = 0
  177. if l_max > max_value:
  178. max_value = l_max
  179. if max_value < self.left_axis['min']:
  180. max_value = self.left_axis['min']
  181.  
  182. y_ticks = self.intervalise(max_value, tick_limit)
  183. max_value = y_ticks[-1]
  184. #find the width of the y_ticks
  185. y_tick_text = [self.left_axis['formatter'](tick) for tick in y_ticks]
  186. def space_required(text):
  187. te = self.ctx.text_extents(text)
  188. return math.ceil(te[4] - te[0])
  189. y_tick_width = max((space_required(text) for text in y_tick_text))
  190.  
  191. top = font_extents[2] / 2.0
  192. #bounds(left, top, right, bottom)
  193. bounds = (y_tick_width + 4, top + 2, self.width, self.height - x_axis_space)
  194.  
  195. self.draw_x_axis(bounds)
  196. self.draw_left_axis(bounds, y_ticks, y_tick_text)
  197.  
  198. def intervalise(self, x, limit=None):
  199. """Given a value x create an array of tick points to got with the graph
  200. The number of ticks returned can be constrained by limit, minimum of 3
  201. """
  202. #Limit is the number of ticks which is 1 + the number of steps as we
  203. #count the 0 tick in limit
  204. if limit is not None:
  205. if limit <3:
  206. limit = 2
  207. else:
  208. limit = limit -1
  209. scale = 1
  210. if 'formatter_scale' in self.left_axis:
  211. scale = self.left_axis['formatter_scale'](x)
  212. x = x / float(scale)
  213.  
  214. #Find the largest power of 10 less than x
  215. log = math.log10(x)
  216. intbit = math.floor(log)
  217.  
  218. interval = math.pow(10, intbit)
  219. steps = int(math.ceil(x / interval))
  220.  
  221. if steps <= 1 and (limit is None or limit >= 10*steps):
  222. interval = interval * 0.1
  223. steps = steps * 10
  224. elif steps <= 2 and (limit is None or limit >= 5*steps):
  225. interval = interval * 0.2
  226. steps = steps * 5
  227. elif steps <=5 and (limit is None or limit >= 2*steps):
  228. interval = interval * 0.5
  229. steps = steps * 2
  230.  
  231. if limit is not None and steps > limit:
  232. multi = steps / float(limit)
  233. if multi > 2:
  234. interval = interval * 5
  235. else:
  236. interval = interval * 2
  237.  
  238. intervals = [i * interval * scale for i in xrange(1+int(math.ceil(x/ interval)))]
  239. return intervals
  240.  
  241. def draw_left_axis(self, bounds, y_ticks, y_tick_text):
  242. (left, top, right, bottom) = bounds
  243. stats = {}
  244. for stat in self.stat_info:
  245. if self.stat_info[stat]['axis'] == 'left':
  246. stats[stat] = self.stat_info[stat]
  247. stats[stat]['values'] = self.stats[stat]
  248. stats[stat]['fill_color'] = change_opacity(stats[stat]['color'], 0.5)
  249. stats[stat]['color'] = change_opacity(stats[stat]['color'], 0.8)
  250.  
  251. height = bottom - top
  252. max_value = y_ticks[-1]
  253. ratio = height / max_value
  254.  
  255. for i, y_val in enumerate(y_ticks):
  256. y = int(bottom - y_val * ratio ) - 0.5
  257. if i != 0:
  258. self.draw_dotted_line(gray, left, y, right, y)
  259. self.draw_y_text(y_tick_text[i], left, y)
  260. self.draw_line(gray, left, top, left, bottom)
  261.  
  262. for stat, info in stats.iteritems():
  263. if len(info['values']) > 0:
  264. self.draw_value_poly(info['values'], info['color'], max_value, bounds)
  265. self.draw_value_poly(info['values'], info['fill_color'], max_value, bounds, info['fill'])
  266.  
  267. def draw_legend(self):
  268. pass
  269.  
  270.  
  271. def trace_path(self, values, max_value, bounds):
  272. (left, top, right, bottom) = bounds
  273. ratio = (bottom - top) / max_value
  274. line_width = self.line_size
  275.  
  276. self.ctx.set_line_width(line_width)
  277. self.ctx.move_to(right, bottom)
  278.  
  279. self.ctx.line_to(right, int(bottom - values[0] * ratio ))
  280.  
  281. x = right
  282. step = (right - left) / float(self.length -1)
  283. for i, value in enumerate(values):
  284. if i == self.length - 1:
  285. x = left
  286.  
  287. self.ctx.line_to(x, int(bottom - value * ratio))
  288. x -= step
  289.  
  290. self.ctx.line_to(
  291. int(right - (len(values) - 1) * step),
  292. bottom)
  293. self.ctx.close_path()
  294.  
  295.  
  296. def draw_value_poly(self, values, color, max_value, bounds, fill=False):
  297. self.trace_path(values, max_value, bounds)
  298. self.ctx.set_source_rgba(*color)
  299.  
  300. if fill:
  301. self.ctx.fill()
  302. else:
  303. self.ctx.stroke()
  304.  
  305. def draw_x_text(self, text, x, y):
  306. """Draws text below and horizontally centered about x,y"""
  307. fe = self.ctx.font_extents()
  308. te = self.ctx.text_extents(text)
  309. height = fe[2]
  310. x_bearing = te[0]
  311. width = te[2]
  312. self.ctx.move_to(int(x - width/2.0 + x_bearing), int(y + height))
  313. self.ctx.set_source_rgba(*self.black)
  314. self.ctx.show_text(text)
  315.  
  316. def draw_y_text(self, text, x, y):
  317. """Draws text left of and vertically centered about x,y"""
  318. fe = self.ctx.font_extents()
  319. te = self.ctx.text_extents(text)
  320. descent = fe[1]
  321. ascent = fe[0]
  322. x_bearing = te[0]
  323. width = te[4]
  324. self.ctx.move_to(int(x - width - x_bearing - 2), int(y + (ascent - descent)/2.0))
  325. self.ctx.set_source_rgba(*self.black)
  326. self.ctx.show_text(text)
  327.  
  328. def draw_rect(self, color, x, y, height, width):
  329. self.ctx.set_source_rgba(*color)
  330. self.ctx.rectangle(x, y, height, width)
  331. self.ctx.fill()
  332.  
  333. def draw_line(self, color, x1, y1, x2, y2):
  334. self.ctx.set_source_rgba(*color)
  335. self.ctx.set_line_width(1)
  336. self.ctx.move_to(x1, y1)
  337. self.ctx.line_to(x2, y2)
  338. self.ctx.stroke()
  339.  
  340. def draw_dotted_line(self, color, x1, y1, x2, y2):
  341. self.ctx.set_source_rgba(*color)
  342. self.ctx.set_line_width(1)
  343. dash, offset = self.ctx.get_dash()
  344. self.ctx.set_dash(self.dash_length, 0)
  345. self.ctx.move_to(x1, y1)
  346. self.ctx.line_to(x2, y2)
  347. self.ctx.stroke()
  348. self.ctx.set_dash(dash, offset)
  349.  
  350. if __name__ == "__main__":
  351. import test
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement