Commit 04572376 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

GraphWidget: Implement progressive drawing to improve interactive responsiveness

parent 406d7c4a
...@@ -28,8 +28,13 @@ ...@@ -28,8 +28,13 @@
#include "graph_properties.hh" #include "graph_properties.hh"
#include <iostream> #include <iostream>
#include <array>
#include <chrono>
#include <unordered_map> #include <unordered_map>
#ifdef HAVE_SPARSEHASH
#include SPARSEHASH_INCLUDE(dense_hash_map)
#endif
#include <cairommconfig.h> #include <cairommconfig.h>
#include <cairomm/context.h> #include <cairomm/context.h>
...@@ -42,6 +47,10 @@ using namespace std; ...@@ -42,6 +47,10 @@ using namespace std;
using namespace boost; using namespace boost;
using namespace graph_tool; using namespace graph_tool;
#ifdef HAVE_SPARSEHASH
using google::dense_hash_map;
#endif
enum vertex_attr_t { enum vertex_attr_t {
VERTEX_SHAPE = 100, VERTEX_SHAPE = 100,
VERTEX_COLOR, VERTEX_COLOR,
...@@ -118,7 +127,11 @@ enum edge_marker_t { ...@@ -118,7 +127,11 @@ enum edge_marker_t {
typedef pair<double, double> pos_t; typedef pair<double, double> pos_t;
typedef std::tuple<double, double, double, double> color_t; typedef std::tuple<double, double, double, double> color_t;
typedef std::unordered_map<int, boost::any> attrs_t; #ifdef HAVE_SPARSEHASH
typedef dense_hash_map<int, boost::any, std::hash<int>> attrs_t;
#else
typedef std::unordered_map<int, boost::any> attrs_t;
#endif
typedef boost::mpl::map41< typedef boost::mpl::map41<
boost::mpl::pair<boost::mpl::int_<VERTEX_SHAPE>, vertex_shape_t>, boost::mpl::pair<boost::mpl::int_<VERTEX_SHAPE>, vertex_shape_t>,
...@@ -411,7 +424,7 @@ public: ...@@ -411,7 +424,7 @@ public:
template <class Value> template <class Value>
Value get(int k) Value get(int k)
{ {
typeof(_attrs.begin()) iter = _attrs.find(k); auto iter = _attrs.find(k);
if (iter != _attrs.end()) if (iter != _attrs.end())
{ {
typedef DynamicPropertyMapWrap<Value, Descriptor, Converter> pmap_t; typedef DynamicPropertyMapWrap<Value, Descriptor, Converter> pmap_t;
...@@ -777,6 +790,13 @@ public: ...@@ -777,6 +790,13 @@ public:
color_t color, fillcolor; color_t color, fillcolor;
double size, pw, aspect; double size, pw, aspect;
size = get_size(cr); size = get_size(cr);
std::array<double, 4> clip;
cr.get_clip_extents(clip[0], clip[1], clip[2], clip[3]);
if ((_pos.first + 2 * size < clip[0] && _pos.second + 2 * size < clip[1]) ||
(_pos.first - 2 * size > clip[2] && _pos.second - 2 * size > clip[3]))
return;
aspect = _attrs.template get<double>(VERTEX_ASPECT); aspect = _attrs.template get<double>(VERTEX_ASPECT);
if (!outline) if (!outline)
...@@ -997,7 +1017,7 @@ public: ...@@ -997,7 +1017,7 @@ public:
EdgeShape(VertexShape& s, VertexShape& t, AttrDict<Descriptor> attrs) EdgeShape(VertexShape& s, VertexShape& t, AttrDict<Descriptor> attrs)
: _s(s), _t(t), _attrs(attrs) {} : _s(s), _t(t), _attrs(attrs) {}
void draw(Cairo::Context& cr) void draw(Cairo::Context& cr, double res = 0.)
{ {
pos_t pos_begin, pos_end; pos_t pos_begin, pos_end;
...@@ -1030,6 +1050,11 @@ public: ...@@ -1030,6 +1050,11 @@ public:
marker_size = get_user_dist(cr, marker_size); marker_size = get_user_dist(cr, marker_size);
bool sloppy = _attrs.template get<uint8_t>(EDGE_SLOPPY); bool sloppy = _attrs.template get<uint8_t>(EDGE_SLOPPY);
if (marker_size < get_user_dist(cr, res))
{
sloppy = true;
}
pos_begin = _s.get_pos(); pos_begin = _s.get_pos();
pos_end = _t.get_pos(); pos_end = _t.get_pos();
...@@ -1441,14 +1466,21 @@ private: ...@@ -1441,14 +1466,21 @@ private:
AttrDict<Descriptor> _attrs; AttrDict<Descriptor> _attrs;
}; };
template <class Graph, class VertexIterator, class PosMap> template <class Graph, class VertexIterator, class PosMap, class Time>
void draw_vertices(Graph&, pair<VertexIterator,VertexIterator> v_range, void draw_vertices(Graph&, pair<VertexIterator, VertexIterator> v_range,
PosMap pos_map, attrs_t& attrs, attrs_t& defaults, PosMap pos_map, attrs_t& attrs, attrs_t& defaults,
size_t offset, Time max_time, size_t& count,
Cairo::Context& cr) Cairo::Context& cr)
{ {
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t; typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
for(VertexIterator v = v_range.first; v != v_range.second; ++v) for(VertexIterator v = v_range.first; v != v_range.second; ++v)
{ {
if (count < offset)
{
count++;
continue;
}
pos_t pos; pos_t pos;
if (pos_map[*v].size() >= 2) if (pos_map[*v].size() >= 2)
{ {
...@@ -1457,18 +1489,29 @@ void draw_vertices(Graph&, pair<VertexIterator,VertexIterator> v_range, ...@@ -1457,18 +1489,29 @@ void draw_vertices(Graph&, pair<VertexIterator,VertexIterator> v_range,
} }
VertexShape<vertex_t> vs(pos, AttrDict<vertex_t>(*v, attrs, defaults)); VertexShape<vertex_t> vs(pos, AttrDict<vertex_t>(*v, attrs, defaults));
vs.draw(cr); vs.draw(cr);
count++;
if (chrono::high_resolution_clock::now() > max_time)
break;
} }
} }
template <class Graph, class EdgeIterator, class PosMap> template <class Graph, class EdgeIterator, class PosMap, class Time>
void draw_edges(Graph& g, pair<EdgeIterator, EdgeIterator> e_range, void draw_edges(Graph& g, pair<EdgeIterator, EdgeIterator> e_range,
PosMap pos_map, attrs_t& eattrs, attrs_t& edefaults, PosMap pos_map, attrs_t& eattrs, attrs_t& edefaults,
attrs_t& vattrs, attrs_t& vdefaults, Cairo::Context& cr) attrs_t& vattrs, attrs_t& vdefaults, double res, size_t offset,
Time max_time, size_t& count, Cairo::Context& cr)
{ {
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t; typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
typedef typename graph_traits<Graph>::edge_descriptor edge_t; typedef typename graph_traits<Graph>::edge_descriptor edge_t;
for(EdgeIterator e = e_range.first; e != e_range.second; ++e) for(EdgeIterator e = e_range.first; e != e_range.second; ++e)
{ {
if (count < offset)
{
count++;
continue;
}
vertex_t s, t; vertex_t s, t;
s = source(*e, g); s = source(*e, g);
t = target(*e, g); t = target(*e, g);
...@@ -1486,7 +1529,10 @@ void draw_edges(Graph& g, pair<EdgeIterator, EdgeIterator> e_range, ...@@ -1486,7 +1529,10 @@ void draw_edges(Graph& g, pair<EdgeIterator, EdgeIterator> e_range,
} }
if (spos == tpos && t != s) if (spos == tpos && t != s)
{
count++;
continue; continue;
}
VertexShape<vertex_t> ss(spos, AttrDict<vertex_t>(s, vattrs, vdefaults)); VertexShape<vertex_t> ss(spos, AttrDict<vertex_t>(s, vattrs, vdefaults));
VertexShape<vertex_t> ts(tpos, AttrDict<vertex_t>(t, vattrs, vdefaults)); VertexShape<vertex_t> ts(tpos, AttrDict<vertex_t>(t, vattrs, vdefaults));
...@@ -1494,7 +1540,11 @@ void draw_edges(Graph& g, pair<EdgeIterator, EdgeIterator> e_range, ...@@ -1494,7 +1540,11 @@ void draw_edges(Graph& g, pair<EdgeIterator, EdgeIterator> e_range,
EdgeShape<edge_t,VertexShape<vertex_t> > es(ss, ts, EdgeShape<edge_t,VertexShape<vertex_t> > es(ss, ts,
AttrDict<edge_t>(*e, eattrs, AttrDict<edge_t>(*e, eattrs,
edefaults)); edefaults));
es.draw(cr); es.draw(cr, res);
count++;
if (chrono::high_resolution_clock::now() > max_time)
break;
} }
} }
...@@ -1546,29 +1596,31 @@ struct ordered_range ...@@ -1546,29 +1596,31 @@ struct ordered_range
struct do_cairo_draw_edges struct do_cairo_draw_edges
{ {
template <class Graph, class PosMap, class EdgeOrder> template <class Graph, class PosMap, class EdgeOrder, class Time>
void operator()(Graph& g, PosMap pos, EdgeOrder edge_order, void operator()(Graph& g, PosMap pos, EdgeOrder edge_order, attrs_t& vattrs,
attrs_t& vattrs, attrs_t& eattrs, attrs_t& vdefaults, attrs_t& eattrs, attrs_t& vdefaults, attrs_t& edefaults,
attrs_t& edefaults, Cairo::Context& cr) const double res, size_t offset, Time max_time, size_t& count,
Cairo::Context& cr) const
{ {
ordered_range<typename graph_traits<Graph>::edge_iterator> ordered_range<typename graph_traits<Graph>::edge_iterator>
edge_range(edges(g)); edge_range(edges(g));
draw_edges(g, edge_range.get_range(edge_order), pos, eattrs, draw_edges(g, edge_range.get_range(edge_order), pos, eattrs, edefaults,
edefaults, vattrs, vdefaults, cr); vattrs, vdefaults, res, offset, max_time, count, cr);
} }
}; };
struct do_cairo_draw_vertices struct do_cairo_draw_vertices
{ {
template <class Graph, class PosMap, class VertexOrder> template <class Graph, class PosMap, class VertexOrder, class Time>
void operator()(Graph& g, PosMap pos, VertexOrder vertex_order, void operator()(Graph& g, PosMap pos, VertexOrder vertex_order,
attrs_t& vattrs, attrs_t&, attrs_t& vdefaults, attrs_t& vattrs, attrs_t&, attrs_t& vdefaults, attrs_t&,
attrs_t&, Cairo::Context& cr) const size_t offset, Time max_time, size_t& count,
Cairo::Context& cr) const
{ {
ordered_range<typename graph_traits<Graph>::vertex_iterator> ordered_range<typename graph_traits<Graph>::vertex_iterator>
vertex_range(vertices(g)); vertex_range(vertices(g));
draw_vertices(g, vertex_range.get_range(vertex_order), pos, vattrs, draw_vertices(g, vertex_range.get_range(vertex_order), pos, vattrs,
vdefaults, cr); vdefaults, offset, max_time, count, cr);
} }
}; };
...@@ -1655,18 +1707,28 @@ struct populate_edge_attrs ...@@ -1655,18 +1707,28 @@ struct populate_edge_attrs
}; };
void cairo_draw(GraphInterface& gi, size_t cairo_draw(GraphInterface& gi,
boost::any pos, boost::any pos,
boost::any vorder, boost::any vorder,
boost::any eorder, boost::any eorder,
bool nodesfirst, bool nodesfirst,
boost::python::dict ovattrs, boost::python::dict ovattrs,
boost::python::dict oeattrs, boost::python::dict oeattrs,
boost::python::dict ovdefaults, boost::python::dict ovdefaults,
boost::python::dict oedefaults, boost::python::dict oedefaults,
boost::python::object ocr) double res,
size_t offset,
int64_t max_time,
boost::python::object ocr)
{ {
attrs_t vattrs, eattrs, vdefaults, edefaults; attrs_t vattrs, eattrs, vdefaults, edefaults;
#ifdef HAVE_SPARSEHASH
vattrs.set_empty_key(numeric_limits<int>::max());
eattrs.set_empty_key(numeric_limits<int>::max());
vdefaults.set_empty_key(numeric_limits<int>::max());
edefaults.set_empty_key(numeric_limits<int>::max());
#endif
typedef graph_traits<GraphInterface::multigraph_t>::vertex_descriptor vertex_t; typedef graph_traits<GraphInterface::multigraph_t>::vertex_descriptor vertex_t;
populate_attrs<vertex_t, vertex_properties>(ovattrs, vattrs); populate_attrs<vertex_t, vertex_properties>(ovattrs, vattrs);
...@@ -1685,31 +1747,55 @@ void cairo_draw(GraphInterface& gi, ...@@ -1685,31 +1747,55 @@ void cairo_draw(GraphInterface& gi,
if (eorder.empty()) if (eorder.empty())
eorder = no_order(); eorder = no_order();
size_t count = 0;
auto mtime = chrono::high_resolution_clock::now();
if (max_time < 0)
mtime = chrono::high_resolution_clock::time_point::max();
else
mtime += chrono::milliseconds(max_time);
Cairo::Context cr(PycairoContext_GET(ocr.ptr())); Cairo::Context cr(PycairoContext_GET(ocr.ptr()));
if (nodesfirst) if (nodesfirst)
run_action<graph_tool::detail::always_directed>() run_action<graph_tool::detail::always_directed>()
(gi, std::bind(do_cairo_draw_vertices(), placeholders::_1, (gi, std::bind(do_cairo_draw_vertices(), placeholders::_1,
placeholders::_2, placeholders::_3, placeholders::_2, placeholders::_3,
std::ref(vattrs), std::ref(eattrs), std::ref(vdefaults), std::ref(vattrs), std::ref(eattrs), std::ref(vdefaults),
std::ref(edefaults), std::ref(cr)), std::ref(edefaults), offset, mtime, std::ref(count),
std::ref(cr)),
vertex_scalar_vector_properties(), vertex_scalar_vector_properties(),
vorder_t())(pos, vorder); vorder_t())(pos, vorder);
if (chrono::high_resolution_clock::now() > mtime)
return count;
run_action<graph_tool::detail::always_directed>() run_action<graph_tool::detail::always_directed>()
(gi, std::bind(do_cairo_draw_edges(), placeholders::_1, placeholders::_2, (gi, std::bind(do_cairo_draw_edges(), placeholders::_1, placeholders::_2,
placeholders::_3, std::ref(vattrs), std::ref(eattrs), placeholders::_3, std::ref(vattrs), std::ref(eattrs),
std::ref(vdefaults), std::ref(edefaults), std::ref(cr)), std::ref(vdefaults), std::ref(edefaults), res, offset,
mtime, std::ref(count), std::ref(cr)),
vertex_scalar_vector_properties(), vertex_scalar_vector_properties(),
eorder_t())(pos, eorder); eorder_t())(pos, eorder);
if (!nodesfirst) if (!nodesfirst)
{
if (chrono::high_resolution_clock::now() > mtime)
return count;
run_action<graph_tool::detail::always_directed>() run_action<graph_tool::detail::always_directed>()
(gi, std::bind(do_cairo_draw_vertices(), placeholders::_1, (gi, std::bind(do_cairo_draw_vertices(), placeholders::_1,
placeholders::_2, placeholders::_3, placeholders::_2, placeholders::_3,
std::ref(vattrs), std::ref(eattrs), std::ref(vdefaults), std::ref(vattrs), std::ref(eattrs), std::ref(vdefaults),
std::ref(edefaults), std::ref(cr)), std::ref(edefaults), offset, mtime, std::ref(count),
std::ref(cr)),
vertex_scalar_vector_properties(), vertex_scalar_vector_properties(),
vorder_t())(pos, vorder); vorder_t())(pos, vorder);
}
if (chrono::high_resolution_clock::now() < mtime)
count = 0;
return count;
} }
struct do_apply_transforms struct do_apply_transforms
......
...@@ -427,9 +427,9 @@ def parse_props(prefix, args): ...@@ -427,9 +427,9 @@ def parse_props(prefix, args):
def cairo_draw(g, pos, cr, vprops=None, eprops=None, vorder=None, eorder=None, def cairo_draw(g, pos, cr, vprops=None, eprops=None, vorder=None, eorder=None,
nodesfirst=False, vcmap=default_cm, nodesfirst=False, vcmap=default_cm, ecmap=default_cm,
ecmap=default_cm, loop_angle=float("nan"), loop_angle=float("nan"), parallel_distance=None, fit_view=False,
parallel_distance=None, fit_view=False, **kwargs): res=0, render_offset=0, max_render_time=-1, **kwargs):
r""" r"""
Draw a graph to a :mod:`cairo` context. Draw a graph to a :mod:`cairo` context.
...@@ -474,6 +474,15 @@ def cairo_draw(g, pos, cr, vprops=None, eprops=None, vorder=None, eorder=None, ...@@ -474,6 +474,15 @@ def cairo_draw(g, pos, cr, vprops=None, eprops=None, vorder=None, eorder=None,
specify the view in user coordinates. specify the view in user coordinates.
bg_color : str or sequence (optional, default: ``None``) bg_color : str or sequence (optional, default: ``None``)
Background color. The default is transparent. Background color. The default is transparent.
res : float (optional, default: ``0.``):
If shape sizes fall below this value, simplified drawing is used.
render_offset : int (optional, default: ``0``):
If supplied, the rendering will skip the specified initial amount of
vertices / edges.
max_render_time : int (optional, default: ``-1``):
Maximum allowed time (in milliseconds) for rendering. If exceeded, the
rendering will return unfinished. If negative values are given, the
rendering will always complete.
vertex_* : :class:`~graph_tool.PropertyMap` or arbitrary types (optional, default: ``None``) vertex_* : :class:`~graph_tool.PropertyMap` or arbitrary types (optional, default: ``None``)
Parameters following the pattern ``vertex_<prop-name>`` specify the Parameters following the pattern ``vertex_<prop-name>`` specify the
vertex property with name ``<prop-name>``, as an alternative to the vertex property with name ``<prop-name>``, as an alternative to the
...@@ -483,6 +492,11 @@ def cairo_draw(g, pos, cr, vprops=None, eprops=None, vorder=None, eorder=None, ...@@ -483,6 +492,11 @@ def cairo_draw(g, pos, cr, vprops=None, eprops=None, vorder=None, eorder=None,
property with name ``<prop-name>``, as an alternative to the ``eprops`` property with name ``<prop-name>``, as an alternative to the ``eprops``
parameter. parameter.
Returns
-------
offset : int
The offset into the completed rendering. If this value is zero, the
rendering was complete.
""" """
vprops = {} if vprops is None else copy.copy(vprops) vprops = {} if vprops is None else copy.copy(vprops)
...@@ -551,11 +565,12 @@ def cairo_draw(g, pos, cr, vprops=None, eprops=None, vorder=None, eorder=None, ...@@ -551,11 +565,12 @@ def cairo_draw(g, pos, cr, vprops=None, eprops=None, vorder=None, eorder=None,
eprops["control_points"] = position_parallel_edges(g, pos, loop_angle, eprops["control_points"] = position_parallel_edges(g, pos, loop_angle,
parallel_distance) parallel_distance)
g = GraphView(g, directed=True) g = GraphView(g, directed=True)
libgraph_tool_draw.cairo_draw(g._Graph__graph, _prop("v", g, pos), count = libgraph_tool_draw.cairo_draw(g._Graph__graph, _prop("v", g, pos),
_prop("v", g, vorder), _prop("e", g, eorder), _prop("v", g, vorder), _prop("e", g, eorder),
nodesfirst, vattrs, eattrs, vdefs, edefs, cr) nodesfirst, vattrs, eattrs, vdefs, edefs, res,
render_offset, max_render_time, cr)
cr.restore() cr.restore()
return count
def color_contrast(color): def color_contrast(color):
c = np.asarray(color) c = np.asarray(color)
......
...@@ -131,7 +131,8 @@ class GraphWidget(Gtk.DrawingArea): ...@@ -131,7 +131,8 @@ class GraphWidget(Gtk.DrawingArea):
eorder=None, nodesfirst=False, update_layout=False, eorder=None, nodesfirst=False, update_layout=False,
layout_K=1., multilevel=False, display_props=None, layout_K=1., multilevel=False, display_props=None,
display_props_size=11, fit_area=0.95, bg_color=None, display_props_size=11, fit_area=0.95, bg_color=None,
layout_callback=None, key_press_callback=None, **kwargs): max_render_time=300, layout_callback=None,
key_press_callback=None, **kwargs):
r"""Interactive GTK+ widget displaying a given graph. r"""Interactive GTK+ widget displaying a given graph.
Parameters Parameters
...@@ -169,6 +170,9 @@ class GraphWidget(Gtk.DrawingArea): ...@@ -169,6 +170,9 @@ class GraphWidget(Gtk.DrawingArea):
Fraction of the drawing area to fit the graph initially. Fraction of the drawing area to fit the graph initially.
bg_color : str or sequence (optional, default: ``None``) bg_color : str or sequence (optional, default: ``None``)
Background color. The default is white. Background color. The default is white.
max_render_time : int (optional, default: ``300``)
Maximum amount of time (in milliseconds) spent rendering portions of
the graph.
layout_callback : function (optional, default: ``Node``) layout_callback : function (optional, default: ``Node``)
User-supplied callback to be called whenever the positions of the layout User-supplied callback to be called whenever the positions of the layout
have changed. It needs to have the following signature: have changed. It needs to have the following signature:
...@@ -260,6 +264,7 @@ class GraphWidget(Gtk.DrawingArea): ...@@ -260,6 +264,7 @@ class GraphWidget(Gtk.DrawingArea):
self.pointer = [0, 0] self.pointer = [0, 0]
self.picked = False self.picked = False
self.selected = g.new_vertex_property("bool") self.selected = g.new_vertex_property("bool")
self.sel_edge_filt = g.new_edge_property("bool", False)
self.srect = None self.srect = None
self.drag_begin = None self.drag_begin = None
self.moved_picked = False self.moved_picked = False
...@@ -275,7 +280,9 @@ class GraphWidget(Gtk.DrawingArea): ...@@ -275,7 +280,9 @@ class GraphWidget(Gtk.DrawingArea):
self.base_geometry = None self.base_geometry = None
self.background = None self.background = None
self.bg_color = bg_color if bg_color is not None else [1, 1, 1, 1] self.bg_color = bg_color if bg_color is not None else [1, 1, 1, 1]
self.surface_callback = None self.regenerate_offset = 0
self.regenerate_max_time = max_render_time
self.max_render_time = max_render_time
self.layout_callback_id = None self.layout_callback_id = None
self.layout_K = layout_K self.layout_K = layout_K
...@@ -359,7 +366,7 @@ class GraphWidget(Gtk.DrawingArea): ...@@ -359,7 +366,7 @@ class GraphWidget(Gtk.DrawingArea):
self.layout_step *= 0.9 self.layout_step *= 0.9
if self.vertex_matrix is not None: if self.vertex_matrix is not None:
self.vertex_matrix.update() self.vertex_matrix.update()
self.regenerate_surface(lazy=False) self.regenerate_surface(reset=True, complete=True)
self.queue_draw() self.queue_draw()
ps = ungroup_vector_property(self.pos, [0, 1]) ps = ungroup_vector_property(self.pos, [0, 1])
delta = np.sqrt((pos_temp[0].fa - ps[0].fa) ** 2 + delta = np.sqrt((pos_temp[0].fa - ps[0].fa) ** 2 +
...@@ -390,7 +397,7 @@ class GraphWidget(Gtk.DrawingArea): ...@@ -390,7 +397,7 @@ class GraphWidget(Gtk.DrawingArea):
adjust_default_sizes(self.g, geometry, self.vprops, adjust_default_sizes(self.g, geometry, self.vprops,
self.eprops, force=True) self.eprops, force=True)
self.fit_to_window(ink=False) self.fit_to_window(ink=False)
self.regenerate_surface(lazy=False) self.regenerate_surface(reset=True, complete=True)
except StopIteration: except StopIteration:
self.g = self.ag self.g = self.ag
self.pos = self.apos self.pos = self.apos
...@@ -410,48 +417,48 @@ class GraphWidget(Gtk.DrawingArea): ...@@ -410,48 +417,48 @@ class GraphWidget(Gtk.DrawingArea):
# Actual drawing # Actual drawing
def regenerate_surface(self, lazy=True, timeout=350): def regenerate_surface(self, reset=False, complete=False):
r"""Redraw the graph surface. If lazy is True, the actual redrawing will r"""Redraw the graph surface."""
be performed after the specified timeout."""
if lazy: if reset:
if self.surface_callback is not None: self.regenerate_offset = 0
gobject.source_remove(self.surface_callback)
f = lambda: self.regenerate_surface(lazy=False) geometry = [self.get_allocated_width() * 3,
self.surface_callback = gobject.timeout_add(timeout, f) self.get_allocated_height() * 3]
else:
geometry = [self.get_allocated_width() * 3, if (self.base is None or self.base_geometry[0] != geometry[0] or
self.get_allocated_height() * 3] self.base_geometry[1] != geometry[1] or reset):
# self.base = cairo.ImageSurface(cairo.FORMAT_ARGB32,
# *geometry)
w = self.get_window()
if w is None: