From d982d13c99f7c4119a9ceea1749a54cca7e53d38 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 22 Apr 2009 10:20:20 +0200 Subject: Tweaks to grapher axis drawing * grapher/GraphWidget.cxx (on_expose_event): Don't draw axis labels that would overlap others. --- grapher/GraphWidget.cxx | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 38f8078d..34b4daf4 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -210,7 +210,7 @@ namespace systemtap // Draw axes double diff = _right - _left; double majorUnit = pow(10.0, floor(log(diff) / log(10.0))); - double startTime = floor(_left / majorUnit) * majorUnit; + double startTime = ceil(_left / majorUnit) * majorUnit; cr->save(); cr->set_source_rgba(1.0, 1.0, 1.0, .9); cr->set_line_cap(Cairo::LINE_CAP_BUTT); @@ -226,15 +226,23 @@ namespace systemtap std::valarray dash(1); dash[0] = height / 10; cr->set_dash(dash, 0.0); - for (double tickVal = startTime; tickVal < _right; tickVal += majorUnit) + double prevTextAdvance = 0; + for (double tickVal = startTime; tickVal <= _right; tickVal += majorUnit) { - cr->move_to((tickVal - _left) * horizScale + 20.0, graphHeight - 5); + double x = (tickVal - _left) * horizScale + 20.0; + cr->move_to(x, 0.0); + cr->line_to(x, height); + cr->move_to(x, graphHeight - 5); std::ostringstream stream; stream << std::fixed << std::setprecision(0) << tickVal; - cr->show_text(stream.str()); - cr->move_to((tickVal - _left) * horizScale + 20.0, 0.0); - cr->line_to((tickVal - _left) * horizScale + 20.0, height); - cr->stroke(); + Cairo::TextExtents extents; + cr->get_text_extents(stream.str(), extents); + // Room for this label? + if (x + extents.x_bearing > prevTextAdvance) + { + cr->show_text(stream.str()); + prevTextAdvance = x + extents.x_advance; + } } cr->stroke(); cr->restore(); -- cgit From 5f4f8b1129659adb2015ce42821faccf8fe6d8d5 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 27 May 2009 10:32:51 +0200 Subject: Templatize GraphData * grapher/GraphData.hxx (GraphDataBase): new superclass for GraphData. Split time data out as a separate vector. (GraphData): Rewrite as template. * grapher/GraphWidget.cxx (on_expose_event): Reflect GraphData templatization. Handle events with string values. * grapher/GraphWidget.hxx (GraphWidget): Keep pointers to GraphDataBase objects instead of GraphData. * grapher/StapParser.cxx (parseData): new member function (ioCallback): Handle new discreet event * grapher/StapParser.hxx (StapParser): keep pointers to GraphDataBase objects instead of GraphData * testsuite/systemtap.examples/general/grapher.stp: Display actual key pressed for keyboard event --- grapher/GraphWidget.cxx | 85 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 23 deletions(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 34b4daf4..d62ec4f3 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "GraphWidget.hxx" @@ -9,6 +10,8 @@ namespace systemtap { + using std::string; + GraphWidget::GraphWidget() : _left(0.0), _right(1.0), _top(1.0), _bottom(0.0), _lineWidth(10), _autoScaling(true), _autoScrolling(true), _zoomFactor(1.0), @@ -55,7 +58,7 @@ namespace systemtap { } - void GraphWidget::addGraphData(std::tr1::shared_ptr data) + void GraphWidget::addGraphData(std::tr1::shared_ptr data) { _datasets.push_back(data); } @@ -94,9 +97,9 @@ namespace systemtap ditr != de; ++ditr) { - if (!(*ditr)->data.empty()) + if (!(*ditr)->times.empty()) { - double lastDataTime = (*ditr)->data.back().first; + double lastDataTime = (*ditr)->times.back(); if (lastDataTime > latestTime) latestTime = lastDataTime; } @@ -108,16 +111,16 @@ namespace systemtap ditr != de; ++ditr) { - GraphData::List& gdata = (*ditr)->data; - if (gdata.size() <= 1) + GraphDataBase::TimeList& gtimes = (*ditr)->times; + if (gtimes.size() <= 1) continue; double totalDiff = 0.0; - for (GraphData::List::reverse_iterator ritr = gdata.rbegin(), - re = gdata.rend(); - ritr + 1 != gdata.rend(); + for (GraphDataBase::TimeList::reverse_iterator ritr = gtimes.rbegin(), + re = gtimes.rend(); + ritr + 1 != gtimes.rend(); ritr++) { - double timeDiff = ritr->first - (ritr + 1)->first; + double timeDiff = *ritr - *(ritr + 1); if (timeDiff < minDiff || (timeDiff != 0 && minDiff == 0)) minDiff = timeDiff; if (minDiff != 0 @@ -148,35 +151,71 @@ namespace systemtap itr != e; ++itr) { + std::tr1::shared_ptr > realData + = std::tr1::dynamic_pointer_cast >(*itr); + std::tr1::shared_ptr > stringData + = std::tr1::dynamic_pointer_cast >(*itr); cr->save(); cr->translate(0.0, height); cr->scale(1.0, -1.0); - GraphData::List::iterator lower - = std::lower_bound((*itr)->data.begin(), (*itr)->data.end(), _left, - GraphData::Compare()); - GraphData::List::iterator upper - = std::upper_bound((*itr)->data.begin(), (*itr)->data.end(), _right, - GraphData::Compare()); - for (GraphData::List::iterator ditr = lower, de = upper; + GraphDataBase::TimeList::iterator lower + = std::lower_bound((*itr)->times.begin(), (*itr)->times.end(), _left); + GraphDataBase::TimeList::iterator upper + = std::upper_bound((*itr)->times.begin(), (*itr)->times.end(), + _right); + // event bar + if ((*itr)->style == GraphDataBase::EVENT) + { + double eventHeight = height * ((*itr)->scale / 100.0); + cr->save(); + cr->set_line_width(3 * _lineWidth); + cr->set_source_rgba((*itr)->color[0], (*itr)->color[1], + (*itr)->color[2], .33); + cr->move_to(0, eventHeight); + cr->line_to(width, eventHeight); + cr->stroke(); + cr->restore(); + } + for (GraphDataBase::TimeList::iterator ditr = lower, de = upper; ditr != de; ++ditr) { + size_t dataIndex = ditr - (*itr)->times.begin(); cr->set_source_rgba((*itr)->color[0], (*itr)->color[1], (*itr)->color[2], 1.0); - if ((*itr)->style == GraphData::BAR) + if ((*itr)->style == GraphDataBase::BAR && realData) { - cr->move_to((ditr->first - _left) * horizScale, 0); - cr->line_to((ditr->first - _left) * horizScale, - ditr->second * height / (*itr)->scale); + cr->move_to((*ditr - _left) * horizScale, 0); + cr->line_to((*ditr - _left) * horizScale, + realData->data[dataIndex] * height / (*itr)->scale); cr->stroke(); } - else + else if ((*itr)->style == GraphDataBase::DOT && realData) { - cr->arc((ditr->first - _left) * horizScale, - ditr->second * height / (*itr)->scale, + cr->arc((*ditr - _left) * horizScale, + realData->data[dataIndex] * height / (*itr)->scale, _lineWidth / 2.0, 0.0, M_PI * 2.0); cr->fill(); } + else if ((*itr)->style == GraphDataBase::EVENT && stringData) + { + double eventHeight = height * ((*itr)->scale / 100.0); + cr->save(); + cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, + Cairo::FONT_WEIGHT_NORMAL); + cr->set_font_size(12.0); + cr->save(); + cr->scale(1.0, -1.0); + cr->move_to((*ditr - _left) * horizScale, + -eventHeight -3.0 * _lineWidth - 2.0); + cr->show_text(stringData->data[dataIndex]); + cr->restore(); + cr->rectangle((*ditr - _left) * horizScale - 1.5 * _lineWidth, + eventHeight - 1.5 * _lineWidth, + 3.0 * _lineWidth, 3.0 * _lineWidth); + cr->fill(); + cr->restore(); + } } cr->restore(); } -- cgit From 364ad890e341bb60ae169af69933a382d4bf9f81 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 27 Jul 2009 12:46:30 +0200 Subject: Support for presenting multiple graphs * grapher/Graph.hxx: New file; class for single graph display. * grapher/Graph.cxx: New file. * grapher/GraphData.hxx: Associate title and axis labels with graph data and not a graph display. * grapher/GraphWidget.hxx: Move graph-related members to Graph class. * grapher/GraphWidget.cxx (getExtents, setExtents): Move to Graph class (on_expose_event): Move graph rendering to Graph. (on_button_press_event): Delegate to Graph. (on_motion_notify_event, on_scroll_event): Modify "active" graph. * grapher/StapParser.cxx (findTaggedValue): New parsing helper function. (io_callback): Support new syntax where properties are attached to graph data and not the entire graph. * grapher/grapher.cxx (GrapherWindow): Don't set graph values. * grapher/Makefile.am: Add Graph.cxx. * testsuite/systemtap.examples/general/grapher.stp: New property syntax. --- grapher/GraphWidget.cxx | 294 +++++++++--------------------------------------- 1 file changed, 53 insertions(+), 241 deletions(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index d62ec4f3..5b0d1b1c 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -1,21 +1,18 @@ #include #include #include -#include -#include -#include + #include #include "GraphWidget.hxx" #include "CairoWidget.hxx" namespace systemtap { - using std::string; + using namespace std; + using namespace std::tr1; GraphWidget::GraphWidget() - : _left(0.0), _right(1.0), _top(1.0), _bottom(0.0), _lineWidth(10), - _autoScaling(true), _autoScrolling(true), _zoomFactor(1.0), - _trackingDrag(false), _playButton(new CairoPlayButton) + : _trackingDrag(false) { add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK); @@ -34,33 +31,20 @@ namespace systemtap false); signal_scroll_event() .connect(sigc::mem_fun(*this, &GraphWidget::on_scroll_event), false); + // Temporary testing of multiple graphs + shared_ptr graph(new Graph); + graph->_graphHeight = 180; + graph->_graphWidth = 580; + _graphs.push_back(graph); } - void GraphWidget::getExtents(double& left, double& right, double& top, - double& bottom) const - { - left = _left; - right = _right; - top = _top; - bottom = _bottom; - } - - void GraphWidget::setExtents(double left, double right, double top, - double bottom) - { - _left = left; - _right = right; - _top = top; - _bottom = bottom; - - } GraphWidget::~GraphWidget() { } void GraphWidget::addGraphData(std::tr1::shared_ptr data) { - _datasets.push_back(data); + _graphs[0]->addGraphData(data); } bool GraphWidget::on_expose_event(GdkEventExpose* event) @@ -78,6 +62,7 @@ namespace systemtap const int height = graphHeight - 20; Cairo::RefPtr cr = window->create_cairo_context(); +#if 0 if(event && !_autoScaling) { // clip to the area indicated by the expose event so that we only @@ -86,233 +71,51 @@ namespace systemtap event->area.width, event->area.height); cr->clip(); } - if (_autoScaling) - { - // line separation - int linesPossible = width / ((int)_lineWidth + 2); - // Find latest time. - double latestTime = 0; - for (DatasetList::iterator ditr = _datasets.begin(), - de = _datasets.end(); - ditr != de; - ++ditr) - { - if (!(*ditr)->times.empty()) - { - double lastDataTime = (*ditr)->times.back(); - if (lastDataTime > latestTime) - latestTime = lastDataTime; - } - } - double minDiff = 0.0; - double maxTotal = 0.0; - for (DatasetList::iterator ditr = _datasets.begin(), - de = _datasets.end(); - ditr != de; - ++ditr) - { - GraphDataBase::TimeList& gtimes = (*ditr)->times; - if (gtimes.size() <= 1) - continue; - double totalDiff = 0.0; - for (GraphDataBase::TimeList::reverse_iterator ritr = gtimes.rbegin(), - re = gtimes.rend(); - ritr + 1 != gtimes.rend(); - ritr++) - { - double timeDiff = *ritr - *(ritr + 1); - if (timeDiff < minDiff || (timeDiff != 0 && minDiff == 0)) - minDiff = timeDiff; - if (minDiff != 0 - && (totalDiff + timeDiff) / minDiff > linesPossible) - break; - totalDiff += timeDiff; - } - if (totalDiff > maxTotal) - maxTotal = totalDiff; - } - // Now we have a global scale. - _right = latestTime; - if (maxTotal != 0) - _left = latestTime - maxTotal; - else - _left = _right - 1.0; - } - cr->save(); - double horizScale = _zoomFactor * width / ( _right - _left); - cr->translate(20.0, 0.0); - cr->set_line_width(_lineWidth); +#endif cr->save(); cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); cr->paint(); - cr->restore(); - - for (DatasetList::iterator itr = _datasets.begin(), e = _datasets.end(); - itr != e; - ++itr) - { - std::tr1::shared_ptr > realData - = std::tr1::dynamic_pointer_cast >(*itr); - std::tr1::shared_ptr > stringData - = std::tr1::dynamic_pointer_cast >(*itr); - cr->save(); - cr->translate(0.0, height); - cr->scale(1.0, -1.0); - GraphDataBase::TimeList::iterator lower - = std::lower_bound((*itr)->times.begin(), (*itr)->times.end(), _left); - GraphDataBase::TimeList::iterator upper - = std::upper_bound((*itr)->times.begin(), (*itr)->times.end(), - _right); - // event bar - if ((*itr)->style == GraphDataBase::EVENT) - { - double eventHeight = height * ((*itr)->scale / 100.0); - cr->save(); - cr->set_line_width(3 * _lineWidth); - cr->set_source_rgba((*itr)->color[0], (*itr)->color[1], - (*itr)->color[2], .33); - cr->move_to(0, eventHeight); - cr->line_to(width, eventHeight); - cr->stroke(); - cr->restore(); - } - for (GraphDataBase::TimeList::iterator ditr = lower, de = upper; - ditr != de; - ++ditr) - { - size_t dataIndex = ditr - (*itr)->times.begin(); - cr->set_source_rgba((*itr)->color[0], (*itr)->color[1], - (*itr)->color[2], 1.0); - if ((*itr)->style == GraphDataBase::BAR && realData) - { - cr->move_to((*ditr - _left) * horizScale, 0); - cr->line_to((*ditr - _left) * horizScale, - realData->data[dataIndex] * height / (*itr)->scale); - cr->stroke(); - } - else if ((*itr)->style == GraphDataBase::DOT && realData) - { - cr->arc((*ditr - _left) * horizScale, - realData->data[dataIndex] * height / (*itr)->scale, - _lineWidth / 2.0, 0.0, M_PI * 2.0); - cr->fill(); - } - else if ((*itr)->style == GraphDataBase::EVENT && stringData) - { - double eventHeight = height * ((*itr)->scale / 100.0); - cr->save(); - cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, - Cairo::FONT_WEIGHT_NORMAL); - cr->set_font_size(12.0); - cr->save(); - cr->scale(1.0, -1.0); - cr->move_to((*ditr - _left) * horizScale, - -eventHeight -3.0 * _lineWidth - 2.0); - cr->show_text(stringData->data[dataIndex]); - cr->restore(); - cr->rectangle((*ditr - _left) * horizScale - 1.5 * _lineWidth, - eventHeight - 1.5 * _lineWidth, - 3.0 * _lineWidth, 3.0 * _lineWidth); - cr->fill(); - cr->restore(); - } - } - cr->restore(); - } - cr->restore(); - cr->save(); - cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, - Cairo::FONT_WEIGHT_BOLD); - cr->set_font_size(14.0); - cr->set_source_rgba(1.0, 1.0, 1.0, .8); - - if (!_title.empty()) - { - cr->move_to(20.0, 20.0); - cr->show_text(_title); - } - if (!_xAxisText.empty()) - { - cr->move_to(10.0, graphHeight - 5); - cr->show_text(_xAxisText); - } - if (!_yAxisText.empty()) + for (GraphList::iterator g = _graphs.begin(); g != _graphs.end(); ++g) { cr->save(); - cr->translate(10.0, height - 10.0); - cr->rotate(-M_PI / 2.0); - cr->move_to(10.0, 0.0); - cr->show_text(_yAxisText); + cr->translate((*g)->_graphX, (*g)->_graphY); + (*g)->draw(cr); cr->restore(); } - cr->restore(); - // Draw axes - double diff = _right - _left; - double majorUnit = pow(10.0, floor(log(diff) / log(10.0))); - double startTime = ceil(_left / majorUnit) * majorUnit; - cr->save(); - cr->set_source_rgba(1.0, 1.0, 1.0, .9); - cr->set_line_cap(Cairo::LINE_CAP_BUTT); - cr->set_line_width(_lineWidth); - cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, - Cairo::FONT_WEIGHT_NORMAL); - cr->set_font_size(10.0); - cr->move_to(20.0, 0.0); - cr->line_to(20.0, height); - cr->move_to(20.0, height); - cr->line_to(graphWidth, height); - cr->stroke(); - std::valarray dash(1); - dash[0] = height / 10; - cr->set_dash(dash, 0.0); - double prevTextAdvance = 0; - for (double tickVal = startTime; tickVal <= _right; tickVal += majorUnit) - { - double x = (tickVal - _left) * horizScale + 20.0; - cr->move_to(x, 0.0); - cr->line_to(x, height); - cr->move_to(x, graphHeight - 5); - std::ostringstream stream; - stream << std::fixed << std::setprecision(0) << tickVal; - Cairo::TextExtents extents; - cr->get_text_extents(stream.str(), extents); - // Room for this label? - if (x + extents.x_bearing > prevTextAdvance) - { - cr->show_text(stream.str()); - prevTextAdvance = x + extents.x_advance; - } - } - cr->stroke(); - cr->restore(); - - if (!_autoScrolling) - { - _playButton->setVisible(true); - _playButton->setOrigin(width / 2 - 25, .875 * height - 50); - _playButton->draw(cr); - } - return true; } bool GraphWidget::on_button_press_event(GdkEventButton* event) { - if (!_autoScrolling && _playButton->containsPoint(event->x, event->y)) + for (GraphList::iterator g = _graphs.begin(); g != _graphs.end(); ++g) + { + if (event->x >= (*g)->_graphX + && event->x < (*g)->_graphX + (*g)->_graphWidth + && event->y >= (*g)->_graphY + && event->y < (*g)->_graphY + (*g)->_graphHeight) + { + _activeGraph = *g; + break; + } + } + if (!_activeGraph) + return true; + if (!_activeGraph->_autoScrolling + && _activeGraph->_playButton->containsPoint(event->x, event->y)) { - _autoScaling = true; - _autoScrolling = true; + _activeGraph->_autoScaling = true; + _activeGraph->_autoScrolling = true; queue_draw(); } else { _trackingDrag = true; - _autoScaling = false; - _autoScrolling = false; + _activeGraph->_autoScaling = false; + _activeGraph->_autoScrolling = false; _dragOriginX = event->x; _dragOriginY = event->y; - _dragOrigLeft = _left; - _dragOrigRight = _right; + _dragOrigLeft = _activeGraph->_left; + _dragOrigRight = _activeGraph->_right; } return true; } @@ -339,14 +142,14 @@ namespace systemtap x = event->x; y = event->y; } - if (_trackingDrag) + if (_trackingDrag && _activeGraph) { Gtk::Allocation allocation = get_allocation(); const int width = allocation.get_width(); double motion = (x - _dragOriginX) / (double) width; double increment = motion * (_dragOrigLeft - _dragOrigRight); - _left = _dragOrigLeft + increment; - _right = _dragOrigRight + increment; + _activeGraph->_left = _dragOrigLeft + increment; + _activeGraph->_right = _dragOrigRight + increment; queue_draw(); } return true; @@ -354,11 +157,20 @@ namespace systemtap bool GraphWidget::on_scroll_event(GdkEventScroll* event) { - if (event->direction == GDK_SCROLL_UP) - _zoomFactor += .1; - else if (event->direction == GDK_SCROLL_DOWN) - _zoomFactor -= .1; - queue_draw(); + for (GraphList::iterator gitr = _graphs.begin(); + gitr != _graphs.end(); + ++gitr) + { + if ((*gitr)->containsPoint(event->x, event->y)) + { + if (event->direction == GDK_SCROLL_UP) + (*gitr)->_zoomFactor += .1; + else if (event->direction == GDK_SCROLL_DOWN) + (*gitr)->_zoomFactor -= .1; + queue_draw(); + break; + } + } return true; } -- cgit