mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-02-25 18:55:27 -06:00
Reimplement current graph widgets in python.
This commit is contained in:
parent
290b896a41
commit
631b7b9bed
@ -22,7 +22,6 @@ import gobject
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import libvirt
|
||||
import sparkline
|
||||
import logging
|
||||
import traceback
|
||||
import sys
|
||||
@ -36,6 +35,7 @@ from virtManager.error import vmmErrorDialog
|
||||
from virtManager.addhardware import vmmAddHardware
|
||||
from virtManager.choosecd import vmmChooseCD
|
||||
from virtManager.serialcon import vmmSerialConsole
|
||||
from virtManager.graphwidgets import Sparkline
|
||||
from virtManager import util as util
|
||||
|
||||
import virtinst
|
||||
@ -193,15 +193,15 @@ class vmmDetails(gobject.GObject):
|
||||
util.tooltip_wrapper(self.window.get_widget("security-dynamic-info"),
|
||||
_("The dynamic SELinux security type tells libvirt to automatically pick a unique label for the guest process and guest image, ensuring total isolation of the guest. (Default)"))
|
||||
|
||||
self.cpu_usage_graph = sparkline.Sparkline()
|
||||
self.cpu_usage_graph = Sparkline()
|
||||
self.cpu_usage_graph.set_property("reversed", True)
|
||||
self.window.get_widget("graph-table").attach(self.cpu_usage_graph, 1, 2, 0, 1)
|
||||
|
||||
self.memory_usage_graph = sparkline.Sparkline()
|
||||
self.memory_usage_graph = Sparkline()
|
||||
self.memory_usage_graph.set_property("reversed", True)
|
||||
self.window.get_widget("graph-table").attach(self.memory_usage_graph, 1, 2, 1, 2)
|
||||
|
||||
self.disk_io_graph = sparkline.Sparkline()
|
||||
self.disk_io_graph = Sparkline()
|
||||
self.disk_io_graph.set_property("reversed", True)
|
||||
self.disk_io_graph.set_property("filled", False)
|
||||
self.disk_io_graph.set_property("num_sets", 2)
|
||||
@ -209,7 +209,7 @@ class vmmDetails(gobject.GObject):
|
||||
[0x82, 0x00, 0x3B, 0x29, 0x5C, 0x45]))
|
||||
self.window.get_widget("graph-table").attach(self.disk_io_graph, 1, 2, 2, 3)
|
||||
|
||||
self.network_traffic_graph = sparkline.Sparkline()
|
||||
self.network_traffic_graph = Sparkline()
|
||||
self.network_traffic_graph.set_property("reversed", True)
|
||||
self.network_traffic_graph.set_property("filled", False)
|
||||
self.network_traffic_graph.set_property("num_sets", 2)
|
||||
|
287
src/virtManager/graphwidgets.py
Normal file
287
src/virtManager/graphwidgets.py
Normal file
@ -0,0 +1,287 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import gobject
|
||||
import gtk.glade
|
||||
|
||||
# For gproperties info, see:
|
||||
# http://www.pygtk.org/docs/pygtk/class-gtkcontainer.html#function-gtk--container-class-install-child-property
|
||||
|
||||
def _draw_sparkline(cairo_ct, cell_area, points, filled, points_per_set,
|
||||
taper=False):
|
||||
for index in range(0, points_per_set):
|
||||
x, y = points[index]
|
||||
|
||||
if index == 0:
|
||||
cairo_ct.move_to(x, y)
|
||||
else:
|
||||
cairo_ct.line_to(x, y)
|
||||
|
||||
if points_per_set:
|
||||
if filled:
|
||||
baseline_y = cell_area.height + cell_area.y
|
||||
if taper:
|
||||
x = cell_area.width + cell_area.x
|
||||
else:
|
||||
x = points[-1][0]
|
||||
cairo_ct.line_to(x, baseline_y)
|
||||
cairo_ct.line_to(0, baseline_y)
|
||||
cairo_ct.fill()
|
||||
else:
|
||||
cairo_ct.stroke()
|
||||
|
||||
|
||||
class CellRendererSparkline(gtk.CellRenderer):
|
||||
__gsignals__ = {
|
||||
}
|
||||
|
||||
__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, window, widget, backround_area, cell_area, expose_area,
|
||||
flags):
|
||||
# window : gtk.gdk.Window (not plain window)
|
||||
# widget : Parent widget (manager treeview)
|
||||
# background_area : GdkRectangle: entire cell area
|
||||
# cell_area : GdkRectangle: area normally rendered by cell
|
||||
# expose_area : GdkRectangle: area that needs updating
|
||||
# flags : flags that affect rendering
|
||||
# flags = gtk.CELL_RENDERER_SELECTED, gtk.CELL_RENDERER_PRELIT,
|
||||
# gtk.CELL_RENDERER_INSENSITIVE or gtk.CELL_RENDERER_SORTED
|
||||
def get_y(index):
|
||||
baseline_y = cell_area.y + cell_area.height
|
||||
|
||||
if self.reversed:
|
||||
n = (len(self.data_array) - index - 1)
|
||||
else:
|
||||
n = index
|
||||
|
||||
val = self.data_array[n]
|
||||
return baseline_y - (cell_area.height * val)
|
||||
|
||||
pixels_per_point = (cell_area.width / (len(self.data_array) - 1))
|
||||
|
||||
points = []
|
||||
for index in range(0, len(self.data_array)):
|
||||
x = index * pixels_per_point
|
||||
y = get_y(index)
|
||||
|
||||
points.append((int(x + cell_area.x), int(y)))
|
||||
|
||||
# Cairo stuff
|
||||
cairo_ct = window.cairo_create()
|
||||
cairo_ct.save()
|
||||
cairo_ct.rectangle(cell_area.x, cell_area.y, cell_area.width,
|
||||
cell_area.height)
|
||||
cairo_ct.clip()
|
||||
cairo_ct.set_line_width(.5)
|
||||
|
||||
_draw_sparkline(cairo_ct, cell_area, points, self.filled, len(points))
|
||||
|
||||
# Stop clipping
|
||||
cairo_ct.restore()
|
||||
del(cairo_ct)
|
||||
return
|
||||
|
||||
def do_get_size(self, widget, cell_area=None):
|
||||
xoffset = 0
|
||||
yoffset = 0
|
||||
width = len(self.data_array)
|
||||
height = 20
|
||||
|
||||
return (xoffset, yoffset, width, height)
|
||||
|
||||
def _sanitize_param_spec_name(self, name):
|
||||
# Why this is made necessary, I have no idea
|
||||
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)
|
||||
|
||||
class Sparkline(gtk.DrawingArea):
|
||||
__gsignals__ = {}
|
||||
|
||||
__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 = []
|
||||
|
||||
self.connect("expose-event", self.do_expose)
|
||||
|
||||
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_expose(self, widget, event):
|
||||
# widget : This widget
|
||||
# event : GdkEvent
|
||||
# cell_area : GdkRectangle: area normally rendered by cell
|
||||
# window : gtk.gdk.Window (not plain window)
|
||||
|
||||
# cell_area : GdkRectangle: area normally rendered by cell
|
||||
cell_area = widget.allocation
|
||||
|
||||
# window : gtk.gdk.Window (not plain window)
|
||||
window = widget.window
|
||||
|
||||
points_per_set = (len(self.data_array) / self.num_sets)
|
||||
pixels_per_point = (float(cell_area.width) /
|
||||
float((points_per_set - 1)))
|
||||
|
||||
# Mid-color graphics context (gtk.GC)
|
||||
# This draws the light gray backing rectangle
|
||||
mid_gc = widget.style.mid_gc[widget.state]
|
||||
window.draw_rectangle(mid_gc, True, 0, 0,
|
||||
cell_area.width - 1,
|
||||
cell_area.height - 1)
|
||||
|
||||
# Foreground-color graphics context
|
||||
# This draws the black border
|
||||
fg_gc = widget.style.fg_gc[widget.state]
|
||||
window.draw_rectangle(fg_gc, False, 0, 0,
|
||||
cell_area.width - 1,
|
||||
cell_area.height - 1)
|
||||
|
||||
# This draws the marker ticks
|
||||
max_ticks = 4
|
||||
dark_gc = widget.style.dark_gc[widget.state]
|
||||
for index in range(0, max_ticks):
|
||||
window.draw_line(dark_gc, 1,
|
||||
(cell_area.height / max_ticks) * index,
|
||||
cell_area.width - 2,
|
||||
(cell_area.height / max_ticks) * index)
|
||||
|
||||
# Draw the actual sparkline
|
||||
def get_y(dataset, index):
|
||||
baseline_y = cell_area.height
|
||||
|
||||
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 - ((cell_area.height - 1) * val)
|
||||
|
||||
cairo_ct = window.cairo_create()
|
||||
cairo_ct.save()
|
||||
cairo_ct.rectangle(0, 0, cell_area.width, cell_area.height)
|
||||
cairo_ct.clip()
|
||||
cairo_ct.set_line_width(.5)
|
||||
|
||||
for dataset in range(0, self.num_sets):
|
||||
if len(self.rgb) == (self.num_sets * 3):
|
||||
cairo_ct.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
|
||||
#print cell_area.width
|
||||
#print cell_area.x
|
||||
#print "\n%s\n" % points
|
||||
_draw_sparkline(cairo_ct, cell_area, points, self.filled,
|
||||
points_per_set, True)
|
||||
|
||||
# Stop clipping
|
||||
cairo_ct.restore()
|
||||
del(cairo_ct)
|
||||
return 0
|
||||
|
||||
def do_size_request(self, requisition):
|
||||
# Requisition: a GtkRequisition instance
|
||||
width = len(self.data_array) / self.num_sets
|
||||
height = 20
|
||||
|
||||
requisition.width = width
|
||||
requisition.height = height
|
||||
|
||||
def _sanitize_param_spec_name(self, name):
|
||||
# Why this is made necessary, I have no idea
|
||||
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)
|
||||
|
||||
gobject.type_register(Sparkline)
|
||||
gobject.type_register(CellRendererSparkline)
|
@ -21,7 +21,6 @@
|
||||
import gobject
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import sparkline
|
||||
import traceback
|
||||
|
||||
from virtinst import Storage
|
||||
@ -31,6 +30,7 @@ from virtManager.createnet import vmmCreateNetwork
|
||||
from virtManager.createpool import vmmCreatePool
|
||||
from virtManager.createvol import vmmCreateVolume
|
||||
from virtManager.error import vmmErrorDialog
|
||||
from virtManager.graphwidgets import Sparkline
|
||||
|
||||
class vmmHost(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
@ -123,11 +123,11 @@ class vmmHost(gobject.GObject):
|
||||
self.conn)
|
||||
|
||||
|
||||
self.cpu_usage_graph = sparkline.Sparkline()
|
||||
self.cpu_usage_graph = Sparkline()
|
||||
self.cpu_usage_graph.show()
|
||||
self.window.get_widget("performance-table").attach(self.cpu_usage_graph, 1, 2, 0, 1)
|
||||
|
||||
self.memory_usage_graph = sparkline.Sparkline()
|
||||
self.memory_usage_graph = Sparkline()
|
||||
self.memory_usage_graph.show()
|
||||
self.window.get_widget("performance-table").attach(self.memory_usage_graph, 1, 2, 1, 2)
|
||||
|
||||
|
@ -24,13 +24,13 @@ import gtk.glade
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
import sparkline
|
||||
import libvirt
|
||||
|
||||
from virtManager.connection import vmmConnection
|
||||
from virtManager.asyncjob import vmmAsyncJob
|
||||
from virtManager.error import vmmErrorDialog
|
||||
from virtManager.delete import vmmDeleteDialog
|
||||
from virtManager.graphwidgets import CellRendererSparkline
|
||||
from virtManager import util as util
|
||||
|
||||
VMLIST_SORT_ID = 1
|
||||
@ -912,7 +912,7 @@ class vmmManager(gobject.GObject):
|
||||
statusCol.set_visible(self.config.is_vmlist_status_visible())
|
||||
|
||||
cpuUsage_txt = gtk.CellRendererText()
|
||||
cpuUsage_img = sparkline.CellRendererSparkline()
|
||||
cpuUsage_img = CellRendererSparkline()
|
||||
cpuUsage_img.set_property("reversed", True)
|
||||
cpuUsageCol.pack_start(cpuUsage_txt, False)
|
||||
cpuUsageCol.pack_start(cpuUsage_img, False)
|
||||
@ -937,7 +937,7 @@ class vmmManager(gobject.GObject):
|
||||
|
||||
diskIOIn_txt = gtk.CellRendererText()
|
||||
diskIOOut_txt = gtk.CellRendererText()
|
||||
diskIO_img = sparkline.CellRendererSparkline()
|
||||
diskIO_img = CellRendererSparkline()
|
||||
diskIO_img.set_property("reversed", True)
|
||||
diskIOCol.pack_start(diskIOIn_txt, False)
|
||||
diskIOCol.pack_start(diskIOOut_txt, False)
|
||||
@ -950,7 +950,7 @@ class vmmManager(gobject.GObject):
|
||||
|
||||
networkTrafficIn_txt = gtk.CellRendererText()
|
||||
networkTrafficOut_txt = gtk.CellRendererText()
|
||||
networkTraffic_img = sparkline.CellRendererSparkline()
|
||||
networkTraffic_img = CellRendererSparkline()
|
||||
networkTraffic_img.set_property("reversed", True)
|
||||
networkTrafficCol.pack_start(networkTrafficIn_txt, False)
|
||||
networkTrafficCol.pack_start(networkTrafficOut_txt, False)
|
||||
|
Loading…
Reference in New Issue
Block a user