wxFreeChart
wxFreeChart copied to clipboard
Make charts interactive
There has been some work on making charts interactive, but this needs to be completed. The minimum requirement is to show a tooltip with the precise values when the user hovers the mouse over a specific data point.
I see that you have changed the mousevents handling in the code. Do you have a code example to enable tooltips? I would like to give it a go with changing the points on the plot using the mouse.
I had a very simple version working, but it is disable for now due to the change in the dataset structure.
Look at the bottom of axisplot.cpp and you will see the code I had in before. You simply need to override OnMouseMove
in any class derived from Plot
to start getting mouse events (hopefully!).
My code did a brute force search of all datasets to see if any datapoint is near the mouse cursor. As soon as it found one that was within a threshold, the handler called SetTipData
with the text to be displayed.
Following your old method, i adapted it to work with the new dataset. Here is the new code :
void XYPlot::OnMouseMove(wxMouseEvent& event)
{
for (size_t set = 0; set < GetDatasetCount(); set++)
{
BiDataSet* dataset = wxDynamicCast(GetDataset(set), BiDataSet);
NumberAxis* xAxis = static_cast<NumberAxis*>(GetDatasetAxis(dataset, false));
NumberAxis* yAxis = static_cast<NumberAxis*>(GetDatasetAxis(dataset, true));
for (size_t ser = 0; ser < dataset->GetSeriesCount(); ser++)
{
DataSeries* series = dataset->GetSeries(ser).get();
wxMemoryDC dummy;
double x = xAxis->ToData(dummy, m_rect.x, m_rect.GetWidth(), event.GetPosition().x);
double y = yAxis->ToData(dummy, m_rect.y, m_rect.GetHeight(), event.GetPosition().y);
for (size_t pt = 0; pt < series->GetSize(); pt++)
{
BiDataPoint* point = wxDynamicCast(series->GetPoint(pt).get(), BiDataPoint);
double fi = point->first.As<double>();
double se = point->second.As<double>();
if (fi < x + 5 && fi > x - 5 &&
se < y + 5 && se > y - 5)
{
wxLogMessage("Got a point in Dataset: %d, Series: %d, Point Index: %d, at Point (x,y): %g, %g",set, ser, pt, fi, se);
SetTipData(wxString::Format("x : %g\ny : %g", fi, se));
return;
}
}
}
}
}
It seems to work quite nice, but it seems that there is an "offset" on the points that the tooltips are displayed. Maybe it is that the +5, -5 values are too high? I will do some more tests and report back. Also the when moving the mouse around the tooltip flickers. Maybe it has to do with the wxMemoryDC or something.
Working a little bit more on the "interactivity" here is a code snippet that i use to drag points. I am using it in the OnMouseMove event and i check if the event.Dragging is true.
if (event.Dragging())
{
for (size_t set = 0; set < GetDatasetCount(); set++)
{
BiDataSet* dataset = wxDynamicCast(GetDataset(set), BiDataSet);
NumberAxis* xAxis = static_cast<NumberAxis*>(GetDatasetAxis(dataset, false));
NumberAxis* yAxis = static_cast<NumberAxis*>(GetDatasetAxis(dataset, true));
for (size_t ser = 0; ser < dataset->GetSeriesCount(); ser++)
{
DataSeries* series = dataset->GetSeries(ser).get();
wxMemoryDC dummy;
double x = xAxis->ToData(dummy, m_rect.x, m_rect.GetWidth(), event.GetPosition().x);
double y = yAxis->ToData(dummy, m_rect.y, m_rect.GetHeight(), event.GetPosition().y);
for (size_t pt = 0; pt < series->GetSize(); pt++)
{
BiDataPoint* point = wxDynamicCast(series->GetPoint(pt).get(), BiDataPoint);
double fi = point->first.As<double>();
double se = point->second.As<double>();
// drag a point vertically
// maybe add a couple flags to enable dragging horizontaly or vertically or both
if ((fi > x - 0.5) && (fi < x + 0.5))
{
point->SetValues(fi, y);
dataset->DatasetChanged();
return;
}
}
}
}
}
I hope that this will help enough to properly implement a "dragging" feature.
Hi, I think that this is okay, but it would be better to somehow capture the point once dragging has started otherwise if you drag the point near another point it may jump and start dragging the other point instead. Also, this would avoid searching all the datasets on every mouse move.
Therefore, I would suggest using both OnMouseDown
(not yet implemented!!) and OnMouseMove
in future.