virt-manager/virtManager/graphwidgets.py
Radostin Stoyanov b8b997fc3a pylint: Resolve consider-using-enumerate
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>
2018-03-03 16:04:14 -05:00

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)