mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-05 10:40:42 +00:00
Merge branch 'master' into vAuto-Settings
This commit is contained in:
commit
be7d95c924
File diff suppressed because it is too large
Load Diff
@ -257,7 +257,7 @@ In verband met de koppeling met Bladwijzers, lijkt me 'Notities' beter
|
||||
<message>
|
||||
<location filename="../oscar/daily.ui" line="1492"/>
|
||||
<source>Flags</source>
|
||||
<translation>Markeringen</translation>
|
||||
<translation>Incident markeringen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../oscar/daily.ui" line="1544"/>
|
||||
@ -3091,7 +3091,7 @@ anders is het geen AHI/uur meer.</translation>
|
||||
<message>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="706"/>
|
||||
<source>CPAP Clock Drift</source>
|
||||
<translation>Correctie afwijking klok CPAP</translation>
|
||||
<translation>Correctie afwijking CPAP-klok</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="502"/>
|
||||
@ -3316,7 +3316,7 @@ Werkt vooral bij importeren.</translation>
|
||||
<message>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="1193"/>
|
||||
<source>Enable Unknown Events Channels</source>
|
||||
<translation>Zet de kanalen van onbekende gebeurtenissen aan</translation>
|
||||
<translation>Kanalen van onbekende gebeurtenissen aan</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="2637"/>
|
||||
<source>Overview Linecharts</source>
|
||||
<translation>Overzicht lijngrafieken</translation>
|
||||
<translation>Soort grafieken in overzicht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="2384"/>
|
||||
<source>Overlay Flags</source>
|
||||
<translation>Markeringen</translation>
|
||||
<translation>Incident markeringen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="2415"/>
|
||||
<source>Standard Bars</source>
|
||||
<translation>Standaardbalken</translation>
|
||||
<translation>Lange markeringen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="810"/>
|
||||
<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>
|
||||
<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>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="2454"/>
|
||||
<source>Tooltip Timeout</source>
|
||||
<translation>Tooltip timeout</translation>
|
||||
<translation>Tijdsduur tooltips</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="2420"/>
|
||||
<source>Top Markers</source>
|
||||
<translation>Top markeringen</translation>
|
||||
<translation>Aanduiding aan bovenzijde</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="618"/>
|
||||
<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>
|
||||
<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>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="2319"/>
|
||||
<source>Switch Tabs</source>
|
||||
<translation>Wissel tabbladen</translation>
|
||||
<translation>Kies tabbladen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../oscar/preferencesdialog.ui" line="2330"/>
|
||||
|
184
oscar/Graphs/gAHIChart.cpp
Normal file
184
oscar/Graphs/gAHIChart.cpp
Normal 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
78
oscar/Graphs/gAHIChart.h
Normal 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
|
||||
|
@ -38,8 +38,10 @@
|
||||
#include "mainwindow.h"
|
||||
#include "Graphs/glcommon.h"
|
||||
#include "Graphs/gLineChart.h"
|
||||
#ifndef REMOVE_FITNESS
|
||||
#include "Graphs/gOverviewGraph.h"
|
||||
#endif
|
||||
#include "Graphs/gSummaryChart.h"
|
||||
#include "Graphs/gSessionTimesChart.h"
|
||||
#include "Graphs/gYAxis.h"
|
||||
#include "Graphs/gFlagsLine.h"
|
||||
#include "SleepLib/profiles.h"
|
||||
@ -2199,11 +2201,18 @@ void gGraphView::populateMenu(gGraph * graph)
|
||||
font.setPointSize(font.pointSize() + 3);
|
||||
|
||||
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));
|
||||
|
||||
limits_menu->clear();
|
||||
if (lc || sc || stg) {
|
||||
#ifndef REMOVE_FITNESS
|
||||
if (lc || sc || stg )
|
||||
#else
|
||||
if (lc || stg )
|
||||
#endif
|
||||
{
|
||||
QWidgetAction * widget = new QWidgetAction(this);
|
||||
MinMaxWidget * minmax = new MinMaxWidget(graph, this);
|
||||
|
||||
|
1308
oscar/Graphs/gOverviewGraph.cpp
Normal file
1308
oscar/Graphs/gOverviewGraph.cpp
Normal file
File diff suppressed because it is too large
Load Diff
170
oscar/Graphs/gOverviewGraph.h
Normal file
170
oscar/Graphs/gOverviewGraph.h
Normal 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 ®ion);
|
||||
|
||||
//! \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
|
@ -10,7 +10,7 @@
|
||||
#ifndef GPRESSURECHART_H
|
||||
#define GPRESSURECHART_H
|
||||
|
||||
#include "gSessionTimesChart.h"
|
||||
#include "gSummaryChart.h"
|
||||
|
||||
class gPressureChart : public gSummaryChart
|
||||
{
|
||||
|
@ -23,746 +23,7 @@
|
||||
|
||||
extern MainWindow * mainwin;
|
||||
|
||||
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 ®ion)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// short SummaryCalcItem::midcalc;
|
||||
|
||||
void gSessionTimesChart::preCalc() {
|
||||
|
||||
@ -1098,230 +359,3 @@ void gSessionTimesChart::paint(QPainter &painter, gGraph &graph, const QRegion &
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -12,261 +12,9 @@
|
||||
|
||||
#include "SleepLib/day.h"
|
||||
#include "SleepLib/profiles.h"
|
||||
#include "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/gGraphView.h"
|
||||
|
||||
#include "Graphs/gSummaryChart.h"
|
||||
|
||||
|
||||
/*! \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
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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>
|
||||
*
|
||||
* 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 GSUMMARYCHART_H
|
||||
#define GSUMMARYCHART_H
|
||||
|
||||
#ifndef GBARCHART_H
|
||||
#define GBARCHART_H
|
||||
|
||||
#include <SleepLib/profiles.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 };
|
||||
#include "SleepLib/day.h"
|
||||
#include "SleepLib/profiles.h"
|
||||
#include "Graphs/gGraphView.h"
|
||||
#include "SleepLib/appsettings.h"
|
||||
|
||||
|
||||
/*! \class SummaryChart
|
||||
\brief The main overall chart type layer used in Overview page
|
||||
*/
|
||||
class SummaryChart: public Layer
|
||||
struct TimeSpan
|
||||
{
|
||||
public:
|
||||
//! \brief Constructs a SummaryChart with QString label, of GraphType type
|
||||
SummaryChart(QString label, GraphType type = GT_BAR);
|
||||
virtual ~SummaryChart();
|
||||
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 &painter, gGraph &w, const QRegion ®ion);
|
||||
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);
|
||||
|
||||
//! \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 summaryChart (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 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();
|
||||
}
|
||||
|
||||
//! \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 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 int addCalc(ChannelID code, SummaryType type, QColor color);
|
||||
virtual int addCalc(ChannelID code, SummaryType type);
|
||||
|
||||
virtual Layer * Clone() {
|
||||
SummaryChart * sc = new SummaryChart(m_label);
|
||||
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(SummaryChart * 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;
|
||||
|
||||
void CloneInto(gSummaryChart * layer) {
|
||||
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;
|
||||
|
||||
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:
|
||||
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;
|
||||
|
||||
protected:
|
||||
//! \brief Key was pressed that effects this layer
|
||||
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)
|
||||
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
|
||||
|
98
oscar/Graphs/gTTIAChart.cpp
Normal file
98
oscar/Graphs/gTTIAChart.cpp
Normal 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
46
oscar/Graphs/gTTIAChart.h
Normal 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
|
102
oscar/Graphs/gUsageChart.cpp
Normal file
102
oscar/Graphs/gUsageChart.cpp
Normal 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
|
54
oscar/Graphs/gUsageChart.h
Normal file
54
oscar/Graphs/gUsageChart.h
Normal 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
|
@ -34,7 +34,9 @@ AppWideSetting::AppWideSetting(Preferences *pref) : PrefSettings(pref)
|
||||
m_graphTooltips = initPref(STR_AS_GraphTooltips, true).toBool();
|
||||
m_usePixmapCaching = initPref(STR_AS_UsePixmapCaching, false).toBool();
|
||||
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();
|
||||
#endif
|
||||
m_lineThickness=initPref(STR_AS_LineThickness, 1.0).toFloat();
|
||||
m_lineCursorMode = initPref(STR_AS_LineCursorMode, true).toBool();
|
||||
initPref(STR_AS_RightSidebarVisible, false);
|
||||
|
@ -18,7 +18,12 @@
|
||||
|
||||
class Preferences;
|
||||
|
||||
#define REMOVE_FITNESS
|
||||
/* valid values are REMOVE_FITNESS or REMOVE_FITNESS_OFF */
|
||||
|
||||
#ifndef REMOVE_FITNESS
|
||||
enum OverviewLinechartModes { OLC_Bartop, OLC_Lines };
|
||||
#endif
|
||||
|
||||
|
||||
// ApplicationWideSettings Strings
|
||||
@ -33,7 +38,9 @@ const QString STR_AS_ShowPieChart = "EnablePieChart";
|
||||
const QString STR_AS_Animations = "AnimationsAndTransitions";
|
||||
const QString STR_AS_SquareWave = "SquareWavePlots";
|
||||
const QString STR_AS_OverlayType = "OverlayType";
|
||||
#ifndef REMOVE_FITNESS
|
||||
const QString STR_AS_OverviewLinechartMode = "OverviewLinechartMode";
|
||||
#endif
|
||||
const QString STR_AS_UsePixmapCaching = "UsePixmapCaching";
|
||||
const QString STR_AS_AllowYAxisScaling = "AllowYAxisScaling";
|
||||
const QString STR_AS_IncludeSerial = "IncludeSerial";
|
||||
@ -82,7 +89,9 @@ public:
|
||||
float m_lineThickness;
|
||||
|
||||
OverlayDisplayType m_odt;
|
||||
#ifndef REMOVE_FITNESS
|
||||
OverviewLinechartModes m_olm;
|
||||
#endif
|
||||
QString m_profileName, m_language;
|
||||
|
||||
QString versionString() const { return getPref(STR_PREF_VersionString).toString(); }
|
||||
@ -137,8 +146,10 @@ public:
|
||||
bool rightSidebarVisible() const { return getPref(STR_AS_RightSidebarVisible).toBool(); }
|
||||
//! \brief Returns the type of overlay flags (which are displayed over the Flow Waveform)
|
||||
inline OverlayDisplayType overlayType() const { return m_odt; }
|
||||
#ifndef REMOVE_FITNESS
|
||||
//! \brief Returns the display type of Overview pages linechart
|
||||
inline OverviewLinechartModes overviewLinechartMode() const { return m_olm; }
|
||||
#endif
|
||||
bool userEventPieChart() const { return getPref(STR_CS_UserEventPieChart).toBool(); }
|
||||
bool showSerialNumbers() const { return getPref(STR_US_ShowSerialNumbers).toBool(); }
|
||||
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
|
||||
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)
|
||||
#ifndef REMOVE_FITNESS
|
||||
void setOverviewLinechartMode(OverviewLinechartModes olm) { setPref(STR_AS_OverviewLinechartMode, (int)(m_olm=olm)); }
|
||||
#endif
|
||||
//! \brief Set the pen width of line plots.
|
||||
void setLineThickness(float size) { setPref(STR_AS_LineThickness, m_lineThickness=size); }
|
||||
//! \brief Sets whether to display Line Cursor
|
||||
|
@ -1087,7 +1087,11 @@ int CleanupProfile(Profile *prof)
|
||||
<< STR_AS_AntiAliasing << STR_AS_LineThickness << STR_AS_UsePixmapCaching
|
||||
<< STR_AS_SquareWave << STR_AS_RightPanelWidth << STR_US_TooltipTimeout
|
||||
<< 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;
|
||||
for (auto & prf :migrateList) {
|
||||
|
@ -136,7 +136,6 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
|
||||
// Remove Incomplete Extras Tab
|
||||
//ui->tabWidget->removeTab(3);
|
||||
|
||||
ZombieMeterMoved=false;
|
||||
BookmarksChanged=false;
|
||||
|
||||
lastcpapday=nullptr;
|
||||
@ -503,6 +502,9 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
|
||||
|
||||
ui->splitter->setVisible(false);
|
||||
|
||||
#ifndef REMOVE_FITNESS
|
||||
ZombieMeterMoved=false;
|
||||
|
||||
if (p_profile->general->unitSystem()==US_English) {
|
||||
ui->weightSpinBox->setSuffix(STR_UNIT_POUND);
|
||||
ui->weightSpinBox->setDecimals(0);
|
||||
@ -513,6 +515,12 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
|
||||
ui->weightSpinBox->setDecimals(1);
|
||||
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->setEmptyText(STR_Empty_NoData);
|
||||
@ -926,9 +934,12 @@ void Daily::on_ReloadDay()
|
||||
// GraphView->fadeOut();
|
||||
Unload(previous_date);
|
||||
}
|
||||
|
||||
unload_time=time.restart();
|
||||
//bool fadedir=previous_date < ui->calendar->selectedDate();
|
||||
#ifndef REMOVE_FITNESS
|
||||
ZombieMeterMoved=false;
|
||||
#endif
|
||||
Load(ui->calendar->selectedDate());
|
||||
load_time=time.restart();
|
||||
|
||||
@ -937,6 +948,9 @@ void Daily::on_ReloadDay()
|
||||
ui->calButton->setText(ui->calendar->selectedDate().toString(MedDateFormat));
|
||||
ui->calendar->setFocus(Qt::ActiveWindowFocusReason);
|
||||
|
||||
#ifndef REMOVE_FITNESS
|
||||
ZombieMeterMoved=false;
|
||||
|
||||
if (p_profile->general->unitSystem()==US_English) {
|
||||
ui->weightSpinBox->setSuffix(STR_UNIT_POUND);
|
||||
ui->weightSpinBox->setDecimals(0);
|
||||
@ -947,6 +961,7 @@ void Daily::on_ReloadDay()
|
||||
ui->weightSpinBox->setDecimals(1);
|
||||
ui->weightSpinBox->setSuffix(STR_UNIT_KG);
|
||||
}
|
||||
#endif
|
||||
this->setCursor(Qt::ArrowCursor);
|
||||
other_time=time.restart();
|
||||
|
||||
@ -1882,6 +1897,7 @@ void Daily::Load(QDate date)
|
||||
//sl.append(tr("Starts"));
|
||||
//sl.append(tr("Notes"));
|
||||
ui->bookmarkTable->setHorizontalHeaderLabels(sl);
|
||||
#ifndef REMOVE_FITNESS
|
||||
ui->ZombieMeter->blockSignals(true);
|
||||
ui->weightSpinBox->blockSignals(true);
|
||||
ui->ouncesSpinBox->blockSignals(true);
|
||||
@ -1895,16 +1911,18 @@ void Daily::Load(QDate date)
|
||||
ui->BMI->display(0);
|
||||
ui->BMI->setVisible(false);
|
||||
ui->BMIlabel->setVisible(false);
|
||||
#endif
|
||||
ui->toggleGraphs->setVisible(false);
|
||||
ui->toggleEvents->setVisible(false);
|
||||
|
||||
BookmarksChanged=false;
|
||||
Session *journal=GetJournalSession(date);
|
||||
if (journal) {
|
||||
bool ok;
|
||||
if (journal->settings.contains(Journal_Notes))
|
||||
ui->JournalNotes->setHtml(journal->settings[Journal_Notes].toString());
|
||||
|
||||
#ifndef REMOVE_FITNESS
|
||||
bool ok;
|
||||
if (journal->settings.contains(Journal_Weight)) {
|
||||
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->blockSignals(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (journal->settings.contains(Bookmark_Start)) {
|
||||
QVariantList start=journal->settings[Bookmark_Start].toList();
|
||||
@ -1984,6 +2003,7 @@ void Daily::Load(QDate date)
|
||||
|
||||
void Daily::UnitsChanged()
|
||||
{
|
||||
#ifndef REMOVE_FITNESS
|
||||
double kg;
|
||||
if (p_profile->general->unitSystem()==US_English) {
|
||||
kg=ui->weightSpinBox->value();
|
||||
@ -2005,6 +2025,7 @@ void Daily::UnitsChanged()
|
||||
ui->ouncesSpinBox->setVisible(false);
|
||||
ui->weightSpinBox->setSuffix(STR_UNIT_KG);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Daily::clearLastDay()
|
||||
@ -2491,6 +2512,7 @@ void Daily::on_removeBookmarkButton_clicked()
|
||||
}
|
||||
mainwin->updateFavourites();
|
||||
}
|
||||
#ifndef REMOVE_FITNESS
|
||||
void Daily::on_ZombieMeter_valueChanged(int action)
|
||||
{
|
||||
Q_UNUSED(action);
|
||||
@ -2503,6 +2525,7 @@ void Daily::on_ZombieMeter_valueChanged(int action)
|
||||
journal->SetChanged(true);
|
||||
mainwin->updateOverview();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Daily::on_bookmarkTable_itemChanged(QTableWidgetItem *item)
|
||||
{
|
||||
@ -2510,6 +2533,7 @@ void Daily::on_bookmarkTable_itemChanged(QTableWidgetItem *item)
|
||||
update_Bookmarks();
|
||||
}
|
||||
|
||||
#ifndef REMOVE_FITNESS
|
||||
void Daily::on_weightSpinBox_valueChanged(double arg1)
|
||||
{
|
||||
// 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->on_weightSpinBox_editingFinished();
|
||||
}
|
||||
#endif
|
||||
|
||||
QString Daily::GetDetailsText()
|
||||
{
|
||||
|
@ -10,7 +10,6 @@
|
||||
#ifndef DAILY_H
|
||||
#define DAILY_H
|
||||
|
||||
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QWidget>
|
||||
@ -25,12 +24,12 @@
|
||||
|
||||
#include "SleepLib/profiles.h"
|
||||
#include "mainwindow.h"
|
||||
#include "Graphs/gSummaryChart.h"
|
||||
#include "Graphs/gGraphView.h"
|
||||
#include "Graphs/gLineChart.h"
|
||||
#include "sessionbar.h"
|
||||
#include "mytextbrowser.h"
|
||||
|
||||
|
||||
namespace Ui {
|
||||
class Daily;
|
||||
}
|
||||
@ -230,7 +229,12 @@ private slots:
|
||||
*/
|
||||
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);
|
||||
\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_graphCombo_activated(int index);
|
||||
|
||||
void on_toggleGraphs_clicked(bool checked);
|
||||
|
||||
/*! \fn on_weightSpinBox_editingFinished();
|
||||
\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_weightSpinBox_valueChanged(double arg1);
|
||||
#endif
|
||||
|
||||
void doToggleSession(Session *);
|
||||
|
||||
@ -354,7 +355,9 @@ private:
|
||||
|
||||
gLineChart *leakchart;
|
||||
|
||||
#ifndef REMOVE_FITNESS
|
||||
bool ZombieMeterMoved;
|
||||
#endif
|
||||
bool BookmarksChanged;
|
||||
|
||||
};
|
||||
|
@ -280,6 +280,12 @@ SOURCES += \
|
||||
Graphs/gspacer.cpp \
|
||||
Graphs/gStatsLine.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/gYAxis.cpp \
|
||||
Graphs/layer.cpp \
|
||||
@ -323,8 +329,6 @@ SOURCES += \
|
||||
SleepLib/xmlreplay.cpp \
|
||||
SleepLib/serialoximeter.cpp \
|
||||
SleepLib/loader_plugins/md300w1_loader.cpp \
|
||||
Graphs/gSessionTimesChart.cpp \
|
||||
Graphs/gPressureChart.cpp \
|
||||
logger.cpp \
|
||||
SleepLib/machine_common.cpp \
|
||||
SleepLib/loader_plugins/weinmann_loader.cpp \
|
||||
@ -382,6 +386,12 @@ HEADERS += \
|
||||
Graphs/gspacer.h \
|
||||
Graphs/gStatsLine.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/gYAxis.h \
|
||||
Graphs/layer.h \
|
||||
@ -428,8 +438,6 @@ HEADERS += \
|
||||
SleepLib/xmlreplay.h \
|
||||
SleepLib/serialoximeter.h \
|
||||
SleepLib/loader_plugins/md300w1_loader.h \
|
||||
Graphs/gSessionTimesChart.h \
|
||||
Graphs/gPressureChart.h \
|
||||
logger.h \
|
||||
SleepLib/loader_plugins/weinmann_loader.h \
|
||||
Graphs/gdailysummary.h \
|
||||
|
@ -29,7 +29,11 @@
|
||||
#include "Graphs/gXAxis.h"
|
||||
#include "Graphs/gLineChart.h"
|
||||
#include "Graphs/gYAxis.h"
|
||||
#include "Graphs/gSessionTimesChart.h"
|
||||
#include "Graphs/gPressureChart.h"
|
||||
#include "Graphs/gAHIChart.h"
|
||||
#include "Graphs/gUsageChart.h"
|
||||
#include "Graphs/gTTIAChart.h"
|
||||
#include "cprogressbar.h"
|
||||
|
||||
#include "mainwindow.h"
|
||||
@ -263,7 +267,8 @@ void Overview::CreateAllGraphs() {
|
||||
//chartsToBeMonitored.insert(ahi,AHI);
|
||||
|
||||
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);
|
||||
|
||||
STG = createGraph("New Session", tr("Session Times"), tr("Session Times"), YT_Time);
|
||||
@ -311,30 +316,34 @@ void Overview::CreateAllGraphs() {
|
||||
sc->addCalc(code, ST_CPH, schema::channel[code].defaultColor());
|
||||
G->AddLayer(sc);
|
||||
chartsToBeMonitored.insert(sc,G);
|
||||
}
|
||||
}
|
||||
if (sc!= nullptr) {
|
||||
sc ->reCalculate();
|
||||
}
|
||||
} // if showInOverview()
|
||||
} // 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 = new SummaryChart("Weight", GT_LINE);
|
||||
weight = new gOverviewGraph("Weight", GT_LINE);
|
||||
weight->setMachineType(MT_JOURNAL);
|
||||
weight->addSlice(Journal_Weight, QColor("black"), ST_SETAVG);
|
||||
WEIGHT->AddLayer(weight);
|
||||
|
||||
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->addSlice(Journal_BMI, QColor("black"), ST_SETAVG);
|
||||
BMI->AddLayer(bmi);
|
||||
|
||||
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->addSlice(Journal_ZombieMeter, QColor("black"), ST_SETAVG);
|
||||
ZOMBIE->AddLayer(zombie);
|
||||
#endif
|
||||
|
||||
connectgSummaryCharts();
|
||||
}
|
||||
@ -532,10 +541,10 @@ void Overview::on_XBoundsChanged(qint64 start,qint64 end)
|
||||
|
||||
bool largerRange=false;
|
||||
if (displayStartDate>maxRangeEndDate || minRangeStartDate>displayEndDate) {
|
||||
// have non-overlaping ranges
|
||||
// have non-overlaping ranges
|
||||
// Only occurs when custom mode is switched to/from a latest mode. custom mode to/from last week.
|
||||
// All other displays expand the existing range.
|
||||
// reset all empty flags to not empty
|
||||
// reset all empty flags to not empty
|
||||
largerRange=true;
|
||||
chartsEmpty = QHash<gSummaryChart*, gGraph*>( chartsToBeMonitored );
|
||||
minRangeStartDate = displayStartDate;
|
||||
@ -737,7 +746,7 @@ void Overview::on_rangeCombo_activated(int index)
|
||||
|
||||
|
||||
DateErrorDisplay::DateErrorDisplay (Overview* overview)
|
||||
: m_overview(overview)
|
||||
: m_overview(overview)
|
||||
{
|
||||
m_visible=false;
|
||||
m_timer = new QTimer();
|
||||
@ -837,7 +846,7 @@ void Overview::setRange(QDate& start, QDate& end, bool updateGraphs/*zoom*/)
|
||||
uiEndDate = end;
|
||||
|
||||
//bool nextSamePage= start.daysTo(end)<=31;
|
||||
bool nextSamePage= (start.year() == end.year() && start.month() == end.month()) ;
|
||||
bool nextSamePage= (start.year() == end.year() && start.month() == end.month()) ;
|
||||
if (samePage>0 ||nextSamePage) {
|
||||
// The widgets do not signal pageChanged on opening - since the page hasn't changed.
|
||||
// however the highlighting may need to be changed.
|
||||
|
@ -19,8 +19,10 @@
|
||||
#include <QTimer>
|
||||
#include "SleepLib/profiles.h"
|
||||
#include "Graphs/gGraphView.h"
|
||||
#ifndef REMOVE_FITNESS
|
||||
#include "Graphs/gOverviewGraph.h"
|
||||
#endif
|
||||
#include "Graphs/gSummaryChart.h"
|
||||
#include "Graphs/gSessionTimesChart.h"
|
||||
|
||||
namespace Ui {
|
||||
class Overview;
|
||||
@ -96,8 +98,10 @@ class Overview : public QWidget
|
||||
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,
|
||||
*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;
|
||||
#endif
|
||||
|
||||
gSummaryChart * stg, *uc, *ahi, * pres, *lk, *npb, *rr, *mv, *tv, *nll, *sn, *ttia;
|
||||
|
||||
@ -173,7 +177,7 @@ class Overview : public QWidget
|
||||
QDate displayStartDate;
|
||||
QDate displayEndDate;
|
||||
|
||||
// min / max dates of the graph Range
|
||||
// min / max dates of the graph Range
|
||||
QDate minRangeStartDate;
|
||||
QDate maxRangeEndDate;
|
||||
|
||||
|
@ -290,13 +290,18 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) :
|
||||
ui->updateCheckEvery->setValue(AppSetting->updateCheckFrequency());
|
||||
if (AppSetting->updatesLastChecked().isValid()) {
|
||||
RefreshLastChecked();
|
||||
} else {
|
||||
} else {
|
||||
ui->updateLastChecked->setText(tr("Never"));
|
||||
}
|
||||
#endif
|
||||
|
||||
ui->overlayFlagsCombo->setCurrentIndex(AppSetting->overlayType());
|
||||
#ifndef REMOVE_FITNESS
|
||||
ui->overviewLinecharts->setCurrentIndex(AppSetting->overviewLinechartMode());
|
||||
#else
|
||||
ui->overviewLinecharts->hide();
|
||||
ui->overviewLinechartsLabel->hide();
|
||||
#endif
|
||||
|
||||
ui->ahiGraphWindowSize->setEnabled(false);
|
||||
ui->ahiGraphWindowSize->setValue(profile->cpap->AHIWindow());
|
||||
@ -883,7 +888,9 @@ bool PreferencesDialog::Save()
|
||||
profile->cpap->setClockDrift(s);
|
||||
|
||||
AppSetting->setOverlayType((OverlayDisplayType)ui->overlayFlagsCombo->currentIndex());
|
||||
#ifndef REMOVE_FITNESS
|
||||
AppSetting->setOverviewLinechartMode((OverviewLinechartModes)ui->overviewLinecharts->currentIndex());
|
||||
#endif
|
||||
|
||||
profile->oxi->setSpO2DropPercentage(ui->spo2Drop->value());
|
||||
profile->oxi->setSpO2DropDuration(ui->spo2DropTime->value());
|
||||
|
@ -2632,7 +2632,7 @@ Mainly affects the importer.</string>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_47">
|
||||
<widget class="QLabel" name="overviewLinechartsLabel">
|
||||
<property name="text">
|
||||
<string>Overview Linecharts</string>
|
||||
</property>
|
||||
|
Loading…
Reference in New Issue
Block a user