mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-23 17:34:21 +03:00
b8b997fc3a
Use enumerate instead of iterating with range and len. This pylint message is emitted when code that iterates with range and len is encountered. Such code can be simplified by using the enumerate built-in. [1] In addition, remove some unused variables to avoid warnings `unused-argument` and `redefined-variable-type`. [1] https://pylint.readthedocs.io/en/latest/technical_reference/features.html#id23 Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
403 lines
13 KiB
Python
403 lines
13 KiB
Python
# Copyright (C) 2013, 2014 Red Hat, Inc.
|
|
#
|
|
# This program is free software; you can 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.
|
|
#
|
|
# This program 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 this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301 USA.
|
|
#
|
|
|
|
from gi.repository import GObject
|
|
from gi.repository import Gtk
|
|
|
|
# pylint: disable=arguments-differ
|
|
# Newer pylint can detect, but warns that overridden arguments are wrong
|
|
|
|
|
|
def rect_print(name, rect):
|
|
# For debugging
|
|
print("%s: height=%d, width=%d, x=%d, y=%d" %
|
|
(name, rect.height, rect.width, rect.x, rect.y))
|
|
|
|
|
|
def _line_helper(cairo_ct, bottom_baseline, points, for_fill=False):
|
|
last_was_zero = False
|
|
last_point = None
|
|
|
|
for index, (x, y) in enumerate(points):
|
|
|
|
# If stats value == 0, we don't want to draw a line
|
|
is_zero = bool(y == bottom_baseline)
|
|
|
|
# If the line is for filling, alter the coords so that fill covers
|
|
# the same area as the parent sparkline: fill is one pixel short
|
|
# to not overwrite the spark line
|
|
if for_fill:
|
|
if index == 0:
|
|
x -= 1
|
|
elif index == (len(points) - 1):
|
|
x += 1
|
|
elif last_was_zero and is_zero:
|
|
y += 1
|
|
|
|
if index == 0:
|
|
cairo_ct.move_to(x, y)
|
|
elif last_was_zero and is_zero and not for_fill:
|
|
cairo_ct.move_to(x, y)
|
|
else:
|
|
cairo_ct.line_to(x, y)
|
|
last_point = (x, y)
|
|
|
|
last_was_zero = is_zero
|
|
|
|
return last_point
|
|
|
|
|
|
def draw_line(cairo_ct, y, h, points):
|
|
if not len(points):
|
|
return
|
|
|
|
last_point = _line_helper(cairo_ct, y + h, points)
|
|
if not last_point:
|
|
# Nothing to draw
|
|
return
|
|
|
|
# Paint the line
|
|
cairo_ct.stroke()
|
|
|
|
|
|
def draw_fill(cairo_ct, x, y, w, h, points, taper=False):
|
|
if not len(points):
|
|
return
|
|
|
|
_line_helper(cairo_ct, y + h, points, for_fill=True)
|
|
|
|
baseline_y = h + y + 1
|
|
if taper:
|
|
start_x = w + x
|
|
else:
|
|
start_x = points[-1][0]
|
|
|
|
# Box out the area to fill
|
|
cairo_ct.line_to(start_x + 1, baseline_y)
|
|
cairo_ct.line_to(x - 1, baseline_y)
|
|
|
|
# Paint the fill
|
|
cairo_ct.fill()
|
|
|
|
|
|
class CellRendererSparkline(Gtk.CellRenderer):
|
|
__gproperties__ = {
|
|
# 'name': (GObject.TYPE_*,
|
|
# nickname, long desc, (type related args), mode)
|
|
# Type related args can be min, max for int (etc.), or default value
|
|
# for strings and bool
|
|
'data_array': (GObject.TYPE_PYOBJECT, "Data Array",
|
|
"Array of data points for the graph",
|
|
GObject.PARAM_READWRITE),
|
|
'reversed': (GObject.TYPE_BOOLEAN, "Reverse data",
|
|
"Process data from back to front.",
|
|
0, GObject.PARAM_READWRITE),
|
|
}
|
|
|
|
def __init__(self):
|
|
Gtk.CellRenderer.__init__(self)
|
|
|
|
self.data_array = []
|
|
self.num_sets = 0
|
|
self.filled = True
|
|
self.reversed = False
|
|
self.rgb = None
|
|
|
|
def do_render(self, cr, widget, background_area, cell_area,
|
|
flags):
|
|
# cr : Cairo context
|
|
# widget : GtkWidget instance
|
|
# background_area : GdkRectangle: entire cell area
|
|
# cell_area : GdkRectangle: area normally rendered by cell
|
|
# flags : flags that affect rendering
|
|
# flags = Gtk.CELL_RENDERER_SELECTED, Gtk.CELL_RENDERER_PRELIT,
|
|
# Gtk.CELL_RENDERER_INSENSITIVE or Gtk.CELL_RENDERER_SORTED
|
|
ignore = widget
|
|
ignore = background_area
|
|
ignore = flags
|
|
|
|
# Indent of the gray border around the graph
|
|
BORDER_PADDING = 2
|
|
# Indent of graph from border
|
|
GRAPH_INDENT = 2
|
|
GRAPH_PAD = (BORDER_PADDING + GRAPH_INDENT)
|
|
|
|
# We don't use yalign, since we expand to the entire height
|
|
ignore = self.get_property("yalign")
|
|
xalign = self.get_property("xalign")
|
|
|
|
# Set up graphing bounds
|
|
graph_x = (cell_area.x + GRAPH_PAD)
|
|
graph_y = (cell_area.y + GRAPH_PAD)
|
|
graph_width = (cell_area.width - (GRAPH_PAD * 2))
|
|
graph_height = (cell_area.height - (GRAPH_PAD * 2))
|
|
|
|
pixels_per_point = (graph_width // max(1, len(self.data_array) - 1))
|
|
|
|
# Graph width needs to be some multiple of the amount of data points
|
|
# we have
|
|
graph_width = (pixels_per_point * max(1, len(self.data_array) - 1))
|
|
|
|
# Recalculate border width based on the amount we are graphing
|
|
border_width = graph_width + (GRAPH_INDENT * 2)
|
|
|
|
# Align the widget
|
|
empty_space = cell_area.width - border_width - (BORDER_PADDING * 2)
|
|
if empty_space:
|
|
xalign_space = int(empty_space * xalign)
|
|
cell_area.x += xalign_space
|
|
graph_x += xalign_space
|
|
|
|
cr.set_line_width(3)
|
|
# 1 == LINE_CAP_ROUND
|
|
cr.set_line_cap(1)
|
|
|
|
# Draw gray graph border
|
|
cr.set_source_rgb(0.8828125, 0.8671875, 0.8671875)
|
|
cr.rectangle(cell_area.x + BORDER_PADDING,
|
|
cell_area.y + BORDER_PADDING,
|
|
border_width,
|
|
cell_area.height - (BORDER_PADDING * 2))
|
|
cr.stroke()
|
|
|
|
# Fill in white box inside graph outline
|
|
cr.set_source_rgb(1, 1, 1)
|
|
cr.rectangle(cell_area.x + BORDER_PADDING,
|
|
cell_area.y + BORDER_PADDING,
|
|
border_width,
|
|
cell_area.height - (BORDER_PADDING * 2))
|
|
cr.fill()
|
|
|
|
def get_y(index):
|
|
baseline_y = graph_y + graph_height
|
|
|
|
if self.reversed:
|
|
n = (len(self.data_array) - index - 1)
|
|
else:
|
|
n = index
|
|
|
|
val = self.data_array[n]
|
|
y = baseline_y - (graph_height * val)
|
|
|
|
y = max(graph_y, y)
|
|
y = min(graph_y + graph_height, y)
|
|
return y
|
|
|
|
points = []
|
|
for index in range(0, len(self.data_array)):
|
|
x = int(((index * pixels_per_point) + graph_x))
|
|
y = int(get_y(index))
|
|
|
|
points.append((x, y))
|
|
|
|
|
|
cell_area.x = graph_x
|
|
cell_area.y = graph_y
|
|
cell_area.width = graph_width
|
|
cell_area.height = graph_height
|
|
|
|
# Set color to dark blue for the actual sparkline
|
|
cr.set_line_width(2)
|
|
cr.set_source_rgb(0.421875, 0.640625, 0.73046875)
|
|
draw_line(cr, cell_area.y, cell_area.height, points)
|
|
|
|
# Set color to light blue for the fill
|
|
cr.set_source_rgba(0.71484375, 0.84765625, 0.89453125, .5)
|
|
draw_fill(cr,
|
|
cell_area.x, cell_area.y,
|
|
cell_area.width, cell_area.height,
|
|
points)
|
|
return
|
|
|
|
def do_get_size(self, widget, cell_area=None):
|
|
ignore = widget
|
|
|
|
FIXED_WIDTH = len(self.data_array)
|
|
FIXED_HEIGHT = 15
|
|
xpad = self.get_property("xpad")
|
|
ypad = self.get_property("ypad")
|
|
|
|
if cell_area:
|
|
# What to do here? haven't encountered this in practice
|
|
xoffset = 0
|
|
yoffset = 0
|
|
else:
|
|
xoffset = 0
|
|
yoffset = 0
|
|
|
|
width = ((xpad * 2) + FIXED_WIDTH)
|
|
height = ((ypad * 2) + FIXED_HEIGHT)
|
|
|
|
return (xoffset, yoffset, width, height)
|
|
|
|
# Properties are passed to use with "-" in the name, but python
|
|
# variables can't be named like that
|
|
def _sanitize_param_spec_name(self, name):
|
|
return name.replace("-", "_")
|
|
def do_get_property(self, param_spec):
|
|
name = self._sanitize_param_spec_name(param_spec.name)
|
|
return getattr(self, name)
|
|
def do_set_property(self, param_spec, value):
|
|
name = self._sanitize_param_spec_name(param_spec.name)
|
|
setattr(self, name, value)
|
|
|
|
def set_property(self, *args, **kwargs):
|
|
# Make pylint happy
|
|
return Gtk.CellRenderer.set_property(self, *args, **kwargs)
|
|
|
|
|
|
class Sparkline(Gtk.DrawingArea):
|
|
__gproperties__ = {
|
|
# 'name': (GObject.TYPE_*,
|
|
# nickname, long desc, (type related args), mode)
|
|
# Type related args can be min, max for int (etc.), or default value
|
|
# for strings and bool
|
|
'data_array': (GObject.TYPE_PYOBJECT, "Data Array",
|
|
"Array of data points for the graph",
|
|
GObject.PARAM_READWRITE),
|
|
'filled': (GObject.TYPE_BOOLEAN, 'Filled', 'the foo of the object',
|
|
1,
|
|
GObject.PARAM_READWRITE),
|
|
'num_sets': (GObject.TYPE_INT, "Number of sets",
|
|
"Number of data sets to graph",
|
|
1, 2, 1, GObject.PARAM_READWRITE),
|
|
'reversed': (GObject.TYPE_BOOLEAN, "Reverse data",
|
|
"Process data from back to front.",
|
|
0, GObject.PARAM_READWRITE),
|
|
'rgb': (GObject.TYPE_PYOBJECT, "rgb array", "List of rgb values",
|
|
GObject.PARAM_READWRITE),
|
|
}
|
|
|
|
def __init__(self):
|
|
Gtk.DrawingArea.__init__(self)
|
|
|
|
self._data_array = []
|
|
self.num_sets = 1
|
|
self.filled = True
|
|
self.reversed = False
|
|
self.rgb = []
|
|
|
|
ctxt = self.get_style_context()
|
|
ctxt.add_class(Gtk.STYLE_CLASS_ENTRY)
|
|
|
|
def set_data_array(self, val):
|
|
self._data_array = val
|
|
self.queue_draw()
|
|
def get_data_array(self):
|
|
return self._data_array
|
|
data_array = property(get_data_array, set_data_array)
|
|
|
|
|
|
def do_draw(self, cr):
|
|
cr.save()
|
|
|
|
window = self.get_window()
|
|
w = window.get_width()
|
|
h = window.get_height()
|
|
|
|
points_per_set = (len(self.data_array) // self.num_sets)
|
|
pixels_per_point = (float(w) /
|
|
(float((points_per_set - 1) or 1)))
|
|
|
|
widget = self
|
|
ctx = widget.get_style_context()
|
|
|
|
# This draws the light gray backing rectangle
|
|
Gtk.render_background(ctx, cr, 0, 0, w - 1, h - 1)
|
|
|
|
# This draws the marker ticks
|
|
max_ticks = 4
|
|
for index in range(1, max_ticks):
|
|
Gtk.render_line(ctx, cr, 1,
|
|
(h // max_ticks) * index,
|
|
w - 2,
|
|
(h // max_ticks) * index)
|
|
|
|
# Foreground-color graphics context
|
|
# This draws the black border
|
|
Gtk.render_frame(ctx, cr, 0, 0, w - 1, h - 1)
|
|
|
|
# Draw the actual sparkline
|
|
def get_y(dataset, index):
|
|
baseline_y = h
|
|
|
|
n = dataset * points_per_set
|
|
if self.reversed:
|
|
n += (points_per_set - index - 1)
|
|
else:
|
|
n += index
|
|
|
|
val = self.data_array[n]
|
|
return baseline_y - ((h - 1) * val)
|
|
|
|
cr.set_line_width(2)
|
|
|
|
for dataset in range(0, self.num_sets):
|
|
if len(self.rgb) == (self.num_sets * 3):
|
|
cr.set_source_rgb(self.rgb[(dataset * 3)],
|
|
self.rgb[(dataset * 3) + 1],
|
|
self.rgb[(dataset * 1) + 2])
|
|
points = []
|
|
for index in range(0, points_per_set):
|
|
x = index * pixels_per_point
|
|
y = get_y(dataset, index)
|
|
|
|
points.append((int(x), int(y)))
|
|
|
|
|
|
if self.num_sets == 1:
|
|
pass
|
|
|
|
draw_line(cr, 0, h, points)
|
|
if self.filled:
|
|
# Fixes a fully filled graph from having an oddly
|
|
# tapered in end (bug 560913). Need to figure out
|
|
# what's really going on.
|
|
points = [(0, h)] + points
|
|
draw_fill(cr, 0, 0, w, h, points, taper=True)
|
|
|
|
cr.restore()
|
|
|
|
return 0
|
|
|
|
def do_size_request(self, requisition):
|
|
width = len(self.data_array) / self.num_sets
|
|
height = 20
|
|
|
|
requisition.width = width
|
|
requisition.height = height
|
|
|
|
# Properties are passed to use with "-" in the name, but python
|
|
# variables can't be named like that
|
|
def _sanitize_param_spec_name(self, name):
|
|
return name.replace("-", "_")
|
|
def do_get_property(self, param_spec):
|
|
name = self._sanitize_param_spec_name(param_spec.name)
|
|
return getattr(self, name)
|
|
def do_set_property(self, param_spec, value):
|
|
name = self._sanitize_param_spec_name(param_spec.name)
|
|
setattr(self, name, value)
|
|
|
|
# These make pylint happy
|
|
def set_property(self, *args, **kwargs):
|
|
return Gtk.DrawingArea.set_property(self, *args, **kwargs)
|
|
def show(self, *args, **kwargs):
|
|
return Gtk.DrawingArea.show(self, *args, **kwargs)
|
|
def destroy(self, *args, **kwargs):
|
|
return Gtk.DrawingArea.destroy(self, *args, **kwargs)
|