Background
I was looking for a simple control in wxWidgets which I could use to plot out some value against time, but I found that apparently I was the only one that needed that kind of graph. (so I don't know why I'm posting it... but anyway).
I decided to create my own class. In the snippet below I choose the Y-axes in a range from 0-100 (% for example) and the x-axes in 3 different scales: 1 hour, 1 day and 2 weeks. This may or may not be useful in your case, so change it if you need something else :)
How to use it
De neGraph class inherits from "wxScrolledWindow", so you'll need two things:
- a 'MyApp' (is always required with wxWidgets)
- a 'MyFrame'. You can pass the pointer from this class to the neGraph class, to let it know who is it's owner.
So to draw the neGraph, you could add something like the following in the constructor from MyFrame:
/* Create a new graph with dimensions 750x500 */
m_plot = new neGraph( this,wxSize(750,500));
/* Set offset from wxCoord(0,0), so basically the position of the graph */
m_plot->SetOffSet(wxSize(0,0));
/* Set x-scale to '2' which is 1 hour*/
m_plot->SetScale(2);
/* Scroll up */
m_plot->Scroll(0,0);
/* Draw a grid */
m_plot->DrawGrid(true);
Adding data
The graph reads all it's data from this: std::vector< neLine > grid_data;
. You can add up to a maximum of ten lines in one graph. For each neLine you add to the graph, a new line is drawn in a new color. The legend will also be automatically updated with the right color and line-name.
The neLine is constructed with a vector of coordinates and a name. These coordinates consist out of a time in minutes and a corresponding y-value. You will have to calculate the time in minutes yourself. For example: If you have a coordinate with minutes=1 and y = 2, the graph will draw the point one minute from the start of x scale.
Trivia:
The x-axes will automatically change the scale of the caption if the graph is resized.
I've added some small helper-function to the file that might come in usefull. You'll find them at the bottom of the file.
Header file (graph.h)
#include <vector>
#include "wx/wx.h"
#include "main.h" // for myFrame*
#define BUTTON_START 7000
#define COMBO_BOX_TIMESPAN 7001
#define C_HOUR "1 hour"
#define C_DAY "1 day"
#define C_WEEKS "2 weeks"
#define STD_OFFSET 50
struct neCoord
{
long minutes;
double y;
};
class neLine
{
public:
std::vector<neCoord> coords;
neLine() {line_name = "<empty>";}
neLine(std::vector<neCoord>,wxString name = "<empty>");
~neLine(){};
void ClearItems(void);
wxString line_name;
void Add(neCoord in_coord){coords.push_back(in_coord);}
};
// define a scrollable canvas for drawing onto
class neGraph: public wxScrolledWindow
{
public:
neGraph( MyFrame*);
neGraph( MyFrame*, wxSize );
~neGraph();
void SetOffSet(wxSize);
void SetScale(int);
int GetScale(void) { return scale; }
void SetStartDate(const wxChar*);
void AddData(neLine in_data) {grid_data.push_back(in_data);}
void ClearGraph(void);
void SetGridSize(wxSize);
void DrawGrid(bool in ) { grid = in; }
private:
void InitGraph(wxInitDialogEvent &event);
void OnPaint(wxPaintEvent &event);
void OnStartClicked(wxCommandEvent &event);
void OnTimeSelection(wxCommandEvent &event);
void DrawAxes(wxDC& dc);
void DrawControls(void);
void FillAxes(wxDC& dc);
void DrawData(wxDC& dc);
wxDateTime start;
int scale;
bool grid;
MyFrame *m_owner;
wxSize g_size;
wxSize req_g_size;
wxSize offset;
std::vector< neLine > grid_data;
wxTextCtrl *t_inputd;
wxTextCtrl *t_inputt;
wxButton *b_go;
wxComboBox *c_timespan;
DECLARE_EVENT_TABLE();
};
void neBox(const wxString);
std::string IntToString(int Input, int add_a_zero = 0);
#endif
NOTE:
This code is only meant as a starting point to create your own graph. It's neither great, done, or fail-proof so don't come complaining if you don't like it ;). I just posted the code here because it took me a few hours to create and I want to save someone else the trouble.
Of course I'll be more then happy to give further information if it's required, so post comments!