Yet More Doxygen stuff, pruned some old unused code

This commit is contained in:
Mark Watkins 2011-12-19 15:35:05 +10:00
parent d7a0b9fecb
commit da855f4bc8
31 changed files with 415 additions and 663 deletions

View File

@ -11,18 +11,37 @@
class gFlagsGroup;
/*! \class gFlagsLine
\brief One single line of event flags in the Event Flags chart
*/
class gFlagsLine:public Layer
{
friend class gFlagsGroup;
public:
/*! \brief Constructs an individual gFlagsLine object
\param code The Channel the data is sourced from
\param col The colour to draw this flag
\param label The label to show to the left of the Flags line.
\param always_visible Whether to always show this line, even if empty
\param Type of Flag, either FT_Bar, or FT_Span
*/
gFlagsLine(ChannelID code,QColor col=Qt::black,QString label="",bool always_visible=false,FlagType flt=FT_Bar);
virtual ~gFlagsLine();
//! \brief Drawing code to add the flags and span markers to the Vertex buffers.
virtual void paint(gGraph & w,int left, int top, int width, int height);
//! \brief Returns true if should always show this flag, even if it's empty
bool isAlwaysVisible() { return m_always_visible; }
//! \brief Set this to true to make a flag line always visible
void setAlwaysVisible(bool b) { m_always_visible=b; }
//! \brief Returns the label for this individual Event Flags line
QString label() { return m_label; }
//! \brief Sets the label for this individual Event Flags line
void setLabel(QString s) { m_label=s; }
void setTotalLines(int i) { total_lines=i; }
void setLineNum(int i) { line_num=i; }
protected:
@ -35,19 +54,36 @@ class gFlagsLine:public Layer
int m_lx, m_ly;
};
/*! \class gFlagsGroup
\brief Contains multiple gFlagsLine entries for the Events Flag graph
*/
class gFlagsGroup:public LayerGroup
{
public:
gFlagsGroup();
virtual ~gFlagsGroup();
//! Draw filled rectangles behind Event Flag's, and an outlines around them all, Calls the individual paint for each gFlagLine
virtual void paint(gGraph & w,int left, int top, int width, int height);
//! Returns the first time represented by all gFlagLine layers, in milliseconds since epoch
virtual qint64 Minx();
//! Returns the last time represented by all gFlagLine layers, in milliseconds since epoch.
virtual qint64 Maxx();
//! Checks if each flag has data, and adds valid gFlagLines to the visible layers list
virtual void SetDay(Day *);
//! Returns true if none of the gFlagLine objects contain any data for this day
virtual bool isEmpty() { return m_empty; }
//! Returns the count of visible flag line entries
int count() { return lvisible.size(); }
//! Returns the height in pixels of each bar
int barHeight() { return m_barh; }
//! Returns a list of Visible gFlagsLine layers to draw
QVector<gFlagsLine *> & visibleLayers() { return lvisible; }
protected:

View File

@ -9,6 +9,9 @@
#include "gGraphView.h"
/*! \class gShadowArea
\brief Displays a Shadow for all graph areas not highlighted (used in Event Flags)
*/
class gShadowArea:public Layer
{
public:
@ -22,6 +25,10 @@ class gShadowArea:public Layer
GLShortBuffer *lines;
};
/*! \class gFooBar
\brief Was a kind of scrollbar thingy that used to be used for representing the overall graph areas.
Currently Unused and empty.
*/
class gFooBar:public Layer
{
public:

View File

@ -253,7 +253,7 @@ public:
//! \brief Draw all this layers custom GLBuffers (ie. the actual OpenGL Vertices)
virtual void drawGLBuf(float linesize);
//! \note not sure why I needed the reference counting stuff.
//! \brief not sure why I needed the reference counting stuff.
short m_refcount;
void addref() { m_refcount++; }
bool unref() { m_refcount--; if (m_refcount<=0) return true; return false; }
@ -439,7 +439,7 @@ public:
//! \brief Set the height element. (relative to the total of all heights)
void setHeight(float height) { m_height=height; }
// Can't remember what these are for..
//! \brief Can't remember what these are for..
int minHeight() { return m_min_height; }
void setMinHeight(int height) { m_min_height=height; }
@ -449,9 +449,10 @@ public:
//! \brief Set whether or not to render the vertical graph title
void showTitle(bool b);
//! \brief Returns printScaleX, used for DPI scaling
//! \brief Returns printScaleX, used for DPI scaling in report printing
float printScaleX();
//! \brief Returns printScaleY, used for DPI scaling..
//! \brief Returns printScaleY, used for DPI scaling in report printing
float printScaleY();
//! \brief Returns true if none of the included layers have data attached
@ -460,6 +461,7 @@ public:
//! \brief Add Layer l to graph object, allowing you to specify position, margin sizes, order, movability status and offsets
void AddLayer(Layer * l,LayerPosition position=LayerCenter, short pixelsX=0, short pixelsY=0, short order=0, bool movable=false, short x=0, short y=0);
void qglColor(QColor col);
//! \brief Queues text for gGraphView object to draw it.

View File

@ -13,21 +13,41 @@
#include "gGraphView.h"
//#include "graphlayer.h"
/*! \class AHIChart
\brief Another graph calculating the AHI/hour, this one looks at all the sessions for a day. Currently Unused.
*/
class AHIChart:public Layer
{
public:
//! \brief Constructs an AHIChart object, with QColor col for the line plots.
AHIChart(const QColor col=QColor("black"));
~AHIChart();
//! \brief Draws the precalculated data to the Vertex buffers
virtual void paint(gGraph & w,int left, int top, int width, int height);
//! \brief AHI/hr Calculations are done for this day here.
//! This also uses the sliding window method
virtual void SetDay(Day *d);
//! \brief Returns the minimum AHI/hr value caculated
virtual EventDataType Miny() { return m_miny; }
//! \brief Returns the maximum AHI/hr value caculated
virtual EventDataType Maxy() { return m_maxy; }
//! \brief Returns true if no data was available
virtual bool isEmpty() { return m_data.size()==0; }
protected:
//! \brief Contains the plot data (Y-axis) generated for this day
QVector<EventDataType> m_data;
//! \brief Contains the time codes (X-axis) generated for this day
QVector<quint64> m_time;
EventDataType m_miny,m_maxy;
EventDataType m_miny;
EventDataType m_maxy;
QColor m_color;
GLShortBuffer * lines;
};
@ -57,7 +77,7 @@ class gLineChart:public Layer
bool GetSquarePlot() { return m_square_plot; }
//! \brief Set this if you want this layer to draw it's empty data message
//! \note They don't show anymore due to the changes in gGraphView. Should modify isEmpty if this still gets to live
//! They don't show anymore due to the changes in gGraphView. Should modify isEmpty if this still gets to live
void ReportEmpty(bool b) { m_report_empty=b; }
//! \brief Returns whether or not to show the empty text message

View File

@ -9,16 +9,27 @@
#include "gGraphView.h"
/*! \class gLineOverlayBar
\brief Shows a flag line, a dot, or a span over the top of a 2D line chart.
*/
class gLineOverlayBar:public Layer
{
public:
//! \brief Constructs a gLineOverlayBar object of type code, setting the flag/span color, the label to show when zoomed
//! in, and the display method requested, of either FT_Bar, FT_Span, or FT_Dot
gLineOverlayBar(ChannelID code,QColor col,QString _label="",FlagType _flt=FT_Bar);
virtual ~gLineOverlayBar();
//! \brief The drawing code that fills the OpenGL vertex GLBuffers
virtual void paint(gGraph & w,int left, int top, int width, int height);
virtual EventDataType Miny() { return 0; }
virtual EventDataType Maxy() { return 0; }
//! \brief Returns true if no Channel data available for this code
virtual bool isEmpty() { return true; }
//! \brief returns a count of all flags drawn during render. (for gLineOverlaySummary)
int count() { return m_count; }
protected:
QColor m_flag_color;
@ -29,6 +40,9 @@ class gLineOverlayBar:public Layer
GLShortBuffer *points,*quads, *lines;
};
/*! \class gLineOverlaySummary
\brief A container class to hold a group of gLineOverlayBar's, and shows the "Section AHI" over the Flow Rate waveform.
*/
class gLineOverlaySummary:public Layer
{
public:
@ -38,7 +52,11 @@ class gLineOverlaySummary:public Layer
virtual void paint(gGraph & w,int left, int top, int width, int height);
virtual EventDataType Miny() { return 0; }
virtual EventDataType Maxy() { return 0; }
//! \brief Returns true if no Channel data available for this code
virtual bool isEmpty() { return true; }
//! \brief Adds a gLineOverlayBar to this list
gLineOverlayBar *add(gLineOverlayBar *bar) { m_overlays.push_back(bar); return bar; }
protected:
QVector<gLineOverlayBar *> m_overlays;

View File

@ -1,167 +0,0 @@
/*
gSessionTime Implementation
Copyright (c)2011 Mark Watkins <jedimark@users.sourceforge.net>
License: GPL
*/
#include <math.h>
#include <SleepLib/profiles.h>
#include "gSessionTime.h"
gTimeYAxis::gTimeYAxis(QColor col)
:gYAxis("",col)
{
}
gTimeYAxis::~gTimeYAxis()
{
}
const QString gTimeYAxis::Format(double v)
{
static QString t;
int i=v;
if (i<0) i=24+i;
t.sprintf("%02i:00",i);
return t;
};
gSessionTime::gSessionTime(ChannelID code,QColor col,Qt::Orientation o)
:Layer(code),m_orientation(o)
{
color.clear();
color.push_back(col);
Xaxis=new gXAxis();
}
gSessionTime::~gSessionTime()
{
delete Xaxis;
}
void gSessionTime::paint(gGraph & w,int left, int top, int width, int height)
{
Q_UNUSED(w);
Q_UNUSED(left);
Q_UNUSED(top);
Q_UNUSED(width);
Q_UNUSED(height);
if (!m_visible) return;
/*if (!data) return;
if (!data->IsReady()) return;
int start_px=w.GetLeftMargin();
int start_py=w.GetBottomMargin();
int width=scrx-(w.GetLeftMargin()+w.GetRightMargin());
int height=scry-(w.GetTopMargin()+w.GetBottomMargin());
double maxx=w.max_x;
double minx=w.min_x;
double xx=maxx - minx;
int days=ceil(xx);
float barwidth=float(width-days)/float(days);
qint64 dy;
//double sd;
double px1,py1;//,py2,px2;
QColor & col1=color[0];
QColor col2("light grey");
QString str;
bool draw_xticks_instead=false;
bool antialias=(*profile)["UseAntiAliasing"].toBool();
QDateTime d;
QTime t;
double start,total;//end,
float textX,textY;
map<int,bool> datedrawn;
int idx=-1;
for (int i=0;i<data->np[0];i++) {
QPointD & rp=data->point[0][i];
if (int(rp.x()) < int(minx)) continue;
if (int(rp.x()) > int(maxx+.5)) break;
if (idx<0) idx=i;
d=QDateTime::fromTime_t(rp.x()*86400.0);
t=d.time();
start=t.hour()+(t.minute()/60.0)+(t.second()/3600.0);
//d=QDateTime::fromTime_t(rp.y()*86400.0);
//t=d.time();
//end=t.hour()+(t.minute()/60.0)+(t.second()/3600.0);
total=(rp.y()-rp.x())*24;
dy=int(rp.x())-int(minx); // day number.
if (dy>=days) continue;
if (start>=12) {
// dy++;
start-=24;
}
start+=12;
if (start+total>24) {
// total=24-start;
}
if (total<0.25) continue; // Hide sessions less than 15 minutes
//double sd=floor(rp.x());
px1=dy*(barwidth+1); // x position for this day
//px2=px1+barwidth; // plus bar width
py1=height/24.0*start;
double h=height/24.0*(total);
QRect rect(start_px+px1,start_py+py1,barwidth,h);
glBegin(GL_QUADS);
glColor4ub(col1.red(),col1.green(),col1.blue(),col1.alpha());
glVertex2f(rect.x(), rect.y()+rect.height());
glVertex2f(rect.x(), rect.y());
glColor4ub(col2.red(),col2.green(),col2.blue(),col2.alpha());
glVertex2f(rect.x()+rect.width(),rect.y());
glVertex2f(rect.x()+rect.width(), rect.y()+rect.height());
glEnd();
glColor4ub(0,0,0,255);
glLineWidth (1);
glBegin(GL_LINE_LOOP);
glVertex2f(rect.x(), rect.y()+rect.height()+.5);
glVertex2f(rect.x(), rect.y());
glVertex2f(rect.x()+rect.width(),rect.y());
glVertex2f(rect.x()+rect.width(), rect.y()+rect.height()+.5);
glEnd();
if (!draw_xticks_instead) {
if (datedrawn.find(dy)==datedrawn.end()) {
datedrawn[dy]=true;
str=FormatX(rp.y());
GetTextExtent(str, textX, textY);
if (!draw_xticks_instead && (textY+6<barwidth)) {
glBegin(GL_LINE);
glVertex2f(start_px+px1+barwidth/2,start_py);
glVertex2f(start_px+px1+barwidth/2,start_py-6);
glEnd();
DrawText(str,start_px+px1+barwidth/2+textY,scry-(start_py-8-textX/2),90);
} else draw_xticks_instead=true;
}
}
}
if (draw_xticks_instead) {
// turn off the minor ticks..
Xaxis->SetShowMinorTicks(false);
Xaxis->Plot(w,scrx,scry);
}
glColor3f (0.0F, 0.0F, 0.0F);
glLineWidth(1);
glBegin (GL_LINES);
glVertex2f (start_px, start_py);
glVertex2f (start_px, start_py+height+1);
glVertex2f (start_px,start_py);
glVertex2f (start_px+width, start_py);
glEnd (); */
}

View File

@ -1,43 +0,0 @@
/*
gSessionTime Header
Copyright (c)2011 Mark Watkins <jedimark@users.sourceforge.net>
License: GPL
*/
#ifndef GSESSIONTIME_H
#define GSESSIONTIME_H
#include "gGraphView.h"
#include "gXAxis.h"
#include "gYAxis.h"
class gTimeYAxis:public gYAxis
{
public:
gTimeYAxis(QColor col=QColor("black"));
virtual ~gTimeYAxis();
virtual const QString Format(double v);
};
class gSessionTime:public Layer
{
public:
gSessionTime(ChannelID="",QColor col=QColor("blue"),Qt::Orientation o=Qt::Horizontal);
virtual ~gSessionTime();
virtual void paint(gGraph & w,int left,int top, int width, int height);
protected:
Qt::Orientation m_orientation;
virtual const QString & FormatX(double v) { static QString t; QDateTime d; d=d.fromTime_t(v*86400.0); t=d.toString("MMM dd"); return t; }
//virtual const wxString & FormatX(double v) { static wxString t; wxDateTime d; d.Set(vi*86400000.0); t=d.Format(wxT("HH:mm")); return t; };
//virtual const wxString & FormatX(double v) { static wxString t; t=wxString::Format(wxT("%.1f"),v); return t; };
virtual const QString & FormatY(double v) { static QString t; t.sprintf("%.1f",v); return t; }
gXAxis *Xaxis;
QVector<QColor> color;
};
#endif // GSESSIONTIME_H

View File

@ -4,6 +4,9 @@
#include "SleepLib/machine.h"
#include "gGraphView.h"
/*! \class gStatsLine
\brief Show a rendered stats area in place of a graph. This is currently unused
*/
class gStatsLine : public Layer
{
public:

View File

@ -8,6 +8,8 @@
#define GXAXIS_H
#include "gGraphView.h"
/*! \class gXAxis
\brief Draws the XTicker timescales underneath graphs */
class gXAxis:public Layer
{
public:

View File

@ -124,8 +124,8 @@ void gXGrid::paint(gGraph & w,int left,int top, int width, int height)
gYAxis::gYAxis(ChannelID code,QColor col)
:Layer(code)
gYAxis::gYAxis(QColor col)
:Layer("")
{
m_line_color=col;
m_text_color=col;
@ -249,7 +249,7 @@ bool gYAxis::mouseMoveEvent(QMouseEvent * event)
}
gYAxisTime::gYAxisTime(QColor col):
gYAxis("",col)
gYAxis(col)
{
show_12hr=true;
}

View File

@ -9,6 +9,10 @@
#include "gGraphView.h"
/*! \class gYSpacer
\brief A dummy vertical spacer object
*/
class gYSpacer:public Layer
{
public:
@ -23,16 +27,29 @@ class gYSpacer:public Layer
};
/*! \class gXGrid
\brief Draws the horizintal major/minor grids over graphs
*/
class gXGrid:public Layer
{
public:
//! \brief Constructs an gXGrid object with default settings, and col for line colour.
gXGrid(QColor col=QColor("black"));
virtual ~gXGrid();
//! \brief Draw the horizontal lines by adding the to the Vertex GLbuffers
virtual void paint(gGraph & w,int left,int top, int width, int height);
//! \brief set the visibility status of Major lines
void setShowMinorLines(bool b) { m_show_minor_lines=b; }
//! \brief set the visibility status of Minor lines
void setShowMajorLines(bool b) { m_show_major_lines=b; }
//! \brief Returns the visibility status of minor lines
bool showMinorLines() { return m_show_minor_lines; }
//! \brief Returns the visibility status of Major lines
bool showMajorLines() { return m_show_major_lines; }
protected:
bool m_show_major_lines;
@ -41,29 +58,51 @@ protected:
QColor m_minor_color;
};
/*! \class gYAxis
\brief Draws the YAxis tick markers, and numeric labels
*/
class gYAxis:public Layer
{
public:
gYAxis(ChannelID code="",QColor col=QColor("black"));
//! \brief Construct a gYAxis object, with QColor col for tickers & text
gYAxis(QColor col=QColor("black"));
virtual ~gYAxis();
virtual void paint(gGraph & w,int left,int top, int width, int height);
void SetShowMinorLines(bool b) { m_show_minor_lines=b; }
void SetShowMajorLines(bool b) { m_show_major_lines=b; }
bool ShowMinorLines() { return m_show_minor_lines; }
bool ShowMajorLines() { return m_show_major_lines; }
void SetShowMinorTicks(bool b) { m_show_minor_ticks=b; }
void SetShowMajorTicks(bool b) { m_show_major_ticks=b; }
bool ShowMinorTicks() { return m_show_minor_ticks; }
bool ShowMajorTicks() { return m_show_major_ticks; }
virtual const QString Format(EventDataType v, int dp);
static const int Margin=60; // Left margin space
void SetScale(float f) { m_yaxis_scale=f; } // Scale yaxis ticker values (only what's displayed)
//! \brief Draw the horizontal tickers display
virtual void paint(gGraph & w,int left,int top, int width, int height);
// void SetShowMinorLines(bool b) { m_show_minor_lines=b; }
// void SetShowMajorLines(bool b) { m_show_major_lines=b; }
// bool ShowMinorLines() { return m_show_minor_lines; }
// bool ShowMajorLines() { return m_show_major_lines; }
//! \brief Sets the visibility status of minor ticks
void SetShowMinorTicks(bool b) { m_show_minor_ticks=b; }
//! \brief Sets the visibility status of Major ticks
void SetShowMajorTicks(bool b) { m_show_major_ticks=b; }
//! \brief Returns the visibility status of Minor ticks
bool ShowMinorTicks() { return m_show_minor_ticks; }
//! \brief Returns the visibility status of Major ticks
bool ShowMajorTicks() { return m_show_major_ticks; }
//! \brief Formats the ticker value.. Override to implement other types
virtual const QString Format(EventDataType v, int dp);
//! \brief Left Margin space in pixels
static const int Margin=60;
//! \brief Set the scale of the Y axis values.. Values can be multiplied by this to convert formats
void SetScale(float f) { m_yaxis_scale=f; }
//! \brief Returns the scale of the Y axis values.. Values can be multiplied by this to convert formats
float Scale() { return m_yaxis_scale; }
protected:
bool m_show_major_lines;
bool m_show_minor_lines;
//bool m_show_major_lines;
//bool m_show_minor_lines;
bool m_show_minor_ticks;
bool m_show_major_ticks;
float m_yaxis_scale;
@ -75,13 +114,20 @@ class gYAxis:public Layer
};
/*! \class gYAxisTime
\brief Draws the YAxis tick markers, and labels in time format
*/
class gYAxisTime:public gYAxis
{
public:
//! \brief Construct a gYAxisTime object, with QColor col for tickers & times
gYAxisTime(QColor col=Qt::black);
virtual ~gYAxisTime();
protected:
//! \brief Overrides gYAxis Format to display Time format
virtual const QString Format(EventDataType v, int dp);
//! \brief Whether to format as 12 or 24 hour times
bool show_12hr;
};

View File

@ -15,7 +15,6 @@
<file>docs/usage.html</file>
<file>icons/oximeter.png</file>
<file>docs/0.0.gif</file>
<file>docs/template_overview.sht</file>
<file>docs/schema.xml</file>
<file>docs/channels.xml</file>
<file>icons/last.png</file>

View File

@ -12,6 +12,9 @@
#include "SleepLib/event.h"
#include "SleepLib/session.h"
/*! \class OneTypePerDay
\brief An Exception class to catch multiple machine records per day
*/
class OneTypePerDay
{
};

View File

@ -13,6 +13,10 @@ License: GPL
const QString cms50_class_name="CMS50";
const int cms50_data_version=4;
/*! \class CMS50Loader
\brief Bulk Importer for CMS50 SPO2Review format.. Deprecated, as the Oximetry module does a better job
*/
class CMS50Loader : public MachineLoader
{
public:

View File

@ -22,6 +22,9 @@ const int intellipap_data_version=1;
//
//********************************************************************************************
/*! \class Intellipap
\brief Intellipap customized machine object
*/
class Intellipap:public CPAP
{
public:
@ -35,24 +38,32 @@ const int intellipap_load_buffer_size=1024*1024;
const QString intellipap_class_name="Intellipap";
/*! \class IntellipapLoader
\brief Loader for DeVilbiss Intellipap Auto data
This is only relatively recent addition and still needs more work
*/
class IntellipapLoader : public MachineLoader
{
public:
IntellipapLoader();
virtual ~IntellipapLoader();
//! \brief Scans path for Intellipap data signature, and Loads any new data
virtual int Open(QString & path,Profile *profile);
//! \brief Returns SleepLib database version of this IntelliPap loader
virtual int Version() { return intellipap_data_version; }
//! \brief Returns the machine class name of this IntelliPap, "Intellipap"
virtual const QString & ClassName() { return intellipap_class_name; }
//! \brief Creates a machine object, indexed by serial number
Machine *CreateMachine(QString serial,Profile *profile);
//! \brief Registers this MachineLoader with the master list, so Intellipap data can load
static void Register();
protected:
QString last;
QHash<QString,Machine *> MachList;
/*int OpenMachine(Machine *m,QString path,Profile *profile);
bool ParseProperties(Machine *m,QString filename);
bool OpenSummary(Session *session,QString filename);
bool OpenEvents(Session *session,QString filename);
bool OpenWaveforms(Session *session,QString filename);*/
unsigned char * m_buffer;
};

View File

@ -106,6 +106,15 @@ PRS1::~PRS1()
}
/*! \struct WaveHeaderList
\brief Used in PRS1 Waveform Parsing */
struct WaveHeaderList {
quint16 interleave;
quint8 sample_format;
WaveHeaderList(quint16 i,quint8 f){ interleave=i; sample_format=f; }
};
PRS1Loader::PRS1Loader()
{
//genCRCTable();
@ -420,19 +429,18 @@ int PRS1Loader::OpenMachine(Machine *m,QString path,Profile *profile)
return cnt;
}
//struct PRS1SummaryR5 {
// quint8 unknown1;
// quint8 unknown2;
// quint8 unknown3;
// quint8 pressure_min;
// quint8 pressure_max;
// quint8 unknown;
struct PRS1SummaryR5 {
quint8 unknown1;
quint8 unknown2;
quint8 unknown3;
quint8 pressure_min;
quint8 pressure_max;
quint8 unknown;
quint32 flags;
// quint32 flags;
};// __attribute__((packed));
//};// __attribute__((packed));
bool PRS1Loader::ParseSummary(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size, char version)
{
@ -1414,6 +1422,7 @@ bool PRS1Loader::OpenEvents(Session *session,QString filename)
return true;
} */
bool PRS1Loader::OpenWaveforms(SessionID sid, QString filename)
{
Session * session=new_sessions[sid];

View File

@ -25,6 +25,9 @@ const int prs1_data_version=8;
//
//********************************************************************************************
/*! \class PRS1
\brief PRS1 customized machine object (via CPAP)
*/
class PRS1:public CPAP
{
public:
@ -38,43 +41,67 @@ const int max_load_buffer_size=1024*1024;
const QString prs1_class_name="PRS1";
/*! \class PRS1Loader
\brief Philips Respironics System One Loader Module
*/
class PRS1Loader : public MachineLoader
{
public:
PRS1Loader();
virtual ~PRS1Loader();
//! \brief Scans directory path for valid PRS1 signature
virtual int Open(QString & path,Profile *profile);
//! \brief Returns the database version of this loader
virtual int Version() { return prs1_data_version; }
//! \brief Return the ClassName, in this case "PRS1"
virtual const QString & ClassName() { return prs1_class_name; }
//! \brief Create a new PRS1 machine record, indexed by Serial number.
Machine *CreateMachine(QString serial,Profile *profile);
//! \brief Register this Module to the list of Loaders, so it knows to search for PRS1 data.
static void Register();
protected:
QString last;
QHash<QString,Machine *> PRS1List;
//! \brief Opens the SD folder structure for this machine, scans for data files and imports any new sessions
int OpenMachine(Machine *m,QString path,Profile *profile);
//! \brief Parses "properties.txt" file containing machine information
bool ParseProperties(Machine *m,QString filename);
//bool OpenSummary(Session *session,QString filename);
//bool OpenEvents(Session *session,QString filename);
//! \brief Parse a .005 waveform file, extracting Flow Rate waveform (and Mask Pressure data if available)
bool OpenWaveforms(SessionID sid, QString filename);
//! \brief ParseWaveform chunk.. Currently unused, as the old one works fine.
bool ParseWaveform(qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size, quint16 duration, quint16 num_signals, quint16 interleave, quint8 sample_format);
//! \brief Parse a data chunk from the .000 (brick) and .001 (summary) files.
bool ParseSummary(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size, char version);
//! \brief Parse a single data chunk from a .002 file containing event data for a standard system one machine
bool Parse002(qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size);
//! \brief Parse a single data chunk from a .002 file containing event data for a family 5 ASV machine (which has a different format)
bool Parse002v5(qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size);
//! \brief Open a PRS1 data file, and break into data chunks, delivering them to the correct parser.
bool OpenFile(Machine *mach, QString filename);
//bool Parse002(Session *session,unsigned char *buffer,int size,qint64 timestamp,long fpos);
//bool Parse002ASV(Session *session,unsigned char *buffer,int size,qint64 timestamp,long fpos);
unsigned char * m_buffer;
QHash<SessionID, Session *> extra_session;
//! \brief PRS1 Data files can store multiple sessions, so store them in this list for later processing.
QHash<SessionID, Session *> new_sessions;
qint32 summary_duration;
};
struct WaveHeaderList {
quint16 interleave;
quint8 sample_format;
WaveHeaderList(quint16 i,quint8 f){ interleave=i; sample_format=f; }
};
#endif // PRS1LOADER_H

View File

@ -196,6 +196,9 @@ bool EDFParser::Open(QString name)
filesize=f.size();
datasize=filesize-EDFHeaderSize;
if (datasize<0) return false;
//Urk.. This needs fixing for VC++, as it doesn't have packed attribute type..
f.read((char *)&header,EDFHeaderSize);
//qDebug() << "Opening " << name;
buffer=new char [datasize];

View File

@ -26,6 +26,10 @@ const int resmed_data_version=5;
const QString resmed_class_name="ResMed";
/*! \struct EDFHeader
\brief Represents the EDF+ header structure, used as a place holder while processing the text data.
\note More information on the EDF+ file format can be obtained from http://edfplus.info
*/
struct EDFHeader {
char version[8];
char patientident[80];
@ -36,50 +40,113 @@ struct EDFHeader {
char num_data_records[8];
char dur_data_records[8];
char num_signals[4];
};// __attribute__ ((packed));
}
#ifndef BUILD_WITH_MSVC
__attribute__ ((packed))
#endif
;
const int EDFHeaderSize=sizeof(EDFHeader);
/*! \struct EDFSignal
\brief Contains information about a single EDF+ Signal
\note More information on the EDF+ file format can be obtained from http://edfplus.info
*/
struct EDFSignal {
public:
//! \brief Name of this Signal
QString label;
//! \brief Tranducer Type (source of the data, usually blank)
QString transducer_type;
//! \brief The units of measurements represented by this signal
QString physical_dimension;
//! \brief The minimum limits of the ungained data
double physical_minimum;
//! \brief The maximum limits of the ungained data
double physical_maximum;
//! \brief The minimum limits of the data with gain and offset applied
double digital_minimum;
//! \brief The maximum limits of the data with gain and offset applied
double digital_maximum;
//! \brief Raw integer data is multiplied by this value
double gain;
//! \brief This value is added to the raw data
double offset;
//! \brief Any prefiltering methods used (usually blank)
QString prefiltering;
//! \brief Number of records
long nr;
//! \brief Reserved (usually blank)
QString reserved;
//! \brief Pointer to the signals sample data
qint16 * data;
//! \brief a non-EDF extra used internally to count the signal data
int pos;
};
/*! \class EDFParser
\author Mark Watkins <jedimark64_at_users.sourceforge.net>
\brief Parse an EDF+ data file into a list of EDFSignal's
\note More information on the EDF+ file format can be obtained from http://edfplus.info
*/
class EDFParser
{
public:
EDFParser(QString filename);
//! \brief Constructs an EDFParser object, opening the filename if one supplied
EDFParser(QString filename="");
~EDFParser();
//! \brief Open the EDF+ file, and read it's header
bool Open(QString name);
//! \brief Read si bytes of 8 bit data from the EDF+ data stream
QString Read(int si);
//! \brief Read 16 bit word of data from the EDF+ data stream
qint16 Read16();
//! \brief Vector containing the list of EDFSignals contained in this edf file
QVector<EDFSignal *> edfsignals;
//! \brief An by-name indexed into the EDFSignal data
QHash<QString,EDFSignal *> lookup;
//! \brief Look up signal names by SleepLib ChannelID.. A little "ResMed"ified.. :/
EDFSignal * lookupSignal(ChannelID);
//! \brief Returns the number of signals contained in this EDF file
long GetNumSignals() { return num_signals; }
//! \brief Returns the number of data records contained per signal.
long GetNumDataRecords() { return num_data_records; }
//! \brief Returns the duration represented by this EDF file (in milliseconds)
qint64 GetDuration() { return dur_data_record; }
//! \brief Returns the patientid field from the EDF header
QString GetPatient() { return patientident; }
//! \brief Parse the EDF+ file into the list of EDFSignals.. Must be call Open(..) first.
bool Parse();
char *buffer;
//! \brief The EDF+ files header structure, used as a place holder while processing the text data.
EDFHeader header;
QString filename;
long filesize;
long datasize;
@ -99,27 +166,50 @@ public:
QString reserved44;
};
/*! \class ResmedLoader
\brief Importer for ResMed S9 Data
*/
class ResmedLoader : public MachineLoader
{
public:
ResmedLoader();
virtual ~ResmedLoader();
//! \brief Scans for S9 SD folder structure signature, and loads any new data if found
virtual int Open(QString & path,Profile *profile);
//! \brief Returns the version number of this ResMed loader
virtual int Version() { return resmed_data_version; }
//! \brief Returns the Machine class name of this loader. ("ResMed")
virtual const QString & ClassName() { return resmed_class_name; }
//! \brief Converts EDFSignal data to time delta packed EventList, and adds to Session
EventList * ToTimeDelta(Session *sess,EDFParser &edf, EDFSignal & es, ChannelID code, long recs,qint64 duration,EventDataType min=0,EventDataType max=0,bool square=false);
//! \brief Create Machine record, and index it by serial number
Machine *CreateMachine(QString serial,Profile *profile);
//! \brief Register the ResmedLoader with the list of other machine loaders
static void Register();
protected:
QHash<QString,Machine *> ResmedList;
bool LoadEVE(Session *sess,EDFParser &edf);
bool LoadBRP(Session *sess,EDFParser &edf);
bool LoadSAD(Session *sess,EDFParser &edf);
bool LoadPLD(Session *sess,EDFParser &edf);
//! \brief Parse the EVE Event annotation data, and save to Session * sess
//! This contains all Hypopnea, Obstructive Apnea, Central and Apnea codes
bool LoadEVE(Session *sess,EDFParser &edf);
//! \brief Parse the BRP High Resolution data, and save to Session * sess
//! This contains Flow Rate, Mask Pressure, and Resp. Event data
bool LoadBRP(Session *sess,EDFParser &edf);
//! \brief Parse the SAD Pulse oximetry attachment data, and save to Session * sess
//! This contains Pulse Rate and SpO2 Oxygen saturation data
bool LoadSAD(Session *sess,EDFParser &edf);
//! \brief Parse the PRD low resolution data, and save to Session * sess
//! This contains the Pressure, Leak, Respiratory Rate, Minute Ventilation, Tidal Volume, etc..
bool LoadPLD(Session *sess,EDFParser &edf);
};
#endif // RESMED_LOADER_H

View File

@ -14,6 +14,9 @@ const QString zeo_class_name="CMS50";
const int zeo_data_version=1;
/*! \class ZEOLoader
\brief Unfinished stub for loading ZEO Personal Sleep Coach data
*/
class ZEOLoader : public MachineLoader
{
public:
@ -22,8 +25,8 @@ class ZEOLoader : public MachineLoader
virtual int Open(QString & path,Profile *profile);
static void Register();
virtual int Version() { return zeo_data_version; };
virtual const QString & ClassName() { return zeo_class_name; };
virtual int Version() { return zeo_data_version; }
virtual const QString & ClassName() { return zeo_class_name; }
Machine *CreateMachine(Profile *profile);

View File

@ -41,12 +41,17 @@ class SaveThread:public QThread
Q_OBJECT
public:
SaveThread(Machine *m,QString p) { machine=m; path=p; }
//! \brief Static millisecond sleep function.. Can be used from anywhere
static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
//! \brief Start Save processing thread running
virtual void run();
protected:
Machine *machine;
QString path;
signals:
//! \brief Signal sent to update the Progress Bar
void UpdateProgress(int i);
};
@ -177,6 +182,8 @@ public:
protected:
};
// This should probably move somewhere else
//! \fn timezoneOffset();
//! \brief Calculate the timezone Offset in milliseconds between system timezone and UTC
qint64 timezoneOffset();

View File

@ -21,7 +21,10 @@ typedef long SessionID;
typedef float EventDataType;
typedef qint16 EventStoreType;
//! \brief Exception class for out of Bounds error.. Unused.
class BoundsError {};
//! \brief Exception class for to trap old database versions.
class OldDBVersion {};
const quint32 magic=0xC73216AB; // Magic number for Sleepyhead Data Files.. Don't touch!
@ -29,16 +32,29 @@ const quint32 magic=0xC73216AB; // Magic number for Sleepyhead Data Files.. Don'
//const int max_number_event_fields=10;
/*! \enum SummaryType
\brief Calculation method to select from dealing with summary information
*/
enum SummaryType { ST_CNT, ST_SUM, ST_AVG, ST_WAVG, ST_90P, ST_MIN, ST_MAX, ST_CPH, ST_SPH, ST_FIRST, ST_LAST, ST_HOURS, ST_SESSIONS, ST_SETMIN, ST_SETAVG, ST_SETMAX, ST_SETWAVG, ST_SETSUM };
/*! \enum MachineType
\brief Generalized type of a machine
*/
enum MachineType { MT_UNKNOWN=0,MT_CPAP,MT_OXIMETER,MT_SLEEPSTAGE,MT_JOURNAL };
//void InitMapsWithoutAwesomeInitializerLists();
/*! \enum CPAPMode
\brief CPAP Machines mode of operation
*/
enum CPAPMode//:short
{
MODE_UNKNOWN=0,MODE_CPAP,MODE_APAP,MODE_BIPAP,MODE_ASV
};
/*! \enum PRTypes
\brief Pressure Relief Types, used by CPAP machines
*/
enum PRTypes//:short
{
PR_UNKNOWN=0,PR_NONE,PR_CFLEX,PR_CFLEXPLUS,PR_AFLEX,PR_BIFLEX,PR_EPR,PR_SMARTFLEX
@ -49,10 +65,14 @@ enum PRTypes//:short
//extern map<PRTypes,QString> PressureReliefNames;
//extern map<CPAPMode,QString> CPAPModeNames;
// These are types supported by wxVariant class. To retain compatability, add to the end of this list only..
/*! \enum MCDataType
\brief Data Types stored by Profile/Preferences objects, etc..
*/
enum MCDataType
{ MC_bool=0, MC_int, MC_long, MC_float, MC_double, MC_string, MC_datetime };
// This all needs replacing with actual integer codes.. There will likely be a big speedup when this happens again.
const QString CPAP_IPAP="IPAP";
const QString CPAP_IPAPLo="IPAPLo";
const QString CPAP_IPAPHi="IPAPHi";

View File

@ -12,6 +12,9 @@ License: GPL
#include "profiles.h"
#include "machine.h"
/*! \class MachineLoader
\brief Base class to derive a new Machine importer from
*/
class MachineLoader
{
public:
@ -21,8 +24,13 @@ public:
//virtual Machine * CreateMachine() {};
virtual int Open(QString &,Profile *)=0; // Scans for new content
//! \brief Override this to scan path and detect new machine data
virtual int Open(QString & path,Profile *)=0; // Scans for new content
//! \brief Override to returns the Version number of this MachineLoader
virtual int Version()=0;
//! \brief Override to returns the class name of this MachineLoader
virtual const QString & ClassName()=0;
@ -47,12 +55,14 @@ public:
virtual bool SaveWaveform(Machine * m, QString & filename);*/
protected:
//! \brief Contains a list of Machine records known by this loader
QList<Machine *> m_machlist;
QString m_class;
MachineType m_type;
Profile * m_profile;
};
// Put in machine loader class as static??
void RegisterLoader(MachineLoader *loader);
void DestroyLoaders();
QList<MachineLoader *> GetLoaders();

View File

@ -41,7 +41,9 @@ enum ScopeType {
class Channel;
extern Channel EmptyChannel;
// this is really a channel definition.
/*! \class Channel
\brief Contains information about a SleepLib data Channel (aka signals)
*/
class Channel
{
public:
@ -81,19 +83,29 @@ protected:
int m_link;
};
/*! \class ChannelList
\brief A list containing a group of Channel objects, and XML storage and retrieval capability
*/
class ChannelList
{
public:
ChannelList();
virtual ~ChannelList();
//! \brief Loads Channel list from XML file specified by filename
bool Load(QString filename);
//! \brief Stores Channel list to XML file specified by filename
bool Save(QString filename);
Channel & operator[](int i) {
if (channels.contains(i))
return *channels[i];
//! \brief Looks up Channel in this List with the index idx, returns EmptyChannel if not found
Channel & operator[](int idx) {
if (channels.contains(idx))
return *channels[idx];
else
return EmptyChannel;
}
//! \brief Looks up Channel from this list by name, returns Empty Channel if not found.
Channel & operator[](QString name) {
if (names.contains(name))
return *names[name];
@ -101,17 +113,24 @@ public:
return EmptyChannel;
}
//! \brief Channel List indexed by integer ID
QHash<int,Channel *> channels;
//! \brief Channel List index by name
QHash<QString,Channel *> names;
//! \brief Channel List indexed by group
QHash<QString,QHash<QString,Channel *> > groups;
QString m_doctype;
};
extern ChannelList channel;
enum LayerType {
/*enum LayerType {
UnspecifiedLayer, Waveform, Flag, Overlay, Group
};
// ?????
class Layer
{
public:
@ -196,7 +215,7 @@ public:
Graph *addGraph(Graph *graph) { m_graphs.push_back(graph); return graph; }
protected:
QVector<Graph *>m_graphs;
};
}; */
void init();

View File

@ -56,12 +56,10 @@ SOURCES += main.cpp\
Graphs/gFlagsLine.cpp \
Graphs/glcommon.cpp \
Graphs/gSegmentChart.cpp \
Graphs/gSessionTime.cpp \
qextserialport/qextserialport.cpp \
preferencesdialog.cpp \
Graphs/gGraphView.cpp \
Graphs/gStatsLine.cpp \
report.cpp \
Graphs/gSummaryChart.cpp \
SleepLib/schema.cpp \
profileselect.cpp \
@ -126,7 +124,6 @@ HEADERS += \
Graphs/gFlagsLine.h \
Graphs/glcommon.h \
Graphs/gSegmentChart.h\
Graphs/gSessionTime.h \
SleepLib/loader_plugins/resmed_loader.h \
qextserialport/qextserialport_global.h \
qextserialport/qextserialport.h \
@ -134,7 +131,6 @@ HEADERS += \
preferencesdialog.h \
Graphs/gGraphView.h \
Graphs/gStatsLine.h \
report.h \
Graphs/gSummaryChart.h \
SleepLib/schema.h \
profileselect.h \
@ -179,7 +175,6 @@ RESOURCES += \
OTHER_FILES += \
docs/index.html \
docs/usage.html \
docs/template_overview.sht \
docs/schema.xml \
docs/graphs.xml \
docs/channels.xml \

View File

@ -1,52 +0,0 @@
<html>
<head>
<style type='text/css'>
p,a,td,body { font-family: 'Serif'; }
p,a,td,body { font-size: 14px; }
</style>
</head>
<body leftmargin=0 rightmargin=0 topmargin=0 marginwidth=0 marginheight=0>
<table width="100%" cellpadding=0 cellspacing=0>
<tr>
<td valign="top"><h2>CPAP Overview</h2>
<table cell_padding=0 cell_spacing=0 rules=cols border=1>
<tr>
<td valign="middle" width=50%>
<table rules="none" border=0 cell_padding=0 cell_spacing=0 width="100%">
<tr><td>Name:</td><td>{{profile.FirstName}} {{profile.LastName}}</td></tr>
<tr><td valign="top">Address:</td><td valign="top">{{profile.Address}}</td></tr>
<tr><td>Phone:</td><td>{{profile.Phone}}</td></tr>
<tr><td>Email:</td><td>{{profile.EmailAddress}}</td></tr>
</table></td>
<td valign="middle" width="50%">
<table width="100%" height="100%" rules="none" border=0>
<tr><td>Gender:</td><td>{{profile.Gender}}</td></tr>
<tr><td>Age:</td><td>{{local.Age}} years</td></tr>
<tr><td>Height:</td><td>{{profile.Height}}{{local.DistanceMeasure}}</td></tr>
</table>
</td>
</tr>
</table>
<td valign="middle" align="center">
{{logo}}
<br/>SleepyHead v{{pref.VersionString}}
<br/>http://sleepyhead.sf.net
</td>
</tr>
<tr>
<td colspan=2>
Reporting from <b>{{local.start}}</b> to <b>{{local.end}}</b>
<hr width="100%">
</td>
</tr>
</table>
&nbsp;<br/>
{{graph.AHI}}
{{graph.Usage}}
{{graph.Leaks}}
{{graph.Pressure}}
{{graph.Settings}}
{{graph.Sessions}}
{{graph.%PB}}
</body>
</html>

View File

@ -14,6 +14,11 @@ namespace Ui {
class ExportCSV;
}
/*! \class ExportCSV
\brief Dialog for exporting SleepLib data in CSV Format
This module still needs a lot of work
*/
class ExportCSV : public QDialog
{
Q_OBJECT

View File

@ -20,7 +20,6 @@
#include "Graphs/gXAxis.h"
#include "Graphs/gLineChart.h"
#include "Graphs/gYAxis.h"
#include "Graphs/gSessionTime.h"
#include "mainwindow.h"
extern MainWindow * mainwin;

View File

@ -30,11 +30,14 @@ public:
MySortFilterProxyModel(QObject *parent = 0);
protected:
//! \brief Simply extends filterAcceptRow to scan children as well
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
};
//! \note MaksProfile in still a work in progress
/*! \struct MaskProfile
\brief This in still a work in progress, and may be used in Unintentional leaks calculations.
*/
struct MaskProfile {
QString name;
EventDataType pflow[5][2];
@ -53,9 +56,10 @@ public:
explicit PreferencesDialog(QWidget *parent, Profile * _profile);
~PreferencesDialog();
/*! \fn Save()
\brief Save the current preferences, called when Ok button is clicked on */
//! \brief Save the current preferences, called when Ok button is clicked on
void Save();
//! \brief Updates the date text of the last time updates where checked
void RefreshLastChecked();
private slots:
@ -83,7 +87,9 @@ private slots:
void on_maskTypeCombo_activated(int index);
private:
//! \brief Populates the Graph Model view with data from the Daily, Overview & Oximetry gGraphView objects
void resetGraphModel();
Ui::PreferencesDialog *ui;
Profile * profile;
QHash<int,QColor> m_new_colors;
@ -92,7 +98,6 @@ private:
MySortFilterProxyModel *graphFilterModel;
QStandardItemModel *graphModel;
QHash<QString,Preference> general;
};

View File

@ -1,274 +0,0 @@
/*
Report Module Implementation
Copyright (c)2011 Mark Watkins <jedimark@users.sourceforge.net>
License: GPL
*/
#include "report.h"
#include "ui_report.h"
#include <QMessageBox>
#include <QBuffer>
#include <Graphs/gYAxis.h>
#include <Graphs/gXAxis.h>
//#include <QTimer>
#include <QPrinter>
#include <QPrintDialog>
#include <QRegExp>
#include <QFile>
#include <QDir>
Report::Report(QWidget *parent, gGraphView * shared, Overview * overview) :
QWidget(parent),
ui(new Ui::Report),
m_overview(overview)
{
ui->setupUi(this);
GraphView=new gGraphView(this,shared);
setMaximumSize(graph_print_width,800);
setMinimumSize(graph_print_width,800);
GraphView->setMaximumSize(graph_print_width,graph_print_height);
GraphView->setMinimumSize(graph_print_width,graph_print_height);
GraphView->hide();
// Reusing the layer data from overview screen,
// (Can't reuse the graphs objects without breaking things)
graphs["Usage"]=UC=new gGraph(GraphView,"Usage","",graph_print_height,0);
UC->AddLayer(m_overview->uc);
graphs["AHI"]=AHI=new gGraph(GraphView,"AHI","",graph_print_height,0);
AHI->AddLayer(m_overview->bc);
graphs["Pressure"]=PR=new gGraph(GraphView,"Pressure","",graph_print_height,0);
PR->AddLayer(m_overview->pr);
graphs["Leaks"]=LK=new gGraph(GraphView,"Leaks","",graph_print_height,0);
LK->AddLayer(m_overview->lk);
graphs["%PB"]=NPB=new gGraph(GraphView,"% in PB","",graph_print_height,0);
NPB->AddLayer(m_overview->npb);
graphs["Settings"]=SET=new gGraph(GraphView,"Settings","",graph_print_height,0);
SET->AddLayer(m_overview->set);
graphs["Sessions"]=SES=new gGraph(GraphView,"Sessions","",graph_print_height,0);
SES->AddLayer(m_overview->ses);
for (QHash<QString,gGraph *>::iterator g=graphs.begin();g!=graphs.end();g++) {
gGraph *gr=g.value();
gr->AddLayer(new gYAxis(),LayerLeft,gYAxis::Margin);
gXAxis *gx=new gXAxis();
gx->setUtcFix(true);
gr->AddLayer(gx,LayerBottom,0,gXAxis::Margin);
gr->AddLayer(new gXGrid());
}
GraphView->hideSplitter();
m_ready=false;
}
Report::~Report()
{
GraphView->trashGraphs();
for (QHash<QString,gGraph *>::iterator g=graphs.begin();g!=graphs.end();g++) {
delete g.value();
}
delete ui;
}
void Report::ReloadGraphs()
{
for (QHash<QString,gGraph *>::iterator g=graphs.begin();g!=graphs.end();g++) {
g.value()->setDay(NULL);
}
startDate=PROFILE.FirstDay();
endDate=PROFILE.LastDay();
for (QHash<QString,gGraph *>::iterator g=graphs.begin();g!=graphs.end();g++) {
g.value()->ResetBounds();
}
m_ready=true;
}
QPixmap Report::Snapshot(gGraph * graph)
{
QDateTime d1(startDate,QTime(0,0,0),Qt::UTC);
qint64 first=qint64(d1.toTime_t())*1000L;
QDateTime d2(endDate,QTime(23,59,59),Qt::UTC);
qint64 last=qint64(d2.toTime_t())*1000L;
GraphView->trashGraphs();
GraphView->addGraph(graph);
GraphView->ResetBounds();
GraphView->SetXBounds(first,last);
QPixmap pixmap=GraphView->renderPixmap(graph_print_width,graph_print_height,false);
return pixmap;
}
QString Report::ParseTemplate(QString input)
{
QString output;
QRegExp rx("\\{\\{(.*)\\}\\}");
rx.setMinimal(true);
int lastpos=0,pos=0;
while ((pos=rx.indexIn(input,pos))!=-1) {
output+=input.mid(lastpos,pos-lastpos);
QString block=rx.cap(1);
QString code=block.section(".",0,0).toLower();
QString key=block.section(".",1,-1);
QHash<QString,QVariant> * pr=NULL;
if (code=="profile") {
pr=&PROFILE.p_preferences;
} else if (code=="pref") {
pr=&PREF.p_preferences;
} else if (code=="local") {
pr=&locals;
}
QString value;
if (pr) {
if (pr->contains(key)) {
if ((*pr)[key].type()==QVariant::String){
value=(*pr)[key].toString();
value.replace("\n","<br/>");
output+=value;
} else if ((*pr)[key].type()==QVariant::Double){
bool ok;
value=QString::number((*pr)[key].toDouble(&ok),'f',2);
if (ok) output+=value; else output+="[NaN]";
} else if ((*pr)[key].type()==QVariant::Int){
bool ok;
value=QString::number((*pr)[key].toInt(&ok));
if (ok) output+=value; else output+="[NaN]";
} else if ((*pr)[key].type()==QVariant::Date){
value=(*pr)[key].toDate().toString();
output+=value;
} else {
qDebug() << "Unknown key type" << (*pr)[key].typeName() << " in " << code << "." << key << "in template";
}
} else {
qDebug() << "Key not found" << code << "." << key << "in template";
}
} else if (code=="graph") {
if (graphs.contains(key)) {
if (!graphs[key]->isEmpty()) {
QPixmap pixmap=Snapshot(graphs[key]);
QByteArray byteArray;
QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG");
//output += "<div align=center><img src=\"data:image/png;base64," + byteArray.toBase64() + "\" width="+QString::number(graph_print_width)+"px height=\""+QString::number(graph_print_height)+"px\"></div>\n";
output += "<div align=center><img src=\"data:image/png;base64," + byteArray.toBase64() + "\" width=\"100%\" height=\""+QString::number(graph_print_height)+"\"></div>\n";
}
} else {
qDebug() << "Graph not found" << key << "in template";
}
} else if (code=="logo") {
QPixmap pixmap(":/docs/sheep.png");
QByteArray byteArray;
QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG");
output += "<img src=\"data:image/png;base64," + byteArray.toBase64() + "\" width=\"100\" height=\"100\">";
}
pos+=rx.matchedLength();
lastpos=pos;
}
output+=input.mid(lastpos); // will just return the input if no tags are used
return output;
}
QString Report::GenerateReport(QString templ,QDate start, QDate end)
{
//if (!m_ready) return;
startDate=start;
endDate=end;
QString filename=PREF.Get("{home}/Reports");
QDir dir(filename);
if (!dir.exists()) {
dir.mkdir(filename);
}
filename+="/"+templ+".sht";
QFile file;
file.setFileName(filename);
QByteArray input;
if (0) { //file.exists()) {
file.open(QIODevice::ReadOnly);
input=file.readAll();
file.close();
} else {
QString f2=":/docs/template_"+templ+".sht";
file.setFileName(f2);
if (!file.exists()) return "";
file.open(QIODevice::ReadOnly);
input=file.readAll();
file.close();
file.setFileName(filename);
file.open(QIODevice::WriteOnly);
file.write(input);
file.close();
}
QString html=input;
locals["start"]=start;
locals["end"]=end;
locals["width"]=graph_print_width-10;
if (PROFILE.Exists("DOB") && !PROFILE["DOB"].toString().isEmpty()) {
QDate dob=PROFILE["DOB"].toDate();
QDateTime d1(dob,QTime(0,0,0));
QDateTime d2(QDate::currentDate(),QTime(0,0,0));
int years=d1.daysTo(d2)/365.25;
locals["Age"]=years;
}
if (!PROFILE.Exists("UnitSystem")) {
PROFILE["UnitSystem"]="Metric";
}
if (PROFILE.Exists("Height") && !PROFILE["Height"].toString().isEmpty()) {
if (PROFILE["UnitSystem"].toString()=="Metric")
locals["DistanceMeasure"]="cm";
else locals["DistanceMeasure"]=" inches";
}
//QFile file(":/docs/template_overview.sht");
//file.open(QIODevice::ReadOnly);
//QString html=file.readAll();
//ui->webView->setHtml(output);
return ParseTemplate(html);
}
void Report::Print(QString html)
{
if (html.isEmpty()) return;
ui->webView->setHtml(html);
QPrinter printer;
#ifdef Q_WS_X11
printer.setPrinterName("Print to File (PDF)");
printer.setOutputFormat(QPrinter::PdfFormat);
#endif
printer.setPrintRange(QPrinter::AllPages);
printer.setOrientation(QPrinter::Portrait);
//printer.setPaperSize(QPrinter::A4);
printer.setResolution(QPrinter::HighResolution);
//printer.setPageSize();
printer.setFullPage(false); // This has nothing to do with scaling
printer.setNumCopies(1);
printer.setPageMargins(10,10,10,10,QPrinter::Millimeter);
QPrintDialog *dialog = new QPrintDialog(&printer);
//printer.setOutputFileName("printYou.pdf");
if ( dialog->exec() == QDialog::Accepted) {
ui->webView->print(&printer);
}
}

View File

@ -1,55 +0,0 @@
/*
Report Module Header
Copyright (c)2011 Mark Watkins <jedimark@users.sourceforge.net>
License: GPL
*/
#ifndef REPORT_H
#define REPORT_H
#include <QWidget>
#include <QWebView>
#include "SleepLib/profiles.h"
#include "Graphs/gGraphView.h"
#include "overview.h"
namespace Ui {
class Report;
}
const int graph_print_width=1024;
const int graph_print_height=150;
class Daily;
class Overview;
class Report : public QWidget
{
Q_OBJECT
public:
explicit Report(QWidget *parent, gGraphView * shared, Overview * overview);
~Report();
QString GenerateReport(QString templ,QDate start, QDate end);
void ReloadGraphs();
QString ParseTemplate(QString input);
QPixmap Snapshot(gGraph * graph);
void Print(QString html);
private:
Ui::Report *ui;
Overview * m_overview;
gGraphView * shared;
gGraphView * GraphView;
gGraph *AHI,*UC,*PR,*LK,*NPB,*SET,*SES;
SummaryChart *bc,*uc,*pr,*lk,*npb,*set,*ses;
QHash<QString,QVariant> locals;
QHash<QString,gGraph *> graphs;
QDate startDate;
QDate endDate;
bool m_ready;
};
#endif // REPORT_H