Merge branch 'master' into vAuto-Settings

This commit is contained in:
Phil Olynyk 2022-11-19 02:28:15 -05:00
commit be7d95c924
26 changed files with 3512 additions and 3190 deletions

File diff suppressed because it is too large Load Diff

View File

@ -257,7 +257,7 @@ In verband met de koppeling met Bladwijzers, lijkt me 'Notities' beter
<message> <message>
<location filename="../oscar/daily.ui" line="1492"/> <location filename="../oscar/daily.ui" line="1492"/>
<source>Flags</source> <source>Flags</source>
<translation>Markeringen</translation> <translation>Incident markeringen</translation>
</message> </message>
<message> <message>
<location filename="../oscar/daily.ui" line="1544"/> <location filename="../oscar/daily.ui" line="1544"/>
@ -3091,7 +3091,7 @@ anders is het geen AHI/uur meer.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="706"/> <location filename="../oscar/preferencesdialog.ui" line="706"/>
<source>CPAP Clock Drift</source> <source>CPAP Clock Drift</source>
<translation>Correctie afwijking klok CPAP</translation> <translation>Correctie afwijking CPAP-klok</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="502"/> <location filename="../oscar/preferencesdialog.ui" line="502"/>
@ -3316,7 +3316,7 @@ Werkt vooral bij importeren.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="1193"/> <location filename="../oscar/preferencesdialog.ui" line="1193"/>
<source>Enable Unknown Events Channels</source> <source>Enable Unknown Events Channels</source>
<translation>Zet de kanalen van onbekende gebeurtenissen aan</translation> <translation>Kanalen van onbekende gebeurtenissen aan</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="1323"/> <location filename="../oscar/preferencesdialog.ui" line="1323"/>
@ -3554,7 +3554,7 @@ Als U een nieuwe computer met SSD hebt, is dit een goede keuze.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2637"/> <location filename="../oscar/preferencesdialog.ui" line="2637"/>
<source>Overview Linecharts</source> <source>Overview Linecharts</source>
<translation>Overzicht lijngrafieken</translation> <translation>Soort grafieken in overzicht</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2582"/> <location filename="../oscar/preferencesdialog.ui" line="2582"/>
@ -3569,7 +3569,7 @@ Als U een nieuwe computer met SSD hebt, is dit een goede keuze.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2384"/> <location filename="../oscar/preferencesdialog.ui" line="2384"/>
<source>Overlay Flags</source> <source>Overlay Flags</source>
<translation>Markeringen</translation> <translation>Incident markeringen</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2410"/> <location filename="../oscar/preferencesdialog.ui" line="2410"/>
@ -3581,7 +3581,7 @@ Als U een nieuwe computer met SSD hebt, is dit een goede keuze.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2415"/> <location filename="../oscar/preferencesdialog.ui" line="2415"/>
<source>Standard Bars</source> <source>Standard Bars</source>
<translation>Standaardbalken</translation> <translation>Lange markeringen</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2368"/> <location filename="../oscar/preferencesdialog.ui" line="2368"/>
@ -3636,7 +3636,7 @@ Als U een nieuwe computer met SSD hebt, is dit een goede keuze.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="810"/> <location filename="../oscar/preferencesdialog.ui" line="810"/>
<source>Calculate Unintentional Leaks When Not Present</source> <source>Calculate Unintentional Leaks When Not Present</source>
<translation>Bereken de onbedoelde lekkage als deze niet door het apparaat gegeven wordt</translation> <translation>Bereken de onbedoelde lekkage als deze niet gegeven wordt</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="906"/> <location filename="../oscar/preferencesdialog.ui" line="906"/>
@ -3656,7 +3656,7 @@ Als U een nieuwe computer met SSD hebt, is dit een goede keuze.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2454"/> <location filename="../oscar/preferencesdialog.ui" line="2454"/>
<source>Tooltip Timeout</source> <source>Tooltip Timeout</source>
<translation>Tooltip timeout</translation> <translation>Tijdsduur tooltips</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2534"/> <location filename="../oscar/preferencesdialog.ui" line="2534"/>
@ -3666,7 +3666,7 @@ Als U een nieuwe computer met SSD hebt, is dit een goede keuze.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2420"/> <location filename="../oscar/preferencesdialog.ui" line="2420"/>
<source>Top Markers</source> <source>Top Markers</source>
<translation>Top markeringen</translation> <translation>Aanduiding aan bovenzijde</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="423"/> <location filename="../oscar/preferencesdialog.ui" line="423"/>
@ -3681,7 +3681,7 @@ Als U een nieuwe computer met SSD hebt, is dit een goede keuze.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="618"/> <location filename="../oscar/preferencesdialog.ui" line="618"/>
<source>Automatically load last used profile on start-up</source> <source>Automatically load last used profile on start-up</source>
<translation>Laadt automatisch het laatste profiel bij opstarten</translation> <translation>Laad automatisch het laatste profiel bij opstarten</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="645"/> <location filename="../oscar/preferencesdialog.ui" line="645"/>
@ -3775,7 +3775,7 @@ Als U een nieuwe computer met SSD hebt, is dit een goede keuze.</translation>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2319"/> <location filename="../oscar/preferencesdialog.ui" line="2319"/>
<source>Switch Tabs</source> <source>Switch Tabs</source>
<translation>Wissel tabbladen</translation> <translation>Kies tabbladen</translation>
</message> </message>
<message> <message>
<location filename="../oscar/preferencesdialog.ui" line="2330"/> <location filename="../oscar/preferencesdialog.ui" line="2330"/>

184
oscar/Graphs/gAHIChart.cpp Normal file
View File

@ -0,0 +1,184 @@
/* gAHUChart Implementation
*
* Copyright (c) 2019-2022 The Oscar Team
* Copyright (c) 2011-2018 Mark Watkins <mark@jedimark.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code
* for more details. */
#define TEST_MACROS_ENABLEDoff
#include "test_macros.h"
#include <math.h>
#include <QLabel>
#include <QDateTime>
#include "mainwindow.h"
#include "SleepLib/profiles.h"
#include "SleepLib/machine_common.h"
#include "gAHIChart.h"
#include "gYAxis.h"
extern MainWindow * mainwin;
//extern short SummaryCalcItem::midcalc;
////////////////////////////////////////////////////////////////////////////
/// AHI Chart Stuff
////////////////////////////////////////////////////////////////////////////
void gAHIChart::preCalc()
{
gSummaryChart::preCalc();
ahi_wavg = 0;
ahi_avg = 0;
total_days = 0;
total_hours = 0;
min_ahi = 99999;
max_ahi = -99999;
ahi_data.clear();
ahi_data.reserve(idx_end-idx_start);
}
void gAHIChart::customCalc(Day *day, QVector<SummaryChartSlice> &list)
{
int size = list.size();
if (size == 0) return;
EventDataType hours = day->hours(m_machtype);
EventDataType ahi_cnt = 0;
for (auto & slice : list) {
SummaryCalcItem * calc = slice.calc;
EventDataType value = slice.value;
float valh = value/ hours;
switch (midcalc) {
case 0:
calc->median_data.append(valh);
break;
case 1:
calc->wavg_sum += value;
calc->divisor += hours;
break;
case 2:
calc->avg_sum += value;
calc->cnt++;
break;
}
calc->min = qMin(valh, calc->min);
calc->max = qMax(valh, calc->max);
ahi_cnt += value;
}
min_ahi = qMin(ahi_cnt / hours, min_ahi);
max_ahi = qMax(ahi_cnt / hours, max_ahi);
ahi_data.append(ahi_cnt / hours);
ahi_wavg += ahi_cnt;
ahi_avg += ahi_cnt;
total_hours += hours;
total_days++;
}
void gAHIChart::afterDraw(QPainter & /*painter */, gGraph &graph, QRectF rect)
{
if (totaldays == nousedays) return;
//int size = idx_end - idx_start;
bool skip = true;
float med = 0;
switch (midcalc) {
case 0:
if (ahi_data.size() > 0) {
med = median(ahi_data.begin(), ahi_data.end());
skip = false;
}
break;
case 1: // wavg
if (total_hours > 0) {
med = ahi_wavg / total_hours;
skip = false;
}
break;
case 2: // avg
if (total_days > 0) {
med = ahi_avg / total_days;
skip = false;
}
break;
}
QStringList txtlist;
if (!skip) txtlist.append(QString("%1 %2 / %3 / %4").arg(STR_TR_AHI).arg(min_ahi, 0, 'f', 2).arg(med, 0, 'f', 2).arg(max_ahi, 0, 'f', 2));
int i = calcitems.size();
while (i > 0) {
i--;
ChannelID code = calcitems[i].code;
schema::Channel & chan = schema::channel[code];
float mid = 0;
skip = true;
switch (midcalc) {
case 0:
if (calcitems[i].median_data.size() > 0) {
mid = median(calcitems[i].median_data.begin(), calcitems[i].median_data.end());
skip = false;
}
break;
case 1:
if (calcitems[i].divisor > 0) {
mid = calcitems[i].wavg_sum / calcitems[i].divisor;
skip = false;
}
break;
case 2:
if (calcitems[i].cnt > 0) {
mid = calcitems[i].avg_sum / calcitems[i].cnt;
skip = false;
}
break;
}
if (!skip) txtlist.append(QString("%1 %2 / %3 / %4").arg(chan.label()).arg(calcitems[i].min, 0, 'f', 2).arg(mid, 0, 'f', 2).arg(calcitems[i].max, 0, 'f', 2));
}
QString txt = txtlist.join(", ");
graph.renderText(txt, rect.left(), rect.top()-5*graph.printScaleY(), 0);
}
void gAHIChart::populate(Day *day, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
float hours = day->hours(m_machtype);
for (auto & calc : calcitems) {
ChannelID code = calc.code;
if (!day->hasData(code, ST_CNT)) continue;
schema::Channel *chan = schema::channel.channels.find(code).value();
float c = day->count(code);
slices.append(SummaryChartSlice(&calc, c, c / hours, chan->label(), calc.color));
}
}
QString gAHIChart::tooltipData(Day *day, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
float total = 0;
float hour = day->hours(m_machtype);
QString txt;
int i = slices.size();
while (i > 0) {
i--;
total += slices[i].value;
txt += QString("\n%1: %2").arg(slices[i].name).arg(float(slices[i].value) / hour, 0, 'f', 2);
}
return QString("\n%1: %2").arg(STR_TR_AHI).arg(float(total) / hour,0,'f',2)+txt;
}

78
oscar/Graphs/gAHIChart.h Normal file
View File

@ -0,0 +1,78 @@
/* gAHIChart Header
*
* Copyright (c) 2019-2022 The Oscar Team
* Copyright (C) 2011-2018 Mark Watkins <mark@jedimark.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code
* for more details. */
#ifndef GAHICHART_H
#define GAHICHART_H
#include "SleepLib/day.h"
#include "SleepLib/profiles.h"
#include "Graphs/gGraphView.h"
#include "Graphs/gSummaryChart.h"
class gAHIChart : public gSummaryChart
{
public:
gAHIChart()
:gSummaryChart("AHIChart", MT_CPAP) {
for (int i = 0; i < ahiChannels.size(); i++)
addCalc(ahiChannels.at(i), ST_CPH);
// addCalc(CPAP_ClearAirway, ST_CPH);
// addCalc(CPAP_AllApnea, ST_CPH);
// addCalc(CPAP_Obstructive, ST_CPH);
// addCalc(CPAP_Apnea, ST_CPH);
// addCalc(CPAP_Hypopnea, ST_CPH);
if (p_profile->general->calculateRDI())
addCalc(CPAP_RERA, ST_CPH);
}
virtual ~gAHIChart() {}
virtual void preCalc();
virtual void customCalc(Day *, QVector<SummaryChartSlice> &);
virtual void afterDraw(QPainter &, gGraph &, QRectF);
virtual void populate(Day *, int idx);
virtual QString tooltipData(Day * day, int);
virtual Layer * Clone() {
gAHIChart * sc = new gAHIChart();
gSummaryChart::CloneInto(sc);
CloneInto(sc);
return sc;
}
void CloneInto(gAHIChart * /* layer */) {
// layer->ahicalc = ahicalc;
// layer->ahi_wavg = ahi_wavg;
// layer->ahi_avg = ahi_avg;
// layer->total_hours = total_hours;
// layer->max_ahi = max_ahi;
// layer->min_ahi = min_ahi;
// layer->total_days = total_days;
// layer->ahi_data = ahi_data;
}
// SummaryCalcItem ahicalc;
double ahi_wavg;
double ahi_avg;
double total_hours;
float max_ahi;
float min_ahi;
int total_days;
QList<float> ahi_data;
};
#endif // GAHICHART_H

View File

@ -38,8 +38,10 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "Graphs/glcommon.h" #include "Graphs/glcommon.h"
#include "Graphs/gLineChart.h" #include "Graphs/gLineChart.h"
#ifndef REMOVE_FITNESS
#include "Graphs/gOverviewGraph.h"
#endif
#include "Graphs/gSummaryChart.h" #include "Graphs/gSummaryChart.h"
#include "Graphs/gSessionTimesChart.h"
#include "Graphs/gYAxis.h" #include "Graphs/gYAxis.h"
#include "Graphs/gFlagsLine.h" #include "Graphs/gFlagsLine.h"
#include "SleepLib/profiles.h" #include "SleepLib/profiles.h"
@ -2199,11 +2201,18 @@ void gGraphView::populateMenu(gGraph * graph)
font.setPointSize(font.pointSize() + 3); font.setPointSize(font.pointSize() + 3);
gLineChart * lc = dynamic_cast<gLineChart *>(findLayer(graph,LT_LineChart)); gLineChart * lc = dynamic_cast<gLineChart *>(findLayer(graph,LT_LineChart));
SummaryChart * sc = dynamic_cast<SummaryChart *>(findLayer(graph,LT_SummaryChart)); #ifndef REMOVE_FITNESS
gOverviewGraph * sc = dynamic_cast<gOverviewGraph *>(findLayer(graph,LT_SummaryChart));
#endif
gSummaryChart * stg = dynamic_cast<gSummaryChart *>(findLayer(graph,LT_Overview)); gSummaryChart * stg = dynamic_cast<gSummaryChart *>(findLayer(graph,LT_Overview));
limits_menu->clear(); limits_menu->clear();
if (lc || sc || stg) { #ifndef REMOVE_FITNESS
if (lc || sc || stg )
#else
if (lc || stg )
#endif
{
QWidgetAction * widget = new QWidgetAction(this); QWidgetAction * widget = new QWidgetAction(this);
MinMaxWidget * minmax = new MinMaxWidget(graph, this); MinMaxWidget * minmax = new MinMaxWidget(graph, this);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,170 @@
/* gOverviewGraph Header
*
* Copyright (c) 2019-2022 The OSCAR Team
* Copyright (C) 2011-2018 Mark Watkins <mark@jedimark.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code
* for more details. */
#ifndef GOVERVIEWGRAPH_H
#define GOVERVIEWGRAPH_H
#include <SleepLib/profiles.h>
#include "gGraphView.h"
#include "gXAxis.h"
#include "SleepLib/appsettings.h"
#ifndef REMOVE_FITNESS
/*
BMI, Weight and Zombie graphs are are hard coded in Overview.cpp
These graph require special handling in class gOverviewGraph.
Currently there are 4 graphs types, one of which is not used.
GT_BAR Used by CPAP graph to make bar graphs for each day
GT_LINE ? Used for making a line ?
GT_POINT ? Used to display points instead of lines ?
GT_SESSION ?? NOT USED.
BMI, Weight and Zombie graphs current use GT_LINE and not GT_BAR
The Overview Linecharts preference allows points to be displayed instead of lines.
*/
/*! \enum GraphType
\value GT_BAR Display as a BarGraph
\value GT_LINE Display as a line plot
*/
enum GraphType { GT_BAR, GT_LINE, GT_POINTS , GT_SESSIONS };
/*! \class gOverviewGraph
\brief The main overall chart type layer used in Overview page
*/
class gOverviewGraph: public Layer
{
public:
//! \brief Constructs a gOverviewGraph with QString label, of GraphType type
gOverviewGraph(QString label, GraphType type = GT_BAR);
virtual ~gOverviewGraph();
//! \brief Renders the graph to the QPainter object
virtual void paint(QPainter &painter, gGraph &w, const QRegion &region);
//! \brief Precalculation code prior to drawing. Day object is not needed here, it's just here for Layer compatability.
virtual void SetDay(Day *day = nullptr);
//! \brief Returns true if no data was found for this day during SetDay
virtual bool isEmpty() { return m_empty; }
//! \brief Adds a layer to the gOverviewGraph (When in Bar mode, it becomes culminative, eg, the AHI chart)
void addSlice(ChannelID code, QColor color, SummaryType type, EventDataType tval = 0.00f) {
m_codes.push_back(code);
m_colors.push_back(color);
m_type.push_back(type);
//m_zeros.push_back(ignore_zeros);
m_typeval.push_back(tval);
}
//! \brief Deselect highlighting (the gold bar)
virtual void deselect() {
hl_day = -1;
}
//! \brief Returns true if currently selected..
virtual bool isSelected() { return hl_day >= 0; }
//! \brief Sets the MachineType this gOverviewGraph is interested in
void setMachineType(MachineType type) { m_machinetype = type; }
//! \brief Returns the MachineType this gOverviewGraph is interested in
MachineType machineType() { return m_machinetype; }
virtual Layer * Clone() {
gOverviewGraph * sc = new gOverviewGraph(m_label);
Layer::CloneInto(sc);
CloneInto(sc);
return sc;
}
void CloneInto(gOverviewGraph * layer) {
layer->m_orientation = m_orientation;
layer->m_colors = m_colors;
layer->m_codes = m_codes;
layer->m_goodcodes = m_goodcodes;
layer->m_type = m_type;
layer->m_typeval = m_typeval;
layer->m_values = m_values;
layer->m_times = m_times;
layer->m_hours = m_hours;
layer->m_days = m_days;
layer->m_empty = m_empty;
layer->m_fday = m_fday;
layer->m_label = m_label;
layer->barw = barw;
layer->l_offset = l_offset;
layer->offset = offset;
layer->l_left = l_left;
layer->l_top = l_top;
layer->l_width= l_width;
layer->l_height = l_height;
layer->rtop = rtop;
layer->l_minx = l_minx;
layer->l_maxx = l_maxx;
layer->hl_day = hl_day;
layer->m_graphtype = m_graphtype;
layer->m_machinetype = m_machinetype;
layer->tz_offset = tz_offset;
layer->tz_hours = tz_hours;
}
protected:
Qt::Orientation m_orientation;
QVector<QColor> m_colors;
QVector<ChannelID> m_codes;
QVector<bool> m_goodcodes;
//QVector<bool> m_zeros;
QVector<SummaryType> m_type;
QVector<EventDataType> m_typeval;
QHash<int, QMap<short, EventDataType> > m_values;
QHash<int, QMap<short, EventDataType> > m_times;
QHash<int, EventDataType> m_hours;
QHash<int, Day *> m_days;
bool m_empty;
int m_fday;
QString m_label;
float barw; // bar width from last draw
qint64 l_offset; // last offset
float offset; // in pixels;
int l_left, l_top, l_width, l_height;
int rtop;
qint64 l_minx, l_maxx;
int hl_day;
//gGraph *graph;
GraphType m_graphtype;
MachineType m_machinetype;
int tz_offset;
float tz_hours;
//! \brief Key was pressed that effects this layer
virtual bool keyPressEvent(QKeyEvent *event, gGraph *graph);
//! \brief Mouse moved over this layers area (shows the hover-over tooltips here)
virtual bool mouseMoveEvent(QMouseEvent *event, gGraph *graph);
//! \brief Mouse Button was pressed over this area
virtual bool mousePressEvent(QMouseEvent *event, gGraph *graph);
//! \brief Mouse Button was released over this area. (jumps to daily view here)
virtual bool mouseReleaseEvent(QMouseEvent *event, gGraph *graph);
};
#endif
#endif // GOVERVIEWGRAPH_H

View File

@ -10,7 +10,7 @@
#ifndef GPRESSURECHART_H #ifndef GPRESSURECHART_H
#define GPRESSURECHART_H #define GPRESSURECHART_H
#include "gSessionTimesChart.h" #include "gSummaryChart.h"
class gPressureChart : public gSummaryChart class gPressureChart : public gSummaryChart
{ {

View File

@ -23,746 +23,7 @@
extern MainWindow * mainwin; extern MainWindow * mainwin;
short SummaryCalcItem::midcalc; /// short SummaryCalcItem::midcalc;
gSummaryChart::gSummaryChart(QString label, MachineType machtype)
:Layer(NoChannel), m_label(label), m_machtype(machtype)
{
m_layertype = LT_Overview;
QDateTime d1 = QDateTime::currentDateTime();
QDateTime d2 = d1;
d1.setTimeSpec(Qt::UTC); // CHECK: Does this deal with DST?
tz_offset = d2.secsTo(d1);
tz_hours = tz_offset / 3600.0;
expected_slices = 5;
idx_end = 0;
idx_start = 0;
}
gSummaryChart::gSummaryChart(ChannelID code, MachineType machtype)
:Layer(code), m_machtype(machtype)
{
m_layertype = LT_Overview;
QDateTime d1 = QDateTime::currentDateTime();
QDateTime d2 = d1;
d1.setTimeSpec(Qt::UTC); // CHECK: Does this deal with DST?
tz_offset = d2.secsTo(d1);
tz_hours = tz_offset / 3600.0;
expected_slices = 5;
addCalc(code, ST_MIN, brighten(schema::channel[code].defaultColor() ,0.60f));
addCalc(code, ST_MID, brighten(schema::channel[code].defaultColor() ,1.20f));
addCalc(code, ST_90P, brighten(schema::channel[code].defaultColor() ,1.70f));
addCalc(code, ST_MAX, brighten(schema::channel[code].defaultColor() ,2.30f));
idx_end = 0;
idx_start = 0;
}
gSummaryChart::~gSummaryChart()
{
}
void gSummaryChart::SetDay(Day *unused_day)
{
cache.clear();
Q_UNUSED(unused_day)
Layer::SetDay(nullptr);
if (m_machtype != MT_CPAP) {
// Channels' machine types are not terribly reliable: oximetry channels can be reported by a CPAP,
// and position channels can be reported by an oximeter. So look for any days with data.
firstday = p_profile->FirstDay();
lastday = p_profile->LastDay();
} else {
// But CPAP channels (like pressure settings) can only be reported by a CPAP.
firstday = p_profile->FirstDay(m_machtype);
lastday = p_profile->LastDay(m_machtype);
}
dayindex.clear();
daylist.clear();
if (!firstday.isValid() || !lastday.isValid()) return;
// daylist.reserve(firstday.daysTo(lastday)+1);
QDate date = firstday;
int idx = 0;
do {
auto di = p_profile->daylist.find(date);
Day * day = nullptr;
if (di != p_profile->daylist.end()) {
day = di.value();
}
daylist.append(day);
dayindex[date] = idx;
idx++;
date = date.addDays(1);
} while (date <= lastday);
m_minx = QDateTime(firstday, QTime(0,0,0), Qt::LocalTime).toMSecsSinceEpoch();
m_maxx = QDateTime(lastday, QTime(23,59,59), Qt::LocalTime).toMSecsSinceEpoch();
m_miny = 0;
m_maxy = 20;
m_empty = false;
m_emptyPrev = true;
}
int gSummaryChart::addCalc(ChannelID code, SummaryType type, QColor color)
{
calcitems.append(SummaryCalcItem(code, type, color));
return calcitems.size() - 1; // return the index of the newly appended calc
}
int gSummaryChart::addCalc(ChannelID code, SummaryType type)
{
return addCalc(code, type, schema::channel[code].defaultColor());
}
bool gSummaryChart::keyPressEvent(QKeyEvent *event, gGraph *graph)
{
Q_UNUSED(event)
Q_UNUSED(graph)
return false;
}
bool gSummaryChart::mouseMoveEvent(QMouseEvent *event, gGraph *graph)
{
Q_UNUSED(event)
Q_UNUSED(graph)
return false;
}
bool gSummaryChart::mousePressEvent(QMouseEvent *event, gGraph *graph)
{
Q_UNUSED(event)
Q_UNUSED(graph)
return false;
}
bool gSummaryChart::mouseReleaseEvent(QMouseEvent *event, gGraph *graph)
{
if (!(event->modifiers() & Qt::ShiftModifier)) return false;
float x = event->x() - m_rect.left();
float y = event->y() - m_rect.top();
qDebug() << x << y;
EventDataType miny;
EventDataType maxy;
graph->roundY(miny, maxy);
QDate date = QDateTime::fromMSecsSinceEpoch(m_minx, Qt::LocalTime).date();
int days = ceil(double(m_maxx - m_minx) / 86400000.0);
float barw = float(m_rect.width()) / float(days);
float idx = x/barw;
date = date.addDays(idx);
auto it = dayindex.find(date);
if (it != dayindex.end()) {
Day * day = daylist.at(it.value());
if (day) {
mainwin->getDaily()->LoadDate(date);
mainwin->JumpDaily();
}
}
return true;
}
void gSummaryChart::preCalc()
{
midcalc = p_profile->general->prefCalcMiddle();
for (auto & calc : calcitems) {
calc.reset(idx_end - idx_start, midcalc);
}
}
void gSummaryChart::customCalc(Day *day, QVector<SummaryChartSlice> & slices)
{
int size = slices.size();
if (size != calcitems.size()) {
return;
}
float hour = day->hours(m_machtype);
for (int i=0; i < size; ++i) {
const SummaryChartSlice & slice = slices.at(i);
SummaryCalcItem & calc = calcitems[i];
calc.update(slice.value, hour);
}
}
void gSummaryChart::afterDraw(QPainter &painter, gGraph &graph, QRectF rect)
{
if (totaldays == nousedays) return;
if (calcitems.size() == 0) return;
QStringList strlist;
QString txt;
int midcalc = p_profile->general->prefCalcMiddle();
QString midstr;
if (midcalc == 0) {
midstr = QObject::tr("Med.");
} else if (midcalc == 1) {
midstr = QObject::tr("W-Avg");
} else {
midstr = QObject::tr("Avg");
}
float perc = p_profile->general->prefCalcPercentile();
QString percstr = QString("%1%").arg(perc, 0, 'f',0);
schema::Channel & chan = schema::channel[calcitems.at(0).code];
for (auto & calc : calcitems) {
if (calcitems.size() == 1) {
float val = calc.min;
if (val < 99998)
strlist.append(QObject::tr("Min: %1").arg(val,0,'f',2));
}
float mid = 0;
switch (midcalc) {
case 0:
if (calc.median_data.size() > 0) {
mid = median(calc.median_data.begin(), calc.median_data.end());
}
break;
case 1:
mid = calc.wavg_sum / calc.divisor;
break;
case 2:
mid = calc.avg_sum / calc.cnt;
break;
}
float val = 0;
switch (calc.type) {
case ST_CPH:
val = mid;
txt = midstr+": ";
break;
case ST_SPH:
val = mid;
txt = midstr+": ";
break;
case ST_MIN:
val = calc.min;
if (val >= 99998) continue;
txt = QObject::tr("Min: ");
break;
case ST_MAX:
val = calc.max;
if (val <= -99998) continue;
txt = QObject::tr("Max: ");
break;
case ST_SETMIN:
val = calc.min;
if (val >= 99998) continue;
txt = QObject::tr("Min: ");
break;
case ST_SETMAX:
val = calc.max;
if (val <= -99998) continue;
txt = QObject::tr("Max: ");
break;
case ST_MID:
val = mid;
txt = QString("%1: ").arg(midstr);
break;
case ST_90P:
val = mid;
txt = QString("%1: ").arg(percstr);
break;
default:
val = mid;
txt = QString("???: ");
break;
}
strlist.append(QString("%1%2").arg(txt).arg(val,0,'f',2));
if (calcitems.size() == 1) {
val = calc.max;
if (val > -99998)
strlist.append(QObject::tr("Max: %1").arg(val,0,'f',2));
}
}
QString str;
if (totaldays > 1) {
str = QObject::tr("%1 (%2 days): ").arg(chan.fullname()).arg(totaldays);
} else {
str = QObject::tr("%1 (%2 day): ").arg(chan.fullname()).arg(totaldays);
}
str += " "+strlist.join(", ");
QRectF rec(rect.left(), rect.top(), 0,0);
painter.setFont(*defaultfont);
rec = painter.boundingRect(rec, Qt::AlignTop, str);
rec.moveBottom(rect.top()-3*graph.printScaleY());
painter.drawText(rec, Qt::AlignTop, str);
// graph.renderText(str, rect.left(), rect.top()-5*graph.printScaleY(), 0);
}
QString gSummaryChart::tooltipData(Day *, int idx)
{
QString txt;
const auto & slices = cache[idx];
int i = slices.size();
while (i > 0) {
i--;
txt += QString("\n%1: %2").arg(slices[i].name).arg(float(slices[i].value), 0, 'f', 2);
}
return txt;
}
void gSummaryChart::populate(Day * day, int idx)
{
bool good = false;
for (const auto & item : calcitems) {
if (day->hasData(item.code, item.type)) {
good = true;
break;
}
}
if (!good) return;
auto & slices = cache[idx];
float hours = day->hours(m_machtype);
if ((hours==0) && (m_machtype != MT_CPAP)) hours = day->hours();
float base = 0;
for (auto & item : calcitems) {
ChannelID code = item.code;
schema::Channel & chan = schema::channel[code];
float value = 0;
QString name;
QColor color;
switch (item.type) {
case ST_CPH:
value = day->count(code) / hours;
name = chan.label();
color = item.color;
slices.append(SummaryChartSlice(&item, value, value, name, color));
break;
case ST_SPH:
value = (100.0 / hours) * (day->sum(code) / 3600.0);
name = QObject::tr("% in %1").arg(chan.label());
color = item.color;
slices.append(SummaryChartSlice(&item, value, value, name, color));
break;
case ST_HOURS:
value = hours;
name = QObject::tr("Hours");
color = COLOR_LightBlue;
slices.append(SummaryChartSlice(&item, value, hours, name, color));
break;
case ST_MIN:
value = day->Min(code);
name = QObject::tr("Min %1").arg(chan.label());
color = item.color;
slices.append(SummaryChartSlice(&item, value, value - base, name, color));
base = value;
break;
case ST_MID:
value = day->calcMiddle(code);
name = day->calcMiddleLabel(code);
color = item.color;
slices.append(SummaryChartSlice(&item, value, value - base, name, color));
base = value;
break;
case ST_90P:
value = day->calcPercentile(code);
name = day->calcPercentileLabel(code);
color = item.color;
slices.append(SummaryChartSlice(&item, value, value - base, name, color));
base = value;
break;
case ST_MAX:
value = day->calcMax(code);
name = day->calcMaxLabel(code);
color = item.color;
slices.append(SummaryChartSlice(&item, value, value - base, name, color));
base = value;
break;
default:
break;
}
}
}
void gSummaryChart::paint(QPainter &painter, gGraph &graph, const QRegion &region)
{
QRectF rect = region.boundingRect();
rect.translate(0.0f, 0.001f);
painter.setPen(QColor(Qt::black));
painter.drawRect(rect);
rect.moveBottom(rect.bottom()+1);
m_minx = graph.min_x;
m_maxx = graph.max_x;
QDateTime date2 = QDateTime::fromMSecsSinceEpoch(m_minx, Qt::LocalTime);
QDateTime enddate2 = QDateTime::fromMSecsSinceEpoch(m_maxx, Qt::LocalTime);
QDate date = date2.date();
QDate enddate = enddate2.date();
int days = ceil(double(m_maxx - m_minx) / 86400000.0);
//float lasty1 = rect.bottom();
auto it = dayindex.find(date);
idx_start = 0;
if (it == dayindex.end()) {
it = dayindex.begin();
} else {
idx_start = it.value();
}
int idx = idx_start;
// Determine how many days after the first day of the chart that this data is to begin
int numDaysOffset = 0;
if (firstday > date) { // date = beginning date of chart; firstday = beginning date of data
numDaysOffset = date.daysTo(firstday);
}
// Determine how many days before the last day of the chart that this data is to end
int numDaysAfter = 0;
if (enddate > lastday) { // enddate = last date of chart; lastday = last date of data
numDaysAfter = lastday.daysTo(enddate);
}
if (numDaysAfter > days) // Nothing to do if this data is off the left edge of the chart
return;
auto ite = dayindex.find(enddate);
idx_end = daylist.size()-1;
if (ite != dayindex.end()) {
idx_end = ite.value();
}
QPoint mouse = graph.graphView()->currentMousePos();
nousedays = 0;
totaldays = 0;
QRectF hl_rect;
QDate hl_date;
Day * hl_day = nullptr;
int hl_idx = -1;
bool hl = false;
if ((daylist.size() == 0) || (it == dayindex.end()))
return;
//Day * lastday = nullptr;
// int dc = 0;
// for (int i=idx; i<=idx_end; ++i) {
// Day * day = daylist.at(i);
// if (day || lastday) {
// dc++;
// }
// lastday = day;
// }
// days = dc;
// lastday = nullptr;
float barw = float(rect.width()) / float(days);
QString hl2_text = "";
QVector<QRectF> outlines;
int size = idx_end - idx;
outlines.reserve(size * expected_slices);
// Virtual call to setup any custom graph stuff
preCalc();
float lastx1 = rect.left();
lastx1 += numDaysOffset * barw;
float right_edge = (rect.left()+rect.width()+1);
/////////////////////////////////////////////////////////////////////
/// Calculate Graph Peaks
/////////////////////////////////////////////////////////////////////
peak_value = 0;
for (int i=idx; i <= idx_end; ++i, lastx1 += barw) {
Day * day = daylist.at(i);
if ((lastx1 + barw) > right_edge)
break;
if (!day) {
continue;
}
day->OpenSummary();
auto cit = cache.find(i);
if (cit == cache.end()) {
populate(day, i);
cit = cache.find(i);
}
if (cit != cache.end()) {
float base = 0, val;
for (const auto & slice : cit.value()) {
val = slice.height;
base += val;
}
peak_value = qMax(peak_value, base);
}
}
m_miny = 0;
m_maxy = ceil(peak_value);
/////////////////////////////////////////////////////////////////////
/// Y-Axis scaling
/////////////////////////////////////////////////////////////////////
EventDataType miny;
EventDataType maxy;
graph.roundY(miny, maxy);
float ymult = float(rect.height()) / (maxy-miny);
lastx1 = rect.left();
lastx1 += numDaysOffset * barw;
/////////////////////////////////////////////////////////////////////
/// Main drawing loop
/////////////////////////////////////////////////////////////////////
do {
Day * day = daylist.at(idx);
if ((lastx1 + barw) > right_edge)
break;
totaldays++;
if (!day)
{
// lasty1 = rect.bottom();
lastx1 += barw;
it++;
nousedays++;
//lastday = day;
continue;
}
//lastday = day;
float x1 = lastx1 + barw;
day->OpenSummary();
QRectF hl2_rect;
bool hlday = false;
QRectF rec2(lastx1, rect.top(), barw, rect.height());
if (rec2.contains(mouse)) {
hl_rect = rec2;
hl_day = day;
hl_date = it.key();
hl_idx = idx;
hl = true;
hlday = true;
}
auto cit = cache.find(idx);
if (cit == cache.end()) {
populate(day, idx);
cit = cache.find(idx);
}
float lastval = 0, val, y1,y2;
if (cit != cache.end()) {
/////////////////////////////////////////////////////////////////////////////////////
/// Draw pressure settings
/////////////////////////////////////////////////////////////////////////////////////
QVector<SummaryChartSlice> & list = cit.value();
customCalc(day, list);
QLinearGradient gradient(lastx1, 0, lastx1 + barw, 0); //rect.bottom(), barw, rect.bottom());
for (const auto & slice : list) {
val = slice.height;
y1 = ((lastval-miny) * ymult);
y2 = (val * ymult);
QColor color = slice.color;
QRectF rec = QRectF(lastx1, rect.bottom() - y1, barw, -y2).intersected(rect);
if (hlday) {
if (rec.contains(mouse.x(), mouse.y())) {
color = Qt::yellow;
hl2_rect = rec;
}
}
if (barw <= 3) {
painter.fillRect(rec, QBrush(color));
} else if (barw > 8) {
gradient.setColorAt(0,color);
gradient.setColorAt(1,brighten(color, 2.0));
painter.fillRect(rec, QBrush(gradient));
// painter.fillRect(rec, slice.brush);
outlines.append(rec);
} else {
painter.fillRect(rec, brighten(color, 1.25));
outlines.append(rec);
}
lastval += val;
}
}
lastx1 = x1;
it++;
} while (++idx <= idx_end);
painter.setPen(QPen(Qt::black,1));
painter.drawRects(outlines);
if (hl) {
QColor col2(255,0,0,64);
painter.fillRect(hl_rect, QBrush(col2));
QString txt = hl_date.toString(Qt::SystemLocaleShortDate)+" ";
if (hl_day) {
// grab extra tooltip data
txt += tooltipData(hl_day, hl_idx);
if (!hl2_text.isEmpty()) {
QColor col = Qt::yellow;
col.setAlpha(255);
// painter.fillRect(hl2_rect, QBrush(col));
txt += hl2_text;
}
}
graph.ToolTip(txt, mouse.x()-15, mouse.y()+5, TT_AlignRight);
}
try {
afterDraw(painter, graph, rect);
} catch(...) {
qDebug() << "Bad median call in" << m_label;
}
// This could be turning off graphs prematurely..
if (cache.size() == 0) {
m_empty = true;
m_emptyPrev = true;
graph.graphView()->updateScale();
emit summaryChartEmpty(this,m_minx,m_maxx,true);
} else if (m_emptyPrev) {
m_emptyPrev = false;
emit summaryChartEmpty(this,m_minx,m_maxx,false);
}
}
QString gUsageChart::tooltipData(Day * day, int)
{
return QObject::tr("\nHours: %1").arg(day->hours(m_machtype), 0, 'f', 2);
}
void gUsageChart::populate(Day *day, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
float hours = day->hours(m_machtype);
QColor cpapcolor = day->summaryOnly() ? QColor(128,128,128) : calcitems[0].color;
bool haveoxi = day->hasMachine(MT_OXIMETER);
QColor goodcolor = haveoxi ? QColor(128,255,196) : cpapcolor;
QColor color = (hours < compliance_threshold) ? QColor(255,64,64) : goodcolor;
slices.append(SummaryChartSlice(&calcitems[0], hours, hours, QObject::tr("Hours"), color));
}
void gUsageChart::preCalc()
{
midcalc = p_profile->general->prefCalcMiddle();
compliance_threshold = p_profile->cpap->complianceHours();
incompdays = 0;
SummaryCalcItem & calc = calcitems[0];
calc.reset(idx_end - idx_start, midcalc);
}
void gUsageChart::customCalc(Day *, QVector<SummaryChartSlice> &list)
{
if (list.size() == 0) {
incompdays++;
return;
}
SummaryChartSlice & slice = list[0];
SummaryCalcItem & calc = calcitems[0];
if (slice.value < compliance_threshold) incompdays++;
calc.update(slice.value, 1);
}
void gUsageChart::afterDraw(QPainter &, gGraph &graph, QRectF rect)
{
if (totaldays == nousedays) return;
if (totaldays > 1) {
float comp = 100.0 - ((float(incompdays + nousedays) / float(totaldays)) * 100.0);
int midcalc = p_profile->general->prefCalcMiddle();
float mid = 0;
SummaryCalcItem & calc = calcitems[0];
switch (midcalc) {
case 0: // median
mid = median(calc.median_data.begin(), calc.median_data.end());
break;
case 1: // w-avg
mid = calc.wavg_sum / calc.divisor;
break;
case 2:
mid = calc.avg_sum / calc.cnt;
break;
}
QString txt = QObject::tr("%1 low usage, %2 no usage, out of %3 days (%4% compliant.) Length: %5 / %6 / %7").
arg(incompdays).arg(nousedays).arg(totaldays).arg(comp,0,'f',1).arg(calc.min, 0, 'f', 2).arg(mid, 0, 'f', 2).arg(calc.max, 0, 'f', 2);;
graph.renderText(txt, rect.left(), rect.top()-5*graph.printScaleY(), 0);
}
}
void gSessionTimesChart::preCalc() { void gSessionTimesChart::preCalc() {
@ -1098,230 +359,3 @@ void gSessionTimesChart::paint(QPainter &painter, gGraph &graph, const QRegion &
afterDraw(painter, graph, rect); afterDraw(painter, graph, rect);
} }
////////////////////////////////////////////////////////////////////////////
/// Total Time in Apnea Chart Stuff
////////////////////////////////////////////////////////////////////////////
void gTTIAChart::preCalc()
{
gSummaryChart::preCalc();
}
void gTTIAChart::customCalc(Day *, QVector<SummaryChartSlice> & slices)
{
if (slices.size() == 0) return;
const SummaryChartSlice & slice = slices.at(0);
calcitems[0].update(slice.value, slice.value);
}
void gTTIAChart::afterDraw(QPainter &, gGraph &graph, QRectF rect)
{
QStringList txtlist;
for (auto & calc : calcitems) {
//ChannelID code = calc.code;
//schema::Channel & chan = schema::channel[code];
float mid = 0;
switch (midcalc) {
case 0:
if (calc.median_data.size() > 0) {
mid = median(calc.median_data.begin(), calc.median_data.end());
}
break;
case 1:
if (calc.divisor > 0) {
mid = calc.wavg_sum / calc.divisor;
}
break;
case 2:
if (calc.cnt > 0) {
mid = calc.avg_sum / calc.cnt;
}
break;
}
txtlist.append(QString("%1 %2 / %3 / %4").arg(QObject::tr("TTIA:")).arg(calc.min, 0, 'f', 2).arg(mid, 0, 'f', 2).arg(calc.max, 0, 'f', 2));
}
QString txt = txtlist.join(", ");
graph.renderText(txt, rect.left(), rect.top()-5*graph.printScaleY(), 0);
}
void gTTIAChart::populate(Day *day, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
// float ttia = day->sum(CPAP_AllApnea) + day->sum(CPAP_Obstructive) + day->sum(CPAP_ClearAirway) + day->sum(CPAP_Apnea) + day->sum(CPAP_Hypopnea);
float ttia = day->sum(AllAhiChannels);
int h = ttia / 3600;
int m = int(ttia) / 60 % 60;
int s = int(ttia) % 60;
slices.append(SummaryChartSlice(&calcitems[0], ttia / 60.0, ttia / 60.0, QObject::tr("\nTTIA: %1").arg(QString().sprintf("%02i:%02i:%02i",h,m,s)), QColor(255,147,150)));
}
QString gTTIAChart::tooltipData(Day *, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
if (slices.size() == 0) return QString();
const SummaryChartSlice & slice = slices.at(0);
return slice.name;
}
////////////////////////////////////////////////////////////////////////////
/// AHI Chart Stuff
////////////////////////////////////////////////////////////////////////////
void gAHIChart::preCalc()
{
gSummaryChart::preCalc();
ahi_wavg = 0;
ahi_avg = 0;
total_days = 0;
total_hours = 0;
min_ahi = 99999;
max_ahi = -99999;
ahi_data.clear();
ahi_data.reserve(idx_end-idx_start);
}
void gAHIChart::customCalc(Day *day, QVector<SummaryChartSlice> &list)
{
int size = list.size();
if (size == 0) return;
EventDataType hours = day->hours(m_machtype);
EventDataType ahi_cnt = 0;
for (auto & slice : list) {
SummaryCalcItem * calc = slice.calc;
EventDataType value = slice.value;
float valh = value/ hours;
switch (midcalc) {
case 0:
calc->median_data.append(valh);
break;
case 1:
calc->wavg_sum += value;
calc->divisor += hours;
break;
case 2:
calc->avg_sum += value;
calc->cnt++;
break;
}
calc->min = qMin(valh, calc->min);
calc->max = qMax(valh, calc->max);
ahi_cnt += value;
}
min_ahi = qMin(ahi_cnt / hours, min_ahi);
max_ahi = qMax(ahi_cnt / hours, max_ahi);
ahi_data.append(ahi_cnt / hours);
ahi_wavg += ahi_cnt;
ahi_avg += ahi_cnt;
total_hours += hours;
total_days++;
}
void gAHIChart::afterDraw(QPainter & /*painter */, gGraph &graph, QRectF rect)
{
if (totaldays == nousedays) return;
//int size = idx_end - idx_start;
bool skip = true;
float med = 0;
switch (midcalc) {
case 0:
if (ahi_data.size() > 0) {
med = median(ahi_data.begin(), ahi_data.end());
skip = false;
}
break;
case 1: // wavg
if (total_hours > 0) {
med = ahi_wavg / total_hours;
skip = false;
}
break;
case 2: // avg
if (total_days > 0) {
med = ahi_avg / total_days;
skip = false;
}
break;
}
QStringList txtlist;
if (!skip) txtlist.append(QString("%1 %2 / %3 / %4").arg(STR_TR_AHI).arg(min_ahi, 0, 'f', 2).arg(med, 0, 'f', 2).arg(max_ahi, 0, 'f', 2));
int i = calcitems.size();
while (i > 0) {
i--;
ChannelID code = calcitems[i].code;
schema::Channel & chan = schema::channel[code];
float mid = 0;
skip = true;
switch (midcalc) {
case 0:
if (calcitems[i].median_data.size() > 0) {
mid = median(calcitems[i].median_data.begin(), calcitems[i].median_data.end());
skip = false;
}
break;
case 1:
if (calcitems[i].divisor > 0) {
mid = calcitems[i].wavg_sum / calcitems[i].divisor;
skip = false;
}
break;
case 2:
if (calcitems[i].cnt > 0) {
mid = calcitems[i].avg_sum / calcitems[i].cnt;
skip = false;
}
break;
}
if (!skip) txtlist.append(QString("%1 %2 / %3 / %4").arg(chan.label()).arg(calcitems[i].min, 0, 'f', 2).arg(mid, 0, 'f', 2).arg(calcitems[i].max, 0, 'f', 2));
}
QString txt = txtlist.join(", ");
graph.renderText(txt, rect.left(), rect.top()-5*graph.printScaleY(), 0);
}
void gAHIChart::populate(Day *day, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
float hours = day->hours(m_machtype);
for (auto & calc : calcitems) {
ChannelID code = calc.code;
if (!day->hasData(code, ST_CNT)) continue;
schema::Channel *chan = schema::channel.channels.find(code).value();
float c = day->count(code);
slices.append(SummaryChartSlice(&calc, c, c / hours, chan->label(), calc.color));
}
}
QString gAHIChart::tooltipData(Day *day, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
float total = 0;
float hour = day->hours(m_machtype);
QString txt;
int i = slices.size();
while (i > 0) {
i--;
total += slices[i].value;
txt += QString("\n%1: %2").arg(slices[i].name).arg(float(slices[i].value) / hour, 0, 'f', 2);
}
return QString("\n%1: %2").arg(STR_TR_AHI).arg(float(total) / hour,0,'f',2)+txt;
}

View File

@ -12,261 +12,9 @@
#include "SleepLib/day.h" #include "SleepLib/day.h"
#include "SleepLib/profiles.h" #include "SleepLib/profiles.h"
#include "gGraphView.h" #include "Graphs/gGraphView.h"
struct TimeSpan
{
public:
TimeSpan():begin(0), end(0) {}
TimeSpan(float b, float e) : begin(b), end(e) {}
TimeSpan(const TimeSpan & copy) {
begin = copy.begin;
end = copy.end;
}
~TimeSpan() {}
float begin;
float end;
};
struct SummaryCalcItem {
SummaryCalcItem() {
code = 0;
type = ST_CNT;
color = Qt::black;
wavg_sum = 0;
avg_sum = 0;
cnt = 0;
divisor = 0;
min = 0;
max = 0;
}
SummaryCalcItem(const SummaryCalcItem & copy) {
code = copy.code;
type = copy.type;
color = copy.color;
wavg_sum = 0;
avg_sum = 0;
cnt = 0;
divisor = 0;
min = 0;
max = 0;
midcalc = p_profile->general->prefCalcMiddle();
}
SummaryCalcItem(ChannelID code, SummaryType type, QColor color)
:code(code), type(type), color(color) {
}
float mid()
{
float val = 0;
switch (midcalc) {
case 0:
if (median_data.size() > 0)
val = median(median_data.begin(), median_data.end());
break;
case 1:
if (divisor > 0)
val = wavg_sum / divisor;
break;
case 2:
if (cnt > 0)
val = avg_sum / cnt;
}
return val;
}
inline void update(float value, float weight) {
if (midcalc == 0) {
median_data.append(value);
}
avg_sum += value;
cnt++;
wavg_sum += value * weight;
divisor += weight;
min = qMin(min, value);
max = qMax(max, value);
}
void reset(int reserve, short mc) {
midcalc = mc;
wavg_sum = 0;
avg_sum = 0;
divisor = 0;
cnt = 0;
min = 99999;
max = -99999;
median_data.clear();
if (midcalc == 0) {
median_data.reserve(reserve);
}
}
ChannelID code;
SummaryType type;
QColor color;
double wavg_sum;
double divisor;
double avg_sum;
int cnt;
EventDataType min;
EventDataType max;
static short midcalc;
QList<float> median_data;
};
struct SummaryChartSlice {
SummaryChartSlice() {
calc = nullptr;
height = 0;
value = 0;
name = ST_CNT;
color = Qt::black;
}
SummaryChartSlice(const SummaryChartSlice & copy) {
calc = copy.calc;
value = copy.value;
height = copy.height;
name = copy.name;
color = copy.color;
// brush = copy.brush;
}
SummaryChartSlice(SummaryCalcItem * calc, EventDataType value, EventDataType height, QString name, QColor color)
:calc(calc), value(value), height(height), name(name), color(color) {
// QLinearGradient gradient(0, 0, 1, 0);
// gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
// gradient.setColorAt(0,color);
// gradient.setColorAt(1,brighten(color));
// brush = QBrush(gradient);
}
SummaryCalcItem * calc;
EventDataType value;
EventDataType height;
QString name;
QColor color;
// QBrush brush;
};
class gSummaryChart : public QObject , public Layer
{
Q_OBJECT;
public:
gSummaryChart(QString label, MachineType machtype);
gSummaryChart(ChannelID code, MachineType machtype);
virtual ~gSummaryChart();
//! \brief Renders the graph to the QPainter object
virtual void paint(QPainter &, gGraph &, const QRegion &);
//! \brief Called whenever data model changes underneath. Day object is not needed here, it's just here for Layer compatability.
virtual void SetDay(Day *day = nullptr);
//! \brief Returns true if no data was found for this day during SetDay
virtual bool isEmpty() { return m_empty; }
//! \brief Allows chart to recalculate empty flag.
void reCalculate() {m_empty=false;};
virtual void populate(Day *, int idx);
//! \brief Override to setup custom stuff before main loop
virtual void preCalc();
//! \brief Override to call stuff in main loop
virtual void customCalc(Day *, QVector<SummaryChartSlice> &);
//! \brief Override to call stuff after draw is complete
virtual void afterDraw(QPainter &, gGraph &, QRectF);
//! \brief Return any extra data to show beneath the date in the hover over tooltip
virtual QString tooltipData(Day *, int);
virtual void dataChanged() {
cache.clear();
}
virtual int addCalc(ChannelID code, SummaryType type, QColor color);
virtual int addCalc(ChannelID code, SummaryType type);
virtual Layer * Clone() {
gSummaryChart * sc = new gSummaryChart(m_label, m_machtype);
Layer::CloneInto(sc);
CloneInto(sc);
// copy this here, because only base summary charts need it
sc->calcitems = calcitems;
return sc;
}
void CloneInto(gSummaryChart * layer) {
layer->m_empty = m_empty;
layer->firstday = firstday;
layer->lastday = lastday;
layer->expected_slices = expected_slices;
layer->nousedays = nousedays;
layer->totaldays = totaldays;
layer->peak_value = peak_value;
layer->idx_start = idx_start;
layer->idx_end = idx_end;
layer->cache.clear();
layer->dayindex = dayindex;
layer->daylist = daylist;
}
signals:
void summaryChartEmpty(gSummaryChart*,qint64,qint64,bool);
protected:
//! \brief Key was pressed that effects this layer
virtual bool keyPressEvent(QKeyEvent *event, gGraph *graph);
//! \brief Mouse moved over this layers area (shows the hover-over tooltips here)
virtual bool mouseMoveEvent(QMouseEvent *event, gGraph *graph);
//! \brief Mouse Button was pressed over this area
virtual bool mousePressEvent(QMouseEvent *event, gGraph *graph);
//! \brief Mouse Button was released over this area. (jumps to daily view here)
virtual bool mouseReleaseEvent(QMouseEvent *event, gGraph *graph);
QString m_label;
MachineType m_machtype;
bool m_empty;
bool m_emptyPrev;
int hl_day;
int tz_offset;
float tz_hours;
QDate firstday;
QDate lastday;
QMap<QDate, int> dayindex;
QList<Day *> daylist;
QHash<int, QVector<SummaryChartSlice> > cache;
QVector<SummaryCalcItem> calcitems;
int expected_slices;
int nousedays;
int totaldays;
EventDataType peak_value;
EventDataType min_value;
int idx_start;
int idx_end;
short midcalc;
};
#include "Graphs/gSummaryChart.h"
/*! \class gSessionTimesChart /*! \class gSessionTimesChart
@ -318,123 +66,4 @@ public:
}; };
class gUsageChart : public gSummaryChart
{
public:
gUsageChart()
:gSummaryChart("Usage", MT_CPAP) {
addCalc(NoChannel, ST_HOURS, QColor(64,128,255));
}
virtual ~gUsageChart() {}
virtual void preCalc();
virtual void customCalc(Day *, QVector<SummaryChartSlice> &);
virtual void afterDraw(QPainter &, gGraph &, QRectF);
virtual void populate(Day *day, int idx);
virtual QString tooltipData(Day * day, int);
virtual Layer * Clone() {
gUsageChart * sc = new gUsageChart();
gSummaryChart::CloneInto(sc);
CloneInto(sc);
return sc;
}
void CloneInto(gUsageChart * layer) {
layer->incompdays = incompdays;
layer->compliance_threshold = compliance_threshold;
}
private:
int incompdays;
EventDataType compliance_threshold;
};
class gTTIAChart : public gSummaryChart
{
public:
gTTIAChart()
:gSummaryChart("TTIA", MT_CPAP) {
addCalc(NoChannel, ST_CNT, QColor(255,147,150));
}
virtual ~gTTIAChart() {}
virtual void preCalc();
virtual void customCalc(Day *, QVector<SummaryChartSlice> &);
virtual void afterDraw(QPainter &, gGraph &, QRectF);
virtual void populate(Day *day, int idx);
virtual QString tooltipData(Day * day, int);
virtual Layer * Clone() {
gTTIAChart * sc = new gTTIAChart();
gSummaryChart::CloneInto(sc);
CloneInto(sc);
return sc;
}
void CloneInto(gTTIAChart * /* layer*/) {
}
private:
};
class gAHIChart : public gSummaryChart
{
public:
gAHIChart()
:gSummaryChart("AHIChart", MT_CPAP) {
for (int i = 0; i < ahiChannels.size(); i++)
addCalc(ahiChannels.at(i), ST_CPH);
// addCalc(CPAP_ClearAirway, ST_CPH);
// addCalc(CPAP_AllApnea, ST_CPH);
// addCalc(CPAP_Obstructive, ST_CPH);
// addCalc(CPAP_Apnea, ST_CPH);
// addCalc(CPAP_Hypopnea, ST_CPH);
if (p_profile->general->calculateRDI())
addCalc(CPAP_RERA, ST_CPH);
}
virtual ~gAHIChart() {}
virtual void preCalc();
virtual void customCalc(Day *, QVector<SummaryChartSlice> &);
virtual void afterDraw(QPainter &, gGraph &, QRectF);
virtual void populate(Day *, int idx);
virtual QString tooltipData(Day * day, int);
virtual Layer * Clone() {
gAHIChart * sc = new gAHIChart();
gSummaryChart::CloneInto(sc);
CloneInto(sc);
return sc;
}
void CloneInto(gAHIChart * /* layer */) {
// layer->ahicalc = ahicalc;
// layer->ahi_wavg = ahi_wavg;
// layer->ahi_avg = ahi_avg;
// layer->total_hours = total_hours;
// layer->max_ahi = max_ahi;
// layer->min_ahi = min_ahi;
// layer->total_days = total_days;
// layer->ahi_data = ahi_data;
}
// SummaryCalcItem ahicalc;
double ahi_wavg;
double ahi_avg;
double total_hours;
float max_ahi;
float min_ahi;
int total_days;
QList<float> ahi_data;
};
#endif // GSESSIONTIMESCHART_H #endif // GSESSIONTIMESCHART_H

File diff suppressed because it is too large Load Diff

View File

@ -1,142 +1,231 @@
/* gSummaryChart Header /* gSessionTimesChart Header
* *
* Copyright (c) 2019-2022 The OSCAR Team * Copyright (c) 2019-2022 The Oscar Team
* Copyright (C) 2011-2018 Mark Watkins <mark@jedimark.net> * Copyright (C) 2011-2018 Mark Watkins <mark@jedimark.net>
* *
* This file is subject to the terms and conditions of the GNU General Public * This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code * License. See the file COPYING in the main directory of the source code
* for more details. */ * for more details. */
#if 1
#ifndef GSUMMARYCHART_H
#define GSUMMARYCHART_H
#ifndef GBARCHART_H #include "SleepLib/day.h"
#define GBARCHART_H #include "SleepLib/profiles.h"
#include "Graphs/gGraphView.h"
#include <SleepLib/profiles.h> #include "SleepLib/appsettings.h"
#include "gGraphView.h"
#include "gXAxis.h"
/*! \enum GraphType
\value GT_BAR Display as a BarGraph
\value GT_LINE Display as a line plot
\value GT_SESSIONS Display type for session times chart
*/
enum GraphType { GT_BAR, GT_LINE, GT_POINTS, GT_SESSIONS };
/*! \class SummaryChart struct TimeSpan
\brief The main overall chart type layer used in Overview page
*/
class SummaryChart: public Layer
{ {
public: public:
//! \brief Constructs a SummaryChart with QString label, of GraphType type TimeSpan():begin(0), end(0) {}
SummaryChart(QString label, GraphType type = GT_BAR); TimeSpan(float b, float e) : begin(b), end(e) {}
virtual ~SummaryChart(); TimeSpan(const TimeSpan & copy) {
begin = copy.begin;
end = copy.end;
}
~TimeSpan() {}
float begin;
float end;
};
struct SummaryCalcItem {
SummaryCalcItem() {
code = 0;
type = ST_CNT;
color = Qt::black;
wavg_sum = 0;
avg_sum = 0;
cnt = 0;
divisor = 0;
min = 0;
max = 0;
}
SummaryCalcItem(const SummaryCalcItem & copy) {
code = copy.code;
type = copy.type;
color = copy.color;
wavg_sum = 0;
avg_sum = 0;
cnt = 0;
divisor = 0;
min = 0;
max = 0;
midcalc = p_profile->general->prefCalcMiddle();
}
SummaryCalcItem(ChannelID code, SummaryType type, QColor color)
:code(code), type(type), color(color) {
}
float mid()
{
float val = 0;
switch (midcalc) {
case 0:
if (median_data.size() > 0)
val = median(median_data.begin(), median_data.end());
break;
case 1:
if (divisor > 0)
val = wavg_sum / divisor;
break;
case 2:
if (cnt > 0)
val = avg_sum / cnt;
}
return val;
}
inline void update(float value, float weight) {
if (midcalc == 0) {
median_data.append(value);
}
avg_sum += value;
cnt++;
wavg_sum += value * weight;
divisor += weight;
min = qMin(min, value);
max = qMax(max, value);
}
void reset(int reserve, short mc) {
midcalc = mc;
wavg_sum = 0;
avg_sum = 0;
divisor = 0;
cnt = 0;
min = 99999;
max = -99999;
median_data.clear();
if (midcalc == 0) {
median_data.reserve(reserve);
}
}
ChannelID code;
SummaryType type;
QColor color;
double wavg_sum;
double divisor;
double avg_sum;
int cnt;
EventDataType min;
EventDataType max;
static short midcalc;
QList<float> median_data;
};
struct SummaryChartSlice {
SummaryChartSlice() {
calc = nullptr;
height = 0;
value = 0;
name = ST_CNT;
color = Qt::black;
}
SummaryChartSlice(const SummaryChartSlice & copy) {
calc = copy.calc;
value = copy.value;
height = copy.height;
name = copy.name;
color = copy.color;
// brush = copy.brush;
}
SummaryChartSlice(SummaryCalcItem * calc, EventDataType value, EventDataType height, QString name, QColor color)
:calc(calc), value(value), height(height), name(name), color(color) {
// QLinearGradient gradient(0, 0, 1, 0);
// gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
// gradient.setColorAt(0,color);
// gradient.setColorAt(1,brighten(color));
// brush = QBrush(gradient);
}
SummaryCalcItem * calc;
EventDataType value;
EventDataType height;
QString name;
QColor color;
// QBrush brush;
};
class gSummaryChart : public QObject , public Layer
{
Q_OBJECT;
public:
gSummaryChart(QString label, MachineType machtype);
gSummaryChart(ChannelID code, MachineType machtype);
virtual ~gSummaryChart();
//! \brief Renders the graph to the QPainter object //! \brief Renders the graph to the QPainter object
virtual void paint(QPainter &painter, gGraph &w, const QRegion &region); virtual void paint(QPainter &, gGraph &, const QRegion &);
//! \brief Precalculation code prior to drawing. Day object is not needed here, it's just here for Layer compatability. //! \brief Called whenever data model changes underneath. Day object is not needed here, it's just here for Layer compatability.
virtual void SetDay(Day *day = nullptr); virtual void SetDay(Day *day = nullptr);
//! \brief Returns true if no data was found for this day during SetDay //! \brief Returns true if no data was found for this day during SetDay
virtual bool isEmpty() { return m_empty; } virtual bool isEmpty() { return m_empty; }
//! \brief Adds a layer to the summaryChart (When in Bar mode, it becomes culminative, eg, the AHI chart) //! \brief Allows chart to recalculate empty flag.
void addSlice(ChannelID code, QColor color, SummaryType type, EventDataType tval = 0.00f) { void reCalculate() {m_empty=false;};
m_codes.push_back(code);
m_colors.push_back(color); virtual void populate(Day *, int idx);
m_type.push_back(type);
//m_zeros.push_back(ignore_zeros); //! \brief Override to setup custom stuff before main loop
m_typeval.push_back(tval); virtual void preCalc();
//! \brief Override to call stuff in main loop
virtual void customCalc(Day *, QVector<SummaryChartSlice> &);
//! \brief Override to call stuff after draw is complete
virtual void afterDraw(QPainter &, gGraph &, QRectF);
//! \brief Return any extra data to show beneath the date in the hover over tooltip
virtual QString tooltipData(Day *, int);
virtual void dataChanged() {
cache.clear();
} }
//! \brief Deselect highlighting (the gold bar) virtual int addCalc(ChannelID code, SummaryType type, QColor color);
virtual void deselect() { virtual int addCalc(ChannelID code, SummaryType type);
hl_day = -1;
}
//! \brief Returns true if currently selected..
virtual bool isSelected() { return hl_day >= 0; }
//! \brief Sets the MachineType this SummaryChart is interested in
void setMachineType(MachineType type) { m_machinetype = type; }
//! \brief Returns the MachineType this SummaryChart is interested in
MachineType machineType() { return m_machinetype; }
virtual Layer * Clone() { virtual Layer * Clone() {
SummaryChart * sc = new SummaryChart(m_label); gSummaryChart * sc = new gSummaryChart(m_label, m_machtype);
Layer::CloneInto(sc); Layer::CloneInto(sc);
CloneInto(sc); CloneInto(sc);
// copy this here, because only base summary charts need it
sc->calcitems = calcitems;
return sc; return sc;
} }
void CloneInto(SummaryChart * layer) { void CloneInto(gSummaryChart * layer) {
layer->m_orientation = m_orientation;
layer->m_colors = m_colors;
layer->m_codes = m_codes;
layer->m_goodcodes = m_goodcodes;
layer->m_type = m_type;
layer->m_typeval = m_typeval;
layer->m_values = m_values;
layer->m_times = m_times;
layer->m_hours = m_hours;
layer->m_days = m_days;
layer->m_empty = m_empty; layer->m_empty = m_empty;
layer->m_fday = m_fday; layer->firstday = firstday;
layer->m_label = m_label; layer->lastday = lastday;
layer->barw = barw; layer->expected_slices = expected_slices;
layer->l_offset = l_offset; layer->nousedays = nousedays;
layer->offset = offset; layer->totaldays = totaldays;
layer->l_left = l_left; layer->peak_value = peak_value;
layer->l_top = l_top; layer->idx_start = idx_start;
layer->l_width= l_width; layer->idx_end = idx_end;
layer->l_height = l_height; layer->cache.clear();
layer->rtop = rtop; layer->dayindex = dayindex;
layer->l_minx = l_minx; layer->daylist = daylist;
layer->l_maxx = l_maxx;
layer->hl_day = hl_day;
layer->m_graphtype = m_graphtype;
layer->m_machinetype = m_machinetype;
layer->tz_offset = tz_offset;
layer->tz_hours = tz_hours;
} }
signals:
void summaryChartEmpty(gSummaryChart*,qint64,qint64,bool);
protected:
protected:
Qt::Orientation m_orientation;
QVector<QColor> m_colors;
QVector<ChannelID> m_codes;
QVector<bool> m_goodcodes;
//QVector<bool> m_zeros;
QVector<SummaryType> m_type;
QVector<EventDataType> m_typeval;
QHash<int, QMap<short, EventDataType> > m_values;
QHash<int, QMap<short, EventDataType> > m_times;
QHash<int, EventDataType> m_hours;
QHash<int, Day *> m_days;
bool m_empty;
int m_fday;
QString m_label;
float barw; // bar width from last draw
qint64 l_offset; // last offset
float offset; // in pixels;
int l_left, l_top, l_width, l_height;
int rtop;
qint64 l_minx, l_maxx;
int hl_day;
//gGraph *graph;
GraphType m_graphtype;
MachineType m_machinetype;
int tz_offset;
float tz_hours;
//! \brief Key was pressed that effects this layer //! \brief Key was pressed that effects this layer
virtual bool keyPressEvent(QKeyEvent *event, gGraph *graph); virtual bool keyPressEvent(QKeyEvent *event, gGraph *graph);
@ -148,7 +237,39 @@ class SummaryChart: public Layer
//! \brief Mouse Button was released over this area. (jumps to daily view here) //! \brief Mouse Button was released over this area. (jumps to daily view here)
virtual bool mouseReleaseEvent(QMouseEvent *event, gGraph *graph); virtual bool mouseReleaseEvent(QMouseEvent *event, gGraph *graph);
QString m_label;
MachineType m_machtype;
bool m_empty;
bool m_emptyPrev;
int hl_day;
int tz_offset;
float tz_hours;
QDate firstday;
QDate lastday;
QMap<QDate, int> dayindex;
QList<Day *> daylist;
QHash<int, QVector<SummaryChartSlice> > cache;
QVector<SummaryCalcItem> calcitems;
int expected_slices;
int nousedays;
int totaldays;
EventDataType peak_value;
EventDataType min_value;
int idx_start;
int idx_end;
short midcalc;
}; };
#endif // GBARCHART_H
#endif // GSUMMARYCHART_H
#endif

View File

@ -0,0 +1,98 @@
/* gTTIAChart Implementation
*
* Copyright (c) 2019-2022 The Oscar Team
* Copyright (c) 2011-2018 Mark Watkins <mark@jedimark.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code
* for more details. */
#define TEST_MACROS_ENABLEDoff
#include "test_macros.h"
#include <math.h>
#include <QLabel>
#include <QDateTime>
#include "mainwindow.h"
#include "SleepLib/profiles.h"
#include "SleepLib/machine_common.h"
#include "gTTIAChart.h"
#include "gYAxis.h"
extern MainWindow * mainwin;
// short SummaryCalcItem::midcalc;
////////////////////////////////////////////////////////////////////////////
/// Total Time in Apnea Chart Stuff
////////////////////////////////////////////////////////////////////////////
void gTTIAChart::preCalc()
{
gSummaryChart::preCalc();
}
void gTTIAChart::customCalc(Day *, QVector<SummaryChartSlice> & slices)
{
if (slices.size() == 0) return;
const SummaryChartSlice & slice = slices.at(0);
calcitems[0].update(slice.value, slice.value);
}
void gTTIAChart::afterDraw(QPainter &, gGraph &graph, QRectF rect)
{
QStringList txtlist;
for (auto & calc : calcitems) {
//ChannelID code = calc.code;
//schema::Channel & chan = schema::channel[code];
float mid = 0;
switch (midcalc) {
case 0:
if (calc.median_data.size() > 0) {
mid = median(calc.median_data.begin(), calc.median_data.end());
}
break;
case 1:
if (calc.divisor > 0) {
mid = calc.wavg_sum / calc.divisor;
}
break;
case 2:
if (calc.cnt > 0) {
mid = calc.avg_sum / calc.cnt;
}
break;
}
txtlist.append(QString("%1 %2 / %3 / %4").arg(QObject::tr("TTIA:")).arg(calc.min, 0, 'f', 2).arg(mid, 0, 'f', 2).arg(calc.max, 0, 'f', 2));
}
QString txt = txtlist.join(", ");
graph.renderText(txt, rect.left(), rect.top()-5*graph.printScaleY(), 0);
}
void gTTIAChart::populate(Day *day, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
// float ttia = day->sum(CPAP_AllApnea) + day->sum(CPAP_Obstructive) + day->sum(CPAP_ClearAirway) + day->sum(CPAP_Apnea) + day->sum(CPAP_Hypopnea);
float ttia = day->sum(AllAhiChannels);
int h = ttia / 3600;
int m = int(ttia) / 60 % 60;
int s = int(ttia) % 60;
slices.append(SummaryChartSlice(&calcitems[0], ttia / 60.0, ttia / 60.0, QObject::tr("\nTTIA: %1").arg(QString().sprintf("%02i:%02i:%02i",h,m,s)), QColor(255,147,150)));
}
QString gTTIAChart::tooltipData(Day *, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
if (slices.size() == 0) return QString();
const SummaryChartSlice & slice = slices.at(0);
return slice.name;
}

46
oscar/Graphs/gTTIAChart.h Normal file
View File

@ -0,0 +1,46 @@
/* gTTIAChart Header
*
* Copyright (c) 2019-2022 The Oscar Team
* Copyright (C) 2011-2018 Mark Watkins <mark@jedimark.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code
* for more details. */
#ifndef GTTIACHART_H
#define GTTIACHART_H
#include "SleepLib/day.h"
#include "SleepLib/profiles.h"
#include "Graphs/gGraphView.h"
#include "Graphs/gSummaryChart.h"
class gTTIAChart : public gSummaryChart
{
public:
gTTIAChart()
:gSummaryChart("TTIA", MT_CPAP) {
addCalc(NoChannel, ST_CNT, QColor(255,147,150));
}
virtual ~gTTIAChart() {}
virtual void preCalc();
virtual void customCalc(Day *, QVector<SummaryChartSlice> &);
virtual void afterDraw(QPainter &, gGraph &, QRectF);
virtual void populate(Day *day, int idx);
virtual QString tooltipData(Day * day, int);
virtual Layer * Clone() {
gTTIAChart * sc = new gTTIAChart();
gSummaryChart::CloneInto(sc);
CloneInto(sc);
return sc;
}
void CloneInto(gTTIAChart * /* layer*/) {
}
private:
};
#endif // GTTIACHART_H

View File

@ -0,0 +1,102 @@
/* gUsageChart Implementation
*
* Copyright (c) 2019-2022 The Oscar Team
* Copyright (c) 2011-2018 Mark Watkins <mark@jedimark.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code
* for more details. */
#if 1
#define TEST_MACROS_ENABLEDoff
#include "test_macros.h"
#include <math.h>
#include <QLabel>
#include <QDateTime>
#include "mainwindow.h"
#include "SleepLib/profiles.h"
#include "SleepLib/machine_common.h"
#include "gUsageChart.h"
#include "gYAxis.h"
extern MainWindow * mainwin;
// short SummaryCalcItem::midcalc;
QString gUsageChart::tooltipData(Day * day, int)
{
return QObject::tr("\nHours: %1").arg(day->hours(m_machtype), 0, 'f', 2);
}
void gUsageChart::populate(Day *day, int idx)
{
QVector<SummaryChartSlice> & slices = cache[idx];
float hours = day->hours(m_machtype);
QColor cpapcolor = day->summaryOnly() ? QColor(128,128,128) : calcitems[0].color;
bool haveoxi = day->hasMachine(MT_OXIMETER);
QColor goodcolor = haveoxi ? QColor(128,255,196) : cpapcolor;
QColor color = (hours < compliance_threshold) ? QColor(255,64,64) : goodcolor;
slices.append(SummaryChartSlice(&calcitems[0], hours, hours, QObject::tr("Hours"), color));
}
void gUsageChart::preCalc()
{
midcalc = p_profile->general->prefCalcMiddle();
compliance_threshold = p_profile->cpap->complianceHours();
incompdays = 0;
SummaryCalcItem & calc = calcitems[0];
calc.reset(idx_end - idx_start, midcalc);
}
void gUsageChart::customCalc(Day *, QVector<SummaryChartSlice> &list)
{
if (list.size() == 0) {
incompdays++;
return;
}
SummaryChartSlice & slice = list[0];
SummaryCalcItem & calc = calcitems[0];
if (slice.value < compliance_threshold) incompdays++;
calc.update(slice.value, 1);
}
void gUsageChart::afterDraw(QPainter &, gGraph &graph, QRectF rect)
{
if (totaldays == nousedays) return;
if (totaldays > 1) {
float comp = 100.0 - ((float(incompdays + nousedays) / float(totaldays)) * 100.0);
int midcalc = p_profile->general->prefCalcMiddle();
float mid = 0;
SummaryCalcItem & calc = calcitems[0];
switch (midcalc) {
case 0: // median
mid = median(calc.median_data.begin(), calc.median_data.end());
break;
case 1: // w-avg
mid = calc.wavg_sum / calc.divisor;
break;
case 2:
mid = calc.avg_sum / calc.cnt;
break;
}
QString txt = QObject::tr("%1 low usage, %2 no usage, out of %3 days (%4% compliant.) Length: %5 / %6 / %7").
arg(incompdays).arg(nousedays).arg(totaldays).arg(comp,0,'f',1).arg(calc.min, 0, 'f', 2).arg(mid, 0, 'f', 2).arg(calc.max, 0, 'f', 2);;
graph.renderText(txt, rect.left(), rect.top()-5*graph.printScaleY(), 0);
}
}
#endif

View File

@ -0,0 +1,54 @@
/* gUsageChart Header
*
* Copyright (c) 2019-2022 The Oscar Team
* Copyright (C) 2011-2018 Mark Watkins <mark@jedimark.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code
* for more details. */
#if 1
#ifndef GUSAGECHART_H
#define GUSAGECHART_H
#include "SleepLib/day.h"
#include "SleepLib/profiles.h"
#include "Graphs/gGraphView.h"
#include "Graphs/gSummaryChart.h"
class gUsageChart : public gSummaryChart
{
public:
gUsageChart()
:gSummaryChart("Usage", MT_CPAP) {
addCalc(NoChannel, ST_HOURS, QColor(64,128,255));
}
virtual ~gUsageChart() {}
virtual void preCalc();
virtual void customCalc(Day *, QVector<SummaryChartSlice> &);
virtual void afterDraw(QPainter &, gGraph &, QRectF);
virtual void populate(Day *day, int idx);
virtual QString tooltipData(Day * day, int);
virtual Layer * Clone() {
gUsageChart * sc = new gUsageChart();
gSummaryChart::CloneInto(sc);
CloneInto(sc);
return sc;
}
void CloneInto(gUsageChart * layer) {
layer->incompdays = incompdays;
layer->compliance_threshold = compliance_threshold;
}
private:
int incompdays;
EventDataType compliance_threshold;
};
#endif // GUSAGECHART_H
#endif

View File

@ -34,7 +34,9 @@ AppWideSetting::AppWideSetting(Preferences *pref) : PrefSettings(pref)
m_graphTooltips = initPref(STR_AS_GraphTooltips, true).toBool(); m_graphTooltips = initPref(STR_AS_GraphTooltips, true).toBool();
m_usePixmapCaching = initPref(STR_AS_UsePixmapCaching, false).toBool(); m_usePixmapCaching = initPref(STR_AS_UsePixmapCaching, false).toBool();
m_odt = (OverlayDisplayType)initPref(STR_AS_OverlayType, (int)ODT_Bars).toInt(); m_odt = (OverlayDisplayType)initPref(STR_AS_OverlayType, (int)ODT_Bars).toInt();
#ifndef REMOVE_FITNESS
m_olm = (OverviewLinechartModes)initPref(STR_AS_OverviewLinechartMode, (int)OLC_Bartop).toInt(); m_olm = (OverviewLinechartModes)initPref(STR_AS_OverviewLinechartMode, (int)OLC_Bartop).toInt();
#endif
m_lineThickness=initPref(STR_AS_LineThickness, 1.0).toFloat(); m_lineThickness=initPref(STR_AS_LineThickness, 1.0).toFloat();
m_lineCursorMode = initPref(STR_AS_LineCursorMode, true).toBool(); m_lineCursorMode = initPref(STR_AS_LineCursorMode, true).toBool();
initPref(STR_AS_RightSidebarVisible, false); initPref(STR_AS_RightSidebarVisible, false);

View File

@ -18,7 +18,12 @@
class Preferences; class Preferences;
#define REMOVE_FITNESS
/* valid values are REMOVE_FITNESS or REMOVE_FITNESS_OFF */
#ifndef REMOVE_FITNESS
enum OverviewLinechartModes { OLC_Bartop, OLC_Lines }; enum OverviewLinechartModes { OLC_Bartop, OLC_Lines };
#endif
// ApplicationWideSettings Strings // ApplicationWideSettings Strings
@ -33,7 +38,9 @@ const QString STR_AS_ShowPieChart = "EnablePieChart";
const QString STR_AS_Animations = "AnimationsAndTransitions"; const QString STR_AS_Animations = "AnimationsAndTransitions";
const QString STR_AS_SquareWave = "SquareWavePlots"; const QString STR_AS_SquareWave = "SquareWavePlots";
const QString STR_AS_OverlayType = "OverlayType"; const QString STR_AS_OverlayType = "OverlayType";
#ifndef REMOVE_FITNESS
const QString STR_AS_OverviewLinechartMode = "OverviewLinechartMode"; const QString STR_AS_OverviewLinechartMode = "OverviewLinechartMode";
#endif
const QString STR_AS_UsePixmapCaching = "UsePixmapCaching"; const QString STR_AS_UsePixmapCaching = "UsePixmapCaching";
const QString STR_AS_AllowYAxisScaling = "AllowYAxisScaling"; const QString STR_AS_AllowYAxisScaling = "AllowYAxisScaling";
const QString STR_AS_IncludeSerial = "IncludeSerial"; const QString STR_AS_IncludeSerial = "IncludeSerial";
@ -82,7 +89,9 @@ public:
float m_lineThickness; float m_lineThickness;
OverlayDisplayType m_odt; OverlayDisplayType m_odt;
#ifndef REMOVE_FITNESS
OverviewLinechartModes m_olm; OverviewLinechartModes m_olm;
#endif
QString m_profileName, m_language; QString m_profileName, m_language;
QString versionString() const { return getPref(STR_PREF_VersionString).toString(); } QString versionString() const { return getPref(STR_PREF_VersionString).toString(); }
@ -137,8 +146,10 @@ public:
bool rightSidebarVisible() const { return getPref(STR_AS_RightSidebarVisible).toBool(); } bool rightSidebarVisible() const { return getPref(STR_AS_RightSidebarVisible).toBool(); }
//! \brief Returns the type of overlay flags (which are displayed over the Flow Waveform) //! \brief Returns the type of overlay flags (which are displayed over the Flow Waveform)
inline OverlayDisplayType overlayType() const { return m_odt; } inline OverlayDisplayType overlayType() const { return m_odt; }
#ifndef REMOVE_FITNESS
//! \brief Returns the display type of Overview pages linechart //! \brief Returns the display type of Overview pages linechart
inline OverviewLinechartModes overviewLinechartMode() const { return m_olm; } inline OverviewLinechartModes overviewLinechartMode() const { return m_olm; }
#endif
bool userEventPieChart() const { return getPref(STR_CS_UserEventPieChart).toBool(); } bool userEventPieChart() const { return getPref(STR_CS_UserEventPieChart).toBool(); }
bool showSerialNumbers() const { return getPref(STR_US_ShowSerialNumbers).toBool(); } bool showSerialNumbers() const { return getPref(STR_US_ShowSerialNumbers).toBool(); }
int openTabAtStart() const { return getPref(STR_US_OpenTabAtStart).toInt(); } int openTabAtStart() const { return getPref(STR_US_OpenTabAtStart).toInt(); }
@ -187,7 +198,9 @@ public:
//! \brief Sets whether to allow double clicking on Y-Axis labels to change vertical scaling mode //! \brief Sets whether to allow double clicking on Y-Axis labels to change vertical scaling mode
void setGraphTooltips(bool b) { setPref(STR_AS_GraphTooltips, m_graphTooltips=b); } void setGraphTooltips(bool b) { setPref(STR_AS_GraphTooltips, m_graphTooltips=b); }
//! \brief Sets the type of overlay flags (which are displayed over the Flow Waveform) //! \brief Sets the type of overlay flags (which are displayed over the Flow Waveform)
#ifndef REMOVE_FITNESS
void setOverviewLinechartMode(OverviewLinechartModes olm) { setPref(STR_AS_OverviewLinechartMode, (int)(m_olm=olm)); } void setOverviewLinechartMode(OverviewLinechartModes olm) { setPref(STR_AS_OverviewLinechartMode, (int)(m_olm=olm)); }
#endif
//! \brief Set the pen width of line plots. //! \brief Set the pen width of line plots.
void setLineThickness(float size) { setPref(STR_AS_LineThickness, m_lineThickness=size); } void setLineThickness(float size) { setPref(STR_AS_LineThickness, m_lineThickness=size); }
//! \brief Sets whether to display Line Cursor //! \brief Sets whether to display Line Cursor

View File

@ -1087,7 +1087,11 @@ int CleanupProfile(Profile *prof)
<< STR_AS_AntiAliasing << STR_AS_LineThickness << STR_AS_UsePixmapCaching << STR_AS_AntiAliasing << STR_AS_LineThickness << STR_AS_UsePixmapCaching
<< STR_AS_SquareWave << STR_AS_RightPanelWidth << STR_US_TooltipTimeout << STR_AS_SquareWave << STR_AS_RightPanelWidth << STR_US_TooltipTimeout
<< STR_AS_Animations << STR_AS_AllowYAxisScaling << STR_AS_GraphTooltips << STR_AS_Animations << STR_AS_AllowYAxisScaling << STR_AS_GraphTooltips
<< STR_CS_UserEventPieChart << STR_AS_OverlayType << STR_AS_OverviewLinechartMode; << STR_CS_UserEventPieChart << STR_AS_OverlayType
#ifndef REMOVE_FITNESS
<< STR_AS_OverviewLinechartMode
#endif
;
int cnt = 0; int cnt = 0;
for (auto & prf :migrateList) { for (auto & prf :migrateList) {

View File

@ -136,7 +136,6 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
// Remove Incomplete Extras Tab // Remove Incomplete Extras Tab
//ui->tabWidget->removeTab(3); //ui->tabWidget->removeTab(3);
ZombieMeterMoved=false;
BookmarksChanged=false; BookmarksChanged=false;
lastcpapday=nullptr; lastcpapday=nullptr;
@ -503,6 +502,9 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
ui->splitter->setVisible(false); ui->splitter->setVisible(false);
#ifndef REMOVE_FITNESS
ZombieMeterMoved=false;
if (p_profile->general->unitSystem()==US_English) { if (p_profile->general->unitSystem()==US_English) {
ui->weightSpinBox->setSuffix(STR_UNIT_POUND); ui->weightSpinBox->setSuffix(STR_UNIT_POUND);
ui->weightSpinBox->setDecimals(0); ui->weightSpinBox->setDecimals(0);
@ -513,6 +515,12 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
ui->weightSpinBox->setDecimals(1); ui->weightSpinBox->setDecimals(1);
ui->weightSpinBox->setSuffix(STR_UNIT_KG); ui->weightSpinBox->setSuffix(STR_UNIT_KG);
} }
#else // REMOVE_FITNESS
// hide the parent widget to make the gridlayout invisible
QWidget *myparent ;
myparent = ui->gridLayout->parentWidget();
if(myparent) myparent->hide();
#endif
GraphView->setEmptyImage(QPixmap(":/icons/logo-md.png")); GraphView->setEmptyImage(QPixmap(":/icons/logo-md.png"));
GraphView->setEmptyText(STR_Empty_NoData); GraphView->setEmptyText(STR_Empty_NoData);
@ -926,9 +934,12 @@ void Daily::on_ReloadDay()
// GraphView->fadeOut(); // GraphView->fadeOut();
Unload(previous_date); Unload(previous_date);
} }
unload_time=time.restart(); unload_time=time.restart();
//bool fadedir=previous_date < ui->calendar->selectedDate(); //bool fadedir=previous_date < ui->calendar->selectedDate();
#ifndef REMOVE_FITNESS
ZombieMeterMoved=false; ZombieMeterMoved=false;
#endif
Load(ui->calendar->selectedDate()); Load(ui->calendar->selectedDate());
load_time=time.restart(); load_time=time.restart();
@ -937,6 +948,9 @@ void Daily::on_ReloadDay()
ui->calButton->setText(ui->calendar->selectedDate().toString(MedDateFormat)); ui->calButton->setText(ui->calendar->selectedDate().toString(MedDateFormat));
ui->calendar->setFocus(Qt::ActiveWindowFocusReason); ui->calendar->setFocus(Qt::ActiveWindowFocusReason);
#ifndef REMOVE_FITNESS
ZombieMeterMoved=false;
if (p_profile->general->unitSystem()==US_English) { if (p_profile->general->unitSystem()==US_English) {
ui->weightSpinBox->setSuffix(STR_UNIT_POUND); ui->weightSpinBox->setSuffix(STR_UNIT_POUND);
ui->weightSpinBox->setDecimals(0); ui->weightSpinBox->setDecimals(0);
@ -947,6 +961,7 @@ void Daily::on_ReloadDay()
ui->weightSpinBox->setDecimals(1); ui->weightSpinBox->setDecimals(1);
ui->weightSpinBox->setSuffix(STR_UNIT_KG); ui->weightSpinBox->setSuffix(STR_UNIT_KG);
} }
#endif
this->setCursor(Qt::ArrowCursor); this->setCursor(Qt::ArrowCursor);
other_time=time.restart(); other_time=time.restart();
@ -1882,6 +1897,7 @@ void Daily::Load(QDate date)
//sl.append(tr("Starts")); //sl.append(tr("Starts"));
//sl.append(tr("Notes")); //sl.append(tr("Notes"));
ui->bookmarkTable->setHorizontalHeaderLabels(sl); ui->bookmarkTable->setHorizontalHeaderLabels(sl);
#ifndef REMOVE_FITNESS
ui->ZombieMeter->blockSignals(true); ui->ZombieMeter->blockSignals(true);
ui->weightSpinBox->blockSignals(true); ui->weightSpinBox->blockSignals(true);
ui->ouncesSpinBox->blockSignals(true); ui->ouncesSpinBox->blockSignals(true);
@ -1895,16 +1911,18 @@ void Daily::Load(QDate date)
ui->BMI->display(0); ui->BMI->display(0);
ui->BMI->setVisible(false); ui->BMI->setVisible(false);
ui->BMIlabel->setVisible(false); ui->BMIlabel->setVisible(false);
#endif
ui->toggleGraphs->setVisible(false); ui->toggleGraphs->setVisible(false);
ui->toggleEvents->setVisible(false); ui->toggleEvents->setVisible(false);
BookmarksChanged=false; BookmarksChanged=false;
Session *journal=GetJournalSession(date); Session *journal=GetJournalSession(date);
if (journal) { if (journal) {
bool ok;
if (journal->settings.contains(Journal_Notes)) if (journal->settings.contains(Journal_Notes))
ui->JournalNotes->setHtml(journal->settings[Journal_Notes].toString()); ui->JournalNotes->setHtml(journal->settings[Journal_Notes].toString());
#ifndef REMOVE_FITNESS
bool ok;
if (journal->settings.contains(Journal_Weight)) { if (journal->settings.contains(Journal_Weight)) {
double kg=journal->settings[Journal_Weight].toDouble(&ok); double kg=journal->settings[Journal_Weight].toDouble(&ok);
@ -1947,6 +1965,7 @@ void Daily::Load(QDate date)
ui->ZombieMeter->setValue(journal->settings[Journal_ZombieMeter].toDouble(&ok)); ui->ZombieMeter->setValue(journal->settings[Journal_ZombieMeter].toDouble(&ok));
ui->ZombieMeter->blockSignals(false); ui->ZombieMeter->blockSignals(false);
} }
#endif
if (journal->settings.contains(Bookmark_Start)) { if (journal->settings.contains(Bookmark_Start)) {
QVariantList start=journal->settings[Bookmark_Start].toList(); QVariantList start=journal->settings[Bookmark_Start].toList();
@ -1984,6 +2003,7 @@ void Daily::Load(QDate date)
void Daily::UnitsChanged() void Daily::UnitsChanged()
{ {
#ifndef REMOVE_FITNESS
double kg; double kg;
if (p_profile->general->unitSystem()==US_English) { if (p_profile->general->unitSystem()==US_English) {
kg=ui->weightSpinBox->value(); kg=ui->weightSpinBox->value();
@ -2005,6 +2025,7 @@ void Daily::UnitsChanged()
ui->ouncesSpinBox->setVisible(false); ui->ouncesSpinBox->setVisible(false);
ui->weightSpinBox->setSuffix(STR_UNIT_KG); ui->weightSpinBox->setSuffix(STR_UNIT_KG);
} }
#endif
} }
void Daily::clearLastDay() void Daily::clearLastDay()
@ -2491,6 +2512,7 @@ void Daily::on_removeBookmarkButton_clicked()
} }
mainwin->updateFavourites(); mainwin->updateFavourites();
} }
#ifndef REMOVE_FITNESS
void Daily::on_ZombieMeter_valueChanged(int action) void Daily::on_ZombieMeter_valueChanged(int action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
@ -2503,6 +2525,7 @@ void Daily::on_ZombieMeter_valueChanged(int action)
journal->SetChanged(true); journal->SetChanged(true);
mainwin->updateOverview(); mainwin->updateOverview();
} }
#endif
void Daily::on_bookmarkTable_itemChanged(QTableWidgetItem *item) void Daily::on_bookmarkTable_itemChanged(QTableWidgetItem *item)
{ {
@ -2510,6 +2533,7 @@ void Daily::on_bookmarkTable_itemChanged(QTableWidgetItem *item)
update_Bookmarks(); update_Bookmarks();
} }
#ifndef REMOVE_FITNESS
void Daily::on_weightSpinBox_valueChanged(double arg1) void Daily::on_weightSpinBox_valueChanged(double arg1)
{ {
// This is called if up/down arrows are used, in which case editingFinished is // This is called if up/down arrows are used, in which case editingFinished is
@ -2596,6 +2620,7 @@ void Daily::on_ouncesSpinBox_editingFinished()
// This is functionally identical to the weightSpinBox_editingFinished, so just call that // This is functionally identical to the weightSpinBox_editingFinished, so just call that
this->on_weightSpinBox_editingFinished(); this->on_weightSpinBox_editingFinished();
} }
#endif
QString Daily::GetDetailsText() QString Daily::GetDetailsText()
{ {

View File

@ -10,7 +10,6 @@
#ifndef DAILY_H #ifndef DAILY_H
#define DAILY_H #define DAILY_H
#include <QMenu> #include <QMenu>
#include <QAction> #include <QAction>
#include <QWidget> #include <QWidget>
@ -25,12 +24,12 @@
#include "SleepLib/profiles.h" #include "SleepLib/profiles.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "Graphs/gSummaryChart.h"
#include "Graphs/gGraphView.h" #include "Graphs/gGraphView.h"
#include "Graphs/gLineChart.h" #include "Graphs/gLineChart.h"
#include "sessionbar.h" #include "sessionbar.h"
#include "mytextbrowser.h" #include "mytextbrowser.h"
namespace Ui { namespace Ui {
class Daily; class Daily;
} }
@ -230,7 +229,12 @@ private slots:
*/ */
void on_bookmarkTable_itemChanged(QTableWidgetItem *item); void on_bookmarkTable_itemChanged(QTableWidgetItem *item);
void on_graphCombo_activated(int index);
void on_toggleGraphs_clicked(bool checked);
#ifndef REMOVE_FITNESS
/*! \fn on_ouncesSpinBox_valueChanged(int arg1); /*! \fn on_ouncesSpinBox_valueChanged(int arg1);
\brief Called when the zombie slider has been moved.. Updates the BMI dislpay and journal objects. \brief Called when the zombie slider has been moved.. Updates the BMI dislpay and journal objects.
@ -238,10 +242,6 @@ private slots:
*/ */
void on_ZombieMeter_valueChanged(int value); void on_ZombieMeter_valueChanged(int value);
void on_graphCombo_activated(int index);
void on_toggleGraphs_clicked(bool checked);
/*! \fn on_weightSpinBox_editingFinished(); /*! \fn on_weightSpinBox_editingFinished();
\brief Called when weight has changed.. Updates the BMI dislpay and journal objects. \brief Called when weight has changed.. Updates the BMI dislpay and journal objects.
@ -260,6 +260,7 @@ private slots:
void on_ouncesSpinBox_valueChanged(int arg1); void on_ouncesSpinBox_valueChanged(int arg1);
void on_weightSpinBox_valueChanged(double arg1); void on_weightSpinBox_valueChanged(double arg1);
#endif
void doToggleSession(Session *); void doToggleSession(Session *);
@ -354,7 +355,9 @@ private:
gLineChart *leakchart; gLineChart *leakchart;
#ifndef REMOVE_FITNESS
bool ZombieMeterMoved; bool ZombieMeterMoved;
#endif
bool BookmarksChanged; bool BookmarksChanged;
}; };

View File

@ -280,6 +280,12 @@ SOURCES += \
Graphs/gspacer.cpp \ Graphs/gspacer.cpp \
Graphs/gStatsLine.cpp \ Graphs/gStatsLine.cpp \
Graphs/gSummaryChart.cpp \ Graphs/gSummaryChart.cpp \
Graphs/gAHIChart.cpp \
Graphs/gTTIAChart.cpp \
Graphs/gUsageChart.cpp \
Graphs/gSessionTimesChart.cpp \
Graphs/gPressureChart.cpp \
Graphs/gOverviewGraph.cpp \
Graphs/gXAxis.cpp \ Graphs/gXAxis.cpp \
Graphs/gYAxis.cpp \ Graphs/gYAxis.cpp \
Graphs/layer.cpp \ Graphs/layer.cpp \
@ -323,8 +329,6 @@ SOURCES += \
SleepLib/xmlreplay.cpp \ SleepLib/xmlreplay.cpp \
SleepLib/serialoximeter.cpp \ SleepLib/serialoximeter.cpp \
SleepLib/loader_plugins/md300w1_loader.cpp \ SleepLib/loader_plugins/md300w1_loader.cpp \
Graphs/gSessionTimesChart.cpp \
Graphs/gPressureChart.cpp \
logger.cpp \ logger.cpp \
SleepLib/machine_common.cpp \ SleepLib/machine_common.cpp \
SleepLib/loader_plugins/weinmann_loader.cpp \ SleepLib/loader_plugins/weinmann_loader.cpp \
@ -382,6 +386,12 @@ HEADERS += \
Graphs/gspacer.h \ Graphs/gspacer.h \
Graphs/gStatsLine.h \ Graphs/gStatsLine.h \
Graphs/gSummaryChart.h \ Graphs/gSummaryChart.h \
Graphs/gAHIChart.h \
Graphs/gTTIAChart.h \
Graphs/gUsageChart.h \
Graphs/gSessionTimesChart.h \
Graphs/gPressureChart.h \
Graphs/gOverviewGraph.h \
Graphs/gXAxis.h \ Graphs/gXAxis.h \
Graphs/gYAxis.h \ Graphs/gYAxis.h \
Graphs/layer.h \ Graphs/layer.h \
@ -428,8 +438,6 @@ HEADERS += \
SleepLib/xmlreplay.h \ SleepLib/xmlreplay.h \
SleepLib/serialoximeter.h \ SleepLib/serialoximeter.h \
SleepLib/loader_plugins/md300w1_loader.h \ SleepLib/loader_plugins/md300w1_loader.h \
Graphs/gSessionTimesChart.h \
Graphs/gPressureChart.h \
logger.h \ logger.h \
SleepLib/loader_plugins/weinmann_loader.h \ SleepLib/loader_plugins/weinmann_loader.h \
Graphs/gdailysummary.h \ Graphs/gdailysummary.h \

View File

@ -29,7 +29,11 @@
#include "Graphs/gXAxis.h" #include "Graphs/gXAxis.h"
#include "Graphs/gLineChart.h" #include "Graphs/gLineChart.h"
#include "Graphs/gYAxis.h" #include "Graphs/gYAxis.h"
#include "Graphs/gSessionTimesChart.h"
#include "Graphs/gPressureChart.h" #include "Graphs/gPressureChart.h"
#include "Graphs/gAHIChart.h"
#include "Graphs/gUsageChart.h"
#include "Graphs/gTTIAChart.h"
#include "cprogressbar.h" #include "cprogressbar.h"
#include "mainwindow.h" #include "mainwindow.h"
@ -263,7 +267,8 @@ void Overview::CreateAllGraphs() {
//chartsToBeMonitored.insert(ahi,AHI); //chartsToBeMonitored.insert(ahi,AHI);
UC = createGraph(STR_GRAPH_Usage, tr("Usage"), tr("Usage\n(hours)")); UC = createGraph(STR_GRAPH_Usage, tr("Usage"), tr("Usage\n(hours)"));
UC->AddLayer(uc = new gUsageChart()); uc = new gUsageChart();
UC->AddLayer(uc);
//chartsToBeMonitored.insert(uc,UC); //chartsToBeMonitored.insert(uc,UC);
STG = createGraph("New Session", tr("Session Times"), tr("Session Times"), YT_Time); STG = createGraph("New Session", tr("Session Times"), tr("Session Times"), YT_Time);
@ -317,24 +322,28 @@ void Overview::CreateAllGraphs() {
} }
} // if showInOverview() } // if showInOverview()
} // for chit } // for chit
// Note The following don not use gSummaryChart. They use SummaryChart instead. and can not be monitored. #ifndef REMOVE_FITNESS
/* To enable these changes: change the REMOTE_FITNESS define in appsettings.h */
// Note The following do not use gSummaryChart. They use gOverviewGraph instead. and can not be monitored.
WEIGHT = createGraph(STR_GRAPH_Weight, STR_TR_Weight, STR_TR_Weight, YT_Weight); WEIGHT = createGraph(STR_GRAPH_Weight, STR_TR_Weight, STR_TR_Weight, YT_Weight);
weight = new SummaryChart("Weight", GT_LINE); weight = new gOverviewGraph("Weight", GT_LINE);
weight->setMachineType(MT_JOURNAL); weight->setMachineType(MT_JOURNAL);
weight->addSlice(Journal_Weight, QColor("black"), ST_SETAVG); weight->addSlice(Journal_Weight, QColor("black"), ST_SETAVG);
WEIGHT->AddLayer(weight); WEIGHT->AddLayer(weight);
BMI = createGraph(STR_GRAPH_BMI, STR_TR_BMI, tr("Body\nMass\nIndex")); BMI = createGraph(STR_GRAPH_BMI, STR_TR_BMI, tr("Body\nMass\nIndex"));
bmi = new SummaryChart("BMI", GT_LINE); bmi = new gOverviewGraph("BMI", GT_LINE);
bmi->setMachineType(MT_JOURNAL); bmi->setMachineType(MT_JOURNAL);
bmi->addSlice(Journal_BMI, QColor("black"), ST_SETAVG); bmi->addSlice(Journal_BMI, QColor("black"), ST_SETAVG);
BMI->AddLayer(bmi); BMI->AddLayer(bmi);
ZOMBIE = createGraph(STR_GRAPH_Zombie, STR_TR_Zombie, tr("How you felt\n(0-10)")); ZOMBIE = createGraph(STR_GRAPH_Zombie, STR_TR_Zombie, tr("How you felt\n(0-10)"));
zombie = new SummaryChart("Zombie", GT_LINE); zombie = new gOverviewGraph("Zombie", GT_LINE);
zombie->setMachineType(MT_JOURNAL); zombie->setMachineType(MT_JOURNAL);
zombie->addSlice(Journal_ZombieMeter, QColor("black"), ST_SETAVG); zombie->addSlice(Journal_ZombieMeter, QColor("black"), ST_SETAVG);
ZOMBIE->AddLayer(zombie); ZOMBIE->AddLayer(zombie);
#endif
connectgSummaryCharts(); connectgSummaryCharts();
} }

View File

@ -19,8 +19,10 @@
#include <QTimer> #include <QTimer>
#include "SleepLib/profiles.h" #include "SleepLib/profiles.h"
#include "Graphs/gGraphView.h" #include "Graphs/gGraphView.h"
#ifndef REMOVE_FITNESS
#include "Graphs/gOverviewGraph.h"
#endif
#include "Graphs/gSummaryChart.h" #include "Graphs/gSummaryChart.h"
#include "Graphs/gSessionTimesChart.h"
namespace Ui { namespace Ui {
class Overview; class Overview;
@ -96,8 +98,10 @@ class Overview : public QWidget
gGraph *createGraph(QString code, QString name, QString units = "", YTickerType yttype = YT_Number); gGraph *createGraph(QString code, QString name, QString units = "", YTickerType yttype = YT_Number);
gGraph *AHI, *AHIHR, *UC, *FL, *SA, *US, *PR, *LK, *NPB, *SET, *SES, *RR, *MV, *TV, *PTB, *PULSE, *SPO2, *NLL, gGraph *AHI, *AHIHR, *UC, *FL, *SA, *US, *PR, *LK, *NPB, *SET, *SES, *RR, *MV, *TV, *PTB, *PULSE, *SPO2, *NLL,
*WEIGHT, *ZOMBIE, *BMI, *TGMV, *TOTLK, *STG, *SN, *TTIA; *WEIGHT, *ZOMBIE, *BMI, *TGMV, *TOTLK, *STG, *SN, *TTIA;
SummaryChart *bc, *sa, *us, *pr, *set, *ses, *ptb, *pulse, *spo2, #ifndef REMOVE_FITNESS
gOverviewGraph *bc, *sa, *us, *pr, *set, *ses, *ptb, *pulse, *spo2,
*weight, *zombie, *bmi, *ahihr, *tgmv, *totlk; *weight, *zombie, *bmi, *ahihr, *tgmv, *totlk;
#endif
gSummaryChart * stg, *uc, *ahi, * pres, *lk, *npb, *rr, *mv, *tv, *nll, *sn, *ttia; gSummaryChart * stg, *uc, *ahi, * pres, *lk, *npb, *rr, *mv, *tv, *nll, *sn, *ttia;

View File

@ -296,7 +296,12 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) :
#endif #endif
ui->overlayFlagsCombo->setCurrentIndex(AppSetting->overlayType()); ui->overlayFlagsCombo->setCurrentIndex(AppSetting->overlayType());
#ifndef REMOVE_FITNESS
ui->overviewLinecharts->setCurrentIndex(AppSetting->overviewLinechartMode()); ui->overviewLinecharts->setCurrentIndex(AppSetting->overviewLinechartMode());
#else
ui->overviewLinecharts->hide();
ui->overviewLinechartsLabel->hide();
#endif
ui->ahiGraphWindowSize->setEnabled(false); ui->ahiGraphWindowSize->setEnabled(false);
ui->ahiGraphWindowSize->setValue(profile->cpap->AHIWindow()); ui->ahiGraphWindowSize->setValue(profile->cpap->AHIWindow());
@ -883,7 +888,9 @@ bool PreferencesDialog::Save()
profile->cpap->setClockDrift(s); profile->cpap->setClockDrift(s);
AppSetting->setOverlayType((OverlayDisplayType)ui->overlayFlagsCombo->currentIndex()); AppSetting->setOverlayType((OverlayDisplayType)ui->overlayFlagsCombo->currentIndex());
#ifndef REMOVE_FITNESS
AppSetting->setOverviewLinechartMode((OverviewLinechartModes)ui->overviewLinecharts->currentIndex()); AppSetting->setOverviewLinechartMode((OverviewLinechartModes)ui->overviewLinecharts->currentIndex());
#endif
profile->oxi->setSpO2DropPercentage(ui->spo2Drop->value()); profile->oxi->setSpO2DropPercentage(ui->spo2Drop->value());
profile->oxi->setSpO2DropDuration(ui->spo2DropTime->value()); profile->oxi->setSpO2DropDuration(ui->spo2DropTime->value());

View File

@ -2632,7 +2632,7 @@ Mainly affects the importer.</string>
</layout> </layout>
</item> </item>
<item row="5" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_47"> <widget class="QLabel" name="overviewLinechartsLabel">
<property name="text"> <property name="text">
<string>Overview Linecharts</string> <string>Overview Linecharts</string>
</property> </property>